hammer/src/backends/glr.c

308 lines
7.7 KiB
C
Raw Normal View History

2013-06-20 11:05:57 +02:00
#include <assert.h>
#include "lr.h"
2013-06-22 18:16:41 +02:00
static bool glr_step(HParseResult **result, HSlist *engines,
2013-06-22 17:40:47 +02:00
HLREngine *engine, const HLRAction *action);
2013-06-19 18:22:19 +02:00
/* GLR compilation (LALR w/o failing on conflict) */
int h_glr_compile(HAllocator* mm__, HParser* parser, const void* params)
{
if (!parser->vtable->isValidCF(parser->env)) {
return -1;
}
2013-06-19 18:22:19 +02:00
int result = h_lalr_compile(mm__, parser, params);
if(result == -1 && parser->backend_data) {
// table is there, just has conflicts? nevermind, that's okay.
result = 0;
}
return result;
}
void h_glr_free(HParser *parser)
{
h_lalr_free(parser);
}
2013-06-22 17:40:47 +02:00
/* Merging engines (when they converge on the same state) */
static HLREngine *lrengine_merge(HLREngine *old, HLREngine *new)
{
HArena *arena = old->arena;
HLREngine *ret = h_arena_malloc(arena, sizeof(HLREngine));
assert(old->state == new->state);
assert(old->input.input == new->input.input);
*ret = *old;
ret->stack = h_slist_new(arena);
ret->merged[0] = old;
ret->merged[1] = new;
return ret;
}
static HSlist *demerge_stack(HSlistNode *bottom, HSlist *stack)
{
HArena *arena = stack->arena;
HSlist *ret = h_slist_new(arena);
// copy the stack from the top
HSlistNode **y = &ret->head;
for(HSlistNode *x=stack->head; x; x=x->next) {
HSlistNode *node = h_arena_malloc(arena, sizeof(HSlistNode));
node->elem = x->elem;
node->next = NULL;
*y = node;
y = &node->next;
}
*y = bottom; // attach the ancestor stack
return ret;
}
static inline HLREngine *respawn(HLREngine *eng, HSlist *stack)
{
// NB: this can be a destructive update because an engine is not used for
// anything after it is merged.
eng->stack = demerge_stack(eng->stack->head, stack);
return eng;
}
static HLREngine *
2013-06-22 18:16:41 +02:00
demerge(HParseResult **result, HSlist *engines,
2013-06-22 17:40:47 +02:00
HLREngine *engine, const HLRAction *action, size_t depth)
{
// no-op on engines that are not merged
if(!engine->merged[0])
return engine;
HSlistNode *p = engine->stack->head;
for(size_t i=0; i<depth; i++) {
// if stack hits bottom, respawn ancestors
if(p == NULL) {
HLREngine *a = respawn(engine->merged[0], engine->stack);
HLREngine *b = respawn(engine->merged[1], engine->stack);
// continue demerge until final depth reached
a = demerge(result, engines, a, action, depth-i);
b = demerge(result, engines, b, action, depth-i);
// step and stow one ancestor...
glr_step(result, engines, a, action);
// ...and return the other
return b;
}
p = p->next;
}
return engine; // there is enough stack before the merge point
}
/* Forking engines (on conflicts */
2013-06-20 11:05:57 +02:00
HLREngine *fork_engine(const HLREngine *engine)
{
HLREngine *eng2 = h_arena_malloc(engine->tarena, sizeof(HLREngine));
eng2->table = engine->table;
eng2->state = engine->state;
eng2->input = engine->input;
2013-06-20 11:05:57 +02:00
// shallow-copy the stack
2013-06-21 23:47:22 +02:00
// this works because h_slist_push and h_slist_drop never modify
2013-06-20 11:05:57 +02:00
// the underlying structure of HSlistNodes, only the head pointer.
// in fact, this gives us prefix sharing for free.
eng2->stack = h_arena_malloc(engine->tarena, sizeof(HSlist));
*eng2->stack = *engine->stack;
2013-06-20 11:05:57 +02:00
eng2->arena = engine->arena;
eng2->tarena = engine->tarena;
return eng2;
}
2013-06-22 17:40:47 +02:00
static const HLRAction *
2013-06-22 18:16:41 +02:00
handle_conflict(HParseResult **result, HSlist *engines,
2013-06-22 17:40:47 +02:00
const HLREngine *engine, const HSlist *branches)
{
// there should be at least two conflicting actions
assert(branches->head);
assert(branches->head->next); // this is just a consistency check
// fork a new engine for all but the first action
for(HSlistNode *x=branches->head->next; x; x=x->next) {
HLRAction *act = x->elem;
HLREngine *eng = fork_engine(engine);
2013-06-22 17:40:47 +02:00
// perform one step and add to engines
glr_step(result, engines, eng, act);
}
// return first action for use with original engine
return branches->head->elem;
}
2013-06-22 14:21:19 +02:00
2013-06-22 17:40:47 +02:00
/* GLR driver */
2013-06-22 18:16:41 +02:00
static bool glr_step(HParseResult **result, HSlist *engines,
2013-06-22 17:40:47 +02:00
HLREngine *engine, const HLRAction *action)
2013-06-22 14:21:19 +02:00
{
2013-06-22 17:40:47 +02:00
// handle forks and demerges (~> spawn engines)
if(action) {
if(action->type == HLR_CONFLICT) {
// fork engine on conflicts
action = handle_conflict(result, engines, engine, action->branches);
} else if(action->type == HLR_REDUCE) {
// demerge/respawn as needed
size_t depth = action->production.length;
engine = demerge(result, engines, engine, action, depth);
2013-06-22 14:21:19 +02:00
}
}
2013-06-22 14:21:19 +02:00
2013-06-22 17:40:47 +02:00
bool run = h_lrengine_step(engine, action);
if(run) {
2013-06-22 18:16:41 +02:00
// store engine in the list, merge if necessary
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(!x) // no merge happened
h_slist_push(engines, engine);
2013-06-22 17:40:47 +02:00
} else if(engine->state == HLR_SUCCESS) {
// save the result
*result = h_lrengine_result(engine);
}
return run;
}
HParseResult *h_glr_parse(HAllocator* mm__, const HParser* parser, HInputStream* stream)
{
HLRTable *table = parser->backend_data;
if(!table)
return NULL;
HArena *arena = h_new_arena(mm__, 0); // will hold the results
HArena *tarena = h_new_arena(mm__, 0); // tmp, deleted after parse
// out-of-memory handling
jmp_buf except;
h_arena_set_except(arena, &except);
h_arena_set_except(tarena, &except);
if(setjmp(except)) {
h_delete_arena(arena);
h_delete_arena(tarena);
return NULL;
}
2013-06-22 18:16:41 +02:00
// allocate engine lists (will hold one engine per state)
2013-06-22 17:40:47 +02:00
// these are swapped each iteration
2013-06-22 18:16:41 +02:00
HSlist *engines = h_slist_new(tarena);
HSlist *engback = h_slist_new(tarena);
2013-06-19 18:22:19 +02:00
2013-06-22 17:40:47 +02:00
// create initial engine
2013-06-22 18:16:41 +02:00
h_slist_push(engines, h_lrengine_new(arena, tarena, table, stream));
2013-06-22 17:40:47 +02:00
HParseResult *result = NULL;
2013-06-22 18:16:41 +02:00
while(result == NULL && !h_slist_empty(engines)) {
assert(h_slist_empty(engback));
// step all engines
while(!h_slist_empty(engines)) {
HLREngine *engine = h_slist_pop(engines);
const HLRAction *action = h_lrengine_action(engine);
glr_step(&result, engback, engine, action);
2013-06-19 18:22:19 +02:00
}
2013-06-22 17:40:47 +02:00
2013-06-22 18:16:41 +02:00
// swap the lists
HSlist *tmp = engines;
2013-06-22 17:40:47 +02:00
engines = engback;
engback = tmp;
2013-06-19 18:22:19 +02:00
}
if(!result)
h_delete_arena(arena);
h_delete_arena(tarena);
return result;
}
HParserBackendVTable h__glr_backend_vtable = {
2013-06-19 18:22:19 +02:00
.compile = h_glr_compile,
.parse = h_glr_parse,
2013-06-19 18:22:19 +02:00
.free = h_glr_free
};
// dummy!
int test_glr(void)
{
HAllocator *mm__ = &system_allocator;
/*
2013-06-19 18:22:19 +02:00
E -> E '+' E
| 'd'
*/
2013-06-19 18:22:19 +02:00
HParser *d = h_ch('d');
HParser *E = h_indirect();
2013-06-19 18:22:19 +02:00
HParser *E_ = h_choice(h_sequence(E, h_ch('+'), E, NULL), d, NULL);
h_bind_indirect(E, E_);
HParser *p = E;
printf("\n==== G R A M M A R ====\n");
HCFGrammar *g = h_cfgrammar_(mm__, h_desugar_augmented(mm__, p));
if(g == NULL) {
fprintf(stderr, "h_cfgrammar failed\n");
return 1;
}
h_pprint_grammar(stdout, g, 0);
printf("\n==== D F A ====\n");
HLRDFA *dfa = h_lr0_dfa(g);
if(dfa)
h_pprint_lrdfa(stdout, g, dfa, 0);
else
fprintf(stderr, "h_lalr_dfa failed\n");
printf("\n==== L R ( 0 ) T A B L E ====\n");
HLRTable *table0 = h_lr0_table(g, dfa);
if(table0)
h_pprint_lrtable(stdout, g, table0, 0);
else
fprintf(stderr, "h_lr0_table failed\n");
h_lrtable_free(table0);
printf("\n==== L A L R T A B L E ====\n");
if(h_compile(p, PB_GLR, NULL)) {
fprintf(stderr, "does not compile\n");
return 2;
}
h_pprint_lrtable(stdout, g, (HLRTable *)p->backend_data, 0);
printf("\n==== P A R S E R E S U L T ====\n");
2013-06-19 18:22:19 +02:00
HParseResult *res = h_parse(p, (uint8_t *)"d+d+d", 5);
if(res)
h_pprint(stdout, res->ast, 0, 2);
else
printf("no parse\n");
return 0;
}