From d5358a724ba5d5abe809c02f55769f7f94deed64 Mon Sep 17 00:00:00 2001 From: Dan Hirsch <thequux@thequux.com> Date: Sat, 12 May 2012 00:40:54 +0100 Subject: [PATCH] Made sequence and choice variadic, and added test suite helper functions --- src/Makefile | 3 ++ src/hammer.c | 87 ++++++++++++++++++++++++++++++++++-------------- src/hammer.h | 6 ++-- src/internal.h | 1 + src/test_suite.h | 38 ++++++++++++++++++--- 5 files changed, 102 insertions(+), 33 deletions(-) diff --git a/src/Makefile b/src/Makefile index 670a4daf..99bebd70 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,3 +18,6 @@ libhammer.a: bitreader.o hammer.o pprint.o bitreader.o: test_suite.h hammer.o: hammer.h + +test: test_suite + ./test_suite -v diff --git a/src/hammer.c b/src/hammer.c index ec010aca..e4addbae 100644 --- a/src/hammer.c +++ b/src/hammer.c @@ -19,6 +19,7 @@ #include "internal.h" #include <assert.h> #include <string.h> +#include <stdarg.h> parse_state_t* from(parse_state_t *ps, const size_t index) { parse_state_t *ret = g_new(parse_state_t, 1); @@ -71,7 +72,10 @@ parse_result_t* do_parse(const parser_t* parser, parse_state_t *state) { } else { // It doesn't exist... run the parse_result_t *res; - res = parser->fn(parser->env, state); + if (parser) + res = parser->fn(parser->env, state); + else + res = NULL; if (state->input_stream.overrun) res = NULL; // overrun is always failure. // update the cache @@ -229,10 +233,27 @@ static parse_result_t* parse_sequence(void *env, parse_state_t *state) { return make_result(tok); } -const parser_t* sequence(const parser_t* p_array[]) { - size_t len = sizeof(p_array) / sizeof(parser_t*); +const parser_t* sequence(const parser_t *p, ...) { + va_list ap; + size_t len = 0; + const parser_t *arg; + va_start(ap, p); + do { + len++; + arg = va_arg(ap, const parser_t *); + } while (arg); + va_end(ap); sequence_t *s = g_new(sequence_t, 1); - s->p_array = (const parser_t**)p_array; s->len = len; + s->p_array = g_new(const parser_t *, len); + + va_start(ap, p); + s->p_array[0] = p; + for (size_t i = 1; i < len; i++) { + s->p_array[i] = va_arg(ap, const parser_t *); + } while (arg); + va_end(ap); + + s->len = len; parser_t *ret = g_new(parser_t, 1); ret->fn = parse_sequence; ret->env = (void*)s; return ret; @@ -252,10 +273,28 @@ static parse_result_t* parse_choice(void *env, parse_state_t *state) { return NULL; } -const parser_t* choice(const parser_t* p_array[]) { - size_t len = sizeof(p_array) / sizeof(parser_t*); +const parser_t* choice(const parser_t* p, ...) { + va_list ap; + size_t len = 0; sequence_t *s = g_new(sequence_t, 1); - s->p_array = (const parser_t**)p_array; s->len = len; + + const parser_t *arg; + va_start(ap, p); + do { + len++; + arg = va_arg(ap, const parser_t *); + } while (arg); + va_end(ap); + s->p_array = g_new(const parser_t *, len); + + va_start(ap, p); + s->p_array[0] = p; + for (size_t i = 1; i < len; i++) { + s->p_array[i] = va_arg(ap, const parser_t *); + } while (arg); + va_end(ap); + + s->len = len; parser_t *ret = g_new(parser_t, 1); ret->fn = parse_choice; ret->env = (void*)s; return ret; @@ -479,6 +518,7 @@ static void test_range(void) { g_check_failed(ret2); } +#if 0 static void test_int64(void) { uint8_t test1[8] = { 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00 }; uint8_t test2[7] = { 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00 }; @@ -578,6 +618,8 @@ static void test_float32(void) { g_check_cmpfloat(ret1->ast->flt, ==, 1); g_check_failed(ret2); } +#endif + static void test_whitespace(void) { uint8_t test1[1] = { 'a' }; @@ -620,8 +662,7 @@ static void test_not_in(void) { static void test_end_p(void) { uint8_t test1[1] = { 'a' }; uint8_t test2[2] = { 'a', 'a' }; - const parser_t *p_array[2] = { ch('a'), end_p() }; - const parser_t *end_p_ = sequence(p_array); + const parser_t *end_p_ = sequence(ch('a'), end_p(), NULL); parse_result_t *ret1 = parse(end_p_, test1, 1); parse_result_t *ret2 = parse(end_p_, test2, 2); g_check_cmpint(ret1->ast->uint, ==, 'a'); @@ -636,22 +677,16 @@ static void test_nothing_p(void) { } static void test_sequence(void) { - uint8_t test1[2] = { 'a', 'b' }; - uint8_t test2[1] = { 'a' }; - uint8_t test3[1] = { 'b' }; - uint8_t test4[3] = { 'a', ' ', 'b' }; - uint8_t test5[4] = { 'a', ' ', ' ', 'b' }; - uint8_t test6[2] = { 'a', 'b' }; - const parser_t *s1[2] = { ch('a'), ch('b') }; - const parser_t *s2[2] = { ch('a'), whitespace(ch('b')) }; - const parser_t *sequence_1 = sequence(s1); - const parser_t *sequence_2 = sequence(s2); - parse_result_t *ret1 = parse(sequence_1, test1, 2); - parse_result_t *ret2 = parse(sequence_1, test2, 1); - parse_result_t *ret3 = parse(sequence_1, test3, 1); - parse_result_t *ret4 = parse(sequence_2, test4, 3); - parse_result_t *ret5 = parse(sequence_2, test5, 4); - parse_result_t *ret6 = parse(sequence_2, test6, 2); + const parser_t *sequence_1 = sequence(ch('a'), ch('b'), NULL); + const parser_t *sequence_2 = sequence(ch('a'), whitespace(ch('b')), NULL); + + g_check_parse_ok(sequence_1, "ab", 2, "(<41> <42>)"); + g_check_parse_failed(sequence_1, "a", 1); + g_check_parse_failed(sequence_1, "b", 1); + g_check_parse_ok(sequence_2, "ab", 2, "(<41> <42>)"); + g_check_parse_ok(sequence_2, "a b", 3, "(<41> <42>)"); + g_check_parse_ok(sequence_2, "a b", 4, "(<41> <42>)"); + //g_check_cmpseq(ret1->ast-> } @@ -727,6 +762,7 @@ void register_parser_tests(void) { g_test_add_func("/core/parser/token", test_token); g_test_add_func("/core/parser/ch", test_ch); g_test_add_func("/core/parser/range", test_range); +#if 0 g_test_add_func("/core/parser/int64", test_int64); g_test_add_func("/core/parser/int32", test_int32); g_test_add_func("/core/parser/int16", test_int16); @@ -737,6 +773,7 @@ void register_parser_tests(void) { g_test_add_func("/core/parser/uint8", test_uint8); g_test_add_func("/core/parser/float64", test_float64); 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/action", test_action); g_test_add_func("/core/parser/left_factor_action", test_left_factor_action); diff --git a/src/hammer.h b/src/hammer.h index 1b84861e..a6445a89 100644 --- a/src/hammer.h +++ b/src/hammer.h @@ -147,11 +147,11 @@ const parser_t* end_p(); /* This parser always fails. */ const parser_t* nothing_p(); -/* Given an array of parsers, p_array, apply each parser in order. The parse succeeds only if all parsers succeed. */ -const parser_t* sequence(const parser_t* p_array[]); +/* Given an null-terminated list of parsers, apply each parser in order. The parse succeeds only if all parsers succeed. */ +const parser_t* sequence(const parser_t* p, ...) __attribute__((sentinel)); /* Given an array of parsers, p_array, apply each parser in order. The first parser to succeed is the result; if no parsers succeed, the parse fails. */ -const parser_t* choice(const parser_t* p_array[]); +const parser_t* choice(const parser_t* p, ...) __attribute__((sentinel)); /* Given two parsers, p1 and p2, this parser succeeds in the following cases: * - if p1 succeeds and p2 fails diff --git a/src/internal.h b/src/internal.h index 6659599a..771a0ee4 100644 --- a/src/internal.h +++ b/src/internal.h @@ -36,4 +36,5 @@ parse_result_t* do_parse(const parser_t* parser, parse_state_t *state); void put_cached(parse_state_t *ps, const parser_t *p, parse_result_t *cached); guint djbhash(const uint8_t *buf, size_t len); char* write_result_unamb(const parsed_token_t* tok); +void pprint(const parsed_token_t* tok, int indent, int delta); #endif // #ifndef HAMMER_INTERNAL__H diff --git a/src/test_suite.h b/src/test_suite.h index 711f9ebe..3c01d3fe 100644 --- a/src/test_suite.h +++ b/src/test_suite.h @@ -15,7 +15,7 @@ #define g_check_bytes(len, n1, op, n2) { \ const uint8_t *_n1 = (n1); \ - uint8_t *_n2 = (n2); \ + const uint8_t *_n2 = (n2); \ if (!(memcmp(_n1, _n2, len) op 0)) { \ g_test_message("Check failed: (%s)", \ #n1 " " #op " " #n2); \ @@ -23,10 +23,18 @@ } \ } -#define g_check_cmpseq(n1, op, n2) { \ - GSequence *seq = (n1); \ - - +#define g_check_string(n1, op, n2) { \ + const char *_n1 = (n1); \ + const char *_n2 = (n2); \ + if (!(strcmp(_n1, _n2) op 0)) { \ + g_test_message("Check failed: (%s) (%s %s %s)", \ + #n1 " " #op " " #n2, \ + _n1, #op, _n2); \ + g_test_fail(); \ + } \ + } + +// TODO: replace uses of this with g_check_parse_failed #define g_check_failed(res) { \ const parse_result_t *result = (res); \ if (NULL != result) { \ @@ -35,6 +43,26 @@ } \ } +#define g_check_parse_failed(parser, input, inp_len) { \ + const parse_result_t *result = parse(parser, (const uint8_t*)input, inp_len); \ + if (NULL != result) { \ + g_test_message("Check failed: shouldn't have succeeded, but did"); \ + g_test_fail(); \ + } \ + } + +#define g_check_parse_ok(parser, input, inp_len, result) { \ + parse_result_t *res = parse(parser, (const uint8_t*)input, inp_len); \ + if (!res) { \ + g_test_message("Parse failed"); \ + g_test_fail(); \ + } else { \ + char* cres = write_result_unamb(res->ast); \ + g_check_string(cres, ==, result); \ + } \ + } + + #define g_check_cmpint(n1, op, n2) g_check_inttype("%d", int, n1, op, n2) #define g_check_cmplong(n1, op, n2) g_check_inttype("%ld", long, n1, op, n2) #define g_check_cmplonglong(n1, op, n2) g_check_inttype("%lld", long long, n1, op, n2) -- GitLab