mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
215 lines
4.8 KiB
C
215 lines
4.8 KiB
C
#include "scope.h"
|
|
|
|
#include "memory.h"
|
|
|
|
//run up the ancestor chain, freeing anything with 0 references left
|
|
static void freeAncestorChain(Scope* scope) {
|
|
scope->references--;
|
|
|
|
//free scope chain
|
|
if (scope->ancestor != NULL) {
|
|
freeAncestorChain(scope->ancestor);
|
|
}
|
|
|
|
if (scope->references > 0) {
|
|
return;
|
|
}
|
|
|
|
freeLiteralDictionary(&scope->variables);
|
|
freeLiteralDictionary(&scope->types);
|
|
|
|
FREE(Scope, scope);
|
|
}
|
|
|
|
//return false if invalid type
|
|
static bool checkType(Literal typeLiteral, Literal value) {
|
|
//for any types
|
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_ANY) {
|
|
return true;
|
|
}
|
|
|
|
//don't allow null types
|
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_NULL) {
|
|
return false;
|
|
}
|
|
|
|
//always allow null values
|
|
if (IS_NULL(value)) {
|
|
return true;
|
|
}
|
|
|
|
//for each type, if a mismatch is found, return false
|
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_BOOLEAN && !IS_BOOLEAN(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_INTEGER && !IS_INTEGER(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_FLOAT && !IS_FLOAT(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_STRING && !IS_STRING(value)) {
|
|
return false;
|
|
}
|
|
|
|
if (IS_ARRAY(value)) {
|
|
//check value's type
|
|
if (AS_TYPE(typeLiteral).typeOf != LITERAL_ARRAY) {
|
|
return false;
|
|
}
|
|
|
|
//check children
|
|
for (int i = 0; i < AS_ARRAY(value)->count; i++) {
|
|
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], AS_ARRAY(value)->literals[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IS_DICTIONARY(value)) {
|
|
//check value's type
|
|
if (AS_TYPE(typeLiteral).typeOf != LITERAL_DICTIONARY) {
|
|
return false;
|
|
}
|
|
|
|
//check children
|
|
for (int i = 0; i < AS_DICTIONARY(value)->capacity; i++) {
|
|
//only assigned and non-tombstoned keys
|
|
if (!IS_NULL(AS_DICTIONARY(value)->entries[i].key)) {
|
|
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[0], AS_DICTIONARY(value)->entries[i].key)) {
|
|
return false;
|
|
}
|
|
|
|
if (!checkType(((Literal*)(AS_TYPE(typeLiteral).subtypes))[1], AS_DICTIONARY(value)->entries[i].value)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IS_FUNCTION(value)) {
|
|
//check value's type
|
|
if (AS_TYPE(typeLiteral).typeOf != LITERAL_FUNCTION) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (AS_TYPE(typeLiteral).typeOf == LITERAL_TYPE && !IS_TYPE(value)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//exposed functions
|
|
Scope* pushScope(Scope* ancestor) {
|
|
Scope* scope = ALLOCATE(Scope, 1);
|
|
scope->ancestor = ancestor;
|
|
initLiteralDictionary(&scope->variables);
|
|
initLiteralDictionary(&scope->types);
|
|
|
|
//tick up all scope reference counts
|
|
scope->references = 0;
|
|
for (Scope* ptr = scope; ptr != NULL; ptr = ptr->ancestor) {
|
|
ptr->references++;
|
|
}
|
|
|
|
return scope;
|
|
}
|
|
|
|
Scope* popScope(Scope* scope) {
|
|
Scope* ret = scope->ancestor;
|
|
|
|
freeAncestorChain(scope);
|
|
|
|
return ret;
|
|
}
|
|
|
|
//returns false if error
|
|
bool declareScopeVariable(Scope* scope, Literal key, Literal type) {
|
|
//don't redefine a variable within this scope
|
|
if (existsLiteralDictionary(&scope->variables, key)) {
|
|
return false;
|
|
}
|
|
|
|
//store the type, for later checking on assignment
|
|
setLiteralDictionary(&scope->types, key, type);
|
|
|
|
setLiteralDictionary(&scope->variables, key, TO_NULL_LITERAL);
|
|
return true;
|
|
}
|
|
|
|
bool isDelcaredScopeVariable(Scope* scope, Literal key) {
|
|
if (scope == NULL) {
|
|
return false;
|
|
}
|
|
|
|
//if it's not in this scope, keep searching up the chain
|
|
if (!existsLiteralDictionary(&scope->variables, key)) {
|
|
return isDelcaredScopeVariable(scope->ancestor, key);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//return false if undefined, or can't be assigned
|
|
bool setScopeVariable(Scope* scope, Literal key, Literal value, bool constCheck) {
|
|
//dead end
|
|
if (scope == NULL) {
|
|
return false;
|
|
}
|
|
|
|
//if it's not in this scope, keep searching up the chain
|
|
if (!existsLiteralDictionary(&scope->variables, key)) {
|
|
return setScopeVariable(scope->ancestor, key, value, constCheck);
|
|
}
|
|
|
|
//type checking
|
|
Literal typeLiteral = getLiteralDictionary(&scope->types, key);
|
|
|
|
if (!checkType(typeLiteral, value)) {
|
|
return false;
|
|
}
|
|
|
|
//const check
|
|
if (constCheck && (AS_TYPE(typeLiteral).constant)) {
|
|
return false;
|
|
}
|
|
|
|
//actually assign
|
|
setLiteralDictionary(&scope->variables, key, value);
|
|
return true;
|
|
}
|
|
|
|
bool getScopeVariable(Scope* scope, Literal key, Literal* valueHandle) {
|
|
//dead end
|
|
if (scope == NULL) {
|
|
return false;
|
|
}
|
|
|
|
//if it's not in this scope, keep searching up the chain
|
|
if (!existsLiteralDictionary(&scope->variables, key)) {
|
|
return getScopeVariable(scope->ancestor, key, valueHandle);
|
|
}
|
|
|
|
*valueHandle = getLiteralDictionary(&scope->variables, key);
|
|
return true;
|
|
}
|
|
|
|
Literal getScopeType(Scope* scope, Literal key) {
|
|
//dead end
|
|
if (scope == NULL) {
|
|
return TO_NULL_LITERAL;
|
|
}
|
|
|
|
//if it's not in this scope, keep searching up the chain
|
|
if (!existsLiteralDictionary(&scope->types, key)) {
|
|
return getScopeType(scope->ancestor, key);
|
|
}
|
|
|
|
return getLiteralDictionary(&scope->types, key);
|
|
}
|