feat: minimal parse of variable binding

This commit is contained in:
Fernando Araoz 2024-12-12 06:47:20 -05:00
parent 44bd9fc5ad
commit 55231e986e
3 changed files with 33 additions and 19 deletions

View File

@ -8,15 +8,17 @@ pub const Expression = union(enum) {
number: *const Token, number: *const Token,
/// Attempts to parse an expression from a token stream. /// Attempts to parse an expression from a token stream.
fn parse(tokens: *const std.ArrayList(Token), pos: usize) ParseError!@This() { ///
/// Receives a pointer to the memory for initialization
pub fn init(target: *Expression, tokens: *const std.ArrayList(Token), pos: usize) error{Unmatched}!void {
std.debug.assert(pos < tokens.items.len); std.debug.assert(pos < tokens.items.len);
const t = tokens.items[pos]; const t = tokens.items[pos];
if (t.token_type != TokenType.Int) { if (t.token_type != TokenType.Int) {
return ParseError.Unmatched; return error.Unmatched;
} }
return .{ target.* = .{
.number = &t, .number = &t,
}; };
} }
@ -27,7 +29,8 @@ test "should parse expression" {
const tokens = try lexic.tokenize(input, std.testing.allocator); const tokens = try lexic.tokenize(input, std.testing.allocator);
defer tokens.deinit(); defer tokens.deinit();
const expr = try Expression.parse(&tokens, 0); var expr: Expression = undefined;
try expr.init(&tokens, 0);
try std.testing.expectEqualDeep("322", expr.number.value); try std.testing.expectEqualDeep("322", expr.number.value);
try std.testing.expectEqualDeep(TokenType.Int, expr.number.token_type); try std.testing.expectEqualDeep(TokenType.Int, expr.number.token_type);
} }
@ -37,7 +40,8 @@ test "should fail on non expression" {
const tokens = try lexic.tokenize(input, std.testing.allocator); const tokens = try lexic.tokenize(input, std.testing.allocator);
defer tokens.deinit(); defer tokens.deinit();
const expr = Expression.parse(&tokens, 0) catch |err| { var expr: Expression = undefined;
expr.init(&tokens, 0) catch |err| {
try std.testing.expectEqual(ParseError.Unmatched, err); try std.testing.expectEqual(ParseError.Unmatched, err);
return; return;
}; };

View File

@ -10,6 +10,8 @@ pub const ParseError = error{
/// 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
Error, Error,
/// OOM. Fatal error, blows up everything
OutOfMemory,
}; };
pub const TokenStream = std.ArrayList(lexic.Token); pub const TokenStream = std.ArrayList(lexic.Token);

View File

@ -11,14 +11,12 @@ const VariableBinding = struct {
is_mutable: bool, is_mutable: bool,
datatype: ?*lexic.Token, datatype: ?*lexic.Token,
identifier: *lexic.Token, identifier: *lexic.Token,
expression: expression.Expression, expression: *expression.Expression,
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
fn init(tokens: *const TokenStream, pos: usize, allocator: std.mem.Allocator) ParseError!@This() { fn init(target: *VariableBinding, tokens: *const TokenStream, pos: usize, allocator: std.mem.Allocator) ParseError!void {
std.debug.assert(pos < tokens.items.len); std.debug.assert(pos < tokens.items.len);
_ = allocator;
// try to parse a var keyword // try to parse a var keyword
const var_keyword = try utils.expect_token_type(lexic.TokenType.K_Var, &tokens.items[pos]); const var_keyword = try utils.expect_token_type(lexic.TokenType.K_Var, &tokens.items[pos]);
_ = var_keyword; _ = var_keyword;
@ -33,7 +31,6 @@ const VariableBinding = struct {
const identifier = utils.expect_token_type(lexic.TokenType.Identifier, &tokens.items[pos + 1]) catch { const identifier = utils.expect_token_type(lexic.TokenType.Identifier, &tokens.items[pos + 1]) catch {
return ParseError.Error; return ParseError.Error;
}; };
_ = identifier;
// parse equal sign // parse equal sign
if (pos + 2 >= tokens.items.len) return ParseError.Error; if (pos + 2 >= tokens.items.len) return ParseError.Error;
@ -43,25 +40,36 @@ const VariableBinding = struct {
_ = equal_sign; _ = equal_sign;
// parse expression // parse expression
if (pos + 3 >= tokens.items.len) return ParseError.Error;
var exp = allocator.create(expression.Expression) catch {
return ParseError.Error;
};
errdefer allocator.destroy(exp);
try exp.init(tokens, pos + 3);
// provisional good return value // return
return ParseError.Unmatched; target.* = .{
.is_mutable = true,
.datatype = null,
.identifier = identifier,
.expression = exp,
.alloc = allocator,
};
} }
fn deinit(self: *@This()) void { fn deinit(self: *@This()) void {
_ = self; self.alloc.destroy(self.expression);
} }
}; };
test "should parse a minimal var" { test "should parse a minimal var" {
const input = "var my_variable ="; const input = "var my_variable = 322";
const tokens = try lexic.tokenize(input, std.testing.allocator); const tokens = try lexic.tokenize(input, std.testing.allocator);
defer tokens.deinit(); defer tokens.deinit();
const binding = VariableBinding.init(&tokens, 0, std.testing.allocator) catch |err| { var binding: VariableBinding = undefined;
try std.testing.expectEqual(ParseError.Unmatched, err); try binding.init(&tokens, 0, std.testing.allocator);
return; defer binding.deinit();
};
try std.testing.expectEqual(false, binding.is_mutable); try std.testing.expectEqual(true, binding.is_mutable);
} }