diff --git a/src/main.zig b/src/main.zig index 485388b..cf125e6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,6 +3,7 @@ const std = @import("std"); const scanner = @import("scanner.zig"); const expr = @import("expr.zig"); const parser = @import("parser.zig"); +const runtime = @import("runtime.zig"); const err = @import("error.zig"); pub fn main() !void { @@ -71,6 +72,5 @@ fn run(allocator: std.mem.Allocator, bytes: []u8) !void { if (err.hasError) { return; } - expr.AstPrint(expression); - std.debug.print("\n", .{}); + runtime.interpret(expression); } diff --git a/src/runtime.zig b/src/runtime.zig new file mode 100644 index 0000000..fca701f --- /dev/null +++ b/src/runtime.zig @@ -0,0 +1,105 @@ +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; + } +} diff --git a/src/scanner.zig b/src/scanner.zig index 9a5b768..389282b 100644 --- a/src/scanner.zig +++ b/src/scanner.zig @@ -144,7 +144,7 @@ pub const Scanner = struct { // Closing ". _ = self.advance(); - self.addToken(token.TokenType.STRING); + self.addString(); } fn number(self: *Scanner) void { @@ -221,4 +221,10 @@ pub const Scanner = struct { const float = std.fmt.parseFloat(f64, self.source[self.start..self.current]) catch 0; self.addTokenInternal(token.TokenType.NUMBER, token.Token.Value{ .number = float }); } + + fn addString(self: *Scanner) void { + self.addTokenInternal(token.TokenType.STRING, token.Token.Value{ + .string = self.source[(self.start + 1)..(self.current - 1)], + }); + } };