2 # Plex - Transition Maps
4 # This version represents state sets direcly as dicts
10 from sys import maxint
11 from types import TupleType
15 A TransitionMap maps an input event to a set of states.
16 An input event is one of: a range of character codes,
17 the empty string (representing an epsilon move), or one
18 of the special symbols BOL, EOL, EOF.
20 For characters, this implementation compactly represents
21 the map by means of a list:
23 [code_0, states_0, code_1, states_1, code_2, states_2,
24 ..., code_n-1, states_n-1, code_n]
26 where |code_i| is a character code, and |states_i| is a
27 set of states corresponding to characters with codes |c|
28 in the range |code_i| <= |c| <= |code_i+1|.
30 The following invariants hold:
34 code_i < code_i+1 for i in 0..n-1
35 states_0 == states_n-1
37 Mappings for the special events '', BOL, EOL, EOF are
38 kept separately in a dictionary.
41 map = None # The list of codes and states
42 special = None # Mapping for special events
44 def __init__(self, map = None, special = None):
46 map = [-maxint, {}, maxint]
50 self.special = special
53 def add(self, event, new_state,
54 TupleType = TupleType):
56 Add transition to |new_state| on |event|.
58 if type(event) == TupleType:
64 map[i + 1][new_state] = 1
67 self.get_special(event)[new_state] = 1
69 def add_set(self, event, new_set,
70 TupleType = TupleType):
72 Add transitions to the states in |new_set| on |event|.
74 if type(event) == TupleType:
80 map[i + 1].update(new_set)
83 self.get_special(event).update(new_set)
88 Return the mapping for epsilon, or None.
90 return self.special.get('', none)
95 Return the mapping as a list of ((code1, code2), state_set) and
96 (special_event, state_set) pairs.
108 result.append(((code0, code1), set))
111 for event, set in self.special.items():
113 result.append((event, set))
116 # ------------------- Private methods --------------------
118 def split(self, code,
119 len = len, maxint = maxint):
121 Search the list for the position of the split point for |code|,
122 inserting a new split point if necessary. Returns index |i| such
123 that |code| == |map[i]|.
125 # We use a funky variation on binary search.
128 # Special case: code == map[-1]
133 # loop invariant: map[lo] <= code < map[hi] and hi - lo >= 2
135 # Find midpoint truncated to even index
136 mid = ((lo + hi) / 2) & ~1
141 # map[lo] <= code < map[hi] and hi - lo == 2
145 map[hi:hi] = [code, map[hi - 1].copy()]
149 def get_special(self, event):
151 Get state set for special event, adding a new entry if necessary.
153 special = self.special
154 set = special.get(event, None)
160 # --------------------- Conversion methods -----------------------
175 map_strs.append(code_str)
178 map_strs.append(state_set_str(map[i]))
181 for event, set in self.special.items():
182 special_strs[event] = state_set_str(set)
184 string.join(map_strs, ","),
188 # --------------------- Debugging methods -----------------------
191 """Check data structure integrity."""
192 if not self.map[-3] < self.map[-1]:
196 def dump(self, file):
201 self.dump_range(map[i], map[i + 2], map[i + 1], file)
203 for event, set in self.special.items():
207 self.dump_trans(event, set, file)
209 def dump_range(self, code0, code1, set, file):
215 k = "< %s" % self.dump_char(code1)
216 elif code1 == maxint:
217 k = "> %s" % self.dump_char(code0 - 1)
218 elif code0 == code1 - 1:
219 k = self.dump_char(code0)
221 k = "%s..%s" % (self.dump_char(code0),
222 self.dump_char(code1 - 1))
223 self.dump_trans(k, set, file)
225 def dump_char(self, code):
227 return repr(chr(code))
229 return "chr(%d)" % code
231 def dump_trans(self, key, set, file):
232 file.write(" %s --> %s\n" % (key, self.dump_set(set)))
234 def dump_set(self, set):
235 return state_set_str(set)
238 # State set manipulation functions
241 #def merge_state_sets(set1, set2):
242 # for state in set2.keys():
245 def state_set_str(set):
246 state_list = set.keys()
248 for state in state_list:
249 str_list.append("S%d" % state.number)
250 return "[%s]" % string.join(str_list, ",")