Loading dfa.py +6 −5 Original line number Diff line number Diff line Loading @@ -359,7 +359,8 @@ class DFA: return reg def terminating_states(self, states, pred): # support function: decides from which of reachable states there is a way to final state def terminating_states(self, states: Set[State], pred: Dict[State, Set[State]]) -> Set[State]: unknown: Set[State] = states.difference(self.final) to_add: Set[State] = states.intersection(self.final) terminate: Set[State] = set() Loading @@ -384,7 +385,7 @@ class DFA: # else should not happen really @staticmethod def one_of(collection: Set[Any]) -> Any: def one_of(collection: Set[type_var]) -> type_var: return next(iter(collection)) def is_empty(self) -> IsEmptyResult: Loading Loading @@ -443,7 +444,7 @@ class DFA: #print(','.join(set(map(lambda x: x.name, on_cycle)))) if self.init in self.final: return IsEmptyResult('ε', True) if len(on_cycle.intersection(terminable)) > 0 else IsEmptyResult('ε') return IsEmptyResult('ε', len(on_cycle.intersection(terminable)) > 0) word: str = [] state: State = self.init Loading @@ -457,7 +458,7 @@ class DFA: if len(on_cycle.intersection(terminable)) > 0: return IsEmptyResult(''.join(word), True) return IsEmptyResult(''.join(word)) return IsEmptyResult(''.join(word), False) # Ha! def is_universal(self): Loading grammars.py +9 −43 Original line number Diff line number Diff line Loading @@ -2,53 +2,11 @@ from __future__ import annotations from typing import Set, Dict, Union, Tuple, TypeVar, List from common import Terminal, Nonterminal, Character, State, Eps from nfa import NFA from grammars_cfg import CFG # Typecheck is sufficient for grammar being a grammar of certain type # with exception of Eps only within the init rule and not use of init elsewhere. class CFG: Rules = Dict[Nonterminal, Set[Union[Eps, Tuple[Union[Terminal, 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 self.init = init def is_nonterminal_used(self, nonterminal) -> bool: for nonterm in self.rules: for rule in self.rules[nonterm]: if not isinstance(rule, Eps) and nonterminal in rule: return True return False def is_regular(self) -> bool: for nonterminal in self.rules: for rule in self.rules[nonterminal]: if isinstance(rule, Eps): if nonterminal == self.init and not self.is_nonterminal_used(nonterminal): continue else: print("Gramatika není regulární: výskyt epsilon u neterminálu, který se vyskytuje na pravé straně některého pravidla.") return False if isinstance(rule, tuple): # TODO nicer if (len(rule) == 1 and isinstance(rule[0], Terminal)) or \ (len(rule) == 2 and isinstance(rule[0], Terminal) and isinstance(rule[1], Nonterminal)): continue print("Gramatika není regulární: špatný tvar pravé strany některého z pravidel.") return False return True def cfg_to_reggrammar(self) -> RegGrammar: if self.is_regular(): return RegGrammar(self.nonterminals, self.terminals, self.rules, self.init) # TODO else? class RegGrammar: Rules = Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] Loading @@ -64,6 +22,14 @@ class RegGrammar: self.init = init assert self.check() @staticmethod def reggrammar_from_cfg(cfg: CFG) -> RegGrammar: try: if cfg.is_regular(): return RegGrammar(cfg.nonterminals, cfg.terminals, cfg.rules, cfg.init) except: # I don't know pass # unused formal requirements check, regular grammars specific (rule variants) def check(self) -> bool: assert len(self.nonterminals) > 0, "empty grammar" Loading grammars_cfg.py +26 −0 Original line number Diff line number Diff line Loading @@ -148,6 +148,32 @@ class CFG: del self.rules[src] self._check() def is_nonterminal_used(self, nonterminal) -> bool: for nonterm in self.rules: for rule in self.rules[nonterm]: if not isinstance(rule, Eps) and nonterminal in rule: return True return False def is_regular(self) -> bool: for nonterminal in self.rules: for rule in self.rules[nonterminal]: if isinstance(rule, Eps): if nonterminal == self.init and not self.is_nonterminal_used(nonterminal): continue else: print("Gramatika není regulární: výskyt epsilon u neterminálu, který se vyskytuje na pravé straně některého pravidla.") return False if isinstance(rule, tuple): # TODO nicer if (len(rule) == 1 and isinstance(rule[0], Terminal)) or \ (len(rule) == 2 and isinstance(rule[0], Terminal) and isinstance(rule[1], Nonterminal)): continue print("Gramatika není regulární: špatný tvar pravé strany některého z pravidel.") return False return True def productions(self) -> Iterable[Tuple[Nonterminal, CFG.Production]]: for src, prods in self.rules.items(): for prod in prods: Loading parser.py +5 −1 Original line number Diff line number Diff line Loading @@ -112,8 +112,12 @@ class Parser: def str_to_cfg(self, string: str) -> CFG: listener = self.common_parse(string, CFGLexer, CFGParser, CFGListener) return CFG(listener.nonterminals, listener.terminals, listener.rules, listener.init) def str_to_reggrammar(self): pass def str_to_dfa(self, string: str) -> DFA: listener = self.common_parse(string, DFALexer, DFAParser, DFAListener) Loading Loading
dfa.py +6 −5 Original line number Diff line number Diff line Loading @@ -359,7 +359,8 @@ class DFA: return reg def terminating_states(self, states, pred): # support function: decides from which of reachable states there is a way to final state def terminating_states(self, states: Set[State], pred: Dict[State, Set[State]]) -> Set[State]: unknown: Set[State] = states.difference(self.final) to_add: Set[State] = states.intersection(self.final) terminate: Set[State] = set() Loading @@ -384,7 +385,7 @@ class DFA: # else should not happen really @staticmethod def one_of(collection: Set[Any]) -> Any: def one_of(collection: Set[type_var]) -> type_var: return next(iter(collection)) def is_empty(self) -> IsEmptyResult: Loading Loading @@ -443,7 +444,7 @@ class DFA: #print(','.join(set(map(lambda x: x.name, on_cycle)))) if self.init in self.final: return IsEmptyResult('ε', True) if len(on_cycle.intersection(terminable)) > 0 else IsEmptyResult('ε') return IsEmptyResult('ε', len(on_cycle.intersection(terminable)) > 0) word: str = [] state: State = self.init Loading @@ -457,7 +458,7 @@ class DFA: if len(on_cycle.intersection(terminable)) > 0: return IsEmptyResult(''.join(word), True) return IsEmptyResult(''.join(word)) return IsEmptyResult(''.join(word), False) # Ha! def is_universal(self): Loading
grammars.py +9 −43 Original line number Diff line number Diff line Loading @@ -2,53 +2,11 @@ from __future__ import annotations from typing import Set, Dict, Union, Tuple, TypeVar, List from common import Terminal, Nonterminal, Character, State, Eps from nfa import NFA from grammars_cfg import CFG # Typecheck is sufficient for grammar being a grammar of certain type # with exception of Eps only within the init rule and not use of init elsewhere. class CFG: Rules = Dict[Nonterminal, Set[Union[Eps, Tuple[Union[Terminal, 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 self.init = init def is_nonterminal_used(self, nonterminal) -> bool: for nonterm in self.rules: for rule in self.rules[nonterm]: if not isinstance(rule, Eps) and nonterminal in rule: return True return False def is_regular(self) -> bool: for nonterminal in self.rules: for rule in self.rules[nonterminal]: if isinstance(rule, Eps): if nonterminal == self.init and not self.is_nonterminal_used(nonterminal): continue else: print("Gramatika není regulární: výskyt epsilon u neterminálu, který se vyskytuje na pravé straně některého pravidla.") return False if isinstance(rule, tuple): # TODO nicer if (len(rule) == 1 and isinstance(rule[0], Terminal)) or \ (len(rule) == 2 and isinstance(rule[0], Terminal) and isinstance(rule[1], Nonterminal)): continue print("Gramatika není regulární: špatný tvar pravé strany některého z pravidel.") return False return True def cfg_to_reggrammar(self) -> RegGrammar: if self.is_regular(): return RegGrammar(self.nonterminals, self.terminals, self.rules, self.init) # TODO else? class RegGrammar: Rules = Dict[Nonterminal, Set[Union[Terminal, Tuple[Terminal, Nonterminal]]]] Loading @@ -64,6 +22,14 @@ class RegGrammar: self.init = init assert self.check() @staticmethod def reggrammar_from_cfg(cfg: CFG) -> RegGrammar: try: if cfg.is_regular(): return RegGrammar(cfg.nonterminals, cfg.terminals, cfg.rules, cfg.init) except: # I don't know pass # unused formal requirements check, regular grammars specific (rule variants) def check(self) -> bool: assert len(self.nonterminals) > 0, "empty grammar" Loading
grammars_cfg.py +26 −0 Original line number Diff line number Diff line Loading @@ -148,6 +148,32 @@ class CFG: del self.rules[src] self._check() def is_nonterminal_used(self, nonterminal) -> bool: for nonterm in self.rules: for rule in self.rules[nonterm]: if not isinstance(rule, Eps) and nonterminal in rule: return True return False def is_regular(self) -> bool: for nonterminal in self.rules: for rule in self.rules[nonterminal]: if isinstance(rule, Eps): if nonterminal == self.init and not self.is_nonterminal_used(nonterminal): continue else: print("Gramatika není regulární: výskyt epsilon u neterminálu, který se vyskytuje na pravé straně některého pravidla.") return False if isinstance(rule, tuple): # TODO nicer if (len(rule) == 1 and isinstance(rule[0], Terminal)) or \ (len(rule) == 2 and isinstance(rule[0], Terminal) and isinstance(rule[1], Nonterminal)): continue print("Gramatika není regulární: špatný tvar pravé strany některého z pravidel.") return False return True def productions(self) -> Iterable[Tuple[Nonterminal, CFG.Production]]: for src, prods in self.rules.items(): for prod in prods: Loading
parser.py +5 −1 Original line number Diff line number Diff line Loading @@ -112,8 +112,12 @@ class Parser: def str_to_cfg(self, string: str) -> CFG: listener = self.common_parse(string, CFGLexer, CFGParser, CFGListener) return CFG(listener.nonterminals, listener.terminals, listener.rules, listener.init) def str_to_reggrammar(self): pass def str_to_dfa(self, string: str) -> DFA: listener = self.common_parse(string, DFALexer, DFAParser, DFAListener) Loading