diff --git a/clox/src/chunk.h b/clox/src/chunk.h index ae0ab72..9fff535 100644 --- a/clox/src/chunk.h +++ b/clox/src/chunk.h @@ -17,6 +17,8 @@ typedef enum { OP_SET_GLOBAL, OP_GET_UPVALUE, OP_SET_UPVALUE, + OP_GET_PROPERTY, + OP_SET_PROPERTY, OP_EQUAL, OP_GREATER, OP_LESS, diff --git a/clox/src/compiler.c b/clox/src/compiler.c index b21a53d..6ab4855 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -307,6 +307,18 @@ static void call(bool canAssign) { emitBytes(OP_CALL, argCount); } +static void dot(bool canAssign) { + consume(TOKEN_IDENTIFIER, "Expect property name after '.'."); + uint8_t name = identifierConstant(&parser.previous); + + if (canAssign && match(TOKEN_EQUAL)) { + expression(); + emitBytes(OP_SET_PROPERTY, name); + } else { + emitBytes(OP_GET_PROPERTY, name); + } +} + static void literal(bool canAssign) { switch (parser.previous.type) { case TOKEN_FALSE: @@ -689,7 +701,7 @@ ParseRule rules[] = { [TOKEN_LEFT_BRACE] = {NULL, NULL, PREC_NONE}, [TOKEN_RIGHT_BRACE] = {NULL, NULL, PREC_NONE}, [TOKEN_COMMA] = {NULL, NULL, PREC_NONE}, - [TOKEN_DOT] = {NULL, NULL, PREC_NONE}, + [TOKEN_DOT] = {NULL, dot, PREC_CALL}, [TOKEN_MINUS] = {unary, binary, PREC_TERM}, [TOKEN_PLUS] = {NULL, binary, PREC_TERM}, [TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE}, diff --git a/clox/src/debug.c b/clox/src/debug.c index a2c2c53..fe96c3d 100644 --- a/clox/src/debug.c +++ b/clox/src/debug.c @@ -73,6 +73,10 @@ int disassembleInstruction(Chunk *chunk, int offset) { return byteInstruction("OP_GET_UPVALUE", chunk, offset); case OP_SET_UPVALUE: return byteInstruction("OP_SET_UPVALUE", chunk, offset); + case OP_GET_PROPERTY: + return constantInstruction("OP_GET_PROPERTY", chunk, offset); + case OP_SET_PROPERTY: + return constantInstruction("OP_SET_PROPERTY", 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 c1f0b90..1d82dd1 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -282,6 +282,38 @@ static InterpretResult run() { *frame->closure->upvalues[slot]->location = peek(0); break; } + case OP_GET_PROPERTY: { + if (!IS_INSTANCE(peek(0))) { + runtimeError("Only instances have properties."); + return INTERPRET_RUNTIME_ERROR; + } + + ObjInstance *instance = AS_INSTANCE(peek(0)); + ObjString *name = READ_STRING(); + + Value value; + if (tableGet(&instance->fields, name, &value)) { + pop(); /* Instance */ + push(value); + break; + } + + runtimeError("Undefined property '%s'.", name->chars); + return INTERPRET_RUNTIME_ERROR; + } + case OP_SET_PROPERTY: { + if (!IS_INSTANCE(peek(1))) { + runtimeError("Only instances have fields."); + return INTERPRET_RUNTIME_ERROR; + } + + ObjInstance *instance = AS_INSTANCE(peek(1)); + tableSet(&instance->fields, READ_STRING(), peek(0)); + Value value = pop(); + pop(); + push(value); + break; + } case OP_EQUAL: { Value b = pop(); Value a = pop();