diff --git a/clox/src/chunk.h b/clox/src/chunk.h index 399a514..4194e8c 100644 --- a/clox/src/chunk.h +++ b/clox/src/chunk.h @@ -9,6 +9,8 @@ typedef enum { OP_NIL, OP_TRUE, OP_FALSE, + OP_POP, + OP_DEFINE_GLOBAL, OP_EQUAL, OP_GREATER, OP_LESS, @@ -18,6 +20,7 @@ typedef enum { OP_DIVIDE, OP_NOT, OP_NEGATE, + OP_PRINT, OP_RETURN, } OpCode; diff --git a/clox/src/compiler.c b/clox/src/compiler.c index 8606557..9dd7927 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -88,6 +88,15 @@ static void consume(TokenType type, const char *message) { 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); } @@ -123,8 +132,12 @@ static void endCompiler() { } static void expression(); +static void statement(); +static void declaration(); static ParseRule *getRule(TokenType type); static void parsePrecedence(Precedence precedence); +static uint8_t parseVariable(const char *name); +static void defineVariable(uint8_t global); static void binary() { TokenType operatorType = parser.previous.type; @@ -185,6 +198,74 @@ static void literal() { static void expression() { parsePrecedence(PREC_ASSIGNMENT); } +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 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(); +} + +static void declaration() { + if (match(TOKEN_VAR)) { + varDeclaration(); + } else { + statement(); + } + + if (parser.panicMode) + synchronize(); +} + +static void statement() { + if (match(TOKEN_PRINT)) { + printStatement(); + } else { + expressionStatement(); + } +} + static void number() { double value = strtod(parser.previous.start, NULL); emitConstant(NUMBER_VAL(value)); @@ -279,6 +360,19 @@ static void parsePrecedence(Precedence precedence) { } } +static uint8_t identifierConstant(Token *name) { + return makeConstant(OBJ_VAL(copyString(name->start, name->length))); +} + +static uint8_t parseVariable(const char *errorMessage) { + consume(TOKEN_IDENTIFIER, errorMessage); + return identifierConstant(&parser.previous); +} + +static void defineVariable(uint8_t global) { + emitBytes(OP_DEFINE_GLOBAL, global); +} + static ParseRule *getRule(TokenType type) { return &rules[type]; } bool compile(const char *source, Chunk *chunk) { @@ -289,8 +383,11 @@ bool compile(const char *source, Chunk *chunk) { parser.panicMode = false; advance(); - expression(); - consume(TOKEN_EOF, "Expect end of expression."); + + while (!match(TOKEN_EOF)) { + declaration(); + } + endCompiler(); return !parser.hadError; } diff --git a/clox/src/debug.c b/clox/src/debug.c index 788e759..5663091 100644 --- a/clox/src/debug.c +++ b/clox/src/debug.c @@ -42,6 +42,10 @@ int disassembleInstruction(Chunk *chunk, int offset) { return simpleInstruction("OP_TRUE", offset); case OP_FALSE: return simpleInstruction("OP_FALSE", offset); + case OP_POP: + return simpleInstruction("OP_POP", offset); + case OP_DEFINE_GLOBAL: + return constantInstruction("OP_DEFINE_GLOBAL", chunk, offset); case OP_EQUAL: return simpleInstruction("OP_EQUAL", offset); case OP_GREATER: @@ -60,6 +64,8 @@ int disassembleInstruction(Chunk *chunk, int offset) { return simpleInstruction("OP_NOT", offset); case OP_NEGATE: return simpleInstruction("OP_NEGATE", offset); + case OP_PRINT: + return simpleInstruction("OP_PRINT", offset); case OP_RETURN: return simpleInstruction("OP_RETURN", offset); default: diff --git a/clox/src/main.c b/clox/src/main.c index c95c7ba..f985584 100644 --- a/clox/src/main.c +++ b/clox/src/main.c @@ -66,7 +66,7 @@ int main(int argc, const char *argv[]) { if (argc == 1) { repl(); - } else if (argc = 2) { + } else if (argc == 2) { runFile(argv[1]); } else { fprintf(stderr, "Usage: clox [path]\n"); diff --git a/clox/src/vm.c b/clox/src/vm.c index e35a1e0..709f3d1 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -29,10 +29,13 @@ static void runtimeError(const char *format, ...) { void initVM() { resetStack(); vm.objects = NULL; + + initTable(&vm.globals); initTable(&vm.strings); } void freeVM() { + freeTable(&vm.globals); freeTable(&vm.strings); freeObjects(); } @@ -70,6 +73,7 @@ static void concatenate() { static InterpretResult run() { #define READ_BYTE() (*vm.ip++) #define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()]) +#define READ_STRING() AS_STRING(READ_CONSTANT()) #define BINARY_OP(valueType, op) \ do { \ if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \ @@ -108,6 +112,15 @@ static InterpretResult run() { case OP_FALSE: push(BOOL_VAL(false)); break; + case OP_POP: + pop(); + break; + case OP_DEFINE_GLOBAL: { + ObjString *name = READ_STRING(); + tableSet(&vm.globals, name, peek(0)); + pop(); + break; + } case OP_EQUAL: { Value b = pop(); Value a = pop(); @@ -152,7 +165,15 @@ static InterpretResult run() { } push(NUMBER_VAL(-AS_NUMBER(pop()))); break; + case OP_PRINT: { + printValue(pop()); + printf("\n"); + break; + } case OP_RETURN: { + /* The book said to remove this, but when I do I get an infinite loop and + then a segfault because it keeps trying to add the constant 1 to the + stack. */ printValue(pop()); printf("\n"); return INTERPRET_OK; @@ -162,6 +183,7 @@ static InterpretResult run() { #undef READ_BYTE #undef READ_CONSTANT +#undef READ_STRING #undef BINARY_OP } diff --git a/clox/src/vm.h b/clox/src/vm.h index f11c1dd..0ab269b 100644 --- a/clox/src/vm.h +++ b/clox/src/vm.h @@ -12,6 +12,7 @@ typedef struct { uint8_t *ip; Value stack[STACK_MAX]; Value *stackTop; + Table globals; Table strings; Obj *objects; } VM;