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, ",")