ParseTree 1.7.1 → 2.0.0

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.
@@ -1,3 +1,35 @@
1
+ === 2.0.0 / 2007-08-01
2
+
3
+ * 2 major enhancements:
4
+
5
+ * Rewrite methods completely rewritten. Rewriters:
6
+ * are no longer recursive.
7
+ * are no longer required to empty the sexp coming in.
8
+ * are depth first, so rewriter gets passed everything already normalized.
9
+ * keep rewriting until type doesn't change.
10
+ * are magical goodness.
11
+ * Added UnifiedRuby module to aid others in writing clean SexpProcessors:
12
+ * bmethod/dmethod/fbody unified with defn
13
+ * fcall/vcall unified with call
14
+ * resbody unified with itself (lots of different forms)
15
+
16
+ * 5 minor enhancements:
17
+
18
+ * Add #modules to Module.
19
+ * Add Sexp::for shortcut for Sexp.from_array ParseTree.translate(...).
20
+ * Add parens for :block nodes as appropriate. May be overzealous.
21
+ * Made add_to_parse_tree global for reuse by other C extensions.
22
+ * Made test_ruby2ruby MUCH more rigorous with circular testing.
23
+
24
+ * 6 bug fixes:
25
+
26
+ * Added $TESTING = true to pt_testcase.rb. OOPS!
27
+ * Fixed some circular bugs, mostly by hacking them out, wrt operator precidence.
28
+ * Fixed splat arg processing when arg has no name.
29
+ * Fixed trinary operator.
30
+ * Fixed BMETHOD with no arguments.
31
+ * Removed hacky "self." thing for defs at top level PT use.
32
+
1
33
  === 1.7.1 / 2007-06-05
2
34
 
3
35
  * 3 minor enhancements:
@@ -11,6 +11,7 @@ lib/composite_sexp_processor.rb
11
11
  lib/parse_tree.rb
12
12
  lib/sexp.rb
13
13
  lib/sexp_processor.rb
14
+ lib/unified_ruby.rb
14
15
  lib/unique.rb
15
16
  test/pt_testcase.rb
16
17
  test/something.rb
@@ -19,4 +20,5 @@ test/test_composite_sexp_processor.rb
19
20
  test/test_parse_tree.rb
20
21
  test/test_sexp.rb
21
22
  test/test_sexp_processor.rb
23
+ test/test_unified_ruby.rb
22
24
  validate.sh
data/README.txt CHANGED
@@ -3,12 +3,12 @@ ParseTree
3
3
  http://www.zenspider.com/ZSS/Products/ParseTree/
4
4
  support@zenspider.com
5
5
 
6
- ** DESCRIPTION:
6
+ == DESCRIPTION:
7
7
 
8
8
  ParseTree is a C extension (using RubyInline) that extracts the parse
9
9
  tree for an entire class or a specific method and returns it as a
10
10
  s-expression (aka sexp) using ruby's arrays, strings, symbols, and
11
- integers.
11
+ integers.
12
12
 
13
13
  As an example:
14
14
 
@@ -32,23 +32,26 @@ becomes:
32
32
  nil],
33
33
  [:return, [:lit, 0]]]]]
34
34
 
35
- ** FEATURES/PROBLEMS:
36
-
37
- + Uses RubyInline, so it just drops in.
38
- + Includes SexpProcessor and CompositeSexpProcessor.
39
- + Allows you to write very clean filters.
40
- + Includes parse_tree_show, which lets you quickly snoop code.
41
- + echo "1+1" | parse_tree_show -f for quick snippet output.
42
- + Includes parse_tree_abc, which lets you get abc metrics on code.
43
- + abc metrics = numbers of assignments, branches, and calls.
44
- + whitespace independent metric for method complexity.
45
- + Includes parse_tree_deps, which shows you basic class level dependencies.
46
- + Only works on methods in classes/modules, not arbitrary code.
47
- + Does not work on the core classes, as they are not ruby (yet).
35
+ == FEATURES/PROBLEMS:
48
36
 
49
- ** SYNOPSYS:
37
+ * Uses RubyInline, so it just drops in.
38
+ * Includes SexpProcessor and CompositeSexpProcessor.
39
+ * Allows you to write very clean filters.
40
+ * Includes UnifiedRuby, allowing you to automatically rewrite ruby quirks.
41
+ * ParseTree#parse_tree_for_string lets you parse arbitrary strings of ruby.
42
+ * Includes parse_tree_show, which lets you quickly snoop code.
43
+ * echo "1+1" | parse_tree_show -f for quick snippet output.
44
+ * Includes parse_tree_abc, which lets you get abc metrics on code.
45
+ * abc metrics = numbers of assignments, branches, and calls.
46
+ * whitespace independent metric for method complexity.
47
+ * Includes parse_tree_deps, which shows you basic class level dependencies.
48
+ * Does not work on the core classes, as they are not ruby (yet).
50
49
 
51
- sexp_array = ParseTree.new.parse_tree(klass)
50
+ == SYNOPSYS:
51
+
52
+ sexp_array = ParseTree.translate(klass)
53
+ sexp_array = ParseTree.translate(klass, :method)
54
+ sexp_array = ParseTree.translate("1+1")
52
55
 
53
56
  or:
54
57
 
@@ -75,20 +78,21 @@ or:
75
78
 
76
79
  % ./parse_tree_abc myfile.rb
77
80
 
78
- ** REQUIREMENTS:
81
+ == REQUIREMENTS:
79
82
 
80
- + RubyInline 3 or better.
83
+ * RubyInline 3.6 or better.
81
84
 
82
- ** INSTALL:
85
+ == INSTALL:
83
86
 
84
- + sudo rake install
85
- + or: sudo gem install ParseTree
87
+ * rake install_gem
88
+ * sudo rake install
89
+ * sudo gem install ParseTree
86
90
 
87
- ** LICENSE:
91
+ == LICENSE:
88
92
 
89
93
  (The MIT License)
90
94
 
91
- Copyright (c) 2001-2004 Ryan Davis, Zen Spider Software
95
+ Copyright (c) 2001-2007 Ryan Davis, Zen Spider Software
92
96
 
93
97
  Permission is hereby granted, free of charge, to any person obtaining
94
98
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -9,10 +9,11 @@ require './lib/parse_tree.rb'
9
9
  Hoe.new("ParseTree", ParseTree::VERSION) do |p|
10
10
  p.rubyforge_name = "parsetree"
11
11
  p.summary = "Extract and enumerate ruby parse trees."
12
- p.description = p.paragraphs_of("README.txt", 2).join("\n\n")
13
- p.changes = p.paragraphs_of("History.txt", 1).join("\n\n")
12
+ p.summary = p.paragraphs_of("README.txt", 2).join("\n\n")
13
+ p.description = p.paragraphs_of("README.txt", 2..6, 8).join("\n\n")
14
+ p.changes = p.paragraphs_of("History.txt", 1..6).join("\n\n")
14
15
  p.clean_globs << File.expand_path("~/.ruby_inline")
15
- p.extra_deps << ['RubyInline', '>= 3.2.0']
16
+ p.extra_deps << ['RubyInline', '>= 3.6.0']
16
17
  p.spec_extras[:require_paths] = proc { |paths| paths << 'test' }
17
18
  end
18
19
 
@@ -34,8 +35,14 @@ end
34
35
 
35
36
  desc 'Show what tests are not sorted'
36
37
  task :sort do
37
- sh "pgrep '^ \\\"(\\w+)' test/pt_testcase.rb | cut -f 2 -d\\\" > x"
38
- sh "sort x > y"
39
- sh "diff x y"
40
- sh "rm -f x y"
38
+ begin
39
+ sh "pgrep '^ \\\"(\\w+)' test/pt_testcase.rb | cut -f 2 -d\\\" > x"
40
+ sh "sort x > y"
41
+ sh "diff x y"
42
+
43
+ sh 'for f in lib/*.rb; do echo $f; grep "^ *def " $f | grep -v sort=skip > x; sort x > y; echo $f; echo; diff x y; done'
44
+ sh 'for f in test/test_*.rb; do echo $f; grep "^ *def.test_" $f > x; sort x > y; echo $f; echo; diff x y; done'
45
+ ensure
46
+ sh 'rm -f x y'
47
+ end
41
48
  end
@@ -7,6 +7,19 @@ begin require 'rubygems'; rescue LoadError; end
7
7
 
8
8
  require 'inline'
9
9
 
10
+ class Module
11
+ def modules
12
+ ancestors[1..-1]
13
+ end
14
+ end
15
+
16
+ class Class
17
+ def modules
18
+ a = self.ancestors
19
+ a[1..a.index(superclass)-1]
20
+ end
21
+ end
22
+
10
23
  ##
11
24
  # ParseTree is a RubyInline-style extension that accesses and
12
25
  # traverses the internal parse tree created by ruby.
@@ -28,7 +41,7 @@ require 'inline'
28
41
 
29
42
  class ParseTree
30
43
 
31
- VERSION = '1.7.1'
44
+ VERSION = '2.0.0'
32
45
 
33
46
  ##
34
47
  # Front end translation method.
@@ -109,7 +122,17 @@ class ParseTree
109
122
  # protected methods are included in instance_methods, go figure!
110
123
 
111
124
  method_names.sort.each do |m|
112
- code << parse_tree_for_method(klass, m.to_sym)
125
+ r = parse_tree_for_method(klass, m.to_sym)
126
+ p m => r if r == [nil]
127
+ code << r
128
+ end
129
+
130
+ klass.modules.each do |mod| # TODO: add a test for this damnit
131
+ mod.instance_methods.each do |m|
132
+ r = parse_tree_for_method(mod, m.to_sym)
133
+ p m => r if r == [nil]
134
+ code << r
135
+ end
113
136
  end
114
137
 
115
138
  klass.singleton_methods(false).sort.each do |m|
@@ -131,7 +154,6 @@ class ParseTree
131
154
  def parse_tree_for_method(klass, method, is_cls_meth=false)
132
155
  $stderr.puts "** parse_tree_for_method(#{klass}, #{method}):" if $DEBUG
133
156
  r = parse_tree_for_meth(klass, method.to_sym, @include_newlines, is_cls_meth)
134
- r[1] = :"self.#{r[1]}" if is_cls_meth
135
157
  r
136
158
  end
137
159
 
@@ -306,11 +328,11 @@ class ParseTree
306
328
  ##
307
329
  # add_to_parse_tree(ary, node, include_newlines, local_variables)
308
330
 
309
- builder.c_raw %Q@
310
- static void add_to_parse_tree(VALUE ary,
311
- NODE * n,
312
- VALUE newlines,
313
- ID * locals) {
331
+ builder.prefix %Q@
332
+ void add_to_parse_tree(VALUE ary,
333
+ NODE * n,
334
+ VALUE newlines,
335
+ ID * locals) {
314
336
  NODE * volatile node = n;
315
337
  NODE * volatile contnode = NULL;
316
338
  VALUE old_ary = Qnil;
@@ -544,7 +566,11 @@ again_no_block:
544
566
  {
545
567
  struct BLOCK *data;
546
568
  Data_Get_Struct(node->nd_cval, struct BLOCK, data);
547
- add_to_parse_tree(current, data->var, newlines, locals);
569
+ if (data->var == 0 || data->var == (NODE *)1 || data->var == (NODE *)2) {
570
+ rb_ary_push(current, Qnil);
571
+ } else {
572
+ add_to_parse_tree(current, data->var, newlines, locals);
573
+ }
548
574
  add_to_parse_tree(current, data->body, newlines, locals);
549
575
  break;
550
576
  }
@@ -773,7 +799,11 @@ again_no_block:
773
799
 
774
800
  if (arg_count > 0) {
775
801
  // *arg name
776
- VALUE sym = rb_str_intern(rb_str_plus(rb_str_new2("*"), rb_str_new2(rb_id2name(locals[i + 3]))));
802
+ VALUE sym = rb_str_new2("*");
803
+ if (locals[i + 3]) {
804
+ rb_str_concat(sym, rb_str_new2(rb_id2name(locals[i + 3])));
805
+ }
806
+ sym = rb_str_intern(sym);
777
807
  rb_ary_push(current, sym);
778
808
  } else if (arg_count == 0) {
779
809
  // nothing to do in this case, empty list
@@ -878,8 +908,9 @@ again_no_block:
878
908
  break;
879
909
 
880
910
  case NODE_CFUNC:
881
- rb_ary_push(current, INT2FIX(node->nd_cfnc));
882
- rb_ary_push(current, INT2FIX(node->nd_argc));
911
+ case NODE_IFUNC:
912
+ rb_ary_push(current, INT2NUM((long)node->nd_cfnc));
913
+ rb_ary_push(current, INT2NUM(node->nd_argc));
883
914
  break;
884
915
 
885
916
  #{if_version :<, "1.9", "#if 0"}
@@ -895,7 +926,6 @@ again_no_block:
895
926
  // I think these are all runtime only... not positive but...
896
927
  case NODE_MEMO: // enum.c zip
897
928
  case NODE_CREF:
898
- case NODE_IFUNC:
899
929
  // #defines:
900
930
  // case NODE_LMASK:
901
931
  // case NODE_LSHIFT:
@@ -942,7 +972,10 @@ static VALUE parse_tree_for_meth(VALUE klass, VALUE method, VALUE newlines, VALU
942
972
  }
943
973
  if (st_lookup(RCLASS(klass)->m_tbl, id, &n)) {
944
974
  node = (NODE*)n;
945
- rb_ary_push(result, ID2SYM(rb_intern("defn")));
975
+ rb_ary_push(result, ID2SYM(rb_intern(is_cls_meth ? "defs": "defn")));
976
+ if (is_cls_meth) {
977
+ rb_ary_push(result, rb_ary_new3(1, ID2SYM(rb_intern("self"))));
978
+ }
946
979
  rb_ary_push(result, ID2SYM(id));
947
980
  add_to_parse_tree(result, node->nd_body, newlines, NULL);
948
981
  } else {
@@ -1,11 +1,13 @@
1
+ require 'parse_tree'
2
+
3
+ $TESTING ||= false # unless defined $TESTING
4
+
1
5
  ##
2
6
  # Sexps are the basic storage mechanism of SexpProcessor. Sexps have
3
7
  # a +type+ (to be renamed +node_type+) which is the first element of
4
8
  # the Sexp. The type is used by SexpProcessor to determine whom to
5
9
  # dispatch the Sexp to for processing.
6
10
 
7
- $TESTING ||= false # unless defined $TESTING
8
-
9
11
  class Sexp < Array # ZenTest FULL
10
12
 
11
13
  @@array_types = [ :array, :args, ]
@@ -17,6 +19,28 @@ class Sexp < Array # ZenTest FULL
17
19
  super(args)
18
20
  end
19
21
 
22
+ ##
23
+ # Creates a new Sexp for +klass+ or +method+ in +klass+.
24
+ #
25
+ # If +walk_ancestors+ is true and +method+ is provided, walks the ancestors
26
+ # of +klass+ until a method definition is found.
27
+
28
+ def self.for(klass, method = nil, walk_ancestors = false)
29
+ sexp = if walk_ancestors and method then
30
+ klass.ancestors.each do |klass|
31
+ sexp = ParseTree.translate klass, method
32
+ break sexp unless sexp == [nil]
33
+ end
34
+ else
35
+ ParseTree.translate klass, method
36
+ end
37
+
38
+ Sexp.from_array sexp
39
+ end
40
+
41
+ ##
42
+ # Creates a new Sexp from Array +a+, typically from ParseTree::translate.
43
+
20
44
  def self.from_array(a)
21
45
  ary = Array === a ? a : [a]
22
46
 
@@ -44,6 +68,9 @@ class Sexp < Array # ZenTest FULL
44
68
  end
45
69
  end
46
70
 
71
+ ##
72
+ # Returns true if this Sexp's pattern matches +sexp+.
73
+
47
74
  def ===(sexp)
48
75
  return nil unless Sexp === sexp
49
76
  pattern = self # this is just for my brain
@@ -57,6 +84,9 @@ class Sexp < Array # ZenTest FULL
57
84
  return nil
58
85
  end
59
86
 
87
+ ##
88
+ # Returns true if this Sexp matches +pattern+. (Opposite of #===.)
89
+
60
90
  def =~(pattern)
61
91
  return pattern === self
62
92
  end
@@ -97,6 +127,9 @@ class Sexp < Array # ZenTest FULL
97
127
  end
98
128
  end
99
129
 
130
+ ##
131
+ # Replaces all Sexps matching +pattern+ with Sexp +repl+.
132
+
100
133
  def gsub(pattern, repl)
101
134
  return repl if pattern == self
102
135
 
@@ -117,6 +150,9 @@ class Sexp < Array # ZenTest FULL
117
150
  return "s(#{sexp_str})"
118
151
  end
119
152
 
153
+ ##
154
+ # Returns the node named +node+, deleting it if +delete+ is true.
155
+
120
156
  def method_missing(meth, delete=false)
121
157
  matches = find_all { | sexp | Sexp === sexp and sexp.first == meth }
122
158
 
@@ -155,7 +191,7 @@ class Sexp < Array # ZenTest FULL
155
191
  end if $DEBUG or $TESTING
156
192
 
157
193
  ##
158
- # Returnes the bare bones structure of the sexp.
194
+ # Returns the bare bones structure of the sexp.
159
195
  # s(:a, :b, s(:c, :d), :e) => s(:a, s(:c))
160
196
 
161
197
  def structure
@@ -171,6 +207,9 @@ class Sexp < Array # ZenTest FULL
171
207
  result
172
208
  end
173
209
 
210
+ ##
211
+ # Replaces the Sexp matching +pattern+ with +repl+.
212
+
174
213
  def sub(pattern, repl)
175
214
  return repl.dup if pattern == self
176
215
 
@@ -213,6 +252,10 @@ end
213
252
  class SexpMatchSpecial < Sexp; end
214
253
 
215
254
  class SexpAny < SexpMatchSpecial
255
+ def ==(o)
256
+ Sexp === o
257
+ end
258
+
216
259
  def ===(o)
217
260
  return Sexp === o
218
261
  end
@@ -164,25 +164,16 @@ class SexpProcessor
164
164
  end
165
165
 
166
166
  def rewrite(exp)
167
- meth = @rewriters[exp.first]
168
- if meth then
169
- r = self.send(meth, exp)
170
- self.assert_empty(meth, exp, nil) if @require_empty
171
- r
172
- else
173
- result = exp.class.new
174
- until exp.empty? do
175
- sub_exp = exp.shift
176
- sub_result = nil
177
-
178
- if Array === sub_exp then
179
- result << rewrite(sub_exp)
180
- else
181
- result << sub_exp
182
- end
183
- end
184
- result
185
- end
167
+ exp.map! { |sub| Array === sub ? rewrite(sub) : sub }
168
+
169
+ type = exp.first
170
+ begin
171
+ meth = @rewriters[type]
172
+ exp = self.send(meth, exp) if meth
173
+ old_type, type = type, exp.first
174
+ end until old_type == type
175
+
176
+ exp
186
177
  end
187
178
 
188
179
  ##
@@ -221,6 +212,11 @@ class SexpProcessor
221
212
 
222
213
  exp = self.rewrite(exp) if @process_level == 1
223
214
 
215
+ if @debug.has_key? type then
216
+ str = exp.inspect
217
+ puts "// DEBUG (rewritten): #{str}" if str =~ @debug[type]
218
+ end
219
+
224
220
  # now do a pass with the real processor (or generic)
225
221
  meth = @processors[type] || @default_method
226
222
  if meth then