2 # Plex - Transition Maps
4 # This version represents state sets direcly as dicts
8 from sys
import maxint
as maxint
10 class TransitionMap(object):
12 A TransitionMap maps an input event to a set of states.
13 An input event is one of: a range of character codes,
14 the empty string (representing an epsilon move), or one
15 of the special symbols BOL, EOL, EOF.
17 For characters, this implementation compactly represents
18 the map by means of a list:
20 [code_0, states_0, code_1, states_1, code_2, states_2,
21 ..., code_n-1, states_n-1, code_n]
23 where |code_i| is a character code, and |states_i| is a
24 set of states corresponding to characters with codes |c|
25 in the range |code_i| <= |c| <= |code_i+1|.
27 The following invariants hold:
31 code_i < code_i+1 for i in 0..n-1
32 states_0 == states_n-1
34 Mappings for the special events '', BOL, EOL, EOF are
35 kept separately in a dictionary.
38 map = None # The list of codes and states
39 special
= None # Mapping for special events
41 def __init__(self
, map = None, special
= None):
43 map = [-maxint
, {}, maxint
]
47 self
.special
= special
50 def add(self
, event
, new_state
,
53 Add transition to |new_state| on |event|.
55 if type(event
) is TupleType
:
61 map[i
+ 1][new_state
] = 1
64 self
.get_special(event
)[new_state
] = 1
66 def add_set(self
, event
, new_set
,
69 Add transitions to the states in |new_set| on |event|.
71 if type(event
) is TupleType
:
77 map[i
+ 1].update(new_set
)
80 self
.get_special(event
).update(new_set
)
85 Return the mapping for epsilon, or None.
87 return self
.special
.get('', none
)
92 Return the mapping as an iterable of ((code1, code2), state_set) and
93 (special_event, state_set) pairs.
105 result
.append(((code0
, code1
), set))
108 for event
, set in self
.special
.iteritems():
110 result
.append((event
, set))
114 # ------------------- Private methods --------------------
116 def split(self
, code
,
117 len = len, maxint
= maxint
):
119 Search the list for the position of the split point for |code|,
120 inserting a new split point if necessary. Returns index |i| such
121 that |code| == |map[i]|.
123 # We use a funky variation on binary search.
126 # Special case: code == map[-1]
131 # loop invariant: map[lo] <= code < map[hi] and hi - lo >= 2
133 # Find midpoint truncated to even index
134 mid
= ((lo
+ hi
) // 2) & ~
1
139 # map[lo] <= code < map[hi] and hi - lo == 2
143 map[hi
:hi
] = [code
, map[hi
- 1].copy()]
147 def get_special(self
, event
):
149 Get state set for special event, adding a new entry if necessary.
151 special
= self
.special
152 set = special
.get(event
, None)
158 # --------------------- Conversion methods -----------------------
173 map_strs
.append(code_str
)
176 map_strs
.append(state_set_str(map[i
]))
179 for event
, set in self
.special
.iteritems():
180 special_strs
[event
] = state_set_str(set)
186 # --------------------- Debugging methods -----------------------
189 """Check data structure integrity."""
190 if not self
.map[-3] < self
.map[-1]:
194 def dump(self
, file):
199 self
.dump_range(map[i
], map[i
+ 2], map[i
+ 1], file)
201 for event
, set in self
.special
.iteritems():
205 self
.dump_trans(event
, set, file)
207 def dump_range(self
, code0
, code1
, set, file):
213 k
= "< %s" % self
.dump_char(code1
)
214 elif code1
== maxint
:
215 k
= "> %s" % self
.dump_char(code0
- 1)
216 elif code0
== code1
- 1:
217 k
= self
.dump_char(code0
)
219 k
= "%s..%s" % (self
.dump_char(code0
),
220 self
.dump_char(code1
- 1))
221 self
.dump_trans(k
, set, file)
223 def dump_char(self
, code
):
225 return repr(chr(code
))
227 return "chr(%d)" % code
229 def dump_trans(self
, key
, set, file):
230 file.write(" %s --> %s\n" % (key
, self
.dump_set(set)))
232 def dump_set(self
, set):
233 return state_set_str(set)
236 # State set manipulation functions
239 #def merge_state_sets(set1, set2):
240 # for state in set2.keys():
243 def state_set_str(set):
244 return "[%s]" % ','.join(["S%d" % state
.number
for state
in set])