]> Repositories - zlox.git/commitdiff
Implement print and expression statements main
authorAyo Reis <hey@ayoreis.com>
Thu, 26 Feb 2026 18:15:29 +0000 (18:15 +0000)
committerAyo Reis <hey@ayoreis.com>
Thu, 26 Feb 2026 18:15:29 +0000 (18:15 +0000)
src/Interpreter.zig
src/Parser.zig
src/main.zig
src/stmt.zig [new file with mode: 0644]

index 0f8c224bf4b5be7e030414891e8fc906394d33ff..09d202a7076efb6172581e87d7360a395ccc058f 100644 (file)
@@ -2,6 +2,7 @@ const std = @import("std");
 const ArenaAllocator = std.heap.ArenaAllocator;
 const Allocator = std.mem.Allocator;
 const Interpreter = @This();
 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;
 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();
 }
 
     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();
 
     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);
 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);
index 7b5a3117d0a17f7383565223dae824dca0cf7dbd..1eb1b9e751ac27843d1c0007ca369f73dc301fd2 100644 (file)
@@ -1,8 +1,10 @@
 const std = @import("std");
 const ArenaAllocator = std.heap.ArenaAllocator;
 const Allocator = std.mem.Allocator;
 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 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");
 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();
 }
 
     self.arena.deinit();
 }
 
-pub fn parse(self: *Parser) !?*Expr {
+pub fn parse(self: *Parser) ![]const *const Stmt {
     self.allocator = self.arena.allocator();
 
     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();
 }
 
     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 })) {
     var expr = try self.comparison();
 
     while (self.match(&.{ .bang_equal, .equal_equal })) {
@@ -51,7 +80,7 @@ fn equality(self: *Parser) !*Expr {
     return 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 })) {
     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;
 }
 
     return expr;
 }
 
-fn term(self: *Parser) !*Expr {
+fn term(self: *Parser) !*const Expr {
     var expr = try self.factor();
 
     while (self.match(&.{ .plus, .minus })) {
     var expr = try self.factor();
 
     while (self.match(&.{ .plus, .minus })) {
@@ -79,7 +108,7 @@ fn term(self: *Parser) !*Expr {
     return expr;
 }
 
     return expr;
 }
 
-fn factor(self: *Parser) !*Expr {
+fn factor(self: *Parser) !*const Expr {
     var expr = try self.unary();
 
     while (self.match(&.{ .slash, .star })) {
     var expr = try self.unary();
 
     while (self.match(&.{ .slash, .star })) {
@@ -93,7 +122,7 @@ fn factor(self: *Parser) !*Expr {
     return 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();
     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();
 }
 
     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 }) };
     if (self.match(&.{.false})) {
         const expr = try self.allocator.create(Expr);
         expr.* = .{ .literal = .init(.{ .boolean = false }) };
index 1f02d3e7f89026fd72a6534bdf48eed06b9c7bc2..19aae3001d437b9ef82c9af772b472b873aacd41 100644 (file)
@@ -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();
     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;
 
 
     // Stop if there was a syntax error.
     if (hadError) return;
 
-    try interpreter.interpret(expression.?);
+    try interpreter.interpret(statements);
     try interpreter.stdout.flush();
 }
 
     try interpreter.stdout.flush();
 }
 
diff --git a/src/stmt.zig b/src/stmt.zig
new file mode 100644 (file)
index 0000000..e4137f4
--- /dev/null
@@ -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,
+            };
+        }
+    };
+};