Tom said:
And Ruby does have ParseTree.
BTW does ParseTree work with ruby 1.9? The last time I tried it
didn't.
Anyway, ruby isn't really made for that kind of thing. If you look at
the example at their homepage, the :scope and :block node seem to be
superfluous when dealing with code as data, and something like
"[:call, [:lvar, :arg1], :==, [:array, [:lit, 0]]]" also seems more
complicate than it could be. I think it's a good start but it doesn't
really match lisp capabilities in this respect where there is no
structural difference between the code and the data that represents
that code. That syntax most people in this thread were mocking has its
advantages and if you're denying that, the difference to "those cl
advocates" is only a marginal one.
Maybe you should rather ask if in ruby it is really necessary to
manipulate code as data the way it is necessary in lisp. With a few
exceptions, I have seldom felt a need for it whereas in lisp the use
of macros is something you do all the time.
Auto-meta-programming is only part of the picture.
Of course, when programming in Lisp, we use auto-meta-programming
obliviously all the time, but there are also a lot of lisp programs
written to implement DSL or to generate inferior languages. For these
heterogenous meta- programming applications, Ruby can be used too.
Here is for example how I generate C++ code from Ruby:
(class MapType
(def makeToStringExpression(fieldAccess , mandatory , inVector = false , encoding = nil)
(if mandatory
(e = fieldAccess)
(ib = [["." , e , :begin]])
(ie = [["." , e , :end]])
else
(e = [[ "." , fieldAccess , :getValuePtr]])
(ib = [["->" , e , :begin]])
(ie = [["->" , e , :end]])
end)
(k = (@keyType . makeToStringExpression(["->" , :i , :first] , true , encoding)))
(v = (@elementType . makeToStringExpression(["->" , :i , :second] , true , encoding)))
(s = [:block ,
[:inline , "typedef " + (self . cxxPath) + " map_type;"] ,
(Cxx::Variable . new
i , (Cxx::Type . new((self . cxxPath)+"::"+"const_iterator")) , ib)) ,
(Cxx::Variable . new
e , (Cxx::Type . new((self . cxxPath)+"::"+"const_iterator")) , ie)) ,
[ "<<" , :s , " (map"] ,
[:for , [ nil , ["!=" , :i , :e],["post++",:i]] ,
[:block , ["<<", :s , " ("] , k , ["<<", :s , " . "] , v , ["<<", :s , ")"]]] ,
[ "<<" , :s , ")"]])
end)
end)
and how I would do it from Lisp:
(defmethod make-to-string-expression ((self map-type) field-access mandatory &optional in-vector encoding)
(let ((f (if mandatory field-access `(\. ,field-access get-value-ptr))))
`(block
(typedef ,(cxx-path self) map-type)
(variable (scope ,(cxx-path self) const-iterator) i (,(if mandatory '\. '->) ,f begin))
(variable (scope ,(cxx-path self) const-iterator) e (,(if mandatory '\. '->) ,f end))
(<< s "(map ")
(for (nil (!= i e) (post++ i))
(block
(<< s " (")
,(make-to-string-expression (key-type self) `(-> i first) t encoding)
(<< s " . ")
,(make-to-string-expression (key-type self) `(-> i second) t encoding)
(<< s ")")))
(<< s ")"))))
(The generator written in Ruby cuts off the C++ syntax at the level of
types and variable expressions, hence the inline and instanciations.
In Lisp, since I could use auto-meta-programming I could easily and
in less time implement the syntax for all the parts of C++, so
there's no need to cross the language boundaries.)