diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2023-05-30 16:42:18 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2023-05-30 16:44:04 +0200 |
commit | 0256e00a9d077588bd3a39f5a1ef7e2eaa2911e4 (patch) | |
tree | bdd3f15616a63a3261f5433c4df7bf83ce9f1be3 | |
parent | fe0b7985483a93d3416e0e5c9e761b6ee1ba310b (diff) | |
download | busybox-w32-0256e00a9d077588bd3a39f5a1ef7e2eaa2911e4.tar.gz busybox-w32-0256e00a9d077588bd3a39f5a1ef7e2eaa2911e4.tar.bz2 busybox-w32-0256e00a9d077588bd3a39f5a1ef7e2eaa2911e4.zip |
awk: fix precedence of = relative to ==
Discovered while adding code to disallow assignments to non-lvalues
function old new delta
parse_expr 936 991 +55
.rodata 105243 105247 +4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 59/0) Total: 59 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | editors/awk.c | 66 | ||||
-rwxr-xr-x | testsuite/awk.tests | 5 |
2 files changed, 50 insertions, 21 deletions
diff --git a/editors/awk.c b/editors/awk.c index c49ad6e02..0f062dcdb 100644 --- a/editors/awk.c +++ b/editors/awk.c | |||
@@ -337,7 +337,9 @@ static void debug_parse_print_tc(uint32_t n) | |||
337 | #undef P | 337 | #undef P |
338 | #undef PRIMASK | 338 | #undef PRIMASK |
339 | #undef PRIMASK2 | 339 | #undef PRIMASK2 |
340 | #define P(x) (x << 24) | 340 | /* Smaller 'x' means _higher_ operator precedence */ |
341 | #define PRECEDENCE(x) (x << 24) | ||
342 | #define P(x) PRECEDENCE(x) | ||
341 | #define PRIMASK 0x7F000000 | 343 | #define PRIMASK 0x7F000000 |
342 | #define PRIMASK2 0x7E000000 | 344 | #define PRIMASK2 0x7E000000 |
343 | 345 | ||
@@ -360,7 +362,7 @@ enum { | |||
360 | OC_MOVE = 0x1f00, OC_PGETLINE = 0x2000, OC_REGEXP = 0x2100, | 362 | OC_MOVE = 0x1f00, OC_PGETLINE = 0x2000, OC_REGEXP = 0x2100, |
361 | OC_REPLACE = 0x2200, OC_RETURN = 0x2300, OC_SPRINTF = 0x2400, | 363 | OC_REPLACE = 0x2200, OC_RETURN = 0x2300, OC_SPRINTF = 0x2400, |
362 | OC_TERNARY = 0x2500, OC_UNARY = 0x2600, OC_VAR = 0x2700, | 364 | OC_TERNARY = 0x2500, OC_UNARY = 0x2600, OC_VAR = 0x2700, |
363 | OC_DONE = 0x2800, | 365 | OC_CONST = 0x2800, OC_DONE = 0x2900, |
364 | 366 | ||
365 | ST_IF = 0x3000, ST_DO = 0x3100, ST_FOR = 0x3200, | 367 | ST_IF = 0x3000, ST_DO = 0x3100, ST_FOR = 0x3200, |
366 | ST_WHILE = 0x3300 | 368 | ST_WHILE = 0x3300 |
@@ -440,9 +442,9 @@ static const uint32_t tokeninfo[] ALIGN4 = { | |||
440 | #define TI_PREINC (OC_UNARY|xV|P(9)|'P') | 442 | #define TI_PREINC (OC_UNARY|xV|P(9)|'P') |
441 | #define TI_PREDEC (OC_UNARY|xV|P(9)|'M') | 443 | #define TI_PREDEC (OC_UNARY|xV|P(9)|'M') |
442 | TI_PREINC, TI_PREDEC, OC_FIELD|xV|P(5), | 444 | TI_PREINC, TI_PREDEC, OC_FIELD|xV|P(5), |
443 | OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74), OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-', | 445 | OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(38), OC_REPLACE|NV|P(38)|'+', OC_REPLACE|NV|P(38)|'-', |
444 | OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/', OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&', | 446 | OC_REPLACE|NV|P(38)|'*', OC_REPLACE|NV|P(38)|'/', OC_REPLACE|NV|P(38)|'%', OC_REPLACE|NV|P(38)|'&', |
445 | OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&', | 447 | OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-', OC_REPLACE|NV|P(38)|'&', OC_BINARY|NV|P(15)|'&', |
446 | OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', | 448 | OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%', OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*', |
447 | OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, | 449 | OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1, |
448 | #define TI_LESS (OC_COMPARE|VV|P(39)|2) | 450 | #define TI_LESS (OC_COMPARE|VV|P(39)|2) |
@@ -1301,7 +1303,7 @@ static uint32_t next_token(uint32_t expected) | |||
1301 | save_tclass = tc; | 1303 | save_tclass = tc; |
1302 | save_info = t_info; | 1304 | save_info = t_info; |
1303 | tc = TC_BINOPX; | 1305 | tc = TC_BINOPX; |
1304 | t_info = OC_CONCAT | SS | P(35); | 1306 | t_info = OC_CONCAT | SS | PRECEDENCE(35); |
1305 | } | 1307 | } |
1306 | 1308 | ||
1307 | t_tclass = tc; | 1309 | t_tclass = tc; |
@@ -1361,9 +1363,8 @@ static node *parse_expr(uint32_t term_tc) | |||
1361 | { | 1363 | { |
1362 | node sn; | 1364 | node sn; |
1363 | node *cn = &sn; | 1365 | node *cn = &sn; |
1364 | node *vn, *glptr; | 1366 | node *glptr; |
1365 | uint32_t tc, expected_tc; | 1367 | uint32_t tc, expected_tc; |
1366 | var *v; | ||
1367 | 1368 | ||
1368 | debug_printf_parse("%s() term_tc(%x):", __func__, term_tc); | 1369 | debug_printf_parse("%s() term_tc(%x):", __func__, term_tc); |
1369 | debug_parse_print_tc(term_tc); | 1370 | debug_parse_print_tc(term_tc); |
@@ -1374,11 +1375,12 @@ static node *parse_expr(uint32_t term_tc) | |||
1374 | expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP | term_tc; | 1375 | expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP | term_tc; |
1375 | 1376 | ||
1376 | while (!((tc = next_token(expected_tc)) & term_tc)) { | 1377 | while (!((tc = next_token(expected_tc)) & term_tc)) { |
1378 | node *vn; | ||
1377 | 1379 | ||
1378 | if (glptr && (t_info == TI_LESS)) { | 1380 | if (glptr && (t_info == TI_LESS)) { |
1379 | /* input redirection (<) attached to glptr node */ | 1381 | /* input redirection (<) attached to glptr node */ |
1380 | debug_printf_parse("%s: input redir\n", __func__); | 1382 | debug_printf_parse("%s: input redir\n", __func__); |
1381 | cn = glptr->l.n = new_node(OC_CONCAT | SS | P(37)); | 1383 | cn = glptr->l.n = new_node(OC_CONCAT | SS | PRECEDENCE(37)); |
1382 | cn->a.n = glptr; | 1384 | cn->a.n = glptr; |
1383 | expected_tc = TS_OPERAND | TS_UOPPRE; | 1385 | expected_tc = TS_OPERAND | TS_UOPPRE; |
1384 | glptr = NULL; | 1386 | glptr = NULL; |
@@ -1390,24 +1392,42 @@ static node *parse_expr(uint32_t term_tc) | |||
1390 | * previous operators with higher priority */ | 1392 | * previous operators with higher priority */ |
1391 | vn = cn; | 1393 | vn = cn; |
1392 | while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2)) | 1394 | while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2)) |
1393 | || ((t_info == vn->info) && t_info == TI_COLON) | 1395 | || (t_info == vn->info && t_info == TI_COLON) |
1394 | ) { | 1396 | ) { |
1395 | vn = vn->a.n; | 1397 | vn = vn->a.n; |
1396 | if (!vn->a.n) syntax_error(EMSG_UNEXP_TOKEN); | 1398 | if (!vn->a.n) syntax_error(EMSG_UNEXP_TOKEN); |
1397 | } | 1399 | } |
1398 | if (t_info == TI_TERNARY) | 1400 | if (t_info == TI_TERNARY) |
1399 | //TODO: why? | 1401 | //TODO: why? |
1400 | t_info += P(6); | 1402 | t_info += PRECEDENCE(6); |
1401 | cn = vn->a.n->r.n = new_node(t_info); | 1403 | cn = vn->a.n->r.n = new_node(t_info); |
1402 | cn->a.n = vn->a.n; | 1404 | cn->a.n = vn->a.n; |
1403 | if (tc & TS_BINOP) { | 1405 | if (tc & TS_BINOP) { |
1404 | cn->l.n = vn; | 1406 | cn->l.n = vn; |
1405 | //FIXME: this is the place to detect and reject assignments to non-lvalues. | 1407 | |
1406 | //Currently we allow "assignments" to consts and temporaries, nonsense like this: | 1408 | /* Prevent: |
1407 | // awk 'BEGIN { "qwe" = 1 }' | 1409 | * awk 'BEGIN { "qwe" = 1 }' |
1408 | // awk 'BEGIN { 7 *= 7 }' | 1410 | * awk 'BEGIN { 7 *= 7 }' |
1409 | // awk 'BEGIN { length("qwe") = 1 }' | 1411 | * awk 'BEGIN { length("qwe") = 1 }' |
1410 | // awk 'BEGIN { (1+1) += 3 }' | 1412 | * awk 'BEGIN { (1+1) += 3 }' |
1413 | */ | ||
1414 | /* Assignment? (including *= and friends) */ | ||
1415 | if (((t_info & OPCLSMASK) == OC_MOVE) | ||
1416 | || ((t_info & OPCLSMASK) == OC_REPLACE) | ||
1417 | ) { | ||
1418 | debug_printf_parse("%s: MOVE/REPLACE vn->info:%08x\n", __func__, vn->info); | ||
1419 | /* Left side is a (variable or array element) | ||
1420 | * or function argument | ||
1421 | * or $FIELD ? | ||
1422 | */ | ||
1423 | if ((vn->info & OPCLSMASK) != OC_VAR | ||
1424 | && (vn->info & OPCLSMASK) != OC_FNARG | ||
1425 | && (vn->info & OPCLSMASK) != OC_FIELD | ||
1426 | ) { | ||
1427 | syntax_error(EMSG_UNEXP_TOKEN); /* no. bad */ | ||
1428 | } | ||
1429 | } | ||
1430 | |||
1411 | expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP; | 1431 | expected_tc = TS_OPERAND | TS_UOPPRE | TC_REGEXP; |
1412 | if (t_info == TI_PGETLINE) { | 1432 | if (t_info == TI_PGETLINE) { |
1413 | /* it's a pipe */ | 1433 | /* it's a pipe */ |
@@ -1443,6 +1463,8 @@ static node *parse_expr(uint32_t term_tc) | |||
1443 | /* one should be very careful with switch on tclass - | 1463 | /* one should be very careful with switch on tclass - |
1444 | * only simple tclasses should be used (TC_xyz, not TS_xyz) */ | 1464 | * only simple tclasses should be used (TC_xyz, not TS_xyz) */ |
1445 | switch (tc) { | 1465 | switch (tc) { |
1466 | var *v; | ||
1467 | |||
1446 | case TC_VARIABLE: | 1468 | case TC_VARIABLE: |
1447 | case TC_ARRAY: | 1469 | case TC_ARRAY: |
1448 | debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__); | 1470 | debug_printf_parse("%s: TC_VARIABLE | TC_ARRAY\n", __func__); |
@@ -1463,14 +1485,14 @@ static node *parse_expr(uint32_t term_tc) | |||
1463 | case TC_NUMBER: | 1485 | case TC_NUMBER: |
1464 | case TC_STRING: | 1486 | case TC_STRING: |
1465 | debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__); | 1487 | debug_printf_parse("%s: TC_NUMBER | TC_STRING\n", __func__); |
1466 | cn->info = OC_VAR; | 1488 | cn->info = OC_CONST; |
1467 | v = cn->l.v = xzalloc(sizeof(var)); | 1489 | v = cn->l.v = xzalloc(sizeof(var)); |
1468 | if (tc & TC_NUMBER) | 1490 | if (tc & TC_NUMBER) { |
1469 | setvar_i(v, t_double); | 1491 | setvar_i(v, t_double); |
1470 | else { | 1492 | } else { |
1471 | setvar_s(v, t_string); | 1493 | setvar_s(v, t_string); |
1472 | expected_tc &= ~TC_UOPPOST; /* "str"++ is not allowed */ | ||
1473 | } | 1494 | } |
1495 | expected_tc &= ~TC_UOPPOST; /* NUM++, "str"++ not allowed */ | ||
1474 | break; | 1496 | break; |
1475 | 1497 | ||
1476 | case TC_REGEXP: | 1498 | case TC_REGEXP: |
@@ -3124,6 +3146,8 @@ static var *evaluate(node *op, var *res) | |||
3124 | 3146 | ||
3125 | /* -- recursive node type -- */ | 3147 | /* -- recursive node type -- */ |
3126 | 3148 | ||
3149 | case XC( OC_CONST ): | ||
3150 | debug_printf_eval("CONST "); | ||
3127 | case XC( OC_VAR ): | 3151 | case XC( OC_VAR ): |
3128 | debug_printf_eval("VAR\n"); | 3152 | debug_printf_eval("VAR\n"); |
3129 | L.v = op->l.v; | 3153 | L.v = op->l.v; |
diff --git a/testsuite/awk.tests b/testsuite/awk.tests index 8ab1c6891..cdab93d21 100755 --- a/testsuite/awk.tests +++ b/testsuite/awk.tests | |||
@@ -547,4 +547,9 @@ testing 'awk does not split on CR (char 13)' \ | |||
547 | 'word1 word2 word3\r word2 word3\r\n' \ | 547 | 'word1 word2 word3\r word2 word3\r\n' \ |
548 | '' 'word1 word2 word3\r' | 548 | '' 'word1 word2 word3\r' |
549 | 549 | ||
550 | testing "awk = has higher precedence than == (despite what gawk manpage claims)" \ | ||
551 | "awk 'BEGIN { v=1; print 2==v; print 2==v=2; print v; print v=3==3; print v}'" \ | ||
552 | '0\n1\n2\n1\n3\n' \ | ||
553 | '' '' | ||
554 | |||
550 | exit $FAILCOUNT | 555 | exit $FAILCOUNT |