From 94ac826cde6bfdd12136ace6285af71dd5181a1d Mon Sep 17 00:00:00 2001 From: omicron Date: Tue, 1 Apr 2025 20:05:35 +0200 Subject: [PATCH] Add basic parser combinators --- src/parser_combinators.c | 75 ++++++++++++++++++++++++++++++++++++++++ src/parser_combinators.h | 16 +++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/parser_combinators.c create mode 100644 src/parser_combinators.h diff --git a/src/parser_combinators.c b/src/parser_combinators.c new file mode 100644 index 0000000..dfb73c4 --- /dev/null +++ b/src/parser_combinators.c @@ -0,0 +1,75 @@ +#include "parser_combinators.h" + +parse_result_t parse_any(tokenlist_entry_t *current, parser_t parsers[]) { + for (parser_t parser = *parsers; parser; parser = *parsers++) { + parse_result_t result = parser(current); + if (result.err == nullptr) + return result; + } + 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_result_t parse_many(tokenlist_entry_t *current, node_id_t id, + bool allow_none, 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) { + 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 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, + parser_t parsers[]) { + ast_node_t *all; + error_t *err = ast_node_alloc(&all); + parse_result_t result; + if (err) + return parse_no_match(); + + all->id = id; + + for (parser_t parser = *parsers; parser && current; parser = *parsers++) { + result = parser(current); + if (result.err) { + ast_node_free(all); + return result; + } + err = ast_node_add_child(all, result.node); + if (err) { + ast_node_free(result.node); + ast_node_free(all); + return parse_error(err); + } + current = result.next; + } + return parse_success(all, current); +} diff --git a/src/parser_combinators.h b/src/parser_combinators.h new file mode 100644 index 0000000..2bfcc93 --- /dev/null +++ b/src/parser_combinators.h @@ -0,0 +1,16 @@ +#include "parser_util.h" + +typedef parse_result_t (*parser_t)(tokenlist_entry_t *); + +parse_result_t parse_any(tokenlist_entry_t *current, parser_t parsers[]); + +// 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); + +// 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, + parser_t parsers[]);