| 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 |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 107 transitions = sorted(transitions, cmp) | 107 transitions = sorted(transitions, cmp) |
| 108 # map transition keys to disjoint ranges | 108 # map transition keys to disjoint ranges |
| 109 disjoint_keys = {'value' : []} | 109 disjoint_keys = {'value' : []} |
| 110 def f((key, state)): | 110 def f((key, state)): |
| 111 ranges = list(key.range_iter()) | 111 ranges = list(key.range_iter()) |
| 112 disjoint_keys['value'] += ranges | 112 disjoint_keys['value'] += ranges |
| 113 return (ranges, state) | 113 return (ranges, state) |
| 114 transitions = map(f, transitions) | 114 transitions = map(f, transitions) |
| 115 disjoint_keys = sorted(disjoint_keys['value'], CodeGenerator.__range_cmp) | 115 disjoint_keys = sorted(disjoint_keys['value'], CodeGenerator.__range_cmp) |
| 116 # dictionary object representing state | 116 # dictionary object representing state |
| 117 (class_keys, distinct_keys, ranges) = (0, 0, 0) |
| 118 for (t, r) in disjoint_keys: |
| 119 if t == 'CLASS': |
| 120 class_keys += 1 |
| 121 elif t == 'LATIN_1': |
| 122 distinct_keys += r[1] - r[0] + 1 |
| 123 ranges += 1 |
| 124 else: |
| 125 raise Exception() |
| 117 return { | 126 return { |
| 118 'node_number' : state.node_number(), | 127 'node_number' : state.node_number(), |
| 119 'original_node_number' : state.node_number(), | 128 'original_node_number' : state.node_number(), |
| 120 'transitions' : transitions, | 129 'transitions' : transitions, |
| 121 'switch_transitions' : [], | 130 'switch_transitions' : [], |
| 122 'disjoint_keys' : disjoint_keys, | 131 'disjoint_keys' : disjoint_keys, |
| 123 'inline' : None, | 132 'inline' : None, |
| 124 'depth' : None, | 133 'depth' : None, |
| 125 'action' : action, | 134 'action' : action, |
| 126 'entry_action' : entry_action, | 135 'entry_action' : entry_action, |
| 127 'match_action' : match_action, | 136 'match_action' : match_action, |
| 137 'class_keys' : class_keys, |
| 138 'distinct_keys' : distinct_keys, |
| 139 'ranges' : ranges |
| 128 } | 140 } |
| 129 | 141 |
| 130 @staticmethod | 142 @staticmethod |
| 131 def __compute_depths(node_number, depth, id_map): | 143 def __compute_depths(node_number, depth, id_map): |
| 132 state = id_map[node_number] | 144 state = id_map[node_number] |
| 133 if state['depth'] != None: | 145 if state['depth'] != None: |
| 134 return | 146 return |
| 135 state['depth'] = depth | 147 state['depth'] = depth |
| 136 for (k, transition_node) in state['transitions']: | 148 for (k, transition_node) in state['transitions']: |
| 137 CodeGenerator.__compute_depths(transition_node, depth + 1, id_map) | 149 CodeGenerator.__compute_depths(transition_node, depth + 1, id_map) |
| 138 | 150 |
| 139 @staticmethod | 151 def __set_inline(self, count, state): |
| 140 def __set_inline(count, state): | |
| 141 assert state['inline'] == None | 152 assert state['inline'] == None |
| 142 inline = False | 153 inline = False |
| 154 # inline terminal states |
| 143 if not state['transitions']: | 155 if not state['transitions']: |
| 144 inline = True | 156 inline = True |
| 145 # TODO add in 1 or 2 element if blocks | 157 # inline next to terminal states with 1 or 2 transitions |
| 158 elif state['distinct_keys'] < 3 and state['class_keys'] == 0: |
| 159 inline = True |
| 160 # ensure state terminates in 1 step |
| 161 for key, state_id in state['transitions']: |
| 162 if self.__dfa_states[state_id]['transitions']: |
| 163 inline = False |
| 164 break |
| 146 state['inline'] = inline | 165 state['inline'] = inline |
| 147 return count + 1 if inline else count | 166 return count + 1 if inline else count |
| 148 | 167 |
| 149 @staticmethod | 168 @staticmethod |
| 150 def __split_transitions(split_count, state): | 169 def __split_transitions(split_count, state): |
| 151 '''Goes through the transitions for 'state' and decides which of them should | 170 '''Goes through the transitions for 'state' and decides which of them should |
| 152 use the if statement and which should use the switch statement.''' | 171 use the if statement and which should use the switch statement.''' |
| 153 assert not state['switch_transitions'] | 172 assert not state['switch_transitions'] |
| 154 (class_keys, distinct_keys, ranges) = (0, 0, 0) | 173 (distinct_keys, ranges) = (state['distinct_keys'], state['ranges']) |
| 155 for (t, r) in state['disjoint_keys']: | |
| 156 if t == 'CLASS': | |
| 157 class_keys += 1 | |
| 158 elif t == 'LATIN_1': | |
| 159 distinct_keys += r[1] - r[0] + 1 | |
| 160 ranges += 1 | |
| 161 else: | |
| 162 raise Exception() | |
| 163 if distinct_keys <= 7 or float(distinct_keys)/float(ranges) >= 7.0: | 174 if distinct_keys <= 7 or float(distinct_keys)/float(ranges) >= 7.0: |
| 164 return split_count | 175 return split_count |
| 165 switch_transitions = [] | 176 switch_transitions = [] |
| 166 if_transitions = [] | 177 if_transitions = [] |
| 167 for (ranges, node_id) in state['transitions']: | 178 for (ranges, node_id) in state['transitions']: |
| 168 i = [] | 179 i = [] |
| 169 s = [] | 180 s = [] |
| 170 for r in ranges: | 181 for r in ranges: |
| 171 if r[0] == 'CLASS': | 182 if r[0] == 'CLASS': |
| 172 i.append(r) | 183 i.append(r) |
| (...skipping 16 matching lines...) Expand all Loading... |
| 189 dfa_states = sorted(dfa_states, cmp=self.__state_cmp) | 200 dfa_states = sorted(dfa_states, cmp=self.__state_cmp) |
| 190 # remap all node numbers | 201 # remap all node numbers |
| 191 for i, state in enumerate(dfa_states): | 202 for i, state in enumerate(dfa_states): |
| 192 state['node_number'] = i | 203 state['node_number'] = i |
| 193 def f((key, original_node_number)): | 204 def f((key, original_node_number)): |
| 194 return (key, id_map[original_node_number]['node_number']) | 205 return (key, id_map[original_node_number]['node_number']) |
| 195 for state in dfa_states: | 206 for state in dfa_states: |
| 196 state['transitions'] = map(f, state['transitions']) | 207 state['transitions'] = map(f, state['transitions']) |
| 197 assert id_map[self.__start_node_number]['node_number'] == 0 | 208 assert id_map[self.__start_node_number]['node_number'] == 0 |
| 198 assert len(dfa_states) == self.__dfa.node_count() | 209 assert len(dfa_states) == self.__dfa.node_count() |
| 210 # store states |
| 211 self.__dfa_states = dfa_states |
| 199 # set nodes to inline | 212 # set nodes to inline |
| 200 if self.__inline: | 213 if self.__inline: |
| 201 inlined = reduce(CodeGenerator.__set_inline, dfa_states, 0) | 214 inlined = reduce(self.__set_inline, dfa_states, 0) |
| 202 if self.__log: | 215 if self.__log: |
| 203 print "%s states inlined" % inlined | 216 print "%s states inlined" % inlined |
| 204 elif self.__log: | 217 elif self.__log: |
| 205 print "no inlining" | 218 print "no inlining" |
| 206 # split transitions | 219 # split transitions |
| 207 if self.__switching: | 220 if self.__switching: |
| 208 switched = reduce(CodeGenerator.__split_transitions, dfa_states, 0) | 221 switched = reduce(CodeGenerator.__split_transitions, dfa_states, 0) |
| 209 if self.__log: | 222 if self.__log: |
| 210 print "%s states use switch (instead of if)" % switched | 223 print "%s states use switch (instead of if)" % switched |
| 211 elif self.__log: | 224 elif self.__log: |
| 212 print "no switching" | 225 print "no switching" |
| 213 # store states | |
| 214 self.__dfa_states = dfa_states | |
| 215 | 226 |
| 216 def process(self): | 227 def process(self): |
| 217 | 228 |
| 218 self.__canonicalize_traversal() | 229 self.__canonicalize_traversal() |
| 219 | 230 |
| 220 dfa_states = self.__dfa_states | 231 dfa_states = self.__dfa_states |
| 221 | 232 |
| 222 default_action = self.__default_action | 233 default_action = self.__default_action |
| 223 assert(default_action and default_action.match_action()) | 234 assert(default_action and default_action.match_action()) |
| 224 default_action = default_action.match_action() | 235 default_action = default_action.match_action() |
| 225 | 236 |
| 226 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | 237 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 227 template_env = jinja2.Environment( | 238 template_env = jinja2.Environment( |
| 228 loader = jinja2.PackageLoader('lexer_generator', '.'), | 239 loader = jinja2.PackageLoader('lexer_generator', '.'), |
| 229 undefined = jinja2.StrictUndefined) | 240 undefined = jinja2.StrictUndefined) |
| 230 template = template_env.get_template('code_generator.jinja') | 241 template = template_env.get_template('code_generator.jinja') |
| 231 | 242 |
| 232 return template.render( | 243 return template.render( |
| 233 start_node_number = 0, | 244 start_node_number = 0, |
| 234 debug_print = self.__debug_print, | 245 debug_print = self.__debug_print, |
| 235 default_action = default_action, | 246 default_action = default_action, |
| 236 dfa_states = dfa_states) | 247 dfa_states = dfa_states) |
| OLD | NEW |