const std = @import("std"); const err = @import("error.zig"); const expr_zig = @import("expr.zig"); const Expr = expr_zig.Expr; const LiteralExpr = expr_zig.LiteralExpr; const LiteralTag = expr_zig.LiteralTag; const token = @import("token.zig"); pub const RuntimeError = error{ TypeError, }; fn printLiteral(expr: LiteralExpr) void { switch (expr) { .number => |num| std.debug.print("{}\n", .{num}), .string => |string| std.debug.print("{s}\n", .{string}), .boolean => |boolean| std.debug.print("{}\n", .{boolean}), .nil => std.debug.print("nil\n", .{}), } } pub fn interpret(expr: Expr) void { var err_or_expr = evaluate(expr); if (err_or_expr) |res| { printLiteral(res); } else |_| { std.debug.print("Runtime Error.\n", .{}); } } pub fn evaluate(expr: Expr) !LiteralExpr { switch (expr) { .literal => |literal| return literal, .grouping => |grouping| return evaluate(grouping.expr.*), .unary => |unary| { var right = try evaluate(unary.right.*); switch (unary.operator.token_type) { .MINUS => switch (right) { .number => |num| return LiteralExpr{ .number = -1 * num }, else => { err.errToken(unary.operator, "Negating non-number"); return RuntimeError.TypeError; }, }, .BANG => return LiteralExpr{ .boolean = !isTruthy(right), }, else => unreachable, } }, .binary => |binary| { var left = try evaluate(binary.left.*); var right = try evaluate(binary.right.*); switch (binary.operator.token_type) { .STAR => { try checkNumberOperands(binary.operator, left, right); return LiteralExpr{ .number = left.number * right.number, }; }, .SLASH => { try checkNumberOperands(binary.operator, left, right); return LiteralExpr{ .number = left.number / right.number, }; }, // FIXME: make this handle string concatenation. .PLUS => { try checkNumberOperands(binary.operator, left, right); return LiteralExpr{ .number = left.number + right.number, }; }, .MINUS => { try checkNumberOperands(binary.operator, left, right); return LiteralExpr{ .number = left.number - right.number, }; }, else => unreachable, } }, } } fn isTruthy(expr: LiteralExpr) bool { return switch (expr) { .nil => false, .boolean => |*boolean| boolean.*, else => true, }; } fn checkNumberOperands(operator: token.Token, left: LiteralExpr, right: LiteralExpr) RuntimeError!void { if (@as(LiteralTag, left) != .number) { err.errToken(operator, "Left operand is non-numeric"); return RuntimeError.TypeError; } if (@as(LiteralTag, right) != .number) { err.errToken(operator, "Right operand is non-numeric"); return RuntimeError.TypeError; } }