| OLD | NEW |
| (Empty) | |
| 1 # Copyright 2014 the V8 project authors. All rights reserved. |
| 2 # Redistribution and use in source and binary forms, with or without |
| 3 # modification, are permitted provided that the following conditions are |
| 4 # met: |
| 5 # |
| 6 # * Redistributions of source code must retain the above copyright |
| 7 # notice, this list of conditions and the following disclaimer. |
| 8 # * Redistributions in binary form must reproduce the above |
| 9 # copyright notice, this list of conditions and the following |
| 10 # disclaimer in the documentation and/or other materials provided |
| 11 # with the distribution. |
| 12 # * Neither the name of Google Inc. nor the names of its |
| 13 # contributors may be used to endorse or promote products derived |
| 14 # from this software without specific prior written permission. |
| 15 # |
| 16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 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. |
| 27 |
| 28 import logging |
| 29 from transition_key import TransitionKey |
| 30 |
| 31 class Path(object): |
| 32 |
| 33 def __init__(self, keys, states): |
| 34 self.__keys = keys |
| 35 self.__states = states |
| 36 assert self.__states |
| 37 self.__hash = None |
| 38 |
| 39 def sub_path(self): |
| 40 if len(self) == 1: |
| 41 return None |
| 42 return Path(self.__keys[:-1], self.__states[:-1]) |
| 43 |
| 44 def key_iter(self): |
| 45 return iter(self.__keys) |
| 46 |
| 47 def state_at(self, index): |
| 48 return self.__states[index] |
| 49 |
| 50 @staticmethod |
| 51 def __hash_function((reps, l), state): |
| 52 n = state.node_number() |
| 53 h = (reps >> (l % 31) + 1) ^ reps ^ n ^ (n << (l % 31) + 1) |
| 54 return (h, l + 1) |
| 55 |
| 56 def __hash__(self): |
| 57 if self.__hash == None: |
| 58 self.__hash = reduce(self.__hash_function, self.__states, (101, -74))[0] |
| 59 return self.__hash |
| 60 |
| 61 def __eq__(self, other): |
| 62 return (isinstance(other, self.__class__) and |
| 63 self.__states == other.__states) |
| 64 |
| 65 def __len__(self): |
| 66 return len(self.__states) |
| 67 |
| 68 class DfaPathWriter(object): |
| 69 |
| 70 def __init__(self, dfa): |
| 71 self.__dfa = dfa |
| 72 |
| 73 def write_lexer_shell_test_file(self, f): |
| 74 dfa = self.__dfa |
| 75 complete_paths = set() |
| 76 total_paths = 0 |
| 77 encoding = dfa.encoding() |
| 78 start_state = dfa.start_state() |
| 79 for path, states_in_path, is_non_looping in dfa.path_iter(): |
| 80 states = [] |
| 81 keys = [] |
| 82 for i, (key, state) in enumerate(path[1:]): |
| 83 states.append(state) |
| 84 keys.append(key) |
| 85 if key == TransitionKey.omega() and is_non_looping: |
| 86 assert i == len(path) - 2 |
| 87 p = Path(keys, states) |
| 88 assert not p in complete_paths |
| 89 add_tail_transition = False |
| 90 total_paths += len(p) |
| 91 while p != None and (p not in complete_paths): |
| 92 complete_paths.add(p) |
| 93 self.__write_lines(p, start_state, add_tail_transition, encoding, f) |
| 94 p = p.sub_path() |
| 95 add_tail_transition = True |
| 96 logging.info("complete_paths %d" % len(complete_paths)) |
| 97 logging.info("total_paths %d" % total_paths) |
| 98 |
| 99 __representative_map = { |
| 100 'non_primary_letter' : 256, |
| 101 'non_primary_whitespace' : 5760, |
| 102 'non_primary_line_terminator' : 8232, |
| 103 'non_primary_everything_else' : 706, |
| 104 'non_primary_identifier_part_not_letter' : 768, |
| 105 } |
| 106 |
| 107 __eos_rep = -1 |
| 108 __omega_rep = -2 |
| 109 |
| 110 @staticmethod |
| 111 def get_representatives(key, remaining = -1): |
| 112 reps = [] |
| 113 contains_special = False |
| 114 for name, r in key.range_iter(None): |
| 115 if name == 'PRIMARY_RANGE': |
| 116 reps.append(r[0]) |
| 117 if r[0] != r[1]: |
| 118 reps.append(r[1]) |
| 119 elif name == 'CLASS': |
| 120 reps.append(DfaPathWriter.__representative_map[r]) |
| 121 elif name == 'UNIQUE': |
| 122 if r == 'eos': |
| 123 assert remaining == 1 |
| 124 reps.append(DfaPathWriter.__eos_rep) |
| 125 contains_special = True |
| 126 else: |
| 127 raise Exception('unimplemented %s %s' % (name, str(r))) |
| 128 elif name == 'OMEGA': |
| 129 assert remaining == 0 |
| 130 reps.append(DfaPathWriter.__omega_rep) |
| 131 contains_special = True |
| 132 else: |
| 133 raise Exception('unimplemented %s %s' % (name, str(r))) |
| 134 if contains_special: |
| 135 assert len(reps) == 1 |
| 136 return reps |
| 137 |
| 138 @staticmethod |
| 139 def no_transition_keys(state, encoding): |
| 140 all_keys = set(state.key_iter()) |
| 141 eos_found = False |
| 142 all_keys.discard(TransitionKey.omega()) |
| 143 eos_key = TransitionKey.unique('eos') |
| 144 if eos_key in all_keys: |
| 145 all_keys.remove(eos_key) |
| 146 eos_found = True |
| 147 # TODO(dcarney): have inverse key return uniques as a separate list |
| 148 inverse_key = TransitionKey.inverse_key(encoding, all_keys) |
| 149 if not inverse_key: |
| 150 return [] |
| 151 reps = DfaPathWriter.get_representatives(inverse_key) |
| 152 if not eos_found: |
| 153 reps.append(DfaPathWriter.__eos_rep) |
| 154 return reps |
| 155 |
| 156 @staticmethod |
| 157 def __write_line(head, tail, f): |
| 158 length = len(head) |
| 159 if tail != DfaPathWriter.__eos_rep: |
| 160 length += 1 |
| 161 if not length: |
| 162 return |
| 163 if tail == DfaPathWriter.__eos_rep: |
| 164 f.write('%s\n' % ' '.join(map(str, head))) |
| 165 elif head: |
| 166 f.write('%s %s\n' % (' '.join(map(str, head)), str(tail))) |
| 167 else: |
| 168 f.write("%s\n" % str(tail)) |
| 169 |
| 170 def __write_lines(self, path, start_state, add_tail_transition, encoding, f): |
| 171 current_state = start_state |
| 172 reps = [] |
| 173 last_index = len(path) - 1 |
| 174 if add_tail_transition: |
| 175 last_index += 1 |
| 176 for i, key in enumerate(path.key_iter()): |
| 177 # current_state = current_state.transition_state_for_key(key) |
| 178 # assert current_state == self.__states[i] |
| 179 reps.append(self.get_representatives(key, last_index - i)) |
| 180 head = [] |
| 181 for i, x in enumerate(reps): |
| 182 assert x |
| 183 if x[0] >= 0: |
| 184 # TODO(dcarney): produce a file with x[-1] here |
| 185 head.append(x[0]) |
| 186 elif x[0] == DfaPathWriter.__eos_rep: |
| 187 # this is a trailing eos or a trailing omega following eos. |
| 188 assert i == len(reps) - 1 or ( |
| 189 len(x) == 1 and i == len(reps) - 2 and |
| 190 len(reps[-1]) == 1 and reps[-1][0] == DfaPathWriter.__omega_rep) |
| 191 assert add_tail_transition == (i == len(reps) - 1) |
| 192 if i == len(reps) - 1: |
| 193 add_tail_transition = False |
| 194 elif x[0] == DfaPathWriter.__omega_rep: |
| 195 # this is a trailing omega |
| 196 # TODO(dcarney): produce an omega transition, not eos. |
| 197 assert not add_tail_transition |
| 198 assert len(x) == 1 and i == len(reps) - 1 |
| 199 else: |
| 200 raise Exception('unreachable') |
| 201 if not add_tail_transition: |
| 202 self.__write_line(head, self.__eos_rep, f) |
| 203 return |
| 204 no_transition_keys = self.no_transition_keys(path.state_at(-1), encoding) |
| 205 # TODO(dcarney): don't test all edges here. |
| 206 for tail in no_transition_keys: |
| 207 self.__write_line(head, tail, f) |
| OLD | NEW |