feat: begin work on error labels
This commit is contained in:
parent
9a4942fd78
commit
079e8eb4d4
@ -19,6 +19,7 @@ pub fn lex(
|
|||||||
cap: usize,
|
cap: usize,
|
||||||
start: usize,
|
start: usize,
|
||||||
err: *errors.ErrorData,
|
err: *errors.ErrorData,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
) LexError!?LexReturn {
|
) LexError!?LexReturn {
|
||||||
assert(start < cap);
|
assert(start < cap);
|
||||||
const first_char = input[start];
|
const first_char = input[start];
|
||||||
@ -27,9 +28,9 @@ pub fn lex(
|
|||||||
if (first_char == '0' and cap > start + 1) {
|
if (first_char == '0' and cap > start + 1) {
|
||||||
const second_char = input[start + 1];
|
const second_char = input[start + 1];
|
||||||
switch (second_char) {
|
switch (second_char) {
|
||||||
'x', 'X' => return prefixed('x', input, cap, start, err),
|
'x', 'X' => return prefixed('x', input, cap, start, err, alloc),
|
||||||
'o', 'O' => return prefixed('o', input, cap, start, err),
|
'o', 'O' => return prefixed('o', input, cap, start, err, alloc),
|
||||||
'b', 'B' => return prefixed('b', input, cap, start, err),
|
'b', 'B' => return prefixed('b', input, cap, start, err, alloc),
|
||||||
else => {
|
else => {
|
||||||
// Continue
|
// Continue
|
||||||
},
|
},
|
||||||
@ -51,6 +52,7 @@ fn prefixed(
|
|||||||
cap: usize,
|
cap: usize,
|
||||||
start: usize,
|
start: usize,
|
||||||
err: *errors.ErrorData,
|
err: *errors.ErrorData,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
) !?LexReturn {
|
) !?LexReturn {
|
||||||
const validator = switch (prefix) {
|
const validator = switch (prefix) {
|
||||||
'x' => utils.is_hex_digit,
|
'x' => utils.is_hex_digit,
|
||||||
@ -64,7 +66,7 @@ fn prefixed(
|
|||||||
// There should be at least 1 valid digit
|
// There should be at least 1 valid digit
|
||||||
if (end_position >= cap or !validator(input[end_position])) {
|
if (end_position >= cap or !validator(input[end_position])) {
|
||||||
// populate error information
|
// populate error information
|
||||||
err.init("Incomplete number", start, end_position);
|
try err.init("Incomplete number", start, end_position, alloc);
|
||||||
|
|
||||||
// throw error
|
// throw error
|
||||||
return LexError.Incomplete;
|
return LexError.Incomplete;
|
||||||
@ -254,7 +256,7 @@ test "should return null if not an integer" {
|
|||||||
|
|
||||||
test "should lex hex number" {
|
test "should lex hex number" {
|
||||||
const input = "0xa";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -268,7 +270,7 @@ test "should fail on integer with leading zero" {
|
|||||||
const input = "0322";
|
const input = "0322";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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);
|
try std.testing.expect(err == token.LexError.LeadingZero);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -285,7 +287,7 @@ test "should fail on integer with leading zero" {
|
|||||||
|
|
||||||
test "should lex hex number 2" {
|
test "should lex hex number 2" {
|
||||||
const input = " 0Xff00AA ";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -299,7 +301,7 @@ test "shouldnt parse incomplete hex number" {
|
|||||||
const input = "0xZZ";
|
const input = "0xZZ";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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);
|
try std.testing.expect(err == token.LexError.Incomplete);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -318,7 +320,7 @@ test "shouldnt parse incomplete hex number 2" {
|
|||||||
const input = "0x";
|
const input = "0x";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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);
|
try std.testing.expect(err == token.LexError.Incomplete);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -335,7 +337,7 @@ test "shouldnt parse incomplete hex number 2" {
|
|||||||
|
|
||||||
test "should lex octal number" {
|
test "should lex octal number" {
|
||||||
const input = "0o755";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -347,7 +349,7 @@ test "should lex octal number" {
|
|||||||
|
|
||||||
test "should lex octal number 2" {
|
test "should lex octal number 2" {
|
||||||
const input = " 0o755 ";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -361,7 +363,7 @@ test "shouldnt parse incomplete octal number" {
|
|||||||
const input = "0o8";
|
const input = "0o8";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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);
|
try std.testing.expect(err == token.LexError.Incomplete);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -378,7 +380,7 @@ test "shouldnt parse incomplete octal number" {
|
|||||||
|
|
||||||
test "should lex binary number" {
|
test "should lex binary number" {
|
||||||
const input = "0b1011";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -392,7 +394,7 @@ test "shouldnt parse incomplete binary number" {
|
|||||||
const input = "0b2";
|
const input = "0b2";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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);
|
try std.testing.expect(err == token.LexError.Incomplete);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -409,7 +411,7 @@ test "shouldnt parse incomplete binary number" {
|
|||||||
|
|
||||||
test "should lex fp number 1" {
|
test "should lex fp number 1" {
|
||||||
const input = "1.2";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -423,7 +425,7 @@ test "should lex fp number 2" {
|
|||||||
const input = "0.1";
|
const input = "0.1";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -435,7 +437,7 @@ test "should lex fp number 2" {
|
|||||||
|
|
||||||
test "should lex fp number 3" {
|
test "should lex fp number 3" {
|
||||||
const input = "123.456";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -449,7 +451,7 @@ test "should fail on incomplete fp number" {
|
|||||||
const input = "123.";
|
const input = "123.";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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);
|
try std.testing.expect(err == token.LexError.IncompleteFloatingNumber);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -466,7 +468,7 @@ test "should fail on incomplete fp number" {
|
|||||||
|
|
||||||
test "should lex scientific number" {
|
test "should lex scientific number" {
|
||||||
const input = "42e+3";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
@ -480,7 +482,7 @@ test "should fail on incomplete scientific number" {
|
|||||||
const input = "123e";
|
const input = "123e";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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);
|
try std.testing.expect(err == token.LexError.IncompleteScientificNumber);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -499,7 +501,7 @@ test "should fail on incomplete scientific number 2" {
|
|||||||
const input = "123e+";
|
const input = "123e+";
|
||||||
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
const errdata = try std.testing.allocator.create(errors.ErrorData);
|
||||||
defer std.testing.allocator.destroy(errdata);
|
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);
|
try std.testing.expect(err == token.LexError.IncompleteScientificNumber);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -516,7 +518,7 @@ test "should fail on incomplete scientific number 2" {
|
|||||||
|
|
||||||
test "should lex floating scientific number" {
|
test "should lex floating scientific number" {
|
||||||
const input = "0.58e+3";
|
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| {
|
if (result) |tuple| {
|
||||||
const r = tuple[0];
|
const r = tuple[0];
|
||||||
|
@ -43,7 +43,7 @@ pub fn tokenize(
|
|||||||
|
|
||||||
// attempt to lex a number
|
// attempt to lex a number
|
||||||
var current_error: errors.ErrorData = undefined;
|
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
|
// recoverable errors
|
||||||
LexError.Incomplete => {
|
LexError.Incomplete => {
|
||||||
// add to list of errors
|
// add to list of errors
|
||||||
@ -126,7 +126,7 @@ pub fn tokenize(
|
|||||||
else {
|
else {
|
||||||
// Create an error "nothing matched" and continue lexing
|
// Create an error "nothing matched" and continue lexing
|
||||||
// after the whitespace
|
// 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);
|
try err_arrl.append(current_error);
|
||||||
current_pos = ignore_until_whitespace(input, actual_next_pos);
|
current_pos = ignore_until_whitespace(input, actual_next_pos);
|
||||||
continue;
|
continue;
|
||||||
|
@ -48,10 +48,11 @@ pub const Module = struct {
|
|||||||
switch (e) {
|
switch (e) {
|
||||||
error.Unmatched => {
|
error.Unmatched => {
|
||||||
// create the error value
|
// create the error value
|
||||||
error_target.init(
|
try error_target.init(
|
||||||
"No statement found",
|
"No statement found",
|
||||||
current_pos,
|
current_pos,
|
||||||
current_pos + 1,
|
current_pos + 1,
|
||||||
|
allocator,
|
||||||
);
|
);
|
||||||
return error.Unmatched;
|
return error.Unmatched;
|
||||||
},
|
},
|
||||||
|
@ -1,22 +1,33 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const ErrorLabel = struct {
|
||||||
|
message: []const u8,
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
};
|
||||||
|
|
||||||
/// Holds information about errors generated during the compilation,
|
/// Holds information about errors generated during the compilation,
|
||||||
/// and pretty prints them.
|
/// and pretty prints them.
|
||||||
pub const ErrorData = struct {
|
pub const ErrorData = struct {
|
||||||
reason: []const u8,
|
reason: []const u8,
|
||||||
start_position: usize,
|
start_position: usize,
|
||||||
end_position: usize,
|
end_position: usize,
|
||||||
|
labels: std.ArrayList(ErrorLabel),
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
target: *@This(),
|
target: *@This(),
|
||||||
reason: []const u8,
|
reason: []const u8,
|
||||||
start_position: usize,
|
start_position: usize,
|
||||||
end_position: usize,
|
end_position: usize,
|
||||||
) void {
|
alloc: std.mem.Allocator,
|
||||||
|
) !void {
|
||||||
target.* = .{
|
target.* = .{
|
||||||
.reason = reason,
|
.reason = reason,
|
||||||
.start_position = start_position,
|
.start_position = start_position,
|
||||||
.end_position = end_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,
|
const error_message = try std.fmt.allocPrint(alloc,
|
||||||
\\Error: {s}
|
\\Error: {s}
|
||||||
\\[{s}:{d}:{d}]
|
\\[{s}:{d}:{d}]
|
||||||
\\
|
|
||||||
\\ {d} | {s}
|
|
||||||
, .{
|
, .{
|
||||||
self.reason,
|
self.reason,
|
||||||
filename,
|
filename,
|
||||||
faulty_line.line_number,
|
faulty_line.line_number,
|
||||||
faulty_line.column_number,
|
faulty_line.column_number,
|
||||||
faulty_line.line_number,
|
|
||||||
faulty_line.line,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return error_message;
|
return error_message;
|
||||||
@ -47,9 +54,8 @@ pub const ErrorData = struct {
|
|||||||
// - Get previous, current and next line
|
// - Get previous, current and next line
|
||||||
// - Display message
|
// - Display message
|
||||||
|
|
||||||
/// Does nothing at the moment
|
|
||||||
pub fn deinit(self: *@This()) void {
|
pub fn deinit(self: *@This()) void {
|
||||||
_ = self;
|
self.labels.deinit(self.alloc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -150,6 +156,8 @@ test "should gen error message" {
|
|||||||
.reason = "Invalid identifier",
|
.reason = "Invalid identifier",
|
||||||
.start_position = 6,
|
.start_position = 6,
|
||||||
.end_position = 9,
|
.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);
|
const out = try err.get_error_str(source, "repl", std.testing.allocator);
|
||||||
defer std.testing.allocator.free(out);
|
defer std.testing.allocator.free(out);
|
||||||
@ -157,7 +165,5 @@ test "should gen error message" {
|
|||||||
try std.testing.expectEqualStrings(
|
try std.testing.expectEqualStrings(
|
||||||
\\Error: Invalid identifier
|
\\Error: Invalid identifier
|
||||||
\\[repl:1:7]
|
\\[repl:1:7]
|
||||||
\\
|
|
||||||
\\ 1 | print(ehh)
|
|
||||||
, out);
|
, out);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user