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 errors = @import("errors");
const Token = lexic.Token; const Token = lexic.Token;
const TokenType = lexic.TokenType; const TokenType = lexic.TokenType;
const ParseError = @import("./types.zig").ParseError;
pub const Expression = union(enum) { pub const Expression = union(enum) {
number: *const Token, number: *const Token,

View File

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

View File

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

View File

@ -3,9 +3,6 @@ const lexic = @import("lexic");
/// Respresents a failure of parsing. /// Respresents a failure of parsing.
pub const ParseError = error{ 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. /// The parse operation parsed after a point of no return.
/// For example, a `var` keyword was found, but then no identifier /// For example, a `var` keyword was found, but then no identifier
/// The parsing should stop /// The parsing should stop