| OLD | NEW |
| 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 | |
| 29 class TransitionKey: | 28 class TransitionKey: |
| 30 | 29 |
| 31 __lower_bound = 0 | 30 __lower_bound = 0 |
| 32 __latin_1_upper_bound = 255 | 31 __latin_1_upper_bound = 255 |
| 33 __unicode_whitespace_bounds = (256, 256) | 32 __unicode_whitespace_bounds = (256, 256) |
| 34 __unicode_literal_bounds = (257, 257) | 33 __unicode_literal_bounds = (257, 257) |
| 35 __upper_bound = 257 | 34 __upper_bound = 257 |
| 36 | 35 |
| 37 @staticmethod | 36 @staticmethod |
| 37 def __verify_ranges(ranges): |
| 38 last = (TransitionKey.__lower_bound - 1, TransitionKey.__lower_bound - 1) |
| 39 for r in ranges: |
| 40 assert TransitionKey.__lower_bound <= r[0] |
| 41 assert r[1] <= TransitionKey.__upper_bound |
| 42 assert r[0] <= r[1] |
| 43 assert last[1] < r[0] |
| 44 # TODO check classes are always disjoint points |
| 45 last = r |
| 46 |
| 47 @staticmethod |
| 38 def __create(ranges): | 48 def __create(ranges): |
| 39 # TODO - verify ranges | 49 TransitionKey.__verify_ranges(ranges) |
| 40 key = TransitionKey() | 50 key = TransitionKey() |
| 41 key.__ranges = ranges | 51 key.__ranges = ranges |
| 42 key.__cached_hash = None | 52 key.__cached_hash = None |
| 43 return key | 53 return key |
| 44 | 54 |
| 45 __cached_epsilon = None | 55 __cached_epsilon = None |
| 46 @staticmethod | 56 @staticmethod |
| 47 def epsilon(): | 57 def epsilon(): |
| 48 if (TransitionKey.__cached_epsilon == None): | 58 if (TransitionKey.__cached_epsilon == None): |
| 49 TransitionKey.__cached_epsilon = TransitionKey.__create([]) | 59 TransitionKey.__cached_epsilon = TransitionKey.__create([]) |
| 50 return TransitionKey.__cached_epsilon | 60 return TransitionKey.__cached_epsilon |
| 51 | 61 |
| 52 __cached_any = None | 62 __cached_any = None |
| 53 @staticmethod | 63 @staticmethod |
| 54 def any(): | 64 def any(): |
| 55 if (TransitionKey.__cached_any == None): | 65 if (TransitionKey.__cached_any == None): |
| 56 bounds = [(TransitionKey.__lower_bound, TransitionKey.__upper_bound)] | 66 bounds = [ |
| 67 (TransitionKey.__lower_bound, TransitionKey.__latin_1_upper_bound), |
| 68 TransitionKey.__unicode_whitespace_bounds, |
| 69 TransitionKey.__unicode_literal_bounds, |
| 70 ] |
| 57 TransitionKey.__cached_any = TransitionKey.__create(bounds) | 71 TransitionKey.__cached_any = TransitionKey.__create(bounds) |
| 58 return TransitionKey.__cached_any | 72 return TransitionKey.__cached_any |
| 59 | 73 |
| 60 @staticmethod | 74 @staticmethod |
| 61 def single_char(char): | 75 def single_char(char): |
| 62 char = ord(char) | 76 char = ord(char) |
| 63 assert (TransitionKey.__lower_bound <= char and | 77 assert (TransitionKey.__lower_bound <= char and |
| 64 char <= TransitionKey.__latin_1_upper_bound) | 78 char <= TransitionKey.__latin_1_upper_bound) |
| 65 return TransitionKey.__create([(char, char)]) | 79 return TransitionKey.__create([(char, char)]) |
| 66 | 80 |
| 67 @staticmethod | 81 @staticmethod |
| 68 def character_class(invert, graph): | 82 def character_class(invert, graph): |
| 69 # TODO | 83 # TODO |
| 70 return TransitionKey.__create([(129, 129)]) | 84 return TransitionKey.__create([(129, 129)]) |
| 71 | 85 |
| 72 def matches_char(self, char): | 86 def matches_char(self, char): |
| 73 char = ord(char) | 87 char = ord(char) |
| 88 # TODO class checks |
| 74 for r in self.__ranges: | 89 for r in self.__ranges: |
| 75 if r[0] <= char and char <= r[1]: return True | 90 if r[0] <= char and char <= r[1]: return True |
| 76 return False | 91 return False |
| 77 | 92 |
| 78 def matches_key(self, key): | 93 def matches_key(self, key): |
| 79 assert isinstance(key, self.__class__) | 94 assert isinstance(key, self.__class__) |
| 80 assert key != TransitionKey.epsilon() | 95 assert key != TransitionKey.epsilon() |
| 81 if self == key: return True | 96 assert len(key.__ranges) == 1 |
| 82 if self == TransitionKey.epsilon(): return False | 97 subkey = key.__ranges[0] |
| 83 # TODO | 98 for k in self.__ranges: |
| 99 if k[0] <= subkey[0] and k[1] >= subkey[1]: return True |
| 84 return False | 100 return False |
| 85 | 101 |
| 86 def __hash__(self): | 102 def __hash__(self): |
| 87 if self.__cached_hash == None: | 103 if self.__cached_hash == None: |
| 88 initial_hash = hash((-1, TransitionKey.__upper_bound + 1)) | 104 initial_hash = hash((-1, TransitionKey.__upper_bound + 1)) |
| 89 f = lambda acc, r: acc ^ hash(r) | 105 f = lambda acc, r: acc ^ hash(r) |
| 90 self.__cached_hash = reduce(f, self.__ranges, initial_hash) | 106 self.__cached_hash = reduce(f, self.__ranges, initial_hash) |
| 91 return self.__cached_hash | 107 return self.__cached_hash |
| 92 | 108 |
| 93 def __eq__(self, other): | 109 def __eq__(self, other): |
| 94 return isinstance(other, self.__class__) and self.__ranges == other.__ranges | 110 return isinstance(other, self.__class__) and self.__ranges == other.__ranges |
| 95 | 111 |
| 96 @staticmethod | 112 @staticmethod |
| 97 def __print_range(r): | 113 def __print_range(r): |
| 114 def to_str(x): |
| 115 if x <= TransitionKey.__latin_1_upper_bound: |
| 116 return chr(x) |
| 117 if x == TransitionKey.__unicode_literal_bounds[0]: |
| 118 return "literal" |
| 119 if x == TransitionKey.__unicode_whitespace_bounds[0]: |
| 120 return "whitespace" |
| 121 assert False |
| 98 if r[0] == r[1]: | 122 if r[0] == r[1]: |
| 99 return "'%s'" % chr(r[0]) | 123 return "'%s'" % to_str(r[0]) |
| 100 else: | 124 else: |
| 101 return "['%s'-'%s']" % (chr(r[0]), chr(r[1])) | 125 return "['%s'-'%s']" % (to_str(r[0]), to_str(r[1])) |
| 102 | 126 |
| 103 def __str__(self): | 127 def __str__(self): |
| 104 if self == self.epsilon(): | 128 if self == self.epsilon(): |
| 105 return "epsilon" | 129 return "epsilon" |
| 106 if self == self.any(): | 130 if self == self.any(): |
| 107 return "any" | 131 return "any" |
| 108 return ", ".join(TransitionKey.__print_range(x) for x in self.__ranges) | 132 return ", ".join(TransitionKey.__print_range(x) for x in self.__ranges) |
| 109 | 133 |
| 110 @staticmethod | 134 @staticmethod |
| 111 def merge_key_set(key_set): | 135 def disjoint_keys(key_set): |
| 112 key_set -= set([TransitionKey.epsilon()]) | 136 ranges = sorted(reduce(lambda acc, x : acc + x.__ranges, key_set, [])) |
| 113 # TODO need intersections and symmetric differences | 137 for i in range(0, len(ranges)): |
| 114 return key_set | 138 right = ranges[i][1] |
| 139 limit = i |
| 140 for j in range(i + 1, len(ranges)): |
| 141 if ranges[j][0] > right: |
| 142 break |
| 143 assert ranges[j][0] >= ranges[i][0] |
| 144 limit = j |
| 145 if limit == i: continue |
| 146 for j in range(i + 1, limit + 1): |
| 147 ranges[j] = (right, ranges[j][1]) |
| 148 TransitionKey.__verify_ranges(ranges) |
| 149 return map(lambda x : TransitionKey.__create([x]), ranges) |
| OLD | NEW |