プログラミングユーティリティ

lex ルーチン

以下のマクロを使用して特殊なアクションを実行できます。

2 つの特殊文字 (たとえば、二重引用符など) の間にある文字をすべて無視する方法の 1 つとして、以下のように input() を使用する方法があります。

¥"      while (input() != '"'); 

スキャナは、最初の二重引用符を読み取ってから 2 番目の二重引用符を読み取るまでは、照合を行わずに後続の文字をすべて読み取ります。(input()unput() の使い方の詳しい例については、「ユーザールーチン」を参照してください。)

これらデフォルトのマクロでは対応していない特殊な入出力 (複数ファイルへの書き込みなど) が必要な場合には、C の標準入出力ルーチンを使用してマクロ関数を書き直してください。

ただし、これらのルーチンは一貫性をもって変更する必要があります。特に、文字セットはすべてのルーチンで同じものを使用し、input() によって返される値 0 はファイルの終わりを意味していなければなりません。また、input()unput() との間の関係も維持する必要があります。関係が維持されていないと、lex の先読み処理は機能しなくなります。

独自の input()output(c)、または unput(c) を作成する場合には、まず初めに #undef input#undef output、または #undef unput を定義セクションに記述してください。

#undef input
#undef output
  . . .
#define input() ...  etc.
more declarations 
  . . .

標準のルーチンが新しいルーチンへ置換されます。詳しくは、「定義」を参照してください。

ユーザーが再定義できる lex ライブラリルーチンは、スキャナがファイルの終わりに到達すると必ず呼び出される yywrap() です。yywrap() が 1 を返した場合には、スキャナは入力の終わりで通常の後処理を行います。新しいソースから引き続き入力を受け取るようにするには、yywrap() を再定義して、継続して処理が必要なときには 0 を返すようにします。デフォルトの yywrap() は必ず 1 を返します。

ファイルの終わりを認識する標準の規則を記述することはできないので注意してください。yywrap() からのみ、ファイルの終わりを認識する標準の規則を変更することができます。input() によって返される値 0 はファイルの終わりとみなされるので、ユーザーが独自の input() を用意しないかぎり、NULL を含んだファイルを取り扱うことはできません。

複数の方法で処理される文字列を扱うための lex ルーチンとしては、yymore()yyless(n)REJECT があります。 与えられた仕様に一致したテキストは配列 yytext[] に格納されることに注意してください。一般に、その仕様のアクションが実行されると、yytext[] 内の文字は、その次に一致した入力ストリーム内の文字に置換されます。

一方、関数 yymore() の場合には、認識された後続の文字は yytext[] 内にすでに格納されている文字の後に追加されます。この関数を使用すれば、ある文字列が有効で、その文字列を含んだ別の文字列も有効なときなどに、処理を連続して行うことができます。

二重引用符で囲まれた文字の集まりとして文字列が定義されている言語において、文字列に二重引用符を含める指定を行うには、その引用符の直前にバックスラッシュを置く必要があります。これに一致する正規表現は多少複雑であるため、次のように記述してください。

¥"[^"]* { 
     if (yytext[yyleng-2] == '¥¥') 
         yymore(); 
     else
         ... 通常処理
     }

スキャナは、文字列 "abc¥"def" に出会うと、まず文字列 "abc¥" と照合します。次に、yymore() を呼び出して、文字列の次の部分 "def を先の文字列の後ろに追加します。文字列の終わりを示す二重引用符は、「通常処理」と記されている部分のコードで取り扱われます。

関数 yyless(n) を使用して、アクションの実行対象となる一致文字の数を指定できます。yytext[] には、式の最初の n 文字だけが保持されます。後続の処理は、nth+1 番目の文字から再開されます。

たとえば、コードの解読を行なっていて、特定の文字 (この例では、小文字または大文字の Z) で終了する文字列の半分だけを処理する場合には、以下のように記述します。

[a-yA-Y]+[Zz] { yyless(yyleng/2); 
                 ... 文字列の前半分を処理
                 ...
                }

また、REJECT 関数を使用すれば、文字列が重複していたり、文字列の一部に別の文字列が含まれている場合であっても、文字列の処理をさらに容易に行うことができます。REJECT 関数では、yytext[] の内容を変更せずに次の規則とその仕様に直接ジャンプすることによって、この処理を実現しています。たとえば、入力テキスト内の正規表現 snapdragon とその部分式 dragon の両方の出現回数をカウントするには、以下のように記述します。

snapdragon    {countflowers++; REJECT;} 
dragon         countmonsters++;

パターンが別のパターンと重複しているときの例を以下に示します。この例では、入力テキストに comediana などの文字列が含まれている場合でも、式 comediandiana の出現回数をカウントします。

comedian    {comiccount++; REJECT;} 
diana        princesscount++; 

この例のアクションはカウンタの値を増やしていくことですが、それよりもずっと複雑なアクションを指定しなければならない場合もあります。カウンタやその他必要な変数は、常に lex 仕様の開始部分の定義セクションで宣言してください。