From: "Ara.T.Howard" Date: 2005-09-11T15:11:42+09:00 Subject: r4 - the simplest ruby pre-processor all this talk about pre-processors and erb got me thinking : what would it take to code up a ruby pre-procssors that could be used for any laguange. my first crack is 38 lines of ruby that pre-process any files on the command line (or stdin) using ruby as the macro language and a convenience function 'macro' which can be used to define ruby methods which return test that is used as output. functions declared this way need not do any explicit io. here's some examples: harp:~ > cat input.r4 | | # any valid ruby code can go here | | x = 42 | | # any output produced is used in the generated output | | p x | any lines which are not marked with a leading '|' are copied verbetim to the output | | # since the pre-processor language is ruby it can do anything | | 10.times{|i| p i} | harp:~ > r4 input.r4 42 any lines which are not marked with a leading '|' are copied verbetim to the output 0 1 2 3 4 5 6 7 8 9 and, using r4's macro shortcut to generate c code: harp:~ > cat input.c | | fields = %w( foo bar foobar barfoo ) | | macro('field'){|name| "int #{ name };" } | | macro('setter') do |name| | <<-c | int set_#{ name }(self, value) | object * self; | int value; | { | return( self->#{ name } = value ); | } | c | end | | macro('getter') do |name| | <<-c | int get_#{ name }(self) | object * self; | { | return( self->#{ name } ); | } | c | end struct object { | | fields.each{|f| field f} | }; | | fields.each{|f| setter(f); getter(f); } | harp:~ > r4 input.c struct object { int foo; int bar; int foobar; int barfoo; }; int set_foo(self, value) object * self; int value; { return( self->foo = value ); } int get_foo(self) object * self; { return( self->foo ); } int set_bar(self, value) object * self; int value; { return( self->bar = value ); } int get_bar(self) object * self; { return( self->bar ); } int set_foobar(self, value) object * self; int value; { return( self->foobar = value ); } int get_foobar(self) object * self; { return( self->foobar ); } int set_barfoo(self, value) object * self; int value; { return( self->barfoo = value ); } int get_barfoo(self) object * self; { return( self->barfoo ); } and, finally, the source for r4: #! /usr/bin/env ruby require 'tempfile' script = Tempfile::new(File::basename(__FILE__)) script << DATA.read pat = %r/^\s*\|(.*)$/ hdoc = [] start_hdoc = lambda do if hdoc.empty? script << "puts <<-'" << hdoc.push("___code_#{ rand(2 ** 42) }___") << "'" << "\n" end end end_hdoc = lambda do unless hdoc.empty? script << hdoc.pop << "\n" end end ARGF.each do |line| m = pat.match line if m code = m[1] end_hdoc[] script << code << "\n" else start_hdoc[] script << line end end end_hdoc[] script.close load script.path __END__ class Object def macro(m, &b) klass = (Class === self ? self : (class << self; self; end)) klass.module_eval do define_method(m){|*a| puts b.call(*a)} end end end obivious the char marking pre-processor lines could be anything - but other than that there is special markup. thoughts/comments/flames? -a -- =============================================================================== | email :: ara [dot] t [dot] howard [at] noaa [dot] gov | phone :: 303.497.6469 | Your life dwells amoung the causes of death | Like a lamp standing in a strong breeze. --Nagarjuna ===============================================================================