| author | |
| committer | |
| log | 4cd49040184a9da68e9584b71ac21f9e19723d59 |
| tree | b4e843e8e58dbed96158fb445cd5056eba0e5262 |
| parent | fa2de52f403657b87c1c7db26b6010bf1c019ad5 |
| signature | Commit is signed but in an unrecognized format. |
13 files changed, 537 insertions(+), 240 deletions(-)
CHANGELOG.md+7| ... | ... | @@ -1,3 +1,10 @@ |
| 1 | ## 0.1.2 | |
| 2 | ||
| 3 | - feat: parsing of assignments within expressions is disallowed now | |
| 4 | - feat: debug builds have `--report-trace` to print stack traces | |
| 5 | - fix: unused warnings now properly work | |
| 6 | - fix: empty files and functions no longer crash | |
| 7 | ||
| 1 | 8 | ## 0.1.1 |
| 2 | 9 | |
| 3 | 10 | - feat: `var` declarations / runtime memory |
build.zig+19-6| ... | ... | @@ -1,4 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Build = std.Build; | |
| 3 | const Target = std.Target; | |
| 4 | const OsVersion = Target.Query.OsVersion; | |
| 2 | 5 | |
| 3 | 6 | pub fn build(b: *std.Build) void { |
| 4 | 7 | b.reference_trace = 20; |
| ... | ... | @@ -28,6 +31,7 @@ pub fn build(b: *std.Build) void { |
| 28 | 31 | .root_source_file = b.path("src/compiler/main.zig"), |
| 29 | 32 | .target = target, |
| 30 | 33 | .optimize = optimize, |
| 34 | .link_libc = true, | |
| 31 | 35 | }); |
| 32 | 36 | b.installArtifact(exe); |
| 33 | 37 | |
| ... | ... | @@ -67,13 +71,17 @@ pub fn build(b: *std.Build) void { |
| 67 | 71 | // wasm_api.root_module.addImport("zg:PropsData", zg.module("PropsData")); |
| 68 | 72 | |
| 69 | 73 | const release = b.step("release", "Package releases"); |
| 74 | const linux_version: OsVersion = .{ .semver = .{ .major = 1, .minor = 0, .patch = 0 } }; | |
| 75 | const mac_version: OsVersion = .{ .semver = .{ .major = 11, .minor = 0, .patch = 0 } }; | |
| 76 | const win_version: OsVersion = .{ .windows = .win7 }; | |
| 77 | ||
| 70 | 78 | inline for (.{ |
| 71 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl }), | |
| 72 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl }), | |
| 73 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos }), | |
| 74 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .macos }), | |
| 75 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows }), | |
| 76 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .windows }), | |
| 79 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version }), | |
| 80 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl, .os_version_min = linux_version }), | |
| 81 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos, .os_version_min = mac_version }), | |
| 82 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .macos, .os_version_min = mac_version }), | |
| 83 | b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu }), | |
| 84 | b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .windows, .os_version_min = win_version, .abi = .gnu }), | |
| 77 | 85 | }) |cross_target| { |
| 78 | 86 | const name = std.fmt.allocPrint( |
| 79 | 87 | b.allocator, |
| ... | ... | @@ -84,19 +92,24 @@ pub fn build(b: *std.Build) void { |
| 84 | 92 | }, |
| 85 | 93 | ) catch @panic("OOM"); |
| 86 | 94 | |
| 95 | // std.log.info("{s}\n", .{cross_target.result.zigTriple(b.allocator) catch @panic("OOM")}); | |
| 96 | ||
| 87 | 97 | const cross_exe = b.addExecutable(.{ |
| 88 | 98 | .name = "compiler", |
| 89 | 99 | .root_source_file = b.path("src/compiler/main.zig"), |
| 90 | 100 | .target = cross_target, |
| 91 | 101 | .optimize = .ReleaseSafe, |
| 92 | 102 | .strip = true, |
| 103 | .link_libc = true, | |
| 93 | 104 | }); |
| 105 | cross_exe.root_module.addImport("build_options", opts_mod); | |
| 94 | 106 | cross_exe.root_module.addImport("zg:PropsData", zg.module("PropsData")); |
| 95 | 107 | cross_exe.root_module.addImport("tracer", tracer.module("tracer")); |
| 96 | 108 | |
| 97 | 109 | release.dependOn(&b.addInstallFile( |
| 98 | 110 | cross_exe.getEmittedBin(), |
| 99 | 111 | b.pathJoin(&.{ |
| 112 | "release", | |
| 100 | 113 | name, |
| 101 | 114 | std.mem.concat(b.allocator, u8, &.{ |
| 102 | 115 | "compiler", |
src/compiler/Ast.zig+1-76| ... | ... | @@ -40,48 +40,6 @@ tokens: Token.List.Slice, |
| 40 | 40 | nodes: Node.List.Slice, |
| 41 | 41 | extra: []const u32, |
| 42 | 42 | |
| 43 | const Parse = @import("Parse.zig"); | |
| 44 | pub fn parse( | |
| 45 | gpa: std.mem.Allocator, | |
| 46 | source: [:0]const u8, | |
| 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 | }; | |
| 64 | errdefer tokens.deinit(gpa); | |
| 65 | ||
| 66 | var parser = Parse.init(gpa, source, tokens); | |
| 67 | errdefer parser.deinit(); | |
| 68 | ||
| 69 | try parser.nodes.ensureUnusedCapacity( | |
| 70 | gpa, | |
| 71 | @max(1, @divFloor(tokens.len, 4)), | |
| 72 | ); | |
| 73 | ||
| 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 | }; | |
| 81 | ||
| 82 | return try parser.finalize(tokens); | |
| 83 | } | |
| 84 | ||
| 85 | 43 | pub const Node = struct { |
| 86 | 44 | tag: Tag, |
| 87 | 45 | main_token: TokenIndex, |
| ... | ... | @@ -645,41 +603,8 @@ pub fn nodeStatements(ast: *const Ast, node: NodeIndex) []const NodeIndex { |
| 645 | 603 | } |
| 646 | 604 | |
| 647 | 605 | pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenIndex) Location { |
| 648 | var loc = Location{ | |
| 649 | .line = 1, | |
| 650 | .column = 1, | |
| 651 | .line_start = start_offset, | |
| 652 | .line_end = self.utf8_source.len, | |
| 653 | }; | |
| 654 | 606 | const token_start = self.tokens.items(.offset)[token_index]; |
| 655 | ||
| 656 | // Scan to by line until we go past the token start | |
| 657 | while (std.mem.indexOfScalarPos(u8, self.utf8_source, loc.line_start, '\n')) |i| { | |
| 658 | if (i >= token_start) { | |
| 659 | break; // Went past | |
| 660 | } | |
| 661 | loc.line += 1; | |
| 662 | loc.line_start = i + 1; | |
| 663 | } | |
| 664 | ||
| 665 | const offset = loc.line_start; | |
| 666 | for (self.utf8_source[offset..], 0..) |c, i| { | |
| 667 | if (i + offset == token_start) { | |
| 668 | loc.line_end = i + offset; | |
| 669 | while (loc.line_end < self.utf8_source.len and self.utf8_source[loc.line_end] != '\n') { | |
| 670 | loc.line_end += 1; | |
| 671 | } | |
| 672 | return loc; | |
| 673 | } | |
| 674 | if (c == '\n') { | |
| 675 | loc.line += 1; | |
| 676 | loc.column = 1; | |
| 677 | loc.line_start = i + 1; | |
| 678 | } else { | |
| 679 | loc.column += 1; | |
| 680 | } | |
| 681 | } | |
| 682 | return loc; | |
| 607 | return Tokenizer.tokenLocation(self.utf8_source, start_offset, token_start); | |
| 683 | 608 | } |
| 684 | 609 | |
| 685 | 610 | pub fn tokenSlice(ast: Ast, token_index: TokenIndex) []const u8 { |
src/compiler/Comp.zig+4-4| ... | ... | @@ -129,16 +129,16 @@ pub const File = struct { |
| 129 | 129 | return ast; |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | var ast_err: Parse.AstError = undefined; | |
| 133 | const ast = Ast.parse( | |
| 132 | var err_data: Parse.AstError.WithTrace = undefined; | |
| 133 | const ast = Parse.parse( | |
| 134 | 134 | comp.gpa, |
| 135 | 135 | (try file.getSource(comp.gpa)).code, |
| 136 | &ast_err, | |
| 136 | &err_data, | |
| 137 | 137 | ) catch |err| switch (err) { |
| 138 | 138 | error.OutOfMemory => return err, |
| 139 | 139 | error.ParseFail => { |
| 140 | var report = try ast_err.toReport(comp, file); | |
| 141 | 140 | // TODO: let this merge with other report |
| 141 | var report = try err_data.toReport(comp, file); | |
| 142 | 142 | report.dumpToStdErr(comp) catch {}; |
| 143 | 143 | report.deinit(comp.gpa); |
| 144 | 144 | return err; |
src/compiler/JsLink.zig+6-2| ... | ... | @@ -69,7 +69,7 @@ pub fn linkJsCode( |
| 69 | 69 | try link.decls.putNoClobber(comp.gpa, decl_index, @enumFromInt(gop.index)); |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | try writer.print("// Language Compiler v0.1.1. Alpha quality software.\n", .{}); | |
| 72 | try writer.print("// Language Compiler v0.1.2. Alpha quality software.\n", .{}); | |
| 73 | 73 | |
| 74 | 74 | // 2. Print every function decl |
| 75 | 75 | for (decls[1..]) |decl_index| { |
| ... | ... | @@ -77,7 +77,11 @@ pub fn linkJsCode( |
| 77 | 77 | } |
| 78 | 78 | |
| 79 | 79 | // 3. Print the top-level code |
| 80 | try link.printDeclContents(&root_decl.codegen.?, writer); | |
| 80 | if (root_decl.codegen.?.nodes.len > 1) { | |
| 81 | try link.printDeclContents(&root_decl.codegen.?, writer); | |
| 82 | } else { | |
| 83 | try writer.print("// (program has no observable effects, thus an empty file)\n", .{}); | |
| 84 | } | |
| 81 | 85 | } |
| 82 | 86 | |
| 83 | 87 | fn deinit(link: *JsLink) void { |
src/compiler/JsNodeGen.zig+11-9| ... | ... | @@ -107,16 +107,18 @@ pub fn build(c: *JsNodeGen) !void { |
| 107 | 107 | |
| 108 | 108 | _ = try c.add(.root, undefined); |
| 109 | 109 | |
| 110 | for (statements[0..c.param_count], 1..) |stmt, expected_id| { | |
| 111 | assert(@intFromEnum(stmt) == expected_id); | |
| 112 | assert(c.air.instTag(stmt) == .arg); | |
| 113 | c.air_node_map[@intFromEnum(stmt)] = .{ | |
| 114 | .node = try c.add(.local, .{ .id = @intFromEnum(stmt) - 1 }), | |
| 115 | }; | |
| 116 | } | |
| 110 | if (statements.len > 0) { | |
| 111 | for (statements[0..c.param_count], 1..) |stmt, expected_id| { | |
| 112 | assert(@intFromEnum(stmt) == expected_id); | |
| 113 | assert(c.air.instTag(stmt) == .arg); | |
| 114 | c.air_node_map[@intFromEnum(stmt)] = .{ | |
| 115 | .node = try c.add(.local, .{ .id = @intFromEnum(stmt) - 1 }), | |
| 116 | }; | |
| 117 | } | |
| 117 | 118 | |
| 118 | for (statements[c.param_count..]) |stmt| { | |
| 119 | try c.codeGenInst(stmt); | |
| 119 | for (statements[c.param_count..]) |stmt| { | |
| 120 | try c.codeGenInst(stmt); | |
| 121 | } | |
| 120 | 122 | } |
| 121 | 123 | |
| 122 | 124 | const items = c.scratch.items[scratch_top..]; |
src/compiler/Parse.zig+150-42| ... | ... | @@ -10,9 +10,49 @@ nodes: Node.List = .{}, |
| 10 | 10 | extra: ArrayListUnmanaged(u32) = .{}, |
| 11 | 11 | scratch: ArrayListUnmanaged(NodeIndex) = .{}, |
| 12 | 12 | |
| 13 | ast_error: ?AstError = null, | |
| 13 | ast_error: ?AstError.WithTrace = null, | |
| 14 | ||
| 15 | pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8, ast_error: *AstError.WithTrace) Parse.Error!Ast { | |
| 16 | var err_byte: ByteOffset = 0; | |
| 17 | var tokens = Tokenizer.tokenize( | |
| 18 | source, | |
| 19 | gpa, | |
| 20 | &err_byte, | |
| 21 | ) catch |err| switch (err) { | |
| 22 | error.OutOfMemory => return error.OutOfMemory, | |
| 23 | else => { | |
| 24 | ast_error.* = .{ | |
| 25 | .err = .{ .tokenization = .{ | |
| 26 | .err = err, | |
| 27 | .byte = err_byte, | |
| 28 | } }, | |
| 29 | .trace = Report.DebugStoredTrace.from(@errorReturnTrace()), | |
| 30 | }; | |
| 31 | return error.ParseFail; | |
| 32 | }, | |
| 33 | }; | |
| 34 | errdefer tokens.deinit(gpa); | |
| 35 | ||
| 36 | var parser = Parse.init(gpa, source, tokens); | |
| 37 | errdefer parser.deinit(); | |
| 38 | ||
| 39 | try parser.nodes.ensureUnusedCapacity( | |
| 40 | gpa, | |
| 41 | @max(1, @divFloor(tokens.len, 4)), | |
| 42 | ); | |
| 43 | ||
| 44 | parser.parseRoot() catch |err| switch (err) { | |
| 45 | error.OutOfMemory => return error.OutOfMemory, | |
| 46 | error.ParseFail => { | |
| 47 | ast_error.* = parser.ast_error.?; | |
| 48 | return error.ParseFail; | |
| 49 | }, | |
| 50 | }; | |
| 51 | ||
| 52 | return try parser.finalize(tokens); | |
| 53 | } | |
| 14 | 54 | |
| 15 | pub fn init(gpa: Allocator, source: [:0]const u8, tokens: Token.List.Slice) Parse { | |
| 55 | fn init(gpa: Allocator, source: [:0]const u8, tokens: Token.List.Slice) Parse { | |
| 16 | 56 | return Parse{ |
| 17 | 57 | .gpa = gpa, |
| 18 | 58 | .source = source, |
| ... | ... | @@ -22,7 +62,7 @@ pub fn init(gpa: Allocator, source: [:0]const u8, tokens: Token.List.Slice) Pars |
| 22 | 62 | } |
| 23 | 63 | |
| 24 | 64 | /// This transfers the parse result into an Ast |
| 25 | pub fn finalize(p: *Parse, token_slice: Token.List.Slice) !Ast { | |
| 65 | fn finalize(p: *Parse, token_slice: Token.List.Slice) !Ast { | |
| 26 | 66 | const extra = try p.extra.toOwnedSlice(p.gpa); |
| 27 | 67 | defer p.* = undefined; |
| 28 | 68 | assert(token_slice.items(.tag).ptr == p.token_tags.ptr); |
| ... | ... | @@ -40,7 +80,7 @@ pub fn finalize(p: *Parse, token_slice: Token.List.Slice) !Ast { |
| 40 | 80 | } |
| 41 | 81 | |
| 42 | 82 | /// This discards all parser work |
| 43 | pub fn deinit(p: *Parse) void { | |
| 83 | fn deinit(p: *Parse) void { | |
| 44 | 84 | defer p.* = undefined; |
| 45 | 85 | p.nodes.deinit(p.gpa); |
| 46 | 86 | p.extra.deinit(p.gpa); |
| ... | ... | @@ -52,19 +92,46 @@ pub const Error = error{ |
| 52 | 92 | ParseFail, |
| 53 | 93 | }; |
| 54 | 94 | |
| 95 | pub const ExpectedContext = enum { | |
| 96 | param, | |
| 97 | argument, | |
| 98 | statement, | |
| 99 | expression, | |
| 100 | else_clause, | |
| 101 | ||
| 102 | pub fn description(tag: ExpectedContext) []const u8 { | |
| 103 | return switch (tag) { | |
| 104 | .param => "a parameter", | |
| 105 | .argument => "an argument", | |
| 106 | .statement => "a statement", | |
| 107 | .expression => "an expression", | |
| 108 | .else_clause => "an else clause", | |
| 109 | }; | |
| 110 | } | |
| 111 | }; | |
| 112 | ||
| 55 | 113 | /// When error.SyntaxError is returned, `p.err` is set to this as well as `p.pos` |
| 114 | /// When printing these, token list and Ast is not actually available, so full tokens | |
| 115 | /// are stored in the errors. | |
| 56 | 116 | pub const AstError = union(enum) { |
| 117 | /// Failure during tokenization | |
| 57 | 118 | tokenization: struct { |
| 58 | 119 | err: Tokenizer.TokenizeError, |
| 59 | byte: u32, | |
| 120 | byte: ByteOffset, | |
| 121 | }, | |
| 122 | /// Unexpected data, expected a token. | |
| 123 | unexpected_token: struct { | |
| 124 | expected: Token.Tag, | |
| 125 | got: Token, | |
| 126 | }, | |
| 127 | /// Unexpected data, expected <ExpectedContext> | |
| 128 | unexpected_context: struct { | |
| 129 | expected: ExpectedContext, | |
| 130 | got: Token, | |
| 60 | 131 | }, |
| 61 | expected_token: Token.Tag, | |
| 62 | expected_statement, | |
| 63 | expected_expr, | |
| 64 | expected_else_value, | |
| 65 | 132 | |
| 66 | pub fn toReport(parse_err: AstError, comp: *Comp, file: *Comp.File) !Report { | |
| 67 | return (Report.Err{ .ast = parse_err }).toReport(comp, file); | |
| 133 | pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report { | |
| 134 | return (Report.Err{ .ast = err }).toReport(comp, file, trace); | |
| 68 | 135 | } |
| 69 | 136 | |
| 70 | 137 | pub fn build(err: AstError, ctx: *Report.MsgContext) !void { |
| ... | ... | @@ -86,11 +153,29 @@ pub const AstError = union(enum) { |
| 86 | 153 | error.GitConflict => "git merge conflict encountered", |
| 87 | 154 | }}); |
| 88 | 155 | }, |
| 89 | ||
| 90 | else => unreachable, | |
| 91 | // | |
| 156 | .unexpected_token => |data| { | |
| 157 | try ctx.msg(.{ .token_stored = data.got }, "expected {s}, but found {s}", .{ | |
| 158 | data.expected.description(), | |
| 159 | data.got.tag.description(), | |
| 160 | }); | |
| 161 | }, | |
| 162 | .unexpected_context => |data| { | |
| 163 | try ctx.msg(.{ .token_stored = data.got }, "expected {s}, but found {s}", .{ | |
| 164 | data.expected.description(), | |
| 165 | data.got.tag.description(), | |
| 166 | }); | |
| 167 | }, | |
| 92 | 168 | } |
| 93 | 169 | } |
| 170 | ||
| 171 | pub const WithTrace = struct { | |
| 172 | err: AstError, | |
| 173 | trace: Report.DebugStoredTrace, | |
| 174 | ||
| 175 | pub fn toReport(err: WithTrace, comp: *Comp, file: *Comp.File) !Report { | |
| 176 | return err.err.toReport(comp, file, err.trace); | |
| 177 | } | |
| 178 | }; | |
| 94 | 179 | }; |
| 95 | 180 | |
| 96 | 181 | pub fn parseRoot(p: *Parse) Error!void { |
| ... | ... | @@ -141,10 +226,10 @@ fn parseModuleMembers(p: *Parse) Error!SubList { |
| 141 | 226 | |
| 142 | 227 | else => { |
| 143 | 228 | if (!p.isExpressionStart()) { |
| 144 | return p.fail(.expected_statement); | |
| 229 | return p.failUnexpectedToken(.statement); | |
| 145 | 230 | } |
| 146 | 231 | |
| 147 | try p.scratch.append(p.gpa, try p.parseExpression()); | |
| 232 | try p.scratch.append(p.gpa, try p.parseExpression(true)); | |
| 148 | 233 | _ = try p.expect(.semi); |
| 149 | 234 | }, |
| 150 | 235 | }; |
| ... | ... | @@ -180,7 +265,7 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex { |
| 180 | 265 | const params = try p.parseParamList(); |
| 181 | 266 | |
| 182 | 267 | const return_type = if (p.eatToken(.minus_lt)) |_| |
| 183 | try p.parseExpression() | |
| 268 | try p.parseExpression(false) | |
| 184 | 269 | else |
| 185 | 270 | null; |
| 186 | 271 | |
| ... | ... | @@ -222,7 +307,7 @@ fn parseParamList(p: *Parse) Error!SubList { |
| 222 | 307 | _ = p.nextToken(); |
| 223 | 308 | }, |
| 224 | 309 | .end_paren => break, |
| 225 | else => return p.fail(.{ .expected_token = .comma }), | |
| 310 | else => return p.failUnexpectedToken(.param), | |
| 226 | 311 | } |
| 227 | 312 | } |
| 228 | 313 | |
| ... | ... | @@ -239,13 +324,13 @@ fn parseParamDecl(p: *Parse) Error!NodeIndex { |
| 239 | 324 | |
| 240 | 325 | _ = try p.expect(.colon); |
| 241 | 326 | |
| 242 | const @"type" = try p.parseExpression(); | |
| 327 | const ty = try p.parseExpression(false); | |
| 243 | 328 | |
| 244 | 329 | return try p.addNode(.{ |
| 245 | 330 | .tag = .param_decl, |
| 246 | 331 | .main_token = main_token, |
| 247 | 332 | .data = .{ .param_decl = .{ |
| 248 | .type = @"type", | |
| 333 | .type = ty, | |
| 249 | 334 | } }, |
| 250 | 335 | }); |
| 251 | 336 | } |
| ... | ... | @@ -274,7 +359,7 @@ fn parseLabelExpression(p: *Parse) Error!NodeIndex { |
| 274 | 359 | |
| 275 | 360 | const main_token = p.assertToken(.id); |
| 276 | 361 | _ = try p.expect(.colon); |
| 277 | const expr = try p.parseExpression(); | |
| 362 | const expr = try p.parseExpression(false); | |
| 278 | 363 | return try p.addNode(.{ |
| 279 | 364 | .tag = .label, |
| 280 | 365 | .main_token = main_token, |
| ... | ... | @@ -301,12 +386,12 @@ fn parseBlockContents(p: *Parse) Error!struct { Node.Tag, SubList } { |
| 301 | 386 | |
| 302 | 387 | else => { |
| 303 | 388 | if (!p.isExpressionStart()) { |
| 304 | return p.fail(.expected_statement); | |
| 389 | return p.failUnexpectedToken(.statement); | |
| 305 | 390 | } |
| 306 | 391 | |
| 307 | 392 | const first_tag = p.currentTag(); |
| 308 | 393 | |
| 309 | try p.scratch.append(p.gpa, try p.parseExpression()); | |
| 394 | try p.scratch.append(p.gpa, try p.parseExpression(true)); | |
| 310 | 395 | |
| 311 | 396 | switch (first_tag) { |
| 312 | 397 | // some control flow and blocks are allowed to not have a |
| ... | ... | @@ -352,13 +437,13 @@ fn parseVarDecl(p: *Parse) Error!NodeIndex { |
| 352 | 437 | _ = try p.expect(.id); |
| 353 | 438 | |
| 354 | 439 | const data_type: OptionalNodeIndex = if (p.eatToken(.colon)) |_| |
| 355 | optionalNodeIndex(try p.parseExpression()) | |
| 440 | optionalNodeIndex(try p.parseExpression(false)) | |
| 356 | 441 | else |
| 357 | 442 | .none; |
| 358 | 443 | |
| 359 | 444 | _ = try p.expect(.equal); |
| 360 | 445 | |
| 361 | const data_init = try p.parseExpression(); | |
| 446 | const data_init = try p.parseExpression(true); | |
| 362 | 447 | |
| 363 | 448 | _ = try p.expect(.semi); |
| 364 | 449 | |
| ... | ... | @@ -412,6 +497,7 @@ const Operator = enum(u8) { |
| 412 | 497 | le = @intFromEnum(Token.Tag.lt_equal), |
| 413 | 498 | gt = @intFromEnum(Token.Tag.gt), |
| 414 | 499 | ge = @intFromEnum(Token.Tag.gt_equal), |
| 500 | // Following operators are assignment operators | |
| 415 | 501 | assign = @intFromEnum(Token.Tag.equal), |
| 416 | 502 | add_assign = @intFromEnum(Token.Tag.plus_equal), |
| 417 | 503 | sub_assign = @intFromEnum(Token.Tag.minus_equal), |
| ... | ... | @@ -551,9 +637,13 @@ const Operator = enum(u8) { |
| 551 | 637 | assert(tag != .root); // invalid operator or lut construction |
| 552 | 638 | return tag; |
| 553 | 639 | } |
| 640 | ||
| 641 | pub fn isAssign(op: Operator) bool { | |
| 642 | return @intFromEnum(op) >= @intFromEnum(Operator.assign); | |
| 643 | } | |
| 554 | 644 | }; |
| 555 | 645 | |
| 556 | fn parseExpression(p: *Parse) Error!NodeIndex { | |
| 646 | fn parseExpression(p: *Parse, allow_assign_operators: bool) Error!NodeIndex { | |
| 557 | 647 | const trace = t.trace(@src()); |
| 558 | 648 | defer trace.end(); |
| 559 | 649 | |
| ... | ... | @@ -569,7 +659,8 @@ fn parseExpression(p: *Parse) Error!NodeIndex { |
| 569 | 659 | var expr = try p.parseExpressionPart(); |
| 570 | 660 | |
| 571 | 661 | while (Operator.fromToken(p.currentTag())) |op| { |
| 572 | expr = try p.parseBinarySuffix(expr, op); | |
| 662 | if (!allow_assign_operators and op.isAssign()) break; | |
| 663 | expr = try p.parseBinarySuffix(expr, op, allow_assign_operators); | |
| 573 | 664 | } |
| 574 | 665 | |
| 575 | 666 | return expr; |
| ... | ... | @@ -616,7 +707,7 @@ fn parseExpressionPart(p: *Parse) Error!NodeIndex { |
| 616 | 707 | }); |
| 617 | 708 | }, |
| 618 | 709 | |
| 619 | else => return p.fail(.expected_expr), | |
| 710 | else => return p.failUnexpectedToken(.expression), | |
| 620 | 711 | }; |
| 621 | 712 | |
| 622 | 713 | // parse field, calls, and index expressions |
| ... | ... | @@ -657,7 +748,7 @@ fn parseExpressionPart(p: *Parse) Error!NodeIndex { |
| 657 | 748 | }, |
| 658 | 749 | .start_bracket => { |
| 659 | 750 | const main_token = p.nextToken(); |
| 660 | const index = try p.parseExpression(); | |
| 751 | const index = try p.parseExpression(false); | |
| 661 | 752 | |
| 662 | 753 | expr = try p.addNode(.{ |
| 663 | 754 | .tag = .index, |
| ... | ... | @@ -676,7 +767,7 @@ fn parseExpressionPart(p: *Parse) Error!NodeIndex { |
| 676 | 767 | return expr; |
| 677 | 768 | } |
| 678 | 769 | |
| 679 | fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) Error!NodeIndex { | |
| 770 | fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator, allow_assign_operators: bool) Error!NodeIndex { | |
| 680 | 771 | const trace = t.trace(@src()); |
| 681 | 772 | defer trace.end(); |
| 682 | 773 | |
| ... | ... | @@ -685,6 +776,8 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) Error!NodeIndex { |
| 685 | 776 | var rhs = try p.parseExpressionPart(); |
| 686 | 777 | |
| 687 | 778 | while (Operator.fromToken(p.currentTag())) |next_op| { |
| 779 | if (!allow_assign_operators and next_op.isAssign()) break; | |
| 780 | ||
| 688 | 781 | const new_level = next_op.level(); |
| 689 | 782 | const use = switch (op.direction()) { |
| 690 | 783 | .ltr => @intFromEnum(new_level) < @intFromEnum(level), |
| ... | ... | @@ -692,7 +785,7 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) Error!NodeIndex { |
| 692 | 785 | }; |
| 693 | 786 | if (!use) break; |
| 694 | 787 | |
| 695 | rhs = try p.parseBinarySuffix(rhs, next_op); | |
| 788 | rhs = try p.parseBinarySuffix(rhs, next_op, allow_assign_operators); | |
| 696 | 789 | } |
| 697 | 790 | |
| 698 | 791 | return try p.addNode(.{ |
| ... | ... | @@ -712,7 +805,7 @@ fn parseReturn(p: *Parse) Error!NodeIndex { |
| 712 | 805 | const main_token = p.assertToken(.kw_return); |
| 713 | 806 | |
| 714 | 807 | const expr = if (p.isExpressionStart()) |
| 715 | try p.parseExpression() | |
| 808 | try p.parseExpression(false) | |
| 716 | 809 | else |
| 717 | 810 | null; |
| 718 | 811 | |
| ... | ... | @@ -737,7 +830,7 @@ fn parseBreak(p: *Parse) Error!NodeIndex { |
| 737 | 830 | null; |
| 738 | 831 | |
| 739 | 832 | const expr = if (p.isExpressionStart()) |
| 740 | try p.parseExpression() | |
| 833 | try p.parseExpression(false) | |
| 741 | 834 | else |
| 742 | 835 | null; |
| 743 | 836 | |
| ... | ... | @@ -776,7 +869,7 @@ fn parseCallArgs(p: *Parse) Error!SubList { |
| 776 | 869 | defer p.scratch.shrinkRetainingCapacity(scratch_top); |
| 777 | 870 | |
| 778 | 871 | while (p.isExpressionStart()) { |
| 779 | try p.scratch.append(p.gpa, try p.parseExpression()); | |
| 872 | try p.scratch.append(p.gpa, try p.parseExpression(false)); | |
| 780 | 873 | |
| 781 | 874 | switch (p.currentTag()) { |
| 782 | 875 | .end_paren => { |
| ... | ... | @@ -787,7 +880,7 @@ fn parseCallArgs(p: *Parse) Error!SubList { |
| 787 | 880 | if (p.currentTag() == .end_paren) |
| 788 | 881 | break; |
| 789 | 882 | }, |
| 790 | else => return p.fail(.{ .expected_token = .end_paren }), | |
| 883 | else => return p.failUnexpectedToken(.argument), | |
| 791 | 884 | } |
| 792 | 885 | } |
| 793 | 886 | |
| ... | ... | @@ -802,7 +895,7 @@ fn parseIf(p: *Parse) Error!NodeIndex { |
| 802 | 895 | |
| 803 | 896 | const main_token = p.assertToken(.kw_if); |
| 804 | 897 | |
| 805 | const cond = try p.parseExpression(); | |
| 898 | const cond = try p.parseExpression(false); | |
| 806 | 899 | |
| 807 | 900 | _ = try p.expectWithoutAdvance(.start_brace); |
| 808 | 901 | const yes = try p.parseBlock(); |
| ... | ... | @@ -838,7 +931,7 @@ fn parseElseValue(p: *Parse) Error!NodeIndex { |
| 838 | 931 | .kw_if => p.parseIf(), |
| 839 | 932 | .id => p.parseLabelExpression(), |
| 840 | 933 | |
| 841 | else => return p.fail(.expected_else_value), | |
| 934 | else => return p.failUnexpectedToken(.else_clause), | |
| 842 | 935 | }; |
| 843 | 936 | } |
| 844 | 937 | |
| ... | ... | @@ -848,7 +941,7 @@ fn parseGroup(p: *Parse) Error!NodeIndex { |
| 848 | 941 | |
| 849 | 942 | const main_token = p.assertToken(.start_paren); |
| 850 | 943 | |
| 851 | const expr = try p.parseExpression(); | |
| 944 | const expr = try p.parseExpression(false); | |
| 852 | 945 | |
| 853 | 946 | _ = try p.expect(.end_paren); |
| 854 | 947 | |
| ... | ... | @@ -885,11 +978,20 @@ fn addExtra(p: *Parse, comptime T: type, extra: T) !ExtraIndex { |
| 885 | 978 | } |
| 886 | 979 | |
| 887 | 980 | fn fail(p: *Parse, ast_error: AstError) Error { |
| 888 | p.ast_error = ast_error; | |
| 889 | std.debug.print("got {s}", .{@tagName(p.currentTag())}); | |
| 981 | p.ast_error = .{ | |
| 982 | .err = ast_error, | |
| 983 | .trace = Report.DebugStoredTrace.capture(@returnAddress()), | |
| 984 | }; | |
| 890 | 985 | return Error.ParseFail; |
| 891 | 986 | } |
| 892 | 987 | |
| 988 | fn failUnexpectedToken(p: *Parse, kind: ExpectedContext) Error { | |
| 989 | return p.fail(.{ .unexpected_context = .{ | |
| 990 | .got = p.fullToken(p.pos), | |
| 991 | .expected = kind, | |
| 992 | } }); | |
| 993 | } | |
| 994 | ||
| 893 | 995 | fn blankNode(p: *Parse, comptime tag: Token.Tag) !NodeIndex { |
| 894 | 996 | const main_token = p.assertToken(tag); |
| 895 | 997 | return try p.addNode(.{ |
| ... | ... | @@ -917,14 +1019,20 @@ fn eatToken(p: *Parse, tag: Token.Tag) ?TokenIndex { |
| 917 | 1019 | |
| 918 | 1020 | fn expect(p: *Parse, tag: Token.Tag) !TokenIndex { |
| 919 | 1021 | if (p.currentTag() != tag) { |
| 920 | return p.fail(.{ .expected_token = tag }); | |
| 1022 | return p.fail(.{ .unexpected_token = .{ | |
| 1023 | .got = p.fullToken(p.pos), | |
| 1024 | .expected = tag, | |
| 1025 | } }); | |
| 921 | 1026 | } |
| 922 | 1027 | return p.nextToken(); |
| 923 | 1028 | } |
| 924 | 1029 | |
| 925 | 1030 | fn expectWithoutAdvance(p: *Parse, tag: Token.Tag) !void { |
| 926 | 1031 | if (p.currentTag() != tag) { |
| 927 | return p.fail(.{ .expected_token = tag }); | |
| 1032 | return p.fail(.{ .unexpected_token = .{ | |
| 1033 | .got = p.fullToken(p.pos), | |
| 1034 | .expected = tag, | |
| 1035 | } }); | |
| 928 | 1036 | } |
| 929 | 1037 | } |
| 930 | 1038 |
src/compiler/Report.zig+137-19| ... | ... | @@ -9,27 +9,89 @@ errors: []Item = &.{}, |
| 9 | 9 | warnings: []Item = &.{}, |
| 10 | 10 | string_bytes: []u8 = &.{}, |
| 11 | 11 | |
| 12 | pub var debug_show_report_trace: if (builtin.mode == .Debug) bool else void = | |
| 13 | if (builtin.mode == .Debug) false; | |
| 14 | ||
| 12 | 15 | pub const Item = struct { |
| 13 | 16 | /// What is this diagnostic |
| 14 | 17 | data: union { @"error": Err, warn: Warn }, |
| 15 | 18 | /// What file was it in |
| 16 | 19 | url: String, |
| 17 | 20 | |
| 21 | debug_stack_trace: DebugStoredTrace, | |
| 22 | ||
| 18 | 23 | /// Type is never stored, inferred from which list it is in. |
| 19 | 24 | pub const Type = enum { @"error", warn }; |
| 20 | 25 | }; |
| 21 | 26 | |
| 27 | /// A variant of std.builtin.StackTrace that stores it's data within itself | |
| 28 | /// instead of being a pointer. This allows capturing the stack trace and | |
| 29 | /// saving it for later printing. | |
| 30 | pub const StoredTrace = struct { | |
| 31 | data: [32]usize, | |
| 32 | index: usize, | |
| 33 | ||
| 34 | pub const empty = .{ | |
| 35 | .data = .{0} ** 32, | |
| 36 | .index = 0, | |
| 37 | }; | |
| 38 | ||
| 39 | fn trace(stored: *StoredTrace) std.builtin.StackTrace { | |
| 40 | return .{ | |
| 41 | .index = stored.index, | |
| 42 | .instruction_addresses = &stored.data, | |
| 43 | }; | |
| 44 | } | |
| 45 | ||
| 46 | pub fn capture(begin: ?usize) StoredTrace { | |
| 47 | var stored: StoredTrace = undefined; | |
| 48 | var frame = stored.trace(); | |
| 49 | std.debug.captureStackTrace(begin orelse @returnAddress(), &frame); | |
| 50 | stored.index = frame.index; | |
| 51 | return stored; | |
| 52 | } | |
| 53 | ||
| 54 | pub fn from(stack_trace: ?*std.builtin.StackTrace) StoredTrace { | |
| 55 | if (stack_trace) |stack| { | |
| 56 | var data: [32]usize = undefined; | |
| 57 | @memset(&data, 0); | |
| 58 | const items = @min(stack.instruction_addresses.len, 32); | |
| 59 | @memcpy(data[0..items], stack.instruction_addresses[0..items]); | |
| 60 | return .{ | |
| 61 | .data = data, | |
| 62 | .index = @min(items, stack.index), | |
| 63 | }; | |
| 64 | } else { | |
| 65 | return empty; | |
| 66 | } | |
| 67 | } | |
| 68 | }; | |
| 69 | ||
| 70 | pub const DebugStoredTrace = if (builtin.mode == .Debug) | |
| 71 | StoredTrace | |
| 72 | else | |
| 73 | struct { | |
| 74 | pub fn capture() @This() { | |
| 75 | return .{}; | |
| 76 | } | |
| 77 | ||
| 78 | pub fn from(_: ?*std.builtin.StackTrace) @This() { | |
| 79 | return .{}; | |
| 80 | } | |
| 81 | }; | |
| 82 | ||
| 22 | 83 | pub const Err = union(enum) { |
| 23 | 84 | pub const FieldEnum = std.meta.FieldEnum(Err); |
| 24 | 85 | |
| 25 | 86 | /// Constructs a `Report` with just a single error. |
| 26 | pub fn toReport(err: Err, comp: *Comp, file: *File) !Report { | |
| 87 | pub fn toReport(err: Err, comp: *Comp, file: *File, trace: DebugStoredTrace) !Report { | |
| 27 | 88 | const string_bytes = try comp.gpa.dupe(u8, file.file_path); |
| 28 | 89 | errdefer comp.gpa.free(string_bytes); |
| 29 | 90 | |
| 30 | 91 | const errors = try comp.gpa.dupe(Item, &.{.{ |
| 31 | 92 | .data = .{ .@"error" = err }, |
| 32 | 93 | .url = .{ .start = 0, .len = @intCast(string_bytes.len) }, |
| 94 | .debug_stack_trace = trace, | |
| 33 | 95 | }}); |
| 34 | 96 | errdefer comp.gpa.free(errors); |
| 35 | 97 | |
| ... | ... | @@ -42,7 +104,7 @@ pub const Err = union(enum) { |
| 42 | 104 | node: NodeIndex, |
| 43 | 105 | |
| 44 | 106 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 45 | try ctx.msgNode(data.node, "unknown identifier '{s}'", .{ | |
| 107 | try ctx.msgNode(data.node, "unknown identifier \"{s}\"", .{ | |
| 46 | 108 | ctx.ast.basicNodeSlice(data.node), |
| 47 | 109 | }); |
| 48 | 110 | } |
| ... | ... | @@ -53,7 +115,7 @@ pub const Err = union(enum) { |
| 53 | 115 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 54 | 116 | try ctx.msgToken( |
| 55 | 117 | ctx.ast.nodeToken(data.node), |
| 56 | "unknown built-in function '{s}'", | |
| 118 | "unknown built-in function \"{s}\"", | |
| 57 | 119 | .{ctx.ast.basicNodeSlice(data.node)}, |
| 58 | 120 | ); |
| 59 | 121 | } |
| ... | ... | @@ -62,7 +124,10 @@ pub const Err = union(enum) { |
| 62 | 124 | node: NodeIndex, |
| 63 | 125 | |
| 64 | 126 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 65 | try ctx.msgNode(data.node, "local cannot be named '_'", .{}); | |
| 127 | try ctx.msgToken(ctx.ast.idToken(data.node), "local cannot be named \"_\"", .{}); | |
| 128 | try ctx.msgAlone("to discard this value, remove '{s}'", .{ | |
| 129 | ctx.ast.tokenSlice(ctx.ast.nodeToken(data.node)), | |
| 130 | }); | |
| 66 | 131 | } |
| 67 | 132 | }, |
| 68 | 133 | duplicate_function: struct { |
| ... | ... | @@ -70,7 +135,7 @@ pub const Err = union(enum) { |
| 70 | 135 | prev: NodeIndex, |
| 71 | 136 | |
| 72 | 137 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 73 | try ctx.msgToken(ctx.ast.fnDeclIdToken(data.node), "re-declaration of '{s}'", .{ | |
| 138 | try ctx.msgToken(ctx.ast.fnDeclIdToken(data.node), "re-declaration of \"{s}\"", .{ | |
| 74 | 139 | ctx.ast.fullFnDecl(data.node).id, |
| 75 | 140 | }); |
| 76 | 141 | try ctx.msgToken(ctx.ast.fnDeclIdToken(data.prev), "also declared here", .{}); |
| ... | ... | @@ -83,7 +148,7 @@ pub const Err = union(enum) { |
| 83 | 148 | prev: NodeIndex, |
| 84 | 149 | |
| 85 | 150 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 86 | try ctx.msgToken(ctx.ast.fnDeclIdToken(data.node), "re-declaration of '{s}'", .{ | |
| 151 | try ctx.msgToken(ctx.ast.fnDeclIdToken(data.node), "re-declaration of \"{s}\"", .{ | |
| 87 | 152 | ctx.ast.fullParamDecl(data.node).id, |
| 88 | 153 | }); |
| 89 | 154 | try ctx.msgToken(ctx.ast.nodeToken(data.prev), "also declared here", .{}); |
| ... | ... | @@ -108,7 +173,7 @@ pub const Err = union(enum) { |
| 108 | 173 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 109 | 174 | try ctx.msg( |
| 110 | 175 | .{ .two_tokens = ctx.ast.nodeToken(data.node) + 1 }, |
| 111 | "unknown block label '{s}'", | |
| 176 | "unknown block label \"{s}\"", | |
| 112 | 177 | .{ctx.ast.tokenSlice(ctx.ast.nodeToken(data.node) + 2)}, |
| 113 | 178 | ); |
| 114 | 179 | } |
| ... | ... | @@ -119,7 +184,7 @@ pub const Err = union(enum) { |
| 119 | 184 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 120 | 185 | try ctx.msgToken( |
| 121 | 186 | ctx.ast.nodeToken(data.node) + 1, |
| 122 | "declaration shadows built-in constant '{s}'", | |
| 187 | "declaration shadows reserved built-in \"{s}\"", | |
| 123 | 188 | .{ctx.ast.fullVarDecl(data.node).id}, |
| 124 | 189 | ); |
| 125 | 190 | } |
| ... | ... | @@ -132,17 +197,28 @@ pub const Warn = union(enum) { |
| 132 | 197 | unused: struct { |
| 133 | 198 | node: NodeIndex, |
| 134 | 199 | kind: UnusedKind, |
| 200 | // shadowed: ?NodeIndex = null, | |
| 135 | 201 | |
| 136 | 202 | fn build(data: @This(), ctx: *MsgContext) !void { |
| 137 | try ctx.msgToken(ctx.ast.idToken(data.node), "unused {s} '{s}'", .{ | |
| 203 | const id = switch (ctx.ast.nodeTag(data.node)) { | |
| 204 | .param_decl => ctx.ast.fullParamDecl(data.node).id, | |
| 205 | .fn_decl => ctx.ast.fullFnDecl(data.node).id, | |
| 206 | .var_decl => ctx.ast.fullVarDecl(data.node).id, | |
| 207 | else => unreachable, // not a valid symbol node | |
| 208 | }; | |
| 209 | ||
| 210 | try ctx.msgToken(ctx.ast.idToken(data.node), "unused {s} \"{s}\"", .{ | |
| 138 | 211 | @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 | }, | |
| 212 | id, | |
| 145 | 213 | }); |
| 214 | ||
| 215 | // if (data.shadowed) |node| { | |
| 216 | // try ctx.msgToken(ctx.ast.idToken(node), "{s} was re-declared here", .{ | |
| 217 | // @tagName(data.kind), | |
| 218 | // }); | |
| 219 | // } | |
| 220 | ||
| 221 | try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id}); | |
| 146 | 222 | } |
| 147 | 223 | }, |
| 148 | 224 | }; |
| ... | ... | @@ -217,6 +293,7 @@ pub fn formatItem( |
| 217 | 293 | .comp = comp, |
| 218 | 294 | .file = file, |
| 219 | 295 | .ast = &ast_data, |
| 296 | .utf8_source = (try file.getSource(comp.gpa)).code, | |
| 220 | 297 | .components = ArrayListUnmanaged(Component).initCapacity(alloc, 16) catch |
| 221 | 298 | unreachable, // stack-fallback allocator has enough space. |
| 222 | 299 | }; |
| ... | ... | @@ -246,6 +323,7 @@ pub fn formatItem( |
| 246 | 323 | } |
| 247 | 324 | |
| 248 | 325 | try tty.setColor(writer, prefix.color()); |
| 326 | try tty.setColor(writer, .bold); | |
| 249 | 327 | try writer.writeAll(@tagName(prefix)); |
| 250 | 328 | try tty.setColor(writer, .reset); |
| 251 | 329 | try tty.setColor(writer, .dim); |
| ... | ... | @@ -260,9 +338,18 @@ pub fn formatItem( |
| 260 | 338 | slice: []const u8, |
| 261 | 339 | }; |
| 262 | 340 | |
| 263 | const utf8_source = (try file.getSource(comp.gpa)).code; | |
| 341 | const utf8_source = ctx.utf8_source; | |
| 264 | 342 | |
| 265 | 343 | const at: SliceLoc = switch (component.highlight) { |
| 344 | .alone => { | |
| 345 | if (is_last) { | |
| 346 | try tty.setColor(writer, .dim); | |
| 347 | try writer.print(" {s}", .{"┴"}); | |
| 348 | try writer.writeAll("\n"); | |
| 349 | try tty.setColor(writer, .reset); | |
| 350 | } | |
| 351 | continue; | |
| 352 | }, | |
| 266 | 353 | inline .node, |
| 267 | 354 | .token, |
| 268 | 355 | .two_tokens, |
| ... | ... | @@ -283,6 +370,16 @@ pub fn formatItem( |
| 283 | 370 | }; |
| 284 | 371 | }, |
| 285 | 372 | |
| 373 | .token_stored => |token| loc: { | |
| 374 | const location = Tokenizer.tokenLocation(ctx.utf8_source, 0, token.offset); | |
| 375 | ||
| 376 | break :loc .{ | |
| 377 | .loc = location, | |
| 378 | .slice = token.slice(utf8_source), | |
| 379 | .byte = token.offset, | |
| 380 | }; | |
| 381 | }, | |
| 382 | ||
| 286 | 383 | inline .codepoint, |
| 287 | 384 | .codepoint_eol, |
| 288 | 385 | => |b, tag| loc: { |
| ... | ... | @@ -344,17 +441,25 @@ pub fn formatItem( |
| 344 | 441 | try tty.setColor(writer, .reset); |
| 345 | 442 | try writer.writeAll("\n"); |
| 346 | 443 | } |
| 444 | ||
| 445 | if (builtin.mode == .Debug and debug_show_report_trace) { | |
| 446 | var trace = item.debug_stack_trace; | |
| 447 | std.debug.dumpStackTrace(trace.trace()); | |
| 448 | } | |
| 347 | 449 | } |
| 348 | 450 | |
| 349 | 451 | pub fn dumpToStdErr(report: Report, comp: *Comp) !void { |
| 350 | const builtin = @import("builtin"); | |
| 351 | ||
| 352 | 452 | const stderr = std.io.getStdErr(); |
| 353 | 453 | const tty = std.io.tty.detectConfig(stderr); |
| 354 | 454 | |
| 355 | if (builtin.os.tag == .windows and tty == .windows_api) { | |
| 455 | if (false or | |
| 356 | 456 | // Windows API uses it's own API calls, so a buffered writer |
| 357 | 457 | // cannot be used :/ |
| 458 | (builtin.os.tag == .windows and tty == .windows_api) or | |
| 459 | // Debug stack trace printer writes directly to standard err, | |
| 460 | // so we need to be unbuffered for that. | |
| 461 | (builtin.mode == .Debug and debug_show_report_trace)) | |
| 462 | { | |
| 358 | 463 | try dumpToWriter(report, comp, stderr.writer(), tty); |
| 359 | 464 | } else { |
| 360 | 465 | var buf = std.io.bufferedWriter(stderr.writer()); |
| ... | ... | @@ -376,6 +481,7 @@ pub const Builder = struct { |
| 376 | 481 | try b.errors.append(b.gpa, .{ |
| 377 | 482 | .data = .{ .@"error" = @unionInit(Err, @tagName(kind), data) }, |
| 378 | 483 | .url = try b.currentFileString(), |
| 484 | .debug_stack_trace = if (builtin.mode == .Debug) StoredTrace.capture(@returnAddress()) else .{}, | |
| 379 | 485 | }); |
| 380 | 486 | } |
| 381 | 487 | |
| ... | ... | @@ -383,6 +489,7 @@ pub const Builder = struct { |
| 383 | 489 | try b.warnings.append(b.gpa, .{ |
| 384 | 490 | .data = .{ .warn = @unionInit(Warn, @tagName(kind), data) }, |
| 385 | 491 | .url = try b.currentFileString(), |
| 492 | .debug_stack_trace = if (builtin.mode == .Debug) StoredTrace.capture(@returnAddress()) else .{}, | |
| 386 | 493 | }); |
| 387 | 494 | } |
| 388 | 495 | |
| ... | ... | @@ -426,8 +533,12 @@ pub const Builder = struct { |
| 426 | 533 | }; |
| 427 | 534 | |
| 428 | 535 | pub const Highlight = union(enum) { |
| 536 | // No source view | |
| 537 | alone, | |
| 429 | 538 | // Highlight one token |
| 430 | 539 | token: TokenIndex, |
| 540 | // Highlight one token without referring to the Ast | |
| 541 | token_stored: Tokenizer.Token, | |
| 431 | 542 | // Highlight two tokens |
| 432 | 543 | two_tokens: TokenIndex, |
| 433 | 544 | // Highlight a Node |
| ... | ... | @@ -448,6 +559,7 @@ pub const MsgContext = struct { |
| 448 | 559 | file: *File, |
| 449 | 560 | comp: *Comp, |
| 450 | 561 | ast: *const Ast, |
| 562 | utf8_source: [:0]const u8, | |
| 451 | 563 | |
| 452 | 564 | /// This arena is is a stack fallback with 16kb, then |
| 453 | 565 | /// falling back to an arena allocator. This should |
| ... | ... | @@ -474,6 +586,10 @@ pub const MsgContext = struct { |
| 474 | 586 | pub fn msgToken(m: *MsgContext, token: TokenIndex, comptime fmt: []const u8, args: anytype) !void { |
| 475 | 587 | return m.msg(.{ .token = token }, fmt, args); |
| 476 | 588 | } |
| 589 | ||
| 590 | pub fn msgAlone(m: *MsgContext, comptime fmt: []const u8, args: anytype) !void { | |
| 591 | return m.msg(.alone, fmt, args); | |
| 592 | } | |
| 477 | 593 | }; |
| 478 | 594 | |
| 479 | 595 | const Report = @This(); |
| ... | ... | @@ -482,6 +598,8 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged; |
| 482 | 598 | const Allocator = std.mem.Allocator; |
| 483 | 599 | const assert = std.debug.assert; |
| 484 | 600 | |
| 601 | const builtin = @import("builtin"); | |
| 602 | ||
| 485 | 603 | const Tokenizer = @import("Tokenizer.zig"); |
| 486 | 604 | const ByteOffset = Tokenizer.ByteOffset; |
| 487 | 605 |
src/compiler/Sema.zig+17-7| ... | ... | @@ -130,11 +130,15 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value { |
| 130 | 130 | |
| 131 | 131 | const return_type = try s.resolveRefComptime(fn_decl.return_type); |
| 132 | 132 | |
| 133 | const root = (try s.analyzeBlock( | |
| 134 | fn_decl.body.toIndex() orelse | |
| 135 | @panic("TODO: please change this to Index. Ref is not allowed here anymore"), | |
| 136 | )).decode().inst; | |
| 137 | assert(@intFromEnum(root) == 0); | |
| 133 | const root_block = fn_decl.body.toIndex() orelse | |
| 134 | @panic("TODO: please change this to Index. Ref is not allowed here anymore"); | |
| 135 | ||
| 136 | _ = if (s.code.instPayload(.block, root_block).len > 0) | |
| 137 | try s.analyzeBlock(root_block) | |
| 138 | else empty_block: { | |
| 139 | var root = try s.addBlock(); | |
| 140 | break :empty_block (try root.finish(s, s.air_instructions.len - 1)); | |
| 141 | }; | |
| 138 | 142 | |
| 139 | 143 | const func_prototype = try s.ip.get(s.gpa, .{ |
| 140 | 144 | .type_function = .{ |
| ... | ... | @@ -384,6 +388,14 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref { |
| 384 | 388 | } |
| 385 | 389 | return InternPool.Index.void_value.toAir(); |
| 386 | 390 | }, |
| 391 | .discard => { | |
| 392 | const operand = try s.resolveRefRuntimeExtra(s.code.instData(.discard, in).operand, false); | |
| 393 | const t = Value.fromInterned(s.typeOfRef(operand)); | |
| 394 | if (t.ip_index == .void_value) { | |
| 395 | std.debug.print("oh no, we have a useless _ = void;\n", .{}); | |
| 396 | } | |
| 397 | return InternPool.Index.void_value.toAir(); | |
| 398 | }, | |
| 387 | 399 | .@"if" => { |
| 388 | 400 | const data: Teg.Inst.If = s.code.instData(.@"if", in).extra_index.get(&s.code); |
| 389 | 401 | |
| ... | ... | @@ -627,7 +639,6 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref { |
| 627 | 639 | .float = result, |
| 628 | 640 | })).toAir(); |
| 629 | 641 | }, |
| 630 | ||
| 631 | 642 | inline .lt, |
| 632 | 643 | .gt, |
| 633 | 644 | => |tag| { |
| ... | ... | @@ -643,7 +654,6 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref { |
| 643 | 654 | if (result) .bool_true else .bool_false, |
| 644 | 655 | ).toAir(); |
| 645 | 656 | }, |
| 646 | ||
| 647 | 657 | .eq, |
| 648 | 658 | => { |
| 649 | 659 | const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq"); |
src/compiler/Teg.Dump.zig+8-1| ... | ... | @@ -120,6 +120,14 @@ const State = struct { |
| 120 | 120 | try s.dumpRefMarker(writer, data.rhs); |
| 121 | 121 | try writer.writeAll(")"); |
| 122 | 122 | }, |
| 123 | .as => { | |
| 124 | const data = teg.instData(.as, inst).extra_index.get(teg); | |
| 125 | try writer.writeAll("("); | |
| 126 | try s.dumpRefMarker(writer, data.type); | |
| 127 | try writer.writeAll(", "); | |
| 128 | try s.dumpRefMarker(writer, data.operand); | |
| 129 | try writer.writeAll(")"); | |
| 130 | }, | |
| 123 | 131 | .@"break" => { |
| 124 | 132 | const data: Teg.Inst.Break = teg.instData(.@"break", inst).extra_index.get(teg); |
| 125 | 133 | try writer.writeAll("("); |
| ... | ... | @@ -192,7 +200,6 @@ const State = struct { |
| 192 | 200 | try s.newline(writer); |
| 193 | 201 | try writer.writeAll("}"); |
| 194 | 202 | }, |
| 195 | else => std.debug.panic("TODO format {s}", .{@tagName(tag)}), | |
| 196 | 203 | } |
| 197 | 204 | } |
| 198 | 205 |
src/compiler/Tokenizer.zig+123-58| ... | ... | @@ -146,10 +146,13 @@ pub const Token = struct { |
| 146 | 146 | /// Used so that parser does not need to do out of bounds checks |
| 147 | 147 | eof, |
| 148 | 148 | |
| 149 | /// Most tokens have a static length, such as "struct" or "+=" | |
| 150 | pub fn staticLen(tag: Tag) ?u32 { | |
| 149 | /// Most tokens have statically known lexeme, such as "struct" or "+=" | |
| 150 | pub fn lexeme(tag: Tag) ?[]const u8 { | |
| 151 | 151 | return switch (tag) { |
| 152 | // do not have a static length | |
| 152 | .invalid => unreachable, | |
| 153 | ||
| 154 | // do not have a static lexeme | |
| 155 | .eof, | |
| 153 | 156 | .id, |
| 154 | 157 | .builtin_id, |
| 155 | 158 | .number_int, |
| ... | ... | @@ -160,68 +163,86 @@ pub const Token = struct { |
| 160 | 163 | .module_doc_comment, |
| 161 | 164 | => null, |
| 162 | 165 | |
| 163 | .@"and" => 1, | |
| 164 | .and_and => 2, | |
| 165 | .bang => 1, | |
| 166 | .bang_equal => 2, | |
| 167 | .equal_lt => 2, | |
| 168 | .caret => 1, | |
| 169 | .end_brace => 1, | |
| 170 | .end_bracket => 1, | |
| 171 | .end_paren => 1, | |
| 172 | .colon => 1, | |
| 173 | .comma => 1, | |
| 174 | .dot => 1, | |
| 175 | .equal => 1, | |
| 176 | .equal_equal => 2, | |
| 177 | .gt => 1, | |
| 178 | .gt_equal => 2, | |
| 179 | .gt_gt => 2, | |
| 180 | .gt_gt_gt => 3, | |
| 181 | .lt => 1, | |
| 182 | .lt_equal => 2, | |
| 183 | .lt_lt => 2, | |
| 184 | .minus => 1, | |
| 185 | .start_brace => 1, | |
| 186 | .start_bracket => 1, | |
| 187 | .start_paren => 1, | |
| 188 | .pipe => 1, | |
| 189 | .pipe_pipe => 2, | |
| 190 | .percent => 1, | |
| 191 | .plus => 1, | |
| 192 | .minus_lt => 2, | |
| 193 | .semi => 1, | |
| 194 | .slash => 1, | |
| 195 | .star => 1, | |
| 196 | .star_star => 2, | |
| 197 | .tilde => 1, | |
| 198 | .slash_equal => 2, | |
| 199 | .percent_equal => 2, | |
| 200 | .tilde_equal => 2, | |
| 201 | .caret_equal => 2, | |
| 202 | .and_equal => 2, | |
| 203 | .pipe_equal => 2, | |
| 204 | .lt_lt_equal => 3, | |
| 205 | .gt_gt_equal => 3, | |
| 206 | .gt_gt_gt_equal => 4, | |
| 207 | .plus_equal => 2, | |
| 208 | .minus_equal => 2, | |
| 209 | .star_equal => 2, | |
| 210 | .star_star_equal => 3, | |
| 211 | .pipe_pipe_equal => 3, | |
| 212 | .and_and_equal => 3, | |
| 213 | ||
| 214 | .invalid => 0, | |
| 215 | .eof => 0, | |
| 166 | .@"and" => "&", | |
| 167 | .and_and => "&&", | |
| 168 | .bang => "!", | |
| 169 | .bang_equal => "!=", | |
| 170 | .equal_lt => "=<", | |
| 171 | .caret => "^", | |
| 172 | .end_brace => "}", | |
| 173 | .end_bracket => "]", | |
| 174 | .end_paren => ")", | |
| 175 | .colon => ":", | |
| 176 | .comma => ",", | |
| 177 | .dot => ".", | |
| 178 | .equal => "=", | |
| 179 | .equal_equal => "==", | |
| 180 | .gt => ">", | |
| 181 | .gt_equal => ">=", | |
| 182 | .gt_gt => ">>", | |
| 183 | .gt_gt_gt => ">>>", | |
| 184 | .lt => "<", | |
| 185 | .lt_equal => "<=", | |
| 186 | .lt_lt => "<<", | |
| 187 | .minus => "-", | |
| 188 | .start_brace => "{", | |
| 189 | .start_bracket => "[", | |
| 190 | .start_paren => "(", | |
| 191 | .pipe => "|", | |
| 192 | .pipe_pipe => "||", | |
| 193 | .percent => "%", | |
| 194 | .plus => "+", | |
| 195 | .minus_lt => "<", | |
| 196 | .semi => ";", | |
| 197 | .slash => "/", | |
| 198 | .star => "*", | |
| 199 | .star_star => "**", | |
| 200 | .tilde => "~", | |
| 201 | .slash_equal => "/=", | |
| 202 | .percent_equal => "%=", | |
| 203 | .tilde_equal => "~=", | |
| 204 | .caret_equal => "^=", | |
| 205 | .and_equal => "&=", | |
| 206 | .pipe_equal => "|=", | |
| 207 | .lt_lt_equal => "<<=", | |
| 208 | .gt_gt_equal => ">>=", | |
| 209 | .gt_gt_gt_equal => ">>>=", | |
| 210 | .plus_equal => "+=", | |
| 211 | .minus_equal => "-=", | |
| 212 | .star_equal => "*=", | |
| 213 | .star_star_equal => "**=", | |
| 214 | .pipe_pipe_equal => "||=", | |
| 215 | .and_and_equal => "&&=", | |
| 216 | 216 | |
| 217 | 217 | inline else => |keyword| comptime len: { |
| 218 | 218 | if (!mem.startsWith(u8, @tagName(keyword), "kw_")) { |
| 219 | 219 | @compileError("Token tag is not a keyword: " ++ @tagName(keyword)); |
| 220 | 220 | } |
| 221 | break :len @tagName(keyword).len - 3; | |
| 221 | break :len @tagName(keyword)[3..]; | |
| 222 | 222 | }, |
| 223 | 223 | }; |
| 224 | 224 | } |
| 225 | ||
| 226 | pub fn description(tag: Tag) []const u8 { | |
| 227 | return switch (tag) { | |
| 228 | .invalid => unreachable, | |
| 229 | .eof => "end of file", | |
| 230 | .id => "an identifier", | |
| 231 | .builtin_id => "a builtin identifier", | |
| 232 | .number_int => "a numeric literal", | |
| 233 | .number_float => "a numeric literal", | |
| 234 | .string => "a string literal", | |
| 235 | .multiline_string => "a multiline string literal", | |
| 236 | .doc_comment => "a documentation comment", | |
| 237 | .module_doc_comment => "a module documentation comment", | |
| 238 | inline else => |t| comptime "\"" ++ t.lexeme().? ++ "\"", | |
| 239 | }; | |
| 240 | } | |
| 241 | ||
| 242 | /// Most tokens have a static length, such as "struct" or "+=" | |
| 243 | pub fn staticLen(tag: Tag) ?u32 { | |
| 244 | return if (tag.lexeme()) |str| @intCast(str.len) else null; | |
| 245 | } | |
| 225 | 246 | }; |
| 226 | 247 | |
| 227 | 248 | /// Instead of storing the length of a token, we can recompute this |
| ... | ... | @@ -243,7 +264,8 @@ pub const Token = struct { |
| 243 | 264 | } |
| 244 | 265 | |
| 245 | 266 | pub fn slice(token: Token, utf8_source: [:0]const u8) []const u8 { |
| 246 | return utf8_source[token.offset..][0..token.len(utf8_source)]; | |
| 267 | return token.tag.lexeme() orelse | |
| 268 | utf8_source[token.offset..][0..token.len(utf8_source)]; | |
| 247 | 269 | } |
| 248 | 270 | |
| 249 | 271 | const keywords = std.ComptimeStringMap(Tag, keywords: { |
| ... | ... | @@ -921,6 +943,48 @@ pub fn skipWhitespace(t: *Tokenizer) void { |
| 921 | 943 | } |
| 922 | 944 | } |
| 923 | 945 | |
| 946 | pub fn tokenLocation( | |
| 947 | utf8_source: []const u8, | |
| 948 | start_offset: ByteOffset, | |
| 949 | token_start: ByteOffset, | |
| 950 | ) Location { | |
| 951 | var loc: Location = .{ | |
| 952 | .line = 1, | |
| 953 | .column = 1, | |
| 954 | .line_start = start_offset, | |
| 955 | .line_end = utf8_source.len, | |
| 956 | }; | |
| 957 | ||
| 958 | // Scan to by line until we go past the token start | |
| 959 | while (std.mem.indexOfScalarPos(u8, utf8_source, loc.line_start, '\n')) |i| { | |
| 960 | if (i >= token_start) { | |
| 961 | break; // Went past | |
| 962 | } | |
| 963 | loc.line += 1; | |
| 964 | loc.line_start = i + 1; | |
| 965 | } | |
| 966 | ||
| 967 | const offset = loc.line_start; | |
| 968 | for (utf8_source[offset..], 0..) |c, i| { | |
| 969 | if (i + offset == token_start) { | |
| 970 | loc.line_end = i + offset; | |
| 971 | while (loc.line_end < utf8_source.len and utf8_source[loc.line_end] != '\n') { | |
| 972 | loc.line_end += 1; | |
| 973 | } | |
| 974 | return loc; | |
| 975 | } | |
| 976 | if (c == '\n') { | |
| 977 | loc.line += 1; | |
| 978 | loc.column = 1; | |
| 979 | loc.line_start = i + 1; | |
| 980 | } else { | |
| 981 | loc.column += 1; | |
| 982 | } | |
| 983 | } | |
| 984 | ||
| 985 | return loc; | |
| 986 | } | |
| 987 | ||
| 924 | 988 | const Tokenizer = @This(); |
| 925 | 989 | const std = @import("std"); |
| 926 | 990 | const unicode = std.unicode; |
| ... | ... | @@ -928,3 +992,4 @@ const assert = std.debug.assert; |
| 928 | 992 | const mem = std.mem; |
| 929 | 993 | const unicode_props = @import("unicode_props.zig"); |
| 930 | 994 | const T = @import("tracer"); |
| 995 | const Location = @import("Ast.zig").Location; |
src/compiler/Transform.zig+26-3| ... | ... | @@ -603,7 +603,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc |
| 603 | 603 | return .void_value; |
| 604 | 604 | }; |
| 605 | 605 | |
| 606 | lookup.mutated = true; | |
| 606 | lookup.used = true; | |
| 607 | 607 | |
| 608 | 608 | return switch (lookup.data) { |
| 609 | 609 | .declaration => try t.addInstRef(.{ |
| ... | ... | @@ -1061,6 +1061,7 @@ fn transformBlockStatement( |
| 1061 | 1061 | const var_decl = t.ast.fullVarDecl(node); |
| 1062 | 1062 | |
| 1063 | 1063 | if (mem.eql(u8, var_decl.id, "_")) { |
| 1064 | // TODO: relax this restriction into just creating a discard node | |
| 1064 | 1065 | try t.report.err(.invalid_discard, .{ .node = node }); |
| 1065 | 1066 | return .void_value; |
| 1066 | 1067 | } |
| ... | ... | @@ -1101,13 +1102,35 @@ fn transformBlockStatement( |
| 1101 | 1102 | }), |
| 1102 | 1103 | }); |
| 1103 | 1104 | |
| 1104 | try t.scope().symbols.put(t.gpa, var_decl.id, .{ | |
| 1105 | const gop = try t.scope().symbols.getOrPut(t.gpa, var_decl.id); | |
| 1106 | ||
| 1107 | // if re-declaring, apply unused warnings now | |
| 1108 | if (gop.found_existing) { | |
| 1109 | const value = gop.value_ptr.*; | |
| 1110 | if (!value.used) { | |
| 1111 | try t.report.warn(.unused, .{ | |
| 1112 | .node = value.node, | |
| 1113 | .kind = switch (value.data) { | |
| 1114 | .declaration => .function, | |
| 1115 | .param => .param, | |
| 1116 | .mutable => .variable, | |
| 1117 | .immutable => switch (t.ast.nodeTag(value.node)) { | |
| 1118 | .param_decl => .param, | |
| 1119 | else => .constant, | |
| 1120 | }, | |
| 1121 | }, | |
| 1122 | // .shadowed = node, | |
| 1123 | }); | |
| 1124 | } | |
| 1125 | } | |
| 1126 | ||
| 1127 | gop.value_ptr.* = .{ | |
| 1105 | 1128 | .node = node, |
| 1106 | 1129 | .data = switch (var_decl.is_const) { |
| 1107 | 1130 | true => .{ .immutable = mem_ref }, |
| 1108 | 1131 | false => .{ .mutable = mem_ref }, |
| 1109 | 1132 | }, |
| 1110 | }); | |
| 1133 | }; | |
| 1111 | 1134 | |
| 1112 | 1135 | return .void_value; |
| 1113 | 1136 | }, |
src/compiler/main.zig+28-13| ... | ... | @@ -21,7 +21,9 @@ pub fn main() !void { |
| 21 | 21 | |
| 22 | 22 | try unicode_props.init(); |
| 23 | 23 | |
| 24 | std.debug.print("\r\x1b[K", .{}); | |
| 24 | if (builtin.mode == .Debug) { | |
| 25 | std.debug.print("\r\x1b[K", .{}); | |
| 26 | } | |
| 25 | 27 | |
| 26 | 28 | const use_gpa = builtin.mode == .Debug; |
| 27 | 29 | |
| ... | ... | @@ -54,14 +56,17 @@ pub fn main() !void { |
| 54 | 56 | |
| 55 | 57 | while (args.next()) |arg| { |
| 56 | 58 | if (std.mem.startsWith(u8, arg, "-")) { |
| 59 | if (builtin.mode == .Debug and std.mem.eql(u8, arg, "--report-trace")) { | |
| 60 | Report.debug_show_report_trace = true; | |
| 61 | continue; | |
| 62 | } | |
| 57 | 63 | if (std.mem.eql(u8, arg, "--dump-ir")) { |
| 58 | 64 | dump_ir = true; |
| 59 | } else { | |
| 60 | std.debug.print("error: unknown flag '{s}'\n", .{arg}); | |
| 61 | maybe_file_path = null; | |
| 62 | break; | |
| 65 | continue; | |
| 63 | 66 | } |
| 64 | continue; | |
| 67 | std.debug.print("error: unknown flag '{s}'\n", .{arg}); | |
| 68 | maybe_file_path = null; | |
| 69 | break; | |
| 65 | 70 | } |
| 66 | 71 | |
| 67 | 72 | if (maybe_file_path == null) { |
| ... | ... | @@ -77,13 +82,23 @@ pub fn main() !void { |
| 77 | 82 | |
| 78 | 83 | const file_path = maybe_file_path orelse { |
| 79 | 84 | std.debug.print("Usage: {s} [flags] <input file>\n", .{exe_name}); |
| 80 | std.debug.print( | |
| 81 | // | 60 chars wrap point| | |
| 82 | \\ | |
| 83 | \\Flags: | |
| 84 | \\ --dump-ir Dump intermediate representations. | |
| 85 | \\ | |
| 86 | , .{}); | |
| 85 | { | |
| 86 | std.debug.print( | |
| 87 | // | 60 chars wrap point| | |
| 88 | \\ | |
| 89 | \\Flags: | |
| 90 | \\ --dump-ir Dump intermediate representations. | |
| 91 | \\ | |
| 92 | , .{}); | |
| 93 | } | |
| 94 | if (builtin.mode == .Debug) { | |
| 95 | std.debug.print( | |
| 96 | \\ | |
| 97 | \\Debug Mode Flags: | |
| 98 | \\ --report-trace Print stack traces per error. Very noisy | |
| 99 | \\ | |
| 100 | , .{}); | |
| 101 | } | |
| 87 | 102 | std.process.exit(1); |
| 88 | 103 | }; |
| 89 | 104 |