ユーザーは、字句アナライザを用意して、入力ストリームを読み取った後にトークン (必要であれば、値も共に) をパーサーに伝達する必要があります。字句アナライザは、yylex() と呼ばれる整数値の関数です。この関数は、読み取ったトークンの種類を表す整数値のトークン番号を返します。値がそのトークンに関連付けられている場合には、その値を外部変数 yylval に割り当てる必要があります。
パーサーと字句アナライザの間で伝達を行うためには、これらのトークン番号が両者で共通の番号でなければなりません。この番号は、yacc で選択することも、ユーザーが選択することもできます。いずれの場合でも、C 言語の #define メカニズムを使用して、字句アナライザがこれらの番号をシンボルとして返せるようにします。
たとえば、yacc 仕様ファイルの宣言セクションにトークン名 DIGIT が定義されているとします。適切なトークンを返すための字句アナライザの関連部分は、以下のように記述できます。
int yylex() { extern int yylval; int c; ... c = getchar(); ... switch (c) { ... case '0': case '1': ... case '9': yylval = c - '0'; return (DIGIT); ... } ... }
このコードは、DIGIT のトークン番号を返します。字句アナライザのコードはサブルーチンセクションに、DIGIT の宣言は宣言セクションに置くことができます。字句アナライザのコードは、以下のようにして、別にコンパイル済みファイルに置くこともできます。
-d オプションを付けて yacc を実行します。これにより、トークンの #define 文を含んだ y.tab.h というファイルが生成されます。
独立したコンパイル済み字句アナライザに #include y.tab.h を追加します。
このメカニズムによって、明示的で変更が容易にできる字句アナライザが作成されます。ただし、C 言語またはパーサーで予約済みのトークン名や重要なトークン名を使用しないでください。
たとえば、トークン名 if または while を使用すると、字句アナライザのコンパイル時に深刻な問題が必ず発生します。トークン名 error はエラー処理用に予約されているので、安易に使用しないでください。
デフォルトの状態では、トークン番号は yacc によって選択されます。リテラル文字のデフォルトのトークン番号は、文字セットの文字の数値です。他の名前には、257 以降のトークン番号が割り当てられます。
自分でトークン番号を割り当てたい場合には、宣言セクションで最初に現われるトークン名またはリテラルの直後に負でない整数を置く必要があります。この整数は、名前またはリテラルのトークン番号とみなされます。この方法で定義されていない名前とリテラルには、yacc によってデフォルトの定義が割り当てられます。その場合には、トークン番号が重複する可能性があります。トークン番号が重複していないことを必ず確認してください。
エンドマーカーは 0 または負のトークン番号を持つ必要があります。このトークン番号を再定義することはできません。したがって、字句アナライザは、入力の終わりに達したときに必ず 0 または 負の数字をトークンとして返すようになっていなければなりません。
第 2 章「字句解析」で説明したように、lex によって生成された字句アナライザは、yacc と協調して動作するように設計されています。これらの字句アナライザの仕様では、構文規則の代わりに正規表現が使用されます。lex を使用して非常に複雑な字句アナライザを生成できますが、理論上仕様が適合しない言語がいくつかあります。そのような言語の場合には、手作業で字句アナライザを作成する必要があります。