2021-12-10 20:22:16 +00:00
|
|
|
#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;
|
|
|
|
|
|
|
|
|
|
struct apfl_error error;
|
|
|
|
|
|
|
|
|
|
bool eof;
|
2021-12-10 21:13:39 +00:00
|
|
|
bool has_token;
|
2021-12-10 20:22:16 +00:00
|
|
|
bool has_unread;
|
2021-12-10 21:13:39 +00:00
|
|
|
struct apfl_token token;
|
2021-12-10 20:22:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum parse_fragment_result {
|
|
|
|
|
PF_OK,
|
|
|
|
|
PF_CANT_HANDLE,
|
|
|
|
|
PF_EOF,
|
|
|
|
|
PF_ERROR,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum parse_fragment_flags {
|
|
|
|
|
FFLAG_NO_EXPAND = 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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;
|
|
|
|
|
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->fragment);
|
|
|
|
|
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;
|
2021-12-10 21:13:39 +00:00
|
|
|
p->has_token = false;
|
2021-12-10 20:22:16 +00:00
|
|
|
p->has_unread = 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
|
2021-12-10 21:13:39 +00:00
|
|
|
read_token(apfl_parser_ptr p, bool need)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
|
|
|
|
if (p->eof) {
|
|
|
|
|
return APFL_PARSE_EOF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p->has_unread) {
|
|
|
|
|
p->has_unread = false;
|
|
|
|
|
return APFL_PARSE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
|
|
|
|
|
if (p->has_token) {
|
|
|
|
|
apfl_token_deinit(&p->token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum apfl_parse_result result = get_preprocessed_token(p, &p->token, need);
|
2021-12-10 20:22:16 +00:00
|
|
|
p->eof = result == APFL_PARSE_EOF;
|
2021-12-10 21:13:39 +00:00
|
|
|
p->has_token = result == APFL_PARSE_OK;
|
2021-12-10 20:22:16 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2021-12-10 21:13:39 +00:00
|
|
|
unread_token(apfl_parser_ptr p)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2021-12-10 21:13:39 +00:00
|
|
|
assert(!p->eof);
|
|
|
|
|
assert(p->has_token);
|
|
|
|
|
assert(!p->has_unread);
|
2021-12-10 20:22:16 +00:00
|
|
|
p->has_unread = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Must only be called after an PF_CANT_HANDLE!
|
2021-12-10 21:13:39 +00:00
|
|
|
static void
|
2021-12-10 20:22:16 +00:00
|
|
|
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.
|
2021-12-10 21:13:39 +00:00
|
|
|
assert(read_token(p, true) == APFL_PARSE_OK);
|
2021-12-10 20:22:16 +00:00
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
return &p->token;
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
|
|
|
|
// Must only be called after an PF_CANT_HANDLE!
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
unexpected_cant_handle(apfl_parser_ptr p)
|
|
|
|
|
{
|
2021-12-10 21:13:39 +00:00
|
|
|
p->error = ERR_UNEXPECTED_TOKEN(p->token);
|
2021-12-10 20:22:16 +00:00
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 enum parse_fragment_result
|
|
|
|
|
parse_parens_head(apfl_parser_ptr p, struct fragment_list *children, struct apfl_position position)
|
|
|
|
|
{
|
|
|
|
|
return parse_fragment_into_list(p, children, true, FFLAG_NO_EXPAND); // \mystuff\TODO:more flags?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
parse_parens_tail(apfl_parser_ptr p, struct fragment_list *children, struct apfl_position position)
|
|
|
|
|
{
|
|
|
|
|
enum parse_fragment_result result;
|
|
|
|
|
for (;;) {
|
|
|
|
|
result = parse_fragment_into_list(p, children, true, 0); // \mystuff\TODO:more flags?
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
|
case PF_OK:
|
|
|
|
|
assert(false); // already handled
|
|
|
|
|
case PF_EOF:
|
|
|
|
|
// \mystuff\TODO:unexpected eof
|
|
|
|
|
case PF_CANT_HANDLE:
|
|
|
|
|
break;
|
|
|
|
|
case PF_ERROR:
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(result == PF_CANT_HANDLE);
|
2021-12-10 21:13:39 +00:00
|
|
|
read_token_after_cant_handle(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
if (p->token.type == APFL_TOK_RPAREN) {
|
2021-12-10 20:22:16 +00:00
|
|
|
result = PF_OK;
|
|
|
|
|
// \mystuff\TODO:finalize list somehow?
|
|
|
|
|
} else {
|
|
|
|
|
p->error = (struct apfl_error) {
|
|
|
|
|
// \mystuff\TODO:unexpected token error
|
|
|
|
|
};
|
|
|
|
|
result = PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
enum parse_fragment_result result;
|
|
|
|
|
|
|
|
|
|
result = parse_parens_head(p, &children, position);
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = parse_parens_tail(p, &children, position);
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out->type = FRAG_EXPR;
|
|
|
|
|
out->expr = TODO(); // \mystuff\TODO:
|
|
|
|
|
out->position = position;
|
|
|
|
|
|
|
|
|
|
return PF_OK;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
// \mystuff\TODO:cleanup
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
skip_inner_bracket_separators(apfl_parser_ptr p)
|
|
|
|
|
{
|
|
|
|
|
for (;;) {
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (read_token(p, true)) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_PARSE_OK:
|
2021-12-10 21:13:39 +00:00
|
|
|
if (
|
|
|
|
|
p->token.type == APFL_TOK_COMMA
|
|
|
|
|
|| p->token.type == APFL_TOK_LINEBREAK
|
|
|
|
|
|| p->token.type == APFL_TOK_SEMICOLON
|
|
|
|
|
) {
|
2021-12-10 20:22:16 +00:00
|
|
|
break; // Note: breaks switch, continues loop
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
unread_token(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
return PF_OK;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
return PF_OK;
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
enum parse_fragment_result result;
|
|
|
|
|
result = skip_inner_bracket_separators(p);
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (read_token(p, true)) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
p->error = apfl_error_simple(APFL_ERR_UNEXPECTED_EOF);
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
if (p->token.type != APFL_TOK_RBRACKET) {
|
|
|
|
|
p->error = ERR_UNEXPECTED_TOKEN(p->token);
|
2021-12-10 20:22:16 +00:00
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out->type = FRAG_EMPTY_DICT;
|
|
|
|
|
out->position = position;
|
|
|
|
|
return PF_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
// Must only be called after PF_CANT_HANDLE
|
2021-12-10 20:22:16 +00:00
|
|
|
static enum parse_fragment_result
|
2021-12-10 21:13:39 +00:00
|
|
|
parse_empty_list_or_dict(apfl_parser_ptr p, struct fragment *out, struct apfl_position position)
|
2021-12-10 20:22:16 +00:00
|
|
|
{
|
2021-12-10 21:13:39 +00:00
|
|
|
assert(p->has_token);
|
2021-12-10 20:22:16 +00:00
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (p->token.type) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_TOK_RBRACKET:
|
|
|
|
|
out->type = FRAG_EMPTY_LIST;
|
|
|
|
|
out->position = position;
|
|
|
|
|
return PF_OK;
|
|
|
|
|
case APFL_TOK_MAPSTO:
|
|
|
|
|
return parse_empty_dict(p, out, position);
|
|
|
|
|
default:
|
2021-12-10 21:13:39 +00:00
|
|
|
p->error = ERR_UNEXPECTED_TOKEN(p->token);
|
2021-12-10 20:22:16 +00:00
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
fragment_to_expr(apfl_parser_ptr p, struct fragment fragment, struct apfl_expr *out)
|
|
|
|
|
{
|
|
|
|
|
// \mystuff\TODO:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!fragment_to_expr(p, fragment, out)) {
|
|
|
|
|
free(out);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
enum parse_fragment_result result;
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_dict dict = {
|
|
|
|
|
.items = NULL,
|
|
|
|
|
.len = 0,
|
|
|
|
|
};
|
|
|
|
|
size_t dict_cap = 0;
|
|
|
|
|
|
|
|
|
|
goto after_mapsto;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
result = skip_inner_bracket_separators(p);
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch ((result = 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);
|
|
|
|
|
result = PF_ERROR;
|
|
|
|
|
goto error;
|
|
|
|
|
case PF_CANT_HANDLE:
|
|
|
|
|
goto maybe_end;
|
|
|
|
|
case PF_ERROR:
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = skip_inner_bracket_separators(p);
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (read_token(p, true)) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
p->error = err_unexpected_eof_after(APFL_TOK_LBRACKET, start);
|
|
|
|
|
result = PF_ERROR;
|
|
|
|
|
goto error;
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
result = PF_ERROR;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
if (p->token.type != APFL_TOK_MAPSTO) {
|
|
|
|
|
unread_token(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
mapsto_pos = p->token.position;
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
after_mapsto:
|
|
|
|
|
result = skip_inner_bracket_separators(p);
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct fragment value;
|
|
|
|
|
switch ((result = 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);
|
|
|
|
|
result = PF_ERROR;
|
|
|
|
|
goto error;
|
|
|
|
|
case PF_CANT_HANDLE:
|
|
|
|
|
result = unexpected_cant_handle(p);
|
|
|
|
|
goto error;
|
|
|
|
|
case PF_ERROR:
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr_dict_pair pair;
|
|
|
|
|
if (
|
|
|
|
|
(pair.k = fragment_to_expr_allocated(p, key)) == NULL
|
|
|
|
|
|| (pair.v = fragment_to_expr_allocated(p, value)) == NULL
|
|
|
|
|
) {
|
|
|
|
|
result = PF_ERROR;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fragment_deinit(&key);
|
|
|
|
|
cleanup_key = false;
|
|
|
|
|
fragment_deinit(&value);
|
|
|
|
|
cleanup_value = false;
|
|
|
|
|
|
|
|
|
|
if (!apfl_resizable_append(
|
|
|
|
|
sizeof(struct apfl_expr_dict_pair),
|
|
|
|
|
&dict.items,
|
|
|
|
|
&dict.len,
|
|
|
|
|
&dict_cap,
|
|
|
|
|
&pair,
|
|
|
|
|
1
|
|
|
|
|
)) {
|
|
|
|
|
// \mystuff\TODO:destroy pair!
|
|
|
|
|
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maybe_end:
|
|
|
|
|
assert(!cleanup_key && !cleanup_value);
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
read_token_after_cant_handle(p);
|
|
|
|
|
if (p->token.type != APFL_TOK_RBRACKET) {
|
|
|
|
|
p->error = ERR_UNEXPECTED_TOKEN(p->token);
|
2021-12-10 20:22:16 +00:00
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out->type = FRAG_EXPR,
|
|
|
|
|
out->expr.type = (struct apfl_expr) {
|
|
|
|
|
.type = APFL_EXPR_DICT,
|
|
|
|
|
.dict = dict,
|
|
|
|
|
.position = start,
|
|
|
|
|
};
|
|
|
|
|
out->position = start;
|
|
|
|
|
return PF_OK;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
if (cleanup_key) {
|
|
|
|
|
fragment_deinit(&key);
|
|
|
|
|
}
|
|
|
|
|
if (cleanup_value) {
|
|
|
|
|
fragment_deinit(&value);
|
|
|
|
|
}
|
|
|
|
|
free(dict.items); // \mystuff\TODO:also destroy all items!
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
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 PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maybe_end:
|
2021-12-10 21:13:39 +00:00
|
|
|
read_token_after_cant_handle(p);
|
|
|
|
|
if (p->token.type != APFL_TOK_RBRACKET) {
|
|
|
|
|
p->error = ERR_UNEXPECTED_TOKEN(p->token);
|
2021-12-10 20:22:16 +00:00
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out->type = FRAG_LIST;
|
|
|
|
|
out->list = list;
|
|
|
|
|
out->position = start;
|
|
|
|
|
|
|
|
|
|
return PF_OK;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
// \mystuff\TODO:clean up list
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
parse_brackets(apfl_parser_ptr p, struct fragment *out, struct apfl_position start)
|
|
|
|
|
{
|
|
|
|
|
enum parse_fragment_result result;
|
|
|
|
|
result = skip_inner_bracket_separators(p);
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct fragment first;
|
|
|
|
|
switch (parse_fragment(p, &first, true, 0)) {
|
|
|
|
|
case PF_OK:
|
|
|
|
|
break;
|
|
|
|
|
case PF_CANT_HANDLE:
|
2021-12-10 21:13:39 +00:00
|
|
|
return parse_empty_list_or_dict(p, out, start);
|
2021-12-10 20:22:16 +00:00
|
|
|
case PF_EOF:
|
|
|
|
|
p->error = err_unexpected_eof_after(APFL_TOK_LBRACKET, start);
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
case PF_ERROR:
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = skip_inner_bracket_separators(p);
|
|
|
|
|
if (result != PF_OK) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (read_token(p, true)) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
p->error = err_unexpected_eof_after(APFL_TOK_LBRACKET, start);
|
|
|
|
|
result = PF_ERROR;
|
|
|
|
|
goto error;
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
result = PF_ERROR;
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
if (p->token.type == APFL_TOK_MAPSTO) {
|
|
|
|
|
struct apfl_position mapsto_pos = p->token.position;
|
2021-12-10 20:22:16 +00:00
|
|
|
return parse_dict(p, out, first, mapsto_pos, start);
|
|
|
|
|
} else {
|
2021-12-10 21:13:39 +00:00
|
|
|
unread_token(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
return parse_list(p, out, first, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
fragment_deinit(&first);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
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 PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 PF_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(inner);
|
|
|
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
|
case PF_OK:
|
|
|
|
|
assert(false); // Already handled above
|
|
|
|
|
break;
|
|
|
|
|
case PF_CANT_HANDLE:
|
|
|
|
|
return unexpected_cant_handle(p);
|
|
|
|
|
case PF_EOF:
|
|
|
|
|
p->error = err_unexpected_eof_after(APFL_TOK_EXPAND, position);
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
case PF_ERROR:
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
parse_stringify(apfl_parser_ptr p, struct fragment *fragment, struct apfl_position position)
|
|
|
|
|
{
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (read_token(p, true)) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
p->error = err_unexpected_eof_after(APFL_TOK_STRINGIFY, position);
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
if (p->token.type != APFL_TOK_NAME) {
|
|
|
|
|
p->error = ERR_UNEXPECTED_TOKEN(p->token);
|
2021-12-10 20:22:16 +00:00
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fragment->type = FRAG_CONSTANT;
|
|
|
|
|
fragment->constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_STRING,
|
2021-12-10 21:13:39 +00:00
|
|
|
.string = apfl_string_move(&p->token.text),
|
2021-12-10 20:22:16 +00:00
|
|
|
};
|
|
|
|
|
fragment->position = position;
|
|
|
|
|
return PF_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_move(&fragment->list), &out->list);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
out->params = malloc(sizeof(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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
fragment_to_partial_assignment(
|
|
|
|
|
apfl_parser_ptr p,
|
|
|
|
|
bool local,
|
|
|
|
|
struct fragment fragment,
|
|
|
|
|
struct apfl_expr_assignment *out
|
|
|
|
|
) {
|
|
|
|
|
out->local = local;
|
|
|
|
|
out->rhs = NULL;
|
|
|
|
|
|
|
|
|
|
bool rv = fragment_to_assignable(p, false, fragment, &out->lhs);
|
|
|
|
|
fragment_deinit(&fragment);
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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_expr *out
|
|
|
|
|
) {
|
|
|
|
|
assert(fragments->len > 0); // \mystuff\TODO: Or should we check this here?
|
|
|
|
|
|
|
|
|
|
out->type = APFL_EXPR_CALL;
|
|
|
|
|
out->position = fragments->children[0].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++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 apfl_expr **leftmost_assignment_expr,
|
|
|
|
|
struct apfl_expr *rightmost_assignment_expr,
|
|
|
|
|
struct apfl_expr *out
|
|
|
|
|
) {
|
|
|
|
|
if (fragments->len == 0) {
|
|
|
|
|
if (rightmost_assignment_expr != NULL) {
|
|
|
|
|
p->error = (struct apfl_error) {
|
|
|
|
|
.type = APFL_ERR_EMPTY_ASSIGNMENT,
|
|
|
|
|
.position = rightmost_assignment_expr->position,
|
|
|
|
|
};
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BODY_FINALIZE_EMPTY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Nasty pointer juggling: If there are no assignments, we want to put the
|
|
|
|
|
// expression generated from the fragments to go directly to the output.
|
|
|
|
|
// Otherwise we want to store it in the right-hand-side of the rightmost
|
|
|
|
|
// assignment expression and the leftmost assignment expression becomes the
|
|
|
|
|
// output.
|
|
|
|
|
struct apfl_expr *dest = NULL;
|
|
|
|
|
if (rightmost_assignment_expr == NULL) {
|
|
|
|
|
dest = out;
|
|
|
|
|
} else {
|
|
|
|
|
assert(*leftmost_assignment_expr != NULL);
|
|
|
|
|
|
|
|
|
|
*out = **leftmost_assignment_expr;
|
|
|
|
|
free(*leftmost_assignment_expr);
|
|
|
|
|
*leftmost_assignment_expr = NULL;
|
|
|
|
|
|
|
|
|
|
assert(rightmost_assignment_expr->type == APFL_EXPR_ASSIGNMENT);
|
|
|
|
|
rightmost_assignment_expr->assignment.rhs = malloc(sizeof(struct apfl_expr));
|
|
|
|
|
if (rightmost_assignment_expr->assignment.rhs == NULL) {
|
|
|
|
|
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
dest = rightmost_assignment_expr->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, dest)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
apfl_expr_deinit(out);
|
|
|
|
|
return BODY_FINALIZE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
parse_body_or_toplevel(
|
|
|
|
|
apfl_parser_ptr p,
|
|
|
|
|
bool handle_eof,
|
|
|
|
|
struct fragment_list *fragments,
|
|
|
|
|
struct apfl_expr *out
|
|
|
|
|
) {
|
|
|
|
|
struct apfl_expr *leftmost_assignment_expr = NULL;
|
|
|
|
|
struct apfl_expr *rightmost_assignment_expr = NULL;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
for (;;) {
|
|
|
|
|
switch (parse_fragment_into_list(p, fragments, true, 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,
|
|
|
|
|
&leftmost_assignment_expr,
|
|
|
|
|
rightmost_assignment_expr,
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break_inner:
|
2021-12-10 21:13:39 +00:00
|
|
|
read_token_after_cant_handle(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (p->token.type) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_TOK_ASSIGN:
|
|
|
|
|
case APFL_TOK_LOCAL_ASSIGN:
|
2021-12-10 21:13:39 +00:00
|
|
|
bool local = p->token.type == APFL_TOK_LOCAL_ASSIGN;
|
|
|
|
|
struct apfl_position position = p->token.position;
|
2021-12-10 20:22:16 +00:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_expr *cur_assignment_expr = ALLOC(struct apfl_expr);
|
|
|
|
|
if (cur_assignment_expr == NULL) {
|
|
|
|
|
p->error = apfl_error_simple(APFL_ERR_MALLOC_FAILED);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cur_assignment_expr->type = APFL_EXPR_ASSIGNMENT;
|
|
|
|
|
cur_assignment_expr->position = position;
|
|
|
|
|
|
|
|
|
|
struct fragment fragment = fragment_move(&fragments->children[0]);
|
|
|
|
|
fragment_list_deinit(fragments); // Reset fragment list
|
|
|
|
|
if (!fragment_to_partial_assignment(
|
|
|
|
|
p,
|
|
|
|
|
local,
|
|
|
|
|
fragment,
|
|
|
|
|
&cur_assignment_expr->assignment
|
|
|
|
|
)) {
|
|
|
|
|
free(cur_assignment_expr);
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rightmost_assignment_expr == NULL) {
|
|
|
|
|
assert(leftmost_assignment_expr == NULL);
|
|
|
|
|
leftmost_assignment_expr = cur_assignment_expr;
|
|
|
|
|
rightmost_assignment_expr = cur_assignment_expr;
|
|
|
|
|
} else {
|
|
|
|
|
rightmost_assignment_expr->assignment.rhs = cur_assignment_expr;
|
|
|
|
|
rightmost_assignment_expr = cur_assignment_expr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case APFL_TOK_LINEBREAK:
|
|
|
|
|
case APFL_TOK_SEMICOLON:
|
|
|
|
|
switch (parse_body_or_toplevel_finalize(
|
|
|
|
|
p,
|
|
|
|
|
fragments,
|
|
|
|
|
&leftmost_assignment_expr,
|
|
|
|
|
rightmost_assignment_expr,
|
|
|
|
|
out
|
|
|
|
|
)) {
|
|
|
|
|
case BODY_FINALIZE_OK:
|
|
|
|
|
return PF_OK;
|
|
|
|
|
case BODY_FINALIZE_ERROR:
|
|
|
|
|
goto error;
|
|
|
|
|
case BODY_FINALIZE_EMPTY:
|
|
|
|
|
// 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:
|
|
|
|
|
if (leftmost_assignment_expr != NULL) {
|
2021-12-10 21:13:39 +00:00
|
|
|
p->error = ERR_UNEXPECTED_TOKEN(p->token);
|
2021-12-10 20:22:16 +00:00
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
unread_token(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
return PF_CANT_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error: // \mystuff\TODO:also on other non ok results???
|
|
|
|
|
DESTROY(leftmost_assignment_expr, apfl_expr_deinit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
parse_braces(
|
|
|
|
|
apfl_parser_ptr p,
|
|
|
|
|
struct fragment *out,
|
|
|
|
|
struct apfl_position start
|
|
|
|
|
) {
|
|
|
|
|
enum {
|
|
|
|
|
FUNTYPE_UNDECIDED,
|
|
|
|
|
FUNTYPE_SIMPLE,
|
|
|
|
|
FUNTYPE_COMPLEX,
|
|
|
|
|
} type = FUNTYPE_UNDECIDED;
|
|
|
|
|
|
|
|
|
|
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 = {};
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
)) {
|
|
|
|
|
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:
|
2021-12-10 21:13:39 +00:00
|
|
|
read_token_after_cant_handle(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (p->token.type) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_TOK_RBRACE:
|
|
|
|
|
// \mystuff\TODO:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_TOK_MAPSTO:
|
|
|
|
|
if (body.len > 0 && !has_params) {
|
|
|
|
|
p->error = (struct apfl_error) {
|
|
|
|
|
.type = APFL_ERR_STATEMENTS_BEFORE_PARAMETERS,
|
2021-12-10 21:13:39 +00:00
|
|
|
.position = p->token.position,
|
2021-12-10 20:22:16 +00:00
|
|
|
};
|
|
|
|
|
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) {};
|
|
|
|
|
body = (struct apfl_expr_body) {};
|
|
|
|
|
body_cap = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type = FUNTYPE_COMPLEX;
|
|
|
|
|
|
|
|
|
|
if (!fragments_to_params(p, fragments, ¶ms)) {
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
has_params = true;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2021-12-10 21:13:39 +00:00
|
|
|
p->error = ERR_UNEXPECTED_TOKEN(p->token);
|
2021-12-10 20:22:16 +00:00
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
// \mystuff\TODO:cleanup
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum parse_fragment_result
|
|
|
|
|
parse_fragment(apfl_parser_ptr p, struct fragment *fragment, bool need, enum parse_fragment_flags flags)
|
|
|
|
|
{
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (read_token(p, need)) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
return PF_EOF;
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
return PF_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum parse_fragment_result result;
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (p->token.type) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_TOK_LPAREN:
|
2021-12-10 21:13:39 +00:00
|
|
|
result = parse_parens(p, fragment, p->token.position);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_TOK_LBRACKET:
|
2021-12-10 21:13:39 +00:00
|
|
|
result = parse_brackets(p, fragment, p->token.position);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_TOK_LBRACE:
|
2021-12-10 21:13:39 +00:00
|
|
|
result = parse_braces(p, fragment, p->token.position);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_TOK_EXPAND:
|
|
|
|
|
if (flags & FFLAG_NO_EXPAND) {
|
2021-12-10 21:13:39 +00:00
|
|
|
unread_token(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
return PF_CANT_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
result = parse_expand(p, fragment, p->token.position);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_TOK_STRINGIFY:
|
2021-12-10 21:13:39 +00:00
|
|
|
result = parse_stringify(p, fragment, p->token.position);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_TOK_NUMBER:
|
|
|
|
|
fragment->type = FRAG_CONSTANT;
|
|
|
|
|
fragment->constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_NUMBER,
|
2021-12-10 21:13:39 +00:00
|
|
|
.number = p->token.number,
|
2021-12-10 20:22:16 +00:00
|
|
|
};
|
2021-12-10 21:13:39 +00:00
|
|
|
fragment->position = p->token.position;
|
2021-12-10 20:22:16 +00:00
|
|
|
result = PF_OK;
|
|
|
|
|
break;
|
|
|
|
|
case APFL_TOK_NAME:
|
2021-12-10 21:13:39 +00:00
|
|
|
if (apfl_string_cmp(p->token.text, "nil") == 0) {
|
2021-12-10 20:22:16 +00:00
|
|
|
fragment->type = FRAG_CONSTANT;
|
|
|
|
|
fragment->constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_NIL,
|
|
|
|
|
};
|
2021-12-10 21:13:39 +00:00
|
|
|
} else if (apfl_string_cmp(p->token.text, "true") == 0) {
|
2021-12-10 20:22:16 +00:00
|
|
|
fragment->type = FRAG_CONSTANT;
|
|
|
|
|
fragment->constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_BOOLEAN,
|
|
|
|
|
.boolean = true,
|
|
|
|
|
};
|
2021-12-10 21:13:39 +00:00
|
|
|
} else if (apfl_string_cmp(p->token.text, "false") == 0) {
|
2021-12-10 20:22:16 +00:00
|
|
|
fragment->type = FRAG_CONSTANT;
|
|
|
|
|
fragment->constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_BOOLEAN,
|
|
|
|
|
.boolean = false,
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
fragment->type = FRAG_NAME;
|
2021-12-10 21:13:39 +00:00
|
|
|
fragment->name = apfl_string_move(&p->token.text);
|
2021-12-10 20:22:16 +00:00
|
|
|
}
|
2021-12-10 21:13:39 +00:00
|
|
|
fragment->position = p->token.position;
|
2021-12-10 20:22:16 +00:00
|
|
|
result = PF_OK;
|
|
|
|
|
break;
|
|
|
|
|
case APFL_TOK_STRING:
|
|
|
|
|
fragment->type = FRAG_CONSTANT;
|
|
|
|
|
fragment->constant = (struct apfl_expr_const) {
|
|
|
|
|
.type = APFL_EXPR_CONST_STRING,
|
2021-12-10 21:13:39 +00:00
|
|
|
.string = apfl_string_move(&p->token.text),
|
2021-12-10 20:22:16 +00:00
|
|
|
};
|
2021-12-10 21:13:39 +00:00
|
|
|
fragment->position = p->token.position;
|
2021-12-10 20:22:16 +00:00
|
|
|
result = PF_OK;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2021-12-10 21:13:39 +00:00
|
|
|
unread_token(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
return PF_CANT_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result == PF_OK) {
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (read_token(p, need)) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_PARSE_OK:
|
|
|
|
|
break;
|
|
|
|
|
case APFL_PARSE_EOF:
|
|
|
|
|
return PF_OK;
|
|
|
|
|
case APFL_PARSE_ERROR:
|
|
|
|
|
return PF_ERROR; // \mystuff\TODO:destroy fragment in case of errors
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-10 21:13:39 +00:00
|
|
|
switch (p->token.type) {
|
2021-12-10 20:22:16 +00:00
|
|
|
case APFL_TOK_DOT:
|
2021-12-10 21:13:39 +00:00
|
|
|
result = parse_dot(p, fragment, p->token.position);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_TOK_AT:
|
2021-12-10 21:13:39 +00:00
|
|
|
result = parse_at(p, fragment, p->token.position);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
case APFL_TOK_QUESTION_MARK:
|
2021-12-10 21:13:39 +00:00
|
|
|
result = parse_predicate(p, fragment, p->token.position);
|
2021-12-10 20:22:16 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2021-12-10 21:13:39 +00:00
|
|
|
unread_token(p);
|
2021-12-10 20:22:16 +00:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum apfl_parse_result
|
|
|
|
|
apfl_parser_next(apfl_parser_ptr p)
|
|
|
|
|
{
|
|
|
|
|
// \mystuff\TODO:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct apfl_error
|
|
|
|
|
apfl_parser_get_error(apfl_parser_ptr p)
|
|
|
|
|
{
|
|
|
|
|
return p->error;
|
|
|
|
|
}
|