| 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 from nfa import Nfa | 28 from nfa import Nfa |
| 29 from transition_keys import TransitionKey |
| 29 | 30 |
| 30 class DfaState: | 31 class DfaState: |
| 31 | 32 |
| 32 def __init__(self, name, node_number): | 33 def __init__(self, name, node_number): |
| 33 self.__name = name | 34 self.__name = name |
| 34 self.__node_number = node_number | 35 self.__node_number = node_number |
| 35 self.__transitions = {} | 36 self.__transitions = {} |
| 36 | 37 |
| 37 def name(self): | 38 def name(self): |
| 38 return self.__name | 39 return self.__name |
| 39 | 40 |
| 40 def node_number(self): | 41 def node_number(self): |
| 41 return self.__node_number | 42 return self.__node_number |
| 42 | 43 |
| 43 def add_transition(self, key, state): | 44 def add_transition(self, key, action, state): |
| 44 assert not self.__transitions.has_key(key) | 45 assert not self.__transitions.has_key(key) |
| 45 self.__transitions[key] = state | 46 self.__transitions[key] = (state, action) |
| 47 |
| 48 def set_action(self, action): |
| 49 assert self.__action == None |
| 50 self.__action = action |
| 46 | 51 |
| 47 def transitions(self): | 52 def transitions(self): |
| 48 return self.__transitions | 53 return self.__transitions |
| 49 | 54 |
| 50 class Dfa: | 55 class Dfa: |
| 51 | 56 |
| 52 def __init__(self, start_name, mapping, end_names): | 57 def __init__(self, start_name, mapping): |
| 58 self.__terminal_set = set() |
| 53 name_map = {} | 59 name_map = {} |
| 54 offset = 0 | 60 action_map = {} |
| 55 self.__terminal_set = set() | 61 for i, name in enumerate(mapping.keys()): |
| 56 for name in mapping.keys(): | 62 name_map[name] = DfaState(name, i) |
| 57 dfa_state = DfaState(name, offset) | 63 for name, node_data in mapping.items(): |
| 58 name_map[name] = dfa_state | 64 node = name_map[name] |
| 59 offset += 1 | 65 if node_data['terminal']: |
| 60 if name in end_names: | 66 self.__terminal_set.add(node) |
| 61 self.__terminal_set.add(dfa_state) | 67 inversion = {} |
| 62 for name, values in mapping.items(): | 68 for key, (state, action) in node_data['transitions'].items(): |
| 63 for key, value in values.items(): | 69 if not state in inversion: |
| 64 name_map[name].add_transition(key, name_map[value]) | 70 inversion[state] = {} |
| 71 # TODO fix this |
| 72 action_key = str(action) |
| 73 if not action_key in action_map: |
| 74 action_map[action_key] = action |
| 75 if not action_key in inversion[state]: |
| 76 inversion[state][action_key] = [] |
| 77 inversion[state][action_key].append(key) |
| 78 for state, values in inversion.items(): |
| 79 for action_key, keys in values.items(): |
| 80 merged_key = TransitionKey.merged_key(keys) |
| 81 action = action_map[action_key] |
| 82 node.add_transition(merged_key, action, name_map[state]) |
| 65 self.__start = name_map[start_name] | 83 self.__start = name_map[start_name] |
| 66 assert self.__terminal_set | 84 assert self.__terminal_set |
| 67 | 85 |
| 68 @staticmethod | 86 @staticmethod |
| 69 def __visit_edges(start, visitor, state): | 87 def __visit_edges(start, visitor, state): |
| 70 edge = set([start]) | 88 edge = set([start]) |
| 71 visited = set() | 89 visited = set() |
| 90 first = lambda v: [x[0] for x in v] |
| 72 while edge: | 91 while edge: |
| 73 f = lambda (next_edge, state), node: ( | 92 f = lambda (next_edge, state), node: ( |
| 74 next_edge | set(node.transitions().values()), | 93 next_edge | set(first(node.transitions().values())), |
| 75 visitor(node, state)) | 94 visitor(node, state)) |
| 76 (next_edge, state) = reduce(f, edge, (set(), state)) | 95 (next_edge, state) = reduce(f, edge, (set(), state)) |
| 77 visited |= edge | 96 visited |= edge |
| 78 edge = next_edge - visited | 97 edge = next_edge - visited |
| 79 return state | 98 return state |
| 80 | 99 |
| 81 def matches(self, string): | 100 def collect_actions(self, string): |
| 82 state = self.__start | 101 state = self.__start |
| 83 for c in string: | 102 for c in string: |
| 84 next_state = None | 103 next = [s for k, s in state.transitions().items() if k.matches_char(c)] |
| 85 for key, transition_state in state.transitions().items(): | 104 if not next: |
| 86 if key.matches_char(c): | 105 yield ('MISS',) |
| 87 next_state = transition_state | 106 return |
| 88 break | 107 assert len(next) == 1 |
| 89 if not next_state: | 108 (state, action) = next[0] |
| 90 return False | 109 if action: |
| 91 state = next_state | 110 yield action |
| 92 return state in self.__terminal_set | 111 if state in self.__terminal_set: |
| 112 yield ('TERMINATE', ) |
| 113 else: |
| 114 yield ('MISS',) |
| 115 |
| 116 def matches(self, string): |
| 117 actions = list(self.collect_actions(string)) |
| 118 return actions and actions[-1][0] == 'TERMINATE' |
| 93 | 119 |
| 94 def to_dot(self): | 120 def to_dot(self): |
| 95 | 121 |
| 96 def f(node, node_content): | 122 def f(node, node_content): |
| 97 for key, value in node.transitions().items(): | 123 for key, (state, action) in node.transitions().items(): |
| 98 node_content.append( | 124 node_content.append( |
| 99 " S_%s -> S_%s [ label = \"%s\" ];" % | 125 " S_%s -> S_%s [ label = \"%s\" ];" % |
| 100 (node.node_number(), value.node_number(), key)) | 126 (node.node_number(), state.node_number(), key)) |
| 101 return node_content | 127 return node_content |
| 102 | 128 |
| 103 node_content = self.__visit_edges(self.__start, f, []) | 129 node_content = self.__visit_edges(self.__start, f, []) |
| 104 terminals = ["S_%d;" % x.node_number() for x in self.__terminal_set] | 130 terminals = ["S_%d;" % x.node_number() for x in self.__terminal_set] |
| 105 start_number = self.__start.node_number() | 131 start_number = self.__start.node_number() |
| 106 start_shape = "circle" | 132 start_shape = "circle" |
| 107 if self.__start in self.__terminal_set: | 133 if self.__start in self.__terminal_set: |
| 108 start_shape = "doublecircle" | 134 start_shape = "doublecircle" |
| 109 | 135 |
| 110 return ''' | 136 return ''' |
| 111 digraph finite_state_machine { | 137 digraph finite_state_machine { |
| 112 rankdir=LR; | 138 rankdir=LR; |
| 113 node [shape = %s, style=filled, bgcolor=lightgrey]; S_%s | 139 node [shape = %s, style=filled, bgcolor=lightgrey]; S_%s |
| 114 node [shape = doublecircle, style=unfilled]; %s | 140 node [shape = doublecircle, style=unfilled]; %s |
| 115 node [shape = circle]; | 141 node [shape = circle]; |
| 116 %s | 142 %s |
| 117 } | 143 } |
| 118 ''' % (start_shape, | 144 ''' % (start_shape, |
| 119 start_number, | 145 start_number, |
| 120 " ".join(terminals), | 146 " ".join(terminals), |
| 121 "\n".join(node_content)) | 147 "\n".join(node_content)) |
| OLD | NEW |