| OLD | NEW |
| (Empty) | |
| 1 # Copyright 2013 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_keys import TransitionKey |
| 29 from automaton import Action |
| 30 from dfa import Dfa |
| 31 |
| 32 class DfaOptimizer(object): |
| 33 |
| 34 def __init__(self, dfa, log): |
| 35 self.__dfa = dfa |
| 36 self.__log = log |
| 37 |
| 38 @staticmethod |
| 39 def transistions_match(encoding, incoming_key, incoming_state, state): |
| 40 keys = set(state.key_iter()) |
| 41 keys.add(incoming_key) |
| 42 disjoint_keys = TransitionKey.disjoint_keys(encoding, keys) |
| 43 for key in disjoint_keys: |
| 44 if not incoming_key.is_superset_of_key(key): |
| 45 continue |
| 46 transition_state = state.transition_state_for_key(key) |
| 47 if incoming_state.transition_state_for_key(key) != transition_state: |
| 48 return False |
| 49 return True |
| 50 |
| 51 def replace_tokens_with_gotos(self): |
| 52 '''replace states with no entry action and match action of type 'token(X)' |
| 53 with new states with entry action store_token(X) and match action |
| 54 goto(state_id) which has (far) fewer transitions''' |
| 55 |
| 56 dfa = self.__dfa |
| 57 encoding = dfa.encoding() |
| 58 |
| 59 incoming_transitions = {} |
| 60 def build_incoming_transitions(state, visitor_state): |
| 61 for key, transition_state in state.key_state_iter(): |
| 62 if not transition_state in incoming_transitions: |
| 63 incoming_transitions[transition_state] = [] |
| 64 incoming_transitions[transition_state].append((key,state)) |
| 65 dfa.visit_all_states(build_incoming_transitions) |
| 66 |
| 67 def is_replacement_candidate(state): |
| 68 action = state.action() |
| 69 if not action or action.entry_action() or not action.match_action(): |
| 70 return False |
| 71 if (action.match_action()[0] == 'token' or |
| 72 action.match_action()[0] == 'harmony_token'): |
| 73 return True |
| 74 return False |
| 75 |
| 76 replacements = {} |
| 77 for state, incoming in incoming_transitions.items(): |
| 78 if len(incoming) < 10: |
| 79 continue |
| 80 if not is_replacement_candidate(state): |
| 81 continue |
| 82 # check to see if incoming edges are matched by an outgoing edge |
| 83 def match_filter((incoming_key, incoming_state)): |
| 84 return (incoming_state != state and # drop self transitions |
| 85 is_replacement_candidate(incoming_state) and |
| 86 self.transistions_match( |
| 87 encoding, incoming_key, incoming_state, state)) |
| 88 for incoming_key_state in incoming: |
| 89 if not match_filter(incoming_key_state): |
| 90 continue |
| 91 (incoming_key, incoming_state) = incoming_key_state |
| 92 # set this transition as to be replaced |
| 93 if not incoming_state in replacements: |
| 94 replacements[incoming_state] = {} |
| 95 replacements[incoming_state][incoming_key] = state |
| 96 # now see if we can gather other transistions into the goto |
| 97 for key in incoming_state.key_iter(): |
| 98 if key == incoming_key: |
| 99 continue |
| 100 if not self.transistions_match( |
| 101 encoding, key, incoming_state, state): |
| 102 continue |
| 103 # this transition can be removed |
| 104 replacements[incoming_state][key] = None |
| 105 if not replacements: |
| 106 return |
| 107 # perform replacement |
| 108 states = {} |
| 109 name = lambda state : str(state.node_number()) |
| 110 counters = {'removals' : 0, 'gotos' : 0} |
| 111 store_states = set([]) |
| 112 # generate a store action to replace an existing action |
| 113 def replacement_action(old_action, match_action): |
| 114 assert not old_action.entry_action() |
| 115 assert old_action.match_action() |
| 116 if old_action.match_action()[0] == 'token': |
| 117 entry_action = ('store_token', old_action.match_action()[1]) |
| 118 elif old_action.match_action()[0] == 'harmony_token': |
| 119 entry_action = ('store_harmony_token', old_action.match_action()[1]) |
| 120 else: |
| 121 raise Exception(old_action.match_action()) |
| 122 return Action(entry_action, match_action, old_action.precedence()) |
| 123 # map the old state to the new state, with fewer transitions and |
| 124 # goto actions |
| 125 def replace_state(state, acc): |
| 126 new_state = { |
| 127 'transitions' : {}, |
| 128 'terminal' : state in self.__dfa.terminal_set(), |
| 129 'action' : state.action(), |
| 130 } |
| 131 states[name(state)] = new_state |
| 132 state_replacements = replacements[state] if state in replacements else {} |
| 133 for transition_key, transition_state in state.transitions().items(): |
| 134 if not transition_key in state_replacements: |
| 135 new_state['transitions'][transition_key] = name(transition_state) |
| 136 continue |
| 137 replacement = state_replacements[transition_key] |
| 138 del state_replacements[transition_key] |
| 139 # just drop the transition |
| 140 if replacement == None: |
| 141 counters['removals'] += 1 |
| 142 continue |
| 143 # do goto replacement |
| 144 counters['gotos'] += 1 |
| 145 match_action = ('goto', name(transition_state)) |
| 146 new_state['action'] = replacement_action(state.action(), match_action) |
| 147 # will need to patch up transition_state |
| 148 store_states.add(name(transition_state)) |
| 149 assert not state_replacements |
| 150 self.__dfa.visit_all_states(replace_state) |
| 151 # now patch up all states with stores |
| 152 for state in store_states: |
| 153 old_action = states[state]['action'] |
| 154 match_action = ('do_stored_token', None) |
| 155 states[state]['action'] = replacement_action(old_action, match_action) |
| 156 start_name = name(self.__dfa.start_state()) |
| 157 if self.__log: |
| 158 print 'gotos inserted %s' % counters['gotos'] |
| 159 print 'transitions removed %s' % counters['removals'] |
| 160 print 'do_stored_token actions added %s' % len(store_states) |
| 161 self.__dfa = Dfa(self.__dfa.encoding(), start_name, states) |
| 162 |
| 163 @staticmethod |
| 164 def optimize(dfa, log): |
| 165 optimizer = DfaOptimizer(dfa, log) |
| 166 optimizer.replace_tokens_with_gotos() |
| 167 return optimizer.__dfa |
| OLD | NEW |