1 const std = @import("std");
2 const ArenaAllocator = std.heap.ArenaAllocator;
3 const Allocator = std.mem.Allocator;
4 const Interpreter = @This();
5 const Stmt = @import("stmt.zig").Stmt;
6 const Expr = @import("expr.zig").Expr;
7 const Token = @import("Token.zig");
8 const Literal = Token.Literal;
9 const lox = @import("main.zig");
11 arena: ArenaAllocator,
13 stdout: *std.Io.Writer,
15 pub const ErrorPayload = struct { token: Token, message: []const u8 };
17 pub fn init(child_allocator: Allocator, stdout: *std.Io.Writer) Interpreter {
19 .arena = .init(child_allocator),
20 .allocator = undefined,
25 pub fn deinit(self: *Interpreter) void {
29 pub fn interpret(self: *Interpreter, statements: []const *const Stmt) !void {
30 self.allocator = self.arena.allocator();
32 for (statements) |statement| {
33 var err_payload: ErrorPayload = undefined;
34 self.execute(statement, &err_payload) catch |err| switch (err) {
35 error.RuntimeError => try lox.runtimeError(err_payload),
41 fn evaluate(self: *Interpreter, expr: *const Expr, err_payload: *ErrorPayload) (Allocator.Error || error{RuntimeError})!Literal {
42 return switch (expr.*) {
43 .binary => |binary| self.visitBinaryExpr(binary, err_payload),
44 .grouping => |grouping| self.visitGroupingExpr(grouping, err_payload),
45 .literal => |literal| self.visitLiteralExpr(literal, err_payload),
46 .unary => |unary| self.visitUnaryExpr(unary, err_payload),
50 fn execute(self: *Interpreter, stmt: *const Stmt, err_payload: *ErrorPayload) !void {
52 .print => |print| self.visitPrintStmt(print, err_payload),
53 .expression => |expression| self.visitExpressionStmt(expression, err_payload),
57 fn visitExpressionStmt(self: *Interpreter, stmt: Stmt.Expression, err_payload: *ErrorPayload) !void {
58 _ = try self.evaluate(stmt.expression, err_payload);
61 fn visitPrintStmt(self: *Interpreter, stmt: Stmt.Print, err_payload: *ErrorPayload) !void {
62 const value = try self.evaluate(stmt.expression, err_payload);
63 try self.stdout.print("{s}\n", .{try self.stringify(value)});
66 fn visitBinaryExpr(self: *Interpreter, expr: Expr.Binary, err_payload: *ErrorPayload) !Literal {
67 const left = try self.evaluate(expr.left, err_payload);
68 const right = try self.evaluate(expr.right, err_payload);
70 return switch (expr.operator.type) {
71 .bang_equal => .{ .boolean = !isEqual(left, right) },
72 .equal_equal => .{ .boolean = isEqual(left, right) },
74 try checkNumberOperands(expr.operator, left, right, err_payload);
75 return .{ .boolean = left.number > right.number };
78 try checkNumberOperands(expr.operator, left, right, err_payload);
79 return .{ .boolean = left.number >= right.number };
82 try checkNumberOperands(expr.operator, left, right, err_payload);
83 return .{ .boolean = left.number < right.number };
86 try checkNumberOperands(expr.operator, left, right, err_payload);
87 return .{ .boolean = left.number <= right.number };
90 try checkNumberOperands(expr.operator, left, right, err_payload);
91 return .{ .number = left.number - right.number };
94 try checkNumberOperands(expr.operator, left, right, err_payload);
95 return .{ .number = left.number / right.number };
98 try checkNumberOperands(expr.operator, left, right, err_payload);
99 return .{ .number = left.number * right.number };
102 if (left == .number and right == .number) {
103 return .{ .number = left.number + right.number };
106 if (left == .string and right == .string) {
107 return .{ .string = try std.mem.concat(self.allocator, u8, &.{ left.string, right.string }) };
110 err_payload.* = .{ .token = expr.operator, .message = "Operands must be two numbers or two strings." };
111 return error.RuntimeError;
117 fn visitGroupingExpr(self: *Interpreter, expr: Expr.Grouping, err_payload: *ErrorPayload) !Literal {
118 return self.evaluate(expr.expression, err_payload);
121 fn visitLiteralExpr(self: *Interpreter, expr: Expr.Literal, err_payload: *ErrorPayload) !Literal {
127 fn visitUnaryExpr(self: *Interpreter, expr: Expr.Unary, err_payload: *ErrorPayload) !Literal {
128 const right = try self.evaluate(expr.right, err_payload);
130 return switch (expr.operator.type) {
131 .bang => .{ .boolean = !isTruthy(right) },
133 try checkNumberOperand(expr.operator, right, err_payload);
134 return .{ .number = -right.number };
140 fn checkNumberOperand(operator: Token, operand: Literal, err_payload: *ErrorPayload) !void {
141 if (operand == .number) return;
142 err_payload.* = .{ .token = operator, .message = "Operand must be a number." };
143 return error.RuntimeError;
146 fn checkNumberOperands(operator: Token, left: Literal, right: Literal, err_payload: *ErrorPayload) !void {
147 if (left == .number and right == .number) return;
148 err_payload.* = .{ .token = operator, .message = "Operands must be numbers." };
149 return error.RuntimeError;
152 fn isTruthy(object: Literal) bool {
153 return switch (object) {
159 fn isEqual(a: Literal, b: Literal) bool {
161 .string => |a_string| b == .string and std.mem.eql(u8, a_string, b.string),
162 .number => |a_number| b == .number and a_number == b.number,
163 .boolean => |a_boolean| b == .boolean and a_boolean == b.boolean,
168 fn stringify(self: *Interpreter, object: Literal) ![]const u8 {
169 return switch (object) {
170 .string => |string| string,
171 .number => |number| std.fmt.allocPrint(self.allocator, "{}", .{number}),
172 .boolean => |boolean| std.fmt.allocPrint(self.allocator, "{}", .{boolean}),