refactor: migrate error reporting to new compiler context
This commit is contained in:
parent
e330b2905e
commit
c54d19bcf7
@ -93,6 +93,121 @@ pub const ErrorData = struct {
|
||||
self.help = help;
|
||||
}
|
||||
|
||||
/// Generates an error string. `alloc` is used to create the string,
|
||||
/// the caller should call `free` on the returning slice.
|
||||
pub fn get_error_str(self: *ErrorData, source_code: []const u8, filename: []const u8, alloc: std.mem.Allocator) ![]u8 {
|
||||
const faulty_line = get_line(source_code, self.start_position);
|
||||
|
||||
var error_message = try std.fmt.allocPrint(alloc,
|
||||
\\Error: {s}
|
||||
\\[{s}:{d}:{d}]
|
||||
, .{
|
||||
self.reason,
|
||||
filename,
|
||||
faulty_line.line_number,
|
||||
faulty_line.column_number,
|
||||
});
|
||||
errdefer alloc.free(error_message);
|
||||
|
||||
// generate errors for each label, and concat
|
||||
for (self.labels.items) |label| {
|
||||
const label_line = get_line(source_code, label.start);
|
||||
|
||||
// Build the error position indicator
|
||||
const column_number_len_str = try std.fmt.allocPrint(
|
||||
alloc,
|
||||
"{d}",
|
||||
.{label_line.line_number},
|
||||
);
|
||||
const column_number_len = column_number_len_str.len;
|
||||
alloc.free(column_number_len_str);
|
||||
|
||||
// position up to where the error starts
|
||||
const error_start_len = column_number_len + 4 + label_line.column_number - 1;
|
||||
const error_len = label.end - label.start;
|
||||
|
||||
// chars for the error
|
||||
const empty_space_before_indicator = try alloc.alloc(u8, error_start_len);
|
||||
defer alloc.free(empty_space_before_indicator);
|
||||
@memset(empty_space_before_indicator, ' ');
|
||||
|
||||
// top error indicator: unicode box drawing characters in the range U+250x-U+257x (3 bytes)
|
||||
const error_indicator = try alloc.alloc(u8, error_len * 3);
|
||||
defer alloc.free(error_indicator);
|
||||
|
||||
// the first char is always '╭', the rest are lines
|
||||
error_indicator[0] = '\xe2';
|
||||
error_indicator[1] = '\x95';
|
||||
error_indicator[2] = '\xad';
|
||||
|
||||
// set bytes of the rest
|
||||
var i: usize = 1;
|
||||
while (i < error_len) : (i += 1) {
|
||||
// set bytes
|
||||
error_indicator[i * 3 + 0] = '\xe2';
|
||||
error_indicator[i * 3 + 1] = '\x94';
|
||||
error_indicator[i * 3 + 2] = '\x80';
|
||||
}
|
||||
|
||||
// bottom error indicator: always ╰─
|
||||
const bottom_error_indicator = "╰─";
|
||||
|
||||
const help_message: []u8 = msg: {
|
||||
if (self.help) |help_text| {
|
||||
// this will be manually freed later
|
||||
break :msg try std.fmt.allocPrint(alloc, "\n Help: {s}", .{help_text});
|
||||
} else {
|
||||
break :msg "";
|
||||
}
|
||||
};
|
||||
defer if (help_message.len > 0) {
|
||||
alloc.free(help_message);
|
||||
};
|
||||
|
||||
const label_error = try std.fmt.allocPrint(alloc,
|
||||
\\
|
||||
\\
|
||||
\\ {d} | {s}
|
||||
\\{s}{s}
|
||||
\\{s}{s} {s}
|
||||
\\{s}
|
||||
, .{
|
||||
label_line.line_number,
|
||||
label_line.line,
|
||||
empty_space_before_indicator,
|
||||
error_indicator,
|
||||
empty_space_before_indicator,
|
||||
bottom_error_indicator,
|
||||
switch (label.message) {
|
||||
.static => label.message.static,
|
||||
.dynamic => label.message.dynamic,
|
||||
},
|
||||
help_message,
|
||||
});
|
||||
errdefer alloc.free(label_error);
|
||||
|
||||
// append the previous bytes to the current ones,
|
||||
// in a temp variable
|
||||
const new_bytes = try std.mem.concat(alloc, u8, &[_][]const u8{ error_message, label_error });
|
||||
|
||||
// free the previous bytes
|
||||
alloc.free(label_error);
|
||||
alloc.free(error_message);
|
||||
|
||||
// reference the new bytes
|
||||
error_message = new_bytes;
|
||||
|
||||
// continue
|
||||
}
|
||||
|
||||
return error_message;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - transform absolute position into line:column
|
||||
// - Get previous, current and next line
|
||||
// - Display message
|
||||
|
||||
pub fn deinit(self: *ErrorData, allocator: std.mem.Allocator) void {
|
||||
// Clean any labels. Those are assumed to have been initialized
|
||||
// by the same allocator this function receives
|
||||
@ -118,3 +233,50 @@ pub const ErrorLabel = struct {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const LineInfo = struct {
|
||||
line: []const u8,
|
||||
/// 1 based
|
||||
line_number: usize,
|
||||
/// 1 based
|
||||
column_number: usize,
|
||||
};
|
||||
|
||||
fn get_line(input: []const u8, at: usize) LineInfo {
|
||||
var line_number: usize = 1;
|
||||
var line_start: usize = 0;
|
||||
var line_end: usize = 0;
|
||||
var current_pos: usize = 0;
|
||||
const cap = input.len;
|
||||
|
||||
// search the start pos of the line
|
||||
while (current_pos < cap and current_pos < at) : (current_pos += 1) {
|
||||
if (input[current_pos] == '\n' and current_pos + 1 < cap) {
|
||||
line_start = current_pos + 1;
|
||||
line_number += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// compute the column number
|
||||
const column_number: usize = current_pos - line_start + 1;
|
||||
|
||||
// search the end pos of the line
|
||||
while (current_pos < cap) : (current_pos += 1) {
|
||||
// EOF is EOL
|
||||
if (current_pos + 1 == cap) {
|
||||
line_end = current_pos + 1;
|
||||
break;
|
||||
}
|
||||
if (input[current_pos] == '\n') {
|
||||
// dont count the newline as part of the... line
|
||||
line_end = current_pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return .{
|
||||
.line = input[line_start..line_end],
|
||||
.line_number = line_number,
|
||||
.column_number = column_number,
|
||||
};
|
||||
}
|
||||
|
@ -106,9 +106,8 @@ fn repl() !void {
|
||||
}
|
||||
|
||||
// Print errors and continue, if any
|
||||
if (error_array.items.len > 0) {
|
||||
for (error_array.items) |e| {
|
||||
var err = e;
|
||||
if (ctx.errors.items.len > 0) {
|
||||
for (ctx.errors.items) |*err| {
|
||||
const err_str = try err.get_error_str(line, "repl", alloc);
|
||||
try stdout.print("\n{s}\n", .{err_str});
|
||||
try bw.flush();
|
||||
|
Loading…
Reference in New Issue
Block a user