#include "toy_scope.h" #include "toy_console_colors.h" #include #include #include #include "toy_print.h" //utils static void incrementRefCount(Toy_Scope* scope) { for (Toy_Scope* iter = scope; iter; iter = iter->next) { //check for issues if (iter->next != NULL && iter->next->refCount == 0) { fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope's ancestor has a refcount of 0'\n" TOY_CC_RESET); exit(-1); } iter->refCount++; } } 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; } } } static Toy_TableEntry* lookupScope(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) { //terminate if (scope == 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; 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 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; } //adjust and continue probe = (probe + 1) % scope->table->capacity; } } //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->refCount = 0; incrementRefCount(newScope); return newScope; } Toy_Scope* Toy_popScope(Toy_Scope* scope) { if (scope == NULL) { return NULL; } decrementRefCount(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); if (entryPtr != NULL) { char buffer[key->info.length + 256]; sprintf(buffer, "Can't redefine a variable: %s", key->name.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) { 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)); Toy_error(buffer); return; } Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value); } 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); if (entryPtr == NULL) { char buffer[key->info.length + 256]; sprintf(buffer, "Undefined variable: %s\n", key->name.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) { 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)); Toy_error(buffer); return; } //constness check if (Toy_getNameStringVarConstant( TOY_VALUE_AS_STRING(entryPtr->key) )) { char buffer[key->info.length + 256]; sprintf(buffer, "Can't reassign to constant variable %s", key->name.data); Toy_error(buffer); return; } entryPtr->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); if (entryPtr == NULL) { char buffer[key->info.length + 256]; sprintf(buffer, "Undefined variable: %s\n", key->name.data); Toy_error(buffer); return NULL; } return &(entryPtr->value); } 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); return entryPtr != NULL; }