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; } }