diff options
| author | Andrea Orru <andrea@orru.io> | 2018-08-06 01:43:19 -0400 |
|---|---|---|
| committer | Andrea Orru <andrea@orru.io> | 2018-08-06 01:43:19 -0400 |
| commit | d2f5e57b68da0b16e5789ca19045ccbcb4ecfa8d (patch) | |
| tree | e9fa3caec533a0d1e2b434868b2fde1f9240e5c8 /src-self-hosted/errmsg.zig | |
| parent | 06614b3fa09954464c2e2f32756cacedc178a282 (diff) | |
| parent | 63a23e848a62d5f167f8d5478de9766cb24aa6eb (diff) | |
| download | zig-d2f5e57b68da0b16e5789ca19045ccbcb4ecfa8d.tar.gz zig-d2f5e57b68da0b16e5789ca19045ccbcb4ecfa8d.zip | |
Merge branch 'master' into zen_stdlib
Diffstat (limited to 'src-self-hosted/errmsg.zig')
| -rw-r--r-- | src-self-hosted/errmsg.zig | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig new file mode 100644 index 0000000000..51e135686a --- /dev/null +++ b/src-self-hosted/errmsg.zig @@ -0,0 +1,237 @@ +const std = @import("std"); +const mem = std.mem; +const os = std.os; +const Token = std.zig.Token; +const ast = std.zig.ast; +const TokenIndex = std.zig.ast.TokenIndex; +const Compilation = @import("compilation.zig").Compilation; +const Scope = @import("scope.zig").Scope; + +pub const Color = enum { + Auto, + Off, + On, +}; + +pub const Span = struct { + first: ast.TokenIndex, + last: ast.TokenIndex, + + pub fn token(i: TokenIndex) Span { + return Span{ + .first = i, + .last = i, + }; + } + + pub fn node(n: *ast.Node) Span { + return Span{ + .first = n.firstToken(), + .last = n.lastToken(), + }; + } +}; + +pub const Msg = struct { + span: Span, + text: []u8, + data: Data, + + const Data = union(enum) { + PathAndTree: PathAndTree, + ScopeAndComp: ScopeAndComp, + }; + + const PathAndTree = struct { + realpath: []const u8, + tree: *ast.Tree, + allocator: *mem.Allocator, + }; + + const ScopeAndComp = struct { + root_scope: *Scope.Root, + compilation: *Compilation, + }; + + pub fn destroy(self: *Msg) void { + switch (self.data) { + Data.PathAndTree => |path_and_tree| { + path_and_tree.allocator.free(self.text); + path_and_tree.allocator.destroy(self); + }, + Data.ScopeAndComp => |scope_and_comp| { + scope_and_comp.root_scope.base.deref(scope_and_comp.compilation); + scope_and_comp.compilation.gpa().free(self.text); + scope_and_comp.compilation.gpa().destroy(self); + }, + } + } + + fn getAllocator(self: *const Msg) *mem.Allocator { + switch (self.data) { + Data.PathAndTree => |path_and_tree| { + return path_and_tree.allocator; + }, + Data.ScopeAndComp => |scope_and_comp| { + return scope_and_comp.compilation.gpa(); + }, + } + } + + pub fn getRealPath(self: *const Msg) []const u8 { + switch (self.data) { + Data.PathAndTree => |path_and_tree| { + return path_and_tree.realpath; + }, + Data.ScopeAndComp => |scope_and_comp| { + return scope_and_comp.root_scope.realpath; + }, + } + } + + pub fn getTree(self: *const Msg) *ast.Tree { + switch (self.data) { + Data.PathAndTree => |path_and_tree| { + return path_and_tree.tree; + }, + Data.ScopeAndComp => |scope_and_comp| { + return scope_and_comp.root_scope.tree; + }, + } + } + + /// Takes ownership of text + /// References root_scope, and derefs when the msg is freed + pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg { + const msg = try comp.gpa().create(Msg{ + .text = text, + .span = span, + .data = Data{ + .ScopeAndComp = ScopeAndComp{ + .root_scope = root_scope, + .compilation = comp, + }, + }, + }); + root_scope.base.ref(); + return msg; + } + + pub fn createFromParseErrorAndScope( + comp: *Compilation, + root_scope: *Scope.Root, + parse_error: *const ast.Error, + ) !*Msg { + const loc_token = parse_error.loc(); + var text_buf = try std.Buffer.initSize(comp.gpa(), 0); + defer text_buf.deinit(); + + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&root_scope.tree.tokens, out_stream); + + const msg = try comp.gpa().create(Msg{ + .text = undefined, + .span = Span{ + .first = loc_token, + .last = loc_token, + }, + .data = Data{ + .ScopeAndComp = ScopeAndComp{ + .root_scope = root_scope, + .compilation = comp, + }, + }, + }); + root_scope.base.ref(); + msg.text = text_buf.toOwnedSlice(); + return msg; + } + + /// `realpath` must outlive the returned Msg + /// `tree` must outlive the returned Msg + /// Caller owns returned Msg and must free with `allocator` + /// allocator will additionally be used for printing messages later. + pub fn createFromParseError( + allocator: *mem.Allocator, + parse_error: *const ast.Error, + tree: *ast.Tree, + realpath: []const u8, + ) !*Msg { + const loc_token = parse_error.loc(); + var text_buf = try std.Buffer.initSize(allocator, 0); + defer text_buf.deinit(); + + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&tree.tokens, out_stream); + + const msg = try allocator.create(Msg{ + .text = undefined, + .data = Data{ + .PathAndTree = PathAndTree{ + .allocator = allocator, + .realpath = realpath, + .tree = tree, + }, + }, + .span = Span{ + .first = loc_token, + .last = loc_token, + }, + }); + msg.text = text_buf.toOwnedSlice(); + errdefer allocator.destroy(msg); + + return msg; + } + + pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void { + const allocator = msg.getAllocator(); + const realpath = msg.getRealPath(); + const tree = msg.getTree(); + + const cwd = try os.getCwd(allocator); + defer allocator.free(cwd); + + const relpath = try os.path.relative(allocator, cwd, realpath); + defer allocator.free(relpath); + + const path = if (relpath.len < realpath.len) relpath else realpath; + + const first_token = tree.tokens.at(msg.span.first); + const last_token = tree.tokens.at(msg.span.last); + const start_loc = tree.tokenLocationPtr(0, first_token); + const end_loc = tree.tokenLocationPtr(first_token.end, last_token); + if (!color_on) { + try stream.print( + "{}:{}:{}: error: {}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + msg.text, + ); + return; + } + + try stream.print( + "{}:{}:{}: error: {}\n{}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + msg.text, + tree.source[start_loc.line_start..start_loc.line_end], + ); + try stream.writeByteNTimes(' ', start_loc.column); + try stream.writeByteNTimes('~', last_token.end - first_token.start); + try stream.write("\n"); + } + + pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void { + const color_on = switch (color) { + Color.Auto => file.isTty(), + Color.On => true, + Color.Off => false, + }; + var stream = &std.io.FileOutStream.init(file).stream; + return msg.printToStream(stream, color_on); + } +}; |
