From ba6ab6759e4257e543ca5da4caf2705711e5b3ed Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Wed, 12 Jan 2022 22:58:10 -0800 Subject: [PATCH] Chapter 23.2 --- clox/src/chunk.h | 2 ++ clox/src/compiler.c | 64 +++++++++++++++++++++++++++++++++++++++++++-- clox/src/debug.c | 12 +++++++++ clox/src/vm.c | 13 +++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/clox/src/chunk.h b/clox/src/chunk.h index 7777ad6..db524cf 100644 --- a/clox/src/chunk.h +++ b/clox/src/chunk.h @@ -25,6 +25,8 @@ typedef enum { OP_NOT, OP_NEGATE, OP_PRINT, + OP_JUMP, + OP_JUMP_IF_FALSE, OP_RETURN, } OpCode; diff --git a/clox/src/compiler.c b/clox/src/compiler.c index a964b79..d965f45 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -119,6 +119,13 @@ static void emitBytes(uint8_t byte1, uint8_t byte2) { emitByte(byte2); } +static int emitJump(uint8_t instruction) { + emitByte(instruction); + emitByte(0xff); + emitByte(0xff); + return currentChunk()->count - 2; +} + static void emitReturn() { emitByte(OP_RETURN); } static uint8_t makeConstant(Value value) { @@ -135,6 +142,18 @@ static void emitConstant(Value value) { emitBytes(OP_CONSTANT, makeConstant(value)); } +static void patchJump(int offset) { + // -2 to adjust for the bytecode for the jump offset itself... + int jump = currentChunk()->count - offset - 2; + + if (jump > UINT16_MAX) { + error("Too much code to jump over."); + } + + currentChunk()->code[offset] = (jump >> 8) & 0xff; + currentChunk()->code[offset + 1] = jump & 0xff; +} + static void initCompiler(Compiler *compiler) { compiler->localCount = 0; compiler->scopeDepth = 0; @@ -258,6 +277,25 @@ static void expressionStatement() { emitByte(OP_POP); } +static void ifStatement() { + consume(TOKEN_LEFT_PAREN, "Expect '(' after 'if'."); + expression(); + consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition."); + + int thenJump = emitJump(OP_JUMP_IF_FALSE); + emitByte(OP_POP); + statement(); + + int elseJump = emitJump(OP_JUMP); + + patchJump(thenJump); + emitByte(OP_POP); + + if (match(TOKEN_ELSE)) + statement(); + patchJump(elseJump); +} + static void printStatement() { expression(); consume(TOKEN_SEMICOLON, "Expect ';' after value."); @@ -302,6 +340,8 @@ static void declaration() { static void statement() { if (match(TOKEN_PRINT)) { printStatement(); + } else if (match(TOKEN_IF)) { + ifStatement(); } else if (match(TOKEN_LEFT_BRACE)) { beginScope(); block(); @@ -316,6 +356,26 @@ static void number(bool canAssign) { emitConstant(NUMBER_VAL(value)); } +static void and_(bool canAssign) { + int endJump = emitJump(OP_JUMP_IF_FALSE); + + emitByte(OP_POP); + parsePrecedence(PREC_AND); + + patchJump(endJump); +} + +static void or_(bool canAssign) { + int elseJump = emitJump(OP_JUMP_IF_FALSE); + int endJump = emitJump(OP_JUMP); + + patchJump(elseJump); + emitByte(OP_POP); + + parsePrecedence(PREC_OR); + patchJump(endJump); +} + static void string(bool canAssign) { emitConstant(OBJ_VAL( copyString(parser.previous.start + 1, parser.previous.length - 2))); @@ -393,7 +453,7 @@ ParseRule rules[] = { [TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE}, [TOKEN_STRING] = {string, NULL, PREC_NONE}, [TOKEN_NUMBER] = {number, NULL, PREC_NONE}, - [TOKEN_AND] = {NULL, NULL, PREC_NONE}, + [TOKEN_AND] = {NULL, and_, PREC_AND}, [TOKEN_CLASS] = {NULL, NULL, PREC_NONE}, [TOKEN_ELSE] = {NULL, NULL, PREC_NONE}, [TOKEN_FALSE] = {literal, NULL, PREC_NONE}, @@ -401,7 +461,7 @@ ParseRule rules[] = { [TOKEN_FUN] = {NULL, NULL, PREC_NONE}, [TOKEN_IF] = {NULL, NULL, PREC_NONE}, [TOKEN_NIL] = {literal, NULL, PREC_NONE}, - [TOKEN_OR] = {NULL, NULL, PREC_NONE}, + [TOKEN_OR] = {NULL, or_, PREC_OR}, [TOKEN_PRINT] = {NULL, NULL, PREC_NONE}, [TOKEN_RETURN] = {NULL, NULL, PREC_NONE}, [TOKEN_SUPER] = {NULL, NULL, PREC_NONE}, diff --git a/clox/src/debug.c b/clox/src/debug.c index 5a3cedc..b3f756a 100644 --- a/clox/src/debug.c +++ b/clox/src/debug.c @@ -30,6 +30,14 @@ static int byteInstruction(const char *name, Chunk *chunk, int offset) { return offset + 2; } +static int jumpInstruction(const char *name, int sign, Chunk *chunk, + int offset) { + uint16_t jump = (uint16_t)(chunk->code[offset + 1] << 8); + jump |= chunk->code[offset + 2]; + printf("%-16s %4d -> %d\n", name, offset, offset + 3 + sign * jump); + return offset + 3; +} + int disassembleInstruction(Chunk *chunk, int offset) { printf("%04d ", offset); if (offset > 0 && chunk->lines[offset] == chunk->lines[offset - 1]) { @@ -80,6 +88,10 @@ int disassembleInstruction(Chunk *chunk, int offset) { return simpleInstruction("OP_NEGATE", offset); case OP_PRINT: return simpleInstruction("OP_PRINT", offset); + case OP_JUMP: + return jumpInstruction("OP_JUMP", 1, chunk, offset); + case OP_JUMP_IF_FALSE: + return jumpInstruction("OP_JUMP_IF_FALSE", 1, chunk, offset); case OP_RETURN: return simpleInstruction("OP_RETURN", offset); default: diff --git a/clox/src/vm.c b/clox/src/vm.c index d869527..49a162d 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -73,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_SHORT() (vm.ip += 2, (uint16_t)((vm.ip[-2] << 8) | vm.ip[-1])) #define READ_STRING() AS_STRING(READ_CONSTANT()) #define BINARY_OP(valueType, op) \ do { \ @@ -199,6 +200,17 @@ static InterpretResult run() { printf("\n"); break; } + case OP_JUMP: { + uint16_t offset = READ_SHORT(); + vm.ip += offset; + break; + } + case OP_JUMP_IF_FALSE: { + uint16_t offset = READ_SHORT(); + if (isFalsey(peek(0))) + vm.ip += offset; + 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 @@ -211,6 +223,7 @@ static InterpretResult run() { } #undef READ_BYTE +#undef READ_SHORT #undef READ_CONSTANT #undef READ_STRING #undef BINARY_OP