]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experiments with the regexp.c extension, trying to get it to report the regexp-span
authordrh <drh@noemail.net>
Mon, 1 Jan 2018 16:59:08 +0000 (16:59 +0000)
committerdrh <drh@noemail.net>
Mon, 1 Jan 2018 16:59:08 +0000 (16:59 +0000)
exact substring that matches the RE.

FossilOrigin-Name: 3d6fba623af9356f6b02c7ef5f29c4bdeb2f8f0359bc0a3194951846afa336da

ext/misc/regexp.c
manifest
manifest.uuid

index b4a8ab5c0423543adc4068baca271597fd70a422..4aaf0058f2d8b5fb72b4417a04a2cb1d0dc7419d 100644 (file)
@@ -76,23 +76,23 @@ SQLITE_EXTENSION_INIT1
 /* The NFA is implemented as sequence of opcodes taken from the following
 ** set.  Each opcode has a single integer argument.
 */
-#define RE_OP_MATCH       1    /* Match the one character in the argument */
-#define RE_OP_ANY         2    /* Match any one character.  (Implements ".") */
-#define RE_OP_ANYSTAR     3    /* Special optimized version of .* */
-#define RE_OP_FORK        4    /* Continue to both next and opcode at iArg */
-#define RE_OP_GOTO        5    /* Jump to opcode at iArg */
-#define RE_OP_ACCEPT      6    /* Halt and indicate a successful match */
-#define RE_OP_CC_INC      7    /* Beginning of a [...] character class */
-#define RE_OP_CC_EXC      8    /* Beginning of a [^...] character class */
-#define RE_OP_CC_VALUE    9    /* Single value in a character class */
-#define RE_OP_CC_RANGE   10    /* Range of values in a character class */
-#define RE_OP_WORD       11    /* Perl word character [A-Za-z0-9_] */
-#define RE_OP_NOTWORD    12    /* Not a perl word character */
-#define RE_OP_DIGIT      13    /* digit:  [0-9] */
-#define RE_OP_NOTDIGIT   14    /* Not a digit */
-#define RE_OP_SPACE      15    /* space:  [ \t\n\r\v\f] */
-#define RE_OP_NOTSPACE   16    /* Not a digit */
-#define RE_OP_BOUNDARY   17    /* Boundary between word and non-word */
+#define RE_OP_MATCH       0    /* Match the one character in the argument */
+#define RE_OP_ANY         1    /* Match any one character.  (Implements ".") */
+#define RE_OP_CC_INC      2    /* Beginning of a [...] character class */
+#define RE_OP_CC_EXC      3    /* Beginning of a [^...] character class */
+#define RE_OP_CC_VALUE    4    /* Single value in a character class */
+#define RE_OP_CC_RANGE    5    /* Range of values in a character class */
+#define RE_OP_WORD        6    /* Perl word character [A-Za-z0-9_] */
+#define RE_OP_NOTWORD     7    /* Not a perl word character */
+#define RE_OP_DIGIT       8    /* digit:  [0-9] */
+#define RE_OP_NOTDIGIT    9    /* Not a digit */
+#define RE_OP_SPACE      10    /* space:  [ \t\n\r\v\f] */
+#define RE_OP_NOTSPACE   11    /* Not a digit */
+#define RE_IMMEDIATE     12    /* OPs >= this evaluate immediately */
+#define RE_OP_BOUNDARY   12    /* Boundary between word and non-word */
+#define RE_OP_FORK       13    /* Continue to both next and opcode at iArg */
+#define RE_OP_GOTO       14    /* Jump to opcode at iArg */
+#define RE_OP_ACCEPT     15    /* Halt and indicate a successful match */
 
 /* Each opcode is a "state" in the NFA */
 typedef unsigned short ReStateNumber;
@@ -118,27 +118,124 @@ struct ReInput {
 
 /* A compiled NFA (or an NFA that is in the process of being compiled) is
 ** an instance of the following object.
+**
+** The application must serialize access to this object.  The code here
+** is threadsafe, as long as each thread is using its own ReCompiled object.
+** But a single ReCompiled object is neither threadsafe nor reentrant.
+**
+** The iBegin value is the earliest possible start of the match.  The
+** true start of the match might be later.
 */
 typedef struct ReCompiled ReCompiled;
 struct ReCompiled {
-  ReInput sIn;                /* Regular expression text */
+  ReInput sIn;                /* Regexp text, or match string text */
   const char *zErr;           /* Error message to return */
   char *aOp;                  /* Operators for the virtual machine */
   int *aArg;                  /* Arguments to each operator */
+  ReStateSet a, b;                  /* Used by re_match() */
   unsigned (*xNextChar)(ReInput*);  /* Next character function */
+  unsigned char nInit;              /* Number of characters in zInit */
+  unsigned char bFlexStart;   /* Input regexp omits the initial "^" */
+  unsigned char bKeepSpan;    /* When matching, remember start and end */
+  unsigned char bMultiBegin;  /* iBegin might not be accurate */
+  unsigned int iBegin;        /* Offset to first character in the match */
+  unsigned int iEnd;          /* Offset past the end of last match char */
+  unsigned int nState;        /* Number of entries in aOp[] and aArg[] */
+  unsigned int nAlloc;        /* Slots allocated for aOp[] and aArg[] */
   unsigned char zInit[12];    /* Initial text to match */
-  int nInit;                  /* Number of characters in zInit */
-  unsigned nState;            /* Number of entries in aOp[] and aArg[] */
-  unsigned nAlloc;            /* Slots allocated for aOp[] and aArg[] */
 };
 
+#if SQLITE_REGEXP_DEBUG
+/************************************************************************
+** The following interfaces are used for development and debugging only
+** and are commented out for deployment.
+*/
+#include <stdio.h>
+#include <ctype.h>
+
+/* Render one character. */
+static char *re_char(int c){
+  static char zBuf[20];
+  if( isprint(c) && (c==' ' || !isspace(c)) ){
+    sqlite3_snprintf(sizeof(zBuf),zBuf,"'%c'",c);
+  }else{
+    sqlite3_snprintf(sizeof(zBuf),zBuf,"0x%02x",c);
+  }
+  return zBuf;
+}
+
+/*
+** Print out a regexp program.
+*/
+static void re_print(ReCompiled *pRe){
+  int i;
+  for(i=0; i<pRe->nState; i++){
+    printf("%3d ", i);
+    switch( pRe->aOp[i] ){
+      case RE_OP_MATCH:    printf("MATCH     %s\n", re_char(pRe->aArg[i]));
+                           break;
+      case RE_OP_ANY:      printf("ANY\n"); break;
+      case RE_OP_WORD:     printf("WORD\n"); break;
+      case RE_OP_NOTWORD:  printf("NOTWORD\n"); break;
+      case RE_OP_DIGIT:    printf("DIGIT\n"); break;
+      case RE_OP_NOTDIGIT: printf("NOTDIGIT\n"); break;
+      case RE_OP_SPACE:    printf("SPACE\n"); break;
+      case RE_OP_NOTSPACE: printf("NOTSPACE\n"); break;
+      case RE_OP_BOUNDARY: printf("BOUNDARY\n"); break;
+      case RE_OP_FORK:     printf("FORK      %d\n", i+pRe->aArg[i]); break;
+      case RE_OP_GOTO:     printf("GOTO      %d\n", i+pRe->aArg[i]); break;
+      case RE_OP_ACCEPT:   printf("ACCEPT\n"); break;
+      case RE_OP_CC_INC: 
+      case RE_OP_CC_EXC: {
+        printf("%s\n", pRe->aOp[i]==RE_OP_CC_INC ? "CC_INC" : "CC_EXC");
+        int j;
+        int n = pRe->aArg[i];
+        for(j=1; j>0 && j<n; j++){
+          printf("%3d ... %s %s\n", i+j,
+           pRe->aOp[i+j]==RE_OP_CC_VALUE ? "VALUE" : "RANGE",
+           re_char(pRe->aArg[i+j]));
+        }
+        i += n-1;
+        break;
+      }
+    }
+  }
+}
+/*
+** Print out a ReStateSet
+*/
+static void restateset_print(ReStateSet *pSet){
+  int i;
+  for(i=0; i<pSet->nState; i++){
+    if( i ) printf(" ");
+    printf(" %2d", pSet->aState[i]);
+  }
+}
+/* End of debugging utilities
+*****************************************************************************/
+#endif /* SQLITE_REGEXP_DEBUG */
+
 /* Add a state to the given state set if it is not already there */
-static void re_add_state(ReStateSet *pSet, int newState){
+static void re_add_state_to_set(ReStateSet *pSet, int newState){
   unsigned i;
   for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
   pSet->aState[pSet->nState++] = (ReStateNumber)newState;
 }
 
+/* Add a state to the appropriate state set.  States for immediate
+** evaluation are added into pRe->a.  If another input token is required
+** before the state can be evaluated, then it is added to pRe->b.
+*/
+static void re_add_state(ReCompiled *pRe, int newState){
+  ReStateSet *pSet;
+  if( pRe->aOp[newState]>=RE_IMMEDIATE ){
+    pSet = &pRe->a;
+  }else{
+    pSet = &pRe->b;
+  }
+  re_add_state_to_set(pSet, newState);
+}
+
 /* Extract the next unicode character from *pzIn and return it.  Advance
 ** *pzIn to the first byte past the end of the character returned.  To
 ** be clear:  this routine converts utf8 to unicode.  This routine is 
@@ -191,112 +288,102 @@ static int re_space_char(int c){
   return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f';
 }
 
+/* Interchange pRe->a and pRe->b */
+static void re_swap(ReCompiled *pRe){
+  ReStateSet t = pRe->a;
+  pRe->a = pRe->b;
+  pRe->b = t;
+}
+
 /* Run a compiled regular expression on the zero-terminated input
 ** string zIn[].  Return true on a match and false if there is no match.
 */
 static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
-  ReStateSet aStateSet[2], *pThis, *pNext;
-  ReStateNumber aSpace[100];
-  ReStateNumber *pToFree;
   unsigned int i = 0;
-  unsigned int iSwap = 0;
   int c = RE_EOF+1;
-  int cPrev = 0;
   int rc = 0;
-  ReInput in;
+  int iIdx = -1;
+  int iBegin = -1;
+  int iReset = -1;
+  ReInput *pIn = &pRe->sIn;
 
-  in.z = zIn;
-  in.i = 0;
-  in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
+  pIn->z = zIn;
+  pIn->i = 0;
+  pIn->mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
+  pRe->bMultiBegin = 0;
 
   /* Look for the initial prefix match, if there is one. */
   if( pRe->nInit ){
     unsigned char x = pRe->zInit[0];
-    while( in.i+pRe->nInit<=in.mx 
-     && (zIn[in.i]!=x ||
-         strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
+    while( pIn->i+pRe->nInit<=pIn->mx 
+     && (zIn[pIn->i]!=x ||
+         strncmp((const char*)zIn+pIn->i,
+                 (const char*)pRe->zInit,pRe->nInit)!=0)
     ){
-      in.i++;
+      pIn->i++;
     }
-    if( in.i+pRe->nInit>in.mx ) return 0;
+    if( pIn->i+pRe->nInit>pIn->mx ) return 0;
   }
 
-  if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
-    pToFree = 0;
-    aStateSet[0].aState = aSpace;
-  }else{
-    pToFree = sqlite3_malloc( sizeof(ReStateNumber)*2*pRe->nState );
-    if( pToFree==0 ) return -1;
-    aStateSet[0].aState = pToFree;
-  }
-  aStateSet[1].aState = &aStateSet[0].aState[pRe->nState];
-  pNext = &aStateSet[1];
-  pNext->nState = 0;
-  re_add_state(pNext, 0);
-  while( c!=RE_EOF && pNext->nState>0 ){
-    cPrev = c;
-    c = pRe->xNextChar(&in);
-    pThis = pNext;
-    pNext = &aStateSet[iSwap];
-    iSwap = 1 - iSwap;
-    pNext->nState = 0;
-    for(i=0; i<pThis->nState; i++){
-      int x = pThis->aState[i];
+re_print(pRe);
+printf("INPUT: [%.*s]\n", nIn, zIn);
+  pRe->b.nState = 0;
+  pRe->a.nState = 0;
+  re_add_state(pRe, 0);
+  iReset = -1;
+  c = 0;
+  while(1){
+    for(i=0; i<pRe->a.nState; i++){
+      int x = pRe->a.aState[i];
+printf("%2d,%-5s %d.%d = ", pRe->sIn.i, re_char(c), i, x);
+restateset_print(&pRe->a);
+printf("  => ");
+restateset_print(&pRe->b);
+printf("\n");
       switch( pRe->aOp[x] ){
         case RE_OP_MATCH: {
-          if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
-          break;
+          if( pRe->aArg[x]!=c ) break;
+          /* Fall thru */
         }
         case RE_OP_ANY: {
-          re_add_state(pNext, x+1);
+        add_next:
+          re_add_state(pRe, x+1);
           break;
         }
         case RE_OP_WORD: {
-          if( re_word_char(c) ) re_add_state(pNext, x+1);
+          if( re_word_char(c) ) goto add_next;
           break;
         }
         case RE_OP_NOTWORD: {
-          if( !re_word_char(c) ) re_add_state(pNext, x+1);
+          if( !re_word_char(c) ) goto add_next;
           break;
         }
         case RE_OP_DIGIT: {
-          if( re_digit_char(c) ) re_add_state(pNext, x+1);
+          if( re_digit_char(c) ) goto add_next;
           break;
         }
         case RE_OP_NOTDIGIT: {
-          if( !re_digit_char(c) ) re_add_state(pNext, x+1);
+          if( !re_digit_char(c) ) goto add_next;
           break;
         }
         case RE_OP_SPACE: {
-          if( re_space_char(c) ) re_add_state(pNext, x+1);
+          if( re_space_char(c) ) goto add_next;
           break;
         }
         case RE_OP_NOTSPACE: {
-          if( !re_space_char(c) ) re_add_state(pNext, x+1);
+          if( !re_space_char(c) ) goto add_next;
           break;
         }
         case RE_OP_BOUNDARY: {
-          if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1);
-          break;
-        }
-        case RE_OP_ANYSTAR: {
-          re_add_state(pNext, x);
-          re_add_state(pThis, x+1);
-          break;
-        }
-        case RE_OP_FORK: {
-          re_add_state(pThis, x+pRe->aArg[x]);
-          re_add_state(pThis, x+1);
-          break;
-        }
-        case RE_OP_GOTO: {
-          re_add_state(pThis, x+pRe->aArg[x]);
+          int iCur = pIn->i;
+          int cNext = pRe->xNextChar(pIn);
+          pIn->i = iCur;
+          if( re_word_char(c)!=re_word_char(cNext) ){
+            if( iReset>=0 ) goto add_next;
+            re_add_state_to_set(&pRe->b, x);
+          }
           break;
         }
-        case RE_OP_ACCEPT: {
-          rc = 1;
-          goto re_match_end;
-        }
         case RE_OP_CC_INC:
         case RE_OP_CC_EXC: {
           int j = 1;
@@ -318,17 +405,51 @@ static int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
             }
           }
           if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit;
-          if( hit ) re_add_state(pNext, x+n);
+          if( hit ) re_add_state(pRe, x+n);
           break;            
         }
+        case RE_OP_FORK: {
+          re_add_state(pRe, x+1);
+          /* Fall thru */
+        }
+        case RE_OP_GOTO: {
+          re_add_state(pRe, x+pRe->aArg[x]);
+          break;
+        }
+        case RE_OP_ACCEPT: {
+          if( !pRe->bKeepSpan ) return 1;
+          pRe->iEnd = pIn->i;
+printf("ACCEPT %d\n", pIn->i);
+          rc = 1;
+          break;
+        }
+      } /* End of switch( pRe->aOp[x] ) */
+    } /* End of loop over states in pRe->a.aState[] */
+    if( iReset<0 ){
+      if( pRe->bFlexStart ){
+        iReset = pRe->b.nState;
+        memcpy(pRe->a.aState, pRe->b.aState, iReset*sizeof(ReStateNumber));
+      }else{
+        iReset = 0;
+      }
+printf("iReset=%d\n", iReset);
+    }else if( c==RE_EOF || pRe->b.nState==0 ){
+      break;
+    }else if( pRe->b.nState>iReset ){
+      if( iBegin<0 ){
+        iBegin = iIdx;
+printf("Begin at %d\n", iIdx);
+      }else{
+        pRe->bMultiBegin = 1;
+printf("Another possible beginning: %d\n", iIdx);
       }
     }
+    re_swap(pRe);
+    pRe->b.nState = iReset;
+    iIdx = pIn->i;
+    c = pRe->xNextChar(pIn);
   }
-  for(i=0; i<pNext->nState; i++){
-    if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; }
-  }
-re_match_end:
-  sqlite3_free(pToFree);
+  pRe->iBegin = iBegin;
   return rc;
 }
 
@@ -493,12 +614,7 @@ static const char *re_subcompile_string(ReCompiled *p){
         break;
       }
       case '.': {
-        if( rePeek(p)=='*' ){
-          re_append(p, RE_OP_ANYSTAR, 0);
-          p->sIn.i++;
-        }else{ 
-          re_append(p, RE_OP_ANY, 0);
-        }
+        re_append(p, RE_OP_ANY, 0);
         break;
       }
       case '*': {
@@ -521,12 +637,12 @@ static const char *re_subcompile_string(ReCompiled *p){
         int m = 0, n = 0;
         int sz, j;
         if( iPrev<0 ) return "'{m,n}' without operand";
-        while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
+        while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0';p->sIn.i++; }
         n = m;
         if( c==',' ){
           p->sIn.i++;
           n = 0;
-          while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
+          while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0';p->sIn.i++; }
         }
         if( c!='}' ) return "unmatched '{'";
         if( n>0 && n<m ) return "n less than m in '{m,n}'";
@@ -614,6 +730,8 @@ void re_free(ReCompiled *pRe){
   if( pRe ){
     sqlite3_free(pRe->aOp);
     sqlite3_free(pRe->aArg);
+    sqlite3_free(pRe->a.aState);
+    sqlite3_free(pRe->b.aState);
     sqlite3_free(pRe);
   }
 }
@@ -643,7 +761,7 @@ const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
   if( zIn[0]=='^' ){
     zIn++;
   }else{
-    re_append(pRe, RE_OP_ANYSTAR, 0);
+    pRe->bFlexStart = 1;
   }
   pRe->sIn.z = (unsigned char*)zIn;
   pRe->sIn.i = 0;
@@ -664,6 +782,12 @@ const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
     re_free(pRe);
     return "unrecognized character";
   }
+  pRe->a.aState = sqlite3_malloc64( sizeof(pRe->a.aState[0])*pRe->nState );
+  pRe->b.aState = sqlite3_malloc64( sizeof(pRe->b.aState[0])*pRe->nState );
+  if( pRe->a.aState==0 || pRe->b.aState==0 ){
+    re_free(pRe);
+    return "out of memory";
+  }
 
   /* The following is a performance optimization.  If the regex begins with
   ** ".*" (if the input regex lacks an initial "^") and afterwards there are
@@ -673,8 +797,8 @@ const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
   ** regex engine over the string.  Do not worry able trying to match
   ** unicode characters beyond plane 0 - those are very rare and this is
   ** just an optimization. */
-  if( pRe->aOp[0]==RE_OP_ANYSTAR ){
-    for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
+  if( pRe->bFlexStart && !noCase ){
+    for(j=0, i=0; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
       unsigned x = pRe->aArg[i];
       if( x<=127 ){
         pRe->zInit[j++] = (unsigned char)x;
@@ -740,6 +864,68 @@ static void re_sql_func(
   }
 }
 
+/*
+** Implementation of the regexp_test1() SQL function.
+**
+**         regexp_test1(PATTERN, STRING)
+**
+** Return the part of STRING that matches PATTERN.  Or return NULL
+** if there is no match.
+*/
+static void re_test1_func(
+  sqlite3_context *context, 
+  int argc, 
+  sqlite3_value **argv
+){
+  ReCompiled *pRe;          /* Compiled regular expression */
+  const char *zPattern;     /* The regular expression */
+  const unsigned char *zStr;/* String being searched */
+  const char *zErr;         /* Compile error message */
+  int setAux = 0;           /* True to invoke sqlite3_set_auxdata() */
+  int rc;
+
+  pRe = sqlite3_get_auxdata(context, 0);
+  if( pRe==0 ){
+    zPattern = (const char*)sqlite3_value_text(argv[0]);
+    if( zPattern==0 ) return;
+    zErr = re_compile(&pRe, zPattern, 0);
+    if( zErr ){
+      re_free(pRe);
+      sqlite3_result_error(context, zErr, -1);
+      return;
+    }
+    if( pRe==0 ){
+      sqlite3_result_error_nomem(context);
+      return;
+    }
+    setAux = 1;
+  }
+  zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
+  if( zStr!=0 ){
+    int iBegin, iEnd;
+    pRe->bKeepSpan = 1;
+    rc = re_match(pRe, zStr, -1);
+    if( !rc ) goto re_test1_end;
+    iBegin = pRe->iBegin;
+    iEnd = pRe->iEnd;
+    if( pRe->bMultiBegin ){
+      pRe->bKeepSpan = 0;
+      while( re_match(pRe, zStr+iBegin, iEnd-iBegin)==0 ){
+        if( zStr[++iBegin]>=0xc0 ){
+          while( (zStr[iBegin]&0xc0)==0x80 ) iBegin++;
+        }
+        if( iBegin>=iEnd ) goto re_test1_end;
+      }
+    }
+    sqlite3_result_text(context, (const char*)zStr+iBegin,
+                        iEnd - iBegin, SQLITE_TRANSIENT);
+  }
+re_test1_end:
+  if( setAux ){
+    sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
+  }
+}
+
 /*
 ** Invoke this routine to register the regexp() function with the
 ** SQLite database connection.
@@ -756,5 +942,9 @@ int sqlite3_regexp_init(
   SQLITE_EXTENSION_INIT2(pApi);
   rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
                                  re_sql_func, 0, 0);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_function(db, "re_test1", 2, SQLITE_UTF8, 0,
+                                 re_test1_func, 0, 0);
+  }
   return rc;
 }
index 682ec0eee70c1d9b6e071a5c89d9ef98d6ef1ba9..c864bc028082e30f23c42af9fd1ef0d10fa20abc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\ssupport\sfor\sthe\ssqlite_unsupported_offset()\sSQL\sfunction\sif\sand\sonly\nif\scompiled\susing\s-DSQLITE_ENABLE_OFFSET_SQL_FUNC.\s\sUse\sthat\sdefinition\nwhen\scompiling\sthe\scommand-line\sshell.
-D 2017-12-29T17:21:21.319
+C Experiments\swith\sthe\sregexp.c\sextension,\strying\sto\sget\sit\sto\sreport\sthe\nexact\ssubstring\sthat\smatches\sthe\sRE.
+D 2018-01-01T16:59:08.869
 F Makefile.in 804c347948b0ad5a962aea3e59413e897227e0173d0e76910f11f473e6c7ae9b
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 57dcce0595d4a0c4b94b6f6ba732b8e33540358adf5fb8e1aa8389d34a9677e8
@@ -282,7 +282,7 @@ F ext/misc/memvfs.c e5225bc22e79dde6b28380f3a068ddf600683a33
 F ext/misc/mmapwarm.c 70b618f2d0bde43fae288ad0b7498a629f2b6f61b50a27e06fae3cd23c83af29
 F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
 F ext/misc/percentile.c 92699c8cd7d517ff610e6037e56506f8904dae2e
-F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4
+F ext/misc/regexp.c 9f6655fe21efc98b5c7e51d94116689fd6e17cad817b4a9eb68d978e28dd7f8a
 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
 F ext/misc/rot13.c 540a169cb0d74f15522a8930b0cccdcb37a4fd071d219a5a083a319fc6e8db77
 F ext/misc/scrub.c 1c5bfb8b0cd18b602fcb55755e84abf0023ac2fb
@@ -1688,8 +1688,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P f4349c0c26611de8a7d5beb99431a575cf531cdeb0ca2413efabcf0a61e6f424 9406c0a685fd5ff3516a66402b0514a1440652822a5eecf0b7af85929f3079e8
-R 35346abc344e9d51558b9d8d5c8f2d13
-T +closed 9406c0a685fd5ff3516a66402b0514a1440652822a5eecf0b7af85929f3079e8
+P 4f1f1f521ae6da96b899e10bfeff6bc1ab7a45de0705076febaae20b441f48c6
+R 1f9db37ee5bdc6528286b18b258e44ca
+T *branch * regexp-span
+T *sym-regexp-span *
+T -sym-trunk *
 U drh
-Z b5b643a0e7da393bb8589918154fc3c1
+Z db39d3e98113e50f1341ba7f68cc2c1a
index 1c26686ec72d5dff31b024198f9fc0bd3d126337..a8a3bf58736948e8cbb86e191c4ebf2b8afabfe1 100644 (file)
@@ -1 +1 @@
-4f1f1f521ae6da96b899e10bfeff6bc1ab7a45de0705076febaae20b441f48c6
\ No newline at end of file
+3d6fba623af9356f6b02c7ef5f29c4bdeb2f8f0359bc0a3194951846afa336da
\ No newline at end of file