commit a15a212daf2301d4e216adb84ddc078a489f4882
parent 807cfbd512863405f87ce32539e9cd7cf2ca988b
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date: Thu, 1 Aug 2024 20:07:13 +0200
And, or, while
Diffstat:
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()]);