diff --git a/clox/src/compiler.c b/clox/src/compiler.c index 2cc2ee4..c002ea8 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -1,23 +1,149 @@ #include +#include #include "common.h" #include "compiler.h" #include "scanner.h" -void compile(const char *source) { - initScanner(source); - int line = -1; - for (;;) { - Token token = scanToken(); - if (token.line != line) { - printf("%4d ", token.line); - line = token.line; - } else { - printf(" | "); - } - printf("%2d '%.*s'\n", token.type, token.length, token.start); +typedef struct { + Token current; + Token previous; + bool hadError; + bool panicMode; +} Parser; - if (token.type == TOKEN_EOF) +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; + +Parser parser; +Chunk *compilingChunk; + +static Chunk *currentChunk() { return compilingChunk; } + +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 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 emitReturn() { 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 endCompiler() { emitReturn(); } + +static void parsePrecedence(Precedence precedence) { + // What goes here? +} +static void expression() { parsePrecedence(PREC_ASSIGNMENT); } + +static void number() { + double value = strtod(parser.previous.start, NULL); + emitConstant(value); +} + +static void unary() { + TokenType operatorType = parser.previous.type; + + // Compile the operand. + parsePrecedence(PREC_UNARY); + + // Emit the operator instruction. + switch (operatorType) { + case TOKEN_MINUS: + emitByte(OP_NEGATE); + break; + default: + return; // Unreachable. + } +} + +static void grouping() { + expression(); + consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression."); +} + +bool compile(const char *source, Chunk *chunk) { + initScanner(source); + compilingChunk = chunk; + + parser.hadError = false; + parser.panicMode = false; + + advance(); + expression(); + consume(TOKEN_EOF, "Expect end of expression."); + endCompiler(); + return !parser.hadError; +} diff --git a/clox/src/compiler.h b/clox/src/compiler.h index 49a45b0..c4376a5 100644 --- a/clox/src/compiler.h +++ b/clox/src/compiler.h @@ -1,6 +1,8 @@ #ifndef COMPILER_H #define COMPILER_H -void compile(const char *source); +#include "vm.h" + +bool compile(const char *source, Chunk *chunk); #endif diff --git a/clox/src/vm.c b/clox/src/vm.c index d9e5248..eb79661 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -81,6 +81,19 @@ static InterpretResult run() { } InterpretResult interpret(const char *source) { - compile(source); - return INTERPRET_OK; + Chunk chunk; + initChunk(&chunk); + + if (!compile(source, &chunk)) { + freeChunk(&chunk); + return INTERPRET_COMPILE_ERROR; + } + + vm.chunk = &chunk; + vm.ip = vm.chunk->code; + + InterpretResult result = run(); + + freeChunk(&chunk); + return result; }