Partially implement pattern matching assignments
We're still missing predicates (need to be able to call functions for that one) and assignments into dictionaries. But we now can deconstruct a list and check against constants. So things like this work now: [1 foo ~bar [a b]] = [1 "Hello" 2 3 4 [5 6]] # foo is: "Hello" # bar is: [2 3 4] # a is: 5 # b is: 6 Pretty cool :)
This commit is contained in:
parent
ae45aeebe2
commit
a14b490dfe
4 changed files with 738 additions and 61 deletions
|
|
@ -81,6 +81,8 @@ struct apfl_string apfl_string_builder_move_string(struct apfl_string_builder *)
|
|||
|
||||
#define apfl_string_builder_append_cstr(builder, cstr) (apfl_string_builder_append((builder), apfl_string_view_from_cstr((cstr))))
|
||||
|
||||
apfl_refcounted_string apfl_string_copy_into_new_refcounted(struct apfl_string_view);
|
||||
|
||||
apfl_refcounted_string apfl_string_move_into_new_refcounted(struct apfl_string *);
|
||||
|
||||
/* Increases the reference of the refcounted string.
|
||||
|
|
@ -572,6 +574,7 @@ enum apfl_value_get_item_result {
|
|||
enum apfl_value_get_item_result apfl_value_get_item(struct apfl_value container, struct apfl_value key, struct apfl_value *out);
|
||||
|
||||
apfl_list apfl_list_incref(apfl_list);
|
||||
size_t apfl_list_len(apfl_list);
|
||||
bool apfl_list_get_item(apfl_list, size_t index, struct apfl_value *out);
|
||||
void apfl_list_unref(apfl_list);
|
||||
|
||||
|
|
|
|||
772
src/eval.c
772
src/eval.c
|
|
@ -16,10 +16,62 @@ struct variable_data {
|
|||
|
||||
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)
|
||||
|
|
@ -127,6 +179,17 @@ variable_unref(variable 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)
|
||||
{
|
||||
|
|
@ -191,6 +254,576 @@ ctx_get_var(apfl_ctx ctx, apfl_refcounted_string name)
|
|||
return ok ? var : NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
constant_to_value(struct apfl_expr_const *constant, struct apfl_value *value)
|
||||
{
|
||||
apfl_refcounted_string rcstring;
|
||||
|
||||
switch (constant->type) {
|
||||
case APFL_EXPR_CONST_NIL:
|
||||
value->type = APFL_VALUE_NIL;
|
||||
return true;
|
||||
case APFL_EXPR_CONST_BOOLEAN:
|
||||
value->type = APFL_VALUE_BOOLEAN;
|
||||
value->boolean = constant->boolean;
|
||||
return true;
|
||||
case APFL_EXPR_CONST_STRING:
|
||||
// TODO: Moving the string will become a problem when we're evaluating the same AST node twice.
|
||||
// The parser probably should already return rcstrings.
|
||||
rcstring = apfl_string_move_into_new_refcounted(&constant->string);
|
||||
if (rcstring == NULL) {
|
||||
return false;
|
||||
}
|
||||
value->type = APFL_VALUE_STRING;
|
||||
value->string = rcstring;
|
||||
return true;
|
||||
case APFL_EXPR_CONST_NUMBER:
|
||||
value->type = APFL_VALUE_NUMBER;
|
||||
value->number = constant->number;
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
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_value value;
|
||||
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 (!constant_to_value(&assignable->constant, &value)) {
|
||||
return MATCH_FATAL_ERROR;
|
||||
}
|
||||
if (!match_pattern_add_constraint(
|
||||
pattern,
|
||||
&constraints_cap,
|
||||
MPATTERN_CONSTRAINT_EQUALS,
|
||||
value
|
||||
)) {
|
||||
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(struct match_pattern_value *pattern_value)
|
||||
{
|
||||
if (pattern_value->var == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pattern_value->member_keys_len > 0) {
|
||||
return MATCH_NOT_YET_IMPLEMENTED;
|
||||
}
|
||||
|
||||
variable_set(pattern_value->var, apfl_value_move(&pattern_value->value));
|
||||
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)
|
||||
{
|
||||
|
|
@ -200,48 +833,15 @@ fatal(void)
|
|||
static struct apfl_result
|
||||
evaluate_constant(struct apfl_expr_const *constant)
|
||||
{
|
||||
apfl_refcounted_string rcstring;
|
||||
|
||||
switch (constant->type) {
|
||||
case APFL_EXPR_CONST_NIL:
|
||||
return (struct apfl_result) {
|
||||
.type = APFL_RESULT_OK,
|
||||
.value = {
|
||||
.type = APFL_VALUE_NIL,
|
||||
},
|
||||
};
|
||||
case APFL_EXPR_CONST_BOOLEAN:
|
||||
return (struct apfl_result) {
|
||||
.type = APFL_RESULT_OK,
|
||||
.value = {
|
||||
.type = APFL_VALUE_BOOLEAN,
|
||||
.boolean = constant->boolean,
|
||||
},
|
||||
};
|
||||
case APFL_EXPR_CONST_STRING:
|
||||
rcstring = apfl_string_move_into_new_refcounted(&constant->string);
|
||||
if (rcstring == NULL) {
|
||||
return fatal();
|
||||
}
|
||||
|
||||
return (struct apfl_result) {
|
||||
.type = APFL_RESULT_OK,
|
||||
.value = {
|
||||
.type = APFL_VALUE_STRING,
|
||||
.string = rcstring,
|
||||
},
|
||||
};
|
||||
case APFL_EXPR_CONST_NUMBER:
|
||||
return (struct apfl_result) {
|
||||
.type = APFL_RESULT_OK,
|
||||
.value = {
|
||||
.type = APFL_VALUE_NUMBER,
|
||||
.number = constant->number,
|
||||
},
|
||||
};
|
||||
struct apfl_value value;
|
||||
if (!constant_to_value(constant, &value)) {
|
||||
return fatal();
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return (struct apfl_result) {
|
||||
.type = APFL_RESULT_OK,
|
||||
.value = value,
|
||||
};
|
||||
}
|
||||
|
||||
static struct apfl_result
|
||||
|
|
@ -433,38 +1033,88 @@ evaluate_at(apfl_ctx ctx, struct apfl_expr_at *at)
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// TODO: Use assignment->local flag. Sonce we don't hev functions yet, it's not yet relevant.
|
||||
struct match_pattern pattern;
|
||||
enum match_result match_result = evaluate_assignment_setup(
|
||||
ctx,
|
||||
&pattern,
|
||||
assignment
|
||||
);
|
||||
|
||||
if (
|
||||
assignment->lhs.type != APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER
|
||||
|| assignment->lhs.var_or_member.type != APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR
|
||||
) {
|
||||
return (struct apfl_result) { .type = APFL_RESULT_ERR }; // TODO: Implement other assignable types
|
||||
}
|
||||
|
||||
apfl_refcounted_string varname = apfl_string_move_into_new_refcounted(&assignment->lhs.var_or_member.var);
|
||||
if (varname == NULL) {
|
||||
return fatal();
|
||||
}
|
||||
|
||||
variable var = ctx_get_var_for_assignment(ctx, varname);
|
||||
|
||||
if (var == NULL) {
|
||||
return fatal();
|
||||
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) {
|
||||
variable_unref(var);
|
||||
match_pattern_deinit(&pattern);
|
||||
return result;
|
||||
}
|
||||
|
||||
apfl_value_deinit(&var->value);
|
||||
var->value = apfl_value_incref(result.value);
|
||||
variable_unref(var);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,11 +157,29 @@ apfl_string_view_from_refcounted_string(apfl_refcounted_string rcstring)
|
|||
return apfl_string_view_from(rcstring->string);
|
||||
}
|
||||
|
||||
apfl_refcounted_string
|
||||
apfl_string_copy_into_new_refcounted(struct apfl_string_view sv)
|
||||
{
|
||||
struct apfl_string str = apfl_string_blank();
|
||||
if (!apfl_string_copy(&str, sv)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
apfl_refcounted_string rcstring = apfl_string_move_into_new_refcounted(&str);
|
||||
if (rcstring == NULL) {
|
||||
apfl_string_deinit(&str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rcstring;
|
||||
}
|
||||
|
||||
apfl_refcounted_string
|
||||
apfl_string_move_into_new_refcounted(struct apfl_string *src)
|
||||
{
|
||||
apfl_refcounted_string dst = ALLOC(struct apfl_refcounted_string_data);
|
||||
if (dst == NULL) {
|
||||
// TODO: Or should we free src here?
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@ apfl_list_incref(apfl_list list)
|
|||
return list;
|
||||
}
|
||||
|
||||
size_t
|
||||
apfl_list_len(apfl_list list)
|
||||
{
|
||||
return list->len;
|
||||
}
|
||||
|
||||
void
|
||||
apfl_list_unref(apfl_list list)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue