authorgravatar for gbell12907@gmail.comCMDRZero <gbell12907@gmail.com> 2026-07-24 15:46:08-05:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-22 12:21:43+02:00
logcc41245dc7c23e019a1bd307e074af4f2121e327
tree644772295c96a96aac5eb69cbd49e72283013b8b
parente5021bc3b7aca7ad179820e4584812b11162d81f

Langref builds with superhtml; Remove unicode note from Ast.zig; Improve clarity in Parse.zig


6 files changed, 62 insertions(+), 75 deletions(-)

doc/langref/ambiguous_precedence_mul_bitand.zig+1-1
...@@ -8,4 +8,4 @@ test "ambiguous precedence" {...@@ -8,4 +8,4 @@ test "ambiguous precedence" {
8 _ = x & y *% z;8 _ = x & y *% z;
9}9}
1010
11// obj=ambiguous operator precedence with operators … & … *% …; use parentheses to disambiguate
\ No newline at end of file
11// obj=ambiguous operator precedence with operators ... & ... *% ...; use parentheses to disambiguate
\ No newline at end of file
doc/langref/ambiguous_precedence_orelse_ne.zig+1-1
...@@ -15,4 +15,4 @@ test "ambiguous precedence" {...@@ -15,4 +15,4 @@ test "ambiguous precedence" {
15 _ = makeBool(x) orelse y != z;15 _ = makeBool(x) orelse y != z;
16}16}
1717
18// obj=ambiguous operator precedence with operators … orelse … != …; use parentheses to disambiguate
\ No newline at end of file
18// obj=ambiguous operator precedence with operators ... orelse ... != ...; use parentheses to disambiguate
\ No newline at end of file
doc/langref/ambiguous_precedence_shift_sub.zig+1-1
...@@ -7,4 +7,4 @@ test "ambiguous precedence" {...@@ -7,4 +7,4 @@ test "ambiguous precedence" {
7 _ = x << y - 1;7 _ = x << y - 1;
8}8}
99
10// obj=ambiguous operator precedence with operators … << … - …; use parentheses to disambiguate
\ No newline at end of file
10// obj=ambiguous operator precedence with operators ... << ... - ...; use parentheses to disambiguate
\ No newline at end of file
doc/langref/nonchaining_sibling_operators.zig+1-1
...@@ -2,4 +2,4 @@ fn foo(x: anytype, y: anytype, z: anytype) void {...@@ -2,4 +2,4 @@ fn foo(x: anytype, y: anytype, z: anytype) void {
2 _ = x << y << z;2 _ = x << y << z;
3} 3}
44
5// obj=repetition of non-chainable operators … << … << …; use parentheses to disambiguate
\ No newline at end of file
5// obj=repetition of non-chainable operators ... << ... << ...; use parentheses to disambiguate
\ No newline at end of file
lib/std/zig/Ast.zig+4-4
...@@ -556,7 +556,7 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void...@@ -556,7 +556,7 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void
556 return w.writeAll("use 'var' or 'const' to declare variable");556 return w.writeAll("use 'var' or 'const' to declare variable");
557 },557 },
558 .ambiguous_operator_precedence => {558 .ambiguous_operator_precedence => {
559 return w.print("ambiguous operator precedence with operators … {s} … {s} …; use parentheses to disambiguate", .{559 return w.print("ambiguous operator precedence with operators ... {s} ... {s} ...; use parentheses to disambiguate", .{
560 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,560 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,
561 tree.tokenTag(parse_error.token).lexeme().?,561 tree.tokenTag(parse_error.token).lexeme().?,
562 });562 });
...@@ -565,19 +565,19 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void...@@ -565,19 +565,19 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void
565 return w.writeAll("previous ambiguous operator here");565 return w.writeAll("previous ambiguous operator here");
566 },566 },
567 .illegal_chained_operators => {567 .illegal_chained_operators => {
568 return w.print("repetition of non-chainable operators … {s} … {s} …; use parentheses to disambiguate", .{568 return w.print("repetition of non-chainable operators ... {s} ... {s} ...; use parentheses to disambiguate", .{
569 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,569 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,
570 tree.tokenTag(parse_error.token).lexeme().?,570 tree.tokenTag(parse_error.token).lexeme().?,
571 });571 });
572 },572 },
573 .hint_operator_precedence_left => {573 .hint_operator_precedence_left => {
574 return w.print("did you mean `(… {s} …) {s} …` ?", .{574 return w.print("previously evaluated as `(... {s} ...) {s} ...`", .{
575 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,575 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,
576 tree.tokenTag(parse_error.token).lexeme().?,576 tree.tokenTag(parse_error.token).lexeme().?,
577 });577 });
578 },578 },
579 .hint_operator_precedence_right => {579 .hint_operator_precedence_right => {
580 return w.print("did you mean `… {s} (… {s} …)` ?", .{580 return w.print("previously evaluated as `... {s} (... {s} ...)`", .{
581 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,581 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,
582 tree.tokenTag(parse_error.token).lexeme().?,582 tree.tokenTag(parse_error.token).lexeme().?,
583 });583 });
lib/std/zig/Parse.zig+54-67
...@@ -1552,34 +1552,47 @@ const PrecClass = struct {...@@ -1552,34 +1552,47 @@ const PrecClass = struct {
15521552
1553 root,1553 root,
15541554
1555 /// For this to work correctly we require that all subgroups are strictly adjacent1555 const Major = enum {
1556 /// Major groups are defined by the first text before an underscore1556 top,
1557 const Major: type = b: {1557 arithmetic,
1558 var major_names: []const []const u8 = &.{};1558 bitwise,
1559 for (std.meta.fieldNames(Group)) |fieldname| {1559 comparison,
1560 var major_name: []const u8 = fieldname[0..];1560 bool,
1561 for (fieldname, 0..) |c, i| {1561 recovery,
1562 if (c == '_') {1562 root,
1563 major_name = fieldname[0..i];1563 };
1564 break;
1565 }
1566 }
1567 if (major_names.len == 0 or !std.mem.eql(u8, major_name, major_names[major_names.len - 1])) {
1568 major_names = major_names ++ &[1][]const u8{major_name};
1569 }
1570 }
15711564
1572 const IntType = std.math.IntFittingRange(0, major_names.len - 1);1565 fn getMajor(group: Group) Major {
1573 var values: [major_names.len]IntType = undefined;1566 return switch (group) {
1574 for (0..major_names.len) |i| values[i] = i;1567 .top,
1568 => .top,
15751569
1576 break :b @Enum(1570 .arithmetic_product_chainable,
1577 IntType,1571 .arithmetic_mod_nonchainable,
1578 .exhaustive,1572 .arithmetic_sum,
1579 major_names,1573 => .arithmetic,
1580 &values,1574
1581 );1575 .bitwise_shift,
1582 };1576 .bitwise_and,
1577 .bitwise_xor,
1578 .bitwise_or,
1579 => .bitwise,
1580
1581 .comparison,
1582 => .comparison,
1583
1584 .bool_and,
1585 .bool_or,
1586 => .bool,
1587
1588 .recovery_orelse,
1589 .recovery_catch,
1590 => .recovery,
1591
1592 .root,
1593 => .root,
1594 };
1595 }
1583 };1596 };
15841597
1585 pub const start: PrecClass = .{ .group = .root };1598 pub const start: PrecClass = .{ .group = .root };
...@@ -1598,29 +1611,22 @@ const PrecClass = struct {...@@ -1598,29 +1611,22 @@ const PrecClass = struct {
15981611
1599 fn setMajorRelation(prec_array: *PrecedenceArray, major_a: Group.Major, rel: Rel, major_b: Group.Major) void {1612 fn setMajorRelation(prec_array: *PrecedenceArray, major_a: Group.Major, rel: Rel, major_b: Group.Major) void {
1600 for (std.meta.fieldNames(Group), 0..) |fieldname_i, i| {1613 for (std.meta.fieldNames(Group), 0..) |fieldname_i, i| {
1601 if (!std.mem.startsWith(u8, fieldname_i, @tagName(major_a))) continue;1614 if (@field(Group, fieldname_i).getMajor() != major_a) continue;
16021615
1603 for (std.meta.fieldNames(Group), 0..) |fieldname_j, j| {1616 for (std.meta.fieldNames(Group), 0..) |fieldname_j, j| {
1604 if (!std.mem.startsWith(u8, fieldname_j, @tagName(major_b))) continue;1617 if (@field(Group, fieldname_j).getMajor() != major_b) continue;
16051618
1606 prec_array.*[i][j] = rel;1619 prec_array[i][j] = rel;
1607 prec_array.*[j][i] = rel.inverted();1620 prec_array[j][i] = rel.inverted();
1608 }1621 }
1609 }1622 }
1610 }1623 }
16111624
1612 fn setMinorRelation(prec_array: *PrecedenceArray, minor_a: Group, rel: Rel, minor_b: Group) void {1625 fn setMinorRelation(prec_array: *PrecedenceArray, minor_a: Group, rel: Rel, minor_b: Group) void {
1613 prec_array.*[@backingInt(minor_a)][@backingInt(minor_b)] = rel;1626 prec_array[@backingInt(minor_a)][@backingInt(minor_b)] = rel;
1614 prec_array.*[@backingInt(minor_b)][@backingInt(minor_a)] = rel.inverted();1627 prec_array[@backingInt(minor_b)][@backingInt(minor_a)] = rel.inverted();
1615 }1628 }
16161629
1617 /// Arithmetic_* > Comparison
1618 /// Bitwise_* > Comparison
1619 /// Comparison > Bool_* > Root
1620 /// Recovery > Root
1621 ///
1622 /// *_Product > *_Sum
1623 ///
1624 /// order[y][x] --> y cmp x1630 /// order[y][x] --> y cmp x
1625 const precedence_ordering = b: {1631 const precedence_ordering = b: {
1626 @setEvalBranchQuota(20_000);1632 @setEvalBranchQuota(20_000);
...@@ -1646,7 +1652,7 @@ const PrecClass = struct {...@@ -1646,7 +1652,7 @@ const PrecClass = struct {
1646 setMinorRelation(&order, .arithmetic_mod_nonchainable, .gt, .arithmetic_sum);1652 setMinorRelation(&order, .arithmetic_mod_nonchainable, .gt, .arithmetic_sum);
1647 setMinorRelation(&order, .arithmetic_product_chainable, .gt, .arithmetic_sum);1653 setMinorRelation(&order, .arithmetic_product_chainable, .gt, .arithmetic_sum);
16481654
1649 computeTransativeOrdering(&order);1655 computeTransitiveOrdering(&order);
1650 break :b order;1656 break :b order;
1651 };1657 };
16521658
...@@ -1658,7 +1664,7 @@ const PrecClass = struct {...@@ -1658,7 +1664,7 @@ const PrecClass = struct {
1658 /// == != < > <= >=1664 /// == != < > <= >=
1659 /// and1665 /// and
1660 /// or1666 /// or
1661 const v16_precedence_ordering = b: {1667 const v0_16_precedence_ordering = b: {
1662 @setEvalBranchQuota(20_000);1668 @setEvalBranchQuota(20_000);
16631669
1664 var order: PrecedenceArray = @splat(@splat(null));1670 var order: PrecedenceArray = @splat(@splat(null));
...@@ -1685,7 +1691,7 @@ const PrecClass = struct {...@@ -1685,7 +1691,7 @@ const PrecClass = struct {
16851691
1686 setMinorRelation(&order, .bool_or, .gt, .root);1692 setMinorRelation(&order, .bool_or, .gt, .root);
16871693
1688 computeTransativeOrdering(&order);1694 computeTransitiveOrdering(&order);
1689 for (order) |row| {1695 for (order) |row| {
1690 for (row) |item| {1696 for (row) |item| {
1691 if (item == null) @compileError("Null entry in old precedence ordering which had none.");1697 if (item == null) @compileError("Null entry in old precedence ordering which had none.");
...@@ -1698,11 +1704,11 @@ const PrecClass = struct {...@@ -1698,11 +1704,11 @@ const PrecClass = struct {
1698 return PrecClass.precedence_ordering[@backingInt(lhs.group)][@backingInt(rhs.group)];1704 return PrecClass.precedence_ordering[@backingInt(lhs.group)][@backingInt(rhs.group)];
1699 }1705 }
17001706
1701 pub fn v16_cmp(lhs: PrecClass, rhs: PrecClass) Rel {1707 pub fn v0_16_cmp(lhs: PrecClass, rhs: PrecClass) Rel {
1702 return PrecClass.v16_precedence_ordering[@backingInt(lhs.group)][@backingInt(rhs.group)].?;1708 return PrecClass.v0_16_precedence_ordering[@backingInt(lhs.group)][@backingInt(rhs.group)].?;
1703 }1709 }
17041710
1705 fn computeTransativeOrdering(order: *PrecedenceArray) void {1711 fn computeTransitiveOrdering(order: *PrecedenceArray) void {
1706 for (0..Group.num_distinct) |i| for (0..Group.num_distinct) |j| for (0..Group.num_distinct) |k| {1712 for (0..Group.num_distinct) |i| for (0..Group.num_distinct) |j| for (0..Group.num_distinct) |k| {
1707 if (order[i][j] == null or order[j][k] == null) continue;1713 if (order[i][j] == null or order[j][k] == null) continue;
1708 // X < Y < Z ==> X < Z X > Y > Z ==> X > Z1714 // X < Y < Z ==> X < Z X > Y > Z ==> X > Z
...@@ -1766,23 +1772,6 @@ const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec...@@ -1766,23 +1772,6 @@ const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec
1766 // zig fmt: on1772 // zig fmt: on
1767});1773});
17681774
1769fn comptimePrecedenceFromNodeTag(comptime tag: Node.Tag) PrecClass {
1770 for (operTable) |data| {
1771 if (data.tag == tag) return data.prec;
1772 }
1773 return .{ .group = .top, .assoc = .left };
1774}
1775
1776pub fn precedenceFromNodeTag(tag: Node.Tag) PrecClass {
1777 return switch (tag) {
1778 inline else => |ct_tag| comptimePrecedenceFromNodeTag(ct_tag),
1779 };
1780}
1781
1782pub fn newPrecedenceBetween(left_tok: Node.Tag, right_tok: Node.Tag) ?PrecClass.Rel {
1783 return precedenceFromNodeTag(left_tok).cmp(precedenceFromNodeTag(right_tok));
1784}
1785
1786fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32) Error!?Node.Index {1775fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32) Error!?Node.Index {
1787 var node = try p.parsePrefixExpr() orelse return null;1776 var node = try p.parsePrefixExpr() orelse return null;
17881777
...@@ -1793,10 +1782,9 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32...@@ -1793,10 +1782,9 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32
1793 const info = operTable[@backingInt(tok_tag)];1782 const info = operTable[@backingInt(tok_tag)];
17941783
1795 const rel = info.prec.cmp(min_exc_prec) orelse b: {1784 const rel = info.prec.cmp(min_exc_prec) orelse b: {
1796 // This issues warnings if the relation is null, which it never is in 0.16.0
1797 const prev_op_tok_tag = p.tokenTag(prev_operator_token);1785 const prev_op_tok_tag = p.tokenTag(prev_operator_token);
1798 const prev_op_info = operTable[@backingInt(prev_op_tok_tag)];1786 const prev_op_info = operTable[@backingInt(prev_op_tok_tag)];
1799 const v16_rel = prev_op_info.prec.v16_cmp(info.prec);1787 const v0_16_rel = prev_op_info.prec.v0_16_cmp(info.prec);
1800 try p.warnMsg(.{1788 try p.warnMsg(.{
1801 .tag = .ambiguous_operator_precedence,1789 .tag = .ambiguous_operator_precedence,
1802 .is_note = false,1790 .is_note = false,
...@@ -1810,7 +1798,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32...@@ -1810,7 +1798,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32
1810 .is_note = true,1798 .is_note = true,
1811 .token = prev_operator_token,1799 .token = prev_operator_token,
1812 });1800 });
1813 if (v16_rel == .gt or v16_rel == .eq) {1801 if (v0_16_rel == .gt or v0_16_rel == .eq) {
1814 try p.warnMsg(.{1802 try p.warnMsg(.{
1815 .tag = .hint_operator_precedence_left,1803 .tag = .hint_operator_precedence_left,
1816 .is_note = true,1804 .is_note = true,
...@@ -1830,7 +1818,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32...@@ -1830,7 +1818,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32
1830 });1818 });
1831 }1819 }
1832 // Use the old precedence rules to construct the tree to ease automatic fixups1820 // Use the old precedence rules to construct the tree to ease automatic fixups
1833 break :b info.prec.v16_cmp(min_exc_prec);1821 break :b info.prec.v0_16_cmp(min_exc_prec);
1834 };1822 };
18351823
1836 if (rel == .lt) {1824 if (rel == .lt) {
...@@ -1838,7 +1826,6 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32...@@ -1838,7 +1826,6 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32
1838 }1826 }
18391827
1840 if (min_exc_prec.group == info.prec.group and min_exc_prec.assoc == .none and info.prec.assoc == .none) {1828 if (min_exc_prec.group == info.prec.group and min_exc_prec.assoc == .none and info.prec.assoc == .none) {
1841 //Special cased check to maintain the old more helpful error for chained comparisons.
1842 if (info.prec.group == .comparison) {1829 if (info.prec.group == .comparison) {
1843 try p.warn(.chained_comparison_operators);1830 try p.warn(.chained_comparison_operators);
1844 } else {1831 } else {