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" {
88 _ = x & y *% z;
99}
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" {
1515 _ = makeBool(x) orelse y != z;
1616}
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" {
77 _ = x << y - 1;
88}
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 {
22 _ = x << y << z;
33}
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
556556 return w.writeAll("use 'var' or 'const' to declare variable");
557557 },
558558 .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", .{
560560 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,
561561 tree.tokenTag(parse_error.token).lexeme().?,
562562 });
......@@ -565,19 +565,19 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void
565565 return w.writeAll("previous ambiguous operator here");
566566 },
567567 .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", .{
569569 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,
570570 tree.tokenTag(parse_error.token).lexeme().?,
571571 });
572572 },
573573 .hint_operator_precedence_left => {
574 return w.print("did you mean `(… {s} …) {s} …` ?", .{
574 return w.print("previously evaluated as `(... {s} ...) {s} ...`", .{
575575 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,
576576 tree.tokenTag(parse_error.token).lexeme().?,
577577 });
578578 },
579579 .hint_operator_precedence_right => {
580 return w.print("did you mean `… {s} (… {s} …)` ?", .{
580 return w.print("previously evaluated as `... {s} (... {s} ...)`", .{
581581 tree.tokenTag(parse_error.token - @as(u32, @intCast(parse_error.extra.offset))).lexeme().?,
582582 tree.tokenTag(parse_error.token).lexeme().?,
583583 });
lib/std/zig/Parse.zig+54-67
......@@ -1552,34 +1552,47 @@ const PrecClass = struct {
15521552
15531553 root,
15541554
1555 /// For this to work correctly we require that all subgroups are strictly adjacent
1556 /// Major groups are defined by the first text before an underscore
1557 const Major: type = b: {
1558 var major_names: []const []const u8 = &.{};
1559 for (std.meta.fieldNames(Group)) |fieldname| {
1560 var major_name: []const u8 = fieldname[0..];
1561 for (fieldname, 0..) |c, i| {
1562 if (c == '_') {
1563 major_name = fieldname[0..i];
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 }
1555 const Major = enum {
1556 top,
1557 arithmetic,
1558 bitwise,
1559 comparison,
1560 bool,
1561 recovery,
1562 root,
1563 };
15711564
1572 const IntType = std.math.IntFittingRange(0, major_names.len - 1);
1573 var values: [major_names.len]IntType = undefined;
1574 for (0..major_names.len) |i| values[i] = i;
1565 fn getMajor(group: Group) Major {
1566 return switch (group) {
1567 .top,
1568 => .top,
15751569
1576 break :b @Enum(
1577 IntType,
1578 .exhaustive,
1579 major_names,
1580 &values,
1581 );
1582 };
1570 .arithmetic_product_chainable,
1571 .arithmetic_mod_nonchainable,
1572 .arithmetic_sum,
1573 => .arithmetic,
1574
1575 .bitwise_shift,
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 }
15831596 };
15841597
15851598 pub const start: PrecClass = .{ .group = .root };
......@@ -1598,29 +1611,22 @@ const PrecClass = struct {
15981611
15991612 fn setMajorRelation(prec_array: *PrecedenceArray, major_a: Group.Major, rel: Rel, major_b: Group.Major) void {
16001613 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
16031616 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;
1607 prec_array.*[j][i] = rel.inverted();
1619 prec_array[i][j] = rel;
1620 prec_array[j][i] = rel.inverted();
16081621 }
16091622 }
16101623 }
16111624
16121625 fn setMinorRelation(prec_array: *PrecedenceArray, minor_a: Group, rel: Rel, minor_b: Group) void {
1613 prec_array.*[@backingInt(minor_a)][@backingInt(minor_b)] = rel;
1614 prec_array.*[@backingInt(minor_b)][@backingInt(minor_a)] = rel.inverted();
1626 prec_array[@backingInt(minor_a)][@backingInt(minor_b)] = rel;
1627 prec_array[@backingInt(minor_b)][@backingInt(minor_a)] = rel.inverted();
16151628 }
16161629
1617 /// Arithmetic_* > Comparison
1618 /// Bitwise_* > Comparison
1619 /// Comparison > Bool_* > Root
1620 /// Recovery > Root
1621 ///
1622 /// *_Product > *_Sum
1623 ///
16241630 /// order[y][x] --> y cmp x
16251631 const precedence_ordering = b: {
16261632 @setEvalBranchQuota(20_000);
......@@ -1646,7 +1652,7 @@ const PrecClass = struct {
16461652 setMinorRelation(&order, .arithmetic_mod_nonchainable, .gt, .arithmetic_sum);
16471653 setMinorRelation(&order, .arithmetic_product_chainable, .gt, .arithmetic_sum);
16481654
1649 computeTransativeOrdering(&order);
1655 computeTransitiveOrdering(&order);
16501656 break :b order;
16511657 };
16521658
......@@ -1658,7 +1664,7 @@ const PrecClass = struct {
16581664 /// == != < > <= >=
16591665 /// and
16601666 /// or
1661 const v16_precedence_ordering = b: {
1667 const v0_16_precedence_ordering = b: {
16621668 @setEvalBranchQuota(20_000);
16631669
16641670 var order: PrecedenceArray = @splat(@splat(null));
......@@ -1685,7 +1691,7 @@ const PrecClass = struct {
16851691
16861692 setMinorRelation(&order, .bool_or, .gt, .root);
16871693
1688 computeTransativeOrdering(&order);
1694 computeTransitiveOrdering(&order);
16891695 for (order) |row| {
16901696 for (row) |item| {
16911697 if (item == null) @compileError("Null entry in old precedence ordering which had none.");
......@@ -1698,11 +1704,11 @@ const PrecClass = struct {
16981704 return PrecClass.precedence_ordering[@backingInt(lhs.group)][@backingInt(rhs.group)];
16991705 }
17001706
1701 pub fn v16_cmp(lhs: PrecClass, rhs: PrecClass) Rel {
1702 return PrecClass.v16_precedence_ordering[@backingInt(lhs.group)][@backingInt(rhs.group)].?;
1707 pub fn v0_16_cmp(lhs: PrecClass, rhs: PrecClass) Rel {
1708 return PrecClass.v0_16_precedence_ordering[@backingInt(lhs.group)][@backingInt(rhs.group)].?;
17031709 }
17041710
1705 fn computeTransativeOrdering(order: *PrecedenceArray) void {
1711 fn computeTransitiveOrdering(order: *PrecedenceArray) void {
17061712 for (0..Group.num_distinct) |i| for (0..Group.num_distinct) |j| for (0..Group.num_distinct) |k| {
17071713 if (order[i][j] == null or order[j][k] == null) continue;
17081714 // X < Y < Z ==> X < Z X > Y > Z ==> X > Z
......@@ -1766,23 +1772,6 @@ const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec
17661772 // zig fmt: on
17671773});
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
17861775fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32) Error!?Node.Index {
17871776 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
17931782 const info = operTable[@backingInt(tok_tag)];
17941783
17951784 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
17971785 const prev_op_tok_tag = p.tokenTag(prev_operator_token);
17981786 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);
18001788 try p.warnMsg(.{
18011789 .tag = .ambiguous_operator_precedence,
18021790 .is_note = false,
......@@ -1810,7 +1798,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32
18101798 .is_note = true,
18111799 .token = prev_operator_token,
18121800 });
1813 if (v16_rel == .gt or v16_rel == .eq) {
1801 if (v0_16_rel == .gt or v0_16_rel == .eq) {
18141802 try p.warnMsg(.{
18151803 .tag = .hint_operator_precedence_left,
18161804 .is_note = true,
......@@ -1830,7 +1818,7 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32
18301818 });
18311819 }
18321820 // 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);
18341822 };
18351823
18361824 if (rel == .lt) {
......@@ -1838,7 +1826,6 @@ fn parseExprPrecedence(p: *Parse, min_exc_prec: PrecClass, prev_token_const: u32
18381826 }
18391827
18401828 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.
18421829 if (info.prec.group == .comparison) {
18431830 try p.warn(.chained_comparison_operators);
18441831 } else {