authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-16 23:01:24+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-12-16 23:01:24+00:00
log7e8be213631821b0b767aec03c22a937541b7d0a
treeedbfa24f29e3788a81e92532aefad74d61c2b43b
parent32354d1190a9d319f0f7970d6e39d2d951f2d3f4
parentc7485d73ac3e6dd105c6ee8fcf774493cc9eb31e
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22250 from mlugg/zon-ast

compiler: introduce ZonGen and make `ast-check` run it for ZON inputs

9 files changed, 1531 insertions(+), 191 deletions(-)

lib/std/zig.zig+2
......@@ -14,6 +14,8 @@ pub const isPrimitive = primitives.isPrimitive;
1414pub const Ast = @import("zig/Ast.zig");
1515pub const AstGen = @import("zig/AstGen.zig");
1616pub const Zir = @import("zig/Zir.zig");
17pub const Zoir = @import("zig/Zoir.zig");
18pub const ZonGen = @import("zig/ZonGen.zig");
1719pub const system = @import("zig/system.zig");
1820pub const CrossTarget = @compileError("deprecated; use std.Target.Query");
1921pub const BuiltinFn = @import("zig/BuiltinFn.zig");
lib/std/zig/AstGen.zig+25-89
......@@ -130,6 +130,8 @@ fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
130130}
131131
132132pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
133 assert(tree.mode == .zig);
134
133135 var arena = std.heap.ArenaAllocator.init(gpa);
134136 defer arena.deinit();
135137
......@@ -11413,83 +11415,7 @@ fn parseStrLit(
1141311415
1141411416fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
1141511417 const raw_string = bytes[offset..];
11416 switch (err) {
11417 .invalid_escape_character => |bad_index| {
11418 return astgen.failOff(
11419 token,
11420 offset + @as(u32, @intCast(bad_index)),
11421 "invalid escape character: '{c}'",
11422 .{raw_string[bad_index]},
11423 );
11424 },
11425 .expected_hex_digit => |bad_index| {
11426 return astgen.failOff(
11427 token,
11428 offset + @as(u32, @intCast(bad_index)),
11429 "expected hex digit, found '{c}'",
11430 .{raw_string[bad_index]},
11431 );
11432 },
11433 .empty_unicode_escape_sequence => |bad_index| {
11434 return astgen.failOff(
11435 token,
11436 offset + @as(u32, @intCast(bad_index)),
11437 "empty unicode escape sequence",
11438 .{},
11439 );
11440 },
11441 .expected_hex_digit_or_rbrace => |bad_index| {
11442 return astgen.failOff(
11443 token,
11444 offset + @as(u32, @intCast(bad_index)),
11445 "expected hex digit or '}}', found '{c}'",
11446 .{raw_string[bad_index]},
11447 );
11448 },
11449 .invalid_unicode_codepoint => |bad_index| {
11450 return astgen.failOff(
11451 token,
11452 offset + @as(u32, @intCast(bad_index)),
11453 "unicode escape does not correspond to a valid unicode scalar value",
11454 .{},
11455 );
11456 },
11457 .expected_lbrace => |bad_index| {
11458 return astgen.failOff(
11459 token,
11460 offset + @as(u32, @intCast(bad_index)),
11461 "expected '{{', found '{c}",
11462 .{raw_string[bad_index]},
11463 );
11464 },
11465 .expected_rbrace => |bad_index| {
11466 return astgen.failOff(
11467 token,
11468 offset + @as(u32, @intCast(bad_index)),
11469 "expected '}}', found '{c}",
11470 .{raw_string[bad_index]},
11471 );
11472 },
11473 .expected_single_quote => |bad_index| {
11474 return astgen.failOff(
11475 token,
11476 offset + @as(u32, @intCast(bad_index)),
11477 "expected single quote ('), found '{c}",
11478 .{raw_string[bad_index]},
11479 );
11480 },
11481 .invalid_character => |bad_index| {
11482 return astgen.failOff(
11483 token,
11484 offset + @as(u32, @intCast(bad_index)),
11485 "invalid byte in string or character literal: '{c}'",
11486 .{raw_string[bad_index]},
11487 );
11488 },
11489 .empty_char_literal => {
11490 return astgen.failOff(token, offset, "empty character literal", .{});
11491 },
11492 }
11418 return err.lower(raw_string, offset, AstGen.failOff, .{ astgen, token });
1149311419}
1149411420
1149511421fn failNode(
......@@ -14019,30 +13945,40 @@ fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
1401913945}
1402013946
1402113947fn lowerAstErrors(astgen: *AstGen) !void {
13948 const gpa = astgen.gpa;
1402213949 const tree = astgen.tree;
1402313950 assert(tree.errors.len > 0);
1402413951
14025 const gpa = astgen.gpa;
14026 const parse_err = tree.errors[0];
14027
1402813952 var msg: std.ArrayListUnmanaged(u8) = .empty;
1402913953 defer msg.deinit(gpa);
1403013954
1403113955 var notes: std.ArrayListUnmanaged(u32) = .empty;
1403213956 defer notes.deinit(gpa);
1403313957
14034 for (tree.errors[1..]) |note| {
14035 if (!note.is_note) break;
14036
13958 var cur_err = tree.errors[0];
13959 for (tree.errors[1..]) |err| {
13960 if (err.is_note) {
13961 try tree.renderError(err, msg.writer(gpa));
13962 try notes.append(gpa, try astgen.errNoteTok(err.token, "{s}", .{msg.items}));
13963 } else {
13964 // Flush error
13965 const extra_offset = tree.errorOffset(cur_err);
13966 try tree.renderError(cur_err, msg.writer(gpa));
13967 try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
13968 notes.clearRetainingCapacity();
13969 cur_err = err;
13970
13971 // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus.
13972 // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors.
13973 return;
13974 }
1403713975 msg.clearRetainingCapacity();
14038 try tree.renderError(note, msg.writer(gpa));
14039 try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items}));
1404013976 }
1404113977
14042 const extra_offset = tree.errorOffset(parse_err);
14043 msg.clearRetainingCapacity();
14044 try tree.renderError(parse_err, msg.writer(gpa));
14045 try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
13978 // Flush error
13979 const extra_offset = tree.errorOffset(cur_err);
13980 try tree.renderError(cur_err, msg.writer(gpa));
13981 try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
1404613982}
1404713983
1404813984const DeclarationName = union(enum) {
lib/std/zig/ErrorBundle.zig+72-1
......@@ -507,7 +507,7 @@ pub const Wip = struct {
507507 }
508508
509509 if (item.data.notes != 0) {
510 const notes_start = try eb.reserveNotes(item.data.notes);
510 const notes_start = try eb.reserveNotes(item.data.notesLen(zir));
511511 const block = zir.extraData(Zir.Inst.Block, item.data.notes);
512512 const body = zir.extra[block.end..][0..block.data.body_len];
513513 for (notes_start.., body) |note_i, body_elem| {
......@@ -547,6 +547,77 @@ pub const Wip = struct {
547547 }
548548 }
549549
550 pub fn addZoirErrorMessages(
551 eb: *ErrorBundle.Wip,
552 zoir: std.zig.Zoir,
553 tree: std.zig.Ast,
554 source: [:0]const u8,
555 src_path: []const u8,
556 ) !void {
557 assert(zoir.hasCompileErrors());
558
559 for (zoir.compile_errors) |err| {
560 const err_span: std.zig.Ast.Span = span: {
561 if (err.token == std.zig.Zoir.CompileError.invalid_token) {
562 break :span tree.nodeToSpan(err.node_or_offset);
563 }
564 const token_start = tree.tokens.items(.start)[err.token];
565 const start = token_start + err.node_or_offset;
566 const end = token_start + @as(u32, @intCast(tree.tokenSlice(err.token).len));
567 break :span .{ .start = start, .end = end, .main = start };
568 };
569 const err_loc = std.zig.findLineColumn(source, err_span.main);
570
571 try eb.addRootErrorMessage(.{
572 .msg = try eb.addString(err.msg.get(zoir)),
573 .src_loc = try eb.addSourceLocation(.{
574 .src_path = try eb.addString(src_path),
575 .span_start = err_span.start,
576 .span_main = err_span.main,
577 .span_end = err_span.end,
578 .line = @intCast(err_loc.line),
579 .column = @intCast(err_loc.column),
580 .source_line = try eb.addString(err_loc.source_line),
581 }),
582 .notes_len = err.note_count,
583 });
584
585 const notes_start = try eb.reserveNotes(err.note_count);
586 for (notes_start.., err.first_note.., 0..err.note_count) |eb_note_idx, zoir_note_idx, _| {
587 const note = zoir.error_notes[zoir_note_idx];
588 const note_span: std.zig.Ast.Span = span: {
589 if (note.token == std.zig.Zoir.CompileError.invalid_token) {
590 break :span tree.nodeToSpan(note.node_or_offset);
591 }
592 const token_start = tree.tokens.items(.start)[note.token];
593 const start = token_start + note.node_or_offset;
594 const end = token_start + @as(u32, @intCast(tree.tokenSlice(note.token).len));
595 break :span .{ .start = start, .end = end, .main = start };
596 };
597 const note_loc = std.zig.findLineColumn(source, note_span.main);
598
599 // This line can cause `wip.extra.items` to be resized.
600 const note_index = @intFromEnum(try eb.addErrorMessage(.{
601 .msg = try eb.addString(note.msg.get(zoir)),
602 .src_loc = try eb.addSourceLocation(.{
603 .src_path = try eb.addString(src_path),
604 .span_start = note_span.start,
605 .span_main = note_span.main,
606 .span_end = note_span.end,
607 .line = @intCast(note_loc.line),
608 .column = @intCast(note_loc.column),
609 .source_line = if (note_loc.eql(err_loc))
610 0
611 else
612 try eb.addString(note_loc.source_line),
613 }),
614 .notes_len = 0,
615 }));
616 eb.extra.items[eb_note_idx] = note_index;
617 }
618 }
619 }
620
550621 fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {
551622 const other_msg = other.getErrorMessage(msg_index);
552623 const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc);
lib/std/zig/Zoir.zig created+239
......@@ -0,0 +1,239 @@
1//! Zig Object Intermediate Representation.
2//! Simplified AST for the ZON (Zig Object Notation) format.
3//! `ZonGen` converts `Ast` to `Zoir`.
4
5nodes: std.MultiArrayList(Node.Repr).Slice,
6extra: []u32,
7limbs: []std.math.big.Limb,
8string_bytes: []u8,
9
10compile_errors: []Zoir.CompileError,
11error_notes: []Zoir.CompileError.Note,
12
13pub fn hasCompileErrors(zoir: Zoir) bool {
14 if (zoir.compile_errors.len > 0) {
15 assert(zoir.nodes.len == 0);
16 assert(zoir.extra.len == 0);
17 assert(zoir.limbs.len == 0);
18 return true;
19 } else {
20 assert(zoir.error_notes.len == 0);
21 return false;
22 }
23}
24
25pub fn deinit(zoir: Zoir, gpa: Allocator) void {
26 var nodes = zoir.nodes;
27 nodes.deinit(gpa);
28
29 gpa.free(zoir.extra);
30 gpa.free(zoir.limbs);
31 gpa.free(zoir.string_bytes);
32 gpa.free(zoir.compile_errors);
33 gpa.free(zoir.error_notes);
34}
35
36pub const Node = union(enum) {
37 /// A literal `true` value.
38 true,
39 /// A literal `false` value.
40 false,
41 /// A literal `null` value.
42 null,
43 /// A literal `inf` value.
44 pos_inf,
45 /// A literal `-inf` value.
46 neg_inf,
47 /// A literal `nan` value.
48 nan,
49 /// An integer literal.
50 int_literal: union(enum) {
51 small: i32,
52 big: std.math.big.int.Const,
53 },
54 /// A floating-point literal.
55 float_literal: f128,
56 /// A Unicode codepoint literal.
57 char_literal: u32,
58 /// An enum literal. The string is the literal, i.e. `foo` for `.foo`.
59 enum_literal: NullTerminatedString,
60 /// A string literal.
61 string_literal: []const u8,
62 /// An empty struct/array literal, i.e. `.{}`.
63 empty_literal,
64 /// An array literal. The `Range` gives the elements of the array literal.
65 array_literal: Node.Index.Range,
66 /// A struct literal. `names.len` is always equal to `vals.len`.
67 struct_literal: struct {
68 names: []const NullTerminatedString,
69 vals: Node.Index.Range,
70 },
71
72 pub const Index = enum(u32) {
73 root = 0,
74 _,
75
76 pub fn get(idx: Index, zoir: Zoir) Node {
77 const repr = zoir.nodes.get(@intFromEnum(idx));
78 return switch (repr.tag) {
79 .true => .true,
80 .false => .false,
81 .null => .null,
82 .pos_inf => .pos_inf,
83 .neg_inf => .neg_inf,
84 .nan => .nan,
85 .int_literal_small => .{ .int_literal = .{ .small = @bitCast(repr.data) } },
86 .int_literal_pos, .int_literal_neg => .{ .int_literal = .{ .big = .{
87 .limbs = l: {
88 const limb_count, const limbs_idx = zoir.extra[repr.data..][0..2].*;
89 break :l zoir.limbs[limbs_idx..][0..limb_count];
90 },
91 .positive = switch (repr.tag) {
92 .int_literal_pos => true,
93 .int_literal_neg => false,
94 else => unreachable,
95 },
96 } } },
97 .float_literal_small => .{ .float_literal = @as(f32, @bitCast(repr.data)) },
98 .float_literal => .{ .float_literal = @bitCast(zoir.extra[repr.data..][0..4].*) },
99 .char_literal => .{ .char_literal = repr.data },
100 .enum_literal => .{ .enum_literal = @enumFromInt(repr.data) },
101 .string_literal => .{ .string_literal = s: {
102 const start, const len = zoir.extra[repr.data..][0..2].*;
103 break :s zoir.string_bytes[start..][0..len];
104 } },
105 .string_literal_null => .{ .string_literal = NullTerminatedString.get(@enumFromInt(repr.data), zoir) },
106 .empty_literal => .empty_literal,
107 .array_literal => .{ .array_literal = a: {
108 const elem_count, const first_elem = zoir.extra[repr.data..][0..2].*;
109 break :a .{ .start = @enumFromInt(first_elem), .len = elem_count };
110 } },
111 .struct_literal => .{ .struct_literal = s: {
112 const elem_count, const first_elem = zoir.extra[repr.data..][0..2].*;
113 const field_names = zoir.extra[repr.data + 2 ..][0..elem_count];
114 break :s .{
115 .names = @ptrCast(field_names),
116 .vals = .{ .start = @enumFromInt(first_elem), .len = elem_count },
117 };
118 } },
119 };
120 }
121
122 pub fn getAstNode(idx: Index, zoir: Zoir) std.zig.Ast.Node.Index {
123 return zoir.nodes.items(.ast_node)[@intFromEnum(idx)];
124 }
125
126 pub const Range = struct {
127 start: Index,
128 len: u32,
129
130 pub fn at(r: Range, i: u32) Index {
131 assert(i < r.len);
132 return @enumFromInt(@intFromEnum(r.start) + i);
133 }
134 };
135 };
136
137 pub const Repr = struct {
138 tag: Tag,
139 data: u32,
140 ast_node: std.zig.Ast.Node.Index,
141
142 pub const Tag = enum(u8) {
143 /// `data` is ignored.
144 true,
145 /// `data` is ignored.
146 false,
147 /// `data` is ignored.
148 null,
149 /// `data` is ignored.
150 pos_inf,
151 /// `data` is ignored.
152 neg_inf,
153 /// `data` is ignored.
154 nan,
155 /// `data` is the `i32` value.
156 int_literal_small,
157 /// `data` is index into `extra` of:
158 /// * `limb_count: u32`
159 /// * `limbs_idx: u32`
160 int_literal_pos,
161 /// Identical to `int_literal_pos`, except the value is negative.
162 int_literal_neg,
163 /// `data` is the `f32` value.
164 float_literal_small,
165 /// `data` is index into `extra` of 4 elements which are a bitcast `f128`.
166 float_literal,
167 /// `data` is the `u32` value.
168 char_literal,
169 /// `data` is a `NullTerminatedString`.
170 enum_literal,
171 /// `data` is index into `extra` of:
172 /// * `start: u32`
173 /// * `len: u32`
174 string_literal,
175 /// Null-terminated string literal,
176 /// `data` is a `NullTerminatedString`.
177 string_literal_null,
178 /// An empty struct/array literal, `.{}`.
179 /// `data` is ignored.
180 empty_literal,
181 /// `data` is index into `extra` of:
182 /// * `elem_count: u32`
183 /// * `first_elem: Node.Index`
184 /// The nodes `first_elem .. first_elem + elem_count` are the children.
185 array_literal,
186 /// `data` is index into `extra` of:
187 /// * `elem_count: u32`
188 /// * `first_elem: Node.Index`
189 /// * `field_name: NullTerminatedString` for each `elem_count`
190 /// The nodes `first_elem .. first_elem + elem_count` are the children.
191 struct_literal,
192 };
193 };
194};
195
196pub const NullTerminatedString = enum(u32) {
197 _,
198 pub fn get(nts: NullTerminatedString, zoir: Zoir) [:0]const u8 {
199 const idx = std.mem.indexOfScalar(u8, zoir.string_bytes[@intFromEnum(nts)..], 0).?;
200 return zoir.string_bytes[@intFromEnum(nts)..][0..idx :0];
201 }
202};
203
204pub const CompileError = extern struct {
205 msg: NullTerminatedString,
206 token: Ast.TokenIndex,
207 /// If `token == invalid_token`, this is an `Ast.Node.Index`.
208 /// Otherwise, this is a byte offset into `token`.
209 node_or_offset: u32,
210
211 /// Ignored if `note_count == 0`.
212 first_note: u32,
213 note_count: u32,
214
215 pub fn getNotes(err: CompileError, zoir: Zoir) []const Note {
216 return zoir.error_notes[err.first_note..][0..err.note_count];
217 }
218
219 pub const Note = extern struct {
220 msg: NullTerminatedString,
221 token: Ast.TokenIndex,
222 /// If `token == invalid_token`, this is an `Ast.Node.Index`.
223 /// Otherwise, this is a byte offset into `token`.
224 node_or_offset: u32,
225 };
226
227 pub const invalid_token: Ast.TokenIndex = std.math.maxInt(Ast.TokenIndex);
228
229 comptime {
230 assert(std.meta.hasUniqueRepresentation(CompileError));
231 assert(std.meta.hasUniqueRepresentation(Note));
232 }
233};
234
235const std = @import("std");
236const assert = std.debug.assert;
237const Allocator = std.mem.Allocator;
238const Ast = std.zig.Ast;
239const Zoir = @This();
lib/std/zig/ZonGen.zig created+835
......@@ -0,0 +1,835 @@
1//! Ingests an `Ast` and produces a `Zoir`.
2
3gpa: Allocator,
4tree: Ast,
5
6nodes: std.MultiArrayList(Zoir.Node.Repr),
7extra: std.ArrayListUnmanaged(u32),
8limbs: std.ArrayListUnmanaged(std.math.big.Limb),
9string_bytes: std.ArrayListUnmanaged(u8),
10string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage),
11
12compile_errors: std.ArrayListUnmanaged(Zoir.CompileError),
13error_notes: std.ArrayListUnmanaged(Zoir.CompileError.Note),
14
15pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zoir {
16 assert(tree.mode == .zon);
17
18 var zg: ZonGen = .{
19 .gpa = gpa,
20 .tree = tree,
21 .nodes = .empty,
22 .extra = .empty,
23 .limbs = .empty,
24 .string_bytes = .empty,
25 .string_table = .empty,
26 .compile_errors = .empty,
27 .error_notes = .empty,
28 };
29 defer {
30 zg.nodes.deinit(gpa);
31 zg.extra.deinit(gpa);
32 zg.limbs.deinit(gpa);
33 zg.string_bytes.deinit(gpa);
34 zg.string_table.deinit(gpa);
35 zg.compile_errors.deinit(gpa);
36 zg.error_notes.deinit(gpa);
37 }
38
39 if (tree.errors.len == 0) {
40 const root_ast_node = tree.nodes.items(.data)[0].lhs;
41 try zg.nodes.append(gpa, undefined); // index 0; root node
42 try zg.expr(root_ast_node, .root);
43 } else {
44 try zg.lowerAstErrors();
45 }
46
47 if (zg.compile_errors.items.len > 0) {
48 const string_bytes = try zg.string_bytes.toOwnedSlice(gpa);
49 errdefer gpa.free(string_bytes);
50 const compile_errors = try zg.compile_errors.toOwnedSlice(gpa);
51 errdefer gpa.free(compile_errors);
52 const error_notes = try zg.error_notes.toOwnedSlice(gpa);
53 errdefer gpa.free(error_notes);
54
55 return .{
56 .nodes = .empty,
57 .extra = &.{},
58 .limbs = &.{},
59 .string_bytes = string_bytes,
60 .compile_errors = compile_errors,
61 .error_notes = error_notes,
62 };
63 } else {
64 assert(zg.error_notes.items.len == 0);
65
66 var nodes = zg.nodes.toOwnedSlice();
67 errdefer nodes.deinit(gpa);
68 const extra = try zg.extra.toOwnedSlice(gpa);
69 errdefer gpa.free(extra);
70 const limbs = try zg.limbs.toOwnedSlice(gpa);
71 errdefer gpa.free(limbs);
72 const string_bytes = try zg.string_bytes.toOwnedSlice(gpa);
73 errdefer gpa.free(string_bytes);
74
75 return .{
76 .nodes = nodes,
77 .extra = extra,
78 .limbs = limbs,
79 .string_bytes = string_bytes,
80 .compile_errors = &.{},
81 .error_notes = &.{},
82 };
83 }
84}
85
86fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator.Error!void {
87 const gpa = zg.gpa;
88 const tree = zg.tree;
89 const node_tags = tree.nodes.items(.tag);
90 const node_datas = tree.nodes.items(.data);
91 const main_tokens = tree.nodes.items(.main_token);
92
93 switch (node_tags[node]) {
94 .root => unreachable,
95 .@"usingnamespace" => unreachable,
96 .test_decl => unreachable,
97 .container_field_init => unreachable,
98 .container_field_align => unreachable,
99 .container_field => unreachable,
100 .fn_decl => unreachable,
101 .global_var_decl => unreachable,
102 .local_var_decl => unreachable,
103 .simple_var_decl => unreachable,
104 .aligned_var_decl => unreachable,
105 .@"defer" => unreachable,
106 .@"errdefer" => unreachable,
107 .switch_case => unreachable,
108 .switch_case_inline => unreachable,
109 .switch_case_one => unreachable,
110 .switch_case_inline_one => unreachable,
111 .switch_range => unreachable,
112 .asm_output => unreachable,
113 .asm_input => unreachable,
114 .for_range => unreachable,
115 .assign => unreachable,
116 .assign_destructure => unreachable,
117 .assign_shl => unreachable,
118 .assign_shl_sat => unreachable,
119 .assign_shr => unreachable,
120 .assign_bit_and => unreachable,
121 .assign_bit_or => unreachable,
122 .assign_bit_xor => unreachable,
123 .assign_div => unreachable,
124 .assign_sub => unreachable,
125 .assign_sub_wrap => unreachable,
126 .assign_sub_sat => unreachable,
127 .assign_mod => unreachable,
128 .assign_add => unreachable,
129 .assign_add_wrap => unreachable,
130 .assign_add_sat => unreachable,
131 .assign_mul => unreachable,
132 .assign_mul_wrap => unreachable,
133 .assign_mul_sat => unreachable,
134
135 .shl,
136 .shr,
137 .add,
138 .add_wrap,
139 .add_sat,
140 .sub,
141 .sub_wrap,
142 .sub_sat,
143 .mul,
144 .mul_wrap,
145 .mul_sat,
146 .div,
147 .mod,
148 .shl_sat,
149 .bit_and,
150 .bit_or,
151 .bit_xor,
152 .bang_equal,
153 .equal_equal,
154 .greater_than,
155 .greater_or_equal,
156 .less_than,
157 .less_or_equal,
158 .array_cat,
159 .array_mult,
160 .bool_and,
161 .bool_or,
162 .bool_not,
163 .bit_not,
164 .negation_wrap,
165 => try zg.addErrorTok(main_tokens[node], "operator '{s}' is not allowed in ZON", .{tree.tokenSlice(main_tokens[node])}),
166
167 .error_union,
168 .merge_error_sets,
169 .optional_type,
170 .anyframe_literal,
171 .anyframe_type,
172 .ptr_type_aligned,
173 .ptr_type_sentinel,
174 .ptr_type,
175 .ptr_type_bit_range,
176 .container_decl,
177 .container_decl_trailing,
178 .container_decl_arg,
179 .container_decl_arg_trailing,
180 .container_decl_two,
181 .container_decl_two_trailing,
182 .tagged_union,
183 .tagged_union_trailing,
184 .tagged_union_enum_tag,
185 .tagged_union_enum_tag_trailing,
186 .tagged_union_two,
187 .tagged_union_two_trailing,
188 .array_type,
189 .array_type_sentinel,
190 .error_set_decl,
191 .fn_proto_simple,
192 .fn_proto_multi,
193 .fn_proto_one,
194 .fn_proto,
195 => try zg.addErrorNode(node, "types are not available in ZON", .{}),
196
197 .call_one,
198 .call_one_comma,
199 .async_call_one,
200 .async_call_one_comma,
201 .call,
202 .call_comma,
203 .async_call,
204 .async_call_comma,
205 .@"return",
206 .if_simple,
207 .@"if",
208 .while_simple,
209 .while_cont,
210 .@"while",
211 .for_simple,
212 .@"for",
213 .@"catch",
214 .@"orelse",
215 .@"break",
216 .@"continue",
217 .@"switch",
218 .switch_comma,
219 .@"nosuspend",
220 .@"suspend",
221 .@"await",
222 .@"resume",
223 .@"try",
224 .unreachable_literal,
225 => try zg.addErrorNode(node, "control flow is not allowed in ZON", .{}),
226
227 .@"comptime" => try zg.addErrorNode(node, "keyword 'comptime' is not allowed in ZON", .{}),
228 .asm_simple, .@"asm" => try zg.addErrorNode(node, "inline asm is not allowed in ZON", .{}),
229
230 .builtin_call_two,
231 .builtin_call_two_comma,
232 .builtin_call,
233 .builtin_call_comma,
234 => try zg.addErrorNode(node, "builtin function calls are not allowed in ZON", .{}),
235
236 .field_access => try zg.addErrorNode(node, "field accesses are not allowed in ZON", .{}),
237
238 .slice_open,
239 .slice,
240 .slice_sentinel,
241 => try zg.addErrorNode(node, "slice operator is not allowed in ZON", .{}),
242
243 .deref, .address_of => try zg.addErrorTok(main_tokens[node], "pointers are not available in ZON", .{}),
244 .unwrap_optional => try zg.addErrorTok(main_tokens[node], "optionals are not available in ZON", .{}),
245 .error_value => try zg.addErrorNode(node, "errors are not available in ZON", .{}),
246
247 .array_access => try zg.addErrorTok(node, "array indexing is not allowed in ZON", .{}),
248
249 .block_two,
250 .block_two_semicolon,
251 .block,
252 .block_semicolon,
253 => try zg.addErrorNode(node, "blocks are not allowed in ZON", .{}),
254
255 .array_init_one,
256 .array_init_one_comma,
257 .array_init,
258 .array_init_comma,
259 .struct_init_one,
260 .struct_init_one_comma,
261 .struct_init,
262 .struct_init_comma,
263 => {
264 var buf: [2]Ast.Node.Index = undefined;
265
266 const type_node = if (tree.fullArrayInit(&buf, node)) |full|
267 full.ast.type_expr
268 else if (tree.fullStructInit(&buf, node)) |full|
269 full.ast.type_expr
270 else
271 unreachable;
272
273 try zg.addErrorNodeNotes(type_node, "types are not available in ZON", .{}, &.{
274 try zg.errNoteNode(type_node, "replace the type with '.'", .{}),
275 });
276 },
277
278 .grouped_expression => {
279 try zg.addErrorTokNotes(main_tokens[node], "expression grouping is not allowed in ZON", .{}, &.{
280 try zg.errNoteTok(main_tokens[node], "these parentheses are always redundant", .{}),
281 });
282 return zg.expr(node_datas[node].lhs, dest_node);
283 },
284
285 .negation => {
286 const child_node = node_datas[node].lhs;
287 switch (node_tags[child_node]) {
288 .number_literal => return zg.numberLiteral(child_node, node, dest_node, .negative),
289 .identifier => {
290 const child_ident = tree.tokenSlice(main_tokens[child_node]);
291 if (mem.eql(u8, child_ident, "inf")) {
292 zg.setNode(dest_node, .{
293 .tag = .neg_inf,
294 .data = 0, // ignored
295 .ast_node = node,
296 });
297 return;
298 }
299 },
300 else => {},
301 }
302 try zg.addErrorTok(main_tokens[node], "expected number or 'inf' after '-'", .{});
303 },
304 .number_literal => try zg.numberLiteral(node, node, dest_node, .positive),
305 .char_literal => try zg.charLiteral(node, dest_node),
306
307 .identifier => try zg.identifier(node, dest_node),
308
309 .enum_literal => {
310 const str_index = zg.identAsString(main_tokens[node]) catch |err| switch (err) {
311 error.BadString => undefined, // doesn't matter, there's an error
312 error.OutOfMemory => |e| return e,
313 };
314 zg.setNode(dest_node, .{
315 .tag = .enum_literal,
316 .data = @intFromEnum(str_index),
317 .ast_node = node,
318 });
319 },
320 .string_literal, .multiline_string_literal => if (zg.strLitAsString(node)) |result| switch (result) {
321 .nts => |nts| zg.setNode(dest_node, .{
322 .tag = .string_literal_null,
323 .data = @intFromEnum(nts),
324 .ast_node = node,
325 }),
326 .slice => |slice| {
327 const extra_index: u32 = @intCast(zg.extra.items.len);
328 try zg.extra.appendSlice(zg.gpa, &.{ slice.start, slice.len });
329 zg.setNode(dest_node, .{
330 .tag = .string_literal,
331 .data = extra_index,
332 .ast_node = node,
333 });
334 },
335 } else |err| switch (err) {
336 error.BadString => {},
337 error.OutOfMemory => |e| return e,
338 },
339
340 .array_init_dot_two,
341 .array_init_dot_two_comma,
342 .array_init_dot,
343 .array_init_dot_comma,
344 => {
345 var buf: [2]Ast.Node.Index = undefined;
346 const full = tree.fullArrayInit(&buf, node).?;
347 assert(full.ast.elements.len != 0); // Otherwise it would be a struct init
348 assert(full.ast.type_expr == 0); // The tag was `array_init_dot_*`
349
350 const first_elem: u32 = @intCast(zg.nodes.len);
351 try zg.nodes.resize(gpa, zg.nodes.len + full.ast.elements.len);
352
353 const extra_index: u32 = @intCast(zg.extra.items.len);
354 try zg.extra.appendSlice(gpa, &.{
355 @intCast(full.ast.elements.len),
356 first_elem,
357 });
358
359 zg.setNode(dest_node, .{
360 .tag = .array_literal,
361 .data = extra_index,
362 .ast_node = node,
363 });
364
365 for (full.ast.elements, first_elem..) |elem_node, elem_dest_node| {
366 try zg.expr(elem_node, @enumFromInt(elem_dest_node));
367 }
368 },
369
370 .struct_init_dot_two,
371 .struct_init_dot_two_comma,
372 .struct_init_dot,
373 .struct_init_dot_comma,
374 => {
375 var buf: [2]Ast.Node.Index = undefined;
376 const full = tree.fullStructInit(&buf, node).?;
377 assert(full.ast.type_expr == 0); // The tag was `struct_init_dot_*`
378
379 if (full.ast.fields.len == 0) {
380 zg.setNode(dest_node, .{
381 .tag = .empty_literal,
382 .data = 0, // ignored
383 .ast_node = node,
384 });
385 return;
386 }
387
388 const first_elem: u32 = @intCast(zg.nodes.len);
389 try zg.nodes.resize(gpa, zg.nodes.len + full.ast.fields.len);
390
391 const extra_index: u32 = @intCast(zg.extra.items.len);
392 try zg.extra.ensureUnusedCapacity(gpa, 2 + full.ast.fields.len);
393 zg.extra.appendSliceAssumeCapacity(&.{
394 @intCast(full.ast.fields.len),
395 first_elem,
396 });
397 const names_start = extra_index + 2;
398 zg.extra.appendNTimesAssumeCapacity(undefined, full.ast.fields.len);
399
400 zg.setNode(dest_node, .{
401 .tag = .struct_literal,
402 .data = extra_index,
403 .ast_node = node,
404 });
405
406 for (full.ast.fields, names_start.., first_elem..) |elem_node, extra_name_idx, elem_dest_node| {
407 const name_token = tree.firstToken(elem_node) - 2;
408 zg.extra.items[extra_name_idx] = @intFromEnum(zg.identAsString(name_token) catch |err| switch (err) {
409 error.BadString => undefined, // doesn't matter, there's an error
410 error.OutOfMemory => |e| return e,
411 });
412 try zg.expr(elem_node, @enumFromInt(elem_dest_node));
413 }
414 },
415 }
416}
417
418fn parseStrLit(zg: *ZonGen, token: Ast.TokenIndex, offset: u32) !u32 {
419 const raw_string = zg.tree.tokenSlice(token)[offset..];
420 const start = zg.string_bytes.items.len;
421 switch (try std.zig.string_literal.parseWrite(zg.string_bytes.writer(zg.gpa), raw_string)) {
422 .success => return @intCast(start),
423 .failure => |err| {
424 try zg.lowerStrLitError(err, token, raw_string, offset);
425 return error.BadString;
426 },
427 }
428}
429
430fn parseMultilineStrLit(zg: *ZonGen, node: Ast.Node.Index) !u32 {
431 const gpa = zg.gpa;
432 const tree = zg.tree;
433 const string_bytes = &zg.string_bytes;
434
435 const first_tok, const last_tok = bounds: {
436 const node_data = tree.nodes.items(.data)[node];
437 break :bounds .{ node_data.lhs, node_data.rhs };
438 };
439
440 const str_index: u32 = @intCast(string_bytes.items.len);
441
442 // First line: do not append a newline.
443 {
444 const line_bytes = tree.tokenSlice(first_tok)[2..];
445 try string_bytes.appendSlice(gpa, line_bytes);
446 }
447 // Following lines: each line prepends a newline.
448 for (first_tok + 1..last_tok + 1) |tok_idx| {
449 const line_bytes = tree.tokenSlice(@intCast(tok_idx))[2..];
450 try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
451 string_bytes.appendAssumeCapacity('\n');
452 string_bytes.appendSliceAssumeCapacity(line_bytes);
453 }
454
455 return @intCast(str_index);
456}
457
458fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 {
459 const tree = zg.tree;
460 assert(tree.tokens.items(.tag)[ident_token] == .identifier);
461 const ident_name = tree.tokenSlice(ident_token);
462 if (!mem.startsWith(u8, ident_name, "@")) {
463 const start = zg.string_bytes.items.len;
464 try zg.string_bytes.appendSlice(zg.gpa, ident_name);
465 return @intCast(start);
466 } else {
467 const start = try zg.parseStrLit(ident_token, 1);
468 const slice = zg.string_bytes.items[start..];
469 if (mem.indexOfScalar(u8, slice, 0) != null) {
470 try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{});
471 return error.BadString;
472 } else if (slice.len == 0) {
473 try zg.addErrorTok(ident_token, "identifier cannot be empty", .{});
474 return error.BadString;
475 }
476 return start;
477 }
478}
479
480const StringLiteralResult = union(enum) {
481 nts: Zoir.NullTerminatedString,
482 slice: struct { start: u32, len: u32 },
483};
484
485fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) !StringLiteralResult {
486 const gpa = zg.gpa;
487 const string_bytes = &zg.string_bytes;
488 const str_index = switch (zg.tree.nodes.items(.tag)[str_node]) {
489 .string_literal => try zg.parseStrLit(zg.tree.nodes.items(.main_token)[str_node], 0),
490 .multiline_string_literal => try zg.parseMultilineStrLit(str_node),
491 else => unreachable,
492 };
493 const key: []const u8 = string_bytes.items[str_index..];
494 if (std.mem.indexOfScalar(u8, key, 0) != null) return .{ .slice = .{
495 .start = str_index,
496 .len = @intCast(key.len),
497 } };
498 const gop = try zg.string_table.getOrPutContextAdapted(
499 gpa,
500 key,
501 StringIndexAdapter{ .bytes = string_bytes },
502 StringIndexContext{ .bytes = string_bytes },
503 );
504 if (gop.found_existing) {
505 string_bytes.shrinkRetainingCapacity(str_index);
506 return .{ .nts = @enumFromInt(gop.key_ptr.*) };
507 }
508 gop.key_ptr.* = str_index;
509 try string_bytes.append(gpa, 0);
510 return .{ .nts = @enumFromInt(str_index) };
511}
512
513fn identAsString(zg: *ZonGen, ident_token: Ast.TokenIndex) !Zoir.NullTerminatedString {
514 const gpa = zg.gpa;
515 const string_bytes = &zg.string_bytes;
516 const str_index = try zg.appendIdentStr(ident_token);
517 const key: []const u8 = string_bytes.items[str_index..];
518 const gop = try zg.string_table.getOrPutContextAdapted(
519 gpa,
520 key,
521 StringIndexAdapter{ .bytes = string_bytes },
522 StringIndexContext{ .bytes = string_bytes },
523 );
524 if (gop.found_existing) {
525 string_bytes.shrinkRetainingCapacity(str_index);
526 return @enumFromInt(gop.key_ptr.*);
527 }
528 gop.key_ptr.* = str_index;
529 try string_bytes.append(gpa, 0);
530 return @enumFromInt(str_index);
531}
532
533fn numberLiteral(zg: *ZonGen, num_node: Ast.Node.Index, src_node: Ast.Node.Index, dest_node: Zoir.Node.Index, sign: enum { negative, positive }) !void {
534 const tree = zg.tree;
535 const num_token = tree.nodes.items(.main_token)[num_node];
536 const num_bytes = tree.tokenSlice(num_token);
537
538 switch (std.zig.parseNumberLiteral(num_bytes)) {
539 .int => |unsigned_num| {
540 if (unsigned_num == 0 and sign == .negative) {
541 try zg.addErrorTokNotes(num_token, "integer literal '-0' is ambiguous", .{}, &.{
542 try zg.errNoteTok(num_token, "use '0' for an integer zero", .{}),
543 try zg.errNoteTok(num_token, "use '-0.0' for a flaoting-point signed zero", .{}),
544 });
545 return;
546 }
547 const num: i65 = switch (sign) {
548 .positive => unsigned_num,
549 .negative => -@as(i65, unsigned_num),
550 };
551 if (std.math.cast(i32, num)) |x| {
552 zg.setNode(dest_node, .{
553 .tag = .int_literal_small,
554 .data = @bitCast(x),
555 .ast_node = src_node,
556 });
557 return;
558 }
559 const max_limbs = comptime std.math.big.int.calcTwosCompLimbCount(@bitSizeOf(@TypeOf(num)));
560 var limbs: [max_limbs]std.math.big.Limb = undefined;
561 var big_int: std.math.big.int.Mutable = .init(&limbs, num);
562 try zg.setBigIntLiteralNode(dest_node, src_node, big_int.toConst());
563 },
564 .big_int => |base| {
565 const gpa = zg.gpa;
566 const num_without_prefix = switch (base) {
567 .decimal => num_bytes,
568 .hex, .binary, .octal => num_bytes[2..],
569 };
570 var big_int: std.math.big.int.Managed = try .init(gpa);
571 defer big_int.deinit();
572 big_int.setString(@intFromEnum(base), num_without_prefix) catch |err| switch (err) {
573 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral`
574 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
575 error.OutOfMemory => return error.OutOfMemory,
576 };
577 switch (sign) {
578 .positive => {},
579 .negative => big_int.negate(),
580 }
581 try zg.setBigIntLiteralNode(dest_node, src_node, big_int.toConst());
582 },
583 .float => {
584 const unsigned_num = std.fmt.parseFloat(f128, num_bytes) catch |err| switch (err) {
585 error.InvalidCharacter => unreachable, // validated by tokenizer
586 };
587 const num: f128 = switch (sign) {
588 .positive => unsigned_num,
589 .negative => -unsigned_num,
590 };
591
592 {
593 // If the value fits into an f32 without losing any precision, store it that way.
594 @setFloatMode(.strict);
595 const smaller_float: f32 = @floatCast(num);
596 const bigger_again: f128 = smaller_float;
597 if (bigger_again == num) {
598 zg.setNode(dest_node, .{
599 .tag = .float_literal_small,
600 .data = @bitCast(smaller_float),
601 .ast_node = src_node,
602 });
603 return;
604 }
605 }
606
607 const elems: [4]u32 = @bitCast(num);
608 const extra_index: u32 = @intCast(zg.extra.items.len);
609 try zg.extra.appendSlice(zg.gpa, &elems);
610 zg.setNode(dest_node, .{
611 .tag = .float_literal,
612 .data = extra_index,
613 .ast_node = src_node,
614 });
615 },
616 .failure => |err| try zg.lowerNumberError(err, num_token, num_bytes),
617 }
618}
619
620fn setBigIntLiteralNode(zg: *ZonGen, dest_node: Zoir.Node.Index, src_node: Ast.Node.Index, val: std.math.big.int.Const) !void {
621 try zg.extra.ensureUnusedCapacity(zg.gpa, 2);
622 try zg.limbs.ensureUnusedCapacity(zg.gpa, val.limbs.len);
623
624 const limbs_idx: u32 = @intCast(zg.limbs.items.len);
625 zg.limbs.appendSliceAssumeCapacity(val.limbs);
626
627 const extra_idx: u32 = @intCast(zg.extra.items.len);
628 zg.extra.appendSliceAssumeCapacity(&.{ @intCast(val.limbs.len), limbs_idx });
629
630 zg.setNode(dest_node, .{
631 .tag = if (val.positive) .int_literal_pos else .int_literal_neg,
632 .data = extra_idx,
633 .ast_node = src_node,
634 });
635}
636
637fn charLiteral(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) !void {
638 const tree = zg.tree;
639 assert(tree.nodes.items(.tag)[node] == .char_literal);
640 const main_token = tree.nodes.items(.main_token)[node];
641 const slice = tree.tokenSlice(main_token);
642 switch (std.zig.parseCharLiteral(slice)) {
643 .success => |codepoint| zg.setNode(dest_node, .{
644 .tag = .char_literal,
645 .data = codepoint,
646 .ast_node = node,
647 }),
648 .failure => |err| try zg.lowerStrLitError(err, main_token, slice, 0),
649 }
650}
651
652fn identifier(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) !void {
653 const tree = zg.tree;
654 assert(tree.nodes.items(.tag)[node] == .identifier);
655 const main_token = tree.nodes.items(.main_token)[node];
656 const ident = tree.tokenSlice(main_token);
657
658 const tag: Zoir.Node.Repr.Tag = t: {
659 if (mem.eql(u8, ident, "true")) break :t .true;
660 if (mem.eql(u8, ident, "false")) break :t .false;
661 if (mem.eql(u8, ident, "null")) break :t .null;
662 if (mem.eql(u8, ident, "inf")) break :t .pos_inf;
663 if (mem.eql(u8, ident, "nan")) break :t .nan;
664 try zg.addErrorNodeNotes(node, "invalid expression", .{}, &.{
665 try zg.errNoteNode(node, "ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'", .{}),
666 try zg.errNoteNode(node, "precede identifier with '.' for an enum literal", .{}),
667 });
668 return;
669 };
670
671 zg.setNode(dest_node, .{
672 .tag = tag,
673 .data = 0, // ignored
674 .ast_node = node,
675 });
676}
677
678fn setNode(zg: *ZonGen, dest: Zoir.Node.Index, repr: Zoir.Node.Repr) void {
679 zg.nodes.set(@intFromEnum(dest), repr);
680}
681
682fn lowerStrLitError(zg: *ZonGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, raw_string: []const u8, offset: u32) Allocator.Error!void {
683 return err.lower(raw_string, offset, ZonGen.addErrorTokOff, .{ zg, token });
684}
685
686fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) Allocator.Error!void {
687 const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null;
688 switch (err) {
689 .leading_zero => if (is_float) {
690 try zg.addErrorTok(token, "number '{s}' has leading zero", .{bytes});
691 } else {
692 try zg.addErrorTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{
693 try zg.errNoteTok(token, "use '0o' prefix for octal literals", .{}),
694 });
695 },
696 .digit_after_base => try zg.addErrorTok(token, "expected a digit after base prefix", .{}),
697 .upper_case_base => |i| try zg.addErrorTokOff(token, @intCast(i), "base prefix must be lowercase", .{}),
698 .invalid_float_base => |i| try zg.addErrorTokOff(token, @intCast(i), "invalid base for float literal", .{}),
699 .repeated_underscore => |i| try zg.addErrorTokOff(token, @intCast(i), "repeated digit separator", .{}),
700 .invalid_underscore_after_special => |i| try zg.addErrorTokOff(token, @intCast(i), "expected digit before digit separator", .{}),
701 .invalid_digit => |info| try zg.addErrorTokOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }),
702 .invalid_digit_exponent => |i| try zg.addErrorTokOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}),
703 .duplicate_exponent => |i| try zg.addErrorTokOff(token, @intCast(i), "duplicate exponent", .{}),
704 .exponent_after_underscore => |i| try zg.addErrorTokOff(token, @intCast(i), "expected digit before exponent", .{}),
705 .special_after_underscore => |i| try zg.addErrorTokOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}),
706 .trailing_special => |i| try zg.addErrorTokOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}),
707 .trailing_underscore => |i| try zg.addErrorTokOff(token, @intCast(i), "trailing digit separator", .{}),
708 .duplicate_period => unreachable, // Validated by tokenizer
709 .invalid_character => unreachable, // Validated by tokenizer
710 .invalid_exponent_sign => |i| {
711 assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer
712 try zg.addErrorTokOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] });
713 },
714 .period_after_exponent => |i| try zg.addErrorTokOff(token, @intCast(i), "unexpected period after exponent", .{}),
715 }
716}
717
718fn errNoteNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {
719 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
720 const writer = zg.string_bytes.writer(zg.gpa);
721 try writer.print(format, args);
722 try writer.writeByte(0);
723
724 return .{
725 .msg = @enumFromInt(message_idx),
726 .token = Zoir.CompileError.invalid_token,
727 .node_or_offset = node,
728 };
729}
730
731fn errNoteTok(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype) Allocator.Error!Zoir.CompileError.Note {
732 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
733 const writer = zg.string_bytes.writer(zg.gpa);
734 try writer.print(format, args);
735 try writer.writeByte(0);
736
737 return .{
738 .msg = @enumFromInt(message_idx),
739 .token = tok,
740 .node_or_offset = 0,
741 };
742}
743
744fn addErrorNode(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype) Allocator.Error!void {
745 return zg.addErrorInner(Zoir.CompileError.invalid_token, node, format, args, &.{});
746}
747fn addErrorTok(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype) Allocator.Error!void {
748 return zg.addErrorInner(tok, 0, format, args, &.{});
749}
750fn addErrorNodeNotes(zg: *ZonGen, node: Ast.Node.Index, comptime format: []const u8, args: anytype, notes: []const Zoir.CompileError.Note) Allocator.Error!void {
751 return zg.addErrorInner(Zoir.CompileError.invalid_token, node, format, args, notes);
752}
753fn addErrorTokNotes(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype, notes: []const Zoir.CompileError.Note) Allocator.Error!void {
754 return zg.addErrorInner(tok, 0, format, args, notes);
755}
756fn addErrorTokOff(zg: *ZonGen, tok: Ast.TokenIndex, offset: u32, comptime format: []const u8, args: anytype) Allocator.Error!void {
757 return zg.addErrorInner(tok, offset, format, args, &.{});
758}
759fn addErrorTokNotesOff(zg: *ZonGen, tok: Ast.TokenIndex, offset: u32, comptime format: []const u8, args: anytype, notes: []const Zoir.CompileError.Note) Allocator.Error!void {
760 return zg.addErrorInner(tok, offset, format, args, notes);
761}
762
763fn addErrorInner(
764 zg: *ZonGen,
765 token: Ast.TokenIndex,
766 node_or_offset: u32,
767 comptime format: []const u8,
768 args: anytype,
769 notes: []const Zoir.CompileError.Note,
770) Allocator.Error!void {
771 const gpa = zg.gpa;
772
773 const first_note: u32 = @intCast(zg.error_notes.items.len);
774 try zg.error_notes.appendSlice(gpa, notes);
775
776 const message_idx: u32 = @intCast(zg.string_bytes.items.len);
777 const writer = zg.string_bytes.writer(zg.gpa);
778 try writer.print(format, args);
779 try writer.writeByte(0);
780
781 try zg.compile_errors.append(gpa, .{
782 .msg = @enumFromInt(message_idx),
783 .token = token,
784 .node_or_offset = node_or_offset,
785 .first_note = first_note,
786 .note_count = @intCast(notes.len),
787 });
788}
789
790fn lowerAstErrors(zg: *ZonGen) Allocator.Error!void {
791 const gpa = zg.gpa;
792 const tree = zg.tree;
793 assert(tree.errors.len > 0);
794
795 var msg: std.ArrayListUnmanaged(u8) = .empty;
796 defer msg.deinit(gpa);
797
798 var notes: std.ArrayListUnmanaged(Zoir.CompileError.Note) = .empty;
799 defer notes.deinit(gpa);
800
801 var cur_err = tree.errors[0];
802 for (tree.errors[1..]) |err| {
803 if (err.is_note) {
804 try tree.renderError(err, msg.writer(gpa));
805 try notes.append(gpa, try zg.errNoteTok(err.token, "{s}", .{msg.items}));
806 } else {
807 // Flush error
808 try tree.renderError(cur_err, msg.writer(gpa));
809 const extra_offset = tree.errorOffset(cur_err);
810 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
811 notes.clearRetainingCapacity();
812 cur_err = err;
813
814 // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus.
815 // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors.
816 return;
817 }
818 msg.clearRetainingCapacity();
819 }
820
821 // Flush error
822 const extra_offset = tree.errorOffset(cur_err);
823 try tree.renderError(cur_err, msg.writer(gpa));
824 try zg.addErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
825}
826
827const std = @import("std");
828const assert = std.debug.assert;
829const mem = std.mem;
830const Allocator = mem.Allocator;
831const StringIndexAdapter = std.hash_map.StringIndexAdapter;
832const StringIndexContext = std.hash_map.StringIndexContext;
833const ZonGen = @This();
834const Zoir = @import("Zoir.zig");
835const Ast = @import("Ast.zig");
lib/std/zig/string_literal.zig+35
......@@ -38,6 +38,41 @@ pub const Error = union(enum) {
3838 invalid_character: usize,
3939 /// `''`. Not returned for string literals.
4040 empty_char_literal,
41
42 /// Returns `func(first_args[0], ..., first_args[n], offset + bad_idx, format, args)`.
43 pub fn lower(
44 err: Error,
45 raw_string: []const u8,
46 offset: u32,
47 comptime func: anytype,
48 first_args: anytype,
49 ) @typeInfo(@TypeOf(func)).@"fn".return_type.? {
50 switch (err) {
51 inline else => |bad_index_or_void, tag| {
52 const bad_index: u32 = switch (@TypeOf(bad_index_or_void)) {
53 void => 0,
54 else => @intCast(bad_index_or_void),
55 };
56 const fmt_str: []const u8, const args = switch (tag) {
57 .invalid_escape_character => .{ "invalid escape character: '{c}'", .{raw_string[bad_index]} },
58 .expected_hex_digit => .{ "expected hex digit, found '{c}'", .{raw_string[bad_index]} },
59 .empty_unicode_escape_sequence => .{ "empty unicode escape sequence", .{} },
60 .expected_hex_digit_or_rbrace => .{ "expected hex digit or '}}', found '{c}'", .{raw_string[bad_index]} },
61 .invalid_unicode_codepoint => .{ "unicode escape does not correspond to a valid unicode scalar value", .{} },
62 .expected_lbrace => .{ "expected '{{', found '{c}'", .{raw_string[bad_index]} },
63 .expected_rbrace => .{ "expected '}}', found '{c}'", .{raw_string[bad_index]} },
64 .expected_single_quote => .{ "expected singel quote ('), found '{c}'", .{raw_string[bad_index]} },
65 .invalid_character => .{ "invalid byte in string or character literal: '{c}'", .{raw_string[bad_index]} },
66 .empty_char_literal => .{ "empty character literal", .{} },
67 };
68 return @call(.auto, func, first_args ++ .{
69 offset + bad_index,
70 fmt_str,
71 args,
72 });
73 },
74 }
75 }
4176};
4277
4378/// Asserts the slice starts and ends with single-quotes.
src/fmt.zig+78-30
......@@ -13,6 +13,7 @@ const usage_fmt =
1313 \\ if the list is non-empty
1414 \\ --ast-check Run zig ast-check on every file
1515 \\ --exclude [file] Exclude file or directory from formatting
16 \\ --zon Treat all input files as ZON, regardless of file extension
1617 \\
1718 \\
1819;
......@@ -21,6 +22,7 @@ const Fmt = struct {
2122 seen: SeenMap,
2223 any_error: bool,
2324 check_ast: bool,
25 force_zon: bool,
2426 color: Color,
2527 gpa: Allocator,
2628 arena: Allocator,
......@@ -35,9 +37,10 @@ pub fn run(
3537 args: []const []const u8,
3638) !void {
3739 var color: Color = .auto;
38 var stdin_flag: bool = false;
39 var check_flag: bool = false;
40 var check_ast_flag: bool = false;
40 var stdin_flag = false;
41 var check_flag = false;
42 var check_ast_flag = false;
43 var force_zon = false;
4144 var input_files = std.ArrayList([]const u8).init(gpa);
4245 defer input_files.deinit();
4346 var excluded_files = std.ArrayList([]const u8).init(gpa);
......@@ -74,6 +77,8 @@ pub fn run(
7477 i += 1;
7578 const next_arg = args[i];
7679 try excluded_files.append(next_arg);
80 } else if (mem.eql(u8, arg, "--zon")) {
81 force_zon = true;
7782 } else {
7883 fatal("unrecognized parameter: '{s}'", .{arg});
7984 }
......@@ -94,23 +99,40 @@ pub fn run(
9499 };
95100 defer gpa.free(source_code);
96101
97 var tree = std.zig.Ast.parse(gpa, source_code, .zig) catch |err| {
102 var tree = std.zig.Ast.parse(gpa, source_code, if (force_zon) .zon else .zig) catch |err| {
98103 fatal("error parsing stdin: {}", .{err});
99104 };
100105 defer tree.deinit(gpa);
101106
102107 if (check_ast_flag) {
103 var zir = try std.zig.AstGen.generate(gpa, tree);
104
105 if (zir.hasCompileErrors()) {
106 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
107 try wip_errors.init(gpa);
108 defer wip_errors.deinit();
109 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");
110 var error_bundle = try wip_errors.toOwnedBundle("");
111 defer error_bundle.deinit(gpa);
112 error_bundle.renderToStdErr(color.renderOptions());
113 process.exit(2);
108 if (!force_zon) {
109 var zir = try std.zig.AstGen.generate(gpa, tree);
110 defer zir.deinit(gpa);
111
112 if (zir.hasCompileErrors()) {
113 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
114 try wip_errors.init(gpa);
115 defer wip_errors.deinit();
116 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");
117 var error_bundle = try wip_errors.toOwnedBundle("");
118 defer error_bundle.deinit(gpa);
119 error_bundle.renderToStdErr(color.renderOptions());
120 process.exit(2);
121 }
122 } else {
123 const zoir = try std.zig.ZonGen.generate(gpa, tree);
124 defer zoir.deinit(gpa);
125
126 if (zoir.hasCompileErrors()) {
127 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
128 try wip_errors.init(gpa);
129 defer wip_errors.deinit();
130 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, "<stdin>");
131 var error_bundle = try wip_errors.toOwnedBundle("");
132 defer error_bundle.deinit(gpa);
133 error_bundle.renderToStdErr(color.renderOptions());
134 process.exit(2);
135 }
114136 }
115137 } else if (tree.errors.len != 0) {
116138 try std.zig.printAstErrorsToStderr(gpa, tree, "<stdin>", color);
......@@ -131,12 +153,13 @@ pub fn run(
131153 fatal("expected at least one source file argument", .{});
132154 }
133155
134 var fmt = Fmt{
156 var fmt: Fmt = .{
135157 .gpa = gpa,
136158 .arena = arena,
137 .seen = Fmt.SeenMap.init(gpa),
159 .seen = .init(gpa),
138160 .any_error = false,
139161 .check_ast = check_ast_flag,
162 .force_zon = force_zon,
140163 .color = color,
141164 .out_buffer = std.ArrayList(u8).init(gpa),
142165 };
......@@ -276,7 +299,13 @@ fn fmtPathFile(
276299 // Add to set after no longer possible to get error.IsDir.
277300 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
278301
279 var tree = try std.zig.Ast.parse(gpa, source_code, .zig);
302 const mode: std.zig.Ast.Mode = mode: {
303 if (fmt.force_zon) break :mode .zon;
304 if (mem.endsWith(u8, sub_path, ".zon")) break :mode .zon;
305 break :mode .zig;
306 };
307
308 var tree = try std.zig.Ast.parse(gpa, source_code, mode);
280309 defer tree.deinit(gpa);
281310
282311 if (tree.errors.len != 0) {
......@@ -289,18 +318,37 @@ fn fmtPathFile(
289318 if (stat.size > std.zig.max_src_size)
290319 return error.FileTooBig;
291320
292 var zir = try std.zig.AstGen.generate(gpa, tree);
293 defer zir.deinit(gpa);
294
295 if (zir.hasCompileErrors()) {
296 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
297 try wip_errors.init(gpa);
298 defer wip_errors.deinit();
299 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);
300 var error_bundle = try wip_errors.toOwnedBundle("");
301 defer error_bundle.deinit(gpa);
302 error_bundle.renderToStdErr(fmt.color.renderOptions());
303 fmt.any_error = true;
321 switch (mode) {
322 .zig => {
323 var zir = try std.zig.AstGen.generate(gpa, tree);
324 defer zir.deinit(gpa);
325
326 if (zir.hasCompileErrors()) {
327 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
328 try wip_errors.init(gpa);
329 defer wip_errors.deinit();
330 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);
331 var error_bundle = try wip_errors.toOwnedBundle("");
332 defer error_bundle.deinit(gpa);
333 error_bundle.renderToStdErr(fmt.color.renderOptions());
334 fmt.any_error = true;
335 }
336 },
337 .zon => {
338 var zoir = try std.zig.ZonGen.generate(gpa, tree);
339 defer zoir.deinit(gpa);
340
341 if (zoir.hasCompileErrors()) {
342 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
343 try wip_errors.init(gpa);
344 defer wip_errors.deinit();
345 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, file_path);
346 var error_bundle = try wip_errors.toOwnedBundle("");
347 defer error_bundle.deinit(gpa);
348 error_bundle.renderToStdErr(fmt.color.renderOptions());
349 fmt.any_error = true;
350 }
351 },
304352 }
305353 }
306354
src/main.zig+123-71
......@@ -19,6 +19,7 @@ const Directory = std.Build.Cache.Directory;
1919const EnvVar = std.zig.EnvVar;
2020const LibCInstallation = std.zig.LibCInstallation;
2121const AstGen = std.zig.AstGen;
22const ZonGen = std.zig.ZonGen;
2223const Server = std.zig.Server;
2324
2425const tracy = @import("tracy.zig");
......@@ -6007,15 +6008,16 @@ fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
60076008const usage_ast_check =
60086009 \\Usage: zig ast-check [file]
60096010 \\
6010 \\ Given a .zig source file, reports any compile errors that can be
6011 \\ ascertained on the basis of the source code alone, without target
6012 \\ information or type checking.
6011 \\ Given a .zig source file or .zon file, reports any compile errors
6012 \\ that can be ascertained on the basis of the source code alone,
6013 \\ without target information or type checking.
60136014 \\
60146015 \\ If [file] is omitted, stdin is used.
60156016 \\
60166017 \\Options:
60176018 \\ -h, --help Print this help and exit
60186019 \\ --color [auto|off|on] Enable or disable colored error messages
6020 \\ --zon Treat the input file as ZON, regardless of file extension
60196021 \\ -t (debug option) Output ZIR in text form to stdout
60206022 \\
60216023 \\
......@@ -6032,6 +6034,7 @@ fn cmdAstCheck(
60326034
60336035 var color: Color = .auto;
60346036 var want_output_text = false;
6037 var force_zon = false;
60356038 var zig_source_file: ?[]const u8 = null;
60366039
60376040 var i: usize = 0;
......@@ -6043,6 +6046,8 @@ fn cmdAstCheck(
60436046 return cleanExit();
60446047 } else if (mem.eql(u8, arg, "-t")) {
60456048 want_output_text = true;
6049 } else if (mem.eql(u8, arg, "--zon")) {
6050 force_zon = true;
60466051 } else if (mem.eql(u8, arg, "--color")) {
60476052 if (i + 1 >= args.len) {
60486053 fatal("expected [auto|on|off] after --color", .{});
......@@ -6110,89 +6115,136 @@ fn cmdAstCheck(
61106115 file.stat.size = source.len;
61116116 }
61126117
6118 const mode: Ast.Mode = mode: {
6119 if (force_zon) break :mode .zon;
6120 if (zig_source_file) |name| {
6121 if (mem.endsWith(u8, name, ".zon")) {
6122 break :mode .zon;
6123 }
6124 }
6125 break :mode .zig;
6126 };
6127
61136128 file.mod = try Package.Module.createLimited(arena, .{
61146129 .root = Path.cwd(),
61156130 .root_src_path = file.sub_file_path,
61166131 .fully_qualified_name = "root",
61176132 });
61186133
6119 file.tree = try Ast.parse(gpa, file.source, .zig);
6134 file.tree = try Ast.parse(gpa, file.source, mode);
61206135 file.tree_loaded = true;
61216136 defer file.tree.deinit(gpa);
61226137
6123 file.zir = try AstGen.generate(gpa, file.tree);
6124 file.zir_loaded = true;
6125 defer file.zir.deinit(gpa);
6138 switch (mode) {
6139 .zig => {
6140 file.zir = try AstGen.generate(gpa, file.tree);
6141 file.zir_loaded = true;
6142 defer file.zir.deinit(gpa);
6143
6144 if (file.zir.hasCompileErrors()) {
6145 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6146 try wip_errors.init(gpa);
6147 defer wip_errors.deinit();
6148 try Compilation.addZirErrorMessages(&wip_errors, &file);
6149 var error_bundle = try wip_errors.toOwnedBundle("");
6150 defer error_bundle.deinit(gpa);
6151 error_bundle.renderToStdErr(color.renderOptions());
6152
6153 if (file.zir.loweringFailed()) {
6154 process.exit(1);
6155 }
6156 }
61266157
6127 if (file.zir.hasCompileErrors()) {
6128 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6129 try wip_errors.init(gpa);
6130 defer wip_errors.deinit();
6131 try Compilation.addZirErrorMessages(&wip_errors, &file);
6132 var error_bundle = try wip_errors.toOwnedBundle("");
6133 defer error_bundle.deinit(gpa);
6134 error_bundle.renderToStdErr(color.renderOptions());
6158 if (!want_output_text) {
6159 if (file.zir.hasCompileErrors()) {
6160 process.exit(1);
6161 } else {
6162 return cleanExit();
6163 }
6164 }
6165 if (!build_options.enable_debug_extensions) {
6166 fatal("-t option only available in builds of zig with debug extensions", .{});
6167 }
61356168
6136 if (file.zir.loweringFailed()) {
6137 process.exit(1);
6138 }
6139 }
6169 {
6170 const token_bytes = @sizeOf(Ast.TokenList) +
6171 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
6172 const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len *
6173 (@sizeOf(Ast.Node.Tag) +
6174 @sizeOf(Ast.Node.Data) +
6175 @sizeOf(Ast.TokenIndex));
6176 const instruction_bytes = file.zir.instructions.len *
6177 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
6178 // the debug safety tag but we want to measure release size.
6179 (@sizeOf(Zir.Inst.Tag) + 8);
6180 const extra_bytes = file.zir.extra.len * @sizeOf(u32);
6181 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6182 file.zir.string_bytes.len * @sizeOf(u8);
6183 const stdout = io.getStdOut();
6184 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
6185 // zig fmt: off
6186 try stdout.writer().print(
6187 \\# Source bytes: {}
6188 \\# Tokens: {} ({})
6189 \\# AST Nodes: {} ({})
6190 \\# Total ZIR bytes: {}
6191 \\# Instructions: {d} ({})
6192 \\# String Table Bytes: {}
6193 \\# Extra Data Items: {d} ({})
6194 \\
6195 , .{
6196 fmtIntSizeBin(file.source.len),
6197 file.tree.tokens.len, fmtIntSizeBin(token_bytes),
6198 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
6199 fmtIntSizeBin(total_bytes),
6200 file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
6201 fmtIntSizeBin(file.zir.string_bytes.len),
6202 file.zir.extra.len, fmtIntSizeBin(extra_bytes),
6203 });
6204 // zig fmt: on
6205 }
61406206
6141 if (!want_output_text) {
6142 if (file.zir.hasCompileErrors()) {
6143 process.exit(1);
6144 } else {
6145 return cleanExit();
6146 }
6147 }
6148 if (!build_options.enable_debug_extensions) {
6149 fatal("-t option only available in builds of zig with debug extensions", .{});
6150 }
6207 try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
61516208
6152 {
6153 const token_bytes = @sizeOf(Ast.TokenList) +
6154 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
6155 const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len *
6156 (@sizeOf(Ast.Node.Tag) +
6157 @sizeOf(Ast.Node.Data) +
6158 @sizeOf(Ast.TokenIndex));
6159 const instruction_bytes = file.zir.instructions.len *
6160 // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
6161 // the debug safety tag but we want to measure release size.
6162 (@sizeOf(Zir.Inst.Tag) + 8);
6163 const extra_bytes = file.zir.extra.len * @sizeOf(u32);
6164 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6165 file.zir.string_bytes.len * @sizeOf(u8);
6166 const stdout = io.getStdOut();
6167 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
6168 // zig fmt: off
6169 try stdout.writer().print(
6170 \\# Source bytes: {}
6171 \\# Tokens: {} ({})
6172 \\# AST Nodes: {} ({})
6173 \\# Total ZIR bytes: {}
6174 \\# Instructions: {d} ({})
6175 \\# String Table Bytes: {}
6176 \\# Extra Data Items: {d} ({})
6177 \\
6178 , .{
6179 fmtIntSizeBin(file.source.len),
6180 file.tree.tokens.len, fmtIntSizeBin(token_bytes),
6181 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
6182 fmtIntSizeBin(total_bytes),
6183 file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
6184 fmtIntSizeBin(file.zir.string_bytes.len),
6185 file.zir.extra.len, fmtIntSizeBin(extra_bytes),
6186 });
6187 // zig fmt: on
6188 }
6209 if (file.zir.hasCompileErrors()) {
6210 process.exit(1);
6211 } else {
6212 return cleanExit();
6213 }
6214 },
6215 .zon => {
6216 const zoir = try ZonGen.generate(gpa, file.tree);
6217 defer zoir.deinit(gpa);
61896218
6190 try @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
6219 if (zoir.hasCompileErrors()) {
6220 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6221 try wip_errors.init(gpa);
6222 defer wip_errors.deinit();
61916223
6192 if (file.zir.hasCompileErrors()) {
6193 process.exit(1);
6194 } else {
6195 return cleanExit();
6224 {
6225 const src_path = try file.fullPath(gpa);
6226 defer gpa.free(src_path);
6227 try wip_errors.addZoirErrorMessages(zoir, file.tree, file.source, src_path);
6228 }
6229
6230 var error_bundle = try wip_errors.toOwnedBundle("");
6231 defer error_bundle.deinit(gpa);
6232 error_bundle.renderToStdErr(color.renderOptions());
6233
6234 process.exit(1);
6235 }
6236
6237 if (!want_output_text) {
6238 return cleanExit();
6239 }
6240
6241 if (!build_options.enable_debug_extensions) {
6242 fatal("-t option only available in builds of zig with debug extensions", .{});
6243 }
6244
6245 try @import("print_zoir.zig").renderToFile(zoir, arena, io.getStdOut());
6246 return cleanExit();
6247 },
61966248 }
61976249}
61986250
src/print_zoir.zig created+122
......@@ -0,0 +1,122 @@
1pub fn renderToFile(zoir: Zoir, arena: Allocator, f: std.fs.File) (std.fs.File.WriteError || Allocator.Error)!void {
2 var bw = std.io.bufferedWriter(f.writer());
3 try renderToWriter(zoir, arena, bw.writer());
4 try bw.flush();
5}
6
7pub fn renderToWriter(zoir: Zoir, arena: Allocator, w: anytype) (@TypeOf(w).Error || Allocator.Error)!void {
8 assert(!zoir.hasCompileErrors());
9
10 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
11 const bytes_per_node = comptime n: {
12 var n: usize = 0;
13 for (@typeInfo(Zoir.Node.Repr).@"struct".fields) |f| {
14 n += @sizeOf(f.type);
15 }
16 break :n n;
17 };
18
19 const node_bytes = zoir.nodes.len * bytes_per_node;
20 const extra_bytes = zoir.extra.len * @sizeOf(u32);
21 const limb_bytes = zoir.limbs.len * @sizeOf(std.math.big.Limb);
22 const string_bytes = zoir.string_bytes.len;
23
24 // zig fmt: off
25 try w.print(
26 \\# Nodes: {} ({})
27 \\# Extra Data Items: {} ({})
28 \\# BigInt Limbs: {} ({})
29 \\# String Table Bytes: {}
30 \\# Total ZON Bytes: {}
31 \\
32 , .{
33 zoir.nodes.len, fmtIntSizeBin(node_bytes),
34 zoir.extra.len, fmtIntSizeBin(extra_bytes),
35 zoir.limbs.len, fmtIntSizeBin(limb_bytes),
36 fmtIntSizeBin(string_bytes),
37 fmtIntSizeBin(node_bytes + extra_bytes + limb_bytes + string_bytes),
38 });
39 // zig fmt: on
40 var pz: PrintZon = .{
41 .w = w.any(),
42 .arena = arena,
43 .zoir = zoir,
44 .indent = 0,
45 };
46
47 return @errorCast(pz.renderRoot());
48}
49
50const PrintZon = struct {
51 w: std.io.AnyWriter,
52 arena: Allocator,
53 zoir: Zoir,
54 indent: u32,
55
56 fn renderRoot(pz: *PrintZon) anyerror!void {
57 try pz.renderNode(.root);
58 try pz.w.writeByte('\n');
59 }
60
61 fn renderNode(pz: *PrintZon, node: Zoir.Node.Index) anyerror!void {
62 const zoir = pz.zoir;
63 try pz.w.print("%{d} = ", .{@intFromEnum(node)});
64 switch (node.get(zoir)) {
65 .true => try pz.w.writeAll("true"),
66 .false => try pz.w.writeAll("false"),
67 .null => try pz.w.writeAll("null"),
68 .pos_inf => try pz.w.writeAll("inf"),
69 .neg_inf => try pz.w.writeAll("-inf"),
70 .nan => try pz.w.writeAll("nan"),
71 .int_literal => |storage| switch (storage) {
72 .small => |x| try pz.w.print("int({d})", .{x}),
73 .big => |x| {
74 const str = try x.toStringAlloc(pz.arena, 10, .lower);
75 try pz.w.print("int(big {s})", .{str});
76 },
77 },
78 .float_literal => |x| try pz.w.print("float({d})", .{x}),
79 .char_literal => |x| try pz.w.print("char({d})", .{x}),
80 .enum_literal => |x| try pz.w.print("enum_literal({p})", .{std.zig.fmtId(x.get(zoir))}),
81 .string_literal => |x| try pz.w.print("str(\"{}\")", .{std.zig.fmtEscapes(x)}),
82 .empty_literal => try pz.w.writeAll("empty_literal(.{})"),
83 .array_literal => |vals| {
84 try pz.w.writeAll("array_literal({");
85 pz.indent += 1;
86 for (0..vals.len) |idx| {
87 try pz.newline();
88 try pz.renderNode(vals.at(@intCast(idx)));
89 try pz.w.writeByte(',');
90 }
91 pz.indent -= 1;
92 try pz.newline();
93 try pz.w.writeAll("})");
94 },
95 .struct_literal => |s| {
96 try pz.w.writeAll("struct_literal({");
97 pz.indent += 1;
98 for (s.names, 0..s.vals.len) |name, idx| {
99 try pz.newline();
100 try pz.w.print("[{p}] ", .{std.zig.fmtId(name.get(zoir))});
101 try pz.renderNode(s.vals.at(@intCast(idx)));
102 try pz.w.writeByte(',');
103 }
104 pz.indent -= 1;
105 try pz.newline();
106 try pz.w.writeAll("})");
107 },
108 }
109 }
110
111 fn newline(pz: *PrintZon) !void {
112 try pz.w.writeByte('\n');
113 for (0..pz.indent) |_| {
114 try pz.w.writeByteNTimes(' ', 2);
115 }
116 }
117};
118
119const std = @import("std");
120const assert = std.debug.assert;
121const Allocator = std.mem.Allocator;
122const Zoir = std.zig.Zoir;