From d424c0f886a52cfd2b393aefb5473c657840b2de Mon Sep 17 00:00:00 2001 From: omicron Date: Tue, 1 Apr 2025 23:39:48 +0200 Subject: [PATCH] Add a parser combinator to parse a delimited list --- src/parser/combinators.c | 55 +++++++++++++++++++++++++++++++++++++--- src/parser/combinators.h | 4 +++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/parser/combinators.c b/src/parser/combinators.c index 94c628a..1c6cba7 100644 --- a/src/parser/combinators.c +++ b/src/parser/combinators.c @@ -1,5 +1,54 @@ #include "combinators.h" +// Parse a list of the given parser delimited by the given token id. Does not +// store the delimiters in the parent node +parse_result_t parse_list(tokenlist_entry_t *current, node_id_t id, + bool allow_none, lexer_token_id_t delimiter_id, + parser_t parser) { + ast_node_t *many; + error_t *err = ast_node_alloc(&many); + parse_result_t result; + if (err) + return parse_error(err); + many->id = id; + + while (current) { + // Skip beyond the delimiter on all but the first iteration + if (many->len > 0) { + if (current->token.id != delimiter_id) + break; + current = tokenlist_next(current); + if (current == nullptr) { + // FIXME: this isn't quite right, we can't consume the delimiter + // if the next element will fail to parse but it's late and I + // must think this through tomorrow + break; + } + } + + result = parser(current); + if (result.err == err_parse_no_match) + break; + if (result.err) { + ast_node_free(many); + return result; + } + err = ast_node_add_child(many, result.node); + if (err) { + ast_node_free(many); + ast_node_free(result.node); + return parse_error(err); + } + current = result.next; + } + + if (!allow_none && many->len == 0) { + ast_node_free(many); + return parse_no_match(); + } + return parse_success(many, current); +} + parse_result_t parse_any(tokenlist_entry_t *current, parser_t parsers[]) { parser_t parser; while ((parser = *parsers++)) { @@ -10,9 +59,9 @@ parse_result_t parse_any(tokenlist_entry_t *current, parser_t parsers[]) { return parse_no_match(); } -// parse as many of the giver parsers objects in a row as possible, potentially -// allowing none wraps the found objects in a new ast node with the given note -// id +// parse as many of the giver parsers objects in a row as possible, +// potentially allowing none wraps the found objects in a new ast node with +// the given note id parse_result_t parse_many(tokenlist_entry_t *current, node_id_t id, bool allow_none, parser_t parser) { ast_node_t *many; diff --git a/src/parser/combinators.h b/src/parser/combinators.h index ae40c7a..1dd8855 100644 --- a/src/parser/combinators.h +++ b/src/parser/combinators.h @@ -13,6 +13,10 @@ parse_result_t parse_any(tokenlist_entry_t *current, parser_t parsers[]); parse_result_t parse_many(tokenlist_entry_t *current, node_id_t id, bool allow_none, parser_t parser); +parse_result_t parse_list(tokenlist_entry_t *current, node_id_t id, + bool allow_none, lexer_token_id_t delimiter_id, + parser_t parser); + // Parse all tries to parse all parsers consecutively and if it succeeds it // wraps the parsed nodes in a new parent node. parse_result_t parse_consecutive(tokenlist_entry_t *current, node_id_t id,