第36回 Ruby/Rails勉強会@関西に参加したので Racc を使ってみる

第36回 Ruby/Rails勉強会@関西に参加した。その中で「Racc でおてがる構文解析」というのがあったのでさっそく試してみた。

Racc とは

Racc とはパーサージェネレータの一種で、yaccRuby 版。僕もあんまりよく分かってないけど、とりあえず使ってみることにする。詳しくは Racc ユーザマニュアル参照。

Racc のインストール

gem で Racc をインストールする。

gem install racc

ここで gem install rack と間違えててだいぶはまった。

あと、 僕は Ubuntu を使ってて mkmf がないよ、とかそんなエラーが出てきたので、 apt-get install ruby1.8-dev してからもう一度 gem コマンドでインストールした。

拡張子 y のファイルを書く

Rubyist Magazine - Ruby Library Report 【第 6 回】 正規表現と構文解析 の 「試用レポート -- racc」 というところを参考にして書いてみた。

四則演算をする文法ファイル。calc.y という名前でファイルを作った。

class Calc
  token NUM
prechigh
  left '*' '/'
  left '+' '-'
preclow
rule
  expr : expr '+' expr { result = val[0] + val[2] }
       | expr '-' expr { result = val[0] - val[2] }
       | expr '*' expr { result = val[0] * val[2] }
       | expr '/' expr { result = val[0] / val[2] }
       | prim          { result = val[0] }

  prim : NUM          { result = val[0] }
       | '(' expr ')' { result = val[1] }
end

---- inner
  def parse(str)
    @tokens = str.split(/([\(\)\+\-\*\/])/).select{|c| !c.nil? && c != ""}
    do_parse()
  end

  def next_token()
    token = @tokens.shift()
    case token
    when /^\d+\z/
      return [:NUM, token.to_i]
    when nil
      return nil
    else
      return [token, nil]
    end
  end

写経した。

パーサークラスを作る

以下のコマンドで y ファイルからパーサークラスを作る。これで新しく calc.rb ファイルが作成される。

racc -o calc.rb calc.y

ちらっとパーサークラスの中身を開いてみた。何か黒いものを見てしまったような気がした。

実際に使ってみる

ここまできたら、あとは使うだけ。

require 'calc'

calc = Calc.new()

puts calc.parse("1+2*3")
# かけ算が優先されるはず

puts calc.parse("3*(8+5*(6-2))/2")
# かっこの中が先に計算されるはず
# 3 * (8 + 5 * 4) / 2 = 3 * 28 / 2 = 42

実行してみる。

% ruby do_calc.rb
7
42

うん、ちゃんと計算できてる。

まとめ

なんか結構難しいと思う。でも雰囲気はつかめた。とりあえずこういうものもあることを覚えておこう。

あと、スーパープレ記法で Racc ファイルがちゃんとハイライトされた。うれしい。