feat: minimal error handling
This commit is contained in:
parent
fb30e1195e
commit
75002582ba
16
build.zig
16
build.zig
@ -22,6 +22,16 @@ pub fn build(b: *std.Build) void {
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
//
|
||||
// Error handling module
|
||||
//
|
||||
const error_module = b.addModule("errors", .{
|
||||
.root_source_file = b.path("src/errors/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
exe.root_module.addImport("errors", error_module);
|
||||
|
||||
//
|
||||
// Lexic module
|
||||
//
|
||||
@ -31,6 +41,7 @@ pub fn build(b: *std.Build) void {
|
||||
.optimize = optimize,
|
||||
});
|
||||
exe.root_module.addImport("lexic", lexic_module);
|
||||
lexic_module.addImport("errors", error_module);
|
||||
|
||||
//
|
||||
// Syntax module
|
||||
@ -40,8 +51,9 @@ pub fn build(b: *std.Build) void {
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
syntax_module.addImport("lexic", lexic_module);
|
||||
exe.root_module.addImport("syntax", syntax_module);
|
||||
syntax_module.addImport("lexic", lexic_module);
|
||||
syntax_module.addImport("errors", error_module);
|
||||
|
||||
// This declares intent for the executable to be installed into the
|
||||
// standard location when the user invokes the "install" step (the default
|
||||
@ -80,6 +92,7 @@ pub fn build(b: *std.Build) void {
|
||||
});
|
||||
exe_unit_tests.root_module.addImport("lexic", lexic_module);
|
||||
exe_unit_tests.root_module.addImport("syntax", syntax_module);
|
||||
exe_unit_tests.root_module.addImport("errors", error_module);
|
||||
|
||||
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||
|
||||
@ -102,6 +115,7 @@ pub fn build(b: *std.Build) void {
|
||||
});
|
||||
file_unit_test.root_module.addImport("lexic", lexic_module);
|
||||
file_unit_test.root_module.addImport("syntax", syntax_module);
|
||||
file_unit_test.root_module.addImport("errors", error_module);
|
||||
|
||||
var test_artifact = b.addRunArtifact(file_unit_test);
|
||||
test_step.dependOn(&test_artifact.step);
|
||||
|
@ -100,7 +100,6 @@ pub fn tokenize(input: []const u8, alloc: std.mem.Allocator) !std.ArrayList(Toke
|
||||
// TODO: check if this is a good error recovery strategy
|
||||
else {
|
||||
// no lexer matched
|
||||
std.debug.print("unmatched args: anytype:c\n", .{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
const std = @import("std");
|
||||
const lexic = @import("lexic");
|
||||
const errors = @import("errors");
|
||||
|
||||
const expression = @import("./expression.zig");
|
||||
const variable = @import("./variable.zig");
|
||||
const types = @import("./types.zig");
|
||||
@ -14,7 +16,19 @@ pub const Module = struct {
|
||||
statements: std.ArrayList(*statement.Statement),
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
pub fn init(target: *@This(), tokens: *const TokenStream, pos: usize, allocator: std.mem.Allocator) ParseError!void {
|
||||
/// Parses a module.
|
||||
///
|
||||
/// If this function fails an error will be returned, and additionally the out parameter
|
||||
/// `error_target` will be populated. If the error returned is OOM, nothing will be there.
|
||||
/// In that case, the caller is responsible for calling the error `deinit` method,
|
||||
/// which will clean it.
|
||||
pub fn init(
|
||||
target: *@This(),
|
||||
tokens: *const TokenStream,
|
||||
pos: usize,
|
||||
allocator: std.mem.Allocator,
|
||||
error_target: *errors.ErrorData,
|
||||
) ParseError!void {
|
||||
var arrl = std.ArrayList(*statement.Statement).init(allocator);
|
||||
errdefer arrl.deinit();
|
||||
errdefer for (arrl.items) |i| {
|
||||
@ -30,7 +44,20 @@ pub const Module = struct {
|
||||
var stmt = try allocator.create(statement.Statement);
|
||||
errdefer allocator.destroy(stmt);
|
||||
|
||||
const next_pos = try stmt.init(tokens, current_pos, allocator);
|
||||
const next_pos = stmt.init(tokens, current_pos, allocator) catch |e| {
|
||||
switch (e) {
|
||||
error.Unmatched => {
|
||||
// create the error value
|
||||
try error_target.init(
|
||||
"No statement found",
|
||||
current_pos,
|
||||
current_pos + 1,
|
||||
);
|
||||
return error.Unmatched;
|
||||
},
|
||||
else => return e,
|
||||
}
|
||||
};
|
||||
current_pos = next_pos;
|
||||
|
||||
try arrl.append(stmt);
|
||||
@ -60,8 +87,11 @@ test "should parse a single statement" {
|
||||
const tokens = try lexic.tokenize(input, std.testing.allocator);
|
||||
defer tokens.deinit();
|
||||
|
||||
const error_target = try std.testing.allocator.create(errors.ErrorData);
|
||||
defer std.testing.allocator.destroy(error_target);
|
||||
|
||||
var module: Module = undefined;
|
||||
_ = try module.init(&tokens, 0, std.testing.allocator);
|
||||
_ = try module.init(&tokens, 0, std.testing.allocator, error_target);
|
||||
|
||||
defer module.deinit();
|
||||
}
|
||||
@ -71,8 +101,11 @@ test "should clean memory if a statement parsing fails after one item has been i
|
||||
const tokens = try lexic.tokenize(input, std.testing.allocator);
|
||||
defer tokens.deinit();
|
||||
|
||||
const error_target = try std.testing.allocator.create(errors.ErrorData);
|
||||
defer std.testing.allocator.destroy(error_target);
|
||||
|
||||
var module: Module = undefined;
|
||||
_ = module.init(&tokens, 0, std.testing.allocator) catch {
|
||||
_ = module.init(&tokens, 0, std.testing.allocator, error_target) catch {
|
||||
return;
|
||||
};
|
||||
defer module.deinit();
|
||||
|
34
src/errors/root.zig
Normal file
34
src/errors/root.zig
Normal file
@ -0,0 +1,34 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const ErrorData = struct {
|
||||
reason: []const u8,
|
||||
start_position: usize,
|
||||
end_position: usize,
|
||||
|
||||
pub fn init(
|
||||
target: *@This(),
|
||||
reason: []const u8,
|
||||
start_position: usize,
|
||||
end_position: usize,
|
||||
) !void {
|
||||
target.* = .{
|
||||
.reason = reason,
|
||||
.start_position = start_position,
|
||||
.end_position = end_position,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn print(self: *@This()) void {
|
||||
std.debug.print("Error: {s}\n", .{self.reason});
|
||||
}
|
||||
|
||||
/// When called, this struct will clean its resources and then
|
||||
/// clean itself.
|
||||
pub fn deinit(self: *@This()) void {
|
||||
_ = self;
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
Loading…
Reference in New Issue
Block a user