diff --git a/src/apfl.h b/src/apfl.h index 9f25c51..5c72e2e 100644 --- a/src/apfl.h +++ b/src/apfl.h @@ -159,6 +159,7 @@ enum apfl_error_type { APFL_ERR_ONLY_ONE_EXPAND_ALLOWED, APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS, APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS, + APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS, }; const char *apfl_error_type_name(enum apfl_error_type); @@ -192,6 +193,7 @@ enum apfl_expr_type { APFL_EXPR_AT, APFL_EXPR_CONSTANT, APFL_EXPR_VAR, + APFL_EXPR_BLANK, }; struct apfl_expr_list_item { @@ -257,6 +259,7 @@ enum apfl_expr_param_type { APFL_EXPR_PARAM_CONSTANT, APFL_EXPR_PARAM_PREDICATE, APFL_EXPR_PARAM_LIST, + APFL_EXPR_PARAM_BLANK, }; struct apfl_expr_params { @@ -295,6 +298,8 @@ enum apfl_expr_assignable_type { APFL_EXPR_ASSIGNABLE_CONSTANT, APFL_EXPR_ASSIGNABLE_PREDICATE, APFL_EXPR_ASSIGNABLE_LIST, + APFL_EXPR_ASSIGNABLE_BLANK, + }; enum apfl_expr_assignable_var_or_member_type { @@ -377,6 +382,7 @@ struct apfl_expr { struct apfl_expr_at at; struct apfl_expr_const constant; struct apfl_string var; + // blank has no further data }; struct apfl_position position; diff --git a/src/error.c b/src/error.c index 445b612..49884aa 100644 --- a/src/error.c +++ b/src/error.c @@ -1,4 +1,4 @@ -#include + #include #include "apfl.h" @@ -52,6 +52,8 @@ apfl_error_type_name(enum apfl_error_type type) return "APFL_ERR_UNEXPECTED_CONSTANT_IN_MEMBER_ACCESS"; case APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS: return "APFL_ERR_UNEXPECTED_EXPR_IN_MEMBER_ACCESS"; + case APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS: + return "APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS"; } return ""; @@ -168,6 +170,13 @@ apfl_error_print(struct apfl_error error, FILE *file) POSARGS ); break; + case APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS: + fprintf( + file, + "Unexpected blank (\"_\") 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 f306177..04afe5b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -437,7 +437,6 @@ static struct apfl_result 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_OR_MEMBER @@ -508,6 +507,13 @@ evaluate(apfl_ctx ctx, struct apfl_expr *expr) return evaluate_assignment(ctx, &expr->assignment); case APFL_EXPR_VAR: return evaluate_var(ctx, &expr->var); + case APFL_EXPR_BLANK: + return (struct apfl_result) { + .type = APFL_RESULT_OK, + .value = { + .type = APFL_VALUE_NIL, + }, + }; case APFL_EXPR_CALL: case APFL_EXPR_SIMPLE_FUNC: case APFL_EXPR_COMPLEX_FUNC: diff --git a/src/expr.c b/src/expr.c index 3d15259..0313cf9 100644 --- a/src/expr.c +++ b/src/expr.c @@ -41,6 +41,9 @@ apfl_expr_deinit(struct apfl_expr *expr) case APFL_EXPR_VAR: apfl_string_deinit(&expr->var); break; + case APFL_EXPR_BLANK: + // nop + break; } } @@ -143,6 +146,9 @@ apfl_expr_param_deinit(struct apfl_expr_param *param) case APFL_EXPR_PARAM_LIST: apfl_expr_params_deinit(¶m->list); break; + case APFL_EXPR_PARAM_BLANK: + // nop + break; } } @@ -224,6 +230,9 @@ apfl_expr_assignable_deinit(struct apfl_expr_assignable *a) case APFL_EXPR_ASSIGNABLE_LIST: apfl_expr_assignable_list_deinit(&a->list); break; + case APFL_EXPR_ASSIGNABLE_BLANK: + // nop + break; } } @@ -284,6 +293,9 @@ apfl_expr_move(struct apfl_expr *in) case APFL_EXPR_VAR: out.var = apfl_string_move(&in->var); break; + case APFL_EXPR_BLANK: + // nop + break; } return out; } @@ -412,6 +424,9 @@ apfl_expr_param_move(struct apfl_expr_param *in) case APFL_EXPR_PARAM_LIST: out.list = apfl_expr_params_move(&in->list); break; + case APFL_EXPR_PARAM_BLANK: + // nop + break; } return out; } @@ -487,6 +502,9 @@ apfl_expr_assignable_move(struct apfl_expr_assignable *in) case APFL_EXPR_ASSIGNABLE_LIST: out.list = apfl_expr_assignable_list_move(&in->list); break; + case APFL_EXPR_ASSIGNABLE_BLANK: + // nop + break; } return out; } @@ -616,6 +634,9 @@ print_param(struct apfl_expr_param *param, unsigned indent, FILE *f) print_params_item(¶m->list.params[i], indent+1, f); } break; + case APFL_EXPR_PARAM_BLANK: + apfl_print_indented(indent, f, "Blank (_)\n"); + break; } } @@ -669,6 +690,9 @@ print_assignable(struct apfl_expr_assignable assignable, unsigned indent, FILE * print_assignable(item->assignable, indent_item, f); } break; + case APFL_EXPR_ASSIGNABLE_BLANK: + apfl_print_indented(indent, f, "Blank (_)\n"); + break; } } @@ -738,6 +762,9 @@ print_expr(struct apfl_expr *expr, unsigned indent, FILE *f) case APFL_EXPR_VAR: apfl_print_indented(indent, f, "Var (" APFL_STR_FMT ") @ " POSFMT "\n", APFL_STR_FMT_ARGS(expr->var), POSARGS(expr->position)); break; + case APFL_EXPR_BLANK: + apfl_print_indented(indent, f, "Blank (_)\n"); + break; } } @@ -840,6 +867,8 @@ param_eq(struct apfl_expr_param a, struct apfl_expr_param b) && apfl_expr_eq(*a.predicate.rhs, *b.predicate.rhs); case APFL_EXPR_PARAM_LIST: return params_eq(a.list, b.list); + case APFL_EXPR_PARAM_BLANK: + return true; } assert(false); @@ -896,6 +925,8 @@ assignable_eq(struct apfl_expr_assignable a, struct apfl_expr_assignable b) } } return true; + case APFL_EXPR_ASSIGNABLE_BLANK: + return true; } assert(false); @@ -961,6 +992,8 @@ apfl_expr_eq(struct apfl_expr a, struct apfl_expr b) return const_eq(a.constant, b.constant); case APFL_EXPR_VAR: return apfl_string_eq(a.var, b.var); + case APFL_EXPR_BLANK: + return true; } assert(false); diff --git a/src/parser.c b/src/parser.c index 9236fb7..a67aa62 100644 --- a/src/parser.c +++ b/src/parser.c @@ -41,6 +41,7 @@ enum fragment_type { FRAG_PREDICATE, FRAG_EXPR, FRAG_LIST, + FRAG_BLANK, }; struct fragment_dot { @@ -148,6 +149,9 @@ fragment_deinit(struct fragment *fragment) case FRAG_LIST: fragment_list_deinit(&fragment->list); break; + case FRAG_BLANK: + // nop + break; } } @@ -209,6 +213,9 @@ fragment_move(struct fragment *in) case FRAG_LIST: out.list = fragment_list_move(&in->list); break; + case FRAG_BLANK: + // nop + break; } return out; @@ -630,6 +637,10 @@ fragment_to_expr_inner(apfl_parser_ptr p, struct fragment *fragment, struct apfl out->list.len++; } return true; + case FRAG_BLANK: + out->type = APFL_EXPR_BLANK; + out->position = fragment->position; + return true; } assert(false); @@ -1054,6 +1065,9 @@ fragment_to_param_inner( case FRAG_LIST: out->type = APFL_EXPR_PARAM_LIST; return fragments_to_params(p, fragment_list_move(&fragment->list), &out->list); + case FRAG_BLANK: + out->type = APFL_EXPR_PARAM_BLANK; + return true; } assert(false); @@ -1213,6 +1227,12 @@ static bool fragment_to_assignable_var_or_member( case FRAG_LIST: p->error = err_unexpected_token(APFL_TOK_LBRACKET, fragment->position); return false; + case FRAG_BLANK: + p->error = (struct apfl_error) { + .type = APFL_ERR_UNEXPECTED_BLANK_IN_MEMBER_ACCESS, + .position = fragment->position, + }; + return false; } assert(false); @@ -1327,6 +1347,9 @@ fragment_to_assignable_inner( out->list.len++; } + return true; + case FRAG_BLANK: + out->type = APFL_EXPR_ASSIGNABLE_BLANK; return true; } @@ -1895,6 +1918,8 @@ parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum par .type = APFL_EXPR_CONST_BOOLEAN, .boolean = false, }; + } else if (apfl_string_eq(p->token.text, "_")) { + fragment->type = FRAG_BLANK; } else { fragment->type = FRAG_NAME; fragment->name = apfl_string_move(&p->token.text); diff --git a/src/parser_test.c b/src/parser_test.c index c312bef..6176422 100644 --- a/src/parser_test.c +++ b/src/parser_test.c @@ -635,8 +635,7 @@ TEST(map, t) { LIST_ADD (struct apfl_expr_subfunc) { .params = LIST_BEGIN(pt, params) LIST_ADD params_item(false, (struct apfl_expr_param) { - .type = APFL_EXPR_PARAM_VAR, - .var = new_string(pt, "_"), + .type = APFL_EXPR_PARAM_BLANK, }), LIST_ADD params_item(false, (struct apfl_expr_param) { .type = APFL_EXPR_PARAM_LIST,