Lisper says to rubyist: gimmie your syntax tree

J

jeff.sykes.rb

module Tracer
if $DEBUG
def trace(str)
STDERR.puts(str)
end
else
def trace(str)
end
end
end

class Application
include Tracer

def initialize
trace "initializing..."
# ...
end

def run
trace "running..."
# ...
end
end

I have unit tests to confirm things are working well, etc, however in
complex multi-threaded code there's no substitute for the trace output
when problems arise. However even an empty method call is significant,

require 'benchmark'
include Tracer
Benchmark.bm(16) { |b|
n = 10_000_000
b.report("with trace") { n.times { trace } }
b.report("without trace") { n.times { } }
}

user system total real
with trace 3.090000 0.000000 3.090000 ( 3.094240)
without trace 1.010000 0.000000 1.010000 ( 1.015994)

So ... why not ...

# Manipulate the AST
SyntaxTree.each_node { |node|
if node.ast_type == :method_call and node.method_name == "trace"
node.ast_type = :noop # or remove it, or whatever
end
}

# No more trace statements!
Application.new.run

Now I could gsub out those trace statements manually (or for a cleaner
approach maybe ParseTree on RubyForge could be used). But that extra
step is cumbersome compared to the magic I desire. If SyntaxTree were
implemented, I think we would be close to having that bit of power
which lisp provides but ruby does not. Removing trace statements
would only be a trivial use of this Ultimate Power!

In my example SyntaxTree is using the code running in memory, but in
principle I could say

code = SyntaxTree.entry_point { Application.new.run }
code.each_node { |node|
# ... manipulate the code ...
}
# Execute some alternate-universe version of Application.new.run
code.call

Now THAT would be getting lispy.
 
L

Le Lann Jean-Christophe

(e-mail address removed) a écrit :
So ... why not ...

# Manipulate the AST
SyntaxTree.each_node { |node|
if node.ast_type == :method_call and node.method_name == "trace"
node.ast_type = :noop # or remove it, or whatever
end
}
I am also looking for something similar in Ruby (programNodeEnumerator
of Smalltalk is so great !)

I've found these remarks :

-http://www.jroller.com/murphee/entry/ruby_let_s_get_an
-http://r2.ifs.hsr.ch/rubyrefactoring.pdf

Hope this helps.
JC
 
P

Paul Brannan

Tracer doesn't inject "trace statements" into the AST. It calls
set_trace_func, so there's a callback for every event (method call,
method return, new line, etc.).

The performance bottleneck of Tracer is that one of the arguments to the
trace function is a binding; creating this binding is an expensive
operation. AFAIK the only way to avoid this is to use the C API to call
rb_add_event_hook instead of using set_trace_func from Ruby. This is
what the ruby-debug gem does, e.g.:

rdebug --trace test.rb

As for your idea of dynamically modifying program structure at run-time,
I've toyed with this idea, but it's not trivial. The biggest hurdle is
Ruby's handling of local/dynamic variables. IMO the AST is too
low-level a program representation to be used for this. An intermediate
representation that abstracts away some of the details of the AST is I
think what's needed.

Paul
 
R

Ryan Davis

An intermediate representation that abstracts away some of the
details of the AST is I think what's needed.

Yup yup.

I'm going this direction with the UnifiedRuby rewriter in ParseTree
(which I will probably extract into its own release), but it is
currently still too low level. I'm working on pulling it up a bit and
making it the default output from ParseTree and ruby_parser. There
will still be "raw" versions you can call.
 
P

Paul Brannan

I'm going this direction with the UnifiedRuby rewriter in ParseTree
(which I will probably extract into its own release), but it is
currently still too low level. I'm working on pulling it up a bit and
making it the default output from ParseTree and ruby_parser. There
will still be "raw" versions you can call.

Nifty!

This will be very powerful when you are done. I wish I had the time...
:)

Paul
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,152
Latest member
LorettaGur
Top