diff options
author | Ron Yorston <rmy@pobox.com> | 2023-07-13 08:06:26 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2023-07-13 08:06:26 +0100 |
commit | bd978d0256fd3a67de1a7dd54f1a37f9435be363 (patch) | |
tree | cb869384a533ac0d95fe787d75be6c050e1e7c1a /shell/math.c | |
parent | b2901ce8efa050da00e0f3a73f3be9bf9402deea (diff) | |
parent | d70256a5c719439cc6fab6a4571c1bb46178e4c7 (diff) | |
download | busybox-w32-bd978d0256fd3a67de1a7dd54f1a37f9435be363.tar.gz busybox-w32-bd978d0256fd3a67de1a7dd54f1a37f9435be363.tar.bz2 busybox-w32-bd978d0256fd3a67de1a7dd54f1a37f9435be363.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell/math.c')
-rw-r--r-- | shell/math.c | 713 |
1 files changed, 451 insertions, 262 deletions
diff --git a/shell/math.c b/shell/math.c index 76d22c9bd..e90a38f05 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -46,7 +46,6 @@ | |||
46 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | 46 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
47 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 47 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
48 | */ | 48 | */ |
49 | |||
50 | /* This is my infix parser/evaluator. It is optimized for size, intended | 49 | /* This is my infix parser/evaluator. It is optimized for size, intended |
51 | * as a replacement for yacc-based parsers. However, it may well be faster | 50 | * as a replacement for yacc-based parsers. However, it may well be faster |
52 | * than a comparable parser written in yacc. The supported operators are | 51 | * than a comparable parser written in yacc. The supported operators are |
@@ -61,7 +60,6 @@ | |||
61 | * to the stack instead of adding them to a queue to end up with an | 60 | * to the stack instead of adding them to a queue to end up with an |
62 | * expression). | 61 | * expression). |
63 | */ | 62 | */ |
64 | |||
65 | /* | 63 | /* |
66 | * Aug 24, 2001 Manuel Novoa III | 64 | * Aug 24, 2001 Manuel Novoa III |
67 | * | 65 | * |
@@ -96,7 +94,6 @@ | |||
96 | * | 94 | * |
97 | * Merge in Aaron's comments previously posted to the busybox list, | 95 | * Merge in Aaron's comments previously posted to the busybox list, |
98 | * modified slightly to take account of my changes to the code. | 96 | * modified slightly to take account of my changes to the code. |
99 | * | ||
100 | */ | 97 | */ |
101 | /* | 98 | /* |
102 | * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> | 99 | * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> |
@@ -116,6 +113,12 @@ | |||
116 | #include "libbb.h" | 113 | #include "libbb.h" |
117 | #include "math.h" | 114 | #include "math.h" |
118 | 115 | ||
116 | #if 1 | ||
117 | # define dbg(...) ((void)0) | ||
118 | #else | ||
119 | # define dbg(...) bb_error_msg(__VA_ARGS__) | ||
120 | #endif | ||
121 | |||
119 | typedef unsigned char operator; | 122 | typedef unsigned char operator; |
120 | 123 | ||
121 | /* An operator's token id is a bit of a bitfield. The lower 5 bits are the | 124 | /* An operator's token id is a bit of a bitfield. The lower 5 bits are the |
@@ -125,9 +128,13 @@ typedef unsigned char operator; | |||
125 | * Consider * and / | 128 | * Consider * and / |
126 | */ | 129 | */ |
127 | #define tok_decl(prec,id) (((id)<<5) | (prec)) | 130 | #define tok_decl(prec,id) (((id)<<5) | (prec)) |
128 | #define PREC(op) ((op) & 0x1F) | 131 | #define ID_SHIFT 5 |
132 | #define PREC(op) ((op) & 0x1f) | ||
129 | 133 | ||
134 | #define PREC_LPAREN 0 | ||
130 | #define TOK_LPAREN tok_decl(0,0) | 135 | #define TOK_LPAREN tok_decl(0,0) |
136 | /* Precedence value of RPAREN is used only to distinguish it from LPAREN */ | ||
137 | #define TOK_RPAREN tok_decl(1,1) | ||
131 | 138 | ||
132 | #define TOK_COMMA tok_decl(1,0) | 139 | #define TOK_COMMA tok_decl(1,0) |
133 | 140 | ||
@@ -135,22 +142,37 @@ typedef unsigned char operator; | |||
135 | * but there are 11 of them, which doesn't fit into 3 bits for unique id. | 142 | * but there are 11 of them, which doesn't fit into 3 bits for unique id. |
136 | * Abusing another precedence level: | 143 | * Abusing another precedence level: |
137 | */ | 144 | */ |
145 | #define PREC_ASSIGN1 2 | ||
138 | #define TOK_ASSIGN tok_decl(2,0) | 146 | #define TOK_ASSIGN tok_decl(2,0) |
139 | #define TOK_AND_ASSIGN tok_decl(2,1) | 147 | #define TOK_AND_ASSIGN tok_decl(2,1) |
140 | #define TOK_OR_ASSIGN tok_decl(2,2) | 148 | #define TOK_OR_ASSIGN tok_decl(2,2) |
141 | #define TOK_XOR_ASSIGN tok_decl(2,3) | 149 | #define TOK_XOR_ASSIGN tok_decl(2,3) |
142 | #define TOK_PLUS_ASSIGN tok_decl(2,4) | 150 | #define TOK_ADD_ASSIGN tok_decl(2,4) |
143 | #define TOK_MINUS_ASSIGN tok_decl(2,5) | 151 | #define TOK_SUB_ASSIGN tok_decl(2,5) |
144 | #define TOK_LSHIFT_ASSIGN tok_decl(2,6) | 152 | #define TOK_LSHIFT_ASSIGN tok_decl(2,6) |
145 | #define TOK_RSHIFT_ASSIGN tok_decl(2,7) | 153 | #define TOK_RSHIFT_ASSIGN tok_decl(2,7) |
146 | 154 | ||
155 | #define PREC_ASSIGN2 3 | ||
147 | #define TOK_MUL_ASSIGN tok_decl(3,0) | 156 | #define TOK_MUL_ASSIGN tok_decl(3,0) |
148 | #define TOK_DIV_ASSIGN tok_decl(3,1) | 157 | /* "/" and "/=" ops have the same id bits */ |
158 | #define DIV_ID1 1 | ||
159 | #define TOK_DIV_ASSIGN tok_decl(3,DIV_ID1) | ||
149 | #define TOK_REM_ASSIGN tok_decl(3,2) | 160 | #define TOK_REM_ASSIGN tok_decl(3,2) |
150 | 161 | ||
151 | #define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) | 162 | #define fix_assignment_prec(prec) do { prec -= (prec == 3); } while (0) |
152 | 163 | ||
153 | /* Ternary conditional operator is right associative too */ | 164 | /* Ternary conditional operator is right associative too */ |
165 | /* | ||
166 | * bash documentation says that precedence order is: | ||
167 | * ... | ||
168 | * expr ? expr1 : expr2 | ||
169 | * = *= /= %= += -= <<= >>= &= ^= |= | ||
170 | * exprA , exprB | ||
171 | * What it omits is that expr1 is parsed as if parenthesized | ||
172 | * (this matches the rules of ?: in C language): | ||
173 | * "v ? 1,2 : 3,4" is parsed as "(v ? (1,2) : 3),4" | ||
174 | * "v ? a=2 : b=4" is parsed as "(v ? (a=1) : b)=4" (thus, this is a syntax error) | ||
175 | */ | ||
154 | #define TOK_CONDITIONAL tok_decl(4,0) | 176 | #define TOK_CONDITIONAL tok_decl(4,0) |
155 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) | 177 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) |
156 | 178 | ||
@@ -179,7 +201,7 @@ typedef unsigned char operator; | |||
179 | #define TOK_SUB tok_decl(13,1) | 201 | #define TOK_SUB tok_decl(13,1) |
180 | 202 | ||
181 | #define TOK_MUL tok_decl(14,0) | 203 | #define TOK_MUL tok_decl(14,0) |
182 | #define TOK_DIV tok_decl(14,1) | 204 | #define TOK_DIV tok_decl(14,DIV_ID1) |
183 | #define TOK_REM tok_decl(14,2) | 205 | #define TOK_REM tok_decl(14,2) |
184 | 206 | ||
185 | /* Exponent is right associative */ | 207 | /* Exponent is right associative */ |
@@ -194,26 +216,25 @@ typedef unsigned char operator; | |||
194 | #define TOK_UPLUS tok_decl(UNARYPREC+1,1) | 216 | #define TOK_UPLUS tok_decl(UNARYPREC+1,1) |
195 | 217 | ||
196 | #define PREC_PRE (UNARYPREC+2) | 218 | #define PREC_PRE (UNARYPREC+2) |
197 | 219 | #define TOK_PRE_INC tok_decl(PREC_PRE,0) | |
198 | #define TOK_PRE_INC tok_decl(PREC_PRE, 0) | 220 | #define TOK_PRE_DEC tok_decl(PREC_PRE,1) |
199 | #define TOK_PRE_DEC tok_decl(PREC_PRE, 1) | ||
200 | 221 | ||
201 | #define PREC_POST (UNARYPREC+3) | 222 | #define PREC_POST (UNARYPREC+3) |
223 | #define TOK_POST_INC tok_decl(PREC_POST,0) | ||
224 | #define TOK_POST_DEC tok_decl(PREC_POST,1) | ||
202 | 225 | ||
203 | #define TOK_POST_INC tok_decl(PREC_POST, 0) | 226 | /* TOK_VALUE marks a number, name, name++/name--, or (EXPR): |
204 | #define TOK_POST_DEC tok_decl(PREC_POST, 1) | 227 | * IOW: something which can be used as the left side of a binary op. |
205 | 228 | * Since it's never pushed to opstack, its precedence does not matter. | |
206 | #define SPEC_PREC (UNARYPREC+4) | 229 | */ |
207 | 230 | #define TOK_VALUE tok_decl(PREC_POST,2) | |
208 | #define TOK_NUM tok_decl(SPEC_PREC, 0) | ||
209 | #define TOK_RPAREN tok_decl(SPEC_PREC, 1) | ||
210 | 231 | ||
211 | static int | 232 | static int |
212 | is_assign_op(operator op) | 233 | is_assign_op(operator op) |
213 | { | 234 | { |
214 | operator prec = PREC(op); | 235 | operator prec = PREC(op); |
215 | fix_assignment_prec(prec); | 236 | return prec == PREC_ASSIGN1 |
216 | return prec == PREC(TOK_ASSIGN) | 237 | || prec == PREC_ASSIGN2 |
217 | || prec == PREC_PRE | 238 | || prec == PREC_PRE |
218 | || prec == PREC_POST; | 239 | || prec == PREC_POST; |
219 | } | 240 | } |
@@ -226,91 +247,107 @@ is_right_associative(operator prec) | |||
226 | || prec == PREC(TOK_CONDITIONAL); | 247 | || prec == PREC(TOK_CONDITIONAL); |
227 | } | 248 | } |
228 | 249 | ||
229 | |||
230 | typedef struct { | 250 | typedef struct { |
231 | arith_t val; | 251 | arith_t val; |
232 | /* We acquire second_val only when "expr1 : expr2" part | 252 | const char *var_name; |
233 | * of ternary ?: op is evaluated. | ||
234 | * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). | ||
235 | * ':' produces a new value which has two parts, val and second_val; | ||
236 | * then '?' selects one of them based on its left side. | ||
237 | */ | ||
238 | arith_t second_val; | ||
239 | char second_val_present; | ||
240 | /* If NULL then it's just a number, else it's a named variable */ | ||
241 | char *var; | ||
242 | } var_or_num_t; | 253 | } var_or_num_t; |
243 | 254 | ||
255 | #define VALID_NAME(name) (name) | ||
256 | #define NOT_NAME(name) (!(name)) | ||
257 | |||
244 | typedef struct remembered_name { | 258 | typedef struct remembered_name { |
245 | struct remembered_name *next; | 259 | struct remembered_name *next; |
246 | const char *var; | 260 | const char *var_name; |
247 | } remembered_name; | 261 | } remembered_name; |
248 | 262 | ||
263 | static ALWAYS_INLINE int isalnum_(int c) | ||
264 | { | ||
265 | return (isalnum(c) || c == '_'); | ||
266 | } | ||
249 | 267 | ||
250 | static arith_t | 268 | static arith_t |
251 | evaluate_string(arith_state_t *math_state, const char *expr); | 269 | evaluate_string(arith_state_t *math_state, const char *expr); |
252 | 270 | ||
253 | static const char* | 271 | static arith_t |
254 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | 272 | arith_lookup_val(arith_state_t *math_state, const char *name, char *endname) |
255 | { | 273 | { |
256 | if (t->var) { | 274 | char c; |
257 | const char *p = math_state->lookupvar(t->var); | 275 | const char *p; |
258 | if (p) { | 276 | |
259 | remembered_name *cur; | 277 | c = *endname; |
260 | remembered_name cur_save; | 278 | *endname = '\0'; |
261 | 279 | p = math_state->lookupvar(name); | |
262 | /* did we already see this name? | 280 | *endname = c; |
263 | * testcase: a=b; b=a; echo $((a)) | 281 | if (p) { |
264 | */ | 282 | arith_t val; |
265 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | 283 | size_t len = endname - name; |
266 | if (strcmp(cur->var, t->var) == 0) { | 284 | remembered_name *cur; |
267 | /* Yes */ | 285 | remembered_name remember; |
268 | return "expression recursion loop detected"; | 286 | |
269 | } | 287 | /* did we already see this name? |
288 | * testcase: a=b; b=a; echo $((a)) | ||
289 | */ | ||
290 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | ||
291 | if (strncmp(cur->var_name, name, len) == 0 | ||
292 | && !isalnum_(cur->var_name[len]) | ||
293 | ) { | ||
294 | /* yes */ | ||
295 | math_state->errmsg = "expression recursion loop detected"; | ||
296 | return -1; | ||
270 | } | 297 | } |
298 | } | ||
271 | 299 | ||
272 | /* push current var name */ | 300 | /* push current var name */ |
273 | cur = math_state->list_of_recursed_names; | 301 | remember.var_name = name; |
274 | cur_save.var = t->var; | 302 | remember.next = math_state->list_of_recursed_names; |
275 | cur_save.next = cur; | 303 | math_state->list_of_recursed_names = &remember; |
276 | math_state->list_of_recursed_names = &cur_save; | ||
277 | 304 | ||
278 | /* recursively evaluate p as expression */ | 305 | /* recursively evaluate p as expression */ |
279 | t->val = evaluate_string(math_state, p); | 306 | /* this sets math_state->errmsg on error */ |
307 | val = evaluate_string(math_state, p); | ||
280 | 308 | ||
281 | /* pop current var name */ | 309 | /* pop current var name */ |
282 | math_state->list_of_recursed_names = cur; | 310 | math_state->list_of_recursed_names = remember.next; |
283 | 311 | ||
284 | return math_state->errmsg; | 312 | return val; |
285 | } | ||
286 | /* treat undefined var as 0 */ | ||
287 | t->val = 0; | ||
288 | } | 313 | } |
314 | /* treat undefined var as 0 */ | ||
289 | return 0; | 315 | return 0; |
290 | } | 316 | } |
291 | 317 | ||
292 | /* "Applying" a token means performing it on the top elements on the integer | 318 | /* "Applying" a token means performing it on the top elements on the integer |
293 | * stack. For an unary operator it will only change the top element, but a | 319 | * stack. For an unary operator it will only change the top element, |
294 | * binary operator will pop two arguments and push the result */ | 320 | * a binary operator will pop two arguments and push the result, |
321 | * the ternary ?: op will pop three arguments and push the result. | ||
322 | */ | ||
295 | static NOINLINE const char* | 323 | static NOINLINE const char* |
296 | arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) | 324 | arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) |
297 | { | 325 | { |
298 | #define NUMPTR (*numstackptr) | 326 | #define NUMSTACKPTR (*numstackptr) |
299 | 327 | ||
300 | var_or_num_t *top_of_stack; | 328 | var_or_num_t *top_of_stack; |
301 | arith_t rez; | 329 | arith_t rez; |
302 | const char *err; | ||
303 | 330 | ||
304 | /* There is no operator that can work without arguments */ | 331 | /* There is no operator that can work without arguments */ |
305 | if (NUMPTR == numstack) | 332 | if (NUMSTACKPTR == numstack) |
306 | goto err; | 333 | goto syntax_err; |
307 | 334 | ||
308 | top_of_stack = NUMPTR - 1; | 335 | top_of_stack = NUMSTACKPTR - 1; |
309 | 336 | ||
310 | /* Resolve name to value, if needed */ | 337 | if (op == TOK_CONDITIONAL_SEP) { |
311 | err = arith_lookup_val(math_state, top_of_stack); | 338 | /* "expr1 ? expr2 : expr3" operation */ |
312 | if (err) | 339 | var_or_num_t *expr1 = &top_of_stack[-2]; |
313 | return err; | 340 | NUMSTACKPTR = expr1 + 1; |
341 | if (expr1 < numstack) /* Example: $((2:3)) */ | ||
342 | return "malformed ?: operator"; | ||
343 | if (expr1->val != 0) /* select expr2 or expr3 */ | ||
344 | top_of_stack--; | ||
345 | rez = top_of_stack->val; | ||
346 | top_of_stack = expr1; | ||
347 | goto ret_rez; | ||
348 | } | ||
349 | if (op == TOK_CONDITIONAL) /* Example: $((a ? b)) */ | ||
350 | return "malformed ?: operator"; | ||
314 | 351 | ||
315 | rez = top_of_stack->val; | 352 | rez = top_of_stack->val; |
316 | if (op == TOK_UMINUS) | 353 | if (op == TOK_UMINUS) |
@@ -323,50 +360,30 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
323 | rez++; | 360 | rez++; |
324 | else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) | 361 | else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) |
325 | rez--; | 362 | rez--; |
326 | else if (op != TOK_UPLUS) { | 363 | else /*if (op != TOK_UPLUS) - always true, we drop TOK_UPLUS earlier */ { |
327 | /* Binary operators */ | 364 | /* Binary operators */ |
328 | arith_t right_side_val; | 365 | arith_t right_side_val; |
329 | char bad_second_val; | ||
330 | |||
331 | /* Binary operators need two arguments */ | ||
332 | if (top_of_stack == numstack) | ||
333 | goto err; | ||
334 | /* ...and they pop one */ | ||
335 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ | ||
336 | |||
337 | bad_second_val = top_of_stack->second_val_present; | ||
338 | if (op == TOK_CONDITIONAL) { /* ? operation */ | ||
339 | /* Make next if (...) protect against | ||
340 | * $((expr1 ? expr2)) - that is, missing ": expr" */ | ||
341 | bad_second_val = !bad_second_val; | ||
342 | } | ||
343 | if (bad_second_val) { | ||
344 | /* Protect against $((expr <not_?_op> expr1 : expr2)) */ | ||
345 | return "malformed ?: operator"; | ||
346 | } | ||
347 | 366 | ||
348 | top_of_stack--; /* now points to left side */ | 367 | if (top_of_stack == numstack) /* have two arguments? */ |
368 | goto syntax_err; /* no */ | ||
369 | |||
370 | /* Pop numstack */ | ||
371 | NUMSTACKPTR = top_of_stack; /* this decrements NUMSTACKPTR */ | ||
349 | 372 | ||
350 | if (op != TOK_ASSIGN) { | 373 | if (math_state->evaluation_disabled) { |
351 | /* Resolve left side value (unless the op is '=') */ | 374 | dbg("binary op %02x skipped", op); |
352 | err = arith_lookup_val(math_state, top_of_stack); | 375 | return NULL; |
353 | if (err) | 376 | /* bash 5.2.12 does not execute "2/0" in disabled |
354 | return err; | 377 | * branches of ?: (and thus does not complain), |
378 | * but complains about negative exp: "2**-1". | ||
379 | * I don't think we need to emulate that. | ||
380 | */ | ||
355 | } | 381 | } |
356 | 382 | ||
383 | top_of_stack--; /* now points to left side */ | ||
357 | right_side_val = rez; | 384 | right_side_val = rez; |
358 | rez = top_of_stack->val; | 385 | rez = top_of_stack->val; |
359 | if (op == TOK_CONDITIONAL) /* ? operation */ | 386 | if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
360 | rez = (rez ? right_side_val : top_of_stack[1].second_val); | ||
361 | else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ | ||
362 | if (top_of_stack == numstack) { | ||
363 | /* Protect against $((expr : expr)) */ | ||
364 | return "malformed ?: operator"; | ||
365 | } | ||
366 | top_of_stack->second_val_present = op; | ||
367 | top_of_stack->second_val = right_side_val; | ||
368 | } | ||
369 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) | ||
370 | rez |= right_side_val; | 387 | rez |= right_side_val; |
371 | else if (op == TOK_OR) | 388 | else if (op == TOK_OR) |
372 | rez = right_side_val || rez; | 389 | rez = right_side_val || rez; |
@@ -394,9 +411,9 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
394 | rez = (rez <= right_side_val); | 411 | rez = (rez <= right_side_val); |
395 | else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) | 412 | else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) |
396 | rez *= right_side_val; | 413 | rez *= right_side_val; |
397 | else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) | 414 | else if (op == TOK_ADD || op == TOK_ADD_ASSIGN) |
398 | rez += right_side_val; | 415 | rez += right_side_val; |
399 | else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) | 416 | else if (op == TOK_SUB || op == TOK_SUB_ASSIGN) |
400 | rez -= right_side_val; | 417 | rez -= right_side_val; |
401 | else if (op == TOK_ASSIGN || op == TOK_COMMA) | 418 | else if (op == TOK_ASSIGN || op == TOK_COMMA) |
402 | rez = right_side_val; | 419 | rez = right_side_val; |
@@ -405,14 +422,26 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
405 | if (right_side_val < 0) | 422 | if (right_side_val < 0) |
406 | return "exponent less than 0"; | 423 | return "exponent less than 0"; |
407 | c = 1; | 424 | c = 1; |
408 | while (--right_side_val >= 0) | 425 | while (right_side_val != 0) { |
426 | if ((right_side_val & 1) == 0) { | ||
427 | /* this if() block is not necessary for correctness, | ||
428 | * but otherwise echo $((3**999999999999999999)) | ||
429 | * takes a VERY LONG time | ||
430 | * (and it's not interruptible by ^C) | ||
431 | */ | ||
432 | rez *= rez; | ||
433 | right_side_val >>= 1; | ||
434 | } | ||
409 | c *= rez; | 435 | c *= rez; |
436 | right_side_val--; | ||
437 | } | ||
410 | rez = c; | 438 | rez = c; |
411 | } | 439 | } |
412 | else if (right_side_val == 0) | 440 | else /*if (op == TOK_DIV || op == TOK_DIV_ASSIGN |
413 | return "divide by zero"; | 441 | || op == TOK_REM || op == TOK_REM_ASSIGN) - always true */ |
414 | else if (op == TOK_DIV || op == TOK_DIV_ASSIGN | 442 | { |
415 | || op == TOK_REM || op == TOK_REM_ASSIGN) { | 443 | if (right_side_val == 0) |
444 | return "divide by zero"; | ||
416 | /* | 445 | /* |
417 | * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))' | 446 | * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))' |
418 | * | 447 | * |
@@ -424,42 +453,53 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ | |||
424 | * Make sure to at least not SEGV here: | 453 | * Make sure to at least not SEGV here: |
425 | */ | 454 | */ |
426 | if (right_side_val == -1 | 455 | if (right_side_val == -1 |
427 | && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */ | 456 | && (rez << 1) == 0 /* MAX_NEGATIVE_INT or 0 */ |
428 | ) { | 457 | ) { |
429 | right_side_val = 1; | 458 | right_side_val = 1; |
430 | } | 459 | } |
431 | if (op == TOK_DIV || op == TOK_DIV_ASSIGN) | 460 | if (op & (DIV_ID1 << ID_SHIFT)) /* DIV or DIV_ASSIGN? */ |
432 | rez /= right_side_val; | 461 | rez /= right_side_val; |
433 | else { | 462 | else |
434 | rez %= right_side_val; | 463 | rez %= right_side_val; |
435 | } | ||
436 | } | 464 | } |
437 | } | 465 | } |
438 | 466 | ||
467 | if (math_state->evaluation_disabled) { | ||
468 | dbg("unary op %02x skipped", op); | ||
469 | return NULL; | ||
470 | } | ||
471 | |||
439 | if (is_assign_op(op)) { | 472 | if (is_assign_op(op)) { |
440 | char buf[sizeof(arith_t)*3 + 2]; | 473 | char buf[sizeof(arith_t)*3 + 2]; |
441 | 474 | ||
442 | if (top_of_stack->var == NULL) { | 475 | if (NOT_NAME(top_of_stack->var_name)) { |
443 | /* Hmm, 1=2 ? */ | 476 | /* Hmm, 1=2 ? */ |
444 | goto err; | 477 | goto syntax_err; |
445 | } | 478 | } |
446 | /* Save to shell variable */ | 479 | /* Save to shell variable */ |
447 | sprintf(buf, ARITH_FMT, rez); | 480 | sprintf(buf, ARITH_FMT, rez); |
448 | math_state->setvar(top_of_stack->var, buf); | 481 | { |
449 | /* After saving, make previous value for v++ or v-- */ | 482 | char *e = (char*)endofname(top_of_stack->var_name); |
450 | if (op == TOK_POST_INC) | 483 | char c = *e; |
451 | rez--; | 484 | *e = '\0'; |
452 | if (op == TOK_POST_DEC) | 485 | math_state->setvar(top_of_stack->var_name, buf); |
453 | rez++; | 486 | *e = c; |
487 | } | ||
488 | /* VAR++ or VAR--? */ | ||
489 | if (PREC(op) == PREC_POST) { | ||
490 | /* Do not store new value to stack (keep old value) */ | ||
491 | goto ret_NULL; | ||
492 | } | ||
454 | } | 493 | } |
455 | 494 | ret_rez: | |
456 | top_of_stack->val = rez; | 495 | top_of_stack->val = rez; |
496 | ret_NULL: | ||
457 | /* Erase var name, it is just a number now */ | 497 | /* Erase var name, it is just a number now */ |
458 | top_of_stack->var = NULL; | 498 | top_of_stack->var_name = NULL; |
459 | return NULL; | 499 | return NULL; |
460 | err: | 500 | syntax_err: |
461 | return "arithmetic syntax error"; | 501 | return "arithmetic syntax error"; |
462 | #undef NUMPTR | 502 | #undef NUMSTACKPTR |
463 | } | 503 | } |
464 | 504 | ||
465 | /* longest must be first */ | 505 | /* longest must be first */ |
@@ -479,8 +519,8 @@ static const char op_tokens[] ALIGN1 = { | |||
479 | '*','=', 0, TOK_MUL_ASSIGN, | 519 | '*','=', 0, TOK_MUL_ASSIGN, |
480 | '/','=', 0, TOK_DIV_ASSIGN, | 520 | '/','=', 0, TOK_DIV_ASSIGN, |
481 | '%','=', 0, TOK_REM_ASSIGN, | 521 | '%','=', 0, TOK_REM_ASSIGN, |
482 | '+','=', 0, TOK_PLUS_ASSIGN, | 522 | '+','=', 0, TOK_ADD_ASSIGN, |
483 | '-','=', 0, TOK_MINUS_ASSIGN, | 523 | '-','=', 0, TOK_SUB_ASSIGN, |
484 | '-','-', 0, TOK_POST_DEC, | 524 | '-','-', 0, TOK_POST_DEC, |
485 | '^','=', 0, TOK_XOR_ASSIGN, | 525 | '^','=', 0, TOK_XOR_ASSIGN, |
486 | '+','+', 0, TOK_POST_INC, | 526 | '+','+', 0, TOK_POST_INC, |
@@ -497,7 +537,6 @@ static const char op_tokens[] ALIGN1 = { | |||
497 | '+', 0, TOK_ADD, | 537 | '+', 0, TOK_ADD, |
498 | '-', 0, TOK_SUB, | 538 | '-', 0, TOK_SUB, |
499 | '^', 0, TOK_BXOR, | 539 | '^', 0, TOK_BXOR, |
500 | /* uniq */ | ||
501 | '~', 0, TOK_BNOT, | 540 | '~', 0, TOK_BNOT, |
502 | ',', 0, TOK_COMMA, | 541 | ',', 0, TOK_COMMA, |
503 | '?', 0, TOK_CONDITIONAL, | 542 | '?', 0, TOK_CONDITIONAL, |
@@ -506,41 +545,26 @@ static const char op_tokens[] ALIGN1 = { | |||
506 | '(', 0, TOK_LPAREN, | 545 | '(', 0, TOK_LPAREN, |
507 | 0 | 546 | 0 |
508 | }; | 547 | }; |
509 | #define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) | 548 | #define END_POINTER (&op_tokens[sizeof(op_tokens)-1]) |
510 | 549 | ||
511 | #if ENABLE_FEATURE_SH_MATH_BASE | 550 | #if ENABLE_FEATURE_SH_MATH_BASE |
512 | static arith_t strto_arith_t(const char *nptr, char **endptr) | 551 | static arith_t parse_with_base(const char *nptr, char **endptr, unsigned base) |
513 | { | 552 | { |
514 | unsigned base; | 553 | arith_t n = 0; |
515 | arith_t n; | 554 | const char *start = nptr; |
516 | |||
517 | # if ENABLE_FEATURE_SH_MATH_64 | ||
518 | n = strtoull(nptr, endptr, 0); | ||
519 | # else | ||
520 | n = strtoul(nptr, endptr, 0); | ||
521 | # endif | ||
522 | if (**endptr != '#' | ||
523 | || (*nptr < '1' || *nptr > '9') | ||
524 | || (n < 2 || n > 64) | ||
525 | ) { | ||
526 | return n; | ||
527 | } | ||
528 | 555 | ||
529 | /* It's "N#nnnn" or "NN#nnnn" syntax, NN can't start with 0, | ||
530 | * NN is in 2..64 range. | ||
531 | */ | ||
532 | base = (unsigned)n; | ||
533 | n = 0; | ||
534 | nptr = *endptr + 1; | ||
535 | for (;;) { | 556 | for (;;) { |
536 | unsigned digit = (unsigned)*nptr - '0'; | 557 | unsigned digit = (unsigned)*nptr - '0'; |
537 | if (digit >= 10 /* not 0..9 */ | 558 | if (digit >= 10 /* not 0..9 */ |
538 | && digit <= 'z' - '0' /* needed to reject e.g. $((64#~)) */ | 559 | && digit <= 'z' - '0' /* reject e.g. $((64#~)) */ |
539 | ) { | 560 | ) { |
540 | /* in bases up to 36, case does not matter for a-z */ | 561 | /* current char is one of :;<=>?@A..Z[\]^_`a..z */ |
562 | |||
563 | /* in bases up to 36, case does not matter for a-z, | ||
564 | * map @A..Z and `a..z to 9..35: */ | ||
541 | digit = (unsigned)(*nptr | 0x20) - ('a' - 10); | 565 | digit = (unsigned)(*nptr | 0x20) - ('a' - 10); |
542 | if (base > 36 && *nptr <= '_') { | 566 | if (base > 36 && *nptr <= '_') { |
543 | /* otherwise, A-Z,@,_ are 36-61,62,63 */ | 567 | /* base > 36: A-Z,@,_ are 36-61,62,63 */ |
544 | if (*nptr == '_') | 568 | if (*nptr == '_') |
545 | digit = 63; | 569 | digit = 63; |
546 | else if (*nptr == '@') | 570 | else if (*nptr == '@') |
@@ -551,8 +575,8 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) | |||
551 | break; /* error: one of [\]^ */ | 575 | break; /* error: one of [\]^ */ |
552 | } | 576 | } |
553 | //bb_error_msg("ch:'%c'%d digit:%u", *nptr, *nptr, digit); | 577 | //bb_error_msg("ch:'%c'%d digit:%u", *nptr, *nptr, digit); |
554 | //if (digit < 10) - example where we need this? | 578 | if (digit < 10) /* reject e.g. $((36#@)) */ |
555 | // break; | 579 | break; |
556 | } | 580 | } |
557 | if (digit >= base) | 581 | if (digit >= base) |
558 | break; | 582 | break; |
@@ -560,15 +584,55 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) | |||
560 | n = n * base + digit; | 584 | n = n * base + digit; |
561 | nptr++; | 585 | nptr++; |
562 | } | 586 | } |
563 | /* Note: we do not set errno on bad chars, we just set a pointer | ||
564 | * to the first invalid char. For example, this allows | ||
565 | * "N#" (empty "nnnn" part): 64#+1 is a valid expression, | ||
566 | * it means 64# + 1, whereas 64#~... is not, since ~ is not a valid | ||
567 | * operator. | ||
568 | */ | ||
569 | *endptr = (char*)nptr; | 587 | *endptr = (char*)nptr; |
588 | /* "64#" and "64#+1" used to be valid expressions, but bash 5.2.15 | ||
589 | * no longer allow such, detect this: | ||
590 | */ | ||
591 | // NB: bash allows $((0x)), this is probably a bug... | ||
592 | if (nptr == start) | ||
593 | *endptr = NULL; /* there weren't any digits, bad */ | ||
570 | return n; | 594 | return n; |
571 | } | 595 | } |
596 | |||
597 | static arith_t strto_arith_t(const char *nptr, char **endptr) | ||
598 | { | ||
599 | /* NB: we do not use strtoull here to be bash-compatible: | ||
600 | * $((99999999999999999999)) is 7766279631452241919 | ||
601 | * (the 64-bit truncated value). | ||
602 | */ | ||
603 | unsigned base; | ||
604 | |||
605 | /* nptr[0] is '0'..'9' here */ | ||
606 | |||
607 | base = nptr[0] - '0'; | ||
608 | if (base == 0) { /* nptr[0] is '0' */ | ||
609 | base = 8; | ||
610 | if ((nptr[1] | 0x20) == 'x') { | ||
611 | base = 16; | ||
612 | nptr += 2; | ||
613 | } | ||
614 | // NB: bash allows $((0x)), this is probably a bug... | ||
615 | return parse_with_base(nptr, endptr, base); | ||
616 | } | ||
617 | |||
618 | /* base is 1..9 here */ | ||
619 | |||
620 | if (nptr[1] == '#') { | ||
621 | if (base > 1) | ||
622 | return parse_with_base(nptr + 2, endptr, base); | ||
623 | /* else: "1#NN", bash says "invalid arithmetic base" */ | ||
624 | } | ||
625 | |||
626 | if (isdigit(nptr[1]) && nptr[2] == '#') { | ||
627 | base = 10 * base + (nptr[1] - '0'); | ||
628 | /* base is at least 10 here */ | ||
629 | if (base <= 64) | ||
630 | return parse_with_base(nptr + 3, endptr, base); | ||
631 | /* else: bash says "invalid arithmetic base" */ | ||
632 | } | ||
633 | |||
634 | return parse_with_base(nptr, endptr, 10); | ||
635 | } | ||
572 | #else /* !ENABLE_FEATURE_SH_MATH_BASE */ | 636 | #else /* !ENABLE_FEATURE_SH_MATH_BASE */ |
573 | # if ENABLE_FEATURE_SH_MATH_64 | 637 | # if ENABLE_FEATURE_SH_MATH_64 |
574 | # define strto_arith_t(nptr, endptr) strtoull(nptr, endptr, 0) | 638 | # define strto_arith_t(nptr, endptr) strtoull(nptr, endptr, 0) |
@@ -580,23 +644,52 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) | |||
580 | static arith_t | 644 | static arith_t |
581 | evaluate_string(arith_state_t *math_state, const char *expr) | 645 | evaluate_string(arith_state_t *math_state, const char *expr) |
582 | { | 646 | { |
647 | /* Stack of integers/names */ | ||
648 | var_or_num_t *numstack, *numstackptr; | ||
649 | /* Stack of operator tokens */ | ||
650 | operator *opstack, *opstackptr; | ||
651 | /* To detect whether we are after a "value": */ | ||
583 | operator lasttok; | 652 | operator lasttok; |
653 | /* To insert implicit () in ?: ternary op: */ | ||
654 | operator insert_op = 0xff; | ||
655 | unsigned ternary_level = 0; | ||
584 | const char *errmsg; | 656 | const char *errmsg; |
585 | const char *start_expr = expr = skip_whitespace(expr); | 657 | const char *start_expr = expr = skip_whitespace(expr); |
586 | unsigned expr_len = strlen(expr) + 2; | 658 | |
587 | /* Stack of integers */ | 659 | { |
588 | /* The proof that there can be no more than strlen(startbuf)/2+1 | 660 | unsigned expr_len = strlen(expr); |
589 | * integers in any given correct or incorrect expression | 661 | /* If LOTS of whitespace, do not blow up the estimation */ |
590 | * is left as an exercise to the reader. */ | 662 | const char *p = expr; |
591 | var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); | 663 | while (*p) { |
592 | var_or_num_t *numstackptr = numstack; | 664 | /* in a run of whitespace, count only 1st char */ |
593 | /* Stack of operator tokens */ | 665 | if (isspace(*p)) { |
594 | operator *const stack = alloca(expr_len * sizeof(stack[0])); | 666 | while (p++, isspace(*p)) |
595 | operator *stackptr = stack; | 667 | expr_len--; |
668 | } else { | ||
669 | p++; | ||
670 | } | ||
671 | } | ||
672 | dbg("expr:'%s' expr_len:%u", expr, expr_len); | ||
673 | /* expr_len deep opstack is needed. Think "------------7". | ||
674 | * Only "?" operator temporarily needs two opstack slots | ||
675 | * (IOW: more than one slot), but its second slot (LPAREN) | ||
676 | * is popped off when ":" is reached. | ||
677 | */ | ||
678 | expr_len++; /* +1 for 1st LPAREN. See what $((1?)) pushes to opstack */ | ||
679 | opstackptr = opstack = alloca(expr_len * sizeof(opstack[0])); | ||
680 | /* There can be no more than (expr_len/2 + 1) | ||
681 | * integers/names in any given correct or incorrect expression. | ||
682 | * (modulo "09", "0v" cases where 2 chars are 2 ints/names, | ||
683 | * but we have code to detect that early) | ||
684 | */ | ||
685 | expr_len = (expr_len / 2) | ||
686 | + 1 /* "1+2" has two nums, 2 = len/2+1, NOT len/2 */; | ||
687 | numstackptr = numstack = alloca(expr_len * sizeof(numstack[0])); | ||
688 | } | ||
596 | 689 | ||
597 | /* Start with a left paren */ | 690 | /* Start with a left paren */ |
598 | *stackptr++ = lasttok = TOK_LPAREN; | 691 | dbg("(%d) op:TOK_LPAREN", (int)(opstackptr - opstack)); |
599 | errmsg = NULL; | 692 | *opstackptr++ = lasttok = TOK_LPAREN; |
600 | 693 | ||
601 | while (1) { | 694 | while (1) { |
602 | const char *p; | 695 | const char *p; |
@@ -607,8 +700,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
607 | if (*expr == '\0') { | 700 | if (*expr == '\0') { |
608 | if (expr == start_expr) { | 701 | if (expr == start_expr) { |
609 | /* Null expression */ | 702 | /* Null expression */ |
610 | numstack->val = 0; | 703 | return 0; |
611 | goto ret; | ||
612 | } | 704 | } |
613 | 705 | ||
614 | /* This is only reached after all tokens have been extracted from the | 706 | /* This is only reached after all tokens have been extracted from the |
@@ -616,46 +708,71 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
616 | * are to be applied in order. At the end, there should be a final | 708 | * are to be applied in order. At the end, there should be a final |
617 | * result on the integer stack */ | 709 | * result on the integer stack */ |
618 | 710 | ||
619 | if (expr != ptr_to_rparen + 1) { | 711 | if (expr != END_POINTER) { |
620 | /* If we haven't done so already, | 712 | /* If we haven't done so already, |
621 | * append a closing right paren | 713 | * append a closing right paren |
622 | * and let the loop process it */ | 714 | * and let the loop process it */ |
623 | expr = ptr_to_rparen; | 715 | expr = END_POINTER; |
624 | //bb_error_msg("expr=')'"); | 716 | op = TOK_RPAREN; |
625 | continue; | 717 | goto tok_found1; |
626 | } | 718 | } |
627 | /* At this point, we're done with the expression */ | 719 | /* At this point, we're done with the expression */ |
628 | if (numstackptr != numstack + 1) { | 720 | if (numstackptr != numstack + 1) { |
629 | /* ...but if there isn't, it's bad */ | 721 | /* if there is not exactly one result, it's bad */ |
630 | goto err; | 722 | /* Example: $((1 2)) */ |
723 | goto syntax_err; | ||
631 | } | 724 | } |
632 | goto ret; | 725 | return numstack->val; |
633 | } | 726 | } |
634 | 727 | ||
635 | p = endofname(expr); | 728 | p = endofname(expr); |
636 | if (p != expr) { | 729 | if (p != expr) { |
637 | /* Name */ | 730 | /* Name */ |
638 | size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ | 731 | if (!math_state->evaluation_disabled) { |
639 | numstackptr->var = alloca(var_name_size); | 732 | numstackptr->var_name = expr; |
640 | safe_strncpy(numstackptr->var, expr, var_name_size); | 733 | dbg("[%d] var:'%.*s'", (int)(numstackptr - numstack), (int)(p - expr), expr); |
641 | //bb_error_msg("var:'%s'", numstackptr->var); | 734 | expr = skip_whitespace(p); |
642 | expr = p; | 735 | /* If it is not followed by "=" operator... */ |
643 | num: | 736 | if (expr[0] != '=' /* not "=..." */ |
644 | numstackptr->second_val_present = 0; | 737 | || expr[1] == '=' /* or "==..." */ |
738 | ) { | ||
739 | /* Evaluate variable to value */ | ||
740 | arith_t val = arith_lookup_val(math_state, numstackptr->var_name, (char*)p); | ||
741 | if (math_state->errmsg) | ||
742 | return val; /* -1 */ | ||
743 | numstackptr->val = val; | ||
744 | } | ||
745 | } else { | ||
746 | dbg("[%d] var:IGNORED", (int)(numstackptr - numstack)); | ||
747 | expr = p; | ||
748 | numstackptr->var_name = NULL; /* not needed, paranoia */ | ||
749 | numstackptr->val = 0; /* not needed, paranoia */ | ||
750 | } | ||
751 | push_value: | ||
645 | numstackptr++; | 752 | numstackptr++; |
646 | lasttok = TOK_NUM; | 753 | lasttok = TOK_VALUE; |
647 | continue; | 754 | continue; |
648 | } | 755 | } |
649 | 756 | ||
650 | if (isdigit(*expr)) { | 757 | if (isdigit(*expr)) { |
651 | /* Number */ | 758 | /* Number */ |
652 | numstackptr->var = NULL; | 759 | char *end; |
653 | errno = 0; | 760 | numstackptr->var_name = NULL; |
654 | numstackptr->val = strto_arith_t(expr, (char**) &expr); | 761 | /* code is smaller compared to using &expr here: */ |
655 | //bb_error_msg("val:%lld", numstackptr->val); | 762 | numstackptr->val = strto_arith_t(expr, &end); |
656 | if (errno) | 763 | expr = end; |
657 | numstackptr->val = 0; /* bash compat */ | 764 | dbg("[%d] val:%lld", (int)(numstackptr - numstack), numstackptr->val); |
658 | goto num; | 765 | if (!expr) /* example: $((10#)) */ |
766 | goto syntax_err; | ||
767 | /* A number can't be followed by another number, or a variable name. | ||
768 | * We'd catch this later anyway, but this would require numstack[] | ||
769 | * to be ~twice as deep to handle strings where _every_ char is | ||
770 | * a new number or name. | ||
771 | * Examples: "09" is two numbers, "0v" is number and name. | ||
772 | */ | ||
773 | if (isalnum(*expr) || *expr == '_') | ||
774 | goto syntax_err; | ||
775 | goto push_value; | ||
659 | } | 776 | } |
660 | 777 | ||
661 | /* Should be an operator */ | 778 | /* Should be an operator */ |
@@ -671,10 +788,11 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
671 | if ((expr[0] == '+' || expr[0] == '-') | 788 | if ((expr[0] == '+' || expr[0] == '-') |
672 | && (expr[1] == expr[0]) | 789 | && (expr[1] == expr[0]) |
673 | ) { | 790 | ) { |
674 | if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */ | 791 | if (numstackptr == numstack || NOT_NAME(numstackptr[-1].var_name)) { |
792 | /* not a VAR++ */ | ||
675 | char next = skip_whitespace(expr + 2)[0]; | 793 | char next = skip_whitespace(expr + 2)[0]; |
676 | if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ | 794 | if (!(isalpha(next) || next == '_')) { |
677 | //bb_error_msg("special %c%c", expr[0], expr[0]); | 795 | /* not a ++VAR */ |
678 | op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); | 796 | op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); |
679 | expr++; | 797 | expr++; |
680 | goto tok_found1; | 798 | goto tok_found1; |
@@ -704,27 +822,41 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
704 | if (*p == '\0') { | 822 | if (*p == '\0') { |
705 | /* No next element, operator not found */ | 823 | /* No next element, operator not found */ |
706 | //math_state->syntax_error_at = expr; | 824 | //math_state->syntax_error_at = expr; |
707 | goto err; | 825 | goto syntax_err; |
708 | } | 826 | } |
709 | } | 827 | } |
828 | /* NB: expr now points past the operator */ | ||
710 | tok_found: | 829 | tok_found: |
711 | op = p[1]; /* fetch TOK_foo value */ | 830 | op = p[1]; /* fetch TOK_foo value */ |
712 | tok_found1: | ||
713 | /* NB: expr now points past the operator */ | ||
714 | 831 | ||
715 | /* post grammar: a++ reduce to num */ | 832 | /* Special rule for "? EXPR :" |
716 | if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) | 833 | * "EXPR in the middle of ? : is parsed as if parenthesized" |
717 | lasttok = TOK_NUM; | 834 | * (this quirk originates in C grammar, I think). |
835 | */ | ||
836 | if (op == TOK_CONDITIONAL) { | ||
837 | insert_op = TOK_LPAREN; | ||
838 | dbg("insert_op=%02x", insert_op); | ||
839 | } | ||
840 | if (op == TOK_CONDITIONAL_SEP) { | ||
841 | insert_op = op; | ||
842 | op = TOK_RPAREN; | ||
843 | dbg("insert_op=%02x op=%02x", insert_op, op); | ||
844 | } | ||
845 | tok_found1: | ||
846 | /* NAME++ is a "value" (something suitable for a binop) */ | ||
847 | if (PREC(lasttok) == PREC_POST) | ||
848 | lasttok = TOK_VALUE; | ||
718 | 849 | ||
719 | /* Plus and minus are binary (not unary) _only_ if the last | 850 | /* Plus and minus are binary (not unary) _only_ if the last |
720 | * token was a number, or a right paren (which pretends to be | 851 | * token was a "value". Think about it. It makes sense. |
721 | * a number, since it evaluates to one). Think about it. | 852 | */ |
722 | * It makes sense. */ | 853 | if (lasttok != TOK_VALUE) { |
723 | if (lasttok != TOK_NUM) { | ||
724 | switch (op) { | 854 | switch (op) { |
725 | case TOK_ADD: | 855 | case TOK_ADD: |
726 | op = TOK_UPLUS; | 856 | //op = TOK_UPLUS; |
727 | break; | 857 | //break; |
858 | /* Unary plus does nothing, do not even push it to opstack */ | ||
859 | continue; | ||
728 | case TOK_SUB: | 860 | case TOK_SUB: |
729 | op = TOK_UMINUS; | 861 | op = TOK_UMINUS; |
730 | break; | 862 | break; |
@@ -744,80 +876,137 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
744 | * stack until we find an operator with a lesser priority than the | 876 | * stack until we find an operator with a lesser priority than the |
745 | * one we have just extracted. If op is right-associative, | 877 | * one we have just extracted. If op is right-associative, |
746 | * then stop "applying" on the equal priority too. | 878 | * then stop "applying" on the equal priority too. |
747 | * Left paren is given the lowest priority so it will never be | 879 | * Left paren will never be "applied" in this way. |
748 | * "applied" in this way. | ||
749 | */ | 880 | */ |
750 | prec = PREC(op); | 881 | prec = PREC(op); |
751 | //bb_error_msg("prec:%02x", prec); | 882 | if (prec != PREC_LPAREN && prec < UNARYPREC) { |
752 | if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { | 883 | /* Binary, ternary or RPAREN */ |
753 | /* not left paren or unary */ | 884 | if (lasttok != TOK_VALUE) { |
754 | if (lasttok != TOK_NUM) { | 885 | /* Must be preceded by a value. |
755 | /* binary op must be preceded by a num */ | 886 | * $((2 2 + * 3)) would be accepted without this. |
756 | goto err; | 887 | */ |
888 | goto syntax_err; | ||
757 | } | 889 | } |
758 | /* The algorithm employed here is simple: while we don't | 890 | /* if op is RPAREN: |
759 | * hit an open paren nor the bottom of the stack, pop | 891 | * while opstack is not empty: |
760 | * tokens and apply them */ | 892 | * pop prev_op |
761 | while (stackptr != stack) { | 893 | * if prev_op is LPAREN (finished evaluating (EXPR)): |
762 | operator prev_op = *--stackptr; | 894 | * goto N |
895 | * evaluate prev_op on top of numstack | ||
896 | * BUG (unpaired RPAREN) | ||
897 | * else (op is not RPAREN): | ||
898 | * while opstack is not empty: | ||
899 | * pop prev_op | ||
900 | * if can't evaluate prev_op (it is lower precedence than op): | ||
901 | * push prev_op back | ||
902 | * goto C | ||
903 | * evaluate prev_op on top of numstack | ||
904 | * C:if op is "?": check result, set disable flag if needed | ||
905 | * push op | ||
906 | * N:loop to parse the rest of string | ||
907 | */ | ||
908 | while (opstackptr != opstack) { | ||
909 | operator prev_op = *--opstackptr; | ||
763 | if (op == TOK_RPAREN) { | 910 | if (op == TOK_RPAREN) { |
764 | //bb_error_msg("op == TOK_RPAREN"); | ||
765 | if (prev_op == TOK_LPAREN) { | 911 | if (prev_op == TOK_LPAREN) { |
766 | //bb_error_msg("prev_op == TOK_LPAREN"); | 912 | /* Erase var name: for example, (VAR) = 1 is not valid */ |
767 | //bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var); | 913 | numstackptr[-1].var_name = NULL; |
768 | if (numstackptr[-1].var) { | 914 | /* (EXPR) is a "value": next operator directly after |
769 | /* Expression is (var), lookup now */ | 915 | * close paren should be considered binary |
770 | errmsg = arith_lookup_val(math_state, &numstackptr[-1]); | 916 | */ |
771 | if (errmsg) | 917 | lasttok = TOK_VALUE; |
772 | goto err_with_custom_msg; | ||
773 | /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ | ||
774 | numstackptr[-1].var = NULL; | ||
775 | } | ||
776 | /* Any operator directly after a | ||
777 | * close paren should consider itself binary */ | ||
778 | lasttok = TOK_NUM; | ||
779 | goto next; | 918 | goto next; |
780 | } | 919 | } |
781 | //bb_error_msg("prev_op != TOK_LPAREN"); | 920 | /* Not (y), but ...x~y). Fall through to evaluate x~y */ |
782 | } else { | 921 | } else { |
783 | operator prev_prec = PREC(prev_op); | 922 | operator prev_prec = PREC(prev_op); |
784 | //bb_error_msg("op != TOK_RPAREN"); | ||
785 | fix_assignment_prec(prec); | 923 | fix_assignment_prec(prec); |
786 | fix_assignment_prec(prev_prec); | 924 | fix_assignment_prec(prev_prec); |
787 | if (prev_prec < prec | 925 | if (prev_prec < prec |
788 | || (prev_prec == prec && is_right_associative(prec)) | 926 | || (prev_prec == prec && is_right_associative(prec)) |
789 | ) { | 927 | ) { |
790 | stackptr++; | 928 | /* ...x~y@. push @ on opstack */ |
791 | break; | 929 | opstackptr++; /* undo removal of ~ op */ |
930 | goto check_cond; | ||
792 | } | 931 | } |
932 | /* else: ...x~y@. Evaluate x~y, replace it on stack with result. Then repeat */ | ||
793 | } | 933 | } |
794 | //bb_error_msg("arith_apply(prev_op:%02x)", prev_op); | 934 | dbg("arith_apply(prev_op:%02x, numstack:%d)", prev_op, (int)(numstackptr - numstack)); |
795 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); | 935 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); |
796 | if (errmsg) | 936 | if (errmsg) |
797 | goto err_with_custom_msg; | 937 | goto err_with_custom_msg; |
938 | dbg(" numstack:%d val:%lld '%s'", (int)(numstackptr - numstack), numstackptr[-1].val, numstackptr[-1].var_name); | ||
939 | if (prev_op == TOK_CONDITIONAL_SEP) { | ||
940 | /* We just executed ":" */ | ||
941 | /* Remove "?" from opstack too, not just ":" */ | ||
942 | opstackptr--; | ||
943 | if (*opstackptr != TOK_CONDITIONAL) { | ||
944 | /* Example: $((1,2:3)) */ | ||
945 | errmsg = "malformed ?: operator"; | ||
946 | goto err_with_custom_msg; | ||
947 | } | ||
948 | /* Example: a=1?2:3,a. We just executed ":". | ||
949 | * Prevent assignment from being still disabled. | ||
950 | */ | ||
951 | if (ternary_level == math_state->evaluation_disabled) { | ||
952 | math_state->evaluation_disabled = 0; | ||
953 | dbg("':' executed: evaluation_disabled=CLEAR"); | ||
954 | } | ||
955 | ternary_level--; | ||
956 | } | ||
957 | } /* while (opstack not empty) */ | ||
958 | |||
959 | if (op == TOK_RPAREN) /* unpaired RPAREN? */ | ||
960 | goto syntax_err; | ||
961 | check_cond: | ||
962 | if (op == TOK_CONDITIONAL) { | ||
963 | /* We just now evaluated EXPR before "?". | ||
964 | * Should we disable evaluation now? | ||
965 | */ | ||
966 | ternary_level++; | ||
967 | if (numstackptr[-1].val == 0 && !math_state->evaluation_disabled) { | ||
968 | math_state->evaluation_disabled = ternary_level; | ||
969 | dbg("'?' entered: evaluation_disabled=%u", math_state->evaluation_disabled); | ||
970 | } | ||
971 | } | ||
972 | } /* if */ | ||
973 | /* else: LPAREN or UNARY: push it on opstack */ | ||
974 | |||
975 | /* Push this operator to opstack */ | ||
976 | dbg("(%d) op:%02x insert_op:%02x", (int)(opstackptr - opstack), op, insert_op); | ||
977 | *opstackptr++ = lasttok = op; | ||
978 | next: | ||
979 | if (insert_op != 0xff) { | ||
980 | op = insert_op; | ||
981 | insert_op = 0xff; | ||
982 | dbg("inserting %02x", op); | ||
983 | if (op == TOK_CONDITIONAL_SEP) { | ||
984 | /* The next token is ":". Toggle "do not evaluate" state */ | ||
985 | if (!math_state->evaluation_disabled) { | ||
986 | math_state->evaluation_disabled = ternary_level; | ||
987 | dbg("':' entered: evaluation_disabled=%u", math_state->evaluation_disabled); | ||
988 | } else if (ternary_level == math_state->evaluation_disabled) { | ||
989 | math_state->evaluation_disabled = 0; | ||
990 | dbg("':' entered: evaluation_disabled=CLEAR"); | ||
991 | } /* else: ternary_level > evaluation_disabled && evaluation_disabled != 0 */ | ||
992 | /* We are in nested "?:" while in outer "?:" disabled branch */ | ||
993 | /* do_nothing */ | ||
798 | } | 994 | } |
799 | if (op == TOK_RPAREN) | 995 | goto tok_found1; |
800 | goto err; | ||
801 | } | 996 | } |
802 | |||
803 | /* Push this operator to the stack and remember it */ | ||
804 | //bb_error_msg("push op:%02x", op); | ||
805 | *stackptr++ = lasttok = op; | ||
806 | next: ; | ||
807 | } /* while (1) */ | 997 | } /* while (1) */ |
808 | 998 | ||
809 | err: | 999 | syntax_err: |
810 | errmsg = "arithmetic syntax error"; | 1000 | errmsg = "arithmetic syntax error"; |
811 | err_with_custom_msg: | 1001 | err_with_custom_msg: |
812 | numstack->val = -1; | ||
813 | ret: | ||
814 | math_state->errmsg = errmsg; | 1002 | math_state->errmsg = errmsg; |
815 | return numstack->val; | 1003 | return -1; |
816 | } | 1004 | } |
817 | 1005 | ||
818 | arith_t FAST_FUNC | 1006 | arith_t FAST_FUNC |
819 | arith(arith_state_t *math_state, const char *expr) | 1007 | arith(arith_state_t *math_state, const char *expr) |
820 | { | 1008 | { |
1009 | math_state->evaluation_disabled = 0; | ||
821 | math_state->errmsg = NULL; | 1010 | math_state->errmsg = NULL; |
822 | math_state->list_of_recursed_names = NULL; | 1011 | math_state->list_of_recursed_names = NULL; |
823 | return evaluate_string(math_state, expr); | 1012 | return evaluate_string(math_state, expr); |