Moved type coersion check, functions can be compared

Also updated some tagged comments
This commit is contained in:
2026-04-15 15:04:54 +10:00
parent dde52f9d8a
commit f9790b99ce
15 changed files with 50 additions and 42 deletions

View File

@@ -87,7 +87,7 @@ void inspect_bytecode(unsigned char* bytecode) {
while(bytecode[pc] != TOY_OPCODE_RETURN) { while(bytecode[pc] != TOY_OPCODE_RETURN) {
pc += inspect_instruction(bytecode, pc, jumps_addr, data_addr); 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)jumps_addr;
(void)param_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: { 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? int len = bytecode[pc + 3]; //only used for names?
(void)stringType; (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

View File

@@ -9,5 +9,5 @@ var b = 69;
var c; var c;
var d; var d;
//TODO: reverse the assignment bytecode order? //URGENT: reverse the assignment bytecode order?
c, d = swap(a, b); c, d = swap(a, b);

View File

@@ -25,7 +25,7 @@ Toy_Bucket* Toy_allocateBucket(unsigned int capacity) {
} }
unsigned char* Toy_partitionBucket(Toy_Bucket** bucketHandle, unsigned int amount) { 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; amount = (amount + 3) & ~3;
assert((*bucketHandle) != NULL && "Expected a 'Toy_Bucket', received NULL"); assert((*bucketHandle) != NULL && "Expected a 'Toy_Bucket', received NULL");

View File

@@ -11,7 +11,6 @@
//misc. utils //misc. utils
static bool checkForChaining(Toy_Ast* ptr) { static bool checkForChaining(Toy_Ast* ptr) {
//BUGFIX
if (ptr == NULL) { if (ptr == NULL) {
return false; return false;
} }
@@ -155,7 +154,7 @@ static unsigned int emitString(Toy_Bytecode** mb, Toy_String* str) {
free(buffer); free(buffer);
} }
else if (str->info.type == TOY_STRING_LEAF) { 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); memcpy(buffer, str->leaf.data, str->info.length);
buffer[str->info.length] = '\0'; 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) { static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment) {
unsigned int result = 0; 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 //target is a variable name
if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) { if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) {

View File

@@ -48,8 +48,6 @@ typedef struct Toy_Bytecode {
unsigned int subsCapacity; unsigned int subsCapacity;
unsigned int subsCount; unsigned int subsCount;
//TODO: duplicate string reuse, see #168
//tools for handling the build process //tools for handling the build process
unsigned int currentScopeDepth; unsigned int currentScopeDepth;
Toy_private_EscapeArray* breakEscapes; Toy_private_EscapeArray* breakEscapes;

View File

@@ -17,7 +17,7 @@ typedef struct Toy_FunctionBytecode {
typedef struct Toy_FunctionNative { typedef struct Toy_FunctionNative {
Toy_FunctionType type; Toy_FunctionType type;
void* native; //TODO: replace with the native function pointer void* ptr; //TODO: replace with the native function pointer
} Toy_FunctionNative; } Toy_FunctionNative;
typedef union Toy_Function_t { typedef union Toy_Function_t {

View File

@@ -330,7 +330,7 @@ static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As
buffer[i] = parser->previous.lexeme[o]; buffer[i] = parser->previous.lexeme[o];
if (buffer[i] != '_') i++; if (buffer[i] != '_') i++;
} while (parser->previous.lexeme[o++] && i < parser->previous.length); } while (parser->previous.lexeme[o++] && i < parser->previous.length);
buffer[i] = '\0'; //BUGFIX buffer[i] = '\0';
int value = 0; int value = 0;
sscanf(buffer, "%d", &value); 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]; buffer[i] = parser->previous.lexeme[o];
if (buffer[i] != '_') i++; if (buffer[i] != '_') i++;
} while (parser->previous.lexeme[o++] && i < parser->previous.length); } while (parser->previous.lexeme[o++] && i < parser->previous.length);
buffer[i] = '\0'; //BUGFIX buffer[i] = '\0';
float value = 0; float value = 0;
sscanf(buffer, "%f", &value); sscanf(buffer, "%f", &value);
@@ -964,7 +964,6 @@ static void makeFunctionDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p
advance(parser); advance(parser);
Toy_Token nameToken = parser->previous; Toy_Token nameToken = parser->previous;
//URGENT: fix this with param type info
//read the type specifier if present //read the type specifier if present
Toy_ValueType varType = TOY_VALUE_ANY; Toy_ValueType varType = TOY_VALUE_ANY;
bool constant = true; //parameters are immutable bool constant = true; //parameters are immutable

View File

@@ -103,6 +103,14 @@ static Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapa
return newEntries; 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 //exposed functions
Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) { Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_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; return;
} }
value = coerceValueTypesIfAble(type, value);
//type check //type check
if (type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && type != value.type && value.type != TOY_VALUE_REFERENCE) { if (type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && type != value.type && value.type != TOY_VALUE_REFERENCE) {
char buffer[key->info.length + 256]; char buffer[key->info.length + 256];
@@ -159,6 +169,8 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
return; return;
} }
value = coerceValueTypesIfAble(entryPtr->type, value);
//type check //type check
if (entryPtr->type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && entryPtr->type != value.type && value.type != TOY_VALUE_REFERENCE) { 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]; char buffer[key->info.length + 256];

View File

@@ -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* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) {
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String)); 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); ret->leaf.data = (char*)Toy_partitionBucket(bucketHandle, length + 1);
strncpy((char*)(ret->leaf.data), cstring, length); strncpy((char*)(ret->leaf.data), cstring, length);
((char*)(ret->leaf.data))[length] = '\0'; //don't forget the null ((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; return str->info.cachedHash;
} }
else if (str->info.type == TOY_STRING_NODE) { 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); char* buffer = Toy_getStringRaw(str);
str->info.cachedHash = hashCString(buffer); str->info.cachedHash = hashCString(buffer);
free(buffer); free(buffer);

View File

@@ -93,7 +93,7 @@ void Toy_freeTable(Toy_Table* table) {
} }
void Toy_insertTable(Toy_Table** tableHandle, Toy_Value key, Toy_Value value) { 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); fprintf(stderr, TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET);
exit(1); 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) { 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); fprintf(stderr, TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET);
exit(1); 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) { 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); Toy_TableEntry* entry = Toy_private_lookupTableEntryPtr(tableHandle, key);
if (entry == NULL) { 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) { 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); fprintf(stderr, TOY_CC_ERROR "ERROR: Bad table key\n" TOY_CC_RESET);
exit(1); exit(1);
} }

View File

@@ -137,8 +137,9 @@ Toy_Value Toy_copyValue(Toy_Value value) {
return TOY_VALUE_FROM_TABLE(result); return TOY_VALUE_FROM_TABLE(result);
} }
case TOY_VALUE_FUNCTION: case TOY_VALUE_FUNCTION: //TODO: implement function duplication elsewhere
return value; //URGENT: concerning 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_OPAQUE:
case TOY_VALUE_ANY: case TOY_VALUE_ANY:
@@ -313,7 +314,17 @@ bool Toy_checkValuesAreEqual(Toy_Value left, Toy_Value right) {
} }
case TOY_VALUE_FUNCTION: 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_OPAQUE:
case TOY_VALUE_ANY: case TOY_VALUE_ANY:
@@ -529,14 +540,13 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
//clean up //clean up
Toy_freeString(open); Toy_freeString(open);
Toy_freeString(close); Toy_freeString(close);
Toy_freeString(comma); //TODO: reusable global, or string type "permanent" Toy_freeString(comma); //TODO: reusable global, or string type "permanent", needs benchmarking
Toy_freeString(quote); //TODO: reusable global, or string type "permanent" Toy_freeString(quote);
return string; return string;
} }
case TOY_VALUE_TABLE: { case TOY_VALUE_TABLE: {
//TODO: concat + free is definitely a performance nightmare, could make an append function?
Toy_Table* ptr = value.as.table; Toy_Table* ptr = value.as.table;
//if table is empty, skip below //if table is empty, skip below
@@ -620,9 +630,9 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
//clean up //clean up
Toy_freeString(open); Toy_freeString(open);
Toy_freeString(close); Toy_freeString(close);
Toy_freeString(colon); //TODO: reusable global, or string type "permanent" Toy_freeString(colon);
Toy_freeString(comma); //TODO: reusable global, or string type "permanent" Toy_freeString(comma);
Toy_freeString(quote); //TODO: reusable global, or string type "permanent" Toy_freeString(quote);
return string; return string;
} }

View File

@@ -37,7 +37,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS
struct Toy_Array* array; //4 | 8 struct Toy_Array* array; //4 | 8
struct Toy_Table* table; //4 | 8 struct Toy_Table* table; //4 | 8
union Toy_Function_t* function;//4 | 8 union Toy_Function_t* function;//4 | 8
//TODO: more types go here //more types go here as needed
} as; //4 | 8 } 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_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_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) #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_NULL() ((Toy_Value){{ .integer = 0 }, TOY_VALUE_NULL})
#define TOY_VALUE_FROM_BOOLEAN(value) ((Toy_Value){{ .boolean = value }, TOY_VALUE_BOOLEAN}) #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_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_TABLE(value) ((Toy_Value){{ .table = value }, TOY_VALUE_TABLE})
#define TOY_VALUE_FROM_FUNCTION(value) ((Toy_Value){{ .function = value }, TOY_VALUE_FUNCTION}) #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}) #define TOY_REFERENCE_FROM_POINTER(ptr) ((Toy_Value){{ .reference = ptr }, TOY_VALUE_REFERENCE})

View File

@@ -209,11 +209,6 @@ static void processDeclare(Toy_VM* vm) {
//get the value //get the value
Toy_Value value = Toy_popStack(&vm->stack); 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 //declare it
Toy_declareScope(vm->scope, name, type, value, constant); 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 value = Toy_popStack(&vm->stack);
Toy_Value name = 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 //assign it
Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), value); //scope now owns the value, doesn't need to be freed Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), value); //scope now owns the value, doesn't need to be freed

View File

@@ -5,6 +5,7 @@ a["beta"] = 6;
print a; print a;
assert a == ["alpha": 1, "beta": 6, "gamma": 3], "1-D tables failed"; 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 //nested
var b = [ var b = [
"outer": ["inner": true], "outer": ["inner": true],

View File

@@ -201,7 +201,7 @@ int test_value_hashing(void) {
Toy_hashValue(f) != 0 || Toy_hashValue(f) != 0 ||
Toy_hashValue(i) != 4147366645 || Toy_hashValue(i) != 4147366645 ||
Toy_hashValue(s) != 994097935 || 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 Toy_hashValue(a) != 2544446955
) )
{ {
@@ -598,6 +598,7 @@ int main(void) {
} }
//TODO: references //TODO: references
//TODO: type coersions
return total; return total;
} }