diff --git a/src/backends/glr.c b/src/backends/glr.c index 9828c2b30fc2a53eab1d2de5fd77b6f0893b6065..5619b26a4558b7ffbfe5b0ac33689e4625848cc2 100644 --- a/src/backends/glr.c +++ b/src/backends/glr.c @@ -174,9 +174,9 @@ static bool glr_step(HParseResult **result, HSlist *engines, HSlistNode *x; for(x=engines->head; x; x=x->next) { HLREngine *eng = x->elem; - if(eng->state == engine->state) { - x->elem = lrengine_merge(eng, engine); - break; + if(eng->state == engine->state && eng->input.index == engine->input.index) { + x->elem = lrengine_merge(eng, engine); + break; } } if(!x) // no merge happened diff --git a/src/t_regression.c b/src/t_regression.c index 2c28b99efe6a36e69e5831044dadcbcc381f4d18..3bf763600962bdcdad6208863ba27f92966195f3 100644 --- a/src/t_regression.c +++ b/src/t_regression.c @@ -1,5 +1,6 @@ #include <glib.h> #include <stdint.h> +#include <stdlib.h> #include "glue.h" #include "hammer.h" #include "test_suite.h" @@ -472,6 +473,141 @@ static void test_issue83() { g_check_cmp_int(r, ==, 0); } +/* + * This is Meg's cut-down bug 60 test case + */ + +static void test_bug60() { + /* There is probably an even smaller example that shows the issue */ + + HParser *zed = NULL; + HParser *alpha = NULL; + HParser *vchar = NULL; + HParser *why = NULL; + HParser *plural_zed = NULL; + HParser *plural_zed_zed = NULL; + HParser *a_to_zed = NULL; + HParser *alphas = NULL; + HParser *rule = NULL; + HParser *rulelist = NULL; + HParser *p = NULL; + HParseResult *r = NULL; + int n; + + zed = h_ch('z'); + + vchar = h_ch_range(0x79, 0x7a); /* allows y and z */ + + alpha = h_ch('a'); + + why = h_ch('y'); + + plural_zed = h_sequence( + why, + h_many(h_choice(alpha, vchar, NULL)), + NULL); + plural_zed_zed = h_choice(plural_zed, zed, NULL); + alphas = h_choice(alpha, h_sequence(plural_zed_zed, alpha, NULL), NULL); + + a_to_zed = h_sequence( + zed, + h_many(h_sequence(h_many1(alphas), zed, NULL)), + NULL); + rule = h_sequence(a_to_zed, plural_zed_zed, NULL); + rulelist = h_many1(h_choice( + rule, + h_sequence(h_many(alphas), plural_zed_zed, NULL), + NULL)); + + p = rulelist; + + g_check_parse_ok(p, PB_GLR, "ayzza", 5); + g_check_parse_match(p, PB_GLR, "ayzza", 5, "(((u0x61) (u0x79 (u0x7a u0x7a u0x61))))"); + +} + +/* + * This is the original bug60 test case; cut down from an ABNF parser + */ + +#define BUG60_ABNF_SCAN_UP_TO 64 + +static void test_bug60_abnf() { + HParser *newline = NULL; + HParser *alpha = NULL; + HParser *sp = NULL; + HParser *vchar = NULL; + HParser *equal = NULL; + HParser *semicolon = NULL; + HParser *comment = NULL; + HParser *c_nl = NULL; + HParser *c_sp = NULL; + HParser *defined_as = NULL; + HParser *alphas = NULL; + HParser *rule = NULL; + HParser *rulelist = NULL; + HParser *p = NULL; + int i, j, r, s_size; + char *s = NULL; + const char *test_string_template = "x = y z%s;%s\n\n"; + char buf_1[BUG60_ABNF_SCAN_UP_TO+1]; + char buf_2[2*BUG60_ABNF_SCAN_UP_TO+1]; + + newline = h_ch('\n'); + alpha = h_choice(h_ch_range('A', 'Z'), h_ch_range('a', 'z'), NULL); + sp = h_ch(' '); + vchar = h_ch_range(0x21, 0x7e); + equal = h_ch('='); + semicolon = h_ch(';'); + comment = h_sequence( + semicolon, + h_many(h_choice(sp, vchar, NULL)), + newline, + NULL); + c_nl = h_choice(comment, newline, NULL); + c_sp = h_choice(sp, h_sequence(c_nl, sp, NULL), NULL); + defined_as = h_sequence(h_many(c_sp), equal, h_many(c_sp), NULL); + alphas = h_sequence( + alpha, + h_many(h_sequence(h_many1(c_sp), alpha, NULL)), + h_many(c_sp), + NULL); + rule = h_sequence(alpha, defined_as, alphas, c_nl, NULL); + rulelist = h_many1(h_choice( + rule, + h_sequence(h_many(c_sp), c_nl, NULL), + NULL)); + + p = rulelist; + g_check_compilable(p, PB_GLR, 1); + + /* Have a buffer for the string */ + s_size = strlen(test_string_template) + 3*BUG60_ABNF_SCAN_UP_TO + 1; + s = malloc(s_size); + g_check_cmp_ptr(s, !=, NULL); + + /* + * Try to parse all the different strings according to the template up to + * the scan limit. + * + * Correct behavior: it parses for all values of i, j. + * Bugged behavior: when i % 3 != 0, parse failures begin to occur at + * j == (i / 3) + (i % 3). + */ + for (i = 0; i < BUG60_ABNF_SCAN_UP_TO; ++i) { + memset(buf_1, ' ', i); + buf_1[i] = 0; + for (j = 0; j < 2*BUG60_ABNF_SCAN_UP_TO; ++j) { + memset(buf_2, 'x', j); + buf_2[j] = 0; + snprintf(s, s_size, test_string_template, buf_1, buf_2); + g_check_parse_ok_no_compile(p, s, strlen(s)); + } + } + + free(s); +} + void register_regression_tests(void) { g_test_add_func("/core/regression/bug118", test_bug118); g_test_add_func("/core/regression/seq_index_path", test_seq_index_path); @@ -488,4 +624,6 @@ void register_regression_tests(void) { g_test_add_func("/core/regression/issue87", test_issue87); g_test_add_func("/core/regression/issue92", test_issue92); g_test_add_func("/core/regression/issue83", test_issue83); + g_test_add_func("/core/regression/bug60", test_bug60); + g_test_add_func("/core/regression/bug60_abnf", test_bug60_abnf); } diff --git a/src/test_suite.h b/src/test_suite.h index 6f95e077650e86d4c5ef1e0f01f2ee2b20ab31d2..775c8818a1f79cb2fb50275a55c0e8e1f8a235fe 100644 --- a/src/test_suite.h +++ b/src/test_suite.h @@ -109,24 +109,36 @@ } while(0) #define g_check_compilable(lang, backend, params) do { \ - if (!h_compile(lang, backend, params)) { \ - g_test_message("Language is not %s(%s)", #backend, params); \ + int r = h_compile((HParser *)(lang), (HParserBackend)(backend), (void *)(params)); \ + if (r != 0) { \ + g_test_message("Language is not %s(%s)", #backend, #params); \ g_test_fail(); \ } \ } while(0) - +#define print_arena_stats(arena) do { \ + if (g_test_verbose()) { \ + HArenaStats stats; \ + h_allocator_stats(arena, &stats); \ + g_test_message("Parse used %zd bytes, wasted %zd bytes. " \ + "Inefficiency: %5f%%", \ + stats.used, stats.wasted, \ + stats.wasted * 100. / (stats.used+stats.wasted)); \ + } \ + } while(0) + #define g_check_parse_failed__m(mm__, parser, backend, input, inp_len) do { \ int skip = h_compile__m(mm__, (HParser *)(parser), (HParserBackend)backend, NULL); \ if(skip != 0) { \ - g_test_message("Compile failed"); \ + g_test_message("Compile failed on line %d", __LINE__); \ g_test_fail(); \ break; \ } \ - HParseResult *result = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ - if (NULL != result) { \ - h_parse_result_free(result); \ - g_test_message("Check failed: shouldn't have succeeded, but did"); \ + HParseResult *res = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ + if (NULL != res) { \ + print_arena_stats(res->arena); \ + h_parse_result_free(res); \ + g_test_message("Check failed: parse shouldn't have succeeded, but did on line %d", __LINE__); \ g_test_fail(); \ } \ } while(0) @@ -134,25 +146,27 @@ #define g_check_parse_failed(p, be, input, len) \ g_check_parse_failed__m(&system_allocator, p, be, input, len) -#define print_arena_stats(arena) do { \ - if (g_test_verbose()) { \ - HArenaStats stats; \ - h_allocator_stats(arena, &stats); \ - g_test_message("Parse used %zd bytes, wasted %zd bytes. " \ - "Inefficiency: %5f%%", \ - stats.used, stats.wasted, \ - stats.wasted * 100. / (stats.used+stats.wasted)); \ +#define g_check_parse_failed_no_compile__m(mm__, parser, input, inp_len) do { \ + HParseResult *res = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ + if (NULL != res) { \ + print_arena_stats(res->arena); \ + h_parse_result_free(res); \ + g_test_message("Check failed: shouldn't have succeeded, but did on ;ine %d", __LINE__); \ + g_test_fail(); \ } \ } while(0) -#define g_check_parse_ok(parser, backend, input, inp_len) do { \ - int skip = h_compile((HParser *)(parser), (HParserBackend) backend, NULL); \ +#define g_check_parse_failed_no_compile(p, input, len) \ + g_check_parse_failed__m(&system_allocator, p, input, len) + +#define g_check_parse_ok__m(mm__, parser, backend, input, inp_len) do { \ + int skip = h_compile__m(mm__, (HParser *)(parser), (HParserBackend) backend, NULL); \ if(skip) { \ g_test_message("Compile failed"); \ g_test_fail(); \ break; \ } \ - HParseResult *res = h_parse(parser, (const uint8_t*)input, inp_len); \ + HParseResult *res = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ if (!res) { \ g_test_message("Parse failed on line %d", __LINE__); \ g_test_fail(); \ @@ -162,14 +176,31 @@ } \ } while(0) -#define g_check_parse_match(parser, backend, input, inp_len, result) do { \ - int skip = h_compile((HParser *)(parser), (HParserBackend) backend, NULL); \ +#define g_check_parse_ok(p, be, input, len) \ + g_check_parse_ok__m(&system_allocator, p, be, input, len) + +#define g_check_parse_ok_no_compile__m(mm__, parser, input, inp_len) do { \ + HParseResult *res = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ + if (!res) { \ + g_test_message("Parse failed on line %d", __LINE__); \ + g_test_fail(); \ + } else { \ + print_arena_stats(res->arena); \ + h_parse_result_free(res); \ + } \ + } while(0) + +#define g_check_parse_ok_no_compile(p, input, len) \ + g_check_parse_ok_no_compile__m(&system_allocator, p, input, len) + +#define g_check_parse_match__m(mm__, parser, backend, input, inp_len, result) do { \ + int skip = h_compile__m(mm__, (HParser *)(parser), (HParserBackend) backend, NULL); \ if(skip) { \ g_test_message("Compile failed"); \ g_test_fail(); \ break; \ } \ - HParseResult *res = h_parse(parser, (const uint8_t*)input, inp_len); \ + HParseResult *res = h_parse__m(mm__, (HParser *)(parser), (const uint8_t*)input, inp_len); \ if (!res) { \ g_test_message("Parse failed on line %d", __LINE__); \ g_test_fail(); \ @@ -182,6 +213,26 @@ } \ } while(0) +#define g_check_parse_match(parser, backend, input, inp_len, result) \ + g_check_parse_match__m(&system_allocator, parser, backend, input, inp_len, result) + +#define g_check_parse_match_no_compile__m(mm__, parser, input, inp_len, result) do { \ + HParseResult *res = h_parse__m(mm__, (HParser *)(parser), (const uint8_t*)input, inp_len); \ + if (!res) { \ + g_test_message("Parse failed on line %d", __LINE__); \ + g_test_fail(); \ + } else { \ + char* cres = h_write_result_unamb(res->ast); \ + g_check_string(cres, ==, result); \ + (&system_allocator)->free(&system_allocator, cres); \ + print_arena_stats(res->arena); \ + h_parse_result_free(res); \ + } \ + } while(0) + +#define g_check_parse_match_no_compile(parser, input, inp_len, result) \ + g_check_parse_match_no_compile__m(&system_allocator, parser, input, inp_len, result) + #define g_check_parse_chunks_failed__m(mm__, parser, backend, chunk1, c1_len, chunk2, c2_len) do { \ int skip = h_compile__m(mm__, (HParser *)(parser), (HParserBackend)backend, NULL); \ if(skip) { \ @@ -193,7 +244,7 @@ } while(0) #define g_check_parse_chunks_failed___m(mm__, parser, chunk1, c1_len, chunk2, c2_len) do { \ - HSuspendedParser *s = h_parse_start__m(mm__, parser); \ + HSuspendedParser *s = h_parse_start__m(mm__, (HParser *)(parser)); \ if(!s) { \ g_test_message("Chunk-wise parsing not available"); \ g_test_fail(); \ @@ -226,7 +277,7 @@ } while(0) #define g_check_parse_chunks_ok_(parser, chunk1, c1_len, chunk2, c2_len) do { \ - HSuspendedParser *s = h_parse_start(parser); \ + HSuspendedParser *s = h_parse_start((HParser *)(parser)); \ if(!s) { \ g_test_message("Chunk-wise parsing not available"); \ g_test_fail(); \ @@ -255,7 +306,7 @@ } while(0) #define g_check_parse_chunks_match_(parser, chunk1, c1_len, chunk2, c2_len, result) do { \ - HSuspendedParser *s = h_parse_start(parser); \ + HSuspendedParser *s = h_parse_start((HParser *)(parser)); \ if(!s) { \ g_test_message("Chunk-wise parsing not available"); \ g_test_fail(); \