fix: memory issues

This commit is contained in:
Fernando Araoz 2024-12-16 06:28:54 -05:00
parent 6edb533f3d
commit 81e38ab8a8
4 changed files with 44 additions and 20 deletions

View File

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

View File

@ -23,8 +23,6 @@ pub const Module = struct {
// parse many statements
while (current_pos < input_len) {
std.debug.print("running on pos {d} \n", .{current_pos});
// FIXME: if a statement was added to the array list,
// and then one of these fails,
// will all previous statements leak memory?
@ -34,6 +32,7 @@ pub const Module = struct {
current_pos = next_pos;
arrl.append(stmt) catch {
// TODO: free stmt if this fails
return ParseError.Error;
};
}
@ -68,6 +67,5 @@ test "should parse a single statement" {
var module: Module = undefined;
_ = try module.init(&tokens, 0, std.testing.allocator);
std.debug.print("len: {d} \n", .{module.statements.items.len});
defer module.deinit();
}

View File

@ -8,14 +8,21 @@ const variable = @import("./variable.zig");
const TokenStream = types.TokenStream;
const ParseError = types.ParseError;
pub const Statement = union(enum) {
VariableBinding: *variable.VariableBinding,
pub const Statement = struct {
alloc: std.mem.Allocator,
value: union(enum) {
variableBinding: *variable.VariableBinding,
},
/// Parses a Statement and return the position of the next token
pub fn init(target: *Statement, tokens: *const TokenStream, pos: usize, allocator: std.mem.Allocator) ParseError!usize {
// try to parse a variable definition
var vardef: variable.VariableBinding = undefined;
var vardef = allocator.create(variable.VariableBinding) catch {
return ParseError.OutOfMemory;
};
errdefer allocator.destroy(vardef);
var parse_failed = false;
const vardef_end = vardef.init(tokens, pos, allocator) catch |err| switch (err) {
error.Unmatched => blk: {
@ -29,7 +36,8 @@ pub const Statement = union(enum) {
if (!parse_failed) {
// return the parsed variable definition
target.* = .{
.VariableBinding = &vardef,
.alloc = allocator,
.value = .{ .variableBinding = vardef },
};
return vardef_end;
}
@ -39,8 +47,11 @@ pub const Statement = union(enum) {
}
pub fn deinit(self: @This()) void {
switch (self) {
.VariableBinding => |v| v.deinit(),
switch (self.value) {
.variableBinding => |v| {
v.deinit();
self.alloc.destroy(v);
},
}
}
};
@ -54,8 +65,8 @@ test "should parse a variable declaration statement" {
_ = try statement.init(&tokens, 0, std.testing.allocator);
defer statement.deinit();
switch (statement) {
.VariableBinding => |v| {
switch (statement.value) {
.variableBinding => |v| {
try std.testing.expectEqual(true, v.is_mutable);
},
}

View File

@ -11,7 +11,7 @@ pub const VariableBinding = struct {
is_mutable: bool,
datatype: ?*lexic.Token,
identifier: *lexic.Token,
expression: *const expression.Expression,
expression: *expression.Expression,
alloc: std.mem.Allocator,
/// Parses a variable binding and returns the position of the next token
@ -43,7 +43,12 @@ pub const VariableBinding = struct {
// parse expression
if (pos + 3 >= tokens.items.len) return ParseError.Error;
const exp = expression.Expression.init(tokens, pos + 3) catch {
// TODO: allocate on the stack
const exp = allocator.create(expression.Expression) catch {
return ParseError.OutOfMemory;
};
errdefer allocator.destroy(exp);
exp.init(tokens, pos + 3) catch {
return ParseError.Error;
};
@ -52,15 +57,15 @@ pub const VariableBinding = struct {
.is_mutable = true,
.datatype = null,
.identifier = identifier,
.expression = &exp,
.expression = exp,
.alloc = allocator,
};
// TODO: when expression parses more than one token this will break.
return pos + 4;
}
pub fn deinit(self: *VariableBinding) void {
_ = self;
pub fn deinit(self: @This()) void {
self.alloc.destroy(self.expression);
}
};
@ -74,6 +79,14 @@ test "should parse a minimal var" {
defer binding.deinit();
try std.testing.expectEqual(true, binding.is_mutable);
try std.testing.expect(binding.datatype == null);
try std.testing.expectEqualDeep("my_variable", binding.identifier.value);
const expr = binding.expression;
switch (expr.*) {
.number => |n| {
try std.testing.expectEqualDeep("322", n.value);
},
}
}
test "should fail is it doesnt start with var" {