diff --git a/src/Makefile b/src/Makefile index cc08eee88add9b8b613b40db3940c87220ab12e6..264608b5ecc2891c61b58f51d3f187d954c05fdb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,6 +4,7 @@ PARSERS := \ bits \ token \ whitespace \ + ignoreseq \ ch \ action \ charset \ diff --git a/src/hammer.c b/src/hammer.c index 11492c3fdfb28705f7eaad360f5d99af74068fb9..3bbd53791987ebc80a4a7d32408feac9adcf4cdf 100644 --- a/src/hammer.c +++ b/src/hammer.c @@ -380,6 +380,37 @@ static void test_whitespace(void) { g_check_parse_failed(whitespace_, "_a", 2); } +static void test_left(void) { + const HParser *left_ = h_left(h_ch('a'), h_ch(' ')); + + g_check_parse_ok(left_, "a ", 2, "u0x61"); + g_check_parse_failed(left_, "a", 1); + g_check_parse_failed(left_, " ", 1); + g_check_parse_failed(left_, "ab", 2); +} + +static void test_right(void) { + const HParser *right_ = h_right(h_ch(' '), h_ch('a')); + + g_check_parse_ok(right_, " a", 2, "u0x61"); + g_check_parse_failed(right_, "a", 1); + g_check_parse_failed(right_, " ", 1); + g_check_parse_failed(right_, "ba", 2); +} + +static void test_middle(void) { + const HParser *middle_ = h_middle(h_ch(' '), h_ch('a'), h_ch(' ')); + + g_check_parse_ok(middle_, " a ", 3, "u0x61"); + g_check_parse_failed(middle_, "a", 1); + g_check_parse_failed(middle_, " ", 1); + g_check_parse_failed(middle_, " a", 2); + g_check_parse_failed(middle_, "a ", 2); + g_check_parse_failed(middle_, " b ", 3); + g_check_parse_failed(middle_, "ba ", 3); + g_check_parse_failed(middle_, " ab", 3); +} + #include <ctype.h> const HParsedToken* upcase(const HParseResult *p) { @@ -608,6 +639,9 @@ void register_parser_tests(void) { g_test_add_func("/core/parser/float32", test_float32); #endif g_test_add_func("/core/parser/whitespace", test_whitespace); + g_test_add_func("/core/parser/left", test_left); + g_test_add_func("/core/parser/right", test_right); + g_test_add_func("/core/parser/middle", test_middle); g_test_add_func("/core/parser/action", test_action); g_test_add_func("/core/parser/in", test_in); g_test_add_func("/core/parser/not_in", test_not_in); diff --git a/src/hammer.h b/src/hammer.h index d0eac9d1d2be3bd860bf05c79d60018223b039fb..f7e9a9beb92452147d5e2dddc98195b4bd0a59ce 100644 --- a/src/hammer.h +++ b/src/hammer.h @@ -216,6 +216,30 @@ const HParser* h_uint8(); */ const HParser* h_whitespace(const HParser* p); +/** + * Given two parsers, p and q, returns a parser that parses them in + * sequence but only returns p's result. + * + * Result token type: p's result type + */ +const HParser* h_left(const HParser* p, const HParser* q); + +/** + * Given two parsers, p and q, returns a parser that parses them in + * sequence but only returns q's result. + * + * Result token type: q's result type + */ +const HParser* h_right(const HParser* p, const HParser* q); + +/** + * Given three parsers, p, x, and q, returns a parser that parses them in + * sequence but only returns x's result. + * + * Result token type: x's result type + */ +const HParser* h_middle(const HParser* p, const HParser* x, const HParser* q); + /** * Given another parser, p, and a function f, returns a parser that * applies p, then applies f to everything in the AST of p's result. diff --git a/src/parsers/ignoreseq.c b/src/parsers/ignoreseq.c new file mode 100644 index 0000000000000000000000000000000000000000..8aac2c82c5f09658c9962860f7f625bff70523f2 --- /dev/null +++ b/src/parsers/ignoreseq.c @@ -0,0 +1,73 @@ +#include "parser_internal.h" + + +// +// general case: parse sequence, pick one result +// + +typedef struct { + const HParser **parsers; + size_t count; // how many parsers in 'ps' + size_t which; // whose result to return +} HIgnoreSeq; + +static HParseResult* parse_ignoreseq(void* env, HParseState *state) { + const HIgnoreSeq *seq = (HIgnoreSeq*)env; + HParseResult *res = NULL; + + for (size_t i=0; i < seq->count; ++i) { + HParseResult *tmp = h_do_parse(seq->parsers[i], state); + if (!tmp) + return NULL; + else if (i == seq->which) + res = tmp; + } + + return res; +} + +static const HParserVtable ignoreseq_vt = { + .parse = parse_ignoreseq, +}; + + +// +// API frontends +// + +static const HParser* h_leftright(const HParser* p, const HParser* q, size_t which) { + HIgnoreSeq *seq = g_new(HIgnoreSeq, 1); + seq->parsers = g_new(const HParser*, 2); + seq->parsers[0] = p; + seq->parsers[1] = q; + seq->count = 2; + seq->which = which; + + HParser *ret = g_new(HParser, 1); + ret->vtable = &ignoreseq_vt; + ret->env = (void*)seq; + return ret; +} + +const HParser* h_left(const HParser* p, const HParser* q) { + return h_leftright(p, q, 0); +} + +const HParser* h_right(const HParser* p, const HParser* q) { + return h_leftright(p, q, 1); +} + +const HParser* h_middle(const HParser* p, const HParser* x, const HParser* q) { + HIgnoreSeq *seq = g_new(HIgnoreSeq, 1); + seq->parsers = g_new(const HParser*, 3); + seq->parsers[0] = p; + seq->parsers[1] = x; + seq->parsers[2] = q; + seq->count = 3; + seq->which = 1; + + HParser *ret = g_new(HParser, 1); + ret->vtable = &ignoreseq_vt; + ret->env = (void*)seq; + return ret; +}