But say I do this:
pt = ParseTree.new.parse_tree_for_string("1 + 2 ; 1 * 2")
(string):1: warning: useless use of + in void context
=> [[:block, [:call, [:lit, 1], :, [:array, [:lit, 2]]], [:call,
[:lit, 1], :, [:array, [:lit, 2]]]]]
Ruby2Ruby.new.process(pt.first)
=> "(1 + 2)\n(1 * 2)\n"
What if I just want 1 * 2?
It depends on what sort of '1 * 2' you want... Do you want the last
expression in a block/defn/whatever? Like, are you trying to analyze
what the return type is? Or do you want to be aware of that void
context warning and discard the crap? Or do you simply want the second
thingy? Or do you want something entirely different that I am not
anticipating?
Well, I'm writing a code generator, and I thought I was very close to
finished, except I was basically doing this:
generated_code = Generators.module_eval(&block)
And that works great for tiny bits of code, it works at the statement
level, but when I want to just collect up *all* the statements in a
block, the whole thing kinds of goes south. So I have something which
generates all the individual statements perfectly but completely falls
apart when I want to create several statements. So it looks as if my
only option might be to rewrite everything, possibly adding a parser,
or using Ruby2C, or something like that. It's kind of a pain in the
ass.
But if I have code like this:
single_line(statement)
statement

which => spans,
:multiple => lines)
And I can turn it into an array of procs, or blocks, or even strings,
then I can just do
code_chunks.each {|code_chunk| @code << Generators.module_eval(code_chunk)}
Or something similar, and harness everything I have so far without
throwing it all away and starting over from scratch or close to it.
All I really need at that point is a collection of code chunks, in
some format which module_eval will be comfortable with.
There are a couple ways to go about it, depending on your answer
above. The easiest is Sexp/Array manipulation. Let's say you were
interested in the arg list of the last call in the block:
This is a bad example because it has two calls in the block. With
unique sub-sexp types in a sexp, you can just pull them out by name:
But it balks when it is ambiguous.
But... if that isn't what you want, there are more powerful means of
dealing with stuff:
class CallArgAnalyzer < SexpProcessor
def process_call sexp
sexp.shift # :call
recv = sexp.shift
name = sexp.shift
args = sexp.shift
# do something with args
return s

nil) # or whatever... depends on context
end
end
Granted, this doesn't actually DO anything, but the framework to do
something is there and solid... Do note that SexpProcessor is meant
for transformational processing so it generally expects a sexp back
(you can set what the return type should be for all process_*
methods). The real point is that it is very easy to get at what you
want and mess with it.
Very easy is a relative term.
In practice none of the code I'm working with or expecting to work
with has more than one statement on a line, but obviously the whole
problem is that it has many statements in a block.
The thing is, I posted a few days ago about a problem with if, not
being able to override it, and if I'm using a fully-fledged parsing
library then I might as well undo my kludge and enable real support
for conditionals like if, unless, while, etc. But it seems like more
work than I'd intended. But I can't deny that being able to use real
Ruby in the code which generates the other code would be a serious
improvement.
--
Giles Bowkett
Podcast:
http://hollywoodgrit.blogspot.com
Blog:
http://gilesbowkett.blogspot.com
Portfolio:
http://www.gilesgoatboy.org
Tumblelog:
http://giles.tumblr.com