feat: begin work on error labels
This commit is contained in:
parent
9a4942fd78
commit
079e8eb4d4
@ -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];
|
||||
|
@ -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, ¤t_error) catch |e| switch (e) {
|
||||
const number_lex = number.lex(input, input_len, actual_next_pos, ¤t_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;
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user