1 #=======================================================================
3 # Python Lexical Analyser
5 # Converting NFA to DFA
7 #=======================================================================
10 from Machines
import LOWEST_PRIORITY
11 from Transitions
import TransitionMap
13 def nfa_to_dfa(old_machine
, debug
= None):
15 Given a nondeterministic Machine, return a new equivalent
16 Machine which is deterministic.
18 # We build a new machine whose states correspond to sets of states
19 # in the old machine. Initially we add a new state corresponding to
20 # the epsilon-closure of each initial old state. Then we give transitions
21 # to each new state which are the union of all transitions out of any
22 # of the corresponding old states. The new state reached on a given
23 # character is the one corresponding to the set of states reachable
24 # on that character from any of the old states. As new combinations of
25 # old states are created, new states are added as needed until closure
27 new_machine
= Machines
.FastMachine()
28 state_map
= StateMap(new_machine
)
29 # Seed the process using the initial states of the old machine.
30 # Make the corresponding new states into initial states of the new
31 # machine with the same names.
32 for (key
, old_state
) in old_machine
.initial_states
.iteritems():
33 new_state
= state_map
.old_to_new(epsilon_closure(old_state
))
34 new_machine
.make_initial_state(key
, new_state
)
35 # Tricky bit here: we add things to the end of this list while we're
36 # iterating over it. The iteration stops when closure is achieved.
37 for new_state
in new_machine
.states
:
38 transitions
= TransitionMap()
39 for old_state
in state_map
.new_to_old(new_state
):
40 for event
, old_target_states
in old_state
.transitions
.iteritems():
41 if event
and old_target_states
:
42 transitions
.add_set(event
, set_epsilon_closure(old_target_states
))
43 for event
, old_states
in transitions
.iteritems():
44 new_machine
.add_transitions(new_state
, event
, state_map
.old_to_new(old_states
))
46 debug
.write("\n===== State Mapping =====\n")
50 def set_epsilon_closure(state_set
):
52 Given a set of states, return the union of the epsilon
53 closures of its member states.
56 for state1
in state_set
:
57 for state2
in epsilon_closure(state1
):
61 def epsilon_closure(state
):
63 Return the set of states reachable from the given state
67 result
= state
.epsilon_closure
70 state
.epsilon_closure
= result
71 add_to_epsilon_closure(result
, state
)
74 def add_to_epsilon_closure(state_set
, state
):
76 Recursively add to |state_set| states reachable from the given state
79 if not state_set
.get(state
, 0):
81 state_set_2
= state
.transitions
.get_epsilon()
83 for state2
in state_set_2
:
84 add_to_epsilon_closure(state_set
, state2
)
86 class StateMap(object):
88 Helper class used by nfa_to_dfa() to map back and forth between
89 sets of states from the old machine and states of the new machine.
91 new_machine
= None # Machine
92 old_to_new_dict
= None # {(old_state,...) : new_state}
93 new_to_old_dict
= None # {id(new_state) : old_state_set}
95 def __init__(self
, new_machine
):
96 self
.new_machine
= new_machine
97 self
.old_to_new_dict
= {}
98 self
.new_to_old_dict
= {}
100 def old_to_new(self
, old_state_set
):
102 Return the state of the new machine corresponding to the
103 set of old machine states represented by |state_set|. A new
104 state will be created if necessary. If any of the old states
105 are accepting states, the new state will be an accepting state
106 with the highest priority action from the old states.
108 key
= self
.make_key(old_state_set
)
109 new_state
= self
.old_to_new_dict
.get(key
, None)
111 action
= self
.highest_priority_action(old_state_set
)
112 new_state
= self
.new_machine
.new_state(action
)
113 self
.old_to_new_dict
[key
] = new_state
114 self
.new_to_old_dict
[id(new_state
)] = old_state_set
115 #for old_state in old_state_set.keys():
116 #new_state.merge_actions(old_state)
119 def highest_priority_action(self
, state_set
):
121 best_priority
= LOWEST_PRIORITY
122 for state
in state_set
:
123 priority
= state
.action_priority
124 if priority
> best_priority
:
125 best_action
= state
.action
126 best_priority
= priority
129 # def old_to_new_set(self, old_state_set):
131 # Return the new state corresponding to a set of old states as
134 # return {self.old_to_new(old_state_set):1}
136 def new_to_old(self
, new_state
):
137 """Given a new state, return a set of corresponding old states."""
138 return self
.new_to_old_dict
[id(new_state
)]
140 def make_key(self
, state_set
):
142 Convert a set of states into a uniquified
143 sorted tuple suitable for use as a dictionary key.
145 lst
= list(state_set
)
149 def dump(self
, file):
150 from Transitions
import state_set_str
151 for new_state
in self
.new_machine
.states
:
152 old_state_set
= self
.new_to_old_dict
[id(new_state
)]
153 file.write(" State %s <-- %s\n" % (
154 new_state
['number'], state_set_str(old_state_set
)))