DzLox

:)
git clone https://git.sr.ht/~ashymad/DzLox
Log | Files | Refs | Submodules | LICENSE

commit 807cfbd512863405f87ce32539e9cd7cf2ca988b
parent 8ccf4e893ff6704a047a0810818ca4f80f0d3552
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date:   Thu,  1 Aug 2024 12:46:03 +0200

If, else and ternary

Diffstat:
Mzlox/src/array.zig | 6++++++
Mzlox/src/chunk.zig | 2++
Mzlox/src/compiler.zig | 51+++++++++++++++++++++++++++++++++++++++++++++++----
Mzlox/src/debug.zig | 11+++++++++++
Mzlox/src/main.zig | 2+-
Mzlox/src/vm.zig | 25+++++++++++++++++++++++--
6 files changed, 90 insertions(+), 7 deletions(-)

diff --git a/zlox/src/array.zig b/zlox/src/array.zig @@ -28,6 +28,12 @@ pub fn Array(comptime T: type, comptime S: type, comptime size: S) type { return self.data[idx]; } + pub fn set(self: *const @This(), idx: S, val: T) !void { + if (idx >= self.len) + return error.IndexOutOfBounds; + self.data[idx] = val; + } + pub fn last(self: *const @This()) !T { if (self.len == 0) return error.IndexOutOfBounds; diff --git a/zlox/src/chunk.zig b/zlox/src/chunk.zig @@ -26,6 +26,8 @@ pub const OP = enum(u8) { SET_GLOBAL, GET_LOCAL, SET_LOCAL, + JUMP_IF_FALSE, + JUMP, }; pub const Chunk = struct { diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -360,15 +360,18 @@ pub fn Compiler(size: comptime_int) type { fn ternary(self: *Self, _: bool) void { const operatorType = self.previous.type catch unreachable; - self.parsePrecedence(getRule(operatorType).precedence.inc()); - // emit bytecode + const thenJump = self.emitJump(OP.JUMP_IF_FALSE); + self.emitOP(OP.POP); + self.parsePrecedence(getRule(operatorType).precedence.inc()); + const elseJump = self.emitJump(OP.JUMP); + self.patchJump(thenJump); self.consume(Token.COLON, "Expected ':' in ternary expression."); + self.emitOP(OP.POP); self.parsePrecedence(getRule(operatorType).precedence.inc()); - - // emit bytecode + self.patchJump(elseJump); } fn declaration(self: *Self) void { @@ -490,6 +493,8 @@ pub fn Compiler(size: comptime_int) type { fn statement(self: *Self) void { if (self.match(Token.PRINT)) { self.printStatement(); + } else if (self.match(Token.IF)) { + self.ifStatement(); } else if (self.match(Token.LEFT_BRACE)) { self.beginScope(); self.block(); @@ -499,6 +504,44 @@ pub fn Compiler(size: comptime_int) type { } } + fn ifStatement(self: *Self) void { + self.consume(Token.LEFT_PAREN, "Expect '(' after 'if'."); + self.expression(); + self.consume(Token.RIGHT_PAREN, "Expect ')' after condition"); + + const thenJump = self.emitJump(OP.JUMP_IF_FALSE); + self.emitOP(OP.POP); + self.statement(); + const elseJump = self.emitJump(OP.JUMP); + self.patchJump(thenJump); + self.emitOP(OP.POP); + if (self.match(Token.ELSE)) self.statement(); + self.patchJump(elseJump); + } + + fn emitJump(self: *Self, instruction: OP) usize { + self.emitOP(instruction); + self.emitByte(0xff); + self.emitByte(0xff); + return self.currentChunk().code.len - 2; + } + + fn patchJump(self: *Self, offset: usize) void { + const jump = self.currentChunk().code.len - offset - 2; + if (jump > std.math.maxInt(u16)) { + self.errorAtPrevious("Jump too large"); + return; + } + + self.currentChunk().code.set(offset, @intCast((jump >> 8) & 0xff)) catch { + self.errorAtPrevious("Invalid jump offset"); + }; + self.currentChunk().code.set(offset + 1, @intCast(jump & 0xff)) catch { + self.errorAtPrevious("Invalid jump offset"); + }; + + } + fn block(self: *Self) void { while(!self.check(Token.RIGHT_BRACE) and !self.check(Token.EOF)) { self.declaration(); diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig @@ -44,6 +44,8 @@ pub fn disassembleInstruction(ch: chunk.Chunk, offset: usize) !usize { @intFromEnum(OP.POP) => simpleInstruction("OP_POP", offset), @intFromEnum(OP.GET_LOCAL) => try byteInstruction("OP_GET_LOCAL", ch, offset), @intFromEnum(OP.SET_LOCAL) => try byteInstruction("OP_SET_LOCAL", ch, offset), + @intFromEnum(OP.JUMP_IF_FALSE) => try jumpInstruction("OP_JUMP_IF_FALSE", 1, ch, offset), + @intFromEnum(OP.JUMP) => try jumpInstruction("OP_JUMP", 1, ch, offset), else => blk: { print("Unknown opcode {}\n", .{try ch.code.get(offset)}); break :blk offset + 1; @@ -68,3 +70,12 @@ fn byteInstruction(name: []const u8, ch: chunk.Chunk, offset: usize) !usize { print("{s:<16} {d:4}\n", .{name, try ch.code.get(offset+1)}); return offset + 2; } + +fn jumpInstruction(name: []const u8, sign: u1, ch: chunk.Chunk, offset: usize) !usize { + const msb: u16 = try ch.code.get(offset + 1); + const lsb: u16 = try ch.code.get(offset + 2); + const jump = (msb << 8) | lsb; + + print("{s:<16} {d:4} -> {d}\n", .{name, offset, offset + 3 + sign * jump}); + return offset + 3; +} diff --git a/zlox/src/main.zig b/zlox/src/main.zig @@ -47,7 +47,7 @@ pub fn repl(allocator: std.mem.Allocator, dbg: bool) anyerror!void { while (Linenoise.linenoise("lox> ")) |line| { defer Linenoise.linenoiseFree(line); VM.interpret(std.mem.span(line), dbg) catch |err| { - std.debug.print("Error: {}\n", .{err}); + std.debug.print("\nError: {}\n", .{err}); }; _ = Linenoise.linenoiseHistoryAdd(line); } diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -28,6 +28,8 @@ pub const VM = struct { var chunk = try compiler.Compiler(stackSize).compile(source, &self.objects, self.allocator); defer chunk.deinit(); + //try debug.disassembleChunk(chunk, "Main"); + try Interpreter(stackSize).run(self, &chunk, dbg); } @@ -45,14 +47,24 @@ pub const VM = struct { try self.execute(dbg); } + fn ip_advance(self: *@This(), adv: usize) void { + self.ip += adv; + } + fn read_byte(self: *@This()) u8 { const out: u8 = self.ip[0]; - self.ip += 1; + self.ip_advance(1); return out; } + fn read_short(self: *@This()) u16 { + const msb: u16 = self.read_byte(); + const lsb: u16 = self.read_byte(); + return (msb << 8) | lsb; + } + fn read_constant(self: *@This()) Value { - return self.chunk.constants.data[self.read_byte()]; + return self.chunk.constants.get(self.read_byte()) catch unreachable; } fn read_string(self: *@This()) *const Obj.String { @@ -124,6 +136,15 @@ pub const VM = struct { try self.binary_op(Value.number, Value.number, Callback.add); } }, + @intFromEnum(OP.JUMP_IF_FALSE) => { + const offset = self.read_short(); + if (!self.peek(0).isTruthy()) { + self.ip_advance(offset); + } + }, + @intFromEnum(OP.JUMP) => { + self.ip_advance(self.read_short()); + }, @intFromEnum(OP.GET_LOCAL) => { self.push(self.stack[self.read_byte()]); },