예제 #1
0
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
예제 #2
0
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
예제 #3
0
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