aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Tom Willemse2021-09-20 21:47:41 -0700
committerGravatar Tom Willemse2021-09-20 21:47:41 -0700
commitfcbea5f617edb6e3dfa68eb31efa402b4916c032 (patch)
tree26fb96c9bc9d603b21d80159027b288c79f7ff24
parentaa41f26a26fcff212ecc55750a4806d83a6cf5dc (diff)
downloadcrafting-interpreters-fcbea5f617edb6e3dfa68eb31efa402b4916c032.tar.gz
crafting-interpreters-fcbea5f617edb6e3dfa68eb31efa402b4916c032.zip
Chapter 21.1 - 21.3
-rw-r--r--clox/src/chunk.h3
-rw-r--r--clox/src/compiler.c101
-rw-r--r--clox/src/debug.c6
-rw-r--r--clox/src/main.c2
-rw-r--r--clox/src/vm.c22
-rw-r--r--clox/src/vm.h1
6 files changed, 132 insertions, 3 deletions
diff --git a/clox/src/chunk.h b/clox/src/chunk.h
index 399a514..4194e8c 100644
--- a/clox/src/chunk.h
+++ b/clox/src/chunk.h
@@ -9,6 +9,8 @@ typedef enum {
OP_NIL,
OP_TRUE,
OP_FALSE,
+ OP_POP,
+ OP_DEFINE_GLOBAL,
OP_EQUAL,
OP_GREATER,
OP_LESS,
@@ -18,6 +20,7 @@ typedef enum {
OP_DIVIDE,
OP_NOT,
OP_NEGATE,
+ OP_PRINT,
OP_RETURN,
} OpCode;
diff --git a/clox/src/compiler.c b/clox/src/compiler.c
index 8606557..9dd7927 100644
--- a/clox/src/compiler.c
+++ b/clox/src/compiler.c
@@ -88,6 +88,15 @@ static void consume(TokenType type, const char *message) {
errorAtCurrent(message);
}
+static bool check(TokenType type) { return parser.current.type == type; }
+
+static bool match(TokenType type) {
+ if (!check(type))
+ return false;
+ advance();
+ return true;
+}
+
static void emitByte(uint8_t byte) {
writeChunk(currentChunk(), byte, parser.previous.line);
}
@@ -123,8 +132,12 @@ static void endCompiler() {
}
static void expression();
+static void statement();
+static void declaration();
static ParseRule *getRule(TokenType type);
static void parsePrecedence(Precedence precedence);
+static uint8_t parseVariable(const char *name);
+static void defineVariable(uint8_t global);
static void binary() {
TokenType operatorType = parser.previous.type;
@@ -185,6 +198,74 @@ static void literal() {
static void expression() { parsePrecedence(PREC_ASSIGNMENT); }
+static void varDeclaration() {
+ uint8_t global = parseVariable("Expect variable name.");
+
+ if (match(TOKEN_EQUAL)) {
+ expression();
+ } else {
+ emitByte(OP_NIL);
+ }
+ consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration.");
+
+ defineVariable(global);
+}
+
+static void expressionStatement() {
+ expression();
+ consume(TOKEN_SEMICOLON, "Expect ';' after expression.");
+ emitByte(OP_POP);
+}
+
+static void printStatement() {
+ expression();
+ consume(TOKEN_SEMICOLON, "Expect ';' after value.");
+ emitByte(OP_PRINT);
+}
+
+static void synchronize() {
+ parser.panicMode = false;
+
+ while (parser.current.type != TOKEN_EOF) {
+ if (parser.previous.type == TOKEN_SEMICOLON)
+ return;
+ switch (parser.current.type) {
+ case TOKEN_CLASS:
+ case TOKEN_FUN:
+ case TOKEN_VAR:
+ case TOKEN_FOR:
+ case TOKEN_IF:
+ case TOKEN_WHILE:
+ case TOKEN_PRINT:
+ case TOKEN_RETURN:
+ return;
+
+ default:; /* Do nothing */
+ }
+ }
+
+ advance();
+}
+
+static void declaration() {
+ if (match(TOKEN_VAR)) {
+ varDeclaration();
+ } else {
+ statement();
+ }
+
+ if (parser.panicMode)
+ synchronize();
+}
+
+static void statement() {
+ if (match(TOKEN_PRINT)) {
+ printStatement();
+ } else {
+ expressionStatement();
+ }
+}
+
static void number() {
double value = strtod(parser.previous.start, NULL);
emitConstant(NUMBER_VAL(value));
@@ -279,6 +360,19 @@ static void parsePrecedence(Precedence precedence) {
}
}
+static uint8_t identifierConstant(Token *name) {
+ return makeConstant(OBJ_VAL(copyString(name->start, name->length)));
+}
+
+static uint8_t parseVariable(const char *errorMessage) {
+ consume(TOKEN_IDENTIFIER, errorMessage);
+ return identifierConstant(&parser.previous);
+}
+
+static void defineVariable(uint8_t global) {
+ emitBytes(OP_DEFINE_GLOBAL, global);
+}
+
static ParseRule *getRule(TokenType type) { return &rules[type]; }
bool compile(const char *source, Chunk *chunk) {
@@ -289,8 +383,11 @@ bool compile(const char *source, Chunk *chunk) {
parser.panicMode = false;
advance();
- expression();
- consume(TOKEN_EOF, "Expect end of expression.");
+
+ while (!match(TOKEN_EOF)) {
+ declaration();
+ }
+
endCompiler();
return !parser.hadError;
}
diff --git a/clox/src/debug.c b/clox/src/debug.c
index 788e759..5663091 100644
--- a/clox/src/debug.c
+++ b/clox/src/debug.c
@@ -42,6 +42,10 @@ int disassembleInstruction(Chunk *chunk, int offset) {
return simpleInstruction("OP_TRUE", offset);
case OP_FALSE:
return simpleInstruction("OP_FALSE", offset);
+ case OP_POP:
+ return simpleInstruction("OP_POP", offset);
+ case OP_DEFINE_GLOBAL:
+ return constantInstruction("OP_DEFINE_GLOBAL", chunk, offset);
case OP_EQUAL:
return simpleInstruction("OP_EQUAL", offset);
case OP_GREATER:
@@ -60,6 +64,8 @@ int disassembleInstruction(Chunk *chunk, int offset) {
return simpleInstruction("OP_NOT", offset);
case OP_NEGATE:
return simpleInstruction("OP_NEGATE", offset);
+ case OP_PRINT:
+ return simpleInstruction("OP_PRINT", offset);
case OP_RETURN:
return simpleInstruction("OP_RETURN", offset);
default:
diff --git a/clox/src/main.c b/clox/src/main.c
index c95c7ba..f985584 100644
--- a/clox/src/main.c
+++ b/clox/src/main.c
@@ -66,7 +66,7 @@ int main(int argc, const char *argv[]) {
if (argc == 1) {
repl();
- } else if (argc = 2) {
+ } else if (argc == 2) {
runFile(argv[1]);
} else {
fprintf(stderr, "Usage: clox [path]\n");
diff --git a/clox/src/vm.c b/clox/src/vm.c
index e35a1e0..709f3d1 100644
--- a/clox/src/vm.c
+++ b/clox/src/vm.c
@@ -29,10 +29,13 @@ static void runtimeError(const char *format, ...) {
void initVM() {
resetStack();
vm.objects = NULL;
+
+ initTable(&vm.globals);
initTable(&vm.strings);
}
void freeVM() {
+ freeTable(&vm.globals);
freeTable(&vm.strings);
freeObjects();
}
@@ -70,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_STRING() AS_STRING(READ_CONSTANT())
#define BINARY_OP(valueType, op) \
do { \
if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \
@@ -108,6 +112,15 @@ static InterpretResult run() {
case OP_FALSE:
push(BOOL_VAL(false));
break;
+ case OP_POP:
+ pop();
+ break;
+ case OP_DEFINE_GLOBAL: {
+ ObjString *name = READ_STRING();
+ tableSet(&vm.globals, name, peek(0));
+ pop();
+ break;
+ }
case OP_EQUAL: {
Value b = pop();
Value a = pop();
@@ -152,7 +165,15 @@ static InterpretResult run() {
}
push(NUMBER_VAL(-AS_NUMBER(pop())));
break;
+ case OP_PRINT: {
+ printValue(pop());
+ printf("\n");
+ 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
+ stack. */
printValue(pop());
printf("\n");
return INTERPRET_OK;
@@ -162,6 +183,7 @@ static InterpretResult run() {
#undef READ_BYTE
#undef READ_CONSTANT
+#undef READ_STRING
#undef BINARY_OP
}
diff --git a/clox/src/vm.h b/clox/src/vm.h
index f11c1dd..0ab269b 100644
--- a/clox/src/vm.h
+++ b/clox/src/vm.h
@@ -12,6 +12,7 @@ typedef struct {
uint8_t *ip;
Value stack[STACK_MAX];
Value *stackTop;
+ Table globals;
Table strings;
Obj *objects;
} VM;