2022-01-02 16:19:54 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
#include "apfl.h"
|
2022-02-08 21:53:13 +00:00
|
|
|
|
|
|
|
|
#include "alloc.h"
|
2022-01-06 21:53:26 +00:00
|
|
|
#include "hashmap.h"
|
2022-01-02 16:19:54 +00:00
|
|
|
#include "internal.h"
|
|
|
|
|
#include "resizable.h"
|
2022-01-20 21:45:09 +00:00
|
|
|
#include "value.h"
|
|
|
|
|
|
|
|
|
|
#define TRY(ex) \
|
|
|
|
|
do { \
|
|
|
|
|
enum apfl_result result = (ex); \
|
|
|
|
|
if (result != APFL_RESULT_OK) { \
|
|
|
|
|
return result; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
2022-01-02 16:19:54 +00:00
|
|
|
|
|
|
|
|
struct apfl_ctx_data {
|
2022-02-08 21:53:13 +00:00
|
|
|
struct apfl_allocator allocator;
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
struct apfl_hashmap scope;
|
2022-01-20 21:45:09 +00:00
|
|
|
|
|
|
|
|
struct apfl_value *stack;
|
|
|
|
|
size_t stack_len;
|
|
|
|
|
size_t stack_cap;
|
2022-01-02 16:19:54 +00:00
|
|
|
};
|
|
|
|
|
|
2022-01-06 21:53:26 +00:00
|
|
|
struct variable_data {
|
|
|
|
|
unsigned refcount;
|
|
|
|
|
struct apfl_value value;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct variable_data *variable;
|
|
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
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;
|
2022-02-08 21:53:13 +00:00
|
|
|
size_t member_keys_cap;
|
2022-01-14 22:16:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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;
|
2022-02-08 21:53:13 +00:00
|
|
|
size_t constraints_cap;
|
2022-01-14 22:16:19 +00:00
|
|
|
};
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result evaluate(apfl_ctx, struct apfl_expr *);
|
2022-02-08 21:53:13 +00:00
|
|
|
static variable variable_new(apfl_ctx);
|
2022-01-06 21:53:26 +00:00
|
|
|
static variable variable_incref(variable var);
|
2022-02-08 21:53:13 +00:00
|
|
|
static void variable_unref(apfl_ctx, variable var);
|
2022-01-14 22:16:19 +00:00
|
|
|
static enum match_result match_pattern_from_assignable(apfl_ctx ctx, struct apfl_expr_assignable *, struct match_pattern *);
|
2022-02-08 21:53:13 +00:00
|
|
|
static void match_pattern_deinit(apfl_ctx, struct match_pattern *);
|
|
|
|
|
static enum match_result match_pattern_match(struct apfl_allocator, struct match_pattern *, struct apfl_value value);
|
2022-01-06 21:53:26 +00:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_ctx ctx = opaque;
|
2022-01-06 21:53:26 +00:00
|
|
|
apfl_refcounted_string *key = _key;
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_refcounted_string_unref(ctx->allocator, *key);
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
scope_destroy_value(void *opaque, void *_value)
|
|
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_ctx ctx = opaque;
|
2022-01-06 21:53:26 +00:00
|
|
|
variable *value = _value;
|
2022-02-08 21:53:13 +00:00
|
|
|
variable_unref(ctx, *value);
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
bool
|
2022-02-08 21:53:13 +00:00
|
|
|
scope_init(apfl_ctx ctx, struct apfl_hashmap *map)
|
2022-01-06 21:53:26 +00:00
|
|
|
{
|
2022-01-22 14:55:09 +00:00
|
|
|
return apfl_hashmap_init(
|
|
|
|
|
map,
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
|
|
|
|
(struct apfl_hashmap_callbacks) {
|
|
|
|
|
.opaque = ctx,
|
|
|
|
|
.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,
|
|
|
|
|
},
|
2022-01-06 21:53:26 +00:00
|
|
|
sizeof(apfl_refcounted_string),
|
|
|
|
|
sizeof(variable)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static variable
|
2022-02-08 21:53:13 +00:00
|
|
|
variable_new(apfl_ctx ctx)
|
2022-01-06 21:53:26 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
variable var = ALLOC_OBJ(ctx->allocator, struct variable_data);
|
2022-01-06 21:53:26 +00:00
|
|
|
if (var == NULL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var->refcount = 1;
|
2022-02-10 20:55:40 +00:00
|
|
|
var->value.type = VALUE_NIL;
|
2022-01-06 21:53:26 +00:00
|
|
|
|
|
|
|
|
return var;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static variable
|
|
|
|
|
variable_incref(variable var)
|
|
|
|
|
{
|
|
|
|
|
if (var != NULL) {
|
|
|
|
|
var->refcount++;
|
|
|
|
|
}
|
|
|
|
|
return var;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2022-02-08 21:53:13 +00:00
|
|
|
variable_unref(apfl_ctx ctx, variable var)
|
2022-01-06 21:53:26 +00:00
|
|
|
{
|
|
|
|
|
if (var != NULL && apfl_refcount_dec(&var->refcount)) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &var->value);
|
|
|
|
|
FREE_OBJ(ctx->allocator, var);
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-02 16:19:54 +00:00
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
static void
|
2022-02-08 21:53:13 +00:00
|
|
|
variable_set(apfl_ctx ctx, variable var, struct apfl_value value)
|
2022-01-14 22:16:19 +00:00
|
|
|
{
|
|
|
|
|
if (var == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &var->value);
|
2022-01-14 22:16:19 +00:00
|
|
|
var->value = apfl_value_move(&value);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static bool
|
|
|
|
|
stack_push(apfl_ctx ctx, struct apfl_value value)
|
|
|
|
|
{
|
|
|
|
|
bool ok = apfl_resizable_append(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-20 21:45:09 +00:00
|
|
|
sizeof(struct apfl_value),
|
|
|
|
|
(void **)&ctx->stack,
|
|
|
|
|
&ctx->stack_len,
|
|
|
|
|
&ctx->stack_cap,
|
|
|
|
|
&value,
|
|
|
|
|
1
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!ok) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &value);
|
2022-01-20 21:45:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-02-10 21:39:39 +00:00
|
|
|
stack_check_index(apfl_ctx ctx, apfl_stackidx *index)
|
2022-01-20 21:45:09 +00:00
|
|
|
{
|
|
|
|
|
if (*index < 0) {
|
|
|
|
|
if ((size_t)-*index > ctx->stack_len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*index = ctx->stack_len + *index;
|
|
|
|
|
} else if ((size_t)*index >= ctx->stack_len) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(0 <= *index && (size_t)*index < ctx->stack_len);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-02-10 21:39:39 +00:00
|
|
|
stack_pop(apfl_ctx ctx, struct apfl_value *value, apfl_stackidx index)
|
2022-01-20 21:45:09 +00:00
|
|
|
{
|
|
|
|
|
if (!stack_check_index(ctx, &index)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*value = ctx->stack[index];
|
|
|
|
|
|
|
|
|
|
assert(apfl_resizable_splice(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-20 21:45:09 +00:00
|
|
|
sizeof(struct apfl_value),
|
|
|
|
|
(void **)ctx->stack,
|
|
|
|
|
&ctx->stack_len,
|
|
|
|
|
&ctx->stack_cap,
|
|
|
|
|
index,
|
|
|
|
|
1,
|
|
|
|
|
NULL,
|
|
|
|
|
0
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_value
|
2022-02-10 21:39:39 +00:00
|
|
|
stack_must_pop(apfl_ctx ctx, apfl_stackidx index)
|
2022-01-20 21:45:09 +00:00
|
|
|
{
|
|
|
|
|
struct apfl_value value;
|
|
|
|
|
assert(stack_pop(ctx, &value, index));
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-02-10 21:39:39 +00:00
|
|
|
stack_get(apfl_ctx ctx, struct apfl_value *value, apfl_stackidx index)
|
2022-01-20 21:45:09 +00:00
|
|
|
{
|
|
|
|
|
if (!stack_check_index(ctx, &index)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*value = apfl_value_incref(ctx->stack[index]);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct apfl_value
|
2022-02-10 21:39:39 +00:00
|
|
|
stack_must_get(apfl_ctx ctx, apfl_stackidx index)
|
2022-01-20 21:45:09 +00:00
|
|
|
{
|
|
|
|
|
struct apfl_value value;
|
|
|
|
|
assert(stack_get(ctx, &value, index));
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2022-02-10 21:39:39 +00:00
|
|
|
stack_drop(apfl_ctx ctx, apfl_stackidx index)
|
2022-01-20 21:45:09 +00:00
|
|
|
{
|
|
|
|
|
struct apfl_value value;
|
|
|
|
|
if (!stack_pop(ctx, &value, index)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &value);
|
2022-01-20 21:45:09 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2022-02-10 21:39:39 +00:00
|
|
|
stack_must_drop(apfl_ctx ctx, apfl_stackidx index)
|
2022-01-20 21:45:09 +00:00
|
|
|
{
|
|
|
|
|
assert(stack_drop(ctx, index));
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-02 16:19:54 +00:00
|
|
|
apfl_ctx
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_ctx_new(struct apfl_allocator allocator)
|
2022-01-02 16:19:54 +00:00
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_ctx ctx = ALLOC_OBJ(allocator, struct apfl_ctx_data);
|
2022-01-02 16:19:54 +00:00
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
if (ctx == NULL) {
|
2022-01-06 21:53:26 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator = allocator;
|
2022-01-20 21:45:09 +00:00
|
|
|
ctx->stack = NULL;
|
|
|
|
|
ctx->stack_len = 0;
|
|
|
|
|
ctx->stack_cap = 0;
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
if (!scope_init(ctx, &ctx->scope)) {
|
|
|
|
|
FREE_OBJ(allocator, ctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-01-02 16:19:54 +00:00
|
|
|
return ctx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
apfl_ctx_destroy(apfl_ctx ctx)
|
|
|
|
|
{
|
2022-01-08 21:09:43 +00:00
|
|
|
if (ctx == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
apfl_hashmap_deinit(&ctx->scope);
|
2022-01-20 21:45:09 +00:00
|
|
|
while (ctx->stack_len > 0) {
|
|
|
|
|
stack_must_drop(ctx, -1);
|
|
|
|
|
}
|
2022-02-09 19:44:21 +00:00
|
|
|
FREE_LIST(ctx->allocator, ctx->stack, ctx->stack_cap);
|
2022-02-08 21:53:13 +00:00
|
|
|
FREE_OBJ(ctx->allocator, ctx);
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-06 21:53:26 +00:00
|
|
|
static variable
|
|
|
|
|
ctx_get_var_for_assignment_inner(apfl_ctx ctx, /*borrowed*/ apfl_refcounted_string name)
|
|
|
|
|
{
|
|
|
|
|
variable var;
|
2022-01-22 14:55:09 +00:00
|
|
|
if (apfl_hashmap_get(&ctx->scope, &name, &var)) {
|
2022-01-06 21:53:26 +00:00
|
|
|
return var;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
if ((var = variable_new(ctx)) == NULL) {
|
2022-01-06 21:53:26 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-22 14:55:09 +00:00
|
|
|
if (!apfl_hashmap_set(&ctx->scope, &name, &var)) {
|
2022-02-08 21:53:13 +00:00
|
|
|
variable_unref(ctx, var);
|
2022-01-06 21:53:26 +00:00
|
|
|
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);
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_refcounted_string_unref(ctx->allocator, name);
|
2022-01-06 21:53:26 +00:00
|
|
|
return var;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static variable
|
|
|
|
|
ctx_get_var(apfl_ctx ctx, apfl_refcounted_string name)
|
|
|
|
|
{
|
|
|
|
|
variable var;
|
2022-01-22 14:55:09 +00:00
|
|
|
bool ok = apfl_hashmap_get(&ctx->scope, &name, &var);
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_refcounted_string_unref(ctx->allocator, name);
|
2022-01-06 21:53:26 +00:00
|
|
|
return ok ? var : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-18 20:18:27 +00:00
|
|
|
static struct apfl_value
|
|
|
|
|
constant_to_value(struct apfl_expr_const *constant)
|
2022-01-02 16:19:54 +00:00
|
|
|
{
|
2022-01-02 16:55:05 +00:00
|
|
|
switch (constant->type) {
|
2022-01-02 16:19:54 +00:00
|
|
|
case APFL_EXPR_CONST_NIL:
|
2022-02-10 20:55:40 +00:00
|
|
|
return (struct apfl_value) { .type = VALUE_NIL };
|
2022-01-02 16:19:54 +00:00
|
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
2022-01-18 20:18:27 +00:00
|
|
|
return (struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_BOOLEAN,
|
2022-01-18 20:18:27 +00:00
|
|
|
.boolean = constant->boolean,
|
|
|
|
|
};
|
2022-01-02 16:19:54 +00:00
|
|
|
case APFL_EXPR_CONST_STRING:
|
2022-01-18 20:18:27 +00:00
|
|
|
return (struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_STRING,
|
2022-01-18 20:18:27 +00:00
|
|
|
.string = apfl_refcounted_string_incref(constant->string),
|
|
|
|
|
};
|
2022-01-02 16:19:54 +00:00
|
|
|
case APFL_EXPR_CONST_NUMBER:
|
2022-01-18 20:18:27 +00:00
|
|
|
return (struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_NUMBER,
|
2022-01-18 20:18:27 +00:00
|
|
|
.number = constant->number,
|
|
|
|
|
};
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
2022-02-10 20:55:40 +00:00
|
|
|
return (struct apfl_value) { .type = VALUE_NIL };
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
match_pattern_add_constraint(
|
2022-02-08 21:53:13 +00:00
|
|
|
struct apfl_allocator allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
struct match_pattern *pattern,
|
|
|
|
|
enum match_pattern_constraint_type type,
|
|
|
|
|
struct apfl_value value
|
|
|
|
|
) {
|
|
|
|
|
struct match_pattern_constraint constraint = {
|
|
|
|
|
.type = type,
|
|
|
|
|
.value = value,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!apfl_resizable_append(
|
2022-02-08 21:53:13 +00:00
|
|
|
allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
sizeof(struct match_pattern_constraint),
|
|
|
|
|
(void **)&pattern->constraints,
|
|
|
|
|
&pattern->constraints_len,
|
2022-02-08 21:53:13 +00:00
|
|
|
&pattern->constraints_cap,
|
2022-01-14 22:16:19 +00:00
|
|
|
&constraint,
|
|
|
|
|
1
|
|
|
|
|
)) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(allocator, &value);
|
2022-01-14 22:16:19 +00:00
|
|
|
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(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
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) {
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_CAP_LIST_WITH_ARGS(
|
|
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
pattern_list->subpatterns,
|
|
|
|
|
pattern_list->subpatterns_len,
|
2022-02-08 21:53:13 +00:00
|
|
|
assignable_list->len,
|
|
|
|
|
match_pattern_deinit,
|
|
|
|
|
ctx
|
2022-01-14 22:16:19 +00:00
|
|
|
);
|
|
|
|
|
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,
|
2022-02-10 20:55:40 +00:00
|
|
|
.value = {.type = VALUE_NIL},
|
2022-01-14 22:16:19 +00:00
|
|
|
.member_keys = NULL,
|
|
|
|
|
.member_keys_len = 0,
|
2022-02-08 21:53:13 +00:00
|
|
|
.member_keys_cap = 0,
|
2022-01-14 22:16:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum match_result result;
|
2022-01-20 21:45:09 +00:00
|
|
|
enum apfl_result eval_result;
|
2022-01-14 22:16:19 +00:00
|
|
|
struct apfl_value str_value;
|
2022-01-20 21:45:09 +00:00
|
|
|
struct apfl_value rhs_value;
|
2022-01-14 22:16:19 +00:00
|
|
|
|
|
|
|
|
next:
|
|
|
|
|
switch (var_or_member->type) {
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
2022-02-14 20:56:36 +00:00
|
|
|
pattern->value.varname = apfl_refcounted_string_incref(var_or_member->var);
|
2022-01-14 22:16:19 +00:00
|
|
|
return MATCH_OK;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
2022-02-10 20:55:40 +00:00
|
|
|
str_value.type = VALUE_STRING;
|
2022-02-14 20:56:36 +00:00
|
|
|
str_value.string = apfl_refcounted_string_incref(var_or_member->dot.rhs);
|
2022-01-14 22:16:19 +00:00
|
|
|
|
|
|
|
|
if (!apfl_resizable_append(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
sizeof(struct apfl_value),
|
|
|
|
|
(void **)&pattern->value.member_keys,
|
|
|
|
|
&pattern->value.member_keys_len,
|
2022-02-08 21:53:13 +00:00
|
|
|
&pattern->value.member_keys_cap,
|
2022-01-14 22:16:19 +00:00
|
|
|
&str_value,
|
|
|
|
|
1
|
|
|
|
|
)) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &str_value);
|
2022-01-14 22:16:19 +00:00
|
|
|
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);
|
2022-01-20 21:45:09 +00:00
|
|
|
switch (eval_result) {
|
2022-01-14 22:16:19 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
rhs_value = stack_must_pop(ctx, -1);
|
|
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
if (!apfl_resizable_append(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
sizeof(struct apfl_value),
|
|
|
|
|
(void **)&pattern->value.member_keys,
|
|
|
|
|
&pattern->value.member_keys_len,
|
2022-02-08 21:53:13 +00:00
|
|
|
&pattern->value.member_keys_cap,
|
2022-01-20 21:45:09 +00:00
|
|
|
&rhs_value,
|
2022-01-14 22:16:19 +00:00
|
|
|
1
|
|
|
|
|
)) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &rhs_value);
|
2022-01-14 22:16:19 +00:00
|
|
|
result = MATCH_FATAL_ERROR;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var_or_member = var_or_member->at.lhs;
|
|
|
|
|
goto next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fail:
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_LIST(
|
|
|
|
|
ctx->allocator,
|
|
|
|
|
pattern->value.member_keys,
|
|
|
|
|
pattern->value.member_keys_cap,
|
|
|
|
|
apfl_value_deinit
|
|
|
|
|
);
|
2022-01-14 22:16:19 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static enum match_result
|
|
|
|
|
match_pattern_from_assignable_inner(
|
|
|
|
|
apfl_ctx ctx,
|
|
|
|
|
struct apfl_expr_assignable *assignable,
|
|
|
|
|
struct match_pattern *pattern
|
|
|
|
|
) {
|
2022-01-20 21:45:09 +00:00
|
|
|
struct apfl_value value;
|
2022-01-14 22:16:19 +00:00
|
|
|
|
|
|
|
|
pattern->type = MPATTERN_BLANK;
|
|
|
|
|
pattern->constraints = NULL;
|
|
|
|
|
pattern->constraints_len = 0;
|
2022-02-08 21:53:13 +00:00
|
|
|
pattern->constraints_cap = 0;
|
2022-01-14 22:16:19 +00:00
|
|
|
|
|
|
|
|
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(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
pattern,
|
|
|
|
|
MPATTERN_CONSTRAINT_EQUALS,
|
2022-01-18 20:18:27 +00:00
|
|
|
constant_to_value(&assignable->constant)
|
2022-01-14 22:16:19 +00:00
|
|
|
)) {
|
|
|
|
|
return MATCH_FATAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
pattern->type = MPATTERN_BLANK;
|
|
|
|
|
return MATCH_OK;
|
|
|
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
2022-01-20 21:45:09 +00:00
|
|
|
switch (evaluate(ctx, assignable->predicate.rhs)) {
|
2022-01-14 22:16:19 +00:00
|
|
|
case APFL_RESULT_OK:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_RESULT_ERR:
|
|
|
|
|
return MATCH_ERROR;
|
|
|
|
|
case APFL_RESULT_ERR_FATAL:
|
|
|
|
|
return MATCH_FATAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
if (!stack_pop(ctx, &value, -1)) {
|
|
|
|
|
return MATCH_FATAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
if (!match_pattern_add_constraint(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
pattern,
|
|
|
|
|
MPATTERN_CONSTRAINT_PREDICATE,
|
2022-01-20 21:45:09 +00:00
|
|
|
apfl_value_move(&value)
|
2022-01-14 22:16:19 +00:00
|
|
|
)) {
|
|
|
|
|
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) {
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_deinit(ctx, pattern);
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
|
|
|
|
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
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_constraint_deinit(struct apfl_allocator allocator, struct match_pattern_constraint *constraint)
|
2022-01-14 22:16:19 +00:00
|
|
|
{
|
|
|
|
|
if (constraint == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(allocator, &constraint->value);
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_deinit(apfl_ctx ctx, struct match_pattern *pattern)
|
2022-01-14 22:16:19 +00:00
|
|
|
{
|
|
|
|
|
if (pattern == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (pattern->type) {
|
|
|
|
|
case MPATTERN_BLANK:
|
|
|
|
|
break;
|
|
|
|
|
case MPATTERN_VALUE:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_refcounted_string_unref(ctx->allocator, pattern->value.varname);
|
|
|
|
|
variable_unref(ctx, pattern->value.var);
|
|
|
|
|
apfl_value_deinit(ctx->allocator, &pattern->value.value);
|
2022-01-14 22:16:19 +00:00
|
|
|
DEINIT_LIST(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
pattern->value.member_keys,
|
2022-02-08 21:53:13 +00:00
|
|
|
pattern->value.member_keys_cap,
|
2022-01-14 22:16:19 +00:00
|
|
|
apfl_value_deinit
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case MPATTERN_LIST:
|
2022-02-08 21:53:13 +00:00
|
|
|
DEINIT_LIST_WITH_ARGS(
|
|
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
pattern->list.subpatterns,
|
|
|
|
|
pattern->list.subpatterns_len,
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_deinit,
|
|
|
|
|
ctx
|
2022-01-14 22:16:19 +00:00
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEINIT_LIST(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
pattern->constraints,
|
2022-02-08 21:53:13 +00:00
|
|
|
pattern->constraints_cap,
|
2022-01-14 22:16:19 +00:00
|
|
|
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(
|
2022-02-08 21:53:13 +00:00
|
|
|
struct apfl_allocator allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
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(
|
2022-02-08 21:53:13 +00:00
|
|
|
allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
apfl_list_incref(list),
|
|
|
|
|
i,
|
|
|
|
|
&list_item
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
enum match_result result = match_pattern_match(
|
2022-02-08 21:53:13 +00:00
|
|
|
allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
&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(
|
2022-02-08 21:53:13 +00:00
|
|
|
allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
apfl_list_incref(list),
|
|
|
|
|
list_index,
|
|
|
|
|
&list_item
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
enum match_result result = match_pattern_match(
|
2022-02-08 21:53:13 +00:00
|
|
|
allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
&pattern_list->subpatterns[subpattern_index],
|
|
|
|
|
apfl_value_move(&list_item)
|
|
|
|
|
);
|
|
|
|
|
if (result != MATCH_OK) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_editable_list mid_builder = apfl_editable_list_new(allocator);
|
2022-01-14 22:16:19 +00:00
|
|
|
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(
|
2022-02-08 21:53:13 +00:00
|
|
|
allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
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(
|
2022-02-08 21:53:13 +00:00
|
|
|
allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
&pattern_list->subpatterns[pattern_list->expand_index],
|
|
|
|
|
(struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_LIST,
|
2022-01-14 22:16:19 +00:00
|
|
|
.list = mid_list,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum match_result
|
|
|
|
|
match_pattern_match_list(
|
2022-02-08 21:53:13 +00:00
|
|
|
struct apfl_allocator allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
struct match_pattern_list *pattern_list,
|
|
|
|
|
struct apfl_value value
|
|
|
|
|
) {
|
2022-02-10 20:55:40 +00:00
|
|
|
if (value.type != VALUE_LIST) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(allocator, &value);
|
2022-01-14 22:16:19 +00:00
|
|
|
return MATCH_DOESNT_MATCH;
|
|
|
|
|
}
|
|
|
|
|
apfl_list list = apfl_list_incref(value.list);
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(allocator, &value);
|
2022-01-14 22:16:19 +00:00
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
enum match_result result = match_pattern_match_list_inner(allocator, pattern_list, list);
|
|
|
|
|
apfl_list_unref(allocator, list);
|
2022-01-14 22:16:19 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum match_result
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_match(struct apfl_allocator allocator, struct match_pattern *pattern, struct apfl_value value)
|
2022-01-14 22:16:19 +00:00
|
|
|
{
|
2022-02-25 20:38:14 +00:00
|
|
|
// We put the constraints into the list from the outside in, so we need
|
2022-01-14 22:16:19 +00:00
|
|
|
// 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) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(allocator, &value);
|
2022-01-14 22:16:19 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (pattern->type) {
|
|
|
|
|
case MPATTERN_BLANK:
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(allocator, &value);
|
2022-01-14 22:16:19 +00:00
|
|
|
return MATCH_OK;
|
|
|
|
|
case MPATTERN_VALUE:
|
|
|
|
|
pattern->value.value = apfl_value_move(&value);
|
|
|
|
|
return MATCH_OK;
|
|
|
|
|
case MPATTERN_LIST:
|
2022-02-08 21:53:13 +00:00
|
|
|
return match_pattern_match_list(allocator, &pattern->list, apfl_value_move(&value));
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
|
return MATCH_FATAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-15 22:09:24 +00:00
|
|
|
static enum match_result
|
|
|
|
|
match_pattern_apply_value_keys(
|
2022-02-08 21:53:13 +00:00
|
|
|
struct apfl_allocator allocator,
|
2022-01-15 22:09:24 +00:00
|
|
|
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
|
|
|
|
|
)) {
|
2022-02-10 20:55:40 +00:00
|
|
|
if (value.type != VALUE_DICT) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(allocator, &value);
|
2022-01-15 22:09:24 +00:00
|
|
|
return MATCH_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
next_ed = apfl_editable_dict_new_from_dict(allocator, value.dict);
|
2022-01-15 22:09:24 +00:00
|
|
|
} else {
|
|
|
|
|
// Be nice and create the missing dictionary
|
2022-02-08 21:53:13 +00:00
|
|
|
next_ed = apfl_editable_dict_new(allocator);
|
2022-01-15 22:09:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (next_ed == NULL) {
|
|
|
|
|
return MATCH_FATAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum match_result result = match_pattern_apply_value_keys(
|
2022-02-08 21:53:13 +00:00
|
|
|
allocator,
|
2022-01-15 22:09:24 +00:00
|
|
|
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) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_DICT,
|
2022-01-15 22:09:24 +00:00
|
|
|
.dict = next_dict,
|
|
|
|
|
}
|
|
|
|
|
)) {
|
|
|
|
|
return MATCH_FATAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return MATCH_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
static enum match_result
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_apply_value(apfl_ctx ctx, struct match_pattern_value *pattern_value)
|
2022-01-14 22:16:19 +00:00
|
|
|
{
|
|
|
|
|
if (pattern_value->var == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-15 22:09:24 +00:00
|
|
|
if (pattern_value->member_keys_len == 0) {
|
2022-02-08 21:53:13 +00:00
|
|
|
variable_set(ctx, pattern_value->var, apfl_value_move(&pattern_value->value));
|
2022-01-15 22:09:24 +00:00
|
|
|
return MATCH_OK;
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-10 20:55:40 +00:00
|
|
|
if (pattern_value->var->value.type != VALUE_DICT) {
|
2022-01-15 22:09:24 +00:00
|
|
|
return MATCH_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_editable_dict ed = apfl_editable_dict_new_from_dict(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-15 22:09:24 +00:00
|
|
|
apfl_dict_incref(pattern_value->var->value.dict)
|
|
|
|
|
);
|
|
|
|
|
if (ed == NULL) {
|
|
|
|
|
return MATCH_FATAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum match_result result = match_pattern_apply_value_keys(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-15 22:09:24 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
variable_set(ctx, pattern_value->var, (struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_DICT,
|
2022-01-15 22:09:24 +00:00
|
|
|
.dict = d,
|
|
|
|
|
});
|
|
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
return MATCH_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum match_result
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_apply(apfl_ctx ctx, struct match_pattern *pattern)
|
2022-01-14 22:16:19 +00:00
|
|
|
{
|
|
|
|
|
switch (pattern->type) {
|
|
|
|
|
case MPATTERN_BLANK:
|
|
|
|
|
return MATCH_OK;
|
|
|
|
|
case MPATTERN_VALUE:
|
2022-02-08 21:53:13 +00:00
|
|
|
return match_pattern_apply_value(ctx, &pattern->value);
|
2022-01-14 22:16:19 +00:00
|
|
|
case MPATTERN_LIST:
|
|
|
|
|
for (size_t i = 0; i < pattern->list.subpatterns_len; i++) {
|
|
|
|
|
enum match_result result = match_pattern_apply(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx,
|
2022-01-14 22:16:19 +00:00
|
|
|
&pattern->list.subpatterns[i]
|
|
|
|
|
);
|
|
|
|
|
if (result != MATCH_OK) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return MATCH_OK;
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
2022-01-14 22:16:19 +00:00
|
|
|
return MATCH_FATAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
|
|
|
|
evaluate_constant(apfl_ctx ctx, struct apfl_expr_const *constant)
|
2022-01-14 22:16:19 +00:00
|
|
|
{
|
2022-01-20 21:45:09 +00:00
|
|
|
return stack_push(ctx, constant_to_value(constant))
|
|
|
|
|
? APFL_RESULT_OK
|
|
|
|
|
: APFL_RESULT_ERR_FATAL;
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-04 22:11:38 +00:00
|
|
|
evaluate_list_expr_to_list(apfl_ctx ctx, struct apfl_expr *expr, apfl_editable_list elist)
|
2022-01-02 16:19:54 +00:00
|
|
|
{
|
2022-01-20 21:45:09 +00:00
|
|
|
enum apfl_result result = evaluate(ctx, expr);
|
|
|
|
|
if (result != APFL_RESULT_OK) {
|
2022-01-02 16:19:54 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
struct apfl_value value;
|
|
|
|
|
if (!stack_pop(ctx, &value, -1)) {
|
|
|
|
|
return APFL_RESULT_ERR_FATAL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-10 20:55:40 +00:00
|
|
|
if (value.type != VALUE_LIST) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &value);
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR;
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
apfl_list list = apfl_list_incref(value.list);
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &value);
|
2022-01-02 16:19:54 +00:00
|
|
|
|
2022-01-04 22:11:38 +00:00
|
|
|
if (!apfl_editable_list_append_list(elist, list)) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR_FATAL;
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_OK;
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-04 22:11:38 +00:00
|
|
|
evaluate_expr_list_into_list(apfl_ctx ctx, struct apfl_expr_list *list, apfl_editable_list elist)
|
2022-01-02 16:19:54 +00:00
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < list->len; i++) {
|
|
|
|
|
struct apfl_expr_list_item *item = &list->items[i];
|
|
|
|
|
|
|
|
|
|
if (item->expand) {
|
2022-01-20 21:45:09 +00:00
|
|
|
TRY(evaluate_list_expr_to_list(ctx, item->expr, elist));
|
2022-01-02 16:19:54 +00:00
|
|
|
} else {
|
2022-01-20 21:45:09 +00:00
|
|
|
TRY(evaluate(ctx, item->expr));
|
|
|
|
|
if (!apfl_editable_list_append(elist, stack_must_pop(ctx, -1))) {
|
|
|
|
|
return APFL_RESULT_ERR_FATAL;
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_OK;
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-02 16:19:54 +00:00
|
|
|
evaluate_list(apfl_ctx ctx, struct apfl_expr_list *list)
|
|
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_editable_list elist = apfl_editable_list_new(ctx->allocator);
|
2022-01-04 22:11:38 +00:00
|
|
|
if (elist == NULL) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR_FATAL;
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
enum apfl_result result = evaluate_expr_list_into_list(ctx, list, elist);
|
|
|
|
|
if (result != APFL_RESULT_OK) {
|
2022-01-04 22:11:38 +00:00
|
|
|
apfl_editable_list_destroy(elist);
|
2022-01-02 16:19:54 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-04 22:11:38 +00:00
|
|
|
apfl_list out = apfl_editable_list_finalize(elist);
|
|
|
|
|
if (out == NULL) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR_FATAL;
|
2022-01-04 22:11:38 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
if (!stack_push(ctx, (struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_LIST,
|
2022-01-20 21:45:09 +00:00
|
|
|
.list = out,
|
|
|
|
|
})) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_list_unref(ctx->allocator, out);
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR_FATAL;
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_OK;
|
|
|
|
|
}
|
2022-01-04 20:51:44 +00:00
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
|
|
|
|
evaluate_dict_into_editable(
|
|
|
|
|
apfl_ctx ctx,
|
|
|
|
|
struct apfl_expr_dict *dict,
|
|
|
|
|
apfl_editable_dict ed
|
|
|
|
|
) {
|
|
|
|
|
for (size_t i = 0; i < dict->len; i++) {
|
|
|
|
|
TRY(evaluate(ctx, dict->items[i].k));
|
|
|
|
|
TRY(evaluate(ctx, dict->items[i].v));
|
2022-01-04 20:51:44 +00:00
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
struct apfl_value value = stack_must_pop(ctx, -1);
|
|
|
|
|
struct apfl_value key = stack_must_pop(ctx, -1);
|
2022-01-04 20:51:44 +00:00
|
|
|
|
|
|
|
|
if (!apfl_editable_dict_set(
|
|
|
|
|
ed,
|
|
|
|
|
apfl_value_move(&key),
|
|
|
|
|
apfl_value_move(&value)
|
|
|
|
|
)) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR_FATAL;
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum apfl_result
|
|
|
|
|
evaluate_dict(apfl_ctx ctx, struct apfl_expr_dict *dict)
|
|
|
|
|
{
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_editable_dict ed = apfl_editable_dict_new(ctx->allocator);
|
2022-01-20 21:45:09 +00:00
|
|
|
|
|
|
|
|
if (ed == NULL) {
|
|
|
|
|
return APFL_RESULT_ERR_FATAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum apfl_result result = evaluate_dict_into_editable(ctx, dict, ed);
|
|
|
|
|
if (result != APFL_RESULT_OK) {
|
|
|
|
|
apfl_editable_dict_destroy(ed);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-04 20:51:44 +00:00
|
|
|
apfl_dict out = apfl_editable_dict_finalize(ed);
|
|
|
|
|
if (out == NULL) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR_FATAL;
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
if (!stack_push(ctx, (struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_DICT,
|
2022-01-20 21:45:09 +00:00
|
|
|
.dict = out,
|
|
|
|
|
})) {
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_dict_unref(ctx->allocator, out);
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR_FATAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return APFL_RESULT_OK;
|
2022-01-04 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-04 23:25:41 +00:00
|
|
|
evaluate_dot(apfl_ctx ctx, struct apfl_expr_dot *dot)
|
|
|
|
|
{
|
2022-01-20 21:45:09 +00:00
|
|
|
TRY(evaluate(ctx, dot->lhs));
|
|
|
|
|
struct apfl_value lhs = stack_must_pop(ctx, -1);
|
2022-01-04 23:25:41 +00:00
|
|
|
|
|
|
|
|
struct apfl_value key = (struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_STRING,
|
2022-01-18 20:18:27 +00:00
|
|
|
.string = apfl_refcounted_string_incref(dot->rhs),
|
2022-01-04 23:25:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct apfl_value out;
|
2022-02-10 20:51:19 +00:00
|
|
|
if (apfl_value_get_item(ctx->allocator, apfl_value_move(&lhs), apfl_value_move(&key), &out) != GET_ITEM_OK) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR; // TODO: Describe error
|
2022-01-04 23:25:41 +00:00
|
|
|
}
|
2022-01-20 21:45:09 +00:00
|
|
|
|
|
|
|
|
return stack_push(ctx, out)
|
|
|
|
|
? APFL_RESULT_OK
|
|
|
|
|
: APFL_RESULT_ERR_FATAL;
|
2022-01-04 23:25:41 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-04 23:25:41 +00:00
|
|
|
evaluate_at(apfl_ctx ctx, struct apfl_expr_at *at)
|
|
|
|
|
{
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
TRY(evaluate(ctx, at->lhs));
|
|
|
|
|
TRY(evaluate(ctx, at->rhs));
|
2022-01-04 23:25:41 +00:00
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
struct apfl_value rhs = stack_must_pop(ctx, -1);
|
|
|
|
|
struct apfl_value lhs = stack_must_pop(ctx, -1);
|
2022-01-04 23:25:41 +00:00
|
|
|
|
|
|
|
|
struct apfl_value out;
|
2022-02-10 20:51:19 +00:00
|
|
|
if (apfl_value_get_item(ctx->allocator, apfl_value_move(&lhs), apfl_value_move(&rhs), &out) != GET_ITEM_OK) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR; // TODO: Describe error
|
2022-01-04 23:25:41 +00:00
|
|
|
}
|
2022-01-20 21:45:09 +00:00
|
|
|
|
|
|
|
|
return stack_push(ctx, out)
|
|
|
|
|
? APFL_RESULT_OK
|
|
|
|
|
: APFL_RESULT_ERR_FATAL;
|
2022-01-04 23:25:41 +00:00
|
|
|
}
|
2022-01-04 20:51:44 +00:00
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-14 22:16:19 +00:00
|
|
|
failing_match_result_to_apfl_result(enum match_result match_result)
|
2022-01-06 21:53:26 +00:00
|
|
|
{
|
2022-01-14 22:16:19 +00:00
|
|
|
if (match_result == MATCH_FATAL_ERROR) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR_FATAL;
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR;
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
2022-01-06 21:53:26 +00:00
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
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;
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
if (!match_pattern_create_vars(ctx, pattern)) {
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_deinit(ctx, pattern);
|
2022-01-14 22:16:19 +00:00
|
|
|
return MATCH_FATAL_ERROR;
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
return MATCH_OK;
|
|
|
|
|
}
|
2022-01-06 21:53:26 +00:00
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
static enum match_result
|
|
|
|
|
evaluate_assignment_finish(
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_ctx ctx,
|
2022-01-14 22:16:19 +00:00
|
|
|
struct match_pattern *pattern,
|
|
|
|
|
struct apfl_value value
|
|
|
|
|
) {
|
|
|
|
|
enum match_result match_result = match_pattern_match(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx->allocator,
|
2022-01-14 22:16:19 +00:00
|
|
|
pattern,
|
|
|
|
|
apfl_value_move(&value)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (match_result != MATCH_OK) {
|
|
|
|
|
return match_result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
return match_pattern_apply(ctx, pattern);
|
2022-01-14 22:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-14 22:16:19 +00:00
|
|
|
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);
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
enum apfl_result result = evaluate(ctx, assignment->rhs);
|
|
|
|
|
if (result != APFL_RESULT_OK) {
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_deinit(ctx, &pattern);
|
2022-01-06 21:53:26 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 22:16:19 +00:00
|
|
|
match_result = evaluate_assignment_finish(
|
2022-02-08 21:53:13 +00:00
|
|
|
ctx,
|
2022-01-14 22:16:19 +00:00
|
|
|
&pattern,
|
2022-01-20 21:45:09 +00:00
|
|
|
stack_must_get(ctx, -1) // stack_must_get instead of stack_must_pop,
|
2022-02-10 21:04:48 +00:00
|
|
|
// so the value is still on the stack on return.
|
2022-01-14 22:16:19 +00:00
|
|
|
);
|
|
|
|
|
|
2022-02-08 21:53:13 +00:00
|
|
|
match_pattern_deinit(ctx, &pattern);
|
2022-01-14 22:16:19 +00:00
|
|
|
if (match_result != MATCH_OK) {
|
2022-01-20 21:45:09 +00:00
|
|
|
stack_must_drop(ctx, -1);
|
2022-01-14 22:16:19 +00:00
|
|
|
return failing_match_result_to_apfl_result(match_result);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_OK;
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-18 20:18:27 +00:00
|
|
|
evaluate_var(apfl_ctx ctx, apfl_refcounted_string varname)
|
2022-01-06 21:53:26 +00:00
|
|
|
{
|
2022-01-18 20:18:27 +00:00
|
|
|
variable var = ctx_get_var(ctx, varname);
|
2022-01-06 21:53:26 +00:00
|
|
|
if (var == NULL) {
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR;
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
bool ok = stack_push(ctx, apfl_value_incref(var->value));
|
2022-02-08 21:53:13 +00:00
|
|
|
variable_unref(ctx, var);
|
2022-01-20 21:45:09 +00:00
|
|
|
return ok
|
|
|
|
|
? APFL_RESULT_OK
|
|
|
|
|
: APFL_RESULT_ERR_FATAL;
|
2022-01-06 21:53:26 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
static enum apfl_result
|
2022-01-02 16:19:54 +00:00
|
|
|
evaluate(apfl_ctx ctx, struct apfl_expr *expr)
|
|
|
|
|
{
|
|
|
|
|
switch (expr->type) {
|
|
|
|
|
case APFL_EXPR_CONSTANT:
|
2022-01-20 21:45:09 +00:00
|
|
|
return evaluate_constant(ctx, &expr->constant);
|
2022-01-02 16:19:54 +00:00
|
|
|
case APFL_EXPR_LIST:
|
|
|
|
|
return evaluate_list(ctx, &expr->list);
|
2022-01-04 20:51:44 +00:00
|
|
|
case APFL_EXPR_DICT:
|
|
|
|
|
return evaluate_dict(ctx, &expr->dict);
|
2022-01-04 23:25:41 +00:00
|
|
|
case APFL_EXPR_DOT:
|
|
|
|
|
return evaluate_dot(ctx, &expr->dot);
|
|
|
|
|
case APFL_EXPR_AT:
|
|
|
|
|
return evaluate_at(ctx, &expr->at);
|
2022-01-06 21:53:26 +00:00
|
|
|
case APFL_EXPR_ASSIGNMENT:
|
|
|
|
|
return evaluate_assignment(ctx, &expr->assignment);
|
2022-01-02 16:19:54 +00:00
|
|
|
case APFL_EXPR_VAR:
|
2022-01-18 20:18:27 +00:00
|
|
|
return evaluate_var(ctx, apfl_refcounted_string_incref(expr->var));
|
2022-01-08 22:20:29 +00:00
|
|
|
case APFL_EXPR_BLANK:
|
2022-01-20 21:45:09 +00:00
|
|
|
return stack_push(ctx, (struct apfl_value) {
|
2022-02-10 20:55:40 +00:00
|
|
|
.type = VALUE_NIL,
|
2022-01-20 21:45:09 +00:00
|
|
|
})
|
|
|
|
|
? APFL_RESULT_OK
|
|
|
|
|
: APFL_RESULT_ERR_FATAL;
|
2022-01-02 16:19:54 +00:00
|
|
|
case APFL_EXPR_CALL:
|
|
|
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
|
|
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
|
|
|
|
break; // Not implemented yet
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
return APFL_RESULT_ERR;
|
2022-01-02 16:19:54 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 21:45:09 +00:00
|
|
|
enum apfl_result
|
2022-01-02 16:19:54 +00:00
|
|
|
apfl_eval(apfl_ctx ctx, struct apfl_expr expr)
|
|
|
|
|
{
|
2022-01-20 21:45:09 +00:00
|
|
|
enum apfl_result result = evaluate(ctx, &expr);
|
2022-02-08 21:53:13 +00:00
|
|
|
|
|
|
|
|
// TODO: expr might have been allocated with another allocator. The apfl_ctx
|
|
|
|
|
// should probably also handle parsing and no longer accept
|
|
|
|
|
// expressions directly.
|
|
|
|
|
apfl_expr_deinit(ctx->allocator, &expr);
|
|
|
|
|
|
2022-01-02 16:19:54 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
2022-01-20 21:45:09 +00:00
|
|
|
|
|
|
|
|
void
|
2022-02-10 21:39:39 +00:00
|
|
|
apfl_debug_print_val(apfl_ctx ctx, apfl_stackidx index, FILE *f)
|
2022-01-20 21:45:09 +00:00
|
|
|
{
|
|
|
|
|
struct apfl_value value;
|
|
|
|
|
if (!stack_pop(ctx, &value, index)) {
|
|
|
|
|
fprintf(f, "apfl_debug_print_val: Invalid stack index %d\n", index);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apfl_value_print(value, f);
|
2022-02-08 21:53:13 +00:00
|
|
|
apfl_value_deinit(ctx->allocator, &value);
|
2022-01-20 21:45:09 +00:00
|
|
|
}
|