diff --git a/clox/Makefile b/clox/Makefile index ce89574..073b0e4 100644 --- a/clox/Makefile +++ b/clox/Makefile @@ -1,5 +1,7 @@ CC = gcc -CFLAGS = -Wall -Wextra -g +INC_DIR = ./ +CFLAGS = -Wall -Wextra -g -I$(INC_DIR) +DEPS = common.h SOURCES = main.c chunk.c memory.c debug.c value.c vm.c compiler.c scanner.c object.c table.c OBJECTS = $(SOURCES:.c=.o) EXECUTABLE = clox @@ -9,7 +11,7 @@ all: $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(CFLAGS) -o $@ $(OBJECTS) -%.o: %.c +%.o: %.c $(DEPS) $(CC) $(CFLAGS) -c $< -o $@ clean: diff --git a/clox/chunk.h b/clox/chunk.h index 3770c32..5a6f6ce 100644 --- a/clox/chunk.h +++ b/clox/chunk.h @@ -10,6 +10,8 @@ typedef enum { OP_TRUE, OP_FALSE, OP_POP, + OP_GET_LOCAL, + OP_SET_LOCAL, OP_GET_GLOBAL, OP_SET_GLOBAL, OP_DEFINE_GLOBAL, diff --git a/clox/chunk.o b/clox/chunk.o index ecb5125..1a9ea30 100644 Binary files a/clox/chunk.o and b/clox/chunk.o differ diff --git a/clox/clox b/clox/clox index 021e15e..7730a19 100755 Binary files a/clox/clox and b/clox/clox differ diff --git a/clox/common.h b/clox/common.h index 7197be4..cc6808c 100644 --- a/clox/common.h +++ b/clox/common.h @@ -6,5 +6,6 @@ #include #define DEBUG_TRACE_EXECUTION +#define UINT8_COUNT (UINT8_MAX + 1) #define DEBUG_PRINT_CODE #endif diff --git a/clox/compiler.c b/clox/compiler.c index 7b7da5a..5c94f2b 100644 --- a/clox/compiler.c +++ b/clox/compiler.c @@ -1,5 +1,6 @@ #include #include +#include #include "common.h" #include "compiler.h" @@ -38,7 +39,19 @@ typedef struct { Precedence precedence; } ParseRule; +typedef struct { + Token name; + int depth; +} Local; + +typedef struct { + Local locals[UINT8_COUNT]; + int localCount; + int scopeDepth; +} Compiler; + Parser parser; +Compiler* current = NULL; Chunk* compilingChunk; static Chunk* currentChunk() { @@ -127,6 +140,12 @@ static void emitConstant(Value value) { emitBytes(OP_CONSTANT, makeConstant(value)); } +static void initCompiler(Compiler* compiler) { + compiler->localCount = 0; + compiler->scopeDepth = 0; + current = compiler; +} + static void endCompiler() { emitReturn(); #ifdef DEBUG_PRINT_CODE @@ -135,6 +154,20 @@ static void endCompiler() { } #endif } + +static void beginScope() { + current->scopeDepth++; +} + +static void endScope() { + current->scopeDepth--; + while (current->localCount > 0 && + current->locals[current->localCount - 1].depth > + current->scopeDepth) { + emitByte(OP_POP); + current->localCount--; + } +} static void expression(); static void statement(); static void declaration(); @@ -144,13 +177,69 @@ static void parsePrecedence(Precedence precedence); static uint8_t identifierConstant(Token* name) { return makeConstant(OBJ_VAL(copyString(name->start,name->length))); } +static bool identifiersEqual(Token* a, Token* b) { + if (a->length != b->length) return false; + return memcmp(a->start, b->start, a->length) == 0; +} +static int resolveLocal(Compiler* compiler, Token* name) { + for (int i = compiler->localCount - 1; i >= 0; i--) { + Local* local = &compiler->locals[i]; + if (identifiersEqual(name, &local->name)) { + if (local->depth == -1) { + error("Can't read local variable in its own initializer."); + } + return i; + } + } + + return -1; +} +static void addLocal(Token name) { + if (current->localCount == UINT8_COUNT) { + error("Too many local variables in function."); + return; + } + Local* local = ¤t->locals[current->localCount++]; + local->name = name; + local->depth = -1; + // local->depth = current->scopeDepth; +} + +static void declareVariable() { + if (current->scopeDepth == 0) return; + + Token* name = &parser.previous; + for (int i = current->localCount - 1; i >= 0; i--) { + Local* local = ¤t->locals[i]; + if (local->depth != -1 && local->depth < current->scopeDepth) { + break; + } + + if (identifiersEqual(name, &local->name)) { + error("Already a variable with this name in this scope."); + } + } + addLocal(*name); +} static uint8_t parseVariable(const char* errorMessage) { consume(TOKEN_IDENTIFIER, errorMessage); + + declareVariable(); + if (current->scopeDepth > 0) return 0; + return identifierConstant(&parser.previous); } +static void markInitialized() { + current->locals[current->localCount - 1].depth = current->scopeDepth; +} + static void defineVariable(uint8_t global) { + if (current->scopeDepth > 0) { + markInitialized(); + return; + } emitBytes(OP_DEFINE_GLOBAL, global); } @@ -188,6 +277,13 @@ static void expression() { // What goes here? parsePrecedence(PREC_ASSIGNMENT); } +static void block() { + while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) { + declaration(); + } + + consume(TOKEN_RIGHT_BRACE, "Expect '}' after block."); +} static void varDeclaration() { uint8_t global = parseVariable("Expect variable name."); @@ -253,6 +349,11 @@ static void statement() { if (match(TOKEN_PRINT)) { printStatement(); } + else if (match(TOKEN_LEFT_BRACE)) { + beginScope(); + block(); + endScope(); + } else { expressionStatement(); } @@ -274,13 +375,23 @@ static void string(bool canAssign) { } static void namedVariable(Token name, bool canAssign) { - uint8_t arg = identifierConstant(&name); + // uint8_t arg = identifierConstant(&name); + uint8_t getOp, setOp; + int arg = resolveLocal(current, &name); + if (arg != -1) { + getOp = OP_GET_LOCAL; + setOp = OP_SET_LOCAL; + } else { + arg = identifierConstant(&name); + getOp = OP_GET_GLOBAL; + setOp = OP_SET_GLOBAL; + } if (canAssign && match(TOKEN_EQUAL)) { expression(); - emitBytes(OP_SET_GLOBAL, arg); + emitBytes(setOp, (uint8_t)arg); } else { - emitBytes(OP_GET_GLOBAL, arg); + emitBytes(getOp, (uint8_t)arg); } } @@ -373,6 +484,8 @@ static ParseRule* getRule(TokenType type) { bool compile(const char* source, Chunk* chunk) { initScanner(source); + Compiler compiler; + initCompiler(&compiler); compilingChunk = chunk; parser.hadError = false; parser.panicMode = false; @@ -380,8 +493,6 @@ bool compile(const char* source, Chunk* chunk) { while (!match(TOKEN_EOF)) { declaration(); } - // expression(); - // consume(TOKEN_EOF, "Expect end of expression."); endCompiler(); return !parser.hadError; } diff --git a/clox/compiler.o b/clox/compiler.o index ed1032b..1827e1e 100644 Binary files a/clox/compiler.o and b/clox/compiler.o differ diff --git a/clox/debug.c b/clox/debug.c index febedc2..b144bfc 100644 --- a/clox/debug.c +++ b/clox/debug.c @@ -22,6 +22,13 @@ static int simpleInstruction(const char* name, int offset) { printf("%s\n", name); return offset + 1; } +static int byteInstruction(const char* name, Chunk* chunk, + int offset) { + uint8_t slot = chunk->code[offset + 1]; + printf("%-16s %4d\n", name, slot); + return offset + 2; +} + int disassembleInstruction(Chunk* chunk, int offset) { printf("%04d ", offset); @@ -44,6 +51,10 @@ int disassembleInstruction(Chunk* chunk, int offset) { return simpleInstruction("OP_FALSE", offset); case OP_POP: return simpleInstruction("OP_POP", offset); + case OP_GET_LOCAL: + return byteInstruction("OP_GET_LOCAL", chunk, offset); + case OP_SET_LOCAL: + return byteInstruction("OP_SET_LOCAL", chunk, offset); case OP_GET_GLOBAL: return constantInstruction("OP_GET_GLOBAL", chunk, offset); case OP_DEFINE_GLOBAL: diff --git a/clox/debug.o b/clox/debug.o index 5d938b5..4ce44ec 100644 Binary files a/clox/debug.o and b/clox/debug.o differ diff --git a/clox/main.o b/clox/main.o index b48a90c..f017a82 100644 Binary files a/clox/main.o and b/clox/main.o differ diff --git a/clox/scanner.o b/clox/scanner.o index f4b971f..27bcf6c 100644 Binary files a/clox/scanner.o and b/clox/scanner.o differ diff --git a/clox/tests/chapter22/case2.lox b/clox/tests/chapter22/case2.lox new file mode 100644 index 0000000..7d7d9f9 --- /dev/null +++ b/clox/tests/chapter22/case2.lox @@ -0,0 +1,4 @@ +{ + var a = "first"; + var a = "second"; +} \ No newline at end of file diff --git a/clox/tests/chapter22/case3.lox b/clox/tests/chapter22/case3.lox new file mode 100644 index 0000000..e3a9042 --- /dev/null +++ b/clox/tests/chapter22/case3.lox @@ -0,0 +1,6 @@ +{ + var a = "outer"; + { + var a = "inner"; + } +} \ No newline at end of file diff --git a/clox/tests/chapter22/case4.lox b/clox/tests/chapter22/case4.lox new file mode 100644 index 0000000..d73869c --- /dev/null +++ b/clox/tests/chapter22/case4.lox @@ -0,0 +1,6 @@ +{ + var a = "outer"; + { + var a = a; + } +} \ No newline at end of file diff --git a/clox/value.o b/clox/value.o index 1d7ac1d..9ccacf3 100644 Binary files a/clox/value.o and b/clox/value.o differ diff --git a/clox/vm.c b/clox/vm.c index d96afaf..45d1c08 100644 --- a/clox/vm.c +++ b/clox/vm.c @@ -145,6 +145,16 @@ static InterpretResult run() { push(BOOL_VAL(isFalsey(pop()))); break; case OP_POP: pop(); break; + case OP_GET_LOCAL: { + uint8_t slot = READ_BYTE(); + push(vm.stack[slot]); + break; + } + case OP_SET_LOCAL: { + uint8_t slot = READ_BYTE(); + vm.stack[slot] = peek(0); + break; + } case OP_GET_GLOBAL: { ObjString* name = READ_STRING(); Value value; diff --git a/clox/vm.o b/clox/vm.o index 7d22bfe..08ee2e5 100644 Binary files a/clox/vm.o and b/clox/vm.o differ