HaskellのParsecを,Rubyで

こんなのがあったんだなぁ.名前は,『rparsec』.

rparsec is the Haskell Parsec implemented in Ruby.
(中略)
Feature hightlight:
Operator precedence grammar
Dynamic grammar, context-sensitive grammar.

A calculator parser takes 10 lines of code; A sophisticated sql language parser takes only about 250 lines of code.

ということで,ふつけるを読んだ人なら皆知っているであろう,HaskellのParsecライブラリをRubyで実装しましたよ,という代物.電卓なら10行で,洗練されたSQLパーサですらたったの250行で書けるという凄まじさ.コレを使えばWikiエンジンもスッキリと実装できるかもしれません.


また,gemではないので,newgemでも使って勝手にgem化するのもアリです.ちなみにボクはnewgemを使ってgem化,インストールしましたが,5分もかかりませんでした.作者もgem化すりゃ良いのに*1

簡単なデモ

コチラを見ながら.
http://jparsec.codehaus.org/Ruby+Parsec
簡単なデモなので,実際にこの電卓パーサが動くかどうかを試してみましょう.

# calc.rb
require 'rubygems'
require_gem 'rparsec'

class Calculator
  include Parsers
  include Functors
  def parser
    ops = OperatorTable.new.
      infixl(char(?+) >> Plus, 20).
      infixl(char(?-) >> Minus, 20).
      infixl(char(?*) >> Mul, 40).
      infixl(char(?/) >> Div, 40).
      prefix(char(?-) >> Neg, 60)
    expr = nil
    term = integer.map(&To_i) | char('(') >> lazy{expr} << char(')')
    delim = whitespace.many_
    expr = delim >> Expressions.build(term, ops, delim)
  end
end

ParsersとFunctorsをincludeしているようです.果たして何者なのでしょうか.では,irbで走らせてみます.

$ irb
irb(main):001:0> require 'calc.rb'
=> true
irb(main):002:0> Calculator.new.parser.parse '1+2*(3-1)'
=> 5
irb(main):003:0> Calculator.new.parser.parse '1+2/(3-1)'
=> 2

ちゃんと計算できています.

大雑把に見る

RDocを見てみると,なるほど.たとえばFunctorsの定数には,

  Plus = proc {|x,y|x+y}
  Minus = proc {|x,y|x-y}
  Mul = proc {|x,y|x*y}
  Div = proc {|x,y|x/y}

こんな感じにprocが入ってる.30個以上.RDocを見れば概要はちゃんと書いてあるので,詳しくはそちらを.他にも,charというメソッドやinfixlなんかはHaskellの臭いがするなぁ.


HaskellのParsecライブラリを知り尽くしていたり,言語処理系に興味がある人は,是非是非.

他にも

rparsecの他にHaskellのParsecっぽいものは,tdp4rというgemがあるようだけれど,RDocが入ってなかったので使い方がイマイチわかりません.

*1:テストはしてないけれど:p