DzLox

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

commit a15a212daf2301d4e216adb84ddc078a489f4882
parent 807cfbd512863405f87ce32539e9cd7cf2ca988b
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date:   Thu,  1 Aug 2024 20:07:13 +0200

And, or, while

Diffstat:
Mzlox/src/chunk.zig | 1+
Mzlox/src/compiler.zig | 53+++++++++++++++++++++++++++++++++++++++++++++++++++--
Mzlox/src/debug.zig | 9+++++----
Mzlox/src/vm.zig | 15+++++++++++----
4 files changed, 68 insertions(+), 10 deletions(-)

diff --git a/zlox/src/chunk.zig b/zlox/src/chunk.zig @@ -28,6 +28,7 @@ pub const OP = enum(u8) { SET_LOCAL, JUMP_IF_FALSE, JUMP, + LOOP, }; pub const Chunk = struct { diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -101,7 +101,7 @@ pub fn Compiler(size: comptime_int) type { T.IDENTIFIER => R(S.variable, null, P.NONE ), T.STRING => R(S.string, null, P.NONE ), T.NUMBER => R(S.number, null, P.NONE ), - T.AND => R(null, null, P.NONE ), + T.AND => R(null, S._and, P.AND ), T.CLASS => R(null, null, P.NONE ), T.ELSE => R(null, null, P.NONE ), T.FALSE => R(S.literal, null, P.NONE ), @@ -109,7 +109,7 @@ pub fn Compiler(size: comptime_int) type { T.FUN => R(null, null, P.NONE ), T.IF => R(null, null, P.NONE ), T.NIL => R(S.literal, null, P.NONE ), - T.OR => R(null, null, P.NONE ), + T.OR => R(null, S._or, P.OR ), T.PRINT => R(null, null, P.NONE ), T.RETURN => R(null, null, P.NONE ), T.SUPER => R(null, null, P.NONE ), @@ -318,6 +318,24 @@ pub fn Compiler(size: comptime_int) type { self.consume(Token.RIGHT_PAREN, "Expected ')' after expression"); } + fn _and(self: *Self, _: bool) void { + const endJump = self.emitJump(OP.JUMP_IF_FALSE); + + self.emitOP(OP.POP); + self.parsePrecedence(Precedence.AND); + self.patchJump(endJump); + } + + fn _or(self: *Self, _: bool) void { + const elseJump = self.emitJump(OP.JUMP_IF_FALSE); + const endJump = self.emitJump(OP.JUMP); + + self.patchJump(elseJump); + self.emitOP(OP.POP); + self.parsePrecedence(Precedence.OR); + self.patchJump(endJump); + } + fn unary(self: *Self, _: bool) void { const operatorType = self.previous.type catch unreachable; @@ -495,6 +513,8 @@ pub fn Compiler(size: comptime_int) type { self.printStatement(); } else if (self.match(Token.IF)) { self.ifStatement(); + } else if (self.match(Token.WHILE)) { + self.whileStatement(); } else if (self.match(Token.LEFT_BRACE)) { self.beginScope(); self.block(); @@ -504,6 +524,35 @@ pub fn Compiler(size: comptime_int) type { } } + fn whileStatement(self: *Self) void { + const loopStart = self.currentChunk().code.len; + + self.consume(Token.LEFT_PAREN, "Expect '(' after 'while'."); + self.expression(); + self.consume(Token.RIGHT_PAREN, "Expect ')' after condition"); + + const exitJump = self.emitJump(OP.JUMP_IF_FALSE); + self.emitOP(OP.POP); + self.statement(); + self.emitLoop(loopStart); + + self.patchJump(exitJump); + self.emitOP(OP.POP); + } + + fn emitLoop(self: *Self, start: usize) void { + self.emitOP(OP.LOOP); + const offset = self.currentChunk().code.len - start + 2; + + if (offset > std.math.maxInt(u16)) { + self.errorAtPrevious("Loop body too large"); + return; + } + + self.emitByte(@intCast((offset >> 8) & 0xff)); + self.emitByte(@intCast(offset & 0xff)); + } + fn ifStatement(self: *Self) void { self.consume(Token.LEFT_PAREN, "Expect '(' after 'if'."); self.expression(); diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig @@ -44,8 +44,9 @@ 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), + @intFromEnum(OP.JUMP_IF_FALSE) => try jumpInstruction("OP_JUMP_IF_FALSE", true, ch, offset), + @intFromEnum(OP.JUMP) => try jumpInstruction("OP_JUMP", true, ch, offset), + @intFromEnum(OP.LOOP) => try jumpInstruction("OP_LOOP", false, ch, offset), else => blk: { print("Unknown opcode {}\n", .{try ch.code.get(offset)}); break :blk offset + 1; @@ -71,11 +72,11 @@ fn byteInstruction(name: []const u8, ch: chunk.Chunk, offset: usize) !usize { return offset + 2; } -fn jumpInstruction(name: []const u8, sign: u1, ch: chunk.Chunk, offset: usize) !usize { +fn jumpInstruction(name: []const u8, sign: bool, 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}); + print("{s:<16} {d:4} -> {d}\n", .{name, offset, if (sign) offset + 3 + jump else offset + 3 - jump}); return offset + 3; } diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -47,13 +47,17 @@ pub const VM = struct { try self.execute(dbg); } - fn ip_advance(self: *@This(), adv: usize) void { + fn ip_add(self: *@This(), adv: usize) void { self.ip += adv; } + fn ip_sub(self: *@This(), adv: usize) void { + self.ip -= adv; + } + fn read_byte(self: *@This()) u8 { const out: u8 = self.ip[0]; - self.ip_advance(1); + self.ip_add(1); return out; } @@ -139,11 +143,14 @@ pub const VM = struct { @intFromEnum(OP.JUMP_IF_FALSE) => { const offset = self.read_short(); if (!self.peek(0).isTruthy()) { - self.ip_advance(offset); + self.ip_add(offset); } }, @intFromEnum(OP.JUMP) => { - self.ip_advance(self.read_short()); + self.ip_add(self.read_short()); + }, + @intFromEnum(OP.LOOP) => { + self.ip_sub(self.read_short()); }, @intFromEnum(OP.GET_LOCAL) => { self.push(self.stack[self.read_byte()]);