Forum: Ruby meta_parse: my first metaprogram

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Edgardo H. (Guest)
on 2006-02-19 19:57
(Received via mailing list)
Hi guys,

After messing up with racc for a couple of hours I realized that most of
the
parse functions look pretty much the same, so I thought of stretching my
metaprogramming muscles and I wrote my first meta_parse function.

require  'strscan'

def meta_parse(*ary)
  case_str = <<ENDSTRING
    def parse(str)
      @q = []
      scanner = StringScanner.new str
      until scanner.empty?
        case
ENDSTRING

  ary.each do |type, exp, meth|
    if meth
      case_str << "         when m = scanner.scan(#{exp}) ; @q <<
[:#{type},
m.#{meth}]\n"
    else
      case_str << "         when m = scanner.scan(#{exp}) ; @q <<
[:#{type},
m]\n"
    end
  end

case_str <<<<END_STRING
        end
      end
      @q << [false,false]
      do_parse
    end
END_STRING
eval case_str
end

Here is a usage example:

meta_parse [:A, '/a/'], [:NUMBER, '/\d+/', :to_i], [:WS, '/(\s|\t)+/']
parse("a   aaa 12132 ").each{|type,val| puts "#{type}: '#{val}'"}

I would like to hear your comments on it, but please be gentle ;-)

Cheers,
Ed
--
Encontrá a "Tu psicópata favorito" http://tuxmaniac.blogspot.com

Thou shalt study thy libraries and strive not to reinvent them without
cause,
that thy code may be short and readable and thy days pleasant and
productive.
-- Seventh commandment for C programmers
Erik V. (Guest)
on 2006-02-19 20:39
(Received via mailing list)
> After messing up with racc for a couple of hours I realized
> that most of the parse functions look pretty much the same,
> so I thought of stretching my metaprogramming muscles and I
> wrote my first meta_parse function.
>
> I would like to hear your comments on it, but please be
> gentle ;-)

I usually try to avoid those inline strings. I don't like them.
As long as you don't pass a block in the call to the
dynamically defined method, you can use "define_method"
instead. I moved a bit of code and came up with the code below.

(Next time, please provide runnable code... )

gegroet,
Erik V. - http://www.erikveen.dds.nl/

----------------------------------------------------------------

 require  'strscan'

 def meta_parse(*ary)
   self.class.module_eval do
     define_method :parse do |str|
       @q = []
       scanner = StringScanner.new(str)
       until scanner.empty?
         ary.each do |type, exp, meth|
           if m = scanner.scan(exp)
             @q << [type, (meth ? m.send(meth) : m)]
           end
         end
       end
       @q << [false,false]
       do_parse
     end
   end
 end

 def do_parse
   p $q
   @q
 end

 meta_parse [:A, /a/], [:NUMBER, /\d+/, :to_i], [:WS, /(\s|\t)+/]
 parse("a   aaa 12132 ").each{|type,val| puts "#{type}: '#{val}'"}
David V. (Guest)
on 2006-02-19 20:54
(Received via mailing list)
> I usually try to avoid those inline strings. I don't like them.
> As long as you don't pass a block in the call to the
> dynamically defined method, you can use "define_method"
> instead.
>

Seconded, metaprogramming code like that will get a major performance
hit if
you whack YARV at it. Unless there's a shaman embedding voodoo chants
into
YARV code for optimization of variable evals.

(Sidenote: it seems #eval is slightly faster than #define_method in
trivial
benchmarks.)

David V.
Christian N. (Guest)
on 2006-02-19 21:55
(Received via mailing list)
"Erik V." <removed_email_address@domain.invalid> writes:

> dynamically defined method, you can use "define_method"
> instead. I moved a bit of code and came up with the code below.

I'd just return a block for this case, though.

>  def meta_parse(*ary)
>    self.class.module_eval do
>      define_method :parse do |str|
...
This topic is locked and can not be replied to.