/** ****************************************************************************** * @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 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; } Local; typedef enum { TYPE_FUNCTION, 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 int scopeDepth; } Compiler; Parser parser; Compiler *current = NULL; static void parsePrecedence(Precedence); static uint8_t parseVariable(const char *); 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 initCompiler(Compiler *compiler, FunctionType type); static void beginScope(); static void endScope(); static void block(); static ObjFunction *endCompiler(); 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() { 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_CONSTANT, makeConstant(OBJ_VAL(fun))); } 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 { 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_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) { 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->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 : "