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. /// Attempts to parse an expression from a token stream.
/// ///
/// Receives a pointer to the memory for initialization /// 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); std.debug.assert(pos < tokens.items.len);
const t = tokens.items[pos]; const t = tokens.items[pos];
@ -18,7 +18,7 @@ pub const Expression = union(enum) {
return error.Unmatched; return error.Unmatched;
} }
return .{ self.* = .{
.number = &t, .number = &t,
}; };
} }
@ -29,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.init(&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);
} }
@ -39,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.init(&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

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

View File

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

View File

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