| 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 from transition_key import TransitionKey |
| 29 from automaton import Term, Action, Automaton |
| 30 from dfa import Dfa |
| 31 |
| 32 class BacktrackingGenerator(object): |
| 33 |
| 34 @staticmethod |
| 35 def generate(dfa, default_action): |
| 36 return BacktrackingGenerator(dfa, default_action, None).__generate() |
| 37 |
| 38 def __init__(self, dfa, default_action, log): |
| 39 self.__dfa = dfa |
| 40 self.__default_action = default_action |
| 41 self.__log = log |
| 42 |
| 43 def __generate(self): |
| 44 dfa = self.__dfa |
| 45 default_action = self.__default_action |
| 46 terminal_set = dfa.terminal_set() |
| 47 # Collect states that terminate currently. |
| 48 action_states = {} |
| 49 omega_states = set() |
| 50 def f(state, acc): |
| 51 omega_state = state.omega_transition() |
| 52 if omega_state == None: |
| 53 if not state in terminal_set: |
| 54 state.key_iter().next() # should have at least one transition |
| 55 return |
| 56 assert omega_state in terminal_set |
| 57 assert not state in terminal_set |
| 58 assert not list(omega_state.key_iter()) |
| 59 omega_states.add(omega_state) |
| 60 match_action = omega_state.action() |
| 61 if not match_action: |
| 62 match_action = default_action |
| 63 action_states[state] = match_action |
| 64 dfa.visit_all_states(f) |
| 65 assert omega_states == terminal_set |
| 66 # Find nodes that need backtracking edges |
| 67 incoming_transitions = dfa.build_incoming_transitions_map() |
| 68 backtracking_map = {} |
| 69 store_states = set() |
| 70 # Start node may be an edge case. |
| 71 start_state = dfa.start_state() |
| 72 if (not start_state in incoming_transitions and |
| 73 not start_state in action_states): |
| 74 action_states[start_state] = default_action |
| 75 for state in incoming_transitions.iterkeys(): |
| 76 if state in omega_states or state in action_states: |
| 77 continue |
| 78 assert not state.omega_transition() |
| 79 seen = set([state]) |
| 80 unchecked = set([state]) |
| 81 match_edge = set() |
| 82 while unchecked: |
| 83 next = set() |
| 84 for unchecked_state in unchecked: |
| 85 if not unchecked_state in incoming_transitions: |
| 86 assert unchecked_state == start_state |
| 87 match_edge.add(unchecked_state) |
| 88 continue |
| 89 for (incoming_key, incoming_state) in incoming_transitions[unchecked_s
tate]: |
| 90 assert not incoming_state in omega_states |
| 91 assert not incoming_key == TransitionKey.omega() |
| 92 if incoming_state in seen: |
| 93 continue |
| 94 if incoming_state in action_states: |
| 95 match_edge.add(incoming_state) |
| 96 seen.add(incoming_state) |
| 97 else: |
| 98 next.add(incoming_state) |
| 99 seen |= unchecked |
| 100 unchecked = next - seen |
| 101 # accumulate unique actions |
| 102 actions = set(map(lambda x : action_states[x].term(), match_edge)) |
| 103 assert actions |
| 104 if not len(actions) == 1: |
| 105 # TODO(dcarney): need to warn here after second pass |
| 106 # actions = set([default_action]) |
| 107 continue |
| 108 action = iter(actions).next() |
| 109 # don't install default actions |
| 110 if action == default_action.term(): |
| 111 continue |
| 112 store_states |= match_edge |
| 113 backtracking_map[state.node_number()] = (action, match_edge) |
| 114 def install_backtracking_action(new_states, node_number): |
| 115 omega_state_id = str(node_number) + '_bt' |
| 116 key = TransitionKey.omega() |
| 117 new_states[str(node_number)]['transitions'][key] = omega_state_id |
| 118 # install new state |
| 119 old_action = backtracking_map[node_number][0] |
| 120 new_states[omega_state_id] = { |
| 121 'transitions' : {}, |
| 122 'action' : Action(Term('backtrack', old_action), 0), |
| 123 'terminal' : True, |
| 124 } |
| 125 def new_state_action(old_state): |
| 126 action = old_state.action() |
| 127 if not old_state in store_states: |
| 128 return action |
| 129 term = Term('store_lexing_state') |
| 130 if action: |
| 131 # TODO(dcarney): split target state instead |
| 132 term = Term('chain', term, action.term()) |
| 133 return Action(term, 0) |
| 134 # Now generate the new dfa. |
| 135 def convert(old_state, new_states): |
| 136 node_number = old_state.node_number() |
| 137 # Clone existing state. |
| 138 new_states[str(node_number)] = { |
| 139 'transitions' : { |
| 140 k : str(v.node_number()) for (k, v) in old_state.key_state_iter() }, |
| 141 'action' : new_state_action(old_state), |
| 142 'terminal' : old_state in terminal_set |
| 143 } |
| 144 # install a backtracking action |
| 145 if node_number in backtracking_map: |
| 146 install_backtracking_action(new_states, node_number) |
| 147 return new_states |
| 148 new_states = dfa.visit_all_states(convert, {}) |
| 149 return Dfa(dfa.encoding(), str(start_state.node_number()), new_states) |
| OLD | NEW |