From 81e38ab8a8746bfe89c81de02ba9a6b0b8934467 Mon Sep 17 00:00:00 2001 From: Fernando Araoz Date: Mon, 16 Dec 2024 06:28:54 -0500 Subject: [PATCH] fix: memory issues --- src/02_syntax/expression.zig | 10 ++++++---- src/02_syntax/root.zig | 4 +--- src/02_syntax/statement.zig | 27 +++++++++++++++++++-------- src/02_syntax/variable.zig | 23 ++++++++++++++++++----- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/02_syntax/expression.zig b/src/02_syntax/expression.zig index 552de27..aaf5fe2 100644 --- a/src/02_syntax/expression.zig +++ b/src/02_syntax/expression.zig @@ -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; }; diff --git a/src/02_syntax/root.zig b/src/02_syntax/root.zig index b0da001..edb9b7b 100644 --- a/src/02_syntax/root.zig +++ b/src/02_syntax/root.zig @@ -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(); } diff --git a/src/02_syntax/statement.zig b/src/02_syntax/statement.zig index 22a42fb..9aebc1a 100644 --- a/src/02_syntax/statement.zig +++ b/src/02_syntax/statement.zig @@ -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); }, } diff --git a/src/02_syntax/variable.zig b/src/02_syntax/variable.zig index 722d758..de8d5dc 100644 --- a/src/02_syntax/variable.zig +++ b/src/02_syntax/variable.zig @@ -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" {