diff --git a/src/SConscript b/src/SConscript index ccb6f5d8cb758219aa1ad83bc44124c0368e32ba..cb6469673c973c16c16030485f91e92143a9c17b 100644 --- a/src/SConscript +++ b/src/SConscript @@ -6,6 +6,9 @@ import os.path Import('env testruns') +# Bump this if you break binary compatibility (e.g. renumber backends) +hammer_shlib_version = "1.0.0" + dist_headers = [ 'hammer.h', 'allocator.h', @@ -21,7 +24,9 @@ parsers_headers = [ backends_headers = [ 'backends/regex.h', - 'backends/contextfree.h' + 'backends/contextfree.h', + 'backends/missing.h', + 'backends/params.h' ] parsers = ['parsers/%s.c'%s for s in @@ -56,7 +61,7 @@ parsers = ['parsers/%s.c'%s for s in 'seek']] backends = ['backends/%s.c' % s for s in - ['packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0']] + ['missing', 'packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0', 'params']] misc_hammer_parts = [ 'allocator.c', @@ -88,6 +93,7 @@ ctests = ['t_benchmark.c', 't_grammar.c', 't_misc.c', 't_mm.c', + 't_names.c', 't_regression.c'] @@ -109,7 +115,8 @@ libhammer_static = None libhammer_shared = None if build_shared_library: - libhammer_shared = env.SharedLibrary('hammer', parsers + backends + misc_hammer_parts) + libhammer_shared = env.SharedLibrary('hammer', parsers + backends + misc_hammer_parts, \ + SHLIBVERSION=hammer_shlib_version) libhammer_static = env.StaticLibrary(static_library_name, parsers + backends + misc_hammer_parts) if libhammer_shared is not None: diff --git a/src/backends/glr.c b/src/backends/glr.c index 06722b1963ed51d4aefc095d7a39a30d10633396..db7c4b314da17a09df2cbf2836bc092123827621 100644 --- a/src/backends/glr.c +++ b/src/backends/glr.c @@ -1,5 +1,6 @@ #include <assert.h> #include "lr.h" +#include "params.h" static bool glr_step(HParseResult **result, HSlist *engines, HLREngine *engine, const HLRAction *action); @@ -241,12 +242,54 @@ HParseResult *h_glr_parse(HAllocator* mm__, const HParser* parser, HInputStream* return result; } +char * h_glr_get_description(HAllocator *mm__, + HParserBackend be, void *param) { + const char *backend_name = "GLR"; + size_t k; + char *descr = NULL; + k = h_get_param_k(param); + + descr = h_format_description_with_param_k(mm__, backend_name, k); + + return descr; +} + +char * h_glr_get_short_name(HAllocator *mm__, + HParserBackend be, void *param) { + const char *backend_name = "GLR"; + + size_t k; + char *name = NULL; + + k = h_get_param_k(param); + + name = h_format_name_with_param_k(mm__, backend_name, k); + + return name; +} + + +int h_glr_extract_params(HParserBackendWithParams * be_with_params, backend_with_params_t * be_with_params_t) { + + return h_extract_param_k(be_with_params, be_with_params_t); +} HParserBackendVTable h__glr_backend_vtable = { .compile = h_glr_compile, .parse = h_glr_parse, - .free = h_glr_free + .free = h_glr_free, + + .copy_params = h_copy_numeric_param, + /* No free_param needed, since it's not actually allocated */ + + /* Name/param resolution functions */ + .backend_short_name = "glr", + .backend_description = "GLR(k) parser backend", + .get_description_with_params = h_glr_get_description, + .get_short_name_with_params = h_glr_get_short_name, + + .extract_params = h_glr_extract_params }; diff --git a/src/backends/lalr.c b/src/backends/lalr.c index 4b2bcaf1085584220456f904acc452d9002c4974..5954fcaa8a5cf236d3091331819ffe5be4f3ee75 100644 --- a/src/backends/lalr.c +++ b/src/backends/lalr.c @@ -1,8 +1,8 @@ #include <assert.h> #include "contextfree.h" #include "lr.h" +#include "params.h" -static const size_t DEFAULT_K = 1; /* LALR-via-SLR grammar transformation */ @@ -275,7 +275,7 @@ HCFChoice *h_desugar_augmented(HAllocator *mm__, HParser *parser) int h_lalr_compile(HAllocator* mm__, HParser* parser, const void* params) { - size_t k = params? (uintptr_t)params : DEFAULT_K; + size_t k = params? (uintptr_t)params : DEFAULT_KMAX; // generate (augmented) CFG from parser // construct LR(0) DFA // build LR(0) table @@ -370,10 +370,43 @@ void h_lalr_free(HParser *parser) HLRTable *table = parser->backend_data; h_lrtable_free(table); parser->backend_data = NULL; - parser->backend = PB_PACKRAT; + parser->backend_vtable = h_get_default_backend_vtable(); + parser->backend = h_get_default_backend(); } +char * h_lalr_get_description(HAllocator *mm__, + HParserBackend be, void *param) { + const char *backend_name = "LALR"; + size_t k; + char *descr = NULL; + k = h_get_param_k(param); + + descr = h_format_description_with_param_k(mm__, backend_name, k); + + return descr; +} + +char * h_lalr_get_short_name(HAllocator *mm__, + HParserBackend be, void *param) { + const char *backend_name = "LALR"; + + size_t k; + char *name = NULL; + + k = h_get_param_k(param); + + name = h_format_name_with_param_k(mm__, backend_name, k); + + return name; +} + + +int h_lalr_extract_params(HParserBackendWithParams * be_with_params, backend_with_params_t * be_with_params_t) { + + return h_extract_param_k(be_with_params, be_with_params_t); + +} HParserBackendVTable h__lalr_backend_vtable = { .compile = h_lalr_compile, @@ -381,11 +414,18 @@ HParserBackendVTable h__lalr_backend_vtable = { .free = h_lalr_free, .parse_start = h_lr_parse_start, .parse_chunk = h_lr_parse_chunk, - .parse_finish = h_lr_parse_finish -}; - + .parse_finish = h_lr_parse_finish, + .copy_params = h_copy_numeric_param, + /* No free_param needed, since it's not actually allocated */ + /* Name/param resolution functions */ + .backend_short_name = "lalr", + .backend_description = "LALR(k) parser backend", + .get_description_with_params = h_lalr_get_description, + .get_short_name_with_params = h_lalr_get_short_name, + .extract_params = h_lalr_extract_params +}; // dummy! int test_lalr(void) diff --git a/src/backends/llk.c b/src/backends/llk.c index 19944a20930eef5fcb71efd3d055aebeb93e9cfa..812af5542594def30a8413576652b6130bb59fa7 100644 --- a/src/backends/llk.c +++ b/src/backends/llk.c @@ -2,8 +2,7 @@ #include "../internal.h" #include "../cfgrammar.h" #include "../parsers/parser_internal.h" - -static const size_t DEFAULT_KMAX = 1; +#include "params.h" /* Generating the LL(k) parse table */ @@ -254,7 +253,8 @@ void h_llk_free(HParser *parser) HLLkTable *table = parser->backend_data; h_llktable_free(table); parser->backend_data = NULL; - parser->backend = PB_PACKRAT; + parser->backend_vtable = h_get_default_backend_vtable(); + parser->backend = h_get_default_backend(); } @@ -606,6 +606,38 @@ HParseResult *h_llk_parse_finish(HSuspendedParser *s) return llk_parse_finish_(s->mm__, s->backend_state); } +char * h_llk_get_description(HAllocator *mm__, + HParserBackend be, void *param) { + const char *backend_name = "LL"; + size_t k, len; + char *descr = NULL; + + k = h_get_param_k(param); + + descr = h_format_description_with_param_k(mm__, backend_name, k); + + return descr; +} + +char * h_llk_get_short_name(HAllocator *mm__, + HParserBackend be, void *param) { + const char *backend_name = "LL"; + + size_t k; + char *name = NULL; + + k = h_get_param_k(param); + + name = h_format_name_with_param_k(mm__, backend_name, k); + + return name; +} + +int h_llk_extract_params(HParserBackendWithParams * be_with_params, backend_with_params_t *be_with_params_t) { + + return h_extract_param_k(be_with_params, be_with_params_t); +} + HParserBackendVTable h__llk_backend_vtable = { .compile = h_llk_compile, @@ -614,7 +646,19 @@ HParserBackendVTable h__llk_backend_vtable = { .parse_start = h_llk_parse_start, .parse_chunk = h_llk_parse_chunk, - .parse_finish = h_llk_parse_finish + .parse_finish = h_llk_parse_finish, + + .copy_params = h_copy_numeric_param, + /* No free_param needed, since it's not actually allocated */ + + /* Name/param resolution functions */ + .backend_short_name = "llk", + .backend_description = "LL(k) parser backend", + .get_description_with_params = h_llk_get_description, + .get_short_name_with_params = h_llk_get_short_name, + + /*extraction of params from string*/ + .extract_params = h_llk_extract_params }; diff --git a/src/backends/missing.c b/src/backends/missing.c new file mode 100644 index 0000000000000000000000000000000000000000..f159bf192329088fac3b5987d498d63f728fb9e6 --- /dev/null +++ b/src/backends/missing.c @@ -0,0 +1,25 @@ +#include "missing.h" + +/* Placeholder backend that always fails */ + +int h_missing_compile(HAllocator* mm__, HParser* parser, const void* params) { + /* Always fail */ + + return -1; +} + +HParseResult *h_missing_parse(HAllocator* mm__, const HParser* parser, HInputStream* stream) { + /* Always fail */ + + return NULL; +} + +void h_missing_free(HParser *parser) { + /* No-op */ +} + +HParserBackendVTable h__missing_backend_vtable = { + .compile = h_missing_compile, + .parse = h_missing_parse, + .free = h_missing_free, +}; diff --git a/src/backends/missing.h b/src/backends/missing.h new file mode 100644 index 0000000000000000000000000000000000000000..4efe5f350331a1dcc75dcd894bf1075e8f62bbd8 --- /dev/null +++ b/src/backends/missing.h @@ -0,0 +1,7 @@ +#ifndef HAMMER_BACKENDS_MISSING__H +#define HAMMER_BACKENDS_MISSING__H + +#include "../hammer.h" +#include "../internal.h" + +#endif /* !defined(HAMMER_BACKENDS_MISSING__H) */ diff --git a/src/backends/packrat.c b/src/backends/packrat.c index 0f7bf476ff8159a214b9b1b8d01728982aaf1307..e69e02b4dc5e522cf2ff7f28caac8536d7691fdd 100644 --- a/src/backends/packrat.c +++ b/src/backends/packrat.c @@ -265,13 +265,15 @@ HParseResult* h_do_parse(const HParser* parser, HParseState *state) { } int h_packrat_compile(HAllocator* mm__, HParser* parser, const void* params) { + parser->backend_vtable = &h__packrat_backend_vtable; parser->backend = PB_PACKRAT; return 0; // No compilation necessary, and everything should work // out of the box. } void h_packrat_free(HParser *parser) { - parser->backend = PB_PACKRAT; // revert to default, oh that's us + parser->backend_vtable = h_get_default_backend_vtable(); + parser->backend = h_get_default_backend(); } static uint32_t cache_key_hash(const void* key) { @@ -446,8 +448,12 @@ HParserBackendVTable h__packrat_backend_vtable = { .compile = h_packrat_compile, .parse = h_packrat_parse, .free = h_packrat_free, - .parse_start = h_packrat_parse_start, .parse_chunk = h_packrat_parse_chunk, - .parse_finish = h_packrat_parse_finish + .parse_finish = h_packrat_parse_finish, + /* Name/param resolution functions */ + .backend_short_name = "packrat", + .backend_description = "Packrat parser with Warth's recursion", + .get_description_with_params = h_get_description_with_no_params, + .get_short_name_with_params = h_get_short_name_with_no_params }; diff --git a/src/backends/regex.c b/src/backends/regex.c index 0337949f948e06f332bbbada27170c4e99fae2ef..4894a8805d34d95471819f7c6f219d9f8af18518 100644 --- a/src/backends/regex.c +++ b/src/backends/regex.c @@ -417,7 +417,8 @@ static void h_regex_free(HParser *parser) { h_free(prog->actions); h_free(prog); parser->backend_data = NULL; - parser->backend = PB_PACKRAT; + parser->backend_vtable = h_get_default_backend_vtable(); + parser->backend = h_get_default_backend(); } static int h_regex_compile(HAllocator *mm__, HParser* parser, const void* params) { @@ -452,7 +453,12 @@ static HParseResult *h_regex_parse(HAllocator* mm__, const HParser* parser, HInp HParserBackendVTable h__regex_backend_vtable = { .compile = h_regex_compile, .parse = h_regex_parse, - .free = h_regex_free + .free = h_regex_free, + /* Name/param resolution functions */ + .backend_short_name = "regex", + .backend_description = "Regular expression matcher (broken)", + .get_description_with_params = h_get_description_with_no_params, + .get_short_name_with_params = h_get_short_name_with_no_params }; #ifndef NDEBUG diff --git a/src/hammer.c b/src/hammer.c index a88374d70dbaa9742b44a85385ca38e8ab40f8cc..e19336760917623ae870cab8f2d1b614c73c60e7 100644 --- a/src/hammer.c +++ b/src/hammer.c @@ -24,13 +24,15 @@ #include "internal.h" #include "allocator.h" #include "parsers/parser_internal.h" +#include "glue.h" static HParserBackendVTable *backends[PB_MAX + 1] = { - &h__packrat_backend_vtable, - &h__regex_backend_vtable, - &h__llk_backend_vtable, - &h__lalr_backend_vtable, - &h__glr_backend_vtable, + &h__missing_backend_vtable, /* For PB_INVALID */ + &h__packrat_backend_vtable, /* For PB_PACKRAT */ + &h__regex_backend_vtable, /* For PB_REGULAR */ + &h__llk_backend_vtable, /* For PB_LLk */ + &h__lalr_backend_vtable, /* For PB_LALR */ + &h__glr_backend_vtable /* For PB_GLR */ }; @@ -41,6 +43,529 @@ typedef struct { const HParser *p2; } HTwoParsers; +/* Backend-related inquiries */ + +int h_is_backend_available(HParserBackend backend) { + if (backend >= PB_MIN && backend <= PB_MAX) { + return (backends[backend] != &h__missing_backend_vtable) ? 1 : 0; + } else return 0; +} + +HParserBackend h_get_default_backend(void) { + /* Call the inline version in internal.h */ + return h_get_default_backend__int(); +} + +HParserBackendVTable * h_get_default_backend_vtable(void){ + return h_get_default_backend_vtable__int(); +} + +/* + * Copy an HParserBackendWithParams, using the backend-supplied copy + * method. + */ + +HParserBackendWithParams * h_copy_backend_with_params( + HParserBackendWithParams *be_with_params) { + return h_copy_backend_with_params__m(&system_allocator, be_with_params); +} + +HParserBackendWithParams * h_copy_backend_with_params__m(HAllocator *mm__, + HParserBackendWithParams *be_with_params) { + HParserBackendWithParams *r = NULL; + int s; + + if (be_with_params && be_with_params->backend >= PB_MIN && + be_with_params->backend <= PB_MAX) { + if (mm__ == NULL) { + /* use the allocator from the input */ + mm__ = be_with_params->mm__; + } + + /* got an allocator? */ + if (mm__) { + r = h_new(HParserBackendWithParams, 1); + if (r) { + r->mm__ = mm__; + r->requested_name = be_with_params->requested_name; + r->backend = be_with_params->backend; + r->backend_vtable = be_with_params->backend_vtable; + if (be_with_params->backend_vtable != NULL + && be_with_params->backend_vtable->copy_params) { + s = be_with_params->backend_vtable->copy_params(mm__, + &(r->params), be_with_params->params); + if (s != 0) { + /* copy_params() failed */ + h_free(r); + r = NULL; + } + } else { + /* else just ignore it and set it to NULL */ + r->params = NULL; + } + } + /* else fail out */ + } + /* else give up and return NULL */ + } + /* else return NULL, nothing to copy */ + + return r; +} + +/* + * copy_params for backends where the parameter is actually a number cast to + * void *, such as LLk, LALR and GLR. Use NULL for free_params in this + * case. + */ + +int h_copy_numeric_param(HAllocator *mm__, void **out, void *in) { + int rv = 0; + + if (out) *out = in; + else rv = -1; /* error return, nowhere to write result */ + + return rv; +} + +/* Free this HParserBackendWithParams, and free the params too */ + +void h_free_backend_with_params(HParserBackendWithParams *be_with_params) { + HAllocator *mm__ = &system_allocator; + + if (be_with_params) { + if (be_with_params->mm__) mm__ = be_with_params->mm__; + + if (h_is_backend_available(be_with_params->backend)) { + if (be_with_params->backend_vtable != NULL + && be_with_params->backend_vtable->free_params) { + be_with_params->backend_vtable-> + free_params(be_with_params->mm__, be_with_params->params); + } + } + + if(be_with_params->requested_name != NULL) { + h_free(be_with_params->requested_name); + } + + h_free(be_with_params); + } +} + +/* + * Internal function to query name or descriptive text; the logic is + * the same in both cases. + */ + +static const char * h_get_string_for_backend( + HParserBackend be, int description) { + /* + * For names, failure to resolve should return null - those are + * cases we can't look up by name either. Human readable descriptions + * should have human-readable error strings + */ + const char *text = description ? "this is a bug!" : NULL; + const char **ptr; + + if (be >= PB_MIN && be <= PB_MAX) { + /* the invalid backend is special */ + if (be == PB_INVALID) { + text = description ? "invalid backend" : NULL; + } else { + if (backends[be] != backends[PB_INVALID]) { + /* Point to the name or description */ + ptr = description ? + &(backends[be]->backend_description) : + &(backends[be]->backend_short_name); + /* See if the backend knows how to describe itself */ + if (*ptr != NULL) { + text = *ptr; + } else { + /* nope */ + text = description ? "no description available" : NULL; + } + } else { + /* + * This is the case for backends which weren't compiled into this + * library. + */ + text = description ? "unsupported backend" : NULL; + } + } + } else { + text = description ? "bad backend number" : NULL; + } + + return text; +} + +/* Return an identifying name for this backend */ + +const char * h_get_name_for_backend(HParserBackend be) { + /* + * call h_get_string_for_backend(), 0 indicates name + * rather than description requested + */ + return h_get_string_for_backend(be, 0); +} + +/* + * Return some human-readable descriptive text for this backend + */ + +const char * h_get_descriptive_text_for_backend(HParserBackend be) { + /* + * call h_get_string_for_backend(), 1 indicates a + * description is requested. + */ + return h_get_string_for_backend(be, 1); +} + +/* + * Internal function to query name or descriptive text for a backend with + * params; the logic is the same in both cases. + */ + +static char * h_get_string_for_backend_with_params__m(HAllocator *mm__, + HParserBackendWithParams *be_with_params, int description) { + char *text = NULL; + const char *generic_text = NULL; + HParserBackend be = PB_INVALID; + char * (**text_getter_func)(HAllocator *mm__, + HParserBackend be, + void *params) = NULL; + + + if (mm__ == NULL || be_with_params == NULL) goto done; + + /* check if we can compute custom text with params for this backend */ + be = be_with_params->backend; + if (be >= PB_MIN && be <= PB_MAX && be != PB_INVALID && + backends[be] != backends[PB_INVALID]) { + text_getter_func = description ? + &(backends[be]->get_description_with_params) : + &(backends[be]->get_short_name_with_params); + + if (*text_getter_func != NULL) { + text = (*text_getter_func)(mm__, be, + be_with_params->params); + } + } + + /* got it? */ + if (!text) { + /* fall back to the generic descriptive text */ + generic_text = h_get_string_for_backend(be, description); + if (generic_text) { + text = h_new(char, strlen(generic_text) + 1); + strncpy(text, generic_text, strlen(generic_text) + 1); + } + } + + done: + return text; +} +/* Allocate and return an identifying name for this backend + * with parameters. The caller is responsible for freeing the result. + */ +char * h_get_name_for_backend_with_params__m(HAllocator *mm__, + HParserBackendWithParams *be_with_params) { + return h_get_string_for_backend_with_params__m(mm__, be_with_params, 0); +} + +char * h_get_name_for_backend_with_params(HParserBackendWithParams *be_with_params) { + return h_get_name_for_backend_with_params__m(&system_allocator, be_with_params); +} +/* + * Allocate and return some human-readable descriptive text for this backend + * with parameters. The caller is responsible for freeing the result. + */ + +char * h_get_descriptive_text_for_backend_with_params__m(HAllocator *mm__, + HParserBackendWithParams *be_with_params) { + return h_get_string_for_backend_with_params__m(mm__, be_with_params, 1); +} + +char * h_get_descriptive_text_for_backend_with_params( + HParserBackendWithParams *be_with_params) { + return h_get_descriptive_text_for_backend_with_params__m( + &system_allocator, be_with_params); +} + +/* Helpers for the above for backends with no params */ + +static char * h_get_backend_text_with_no_params(HAllocator *mm__, + HParserBackend be, + int description) { + char *text = NULL; + const char *src = NULL; + int size; + + if (!(mm__ != NULL && be != PB_INVALID && + be >= PB_MIN && be <= PB_MAX)) goto done; + + src = description ? + backends[be]->backend_description : + backends[be]->backend_short_name; + + if (src) { + size = strlen(src) + 1; + text = h_new(char, size); + if (text) strncpy(text, src, size); + } + + done: + return text; +} + +/* Query a backend by short name; return PB_INVALID if no match */ + +HParserBackend h_query_backend_by_name(const char *name) { + HParserBackend result = PB_INVALID, i; + + if (name != NULL) { + /* Okay, iterate over the backends PB_MIN <= i <= PB_MAX and check */ + i = PB_MIN; + do { + if (i != PB_INVALID) { + if (backends[i]->backend_short_name != NULL) { + if (strcmp(name, backends[i]->backend_short_name) == 0) { + result = i; + } + } + } + ++i; + } while (i <= PB_MAX && result == PB_INVALID); + } + + return result; +} + +char * h_get_description_with_no_params(HAllocator *mm__, + HParserBackend be, void *params) { + return h_get_backend_text_with_no_params(mm__, be, 1); +} + +char * h_get_short_name_with_no_params(HAllocator *mm__, + HParserBackend be, void *params) { + return h_get_backend_text_with_no_params(mm__, be, 0); +} + + +/*TODO: possibly move to its own file? + * If so, move include of glue.h as well + * this parser is the only code using glue.h in here + */ + +HParsedToken *act_backend_with_params(const HParseResult *p, void* user_data) +{ + backend_with_params_t *be_with_params = H_ALLOC(backend_with_params_t); + + backend_name_t *name = H_FIELD(backend_name_t, 0); + be_with_params->name = *name; + + HParsedToken * param_list = p->ast->seq->elements[1]; + if (param_list->token_type == TT_SEQUENCE && param_list->seq->used == 3) { + backend_params_t *params = H_INDEX(backend_params_t, param_list, 1); + be_with_params->params = *params; + } + + return H_MAKE(backend_with_params_t, (void*)be_with_params); +} + +HParsedToken* act_backend_name(const HParseResult *p, void* user_data) { + backend_name_t *r = H_ALLOC(backend_name_t); + + HParsedToken *flat = h_act_flatten(p, user_data); + + r->len = h_seq_len(flat); + r->name = h_arena_malloc(p->arena, r->len + 1); + for (size_t i=0; i<r->len; ++i) { + + r->name[i] = flat->seq->elements[i]->uint; + } + r->name[r->len] = 0; + + return H_MAKE(backend_name_t, r); +} + +HParsedToken *act_param(const HParseResult *p, void* user_data) +{ + backend_param_t *r = H_ALLOC(backend_param_t); + + r->len = h_seq_len(p->ast); + r->param = h_arena_malloc(p->arena, r->len + 1); + for (size_t i=0; i<r->len; ++i) + r->param[i] = H_FIELD_UINT(i); + r->param[r->len] = 0; + + return H_MAKE(backend_param_t, r); + +} + +HParsedToken *act_param_name(const HParseResult *p, void* user_data) +{ + backend_param_name_t *r = H_ALLOC(backend_param_name_t); + + HParsedToken *flat = h_act_flatten(p, user_data); + + r->len = h_seq_len(flat); + r->param_name = h_arena_malloc(p->arena, r->len + 1); + for (size_t i=0; i<r->len; ++i) + r->param_name[i] = flat->seq->elements[i]->uint; + r->param_name[r->len] = 0; + + return H_MAKE(backend_param_name_t, r); + +} + +HParsedToken *act_param_with_name(const HParseResult *p, void* user_data) +{ + backend_param_with_name_t *backend_param_with_name = H_ALLOC(backend_param_with_name_t); + + HParsedToken *tok = p->ast->seq->elements[0]; + + if (tok->token_type == TT_SEQUENCE) { + backend_param_name_t *param_name = H_INDEX(backend_param_name_t, tok, 0); + backend_param_with_name->param_name = *param_name; + } + + backend_param_t *param = H_FIELD(backend_param_t, 1); + backend_param_with_name->param = *param; + + return H_MAKE(backend_param_with_name_t, backend_param_with_name); +} + +HParsedToken *act_backend_params(const HParseResult *p, void* user_data) +{ + HParsedToken *res = H_MAKE(backend_params_t, (void*)p->ast); + + backend_params_t *bp = H_ALLOC(backend_params_t); + + HParsedToken **fields = h_seq_elements(p->ast); + + bp->len = h_seq_len(p->ast); + bp->params = h_arena_malloc(p->arena, sizeof(backend_param_with_name_t)*bp->len); + for(size_t i=0; i<bp->len; i++) { + bp->params[i] = *H_INDEX(backend_param_with_name_t, p->ast, i); + } + + return H_MAKE(backend_params_t, bp); + + return res; +} + +static HParser * build_hparser_rule(void) { + + H_RULE(alpha, h_choice(h_ch_range('A', 'Z'), h_ch_range('a', 'z'), NULL)); + H_RULE(digit, h_ch_range(0x30, 0x39)); + H_RULE(sp, h_ch(' ')); + H_RULE(eq, h_ch('=')); + H_RULE(comma, h_ch(',')); + H_RULE(sep, h_sequence(comma, h_optional(sp), NULL)); + H_RULE(left_paren, h_ch('(')); + H_RULE(right_paren, h_ch(')')); + + H_RULE(alphas, h_many1(alpha)); + H_RULE(digits, h_many1(digit)); + + H_ARULE(backend_name, h_sequence( + alphas, + h_many(h_choice(digit, alpha, NULL)), + NULL)); + H_ARULE(param, h_choice(digits, alphas, NULL)); + H_ARULE(param_name, h_sequence(alphas, h_optional(digits), NULL)); + H_ARULE(param_with_name, + h_sequence(h_optional(h_sequence( + param_name, eq, NULL)), param, NULL)); + H_ARULE(backend_params, h_sepBy(param_with_name, sep)); + + H_RULE(param_list, h_sequence(left_paren, backend_params, right_paren, NULL) ); + + H_ARULE(backend_with_params, h_sequence(backend_name, h_optional(param_list), NULL)); + + return backend_with_params; +} + + + +static HParser * build_hparser(void) { + + HParser *p = NULL; + int r; + p = build_hparser_rule(); + r = h_compile(p, PB_PACKRAT, NULL); + + if (r == 0) { + return p; + } else { + printf("Compiling parser failed\n"); + return NULL; + } +} + +HParserBackendWithParams * h_get_backend_with_params_by_name(const char *name_with_params) { + HAllocator *mm__ = &system_allocator; + HParserBackendWithParams *result = NULL; + + HParser *parser = NULL; + HParseResult *r = NULL; + + if(name_with_params != NULL) { + result = h_new(HParserBackendWithParams, 1); + if (result) { + result->mm__ = mm__; + result->requested_name = NULL; + + parser = build_hparser(); + if (!parser) { + return NULL; + } + + r = h_parse(parser, (const uint8_t *)name_with_params, strlen(name_with_params)); + + if (r) { + + backend_with_params_t *be_w_params = r->ast->user; + + + backend_name_t *name = &be_w_params->name; + + backend_params_t *params = &be_w_params->params; + + result->requested_name = h_new(char,name->len+1); + memset(result->requested_name, '\0', name->len+1); + strncpy(result->requested_name, (char*) name->name, name->len); + + result->backend = h_query_backend_by_name(result->requested_name); + + + + if (result->backend >= PB_MIN && result->backend <= PB_MAX) + result->backend_vtable = backends[result->backend]; + else + result->backend_vtable = backends[PB_INVALID]; + + /* use the backend supplied method to extract any params from the input */ + result->params = NULL; + if(params->len > 0) { + if (result->backend_vtable->extract_params) { + result->backend_vtable->extract_params(result, be_w_params); + } + } + // free the parse result + h_parse_result_free(r); + r = NULL; + + //free the memory used by the backend for parser data + //(TODO: code to completely free the memory used for this parser) + parser->backend_vtable->free(parser); + } + } + } + + return result; +} HParseResult* h_parse(const HParser* parser, const uint8_t* input, size_t length) { @@ -58,8 +583,8 @@ HParseResult* h_parse__m(HAllocator* mm__, const HParser* parser, const uint8_t* .input = input, .last_chunk = true }; - - return backends[parser->backend]->parse(mm__, parser, &input_stream); + + return parser->backend_vtable->parse(mm__, parser, &input_stream); } void h_parse_result_free__m(HAllocator *alloc, HParseResult *result) { @@ -86,15 +611,37 @@ bool h_not_regular(HRVMProg *prog, void *env) { return false; } +int h_compile_for_backend_with_params(HParser* parser, HParserBackendWithParams* be_with_params){ + HAllocator *mm__ = &system_allocator; + + if (be_with_params) { + if (be_with_params->mm__) mm__ = be_with_params->mm__; + } + + return h_compile_for_backend_with_params__m(mm__, parser, be_with_params); +} + +int h_compile_for_backend_with_params__m(HAllocator* mm__, HParser* parser, HParserBackendWithParams* be_with_params) { + int ret = h_compile__m(mm__, parser, be_with_params->backend, be_with_params->params); + if (!ret) + be_with_params->backend_vtable = parser->backend_vtable; + return ret; +} + int h_compile(HParser* parser, HParserBackend backend, const void* params) { return h_compile__m(&system_allocator, parser, backend, params); } int h_compile__m(HAllocator* mm__, HParser* parser, HParserBackend backend, const void* params) { - backends[parser->backend]->free(parser); + if (parser->backend >= PB_MIN && parser->backend <= PB_MAX && + backends[parser->backend]->free != NULL) { + backends[parser->backend]->free(parser); + } int ret = backends[backend]->compile(mm__, parser, params); - if (!ret) + if (!ret) { parser->backend = backend; + parser->backend_vtable = backends[backend]; + } return ret; } @@ -103,7 +650,7 @@ HSuspendedParser* h_parse_start(const HParser* parser) { return h_parse_start__m(&system_allocator, parser); } HSuspendedParser* h_parse_start__m(HAllocator* mm__, const HParser* parser) { - if(!backends[parser->backend]->parse_start) + if(!parser->backend_vtable || !parser->backend_vtable->parse_start) return NULL; // allocate and init suspended state @@ -120,13 +667,13 @@ HSuspendedParser* h_parse_start__m(HAllocator* mm__, const HParser* parser) { // backend-specific initialization // should allocate s->backend_state - backends[parser->backend]->parse_start(s); + parser->backend_vtable->parse_start(s); return s; } bool h_parse_chunk(HSuspendedParser* s, const uint8_t* input, size_t length) { - assert(backends[s->parser->backend]->parse_chunk != NULL); + assert(s->parser->backend_vtable->parse_chunk != NULL); // no-op if parser is already done if(s->done) @@ -145,7 +692,7 @@ bool h_parse_chunk(HSuspendedParser* s, const uint8_t* input, size_t length) { }; // process chunk - s->done = backends[s->parser->backend]->parse_chunk(s, &input_stream); + s->done = s->parser->backend_vtable->parse_chunk(s, &input_stream); s->endianness = input_stream.endianness; s->pos += input_stream.index; s->bit_offset = input_stream.bit_offset; @@ -154,8 +701,8 @@ bool h_parse_chunk(HSuspendedParser* s, const uint8_t* input, size_t length) { } HParseResult* h_parse_finish(HSuspendedParser* s) { - assert(backends[s->parser->backend]->parse_chunk != NULL); - assert(backends[s->parser->backend]->parse_finish != NULL); + assert(s->parser->backend_vtable->parse_chunk != NULL); + assert(s->parser->backend_vtable->parse_finish != NULL); HAllocator *mm__ = s->mm__; @@ -172,12 +719,12 @@ HParseResult* h_parse_finish(HSuspendedParser* s) { .last_chunk = true }; - s->done = backends[s->parser->backend]->parse_chunk(s, &empty); + s->done = s->parser->backend_vtable->parse_chunk(s, &empty); assert(s->done); } // extract result - HParseResult *r = backends[s->parser->backend]->parse_finish(s); + HParseResult *r = s->parser->backend_vtable->parse_finish(s); if(r) r->bit_length = s->pos * 8 + s->bit_offset; diff --git a/src/hammer.h b/src/hammer.h index 787af0b254a969226eeda985fc75d470796bd7cc..d84497d9a7f1ce302efea3cedea710f23834331e 100644 --- a/src/hammer.h +++ b/src/hammer.h @@ -41,7 +41,12 @@ typedef struct HParseState_ HParseState; typedef enum HParserBackend_ { PB_MIN = 0, - PB_PACKRAT = PB_MIN, // PB_MIN is always the default. + /* + * Have a backend that always fails to pass around "no such backend" + * indications + */ + PB_INVALID = PB_MIN, + PB_PACKRAT, PB_REGULAR, PB_LLk, PB_LALR, @@ -49,6 +54,26 @@ typedef enum HParserBackend_ { PB_MAX = PB_GLR } HParserBackend; +typedef struct HParserBackendVTable_ HParserBackendVTable; + +typedef struct HParserBackendWithParams_ { + /* Name of backend extracted from a string if the choice of backend was specified in a call using a string */ + char *requested_name; + /* The backend (if backend is to be loaded from an external module set to invalid (?))*/ + HParserBackend backend; + /* Backend vtable (TODO: use this instead of the enum so we can get rid of that) */ + HParserBackendVTable * backend_vtable; + /* + * Backend-specific parameters - if this needs to be freed, the backend + * should provide a free_params method in its vtable; currently no backends + * do this - PB_PACKRAT and PB_REGULAR take no params, and PB_LLk, PB_LALR + * and PB_GLR take an integer cast to void * + */ + void *params; + /* Allocator to use to free this (and the params if necessary) */ + HAllocator *mm__; +} HParserBackendWithParams; + typedef enum HTokenType_ { // Before you change the explicit values of these, think of the poor bindings ;_; TT_INVALID = 0, @@ -137,6 +162,7 @@ typedef struct HParserVtable_ HParserVtable; typedef struct HParser_ { const HParserVtable *vtable; HParserBackend backend; + HParserBackendVTable * backend_vtable; void* backend_data; void *env; HCFChoice *desugared; /* if the parser can be desugared, its desugared form */ @@ -175,6 +201,53 @@ typedef bool (*HPredicate)(HParseResult *p, void* user_data); */ typedef HParser* (*HContinuation)(HAllocator *mm__, const HParsedToken *x, void *env); +/* + * For parser used when extracting name and params for backend by name + * TODO: possibly move to its own file? + */ + +enum BackendTokenType_ { + TT_backend_with_params_t = TT_USER, + TT_backend_name_t, + TT_backend_param_t, + TT_backend_param_name_t, + TT_backend_param_with_name_t, + TT_backend_params_t +}; + +typedef struct backend_param { + size_t len; + uint8_t *param; + uint8_t *param_name; +} backend_param_t; + +typedef struct backend_param_name { + size_t len; + uint8_t *param_name; + size_t param_id; +} backend_param_name_t; + +typedef struct backend_param_with_name { + backend_param_name_t param_name; + backend_param_t param; +} backend_param_with_name_t; + +typedef struct { + uint8_t *name; + size_t len; +} backend_name_t; + +typedef struct backend_params { + backend_param_with_name_t *params; + size_t len; +} backend_params_t; + +typedef struct backend_with_params { + backend_name_t name; + backend_params_t params; +} backend_with_params_t; + + // {{{ Stuff for benchmarking typedef struct HParserTestcase_ { unsigned char* input; @@ -262,6 +335,89 @@ typedef struct HBenchmarkResults_ { #endif // SWIG // }}} +/** + * Ask if this backend is available + */ + +int h_is_backend_available(HParserBackend backend); + +/** + * Ask what the default backend is (currently always PB_PACKRAT) + */ + +HParserBackend h_get_default_backend(void); + +HParserBackendVTable * h_get_default_backend_vtable(void); + +/** + * Copy a backend+params, using the backend-supplied copy method; the + * allocator used is the one passed in, or call the __m version with + * a NULL allocator to use the one from the source HParserBackendWithParams + */ + +HAMMER_FN_DECL(HParserBackendWithParams *, h_copy_backend_with_params, + HParserBackendWithParams *be_with_params); + +/** + * Free a backend+params + */ + +void h_free_backend_with_params(HParserBackendWithParams *be_with_params); + +/** + * Get a name string for a backend; this is constant per backend and so + * need not be freed; it will resolve to the backend under + * h_get_backend_by_name(). + */ + +const char * h_get_name_for_backend(HParserBackend be); + +/** + * Get a name string for a backend with parameters; it is the caller's + * responsibility to free it later. This will resolve to the same + * backend and parameters under h_get_backend_with_params_by_name(). + */ + +HAMMER_FN_DECL(char *, h_get_name_for_backend_with_params, + HParserBackendWithParams *be_with_params); + +/** + * Get a human-readable descriptive string for a backend; this is constant + * per backend and so need not be freed. + */ + +const char * h_get_descriptive_text_for_backend(HParserBackend be); + +/** + * Get a human-readable descriptive string for a backend with params; it is + * the caller's responsibility to free it later. Sorry, but it's allowed + * to depend on the params and putting keeping the buffer elsewhere and + * replacing it on the next call wouldn't be thread-safe. + */ + +HAMMER_FN_DECL(char *, h_get_descriptive_text_for_backend_with_params, + HParserBackendWithParams *be_with_params); + +/** + * Look up an HParserBackend by name; this should round-trip with + * h_get_name_for_backend(). + */ + +HParserBackend h_query_backend_by_name(const char *name); + +/** + * Get a Hammer Backend with params from a string of the form + * backend_name(params) for example "lalr(1)". + * + * If the backend is one of the existing backends in the HBackend enum, + * HBackend will be populated in the result. + * + * Otherwise the result will save the name for use in attempts later at + * loading the named module. + * + */ + +HAMMER_FN_DECL(HParserBackendWithParams *, h_get_backend_with_params_by_name, const char *name_with_params); /** * Top-level function to call a parser that has been built over some @@ -795,6 +951,8 @@ void h_pprintln(FILE* stream, const HParsedToken* tok); * * Consult each backend for details. */ +HAMMER_FN_DECL(int, h_compile_for_backend_with_params, HParser* parser, HParserBackendWithParams *be_with_params); + HAMMER_FN_DECL(int, h_compile, HParser* parser, HParserBackend backend, const void* params); /** diff --git a/src/internal.h b/src/internal.h index 203e34126f225837a6d6b0e77d1267356c5b7998..a53c5914ad9ee2d5996dfe6c2d76aa51242ef43b 100644 --- a/src/internal.h +++ b/src/internal.h @@ -240,6 +240,33 @@ typedef struct HParserBackendVTable_ { HParseResult *(*parse_finish)(HSuspendedParser *s); // parse_finish must free s->backend_state. // parse_finish will not be called before parse_chunk reports done. + + /* The backend knows how to free its params */ + void (*free_params)(HAllocator *mm__, void *p); + /* + * ..and how to copy them + * + * Since the backend params need not actually be an allocated object, + * (and in fact no current backends use this, although it is permissible), + * but might (as in PB_GLR) be some numeric constant cast to void * which + * copy_params() should just pass through, we can't use returning NULL + * to signal allocation failure. Hence, passing the result out in a + * void ** and returning a status code (0 indicates success). + */ + int (*copy_params)(HAllocator *mm__, void **out, void *in); + + /* Description/name handling */ + const char *backend_short_name; + const char *backend_description; + char * (*get_description_with_params)(HAllocator *mm__, + HParserBackend be, + void *params); + char * (*get_short_name_with_params)(HAllocator *mm__, + HParserBackend be, + void *params); + + /* extract params from the input string */ + int (*extract_params)(HParserBackendWithParams * be_with_params, backend_with_params_t *be_with_params_t); } HParserBackendVTable; @@ -320,6 +347,7 @@ struct HBitWriter_ { // Backends {{{ +extern HParserBackendVTable h__missing_backend_vtable; extern HParserBackendVTable h__packrat_backend_vtable; extern HParserBackendVTable h__llk_backend_vtable; extern HParserBackendVTable h__lalr_backend_vtable; @@ -328,6 +356,16 @@ extern HParserBackendVTable h__glr_backend_vtable; // TODO(thequux): Set symbol visibility for these functions so that they aren't exported. +/* + * Helper functions for backend with params names and descriptions for + * backends which take no params. + */ + +char * h_get_description_with_no_params(HAllocator *mm__, + HParserBackend be, void *params); +char * h_get_short_name_with_no_params(HAllocator *mm__, + HParserBackend be, void *params); + int64_t h_read_bits(HInputStream* state, int count, char signed_p); void h_skip_bits(HInputStream* state, size_t count); void h_seek_bits(HInputStream* state, size_t pos); @@ -345,12 +383,38 @@ static inline size_t h_input_stream_length(HInputStream *state) { HParseResult* h_do_parse(const HParser* parser, HParseState *state); void put_cached(HParseState *ps, const HParser *p, HParseResult *cached); +/* + * Inline this for benefit of h_new_parser() below, then make + * the API h_get_default_backend() call it. + */ +static inline HParserBackend h_get_default_backend__int(void) { + return PB_PACKRAT; +} + +static inline HParserBackendVTable * h_get_default_backend_vtable__int(void) { + return &h__packrat_backend_vtable; +} + +static inline HParserBackendVTable * h_get_missing_backend_vtable__int(void) { + return &h__missing_backend_vtable; +} + +/* copy_params for backends where the parameter is not actually a pointer */ + +int h_copy_numeric_param(HAllocator *mm__, void **out, void *in); + static inline HParser *h_new_parser(HAllocator *mm__, const HParserVtable *vt, void *env) { HParser *p = h_new(HParser, 1); memset(p, 0, sizeof(HParser)); p->vtable = vt; p->env = env; + /* + * Current limitation: if we specify backends solely by HParserBackend, we + * can't set a default backend that requires any parameters to h_compile() + */ + p->backend = h_get_default_backend__int(); + p->backend_vtable = h_get_default_backend_vtable__int(); return p; } diff --git a/src/parsers/choice.c b/src/parsers/choice.c index 20bb7b79e6c5a214cf8653fc782968028e6692d9..882ecbf85162c6ad11493141a0a406f9dd5c2a29 100644 --- a/src/parsers/choice.c +++ b/src/parsers/choice.c @@ -165,7 +165,8 @@ HParser* h_choice__ma(HAllocator* mm__, void *args[]) { HParser *ret = h_new(HParser, 1); ret->vtable = &choice_vt; ret->env = (void*)s; - ret->backend = PB_MIN; + ret->backend = h_get_default_backend(); + ret->backend_vtable = h_get_default_backend_vtable(); ret->desugared = NULL; return ret; } diff --git a/src/parsers/epsilon.c b/src/parsers/epsilon.c index be614489cecfec6f30e4c2bfdd18c323be894446..849015a014bbd471f92047e8268d4b19ce346561 100644 --- a/src/parsers/epsilon.c +++ b/src/parsers/epsilon.c @@ -29,7 +29,8 @@ HParser* h_epsilon_p__m(HAllocator* mm__) { HParser *epsilon_p = h_new(HParser, 1); epsilon_p->desugared = NULL; epsilon_p->backend_data = NULL; - epsilon_p->backend = 0; + epsilon_p->backend = h_get_default_backend(); + epsilon_p->backend_vtable = h_get_default_backend_vtable(); epsilon_p->vtable = &epsilon_vt; return epsilon_p; } diff --git a/src/parsers/permutation.c b/src/parsers/permutation.c index 07dcaa71267d8b455dc0be00e0cdf7c0cbc542e6..f6f170a6093001d0d0c9941c4885e237445ddee2 100644 --- a/src/parsers/permutation.c +++ b/src/parsers/permutation.c @@ -181,7 +181,8 @@ HParser* h_permutation__ma(HAllocator* mm__, void *args[]) { HParser *ret = h_new(HParser, 1); ret->vtable = &permutation_vt; ret->env = (void*)s; - ret->backend = PB_MIN; + ret->backend = h_get_default_backend(); + ret->backend_vtable = h_get_default_backend_vtable(); ret->desugared = NULL; return ret; } diff --git a/src/parsers/sequence.c b/src/parsers/sequence.c index 2e7b4bc7286ec0ac32af012126e4289226297be0..e532fd43529e327383c5fdb691381e9fe6610c30 100644 --- a/src/parsers/sequence.c +++ b/src/parsers/sequence.c @@ -174,7 +174,8 @@ HParser* h_sequence__ma(HAllocator* mm__, void *args[]) { HParser *ret = h_new(HParser, 1); ret->vtable = &sequence_vt; ret->env = (void*)s; - ret->backend = PB_MIN; + ret->backend = h_get_default_backend(); + ret->backend_vtable = h_get_default_backend_vtable(); ret->desugared = NULL; return ret; } diff --git a/src/t_names.c b/src/t_names.c new file mode 100644 index 0000000000000000000000000000000000000000..85fbd92ed1dcabcfe4cac98d8694472008dc9e87 --- /dev/null +++ b/src/t_names.c @@ -0,0 +1,197 @@ +#include <glib.h> +#include <string.h> +#include "test_suite.h" +#include "hammer.h" + +static void test_tt_backend_description(void) { + const char *desc = NULL; + + /* Check that we get some */ + desc = h_get_descriptive_text_for_backend(PB_PACKRAT); + g_check_cmp_ptr(desc, !=, NULL); + desc = h_get_descriptive_text_for_backend(PB_REGULAR); + g_check_cmp_ptr(desc, !=, NULL); + desc = h_get_descriptive_text_for_backend(PB_LLk); + g_check_cmp_ptr(desc, !=, NULL); + desc = h_get_descriptive_text_for_backend(PB_LALR); + g_check_cmp_ptr(desc, !=, NULL); + desc = h_get_descriptive_text_for_backend(PB_GLR); + g_check_cmp_ptr(desc, !=, NULL); +} + +/* Reference backend names */ +static const char * packrat_name = "packrat"; +static const char * regular_name = "regex"; +static const char * llk_name = "llk"; +static const char * lalr_name = "lalr"; +static const char * glr_name = "glr"; + +static void test_tt_backend_short_name(void) { + const char *name = NULL; + + name = h_get_name_for_backend(PB_PACKRAT); + g_check_maybe_string_eq(name, packrat_name); + name = h_get_name_for_backend(PB_REGULAR); + g_check_maybe_string_eq(name, regular_name); + name = h_get_name_for_backend(PB_LLk); + g_check_maybe_string_eq(name, llk_name); + name = h_get_name_for_backend(PB_LALR); + g_check_maybe_string_eq(name, lalr_name); + name = h_get_name_for_backend(PB_GLR); + g_check_maybe_string_eq(name, glr_name); +} + +static void test_tt_query_backend_by_name(void) { + HParserBackend be; + + be = h_query_backend_by_name(packrat_name); + g_check_inttype("%d", HParserBackend, be, ==, PB_PACKRAT); + be = h_query_backend_by_name(regular_name); + g_check_inttype("%d", HParserBackend, be, ==, PB_REGULAR); + +} + +static void test_tt_get_backend_with_params_by_name(void) { + HParserBackendWithParams * be_w_p = NULL; + + /*requests to use default params, or for backends without params*/ + be_w_p = h_get_backend_with_params_by_name(packrat_name); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_PACKRAT); + g_check_maybe_string_eq(be_w_p->requested_name, packrat_name); + h_free_backend_with_params(be_w_p); + + be_w_p = h_get_backend_with_params_by_name(glr_name); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_GLR); + g_check_maybe_string_eq(be_w_p->requested_name, glr_name); + h_free_backend_with_params(be_w_p); + + /* request with params */ + be_w_p = h_get_backend_with_params_by_name("lalr(1)"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_LALR); + g_check_maybe_string_eq(be_w_p->requested_name, lalr_name); + g_check_cmp_size((uintptr_t)be_w_p->params, ==, 1); + h_free_backend_with_params(be_w_p); + + /*request for default params - alternative possible style */ + be_w_p = h_get_backend_with_params_by_name("glr()"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_GLR); + g_check_maybe_string_eq(be_w_p->requested_name, glr_name);; + h_free_backend_with_params(be_w_p); + + /*request for a backend not in the enum of backends included in hammer */ + be_w_p = h_get_backend_with_params_by_name("llvm()"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_INVALID); + g_check_maybe_string_eq(be_w_p->requested_name, "llvm"); + h_free_backend_with_params(be_w_p); + + be_w_p = h_get_backend_with_params_by_name("packrat(0)"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_PACKRAT); + g_check_maybe_string_eq(be_w_p->requested_name, packrat_name); + h_free_backend_with_params(be_w_p); + + be_w_p = h_get_backend_with_params_by_name("glr(1,2)"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_GLR); + g_check_maybe_string_eq(be_w_p->requested_name, glr_name); + g_check_cmp_size((uintptr_t)be_w_p->params, ==, 1); + h_free_backend_with_params(be_w_p); + + be_w_p = h_get_backend_with_params_by_name("glr(1,vnvnvn)"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_GLR); + g_check_maybe_string_eq(be_w_p->requested_name, glr_name); + g_check_cmp_size((uintptr_t)be_w_p->params, ==, 1); + h_free_backend_with_params(be_w_p); + + be_w_p = h_get_backend_with_params_by_name("lalr(1, 2)"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_LALR); + g_check_maybe_string_eq(be_w_p->requested_name, lalr_name); + g_check_cmp_size((uintptr_t)be_w_p->params, ==, 1); + h_free_backend_with_params(be_w_p); + + be_w_p = h_get_backend_with_params_by_name("llk(k=2)"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_LLk); + g_check_maybe_string_eq(be_w_p->requested_name, llk_name); + g_check_cmp_size((uintptr_t)be_w_p->params, ==, 2); + h_free_backend_with_params(be_w_p); + +} + +static void test_tt_h_get_descriptive_text_for_backend_with_params(void){ + HAllocator *mm__ = &system_allocator; + HParserBackendWithParams *be_with_params = h_get_backend_with_params_by_name("llk(1)"); + char * descr = h_get_descriptive_text_for_backend_with_params(be_with_params); + g_check_maybe_string_eq(descr, "LL(1) parser backend"); + h_free(descr); + h_free_backend_with_params(be_with_params); + be_with_params = h_get_backend_with_params_by_name("lalr(1)"); + descr = h_get_descriptive_text_for_backend_with_params(be_with_params); + g_check_maybe_string_eq(descr, "LALR(1) parser backend"); + h_free(descr); + h_free_backend_with_params(be_with_params); + be_with_params = h_get_backend_with_params_by_name("glr(2)"); + descr = h_get_descriptive_text_for_backend_with_params(be_with_params); + g_check_maybe_string_eq(descr, "GLR(2) parser backend"); + h_free(descr); + h_free_backend_with_params(be_with_params); + be_with_params = h_get_backend_with_params_by_name("glr()"); + descr = h_get_descriptive_text_for_backend_with_params(be_with_params); + g_check_maybe_string_eq(descr, "GLR(k) parser backend (default k is 1)"); + h_free(descr); + h_free_backend_with_params(be_with_params); +} + +static void test_tt_h_get_name_for_backend_with_params(void){ + HAllocator *mm__ = &system_allocator; + HParserBackendWithParams *be_with_params = h_get_backend_with_params_by_name("llk(1)"); + char * descr = h_get_name_for_backend_with_params(be_with_params); + g_check_maybe_string_eq(descr, "LL(1)"); + h_free(descr); + h_free_backend_with_params(be_with_params); + be_with_params = h_get_backend_with_params_by_name("lalr(2)"); + descr = h_get_name_for_backend_with_params(be_with_params); + g_check_maybe_string_eq(descr, "LALR(2)"); + h_free(descr); + h_free_backend_with_params(be_with_params); + be_with_params = h_get_backend_with_params_by_name("glr(1)"); + descr = h_get_name_for_backend_with_params(be_with_params); + g_check_maybe_string_eq(descr, "GLR(1)"); + h_free(descr); + h_free_backend_with_params(be_with_params); + be_with_params = h_get_backend_with_params_by_name("glr"); + descr = h_get_name_for_backend_with_params(be_with_params); + g_check_maybe_string_eq(descr, "GLR(k)"); + h_free(descr); + h_free_backend_with_params(be_with_params); +} + +/* test that we can request a backend with params from character + * and compile a parser using it */ +static void test_tt_h_compile_for_backend_with_params(void) { + HParserBackendWithParams * be_w_p = NULL; + + be_w_p = h_get_backend_with_params_by_name("llk(1)"); + g_check_inttype("%d", HParserBackend, be_w_p->backend, ==, PB_LLk); + + HParser *p = h_many(h_sequence(h_ch('A'), h_ch('B'), NULL)); + + int r = h_compile_for_backend_with_params(p, be_w_p); + + h_free_backend_with_params(be_w_p); + be_w_p = NULL; + + if (r != 0) { + g_test_message("Compile failed"); + g_test_fail(); + } +} + +void register_names_tests(void) { + g_test_add_func("/core/names/tt_backend_short_name", test_tt_backend_short_name); + g_test_add_func("/core/names/tt_backend_description", test_tt_backend_description); + g_test_add_func("/core/names/tt_query_backend_by_name", test_tt_query_backend_by_name); + g_test_add_func("/core/names/tt_get_backend_with_params_by_name", test_tt_get_backend_with_params_by_name); + g_test_add_func("/core/names/tt_test_tt_h_get_descriptive_text_for_backend_with_params", + test_tt_h_get_descriptive_text_for_backend_with_params); + g_test_add_func("/core/names/test_tt_h_get_name_for_backend_with_params", + test_tt_h_get_name_for_backend_with_params); + g_test_add_func("/core/names/tt_h_compile_for_backend_with_params", test_tt_h_compile_for_backend_with_params); + } diff --git a/src/test_suite.c b/src/test_suite.c index f569644295f4d12efd369b76a2a994a9fdc332f3..8bb303686410f2b4588846fc6bb13d1869a67981 100644 --- a/src/test_suite.c +++ b/src/test_suite.c @@ -25,6 +25,7 @@ extern void register_parser_tests(); extern void register_grammar_tests(); extern void register_misc_tests(); extern void register_mm_tests(); +extern void register_names_tests(); extern void register_benchmark_tests(); extern void register_regression_tests(); @@ -38,6 +39,7 @@ int main(int argc, char** argv) { register_grammar_tests(); register_misc_tests(); register_mm_tests(); + register_names_tests(); register_regression_tests(); if (g_test_slow() || g_test_perf()) register_benchmark_tests(); diff --git a/src/test_suite.h b/src/test_suite.h index dad0621db148431b5953a1291593d22d2a11cd55..93f3e19de01db05e8eb8aa3bc51371cee7a5a635 100644 --- a/src/test_suite.h +++ b/src/test_suite.h @@ -35,6 +35,18 @@ } \ } while(0) +/* Comparison for ptr types; only == and != will work */ +#define g_check_cmp_ptr(p1, op, p2) do { \ + const void *_p1 = (p1); \ + const void *_p2 = (p2); \ + if (!(_p1 op _p2)) { \ + g_test_message("Check failed: (%s): (%p %s %p)", \ + #p1 " " #op " " #p2, \ + _p1, #op, _p2); \ + g_test_fail(); \ + } \ + } while (0); + #define g_check_bytes(len, n1, op, n2) do { \ const uint8_t *_n1 = (n1); \ const uint8_t *_n2 = (n2); \ @@ -56,6 +68,32 @@ } \ } while(0) +#define g_check_maybe_string_eq(n1, n2) do { \ + const char *_n1 = (n1); \ + const char *_n2 = (n2); \ + if (_n1 != _n2 && _n1 != NULL && _n2 != NULL) { \ + if (!(strcmp(_n1, _n2) == 0)) { \ + g_test_message("Check failed: (%s) (\"%s\" == \"%s\")", \ + #n1 " == " #n2, _n1, _n2); \ + g_test_fail(); \ + } \ + } else { \ + if (_n1 != NULL || _n2 != NULL) { \ + if (_n1 != NULL && _n2 == NULL) { \ + g_test_message("Check failed: (%s) (\"%s\" == NULL)", \ + #n1 " == " #n2, _n1); \ + g_test_fail(); \ + } else if (_n1 == NULL && _n2 != NULL) { \ + g_test_message("Check failed: (%s) (NULL == \"%s\")", \ + #n1 " == " #n2, _n2); \ + g_test_fail(); \ + } \ + /* else both are not-NULL, but point to the same string - succeed */ \ + } \ + /* else both are NULL, succeed */ \ + } \ + } while(0) + #define g_check_regular(lang) do { \ if (!lang->isValidRegular(lang->env)) { \ g_test_message("Language is not regular"); \ @@ -455,7 +493,6 @@ #define g_check_cmp_uint32(n1, op, n2) g_check_inttype("%u", uint32_t, n1, op, n2) #define g_check_cmp_uint64(n1, op, n2) g_check_inttype("%" PRIu64, uint64_t, n1, op, n2) #define g_check_cmp_size(n1, op, n2) g_check_inttype("%zu", size_t, n1, op, n2) -#define g_check_cmp_ptr(n1, op, n2) g_check_inttype("%p", void *, n1, op, n2) #define g_check_cmpfloat(n1, op, n2) g_check_inttype("%g", float, n1, op, n2) #define g_check_cmpdouble(n1, op, n2) g_check_inttype("%g", double, n1, op, n2)