Class: Reflexive::ParseTreeTopDownWalker
- Inherits:
-
Object
- Object
- Reflexive::ParseTreeTopDownWalker
- Defined in:
- lib/reflexive/parse_tree_top_down_walker.rb
Instance Method Summary collapse
- #add_local_variable(scanner_event) ⇒ Object
- #add_local_variables_from_lhs(lhs_event) ⇒ Object
- #add_local_variables_from_mlhs(mlhs_event) ⇒ Object
- #add_local_variables_from_params_event(params_event) ⇒ Object
- #constant_access(scanner_event, name = nil) ⇒ Object
- #constant_access_scope ⇒ Object
- #current_variables_scope ⇒ Object
- #extract_scanner_events_from_tree(tree) ⇒ Object
-
#initialize(events_tree) ⇒ ParseTreeTopDownWalker
constructor
A new instance of ParseTreeTopDownWalker.
- #is_ident?(event) ⇒ Boolean
- #keep_walking(*args) ⇒ Object
- #local_variable_access(scanner_event) ⇒ Object
- #local_variable_assignment(scanner_event) ⇒ Object
- #local_variable_defined?(name) ⇒ Boolean
- #local_variable_scope_id(name) ⇒ Object
- #merge_tags(scanner_event, tags) ⇒ Object
- #method_call(scanner_event, receiver, *args) ⇒ Object
- #on_assign(lhs, rhs) ⇒ Object
- #on_brace_block(params, body) ⇒ Object
-
#on_call(receiver, dot, method) ⇒ Object
[:call, [:var_ref, :ident=>“subclasses”].
- #on_class(name, ancestor, body) ⇒ Object
- #on_command(operation, command_args) ⇒ Object
-
#on_command_call(receiver, dot, method, args) ⇒ Object
primary_value => anything operation2 : tIDENTIFIER | tCONSTANT | tFID | op ; command_args => anything.
- #on_const_path_ref(primary_value, name) ⇒ Object
- #on_const_ref(const_ref_event) ⇒ Object
- #on_def(name, params, body) ⇒ Object
- #on_default(type, event_args) ⇒ Object
- #on_defs(target, period, name, params, body) ⇒ Object
- #on_do_block(params, body) ⇒ Object
- #on_fcall(operation) ⇒ Object
- #on_massign(mlhs, mrhs) ⇒ Object
- #on_method_add_arg(method, arguments) ⇒ Object
- #on_module(name, body) ⇒ Object
- #on_program(body) ⇒ Object
- #on_sclass(target, body) ⇒ Object
- #on_var_ref(ref_event) ⇒ Object
- #parser_event?(event) ⇒ Boolean
- #parser_events?(events) ⇒ Boolean
- #pop_dynamic_variables_context ⇒ Object
- #pop_local_variables_context ⇒ Object
- #pop_namespace_scope ⇒ Object
- #push_dynamic_variables_context ⇒ Object
- #push_local_variables_context ⇒ Object
- #push_namespace_instance_scope ⇒ Object
- #push_namespace_scope(namespace_name) ⇒ Object
- #resolve_argument(argument) ⇒ Object
- #resolve_arguments(arguments) ⇒ Object
- #resolve_constant_ref(events) ⇒ Object
- #resolve_receiver(receiver) ⇒ Object
- #scanner_event?(event) ⇒ Boolean
- #variable_id(scanner_event) ⇒ Object
- #variables_scope ⇒ Object
-
#variables_scope_id ⇒ Object
TODO crazy, replace that with something more appropriate.
- #walk(event = @events_tree) ⇒ Object
Constructor Details
#initialize(events_tree) ⇒ ParseTreeTopDownWalker
Returns a new instance of ParseTreeTopDownWalker.
6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 6 def initialize(events_tree) @events_tree = events_tree @local_variables = [] @dynamic_variables = [] @variables_scope_id = 1 VariablesScope.reset_guid # empty - top level # ["Module", "Class"] - class Class in module Module, class dev level # ["Module", "Class", :instance] - class Class in module Module, instance level @scope = [] # @scope.last - is basically "implicit self" # also used for constant lookup end |
Instance Method Details
#add_local_variable(scanner_event) ⇒ Object
35 36 37 38 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 35 def add_local_variable(scanner_event) current_variables_scope.merge!(scanner_event[:ident] => scanner_event) local_variable_assignment(scanner_event) end |
#add_local_variables_from_lhs(lhs_event) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 59 def add_local_variables_from_lhs(lhs_event) # [:var_field, {:ident=>"v1"}] if lhs_event[0] == :var_field if (scanner_event = lhs_event[1]).is_a?(Hash) if scanner_event[:ident] add_local_variable(scanner_event) end end end # raise "don't know how to add local variables from lhs_event : #{ lhs_event }" end |
#add_local_variables_from_mlhs(mlhs_event) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 71 def add_local_variables_from_mlhs(mlhs_event) # [{:ident=>"a"}, {:ident=>"b"}], if mlhs_event.is_a?(Array) if mlhs_event[0] == :mlhs_add_star add_local_variables_from_mlhs(mlhs_event[1]) add_local_variable(mlhs_event[2]) if mlhs_event[2][:ident] add_local_variables_from_mlhs(mlhs_event[3]) if mlhs_event[3].is_a?(Array) else mlhs_event.each do |event| next unless scanner_event?(event) if event[:ident] add_local_variable(event) end end end end # raise "don't know how to add local variables from lhs_event : #{ lhs_event }" end |
#add_local_variables_from_params_event(params_event) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 90 def add_local_variables_from_params_event(params_event) return unless params_event params_event = params_event[1] if params_event[0] == :paren # ? found = false if = params_event[2] .each do |optional_argument| if scanner_event?(event = optional_argument[0]) add_local_variable(event) keep_walking(optional_argument[1..-1]) end end end for scanner_event in extract_scanner_events_from_tree(params_event.values_at(1,3,4,5)) if scanner_event[:ident] found = true add_local_variable(scanner_event) end end # raise "don't know how to add local variables from params_event: #{ params_event }" unless found end |
#constant_access(scanner_event, name = nil) ⇒ Object
414 415 416 417 418 419 420 421 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 414 def constant_access(scanner_event, name = nil) unless name name = scanner_event[:const] end (scanner_event, :constant_access => { :name => name, :scope => constant_access_scope }) end |
#constant_access_scope ⇒ Object
408 409 410 411 412 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 408 def constant_access_scope scope = @scope.dup scope.pop if scope.last == :instance # class/instance doesn't matter for constant lookup scope end |
#current_variables_scope ⇒ Object
31 32 33 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 31 def current_variables_scope (@dynamic_variables.size > 0 ? @dynamic_variables : @local_variables).last end |
#extract_scanner_events_from_tree(tree) ⇒ Object
113 114 115 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 113 def extract_scanner_events_from_tree(tree) tree.flatten.select { |e| scanner_event?(e) } end |
#is_ident?(event) ⇒ Boolean
449 450 451 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 449 def is_ident?(event) scanner_event?(event) && event[:ident] end |
#keep_walking(*args) ⇒ Object
168 169 170 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 168 def keep_walking(*args) on_default(nil, args) end |
#local_variable_access(scanner_event) ⇒ Object
402 403 404 405 406 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 402 def local_variable_access(scanner_event) existing_variable_id = "#{ local_variable_scope_id(scanner_event[:ident]) }:#{ scanner_event[:ident] }" (scanner_event, :local_variable_access => existing_variable_id ) end |
#local_variable_assignment(scanner_event) ⇒ Object
423 424 425 426 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 423 def local_variable_assignment(scanner_event) (scanner_event, :local_variable_assignment => variable_id(scanner_event)) end |
#local_variable_defined?(name) ⇒ Boolean
51 52 53 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 51 def local_variable_defined?(name) variables_scope.any? { |variables| variables.has_key?(name) } end |
#local_variable_scope_id(name) ⇒ Object
55 56 57 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 55 def local_variable_scope_id(name) variables_scope.detect { |variables| variables.has_key?(name) }.guid end |
#merge_tags(scanner_event, tags) ⇒ Object
432 433 434 435 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 432 def (scanner_event, ) scanner_event[:tags] ||= {} scanner_event[:tags].merge!() end |
#method_call(scanner_event, receiver, *args) ⇒ Object
390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 390 def method_call(scanner_event, receiver, *args) unless receiver # implict self concept (will be fetched from constant_access_scope) receiver = @scope.last == :instance ? :instance : :class end (scanner_event, {:method_call => {:name => scanner_event[:ident], :receiver => receiver, :scope => constant_access_scope}}) end |
#on_assign(lhs, rhs) ⇒ Object
235 236 237 238 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 235 def on_assign(lhs, rhs) add_local_variables_from_lhs(lhs) keep_walking(rhs) end |
#on_brace_block(params, body) ⇒ Object
228 229 230 231 232 233 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 228 def on_brace_block(params, body) push_dynamic_variables_context add_local_variables_from_params_event(params) keep_walking(body) pop_dynamic_variables_context end |
#on_call(receiver, dot, method) ⇒ Object
[:call,
[:var_ref, {:ident=>"subclasses"}]
338 339 340 341 342 343 344 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 338 def on_call(receiver, dot, method) if rcv = resolve_receiver(receiver) method_call(method, [rcv]) end keep_walking(receiver) end |
#on_class(name, ancestor, body) ⇒ Object
195 196 197 198 199 200 201 202 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 195 def on_class(name, ancestor, body) keep_walking(name, ancestor) push_local_variables_context push_namespace_scope(resolve_constant_ref(name)) keep_walking(body) pop_namespace_scope pop_local_variables_context end |
#on_command(operation, command_args) ⇒ Object
245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 245 def on_command(operation, command_args) method_call(operation, nil) if is_ident?(operation) if operation[:ident] == "autoload" && (arguments = resolve_arguments(command_args)) if [:const, :tstring_content].include?(arguments[0].keys.first) constant_access(arguments[0], arguments[0].values.first) end end keep_walking(command_args) end |
#on_command_call(receiver, dot, method, args) ⇒ Object
primary_value => anything operation2 : tIDENTIFIER | tCONSTANT | tFID | op ; command_args => anything
309 310 311 312 313 314 315 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 309 def on_command_call(receiver, dot, method, args) if is_ident?(method) && (constant = resolve_constant_ref(receiver)) method_call(method, [constant]) end keep_walking(receiver, args) end |
#on_const_path_ref(primary_value, name) ⇒ Object
382 383 384 385 386 387 388 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 382 def on_const_path_ref(primary_value, name) keep_walking(primary_value) if (constant = resolve_constant_ref(primary_value)) && scanner_event?(name) && name[:const] constant_access(name, "#{ constant }::#{ name[:const] }") end end |
#on_const_ref(const_ref_event) ⇒ Object
374 375 376 377 378 379 380 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 374 def on_const_ref(const_ref_event) if scanner_event?(const_ref_event) if const_ref_event[:const] constant_access(const_ref_event) end end end |
#on_def(name, params, body) ⇒ Object
178 179 180 181 182 183 184 185 186 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 178 def on_def(name, params, body) push_local_variables_context # TODO this is hack :( push_namespace_instance_scope unless @in_singleton_class_defition add_local_variables_from_params_event(params) keep_walking(body) pop_namespace_scope unless @in_singleton_class_defition pop_local_variables_context end |
#on_default(type, event_args) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 142 def on_default(type, event_args) return unless event_args # no-args event event_args.each do |arg| if arg == nil # empty arg - pass # why the following isn't reported with scanner events? elsif type == :call && [:".", :"::"].include?(arg) elsif type == :var_ref && [:".", :"::"].include?(arg) elsif type == :field && [:".", :"::"].include?(arg) elsif type == :command_call && ([:".", :"::"].include?(arg) || arg == false) elsif type == :args_add_block && arg == false elsif type == :unary && arg.is_a?(Symbol) elsif type == :binary && arg.is_a?(Symbol) elsif scanner_event?(arg) # scanner event - pass elsif (parser_events?(arg) rescue r(type, event_args)) arg.each do |event| walk(event) end elsif parser_event?(arg) walk(arg) end end end |
#on_defs(target, period, name, params, body) ⇒ Object
188 189 190 191 192 193 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 188 def on_defs(target, period, name, params, body) push_local_variables_context add_local_variables_from_params_event(params) keep_walking(body) pop_local_variables_context end |
#on_do_block(params, body) ⇒ Object
221 222 223 224 225 226 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 221 def on_do_block(params, body) push_dynamic_variables_context add_local_variables_from_params_event(params) keep_walking(body) pop_dynamic_variables_context end |
#on_fcall(operation) ⇒ Object
284 285 286 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 284 def on_fcall(operation) method_call(operation, nil) if is_ident?(operation) end |
#on_massign(mlhs, mrhs) ⇒ Object
240 241 242 243 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 240 def on_massign(mlhs, mrhs) add_local_variables_from_mlhs(mlhs) keep_walking(mrhs) end |
#on_method_add_arg(method, arguments) ⇒ Object
288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 288 def on_method_add_arg(method, arguments) if method[0] == :fcall && scanner_event?(method[1]) && method[1][:ident] == "autoload" if arguments = resolve_arguments(arguments) if [:const, :tstring_content].include?(arguments[0].keys.first) constant_access(arguments[0], arguments[0].values.first) end end end keep_walking(method, arguments) end |
#on_module(name, body) ⇒ Object
212 213 214 215 216 217 218 219 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 212 def on_module(name, body) keep_walking(name) push_local_variables_context push_namespace_scope(resolve_constant_ref(name)) keep_walking(body) pop_namespace_scope pop_local_variables_context end |
#on_program(body) ⇒ Object
172 173 174 175 176 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 172 def on_program(body) push_local_variables_context keep_walking(body) pop_local_variables_context end |
#on_sclass(target, body) ⇒ Object
204 205 206 207 208 209 210 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 204 def on_sclass(target, body) push_local_variables_context @in_singleton_class_defition = true keep_walking(body) @in_singleton_class_defition = false pop_local_variables_context end |
#on_var_ref(ref_event) ⇒ Object
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 346 def on_var_ref(ref_event) # [:var_ref, {:kw=>"false"}] # [:var_ref, {:cvar=>"@@subclasses"}] # [:var_ref, {:ident=>"child"}] (sic!) # [:binary, # [:var_ref, {:ident=>"nonreloadables"}], # :<<, # [:var_ref, {:ident=>"klass"}] # #[:call, # [:var_ref, {:ident=>"klass"}], # :".", # {:ident=>"instance_variables"}], # # if scanner_event?(ref_event) if ref_event[:ident] if local_variable_defined?(ref_event[:ident]) local_variable_access(ref_event) else method_call(ref_event, nil) end elsif ref_event[:const] constant_access(ref_event) end end end |
#parser_event?(event) ⇒ Boolean
441 442 443 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 441 def parser_event?(event) event.is_a?(Array) && event[0].is_a?(Symbol) end |
#parser_events?(events) ⇒ Boolean
445 446 447 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 445 def parser_events?(events) events.all? { |event| parser_event?(event) } end |
#pop_dynamic_variables_context ⇒ Object
129 130 131 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 129 def pop_dynamic_variables_context @dynamic_variables.pop end |
#pop_local_variables_context ⇒ Object
121 122 123 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 121 def pop_local_variables_context @local_variables.pop end |
#pop_namespace_scope ⇒ Object
27 28 29 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 27 def pop_namespace_scope @scope.pop end |
#push_dynamic_variables_context ⇒ Object
125 126 127 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 125 def push_dynamic_variables_context @dynamic_variables << VariablesScope.new end |
#push_local_variables_context ⇒ Object
117 118 119 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 117 def push_local_variables_context @local_variables << VariablesScope.new end |
#push_namespace_instance_scope ⇒ Object
23 24 25 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 23 def push_namespace_instance_scope @scope << :instance end |
#push_namespace_scope(namespace_name) ⇒ Object
19 20 21 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 19 def push_namespace_scope(namespace_name) @scope << namespace_name end |
#resolve_argument(argument) ⇒ Object
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 266 def resolve_argument(argument) if argument[0] == :symbol_literal # [:symbol_literal, [:symbol, {:const=>"C"}]] if argument[1].is_a?(Array) if argument[1][0] == :symbol argument[1][1] # {:const=>"C"} end end elsif argument[0] == :string_literal # [:string_literal, [:string_content, {:tstring_content=>"C"}]] if argument[1].is_a?(Array) if argument[1][0] == :string_content argument[1][1] # {:tstring_content=>"C"} end end end end |
#resolve_arguments(arguments) ⇒ Object
257 258 259 260 261 262 263 264 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 257 def resolve_arguments(arguments) arguments = arguments[1] if arguments[0] == :arg_paren if arguments[0] == :args_add_block if arguments[1].is_a?(Array) arguments[1].map { |a| resolve_argument(a) } end end end |
#resolve_constant_ref(events) ⇒ Object
317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 317 def resolve_constant_ref(events) if events[0] == :var_ref || events[0] == :const_ref && scanner_event?(events[1]) && events[1][:const] events[1][:const] elsif events[0] == :top_const_ref && scanner_event?(events[1]) && events[1][:const] "::" + events[1][:const] elsif events[0] == :const_path_ref && (constant = resolve_constant_ref(events[1])) "#{ constant }::#{ events[2][:const] }" end end |
#resolve_receiver(receiver) ⇒ Object
332 333 334 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 332 def resolve_receiver(receiver) resolve_constant_ref(receiver) end |
#scanner_event?(event) ⇒ Boolean
437 438 439 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 437 def scanner_event?(event) event.is_a?(Hash) end |
#variable_id(scanner_event) ⇒ Object
428 429 430 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 428 def variable_id(scanner_event) "#{ variables_scope_id }:#{ scanner_event[:ident] }" end |
#variables_scope ⇒ Object
40 41 42 43 44 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 40 def variables_scope merged_scope = @dynamic_variables.dup.reverse merged_scope << @local_variables.last if @local_variables.size > 0 merged_scope end |
#variables_scope_id ⇒ Object
TODO crazy, replace that with something more appropriate
47 48 49 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 47 def variables_scope_id current_variables_scope.guid end |
#walk(event = @events_tree) ⇒ Object
133 134 135 136 137 138 139 140 |
# File 'lib/reflexive/parse_tree_top_down_walker.rb', line 133 def walk(event = @events_tree) type, *args = event if respond_to?("on_#{ type }") send("on_#{ type }", *args) #rescue r($!, event) else on_default(type, args) end end |