def expand(item, firsts): next_symbol = item.NextSymbol if next_symbol is None or not next_symbol.IsNonTerminal: return [] look_a_heads = ContainerSet() for preview in item.Preview(): look_a_heads.hard_update(compute_local_first(firsts, preview)) assert not look_a_heads.contains_epsilon return [[Item(prod, 0, look_a_heads)] for prod in next_symbol.productions]
def compute_firsts(G): firsts = {} change = True # init First(Vt) for terminal in G.terminals: firsts[terminal] = ContainerSet(terminal) # init First(Vn) for non_terminal in G.nonTerminals: firsts[non_terminal] = ContainerSet() while change: change = False # P: X -> alpha for production in G.Productions: X = production.Left alpha = production.Right # get current First(X) first_x = firsts[X] # init First(alpha) try: first_alpha = firsts[alpha] except: first_alpha = firsts[alpha] = ContainerSet() # CurrentFirst(alpha)??? local_first = compute_local_first(firsts, alpha) # update First(X) and First(alpha) from CurrentFirst(alpha) change |= first_alpha.hard_update(local_first) change |= first_x.hard_update(local_first) # First(Vt) + First(Vt) + First(RightSides) return firsts
def compute_follows(G, firsts): follows = {} change = True # init Follow(Vn) for non_terminal in G.nonTerminals: follows[non_terminal] = ContainerSet() follows[G.startSymbol] = ContainerSet(G.EOF) while change: change = False # P: X -> alpha for production in G.Productions: X = production.Left alpha = production.Right for w in range(0, len(alpha)): if alpha[w].IsTerminal: continue if w < len(alpha) - 1: t = alpha[w + 1] else: t = G.Epsilon if w == len(alpha) - 1: change = follows[alpha[w]].add(follows[X]) continue first = firsts[t] cop = copy.copy(first) cop.set_epsilon(False) change = follows[alpha[w]].add(cop) if first.contains_epsilon: change = follows[alpha[w]].add(follows[X]) return follows
def epsilon_closure(automaton, states): pending = [s for s in states ] # equivalente a list(states) pero me gusta así :p closure = {s for s in states } # equivalente a set(states) pero me gusta así :p while pending: state = pending.pop() for trans in automaton.epsilon_transitions(state): if not trans in closure: pending.append(trans) closure.add(trans) resp = ContainerSet(*closure) return resp
def closure_lr1(items, firsts): closure = ContainerSet(*items) changed = True while changed: changed = False new_items = ContainerSet() for item in closure: exp = expand(item, firsts) new_items.extend(exp) changed = closure.update(new_items) return compress(closure)
def compute_local_first(firsts, alpha): first_alpha = ContainerSet() try: alpha_is_epsilon = alpha.IsEpsilon except: alpha_is_epsilon = False if alpha_is_epsilon: first_alpha.set_epsilon() else: for symbol in alpha: first_alpha.update(firsts[symbol]) if not firsts[symbol].contains_epsilon: break else: first_alpha.set_epsilon() return first_alpha
def build_lr1_automaton(grammar): assert len(grammar.startSymbol.productions) == 1, 'Grammar must be augmented' firsts = compute_firsts(grammar) firsts[grammar.EOF] = ContainerSet(grammar.EOF) start_production = grammar.startSymbol.productions[0] start_item = Item(start_production, 0, lookaheads=(grammar.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 grammar.terminals + grammar.nonTerminals: kernels = goto_lr1(current_state.state, symbol, just_kernel=True) if not kernels: continue try: next_state = visited[kernels] except KeyError: pending.append(kernels) visited[pending[-1]] = next_state = State(frozenset(goto_lr1(current_state.state, symbol, firsts)), True) current_state.add_transition(symbol.Name, next_state) automaton.set_formatter(multi_line_formatter) return automaton