#include #include #include #include "common.h" #include "compiler.h" #include "memory.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; bool isCaptured; } Local; typedef struct { uint8_t index; bool isLocal; } Upvalue; typedef enum { TYPE_FUNCTION, TYPE_INITIALIZER, TYPE_METHOD, TYPE_SCRIPT } FunctionType; typedef struct Compiler { struct Compiler *enclosing; ObjFunction *function; FunctionType type; Local locals[UINT8_COUNT]; int localCount; Upvalue upvalues[UINT8_COUNT]; int scopeDepth; } Compiler; typedef struct ClassCompiler { struct ClassCompiler *enclosing; } ClassCompiler; static int resolveUpvalue(Compiler *, Token *); Parser parser; Compiler *current = NULL; ClassCompiler *currentClass = NULL; Chunk *compilingChunk; 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); } } static 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); 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 emitConstant(Value value) { emitBytes(OP_CONSTANT, makeConstant(value)); } 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 initCompiler(Compiler *compiler, FunctionType type) { compiler->enclosing = current; compiler->function = NULL; compiler->type = type; compiler->localCount = 0; compiler->scopeDepth = 0; compiler->function = newFunction(); current = compiler; if (type != TYPE_SCRIPT) { 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 : "