スキャナ作成キットの使いかた
パーサを使うときは、たいてい文字列をトークンに切りわけてくれる
スキャナが必要になります。しかし実は Ruby は文字列の最初からトークンに
切りわけていくという作業があまり得意ではありません。正確に言うと、
簡単にできるのですが、非常に大きいオーバーヘッドがかかります。
racc に添付されている scanner.rb と strscan はこのオーバーヘッドを
回避しつつ、手軽にスキャナをつくるためのライブラリです。
まあ、ちょっとしか手軽にならないんですけど…
使い方
以下に、scanner.rb を利用する簡単な例を示します。
require 'racc/scanner'
class MyScanner < Scanner
SPACE = /\A [\t]/
WORD = /\A[_\w][_\w\d]*/
def reset( str )
super
end
def scan
tmp = nil
while true do
@scan.skip SPACE
return [false, false] if @scan.empty?
return [false, false]
end
if tmp = @scan.scan( WORD ) then
return [:WORD, tmp]
end
end
end
end
このクラスは、スペースとタブ、アルファベットのならびから、単語を切りだす
スキャナです。以下、順次説明します。
reset メソッドはこの場合必要ないのですが、わかりやすさのために追加しています。
スキャン対象にすべき文字列でスキャナを初期化します。
scan メソッドの冒頭の unless 文はもう文字列がなくなった(全部スキャンし終わった)
ことのチェックです。その後の skip と scan はどちらも正規表現を引数にとって、
マッチするかどうか調べるメソッドですが、skip はマッチした文をとばすだけで、
scan はマッチした部分の文字列を返すという違いがあります。
SPACE のほうはトークンとしては意味を持たないので捨てますが、
WORD のほうは意味のあるトークンなので、@pipe に入れておきます。
ちなみに、マッチに使う正規表現は必ず文字列の先頭からマッチしなければいけません
(ようは、 /\A…/ じゃないとだめってことです)。もし先頭より後でマッチした場合は、
マッチしたところまで全部がマッチしたものとみなされてしまいます。例えば次のような場合は
s = StrScanner.new( "word word" )
ret = s.scan( /\s+/ )
何事もなかったかのように ret には "word " が返ります。
先頭からマッチしなかった場合、それを知る手段はありません。
あとは、以下のリファレンスなどを見て考えてください(いいかげんだな)。
StrScanner reference manual
クラスメソッド
- new( str: String, dup_p = true ): StrScanner
-
新しいStrScannerオブジェクトを生成します。strはスキャンする文字列、dup_pは
文字列を複製して使うかどうかを真偽値で指定します。
dupしないと生成が高速になりますが、その場合もとの文字列からとりだしたポインタを
そのまま使うので、もしスキャン中にその文字列がガーベージコレクトされると落ちます。
また、他のスレッドがその文字列を触れるときも危険です。StrScannerでは最初に取得した
ポインタと長さを最後まで使うので、もし文字列が短かく変更されたり、realloc がおこったり
すると落ちます。
メソッド
- scan( regex: Regexp ): String
-
正規表現regexとマッチを行って、マッチしたらスキャンポインタを進めたうえで
その部分の文字列を返し、マッチしなかったらnilを返します。
- skip( regex: Regexp ): Integer
-
正規表現regexとマッチを行って、マッチしたらスキャンポインタを進めたうえで
マッチした文字列の長さを返し、マッチしなかったらnilを返します。
- match?( regex: Regexp ): Boolean
-
正規表現regexとマッチを行って、マッチしたらスキャンポインタは進めずに
マッチした文字列の長さを返し、マッチしなかったらnilを返します。
- fullscan( regex: Regexp, makestr_p: Boolean, fwdptr_p: Boolean )
-
正規表現regexとマッチを行います。マッチしなかったらnilを返します。
マッチした場合は、makestr_pが真のときにはマッチした文字列を返します。
偽のときはマッチした文字列の長さを返します。
fwdptr_pが真のときはスキャンポインタを進めます。偽のときはそのままになります。
- getch : String
-
スキャンポインタが指す1バイトを文字列として返し、ポインタをひとつすすめます。
- rest : String
-
スキャンポインタ以降の文字列を返します。
- empty? : Boolean
-
スキャンすべき文字列がなくなったとき真。
- restsize : Integer
-
スキャンポインタのうしろの文字列の長さを返します。
- unscan
-
スキャンポインタを一回分もとに戻します。一回分以上は取り消せません。
- matched
-
一回前のスキャンポインタから現在のポインタまでの文字列を返します。
- matchedsize
-
一回前のスキャンポインタから現在位置までの長さを返します。
Copyright(c) 1998-1999 Minero Aoki
<aamine@dp.u-netsurf.ne.jp>