| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Bison構文解析器の正体は、yyparseという名前のCの関数です。 ここでは、yyparseとほかに使う必要がある関数の間の インターフェイスの方法を示します。
構文解析器の内部では、多くの`yy'または`YY'で始まるCの識別子が 使われていることに注意してください。 本書で説明しているものを除いて、そのような識別子を 文法ファイルのアクションや追加のCプログラムの中で使うと、 問題が起きるでしょう。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
yyparse
yyparse"へのコメント(無し)
構文解析を始めるには、関数yyparseを呼び出します。 この関数は、トークンを読み、アクションを実行し、最後には入力ファイルの 終わりに達するか回復不可能な構文エラーに達して、戻ります。 読み込みを打ち切ってyyparse関数から戻るような アクションを書くことも可能です。
構文解析が成功する、つまり入力ファイルの終わりに達すると、 yyparseからの戻り値が0になります。
構文解析が失敗する、つまり構文エラーが発生すると、 戻り値が1になります。
アクションの中で、次のマクロを使って、 yyparseからただちに戻れます。
YYACCEPTYYABORT| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
yylex
yylex"へのコメント(無し)
字句解析器(lexical analyzer)関数yylexは、 入力からトークンを認識し、構文解析器に返します。 Bisonはこの関数を自動的に生成しないので、 yyparseから呼び出されるようにyylexを書く必要があります。 関数yylexは"lexical scanner"と呼ばれることもあります。
単純なプログラムでは、よく文法ファイルの最後でyylexを 定義します。yylexが別のソースファイルの中で定義する場合は、 そこでトークン型マクロ定義を使えるように準備する必要があります。 そのためには、`-d'オプションを指定してBisonを実行してください。 すると、マクロ定義がヘッダファイル`name.tab.h'に 書き込まれ、それを必要とするソースファイルにインクルードできます。 See 節 Invoking Bison。
意味値を返さなければならないか.
4.2.1 yylexを呼び出す方法yyparseがyylexを呼ぶ方法.4.2.2 トークンの意味値 yylexがどのように読み込んだトークンの
テキストの位置(行数など)を返さなければならない
4.2.3 トークンのテキスト中の位置 アクションが望むときに、どのように yylexが
か。
違うか (@pxref{Pure Decl, ,A Pure (Reentrant) Parser}).
4.2.4 再入可能構文解析器を呼び出す方法
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
yylexを呼び出す方法yylexを呼び出す方法"へのコメント(無し)
yylexが返す値は、見つかったトークンの型に対する番号で、 入力ファイルの終わりに達した場合には0を返します。
トークンが文法規則の中で名前で参照される場合、 構文解析器ファイルの中でのその名前は、 トークン型に対する適切な番号にCのマクロとして定義されます。 したがって、yylexは型を示すためにその名前を使用できます。 See 節 3.2 記号、終端と非終端。
文法規則の中でトークンが1文字リテラルとして参照される場合には、 その文字の文字符号がトークン型に対する番号でもあります。 そこで、yylexは、単純に文字符号を返します。 しかし、戻り値0は入力ファイルの終わりを意味するので、 ヌル文字('\0')の文字符号を返してはいけません。
以下に例を示します。
yylex ()
{
...
if (c == EOF) /* ファイルの終わりか調べる。 */
return 0;
...
if (c == '+' || c == '-')
return c; /* `+' に対するトークン型が '+' であると仮定する。 */
...
return INT; /* トークン型を返す。 */
...
}
|
このようなインターフェイスは、 lexが生成した字句解析器を、 yylexの定義を変えずに使えるように設計されています。
文法規則が文字列リテラルトークンを使っている場合には、 yylexがそれに対するトークン型番号を使う、 2つの方法があります。
yylexはその記号名を他のトークンの記号名と 同様に使えます。この場合、文法ファイルの中での文字列リテラルトークンの 利用は、yylexにまったく影響しません。
yylexは、yytname表の中で、 複数文字トークンを見つけられます。 トークンに対する表の添え字は、そのトークン型の番号です。 複数文字トークンはyytnameの中にダブルクォート記号で囲まれて 記憶されます。 トークンに含まれる文字はエスケープされず、 表の中の文字列にそのまま書き込まれています。
トークンを構成する文字列がtoken_bufferに記憶されていると仮定して、 yytnameからトークンを探し出すプログラムを示します。
for (i = 0; i < YYNTOKENS; i++)
{
if (yytname[i] != 0
&& yytname[i][0] == '"'
&& strncmp (yytname[i] + 1, token_buffer,
strlen (token_buffer))
&& yytname[i][strlen (token_buffer) + 1] == '"'
&& yytname[i][strlen (token_buffer) + 2] == 0)
break;
}
|
yytname表は、%token_table宣言をした場合にのみ生成されます。 See 節 3.6.8 Bison宣言の要約。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
通常の再入可能でない構文解析器では、 トークンの意味値が広域変数yylvalに代入される必要があります。 意味値に対してただ1つのデータ型を使っている場合には、 yylvalの型もそうです。 したがって、たとえば、宣言を省略して型がintならば、 次のように書けます。
... yylval = value; /* 値をBisonスタックに積む。 */ return INT; /* トークン型を返す。 */ ... |
複数のデータ型を使っている場合には、 %union宣言で作られた共用体がyylvalの型になります (see 節 The Collection of Value Types)。 そこで、トークンの値を代入するには、 共用体のメンバの名前を指定する必要があります。 %union宣言の例を示します。
%union {
int intval;
double val;
symrec *tptr;
}
|
すると、yylexの中のプログラムは次のようになります。
... yylval.intval = value; /* 値をBisonスタックに積む。 */ return INT; /* トークン型を返す。 */ ... |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
アクションの中で`@n'機能 (see 節 Special Features for Use in Actions)を 使っている場合には、トークンとグループのテキスト中の位置を 見失わないように、yylexの中で位置情報を提供する必要があります。 関数yyparseは、ちょうど解析されたトークンのテキスト中の位置が、 広域変数yyllocに記憶されていると仮定します。 そこで、yylexは、yylocに正しいデータを記憶する必要があります。 変数yyllocは構造体で、アクションの中で使われる場合にのみ、 メンバを初期化する必要があります。 メンバは、first_line、first_column、 last_line、last_columnの4つです。 この機能を使うと、構文解析器が著しく遅くなることに注意してください。
yyllocのデータ型は、YYLTYPEという名前を持っています。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
純粋な、つまり再入可能な、構文解析器を生成するために、 %pure_parserをBison宣言すると、広域変数yylvalと yyllocを使えなくなります (see 節 A Pure (Reentrant) Parser)。 このような構文解析器では、2つの広域変数の代わりに、 yylexへの引数として渡されるポインタを使います。 yylexを次のように宣言し、 これらのポインタを通して情報を受け渡しする必要があります。
yylex (lvalp, llocp)
YYSTYPE *lvalp;
YYLTYPE *llocp;
{
...
*lvalp = value; /* 値をBisonスタックに積む。 */
return INT; /* トークン型を返す。 */
...
}
|
文法ファイルがテキスト中の位置を参照するための`@'機能を 使っていない場合は、YYLTYPEは定義されません。 この場合、第2引数を省略し、yylexは 1個の引数をともなって呼び出されます。
再入可能な構文解析器を使っている場合、 再入可能な方法で構文解析器に追加の引数を渡す方法があります。 そのためには、マクロYYPARSE_PARAMを変数名として定義します。 すると、関数yyparseは、定義された名前で、型がvoid *の 追加の引数を受け取ります。
yyparseを呼び出すときに、オブジェクトの番地を void *型にキャストして渡します。 文法のアクションは、ポインタを適切な型へのポインタへキャストし、 逆参照して、オブジェクトの内容を参照できます。 例を示します。
%{
struct parser_control
{
int nastiness;
int randomness;
};
#define YYPARSE_PARAM parm
%}
|
次のように構文解析器を呼び出します。
struct parser_control
{
int nastiness;
int randomness;
};
...
{
struct parser_control foo;
... /*
|
文法アクションの中では、データを参照するために次のような式を使います。
((struct parser_control *) parm)->randomness |
yylexに追加の引数を渡したい場合には、 YYPARSE_PARAMと同様に、マクロYYLEX_PARAMを定義します。 例を示します。
%{
struct parser_control
{
int nastiness;
int randomness;
};
#define YYPARSE_PARAM parm
#define YYLEX_PARAM parm
%}
|
そして、yylexが追加の引数、parmの値を受け取るように、 yylexを定義する必要があります (型YYLTYPEのどの引数が渡されるかに応じて、 引数の合計が2個または3個になります)。 引数を正しいオブジェクト型として宣言できます。すなわちvoid *として 宣言し、上記の番地を参照できます。
YYPARSE_PARAMを使わずに、`%pure_parser'を使って、 再入可能な構文解析器を生成することも可能です。 その場合、引数をつけずにyyparseを呼び出すべきです。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
yyerror
yyerror"へのコメント(無し)
Bison構文解析器は、文法規則に適合しないトークンを読むたびに、 構文解析エラー(parse error)すなわち文法エラー(syntax error)を 検出します。文法中のアクションは、マクロYYERRORを使って、 明示的にエラーを示せます (see 節 Special Features for Use in Actions)。
Bison構文解析器は、yyerrorという名前の関数を使って、 エラーを報告するようになっています。 事前に用意が必要な この関数は、文法エラーが発生するたびに、1個の引数をともなって、 yyparseから呼び出されます。 構文解析エラーに対して、引数の文字列は通常"parse error"です。
Bison定義部(see 節 The Bison Declarations Section)で、 マクロYYERROR_VERBOSEを定義すると、 "parse error"の代わりに、 エラーを詳細に報告する文字列が用意されます。 マクロYYERROR_VERBOSEの定義はなんでもかまいません。
構文解析器は、もう1種類のエラーであるスタックオーバーフローを検出する 可能性があります。これは、入力がきわめて深い入れ子からなっていると 起こることがあります。Bison構文解析器は自動的にスタックの限界を大きく拡張し ているので、スタックオーバーフローはめったに起きません。 しかし、もしスタックオーバーフローが起きれば、 "parser stack overflow"という 文字列の引数をともなって、yyerrorが呼び出されます。
単純なプログラムでは、次の例のようにyyerrorを定義できます。
yyerror (s)
char *s;
{
fprintf (stderr, "%s\n", s);
}
|
yyerrorから戻った後、yyparseは、 適切なエラー回復文法規則(see 節 6. エラーからの回復)があれば、 エラーからの回復を試みます。 もし、回復が不可能ならば、yyparseは即座に1を返します。
変数yynerrsには、それまでに出くわした文法エラーの数が記憶されています。 通常、この変数は広域変数です。 しかし、再入可能な構文解析器(see 節 A Pure (Reentrant) Parser) を生成した場合には、アクションからのみ参照可能な局所変数になります。
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
$$に似ていますが、%union宣言で指定された共用体の中の typealtを選びます。 See 節 Data Types of Values in Actions。
$nに似ていますが、%union宣言で指定された共用体の中の typealtを選びます。 See 節 Data Types of Values in Actions。
yyparseからただちに戻り、失敗を示します。 See 節 The Parser Function yyparse。
yyparseからただちに戻り、成功を示します。 See 節 The Parser Function yyparse。
先読みトークンがすでにあるような、このマクロが無効な状況で このマクロを使うと、メッセージ`cannnot back up'をともなう 文法エラーが報告され、通常のエラー回復が行われます。
どちらの場合も、アクションの残りの部分は実行されません。
yycharに記憶されている値です。
yyerrorを呼び出さず、 メッセージは表示されません。 もし、エラーメッセージを表示したければ、`YYERROR'文よりも先に、 明示的にyyerrorを呼び出してください。 See 節 6. エラーからの回復。
yyparseの局所変数です)。 先読みトークンがない場合には、この変数に YYEMPTYという値が入っています。 See 節 Look-Ahead Tokens。
struct {
int first_line, last_line;
int first_column, last_column;
};
|
たとえば、第3要素の開始行番号を知るには、 `@3.first_line'とします。
この構造体のメンバを有効な情報にするためには、 yylexがそれぞれのトークンに対して、 情報を提供する必要があります。 一部分のメンバだけが必要ならば、 yylexはそのメンバの値を設定するだけでかまいません。 (13)
この機能を使うと、字句解析器が著しく遅くなります。
| [ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |