diff --git a/samples/ch23_while.lox b/samples/ch23_while.lox new file mode 100644 index 0000000..b64c0f8 --- /dev/null +++ b/samples/ch23_while.lox @@ -0,0 +1,6 @@ +var i = 0; + +while(i < 10) { + print i; + i = i + 1; +} \ No newline at end of file diff --git a/src/chunk.zig b/src/chunk.zig index edde36e..7f9d6aa 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -101,6 +101,7 @@ pub const Chunk = struct { @intFromEnum(OpCode.OP_SET_LOCAL) => return utils.byte_instruction("OP_SET_LOCAL", self, offset), @intFromEnum(OpCode.OP_JUMP) => return utils.jump_instruction("OP_JUMP", 1, self, offset), @intFromEnum(OpCode.OP_JUMP_IF_FALSE) => return utils.jump_instruction("OP_JUMP_IF_FALSE", 1, self, offset), + @intFromEnum(OpCode.OP_LOOP) => return utils.jump_instruction("OP_LOOP", -1, self, offset), else => { debug.print("unknown opcode {d}\n", .{instruction}); return offset + 1; diff --git a/src/compile.zig b/src/compile.zig index 4dcfa81..5e6d0af 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -362,6 +362,8 @@ const Parser = struct { try self.print_statement(); } else if (self.match(TokenType.IF)) { try self.if_statement(); + } else if (self.match(TokenType.WHILE)) { + try self.while_statement(); } else if (self.match(TokenType.LEFT_BRACE)) { self.begin_scope(); try self.block(); @@ -616,6 +618,32 @@ const Parser = struct { try self.parse_precedence(Precedence.Or); self.patch_jump(end_jump); } + + fn while_statement(self: *Parser) ParsingError!void { + const loop_start = self.chunk.count; + self.consume(TokenType.LEFT_PAREN, "Expect '(' after 'while'."); + try self.expression(); + self.consume(TokenType.RIGHT_PAREN, "Expect ')' after condition."); + + const exit_jump = try self.emit_jump(@intFromEnum(OpCode.OP_JUMP_IF_FALSE)); + try self.emit_byte(@intFromEnum(OpCode.OP_POP)); + try self.statement(); + try self.emit_loop(loop_start); + self.patch_jump(exit_jump); + try self.emit_byte(@intFromEnum(OpCode.OP_POP)); + } + + fn emit_loop(self: *Parser, loop_start: usize) ParsingError!void { + try self.emit_byte(@intFromEnum(OpCode.OP_LOOP)); + + const offset = self.chunk.count - loop_start + 2; + if (offset > 65536) { + self.error_msg("Loop body too large."); + } + + try self.emit_byte(@intCast((offset >> 8) & 0xff)); + try self.emit_byte(@intCast(offset & 0xff)); + } }; const Compiler = struct { diff --git a/src/opcode.zig b/src/opcode.zig index 855a11f..7c0cdde 100644 --- a/src/opcode.zig +++ b/src/opcode.zig @@ -21,5 +21,6 @@ pub const OpCode = enum(u8) { OP_PRINT, OP_JUMP, OP_JUMP_IF_FALSE, + OP_LOOP, OP_RETURN, }; diff --git a/src/vm.zig b/src/vm.zig index b4871dc..c21e200 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -192,6 +192,10 @@ pub const VM = struct { self.ip.? += offset; } }, + @intFromEnum(OpCode.OP_LOOP) => { + const offset = self.read_short(); + self.ip.? -= offset; + }, else => { debug.print("Invalid instruction: {d}\n", .{instruction}); return InterpretResult.RUNTIME_ERROR;