#include #include "lr.h" /* Constructing the characteristic automaton (handle recognizer) */ static HLRItem *advance_mark(HArena *arena, const HLRItem *item) { assert(item->rhs[item->mark] != NULL); HLRItem *ret = h_arena_malloc(arena, sizeof(HLRItem)); *ret = *item; ret->mark++; return ret; } static void expand_to_closure(HCFGrammar *g, HHashSet *items) { HAllocator *mm__ = g->mm__; HArena *arena = g->arena; HSlist *work = h_slist_new(arena); // initialize work list with items H_FOREACH_KEY(items, HLRItem *item) h_slist_push(work, (void *)item); H_END_FOREACH while(!h_slist_empty(work)) { const HLRItem *item = h_slist_pop(work); HCFChoice *sym = item->rhs[item->mark]; // symbol after mark // if there is a non-terminal after the mark, follow it // NB: unlike LLk, we do consider HCF_CHARSET a non-terminal here if(sym != NULL && (sym->type==HCF_CHOICE || sym->type==HCF_CHARSET)) { // add items corresponding to the productions of sym if(sym->type == HCF_CHOICE) { for(HCFSequence **p=sym->seq; *p; p++) { HLRItem *it = h_lritem_new(arena, sym, (*p)->items, 0); if(!h_hashset_present(items, it)) { h_hashset_put(items, it); h_slist_push(work, it); } } } else { // HCF_CHARSET for(unsigned int i=0; i<256; i++) { if(charset_isset(sym->charset, i)) { // XXX allocate these single-character symbols statically somewhere HCFChoice **rhs = h_new(HCFChoice *, 2); rhs[0] = h_new(HCFChoice, 1); rhs[0]->type = HCF_CHAR; rhs[0]->chr = i; rhs[1] = NULL; HLRItem *it = h_lritem_new(arena, sym, rhs, 0); h_hashset_put(items, it); // single-character item needs no further work } } // if sym is a non-terminal, we need a reshape on it // this seems as good a place as any to set it sym->reshape = h_act_first; } } } } HLRDFA *h_lr0_dfa(HCFGrammar *g) { HArena *arena = g->arena; HHashSet *states = h_hashset_new(arena, h_eq_lr_itemset, h_hash_lr_itemset); // maps itemsets to assigned array indices HSlist *transitions = h_slist_new(arena); // list of states that need to be processed // to save lookups, we push two elements per state, the itemset and its // assigned index. HSlist *work = h_slist_new(arena); // make initial state (kernel) HLRState *start = h_lrstate_new(arena); assert(g->start->type == HCF_CHOICE); for(HCFSequence **p=g->start->seq; *p; p++) h_hashset_put(start, h_lritem_new(arena, g->start, (*p)->items, 0)); expand_to_closure(g, start); h_hashtable_put(states, start, 0); h_slist_push(work, start); h_slist_push(work, 0); // while work to do (on some state) // determine edge symbols // for each edge symbol: // advance respective items -> destination state (kernel) // compute closure // if destination is a new state: // add it to state set // add transition to it // add it to the work list while(!h_slist_empty(work)) { size_t state_idx = (uintptr_t)h_slist_pop(work); HLRState *state = h_slist_pop(work); // maps edge symbols to neighbor states (item sets) of s HHashTable *neighbors = h_hashtable_new(arena, h_eq_symbol, h_hash_symbol); // iterate over state (closure) and generate neighboring sets H_FOREACH_KEY(state, HLRItem *item) HCFChoice *sym = item->rhs[item->mark]; // symbol after mark if(sym != NULL) { // mark was not at the end // find or create prospective neighbor set HLRState *neighbor = h_hashtable_get(neighbors, sym); if(neighbor == NULL) { neighbor = h_lrstate_new(arena); h_hashtable_put(neighbors, sym, neighbor); } // ...and add the advanced item to it h_hashset_put(neighbor, advance_mark(arena, item)); } H_END_FOREACH // merge expanded neighbor sets into the set of existing states H_FOREACH(neighbors, HCFChoice *symbol, HLRState *neighbor) expand_to_closure(g, neighbor); // look up existing state, allocate new if not found size_t neighbor_idx; if(!h_hashset_present(states, neighbor)) { neighbor_idx = states->used; h_hashtable_put(states, neighbor, (void *)(uintptr_t)neighbor_idx); h_slist_push(work, neighbor); h_slist_push(work, (void *)(uintptr_t)neighbor_idx); } else { neighbor_idx = (uintptr_t)h_hashtable_get(states, neighbor); } // add transition "state --symbol--> neighbor" HLRTransition *t = h_arena_malloc(arena, sizeof(HLRTransition)); t->from = state_idx; t->to = neighbor_idx; t->symbol = symbol; h_slist_push(transitions, t); H_END_FOREACH } // end while(work) // fill DFA struct HLRDFA *dfa = h_arena_malloc(arena, sizeof(HLRDFA)); dfa->nstates = states->used; dfa->states = h_arena_malloc(arena, dfa->nstates*sizeof(HLRState *)); H_FOREACH(states, HLRState *state, void *v) size_t idx = (uintptr_t)v; dfa->states[idx] = state; H_END_FOREACH dfa->transitions = transitions; return dfa; } /* LR(0) table generation */ static inline void put_shift(HLRTable *table, size_t state, const HCFChoice *symbol, size_t nextstate) { HLRAction *action = h_shift_action(table->arena, nextstate); switch(symbol->type) { case HCF_END: h_stringmap_put_end(table->tmap[state], action); break; case HCF_CHAR: h_stringmap_put_char(table->tmap[state], symbol->chr, action); break; default: // nonterminal case h_hashtable_put(table->ntmap[state], symbol, action); } } HLRTable *h_lr0_table(HCFGrammar *g, const HLRDFA *dfa) { HAllocator *mm__ = g->mm__; HLRTable *table = h_lrtable_new(mm__, dfa->nstates); HArena *arena = table->arena; // remember start symbol table->start = g->start; // shift to the accepting end state for the start symbol put_shift(table, 0, g->start, HLR_SUCCESS); // add shift entries for(HSlistNode *x = dfa->transitions->head; x; x = x->next) { // for each transition x-A->y, add "shift, goto y" to table entry (x,A) HLRTransition *t = x->elem; put_shift(table, t->from, t->symbol, t->to); } // add reduce entries, record inadequate states for(size_t i=0; instates; i++) { bool inadeq = false; // find reducible items in state H_FOREACH_KEY(dfa->states[i], HLRItem *item) if(item->mark == item->len) { // mark at the end HLRAction *reduce = h_reduce_action(arena, item); // check for reduce/reduce conflict on forall if(table->forall[i]) { reduce = h_lr_conflict(arena, table->forall[i], reduce); inadeq = true; } table->forall[i] = reduce; // check for shift/reduce conflict with other entries // NOTE: these are not recorded as HLR_CONFLICTs at this point if(!h_lrtable_row_empty(table, i)) inadeq = true; } H_END_FOREACH if(inadeq) h_slist_push(table->inadeq, (void *)(uintptr_t)i); } return table; }