def build_LR1_automaton(self, G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0, lookaheads=(G.EOF,)) start = frozenset([start_item]) closure = self.closure_lr1(start, firsts) automaton = State(frozenset(closure), True) pending = [ start ] visited = { start: automaton } while pending: current = pending.pop() current_state = visited[current] for symbol in G.terminals + G.nonTerminals: closure_current = self.closure_lr1(current, firsts) goto = self.goto_lr1(closure_current, symbol, just_kernel=True) if len(goto) == 0: continue try: next_state = visited[goto] except KeyError: next_closure = self.closure_lr1(goto, firsts) visited[goto] = next_state = State(frozenset(next_closure), True) pending.append(goto) current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multiline_formatter) return automaton
def build_LR0_automaton(G: Grammar): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0) automaton = State(start_item, True) pending = [start_item] visited = {start_item: automaton} while pending: current_item = pending.pop() if current_item.IsReduceItem: continue current_state = visited[current_item] next_symbol = current_item.NextSymbol next_item = current_item.NextItem() #- $(X --> a.cB) ---c---> (X --> ac.B) con c in V_T #- $(X --> a.YB) ---Y---> (X --> aY.B) con Y in V_N next_state = get_state(visited, pending, next_item) current_state.add_transition(next_symbol.Name, next_state) if next_symbol in G.nonTerminals: sym_productions = G.symbDict[next_symbol.Name].productions #- $(X --> a.YB) ---e---> (Y --> .Z) con Y in V_N for pr in [Item(x, 0) for x in sym_productions]: trans_state = get_state(visited, pending, pr) current_state.add_epsilon_transition(trans_state) return automaton.to_deterministic(lr0_formatter)
def build_lr0_automaton(G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0) start = frozenset([start_item]) automaton = State(closure_lr0(start), True) pending = [start] visited = {start: automaton} while pending: current = pending.pop() current_state = visited[current] current_closure = current_state.state for symbol in G.terminals + G.nonTerminals: kernel = goto_lr0(current_closure, symbol) if kernel == frozenset(): continue try: next_state = visited[kernel] except KeyError: next_state = visited[kernel] = State(closure_lr0(kernel), True) pending.append(kernel) current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multiline_formatter) return automaton
def _build_automaton(self): start = State('start') automatons = self.regexs for i in automatons: start.add_epsilon_transition(i) final = start.to_deterministic() return final
def build_LALR_automaton(G): assert len(G.startSymbol.productions) == 1, "Grammar must be augmented" lr1_automaton = build_LR1_automaton(G) same_kernel = {} for node in lr1_automaton: just_center = frozenset([item.Center() for item in node.state]) try: same_kernel[just_center].append(node) except KeyError: same_kernel[just_center] = [node] start = frozenset([item.Center() for item in lr1_automaton.state ]) # como cabecera solo quedan los items sin lookahead automaton = State( lr1_automaton.state, True ) # en visited se guarda el estado que corresponde a la fusion de estaods ocn el mismo nucleo pending = [start] visited = {start: automaton} while pending: current = pending.pop() current_state = visited[current] # se van a actualizar # todos los estados con los que el estado actual tiene alguna transicion lr1_state = same_kernel[current][0] # chequear que cada estado del cjto analizado tenga esa transicion for symbol in G.terminals + G.nonTerminals: if lr1_state.has_transition(symbol.Name): state = lr1_state.transitions[symbol.Name][0] center_items = frozenset( [item.Center() for item in state.state]) try: next_state = visited[center_items] except KeyError: kernel_set = same_kernel[center_items] items_with_lookahead = {} for node in kernel_set: for item in node.state: try: current_item = items_with_lookahead[ item.Center()] except KeyError: current_item = items_with_lookahead[ item.Center()] = set() current_item.update(item.lookaheads) completed_items = [ Item(item.production, item.pos, lookaheads) for item, lookaheads in items_with_lookahead.items() ] next_state = State(frozenset(completed_items), True) visited[center_items] = next_state pending.append(center_items) current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multiline_formatter) return automaton
def DerivationTree(derivation, G): lent = len(derivation) nonTerminalstack = [] root = State(G.startSymbol.Name) nonTerminalstack.append(root) while lent > 0: lent -= 1 next_production = derivation[lent] print("next_production", next_production) currentNode = nonTerminalstack.pop() # assert currentNode.state == next_production.Left.Name, "Wrong derivation" if next_production.IsEpsilon: currentNode.add_transition(" ", State("epsilon", True)) for symbol in next_production.Right: if symbol.IsTerminal: currentNode.add_transition(" ", State(symbol.Name, True)) else: nonTerminalstack.append(State(symbol.Name)) currentNode.add_transition( " ", nonTerminalstack[len(nonTerminalstack) - 1]) return root
def build_larl1_automaton(G, firsts=None): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' if not firsts: firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0, lookaheads=ContainerSet(G.EOF)) start = frozenset([start_item.Center()]) closure = closure_lr1([start_item], firsts) automaton = State(frozenset(closure), True) pending = [start] visited = {start: automaton} while pending: current = pending.pop() current_state = visited[current] current_closure = current_state.state for symbol in G.terminals + G.nonTerminals: goto = goto_lr1(current_closure, symbol, just_kernel=True) closure = closure_lr1(goto, firsts) center = frozenset(item.Center() for item in goto) if center == frozenset(): continue try: next_state = visited[center] centers = {item.Center(): item for item in next_state.state} centers = { item.Center(): (centers[item.Center()], item) for item in closure } updated_items = set() for c, (itemA, itemB) in centers.items(): item = Item(c.production, c.pos, itemA.lookaheads | itemB.lookaheads) updated_items.add(item) updated_items = frozenset(updated_items) if next_state.state != updated_items: pending.append(center) next_state.state = updated_items except KeyError: visited[center] = next_state = State(frozenset(closure), True) pending.append(center) if current_state[symbol.Name] is None: current_state.add_transition(symbol.Name, next_state) else: assert current_state.get( symbol.Name) is next_state, 'Bad build!!!' automaton.set_formatter(multiline_formatter) return automaton
def _build_automaton(self): start = State('start') regexs = self.regexs for regex in regexs: start.add_epsilon_transition(regex) return start.to_deterministic()
def _build_parsing_table(self): G = self.G.AugmentedGrammar(True) automaton = build_LR1_automaton(G) centers = {} for i, node in enumerate(automaton): node.idx = i try: same_center = centers[node_centers(node)] centers[node_centers(node)] = State( compress(node.state.union(same_center.state)), True) except KeyError: centers[node_centers(node)] = node centers[node_centers(node)].idx = i self.automaton = automaton self.centers = centers Goto = {} firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) for node in centers.values(): for symbol in G.terminals + G.nonTerminals: temp = goto_lr1(node.state, symbol, firsts) if not temp: continue Goto[(node.idx, symbol)] = centers[node_centers(State(temp, True))] for node in centers.values(): idx = node.idx for item in node.state: # Your code here!!! # - Fill `self.Action` and `self.Goto` according to `item`) # - Feel free to use `self._register(...)`) if item.IsReduceItem: production = item.production if production.Left == G.startSymbol: self._register(self.action, (idx, G.EOF), ("OK", None)) else: for lookahead in item.lookaheads: self._register(self.action, (idx, lookahead), ("REDUCE", production)) else: next_symbol = item.NextSymbol if next_symbol.IsTerminal: self._register( self.action, (idx, next_symbol), ("SHIFT", Goto[(node.idx, next_symbol)].idx)) else: self._register(self.goto, (idx, next_symbol), Goto[(node.idx, next_symbol)].idx)
def build_LR0_automaton(G): assert len(G.startSymbol.productions) == 1, "Grammar must be augmented" start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0) automaton = State(start_item, True) pending = [start_item] visited = {start_item: automaton} while pending: current_item = pending.pop() if current_item.IsReduceItem: continue # (Decide which transitions to add) # agregar las epsilon transiciones # a estados donde el item posee producciones a partir del simbolo actual en la posicion 0 # y agregar la transicion a partir del simbolo siguiente next_item = current_item.NextItem() try: next_state = visited[next_item] except KeyError: next_state = State(next_item, True) visited[next_item] = next_state pending.append(next_item) if current_item.NextSymbol.IsNonTerminal: epsilon_productions = current_item.NextSymbol.productions else: epsilon_productions = None current_state = visited[current_item] # (Adding the decided transitions) current_state.add_transition(current_item.NextSymbol.Name, next_state) if epsilon_productions: for eproduction in epsilon_productions: epItem = Item(eproduction, 0) try: epState = visited[epItem] except KeyError: epState = State(epItem, True) visited[epItem] = epState pending.append(epItem) current_state.add_epsilon_transition(epState) return automaton
def build_LALR1_automaton(G): automaton = build_LR1_automaton(G) stKernel = {} for node in automaton: kernel = frozenset([item.Center() for item in node.state]) try: stKernel[kernel].append(node) except KeyError: stKernel[kernel] = [node] initial = frozenset([item.Center() for item in automaton.state]) automaton = State(automaton.state, True) visited = {initial: automaton} pending = [initial] while pending: current = pending.pop() current_state = visited[current] lr1_state = stKernel[current][0] for symbol in G.terminals + G.nonTerminals: if symbol.Name in lr1_state.transitions: dest_core = frozenset([ item.Center() for item in lr1_state.transitions[symbol.Name][0].state ]) try: next_state = visited[dest_core] except KeyError: union_core = {center: set() for center in dest_core} for node in stKernel[dest_core]: for item in node.state: union_core[item.Center()].update(item.lookaheads) union_core = frozenset([ Item(center.production, center.pos, lookaheads=lookaheads) for center, lookaheads in union_core.items() ]) next_state = State(union_core, True) visited[dest_core] = next_state pending.append(dest_core) current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multiline_formatter) return automaton
def get_state(visited, pending, item): if not item in visited.keys(): new_state = State(item, True) pending.append(item) visited[item] = new_state return new_state return visited[item]
def build_lalr_automaton(G): def centers(items: [Item]): return frozenset(item.Center() for item in items) def lookaheads(items: [Item]): return {item.Center(): item.lookaheads for item in items} def subset(items1, items2): return all(items1[i] <= items2[i] for i in items1) assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0, (G.EOF, )) start = State(frozenset(closure_lr1([start_item], firsts)), True) pending = [start_item] visisted_centers = {centers(start.state): start} visited = {start_item: start} while pending: current_state = visited[pending.pop()] for symbol in G.terminals + G.nonTerminals: next_item = frozenset(goto_lr1(current_state.state, symbol, firsts)) if next_item: try: next_state = visisted_centers[centers(next_item)] if not subset(lookaheads(next_item), lookaheads(next_state.state)): next_state.state = compress( list(next_state.state) + list(next_item)) pending.append(frozenset(next_state.state)) visited[frozenset(next_state.state)] = next_state except KeyError: next_state = State(next_item, True) pending += [next_item] visisted_centers[centers(next_item)] = next_state visited[next_item] = next_state current_state.add_transition(symbol.Name, next_state) return start
def build_LR0_automaton(G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0) automaton = State(start_item, True) pending = [start_item] visited = {start_item: automaton} while pending: current_item = pending.pop() if current_item.IsReduceItem: continue # Your code here!!! (Decide which transitions to add) current_state = visited[current_item] next_symbol = current_item.NextSymbol next_item = current_item.NextItem() try: next_state = visited[next_item] except KeyError: next_state = State(next_item, True) visited[next_item] = next_state current_state.add_transition(next_symbol.Name, next_state) # print('Added trans: ', current_state, f'==={next_symbol.Name}===>', next_state) pending.append(next_item) if next_symbol.IsNonTerminal: # If symbol is non terminal, add e-productions to Y->.alpha for prod in next_symbol.productions: new_item = Item(prod, 0) try: new_state = visited[new_item] item_was_found = True except KeyError: new_state = State(new_item, True) visited[new_item] = new_state item_was_found = False current_state.add_epsilon_transition(new_state) # print('Added e-trans:', current_state, '==>', new_state) if not item_was_found: pending.append(new_item) return automaton
def _build_regexs(self, table): regexs = [] for n, (token_type, text_regex) in enumerate(table): automaton = State.from_nfa(self.regex(text_regex)) for state in automaton: if state.final: state.tag = (n, token_type) regexs.append(automaton) return regexs
def build_LR0_automaton(G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0) automaton = State(start_item, True) pending = [start_item] visited = {start_item: automaton} while pending: current_item = pending.pop() if current_item.IsReduceItem: continue if current_item.NextSymbol.IsTerminal: if not current_item.NextItem in visited: visited[current_item.NextItem] = State(current_item.NextItem, True) pending.append(current_item.NextItem) else: for production in current_item.NextSymbol.productions: item = Item(production, 0) if not item in visited: visited[item] = State(item, True) pending.append(item) if not current_item.NextItem in visited: visited[current_item.NextItem] = State(current_item.NextItem, True) pending.append(current_item.NextItem) current_state = visited[current_item] # Your code here!!! (Add the decided transitions) current_state.add_transition(current_item.NextSymbol.Name, visited[current_item.NextItem]) if current_item.NextSymbol.IsNonTerminal: for production in current_item.NextSymbol.productions: current_state.add_epsilon_transition(visited[Item( production, 0)]) # automaton.set_formatter(multiline_formatter_lr0) return automaton
def build_automaton(G: Grammar): """ Build the finite automaton for a regular grammar """ states = { nonTerminal: State(nonTerminal.Name) for nonTerminal in G.nonTerminals } final_state = State('F\'', True) start_in_right = False epsilon_production = False for nonTerminal in G.nonTerminals: for production in nonTerminal.productions: right = production.Right # Start Symbol produces epsilon if isinstance(right, Epsilon) and nonTerminal == G.startSymbol: epsilon_production = True continue start_in_right |= G.startSymbol in right n = len(right) # X --> w if n == 1 and isinstance(right[0], Terminal): states[nonTerminal].add_transition(right[0].Name, final_state) continue # X --> w Y if n == 2 and isinstance(right[0], Terminal) and isinstance( right[1], NonTerminal): states[nonTerminal].add_transition(right[0].Name, states[right[1]]) continue return states[G.startSymbol], False states[G.startSymbol].final = epsilon_production return states[G.startSymbol], not (start_in_right and epsilon_production)
def build_LR0_automaton(G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0) automaton = State(start_item, True) pending = [start_item] visited = {start_item: automaton} while pending: current_item = pending.pop() if current_item.IsReduceItem: continue # Your code here!!! (Decide which transitions to add) new_item = current_item.NextItem() transition_symbol = current_item.NextSymbol.Name next_items = [(new_item, transition_symbol)] if current_item.NextSymbol in G.nonTerminals: for production in current_item.NextSymbol.productions: new_item = Item(production, 0) next_items.append((new_item, G.Epsilon)) current_state = visited[current_item] # Your code here!!! (Add the decided transitions) for item, symbol in next_items: try: state = visited[item] except KeyError: state = State(item, True) visited[item] = state pending.append(item) if symbol == G.Epsilon: current_state.add_epsilon_transition(state) else: current_state.add_transition(symbol, state) return automaton
def build_LR1_automaton(G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0, lookaheads=(G.EOF, )) start = frozenset([start_item]) closure = closure_lr1(start, firsts) automaton = State(frozenset(closure), True) pending = [start] visited = {start: automaton} while pending: current = pending.pop() current_state = visited[current] for symbol in G.terminals + G.nonTerminals: # Your code here!!! (Get/Build `next_state`) dest = goto_lr1(current_state.state, symbol, just_kernel=True) if not dest: continue if dest in visited: next_state = visited[dest] else: items = goto_lr1(current_state.state, symbol, firsts, just_kernel=False) next_state = State(frozenset(items), True) visited[dest] = next_state pending.append(dest) current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multiline_formatter) return automaton
def Compact_Automata(self, automata): new_states = {} for state in automata: new_states[state] = state states_to_compress = [] for state1 in automata: if not new_states[state1] == state1: continue states_to_compress = [state1] for state2 in automata: if state1 == state2 or not new_states[state2] == state2: continue node1 = state1.state node2 = state2.state are_equals = False if len(node1) == len(node2): for item1 in node1: are_equals = False for item2 in node2: if item1.Center() == item2.Center(): are_equals = True if not are_equals: break if are_equals: states_to_compress.append(state2) compress_set = ContainerSet() for state in states_to_compress: node = state.state compress_set.update(ContainerSet(*node)) new_node = self.compress(compress_set) new_state = State(frozenset(new_node), True) for state in states_to_compress: new_states[state] = new_state new_automata = new_states[automata] for state in automata: for key in state.transitions: for to_state in state.transitions[key]: try: assert new_states[to_state] in new_states[state].transitions[key] except: new_states[state].add_transition(key, new_states[to_state]) return new_automata
def _build_regexs(table): regexs = [] for n, (token_type, regex) in enumerate(table): NFA = Regex.build_automaton(regex) automaton, states = State.from_nfa(NFA, get_states=True) for state in automaton: if state.final: state.tag = [(n, token_type)] regexs.append(automaton) return regexs
def build_LR1_automaton(G): assert len(G.startSymbol.productions) == 1, "Grammar must be augmented" firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0, lookaheads=(G.EOF, )) start = frozenset([start_item]) # como cabecera solo queda el kernel closure = closure_lr1(start, firsts) automaton = State(frozenset(closure), True) # en visited si se guarda el estado completo pending = [start] visited = {start: automaton} while pending: current = pending.pop() current_state = visited[current] closure = closure_lr1(current, firsts) for symbol in G.terminals + G.nonTerminals: # (Get/Build `next_state`) # closure = closure_lr1(current,firsts) goto = goto_lr1(closure, symbol, firsts, True) if not goto: continue try: next_state = visited[goto] except KeyError: next_state = visited[goto] = State( frozenset(closure_lr1(goto, firsts)), True) pending.append(goto) current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multiline_formatter) return automaton
def _build_regexs(self, table): regexs = [] for n, (token_type, regex) in enumerate(table): dfa = Regex(regex) automaton_list = State.from_nfa( dfa.automaton, 'texto random pra que haga lo que quiero') for i in automaton_list[1]: if (i.final): i.tag = (n, token_type) regexs.append(automaton_list[0]) return regexs
def build_LR1_automaton(G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0, lookaheads=(G.EOF, )) start = frozenset([start_item]) closure = closure_lr1(start, firsts) automaton = State(frozenset(closure), True) pending = [start] visited = {start: automaton} while pending: current = pending.pop() current_state = visited[current] for symbol in G.terminals + G.nonTerminals: # Your code here!!! (Get/Build `next_state`) next_state_key = goto_lr1(current_state.state, symbol, just_kernel=True) # next_state_key = frozenset([i.NextItem() for i in current_state.state if i.NextSymbol == symbol]) if not next_state_key: continue try: next_state = visited[next_state_key] except KeyError: next_state_items = goto_lr1(current_state.state, symbol, firsts) next_state = State(frozenset(next_state_items), True) pending.append(next_state_key) visited[next_state_key] = next_state current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multiline_formatter) return automaton
def build_LR0_automaton(G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0) automaton = State(start_item, True) pending = [ start_item ] visited = { start_item: automaton } while pending: current_item = pending.pop() if current_item.IsReduceItem: continue state = visited[current_item] next_item = current_item.NextItem() try: new_state = visited[next_item] except KeyError: new_state = visited[next_item] = State(next_item, True) pending.append(next_item) next_symbol = current_item.NextSymbol state.add_transition(next_symbol.Name, new_state) if next_symbol.IsNonTerminal: for prod in next_symbol.productions: item = Item(prod, 0) try: visited[item] except KeyError: visited[item] = State(item, True) pending.append(item) state.add_epsilon_transition(visited[item]) current_state = visited[current_item] return automaton
def build_LR0_automaton(G): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0) automaton = State(start_item, True) pending = [start_item] visited = {start_item: automaton} while pending: current_item = pending.pop() if current_item.IsReduceItem: continue next_item = current_item.NextItem() symbol = current_item.NextSymbol try: visited[current_item].add_transition(symbol.Name, visited[next_item]) except: visited[next_item] = State(next_item, True) visited[current_item].add_transition(symbol.Name, visited[next_item]) pending.append(next_item) if symbol.IsNonTerminal: prod = symbol.productions for p in prod: item = Item(p, 0) try: visited[current_item].add_epsilon_transition(visited[item]) except KeyError: visited[item] = State(item, True) visited[current_item].add_epsilon_transition(visited[item]) pending.append(item) return automaton
def _build_regexs(self, table): regexs = {} for n, (token_type, regex) in enumerate(table): # Your code here!!! # - Remember to tag the final states with the token_type and priority. # - <State>.tag might be useful for that purpose ;-) auto = regex_automaton(regex) auto, states = State.from_nfa(auto, True) for st in states: if st.final: st.tag = (n, token_type) regexs[token_type] = auto return regexs
def state_transpose(initial_state: State): """ returns the state equivalent to initial_state on the transpose graph and a dictionary mapping idx to new states transposed """ state_dict = { } # state_dict[key] = (original_state,copy_of_original_state_with_transposed_transitions) idx = 0 for x in initial_state: new_state = State(x.state, x.final, x.formatter, x.shape) if not hasattr(x, 'idx'): x.idx = idx idx += 1 new_state.idx = x.idx state_dict[x.idx] = (x, new_state) for x in initial_state: for key, states in x.transitions.items(): for state in states: state_dict[state.idx][1].add_transition( key, state_dict[x.idx][1]) return state_dict[initial_state.idx][1], state_dict
def build_tree(self, current_node, index, parse_list, left_to_right): new_nodes = [] if parse_list[index[0]].Right: for x in parse_list[index[0]].Right: new_nodes.append(State(x)) else: new_nodes.append(State(parse_list[index[0]].Right)) if not left_to_right: new_nodes.reverse() for x in new_nodes: current_node.add_transition('', x) if x.state.IsNonTerminal: index[0] += 1 self.build_tree(x, index, parse_list, left_to_right) if not left_to_right: try: current_node.transitions[''].reverse() except: pass
def build_lr1_automaton(G, firsts=None): assert len(G.startSymbol.productions) == 1, 'Grammar must be augmented' if not firsts: firsts = compute_firsts(G) firsts[G.EOF] = ContainerSet(G.EOF) start_production = G.startSymbol.productions[0] start_item = Item(start_production, 0, lookaheads=(G.EOF, )) start = frozenset([start_item]) closure = closure_lr1(start, firsts) automaton = State(frozenset(closure), True) pending = [start] visited = {start: automaton} while pending: current = pending.pop() current_state = visited[current] current_closure = current_state.state for symbol in G.terminals + G.nonTerminals: kernel = goto_lr1(current_closure, symbol, just_kernel=True) if kernel == frozenset(): continue try: next_state = visited[kernel] except KeyError: goto = closure_lr1(kernel, firsts) visited[kernel] = next_state = State(frozenset(goto), True) pending.append(kernel) current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multiline_formatter) return automaton