diff options
-rw-r--r-- | shell/ash_test/ash-arith/arith-precedence1.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-arith/arith-precedence1.tests | 13 | ||||
-rw-r--r-- | shell/math.c | 77 |
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 @@ | |||
1 | 4:4 | 1 | 4:4 |
2 | 4:4 | ||
3 | 4:4 | ||
4 | 4: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 @@ | |||
1 | exec 2>&1 | 1 | exec 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) | ||
12 | echo 4:$((0 ? 1,2 : 3,4)) | ||
13 | echo 4:$((1 ? 1,2 : 3,4)) | ||
2 | echo 4:"$((0 ? 1,2 : 3,4))" | 14 | echo 4:"$((0 ? 1,2 : 3,4))" |
15 | echo 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 | |||
119 | typedef unsigned char operator; | 125 | typedef 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 | ||
252 | typedef struct remembered_name { | 269 | typedef 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 | } | 854 | dbg(" 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 | ||