Fossil SCM
Fix TH1 expression parsing when there are unbalanced parenthesis. Add more tests.
Commit
00c01d7a36c8526c4adb6dcf2a1af5c1bb297ed9
Parent
cd64405786ee7d6…
2 files changed
+49
-34
+25
M
src/th.c
+49
-34
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -1774,10 +1774,11 @@ | ||
| 1774 | 1774 | ** the expression module means the Th_Expr() and exprXXX() functions. |
| 1775 | 1775 | */ |
| 1776 | 1776 | typedef struct Operator Operator; |
| 1777 | 1777 | struct Operator { |
| 1778 | 1778 | const char *zOp; |
| 1779 | + int nOp; | |
| 1779 | 1780 | int eOp; |
| 1780 | 1781 | int iPrecedence; |
| 1781 | 1782 | int eArgType; |
| 1782 | 1783 | }; |
| 1783 | 1784 | typedef struct Expr Expr; |
| @@ -1831,47 +1832,47 @@ | ||
| 1831 | 1832 | #define ARG_NUMBER 2 |
| 1832 | 1833 | #define ARG_STRING 3 |
| 1833 | 1834 | |
| 1834 | 1835 | static Operator aOperator[] = { |
| 1835 | 1836 | |
| 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}, | |
| 1838 | 1839 | |
| 1839 | 1840 | /* 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}, | |
| 1844 | 1845 | |
| 1845 | 1846 | /* Binary operators. It is important to the parsing in Th_Expr() that |
| 1846 | 1847 | * the two-character symbols ("==") appear before the one-character |
| 1847 | 1848 | * ones ("="). And that the priorities of all binary operators are |
| 1848 | 1849 | * integers between 2 and 12. |
| 1849 | 1850 | */ |
| 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} | |
| 1873 | 1874 | }; |
| 1874 | 1875 | |
| 1875 | 1876 | /* |
| 1876 | 1877 | ** The first part of the string (zInput,nInput) contains an integer. |
| 1877 | 1878 | ** Set *pnVarname to the number of bytes in the numeric string. |
| @@ -2178,10 +2179,11 @@ | ||
| 2178 | 2179 | int *pnToken /* OUT: Size of token array */ |
| 2179 | 2180 | ){ |
| 2180 | 2181 | int i; |
| 2181 | 2182 | |
| 2182 | 2183 | int rc = TH_OK; |
| 2184 | + int nNest = 0; | |
| 2183 | 2185 | int nToken = 0; |
| 2184 | 2186 | Expr **apToken = 0; |
| 2185 | 2187 | |
| 2186 | 2188 | for(i=0; rc==TH_OK && i<nExpr; ){ |
| 2187 | 2189 | char c = zExpr[i]; |
| @@ -2222,20 +2224,29 @@ | ||
| 2222 | 2224 | break; |
| 2223 | 2225 | } |
| 2224 | 2226 | |
| 2225 | 2227 | default: { |
| 2226 | 2228 | 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 ){ | |
| 2230 | 2242 | Expr *pPrev = apToken[nToken-1]; |
| 2231 | 2243 | if( !pPrev->pOp || pPrev->pOp->eOp==OP_CLOSE_BRACKET ){ |
| 2232 | 2244 | continue; |
| 2233 | 2245 | } |
| 2234 | 2246 | } |
| 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 ){ | |
| 2237 | 2248 | pNew->pOp = &aOperator[j]; |
| 2238 | 2249 | i += nOp; |
| 2239 | 2250 | break; |
| 2240 | 2251 | } |
| 2241 | 2252 | } |
| @@ -2264,10 +2275,14 @@ | ||
| 2264 | 2275 | Th_Free(interp, pNew); |
| 2265 | 2276 | rc = TH_ERROR; |
| 2266 | 2277 | } |
| 2267 | 2278 | } |
| 2268 | 2279 | } |
| 2280 | + | |
| 2281 | + if( nNest!=0 ){ | |
| 2282 | + rc = TH_ERROR; | |
| 2283 | + } | |
| 2269 | 2284 | |
| 2270 | 2285 | *papToken = apToken; |
| 2271 | 2286 | *pnToken = nToken; |
| 2272 | 2287 | return rc; |
| 2273 | 2288 | } |
| 2274 | 2289 |
| --- 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 |
+25
| --- test/th1.test | ||
| +++ test/th1.test | ||
| @@ -482,10 +482,35 @@ | ||
| 482 | 482 | |
| 483 | 483 | ############################################################################### |
| 484 | 484 | |
| 485 | 485 | fossil test-th-eval "expr (-1)+1" |
| 486 | 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}} | |
| 487 | 512 | |
| 488 | 513 | ############################################################################### |
| 489 | 514 | |
| 490 | 515 | fossil test-th-eval "checkout 1"; # NOTE: Assumes running "in tree". |
| 491 | 516 | test th1-checkout-1 {[string length $RESULT] > 0} |
| 492 | 517 |
| --- 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 |