この章では字句スキャン処理の概念を紹介し、 Flexのようなツールの必要性を指摘します。 この章の後半部分でFlexを紹介し、 Flexを使うことのできる状況の実例をいくつか紹介します。
UnixおよびCの世界では、 ファイルは通常個々のバイトが連続したものとして扱われます。 個々のバイトを集めてどのようにグループ化するかという点は、 プログラマが決めることです。 このような抽象化は非常に強力です。 というのは、 どのようなファイルであってもこの抽象化方法によって表現することができるからです。 しかしこの方法には短所もあり、 プログラマはほとんど常に生のファイルに対して構造をあてはめなければなりません。 言葉を変えると、 ファイルをより意味のある部分に分割しなければならないということです。 例えば、 コンパイラのある部分はファイルから連続した文字を受け取り、 構文チェッカが理解することのできる構成要素、 例えば、 数値、キーワード、文字列などにグループ化します。 このようなことを行う理由は、 コンパイラの言語パーサが処理を行うのは、 連続した文字に対してではなく、 その言語のシンボルが連続したものに対してだからです。
データベース・アプリケーションや、 バイナリ・ファイルを扱うアプリケーションは、 扱うデータに対してある固定されたフォーマットというものを持っていることが多く、 そのフォーマットを使って入力データから意味を導き出します。 テキストを入力するプログラムは通常これとは反対で、 入力を単語やシンボルに分割しなければならないことが多いのですが、 通常これらの単語やシンボルがどのように配置されているかを示す決まった構造というものは存在しません。 したがって、 テキスト処理を行うプログラムは、 入力された情報を意味のあるシンボルに分割するために、 字句解析もしくは字句スキャンと呼ばれる処理を行う部分を持っていることが多く、 そこで入力情報の分割が行われます。 このようなことを行う関数群のことを字句アナライザもしくは字句スキャナ、 あるいは短く「スキャナ」と呼びます。
一般的に、 スキャナを作成するのは、 プログラマにとって難しいことでも面白いことでもないのですが、 時間のかかる作業になることはあります。 普及しているプログラミング言語のほとんどは、 スキャナの作成を支援する機能が不十分です。 というのは、 連続した文字を単語、トークン、シンボルに分割する組み込みの機能を持っていないからです。 通常はこのような仕事を行うライブラリ・ルーチンが存在しますが、 柔軟でなかったり、 使いにくいものであったり、 あるいは、 ルーチンとのやりとりにあまりに多くのコードが必要になったりすることが多いために、 実装上の細かな点によって根本的な問題が不明瞭にされてしまいます。
1つの良い例が、 C言語で許されているすべての数値型 (浮動小数、10進整数、16進整数、8進整数) を処理するスキャナをC言語で記述する場合です。 これは非常に難しいということはありませんが、 出来上がったコードは通常美しいとはとても言えないものでしょうし、 その保守や拡張は容易でないことが多いのです。
ほとんどのプログラマが即座に断言するように、 他人の書いたコードを保守するのは通常あまり楽しい作業ではありません。 さらに、 美しくないコードを保守するのは、 楽しいというにはほど遠いものです。 このように、 スキャナを書くことが退屈で、 その保守が難しいとなると、 スキャナの作成をより容易にしてくれる方法を考えようとするに足る理由のあったことが読者にもお分かりいただけるでしょう。
ここでFlexが登場することになります。 Flexはプログラマに対して、 字句解析処理部分をきれいに記述し、 その記述にしたがった効率的な字句スキャナを生成する方法を提供します。 プログラマはFlexに対して、 必要なスキャナに関する記述情報を提供します。 Flexはその記述情報を使って、 C言語で書かれたスキャナを生成します。 記述に使われる言語は上級言語であり、 スキャナの記述に関してはC言語よりもはるかに適しています。 その上級言語を使うことで、 プログラマは、 文字をどのようにグループ化し、 グループ化が完了した時にどのようなアクションを発生させるかを指定することができます。
注:このマニュアルのほとんどの部分はFlex、Lexの両方を対象にしています。 Lexは (Flexには劣りますが) ほとんどのUnixシステム上にある標準のスキャナ生成ユーティリティです。 両者の間に違いがある場合には、 Flexを優先させています。 Lexについては標準Lexで簡単に説明してあります。
ここでも1つの良い例がコンパイラです。 前に議論したように、 コンパイラの構文チェッカは、 文字が連続したものではなく、 言語文法の構成要素を表すトークンが連続したものを、 入力として受け取る必要があります。 Flexはこのような場合に最適です。 Flexによって生成されたスキャナが構文チェッカとファイルの仲介役となり、 構文チェッカが次の意味のあるトークンを要求するのを待ちます。 Flexはファイルを読み、 プログラマが与える記述にしたがって文字をグループ化して、 マッチしたトークンを返却します。 この処理は、 スキャナもしくはパーサが終了するまで続きます。
Cのコンパイラを作成する場合、 このようなことを行うために必要となるFlexの記述情報は、 コードの行数にして100行から300行くらいになるかもしれません。 この記述情報のほとんどは、 シンボル・テーブルの管理、識別子の検索、型のマッピング、ある数の値等の追加情報の返却を行うための補助的なCコードになるでしょう。 こうしたコード自体は記述情報の一部ではありませんが、 通常、 コンパイラによって必要とされるものです。
概念的にはFlexは、 原材料(文字)を取り込み、 消費者(パーサ等)がすぐに使うことができる完成品(トークン)を製造する工場のようなものです。
Flexはコンパイラにしか使えないということはありません。 読者のコンピュータ上にある、 ファイルを読み込んだり、 なんらかの形で文字のグループを処理する必要のあるすべてのプログラム、 特に、 変換フィルタや言語ツールのことを考えてみてください。 こうしたプログラムのほとんどすべてを、 Flex単体、 もしくはFlexと他のツールの組み合わせによって作成することができます。
1つの良い例が文字数のカウントです。
例えば、
ファイルの中の全行数、
個々の文字の出現回数、
`foo'という単語の出現回数を調べるプログラムを作成したいとしましょう。
標準的なツール
(grep
、sed
、awk
、perl
等)
で作成することも、
C言語のプログラムを書いて作成することもできますが、
Flexで作成することも可能です。
他の例として、
特定のキーワードを探す必要のある、
メール・リーダがあります。
この場合でも、
標準ツールで実現することも、
FlexとC言語で実現することもできます。
Flexを使うことで、 プログラマは、 スキャナの開発やファイルを構成する文字の処理にかかる時間を大幅に減らすことができます。 ほとんどの場合、 Flexに対する入力情報は、 既存のプログラミング言語で記述されたコードと比較して、 より理解しやすく、 少なくとも同程度の移植性があり、 保守もより簡単です。 それだけでなく、 Flexでスキャナを開発するのにかかる時間は、 既存のプログラミング言語で同等のスキャナを開発する場合と比べきわめて短くてすむので、 Flexは、 プロトタイピングや、 一度しか使わないプログラムやフィルタの開発に最適です。