fix: memory issues in syntax root
This commit is contained in:
parent
9a31d1aca3
commit
6edb533f3d
@ -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(target: *Expression, tokens: *const std.ArrayList(Token), pos: usize) error{Unmatched}!void {
|
||||
pub fn init(tokens: *const std.ArrayList(Token), pos: usize) error{Unmatched}!Expression {
|
||||
std.debug.assert(pos < tokens.items.len);
|
||||
|
||||
const t = tokens.items[pos];
|
||||
@ -18,7 +18,7 @@ pub const Expression = union(enum) {
|
||||
return error.Unmatched;
|
||||
}
|
||||
|
||||
target.* = .{
|
||||
return .{
|
||||
.number = &t,
|
||||
};
|
||||
}
|
||||
@ -29,8 +29,7 @@ test "should parse expression" {
|
||||
const tokens = try lexic.tokenize(input, std.testing.allocator);
|
||||
defer tokens.deinit();
|
||||
|
||||
var expr: Expression = undefined;
|
||||
try expr.init(&tokens, 0);
|
||||
const expr = try Expression.init(&tokens, 0);
|
||||
try std.testing.expectEqualDeep("322", expr.number.value);
|
||||
try std.testing.expectEqualDeep(TokenType.Int, expr.number.token_type);
|
||||
}
|
||||
@ -40,8 +39,7 @@ test "should fail on non expression" {
|
||||
const tokens = try lexic.tokenize(input, std.testing.allocator);
|
||||
defer tokens.deinit();
|
||||
|
||||
var expr: Expression = undefined;
|
||||
expr.init(&tokens, 0) catch |err| {
|
||||
const expr = Expression.init(&tokens, 0) catch |err| {
|
||||
try std.testing.expectEqual(ParseError.Unmatched, err);
|
||||
return;
|
||||
};
|
||||
|
@ -3,21 +3,71 @@ const lexic = @import("lexic");
|
||||
const expression = @import("./expression.zig");
|
||||
const variable = @import("./variable.zig");
|
||||
const types = @import("./types.zig");
|
||||
const statement = @import("./statement.zig");
|
||||
|
||||
const Token = lexic.Token;
|
||||
const TokenType = lexic.TokenType;
|
||||
const ParseError = types.ParseError;
|
||||
const TokenStream = types.TokenStream;
|
||||
|
||||
const Statement = union(enum) {
|
||||
VariableBinding: u8,
|
||||
pub const Module = struct {
|
||||
statements: std.ArrayList(*statement.Statement),
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
fn parse(tokens: *const std.ArrayList(Token), pos: usize) ParseError!@This() {
|
||||
_ = tokens;
|
||||
_ = pos;
|
||||
return ParseError.Error;
|
||||
pub fn init(target: *@This(), tokens: *const TokenStream, pos: usize, allocator: std.mem.Allocator) ParseError!void {
|
||||
var arrl = std.ArrayList(*statement.Statement).init(allocator);
|
||||
errdefer arrl.deinit();
|
||||
|
||||
const input_len = tokens.items.len;
|
||||
var current_pos = pos;
|
||||
|
||||
// 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?
|
||||
var stmt = try allocator.create(statement.Statement);
|
||||
errdefer allocator.destroy(stmt);
|
||||
const next_pos = try stmt.init(tokens, current_pos, allocator);
|
||||
current_pos = next_pos;
|
||||
|
||||
arrl.append(stmt) catch {
|
||||
return ParseError.Error;
|
||||
};
|
||||
}
|
||||
|
||||
target.* = .{
|
||||
// FIXME: is this copying the whole arraylist? should use a pointer?
|
||||
.statements = arrl,
|
||||
.alloc = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: @This()) void {
|
||||
// FIXME: should deinit all elements inside the arraylist no? otherwise
|
||||
// they will leak no?
|
||||
for (self.statements.items) |stmt| {
|
||||
stmt.deinit();
|
||||
self.alloc.destroy(stmt);
|
||||
}
|
||||
self.statements.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
||||
|
||||
test "should parse a single statement" {
|
||||
const input = "var my_variable = 322";
|
||||
const tokens = try lexic.tokenize(input, std.testing.allocator);
|
||||
defer tokens.deinit();
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -11,14 +11,16 @@ const ParseError = types.ParseError;
|
||||
pub const Statement = union(enum) {
|
||||
VariableBinding: *variable.VariableBinding,
|
||||
|
||||
fn init(target: *Statement, tokens: *const TokenStream, pos: usize, allocator: std.mem.Allocator) ParseError!void {
|
||||
/// 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 parse_failed = false;
|
||||
vardef.init(tokens, pos, allocator) catch |err| switch (err) {
|
||||
error.Unmatched => {
|
||||
const vardef_end = vardef.init(tokens, pos, allocator) catch |err| switch (err) {
|
||||
error.Unmatched => blk: {
|
||||
parse_failed = true;
|
||||
break :blk 0;
|
||||
},
|
||||
else => {
|
||||
return err;
|
||||
@ -29,14 +31,14 @@ pub const Statement = union(enum) {
|
||||
target.* = .{
|
||||
.VariableBinding = &vardef,
|
||||
};
|
||||
return;
|
||||
return vardef_end;
|
||||
}
|
||||
|
||||
// fail
|
||||
return ParseError.Unmatched;
|
||||
}
|
||||
|
||||
fn deinit(self: @This()) void {
|
||||
pub fn deinit(self: @This()) void {
|
||||
switch (self) {
|
||||
.VariableBinding => |v| v.deinit(),
|
||||
}
|
||||
@ -49,7 +51,7 @@ test "should parse a variable declaration statement" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var statement: Statement = undefined;
|
||||
try statement.init(&tokens, 0, std.testing.allocator);
|
||||
_ = try statement.init(&tokens, 0, std.testing.allocator);
|
||||
defer statement.deinit();
|
||||
|
||||
switch (statement) {
|
||||
@ -65,7 +67,7 @@ test "should fail on other constructs" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var statement: Statement = undefined;
|
||||
statement.init(&tokens, 0, std.testing.allocator) catch |e| switch (e) {
|
||||
_ = statement.init(&tokens, 0, std.testing.allocator) catch |e| switch (e) {
|
||||
error.Unmatched => {
|
||||
return;
|
||||
},
|
||||
|
@ -11,11 +11,11 @@ pub const VariableBinding = struct {
|
||||
is_mutable: bool,
|
||||
datatype: ?*lexic.Token,
|
||||
identifier: *lexic.Token,
|
||||
expression: *expression.Expression,
|
||||
expression: *const expression.Expression,
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
/// Parses a variable binding
|
||||
pub fn init(target: *VariableBinding, tokens: *const TokenStream, pos: usize, allocator: std.mem.Allocator) ParseError!void {
|
||||
/// Parses a variable binding and returns the position of the next token
|
||||
pub fn init(target: *VariableBinding, tokens: *const TokenStream, pos: usize, allocator: std.mem.Allocator) ParseError!usize {
|
||||
std.debug.assert(pos < tokens.items.len);
|
||||
|
||||
// try to parse a var keyword
|
||||
@ -42,9 +42,8 @@ pub const VariableBinding = struct {
|
||||
|
||||
// parse expression
|
||||
if (pos + 3 >= tokens.items.len) return ParseError.Error;
|
||||
var exp = try allocator.create(expression.Expression);
|
||||
errdefer allocator.destroy(exp);
|
||||
exp.init(tokens, pos + 3) catch {
|
||||
|
||||
const exp = expression.Expression.init(tokens, pos + 3) catch {
|
||||
return ParseError.Error;
|
||||
};
|
||||
|
||||
@ -53,13 +52,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.alloc.destroy(self.expression);
|
||||
_ = self;
|
||||
}
|
||||
};
|
||||
|
||||
@ -69,7 +70,7 @@ test "should parse a minimal var" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var binding: VariableBinding = undefined;
|
||||
try binding.init(&tokens, 0, std.testing.allocator);
|
||||
_ = try binding.init(&tokens, 0, std.testing.allocator);
|
||||
defer binding.deinit();
|
||||
|
||||
try std.testing.expectEqual(true, binding.is_mutable);
|
||||
@ -81,7 +82,7 @@ test "should fail is it doesnt start with var" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var binding: VariableBinding = undefined;
|
||||
binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
_ = binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
try std.testing.expectEqual(ParseError.Unmatched, err);
|
||||
return;
|
||||
};
|
||||
@ -95,7 +96,7 @@ test "should fail if the idenfier is missing" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var binding: VariableBinding = undefined;
|
||||
binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
_ = binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
try std.testing.expectEqual(ParseError.Error, err);
|
||||
return;
|
||||
};
|
||||
@ -109,7 +110,7 @@ test "should fail if there is not an identifier after var" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var binding: VariableBinding = undefined;
|
||||
binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
_ = binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
try std.testing.expectEqual(ParseError.Error, err);
|
||||
return;
|
||||
};
|
||||
@ -123,7 +124,7 @@ test "should fail if the equal sign is missing" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var binding: VariableBinding = undefined;
|
||||
binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
_ = binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
try std.testing.expectEqual(ParseError.Error, err);
|
||||
return;
|
||||
};
|
||||
@ -137,7 +138,7 @@ test "should fail if the equal sign is not found" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var binding: VariableBinding = undefined;
|
||||
binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
_ = binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
try std.testing.expectEqual(ParseError.Error, err);
|
||||
return;
|
||||
};
|
||||
@ -151,7 +152,7 @@ test "should fail if the expression parsing fails" {
|
||||
defer tokens.deinit();
|
||||
|
||||
var binding: VariableBinding = undefined;
|
||||
binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
_ = binding.init(&tokens, 0, std.testing.allocator) catch |err| {
|
||||
try std.testing.expectEqual(ParseError.Error, err);
|
||||
return;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user