diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-15 10:07:12 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2023-06-15 10:14:43 +0200 |
commit | 5f56a0388271d2de6cf31af1041bdcb3d11029fc (patch) | |
tree | e569191d44b44dcdc87e31c9927374a1ea97c20c | |
parent | 3829d8b6758439251fc3e34dcedf5910d039b07d (diff) | |
download | busybox-w32-5f56a0388271d2de6cf31af1041bdcb3d11029fc.tar.gz busybox-w32-5f56a0388271d2de6cf31af1041bdcb3d11029fc.tar.bz2 busybox-w32-5f56a0388271d2de6cf31af1041bdcb3d11029fc.zip |
shell/math: fix parsing of ?: and explain why it's parsed that way
This fixes arith-precedence1.tests.
This breaks arith-ternary2.tests again (we now evaluate variables
on not-taken branches). We need a better logic here anyway:
not only bare variables should not evaluate when not-taken:
1 ? eval_me : do_not_eval
but any (arbitrarily complex) expressions shouldn't
evaluate as well!
1 ? var_is_set=1 : ((var_is_not_set=2,var2*=4))
function old new delta
evaluate_string 1097 1148 +51
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/ash_test/ash-arith/arith-ternary_nested1.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-arith/arith-ternary_nested1.tests | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-arith/arith.right | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-arith/arith-precedence1.right | 4 | ||||
-rwxr-xr-x | shell/hush_test/hush-arith/arith-precedence1.tests | 15 | ||||
-rw-r--r-- | shell/hush_test/hush-arith/arith-ternary2.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-arith/arith-ternary2.tests | 7 | ||||
-rw-r--r-- | shell/hush_test/hush-arith/arith-ternary_nested1.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-arith/arith-ternary_nested1.tests | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-arith/arith.right | 2 | ||||
-rw-r--r-- | shell/math.c | 48 |
11 files changed, 61 insertions, 26 deletions
diff --git a/shell/ash_test/ash-arith/arith-ternary_nested1.right b/shell/ash_test/ash-arith/arith-ternary_nested1.right new file mode 100644 index 000000000..d80319695 --- /dev/null +++ b/shell/ash_test/ash-arith/arith-ternary_nested1.right | |||
@@ -0,0 +1 @@ | |||
3:3 | |||
diff --git a/shell/ash_test/ash-arith/arith-ternary_nested1.tests b/shell/ash_test/ash-arith/arith-ternary_nested1.tests new file mode 100755 index 000000000..469584bea --- /dev/null +++ b/shell/ash_test/ash-arith/arith-ternary_nested1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | exec 2>&1 | ||
2 | echo 3:$((1?(2?(3):4):5)) | ||
diff --git a/shell/ash_test/ash-arith/arith.right b/shell/ash_test/ash-arith/arith.right index 61fcab55e..8bc78b8d1 100644 --- a/shell/ash_test/ash-arith/arith.right +++ b/shell/ash_test/ash-arith/arith.right | |||
@@ -92,7 +92,7 @@ ghi | |||
92 | ./arith.tests: line 190: arithmetic syntax error | 92 | ./arith.tests: line 190: arithmetic syntax error |
93 | 16 16 | 93 | 16 16 |
94 | ./arith.tests: line 195: arithmetic syntax error | 94 | ./arith.tests: line 195: arithmetic syntax error |
95 | ./arith.tests: line 196: malformed ?: operator | 95 | ./arith.tests: line 196: arithmetic syntax error |
96 | ./arith.tests: line 197: arithmetic syntax error | 96 | ./arith.tests: line 197: arithmetic syntax error |
97 | 9 9 | 97 | 9 9 |
98 | ./arith.tests: line 204: arithmetic syntax error | 98 | ./arith.tests: line 204: arithmetic syntax error |
diff --git a/shell/hush_test/hush-arith/arith-precedence1.right b/shell/hush_test/hush-arith/arith-precedence1.right new file mode 100644 index 000000000..3f9320a13 --- /dev/null +++ b/shell/hush_test/hush-arith/arith-precedence1.right | |||
@@ -0,0 +1,4 @@ | |||
1 | 4:4 | ||
2 | 4:4 | ||
3 | 4:4 | ||
4 | 4:4 | ||
diff --git a/shell/hush_test/hush-arith/arith-precedence1.tests b/shell/hush_test/hush-arith/arith-precedence1.tests new file mode 100755 index 000000000..bfef05292 --- /dev/null +++ b/shell/hush_test/hush-arith/arith-precedence1.tests | |||
@@ -0,0 +1,15 @@ | |||
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)) | ||
14 | echo 4:"$((0 ? 1,2 : 3,4))" | ||
15 | echo 4:"$((1 ? 1,2 : 3,4))" | ||
diff --git a/shell/hush_test/hush-arith/arith-ternary2.right b/shell/hush_test/hush-arith/arith-ternary2.right deleted file mode 100644 index a549b1b5c..000000000 --- a/shell/hush_test/hush-arith/arith-ternary2.right +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | 6:6 | ||
2 | a=b=+err+ | ||
3 | b=6 | ||
diff --git a/shell/hush_test/hush-arith/arith-ternary2.tests b/shell/hush_test/hush-arith/arith-ternary2.tests deleted file mode 100755 index cb3163932..000000000 --- a/shell/hush_test/hush-arith/arith-ternary2.tests +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | exec 2>&1 | ||
2 | a='b=+err+' | ||
3 | b=5 | ||
4 | # The not-taken branch should not parse variables | ||
5 | echo 6:$((0 ? a : ++b)) | ||
6 | echo "a=$a" | ||
7 | echo "b=$b" | ||
diff --git a/shell/hush_test/hush-arith/arith-ternary_nested1.right b/shell/hush_test/hush-arith/arith-ternary_nested1.right new file mode 100644 index 000000000..d80319695 --- /dev/null +++ b/shell/hush_test/hush-arith/arith-ternary_nested1.right | |||
@@ -0,0 +1 @@ | |||
3:3 | |||
diff --git a/shell/hush_test/hush-arith/arith-ternary_nested1.tests b/shell/hush_test/hush-arith/arith-ternary_nested1.tests new file mode 100755 index 000000000..469584bea --- /dev/null +++ b/shell/hush_test/hush-arith/arith-ternary_nested1.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | exec 2>&1 | ||
2 | echo 3:$((1?(2?(3):4):5)) | ||
diff --git a/shell/hush_test/hush-arith/arith.right b/shell/hush_test/hush-arith/arith.right index a8612295e..df8154f97 100644 --- a/shell/hush_test/hush-arith/arith.right +++ b/shell/hush_test/hush-arith/arith.right | |||
@@ -94,7 +94,7 @@ ghi | |||
94 | hush: arithmetic syntax error | 94 | hush: arithmetic syntax error |
95 | 16 16 | 95 | 16 16 |
96 | hush: arithmetic syntax error | 96 | hush: arithmetic syntax error |
97 | hush: malformed ?: operator | 97 | hush: arithmetic syntax error |
98 | hush: arithmetic syntax error | 98 | hush: arithmetic syntax error |
99 | 9 9 | 99 | 9 9 |
100 | hush: arithmetic syntax error | 100 | hush: arithmetic syntax error |
diff --git a/shell/math.c b/shell/math.c index 748c3b3ad..f6aa02ac2 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -157,17 +157,17 @@ typedef unsigned char operator; | |||
157 | #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) |
158 | 158 | ||
159 | /* Ternary conditional operator is right associative too */ | 159 | /* Ternary conditional operator is right associative too */ |
160 | // FIXME: | 160 | /* |
161 | // bash documentation says that precedence order is: | 161 | * bash documentation says that precedence order is: |
162 | // ... | 162 | * ... |
163 | // expr ? expr1 : expr2 | 163 | * expr ? expr1 : expr2 |
164 | // = *= /= %= += -= <<= >>= &= ^= |= | 164 | * = *= /= %= += -= <<= >>= &= ^= |= |
165 | // exprA , exprB | 165 | * exprA , exprB |
166 | // but in practice, the rules for expr1 and expr2 are different: | 166 | * What it omits is that expr1 is parsed as if parenthesized |
167 | // assignments and commas in expr1 have higher precedence than ?:, | 167 | * (this matches the rules of ?: in C language): |
168 | // but in expr2 they haven't: | 168 | * "v ? 1,2 : 3,4" is parsed as "(v ? (1,2) : 3),4" |
169 | // "v ? 1,2 : 3,4" is parsed as "(v ? (1,2) : 3),4" | 169 | * "v ? a=2 : b=4" is parsed as "(v ? (a=1) : b)=4" (thus, this is a syntax error) |
170 | // "v ? a=2 : b=4" is parsed as "(v ? (a=1) : b)=4" (thus, this is a syntax error) | 170 | */ |
171 | #define TOK_CONDITIONAL tok_decl(4,0) | 171 | #define TOK_CONDITIONAL tok_decl(4,0) |
172 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) | 172 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) |
173 | 173 | ||
@@ -629,6 +629,7 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
629 | /* Stack of operator tokens */ | 629 | /* Stack of operator tokens */ |
630 | operator *const opstack = alloca(expr_len * sizeof(opstack[0])); | 630 | operator *const opstack = alloca(expr_len * sizeof(opstack[0])); |
631 | operator *opstackptr = opstack; | 631 | operator *opstackptr = opstack; |
632 | operator insert_op = 0xff; | ||
632 | 633 | ||
633 | /* Start with a left paren */ | 634 | /* Start with a left paren */ |
634 | dbg("(%d) op:TOK_LPAREN", (int)(opstackptr - opstack)); | 635 | dbg("(%d) op:TOK_LPAREN", (int)(opstackptr - opstack)); |
@@ -751,11 +752,24 @@ evaluate_string(arith_state_t *math_state, const char *expr) | |||
751 | goto err; | 752 | goto err; |
752 | } | 753 | } |
753 | } | 754 | } |
755 | /* NB: expr now points past the operator */ | ||
754 | tok_found: | 756 | tok_found: |
755 | op = p[1]; /* fetch TOK_foo value */ | 757 | op = p[1]; /* fetch TOK_foo value */ |
756 | tok_found1: | ||
757 | /* NB: expr now points past the operator */ | ||
758 | 758 | ||
759 | /* Special rule for "? EXPR :" | ||
760 | * "EXPR in the middle of ? : is parsed as if parenthesized" | ||
761 | * (this quirk originates in C grammar, I think). | ||
762 | */ | ||
763 | if (op == TOK_CONDITIONAL) { | ||
764 | insert_op = TOK_LPAREN; | ||
765 | dbg("insert_op=%02x", insert_op); | ||
766 | } | ||
767 | if (op == TOK_CONDITIONAL_SEP) { | ||
768 | insert_op = op; | ||
769 | op = TOK_RPAREN; | ||
770 | dbg("insert_op=%02x op=%02x", insert_op, op); | ||
771 | } | ||
772 | tok_found1: | ||
759 | /* post grammar: a++ reduce to num */ | 773 | /* post grammar: a++ reduce to num */ |
760 | if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) | 774 | if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) |
761 | lasttok = TOK_NUM; | 775 | lasttok = TOK_NUM; |
@@ -865,9 +879,15 @@ dbg(" numstack:%d val:%lld '%s'", (int)(numstackptr - numstack), numstackptr[ | |||
865 | /* else: LPAREN or UNARY: push it on opstack */ | 879 | /* else: LPAREN or UNARY: push it on opstack */ |
866 | push_op: | 880 | push_op: |
867 | /* Push this operator to opstack */ | 881 | /* Push this operator to opstack */ |
868 | dbg("(%d) op:%02x", (int)(opstackptr - opstack), op); | 882 | dbg("(%d) op:%02x insert_op:%02x", (int)(opstackptr - opstack), op, insert_op); |
869 | *opstackptr++ = lasttok = op; | 883 | *opstackptr++ = lasttok = op; |
870 | next: ; | 884 | next: ; |
885 | if (insert_op != 0xff) { | ||
886 | op = insert_op; | ||
887 | insert_op = 0xff; | ||
888 | dbg("inserting %02x", op); | ||
889 | goto tok_found1; | ||
890 | } | ||
871 | } /* while (1) */ | 891 | } /* while (1) */ |
872 | 892 | ||
873 | err: | 893 | err: |