apfl/src/scope.c

185 lines
4.1 KiB
C
Raw Normal View History

#include <assert.h>
#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)
{
struct gc_object *child = apfl_value_get_gc_object(*var);
if (child != NULL) {
cb(opaque, child);
}
}
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;
}