1 #=======================================================================
3 # Python Lexical Analyser
5 # Classes for building NFAs and DFAs
7 #=======================================================================
11 from Transitions import TransitionMap
13 LOWEST_PRIORITY = -sys.maxint
15 class Machine(object):
16 """A collection of Nodes representing an NFA or DFA."""
17 states = None # [Node]
19 initial_states = None # {(name, bol): Node}
23 self.initial_states = {}
26 #print "Destroying", self ###
27 for state in self.states:
31 """Add a new state to the machine and return it."""
33 n = self.next_state_number
34 self.next_state_number = n + 1
39 def new_initial_state(self, name):
40 state = self.new_state()
41 self.make_initial_state(name, state)
44 def make_initial_state(self, name, state):
45 self.initial_states[name] = state
47 def get_initial_state(self, name):
48 return self.initial_states[name]
51 file.write("Plex.Machine:\n")
52 if self.initial_states is not None:
53 file.write(" Initial states:\n")
54 for (name, state) in self.initial_states.iteritems():
55 file.write(" '%s': %d\n" % (name, state.number))
60 """A state of an NFA or DFA."""
61 transitions = None # TransitionMap
62 action = None # Action
63 action_priority = None # integer
64 number = 0 # for debug output
65 epsilon_closure = None # used by nfa_to_dfa()
68 # Preinitialise the list of empty transitions, because
69 # the nfa-to-dfa algorithm needs it
70 #self.transitions = {'':[]}
71 self.transitions = TransitionMap()
72 self.action_priority = LOWEST_PRIORITY
75 #print "Destroying", self ###
76 self.transitions = None
78 self.epsilon_closure = None
80 def add_transition(self, event, new_state):
81 self.transitions.add(event, new_state)
83 def link_to(self, state):
84 """Add an epsilon-move from this state to another state."""
85 self.add_transition('', state)
87 def set_action(self, action, priority):
88 """Make this an accepting state with the given action. If
89 there is already an action, choose the action with highest
91 if priority > self.action_priority:
93 self.action_priority = priority
98 def get_action_priority(self):
99 return self.action_priority
101 def is_accepting(self):
102 return self.action is not None
105 return "State %d" % self.number
107 def dump(self, file):
109 file.write(" State %d:\n" % self.number)
111 # self.dump_transitions(file)
112 self.transitions.dump(file)
115 priority = self.action_priority
116 if action is not None:
117 file.write(" %s [priority %d]\n" % (action, priority))
119 def __lt__(self, other):
120 return self.number < other.number
122 class FastMachine(object):
124 FastMachine is a deterministic machine represented in a way that
125 allows fast scanning.
127 initial_states = None # {state_name:state}
128 states = None # [state]
129 # where state = {event:state, 'else':state, 'action':Action}
130 next_number = 1 # for debugging
132 new_state_template = {
133 '':None, 'bol':None, 'eol':None, 'eof':None, 'else':None
136 def __init__(self, old_machine = None):
137 self.initial_states = initial_states = {}
140 self.old_to_new = old_to_new = {}
141 for old_state in old_machine.states:
142 new_state = self.new_state()
143 old_to_new[old_state] = new_state
144 for name, old_state in old_machine.initial_states.iteritems():
145 initial_states[name] = old_to_new[old_state]
146 for old_state in old_machine.states:
147 new_state = old_to_new[old_state]
148 for event, old_state_set in old_state.transitions.iteritems():
150 new_state[event] = old_to_new[old_state_set.keys()[0]]
152 new_state[event] = None
153 new_state['action'] = old_state.action
156 for state in self.states:
159 def new_state(self, action = None):
160 number = self.next_number
161 self.next_number = number + 1
162 result = self.new_state_template.copy()
163 result['number'] = number
164 result['action'] = action
165 self.states.append(result)
168 def make_initial_state(self, name, state):
169 self.initial_states[name] = state
171 def add_transitions(self, state, event, new_state, maxint=sys.maxint):
172 if type(event) is tuple:
175 state['else'] = new_state
176 elif code1 != maxint:
178 state[chr(code0)] = new_state
181 state[event] = new_state
183 def get_initial_state(self, name):
184 return self.initial_states[name]
186 def dump(self, file):
187 file.write("Plex.FastMachine:\n")
188 file.write(" Initial states:\n")
189 for name, state in self.initial_states.iteritems():
190 file.write(" %s: %s\n" % (repr(name), state['number']))
191 for state in self.states:
192 self.dump_state(state, file)
194 def dump_state(self, state, file):
196 file.write(" State %d:\n" % state['number'])
198 self.dump_transitions(state, file)
200 action = state['action']
201 if action is not None:
202 file.write(" %s\n" % action)
204 def dump_transitions(self, state, file):
205 chars_leading_to_state = {}
206 special_to_state = {}
207 for (c, s) in state.iteritems():
209 chars = chars_leading_to_state.get(id(s), None)
212 chars_leading_to_state[id(s)] = chars
215 special_to_state[c] = s
217 for state in self.states:
218 char_list = chars_leading_to_state.get(id(state), None)
220 ranges = self.chars_to_ranges(char_list)
221 ranges_to_state[ranges] = state
222 ranges_list = ranges_to_state.keys()
224 for ranges in ranges_list:
225 key = self.ranges_to_string(ranges)
226 state = ranges_to_state[ranges]
227 file.write(" %s --> State %d\n" % (key, state['number']))
228 for key in ('bol', 'eol', 'eof', 'else'):
229 state = special_to_state.get(key, None)
231 file.write(" %s --> State %d\n" % (key, state['number']))
233 def chars_to_ranges(self, char_list):
239 c1 = ord(char_list[i])
242 while i < n and ord(char_list[i]) == c2 + 1:
245 result.append((chr(c1), chr(c2)))
248 def ranges_to_string(self, range_list):
249 return ','.join(map(self.range_to_string, range_list))
251 def range_to_string(self, range_tuple):
252 (c1, c2) = range_tuple
256 return "%s..%s" % (repr(c1), repr(c2))