From c10cbcdf99824bf81fb0874deb258d5c8c7cb22d Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Mon, 6 Sep 2021 02:03:31 -0700 Subject: [PATCH] Chapter 18 --- clox/src/chunk.h | 7 ++++++ clox/src/compiler.c | 57 +++++++++++++++++++++++++++++++++++++-------- clox/src/debug.c | 14 +++++++++++ clox/src/value.c | 29 ++++++++++++++++++++++- clox/src/value.h | 1 + clox/src/vm.c | 28 ++++++++++++++++++++++ 6 files changed, 125 insertions(+), 11 deletions(-) diff --git a/clox/src/chunk.h b/clox/src/chunk.h index 185f13c..399a514 100644 --- a/clox/src/chunk.h +++ b/clox/src/chunk.h @@ -6,10 +6,17 @@ typedef enum { OP_CONSTANT, + OP_NIL, + OP_TRUE, + OP_FALSE, + OP_EQUAL, + OP_GREATER, + OP_LESS, OP_ADD, OP_SUBTRACT, OP_MULTIPLY, OP_DIVIDE, + OP_NOT, OP_NEGATE, OP_RETURN, } OpCode; diff --git a/clox/src/compiler.c b/clox/src/compiler.c index 81e1616..ab8fe7d 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -132,6 +132,24 @@ static void binary() { parsePrecedence((Precedence)(rule->precedence + 1)); switch (operatorType) { + case TOKEN_BANG_EQUAL: + emitBytes(OP_EQUAL, OP_NOT); + break; + case TOKEN_EQUAL_EQUAL: + emitByte(OP_EQUAL); + break; + case TOKEN_GREATER: + emitByte(OP_GREATER); + break; + case TOKEN_GREATER_EQUAL: + emitBytes(OP_LESS, OP_NOT); + break; + case TOKEN_LESS: + emitByte(OP_LESS); + break; + case TOKEN_LESS_EQUAL: + emitBytes(OP_GREATER, OP_NOT); + break; case TOKEN_PLUS: emitByte(OP_ADD); break; @@ -149,6 +167,22 @@ static void binary() { } } +static void literal() { + switch (parser.previous.type) { + case TOKEN_FALSE: + emitByte(OP_FALSE); + break; + case TOKEN_NIL: + emitByte(OP_NIL); + break; + case TOKEN_TRUE: + emitByte(OP_TRUE); + break; + default: + return; /* Unreachable */ + } +} + static void expression() { parsePrecedence(PREC_ASSIGNMENT); } static void number() { @@ -164,6 +198,9 @@ static void unary() { // Emit the operator instruction. switch (operatorType) { + case TOKEN_BANG: + emitByte(OP_NOT); + break; case TOKEN_MINUS: emitByte(OP_NEGATE); break; @@ -189,31 +226,31 @@ ParseRule rules[] = { [TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE}, [TOKEN_SLASH] = {NULL, binary, PREC_FACTOR}, [TOKEN_STAR] = {NULL, binary, PREC_FACTOR}, - [TOKEN_BANG] = {NULL, NULL, PREC_NONE}, - [TOKEN_BANG_EQUAL] = {NULL, NULL, PREC_NONE}, + [TOKEN_BANG] = {unary, NULL, PREC_NONE}, + [TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY}, [TOKEN_EQUAL] = {NULL, NULL, PREC_NONE}, - [TOKEN_EQUAL_EQUAL] = {NULL, NULL, PREC_NONE}, - [TOKEN_GREATER] = {NULL, NULL, PREC_NONE}, - [TOKEN_GREATER_EQUAL] = {NULL, NULL, PREC_NONE}, - [TOKEN_LESS] = {NULL, NULL, PREC_NONE}, - [TOKEN_LESS_EQUAL] = {NULL, NULL, PREC_NONE}, + [TOKEN_EQUAL_EQUAL] = {NULL, binary, PREC_EQUALITY}, + [TOKEN_GREATER] = {NULL, binary, PREC_COMPARISON}, + [TOKEN_GREATER_EQUAL] = {NULL, binary, PREC_COMPARISON}, + [TOKEN_LESS] = {NULL, binary, PREC_COMPARISON}, + [TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON}, [TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE}, [TOKEN_STRING] = {NULL, NULL, PREC_NONE}, [TOKEN_NUMBER] = {number, NULL, PREC_NONE}, [TOKEN_AND] = {NULL, NULL, PREC_NONE}, [TOKEN_CLASS] = {NULL, NULL, PREC_NONE}, [TOKEN_ELSE] = {NULL, NULL, PREC_NONE}, - [TOKEN_FALSE] = {NULL, NULL, PREC_NONE}, + [TOKEN_FALSE] = {literal, NULL, PREC_NONE}, [TOKEN_FOR] = {NULL, NULL, PREC_NONE}, [TOKEN_FUN] = {NULL, NULL, PREC_NONE}, [TOKEN_IF] = {NULL, NULL, PREC_NONE}, - [TOKEN_NIL] = {NULL, NULL, PREC_NONE}, + [TOKEN_NIL] = {literal, NULL, PREC_NONE}, [TOKEN_OR] = {NULL, NULL, PREC_NONE}, [TOKEN_PRINT] = {NULL, NULL, PREC_NONE}, [TOKEN_RETURN] = {NULL, NULL, PREC_NONE}, [TOKEN_SUPER] = {NULL, NULL, PREC_NONE}, [TOKEN_THIS] = {NULL, NULL, PREC_NONE}, - [TOKEN_TRUE] = {NULL, NULL, PREC_NONE}, + [TOKEN_TRUE] = {literal, NULL, PREC_NONE}, [TOKEN_VAR] = {NULL, NULL, PREC_NONE}, [TOKEN_WHILE] = {NULL, NULL, PREC_NONE}, [TOKEN_ERROR] = {NULL, NULL, PREC_NONE}, diff --git a/clox/src/debug.c b/clox/src/debug.c index 570bde9..788e759 100644 --- a/clox/src/debug.c +++ b/clox/src/debug.c @@ -36,6 +36,18 @@ int disassembleInstruction(Chunk *chunk, int offset) { switch (instruction) { case OP_CONSTANT: return constantInstruction("OP_CONSTANT", chunk, offset); + case OP_NIL: + return simpleInstruction("OP_NIL", offset); + case OP_TRUE: + return simpleInstruction("OP_TRUE", offset); + case OP_FALSE: + return simpleInstruction("OP_FALSE", offset); + case OP_EQUAL: + return simpleInstruction("OP_EQUAL", offset); + case OP_GREATER: + return simpleInstruction("OP_GREATER", offset); + case OP_LESS: + return simpleInstruction("OP_LESS", offset); case OP_ADD: return simpleInstruction("OP_ADD", offset); case OP_SUBTRACT: @@ -44,6 +56,8 @@ int disassembleInstruction(Chunk *chunk, int offset) { return simpleInstruction("OP_MULTIPLY", offset); case OP_DIVIDE: return simpleInstruction("OP_DIVIDE", offset); + case OP_NOT: + return simpleInstruction("OP_NOT", offset); case OP_NEGATE: return simpleInstruction("OP_NEGATE", offset); case OP_RETURN: diff --git a/clox/src/value.c b/clox/src/value.c index 0737999..34e55b8 100644 --- a/clox/src/value.c +++ b/clox/src/value.c @@ -26,4 +26,31 @@ void freeValueArray(ValueArray *array) { initValueArray(array); } -void printValue(Value value) { printf("%g", AS_NUMBER(value)); } +void printValue(Value value) { + switch (value.type) { + case VAL_BOOL: + printf(AS_BOOL(value) ? "true" : "false"); + break; + case VAL_NIL: + printf("nil"); + break; + case VAL_NUMBER: + printf("%g", AS_NUMBER(value)); + break; + } +} + +bool valuesEqual(Value a, Value b) { + if (a.type != b.type) + return false; + switch (a.type) { + case VAL_BOOL: + return AS_BOOL(a) == AS_BOOL(b); + case VAL_NIL: + return true; + case VAL_NUMBER: + return AS_NUMBER(a) == AS_NUMBER(b); + default: + return false; /* Unreachable */ + } +} diff --git a/clox/src/value.h b/clox/src/value.h index 7478aaf..96df347 100644 --- a/clox/src/value.h +++ b/clox/src/value.h @@ -34,6 +34,7 @@ typedef struct { Value *values; } ValueArray; +bool valuesEqual(Value a, Value b); void initValueArray(ValueArray *array); void writeValueArray(ValueArray *array, Value value); void freeValueArray(ValueArray *array); diff --git a/clox/src/vm.c b/clox/src/vm.c index 381c8c0..df9f490 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -39,6 +39,10 @@ Value pop() { static Value peek(int distance) { return vm.stackTop[-1 - distance]; } +static bool isFalsey(Value value) { + return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value)); +} + static InterpretResult run() { #define READ_BYTE() (*vm.ip++) #define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()]) @@ -71,6 +75,27 @@ static InterpretResult run() { push(constant); break; } + case OP_NIL: + push(NIL_VAL); + break; + case OP_TRUE: + push(BOOL_VAL(true)); + break; + case OP_FALSE: + push(BOOL_VAL(false)); + break; + case OP_EQUAL: { + Value b = pop(); + Value a = pop(); + push(BOOL_VAL(valuesEqual(a, b))); + break; + } + case OP_GREATER: + BINARY_OP(BOOL_VAL, >); + break; + case OP_LESS: + BINARY_OP(BOOL_VAL, <); + break; case OP_ADD: BINARY_OP(NUMBER_VAL, +); break; @@ -83,6 +108,9 @@ static InterpretResult run() { case OP_DIVIDE: BINARY_OP(NUMBER_VAL, /); break; + case OP_NOT: + push(BOOL_VAL(isFalsey(pop()))); + break; case OP_NEGATE: if (!IS_NUMBER(peek(0))) { runtimeError("Operand must be a number.");