#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) { iter->refCount++; } } static void decrementRefCount(Toy_Scope* scope) { for (Toy_Scope* iter = scope; iter; iter = iter->next) { iter->refCount--; } } static Toy_Value* lookupScope(Toy_Scope* scope, Toy_Value key, unsigned int hash, bool recursive) { //terminate if (scope == NULL) { return 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_VALUES_ARE_EQUAL(scope->table->data[probe].key, key)) { return &(scope->table->data[probe].value); } //if its an empty slot 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_partitionBucket(bucketHandle, sizeof(Toy_Scope)); newScope->next = scope; newScope->table = Toy_allocateTable(); newScope->refCount = 0; incrementRefCount(newScope); return newScope; } Toy_Scope* Toy_popScope(Toy_Scope* scope) { decrementRefCount(scope); if (scope->refCount == 0) { Toy_freeTable(scope->table); scope->table = NULL; } return scope->next; } Toy_Scope* deepCopyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) { Toy_Scope* newScope = Toy_pushScope(bucketHandle, scope->next); //forcibly copy the contents for (int i = 0; i < scope->table->capacity; i++) { if (!TOY_VALUE_IS_NULL(scope->table->data[i].key)) { Toy_insertTable(&newScope->table, scope->table->data[i].key, scope->table->data[i].value); } } return newScope; } void Toy_declareScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key, Toy_Value value) { if (key.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_Value* valuePtr = lookupScope(scope, key, Toy_hashValue(key), false); if (valuePtr != NULL) { char buffer[key.length + 256]; snprintf(buffer, "Can't redefine a variable: %s", key.as.name.data); Toy_error(buffer); return; } Toy_insertTable(&scope->table, key, value); } void Toy_assignScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key, Toy_Value value) { if (key.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_Value* valuePtr = lookupScope(scope, key, Toy_hashValue(key), true); if (valuePtr == NULL) { char buffer[key.length + 256]; snprintf(buffer, "Undefined variable: %s", key.as.name.data); Toy_error(buffer); return; } *valuePtr = value; } Toy_Value Toy_accessScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key) { if (key.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_Value* valuePtr = lookupScope(scope, key, Toy_hashValue(key), true); if (valuePtr == NULL) { char buffer[key.length + 256]; snprintf(buffer, "Undefined variable: %s", key.as.name.data); Toy_error(buffer); return; } return *valuePtr; } bool Toy_isDeclaredScope(Toy_Bucket** bucketHandle, Toy_Scope* scope, Toy_String key) { if (key.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_Value* valuePtr = lookupScope(scope, key, Toy_hashValue(key), true); return valuePtr != NULL; }