[PATCH] awk: fix ternary operator and precedence of =
Natanael Copa
ncopa at alpinelinux.org
Wed May 22 21:02:57 UTC 2024
Adjust the = precedence test to match behavior of gawk, mawk and
FreeBSD. awk 'BEGIN {print v=3==3; print v}' should print two '1'.
To fix this, and to unbreak the ternary conditional operator, we restore
the precedence of = in the token list, but override this with a lower
priority when the assignment is on the right side of a compare.
This fixes commit 0256e00a9d07 (awk: fix precedence of = relative to ==)
Signed-off-by: Natanael Copa <ncopa at alpinelinux.org>
---
editors/awk.c | 18 ++++++++++++++----
testsuite/awk.tests | 9 +++++++--
2 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/editors/awk.c b/editors/awk.c
index 0981c6735..cf46b4e5b 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -442,9 +442,10 @@ static const uint32_t tokeninfo[] ALIGN4 = {
#define TI_PREINC (OC_UNARY|xV|P(9)|'P')
#define TI_PREDEC (OC_UNARY|xV|P(9)|'M')
TI_PREINC, TI_PREDEC, OC_FIELD|xV|P(5),
- OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(38), OC_REPLACE|NV|P(38)|'+', OC_REPLACE|NV|P(38)|'-',
- OC_REPLACE|NV|P(38)|'*', OC_REPLACE|NV|P(38)|'/', OC_REPLACE|NV|P(38)|'%', OC_REPLACE|NV|P(38)|'&',
- OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(38)|'&', OC_BINARY|NV|P(15)|'&',
+#define TI_ASSIGN (OC_MOVE|VV|P(74))
+ OC_COMPARE|VV|P(39)|5, TI_ASSIGN, OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-',
+ OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&',
+ OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&',
OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*',
OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
#define TI_LESS (OC_COMPARE|VV|P(39)|2)
@@ -1390,11 +1391,19 @@ static node *parse_expr(uint32_t term_tc)
continue;
}
if (tc & (TS_BINOP | TC_UOPPOST)) {
+ int prio;
debug_printf_parse("%s: TS_BINOP | TC_UOPPOST tc:%x\n", __func__, tc);
/* for binary and postfix-unary operators, jump back over
* previous operators with higher priority */
vn = cn;
- while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2))
+ /* Let assignment get higher priority when used on right
+ * side in compare. i.e: 2==v=3 */
+ if (t_info == TI_ASSIGN && (vn->a.n->info & OPCLSMASK) == OC_COMPARE) {
+ prio = PRECEDENCE(38);
+ } else {
+ prio = (t_info & PRIMASK);
+ }
+ while ((prio > (vn->a.n->info & PRIMASK2))
|| (t_info == vn->info && t_info == TI_COLON)
) {
vn = vn->a.n;
@@ -1426,6 +1435,7 @@ static node *parse_expr(uint32_t term_tc)
if ((vn->info & OPCLSMASK) != OC_VAR
&& (vn->info & OPCLSMASK) != OC_FNARG
&& (vn->info & OPCLSMASK) != OC_FIELD
+ && (vn->info & OPCLSMASK) != OC_COMPARE
) {
syntax_error(EMSG_UNEXP_TOKEN); /* no. bad */
}
diff --git a/testsuite/awk.tests b/testsuite/awk.tests
index 063084a1c..d3ca476a7 100755
--- a/testsuite/awk.tests
+++ b/testsuite/awk.tests
@@ -547,9 +547,14 @@ testing 'awk does not split on CR (char 13)' \
'word1 word2 word3\r word2 word3\r\n' \
'' 'word1 word2 word3\r'
-testing "awk = has higher precedence than == (despite what gawk manpage claims)" \
+testing "awk = has higher precedence than == on right side" \
"awk 'BEGIN { v=1; print 2==v; print 2==v=2; print v; print v=3==3; print v}'" \
- '0\n1\n2\n1\n3\n' \
+ '0\n1\n2\n1\n1\n' \
+ '' ''
+
+testing 'awk ternary precedence' \
+ "awk 'BEGIN { a = 0 ? \"yes\": \"no\"; print a }'" \
+ 'no\n' \
'' ''
sq="'"
--
2.45.1
More information about the busybox
mailing list