Implemented and tested Toy_String, read more

Strings are needed for the handling of identifiers in the key/value
variable storage, so I've got them working first. I used the rope
pattern, which seems to be quite an interesting approach.

I'll add comparison checks later.

Adjusted how buckets are handled in all tests, could've been an issue
down the line.

Added the build instructions to README.md.
This commit is contained in:
2024-09-30 15:22:00 +10:00
parent c1f2e19e55
commit 8d6bdb88b4
11 changed files with 580 additions and 80 deletions

View File

@@ -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);

View File

@@ -9,12 +9,12 @@
#include <string.h>
//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);

View File

@@ -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);

View File

@@ -9,12 +9,12 @@
#include <string.h>
//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);

316
tests/cases/test_string.c Normal file
View File

@@ -0,0 +1,316 @@
#include "toy_string.h"
#include "toy_console_colors.h"
#include "toy_memory.h"
#include <stdio.h>
#include <string.h>
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;
}

View File

@@ -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);