スキャナ作成キットの使いかた


パーサを使うときは、たいてい文字列をトークンに切りわけてくれる スキャナが必要になります。しかし実は 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>