So... drbrain comes up to me in an IM and says flgr is saying it'd be really cool if you could ask a method for its source. I know what he is doing, baiting me like that, but I play along anyways to see what the outcome is like. drbrain and I talked about it and thought it'd be really cool if our ruby2c system added a to_c method to the Method class. That isn't hard at all really, so we added:
class Method
# with_class_and_method_name is a silly method.
# Implementation is an exercise for the reader.
def to_sexp
with_class_and_method_name do |klass, method|
ParseTree.new.parse_tree_for_method(klass, method)
end
end
def to_c
with_class_and_method_name do |klass, method|
RubyToC.translate(klass, method)
end
end
end
But the question came up... can we do this to display ruby code? The answer is yes, and it only took me about 30 minutes to get the proof of concept up and running. First, the example code:
class Example
def example(arg1)
return "Blah: " + arg1.to_s
end
end
e = Example.new
puts "sexp:"
p e.method(:example).to_sexp
puts "C:"
puts e.method(:example).to_c
puts "Ruby:"
puts e.method(:example).to_ruby
and now the output:
sexp:
[:defn, :example, [:scope, [:block, [:args, :arg1], [:return, [:call, [:str, "Blah: "], :+, [:array, [:call, [:lvar, :arg1], :to_s]]]]]]]
C:
str
example(long arg1) {
return strcat("Blah: ", to_s(arg1));
}
Ruby:
def example(arg1)
return "Blah: " + arg1.to_s
end
Cool huh? I can now translate any method to C or get the ruby code for it (sans-comments unfortunately) simply by calling to_c or to_ruby on the method itself!
RubyToRuby.rb
require 'parse_tree'
require 'support'
require 'sexp_processor'
class RubyToRuby < SexpProcessor
def self.translate(klass, method=nil)
unless method.nil? then
self.new.process(ParseTree.new.parse_tree_for_method(klass, method))
else
self.new.process(ParseTree.new.parse_tree(klass))
end
end
def initialize
super
@env = Environment.new
self.auto_shift_type = true
self.strict = true
self.expected = String
end
def process_args(exp)
args = []
until exp.empty? do
args << exp.shift
end
return "(#{args.join ', '})"
end
def process_array(exp)
code = []
until exp.empty? do
code << process(exp.shift)
end
return code.join(", ")
end
def process_block(exp)
code = []
until exp.empty? do
code << process(exp.shift)
end
body = code.join("\n")
body += "\n"
return body
end
def process_call(exp)
receiver = process exp.shift
name = exp.shift
args = process exp.shift
case name
when :<=>, :==, :<, :>, :<=, :>=, :-, :+, :*, :/, :% then #
"#{receiver} #{name} #{args}"
when :[] then
"#{receiver}[#{args}]"
else
"#{receiver}.#{name}#{args}"
end
end
def process_defn(exp)
name = exp.shift
args = process exp.shift
body = process exp.shift
return "def #{name}#{args}#{body}end".gsub(/\n\n+/, "\n")
end
def process_fcall(exp)
exp_orig = exp.deep_clone
# [:fcall, :puts, [:array, [:str, "This is a weird loop"]]]
name = exp.shift.to_s
args = exp.shift
code = []
unless args.nil? then
assert_type args, :array
args.shift # :array
until args.empty? do
code << process(args.shift)
end
end
return "#{name}(#{code.join(', ')})"
end
def process_lvar(exp)
exp.shift.to_s
end
def process_return(exp)
return "return #{process exp.shift}"
end
def process_scope(exp)
return process(exp.shift)
end
def process_str(exp)
return "\"#{exp.shift}\""
end
def process_vcall(exp)
return exp.shift.to_s
end
def process_while(exp)
cond = process(exp.shift)
body = process(exp.shift)
post = exp.empty? ? false : exp.shift
code = []
unless post then
code << "while #{cond} do"
code << body
code << "end"
else
code << "begin"
code << body
code << "end while #{cond}"
end
body = code.join("\n")
return body
end
end

Wow!
I took and hacked it further, there's a little self-test at the bottom of the file:
http://dark.fhtr.org/ruby2ruby.rb