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

split regular grammars and finite automata

parent 9932bacd
Pipeline #54103 passed with stage
in 22 seconds
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
......
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")
......
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:
......
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
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