ParseTree 1.7.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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