#include "gravity_symboltable.h"
#include "gravity_macros.h"
#include "gravity_array.h"
#include "gravity_hash.h"
typedef marray_t(gravity_hash_t*) ghash_r;
#define scope_stack_init(r) marray_init(*r)
#define scope_stack_size(r) marray_size(*r)
#define scope_stack_get(r,n) marray_get(*r,n)
#define scope_stack_free(r) marray_destroy(*r)
#define scope_stack_push(r,hash) marray_push(gravity_hash_t*,*r,hash)
#define scope_stack_pop(r) (marray_size(*r) ? marray_pop(*r) : NULL)
#define VALUE_FROM_NODE(x) ((gravity_value_t){.isa = NULL, .p = (gravity_object_t *)(x)})
#define VALUE_AS_NODE(x) ((gnode_t *)VALUE_AS_OBJECT(x))
struct symboltable_t {
ghash_r *stack;
uint32_t counter;
};
static void check_upvalue_inscope (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
#pragma unused(hashtable, key)
uint32_t index = *(uint32_t *)data;
gnode_t *node = VALUE_AS_NODE(value);
if (NODE_ISA(node, NODE_VARIABLE)) {
gnode_var_t *var = (gnode_var_t *)node;
if (var->upvalue) {
if ((index == UINT32_MAX) || (var->index < index)) *(uint32_t *)data = var->index;
}
}
}
static void symboltable_hash_free (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
#pragma unused(hashtable, value, data)
gravity_value_free(NULL, key);
}
static void symboltable_keyvalue_free (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
#pragma unused(hashtable, data)
gnode_t *node = VALUE_AS_NODE(value);
gnode_free(node);
gravity_value_free(NULL, key);
}
symboltable_t *symboltable_create (bool is_enum) {
symboltable_t *table = mem_alloc(sizeof(symboltable_t));
gravity_hash_t *hash = gravity_hash_create(0, gravity_value_hash, gravity_value_equals,
(is_enum) ? symboltable_keyvalue_free : symboltable_hash_free, NULL);
if (!table) return NULL;
table->counter = 0;
table->stack = mem_alloc(sizeof(ghash_r));
scope_stack_init(table->stack);
scope_stack_push(table->stack, hash);
return table;
}
void symboltable_free (symboltable_t *table) {
size_t i, n = scope_stack_size(table->stack);
for (i=0; i<n; ++i) {
gravity_hash_t *h = scope_stack_get(table->stack, i);
gravity_hash_free(h);
}
if (table->stack) {
scope_stack_free(table->stack);
mem_free(table->stack);
}
mem_free(table);
}
bool symboltable_insert (symboltable_t *table, const char *identifier, gnode_t *node) {
size_t n = scope_stack_size(table->stack);
gravity_hash_t *h = scope_stack_get(table->stack, n-1);
gravity_value_t key = VALUE_FROM_CSTRING(NULL, identifier);
if (gravity_hash_lookup(h, key) != NULL) {
gravity_value_free(NULL, key);
return false;
}
gravity_hash_insert(h, key, VALUE_FROM_NODE(node));
++table->counter;
return true;
}
gnode_t *symboltable_lookup (symboltable_t *table, const char *identifier) {
STATICVALUE_FROM_STRING(key, identifier, strlen(identifier));
size_t n = scope_stack_size(table->stack);
for (int i=(int)n-1; i>=0; --i) {
gravity_hash_t *h = scope_stack_get(table->stack, i);
gravity_value_t *v = gravity_hash_lookup(h, key);
if (v) return VALUE_AS_NODE(*v);
}
return NULL;
}
uint32_t symboltable_count (symboltable_t *table, uint32_t index) {
gravity_hash_t *h = scope_stack_get(table->stack, index);
return gravity_hash_count(h);
}
gnode_t *symboltable_global_lookup (symboltable_t *table, const char *identifier) {
gravity_hash_t *h = scope_stack_get(table->stack, 0);
STATICVALUE_FROM_STRING(key, identifier, strlen(identifier));
gravity_value_t *v = gravity_hash_lookup(h, key);
return (v) ? VALUE_AS_NODE(*v) : NULL;
}
void symboltable_enter_scope (symboltable_t *table) {
gravity_hash_t *h = gravity_hash_create(0, gravity_value_hash, gravity_value_equals, symboltable_hash_free, NULL);
scope_stack_push(table->stack, h);
}
uint32_t symboltable_local_index (symboltable_t *table) {
return table->counter - 1;
}
uint32_t symboltable_exit_scope (symboltable_t *table, uint32_t *nlevel) {
gravity_hash_t *h = (gravity_hash_t *)scope_stack_pop(table->stack);
if (nlevel) {
*nlevel = UINT32_MAX;
gravity_hash_iterate(h, check_upvalue_inscope, (void *)nlevel);
}
gravity_hash_free(h);
return table->counter;
}
void symboltable_dump (symboltable_t *table) {
size_t n = scope_stack_size(table->stack);
for (int i=(int)n-1; i>=0; --i) {
gravity_hash_t *h = (gravity_hash_t *)scope_stack_get(table->stack, i);
gravity_hash_dump(h);
}
}