diff --git a/clox/src/compiler.c b/clox/src/compiler.c index 75df7aa..81e1616 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -153,7 +153,7 @@ static void expression() { parsePrecedence(PREC_ASSIGNMENT); } static void number() { double value = strtod(parser.previous.start, NULL); - emitConstant(value); + emitConstant(NUMBER_VAL(value)); } static void unary() { diff --git a/clox/src/value.c b/clox/src/value.c index 9a27034..0737999 100644 --- a/clox/src/value.c +++ b/clox/src/value.c @@ -3,28 +3,27 @@ #include "memory.h" #include "value.h" -void initValueArray(ValueArray* array) { +void initValueArray(ValueArray *array) { array->values = NULL; array->capacity = 0; array->count = 0; } -void writeValueArray(ValueArray* array, Value value) { +void writeValueArray(ValueArray *array, Value value) { if (array->capacity < array->count + 1) { int oldCapacity = array->capacity; array->capacity = GROW_CAPACITY(oldCapacity); - array->values = GROW_ARRAY(Value, array->values, oldCapacity, array->capacity); + array->values = + GROW_ARRAY(Value, array->values, oldCapacity, array->capacity); } array->values[array->count] = value; array->count++; } -void freeValueArray(ValueArray* array) { +void freeValueArray(ValueArray *array) { FREE_ARRAY(Value, array->values, array->capacity); initValueArray(array); } -void printValue(Value value) { - printf("%g", value); -} +void printValue(Value value) { printf("%g", AS_NUMBER(value)); } diff --git a/clox/src/value.h b/clox/src/value.h index 86d4b1c..7478aaf 100644 --- a/clox/src/value.h +++ b/clox/src/value.h @@ -3,17 +3,40 @@ #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 IS_BOOL(value) ((value).type == VAL_BOOL) +#define IS_NIL(value) ((value).type == VAL_NIL) +#define IS_NUMBER(value) ((value).type == VAL_NUMBER) + +#define AS_BOOL(value) ((value).as.boolean) +#define AS_NUMBER(value) ((value).as.number) + +#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}}) typedef struct { int capacity; int count; - Value* values; + Value *values; } ValueArray; -void initValueArray(ValueArray* array); -void writeValueArray(ValueArray* array, Value value); -void freeValueArray(ValueArray* array); +void initValueArray(ValueArray *array); +void writeValueArray(ValueArray *array, Value value); +void freeValueArray(ValueArray *array); void printValue(Value value); #endif diff --git a/clox/src/vm.c b/clox/src/vm.c index eb79661..381c8c0 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -1,3 +1,4 @@ +#include #include #include "common.h" @@ -9,6 +10,19 @@ VM vm; 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 initVM() { resetStack(); } void freeVM() {} @@ -23,14 +37,19 @@ Value pop() { return *vm.stackTop; } +static Value peek(int distance) { return vm.stackTop[-1 - distance]; } + 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."); \ + } \ + double b = AS_NUMBER(pop()); \ + double a = AS_NUMBER(pop()); \ + push(valueType(a op b)); \ } while (false) for (;;) { @@ -53,19 +72,23 @@ static InterpretResult run() { break; } case OP_ADD: - BINARY_OP(+); + BINARY_OP(NUMBER_VAL, +); break; case OP_SUBTRACT: - BINARY_OP(-); + BINARY_OP(NUMBER_VAL, -); break; case OP_MULTIPLY: - BINARY_OP(*); + BINARY_OP(NUMBER_VAL, *); break; case OP_DIVIDE: - BINARY_OP(/); + BINARY_OP(NUMBER_VAL, /); break; case OP_NEGATE: - push(-pop()); + 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());