On Tue, 20 Jun 2006 07:42:31 +0200, Logan C.
[email protected]
wrote:
MMm. I’ll be honest, I was kind of disappointed. I was hoping for a
response like “Yes, but ParseTree has limitation or uses style,
and RubyNode has a implementation” :). Ah well.
Then you should have asked for a comparison ;-). Anyway:
When I started working on Ruby2CExtension (around February 2006), I
intended to work with Ruby 1.9 and ParseTree didn’t support 1.9 back
then.
So I looked for alternatives.
I first found Ripper, which works in 1.9, but doesn’t return the exact
node tree. For example “1+1” produces something like [:program,
[[:binary,
int(1), :+, int(1)]]], while with RubyNode you get [:call, {:mid=>:+,
:recv=>[:lit, {:lit=>1}], :args=>[:array, [[:lit, {:lit=>1}]]]}]. And
Ripper also is work in progress and has some bigger problems for example
with here documents.
Then I found Nodewrap, which worked really nice. But as I progressed
with
Ruby2CExtension I found that Nodewrap had some problems with some node
types, I sent some patches to Paul B. and he was actually working
on
a new release, but didn’t have enough time.
So I finally wrote my own node tree accessing library. It had the
following design goals:
-as simple as possible, easily maintainable
-read only (because I saw that Nodewrap had to jump through lots of
hoops
to allow write access)
-compatible to different Ruby versions
-low level and high level access
-get as much information about the node types as possible by parsing
Ruby
source code
Because of the last point RubyNode is not easily gemifyable, but on the
other hand it should easily adapt to changes in Ruby even without
changing
the RubyNode source code (at least it should never segfault).
Some things that RubyNode can do and ParseTree currently cannot:
-Low level access:
ParseTree only gives you the s-exps, with RubyNode you can get the flags
field, the line number and the filename of the node. You can also get
each
the raw long value of each union if you really want, and so on.
-Access node trees of procs
-Parse arbitrary strings of Ruby code to node trees without evaling
them:
ParseTree only allows evaling code and then only provides access to
method
node trees, with RubyNode you can just do:
irb(main):001:0> pp “p 1;class A; def foo;42;end;end;p
2”.parse_to_nodes.transform
[:block,
[[:fcall, {:mid=>:p, :args=>[:array, [[:lit, {:lit=>1}]]]}],
[:class,
{:body=>
[:scope,
{:next=>
[:defn,
{:mid=>:foo,
:defn=>
[:scope,
{:next=>
[:block,
[[:args, {:rest=>-1, :opt=>false, :cnt=>0}],
[:lit, {:lit=>42}]]],
:rval=>false,
:tbl=>nil}],
:noex=>2}],
:rval=>false,
:tbl=>nil}],
:super=>false,
:cpath=>[:colon2, {:mid=>:A, :head=>false}]}],
[:fcall, {:mid=>:p, :args=>[:array, [[:lit, {:lit=>2}]]]}]]]
This feature is actually quite simple and I think it should be added to
ParseTree.
As you can see, the pretty printed node tree above is a bit verbose, but
the hashes are IMO much more flexible and nicer than accessing the
attributes by position.
RubyNode doesn’t have an equivalent to SexpProcessor, but it is easy
enough to make your own, as I did for Ruby2CExtension. Example:
class NodeProcessor
def process(node)
case node
when false
“Qnil”
else
begin
send(“process_#{node.first}”, node.last)
rescue
# handle
end
end
end
def process_class(hash)
# …
end
…
end
I hope this answers all your questions.
And while I am at it: Ruby2CExtension vs. ZenObfuscate:
I can’t really compare them because I don’t have access to ZenObfuscate,
but from the announcement:
- Known Limitations
There are issues with what the obfuscator can translate to C
and as a result you may need to modify your code in order to
translate it. Usually this is a pretty straightforward and
simple task. We do a good job of translating static ruby to
its equivalent C, but not all ruby has an equivalent in C.
- Only translates methods in classes and modules, not
freestanding code.
Ruby2CExtension translates free standing code.
- Explicit returns are required in all methods.
Ruby2CExtension doesn’t require those.
- Temporary: Conditional logic (including ?:) may not be on the
right hand side of an assignment.
No problem in Ruby2CExtension
- Temporaryish: Exception handling and generic block closures
currently don't translate.
They do in Ruby2CExtension (except for some things described at
http://ruby2cext.rubyforge.org/limitations.html#section10)
- Some expressions in ruby we don't currently do, but could
upon request, where some other ruby expressions will never
translate.
I am not sure what Ryan means here, but Ruby2CExtension can translate
arbitrary Ruby expressions (again except for things described at
http://ruby2cext.rubyforge.org/limitations.html)
And Ruby2CExtension is free.
Dominik