#include #include #include #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; }