zlox/src/compile.zig

1064 lines
37 KiB
Zig

const std = @import("std");
const debug = std.debug;
const Allocator = std.mem.Allocator;
const constants = @import("./constant.zig");
const Obj = @import("./object.zig").Obj;
const ObjType = @import("./object.zig").ObjType;
const OpCode = @import("./opcode.zig").OpCode;
const Scanner = @import("./scanner.zig").Scanner;
const Token = @import("./scanner.zig").Token;
const TokenType = @import("./scanner.zig").TokenType;
const Chunk = @import("./chunk.zig").Chunk;
const Value = @import("./values.zig").Value;
const VM = @import("./vm.zig").VM;
const ParsingError = @import("./errors.zig").ParsingError;
const identifiers_equals = @import("./utils.zig").identifiers_equals;
const Precedence = enum {
None,
Assignement,
Or,
And,
Equality,
Comparison,
Term,
Factor,
Unary,
Index,
Call,
Primary,
};
const ParserFn = *const fn (*Parser, bool) ParsingError!void;
const ClassCompiler = struct {
enclosing: ?*ClassCompiler,
};
const ParserRule = struct {
prefix: ?ParserFn,
infix: ?ParserFn,
precedence: Precedence,
};
pub const Parser = struct {
compiler: *Compiler,
current: ?Token,
previous: ?Token,
scanner: *Scanner,
had_error: bool,
panic_mode: bool,
vm: *VM,
class_compiler: ?*ClassCompiler,
fn new(vm: *VM, compiler: *Compiler, scanner: *Scanner) Parser {
return Parser{
.compiler = compiler,
.current = null,
.previous = null,
.scanner = scanner,
.had_error = false,
.panic_mode = false,
.vm = vm,
.class_compiler = null,
};
}
inline fn current_chunk(self: *Parser) *Chunk {
return self.compiler.function.chunk;
}
inline fn current_class_compiler(self: *Parser) ?*ClassCompiler {
return self.class_compiler;
}
fn advance(self: *Parser) void {
self.previous = self.current;
while (true) {
self.current = self.scanner.scan_token();
if (self.current.?.token_type != TokenType.ERROR) {
break;
}
self.error_at_current(self.current.?.start);
}
}
fn expression(self: *Parser) ParsingError!void {
try self.parse_precedence(Precedence.Assignement);
}
fn consume(self: *Parser, token_type: TokenType, error_message: []const u8) void {
if (self.current.?.token_type == token_type) {
self.advance();
return;
}
self.error_at_current(error_message);
}
fn error_at_current(self: *Parser, error_message: []const u8) void {
self.error_at(self.current.?, error_message);
}
fn error_at(self: *Parser, token: Token, error_message: []const u8) void {
if (self.panic_mode) {
return;
}
self.panic_mode = true;
debug.print("[line {d}] Error", .{token.line});
if (token.token_type == TokenType.EOF) {
debug.print(" at end", .{});
} else if (token.token_type == TokenType.ERROR) {
// Nothing
} else {
const expr = std.mem.trimRight(u8, token.start[0..token.length], "\n");
debug.print(" at '{s}'", .{expr});
}
debug.print(": {s}\n", .{error_message});
self.had_error = true;
}
fn error_msg(self: *Parser, error_message: []const u8) void {
self.error_at(self.previous.?, error_message);
}
fn emit_byte(self: *Parser, byte: u8) ParsingError!void {
self.current_chunk().write(byte, self.previous.?.line) catch |err| {
switch (err) {
error.OutOfMemory => return ParsingError.OutOfMemory,
}
};
}
fn emit_bytes(self: *Parser, byte0: u8, byte1: u8) ParsingError!void {
try self.emit_byte(byte0);
try self.emit_byte(byte1);
}
fn emit_return(self: *Parser) ParsingError!void {
if (self.compiler.function_type == FunctionType.Initializer) {
try self.emit_bytes(@intFromEnum(OpCode.OP_GET_LOCAL), 0);
} else {
try self.emit_byte(@intFromEnum(OpCode.OP_NIL));
}
try self.emit_byte(@intFromEnum(OpCode.OP_RETURN));
}
fn end_parser(self: *Parser) !*Obj.Function {
try self.emit_return();
if (!self.had_error and constants.DEBUG_PRINT_CODE) {
self.current_chunk().dissassemble("code");
}
const function_obj = self.compiler.function;
if (self.compiler.enclosing != null) {
self.compiler = self.compiler.enclosing.?;
}
return function_obj;
}
fn number(self: *Parser, can_assign: bool) ParsingError!void {
_ = can_assign;
const value = std.fmt.parseFloat(f64, self.previous.?.start[0..self.previous.?.length]) catch {
self.error_msg("Failed converting float.");
return ParsingError.FloatConv;
};
self.emit_constant(Value.number_val(value)) catch |err| {
self.error_msg("Failed emiting constant.");
return switch (err) {
error.OutOfMemory => ParsingError.OutOfMemory,
else => ParsingError.Unknown,
};
};
}
fn emit_constant(self: *Parser, value: Value) !void {
const constant = try self.make_constant(value);
try self.emit_bytes(@intFromEnum(OpCode.OP_CONSTANT), constant);
}
fn make_constant(self: *Parser, value: Value) !u8 {
const constant = try self.current_chunk().add_constant(value);
if (constant > constants.UINT8_MAX) {
self.error_msg("Too many constants in one chunk.");
return 0;
}
return @intCast(constant);
}
fn grouping(self: *Parser, can_assign: bool) ParsingError!void {
_ = can_assign;
try self.expression();
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after expression.");
}
fn index_(self: *Parser, can_assign: bool) ParsingError!void {
try self.expression();
self.consume(TokenType.RIGHT_BRACKET, "Expecting ']");
if (can_assign and self.match(TokenType.EQUAL)) {
try self.expression();
try self.emit_byte(@intFromEnum(OpCode.OP_INDEX_SET));
} else {
try self.emit_byte(@intFromEnum(OpCode.OP_INDEX_GET));
}
}
fn unary(self: *Parser, can_assign: bool) ParsingError!void {
_ = can_assign;
const operation_type = self.previous.?.token_type;
// Compile the operand
try self.parse_precedence(Precedence.Unary);
// Emit the operator instruction
switch (operation_type) {
TokenType.MINUS => self.emit_byte(@intFromEnum(OpCode.OP_NEGATE)) catch |err| {
self.error_msg("Failed emiting NEGATE opcode.");
return switch (err) {
error.OutOfMemory => ParsingError.OutOfMemory,
else => ParsingError.Unknown,
};
},
TokenType.BANG => self.emit_byte(@intFromEnum(OpCode.OP_NOT)) catch |err| {
self.error_msg("Failed emiting NOT opcode.");
return switch (err) {
error.OutOfMemory => ParsingError.OutOfMemory,
else => ParsingError.Unknown,
};
},
else => {},
}
}
fn binary(self: *Parser, can_assign: bool) ParsingError!void {
_ = can_assign;
const operator_type = self.previous.?.token_type;
const parser_rule = Parser.get_rule(operator_type);
try self.parse_precedence(@enumFromInt(1 + @intFromEnum(parser_rule.precedence)));
return switch (operator_type) {
TokenType.BANG_EQUAL => self.emit_bytes(@intFromEnum(OpCode.OP_EQUAL), @intFromEnum(OpCode.OP_NOT)),
TokenType.EQUAL_EQUAL => self.emit_byte(@intFromEnum(OpCode.OP_EQUAL)),
TokenType.GREATER => self.emit_byte(@intFromEnum(OpCode.OP_GREATER)),
TokenType.GREATER_EQUAL => self.emit_bytes(@intFromEnum(OpCode.OP_LESS), @intFromEnum(OpCode.OP_NOT)),
TokenType.LESS => self.emit_byte(@intFromEnum(OpCode.OP_LESS)),
TokenType.LESS_EQUAL => self.emit_bytes(@intFromEnum(OpCode.OP_GREATER), @intFromEnum(OpCode.OP_NOT)),
TokenType.PLUS => self.emit_byte(@intFromEnum(OpCode.OP_ADD)),
TokenType.MINUS => self.emit_byte(@intFromEnum(OpCode.OP_SUBSTRACT)),
TokenType.STAR => self.emit_byte(@intFromEnum(OpCode.OP_MULTIPLY)),
TokenType.SLASH => self.emit_byte(@intFromEnum(OpCode.OP_DIVIDE)),
else => return,
};
}
fn get_rule(operator_type: TokenType) ParserRule {
return switch (operator_type) {
TokenType.LEFT_PAREN => ParserRule{ .prefix = grouping, .infix = call, .precedence = Precedence.Call },
TokenType.RIGHT_PAREN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.LEFT_BRACE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.RIGHT_BRACE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.COMMA => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.DOT => ParserRule{ .prefix = null, .infix = dot, .precedence = Precedence.Call },
TokenType.MINUS => ParserRule{ .prefix = unary, .infix = binary, .precedence = Precedence.Term },
TokenType.PLUS => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Term },
TokenType.LEFT_BRACKET => ParserRule{ .prefix = null, .infix = index_, .precedence = Precedence.Unary },
TokenType.RIGHT_BRACKET => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.SEMICOLON => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.SLASH => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
TokenType.STAR => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
TokenType.BANG => ParserRule{ .prefix = unary, .infix = null, .precedence = Precedence.None },
TokenType.BANG_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Equality },
TokenType.EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.EQUAL_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Equality },
TokenType.GREATER => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
TokenType.GREATER_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
TokenType.LESS => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
TokenType.LESS_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
TokenType.IDENTIFIER => ParserRule{ .prefix = variable, .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.AND => ParserRule{ .prefix = null, .infix = and_, .precedence = Precedence.And },
TokenType.CLASS => 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.FOR => 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.NIL => ParserRule{ .prefix = literal, .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.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.SUPER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.THIS => ParserRule{ .prefix = this_, .infix = null, .precedence = Precedence.None },
TokenType.TRUE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
TokenType.VAR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.WHILE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.ERROR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
TokenType.EOF => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
};
}
fn parse_precedence(self: *Parser, precedence: Precedence) ParsingError!void {
self.advance();
const prefix_rule = Parser.get_rule(self.previous.?.token_type).prefix;
if (prefix_rule == null) {
self.error_msg("Expect expression.");
return;
}
const can_assign = @intFromEnum(precedence) <= @intFromEnum(Precedence.Assignement);
try prefix_rule.?(self, can_assign);
while (@intFromEnum(precedence) <= @intFromEnum(Parser.get_rule(self.current.?.token_type).precedence)) {
self.advance();
const infix_rule = Parser.get_rule(self.previous.?.token_type).infix;
try infix_rule.?(self, can_assign);
}
if (can_assign and self.match(TokenType.EQUAL)) {
self.error_msg("Invalid assignment target.");
}
}
fn literal(self: *Parser, can_assign: bool) ParsingError!void {
_ = can_assign;
try switch (self.previous.?.token_type) {
TokenType.NIL => self.emit_byte(@intFromEnum(OpCode.OP_NIL)),
TokenType.TRUE => self.emit_byte(@intFromEnum(OpCode.OP_TRUE)),
TokenType.FALSE => self.emit_byte(@intFromEnum(OpCode.OP_FALSE)),
else => unreachable,
};
}
fn string(self: *Parser, can_assign: bool) ParsingError!void {
_ = can_assign;
const str = self.previous.?.start[1 .. self.previous.?.length - 1];
const string_obj = self.vm.copy_string(str);
try self.emit_constant(Value.obj_val(&string_obj.obj));
}
fn variable(self: *Parser, can_assign: bool) ParsingError!void {
try self.named_variable(self.previous.?, can_assign);
}
fn named_variable(self: *Parser, token: Token, can_assign: bool) ParsingError!void {
var get_op: OpCode = OpCode.OP_GET_LOCAL;
var set_op: OpCode = OpCode.OP_SET_LOCAL;
var arg = self.resolve_local(self.compiler, token);
const upvalue_arg = self.resolve_upvalue(self.compiler, token);
if (arg != -1) {
get_op = OpCode.OP_GET_LOCAL;
set_op = OpCode.OP_SET_LOCAL;
} else if (upvalue_arg != -1) {
get_op = OpCode.OP_GET_UPVALUE;
set_op = OpCode.OP_SET_UPVALUE;
arg = upvalue_arg;
} else {
arg = try self.identifier_constant(token);
get_op = OpCode.OP_GET_GLOBAL;
set_op = OpCode.OP_SET_GLOBAL;
}
// handle assignments by index:
// - push value to the stack
// - modify it (through a OP_INDEX_SET)
// - update the variable
if (can_assign and self.match(TokenType.LEFT_BRACKET)) {
try self.emit_bytes(@intFromEnum(get_op), @intCast(arg));
try self.index_(can_assign);
try self.emit_bytes(@intFromEnum(set_op), @intCast(arg));
return;
}
if (can_assign and self.match(TokenType.EQUAL)) {
try self.expression();
try self.emit_bytes(@intFromEnum(set_op), @intCast(arg));
} else {
try self.emit_bytes(@intFromEnum(get_op), @intCast(arg));
}
}
fn declaration(self: *Parser) ParsingError!void {
if (self.match(TokenType.CLASS)) {
try self.class_declaration();
} else if (self.match(TokenType.FUN)) {
try self.fun_declaration();
} else if (self.match(TokenType.VAR)) {
try self.var_declaration();
} else {
try self.statement();
}
if (self.panic_mode) {
self.synchronize();
}
}
fn statement(self: *Parser) ParsingError!void {
if (self.match(TokenType.PRINT)) {
try self.print_statement();
} else if (self.match(TokenType.FOR)) {
try self.for_statement();
} else if (self.match(TokenType.IF)) {
try self.if_statement();
} else if (self.match(TokenType.RETURN)) {
try self.return_statement();
} else if (self.match(TokenType.WHILE)) {
try self.while_statement();
} else if (self.match(TokenType.LEFT_BRACE)) {
self.begin_scope();
try self.block();
try self.end_scope();
} else {
try self.expression_statement();
}
}
fn match(self: *Parser, token_type: TokenType) bool {
if (!self.check(token_type))
return false;
self.advance();
return true;
}
fn check(self: *Parser, token_type: TokenType) bool {
return self.current.?.token_type == token_type;
}
fn print_statement(self: *Parser) ParsingError!void {
try self.expression();
self.consume(TokenType.SEMICOLON, "Expect ';' after value.");
try self.emit_byte(@intFromEnum(OpCode.OP_PRINT));
}
fn expression_statement(self: *Parser) ParsingError!void {
try self.expression();
self.consume(TokenType.SEMICOLON, "Expect ';' after value.");
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
}
fn synchronize(self: *Parser) void {
self.panic_mode = false;
while (self.current.?.token_type != TokenType.EOF) {
if (self.previous.?.token_type == TokenType.SEMICOLON) {
return;
}
switch (self.current.?.token_type) {
TokenType.CLASS,
TokenType.FUN,
TokenType.VAR,
TokenType.FOR,
TokenType.IF,
TokenType.WHILE,
TokenType.PRINT,
TokenType.RETURN,
=> return,
else => {},
}
self.advance();
}
}
fn var_declaration(self: *Parser) ParsingError!void {
const global = try self.parse_variable("Expect variable name.");
if (self.match(TokenType.EQUAL)) {
try self.expression();
} else {
try self.emit_byte(@intFromEnum(OpCode.OP_NIL));
}
self.consume(TokenType.SEMICOLON, "Expect ';' after variable declaration.");
try self.define_variable(global);
}
fn parse_variable(self: *Parser, err_msg: []const u8) ParsingError!u8 {
self.consume(TokenType.IDENTIFIER, err_msg);
self.declare_variable();
if (self.compiler.scope_depth > 0) {
return 0;
}
return self.identifier_constant(self.previous.?);
}
fn identifier_constant(self: *Parser, token: Token) ParsingError!u8 {
const copy = &self.vm.copy_string(token.start[0..token.length]).obj;
return self.make_constant(Value.obj_val(copy));
}
fn define_variable(self: *Parser, global: u8) ParsingError!void {
if (self.compiler.scope_depth > 0) {
self.mark_initialized();
return;
}
return self.emit_bytes(@intFromEnum(OpCode.OP_DEFINE_GLOBAL), global);
}
fn mark_initialized(self: *Parser) void {
if (self.compiler.scope_depth == 0) {
return;
}
self.compiler.locals[self.compiler.local_count - 1].depth = self.compiler.scope_depth;
}
fn declare_variable(self: *Parser) void {
if (self.compiler.scope_depth == 0) {
return;
}
const name = self.previous.?;
if (self.compiler.local_count == 0) {
self.add_local(name);
return;
}
var idx = self.compiler.local_count - 1;
while (idx >= 0) {
const local = &self.compiler.locals[idx];
if (local.depth != null and local.depth.? < self.compiler.scope_depth) {
break;
}
if (identifiers_equals(name, local.name)) {
self.error_msg("Already a variable with this name in this scope.");
}
if (idx == 0) {
break;
}
idx -= 1;
}
self.add_local(name);
}
fn block(self: *Parser) ParsingError!void {
while (!self.check(TokenType.RIGHT_BRACE) and !self.check(TokenType.EOF)) {
try self.declaration();
}
self.consume(TokenType.RIGHT_BRACE, "Expect '}' after block.");
}
fn begin_scope(self: *Parser) void {
self.compiler.scope_depth += 1;
}
fn end_scope(self: *Parser) !void {
self.compiler.scope_depth -= 1;
while (self.compiler.local_count > 0 and self.compiler.locals[self.compiler.local_count - 1].depth.? > self.compiler.scope_depth) {
if (self.compiler.locals[self.compiler.local_count - 1].is_captured) {
try self.emit_byte(@intFromEnum(OpCode.OP_CLOSE_UPVALUE));
} else {
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
}
self.compiler.local_count -= 1;
}
}
fn add_local(self: *Parser, token: Token) void {
if (self.compiler.local_count == constants.UINT8_COUNT) {
self.error_msg("Too many local variables in function.");
return;
}
var local = &self.compiler.locals[self.compiler.local_count];
self.compiler.local_count += 1;
local.name = token;
local.depth = null;
local.is_captured = false;
}
fn resolve_local(self: *Parser, compiler: *Compiler, name: Token) isize {
if (compiler.local_count == 0) {
return -1;
}
var idx: u8 = @intCast(compiler.local_count - 1);
while (idx >= 0) {
const local = &compiler.locals[idx];
if (identifiers_equals(local.name, name)) {
if (local.depth == null) {
self.error_msg("Can't read local variable in its own initializer.");
}
return idx;
}
if (idx == 0) {
break;
}
idx -= 1;
}
return -1;
}
fn resolve_upvalue(self: *Parser, compiler: *Compiler, name: Token) isize {
if (compiler.enclosing == null) {
return -1;
}
const local = self.resolve_local(compiler.enclosing.?, name);
if (local != -1) {
compiler.enclosing.?.locals[@intCast(local)].is_captured = true;
return @intCast(self.add_upvalue(compiler, @intCast(local), true));
}
const upvalue = self.resolve_upvalue(compiler.enclosing.?, name);
if (upvalue != -1) {
return @intCast(self.add_upvalue(compiler, @intCast(upvalue), false));
}
return -1;
}
fn add_upvalue(self: *Parser, compiler: *Compiler, index: u8, is_local: bool) usize {
const upvalue_count = compiler.function.upvalue_count;
for (0..upvalue_count) |i| {
const upvalue: *Upvalue = &compiler.upvalues[i];
if (upvalue.index == index and upvalue.is_local == is_local) {
return i;
}
}
if (upvalue_count == constants.UINT8_COUNT) {
self.error_msg("Too many closure variables in function.");
return 0;
}
compiler.upvalues[upvalue_count].is_local = is_local;
compiler.upvalues[upvalue_count].index = index;
compiler.function.upvalue_count += 1;
return compiler.function.upvalue_count - 1;
}
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.current_chunk().count - 2;
}
fn patch_jump(self: *Parser, offset: usize) void {
const jump = self.current_chunk().count - offset - 2;
if (jump > constants.UINT16_MAX) {
self.error_msg("Too much code to jump over.");
}
const b1 = (jump >> 8) & 0xff;
const b0 = jump & 0xff;
self.current_chunk().code[offset] = @intCast(b1);
self.current_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);
}
fn while_statement(self: *Parser) ParsingError!void {
const loop_start = self.current_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.current_chunk().count - loop_start + 2;
if (offset > constants.UINT16_MAX) {
self.error_msg("Loop body too large.");
}
try self.emit_byte(@intCast((offset >> 8) & 0xff));
try self.emit_byte(@intCast(offset & 0xff));
}
fn for_statement(self: *Parser) ParsingError!void {
self.begin_scope();
self.consume(TokenType.LEFT_PAREN, "Expect '(' after 'for'.");
if (self.match(TokenType.SEMICOLON)) {
// No initializer
} else if (self.match(TokenType.VAR)) {
try self.var_declaration();
} else {
try self.expression_statement();
}
var loop_start = self.current_chunk().count;
var exit_jump: ?usize = null;
if (!self.match(TokenType.SEMICOLON)) {
try self.expression();
self.consume(TokenType.SEMICOLON, "Expect ';' after loop condition.");
// Jump out of the loop if the condition is false.
exit_jump = try self.emit_jump(@intFromEnum(OpCode.OP_JUMP_IF_FALSE));
_ = try self.emit_byte(@intFromEnum(OpCode.OP_POP)); // Condition
}
if (!self.match(TokenType.RIGHT_PAREN)) {
const body_jump = try self.emit_jump(@intFromEnum(OpCode.OP_JUMP));
const increment_start = self.current_chunk().count;
try self.expression();
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after for clauses.");
try self.emit_loop(loop_start);
loop_start = increment_start;
self.patch_jump(body_jump);
}
try self.statement();
try self.emit_loop(loop_start);
if (exit_jump != null) {
self.patch_jump(exit_jump.?);
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
}
try self.end_scope();
}
fn fun_declaration(self: *Parser) ParsingError!void {
const global: u8 = try self.parse_variable("Expect function name.");
self.mark_initialized();
try self.function(FunctionType.Function);
try self.define_variable(global);
}
fn function(self: *Parser, function_type: FunctionType) ParsingError!void {
var compiler = Compiler.new(self.vm, self.compiler, function_type);
self.compiler = &compiler;
if (function_type != FunctionType.Script) {
self.compiler.function.name = self.vm.copy_string(self.previous.?.start[0..self.previous.?.length]);
}
self.begin_scope();
self.consume(TokenType.LEFT_PAREN, "Expect '(' after function name.");
if (!self.check(TokenType.RIGHT_PAREN)) {
while (true) {
self.compiler.function.arity += 1;
if (self.compiler.function.arity > 255) {
self.error_at_current("Can't have more than 255 parameters.");
}
const constant = try self.parse_variable("Expect parameter name.");
try self.define_variable(constant);
if (!self.match(TokenType.COMMA)) {
break;
}
}
}
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after parameters.");
self.consume(TokenType.LEFT_BRACE, "Expect '{' before function body.");
try self.block();
const obj_function = try self.end_parser();
const constant = try self.make_constant(Value.obj_val(&obj_function.obj));
try self.emit_bytes(@intFromEnum(OpCode.OP_CLOSURE), constant);
for (0..obj_function.upvalue_count) |i| {
if (compiler.upvalues[i].is_local) {
try self.emit_byte(1);
} else {
try self.emit_byte(0);
}
try self.emit_byte(@intCast(compiler.upvalues[i].index));
}
}
fn call(self: *Parser, can_assign: bool) ParsingError!void {
_ = can_assign;
const arg_count = try self.argument_list();
try self.emit_bytes(@intFromEnum(OpCode.OP_CALL), @intCast(arg_count));
}
fn argument_list(self: *Parser) ParsingError!usize {
var arg_count: usize = 0;
if (!self.check(TokenType.RIGHT_PAREN)) {
while (true) {
try self.expression();
if (arg_count == 16) {
self.error_msg("Can't have more than 16 arguments.");
}
arg_count += 1;
if (!self.match(TokenType.COMMA)) {
break;
}
}
}
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after arguments.");
return arg_count;
}
fn return_statement(self: *Parser) ParsingError!void {
if (self.compiler.function_type == FunctionType.Script) {
self.error_msg("Can't return from top-level code.");
}
if (self.match(TokenType.SEMICOLON)) {
try self.emit_return();
} else {
if (self.compiler.function_type == FunctionType.Initializer) {
self.error_msg("Can't return a value from an initialiaer");
}
try self.expression();
self.consume(TokenType.SEMICOLON, "Expect ';' after return value.");
try self.emit_byte(@intFromEnum(OpCode.OP_RETURN));
}
}
fn class_declaration(self: *Parser) ParsingError!void {
self.consume(TokenType.IDENTIFIER, "Expect class name.");
const class_name = self.previous.?;
const name_constant = try self.identifier_constant(self.previous.?);
self.declare_variable();
try self.emit_bytes(@intFromEnum(OpCode.OP_CLASS), name_constant);
try self.define_variable(name_constant);
var class_compiler = ClassCompiler{
.enclosing = self.current_class_compiler(),
};
self.class_compiler = &class_compiler;
try self.named_variable(class_name, false);
self.consume(TokenType.LEFT_BRACE, "Expect '{' before class body.");
while (!self.check(TokenType.RIGHT_BRACE) and !self.check(TokenType.EOF)) {
try self.method();
}
self.consume(TokenType.RIGHT_BRACE, "Expect '}' after class body.");
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
self.class_compiler = self.current_class_compiler().?.enclosing;
}
fn dot(self: *Parser, can_assign: bool) ParsingError!void {
self.consume(TokenType.IDENTIFIER, "Expect property name after '.'.");
const name = try self.identifier_constant(self.previous.?);
if (can_assign and self.match(TokenType.EQUAL)) {
try self.expression();
try self.emit_bytes(@intFromEnum(OpCode.OP_SET_PROPERTY), name);
} else if (self.match(TokenType.LEFT_PAREN)) {
const arg_count = try self.argument_list();
try self.emit_bytes(@intFromEnum(OpCode.OP_INVOKE), name);
try self.emit_byte(@intCast(arg_count));
} else {
try self.emit_bytes(@intFromEnum(OpCode.OP_GET_PROPERTY), name);
}
}
fn method(self: *Parser) ParsingError!void {
self.consume(TokenType.IDENTIFIER, "Expect method name.");
const constant = try self.identifier_constant(self.previous.?);
var function_type: FunctionType = FunctionType.Method;
// std.debug.print("len: {d} {s}\n", .{self.previous.?.length, });
if (self.previous.?.length == 4 and std.mem.eql(u8, self.previous.?.start[0..4], "init")) {
function_type = FunctionType.Initializer;
}
try self.function(function_type);
try self.emit_bytes(@intFromEnum(OpCode.OP_METHOD), constant);
}
fn this_(self: *Parser, can_assign: bool) ParsingError!void {
if (self.current_class_compiler() == null) {
self.error_msg("Can't use 'this' outside of a class.");
return;
}
_ = can_assign;
try self.variable(false);
}
};
const FunctionType = enum {
Function,
Script,
Method,
Initializer,
};
pub const Compiler = struct {
enclosing: ?*Compiler,
function: *Obj.Function,
function_type: FunctionType,
locals: [constants.UINT8_COUNT]Local,
local_count: usize,
upvalues: [constants.UINT8_COUNT]Upvalue,
scope_depth: usize,
fn new(vm: *VM, enclosing: ?*Compiler, function_type: FunctionType) Compiler {
const obj_function = Obj.Function.new(vm);
var compiler = Compiler{
.locals = undefined,
.local_count = 0,
.upvalues = undefined,
.scope_depth = 0,
.function = obj_function,
.function_type = function_type,
.enclosing = enclosing,
};
compiler.local_count += 1;
compiler.locals[0].depth = 0;
compiler.locals[0].name = Token{
.token_type = TokenType.EOF,
.start = "",
.length = 0,
.line = 0,
};
compiler.locals[0].is_captured = false;
if (function_type != FunctionType.Function) {
compiler.locals[0].name.start = "this";
compiler.locals[0].name.length = 4;
}
return compiler;
}
fn destroy(self: *Compiler) void {
// do not destroy function here! it is used after compiler life.
_ = self;
}
};
const Local = struct {
name: Token,
depth: ?usize,
is_captured: bool,
};
const Upvalue = struct {
index: usize,
is_local: bool,
};
pub fn compile(vm: *VM, contents: []const u8) !?*Obj.Function {
var compiler = Compiler.new(vm, null, FunctionType.Script);
var scanner = Scanner.init(contents);
var parser = Parser.new(vm, &compiler, &scanner);
vm.parser = &parser;
parser.advance();
while (!parser.match(TokenType.EOF)) {
try parser.declaration();
}
const function = try parser.end_parser();
vm.parser = null;
if (!parser.had_error) {
return function;
} else {
return null;
}
}