diff --git a/README.md b/README.md index 5384dec..d77e590 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,15 @@ This repository holds the reference implementation for Toy version 2.x, written ## Building -*Omitted for review.* +Supported platforms are: `linux-latest`, `windows-latest`, `macos-latest`, using [GitHub's standard runners](https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories). + +To build the library, run `make source`. + +To build the library and repl, run `make repl`. + +To build and run the test cases, run `make tests`. + +To build and run the test cases under gdb, run `make tests-gdb`. ## Tools diff --git a/source/toy_memory.c b/source/toy_memory.c index e7b7eb5..de84439 100644 --- a/source/toy_memory.c +++ b/source/toy_memory.c @@ -28,7 +28,7 @@ void Toy_initBucket(Toy_Bucket** bucketHandle, size_t capacity) { exit(1); } - (*bucketHandle) = malloc(sizeof(Toy_Bucket)); + (*bucketHandle) = malloc(sizeof(Toy_Bucket)); //TODO: rework the bucket, so there's only one malloc() call instead of two when partitioning if ((*bucketHandle) == NULL) { fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Failed to allocate space for a bucket\n" TOY_CC_RESET); @@ -48,6 +48,12 @@ void* Toy_partBucket(Toy_Bucket** bucketHandle, size_t space) { exit(1); } + //if you try to allocate too much space + if ((*bucketHandle)->capacity < space) { + fprintf(stderr, TOY_CC_ERROR "[internal] ERROR: Failed to partition bucket memory, not enough capacity\n" TOY_CC_RESET); + exit(1); + } + //if out of space in the current bucket if ((*bucketHandle)->capacity < (*bucketHandle)->count + space) { //move to the next bucket diff --git a/source/toy_string.c b/source/toy_string.c new file mode 100644 index 0000000..beb6866 --- /dev/null +++ b/source/toy_string.c @@ -0,0 +1,126 @@ +#include "toy_string.h" +#include "toy_console_colors.h" + +#include +#include +#include + +//utils +static void deepCopyUtil(char* dest, Toy_String* str) { + //sometimes, "clever" can be a bad thing... + if (str->type == TOY_STRING_NODE) { + deepCopyUtil(dest, str->as.node.left); + deepCopyUtil(dest + str->as.node.left->length, str->as.node.right); + } + + else { + memcpy(dest, str->as.leaf.data, str->length); + } +} + +static void incrementRefCount(Toy_String* str) { + str->refCount++; + if (str->type == TOY_STRING_NODE) { + incrementRefCount(str->as.node.left); + incrementRefCount(str->as.node.right); + } +} + +static void decrementRefCount(Toy_String* str) { + str->refCount--; + if (str->type == TOY_STRING_NODE) { + decrementRefCount(str->as.node.left); + decrementRefCount(str->as.node.right); + } +} + +//exposed functions +Toy_String* Toy_createString(Toy_Bucket** bucket, const char* cstring) { + int length = strlen(cstring); + + return Toy_createStringLength(bucket, cstring, length); +} + +Toy_String* Toy_createStringLength(Toy_Bucket** bucket, const char* cstring, int length) { + Toy_String* ret = (Toy_String*)Toy_partBucket(bucket, sizeof(Toy_String) + length + 1); //TODO: compensate for partitioning more space than bucket capacity + + ret->type = TOY_STRING_LEAF; + ret->length = length; + ret->refCount = 1; + memcpy(ret->as.leaf.data, cstring, length); + ret->as.leaf.data[length] = '\0'; + + return ret; +} + +Toy_String* Toy_copyString(Toy_Bucket** bucket, Toy_String* str) { + if (str->refCount <= 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't copy a string with refcount below or equal to zero\n" TOY_CC_RESET); + exit(-1); + } + incrementRefCount(str); + return str; +} + +Toy_String* Toy_deepCopyString(Toy_Bucket** bucket, Toy_String* str) { + if (str->refCount <= 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't deep copy a string with refcount below or equal to zero\n" TOY_CC_RESET); + exit(-1); + } + Toy_String* ret = (Toy_String*)Toy_partBucket(bucket, sizeof(Toy_String) + str->length + 1); //TODO: compensate for partitioning more space than bucket capacity + + //TODO + ret->type = TOY_STRING_LEAF; + ret->length = str->length; + ret->refCount = 1; + deepCopyUtil(ret->as.leaf.data, str); //copy each leaf into the buffer + ret->as.leaf.data[ret->length] = '\0'; + + return ret; +} + +Toy_String* Toy_concatString(Toy_Bucket** bucket, Toy_String* left, Toy_String* right) { + if (left->refCount <= 0 || right->refCount <= 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't concatenate a string with refcount below or equal to zero\n" TOY_CC_RESET); + exit(-1); + } + + Toy_String* ret = (Toy_String*)Toy_partBucket(bucket, sizeof(Toy_String)); + + ret->type = TOY_STRING_NODE; + ret->length = left->length + right->length; + ret->refCount = 1; + ret->as.node.left = left; + ret->as.node.right = right; + + incrementRefCount(left); + incrementRefCount(right); + + return ret; +} + +void Toy_freeString(Toy_String* str) { + decrementRefCount(str); //TODO: tool for checking the bucket is empty, and freeing it +} + +int Toy_getStringLength(Toy_String* str) { + return str->length; +} + +int Toy_getStringRefCount(Toy_String* str) { + return str->refCount; +} + +char* Toy_getStringRawBuffer(Toy_String* str) { + if (str->refCount <= 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't get raw string buffer of a string with refcount below or equal to zero\n" TOY_CC_RESET); + exit(-1); + } + + char* buffer = TOY_ALLOCATE(char, str->length + 1); + + deepCopyUtil(buffer, str); + buffer[str->length] = '\0'; + + return buffer; +} diff --git a/source/toy_string.h b/source/toy_string.h new file mode 100644 index 0000000..349da24 --- /dev/null +++ b/source/toy_string.h @@ -0,0 +1,43 @@ +#include "toy_common.h" + +#include "toy_memory.h" + +//rope pattern +typedef struct Toy_String { //32 | 64 BITNESS + enum Toy_StringType { + TOY_STRING_NODE, + TOY_STRING_LEAF, + } type; //4 | 4 + + int length; //4 | 4 + int refCount; //4 | 4 + + union { + struct { + struct Toy_String* left; //4 | 8 + struct Toy_String* right; //4 | 8 + } node; //8 | 16 + + struct { + int dummy; //4 | 4 + char data[]; //- | - + } leaf; //4 | 4 + } as; //8 | 16 +} Toy_String; //20 | 28 + +TOY_API Toy_String* Toy_createString(Toy_Bucket** bucket, const char* cstring); +TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucket, const char* cstring, int length); + +TOY_API Toy_String* Toy_copyString(Toy_Bucket** bucket, Toy_String* str); +TOY_API Toy_String* Toy_deepCopyString(Toy_Bucket** bucket, Toy_String* str); + +TOY_API Toy_String* Toy_concatString(Toy_Bucket** bucket, Toy_String* left, Toy_String* right); + +TOY_API void Toy_freeString(Toy_String* str); + +TOY_API int Toy_getStringLength(Toy_String* str); +TOY_API int Toy_getStringRefCount(Toy_String* str); + +TOY_API char* Toy_getStringRawBuffer(Toy_String* str); //allocates the buffer on the heap, needs to be freed + +//TODO: compare diff --git a/source/toy_vm.h b/source/toy_vm.h index d96d08c..3f20da3 100644 --- a/source/toy_vm.h +++ b/source/toy_vm.h @@ -26,7 +26,8 @@ typedef struct Toy_VM { int routineCounter; - //scope - block-level key/value pairs + //heap - block-level key/value pairs + //TODO: needs string util for identifiers //stack - immediate-level values only Toy_Stack stack; diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index 9e15393..0d4da76 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -57,12 +57,12 @@ int test_sizeof_ast_32bit() { return -err; } -int test_type_emission(Toy_Bucket* bucket) { +int test_type_emission(Toy_Bucket** bucket) { //emit value { //emit to an AST Toy_Ast* ast = NULL; - Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42)); + Toy_private_emitAstValue(bucket, &ast, TOY_VALUE_TO_INTEGER(42)); //check if it worked if ( @@ -79,8 +79,8 @@ int test_type_emission(Toy_Bucket* bucket) { { //build the AST Toy_Ast* ast = NULL; - Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42)); - Toy_private_emitAstUnary(&bucket, &ast, TOY_AST_FLAG_NEGATE); + Toy_private_emitAstValue(bucket, &ast, TOY_VALUE_TO_INTEGER(42)); + Toy_private_emitAstUnary(bucket, &ast, TOY_AST_FLAG_NEGATE); //check if it worked if ( @@ -100,9 +100,9 @@ int test_type_emission(Toy_Bucket* bucket) { //build the AST Toy_Ast* ast = NULL; Toy_Ast* right = NULL; - Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42)); - Toy_private_emitAstValue(&bucket, &right, TOY_VALUE_TO_INTEGER(69)); - Toy_private_emitAstBinary(&bucket, &ast, TOY_AST_FLAG_ADD, right); + Toy_private_emitAstValue(bucket, &ast, TOY_VALUE_TO_INTEGER(42)); + Toy_private_emitAstValue(bucket, &right, TOY_VALUE_TO_INTEGER(69)); + Toy_private_emitAstBinary(bucket, &ast, TOY_AST_FLAG_ADD, right); //check if it worked if ( @@ -124,10 +124,10 @@ int test_type_emission(Toy_Bucket* bucket) { //build the AST Toy_Ast* ast = NULL; Toy_Ast* right = NULL; - Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42)); - Toy_private_emitAstValue(&bucket, &right, TOY_VALUE_TO_INTEGER(69)); - Toy_private_emitAstBinary(&bucket, &ast, TOY_AST_FLAG_ADD, right); - Toy_private_emitAstGroup(&bucket, &ast); + Toy_private_emitAstValue(bucket, &ast, TOY_VALUE_TO_INTEGER(42)); + Toy_private_emitAstValue(bucket, &right, TOY_VALUE_TO_INTEGER(69)); + Toy_private_emitAstBinary(bucket, &ast, TOY_AST_FLAG_ADD, right); + Toy_private_emitAstGroup(bucket, &ast); //check if it worked if ( @@ -150,19 +150,19 @@ int test_type_emission(Toy_Bucket* bucket) { { //initialize the root block Toy_Ast* block = NULL; - Toy_private_initAstBlock(&bucket, &block); + Toy_private_initAstBlock(bucket, &block); //loop over the ast emissions, appending each one as you go for (int i = 0; i < 5; i++) { //build the AST Toy_Ast* ast = NULL; Toy_Ast* right = NULL; - Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42)); - Toy_private_emitAstValue(&bucket, &right, TOY_VALUE_TO_INTEGER(69)); - Toy_private_emitAstBinary(&bucket, &ast, TOY_AST_FLAG_ADD, right); - Toy_private_emitAstGroup(&bucket, &ast); + Toy_private_emitAstValue(bucket, &ast, TOY_VALUE_TO_INTEGER(42)); + Toy_private_emitAstValue(bucket, &right, TOY_VALUE_TO_INTEGER(69)); + Toy_private_emitAstBinary(bucket, &ast, TOY_AST_FLAG_ADD, right); + Toy_private_emitAstGroup(bucket, &ast); - Toy_private_appendAstBlock(&bucket, &block, ast); + Toy_private_appendAstBlock(bucket, &block, ast); } //check if it worked @@ -218,7 +218,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_type_emission(bucket); + res = test_type_emission(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); diff --git a/tests/cases/test_bytecode.c b/tests/cases/test_bytecode.c index 51b3afc..ce69ad9 100644 --- a/tests/cases/test_bytecode.c +++ b/tests/cases/test_bytecode.c @@ -9,12 +9,12 @@ #include //tests -int test_bytecode_header(Toy_Bucket* bucket) { +int test_bytecode_header(Toy_Bucket** bucket) { //simple test to ensure the header looks right { //setup Toy_Ast* ast = NULL; - Toy_private_emitAstPass(&bucket, &ast); + Toy_private_emitAstPass(bucket, &ast); //run Toy_Bytecode bc = Toy_compileBytecode(ast); @@ -49,7 +49,7 @@ int test_bytecode_header(Toy_Bucket* bucket) { return 0; } -int test_bytecode_from_source(Toy_Bucket* bucket) { +int test_bytecode_from_source(Toy_Bucket** bucket) { { //setup const char* source = "(1 + 2) * (3 + 4);"; @@ -58,7 +58,7 @@ int test_bytecode_from_source(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run Toy_Bytecode bc = Toy_compileBytecode(ast); @@ -177,7 +177,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_bytecode_header(bucket); + res = test_bytecode_header(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); @@ -188,7 +188,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_bytecode_from_source(bucket); + res = test_bytecode_from_source(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); diff --git a/tests/cases/test_parser.c b/tests/cases/test_parser.c index 61578d2..3719897 100644 --- a/tests/cases/test_parser.c +++ b/tests/cases/test_parser.c @@ -15,7 +15,7 @@ Toy_Ast* makeAstFromSource(Toy_Bucket** bucket, const char* source) { } //tests -int test_simple_empty_parsers(Toy_Bucket* bucket) { +int test_simple_empty_parsers(Toy_Bucket** bucket) { //simple parser setup and cleanup { //raw source code and lexer @@ -27,7 +27,7 @@ int test_simple_empty_parsers(Toy_Bucket* bucket) { Toy_Parser parser; Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //check if it worked if ( @@ -50,7 +50,7 @@ int test_simple_empty_parsers(Toy_Bucket* bucket) { Toy_Parser parser; Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //check if it worked if ( @@ -75,7 +75,7 @@ int test_simple_empty_parsers(Toy_Bucket* bucket) { Toy_Parser parser; Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); Toy_Ast* iter = ast; @@ -98,10 +98,10 @@ int test_simple_empty_parsers(Toy_Bucket* bucket) { return 0; } -int test_values(Toy_Bucket* bucket) { +int test_values(Toy_Bucket** bucket) { //test boolean true { - Toy_Ast* ast = makeAstFromSource(&bucket, "true;"); + Toy_Ast* ast = makeAstFromSource(bucket, "true;"); //check if it worked if ( @@ -119,7 +119,7 @@ int test_values(Toy_Bucket* bucket) { //test boolean false (just to be safe) { - Toy_Ast* ast = makeAstFromSource(&bucket, "false;"); + Toy_Ast* ast = makeAstFromSource(bucket, "false;"); //check if it worked if ( @@ -137,7 +137,7 @@ int test_values(Toy_Bucket* bucket) { //test integer { - Toy_Ast* ast = makeAstFromSource(&bucket, "42;"); + Toy_Ast* ast = makeAstFromSource(bucket, "42;"); //check if it worked if ( @@ -155,7 +155,7 @@ int test_values(Toy_Bucket* bucket) { //test float { - Toy_Ast* ast = makeAstFromSource(&bucket, "3.1415;"); + Toy_Ast* ast = makeAstFromSource(bucket, "3.1415;"); //check if it worked if ( @@ -173,7 +173,7 @@ int test_values(Toy_Bucket* bucket) { //test integer with separators { - Toy_Ast* ast = makeAstFromSource(&bucket, "1_234_567_890;"); + Toy_Ast* ast = makeAstFromSource(bucket, "1_234_567_890;"); //check if it worked if ( @@ -191,7 +191,7 @@ int test_values(Toy_Bucket* bucket) { //test float with separators { - Toy_Ast* ast = makeAstFromSource(&bucket, "3.141_592_65;"); + Toy_Ast* ast = makeAstFromSource(bucket, "3.141_592_65;"); //check if it worked if ( @@ -210,10 +210,10 @@ int test_values(Toy_Bucket* bucket) { return 0; } -int test_unary(Toy_Bucket* bucket) { +int test_unary(Toy_Bucket** bucket) { //test unary boolean negation (!true) { - Toy_Ast* ast = makeAstFromSource(&bucket, "!true;"); + Toy_Ast* ast = makeAstFromSource(bucket, "!true;"); //check if it worked if ( @@ -231,7 +231,7 @@ int test_unary(Toy_Bucket* bucket) { //test unary boolean negation (!false, just to be safe) { - Toy_Ast* ast = makeAstFromSource(&bucket, "!false;"); + Toy_Ast* ast = makeAstFromSource(bucket, "!false;"); //check if it worked if ( @@ -249,7 +249,7 @@ int test_unary(Toy_Bucket* bucket) { //test unary integer negation { - Toy_Ast* ast = makeAstFromSource(&bucket, "-42;"); + Toy_Ast* ast = makeAstFromSource(bucket, "-42;"); //check if it worked if ( @@ -267,7 +267,7 @@ int test_unary(Toy_Bucket* bucket) { //test unary float negation { - Toy_Ast* ast = makeAstFromSource(&bucket, "-3.1415;"); + Toy_Ast* ast = makeAstFromSource(bucket, "-3.1415;"); //check if it worked if ( @@ -285,7 +285,7 @@ int test_unary(Toy_Bucket* bucket) { //ensure unary negation doesn't occur with a group { - Toy_Ast* ast = makeAstFromSource(&bucket, "-(42);"); + Toy_Ast* ast = makeAstFromSource(bucket, "-(42);"); //check if it worked if ( @@ -301,7 +301,7 @@ int test_unary(Toy_Bucket* bucket) { //ensure unary negation doesn't occur with a space { - Toy_Ast* ast = makeAstFromSource(&bucket, "- 42;"); + Toy_Ast* ast = makeAstFromSource(bucket, "- 42;"); //check if it worked if ( @@ -318,10 +318,10 @@ int test_unary(Toy_Bucket* bucket) { return 0; } -int test_binary(Toy_Bucket* bucket) { +int test_binary(Toy_Bucket** bucket) { //test binary add (term); also covers subtract { - Toy_Ast* ast = makeAstFromSource(&bucket, "1 + 2;"); + Toy_Ast* ast = makeAstFromSource(bucket, "1 + 2;"); //check if it worked if ( @@ -348,7 +348,7 @@ int test_binary(Toy_Bucket* bucket) { //test binary multiply (factor); also covers divide and modulo { - Toy_Ast* ast = makeAstFromSource(&bucket, "3 * 5;"); + Toy_Ast* ast = makeAstFromSource(bucket, "3 * 5;"); //check if it worked if ( @@ -375,7 +375,7 @@ int test_binary(Toy_Bucket* bucket) { //test binary assign (using numbers for now, as identifiers aren't coded yet) { - Toy_Ast* ast = makeAstFromSource(&bucket, "1 = 2;"); + Toy_Ast* ast = makeAstFromSource(bucket, "1 = 2;"); //check if it worked if ( @@ -402,7 +402,7 @@ int test_binary(Toy_Bucket* bucket) { //test binary compare (equality) { - Toy_Ast* ast = makeAstFromSource(&bucket, "42 == 69;"); + Toy_Ast* ast = makeAstFromSource(bucket, "42 == 69;"); //check if it worked if ( @@ -430,10 +430,10 @@ int test_binary(Toy_Bucket* bucket) { return 0; } -int test_precedence(Toy_Bucket* bucket) { +int test_precedence(Toy_Bucket** bucket) { //test term-factor precedence { - Toy_Ast* ast = makeAstFromSource(&bucket, "1 * 2 + 3 * 4;"); + Toy_Ast* ast = makeAstFromSource(bucket, "1 * 2 + 3 * 4;"); //check if it worked if ( @@ -474,7 +474,7 @@ int test_precedence(Toy_Bucket* bucket) { //test left-recrusive precedence { - Toy_Ast* ast = makeAstFromSource(&bucket, "1 + 2 + 3 + 4 + 5 + 6;"); + Toy_Ast* ast = makeAstFromSource(bucket, "1 + 2 + 3 + 4 + 5 + 6;"); //check if it worked if ( @@ -531,7 +531,7 @@ int test_precedence(Toy_Bucket* bucket) { //test group precedence { - Toy_Ast* ast = makeAstFromSource(&bucket, "(1 + 2) * (3 + 4);"); + Toy_Ast* ast = makeAstFromSource(bucket, "(1 + 2) * (3 + 4);"); //check if it worked if ( @@ -580,7 +580,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_simple_empty_parsers(bucket); + res = test_simple_empty_parsers(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); @@ -591,7 +591,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_values(bucket); + res = test_values(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); @@ -602,7 +602,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_unary(bucket); + res = test_unary(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); @@ -613,7 +613,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_binary(bucket); + res = test_binary(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); @@ -624,7 +624,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_precedence(bucket); + res = test_precedence(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); diff --git a/tests/cases/test_routine.c b/tests/cases/test_routine.c index 848455c..9a5d486 100644 --- a/tests/cases/test_routine.c +++ b/tests/cases/test_routine.c @@ -9,12 +9,12 @@ #include //tests -int test_routine_header_and_values(Toy_Bucket* bucket) { +int test_routine_header_and_values(Toy_Bucket** bucket) { //simple test to ensure the header looks right with an empty ast { //setup Toy_Ast* ast = NULL; - Toy_private_emitAstPass(&bucket, &ast); + Toy_private_emitAstPass(bucket, &ast); //run void* buffer = Toy_compileRoutine(ast); @@ -63,7 +63,7 @@ int test_routine_header_and_values(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -112,7 +112,7 @@ int test_routine_header_and_values(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -165,7 +165,7 @@ int test_routine_header_and_values(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -218,7 +218,7 @@ int test_routine_header_and_values(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -272,7 +272,7 @@ int test_routine_header_and_values(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -320,11 +320,11 @@ int test_routine_header_and_values(Toy_Bucket* bucket) { return 0; } -// int test_routine_unary(Toy_Bucket* bucket) { +// int test_routine_unary(Toy_Bucket** bucket) { // //TODO: Nothing produces a unary instruction yet // } -int test_routine_binary(Toy_Bucket* bucket) { +int test_routine_binary(Toy_Bucket** bucket) { //produce a simple algorithm { //setup @@ -334,7 +334,7 @@ int test_routine_binary(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -400,7 +400,7 @@ int test_routine_binary(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -466,7 +466,7 @@ int test_routine_binary(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -537,7 +537,7 @@ int test_routine_binary(Toy_Bucket* bucket) { Toy_bindLexer(&lexer, source); Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); //run void* buffer = Toy_compileRoutine(ast); @@ -630,7 +630,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_routine_header_and_values(bucket); + res = test_routine_header_and_values(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); @@ -641,7 +641,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_routine_binary(bucket); + res = test_routine_binary(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); diff --git a/tests/cases/test_string.c b/tests/cases/test_string.c new file mode 100644 index 0000000..2988447 --- /dev/null +++ b/tests/cases/test_string.c @@ -0,0 +1,316 @@ +#include "toy_string.h" +#include "toy_console_colors.h" + +#include "toy_memory.h" + +#include +#include + +int test_sizeof_string_64bit() { + //test for the correct size + { + if (sizeof(Toy_String) != 28) { + fprintf(stderr, TOY_CC_ERROR "ERROR: 'Toy_String' is an unexpected size in memory\n" TOY_CC_RESET); + return -1; + } + } + + return 0; +} + +int test_sizeof_string_32bit() { + //test for the correct size + { + if (sizeof(Toy_String) != 20) { + fprintf(stderr, TOY_CC_ERROR "ERROR: 'Toy_String' is an unexpected size in memory\n" TOY_CC_RESET); + return -1; + } + } + + return 0; +} + +int test_string_allocation() { + //allocate a single string from a c-string + { + //setup + Toy_Bucket* bucket = NULL; + Toy_initBucket(&bucket, 32); + + const char* cstring = "Hello world"; + Toy_String* str = Toy_createString(&bucket, cstring); + + //check + if (str->type != TOY_STRING_LEAF || + str->length != 11 || + str->refCount != 1 || + strcmp(str->as.leaf.data, "Hello world") != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a Toy_String with a private bucket\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + //free the string + Toy_freeString(str); + + //inspect the bucket + if (bucket->capacity != 32 || + bucket->count != sizeof(Toy_String) + 12 || + bucket->next != NULL) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected bucket state after a string was freed\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + //inspect the (now freed) string's memory + if (Toy_getStringRefCount(str) != 0) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected string state after it was freed\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + Toy_freeBucket(&bucket); + } + + //copy and deep copy a string + { + //setup + Toy_Bucket* bucket = NULL; + Toy_initBucket(&bucket, 32); + + const char* cstring = "Hello world"; + Toy_String* str = Toy_createString(&bucket, cstring); + + //shallow and deep + Toy_String* shallow = Toy_copyString(&bucket, str); + Toy_String* deep = Toy_deepCopyString(&bucket, str); + + if (str != shallow || + str == deep || + shallow->refCount != 2 || + deep->refCount != 1 || + strcmp(shallow->as.leaf.data, deep->as.leaf.data) != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to copy a string correctly\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + Toy_freeBucket(&bucket); + } + + //allocate a zero-length string + { + //setup + Toy_Bucket* bucket = NULL; + Toy_initBucket(&bucket, 32); + + const char* cstring = ""; + Toy_String* str = Toy_createString(&bucket, cstring); + + //check + if (str->type != TOY_STRING_LEAF || + str->length != 0 || + str->refCount != 1 || + strcmp(str->as.leaf.data, "") != 0 || + bucket->count != sizeof(Toy_String) + 1) //1 for the null character + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a Toy_String with zero length\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + //free the string + Toy_freeString(str); + + Toy_freeBucket(&bucket); + } + + return 0; +} + +int test_string_concatenation() { + //one big bucket o' fun + Toy_Bucket* bucket = NULL; + Toy_initBucket(&bucket, 1024); + + //concatenate two strings, and check the refcounts + { + //setup + Toy_String* first = Toy_createString(&bucket, "Hello "); + Toy_String* second = Toy_createString(&bucket, "world"); + + //concatenate + Toy_String* result = Toy_concatString(&bucket, first, second); + + //check the refcounts + if (first->refCount != 2 || + second->refCount != 2 || + result->refCount != 1 || + result->length != 11) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state for string refcounts after concatenation\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + //clean up the separate strings + Toy_freeString(first); + Toy_freeString(second); + + //check the refcounts again + if (first->refCount != 1 || + second->refCount != 1 || + result->refCount != 1 || + result->length != 11) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state for string refcounts after concatenation and free\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + //clean up + Toy_freeString(result); + } + + //concatenate two strings, and check the resulting buffer + { + //setup + Toy_String* first = Toy_createString(&bucket, "Hello "); + Toy_String* second = Toy_createString(&bucket, "world"); + + //concatenate + Toy_String* result = Toy_concatString(&bucket, first, second); + + char* buffer = Toy_getStringRawBuffer(result); + + //check the refcounts + if (strlen(buffer) != 11 || + strcmp(buffer, "Hello world") != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to get the raw buffer from concatenated string\n" TOY_CC_RESET); + TOY_FREE_ARRAY(char, buffer, result->length + 1); + Toy_freeBucket(&bucket); + return -1; + } + + TOY_FREE_ARRAY(char, buffer, result->length + 1); + Toy_freeString(result); + Toy_freeString(first); + Toy_freeString(second); + } + + Toy_freeBucket(&bucket); + return 0; +} + +int test_string_with_stressed_bucket() { + //how much is that dog in the window? + { + //test data: 36 characters total, 44 with spaces + char* testData[] = { + "the", + "quick", + "brown", + "fox", + "jumped", //longest word: 6 characters + "over", + "the", + "lazy", + "dog", //9 entries long + NULL, + }; + + //setup + Toy_Bucket* bucket = NULL; + Toy_initBucket(&bucket, 128); //deliberately too short for one bucket + + //stress + Toy_String* str = Toy_createString(&bucket, testData[0]); + Toy_String* ptr = str; + for (int i = 1; testData[i]; i++) { + str = Toy_concatString(&bucket, str, Toy_createString(&bucket, testData[i])); + } + + //check + if (ptr->refCount != 9 || + str->length != 36) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state of the string after stress test\n" TOY_CC_RESET); + Toy_freeBucket(&bucket); + return -1; + } + + //grab the buffer + char* buffer = Toy_getStringRawBuffer(str); + + if (strcmp(buffer, "thequickbrownfoxjumpedoverthelazydog") != 0 || + strlen(buffer) != 36) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state of the raw buffer after string stress test: '%s'\n" TOY_CC_RESET, buffer); + Toy_reallocate(buffer, 0, 0); //direct call to free, regardless of size + Toy_freeBucket(&bucket); + return -1; + } + + if (bucket->next == NULL) //just to make sure + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected state of the bucket after string stress test\n" TOY_CC_RESET); + Toy_reallocate(buffer, 0, 0); //direct call to free, regardless of size + Toy_freeBucket(&bucket); + return -1; + } + + //clean up + TOY_FREE_ARRAY(char, buffer, str->length); + Toy_freeBucket(&bucket); + } + + // + return 0; +} + +int main() { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { +#if TOY_BITNESS == 64 + res = test_sizeof_string_64bit(); +#else + res = test_sizeof_string_32bit(); +#endif + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_string_allocation(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_string_concatenation(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_string_with_stressed_bucket(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + return total; +} \ No newline at end of file diff --git a/tests/cases/test_vm.c b/tests/cases/test_vm.c index bb3e470..82c728b 100644 --- a/tests/cases/test_vm.c +++ b/tests/cases/test_vm.c @@ -23,7 +23,7 @@ Toy_Bytecode makeBytecodeFromSource(Toy_Bucket** bucket, const char* source) { } //tests -int test_setup_and_teardown(Toy_Bucket* bucket) { +int test_setup_and_teardown(Toy_Bucket** bucket) { //basic init & quit { //generate bytecode for testing @@ -35,7 +35,7 @@ int test_setup_and_teardown(Toy_Bucket* bucket) { Toy_Parser parser; Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); @@ -75,7 +75,7 @@ int test_setup_and_teardown(Toy_Bucket* bucket) { } //tests -int test_simple_execution(Toy_Bucket* bucket) { +int test_simple_execution(Toy_Bucket** bucket) { //basic init & quit { //generate bytecode for testing @@ -87,7 +87,7 @@ int test_simple_execution(Toy_Bucket* bucket) { Toy_Parser parser; Toy_bindParser(&parser, &lexer); - Toy_Ast* ast = Toy_scanParser(&bucket, &parser); + Toy_Ast* ast = Toy_scanParser(bucket, &parser); Toy_Bytecode bc = Toy_compileBytecode(ast); @@ -126,7 +126,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_setup_and_teardown(bucket); + res = test_setup_and_teardown(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); @@ -137,7 +137,7 @@ int main() { { Toy_Bucket* bucket = NULL; TOY_BUCKET_INIT(Toy_Ast, bucket, 32); - res = test_simple_execution(bucket); + res = test_simple_execution(&bucket); TOY_BUCKET_FREE(bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);