From f705d82aee95595421de8eead2c89e9e83a1e64b Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 28 Aug 2022 09:35:46 +0100 Subject: [PATCH] Wrote some small tests --- source/compiler.c | 6 +- source/interpreter.c | 24 +- source/interpreter.h | 2 + source/literal.c | 489 +---------------------------------- source/literal.h | 18 +- source/literal_array.c | 4 +- source/literal_array.h | 1 + source/literal_dictionary.c | 20 +- source/literal_dictionary.h | 1 + source/literal_util.c | 498 ++++++++++++++++++++++++++++++++++++ source/literal_util.h | 10 + source/memory.c | 2 +- source/node.h | 1 + source/parser.c | 2 +- test/makefile | 6 +- test/test_literal.c | 50 ++++ 16 files changed, 605 insertions(+), 529 deletions(-) create mode 100644 source/literal_util.c create mode 100644 source/literal_util.h create mode 100644 test/test_literal.c diff --git a/source/compiler.c b/source/compiler.c index ac0fd9f..8f0b2e9 100644 --- a/source/compiler.c +++ b/source/compiler.c @@ -760,7 +760,7 @@ static unsigned char* collateCompilerHeaderOpt(Compiler* compiler, int* size, bo emitByte(&collation, &capacity, &count, TOY_VERSION_PATCH); //embed the build info - if (strlen(TOY_VERSION_BUILD) + count + 1 > capacity) { + if ((int)strlen(TOY_VERSION_BUILD) + count + 1 > capacity) { int oldCapacity = capacity; capacity = strlen(TOY_VERSION_BUILD) + count + 1; //full header size collation = GROW_ARRAY(unsigned char, collation, oldCapacity, capacity); @@ -808,7 +808,7 @@ static unsigned char* collateCompilerHeaderOpt(Compiler* compiler, int* size, bo Literal str = compiler->literalCache.literals[i]; - for (int c = 0; c < STRLEN(str); c++) { + for (int c = 0; c < (int)strlen(AS_STRING(str)); c++) { emitByte(&collation, &capacity, &count, AS_STRING(str)[c]); } @@ -885,7 +885,7 @@ static unsigned char* collateCompilerHeaderOpt(Compiler* compiler, int* size, bo Literal identifier = compiler->literalCache.literals[i]; - for (int c = 0; c < STRLEN_I(identifier); c++) { + for (int c = 0; c < (int)strlen(AS_IDENTIFIER(identifier)); c++) { emitByte(&collation, &capacity, &count, AS_IDENTIFIER(identifier)[c]); } diff --git a/source/interpreter.c b/source/interpreter.c index f7c4ecd..291100e 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -26,7 +26,7 @@ bool injectNativeFn(Interpreter* interpreter, char* name, NativeFn func) { return false; } - Literal identifier = TO_IDENTIFIER_LITERAL(name); + Literal identifier = TO_IDENTIFIER_LITERAL(copyString(name, strlen(name)), strlen(name)); //make sure the name isn't taken if (existsLiteralDictionary(&interpreter->scope->variables, identifier)) { @@ -99,10 +99,10 @@ int _set(Interpreter* interpreter, LiteralArray* arguments) { //if it's a string or an identifier, make a local copy if (IS_STRING(val)) { - val = TO_STRING_LITERAL(copyString(AS_STRING(val), STRLEN(val))); + val = TO_STRING_LITERAL(copyString(AS_STRING(val), strlen(AS_STRING(val)) ), strlen(AS_STRING(val))); } if (IS_IDENTIFIER(val)) { - val = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(val), STRLEN_I(val))); + val = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(val), strlen(AS_IDENTIFIER(val)) ), strlen(AS_STRING(val))); } //TODO: proper copy function for literals @@ -266,7 +266,7 @@ int _length(Interpreter* interpreter, LiteralArray* arguments) { return 1; case LITERAL_STRING: - pushLiteralArray(&interpreter->stack, TO_INTEGER_LITERAL( STRLEN(obj) )); + pushLiteralArray(&interpreter->stack, TO_INTEGER_LITERAL( strlen(AS_STRING(obj)) )); return 1; default: @@ -535,7 +535,7 @@ static bool execArithmetic(Interpreter* interpreter, Opcode opcode) { //special case for string concatenation ONLY if (IS_STRING(lhs) && IS_STRING(rhs)) { //check for overflow - if (STRLEN(lhs) + STRLEN(rhs) > MAX_STRING_LENGTH) { + if (strlen(AS_STRING(lhs)) + strlen(AS_STRING(rhs)) > MAX_STRING_LENGTH) { printf(ERROR "ERROR: Can't concatenate these strings (result is too long)\n" RESET); return false; } @@ -543,7 +543,7 @@ static bool execArithmetic(Interpreter* interpreter, Opcode opcode) { //concat the strings char buffer[MAX_STRING_LENGTH]; snprintf(buffer, MAX_STRING_LENGTH, "%s%s", AS_STRING(lhs), AS_STRING(rhs)); - pushLiteralArray(&interpreter->stack, TO_STRING_LITERAL(buffer)); + pushLiteralArray(&interpreter->stack, TO_STRING_LITERAL( copyString(buffer, strlen(buffer)), strlen(buffer) )); return true; } @@ -823,19 +823,21 @@ static bool execValCast(Interpreter* interpreter) { case LITERAL_STRING: if (IS_BOOLEAN(value)) { - result = TO_STRING_LITERAL(AS_BOOLEAN(value) ? "true" : "false"); + char* str = AS_BOOLEAN(value) ? "true" : "false"; + + result = TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str)); } if (IS_INTEGER(value)) { char buffer[128]; snprintf(buffer, 128, "%d", AS_INTEGER(value)); - result = TO_STRING_LITERAL(buffer); + result = TO_STRING_LITERAL(copyString(buffer, strlen(buffer)), strlen(buffer)); } if (IS_FLOAT(value)) { char buffer[128]; snprintf(buffer, 128, "%g", AS_FLOAT(value)); - result = TO_STRING_LITERAL(buffer); + result = TO_STRING_LITERAL(copyString(buffer, strlen(buffer)), strlen(buffer)); } break; @@ -1496,7 +1498,7 @@ static void readInterpreterSections(Interpreter* interpreter) { case LITERAL_STRING: { char* s = readString(interpreter->bytecode, &interpreter->count); - pushLiteralArray(&interpreter->literalCache, TO_STRING_LITERAL(s)); + pushLiteralArray(&interpreter->literalCache, TO_STRING_LITERAL( copyString(s, strlen(s)), strlen(s) )); if (command.verbose) { printf("(string \"%s\")\n", s); @@ -1571,7 +1573,7 @@ static void readInterpreterSections(Interpreter* interpreter) { case LITERAL_IDENTIFIER: { char* str = readString(interpreter->bytecode, &interpreter->count); - Literal identifier = TO_IDENTIFIER_LITERAL(str); + Literal identifier = TO_IDENTIFIER_LITERAL(copyString(str, strlen(str)), strlen(str)); pushLiteralArray(&interpreter->literalCache, identifier); diff --git a/source/interpreter.h b/source/interpreter.h index 8fb76e0..5e476c6 100644 --- a/source/interpreter.h +++ b/source/interpreter.h @@ -2,6 +2,8 @@ #include "opcodes.h" +#include "literal.h" +#include "literal_util.h" #include "literal_array.h" #include "literal_dictionary.h" #include "scope.h" diff --git a/source/literal.c b/source/literal.c index bf8b7fd..5600d33 100644 --- a/source/literal.c +++ b/source/literal.c @@ -1,38 +1,9 @@ #include "literal.h" #include "memory.h" -#include "literal_array.h" -#include "literal_dictionary.h" - #include "console_colors.h" #include -#include - -//utils -static void stdoutWrapper(const char* output) { - fprintf(stdout, "%s", output); -} - -//buffer the prints -static char* globalPrintBuffer = NULL; -static size_t globalPrintCapacity = 0; -static size_t globalPrintCount = 0; - -//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else -static char quotes = 0; //set to 0 to not show string quotes - -static void printToBuffer(const char* str) { - while (strlen(str) + globalPrintCount > globalPrintCapacity) { - int oldCapacity = globalPrintCapacity; - - globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity); - globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity); - } - - snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str); - globalPrintCount += strlen(str); -} //hash util functions static unsigned int hashString(const char* string, int length) { @@ -54,281 +25,14 @@ static unsigned int hash(unsigned int x) { } //exposed functions -void printLiteral(Literal literal) { - printLiteralCustom(literal, stdoutWrapper); -} - -void printLiteralCustom(Literal literal, void (printFn)(const char*)) { - switch(literal.type) { - case LITERAL_NULL: - printFn("null"); - break; - - case LITERAL_BOOLEAN: - printFn(AS_BOOLEAN(literal) ? "true" : "false"); - break; - - case LITERAL_INTEGER: { - char buffer[256]; - snprintf(buffer, 256, "%d", AS_INTEGER(literal)); - printFn(buffer); - } - break; - - case LITERAL_FLOAT: { - char buffer[256]; - snprintf(buffer, 256, "%g", AS_FLOAT(literal)); - printFn(buffer); - } - break; - - case LITERAL_STRING: { - char buffer[MAX_STRING_LENGTH]; - if (!quotes) { - snprintf(buffer, MAX_STRING_LENGTH, "%.*s", STRLEN(literal), AS_STRING(literal)); - } - else { - snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, STRLEN(literal), AS_STRING(literal), quotes); - } - printFn(buffer); - } - break; - - case LITERAL_ARRAY: { - LiteralArray* ptr = AS_ARRAY(literal); - - //hold potential parent-call buffers on the C stack - char* cacheBuffer = globalPrintBuffer; - globalPrintBuffer = NULL; - int cacheCapacity = globalPrintCapacity; - globalPrintCapacity = 0; - int cacheCount = globalPrintCount; - globalPrintCount = 0; - - //print the contents to the global buffer - printToBuffer("["); - for (int i = 0; i < ptr->count; i++) { - quotes = '"'; - printLiteralCustom(ptr->literals[i], printToBuffer); - - if (i + 1 < ptr->count) { - printToBuffer(","); - } - } - printToBuffer("]"); - - //swap the parent-call buffer back into place - char* printBuffer = globalPrintBuffer; - int printCapacity = globalPrintCapacity; - int printCount = globalPrintCount; - - globalPrintBuffer = cacheBuffer; - globalPrintCapacity = cacheCapacity; - globalPrintCount = cacheCount; - - //finally, output and cleanup - printFn(printBuffer); - FREE_ARRAY(char, printBuffer, printCapacity); - quotes = 0; - } - break; - - case LITERAL_DICTIONARY: { - LiteralDictionary* ptr = AS_DICTIONARY(literal); - - //hold potential parent-call buffers on the C stack - char* cacheBuffer = globalPrintBuffer; - globalPrintBuffer = NULL; - int cacheCapacity = globalPrintCapacity; - globalPrintCapacity = 0; - int cacheCount = globalPrintCount; - globalPrintCount = 0; - - //print the contents to the global buffer - int delimCount = 0; - printToBuffer("["); - for (int i = 0; i < ptr->capacity; i++) { - if (IS_NULL(ptr->entries[i].key)) { - continue; - } - - if (delimCount++ > 0) { - printToBuffer(","); - } - - quotes = '"'; - printLiteralCustom(ptr->entries[i].key, printToBuffer); - printToBuffer(":"); - quotes = '"'; - printLiteralCustom(ptr->entries[i].value, printToBuffer); - } - - //empty dicts MUST have a ":" printed - if (ptr->count == 0) { - printToBuffer(":"); - } - - printToBuffer("]"); - - //swap the parent-call buffer back into place - char* printBuffer = globalPrintBuffer; - int printCapacity = globalPrintCapacity; - int printCount = globalPrintCount; - - globalPrintBuffer = cacheBuffer; - globalPrintCapacity = cacheCapacity; - globalPrintCount = cacheCount; - - //finally, output and cleanup - printFn(printBuffer); - FREE_ARRAY(char, printBuffer, printCapacity); - quotes = 0; - } - break; - - //TODO: functions - case LITERAL_FUNCTION: - case LITERAL_FUNCTION_NATIVE: - printFn("(function)"); - break; - - case LITERAL_IDENTIFIER: { - char buffer[256]; - snprintf(buffer, 256, "%.*s", STRLEN_I(literal), AS_IDENTIFIER(literal)); - printFn(buffer); - } - break; - - case LITERAL_TYPE: { - //hold potential parent-call buffers on the C stack - char* cacheBuffer = globalPrintBuffer; - globalPrintBuffer = NULL; - int cacheCapacity = globalPrintCapacity; - globalPrintCapacity = 0; - int cacheCount = globalPrintCount; - globalPrintCount = 0; - - //print the type correctly - printToBuffer("<"); - - switch(AS_TYPE(literal).typeOf) { - case LITERAL_NULL: - printToBuffer("null"); - break; - - case LITERAL_BOOLEAN: - printToBuffer("bool"); - break; - - case LITERAL_INTEGER: - printToBuffer("int"); - break; - - case LITERAL_FLOAT: - printToBuffer("float"); - break; - - case LITERAL_STRING: - printToBuffer("string"); - break; - - case LITERAL_ARRAY: - //print all in the array - printToBuffer("["); - for (int i = 0; i < AS_TYPE(literal).count; i++) { - printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer); - } - printToBuffer("]"); - break; - - case LITERAL_DICTIONARY: - printToBuffer("["); - - for (int i = 0; i < AS_TYPE(literal).count; i += 2) { - printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer); - printToBuffer(":"); - printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i + 1], printToBuffer); - } - printToBuffer("]"); - break; - - case LITERAL_FUNCTION: - printToBuffer("function"); - //TODO: how to print a function - break; - - case LITERAL_IDENTIFIER: - printToBuffer("identifier"); - break; - - case LITERAL_TYPE: - printToBuffer("type"); - break; - - case LITERAL_ANY: - printToBuffer("any"); - break; - - default: - //should never be seen - fprintf(stderr, ERROR "[internal] Unrecognized literal type in print type: %d\n" RESET, AS_TYPE(literal).typeOf); - } - - //const (printed last) - if (AS_TYPE(literal).constant) { - printToBuffer(" const"); - } - - printToBuffer(">"); - - //swap the parent-call buffer back into place - char* printBuffer = globalPrintBuffer; - int printCapacity = globalPrintCapacity; - int printCount = globalPrintCount; - - globalPrintBuffer = cacheBuffer; - globalPrintCapacity = cacheCapacity; - globalPrintCount = cacheCount; - - //finally, output and cleanup - printFn(printBuffer); - FREE_ARRAY(char, printBuffer, printCapacity); - quotes = 0; - } - break; - - case LITERAL_TYPE_INTERMEDIATE: - case LITERAL_FUNCTION_INTERMEDIATE: - printFn("Unprintable literal found"); - break; - - case LITERAL_ANY: - printFn("(any)"); - break; - - default: - //should never be seen - fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type); - } -} - void freeLiteral(Literal literal) { if (IS_STRING(literal)) { - FREE_ARRAY(char, AS_STRING(literal), STRLEN(literal) + 1); + FREE_ARRAY(char, AS_STRING(literal), literal.as.string.length); return; } - //TODO: are these needed? I wish i had valgrind - // if (IS_ARRAY(literal)) { - // freeLiteralArray(((LiteralArray*)(AS_ARRAY(literal)))); - // } - - // if (IS_DICTIONARY(literal)) { - // freeLiteralDictionary(((LiteralDictionary*)(AS_DICTIONARY(literal)))); - // } - if (IS_IDENTIFIER(literal)) { - FREE_ARRAY(char, AS_IDENTIFIER(literal), STRLEN_I(literal) + 1); + FREE_ARRAY(char, AS_IDENTIFIER(literal), literal.as.identifier.length); return; } @@ -353,8 +57,8 @@ bool _isTruthy(Literal x) { return true; } -Literal _toStringLiteral(char* str) { - return ((Literal){LITERAL_STRING, { .string.ptr = (char*)str, .string.length = strlen((char*)str) }}); +Literal _toStringLiteral(char* str, int length) { + return ((Literal){LITERAL_STRING, { .string.ptr = (char*)str, .string.length = length }}); } Literal _toIdentifierLiteral(char* str, int length) { @@ -375,188 +79,3 @@ Literal* _typePushSubtype(Literal* lit, Literal subtype) { return &((Literal*)(AS_TYPE(*lit).subtypes))[ AS_TYPE(*lit).count - 1 ]; } -char* copyString(char* original, int length) { - //make a local copy of the char array - char* buffer = ALLOCATE(char, length + 1); - strncpy(buffer, original, length); - buffer[length] = '\0'; - return buffer; -} - -bool literalsAreEqual(Literal lhs, Literal rhs) { - //utility for other things - if (lhs.type != rhs.type) { - // ints and floats are compatible - if ((IS_INTEGER(lhs) || IS_FLOAT(lhs)) && (IS_INTEGER(rhs) || IS_FLOAT(rhs))) { - if (IS_INTEGER(lhs)) { - return AS_INTEGER(lhs) + AS_FLOAT(rhs); - } - else { - return AS_FLOAT(lhs) + AS_INTEGER(rhs); - } - } - - return false; - } - - switch(lhs.type) { - case LITERAL_NULL: - return true; //can only be true because of the check above - - case LITERAL_BOOLEAN: - return AS_BOOLEAN(lhs) == AS_BOOLEAN(rhs); - - case LITERAL_INTEGER: - return AS_INTEGER(lhs) == AS_INTEGER(rhs); - - case LITERAL_FLOAT: - return AS_FLOAT(lhs) == AS_FLOAT(rhs); - - case LITERAL_STRING: - if (STRLEN(lhs) != STRLEN(rhs)) { - return false; - } - return !strncmp(AS_STRING(lhs), AS_STRING(rhs), STRLEN(lhs)); - - case LITERAL_ARRAY: - case LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array - //mismatched sizes - if (AS_ARRAY(lhs)->count != AS_ARRAY(rhs)->count) { - return false; - } - - //mismatched elements (in order) - for (int i = 0; i < AS_ARRAY(lhs)->count; i++) { - if (!literalsAreEqual( AS_ARRAY(lhs)->literals[i], AS_ARRAY(rhs)->literals[i] )) { - return false; - } - } - return true; - - case LITERAL_DICTIONARY: - //relatively slow, especially when nested - for (int i = 0; i < AS_DICTIONARY(lhs)->capacity; i++) { - if (!IS_NULL(AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys - //check it exists in rhs - if (!existsLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key)) { - return false; - } - - //compare the values - if (!literalsAreEqual(AS_DICTIONARY(lhs)->entries[i].value, getLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key) )) { - return false; - } - } - } - - return true; - - case LITERAL_FUNCTION: - case LITERAL_FUNCTION_NATIVE: - return false; //functions are never equal - break; - - case LITERAL_IDENTIFIER: - //check shortcuts - if (HASH_I(lhs) != HASH_I(rhs) && STRLEN_I(lhs) != STRLEN_I(rhs)) { - return false; - } - - return !strncmp(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs), STRLEN_I(lhs)); - - case LITERAL_TYPE: - //check types - if (AS_TYPE(lhs).typeOf != AS_TYPE(rhs).typeOf) { - return false; - } - - //const don't match - if (AS_TYPE(lhs).constant != AS_TYPE(rhs).constant) { - return false; - } - - //check subtypes - if (AS_TYPE(lhs).count != AS_TYPE(rhs).count) { - return false; - } - - //check array|dictionary signatures are the same (in order) - if (AS_TYPE(lhs).typeOf == LITERAL_ARRAY || AS_TYPE(lhs).typeOf == LITERAL_DICTIONARY) { - for (int i = 0; i < AS_TYPE(lhs).count; i++) { - if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[i])) { - return false; - } - } - } - return true; - - case LITERAL_ANY: - return true; - - case LITERAL_FUNCTION_INTERMEDIATE: - fprintf(stderr, ERROR "[internal] Can't compare intermediate functions\n" RESET); - return false; - - default: - //should never be seen - fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type); - return false; - } - - return false; -} - -int hashLiteral(Literal lit) { - switch(lit.type) { - case LITERAL_NULL: - return 0; - - case LITERAL_BOOLEAN: - return AS_BOOLEAN(lit) ? 1 : 0; - - case LITERAL_INTEGER: - return hash((unsigned int)AS_INTEGER(lit)); - - case LITERAL_FLOAT: - return hash(*(unsigned int*)(&AS_FLOAT(lit))); - - case LITERAL_STRING: - return hashString(AS_STRING(lit), STRLEN(lit)); - - case LITERAL_ARRAY: { - unsigned int res = 0; - for (int i = 0; i < AS_DICTIONARY(lit)->count; i++) { - res += hashLiteral(AS_ARRAY(lit)->literals[i]); - } - return hash(res); - } - - case LITERAL_DICTIONARY: { - unsigned int res = 0; - for (int i = 0; i < AS_DICTIONARY(lit)->capacity; i++) { - if (!IS_NULL(AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys - res += hashLiteral(AS_DICTIONARY(lit)->entries[i].key); - res += hashLiteral(AS_DICTIONARY(lit)->entries[i].value); - } - } - return hash(res); - } - - // case LITERAL_FUNCTION: - // // - - case LITERAL_IDENTIFIER: - return HASH_I(lit); //pre-computed - - // case LITERAL_TYPE: - // //not needed - - // case LITERAL_ANY: - // //not needed - - default: - //should never bee seen - fprintf(stderr, ERROR "[internal] Unrecognized literal type in hash: %d\n" RESET, lit.type); - return 0; - } -} diff --git a/source/literal.h b/source/literal.h index f67c89d..f521e01 100644 --- a/source/literal.h +++ b/source/literal.h @@ -86,33 +86,25 @@ typedef struct { #define TO_BOOLEAN_LITERAL(value) ((Literal){LITERAL_BOOLEAN, { .boolean = value }}) #define TO_INTEGER_LITERAL(value) ((Literal){LITERAL_INTEGER, { .integer = value }}) #define TO_FLOAT_LITERAL(value) ((Literal){LITERAL_FLOAT, { .number = value }}) -#define TO_STRING_LITERAL(value) _toStringLiteral(value) +#define TO_STRING_LITERAL(value, l) _toStringLiteral(value, l) #define TO_ARRAY_LITERAL(value) ((Literal){LITERAL_ARRAY, { .array = value }}) #define TO_DICTIONARY_LITERAL(value) ((Literal){LITERAL_DICTIONARY, { .dictionary = value }}) #define TO_FUNCTION_LITERAL(value, l) ((Literal){LITERAL_FUNCTION, { .function.ptr = value, .function.scope = NULL, .function.length = l }}) -#define TO_IDENTIFIER_LITERAL(value) _toIdentifierLiteral(value, strlen(value)) +#define TO_IDENTIFIER_LITERAL(value, l) _toIdentifierLiteral(value, l) #define TO_TYPE_LITERAL(value, c) ((Literal){ LITERAL_TYPE, { .type.typeOf = value, .type.constant = c, .type.subtypes = NULL, .type.capacity = 0, .type.count = 0 }}) -//utils -void printLiteral(Literal literal); -void printLiteralCustom(Literal literal, void (printFn)(const char*)); void freeLiteral(Literal literal); #define IS_TRUTHY(x) _isTruthy(x) #define MAX_STRING_LENGTH 4096 -#define STRLEN(lit) ((lit).as.string.length) -#define STRLEN_I(lit) ((lit).as.identifier.length) +// #define STRLEN(lit) ((lit).as.string.length) +// #define STRLEN_I(lit) ((lit).as.identifier.length) #define HASH_I(lit) ((lit).as.identifier.hash) #define TYPE_PUSH_SUBTYPE(lit, subtype) _typePushSubtype(lit, subtype) //BUGFIX: macros are not functions bool _isTruthy(Literal x); -Literal _toStringLiteral(char* str); +Literal _toStringLiteral(char* str, int length); Literal _toIdentifierLiteral(char* str, int length); Literal* _typePushSubtype(Literal* lit, Literal subtype); - -//utils -char* copyString(char* original, int length); -bool literalsAreEqual(Literal lhs, Literal rhs); -int hashLiteral(Literal lit); \ No newline at end of file diff --git a/source/literal_array.c b/source/literal_array.c index 8276ed6..fec0f3f 100644 --- a/source/literal_array.c +++ b/source/literal_array.c @@ -22,10 +22,10 @@ int pushLiteralArray(LiteralArray* array, Literal literal) { //if it's a string or an identifier, make a local copy if (IS_STRING(literal)) { - literal = TO_STRING_LITERAL(copyString(AS_STRING(literal), STRLEN(literal))); + literal = TO_STRING_LITERAL(copyString(AS_STRING(literal), strlen( AS_STRING(literal) )), strlen( AS_STRING(literal) )); } if (IS_IDENTIFIER(literal)) { - literal = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(literal), STRLEN_I(literal))); + literal = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(literal), strlen( AS_IDENTIFIER(literal) )), strlen( AS_IDENTIFIER(literal) )); } array->literals[array->count] = literal; diff --git a/source/literal_array.h b/source/literal_array.h index 625dc63..1f85cbe 100644 --- a/source/literal_array.h +++ b/source/literal_array.h @@ -1,6 +1,7 @@ #pragma once #include "literal.h" +#include "literal_util.h" typedef struct LiteralArray { Literal* literals; diff --git a/source/literal_dictionary.c b/source/literal_dictionary.c index 0e133d3..8de5cf2 100644 --- a/source/literal_dictionary.c +++ b/source/literal_dictionary.c @@ -15,12 +15,12 @@ static void setEntryValues(_entry* entry, Literal key, Literal value) { //take ownership of the copied string if (IS_STRING(key)) { - entry->key = TO_STRING_LITERAL( copyString(AS_STRING(key), STRLEN(key)) ); + entry->key = TO_STRING_LITERAL( copyString(AS_STRING(key), strlen(AS_STRING(key)) ), strlen(AS_STRING(key))); } //OR take ownership of the copied identifier else if (IS_IDENTIFIER(key)) { - entry->key = TO_IDENTIFIER_LITERAL( copyString(AS_IDENTIFIER(key), STRLEN_I(key)) ); + entry->key = TO_IDENTIFIER_LITERAL( copyString(AS_IDENTIFIER(key), strlen( AS_IDENTIFIER(key))), strlen(AS_IDENTIFIER(key)) ); } else { @@ -33,10 +33,10 @@ static void setEntryValues(_entry* entry, Literal key, Literal value) { //take ownership of the copied string if (IS_STRING(value)) { - char* buffer = ALLOCATE(char, STRLEN(value) + 1); - strncpy(buffer, AS_STRING(value), STRLEN(value)); - buffer[STRLEN(value)] = '\0'; - entry->value = TO_STRING_LITERAL(buffer); + char* buffer = ALLOCATE(char, strlen(AS_STRING(value)) + 1); + strncpy(buffer, AS_STRING(value), strlen(AS_STRING(value))); + buffer[strlen(AS_STRING(value))] = '\0'; + entry->value = TO_STRING_LITERAL(buffer, strlen(buffer)); } //OR take ownership of the copied function @@ -125,17 +125,17 @@ static bool setEntryArray(_entry** dictionaryHandle, int* capacityPtr, int conta //if it's a string or an identifier, make a local copy if (IS_STRING(key)) { - key = TO_STRING_LITERAL(copyString(AS_STRING(key), STRLEN(key))); + key = TO_STRING_LITERAL(copyString(AS_STRING(key), strlen(AS_STRING(key)) ), strlen(AS_STRING(key))); } if (IS_IDENTIFIER(key)) { - key = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(key), STRLEN_I(key))); + key = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(key), strlen(AS_IDENTIFIER(key)) ), strlen(AS_IDENTIFIER(key))); } if (IS_STRING(value)) { - value = TO_STRING_LITERAL(copyString(AS_STRING(value), STRLEN(value))); + key = TO_STRING_LITERAL(copyString(AS_STRING(value), strlen(AS_STRING(value)) ), strlen(AS_STRING(value))); } if (IS_IDENTIFIER(value)) { - value = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(value), STRLEN_I(value))); + key = TO_IDENTIFIER_LITERAL(copyString(AS_IDENTIFIER(value), strlen(AS_IDENTIFIER(value)) ), strlen(AS_IDENTIFIER(value))); } //true = contains increase diff --git a/source/literal_dictionary.h b/source/literal_dictionary.h index f6cb34a..fec7bb4 100644 --- a/source/literal_dictionary.h +++ b/source/literal_dictionary.h @@ -1,6 +1,7 @@ #pragma once #include "literal.h" +#include "literal_util.h" //TODO: benchmark this #define DICTIONARY_MAX_LOAD 0.75 diff --git a/source/literal_util.c b/source/literal_util.c new file mode 100644 index 0000000..b2c77d6 --- /dev/null +++ b/source/literal_util.c @@ -0,0 +1,498 @@ +#include "literal_util.h" + +#include "literal_array.h" +#include "literal_dictionary.h" + +#include "memory.h" +#include "console_colors.h" + +#include + +char* copyString(char* original, int length) { + //make a local copy of the char array + char* buffer = ALLOCATE(char, length + 1); + strncpy(buffer, original, length); + buffer[length] = '\0'; + return buffer; +} + +bool literalsAreEqual(Literal lhs, Literal rhs) { + //utility for other things + if (lhs.type != rhs.type) { + // ints and floats are compatible + if ((IS_INTEGER(lhs) || IS_FLOAT(lhs)) && (IS_INTEGER(rhs) || IS_FLOAT(rhs))) { + if (IS_INTEGER(lhs)) { + return AS_INTEGER(lhs) + AS_FLOAT(rhs); + } + else { + return AS_FLOAT(lhs) + AS_INTEGER(rhs); + } + } + + return false; + } + + switch(lhs.type) { + case LITERAL_NULL: + return true; //can only be true because of the check above + + case LITERAL_BOOLEAN: + return AS_BOOLEAN(lhs) == AS_BOOLEAN(rhs); + + case LITERAL_INTEGER: + return AS_INTEGER(lhs) == AS_INTEGER(rhs); + + case LITERAL_FLOAT: + return AS_FLOAT(lhs) == AS_FLOAT(rhs); + + case LITERAL_STRING: + if (strlen(AS_STRING(lhs)) != strlen(AS_STRING(rhs))) { + return false; + } + return !strncmp(AS_STRING(lhs), AS_STRING(rhs), strlen(AS_STRING(lhs))); + + case LITERAL_ARRAY: + case LITERAL_TYPE_INTERMEDIATE: //BUGFIX: used for storing types as an array + //mismatched sizes + if (AS_ARRAY(lhs)->count != AS_ARRAY(rhs)->count) { + return false; + } + + //mismatched elements (in order) + for (int i = 0; i < AS_ARRAY(lhs)->count; i++) { + if (!literalsAreEqual( AS_ARRAY(lhs)->literals[i], AS_ARRAY(rhs)->literals[i] )) { + return false; + } + } + return true; + + case LITERAL_DICTIONARY: + //relatively slow, especially when nested + for (int i = 0; i < AS_DICTIONARY(lhs)->capacity; i++) { + if (!IS_NULL(AS_DICTIONARY(lhs)->entries[i].key)) { //only compare non-null keys + //check it exists in rhs + if (!existsLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key)) { + return false; + } + + //compare the values + if (!literalsAreEqual(AS_DICTIONARY(lhs)->entries[i].value, getLiteralDictionary(AS_DICTIONARY(rhs), AS_DICTIONARY(lhs)->entries[i].key) )) { + return false; + } + } + } + + return true; + + case LITERAL_FUNCTION: + case LITERAL_FUNCTION_NATIVE: + return false; //functions are never equal + break; + + case LITERAL_IDENTIFIER: + //check shortcuts + if (HASH_I(lhs) != HASH_I(rhs) && strlen(AS_IDENTIFIER(lhs)) != strlen(AS_IDENTIFIER(rhs))) { + return false; + } + + return !strncmp(AS_IDENTIFIER(lhs), AS_IDENTIFIER(rhs), strlen( AS_IDENTIFIER(lhs) )); + + case LITERAL_TYPE: + //check types + if (AS_TYPE(lhs).typeOf != AS_TYPE(rhs).typeOf) { + return false; + } + + //const don't match + if (AS_TYPE(lhs).constant != AS_TYPE(rhs).constant) { + return false; + } + + //check subtypes + if (AS_TYPE(lhs).count != AS_TYPE(rhs).count) { + return false; + } + + //check array|dictionary signatures are the same (in order) + if (AS_TYPE(lhs).typeOf == LITERAL_ARRAY || AS_TYPE(lhs).typeOf == LITERAL_DICTIONARY) { + for (int i = 0; i < AS_TYPE(lhs).count; i++) { + if (!literalsAreEqual(((Literal*)(AS_TYPE(lhs).subtypes))[i], ((Literal*)(AS_TYPE(rhs).subtypes))[i])) { + return false; + } + } + } + return true; + + case LITERAL_ANY: + return true; + + case LITERAL_FUNCTION_INTERMEDIATE: + fprintf(stderr, ERROR "[internal] Can't compare intermediate functions\n" RESET); + return false; + + default: + //should never be seen + fprintf(stderr, ERROR "[internal] Unrecognized literal type in equality: %d\n" RESET, lhs.type); + return false; + } + + return false; +} + +//hash util functions +static unsigned int hashString(const char* string, int length) { + unsigned int hash = 2166136261u; + + for (int i = 0; i < length; i++) { + hash *= string[i]; + hash *= 16777619; + } + + return hash; +} + +static unsigned int hash(unsigned int x) { + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return x; +} + +int hashLiteral(Literal lit) { + switch(lit.type) { + case LITERAL_NULL: + return 0; + + case LITERAL_BOOLEAN: + return AS_BOOLEAN(lit) ? 1 : 0; + + case LITERAL_INTEGER: + return hash((unsigned int)AS_INTEGER(lit)); + + case LITERAL_FLOAT: + return hash(*(unsigned int*)(&AS_FLOAT(lit))); + + case LITERAL_STRING: + return hashString(AS_STRING(lit), strlen(AS_STRING(lit))); + + case LITERAL_ARRAY: { + unsigned int res = 0; + for (int i = 0; i < AS_DICTIONARY(lit)->count; i++) { + res += hashLiteral(AS_ARRAY(lit)->literals[i]); + } + return hash(res); + } + + case LITERAL_DICTIONARY: { + unsigned int res = 0; + for (int i = 0; i < AS_DICTIONARY(lit)->capacity; i++) { + if (!IS_NULL(AS_DICTIONARY(lit)->entries[i].key)) { //only hash non-null keys + res += hashLiteral(AS_DICTIONARY(lit)->entries[i].key); + res += hashLiteral(AS_DICTIONARY(lit)->entries[i].value); + } + } + return hash(res); + } + + // case LITERAL_FUNCTION: + // // + + case LITERAL_IDENTIFIER: + return HASH_I(lit); //pre-computed + + // case LITERAL_TYPE: + // //not needed + + // case LITERAL_ANY: + // //not needed + + default: + //should never bee seen + fprintf(stderr, ERROR "[internal] Unrecognized literal type in hash: %d\n" RESET, lit.type); + return 0; + } +} + +//utils +static void stdoutWrapper(const char* output) { + fprintf(stdout, "%s", output); +} + +//buffer the prints +static char* globalPrintBuffer = NULL; +static size_t globalPrintCapacity = 0; +static size_t globalPrintCount = 0; + +//BUGFIX: string quotes shouldn't show when just printing strings, but should show when printing them as members of something else +static char quotes = 0; //set to 0 to not show string quotes + +static void printToBuffer(const char* str) { + while (strlen(str) + globalPrintCount > globalPrintCapacity) { + int oldCapacity = globalPrintCapacity; + + globalPrintCapacity = GROW_CAPACITY(globalPrintCapacity); + globalPrintBuffer = GROW_ARRAY(char, globalPrintBuffer, oldCapacity, globalPrintCapacity); + } + + snprintf(globalPrintBuffer + globalPrintCount, strlen(str) + 1, "%s", str); + globalPrintCount += strlen(str); +} + +//exposed functions +void printLiteral(Literal literal) { + printLiteralCustom(literal, stdoutWrapper); +} + +void printLiteralCustom(Literal literal, void (printFn)(const char*)) { + switch(literal.type) { + case LITERAL_NULL: + printFn("null"); + break; + + case LITERAL_BOOLEAN: + printFn(AS_BOOLEAN(literal) ? "true" : "false"); + break; + + case LITERAL_INTEGER: { + char buffer[256]; + snprintf(buffer, 256, "%d", AS_INTEGER(literal)); + printFn(buffer); + } + break; + + case LITERAL_FLOAT: { + char buffer[256]; + snprintf(buffer, 256, "%g", AS_FLOAT(literal)); + printFn(buffer); + } + break; + + case LITERAL_STRING: { + char buffer[MAX_STRING_LENGTH]; + if (!quotes) { + snprintf(buffer, MAX_STRING_LENGTH, "%.*s", (int)strlen(AS_STRING(literal)), AS_STRING(literal)); + } + else { + snprintf(buffer, MAX_STRING_LENGTH, "%c%.*s%c", quotes, (int)strlen(AS_STRING(literal)), AS_STRING(literal), quotes); + } + printFn(buffer); + } + break; + + case LITERAL_ARRAY: { + LiteralArray* ptr = AS_ARRAY(literal); + + //hold potential parent-call buffers on the C stack + char* cacheBuffer = globalPrintBuffer; + globalPrintBuffer = NULL; + int cacheCapacity = globalPrintCapacity; + globalPrintCapacity = 0; + int cacheCount = globalPrintCount; + globalPrintCount = 0; + + //print the contents to the global buffer + printToBuffer("["); + for (int i = 0; i < ptr->count; i++) { + quotes = '"'; + printLiteralCustom(ptr->literals[i], printToBuffer); + + if (i + 1 < ptr->count) { + printToBuffer(","); + } + } + printToBuffer("]"); + + //swap the parent-call buffer back into place + char* printBuffer = globalPrintBuffer; + int printCapacity = globalPrintCapacity; + int printCount = globalPrintCount; + + globalPrintBuffer = cacheBuffer; + globalPrintCapacity = cacheCapacity; + globalPrintCount = cacheCount; + + //finally, output and cleanup + printFn(printBuffer); + FREE_ARRAY(char, printBuffer, printCapacity); + quotes = 0; + } + break; + + case LITERAL_DICTIONARY: { + LiteralDictionary* ptr = AS_DICTIONARY(literal); + + //hold potential parent-call buffers on the C stack + char* cacheBuffer = globalPrintBuffer; + globalPrintBuffer = NULL; + int cacheCapacity = globalPrintCapacity; + globalPrintCapacity = 0; + int cacheCount = globalPrintCount; + globalPrintCount = 0; + + //print the contents to the global buffer + int delimCount = 0; + printToBuffer("["); + for (int i = 0; i < ptr->capacity; i++) { + if (IS_NULL(ptr->entries[i].key)) { + continue; + } + + if (delimCount++ > 0) { + printToBuffer(","); + } + + quotes = '"'; + printLiteralCustom(ptr->entries[i].key, printToBuffer); + printToBuffer(":"); + quotes = '"'; + printLiteralCustom(ptr->entries[i].value, printToBuffer); + } + + //empty dicts MUST have a ":" printed + if (ptr->count == 0) { + printToBuffer(":"); + } + + printToBuffer("]"); + + //swap the parent-call buffer back into place + char* printBuffer = globalPrintBuffer; + int printCapacity = globalPrintCapacity; + int printCount = globalPrintCount; + + globalPrintBuffer = cacheBuffer; + globalPrintCapacity = cacheCapacity; + globalPrintCount = cacheCount; + + //finally, output and cleanup + printFn(printBuffer); + FREE_ARRAY(char, printBuffer, printCapacity); + quotes = 0; + } + break; + + //TODO: functions + case LITERAL_FUNCTION: + case LITERAL_FUNCTION_NATIVE: + printFn("(function)"); + break; + + case LITERAL_IDENTIFIER: { + char buffer[256]; + snprintf(buffer, 256, "%.*s", (int)strlen( AS_IDENTIFIER(literal) ), AS_IDENTIFIER(literal)); + printFn(buffer); + } + break; + + case LITERAL_TYPE: { + //hold potential parent-call buffers on the C stack + char* cacheBuffer = globalPrintBuffer; + globalPrintBuffer = NULL; + int cacheCapacity = globalPrintCapacity; + globalPrintCapacity = 0; + int cacheCount = globalPrintCount; + globalPrintCount = 0; + + //print the type correctly + printToBuffer("<"); + + switch(AS_TYPE(literal).typeOf) { + case LITERAL_NULL: + printToBuffer("null"); + break; + + case LITERAL_BOOLEAN: + printToBuffer("bool"); + break; + + case LITERAL_INTEGER: + printToBuffer("int"); + break; + + case LITERAL_FLOAT: + printToBuffer("float"); + break; + + case LITERAL_STRING: + printToBuffer("string"); + break; + + case LITERAL_ARRAY: + //print all in the array + printToBuffer("["); + for (int i = 0; i < AS_TYPE(literal).count; i++) { + printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer); + } + printToBuffer("]"); + break; + + case LITERAL_DICTIONARY: + printToBuffer("["); + + for (int i = 0; i < AS_TYPE(literal).count; i += 2) { + printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i], printToBuffer); + printToBuffer(":"); + printLiteralCustom(((Literal*)(AS_TYPE(literal).subtypes))[i + 1], printToBuffer); + } + printToBuffer("]"); + break; + + case LITERAL_FUNCTION: + printToBuffer("function"); + //TODO: how to print a function + break; + + case LITERAL_IDENTIFIER: + printToBuffer("identifier"); + break; + + case LITERAL_TYPE: + printToBuffer("type"); + break; + + case LITERAL_ANY: + printToBuffer("any"); + break; + + default: + //should never be seen + fprintf(stderr, ERROR "[internal] Unrecognized literal type in print type: %d\n" RESET, AS_TYPE(literal).typeOf); + } + + //const (printed last) + if (AS_TYPE(literal).constant) { + printToBuffer(" const"); + } + + printToBuffer(">"); + + //swap the parent-call buffer back into place + char* printBuffer = globalPrintBuffer; + int printCapacity = globalPrintCapacity; + int printCount = globalPrintCount; + + globalPrintBuffer = cacheBuffer; + globalPrintCapacity = cacheCapacity; + globalPrintCount = cacheCount; + + //finally, output and cleanup + printFn(printBuffer); + FREE_ARRAY(char, printBuffer, printCapacity); + quotes = 0; + } + break; + + case LITERAL_TYPE_INTERMEDIATE: + case LITERAL_FUNCTION_INTERMEDIATE: + printFn("Unprintable literal found"); + break; + + case LITERAL_ANY: + printFn("(any)"); + break; + + default: + //should never be seen + fprintf(stderr, ERROR "[internal] Unrecognized literal type in print: %d\n" RESET, literal.type); + } +} \ No newline at end of file diff --git a/source/literal_util.h b/source/literal_util.h new file mode 100644 index 0000000..950d329 --- /dev/null +++ b/source/literal_util.h @@ -0,0 +1,10 @@ +#pragma once + +#include "literal.h" + +char* copyString(char* original, int length); +bool literalsAreEqual(Literal lhs, Literal rhs); +int hashLiteral(Literal lit); + +void printLiteral(Literal literal); +void printLiteralCustom(Literal literal, void (printFn)(const char*)); diff --git a/source/memory.c b/source/memory.c index c0bf3ca..603b43c 100644 --- a/source/memory.c +++ b/source/memory.c @@ -20,7 +20,7 @@ void* reallocate(void* pointer, size_t oldSize, size_t newSize) { void* mem = realloc(pointer, newSize); if (mem == NULL) { - fprintf(stderr, ERROR "[internal]Memory allocation error (requested %d for %d, replacing %d)\n" ERROR, (int)newSize, (int)pointer, (int)oldSize); + fprintf(stderr, ERROR "[internal]Memory allocation error (requested %d for %ld, replacing %d)\n" ERROR, (int)newSize, (long int)pointer, (int)oldSize); exit(-1); } diff --git a/source/node.h b/source/node.h index df64109..4e51dfa 100644 --- a/source/node.h +++ b/source/node.h @@ -1,6 +1,7 @@ #pragma once #include "literal.h" +#include "literal_util.h" //TODO: move this to node_util.h #include "opcodes.h" #include "token_types.h" diff --git a/source/parser.c b/source/parser.c index ada6e56..8cb6d89 100644 --- a/source/parser.c +++ b/source/parser.c @@ -255,7 +255,7 @@ static Opcode string(Parser* parser, Node** nodeHandle) { error(parser, parser->previous, buffer); } - emitNodeLiteral(nodeHandle, TO_STRING_LITERAL(copyString(parser->previous.lexeme, length))); + emitNodeLiteral(nodeHandle, TO_STRING_LITERAL(copyString(parser->previous.lexeme, length), length)); return OP_EOF; } diff --git a/test/makefile b/test/makefile index 3d641b1..97d076d 100644 --- a/test/makefile +++ b/test/makefile @@ -12,7 +12,7 @@ OBJ = $(addprefix $(ODIR)/,$(TARGETS:../source/%.c=%.o)) $(addprefix $(ODIR)/,$( all: $(OBJ) $(TESTS:%.c=../$(OUTDIR)/%.exe) ../$(OUTDIR)/%.exe: $(ODIR)/%.o - $(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS) + @$(CC) -o $@ $< $(TARGETS:../source/%.c=$(ODIR)/%.o) $(CFLAGS) $(LIBS) $@ $(OBJ): | $(ODIR) @@ -21,10 +21,10 @@ $(ODIR): mkdir $(ODIR) $(ODIR)/%.o: %.c - $(CC) -c -o $@ $< $(CFLAGS) + @$(CC) -c -o $@ $< $(CFLAGS) $(ODIR)/%.o: ../source/%.c - $(CC) -c -o $@ $< $(CFLAGS) + @$(CC) -c -o $@ $< $(CFLAGS) .PHONY: clean diff --git a/test/test_literal.c b/test/test_literal.c new file mode 100644 index 0000000..31838b9 --- /dev/null +++ b/test/test_literal.c @@ -0,0 +1,50 @@ +#include "literal.h" + +#include "memory.h" +#include "console_colors.h" + +#include + +int main() { + { + //test a single null literal + Literal literal = TO_NULL_LITERAL; + + // + if (!IS_NULL(literal)) { + fprintf(stderr, ERROR "ERROR: null literal failed\n" RESET); + return -1; + } + } + + { + //test boolean literals + Literal t = TO_BOOLEAN_LITERAL(true); + Literal f = TO_BOOLEAN_LITERAL(false); + + // + if (!IS_TRUTHY(t) || IS_TRUTHY(f)) { + fprintf(stderr, ERROR "ERROR: boolean literal failed\n" RESET); + return -1; + } + } + + { + char* buffer = ALLOCATE(char, 128); + + snprintf(buffer, 128, "Hello world"); + + Literal literal = TO_STRING_LITERAL(buffer, 128); + + freeLiteral(literal); + } + + //check allocated memory + if (getAllocatedMemoryCount() != 0) { + fprintf(stderr, ERROR "ERROR: Dangling memory detected: %d byes\n" RESET, getAllocatedMemoryCount()); + return -1; + } + + printf(NOTICE "All good\n" RESET); + return 0; +} \ No newline at end of file