aboutsummaryrefslogtreecommitdiff
path: root/shell/math.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2023-07-13 08:06:26 +0100
committerRon Yorston <rmy@pobox.com>2023-07-13 08:06:26 +0100
commitbd978d0256fd3a67de1a7dd54f1a37f9435be363 (patch)
treecb869384a533ac0d95fe787d75be6c050e1e7c1a /shell/math.c
parentb2901ce8efa050da00e0f3a73f3be9bf9402deea (diff)
parentd70256a5c719439cc6fab6a4571c1bb46178e4c7 (diff)
downloadbusybox-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.c713
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
119typedef unsigned char operator; 122typedef 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
211static int 232static int
212is_assign_op(operator op) 233is_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
230typedef struct { 250typedef 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
244typedef struct remembered_name { 258typedef 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
263static ALWAYS_INLINE int isalnum_(int c)
264{
265 return (isalnum(c) || c == '_');
266}
249 267
250static arith_t 268static arith_t
251evaluate_string(arith_state_t *math_state, const char *expr); 269evaluate_string(arith_state_t *math_state, const char *expr);
252 270
253static const char* 271static arith_t
254arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) 272arith_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 */
295static NOINLINE const char* 323static NOINLINE const char*
296arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) 324arith_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
512static arith_t strto_arith_t(const char *nptr, char **endptr) 551static 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
597static 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)
580static arith_t 644static arith_t
581evaluate_string(arith_state_t *math_state, const char *expr) 645evaluate_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;
938dbg(" 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
818arith_t FAST_FUNC 1006arith_t FAST_FUNC
819arith(arith_state_t *math_state, const char *expr) 1007arith(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);