apfl/src/eval.c

1261 lines
31 KiB
C
Raw Normal View History

#include <assert.h>
#include "apfl.h"
#include "hashmap.h"
#include "internal.h"
#include "resizable.h"
struct apfl_ctx_data {
apfl_hashmap scope;
};
struct variable_data {
unsigned refcount;
struct apfl_value value;
};
typedef struct variable_data *variable;
enum match_result {
MATCH_OK,
MATCH_DOESNT_MATCH,
MATCH_ERROR,
MATCH_FATAL_ERROR,
MATCH_NOT_YET_IMPLEMENTED,
};
enum match_pattern_type {
MPATTERN_BLANK,
MPATTERN_VALUE,
MPATTERN_LIST,
};
struct match_pattern_value {
apfl_refcounted_string varname;
variable var;
struct apfl_value value;
struct apfl_value *member_keys;
size_t member_keys_len;
};
struct match_pattern_list {
struct match_pattern *subpatterns;
size_t subpatterns_len;
bool with_expand;
size_t expand_index;
};
enum match_pattern_constraint_type {
MPATTERN_CONSTRAINT_EQUALS,
MPATTERN_CONSTRAINT_PREDICATE,
};
struct match_pattern_constraint {
enum match_pattern_constraint_type type;
struct apfl_value value;
};
struct match_pattern {
enum match_pattern_type type;
union {
struct match_pattern_value value;
struct match_pattern_list list;
};
struct match_pattern_constraint *constraints;
size_t constraints_len;
};
static struct apfl_result evaluate(apfl_ctx, struct apfl_expr *);
static variable variable_new(void);
static variable variable_incref(variable var);
static void variable_unref(variable var);
static enum match_result match_pattern_from_assignable(apfl_ctx ctx, struct apfl_expr_assignable *, struct match_pattern *);
static void match_pattern_deinit(struct match_pattern *);
static enum match_result match_pattern_match(struct match_pattern *, struct apfl_value);
static bool
scope_keys_eq(void *opaque, const void *_a, const void *_b)
{
(void)opaque;
apfl_refcounted_string const *a = _a;
apfl_refcounted_string const *b = _b;
return *a == *b || apfl_string_eq(*a, *b);
}
static apfl_hash
scope_calc_hash(void *opaque, const void *_key)
{
(void)opaque;
apfl_refcounted_string const *key = _key;
struct apfl_string_view sv = apfl_string_view_from(*key);
return apfl_hash_fnv1a(sv.bytes, sv.len);
}
static void
scope_destroy_key(void *opaque, void *_key)
{
(void)opaque;
apfl_refcounted_string *key = _key;
apfl_refcounted_string_unref(*key);
}
static void
scope_destroy_value(void *opaque, void *_value)
{
(void)opaque;
variable *value = _value;
variable_unref(*value);
}
static void
scope_copy_key(void *opaque, void *_dest, void *_src)
{
(void)opaque;
apfl_refcounted_string *dest = _dest;
apfl_refcounted_string *src = _src;
*dest = apfl_refcounted_string_incref(*src);
}
static void
scope_copy_value(void *opaque, void *_dest, void *_src)
{
(void)opaque;
variable *dest = _dest;
variable *src = _src;
*dest = variable_incref(*src);
}
static const struct apfl_hashmap_callbacks scope_hashmap_callbacks = {
.opaque = NULL,
.keys_eq = scope_keys_eq,
.calc_hash = scope_calc_hash,
.destroy_key = scope_destroy_key,
.destroy_value = scope_destroy_value,
.copy_key = scope_copy_key,
.copy_value = scope_copy_value,
};
apfl_hashmap
scope_new(void)
{
return apfl_hashmap_new(
scope_hashmap_callbacks,
sizeof(apfl_refcounted_string),
sizeof(variable)
);
}
static variable
variable_new(void)
{
variable var = ALLOC(struct variable_data);
if (var == NULL) {
return NULL;
}
var->refcount = 1;
var->value.type = APFL_VALUE_NIL;
return var;
}
static variable
variable_incref(variable var)
{
if (var != NULL) {
var->refcount++;
}
return var;
}
static void
variable_unref(variable var)
{
if (var != NULL && apfl_refcount_dec(&var->refcount)) {
apfl_value_deinit(&var->value);
free(var);
}
}
static void
variable_set(variable var, struct apfl_value value)
{
if (var == NULL) {
return;
}
apfl_value_deinit(&var->value);
var->value = apfl_value_move(&value);
}
apfl_ctx
apfl_ctx_new(void)
{
apfl_ctx ctx = ALLOC(struct apfl_ctx_data);
if (ctx == NULL) {
return NULL;
}
if ((ctx->scope = scope_new()) == NULL) {
free(ctx);
return NULL;
}
return ctx;
}
void
apfl_ctx_destroy(apfl_ctx ctx)
{
if (ctx == NULL) {
return;
}
apfl_hashmap_destroy(ctx->scope);
free(ctx);
}
static variable
ctx_get_var_for_assignment_inner(apfl_ctx ctx, /*borrowed*/ apfl_refcounted_string name)
{
variable var;
if (apfl_hashmap_get(ctx->scope, &name, &var)) {
return var;
}
if ((var = variable_new()) == NULL) {
return NULL;
}
if (!apfl_hashmap_set(ctx->scope, &name, &var)) {
variable_unref(var);
return NULL;
}
return var;
}
static variable
ctx_get_var_for_assignment(apfl_ctx ctx, apfl_refcounted_string name)
{
variable var = ctx_get_var_for_assignment_inner(ctx, name);
apfl_refcounted_string_unref(name);
return var;
}
static variable
ctx_get_var(apfl_ctx ctx, apfl_refcounted_string name)
{
variable var;
bool ok = apfl_hashmap_get(ctx->scope, &name, &var);
apfl_refcounted_string_unref(name);
return ok ? var : NULL;
}
static struct apfl_value
constant_to_value(struct apfl_expr_const *constant)
{
switch (constant->type) {
case APFL_EXPR_CONST_NIL:
return (struct apfl_value) { .type = APFL_VALUE_NIL };
case APFL_EXPR_CONST_BOOLEAN:
return (struct apfl_value) {
.type = APFL_VALUE_BOOLEAN,
.boolean = constant->boolean,
};
case APFL_EXPR_CONST_STRING:
return (struct apfl_value) {
.type = APFL_VALUE_STRING,
.string = apfl_refcounted_string_incref(constant->string),
};
case APFL_EXPR_CONST_NUMBER:
return (struct apfl_value) {
.type = APFL_VALUE_NUMBER,
.number = constant->number,
};
}
assert(false);
return (struct apfl_value) { .type = APFL_VALUE_NIL };
}
static bool
match_pattern_add_constraint(
struct match_pattern *pattern,
size_t *constraints_cap,
enum match_pattern_constraint_type type,
struct apfl_value value
) {
struct match_pattern_constraint constraint = {
.type = type,
.value = value,
};
if (!apfl_resizable_append(
sizeof(struct match_pattern_constraint),
(void **)&pattern->constraints,
&pattern->constraints_len,
constraints_cap,
&constraint,
1
)) {
apfl_value_deinit(&value);
return false;
}
return true;
}
static enum match_result
match_pattern_from_assignable_list(
apfl_ctx ctx,
struct apfl_expr_assignable_list *assignable_list,
struct match_pattern *pattern
) {
pattern->type = MPATTERN_LIST;
struct match_pattern_list *pattern_list = &pattern->list;
*pattern_list = (struct match_pattern_list) {
.subpatterns = NULL,
.subpatterns_len = 0,
.with_expand = false,
.expand_index = 0,
};
if (assignable_list->len == 0) {
return MATCH_OK;
}
if ((pattern_list->subpatterns = ALLOC_LIST(
struct match_pattern,
assignable_list->len
)) == NULL) {
return MATCH_FATAL_ERROR;
}
for (size_t i = 0; i < assignable_list->len; i++) {
struct apfl_expr_assignable_list_item *item = &assignable_list->items[i];
if (item->expand) {
if (pattern_list->with_expand) {
return MATCH_ERROR;
}
pattern_list->with_expand = true;
pattern_list->expand_index = i;
}
enum match_result result = match_pattern_from_assignable(
ctx,
&item->assignable,
&pattern_list->subpatterns[i]
);
if (result != MATCH_OK) {
DEINIT_LIST(
pattern_list->subpatterns,
pattern_list->subpatterns_len,
match_pattern_deinit
);
return result;
}
pattern_list->subpatterns_len++;
}
return MATCH_OK;
}
static enum match_result
match_pattern_from_var_or_member(
apfl_ctx ctx,
struct apfl_expr_assignable_var_or_member *var_or_member,
struct match_pattern *pattern
) {
pattern->type = MPATTERN_VALUE;
pattern->value = (struct match_pattern_value) {
.varname = NULL,
.var = NULL,
.value = {.type = APFL_VALUE_NIL},
.member_keys = NULL,
.member_keys_len = 0,
};
size_t member_keys_cap = 0;
enum match_result result;
struct apfl_result eval_result;
struct apfl_value str_value;
next:
switch (var_or_member->type) {
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
if ((pattern->value.varname = apfl_string_copy_into_new_refcounted(
apfl_string_view_from(var_or_member->var)
)) == NULL) {
result = MATCH_FATAL_ERROR;
goto fail;
}
return MATCH_OK;
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
str_value.type = APFL_VALUE_STRING;
if ((str_value.string = apfl_string_copy_into_new_refcounted(
apfl_string_view_from(var_or_member->dot.rhs)
)) == NULL) {
result = MATCH_FATAL_ERROR;
goto fail;
}
if (!apfl_resizable_append(
sizeof(struct apfl_value),
(void **)&pattern->value.member_keys,
&pattern->value.member_keys_len,
&member_keys_cap,
&str_value,
1
)) {
apfl_value_deinit(&str_value);
result = MATCH_FATAL_ERROR;
goto fail;
}
var_or_member = var_or_member->dot.lhs;
goto next;
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
eval_result = evaluate(ctx, var_or_member->at.rhs);
switch (eval_result.type) {
case APFL_RESULT_OK:
break;
case APFL_RESULT_ERR:
result = MATCH_ERROR;
goto fail;
case APFL_RESULT_ERR_FATAL:
result = MATCH_FATAL_ERROR;
goto fail;
}
if (!apfl_resizable_append(
sizeof(struct apfl_value),
(void **)&pattern->value.member_keys,
&pattern->value.member_keys_len,
&member_keys_cap,
&eval_result.value,
1
)) {
apfl_value_deinit(&eval_result.value);
result = MATCH_FATAL_ERROR;
goto fail;
}
var_or_member = var_or_member->at.lhs;
goto next;
}
fail:
DEINIT_LIST(pattern->value.member_keys, pattern->value.member_keys_len, apfl_value_deinit);
return result;
}
static enum match_result
match_pattern_from_assignable_inner(
apfl_ctx ctx,
struct apfl_expr_assignable *assignable,
struct match_pattern *pattern
) {
struct apfl_result result;
pattern->type = MPATTERN_BLANK;
pattern->constraints = NULL;
pattern->constraints_len = 0;
size_t constraints_cap = 0;
next:
switch (assignable->type) {
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
return match_pattern_from_var_or_member(ctx, &assignable->var_or_member, pattern);
case APFL_EXPR_ASSIGNABLE_CONSTANT:
if (!match_pattern_add_constraint(
pattern,
&constraints_cap,
MPATTERN_CONSTRAINT_EQUALS,
constant_to_value(&assignable->constant)
)) {
return MATCH_FATAL_ERROR;
}
pattern->type = MPATTERN_BLANK;
return MATCH_OK;
case APFL_EXPR_ASSIGNABLE_PREDICATE:
result = evaluate(ctx, assignable->predicate.rhs);
switch (result.type) {
case APFL_RESULT_OK:
break;
case APFL_RESULT_ERR:
return MATCH_ERROR;
case APFL_RESULT_ERR_FATAL:
return MATCH_FATAL_ERROR;
}
if (!match_pattern_add_constraint(
pattern,
&constraints_cap,
MPATTERN_CONSTRAINT_PREDICATE,
apfl_value_move(&result.value)
)) {
return MATCH_FATAL_ERROR;
}
assignable = assignable->predicate.lhs;
goto next;
case APFL_EXPR_ASSIGNABLE_LIST:
return match_pattern_from_assignable_list(ctx, &assignable->list, pattern);
case APFL_EXPR_ASSIGNABLE_BLANK:
pattern->type = MPATTERN_BLANK;
return MATCH_OK;
}
assert(false);
return MATCH_FATAL_ERROR;
}
static enum match_result
match_pattern_from_assignable(
apfl_ctx ctx,
struct apfl_expr_assignable *assignable,
struct match_pattern *pattern
) {
enum match_result result = match_pattern_from_assignable_inner(ctx, assignable, pattern);
if (result != MATCH_OK) {
match_pattern_deinit(pattern);
}
return result;
}
static bool
match_pattern_create_vars(apfl_ctx ctx, struct match_pattern *pattern)
{
switch (pattern->type) {
case MPATTERN_BLANK:
return true;
case MPATTERN_VALUE:
if (pattern->value.var != NULL) {
return true;
}
if ((pattern->value.var = ctx_get_var_for_assignment(
ctx,
apfl_refcounted_string_incref(pattern->value.varname)
)) == NULL) {
return false;
}
return true;
case MPATTERN_LIST:
for (size_t i = 0; i < pattern->list.subpatterns_len; i++) {
if (!match_pattern_create_vars(ctx, &pattern->list.subpatterns[i])) {
return false;
}
}
return true;
}
assert(false);
return false;
}
static void
match_pattern_constraint_deinit(struct match_pattern_constraint *constraint)
{
if (constraint == NULL) {
return;
}
apfl_value_deinit(&constraint->value);
}
static void
match_pattern_deinit(struct match_pattern *pattern)
{
if (pattern == NULL) {
return;
}
switch (pattern->type) {
case MPATTERN_BLANK:
break;
case MPATTERN_VALUE:
apfl_refcounted_string_unref(pattern->value.varname);
variable_unref(pattern->value.var);
apfl_value_deinit(&pattern->value.value);
DEINIT_LIST(
pattern->value.member_keys,
pattern->value.member_keys_len,
apfl_value_deinit
);
break;
case MPATTERN_LIST:
DEINIT_LIST(
pattern->list.subpatterns,
pattern->list.subpatterns_len,
match_pattern_deinit
);
break;
}
DEINIT_LIST(
pattern->constraints,
pattern->constraints_len,
match_pattern_constraint_deinit
);
}
static enum match_result
match_pattern_check_constraint(
struct match_pattern_constraint *constraint,
const struct apfl_value *value
) {
switch (constraint->type) {
case MPATTERN_CONSTRAINT_PREDICATE:
return MATCH_NOT_YET_IMPLEMENTED;
case MPATTERN_CONSTRAINT_EQUALS:
if (apfl_value_eq(constraint->value, *value)) {
return MATCH_OK;
}
return MATCH_DOESNT_MATCH;
}
assert(false);
return MATCH_FATAL_ERROR;
}
static enum match_result
match_pattern_match_list_inner(
struct match_pattern_list *pattern_list,
/*borrowed*/ apfl_list list
) {
size_t list_len = apfl_list_len(list);
if (pattern_list->with_expand
? (list_len < pattern_list->subpatterns_len - 1)
: (pattern_list->subpatterns_len != list_len)
) {
return MATCH_DOESNT_MATCH;
}
size_t limit = pattern_list->with_expand
? pattern_list->expand_index
: pattern_list->subpatterns_len;
for (size_t i = 0; i < limit; i++) {
struct apfl_value list_item;
// Will not fail, as i is a valid index for the list
assert(apfl_list_get_item(
apfl_list_incref(list),
i,
&list_item
));
enum match_result result = match_pattern_match(
&pattern_list->subpatterns[i],
apfl_value_move(&list_item)
);
if (result != MATCH_OK) {
return result;
}
}
if (!pattern_list->with_expand) {
return MATCH_OK;
}
size_t tail_len = pattern_list->subpatterns_len - pattern_list->expand_index - 1;
for (size_t i = 0; i < tail_len; i++) {
size_t subpattern_index = pattern_list->subpatterns_len - i - 1;
size_t list_index = list_len - i - 1;
struct apfl_value list_item;
// Will not fail, as list_index is a valid index for the list
assert(apfl_list_get_item(
apfl_list_incref(list),
list_index,
&list_item
));
enum match_result result = match_pattern_match(
&pattern_list->subpatterns[subpattern_index],
apfl_value_move(&list_item)
);
if (result != MATCH_OK) {
return result;
}
}
apfl_editable_list mid_builder = apfl_editable_list_new();
if (mid_builder == NULL) {
return MATCH_ERROR;
}
for (size_t i = pattern_list->expand_index; i < list_len - tail_len; i++) {
struct apfl_value list_item;
// Will not fail, as i is a valid index for the list
assert(apfl_list_get_item(
apfl_list_incref(list),
i,
&list_item
));
if (!apfl_editable_list_append(
mid_builder,
apfl_value_move(&list_item))
) {
apfl_editable_list_destroy(mid_builder);
return MATCH_FATAL_ERROR;
}
}
apfl_list mid_list = apfl_editable_list_finalize(mid_builder);
if (mid_list == NULL) {
return MATCH_FATAL_ERROR;
}
return match_pattern_match(
&pattern_list->subpatterns[pattern_list->expand_index],
(struct apfl_value) {
.type = APFL_VALUE_LIST,
.list = mid_list,
}
);
}
static enum match_result
match_pattern_match_list(
struct match_pattern_list *pattern_list,
struct apfl_value value
) {
if (value.type != APFL_VALUE_LIST) {
apfl_value_deinit(&value);
return MATCH_DOESNT_MATCH;
}
apfl_list list = apfl_list_incref(value.list);
apfl_value_deinit(&value);
enum match_result result = match_pattern_match_list_inner(pattern_list, list);
apfl_list_unref(list);
return result;
}
static enum match_result
match_pattern_match(struct match_pattern *pattern, struct apfl_value value)
{
// We put the contraints into the list from the outside in, so we need
// to iterate in reverse order.
for (size_t i = pattern->constraints_len; i-- > 0; ) {
enum match_result result = match_pattern_check_constraint(
&pattern->constraints[i],
&value
);
if (result != MATCH_OK) {
apfl_value_deinit(&value);
return result;
}
}
switch (pattern->type) {
case MPATTERN_BLANK:
apfl_value_deinit(&value);
return MATCH_OK;
case MPATTERN_VALUE:
pattern->value.value = apfl_value_move(&value);
return MATCH_OK;
case MPATTERN_LIST:
return match_pattern_match_list(&pattern->list, apfl_value_move(&value));
}
assert(false);
return MATCH_FATAL_ERROR;
}
static enum match_result
match_pattern_apply_value_keys(
struct match_pattern_value *pattern_value,
apfl_editable_dict ed,
size_t i
) {
struct apfl_value *key = &pattern_value->member_keys[i];
if (i == 0) {
if (!apfl_editable_dict_set(
ed,
apfl_value_incref(*key),
apfl_value_move(&pattern_value->value)
)) {
return MATCH_FATAL_ERROR;
}
return MATCH_OK;
}
apfl_editable_dict next_ed;
struct apfl_value value;
if (apfl_editable_dict_get(
ed,
apfl_value_incref(*key),
&value
)) {
if (value.type != APFL_VALUE_DICT) {
apfl_value_deinit(&value);
return MATCH_ERROR;
}
next_ed = apfl_editable_dict_new_from_dict(value.dict);
} else {
// Be nice and create the missing dictionary
next_ed = apfl_editable_dict_new();
}
if (next_ed == NULL) {
return MATCH_FATAL_ERROR;
}
enum match_result result = match_pattern_apply_value_keys(
pattern_value,
next_ed,
i - 1
);
if (result != MATCH_OK) {
apfl_editable_dict_destroy(next_ed);
return result;
}
apfl_dict next_dict = apfl_editable_dict_finalize(next_ed);
if (next_dict == NULL) {
return MATCH_FATAL_ERROR;
}
if (!apfl_editable_dict_set(
ed,
apfl_value_incref(*key),
(struct apfl_value) {
.type = APFL_VALUE_DICT,
.dict = next_dict,
}
)) {
return MATCH_FATAL_ERROR;
}
return MATCH_OK;
}
static enum match_result
match_pattern_apply_value(struct match_pattern_value *pattern_value)
{
if (pattern_value->var == NULL) {
return false;
}
if (pattern_value->member_keys_len == 0) {
variable_set(pattern_value->var, apfl_value_move(&pattern_value->value));
return MATCH_OK;
}
if (pattern_value->var->value.type != APFL_VALUE_DICT) {
return MATCH_ERROR;
}
apfl_editable_dict ed = apfl_editable_dict_new_from_dict(
apfl_dict_incref(pattern_value->var->value.dict)
);
if (ed == NULL) {
return MATCH_FATAL_ERROR;
}
enum match_result result = match_pattern_apply_value_keys(
pattern_value,
ed,
pattern_value->member_keys_len - 1
);
if (result != MATCH_OK) {
apfl_editable_dict_destroy(ed);
return result;
}
apfl_dict d = apfl_editable_dict_finalize(ed);
if (d == NULL) {
return MATCH_FATAL_ERROR;
}
variable_set(pattern_value->var, (struct apfl_value) {
.type = APFL_VALUE_DICT,
.dict = d,
});
return MATCH_OK;
}
static enum match_result
match_pattern_apply(struct match_pattern *pattern)
{
switch (pattern->type) {
case MPATTERN_BLANK:
return MATCH_OK;
case MPATTERN_VALUE:
return match_pattern_apply_value(&pattern->value);
case MPATTERN_LIST:
for (size_t i = 0; i < pattern->list.subpatterns_len; i++) {
enum match_result result = match_pattern_apply(
&pattern->list.subpatterns[i]
);
if (result != MATCH_OK) {
return result;
}
}
return MATCH_OK;
}
assert(false);
return MATCH_FATAL_ERROR;
}
static struct apfl_result
fatal(void)
{
return (struct apfl_result) { .type = APFL_RESULT_ERR_FATAL };
}
static struct apfl_result
evaluate_constant(struct apfl_expr_const *constant)
{
return (struct apfl_result) {
.type = APFL_RESULT_OK,
.value = constant_to_value(constant),
};
}
static struct apfl_result
evaluate_list_expr_to_list(apfl_ctx ctx, struct apfl_expr *expr, apfl_editable_list elist)
{
struct apfl_result result = evaluate(ctx, expr);
if (result.type != APFL_RESULT_OK) {
return result;
}
if (result.value.type != APFL_VALUE_LIST) {
apfl_value_deinit(&result.value);
return (struct apfl_result) { .type = APFL_RESULT_ERR };
}
apfl_list list = apfl_list_incref(result.value.list);
apfl_value_deinit(&result.value);
if (!apfl_editable_list_append_list(elist, list)) {
return fatal();
}
return (struct apfl_result) { .type = APFL_RESULT_OK, .value.type = APFL_VALUE_NIL };
}
static struct apfl_result
evaluate_expr_list_into_list(apfl_ctx ctx, struct apfl_expr_list *list, apfl_editable_list elist)
{
for (size_t i = 0; i < list->len; i++) {
struct apfl_expr_list_item *item = &list->items[i];
if (item->expand) {
struct apfl_result result = evaluate_list_expr_to_list(ctx, item->expr, elist);
if (result.type != APFL_RESULT_OK) {
return result;
}
} else {
struct apfl_result result = evaluate(ctx, item->expr);
if (result.type != APFL_RESULT_OK) {
return result;
}
if (!apfl_editable_list_append(elist, apfl_value_move(&result.value))) {
return fatal();
}
}
}
return (struct apfl_result) { .type = APFL_RESULT_OK, .value.type = APFL_VALUE_NIL };
}
static struct apfl_result
evaluate_list(apfl_ctx ctx, struct apfl_expr_list *list)
{
apfl_editable_list elist = apfl_editable_list_new();
if (elist == NULL) {
return fatal();
}
struct apfl_result result = evaluate_expr_list_into_list(ctx, list, elist);
if (result.type != APFL_RESULT_OK) {
apfl_editable_list_destroy(elist);
return result;
}
apfl_list out = apfl_editable_list_finalize(elist);
if (out == NULL) {
return fatal();
}
return (struct apfl_result) {
.type = APFL_RESULT_OK,
.value = {
.type = APFL_VALUE_LIST,
.list = out,
},
};
}
static struct apfl_result
evaluate_dict(apfl_ctx ctx, struct apfl_expr_dict *dict)
{
apfl_editable_dict ed = apfl_editable_dict_new();
if (ed == NULL) {
return fatal();
}
for (size_t i = 0; i < dict->len; i++) {
struct apfl_result result;
struct apfl_value key, value;
result = evaluate(ctx, dict->items[i].k);
if (result.type != APFL_RESULT_OK) {
apfl_editable_dict_destroy(ed);
return result;
}
key = result.value;
result = evaluate(ctx, dict->items[i].v);
if (result.type != APFL_RESULT_OK) {
apfl_editable_dict_destroy(ed);
apfl_value_deinit(&key);
return result;
}
value = result.value;
if (!apfl_editable_dict_set(
ed,
apfl_value_move(&key),
apfl_value_move(&value)
)) {
apfl_editable_dict_destroy(ed);
return fatal();
}
}
apfl_dict out = apfl_editable_dict_finalize(ed);
if (out == NULL) {
return fatal();
}
return (struct apfl_result) {
.type = APFL_RESULT_OK,
.value = {
.type = APFL_VALUE_DICT,
.dict = out,
},
};
}
static struct apfl_result
evaluate_dot(apfl_ctx ctx, struct apfl_expr_dot *dot)
{
struct apfl_result result;
result = evaluate(ctx, dot->lhs);
if (result.type != APFL_RESULT_OK) {
return result;
}
struct apfl_value lhs = result.value;
struct apfl_value key = (struct apfl_value) {
.type = APFL_VALUE_STRING,
.string = apfl_refcounted_string_incref(dot->rhs),
};
struct apfl_value out;
if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&key), &out) == APFL_VALUE_GET_ITEM_OK) {
return (struct apfl_result) {
.type = APFL_RESULT_OK,
.value = out,
};
} else {
return (struct apfl_result) { .type = APFL_RESULT_ERR }; // TODO: Describe error
}
}
static struct apfl_result
evaluate_at(apfl_ctx ctx, struct apfl_expr_at *at)
{
struct apfl_result result;
result = evaluate(ctx, at->lhs);
if (result.type != APFL_RESULT_OK) {
return result;
}
struct apfl_value lhs = result.value;
result = evaluate(ctx, at->rhs);
if (result.type != APFL_RESULT_OK) {
apfl_value_deinit(&lhs);
return result;
}
struct apfl_value rhs = result.value;
struct apfl_value out;
if (apfl_value_get_item(apfl_value_move(&lhs), apfl_value_move(&rhs), &out) == APFL_VALUE_GET_ITEM_OK) {
return (struct apfl_result) {
.type = APFL_RESULT_OK,
.value = out,
};
} else {
return (struct apfl_result) { .type = APFL_RESULT_ERR }; // TODO: Describe error
}
}
static struct apfl_result
failing_match_result_to_apfl_result(enum match_result match_result)
{
if (match_result == MATCH_FATAL_ERROR) {
return fatal();
}
return (struct apfl_result) { .type = APFL_RESULT_ERR };
}
static enum match_result
evaluate_assignment_setup(
apfl_ctx ctx,
struct match_pattern *pattern,
struct apfl_expr_assignment *assignment
) {
enum match_result result = match_pattern_from_assignable(
ctx,
&assignment->lhs,
pattern
);
if (result != MATCH_OK) {
return result;
}
if (!match_pattern_create_vars(ctx, pattern)) {
match_pattern_deinit(pattern);
return MATCH_FATAL_ERROR;
}
return MATCH_OK;
}
static enum match_result
evaluate_assignment_finish(
struct match_pattern *pattern,
struct apfl_value value
) {
enum match_result match_result = match_pattern_match(
pattern,
apfl_value_move(&value)
);
if (match_result != MATCH_OK) {
return match_result;
}
return match_pattern_apply(pattern);
}
static struct apfl_result
evaluate_assignment(apfl_ctx ctx, struct apfl_expr_assignment *assignment)
{
struct match_pattern pattern;
enum match_result match_result = evaluate_assignment_setup(
ctx,
&pattern,
assignment
);
if (match_result != MATCH_OK) {
return failing_match_result_to_apfl_result(match_result);
}
struct apfl_result result = evaluate(ctx, assignment->rhs);
if (result.type != APFL_RESULT_OK) {
match_pattern_deinit(&pattern);
return result;
}
match_result = evaluate_assignment_finish(
&pattern,
apfl_value_incref(result.value)
);
match_pattern_deinit(&pattern);
if (match_result != MATCH_OK) {
apfl_value_deinit(&result.value);
return failing_match_result_to_apfl_result(match_result);
}
return result;
}
static struct apfl_result
evaluate_var(apfl_ctx ctx, apfl_refcounted_string varname)
{
variable var = ctx_get_var(ctx, varname);
if (var == NULL) {
return (struct apfl_result) { .type = APFL_RESULT_ERR };
}
struct apfl_value value = apfl_value_incref(var->value);
variable_unref(var);
return (struct apfl_result) {
.type = APFL_RESULT_OK,
.value = value,
};
}
static struct apfl_result
evaluate(apfl_ctx ctx, struct apfl_expr *expr)
{
switch (expr->type) {
case APFL_EXPR_CONSTANT:
return evaluate_constant(&expr->constant);
case APFL_EXPR_LIST:
return evaluate_list(ctx, &expr->list);
case APFL_EXPR_DICT:
return evaluate_dict(ctx, &expr->dict);
case APFL_EXPR_DOT:
return evaluate_dot(ctx, &expr->dot);
case APFL_EXPR_AT:
return evaluate_at(ctx, &expr->at);
case APFL_EXPR_ASSIGNMENT:
return evaluate_assignment(ctx, &expr->assignment);
case APFL_EXPR_VAR:
return evaluate_var(ctx, apfl_refcounted_string_incref(expr->var));
case APFL_EXPR_BLANK:
return (struct apfl_result) {
.type = APFL_RESULT_OK,
.value = {
.type = APFL_VALUE_NIL,
},
};
case APFL_EXPR_CALL:
case APFL_EXPR_SIMPLE_FUNC:
case APFL_EXPR_COMPLEX_FUNC:
break; // Not implemented yet
}
return (struct apfl_result) { .type = APFL_RESULT_ERR };
}
struct apfl_result
apfl_eval(apfl_ctx ctx, struct apfl_expr expr)
{
struct apfl_result result = evaluate(ctx, &expr);
apfl_expr_deinit(&expr);
return result;
}