せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】【メタプログラミング】言語内DSLの実装。StringScannerを使ったインデントパーサーの実装

 
■topic summary
study about DSL
 

この本に載っているDSLをお勉強。

プログラミング言語 Ruby

プログラミング言語 Ruby

出力のインデントは自作。

 

DSLコード

pagetitle = "xml generate test page"
out = ""
XML.generate(out) do
  html do
    head do
      title{pagetitle}
      comment "this is test"
    end
    body do
      h1(style: "font:red",size: "5px"){pagetitle}
      ul type: "sequre" do
        li {RUBY_VERSION}
        li {Time.now.to_s}
      end
    end
  end
end
XML.show(out)

 
 

出力コード

<html>
  <head>
    <title>
      xml generate test page
    </title>
    <!-- this is test -->
  </head>
  <body>
    <h1 style='font:red' size='5px'>
      xml generate test page
    </h1>
    <ul type='sequre'>
      <li>
        2.0.0
      </li>
      <li>
        2013-11-23 19:38:28 +0900
      </li>
    </ul>
  </body>
</html>

 

 

ポイント

instance_evalに渡すブロックの中にさらにブロックが存在
method_missingを利用(第一引数にメソッド名が渡される)


 

実装

require'strscan'

class XML
  def initialize(out)
    @out = out
  end
  def comment(text)
    @out << "<!-- #{text} -->"
    nil
  end
  def tag(tagname, attribute={})
    @out << "<#{tagname}"
    attribute.each do |key, val|
      @out << " #{key}='#{val}'"
    end
    @out << ">"
    if block_given?
      content = yield
      @out << content if content
    end
    @out << "</#{tagname}>"
    nil
  end

  alias method_missing tag
  def self.generate(out, &b)
    XML.new(out).instance_eval(&b)
  end
  def self.show(text)
    result = ""
    nest_level = 0
    s = StringScanner.new(text)
    while !s.eos?
      if s.scan(/<[^>]+>/)
        if s[0].index("</")
          nest_level -= 1
          result << "\t"*nest_level + s[0] + "\n"
        elsif s[0].index("/>") || s[0].index("<!--") 
          result << "\t"*nest_level + s[0] + "\n"
        else  #開始タグ
          result << "\t"*nest_level + s[0] + "\n"
          nest_level += 1
        end
      else s.scan(/[^<]+/)
        result << "\t"*nest_level + s[0] + "\n"
      end
    end
    print result
  end
end

 


ふむふむ。