diff --git a/src/compiler.zig b/src/compiler.zig index 6695de9..ae21e84 100644 --- a/src/compiler.zig +++ b/src/compiler.zig @@ -167,8 +167,6 @@ pub const Compiler = struct { if (token_opt) |token| { self.parser.current = token; break; - } else { - self.errorCurrent(self.parser.current.lexeme); } } } @@ -221,11 +219,12 @@ pub const Compiler = struct { /// Emits bytecode for a number being loaded into the code. fn number(self: *Compiler) !void { + std.debug.warn("parsing number: '{}'\n", self.parser.previous.lexeme); var value: f64 = try std.fmt.parseFloat( f64, self.parser.previous.lexeme, ); - try self.emitConstant(value); + try self.emitConstant(values.NumberVal(value)); } /// Emits bytecode for a given unary. diff --git a/src/main.zig b/src/main.zig index 4611d6f..660a9e7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -12,12 +12,12 @@ const InterpretResult = vm.InterpretResult; pub var hadError = false; -fn run(allocator: *Allocator, data: []u8) !InterpretResult { +fn run(allocator: *Allocator, data: []u8) !void { var stdout_file = try std.io.getStdOut(); const stdout = &stdout_file.outStream().stream; var vmach = try vm.VM.init(allocator, stdout, data, true); - return try vmach.interpret(); + try vmach.interpret(); } pub fn doError(line: usize, message: []const u8) !void { @@ -40,9 +40,14 @@ fn runFile(allocator: *Allocator, path: []const u8) !void { var slice = try allocator.alloc(u8, total_bytes); _ = try lox_file.read(slice); - var res = try run(allocator, slice); - if (res == vm.InterpretResult.CompileError) std.os.exit(65); - if (res == vm.InterpretResult.RuntimeError) std.os.exit(70); + run(allocator, slice) catch |err| { + switch (err) { + InterpretResult.Ok => {}, + InterpretResult.CompileError => std.os.exit(65), + InterpretResult.RuntimeError => std.os.exit(70), + else => return err, + } + }; } fn runPrompt(allocator: *Allocator) !void { @@ -59,7 +64,15 @@ fn runPrompt(allocator: *Allocator) !void { return err; }; - _ = try run(allocator, line); + run(allocator, line) catch |err| { + switch (err) { + InterpretResult.Ok => {}, + InterpretResult.CompileError, InterpretResult.RuntimeError => blk: { + try stdout.print("compile/runtime error.\n"); + }, + else => return err, + } + }; } } diff --git a/src/new_scanner.zig b/src/new_scanner.zig index 0e2cbb0..1a8efc4 100644 --- a/src/new_scanner.zig +++ b/src/new_scanner.zig @@ -147,8 +147,8 @@ pub const Scanner = struct { } fn peekNext(self: *Scanner) u8 { - if (self.isAtEnd()) return 0; - return self.source[self.current - 1]; + if (self.current + 1 >= self.source.len) return 0; + return self.source[self.current + 1]; } fn skipWhitespace(self: *Scanner) void { @@ -255,10 +255,12 @@ pub const Scanner = struct { '>' => self.makeMatchToken('=', .GREATER_EQUAL, .GREATER), '/' => blk: { + std.debug.warn("next: {}\n", self.peekNext()); if (self.peekNext() == '/') { while (self.peek() != '\n' and !self.isAtEnd()) { _ = self.advance(); } + break :blk null; } else { break :blk self.makeToken(.SLASH); diff --git a/src/value.zig b/src/value.zig index b97d402..aa33ecc 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3,10 +3,42 @@ const std = @import("std"); const Allocator = std.mem.Allocator; // NOTE: right now, only numbers. -pub const Value = f64; + +pub const ValueType = enum(u8) { + Bool, + Nil, + Number, +}; + +pub const ValueValue = union(ValueType) { + Bool: bool, + Nil: void, + Number: f64, +}; + +pub const Value = struct { + vtype: ValueType, + as: ValueValue, +}; + +// helper functions +pub fn BoolVal(val: bool) Value { + return Value{ .vtype = .Bool, .as = ValueValue{ .Bool = val } }; +} + +pub fn NilVal() Value { + return Value{ .vtype = .Nil, .as = ValueValue{ .Nil = {} } }; +} + +pub fn NumberVal(val: f64) Value { + return Value{ .vtype = .Number, .as = ValueValue{ .Number = val } }; +} pub fn printValue(stdout: var, value: Value) !void { - try stdout.print("{}", value); + switch (value.as) { + .Number => try stdout.print("{}", value.as.Number), + else => unreachable, + } } pub const ValueList = struct { diff --git a/src/vm.zig b/src/vm.zig index e58bc66..398f9ab 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -1,6 +1,7 @@ const std = @import("std"); const chunk = @import("chunk.zig"); const value = @import("value.zig"); +const values = value; const compiler = @import("compiler.zig"); const Chunk = chunk.Chunk; @@ -9,7 +10,7 @@ const Compiler = compiler.Compiler; pub const StdOut = *std.io.OutStream(std.fs.File.WriteError); -pub const InterpretResult = enum { +pub const InterpretResult = error{ Ok, CompileError, RuntimeError, @@ -91,35 +92,50 @@ pub const VM = struct { } /// gets a f64 out of a value on the top of the stack. - fn popNum(self: *VM) f64 { - return self.pop(); + fn popNum(self: *VM) !f64 { + var val: Value = self.pop(); + + switch (val.vtype) { + .Number => return val.as.Number, + + else => |vtype| blk: { + self.runtimeError("Expected number, got {x}", vtype); + return InterpretResult.RuntimeError; + }, + } } fn doAdd(self: *VM) !void { - var b = self.popNum(); - var a = self.popNum(); - try self.push(a + b); + var b = try self.popNum(); + var a = try self.popNum(); + try self.push(values.NumberVal(a + b)); } fn doSub(self: *VM) !void { - var b = self.popNum(); - var a = self.popNum(); - try self.push(a * b); + var b = try self.popNum(); + var a = try self.popNum(); + try self.push(values.NumberVal(a - b)); } fn doMul(self: *VM) !void { - var b = self.popNum(); - var a = self.popNum(); - try self.push(a * b); + var b = try self.popNum(); + var a = try self.popNum(); + try self.push(values.NumberVal(a * b)); } fn doDiv(self: *VM) !void { - var b = self.popNum(); - var a = self.popNum(); - try self.push(a / b); + var b = try self.popNum(); + var a = try self.popNum(); + try self.push(values.NumberVal(a / b)); } - fn run(self: *VM) !InterpretResult { + fn runtimeError(self: *VM, comptime fmt: []const u8, args: ...) void { + std.debug.warn(fmt, args); + std.debug.warn("\n[line {}] in script\n", self.chk.lines[self.ip]); + self.resetStack(); + } + + fn run(self: *VM) !void { while (true) { if (self.debug_flag) { try self.debugStack(); @@ -150,7 +166,22 @@ pub const VM = struct { chunk.OpCode.Subtract => try self.doSub(), chunk.OpCode.Multiply => try self.doMul(), chunk.OpCode.Divide => try self.doDiv(), - chunk.OpCode.Negate => try self.push(-self.pop()), + chunk.OpCode.Negate => blk: { + var val = self.peek(0); + if (val.vtype != .Bool) { + self.runtimeError("Operand must be a number."); + return InterpretResult.RuntimeError; + } + + val = self.pop(); + switch (val.as) { + .Number => |num| { + try self.push(values.NumberVal(-num)); + }, + else => unreachable, + } + }, + else => blk: { std.debug.warn("Unknown instruction: {x}\n", instruction); return InterpretResult.RuntimeError; @@ -159,7 +190,7 @@ pub const VM = struct { } } - pub fn interpret(self: *VM) !InterpretResult { + pub fn interpret(self: *VM) !void { //self.ip = 0; //self.debug("VM start\n"); //var res = try self.run(); @@ -196,4 +227,8 @@ pub const VM = struct { self.stackTop -= 1; return self.stack[self.stackTop]; } + + pub fn peek(self: *VM, distance: usize) Value { + return self.stack[self.stackTop - 1 - distance]; + } };