Fossil SCM

Fix TH1 expression parsing when there are unbalanced parenthesis. Add more tests.

mistachkin 2014-08-28 00:00 UTC th1ExprFix
Commit 00c01d7a36c8526c4adb6dcf2a1af5c1bb297ed9
2 files changed +49 -34 +25
+49 -34
--- src/th.c
+++ src/th.c
@@ -1774,10 +1774,11 @@
17741774
** the expression module means the Th_Expr() and exprXXX() functions.
17751775
*/
17761776
typedef struct Operator Operator;
17771777
struct Operator {
17781778
const char *zOp;
1779
+ int nOp;
17791780
int eOp;
17801781
int iPrecedence;
17811782
int eArgType;
17821783
};
17831784
typedef struct Expr Expr;
@@ -1831,47 +1832,47 @@
18311832
#define ARG_NUMBER 2
18321833
#define ARG_STRING 3
18331834
18341835
static Operator aOperator[] = {
18351836
1836
- {"(", OP_OPEN_BRACKET, -1, 0},
1837
- {")", OP_CLOSE_BRACKET, -1, 0},
1837
+ {"(", 1, OP_OPEN_BRACKET, -1, 0},
1838
+ {")", 1, OP_CLOSE_BRACKET, -1, 0},
18381839
18391840
/* Note: all unary operators have (iPrecedence==1) */
1840
- {"-", OP_UNARY_MINUS, 1, ARG_NUMBER},
1841
- {"+", OP_UNARY_PLUS, 1, ARG_NUMBER},
1842
- {"~", OP_BITWISE_NOT, 1, ARG_INTEGER},
1843
- {"!", OP_LOGICAL_NOT, 1, ARG_INTEGER},
1841
+ {"-", 1, OP_UNARY_MINUS, 1, ARG_NUMBER},
1842
+ {"+", 1, OP_UNARY_PLUS, 1, ARG_NUMBER},
1843
+ {"~", 1, OP_BITWISE_NOT, 1, ARG_INTEGER},
1844
+ {"!", 1, OP_LOGICAL_NOT, 1, ARG_INTEGER},
18441845
18451846
/* Binary operators. It is important to the parsing in Th_Expr() that
18461847
* the two-character symbols ("==") appear before the one-character
18471848
* ones ("="). And that the priorities of all binary operators are
18481849
* integers between 2 and 12.
18491850
*/
1850
- {"<<", OP_LEFTSHIFT, 4, ARG_INTEGER},
1851
- {">>", OP_RIGHTSHIFT, 4, ARG_INTEGER},
1852
- {"<=", OP_LE, 5, ARG_NUMBER},
1853
- {">=", OP_GE, 5, ARG_NUMBER},
1854
- {"==", OP_EQ, 6, ARG_NUMBER},
1855
- {"!=", OP_NE, 6, ARG_NUMBER},
1856
- {"eq", OP_SEQ, 7, ARG_STRING},
1857
- {"ne", OP_SNE, 7, ARG_STRING},
1858
- {"&&", OP_LOGICAL_AND, 11, ARG_INTEGER},
1859
- {"||", OP_LOGICAL_OR, 12, ARG_INTEGER},
1860
-
1861
- {"*", OP_MULTIPLY, 2, ARG_NUMBER},
1862
- {"/", OP_DIVIDE, 2, ARG_NUMBER},
1863
- {"%", OP_MODULUS, 2, ARG_INTEGER},
1864
- {"+", OP_ADD, 3, ARG_NUMBER},
1865
- {"-", OP_SUBTRACT, 3, ARG_NUMBER},
1866
- {"<", OP_LT, 5, ARG_NUMBER},
1867
- {">", OP_GT, 5, ARG_NUMBER},
1868
- {"&", OP_BITWISE_AND, 8, ARG_INTEGER},
1869
- {"^", OP_BITWISE_XOR, 9, ARG_INTEGER},
1870
- {"|", OP_BITWISE_OR, 10, ARG_INTEGER},
1871
-
1872
- {0,0,0,0}
1851
+ {"<<", 2, OP_LEFTSHIFT, 4, ARG_INTEGER},
1852
+ {">>", 2, OP_RIGHTSHIFT, 4, ARG_INTEGER},
1853
+ {"<=", 2, OP_LE, 5, ARG_NUMBER},
1854
+ {">=", 2, OP_GE, 5, ARG_NUMBER},
1855
+ {"==", 2, OP_EQ, 6, ARG_NUMBER},
1856
+ {"!=", 2, OP_NE, 6, ARG_NUMBER},
1857
+ {"eq", 2, OP_SEQ, 7, ARG_STRING},
1858
+ {"ne", 2, OP_SNE, 7, ARG_STRING},
1859
+ {"&&", 2, OP_LOGICAL_AND, 11, ARG_INTEGER},
1860
+ {"||", 2, OP_LOGICAL_OR, 12, ARG_INTEGER},
1861
+
1862
+ {"*", 1, OP_MULTIPLY, 2, ARG_NUMBER},
1863
+ {"/", 1, OP_DIVIDE, 2, ARG_NUMBER},
1864
+ {"%", 1, OP_MODULUS, 2, ARG_INTEGER},
1865
+ {"+", 1, OP_ADD, 3, ARG_NUMBER},
1866
+ {"-", 1, OP_SUBTRACT, 3, ARG_NUMBER},
1867
+ {"<", 1, OP_LT, 5, ARG_NUMBER},
1868
+ {">", 1, OP_GT, 5, ARG_NUMBER},
1869
+ {"&", 1, OP_BITWISE_AND, 8, ARG_INTEGER},
1870
+ {"^", 1, OP_BITWISE_XOR, 9, ARG_INTEGER},
1871
+ {"|", 1, OP_BITWISE_OR, 10, ARG_INTEGER},
1872
+
1873
+ {0,0,0,0,0}
18731874
};
18741875
18751876
/*
18761877
** The first part of the string (zInput,nInput) contains an integer.
18771878
** Set *pnVarname to the number of bytes in the numeric string.
@@ -2178,10 +2179,11 @@
21782179
int *pnToken /* OUT: Size of token array */
21792180
){
21802181
int i;
21812182
21822183
int rc = TH_OK;
2184
+ int nNest = 0;
21832185
int nToken = 0;
21842186
Expr **apToken = 0;
21852187
21862188
for(i=0; rc==TH_OK && i<nExpr; ){
21872189
char c = zExpr[i];
@@ -2222,20 +2224,29 @@
22222224
break;
22232225
}
22242226
22252227
default: {
22262228
int j;
2227
- for(j=0; aOperator[j].zOp; j++){
2228
- int nOp;
2229
- if( aOperator[j].iPrecedence==1 && nToken>0 ){
2229
+ const char *zOp;
2230
+ for(j=0; (zOp=aOperator[j].zOp); j++){
2231
+ int nOp = aOperator[j].nOp;
2232
+ int isMatch = 0;
2233
+ if( (nExpr-i)>=nOp && 0==memcmp(zOp, &zExpr[i], nOp) ){
2234
+ isMatch = 1;
2235
+ }
2236
+ if( isMatch && aOperator[j].eOp==OP_OPEN_BRACKET ){
2237
+ nNest++;
2238
+ }else if( isMatch && aOperator[j].eOp==OP_CLOSE_BRACKET ){
2239
+ nNest--;
2240
+ }
2241
+ if( nToken>0 && aOperator[j].iPrecedence==1 ){
22302242
Expr *pPrev = apToken[nToken-1];
22312243
if( !pPrev->pOp || pPrev->pOp->eOp==OP_CLOSE_BRACKET ){
22322244
continue;
22332245
}
22342246
}
2235
- nOp = th_strlen((const char *)aOperator[j].zOp);
2236
- if( (nExpr-i)>=nOp && 0==memcmp(aOperator[j].zOp, &zExpr[i], nOp) ){
2247
+ if( isMatch ){
22372248
pNew->pOp = &aOperator[j];
22382249
i += nOp;
22392250
break;
22402251
}
22412252
}
@@ -2264,10 +2275,14 @@
22642275
Th_Free(interp, pNew);
22652276
rc = TH_ERROR;
22662277
}
22672278
}
22682279
}
2280
+
2281
+ if( nNest!=0 ){
2282
+ rc = TH_ERROR;
2283
+ }
22692284
22702285
*papToken = apToken;
22712286
*pnToken = nToken;
22722287
return rc;
22732288
}
22742289
--- src/th.c
+++ src/th.c
@@ -1774,10 +1774,11 @@
1774 ** the expression module means the Th_Expr() and exprXXX() functions.
1775 */
1776 typedef struct Operator Operator;
1777 struct Operator {
1778 const char *zOp;
 
1779 int eOp;
1780 int iPrecedence;
1781 int eArgType;
1782 };
1783 typedef struct Expr Expr;
@@ -1831,47 +1832,47 @@
1831 #define ARG_NUMBER 2
1832 #define ARG_STRING 3
1833
1834 static Operator aOperator[] = {
1835
1836 {"(", OP_OPEN_BRACKET, -1, 0},
1837 {")", OP_CLOSE_BRACKET, -1, 0},
1838
1839 /* Note: all unary operators have (iPrecedence==1) */
1840 {"-", OP_UNARY_MINUS, 1, ARG_NUMBER},
1841 {"+", OP_UNARY_PLUS, 1, ARG_NUMBER},
1842 {"~", OP_BITWISE_NOT, 1, ARG_INTEGER},
1843 {"!", OP_LOGICAL_NOT, 1, ARG_INTEGER},
1844
1845 /* Binary operators. It is important to the parsing in Th_Expr() that
1846 * the two-character symbols ("==") appear before the one-character
1847 * ones ("="). And that the priorities of all binary operators are
1848 * integers between 2 and 12.
1849 */
1850 {"<<", OP_LEFTSHIFT, 4, ARG_INTEGER},
1851 {">>", OP_RIGHTSHIFT, 4, ARG_INTEGER},
1852 {"<=", OP_LE, 5, ARG_NUMBER},
1853 {">=", OP_GE, 5, ARG_NUMBER},
1854 {"==", OP_EQ, 6, ARG_NUMBER},
1855 {"!=", OP_NE, 6, ARG_NUMBER},
1856 {"eq", OP_SEQ, 7, ARG_STRING},
1857 {"ne", OP_SNE, 7, ARG_STRING},
1858 {"&&", OP_LOGICAL_AND, 11, ARG_INTEGER},
1859 {"||", OP_LOGICAL_OR, 12, ARG_INTEGER},
1860
1861 {"*", OP_MULTIPLY, 2, ARG_NUMBER},
1862 {"/", OP_DIVIDE, 2, ARG_NUMBER},
1863 {"%", OP_MODULUS, 2, ARG_INTEGER},
1864 {"+", OP_ADD, 3, ARG_NUMBER},
1865 {"-", OP_SUBTRACT, 3, ARG_NUMBER},
1866 {"<", OP_LT, 5, ARG_NUMBER},
1867 {">", OP_GT, 5, ARG_NUMBER},
1868 {"&", OP_BITWISE_AND, 8, ARG_INTEGER},
1869 {"^", OP_BITWISE_XOR, 9, ARG_INTEGER},
1870 {"|", OP_BITWISE_OR, 10, ARG_INTEGER},
1871
1872 {0,0,0,0}
1873 };
1874
1875 /*
1876 ** The first part of the string (zInput,nInput) contains an integer.
1877 ** Set *pnVarname to the number of bytes in the numeric string.
@@ -2178,10 +2179,11 @@
2178 int *pnToken /* OUT: Size of token array */
2179 ){
2180 int i;
2181
2182 int rc = TH_OK;
 
2183 int nToken = 0;
2184 Expr **apToken = 0;
2185
2186 for(i=0; rc==TH_OK && i<nExpr; ){
2187 char c = zExpr[i];
@@ -2222,20 +2224,29 @@
2222 break;
2223 }
2224
2225 default: {
2226 int j;
2227 for(j=0; aOperator[j].zOp; j++){
2228 int nOp;
2229 if( aOperator[j].iPrecedence==1 && nToken>0 ){
 
 
 
 
 
 
 
 
 
 
2230 Expr *pPrev = apToken[nToken-1];
2231 if( !pPrev->pOp || pPrev->pOp->eOp==OP_CLOSE_BRACKET ){
2232 continue;
2233 }
2234 }
2235 nOp = th_strlen((const char *)aOperator[j].zOp);
2236 if( (nExpr-i)>=nOp && 0==memcmp(aOperator[j].zOp, &zExpr[i], nOp) ){
2237 pNew->pOp = &aOperator[j];
2238 i += nOp;
2239 break;
2240 }
2241 }
@@ -2264,10 +2275,14 @@
2264 Th_Free(interp, pNew);
2265 rc = TH_ERROR;
2266 }
2267 }
2268 }
 
 
 
 
2269
2270 *papToken = apToken;
2271 *pnToken = nToken;
2272 return rc;
2273 }
2274
--- src/th.c
+++ src/th.c
@@ -1774,10 +1774,11 @@
1774 ** the expression module means the Th_Expr() and exprXXX() functions.
1775 */
1776 typedef struct Operator Operator;
1777 struct Operator {
1778 const char *zOp;
1779 int nOp;
1780 int eOp;
1781 int iPrecedence;
1782 int eArgType;
1783 };
1784 typedef struct Expr Expr;
@@ -1831,47 +1832,47 @@
1832 #define ARG_NUMBER 2
1833 #define ARG_STRING 3
1834
1835 static Operator aOperator[] = {
1836
1837 {"(", 1, OP_OPEN_BRACKET, -1, 0},
1838 {")", 1, OP_CLOSE_BRACKET, -1, 0},
1839
1840 /* Note: all unary operators have (iPrecedence==1) */
1841 {"-", 1, OP_UNARY_MINUS, 1, ARG_NUMBER},
1842 {"+", 1, OP_UNARY_PLUS, 1, ARG_NUMBER},
1843 {"~", 1, OP_BITWISE_NOT, 1, ARG_INTEGER},
1844 {"!", 1, OP_LOGICAL_NOT, 1, ARG_INTEGER},
1845
1846 /* Binary operators. It is important to the parsing in Th_Expr() that
1847 * the two-character symbols ("==") appear before the one-character
1848 * ones ("="). And that the priorities of all binary operators are
1849 * integers between 2 and 12.
1850 */
1851 {"<<", 2, OP_LEFTSHIFT, 4, ARG_INTEGER},
1852 {">>", 2, OP_RIGHTSHIFT, 4, ARG_INTEGER},
1853 {"<=", 2, OP_LE, 5, ARG_NUMBER},
1854 {">=", 2, OP_GE, 5, ARG_NUMBER},
1855 {"==", 2, OP_EQ, 6, ARG_NUMBER},
1856 {"!=", 2, OP_NE, 6, ARG_NUMBER},
1857 {"eq", 2, OP_SEQ, 7, ARG_STRING},
1858 {"ne", 2, OP_SNE, 7, ARG_STRING},
1859 {"&&", 2, OP_LOGICAL_AND, 11, ARG_INTEGER},
1860 {"||", 2, OP_LOGICAL_OR, 12, ARG_INTEGER},
1861
1862 {"*", 1, OP_MULTIPLY, 2, ARG_NUMBER},
1863 {"/", 1, OP_DIVIDE, 2, ARG_NUMBER},
1864 {"%", 1, OP_MODULUS, 2, ARG_INTEGER},
1865 {"+", 1, OP_ADD, 3, ARG_NUMBER},
1866 {"-", 1, OP_SUBTRACT, 3, ARG_NUMBER},
1867 {"<", 1, OP_LT, 5, ARG_NUMBER},
1868 {">", 1, OP_GT, 5, ARG_NUMBER},
1869 {"&", 1, OP_BITWISE_AND, 8, ARG_INTEGER},
1870 {"^", 1, OP_BITWISE_XOR, 9, ARG_INTEGER},
1871 {"|", 1, OP_BITWISE_OR, 10, ARG_INTEGER},
1872
1873 {0,0,0,0,0}
1874 };
1875
1876 /*
1877 ** The first part of the string (zInput,nInput) contains an integer.
1878 ** Set *pnVarname to the number of bytes in the numeric string.
@@ -2178,10 +2179,11 @@
2179 int *pnToken /* OUT: Size of token array */
2180 ){
2181 int i;
2182
2183 int rc = TH_OK;
2184 int nNest = 0;
2185 int nToken = 0;
2186 Expr **apToken = 0;
2187
2188 for(i=0; rc==TH_OK && i<nExpr; ){
2189 char c = zExpr[i];
@@ -2222,20 +2224,29 @@
2224 break;
2225 }
2226
2227 default: {
2228 int j;
2229 const char *zOp;
2230 for(j=0; (zOp=aOperator[j].zOp); j++){
2231 int nOp = aOperator[j].nOp;
2232 int isMatch = 0;
2233 if( (nExpr-i)>=nOp && 0==memcmp(zOp, &zExpr[i], nOp) ){
2234 isMatch = 1;
2235 }
2236 if( isMatch && aOperator[j].eOp==OP_OPEN_BRACKET ){
2237 nNest++;
2238 }else if( isMatch && aOperator[j].eOp==OP_CLOSE_BRACKET ){
2239 nNest--;
2240 }
2241 if( nToken>0 && aOperator[j].iPrecedence==1 ){
2242 Expr *pPrev = apToken[nToken-1];
2243 if( !pPrev->pOp || pPrev->pOp->eOp==OP_CLOSE_BRACKET ){
2244 continue;
2245 }
2246 }
2247 if( isMatch ){
 
2248 pNew->pOp = &aOperator[j];
2249 i += nOp;
2250 break;
2251 }
2252 }
@@ -2264,10 +2275,14 @@
2275 Th_Free(interp, pNew);
2276 rc = TH_ERROR;
2277 }
2278 }
2279 }
2280
2281 if( nNest!=0 ){
2282 rc = TH_ERROR;
2283 }
2284
2285 *papToken = apToken;
2286 *pnToken = nToken;
2287 return rc;
2288 }
2289
--- test/th1.test
+++ test/th1.test
@@ -482,10 +482,35 @@
482482
483483
###############################################################################
484484
485485
fossil test-th-eval "expr (-1)+1"
486486
test th1-expr-36 {$RESULT eq {0}}
487
+
488
+###############################################################################
489
+
490
+fossil test-th-eval "expr (((-1)))"
491
+test th1-expr-37 {$RESULT eq {-1}}
492
+
493
+###############################################################################
494
+
495
+fossil test-th-eval "expr (((1)))"
496
+test th1-expr-38 {$RESULT eq {1}}
497
+
498
+###############################################################################
499
+
500
+fossil test-th-eval "expr (((1))"
501
+test th1-expr-39 {$RESULT eq {TH_ERROR: syntax error in expression: "(((1))"}}
502
+
503
+###############################################################################
504
+
505
+fossil test-th-eval "expr ((1)))"
506
+test th1-expr-40 {$RESULT eq {TH_ERROR: syntax error in expression: "((1)))"}}
507
+
508
+###############################################################################
509
+
510
+fossil test-th-eval "expr (((1)*2)*2)"
511
+test th1-expr-41 {$RESULT eq {4}}
487512
488513
###############################################################################
489514
490515
fossil test-th-eval "checkout 1"; # NOTE: Assumes running "in tree".
491516
test th1-checkout-1 {[string length $RESULT] > 0}
492517
--- test/th1.test
+++ test/th1.test
@@ -482,10 +482,35 @@
482
483 ###############################################################################
484
485 fossil test-th-eval "expr (-1)+1"
486 test th1-expr-36 {$RESULT eq {0}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
488 ###############################################################################
489
490 fossil test-th-eval "checkout 1"; # NOTE: Assumes running "in tree".
491 test th1-checkout-1 {[string length $RESULT] > 0}
492
--- test/th1.test
+++ test/th1.test
@@ -482,10 +482,35 @@
482
483 ###############################################################################
484
485 fossil test-th-eval "expr (-1)+1"
486 test th1-expr-36 {$RESULT eq {0}}
487
488 ###############################################################################
489
490 fossil test-th-eval "expr (((-1)))"
491 test th1-expr-37 {$RESULT eq {-1}}
492
493 ###############################################################################
494
495 fossil test-th-eval "expr (((1)))"
496 test th1-expr-38 {$RESULT eq {1}}
497
498 ###############################################################################
499
500 fossil test-th-eval "expr (((1))"
501 test th1-expr-39 {$RESULT eq {TH_ERROR: syntax error in expression: "(((1))"}}
502
503 ###############################################################################
504
505 fossil test-th-eval "expr ((1)))"
506 test th1-expr-40 {$RESULT eq {TH_ERROR: syntax error in expression: "((1)))"}}
507
508 ###############################################################################
509
510 fossil test-th-eval "expr (((1)*2)*2)"
511 test th1-expr-41 {$RESULT eq {4}}
512
513 ###############################################################################
514
515 fossil test-th-eval "checkout 1"; # NOTE: Assumes running "in tree".
516 test th1-checkout-1 {[string length $RESULT] > 0}
517

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button