ParseTree 3.0.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.add_include_dirs("../../RubyInline/dev/lib",
7
+ "../../sexp_processor/dev/lib",
8
+ "../../ZenTest/dev/lib",
9
+ "lib")
10
+
11
+ require './lib/parse_tree.rb'
12
+
13
+ Hoe.new("ParseTree", ParseTree::VERSION) do |pt|
14
+ pt.rubyforge_name = "parsetree"
15
+
16
+ pt.developer('Ryan Davis', 'ryand-ruby@zenspider.com')
17
+
18
+ pt.clean_globs << File.expand_path("~/.ruby_inline")
19
+ pt.extra_deps << ['RubyInline', '>= 3.7.0']
20
+ pt.extra_deps << ['sexp_processor', '>= 3.0.0']
21
+ pt.spec_extras[:require_paths] = proc { |paths| paths << 'test' }
22
+
23
+ pt.multiruby_skip << "mri_rel_1_9" << "rubinius" << "mri_trunk"
24
+ end
25
+
26
+ desc 'Run in gdb'
27
+ task :debug do
28
+ puts "RUN: r -d #{Hoe::RUBY_FLAGS} test/test_all.rb #{Hoe::FILTER}"
29
+ sh "gdb ~/.multiruby/install/19/bin/ruby"
30
+ end
31
+
32
+ desc 'Run a very basic demo'
33
+ task :demo do
34
+ sh "echo 1+1 | ruby #{Hoe::RUBY_FLAGS} ./bin/parse_tree_show -f"
35
+ end
@@ -0,0 +1,89 @@
1
+ #!/usr/local/bin/ruby -ws
2
+
3
+ # ABC metric
4
+ #
5
+ # Assignments, Branches, and Calls
6
+ #
7
+ # A simple way to measure the complexity of a function or method.
8
+
9
+ if defined? $I and String === $I then
10
+ $I.split(/:/).each do |dir|
11
+ $: << dir
12
+ end
13
+ end
14
+
15
+ PARSE_TREE_ABC=true
16
+
17
+ begin require 'rubygems' rescue LoadError end
18
+ require 'sexp'
19
+ require 'parse_tree'
20
+ require 'sexp_processor'
21
+
22
+ old_classes = []
23
+ ObjectSpace.each_object(Module) do |klass|
24
+ old_classes << klass
25
+ end
26
+
27
+ ARGV.each do |name|
28
+ begin
29
+ require name
30
+ rescue NameError => err
31
+ $stderr.puts "ERROR requiring #{name}. Perhaps you need to add some -I's?\n\n#{err}"
32
+ end
33
+ end
34
+
35
+ new_classes = []
36
+ ObjectSpace.each_object(Module) do |klass|
37
+ new_classes << klass
38
+ end
39
+
40
+ score = {}
41
+
42
+ new_classes -= old_classes
43
+
44
+ klasses = Sexp.from_array(ParseTree.new.parse_tree(*new_classes))
45
+ klasses.each do |klass|
46
+ klass.shift # :class
47
+ klassname = klass.shift
48
+ klass.shift # superclass
49
+ methods = klass
50
+
51
+ methods.each do |defn|
52
+ a=b=c=0
53
+ defn.shift
54
+ methodname = defn.shift
55
+ tokens = defn.structure.flatten
56
+ tokens.each do |token|
57
+ case token
58
+ when :attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn then
59
+ a += 1
60
+ when :and, :case, :else, :if, :iter, :or, :rescue, :until, :when, :while then
61
+ b += 1
62
+ when :call, :fcall, :super, :vcall, :yield then
63
+ c += 1
64
+ when :args, :argscat, :array, :begin, :block, :block_arg, :block_pass, :bool, :cfunc, :colon2, :const, :cvar, :defined, :defn, :dregx, :dstr, :dvar, :dxstr, :ensure, :false, :fbody, :gvar, :hash, :ivar, :lit, :long, :lvar, :match2, :match3, :nil, :not, :nth_ref, :return, :scope, :self, :splat, :str, :to_ary, :true, :unknown, :value, :void, :zarray, :zarray, :zclass, :zsuper then
65
+ # ignore
66
+ else
67
+ puts "unhandled token #{token.inspect}" if $VERBOSE
68
+ end
69
+ end
70
+ key = ["#{klassname}.#{methodname}", a, b, c]
71
+ val = Math.sqrt(a*a+b*b+c*c)
72
+ score[key] = val
73
+ end
74
+ end
75
+
76
+ puts "|ABC| = Math.sqrt(assignments^2 + branches^2 + calls^2)"
77
+ puts
78
+ count = 1
79
+ ta = tb = tc = tval = 0
80
+ score.sort_by { |k,v| v }.reverse.each do |key,val|
81
+ name, a, b, c = *key
82
+ ta += a
83
+ tb += b
84
+ tc += c
85
+ tval += val
86
+ printf "%3d) %-50s = %2d + %2d + %2d = %6.2f\n", count, name, a, b, c, val
87
+ count += 1
88
+ end rescue nil
89
+ printf "%3d) %-50s = %2d + %2d + %2d = %6.2f\n", count, "Total", ta, tb, tc, tval
@@ -0,0 +1,28 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'parse_tree'
4
+
5
+ all_nodes = ParseTree::NODE_NAMES
6
+
7
+ ARGV.each do |processor|
8
+ require processor
9
+ end
10
+
11
+ ObjectSpace.each_object(Class) do |klass|
12
+ if klass < SexpProcessor then
13
+
14
+ processor = klass.new
15
+ processors = klass.public_instance_methods(true).grep(/process_/)
16
+
17
+ if processor.strict then
18
+ puts "#{klass.name}:"
19
+ puts
20
+
21
+ # TODO: check unsupported against supported
22
+ processors = processors.map { |m| m[8..-1].intern } + processor.unsupported
23
+ unsupported = all_nodes - processors
24
+ p unsupported.sort_by { |sym| sym.to_s }
25
+ puts
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,62 @@
1
+ #!/usr/local/bin/ruby -ws
2
+
3
+ old_classes = []; new_classes = []
4
+
5
+ ObjectSpace.each_object(Module) { |klass| old_classes << klass } if defined? $a
6
+
7
+ require 'pp'
8
+
9
+ begin require 'rubygems' rescue LoadError end
10
+ require 'parse_tree'
11
+ require 'sexp_processor'
12
+
13
+ ObjectSpace.each_object(Module) { |klass| old_classes << klass } unless defined? $a
14
+
15
+ class DependencyAnalyzer < SexpProcessor
16
+
17
+ attr_reader :dependencies
18
+ attr_accessor :current_class
19
+
20
+ def initialize
21
+ super
22
+ self.auto_shift_type = true
23
+ @dependencies = Hash.new { |h,k| h[k] = [] }
24
+ @current_method = nil
25
+ @current_class = nil
26
+ end
27
+
28
+ def self.process(*klasses)
29
+ analyzer = self.new
30
+ klasses.each do |start_klass|
31
+ analyzer.current_class = start_klass
32
+ analyzer.process(ParseTree.new.parse_tree(start_klass))
33
+ end
34
+
35
+ deps = analyzer.dependencies
36
+ deps.keys.sort_by {|k| k.to_s}.each do |dep_to|
37
+ dep_from = deps[dep_to]
38
+ puts "#{dep_to}: #{dep_from.uniq.sort.join(", ")}"
39
+ end
40
+ end
41
+
42
+ def process_defn(exp)
43
+ name = exp.shift
44
+ @current_method = name
45
+ return s(:defn, name, process(exp.shift), process(exp.shift))
46
+ end
47
+
48
+ def process_const(exp)
49
+ name = exp.shift
50
+ const = (defined?($c) ? @current_class.name : "#{@current_class}.#{@current_method}")
51
+ is_class = ! (Object.const_get(name) rescue nil).nil?
52
+ @dependencies[name] << const if is_class
53
+ return s(:const, name)
54
+ end
55
+ end
56
+
57
+ if __FILE__ == $0 then
58
+ ARGV.each { |name| require name }
59
+ ObjectSpace.each_object(Module) { |klass| new_classes << klass }
60
+ new_classes.delete DependencyAnalyzer unless defined? $a
61
+ DependencyAnalyzer.process(*(new_classes - old_classes))
62
+ end
@@ -0,0 +1,60 @@
1
+ #!/usr/local/bin/ruby -ws
2
+
3
+ require 'pp'
4
+ begin require 'rubygems' rescue LoadError end
5
+ require 'parse_tree'
6
+ require 'sexp'
7
+
8
+ $a ||= false
9
+ $h ||= false
10
+ $n ||= false
11
+ $r ||= false
12
+ $s ||= false
13
+ $u ||= false
14
+ $n = $n.intern if $n
15
+
16
+ if $h then
17
+ puts "usage: #{File.basename $0} [options] [file...]"
18
+ puts "options:"
19
+ puts "-h : display usage"
20
+ puts "-a : all nodes, including newline"
21
+ puts "-n=node : only display matching nodes"
22
+ puts "-r : raw arrays, no sexps"
23
+ puts "-s : structural sexps, strip all content and show bare tree"
24
+ puts "-u : unified sexps"
25
+
26
+ exit 1
27
+ end
28
+
29
+ ARGV.push "-" if ARGV.empty?
30
+
31
+ if $u then
32
+ require 'sexp_processor'
33
+ require 'unified_ruby'
34
+
35
+ class Unifier < SexpProcessor
36
+ include UnifiedRuby
37
+ end
38
+ end
39
+
40
+ parse_tree = ParseTree.new($a)
41
+ unifier = Unifier.new if $u
42
+
43
+ ARGV.each do |file|
44
+ ruby = file == "-" ? $stdin.read : File.read(file)
45
+
46
+ sexp = parse_tree.parse_tree_for_string(ruby, file).first
47
+ sexp = Sexp.from_array sexp unless $r
48
+ sexp = unifier.process(sexp) if $u
49
+ sexp = sexp.structure if $s
50
+
51
+ if $n then
52
+ sexp.each_of_type $n do |node|
53
+ p node
54
+ end
55
+ elsif defined? $q then
56
+ p sexp
57
+ else
58
+ pp sexp
59
+ end
60
+ end
data/demo/printer.rb ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/local/bin/ruby -w
2
+ require 'rubygems'
3
+ require 'sexp_processor'
4
+
5
+ class QuickPrinter < SexpProcessor
6
+ def initialize
7
+ super
8
+ self.strict = false
9
+ self.auto_shift_type = true
10
+ end
11
+ def process_defn(exp)
12
+ name = exp.shift
13
+ args = process exp.shift
14
+ body = process exp.shift
15
+ puts " def #{name}"
16
+ return s(:defn, name, args, body)
17
+ end
18
+ end
19
+
20
+ QuickPrinter.new.process(*ParseTree.new.parse_tree(QuickPrinter))
data/lib/parse_tree.rb ADDED
@@ -0,0 +1,1153 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ raise LoadError, "ParseTree doesn't work with ruby #{RUBY_VERSION}" if
4
+ RUBY_VERSION >= "1.9"
5
+ raise LoadError, "ParseTree isn't needed with rubinius" if
6
+ defined? RUBY_ENGINE and RUBY_ENGINE == "rbx"
7
+
8
+ require 'rubygems'
9
+ require 'inline'
10
+ require 'unified_ruby'
11
+
12
+ class Module
13
+ def modules
14
+ ancestors[1..-1]
15
+ end
16
+ end
17
+
18
+ class Class
19
+ def modules
20
+ a = self.ancestors
21
+ a[1..a.index(superclass)-1]
22
+ end
23
+ end
24
+
25
+ ##
26
+ # ParseTree is a RubyInline-style extension that accesses and
27
+ # traverses the internal parse tree created by ruby.
28
+ #
29
+ # class Example
30
+ # def blah
31
+ # return 1 + 1
32
+ # end
33
+ # end
34
+ #
35
+ # ParseTree.new.parse_tree(Example)
36
+ # => [[:class, :Example, :Object,
37
+ # [:defn,
38
+ # "blah",
39
+ # [:scope,
40
+ # [:block,
41
+ # [:args],
42
+ # [:return, [:call, [:lit, 1], "+", [:array, [:lit, 1]]]]]]]]]
43
+
44
+ class RawParseTree
45
+
46
+ VERSION = '3.0.1'
47
+
48
+ ##
49
+ # Front end translation method.
50
+
51
+ def self.translate(klass_or_str, method=nil)
52
+ pt = self.new(false)
53
+ case klass_or_str
54
+ when String then
55
+ sexp = pt.parse_tree_for_string(klass_or_str).first
56
+ if method then
57
+ # class, scope, block, *methods
58
+ sexp.last.last[1..-1].find do |defn|
59
+ defn[1] == method
60
+ end
61
+ else
62
+ sexp
63
+ end
64
+ else
65
+ unless method.nil? then
66
+ if method.to_s =~ /^self\./ then
67
+ method = method.to_s[5..-1].intern
68
+ pt.parse_tree_for_method(klass_or_str, method, true)
69
+ else
70
+ pt.parse_tree_for_method(klass_or_str, method)
71
+ end
72
+ else
73
+ pt.parse_tree(klass_or_str).first
74
+ end
75
+ end
76
+ end
77
+
78
+ ##
79
+ # Initializes a ParseTree instance. Includes newline nodes if
80
+ # +include_newlines+ which defaults to +$DEBUG+.
81
+
82
+ def initialize(include_newlines=$DEBUG)
83
+ @include_newlines = include_newlines
84
+ end
85
+
86
+ ##
87
+ # Main driver for ParseTree. Returns an array of arrays containing
88
+ # the parse tree for +klasses+.
89
+ #
90
+ # Structure:
91
+ #
92
+ # [[:class, classname, superclassname, [:defn :method1, ...], ...], ...]
93
+ #
94
+ # NOTE: v1.0 - v1.1 had the signature (klass, meth=nil). This wasn't
95
+ # used much at all and since parse_tree_for_method already existed,
96
+ # it was deemed more useful to expand this method to do multiple
97
+ # classes.
98
+
99
+ def parse_tree(*klasses)
100
+ result = []
101
+ klasses.each do |klass|
102
+ klassname = klass.name rescue '' # HACK klass.name should never be nil
103
+ # Tempfile's DelegateClass(File) seems to
104
+ # cause this
105
+ klassname = "UnnamedClass_#{klass.object_id}" if klassname.empty?
106
+ klassname = klassname.to_sym
107
+
108
+ code = if Class === klass then
109
+ sc = klass.superclass
110
+ sc_name = ((sc.nil? or sc.name.empty?) ? "nil" : sc.name).intern
111
+ [:class, klassname, [:const, sc_name]]
112
+ else
113
+ [:module, klassname]
114
+ end
115
+
116
+ method_names = []
117
+ method_names += klass.instance_methods false
118
+ method_names += klass.private_instance_methods false
119
+ # protected methods are included in instance_methods, go figure!
120
+
121
+ method_names.sort.each do |m|
122
+ r = parse_tree_for_method(klass, m.to_sym)
123
+ code << r
124
+ end
125
+
126
+ klass.modules.each do |mod| # TODO: add a test for this damnit
127
+ mod.instance_methods.each do |m|
128
+ r = parse_tree_for_method(mod, m.to_sym)
129
+ code << r
130
+ end
131
+ end
132
+
133
+ klass.singleton_methods(false).sort.each do |m|
134
+ code << parse_tree_for_method(klass, m.to_sym, true)
135
+ end
136
+
137
+ result << code
138
+ end
139
+ return result
140
+ end
141
+
142
+ ##
143
+ # Returns the parse tree for just one +method+ of a class +klass+.
144
+ #
145
+ # Format:
146
+ #
147
+ # [:defn, :name, :body]
148
+
149
+ def parse_tree_for_method(klass, method, is_cls_meth=false, verbose = true)
150
+ $stderr.puts "** parse_tree_for_method(#{klass}, #{method}):" if $DEBUG
151
+ old_verbose, $VERBOSE = $VERBOSE, verbose
152
+ r = parse_tree_for_meth(klass, method.to_sym, is_cls_meth)
153
+ r
154
+ ensure
155
+ $VERBOSE = old_verbose
156
+ end
157
+
158
+ ##
159
+ # Returns the parse tree for a string +source+.
160
+ #
161
+ # Format:
162
+ #
163
+ # [[sexps] ... ]
164
+
165
+ def parse_tree_for_string(source,
166
+ filename = '(string)', line = 1, verbose = true)
167
+ old_verbose, $VERBOSE = $VERBOSE, verbose
168
+ return parse_tree_for_str0(source, filename, line)
169
+ ensure
170
+ $VERBOSE = old_verbose
171
+ end
172
+
173
+ def parse_tree_for_str0(*__1args2__) # :nodoc:
174
+ parse_tree_for_str(*__1args2__) # just helps clean up the binding
175
+ end
176
+
177
+ if RUBY_VERSION < "1.8.4" then
178
+ inline do |builder|
179
+ builder.add_type_converter("bool", '', '')
180
+ builder.c_singleton "
181
+ bool has_alloca() {
182
+ (void)self;
183
+ #ifdef C_ALLOCA
184
+ return Qtrue;
185
+ #else
186
+ return Qfalse;
187
+ #endif
188
+ }"
189
+ end
190
+ else
191
+ def self.has_alloca
192
+ true
193
+ end
194
+ end
195
+
196
+
197
+ NODE_NAMES = [
198
+ # 00
199
+ :method, :fbody, :cfunc, :scope, :block,
200
+ :if, :case, :when, :opt_n, :while,
201
+ # 10
202
+ :until, :iter, :for, :break, :next,
203
+ :redo, :retry, :begin, :rescue, :resbody,
204
+ # 20
205
+ :ensure, :and, :or, :not, :masgn,
206
+ :lasgn, :dasgn, :dasgn_curr, :gasgn, :iasgn,
207
+ # 30
208
+ :cdecl, :cvasgn, :cvdecl, :op_asgn1, :op_asgn2,
209
+ :op_asgn_and, :op_asgn_or, :call, :fcall, :vcall,
210
+ # 40
211
+ :super, :zsuper, :array, :zarray, :hash,
212
+ :return, :yield, :lvar, :dvar, :gvar,
213
+ # 50
214
+ :ivar, :const, :cvar, :nth_ref, :back_ref,
215
+ :match, :match2, :match3, :lit, :str,
216
+ # 60
217
+ :dstr, :xstr, :dxstr, :evstr, :dregx,
218
+ :dregx_once, :args, :argscat, :argspush, :splat,
219
+ # 70
220
+ :to_ary, :svalue, :block_arg, :block_pass, :defn,
221
+ :defs, :alias, :valias, :undef, :class,
222
+ # 80
223
+ :module, :sclass, :colon2, :colon3, :cref,
224
+ :dot2, :dot3, :flip2, :flip3, :attrset,
225
+ # 90
226
+ :self, :nil, :true, :false, :defined,
227
+ # 95
228
+ :newline, :postexe, :alloca, :dmethod, :bmethod,
229
+ # 100
230
+ :memo, :ifunc, :dsym, :attrasgn,
231
+ :last
232
+ ]
233
+
234
+ if RUBY_VERSION < "1.8.4" then
235
+ NODE_NAMES.delete :alloca unless has_alloca
236
+ end
237
+
238
+ if RUBY_VERSION > "1.9" then
239
+ NODE_NAMES.insert NODE_NAMES.index(:hash), :values
240
+ NODE_NAMES.insert NODE_NAMES.index(:defined), :errinfo
241
+ NODE_NAMES.insert NODE_NAMES.index(:last), :prelude, :lambda
242
+ NODE_NAMES.delete :dmethod
243
+ NODE_NAMES[128] = NODE_NAMES.delete :newline
244
+ end
245
+
246
+ ############################################################
247
+ # END of rdoc methods
248
+ ############################################################
249
+
250
+ inline do |builder|
251
+ builder.add_type_converter("bool", '', '')
252
+ builder.add_type_converter("ID *", '', '')
253
+ builder.add_type_converter("NODE *", '(NODE *)', '(VALUE)')
254
+ builder.include '"intern.h"'
255
+ builder.include '"version.h"'
256
+ builder.include '"rubysig.h"'
257
+ builder.include '"node.h"'
258
+ builder.include '"st.h"'
259
+ builder.include '"env.h"'
260
+
261
+ if RUBY_VERSION < "1.8.6" then
262
+ builder.prefix '#define RARRAY_PTR(s) (RARRAY(s)->ptr)'
263
+ builder.prefix '#define RARRAY_LEN(s) (RARRAY(s)->len)'
264
+ end
265
+
266
+ if ENV['ANAL'] or ENV['DOMAIN'] =~ /zenspider/ then
267
+ builder.add_compile_flags "-Wall"
268
+ builder.add_compile_flags "-W"
269
+ builder.add_compile_flags "-Wpointer-arith"
270
+ builder.add_compile_flags "-Wcast-qual"
271
+ builder.add_compile_flags "-Wcast-align"
272
+ builder.add_compile_flags "-Wwrite-strings"
273
+ builder.add_compile_flags "-Wmissing-noreturn"
274
+ builder.add_compile_flags "-Wno-long-long"
275
+
276
+ # NOTE: this flag doesn't work w/ gcc 2.95.x - the FreeBSD default
277
+ # builder.add_compile_flags "-Wno-strict-aliasing"
278
+ # ruby.h screws these up hardcore:
279
+ # builder.add_compile_flags "-Wundef"
280
+ # builder.add_compile_flags "-Wconversion"
281
+ # builder.add_compile_flags "-Wstrict-prototypes"
282
+ # builder.add_compile_flags "-Wmissing-prototypes"
283
+ # builder.add_compile_flags "-Wsign-compare"
284
+ end
285
+
286
+ # NOTE: If you get weird compiler errors like:
287
+ # dereferencing type-punned pointer will break strict-aliasing rules
288
+ # PLEASE do one of the following:
289
+ # 1) Get me a login on your box so I can repro this and get it fixed.
290
+ # 2) Fix it and send me the patch
291
+ # 3) (quick, but dirty and bad), comment out the following line:
292
+ builder.add_compile_flags "-Werror" unless RUBY_PLATFORM =~ /mswin/
293
+
294
+ builder.prefix %{
295
+ #define nd_3rd u3.node
296
+ static unsigned case_level = 0;
297
+ static unsigned when_level = 0;
298
+ static unsigned inside_case_args = 0;
299
+ }
300
+
301
+ builder.prefix %{
302
+ static VALUE wrap_into_node(const char * name, VALUE val) {
303
+ VALUE n = rb_ary_new();
304
+ rb_ary_push(n, ID2SYM(rb_intern(name)));
305
+ if (val) rb_ary_push(n, val);
306
+ return n;
307
+ }
308
+ }
309
+
310
+ builder.prefix %{
311
+ struct METHOD {
312
+ VALUE klass, rklass;
313
+ VALUE recv;
314
+ ID id, oid;
315
+ #if RUBY_VERSION_CODE > 182
316
+ int safe_level;
317
+ #endif
318
+ NODE *body;
319
+ };
320
+
321
+ struct BLOCK {
322
+ NODE *var;
323
+ NODE *body;
324
+ VALUE self;
325
+ struct FRAME frame;
326
+ struct SCOPE *scope;
327
+ VALUE klass;
328
+ NODE *cref;
329
+ int iter;
330
+ int vmode;
331
+ int flags;
332
+ int uniq;
333
+ struct RVarmap *dyna_vars;
334
+ VALUE orig_thread;
335
+ VALUE wrapper;
336
+ VALUE block_obj;
337
+ struct BLOCK *outer;
338
+ struct BLOCK *prev;
339
+ };
340
+ } unless RUBY_VERSION >= "1.9" # we got matz to add this to env.h
341
+
342
+ ##
343
+ # add_to_parse_tree(self, ary, node, local_variables)
344
+
345
+ builder.prefix %Q@
346
+ void add_to_parse_tree(VALUE self, VALUE ary, NODE * n, ID * locals) {
347
+ NODE * volatile node = n;
348
+ VALUE current;
349
+ VALUE node_name;
350
+ static VALUE node_names = Qnil;
351
+ static int masgn_level = 0;
352
+
353
+ if (NIL_P(node_names)) {
354
+ node_names = rb_const_get_at(rb_path2class("RawParseTree"),rb_intern("NODE_NAMES"));
355
+ }
356
+
357
+ if (!node) return;
358
+
359
+ again:
360
+
361
+ if (node) {
362
+ node_name = rb_ary_entry(node_names, nd_type(node));
363
+ if (RTEST(ruby_debug)) {
364
+ fprintf(stderr, "%15s: %s%s%s\\n",
365
+ rb_id2name(SYM2ID(node_name)),
366
+ (RNODE(node)->u1.node != NULL ? "u1 " : " "),
367
+ (RNODE(node)->u2.node != NULL ? "u2 " : " "),
368
+ (RNODE(node)->u3.node != NULL ? "u3 " : " "));
369
+ }
370
+ } else {
371
+ node_name = ID2SYM(rb_intern("ICKY"));
372
+ }
373
+
374
+ current = rb_ary_new();
375
+ rb_ary_push(ary, current);
376
+ rb_ary_push(current, node_name);
377
+
378
+ switch (nd_type(node)) {
379
+
380
+ case NODE_BLOCK:
381
+ {
382
+ while (node) {
383
+ add_to_parse_tree(self, current, node->nd_head, locals);
384
+ node = node->nd_next;
385
+ }
386
+ if (!masgn_level && RARRAY_LEN(current) == 2) {
387
+ rb_ary_pop(ary);
388
+ rb_ary_push(ary, rb_ary_pop(current));
389
+ return;
390
+ }
391
+ }
392
+ break;
393
+
394
+ case NODE_FBODY:
395
+ case NODE_DEFINED:
396
+ add_to_parse_tree(self, current, node->nd_head, locals);
397
+ break;
398
+
399
+ case NODE_COLON2:
400
+ add_to_parse_tree(self, current, node->nd_head, locals);
401
+ rb_ary_push(current, ID2SYM(node->nd_mid));
402
+ break;
403
+
404
+ case NODE_MATCH2:
405
+ case NODE_MATCH3:
406
+ add_to_parse_tree(self, current, node->nd_recv, locals);
407
+ add_to_parse_tree(self, current, node->nd_value, locals);
408
+ break;
409
+
410
+ case NODE_BEGIN:
411
+ case NODE_OPT_N:
412
+ case NODE_NOT:
413
+ add_to_parse_tree(self, current, node->nd_body, locals);
414
+ break;
415
+
416
+ case NODE_IF:
417
+ add_to_parse_tree(self, current, node->nd_cond, locals);
418
+ if (node->nd_body) {
419
+ add_to_parse_tree(self, current, node->nd_body, locals);
420
+ } else {
421
+ rb_ary_push(current, Qnil);
422
+ }
423
+ if (node->nd_else) {
424
+ add_to_parse_tree(self, current, node->nd_else, locals);
425
+ } else {
426
+ rb_ary_push(current, Qnil);
427
+ }
428
+ break;
429
+
430
+ case NODE_CASE:
431
+ case_level++;
432
+ if (node->nd_head != NULL) {
433
+ add_to_parse_tree(self, current, node->nd_head, locals); /* expr */
434
+ } else {
435
+ rb_ary_push(current, Qnil);
436
+ }
437
+ node = node->nd_body;
438
+ while (node) {
439
+ add_to_parse_tree(self, current, node, locals);
440
+ if (nd_type(node) == NODE_WHEN) { /* when */
441
+ node = node->nd_next;
442
+ } else {
443
+ break; /* else */
444
+ }
445
+ if (! node) {
446
+ rb_ary_push(current, Qnil); /* no else */
447
+ }
448
+ }
449
+ case_level--;
450
+ break;
451
+
452
+ case NODE_WHEN:
453
+ when_level++;
454
+ if (!inside_case_args && case_level < when_level) { /* when without case, ie, no expr in case */
455
+ if (when_level > 0) when_level--;
456
+ rb_ary_pop(ary); /* reset what current is pointing at */
457
+ node = NEW_CASE(0, node);
458
+ goto again;
459
+ }
460
+ inside_case_args++;
461
+ add_to_parse_tree(self, current, node->nd_head, locals); /* args */
462
+ inside_case_args--;
463
+
464
+ if (node->nd_body) {
465
+ add_to_parse_tree(self, current, node->nd_body, locals); /* body */
466
+ } else {
467
+ rb_ary_push(current, Qnil);
468
+ }
469
+
470
+ if (when_level > 0) when_level--;
471
+ break;
472
+
473
+ case NODE_WHILE:
474
+ case NODE_UNTIL:
475
+ add_to_parse_tree(self, current, node->nd_cond, locals);
476
+ if (node->nd_body) {
477
+ add_to_parse_tree(self, current, node->nd_body, locals);
478
+ } else {
479
+ rb_ary_push(current, Qnil);
480
+ }
481
+ rb_ary_push(current, node->nd_3rd == 0 ? Qfalse : Qtrue);
482
+ break;
483
+
484
+ case NODE_BLOCK_PASS:
485
+ add_to_parse_tree(self, current, node->nd_body, locals);
486
+ add_to_parse_tree(self, current, node->nd_iter, locals);
487
+ break;
488
+
489
+ case NODE_ITER:
490
+ case NODE_FOR:
491
+ add_to_parse_tree(self, current, node->nd_iter, locals);
492
+ masgn_level++;
493
+ if (node->nd_var != (NODE *)1
494
+ && node->nd_var != (NODE *)2
495
+ && node->nd_var != NULL) {
496
+ add_to_parse_tree(self, current, node->nd_var, locals);
497
+ } else {
498
+ if (node->nd_var == NULL) {
499
+ // e.g. proc {}
500
+ rb_ary_push(current, Qnil);
501
+ } else {
502
+ // e.g. proc {||}
503
+ rb_ary_push(current, INT2FIX(0));
504
+ }
505
+ }
506
+ masgn_level--;
507
+ add_to_parse_tree(self, current, node->nd_body, locals);
508
+ break;
509
+
510
+ case NODE_BREAK:
511
+ case NODE_NEXT:
512
+ if (node->nd_stts)
513
+ add_to_parse_tree(self, current, node->nd_stts, locals);
514
+
515
+ break;
516
+
517
+ case NODE_YIELD:
518
+ if (node->nd_stts)
519
+ add_to_parse_tree(self, current, node->nd_stts, locals);
520
+
521
+ if (node->nd_stts
522
+ && (nd_type(node->nd_stts) == NODE_ARRAY
523
+ || nd_type(node->nd_stts) == NODE_ZARRAY)
524
+ && !node->nd_state)
525
+ rb_ary_push(current, Qtrue);
526
+
527
+ break;
528
+
529
+ case NODE_RESCUE:
530
+ add_to_parse_tree(self, current, node->nd_1st, locals);
531
+ add_to_parse_tree(self, current, node->nd_2nd, locals);
532
+ add_to_parse_tree(self, current, node->nd_3rd, locals);
533
+ break;
534
+
535
+ /*
536
+ // rescue body:
537
+ // begin stmt rescue exception => var; stmt; [rescue e2 => v2; s2;]* end
538
+ // stmt rescue stmt
539
+ // a = b rescue c
540
+ */
541
+
542
+ case NODE_RESBODY:
543
+ if (node->nd_3rd) {
544
+ add_to_parse_tree(self, current, node->nd_3rd, locals);
545
+ } else {
546
+ rb_ary_push(current, Qnil);
547
+ }
548
+ add_to_parse_tree(self, current, node->nd_2nd, locals);
549
+ add_to_parse_tree(self, current, node->nd_1st, locals);
550
+ break;
551
+
552
+ case NODE_ENSURE:
553
+ add_to_parse_tree(self, current, node->nd_head, locals);
554
+ if (node->nd_ensr) {
555
+ add_to_parse_tree(self, current, node->nd_ensr, locals);
556
+ }
557
+ break;
558
+
559
+ case NODE_AND:
560
+ case NODE_OR:
561
+ add_to_parse_tree(self, current, node->nd_1st, locals);
562
+ add_to_parse_tree(self, current, node->nd_2nd, locals);
563
+ break;
564
+
565
+ case NODE_DOT2:
566
+ case NODE_DOT3:
567
+ case NODE_FLIP2:
568
+ case NODE_FLIP3:
569
+ add_to_parse_tree(self, current, node->nd_beg, locals);
570
+ add_to_parse_tree(self, current, node->nd_end, locals);
571
+ break;
572
+
573
+ case NODE_RETURN:
574
+ if (node->nd_stts)
575
+ add_to_parse_tree(self, current, node->nd_stts, locals);
576
+ break;
577
+
578
+ case NODE_ARGSCAT:
579
+ case NODE_ARGSPUSH:
580
+ add_to_parse_tree(self, current, node->nd_head, locals);
581
+ add_to_parse_tree(self, current, node->nd_body, locals);
582
+ break;
583
+
584
+ case NODE_CALL:
585
+ case NODE_FCALL:
586
+ case NODE_VCALL:
587
+ if (nd_type(node) != NODE_FCALL)
588
+ add_to_parse_tree(self, current, node->nd_recv, locals);
589
+ rb_ary_push(current, ID2SYM(node->nd_mid));
590
+ if (node->nd_args || nd_type(node) != NODE_FCALL)
591
+ add_to_parse_tree(self, current, node->nd_args, locals);
592
+ break;
593
+
594
+ case NODE_SUPER:
595
+ add_to_parse_tree(self, current, node->nd_args, locals);
596
+ break;
597
+
598
+ case NODE_BMETHOD:
599
+ {
600
+ struct BLOCK *data;
601
+ Data_Get_Struct(node->nd_cval, struct BLOCK, data);
602
+ if (data->var == 0 || data->var == (NODE *)1 || data->var == (NODE *)2) {
603
+ rb_ary_push(current, Qnil);
604
+ } else {
605
+ masgn_level++;
606
+ add_to_parse_tree(self, current, data->var, locals);
607
+ masgn_level--;
608
+ }
609
+ add_to_parse_tree(self, current, data->body, locals);
610
+ }
611
+ break;
612
+
613
+ #if RUBY_VERSION_CODE < 190
614
+ case NODE_DMETHOD:
615
+ {
616
+ struct METHOD *data;
617
+ Data_Get_Struct(node->nd_cval, struct METHOD, data);
618
+ rb_ary_push(current, ID2SYM(data->id));
619
+ add_to_parse_tree(self, current, data->body, locals);
620
+ break;
621
+ }
622
+ #endif
623
+
624
+ case NODE_METHOD:
625
+ add_to_parse_tree(self, current, node->nd_3rd, locals);
626
+ break;
627
+
628
+ case NODE_SCOPE:
629
+ add_to_parse_tree(self, current, node->nd_next, node->nd_tbl);
630
+ break;
631
+
632
+ case NODE_OP_ASGN1:
633
+ add_to_parse_tree(self, current, node->nd_recv, locals);
634
+ #if RUBY_VERSION_CODE < 185
635
+ add_to_parse_tree(self, current, node->nd_args->nd_next, locals);
636
+ rb_ary_pop(rb_ary_entry(current, -1)); /* no idea why I need this */
637
+ #else
638
+ add_to_parse_tree(self, current, node->nd_args->nd_2nd, locals);
639
+ #endif
640
+ switch (node->nd_mid) {
641
+ case 0:
642
+ rb_ary_push(current, ID2SYM(rb_intern("||")));
643
+ break;
644
+ case 1:
645
+ rb_ary_push(current, ID2SYM(rb_intern("&&")));
646
+ break;
647
+ default:
648
+ rb_ary_push(current, ID2SYM(node->nd_mid));
649
+ break;
650
+ }
651
+ add_to_parse_tree(self, current, node->nd_args->nd_head, locals);
652
+ break;
653
+
654
+ case NODE_OP_ASGN2:
655
+ add_to_parse_tree(self, current, node->nd_recv, locals);
656
+ rb_ary_push(current, ID2SYM(node->nd_next->nd_aid));
657
+
658
+ switch (node->nd_next->nd_mid) {
659
+ case 0:
660
+ rb_ary_push(current, ID2SYM(rb_intern("||")));
661
+ break;
662
+ case 1:
663
+ rb_ary_push(current, ID2SYM(rb_intern("&&")));
664
+ break;
665
+ default:
666
+ rb_ary_push(current, ID2SYM(node->nd_next->nd_mid));
667
+ break;
668
+ }
669
+
670
+ add_to_parse_tree(self, current, node->nd_value, locals);
671
+ break;
672
+
673
+ case NODE_OP_ASGN_AND:
674
+ case NODE_OP_ASGN_OR:
675
+ add_to_parse_tree(self, current, node->nd_head, locals);
676
+ add_to_parse_tree(self, current, node->nd_value, locals);
677
+ break;
678
+
679
+ case NODE_MASGN:
680
+ masgn_level++;
681
+ if (node->nd_head) {
682
+ add_to_parse_tree(self, current, node->nd_head, locals);
683
+ } else {
684
+ rb_ary_push(current, Qnil);
685
+ }
686
+ if (node->nd_args) {
687
+ if (node->nd_args != (NODE *)-1) {
688
+ add_to_parse_tree(self, current, node->nd_args, locals);
689
+ } else {
690
+ rb_ary_push(current, wrap_into_node("splat", 0));
691
+ }
692
+ } else {
693
+ rb_ary_push(current, Qnil);
694
+ }
695
+ if (node->nd_value) {
696
+ add_to_parse_tree(self, current, node->nd_value, locals);
697
+ } else {
698
+ rb_ary_push(current, Qnil);
699
+ }
700
+ masgn_level--;
701
+ break;
702
+
703
+ case NODE_LASGN:
704
+ case NODE_IASGN:
705
+ case NODE_DASGN:
706
+ case NODE_CVASGN:
707
+ case NODE_CVDECL:
708
+ case NODE_GASGN:
709
+ rb_ary_push(current, ID2SYM(node->nd_vid));
710
+ add_to_parse_tree(self, current, node->nd_value, locals);
711
+ break;
712
+
713
+ case NODE_CDECL:
714
+ if (node->nd_vid) {
715
+ rb_ary_push(current, ID2SYM(node->nd_vid));
716
+ } else {
717
+ add_to_parse_tree(self, current, node->nd_else, locals);
718
+ }
719
+
720
+ add_to_parse_tree(self, current, node->nd_value, locals);
721
+ break;
722
+
723
+ case NODE_DASGN_CURR:
724
+ rb_ary_push(current, ID2SYM(node->nd_vid));
725
+ if (node->nd_value) {
726
+ add_to_parse_tree(self, current, node->nd_value, locals);
727
+ if (!masgn_level && RARRAY_LEN(current) == 2) {
728
+ rb_ary_pop(ary);
729
+ return;
730
+ }
731
+ } else {
732
+ if (!masgn_level) {
733
+ rb_ary_pop(ary);
734
+ return;
735
+ }
736
+ }
737
+ break;
738
+
739
+ case NODE_VALIAS: /* u1 u2 (alias $global $global2) */
740
+ #if RUBY_VERSION_CODE < 185
741
+ rb_ary_push(current, ID2SYM(node->u2.id));
742
+ rb_ary_push(current, ID2SYM(node->u1.id));
743
+ #else
744
+ rb_ary_push(current, ID2SYM(node->u1.id));
745
+ rb_ary_push(current, ID2SYM(node->u2.id));
746
+ #endif
747
+ break;
748
+ case NODE_ALIAS: /* u1 u2 (alias :blah :blah2) */
749
+ #if RUBY_VERSION_CODE < 185
750
+ rb_ary_push(current, wrap_into_node("lit", ID2SYM(node->u2.id)));
751
+ rb_ary_push(current, wrap_into_node("lit", ID2SYM(node->u1.id)));
752
+ #else
753
+ add_to_parse_tree(self, current, node->nd_1st, locals);
754
+ add_to_parse_tree(self, current, node->nd_2nd, locals);
755
+ #endif
756
+ break;
757
+
758
+ case NODE_UNDEF: /* u2 (undef name, ...) */
759
+ #if RUBY_VERSION_CODE < 185
760
+ rb_ary_push(current, wrap_into_node("lit", ID2SYM(node->u2.id)));
761
+ #else
762
+ add_to_parse_tree(self, current, node->nd_value, locals);
763
+ #endif
764
+ break;
765
+
766
+ case NODE_COLON3: /* u2 (::OUTER_CONST) */
767
+ rb_ary_push(current, ID2SYM(node->u2.id));
768
+ break;
769
+
770
+ case NODE_HASH:
771
+ {
772
+ NODE *list;
773
+
774
+ list = node->nd_head;
775
+ while (list) {
776
+ add_to_parse_tree(self, current, list->nd_head, locals);
777
+ list = list->nd_next;
778
+ if (list == 0)
779
+ rb_bug("odd number list for Hash");
780
+ add_to_parse_tree(self, current, list->nd_head, locals);
781
+ list = list->nd_next;
782
+ }
783
+ }
784
+ break;
785
+
786
+ case NODE_ARRAY:
787
+ while (node) {
788
+ add_to_parse_tree(self, current, node->nd_head, locals);
789
+ node = node->nd_next;
790
+ }
791
+ break;
792
+
793
+ case NODE_DSTR:
794
+ case NODE_DSYM:
795
+ case NODE_DXSTR:
796
+ case NODE_DREGX:
797
+ case NODE_DREGX_ONCE:
798
+ {
799
+ NODE *list = node->nd_next;
800
+ rb_ary_push(current, rb_str_new3(node->nd_lit));
801
+ while (list) {
802
+ if (list->nd_head) {
803
+ switch (nd_type(list->nd_head)) {
804
+ case NODE_STR:
805
+ add_to_parse_tree(self, current, list->nd_head, locals);
806
+ break;
807
+ case NODE_EVSTR:
808
+ add_to_parse_tree(self, current, list->nd_head, locals);
809
+ break;
810
+ default:
811
+ add_to_parse_tree(self, current, list->nd_head, locals);
812
+ break;
813
+ }
814
+ }
815
+ list = list->nd_next;
816
+ }
817
+ switch (nd_type(node)) {
818
+ case NODE_DREGX:
819
+ case NODE_DREGX_ONCE:
820
+ if (node->nd_cflag) {
821
+ rb_ary_push(current, INT2FIX(node->nd_cflag));
822
+ }
823
+ }
824
+ }
825
+ break;
826
+
827
+ case NODE_DEFN:
828
+ case NODE_DEFS:
829
+ if (node->nd_defn) {
830
+ if (nd_type(node) == NODE_DEFS)
831
+ add_to_parse_tree(self, current, node->nd_recv, locals);
832
+ rb_ary_push(current, ID2SYM(node->nd_mid));
833
+ add_to_parse_tree(self, current, node->nd_defn, locals);
834
+ }
835
+ break;
836
+
837
+ case NODE_CLASS:
838
+ case NODE_MODULE:
839
+ if (nd_type(node->nd_cpath) == NODE_COLON2 && ! node->nd_cpath->nd_vid) {
840
+ rb_ary_push(current, ID2SYM((ID)node->nd_cpath->nd_mid));
841
+ } else {
842
+ add_to_parse_tree(self, current, node->nd_cpath, locals);
843
+ }
844
+
845
+ if (nd_type(node) == NODE_CLASS) {
846
+ if (node->nd_super) {
847
+ add_to_parse_tree(self, current, node->nd_super, locals);
848
+ } else {
849
+ rb_ary_push(current, Qnil);
850
+ }
851
+ }
852
+ add_to_parse_tree(self, current, node->nd_body, locals);
853
+ break;
854
+
855
+ case NODE_SCLASS:
856
+ add_to_parse_tree(self, current, node->nd_recv, locals);
857
+ add_to_parse_tree(self, current, node->nd_body, locals);
858
+ break;
859
+
860
+ case NODE_ARGS: {
861
+ NODE *optnode;
862
+ int i = 0, max_args = node->nd_cnt;
863
+
864
+ /* push regular argument names */
865
+ for (; i < max_args; i++) {
866
+ rb_ary_push(current, ID2SYM(locals[i + 3]));
867
+ }
868
+
869
+ /* look for optional arguments */
870
+ masgn_level++;
871
+ optnode = node->nd_opt;
872
+ while (optnode) {
873
+ rb_ary_push(current, ID2SYM(locals[i + 3]));
874
+ i++;
875
+ optnode = optnode->nd_next;
876
+ }
877
+
878
+ /* look for vargs */
879
+ #if RUBY_VERSION_CODE > 184
880
+ if (node->nd_rest) {
881
+ VALUE sym = rb_str_new2("*");
882
+ if (locals[i + 3]) {
883
+ rb_str_concat(sym, rb_str_new2(rb_id2name(locals[i + 3])));
884
+ }
885
+ sym = rb_str_intern(sym);
886
+ rb_ary_push(current, sym);
887
+ }
888
+ #else
889
+ {
890
+ long arg_count = (long)node->nd_rest;
891
+ if (arg_count > 0) {
892
+ /* *arg name */
893
+ VALUE sym = rb_str_new2("*");
894
+ if (locals[i + 3]) {
895
+ rb_str_concat(sym, rb_str_new2(rb_id2name(locals[i + 3])));
896
+ }
897
+ sym = rb_str_intern(sym);
898
+ rb_ary_push(current, sym);
899
+ } else if (arg_count == 0) {
900
+ /* nothing to do in this case, empty list */
901
+ } else if (arg_count == -1) {
902
+ /* nothing to do in this case, handled above */
903
+ } else if (arg_count == -2) {
904
+ /* nothing to do in this case, no name == no use */
905
+ rb_ary_push(current, rb_str_intern(rb_str_new2("*")));
906
+ } else {
907
+ rb_raise(rb_eArgError,
908
+ "not a clue what this arg value is: %ld", arg_count);
909
+ }
910
+ }
911
+ #endif
912
+
913
+ optnode = node->nd_opt;
914
+ if (optnode) {
915
+ add_to_parse_tree(self, current, node->nd_opt, locals);
916
+ }
917
+ masgn_level--;
918
+ } break;
919
+
920
+ case NODE_LVAR:
921
+ case NODE_DVAR:
922
+ case NODE_IVAR:
923
+ case NODE_CVAR:
924
+ case NODE_GVAR:
925
+ case NODE_CONST:
926
+ case NODE_ATTRSET:
927
+ rb_ary_push(current, ID2SYM(node->nd_vid));
928
+ break;
929
+
930
+ case NODE_XSTR: /* u1 (%x{ls}) */
931
+ case NODE_STR: /* u1 */
932
+ case NODE_LIT:
933
+ rb_ary_push(current, node->nd_lit);
934
+ if (node->nd_cflag) {
935
+ rb_ary_push(current, INT2FIX(node->nd_cflag));
936
+ }
937
+ break;
938
+
939
+ case NODE_MATCH: /* u1 -> [:lit, u1] */
940
+ {
941
+ rb_ary_push(current, wrap_into_node("lit", node->nd_lit));
942
+ }
943
+ break;
944
+
945
+ case NODE_NEWLINE:
946
+ rb_ary_push(current, INT2FIX(nd_line(node)));
947
+ rb_ary_push(current, rb_str_new2(node->nd_file));
948
+ if (! RTEST(rb_iv_get(self, "\@include_newlines"))) {
949
+ rb_ary_pop(ary); /* nuke it */
950
+ node = node->nd_next;
951
+ goto again;
952
+ } else {
953
+ add_to_parse_tree(self, current, node->nd_next, locals);
954
+ }
955
+ break;
956
+
957
+ case NODE_NTH_REF: /* u2 u3 ($1) - u3 is local_cnt('~') ignorable? */
958
+ rb_ary_push(current, INT2FIX(node->nd_nth));
959
+ break;
960
+
961
+ case NODE_BACK_REF: /* u2 u3 ($& etc) */
962
+ {
963
+ char c = node->nd_nth;
964
+ rb_ary_push(current, rb_str_intern(rb_str_new(&c, 1)));
965
+ }
966
+ break;
967
+
968
+ case NODE_BLOCK_ARG: /* u1 u3 (def x(&b) */
969
+ rb_ary_push(current, ID2SYM(node->u1.id));
970
+ break;
971
+
972
+ /* these nodes are empty and do not require extra work: */
973
+ case NODE_RETRY:
974
+ case NODE_FALSE:
975
+ case NODE_NIL:
976
+ case NODE_SELF:
977
+ case NODE_TRUE:
978
+ case NODE_ZARRAY:
979
+ case NODE_ZSUPER:
980
+ case NODE_REDO:
981
+ break;
982
+
983
+ case NODE_SPLAT:
984
+ case NODE_TO_ARY:
985
+ case NODE_SVALUE: /* a = b, c */
986
+ add_to_parse_tree(self, current, node->nd_head, locals);
987
+ break;
988
+
989
+ case NODE_ATTRASGN: /* literal.meth = y u1 u2 u3 */
990
+ /* node id node */
991
+ if (node->nd_1st == RNODE(1)) {
992
+ add_to_parse_tree(self, current, NEW_SELF(), locals);
993
+ } else {
994
+ add_to_parse_tree(self, current, node->nd_1st, locals);
995
+ }
996
+ rb_ary_push(current, ID2SYM(node->u2.id));
997
+ add_to_parse_tree(self, current, node->nd_3rd, locals);
998
+ break;
999
+
1000
+ case NODE_EVSTR:
1001
+ add_to_parse_tree(self, current, node->nd_2nd, locals);
1002
+ break;
1003
+
1004
+ case NODE_POSTEXE: /* END { ... } */
1005
+ /* Nothing to do here... we are in an iter block */
1006
+ break;
1007
+
1008
+ case NODE_CFUNC:
1009
+ case NODE_IFUNC:
1010
+ rb_ary_push(current, INT2NUM((long)node->nd_cfnc));
1011
+ rb_ary_push(current, INT2NUM(node->nd_argc));
1012
+ break;
1013
+
1014
+ #if RUBY_VERSION_CODE >= 190
1015
+ case NODE_ERRINFO:
1016
+ case NODE_VALUES:
1017
+ case NODE_PRELUDE:
1018
+ case NODE_LAMBDA:
1019
+ puts("no worky in 1.9 yet");
1020
+ break;
1021
+ #endif
1022
+
1023
+ /* Nodes we found but have yet to decypher */
1024
+ /* I think these are all runtime only... not positive but... */
1025
+ case NODE_MEMO: /* enum.c zip */
1026
+ case NODE_CREF:
1027
+ /* #defines: */
1028
+ /* case NODE_LMASK: */
1029
+ /* case NODE_LSHIFT: */
1030
+ default:
1031
+ rb_warn("Unhandled node #%d type '%s'", nd_type(node), rb_id2name(SYM2ID(rb_ary_entry(node_names, nd_type(node)))));
1032
+ if (RNODE(node)->u1.node != NULL) rb_warning("unhandled u1 value");
1033
+ if (RNODE(node)->u2.node != NULL) rb_warning("unhandled u2 value");
1034
+ if (RNODE(node)->u3.node != NULL) rb_warning("unhandled u3 value");
1035
+ if (RTEST(ruby_debug)) fprintf(stderr, "u1 = %p u2 = %p u3 = %p\\n", (void*)node->nd_1st, (void*)node->nd_2nd, (void*)node->nd_3rd);
1036
+ rb_ary_push(current, INT2FIX(-99));
1037
+ rb_ary_push(current, INT2FIX(nd_type(node)));
1038
+ break;
1039
+ }
1040
+ }
1041
+ @ # end of add_to_parse_tree block
1042
+
1043
+ builder.c %Q{
1044
+ static VALUE parse_tree_for_meth(VALUE klass, VALUE method, VALUE is_cls_meth) {
1045
+ VALUE n;
1046
+ NODE *node = NULL;
1047
+ ID id;
1048
+ VALUE result = rb_ary_new();
1049
+ VALUE version = rb_const_get_at(rb_cObject,rb_intern("RUBY_VERSION"));
1050
+
1051
+ (void) self; /* quell warnings */
1052
+
1053
+ if (strcmp(StringValuePtr(version), #{RUBY_VERSION.inspect})) {
1054
+ rb_fatal("bad version, %s != #{RUBY_VERSION}\\n", StringValuePtr(version));
1055
+ }
1056
+
1057
+ id = rb_to_id(method);
1058
+ if (RTEST(is_cls_meth)) { /* singleton method */
1059
+ klass = CLASS_OF(klass);
1060
+ }
1061
+ if (st_lookup(RCLASS(klass)->m_tbl, id, &n)) {
1062
+ node = (NODE*)n;
1063
+ rb_ary_push(result, ID2SYM(rb_intern(is_cls_meth ? "defs": "defn")));
1064
+ if (is_cls_meth) {
1065
+ rb_ary_push(result, rb_ary_new3(1, ID2SYM(rb_intern("self"))));
1066
+ }
1067
+ rb_ary_push(result, ID2SYM(id));
1068
+ add_to_parse_tree(self, result, node->nd_body, NULL);
1069
+ } else {
1070
+ rb_ary_push(result, Qnil);
1071
+ }
1072
+
1073
+ return result;
1074
+ }
1075
+ }
1076
+
1077
+ extern_mode = RUBY_PLATFORM =~ /mswin/ ? 'RUBY_EXTERN' : 'extern'
1078
+ builder.prefix " #{extern_mode} NODE *ruby_eval_tree_begin; " \
1079
+ if RUBY_VERSION < '1.9.0'
1080
+
1081
+ # FIXME: ruby_in_eval is not properly exported across platforms
1082
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/13558
1083
+ builder.c %Q{
1084
+ static VALUE parse_tree_for_str(VALUE source, VALUE filename, VALUE line) {
1085
+ VALUE tmp;
1086
+ VALUE result = rb_ary_new();
1087
+ NODE *node = NULL;
1088
+ int critical;
1089
+
1090
+ tmp = rb_check_string_type(filename);
1091
+ if (NIL_P(tmp)) {
1092
+ filename = rb_str_new2("(string)");
1093
+ }
1094
+
1095
+ if (NIL_P(line)) {
1096
+ line = LONG2FIX(1);
1097
+ }
1098
+
1099
+ ruby_nerrs = 0;
1100
+ StringValue(source);
1101
+ critical = rb_thread_critical;
1102
+ rb_thread_critical = Qtrue;
1103
+ ruby_in_eval++;
1104
+ node = rb_compile_string(StringValuePtr(filename), source, NUM2INT(line));
1105
+ ruby_in_eval--;
1106
+ rb_thread_critical = critical;
1107
+
1108
+ if (ruby_nerrs > 0) {
1109
+ ruby_nerrs = 0;
1110
+ #if RUBY_VERSION_CODE < 190
1111
+ ruby_eval_tree_begin = 0;
1112
+ #endif
1113
+ rb_exc_raise(ruby_errinfo);
1114
+ }
1115
+
1116
+ add_to_parse_tree(self, result, node, NULL);
1117
+
1118
+ return result;
1119
+ }
1120
+ }
1121
+
1122
+ end # inline call
1123
+ end # RawParseTree class
1124
+
1125
+ class ParseTree < RawParseTree
1126
+ ##
1127
+ # Initializes a ParseTree instance. Includes newline nodes if
1128
+ # +include_newlines+ which defaults to +$DEBUG+.
1129
+
1130
+ def initialize(include_newlines=$DEBUG)
1131
+ super
1132
+ @unifier = Unifier.new
1133
+ end
1134
+
1135
+ ##
1136
+ # Main driver for ParseTree. Returns a Sexp instance containing the
1137
+ # AST representing the input given. This is a UnifiedRuby sexp, not
1138
+ # a raw sexp from ruby. If you want raw, use the old
1139
+ # parse_tree_for_xxx methods... Please tell me if/why you want raw,
1140
+ # I'd like to know so I can justify keeping the code around.
1141
+
1142
+ def process(input, verbose = nil, file = "(string)", line = -1)
1143
+ case input
1144
+ when Array then
1145
+ @unifier.process(input)
1146
+ when String then
1147
+ pt = self.parse_tree_for_string(input, file, line, verbose).first
1148
+ @unifier.process(pt)
1149
+ else
1150
+ raise ArgumentError, "Unknown input type #{input.inspect}"
1151
+ end
1152
+ end
1153
+ end