mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Moved type coersion check, functions can be compared
Also updated some tagged comments
This commit is contained in:
@@ -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
|
||||
@@ -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);
|
||||
@@ -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");
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user