=begin
content =<<-ENDTXT
<ul>
  <r:documents>
  <li><r:icon/><r:name/></li>
  </r:documents>
</ul>
ENDTXT
==>
result =<<-ENDTXT
<ul>
  <% @node.documents.each do |document| -%>
  <li><%= @node.icon ? icon.img_tag('pv') : '' %><%= document.name %></li>
  <% end -%>
</ul>
ENDTXT

rules :
=end

class Zafu
  def expand(text = @context[:inner])
    # scan to first opening <r:...> tag
    if text =~ /(.*?)<r:([^>]+)>(.*)/m
      out  << $1.strip
      rest = $3
      tag  = $2
      if tag[-1..-1] == '/'
        sym = tag[0..-2].to_sym
      elsif rest =~ /(.*)<\/r:#{tag}>(.*)/m
        sym   = tag.to_sym
        inner = $1
        rest  = $2
      else
        raise Exception.new
      end
      out << call_with(sym, nil, :inner=>inner)
      out << rest.strip
    end
  end
  
  def expand_with(new_context)
    context = @context.dup
    content = @content.dup
    @content = ""
    @context.merge!(new_context)
    res = expand
    res = @content unless res.kind_of?(String)
    @content = content
    @context = context
    res
  end
  
  def call_with(sym, params, new_context)
    @context ||= {}
    context = @context.dup
    content = @content.dup
    @content = ""
    @context.merge!(new_context)
    res = self.send(sym,params)
    res = @content unless res.kind_of?(String)
    @content = content
    @context = context
    res
  end
  
  def out
    self
  end

  def <<(str)
    @content ||= ""
    @content << str
  end

  def documents(params)
    out << "<% #{node}.documents.each do |document| -%>\n"
    out << expand_with(:node=>'document') + "\n"
    out << "<% end -%>\n"
  end

  def name(params)
    out << "<%= #{node}.name %>"
  end
  
  def node
    @context[:node] || '@node'
  end
end

res = Zafu.new.expand <<-ENDTXT
<ul>
  <r:documents>
  <li><r:name/></li>
  </r:documents>
</ul>
ENDTXT

puts res
