[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
Bison構文解析器の正体は、yyparse
という名前のCの関数です。 ここでは、yyparse
とほかに使う必要がある関数の間の インターフェイスの方法を示します。
構文解析器の内部では、多くの`yy'または`YY'で始まるCの識別子が 使われていることに注意してください。 本書で説明しているものを除いて、そのような識別子を 文法ファイルのアクションや追加のCプログラムの中で使うと、 問題が起きるでしょう。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |
yyparse
yyparse
"へのコメント(無し)
構文解析を始めるには、関数yyparse
を呼び出します。 この関数は、トークンを読み、アクションを実行し、最後には入力ファイルの 終わりに達するか回復不可能な構文エラーに達して、戻ります。 読み込みを打ち切ってyyparse
関数から戻るような アクションを書くことも可能です。
構文解析が成功する、つまり入力ファイルの終わりに達すると、 yyparse
からの戻り値が0になります。
構文解析が失敗する、つまり構文エラーが発生すると、 戻り値が1になります。
アクションの中で、次のマクロを使って、 yyparse
からただちに戻れます。
YYACCEPT
YYABORT
[ < ] | [ > ] | [ << ] | [ 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)
この機能を使うと、字句解析器が著しく遅くなります。
[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] [?] |