aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/ash_test/ash-arith/arith-precedence1.right3
-rwxr-xr-xshell/ash_test/ash-arith/arith-precedence1.tests13
-rw-r--r--shell/math.c77
3 files changed, 65 insertions, 28 deletions
diff --git a/shell/ash_test/ash-arith/arith-precedence1.right b/shell/ash_test/ash-arith/arith-precedence1.right
index 7f407b5d2..3f9320a13 100644
--- a/shell/ash_test/ash-arith/arith-precedence1.right
+++ b/shell/ash_test/ash-arith/arith-precedence1.right
@@ -1 +1,4 @@
14:4 14:4
24:4
34:4
44:4
diff --git a/shell/ash_test/ash-arith/arith-precedence1.tests b/shell/ash_test/ash-arith/arith-precedence1.tests
index 964ae4ee2..bfef05292 100755
--- a/shell/ash_test/ash-arith/arith-precedence1.tests
+++ b/shell/ash_test/ash-arith/arith-precedence1.tests
@@ -1,2 +1,15 @@
1exec 2>&1 1exec 2>&1
2# bash documentation says that precedence order is:
3# ...
4# expr ? expr1 : expr2
5# = *= /= %= += -= <<= >>= &= ^= |=
6# exprA , exprB
7# but in practice, the rules for expr1 and expr2 are different:
8# assignments and commas in expr1 have higher precedence than :?,
9# but in expr2 they haven't:
10# "v ? 1,2 : 3,4" is parsed as "(v ? (1,2) : 3),4"
11# "v ? a=2 : b=4" is parsed as "(v ? (a=1) : b)=4" (thus, this is a syntax error)
12echo 4:$((0 ? 1,2 : 3,4))
13echo 4:$((1 ? 1,2 : 3,4))
2echo 4:"$((0 ? 1,2 : 3,4))" 14echo 4:"$((0 ? 1,2 : 3,4))"
15echo 4:"$((1 ? 1,2 : 3,4))"
diff --git a/shell/math.c b/shell/math.c
index 57eb56952..d5f3ce361 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -116,6 +116,12 @@
116#include "libbb.h" 116#include "libbb.h"
117#include "math.h" 117#include "math.h"
118 118
119#if 1
120# define dbg(...) ((void)0)
121#else
122# define dbg(...) bb_error_msg(__VA_ARGS__)
123#endif
124
119typedef unsigned char operator; 125typedef unsigned char operator;
120 126
121/* An operator's token id is a bit of a bitfield. The lower 5 bits are the 127/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
@@ -151,6 +157,17 @@ typedef unsigned char operator;
151#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) 157#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0)
152 158
153/* Ternary conditional operator is right associative too */ 159/* Ternary conditional operator is right associative too */
160// FIXME:
161// bash documentation says that precedence order is:
162// ...
163// expr ? expr1 : expr2
164// = *= /= %= += -= <<= >>= &= ^= |=
165// exprA , exprB
166// but in practice, the rules for expr1 and expr2 are different:
167// assignments and commas in expr1 have higher precedence than ?:,
168// but in expr2 they haven't:
169// "v ? 1,2 : 3,4" is parsed as "(v ? (1,2) : 3),4"
170// "v ? a=2 : b=4" is parsed as "(v ? (a=1) : b)=4" (thus, this is a syntax error)
154#define TOK_CONDITIONAL tok_decl(4,0) 171#define TOK_CONDITIONAL tok_decl(4,0)
155#define TOK_CONDITIONAL_SEP tok_decl(4,1) 172#define TOK_CONDITIONAL_SEP tok_decl(4,1)
156 173
@@ -246,7 +263,7 @@ typedef struct {
246} var_or_num_t; 263} var_or_num_t;
247 264
248#define VALID_NAME(name) ((name) && (name) != SECOND_VAL_VALID) 265#define VALID_NAME(name) ((name) && (name) != SECOND_VAL_VALID)
249#define NOT_NAME(name) (!(name) || (name) == SECOND_VAL_VALID) 266#define NOT_NAME(name) (!(name) || (name) == SECOND_VAL_VALID)
250 267
251 268
252typedef struct remembered_name { 269typedef struct remembered_name {
@@ -624,11 +641,12 @@ evaluate_string(arith_state_t *math_state, const char *expr)
624 var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); 641 var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0]));
625 var_or_num_t *numstackptr = numstack; 642 var_or_num_t *numstackptr = numstack;
626 /* Stack of operator tokens */ 643 /* Stack of operator tokens */
627 operator *const stack = alloca(expr_len * sizeof(stack[0])); 644 operator *const opstack = alloca(expr_len * sizeof(opstack[0]));
628 operator *stackptr = stack; 645 operator *opstackptr = opstack;
629 646
630 /* Start with a left paren */ 647 /* Start with a left paren */
631 *stackptr++ = lasttok = TOK_LPAREN; 648 dbg("(%d) op:TOK_LPAREN", (int)(opstackptr - opstack));
649 *opstackptr++ = lasttok = TOK_LPAREN;
632 errmsg = NULL; 650 errmsg = NULL;
633 651
634 while (1) { 652 while (1) {
@@ -655,11 +673,11 @@ evaluate_string(arith_state_t *math_state, const char *expr)
655 * and let the loop process it */ 673 * and let the loop process it */
656 expr = ptr_to_rparen; 674 expr = ptr_to_rparen;
657//bb_error_msg("expr=')'"); 675//bb_error_msg("expr=')'");
658 continue; 676 goto tok_find;
659 } 677 }
660 /* At this point, we're done with the expression */ 678 /* At this point, we're done with the expression */
661 if (numstackptr != numstack + 1) { 679 if (numstackptr != numstack + 1) {
662 /* ...but if there isn't, it's bad */ 680 /* if there is not exactly one result, it's bad */
663 goto err; 681 goto err;
664 } 682 }
665 goto ret; 683 goto ret;
@@ -671,9 +689,9 @@ evaluate_string(arith_state_t *math_state, const char *expr)
671 size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ 689 size_t var_name_size = (p - expr) + 1; /* +1 for NUL */
672 numstackptr->var_name = alloca(var_name_size); 690 numstackptr->var_name = alloca(var_name_size);
673 safe_strncpy(numstackptr->var_name, expr, var_name_size); 691 safe_strncpy(numstackptr->var_name, expr, var_name_size);
674//bb_error_msg("var:'%s'", numstackptr->var); 692 dbg("[%d] var:'%s'", (int)(numstackptr - numstack), numstackptr->var_name);
675 expr = p; 693 expr = p;
676 num: 694 push_num:
677 numstackptr++; 695 numstackptr++;
678 lasttok = TOK_NUM; 696 lasttok = TOK_NUM;
679 continue; 697 continue;
@@ -684,6 +702,7 @@ evaluate_string(arith_state_t *math_state, const char *expr)
684 numstackptr->var_name = NULL; 702 numstackptr->var_name = NULL;
685 errno = 0; 703 errno = 0;
686 numstackptr->val = strto_arith_t(expr, (char**) &expr); 704 numstackptr->val = strto_arith_t(expr, (char**) &expr);
705 dbg("[%d] val:%lld", (int)(numstackptr - numstack), numstackptr->val);
687 /* A number can't be followed by another number, or a variable name. 706 /* A number can't be followed by another number, or a variable name.
688 * We'd catch this later anyway, but this would require numstack[] 707 * We'd catch this later anyway, but this would require numstack[]
689 * to be twice as deep to handle strings where _every_ char is 708 * to be twice as deep to handle strings where _every_ char is
@@ -691,10 +710,9 @@ evaluate_string(arith_state_t *math_state, const char *expr)
691 */ 710 */
692 if (isalnum(*expr) || *expr == '_') 711 if (isalnum(*expr) || *expr == '_')
693 goto err; 712 goto err;
694//bb_error_msg("val:%lld", numstackptr->val);
695 if (errno) 713 if (errno)
696 numstackptr->val = 0; /* bash compat */ 714 numstackptr->val = 0; /* bash compat */
697 goto num; 715 goto push_num;
698 } 716 }
699 717
700 /* Should be an operator */ 718 /* Should be an operator */
@@ -715,14 +733,13 @@ evaluate_string(arith_state_t *math_state, const char *expr)
715 char next = skip_whitespace(expr + 2)[0]; 733 char next = skip_whitespace(expr + 2)[0];
716 if (!(isalpha(next) || next == '_')) { 734 if (!(isalpha(next) || next == '_')) {
717 /* not a ++VAR */ 735 /* not a ++VAR */
718 //bb_error_msg("special %c%c", expr[0], expr[0]);
719 op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); 736 op = (expr[0] == '+' ? TOK_ADD : TOK_SUB);
720 expr++; 737 expr++;
721 goto tok_found1; 738 goto tok_found1;
722 } 739 }
723 } 740 }
724 } 741 }
725 742 tok_find:
726 p = op_tokens; 743 p = op_tokens;
727 while (1) { 744 while (1) {
728 /* Compare expr to current op_tokens[] element */ 745 /* Compare expr to current op_tokens[] element */
@@ -799,13 +816,10 @@ evaluate_string(arith_state_t *math_state, const char *expr)
799 /* The algorithm employed here is simple: while we don't 816 /* The algorithm employed here is simple: while we don't
800 * hit an open paren nor the bottom of the stack, pop 817 * hit an open paren nor the bottom of the stack, pop
801 * tokens and apply them */ 818 * tokens and apply them */
802 while (stackptr != stack) { 819 while (opstackptr != opstack) {
803 operator prev_op = *--stackptr; 820 operator prev_op = *--opstackptr;
804 if (op == TOK_RPAREN) { 821 if (op == TOK_RPAREN) {
805//bb_error_msg("op == TOK_RPAREN");
806 if (prev_op == TOK_LPAREN) { 822 if (prev_op == TOK_LPAREN) {
807//bb_error_msg("prev_op == TOK_LPAREN");
808//bb_error_msg(" %p %p numstackptr[-1].var_name:'%s'", numstack, numstackptr-1, numstackptr[-1].var_name);
809 if (VALID_NAME(numstackptr[-1].var_name)) { 823 if (VALID_NAME(numstackptr[-1].var_name)) {
810 /* Expression is (var), lookup now */ 824 /* Expression is (var), lookup now */
811 errmsg = arith_lookup_val(math_state, &numstackptr[-1]); 825 errmsg = arith_lookup_val(math_state, &numstackptr[-1]);
@@ -819,31 +833,38 @@ evaluate_string(arith_state_t *math_state, const char *expr)
819 lasttok = TOK_NUM; 833 lasttok = TOK_NUM;
820 goto next; 834 goto next;
821 } 835 }
822//bb_error_msg("prev_op != TOK_LPAREN"); 836 /* Not (y), but ...x~y): fall through to evaluate x~y */
823 } else { 837 } else {
824 operator prev_prec = PREC(prev_op); 838 operator prev_prec = PREC(prev_op);
825//bb_error_msg("op != TOK_RPAREN");
826 fix_assignment_prec(prec); 839 fix_assignment_prec(prec);
827 fix_assignment_prec(prev_prec); 840 fix_assignment_prec(prev_prec);
828 if (prev_prec < prec 841 if (prev_prec < prec
829 || (prev_prec == prec && is_right_associative(prec)) 842 || (prev_prec == prec && is_right_associative(prec))
830 ) { 843 ) {
831 stackptr++; 844 /* ...x~y@: push @ on opstack */
832 break; 845 opstackptr++; /* undo removal of ~ op */
846 goto push_op;
833 } 847 }
848 /* ...x~y@: evaluate x~y, replace it on stack with result. Then repeat */
834 } 849 }
835//bb_error_msg("arith_apply(prev_op:%02x)", prev_op); 850 dbg("arith_apply(prev_op:%02x, numstack:%d)", prev_op, (int)(numstackptr - numstack));
836 errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); 851 errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr);
837 if (errmsg) 852 if (errmsg)
838 goto err_with_custom_msg; 853 goto err_with_custom_msg;
839 } 854dbg(" numstack:%d val:%lld %lld %p", (int)(numstackptr - numstack),
840 if (op == TOK_RPAREN) 855 numstackptr[-1].val,
856 numstackptr[-1].var_name == SECOND_VAL_VALID ? numstackptr[-1].second_val : 0,
857 numstackptr[-1].var_name
858);
859 } /* while (opstack not empty) */
860 if (op == TOK_RPAREN) /* unpaired RPAREN? */
841 goto err; 861 goto err;
842 } 862 }
843 863 /* else: LPAREN or UNARY: push it on opstack */
844 /* Push this operator to the stack and remember it */ 864 push_op:
845//bb_error_msg("push op:%02x", op); 865 /* Push this operator to opstack */
846 *stackptr++ = lasttok = op; 866 dbg("(%d) op:%02x", (int)(opstackptr - opstack), op);
867 *opstackptr++ = lasttok = op;
847 next: ; 868 next: ;
848 } /* while (1) */ 869 } /* while (1) */
849 870