2013-06-20 11:05:57 +02:00
|
|
|
#include <assert.h>
|
2013-06-19 17:20:53 +02:00
|
|
|
#include "lr.h"
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
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-19 17:20:53 +02:00
|
|
|
|
|
|
|
|
/* GLR driver */
|
|
|
|
|
|
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;
|
2013-06-21 20:11:19 +02:00
|
|
|
eng2->input = engine->input;
|
2013-06-20 11:05:57 +02:00
|
|
|
|
2013-06-21 20:11:19 +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.
|
2013-06-21 20:11:19 +02:00
|
|
|
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 12:51:25 +02:00
|
|
|
static void stow_engine(HSlist *engines, HLREngine *engine)
|
|
|
|
|
{
|
|
|
|
|
// XXX switch to one engine per state, and do the merge here
|
|
|
|
|
h_slist_push(engines, engine);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const HLRAction *handle_conflict(HSlist *engines, 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);
|
|
|
|
|
|
|
|
|
|
// perform one step and add to list
|
|
|
|
|
h_lrengine_step(eng, act);
|
|
|
|
|
stow_engine(engines, eng);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return first action for use with original engine
|
|
|
|
|
return branches->head->elem;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-22 14:21:19 +02:00
|
|
|
static HSlist *demerge_stack(HSlistNode *bottom, HSlistNode *mp, HSlist *stack)
|
2013-06-22 12:51:25 +02:00
|
|
|
{
|
2013-06-22 14:21:19 +02:00
|
|
|
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!=mp; 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;
|
|
|
|
|
}
|
2013-06-22 12:51:25 +02:00
|
|
|
|
2013-06-22 14:21:19 +02:00
|
|
|
static void demerge(HSlist *engines, HLREngine *engine,
|
|
|
|
|
const HLRAction *action, size_t depth)
|
|
|
|
|
{
|
|
|
|
|
// no-op on engines that are not merged
|
|
|
|
|
if(!engine->merged)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
HSlistNode *p = engine->stack->head;
|
|
|
|
|
for(size_t i=0; i<depth; i++) {
|
|
|
|
|
// if stack hits mergepoint, respawn ancestor
|
|
|
|
|
if(p == engine->mp) {
|
|
|
|
|
HLREngine *eng = engine->merged;
|
|
|
|
|
eng->stack = demerge_stack(eng->stack->head, engine->mp, engine->stack);
|
|
|
|
|
demerge(engines, eng, action, depth-i);
|
|
|
|
|
|
|
|
|
|
// call step and stow on restored ancestor
|
|
|
|
|
h_lrengine_step(eng, action);
|
|
|
|
|
stow_engine(engines, eng);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
p = p->next;
|
2013-06-22 12:51:25 +02:00
|
|
|
}
|
2013-06-22 14:21:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
handle_demerge(HSlist *engines, HLREngine *engine, const HLRAction *reduce)
|
|
|
|
|
{
|
|
|
|
|
demerge(engines, engine, reduce, reduce->production.length);
|
2013-06-22 12:51:25 +02:00
|
|
|
}
|
|
|
|
|
|
2013-06-19 17:20:53 +02:00
|
|
|
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
|
|
|
|
|
|
2013-06-19 18:22:19 +02:00
|
|
|
HSlist *engines = h_slist_new(tarena);
|
2013-06-21 20:11:19 +02:00
|
|
|
h_slist_push(engines, h_lrengine_new(arena, tarena, table, stream));
|
2013-06-19 18:22:19 +02:00
|
|
|
|
|
|
|
|
HParseResult *result = NULL;
|
|
|
|
|
while(result == NULL && !h_slist_empty(engines)) {
|
|
|
|
|
for(HSlistNode **x = &engines->head; *x; ) {
|
|
|
|
|
HLREngine *engine = (*x)->elem;
|
|
|
|
|
|
2013-06-22 12:51:25 +02:00
|
|
|
// remove engine from list; it may come back in below
|
|
|
|
|
*x = (*x)->next; // advance x, removing the current element
|
2013-06-21 18:46:16 +02:00
|
|
|
|
2013-06-22 12:51:25 +02:00
|
|
|
// drop those engines that have terminated
|
|
|
|
|
if(!engine->run) {
|
2013-06-21 18:46:16 +02:00
|
|
|
// check for parse success
|
|
|
|
|
HParseResult *res = h_lrengine_result(engine);
|
|
|
|
|
if(res)
|
|
|
|
|
result = res;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-21 20:11:19 +02:00
|
|
|
const HLRAction *action = h_lrengine_action(engine);
|
2013-06-20 11:05:57 +02:00
|
|
|
|
2013-06-22 12:51:25 +02:00
|
|
|
// handle forks and demerges (~> spawn engines)
|
|
|
|
|
if(action) {
|
|
|
|
|
if(action->type == HLR_CONFLICT) {
|
|
|
|
|
// fork engine on conflicts
|
|
|
|
|
action = handle_conflict(engines, engine, action->branches);
|
|
|
|
|
} else if(action->type == HLR_REDUCE) {
|
2013-06-22 14:21:19 +02:00
|
|
|
// demerge/respawn as needed
|
|
|
|
|
handle_demerge(engines, engine, action);
|
2013-06-22 12:51:25 +02:00
|
|
|
}
|
2013-06-20 11:05:57 +02:00
|
|
|
}
|
|
|
|
|
|
2013-06-21 18:46:16 +02:00
|
|
|
h_lrengine_step(engine, action);
|
2013-06-22 12:51:25 +02:00
|
|
|
stow_engine(engines, engine);
|
2013-06-19 18:22:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
2013-06-19 17:20:53 +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,
|
2013-06-19 17:20:53 +02:00
|
|
|
.parse = h_glr_parse,
|
2013-06-19 18:22:19 +02:00
|
|
|
.free = h_glr_free
|
2013-06-19 17:20:53 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-06-21 18:46:16 +02:00
|
|
|
// XXX TODO
|
|
|
|
|
// - implement engine merging
|
|
|
|
|
// - triggered when two enter the same state
|
|
|
|
|
// - old stacks (/engines?) saved
|
|
|
|
|
// - new common suffix stack created
|
|
|
|
|
// - when rewinding (during reduce), watch for empty stack -> demerge
|
|
|
|
|
|
2013-06-19 17:20:53 +02:00
|
|
|
|
|
|
|
|
// dummy!
|
|
|
|
|
int test_glr(void)
|
|
|
|
|
{
|
2013-06-21 23:53:47 +02:00
|
|
|
HAllocator *mm__ = &system_allocator;
|
|
|
|
|
|
2013-06-19 17:20:53 +02:00
|
|
|
/*
|
2013-06-19 18:22:19 +02:00
|
|
|
E -> E '+' E
|
|
|
|
|
| 'd'
|
2013-06-19 17:20:53 +02:00
|
|
|
*/
|
|
|
|
|
|
2013-06-19 18:22:19 +02:00
|
|
|
HParser *d = h_ch('d');
|
2013-06-19 17:20:53 +02:00
|
|
|
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);
|
2013-06-19 17:20:53 +02:00
|
|
|
h_bind_indirect(E, E_);
|
|
|
|
|
HParser *p = E;
|
|
|
|
|
|
|
|
|
|
printf("\n==== G R A M M A R ====\n");
|
2013-06-21 23:53:47 +02:00
|
|
|
HCFGrammar *g = h_cfgrammar_(mm__, h_desugar_augmented(mm__, p));
|
2013-06-19 17:20:53 +02:00
|
|
|
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);
|
2013-06-19 17:20:53 +02:00
|
|
|
if(res)
|
|
|
|
|
h_pprint(stdout, res->ast, 0, 2);
|
|
|
|
|
else
|
|
|
|
|
printf("no parse\n");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|