| author | |
| committer | |
| log | 7e8be213631821b0b767aec03c22a937541b7d0a |
| tree | edbfa24f29e3788a81e92532aefad74d61c2b43b |
| parent | 32354d1190a9d319f0f7970d6e39d2d951f2d3f4 |
| parent | c7485d73ac3e6dd105c6ee8fcf774493cc9eb31e |
| signature |
compiler: introduce ZonGen and make `ast-check` run it for ZON inputs9 files changed, 1531 insertions(+), 191 deletions(-)
lib/std/zig.zig+2| ... | ... | @@ -14,6 +14,8 @@ pub const isPrimitive = primitives.isPrimitive; |
| 14 | 14 | pub const Ast = @import("zig/Ast.zig"); |
| 15 | 15 | pub const AstGen = @import("zig/AstGen.zig"); |
| 16 | 16 | pub const Zir = @import("zig/Zir.zig"); |
| 17 | pub const Zoir = @import("zig/Zoir.zig"); | |
| 18 | pub const ZonGen = @import("zig/ZonGen.zig"); | |
| 17 | 19 | pub const system = @import("zig/system.zig"); |
| 18 | 20 | pub const CrossTarget = @compileError("deprecated; use std.Target.Query"); |
| 19 | 21 | pub 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 { |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | 132 | pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { |
| 133 | assert(tree.mode == .zig); | |
| 134 | ||
| 133 | 135 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 134 | 136 | defer arena.deinit(); |
| 135 | 137 | |
| ... | ... | @@ -11413,83 +11415,7 @@ fn parseStrLit( |
| 11413 | 11415 | |
| 11414 | 11416 | fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError { |
| 11415 | 11417 | 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 }); | |
| 11493 | 11419 | } |
| 11494 | 11420 | |
| 11495 | 11421 | fn failNode( |
| ... | ... | @@ -14019,30 +13945,40 @@ fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void { |
| 14019 | 13945 | } |
| 14020 | 13946 | |
| 14021 | 13947 | fn lowerAstErrors(astgen: *AstGen) !void { |
| 13948 | const gpa = astgen.gpa; | |
| 14022 | 13949 | const tree = astgen.tree; |
| 14023 | 13950 | assert(tree.errors.len > 0); |
| 14024 | 13951 | |
| 14025 | const gpa = astgen.gpa; | |
| 14026 | const parse_err = tree.errors[0]; | |
| 14027 | ||
| 14028 | 13952 | var msg: std.ArrayListUnmanaged(u8) = .empty; |
| 14029 | 13953 | defer msg.deinit(gpa); |
| 14030 | 13954 | |
| 14031 | 13955 | var notes: std.ArrayListUnmanaged(u32) = .empty; |
| 14032 | 13956 | defer notes.deinit(gpa); |
| 14033 | 13957 | |
| 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 | } | |
| 14037 | 13975 | msg.clearRetainingCapacity(); |
| 14038 | try tree.renderError(note, msg.writer(gpa)); | |
| 14039 | try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items})); | |
| 14040 | 13976 | } |
| 14041 | 13977 | |
| 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); | |
| 14046 | 13982 | } |
| 14047 | 13983 | |
| 14048 | 13984 | const DeclarationName = union(enum) { |
lib/std/zig/ErrorBundle.zig+72-1| ... | ... | @@ -507,7 +507,7 @@ pub const Wip = struct { |
| 507 | 507 | } |
| 508 | 508 | |
| 509 | 509 | 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)); | |
| 511 | 511 | const block = zir.extraData(Zir.Inst.Block, item.data.notes); |
| 512 | 512 | const body = zir.extra[block.end..][0..block.data.body_len]; |
| 513 | 513 | for (notes_start.., body) |note_i, body_elem| { |
| ... | ... | @@ -547,6 +547,77 @@ pub const Wip = struct { |
| 547 | 547 | } |
| 548 | 548 | } |
| 549 | 549 | |
| 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 | ||
| 550 | 621 | fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex { |
| 551 | 622 | const other_msg = other.getErrorMessage(msg_index); |
| 552 | 623 | 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 | ||
| 5 | nodes: std.MultiArrayList(Node.Repr).Slice, | |
| 6 | extra: []u32, | |
| 7 | limbs: []std.math.big.Limb, | |
| 8 | string_bytes: []u8, | |
| 9 | ||
| 10 | compile_errors: []Zoir.CompileError, | |
| 11 | error_notes: []Zoir.CompileError.Note, | |
| 12 | ||
| 13 | pub 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 | ||
| 25 | pub 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 | ||
| 36 | pub 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 | ||
| 196 | pub 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 | ||
| 204 | pub 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 | ||
| 235 | const std = @import("std"); | |
| 236 | const assert = std.debug.assert; | |
| 237 | const Allocator = std.mem.Allocator; | |
| 238 | const Ast = std.zig.Ast; | |
| 239 | const Zoir = @This(); |
lib/std/zig/ZonGen.zig created+835| ... | ... | @@ -0,0 +1,835 @@ |
| 1 | //! Ingests an `Ast` and produces a `Zoir`. | |
| 2 | ||
| 3 | gpa: Allocator, | |
| 4 | tree: Ast, | |
| 5 | ||
| 6 | nodes: std.MultiArrayList(Zoir.Node.Repr), | |
| 7 | extra: std.ArrayListUnmanaged(u32), | |
| 8 | limbs: std.ArrayListUnmanaged(std.math.big.Limb), | |
| 9 | string_bytes: std.ArrayListUnmanaged(u8), | |
| 10 | string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage), | |
| 11 | ||
| 12 | compile_errors: std.ArrayListUnmanaged(Zoir.CompileError), | |
| 13 | error_notes: std.ArrayListUnmanaged(Zoir.CompileError.Note), | |
| 14 | ||
| 15 | pub 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 | ||
| 86 | fn 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 | ||
| 418 | fn 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 | ||
| 430 | fn 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 | ||
| 458 | fn 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 | ||
| 480 | const StringLiteralResult = union(enum) { | |
| 481 | nts: Zoir.NullTerminatedString, | |
| 482 | slice: struct { start: u32, len: u32 }, | |
| 483 | }; | |
| 484 | ||
| 485 | fn 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 | ||
| 513 | fn 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 | ||
| 533 | fn 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 | ||
| 620 | fn 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 | ||
| 637 | fn 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 | ||
| 652 | fn 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 | ||
| 678 | fn setNode(zg: *ZonGen, dest: Zoir.Node.Index, repr: Zoir.Node.Repr) void { | |
| 679 | zg.nodes.set(@intFromEnum(dest), repr); | |
| 680 | } | |
| 681 | ||
| 682 | fn 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 | ||
| 686 | fn 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 | ||
| 718 | fn 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 | ||
| 731 | fn 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 | ||
| 744 | fn 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 | } | |
| 747 | fn addErrorTok(zg: *ZonGen, tok: Ast.TokenIndex, comptime format: []const u8, args: anytype) Allocator.Error!void { | |
| 748 | return zg.addErrorInner(tok, 0, format, args, &.{}); | |
| 749 | } | |
| 750 | fn 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 | } | |
| 753 | fn 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 | } | |
| 756 | fn 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 | } | |
| 759 | fn 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 | ||
| 763 | fn 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 | ||
| 790 | fn 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 | ||
| 827 | const std = @import("std"); | |
| 828 | const assert = std.debug.assert; | |
| 829 | const mem = std.mem; | |
| 830 | const Allocator = mem.Allocator; | |
| 831 | const StringIndexAdapter = std.hash_map.StringIndexAdapter; | |
| 832 | const StringIndexContext = std.hash_map.StringIndexContext; | |
| 833 | const ZonGen = @This(); | |
| 834 | const Zoir = @import("Zoir.zig"); | |
| 835 | const Ast = @import("Ast.zig"); |
lib/std/zig/string_literal.zig+35| ... | ... | @@ -38,6 +38,41 @@ pub const Error = union(enum) { |
| 38 | 38 | invalid_character: usize, |
| 39 | 39 | /// `''`. Not returned for string literals. |
| 40 | 40 | 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 | } | |
| 41 | 76 | }; |
| 42 | 77 | |
| 43 | 78 | /// Asserts the slice starts and ends with single-quotes. |
src/fmt.zig+78-30| ... | ... | @@ -13,6 +13,7 @@ const usage_fmt = |
| 13 | 13 | \\ if the list is non-empty |
| 14 | 14 | \\ --ast-check Run zig ast-check on every file |
| 15 | 15 | \\ --exclude [file] Exclude file or directory from formatting |
| 16 | \\ --zon Treat all input files as ZON, regardless of file extension | |
| 16 | 17 | \\ |
| 17 | 18 | \\ |
| 18 | 19 | ; |
| ... | ... | @@ -21,6 +22,7 @@ const Fmt = struct { |
| 21 | 22 | seen: SeenMap, |
| 22 | 23 | any_error: bool, |
| 23 | 24 | check_ast: bool, |
| 25 | force_zon: bool, | |
| 24 | 26 | color: Color, |
| 25 | 27 | gpa: Allocator, |
| 26 | 28 | arena: Allocator, |
| ... | ... | @@ -35,9 +37,10 @@ pub fn run( |
| 35 | 37 | args: []const []const u8, |
| 36 | 38 | ) !void { |
| 37 | 39 | 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; | |
| 41 | 44 | var input_files = std.ArrayList([]const u8).init(gpa); |
| 42 | 45 | defer input_files.deinit(); |
| 43 | 46 | var excluded_files = std.ArrayList([]const u8).init(gpa); |
| ... | ... | @@ -74,6 +77,8 @@ pub fn run( |
| 74 | 77 | i += 1; |
| 75 | 78 | const next_arg = args[i]; |
| 76 | 79 | try excluded_files.append(next_arg); |
| 80 | } else if (mem.eql(u8, arg, "--zon")) { | |
| 81 | force_zon = true; | |
| 77 | 82 | } else { |
| 78 | 83 | fatal("unrecognized parameter: '{s}'", .{arg}); |
| 79 | 84 | } |
| ... | ... | @@ -94,23 +99,40 @@ pub fn run( |
| 94 | 99 | }; |
| 95 | 100 | defer gpa.free(source_code); |
| 96 | 101 | |
| 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| { | |
| 98 | 103 | fatal("error parsing stdin: {}", .{err}); |
| 99 | 104 | }; |
| 100 | 105 | defer tree.deinit(gpa); |
| 101 | 106 | |
| 102 | 107 | 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 | } | |
| 114 | 136 | } |
| 115 | 137 | } else if (tree.errors.len != 0) { |
| 116 | 138 | try std.zig.printAstErrorsToStderr(gpa, tree, "<stdin>", color); |
| ... | ... | @@ -131,12 +153,13 @@ pub fn run( |
| 131 | 153 | fatal("expected at least one source file argument", .{}); |
| 132 | 154 | } |
| 133 | 155 | |
| 134 | var fmt = Fmt{ | |
| 156 | var fmt: Fmt = .{ | |
| 135 | 157 | .gpa = gpa, |
| 136 | 158 | .arena = arena, |
| 137 | .seen = Fmt.SeenMap.init(gpa), | |
| 159 | .seen = .init(gpa), | |
| 138 | 160 | .any_error = false, |
| 139 | 161 | .check_ast = check_ast_flag, |
| 162 | .force_zon = force_zon, | |
| 140 | 163 | .color = color, |
| 141 | 164 | .out_buffer = std.ArrayList(u8).init(gpa), |
| 142 | 165 | }; |
| ... | ... | @@ -276,7 +299,13 @@ fn fmtPathFile( |
| 276 | 299 | // Add to set after no longer possible to get error.IsDir. |
| 277 | 300 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; |
| 278 | 301 | |
| 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); | |
| 280 | 309 | defer tree.deinit(gpa); |
| 281 | 310 | |
| 282 | 311 | if (tree.errors.len != 0) { |
| ... | ... | @@ -289,18 +318,37 @@ fn fmtPathFile( |
| 289 | 318 | if (stat.size > std.zig.max_src_size) |
| 290 | 319 | return error.FileTooBig; |
| 291 | 320 | |
| 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 | }, | |
| 304 | 352 | } |
| 305 | 353 | } |
| 306 | 354 |
src/main.zig+123-71| ... | ... | @@ -19,6 +19,7 @@ const Directory = std.Build.Cache.Directory; |
| 19 | 19 | const EnvVar = std.zig.EnvVar; |
| 20 | 20 | const LibCInstallation = std.zig.LibCInstallation; |
| 21 | 21 | const AstGen = std.zig.AstGen; |
| 22 | const ZonGen = std.zig.ZonGen; | |
| 22 | 23 | const Server = std.zig.Server; |
| 23 | 24 | |
| 24 | 25 | const tracy = @import("tracy.zig"); |
| ... | ... | @@ -6007,15 +6008,16 @@ fn parseCodeModel(arg: []const u8) std.builtin.CodeModel { |
| 6007 | 6008 | const usage_ast_check = |
| 6008 | 6009 | \\Usage: zig ast-check [file] |
| 6009 | 6010 | \\ |
| 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. | |
| 6013 | 6014 | \\ |
| 6014 | 6015 | \\ If [file] is omitted, stdin is used. |
| 6015 | 6016 | \\ |
| 6016 | 6017 | \\Options: |
| 6017 | 6018 | \\ -h, --help Print this help and exit |
| 6018 | 6019 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 6020 | \\ --zon Treat the input file as ZON, regardless of file extension | |
| 6019 | 6021 | \\ -t (debug option) Output ZIR in text form to stdout |
| 6020 | 6022 | \\ |
| 6021 | 6023 | \\ |
| ... | ... | @@ -6032,6 +6034,7 @@ fn cmdAstCheck( |
| 6032 | 6034 | |
| 6033 | 6035 | var color: Color = .auto; |
| 6034 | 6036 | var want_output_text = false; |
| 6037 | var force_zon = false; | |
| 6035 | 6038 | var zig_source_file: ?[]const u8 = null; |
| 6036 | 6039 | |
| 6037 | 6040 | var i: usize = 0; |
| ... | ... | @@ -6043,6 +6046,8 @@ fn cmdAstCheck( |
| 6043 | 6046 | return cleanExit(); |
| 6044 | 6047 | } else if (mem.eql(u8, arg, "-t")) { |
| 6045 | 6048 | want_output_text = true; |
| 6049 | } else if (mem.eql(u8, arg, "--zon")) { | |
| 6050 | force_zon = true; | |
| 6046 | 6051 | } else if (mem.eql(u8, arg, "--color")) { |
| 6047 | 6052 | if (i + 1 >= args.len) { |
| 6048 | 6053 | fatal("expected [auto|on|off] after --color", .{}); |
| ... | ... | @@ -6110,89 +6115,136 @@ fn cmdAstCheck( |
| 6110 | 6115 | file.stat.size = source.len; |
| 6111 | 6116 | } |
| 6112 | 6117 | |
| 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 | ||
| 6113 | 6128 | file.mod = try Package.Module.createLimited(arena, .{ |
| 6114 | 6129 | .root = Path.cwd(), |
| 6115 | 6130 | .root_src_path = file.sub_file_path, |
| 6116 | 6131 | .fully_qualified_name = "root", |
| 6117 | 6132 | }); |
| 6118 | 6133 | |
| 6119 | file.tree = try Ast.parse(gpa, file.source, .zig); | |
| 6134 | file.tree = try Ast.parse(gpa, file.source, mode); | |
| 6120 | 6135 | file.tree_loaded = true; |
| 6121 | 6136 | defer file.tree.deinit(gpa); |
| 6122 | 6137 | |
| 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 | } | |
| 6126 | 6157 | |
| 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 | } | |
| 6135 | 6168 | |
| 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 | } | |
| 6140 | 6206 | |
| 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()); | |
| 6151 | 6208 | |
| 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); | |
| 6189 | 6218 | |
| 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(); | |
| 6191 | 6223 | |
| 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 | }, | |
| 6196 | 6248 | } |
| 6197 | 6249 | } |
| 6198 | 6250 |
src/print_zoir.zig created+122| ... | ... | @@ -0,0 +1,122 @@ |
| 1 | pub 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 | ||
| 7 | pub 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 | ||
| 50 | const 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 | ||
| 119 | const std = @import("std"); | |
| 120 | const assert = std.debug.assert; | |
| 121 | const Allocator = std.mem.Allocator; | |
| 122 | const Zoir = std.zig.Zoir; |