2022-04-15 12:41:22 +00:00
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 19:41:05 +00:00
|
|
|
bool
|
|
|
|
|
apfl_scope_has(struct scope *scope, struct apfl_string *name)
|
|
|
|
|
{
|
|
|
|
|
return apfl_hashmap_isset(&scope->map, &name);
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-15 12:41:22 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 19:41:05 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-15 12:41:22 +00:00
|
|
|
bool
|
|
|
|
|
apfl_scope_create_var(struct gc *gc, struct scope *scope, struct apfl_string *name)
|
|
|
|
|
{
|
|
|
|
|
return get_or_create_variable(gc, scope, name) != NULL;
|
|
|
|
|
}
|
2022-07-11 19:41:05 +00:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|