apfl/src/parser.c

1955 lines
51 KiB
C

#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#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, &params)) {
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;
}
enum parse_fragment_result result = PF_OK; // \mystuff\TODO:i think we can get rid of this var?
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;
result = PF_OK;
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;
result = PF_OK;
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;
result = PF_OK;
break;
default:
unread_token(p);
return PF_CANT_HANDLE;
}
if (result != PF_OK) {
return result;
}
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);
}