From 914ee6fcfa9866a409c56563303edbb592c01c40 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 5 Apr 2026 10:13:58 +1000 Subject: [PATCH] WIP: Fixed strings and scopes, still reworking impacted areas --- source/toy_ast.c | 4 +- source/toy_ast.h | 4 +- source/toy_module_compiler.c | 34 +++---- source/toy_parser.c | 34 +++---- source/toy_scope.c | 175 ++++++++++++++++++++--------------- source/toy_scope.h | 37 ++++++-- source/toy_string.c | 156 +++++++------------------------ source/toy_string.h | 25 +---- source/toy_value.c | 2 +- 9 files changed, 204 insertions(+), 267 deletions(-) diff --git a/source/toy_ast.c b/source/toy_ast.c index 31d45dd..86fc3bd 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -181,11 +181,13 @@ void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { (*astHandle) = tmp; } -void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr) { +void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr) { Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); tmp->type = TOY_AST_VAR_DECLARE; tmp->varDeclare.name = name; + tmp->varDeclare.valueType = valueType; + tmp->varDeclare.constant = constant; tmp->varDeclare.expr = expr; (*astHandle) = tmp; diff --git a/source/toy_ast.h b/source/toy_ast.h index 3536b74..249fb36 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -185,6 +185,8 @@ typedef struct Toy_AstPrint { typedef struct Toy_AstVarDeclare { Toy_AstType type; Toy_String* name; + Toy_ValueType valueType; + bool constant; Toy_Ast* expr; } Toy_AstVarDeclare; @@ -273,7 +275,7 @@ void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); -void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr); +void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr); void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr); void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); diff --git a/source/toy_module_compiler.c b/source/toy_module_compiler.c index 3088dc8..7c55379 100644 --- a/source/toy_module_compiler.c +++ b/source/toy_module_compiler.c @@ -148,7 +148,7 @@ static unsigned int emitString(Toy_ModuleCompiler** mb, Toy_String* str) { //move the string into the data section if (str->info.type == TOY_STRING_NODE) { - char* buffer = Toy_getStringRawBuffer(str); + char* buffer = Toy_getStringRaw(str); dataAddr = emitCStringToData(&(*mb)->data, &(*mb)->dataCapacity, &(*mb)->dataCount, buffer); @@ -157,9 +157,6 @@ static unsigned int emitString(Toy_ModuleCompiler** mb, Toy_String* str) { else if (str->info.type == TOY_STRING_LEAF) { dataAddr = emitCStringToData(&(*mb)->data, &(*mb)->dataCapacity, &(*mb)->dataCount, str->leaf.data); } - else if (str->info.type == TOY_STRING_NAME) { - dataAddr = emitCStringToData(&(*mb)->data, &(*mb)->dataCapacity, &(*mb)->dataCount, str->name.data); - } //mark the position within the jump index, reusing an existing entry if it exists for (unsigned int i = 0; i < (*mb)->jumpsCount; i++) { @@ -193,15 +190,8 @@ static unsigned int emitParameters(Toy_ModuleCompiler* mb, Toy_Ast* ast) { return 0; } - //check the string type - if (TOY_VALUE_IS_STRING(ast->value.value) == false || TOY_VALUE_AS_STRING(ast->value.value)->info.type != TOY_STRING_NAME) { - fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Function parameters must be name strings\n" TOY_CC_RESET); - mb->panic = true; - return 0; - } - //the address within the data section - unsigned int dataAddr = emitCStringToData(&(mb->data), &(mb->dataCapacity), &(mb->dataCount), TOY_VALUE_AS_STRING(ast->value.value)->name.data); + unsigned int dataAddr = emitCStringToData(&(mb->data), &(mb->dataCapacity), &(mb->dataCount), TOY_VALUE_AS_STRING(ast->value.value)->leaf.data); //check the param index for that entry i.e. don't reuse parameter names for (unsigned int i = 0; i < mb->paramCount; i++) { @@ -215,7 +205,7 @@ static unsigned int emitParameters(Toy_ModuleCompiler* mb, Toy_Ast* ast) { //emit to the param index EMIT_INT(&mb, param, dataAddr); - EMIT_INT(&mb, param, (unsigned int)(TOY_VALUE_AS_STRING(ast->value.value)->name.varType)); //don't forget this bit + EMIT_INT(&mb, param, TOY_VALUE_UNKNOWN); //TODO: encode function parameter types properly //this returns the number of written parameters return 1; @@ -293,7 +283,7 @@ static unsigned int writeInstructionUnary(Toy_ModuleCompiler** mb, Toy_AstUnary EMIT_BYTE(mb, code, TOY_OPCODE_READ); EMIT_BYTE(mb, code, TOY_VALUE_STRING); - EMIT_BYTE(mb, code, TOY_STRING_NAME); + EMIT_BYTE(mb, code, TOY_STRING_LEAF); EMIT_BYTE(mb, code, name->info.length); //store the length (max 255) emitString(mb, name); @@ -328,7 +318,7 @@ static unsigned int writeInstructionUnary(Toy_ModuleCompiler** mb, Toy_AstUnary EMIT_BYTE(mb, code, TOY_OPCODE_READ); EMIT_BYTE(mb, code, TOY_VALUE_STRING); - EMIT_BYTE(mb, code, TOY_STRING_NAME); + EMIT_BYTE(mb, code, TOY_STRING_LEAF); EMIT_BYTE(mb, code, name->info.length); //store the length (max 255) emitString(mb, name); @@ -344,7 +334,7 @@ static unsigned int writeInstructionUnary(Toy_ModuleCompiler** mb, Toy_AstUnary EMIT_BYTE(mb, code, TOY_OPCODE_READ); EMIT_BYTE(mb, code, TOY_VALUE_STRING); - EMIT_BYTE(mb, code, TOY_STRING_NAME); + EMIT_BYTE(mb, code, TOY_STRING_LEAF); EMIT_BYTE(mb, code, name->info.length); //store the length (max 255) emitString(mb, name); @@ -793,9 +783,9 @@ static unsigned int writeInstructionVarDeclare(Toy_ModuleCompiler** mb, Toy_AstV //delcare with the given name string EMIT_BYTE(mb, code, TOY_OPCODE_DECLARE); - EMIT_BYTE(mb, code, Toy_getNameStringVarType(ast.name)); + EMIT_BYTE(mb, code, ast.type); EMIT_BYTE(mb, code, ast.name->info.length); //quick optimisation to skip a 'strlen()' call - EMIT_BYTE(mb, code, Toy_getNameStringVarConstant(ast.name) ? 1 : 0); //check for constness + EMIT_BYTE(mb, code, ast.constant); //check for constness emitString(mb, ast.name); @@ -806,14 +796,14 @@ static unsigned int writeInstructionAssign(Toy_ModuleCompiler** mb, Toy_AstVarAs unsigned int result = 0; //target is a name string - if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value) && TOY_VALUE_AS_STRING(ast.target->value.value)->info.type == TOY_STRING_NAME) { + if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) { //name string Toy_String* target = TOY_VALUE_AS_STRING(ast.target->value.value); //emit the name string EMIT_BYTE(mb, code, TOY_OPCODE_READ); EMIT_BYTE(mb, code, TOY_VALUE_STRING); - EMIT_BYTE(mb, code, TOY_STRING_NAME); + EMIT_BYTE(mb, code, TOY_STRING_LEAF); EMIT_BYTE(mb, code, target->info.length); //store the length (max 255) emitString(mb, target); @@ -967,7 +957,7 @@ static unsigned int writeInstructionAssign(Toy_ModuleCompiler** mb, Toy_AstVarAs } static unsigned int writeInstructionAccess(Toy_ModuleCompiler** mb, Toy_AstVarAccess ast) { - if (!(ast.child->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.child->value.value) && TOY_VALUE_AS_STRING(ast.child->value.value)->info.type == TOY_STRING_NAME)) { + if (!(ast.child->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.child->value.value))) { fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Found a non-name-string in a value node when trying to write access\n" TOY_CC_RESET); (*mb)->panic = true; return 0; @@ -978,7 +968,7 @@ static unsigned int writeInstructionAccess(Toy_ModuleCompiler** mb, Toy_AstVarAc //push the name EMIT_BYTE(mb, code, TOY_OPCODE_READ); EMIT_BYTE(mb, code, TOY_VALUE_STRING); - EMIT_BYTE(mb, code, TOY_STRING_NAME); + EMIT_BYTE(mb, code, TOY_STRING_LEAF); EMIT_BYTE(mb, code, name->info.length); //store the length (max 255) emitString(mb, name); diff --git a/source/toy_parser.c b/source/toy_parser.c index 1179008..53c2e20 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -112,7 +112,7 @@ typedef struct ParsingTuple { static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule); -static Toy_AstFlag nameString(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); +static Toy_AstFlag identifier(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag unary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); @@ -127,7 +127,7 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_PRIMARY,literal,NULL},// TOY_TOKEN_NULL, //variable names (initially handled as a string) - {PREC_PRIMARY,nameString,NULL},// TOY_TOKEN_NAME, + {PREC_PRIMARY,identifier,NULL},// TOY_TOKEN_NAME, //types {PREC_NONE,NULL,NULL},// TOY_TOKEN_TYPE_BOOLEAN, @@ -266,9 +266,9 @@ static Toy_ValueType readType(Toy_Parser* parser) { } } -static Toy_AstFlag nameString(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { +static Toy_AstFlag identifier(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { //emit the name string - Toy_String* name = Toy_createNameStringLength(bucketHandle, parser->previous.lexeme, parser->previous.length, TOY_VALUE_UNKNOWN, false); + Toy_String* name = Toy_toStringLength(bucketHandle, parser->previous.lexeme, parser->previous.length); Toy_Value value = TOY_VALUE_FROM_STRING(name); Toy_private_emitAstValue(bucketHandle, rootHandle, value); @@ -388,7 +388,7 @@ static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As buffer[i] = '\0'; unsigned int len = i - escapeCounter; //NOTE: len is ONLY the string length - Toy_private_emitAstValue(bucketHandle, rootHandle, TOY_VALUE_FROM_STRING(Toy_createStringLength(bucketHandle, buffer, len))); + Toy_private_emitAstValue(bucketHandle, rootHandle, TOY_VALUE_FROM_STRING(Toy_toStringLength(bucketHandle, buffer, len))); return TOY_AST_FLAG_NONE; } @@ -434,7 +434,7 @@ static Toy_AstFlag unary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast* parsePrecedence(bucketHandle, parser, &primary, PREC_PRIMARY); //double check it's a name string within an access NOTE: doing some fiddling with the existing AST here - if (primary->type != TOY_AST_VAR_ACCESS || primary->varAccess.child->type != TOY_AST_VALUE || TOY_VALUE_IS_STRING(primary->varAccess.child->value.value) != true || TOY_VALUE_AS_STRING(primary->varAccess.child->value.value)->info.type != TOY_STRING_NAME) { + if (primary->type != TOY_AST_VAR_ACCESS || primary->varAccess.child->type != TOY_AST_VALUE || TOY_VALUE_IS_STRING(primary->varAccess.child->value.value) != true) { printError(parser, parser->previous, "Unexpected non-name-string token in unary-prefix operator precedence rule"); Toy_private_emitAstError(bucketHandle, rootHandle); } @@ -685,7 +685,7 @@ static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, T nameRule(bucketHandle, parser, &primary); //this is to skip the call to advance() at the beginning of parsePrecedence() //double check it's a name string within an access NOTE: doing some fiddling with the existing AST here - if (primary->type != TOY_AST_VAR_ACCESS || primary->varAccess.child->type != TOY_AST_VALUE || TOY_VALUE_IS_STRING(primary->varAccess.child->value.value) != true || TOY_VALUE_AS_STRING(primary->varAccess.child->value.value)->info.type != TOY_STRING_NAME) { + if (primary->type != TOY_AST_VAR_ACCESS || primary->varAccess.child->type != TOY_AST_VALUE || TOY_VALUE_IS_STRING(primary->varAccess.child->value.value) != true) { printError(parser, parser->previous, "Unexpected non-name-string token in unary-postfix operator precedence rule"); Toy_private_emitAstError(bucketHandle, rootHandle); return TOY_AST_FLAG_NONE; @@ -924,7 +924,7 @@ static void makeVariableDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p } //build the name string - Toy_String* nameStr = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, varType, constant); + Toy_String* nameStr = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length); //if there's an assignment, read it, or default to null Toy_Ast* expr = NULL; @@ -936,7 +936,7 @@ static void makeVariableDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p } //finally, emit the declaration as an Ast - Toy_private_emitAstVariableDeclaration(bucketHandle, rootHandle, nameStr, expr); + Toy_private_emitAstVariableDeclaration(bucketHandle, rootHandle, nameStr, varType, constant, expr); consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of var statement"); } @@ -952,7 +952,7 @@ static void makeFunctionDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p //build the name string Toy_Token nameToken = parser->previous; - Toy_String* nameStr = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, TOY_VALUE_FUNCTION, true); + Toy_String* nameStr = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length); //read the function parameters (done manually to avoid other syntax issues) Toy_Ast* params = NULL; @@ -970,23 +970,25 @@ static void makeFunctionDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p advance(parser); Toy_Token nameToken = parser->previous; + //TODO: fix this with param type info //read the type specifier if present - Toy_ValueType varType = TOY_VALUE_ANY; - bool constant = true; //parameters are immutable + // Toy_ValueType varType = TOY_VALUE_ANY; + // bool constant = true; //parameters are immutable if (match(parser, TOY_TOKEN_OPERATOR_COLON)) { - varType = readType(parser); + // varType = readType(parser); + readType(parser); if (match(parser, TOY_TOKEN_KEYWORD_CONST)) { - constant = true; + // constant = true; } } //emit the parameter as a name string - Toy_String* name = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, varType, constant); + Toy_String* name = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length); Toy_Value value = TOY_VALUE_FROM_STRING(name); Toy_Ast* ast = NULL; - Toy_private_emitAstValue(bucketHandle, &ast, value); + Toy_private_emitAstValue(bucketHandle, &ast, value); //TODO: params with type info //add to the params aggregate (is added backwards, because weird) Toy_private_emitAstAggregate(bucketHandle, ¶ms, TOY_AST_FLAG_COLLECTION, ast); diff --git a/source/toy_scope.c b/source/toy_scope.c index 69ac7d5..900ecff 100644 --- a/source/toy_scope.c +++ b/source/toy_scope.c @@ -23,61 +23,118 @@ static void incrementRefCount(Toy_Scope* scope) { static void decrementRefCount(Toy_Scope* scope) { for (Toy_Scope* iter = scope; iter; iter = iter->next) { iter->refCount--; - if (iter->refCount == 0 && iter->table != NULL) { - Toy_freeTable(iter->table); - iter->table = NULL; + if (iter->refCount == 0 && iter->data != NULL) { + //free the scope entries when this scope is no longer needed + for (unsigned int i = 0; i < iter->capacity; i++) { + Toy_freeString(&(iter->data[i].key)); + Toy_freeValue(iter->data[i].value); + } + free(iter->data); } } } -static Toy_TableEntry* lookupScope(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) { +static Toy_ScopeEntry* lookupScopeEntryPtr(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) { //terminate - if (scope == NULL) { + if (scope == NULL || scope->data == NULL) { return NULL; } - //continue after a dummy - if (scope->table == NULL) { - return recursive ? lookupScope(scope->next, key, hash, recursive) : NULL; - } - - //copy and modify the code from Toy_lookupTable, so it can behave slightly differently - unsigned int probe = hash % scope->table->capacity; + //probe for the correct location + unsigned int probe = hash % scope->capacity; while (true) { //found the entry - if (TOY_VALUE_IS_STRING(scope->table->data[probe].key) && Toy_compareStrings(TOY_VALUE_AS_STRING(scope->table->data[probe].key), key) == 0) { - return &(scope->table->data[probe]); + if (Toy_compareStrings(&(scope->data[probe].key), key) == 0) { + return &(scope->data[probe]); } //if its an empty slot (didn't find it here) - if (TOY_VALUE_IS_NULL(scope->table->data[probe].key)) { - return recursive ? lookupScope(scope->next, key, hash, recursive) : NULL; + if (scope->data[probe].key.info.length == 0) { + return recursive ? lookupScopeEntryPtr(scope->next, key, hash, recursive) : NULL; } //adjust and continue - probe = (probe + 1) % scope->table->capacity; + probe = (probe + 1) % scope->capacity; } } +void probeAndInsert(Toy_Scope* scope, Toy_String* key, Toy_Value value, Toy_ValueType type, bool constant) { + //make the entry + unsigned int probe = Toy_hashString(key) % scope->capacity; + Toy_ScopeEntry entry = (Toy_ScopeEntry){ .key = *key, .value = value, .type = type, .constant = constant, .psl = 0 }; + + //probe + while (true) { + //if we're overriding an existing value + if (Toy_compareStrings(&(scope->data[probe].key), &(entry.key)) == 0) { + scope->data[probe] = entry; + scope->maxPsl = entry.psl > scope->maxPsl ? entry.psl : scope->maxPsl; + return; + } + + //if this spot is free, insert and return + if (TOY_VALUE_IS_NULL(scope->data[probe].value)) { + scope->data[probe] = entry; + scope->count++; + scope->maxPsl = entry.psl > scope->maxPsl ? entry.psl : scope->maxPsl; + return; + } + + //if the new entry is "poorer", insert it and shift the old one + if (scope->data[probe].psl < entry.psl) { + Toy_ScopeEntry tmp = scope->data[probe]; + scope->data[probe] = entry; + entry = tmp; + } + + //adjust and continue + probe++; + probe &= scope->capacity - 1; //DOOM hack + entry.psl++; + } +} + +Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapacity) { + //allocate and zero a new Toy_ScopeEntry array in memory + Toy_ScopeEntry* newEntries = malloc(newCapacity * sizeof(Toy_ScopeEntry)); + + if (newEntries == NULL) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate space for 'Toy_Scope' entries\n" TOY_CC_RESET); + exit(1); + } + + //wipe the memory + memset(newEntries, 0, newCapacity * sizeof(Toy_ScopeEntry)); + + if (scope == NULL) { //for initial allocations + return newEntries; + } + + //movethe old data into the new block of memory + unsigned int oldCapacity = scope->capacity; + Toy_ScopeEntry* oldEntries = scope->data; + scope->capacity = newCapacity; + scope->data = newEntries; + + //for each existing entry in the old array, copy it into the new array + for (unsigned int i = 0; i < oldCapacity; i++) { + if (oldEntries[i].key.info.length > 0) { + probeAndInsert(scope, &(oldEntries[i].key), oldEntries[i].value, oldEntries[i].type, oldEntries[i].constant); + } + } + + //clean up and return + free(oldEntries); + return newEntries; +} + //exposed functions Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) { Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope)); newScope->next = scope; - newScope->table = Toy_allocateTable(); - newScope->refCount = 0; - - incrementRefCount(newScope); - - return newScope; -} - -Toy_Scope* Toy_private_pushDummyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) { - Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope)); - - newScope->next = scope; - newScope->table = NULL; + newScope->data = adjustScopeEntries(NULL, TOY_SCOPE_INITIAL_CAPACITY); newScope->refCount = 0; incrementRefCount(newScope); @@ -94,66 +151,49 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) { return scope->next; } -void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) { - if (key->info.type != TOY_STRING_NAME) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET); - exit(-1); - } - - if (scope->table == NULL) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Can't declare in a dummy scope\n" TOY_CC_RESET); - exit(-1); - } - - Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), false); +void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant) { + Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), false); if (entryPtr != NULL) { char buffer[key->info.length + 256]; - sprintf(buffer, "Can't redefine a variable: %s", key->name.data); + sprintf(buffer, "Can't redefine a variable: %s", key->leaf.data); Toy_error(buffer); return; } //type check - Toy_ValueType kt = Toy_getNameStringVarType(key); - if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != 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]; - sprintf(buffer, "Incorrect value type in declaration of '%s' (expected %s, got %s)", key->name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type)); + sprintf(buffer, "Incorrect value type in declaration of '%s' (expected %s, got %s)", key->leaf.data, Toy_private_getValueTypeAsCString(type), Toy_private_getValueTypeAsCString(value.type)); Toy_error(buffer); return; } - Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value); + probeAndInsert(scope, Toy_copyString(key), value, type, constant); } void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) { - if (key->info.type != TOY_STRING_NAME) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET); - exit(-1); - } - - Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true); + Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true); if (entryPtr == NULL) { char buffer[key->info.length + 256]; - sprintf(buffer, "Undefined variable: %s\n", key->name.data); + sprintf(buffer, "Undefined variable: %s\n", key->leaf.data); Toy_error(buffer); return; } //type check - Toy_ValueType kt = Toy_getNameStringVarType( TOY_VALUE_AS_STRING(entryPtr->key) ); - if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != 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]; - sprintf(buffer, "Incorrect value type in assignment of '%s' (expected %s, got %s)", key->name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type)); + sprintf(buffer, "Incorrect value type in assignment of '%s' (expected %s, got %s)", key->leaf.data, Toy_private_getValueTypeAsCString(entryPtr->type), Toy_private_getValueTypeAsCString(value.type)); Toy_error(buffer); return; } //constness check - if (Toy_getNameStringVarConstant( TOY_VALUE_AS_STRING(entryPtr->key) )) { + if (entryPtr->constant) { char buffer[key->info.length + 256]; - sprintf(buffer, "Can't reassign to constant variable %s", key->name.data); + sprintf(buffer, "Can't assign to a constant variable %s", key->leaf.data); Toy_error(buffer); return; } @@ -162,16 +202,11 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) { } Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) { - if (key->info.type != TOY_STRING_NAME) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET); - exit(-1); - } - - Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true); + Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true); if (entryPtr == NULL) { char buffer[key->info.length + 256]; - sprintf(buffer, "Undefined variable: %s\n", key->name.data); + sprintf(buffer, "Undefined variable: %s\n", key->leaf.data); Toy_error(buffer); return NULL; } @@ -180,12 +215,6 @@ Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) { } bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key) { - if (key->info.type != TOY_STRING_NAME) { - fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET); - exit(-1); - } - - Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true); - + Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true); return entryPtr != NULL; } diff --git a/source/toy_scope.h b/source/toy_scope.h index 5793a90..fee59c1 100644 --- a/source/toy_scope.h +++ b/source/toy_scope.h @@ -3,27 +3,50 @@ #include "toy_common.h" #include "toy_bucket.h" -#include "toy_value.h" #include "toy_string.h" -#include "toy_table.h" +#include "toy_value.h" -//wraps Toy_Table, restricting keys to name strings, and handles scopes as a linked list +//keys are leaf-only strings +typedef struct Toy_ScopeEntry { + Toy_String key; + Toy_Value value; + Toy_ValueType type; + bool constant; + unsigned int psl; +} Toy_ScopeEntry; + +//holds a table-like collection of variables TODO: check bitness typedef struct Toy_Scope { struct Toy_Scope* next; - Toy_Table* table; unsigned int refCount; + unsigned int capacity; + unsigned int count; + unsigned int maxPsl; + Toy_ScopeEntry* data; } Toy_Scope; //handle deep scopes - the scope is stored in the bucket, not the table TOY_API Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope); TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope); -TOY_API Toy_Scope* Toy_private_pushDummyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope); //doesn't delcare a table for storage //manage the contents -TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value); +TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant); TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value); TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key); TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key); -//TODO: delcare with a custom table (game engine entities) \ No newline at end of file +//some useful sizes, could be swapped out as needed +#ifndef TOY_SCOPE_INITIAL_CAPACITY +#define TOY_SCOPE_INITIAL_CAPACITY 8 +#endif + +//NOTE: The DOOM hack needs a power of 2 +#ifndef TOY_SCOPE_EXPANSION_RATE +#define TOY_SCOPE_EXPANSION_RATE 2 +#endif + +//expand when the contents passes a certain percentage (80%) of the capacity +#ifndef TOY_SCOPE_EXPANSION_THRESHOLD +#define TOY_SCOPE_EXPANSION_THRESHOLD 0.8f +#endif diff --git a/source/toy_string.c b/source/toy_string.c index e67f96a..c324b37 100644 --- a/source/toy_string.c +++ b/source/toy_string.c @@ -9,18 +9,6 @@ //utils #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) -static void deepCopyUtil(char* dest, Toy_String* str) { - //sometimes, "clever" can be a bad thing... - if (str->info.type == TOY_STRING_NODE) { - deepCopyUtil(dest, str->node.left); - deepCopyUtil(dest + str->node.left->info.length, str->node.right); - } - - else { - memcpy(dest, str->leaf.data, str->info.length); - } -} - static void incrementRefCount(Toy_String* str) { str->info.refCount++; if (str->info.type == TOY_STRING_NODE) { @@ -37,69 +25,19 @@ static void decrementRefCount(Toy_String* str) { } } -static unsigned int hashCString(const char* string) { - unsigned int hash = 2166136261u; - - for (unsigned int i = 0; string[i]; i++) { - hash *= string[i]; - hash ^= 16777619; - } - - return hash; +//exposed functions +Toy_String* Toy_toString(Toy_Bucket** bucketHandle, const char* cstring) { + return Toy_toStringLength(bucketHandle, cstring, strlen(cstring)); } -static Toy_String* partitionStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) { - Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String) + length + 1); +Toy_String* Toy_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) { + Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String)); ret->info.type = TOY_STRING_LEAF; ret->info.length = length; ret->info.refCount = 1; ret->info.cachedHash = 0; //don't calc until needed - memcpy(ret->leaf.data, cstring, length + 1); - ret->leaf.data[length] = '\0'; - - return ret; -} - -//exposed functions -Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring) { - unsigned int length = strlen(cstring); - - return Toy_createStringLength(bucketHandle, cstring, length); -} - -Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) { - //normal behaviour - if (length < (*bucketHandle)->capacity - sizeof(Toy_String) - 1) { - return partitionStringLength(bucketHandle, cstring, length); - } - - //break the string up if it's too long - Toy_String* result = NULL; - - for (unsigned int i = 0; i < length; i += (*bucketHandle)->capacity - sizeof(Toy_String) - 1) { //increment by the amount actually used by the cstring - unsigned int amount = MIN((length - i), (*bucketHandle)->capacity - sizeof(Toy_String) - 1); - Toy_String* fragment = partitionStringLength(bucketHandle, cstring + i, amount); - - result = result == NULL ? fragment : Toy_concatStrings(bucketHandle, result, fragment); - } - - return result; -} - -Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType varType, bool constant) { - assert(varType != TOY_VALUE_NULL && "Can't declare a name string with variable type 'null'"); - - Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String) + length + 1); - - ret->info.type = TOY_STRING_NAME; - ret->info.length = length; - ret->info.refCount = 1; - ret->info.cachedHash = 0; //don't calc until needed - memcpy(ret->name.data, cname, length + 1); - ret->name.data[length] = '\0'; - ret->name.varType = varType; - ret->name.varConstant = constant; + ret->leaf.data = cstring; //don't make a local copy return ret; } @@ -110,42 +48,8 @@ Toy_String* Toy_copyString(Toy_String* str) { return str; } -Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str) { - assert(str->info.refCount != 0 && "Can't deep copy a string with refcount of zero"); - - //handle deep copies of strings that are too long for the bucket capacity - if (sizeof(Toy_String) + str->info.length + 1 > (*bucketHandle)->capacity) { - char* buffer = Toy_getStringRawBuffer(str); - Toy_String* result = Toy_createStringLength(bucketHandle, buffer, str->info.length); //handles the fragmenting - free(buffer); - return result; - } - - Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String) + str->info.length + 1); - - if (str->info.type == TOY_STRING_NODE || str->info.type == TOY_STRING_LEAF) { - ret->info.type = TOY_STRING_LEAF; - ret->info.length = str->info.length; - ret->info.refCount = 1; - ret->info.cachedHash = str->info.cachedHash; - deepCopyUtil(ret->leaf.data, str); //copy each leaf into the buffer - ret->leaf.data[ret->info.length] = '\0'; - } - else { - ret->info.type = TOY_STRING_NAME; - ret->info.length = str->info.length; - ret->info.refCount = 1; - ret->info.cachedHash = str->info.cachedHash; - memcpy(ret->name.data, str->name.data, str->info.length + 1); - ret->name.data[ret->info.length] = '\0'; - } - - return ret; -} - Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right) { assert(left->info.refCount != 0 && right->info.refCount != 0 && "Can't concatenate a string with a refcount of zero"); - assert(left->info.type != TOY_STRING_NAME && right->info.type != TOY_STRING_NAME && "Can't concatenate a name string"); Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String)); @@ -175,21 +79,20 @@ unsigned int Toy_getStringRefCount(Toy_String* str) { return str->info.refCount; } -Toy_ValueType Toy_getNameStringVarType(Toy_String* str) { - assert(str->info.type == TOY_STRING_NAME && "Can't get the variable type of a non-name string"); +static void getStringRawUtil(char* dest, Toy_String* str) { + //sometimes, "clever" can be a bad thing... + if (str->info.type == TOY_STRING_NODE) { + getStringRawUtil(dest, str->node.left); + getStringRawUtil(dest + str->node.left->info.length, str->node.right); + } - return str->name.varType; + else { + memcpy(dest, str->leaf.data, str->info.length); + } } -bool Toy_getNameStringVarConstant(Toy_String* str) { - assert(str->info.type == TOY_STRING_NAME && "Can't get the variable constness of a non-name string"); - - return str->name.varConstant; -} - -char* Toy_getStringRawBuffer(Toy_String* str) { - assert(str->info.type != TOY_STRING_NAME && "Can't get raw string buffer of a name string"); - assert(str->info.refCount != 0 && "Can't get raw string buffer of a string with refcount of zero"); +char* Toy_getStringRaw(Toy_String* str) { + assert(str->info.refCount != 0 && "Can't build a raw string from a string with refcount of zero"); //BUGFIX: Make sure it's aligned, and there's space for the null unsigned int len = (str->info.length + 3) & ~3; @@ -199,7 +102,7 @@ char* Toy_getStringRawBuffer(Toy_String* str) { char* buffer = malloc(len); - deepCopyUtil(buffer, str); + getStringRawUtil(buffer, str); buffer[str->info.length] = '\0'; return buffer; @@ -284,11 +187,6 @@ int Toy_compareStrings(Toy_String* left, Toy_String* right) { return left->info.length - right->info.length; } - if (left->info.type == TOY_STRING_NAME || right->info.type == TOY_STRING_NAME) { - assert (left->info.type == right->info.type && "Can't compare a name string to a non-name string"); - return strncmp(left->name.data, right->name.data, left->info.length); - } - //util pointers const char* leftHead = NULL; const char* rightHead = NULL; @@ -296,22 +194,30 @@ int Toy_compareStrings(Toy_String* left, Toy_String* right) { return deepCompareUtil(left, right, &leftHead, &rightHead); } +static unsigned int hashCString(const char* string) { + unsigned int hash = 2166136261u; + + for (unsigned int i = 0; string[i]; i++) { + hash *= string[i]; + hash ^= 16777619; + } + + return hash; +} + unsigned int Toy_hashString(Toy_String* str) { if (str->info.cachedHash != 0) { 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_getStringRawBuffer(str); + char* buffer = Toy_getStringRaw(str); str->info.cachedHash = hashCString(buffer); free(buffer); } - else if (str->info.type == TOY_STRING_LEAF) { + else { str->info.cachedHash = hashCString(str->leaf.data); } - else if (str->info.type == TOY_STRING_NAME) { - str->info.cachedHash = hashCString(str->name.data); - } return str->info.cachedHash; } diff --git a/source/toy_string.h b/source/toy_string.h index f877995..42fe622 100644 --- a/source/toy_string.h +++ b/source/toy_string.h @@ -3,7 +3,6 @@ #include "toy_common.h" #include "toy_bucket.h" -#include "toy_value.h" //forward declare union Toy_String_t; @@ -12,7 +11,6 @@ union Toy_String_t; typedef enum Toy_StringType { TOY_STRING_NODE, TOY_STRING_LEAF, - TOY_STRING_NAME, } Toy_StringType; typedef struct Toy_StringInfo { @@ -30,43 +28,28 @@ typedef struct Toy_StringNode { typedef struct Toy_StringLeaf { Toy_StringInfo _padding; - char data[]; + const char* data; } Toy_StringLeaf; -typedef struct Toy_StringName { - Toy_StringInfo _padding; - Toy_ValueType varType; - bool varConstant; - char data[]; -} Toy_StringName; - typedef union Toy_String_t { Toy_StringInfo info; Toy_StringNode node; Toy_StringLeaf leaf; - Toy_StringName name; } Toy_String; // -TOY_API Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring); -TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length); - -TOY_API Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType varType, bool constant); //for variable names +TOY_API Toy_String* Toy_toString(Toy_Bucket** bucketHandle, const char* cstring); +TOY_API Toy_String* Toy_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length); TOY_API Toy_String* Toy_copyString(Toy_String* str); -TOY_API Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str); - TOY_API Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right); TOY_API void Toy_freeString(Toy_String* str); TOY_API unsigned int Toy_getStringLength(Toy_String* str); TOY_API unsigned int Toy_getStringRefCount(Toy_String* str); -TOY_API Toy_ValueType Toy_getNameStringVarType(Toy_String* str); -TOY_API bool Toy_getNameStringVarConstant(Toy_String* str); - -TOY_API char* Toy_getStringRawBuffer(Toy_String* str); //allocates the buffer on the heap, needs to be freed +TOY_API char* Toy_getStringRaw(Toy_String* str); //allocates the buffer on the heap, needs to be freed TOY_API int Toy_compareStrings(Toy_String* left, Toy_String* right); //return value mimics strcmp() TOY_API unsigned int Toy_hashString(Toy_String* string); diff --git a/source/toy_value.c b/source/toy_value.c index f7ba2df..6d155a1 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -434,7 +434,7 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) { switch(value.type) { case TOY_VALUE_NULL: - return Toy_createString(bucketHandle, ""); + return Toy_createString(bucketHandle, ""); //TODO: remake "createString" to to handle params like this case TOY_VALUE_BOOLEAN: return Toy_createString(bucketHandle, value.as.boolean ? "" : "");