Loading demo.py 0 → 100644 +160 −0 Original line number Diff line number Diff line 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 objects.py +69 −51 Original line number Diff line number Diff line 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: Loading @@ -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 Loading Loading @@ -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, Loading @@ -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) Loading @@ -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: Loading @@ -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] Loading @@ -126,18 +146,13 @@ class DFA: def complement(self) -> DFA: dfa = deepcopy(self) if not dfa.is_total(): dfa.total() new_final = set() for state in dfa.states: if state not in dfa.final: new_final.add(state) dfa = dfa.total() 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(): Loading @@ -151,33 +166,34 @@ 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 if not dfa.init and state_1 == dfa_1.init \ and state_2 == dfa_2.init: dfa.init = state dfa.init = State(f"{dfa_1.init.name}_{dfa_2.init.name}") dfa.states.add(dfa.init) # new final if state_1 in dfa_1.final or state_2 in dfa_2.final: # 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) if operation == Operation.Union: if operation == Composition.Union: dfa.final.add(state) elif operation == Operation.Intersection and \ 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.Subtraction and \ state_2 not in dfa_2.final: # TODO is it this? elif operation == Composition.Subtraction and \ state_2 not in dfa_2.final: 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: if (state_1, terminal) in dfa_1.transition and \ Loading @@ -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() Loading @@ -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) Loading @@ -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: Loading Loading @@ -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) Loading test.pydeleted 100644 → 0 +0 −177 Original line number Diff line number Diff line 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 Loading
demo.py 0 → 100644 +160 −0 Original line number Diff line number Diff line 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
objects.py +69 −51 Original line number Diff line number Diff line 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: Loading @@ -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 Loading Loading @@ -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, Loading @@ -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) Loading @@ -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: Loading @@ -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] Loading @@ -126,18 +146,13 @@ class DFA: def complement(self) -> DFA: dfa = deepcopy(self) if not dfa.is_total(): dfa.total() new_final = set() for state in dfa.states: if state not in dfa.final: new_final.add(state) dfa = dfa.total() 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(): Loading @@ -151,33 +166,34 @@ 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 if not dfa.init and state_1 == dfa_1.init \ and state_2 == dfa_2.init: dfa.init = state dfa.init = State(f"{dfa_1.init.name}_{dfa_2.init.name}") dfa.states.add(dfa.init) # new final if state_1 in dfa_1.final or state_2 in dfa_2.final: # 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) if operation == Operation.Union: if operation == Composition.Union: dfa.final.add(state) elif operation == Operation.Intersection and \ 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.Subtraction and \ state_2 not in dfa_2.final: # TODO is it this? elif operation == Composition.Subtraction and \ state_2 not in dfa_2.final: 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: if (state_1, terminal) in dfa_1.transition and \ Loading @@ -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() Loading @@ -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) Loading @@ -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: Loading Loading @@ -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) Loading
test.pydeleted 100644 → 0 +0 −177 Original line number Diff line number Diff line 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