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

slightly polished demo

parent 2f375562
Loading
Loading
Loading
Loading
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
+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:
@@ -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()

        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():
@@ -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 \
@@ -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)

test.py

deleted100644 → 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