Class: ParseTree

Inherits:
Object show all
Defined in:
lib/parse_tree.rb

Overview

ParseTree is a RubyInline-style extension that accesses and traverses the internal parse tree created by ruby.

class Example
  def blah
    return 1 + 1
  end
end

ParseTree.new.parse_tree(Example)
=> [[:class, :Example, :Object,
       [:defn,
         "blah",
         [:scope,
           [:block,
             [:args],
             [:return, [:call, [:lit, 1], "+", [:array, [:lit, 1]]]]]]]]]

Constant Summary collapse

VERSION =
'2.2.0'
NODE_NAMES =
[
 #  00
 :method, :fbody, :cfunc, :scope, :block,
 :if, :case, :when, :opt_n, :while,
 #  10
 :until, :iter, :for, :break, :next,
 :redo, :retry, :begin, :rescue, :resbody,
 #  20
 :ensure, :and, :or, :not, :masgn,
 :lasgn, :dasgn, :dasgn_curr, :gasgn, :iasgn,
 #  30
 :cdecl, :cvasgn, :cvdecl, :op_asgn1, :op_asgn2,
 :op_asgn_and, :op_asgn_or, :call, :fcall, :vcall,
 #  40
 :super, :zsuper, :array, :zarray, :hash,
 :return, :yield, :lvar, :dvar, :gvar,
 #  50
 :ivar, :const, :cvar, :nth_ref, :back_ref,
 :match, :match2, :match3, :lit, :str,
 #  60
 :dstr, :xstr, :dxstr, :evstr, :dregx,
 :dregx_once, :args, :argscat, :argspush, :splat,
 #  70
 :to_ary, :svalue, :block_arg, :block_pass, :defn,
 :defs, :alias, :valias, :undef, :class,
 #  80
 :module, :sclass, :colon2, :colon3, :cref,
 :dot2, :dot3, :flip2, :flip3, :attrset,
 #  90
 :self, :nil, :true, :false, :defined,
 #  95
 :newline, :postexe, :alloca, :dmethod, :bmethod,
 # 100
 :memo, :ifunc, :dsym, :attrasgn,
 :last
]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(include_newlines = $DEBUG) ⇒ ParseTree

Initializes a ParseTree instance. Includes newline nodes if include_newlines which defaults to $DEBUG.



81
82
83
# File 'lib/parse_tree.rb', line 81

def initialize(include_newlines=$DEBUG)
  @include_newlines = include_newlines
end

Class Method Details

.has_allocaObject



190
191
192
# File 'lib/parse_tree.rb', line 190

def self.has_alloca
  true
end

.translate(klass_or_str, method = nil) ⇒ Object

Front end translation method.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/parse_tree.rb', line 50

def self.translate(klass_or_str, method=nil)
  pt = self.new(false)
  case klass_or_str
  when String then
    sexp = pt.parse_tree_for_string(klass_or_str).first
    if method then
      # class, scope, block, *methods
      sexp.last.last[1..-1].find do |defn|
        defn[1] == method
      end
    else
      sexp
    end
  else
    unless method.nil? then
      if method.to_s =~ /^self\./ then
        method = method.to_s[5..-1].intern
        pt.parse_tree_for_method(klass_or_str, method, true)
      else
        pt.parse_tree_for_method(klass_or_str, method)
      end
    else
      pt.parse_tree(klass_or_str).first
    end
  end
end

Instance Method Details

#parse_tree(*klasses) ⇒ Object

Main driver for ParseTree. Returns an array of arrays containing the parse tree for klasses.

Structure:

[[:class, classname, superclassname, [:defn :method1, ...], ...], ...]

NOTE: v1.0 - v1.1 had the signature (klass, meth=nil). This wasn’t used much at all and since parse_tree_for_method already existed, it was deemed more useful to expand this method to do multiple classes.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/parse_tree.rb', line 98

def parse_tree(*klasses)
  result = []
  klasses.each do |klass|
    klassname = klass.name rescue '' # HACK klass.name should never be nil
                                 # Tempfile's DelegateClass(File) seems to
                                 # cause this
    klassname = "UnnamedClass_#{klass.object_id}" if klassname.empty?
    klassname = klassname.to_sym

    code = if Class === klass then
             sc = klass.superclass
             sc_name = ((sc.nil? or sc.name.empty?) ? "nil" : sc.name).intern
             [:class, klassname, [:const, sc_name]]
           else
             [:module, klassname]
           end

    method_names = []
    method_names += klass.instance_methods false
    method_names += klass.private_instance_methods false
    # protected methods are included in instance_methods, go figure!

    method_names.sort.each do |m|
      r = parse_tree_for_method(klass, m.to_sym)
      code << r
    end

    klass.modules.each do |mod| # TODO: add a test for this damnit
      mod.instance_methods.each do |m|
        r = parse_tree_for_method(mod, m.to_sym)
        code << r
      end
    end

    klass.singleton_methods(false).sort.each do |m|
      code << parse_tree_for_method(klass, m.to_sym, true)
    end

    result << code
  end
  return result
end

#parse_tree_for_method(klass, method, is_cls_meth = false, verbose = true) ⇒ Object

Returns the parse tree for just one method of a class klass.

Format:

[:defn, :name, :body]


148
149
150
151
152
153
154
155
# File 'lib/parse_tree.rb', line 148

def parse_tree_for_method(klass, method, is_cls_meth=false, verbose = true)
  $stderr.puts "** parse_tree_for_method(#{klass}, #{method}):" if $DEBUG
  old_verbose, $VERBOSE = $VERBOSE, verbose
  r = parse_tree_for_meth(klass, method.to_sym, is_cls_meth)
  r
ensure
  $VERBOSE = old_verbose
end

#parse_tree_for_str0(*__1args2__) ⇒ Object

:nodoc:



172
173
174
# File 'lib/parse_tree.rb', line 172

def parse_tree_for_str0(*__1args2__) # :nodoc:
  parse_tree_for_str(*__1args2__)    # just helps clean up the binding
end

#parse_tree_for_string(source, filename = '(string)', line = 1, verbose = true) ⇒ Object

Returns the parse tree for a string source.

Format:

[[sexps] ... ]


164
165
166
167
168
169
170
# File 'lib/parse_tree.rb', line 164

def parse_tree_for_string(source,
                          filename = '(string)', line = 1, verbose = true)
  old_verbose, $VERBOSE = $VERBOSE, verbose
  return parse_tree_for_str0(source, filename, line)
ensure
  $VERBOSE = old_verbose
end