It's now possible to assign to a key of a dictionary and even to a nested key path. This patch changes the way matchers work a bit: First, a function call stack frame now has a stack of matchers that are manipulateable instead of a single matcher. Second, the matcher is now in charge of setting the matched values to the variables (previously the caller of the matcher needed to extract the matched values and assign them itself). This change simplifies code generation, especially for chained assignments and dictionary key paths. This removes the last usage of APFL_ERR_NOT_IMPLEMENTED :)
181 lines
4.1 KiB
C
181 lines
4.1 KiB
C
#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)
|
|
{
|
|
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;
|
|
}
|