Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: tools/lexer_generator/transition_keys.py

Issue 82983002: Experimental parser: KeyEncoding class added (Closed) Base URL: https://v8.googlecode.com/svn/branches/experimental/parser
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/lexer_generator/transition_key_test.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2013 the V8 project authors. All rights reserved. 1 # Copyright 2013 the V8 project authors. All rights reserved.
2 # Redistribution and use in source and binary forms, with or without 2 # Redistribution and use in source and binary forms, with or without
3 # modification, are permitted provided that the following conditions are 3 # modification, are permitted provided that the following conditions are
4 # met: 4 # met:
5 # 5 #
6 # * Redistributions of source code must retain the above copyright 6 # * Redistributions of source code must retain the above copyright
7 # notice, this list of conditions and the following disclaimer. 7 # notice, this list of conditions and the following disclaimer.
8 # * Redistributions in binary form must reproduce the above 8 # * Redistributions in binary form must reproduce the above
9 # copyright notice, this list of conditions and the following 9 # copyright notice, this list of conditions and the following
10 # disclaimer in the documentation and/or other materials provided 10 # disclaimer in the documentation and/or other materials provided
11 # with the distribution. 11 # with the distribution.
12 # * Neither the name of Google Inc. nor the names of its 12 # * Neither the name of Google Inc. nor the names of its
13 # contributors may be used to endorse or promote products derived 13 # contributors may be used to endorse or promote products derived
14 # from this software without specific prior written permission. 14 # from this software without specific prior written permission.
15 # 15 #
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 27
28 from types import TupleType
28 from string import printable 29 from string import printable
29 30
30 class TransitionKey: 31 class KeyEncoding(object):
32
33 __encodings = {}
34
35 @staticmethod
36 def get(name):
37 if not KeyEncoding.__encodings:
38 Latin1Encoding()
39 Utf16Encoding()
40 return KeyEncoding.__encodings[name]
41
42 def __init__(self, name, primary_range, class_names):
43 assert not name in KeyEncoding.__encodings
44 KeyEncoding.__encodings[name] = self
45 assert primary_range[0] <= primary_range[1]
46 assert primary_range[0] >= 0
47 self.__name = name
48 self.__primary_range = primary_range
49 self.__lower_bound = primary_range[0]
50 self.__upper_bound = primary_range[1] + len(class_names)
51 f = lambda i : (i + primary_range[1] + 1, i + primary_range[1] + 1)
52 self.__class_ranges = {name : f(i) for i, name in enumerate(class_names)}
53 self.__predefined_ranges = {}
54
55 def name(self):
56 return self.__name
57
58 def add_predefined_range(self, name, ranges):
59 # TODO verify disjointness
60 self.__predefined_ranges[name] = ranges
61
62 def lower_bound(self):
63 return self.__lower_bound
64
65 def upper_bound(self):
66 return self.__upper_bound
67
68 def primary_range(self):
69 return self.__primary_range
70
71 def class_range(self, name):
72 ranges = self.__class_ranges
73 return None if not name in ranges else ranges[name]
74
75 def class_range_iter(self):
76 return self.__class_ranges.iteritems()
77
78 def class_value_iter(self):
79 return self.__class_ranges.itervalues()
80
81 def predefined_range_iter(self, name):
82 ranges = self.__predefined_ranges
83 return None if not name in ranges else iter(ranges[name])
84
85 def is_primary_range(self, r):
86 assert self.lower_bound() <= r[0] and r[1] <= self.upper_bound()
87 primary_range = self.__primary_range
88 if (primary_range[0] <= r[0] and r[1] <= primary_range[1]):
89 return True
90 assert r[0] == r[1]
91 return False
92
93 def in_primary_range(self, c):
94 return self.is_primary_range((c, c))
95
96 def is_class_range(self, r):
97 return not self.is_primary_range(r)
98
99 class TransitionKey(object):
31 '''Represents a transition from a state in DFA or NFA to another state. 100 '''Represents a transition from a state in DFA or NFA to another state.
32 101
33 A transition key has a list of character ranges and a list of class ranges 102 A transition key has a list of character ranges and a list of class ranges
34 (e.g., "whitespace"), defining for which characters the transition 103 (e.g., "whitespace"), defining for which characters the transition
35 happens. When we generate code based on the transition key, the character 104 happens. When we generate code based on the transition key, the character
36 ranges generate simple checks and the class ranges generate more complicated 105 ranges generate simple checks and the class ranges generate more complicated
37 conditions, e.g., function calls.''' 106 conditions, e.g., function calls.'''
38 107
39 __class_bounds = { 108 __cached_keys = {
40 'latin_1' : (1, 255), 109 'no_encoding' : {},
41 # These are not real ranges; they just need to be separate from any real 110 'latin1' : {},
42 # ranges. 111 'utf8' : {},
43 'non_latin_1_whitespace' : (256, 256), 112 'utf16' : {},
44 'non_latin_1_letter' : (257, 257),
45 'non_latin_1_identifier_part_not_letter' : (258, 258),
46 'non_latin_1_line_terminator' : (259, 259),
47 'eos' : (260, 260),
48 'zero' : (261, 261),
49 'byte_order_mark' : (262, 262),
50 'non_latin_1_everything_else' : (263, 263),
51 } 113 }
52 __lower_bound = min(__class_bounds.values(), key=lambda item: item[0])[0]
53 __upper_bound = max(__class_bounds.values(), key=lambda item: item[1])[1]
54
55 __cached_keys = {}
56 114
57 __unique_key_counter = -1 115 __unique_key_counter = -1
58 116
59 __predefined_ranges = { 117 @staticmethod
60 'whitespace' : [ 118 def __is_unique_range(r):
61 (9, 9), (11, 12), (32, 32), (133, 133), (160, 160), 119 return (r[0] == r[1] and
62 __class_bounds['non_latin_1_whitespace']], 120 r[0] < 0 and
63 'letter' : [ 121 r[1] > TransitionKey.__unique_key_counter)
64 (65, 90), (97, 122), (170, 170), (181, 181),
65 (186, 186), (192, 214), (216, 246), (248, 255),
66 __class_bounds['non_latin_1_letter']],
67 'line_terminator' : [
68 (10, 10), (13, 13),
69 __class_bounds['non_latin_1_line_terminator']],
70 'identifier_part_not_letter' : [
71 (48, 57), (95, 95),
72 __class_bounds['non_latin_1_identifier_part_not_letter']],
73 }
74 122
75 @staticmethod 123 @staticmethod
76 def __in_latin_1(char): 124 def __verify_ranges(encoding, ranges, check_merged):
77 bound = TransitionKey.__class_bounds['latin_1']
78 return (bound[0] <= char and char <= bound[1])
79
80 @staticmethod
81 def __is_class_range(r):
82 return r[0] == r[1] and not TransitionKey.__in_latin_1(r[0])
83
84 @staticmethod
85 def __is_unique_range(r):
86 return r[0] == r[1] and r[0] < TransitionKey.__lower_bound
87
88 @staticmethod
89 def __verify_ranges(ranges, check_merged):
90 assert ranges 125 assert ranges
91 if len(ranges) == 1 and TransitionKey.__is_class_range(ranges[0]): 126 if len(ranges) == 1 and TransitionKey.__is_unique_range(ranges[0]):
92 return 127 return
93 last = None 128 last = None
94 for r in ranges: 129 for r in ranges:
95 assert TransitionKey.__lower_bound <= r[0] 130 assert not TransitionKey.__is_unique_range(r)
96 assert r[1] <= TransitionKey.__upper_bound 131 r_is_class = encoding.is_class_range(r)
97 assert r[0] <= r[1]
98 r_is_class = TransitionKey.__is_class_range(r)
99 # Assert that the ranges are in order. 132 # Assert that the ranges are in order.
100 if last != None and check_merged: 133 if last != None and check_merged:
101 assert last[1] + 1 < r[0] or r_is_class 134 assert last[1] + 1 < r[0] or r_is_class
102 if not TransitionKey.__in_latin_1(r[0]):
103 assert r_is_class
104 if not TransitionKey.__in_latin_1(r[1]):
105 assert r_is_class
106 last = r 135 last = r
107 136
108 def __is_unique(self): 137 def __is_unique(self):
109 return len(self.__ranges) == 1 and self.__is_unique_range(self.__ranges[0]) 138 return len(self.__ranges) == 1 and self.__is_unique_range(self.__ranges[0])
110 139
111 @staticmethod 140 @staticmethod
112 def __cached_key(name, bounds_getter): 141 def __cached_key(encoding, name, bounds_getter):
113 if not name in TransitionKey.__cached_keys: 142 encoding_name = encoding.name() if encoding else 'no_encoding'
143 cache = TransitionKey.__cached_keys[encoding_name]
144 if not name in cache:
114 bounds = bounds_getter(name) 145 bounds = bounds_getter(name)
115 TransitionKey.__cached_keys[name] = TransitionKey(bounds, name) 146 cache[name] = TransitionKey(encoding, bounds, name)
116 return TransitionKey.__cached_keys[name] 147 return cache[name]
117 148
118 @staticmethod 149 @staticmethod
119 def epsilon(): 150 def epsilon():
120 '''Returns a TransitionKey for the epsilon (empty) transition.''' 151 '''Returns a TransitionKey for the epsilon (empty) transition.'''
121 return TransitionKey.__cached_key('epsilon', lambda name : []) 152 return TransitionKey.__cached_key(None, 'epsilon', lambda name : [])
122 153
123 @staticmethod 154 @staticmethod
124 def any(): 155 def any(encoding):
125 '''Returns a TransitionKey which matches any character.''' 156 '''Returns a TransitionKey which matches any character.'''
126 return TransitionKey.__cached_key( 157 return TransitionKey.__cached_key(
158 encoding,
127 'any', 159 'any',
128 lambda name : sorted(TransitionKey.__class_bounds.values())) 160 lambda name : sorted(
161 list(encoding.class_value_iter()) + [encoding.primary_range()]))
129 162
130 @staticmethod 163 @staticmethod
131 def single_char(char): 164 def single_char(encoding, char):
132 '''Returns a TransitionKey for a single-character transition.''' 165 '''Returns a TransitionKey for a single-character transition.'''
133 char = ord(char) 166 r = (ord(char), ord(char))
134 assert TransitionKey.__in_latin_1(char) 167 assert encoding.is_primary_range(r)
135 return TransitionKey([(char, char)]) 168 return TransitionKey(encoding, [r])
136 169
137 @staticmethod 170 @staticmethod
138 def unique(name): 171 def unique(name):
139 '''Returns a unique TransitionKey for the given name (and creates it if it 172 '''Returns a unique TransitionKey for the given name (and creates it if it
140 doesn't exist yet). The TransitionKey won't have any real character range, 173 doesn't exist yet). The TransitionKey won't have any real character range,
141 but a newly-created "mock" character range which is separate from all other 174 but a newly-created "mock" character range which is separate from all other
142 character ranges.''' 175 character ranges.'''
143 def get_bounds(name): 176 def get_bounds(name):
144 bound = TransitionKey.__unique_key_counter 177 bound = TransitionKey.__unique_key_counter
145 TransitionKey.__unique_key_counter -= 1 178 TransitionKey.__unique_key_counter -= 1
146 return [(bound, bound)] 179 return [(bound, bound)]
147 name = '__' + name 180 name = '__' + name
148 return TransitionKey.__cached_key(name, get_bounds) 181 return TransitionKey.__cached_key(None, name, get_bounds)
149 182
150 @staticmethod 183 @staticmethod
151 def __process_graph(graph, ranges, key_map): 184 def __process_graph(encoding, graph, ranges, key_map):
152 key = graph[0] 185 key = graph[0]
153 if key == 'RANGE': 186 if key == 'RANGE':
154 ranges.append((ord(graph[1]), ord(graph[2]))) 187 ranges.append((ord(graph[1]), ord(graph[2])))
155 elif key == 'LITERAL': 188 elif key == 'LITERAL':
156 ranges.append((ord(graph[1]), ord(graph[1]))) 189 ranges.append((ord(graph[1]), ord(graph[1])))
157 elif key == 'CAT': 190 elif key == 'CAT':
158 for x in [graph[1], graph[2]]: 191 for x in [graph[1], graph[2]]:
159 TransitionKey.__process_graph(x, ranges, key_map) 192 TransitionKey.__process_graph(encoding, x, ranges, key_map)
160 elif key == 'CHARACTER_CLASS': 193 elif key == 'CHARACTER_CLASS':
161 class_name = graph[1] 194 class_name = graph[1]
162 if class_name in TransitionKey.__class_bounds: 195 if encoding.class_range(class_name):
196 r = encoding.class_range(class_name)
163 if class_name in key_map: 197 if class_name in key_map:
164 assert (key_map[class_name] == 198 assert key_map[class_name] == TransitionKey(encoding, [r])
165 TransitionKey([TransitionKey.__class_bounds[class_name]])) 199 ranges.append(r)
166 ranges.append(TransitionKey.__class_bounds[class_name]) 200 elif encoding.predefined_range_iter(class_name):
167 elif class_name in TransitionKey.__predefined_ranges: 201 rs = list(encoding.predefined_range_iter(class_name))
168 if class_name in key_map: 202 if class_name in key_map:
169 assert (key_map[class_name] == 203 assert key_map[class_name] == TransitionKey(encoding, rs)
170 TransitionKey(TransitionKey.__predefined_ranges[class_name])) 204 ranges += rs
171 ranges += TransitionKey.__predefined_ranges[class_name]
172 elif class_name in key_map: 205 elif class_name in key_map:
173 ranges += key_map[class_name].__ranges 206 ranges += key_map[class_name].__ranges
174 else: 207 else:
175 raise Exception('unknown character class [%s]' % graph[1]) 208 raise Exception('unknown character class [%s]' % graph[1])
176 else: 209 else:
177 raise Exception('bad key [%s]' % key) 210 raise Exception('bad key [%s]' % key)
178 211
179 @staticmethod 212 @staticmethod
180 def character_class(graph, key_map): 213 def character_class(encoding, graph, key_map):
181 '''Processes 'graph' (a representation of a character class in the rule 214 '''Processes 'graph' (a representation of a character class in the rule
182 file), and constructs a TransitionKey based on it. 'key_map' contains 215 file), and constructs a TransitionKey based on it. 'key_map' contains
183 already constructed aliases for character classes (they can be used in the 216 already constructed aliases for character classes (they can be used in the
184 new character class). It is a map from strings (character class names) to 217 new character class). It is a map from strings (character class names) to
185 TransitionKeys. For example, graph might represent the character class 218 TransitionKeys. For example, graph might represent the character class
186 [a-z:digit:] where 'digit' is a previously constructed character class found 219 [a-z:digit:] where 'digit' is a previously constructed character class found
187 in "key_map".''' 220 in "key_map".'''
188 ranges = [] 221 ranges = []
189 assert graph[0] == 'CLASS' or graph[0] == 'NOT_CLASS' 222 assert graph[0] == 'CLASS' or graph[0] == 'NOT_CLASS'
190 invert = graph[0] == 'NOT_CLASS' 223 invert = graph[0] == 'NOT_CLASS'
191 TransitionKey.__process_graph(graph[1], ranges, key_map) 224 TransitionKey.__process_graph(encoding, graph[1], ranges, key_map)
192 return TransitionKey.__key_from_ranges(invert, ranges) 225 return TransitionKey.__key_from_ranges(encoding, invert, ranges)
193 226
194 def matches_char(self, char): 227 def matches_char(self, char):
195 char = ord(char) 228 char = ord(char)
196 # TODO class checks 229 assert char < 128
197 for r in self.__ranges: 230 for r in self.__ranges:
198 if r[0] <= char and char <= r[1]: return True 231 if r[0] <= char and char <= r[1]: return True
199 return False 232 return False
200 233
201 def is_superset_of_key(self, key): 234 def is_superset_of_key(self, key):
202 '''Returns true if 'key' is a sub-key of this TransitionKey.''' 235 '''Returns true if 'key' is a sub-key of this TransitionKey.'''
203 assert isinstance(key, self.__class__) 236 assert isinstance(key, self.__class__)
204 assert key != TransitionKey.epsilon() and not key.__is_unique() 237 assert key != TransitionKey.epsilon() and not key.__is_unique()
205 assert len(key.__ranges) == 1 238 assert len(key.__ranges) == 1
206 subkey = key.__ranges[0] 239 subkey = key.__ranges[0]
(...skipping 19 matching lines...) Expand all
226 c = cmp(l, r) 259 c = cmp(l, r)
227 if c: 260 if c:
228 return c 261 return c
229 i += 1 262 i += 1
230 if i == left_length and i == right_length: 263 if i == left_length and i == right_length:
231 return 0 264 return 0
232 return 1 if i != left_length else -1 265 return 1 if i != left_length else -1
233 266
234 def __hash__(self): 267 def __hash__(self):
235 if self.__cached_hash == None: 268 if self.__cached_hash == None:
236 initial_hash = hash((-1, TransitionKey.__upper_bound + 1)) 269 self.__cached_hash = hash(self.__ranges)
237 f = lambda acc, r: acc ^ hash(r)
238 self.__cached_hash = reduce(f, self.__ranges, initial_hash)
239 return self.__cached_hash 270 return self.__cached_hash
240 271
241 def __eq__(self, other): 272 def __eq__(self, other):
242 return isinstance(other, self.__class__) and self.__ranges == other.__ranges 273 return isinstance(other, self.__class__) and self.__ranges == other.__ranges
243 274
244 @staticmethod 275 @staticmethod
245 def __class_name(r): 276 def __class_name(encoding, r):
246 for name, v in TransitionKey.__class_bounds.items(): 277 for name, v in encoding.class_range_iter():
247 if r == v: return name 278 if r == v: return name
248 assert False 279 assert False
249 280
250 def range_iter(self): 281 def range_iter(self, encoding):
251 assert not self == TransitionKey.epsilon() and not self.__is_unique() 282 assert not self == TransitionKey.epsilon() and not self.__is_unique()
252 for r in self.__ranges: 283 for r in self.__ranges:
253 if self.__is_class_range(r): 284 if encoding.is_class_range(r):
254 yield ('CLASS', TransitionKey.__class_name(r)) 285 yield ('CLASS', TransitionKey.__class_name(encoding, r))
255 else: 286 else:
256 yield ('LATIN_1', r) 287 yield ('LATIN_1', r)
257 288
258 __printable_cache = { 289 __printable_cache = {
259 ord('\t') : '\\t', 290 ord('\t') : '\\t',
260 ord('\n') : '\\n', 291 ord('\n') : '\\n',
261 ord('\r') : '\\r', 292 ord('\r') : '\\r',
262 } 293 }
263 294
264 @staticmethod 295 @staticmethod
265 def __range_str(r): 296 def __range_str(encoding, r):
266 if TransitionKey.__is_class_range(r): 297 if encoding and encoding.is_class_range(r):
267 return TransitionKey.__class_name(r) 298 return TransitionKey.__class_name(encoding, r)
268 def to_str(x): 299 def to_str(x):
269 assert TransitionKey.__in_latin_1(x) 300 assert not encoding or encoding.in_primary_range(x)
301 if x > 127:
302 return str(x)
270 if not x in TransitionKey.__printable_cache: 303 if not x in TransitionKey.__printable_cache:
271 res = "'%s'" % chr(x) if chr(x) in printable else str(x) 304 res = "'%s'" % chr(x) if chr(x) in printable else str(x)
272 TransitionKey.__printable_cache[x] = res 305 TransitionKey.__printable_cache[x] = res
273 return TransitionKey.__printable_cache[x] 306 return TransitionKey.__printable_cache[x]
274 if r[0] == r[1]: 307 if r[0] == r[1]:
275 return '%s' % to_str(r[0]) 308 return '%s' % to_str(r[0])
276 else: 309 else:
277 return '[%s-%s]' % (to_str(r[0]), to_str(r[1])) 310 return '[%s-%s]' % (to_str(r[0]), to_str(r[1]))
278 311
279 def __init__(self, ranges, name = None): 312 def __init__(self, encoding, ranges, name = None):
280 if not ranges: 313 if not ranges:
281 assert name == 'epsilon' 314 assert name == 'epsilon'
282 assert not name in TransitionKey.__cached_keys 315 assert not name in TransitionKey.__cached_keys['no_encoding']
283 else: 316 else:
284 TransitionKey.__verify_ranges(ranges, True) 317 TransitionKey.__verify_ranges(encoding, ranges, True)
285 self.__name = name 318 self.__name = name
286 self.__ranges = tuple(ranges) # immutable 319 self.__ranges = tuple(ranges) # immutable
287 self.__cached_hash = None 320 self.__cached_hash = None
288 321
289 def __str__(self): 322 def to_string(self, encoding):
290 if self.__name: 323 if self.__name:
291 return self.__name 324 return self.__name
292 return ', '.join(TransitionKey.__range_str(x) for x in self.__ranges) 325 strings = [TransitionKey.__range_str(encoding, x) for x in self.__ranges]
326 return ', '.join(strings)
327
328 def __str__(self):
329 self.to_string(None)
293 330
294 @staticmethod 331 @staticmethod
295 def __disjoint_keys(range_map): 332 def __disjoint_keys(encoding, range_map):
296 '''Takes a set of possibly overlapping ranges, returns a list of ranges 333 '''Takes a set of possibly overlapping ranges, returns a list of ranges
297 which don't overlap and which cover the same points as the original 334 which don't overlap and which cover the same points as the original
298 set. range_map is a map from lower bounds to a list of upper bounds.''' 335 set. range_map is a map from lower bounds to a list of upper bounds.'''
299 sort = lambda x : sorted(set(x)) 336 sort = lambda x : sorted(set(x))
300 range_map = sorted(map(lambda (k, v): (k, sort(v)), range_map.items())) 337 range_map = sorted(map(lambda (k, v): (k, sort(v)), range_map.items()))
301 ranges = [] 338 ranges = []
302 upper_bound = TransitionKey.__upper_bound + 1 339 upper_bound = encoding.upper_bound() + 1
303 for i in range(len(range_map)): 340 for i in range(len(range_map)):
304 (left, left_values) = range_map[i] 341 (left, left_values) = range_map[i]
305 next = range_map[i + 1][0] if i != len(range_map) - 1 else upper_bound 342 next = range_map[i + 1][0] if i != len(range_map) - 1 else upper_bound
306 to_push = [] 343 to_push = []
307 for v in left_values: 344 for v in left_values:
308 assert left <= next 345 assert left <= next
309 if v >= next: 346 if v >= next:
310 if not to_push and left < next: 347 if not to_push and left < next:
311 ranges.append((left, next - 1)) 348 ranges.append((left, next - 1))
312 to_push.append(v) 349 to_push.append(v)
313 else: 350 else:
314 ranges.append((left, v)) 351 ranges.append((left, v))
315 left = v + 1 352 left = v + 1
316 if to_push: 353 if to_push:
317 current = range_map[i + 1] 354 current = range_map[i + 1]
318 range_map[i + 1] = (current[0], sort(current[1] + to_push)) 355 range_map[i + 1] = (current[0], sort(current[1] + to_push))
319 return ranges 356 return ranges
320 357
321 @staticmethod 358 @staticmethod
322 def __disjoint_ranges_from_key_set(key_set): 359 def __disjoint_ranges_from_key_set(encoding, key_set):
323 if not key_set: 360 if not key_set:
324 return [] 361 return []
325 range_map = {} 362 range_map = {}
326 for x in key_set: 363 for x in key_set:
327 assert not x.__is_unique() 364 assert not x.__is_unique()
328 assert x != TransitionKey.epsilon 365 assert x != TransitionKey.epsilon()
329 for r in x.__ranges: 366 for r in x.__ranges:
330 if not r[0] in range_map: 367 if not r[0] in range_map:
331 range_map[r[0]] = [] 368 range_map[r[0]] = []
332 range_map[r[0]].append(r[1]) 369 range_map[r[0]].append(r[1])
333 ranges = TransitionKey.__disjoint_keys(range_map) 370 ranges = TransitionKey.__disjoint_keys(encoding, range_map)
334 TransitionKey.__verify_ranges(ranges, False) 371 TransitionKey.__verify_ranges(encoding, ranges, False)
335 return ranges 372 return ranges
336 373
337 @staticmethod 374 @staticmethod
338 def disjoint_keys(key_set): 375 def disjoint_keys(encoding, key_set):
339 '''Takes a set of possibly overlapping TransitionKeys, returns a list of 376 '''Takes a set of possibly overlapping TransitionKeys, returns a list of
340 TransitionKeys which don't overlap and whose union is the same as the union 377 TransitionKeys which don't overlap and whose union is the same as the union
341 of the original key_set. In addition, TransitionKeys are not merged, only 378 of the original key_set. In addition, TransitionKeys are not merged, only
342 split. 379 split.
343 380
344 For example, if key_set contains two TransitionKeys for ranges [1-10] and 381 For example, if key_set contains two TransitionKeys for ranges [1-10] and
345 [5-15], disjoint_keys returns a set of three TransitionKeys: [1-4], [5-10], 382 [5-15], disjoint_keys returns a set of three TransitionKeys: [1-4], [5-10],
346 [11-16].''' 383 [11-16].'''
347 ranges = TransitionKey.__disjoint_ranges_from_key_set(key_set) 384 ranges = TransitionKey.__disjoint_ranges_from_key_set(encoding, key_set)
348 return map(lambda x : TransitionKey([x]), ranges) 385 return map(lambda x : TransitionKey(encoding, [x]), ranges)
349 386
350 @staticmethod 387 @staticmethod
351 def inverse_key(key_set): 388 def inverse_key(encoding, key_set):
352 '''Returns a TransitionKey which matches represents the inverse of the union 389 '''Returns a TransitionKey which matches represents the inverse of the union
353 of 'key_set'. The TransitionKeys contain a set of character ranges and a set 390 of 'key_set'. The TransitionKeys contain a set of character ranges and a set
354 of classes. The character ranges are inverted in relation to the latin_1 391 of classes. The character ranges are inverted in relation to the latin_1
355 character range, and the character classes are inverted in relation to all 392 character range, and the character classes are inverted in relation to all
356 character classes in __class_bounds.''' 393 character classes in __class_bounds.'''
357 ranges = TransitionKey.__disjoint_ranges_from_key_set(key_set) 394 ranges = TransitionKey.__disjoint_ranges_from_key_set(encoding, key_set)
358 inverse = TransitionKey.__invert_ranges(ranges) 395 inverse = TransitionKey.__invert_ranges(encoding, ranges)
359 if not inverse: 396 if not inverse:
360 return None 397 return None
361 return TransitionKey(inverse) 398 return TransitionKey(encoding, inverse)
362 399
363 @staticmethod 400 @staticmethod
364 def __key_from_ranges(invert, ranges): 401 def __key_from_ranges(encoding, invert, ranges):
365 range_map = {} 402 range_map = {}
366 for r in ranges: 403 for r in ranges:
367 if not r[0] in range_map: 404 if not r[0] in range_map:
368 range_map[r[0]] = [] 405 range_map[r[0]] = []
369 range_map[r[0]].append(r[1]) 406 range_map[r[0]].append(r[1])
370 ranges = TransitionKey.__disjoint_keys(range_map) 407 ranges = TransitionKey.__disjoint_keys(encoding, range_map)
371 ranges = TransitionKey.__merge_ranges(ranges) 408 ranges = TransitionKey.__merge_ranges(encoding, ranges)
372 if invert: 409 if invert:
373 ranges = TransitionKey.__invert_ranges(ranges) 410 ranges = TransitionKey.__invert_ranges(encoding, ranges)
374 return TransitionKey(ranges) 411 return TransitionKey(encoding, ranges)
375 412
376 @staticmethod 413 @staticmethod
377 def __merge_ranges(ranges): 414 def __merge_ranges(encoding, ranges):
378 merged = [] 415 merged = []
379 last = None 416 last = None
380 for r in ranges: 417 for r in ranges:
381 assert not TransitionKey.__is_unique_range(r) 418 assert not TransitionKey.__is_unique_range(r)
382 if last == None: 419 if last == None:
383 last = r 420 last = r
384 elif (last[1] + 1 == r[0] and not TransitionKey.__is_class_range(r)): 421 elif (last[1] + 1 == r[0] and not encoding.is_class_range(r)):
385 last = (last[0], r[1]) 422 last = (last[0], r[1])
386 else: 423 else:
387 merged.append(last) 424 merged.append(last)
388 last = r 425 last = r
389 if last != None: 426 if last != None:
390 merged.append(last) 427 merged.append(last)
391 return merged 428 return merged
392 429
393 @staticmethod 430 @staticmethod
394 def merged_key(keys): 431 def merged_key(encoding, keys):
395 f = lambda acc, key: acc + list(key.__ranges) 432 f = lambda acc, key: acc + list(key.__ranges)
396 return TransitionKey.__key_from_ranges(False, reduce(f, keys, [])) 433 return TransitionKey.__key_from_ranges(encoding, False, reduce(f, keys, []))
397 434
398 @staticmethod 435 @staticmethod
399 def __invert_ranges(ranges): 436 def __invert_ranges(encoding, ranges):
400 inverted = [] 437 inverted = []
401 last = None 438 last = None
402 # Extract character classes (as opposed to character ranges) from 439 classes = set(encoding.class_value_iter())
403 # __class_bounds. Since latin_1 is the only real character range, we can do
404 # this.
405 classes = set(TransitionKey.__class_bounds.values())
406 latin_1 = TransitionKey.__class_bounds['latin_1']
407 classes.remove(latin_1)
408 for r in ranges: 440 for r in ranges:
409 assert not TransitionKey.__is_unique_range(r) 441 assert not TransitionKey.__is_unique_range(r)
410 if TransitionKey.__is_class_range(r): 442 if encoding.is_class_range(r):
411 classes.remove(r) 443 classes.remove(r)
412 continue 444 continue
413 if last == None: 445 if last == None:
414 if r[0] != TransitionKey.__lower_bound: 446 if r[0] != encoding.lower_bound():
415 inverted.append((TransitionKey.__lower_bound, r[0] - 1)) 447 inverted.append((encoding.lower_bound(), r[0] - 1))
416 elif last[1] + 1 < r[0]: 448 elif last[1] + 1 < r[0]:
417 inverted.append((last[1] + 1, r[0] - 1)) 449 inverted.append((last[1] + 1, r[0] - 1))
418 last = r 450 last = r
419 upper_bound = latin_1[1] 451 upper_bound = encoding.primary_range()[1]
420 if last == None: 452 if last == None:
421 inverted.append(latin_1) 453 inverted.append(encoding.primary_range())
422 elif last[1] < upper_bound: 454 elif last[1] < upper_bound:
423 inverted.append((last[1] + 1, upper_bound)) 455 inverted.append((last[1] + 1, upper_bound))
424 inverted += list(classes) 456 inverted += list(classes)
425 return inverted 457 return inverted
458
459 class Latin1Encoding(KeyEncoding):
460
461 def __init__(self):
462 super(Latin1Encoding, self).__init__(
463 'latin1',
464 (1, 255),
465 ['eos', 'zero', 'byte_order_mark'])
466 self.add_predefined_range(
467 'whitespace', [(9, 9), (11, 12), (32, 32), (133, 133), (160, 160)])
468 self.add_predefined_range(
469 'letter', [
470 (65, 90), (97, 122), (170, 170), (181, 181),
471 (186, 186), (192, 214), (216, 246), (248, 255)])
472 self.add_predefined_range('line_terminator', [(10, 10), (13, 13)])
473 self.add_predefined_range(
474 'identifier_part_not_letter', [(48, 57), (95, 95)])
475
476 class Utf16Encoding(KeyEncoding):
477
478 def __init__(self):
479 super(Utf16Encoding, self).__init__(
480 'utf16',
481 (1, 255),
482 ['eos', 'zero', 'byte_order_mark',
483 'non_latin_1_whitespace',
484 'non_latin_1_letter',
485 'non_latin_1_identifier_part_not_letter',
486 'non_latin_1_line_terminator',
487 'non_latin_1_everything_else'])
488 self.add_predefined_range(
489 'whitespace',
490 [(9, 9), (11, 12), (32, 32), (133, 133), (160, 160),
491 self.class_range('non_latin_1_whitespace')])
492 self.add_predefined_range(
493 'letter', [
494 (65, 90), (97, 122), (170, 170), (181, 181),
495 (186, 186), (192, 214), (216, 246), (248, 255),
496 self.class_range('non_latin_1_letter')])
497 self.add_predefined_range(
498 'line_terminator',
499 [(10, 10), (13, 13), self.class_range('non_latin_1_line_terminator')])
500 self.add_predefined_range(
501 'identifier_part_not_letter',
502 [(48, 57), (95, 95),
503 self.class_range('non_latin_1_identifier_part_not_letter')])
OLDNEW
« no previous file with comments | « tools/lexer_generator/transition_key_test.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698