Loading demo.py +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 Loading Loading @@ -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():") Loading Loading @@ -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 objects.py +121 −0 Original line number Diff line number Diff line Loading @@ -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): Loading Loading @@ -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] Loading Loading @@ -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], Loading @@ -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)) parser.py +42 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
demo.py +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 Loading Loading @@ -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():") Loading Loading @@ -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
objects.py +121 −0 Original line number Diff line number Diff line Loading @@ -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): Loading Loading @@ -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] Loading Loading @@ -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], Loading @@ -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))
parser.py +42 −0 Original line number Diff line number Diff line Loading @@ -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 Loading