refactor: new direction for error reporting

This commit is contained in:
Brett Broadhurst 2026-03-18 17:52:07 -06:00
parent 72b686d750
commit c940374f27
Failed to generate hash of commit
11 changed files with 758 additions and 582 deletions

View file

@ -123,62 +123,6 @@ fn nodeTagToString(tag: Ast.Node.Tag) []const u8 {
};
}
fn makeErrorInfo(r: *Render, err: Ast.Error, message: []const u8) ErrorInfo {
const line_index = r.lines.calculateLine(err.loc.start);
const snippet_range = r.lines.ranges.items[line_index];
const column = err.loc.start - snippet_range.start;
const snippet = r.tree.source[snippet_range.start..snippet_range.end];
return ErrorInfo{
.filename = r.tree.filename,
.message = message,
.line = line_index,
.column = column,
.snippet = snippet,
};
}
fn renderErrorf(r: *Render, writer: *std.Io.Writer, err: Ast.Error, message: []const u8) !void {
const error_info = makeErrorInfo(r, err, message);
const filename = error_info.filename;
const line = error_info.line + 1;
const col = error_info.column + 1;
const snippet = error_info.message;
try writer.print("{s}:{d}:{d}: error: {s}\n", .{ filename, line, col, snippet });
try writer.print("{d:<4} | {s}\n", .{ line, error_info.snippet });
try writer.writeAll(" | ");
var i: usize = 0;
while (i < col) : (i += 1) {
try writer.writeByte(' ');
}
try writer.writeByte('^');
try writer.writeAll("\n\n");
}
fn renderError(r: *Render, writer: *std.Io.Writer, err: Ast.Error) !void {
switch (err.tag) {
.panic => try renderErrorf(r, writer, err, "parser panicked"),
.unknown_identifier => try renderErrorf(r, writer, err, "unknown identifier"),
.redefined_identifier => try renderErrorf(r, writer, err, "redefined identifier"),
.assignment_to_const => try renderErrorf(r, writer, err, "assignment to constant value"),
.unexpected_token => try renderErrorf(r, writer, err, "unexpected token"),
.expected_newline => try renderErrorf(r, writer, err, "expected newline"),
.expected_double_quote => try renderErrorf(r, writer, err, "unterminated string, expected closing quote"),
.expected_identifier => try renderErrorf(r, writer, err, "expected identifier"),
.expected_expression => try renderErrorf(r, writer, err, "expected expression"),
.invalid_expression => try renderErrorf(r, writer, err, "invalid expression"),
.invalid_lvalue => try renderErrorf(r, writer, err, "invalid lvalue for assignment"),
.too_many_arguments => try renderErrorf(r, writer, err, "too many arguments to '{s}'"),
.too_many_parameters => try renderErrorf(r, writer, err, "too many parameters defined for '{s}'"),
.unexpected_else_stmt => try renderErrorf(r, writer, err, "unexpected else stmt"),
.invalid_else_stmt => try renderErrorf(r, writer, err, "invalid else stmt"),
.invalid_switch_case => try renderErrorf(r, writer, err, "invalid switch case expression"),
}
}
const Prefix = struct {
buf: std.ArrayListUnmanaged(u8) = .empty,
@ -211,10 +155,8 @@ fn writeType(r: *Render, writer: *std.Io.Writer, node: *const Ast.Node) !void {
}
fn writeLexeme(r: *Render, writer: *std.Io.Writer, node: *const Ast.Node) !void {
const bytes = r.tree.source;
const lexeme = bytes[node.loc.start..node.loc.end];
try r.tty_config.setColor(writer, .yellow);
try writer.print("`{s}`", .{lexeme});
try writer.print("`{s}`", .{r.tree.nodeSlice(node)});
try r.tty_config.setColor(writer, .reset);
}
@ -549,23 +491,3 @@ pub fn renderTree(
}
try writer.flush();
}
pub fn renderErrors(
gpa: std.mem.Allocator,
writer: *std.Io.Writer,
ast: *const Ast,
options: Options,
) !void {
var r: Render = .{
.gpa = gpa,
.tree = ast,
.tty_config = if (options.use_color) .escape_codes else .no_color,
.prefix = .{},
.lines = try LineCache.build(gpa, ast.source),
};
defer r.prefix.deinit(gpa);
defer r.lines.deinit(gpa);
for (ast.errors) |err| try r.renderError(writer, err);
try writer.flush();
}