ParseTree 3.0.1-x86-mswin32-60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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