def _duplicate_context(grammar, orig_grammar, symbol, expansion, depth, seen): for i in range(len(grammar[symbol])): if expansion is None or grammar[symbol][i] == expansion: new_expansion = "" for (s, c) in expansion_to_children(grammar[symbol][i]): if s in seen: # Duplicated already new_expansion += seen[s] elif c == [] or depth == 0: # Terminal symbol or end of recursion new_expansion += s else: # Nonterminal symbol - duplicate # Add new symbol with copy of rule new_s = new_symbol(grammar, s) grammar[new_s] = copy.deepcopy(orig_grammar[s]) # Duplicate its expansions recursively # {**seen, **{s: new_s}} is seen + {s: new_s} _duplicate_context(grammar, orig_grammar, new_s, expansion=None, depth=depth - 1, seen={ **seen, **{ s: new_s } }) new_expansion += new_s grammar[symbol][i] = new_expansion
def alternate_reductions(self, tree, symbol, depth=-1): reductions = [] expansions = self.grammar.get(symbol, []) expansions.sort( key=lambda expansion: len( expansion_to_children(expansion))) for expansion in expansions: expansion_children = expansion_to_children(expansion) match = True new_children_reductions = [] for (alt_symbol, _) in expansion_children: child_reductions = self.subtrees_with_symbol( tree, alt_symbol, depth=depth) if len(child_reductions) == 0: match = False # Child not found; cannot apply rule break new_children_reductions.append(child_reductions) if not match: continue # Try next alternative # Use the first suitable combination for new_children in possible_combinations(new_children_reductions): new_tree = (symbol, new_children) if number_of_nodes(new_tree) < number_of_nodes(tree): reductions.append(new_tree) if not self.try_all_combinations: break # Sort by number of nodes reductions.sort(key=number_of_nodes) return reductions
def derivation_reductions(self, tree): (symbol, children) = tree if len(children) == 0: return [] # Terminal symbol print("Trying alternative expansions for " + symbol) # Possible expansions for this symbol expansions = self.grammar[symbol] print("Expansions: " + repr(expansions)) alternatives = \ [expansion_to_children(expansion) for expansion in expansions] reductions = [] for alternative in alternatives: if len(alternative) > len(children): continue # New alternative has more children match = True new_children_reductions = [] # print("Trying alternative expansion " + queue_to_string(alternative)) for alt_child in alternative: (alt_symbol, _) = alt_child child_reductions = subtrees_with_symbol(alt_symbol, tree) if len(child_reductions) == 0: # Child not found; cannot apply rule match = False break # print("Found alternatives " + queue_to_string(child_reductions)) new_children_reductions.append(child_reductions) if not match: continue # Try next alternative # Go through the possible combinations for new_children in possible_combinations(new_children_reductions): new_tree = (symbol, new_children) if number_of_nodes(new_tree) >= number_of_nodes(tree): continue # No reduction reductions.append(new_tree) # Apply this recursively if children is not None: for i in range(0, len(children)): child = children[i] child_reductions = self.derivation_reductions(child) for reduced_child in child_reductions: new_children = (children[:i] + [reduced_child] + children[i + 1:]) reductions.append((symbol, new_children)) # Filter duplicates unique_reductions = [] for r in reductions: if r not in unique_reductions: unique_reductions.append(r) reductions = unique_reductions if len(reductions) > 0: # We have a new expansion print("Can reduce " + symbol + " " + all_terminals(tree) + " to reduced subtrees " + queue_to_string(reductions)) return reductions