Commit d0b41a0f authored by Kateřina Sloupová's avatar Kateřina Sloupová
Browse files

transformations DFA -> REG, REG -> NFA, nfa_to_str, emptiness

parent 84d04cee
Loading
Loading
Loading
Loading
Loading
+36 −3
Original line number Diff line number Diff line
from parser import Parser
from objects import Terminal, Nonterminal, State, DFA, REG
from objects import Terminal, Nonterminal, State, DFA, REG, NFA
from objects import Composition as comp
from typing import Set, Dict, Tuple

@@ -52,16 +52,26 @@ def reg_2():
    reg = REG(nonterminals, terminals, rules, init)
    return reg

def nfa_1():
    q0 = State("q0")
    q1 = State("q1")
    q2 = State("q2")
    a = Terminal("a")
    transition = {(q0, a): {q1, q2}, (q1, a): {q1}, (q2, a): {q2}}
    nfa = NFA({q0, q1, q2}, {a}, transition, q0, {q1, q2})
    return nfa

def main():
    dfa_1 = make_dfa({"q_0, q_1"}, {"a"}, {("q_0", "a"): "q_1", ("q_1", "a"): "q_1"}, "q_0", {"q_1"})
    dfa_2 = make_dfa({"r_0, r_1", "r_2"}, {"a", "b"}, {("r_0", "a"): "r_1", ("r_0", "b"): "r_1",
    dfa_1 = make_dfa({"q_0", "q_1"}, {"a"}, {("q_0", "a"): "q_1", ("q_1", "a"): "q_1"}, "q_0", {"q_1"})
    dfa_2 = make_dfa({"r_0", "r_1", "r_2"}, {"a", "b"}, {("r_0", "a"): "r_1", ("r_0", "b"): "r_1",
                                       ("r_1", "a"): "r_2", ("r_1", "b"): "r_2"}, "r_0", {"r_2"})
    dfa_3 = make_dfa({"1", "2", "3", "4", "5", "6", "7"}, {"a", "b"}, {("1", "a"): "2", ("2", "a"): "3", ("2", "b"): "4",
                    ("3", "a"): "6", ("3", "b"): "5", ("4", "a"): "3", ("4", "b"): "2", ("5", "a"): "6", ("5", "b"): "3",
                    ("6", "a"): "2", ("7", "a"): "6", ("7", "b"): "1", }, "1", {"3", "5", "6"})

    reg1 = reg_1()
    reg2 = reg_2()
    nfa1 = nfa_1()
    parser = Parser()

    print("parser.dfa_to_str():")
@@ -156,5 +166,28 @@ def main():
    print()
    canonical = minimal.canonize()
    print(parser.dfa_to_str(canonical, True))
    print()

    print("__________________________________")
    print("DFA.dfa_to_reg(), REG.reg_to_nfa()")
    print("Transformation of DFA to REG and REG to NFA")
    print("plus string representation of REG and NFA.")
    print()
    print("Original DFA:")
    dfa_4 = make_dfa({"q_0", "q_1"}, {"a"}, {("q_0", "a"): "q_1", ("q_1", "a"): "q_1"}, "q_0", {"q_1"})
    print(parser.dfa_to_str(dfa_4, True))
    print()
    dfareg = dfa_4.dfa_to_reg()
    print("DFA transformated to REG:")
    print(parser.reg_to_str(dfareg, True))
    print()
    regdfa = dfareg.reg_to_nfa()
    print("REG transformated to NFA:")
    print(parser.nfa_to_str(regdfa, True))
    print()

    #det = nfa1.determinize()
    #print(parser.dfa_to_str(det, True))


main()
 No newline at end of file
+121 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ from typing import Set, List, Dict, Tuple, Optional, TypeVar
from enum import Enum
import enum
from copy import deepcopy
from collections import deque


class Composition(Enum):
@@ -74,6 +75,38 @@ class REG:
        self.rules = self.dict_or_none(rules)
        self.init = init

    def reg_to_nfa(self) -> NFA:
        dfa = DFA(set(), set(), {}, None, set())
        for nonterminal in self.nonterminals:
            state = State(nonterminal.name)
            dfa.states.add(state)

        dfa.terminals = self.terminals
        dfa.init = State(self.init.name)
        final = State("new_final")  # TODO assert that name is unique, as for hell in make_total
        dfa.states.add(final)
        dfa.final.add(final)

        for nonterminal in self.rules:
            for rule in self.rules[nonterminal]:

                # rule A -> a becomes d(A, a) = final
                if type(rule) == Terminal: # TODO and a is not \eps
                    if (nonterminal, rule) in dfa.transition:
                        dfa.transition[nonterminal, rule].add(final)
                    else:
                        dfa.transition[nonterminal, rule] = {final}

                # rule A -> aB becomes d(A, a) = B
                elif (nonterminal, rule[0]) in dfa.transition:
                    dfa.transition[nonterminal, rule[0]].add(rule[1])
                else:
                    dfa.transition[nonterminal, rule[0]] = {rule[1]}

        # TODO if init -> \eps: dfa.final.add(dfa.init)
        # I need to know how to treat \eps
        return dfa


class DFA:
    Transition = Dict[Tuple[State, Terminal], State]
@@ -359,6 +392,59 @@ class DFA:
            i += 1
        return dfa

    def dfa_to_reg(self) -> REG:
        reg = REG(set(), set(), {}, None)

        for state in self.states:
            nonterminal = Nonterminal(state.name)
            reg.nonterminals.add(nonterminal)

        reg.terminals = self.terminals
        init = Nonterminal("new_init")  # TODO assert that name is unique, as for hell in make_total
        reg.init = init
        reg.nonterminals.add(init)

        for state, terminal in self.transition:
            move = self.transition[state, terminal]
            if state == self.init:
                nonterminal2 = Nonterminal(move.name)
                if init in reg.rules:
                    reg.rules[init].add((terminal, nonterminal2))
                else:
                    reg.rules[init] = {(terminal, nonterminal2)}
                if move in self.final:
                    reg.rules[init].add((terminal))
            else:
                nonterminal1 = Nonterminal(state.name)
                nonterminal2 = Nonterminal(move.name)
                if nonterminal1 in reg.rules:
                    reg.rules[nonterminal1].add((terminal, nonterminal2))
                else:
                    reg.rules[nonterminal1] = {(terminal, nonterminal2)}
                if move in self.final:
                    reg.rules[nonterminal1].add((terminal))

        if self.init in self.final:
            pass # TODO eg.rules[init].add((\eps))

        return reg

    def is_empty(self) -> bool:
        reached = deque(self.init)
        reachable = set(self.init)
        while len(reached) > 0:
            actual = reachable.popleft()
            for terminal in self.terminals:
                if self.transition[actual, terminal] is not None and self.transition[actual, terminal] not in reachable:
                    reached.append(self.transition[actual, terminal])
                    reachable.add(self.transition[actual, terminal])

        return len(reachable.intersection(self.final)) > 0

    # Ha!
    def is_universal(self):
        return self.is_empty(self.complement())


class NFA:
    def __init__(self, states: Set[State],
@@ -371,3 +457,38 @@ class NFA:
        self.transition = transition
        self.init = init
        self.final = final

    def determinize(self) -> DFA:
        states = set(frozenset(self.init))
        transition = {}
        final = set()
        done = set()

        todo = states.difference(done)
        while len(todo) > 0:
            subset = iter(todo).next()# arbitrary element from set
            if subset.difference(self.final) > 0:
                final.add(subset)
            for terminal in self.terminals:
                new_subset = set()
                for state in subset:
                    if (state, terminal) in self.transition:
                        new_subset.add(self.transition[state, terminal])
                states.add(new_subset)
                transition[subset, terminal] = new_subset

            done.add(subset)

        dfa = DFA(set(), self.terminals, {}, self.init, set())
        for state in states:
            dfa.states.add(self.unset(state))
        for state in final:
            dfa.final.add(self.unset(state))
        for state, terminal in transition:
            dfa.transition[self.unset(state), terminal] = self.unset(self.transition[state, terminal])

        # states from sets


    def unset(self, states : Set[State]) -> State:
        return State('_'.join(states.name))
+42 −0
Original line number Diff line number Diff line
@@ -87,6 +87,48 @@ class Parser:
        else:
            return transition + "\n" + final


    def nfa_to_str(self, dfa: DFA, full : bool = False) -> str:

        # full - verbose description of DFA - only for development, dismiss later

        states = "{"
        for state in dfa.states:
            states += state.name + ","
        states = states[:-1] + "}"

        # Vlada: states = ",".join(dfa.states) but I use it wrong or what

        terminals = "{"
        for terminal in dfa.terminals:
            terminals += terminal.name + ","
        terminals = terminals[:-1] + "}"

        transition = ""
        for key, set_states in dfa.transition.items():
            state_1, terminal = key
            transition += "(" + state_1.name + "," + terminal.name + ")={"
            for state in set_states:
                transition += state.name + ","
            transition = transition[:-1] + "}\n"
        transition = "{" + transition[:-1] + "}"

        init = dfa.init.name

        final = "F={"
        for state in dfa.final:
            final += state.name + ","
        final = final[:-1] + "}"

        if full:
            return "NFA = (" + states + "," + terminals + ",d," + \
                   init + "," + final + ")\n" + "d = " + transition

        else:
            return transition + "\n" + final

# TODO DFA/NFA to string are too similar

# all: string <-> formal object
# reg
# dfa