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) {
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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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],

View File

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