Pairs are 2-tuples of values that are constructed and matched with the `::`
operator. They can also be matched with a `:` operator, the LHS is an
expression then, the pair will then only match, if the LHS matches the
result of that expression.
Pairs should be useful to do something similar what sum types / tagged
unions do in statically typed languages, e.g. you could write something
like:
some := (symbol) # Somthing that creates a unique value
filter-map := {
_ [] -> []
f [x ~xs] ->
{
some:y -> [y ~(filter-map f xs)]
nil -> filter-map f xs
} (f x)
}
filter-map {
x?even -> some :: (* x 10)
_ -> nil
} some-list
1200 lines
37 KiB
C
1200 lines
37 KiB
C
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#include "apfl.h"
|
|
|
|
#include "alloc.h"
|
|
#include "format.h"
|
|
|
|
#define TRY(x) do { if (!(x)) return false; } while (0)
|
|
|
|
void
|
|
apfl_expr_deinit(struct apfl_allocator allocator, struct apfl_expr *expr)
|
|
{
|
|
switch (expr->type) {
|
|
case APFL_EXPR_LIST:
|
|
apfl_expr_list_deinit(allocator, &expr->list);
|
|
break;
|
|
case APFL_EXPR_DICT:
|
|
apfl_expr_dict_deinit(allocator, &expr->dict);
|
|
break;
|
|
case APFL_EXPR_CALL:
|
|
apfl_expr_call_deinit(allocator, &expr->call);
|
|
break;
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
|
apfl_expr_body_deinit(allocator, &expr->simple_func);
|
|
break;
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
|
apfl_expr_complex_func_deinit(allocator, &expr->complex_func);
|
|
break;
|
|
case APFL_EXPR_ASSIGNMENT:
|
|
apfl_expr_assignment_deinit(allocator, &expr->assignment);
|
|
break;
|
|
case APFL_EXPR_DOT:
|
|
apfl_expr_dot_deinit(allocator, &expr->dot);
|
|
break;
|
|
case APFL_EXPR_AT:
|
|
apfl_expr_pair_deinit(allocator, &expr->at);
|
|
break;
|
|
case APFL_EXPR_PAIR:
|
|
apfl_expr_pair_deinit(allocator, &expr->pair);
|
|
break;
|
|
case APFL_EXPR_CONSTANT:
|
|
apfl_expr_const_deinit(allocator, &expr->constant);
|
|
break;
|
|
case APFL_EXPR_VAR:
|
|
apfl_string_deinit(allocator, &expr->var);
|
|
break;
|
|
case APFL_EXPR_BLANK:
|
|
// nop
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
apfl_expr_list_deinit(struct apfl_allocator allocator, struct apfl_expr_list *list)
|
|
{
|
|
DEINIT_LIST(allocator, list->items, list->len, apfl_expr_list_item_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_list_item *item)
|
|
{
|
|
DESTROY(allocator, item->expr, apfl_expr_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_dict_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_dict_pair *pair)
|
|
{
|
|
DESTROY(allocator, pair->k, apfl_expr_deinit);
|
|
DESTROY(allocator, pair->v, apfl_expr_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_dict_deinit(struct apfl_allocator allocator, struct apfl_expr_dict *dict)
|
|
{
|
|
DEINIT_CAP_LIST(allocator, dict->items, dict->len, dict->cap, apfl_expr_dict_pair_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_call_deinit(struct apfl_allocator allocator, struct apfl_expr_call *call)
|
|
{
|
|
DESTROY(allocator, call->callee, apfl_expr_deinit);
|
|
apfl_expr_list_deinit(allocator, &call->arguments);
|
|
}
|
|
|
|
void
|
|
apfl_expr_body_deinit(struct apfl_allocator allocator, struct apfl_expr_body *body)
|
|
{
|
|
DEINIT_CAP_LIST(allocator, body->items, body->len, body->cap, apfl_expr_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_const_deinit(struct apfl_allocator allocator, struct apfl_expr_const *constant)
|
|
{
|
|
switch (constant->type) {
|
|
case APFL_EXPR_CONST_NIL:
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
|
case APFL_EXPR_CONST_NUMBER:
|
|
// nop
|
|
break;
|
|
case APFL_EXPR_CONST_STRING:
|
|
apfl_string_deinit(allocator, &constant->string);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define DEINIT_GENERIC_LHS_RHS_EXPR(allocator, x, lhs_deiniter) \
|
|
do { \
|
|
DESTROY(allocator, x->lhs, lhs_deiniter); \
|
|
DESTROY(allocator, x->rhs, apfl_expr_deinit); \
|
|
} while (0)
|
|
|
|
void
|
|
apfl_expr_param_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_param_predicate *pred)
|
|
{
|
|
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_param_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_param_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_param_pair *pair)
|
|
{
|
|
DESTROY(allocator, pair->lhs, apfl_expr_param_deinit);
|
|
DESTROY(allocator, pair->rhs, apfl_expr_param_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_param_tagged_deinit(struct apfl_allocator allocator, struct apfl_expr_param_tagged *tagged)
|
|
{
|
|
DESTROY(allocator, tagged->lhs, apfl_expr_deinit);
|
|
DESTROY(allocator, tagged->rhs, apfl_expr_param_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_params_deinit(struct apfl_allocator allocator, struct apfl_expr_params *params)
|
|
{
|
|
DEINIT_CAP_LIST(allocator, params->params, params->len, params->cap, apfl_expr_params_item_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_params_item_deinit(struct apfl_allocator allocator, struct apfl_expr_params_item *item)
|
|
{
|
|
apfl_expr_param_deinit(allocator, &item->param);
|
|
}
|
|
|
|
void
|
|
apfl_expr_param_deinit(struct apfl_allocator allocator, struct apfl_expr_param *param)
|
|
{
|
|
switch (param->type) {
|
|
case APFL_EXPR_PARAM_VAR:
|
|
apfl_string_deinit(allocator, ¶m->var);
|
|
break;
|
|
case APFL_EXPR_PARAM_CONSTANT:
|
|
apfl_expr_const_deinit(allocator, ¶m->constant);
|
|
break;
|
|
case APFL_EXPR_PARAM_PREDICATE:
|
|
apfl_expr_param_predicate_deinit(allocator, ¶m->predicate);
|
|
break;
|
|
case APFL_EXPR_PARAM_LIST:
|
|
apfl_expr_params_deinit(allocator, ¶m->list);
|
|
break;
|
|
case APFL_EXPR_PARAM_PAIR:
|
|
apfl_expr_param_pair_deinit(allocator, ¶m->pair);
|
|
break;
|
|
case APFL_EXPR_PARAM_TAGGED:
|
|
apfl_expr_param_tagged_deinit(allocator, ¶m->tagged);
|
|
break;
|
|
case APFL_EXPR_PARAM_BLANK:
|
|
// nop
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
apfl_expr_subfunc_deinit(struct apfl_allocator allocator, struct apfl_expr_subfunc *subfunc)
|
|
{
|
|
apfl_expr_params_deinit(allocator, &subfunc->params);
|
|
apfl_expr_body_deinit(allocator, &subfunc->body);
|
|
}
|
|
|
|
void
|
|
apfl_expr_complex_func_deinit(struct apfl_allocator allocator, struct apfl_expr_complex_func *cf)
|
|
{
|
|
DEINIT_CAP_LIST(allocator, cf->subfuncs, cf->len, cf->cap, apfl_expr_subfunc_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_predicate_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_predicate *pred)
|
|
{
|
|
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pred, apfl_expr_assignable_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_list_item_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list_item *item)
|
|
{
|
|
apfl_expr_assignable_deinit(allocator, &item->assignable);
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_list_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_list *list)
|
|
{
|
|
DEINIT_LIST(allocator, list->items, list->len, apfl_expr_assignable_list_item_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_pair *pair)
|
|
{
|
|
DESTROY(allocator, pair->lhs, apfl_expr_assignable_deinit);
|
|
DESTROY(allocator, pair->rhs, apfl_expr_assignable_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_tagged_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_tagged *tagged)
|
|
{
|
|
DESTROY(allocator, tagged->lhs, apfl_expr_deinit);
|
|
DESTROY(allocator, tagged->rhs, apfl_expr_assignable_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_dot *dot)
|
|
{
|
|
DESTROY(allocator, dot->lhs, apfl_expr_assignable_var_or_member_deinit);
|
|
apfl_string_deinit(allocator, &dot->rhs);
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_var_or_member_at_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member_at *at)
|
|
{
|
|
DESTROY(allocator, at->lhs, apfl_expr_assignable_var_or_member_deinit);
|
|
DESTROY(allocator, at->rhs, apfl_expr_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_var_or_member_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable_var_or_member *var_or_member)
|
|
{
|
|
switch (var_or_member->type) {
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
|
apfl_string_deinit(allocator, &var_or_member->var);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
|
apfl_expr_assignable_var_or_member_dot_deinit(allocator, &var_or_member->dot);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
|
apfl_expr_assignable_var_or_member_at_deinit(allocator, &var_or_member->at);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignable_deinit(struct apfl_allocator allocator, struct apfl_expr_assignable *a)
|
|
{
|
|
switch (a->type) {
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
|
apfl_expr_assignable_var_or_member_deinit(allocator, &a->var_or_member);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
|
apfl_expr_const_deinit(allocator, &a->constant);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
|
apfl_expr_assignable_predicate_deinit(allocator, &a->predicate);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_LIST:
|
|
apfl_expr_assignable_list_deinit(allocator, &a->list);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_PAIR:
|
|
apfl_expr_assignable_pair_deinit(allocator, &a->pair);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_TAGGED:
|
|
apfl_expr_assignable_tagged_deinit(allocator, &a->tagged);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_BLANK:
|
|
// nop
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
apfl_expr_assignment_deinit(struct apfl_allocator allocator, struct apfl_expr_assignment *a)
|
|
{
|
|
apfl_expr_assignable_deinit(allocator, &a->lhs);
|
|
DESTROY(allocator, a->rhs, apfl_expr_deinit);
|
|
}
|
|
|
|
void
|
|
apfl_expr_dot_deinit(struct apfl_allocator allocator, struct apfl_expr_dot *dot)
|
|
{
|
|
DESTROY(allocator, dot->lhs, apfl_expr_deinit);
|
|
apfl_string_deinit(allocator, &dot->rhs);
|
|
}
|
|
|
|
void
|
|
apfl_expr_pair_deinit(struct apfl_allocator allocator, struct apfl_expr_pair *pair)
|
|
{
|
|
DEINIT_GENERIC_LHS_RHS_EXPR(allocator, pair, apfl_expr_deinit);
|
|
}
|
|
|
|
// Move functions
|
|
|
|
struct apfl_expr
|
|
apfl_expr_move(struct apfl_expr *in)
|
|
{
|
|
struct apfl_expr out = *in;
|
|
switch (in->type) {
|
|
case APFL_EXPR_LIST:
|
|
out.list = apfl_expr_list_move(&in->list);
|
|
break;
|
|
case APFL_EXPR_DICT:
|
|
out.dict = apfl_expr_dict_move(&in->dict);
|
|
break;
|
|
case APFL_EXPR_CALL:
|
|
out.call = apfl_expr_call_move(&in->call);
|
|
break;
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
|
out.simple_func = apfl_expr_body_move(&in->simple_func);
|
|
break;
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
|
out.complex_func = apfl_expr_complex_func_move(&in->complex_func);
|
|
break;
|
|
case APFL_EXPR_ASSIGNMENT:
|
|
out.assignment = apfl_expr_assignment_move(&in->assignment);
|
|
break;
|
|
case APFL_EXPR_DOT:
|
|
out.dot = apfl_expr_dot_move(&in->dot);
|
|
break;
|
|
case APFL_EXPR_AT:
|
|
out.at = apfl_expr_pair_move(&in->at);
|
|
break;
|
|
case APFL_EXPR_PAIR:
|
|
out.pair = apfl_expr_pair_move(&in->pair);
|
|
break;
|
|
case APFL_EXPR_CONSTANT:
|
|
out.constant = apfl_expr_const_move(&in->constant);
|
|
break;
|
|
case APFL_EXPR_VAR:
|
|
out.var = apfl_string_move(&in->var);
|
|
break;
|
|
case APFL_EXPR_BLANK:
|
|
// nop
|
|
break;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
#define MOVE_LIST(out, in, items, len) \
|
|
do { \
|
|
MOVEPTR(out.items, in->items); \
|
|
out.len = in->len; \
|
|
in->len = 0; \
|
|
} while (0)
|
|
|
|
#define MOVE_CAP_LIST(out, in, items, len, cap) \
|
|
do { \
|
|
MOVEPTR(out.items, in->items); \
|
|
out.len = in->len; \
|
|
out.cap = in->cap; \
|
|
in->len = 0; \
|
|
in->cap = 0; \
|
|
} while (0)
|
|
|
|
struct apfl_expr_list
|
|
apfl_expr_list_move(struct apfl_expr_list *in)
|
|
{
|
|
struct apfl_expr_list out;
|
|
MOVE_LIST(out, in, items, len);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_list_item
|
|
apfl_expr_list_item_move(struct apfl_expr_list_item *in)
|
|
{
|
|
struct apfl_expr_list_item out = *in;
|
|
in->expr = NULL;
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_dict_pair
|
|
apfl_expr_dict_pair_move(struct apfl_expr_dict_pair *in)
|
|
{
|
|
struct apfl_expr_dict_pair out = *in;
|
|
in->k = NULL;
|
|
in->v = NULL;
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_dict
|
|
apfl_expr_dict_move(struct apfl_expr_dict *in)
|
|
{
|
|
struct apfl_expr_dict out;
|
|
MOVE_CAP_LIST(out, in, items, len, cap);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_call
|
|
apfl_expr_call_move(struct apfl_expr_call *in)
|
|
{
|
|
struct apfl_expr_call out;
|
|
|
|
MOVEPTR(out.callee, in->callee);
|
|
out.arguments = apfl_expr_list_move(&in->arguments);
|
|
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_body
|
|
apfl_expr_body_move(struct apfl_expr_body *in)
|
|
{
|
|
struct apfl_expr_body out;
|
|
MOVE_CAP_LIST(out, in, items, len, cap);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_const
|
|
apfl_expr_const_move(struct apfl_expr_const *in)
|
|
{
|
|
struct apfl_expr_const out = *in;
|
|
|
|
switch (in->type) {
|
|
case APFL_EXPR_CONST_NIL:
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
|
case APFL_EXPR_CONST_NUMBER:
|
|
// nop
|
|
break;
|
|
case APFL_EXPR_CONST_STRING:
|
|
out.string = apfl_string_move(&in->string);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
#define GENERIC_LHS_RHS_PTRS_MOVE(out, in) \
|
|
do { \
|
|
MOVEPTR(out.lhs, in->lhs); \
|
|
MOVEPTR(out.rhs, in->rhs); \
|
|
} while (0)
|
|
|
|
struct apfl_expr_param_predicate
|
|
apfl_expr_param_predicate_move(struct apfl_expr_param_predicate *in)
|
|
{
|
|
struct apfl_expr_param_predicate out;
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_param_pair
|
|
apfl_expr_param_pair_move(struct apfl_expr_param_pair *in)
|
|
{
|
|
struct apfl_expr_param_pair out;
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_param_tagged
|
|
apfl_expr_param_tagged_move(struct apfl_expr_param_tagged *in)
|
|
{
|
|
struct apfl_expr_param_tagged out;
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_params
|
|
apfl_expr_params_move(struct apfl_expr_params *in)
|
|
{
|
|
struct apfl_expr_params out;
|
|
MOVE_CAP_LIST(out, in, params, len, cap);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_param
|
|
apfl_expr_param_move(struct apfl_expr_param *in)
|
|
{
|
|
struct apfl_expr_param out = *in;
|
|
switch (in->type) {
|
|
case APFL_EXPR_PARAM_VAR:
|
|
out.var = apfl_string_move(&in->var);
|
|
break;
|
|
case APFL_EXPR_PARAM_CONSTANT:
|
|
out.constant = apfl_expr_const_move(&in->constant);
|
|
break;
|
|
case APFL_EXPR_PARAM_PREDICATE:
|
|
out.predicate = apfl_expr_param_predicate_move(&in->predicate);
|
|
break;
|
|
case APFL_EXPR_PARAM_LIST:
|
|
out.list = apfl_expr_params_move(&in->list);
|
|
break;
|
|
case APFL_EXPR_PARAM_PAIR:
|
|
out.pair = apfl_expr_param_pair_move(&in->pair);
|
|
break;
|
|
case APFL_EXPR_PARAM_TAGGED:
|
|
out.tagged = apfl_expr_param_tagged_move(&in->tagged);
|
|
break;
|
|
case APFL_EXPR_PARAM_BLANK:
|
|
// nop
|
|
break;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_subfunc
|
|
apfl_expr_subfunc_move(struct apfl_expr_subfunc *in)
|
|
{
|
|
return (struct apfl_expr_subfunc) {
|
|
.params = apfl_expr_params_move(&in->params),
|
|
.body = apfl_expr_body_move(&in->body),
|
|
};
|
|
}
|
|
|
|
struct apfl_expr_complex_func
|
|
apfl_expr_complex_func_move(struct apfl_expr_complex_func *in)
|
|
{
|
|
struct apfl_expr_complex_func out;
|
|
MOVE_CAP_LIST(out, in, subfuncs, len, cap);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_assignable_var_or_member
|
|
apfl_expr_assignable_var_or_member_move(struct apfl_expr_assignable_var_or_member *in)
|
|
{
|
|
struct apfl_expr_assignable_var_or_member out = *in;
|
|
switch (in->type) {
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
|
out.var = apfl_string_move(&in->var);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
|
MOVEPTR(out.dot.lhs, in->dot.lhs);
|
|
out.dot.rhs = apfl_string_move(&in->dot.rhs);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
|
MOVEPTR(out.at.lhs, in->at.lhs);
|
|
MOVEPTR(out.at.rhs, in->at.rhs);
|
|
break;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_assignable_predicate
|
|
apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *in)
|
|
{
|
|
struct apfl_expr_assignable_predicate out;
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_assignable_pair
|
|
apfl_expr_assignable_pair_move(struct apfl_expr_assignable_pair *in)
|
|
{
|
|
struct apfl_expr_assignable_pair out;
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_assignable_tagged
|
|
apfl_expr_assignable_tagged_move(struct apfl_expr_assignable_tagged *in)
|
|
{
|
|
struct apfl_expr_assignable_tagged out;
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_assignable_list
|
|
apfl_expr_assignable_list_move(struct apfl_expr_assignable_list *in)
|
|
{
|
|
struct apfl_expr_assignable_list out;
|
|
MOVE_LIST(out, in, items, len);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_assignable
|
|
apfl_expr_assignable_move(struct apfl_expr_assignable *in)
|
|
{
|
|
struct apfl_expr_assignable out = *in;
|
|
switch (in->type) {
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
|
out.var_or_member = apfl_expr_assignable_var_or_member_move(&in->var_or_member);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
|
out.constant = apfl_expr_const_move(&in->constant);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
|
out.predicate = apfl_expr_assignable_predicate_move(&in->predicate);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_LIST:
|
|
out.list = apfl_expr_assignable_list_move(&in->list);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_PAIR:
|
|
out.pair = apfl_expr_assignable_pair_move(&in->pair);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_TAGGED:
|
|
out.tagged = apfl_expr_assignable_tagged_move(&in->tagged);
|
|
break;
|
|
case APFL_EXPR_ASSIGNABLE_BLANK:
|
|
// nop
|
|
break;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_assignment
|
|
apfl_expr_assignment_move(struct apfl_expr_assignment *in)
|
|
{
|
|
struct apfl_expr_assignment out = *in;
|
|
out.lhs = apfl_expr_assignable_move(&in->lhs);
|
|
MOVEPTR(out.rhs, in->rhs);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_dot
|
|
apfl_expr_dot_move(struct apfl_expr_dot *in)
|
|
{
|
|
struct apfl_expr_dot out;
|
|
MOVEPTR(out.lhs, in->lhs);
|
|
out.rhs = apfl_string_move(&in->rhs);
|
|
return out;
|
|
}
|
|
|
|
struct apfl_expr_pair
|
|
apfl_expr_pair_move(struct apfl_expr_pair *in)
|
|
{
|
|
struct apfl_expr_pair out;
|
|
GENERIC_LHS_RHS_PTRS_MOVE(out, in);
|
|
return out;
|
|
}
|
|
|
|
static bool
|
|
format_expr_headline(struct apfl_io_writer w, const char *name, struct apfl_position pos, unsigned indent)
|
|
{
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, name));
|
|
TRY(apfl_io_write_string(w, " @ "));
|
|
TRY(apfl_format_put_pos(w, pos));
|
|
TRY(apfl_io_write_string(w, "\n"));
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
format_simple_headline(struct apfl_io_writer w, const char *headline, unsigned indent)
|
|
{
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, headline));
|
|
TRY(apfl_io_write_string(w, "\n"));
|
|
return true;
|
|
}
|
|
|
|
static bool format_expr(struct apfl_io_writer, struct apfl_expr *, unsigned indent);
|
|
|
|
static bool
|
|
format_expr_list(struct apfl_io_writer w, struct apfl_expr_list *list, unsigned indent)
|
|
{
|
|
for (size_t i = 0; i < list->len; i++) {
|
|
unsigned item_indent = indent;
|
|
if (list->items[i].expand) {
|
|
TRY(format_simple_headline(w, "Expand", indent));
|
|
item_indent++;
|
|
}
|
|
TRY(format_expr(w, list->items[i].expr, item_indent));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
format_body(struct apfl_io_writer w, struct apfl_expr_body *body, unsigned indent)
|
|
{
|
|
for (size_t i = 0; i < body->len; i++) {
|
|
TRY(format_expr(w, &body->items[i], indent));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
format_constant_with_pos(struct apfl_io_writer w, struct apfl_expr_const constant, struct apfl_position pos, unsigned indent)
|
|
{
|
|
switch (constant.type) {
|
|
case APFL_EXPR_CONST_NIL:
|
|
return format_expr_headline(w, "Const (nil)", pos, indent);
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
|
return format_expr_headline(w, constant.boolean ? "Const (true)" : "Const (false", pos, indent);
|
|
case APFL_EXPR_CONST_STRING:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Const ("));
|
|
TRY(apfl_io_write_string(w, constant.string));
|
|
TRY(apfl_io_write_string(w, ") @ "));
|
|
TRY(apfl_format_put_pos(w, pos));
|
|
TRY(apfl_io_write_string(w, "\n"));
|
|
return true;
|
|
case APFL_EXPR_CONST_NUMBER:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Number ("));
|
|
TRY(apfl_format_put_number(w, constant.number));
|
|
TRY(apfl_io_write_string(w, ") @ "));
|
|
TRY(apfl_format_put_pos(w, pos));
|
|
TRY(apfl_io_write_string(w, "\n"));
|
|
return true;
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
format_constant(struct apfl_io_writer w, struct apfl_expr_const constant, unsigned indent)
|
|
{
|
|
switch (constant.type) {
|
|
case APFL_EXPR_CONST_NIL:
|
|
return format_simple_headline(w, "Const (nil)", indent);
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
|
return format_simple_headline(w, constant.boolean ? "Const (true)" : "Const (false", indent);
|
|
case APFL_EXPR_CONST_STRING:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Const ("));
|
|
TRY(apfl_io_write_string(w, constant.string));
|
|
TRY(apfl_io_write_string(w, ")\n"));
|
|
return true;
|
|
case APFL_EXPR_CONST_NUMBER:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Number ("));
|
|
TRY(apfl_format_put_number(w, constant.number));
|
|
TRY(apfl_io_write_string(w, ")\n"));
|
|
return true;
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
static bool format_param(struct apfl_io_writer w, struct apfl_expr_param *, unsigned);
|
|
|
|
static bool
|
|
format_params_item(struct apfl_io_writer w, struct apfl_expr_params_item *item, unsigned indent)
|
|
{
|
|
if (item->expand) {
|
|
TRY(format_simple_headline(w, "Expand", indent));
|
|
indent++;
|
|
}
|
|
return format_param(w, &item->param, indent);
|
|
}
|
|
|
|
static bool
|
|
format_param(struct apfl_io_writer w, struct apfl_expr_param *param, unsigned indent)
|
|
{
|
|
switch (param->type) {
|
|
case APFL_EXPR_PARAM_VAR:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Var ("));
|
|
TRY(apfl_io_write_string(w, param->var));
|
|
TRY(apfl_io_write_string(w, ")\n"));
|
|
return true;
|
|
case APFL_EXPR_PARAM_CONSTANT:
|
|
return format_constant(w, param->constant, indent);
|
|
case APFL_EXPR_PARAM_PREDICATE:
|
|
TRY(format_simple_headline(w, "Predicate", indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_param(w, param->predicate.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_expr(w, param->predicate.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_PARAM_PAIR:
|
|
TRY(format_simple_headline(w, "Pair", indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_param(w, param->pair.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_param(w, param->pair.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_PARAM_TAGGED:
|
|
TRY(format_simple_headline(w, "Tagged", indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_expr(w, param->tagged.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_param(w, param->tagged.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_PARAM_LIST:
|
|
TRY(format_simple_headline(w, "List", indent));
|
|
for (size_t i = 0; i < param->list.len; i++) {
|
|
TRY(format_params_item(w, ¶m->list.params[i], indent+1));
|
|
}
|
|
return true;
|
|
case APFL_EXPR_PARAM_BLANK:
|
|
return format_simple_headline(w, "Blank (_)", indent);
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
format_assignable_var_or_member(struct apfl_io_writer w, struct apfl_expr_assignable_var_or_member var_or_member, unsigned indent)
|
|
{
|
|
switch (var_or_member.type) {
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Var ("));
|
|
TRY(apfl_io_write_string(w, var_or_member.var));
|
|
TRY(apfl_io_write_string(w, ")\n"));
|
|
return true;
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Dot ("));
|
|
TRY(apfl_io_write_string(w, var_or_member.dot.rhs));
|
|
TRY(apfl_io_write_string(w, ")\n"));
|
|
TRY(format_assignable_var_or_member(w, *var_or_member.dot.lhs, indent+1));
|
|
return true;
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
|
TRY(format_simple_headline(w, "At", indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_assignable_var_or_member(w, *var_or_member.at.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_expr(w, var_or_member.at.rhs, indent+2));
|
|
return true;
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
format_assignable(struct apfl_io_writer w, struct apfl_expr_assignable assignable, unsigned indent)
|
|
{
|
|
switch(assignable.type) {
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
|
return format_assignable_var_or_member(w, assignable.var_or_member, indent);
|
|
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
|
return format_constant(w, assignable.constant, indent);
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
|
TRY(format_simple_headline(w, "Predicate", indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_assignable(w, *assignable.predicate.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_expr(w, assignable.predicate.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_ASSIGNABLE_PAIR:
|
|
TRY(format_simple_headline(w, "Pair", indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_assignable(w, *assignable.pair.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_assignable(w, *assignable.pair.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_ASSIGNABLE_TAGGED:
|
|
TRY(format_simple_headline(w, "Tagged", indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_expr(w, assignable.tagged.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_assignable(w, *assignable.tagged.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_ASSIGNABLE_LIST:
|
|
TRY(format_simple_headline(w, "List", indent));
|
|
for (size_t i = 0; i < assignable.list.len; i++) {
|
|
struct apfl_expr_assignable_list_item *item = &assignable.list.items[i];
|
|
unsigned indent_item = indent+1;
|
|
if (item->expand) {
|
|
TRY(format_simple_headline(w, "Expand", indent_item));
|
|
indent_item++;
|
|
}
|
|
TRY(format_assignable(w, item->assignable, indent_item));
|
|
}
|
|
return true;
|
|
case APFL_EXPR_ASSIGNABLE_BLANK:
|
|
return format_simple_headline(w, "Blank (_)", indent);
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
format_expr(struct apfl_io_writer w, struct apfl_expr *expr, unsigned indent)
|
|
{
|
|
switch (expr->type) {
|
|
case APFL_EXPR_LIST:
|
|
TRY(format_expr_headline(w, "List", expr->position, indent));
|
|
TRY(format_expr_list(w, &expr->list, indent+1));
|
|
return true;
|
|
case APFL_EXPR_DICT:
|
|
TRY(format_expr_headline(w, "Dict", expr->position, indent));
|
|
for (size_t i = 0; i < expr->dict.len; i++) {
|
|
TRY(format_simple_headline(w, "Dict item", indent+1));
|
|
TRY(format_simple_headline(w, "Key", indent+2));
|
|
TRY(format_expr(w, expr->dict.items[i].k, indent+3));
|
|
TRY(format_simple_headline(w, "Value", indent+2));
|
|
TRY(format_expr(w, expr->dict.items[i].v, indent+3));
|
|
}
|
|
return true;
|
|
case APFL_EXPR_CALL:
|
|
TRY(format_expr_headline(w, "Call", expr->position, indent));
|
|
TRY(format_simple_headline(w, "Callee", indent+1));
|
|
TRY(format_expr(w, expr->call.callee, indent+2));
|
|
TRY(format_simple_headline(w, "Args", indent+1));
|
|
TRY(format_expr_list(w, &expr->call.arguments, indent+2));
|
|
return true;
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
|
TRY(format_expr_headline(w, "Simple function", expr->position, indent));
|
|
TRY(format_body(w, &expr->simple_func, indent+1));
|
|
return true;
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
|
TRY(format_expr_headline(w, "Complex function", expr->position, indent));
|
|
for (size_t i = 0; i < expr->complex_func.len; i++) {
|
|
TRY(format_simple_headline(w, "Subfunction", indent+1));
|
|
struct apfl_expr_subfunc *sub = &expr->complex_func.subfuncs[i];
|
|
TRY(format_simple_headline(w, "Parameters", indent+2));
|
|
for (size_t j = 0; j < sub->params.len; j++) {
|
|
TRY(format_params_item(w, &sub->params.params[j], indent+3));
|
|
}
|
|
TRY(format_simple_headline(w, "Body", indent+2));
|
|
TRY(format_body(w, &sub->body, indent+3));
|
|
}
|
|
return true;
|
|
case APFL_EXPR_ASSIGNMENT:
|
|
TRY(format_expr_headline(w, "Assignment", expr->position, indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_assignable(w, expr->assignment.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_expr(w, expr->assignment.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_DOT:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Dot ("));
|
|
TRY(apfl_io_write_string(w, expr->dot.rhs));
|
|
TRY(apfl_io_write_string(w, ") @ "));
|
|
TRY(apfl_format_put_pos(w, expr->position));
|
|
TRY(apfl_io_write_string(w, "\n"));
|
|
TRY(format_expr(w, expr->dot.lhs, indent+1));
|
|
return true;
|
|
case APFL_EXPR_AT:
|
|
TRY(format_expr_headline(w, "At", expr->position, indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_expr(w, expr->at.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_expr(w, expr->at.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_PAIR:
|
|
TRY(format_expr_headline(w, "Pair", expr->position, indent));
|
|
TRY(format_simple_headline(w, "LHS", indent+1));
|
|
TRY(format_expr(w, expr->pair.lhs, indent+2));
|
|
TRY(format_simple_headline(w, "RHS", indent+1));
|
|
TRY(format_expr(w, expr->pair.rhs, indent+2));
|
|
return true;
|
|
case APFL_EXPR_CONSTANT:
|
|
return format_constant_with_pos(w, expr->constant, expr->position, indent);
|
|
case APFL_EXPR_VAR:
|
|
TRY(apfl_format_put_indent(w, indent));
|
|
TRY(apfl_io_write_string(w, "Var ("));
|
|
TRY(apfl_io_write_string(w, expr->var));
|
|
TRY(apfl_io_write_string(w, ") @ "));
|
|
TRY(apfl_format_put_pos(w, expr->position));
|
|
TRY(apfl_io_write_string(w, "\n"));
|
|
return true;
|
|
case APFL_EXPR_BLANK:
|
|
return format_expr_headline(w, "Blank (_)", expr->position, indent);
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
apfl_expr_print(struct apfl_expr expr, FILE *f)
|
|
{
|
|
return format_expr(apfl_io_file_writer(f), &expr, 0);
|
|
}
|
|
|
|
static bool
|
|
expr_list_eq(struct apfl_expr_list a, struct apfl_expr_list b)
|
|
{
|
|
if (a.len != b.len) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < a.len; i++) {
|
|
if (
|
|
a.items[i].expand != b.items[i].expand
|
|
|| !apfl_expr_eq(*a.items[i].expr, *b.items[i].expr)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
body_eq(struct apfl_expr_body a, struct apfl_expr_body b)
|
|
{
|
|
if (a.len != b.len) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < a.len; i++) {
|
|
if (!apfl_expr_eq(a.items[i], b.items[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool param_eq(struct apfl_expr_param, struct apfl_expr_param);
|
|
|
|
static bool
|
|
params_eq(struct apfl_expr_params a, struct apfl_expr_params b)
|
|
{
|
|
if (a.len != b.len) {
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < a.len; i++) {
|
|
if (
|
|
a.params[i].expand != b.params[i].expand
|
|
|| !param_eq(a.params[i].param, b.params[i].param)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
const_eq(struct apfl_expr_const a, struct apfl_expr_const b)
|
|
{
|
|
if (a.type != b.type) {
|
|
return false;
|
|
}
|
|
|
|
switch (a.type) {
|
|
case APFL_EXPR_CONST_NIL:
|
|
return true;
|
|
case APFL_EXPR_CONST_BOOLEAN:
|
|
return a.boolean == b.boolean;
|
|
case APFL_EXPR_CONST_STRING:
|
|
return apfl_string_eq(a.string, b.string);
|
|
case APFL_EXPR_CONST_NUMBER:
|
|
return a.number == b.number;
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
param_eq(struct apfl_expr_param a, struct apfl_expr_param b)
|
|
{
|
|
if (a.type != b.type) {
|
|
return false;
|
|
}
|
|
|
|
switch (a.type) {
|
|
case APFL_EXPR_PARAM_VAR:
|
|
return apfl_string_eq(a.var, b.var);
|
|
case APFL_EXPR_PARAM_CONSTANT:
|
|
return const_eq(a.constant, b.constant);
|
|
case APFL_EXPR_PARAM_PREDICATE:
|
|
return param_eq(*a.predicate.lhs, *b.predicate.lhs)
|
|
&& apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs);
|
|
case APFL_EXPR_PARAM_PAIR:
|
|
return param_eq(*a.pair.lhs, *b.pair.lhs)
|
|
&& param_eq(*a.pair.rhs, *b.pair.rhs);
|
|
case APFL_EXPR_PARAM_TAGGED:
|
|
return apfl_expr_eq(*a.tagged.lhs, *b.tagged.lhs)
|
|
&& param_eq(*a.tagged.rhs, *b.tagged.rhs);
|
|
case APFL_EXPR_PARAM_LIST:
|
|
return params_eq(a.list, b.list);
|
|
case APFL_EXPR_PARAM_BLANK:
|
|
return true;
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
assignable_var_or_member_eq(struct apfl_expr_assignable_var_or_member a, struct apfl_expr_assignable_var_or_member b)
|
|
{
|
|
if (a.type != b.type) {
|
|
return false;
|
|
}
|
|
|
|
switch (a.type) {
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
|
|
return apfl_string_eq(a.var, b.var);
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
|
|
return assignable_var_or_member_eq(*a.dot.lhs, *b.dot.lhs)
|
|
&& apfl_string_eq(a.dot.rhs, b.dot.rhs);
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
|
|
return assignable_var_or_member_eq(*a.at.lhs, *b.at.lhs)
|
|
&& apfl_expr_eq(*a.at.rhs, *b.at.rhs);
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
assignable_eq(struct apfl_expr_assignable a, struct apfl_expr_assignable b)
|
|
{
|
|
if (a.type != b.type) {
|
|
return false;
|
|
}
|
|
|
|
switch (a.type) {
|
|
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
|
|
return assignable_var_or_member_eq(a.var_or_member, b.var_or_member);
|
|
case APFL_EXPR_ASSIGNABLE_CONSTANT:
|
|
return const_eq(a.constant, b.constant);
|
|
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
|
return assignable_eq(*a.predicate.lhs, *b.predicate.lhs)
|
|
&& apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs);
|
|
case APFL_EXPR_ASSIGNABLE_PAIR:
|
|
return assignable_eq(*a.pair.lhs, *b.pair.lhs)
|
|
&& assignable_eq(*a.pair.rhs, *b.pair.rhs);
|
|
case APFL_EXPR_ASSIGNABLE_TAGGED:
|
|
return apfl_expr_eq(*a.tagged.lhs, *b.tagged.lhs)
|
|
&& assignable_eq(*a.tagged.rhs, *b.tagged.rhs);
|
|
case APFL_EXPR_ASSIGNABLE_LIST:
|
|
if (a.list.len != b.list.len) {
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < a.list.len; i++) {
|
|
if (
|
|
a.list.items[i].expand != b.list.items[i].expand
|
|
|| !assignable_eq(a.list.items[i].assignable, b.list.items[i].assignable)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
case APFL_EXPR_ASSIGNABLE_BLANK:
|
|
return true;
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
apfl_expr_eq(struct apfl_expr a, struct apfl_expr b)
|
|
{
|
|
if (a.type != b.type) {
|
|
return false;
|
|
}
|
|
|
|
if (!apfl_position_eq(a.position, b.position)) {
|
|
return false;
|
|
}
|
|
|
|
switch (a.type) {
|
|
case APFL_EXPR_LIST:
|
|
return expr_list_eq(a.list, b.list);
|
|
case APFL_EXPR_DICT:
|
|
if (a.dict.len != b.dict.len) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < a.dict.len; i++) {
|
|
if (
|
|
!apfl_expr_eq(*a.dict.items[i].k, *b.dict.items[i].k)
|
|
|| !apfl_expr_eq(*a.dict.items[i].v, *b.dict.items[i].v)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
case APFL_EXPR_CALL:
|
|
return apfl_expr_eq(*a.call.callee, *b.call.callee)
|
|
&& expr_list_eq(a.call.arguments, b.call.arguments);
|
|
case APFL_EXPR_SIMPLE_FUNC:
|
|
return body_eq(a.simple_func, b.simple_func);
|
|
case APFL_EXPR_COMPLEX_FUNC:
|
|
if (a.complex_func.len != b.complex_func.len) {
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < a.complex_func.len; i++) {
|
|
if (
|
|
!params_eq(a.complex_func.subfuncs[i].params, b.complex_func.subfuncs[i].params)
|
|
|| !body_eq(a.complex_func.subfuncs[i].body, b.complex_func.subfuncs[i].body)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
case APFL_EXPR_ASSIGNMENT:
|
|
return assignable_eq(a.assignment.lhs, b.assignment.lhs)
|
|
&& apfl_expr_eq(*a.assignment.rhs, *b.assignment.rhs);
|
|
case APFL_EXPR_DOT:
|
|
return apfl_expr_eq(*a.dot.lhs, *b.dot.lhs)
|
|
&& apfl_string_eq(a.dot.rhs, b.dot.rhs);
|
|
case APFL_EXPR_AT:
|
|
return apfl_expr_eq(*a.at.lhs, *b.at.lhs)
|
|
&& apfl_expr_eq(*a.at.rhs, *b.at.rhs);
|
|
case APFL_EXPR_PAIR:
|
|
return apfl_expr_eq(*a.pair.lhs, *b.pair.lhs)
|
|
&& apfl_expr_eq(*a.pair.rhs, *b.pair.rhs);
|
|
case APFL_EXPR_CONSTANT:
|
|
return const_eq(a.constant, b.constant);
|
|
case APFL_EXPR_VAR:
|
|
return apfl_string_eq(a.var, b.var);
|
|
case APFL_EXPR_BLANK:
|
|
return true;
|
|
}
|
|
|
|
assert(false);
|
|
return false;
|
|
}
|