15 files changed, 960 insertions(+), 287 deletions(-)
CHANGELOG.md created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | ## 0.1.1-alpha | ||
| 2 | |||
| 3 | - introduce `Report` and show errors during Transform. | ||
| 4 | - add flag --dump-ir for enabling ir dumps | ||
| 5 | |||
| 6 | ## 0.1.0-alpha | ||
| 7 | |||
| 8 | initial demo of compiler pipeline in zig. | ||
demo.txt created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | no "waay | ||
src/compiler/Ast.zig+131-83| ... | @@ -34,96 +34,54 @@ | ... | @@ -34,96 +34,54 @@ |
| 34 | //! The `Ast` owns token and node data, but not the source code. | 34 | //! The `Ast` owns token and node data, but not the source code. |
| 35 | 35 | ||
| 36 | /// Externally owned source code. | 36 | /// Externally owned source code. |
| 37 | utf8_source: []const u8, | 37 | utf8_source: [:0]const u8, |
| 38 | 38 | ||
| 39 | tokens: Token.List.Slice, | 39 | tokens: Token.List.Slice, |
| 40 | nodes: Node.List.Slice, | 40 | nodes: Node.List.Slice, |
| 41 | extra: []const u32, | 41 | extra: []const u32, |
| 42 | 42 | ||
| 43 | pub fn parse(gpa: std.mem.Allocator, source: []const u8) !Ast { | 43 | const Parse = @import("Parse.zig"); |
| 44 | const Parse = @import("Parse.zig"); | 44 | pub fn parse( |
| 45 | 45 | gpa: std.mem.Allocator, | |
| 46 | var todo: ByteOffset = 0; | 46 | source: [:0]const u8, |
| 47 | var tokens = try Tokenizer.tokenize(source, gpa, &todo); | 47 | ast_error: *Parse.AstError, |
| 48 | ) Parse.Error!Ast { | ||
| 49 | var err_byte: ByteOffset = 0; | ||
| 50 | var tokens = Tokenizer.tokenize( | ||
| 51 | source, | ||
| 52 | gpa, | ||
| 53 | &err_byte, | ||
| 54 | ) catch |err| switch (err) { | ||
| 55 | error.OutOfMemory => return error.OutOfMemory, | ||
| 56 | else => { | ||
| 57 | ast_error.* = .{ .tokenization = .{ | ||
| 58 | .err = err, | ||
| 59 | .byte = err_byte, | ||
| 60 | } }; | ||
| 61 | return error.ParseFail; | ||
| 62 | }, | ||
| 63 | }; | ||
| 48 | errdefer tokens.deinit(gpa); | 64 | errdefer tokens.deinit(gpa); |
| 49 | 65 | ||
| 50 | var parser = Parse.init(gpa, source, tokens); | 66 | var parser = Parse.init(gpa, source, tokens); |
| 51 | errdefer parser.deinit(); | 67 | errdefer parser.deinit(); |
| 52 | 68 | ||
| 53 | try parser.nodes.ensureUnusedCapacity(gpa, @divFloor(tokens.len, 4)); | 69 | try parser.nodes.ensureUnusedCapacity( |
| 70 | gpa, | ||
| 71 | @max(1, @divFloor(tokens.len, 4)), | ||
| 72 | ); | ||
| 54 | 73 | ||
| 55 | try parser.parseRoot(); | 74 | parser.parseRoot() catch |err| switch (err) { |
| 75 | error.OutOfMemory => return error.OutOfMemory, | ||
| 76 | error.ParseFail => { | ||
| 77 | ast_error.* = parser.ast_error.?; | ||
| 78 | return error.ParseFail; | ||
| 79 | }, | ||
| 80 | }; | ||
| 56 | 81 | ||
| 57 | return try parser.finalize(tokens); | 82 | return try parser.finalize(tokens); |
| 58 | } | 83 | } |
| 59 | 84 | ||
| 60 | pub const Location = struct { | ||
| 61 | line: usize, | ||
| 62 | column: usize, | ||
| 63 | line_start: usize, | ||
| 64 | line_end: usize, | ||
| 65 | }; | ||
| 66 | |||
| 67 | pub const Span = struct { | ||
| 68 | start: u32, | ||
| 69 | end: u32, | ||
| 70 | main: u32, | ||
| 71 | }; | ||
| 72 | |||
| 73 | // This can be removed when the following issue is implemented: | ||
| 74 | // See: https://github.com/ziglang/zig/issues/104 | ||
| 75 | // See: https://github.com/ziglang/zig/issues/3806 | ||
| 76 | pub const OptionalNodeIndex = enum(u32) { | ||
| 77 | none = 0, | ||
| 78 | _, | ||
| 79 | |||
| 80 | pub fn init(idx: ?NodeIndex) OptionalNodeIndex { | ||
| 81 | assert(idx != 0); | ||
| 82 | return @enumFromInt(idx orelse return .none); | ||
| 83 | } | ||
| 84 | |||
| 85 | pub fn unwrap(idx: OptionalNodeIndex) ?NodeIndex { | ||
| 86 | return switch (idx) { | ||
| 87 | .none => null, | ||
| 88 | _ => @intFromEnum(idx), | ||
| 89 | }; | ||
| 90 | } | ||
| 91 | }; | ||
| 92 | |||
| 93 | pub const OptionalTokenIndex = enum(u32) { | ||
| 94 | none = 0, | ||
| 95 | _, | ||
| 96 | |||
| 97 | pub fn init(idx: ?TokenIndex) OptionalTokenIndex { | ||
| 98 | assert(idx != 0); | ||
| 99 | return @enumFromInt(idx orelse return .none); | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn unwrap(idx: OptionalTokenIndex) ?TokenIndex { | ||
| 103 | return switch (idx) { | ||
| 104 | .none => null, | ||
| 105 | _ => @intFromEnum(idx), | ||
| 106 | }; | ||
| 107 | } | ||
| 108 | }; | ||
| 109 | |||
| 110 | pub const OptionalExtraIndex = enum(u32) { | ||
| 111 | none = std.math.maxInt(u32), | ||
| 112 | _, | ||
| 113 | |||
| 114 | pub fn init(idx: ?ExtraIndex) OptionalExtraIndex { | ||
| 115 | assert(idx != std.math.maxInt(u32)); | ||
| 116 | return @enumFromInt(idx orelse return .none); | ||
| 117 | } | ||
| 118 | |||
| 119 | pub fn unwrap(idx: OptionalExtraIndex) ?ExtraIndex { | ||
| 120 | return switch (idx) { | ||
| 121 | .none => null, | ||
| 122 | _ => @intFromEnum(idx), | ||
| 123 | }; | ||
| 124 | } | ||
| 125 | }; | ||
| 126 | |||
| 127 | pub const Node = struct { | 85 | pub const Node = struct { |
| 128 | tag: Tag, | 86 | tag: Tag, |
| 129 | main_token: TokenIndex, | 87 | main_token: TokenIndex, |
| ... | @@ -462,6 +420,76 @@ pub const Node = struct { | ... | @@ -462,6 +420,76 @@ pub const Node = struct { |
| 462 | }; | 420 | }; |
| 463 | }; | 421 | }; |
| 464 | 422 | ||
| 423 | pub const Error = error{ OutOfMemory, ParseFail }; | ||
| 424 | |||
| 425 | pub const Location = struct { | ||
| 426 | // TODO: add an ordinal number type, replace with Ordinal(usize) | ||
| 427 | line: usize, | ||
| 428 | column: usize, | ||
| 429 | line_start: usize, | ||
| 430 | line_end: usize, | ||
| 431 | }; | ||
| 432 | |||
| 433 | pub const Span = struct { | ||
| 434 | start: u32, | ||
| 435 | end: u32, | ||
| 436 | main: u32, | ||
| 437 | }; | ||
| 438 | |||
| 439 | // This can be removed when the following issue is implemented: | ||
| 440 | // See: https://github.com/ziglang/zig/issues/104 | ||
| 441 | // See: https://github.com/ziglang/zig/issues/3806 | ||
| 442 | pub const OptionalNodeIndex = enum(u32) { | ||
| 443 | none = 0, | ||
| 444 | _, | ||
| 445 | |||
| 446 | pub fn init(idx: ?NodeIndex) OptionalNodeIndex { | ||
| 447 | assert(idx != 0); | ||
| 448 | return @enumFromInt(idx orelse return .none); | ||
| 449 | } | ||
| 450 | |||
| 451 | pub fn unwrap(idx: OptionalNodeIndex) ?NodeIndex { | ||
| 452 | return switch (idx) { | ||
| 453 | .none => null, | ||
| 454 | _ => @intFromEnum(idx), | ||
| 455 | }; | ||
| 456 | } | ||
| 457 | }; | ||
| 458 | |||
| 459 | pub const OptionalTokenIndex = enum(u32) { | ||
| 460 | none = 0, | ||
| 461 | _, | ||
| 462 | |||
| 463 | pub fn init(idx: ?TokenIndex) OptionalTokenIndex { | ||
| 464 | assert(idx != 0); | ||
| 465 | return @enumFromInt(idx orelse return .none); | ||
| 466 | } | ||
| 467 | |||
| 468 | pub fn unwrap(idx: OptionalTokenIndex) ?TokenIndex { | ||
| 469 | return switch (idx) { | ||
| 470 | .none => null, | ||
| 471 | _ => @intFromEnum(idx), | ||
| 472 | }; | ||
| 473 | } | ||
| 474 | }; | ||
| 475 | |||
| 476 | pub const OptionalExtraIndex = enum(u32) { | ||
| 477 | none = std.math.maxInt(u32), | ||
| 478 | _, | ||
| 479 | |||
| 480 | pub fn init(idx: ?ExtraIndex) OptionalExtraIndex { | ||
| 481 | assert(idx != std.math.maxInt(u32)); | ||
| 482 | return @enumFromInt(idx orelse return .none); | ||
| 483 | } | ||
| 484 | |||
| 485 | pub fn unwrap(idx: OptionalExtraIndex) ?ExtraIndex { | ||
| 486 | return switch (idx) { | ||
| 487 | .none => null, | ||
| 488 | _ => @intFromEnum(idx), | ||
| 489 | }; | ||
| 490 | } | ||
| 491 | }; | ||
| 492 | |||
| 465 | pub const SubList = struct { | 493 | pub const SubList = struct { |
| 466 | /// Either NodeIndex or TokenIndex, depending on where this came from. | 494 | /// Either NodeIndex or TokenIndex, depending on where this came from. |
| 467 | start: u32, | 495 | start: u32, |
| ... | @@ -598,6 +626,19 @@ pub fn fullParamDecl(ast: Ast, node: NodeIndex) full.ParamDecl { | ... | @@ -598,6 +626,19 @@ pub fn fullParamDecl(ast: Ast, node: NodeIndex) full.ParamDecl { |
| 598 | }; | 626 | }; |
| 599 | } | 627 | } |
| 600 | 628 | ||
| 629 | pub fn idToken(ast: Ast, node: NodeIndex) TokenIndex { | ||
| 630 | return switch (ast.nodeTag(node)) { | ||
| 631 | .param_decl, | ||
| 632 | .id, | ||
| 633 | .label, | ||
| 634 | => ast.nodeToken(node), | ||
| 635 | .fn_decl, | ||
| 636 | .var_decl, | ||
| 637 | => ast.nodeToken(node) + 1, | ||
| 638 | else => unreachable, // does not have an id | ||
| 639 | }; | ||
| 640 | } | ||
| 641 | |||
| 601 | pub fn nodeStatements(ast: *const Ast, node: NodeIndex) []const NodeIndex { | 642 | pub fn nodeStatements(ast: *const Ast, node: NodeIndex) []const NodeIndex { |
| 602 | const data = ast.nodes.items(.data)[node].list; | 643 | const data = ast.nodes.items(.data)[node].list; |
| 603 | return data.slice(ast); | 644 | return data.slice(ast); |
| ... | @@ -605,15 +646,15 @@ pub fn nodeStatements(ast: *const Ast, node: NodeIndex) []const NodeIndex { | ... | @@ -605,15 +646,15 @@ pub fn nodeStatements(ast: *const Ast, node: NodeIndex) []const NodeIndex { |
| 605 | 646 | ||
| 606 | pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenIndex) Location { | 647 | pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenIndex) Location { |
| 607 | var loc = Location{ | 648 | var loc = Location{ |
| 608 | .line = 0, | 649 | .line = 1, |
| 609 | .column = 0, | 650 | .column = 1, |
| 610 | .line_start = start_offset, | 651 | .line_start = start_offset, |
| 611 | .line_end = self.source.len, | 652 | .line_end = self.utf8_source.len, |
| 612 | }; | 653 | }; |
| 613 | const token_start = self.tokens.items(.start)[token_index]; | 654 | const token_start = self.tokens.items(.offset)[token_index]; |
| 614 | 655 | ||
| 615 | // Scan to by line until we go past the token start | 656 | // Scan to by line until we go past the token start |
| 616 | while (std.mem.indexOfScalarPos(u8, self.source, loc.line_start, '\n')) |i| { | 657 | while (std.mem.indexOfScalarPos(u8, self.utf8_source, loc.line_start, '\n')) |i| { |
| 617 | if (i >= token_start) { | 658 | if (i >= token_start) { |
| 618 | break; // Went past | 659 | break; // Went past |
| 619 | } | 660 | } |
| ... | @@ -622,17 +663,17 @@ pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenInde | ... | @@ -622,17 +663,17 @@ pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenInde |
| 622 | } | 663 | } |
| 623 | 664 | ||
| 624 | const offset = loc.line_start; | 665 | const offset = loc.line_start; |
| 625 | for (self.source[offset..], 0..) |c, i| { | 666 | for (self.utf8_source[offset..], 0..) |c, i| { |
| 626 | if (i + offset == token_start) { | 667 | if (i + offset == token_start) { |
| 627 | loc.line_end = i + offset; | 668 | loc.line_end = i + offset; |
| 628 | while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') { | 669 | while (loc.line_end < self.utf8_source.len and self.utf8_source[loc.line_end] != '\n') { |
| 629 | loc.line_end += 1; | 670 | loc.line_end += 1; |
| 630 | } | 671 | } |
| 631 | return loc; | 672 | return loc; |
| 632 | } | 673 | } |
| 633 | if (c == '\n') { | 674 | if (c == '\n') { |
| 634 | loc.line += 1; | 675 | loc.line += 1; |
| 635 | loc.column = 0; | 676 | loc.column = 1; |
| 636 | loc.line_start = i + 1; | 677 | loc.line_start = i + 1; |
| 637 | } else { | 678 | } else { |
| 638 | loc.column += 1; | 679 | loc.column += 1; |
| ... | @@ -651,6 +692,11 @@ pub fn basicNodeSlice(ast: Ast, node: NodeIndex) []const u8 { | ... | @@ -651,6 +692,11 @@ pub fn basicNodeSlice(ast: Ast, node: NodeIndex) []const u8 { |
| 651 | return ast.tokenSlice(token); | 692 | return ast.tokenSlice(token); |
| 652 | } | 693 | } |
| 653 | 694 | ||
| 695 | pub fn fnDeclIdToken(tree: Ast, node: NodeIndex) TokenIndex { | ||
| 696 | assert(tree.nodeTag(node) == .fn_decl); | ||
| 697 | return tree.nodeToken(node) + 1; | ||
| 698 | } | ||
| 699 | |||
| 654 | pub fn extraData(tree: Ast, index: usize, comptime T: type) T { | 700 | pub fn extraData(tree: Ast, index: usize, comptime T: type) T { |
| 655 | const fields = std.meta.fields(T); | 701 | const fields = std.meta.fields(T); |
| 656 | var result: T = undefined; | 702 | var result: T = undefined; |
| ... | @@ -699,3 +745,5 @@ const Tokenizer = @import("Tokenizer.zig"); | ... | @@ -699,3 +745,5 @@ const Tokenizer = @import("Tokenizer.zig"); |
| 699 | const Token = Tokenizer.Token; | 745 | const Token = Tokenizer.Token; |
| 700 | const TokenList = Tokenizer.Token.List; | 746 | const TokenList = Tokenizer.Token.List; |
| 701 | const ByteOffset = Tokenizer.ByteOffset; | 747 | const ByteOffset = Tokenizer.ByteOffset; |
| 748 | |||
| 749 | const Report = @import("Report.zig"); |
src/compiler/Comp.zig+60-11| ... | @@ -10,6 +10,8 @@ files: std.StringArrayHashMapUnmanaged(*File) = .{}, | ... | @@ -10,6 +10,8 @@ files: std.StringArrayHashMapUnmanaged(*File) = .{}, |
| 10 | /// # of sema instructions in this compilation cycle | 10 | /// # of sema instructions in this compilation cycle |
| 11 | analyzed_instructions: usize = 0, | 11 | analyzed_instructions: usize = 0, |
| 12 | 12 | ||
| 13 | options: Options = .{}, | ||
| 14 | |||
| 13 | // TODO: consider computing this dynamicly per-link | 15 | // TODO: consider computing this dynamicly per-link |
| 14 | current_compile_decls: std.ArrayListUnmanaged(Decl.Index) = .{}, | 16 | current_compile_decls: std.ArrayListUnmanaged(Decl.Index) = .{}, |
| 15 | 17 | ||
| ... | @@ -74,7 +76,16 @@ pub const File = struct { | ... | @@ -74,7 +76,16 @@ pub const File = struct { |
| 74 | 76 | ||
| 75 | path_digest: ?Cache.BinDigest, | 77 | path_digest: ?Cache.BinDigest, |
| 76 | 78 | ||
| 77 | pub fn getSource(file: *File, gpa: Allocator) !Source { | 79 | const GetSourceError = |
| 80 | std.fs.File.OpenError || | ||
| 81 | std.fs.File.ReadError || | ||
| 82 | std.fs.File.StatError || | ||
| 83 | Allocator.Error || error{ | ||
| 84 | FileTooBig, | ||
| 85 | UnexpectedEndOfFile, | ||
| 86 | }; | ||
| 87 | |||
| 88 | pub fn getSource(file: *File, gpa: Allocator) GetSourceError!Source { | ||
| 78 | if (file.source) |source| { | 89 | if (file.source) |source| { |
| 79 | return source; | 90 | return source; |
| 80 | } | 91 | } |
| ... | @@ -111,17 +122,42 @@ pub const File = struct { | ... | @@ -111,17 +122,42 @@ pub const File = struct { |
| 111 | return loaded_source; | 122 | return loaded_source; |
| 112 | } | 123 | } |
| 113 | 124 | ||
| 114 | pub fn getAst(file: *File, gpa: Allocator) !Ast { | 125 | const GetAstError = GetSourceError || Parse.Error; |
| 126 | |||
| 127 | pub fn getAst(file: *File, comp: *Comp) GetAstError!Ast { | ||
| 115 | if (file.ast) |ast| { | 128 | if (file.ast) |ast| { |
| 116 | return ast; | 129 | return ast; |
| 117 | } | 130 | } |
| 118 | 131 | ||
| 119 | const ast = try Ast.parse(gpa, (try file.getSource(gpa)).code); | 132 | var ast_err: Parse.AstError = undefined; |
| 133 | const ast = Ast.parse( | ||
| 134 | comp.gpa, | ||
| 135 | (try file.getSource(comp.gpa)).code, | ||
| 136 | &ast_err, | ||
| 137 | ) catch |err| switch (err) { | ||
| 138 | error.OutOfMemory => return err, | ||
| 139 | error.ParseFail => { | ||
| 140 | var report = try ast_err.toReport(comp, file); | ||
| 141 | // TODO: let this merge with other report | ||
| 142 | report.dumpToStdErr(comp) catch {}; | ||
| 143 | report.deinit(comp.gpa); | ||
| 144 | return err; | ||
| 145 | }, | ||
| 146 | }; | ||
| 147 | |||
| 148 | if (comp.options.dump_ir) { | ||
| 149 | ast.dumpToWriter(std.io.getStdErr().writer()) catch { | ||
| 150 | // Simplify the error set. | ||
| 151 | }; | ||
| 152 | } | ||
| 153 | |||
| 120 | file.ast = ast; | 154 | file.ast = ast; |
| 121 | return ast; | 155 | return ast; |
| 122 | } | 156 | } |
| 123 | 157 | ||
| 124 | pub fn getTeg(file: *File, gpa: Allocator) !Teg { | 158 | const GetTegError = GetAstError || Transform.Error; |
| 159 | |||
| 160 | pub fn getTeg(file: *File, comp: *Comp, fail_report: *Report) GetTegError!Teg { | ||
| 125 | if (file.teg) |teg| { | 161 | if (file.teg) |teg| { |
| 126 | return teg; | 162 | return teg; |
| 127 | } | 163 | } |
| ... | @@ -136,7 +172,13 @@ pub const File = struct { | ... | @@ -136,7 +172,13 @@ pub const File = struct { |
| 136 | }; | 172 | }; |
| 137 | file.path_digest = bin_digest; | 173 | file.path_digest = bin_digest; |
| 138 | 174 | ||
| 139 | const teg = try Transform.transform(try file.getAst(gpa), gpa); | 175 | const teg = try Transform.transform( |
| 176 | try file.getAst(comp), | ||
| 177 | comp.gpa, | ||
| 178 | file, | ||
| 179 | fail_report, | ||
| 180 | ); | ||
| 181 | |||
| 140 | file.teg = teg; | 182 | file.teg = teg; |
| 141 | return teg; | 183 | return teg; |
| 142 | } | 184 | } |
| ... | @@ -290,8 +332,6 @@ pub const Namespace = struct { | ... | @@ -290,8 +332,6 @@ pub const Namespace = struct { |
| 290 | pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !Namespace.Index { | 332 | pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !Namespace.Index { |
| 291 | const teg = file.teg.?; | 333 | const teg = file.teg.?; |
| 292 | 334 | ||
| 293 | log.info("Index {} @ {d}", .{ std.fmt.fmtSliceHexLower(file.path_digest.?[0..8]), @intFromEnum(module_index) }); | ||
| 294 | |||
| 295 | const ns = try comp.intern_pool.createNamespace(comp.gpa, .{ | 335 | const ns = try comp.intern_pool.createNamespace(comp.gpa, .{ |
| 296 | .parent = .none, | 336 | .parent = .none, |
| 297 | .file_scope = file, | 337 | .file_scope = file, |
| ... | @@ -322,7 +362,13 @@ pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !N | ... | @@ -322,7 +362,13 @@ pub fn indexTegModule(comp: *Comp, file: *File, module_index: Teg.Inst.Index) !N |
| 322 | } | 362 | } |
| 323 | 363 | ||
| 324 | pub fn semaEntryPoint(comp: *Comp, file: *File) !void { | 364 | pub fn semaEntryPoint(comp: *Comp, file: *File) !void { |
| 325 | const teg = try file.getTeg(comp.gpa); | 365 | var report: Report = undefined; |
| 366 | const teg = try file.getTeg(comp, &report); | ||
| 367 | |||
| 368 | // TODO, move this elsewhere. we should print all errors and warnings at once | ||
| 369 | if (teg.report.warnings.len > 0) { | ||
| 370 | try teg.report.dumpToStdErr(comp); | ||
| 371 | } | ||
| 326 | 372 | ||
| 327 | const ns = try comp.indexTegModule(file, .main_module_inst); | 373 | const ns = try comp.indexTegModule(file, .main_module_inst); |
| 328 | const decl_index = comp.intern_pool.namespacePtr(ns).lookupDirect(comp, .empty) orelse | 374 | const decl_index = comp.intern_pool.namespacePtr(ns).lookupDirect(comp, .empty) orelse |
| ... | @@ -354,9 +400,6 @@ pub fn codeGenDecl(c: *Comp, decl: *Decl, air: *const Air) !void { | ... | @@ -354,9 +400,6 @@ pub fn codeGenDecl(c: *Comp, decl: *Decl, air: *const Air) !void { |
| 354 | const t = tracer.trace(@src()); | 400 | const t = tracer.trace(@src()); |
| 355 | defer t.end(); | 401 | defer t.end(); |
| 356 | 402 | ||
| 357 | std.debug.print("Start Codegen\n", .{}); | ||
| 358 | defer std.debug.print("End Codegen\n", .{}); | ||
| 359 | |||
| 360 | var arena_state = std.heap.ArenaAllocator.init(c.gpa); | 403 | var arena_state = std.heap.ArenaAllocator.init(c.gpa); |
| 361 | defer arena_state.deinit(); | 404 | defer arena_state.deinit(); |
| 362 | const arena = arena_state.allocator(); | 405 | const arena = arena_state.allocator(); |
| ... | @@ -371,6 +414,10 @@ pub fn link(c: *Comp, writer: anytype) !void { | ... | @@ -371,6 +414,10 @@ pub fn link(c: *Comp, writer: anytype) !void { |
| 371 | try JsLink.linkJsCode(c, c.current_compile_decls.items, writer); | 414 | try JsLink.linkJsCode(c, c.current_compile_decls.items, writer); |
| 372 | } | 415 | } |
| 373 | 416 | ||
| 417 | const Options = struct { | ||
| 418 | dump_ir: bool = false, | ||
| 419 | }; | ||
| 420 | |||
| 374 | const Comp = @This(); | 421 | const Comp = @This(); |
| 375 | 422 | ||
| 376 | const std = @import("std"); | 423 | const std = @import("std"); |
| ... | @@ -391,5 +438,7 @@ const Transform = @import("Transform.zig"); | ... | @@ -391,5 +438,7 @@ const Transform = @import("Transform.zig"); |
| 391 | const JsNodeGen = @import("JsNodeGen.zig"); | 438 | const JsNodeGen = @import("JsNodeGen.zig"); |
| 392 | const JsTree = @import("JsTree.zig"); | 439 | const JsTree = @import("JsTree.zig"); |
| 393 | const JsLink = @import("JsLink.zig"); | 440 | const JsLink = @import("JsLink.zig"); |
| 441 | const Report = @import("Report.zig"); | ||
| 442 | const Parse = @import("Parse.zig"); | ||
| 394 | 443 | ||
| 395 | const tracer = @import("tracer"); | 444 | const tracer = @import("tracer"); |
src/compiler/JsLink.zig+1-4| ... | @@ -19,7 +19,6 @@ pub fn linkJsCode( | ... | @@ -19,7 +19,6 @@ pub fn linkJsCode( |
| 19 | decls: []const Decl.Index, | 19 | decls: []const Decl.Index, |
| 20 | writer: anytype, | 20 | writer: anytype, |
| 21 | ) !void { | 21 | ) !void { |
| 22 | log.info("Linking {d} decls", .{decls.len}); | ||
| 23 | 22 | ||
| 24 | // Arena allocator is used for the main flow | 23 | // Arena allocator is used for the main flow |
| 25 | var link: JsLink = .{ .comp = comp }; | 24 | var link: JsLink = .{ .comp = comp }; |
| ... | @@ -68,7 +67,7 @@ pub fn linkJsCode( | ... | @@ -68,7 +67,7 @@ pub fn linkJsCode( |
| 68 | try link.decls.putNoClobber(comp.gpa, decl_index, @enumFromInt(gop.index)); | 67 | try link.decls.putNoClobber(comp.gpa, decl_index, @enumFromInt(gop.index)); |
| 69 | } | 68 | } |
| 70 | 69 | ||
| 71 | try writer.print("// Compiler Demo. Do not use in production\n", .{}); | 70 | try writer.print("// Language Compiler v0.0.1-alpha2\n", .{}); |
| 72 | 71 | ||
| 73 | // 2. Print every function decl | 72 | // 2. Print every function decl |
| 74 | for (decls[1..]) |decl_index| { | 73 | for (decls[1..]) |decl_index| { |
| ... | @@ -106,7 +105,6 @@ fn reserveExternSymbol(link: *JsLink, extern_symbol: String.WithLen) !void { | ... | @@ -106,7 +105,6 @@ fn reserveExternSymbol(link: *JsLink, extern_symbol: String.WithLen) !void { |
| 106 | 105 | ||
| 107 | fn appendLinkString(link: *JsLink, string: []const u8) !LinkString { | 106 | fn appendLinkString(link: *JsLink, string: []const u8) !LinkString { |
| 108 | const start = link.string_bytes.items.len; | 107 | const start = link.string_bytes.items.len; |
| 109 | log.info("Append '{s}' -> .{{ .start = {d}, .len = {d} }}", .{ string, start, string.len }); | ||
| 110 | try link.string_bytes.appendSlice(link.comp.gpa, string); | 108 | try link.string_bytes.appendSlice(link.comp.gpa, string); |
| 111 | return .{ .start = @intCast(start), .len = @intCast(string.len) }; | 109 | return .{ .start = @intCast(start), .len = @intCast(string.len) }; |
| 112 | } | 110 | } |
| ... | @@ -170,7 +168,6 @@ fn printBlock(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList, writer | ... | @@ -170,7 +168,6 @@ fn printBlock(link: *JsLink, tree: *const JsTree, list: JsTree.IndexList, writer |
| 170 | } | 168 | } |
| 171 | 169 | ||
| 172 | fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anytype) @TypeOf(writer).Error!void { | 170 | fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anytype) @TypeOf(writer).Error!void { |
| 173 | log.info("print node {d}", .{@intFromEnum(node)}); | ||
| 174 | switch (tree.nodeTag(node)) { | 171 | switch (tree.nodeTag(node)) { |
| 175 | else => |tag| std.debug.panic("TODO: printExpr .{s}", .{@tagName(tag)}), | 172 | else => |tag| std.debug.panic("TODO: printExpr .{s}", .{@tagName(tag)}), |
| 176 | .root => unreachable, | 173 | .root => unreachable, |
src/compiler/JsNodeGen.zig-2| ... | @@ -117,7 +117,6 @@ pub fn build(c: *JsNodeGen) !void { | ... | @@ -117,7 +117,6 @@ pub fn build(c: *JsNodeGen) !void { |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void { | 119 | fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void { |
| 120 | std.debug.print("JsNodeGen: #{d}\n", .{@intFromEnum(inst)}); | ||
| 121 | switch (c.air.instTag(inst)) { | 120 | switch (c.air.instTag(inst)) { |
| 122 | .arg => unreachable, // codegen must skip over these instructions. | 121 | .arg => unreachable, // codegen must skip over these instructions. |
| 123 | 122 | ||
| ... | @@ -400,7 +399,6 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br | ... | @@ -400,7 +399,6 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br |
| 400 | this_label, | 399 | this_label, |
| 401 | }); | 400 | }); |
| 402 | 401 | ||
| 403 | std.debug.print("requires_label {}\n", .{requires_label}); | ||
| 404 | const block: Air.IndexList = c.air.instData(.block, i); | 402 | const block: Air.IndexList = c.air.instData(.block, i); |
| 405 | for (block.slice(c.air)) |item| { | 403 | for (block.slice(c.air)) |item| { |
| 406 | try c.codeGenInst(item); | 404 | try c.codeGenInst(item); |
src/compiler/Parse.zig+62-27| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | //! Parser takes a Token list and creates a Node list. It does not own the source code or | 1 | //! Parser takes a Token list and creates a Node list. It does not own the source code or |
| 2 | //! token data, however transferring into an `Ast` does move token list ownership. | 2 | //! token data, however transferring into an `Ast` does move token list ownership. |
| 3 | gpa: Allocator, | 3 | gpa: Allocator, |
| 4 | source: []const u8, | 4 | source: [:0]const u8, |
| 5 | token_tags: []const Token.Tag, | 5 | token_tags: []const Token.Tag, |
| 6 | token_starts: []const ByteOffset, | 6 | token_starts: []const ByteOffset, |
| 7 | 7 | ||
| ... | @@ -12,7 +12,7 @@ scratch: ArrayListUnmanaged(NodeIndex) = .{}, | ... | @@ -12,7 +12,7 @@ scratch: ArrayListUnmanaged(NodeIndex) = .{}, |
| 12 | 12 | ||
| 13 | ast_error: ?AstError = null, | 13 | ast_error: ?AstError = null, |
| 14 | 14 | ||
| 15 | pub fn init(gpa: Allocator, source: []const u8, tokens: Token.List.Slice) Parse { | 15 | pub fn init(gpa: Allocator, source: [:0]const u8, tokens: Token.List.Slice) Parse { |
| 16 | return Parse{ | 16 | return Parse{ |
| 17 | .gpa = gpa, | 17 | .gpa = gpa, |
| 18 | .source = source, | 18 | .source = source, |
| ... | @@ -36,8 +36,6 @@ pub fn finalize(p: *Parse, token_slice: Token.List.Slice) !Ast { | ... | @@ -36,8 +36,6 @@ pub fn finalize(p: *Parse, token_slice: Token.List.Slice) !Ast { |
| 36 | .extra = extra, | 36 | .extra = extra, |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | ast.dumpToWriter(std.io.getStdErr().writer()) catch {}; | ||
| 40 | |||
| 41 | return ast; | 39 | return ast; |
| 42 | } | 40 | } |
| 43 | 41 | ||
| ... | @@ -50,18 +48,52 @@ pub fn deinit(p: *Parse) void { | ... | @@ -50,18 +48,52 @@ pub fn deinit(p: *Parse) void { |
| 50 | } | 48 | } |
| 51 | 49 | ||
| 52 | pub const Error = error{ | 50 | pub const Error = error{ |
| 53 | SyntaxError, | 51 | OutOfMemory, |
| 52 | ParseFail, | ||
| 54 | }; | 53 | }; |
| 55 | 54 | ||
| 56 | /// When error.SyntaxError is returned, `p.err` is set to this as well as `p.pos` | 55 | /// When error.SyntaxError is returned, `p.err` is set to this as well as `p.pos` |
| 57 | pub const AstError = union(enum) { | 56 | pub const AstError = union(enum) { |
| 57 | tokenization: struct { | ||
| 58 | err: Tokenizer.TokenizeError, | ||
| 59 | byte: u32, | ||
| 60 | }, | ||
| 58 | expected_token: Token.Tag, | 61 | expected_token: Token.Tag, |
| 59 | expected_statement, | 62 | expected_statement, |
| 60 | expected_expr, | 63 | expected_expr, |
| 61 | expected_else_value, | 64 | expected_else_value, |
| 65 | |||
| 66 | pub fn toReport(parse_err: AstError, comp: *Comp, file: *Comp.File) !Report { | ||
| 67 | return (Report.Err{ .ast = parse_err }).toReport(comp, file); | ||
| 68 | } | ||
| 69 | |||
| 70 | pub fn build(err: AstError, ctx: *Report.MsgContext) !void { | ||
| 71 | switch (err) { | ||
| 72 | .tokenization => |data| { | ||
| 73 | // TODO: consider making tokenizer emit AstError more directly, | ||
| 74 | // so that it can just annotate start and ends | ||
| 75 | try ctx.msg(switch (data.err) { | ||
| 76 | error.UnterminatedString => .{ .codepoint_eol = data.byte }, | ||
| 77 | else => .{ .codepoint = data.byte }, | ||
| 78 | }, "{s}", .{switch (data.err) { | ||
| 79 | error.OutOfMemory => unreachable, | ||
| 80 | error.InvalidSyntax => "unexpected token", | ||
| 81 | error.TrailingNumericSeparator => "trailing numberic separator", | ||
| 82 | error.UnterminatedString => "unterminated string literal", | ||
| 83 | error.InvalidEscapeSequence => "invalid escape sequence", | ||
| 84 | error.InvalidEscapeCodepoint => "invalid escape codepoint", | ||
| 85 | error.InvalidUtf8 => "file is not valid utf8", | ||
| 86 | error.GitConflict => "git merge conflict encountered", | ||
| 87 | }}); | ||
| 88 | }, | ||
| 89 | |||
| 90 | else => unreachable, | ||
| 91 | // | ||
| 92 | } | ||
| 93 | } | ||
| 62 | }; | 94 | }; |
| 63 | 95 | ||
| 64 | pub fn parseRoot(p: *Parse) !void { | 96 | pub fn parseRoot(p: *Parse) Error!void { |
| 65 | const trace = t.trace(@src()); | 97 | const trace = t.trace(@src()); |
| 66 | defer trace.end(); | 98 | defer trace.end(); |
| 67 | 99 | ||
| ... | @@ -82,7 +114,7 @@ pub fn parseRoot(p: *Parse) !void { | ... | @@ -82,7 +114,7 @@ pub fn parseRoot(p: *Parse) !void { |
| 82 | }; | 114 | }; |
| 83 | } | 115 | } |
| 84 | 116 | ||
| 85 | fn parseModuleMembers(p: *Parse) !SubList { | 117 | fn parseModuleMembers(p: *Parse) Error!SubList { |
| 86 | const trace = t.trace(@src()); | 118 | const trace = t.trace(@src()); |
| 87 | defer trace.end(); | 119 | defer trace.end(); |
| 88 | 120 | ||
| ... | @@ -103,7 +135,7 @@ fn parseModuleMembers(p: *Parse) !SubList { | ... | @@ -103,7 +135,7 @@ fn parseModuleMembers(p: *Parse) !SubList { |
| 103 | .kw_enum, | 135 | .kw_enum, |
| 104 | .kw_trait, | 136 | .kw_trait, |
| 105 | .kw_impl, | 137 | .kw_impl, |
| 106 | => return error.Todo, | 138 | => |tag| std.debug.panic("TODO: parse {s}", .{@tagName(tag)}), |
| 107 | 139 | ||
| 108 | .eof => break, | 140 | .eof => break, |
| 109 | 141 | ||
| ... | @@ -120,7 +152,7 @@ fn parseModuleMembers(p: *Parse) !SubList { | ... | @@ -120,7 +152,7 @@ fn parseModuleMembers(p: *Parse) !SubList { |
| 120 | return p.scratchToSubList(scratch_top); | 152 | return p.scratchToSubList(scratch_top); |
| 121 | } | 153 | } |
| 122 | 154 | ||
| 123 | fn parseModuleDecl(p: *Parse) !NodeIndex { | 155 | fn parseModuleDecl(p: *Parse) Error!NodeIndex { |
| 124 | const trace = t.trace(@src()); | 156 | const trace = t.trace(@src()); |
| 125 | defer trace.end(); | 157 | defer trace.end(); |
| 126 | 158 | ||
| ... | @@ -135,7 +167,7 @@ fn parseModuleDecl(p: *Parse) !NodeIndex { | ... | @@ -135,7 +167,7 @@ fn parseModuleDecl(p: *Parse) !NodeIndex { |
| 135 | }; | 167 | }; |
| 136 | } | 168 | } |
| 137 | 169 | ||
| 138 | fn parseFnDecl(p: *Parse) !NodeIndex { | 170 | fn parseFnDecl(p: *Parse) Error!NodeIndex { |
| 139 | const trace = t.trace(@src()); | 171 | const trace = t.trace(@src()); |
| 140 | defer trace.end(); | 172 | defer trace.end(); |
| 141 | 173 | ||
| ... | @@ -170,7 +202,7 @@ fn parseFnDecl(p: *Parse) !NodeIndex { | ... | @@ -170,7 +202,7 @@ fn parseFnDecl(p: *Parse) !NodeIndex { |
| 170 | }); | 202 | }); |
| 171 | } | 203 | } |
| 172 | 204 | ||
| 173 | fn parseParamList(p: *Parse) !SubList { | 205 | fn parseParamList(p: *Parse) Error!SubList { |
| 174 | const trace = t.trace(@src()); | 206 | const trace = t.trace(@src()); |
| 175 | defer trace.end(); | 207 | defer trace.end(); |
| 176 | 208 | ||
| ... | @@ -199,7 +231,7 @@ fn parseParamList(p: *Parse) !SubList { | ... | @@ -199,7 +231,7 @@ fn parseParamList(p: *Parse) !SubList { |
| 199 | return p.scratchToSubList(scratch_top); | 231 | return p.scratchToSubList(scratch_top); |
| 200 | } | 232 | } |
| 201 | 233 | ||
| 202 | fn parseParamDecl(p: *Parse) !NodeIndex { | 234 | fn parseParamDecl(p: *Parse) Error!NodeIndex { |
| 203 | const trace = t.trace(@src()); | 235 | const trace = t.trace(@src()); |
| 204 | defer trace.end(); | 236 | defer trace.end(); |
| 205 | 237 | ||
| ... | @@ -218,7 +250,7 @@ fn parseParamDecl(p: *Parse) !NodeIndex { | ... | @@ -218,7 +250,7 @@ fn parseParamDecl(p: *Parse) !NodeIndex { |
| 218 | }); | 250 | }); |
| 219 | } | 251 | } |
| 220 | 252 | ||
| 221 | fn parseBlock(p: *Parse) !NodeIndex { | 253 | fn parseBlock(p: *Parse) Error!NodeIndex { |
| 222 | const trace = t.trace(@src()); | 254 | const trace = t.trace(@src()); |
| 223 | defer trace.end(); | 255 | defer trace.end(); |
| 224 | 256 | ||
| ... | @@ -236,7 +268,7 @@ fn parseBlock(p: *Parse) !NodeIndex { | ... | @@ -236,7 +268,7 @@ fn parseBlock(p: *Parse) !NodeIndex { |
| 236 | }); | 268 | }); |
| 237 | } | 269 | } |
| 238 | 270 | ||
| 239 | fn parseLabelExpression(p: *Parse) !NodeIndex { | 271 | fn parseLabelExpression(p: *Parse) Error!NodeIndex { |
| 240 | const trace = t.trace(@src()); | 272 | const trace = t.trace(@src()); |
| 241 | defer trace.end(); | 273 | defer trace.end(); |
| 242 | 274 | ||
| ... | @@ -252,7 +284,7 @@ fn parseLabelExpression(p: *Parse) !NodeIndex { | ... | @@ -252,7 +284,7 @@ fn parseLabelExpression(p: *Parse) !NodeIndex { |
| 252 | }); | 284 | }); |
| 253 | } | 285 | } |
| 254 | 286 | ||
| 255 | fn parseBlockContents(p: *Parse) !struct { Node.Tag, SubList } { | 287 | fn parseBlockContents(p: *Parse) Error!struct { Node.Tag, SubList } { |
| 256 | const trace = t.trace(@src()); | 288 | const trace = t.trace(@src()); |
| 257 | defer trace.end(); | 289 | defer trace.end(); |
| 258 | 290 | ||
| ... | @@ -308,7 +340,7 @@ fn parseBlockContents(p: *Parse) !struct { Node.Tag, SubList } { | ... | @@ -308,7 +340,7 @@ fn parseBlockContents(p: *Parse) !struct { Node.Tag, SubList } { |
| 308 | } | 340 | } |
| 309 | 341 | ||
| 310 | /// VarDecl <- (kw_pub)? (kw_var | kw_let) id (colon expr)? equal expr semi | 342 | /// VarDecl <- (kw_pub)? (kw_var | kw_let) id (colon expr)? equal expr semi |
| 311 | fn parseVarDecl(p: *Parse) !NodeIndex { | 343 | fn parseVarDecl(p: *Parse) Error!NodeIndex { |
| 312 | const trace = t.trace(@src()); | 344 | const trace = t.trace(@src()); |
| 313 | defer trace.end(); | 345 | defer trace.end(); |
| 314 | 346 | ||
| ... | @@ -521,7 +553,7 @@ const Operator = enum(u8) { | ... | @@ -521,7 +553,7 @@ const Operator = enum(u8) { |
| 521 | } | 553 | } |
| 522 | }; | 554 | }; |
| 523 | 555 | ||
| 524 | fn parseExpression(p: *Parse) anyerror!NodeIndex { | 556 | fn parseExpression(p: *Parse) Error!NodeIndex { |
| 525 | const trace = t.trace(@src()); | 557 | const trace = t.trace(@src()); |
| 526 | defer trace.end(); | 558 | defer trace.end(); |
| 527 | 559 | ||
| ... | @@ -543,7 +575,7 @@ fn parseExpression(p: *Parse) anyerror!NodeIndex { | ... | @@ -543,7 +575,7 @@ fn parseExpression(p: *Parse) anyerror!NodeIndex { |
| 543 | return expr; | 575 | return expr; |
| 544 | } | 576 | } |
| 545 | 577 | ||
| 546 | fn parseExpressionPart(p: *Parse) !NodeIndex { | 578 | fn parseExpressionPart(p: *Parse) Error!NodeIndex { |
| 547 | const trace = t.trace(@src()); | 579 | const trace = t.trace(@src()); |
| 548 | defer trace.end(); | 580 | defer trace.end(); |
| 549 | 581 | ||
| ... | @@ -644,7 +676,7 @@ fn parseExpressionPart(p: *Parse) !NodeIndex { | ... | @@ -644,7 +676,7 @@ fn parseExpressionPart(p: *Parse) !NodeIndex { |
| 644 | return expr; | 676 | return expr; |
| 645 | } | 677 | } |
| 646 | 678 | ||
| 647 | fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) !NodeIndex { | 679 | fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) Error!NodeIndex { |
| 648 | const trace = t.trace(@src()); | 680 | const trace = t.trace(@src()); |
| 649 | defer trace.end(); | 681 | defer trace.end(); |
| 650 | 682 | ||
| ... | @@ -673,7 +705,7 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) !NodeIndex { | ... | @@ -673,7 +705,7 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) !NodeIndex { |
| 673 | }); | 705 | }); |
| 674 | } | 706 | } |
| 675 | 707 | ||
| 676 | fn parseReturn(p: *Parse) !NodeIndex { | 708 | fn parseReturn(p: *Parse) Error!NodeIndex { |
| 677 | const trace = t.trace(@src()); | 709 | const trace = t.trace(@src()); |
| 678 | defer trace.end(); | 710 | defer trace.end(); |
| 679 | 711 | ||
| ... | @@ -693,7 +725,7 @@ fn parseReturn(p: *Parse) !NodeIndex { | ... | @@ -693,7 +725,7 @@ fn parseReturn(p: *Parse) !NodeIndex { |
| 693 | }); | 725 | }); |
| 694 | } | 726 | } |
| 695 | 727 | ||
| 696 | fn parseBreak(p: *Parse) !NodeIndex { | 728 | fn parseBreak(p: *Parse) Error!NodeIndex { |
| 697 | const trace = t.trace(@src()); | 729 | const trace = t.trace(@src()); |
| 698 | defer trace.end(); | 730 | defer trace.end(); |
| 699 | 731 | ||
| ... | @@ -719,7 +751,7 @@ fn parseBreak(p: *Parse) !NodeIndex { | ... | @@ -719,7 +751,7 @@ fn parseBreak(p: *Parse) !NodeIndex { |
| 719 | }); | 751 | }); |
| 720 | } | 752 | } |
| 721 | 753 | ||
| 722 | fn parseBuiltinCall(p: *Parse) !NodeIndex { | 754 | fn parseBuiltinCall(p: *Parse) Error!NodeIndex { |
| 723 | const trace = t.trace(@src()); | 755 | const trace = t.trace(@src()); |
| 724 | defer trace.end(); | 756 | defer trace.end(); |
| 725 | 757 | ||
| ... | @@ -736,7 +768,7 @@ fn parseBuiltinCall(p: *Parse) !NodeIndex { | ... | @@ -736,7 +768,7 @@ fn parseBuiltinCall(p: *Parse) !NodeIndex { |
| 736 | }); | 768 | }); |
| 737 | } | 769 | } |
| 738 | 770 | ||
| 739 | fn parseCallArgs(p: *Parse) anyerror!SubList { | 771 | fn parseCallArgs(p: *Parse) Error!SubList { |
| 740 | const trace = t.trace(@src()); | 772 | const trace = t.trace(@src()); |
| 741 | defer trace.end(); | 773 | defer trace.end(); |
| 742 | 774 | ||
| ... | @@ -764,7 +796,7 @@ fn parseCallArgs(p: *Parse) anyerror!SubList { | ... | @@ -764,7 +796,7 @@ fn parseCallArgs(p: *Parse) anyerror!SubList { |
| 764 | return p.scratchToSubList(scratch_top); | 796 | return p.scratchToSubList(scratch_top); |
| 765 | } | 797 | } |
| 766 | 798 | ||
| 767 | fn parseIf(p: *Parse) !NodeIndex { | 799 | fn parseIf(p: *Parse) Error!NodeIndex { |
| 768 | const trace = t.trace(@src()); | 800 | const trace = t.trace(@src()); |
| 769 | defer trace.end(); | 801 | defer trace.end(); |
| 770 | 802 | ||
| ... | @@ -795,7 +827,7 @@ fn parseIf(p: *Parse) !NodeIndex { | ... | @@ -795,7 +827,7 @@ fn parseIf(p: *Parse) !NodeIndex { |
| 795 | }); | 827 | }); |
| 796 | } | 828 | } |
| 797 | 829 | ||
| 798 | fn parseElseValue(p: *Parse) anyerror!NodeIndex { | 830 | fn parseElseValue(p: *Parse) Error!NodeIndex { |
| 799 | const trace = t.trace(@src()); | 831 | const trace = t.trace(@src()); |
| 800 | defer trace.end(); | 832 | defer trace.end(); |
| 801 | 833 | ||
| ... | @@ -810,7 +842,7 @@ fn parseElseValue(p: *Parse) anyerror!NodeIndex { | ... | @@ -810,7 +842,7 @@ fn parseElseValue(p: *Parse) anyerror!NodeIndex { |
| 810 | }; | 842 | }; |
| 811 | } | 843 | } |
| 812 | 844 | ||
| 813 | fn parseGroup(p: *Parse) !NodeIndex { | 845 | fn parseGroup(p: *Parse) Error!NodeIndex { |
| 814 | const trace = t.trace(@src()); | 846 | const trace = t.trace(@src()); |
| 815 | defer trace.end(); | 847 | defer trace.end(); |
| 816 | 848 | ||
| ... | @@ -855,7 +887,7 @@ fn addExtra(p: *Parse, comptime T: type, extra: T) !ExtraIndex { | ... | @@ -855,7 +887,7 @@ fn addExtra(p: *Parse, comptime T: type, extra: T) !ExtraIndex { |
| 855 | fn fail(p: *Parse, ast_error: AstError) Error { | 887 | fn fail(p: *Parse, ast_error: AstError) Error { |
| 856 | p.ast_error = ast_error; | 888 | p.ast_error = ast_error; |
| 857 | std.debug.print("got {s}", .{@tagName(p.currentTag())}); | 889 | std.debug.print("got {s}", .{@tagName(p.currentTag())}); |
| 858 | return Error.SyntaxError; | 890 | return Error.ParseFail; |
| 859 | } | 891 | } |
| 860 | 892 | ||
| 861 | fn blankNode(p: *Parse, comptime tag: Token.Tag) !NodeIndex { | 893 | fn blankNode(p: *Parse, comptime tag: Token.Tag) !NodeIndex { |
| ... | @@ -959,3 +991,6 @@ const optionalNodeIndex = OptionalNodeIndex.init; | ... | @@ -959,3 +991,6 @@ const optionalNodeIndex = OptionalNodeIndex.init; |
| 959 | const optionalExtraIndex = OptionalExtraIndex.init; | 991 | const optionalExtraIndex = OptionalExtraIndex.init; |
| 960 | 992 | ||
| 961 | const t = @import("tracer"); | 993 | const t = @import("tracer"); |
| 994 | |||
| 995 | const Report = @import("Report.zig"); | ||
| 996 | const Comp = @import("Comp.zig"); |
src/compiler/Report.zig+466-15| ... | @@ -1,38 +1,489 @@ | ... | @@ -1,38 +1,489 @@ |
| 1 | //! A Report is a list of errors and warnings for a part of a compilation. | ||
| 2 | //! | ||
| 3 | //! `Err/Warn` structs are unions of every possible message, the logic | ||
| 4 | //! for printing them is all done here. | ||
| 5 | //! | ||
| 6 | //! Across the compiler, `Report.Builder`s' `.err` and `.warn` methods | ||
| 7 | //! can add messages by ID. | ||
| 1 | errors: []Item = &.{}, | 8 | errors: []Item = &.{}, |
| 2 | warns: []Item = &.{}, | 9 | warnings: []Item = &.{}, |
| 3 | string_bytes: []u8 = &.{}, | 10 | string_bytes: []u8 = &.{}, |
| 4 | 11 | ||
| 5 | pub const Item = struct { | 12 | pub const Item = struct { |
| 6 | offset: ByteOffset, | 13 | /// What is this diagnostic |
| 7 | message: String, | 14 | data: union { @"error": Err, warn: Warn }, |
| 15 | /// What file was it in | ||
| 16 | url: String, | ||
| 17 | |||
| 18 | /// Type is never stored, inferred from which list it is in. | ||
| 19 | pub const Type = enum { @"error", warn }; | ||
| 20 | }; | ||
| 21 | |||
| 22 | pub const Err = union(enum) { | ||
| 23 | pub const FieldEnum = std.meta.FieldEnum(Err); | ||
| 24 | |||
| 25 | /// Constructs a `Report` with just a single error. | ||
| 26 | pub fn toReport(err: Err, comp: *Comp, file: *File) !Report { | ||
| 27 | const string_bytes = try comp.gpa.dupe(u8, file.file_path); | ||
| 28 | errdefer comp.gpa.free(string_bytes); | ||
| 29 | |||
| 30 | const errors = try comp.gpa.dupe(Item, &.{.{ | ||
| 31 | .data = .{ .@"error" = err }, | ||
| 32 | .url = .{ .start = 0, .len = @intCast(string_bytes.len) }, | ||
| 33 | }}); | ||
| 34 | errdefer comp.gpa.free(errors); | ||
| 35 | |||
| 36 | return .{ .errors = errors, .string_bytes = string_bytes }; | ||
| 37 | } | ||
| 38 | |||
| 39 | ast: @import("Parse.zig").AstError, | ||
| 40 | |||
| 41 | unknown_identifier: struct { | ||
| 42 | node: NodeIndex, | ||
| 43 | |||
| 44 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 45 | try ctx.msgNode(data.node, "unknown identifier '{s}'", .{ | ||
| 46 | ctx.ast.basicNodeSlice(data.node), | ||
| 47 | }); | ||
| 48 | } | ||
| 49 | }, | ||
| 50 | invalid_builtin: struct { | ||
| 51 | node: NodeIndex, | ||
| 52 | |||
| 53 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 54 | try ctx.msgToken( | ||
| 55 | ctx.ast.nodeToken(data.node), | ||
| 56 | "unknown built-in function '{s}'", | ||
| 57 | .{ctx.ast.basicNodeSlice(data.node)}, | ||
| 58 | ); | ||
| 59 | } | ||
| 60 | }, | ||
| 61 | invalid_discard: struct { | ||
| 62 | node: NodeIndex, | ||
| 63 | |||
| 64 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 65 | try ctx.msgNode(data.node, "local cannot be named '_'", .{}); | ||
| 66 | } | ||
| 67 | }, | ||
| 68 | duplicate_function: struct { | ||
| 69 | node: NodeIndex, | ||
| 70 | prev: NodeIndex, | ||
| 71 | |||
| 72 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 73 | try ctx.msgToken(ctx.ast.fnDeclIdToken(data.node), "re-declaration of '{s}'", .{ | ||
| 74 | ctx.ast.fullFnDecl(data.node).id, | ||
| 75 | }); | ||
| 76 | try ctx.msgToken(ctx.ast.fnDeclIdToken(data.prev), "also declared here", .{}); | ||
| 77 | // TODO: be able to store a full list of other declarations | ||
| 78 | // and add them here as "and here" and so on | ||
| 79 | } | ||
| 80 | }, | ||
| 81 | duplicate_param: struct { | ||
| 82 | node: NodeIndex, | ||
| 83 | prev: NodeIndex, | ||
| 84 | |||
| 85 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 86 | try ctx.msgToken(ctx.ast.fnDeclIdToken(data.node), "re-declaration of '{s}'", .{ | ||
| 87 | ctx.ast.fullParamDecl(data.node).id, | ||
| 88 | }); | ||
| 89 | try ctx.msgToken(ctx.ast.nodeToken(data.prev), "also declared here", .{}); | ||
| 90 | // TODO: be able to store a full list of other declarations | ||
| 91 | // and add them here as "and here" and so on | ||
| 92 | } | ||
| 93 | }, | ||
| 94 | invalid_return: struct { | ||
| 95 | node: NodeIndex, | ||
| 96 | |||
| 97 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 98 | try ctx.msgNode( | ||
| 99 | data.node, | ||
| 100 | "return cannot be used here", | ||
| 101 | .{}, | ||
| 102 | ); | ||
| 103 | } | ||
| 104 | }, | ||
| 105 | unresolved_label: struct { | ||
| 106 | node: NodeIndex, | ||
| 107 | |||
| 108 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 109 | try ctx.msg( | ||
| 110 | .{ .two_tokens = ctx.ast.nodeToken(data.node) + 1 }, | ||
| 111 | "unknown block label '{s}'", | ||
| 112 | .{ctx.ast.tokenSlice(ctx.ast.nodeToken(data.node) + 2)}, | ||
| 113 | ); | ||
| 114 | } | ||
| 115 | }, | ||
| 116 | shadow_builtin_variable: struct { | ||
| 117 | node: NodeIndex, | ||
| 118 | |||
| 119 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 120 | try ctx.msgToken( | ||
| 121 | ctx.ast.nodeToken(data.node) + 1, | ||
| 122 | "declaration shadows built-in constant '{s}'", | ||
| 123 | .{ctx.ast.fullVarDecl(data.node).id}, | ||
| 124 | ); | ||
| 125 | } | ||
| 126 | }, | ||
| 127 | }; | ||
| 128 | |||
| 129 | pub const Warn = union(enum) { | ||
| 130 | pub const FieldEnum = std.meta.FieldEnum(Warn); | ||
| 131 | |||
| 132 | unused: struct { | ||
| 133 | node: NodeIndex, | ||
| 134 | kind: UnusedKind, | ||
| 135 | |||
| 136 | fn build(data: @This(), ctx: *MsgContext) !void { | ||
| 137 | try ctx.msgToken(ctx.ast.idToken(data.node), "unused {s} '{s}'", .{ | ||
| 138 | @tagName(data.kind), | ||
| 139 | switch (ctx.ast.nodeTag(data.node)) { | ||
| 140 | .param_decl => ctx.ast.fullParamDecl(data.node).id, | ||
| 141 | .fn_decl => ctx.ast.fullFnDecl(data.node).id, | ||
| 142 | .var_decl => ctx.ast.fullVarDecl(data.node).id, | ||
| 143 | else => unreachable, // not a valid symbol node | ||
| 144 | }, | ||
| 145 | }); | ||
| 146 | } | ||
| 147 | }, | ||
| 148 | }; | ||
| 149 | |||
| 150 | pub const UnusedKind = enum { | ||
| 151 | param, | ||
| 152 | constant, | ||
| 153 | variable, | ||
| 154 | function, | ||
| 8 | }; | 155 | }; |
| 9 | 156 | ||
| 10 | pub const String = struct { | 157 | pub const String = struct { |
| 11 | start: u32, | 158 | start: u32, |
| 12 | len: u32, | 159 | len: u32, |
| 13 | 160 | ||
| 14 | pub fn message(item: Item, report: Report) []const u8 { | 161 | pub fn slice(str: String, report: Report) []const u8 { |
| 15 | return report.string_bytes[item.message_start..][0..item.message_len]; | 162 | return report.string_bytes[str.start..][0..str.len]; |
| 163 | } | ||
| 164 | }; | ||
| 165 | |||
| 166 | const Prefix = enum { | ||
| 167 | @"error", | ||
| 168 | warn, | ||
| 169 | note, | ||
| 170 | |||
| 171 | fn color(prefix: Prefix) std.io.tty.Color { | ||
| 172 | return switch (prefix) { | ||
| 173 | .@"error" => .red, | ||
| 174 | .warn => .yellow, | ||
| 175 | .note => .blue, | ||
| 176 | }; | ||
| 16 | } | 177 | } |
| 17 | }; | 178 | }; |
| 18 | 179 | ||
| 19 | pub fn hasErrors(report: Report) bool { | 180 | pub fn deinit(report: Report, gpa: Allocator) void { |
| 20 | return report.errors.len > 0; | 181 | gpa.free(report.errors); |
| 182 | gpa.free(report.warnings); | ||
| 183 | gpa.free(report.string_bytes); | ||
| 21 | } | 184 | } |
| 22 | 185 | ||
| 23 | pub fn hasWarns(report: Report) bool { | 186 | pub fn dumpToWriter(report: Report, comp: *Comp, writer: anytype) !void { |
| 24 | return report.warns.len > 0; | 187 | const tty = util.detectTTYFromWriter(writer); |
| 188 | try writer.print("{d} errors, {d} warnings\n", .{ report.errors.len, report.warnings.len }); | ||
| 189 | for (report.errors) |item| { | ||
| 190 | try report.formatItem(item, .@"error", comp, writer, tty); | ||
| 191 | } | ||
| 192 | for (report.warnings) |item| { | ||
| 193 | try report.formatItem(item, .warn, comp, writer, tty); | ||
| 194 | } | ||
| 25 | } | 195 | } |
| 26 | 196 | ||
| 27 | pub const Wip = struct { | 197 | pub fn formatItem( |
| 28 | errors: ArrayListUnmanaged(Item), | 198 | report: Report, |
| 29 | warns: ArrayListUnmanaged(Item), | 199 | item: Item, |
| 30 | string_bytes: ArrayListUnmanaged(u8), | 200 | item_type: Item.Type, |
| 201 | comp: *Comp, | ||
| 202 | writer: anytype, | ||
| 203 | tty: std.io.tty.Config, | ||
| 204 | ) !void { | ||
| 205 | var arena = std.heap.ArenaAllocator.init(comp.gpa); | ||
| 206 | defer arena.deinit(); | ||
| 207 | var sfa = std.heap.stackFallback(16384, arena.allocator()); | ||
| 208 | const alloc = sfa.get(); | ||
| 209 | |||
| 210 | const file = try comp.getFile(item.url.slice(report)); | ||
| 211 | |||
| 212 | const need_load_ast = !(item_type == .@"error" and item.data.@"error" == .ast); | ||
| 213 | const ast_data = if (need_load_ast) try file.getAst(comp) else undefined; | ||
| 214 | const ast = if (need_load_ast) &ast_data else null; | ||
| 215 | |||
| 216 | var ctx: MsgContext = .{ | ||
| 217 | .arena = alloc, | ||
| 218 | .comp = comp, | ||
| 219 | .file = file, | ||
| 220 | .ast = &ast_data, | ||
| 221 | .components = ArrayListUnmanaged(Component).initCapacity(alloc, 16) catch | ||
| 222 | unreachable, // stack-fallback allocator has enough space. | ||
| 223 | }; | ||
| 224 | |||
| 225 | switch (item_type) { | ||
| 226 | inline else => |tag| switch (@field(item.data, @tagName(tag))) { | ||
| 227 | inline else => |payload| try payload.build(&ctx), | ||
| 228 | }, | ||
| 229 | } | ||
| 230 | |||
| 231 | assert(ctx.components.items.len > 0); | ||
| 232 | |||
| 233 | for (ctx.components.items, 1..) |component, i| { | ||
| 234 | const prefix: Prefix = if (i == 1) | ||
| 235 | switch (item_type) { | ||
| 236 | inline else => |tag| @field(Prefix, @tagName(tag)), | ||
| 237 | } | ||
| 238 | else | ||
| 239 | .note; | ||
| 240 | |||
| 241 | const is_last = i == ctx.components.items.len; | ||
| 242 | |||
| 243 | if (i != 1) { | ||
| 244 | try tty.setColor(writer, .dim); | ||
| 245 | try writer.writeAll(" │ "); | ||
| 246 | try tty.setColor(writer, .reset); | ||
| 247 | } | ||
| 248 | |||
| 249 | try tty.setColor(writer, prefix.color()); | ||
| 250 | try writer.writeAll(@tagName(prefix)); | ||
| 251 | try tty.setColor(writer, .reset); | ||
| 252 | try tty.setColor(writer, .dim); | ||
| 253 | try writer.writeAll(": "); | ||
| 254 | try tty.setColor(writer, .reset); | ||
| 255 | try writer.writeAll(component.message_utf8); | ||
| 256 | try writer.writeAll("\n"); | ||
| 257 | |||
| 258 | const SliceLoc = struct { | ||
| 259 | loc: Location, | ||
| 260 | byte: ByteOffset, | ||
| 261 | slice: []const u8, | ||
| 262 | }; | ||
| 263 | |||
| 264 | const utf8_source = (try file.getSource(comp.gpa)).code; | ||
| 265 | |||
| 266 | const at: SliceLoc = switch (component.highlight) { | ||
| 267 | inline .node, | ||
| 268 | .token, | ||
| 269 | .two_tokens, | ||
| 270 | => |payload, tag| loc: { | ||
| 271 | const token_index: TokenIndex = switch (tag) { | ||
| 272 | .node => ast.?.nodeToken(payload), | ||
| 273 | .token, .two_tokens => payload, | ||
| 274 | else => comptime unreachable, | ||
| 275 | }; | ||
| 276 | |||
| 277 | const location = ast.?.tokenLocation(0, token_index); | ||
| 278 | const token = ast.?.tokens.get(token_index); | ||
| 279 | |||
| 280 | break :loc .{ | ||
| 281 | .loc = location, | ||
| 282 | .slice = token.slice(ast.?.utf8_source), | ||
| 283 | .byte = token.offset, | ||
| 284 | }; | ||
| 285 | }, | ||
| 286 | |||
| 287 | inline .codepoint, | ||
| 288 | .codepoint_eol, | ||
| 289 | => |b, tag| loc: { | ||
| 290 | const len = switch (tag) { | ||
| 291 | .codepoint => std.unicode.utf8ByteSequenceLength(utf8_source[b]) catch 1, | ||
| 292 | .codepoint_eol => if (std.mem.indexOfScalar(u8, utf8_source[b..], '\n')) |n| | ||
| 293 | n | ||
| 294 | else | ||
| 295 | (utf8_source.len - b), | ||
| 296 | else => comptime unreachable, | ||
| 297 | }; | ||
| 298 | break :loc .{ | ||
| 299 | .byte = b, | ||
| 300 | .slice = utf8_source[b..][0..len], | ||
| 301 | .loc = .{ | ||
| 302 | .column = 0, | ||
| 303 | .line = 0, | ||
| 304 | .line_start = 0, | ||
| 305 | .line_end = utf8_source.len, | ||
| 306 | }, | ||
| 307 | }; | ||
| 308 | }, | ||
| 309 | }; | ||
| 310 | |||
| 311 | const line_before = utf8_source[at.loc.line_start..at.byte]; | ||
| 312 | const trimmed_before = std.mem.trimLeft(u8, line_before, " \t\r"); | ||
| 313 | const line_after = utf8_source[at.byte + at.slice.len .. at.loc.line_end + 1]; | ||
| 314 | const width = std.fmt.count("{d}", .{at.loc.line}); | ||
| 315 | |||
| 316 | try tty.setColor(writer, .dim); | ||
| 317 | try writer.writeByteNTimes(' ', width); | ||
| 318 | try writer.print("{s}{s}:{d}:{d}\n", .{ | ||
| 319 | if (i == 1) "┬ " else "├ ", | ||
| 320 | file.file_path, | ||
| 321 | at.loc.line, | ||
| 322 | at.loc.column, | ||
| 323 | }); | ||
| 324 | try writer.print("{d}", .{at.loc.line}); | ||
| 325 | try writer.print("{s} ", .{"│"}); | ||
| 326 | try tty.setColor(writer, .reset); | ||
| 327 | try writer.writeAll(trimmed_before); | ||
| 328 | try tty.setColor(writer, prefix.color()); | ||
| 329 | try tty.setColor(writer, .bold); | ||
| 330 | try writer.writeAll(at.slice); | ||
| 331 | try tty.setColor(writer, .reset); | ||
| 332 | try writer.writeAll(line_after); | ||
| 333 | try tty.setColor(writer, .dim); | ||
| 334 | try writer.writeAll(if (is_last) " ╰" else " ├"); | ||
| 335 | try writer.writeBytesNTimes( | ||
| 336 | "─", | ||
| 337 | width + | ||
| 338 | (at.byte - at.loc.line_start) - | ||
| 339 | (line_before.len - trimmed_before.len), | ||
| 340 | ); | ||
| 341 | try tty.setColor(writer, .reset); | ||
| 342 | try tty.setColor(writer, prefix.color()); | ||
| 343 | try tty.setColor(writer, .bold); | ||
| 344 | try writer.writeBytesNTimes("━", at.slice.len); | ||
| 345 | try tty.setColor(writer, .reset); | ||
| 346 | try writer.writeAll("\n"); | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | pub fn dumpToStdErr(report: Report, comp: *Comp) !void { | ||
| 351 | const stderr = std.io.getStdErr().writer(); | ||
| 352 | |||
| 353 | // Disabled because of coloring issues. | ||
| 354 | // var buf = std.io.bufferedWriter(stderr); | ||
| 355 | // const writer = buf.writer(); | ||
| 356 | try dumpToWriter(report, comp, stderr); | ||
| 357 | // try buf.flush(); | ||
| 358 | } | ||
| 359 | |||
| 360 | pub const Builder = struct { | ||
| 361 | gpa: Allocator, | ||
| 362 | current_file: *Comp.File, | ||
| 363 | errors: ArrayListUnmanaged(Item) = .{}, | ||
| 364 | warnings: ArrayListUnmanaged(Item) = .{}, | ||
| 365 | string_bytes: ArrayListUnmanaged(u8) = .{}, | ||
| 366 | current_file_str: ?String = null, | ||
| 367 | |||
| 368 | pub fn err(b: *Builder, comptime kind: Err.FieldEnum, data: std.meta.FieldType(Err, kind)) !void { | ||
| 369 | try b.errors.append(b.gpa, .{ | ||
| 370 | .data = .{ .@"error" = @unionInit(Err, @tagName(kind), data) }, | ||
| 371 | .url = try b.currentFileString(), | ||
| 372 | }); | ||
| 373 | } | ||
| 374 | |||
| 375 | pub fn warn(b: *Builder, comptime kind: Warn.FieldEnum, data: std.meta.FieldType(Warn, kind)) !void { | ||
| 376 | try b.warnings.append(b.gpa, .{ | ||
| 377 | .data = .{ .warn = @unionInit(Warn, @tagName(kind), data) }, | ||
| 378 | .url = try b.currentFileString(), | ||
| 379 | }); | ||
| 380 | } | ||
| 381 | |||
| 382 | pub fn addString(b: *Builder, str: []const u8) !String { | ||
| 383 | const top = b.string_bytes.items.len; | ||
| 384 | try b.string_bytes.appendSlice(b.gpa, str); | ||
| 385 | return .{ .start = @intCast(top), .len = @intCast(str.len) }; | ||
| 386 | } | ||
| 387 | |||
| 388 | pub fn currentFileString(b: *Builder) !String { | ||
| 389 | return b.current_file_str orelse { | ||
| 390 | b.current_file_str = try b.addString(b.current_file.file_path); | ||
| 391 | return b.current_file_str.?; | ||
| 392 | }; | ||
| 393 | } | ||
| 394 | |||
| 395 | pub fn deinit(b: *Builder) void { | ||
| 396 | b.errors.deinit(b.gpa); | ||
| 397 | b.warnings.deinit(b.gpa); | ||
| 398 | } | ||
| 399 | |||
| 400 | pub fn finalize(b: *Builder) !Report { | ||
| 401 | defer b.* = undefined; | ||
| 402 | errdefer b.deinit(); | ||
| 403 | |||
| 404 | const errors = try b.errors.toOwnedSlice(b.gpa); | ||
| 405 | errdefer b.gpa.free(errors); | ||
| 406 | |||
| 407 | const warnings = try b.warnings.toOwnedSlice(b.gpa); | ||
| 408 | errdefer b.gpa.free(warnings); | ||
| 409 | |||
| 410 | const string_bytes = try b.string_bytes.toOwnedSlice(b.gpa); | ||
| 411 | errdefer b.gpa.free(string_bytes); | ||
| 412 | |||
| 413 | return .{ | ||
| 414 | .errors = errors, | ||
| 415 | .warnings = warnings, | ||
| 416 | .string_bytes = string_bytes, | ||
| 417 | }; | ||
| 418 | } | ||
| 419 | }; | ||
| 420 | |||
| 421 | pub const Highlight = union(enum) { | ||
| 422 | // Highlight one token | ||
| 423 | token: TokenIndex, | ||
| 424 | // Highlight two tokens | ||
| 425 | two_tokens: TokenIndex, | ||
| 426 | // Highlight a Node | ||
| 427 | node: NodeIndex, | ||
| 428 | // Highlight a codepoint | ||
| 429 | codepoint: ByteOffset, | ||
| 430 | // Highlight from the start of a codepoint to '\n' | ||
| 431 | codepoint_eol: ByteOffset, | ||
| 432 | }; | ||
| 433 | |||
| 434 | const Component = struct { | ||
| 435 | message_utf8: []const u8, | ||
| 436 | highlight: Highlight, | ||
| 437 | }; | ||
| 438 | |||
| 439 | /// This serves as a small API to make creating messages easier. | ||
| 440 | pub const MsgContext = struct { | ||
| 441 | file: *File, | ||
| 442 | comp: *Comp, | ||
| 443 | ast: *const Ast, | ||
| 444 | |||
| 445 | /// This arena is is a stack fallback with 16kb, then | ||
| 446 | /// falling back to an arena allocator. This should | ||
| 447 | /// make the cost of arbitrarily creating `Component` | ||
| 448 | /// nodes extremely lightweight. | ||
| 449 | arena: Allocator, | ||
| 450 | |||
| 451 | /// First component is "error" or "warn" | ||
| 452 | /// Trailing components are "note" | ||
| 453 | components: ArrayListUnmanaged(Component), | ||
| 454 | |||
| 455 | pub fn msg(m: *MsgContext, highlight: Highlight, comptime fmt: []const u8, args: anytype) !void { | ||
| 456 | const text = try std.fmt.allocPrint(m.arena, fmt, args); | ||
| 457 | try m.components.append(m.arena, .{ | ||
| 458 | .message_utf8 = text, | ||
| 459 | .highlight = highlight, | ||
| 460 | }); | ||
| 461 | } | ||
| 462 | |||
| 463 | pub fn msgNode(m: *MsgContext, node: NodeIndex, comptime fmt: []const u8, args: anytype) !void { | ||
| 464 | return m.msg(.{ .node = node }, fmt, args); | ||
| 465 | } | ||
| 466 | |||
| 467 | pub fn msgToken(m: *MsgContext, token: TokenIndex, comptime fmt: []const u8, args: anytype) !void { | ||
| 468 | return m.msg(.{ .token = token }, fmt, args); | ||
| 469 | } | ||
| 31 | }; | 470 | }; |
| 32 | 471 | ||
| 33 | const Report = @This(); | 472 | const Report = @This(); |
| 34 | const std = @import("std"); | 473 | const std = @import("std"); |
| 35 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | 474 | const ArrayListUnmanaged = std.ArrayListUnmanaged; |
| 475 | const Allocator = std.mem.Allocator; | ||
| 36 | const assert = std.debug.assert; | 476 | const assert = std.debug.assert; |
| 37 | pub const Tokenizer = @import("Tokenizer.zig"); | 477 | |
| 38 | pub const ByteOffset = Tokenizer.ByteOffset; | 478 | const Tokenizer = @import("Tokenizer.zig"); |
| 479 | const ByteOffset = Tokenizer.ByteOffset; | ||
| 480 | |||
| 481 | const Ast = @import("Ast.zig"); | ||
| 482 | const TokenIndex = Ast.TokenIndex; | ||
| 483 | const NodeIndex = Ast.NodeIndex; | ||
| 484 | const Location = Ast.Location; | ||
| 485 | |||
| 486 | const Comp = @import("Comp.zig"); | ||
| 487 | const File = Comp.File; | ||
| 488 | |||
| 489 | const util = @import("util.zig"); |
src/compiler/Sema.zig+11-32| ... | @@ -37,7 +37,6 @@ const InstMap = struct { | ... | @@ -37,7 +37,6 @@ const InstMap = struct { |
| 37 | 37 | ||
| 38 | pub fn init(allocator: Allocator, code: *const Teg, inst_start: Teg.Inst.Index) !InstMap { | 38 | pub fn init(allocator: Allocator, code: *const Teg, inst_start: Teg.Inst.Index) !InstMap { |
| 39 | const decl = code.declData(inst_start); | 39 | const decl = code.declData(inst_start); |
| 40 | log.info("pre-allocating {d} map-indicies for InstMap", .{decl.instruction_count}); | ||
| 41 | const insts = try allocator.alloc(Air.Inst.Ref.Optional, decl.instruction_count); | 40 | const insts = try allocator.alloc(Air.Inst.Ref.Optional, decl.instruction_count); |
| 42 | @memset(insts, .none); | 41 | @memset(insts, .none); |
| 43 | return .{ | 42 | return .{ |
| ... | @@ -72,7 +71,6 @@ pub fn init(s: *Sema) !void { | ... | @@ -72,7 +71,6 @@ pub fn init(s: *Sema) !void { |
| 72 | 71 | ||
| 73 | try s.comp.current_compile_decls.append(s.gpa, s.decl_index); | 72 | try s.comp.current_compile_decls.append(s.gpa, s.decl_index); |
| 74 | 73 | ||
| 75 | log.info("init sema on index {}", .{s.decl_index}); | ||
| 76 | s.inst_map = try InstMap.init( | 74 | s.inst_map = try InstMap.init( |
| 77 | s.arena, | 75 | s.arena, |
| 78 | &s.code, | 76 | &s.code, |
| ... | @@ -193,8 +191,6 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { | ... | @@ -193,8 +191,6 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 193 | const t = tracer.traceFmt(@src(), "%{d}, {s}", .{ @intFromEnum(in), @tagName(s.code.instTag(in)) }); | 191 | const t = tracer.traceFmt(@src(), "%{d}, {s}", .{ @intFromEnum(in), @tagName(s.code.instTag(in)) }); |
| 194 | defer t.end(); | 192 | defer t.end(); |
| 195 | 193 | ||
| 196 | log.info("runtime %{d} = .{s}", .{ @intFromEnum(in), @tagName(s.code.instTag(in)) }); | ||
| 197 | |||
| 198 | const out = try s.analyzeCodeInner(in); | 194 | const out = try s.analyzeCodeInner(in); |
| 199 | if (s.code.instTag(in) != .block) | 195 | if (s.code.instTag(in) != .block) |
| 200 | try s.inst_map.add(in, out); | 196 | try s.inst_map.add(in, out); |
| ... | @@ -226,8 +222,6 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { | ... | @@ -226,8 +222,6 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 226 | const string = rhs.asString(s.ip); | 222 | const string = rhs.asString(s.ip); |
| 227 | 223 | ||
| 228 | const base_type = s.typeOfRef(base); | 224 | const base_type = s.typeOfRef(base); |
| 229 | log.info("process field with type {any} . {s}", .{ base_type, string.toSlice(s.ip) }); | ||
| 230 | |||
| 231 | switch (base_type) { | 225 | switch (base_type) { |
| 232 | .opaque_type => { | 226 | .opaque_type => { |
| 233 | return s.addInstRef(.field, .{ | 227 | return s.addInstRef(.field, .{ |
| ... | @@ -277,18 +271,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { | ... | @@ -277,18 +271,12 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 277 | } | 271 | } |
| 278 | 272 | ||
| 279 | const left_type = s.typeOfRef(lhs); | 273 | const left_type = s.typeOfRef(lhs); |
| 274 | _ = left_type; // autofix | ||
| 280 | const right_type = s.typeOfRef(rhs); | 275 | const right_type = s.typeOfRef(rhs); |
| 276 | _ = right_type; // autofix | ||
| 281 | 277 | ||
| 282 | // TODO: validate type for operation | 278 | // TODO: validate type for operation |
| 283 | 279 | ||
| 284 | log.info("runtime {s} operation on {} and {}", .{ | ||
| 285 | @tagName(tag), | ||
| 286 | left_type.toKey(s.ip), | ||
| 287 | right_type.toKey(s.ip), | ||
| 288 | }); | ||
| 289 | |||
| 290 | // todo: type check | ||
| 291 | |||
| 292 | return s.addInstRef(tag.airTag(), .{ | 280 | return s.addInstRef(tag.airTag(), .{ |
| 293 | .lhs = lhs, | 281 | .lhs = lhs, |
| 294 | .rhs = rhs, | 282 | .rhs = rhs, |
| ... | @@ -300,14 +288,10 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { | ... | @@ -300,14 +288,10 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 300 | const operand = try s.resolveRefRuntime(un.operand); | 288 | const operand = try s.resolveRefRuntime(un.operand); |
| 301 | 289 | ||
| 302 | const operand_type = s.typeOfRef(operand); | 290 | const operand_type = s.typeOfRef(operand); |
| 291 | _ = operand_type; // autofix | ||
| 303 | 292 | ||
| 304 | // TODO: validate type for operation | 293 | // TODO: validate type for operation |
| 305 | 294 | ||
| 306 | log.info("runtime {s} operation on {}", .{ | ||
| 307 | @tagName(tag), | ||
| 308 | operand_type.toKey(s.ip), | ||
| 309 | }); | ||
| 310 | |||
| 311 | return s.addInstRef(tag.airTag(), .{ | 295 | return s.addInstRef(tag.airTag(), .{ |
| 312 | .operand = operand, | 296 | .operand = operand, |
| 313 | }); | 297 | }); |
| ... | @@ -425,7 +409,6 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { | ... | @@ -425,7 +409,6 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 425 | fn markInst(s: *Sema, inst: Air.Inst.Index) void { | 409 | fn markInst(s: *Sema, inst: Air.Inst.Index) void { |
| 426 | const flags: *Air.Flags = &s.air_instructions.items(.flags)[@intFromEnum(inst)]; | 410 | const flags: *Air.Flags = &s.air_instructions.items(.flags)[@intFromEnum(inst)]; |
| 427 | flags.uses = flags.uses.increment(); | 411 | flags.uses = flags.uses.increment(); |
| 428 | log.info("markInst {}", .{inst}); | ||
| 429 | } | 412 | } |
| 430 | 413 | ||
| 431 | fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref { | 414 | fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref { |
| ... | @@ -485,15 +468,13 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value { | ... | @@ -485,15 +468,13 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value { |
| 485 | } | 468 | } |
| 486 | } | 469 | } |
| 487 | 470 | ||
| 488 | const tag = s.code.instTag(in); | 471 | switch (s.code.instTag(in)) { |
| 489 | log.info("comptime %{d} = .{s}", .{ @intFromEnum(in), @tagName(tag) }); | ||
| 490 | switch (tag) { | ||
| 491 | .string => { | 472 | .string => { |
| 492 | const string: Teg.SlicedString = s.code.instPayload(.string, in); | 473 | const string: Teg.SlicedString = s.code.instPayload(.string, in); |
| 493 | return s.makeValue(.{ .string = string.slice(s.code.string_bytes) }); | 474 | return s.makeValue(.{ .string = string.slice(s.code.string_bytes) }); |
| 494 | }, | 475 | }, |
| 495 | 476 | ||
| 496 | else => std.debug.panic("TODO: comptime analyze of .{s}", .{@tagName(tag)}), | 477 | else => |tag| std.debug.panic("TODO: comptime analyze of .{s}", .{@tagName(tag)}), |
| 497 | } | 478 | } |
| 498 | } | 479 | } |
| 499 | 480 | ||
| ... | @@ -509,12 +490,13 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value { | ... | @@ -509,12 +490,13 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value { |
| 509 | var arena = std.heap.ArenaAllocator.init(s.gpa); | 490 | var arena = std.heap.ArenaAllocator.init(s.gpa); |
| 510 | defer arena.deinit(); | 491 | defer arena.deinit(); |
| 511 | 492 | ||
| 493 | var todo: Report = undefined; | ||
| 512 | var sema: Sema = .{ | 494 | var sema: Sema = .{ |
| 513 | .comp = s.comp, | 495 | .comp = s.comp, |
| 514 | .gpa = s.gpa, | 496 | .gpa = s.gpa, |
| 515 | .ip = s.ip, | 497 | .ip = s.ip, |
| 516 | .arena = arena.allocator(), | 498 | .arena = arena.allocator(), |
| 517 | .code = try s.ip.namespacePtr(decl.src_namespace).file_scope.getTeg(s.gpa), | 499 | .code = try s.ip.namespacePtr(decl.src_namespace).file_scope.getTeg(s.comp, &todo), |
| 518 | .decl = decl, | 500 | .decl = decl, |
| 519 | .decl_index = decl_index, | 501 | .decl_index = decl_index, |
| 520 | .namespace_index = decl.src_namespace, | 502 | .namespace_index = decl.src_namespace, |
| ... | @@ -529,10 +511,9 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value { | ... | @@ -529,10 +511,9 @@ fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value { |
| 529 | 511 | ||
| 530 | return value; | 512 | return value; |
| 531 | }, | 513 | }, |
| 532 | .complete => { | 514 | else => { |
| 533 | return decl.val; | 515 | return decl.val; |
| 534 | }, | 516 | }, |
| 535 | else => |tag| std.debug.panic("TODO: load_decl when decl is in state .{s}", .{@tagName(tag)}), | ||
| 536 | } | 517 | } |
| 537 | } | 518 | } |
| 538 | 519 | ||
| ... | @@ -573,7 +554,6 @@ const IncompleteBlock = struct { | ... | @@ -573,7 +554,6 @@ const IncompleteBlock = struct { |
| 573 | }; | 554 | }; |
| 574 | 555 | ||
| 575 | fn addBlock(s: *Sema) !IncompleteBlock { | 556 | fn addBlock(s: *Sema) !IncompleteBlock { |
| 576 | log.info("start block at @{d}", .{s.air_instructions.len}); | ||
| 577 | try s.air_instructions.append(s.gpa, .{ | 557 | try s.air_instructions.append(s.gpa, .{ |
| 578 | .tag = .block, | 558 | .tag = .block, |
| 579 | .data = undefined, | 559 | .data = undefined, |
| ... | @@ -583,7 +563,6 @@ fn addBlock(s: *Sema) !IncompleteBlock { | ... | @@ -583,7 +563,6 @@ fn addBlock(s: *Sema) !IncompleteBlock { |
| 583 | } | 563 | } |
| 584 | 564 | ||
| 585 | fn addInstRef(s: *Sema, comptime tag: Air.Inst.Tag, data: tag.Data()) !Air.Inst.Ref { | 565 | fn addInstRef(s: *Sema, comptime tag: Air.Inst.Tag, data: tag.Data()) !Air.Inst.Ref { |
| 586 | log.info("add air .{s}, {}", .{ @tagName(tag), data }); | ||
| 587 | try s.air_instructions.append(s.gpa, .{ | 566 | try s.air_instructions.append(s.gpa, .{ |
| 588 | .tag = tag, | 567 | .tag = tag, |
| 589 | .data = Air.EncodedData.encode(tag, data), | 568 | .data = Air.EncodedData.encode(tag, data), |
| ... | @@ -627,11 +606,9 @@ fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref { | ... | @@ -627,11 +606,9 @@ fn mapTeg(s: *Sema, inst: Teg.Inst.Ref) Air.Inst.Ref { |
| 627 | } | 606 | } |
| 628 | 607 | ||
| 629 | fn validateType(s: *Sema, checked_type: Value, wanted_type: InternPool.Index) !void { | 608 | fn validateType(s: *Sema, checked_type: Value, wanted_type: InternPool.Index) !void { |
| 609 | _ = s; // autofix | ||
| 630 | if (checked_type.toIntern() != wanted_type) { | 610 | if (checked_type.toIntern() != wanted_type) { |
| 631 | log.info("failed type check {}, got {}", .{ Value.fromInterned(wanted_type).fmt(s.ip), checked_type.fmt(s.ip) }); | ||
| 632 | return error.TypeCheckFailed; | 611 | return error.TypeCheckFailed; |
| 633 | } else { | ||
| 634 | log.info("passed type check {}", .{checked_type.fmt(s.ip)}); | ||
| 635 | } | 612 | } |
| 636 | } | 613 | } |
| 637 | 614 | ||
| ... | @@ -690,4 +667,6 @@ const InternPool = @import("InternPool.zig"); | ... | @@ -690,4 +667,6 @@ const InternPool = @import("InternPool.zig"); |
| 690 | const NullTerminatedString = InternPool.NullTerminatedString; | 667 | const NullTerminatedString = InternPool.NullTerminatedString; |
| 691 | const dod = @import("dod.zig"); | 668 | const dod = @import("dod.zig"); |
| 692 | 669 | ||
| 670 | const Report = @import("Report.zig"); | ||
| 671 | |||
| 693 | const tracer = @import("tracer"); | 672 | const tracer = @import("tracer"); |
src/compiler/Teg.zig+4| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | insts: Inst.List.Slice, | 3 | insts: Inst.List.Slice, |
| 4 | string_bytes: []const u8, | 4 | string_bytes: []const u8, |
| 5 | extra: ExtraStore = .{}, | 5 | extra: ExtraStore = .{}, |
| 6 | report: Report, | ||
| 6 | 7 | ||
| 7 | pub const ExtraStore = dod.ExtraStore(.teg, .@"const"); | 8 | pub const ExtraStore = dod.ExtraStore(.teg, .@"const"); |
| 8 | pub const EncodedData = dod.EncodedData(Inst.Tag, u64); | 9 | pub const EncodedData = dod.EncodedData(Inst.Tag, u64); |
| ... | @@ -343,6 +344,7 @@ pub fn deinit(teg: *Teg, gpa: std.mem.Allocator) void { | ... | @@ -343,6 +344,7 @@ pub fn deinit(teg: *Teg, gpa: std.mem.Allocator) void { |
| 343 | gpa.free(teg.string_bytes); | 344 | gpa.free(teg.string_bytes); |
| 344 | teg.extra.deinit(gpa); | 345 | teg.extra.deinit(gpa); |
| 345 | teg.insts.deinit(gpa); | 346 | teg.insts.deinit(gpa); |
| 347 | teg.report.deinit(gpa); | ||
| 346 | teg.* = undefined; | 348 | teg.* = undefined; |
| 347 | } | 349 | } |
| 348 | 350 | ||
| ... | @@ -433,3 +435,5 @@ const Float = dod.Float; | ... | @@ -433,3 +435,5 @@ const Float = dod.Float; |
| 433 | const InternPool = @import("InternPool.zig"); | 435 | const InternPool = @import("InternPool.zig"); |
| 434 | 436 | ||
| 435 | const Air = @import("Air.zig"); | 437 | const Air = @import("Air.zig"); |
| 438 | |||
| 439 | const Report = @import("Report.zig"); |
src/compiler/Tokenizer.zig+96-79| ... | @@ -5,18 +5,14 @@ | ... | @@ -5,18 +5,14 @@ |
| 5 | //! Retriving payloads like string/numeric values, identifier names, etc is | 5 | //! Retriving payloads like string/numeric values, identifier names, etc is |
| 6 | //! done separately, either in Parse or Transform, when the respective data | 6 | //! done separately, either in Parse or Transform, when the respective data |
| 7 | //! is actually needed. Tokenization does guarante that such data is valid. | 7 | //! is actually needed. Tokenization does guarante that such data is valid. |
| 8 | 8 | code: [:0]const u8, | |
| 9 | // TODO: make `code` a [:0]const u8. this doesnt disallow null bytes, but | ||
| 10 | // it requires the source to end with one. This can make byteAt(1) return u8 | ||
| 11 | // instead of an optional. I am told this improves performance. | ||
| 12 | code: []const u8, | ||
| 13 | pos: ByteOffset = 0, | 9 | pos: ByteOffset = 0, |
| 14 | 10 | ||
| 15 | /// Tokenize source code into a list of tokens. | 11 | /// Tokenize source code into a list of tokens. |
| 16 | /// When an error occurs, the position of the error is returned in `error_pos`. | 12 | /// When an error occurs, the position of the error is returned in `error_pos`. |
| 17 | /// Caller owns returned slice. | 13 | /// Caller owns returned slice. |
| 18 | pub fn tokenize( | 14 | pub fn tokenize( |
| 19 | source_code: []const u8, | 15 | source_code: [:0]const u8, |
| 20 | gpa: mem.Allocator, | 16 | gpa: mem.Allocator, |
| 21 | error_pos: *ByteOffset, | 17 | error_pos: *ByteOffset, |
| 22 | ) TokenizeError!Token.List.Slice { | 18 | ) TokenizeError!Token.List.Slice { |
| ... | @@ -233,7 +229,7 @@ pub const Token = struct { | ... | @@ -233,7 +229,7 @@ pub const Token = struct { |
| 233 | /// | 229 | /// |
| 234 | /// If you are computing the length of something that is knowable | 230 | /// If you are computing the length of something that is knowable |
| 235 | /// without the source code, use token.tag.staticLen() instead. | 231 | /// without the source code, use token.tag.staticLen() instead. |
| 236 | pub fn len(token: Token, utf8_source: []const u8) u32 { | 232 | pub fn len(token: Token, utf8_source: [:0]const u8) u32 { |
| 237 | return token.tag.staticLen() orelse { | 233 | return token.tag.staticLen() orelse { |
| 238 | var tokenizer = Tokenizer{ .code = utf8_source, .pos = token.offset }; | 234 | var tokenizer = Tokenizer{ .code = utf8_source, .pos = token.offset }; |
| 239 | assert( | 235 | assert( |
| ... | @@ -246,7 +242,7 @@ pub const Token = struct { | ... | @@ -246,7 +242,7 @@ pub const Token = struct { |
| 246 | }; | 242 | }; |
| 247 | } | 243 | } |
| 248 | 244 | ||
| 249 | pub fn slice(token: Token, utf8_source: []const u8) []const u8 { | 245 | pub fn slice(token: Token, utf8_source: [:0]const u8) []const u8 { |
| 250 | return utf8_source[token.offset..][0..token.len(utf8_source)]; | 246 | return utf8_source[token.offset..][0..token.len(utf8_source)]; |
| 251 | } | 247 | } |
| 252 | 248 | ||
| ... | @@ -294,7 +290,7 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -294,7 +290,7 @@ pub fn next(t: *Tokenizer) !?Token { |
| 294 | const trace = T.trace(@src()); | 290 | const trace = T.trace(@src()); |
| 295 | defer trace.end(); | 291 | defer trace.end(); |
| 296 | 292 | ||
| 297 | switch (t.byte() orelse return null) { | 293 | switch (t.byte()) { |
| 298 | ' ', '\t', '\n', '\r' => unreachable, // call skipWhitespace | 294 | ' ', '\t', '\n', '\r' => unreachable, // call skipWhitespace |
| 299 | 295 | ||
| 300 | 'a'...'z', 'A'...'Z', '_', '$' => { | 296 | 'a'...'z', 'A'...'Z', '_', '$' => { |
| ... | @@ -303,7 +299,7 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -303,7 +299,7 @@ pub fn next(t: *Tokenizer) !?Token { |
| 303 | return try t.tokenizeIdentifier(start); | 299 | return try t.tokenizeIdentifier(start); |
| 304 | }, | 300 | }, |
| 305 | 301 | ||
| 306 | '@' => switch (t.byteAt(1) orelse 0) { | 302 | '@' => switch (t.nextByte()) { |
| 307 | '"' => { | 303 | '"' => { |
| 308 | const start = t.pos; | 304 | const start = t.pos; |
| 309 | t.pos += 1; | 305 | t.pos += 1; |
| ... | @@ -319,12 +315,12 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -319,12 +315,12 @@ pub fn next(t: *Tokenizer) !?Token { |
| 319 | 315 | ||
| 320 | '0'...'9' => return try t.tokenizeNumber(), | 316 | '0'...'9' => return try t.tokenizeNumber(), |
| 321 | 317 | ||
| 322 | '.' => switch (t.byteAt(1) orelse 0) { | 318 | '.' => switch (t.nextByte()) { |
| 323 | '0'...'9' => return try t.tokenizeNumber(), | 319 | '0'...'9' => return try t.tokenizeNumber(), |
| 324 | else => return t.simpleToken(.dot), | 320 | else => return t.simpleToken(.dot), |
| 325 | }, | 321 | }, |
| 326 | 322 | ||
| 327 | '/' => switch (t.byteAt(1) orelse 0) { | 323 | '/' => switch (t.nextByte()) { |
| 328 | '=' => return t.simpleToken(.slash_equal), | 324 | '=' => return t.simpleToken(.slash_equal), |
| 329 | '/' => { | 325 | '/' => { |
| 330 | if ((t.byteAt(2) orelse 0 == '/') and | 326 | if ((t.byteAt(2) orelse 0 == '/') and |
| ... | @@ -349,7 +345,7 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -349,7 +345,7 @@ pub fn next(t: *Tokenizer) !?Token { |
| 349 | else => return t.simpleToken(.slash), | 345 | else => return t.simpleToken(.slash), |
| 350 | }, | 346 | }, |
| 351 | 347 | ||
| 352 | '\\' => switch (t.byteAt(1) orelse 0) { | 348 | '\\' => switch (t.nextByte()) { |
| 353 | '\\' => { | 349 | '\\' => { |
| 354 | const start = t.pos; | 350 | const start = t.pos; |
| 355 | t.pos = @intCast(mem.indexOfScalarPos(u8, t.code, t.pos + 2, '\n') orelse t.code.len); | 351 | t.pos = @intCast(mem.indexOfScalarPos(u8, t.code, t.pos + 2, '\n') orelse t.code.len); |
| ... | @@ -360,30 +356,30 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -360,30 +356,30 @@ pub fn next(t: *Tokenizer) !?Token { |
| 360 | 356 | ||
| 361 | '"' => return try t.tokenizeString(), | 357 | '"' => return try t.tokenizeString(), |
| 362 | 358 | ||
| 363 | '&' => switch (t.byteAt(1) orelse 0) { | 359 | '&' => switch (t.nextByte()) { |
| 364 | '&' => return t.simpleToken(.and_and), | 360 | '&' => return t.simpleToken(.and_and), |
| 365 | '=' => return t.simpleToken(.and_equal), | 361 | '=' => return t.simpleToken(.and_equal), |
| 366 | else => return t.simpleToken(.@"and"), | 362 | else => return t.simpleToken(.@"and"), |
| 367 | }, | 363 | }, |
| 368 | '|' => switch (t.byteAt(1) orelse 0) { | 364 | '|' => switch (t.nextByte()) { |
| 369 | '|' => return t.simpleToken(.pipe_pipe), | 365 | '|' => return t.simpleToken(.pipe_pipe), |
| 370 | '=' => return t.simpleToken(.pipe_equal), | 366 | '=' => return t.simpleToken(.pipe_equal), |
| 371 | else => return t.simpleToken(.pipe), | 367 | else => return t.simpleToken(.pipe), |
| 372 | }, | 368 | }, |
| 373 | '^' => switch (t.byteAt(1) orelse 0) { | 369 | '^' => switch (t.nextByte()) { |
| 374 | '^' => return t.simpleToken(.caret), | 370 | '^' => return t.simpleToken(.caret), |
| 375 | '=' => return t.simpleToken(.caret_equal), | 371 | '=' => return t.simpleToken(.caret_equal), |
| 376 | else => return t.simpleToken(.caret), | 372 | else => return t.simpleToken(.caret), |
| 377 | }, | 373 | }, |
| 378 | '!' => switch (t.byteAt(1) orelse 0) { | 374 | '!' => switch (t.nextByte()) { |
| 379 | '=' => return t.simpleToken(.equal_equal), | 375 | '=' => return t.simpleToken(.equal_equal), |
| 380 | else => return t.simpleToken(.bang_equal), | 376 | else => return t.simpleToken(.bang_equal), |
| 381 | }, | 377 | }, |
| 382 | '=' => switch (t.byteAt(1) orelse 0) { | 378 | '=' => switch (t.nextByte()) { |
| 383 | '=' => return t.simpleToken(.equal_equal), | 379 | '=' => return t.simpleToken(.equal_equal), |
| 384 | else => return t.simpleToken(.equal), | 380 | else => return t.simpleToken(.equal), |
| 385 | }, | 381 | }, |
| 386 | '>' => switch (t.byteAt(1) orelse 0) { | 382 | '>' => switch (t.nextByte()) { |
| 387 | '=' => return t.simpleToken(.gt_equal), | 383 | '=' => return t.simpleToken(.gt_equal), |
| 388 | '>' => switch (t.byteAt(2) orelse 0) { | 384 | '>' => switch (t.byteAt(2) orelse 0) { |
| 389 | '>' => return t.simpleToken(.gt_gt_gt), | 385 | '>' => return t.simpleToken(.gt_gt_gt), |
| ... | @@ -391,7 +387,7 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -391,7 +387,7 @@ pub fn next(t: *Tokenizer) !?Token { |
| 391 | }, | 387 | }, |
| 392 | else => return t.simpleToken(.gt), | 388 | else => return t.simpleToken(.gt), |
| 393 | }, | 389 | }, |
| 394 | '<' => switch (t.byteAt(1) orelse 0) { | 390 | '<' => switch (t.nextByte()) { |
| 395 | '=' => return t.simpleToken(.lt_equal), | 391 | '=' => return t.simpleToken(.lt_equal), |
| 396 | '<' => switch (t.byteAt(2) orelse 0) { | 392 | '<' => switch (t.byteAt(2) orelse 0) { |
| 397 | '=' => return t.simpleToken(.lt_lt_equal), | 393 | '=' => return t.simpleToken(.lt_lt_equal), |
| ... | @@ -399,14 +395,12 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -399,14 +395,12 @@ pub fn next(t: *Tokenizer) !?Token { |
| 399 | // Git merge conflicts are <<<<<<< | 395 | // Git merge conflicts are <<<<<<< |
| 400 | const start = t.pos; | 396 | const start = t.pos; |
| 401 | t.pos += 2; | 397 | t.pos += 2; |
| 402 | brk: { | 398 | var remain: u3 = 4; |
| 403 | var remain: u3 = 4; | 399 | while (t.byte() == '<') { |
| 404 | while (t.byte() orelse break :brk == '<') { | 400 | t.pos += 1; |
| 405 | t.pos += 1; | 401 | remain -= 1; |
| 406 | remain -= 1; | 402 | if (remain == 0) { |
| 407 | if (remain == 0) { | 403 | return error.GitConflict; |
| 408 | return error.GitConflict; | ||
| 409 | } | ||
| 410 | } | 404 | } |
| 411 | } | 405 | } |
| 412 | t.pos = start; | 406 | t.pos = start; |
| ... | @@ -415,25 +409,25 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -415,25 +409,25 @@ pub fn next(t: *Tokenizer) !?Token { |
| 415 | }, | 409 | }, |
| 416 | else => return t.simpleToken(.lt), | 410 | else => return t.simpleToken(.lt), |
| 417 | }, | 411 | }, |
| 418 | '+' => switch (t.byteAt(1) orelse 0) { | 412 | '+' => switch (t.nextByte()) { |
| 419 | '=' => return t.simpleToken(.plus_equal), | 413 | '=' => return t.simpleToken(.plus_equal), |
| 420 | else => return t.simpleToken(.plus), | 414 | else => return t.simpleToken(.plus), |
| 421 | }, | 415 | }, |
| 422 | '-' => switch (t.byteAt(1) orelse 0) { | 416 | '-' => switch (t.nextByte()) { |
| 423 | '=' => return t.simpleToken(.minus_equal), | 417 | '=' => return t.simpleToken(.minus_equal), |
| 424 | '>' => return t.simpleToken(.minus_lt), | 418 | '>' => return t.simpleToken(.minus_lt), |
| 425 | else => return t.simpleToken(.minus), | 419 | else => return t.simpleToken(.minus), |
| 426 | }, | 420 | }, |
| 427 | '*' => switch (t.byteAt(1) orelse 0) { | 421 | '*' => switch (t.nextByte()) { |
| 428 | '*' => return t.simpleToken(.star_star), | 422 | '*' => return t.simpleToken(.star_star), |
| 429 | '=' => return t.simpleToken(.star_equal), | 423 | '=' => return t.simpleToken(.star_equal), |
| 430 | else => return t.simpleToken(.star), | 424 | else => return t.simpleToken(.star), |
| 431 | }, | 425 | }, |
| 432 | '%' => switch (t.byteAt(1) orelse 0) { | 426 | '%' => switch (t.nextByte()) { |
| 433 | '=' => return t.simpleToken(.percent_equal), | 427 | '=' => return t.simpleToken(.percent_equal), |
| 434 | else => return t.simpleToken(.percent), | 428 | else => return t.simpleToken(.percent), |
| 435 | }, | 429 | }, |
| 436 | '~' => switch (t.byteAt(1) orelse 0) { | 430 | '~' => switch (t.nextByte()) { |
| 437 | '=' => return t.simpleToken(.tilde_equal), | 431 | '=' => return t.simpleToken(.tilde_equal), |
| 438 | else => return t.simpleToken(.tilde), | 432 | else => return t.simpleToken(.tilde), |
| 439 | }, | 433 | }, |
| ... | @@ -462,19 +456,40 @@ pub fn next(t: *Tokenizer) !?Token { | ... | @@ -462,19 +456,40 @@ pub fn next(t: *Tokenizer) !?Token { |
| 462 | }, | 456 | }, |
| 463 | 457 | ||
| 464 | else => return error.InvalidSyntax, | 458 | else => return error.InvalidSyntax, |
| 459 | |||
| 460 | 0 => { | ||
| 461 | if (t.pos == t.code.len) return null; | ||
| 462 | return error.InvalidSyntax; | ||
| 463 | }, | ||
| 465 | } | 464 | } |
| 466 | } | 465 | } |
| 467 | } | 466 | } |
| 468 | 467 | ||
| 469 | fn byte(t: Tokenizer) ?u8 { | 468 | /// Source code is null terminated, so accessing this is safe |
| 470 | if (t.pos >= t.code.len) return null; | 469 | fn byte(t: Tokenizer) u8 { |
| 471 | return t.code[t.pos]; | 470 | return t.code[t.pos]; |
| 472 | } | 471 | } |
| 473 | 472 | ||
| 473 | /// Source code is null terminated. | ||
| 474 | fn nextByte(t: Tokenizer) u8 { | ||
| 475 | assert(t.byte() < 0x80); | ||
| 476 | return t.code[t.pos + 1]; | ||
| 477 | } | ||
| 478 | |||
| 474 | /// Remember that using this only makes sense if you know for certain that all | 479 | /// Remember that using this only makes sense if you know for certain that all |
| 475 | /// previous bytes are ASCII, otherwise you may hit UB | 480 | /// previous bytes are ASCII, otherwise you may hit UB |
| 476 | fn byteAt(t: Tokenizer, offset: u32) ?u8 { | 481 | fn byteAt(t: Tokenizer, comptime offset: u32) ?u8 { |
| 477 | if (t.pos + offset >= t.code.len) return null; | 482 | if (offset == 1) @compileError("use nextByte()"); |
| 483 | |||
| 484 | // assert that the previous bytes are ASCII | ||
| 485 | // current byte is allowed to be a continuation byte | ||
| 486 | for (t.code[t.pos .. t.pos + offset]) |b| { | ||
| 487 | assert(b < 0x80); | ||
| 488 | } | ||
| 489 | return t.code[t.pos + offset]; | ||
| 490 | } | ||
| 491 | |||
| 492 | fn byteAtRt(t: Tokenizer, offset: u32) ?u8 { | ||
| 478 | // assert that the previous bytes are ASCII | 493 | // assert that the previous bytes are ASCII |
| 479 | // current byte is allowed to be a continuation byte | 494 | // current byte is allowed to be a continuation byte |
| 480 | for (t.code[t.pos .. t.pos + offset]) |b| { | 495 | for (t.code[t.pos .. t.pos + offset]) |b| { |
| ... | @@ -484,7 +499,7 @@ fn byteAt(t: Tokenizer, offset: u32) ?u8 { | ... | @@ -484,7 +499,7 @@ fn byteAt(t: Tokenizer, offset: u32) ?u8 { |
| 484 | } | 499 | } |
| 485 | 500 | ||
| 486 | fn codePoint(t: Tokenizer) !struct { u3, u21 } { | 501 | fn codePoint(t: Tokenizer) !struct { u3, u21 } { |
| 487 | const b = t.byte() orelse unreachable; // must check the first byte manually | 502 | const b = t.byte(); // must check the first byte manually |
| 488 | assert(b >= 0x80); // must be a continuation byte | 503 | assert(b >= 0x80); // must be a continuation byte |
| 489 | 504 | ||
| 490 | const byte_len = unicode.utf8ByteSequenceLength(b) catch | 505 | const byte_len = unicode.utf8ByteSequenceLength(b) catch |
| ... | @@ -498,7 +513,7 @@ fn codePoint(t: Tokenizer) !struct { u3, u21 } { | ... | @@ -498,7 +513,7 @@ fn codePoint(t: Tokenizer) !struct { u3, u21 } { |
| 498 | } | 513 | } |
| 499 | 514 | ||
| 500 | fn isAtIdentifierStart(t: *Tokenizer) !bool { | 515 | fn isAtIdentifierStart(t: *Tokenizer) !bool { |
| 501 | switch (t.byte() orelse return false) { | 516 | switch (t.byte()) { |
| 502 | 'a'...'z', 'A'...'Z', '_', '$' => return true, | 517 | 'a'...'z', 'A'...'Z', '_', '$' => return true, |
| 503 | else => return false, | 518 | else => return false, |
| 504 | 0x80...0xFF => { | 519 | 0x80...0xFF => { |
| ... | @@ -513,7 +528,7 @@ fn tokenizeIdentifier(t: *Tokenizer, start_byte: u32) !Token { | ... | @@ -513,7 +528,7 @@ fn tokenizeIdentifier(t: *Tokenizer, start_byte: u32) !Token { |
| 513 | defer trace.end(); | 528 | defer trace.end(); |
| 514 | 529 | ||
| 515 | while (true) { | 530 | while (true) { |
| 516 | switch (t.byte() orelse break) { | 531 | switch (t.byte()) { |
| 517 | 'a'...'z', 'A'...'Z', '0'...'9', '_', '$' => { | 532 | 'a'...'z', 'A'...'Z', '0'...'9', '_', '$' => { |
| 518 | t.pos += 1; | 533 | t.pos += 1; |
| 519 | }, | 534 | }, |
| ... | @@ -555,8 +570,8 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { | ... | @@ -555,8 +570,8 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { |
| 555 | defer trace.end(); | 570 | defer trace.end(); |
| 556 | 571 | ||
| 557 | // Caller ensures the following invariant: | 572 | // Caller ensures the following invariant: |
| 558 | switch (t.byte() orelse unreachable) { | 573 | switch (t.byte()) { |
| 559 | '.' => switch (t.byteAt(1) orelse unreachable) { | 574 | '.' => switch (t.nextByte()) { |
| 560 | '0'...'9' => {}, | 575 | '0'...'9' => {}, |
| 561 | else => unreachable, | 576 | else => unreachable, |
| 562 | }, | 577 | }, |
| ... | @@ -566,7 +581,7 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { | ... | @@ -566,7 +581,7 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { |
| 566 | 581 | ||
| 567 | // Parse Hex, Binary, Octal numbers | 582 | // Parse Hex, Binary, Octal numbers |
| 568 | if (t.byte() == '0') { | 583 | if (t.byte() == '0') { |
| 569 | switch (t.byteAt(1) orelse 0) { | 584 | switch (t.nextByte()) { |
| 570 | 'x', 'X' => { | 585 | 'x', 'X' => { |
| 571 | t.pos += 2; | 586 | t.pos += 2; |
| 572 | try t.consumeIntegerRadix(.hex); | 587 | try t.consumeIntegerRadix(.hex); |
| ... | @@ -594,8 +609,8 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { | ... | @@ -594,8 +609,8 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { |
| 594 | try t.consumeInteger(); | 609 | try t.consumeInteger(); |
| 595 | 610 | ||
| 596 | // decimal point notation | 611 | // decimal point notation |
| 597 | switch (t.byte() orelse 0) { | 612 | switch (t.byte()) { |
| 598 | '.' => switch (t.byteAt(1) orelse 0) { | 613 | '.' => switch (t.nextByte()) { |
| 599 | '0'...'9' => { | 614 | '0'...'9' => { |
| 600 | t.pos += 1; | 615 | t.pos += 1; |
| 601 | tag = .number_float; | 616 | tag = .number_float; |
| ... | @@ -611,15 +626,15 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { | ... | @@ -611,15 +626,15 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { |
| 611 | } | 626 | } |
| 612 | } | 627 | } |
| 613 | 628 | ||
| 614 | switch (t.byte() orelse 0) { | 629 | switch (t.byte()) { |
| 615 | 'e', 'E' => { | 630 | 'e', 'E' => { |
| 616 | tag = .number_float; | 631 | tag = .number_float; |
| 617 | t.pos += 1; | 632 | t.pos += 1; |
| 618 | 633 | ||
| 619 | switch (t.byte() orelse 0) { | 634 | switch (t.byte()) { |
| 620 | '+', '-' => { | 635 | '+', '-' => { |
| 621 | t.pos += 1; | 636 | t.pos += 1; |
| 622 | switch (t.byte() orelse 0) { | 637 | switch (t.byte()) { |
| 623 | '0'...'9' => {}, | 638 | '0'...'9' => {}, |
| 624 | else => return t.maybeUTF8Error(error.InvalidSyntax), | 639 | else => return t.maybeUTF8Error(error.InvalidSyntax), |
| 625 | } | 640 | } |
| ... | @@ -631,8 +646,8 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { | ... | @@ -631,8 +646,8 @@ fn tokenizeNumber(t: *Tokenizer) !?Token { |
| 631 | try t.consumeInteger(); | 646 | try t.consumeInteger(); |
| 632 | 647 | ||
| 633 | // decimal point notation | 648 | // decimal point notation |
| 634 | switch (t.byte() orelse 0) { | 649 | switch (t.byte()) { |
| 635 | '.' => switch (t.byteAt(1) orelse 0) { | 650 | '.' => switch (t.nextByte()) { |
| 636 | '0'...'9' => { | 651 | '0'...'9' => { |
| 637 | t.pos += 1; | 652 | t.pos += 1; |
| 638 | tag = .number_float; | 653 | tag = .number_float; |
| ... | @@ -662,7 +677,7 @@ fn maybeConsumeInteger(t: *Tokenizer) !?void { | ... | @@ -662,7 +677,7 @@ fn maybeConsumeInteger(t: *Tokenizer) !?void { |
| 662 | const trace = T.trace(@src()); | 677 | const trace = T.trace(@src()); |
| 663 | defer trace.end(); | 678 | defer trace.end(); |
| 664 | 679 | ||
| 665 | switch (t.byte() orelse return null) { | 680 | switch (t.byte()) { |
| 666 | '0'...'9' => { | 681 | '0'...'9' => { |
| 667 | t.pos += 1; | 682 | t.pos += 1; |
| 668 | }, | 683 | }, |
| ... | @@ -676,7 +691,7 @@ fn consumeInteger(t: *Tokenizer) !void { | ... | @@ -676,7 +691,7 @@ fn consumeInteger(t: *Tokenizer) !void { |
| 676 | const trace = T.trace(@src()); | 691 | const trace = T.trace(@src()); |
| 677 | defer trace.end(); | 692 | defer trace.end(); |
| 678 | 693 | ||
| 679 | switch (t.byte() orelse unreachable) { | 694 | switch (t.byte()) { |
| 680 | '0'...'9' => { | 695 | '0'...'9' => { |
| 681 | t.pos += 1; | 696 | t.pos += 1; |
| 682 | }, | 697 | }, |
| ... | @@ -684,13 +699,13 @@ fn consumeInteger(t: *Tokenizer) !void { | ... | @@ -684,13 +699,13 @@ fn consumeInteger(t: *Tokenizer) !void { |
| 684 | } | 699 | } |
| 685 | 700 | ||
| 686 | while (true) { | 701 | while (true) { |
| 687 | switch (t.byte() orelse return) { | 702 | switch (t.byte()) { |
| 688 | '0'...'9' => { | 703 | '0'...'9' => { |
| 689 | t.pos += 1; | 704 | t.pos += 1; |
| 690 | }, | 705 | }, |
| 691 | '_' => { | 706 | '_' => { |
| 692 | t.pos += 1; | 707 | t.pos += 1; |
| 693 | switch (t.byte() orelse 0) { | 708 | switch (t.byte()) { |
| 694 | '0'...'9' => { | 709 | '0'...'9' => { |
| 695 | t.pos += 1; | 710 | t.pos += 1; |
| 696 | }, | 711 | }, |
| ... | @@ -715,7 +730,7 @@ fn consumeIntegerRadix(t: *Tokenizer, comptime radix: Radix) !void { | ... | @@ -715,7 +730,7 @@ fn consumeIntegerRadix(t: *Tokenizer, comptime radix: Radix) !void { |
| 715 | }; | 730 | }; |
| 716 | 731 | ||
| 717 | first: { | 732 | first: { |
| 718 | const b = t.byte() orelse return; | 733 | const b = t.byte(); |
| 719 | inline for (digits) |digit| { | 734 | inline for (digits) |digit| { |
| 720 | if (b == digit) { | 735 | if (b == digit) { |
| 721 | break :first; | 736 | break :first; |
| ... | @@ -728,7 +743,7 @@ fn consumeIntegerRadix(t: *Tokenizer, comptime radix: Radix) !void { | ... | @@ -728,7 +743,7 @@ fn consumeIntegerRadix(t: *Tokenizer, comptime radix: Radix) !void { |
| 728 | 743 | ||
| 729 | while (true) { | 744 | while (true) { |
| 730 | brk: { | 745 | brk: { |
| 731 | const b = t.byte() orelse return; | 746 | const b = t.byte(); |
| 732 | inline for (digits) |digit| { | 747 | inline for (digits) |digit| { |
| 733 | if (b == digit) { | 748 | if (b == digit) { |
| 734 | break :brk; | 749 | break :brk; |
| ... | @@ -741,7 +756,7 @@ fn consumeIntegerRadix(t: *Tokenizer, comptime radix: Radix) !void { | ... | @@ -741,7 +756,7 @@ fn consumeIntegerRadix(t: *Tokenizer, comptime radix: Radix) !void { |
| 741 | } | 756 | } |
| 742 | 757 | ||
| 743 | fn tokenizeString(t: *Tokenizer) !Token { | 758 | fn tokenizeString(t: *Tokenizer) !Token { |
| 744 | assert(t.byte().? == '"'); | 759 | assert(t.byte() == '"'); |
| 745 | const start = t.pos; | 760 | const start = t.pos; |
| 746 | try t.consumeString(); | 761 | try t.consumeString(); |
| 747 | return .{ .tag = .string, .offset = start }; | 762 | return .{ .tag = .string, .offset = start }; |
| ... | @@ -751,32 +766,30 @@ fn consumeString(t: *Tokenizer) !void { | ... | @@ -751,32 +766,30 @@ fn consumeString(t: *Tokenizer) !void { |
| 751 | const trace = T.trace(@src()); | 766 | const trace = T.trace(@src()); |
| 752 | defer trace.end(); | 767 | defer trace.end(); |
| 753 | 768 | ||
| 754 | assert(t.byte().? == '"'); | 769 | const start = t.pos; |
| 770 | assert(t.byte() == '"'); | ||
| 755 | 771 | ||
| 756 | t.pos += 1; | 772 | t.pos += 1; |
| 757 | 773 | ||
| 758 | while (true) { | 774 | while (true) { |
| 759 | const b = t.byte() orelse return error.UnterminatedString; | 775 | switch (t.byte()) { |
| 760 | switch (b) { | ||
| 761 | '"' => { | 776 | '"' => { |
| 762 | t.pos += 1; | 777 | t.pos += 1; |
| 763 | break; | 778 | break; |
| 764 | }, | 779 | }, |
| 765 | '\\' => { | 780 | '\\' => { |
| 766 | switch (t.byteAt(1) orelse { | 781 | switch (t.nextByte()) { |
| 767 | // we want error.InvalidEscapeSequence to be at the '\' char, | 782 | '\n' => { |
| 768 | // but we want error.UnterminatedString to be at the end of the source code | 783 | t.pos = start; |
| 769 | t.pos += 1; | 784 | return error.UnterminatedString; |
| 770 | return error.UnterminatedString; | 785 | }, |
| 771 | }) { | ||
| 772 | '\n' => return error.UnterminatedString, | ||
| 773 | '\\' => t.pos += 2, | 786 | '\\' => t.pos += 2, |
| 774 | '"' => t.pos += 2, | 787 | '"' => t.pos += 2, |
| 775 | 'n' => t.pos += 2, | 788 | 'n' => t.pos += 2, |
| 776 | 'r' => t.pos += 2, | 789 | 'r' => t.pos += 2, |
| 777 | 't' => t.pos += 2, | 790 | 't' => t.pos += 2, |
| 778 | '0' => switch (t.byteAt(2) orelse { | 791 | '0' => switch (t.byteAt(2) orelse { |
| 779 | t.pos += 2; | 792 | t.pos = start; |
| 780 | return error.UnterminatedString; | 793 | return error.UnterminatedString; |
| 781 | }) { | 794 | }) { |
| 782 | // '\0xFF' hex escape | 795 | // '\0xFF' hex escape |
| ... | @@ -788,7 +801,7 @@ fn consumeString(t: *Tokenizer) !void { | ... | @@ -788,7 +801,7 @@ fn consumeString(t: *Tokenizer) !void { |
| 788 | const esc_start = t.pos; | 801 | const esc_start = t.pos; |
| 789 | t.consumeStringUnicodeEscape() catch |err| switch (err) { | 802 | t.consumeStringUnicodeEscape() catch |err| switch (err) { |
| 790 | error.InvalidEscapeSequence => { | 803 | error.InvalidEscapeSequence => { |
| 791 | if (t.byte().? == '\n') | 804 | if (t.byte() == '\n') |
| 792 | return error.UnterminatedString; | 805 | return error.UnterminatedString; |
| 793 | t.pos = esc_start; | 806 | t.pos = esc_start; |
| 794 | return err; | 807 | return err; |
| ... | @@ -797,12 +810,16 @@ fn consumeString(t: *Tokenizer) !void { | ... | @@ -797,12 +810,16 @@ fn consumeString(t: *Tokenizer) !void { |
| 797 | t.pos = esc_start; | 810 | t.pos = esc_start; |
| 798 | return err; | 811 | return err; |
| 799 | }, | 812 | }, |
| 800 | error.UnterminatedString => return err, | 813 | // error.UnterminatedString => return err, |
| 801 | }; | 814 | }; |
| 802 | }, | 815 | }, |
| 803 | else => return t.maybeUTF8ErrorAt(error.InvalidEscapeSequence, 1), | 816 | else => return t.maybeUTF8ErrorAt(error.InvalidEscapeSequence, 1), |
| 804 | } | 817 | } |
| 805 | }, | 818 | }, |
| 819 | '\n' => { | ||
| 820 | t.pos = start; | ||
| 821 | return error.UnterminatedString; | ||
| 822 | }, | ||
| 806 | else => { | 823 | else => { |
| 807 | t.pos += 1; | 824 | t.pos += 1; |
| 808 | }, | 825 | }, |
| ... | @@ -818,16 +835,16 @@ fn consumeStringUnicodeEscape(t: *Tokenizer) !void { | ... | @@ -818,16 +835,16 @@ fn consumeStringUnicodeEscape(t: *Tokenizer) !void { |
| 818 | const trace = T.trace(@src()); | 835 | const trace = T.trace(@src()); |
| 819 | defer trace.end(); | 836 | defer trace.end(); |
| 820 | 837 | ||
| 821 | assert(t.byte().? == '\\'); | 838 | assert(t.byte() == '\\'); |
| 822 | assert(t.byteAt(1).? == 'u'); | 839 | assert(t.nextByte() == 'u'); |
| 823 | t.pos += 2; | 840 | t.pos += 2; |
| 824 | 841 | ||
| 825 | if ((t.byte() orelse return error.UnterminatedString) != '{') | 842 | if (t.byte() != '{') |
| 826 | return error.InvalidEscapeSequence; | 843 | return error.InvalidEscapeSequence; |
| 827 | t.pos += 1; | 844 | t.pos += 1; |
| 828 | 845 | ||
| 829 | if ((t.byte() orelse return error.UnterminatedString) == '0') { | 846 | if (t.byte() == '0') { |
| 830 | if ((t.byteAt(1) orelse return error.UnterminatedString) == 'x') { | 847 | if (t.nextByte() == 'x') { |
| 831 | t.pos += 2; | 848 | t.pos += 2; |
| 832 | } | 849 | } |
| 833 | } | 850 | } |
| ... | @@ -837,7 +854,7 @@ fn consumeStringUnicodeEscape(t: *Tokenizer) !void { | ... | @@ -837,7 +854,7 @@ fn consumeStringUnicodeEscape(t: *Tokenizer) !void { |
| 837 | 854 | ||
| 838 | const slice = t.code[start..t.pos]; | 855 | const slice = t.code[start..t.pos]; |
| 839 | 856 | ||
| 840 | if ((t.byte() orelse return error.UnterminatedString) != '}') | 857 | if (t.byte() != '}') |
| 841 | return error.InvalidEscapeSequence; | 858 | return error.InvalidEscapeSequence; |
| 842 | 859 | ||
| 843 | const code_point = std.fmt.parseInt(u21, slice, 16) catch |err| switch (err) { | 860 | const code_point = std.fmt.parseInt(u21, slice, 16) catch |err| switch (err) { |
| ... | @@ -866,7 +883,7 @@ fn maybeUTF8Error(t: *Tokenizer, comptime real_err: anytype) @Type(.{ | ... | @@ -866,7 +883,7 @@ fn maybeUTF8Error(t: *Tokenizer, comptime real_err: anytype) @Type(.{ |
| 866 | .{ .name = @errorName(real_err) }, | 883 | .{ .name = @errorName(real_err) }, |
| 867 | }, | 884 | }, |
| 868 | }) { | 885 | }) { |
| 869 | switch (t.byte() orelse return real_err) { | 886 | switch (t.byte()) { |
| 870 | 0x80...0xFF => { | 887 | 0x80...0xFF => { |
| 871 | _ = try t.codePoint(); | 888 | _ = try t.codePoint(); |
| 872 | return real_err; | 889 | return real_err; |
| ... | @@ -881,7 +898,7 @@ fn maybeUTF8ErrorAt(t: *Tokenizer, comptime real_err: anytype, at: u32) @Type(.{ | ... | @@ -881,7 +898,7 @@ fn maybeUTF8ErrorAt(t: *Tokenizer, comptime real_err: anytype, at: u32) @Type(.{ |
| 881 | .{ .name = @errorName(real_err) }, | 898 | .{ .name = @errorName(real_err) }, |
| 882 | }, | 899 | }, |
| 883 | }) { | 900 | }) { |
| 884 | switch (t.byteAt(at) orelse return real_err) { | 901 | switch (t.byteAtRt(at) orelse return real_err) { |
| 885 | 0x80...0xFF => { | 902 | 0x80...0xFF => { |
| 886 | _ = try t.codePoint(); | 903 | _ = try t.codePoint(); |
| 887 | return real_err; | 904 | return real_err; |
| ... | @@ -895,7 +912,7 @@ pub fn skipWhitespace(t: *Tokenizer) void { | ... | @@ -895,7 +912,7 @@ pub fn skipWhitespace(t: *Tokenizer) void { |
| 895 | defer trace.end(); | 912 | defer trace.end(); |
| 896 | 913 | ||
| 897 | while (true) { | 914 | while (true) { |
| 898 | switch (t.byte() orelse break) { | 915 | switch (t.byte()) { |
| 899 | ' ', '\t', '\n', '\r' => { | 916 | ' ', '\t', '\n', '\r' => { |
| 900 | t.pos += 1; | 917 | t.pos += 1; |
| 901 | }, | 918 | }, |
src/compiler/Transform.zig+95-28| ... | @@ -14,29 +14,51 @@ scratch: std.ArrayListUnmanaged(u32) = .{}, | ... | @@ -14,29 +14,51 @@ scratch: std.ArrayListUnmanaged(u32) = .{}, |
| 14 | call_args: std.ArrayListUnmanaged(Inst.Ref) = .{}, | 14 | call_args: std.ArrayListUnmanaged(Inst.Ref) = .{}, |
| 15 | scopes: std.ArrayListUnmanaged(Scope) = .{}, | 15 | scopes: std.ArrayListUnmanaged(Scope) = .{}, |
| 16 | 16 | ||
| 17 | pub fn transform(ast: Ast, gpa: Allocator) !Teg { | 17 | report: Report.Builder, |
| 18 | |||
| 19 | pub const Error = error{ | ||
| 20 | OutOfMemory, | ||
| 21 | TransformFail, | ||
| 22 | }; | ||
| 23 | |||
| 24 | pub fn transform( | ||
| 25 | ast: Ast, | ||
| 26 | gpa: Allocator, | ||
| 27 | file: *Comp.File, | ||
| 28 | fail_report: *Report, | ||
| 29 | ) Error!Teg { | ||
| 18 | const trace = tracer.trace(@src()); | 30 | const trace = tracer.trace(@src()); |
| 19 | defer trace.end(); | 31 | defer trace.end(); |
| 20 | 32 | ||
| 21 | var t: Transform = .{ | 33 | var t: Transform = .{ |
| 22 | .ast = ast, | 34 | .ast = ast, |
| 23 | .gpa = gpa, | 35 | .gpa = gpa, |
| 36 | .report = .{ .gpa = gpa, .current_file = file }, | ||
| 24 | }; | 37 | }; |
| 25 | errdefer t.deinit(); | 38 | errdefer t.deinit(); |
| 26 | 39 | ||
| 27 | try t.string_bytes.append(t.gpa, 0); | 40 | try t.string_bytes.append(t.gpa, 0); |
| 28 | 41 | ||
| 29 | const module = try t.transformModule(0); | 42 | const module = t.transformModule(0) catch |err| switch (err) { |
| 43 | error.OutOfMemory => return err, | ||
| 44 | }; | ||
| 45 | |||
| 46 | const report = try t.report.finalize(); | ||
| 47 | if (report.errors.len > 0) { | ||
| 48 | fail_report.* = report; | ||
| 49 | return error.TransformFail; | ||
| 50 | } | ||
| 51 | |||
| 30 | assert(@intFromEnum(module) == 0); | 52 | assert(@intFromEnum(module) == 0); |
| 31 | 53 | ||
| 32 | try t.insts.setCapacity(t.gpa, t.insts.len); | 54 | try t.insts.setCapacity(t.gpa, t.insts.len); |
| 33 | 55 | ||
| 34 | defer t.deinit(); | 56 | defer t.deinit(); |
| 35 | |||
| 36 | return Teg{ | 57 | return Teg{ |
| 37 | .insts = t.insts.toOwnedSlice(), | 58 | .insts = t.insts.toOwnedSlice(), |
| 38 | .string_bytes = try t.string_bytes.toOwnedSlice(t.gpa), | 59 | .string_bytes = try t.string_bytes.toOwnedSlice(t.gpa), |
| 39 | .extra = try t.extra.toOwnedStore(t.gpa), | 60 | .extra = try t.extra.toOwnedStore(t.gpa), |
| 61 | .report = report, | ||
| 40 | }; | 62 | }; |
| 41 | } | 63 | } |
| 42 | 64 | ||
| ... | @@ -45,6 +67,9 @@ pub fn deinit(t: *Transform) void { | ... | @@ -45,6 +67,9 @@ pub fn deinit(t: *Transform) void { |
| 45 | t.call_args.deinit(t.gpa); | 67 | t.call_args.deinit(t.gpa); |
| 46 | assert(t.scopes.items.len == 0); | 68 | assert(t.scopes.items.len == 0); |
| 47 | t.scopes.deinit(t.gpa); | 69 | t.scopes.deinit(t.gpa); |
| 70 | t.string_bytes.deinit(t.gpa); | ||
| 71 | t.insts.deinit(t.gpa); | ||
| 72 | t.extra.deinit(t.gpa); | ||
| 48 | t.* = undefined; | 73 | t.* = undefined; |
| 49 | } | 74 | } |
| 50 | 75 | ||
| ... | @@ -65,6 +90,25 @@ const Scope = struct { | ... | @@ -65,6 +90,25 @@ const Scope = struct { |
| 65 | }; | 90 | }; |
| 66 | }; | 91 | }; |
| 67 | 92 | ||
| 93 | fn addUnusedWarnings(s: Scope, t: *Transform) !void { | ||
| 94 | for (s.symbols.values()) |value| { | ||
| 95 | if (!value.used) { | ||
| 96 | try t.report.warn(.unused, .{ | ||
| 97 | .node = value.node, | ||
| 98 | .kind = switch (value.data) { | ||
| 99 | .declaration => .function, | ||
| 100 | .param => .param, | ||
| 101 | .mutable => .variable, | ||
| 102 | .immutable => switch (t.ast.nodeTag(value.node)) { | ||
| 103 | .param_decl => .param, | ||
| 104 | else => .constant, | ||
| 105 | }, | ||
| 106 | }, | ||
| 107 | }); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 68 | fn deinit(s: *Scope, gpa: Allocator) void { | 112 | fn deinit(s: *Scope, gpa: Allocator) void { |
| 69 | s.symbols.deinit(gpa); | 113 | s.symbols.deinit(gpa); |
| 70 | s.* = undefined; | 114 | s.* = undefined; |
| ... | @@ -171,7 +215,7 @@ const BuiltInIds = std.ComptimeStringMap(Inst.Ref, .{ | ... | @@ -171,7 +215,7 @@ const BuiltInIds = std.ComptimeStringMap(Inst.Ref, .{ |
| 171 | // todo: nan/infinity? | 215 | // todo: nan/infinity? |
| 172 | }); | 216 | }); |
| 173 | 217 | ||
| 174 | fn transformModule(t: *Transform, module_node: NodeIndex) !Inst.Index { | 218 | fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index { |
| 175 | const trace = tracer.trace(@src()); | 219 | const trace = tracer.trace(@src()); |
| 176 | defer trace.end(); | 220 | defer trace.end(); |
| 177 | 221 | ||
| ... | @@ -259,10 +303,12 @@ fn transformModule(t: *Transform, module_node: NodeIndex) !Inst.Index { | ... | @@ -259,10 +303,12 @@ fn transformModule(t: *Transform, module_node: NodeIndex) !Inst.Index { |
| 259 | .extra_index = try t.extra.append(t.gpa, IndexList, declarations), | 303 | .extra_index = try t.extra.append(t.gpa, IndexList, declarations), |
| 260 | }); | 304 | }); |
| 261 | 305 | ||
| 306 | try t.scope().addUnusedWarnings(t); | ||
| 307 | |||
| 262 | return module; | 308 | return module; |
| 263 | } | 309 | } |
| 264 | 310 | ||
| 265 | fn previsitModuleStatement(t: *Transform, node: NodeIndex) !void { | 311 | fn previsitModuleStatement(t: *Transform, node: NodeIndex) Allocator.Error!void { |
| 266 | const trace = tracer.trace(@src()); | 312 | const trace = tracer.trace(@src()); |
| 267 | defer trace.end(); | 313 | defer trace.end(); |
| 268 | 314 | ||
| ... | @@ -272,7 +318,11 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) !void { | ... | @@ -272,7 +318,11 @@ fn previsitModuleStatement(t: *Transform, node: NodeIndex) !void { |
| 272 | const fn_decl = t.ast.fullFnDecl(node); | 318 | const fn_decl = t.ast.fullFnDecl(node); |
| 273 | const gop = try t.scope().symbols.getOrPut(t.gpa, fn_decl.id); | 319 | const gop = try t.scope().symbols.getOrPut(t.gpa, fn_decl.id); |
| 274 | if (gop.found_existing) { | 320 | if (gop.found_existing) { |
| 275 | return error.DuplicateFunction; | 321 | try t.report.err(.duplicate_function, .{ |
| 322 | .node = node, | ||
| 323 | .prev = gop.value_ptr.node, | ||
| 324 | }); | ||
| 325 | return; | ||
| 276 | } | 326 | } |
| 277 | gop.value_ptr.* = .{ | 327 | gop.value_ptr.* = .{ |
| 278 | .node = node, | 328 | .node = node, |
| ... | @@ -299,7 +349,7 @@ fn transformModuleStatement(t: *Transform, decl: BlockContext, node: NodeIndex) | ... | @@ -299,7 +349,7 @@ fn transformModuleStatement(t: *Transform, decl: BlockContext, node: NodeIndex) |
| 299 | } | 349 | } |
| 300 | } | 350 | } |
| 301 | 351 | ||
| 302 | fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) !Inst.Index { | 352 | fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) Allocator.Error!Inst.Index { |
| 303 | const trace = tracer.trace(@src()); | 353 | const trace = tracer.trace(@src()); |
| 304 | defer trace.end(); | 354 | defer trace.end(); |
| 305 | 355 | ||
| ... | @@ -350,6 +400,8 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) ! | ... | @@ -350,6 +400,8 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) ! |
| 350 | .extra_index = decl_extra, | 400 | .extra_index = decl_extra, |
| 351 | }); | 401 | }); |
| 352 | 402 | ||
| 403 | try t.scope().addUnusedWarnings(t); | ||
| 404 | |||
| 353 | return func; | 405 | return func; |
| 354 | } | 406 | } |
| 355 | 407 | ||
| ... | @@ -366,7 +418,7 @@ fn transformExpressionToBlock( | ... | @@ -366,7 +418,7 @@ fn transformExpressionToBlock( |
| 366 | decl: BlockContext, | 418 | decl: BlockContext, |
| 367 | node: NodeIndex, | 419 | node: NodeIndex, |
| 368 | comptime is_function: bool, | 420 | comptime is_function: bool, |
| 369 | ) anyerror!Inst.Ref { | 421 | ) Allocator.Error!Inst.Ref { |
| 370 | const trace = tracer.trace(@src()); | 422 | const trace = tracer.trace(@src()); |
| 371 | defer trace.end(); | 423 | defer trace.end(); |
| 372 | 424 | ||
| ... | @@ -461,7 +513,7 @@ fn transformExpressionToBlock( | ... | @@ -461,7 +513,7 @@ fn transformExpressionToBlock( |
| 461 | return ret_block.toRef(); | 513 | return ret_block.toRef(); |
| 462 | } | 514 | } |
| 463 | 515 | ||
| 464 | fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) !Inst.Ref { | 516 | fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) Allocator.Error!Inst.Ref { |
| 465 | const trace = tracer.trace(@src()); | 517 | const trace = tracer.trace(@src()); |
| 466 | defer trace.end(); | 518 | defer trace.end(); |
| 467 | 519 | ||
| ... | @@ -472,7 +524,11 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) !Inst | ... | @@ -472,7 +524,11 @@ fn transformParamDecl(t: *Transform, decl: BlockContext, param: NodeIndex) !Inst |
| 472 | // can use this param's value, if known at compile time. | 524 | // can use this param's value, if known at compile time. |
| 473 | const sym = try t.scope().symbols.getOrPut(t.gpa, param_decl.id); | 525 | const sym = try t.scope().symbols.getOrPut(t.gpa, param_decl.id); |
| 474 | if (sym.found_existing) { | 526 | if (sym.found_existing) { |
| 475 | return error.DuplicateParam; | 527 | try t.report.err(.duplicate_param, .{ |
| 528 | .node = param, | ||
| 529 | .prev = sym.value_ptr.node, | ||
| 530 | }); | ||
| 531 | return .void_value; | ||
| 476 | } | 532 | } |
| 477 | 533 | ||
| 478 | sym.value_ptr.* = .{ | 534 | sym.value_ptr.* = .{ |
| ... | @@ -501,7 +557,8 @@ const Builtin = enum { | ... | @@ -501,7 +557,8 @@ const Builtin = enum { |
| 501 | defer trace.end(); | 557 | defer trace.end(); |
| 502 | 558 | ||
| 503 | if (args.len != 1) { | 559 | if (args.len != 1) { |
| 504 | return error.InvalidBuiltin; | 560 | // try t.report.err(.invalid_builtin_args, .{ .id = "js" }); |
| 561 | return .void_value; | ||
| 505 | } | 562 | } |
| 506 | 563 | ||
| 507 | const source = try t.transformExpression(decl, args[0]) orelse return null; | 564 | const source = try t.transformExpression(decl, args[0]) orelse return null; |
| ... | @@ -518,7 +575,7 @@ const Builtin = enum { | ... | @@ -518,7 +575,7 @@ const Builtin = enum { |
| 518 | 575 | ||
| 519 | /// This is the primary transformation function: converting an AST expression into instructions. | 576 | /// This is the primary transformation function: converting an AST expression into instructions. |
| 520 | /// `null` represents a `return` or `break`, aka this branch of code is dead. | 577 | /// `null` represents a `return` or `break`, aka this branch of code is dead. |
| 521 | fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyerror!?Inst.Ref { | 578 | fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Allocator.Error!?Inst.Ref { |
| 522 | const node_tag = t.ast.nodes.items(.tag)[node]; | 579 | const node_tag = t.ast.nodes.items(.tag)[node]; |
| 523 | 580 | ||
| 524 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); | 581 | const trace = tracer.traceFmt(@src(), "{s}", .{@tagName(node_tag)}); |
| ... | @@ -537,17 +594,13 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer | ... | @@ -537,17 +594,13 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer |
| 537 | return ref; | 594 | return ref; |
| 538 | } | 595 | } |
| 539 | 596 | ||
| 540 | if (mem.eql(u8, id, "_")) { | ||
| 541 | return error.InvalidDiscard; | ||
| 542 | } | ||
| 543 | |||
| 544 | if (mem.startsWith(u8, id, "@")) { | 597 | if (mem.startsWith(u8, id, "@")) { |
| 545 | @panic("TODO: string identifiers"); | 598 | @panic("TODO: string identifiers"); |
| 546 | } | 599 | } |
| 547 | 600 | ||
| 548 | const lookup = t.lookupId(id) orelse { | 601 | const lookup = t.lookupId(id) orelse { |
| 549 | std.debug.print("lookupId failed for {s}\n", .{id}); | 602 | try t.report.err(.unknown_identifier, .{ .node = node }); |
| 550 | return error.UnknownIdentifier; | 603 | return .void_value; |
| 551 | }; | 604 | }; |
| 552 | 605 | ||
| 553 | return switch (lookup.data) { | 606 | return switch (lookup.data) { |
| ... | @@ -706,7 +759,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer | ... | @@ -706,7 +759,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer |
| 706 | 759 | ||
| 707 | .label => { | 760 | .label => { |
| 708 | const expr = t.ast.nodeData(node).label.expr; | 761 | const expr = t.ast.nodeData(node).label.expr; |
| 709 | const label = t.ast.tokenSlice(t.ast.nodes.items(.main_token)[node]); | 762 | const label = t.ast.tokenSlice(t.ast.nodeToken(node)); |
| 710 | 763 | ||
| 711 | var target: BlockContext.BreakTarget = .{ | 764 | var target: BlockContext.BreakTarget = .{ |
| 712 | .parent = decl.break_target, | 765 | .parent = decl.break_target, |
| ... | @@ -778,7 +831,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer | ... | @@ -778,7 +831,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer |
| 778 | .builtin_call, | 831 | .builtin_call, |
| 779 | => { | 832 | => { |
| 780 | const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse { | 833 | const builtin = Builtin.map.get(t.ast.basicNodeSlice(node)[1..]) orelse { |
| 781 | return error.InvalidBuiltin; | 834 | try t.report.err(.invalid_builtin, .{ .node = node }); |
| 835 | return .void_value; | ||
| 782 | }; | 836 | }; |
| 783 | 837 | ||
| 784 | const base = t.ast.nodeData(node).list; | 838 | const base = t.ast.nodeData(node).list; |
| ... | @@ -847,7 +901,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer | ... | @@ -847,7 +901,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer |
| 847 | const string_ref = try t.addInstRef(.{ | 901 | const string_ref = try t.addInstRef(.{ |
| 848 | .tag = .string, | 902 | .tag = .string, |
| 849 | .data = EncodedData.encode(.string, .{ | 903 | .data = EncodedData.encode(.string, .{ |
| 850 | .src_node = decl.nodeOffset(node), // TODO: this is incorrect, there is no node | 904 | .src_node = decl.nodeOffset(node), |
| 851 | .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str), | 905 | .extra_index = try t.extra.append(t.gpa, SlicedString, prop_name_str), |
| 852 | }), | 906 | }), |
| 853 | }); | 907 | }); |
| ... | @@ -915,7 +969,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer | ... | @@ -915,7 +969,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer |
| 915 | const ret_node = t.ast.nodeData(node).@"return"; | 969 | const ret_node = t.ast.nodeData(node).@"return"; |
| 916 | 970 | ||
| 917 | const ret_block_ptr = decl.return_target orelse { | 971 | const ret_block_ptr = decl.return_target orelse { |
| 918 | return error.InvalidReturn; | 972 | // return error.InvalidReturn; |
| 973 | @panic("TODO"); | ||
| 919 | }; | 974 | }; |
| 920 | 975 | ||
| 921 | const ret_block = if (ret_block_ptr.*) |block| | 976 | const ret_block = if (ret_block_ptr.*) |block| |
| ... | @@ -957,7 +1012,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer | ... | @@ -957,7 +1012,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) anyer |
| 957 | ); | 1012 | ); |
| 958 | 1013 | ||
| 959 | const block = try decl.findAndMarkBreak(t, label) orelse { | 1014 | const block = try decl.findAndMarkBreak(t, label) orelse { |
| 960 | return error.UnknownBreakTarget; | 1015 | try t.report.err(.unresolved_label, .{ .node = node }); |
| 1016 | return null; | ||
| 961 | }; | 1017 | }; |
| 962 | 1018 | ||
| 963 | const operand: Inst.Ref = if (brk_node.value.unwrap()) |expr| | 1019 | const operand: Inst.Ref = if (brk_node.value.unwrap()) |expr| |
| ... | @@ -1001,6 +1057,17 @@ fn transformBlockStatement( | ... | @@ -1001,6 +1057,17 @@ fn transformBlockStatement( |
| 1001 | 1057 | ||
| 1002 | .var_decl => { | 1058 | .var_decl => { |
| 1003 | const var_decl = t.ast.fullVarDecl(node); | 1059 | const var_decl = t.ast.fullVarDecl(node); |
| 1060 | |||
| 1061 | if (mem.eql(u8, var_decl.id, "_")) { | ||
| 1062 | try t.report.err(.invalid_discard, .{ .node = node }); | ||
| 1063 | return .void_value; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | if (BuiltInIds.has(var_decl.id)) { | ||
| 1067 | try t.report.err(.shadow_builtin_variable, .{ .node = node }); | ||
| 1068 | return .void_value; | ||
| 1069 | } | ||
| 1070 | |||
| 1004 | const type_ref = if (var_decl.type) |data_type| | 1071 | const type_ref = if (var_decl.type) |data_type| |
| 1005 | try t.transformExpression(decl, data_type) | 1072 | try t.transformExpression(decl, data_type) |
| 1006 | else | 1073 | else |
| ... | @@ -1040,7 +1107,7 @@ fn transformBlockStatement( | ... | @@ -1040,7 +1107,7 @@ fn transformBlockStatement( |
| 1040 | }, | 1107 | }, |
| 1041 | }); | 1108 | }); |
| 1042 | 1109 | ||
| 1043 | return mem_ref; | 1110 | return .void_value; |
| 1044 | }, | 1111 | }, |
| 1045 | 1112 | ||
| 1046 | .fn_decl, | 1113 | .fn_decl, |
| ... | @@ -1145,10 +1212,7 @@ fn getInstData(t: *Transform, index: Inst.Index) *EncodedData { | ... | @@ -1145,10 +1212,7 @@ fn getInstData(t: *Transform, index: Inst.Index) *EncodedData { |
| 1145 | } | 1212 | } |
| 1146 | 1213 | ||
| 1147 | fn addScratch(t: *Transform, comptime T: type, value: T) !void { | 1214 | fn addScratch(t: *Transform, comptime T: type, value: T) !void { |
| 1148 | try t.scratch.append(t.gpa, switch (T) { | 1215 | try t.scratch.append(t.gpa, @intFromEnum(value)); |
| 1149 | Inst.Ref, Inst.Index => @intFromEnum(value), | ||
| 1150 | else => @compileError("addScratch not implemented for " ++ @typeName(T)), | ||
| 1151 | }); | ||
| 1152 | } | 1216 | } |
| 1153 | 1217 | ||
| 1154 | fn scratchToRefList(t: *Transform, scratch_top: usize) !RefList { | 1218 | fn scratchToRefList(t: *Transform, scratch_top: usize) !RefList { |
| ... | @@ -1273,4 +1337,7 @@ const optionalExtraIndex = OptionalExtraIndex.init; | ... | @@ -1273,4 +1337,7 @@ const optionalExtraIndex = OptionalExtraIndex.init; |
| 1273 | 1337 | ||
| 1274 | const InternPool = @import("InternPool.zig"); | 1338 | const InternPool = @import("InternPool.zig"); |
| 1275 | 1339 | ||
| 1340 | const Report = @import("Report.zig"); | ||
| 1341 | const Comp = @import("Comp.zig"); | ||
| 1342 | |||
| 1276 | const tracer = @import("tracer"); | 1343 | const tracer = @import("tracer"); |
src/compiler/Value.zig-1| ... | @@ -32,7 +32,6 @@ pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen { | ... | @@ -32,7 +32,6 @@ pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen { |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | pub fn @"type"(val: Value, ip: *InternPool) Value { | 34 | pub fn @"type"(val: Value, ip: *InternPool) Value { |
| 35 | std.debug.print("typeOfValue {any}\n", .{val.toKey(ip)}); | ||
| 36 | const v: InternPool.Index = switch (val.toKey(ip)) { | 35 | const v: InternPool.Index = switch (val.toKey(ip)) { |
| 37 | .simple_type, .type_function => .type_type, | 36 | .simple_type, .type_function => .type_type, |
| 38 | 37 |
src/compiler/main.zig+23-3| ... | @@ -3,11 +3,15 @@ const Tokenizer = @import("Tokenizer.zig"); | ... | @@ -3,11 +3,15 @@ const Tokenizer = @import("Tokenizer.zig"); |
| 3 | const Parse = @import("Parse.zig"); | 3 | const Parse = @import("Parse.zig"); |
| 4 | const Transform = @import("Transform.zig"); | 4 | const Transform = @import("Transform.zig"); |
| 5 | const Comp = @import("Comp.zig"); | 5 | const Comp = @import("Comp.zig"); |
| 6 | 6 | const unicode_props = @import("unicode_props.zig"); | |
| 7 | const T = @import("tracer"); | 7 | const T = @import("tracer"); |
| 8 | pub const tracer_impl = @field(T, "none"); | 8 | pub const tracer_impl = @field(T, "none"); |
| 9 | 9 | ||
| 10 | pub const Report = @import("Report.zig"); | ||
| 11 | |||
| 10 | pub fn main() !void { | 12 | pub fn main() !void { |
| 13 | try unicode_props.init(); | ||
| 14 | |||
| 11 | std.debug.print("\r\x1b[K", .{}); | 15 | std.debug.print("\r\x1b[K", .{}); |
| 12 | 16 | ||
| 13 | try T.init(); | 17 | try T.init(); |
| ... | @@ -45,7 +49,14 @@ pub fn main() !void { | ... | @@ -45,7 +49,14 @@ pub fn main() !void { |
| 45 | defer alloc.free(exe_name); | 49 | defer alloc.free(exe_name); |
| 46 | 50 | ||
| 47 | const file_path = maybe_file_path orelse { | 51 | const file_path = maybe_file_path orelse { |
| 48 | std.debug.print("Usage: {s} <input file>\n", .{exe_name}); | 52 | std.debug.print("Usage: {s} [flags] <input file>\n", .{exe_name}); |
| 53 | std.debug.print( | ||
| 54 | // | 60 chars wrap point| | ||
| 55 | \\ | ||
| 56 | \\Flags: | ||
| 57 | \\ --dump-ir Dump intermediate representations. | ||
| 58 | \\ | ||
| 59 | , .{}); | ||
| 49 | std.process.exit(1); | 60 | std.process.exit(1); |
| 50 | }; | 61 | }; |
| 51 | 62 | ||
| ... | @@ -54,7 +65,16 @@ pub fn main() !void { | ... | @@ -54,7 +65,16 @@ pub fn main() !void { |
| 54 | defer comp.deinit(); | 65 | defer comp.deinit(); |
| 55 | const main_file = try comp.getFile(file_path); | 66 | const main_file = try comp.getFile(file_path); |
| 56 | 67 | ||
| 57 | const teg = try main_file.getTeg(comp.gpa); | 68 | var fail_report: Report = undefined; |
| 69 | const teg = main_file.getTeg(&comp, &fail_report) catch |err| switch (err) { | ||
| 70 | error.TransformFail => { | ||
| 71 | std.debug.print("Transform failed:\n", .{}); | ||
| 72 | try fail_report.dumpToStdErr(&comp); | ||
| 73 | std.process.exit(1); | ||
| 74 | }, | ||
| 75 | else => return err, | ||
| 76 | }; | ||
| 77 | |||
| 58 | { | 78 | { |
| 59 | const stderr = std.io.getStdErr().writer(); | 79 | const stderr = std.io.getStdErr().writer(); |
| 60 | try teg.dumpToWriter(stderr); | 80 | try teg.dumpToWriter(stderr); |
src/compiler/util.zig+2-2| ... | @@ -7,9 +7,9 @@ pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config { | ... | @@ -7,9 +7,9 @@ pub fn detectTTYFromWriter(writer: anytype) std.io.tty.Config { |
| 7 | return std.io.tty.detectConfig(writer.context); | 7 | return std.io.tty.detectConfig(writer.context); |
| 8 | } | 8 | } |
| 9 | if (@TypeOf(writer) == std.io.AnyWriter) { | 9 | if (@TypeOf(writer) == std.io.AnyWriter) { |
| 10 | const writeFn = comptime (std.fs.File{ .handle = undefined }) | 10 | const fileWriteFn = comptime (std.fs.File{ .handle = undefined }) |
| 11 | .writer().any().writeFn; | 11 | .writer().any().writeFn; |
| 12 | if (writer.writeFn == writeFn) { | 12 | if (writer.writeFn == fileWriteFn) { |
| 13 | const file: *const std.fs.File = @alignCast(@ptrCast(writer.context)); | 13 | const file: *const std.fs.File = @alignCast(@ptrCast(writer.context)); |
| 14 | return std.io.tty.detectConfig(file.*); | 14 | return std.io.tty.detectConfig(file.*); |
| 15 | } | 15 | } |