From f9790b99ce03d6466bd494e38c9ca1c21654f7ec Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Wed, 15 Apr 2026 15:04:54 +1000 Subject: [PATCH] Moved type coersion check, functions can be compared Also updated some tagged comments --- repl/bytecode_inspector.c | 6 ++++-- scripts/hello_world.toy | 2 +- source/toy_bucket.c | 2 +- source/toy_compiler.c | 5 ++--- source/toy_compiler.h | 2 -- source/toy_function.h | 2 +- source/toy_parser.c | 5 ++--- source/toy_scope.c | 12 ++++++++++++ source/toy_string.c | 3 +-- source/toy_table.c | 7 +++---- source/toy_value.c | 28 +++++++++++++++++++--------- source/toy_value.h | 4 +--- source/toy_vm.c | 10 ---------- tests/scripts/test_tables.toy | 1 + tests/units/test_value.c | 3 ++- 15 files changed, 50 insertions(+), 42 deletions(-) diff --git a/repl/bytecode_inspector.c b/repl/bytecode_inspector.c index 80d0173..c403261 100644 --- a/repl/bytecode_inspector.c +++ b/repl/bytecode_inspector.c @@ -87,7 +87,7 @@ void inspect_bytecode(unsigned char* bytecode) { while(bytecode[pc] != TOY_OPCODE_RETURN) { pc += inspect_instruction(bytecode, pc, jumps_addr, data_addr); } - pc += inspect_instruction(bytecode, pc, jumps_addr, data_addr); //BUGFIX: one more for return + pc += inspect_instruction(bytecode, pc, jumps_addr, data_addr); //one more for the final return (void)jumps_addr; (void)param_addr; @@ -289,7 +289,7 @@ int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_ad } case TOY_VALUE_STRING: { - Toy_StringType stringType = (Toy_StringType)(*(bytecode + pc + 2)); //TODO: not needed? + Toy_StringType stringType = (Toy_StringType)(*(bytecode + pc + 2)); //Probably not needed int len = bytecode[pc + 3]; //only used for names? (void)stringType; @@ -318,3 +318,5 @@ int inspect_read(unsigned char* bytecode, unsigned int pc, unsigned int jumps_ad } } } + +//URGENT: Check if strings are reused in the bytecode \ No newline at end of file diff --git a/scripts/hello_world.toy b/scripts/hello_world.toy index aaa244a..cf64973 100644 --- a/scripts/hello_world.toy +++ b/scripts/hello_world.toy @@ -9,5 +9,5 @@ var b = 69; var c; var d; -//TODO: reverse the assignment bytecode order? +//URGENT: reverse the assignment bytecode order? c, d = swap(a, b); \ No newline at end of file diff --git a/source/toy_bucket.c b/source/toy_bucket.c index a03db5b..79cc5b1 100644 --- a/source/toy_bucket.c +++ b/source/toy_bucket.c @@ -25,7 +25,7 @@ Toy_Bucket* Toy_allocateBucket(unsigned int capacity) { } unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount) { - //BUGFIX: the endpoint must be aligned to the word size, otherwise you'll get a bus error from moving pointers + //the endpoint must be aligned to the word size, otherwise you'll get a bus error from moving pointers amount = (amount + 3) & ~3; assert((*bucketHandle) != NULL && "Expected a 'Toy_Bucket', received NULL"); diff --git a/source/toy_compiler.c b/source/toy_compiler.c index f64759a..cf1efca 100644 --- a/source/toy_compiler.c +++ b/source/toy_compiler.c @@ -11,7 +11,6 @@ //misc. utils static bool checkForChaining(Toy_Ast* ptr) { - //BUGFIX if (ptr == NULL) { return false; } @@ -155,7 +154,7 @@ static unsigned int emitString(Toy_Bytecode** mb, Toy_String* str) { free(buffer); } else if (str->info.type == TOY_STRING_LEAF) { - char buffer[str->info.length + 1]; //BUGFIX: make sure its a null-terminated c-string + char buffer[str->info.length + 1]; //make sure its a null-terminated c-string memcpy(buffer, str->leaf.data, str->info.length); buffer[str->info.length] = '\0'; @@ -801,7 +800,7 @@ static unsigned int writeInstructionVarDeclare(Toy_Bytecode** mb, Toy_AstVarDecl static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment) { unsigned int result = 0; - //TODO: flip the order of target & value, to allow chained assignment AND multiple return values + //URGENT: flip the order of target & value, to allow chained assignment AND multiple return values //target is a variable name if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) { diff --git a/source/toy_compiler.h b/source/toy_compiler.h index 514b85b..051588a 100644 --- a/source/toy_compiler.h +++ b/source/toy_compiler.h @@ -48,8 +48,6 @@ typedef struct Toy_Bytecode { unsigned int subsCapacity; unsigned int subsCount; - //TODO: duplicate string reuse, see #168 - //tools for handling the build process unsigned int currentScopeDepth; Toy_private_EscapeArray* breakEscapes; diff --git a/source/toy_function.h b/source/toy_function.h index b282176..2149eaf 100644 --- a/source/toy_function.h +++ b/source/toy_function.h @@ -17,7 +17,7 @@ typedef struct Toy_FunctionBytecode { typedef struct Toy_FunctionNative { Toy_FunctionType type; - void* native; //TODO: replace with the native function pointer + void* ptr; //TODO: replace with the native function pointer } Toy_FunctionNative; typedef union Toy_Function_t { diff --git a/source/toy_parser.c b/source/toy_parser.c index ea462c8..98ba5b4 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -330,7 +330,7 @@ static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As buffer[i] = parser->previous.lexeme[o]; if (buffer[i] != '_') i++; } while (parser->previous.lexeme[o++] && i < parser->previous.length); - buffer[i] = '\0'; //BUGFIX + buffer[i] = '\0'; int value = 0; sscanf(buffer, "%d", &value); @@ -347,7 +347,7 @@ static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As buffer[i] = parser->previous.lexeme[o]; if (buffer[i] != '_') i++; } while (parser->previous.lexeme[o++] && i < parser->previous.length); - buffer[i] = '\0'; //BUGFIX + buffer[i] = '\0'; float value = 0; sscanf(buffer, "%f", &value); @@ -964,7 +964,6 @@ static void makeFunctionDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p advance(parser); Toy_Token nameToken = parser->previous; - //URGENT: fix this with param type info //read the type specifier if present Toy_ValueType varType = TOY_VALUE_ANY; bool constant = true; //parameters are immutable diff --git a/source/toy_scope.c b/source/toy_scope.c index a7b3e16..b21634c 100644 --- a/source/toy_scope.c +++ b/source/toy_scope.c @@ -103,6 +103,14 @@ static Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapa return newEntries; } +Toy_Value coerceValueTypesIfAble(Toy_ValueType type, Toy_Value value) { + //integer to float + if (type == TOY_VALUE_FLOAT && value.type == TOY_VALUE_INTEGER) { + value = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(value) ); + } + return value; +} + //exposed functions Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) { Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope)); @@ -138,6 +146,8 @@ void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy return; } + value = coerceValueTypesIfAble(type, value); + //type check if (type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && type != value.type && value.type != TOY_VALUE_REFERENCE) { char buffer[key->info.length + 256]; @@ -159,6 +169,8 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) { return; } + value = coerceValueTypesIfAble(entryPtr->type, value); + //type check if (entryPtr->type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && entryPtr->type != value.type && value.type != TOY_VALUE_REFERENCE) { char buffer[key->info.length + 256]; diff --git a/source/toy_string.c b/source/toy_string.c index 36ac200..59a6dfc 100644 --- a/source/toy_string.c +++ b/source/toy_string.c @@ -45,7 +45,7 @@ Toy_String* Toy_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, u Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) { Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String)); - if (length > 0) { //BUGFIX + if (length > 0) { ret->leaf.data = (char*)Toy_partitionBucket(bucketHandle, length + 1); strncpy((char*)(ret->leaf.data), cstring, length); ((char*)(ret->leaf.data))[length] = '\0'; //don't forget the null @@ -232,7 +232,6 @@ unsigned int Toy_hashString(Toy_String* str) { return str->info.cachedHash; } else if (str->info.type == TOY_STRING_NODE) { - //TODO: I wonder if it would be possible to discretely swap the composite node string with a new leaf string here? Would that speed up other parts of the code by not having to walk the tree in future? - needs to be benchmarked char* buffer = Toy_getStringRaw(str); str->info.cachedHash = hashCString(buffer); free(buffer); diff --git a/source/toy_table.c b/source/toy_table.c index e665541..6d37b99 100644 --- a/source/toy_table.c +++ b/source/toy_table.c @@ -93,7 +93,7 @@ void Toy_freeTable(Toy_Table* table) { } void Toy_insertTable(Toy_Table** tableHandle, Toy_Value key, Toy_Value value) { - if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key)) { //TODO: disallow functions and opaques + if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key) || TOY_VALUE_IS_FUNCTION(key) || TOY_VALUE_IS_OPAQUE(key)) { fprintf(stderr, TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET); exit(1); } @@ -107,7 +107,7 @@ void Toy_insertTable(Toy_Table** tableHandle, Toy_Value key, Toy_Value value) { } Toy_TableEntry* Toy_private_lookupTableEntryPtr(Toy_Table** tableHandle, Toy_Value key) { - if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key)) { //TODO: disallow functions and opaques + if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key) || TOY_VALUE_IS_FUNCTION(key) || TOY_VALUE_IS_OPAQUE(key)) { fprintf(stderr, TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET); exit(1); } @@ -133,7 +133,6 @@ Toy_TableEntry* Toy_private_lookupTableEntryPtr(Toy_Table** tableHandle, Toy_Val } Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key) { - //TODO: I hacked this in at a moment's notice, clean it up Toy_TableEntry* entry = Toy_private_lookupTableEntryPtr(tableHandle, key); if (entry == NULL) { @@ -145,7 +144,7 @@ Toy_Value Toy_lookupTable(Toy_Table** tableHandle, Toy_Value key) { } void Toy_removeTable(Toy_Table** tableHandle, Toy_Value key) { - if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key)) { //TODO: disallow functions and opaques + if (TOY_VALUE_IS_NULL(key) || TOY_VALUE_IS_BOOLEAN(key) || TOY_VALUE_IS_FUNCTION(key) || TOY_VALUE_IS_OPAQUE(key)) { fprintf(stderr, TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET); exit(1); } diff --git a/source/toy_value.c b/source/toy_value.c index 7476953..aadf6ac 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -137,8 +137,9 @@ Toy_Value Toy_copyValue(Toy_Value value) { return TOY_VALUE_FROM_TABLE(result); } - case TOY_VALUE_FUNCTION: - return value; //URGENT: concerning + case TOY_VALUE_FUNCTION: //TODO: implement function duplication elsewhere + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't copy a function (use a reference instead), exiting\n" TOY_CC_RESET); + exit(-1); case TOY_VALUE_OPAQUE: case TOY_VALUE_ANY: @@ -313,7 +314,17 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) { } case TOY_VALUE_FUNCTION: - return false; //URGENT: test this + if (right.type == TOY_VALUE_FUNCTION && left.as.function->type == right.as.function->type) { + if (left.as.function->type == TOY_FUNCTION_CUSTOM) { + return left.as.function->bytecode.code == right.as.function->bytecode.code; + } + else { + return left.as.function->native.ptr == right.as.function->native.ptr; + } + } + else { + return false; + } case TOY_VALUE_OPAQUE: case TOY_VALUE_ANY: @@ -529,14 +540,13 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) { //clean up Toy_freeString(open); Toy_freeString(close); - Toy_freeString(comma); //TODO: reusable global, or string type "permanent" - Toy_freeString(quote); //TODO: reusable global, or string type "permanent" + Toy_freeString(comma); //TODO: reusable global, or string type "permanent", needs benchmarking + Toy_freeString(quote); return string; } case TOY_VALUE_TABLE: { - //TODO: concat + free is definitely a performance nightmare, could make an append function? Toy_Table* ptr = value.as.table; //if table is empty, skip below @@ -620,9 +630,9 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) { //clean up Toy_freeString(open); Toy_freeString(close); - Toy_freeString(colon); //TODO: reusable global, or string type "permanent" - Toy_freeString(comma); //TODO: reusable global, or string type "permanent" - Toy_freeString(quote); //TODO: reusable global, or string type "permanent" + Toy_freeString(colon); + Toy_freeString(comma); + Toy_freeString(quote); return string; } diff --git a/source/toy_value.h b/source/toy_value.h index 58d4d15..36a15ae 100644 --- a/source/toy_value.h +++ b/source/toy_value.h @@ -37,7 +37,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS struct Toy_Array* array; //4 | 8 struct Toy_Table* table; //4 | 8 union Toy_Function_t* function;//4 | 8 - //TODO: more types go here + //more types go here as needed } as; //4 | 8 @@ -63,7 +63,6 @@ typedef struct Toy_Value { //32 | 64 BITNESS #define TOY_VALUE_AS_ARRAY(value) ((TOY_VALUE_IS_REFERENCE(value) ? Toy_unwrapValue(value) : value).as.array) #define TOY_VALUE_AS_TABLE(value) ((TOY_VALUE_IS_REFERENCE(value) ? Toy_unwrapValue(value) : value).as.table) #define TOY_VALUE_AS_FUNCTION(value) ((TOY_VALUE_IS_REFERENCE(value) ? Toy_unwrapValue(value) : value).as.function) -//TODO: more #define TOY_VALUE_FROM_NULL() ((Toy_Value){{ .integer = 0 }, TOY_VALUE_NULL}) #define TOY_VALUE_FROM_BOOLEAN(value) ((Toy_Value){{ .boolean = value }, TOY_VALUE_BOOLEAN}) @@ -73,7 +72,6 @@ typedef struct Toy_Value { //32 | 64 BITNESS #define TOY_VALUE_FROM_ARRAY(value) ((Toy_Value){{ .array = value }, TOY_VALUE_ARRAY}) #define TOY_VALUE_FROM_TABLE(value) ((Toy_Value){{ .table = value }, TOY_VALUE_TABLE}) #define TOY_VALUE_FROM_FUNCTION(value) ((Toy_Value){{ .function = value }, TOY_VALUE_FUNCTION}) -//TODO: more #define TOY_REFERENCE_FROM_POINTER(ptr) ((Toy_Value){{ .reference = ptr }, TOY_VALUE_REFERENCE}) diff --git a/source/toy_vm.c b/source/toy_vm.c index 49a6568..1b802d8 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -209,11 +209,6 @@ static void processDeclare(Toy_VM* vm) { //get the value Toy_Value value = Toy_popStack(&vm->stack); - //BUGFIX: only allowable type coersion - if (type == TOY_VALUE_FLOAT && value.type == TOY_VALUE_INTEGER) { - value = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(value) ); - } - //declare it Toy_declareScope(vm->scope, name, type, value, constant); @@ -226,11 +221,6 @@ static void processAssign(Toy_VM* vm) { Toy_Value value = Toy_popStack(&vm->stack); Toy_Value name = Toy_popStack(&vm->stack); - //URGENT: only allowable type coersion - // if (TOY_VALUE_AS_STRING(name)->name.varType == TOY_VALUE_FLOAT && value.type == TOY_VALUE_INTEGER) { - // value = TOY_VALUE_FROM_FLOAT( (float)TOY_VALUE_AS_INTEGER(value) ); - // } - //assign it Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), value); //scope now owns the value, doesn't need to be freed diff --git a/tests/scripts/test_tables.toy b/tests/scripts/test_tables.toy index 08ad0e8..44567fa 100644 --- a/tests/scripts/test_tables.toy +++ b/tests/scripts/test_tables.toy @@ -5,6 +5,7 @@ a["beta"] = 6; print a; assert a == ["alpha": 1, "beta": 6, "gamma": 3], "1-D tables failed"; +//BUG: nested tables causes an issue under GDB, idk why the tests pass without it //nested var b = [ "outer": ["inner": true], diff --git a/tests/units/test_value.c b/tests/units/test_value.c index 3486495..6cedf74 100644 --- a/tests/units/test_value.c +++ b/tests/units/test_value.c @@ -201,7 +201,7 @@ int test_value_hashing(void) { Toy_hashValue(f) != 0 || Toy_hashValue(i) != 4147366645 || Toy_hashValue(s) != 994097935 || - TOY_VALUE_AS_STRING(s)->info.cachedHash == 0 || //TODO: check this + TOY_VALUE_AS_STRING(s)->info.cachedHash == 0 || Toy_hashValue(a) != 2544446955 ) { @@ -598,6 +598,7 @@ int main(void) { } //TODO: references + //TODO: type coersions return total; } \ No newline at end of file