Rubyのパーサジェネレータ Racc のメモ。Ruby 1.8以上ならばランタイムライブラリが標準ライブラリとして含まれている。
環境
% rvm -v rvm 1.25.22 (stable) by Wayne E. Seguin <wayneeseguin@gmail.com>, Michal Papis <mpapis@gmail.com> [https://rvm.io/] % ruby -v ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-linux]
チュートリアル:calc.y にべき乗、変数を付け加える
GitHub:tenderlove/racc sample/calc.yよりサンプルプログラムを入手する。以後は以下にしたがい使い方を覚える。
コンパイルして実行してみる。
% racc -o calc.rb calc.y % ruby calc.rb
strscanでスキャナ(字句解析器)を作成する
上のチュートリアルの「パーサとスキャナの分離」まで終わっているとする。Calcp#parse()をRubyの標準ライブラリstrscanを使って書き直す。構文の定義ファイルをcalc.rbとする。
require './calc.rb'
require "strscan"
class Calcp
def initialize
@vartab = {}
end
def do_assign(varname, val)
@vartab[varname] = val
end
def do_refvar(varname)
@vartab[varname]
end
def parse(str)
@q = []
s = StringScanner.new(str)
until s.eos?
case
when s.scan(/\s+/)
when s.scan(/\d+/)
@q.push [:NUMBER, s[0].to_i]
when s.scan(/[a-zA-Z]+/)
@q.push [:IDENT, s[0]]
when s.scan(/.|\n/o)
@q.push [s[0], s[0]]
end
end
@q.push [false, '$end']
do_parse
end
def next_token
@q.shift
end
end
parser = Calcp.new
puts
puts 'type "Q" to quit.'
puts
while true
puts
print '? '
str = gets.chop!
break if /q/i =~ str
begin
puts "= #{parser.parse(str)}"
rescue ParseError
puts $!
end
end元のCalcp#parse()では、正規表現にマッチした部分を捨てる処理があったが、strscanの場合はStringScannerオブジェクト自身がどこまで文字列を読み込んだのか覚えているのでその処理が不要になる。