Hi!
I’m a beginner with Zig, learning the language by creating a bytecode VM.
Thanks for your feedback under this post: A VM bytecode interpreter for EduScript, an educational language
I started the development of the parser, and I wonder if I use pointers correctly, if I structure my nodes in an optimal way… Particularly the construction of binary expressions.
I’d be very happy to have some feedback before continuing
You can read the code and tests here.
Nodes:
pub const Stmt = union(enum) {
expr: Expr,
empty: Empty,
};
pub const Expr = union(enum) {
binary: Binary,
literal: Literal,
};
pub const Binary = struct {
left: *Expr,
operator: Token.Type,
right: *Expr,
};
pub const Literal = union(enum) {
number: f64,
string: []const u8,
boolean: bool,
nullVal: void,
undefinedVal: void,
};
pub const Empty = struct {};
pub const Program = struct {
statements: std.ArrayList(Stmt),
};
pub fn createBinary(allocator: std.mem.Allocator, op: Token.Type, left: Expr, right: Expr) !Binary {
const binary = Binary{
.left = try allocator.create(Expr),
.right = try allocator.create(Expr),
.operator = op,
};
binary.left.* = left;
binary.right.* = right;
return binary;
}
Use-case:
fn parseExpr(self: *@This(), allocator: std.mem.Allocator, min_precedence: usize) !?Node.Expr {
var left = try self.parsePrimaryExpr(allocator);
// Try to create binary expression node (e.g. 1 + 2, true == false)
while (self.current < self.tokens.len) {
var op = self.peek();
// if precedence == 0, not a binary expression operator
const precedence = self.getPrecedence(op.token_type);
if (precedence < min_precedence or precedence == 0) break;
op = try self.consume(op.token_type);
const right = try self.parseExpr(allocator, precedence + 1) orelse unreachable;
left = Node.Expr{ .binary = try Node.createBinary(allocator, op.token_type, left.?, right) };
}
return left;
}