expr+parser: Restrict what an assignable can be

If you assign into a member access (`foo.bar = baz` or `foo@bar = baz`), it
is no longer permitted that the LHS of the at/dot is an arbitrary
assignable. It now must be a variable, at or dot. This disallows some silly
constructs (e.g. `[foo]@bar = baz`), increases the similarity to function
parameters and should make writing the evaluation code for these more easy.
This commit is contained in:
Laria 2022-01-08 22:55:24 +01:00
parent ac3af0baf1
commit 50cd2c18d2
6 changed files with 391 additions and 212 deletions

View file

@ -157,6 +157,8 @@ enum apfl_error_type {
APFL_ERR_INVALID_ASSIGNMENT_LHS,
APFL_ERR_EMPTY_ASSIGNMENT,
APFL_ERR_ONLY_ONE_EXPAND_ALLOWED,
APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS,
APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS,
};
const char *apfl_error_type_name(enum apfl_error_type);
@ -289,23 +291,38 @@ struct apfl_expr_complex_func {
};
enum apfl_expr_assignable_type {
APFL_EXPR_ASSIGNABLE_VAR,
APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
APFL_EXPR_ASSIGNABLE_CONSTANT,
APFL_EXPR_ASSIGNABLE_PREDICATE,
APFL_EXPR_ASSIGNABLE_DOT,
APFL_EXPR_ASSIGNABLE_AT,
APFL_EXPR_ASSIGNABLE_LIST,
};
struct apfl_expr_assignable_predicate {
struct apfl_expr_assignable *lhs;
struct apfl_expr *rhs;
enum apfl_expr_assignable_var_or_member_type {
APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT,
APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT,
};
struct apfl_expr_assignable_dot {
struct apfl_expr_assignable *lhs;
struct apfl_expr_assignable_var_or_member_dot {
struct apfl_expr_assignable_var_or_member *lhs;
struct apfl_string rhs;
};
struct apfl_expr_assignable_at {
struct apfl_expr_assignable_var_or_member_at {
struct apfl_expr_assignable_var_or_member *lhs;
struct apfl_expr *rhs;
};
struct apfl_expr_assignable_var_or_member {
enum apfl_expr_assignable_var_or_member_type type;
union {
struct apfl_string var;
struct apfl_expr_assignable_var_or_member_dot dot;
struct apfl_expr_assignable_var_or_member_at at;
};
};
struct apfl_expr_assignable_predicate {
struct apfl_expr_assignable *lhs;
struct apfl_expr *rhs;
};
@ -318,11 +335,9 @@ struct apfl_expr_assignable {
enum apfl_expr_assignable_type type;
union {
struct apfl_string var;
struct apfl_expr_assignable_var_or_member var_or_member;
struct apfl_expr_const constant;
struct apfl_expr_assignable_predicate predicate;
struct apfl_expr_assignable_dot dot;
struct apfl_expr_assignable_at at;
struct apfl_expr_assignable_list list;
};
};
@ -389,10 +404,11 @@ 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_var_or_member_dot_deinit(struct apfl_expr_assignable_var_or_member_dot *);
void apfl_expr_assignable_var_or_member_at_deinit(struct apfl_expr_assignable_var_or_member_at *);
void apfl_expr_assignable_var_or_member_deinit(struct apfl_expr_assignable_var_or_member *);
void apfl_expr_assignable_deinit(struct apfl_expr_assignable *);
void apfl_expr_assignment_deinit(struct apfl_expr_assignment *);
void apfl_expr_dot_deinit(struct apfl_expr_dot *);
@ -402,28 +418,29 @@ void apfl_expr_at_deinit(struct apfl_expr_at *);
// Begin move functions
struct apfl_expr apfl_expr_move(struct apfl_expr *);
struct apfl_expr_list apfl_expr_list_move(struct apfl_expr_list *);
struct apfl_expr_list_item apfl_expr_list_item_move(struct apfl_expr_list_item *);
struct apfl_expr_dict_pair apfl_expr_dict_pair_move(struct apfl_expr_dict_pair *);
struct apfl_expr_dict apfl_expr_dict_move(struct apfl_expr_dict *);
struct apfl_expr_call apfl_expr_call_move(struct apfl_expr_call *);
struct apfl_expr_body apfl_expr_body_move(struct apfl_expr_body *);
struct apfl_expr_const apfl_expr_const_move(struct apfl_expr_const *);
struct apfl_expr_param_predicate apfl_expr_param_predicate_move(struct apfl_expr_param_predicate *);
struct apfl_expr_param_list apfl_expr_param_list_move(struct apfl_expr_param_list *);
struct apfl_expr_params apfl_expr_params_move(struct apfl_expr_params *);
struct apfl_expr_param apfl_expr_param_move(struct apfl_expr_param *);
struct apfl_expr_subfunc apfl_expr_subfunc_move(struct apfl_expr_subfunc *);
struct apfl_expr_complex_func apfl_expr_complex_func_move(struct apfl_expr_complex_func *);
struct apfl_expr_assignable_predicate apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *);
struct apfl_expr_assignable_dot apfl_expr_assignable_dot_move(struct apfl_expr_assignable_dot *);
struct apfl_expr_assignable_at apfl_expr_assignable_at_move(struct apfl_expr_assignable_at *);
struct apfl_expr_assignable_list apfl_expr_assignable_list_move(struct apfl_expr_assignable_list *);
struct apfl_expr_assignable apfl_expr_assignable_move(struct apfl_expr_assignable *);
struct apfl_expr_assignment apfl_expr_assignment_move(struct apfl_expr_assignment *);
struct apfl_expr_dot apfl_expr_dot_move(struct apfl_expr_dot *);
struct apfl_expr_at apfl_expr_at_move(struct apfl_expr_at *);
struct apfl_expr apfl_expr_move(struct apfl_expr *);
struct apfl_expr_list apfl_expr_list_move(struct apfl_expr_list *);
struct apfl_expr_list_item apfl_expr_list_item_move(struct apfl_expr_list_item *);
struct apfl_expr_dict_pair apfl_expr_dict_pair_move(struct apfl_expr_dict_pair *);
struct apfl_expr_dict apfl_expr_dict_move(struct apfl_expr_dict *);
struct apfl_expr_call apfl_expr_call_move(struct apfl_expr_call *);
struct apfl_expr_body apfl_expr_body_move(struct apfl_expr_body *);
struct apfl_expr_const apfl_expr_const_move(struct apfl_expr_const *);
struct apfl_expr_param_predicate apfl_expr_param_predicate_move(struct apfl_expr_param_predicate *);
struct apfl_expr_param_list apfl_expr_param_list_move(struct apfl_expr_param_list *);
struct apfl_expr_params apfl_expr_params_move(struct apfl_expr_params *);
struct apfl_expr_param apfl_expr_param_move(struct apfl_expr_param *);
struct apfl_expr_subfunc apfl_expr_subfunc_move(struct apfl_expr_subfunc *);
struct apfl_expr_complex_func apfl_expr_complex_func_move(struct apfl_expr_complex_func *);
struct apfl_expr_assignable_var_or_member apfl_expr_assignable_var_or_member_move(struct apfl_expr_assignable_var_or_member *);
struct apfl_expr_assignable_predicate apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *);
struct apfl_expr_assignable_dot apfl_expr_assignable_dot_move(struct apfl_expr_assignable_dot *);
struct apfl_expr_assignable_at apfl_expr_assignable_at_move(struct apfl_expr_assignable_at *);
struct apfl_expr_assignable_list apfl_expr_assignable_list_move(struct apfl_expr_assignable_list *);
struct apfl_expr_assignable apfl_expr_assignable_move(struct apfl_expr_assignable *);
struct apfl_expr_assignment apfl_expr_assignment_move(struct apfl_expr_assignment *);
struct apfl_expr_dot apfl_expr_dot_move(struct apfl_expr_dot *);
struct apfl_expr_at apfl_expr_at_move(struct apfl_expr_at *);
// End move functions

View file

@ -48,6 +48,10 @@ apfl_error_type_name(enum apfl_error_type type)
return "APFL_ERR_EMPTY_ASSIGNMENT";
case APFL_ERR_ONLY_ONE_EXPAND_ALLOWED:
return "APFL_ERR_ONLY_ONE_EXPAND_ALLOWED";
case APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS:
return "APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS";
case APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS:
return "APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS";
}
return "<unknown error>";
@ -149,6 +153,21 @@ apfl_error_print(struct apfl_error error, FILE *file)
"Only one expansion (~) is allowed per level, near " POSFMT "\n",
POSARGS
);
break;
case APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS:
fprintf(
file,
"Unexpected constant in member access near " POSFMT "\n",
POSARGS
);
break;
case APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS:
fprintf(
file,
"Unexpected expression in member access near " POSFMT "\n",
POSARGS
);
break;
}
fprintf(file, "Unknown error %d\n", (int)error.type);

View file

@ -439,11 +439,14 @@ evaluate_assignment(apfl_ctx ctx, struct apfl_expr_assignment *assignment)
// TODO: Use assignment->local flag. Sonce we don't hev functions yet, it's not yet relevant.
// TODO: Handle special variable _. Maybe the parser should already generate a different assignment type for it?
if (assignment->lhs.type != APFL_EXPR_ASSIGNABLE_VAR) {
if (
assignment->lhs.type != APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER
|| assignment->lhs.var_or_member.type != APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR
) {
return (struct apfl_result) { .type = APFL_RESULT_ERR }; // TODO: Implement other assignable types
}
apfl_refcounted_string varname = apfl_string_move_into_new_refcounted(&assignment->lhs.var);
apfl_refcounted_string varname = apfl_string_move_into_new_refcounted(&assignment->lhs.var_or_member.var);
if (varname == NULL) {
return fatal();
}

View file

@ -165,24 +165,6 @@ apfl_expr_assignable_predicate_deinit(struct apfl_expr_assignable_predicate *pre
DEINIT_GENERIC_LHS_RHS_EXPR(pred, apfl_expr_assignable_deinit);
}
#define GENERIC_DOT_DEINIT(dot, lhs_deiniter) \
do { \
DESTROY(dot->lhs, lhs_deiniter); \
apfl_string_deinit(&dot->rhs); \
} while (0)
void
apfl_expr_assignable_dot_deinit(struct apfl_expr_assignable_dot *dot)
{
GENERIC_DOT_DEINIT(dot, apfl_expr_assignable_deinit);
}
void
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)
{
@ -195,12 +177,43 @@ apfl_expr_assignable_list_deinit(struct apfl_expr_assignable_list *list)
DEINIT_LIST(list->items, list->len, apfl_expr_assignable_list_item_deinit);
}
void
apfl_expr_assignable_var_or_member_dot_deinit(struct apfl_expr_assignable_var_or_member_dot *dot)
{
DESTROY(dot->lhs, apfl_expr_assignable_var_or_member_deinit);
apfl_string_deinit(&dot->rhs);
}
void
apfl_expr_assignable_var_or_member_at_deinit(struct apfl_expr_assignable_var_or_member_at *at)
{
DESTROY(at->lhs, apfl_expr_assignable_var_or_member_deinit);
DESTROY(at->rhs, apfl_expr_deinit);
}
void
apfl_expr_assignable_var_or_member_deinit(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(&var_or_member->var);
break;
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
apfl_expr_assignable_var_or_member_dot_deinit(&var_or_member->dot);
break;
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
apfl_expr_assignable_var_or_member_at_deinit(&var_or_member->at);
break;
}
}
void
apfl_expr_assignable_deinit(struct apfl_expr_assignable *a)
{
switch (a->type) {
case APFL_EXPR_ASSIGNABLE_VAR:
apfl_string_deinit(&a->var);
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
apfl_expr_assignable_var_or_member_deinit(&a->var_or_member);
break;
case APFL_EXPR_ASSIGNABLE_CONSTANT:
apfl_expr_const_deinit(&a->constant);
@ -208,12 +221,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_DOT:
apfl_expr_assignable_dot_deinit(&a->dot);
break;
case APFL_EXPR_ASSIGNABLE_AT:
apfl_expr_assignable_at_deinit(&a->at);
break;
case APFL_EXPR_ASSIGNABLE_LIST:
apfl_expr_assignable_list_deinit(&a->list);
break;
@ -230,7 +237,8 @@ apfl_expr_assignment_deinit(struct apfl_expr_assignment *a)
void
apfl_expr_dot_deinit(struct apfl_expr_dot *dot)
{
GENERIC_DOT_DEINIT(dot, apfl_expr_deinit);
DESTROY(dot->lhs, apfl_expr_deinit);
apfl_string_deinit(&dot->rhs);
}
void
@ -425,6 +433,27 @@ apfl_expr_complex_func_move(struct apfl_expr_complex_func *in)
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)
{
@ -433,28 +462,6 @@ apfl_expr_assignable_predicate_move(struct apfl_expr_assignable_predicate *in)
return out;
}
#define GENERIC_DOT_MOVE(out, in) \
do { \
MOVEPTR(out.lhs, in->lhs); \
out.rhs = apfl_string_move(&in->rhs); \
} while (0)
struct apfl_expr_assignable_dot
apfl_expr_assignable_dot_move(struct apfl_expr_assignable_dot *in)
{
struct apfl_expr_assignable_dot out;
GENERIC_DOT_MOVE(out, in);
return out;
}
struct apfl_expr_assignable_at
apfl_expr_assignable_at_move(struct apfl_expr_assignable_at *in)
{
struct apfl_expr_assignable_at 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)
{
@ -468,8 +475,8 @@ apfl_expr_assignable_move(struct apfl_expr_assignable *in)
{
struct apfl_expr_assignable out = *in;
switch (in->type) {
case APFL_EXPR_ASSIGNABLE_VAR:
out.var = apfl_string_move(&in->var);
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);
@ -477,12 +484,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_DOT:
out.dot = apfl_expr_assignable_dot_move(&in->dot);
break;
case APFL_EXPR_ASSIGNABLE_AT:
out.at = apfl_expr_assignable_at_move(&in->at);
break;
case APFL_EXPR_ASSIGNABLE_LIST:
out.list = apfl_expr_assignable_list_move(&in->list);
break;
@ -503,7 +504,8 @@ struct apfl_expr_dot
apfl_expr_dot_move(struct apfl_expr_dot *in)
{
struct apfl_expr_dot out;
GENERIC_DOT_MOVE(out, in);
MOVEPTR(out.lhs, in->lhs);
out.rhs = apfl_string_move(&in->rhs);
return out;
}
@ -617,12 +619,33 @@ print_param(struct apfl_expr_param *param, unsigned indent, FILE *f)
}
}
static void
print_assignable_var_or_member(struct apfl_expr_assignable_var_or_member var_or_member, unsigned indent, FILE *f)
{
switch (var_or_member.type) {
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR:
apfl_print_indented(indent, f, "Variable (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(var_or_member.var));
break;
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT:
apfl_print_indented(indent, f, "Dot (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(var_or_member.dot.rhs));
print_assignable_var_or_member(*var_or_member.dot.lhs, indent+1, f);
break;
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT:
apfl_print_indented(indent, f, "At\n");
apfl_print_indented(indent+1, f, "LHS\n");
print_assignable_var_or_member(*var_or_member.at.lhs, indent+2, f);
apfl_print_indented(indent+1, f, "RHS\n");
print_expr(var_or_member.at.rhs, indent+2, f);
break;
}
}
static void
print_assignable(struct apfl_expr_assignable assignable, unsigned indent, FILE *f)
{
switch(assignable.type) {
case APFL_EXPR_ASSIGNABLE_VAR:
apfl_print_indented(indent, f, "Var (" APFL_STR_FMT ")\n", APFL_STR_FMT_ARGS(assignable.var));
case APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER:
print_assignable_var_or_member(assignable.var_or_member, indent, f);
break;
case APFL_EXPR_ASSIGNABLE_CONSTANT:
print_constant(assignable.constant, indent, f);
@ -634,17 +657,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_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);
break;
case APFL_EXPR_ASSIGNABLE_AT:
apfl_print_indented(indent, f, "At\n");
apfl_print_indented(indent+1, f, "LHS\n");
print_assignable(*assignable.at.lhs, indent+2, f);
apfl_print_indented(indent+1, f, "RHS\n");
print_expr(assignable.at.rhs, indent+2, f);
break;
case APFL_EXPR_ASSIGNABLE_LIST:
apfl_print_indented(indent, f, "List\n");
for (size_t i = 0; i < assignable.list.len; i++) {
@ -834,6 +846,28 @@ param_eq(struct apfl_expr_param a, struct apfl_expr_param b)
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)
{
@ -842,19 +876,13 @@ assignable_eq(struct apfl_expr_assignable a, struct apfl_expr_assignable b)
}
switch (a.type) {
case APFL_EXPR_ASSIGNABLE_VAR:
return apfl_string_eq(a.var, b.var);
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_DOT:
return assignable_eq(*a.dot.lhs, *b.dot.lhs)
&& apfl_string_eq(a.dot.rhs, b.dot.rhs);
case APFL_EXPR_ASSIGNABLE_AT:
return assignable_eq(*a.at.lhs, *b.at.lhs)
&& apfl_expr_eq(*a.at.rhs, *b.at.rhs);
case APFL_EXPR_ASSIGNABLE_LIST:
if (a.list.len != b.list.len) {
return false;

View file

@ -1140,6 +1140,86 @@ fragments_to_params(
return ok;
}
static bool fragment_to_assignable_var_or_member(
apfl_parser_ptr p,
struct fragment *fragment,
struct apfl_expr_assignable_var_or_member *out
) {
struct apfl_expr_assignable_var_or_member *lhs;
struct apfl_expr *rhs;
switch (fragment->type) {
case FRAG_EXPAND:
p->error = err_unexpected_token(APFL_TOK_EXPAND, fragment->position);
return false;
case FRAG_CONSTANT:
p->error = (struct apfl_error) {
.type = APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS,
.position = fragment->position,
};
return false;
case FRAG_NAME:
out->type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
out->var = apfl_string_move(&fragment->name);
return true;
case FRAG_DOT:
lhs = ALLOC(struct apfl_expr_assignable_var_or_member);
if (lhs == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return false;
}
if (!fragment_to_assignable_var_or_member(p, fragment->dot.lhs, lhs)) {
free(lhs);
return false;
}
out->type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT;
out->dot = (struct apfl_expr_assignable_var_or_member_dot) {
.lhs = lhs,
.rhs = apfl_string_move(&fragment->dot.rhs),
};
return true;
case FRAG_AT:
lhs = ALLOC(struct apfl_expr_assignable_var_or_member);
rhs = ALLOC(struct apfl_expr);
if (lhs == NULL || rhs == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
return false;
}
if (!fragment_to_assignable_var_or_member(p, fragment->at.lhs, lhs)) {
free(lhs);
free(rhs);
return false;
}
if (!fragment_to_expr(p, fragment_move(fragment->at.rhs), rhs)) {
DESTROY(lhs, apfl_expr_assignable_var_or_member_deinit);
free(rhs);
return false;
}
out->type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT;
out->at = (struct apfl_expr_assignable_var_or_member_at) {
.lhs = lhs,
.rhs = rhs,
};
return true;
case FRAG_PREDICATE:
p->error = err_unexpected_token(APFL_TOK_QUESTION_MARK, fragment->position);
return false;
case FRAG_EXPR:
p->error = (struct apfl_error) {
.type = APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS,
.position = fragment->position,
};
return false;
case FRAG_LIST:
p->error = err_unexpected_token(APFL_TOK_LBRACKET, fragment->position);
return false;
}
assert(false);
return false;
}
static bool
fragment_to_assignable(
apfl_parser_ptr p,
@ -1153,6 +1233,8 @@ fragment_to_assignable_inner(
struct fragment *fragment,
struct apfl_expr_assignable *out
) {
struct apfl_expr_assignable_var_or_member var_or_member;
switch (fragment->type) {
case FRAG_EXPAND:
p->error = err_unexpected_token(APFL_TOK_EXPAND, fragment->position);
@ -1162,57 +1244,13 @@ fragment_to_assignable_inner(
out->constant = apfl_expr_const_move(&fragment->constant);
return true;
case FRAG_NAME:
out->type = APFL_EXPR_ASSIGNABLE_VAR;
out->var = apfl_string_move(&fragment->name);
return true;
case FRAG_DOT:
out->type = APFL_EXPR_ASSIGNABLE_DOT;
out->dot.rhs = apfl_string_move(&fragment->dot.rhs);
if ((out->dot.lhs = malloc(sizeof(struct apfl_expr_assignable))) == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
goto error;
}
if (!fragment_to_assignable(
p,
fragment_move(fragment->dot.lhs),
out->dot.lhs
)) {
goto error;
}
return true;
case FRAG_AT:
out->type = APFL_EXPR_ASSIGNABLE_AT;
out->at.lhs = malloc(sizeof(struct apfl_expr_assignable));
out->at.rhs = malloc(sizeof(struct apfl_expr));
if (out->at.lhs == NULL || out->at.rhs == NULL) {
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
if (!fragment_to_assignable_var_or_member(p, fragment, &var_or_member)) {
goto error;
}
if (!fragment_to_assignable(
p,
fragment_move(fragment->at.lhs),
out->at.lhs
)) {
free(out->at.rhs);
out->at.rhs = NULL;
goto error;
}
if (!fragment_to_expr(
p,
fragment_move(fragment->at.rhs),
out->at.rhs
)) {
goto error;
}
out->type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER;
out->var_or_member = var_or_member;
return true;
case FRAG_PREDICATE:
out->type = APFL_EXPR_ASSIGNABLE_PREDICATE;

View file

@ -289,6 +289,26 @@ new_const_expr(struct parser_test *pt, int line, int col, struct apfl_expr_const
return new_helper(pt, sizeof(struct apfl_expr), &expr);
}
static struct apfl_expr_assignable
assignable_var(struct parser_test *pt, const char *name)
{
return (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
.var_or_member = {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
.var = new_string(pt, name),
},
};
}
static struct apfl_expr_assignable *
new_assignable_var(struct parser_test *pt, const char *name)
{
struct apfl_expr_assignable assignable = assignable_var(pt, name);
return new_helper(pt, sizeof(struct apfl_expr_assignable), &assignable);
}
TEST(empty, t) {
struct parser_test *pt = new_parser_test(t, "");
expect_eof(pt);
@ -516,10 +536,7 @@ TEST(factorial, t) {
.type = APFL_EXPR_ASSIGNMENT,
.position = POS(1, 11),
.assignment = (struct apfl_expr_assignment) {
.lhs = (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR,
.var = new_string(pt, "factorial"),
},
.lhs = assignable_var(pt, "factorial"),
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_COMPLEX_FUNC,
.position = POS(1, 14),
@ -610,10 +627,7 @@ TEST(map, t) {
.type = APFL_EXPR_ASSIGNMENT,
.position = POS(1, 5),
.assignment = (struct apfl_expr_assignment) {
.lhs = (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR,
.var = new_string(pt, "map"),
},
.lhs = assignable_var(pt, "map"),
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_COMPLEX_FUNC,
.position = POS(1, 8),
@ -841,10 +855,7 @@ TEST(assignment, t) {
.position = POS(1, 3),
.assignment = (struct apfl_expr_assignment) {
.local = false,
.lhs = (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR,
.var = new_string(pt, "a"),
},
.lhs = assignable_var(pt, "a"),
.rhs = new_var(pt, 1, 5, "b"),
},
});
@ -854,13 +865,16 @@ TEST(assignment, t) {
.assignment = (struct apfl_expr_assignment) {
.local = true,
.lhs = (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_DOT,
.dot = (struct apfl_expr_assignable_dot) {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR,
.var = new_string(pt, "foo"),
} END_NEW,
.rhs = new_string(pt, "bar"),
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
.var_or_member = {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT,
.dot = {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable_var_or_member) {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
.var = new_string(pt, "foo"),
} END_NEW,
.rhs = new_string(pt, "bar"),
},
},
},
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
@ -871,10 +885,7 @@ TEST(assignment, t) {
.lhs = (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_PREDICATE,
.predicate = (struct apfl_expr_assignable_predicate) {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR,
.var = new_string(pt, "bar"),
} END_NEW,
.lhs = new_assignable_var(pt, "bar"),
.rhs = new_var(pt, 2, 16, "pred"),
},
},
@ -904,10 +915,7 @@ TEST(assignment, t) {
}),
LIST_END,
}),
LIST_ADD assignable_list_item(true, (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR,
.var = new_string(pt, "xs"),
}),
LIST_ADD assignable_list_item(true, assignable_var(pt, "xs")),
LIST_END,
},
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
@ -916,23 +924,26 @@ TEST(assignment, t) {
.assignment = (struct apfl_expr_assignment) {
.local = true,
.lhs = (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_AT,
.at = (struct apfl_expr_assignable_at) {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR,
.var = new_string(pt, "bla"),
} END_NEW,
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_CALL,
.position = POS(3, 19),
.call = (struct apfl_expr_call) {
.callee = new_var(pt, 3, 20, "a"),
.arguments = LIST_BEGIN(pt, list)
LIST_ADD list_item(false, new_var(pt, 3, 22, "b")),
LIST_END,
},
} END_NEW,
},
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
.var_or_member = {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT,
.at = {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable_var_or_member) {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
.var = new_string(pt, "bla"),
} END_NEW,
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_CALL,
.position = POS(3, 19),
.call = (struct apfl_expr_call) {
.callee = new_var(pt, 3, 20, "a"),
.arguments = LIST_BEGIN(pt, list)
LIST_ADD list_item(false, new_var(pt, 3, 22, "b")),
LIST_END,
},
} END_NEW,
},
}
},
.rhs = BEGIN_NEW(pt, struct apfl_expr) {
.type = APFL_EXPR_CALL,
@ -990,10 +1001,7 @@ TEST(simple_function, t) {
.position = POS(4, 9),
.assignment = (struct apfl_expr_assignment) {
.local = false,
.lhs = (struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR,
.var = new_string(pt, "baz"),
},
.lhs = assignable_var(pt, "baz"),
.rhs = new_const_expr(pt, 4, 11, num_const(10)),
},
},
@ -1223,6 +1231,50 @@ TEST(function_calls, t) {
destroy_parser_test(pt);
}
TEST(member_assignment_with_predicate, t) {
struct parser_test *pt = new_parser_test(t, "a.b@c?d?e = f");
expect_expr(pt, (struct apfl_expr) {
.type = APFL_EXPR_ASSIGNMENT,
.position = POS(1, 11),
.assignment = {
.lhs = {
.type = APFL_EXPR_ASSIGNABLE_PREDICATE,
.predicate = {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_PREDICATE,
.predicate = {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable) {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER,
.var_or_member = {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_AT,
.at = {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable_var_or_member) {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_DOT,
.dot = {
.lhs = BEGIN_NEW(pt, struct apfl_expr_assignable_var_or_member) {
.type = APFL_EXPR_ASSIGNABLE_VAR_OR_MEMBER_VAR,
.var = new_string(pt, "a"),
} END_NEW,
.rhs = new_string(pt, "b"),
},
} END_NEW,
.rhs = new_var(pt, 1, 5, "c"),
},
},
} END_NEW,
.rhs = new_var(pt, 1, 7, "d"),
}
} END_NEW,
.rhs = new_var(pt, 1, 9, "e"),
},
},
.rhs = new_var(pt, 1, 13, "f")
}
});
expect_eof(pt);
destroy_parser_test(pt);
}
TEST(err_empty_assignment, t) {
struct parser_test *pt = new_parser_test(t, "= foo bar");
expect_error_of_type(pt, APFL_ERR_EMPTY_ASSIGNMENT);
@ -1329,6 +1381,24 @@ TEST(err_params_multiple_expands_nested, t) {
destroy_parser_test(pt);
}
TEST(err_unexpected_expression_in_member_assignable, t) {
struct parser_test *pt = new_parser_test(t, "(foo).bar = 123");
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS);
destroy_parser_test(pt);
}
TEST(err_unexpected_constant_in_member_assignable, t) {
struct parser_test *pt = new_parser_test(t, "nil.bar = 456");
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS);
destroy_parser_test(pt);
}
TEST(err_unexpected_assignment_member_access, t) {
struct parser_test *pt = new_parser_test(t, "[1 2 3]@bar = 456");
expect_error_of_type(pt, APFL_ERR_UNEXPECTED_TOKEN);
destroy_parser_test(pt);
}
TESTS_BEGIN
ADDTEST(empty),
ADDTEST(hello_world),
@ -1349,6 +1419,7 @@ TESTS_BEGIN
ADDTEST(simple_function),
ADDTEST(complex_function),
ADDTEST(function_calls),
ADDTEST(member_assignment_with_predicate),
ADDTEST(err_empty_assignment),
ADDTEST(err_mismatching_parens),
ADDTEST(err_unclosed_func),
@ -1366,4 +1437,7 @@ TESTS_BEGIN
ADDTEST(err_assign_multiple_expands_per_level_nested),
ADDTEST(err_params_multiple_expands),
ADDTEST(err_params_multiple_expands_nested),
ADDTEST(err_unexpected_expression_in_member_assignable),
ADDTEST(err_unexpected_constant_in_member_assignable),
ADDTEST(err_unexpected_assignment_member_access),
TESTS_END