Improve AST representation of expansion in assignables / parameters
The previous representation didn't properly model the fact that an assignable / parameter can only be expanded, if it's a list element. This now better models this. Other than being more correct, this should also make evaluating these a bit easier. While I was at it, I also improved the error message for multiple expansions on the same level and added tests for these.
This commit is contained in:
parent
e928f40da4
commit
4eea93ff97
5 changed files with 275 additions and 197 deletions
21
src/apfl.h
21
src/apfl.h
|
|
@ -155,6 +155,7 @@ enum apfl_error_type {
|
|||
APFL_ERR_UNEXPECTED_EXPRESSION,
|
||||
APFL_ERR_INVALID_ASSIGNMENT_LHS,
|
||||
APFL_ERR_EMPTY_ASSIGNMENT,
|
||||
APFL_ERR_ONLY_ONE_EXPAND_ALLOWED,
|
||||
};
|
||||
|
||||
const char *apfl_error_type_name(enum apfl_error_type);
|
||||
|
|
@ -252,12 +253,11 @@ enum apfl_expr_param_type {
|
|||
APFL_EXPR_PARAM_VAR,
|
||||
APFL_EXPR_PARAM_CONSTANT,
|
||||
APFL_EXPR_PARAM_PREDICATE,
|
||||
APFL_EXPR_PARAM_EXPAND,
|
||||
APFL_EXPR_PARAM_LIST,
|
||||
};
|
||||
|
||||
struct apfl_expr_params {
|
||||
struct apfl_expr_param *params;
|
||||
struct apfl_expr_params_item *params;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
|
|
@ -268,11 +268,15 @@ struct apfl_expr_param {
|
|||
struct apfl_string var;
|
||||
struct apfl_expr_const constant;
|
||||
struct apfl_expr_param_predicate predicate;
|
||||
struct apfl_expr_param *expand;
|
||||
struct apfl_expr_params list;
|
||||
};
|
||||
};
|
||||
|
||||
struct apfl_expr_params_item {
|
||||
bool expand;
|
||||
struct apfl_expr_param param;
|
||||
};
|
||||
|
||||
struct apfl_expr_subfunc {
|
||||
struct apfl_expr_params params;
|
||||
struct apfl_expr_body body;
|
||||
|
|
@ -287,7 +291,6 @@ enum apfl_expr_assignable_type {
|
|||
APFL_EXPR_ASSIGNABLE_VAR,
|
||||
APFL_EXPR_ASSIGNABLE_CONSTANT,
|
||||
APFL_EXPR_ASSIGNABLE_PREDICATE,
|
||||
APFL_EXPR_ASSIGNABLE_EXPAND,
|
||||
APFL_EXPR_ASSIGNABLE_DOT,
|
||||
APFL_EXPR_ASSIGNABLE_AT,
|
||||
APFL_EXPR_ASSIGNABLE_LIST,
|
||||
|
|
@ -306,7 +309,7 @@ struct apfl_expr_assignable_at {
|
|||
struct apfl_expr *rhs;
|
||||
};
|
||||
struct apfl_expr_assignable_list {
|
||||
struct apfl_expr_assignable *children;
|
||||
struct apfl_expr_assignable_list_item *items;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
|
|
@ -317,13 +320,17 @@ struct apfl_expr_assignable {
|
|||
struct apfl_string var;
|
||||
struct apfl_expr_const constant;
|
||||
struct apfl_expr_assignable_predicate predicate;
|
||||
struct apfl_expr_assignable *expand;
|
||||
struct apfl_expr_assignable_dot dot;
|
||||
struct apfl_expr_assignable_at at;
|
||||
struct apfl_expr_assignable_list list;
|
||||
};
|
||||
};
|
||||
|
||||
struct apfl_expr_assignable_list_item {
|
||||
struct apfl_expr_assignable assignable;
|
||||
bool expand;
|
||||
};
|
||||
|
||||
struct apfl_expr_assignment {
|
||||
bool local;
|
||||
struct apfl_expr_assignable lhs;
|
||||
|
|
@ -376,12 +383,14 @@ void apfl_expr_const_deinit(struct apfl_expr_const *);
|
|||
void apfl_expr_param_predicate_deinit(struct apfl_expr_param_predicate *);
|
||||
void apfl_expr_param_list_deinit(struct apfl_expr_param_list *);
|
||||
void apfl_expr_params_deinit(struct apfl_expr_params *);
|
||||
void apfl_expr_params_item_deinit(struct apfl_expr_params_item *);
|
||||
void apfl_expr_param_deinit(struct apfl_expr_param *);
|
||||
void apfl_expr_subfunc_deinit(struct apfl_expr_subfunc *);
|
||||
void apfl_expr_complex_func_deinit(struct apfl_expr_complex_func *);
|
||||
void apfl_expr_assignable_predicate_deinit(struct apfl_expr_assignable_predicate *);
|
||||
void apfl_expr_assignable_dot_deinit(struct apfl_expr_assignable_dot *);
|
||||
void apfl_expr_assignable_at_deinit(struct apfl_expr_assignable_at *);
|
||||
void apfl_expr_assignable_list_item_deinit(struct apfl_expr_assignable_list_item *);
|
||||
void apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *);
|
||||
void apfl_expr_assignable_deinit(struct apfl_expr_assignable *);
|
||||
void apfl_expr_assignment_deinit(struct apfl_expr_assignment *);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ apfl_error_type_name(enum apfl_error_type type)
|
|||
return "APFL_ERR_INVALID_ASSIGNMENT_LHS";
|
||||
case APFL_ERR_EMPTY_ASSIGNMENT:
|
||||
return "APFL_ERR_EMPTY_ASSIGNMENT";
|
||||
case APFL_ERR_ONLY_ONE_EXPAND_ALLOWED:
|
||||
return "APFL_ERR_ONLY_ONE_EXPAND_ALLOWED";
|
||||
}
|
||||
|
||||
return "<unknown error>";
|
||||
|
|
@ -136,6 +138,12 @@ apfl_error_print(struct apfl_error error, FILE *file)
|
|||
POSARGS
|
||||
);
|
||||
return;
|
||||
case APFL_ERR_ONLY_ONE_EXPAND_ALLOWED:
|
||||
fprintf(
|
||||
file,
|
||||
"Only one expansion (~) is allowed per level, near " POSFMT "\n",
|
||||
POSARGS
|
||||
);
|
||||
}
|
||||
|
||||
fprintf(file, "Unknown error %d\n", (int)error.type);
|
||||
|
|
|
|||
76
src/expr.c
76
src/expr.c
|
|
@ -118,7 +118,13 @@ apfl_expr_param_list_deinit(struct apfl_expr_param_list *list)
|
|||
void
|
||||
apfl_expr_params_deinit(struct apfl_expr_params *params)
|
||||
{
|
||||
DEINIT_LIST(params->params, params->len, apfl_expr_param_deinit);
|
||||
DEINIT_LIST(params->params, params->len, apfl_expr_params_item_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_params_item_deinit(struct apfl_expr_params_item *item)
|
||||
{
|
||||
apfl_expr_param_deinit(&item->param);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -134,9 +140,6 @@ apfl_expr_param_deinit(struct apfl_expr_param *param)
|
|||
case APFL_EXPR_PARAM_PREDICATE:
|
||||
apfl_expr_param_predicate_deinit(¶m->predicate);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_EXPAND:
|
||||
DESTROY(param->expand, apfl_expr_param_deinit);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_LIST:
|
||||
apfl_expr_params_deinit(¶m->list);
|
||||
break;
|
||||
|
|
@ -180,10 +183,16 @@ apfl_expr_assignable_at_deinit(struct apfl_expr_assignable_at *at)
|
|||
DEINIT_GENERIC_LHS_RHS_EXPR(at, apfl_expr_assignable_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignable_list_item_deinit(struct apfl_expr_assignable_list_item *item)
|
||||
{
|
||||
apfl_expr_assignable_deinit(&item->assignable);
|
||||
}
|
||||
|
||||
void
|
||||
apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *list)
|
||||
{
|
||||
DEINIT_LIST(list->children, list->len, apfl_expr_assignable_deinit);
|
||||
DEINIT_LIST(list->items, list->len, apfl_expr_assignable_list_item_deinit);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -199,9 +208,6 @@ apfl_expr_assignable_deinit(struct apfl_expr_assignable *a)
|
|||
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
||||
apfl_expr_assignable_predicate_deinit(&a->predicate);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_EXPAND:
|
||||
DESTROY(a->expand, apfl_expr_assignable_deinit);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_DOT:
|
||||
apfl_expr_assignable_dot_deinit(&a->dot);
|
||||
break;
|
||||
|
|
@ -395,9 +401,6 @@ apfl_expr_param_move(struct apfl_expr_param *in)
|
|||
case APFL_EXPR_PARAM_PREDICATE:
|
||||
out.predicate = apfl_expr_param_predicate_move(&in->predicate);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_EXPAND:
|
||||
MOVEPTR(out.expand, in->expand);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_LIST:
|
||||
out.list = apfl_expr_params_move(&in->list);
|
||||
break;
|
||||
|
|
@ -456,7 +459,7 @@ 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, children, len);
|
||||
MOVE_LIST(out, in, items, len);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -474,9 +477,6 @@ apfl_expr_assignable_move(struct apfl_expr_assignable *in)
|
|||
case APFL_EXPR_ASSIGNABLE_PREDICATE:
|
||||
out.predicate = apfl_expr_assignable_predicate_move(&in->predicate);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_EXPAND:
|
||||
MOVEPTR(out.expand, in->expand);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_DOT:
|
||||
out.dot = apfl_expr_assignable_dot_move(&in->dot);
|
||||
break;
|
||||
|
|
@ -579,6 +579,18 @@ print_constant(struct apfl_expr_const constant, unsigned indent, FILE *f)
|
|||
}
|
||||
}
|
||||
|
||||
static void print_param(struct apfl_expr_param *, unsigned, FILE *);
|
||||
|
||||
static void
|
||||
print_params_item(struct apfl_expr_params_item *item, unsigned indent, FILE *f)
|
||||
{
|
||||
if (item->expand) {
|
||||
apfl_print_indented(indent, f, "Expand\n");
|
||||
indent++;
|
||||
}
|
||||
print_param(&item->param, indent, f);
|
||||
}
|
||||
|
||||
static void
|
||||
print_param(struct apfl_expr_param *param, unsigned indent, FILE *f)
|
||||
{
|
||||
|
|
@ -596,14 +608,10 @@ print_param(struct apfl_expr_param *param, unsigned indent, FILE *f)
|
|||
apfl_print_indented(indent+1, f, "RHS\n");
|
||||
print_expr(param->predicate.rhs, indent+2, f);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_EXPAND:
|
||||
apfl_print_indented(indent, f, "Expand\n");
|
||||
print_param(param->expand, indent+1, f);
|
||||
break;
|
||||
case APFL_EXPR_PARAM_LIST:
|
||||
apfl_print_indented(indent, f, "List\n");
|
||||
for (size_t i = 0; i < param->list.len; i++) {
|
||||
print_param(¶m->list.params[i], indent+1, f);
|
||||
print_params_item(¶m->list.params[i], indent+1, f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -626,10 +634,6 @@ print_assignable(struct apfl_expr_assignable assignable, unsigned indent, FILE *
|
|||
apfl_print_indented(indent+1, f, "RHS\n");
|
||||
print_expr(assignable.predicate.rhs, indent+2, f);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_EXPAND:
|
||||
apfl_print_indented(indent, f, "Expand\n");
|
||||
print_assignable(*assignable.expand, indent+1, f);
|
||||
break;
|
||||
case APFL_EXPR_ASSIGNABLE_DOT:
|
||||
apfl_print_indented(indent, f, "Dot (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(assignable.dot.rhs));
|
||||
print_assignable(*assignable.dot.lhs, indent+1, f);
|
||||
|
|
@ -644,7 +648,13 @@ print_assignable(struct apfl_expr_assignable assignable, unsigned indent, FILE *
|
|||
case APFL_EXPR_ASSIGNABLE_LIST:
|
||||
apfl_print_indented(indent, f, "List\n");
|
||||
for (size_t i = 0; i < assignable.list.len; i++) {
|
||||
print_assignable(assignable.list.children[i], indent+1, f);
|
||||
struct apfl_expr_assignable_list_item *item = &assignable.list.items[i];
|
||||
unsigned indent_item = indent+1;
|
||||
if (item->expand) {
|
||||
apfl_print_indented(indent_item, f, "Expand\n");
|
||||
indent_item++;
|
||||
}
|
||||
print_assignable(item->assignable, indent_item, f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -686,7 +696,7 @@ print_expr(struct apfl_expr *expr, unsigned indent, FILE *f)
|
|||
struct apfl_expr_subfunc *sub = &expr->complex_func.subfuncs[i];
|
||||
apfl_print_indented(indent+2, f, "Parameters\n");
|
||||
for (size_t j = 0; j < sub->params.len; j++) {
|
||||
print_param(&sub->params.params[j], indent+3, f);
|
||||
print_params_item(&sub->params.params[j], indent+3, f);
|
||||
}
|
||||
apfl_print_indented(indent+2, f, "Body\n");
|
||||
print_body(&sub->body, indent+3, f);
|
||||
|
|
@ -769,7 +779,10 @@ params_eq(struct apfl_expr_params a, struct apfl_expr_params b)
|
|||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < a.len; i++) {
|
||||
if (!param_eq(a.params[i], b.params[i])) {
|
||||
if (
|
||||
a.params[i].expand != b.params[i].expand
|
||||
|| !param_eq(a.params[i].param, b.params[i].param)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -813,8 +826,6 @@ param_eq(struct apfl_expr_param a, struct apfl_expr_param b)
|
|||
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_EXPAND:
|
||||
return param_eq(*a.expand, *b.expand);
|
||||
case APFL_EXPR_PARAM_LIST:
|
||||
return params_eq(a.list, b.list);
|
||||
}
|
||||
|
|
@ -838,8 +849,6 @@ assignable_eq(struct apfl_expr_assignable a, struct apfl_expr_assignable b)
|
|||
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_EXPAND:
|
||||
return assignable_eq(*a.expand, *b.expand);
|
||||
case APFL_EXPR_ASSIGNABLE_DOT:
|
||||
return assignable_eq(*a.dot.lhs, *b.dot.lhs)
|
||||
&& apfl_string_eq(a.dot.rhs, b.dot.rhs);
|
||||
|
|
@ -851,7 +860,10 @@ assignable_eq(struct apfl_expr_assignable a, struct apfl_expr_assignable b)
|
|||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < a.list.len; i++) {
|
||||
if (!assignable_eq(a.list.children[i], b.list.children[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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
202
src/parser.c
202
src/parser.c
|
|
@ -972,7 +972,18 @@ parse_stringify(apfl_parser_ptr p, struct fragment *fragment, struct apfl_positi
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool fragment_to_param_recursive(apfl_parser_ptr, struct fragment *, struct apfl_expr_param *);
|
||||
static struct fragment
|
||||
fragment_unwrap_expand(struct fragment fragment)
|
||||
{
|
||||
assert(fragment.type == FRAG_EXPAND);
|
||||
struct fragment tmp = fragment_move(fragment.expand);
|
||||
free(fragment.expand);
|
||||
fragment.expand = NULL;
|
||||
fragment_deinit(&fragment);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static bool fragment_to_param(apfl_parser_ptr, struct fragment, struct apfl_expr_param *);
|
||||
|
||||
static bool fragments_to_params(apfl_parser_ptr, struct fragment_list, struct apfl_expr_params *);
|
||||
|
||||
|
|
@ -990,7 +1001,7 @@ predicate_fragment_to_param(
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (!fragment_to_param_recursive(p, lhs_rhs->lhs, out->predicate.lhs)) {
|
||||
if (!fragment_to_param(p, fragment_move(lhs_rhs->lhs), out->predicate.lhs)) {
|
||||
free(out->predicate.lhs);
|
||||
out->predicate.lhs = NULL;
|
||||
goto error;
|
||||
|
|
@ -1009,7 +1020,7 @@ error:
|
|||
}
|
||||
|
||||
static bool
|
||||
fragment_to_param_recursive(
|
||||
fragment_to_param_inner(
|
||||
apfl_parser_ptr p,
|
||||
struct fragment *fragment,
|
||||
struct apfl_expr_param *out
|
||||
|
|
@ -1051,23 +1062,71 @@ fragment_to_param_recursive(
|
|||
static bool
|
||||
fragment_to_param(
|
||||
apfl_parser_ptr p,
|
||||
struct fragment *fragment,
|
||||
struct apfl_expr_param *out,
|
||||
bool *seen_expand
|
||||
struct fragment fragment,
|
||||
struct apfl_expr_param *out
|
||||
) {
|
||||
if (fragment->type == FRAG_EXPAND && !*seen_expand) {
|
||||
*seen_expand = true; // This prevents a param list with more than one ~
|
||||
bool ok = fragment_to_param_inner(p, &fragment, out);
|
||||
fragment_deinit(&fragment);
|
||||
return ok;
|
||||
}
|
||||
|
||||
out->type = APFL_EXPR_PARAM_EXPAND;
|
||||
if ((out->expand = malloc(sizeof(struct apfl_expr_param))) == NULL) {
|
||||
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
return fragment_to_param_recursive(p, fragment->expand, out->expand);
|
||||
static bool
|
||||
fragments_to_params_inner(
|
||||
apfl_parser_ptr p,
|
||||
/*borrowed*/ struct fragment_list fragments,
|
||||
struct apfl_expr_params *out
|
||||
) {
|
||||
if (fragments.len == 0) {
|
||||
out->len = 0;
|
||||
out->params = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
return fragment_to_param_recursive(p, fragment, out);
|
||||
out->params = ALLOC_LIST(struct apfl_expr_params_item, fragments.len);
|
||||
out->len = 0;
|
||||
if (out->params == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
bool seen_expand = false;
|
||||
for (size_t i = 0; i < fragments.len; i++) {
|
||||
struct apfl_expr_params_item *out_item = &out->params[i];
|
||||
struct fragment item_fragment = fragment_move(&fragments.children[i]);
|
||||
|
||||
if (item_fragment.type == FRAG_EXPAND) {
|
||||
if (seen_expand) {
|
||||
p->error = (struct apfl_error) {
|
||||
.type = APFL_ERR_ONLY_ONE_EXPAND_ALLOWED,
|
||||
.position = item_fragment.position,
|
||||
};
|
||||
fragment_deinit(&item_fragment);
|
||||
goto error;
|
||||
}
|
||||
|
||||
out_item->expand = true;
|
||||
seen_expand = true;
|
||||
|
||||
item_fragment = fragment_unwrap_expand(item_fragment);
|
||||
} else {
|
||||
out_item->expand = false;
|
||||
}
|
||||
|
||||
if (!fragment_to_param(
|
||||
p,
|
||||
fragment_move(&item_fragment),
|
||||
&out_item->param
|
||||
)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
out->len++;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
apfl_expr_params_deinit(out);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -1076,48 +1135,14 @@ fragments_to_params(
|
|||
struct fragment_list fragments,
|
||||
struct apfl_expr_params *out
|
||||
) {
|
||||
bool result = true;
|
||||
|
||||
if (fragments.len == 0) {
|
||||
out->len = 0;
|
||||
out->params = NULL;
|
||||
goto ok;
|
||||
}
|
||||
|
||||
out->params = ALLOC_LIST(struct apfl_expr_param, fragments.len);
|
||||
out->len = 0;
|
||||
if (out->params == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
bool seen_expand = false;
|
||||
for (size_t i = 0; i < fragments.len; i++) {
|
||||
if (!fragment_to_param(
|
||||
p,
|
||||
&fragments.children[i],
|
||||
&out->params[i],
|
||||
&seen_expand
|
||||
)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
out->len++;
|
||||
}
|
||||
|
||||
goto ok;
|
||||
|
||||
error:
|
||||
result = false;
|
||||
apfl_expr_params_deinit(out);
|
||||
ok:
|
||||
bool ok = fragments_to_params_inner(p, fragments, out);
|
||||
fragment_list_deinit(&fragments);
|
||||
return result;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool
|
||||
fragment_to_assignable(
|
||||
apfl_parser_ptr p,
|
||||
bool expand_ok,
|
||||
struct fragment fragment,
|
||||
struct apfl_expr_assignable *out
|
||||
);
|
||||
|
|
@ -1125,36 +1150,13 @@ fragment_to_assignable(
|
|||
static bool
|
||||
fragment_to_assignable_inner(
|
||||
apfl_parser_ptr p,
|
||||
bool expand_ok,
|
||||
struct fragment *fragment,
|
||||
struct apfl_expr_assignable *out
|
||||
) {
|
||||
switch (fragment->type) {
|
||||
case FRAG_EXPAND:
|
||||
if (!expand_ok) {
|
||||
p->error = (struct apfl_error) {
|
||||
.type = APFL_ERR_INVALID_ASSIGNMENT_LHS,
|
||||
.position = fragment->position,
|
||||
};
|
||||
goto error;
|
||||
}
|
||||
|
||||
out->type = APFL_EXPR_ASSIGNABLE_EXPAND;
|
||||
if ((out->expand = malloc(sizeof(struct apfl_expr_assignable))) == NULL) {
|
||||
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!fragment_to_assignable(
|
||||
p,
|
||||
false,
|
||||
fragment_move(fragment->expand),
|
||||
out->expand
|
||||
)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return true;
|
||||
p->error = err_unexpected_token(APFL_TOK_EXPAND, fragment->position);
|
||||
goto error;
|
||||
case FRAG_CONSTANT:
|
||||
out->type = APFL_EXPR_ASSIGNABLE_CONSTANT;
|
||||
out->constant = apfl_expr_const_move(&fragment->constant);
|
||||
|
|
@ -1175,7 +1177,6 @@ fragment_to_assignable_inner(
|
|||
|
||||
if (!fragment_to_assignable(
|
||||
p,
|
||||
expand_ok,
|
||||
fragment_move(fragment->dot.lhs),
|
||||
out->dot.lhs
|
||||
)) {
|
||||
|
|
@ -1196,7 +1197,6 @@ fragment_to_assignable_inner(
|
|||
|
||||
if (!fragment_to_assignable(
|
||||
p,
|
||||
expand_ok,
|
||||
fragment_move(fragment->at.lhs),
|
||||
out->at.lhs
|
||||
)) {
|
||||
|
|
@ -1227,7 +1227,6 @@ fragment_to_assignable_inner(
|
|||
|
||||
if (!fragment_to_assignable(
|
||||
p,
|
||||
expand_ok,
|
||||
fragment_move(fragment->at.lhs),
|
||||
out->predicate.lhs
|
||||
)) {
|
||||
|
|
@ -1254,23 +1253,39 @@ fragment_to_assignable_inner(
|
|||
case FRAG_LIST:
|
||||
out->type = APFL_EXPR_ASSIGNABLE_LIST;
|
||||
out->list.len = 0;
|
||||
out->list.children = malloc(sizeof(struct apfl_expr_assignable) * fragment->list.len);
|
||||
out->list.items = ALLOC_LIST(struct apfl_expr_assignable_list_item, fragment->list.len);
|
||||
|
||||
expand_ok = true;
|
||||
bool expand_ok = true;
|
||||
for (size_t i = 0; i < fragment->list.len; i++) {
|
||||
struct apfl_expr_assignable *cur = &out->list.children[i];
|
||||
struct apfl_expr_assignable_list_item *out_item = &out->list.items[i];
|
||||
struct fragment item_fragment = fragment_move(&fragment->list.children[i]);
|
||||
|
||||
if (item_fragment.type == FRAG_EXPAND) {
|
||||
if (!expand_ok) {
|
||||
p->error = (struct apfl_error) {
|
||||
.type = APFL_ERR_ONLY_ONE_EXPAND_ALLOWED,
|
||||
.position = item_fragment.position,
|
||||
};
|
||||
fragment_deinit(&item_fragment);
|
||||
goto error;
|
||||
}
|
||||
|
||||
out_item->expand = true;
|
||||
expand_ok = false;
|
||||
|
||||
item_fragment = fragment_unwrap_expand(item_fragment);
|
||||
} else {
|
||||
out_item->expand = false;
|
||||
}
|
||||
|
||||
if (!fragment_to_assignable(
|
||||
p,
|
||||
expand_ok,
|
||||
fragment_move(&fragment->list.children[i]),
|
||||
cur
|
||||
fragment_move(&item_fragment),
|
||||
&out_item->assignable
|
||||
)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
expand_ok = expand_ok && cur->type != APFL_EXPR_ASSIGNABLE_EXPAND;
|
||||
|
||||
out->list.len++;
|
||||
}
|
||||
|
||||
|
|
@ -1286,16 +1301,17 @@ error:
|
|||
static bool
|
||||
fragment_to_assignable(
|
||||
apfl_parser_ptr p,
|
||||
bool expand_ok,
|
||||
struct fragment fragment,
|
||||
struct apfl_expr_assignable *outptr
|
||||
) {
|
||||
struct apfl_expr_assignable out = { // Just a value that can be safely deinited
|
||||
.type = APFL_EXPR_ASSIGNABLE_EXPAND,
|
||||
.expand = NULL,
|
||||
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
||||
.constant = {
|
||||
.type = APFL_EXPR_CONST_NIL,
|
||||
},
|
||||
};
|
||||
|
||||
bool result = fragment_to_assignable_inner(p, expand_ok, &fragment, &out);
|
||||
bool result = fragment_to_assignable_inner(p, &fragment, &out);
|
||||
fragment_deinit(&fragment);
|
||||
if (result) {
|
||||
*outptr = out;
|
||||
|
|
@ -1556,7 +1572,7 @@ break_inner:
|
|||
struct fragment fragment = fragment_move(&fragments->children[0]);
|
||||
fragment_list_deinit(fragments); // Reset fragment list
|
||||
|
||||
if (!fragment_to_assignable(p, false, fragment, &cur_partial->lhs)) {
|
||||
if (!fragment_to_assignable(p, fragment, &cur_partial->lhs)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,24 @@ list_item(bool expand, struct apfl_expr *expr)
|
|||
};
|
||||
}
|
||||
|
||||
static struct apfl_expr_assignable_list_item
|
||||
assignable_list_item(bool expand, struct apfl_expr_assignable assignable)
|
||||
{
|
||||
return (struct apfl_expr_assignable_list_item) {
|
||||
.expand = expand,
|
||||
.assignable = assignable,
|
||||
};
|
||||
}
|
||||
|
||||
static struct apfl_expr_params_item
|
||||
params_item(bool expand, struct apfl_expr_param param)
|
||||
{
|
||||
return (struct apfl_expr_params_item) {
|
||||
.expand = expand,
|
||||
.param = param,
|
||||
};
|
||||
}
|
||||
|
||||
// Fugly macros to make it a bit easier to create a heap allocated struct value
|
||||
#define BEGIN_NEW(pt, T) ((T *)new_helper(pt, sizeof(T), &((T)
|
||||
#define END_NEW )))
|
||||
|
|
@ -186,8 +204,8 @@ MKLISTBUILDER(list, struct apfl_expr_list, struct apfl_expr_list_item, items, le
|
|||
MKLISTBUILDER(body, struct apfl_expr_body, struct apfl_expr, items, len)
|
||||
MKLISTBUILDER(dict, struct apfl_expr_dict, struct apfl_expr_dict_pair, items, len)
|
||||
MKLISTBUILDER(complex_func, struct apfl_expr_complex_func, struct apfl_expr_subfunc, subfuncs, len)
|
||||
MKLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_param, params, len)
|
||||
MKLISTBUILDER(assignable_list, struct apfl_expr_assignable_list, struct apfl_expr_assignable, children, len)
|
||||
MKLISTBUILDER(params, struct apfl_expr_params, struct apfl_expr_params_item, params, len)
|
||||
MKLISTBUILDER(assignable_list, struct apfl_expr_assignable_list, struct apfl_expr_assignable_list_item, items, len)
|
||||
|
||||
#define POS(l, c) (struct apfl_position) { .line = l, .col = c }
|
||||
|
||||
|
|
@ -508,10 +526,10 @@ TEST(factorial, t) {
|
|||
.complex_func = LIST_BEGIN(pt, complex_func)
|
||||
LIST_ADD (struct apfl_expr_subfunc) {
|
||||
.params = LIST_BEGIN(pt, params)
|
||||
LIST_ADD (struct apfl_expr_param) {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_CONSTANT,
|
||||
.constant = num_const(0),
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
.body = LIST_BEGIN(pt, body)
|
||||
LIST_ADD const_expr(2, 10, num_const(1)),
|
||||
|
|
@ -519,10 +537,10 @@ TEST(factorial, t) {
|
|||
},
|
||||
LIST_ADD (struct apfl_expr_subfunc) {
|
||||
.params = LIST_BEGIN(pt, params)
|
||||
LIST_ADD (struct apfl_expr_param) {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "n"),
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
.body = LIST_BEGIN(pt, body)
|
||||
LIST_ADD (struct apfl_expr) {
|
||||
|
|
@ -602,15 +620,15 @@ TEST(map, t) {
|
|||
.complex_func = LIST_BEGIN(pt, complex_func)
|
||||
LIST_ADD (struct apfl_expr_subfunc) {
|
||||
.params = LIST_BEGIN(pt, params)
|
||||
LIST_ADD (struct apfl_expr_param) {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "_"),
|
||||
},
|
||||
LIST_ADD (struct apfl_expr_param) {
|
||||
}),
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_LIST,
|
||||
.list = LIST_BEGIN(pt, params)
|
||||
LIST_END,
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
.body = LIST_BEGIN(pt, body)
|
||||
LIST_ADD (struct apfl_expr) {
|
||||
|
|
@ -623,26 +641,23 @@ TEST(map, t) {
|
|||
},
|
||||
LIST_ADD (struct apfl_expr_subfunc) {
|
||||
.params = LIST_BEGIN(pt, params)
|
||||
LIST_ADD (struct apfl_expr_param) {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "f"),
|
||||
},
|
||||
LIST_ADD (struct apfl_expr_param) {
|
||||
}),
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_LIST,
|
||||
.list = LIST_BEGIN(pt, params)
|
||||
LIST_ADD (struct apfl_expr_param) {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "x"),
|
||||
},
|
||||
LIST_ADD (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_EXPAND,
|
||||
.expand = BEGIN_NEW(pt, struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "xs"),
|
||||
} END_NEW
|
||||
},
|
||||
}),
|
||||
LIST_ADD params_item(true, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "xs"),
|
||||
}),
|
||||
LIST_END,
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
.body = LIST_BEGIN(pt, body)
|
||||
LIST_ADD (struct apfl_expr) {
|
||||
|
|
@ -876,26 +891,23 @@ TEST(assignment, t) {
|
|||
.lhs = (struct apfl_expr_assignable) {
|
||||
.type = APFL_EXPR_ASSIGNABLE_LIST,
|
||||
.list = LIST_BEGIN(pt, assignable_list)
|
||||
LIST_ADD (struct apfl_expr_assignable) {
|
||||
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
||||
.type = APFL_EXPR_ASSIGNABLE_LIST,
|
||||
.list = LIST_BEGIN(pt, assignable_list)
|
||||
LIST_ADD (struct apfl_expr_assignable) {
|
||||
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
||||
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
||||
.constant = num_const(1),
|
||||
},
|
||||
LIST_ADD (struct apfl_expr_assignable) {
|
||||
}),
|
||||
LIST_ADD assignable_list_item(false, (struct apfl_expr_assignable) {
|
||||
.type = APFL_EXPR_ASSIGNABLE_CONSTANT,
|
||||
.constant = num_const(2),
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
},
|
||||
LIST_ADD (struct apfl_expr_assignable) {
|
||||
.type = APFL_EXPR_ASSIGNABLE_EXPAND,
|
||||
.expand = BEGIN_NEW(pt, struct apfl_expr_assignable) {
|
||||
.type = APFL_EXPR_ASSIGNABLE_VAR,
|
||||
.var = new_string(pt, "xs"),
|
||||
} END_NEW,
|
||||
},
|
||||
}),
|
||||
LIST_ADD assignable_list_item(true, (struct apfl_expr_assignable) {
|
||||
.type = APFL_EXPR_ASSIGNABLE_VAR,
|
||||
.var = new_string(pt, "xs"),
|
||||
}),
|
||||
LIST_END,
|
||||
},
|
||||
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
|
||||
|
|
@ -1017,18 +1029,15 @@ TEST(complex_function, t) {
|
|||
.complex_func = LIST_BEGIN(pt, complex_func)
|
||||
LIST_ADD {
|
||||
.params = LIST_BEGIN(pt, params)
|
||||
LIST_ADD {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "a"),
|
||||
},
|
||||
LIST_ADD {
|
||||
.type = APFL_EXPR_PARAM_EXPAND,
|
||||
.expand = BEGIN_NEW(pt, struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "b"),
|
||||
} END_NEW
|
||||
},
|
||||
LIST_ADD {
|
||||
}),
|
||||
LIST_ADD params_item(true, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "b"),
|
||||
}),
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_PREDICATE,
|
||||
.predicate = {
|
||||
.lhs = BEGIN_NEW(pt, struct apfl_expr_param) {
|
||||
|
|
@ -1052,7 +1061,7 @@ TEST(complex_function, t) {
|
|||
},
|
||||
} END_NEW,
|
||||
},
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
.body = LIST_BEGIN(pt, body)
|
||||
LIST_ADD var(pt, 1, 21, "foo"),
|
||||
|
|
@ -1062,29 +1071,29 @@ TEST(complex_function, t) {
|
|||
},
|
||||
LIST_ADD {
|
||||
.params = LIST_BEGIN(pt, params)
|
||||
LIST_ADD {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_CONSTANT,
|
||||
.constant = num_const(1),
|
||||
},
|
||||
LIST_ADD {
|
||||
}),
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_LIST,
|
||||
.list = LIST_BEGIN(pt, params)
|
||||
LIST_END,
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
.body = LIST_BEGIN(pt, body)
|
||||
LIST_END,
|
||||
},
|
||||
LIST_ADD {
|
||||
.params = LIST_BEGIN(pt, params)
|
||||
LIST_ADD {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "x"),
|
||||
},
|
||||
LIST_ADD {
|
||||
}),
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_LIST,
|
||||
.list = LIST_BEGIN(pt, params)
|
||||
LIST_ADD {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_PREDICATE,
|
||||
.predicate = {
|
||||
.lhs = BEGIN_NEW(pt, struct apfl_expr_param) {
|
||||
|
|
@ -1093,16 +1102,13 @@ TEST(complex_function, t) {
|
|||
} END_NEW,
|
||||
.rhs = new_var(pt, 3, 15, "x"),
|
||||
},
|
||||
},
|
||||
LIST_ADD {
|
||||
.type = APFL_EXPR_PARAM_EXPAND,
|
||||
.expand = BEGIN_NEW(pt, struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "ys"),
|
||||
} END_NEW,
|
||||
},
|
||||
}),
|
||||
LIST_ADD params_item(true, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "ys"),
|
||||
}),
|
||||
LIST_END
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
.body = LIST_BEGIN(pt, body)
|
||||
LIST_ADD var(pt, 4, 5, "a"),
|
||||
|
|
@ -1119,10 +1125,10 @@ TEST(complex_function, t) {
|
|||
.complex_func = LIST_BEGIN(pt, complex_func)
|
||||
LIST_ADD {
|
||||
.params = LIST_BEGIN(pt, params)
|
||||
LIST_ADD {
|
||||
LIST_ADD params_item(false, (struct apfl_expr_param) {
|
||||
.type = APFL_EXPR_PARAM_VAR,
|
||||
.var = new_string(pt, "x"),
|
||||
},
|
||||
}),
|
||||
LIST_END,
|
||||
.body = LIST_BEGIN(pt, body)
|
||||
LIST_ADD var(pt, 6, 14, "y"),
|
||||
|
|
@ -1300,6 +1306,29 @@ TEST(err_dict_mapsto_too_early, t) {
|
|||
destroy_parser_test(pt);
|
||||
}
|
||||
|
||||
TEST(err_assign_multiple_expands_per_level, t) {
|
||||
struct parser_test *pt = new_parser_test(t, "[~foo ~bar] = baz");
|
||||
expect_error_of_type(pt, APFL_ERR_ONLY_ONE_EXPAND_ALLOWED);
|
||||
destroy_parser_test(pt);
|
||||
}
|
||||
TEST(err_assign_multiple_expands_per_level_nested, t) {
|
||||
struct parser_test *pt = new_parser_test(t, "[1 [~foo ~bar]] = baz");
|
||||
expect_error_of_type(pt, APFL_ERR_ONLY_ONE_EXPAND_ALLOWED);
|
||||
destroy_parser_test(pt);
|
||||
}
|
||||
|
||||
TEST(err_params_multiple_expands, t) {
|
||||
struct parser_test *pt = new_parser_test(t, "{ ~foo ~bar -> baz }");
|
||||
expect_error_of_type(pt, APFL_ERR_ONLY_ONE_EXPAND_ALLOWED);
|
||||
destroy_parser_test(pt);
|
||||
}
|
||||
|
||||
TEST(err_params_multiple_expands_nested, t) {
|
||||
struct parser_test *pt = new_parser_test(t, "{ 1 [~foo ~bar] -> baz }");
|
||||
expect_error_of_type(pt, APFL_ERR_ONLY_ONE_EXPAND_ALLOWED);
|
||||
destroy_parser_test(pt);
|
||||
}
|
||||
|
||||
TESTS_BEGIN
|
||||
ADDTEST(empty),
|
||||
ADDTEST(hello_world),
|
||||
|
|
@ -1333,4 +1362,8 @@ TESTS_BEGIN
|
|||
ADDTEST(err_statements_before_params),
|
||||
ADDTEST(err_dict_mapsto_missing),
|
||||
ADDTEST(err_dict_mapsto_too_early),
|
||||
ADDTEST(err_assign_multiple_expands_per_level),
|
||||
ADDTEST(err_assign_multiple_expands_per_level_nested),
|
||||
ADDTEST(err_params_multiple_expands),
|
||||
ADDTEST(err_params_multiple_expands_nested),
|
||||
TESTS_END
|
||||
|
|
|
|||
Loading…
Reference in a new issue