From: Ayo Reis Date: Thu, 26 Feb 2026 18:15:29 +0000 (+0000) Subject: Implement print and expression statements X-Git-Url: https://git.ayoreis.com/zlox.git/commitdiff_plain/refs/heads/main?ds=inline Implement print and expression statements --- diff --git a/src/Interpreter.zig b/src/Interpreter.zig index 0f8c224..09d202a 100644 --- a/src/Interpreter.zig +++ b/src/Interpreter.zig @@ -2,6 +2,7 @@ const std = @import("std"); const ArenaAllocator = std.heap.ArenaAllocator; const Allocator = std.mem.Allocator; const Interpreter = @This(); +const Stmt = @import("stmt.zig").Stmt; const Expr = @import("expr.zig").Expr; const Token = @import("Token.zig"); const Literal = Token.Literal; @@ -25,16 +26,15 @@ pub fn deinit(self: *Interpreter) void { self.arena.deinit(); } -pub fn interpret(self: *Interpreter, expression: *const Expr) !void { +pub fn interpret(self: *Interpreter, statements: []const *const Stmt) !void { self.allocator = self.arena.allocator(); - var err_payload: ErrorPayload = undefined; - if (self.evaluate(expression, &err_payload)) |value| { - const stringified = try self.stringify(value); - try self.stdout.print("{s}\n", .{stringified}); - } else |err| switch (err) { - error.RuntimeError => try lox.runtimeError(err_payload), - else => return err, + for (statements) |statement| { + var err_payload: ErrorPayload = undefined; + self.execute(statement, &err_payload) catch |err| switch (err) { + error.RuntimeError => try lox.runtimeError(err_payload), + else => return err, + }; } } @@ -47,6 +47,22 @@ fn evaluate(self: *Interpreter, expr: *const Expr, err_payload: *ErrorPayload) ( }; } +fn execute(self: *Interpreter, stmt: *const Stmt, err_payload: *ErrorPayload) !void { + try switch (stmt.*) { + .print => |print| self.visitPrintStmt(print, err_payload), + .expression => |expression| self.visitExpressionStmt(expression, err_payload), + }; +} + +fn visitExpressionStmt(self: *Interpreter, stmt: Stmt.Expression, err_payload: *ErrorPayload) !void { + _ = try self.evaluate(stmt.expression, err_payload); +} + +fn visitPrintStmt(self: *Interpreter, stmt: Stmt.Print, err_payload: *ErrorPayload) !void { + const value = try self.evaluate(stmt.expression, err_payload); + try self.stdout.print("{s}\n", .{try self.stringify(value)}); +} + fn visitBinaryExpr(self: *Interpreter, expr: Expr.Binary, err_payload: *ErrorPayload) !Literal { const left = try self.evaluate(expr.left, err_payload); const right = try self.evaluate(expr.right, err_payload); diff --git a/src/Parser.zig b/src/Parser.zig index 7b5a311..1eb1b9e 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -1,8 +1,10 @@ const std = @import("std"); const ArenaAllocator = std.heap.ArenaAllocator; const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayList; const Token = @import("Token.zig"); const Parser = @This(); +const Stmt = @import("stmt.zig").Stmt; const Expr = @import("expr.zig").Expr; const TokenType = @import("token_type.zig").TokenType; const lox = @import("main.zig"); @@ -24,20 +26,47 @@ pub fn deinit(self: *Parser) void { self.arena.deinit(); } -pub fn parse(self: *Parser) !?*Expr { +pub fn parse(self: *Parser) ![]const *const Stmt { self.allocator = self.arena.allocator(); - return self.expression() catch |err2| switch (err2) { - error.ParseError => return null, - else => return err2, - }; + var statements: ArrayList(*const Stmt) = .empty; + + while (!self.isAtEnd()) { + try statements.append(self.allocator, try self.statement()); + } + + return statements.toOwnedSlice(self.allocator); } -fn expression(self: *Parser) (Allocator.Error || std.Io.Writer.Error || error{ParseError})!*Expr { +fn expression(self: *Parser) (Allocator.Error || std.Io.Writer.Error || error{ParseError})!*const Expr { return self.equality(); } -fn equality(self: *Parser) !*Expr { +fn statement(self: *Parser) !*const Stmt { + if (self.match(&.{.print})) { + return self.printStatement(); + } + + return self.expressionStatement(); +} + +fn printStatement(self: *Parser) !*const Stmt { + const value = try self.expression(); + _ = try self.consume(.semicolon, "Expect ';' after value."); + const stmt = try self.allocator.create(Stmt); + stmt.* = .{ .print = .init(value) }; + return stmt; +} + +fn expressionStatement(self: *Parser) !*const Stmt { + const expr = try self.expression(); + _ = try self.consume(.semicolon, "Expect ';' after expression."); + const stmt = try self.allocator.create(Stmt); + stmt.* = .{ .expression = .init(expr) }; + return stmt; +} + +fn equality(self: *Parser) !*const Expr { var expr = try self.comparison(); while (self.match(&.{ .bang_equal, .equal_equal })) { @@ -51,7 +80,7 @@ fn equality(self: *Parser) !*Expr { return expr; } -fn comparison(self: *Parser) !*Expr { +fn comparison(self: *Parser) !*const Expr { var expr = try self.term(); while (self.match(&.{ .greater, .greater_equal, .less, .less_equal })) { @@ -65,7 +94,7 @@ fn comparison(self: *Parser) !*Expr { return expr; } -fn term(self: *Parser) !*Expr { +fn term(self: *Parser) !*const Expr { var expr = try self.factor(); while (self.match(&.{ .plus, .minus })) { @@ -79,7 +108,7 @@ fn term(self: *Parser) !*Expr { return expr; } -fn factor(self: *Parser) !*Expr { +fn factor(self: *Parser) !*const Expr { var expr = try self.unary(); while (self.match(&.{ .slash, .star })) { @@ -93,7 +122,7 @@ fn factor(self: *Parser) !*Expr { return expr; } -fn unary(self: *Parser) !*Expr { +fn unary(self: *Parser) !*const Expr { if (self.match(&.{ .bang, .minus })) { const operator = self.previous(); const right = try self.unary(); @@ -105,7 +134,7 @@ fn unary(self: *Parser) !*Expr { return self.primary(); } -fn primary(self: *Parser) !*Expr { +fn primary(self: *Parser) !*const Expr { if (self.match(&.{.false})) { const expr = try self.allocator.create(Expr); expr.* = .{ .literal = .init(.{ .boolean = false }) }; diff --git a/src/main.zig b/src/main.zig index 1f02d3e..19aae30 100644 --- a/src/main.zig +++ b/src/main.zig @@ -70,12 +70,12 @@ fn run(allocator: Allocator, source: []const u8) !void { defer allocator.free(tokens); var parser = try Parser.init(allocator, tokens); defer parser.deinit(); - const expression = try parser.parse(); + const statements = try parser.parse(); // Stop if there was a syntax error. if (hadError) return; - try interpreter.interpret(expression.?); + try interpreter.interpret(statements); try interpreter.stdout.flush(); } diff --git a/src/stmt.zig b/src/stmt.zig new file mode 100644 index 0000000..e4137f4 --- /dev/null +++ b/src/stmt.zig @@ -0,0 +1,26 @@ +const Expr = @import("expr.zig").Expr; + +pub const Stmt = union(enum) { + expression: Expression, + print: Print, + + pub const Expression = struct { + expression: *const Expr, + + pub fn init(expression: *const Expr) Expression { + return .{ + .expression = expression, + }; + } + }; + + pub const Print = struct { + expression: *const Expr, + + pub fn init(expression: *const Expr) Print { + return .{ + .expression = expression, + }; + } + }; +};