Current design of the finite_automaton module contains multiple cyclic imports, and that is due to a bigger problem: confusing architecture that makes the module hard to maintain. I think inheritance is the right way to design finite automata, but we need to refactor some methods and interfaces to restore a proper hierarchy. For example, to_deterministic abstract method is one of the causes of cyclic imports:
|
|
|
def to_deterministic(self): |
|
""" Turns the automaton into a deterministic one""" |
|
raise NotImplementedError |
|
|
And similar situation with this method:
|
def remove_epsilon_transitions(self) -> "NondeterministicFiniteAutomaton": |
|
""" Removes the epsilon transitions from the automaton |
|
|
|
Returns |
|
---------- |
|
dfa : :class:`~pyformlang.finite_automaton.\ |
|
NondeterministicFiniteAutomaton` |
|
A non-deterministic finite automaton equivalent to the current \ |
|
nfa, with no epsilon transition |
|
""" |
|
from pyformlang.finite_automaton import NondeterministicFiniteAutomaton |
|
nfa = NondeterministicFiniteAutomaton() |
|
for state in self._start_state: |
|
nfa.add_start_state(state) |
|
for state in self._final_states: |
|
nfa.add_final_state(state) |
|
start_eclose = self.eclose_iterable(self._start_state) |
|
for state in start_eclose: |
|
nfa.add_start_state(state) |
|
for state in self._states: |
|
eclose = self.eclose(state) |
|
for e_state in eclose: |
|
if e_state in self._final_states: |
|
nfa.add_final_state(state) |
|
for symb in self._input_symbols: |
|
for next_state in self._transition_function(e_state, symb): |
|
nfa.add_transition(state, symb, next_state) |
|
return nfa |
I think the right way to handle this, is to make sure that inherited classes don't know anything about their descendants. To do this we can get rid of to_deterministic abstraction, and refactor it's implementations as class methods of the DFA class. And also do the same thing with remove_epsilon_transitions. It might look like this:
class DeterministicFiniteAutomaton(NondeterministicFiniteAutomaton):
@classmethod
def from_epsilon_nfa(cls, enfa: EpsilonNFA) \
-> DeterministicFiniteAutomaton:
# implementation for enfa
@classmethod
def from_nfa(cls, nfa: NondeterministicFiniteAutomaton): # ...
And similarly with remove_epsilon_transitions:
class NondeterministicFiniteAutomaton(EpsilonNFA):
@classmethod
def remove_epsilon_transitions(cls, enfa: EpsilonNFA) \
-> NondeterministicFiniteAutomaton:
# implementation
This may require some refactoring to make sure we don't use any protected members, but it seems completely doable.
As an alternative, maybe we could try to somehow get rid of inheritance, or just leave a couple of shortcuts like in the current state of pyformlang, but neither of these seems like a good solution.
Also there there are cyclic imports between Regex and EpsilonNFA so there are more things we need to rework outside of finite_automaton module.
Current design of the
finite_automatonmodule contains multiple cyclic imports, and that is due to a bigger problem: confusing architecture that makes the module hard to maintain. I think inheritance is the right way to design finite automata, but we need to refactor some methods and interfaces to restore a proper hierarchy. For example,to_deterministicabstract method is one of the causes of cyclic imports:pyformlang/pyformlang/finite_automaton/finite_automaton.py
Lines 596 to 600 in 4f36e28
And similar situation with this method:
pyformlang/pyformlang/finite_automaton/epsilon_nfa.py
Lines 254 to 281 in 4f36e28
I think the right way to handle this, is to make sure that inherited classes don't know anything about their descendants. To do this we can get rid of
to_deterministicabstraction, and refactor it's implementations as class methods of the DFA class. And also do the same thing withremove_epsilon_transitions. It might look like this:And similarly with
remove_epsilon_transitions:This may require some refactoring to make sure we don't use any protected members, but it seems completely doable.
As an alternative, maybe we could try to somehow get rid of inheritance, or just leave a couple of shortcuts like in the current state of pyformlang, but neither of these seems like a good solution.
Also there there are cyclic imports between
RegexandEpsilonNFAso there are more things we need to rework outside offinite_automatonmodule.