authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-13 17:40:03-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-05-13 17:40:03-04:00
log173142cc3662e9ecd54c8e957763515712203f9c
treecb59541f0df737cc5236d4f72e31e7fa97b4774a
parent1b222cb1ff6b95be2f8b03a0ae872efcc7873aa6
parent33da465079d8d51bbe8d6e6c9976815a750872d0
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8611 from shachaf/precedence

parser: Use an operator precedence table

2 files changed, 88 insertions(+), 241 deletions(-)

lib/std/zig/parse.zig+77-241
......@@ -1309,9 +1309,8 @@ const Parser = struct {
13091309 return expr;
13101310 }
13111311
1312 /// Expr <- BoolOrExpr
13131312 fn parseExpr(p: *Parser) Error!Node.Index {
1314 return p.parseBoolOrExpr();
1313 return p.parseExprPrecedence(0);
13151314 }
13161315
13171316 fn expectExpr(p: *Parser) Error!Node.Index {
......@@ -1323,263 +1322,100 @@ const Parser = struct {
13231322 }
13241323 }
13251324
1326 /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
1327 fn parseBoolOrExpr(p: *Parser) Error!Node.Index {
1328 var res = try p.parseBoolAndExpr();
1329 if (res == 0) return null_node;
1325 const Assoc = enum {
1326 left,
1327 none,
1328 };
13301329
1331 while (true) {
1332 switch (p.token_tags[p.tok_i]) {
1333 .keyword_or => {
1334 const or_token = p.nextToken();
1335 const rhs = try p.parseBoolAndExpr();
1336 if (rhs == 0) {
1337 return p.fail(.invalid_token);
1338 }
1339 res = try p.addNode(.{
1340 .tag = .bool_or,
1341 .main_token = or_token,
1342 .data = .{
1343 .lhs = res,
1344 .rhs = rhs,
1345 },
1346 });
1347 },
1348 else => return res,
1349 }
1350 }
1351 }
1330 const OperInfo = struct {
1331 prec: i8,
1332 tag: Node.Tag,
1333 assoc: Assoc = Assoc.left,
1334 };
13521335
1353 /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
1354 fn parseBoolAndExpr(p: *Parser) !Node.Index {
1355 var res = try p.parseCompareExpr();
1356 if (res == 0) return null_node;
1336 // A table of binary operator information. Higher precedence numbers are
1337 // stickier. All operators at the same precedence level should have the same
1338 // associativity.
1339 const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec = -1, .tag = Node.Tag.root }, 0, .{
1340 .keyword_or = .{ .prec = 10, .tag = .bool_or },
1341
1342 .keyword_and = .{ .prec = 20, .tag = .bool_and },
1343 .invalid_ampersands = .{ .prec = 20, .tag = .bool_and },
1344
1345 .equal_equal = .{ .prec = 30, .tag = .equal_equal, .assoc = Assoc.none },
1346 .bang_equal = .{ .prec = 30, .tag = .bang_equal, .assoc = Assoc.none },
1347 .angle_bracket_left = .{ .prec = 30, .tag = .less_than, .assoc = Assoc.none },
1348 .angle_bracket_right = .{ .prec = 30, .tag = .greater_than, .assoc = Assoc.none },
1349 .angle_bracket_left_equal = .{ .prec = 30, .tag = .less_or_equal, .assoc = Assoc.none },
1350 .angle_bracket_right_equal = .{ .prec = 30, .tag = .greater_or_equal, .assoc = Assoc.none },
1351
1352 .ampersand = .{ .prec = 40, .tag = .bit_and },
1353 .caret = .{ .prec = 40, .tag = .bit_xor },
1354 .pipe = .{ .prec = 40, .tag = .bit_or },
1355 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },
1356 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
1357
1358 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left },
1359 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right },
1360
1361 .plus = .{ .prec = 60, .tag = .add },
1362 .minus = .{ .prec = 60, .tag = .sub },
1363 .plus_plus = .{ .prec = 60, .tag = .array_cat },
1364 .plus_percent = .{ .prec = 60, .tag = .add_wrap },
1365 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },
1366
1367 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },
1368 .asterisk = .{ .prec = 70, .tag = .mul },
1369 .slash = .{ .prec = 70, .tag = .div },
1370 .percent = .{ .prec = 70, .tag = .mod },
1371 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },
1372 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },
1373 });
13571374
1358 while (true) {
1359 switch (p.token_tags[p.tok_i]) {
1360 .keyword_and => {
1361 const and_token = p.nextToken();
1362 const rhs = try p.parseCompareExpr();
1363 if (rhs == 0) {
1364 return p.fail(.invalid_token);
1365 }
1366 res = try p.addNode(.{
1367 .tag = .bool_and,
1368 .main_token = and_token,
1369 .data = .{
1370 .lhs = res,
1371 .rhs = rhs,
1372 },
1373 });
1374 },
1375 .invalid_ampersands => {
1376 try p.warn(.invalid_and);
1377 p.tok_i += 1;
1378 return p.parseCompareExpr();
1379 },
1380 else => return res,
1381 }
1375 fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {
1376 var node = try p.parsePrefixExpr();
1377 if (node == 0) {
1378 return null_node;
13821379 }
1383 }
13841380
1385 /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
1386 /// CompareOp
1387 /// <- EQUALEQUAL
1388 /// / EXCLAMATIONMARKEQUAL
1389 /// / LARROW
1390 /// / RARROW
1391 /// / LARROWEQUAL
1392 /// / RARROWEQUAL
1393 fn parseCompareExpr(p: *Parser) !Node.Index {
1394 const expr = try p.parseBitwiseExpr();
1395 if (expr == 0) return null_node;
1396
1397 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1398 .equal_equal => .equal_equal,
1399 .bang_equal => .bang_equal,
1400 .angle_bracket_left => .less_than,
1401 .angle_bracket_right => .greater_than,
1402 .angle_bracket_left_equal => .less_or_equal,
1403 .angle_bracket_right_equal => .greater_or_equal,
1404 else => return expr,
1405 };
1406 return p.addNode(.{
1407 .tag = tag,
1408 .main_token = p.nextToken(),
1409 .data = .{
1410 .lhs = expr,
1411 .rhs = try p.expectBitwiseExpr(),
1412 },
1413 });
1414 }
1415
1416 /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
1417 /// BitwiseOp
1418 /// <- AMPERSAND
1419 /// / CARET
1420 /// / PIPE
1421 /// / KEYWORD_orelse
1422 /// / KEYWORD_catch Payload?
1423 fn parseBitwiseExpr(p: *Parser) !Node.Index {
1424 var res = try p.parseBitShiftExpr();
1425 if (res == 0) return null_node;
1381 var banned_prec: i8 = -1;
14261382
14271383 while (true) {
1428 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1429 .ampersand => .bit_and,
1430 .caret => .bit_xor,
1431 .pipe => .bit_or,
1432 .keyword_orelse => .@"orelse",
1384 const tok_tag = p.token_tags[p.tok_i];
1385 const info = operTable[@intCast(usize, @enumToInt(tok_tag))];
1386 if (info.prec < min_prec or info.prec == banned_prec) {
1387 break;
1388 }
1389 const oper_token = p.nextToken();
1390 // Special-case handling for "catch" and "&&".
1391 switch (tok_tag) {
14331392 .keyword_catch => {
1434 const catch_token = p.nextToken();
14351393 _ = try p.parsePayload();
1436 const rhs = try p.parseBitShiftExpr();
1437 if (rhs == 0) {
1438 return p.fail(.invalid_token);
1439 }
1440 res = try p.addNode(.{
1441 .tag = .@"catch",
1442 .main_token = catch_token,
1443 .data = .{
1444 .lhs = res,
1445 .rhs = rhs,
1446 },
1447 });
1448 continue;
1449 },
1450 else => return res,
1451 };
1452 res = try p.addNode(.{
1453 .tag = tag,
1454 .main_token = p.nextToken(),
1455 .data = .{
1456 .lhs = res,
1457 .rhs = try p.expectBitShiftExpr(),
14581394 },
1459 });
1460 }
1461 }
1462
1463 fn expectBitwiseExpr(p: *Parser) Error!Node.Index {
1464 const node = try p.parseBitwiseExpr();
1465 if (node == 0) {
1466 return p.fail(.invalid_token);
1467 } else {
1468 return node;
1469 }
1470 }
1471
1472 /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
1473 /// BitShiftOp
1474 /// <- LARROW2
1475 /// / RARROW2
1476 fn parseBitShiftExpr(p: *Parser) Error!Node.Index {
1477 var res = try p.parseAdditionExpr();
1478 if (res == 0) return null_node;
1479
1480 while (true) {
1481 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1482 .angle_bracket_angle_bracket_left => .bit_shift_left,
1483 .angle_bracket_angle_bracket_right => .bit_shift_right,
1484 else => return res,
1485 };
1486 res = try p.addNode(.{
1487 .tag = tag,
1488 .main_token = p.nextToken(),
1489 .data = .{
1490 .lhs = res,
1491 .rhs = try p.expectAdditionExpr(),
1395 .invalid_ampersands => {
1396 try p.warn(.invalid_and);
14921397 },
1493 });
1494 }
1495 }
1496
1497 fn expectBitShiftExpr(p: *Parser) Error!Node.Index {
1498 const node = try p.parseBitShiftExpr();
1499 if (node == 0) {
1500 return p.fail(.invalid_token);
1501 } else {
1502 return node;
1503 }
1504 }
1505
1506 /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
1507 /// AdditionOp
1508 /// <- PLUS
1509 /// / MINUS
1510 /// / PLUS2
1511 /// / PLUSPERCENT
1512 /// / MINUSPERCENT
1513 fn parseAdditionExpr(p: *Parser) Error!Node.Index {
1514 var res = try p.parseMultiplyExpr();
1515 if (res == 0) return null_node;
1398 else => {},
1399 }
1400 const rhs = try p.parseExprPrecedence(info.prec + 1);
1401 if (rhs == 0) {
1402 return p.fail(.invalid_token);
1403 }
15161404
1517 while (true) {
1518 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1519 .plus => .add,
1520 .minus => .sub,
1521 .plus_plus => .array_cat,
1522 .plus_percent => .add_wrap,
1523 .minus_percent => .sub_wrap,
1524 else => return res,
1525 };
1526 res = try p.addNode(.{
1527 .tag = tag,
1528 .main_token = p.nextToken(),
1405 node = try p.addNode(.{
1406 .tag = info.tag,
1407 .main_token = oper_token,
15291408 .data = .{
1530 .lhs = res,
1531 .rhs = try p.expectMultiplyExpr(),
1409 .lhs = node,
1410 .rhs = rhs,
15321411 },
15331412 });
1534 }
1535 }
15361413
1537 fn expectAdditionExpr(p: *Parser) Error!Node.Index {
1538 const node = try p.parseAdditionExpr();
1539 if (node == 0) {
1540 return p.fail(.invalid_token);
1541 }
1542 return node;
1543 }
1544
1545 /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
1546 /// MultiplyOp
1547 /// <- PIPE2
1548 /// / ASTERISK
1549 /// / SLASH
1550 /// / PERCENT
1551 /// / ASTERISK2
1552 /// / ASTERISKPERCENT
1553 fn parseMultiplyExpr(p: *Parser) Error!Node.Index {
1554 var res = try p.parsePrefixExpr();
1555 if (res == 0) return null_node;
1556
1557 while (true) {
1558 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1559 .pipe_pipe => .merge_error_sets,
1560 .asterisk => .mul,
1561 .slash => .div,
1562 .percent => .mod,
1563 .asterisk_asterisk => .array_mult,
1564 .asterisk_percent => .mul_wrap,
1565 else => return res,
1566 };
1567 res = try p.addNode(.{
1568 .tag = tag,
1569 .main_token = p.nextToken(),
1570 .data = .{
1571 .lhs = res,
1572 .rhs = try p.expectPrefixExpr(),
1573 },
1574 });
1414 if (info.assoc == Assoc.none) {
1415 banned_prec = info.prec;
1416 }
15751417 }
1576 }
15771418
1578 fn expectMultiplyExpr(p: *Parser) Error!Node.Index {
1579 const node = try p.parseMultiplyExpr();
1580 if (node == 0) {
1581 return p.fail(.invalid_token);
1582 }
15831419 return node;
15841420 }
15851421
lib/std/zig/parser_test.zig+11
......@@ -2828,6 +2828,7 @@ test "zig fmt: precedence" {
28282828 \\ a or b and c;
28292829 \\ (a or b) and c;
28302830 \\ (a or b) and c;
2831 \\ a == b and c == d;
28312832 \\}
28322833 \\
28332834 );
......@@ -4892,6 +4893,16 @@ test "recovery: missing comma" {
48924893 });
48934894}
48944895
4896test "recovery: non-associative operators" {
4897 try testError(
4898 \\const x = a == b == c;
4899 \\const x = a == b != c;
4900 , &[_]Error{
4901 .expected_token,
4902 .expected_token,
4903 });
4904}
4905
48954906test "recovery: extra qualifier" {
48964907 try testError(
48974908 \\const a: *const const u8;