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

slightly polished demo

parent 2f375562
Pipeline #53377 failed with stage
in 20 seconds
from parser import Parser
from objects import Terminal, Nonterminal, State, DFA, REG
from objects import Composition as comp
from typing import Set, Dict, Tuple
def make_dfa(states: Set[str], terminals: Set[str],
transition: Dict[Tuple[str, str], str], init: str, final: Set[str]):
dfa = DFA(set(), set(), {}, None, set())
dfa.init = State(init)
for name in states:
state = State(name)
dfa.states.add(state)
for name in terminals:
terminal = Terminal(name)
dfa.terminals.add(terminal)
for name in final:
state = State(name)
dfa.final.add(state)
for x, y in transition:
state = State(x)
terminal = Terminal(y)
move = State(transition[x, y])
dfa.transition[state, terminal] = move
return dfa
def reg_1():
S = Nonterminal("S")
A = Nonterminal("A")
a = Terminal("a")
nonterminals = {S, A}
init = S
terminals = {a}
rules = dict()
rules[S] = {(a, S), (a)}
rules[A] = {(a)}
reg = REG(nonterminals, terminals, rules, init)
return reg
def reg_2():
S = Nonterminal("S")
A = Nonterminal("A")
a = Terminal("a")
nonterminals = {S, A}
init = S
terminals = {a}
rules = dict()
reg = REG(nonterminals, terminals, rules, init)
return reg
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",
("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()
parser = Parser()
print("parser.dfa_to_str():")
print("String representation of DFA, simple/full.")
print()
print(parser.dfa_to_str(dfa_1))
print()
print(parser.dfa_to_str(dfa_1, True))
print()
print("______________")
print("DFA.accepts():")
print("Test of DFA accepting given word:")
print("dfa_1.accepts('a'):", dfa_1.accepts("a"))
print("dfa_1.accepts('ab'):", dfa_1.accepts("ab"))
print()
print("________________________________")
print("DFA.make_total(), DFA.is_total()")
print("Add new terminal for DFA, check its totality, then make total.")
print()
b = Terminal("b")
dfa_1.terminals.add(b)
print("dfa_1.terminals.add(b), dfa_1.is_total():", dfa_1.is_total())
print()
total_dfa = dfa_1.total()
print("dfa_1.total(), total_dfa.is_total():", total_dfa.is_total())
print()
print(parser.dfa_to_str(total_dfa, True))
print()
print("________________")
print("DFA.complement()")
print("Complement of DFA.")
compl_dfa = dfa_1.complement()
print(parser.dfa_to_str(compl_dfa, True))
print()
print("_________________")
print("DFA.composition()")
print("Composition makes union, intersection and subtraction of two DFAs.")
print()
print("second DFA:")
print(parser.dfa_to_str(dfa_2, True))
print()
print("Composition:")
union = dfa_1.composition(dfa_1, dfa_2, comp.Union)
print(parser.dfa_to_str(union, True))
print()
print("Intersection:")
intersection = dfa_1.composition(dfa_1, dfa_2, comp.Intersection)
print(parser.dfa_to_str(intersection, True))
print()
print("___________________________")
print("DFA.eliminate_unreachable()")
print("Add new state (also to transition) and eliminate unreachable states.")
q_x = State("q_x")
a = Terminal("a")
dfa_1.states.add(q_x)
dfa_1.final.add(q_x)
dfa_1.transition[q_x, a] = q_x
print()
print("DFA with new state:")
print(parser.dfa_to_str(dfa_1, True))
print()
print("DFA without unreachable states:")
eliminated = dfa_1.eliminate_unreachable()
print(parser.dfa_to_str(eliminated, True))
print()
print("______________")
print("DFA.minimize()")
print("Minimization of DFA from slides:")
print()
print(parser.dfa_to_str(dfa_3, True))
print()
print("Minimal DFA:")
minimal = dfa_3.minimize()
print(parser.dfa_to_str(minimal, True))
print()
print("______________")
print("DFA.canonize()")
print("Canonization of previous minimized DFA.")
print()
canonical = minimal.canonize()
print(parser.dfa_to_str(canonical, True))
main()
\ No newline at end of file
from __future__ import annotations
from typing import Set, List, Dict, Tuple, Optional, Any
from typing import Set, List, Dict, Tuple, Optional, TypeVar
from enum import Enum
import enum
from copy import deepcopy
class Operation(Enum):
class Composition(Enum):
Union = enum.auto()
Intersection = enum.auto()
Subtraction = enum.auto() # difference?
Subtraction = enum.auto()
class Terminal:
......@@ -24,7 +24,11 @@ class Terminal:
return hash(self.name)
# TODO ask - only name equality?
class Eps:
def __init__(self):
pass
class Nonterminal:
def __init__(self, name: str):
self.name = name
......@@ -53,24 +57,32 @@ class State:
class REG:
Rules = Dict[Nonterminal, Set[Tuple[Terminal, Optional[Nonterminal]]]]
type_var = TypeVar('type_var')
def __init__(self, nonterminals: Set[Nonterminal],
terminals: Set[Terminal],
rules: Rules,
init: Nonterminal):
self.nonterminals = nonterminals
self.terminals = terminals
self.rules = rules
def set_or_none(self, out : Optional[Set[type_var]]) -> Set[type_var]:
return set() if out is None else out
def dict_or_none(self, out : Optional[Dict[type_var]]) -> Set[type_var]:
return dict() if out is None else out
def __init__(self, nonterminals: Set[Nonterminal] = None,
terminals: Set[Terminal] = None,
rules: Rules = None,
init: Nonterminal = None):
self.nonterminals = self.set_or_none(nonterminals)
self.terminals = self.set_or_none(terminals)
self.rules = self.dict_or_none(rules)
self.init = init
class DFA:
Transition = Dict[Tuple[State, Terminal], State]
type_var = TypeVar('type_var')
def set_or_none(self, out : Optional[Set[Any]]) -> Set[Any]:
def set_or_none(self, out : Optional[Set[type_var]]) -> Set[type_var]:
return set() if out is None else out
def dict_or_none(self, out : Optional[Dict[Any]]) -> Set[Any]:
def dict_or_none(self, out : Optional[Dict[type_var]]) -> Set[type_var]:
return dict() if out is None else out
def __init__(self, states: Set[State] = None,
......@@ -78,6 +90,11 @@ class DFA:
transition: Transition = None,
init: State = None,
final: Set[State] = None):
# TODO learn how to assert
# assert all or nothing
# assert init exists
# assert final is subset of states
# valid automaton has min one state!
self.states = self.set_or_none(states)
self.terminals = self.set_or_none(terminals)
self.transition = self.dict_or_none(transition)
......@@ -85,11 +102,14 @@ class DFA:
self.final = self.set_or_none(final)
def total(self) -> DFA:
if self.is_total():
return self
dfa = deepcopy(self)
# total: Transition
total = {}
# TODO consider import itertools
# check: consider import itertools
hell_id = 0
hell = State(str(hell_id))
while hell in dfa.states:
......@@ -116,8 +136,8 @@ class DFA:
def accepts(self, word: str) -> bool:
state = self.init
for i in range(len(word)):
terminal = Terminal(word[i])
for character in word:
terminal = Terminal(character)
if (state, terminal) not in self.transition:
return False
state = self.transition[state, terminal]
......@@ -126,18 +146,13 @@ class DFA:
def complement(self) -> DFA:
dfa = deepcopy(self)
if not dfa.is_total():
dfa.total()
dfa = dfa.total()
new_final = set()
for state in dfa.states:
if state not in dfa.final:
new_final.add(state)
dfa.final = new_final
dfa.final = dfa.states.difference(dfa.final)
return dfa
@staticmethod
def composition(dfa_1: DFA, dfa_2: DFA, operation: Operation) -> DFA:
def composition(dfa_1: DFA, dfa_2: DFA, operation: Composition) -> DFA:
# 2019 IB102 slides want both total
if not dfa_1.is_total():
......@@ -151,32 +166,33 @@ class DFA:
dfa.terminals = dfa_1.terminals.union(dfa_2.terminals)
# ATTENTION: terminals may differ -> make total at the end?
# new states
for state_1 in dfa_1.states:
for state_2 in dfa_2.states:
# f"{state_1.name}_{state_2.name}" ?
state = State(state_1.name + "_" + state_2.name)
dfa.states.add(state)
# new init
dfa.init = State(f"{dfa_1.init.name}_{dfa_2.init.name}")
dfa.states.add(dfa.init)
# new init
if not dfa.init and state_1 == dfa_1.init \
and state_2 == dfa_2.init:
dfa.init = state
# new final TODO also outside
for state_1 in dfa_1.final:
for state_2 in dfa_2.final:
state = State(f"{state_1.name}_{state_2.name}")
dfa.states.add(state)
# new final
if state_1 in dfa_1.final or state_2 in dfa_2.final:
if operation == Composition.Union:
dfa.final.add(state)
if operation == Operation.Union:
dfa.final.add(state)
elif operation == Composition.Intersection and \
state_1 in dfa_1.final and \
state_2 in dfa_2.final:
dfa.final.add(state)
elif operation == Operation.Intersection and \
state_1 in dfa_1.final and \
state_2 in dfa_2.final:
dfa.final.add(state)
elif operation == Composition.Subtraction and \
state_2 not in dfa_2.final:
dfa.final.add(state)
elif operation == Operation.Subtraction and \
state_2 not in dfa_2.final: # TODO is it this?
dfa.final.add(state)
# rest of states
for state_1 in dfa_1.states:
for state_2 in dfa_2.states:
state = State(f"{state_1.name}_{state_2.name}") # TODO avoid conflicts!
dfa.states.add(state)
# new transition
for terminal in dfa.terminals:
......@@ -191,17 +207,17 @@ class DFA:
@staticmethod
def union(dfa_1: DFA, dfa_2: DFA) -> DFA:
return DFA.composition(dfa_1, dfa_2, Operation.Union)
return DFA.composition(dfa_1, dfa_2, Composition.Union)
@staticmethod
def intersection(dfa_1: DFA, dfa_2: DFA) -> DFA:
return DFA.composition(dfa_1, dfa_2, Operation.Intersection)
return DFA.composition(dfa_1, dfa_2, Composition.Intersection)
@staticmethod
def subtraction(dfa_1: DFA, dfa_2: DFA) -> DFA:
return DFA.composition(dfa_1, dfa_2, Operation.Subtraction)
return DFA.composition(dfa_1, dfa_2, Composition.Subtraction)
def eliminate_unreachable(self): # -> DFA:
def eliminate_unreachable(self) -> DFA:
dfa = deepcopy(self)
previous = set()
......@@ -223,6 +239,8 @@ class DFA:
return dfa
# TODO sorted(set, key = lambda x: x.name) sorted_terminals
def sort_terminals(self) -> List[Terminal]:
to_sort = deepcopy(self.terminals)
to_sort = list(to_sort)
......@@ -242,7 +260,7 @@ class DFA:
terminals = self.sort_terminals() # bc I want to iterate always in same order
previous = []
previous : Optional[Dict[State, int]] = None # TODO check this also at other places
actual = dict()
for state in dfa.states:
if state in dfa.final:
......@@ -327,7 +345,7 @@ class DFA:
terminals = dfa.sort_terminals()
i = 0
queue = [dfa.init]
queue = [dfa.init] # TODO deque
while len(queue) > 0:
actual = queue.pop(0)
......
from parser import Parser
from objects import Terminal, Nonterminal, State, DFA, REG
from objects import Operation as op
def test_dfa():
q_0 = State("q_0")
q_1 = State("q_1")
a = Terminal("a")
states = {q_0, q_1}
init = q_0
final = {q_1}
terminals = {a}
transition = dict()
transition[q_0, a] = q_1
transition[q_1, a] = q_1
dfa = DFA(states, terminals, transition, init, final)
return dfa
def test_dfa2():
r_0 = State("r_0")
r_1 = State("r_1")
r_2 = State("r_2")
a = Terminal("a")
b = Terminal("b")
states = {r_0, r_1, r_2}
init = r_0
final = {r_2}
terminals = {a, b}
transition = dict()
transition[r_0, a] = r_1
transition[r_0, b] = r_1
transition[r_1, a] = r_2
transition[r_1, b] = r_2
dfa = DFA(states, terminals, transition, init, final)
return dfa
def test_dfa_min():
states = set()
st = {}
for i in range(1,8):
state = State(str(i))
st[str(i)] = state
states.add(state)
a = Terminal("a")
b = Terminal("b")
init = st[str(1)]
final = {st[str(3)], st[str(5)], st[str(6)]}
terminals = {a, b}
transition = dict()
transition[st[str(1)], a] = st[str(2)]
transition[st[str(2)], a] = st[str(3)]
transition[st[str(2)], b] = st[str(4)]
transition[st[str(3)], a] = st[str(6)]
transition[st[str(3)], b] = st[str(5)]
transition[st[str(4)], a] = st[str(3)]
transition[st[str(4)], b] = st[str(2)]
transition[st[str(5)], a] = st[str(6)]
transition[st[str(5)], b] = st[str(3)]
transition[st[str(6)], a] = st[str(2)]
transition[st[str(7)], a] = st[str(6)]
transition[st[str(7)], b] = st[str(1)]
dfa = DFA(states, terminals, transition, init, final)
return dfa
def test_reg():
S = Nonterminal("S")
A = Nonterminal("A")
a = Terminal("a")
nonterminals = {S, A}
init = S
terminals = {a}
rules = dict()
rules[S] = {(a, S), (a)}
rules[A] = {(a)}
reg = REG(nonterminals, terminals, rules, init)
return reg
def test_reg2():
S = Nonterminal("S")
A = Nonterminal("A")
a = Terminal("a")
nonterminals = {S, A}
init = S
terminals = {a}
rules = dict()
reg = REG(nonterminals, terminals, rules, init)
return reg
def main():
dfa = test_dfa()
dfa2 = test_dfa2()
reg = test_reg()
reg2 = test_reg2()
parser = Parser()
print("test: dfa_to_str")
print(parser.dfa_to_str(dfa))
print()
print(parser.dfa_to_str(dfa, True))
print()
print("test: accepts")
print(dfa.accepts("a"), dfa.accepts("aa"), dfa.accepts("ab"))
print("test: add terminal, make total, is_total")
b = Terminal("b")
dfa.terminals.add(b)
print(dfa.is_total())
total_dfa = dfa.total()
print(parser.dfa_to_str(total_dfa, True))
print(total_dfa.is_total())
print()
print("test: complement")
compl_dfa = dfa.complement()
print(parser.dfa_to_str(compl_dfa, True))
print()
print("test: reg_to_str")
print(parser.reg_to_str(reg))
print()
print(parser.reg_to_str(reg, True))
print()
print(parser.reg_to_str(reg2))
print()
print(parser.reg_to_str(reg2, True))
print()
print("test: second dfa_to_str")
print(parser.dfa_to_str(dfa2, True))
print()
print("test: composition")
union = dfa.composition(dfa, dfa2, op.Union)
print(parser.dfa_to_str(union, True))
print()
intersection = dfa.composition(dfa, dfa2, op.Intersection)
print(parser.dfa_to_str(intersection, True))
print()
print("test: add state, eliminate_unreachable")
q_x = State("q_x")
a = Terminal("a")
dfa.states.add(q_x)
dfa.final.add(q_x)
dfa.transition[q_x,a] = q_x
print(parser.dfa_to_str(dfa, True))
print()
eliminated = dfa.eliminate_unreachable()
print(parser.dfa_to_str(eliminated, True))
print()
dfa_min = test_dfa_min()
print(parser.dfa_to_str(dfa_min, True))
print("test: minimize")
minimal = dfa_min.minimize()
print(parser.dfa_to_str(minimal, True))
print("test: canonize")
canonical = minimal.canonize()
print(parser.dfa_to_str(canonical, True))
main()
\ No newline at end of file
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