authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-19 03:11:59-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-19 03:11:59-07:00
logc1d54bf8b06f71d81e8614572483a69720da4ea6
tree10775f14eb33a1b28aecbc4558bb5214ed732b94
parent634a70376c0023f46a6aae6611ed672397eae557
signature Commit is signed but in an unrecognized format.

better error handling


15 files changed, 960 insertions(+), 287 deletions(-)

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