From 50cd2c18d281b699b79df10f55ac4e345501190a Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Sat, 8 Jan 2022 22:55:24 +0100 Subject: [PATCH] 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. --- src/apfl.h | 89 +++++++++++++--------- src/error.c | 19 +++++ src/eval.c | 7 +- src/expr.c | 186 ++++++++++++++++++++++++++-------------------- src/parser.c | 132 ++++++++++++++++++++------------ src/parser_test.c | 170 ++++++++++++++++++++++++++++++------------ 6 files changed, 391 insertions(+), 212 deletions(-) diff --git a/src/apfl.h b/src/apfl.h index a381b52..9f25c51 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -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 diff --git a/src/error.c b/src/error.c index 4f5e9e7..445b612 100644 --- a/src/error.c +++ b/src/error.c @@ -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 ""; @@ -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); diff --git a/src/eval.c b/src/eval.c index ac49051..f306177 100644 --- a/src/eval.c +++ b/src/eval.c @@ -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(); } diff --git a/src/expr.c b/src/expr.c index b0799a9..3d15259 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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; diff --git a/src/parser.c b/src/parser.c index c77d1b7..9236fb7 100644 --- a/src/parser.c +++ b/src/parser.c @@ -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; diff --git a/src/parser_test.c b/src/parser_test.c index a81f6a2..c312bef 100644 --- a/src/parser_test.c +++ b/src/parser_test.c @@ -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