refactor: use null to signal unmatch in parser

This commit is contained in:
Fernando Araoz 2025-01-27 20:11:13 -05:00
parent 3671c221a9
commit 6a194d4065
4 changed files with 32 additions and 46 deletions

View File

@ -3,7 +3,6 @@ const lexic = @import("lexic");
const errors = @import("errors");
const Token = lexic.Token;
const TokenType = lexic.TokenType;
const ParseError = @import("./types.zig").ParseError;
pub const Expression = union(enum) {
number: *const Token,

View File

@ -13,7 +13,7 @@ const ParseError = types.ParseError;
const TokenStream = types.TokenStream;
pub const Module = struct {
statements: std.ArrayList(*statement.Statement),
statements: std.ArrayList(statement.Statement),
alloc: std.mem.Allocator,
/// Parses a module.
@ -29,11 +29,10 @@ pub const Module = struct {
allocator: std.mem.Allocator,
err_arrl: *std.ArrayList(errors.ErrorData),
) ParseError!void {
var arrl = std.ArrayList(*statement.Statement).init(allocator);
var arrl = std.ArrayList(statement.Statement).init(allocator);
errdefer arrl.deinit();
errdefer for (arrl.items) |i| {
i.deinit();
allocator.destroy(i);
};
const input_len = tokens.items.len;
@ -41,30 +40,27 @@ pub const Module = struct {
// parse many statements
while (current_pos < input_len) {
var stmt = try allocator.create(statement.Statement);
errdefer allocator.destroy(stmt);
var stmt: statement.Statement = undefined;
const next_pos = stmt.init(tokens, current_pos, allocator) catch |e| {
switch (e) {
error.Unmatched => {
// create the error value
var error_target: errors.ErrorData = undefined;
try error_target.init(
"No statement found",
// TODO: handle other errors of vardef parsing
const next_pos = try stmt.init(tokens, current_pos, allocator);
if (next_pos) |next_pos_actual| {
current_pos = next_pos_actual;
try arrl.append(stmt);
continue;
}
// nothing matched, but there are tokens. this in an error
var err: errors.ErrorData = undefined;
try err.init(
"No statement matched",
current_pos,
current_pos + 1,
allocator,
);
defer error_target.deinit();
try err_arrl.append(error_target);
return error.Unmatched;
},
else => return e,
}
};
current_pos = next_pos;
try arrl.append(stmt);
try err_arrl.append(err);
return error.Error;
}
target.* = .{
@ -76,7 +72,6 @@ pub const Module = struct {
pub fn deinit(self: @This()) void {
for (self.statements.items) |stmt| {
stmt.deinit();
self.alloc.destroy(stmt);
}
self.statements.deinit();
}

View File

@ -21,14 +21,13 @@ pub const Statement = struct {
tokens: *const TokenStream,
pos: usize,
allocator: std.mem.Allocator,
) ParseError!usize {
) ParseError!?usize {
// try to parse a variable definition
var vardef = allocator.create(variable.VariableBinding) catch {
return ParseError.OutOfMemory;
};
var vardef = try allocator.create(variable.VariableBinding);
errdefer allocator.destroy(vardef);
// TODO: handle other errors of vardef parsing
if (try vardef.init(tokens, pos, allocator)) |vardef_end| {
// variable definition parsed
// return the parsed variable definition
@ -38,10 +37,10 @@ pub const Statement = struct {
};
return vardef_end;
}
// TODO: handle other errors of vardef parsing
// fail
return ParseError.Unmatched;
// manually deallocate
allocator.destroy(vardef);
return null;
}
pub fn deinit(self: @This()) void {
@ -81,15 +80,11 @@ test "should fail on other constructs" {
defer tokens.deinit();
var statement: Statement = undefined;
_ = statement.init(&tokens, 0, std.testing.allocator) catch |e| switch (e) {
error.Unmatched => {
const result = try statement.init(&tokens, 0, std.testing.allocator);
if (result == null) {
// good path
return;
},
else => {
try std.testing.expect(false);
return;
},
};
}
try std.testing.expect(false);
}

View File

@ -3,9 +3,6 @@ const lexic = @import("lexic");
/// Respresents a failure of parsing.
pub const ParseError = error{
/// The parse operation failed, but it is recoverable.
/// Other parsers should be considered.
Unmatched,
/// The parse operation parsed after a point of no return.
/// For example, a `var` keyword was found, but then no identifier
/// The parsing should stop