authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-22 01:23:06-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-22 01:23:06-07:00
log4cd49040184a9da68e9584b71ac21f9e19723d59
treeb4e843e8e58dbed96158fb445cd5056eba0e5262
parentfa2de52f403657b87c1c7db26b6010bf1c019ad5
signature Commit is signed but in an unrecognized format.

some bugfixes


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
18## 0.1.1
29
310- feat: `var` declarations / runtime memory
build.zig+19-6
......@@ -1,4 +1,7 @@
11const std = @import("std");
2const Build = std.Build;
3const Target = std.Target;
4const OsVersion = Target.Query.OsVersion;
25
36pub fn build(b: *std.Build) void {
47 b.reference_trace = 20;
......@@ -28,6 +31,7 @@ pub fn build(b: *std.Build) void {
2831 .root_source_file = b.path("src/compiler/main.zig"),
2932 .target = target,
3033 .optimize = optimize,
34 .link_libc = true,
3135 });
3236 b.installArtifact(exe);
3337
......@@ -67,13 +71,17 @@ pub fn build(b: *std.Build) void {
6771 // wasm_api.root_module.addImport("zg:PropsData", zg.module("PropsData"));
6872
6973 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
7078 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 }),
7785 }) |cross_target| {
7886 const name = std.fmt.allocPrint(
7987 b.allocator,
......@@ -84,19 +92,24 @@ pub fn build(b: *std.Build) void {
8492 },
8593 ) catch @panic("OOM");
8694
95 // std.log.info("{s}\n", .{cross_target.result.zigTriple(b.allocator) catch @panic("OOM")});
96
8797 const cross_exe = b.addExecutable(.{
8898 .name = "compiler",
8999 .root_source_file = b.path("src/compiler/main.zig"),
90100 .target = cross_target,
91101 .optimize = .ReleaseSafe,
92102 .strip = true,
103 .link_libc = true,
93104 });
105 cross_exe.root_module.addImport("build_options", opts_mod);
94106 cross_exe.root_module.addImport("zg:PropsData", zg.module("PropsData"));
95107 cross_exe.root_module.addImport("tracer", tracer.module("tracer"));
96108
97109 release.dependOn(&b.addInstallFile(
98110 cross_exe.getEmittedBin(),
99111 b.pathJoin(&.{
112 "release",
100113 name,
101114 std.mem.concat(b.allocator, u8, &.{
102115 "compiler",
src/compiler/Ast.zig+1-76
......@@ -40,48 +40,6 @@ tokens: Token.List.Slice,
4040nodes: Node.List.Slice,
4141extra: []const u32,
4242
43const Parse = @import("Parse.zig");
44pub 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
8543pub const Node = struct {
8644 tag: Tag,
8745 main_token: TokenIndex,
......@@ -645,41 +603,8 @@ pub fn nodeStatements(ast: *const Ast, node: NodeIndex) []const NodeIndex {
645603}
646604
647605pub 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 };
654606 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);
683608}
684609
685610pub fn tokenSlice(ast: Ast, token_index: TokenIndex) []const u8 {
src/compiler/Comp.zig+4-4
......@@ -129,16 +129,16 @@ pub const File = struct {
129129 return ast;
130130 }
131131
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(
134134 comp.gpa,
135135 (try file.getSource(comp.gpa)).code,
136 &ast_err,
136 &err_data,
137137 ) catch |err| switch (err) {
138138 error.OutOfMemory => return err,
139139 error.ParseFail => {
140 var report = try ast_err.toReport(comp, file);
141140 // TODO: let this merge with other report
141 var report = try err_data.toReport(comp, file);
142142 report.dumpToStdErr(comp) catch {};
143143 report.deinit(comp.gpa);
144144 return err;
src/compiler/JsLink.zig+6-2
......@@ -69,7 +69,7 @@ pub fn linkJsCode(
6969 try link.decls.putNoClobber(comp.gpa, decl_index, @enumFromInt(gop.index));
7070 }
7171
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", .{});
7373
7474 // 2. Print every function decl
7575 for (decls[1..]) |decl_index| {
......@@ -77,7 +77,11 @@ pub fn linkJsCode(
7777 }
7878
7979 // 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 }
8185}
8286
8387fn deinit(link: *JsLink) void {
src/compiler/JsNodeGen.zig+11-9
......@@ -107,16 +107,18 @@ pub fn build(c: *JsNodeGen) !void {
107107
108108 _ = try c.add(.root, undefined);
109109
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 }
117118
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 }
120122 }
121123
122124 const items = c.scratch.items[scratch_top..];
src/compiler/Parse.zig+150-42
......@@ -10,9 +10,49 @@ nodes: Node.List = .{},
1010extra: ArrayListUnmanaged(u32) = .{},
1111scratch: ArrayListUnmanaged(NodeIndex) = .{},
1212
13ast_error: ?AstError = null,
13ast_error: ?AstError.WithTrace = null,
14
15pub 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}
1454
15pub fn init(gpa: Allocator, source: [:0]const u8, tokens: Token.List.Slice) Parse {
55fn init(gpa: Allocator, source: [:0]const u8, tokens: Token.List.Slice) Parse {
1656 return Parse{
1757 .gpa = gpa,
1858 .source = source,
......@@ -22,7 +62,7 @@ pub fn init(gpa: Allocator, source: [:0]const u8, tokens: Token.List.Slice) Pars
2262}
2363
2464/// This transfers the parse result into an Ast
25pub fn finalize(p: *Parse, token_slice: Token.List.Slice) !Ast {
65fn finalize(p: *Parse, token_slice: Token.List.Slice) !Ast {
2666 const extra = try p.extra.toOwnedSlice(p.gpa);
2767 defer p.* = undefined;
2868 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 {
4080}
4181
4282/// This discards all parser work
43pub fn deinit(p: *Parse) void {
83fn deinit(p: *Parse) void {
4484 defer p.* = undefined;
4585 p.nodes.deinit(p.gpa);
4686 p.extra.deinit(p.gpa);
......@@ -52,19 +92,46 @@ pub const Error = error{
5292 ParseFail,
5393};
5494
95pub 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
55113/// 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.
56116pub const AstError = union(enum) {
117 /// Failure during tokenization
57118 tokenization: struct {
58119 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,
60131 },
61 expected_token: Token.Tag,
62 expected_statement,
63 expected_expr,
64 expected_else_value,
65132
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);
68135 }
69136
70137 pub fn build(err: AstError, ctx: *Report.MsgContext) !void {
......@@ -86,11 +153,29 @@ pub const AstError = union(enum) {
86153 error.GitConflict => "git merge conflict encountered",
87154 }});
88155 },
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 },
92168 }
93169 }
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 };
94179};
95180
96181pub fn parseRoot(p: *Parse) Error!void {
......@@ -141,10 +226,10 @@ fn parseModuleMembers(p: *Parse) Error!SubList {
141226
142227 else => {
143228 if (!p.isExpressionStart()) {
144 return p.fail(.expected_statement);
229 return p.failUnexpectedToken(.statement);
145230 }
146231
147 try p.scratch.append(p.gpa, try p.parseExpression());
232 try p.scratch.append(p.gpa, try p.parseExpression(true));
148233 _ = try p.expect(.semi);
149234 },
150235 };
......@@ -180,7 +265,7 @@ fn parseFnDecl(p: *Parse) Error!NodeIndex {
180265 const params = try p.parseParamList();
181266
182267 const return_type = if (p.eatToken(.minus_lt)) |_|
183 try p.parseExpression()
268 try p.parseExpression(false)
184269 else
185270 null;
186271
......@@ -222,7 +307,7 @@ fn parseParamList(p: *Parse) Error!SubList {
222307 _ = p.nextToken();
223308 },
224309 .end_paren => break,
225 else => return p.fail(.{ .expected_token = .comma }),
310 else => return p.failUnexpectedToken(.param),
226311 }
227312 }
228313
......@@ -239,13 +324,13 @@ fn parseParamDecl(p: *Parse) Error!NodeIndex {
239324
240325 _ = try p.expect(.colon);
241326
242 const @"type" = try p.parseExpression();
327 const ty = try p.parseExpression(false);
243328
244329 return try p.addNode(.{
245330 .tag = .param_decl,
246331 .main_token = main_token,
247332 .data = .{ .param_decl = .{
248 .type = @"type",
333 .type = ty,
249334 } },
250335 });
251336}
......@@ -274,7 +359,7 @@ fn parseLabelExpression(p: *Parse) Error!NodeIndex {
274359
275360 const main_token = p.assertToken(.id);
276361 _ = try p.expect(.colon);
277 const expr = try p.parseExpression();
362 const expr = try p.parseExpression(false);
278363 return try p.addNode(.{
279364 .tag = .label,
280365 .main_token = main_token,
......@@ -301,12 +386,12 @@ fn parseBlockContents(p: *Parse) Error!struct { Node.Tag, SubList } {
301386
302387 else => {
303388 if (!p.isExpressionStart()) {
304 return p.fail(.expected_statement);
389 return p.failUnexpectedToken(.statement);
305390 }
306391
307392 const first_tag = p.currentTag();
308393
309 try p.scratch.append(p.gpa, try p.parseExpression());
394 try p.scratch.append(p.gpa, try p.parseExpression(true));
310395
311396 switch (first_tag) {
312397 // some control flow and blocks are allowed to not have a
......@@ -352,13 +437,13 @@ fn parseVarDecl(p: *Parse) Error!NodeIndex {
352437 _ = try p.expect(.id);
353438
354439 const data_type: OptionalNodeIndex = if (p.eatToken(.colon)) |_|
355 optionalNodeIndex(try p.parseExpression())
440 optionalNodeIndex(try p.parseExpression(false))
356441 else
357442 .none;
358443
359444 _ = try p.expect(.equal);
360445
361 const data_init = try p.parseExpression();
446 const data_init = try p.parseExpression(true);
362447
363448 _ = try p.expect(.semi);
364449
......@@ -412,6 +497,7 @@ const Operator = enum(u8) {
412497 le = @intFromEnum(Token.Tag.lt_equal),
413498 gt = @intFromEnum(Token.Tag.gt),
414499 ge = @intFromEnum(Token.Tag.gt_equal),
500 // Following operators are assignment operators
415501 assign = @intFromEnum(Token.Tag.equal),
416502 add_assign = @intFromEnum(Token.Tag.plus_equal),
417503 sub_assign = @intFromEnum(Token.Tag.minus_equal),
......@@ -551,9 +637,13 @@ const Operator = enum(u8) {
551637 assert(tag != .root); // invalid operator or lut construction
552638 return tag;
553639 }
640
641 pub fn isAssign(op: Operator) bool {
642 return @intFromEnum(op) >= @intFromEnum(Operator.assign);
643 }
554644};
555645
556fn parseExpression(p: *Parse) Error!NodeIndex {
646fn parseExpression(p: *Parse, allow_assign_operators: bool) Error!NodeIndex {
557647 const trace = t.trace(@src());
558648 defer trace.end();
559649
......@@ -569,7 +659,8 @@ fn parseExpression(p: *Parse) Error!NodeIndex {
569659 var expr = try p.parseExpressionPart();
570660
571661 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);
573664 }
574665
575666 return expr;
......@@ -616,7 +707,7 @@ fn parseExpressionPart(p: *Parse) Error!NodeIndex {
616707 });
617708 },
618709
619 else => return p.fail(.expected_expr),
710 else => return p.failUnexpectedToken(.expression),
620711 };
621712
622713 // parse field, calls, and index expressions
......@@ -657,7 +748,7 @@ fn parseExpressionPart(p: *Parse) Error!NodeIndex {
657748 },
658749 .start_bracket => {
659750 const main_token = p.nextToken();
660 const index = try p.parseExpression();
751 const index = try p.parseExpression(false);
661752
662753 expr = try p.addNode(.{
663754 .tag = .index,
......@@ -676,7 +767,7 @@ fn parseExpressionPart(p: *Parse) Error!NodeIndex {
676767 return expr;
677768}
678769
679fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) Error!NodeIndex {
770fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator, allow_assign_operators: bool) Error!NodeIndex {
680771 const trace = t.trace(@src());
681772 defer trace.end();
682773
......@@ -685,6 +776,8 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) Error!NodeIndex {
685776 var rhs = try p.parseExpressionPart();
686777
687778 while (Operator.fromToken(p.currentTag())) |next_op| {
779 if (!allow_assign_operators and next_op.isAssign()) break;
780
688781 const new_level = next_op.level();
689782 const use = switch (op.direction()) {
690783 .ltr => @intFromEnum(new_level) < @intFromEnum(level),
......@@ -692,7 +785,7 @@ fn parseBinarySuffix(p: *Parse, lhs: NodeIndex, op: Operator) Error!NodeIndex {
692785 };
693786 if (!use) break;
694787
695 rhs = try p.parseBinarySuffix(rhs, next_op);
788 rhs = try p.parseBinarySuffix(rhs, next_op, allow_assign_operators);
696789 }
697790
698791 return try p.addNode(.{
......@@ -712,7 +805,7 @@ fn parseReturn(p: *Parse) Error!NodeIndex {
712805 const main_token = p.assertToken(.kw_return);
713806
714807 const expr = if (p.isExpressionStart())
715 try p.parseExpression()
808 try p.parseExpression(false)
716809 else
717810 null;
718811
......@@ -737,7 +830,7 @@ fn parseBreak(p: *Parse) Error!NodeIndex {
737830 null;
738831
739832 const expr = if (p.isExpressionStart())
740 try p.parseExpression()
833 try p.parseExpression(false)
741834 else
742835 null;
743836
......@@ -776,7 +869,7 @@ fn parseCallArgs(p: *Parse) Error!SubList {
776869 defer p.scratch.shrinkRetainingCapacity(scratch_top);
777870
778871 while (p.isExpressionStart()) {
779 try p.scratch.append(p.gpa, try p.parseExpression());
872 try p.scratch.append(p.gpa, try p.parseExpression(false));
780873
781874 switch (p.currentTag()) {
782875 .end_paren => {
......@@ -787,7 +880,7 @@ fn parseCallArgs(p: *Parse) Error!SubList {
787880 if (p.currentTag() == .end_paren)
788881 break;
789882 },
790 else => return p.fail(.{ .expected_token = .end_paren }),
883 else => return p.failUnexpectedToken(.argument),
791884 }
792885 }
793886
......@@ -802,7 +895,7 @@ fn parseIf(p: *Parse) Error!NodeIndex {
802895
803896 const main_token = p.assertToken(.kw_if);
804897
805 const cond = try p.parseExpression();
898 const cond = try p.parseExpression(false);
806899
807900 _ = try p.expectWithoutAdvance(.start_brace);
808901 const yes = try p.parseBlock();
......@@ -838,7 +931,7 @@ fn parseElseValue(p: *Parse) Error!NodeIndex {
838931 .kw_if => p.parseIf(),
839932 .id => p.parseLabelExpression(),
840933
841 else => return p.fail(.expected_else_value),
934 else => return p.failUnexpectedToken(.else_clause),
842935 };
843936}
844937
......@@ -848,7 +941,7 @@ fn parseGroup(p: *Parse) Error!NodeIndex {
848941
849942 const main_token = p.assertToken(.start_paren);
850943
851 const expr = try p.parseExpression();
944 const expr = try p.parseExpression(false);
852945
853946 _ = try p.expect(.end_paren);
854947
......@@ -885,11 +978,20 @@ fn addExtra(p: *Parse, comptime T: type, extra: T) !ExtraIndex {
885978}
886979
887980fn 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 };
890985 return Error.ParseFail;
891986}
892987
988fn failUnexpectedToken(p: *Parse, kind: ExpectedContext) Error {
989 return p.fail(.{ .unexpected_context = .{
990 .got = p.fullToken(p.pos),
991 .expected = kind,
992 } });
993}
994
893995fn blankNode(p: *Parse, comptime tag: Token.Tag) !NodeIndex {
894996 const main_token = p.assertToken(tag);
895997 return try p.addNode(.{
......@@ -917,14 +1019,20 @@ fn eatToken(p: *Parse, tag: Token.Tag) ?TokenIndex {
9171019
9181020fn expect(p: *Parse, tag: Token.Tag) !TokenIndex {
9191021 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 } });
9211026 }
9221027 return p.nextToken();
9231028}
9241029
9251030fn expectWithoutAdvance(p: *Parse, tag: Token.Tag) !void {
9261031 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 } });
9281036 }
9291037}
9301038
src/compiler/Report.zig+137-19
......@@ -9,27 +9,89 @@ errors: []Item = &.{},
99warnings: []Item = &.{},
1010string_bytes: []u8 = &.{},
1111
12pub var debug_show_report_trace: if (builtin.mode == .Debug) bool else void =
13 if (builtin.mode == .Debug) false;
14
1215pub const Item = struct {
1316 /// What is this diagnostic
1417 data: union { @"error": Err, warn: Warn },
1518 /// What file was it in
1619 url: String,
1720
21 debug_stack_trace: DebugStoredTrace,
22
1823 /// Type is never stored, inferred from which list it is in.
1924 pub const Type = enum { @"error", warn };
2025};
2126
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.
30pub 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
70pub const DebugStoredTrace = if (builtin.mode == .Debug)
71 StoredTrace
72else
73 struct {
74 pub fn capture() @This() {
75 return .{};
76 }
77
78 pub fn from(_: ?*std.builtin.StackTrace) @This() {
79 return .{};
80 }
81 };
82
2283pub const Err = union(enum) {
2384 pub const FieldEnum = std.meta.FieldEnum(Err);
2485
2586 /// 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 {
2788 const string_bytes = try comp.gpa.dupe(u8, file.file_path);
2889 errdefer comp.gpa.free(string_bytes);
2990
3091 const errors = try comp.gpa.dupe(Item, &.{.{
3192 .data = .{ .@"error" = err },
3293 .url = .{ .start = 0, .len = @intCast(string_bytes.len) },
94 .debug_stack_trace = trace,
3395 }});
3496 errdefer comp.gpa.free(errors);
3597
......@@ -42,7 +104,7 @@ pub const Err = union(enum) {
42104 node: NodeIndex,
43105
44106 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}\"", .{
46108 ctx.ast.basicNodeSlice(data.node),
47109 });
48110 }
......@@ -53,7 +115,7 @@ pub const Err = union(enum) {
53115 fn build(data: @This(), ctx: *MsgContext) !void {
54116 try ctx.msgToken(
55117 ctx.ast.nodeToken(data.node),
56 "unknown built-in function '{s}'",
118 "unknown built-in function \"{s}\"",
57119 .{ctx.ast.basicNodeSlice(data.node)},
58120 );
59121 }
......@@ -62,7 +124,10 @@ pub const Err = union(enum) {
62124 node: NodeIndex,
63125
64126 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 });
66131 }
67132 },
68133 duplicate_function: struct {
......@@ -70,7 +135,7 @@ pub const Err = union(enum) {
70135 prev: NodeIndex,
71136
72137 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}\"", .{
74139 ctx.ast.fullFnDecl(data.node).id,
75140 });
76141 try ctx.msgToken(ctx.ast.fnDeclIdToken(data.prev), "also declared here", .{});
......@@ -83,7 +148,7 @@ pub const Err = union(enum) {
83148 prev: NodeIndex,
84149
85150 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}\"", .{
87152 ctx.ast.fullParamDecl(data.node).id,
88153 });
89154 try ctx.msgToken(ctx.ast.nodeToken(data.prev), "also declared here", .{});
......@@ -108,7 +173,7 @@ pub const Err = union(enum) {
108173 fn build(data: @This(), ctx: *MsgContext) !void {
109174 try ctx.msg(
110175 .{ .two_tokens = ctx.ast.nodeToken(data.node) + 1 },
111 "unknown block label '{s}'",
176 "unknown block label \"{s}\"",
112177 .{ctx.ast.tokenSlice(ctx.ast.nodeToken(data.node) + 2)},
113178 );
114179 }
......@@ -119,7 +184,7 @@ pub const Err = union(enum) {
119184 fn build(data: @This(), ctx: *MsgContext) !void {
120185 try ctx.msgToken(
121186 ctx.ast.nodeToken(data.node) + 1,
122 "declaration shadows built-in constant '{s}'",
187 "declaration shadows reserved built-in \"{s}\"",
123188 .{ctx.ast.fullVarDecl(data.node).id},
124189 );
125190 }
......@@ -132,17 +197,28 @@ pub const Warn = union(enum) {
132197 unused: struct {
133198 node: NodeIndex,
134199 kind: UnusedKind,
200 // shadowed: ?NodeIndex = null,
135201
136202 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}\"", .{
138211 @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,
145213 });
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});
146222 }
147223 },
148224};
......@@ -217,6 +293,7 @@ pub fn formatItem(
217293 .comp = comp,
218294 .file = file,
219295 .ast = &ast_data,
296 .utf8_source = (try file.getSource(comp.gpa)).code,
220297 .components = ArrayListUnmanaged(Component).initCapacity(alloc, 16) catch
221298 unreachable, // stack-fallback allocator has enough space.
222299 };
......@@ -246,6 +323,7 @@ pub fn formatItem(
246323 }
247324
248325 try tty.setColor(writer, prefix.color());
326 try tty.setColor(writer, .bold);
249327 try writer.writeAll(@tagName(prefix));
250328 try tty.setColor(writer, .reset);
251329 try tty.setColor(writer, .dim);
......@@ -260,9 +338,18 @@ pub fn formatItem(
260338 slice: []const u8,
261339 };
262340
263 const utf8_source = (try file.getSource(comp.gpa)).code;
341 const utf8_source = ctx.utf8_source;
264342
265343 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 },
266353 inline .node,
267354 .token,
268355 .two_tokens,
......@@ -283,6 +370,16 @@ pub fn formatItem(
283370 };
284371 },
285372
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
286383 inline .codepoint,
287384 .codepoint_eol,
288385 => |b, tag| loc: {
......@@ -344,17 +441,25 @@ pub fn formatItem(
344441 try tty.setColor(writer, .reset);
345442 try writer.writeAll("\n");
346443 }
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 }
347449}
348450
349451pub fn dumpToStdErr(report: Report, comp: *Comp) !void {
350 const builtin = @import("builtin");
351
352452 const stderr = std.io.getStdErr();
353453 const tty = std.io.tty.detectConfig(stderr);
354454
355 if (builtin.os.tag == .windows and tty == .windows_api) {
455 if (false or
356456 // Windows API uses it's own API calls, so a buffered writer
357457 // 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 {
358463 try dumpToWriter(report, comp, stderr.writer(), tty);
359464 } else {
360465 var buf = std.io.bufferedWriter(stderr.writer());
......@@ -376,6 +481,7 @@ pub const Builder = struct {
376481 try b.errors.append(b.gpa, .{
377482 .data = .{ .@"error" = @unionInit(Err, @tagName(kind), data) },
378483 .url = try b.currentFileString(),
484 .debug_stack_trace = if (builtin.mode == .Debug) StoredTrace.capture(@returnAddress()) else .{},
379485 });
380486 }
381487
......@@ -383,6 +489,7 @@ pub const Builder = struct {
383489 try b.warnings.append(b.gpa, .{
384490 .data = .{ .warn = @unionInit(Warn, @tagName(kind), data) },
385491 .url = try b.currentFileString(),
492 .debug_stack_trace = if (builtin.mode == .Debug) StoredTrace.capture(@returnAddress()) else .{},
386493 });
387494 }
388495
......@@ -426,8 +533,12 @@ pub const Builder = struct {
426533};
427534
428535pub const Highlight = union(enum) {
536 // No source view
537 alone,
429538 // Highlight one token
430539 token: TokenIndex,
540 // Highlight one token without referring to the Ast
541 token_stored: Tokenizer.Token,
431542 // Highlight two tokens
432543 two_tokens: TokenIndex,
433544 // Highlight a Node
......@@ -448,6 +559,7 @@ pub const MsgContext = struct {
448559 file: *File,
449560 comp: *Comp,
450561 ast: *const Ast,
562 utf8_source: [:0]const u8,
451563
452564 /// This arena is is a stack fallback with 16kb, then
453565 /// falling back to an arena allocator. This should
......@@ -474,6 +586,10 @@ pub const MsgContext = struct {
474586 pub fn msgToken(m: *MsgContext, token: TokenIndex, comptime fmt: []const u8, args: anytype) !void {
475587 return m.msg(.{ .token = token }, fmt, args);
476588 }
589
590 pub fn msgAlone(m: *MsgContext, comptime fmt: []const u8, args: anytype) !void {
591 return m.msg(.alone, fmt, args);
592 }
477593};
478594
479595const Report = @This();
......@@ -482,6 +598,8 @@ const ArrayListUnmanaged = std.ArrayListUnmanaged;
482598const Allocator = std.mem.Allocator;
483599const assert = std.debug.assert;
484600
601const builtin = @import("builtin");
602
485603const Tokenizer = @import("Tokenizer.zig");
486604const ByteOffset = Tokenizer.ByteOffset;
487605
src/compiler/Sema.zig+17-7
......@@ -130,11 +130,15 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
130130
131131 const return_type = try s.resolveRefComptime(fn_decl.return_type);
132132
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 };
138142
139143 const func_prototype = try s.ip.get(s.gpa, .{
140144 .type_function = .{
......@@ -384,6 +388,14 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
384388 }
385389 return InternPool.Index.void_value.toAir();
386390 },
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 },
387399 .@"if" => {
388400 const data: Teg.Inst.If = s.code.instData(.@"if", in).extra_index.get(&s.code);
389401
......@@ -627,7 +639,6 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref {
627639 .float = result,
628640 })).toAir();
629641 },
630
631642 inline .lt,
632643 .gt,
633644 => |tag| {
......@@ -643,7 +654,6 @@ fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref {
643654 if (result) .bool_true else .bool_false,
644655 ).toAir();
645656 },
646
647657 .eq,
648658 => {
649659 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 {
120120 try s.dumpRefMarker(writer, data.rhs);
121121 try writer.writeAll(")");
122122 },
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 },
123131 .@"break" => {
124132 const data: Teg.Inst.Break = teg.instData(.@"break", inst).extra_index.get(teg);
125133 try writer.writeAll("(");
......@@ -192,7 +200,6 @@ const State = struct {
192200 try s.newline(writer);
193201 try writer.writeAll("}");
194202 },
195 else => std.debug.panic("TODO format {s}", .{@tagName(tag)}),
196203 }
197204 }
198205
src/compiler/Tokenizer.zig+123-58
......@@ -146,10 +146,13 @@ pub const Token = struct {
146146 /// Used so that parser does not need to do out of bounds checks
147147 eof,
148148
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 {
151151 return switch (tag) {
152 // do not have a static length
152 .invalid => unreachable,
153
154 // do not have a static lexeme
155 .eof,
153156 .id,
154157 .builtin_id,
155158 .number_int,
......@@ -160,68 +163,86 @@ pub const Token = struct {
160163 .module_doc_comment,
161164 => null,
162165
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 => "&&=",
216216
217217 inline else => |keyword| comptime len: {
218218 if (!mem.startsWith(u8, @tagName(keyword), "kw_")) {
219219 @compileError("Token tag is not a keyword: " ++ @tagName(keyword));
220220 }
221 break :len @tagName(keyword).len - 3;
221 break :len @tagName(keyword)[3..];
222222 },
223223 };
224224 }
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 }
225246 };
226247
227248 /// Instead of storing the length of a token, we can recompute this
......@@ -243,7 +264,8 @@ pub const Token = struct {
243264 }
244265
245266 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)];
247269 }
248270
249271 const keywords = std.ComptimeStringMap(Tag, keywords: {
......@@ -921,6 +943,48 @@ pub fn skipWhitespace(t: *Tokenizer) void {
921943 }
922944}
923945
946pub 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
924988const Tokenizer = @This();
925989const std = @import("std");
926990const unicode = std.unicode;
......@@ -928,3 +992,4 @@ const assert = std.debug.assert;
928992const mem = std.mem;
929993const unicode_props = @import("unicode_props.zig");
930994const T = @import("tracer");
995const Location = @import("Ast.zig").Location;
src/compiler/Transform.zig+26-3
......@@ -603,7 +603,7 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
603603 return .void_value;
604604 };
605605
606 lookup.mutated = true;
606 lookup.used = true;
607607
608608 return switch (lookup.data) {
609609 .declaration => try t.addInstRef(.{
......@@ -1061,6 +1061,7 @@ fn transformBlockStatement(
10611061 const var_decl = t.ast.fullVarDecl(node);
10621062
10631063 if (mem.eql(u8, var_decl.id, "_")) {
1064 // TODO: relax this restriction into just creating a discard node
10641065 try t.report.err(.invalid_discard, .{ .node = node });
10651066 return .void_value;
10661067 }
......@@ -1101,13 +1102,35 @@ fn transformBlockStatement(
11011102 }),
11021103 });
11031104
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.* = .{
11051128 .node = node,
11061129 .data = switch (var_decl.is_const) {
11071130 true => .{ .immutable = mem_ref },
11081131 false => .{ .mutable = mem_ref },
11091132 },
1110 });
1133 };
11111134
11121135 return .void_value;
11131136 },
src/compiler/main.zig+28-13
......@@ -21,7 +21,9 @@ pub fn main() !void {
2121
2222 try unicode_props.init();
2323
24 std.debug.print("\r\x1b[K", .{});
24 if (builtin.mode == .Debug) {
25 std.debug.print("\r\x1b[K", .{});
26 }
2527
2628 const use_gpa = builtin.mode == .Debug;
2729
......@@ -54,14 +56,17 @@ pub fn main() !void {
5456
5557 while (args.next()) |arg| {
5658 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 }
5763 if (std.mem.eql(u8, arg, "--dump-ir")) {
5864 dump_ir = true;
59 } else {
60 std.debug.print("error: unknown flag '{s}'\n", .{arg});
61 maybe_file_path = null;
62 break;
65 continue;
6366 }
64 continue;
67 std.debug.print("error: unknown flag '{s}'\n", .{arg});
68 maybe_file_path = null;
69 break;
6570 }
6671
6772 if (maybe_file_path == null) {
......@@ -77,13 +82,23 @@ pub fn main() !void {
7782
7883 const file_path = maybe_file_path orelse {
7984 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 }
87102 std.process.exit(1);
88103 };
89104