2006-07-01  ウィスパー3―その2―

今回のお話は,役に立つ人がいそうなので詳しく書きました。意味が分からない人には,怪しげな暗号のように見えるかもしれませんが,気にしないでください。

Perlでyaccを使うには

今回はkmyacc(多言語対応LALRパーサー生成系)を使わせていただきました。kmyaccはPerlにも対応しているのです。

kmyaccを使うには,まずソースファイルをコンパイルして実行ファイルを得なければなりません。WindowsではCygwin環境(Windowsで動作するUNIX系OSの擬似環境)を導入してmakeすれば,実行ファイルを得ることができます。

私,Perlのyacc以前に,素のyaccをまともに使ったことがありませんでした。参考書としてオライリーの「lex&yaccプログラミング」のお世話になりました。いつもの動物本シリーズです。

この本に書かれている例はC言語なのですが,今回使う処理系はPerlです。Perlのお約束と照らし合わせるのにやや苦戦しました。最初はまったくわけが分からなかったので,Cのyaccをまず使ってみて,Perl版との相違を調べることからはじめました。

kmyaccを使ってみて,気づいたことを箇条書きにします。

  • yylex関数の中で解析する文字列を読み込み,字句を返す
  • 文字列のバッファはyylex関数の外に置く
  • 字句の値は$yylval(グローバル変数)に代入する。字句の種類は関数の返り値とする(return文で返す)

詳しくはkmyaccのサンプルプログラムを見れば分かるはず。

ちなみに「Perlのyaccがあるのは分かったけれど,Perlのlexは何処に?」と思った人がいるかもしれません。Perlのlexはどうもなさそうです。今回は字句の解析を自前ですることにしました。

どこまでが字句解析で,どこまでが構文解析か

以下は字句と構文の一般的な話です。kmyaccの話ではありません。

作ってみて,役割分担が曖昧になってしまうのが気になりました。

例えば「1 AND 1」を還元する式を考えてみます。数値と演算子の間には「空白」があるので(また空白で区切らなければいけない,という決まりにしたので),構文は「NUMBER SPACE AND SPACE NUMBER」としても良さそうだし,空白(SPACE)を前処理で取り除いて「NUMBER AND NUMBER」としても良さそうです。

最初は前者の方法を採用していました。でも「空白が2文字以上続いた場合」「前後と末尾に空白がある場合」なども字句と構文で意識しなければならなくなってしまって,どうも「これではうまくいきそうない」と思いました。

参考書(前出の動物本)を読んでみたところ,空白は字句解析の前に処理してしまった方がよいのだそうです。処理手順は「字句解析」「構文解析」の2段階でよいのかと思ったら,どうも字句の解析の前にもう1段階必要だったのです。

文脈の保持

文脈をどうやって扱うのか悩みました。

仮に「2006」という文字の並びがあったとします。これを数値として解釈させるのか,文字列として解釈させるのか,はたまた日付として解釈させるのかは,前後の文脈に依るわけです。

字句解析のコード中に,文脈を保持する変数を導入して,値で場合分けしたらうまく書けました。

今後の課題

以前もお話しましたが,ウィスパー3では集合演算をDB中のレコードに適用します。まだまだ未解決の問題が山積みなのです。

  • 式を最適化する ― 式が「A * B + A - A」のようなとき必要なのは「A * B」のみ。「A - A」を計算すると空集合になるので事前に消す
  • 式をSQLに展開する ― テンプレートによって式をSQL文に展開する
  • 日付時刻の範囲を表すクラス ― 既成のPerlモジュールの存在を調べる。日付時刻範囲オブジェクトt1,t2があったとき,t1.contains(t2)として範囲に「含まれる,含まれない」を真偽値で返したり,t1.concatenate(t2)として範囲を追加・拡張したりできるようなメソッドがあると良い

関連リンク