diff --git a/src/chunk.zig b/src/chunk.zig index 76d4bb9..abc54af 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -31,6 +31,8 @@ const AllOpcodes = struct { pub DefineGlobal: u8 = 17, pub DefineGlobalLong: u8 = 18, + pub GetGlobal: u8 = 19, + pub GetGlobalLong: u8 = 20, }; pub const OpCode = AllOpcodes{}; @@ -221,6 +223,10 @@ pub const Chunk = struct { return try simpleInstruction(stdout, "OP_DEFGLOBAL", index); } else if (instruction == OpCode.DefineGlobalLong) { return try simpleInstruction(stdout, "OP_DEFGLOBAL_LONG", index); + } else if (instruction == OpCode.GetGlobal) { + return try simpleInstruction(stdout, "OP_GETGLOBAL", index); + } else if (instruction == OpCode.GetGlobalLong) { + return try simpleInstruction(stdout, "OP_GETGLOBAL_LONG", index); } else { try stdout.print("Unknown opcode: {}\n", instruction); return index + 1; diff --git a/src/compiler.zig b/src/compiler.zig index 1b44624..de0f25a 100644 --- a/src/compiler.zig +++ b/src/compiler.zig @@ -89,7 +89,7 @@ var rules = []ParseRule{ ParseRule{ .infix = Compiler.binary, .precedence = .Comparison }, ParseRule{ .infix = Compiler.binary, .precedence = .Comparison }, - ParseRule{}, + ParseRule{ .prefix = Compiler.variable }, ParseRule{ .prefix = Compiler.string }, ParseRule{ .prefix = Compiler.number }, ParseRule{ .precedence = .And }, @@ -252,6 +252,19 @@ pub const Compiler = struct { ))); } + fn namedVariable(self: *Compiler, tok: *Token) !void { + var idx = try self.identifierConstant(tok); + try self.emitConstWithIndex( + chunks.OpCode.GetGlobal, + chunks.OpCode.GetGlobalLong, + idx, + ); + } + + fn variable(self: *Compiler) !void { + try self.namedVariable(&self.parser.previous); + } + /// Emits bytecode for a given unary. fn unary(self: *Compiler) !void { var ttype = self.parser.previous.ttype; @@ -368,11 +381,16 @@ pub const Compiler = struct { return try self.identifierConstant(&self.parser.previous); } - fn defineVariable(self: *Compiler, global: chunks.ConstantIndex) !void { - switch (global) { - .Small => |val| try self.emitBytes(chunks.OpCode.DefineGlobal, val), + fn emitConstWithIndex( + self: *Compiler, + op_short: u8, + op_long: u8, + idx: chunks.ConstantIndex, + ) !void { + switch (idx) { + .Small => |val| try self.emitBytes(op_short, val), .Long => |val| blk: { - try self.emitByte(chunks.OpCode.DefineGlobalLong); + try self.emitByte(op_long); try self.emitByte(val[0]); try self.emitByte(val[1]); try self.emitByte(val[2]); @@ -381,6 +399,14 @@ pub const Compiler = struct { } } + fn defineVariable(self: *Compiler, global: chunks.ConstantIndex) !void { + try self.emitConstWithIndex( + chunks.OpCode.DefineGlobal, + chunks.OpCode.DefineGlobalLong, + global, + ); + } + fn varDecl(self: *Compiler) !void { var global = try self.parseVariable("Expect variable name."); diff --git a/src/main.zig b/src/main.zig index 8451723..a38e2a3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -16,9 +16,17 @@ 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); + var vmach = try vm.VM.init(allocator, stdout, true); defer vmach.deinit(); - try vmach.interpret(); + try vmach.interpret(data); +} + +fn runWithVM(vmach: *vm.VM, data: []u8) !void { + var stdout_file = try std.io.getStdOut(); + const stdout = &stdout_file.outStream().stream; + + defer vmach.deinit(); + try vmach.interpret(data); } pub fn doError(line: usize, message: []const u8) !void { @@ -55,6 +63,9 @@ fn runPrompt(allocator: *Allocator) !void { var stdout_file = try std.io.getStdOut(); const stdout = &stdout_file.outStream().stream; + var vmach = try vm.VM.init(allocator, stdout, true); + defer vmach.deinit(); + while (true) { try stdout.print(">"); var buffer = try std.Buffer.init(allocator, ""[0..]); @@ -65,7 +76,7 @@ fn runPrompt(allocator: *Allocator) !void { return err; }; - run(allocator, line) catch |err| { + runWithVM(&vmach, line) catch |err| { switch (err) { InterpretResult.Ok => {}, InterpretResult.CompileError, InterpretResult.RuntimeError => blk: { diff --git a/src/vm.zig b/src/vm.zig index 0b6e3fb..24a72f8 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -40,7 +40,6 @@ pub const ValueMap = std.AutoHashMap([]const u8, values.Value); pub const VM = struct { chk: *Chunk = undefined, - src: []const u8, ip: usize = 0, stack: []Value, @@ -60,12 +59,9 @@ pub const VM = struct { pub fn init( allocator: *std.mem.Allocator, stdout: StdOut, - source: []const u8, debug_flag: bool, ) !VM { var self = VM{ - .src = source, - .stack = try allocator.alloc(Value, 256), .stdout = stdout, .debug_flag = debug_flag, @@ -106,6 +102,7 @@ pub const VM = struct { } pub fn deinit(self: *VM) void { + self.globals.deinit(); self.deinitObjects(); } @@ -229,6 +226,25 @@ pub const VM = struct { _ = self.pop(); } + fn readString(self: *VM) []u8 { + return self.readConst().as.Object.value.String; + } + + fn readStringLong(self: *VM) []u8 { + return self.readConstLong().as.Object.value.String; + } + + fn doGetGlobal(self: *VM, name: []u8) !void { + var kv_opt = self.globals.get(name); + + if (kv_opt) |kv| { + try self.push(kv.value); + } else { + self.runtimeError("Undefined variable '{}'.", name); + return InterpretResult.RuntimeError; + } + } + fn run(self: *VM) !void { while (true) { if (self.debug_flag) { @@ -270,15 +286,24 @@ pub const VM = struct { break :blk; }, + chunk.OpCode.GetGlobal => blk: { + try self.doGetGlobal(self.readString()); + break :blk; + }, + chunk.OpCode.GetGlobalLong => blk: { + try self.doGetGlobal(self.readStringLong()); + break :blk; + }, + // extracting the name is different depending of the // op code since one just uses a single byte, the other // uses three bytes since its a u24. chunk.OpCode.DefineGlobal => blk: { - try self.defGlobal(self.readConst().as.Object.value.String); + try self.defGlobal(self.readString()); break :blk; }, chunk.OpCode.DefineGlobalLong => blk: { - try self.defGlobal(self.readConstLong().as.Object.value.String); + try self.defGlobal(self.readStringLong()); break :blk; }, @@ -323,7 +348,7 @@ pub const VM = struct { } } - pub fn interpret(self: *VM) !void { + pub fn interpret(self: *VM, src: []const u8) !void { //self.ip = 0; //self.debug("VM start\n"); //var res = try self.run(); @@ -335,7 +360,7 @@ pub const VM = struct { self.allocator, &chk, self.stdout, - self.src, + src, self.debug_flag, self, );