1955 lines
51 KiB
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, ¶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;
|
|
}
|
|
|
|
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);
|
|
}
|