#include #include "hashmap.h" #include "scope.h" typedef struct apfl_value *variable; void apfl_scope_deinit(struct apfl_allocator allocator, struct scope *scope) { (void)allocator; apfl_hashmap_deinit(&scope->map); } static bool scope_keys_eq(void *opaque, const void *_a, const void *_b) { (void)opaque; const struct apfl_string * const *a = _a; const struct apfl_string * const *b = _b; return apfl_string_eq(**a, **b); } static apfl_hash scope_calc_hash(void *opaque, const void *_key) { (void)opaque; const struct apfl_string *const*key = _key; struct apfl_string_view sv = apfl_string_view_from(**key); return apfl_hash_fnv1a(sv.bytes, sv.len); } struct scope * apfl_scope_new(struct gc *gc) { struct apfl_hashmap map; if (!apfl_hashmap_init( &map, gc->allocator, (struct apfl_hashmap_callbacks) { .opaque = NULL, .keys_eq = scope_keys_eq, .calc_hash = scope_calc_hash, }, sizeof(struct apfl_string *), sizeof(variable) )) { return NULL; } struct scope *scope = apfl_gc_new_scope(gc); if (scope == NULL) { apfl_hashmap_deinit(&map); return NULL; } *scope = (struct scope) { .map = map, }; return scope; } void apfl_gc_var_traverse(struct apfl_value *var, gc_visitor cb, void *opaque) { apfl_value_visit_gc_object(*var, cb, opaque); } void apfl_gc_scope_traverse(struct scope *scope, gc_visitor cb, void *opaque) { HASHMAP_EACH(&scope->map, cur) { struct apfl_string **k = apfl_hashmap_cursor_peek_key(cur); assert(k != NULL); cb(opaque, GC_OBJECT_FROM(*k, GC_TYPE_STRING)); variable *v = apfl_hashmap_cursor_peek_value(cur); assert(v != NULL); cb(opaque, GC_OBJECT_FROM(*v, GC_TYPE_VAR)); } } bool apfl_scope_get(struct scope *scope, struct apfl_string *name, struct apfl_value *out) { variable var; if (!apfl_hashmap_get(&scope->map, &name, &var)) { return false; } // The value is now in the variable and outside of it (presumably to be // saved onto the stack). We need to set the COW flag so a mutation of one // copy doesn't affect the other one. *out = apfl_value_set_cow_flag(*var); return true; } bool apfl_scope_has(struct scope *scope, struct apfl_string *name) { return apfl_hashmap_isset(&scope->map, &name); } static variable get_or_create_variable(struct gc *gc, struct scope *scope, struct apfl_string *name) { variable var; if (apfl_hashmap_get(&scope->map, &name, &var)) { return var; } struct apfl_hashmap_prepared_set prepared_set; if (!apfl_hashmap_prepare_set(&scope->map, &prepared_set, &name)) { return NULL; } var = apfl_gc_new_var(gc); if (var == NULL) { return NULL; } *var = (struct apfl_value) { .type = VALUE_NIL }; apfl_hashmap_set_prepared(prepared_set, &var); return var; } bool apfl_scope_set(struct gc *gc, struct scope *scope, struct apfl_string *name, struct apfl_value value) { variable var = get_or_create_variable(gc, scope, name); if (var == NULL) { return false; } *var = value; return true; } bool apfl_scope_update_existing(struct scope *scope, struct apfl_string *name, struct apfl_value value) { variable var; if (!apfl_hashmap_get(&scope->map, &name, &var)) { return false; } *var = value; return true; } bool apfl_scope_create_var(struct gc *gc, struct scope *scope, struct apfl_string *name) { return get_or_create_variable(gc, scope, name) != NULL; } bool apfl_scope_merge_into(struct scope *self, struct scope *other) { if (self == NULL || other == NULL) { return NULL; } HASHMAP_EACH(&other->map, cur) { struct apfl_string **k = apfl_hashmap_cursor_peek_key(cur); assert(k != NULL); variable *v = apfl_hashmap_cursor_peek_value(cur); assert(v != NULL); if (!apfl_hashmap_set(&self->map, k, v)) { return false; } } return true; }