#include #include #include #include "apfl.h" #include "resizable.h" #include "internal.h" struct apfl_parser { struct apfl_parser_token_source token_source; bool has_expr; struct apfl_expr expr; struct apfl_error error; bool eof; bool has_token; bool has_unread; struct apfl_token token; }; enum parse_fragment_result { PF_OK, PF_CANT_HANDLE, PF_EOF, PF_ERROR, }; enum parse_fragment_flags { FFLAG_NO_EXPAND = 1, FFLAG_NO_POSTFIXS = 2, }; enum fragment_type { FRAG_EXPAND, FRAG_CONSTANT, FRAG_NAME, FRAG_DOT, FRAG_AT, FRAG_PREDICATE, FRAG_EXPR, FRAG_LIST, }; struct fragment_dot { struct fragment *lhs; struct apfl_string rhs; }; struct fragment_lhs_rhs { struct fragment *lhs; struct fragment *rhs; }; struct fragment_list { APFL_RESIZABLE_TRAIT(struct fragment, children) }; struct fragment { enum fragment_type type; union { struct fragment *expand; struct apfl_expr_const constant; struct apfl_string name; struct fragment_dot dot; struct fragment_lhs_rhs at; struct fragment_lhs_rhs predicate; struct apfl_expr expr; struct fragment_list list; }; struct apfl_position position; }; static enum parse_fragment_result parse_fragment(apfl_parser_ptr, struct fragment*, bool need, enum parse_fragment_flags); static bool grow_fragment_cap(struct fragment_list *list, size_t inc) { return apfl_resizable_grow_cap( sizeof(struct fragment), APFL_RESIZABLE_ARGS(*list, children), inc ); } static bool append_fragment(struct fragment_list *list, struct fragment fragment) { return apfl_resizable_append( sizeof(struct fragment), APFL_RESIZABLE_ARGS(*list, children), &fragment, 1 ); } static void fragment_deinit(struct fragment *); static void deinit_fragment_lhs_rhs(struct fragment_lhs_rhs *lr) { DESTROY(lr->lhs, fragment_deinit); DESTROY(lr->rhs, fragment_deinit); } static void fragment_list_deinit(struct fragment_list *list) { DEINIT_LIST(list->children, list->len, fragment_deinit); apfl_resizable_init(APFL_RESIZABLE_ARGS(*list, children)); } static void fragment_deinit(struct fragment *fragment) { if (fragment == NULL) { return; } switch (fragment->type) { case FRAG_EXPAND: DESTROY(fragment->expand, fragment_deinit); break; case FRAG_CONSTANT: apfl_expr_const_deinit(&fragment->constant); break; case FRAG_NAME: apfl_string_deinit(&fragment->name); break; case FRAG_DOT: DESTROY(fragment->dot.lhs, fragment_deinit); apfl_string_deinit(&fragment->dot.rhs); break; case FRAG_AT: deinit_fragment_lhs_rhs(&fragment->at); break; case FRAG_PREDICATE: deinit_fragment_lhs_rhs(&fragment->at); break; case FRAG_EXPR: apfl_expr_deinit(&fragment->expr); break; case FRAG_LIST: fragment_list_deinit(&fragment->list); break; } } static struct fragment_dot fragment_dot_move(struct fragment_dot *in) { struct fragment_dot out; MOVEPTR(out.lhs, in->lhs); out.rhs = apfl_string_move(&in->rhs); return out; } static struct fragment_lhs_rhs fragment_lhs_rhs_move(struct fragment_lhs_rhs *in) { struct fragment_lhs_rhs out; MOVEPTR(out.lhs, in->lhs); MOVEPTR(out.rhs, in->rhs); return out; } static struct fragment_list fragment_list_move(struct fragment_list *in) { struct fragment_list out = *in; MOVEPTR(out.children, in->children); in->len = 0; in->cap = 0; return out; } static struct fragment fragment_move(struct fragment *in) { struct fragment out = *in; switch (in->type) { case FRAG_EXPAND: MOVEPTR(out.expand, in->expand); break; case FRAG_CONSTANT: out.constant = apfl_expr_const_move(&in->constant); break; case FRAG_NAME: out.name = apfl_string_move(&in->name); break; case FRAG_DOT: out.dot = fragment_dot_move(&in->dot); break; case FRAG_AT: out.at = fragment_lhs_rhs_move(&in->at); break; case FRAG_PREDICATE: out.predicate = fragment_lhs_rhs_move(&in->predicate); break; case FRAG_EXPR: out.expr = apfl_expr_move(&in->expr); break; case FRAG_LIST: out.list = fragment_list_move(&in->list); break; } return out; } apfl_parser_ptr apfl_parser_new(struct apfl_parser_token_source token_source) { apfl_parser_ptr p = malloc(sizeof(struct apfl_parser)); if (p == NULL) { return NULL; } p->token_source = token_source; p->eof = false; p->has_token = false; p->has_unread = false; p->has_expr = false; return p; } static enum apfl_parse_result get_raw_token(apfl_parser_ptr p, struct apfl_token *token, bool need) { struct apfl_parser_token_source *src = &(p->token_source); enum apfl_parse_result result = src->next(src->opaque, need); switch (result) { case APFL_PARSE_ERROR: p->error = src->get_error(src->opaque); break; case APFL_PARSE_OK: *token = src->get_token(src->opaque); break; default: // nop break; } return result; } static enum apfl_parse_result get_non_comment_token(apfl_parser_ptr p, struct apfl_token *token, bool need) { for (;;) { enum apfl_parse_result result = get_raw_token(p, token, need); if (result != APFL_PARSE_OK) { return result; } if (token->type == APFL_TOK_COMMENT) { apfl_token_deinit(token); } else { return APFL_PARSE_OK; } } } static enum apfl_parse_result get_preprocessed_token(apfl_parser_ptr p, struct apfl_token *token, bool need) { enum apfl_parse_result result; for (;;) { result = get_non_comment_token(p, token, need); if (result != APFL_PARSE_OK) { return result; } if (token->type != APFL_TOK_CONTINUE_LINE) { return APFL_PARSE_OK; } struct apfl_position continue_line_pos = token->position; apfl_token_deinit(token); result = get_non_comment_token(p, token, true); if (result != APFL_PARSE_OK) { return result; } if (token->type != APFL_TOK_LINEBREAK) { apfl_token_deinit(token); p->error = (struct apfl_error) { .type = APFL_ERR_NO_LINEBREAK_AFTER_CONTINUE_LINE, .position = continue_line_pos, }; return APFL_PARSE_ERROR; } apfl_token_deinit(token); } } static enum apfl_parse_result read_token(apfl_parser_ptr p, bool need) { if (p->eof) { return APFL_PARSE_EOF; } if (p->has_unread) { p->has_unread = false; return APFL_PARSE_OK; } if (p->has_token) { apfl_token_deinit(&p->token); } enum apfl_parse_result result = get_preprocessed_token(p, &p->token, need); p->eof = result == APFL_PARSE_EOF; p->has_token = result == APFL_PARSE_OK; return result; } static void unread_token(apfl_parser_ptr p) { assert(!p->eof); assert(p->has_token); assert(!p->has_unread); p->has_unread = true; } // Must only be called after an PF_CANT_HANDLE! static void read_token_after_cant_handle(apfl_parser_ptr p) { // A function that returns PF_CANT_HANDLE always unreads a token, so we are // guaranteed to have at least one token. assert(read_token(p, true) == APFL_PARSE_OK); } static struct apfl_error err_unexpected_token(enum apfl_token_type token_type, struct apfl_position pos) { return (struct apfl_error) { .type = APFL_ERR_UNEXPECTED_TOKEN, .token_type = token_type, .position = pos, }; } #define ERR_UNEXPECTED_TOKEN(t) (err_unexpected_token((t).type, (t).position)) static enum parse_fragment_result parse_fragment_into_list(apfl_parser_ptr p, struct fragment_list *list, bool need, enum parse_fragment_flags flags) { if (!grow_fragment_cap(list, 1)) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return PF_ERROR; } struct fragment *elem = &list->children[list->len]; enum parse_fragment_result result = parse_fragment(p, elem, need, flags); if (result != PF_OK) {// \mystuff\TODO:even more flags? return result; } list->len++; return PF_OK; } static struct apfl_error err_unexpected_eof_after(enum apfl_token_type token_type, struct apfl_position pos) { return (struct apfl_error) { .type = APFL_ERR_UNEXPECTED_EOF_AFTER_TOKEN, .token_type = token_type, .position = pos, }; } static bool fragments_to_call( apfl_parser_ptr, struct fragment_list *, struct apfl_position position, struct apfl_expr * ); static bool parse_parens_head( apfl_parser_ptr p, struct fragment_list *children, struct apfl_position position ) { switch (parse_fragment_into_list(p, children, true, FFLAG_NO_EXPAND)) { case PF_OK: return true; case PF_EOF: p->error = err_unexpected_eof_after(APFL_TOK_LPAREN, position); return false; case PF_CANT_HANDLE: read_token_after_cant_handle(p); p->error = ERR_UNEXPECTED_TOKEN(p->token); return false; case PF_ERROR: return false; } assert(false); } static bool parse_parens_tail(apfl_parser_ptr p, struct fragment_list *children, struct apfl_position position) { for (;;) { switch (parse_fragment_into_list(p, children, true, 0)) { case PF_OK: break; case PF_EOF: p->error = err_unexpected_eof_after(APFL_TOK_LPAREN, position); return false; case PF_CANT_HANDLE: read_token_after_cant_handle(p); if (p->token.type == APFL_TOK_RPAREN) { return true; } else if (p->token.type == APFL_TOK_LINEBREAK) { // Ignore linebreaks inside (...) break; } else { p->error = ERR_UNEXPECTED_TOKEN(p->token); return false; } case PF_ERROR: return false; } } } static bool parse_parens(apfl_parser_ptr p, struct fragment *out, struct apfl_position position) { struct fragment_list children; apfl_resizable_init(APFL_RESIZABLE_ARGS(children, children)); if (!parse_parens_head(p, &children, position)) { goto error; } if (!parse_parens_tail(p, &children, position)) { goto error; } out->type = FRAG_EXPR; out->position = position; if (!fragments_to_call(p, &children, position, &out->expr)) { goto error; } return true; error: fragment_list_deinit(&children); return false; } static bool skip_inner_bracket_separators(apfl_parser_ptr p) { for (;;) { switch (read_token(p, true)) { case APFL_PARSE_OK: if ( p->token.type == APFL_TOK_COMMA || p->token.type == APFL_TOK_LINEBREAK || p->token.type == APFL_TOK_SEMICOLON ) { break; // Note: breaks switch, continues loop } unread_token(p); return true; case APFL_PARSE_EOF: return true; case APFL_PARSE_ERROR: return false; } } } static bool parse_empty_dict(apfl_parser_ptr p, struct fragment *out, struct apfl_position position) { // We already got `[ ->`, we now read another (non separator) token and return success, if it's an `]`. // Else it's a syntax error if (!skip_inner_bracket_separators(p)) { return false; } switch (read_token(p, true)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: p->error = apfl_error_simple(APFL_ERR_UNEXPECTED_EOF); return false; case APFL_PARSE_ERROR: return false; } if (p->token.type != APFL_TOK_RBRACKET) { p->error = ERR_UNEXPECTED_TOKEN(p->token); return false; } out->type = FRAG_EXPR; out->expr = (struct apfl_expr) { .type = APFL_EXPR_DICT, .dict.items = NULL, .dict.len = 0, .position = position, }; out->position = position; return true; } // Must only be called after PF_CANT_HANDLE static bool parse_empty_list_or_dict(apfl_parser_ptr p, struct fragment *out, struct apfl_position position) { read_token_after_cant_handle(p); switch (p->token.type) { case APFL_TOK_RBRACKET: out->type = FRAG_LIST; out->list = (struct fragment_list) { .children = NULL, .len = 0, }; out->position = position; return true; case APFL_TOK_MAPSTO: return parse_empty_dict(p, out, position); default: p->error = ERR_UNEXPECTED_TOKEN(p->token); return false; } } static bool fragment_to_list_item( apfl_parser_ptr, struct fragment, struct apfl_expr_list_item * ); static struct apfl_expr *fragment_to_expr_allocated(apfl_parser_ptr, struct fragment); static bool fragment_to_expr(apfl_parser_ptr p, struct fragment fragment, struct apfl_expr *out) { switch (fragment.type) { case FRAG_EXPAND: p->error = err_unexpected_token(APFL_TOK_EXPAND, fragment.position); return false; case FRAG_CONSTANT: out->type = APFL_EXPR_CONSTANT; out->constant = fragment.constant; out->position = fragment.position; return true; case FRAG_NAME: out->type = APFL_EXPR_VAR; out->var = apfl_string_move(&fragment.name); out->position = fragment.position; return true; case FRAG_DOT: out->type = APFL_EXPR_DOT; if ((out->dot.lhs = fragment_to_expr_allocated(p, fragment_move(fragment.dot.lhs))) == NULL) { return false; } out->dot.rhs = apfl_string_move(&fragment.dot.rhs); out->position = fragment.position; return true; case FRAG_AT: out->type = APFL_EXPR_AT; if ((out->at.lhs = fragment_to_expr_allocated(p, fragment_move(fragment.at.lhs))) == NULL) { return false; } if ((out->at.rhs = fragment_to_expr_allocated(p, fragment_move(fragment.at.rhs))) == NULL) { return false; } out->position = fragment.position; return true; case FRAG_PREDICATE: p->error = err_unexpected_token(APFL_TOK_QUESTION_MARK, fragment.position); return false; case FRAG_EXPR: *out = apfl_expr_move(&fragment.expr); return true; case FRAG_LIST: out->type = APFL_EXPR_LIST; out->position = fragment.position; out->list.len = 0; if (fragment.list.len == 0) { out->list.items = NULL; return true; } if ((out->list.items = ALLOC_LIST(struct apfl_expr_list_item, fragment.list.len)) == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); fragment_deinit(&fragment); return false; } for (size_t i = 0; i < fragment.list.len; i++) { if (!fragment_to_list_item(p, fragment_move(&fragment.list.children[i]), &out->list.items[i])) { fragment_deinit(&fragment); return false; } out->list.len++; } return true; } assert(false); } static struct apfl_expr * fragment_to_expr_allocated(apfl_parser_ptr p, struct fragment fragment) { struct apfl_expr *out = malloc(sizeof(struct apfl_expr)); if (out == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return NULL; } if (!fragment_to_expr(p, fragment, out)) { free(out); return NULL; } return out; } static bool parse_dict( apfl_parser_ptr p, struct fragment *out, struct fragment key, struct apfl_position mapsto_pos, struct apfl_position start ) { struct fragment value; bool cleanup_key = true; bool cleanup_value = false; struct apfl_expr_dict dict = { .items = NULL, .len = 0, }; size_t dict_cap = 0; goto after_mapsto; for (;;) { if (!skip_inner_bracket_separators(p)) { goto error; } switch (parse_fragment(p, &key, true, FFLAG_NO_EXPAND)) { case PF_OK: cleanup_key = true; break; case PF_EOF: p->error = err_unexpected_eof_after(APFL_TOK_LBRACKET, start); goto error; case PF_CANT_HANDLE: goto maybe_end; case PF_ERROR: goto error; } if (!skip_inner_bracket_separators(p)) { goto error; } switch (read_token(p, true)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: p->error = err_unexpected_eof_after(APFL_TOK_LBRACKET, start); goto error; case APFL_PARSE_ERROR: goto error; } if (p->token.type != APFL_TOK_MAPSTO) { unread_token(p); goto error; } mapsto_pos = p->token.position; after_mapsto: if (!skip_inner_bracket_separators(p)) { goto error; } struct fragment value; switch (parse_fragment(p, &value, true, FFLAG_NO_EXPAND)) { case PF_OK: cleanup_value = true; break; case PF_EOF: p->error = err_unexpected_eof_after(APFL_TOK_MAPSTO, mapsto_pos); goto error; case PF_CANT_HANDLE: read_token_after_cant_handle(p); p->error = ERR_UNEXPECTED_TOKEN(p->token); goto error; case PF_ERROR: goto error; } struct apfl_expr_dict_pair pair; if ( (pair.k = fragment_to_expr_allocated(p, fragment_move(&key))) == NULL || (pair.v = fragment_to_expr_allocated(p, fragment_move(&value))) == NULL ) { goto error; } fragment_deinit(&key); cleanup_key = false; fragment_deinit(&value); cleanup_value = false; if (!apfl_resizable_append( sizeof(struct apfl_expr_dict_pair), (void **)&dict.items, &dict.len, &dict_cap, &pair, 1 )) { // \mystuff\TODO:destroy pair! p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return false; } } maybe_end: assert(!cleanup_key && !cleanup_value); read_token_after_cant_handle(p); if (p->token.type != APFL_TOK_RBRACKET) { p->error = ERR_UNEXPECTED_TOKEN(p->token); goto error; } out->type = FRAG_EXPR, out->expr = (struct apfl_expr) { .type = APFL_EXPR_DICT, .dict = dict, .position = start, }; out->position = start; return true; error: if (cleanup_key) { fragment_deinit(&key); } if (cleanup_value) { fragment_deinit(&value); } free(dict.items); // \mystuff\TODO:also destroy all items! return false; } static bool parse_list( apfl_parser_ptr p, struct fragment *out, struct fragment first, struct apfl_position start ) { struct fragment_list list; apfl_resizable_init(APFL_RESIZABLE_ARGS(list, children)); if (!append_fragment(&list, first)) { fragment_deinit(&first); p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return false; } for (;;) { switch (parse_fragment_into_list(p, &list, true, 0)) { case PF_OK: break; case PF_EOF: p->error = err_unexpected_eof_after(APFL_TOK_LBRACKET, start); goto error; case PF_CANT_HANDLE: goto maybe_end; case PF_ERROR: goto error; } if (!skip_inner_bracket_separators(p)) { goto error; } } maybe_end: read_token_after_cant_handle(p); if (p->token.type != APFL_TOK_RBRACKET) { p->error = ERR_UNEXPECTED_TOKEN(p->token); goto error; } out->type = FRAG_LIST; out->list = list; out->position = start; return true; error: // \mystuff\TODO:clean up list return false; } static bool parse_brackets(apfl_parser_ptr p, struct fragment *out, struct apfl_position start) { if (!skip_inner_bracket_separators(p)) { return false; } struct fragment first; switch (parse_fragment(p, &first, true, 0)) { case PF_OK: break; case PF_CANT_HANDLE: return parse_empty_list_or_dict(p, out, start); case PF_EOF: p->error = err_unexpected_eof_after(APFL_TOK_LBRACKET, start); return false; case PF_ERROR: return false; } if (!skip_inner_bracket_separators(p)) { goto error; } switch (read_token(p, true)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: p->error = err_unexpected_eof_after(APFL_TOK_LBRACKET, start); goto error; case APFL_PARSE_ERROR: goto error; } if (p->token.type == APFL_TOK_MAPSTO) { struct apfl_position mapsto_pos = p->token.position; return parse_dict(p, out, first, mapsto_pos, start); } else { unread_token(p); return parse_list(p, out, first, start); } error: fragment_deinit(&first); return false; } static bool parse_expand(apfl_parser_ptr p, struct fragment *fragment, struct apfl_position position) { struct fragment *inner = malloc(sizeof(struct fragment)); if (inner == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return false; } enum parse_fragment_result result = parse_fragment(p, inner, true, FFLAG_NO_EXPAND); if (result == PF_OK) { fragment->type = FRAG_EXPAND; fragment->expand = inner; fragment->position = position; return true; } free(inner); switch (result) { case PF_OK: assert(false); // Already handled above break; case PF_CANT_HANDLE: p->error = ERR_UNEXPECTED_TOKEN(p->token); return false; case PF_EOF: p->error = err_unexpected_eof_after(APFL_TOK_EXPAND, position); return false; case PF_ERROR: return false; } assert(false); } static bool must_read_token_after(apfl_parser_ptr p, enum apfl_token_type want_type) { enum apfl_token_type cur_type = p->token.type; struct apfl_position cur_pos = p->token.position; switch (read_token(p, true)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: p->error = err_unexpected_eof_after(cur_type, cur_pos); return false; case APFL_PARSE_ERROR: return false; } if (p->token.type != want_type) { p->error = ERR_UNEXPECTED_TOKEN(p->token); return false; } return true; } static bool parse_stringify(apfl_parser_ptr p, struct fragment *fragment, struct apfl_position position) { if (!must_read_token_after(p, APFL_TOK_NAME)) { return false; } fragment->type = FRAG_CONSTANT; fragment->constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_STRING, .string = apfl_string_move(&p->token.text), }; fragment->position = position; return true; } static bool fragment_to_param_recursive(apfl_parser_ptr, struct fragment *, struct apfl_expr_param *); static bool fragments_to_params(apfl_parser_ptr, struct fragment_list *, struct apfl_expr_params *); static bool predicate_fragment_to_param( apfl_parser_ptr p, struct fragment_lhs_rhs *lhs_rhs, struct apfl_expr_param *out ) { out->type = APFL_EXPR_PARAM_PREDICATE; out->predicate.lhs = NULL; out->predicate.rhs = NULL; if ((out->predicate.lhs = malloc(sizeof(struct apfl_expr_param))) == NULL) { goto error; } if (!fragment_to_param_recursive(p, lhs_rhs->lhs, out->predicate.lhs)) { free(out->predicate.lhs); out->predicate.lhs = NULL; goto error; } out->predicate.rhs = fragment_to_expr_allocated(p, fragment_move(lhs_rhs->rhs)); if (out->predicate.rhs == NULL) { goto error; } return true; error: apfl_expr_param_deinit(out); return false; } static bool fragment_to_param_recursive( apfl_parser_ptr p, struct fragment *fragment, struct apfl_expr_param *out ) { switch (fragment->type) { case FRAG_EXPAND: p->error = err_unexpected_token(APFL_TOK_EXPAND, fragment->position); return false; case FRAG_CONSTANT: out->type = APFL_EXPR_PARAM_CONSTANT; out->constant = apfl_expr_const_move(&fragment->constant); return true; case FRAG_NAME: out->type = APFL_EXPR_PARAM_VAR; out->var = apfl_string_move(&fragment->name); return true; case FRAG_DOT: p->error = err_unexpected_token(APFL_TOK_DOT, fragment->position); return false; case FRAG_AT: p->error = err_unexpected_token(APFL_TOK_AT, fragment->position); return false; case FRAG_PREDICATE: return predicate_fragment_to_param(p, &fragment->predicate, out); case FRAG_EXPR: p->error = (struct apfl_error) { .type = APFL_ERR_UNEXPECTED_EXPRESSION, .position = fragment->position, }; return false; case FRAG_LIST: out->type = APFL_EXPR_PARAM_LIST; return fragments_to_params(p, &fragment->list, &out->list); } assert(false); } static bool fragment_to_param( apfl_parser_ptr p, struct fragment *fragment, struct apfl_expr_param *out, bool *seen_expand ) { if (fragment->type == FRAG_EXPAND && !*seen_expand) { *seen_expand = true; // This prevents a param list with more than one ~ 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); } return fragment_to_param_recursive(p, fragment, out); } static bool fragments_to_params( apfl_parser_ptr p, 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: fragment_list_deinit(fragments); return result; } static bool fragment_to_assignable( 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; case FRAG_CONSTANT: out->type = APFL_EXPR_ASSIGNABLE_CONSTANT; 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, expand_ok, 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); goto error; } if (!fragment_to_assignable( p, expand_ok, 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; } return true; case FRAG_PREDICATE: out->type = APFL_EXPR_ASSIGNABLE_PREDICATE; out->predicate.lhs = malloc(sizeof(struct apfl_expr_assignable)); out->predicate.rhs = malloc(sizeof(struct apfl_expr)); if (out->predicate.lhs == NULL || out->predicate.rhs == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } if (!fragment_to_assignable( p, expand_ok, fragment_move(fragment.at.lhs), out->predicate.lhs )) { free(out->predicate.rhs); out->predicate.rhs = NULL; goto error; } if (!fragment_to_expr( p, fragment_move(fragment.at.rhs), out->predicate.rhs )) { goto error; } return true; case FRAG_EXPR: p->error = (struct apfl_error) { .type = APFL_ERR_INVALID_ASSIGNMENT_LHS, .position = fragment.position, }; goto error; 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); expand_ok = true; for (size_t i = 0; i < fragment.list.len; i++) { struct apfl_expr_assignable *cur = &out->list.children[i]; if (!fragment_to_assignable( p, expand_ok, fragment_move(&fragment.list.children[i]), cur )) { goto error; } expand_ok = expand_ok && cur->type != APFL_EXPR_ASSIGNABLE_EXPAND; out->list.len++; } return true; } assert(false); // Should not be reached error: apfl_expr_assignable_deinit(out); return false; } struct partial_assignment { struct apfl_expr_assignable lhs; bool local; struct apfl_position position; }; static void partial_assignment_deinit(struct partial_assignment *pa) { apfl_expr_assignable_deinit(&pa->lhs); } struct partial_assignment_list { APFL_RESIZABLE_TRAIT(struct partial_assignment, items) }; enum parse_body_or_toplevel_finalize_result { BODY_FINALIZE_ERROR, BODY_FINALIZE_OK, BODY_FINALIZE_EMPTY, }; static bool fragment_to_list_item( apfl_parser_ptr p, struct fragment fragment, struct apfl_expr_list_item *out ) { if (fragment.type == FRAG_EXPAND) { out->expand = true; out->expr = fragment_to_expr_allocated(p, fragment_move(fragment.expand)); fragment_deinit(&fragment); return out->expr != NULL; } else { out->expand = false; out->expr = fragment_to_expr_allocated(p, fragment_move(&fragment)); return out->expr != NULL; } } static bool fragments_to_call( apfl_parser_ptr p, struct fragment_list *fragments, // \mystuff\TODO:really a pointer? Why? struct apfl_position position, struct apfl_expr *out ) { assert(fragments->len > 0); // \mystuff\TODO: Or should we check this here? out->type = APFL_EXPR_CALL; out->position = position; out->call.arguments = (struct apfl_expr_list) { .items = NULL, .len = 0, }; out->call.callee = fragment_to_expr_allocated(p, fragment_move(&fragments->children[0])); if (out->call.callee == NULL) { goto error; } if (fragments->len == 1) { return true; } out->call.arguments.items = ALLOC_LIST(struct apfl_expr_list_item, (fragments->len - 1)); for (size_t i = 1; i < fragments->len; i++) { if (!fragment_to_list_item(p, fragment_move(&fragments->children[i]), &out->call.arguments.items[i-1])) { goto error; } out->call.arguments.len++; } return true; error: apfl_expr_deinit(out); return false; } static enum parse_body_or_toplevel_finalize_result parse_body_or_toplevel_finalize( apfl_parser_ptr p, struct fragment_list *fragments, struct partial_assignment_list partial_assignments, struct apfl_expr *out ) { if (fragments->len == 0) { if (partial_assignments.len > 0) { p->error = (struct apfl_error) { .type = APFL_ERR_EMPTY_ASSIGNMENT, .position = partial_assignments.items[partial_assignments.len-1].position, }; goto error; } return BODY_FINALIZE_EMPTY; } struct apfl_expr *dest = out; for (size_t i = 0; i < partial_assignments.len; i++) { struct partial_assignment *cur = &partial_assignments.items[i]; dest->type = APFL_EXPR_ASSIGNMENT; dest->position = cur->position; dest->assignment.local = cur->local; dest->assignment.lhs = apfl_expr_assignable_move(&cur->lhs); if ((dest->assignment.rhs = ALLOC(struct apfl_expr)) == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } dest = dest->assignment.rhs; } if (fragments->len == 1) { if (!fragment_to_expr(p, fragment_move(&fragments->children[0]), dest)) { goto error; } } else { if (!fragments_to_call(p, fragments, fragments->children[0].position, dest)) { goto error; } } fragment_list_deinit(fragments); DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit); return BODY_FINALIZE_OK; error: DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit); apfl_expr_deinit(out); return BODY_FINALIZE_ERROR; } static struct partial_assignment_list partial_assignment_list_move(struct partial_assignment_list *in) { struct partial_assignment_list out = *in; apfl_resizable_init(APFL_RESIZABLE_ARGS(*in, items)); return out; } static enum parse_fragment_result parse_body_or_toplevel( apfl_parser_ptr p, bool handle_eof, struct fragment_list *fragments, struct apfl_expr *out, bool need ) { struct partial_assignment_list partial_assignments; apfl_resizable_init(APFL_RESIZABLE_ARGS(partial_assignments, items)); bool first; first = true; for (;;) { for (;;) { switch (parse_fragment_into_list(p, fragments, need || !first, 0)) { case PF_OK: break; case PF_CANT_HANDLE: goto break_inner; case PF_EOF: if (handle_eof) { switch (parse_body_or_toplevel_finalize( p, fragments, partial_assignment_list_move(&partial_assignments), out )) { case BODY_FINALIZE_OK: return PF_OK; case BODY_FINALIZE_ERROR: goto error; case BODY_FINALIZE_EMPTY: return PF_EOF; } } else { return PF_EOF; } case PF_ERROR: goto error; } first = false; } break_inner: read_token_after_cant_handle(p); bool is_rbrace = false; switch (p->token.type) { case APFL_TOK_ASSIGN: case APFL_TOK_LOCAL_ASSIGN: bool local = p->token.type == APFL_TOK_LOCAL_ASSIGN; struct apfl_position position = p->token.position; if (fragments->len == 0) { p->error = (struct apfl_error) { .type = APFL_ERR_EMPTY_ASSIGNMENT, .position = position, }; goto error; } if (fragments->len > 1) { p->error = err_unexpected_token( local ? APFL_TOK_LOCAL_ASSIGN : APFL_TOK_ASSIGN, position ); goto error; } if (!apfl_resizable_grow_cap( sizeof(struct partial_assignment), APFL_RESIZABLE_ARGS(partial_assignments, items), 1 )) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } struct partial_assignment *cur_partial = &partial_assignments.items[partial_assignments.len]; cur_partial->local = local; cur_partial->position = position; 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)) { goto error; } partial_assignments.len++; break; case APFL_TOK_RBRACE: is_rbrace = true; unread_token(p); // fallthrough case APFL_TOK_LINEBREAK: case APFL_TOK_SEMICOLON: switch (parse_body_or_toplevel_finalize( p, fragments, partial_assignment_list_move(&partial_assignments), out )) { case BODY_FINALIZE_OK: return PF_OK; case BODY_FINALIZE_ERROR: goto error; case BODY_FINALIZE_EMPTY: if (is_rbrace) { return PF_CANT_HANDLE; } // If there was nothing to finalize, we have an empty expression // that doesn't need to end up in the AST. So let's just // continue with the outermost loop and try again. break; default: assert(false); } break; default: if (partial_assignments.len > 0) { p->error = ERR_UNEXPECTED_TOKEN(p->token); goto error; } unread_token(p); return PF_CANT_HANDLE; } } error: // \mystuff\TODO:also on other non ok results??? DEINIT_LIST(partial_assignments.items, partial_assignments.len, partial_assignment_deinit); return PF_ERROR; } static bool parse_braces( apfl_parser_ptr p, struct fragment *out, struct apfl_position start ) { struct apfl_expr_subfunc *subfuncs = NULL; size_t subfuncs_len = 0; size_t subfuncs_cap = 0; bool has_params = false; struct apfl_expr_params params = { .params = NULL, .len = 0 }; struct fragment_list fragments; apfl_resizable_init(APFL_RESIZABLE_ARGS(fragments, children)); struct apfl_expr_body body = { .items = NULL, .len = 0, }; size_t body_cap = 0; for (;;) { struct apfl_expr expr; switch (parse_body_or_toplevel( p, false, &fragments, &expr, true )) { case PF_OK: if (!apfl_resizable_append( sizeof(struct apfl_expr), (void **)&body.items, &body.len, &body_cap, &expr, 1 )) { apfl_expr_deinit(&expr); p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } break; case PF_EOF: p->error = apfl_error_simple(APFL_ERR_UNEXPECTED_EOF); goto error; case PF_ERROR: goto error; case PF_CANT_HANDLE: read_token_after_cant_handle(p); switch (p->token.type) { case APFL_TOK_RBRACE: // TODO: Rather fulgly duplication if (has_params) { // Finalize previous subfunc and append if (!apfl_resizable_append( sizeof(struct apfl_expr_subfunc), (void **)&subfuncs, &subfuncs_len, &subfuncs_cap, &(struct apfl_expr_subfunc) { .params = params, .body = body, }, 1 )) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } params = (struct apfl_expr_params) { .params = NULL, .len = 0 }; body = (struct apfl_expr_body) { .items = NULL, .len = 0 }; body_cap = 0; } out->type = FRAG_EXPR; if (has_params) { out->expr = (struct apfl_expr) { .type = APFL_EXPR_COMPLEX_FUNC, .complex_func.subfuncs = subfuncs, .complex_func.len = subfuncs_len, .position = start, }; subfuncs = NULL; subfuncs_len = 0; subfuncs_cap = 0; } else { out->expr = (struct apfl_expr) { .type = APFL_EXPR_SIMPLE_FUNC, .simple_func = apfl_expr_body_move(&body), .position = start, }; body_cap = 0; } DEINIT_LIST(fragments.children, fragments.len, fragment_deinit); return true; case APFL_TOK_MAPSTO: if (body.len > 0 && !has_params) { p->error = (struct apfl_error) { .type = APFL_ERR_STATEMENTS_BEFORE_PARAMETERS, .position = p->token.position, }; goto error; } if (has_params) { // Finalize previous subfunc and append if (!apfl_resizable_append( sizeof(struct apfl_expr_subfunc), (void **)&subfuncs, &subfuncs_len, &subfuncs_cap, &(struct apfl_expr_subfunc) { .params = params, .body = body, }, 1 )) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); goto error; } params = (struct apfl_expr_params) { .params = NULL, .len = 0 }; body = (struct apfl_expr_body) { .items = NULL, .len = 0 }; body_cap = 0; } if (!fragments_to_params(p, &fragments, ¶ms)) { goto error; } has_params = true; break; default: p->error = ERR_UNEXPECTED_TOKEN(p->token); goto error; } break; } } error: // \mystuff\TODO:cleanup return false; } static enum parse_fragment_result parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum parse_fragment_flags flags) { struct fragment *lhs = NULL; struct fragment *rhs = NULL; switch (read_token(p, need)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: return PF_EOF; case APFL_PARSE_ERROR: return PF_ERROR; } switch (p->token.type) { case APFL_TOK_LPAREN: if (!parse_parens(p, fragment, p->token.position)) { goto error; } break; case APFL_TOK_LBRACKET: if (!parse_brackets(p, fragment, p->token.position)) { goto error; } break; case APFL_TOK_LBRACE: if (!parse_braces(p, fragment, p->token.position)) { goto error; } break; case APFL_TOK_EXPAND: if (flags & FFLAG_NO_EXPAND) { unread_token(p); return PF_CANT_HANDLE; } if (!parse_expand(p, fragment, p->token.position)) { goto error; } break; case APFL_TOK_STRINGIFY: if (!parse_stringify(p, fragment, p->token.position)) { goto error; } break; case APFL_TOK_NUMBER: fragment->type = FRAG_CONSTANT; fragment->constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_NUMBER, .number = p->token.number, }; fragment->position = p->token.position; break; case APFL_TOK_NAME: if (apfl_string_cmp(p->token.text, "nil") == 0) { fragment->type = FRAG_CONSTANT; fragment->constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_NIL, }; } else if (apfl_string_cmp(p->token.text, "true") == 0) { fragment->type = FRAG_CONSTANT; fragment->constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_BOOLEAN, .boolean = true, }; } else if (apfl_string_cmp(p->token.text, "false") == 0) { fragment->type = FRAG_CONSTANT; fragment->constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_BOOLEAN, .boolean = false, }; } else { fragment->type = FRAG_NAME; fragment->name = apfl_string_move(&p->token.text); } fragment->position = p->token.position; break; case APFL_TOK_STRING: fragment->type = FRAG_CONSTANT; fragment->constant = (struct apfl_expr_const) { .type = APFL_EXPR_CONST_STRING, .string = apfl_string_move(&p->token.text), }; fragment->position = p->token.position; break; default: unread_token(p); return PF_CANT_HANDLE; } for (; !(flags & FFLAG_NO_POSTFIXS); ) { switch (read_token(p, need)) { case APFL_PARSE_OK: break; case APFL_PARSE_EOF: return PF_OK; case APFL_PARSE_ERROR: fragment_deinit(fragment); return PF_ERROR; } enum apfl_token_type token_type = p->token.type; struct apfl_position token_pos = p->token.position; switch (p->token.type) { case APFL_TOK_DOT: if (!must_read_token_after(p, APFL_TOK_NAME)) { return PF_ERROR; } if ((lhs = ALLOC(struct fragment)) == NULL) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return PF_ERROR; } *lhs = *fragment; fragment->type = FRAG_DOT; fragment->position = token_pos; MOVEPTR(fragment->dot.lhs, lhs); fragment->dot.rhs = apfl_string_move(&p->token.text); break; case APFL_TOK_AT: case APFL_TOK_QUESTION_MARK: if ( ((lhs = ALLOC(struct fragment)) == NULL) || ((rhs = ALLOC(struct fragment)) == NULL) ) { p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED); return PF_ERROR; } switch (parse_fragment(p, rhs, true, FFLAG_NO_POSTFIXS | FFLAG_NO_EXPAND)) { case PF_OK: break; case PF_ERROR: goto error; case PF_EOF: p->error = err_unexpected_eof_after(token_type, token_pos); goto error; case PF_CANT_HANDLE: read_token_after_cant_handle(p); p->error = ERR_UNEXPECTED_TOKEN(p->token); goto error; } *lhs = *fragment; if (token_type == APFL_TOK_AT) { fragment->type = FRAG_AT; fragment->position = token_pos; fragment->at.lhs = lhs; fragment->at.rhs = rhs; } else { assert(token_type == APFL_TOK_QUESTION_MARK); fragment->type = FRAG_PREDICATE; fragment->position = token_pos; fragment->at.lhs = lhs; fragment->at.rhs = rhs; } break; default: unread_token(p); return PF_OK; } } return PF_OK; error: DESTROY(lhs, fragment_deinit); DESTROY(rhs, fragment_deinit); fragment_deinit(fragment); return PF_ERROR; } enum apfl_parse_result apfl_parser_next(apfl_parser_ptr p) { if (p->has_expr) { apfl_expr_deinit(&p->expr); } struct fragment_list fragments; // TODO: Clean me up! apfl_resizable_init(APFL_RESIZABLE_ARGS(fragments, children)); switch (parse_body_or_toplevel( p, true, &fragments, &p->expr, false )) { case PF_OK: p->has_expr = true; return APFL_PARSE_OK; case PF_ERROR: return APFL_PARSE_ERROR; case PF_EOF: return APFL_PARSE_EOF; case PF_CANT_HANDLE: read_token_after_cant_handle(p); p->error = ERR_UNEXPECTED_TOKEN(p->token); return APFL_PARSE_ERROR; } assert(false); } struct apfl_error apfl_parser_get_error(apfl_parser_ptr p) { return p->error; } struct apfl_expr apfl_parser_get_expr(apfl_parser_ptr p) { p->has_expr = false; return p->expr; } void apfl_parser_destroy(apfl_parser_ptr p) { if (p == NULL) { return; } if (p->has_expr) { apfl_expr_deinit(&p->expr); } if (p->has_token) { apfl_token_deinit(&p->token); } free(p); }