/** ****************************************************************************** * @file : compiler.c * @author : simon * @brief : None * @attention : None * @date : 2023/8/17 ****************************************************************************** */ #include "compiler.h" #include "common.h" #include "scanner.h" #ifdef DEBUG_PRINT_CODE #include "debug.h" #endif #include "memory.h" typedef struct { Token current; Token previous; bool hadError; bool panicMode; } Parser; typedef enum { PREC_NONE, PREC_ASSIGNMENT,// = PREC_OR, // or PREC_AND, // and PREC_EQUALITY, // == != PREC_COMPARISON,// < > <= >= PREC_TERM, // + - PREC_FACTOR, // * / PREC_UNARY, // ! - PREC_CALL, // . () PREC_PRIMARY } Precedence; typedef void (*ParseFn)(bool canAssign); typedef struct { ParseFn prefix; ParseFn infix; Precedence precedence; } ParseRule; typedef struct { Token name; int depth; bool isCaptured; } Local; typedef struct { uint8_t index; bool isLocal; } Upvalue; typedef enum { TYPE_METHOD, TYPE_FUNCTION, TYPE_INITIALIZER, TYPE_SCRIPT, } FunctionType; typedef struct Compiler { struct Compiler *enclosing; ObjFunction *function; FunctionType type; Local locals[UINT8_COUNT]; int localCount;/// how many locals are in scope Upvalue upvalues[UINT8_COUNT]; int scopeDepth; } Compiler; typedef struct ClassCompiler { struct ClassCompiler *enclosing; bool hasSuperclass; } ClassCompiler; Parser parser; Compiler *current = NULL; ClassCompiler *currentClass = NULL; static void parsePrecedence(Precedence); static uint8_t parseVariable(const char *); static void declareVariable(); static void defineVariable(uint8_t); static uint8_t identifierConstant(Token *name); static bool identifiersEqual(Token *a, Token *b); static ParseRule *getRule(TokenType); static void statement(); static void declaration(); static void markInitialized(); static void namedVariable(Token name, bool canAssign); static void addLocal(Token name); static void initCompiler(Compiler *compiler, FunctionType type); static void beginScope(); static void endScope(); static void block(); static ObjFunction *endCompiler(); static void variable(bool); static uint8_t argumentList(); static Chunk *currentChunk() { return ¤t->function->chunk; } static void errorAt(Token *token, const char *message) { if (parser.panicMode) return; parser.panicMode = true; fprintf(stderr, "[line %d] Error", token->line); if (token->type == TOKEN_EOF) { fprintf(stderr, " at end"); } else if (token->type == TOKEN_ERROR) { ///! Nothing. } else { fprintf(stderr, " at '%.*s'", token->length, token->start); } fprintf(stderr, ": %s\n", message); parser.hadError = true; } static void error(const char *message) { errorAt(&parser.previous, message); } static void errorAtCurrent(const char *message) { errorAt(&parser.current, message); } static void advance() { parser.previous = parser.current; for (;;) { parser.current = scanToken(); if (parser.current.type != TOKEN_ERROR) break; errorAtCurrent(parser.current.start); } } void consume(TokenType type, const char *message) { if (parser.current.type == type) { advance(); return; } errorAtCurrent(message); } static bool check(TokenType type) { return parser.current.type == type; } static bool match(TokenType type) { if (!check(type)) return false; advance(); return true; } static void emitByte(uint8_t byte) { writeChunk(currentChunk(), byte, parser.previous.line); } static void emitBytes(uint8_t byte1, uint8_t byte2) { emitByte(byte1); emitByte(byte2); } static void emitLoop(int loopStart) { emitByte(OP_LOOP); int offset = currentChunk()->count - loopStart + 2; if (offset > UINT16_MAX) error("Loop body too large."); emitByte((offset >> 8) & 0xFF); emitByte(offset & 0xFF); } static int emitJump(uint8_t instruction) { emitByte(instruction); emitByte(0xff);// 占位字节,后面填入需要跳转的 offset emitByte(0xff);// return currentChunk()->count - 2; } static void emitReturn() { if (current->type == TYPE_INITIALIZER) { emitBytes(OP_GET_LOCAL, 0); } else { emitByte(OP_NIL); } emitByte(OP_RETURN); } static uint8_t makeConstant(Value value) { int constant = addConstant(currentChunk(), value); if (constant > UINT8_MAX) { error("Too many constants in one chunk."); return 0; } return (uint8_t) constant; } static void expression() { parsePrecedence(PREC_ASSIGNMENT); } static void function(FunctionType type) { Compiler compiler; initCompiler(&compiler, type); beginScope(); consume(TOKEN_LEFT_PAREN, "Expect '(' after function name."); ///! Parameters if (!check(TOKEN_RIGHT_PAREN)) { do { current->function->arity++; if (current->function->arity > 255) { errorAtCurrent("Can't have more than 255 parameters."); } uint8_t constant = parseVariable("Expect parameter name."); defineVariable(constant); } while (match(TOKEN_COMMA)); } consume(TOKEN_RIGHT_PAREN, "Expect ')' after parameters."); consume(TOKEN_LEFT_BRACE, "Expect '{' before function body."); ///! Body block(); ObjFunction *fun = endCompiler(); emitBytes(OP_CLOSURE, makeConstant(OBJ_VAL(fun))); for (int i = 0; i < fun->upvalueCount; i++) { emitByte(compiler.upvalues[i].isLocal ? 1 : 0); emitByte(compiler.upvalues[i].index); } //! NOTE: //! this beginScope() doesn't have a corresponding endScope() call. //! Because we end Compiler completely when we reach the end of the //! function body, there's no need to close the lingering outermost //! scope. } static void method() { //! To define a new method, the VM needs three things: //! //! 1. The name of the method. //! //! 2. The closure for the method body. //! //! 3. The class to bind the method to. consume(TOKEN_IDENTIFIER, "Expect method name."); uint8_t constant = identifierConstant(&parser.previous); // Method Body ObjClosure FunctionType type = TYPE_METHOD; if (parser.previous.length == 4 && memcmp(parser.previous.start, "init", 4) == 0) { type = TYPE_INITIALIZER; } function(type); emitBytes(OP_METHOD, constant); } static Token syntheticToken(const char *text) { Token token; token.start = text; token.length = (int) strlen(text); return token; } static void classDeclaration() { consume(TOKEN_IDENTIFIER, "Expect class name."); Token className = parser.previous; uint8_t nameConstant = identifierConstant(&parser.previous); declareVariable(); emitBytes(OP_CLASS, nameConstant); defineVariable(nameConstant); ClassCompiler classCompiler; classCompiler.enclosing = currentClass; classCompiler.hasSuperclass = false; currentClass = &classCompiler; // Superclass if (match(TOKEN_LESS)) { consume(TOKEN_IDENTIFIER, "Expect superclass name."); variable(false); if (identifiersEqual(&className, &parser.previous)) { error("A class can't inherit from itself."); } beginScope(); addLocal(syntheticToken("super")); defineVariable(0); namedVariable(className, false); emitByte(OP_INHERIT); classCompiler.hasSuperclass = true; } namedVariable(className, false); consume(TOKEN_LEFT_BRACE, "Expect '{' before class body."); while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) { method(); } consume(TOKEN_RIGHT_BRACE, "Expect '}' after class body."); emitByte(OP_POP); if (classCompiler.hasSuperclass) { endScope(); } currentClass = currentClass->enclosing; } static void funDeclaration() { uint8_t global = parseVariable("Expect function name."); markInitialized(); function(TYPE_FUNCTION); defineVariable(global); } /// for example: var a; var b = 10; static void varDeclaration() { uint8_t global = parseVariable("Expect variable name."); // 变量初始化 if (match(TOKEN_EQUAL)) { expression(); } else { emitByte(OP_NIL); } consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration."); defineVariable(global); } static void expressionStatement() { expression(); consume(TOKEN_SEMICOLON, "Expect ';' after expression."); emitByte(OP_POP); } static void patchJump(int offset) { // -2 to adjust for the bytecode for the jump offset itself. int jump = currentChunk()->count - offset - 2; if (jump > UINT16_MAX) error("Too much code to jump over."); currentChunk()->code[offset] = (jump >> 8) & 0xFF; currentChunk()->code[offset + 1] = jump & 0xFF; } static void forStatement() { /** * ********************************************************* * initializer clause * L4: condition expression * OP_JUMP_IF_FALSE L1 * OP_POP * OP_JUMP L3 * L2: increment expression * OP_POP * OP_LOOP L4 * L3: body statement * OP_LOOP L2 * L1: OP_POP * continues... * ********************************************************* */ beginScope(); // for(I;II;III) consume(TOKEN_LEFT_PAREN, "Expect '(' after 'for'."); ///! I.<> if (match(TOKEN_SEMICOLON)) { // No initializer. } else if (match(TOKEN_VAR)) { varDeclaration(); } else { expressionStatement();// ; op_pop } ///! II.<> int loopStart = currentChunk()->count; int exitJump = -1; if (!match(TOKEN_SEMICOLON)) { expression(); consume(TOKEN_SEMICOLON, "Expect ';' after loop conditon. "); // Jump out of the loop if the condition is false. exitJump = emitJump(OP_JUMP_IF_FALSE); emitByte(OP_POP); } ///! III.<> if (!match(TOKEN_RIGHT_PAREN)) { int bodyJump = emitJump(OP_JUMP); int incrementStart = currentChunk()->count; expression(); emitByte(OP_POP); consume(TOKEN_RIGHT_PAREN, "Expect ')' after for clauses."); emitLoop(loopStart); loopStart = incrementStart; patchJump(bodyJump); } ///! <> statement(); emitLoop(loopStart); if (exitJump != -1) { patchJump(exitJump); emitByte(OP_POP);// Condition. } endScope(); } static void returnStatement() { if (current->type == TYPE_SCRIPT) { error("Can't return from top-level code."); } if (match(TOKEN_SEMICOLON)) { emitReturn(); } else { if (current->type == TYPE_INITIALIZER) { error("Can't return a value from an initializer."); } expression(); consume(TOKEN_SEMICOLON, "Expect ';' after return value."); emitByte(OP_RETURN); } } static void ifStatement() { consume(TOKEN_LEFT_PAREN, "Expect '(' after 'if'."); expression(); consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition."); int thenJump = emitJump(OP_JUMP_IF_FALSE); emitByte(OP_POP); statement();// then branch statement int elseJump = emitJump(OP_JUMP); patchJump(thenJump); emitByte(OP_POP); if (match(TOKEN_ELSE)) statement();// else branch statement patchJump(elseJump); } static void whileStatement() { int loopStart = currentChunk()->count; consume(TOKEN_LEFT_PAREN, "Expect '(' after 'while'."); expression();// while condition. consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition."); int exitJump = emitJump(OP_JUMP_IF_FALSE); emitByte(OP_POP); statement();// while body. emitLoop(loopStart); patchJump(exitJump); emitByte(OP_POP); } static void printStatement() { expression(); consume(TOKEN_SEMICOLON, "Expect ';' after value."); emitByte(OP_PRINT); } static void synchronize() { parser.panicMode = false; while (parser.current.type != TOKEN_EOF) { if (parser.previous.type == TOKEN_SEMICOLON) return; switch (parser.current.type) { case TOKEN_CLASS: case TOKEN_FUN: case TOKEN_VAR: case TOKEN_FOR: case TOKEN_IF: case TOKEN_WHILE: case TOKEN_PRINT: case TOKEN_RETURN: return; default:;// Do nothing. } advance(); } } ///declaration → classDecl /// | funDecl /// | varDecl /// | statement ; static void declaration() { if (match(TOKEN_CLASS)) { classDeclaration(); } else if (match(TOKEN_FUN)) { funDeclaration(); } else if (match(TOKEN_VAR)) { varDeclaration(); } else { statement(); } if (parser.panicMode) synchronize(); } static void block() { while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) { declaration(); } consume(TOKEN_RIGHT_BRACE, "Expect '}' after block."); } ///statement → exprStmt /// | forStmt /// | ifStmt /// | printStmt /// | returnStmt /// | whileStmt /// | block ; static void statement() { if (match(TOKEN_PRINT)) { printStatement(); } else if (match(TOKEN_FOR)) { forStatement(); } else if (match(TOKEN_IF)) { ifStatement(); } else if (match(TOKEN_RETURN)) { returnStatement(); } else if (match(TOKEN_WHILE)) { whileStatement(); } else if (match(TOKEN_LEFT_BRACE)) { ///! block → "{" declaration* "}" ; beginScope(); block(); endScope(); } else { expressionStatement(); } } static void beginScope() { current->scopeDepth++; } static void endScope() { current->scopeDepth--; while (current->localCount > 0 && current->locals[current->localCount - 1].depth > current->scopeDepth) { if (current->locals[current->localCount - 1].isCaptured) { emitByte(OP_CLOSE_UPVALUE); } else { emitByte(OP_POP); } current->localCount--; } } static void emitConstant(Value value) { emitBytes(OP_CONSTANT, makeConstant(value)); } static void initCompiler(Compiler *compiler, FunctionType type) { compiler->enclosing = current;// 上一极的 compiler compiler->function = NULL; compiler->type = type; compiler->localCount = 0; compiler->scopeDepth = 0; compiler->function = newFunction(); current = compiler; if (type != TYPE_SCRIPT) { ///! Function name current->function->name = copyString(parser.previous.start, parser.previous.length); } Local *local = ¤t->locals[current->localCount++]; local->depth = 0; local->isCaptured = false; if (type != TYPE_FUNCTION) { local->name.start = "this"; local->name.length = 4; } else { local->name.start = ""; local->name.length = 0; } } static ObjFunction *endCompiler() { emitReturn(); ObjFunction *function = current->function; #ifdef DEBUG_PRINT_CODE if (!parser.hadError) { disassembleChunk(currentChunk(), function->name != NULL ? function->name->chars : "