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;
|
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 {
|
pub fn deinit(self: *ErrorData, allocator: std.mem.Allocator) void {
|
||||||
// Clean any labels. Those are assumed to have been initialized
|
// Clean any labels. Those are assumed to have been initialized
|
||||||
// by the same allocator this function receives
|
// 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
|
// Print errors and continue, if any
|
||||||
if (error_array.items.len > 0) {
|
if (ctx.errors.items.len > 0) {
|
||||||
for (error_array.items) |e| {
|
for (ctx.errors.items) |*err| {
|
||||||
var err = e;
|
|
||||||
const err_str = try err.get_error_str(line, "repl", alloc);
|
const err_str = try err.get_error_str(line, "repl", alloc);
|
||||||
try stdout.print("\n{s}\n", .{err_str});
|
try stdout.print("\n{s}\n", .{err_str});
|
||||||
try bw.flush();
|
try bw.flush();
|
||||||
|
Loading…
Reference in New Issue
Block a user