apfl/src/expr.c
Laria Carolin Chabowski 6056e3a450 Implement pairs
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
2023-03-22 23:54:03 +01:00

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, &param->var);
break;
case APFL_EXPR_PARAM_CONSTANT:
apfl_expr_const_deinit(allocator, &param->constant);
break;
case APFL_EXPR_PARAM_PREDICATE:
apfl_expr_param_predicate_deinit(allocator, &param->predicate);
break;
case APFL_EXPR_PARAM_LIST:
apfl_expr_params_deinit(allocator, &param->list);
break;
case APFL_EXPR_PARAM_PAIR:
apfl_expr_param_pair_deinit(allocator, &param->pair);
break;
case APFL_EXPR_PARAM_TAGGED:
apfl_expr_param_tagged_deinit(allocator, &param->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, &param->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;
}