Rubyで簡単DSL

2008-03-21

階層を持つ設定ファイルを扱いたいけど、パースしたりするのはメンドイ。 そんなとき Ruby だったら簡単に DSL で作れる。 例えばアニメーションデータみたいなの

anim("test") {
play "intropat"
loop(3) {
play "looppat"
}
play "endpat"
}

こんな感じの設定ファイルだとして、読み込むためのルーチンはこんな感じ:

$Temp = []

# 要素を追加
def add(data)
$Temp.push data
end

# 階層を作る
def block(&blk)
temp = $Temp # いったん退避
$Temp = [] # 新しくバッファを作成
blk.call # ブロック呼び出し
r = $Temp # 結果を取り出す
$Temp = temp # バッファを元の状態に戻す
r # 結果を返す
end

# あるデータの再生コマンド
def play(name)
add [:play, name]
end

# 区間のループコマンド
def loop(num, &blk)
stmts = block {blk.call}
add [:loop, num, stmts]
end

# アニメーション
def anim(fn, &blk)
stmts = block {blk.call}
add [:anim, fn, stmts]
end

# 読み込む
def parse_anim(s)
block {
eval(s)
}
end

これだけで

parse_anim(File.read("test.anm"))
;=> [[:anim, "test", [[:play, "intropat"], [:loop, 3, [[:play, "looppat"]]], [:play, "endpat"]]]]

てな感じで読み込めて、あとは好きなように加工できる。 設定ファイルで文字列をクォーテーションでくくらないといけないのがアレだけど、それ以外は相当シンプルでよい DSL だと思う。 プログラム書くのも超簡単だし。 実際にはクラスにするのがいいかな。

Rubyは

  • 関数の括弧が省略できる
  • 関数の後ろに中括弧 { … } で囲ってブロックを渡せる
  • 行末にセミコロンとかをつけなくていい

というところがいいね。