reflexive 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,392 @@
1
+ require "zlib"
2
+ require "reflexive/variables_scope"
3
+
4
+ module Reflexive
5
+ class ParseTreeTopDownWalker
6
+ def initialize(events_tree)
7
+ @events_tree = events_tree
8
+ @local_variables = []
9
+ @dynamic_variables = []
10
+ @variables_scope_id = 1
11
+ VariablesScope.reset_guid
12
+ # empty - top level
13
+ # ["Module", "Class"] - class Class in module Module, class dev level
14
+ # ["Module", "Class", :instance] - class Class in module Module, instance level
15
+ @scope = [] # @scope.last - is basically "implicit self"
16
+ # also used for constant lookup
17
+ end
18
+
19
+ def push_namespace_scope(namespace_name)
20
+ @scope << namespace_name
21
+ end
22
+
23
+ def push_namespace_instance_scope
24
+ @scope << :instance
25
+ end
26
+
27
+ def pop_namespace_scope
28
+ @scope.pop
29
+ end
30
+
31
+ def current_variables_scope
32
+ (@dynamic_variables.size > 0 ? @dynamic_variables : @local_variables).last
33
+ end
34
+
35
+ def add_local_variable(scanner_event)
36
+ current_variables_scope.merge!(scanner_event[:ident] => scanner_event)
37
+ local_variable_assignment(scanner_event)
38
+ end
39
+
40
+ def variables_scope
41
+ merged_scope = @dynamic_variables.dup.reverse
42
+ merged_scope << @local_variables.last if @local_variables.size > 0
43
+ merged_scope
44
+ end
45
+
46
+ # TODO crazy, replace that with something more appropriate
47
+ def variables_scope_id
48
+ current_variables_scope.guid
49
+ end
50
+
51
+ def local_variable_defined?(name)
52
+ variables_scope.any? { |variables| variables.has_key?(name) }
53
+ end
54
+
55
+ def local_variable_scope_id(name)
56
+ variables_scope.detect { |variables| variables.has_key?(name) }.guid
57
+ end
58
+
59
+ def add_local_variables_from_lhs(lhs_event)
60
+ # [:var_field, {:ident=>"v1"}]
61
+ if lhs_event[0] == :var_field
62
+ if (scanner_event = lhs_event[1]).is_a?(Hash)
63
+ if scanner_event[:ident]
64
+ add_local_variable(scanner_event)
65
+ end
66
+ end
67
+ end
68
+ # raise "don't know how to add local variables from lhs_event : #{ lhs_event }"
69
+ end
70
+
71
+ def add_local_variables_from_mlhs(mlhs_event)
72
+ # [{:ident=>"a"}, {:ident=>"b"}],
73
+ if mlhs_event.is_a?(Array)
74
+ if mlhs_event[0] == :mlhs_add_star
75
+ add_local_variables_from_mlhs(mlhs_event[1])
76
+ add_local_variable(mlhs_event[2]) if mlhs_event[2][:ident]
77
+ add_local_variables_from_mlhs(mlhs_event[3]) if mlhs_event[3].is_a?(Array)
78
+ else
79
+ mlhs_event.each do |event|
80
+ next unless scanner_event?(event)
81
+ if event[:ident]
82
+ add_local_variable(event)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ # raise "don't know how to add local variables from lhs_event : #{ lhs_event }"
88
+ end
89
+
90
+ def add_local_variables_from_params_event(params_event)
91
+ return unless params_event
92
+ params_event = params_event[1] if params_event[0] == :paren # ?
93
+ found = false
94
+ for scanner_event in extract_scanner_events_from_tree(params_event)
95
+ if scanner_event[:ident]
96
+ found = true
97
+ add_local_variable(scanner_event)
98
+ end
99
+ end
100
+ # raise "don't know how to add local variables from params_event: #{ params_event }" unless found
101
+ end
102
+
103
+ def extract_scanner_events_from_tree(tree)
104
+ tree.flatten.select { |e| scanner_event?(e) }
105
+ end
106
+
107
+ def push_local_variables_context
108
+ @local_variables << VariablesScope.new
109
+ end
110
+
111
+ def pop_local_variables_context
112
+ @local_variables.pop
113
+ end
114
+
115
+ def push_dynamic_variables_context
116
+ @dynamic_variables << VariablesScope.new
117
+ end
118
+
119
+ def pop_dynamic_variables_context
120
+ @dynamic_variables.pop
121
+ end
122
+
123
+ def walk(event = @events_tree)
124
+ type, *args = event
125
+ if respond_to?("on_#{ type }")
126
+ send("on_#{ type }", *args) #rescue r($!, event)
127
+ else
128
+ on_default(type, args)
129
+ end
130
+ end
131
+
132
+ def on_default(type, event_args)
133
+ return unless event_args # no-args event
134
+ event_args.each do |arg|
135
+ if arg == nil
136
+ # empty arg - pass
137
+
138
+ # why the following isn't reported with scanner events?
139
+ elsif type == :call && [:".", :"::"].include?(arg)
140
+ elsif type == :var_ref && [:".", :"::"].include?(arg)
141
+ elsif type == :field && [:".", :"::"].include?(arg)
142
+ elsif type == :command_call && ([:".", :"::"].include?(arg) || arg == false)
143
+ elsif type == :args_add_block && arg == false
144
+ elsif type == :unary && arg.is_a?(Symbol)
145
+ elsif type == :binary && arg.is_a?(Symbol)
146
+ elsif scanner_event?(arg)
147
+ # scanner event - pass
148
+ elsif (parser_events?(arg) rescue r(type, event_args))
149
+ arg.each do |event|
150
+ walk(event)
151
+ end
152
+ elsif parser_event?(arg)
153
+ walk(arg)
154
+ end
155
+ end
156
+ end
157
+
158
+ def keep_walking(*args)
159
+ on_default(nil, args)
160
+ end
161
+
162
+ def on_program(body)
163
+ push_local_variables_context
164
+ keep_walking(body)
165
+ pop_local_variables_context
166
+ end
167
+
168
+ def on_def(name, params, body)
169
+ push_local_variables_context
170
+ add_local_variables_from_params_event(params)
171
+ push_namespace_instance_scope
172
+ keep_walking(body)
173
+ pop_namespace_scope
174
+ pop_local_variables_context
175
+ end
176
+
177
+ def on_defs(target, period, name, params, body)
178
+ push_local_variables_context
179
+ add_local_variables_from_params_event(params)
180
+ keep_walking(body)
181
+ pop_local_variables_context
182
+ end
183
+
184
+ def on_class(name, ancestor, body)
185
+ keep_walking(name)
186
+ push_local_variables_context
187
+ push_namespace_scope(resolve_constant_ref(name))
188
+ keep_walking(body)
189
+ pop_namespace_scope
190
+ pop_local_variables_context
191
+ end
192
+
193
+ def on_sclass(target, body)
194
+ push_local_variables_context
195
+ keep_walking(body)
196
+ pop_local_variables_context
197
+ end
198
+
199
+ def on_module(name, body)
200
+ keep_walking(name)
201
+ push_local_variables_context
202
+ push_namespace_scope(resolve_constant_ref(name))
203
+ keep_walking(body)
204
+ pop_namespace_scope
205
+ pop_local_variables_context
206
+ end
207
+
208
+ def on_do_block(params, body)
209
+ push_dynamic_variables_context
210
+ add_local_variables_from_params_event(params)
211
+ keep_walking(body)
212
+ pop_dynamic_variables_context
213
+ end
214
+
215
+ def on_brace_block(params, body)
216
+ push_dynamic_variables_context
217
+ add_local_variables_from_params_event(params)
218
+ keep_walking(body)
219
+ pop_dynamic_variables_context
220
+ end
221
+
222
+ def on_assign(lhs, rhs)
223
+ add_local_variables_from_lhs(lhs)
224
+ keep_walking(rhs)
225
+ end
226
+
227
+ def on_massign(mlhs, mrhs)
228
+ add_local_variables_from_mlhs(mlhs)
229
+ keep_walking(mrhs)
230
+ end
231
+
232
+ def on_command(operation, command_args)
233
+ method_call(operation, nil) if is_ident?(operation)
234
+ keep_walking(command_args)
235
+ end
236
+
237
+ def on_fcall(operation)
238
+ method_call(operation, nil) if is_ident?(operation)
239
+ end
240
+
241
+ # primary_value => anything
242
+ # operation2 : tIDENTIFIER
243
+ # | tCONSTANT
244
+ # | tFID
245
+ # | op
246
+ # ;
247
+ # command_args => anything
248
+ def on_command_call(receiver, dot, method, args)
249
+ if is_ident?(method) &&
250
+ (constant = resolve_constant_ref(receiver))
251
+ method_call(method, [constant])
252
+ end
253
+ keep_walking(receiver, args)
254
+ end
255
+
256
+ def resolve_constant_ref(events)
257
+ if events[0] == :var_ref || events[0] == :const_ref &&
258
+ scanner_event?(events[1]) &&
259
+ events[1][:const]
260
+ events[1][:const]
261
+ elsif events[0] == :top_const_ref &&
262
+ scanner_event?(events[1]) &&
263
+ events[1][:const]
264
+ "::" + events[1][:const]
265
+ elsif events[0] == :const_path_ref &&
266
+ (constant = resolve_constant_ref(events[1]))
267
+ "#{ constant }::#{ events[2][:const] }"
268
+ end
269
+ end
270
+
271
+ def resolve_receiver(receiver)
272
+ resolve_constant_ref(receiver)
273
+ end
274
+
275
+ # [:call,
276
+ # [:var_ref, {:ident=>"subclasses"}]
277
+ def on_call(receiver, dot, method)
278
+ if rcv = resolve_receiver(receiver)
279
+ method_call(method, [rcv])
280
+ else
281
+ keep_walking(receiver)
282
+ end
283
+ end
284
+
285
+ def on_var_ref(ref_event)
286
+ # [:var_ref, {:kw=>"false"}]
287
+ # [:var_ref, {:cvar=>"@@subclasses"}]
288
+ # [:var_ref, {:ident=>"child"}] (sic!)
289
+ # [:binary,
290
+ # [:var_ref, {:ident=>"nonreloadables"}],
291
+ # :<<,
292
+ # [:var_ref, {:ident=>"klass"}]
293
+ #
294
+ #[:call,
295
+ # [:var_ref, {:ident=>"klass"}],
296
+ # :".",
297
+ # {:ident=>"instance_variables"}],
298
+ #
299
+ #
300
+ if scanner_event?(ref_event)
301
+ if ref_event[:ident]
302
+ if local_variable_defined?(ref_event[:ident])
303
+ local_variable_access(ref_event)
304
+ else
305
+ method_call(ref_event, nil)
306
+ end
307
+ elsif ref_event[:const]
308
+ constant_access(ref_event)
309
+ end
310
+ end
311
+ end
312
+
313
+ def on_const_ref(const_ref_event)
314
+ if scanner_event?(const_ref_event)
315
+ if const_ref_event[:const]
316
+ constant_access(const_ref_event)
317
+ end
318
+ end
319
+ end
320
+
321
+ def on_const_path_ref(primary_value, name)
322
+ keep_walking(primary_value)
323
+ if (constant = resolve_constant_ref(primary_value)) &&
324
+ scanner_event?(name) && name[:const]
325
+ constant_access(name, "#{ constant }::#{ name[:const] }")
326
+ end
327
+ end
328
+
329
+ def method_call(scanner_event, receiver, *args)
330
+ unless receiver
331
+ # implict self concept
332
+ receiver = @scope.dup
333
+ end
334
+ merge_tags(scanner_event,
335
+ {:method_call =>
336
+ {:name => scanner_event[:ident],
337
+ :receiver => receiver,
338
+ :scope => constant_access_scope}})
339
+ end
340
+
341
+ def local_variable_access(scanner_event)
342
+ existing_variable_id = "#{ local_variable_scope_id(scanner_event[:ident]) }:#{ scanner_event[:ident] }"
343
+ merge_tags(scanner_event,
344
+ :local_variable_access => existing_variable_id )
345
+ end
346
+
347
+ def constant_access_scope
348
+ scope = @scope.dup
349
+ scope.pop if scope.last == :instance # class/instance doesn't matter for constant lookup
350
+ scope
351
+ end
352
+
353
+ def constant_access(scanner_event, name = nil)
354
+ unless name
355
+ name = scanner_event[:const]
356
+ end
357
+ merge_tags(scanner_event,
358
+ :constant_access => { :name => name,
359
+ :scope => constant_access_scope })
360
+ end
361
+
362
+ def local_variable_assignment(scanner_event)
363
+ merge_tags(scanner_event,
364
+ :local_variable_assignment => variable_id(scanner_event))
365
+ end
366
+
367
+ def variable_id(scanner_event)
368
+ "#{ variables_scope_id }:#{ scanner_event[:ident] }"
369
+ end
370
+
371
+ def merge_tags(scanner_event, tags)
372
+ scanner_event[:tags] ||= {}
373
+ scanner_event[:tags].merge!(tags)
374
+ end
375
+
376
+ def scanner_event?(event)
377
+ event.is_a?(Hash)
378
+ end
379
+
380
+ def parser_event?(event)
381
+ event.is_a?(Array) && event[0].is_a?(Symbol)
382
+ end
383
+
384
+ def parser_events?(events)
385
+ events.all? { |event| parser_event?(event) }
386
+ end
387
+
388
+ def is_ident?(event)
389
+ scanner_event?(event) && event[:ident]
390
+ end
391
+ end
392
+ end
@@ -0,0 +1,274 @@
1
+ require "ripper"
2
+ require "reflexive/parse_tree_top_down_walker"
3
+
4
+ module Reflexive
5
+ # Records all scanner events, injects meta-events when scope
6
+ # changes (required to implement constant/method look-up)
7
+ class ReflexiveRipper < Ripper
8
+ attr_accessor :scanner_events
9
+
10
+ META_EVENT = [ :meta_scope ].freeze
11
+
12
+ def initialize(*args)
13
+ super
14
+ @scanner_events = []
15
+ @await_scope_change = false
16
+ @scope = []
17
+ @new_scope = nil
18
+ end
19
+
20
+ def parse
21
+ parse_tree = super
22
+ require 'pp'
23
+ # pp parse_tree
24
+
25
+ ParseTreeTopDownWalker.new(parse_tree).walk
26
+ parse_tree
27
+ end
28
+
29
+ # Returns array in format: [event_value, event_name, tags]
30
+ def self.destruct_scanner_event(scanner_event)
31
+ tags = scanner_event.delete(:tags)
32
+ [ scanner_event.values.first, scanner_event.keys.first, tags ]
33
+ end
34
+
35
+ def self.is_meta_event?(event)
36
+ META_EVENT.include?(event)
37
+ end
38
+
39
+ Ripper::SCANNER_EVENTS.each do |meth|
40
+ define_method("on_#{ meth }") do |*args|
41
+ result = super(*args)
42
+ @scanner_events << { meth.to_sym => args[0] }
43
+ stop_await_scope_change
44
+ @scanner_events[-1]
45
+ end
46
+ end
47
+
48
+ def stop_await_scope_change
49
+ # get here when any of 3 possible constant reference ends,
50
+ # which means we have new scope in effect
51
+ if @await_scope_change
52
+ if @new_scope
53
+ @scope << @new_scope.dup
54
+ inject_current_scope
55
+ @new_scope = nil
56
+ end
57
+ @await_scope_change = false
58
+ end
59
+ end
60
+
61
+ def inject_current_scope
62
+ @scanner_events << { :meta_scope => @scope.dup }
63
+ end
64
+
65
+ PARSER_EVENT_TABLE.each do |event, arity|
66
+ if /_new\z/ =~ event.to_s and arity == 0
67
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
68
+ def on_#{event}
69
+ []
70
+ end
71
+ End
72
+ elsif /_add\z/ =~ event.to_s
73
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
74
+ def on_#{event}(list, item)
75
+ list.push item
76
+ list
77
+ end
78
+ End
79
+ else
80
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
81
+ def on_#{event}(*args)
82
+ [:#{event}, *args]
83
+ end
84
+ End
85
+ end
86
+ end
87
+
88
+ # Two parser events which fire when class/module definition ends
89
+ def on_class(*args)
90
+ @scope.pop
91
+ inject_current_scope
92
+
93
+ [:class, *args]
94
+ end
95
+
96
+ def on_module(*args)
97
+ @scope.pop
98
+ inject_current_scope
99
+
100
+ [:module, *args]
101
+ end
102
+
103
+ def on_parse_error(*args)
104
+ raise SyntaxError, "#{ lineno }: #{ args[0] }"
105
+ end
106
+
107
+ # def on_call(*args)
108
+ # # puts "PARSE: on_call: #{ args.inspect }"
109
+ # if args.size == 3 &&
110
+ # (constant = resolve_constant_ref(args[0])) &&
111
+ # args[1] == :"." &&
112
+ # args[2][1] == :ident
113
+ #
114
+ # meth_ident_token = args[2]
115
+ # index = token_index(meth_ident_token)
116
+ # tags = { :constant => constant, :method => args[2][0] }
117
+ # # wrap the method identifier token in :method_call meta tokens
118
+ # @scanner_events[index .. index] = [
119
+ # # [ :method_call, :open, tags ],
120
+ # meth_ident_token,
121
+ # # [ :method_call, :close ]
122
+ # ]
123
+ # end
124
+ # [:call, *args]
125
+ # end
126
+ #
127
+ # def on_var_ref(*args)
128
+ # # puts "PARSER: var_ref: #{ args.inspect }"
129
+ # if args.size == 1 &&
130
+ # args[0].size == 2 &&
131
+ # args[0][1] == :ident
132
+ # meth_ident_token = args[0]
133
+ # index = token_index(meth_ident_token)
134
+ # tags = { :instance_method => args[0][0] }
135
+ # @scanner_events[index .. index] = [
136
+ # [ :method_call, :open, tags ],
137
+ # meth_ident_token,
138
+ # [ :method_call, :close ]
139
+ # ]
140
+ # # r @scanner_events[177]
141
+ # end
142
+ # [:var_ref, *args]
143
+ # end
144
+
145
+ def token_index(token)
146
+ @scanner_events.index do |t|
147
+ t.object_id == token.object_id
148
+ end
149
+ end
150
+
151
+ def resolve_constant_ref(tokens)
152
+ self.class.resolve_constant_ref(tokens)
153
+ end
154
+
155
+ def self.resolve_constant_ref(tokens)
156
+ if tokens[0] == :var_ref && tokens[1][1] == :const
157
+ # [:var_ref, ["B", :const]]
158
+ tokens[1][0]
159
+ elsif tokens[0] == :top_const_ref && tokens[1][1] == :const
160
+ # [:top_const_ref, ["A", :const]]
161
+ "::" + tokens[1][0]
162
+ # [:const_path_ref, [:top_const_ref, ["A", :const]], ["B", :const]]
163
+ # [:const_path_ref, [:var_ref, ["A", :const]], ["B", :const]]
164
+ elsif tokens[0] == :const_path_ref && (constant = resolve_constant_ref(tokens[1]))
165
+ "#{ constant }::#{ tokens[2][0] }"
166
+ end
167
+ end
168
+
169
+ def on_sp(*args)
170
+ @scanner_events << { :sp => args[0] }
171
+ # ignore space tokens when waiting for scope changes:
172
+ # stop_await_scope_change
173
+
174
+ @scanner_events[-1]
175
+ end
176
+
177
+ # parse.y:
178
+ #
179
+ # | k_class cpath superclass
180
+ # | k_module cpath
181
+ #
182
+ # matches "class"
183
+ def on_kw(kw)
184
+ if %w(class module).include?(kw)
185
+ @await_scope_change = "k_class"
186
+ else
187
+ stop_await_scope_change
188
+ end
189
+
190
+ @scanner_events << { :kw => kw }
191
+
192
+ @scanner_events[-1]
193
+ end
194
+
195
+ # matches "::" in two cases
196
+ # 1. "class ::TopLevel",
197
+ # 2. "class Nested::Class"
198
+ def on_op(token)
199
+ if @await_scope_change == "k_class" &&
200
+ token == "::"
201
+ # tCOLON2, tCOLON3
202
+ # cpath : tCOLON3 cname
203
+ # {
204
+ # /*%%%*/
205
+ # $$ = NEW_COLON3($2);
206
+ # /*%
207
+ # $$ = dispatch1(top_const_ref, $2);
208
+ # %*/
209
+ # }
210
+ @await_scope_change = "k_class tCOLON3"
211
+ elsif @await_scope_change == "k_class primary_value tCOLON2" &&
212
+ token == "::" # tCOLON2, tCOLON3
213
+ @new_scope << "::" # append to the last defined scope
214
+ else
215
+ stop_await_scope_change
216
+ end
217
+
218
+ @scanner_events << { :op => token }
219
+
220
+ @scanner_events[-1]
221
+ end
222
+
223
+ # cname : tIDENTIFIER
224
+ # {
225
+ # /*%%%*/
226
+ # yyerror("class/module name must be CONSTANT");
227
+ # /*%
228
+ # $$ = dispatch1(class_name_error, $1);
229
+ # %*/
230
+ # }
231
+ # | tCONSTANT
232
+ # ;
233
+ #
234
+ # "ClassName"
235
+ # or
236
+ # "ClassName::NestedClassName"
237
+ def on_const(const_name)
238
+ if @await_scope_change == "k_class tCOLON3" # "class ::TopClass"
239
+ @new_scope = "::#{ const_name }"
240
+ elsif @await_scope_change == "k_class" # "class NormalClass"
241
+ # | cname
242
+ # {
243
+ # /*%%%*/
244
+ # $$ = NEW_COLON2(0, $$);
245
+ # /*%
246
+ # $$ = dispatch1(const_ref, $1);
247
+ # %*/
248
+ # }
249
+ @new_scope = "#{ const_name }"
250
+ @await_scope_change = "k_class primary_value tCOLON2" # "class Class::NestedClass"
251
+ elsif @await_scope_change == "k_class primary_value tCOLON2"
252
+ #
253
+ # | primary_value tCOLON2 cname
254
+ # {
255
+ # /*%%%*/
256
+ # $$ = NEW_COLON2($1, $3);
257
+ # /*%
258
+ # $$ = dispatch2(const_path_ref, $1, $3);
259
+ # %*/
260
+ # }
261
+ # ;
262
+ #
263
+ # cname : tIDENTIFIER
264
+ @new_scope << "#{ const_name }" # append to the scope
265
+ else
266
+ stop_await_scope_change
267
+ end
268
+
269
+ @scanner_events << { :const => const_name }
270
+
271
+ @scanner_events[-1]
272
+ end
273
+ end
274
+ end