diff --git a/src/' b/src/' deleted file mode 100644 index 4780c8e..0000000 --- a/src/' +++ /dev/null @@ -1,47 +0,0 @@ -#include "common.h" -#include "vm.h" - -VM vm; - -void newVM() { - -} - -void freeVM() { - -} - -static InterpretResult run() { -#define READ_BYTE() (*vm.ip++) -#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()]) - - for (;;) { -#ifdef DEBUG_TRACE_EXECUTION - disassembleInstruction(vm.chunk, - (int)(vm.ip - vm.chunk->code)); -#endif - - uint8_t instruction; - switch (instruction = READ_BYTE()) { - case OP_CONSTANT: { - Value constant = READ_CONSTANT(); - printValue(constant); - printf("\n"); - break; - } - case OP_RETURN: { - return INTERPRET_OK; - } - } - } - -#undef READ_BYTE -#undef READ_CONSTANT -} - -InterpretResult interpret(Chunk* chunk) { - vm.chunk = chunk; - vm.ip = vm.chunk->code; - return run(); -} - diff --git a/src/chunk.h b/src/chunk.h index f02ad14..563a80c 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -8,10 +8,17 @@ typedef enum { OP_NOOP, 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/src/compiler.c b/src/compiler.c index 82c316d..9416a3e 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -141,7 +141,7 @@ static void grouping() { static void number() { double value = strtod(parser.previous.start, NULL); - emitConstant(value); + emitConstant(NUMBER_VAL(value)); } static void binary() { @@ -150,6 +150,12 @@ 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; case TOKEN_MINUS: emitByte(OP_SUBTRACT); break; case TOKEN_STAR: emitByte(OP_MULTIPLY); break; @@ -158,6 +164,15 @@ 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 unary() { TokenType operatorType = parser.previous.type; @@ -166,6 +181,7 @@ static void unary() { // Emit the operator instruction. switch (operatorType) { + case TOKEN_BANG: emitByte(OP_NOT); break; case TOKEN_MINUS: emitByte(OP_NEGATE); break; default: return; // Unreachable. } @@ -183,31 +199,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_TYPE] = {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_FN] = {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_LET] = {NULL, NULL, PREC_NONE}, [TOKEN_WHILE] = {NULL, NULL, PREC_NONE}, [TOKEN_ERROR] = {NULL, NULL, PREC_NONE}, diff --git a/src/debug.c b/src/debug.c index 0f9a408..dca5802 100644 --- a/src/debug.c +++ b/src/debug.c @@ -42,6 +42,18 @@ int disassembleInstruction(Chunk* chunk, int offset) { return simpleInstruction("OP_NEGATE", offset); 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: @@ -50,6 +62,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_RETURN: return simpleInstruction("OP_RETURN", offset); default: diff --git a/src/value.c b/src/value.c index 664afdb..13d4591 100644 --- a/src/value.c +++ b/src/value.c @@ -27,5 +27,21 @@ void freeValueArray(ValueArray *array) { } void printValue(Value value) { - printf("%g", 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/src/value.h b/src/value.h index 6a8c1bd..19ac3b5 100644 --- a/src/value.h +++ b/src/value.h @@ -3,7 +3,30 @@ #include "common.h" -typedef double Value; +typedef enum { + VAL_BOOL, + VAL_NIL, + VAL_NUMBER, +} ValueType; + +typedef struct { + ValueType type; + union { + bool boolean; + double number; + } as; +} Value; + +#define BOOL_VAL(value) ((Value){VAL_BOOL,{.boolean = value}}) +#define NIL_VAL ((Value){VAL_NIL, {.number = 0}}) +#define NUMBER_VAL(value) ((Value){VAL_NUMBER, {.number = value}}) + +#define AS_BOOL(value) ((value).as.boolean) +#define AS_NUMBER(value) ((value).as.number) + +#define IS_BOOL(value) ((value).type == VAL_BOOL) +#define IS_NIL(value) ((value).type == VAL_NIL) +#define IS_NUMBER(value) ((value).type == VAL_NUMBER) typedef struct { int capacity; @@ -11,6 +34,7 @@ typedef struct { Value *values; } ValueArray; +bool valuesEqual(Value a, Value b); void newValueArray(ValueArray *array); void writeValueArray(ValueArray *array, Value value); void freeValueArray(ValueArray *array); diff --git a/src/vm.c b/src/vm.c index bd909b9..da00e48 100644 --- a/src/vm.c +++ b/src/vm.c @@ -4,6 +4,7 @@ #include "vm.h" #include +#include VM vm; @@ -11,6 +12,19 @@ static void resetStack() { vm.stackTop = vm.stack; } +static void runtimeError(const char* format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fputs("\n", stderr); + + size_t instruction = vm.ip - vm.chunk->code - 1; + int line = vm.chunk->lines[instruction]; + fprintf(stderr, "[line %d] in script\n", line); + resetStack(); +} + void newVM() { resetStack(); } @@ -29,14 +43,26 @@ Value pop() { return *vm.stackTop; } +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()]) -#define BINARY_OP(op) \ +#define BINARY_OP(valueType, op) \ do { \ - double b = pop(); \ - double a = pop(); \ - push(a op b); \ + if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) { \ + runtimeError("Operands must be numbers."); \ + return INTERPRET_RUNTIME_ERROR; \ + } \ + double b = AS_NUMBER(pop()); \ + double a = AS_NUMBER(pop()); \ + push(valueType(a op b)); \ } while (false) for (;;) { @@ -59,11 +85,31 @@ static InterpretResult run() { push(constant); break; } - case OP_ADD: BINARY_OP(+); break; - case OP_SUBTRACT: BINARY_OP(-); break; - case OP_MULTIPLY: BINARY_OP(*); break; - case OP_DIVIDE: BINARY_OP(/); break; - case OP_NEGATE: push(-pop()); 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; + case OP_SUBTRACT: BINARY_OP(NUMBER_VAL, -); break; + case OP_MULTIPLY: BINARY_OP(NUMBER_VAL, *); break; + 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."); + return INTERPRET_RUNTIME_ERROR; + } + push(NUMBER_VAL(-AS_NUMBER(pop()))); + break; case OP_RETURN: { printValue(pop()); printf("\n"); diff --git a/test/add.ztl b/test/add.ztl index 0e92774..e0ef584 100644 --- a/test/add.ztl +++ b/test/add.ztl @@ -1 +1 @@ -1 + 2; +1 + 2