Exemplo n.º 1
0
    def test_rg_to_nfa_conversion(self) -> None:
        grammar = RegularGrammar("S", {"S": {"&"}})
        nfa = NFA.from_regular_grammar(grammar)
        self.assertTrue(nfa.accept(""))

        test_nfa = TestNFA()

        grammar = RegularGrammar(
            "S", {
                "S": {"0S", "1A", "0"},
                "A": {"0B", "1C"},
                "B": {"0D", "1S", "1"},
                "C": {"0A", "1B"},
                "D": {"0C", "1D"}
            })
        nfa = NFA.from_regular_grammar(grammar)
        true_cases = {"0", "101", "1000101011"}
        false_cases = {"", "1", "101010111", "11101010"}
        test_nfa.nfa_test(nfa, true_cases, false_cases)

        grammar = RegularGrammar(
            "S'", {
                "S'": {"aA", "cC", "bA", "bC", "&"},
                "S": {"aA", "cC", "bA", "bC"},
                "A": {"bS", "cD", "b", "c"},
                "C": {"bS", "aE", "b", "a"},
                "D": {"aA", "bA", "bC"},
                "E": {"cC", "bC", "bA"},
            })
        nfa = NFA.from_regular_grammar(grammar)
        true_cases = {"", "abab", "caca", "abcabbbacb"}
        false_cases = {"aa", "cc", "bbb", "babcb"}
        test_nfa.nfa_test(nfa, true_cases, false_cases)
Exemplo n.º 2
0
    def test_union(self) -> None:
        first_nfa = NFA.load("examples/aa.json")
        second_nfa = NFA.load("examples/endsWbb.json")

        first_nfa.union(second_nfa)
        self.nfa_test(first_nfa, {"aa", "abb", "abbaabb"},
                      {"abbaab", "ab", "aaa"})
Exemplo n.º 3
0
    def test_minimization(self) -> None:
        nfa = NFA.load("examples/bdiv3.json")
        nfa.minimize()
        self.assertEqual(len(nfa.states), 3)
        true_cases = {"", "aa", "bbb", "abababa", "aaaaabbaababaaabb"}
        false_cases = {"ba", "bbaaa", "ababababa", "bbaaabb", "babbaaabba"}
        self.nfa_test(nfa, true_cases, false_cases)

        nfa = NFA.load("examples/one1.json")
        nfa.minimize()
        self.assertEqual(len(nfa.states), 2)
        true_cases = {"1", "01", "10", "000000000100000"}
        false_cases = {"", "0", "00", "11", "111", "0100001", "1000001"}
        self.nfa_test(nfa, true_cases, false_cases)

        nfa = NFA.load("examples/div5.json")
        nfa.determinize()
        nfa.minimize()
        self.assertEqual(len(nfa.states), 6)
        true_cases = {"0", "101", "1000101011"}
        false_cases = {"", "1", "101010111", "11101010"}
        self.nfa_test(nfa, true_cases, false_cases)

        nfa = regex_to_dfa("aaa*|a*")
        nfa.minimize()
        self.assertEqual(len(nfa.states), 1)

        nfa = NFA.load("examples/endsWbb.json")
        with self.assertRaises(RuntimeError):
            nfa.minimize()
        with self.assertRaises(RuntimeError):
            nfa.merge_equivalent()
Exemplo n.º 4
0
    def test_equivalence(self) -> None:
        first_nfa = NFA.load("examples/aaORbb.json")
        second_nfa = NFA.load("examples/one1.json")

        self.assertFalse(first_nfa.is_equal(second_nfa))
        self.assertFalse(second_nfa.is_equal(first_nfa))
        self.assertTrue(first_nfa.is_equal(first_nfa))
        self.assertTrue(second_nfa.is_equal(second_nfa))
Exemplo n.º 5
0
    def test_accept(self) -> None:
        nfa = NFA.load("examples/div3.json")
        true_cases = {"110100111", "111111000", "1110000001"}
        false_cases = {"1000000110", "110001", "1010001010", "211"}
        self.nfa_test(nfa, true_cases, false_cases)

        nfa = NFA.load("examples/aaORbb.json")
        true_cases = {"baabaabab", "bbababbbab", "babaaaaba"}
        false_cases = {"", "aabb", "bbaa", "ababababaababababb"}
        self.nfa_test(nfa, true_cases, false_cases)
Exemplo n.º 6
0
 def _grammar_to_nfa(self) -> None:
     try:
         self._nfa = NFA.from_regular_grammar(
             parse_grammar_text(self.grammarText.toPlainText()))
         self._update_table()
     except RuntimeError as error:
         QMessageBox.information(self, "Error", error.args[0])
Exemplo n.º 7
0
    def __init__(self) -> None:
        QMainWindow.__init__(self)

        self.setupUi(self)
        self.resize(600, 400)

        self.regexToDFAButton.clicked.connect(self._regex_to_dfa)

        self.addSymbolButton.clicked.connect(self._add_symbols)
        self.addStateButton.clicked.connect(self._add_states)
        self.removeSymbolButton.clicked.connect(self._remove_symbols)
        self.removeStateButton.clicked.connect(self._remove_states)
        self.finalStateButton.clicked.connect(self._toggle_final_states)

        self.fromNFAbutton.clicked.connect(self._nfa_to_grammar)
        self.toNFAbutton.clicked.connect(self._grammar_to_nfa)

        self.testButton.clicked.connect(self._test_string)

        self.actionNew.triggered.connect(self._new)
        self.actionOpen.triggered.connect(self._open)
        self.actionSave.triggered.connect(self._save)

        self.actionDeterminize.triggered.connect(self._determinize)

        self.actionRemove_unreachable_states.triggered.connect(
            self._remove_unreachable)
        self.actionRemove_dead_states.triggered.connect(self._remove_dead)
        self.actionMerge_equivalent_states.triggered.connect(
            self._merge_equivalent)
        self.actionFull_minimization.triggered.connect(self._minimize)

        self.action_to_abc.triggered.connect(self._beautify_abc)
        self.action_to_qn.triggered.connect(self._beautify_qn)

        self.actionUnion.triggered.connect(self._union)
        self.actionComplement.triggered.connect(self._complement)
        self.actionIntersection.triggered.connect(self._intersection)
        self.actionContains.triggered.connect(self._contains)
        self.actionEquivalent.triggered.connect(self._is_equal)

        self.transitionTable.cellChanged.connect(self._update_nfa)

        self._grammar = RegularGrammar()
        self._nfa = NFA()
        self._update_table()
Exemplo n.º 8
0
    def test_nfa_to_rg_conversion(self) -> None:
        grammar = RegularGrammar.from_nfa(NFA.load("examples/div3.json"))
        self.assertTrue(grammar.initial_symbol() == "S'")
        self.assertTrue(
            grammar.productions() == {
                "S'": {"0S", "1A", "0", "&"},
                "S": {"0S", "1A", "0"},
                "A": {"0B", "1S", "1"},
                "B": {"0A", "1B"}
            })

        grammar = RegularGrammar.from_nfa(NFA.load("examples/endsWbb.json"))
        self.assertTrue(grammar.initial_symbol() == "S")
        self.assertTrue(grammar.productions() == {
            "S": {"aS", "bS", "bA"},
            "A": {"b", "bB"}
        })
Exemplo n.º 9
0
    def test_intersection(self) -> None:
        first_nfa = NFA.load("examples/aaORbb.json")
        second_nfa = NFA.load("examples/aa.json")

        first_nfa.intersection(second_nfa)
        self.nfa_test(first_nfa, {"aa"}, {"", "bb", "bbaa"})

        first_nfa = NFA.load("examples/bb.json")
        second_nfa = NFA.load("examples/aa.json")

        first_nfa.intersection(second_nfa)
        self.assertTrue(first_nfa.is_empty())

        first_nfa = regex_to_dfa("a")
        second_nfa = regex_to_dfa("b")
        first_nfa.intersection(second_nfa)
        self.assertTrue(first_nfa.is_empty())
Exemplo n.º 10
0
 def _intersection(self) -> None:
     try:
         path, _ = QFileDialog.getOpenFileName(self)
         if path:
             second_nfa = NFA.load(path)
             self._nfa.intersection(second_nfa)
             self._update_table()
     except RuntimeError as error:
         QMessageBox.information(self, "Error", error.args[0])
Exemplo n.º 11
0
def regex_to_dfa(regex: str) -> NFA:
    """ Transforms a RegExp into a DFA using the De Simone/Aho method. """
    root = RegExpParser(regex).parse()
    thread_tree(root)

    alphabet: Set[str] = set()
    transitions: Dict[Tuple[str, str], Set[str]] = {}
    initial_state = "q0"
    final_states: Set[str] = set()
    states = {initial_state}

    initial_nodes = frozenset(root.down())
    compositions = {initial_nodes: initial_state}
    if END_NODE in initial_nodes:
        final_states.add(initial_state)

    new_compositions = {initial_nodes}
    while new_compositions:
        symbols: Dict[str, Set[Node]] = defaultdict(set)
        composition = new_compositions.pop()  # composition of the new state

        # separate nodes of the same symbol
        for node in composition:
            if node.symbol != END:
                symbols[node.symbol].add(node)

        # build the new state transitions
        for symbol, nodes in symbols.items():
            # create composition of the new state, that is, the nodes of the
            # tree you're in, when you're in that state
            new_state_composition: Set[Node] = set()
            for node in nodes:
                new_state_composition.update(node.right.up())
            frozen_new_composition = frozenset(new_state_composition)

            # if there's a state with the same composition, they're equivalent,
            # no need to create another one
            if frozen_new_composition in compositions:
                new_state = compositions[frozen_new_composition]
            else:  # else, create the new state
                new_state = "q" + str(len(compositions))
                compositions[frozen_new_composition] = new_state
                new_compositions.add(frozen_new_composition)
                if END_NODE in frozen_new_composition:
                    final_states.add(new_state)

            transitions[compositions[composition], symbol] = {new_state}

    for (state, symbol), next_state in transitions.items():
        states.update({state} | next_state)
        alphabet.add(symbol)

    Node.up.cache_clear()
    Node.down.cache_clear()
    return NFA(states, alphabet, transitions, initial_state, final_states)
Exemplo n.º 12
0
    def test_determinization(self) -> None:
        nfa = NFA.load("examples/endsWbb.json")
        self.assertFalse(nfa.is_deterministic())
        true_cases = {"bb", "abaabbabaabb", "babb", "abbabbabb"}
        false_cases = {"", "abba", "bbbbbba", "bbbaaabba", "absbb"}
        self.nfa_test(nfa, true_cases, false_cases)
        nfa.determinize()
        self.assertTrue(nfa.is_deterministic())
        self.nfa_test(nfa, true_cases, false_cases)

        nfa = NFA.load("examples/bad_case.json")
        self.assertFalse(nfa.is_deterministic())
        true_cases = {
            "babb", "abbabbabb", "baaa", "bbbb", "aababa", "bbbbbbbb"
        }
        false_cases = {"", "abbb", "aaaaaa"}
        self.nfa_test(nfa, true_cases, false_cases)
        nfa.determinize()
        self.assertTrue(nfa.is_deterministic())
        self.nfa_test(nfa, true_cases, false_cases)
Exemplo n.º 13
0
 def _is_equal(self) -> None:
     try:
         path, _ = QFileDialog.getOpenFileName(self)
         if path:
             second_nfa = NFA.load(path)
             if self._nfa.is_equal(second_nfa):
                 QMessageBox.information(self, "Equivalent",
                                         "The automata are equivalent.")
             else:
                 QMessageBox.information(
                     self, "Equivalent", "The automata are not equivalent.")
     except RuntimeError as error:
         QMessageBox.information(self, "Error", error.args[0])
Exemplo n.º 14
0
    def test_emptiness(self) -> None:
        nfa = NFA.load("examples/one1.json")
        self.assertFalse(nfa.is_empty())
        self.assertFalse(nfa.is_finite())

        nfa = NFA.load("examples/bad_case.json")
        self.assertFalse(nfa.is_empty())
        self.assertFalse(nfa.is_finite())

        nfa = NFA.load("examples/aa.json")
        self.assertFalse(nfa.is_empty())
        self.assertTrue(nfa.is_finite())

        nfa = NFA.load("examples/empty.json")
        self.assertTrue(nfa.is_empty())

        nfa = NFA.load("examples/useless_loop.json")
        self.assertFalse(nfa.is_empty())
        self.assertTrue(nfa.is_finite())

        nfa = regex_to_dfa("aa|bbb|cccc")
        self.assertFalse(nfa.is_empty())
        self.assertTrue(nfa.is_finite())
Exemplo n.º 15
0
 def _contains(self) -> None:
     try:
         path, _ = QFileDialog.getOpenFileName(self)
         if path:
             second_nfa = NFA.load(path)
             if self._nfa.contains(second_nfa):
                 QMessageBox.information(
                     self, "Contains",
                     "The automaton contains the second one.")
             else:
                 QMessageBox.information(
                     self, "Contains",
                     "The automaton does not contain the second one.")
     except RuntimeError as error:
         QMessageBox.information(self, "Error", error.args[0])
Exemplo n.º 16
0
    def test_complement(self) -> None:
        nfa = NFA.load("examples/endsWbb.json")

        nfa.complement()
        self.nfa_test(nfa, {"ab", "babbaab"}, {"abb", "aaabb"})
Exemplo n.º 17
0
    def test_containment(self) -> None:
        first_nfa = NFA.load("examples/aaORbb.json")
        second_nfa = NFA.load("examples/aa.json")

        self.assertTrue(first_nfa.contains(second_nfa))
        self.assertFalse(second_nfa.contains(first_nfa))
Exemplo n.º 18
0
 def _new(self) -> None:
     self._nfa = NFA()
     self._grammar = RegularGrammar()
     self._update_table()
Exemplo n.º 19
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self) -> None:
        QMainWindow.__init__(self)

        self.setupUi(self)
        self.resize(600, 400)

        self.regexToDFAButton.clicked.connect(self._regex_to_dfa)

        self.addSymbolButton.clicked.connect(self._add_symbols)
        self.addStateButton.clicked.connect(self._add_states)
        self.removeSymbolButton.clicked.connect(self._remove_symbols)
        self.removeStateButton.clicked.connect(self._remove_states)
        self.finalStateButton.clicked.connect(self._toggle_final_states)

        self.fromNFAbutton.clicked.connect(self._nfa_to_grammar)
        self.toNFAbutton.clicked.connect(self._grammar_to_nfa)

        self.testButton.clicked.connect(self._test_string)

        self.actionNew.triggered.connect(self._new)
        self.actionOpen.triggered.connect(self._open)
        self.actionSave.triggered.connect(self._save)

        self.actionDeterminize.triggered.connect(self._determinize)

        self.actionRemove_unreachable_states.triggered.connect(
            self._remove_unreachable)
        self.actionRemove_dead_states.triggered.connect(self._remove_dead)
        self.actionMerge_equivalent_states.triggered.connect(
            self._merge_equivalent)
        self.actionFull_minimization.triggered.connect(self._minimize)

        self.action_to_abc.triggered.connect(self._beautify_abc)
        self.action_to_qn.triggered.connect(self._beautify_qn)

        self.actionUnion.triggered.connect(self._union)
        self.actionComplement.triggered.connect(self._complement)
        self.actionIntersection.triggered.connect(self._intersection)
        self.actionContains.triggered.connect(self._contains)
        self.actionEquivalent.triggered.connect(self._is_equal)

        self.transitionTable.cellChanged.connect(self._update_nfa)

        self._grammar = RegularGrammar()
        self._nfa = NFA()
        self._update_table()

    def _regex_to_dfa(self) -> None:
        try:
            self._nfa = regex_to_dfa(self.regexInput.text())
            self._update_table()
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _add_symbols(self) -> None:
        text, ok = QInputDialog.getText(self, "Add symbols",
                                        "Symbols (a,b,c,...):")
        if ok:
            for symbol in text.replace(" ", "").split(","):
                self._nfa.add_symbol(symbol)
            self._update_table()

    def _add_states(self) -> None:
        text, ok = QInputDialog.getText(self, "Add states",
                                        "States (q0,q1,...):")
        if ok:
            for state in text.replace(" ", "").split(","):
                self._nfa.add_state(state)
            self._update_table()

    def _remove_symbols(self) -> None:
        text, ok = QInputDialog.getText(self, "Remove symbols",
                                        "Symbols (a,b,c,...):")
        if ok:
            for symbol in text.replace(" ", "").split(","):
                self._nfa.remove_symbol(symbol)
            self._update_table()

    def _remove_states(self) -> None:
        text, ok = QInputDialog.getText(self, "Remove states",
                                        "States (q0,q1,...):")
        if ok:
            for state in text.replace(" ", "").split(","):
                self._nfa.remove_state(state)
            self._update_table()

    def _toggle_final_states(self) -> None:
        text, ok = QInputDialog.getText(self, "Final states",
                                        "States (q0,q1,...):")
        if ok:
            for state in text.replace(" ", "").split(","):
                self._nfa.toggle_final_state(state)
            self._update_table()

    def _test_emptiness(self) -> None:
        if self._nfa.is_empty():
            self.languageLabel.setText("The language is empty.")
        elif self._nfa.is_finite():
            self.languageLabel.setText("The language is finite.")
        else:
            self.languageLabel.setText("The language is infinite.")

    def _remove_unreachable(self) -> None:
        self._nfa.remove_unreachable()
        self._update_table()

    def _remove_dead(self) -> None:
        self._nfa.remove_dead()
        self._update_table()

    def _merge_equivalent(self) -> None:
        try:
            self._nfa.merge_equivalent()
            self._update_table()
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _minimize(self) -> None:
        try:
            self._nfa.minimize()
            self._update_table()
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _test_string(self) -> None:
        try:
            self.statusbar.showMessage("String accepted" if self._nfa.accept(
                self.inputString.text()) else "String rejected")
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _determinize(self) -> None:
        self._nfa.determinize()
        self._update_table()

    def _beautify_qn(self) -> None:
        self._nfa.beautify_qn()
        self._update_table()

    def _beautify_abc(self) -> None:
        try:
            self._nfa.beautify_abc()
            self._update_table()
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _nfa_to_grammar(self) -> None:
        self._grammar = RegularGrammar.from_nfa(self._nfa)
        self._update_grammar_text()

    def _grammar_to_nfa(self) -> None:
        try:
            self._nfa = NFA.from_regular_grammar(
                parse_grammar_text(self.grammarText.toPlainText()))
            self._update_table()
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _union(self) -> None:
        try:
            path, _ = QFileDialog.getOpenFileName(self)
            if path:
                second_nfa = NFA.load(path)
                self._nfa.union(second_nfa)
                self._update_table()
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _complement(self) -> None:
        self._nfa.complement()
        self._update_table()

    def _intersection(self) -> None:
        try:
            path, _ = QFileDialog.getOpenFileName(self)
            if path:
                second_nfa = NFA.load(path)
                self._nfa.intersection(second_nfa)
                self._update_table()
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _contains(self) -> None:
        try:
            path, _ = QFileDialog.getOpenFileName(self)
            if path:
                second_nfa = NFA.load(path)
                if self._nfa.contains(second_nfa):
                    QMessageBox.information(
                        self, "Contains",
                        "The automaton contains the second one.")
                else:
                    QMessageBox.information(
                        self, "Contains",
                        "The automaton does not contain the second one.")
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _is_equal(self) -> None:
        try:
            path, _ = QFileDialog.getOpenFileName(self)
            if path:
                second_nfa = NFA.load(path)
                if self._nfa.is_equal(second_nfa):
                    QMessageBox.information(self, "Equivalent",
                                            "The automata are equivalent.")
                else:
                    QMessageBox.information(
                        self, "Equivalent", "The automata are not equivalent.")
        except RuntimeError as error:
            QMessageBox.information(self, "Error", error.args[0])

    def _update_nfa(self, row: int, col: int) -> None:
        states = self._nfa.states
        alphabet = self._nfa.alphabet
        next_states = \
            set(self.transitionTable.item(row, col).text().replace(
                " ", "").split(","))

        try:
            self._nfa.set_transition(
                states[row], alphabet[col],
                next_states if next_states != {""} else set())
        except KeyError as error:
            QMessageBox.information(self, "Error", error.args[0])
            self.transitionTable.item(row, col).setText("")

        self._test_emptiness()

    def _update_table(self) -> None:
        # avoid useless calls to _update_nfa()
        self.transitionTable.cellChanged.disconnect(self._update_nfa)

        states = []
        for state in self._nfa.states:
            preffix = ""
            if state in self._nfa.final_states:
                preffix += "*"
            if state == self._nfa.initial_state:
                preffix += "->"
            states.append(preffix + state)

        alphabet = self._nfa.alphabet

        self.transitionTable.setRowCount(len(states))
        self.transitionTable.setVerticalHeaderLabels(states)

        self.transitionTable.setColumnCount(len(alphabet))
        self.transitionTable.setHorizontalHeaderLabels(alphabet)

        table = self._nfa.transition_table
        for i, state in enumerate(self._nfa.states):
            for j, symbol in enumerate(alphabet):
                transition = ",".join(sorted(table[state, symbol])) \
                    if (state, symbol) in table else ""
                self.transitionTable.setItem(i, j,
                                             QTableWidgetItem(transition))

        self._test_emptiness()

        # reconnect
        self.transitionTable.cellChanged.connect(self._update_nfa)

    def _update_grammar_text(self) -> None:
        """
            "B", {"aB", "bC", "a"} turns into
            "B -> aB | bC | a"
        """
        def transform_production(non_terminal: str, productions: Set[str]):
            return "{} -> {}".format(non_terminal,
                                     " | ".join(sorted(productions)))

        initial_symbol = self._grammar.initial_symbol()
        productions = self._grammar.productions()

        text = ""

        if initial_symbol in productions:
            text = transform_production(initial_symbol,
                                        productions[initial_symbol]) + "\n"

        for non_terminal in sorted(set(productions.keys()) - {initial_symbol}):
            text += transform_production(non_terminal,
                                         productions[non_terminal]) + "\n"

        self.grammarText.setPlainText(text)

    def _new(self) -> None:
        self._nfa = NFA()
        self._grammar = RegularGrammar()
        self._update_table()

    def _open(self) -> None:
        path, _ = QFileDialog.getOpenFileName(self)
        if path:
            self._nfa = NFA.load(path)
            self._update_table()

    def _save(self) -> None:
        path, _ = QFileDialog.getSaveFileName(self)
        if path:
            self._nfa.save(path)
Exemplo n.º 20
0
 def test_dead_removal(self) -> None:
     nfa = NFA.load("examples/one1.json")
     self.assertEqual(nfa.states, ['A', 'B', 'C', 'D', 'E', 'F'])
     nfa.remove_dead()
     self.assertEqual(nfa.states, ['A', 'B', 'C', 'D', 'E'])
Exemplo n.º 21
0
 def _open(self) -> None:
     path, _ = QFileDialog.getOpenFileName(self)
     if path:
         self._nfa = NFA.load(path)
         self._update_table()
Exemplo n.º 22
0
 def nfa_test(self, nfa: NFA, true_cases: Set[str], false_cases: Set[str]) \
         -> None:
     self.assertTrue(all(nfa.accept(s) for s in true_cases))
     self.assertFalse(any(nfa.accept(s) for s in false_cases))