【Ruby】【メタプログラミング】言語内DSLの実装。StringScannerを使ったインデントパーサーの実装
■topic summary
study about DSL
この本に載っているDSLをお勉強。
- 作者: まつもとゆきひろ,David Flanagan,卜部昌平(監訳),長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/01/26
- メディア: 大型本
- 購入: 21人 クリック: 356回
- この商品を含むブログ (124件) を見る
出力のインデントは自作。
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
ふむふむ。