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

split regular grammars and finite automata

parent 9932bacd
Loading
Loading
Loading
Loading
Loading
+7 −6
Original line number Diff line number Diff line
from parser import Parser
from objects import Character, Terminal, Nonterminal, State, DFA, REG, NFA
from objects import Composition as comp
from typing import Set, Dict, Tuple
from reg_automata import Character, State, DFA, NFA
from reg_automata import Composition as comp
from reg_grammars import REG, Terminal, Nonterminal
from typing import Set, Dict, Tuple, Union


def make_dfa(states: Set[str], characters: Set[str],
@@ -38,7 +39,7 @@ def reg_1():
    nonterminals = {S, A}
    init = S
    terminals = {a}
    rules = dict()
    rules : Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] = dict()
    rules[S] = {(a, S), (a)}
    rules[A] = {(a)}
    reg = REG(nonterminals, terminals, rules, init)
@@ -52,7 +53,7 @@ def reg_2():
    nonterminals = {S, A}
    init = S
    terminals = {a}
    rules = dict()
    rules : Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] = dict()
    reg = REG(nonterminals, terminals, rules, init)
    return reg

@@ -60,7 +61,7 @@ def nfa_1():
    q0 = State("q0")
    q1 = State("q1")
    q2 = State("q2")
    a = Terminal("a")
    a = Character("a")
    transition = {(q0, a): {q1, q2}, (q1, a): {q1}, (q2, a): {q2}}
    nfa = NFA({q0, q1, q2}, {a}, transition, q0, {q1, q2})
    return nfa
+3 −2
Original line number Diff line number Diff line
from typing import List, Dict, Tuple, Optional
import re
from objects import REG, DFA, NFA, Terminal
from reg_automata import DFA, NFA
from reg_grammars import REG, Terminal


# TODO ask: commas at the ends of lines or not?
@@ -28,7 +29,7 @@ class Parser:
        return out

    # TODO ask: import rules
    def rules_to_str(self, rules: REG.Rules):
    def rules_to_str(self, rules: REG.Rules) -> str:
        out = ""
        for rule in rules:
            out = out[:-1] + (",\n")
+8 −117
Original line number Diff line number Diff line
from __future__ import annotations
from typing import Set, FrozenSet, List, Dict, Union, Tuple, Deque, Optional, TypeVar
from typing import Set, FrozenSet, List, Dict, Union, Tuple, Deque, Optional, TypeVar, Any
from enum import Enum
import enum
from copy import deepcopy
@@ -12,19 +12,6 @@ class Composition(Enum):
    Subtraction = enum.auto()


class Terminal:
    def __init__(self, name: str):
        self.name = name

    def __eq__(self, obj):
        if isinstance(obj, Terminal):
            return obj.name == self.name
        return False

    def __hash__(self):
        return hash(self.name)


class Character:
    def __init__(self, name: str):
        self.name = name
@@ -42,19 +29,6 @@ class Eps:
    pass


class Nonterminal:
    def __init__(self, name: str):
        self.name = name

    def __eq__(self, obj):
        if isinstance(obj, Nonterminal):
            return obj.name == self.name
        return False

    def __hash__(self):
        return hash(self.name)


class State:
    def __init__(self, name: str):
        self.name = name
@@ -68,90 +42,6 @@ class State:
        return hash(self.name)


class REG:
    Rules = Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]]
    type_var = TypeVar('type_var')

    # TODO should accept init -> epsilon
    def __init__(self, nonterminals: Set[Nonterminal],
                 terminals: Set[Terminal],
                 rules: Rules,
                 init: Nonterminal):
        self.nonterminals = nonterminals
        self.terminals = terminals
        self.rules = rules
        self.init = init
        assert self.check

    # TODO ask about concept
    def check(self) -> bool:
        # scheme of grammar - empty sets and dicts are implemention only variant
        if len(self.nonterminals) == 0:
            assert len(self.terminals) == 0 and len(self.rules) == 0 and self.init == None, "nonempty scheme of grammar"
            return True

        nonterminal_names = set(map(lambda x: x.name, self.nonterminals))
        terminal_names = set(map(lambda x: x.name, self.terminals))
        assert len(nonterminal_names.intersection(terminal_names)) == 0, "name conflict"

        for nonterminal in self.rules:
            assert nonterminal in self.nonterminals, "unknown nonterminal " + nonterminal.name
            for rule in self.rules[nonterminal]:
                if isinstance(rule, Terminal):
                    assert rule in self.terminals, "unknown terminal " + rule.name
                else:
                    assert rule[0] in self.terminals, "unknown terminal " + rule[0].name
                    assert rule[1] in self.nonterminals, "unknown nonterminal " + rule[1].name

        assert self.init in self.nonterminals, "init not in nonterminals"

        return True

    def reg_to_nfa(self) -> NFA:

        init = State(self.init.name)
        states: Set[State] = set()
        for nonterminal in self.nonterminals:
            state = State(nonterminal.name)
            states.add(state)

        characters: Set[Character] = set()
        for terminal in self.terminals:
            character = Character(terminal.name)
            characters.add(character)

        final = State("new_final")  # TODO assert that name is unique, as for hell in make_total
        states.add(final)
        transition: Dict[Tuple[State, Character], Set[State]] = dict()

        for nonterminal in self.rules:
            state = State(nonterminal.name)
            for rule in self.rules[nonterminal]:

                # rule A -> a becomes d(A, a) = final
                if isinstance(rule, Terminal):  # TODO and a is not \eps
                    character = Character(rule.name)
                    if (state, character) in transition:
                        transition[state, character].add(final)
                    else:
                        transition[state, character] = {final}

                # rule A -> aB becomes d(A, a) = B
                elif (state, rule[0]) in transition:
                    character = Character(rule[0].name)
                    move = State(rule[1].name)
                    transition[state, character].add(move)
                else:
                    character = Character(rule[0].name)
                    move = State(rule[1].name)
                    transition[state, character] = {move}

        # TODO if init -> \eps: nfa.final.add(nfa.init)
        # I need to know how to treat \eps
        nfa = NFA(states, characters, transition, init, {final})
        return nfa


class DFA:
    Transition = Dict[Tuple[State, Character], State]
    type_var = TypeVar('type_var')
@@ -173,7 +63,7 @@ class DFA:
        self.transition = self.dict_or_none(transition)
        self.init = init
        self.final = self.set_or_none(final)
        assert self.check
        assert self.check()

    def check(self) -> bool:
        # # scheme of automaton - empty sets and dicts are implemention only variant
@@ -439,7 +329,9 @@ class DFA:
            i += 1
        return dfa

    def dfa_to_reg(self) -> REG:
    def dfa_to_reg(self) -> Any:
        from reg_grammars import REG, Terminal, Nonterminal

        nonterminals: Set[Nonterminal] = set()
        terminals: Set[Terminal] = set()
        rules: Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] = dict()
@@ -484,10 +376,9 @@ class DFA:
        return reg

    def is_empty(self) -> bool:
        reached: Deque[State] = deque()
        reached.append(self.init)  # TODO ask: I need to do it like this bc State is not iterable - shall it be?
        reachable: Set[State] = set()
        reachable.add(self.init)
        reached: Deque[State] = deque([self.init])
        reachable: Set[State] = {self.init}

        while len(reached) > 0:
            actual = reached.popleft()
            for character in self.characters:

reg_grammars.py

0 → 100644
+115 −0
Original line number Diff line number Diff line
from __future__ import annotations
from typing import Set, Dict, Union, Tuple, TypeVar
from reg_automata import Character, State, NFA


class Terminal:
    def __init__(self, name: str):
        self.name = name

    def __eq__(self, obj):
        if isinstance(obj, Terminal):
            return obj.name == self.name
        return False

    def __hash__(self):
        return hash(self.name)

class Eps:
    pass


class Nonterminal:
    def __init__(self, name: str):
        self.name = name

    def __eq__(self, obj):
        if isinstance(obj, Nonterminal):
            return obj.name == self.name
        return False

    def __hash__(self):
        return hash(self.name)

class REG:
    Rules = Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]]
    type_var = TypeVar('type_var')

    # TODO should accept init -> epsilon
    def __init__(self, nonterminals: Set[Nonterminal],
                 terminals: Set[Terminal],
                 rules: Rules,
                 init: Nonterminal):
        self.nonterminals = nonterminals
        self.terminals = terminals
        self.rules = rules
        self.init = init
        assert self.check

    # TODO ask about concept
    def check(self) -> bool:
        # scheme of grammar - empty sets and dicts are implemention only variant
        if len(self.nonterminals) == 0:
            assert len(self.terminals) == 0 and len(self.rules) == 0 and self.init == None, "nonempty scheme of grammar"
            return True

        nonterminal_names = set(map(lambda x: x.name, self.nonterminals))
        terminal_names = set(map(lambda x: x.name, self.terminals))
        assert len(nonterminal_names.intersection(terminal_names)) == 0, "name conflict"

        for nonterminal in self.rules:
            assert nonterminal in self.nonterminals, "unknown nonterminal " + nonterminal.name
            for rule in self.rules[nonterminal]:
                if isinstance(rule, Terminal):
                    assert rule in self.terminals, "unknown terminal " + rule.name
                else:
                    assert rule[0] in self.terminals, "unknown terminal " + rule[0].name
                    assert rule[1] in self.nonterminals, "unknown nonterminal " + rule[1].name

        assert self.init in self.nonterminals, "init not in nonterminals"

        return True

    def reg_to_nfa(self) -> NFA:

        init = State(self.init.name)
        states: Set[State] = set()
        for nonterminal in self.nonterminals:
            state = State(nonterminal.name)
            states.add(state)

        characters: Set[Character] = set()
        for terminal in self.terminals:
            character = Character(terminal.name)
            characters.add(character)

        final = State("new_final")  # TODO assert that name is unique, as for hell in make_total
        states.add(final)
        transition: Dict[Tuple[State, Character], Set[State]] = dict()

        for nonterminal in self.rules:
            state = State(nonterminal.name)
            for rule in self.rules[nonterminal]:

                # rule A -> a becomes d(A, a) = final
                if isinstance(rule, Terminal):  # TODO and a is not \eps
                    character = Character(rule.name)
                    if (state, character) in transition:
                        transition[state, character].add(final)
                    else:
                        transition[state, character] = {final}

                # rule A -> aB becomes d(A, a) = B
                elif (state, rule[0]) in transition:
                    character = Character(rule[0].name)
                    move = State(rule[1].name)
                    transition[state, character].add(move)
                else:
                    character = Character(rule[0].name)
                    move = State(rule[1].name)
                    transition[state, character] = {move}

        # TODO if init -> \eps: nfa.final.add(nfa.init)
        # I need to know how to treat \eps
        nfa = NFA(states, characters, transition, init, {final})
        return nfa
 No newline at end of file