Skip to content

Commit 89b44f5

Browse files
Removed my edits from core.py and instead extended Machine to add appropiate functionality. Implemented skeleton for regex
1 parent d43017f commit 89b44f5

File tree

3 files changed

+97
-67
lines changed

3 files changed

+97
-67
lines changed

sudkampPython/finiteAutomaton.py

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,39 @@
1-
from sudkampPython.transitions import Machine
1+
from sudkampPython.transitions import Machine, State
2+
3+
class FiniteStateMachine(Machine):
4+
""" An adapted Machine class better suited for use in algorithms. """"
5+
6+
def __init__( *args, **kwargs ):
7+
super(FiniteStateMachine, self).__init__(**kwargs)
8+
9+
self.final_states = set()
10+
11+
@property
12+
def alphabet(self):
13+
return self.events.keys()
14+
15+
@property
16+
def final(self):
17+
""" Return the set of final (or 'accepting') states. """
18+
return self.final_states
19+
20+
def set_final(self, state):
21+
""" Set state as a final (or 'accepting') state. """
22+
if not isinstance(state, State): state = state.name
23+
if state not in self.states:
24+
raise MachineError("Can't set state as final. State not in machine.")
25+
self.final_states.add(state)
26+
27+
def read(self, symbol):
28+
"""
29+
For a transition q0 -[a]-> q1 the stock library can do
30+
machine.a()
31+
machine._process(s.a)
32+
machine.events['a']_trigger()
33+
none of which are particularly good for our purposes. With read() you can simply do
34+
machine.read('a')
35+
"""
36+
return self.events[symbol]._trigger()
237

338
"""
439
Algorithm 5.7.2

sudkampPython/regex.py

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import itertools
2+
13
"""
24
Algorithm 6.2.2
35
Construction of a Regular Expression from a Finite Automaton
@@ -20,5 +22,31 @@
2022
until the only nodes in G are q0 and qt
2123
2. determine the expression accepted by G
2224
"""
23-
def constructRegExFromFiniteAutomaton( finiteAutomaton ):
24-
raise NotImplementedError
25+
def constructRegExFromFiniteAutomaton( G ):
26+
q0 = G.initial()
27+
qt = list(G.final())[0] # should be only one final state in set
28+
while True:
29+
qi = [x for x in G.states.values() if x.name not in [q0.name, qt.name]][0]
30+
# delete the qi from G according to the following procedure
31+
for j,k in [(j,k) for j,k in list(itertools.product()) if qi.name not in [j.name, k.name])]:
32+
w_ji = [(a,t) for a in G.alphabet for t in nfa.events[a].transitions[j.name] if t.dest == qi.name]
33+
w_ik = [(a,t) for a in G.alphabet for t in nfa.events[a].transitions[qi.name] if t.dest == k.name]
34+
w_ii = [(a,t) for a in G.alphabet for t in nfa.events[a].transitions[qi.name] if t.dest == qi.name]
35+
if w_ji and w_ik and not w_ii:
36+
# add arc from j to k labeled w_ji-w_ik
37+
if w_ji and w_ik and w_ii:
38+
# add arc from j to k labeled w_ji(w_ii)*w_ik
39+
jkArcs = [(a,t) for a in G.alphabet for t in nfa.events[a].transitions[j.name] if t.dest == k.name]
40+
if jkArcs:
41+
newSymbol = '_OR_'.join([a for a,_ in jkArcs]) # union of all symbols
42+
# remove all arcs
43+
for event in G.events.values():
44+
for t in event.transitions.values():
45+
if (t.source == j.name and t.dest == k.name): del event.transitions[t.source]
46+
# replace with new arc
47+
G.add_transition( newSymbol, j.name, k.name)
48+
# remove qi and all arcs incident to it in G
49+
for event in G.events.values():
50+
for t in event.transitions.values():
51+
if qi.name in [t.source,t.dest]: del event.transitions[t.source]
52+
del G.states[qi.name]

sudkampPython/transitions/core.py

+31-64
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ def __init__(self, name, on_enter=None, on_exit=None,
3838
callable, or a list of strings.
3939
ignore_invalid_triggers (Boolean): Optional flag to indicate if
4040
unhandled/invalid triggers should raise an exception
41-
4241
"""
4342
self.name = name
4443
self.ignore_invalid_triggers = ignore_invalid_triggers
@@ -70,37 +69,37 @@ def add_callback(self, trigger, func):
7069

7170
class Condition(object):
7271

73-
def __init__(self, func, target=True):
74-
"""
75-
Args:
76-
func (string): Name of the condition-checking callable
77-
target (bool): Indicates the target state--i.e., when True,
78-
the condition-checking callback should return True to pass,
79-
and when False, the callback should return False to pass.
80-
Notes:
81-
This class should not be initialized or called from outside a
82-
Transition instance, and exists at module level (rather than
83-
nesting under the ransition class) only because of a bug in
84-
dill that prevents serialization under Python 2.7.
85-
"""
86-
self.func = func
87-
self.target = target
88-
89-
def check(self, event_data):
90-
""" Check whether the condition passes.
91-
Args:
92-
event_data (EventData): An EventData instance to pass to the
93-
condition (if event sending is enabled) or to extract arguments
94-
from (if event sending is disabled). Also contains the data
95-
model attached to the current machine which is used to invoke
96-
the condition.
97-
"""
98-
predicate = getattr(event_data.model, self.func)
99-
if event_data.machine.send_event:
100-
return predicate(event_data) == self.target
101-
else:
102-
return predicate(
103-
*event_data.args, **event_data.kwargs) == self.target
72+
def __init__(self, func, target=True):
73+
"""
74+
Args:
75+
func (string): Name of the condition-checking callable
76+
target (bool): Indicates the target state--i.e., when True,
77+
the condition-checking callback should return True to pass,
78+
and when False, the callback should return False to pass.
79+
Notes:
80+
This class should not be initialized or called from outside a
81+
Transition instance, and exists at module level (rather than
82+
nesting under the ransition class) only because of a bug in
83+
dill that prevents serialization under Python 2.7.
84+
"""
85+
self.func = func
86+
self.target = target
87+
88+
def check(self, event_data):
89+
""" Check whether the condition passes.
90+
Args:
91+
event_data (EventData): An EventData instance to pass to the
92+
condition (if event sending is enabled) or to extract arguments
93+
from (if event sending is disabled). Also contains the data
94+
model attached to the current machine which is used to invoke
95+
the condition.
96+
"""
97+
predicate = getattr(event_data.model, self.func)
98+
if event_data.machine.send_event:
99+
return predicate(event_data) == self.target
100+
else:
101+
return predicate(
102+
*event_data.args, **event_data.kwargs) == self.target
104103

105104

106105
class Transition(object):
@@ -323,7 +322,6 @@ def __init__(self, model=None, states=None, initial=None, transitions=None,
323322
executed in a state callback function will be queued and executed later.
324323
Due to the nature of the queued processing, all transitions will
325324
_always_ return True since conditional checks cannot be conducted at queueing time.
326-
327325
**kwargs additional arguments passed to next class in MRO. This can be ignored in most cases.
328326
"""
329327

@@ -333,9 +331,7 @@ def __init__(self, model=None, states=None, initial=None, transitions=None,
333331
raise MachineError('Passing arguments {0} caused an inheritance error: {1}'.format(kwargs.keys(), e))
334332

335333
self.model = self if model is None else model
336-
self.alphabet = self.events.keys() # a simple renaming
337334
self.states = OrderedDict()
338-
self.final_states = set()
339335
self.events = {}
340336
self.current_state = None
341337
self.send_event = send_event
@@ -381,11 +377,6 @@ def initial(self):
381377
""" Return the initial state. """
382378
return self._initial
383379

384-
@property
385-
def final(self):
386-
""" Return the set of final (or 'accepting') states """
387-
return self.final_states
388-
389380
@property
390381
def has_queue(self):
391382
""" Return boolean indicating if machine has queue or not """
@@ -395,10 +386,6 @@ def is_state(self, state):
395386
""" Check whether the current state matches the named state. """
396387
return self.current_state.name == state
397388

398-
def is_final(self, state):
399-
""" Check whether the named state is a final state. """
400-
return state in self.final_states
401-
402389
def get_state(self, state):
403390
""" Return the State instance with the passed name. """
404391
if state not in self.states:
@@ -412,12 +399,6 @@ def set_state(self, state):
412399
self.current_state = state
413400
self.model.state = self.current_state.name
414401

415-
def set_final(self, state):
416-
""" Set state as a final ('accepting') state. """
417-
if state not in self.states:
418-
raise MachineError("Can't set state as final. State not in machine.")
419-
self.final_states.add(state)
420-
421402
def add_state(self, *args, **kwargs):
422403
""" Alias for add_states. """
423404
self.add_states(*args, **kwargs)
@@ -563,20 +544,6 @@ def _callback(self, func, event_data):
563544
else:
564545
func(*event_data.args, **event_data.kwargs)
565546

566-
def read(self, symbol):
567-
"""
568-
This has been added to the stock "transitions" library to be a more appropiate
569-
transition handling method for algorithm implementation.
570-
571-
For a transition q0 -[a]-> q1 the stock library can do
572-
machine.a()
573-
machine._process(s.a)
574-
machine.events['a']_trigger()
575-
none of which are particularly good for our purposes. With read() you can simply do
576-
machine.read('a')
577-
"""
578-
return self.events[symbol]._trigger()
579-
580547
def _process(self, trigger):
581548

582549
# default processing

0 commit comments

Comments
 (0)