feat: begin work on error labels

This commit is contained in:
Fernando Araoz 2024-12-25 06:46:42 -05:00
parent 9a4942fd78
commit 079e8eb4d4
4 changed files with 43 additions and 34 deletions

View File

@ -19,6 +19,7 @@ pub fn lex(
cap: usize,
start: usize,
err: *errors.ErrorData,
alloc: std.mem.Allocator,
) LexError!?LexReturn {
assert(start < cap);
const first_char = input[start];
@ -27,9 +28,9 @@ pub fn lex(
if (first_char == '0' and cap > start + 1) {
const second_char = input[start + 1];
switch (second_char) {
'x', 'X' => return prefixed('x', input, cap, start, err),
'o', 'O' => return prefixed('o', input, cap, start, err),
'b', 'B' => return prefixed('b', input, cap, start, err),
'x', 'X' => return prefixed('x', input, cap, start, err, alloc),
'o', 'O' => return prefixed('o', input, cap, start, err, alloc),
'b', 'B' => return prefixed('b', input, cap, start, err, alloc),
else => {
// Continue
},
@ -51,6 +52,7 @@ fn prefixed(
cap: usize,
start: usize,
err: *errors.ErrorData,
alloc: std.mem.Allocator,
) !?LexReturn {
const validator = switch (prefix) {
'x' => utils.is_hex_digit,
@ -64,7 +66,7 @@ fn prefixed(
// There should be at least 1 valid digit
if (end_position >= cap or !validator(input[end_position])) {
// populate error information
err.init("Incomplete number", start, end_position);
try err.init("Incomplete number", start, end_position, alloc);
// throw error
return LexError.Incomplete;
@ -254,7 +256,7 @@ test "should return null if not an integer" {
test "should lex hex number" {
const input = "0xa";
const result = try lex(input, input.len, 0, undefined);
const result = try lex(input, input.len, 0, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -268,7 +270,7 @@ test "should fail on integer with leading zero" {
const input = "0322";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = lex(input, input.len, 0, errdata) catch |err| {
const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| {
try std.testing.expect(err == token.LexError.LeadingZero);
return;
};
@ -285,7 +287,7 @@ test "should fail on integer with leading zero" {
test "should lex hex number 2" {
const input = " 0Xff00AA ";
const result = try lex(input, input.len, 2, undefined);
const result = try lex(input, input.len, 2, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -299,7 +301,7 @@ test "shouldnt parse incomplete hex number" {
const input = "0xZZ";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = lex(input, input.len, 0, errdata) catch |err| {
const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| {
try std.testing.expect(err == token.LexError.Incomplete);
return;
};
@ -318,7 +320,7 @@ test "shouldnt parse incomplete hex number 2" {
const input = "0x";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = lex(input, input.len, 0, errdata) catch |err| {
const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| {
try std.testing.expect(err == token.LexError.Incomplete);
return;
};
@ -335,7 +337,7 @@ test "shouldnt parse incomplete hex number 2" {
test "should lex octal number" {
const input = "0o755";
const result = try lex(input, input.len, 0, undefined);
const result = try lex(input, input.len, 0, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -347,7 +349,7 @@ test "should lex octal number" {
test "should lex octal number 2" {
const input = " 0o755 ";
const result = try lex(input, input.len, 2, undefined);
const result = try lex(input, input.len, 2, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -361,7 +363,7 @@ test "shouldnt parse incomplete octal number" {
const input = "0o8";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = lex(input, input.len, 0, errdata) catch |err| {
const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| {
try std.testing.expect(err == token.LexError.Incomplete);
return;
};
@ -378,7 +380,7 @@ test "shouldnt parse incomplete octal number" {
test "should lex binary number" {
const input = "0b1011";
const result = try lex(input, input.len, 0, undefined);
const result = try lex(input, input.len, 0, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -392,7 +394,7 @@ test "shouldnt parse incomplete binary number" {
const input = "0b2";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = lex(input, input.len, 0, errdata) catch |err| {
const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| {
try std.testing.expect(err == token.LexError.Incomplete);
return;
};
@ -409,7 +411,7 @@ test "shouldnt parse incomplete binary number" {
test "should lex fp number 1" {
const input = "1.2";
const result = try lex(input, input.len, 0, undefined);
const result = try lex(input, input.len, 0, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -423,7 +425,7 @@ test "should lex fp number 2" {
const input = "0.1";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = try lex(input, input.len, 0, errdata);
const result = try lex(input, input.len, 0, errdata, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -435,7 +437,7 @@ test "should lex fp number 2" {
test "should lex fp number 3" {
const input = "123.456";
const result = try lex(input, input.len, 0, undefined);
const result = try lex(input, input.len, 0, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -449,7 +451,7 @@ test "should fail on incomplete fp number" {
const input = "123.";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = lex(input, input.len, 0, errdata) catch |err| {
const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| {
try std.testing.expect(err == token.LexError.IncompleteFloatingNumber);
return;
};
@ -466,7 +468,7 @@ test "should fail on incomplete fp number" {
test "should lex scientific number" {
const input = "42e+3";
const result = try lex(input, input.len, 0, undefined);
const result = try lex(input, input.len, 0, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];
@ -480,7 +482,7 @@ test "should fail on incomplete scientific number" {
const input = "123e";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = lex(input, input.len, 0, errdata) catch |err| {
const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| {
try std.testing.expect(err == token.LexError.IncompleteScientificNumber);
return;
};
@ -499,7 +501,7 @@ test "should fail on incomplete scientific number 2" {
const input = "123e+";
const errdata = try std.testing.allocator.create(errors.ErrorData);
defer std.testing.allocator.destroy(errdata);
const result = lex(input, input.len, 0, errdata) catch |err| {
const result = lex(input, input.len, 0, errdata, std.testing.allocator) catch |err| {
try std.testing.expect(err == token.LexError.IncompleteScientificNumber);
return;
};
@ -516,7 +518,7 @@ test "should fail on incomplete scientific number 2" {
test "should lex floating scientific number" {
const input = "0.58e+3";
const result = try lex(input, input.len, 0, undefined);
const result = try lex(input, input.len, 0, undefined, std.testing.allocator);
if (result) |tuple| {
const r = tuple[0];

View File

@ -43,7 +43,7 @@ pub fn tokenize(
// attempt to lex a number
var current_error: errors.ErrorData = undefined;
const number_lex = number.lex(input, input_len, actual_next_pos, &current_error) catch |e| switch (e) {
const number_lex = number.lex(input, input_len, actual_next_pos, &current_error, alloc) catch |e| switch (e) {
// recoverable errors
LexError.Incomplete => {
// add to list of errors
@ -126,7 +126,7 @@ pub fn tokenize(
else {
// Create an error "nothing matched" and continue lexing
// after the whitespace
current_error.init("Unrecognized character", actual_next_pos, actual_next_pos + 1);
try current_error.init("Unrecognized character", actual_next_pos, actual_next_pos + 1, alloc);
try err_arrl.append(current_error);
current_pos = ignore_until_whitespace(input, actual_next_pos);
continue;

View File

@ -48,10 +48,11 @@ pub const Module = struct {
switch (e) {
error.Unmatched => {
// create the error value
error_target.init(
try error_target.init(
"No statement found",
current_pos,
current_pos + 1,
allocator,
);
return error.Unmatched;
},

View File

@ -1,22 +1,33 @@
const std = @import("std");
pub const ErrorLabel = struct {
message: []const u8,
start: usize,
end: usize,
};
/// Holds information about errors generated during the compilation,
/// and pretty prints them.
pub const ErrorData = struct {
reason: []const u8,
start_position: usize,
end_position: usize,
labels: std.ArrayList(ErrorLabel),
alloc: std.mem.Allocator,
pub fn init(
target: *@This(),
reason: []const u8,
start_position: usize,
end_position: usize,
) void {
alloc: std.mem.Allocator,
) !void {
target.* = .{
.reason = reason,
.start_position = start_position,
.end_position = end_position,
.labels = std.ArrayList(ErrorLabel).init(alloc),
.alloc = alloc,
};
}
@ -28,15 +39,11 @@ pub const ErrorData = struct {
const error_message = try std.fmt.allocPrint(alloc,
\\Error: {s}
\\[{s}:{d}:{d}]
\\
\\ {d} | {s}
, .{
self.reason,
filename,
faulty_line.line_number,
faulty_line.column_number,
faulty_line.line_number,
faulty_line.line,
});
return error_message;
@ -47,9 +54,8 @@ pub const ErrorData = struct {
// - Get previous, current and next line
// - Display message
/// Does nothing at the moment
pub fn deinit(self: *@This()) void {
_ = self;
self.labels.deinit(self.alloc);
}
};
@ -150,6 +156,8 @@ test "should gen error message" {
.reason = "Invalid identifier",
.start_position = 6,
.end_position = 9,
.labels = std.ArrayList(ErrorLabel).init(std.testing.allocator),
.alloc = std.testing.allocator,
};
const out = try err.get_error_str(source, "repl", std.testing.allocator);
defer std.testing.allocator.free(out);
@ -157,7 +165,5 @@ test "should gen error message" {
try std.testing.expectEqualStrings(
\\Error: Invalid identifier
\\[repl:1:7]
\\
\\ 1 | print(ehh)
, out);
}