implementing jumping back and forth (ch23)
This commit is contained in:
parent
34a40813f7
commit
9de028d09c
7
samples/ch23_if.lox
Normal file
7
samples/ch23_if.lox
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
if (true) {
|
||||||
|
print "first is true!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) {
|
||||||
|
print "second if false!";
|
||||||
|
}
|
13
samples/ch23_if_else.lox
Normal file
13
samples/ch23_if_else.lox
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
var a = 1;
|
||||||
|
|
||||||
|
if (a == 1) {
|
||||||
|
print "first is true!";
|
||||||
|
} else {
|
||||||
|
print "first is false!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a == 2) {
|
||||||
|
print "second is false!";
|
||||||
|
} else {
|
||||||
|
print "second is false!";
|
||||||
|
}
|
31
samples/ch23_if_else_logical_operators.lox
Normal file
31
samples/ch23_if_else_logical_operators.lox
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
print "and...";
|
||||||
|
|
||||||
|
if (true and true) {
|
||||||
|
print "should show";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true and false) {
|
||||||
|
print "should not show";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false and true) {
|
||||||
|
print "should not show";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false and false) {
|
||||||
|
print "should not show";
|
||||||
|
}
|
||||||
|
|
||||||
|
print "or...";
|
||||||
|
if (true or true) {
|
||||||
|
print "should show";
|
||||||
|
}
|
||||||
|
if (true or false) {
|
||||||
|
print "should show";
|
||||||
|
}
|
||||||
|
if (false or true) {
|
||||||
|
print "should show";
|
||||||
|
}
|
||||||
|
if (false or false) {
|
||||||
|
print "should show";
|
||||||
|
}
|
@ -99,6 +99,8 @@ pub const Chunk = struct {
|
|||||||
@intFromEnum(OpCode.OP_SET_GLOBAL) => return utils.constant_instruction("OP_SET_GLOBAL", self, offset),
|
@intFromEnum(OpCode.OP_SET_GLOBAL) => return utils.constant_instruction("OP_SET_GLOBAL", self, offset),
|
||||||
@intFromEnum(OpCode.OP_GET_LOCAL) => return utils.byte_instruction("OP_GET_LOCAL", self, offset),
|
@intFromEnum(OpCode.OP_GET_LOCAL) => return utils.byte_instruction("OP_GET_LOCAL", self, offset),
|
||||||
@intFromEnum(OpCode.OP_SET_LOCAL) => return utils.byte_instruction("OP_SET_LOCAL", self, offset),
|
@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),
|
||||||
else => {
|
else => {
|
||||||
debug.print("unknown opcode {d}\n", .{instruction});
|
debug.print("unknown opcode {d}\n", .{instruction});
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
|
@ -250,7 +250,7 @@ const Parser = struct {
|
|||||||
TokenType.IDENTIFIER => ParserRule{ .prefix = variable, .infix = null, .precedence = Precedence.None },
|
TokenType.IDENTIFIER => ParserRule{ .prefix = variable, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.STRING => ParserRule{ .prefix = string, .infix = null, .precedence = Precedence.None },
|
TokenType.STRING => ParserRule{ .prefix = string, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.NUMBER => ParserRule{ .prefix = number, .infix = null, .precedence = Precedence.None },
|
TokenType.NUMBER => ParserRule{ .prefix = number, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.AND => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.AND => ParserRule{ .prefix = null, .infix = and_, .precedence = Precedence.And },
|
||||||
TokenType.CLASS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.CLASS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.ELSE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.ELSE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.FALSE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
TokenType.FALSE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
||||||
@ -258,7 +258,7 @@ const Parser = struct {
|
|||||||
TokenType.FUN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.FUN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.IF => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.IF => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.NIL => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
TokenType.NIL => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.OR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.OR => ParserRule{ .prefix = null, .infix = or_, .precedence = Precedence.Or },
|
||||||
TokenType.PRINT => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.PRINT => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.SUPER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.SUPER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
@ -360,6 +360,8 @@ const Parser = struct {
|
|||||||
fn statement(self: *Parser) ParsingError!void {
|
fn statement(self: *Parser) ParsingError!void {
|
||||||
if (self.match(TokenType.PRINT)) {
|
if (self.match(TokenType.PRINT)) {
|
||||||
try self.print_statement();
|
try self.print_statement();
|
||||||
|
} else if (self.match(TokenType.IF)) {
|
||||||
|
try self.if_statement();
|
||||||
} else if (self.match(TokenType.LEFT_BRACE)) {
|
} else if (self.match(TokenType.LEFT_BRACE)) {
|
||||||
self.begin_scope();
|
self.begin_scope();
|
||||||
try self.block();
|
try self.block();
|
||||||
@ -552,6 +554,68 @@ const Parser = struct {
|
|||||||
|
|
||||||
return ParsingError.NotFound;
|
return ParsingError.NotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn if_statement(self: *Parser) !void {
|
||||||
|
self.consume(TokenType.LEFT_PAREN, "Expect '(' after 'if'.");
|
||||||
|
try self.expression();
|
||||||
|
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after condition.");
|
||||||
|
|
||||||
|
const then_jump = try self.emit_jump(@intFromEnum(OpCode.OP_JUMP_IF_FALSE));
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
|
||||||
|
try self.statement();
|
||||||
|
const else_jump = try self.emit_jump(@intFromEnum(OpCode.OP_JUMP));
|
||||||
|
|
||||||
|
self.patch_jump(then_jump);
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
|
||||||
|
|
||||||
|
if (self.match(TokenType.ELSE)) {
|
||||||
|
try self.statement();
|
||||||
|
}
|
||||||
|
self.patch_jump(else_jump);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_jump(self: *Parser, instruction: u8) ParsingError!usize {
|
||||||
|
try self.emit_byte(instruction);
|
||||||
|
try self.emit_byte(0xff);
|
||||||
|
try self.emit_byte(0xff);
|
||||||
|
|
||||||
|
return self.chunk.count - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn patch_jump(self: *Parser, offset: usize) void {
|
||||||
|
const jump = self.chunk.count - offset - 2;
|
||||||
|
|
||||||
|
if (jump > 65535) {
|
||||||
|
self.error_msg("Too much code to jump over.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const b1 = (jump >> 8) & 0xff;
|
||||||
|
const b0 = jump & 0xff;
|
||||||
|
|
||||||
|
self.chunk.code[offset] = @intCast(b1);
|
||||||
|
self.chunk.code[offset + 1] = @intCast(b0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn and_(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
_ = can_assign;
|
||||||
|
const end_jump = try self.emit_jump(@intFromEnum(OpCode.OP_JUMP_IF_FALSE));
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
|
||||||
|
|
||||||
|
try self.parse_precedence(Precedence.And);
|
||||||
|
self.patch_jump(end_jump);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn or_(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
_ = can_assign;
|
||||||
|
const else_jump = try self.emit_jump(@intFromEnum(OpCode.OP_JUMP_IF_FALSE));
|
||||||
|
const end_jump = try self.emit_jump(@intFromEnum(OpCode.OP_JUMP));
|
||||||
|
|
||||||
|
self.patch_jump(else_jump);
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
|
||||||
|
|
||||||
|
try self.parse_precedence(Precedence.Or);
|
||||||
|
self.patch_jump(end_jump);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Compiler = struct {
|
const Compiler = struct {
|
||||||
|
@ -19,5 +19,7 @@ pub const OpCode = enum(u8) {
|
|||||||
OP_NOT,
|
OP_NOT,
|
||||||
OP_NEGATE,
|
OP_NEGATE,
|
||||||
OP_PRINT,
|
OP_PRINT,
|
||||||
|
OP_JUMP,
|
||||||
|
OP_JUMP_IF_FALSE,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
};
|
};
|
||||||
|
@ -330,7 +330,7 @@ pub const Scanner = struct {
|
|||||||
},
|
},
|
||||||
'i' => self.check_keyword(1, 1, "f", TokenType.IF),
|
'i' => self.check_keyword(1, 1, "f", TokenType.IF),
|
||||||
'n' => self.check_keyword(1, 2, "il", TokenType.NIL),
|
'n' => self.check_keyword(1, 2, "il", TokenType.NIL),
|
||||||
'o' => self.check_keyword(1, 1, "or", TokenType.OR),
|
'o' => self.check_keyword(1, 1, "r", TokenType.OR),
|
||||||
'p' => self.check_keyword(1, 4, "rint", TokenType.PRINT),
|
'p' => self.check_keyword(1, 4, "rint", TokenType.PRINT),
|
||||||
'r' => self.check_keyword(1, 5, "eturn", TokenType.RETURN),
|
'r' => self.check_keyword(1, 5, "eturn", TokenType.RETURN),
|
||||||
's' => self.check_keyword(1, 4, "uper", TokenType.SUPER),
|
's' => self.check_keyword(1, 4, "uper", TokenType.SUPER),
|
||||||
|
@ -34,6 +34,16 @@ pub fn byte_instruction(opcode_name: []const u8, chunk: Chunk, offset: usize) us
|
|||||||
return offset + 2;
|
return offset + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn jump_instruction(opcode_name: []const u8, sign: i32, chunk: Chunk, offset: usize) usize {
|
||||||
|
var jump: u16 = @as(u16, chunk.code[offset + 1]) << 8;
|
||||||
|
jump |= chunk.code[offset + 2];
|
||||||
|
|
||||||
|
const address: i32 = @as(i32, @intCast(offset)) + 3 + sign * jump;
|
||||||
|
|
||||||
|
debug.print("{s:16} {d:4} -> {}", .{ opcode_name, offset, address });
|
||||||
|
return offset + 3;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn compute_hash(str: []const u8) u32 {
|
pub fn compute_hash(str: []const u8) u32 {
|
||||||
var res_hash: u32 = 2166136261;
|
var res_hash: u32 = 2166136261;
|
||||||
|
|
||||||
|
16
src/vm.zig
16
src/vm.zig
@ -182,6 +182,16 @@ pub const VM = struct {
|
|||||||
const slot = self.read_byte();
|
const slot = self.read_byte();
|
||||||
self.stack.items[slot] = self.peek(0);
|
self.stack.items[slot] = self.peek(0);
|
||||||
},
|
},
|
||||||
|
@intFromEnum(OpCode.OP_JUMP) => {
|
||||||
|
const offset = self.read_short();
|
||||||
|
self.ip.? += offset;
|
||||||
|
},
|
||||||
|
@intFromEnum(OpCode.OP_JUMP_IF_FALSE) => {
|
||||||
|
const offset = self.read_short();
|
||||||
|
if (self.peek(0).is_falsey()) {
|
||||||
|
self.ip.? += offset;
|
||||||
|
}
|
||||||
|
},
|
||||||
else => {
|
else => {
|
||||||
debug.print("Invalid instruction: {d}\n", .{instruction});
|
debug.print("Invalid instruction: {d}\n", .{instruction});
|
||||||
return InterpretResult.RUNTIME_ERROR;
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
@ -201,6 +211,12 @@ pub const VM = struct {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_short(self: *VM) u16 {
|
||||||
|
self.ip.? += 2;
|
||||||
|
|
||||||
|
return (@as(u16, self.chunk.?.code[self.ip.? - 2]) << 8) | (@as(u16, self.chunk.?.code[self.ip.? - 1]));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_constant(self: *VM) Value {
|
pub fn read_constant(self: *VM) Value {
|
||||||
return self.chunk.?.constants.values[read_byte(self)];
|
return self.chunk.?.constants.values[read_byte(self)];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user