aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2023-06-15 10:07:12 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2023-06-15 10:14:43 +0200
commit5f56a0388271d2de6cf31af1041bdcb3d11029fc (patch)
treee569191d44b44dcdc87e31c9927374a1ea97c20c
parent3829d8b6758439251fc3e34dcedf5910d039b07d (diff)
downloadbusybox-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.right1
-rwxr-xr-xshell/ash_test/ash-arith/arith-ternary_nested1.tests2
-rw-r--r--shell/ash_test/ash-arith/arith.right2
-rw-r--r--shell/hush_test/hush-arith/arith-precedence1.right4
-rwxr-xr-xshell/hush_test/hush-arith/arith-precedence1.tests15
-rw-r--r--shell/hush_test/hush-arith/arith-ternary2.right3
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary2.tests7
-rw-r--r--shell/hush_test/hush-arith/arith-ternary_nested1.right1
-rwxr-xr-xshell/hush_test/hush-arith/arith-ternary_nested1.tests2
-rw-r--r--shell/hush_test/hush-arith/arith.right2
-rw-r--r--shell/math.c48
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 @@
1exec 2>&1
2echo 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
9316 16 9316 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
979 9 979 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 @@
14:4
24:4
34:4
44: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 @@
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))
14echo 4:"$((0 ? 1,2 : 3,4))"
15echo 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 @@
16:6
2a=b=+err+
3b=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 @@
1exec 2>&1
2a='b=+err+'
3b=5
4# The not-taken branch should not parse variables
5echo 6:$((0 ? a : ++b))
6echo "a=$a"
7echo "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 @@
1exec 2>&1
2echo 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
94hush: arithmetic syntax error 94hush: arithmetic syntax error
9516 16 9516 16
96hush: arithmetic syntax error 96hush: arithmetic syntax error
97hush: malformed ?: operator 97hush: arithmetic syntax error
98hush: arithmetic syntax error 98hush: arithmetic syntax error
999 9 999 9
100hush: arithmetic syntax error 100hush: 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: