diff --git a/clox/src/chunk.h b/clox/src/chunk.h index dda2b31..2807d30 100644 --- a/clox/src/chunk.h +++ b/clox/src/chunk.h @@ -12,6 +12,7 @@ typedef enum { OP_POP, OP_GET_GLOBAL, OP_DEFINE_GLOBAL, + OP_SET_GLOBAL, OP_EQUAL, OP_GREATER, OP_LESS, diff --git a/clox/src/compiler.c b/clox/src/compiler.c index f125c10..0fc41b8 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -30,7 +30,7 @@ typedef enum { PREC_PRIMARY } Precedence; -typedef void (*ParseFn)(); +typedef void (*ParseFn)(bool canAssign); typedef struct { ParseFn prefix; @@ -140,7 +140,7 @@ static uint8_t parseVariable(const char *name); static void defineVariable(uint8_t global); static uint8_t identifierConstant(Token *name); -static void binary() { +static void binary(bool canAssign) { TokenType operatorType = parser.previous.type; ParseRule *rule = getRule(operatorType); parsePrecedence((Precedence)(rule->precedence + 1)); @@ -181,7 +181,7 @@ static void binary() { } } -static void literal() { +static void literal(bool canAssign) { switch (parser.previous.type) { case TOKEN_FALSE: emitByte(OP_FALSE); @@ -267,26 +267,32 @@ static void statement() { } } -static void number() { +static void number(bool canAssign) { double value = strtod(parser.previous.start, NULL); emitConstant(NUMBER_VAL(value)); } -static void string() { +static void string(bool canAssign) { emitConstant(OBJ_VAL( copyString(parser.previous.start + 1, parser.previous.length - 2))); } -static void namedVariable(Token name) { +static void namedVariable(Token name, bool canAssign) { uint8_t arg = identifierConstant(&name); - emitBytes(OP_GET_GLOBAL, arg); + + if (canAssign && match(TOKEN_EQUAL)) { + expression(); + emitBytes(OP_SET_GLOBAL, arg); + } else { + emitBytes(OP_GET_GLOBAL, arg); + } } -static void variable() { - namedVariable(parser.previous); +static void variable(bool canAssign) { + namedVariable(parser.previous, canAssign); } -static void unary() { +static void unary(bool canAssign) { TokenType operatorType = parser.previous.type; // Compile the operand. @@ -305,7 +311,7 @@ static void unary() { } } -static void grouping() { +static void grouping(bool canAssign) { expression(); consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression."); } @@ -361,12 +367,17 @@ static void parsePrecedence(Precedence precedence) { return; } - prefixRule(); + bool canAssign = precedence <= PREC_ASSIGNMENT; + prefixRule(canAssign); while (precedence <= getRule(parser.current.type)->precedence) { advance(); ParseFn infixRule = getRule(parser.previous.type)->infix; - infixRule(); + infixRule(canAssign); + } + + if (canAssign && match(TOKEN_EQUAL)) { + error("Invalid assignment target."); } } diff --git a/clox/src/debug.c b/clox/src/debug.c index 669fc78..61deb52 100644 --- a/clox/src/debug.c +++ b/clox/src/debug.c @@ -48,6 +48,8 @@ int disassembleInstruction(Chunk *chunk, int offset) { return constantInstruction("OP_GET_GLOBAL", chunk, offset); case OP_DEFINE_GLOBAL: return constantInstruction("OP_DEFINE_GLOBAL", chunk, offset); + case OP_SET_GLOBAL: + return constantInstruction("OP_SET_GLOBAL", chunk, offset); case OP_EQUAL: return simpleInstruction("OP_EQUAL", offset); case OP_GREATER: diff --git a/clox/src/vm.c b/clox/src/vm.c index 5e35abd..80e11c3 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -119,8 +119,8 @@ static InterpretResult run() { ObjString *name = READ_STRING(); Value value; if (!tableGet(&vm.globals, name, &value)) { - runtimeError("Undefined variable '%s'.", name->chars); - return INTERPRET_RUNTIME_ERROR; + runtimeError("Undefined variable '%s'.", name->chars); + return INTERPRET_RUNTIME_ERROR; } push(value); break; @@ -131,6 +131,15 @@ static InterpretResult run() { pop(); break; } + case OP_SET_GLOBAL: { + ObjString *name = READ_STRING(); + if (tableSet(&vm.globals, name, peek(0))) { + tableDelete(&vm.globals, name); + runtimeError("Undefined variable '%s'.", name->chars); + return INTERPRET_RUNTIME_ERROR; + } + break; + } case OP_EQUAL: { Value b = pop(); Value a = pop();