def remove_rules_with_epsilon(grammar: Grammar, transform_grammar=False) -> Grammar: """ Remove epsilon rules. :param grammar: Grammar where rules remove :param transform_grammar: True if transformation should be performed in place, false otherwise. False by default. :return: Grammar without epsilon rules. """ # Copy if required if transform_grammar is False: grammar = copy(grammar) # Find nonterminals rewritable to epsilon rewritable = find_nonterminals_rewritable_to_epsilon(grammar) # Create list from rules rules = list(grammar.rules()) index = 0 # Iterate thought rules while index < len(rules): rule = rules[index] index += 1 right = rule.right if right == [EPSILON]: if not grammar.start_isSet( ) or rule.fromSymbol != grammar.start_get(): grammar.remove_rule(rule) # Continue IS executed, but due optimalization line is marked as missed. continue #pragma: no cover for rule_index in range(len(right)): symbol = right[rule_index] if symbol in rewritable: new_rule = _create_rule(rule, rule_index, rewritable) rules.append(new_rule) grammar.add_rule(new_rule) return grammar
def remove_unreachable_symbols(grammar: Grammar, transform_grammar=False) -> Grammar: """ Remove unreachable symbols from the gramar :param grammar: Grammar where to symbols remove :param transform_grammar: True if transformation should be performed in place, false otherwise. False by default. :return: Grammar without unreachable symbols. """ # Copy if required if transform_grammar is False: grammar = copy(grammar) # Check if start symbol is set if not grammar.start_isSet(): raise StartSymbolNotSpecifiedException() # Create process sets reachable = {grammar.start_get()} rules = grammar.rules() # Begin iterations while True: # Create sets for current iteration active = reachable.copy() processedRules = [] # Loop rest of rules for rule in rules: # If left part of rule already in reachable symbols if rule.fromSymbol in reachable: # Set symbols as reachable processedRules.append(rule) for symbol in rule.right: active.add(symbol) # End of rules loop # Remove processed rules for item in processedRules: rules.remove(item) # If current and previous iterations are same, than end iterations if active == reachable: break reachable = active # End of iterations # Set symbols to remove allSymbols = set(grammar.nonterms()).union( set(x.s for x in grammar.terms())) for symbol in allSymbols.difference(reachable): try: grammar.remove_nonterm(symbol) except NotNonterminalException: grammar.remove_term(symbol) return grammar
def remove_nongenerating_nonterminals(grammar: Grammar, transform_grammar=False) -> Grammar: """ Remove nongenerating symbols from the grammar :param grammar: Grammar where to remove nongenerating symbols :param transform_grammar: True if transformation should be performed in place, false otherwise. False by default. :return: Grammar without nongenerating symbols """ # Copy if required if transform_grammar is False: grammar = copy(grammar) # Create working sets generates = set(item.s for item in grammar.terms()) generates.add(EPSILON) rules = set(rule for rule in grammar.rules()) while True: # Create set of next iteration additional = generates.copy() processedRules = [] # Iterate over unprocessed rules for rule in rules: rightPart = rule.right allIn = True # Check if all symbols on the right part of rule are in generates set for symbol in rightPart: if symbol not in generates: allIn = False break # Symbol is missing so rule is not process if not allIn: continue # Rule is process - remove it from processing rules and make symbol as generating additional.add(rule.fromSymbol) processedRules.append(rule) # End of rules iterations # Remove process rules in current iteration for item in processedRules: rules.remove(item) # If current and previous iterations are same, than end iterations if additional == generates: break # Swap sets from previous and current iterations generates = additional # Remove nonterms that are not generating for nonterm in set(grammar.nonterms()).difference(generates): grammar.remove_nonterm(nonterm) return grammar
def find_nonterminals_rewritable_to_epsilon(grammar: Grammar): """ Get nonterminals rewritable to epsilon. :param grammar: Grammar where to search. :return: Dictionary, where key is nonterminal rewritable to epsilon and value is rule that is responsible for it. """ rewritable = dict() rewritable[EPSILON] = None while True: working = rewritable.copy() for rule in grammar.rules(): allRewritable = True for symbol in rule.right: if symbol not in rewritable: allRewritable = False if allRewritable and rule.fromSymbol not in working: working[rule.fromSymbol] = rule if working == rewritable: break rewritable = working del rewritable[EPSILON] return rewritable