mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
I added reference values in 62ca7a1fb7,
but forgot to mention it. I'm now using references to assign to the
internals of an array, no matter how many levels deep it is.
194 lines
5.6 KiB
C
194 lines
5.6 KiB
C
#include "toy_scope.h"
|
|
#include "toy_console_colors.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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--;
|
|
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;
|
|
}
|
|
|
|
//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_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) {
|
|
if (scope == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
decrementRefCount(scope);
|
|
return scope->next;
|
|
}
|
|
|
|
Toy_Scope* Toy_deepCopyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
|
//copy/pasted from pushScope, so I can allocate the table manually
|
|
Toy_Scope* newScope = Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope));
|
|
|
|
newScope->next = scope->next;
|
|
newScope->table = Toy_private_adjustTableCapacity(NULL, scope->table->capacity);
|
|
newScope->refCount = 0;
|
|
|
|
incrementRefCount(newScope);
|
|
|
|
//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, Toy_copyValue(scope->table->data[i].key), Toy_copyValue(scope->table->data[i].value));
|
|
}
|
|
}
|
|
|
|
return newScope;
|
|
}
|
|
|
|
void Toy_declareScope(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_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), false);
|
|
|
|
if (entryPtr != NULL) {
|
|
char buffer[key->length + 256];
|
|
sprintf(buffer, "Can't redefine a variable: %s", key->as.name.data);
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
//type check
|
|
Toy_ValueType kt = Toy_getNameStringType(key);
|
|
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type && value.type != TOY_VALUE_REFERENCE) {
|
|
char buffer[key->length + 256];
|
|
sprintf(buffer, "Incorrect value type in declaration of '%s' (expected %s, got %s)", key->as.name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type));
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
//constness check
|
|
if (Toy_getNameStringConstant(key) && value.type == TOY_VALUE_NULL) {
|
|
char buffer[key->length + 256];
|
|
sprintf(buffer, "Can't declare %s as const with value 'null'", key->as.name.data);
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value);
|
|
}
|
|
|
|
|
|
//TODO: check for clearign old values
|
|
void Toy_assignScope(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_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
|
|
if (entryPtr == NULL) {
|
|
char buffer[key->length + 256];
|
|
sprintf(buffer, "Undefined variable: %s\n", key->as.name.data);
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
//type check
|
|
Toy_ValueType kt = Toy_getNameStringType( 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->length + 256];
|
|
sprintf(buffer, "Incorrect value type in assignment of '%s' (expected %s, got %s)", key->as.name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type));
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
//constness check
|
|
if (Toy_getNameStringConstant( TOY_VALUE_AS_STRING(entryPtr->key) )) {
|
|
char buffer[key->length + 256];
|
|
sprintf(buffer, "Can't assign to const %s", key->as.name.data);
|
|
Toy_error(buffer);
|
|
return;
|
|
}
|
|
|
|
entryPtr->value = value;
|
|
}
|
|
|
|
Toy_Value* Toy_accessScopeAsPointer(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_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
|
|
if (entryPtr == NULL) {
|
|
char buffer[key->length + 256];
|
|
sprintf(buffer, "Undefined variable: %s\n", key->as.name.data);
|
|
Toy_error(buffer);
|
|
NULL;
|
|
}
|
|
|
|
return &(entryPtr->value);
|
|
}
|
|
|
|
bool Toy_isDeclaredScope(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_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
|
|
return entryPtr != NULL;
|
|
}
|