diff --git a/clox/compiler.c b/clox/compiler.c index 67e1ab2..2ab79d3 100644 --- a/clox/compiler.c +++ b/clox/compiler.c @@ -173,6 +173,11 @@ static void number() { emitConstant(NUMBER_VAL(value)); } +static void string() { + emitConstant(OBJ_VAL(copyString(parser.previous.start + 1, + parser.previous.length - 2))); +} + static void unary() { TokenType operatorType = parser.previous.type; @@ -226,7 +231,7 @@ ParseRule rules[] = { [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_STRING] = {string, NULL, PREC_NONE}, [TOKEN_NUMBER] = {number, NULL, PREC_NONE}, [TOKEN_AND] = {NULL, NULL, PREC_NONE}, [TOKEN_CLASS] = {NULL, NULL, PREC_NONE}, diff --git a/clox/compiler.h b/clox/compiler.h index 6e976ad..83906b4 100644 --- a/clox/compiler.h +++ b/clox/compiler.h @@ -1,6 +1,7 @@ #ifndef clox_compiler_h #define clox_compiler_h +#include "object.h" #include "vm.h" bool compile(const char* source, Chunk* chunk); diff --git a/clox/memory.c b/clox/memory.c index ead60c8..aca11ed 100644 --- a/clox/memory.c +++ b/clox/memory.c @@ -1,6 +1,7 @@ #include #include "memory.h" +#include "vm.h" void* reallocate(void* pointer, size_t oldSize, size_t newSize) { if (newSize == 0) { @@ -12,3 +13,23 @@ void* reallocate(void* pointer, size_t oldSize, size_t newSize) { if (result == NULL) exit(1); return result; } +static void freeObject(Obj* object) { + switch (object->type) { + case OBJ_STRING: { + ObjString* string = (ObjString*)object; + FREE_ARRAY(char, string->chars, string->length + 1); + FREE(ObjString, object); + break; + } + } +} +void freeObjects() { + Obj* object = vm.objects; + while (object != NULL) { + Obj* next = object->next; + freeObject(object); + object = next; + } +} + + diff --git a/clox/memory.h b/clox/memory.h index faec02e..fbdfd82 100644 --- a/clox/memory.h +++ b/clox/memory.h @@ -2,6 +2,12 @@ #define clox_memory_h #include "common.h" +#include "object.h" + +#define ALLOCATE(type, count) \ + (type*)reallocate(NULL, 0, sizeof(type) * (count)) + +#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0) #define GROW_CAPACITY(capacity) \ ((capacity) < 8 ? 8 : (capacity) * 2) @@ -14,5 +20,6 @@ reallocate(pointer, sizeof(type) * (oldCount), 0) void* reallocate(void* pointer, size_t oldSize, size_t newSize); +void freeObjects(); #endif diff --git a/clox/object.c b/clox/object.c new file mode 100644 index 0000000..811feb9 --- /dev/null +++ b/clox/object.c @@ -0,0 +1,43 @@ +#include +#include + +#include "memory.h" +#include "object.h" +#include "value.h" +#include "vm.h" + +#define ALLOCATE_OBJ(type, objectType) \ + (type*)allocateObject(sizeof(type), objectType) + +static Obj* allocateObject(size_t size, ObjType type) { + Obj* object = (Obj*)reallocate(NULL, 0, size); + object->type = type; + + object->next = vm.objects; + vm.objects = object; + return object; +} + + +static ObjString* allocateString(char* chars, int length) { + ObjString* string = ALLOCATE_OBJ(ObjString, OBJ_STRING); + string->length = length; + string->chars = chars; + return string; +} +ObjString* takeString(char* chars, int length) { + return allocateString(chars, length); +} +ObjString* copyString(const char* chars, int length) { + char* heapChars = ALLOCATE(char, length + 1); + memcpy(heapChars, chars, length); + heapChars[length] = '\0'; + return allocateString(heapChars, length); +} +void printObject(Value value) { + switch (OBJ_TYPE(value)) { + case OBJ_STRING: + printf("%s", AS_CSTRING(value)); + break; + } +} diff --git a/clox/object.h b/clox/object.h new file mode 100644 index 0000000..82e38ed --- /dev/null +++ b/clox/object.h @@ -0,0 +1,33 @@ +#ifndef clox_object_h +#define clox_object_h + +#include "value.h" + +#define OBJ_TYPE(value) (AS_OBJ(value)->type) + +#define IS_STRING(value) isObjType(value, OBJ_STRING) + +#define AS_STRING(value) ((ObjString*)AS_OBJ(value)) +#define AS_CSTRING(value) (((ObjString*)AS_OBJ(value))->chars) + +typedef enum { + OBJ_STRING, +} ObjType; + +struct Obj { + ObjType type; + struct Obj* next; +}; +struct ObjString { + Obj obj; + int length; + char* chars; +}; +ObjString* takeString(char* chars, int length); +ObjString* copyString(const char* chars, int length); +void printObject(Value value); +static inline bool isObjType(Value value, ObjType type) { + return IS_OBJ(value) && AS_OBJ(value)->type == type; +} + +#endif diff --git a/clox/tests/test_string.lox b/clox/tests/test_string.lox new file mode 100644 index 0000000..deaa69c --- /dev/null +++ b/clox/tests/test_string.lox @@ -0,0 +1 @@ +"st" + "ri" + "ng" diff --git a/clox/value.c b/clox/value.c index f1f3727..e06e97d 100644 --- a/clox/value.c +++ b/clox/value.c @@ -1,5 +1,7 @@ #include +#include "string.h" +#include "object.h" #include "memory.h" #include "value.h" @@ -30,6 +32,7 @@ void printValue(Value value) { break; case VAL_NIL: printf("nil"); break; case VAL_NUMBER: printf("%g", AS_NUMBER(value)); break; + case VAL_OBJ: printObject(value); break; } } @@ -39,6 +42,13 @@ bool valuesEqual(Value a, Value b) { 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); + case VAL_OBJ: { + ObjString* aString = AS_STRING(a); + ObjString* bString = AS_STRING(b); + return aString->length == bString->length && + memcmp(aString->chars, bString->chars, + aString->length) == 0; + } default: return false; // Unreachable. } } diff --git a/clox/value.h b/clox/value.h index c8af524..6a9b6bb 100644 --- a/clox/value.h +++ b/clox/value.h @@ -3,13 +3,15 @@ #include "common.h" - +typedef struct Obj Obj; +typedef struct ObjString ObjString; //typedef double Value; typedef enum { VAL_BOOL, VAL_NIL, VAL_NUMBER, + VAL_OBJ } ValueType; typedef struct { @@ -17,19 +19,22 @@ typedef struct { union { bool boolean; double number; - } as; + Obj* obj; + } 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 IS_OBJ(value) ((value).type == VAL_OBJ) #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}}) +#define OBJ_VAL(object) ((Value){VAL_OBJ, {.obj = (Obj*)object}}) + typedef struct { int capacity; diff --git a/clox/vm.c b/clox/vm.c index 8e70669..3a70840 100644 --- a/clox/vm.c +++ b/clox/vm.c @@ -1,9 +1,12 @@ #include #include +#include #include "common.h" #include "compiler.h" -#include "vm.h" #include "debug.h" +#include "object.h" +#include "memory.h" +#include "vm.h" VM vm; static void resetStack() { vm.stackTop = vm.stack; @@ -24,10 +27,11 @@ static void runtimeError(const char* format, ...) { void initVM() { resetStack(); + vm.objects = NULL; } void freeVM() { - + freeObjects(); } void push(Value value) { *vm.stackTop = value; @@ -45,6 +49,20 @@ static Value peek(int distance) { static bool isFalsey(Value value) { return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value)); } +static void concatenate() { + ObjString* b = AS_STRING(pop()); + ObjString* a = AS_STRING(pop()); + + int length = a->length + b->length; + char* chars = ALLOCATE(char, length + 1); + memcpy(chars, a->chars, a->length); + memcpy(chars + a->length, b->chars, b->length); + chars[length] = '\0'; + + ObjString* result = takeString(chars, length); + push(OBJ_VAL(result)); +} + static InterpretResult run() { #define READ_BYTE() (*vm.ip++) @@ -91,7 +109,20 @@ static InterpretResult run() { } 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_ADD: { + if (IS_STRING(peek(0)) && IS_STRING(peek(1))) { + concatenate(); + } else if (IS_NUMBER(peek(0)) && IS_NUMBER(peek(1))) { + double b = AS_NUMBER(pop()); + double a = AS_NUMBER(pop()); + push(NUMBER_VAL(a + b)); + } else { + runtimeError( + "Operands must be two numbers or two strings."); + return INTERPRET_RUNTIME_ERROR; + } + 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; diff --git a/clox/vm.h b/clox/vm.h index 87e3416..c8b1d39 100644 --- a/clox/vm.h +++ b/clox/vm.h @@ -11,6 +11,7 @@ typedef struct { uint8_t* ip; Value stack[STACK_MAX]; Value* stackTop; + Obj* objects; } VM; typedef enum { INTERPRET_OK, @@ -20,7 +21,8 @@ typedef enum { void initVM(); void freeVM(); -InterpretResult interpret(const char* source); +InterpretResult interpret(const char* chunk); +extern VM vm; void push(Value value); Value pop();