From 58cecafb3b2b7e9484a254d4a73f94c0cd5f3a42 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 29 Nov 2024 12:17:54 +1100 Subject: [PATCH] WIP: Adding arrays to value structure, tests incomplete --- source/toy_value.c | 125 ++++++++++++++++++++++++++-------- source/toy_value.h | 9 ++- source/toy_vm.c | 17 ++++- tests/cases/test_value.c | 140 +++++++++++++++++++++++++++------------ 4 files changed, 216 insertions(+), 75 deletions(-) diff --git a/source/toy_value.c b/source/toy_value.c index 1bafa20..9f62552 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -1,7 +1,9 @@ #include "toy_value.h" #include "toy_console_colors.h" +#include "toy_bucket.h" #include "toy_string.h" +#include "toy_array.h" #include #include @@ -32,7 +34,18 @@ unsigned int Toy_hashValue(Toy_Value value) { case TOY_VALUE_STRING: return Toy_hashString(TOY_VALUE_AS_STRING(value)); - case TOY_VALUE_ARRAY: + case TOY_VALUE_ARRAY: { + //since array internals can change, recalc the hash each time it's needed + Toy_Array* array = TOY_VALUE_AS_ARRAY(value); + unsigned int hash = 0; + + for (unsigned int i = 0; i < array->count; i++) { + hash ^= Toy_hashValue(array->data[i]); + } + + return hash; + } + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: @@ -59,7 +72,17 @@ Toy_Value Toy_copyValue(Toy_Value value) { return TOY_VALUE_FROM_STRING(Toy_copyString(string)); } - case TOY_VALUE_ARRAY: + case TOY_VALUE_ARRAY: { + Toy_Array* array = TOY_VALUE_AS_ARRAY(value); + Toy_Array* result = Toy_resizeArray(NULL, array->capacity); + + for (unsigned int i = 0; i < array->count; i++) { + result->data[i] = Toy_copyValue(array->data[i]); + } + + return TOY_VALUE_FROM_ARRAY(result); + } + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: @@ -88,7 +111,16 @@ void Toy_freeValue(Toy_Value value) { break; } - case TOY_VALUE_ARRAY: + case TOY_VALUE_ARRAY: { + Toy_Array* array = TOY_VALUE_AS_ARRAY(value); + + for (unsigned int i = 0; i < array->count; i++) { + Toy_freeValue(array->data[i]); + } + + TOY_ARRAY_FREE(array); + } + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: @@ -154,7 +186,26 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) { break; } - case TOY_VALUE_ARRAY: + case TOY_VALUE_ARRAY: { + Toy_Array* leftArray = TOY_VALUE_AS_ARRAY(left); + Toy_Array* rightArray = TOY_VALUE_AS_ARRAY(right); + + //different lengths is an easy way to check + if (leftArray->count != rightArray->count) { + return false; + } + + for (unsigned int i = 0; i < leftArray->count; i++) { + //any mismatch is an easy difference + if (Toy_checkValuesAreEqual(leftArray->data[i], rightArray->data[i])) { + return false; + } + } + + //finally + return true; + } + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: @@ -169,6 +220,8 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) { } bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) { + //NOTE: "equal" and "comparable" are different - equal means they're identical, comparable is only possible for certain types + switch(left.type) { case TOY_VALUE_NULL: return false; @@ -184,6 +237,9 @@ bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right) { return TOY_VALUE_IS_STRING(right); case TOY_VALUE_ARRAY: + //nothing is comparable with an array + return false; + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: @@ -232,6 +288,8 @@ int Toy_compareValues(Toy_Value left, Toy_Value right) { } case TOY_VALUE_ARRAY: + break; + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: @@ -245,48 +303,57 @@ int Toy_compareValues(Toy_Value left, Toy_Value right) { return -1; } -void Toy_stringifyValue(Toy_Value value, Toy_callbackType callback) { - //NOTE: don't append a newline +Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) { + //TODO: could have "constant" strings that can be referenced, instead of null, true, false, etc. + switch(value.type) { case TOY_VALUE_NULL: - callback("null"); - break; + return Toy_createString(bucketHandle, "null"); case TOY_VALUE_BOOLEAN: - callback(TOY_VALUE_AS_BOOLEAN(value) ? "true" : "false"); - break; + return Toy_createString(bucketHandle, TOY_VALUE_AS_BOOLEAN(value) ? "true" : "false"); case TOY_VALUE_INTEGER: { char buffer[16]; sprintf(buffer, "%d", TOY_VALUE_AS_INTEGER(value)); - callback(buffer); - break; + return Toy_createString(bucketHandle, buffer); } case TOY_VALUE_FLOAT: { char buffer[16]; sprintf(buffer, "%f", TOY_VALUE_AS_FLOAT(value)); - callback(buffer); - break; + return Toy_createString(bucketHandle, buffer); } - case TOY_VALUE_STRING: { - Toy_String* str = TOY_VALUE_AS_STRING(value); - if (str->type == TOY_STRING_NODE) { - char* buffer = Toy_getStringRawBuffer(str); - callback(buffer); - free(buffer); + case TOY_VALUE_STRING: + return Toy_copyString(TOY_VALUE_AS_STRING(value)); + + case TOY_VALUE_ARRAY: { + //TODO: concat + free is definitely a performance nightmare + Toy_Array* array = TOY_VALUE_AS_ARRAY(value); + Toy_String* string = Toy_createStringLength(bucketHandle, "[", 1); + Toy_String* comma = Toy_createStringLength(bucketHandle, ",", 1); //reusable + + for (unsigned int i = 0; i < array->count; i++) { + //append each element + Toy_String* tmp = Toy_concatStrings(bucketHandle, string, Toy_stringifyValue(bucketHandle, array->data[i])); //increment ref + Toy_freeString(string); //decrement ref + string = tmp; + + //if we need a comma + if (i + 1 < array->count) { + Toy_String* tmp = Toy_concatStrings(bucketHandle, string, comma); //increment ref + Toy_freeString(string); //decrement ref + string = tmp; + } } - else if (str->type == TOY_STRING_LEAF) { - callback(str->as.leaf.data); - } - else if (str->type == TOY_STRING_NAME) { - callback(str->as.name.data); //should this be a thing? - } - break; + + //clean up + Toy_freeString(comma); //TODO: reusable global, or string type "permanent" + + return string; } - case TOY_VALUE_ARRAY: case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: @@ -296,6 +363,8 @@ void Toy_stringifyValue(Toy_Value value, Toy_callbackType callback) { fprintf(stderr, TOY_CC_ERROR "Unknown types in value stringify, exiting\n" TOY_CC_RESET); exit(-1); } + + return NULL; } const char* Toy_private_getValueTypeAsCString(Toy_ValueType type) { diff --git a/source/toy_value.h b/source/toy_value.h index 4cd4f3c..914bf44 100644 --- a/source/toy_value.h +++ b/source/toy_value.h @@ -4,7 +4,9 @@ #include "toy_print.h" //forward declarations +struct Toy_Bucket; struct Toy_String; +struct Toy_Array; typedef enum Toy_ValueType { TOY_VALUE_NULL, @@ -28,6 +30,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS int integer; //4 | 4 float number; //4 | 4 struct Toy_String* string; //4 | 8 + struct Toy_Array* array; //4 | 8 //TODO: more types go here //TODO: consider 'stack' as a possible addition } as; //4 | 8 @@ -49,6 +52,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS #define TOY_VALUE_AS_INTEGER(value) ((value).as.integer) #define TOY_VALUE_AS_FLOAT(value) ((value).as.number) #define TOY_VALUE_AS_STRING(value) ((value).as.string) +#define TOY_VALUE_AS_ARRAY(value) ((value).as.array) //TODO: more #define TOY_VALUE_FROM_NULL() ((Toy_Value){{ .integer = 0 }, TOY_VALUE_NULL}) @@ -56,6 +60,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS #define TOY_VALUE_FROM_INTEGER(value) ((Toy_Value){{ .integer = value }, TOY_VALUE_INTEGER}) #define TOY_VALUE_FROM_FLOAT(value) ((Toy_Value){{ .number = value }, TOY_VALUE_FLOAT}) #define TOY_VALUE_FROM_STRING(value) ((Toy_Value){{ .string = value }, TOY_VALUE_STRING}) +#define TOY_VALUE_FROM_ARRAY(value) ((Toy_Value){{ .array = value }, TOY_VALUE_ARRAY}) //TODO: more //utilities @@ -69,8 +74,8 @@ TOY_API bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right); TOY_API bool Toy_checkValuesAreComparable(Toy_Value left, Toy_Value right); TOY_API int Toy_compareValues(Toy_Value left, Toy_Value right); -//convert the value to a string, then forward it to a callback -TOY_API void Toy_stringifyValue(Toy_Value value, Toy_callbackType callback); +//convert the value to a string - values that *are* strings are simply copied +TOY_API struct Toy_String* Toy_stringifyValue(struct Toy_Bucket** bucketHandle, Toy_Value value); //for debugging TOY_API const char* Toy_private_getValueTypeAsCString(Toy_ValueType type); diff --git a/source/toy_vm.c b/source/toy_vm.c index 8f84c4a..bd4d3c6 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -435,7 +435,14 @@ static void processAssert(Toy_VM* vm) { //do the check if (TOY_VALUE_IS_NULL(value) || Toy_checkValueIsTruthy(value) == false) { //on a failure, print the message - Toy_stringifyValue(message, Toy_assertFailure); + Toy_String* string = Toy_stringifyValue(&vm->stringBucket, message); + char* buffer = Toy_getStringRawBuffer(string); + + Toy_assertFailure(buffer); + + free(buffer); + Toy_freeString(string); + return; } //cleanup @@ -446,7 +453,13 @@ static void processAssert(Toy_VM* vm) { static void processPrint(Toy_VM* vm) { //print the value on top of the stack, popping it Toy_Value value = Toy_popStack(&vm->stack); - Toy_stringifyValue(value, Toy_print); + Toy_String* string = Toy_stringifyValue(&vm->stringBucket, value); + char* buffer = Toy_getStringRawBuffer(string); //TODO: check string type to skip this call + + Toy_print(buffer); + + free(buffer); + Toy_freeString(string); Toy_freeValue(value); } diff --git a/tests/cases/test_value.c b/tests/cases/test_value.c index ba786c9..b863470 100644 --- a/tests/cases/test_value.c +++ b/tests/cases/test_value.c @@ -3,6 +3,7 @@ #include "toy_bucket.h" #include "toy_string.h" +#include "toy_array.h" #include #include @@ -63,6 +64,77 @@ int test_value_creation() { Toy_freeBucket(&bucket); } + //test creating arrays + { + //setup + Toy_Array* array = TOY_ARRAY_ALLOCATE(); + TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(42)); + TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(69)); + TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(8891)); + + Toy_Value v = TOY_VALUE_FROM_ARRAY(array); + + if (TOY_VALUE_AS_ARRAY(v) == false || + TOY_VALUE_AS_ARRAY(v)->capacity != 8 || + TOY_VALUE_AS_ARRAY(v)->count != 3 || + TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[0]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[0]) != 42 || + TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[1]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[1]) != 69 || + TOY_VALUE_IS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[2]) != true || TOY_VALUE_AS_INTEGER(TOY_VALUE_AS_ARRAY(v)->data[2]) != 8891 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: 'array' value failed\n" TOY_CC_RESET); + TOY_ARRAY_FREE(array); + return -1; + } + } + + return 0; +} + +//URGENT: copy values + +int test_value_hashing() { + //test value hashing + { + //setup + Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); + + //values + Toy_Value n = TOY_VALUE_FROM_NULL(); + Toy_Value t = TOY_VALUE_FROM_BOOLEAN(true); + Toy_Value f = TOY_VALUE_FROM_BOOLEAN(false); + Toy_Value i = TOY_VALUE_FROM_INTEGER(42); + //skip float + Toy_Value s = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "Hello world")); + + Toy_Array* array = TOY_ARRAY_ALLOCATE(); + TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(42)); + TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(69)); + TOY_ARRAY_PUSHBACK(array, TOY_VALUE_FROM_INTEGER(8891)); + Toy_Value a = TOY_VALUE_FROM_ARRAY(array); + + if (Toy_hashValue(n) != 0 || + Toy_hashValue(t) != 1 || + Toy_hashValue(f) != 0 || + Toy_hashValue(i) != 4147366645 || + Toy_hashValue(s) != 994097935 || + TOY_VALUE_AS_STRING(s)->cachedHash == 0 || + Toy_hashValue(a) != 2544446955 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected hash of a value\n" TOY_CC_RESET); + TOY_ARRAY_FREE(array); + Toy_freeBucket(&bucket); + return -1; + } + + //cleanup + TOY_ARRAY_FREE(array); + Toy_freeBucket(&bucket); + } + + //URGENT: string hashing, use godbolt + return 0; } @@ -171,42 +243,6 @@ int test_value_comparison() { return 0; } -int test_value_hashing() { - //test value hashing - { - //setup - Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); - - //values - Toy_Value n = TOY_VALUE_FROM_NULL(); - Toy_Value t = TOY_VALUE_FROM_BOOLEAN(true); - Toy_Value f = TOY_VALUE_FROM_BOOLEAN(false); - Toy_Value i = TOY_VALUE_FROM_INTEGER(42); - //skip float - Toy_Value s = TOY_VALUE_FROM_STRING(Toy_createString(&bucket, "Hello world")); - - if (Toy_hashValue(n) != 0 || - Toy_hashValue(t) != 1 || - Toy_hashValue(f) != 0 || - Toy_hashValue(i) != 4147366645 || - Toy_hashValue(s) != 994097935 || - TOY_VALUE_AS_STRING(s)->cachedHash == 0 - ) - { - fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected hash of a value\n" TOY_CC_RESET); - Toy_freeBucket(&bucket); - return -1; - } - - //cleanup - Toy_freeBucket(&bucket); - } - - //NOTE: string hash is a PITA, skipping - - return 0; -} - int main() { //run each test set, returning the total errors given int total = 0, res = 0; @@ -219,6 +255,22 @@ int main() { total += res; } + // { //URGENT: test copying + // res = test_value_copying(); + // if (res == 0) { + // printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + // } + // total += res; + // } + + { + res = test_value_hashing(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + { res = test_value_equality(); if (res == 0) { @@ -235,13 +287,15 @@ int main() { total += res; } - { - res = test_value_hashing(); - if (res == 0) { - printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); - } - total += res; - } + // { //URGENT: test stringify + // res = test_value_stringify(); + // if (res == 0) { + // printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + // } + // total += res; + // } + + //URGENT: arrays return total; }