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
Pipeline #53402 failed with stage
in 20 seconds
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
......@@ -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))
......@@ -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
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment