[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4. 構文解析器のC言語インターフェイス

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Interface"
"bison/構文解析器のC言語インターフェイス"へのコメント(無し)

Bison構文解析器の正体は、yyparseという名前のCの関数です。 ここでは、yyparseとほかに使う必要がある関数の間の インターフェイスの方法を示します。

構文解析器の内部では、多くの`yy'または`YY'で始まるCの識別子が 使われていることに注意してください。 本書で説明しているものを除いて、そのような識別子を 文法ファイルのアクションや追加のCプログラムの中で使うと、 問題が起きるでしょう。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4.1 構文解析器関数yyparse

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Parser%20Function"
"bison/構文解析器関数yyparse"へのコメント(無し)

構文解析を始めるには、関数yyparseを呼び出します。 この関数は、トークンを読み、アクションを実行し、最後には入力ファイルの 終わりに達するか回復不可能な構文エラーに達して、戻ります。 読み込みを打ち切ってyyparse関数から戻るような アクションを書くことも可能です。

構文解析が成功する、つまり入力ファイルの終わりに達すると、 yyparseからの戻り値が0になります。

構文解析が失敗する、つまり構文エラーが発生すると、 戻り値が1になります。

アクションの中で、次のマクロを使って、 yyparseからただちに戻れます。

YYACCEPT
成功の印である戻り値0をともなって、ただちに戻ります。

YYABORT
失敗の印である戻り値1をともなって、ただちに戻ります。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4.2 字句解析器関数yylex

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Lexical"
"bison/字句解析器関数yylex"へのコメント(無し)

字句解析器(lexical analyzer)関数yylexは、 入力からトークンを認識し、構文解析器に返します。 Bisonはこの関数を自動的に生成しないので、 yyparseから呼び出されるようにyylexを書く必要があります。 関数yylexは"lexical scanner"と呼ばれることもあります。

単純なプログラムでは、よく文法ファイルの最後でyylexを 定義します。yylexが別のソースファイルの中で定義する場合は、 そこでトークン型マクロ定義を使えるように準備する必要があります。 そのためには、`-d'オプションを指定してBisonを実行してください。 すると、マクロ定義がヘッダファイル`name.tab.h'に 書き込まれ、それを必要とするソースファイルにインクルードできます。 See 節 Invoking Bison

4.2.1 yylexを呼び出す方法    yyparseyylexを呼ぶ方法.
4.2.2 トークンの意味値    yylexがどのように読み込んだトークンの
                        意味値を返さなければならないか.
4.2.3 トークンのテキスト中の位置    アクションが望むときに、どのようにyylex
                        テキストの位置(行数など)を返さなければならない
                        か。
4.2.4 再入可能構文解析器を呼び出す方法   
                        違うか (@pxref{Pure Decl, ,A Pure (Reentrant) Parser}).



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4.2.1 yylexを呼び出す方法

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Calling%20Convention"
"bison/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つの方法があります。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4.2.2 トークンの意味値

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Token%20Values"
"bison/トークンの意味値"へのコメント(無し)

通常の再入可能でない構文解析器では、 トークンの意味値が広域変数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 ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4.2.3 トークンのテキスト中の位置

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Token%20Positions"
"bison/トークンのテキスト中の位置"へのコメント(無し)

アクションの中で`@n'機能 (see 節 Special Features for Use in Actions)を 使っている場合には、トークンとグループのテキスト中の位置を 見失わないように、yylexの中で位置情報を提供する必要があります。 関数yyparseは、ちょうど解析されたトークンのテキスト中の位置が、 広域変数yyllocに記憶されていると仮定します。 そこで、yylexは、yylocに正しいデータを記憶する必要があります。 変数yyllocは構造体で、アクションの中で使われる場合にのみ、 メンバを初期化する必要があります。 メンバは、first_linefirst_columnlast_linelast_columnの4つです。 この機能を使うと、構文解析器が著しく遅くなることに注意してください。

yyllocのデータ型は、YYLTYPEという名前を持っています。



[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4.2.4 再入可能構文解析器を呼び出す方法

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Pure%20Calling"
"bison/再入可能構文解析器を呼び出す方法"へのコメント(無し)

純粋な、つまり再入可能な、構文解析器を生成するために、 %pure_parserをBison宣言すると、広域変数yylvalyyllocを使えなくなります (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;
  ...  /* fooに正しいデータを記憶  */
  value = yyparse ((void *) &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 ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4.3 エラー報告関数yyerror

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Error%20Reporting"
"bison/エラー報告関数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 ] [ >> ]         [表紙] [目次] [索引] [検索] [上端 / 下端] [?]

4.4 アクション中で使える特別な機能

URL="https://bookshelf.jp/cgi-bin/goto.cgi?file=bison-ja&node=Action%20Features"
"bison/アクション中で使える特別な機能"へのコメント(無し)
ここの表では、アクション中で有用な、Bisonの構造物、変数、 マクロを示します。

`$$'
現在の規則で作られるグループに対する意味値を保持する変数のように働きます。 See 節 3.5.3 アクション

`$n'
現在の規則のn番目の構成要素に対する意味値を保持する変数のように 働きます。See 節 3.5.3 アクション

`$<typealt>$'
$$に似ていますが、%union宣言で指定された共用体の中の typealtを選びます。 See 節 Data Types of Values in Actions

`$<typealt>n'
$nに似ていますが、%union宣言で指定された共用体の中の typealtを選びます。 See 節 Data Types of Values in Actions

`YYABORT;'
yyparseからただちに戻り、失敗を示します。 See 節 The Parser Function yyparse

`YYACCEPT;'
yyparseからただちに戻り、成功を示します。 See 節 The Parser Function yyparse

`YYBACKUP (token, value);'
トークンを逆シフトします。 1個の値を還元する規則の中で、先読みトークンがない場合にのみ、 このマクロが使えます。 このマクロは、トークン型がtokenで意味値がvalueのトークンを、 先読みトークンとして格納し、この規則で還元されるはずだった値を捨てます。

先読みトークンがすでにあるような、このマクロが無効な状況で このマクロを使うと、メッセージ`cannnot back up'をともなう 文法エラーが報告され、通常のエラー回復が行われます。

どちらの場合も、アクションの残りの部分は実行されません。

`YYEMPTY'
先読みトークンがない場合に、変数yycharに記憶されている値です。

`YYERROR;'
ただちに文法エラーを発生させます。この文は、構文解析器がエラーを検出したように エラー回復を始めますが、yyerrorを呼び出さず、 メッセージは表示されません。 もし、エラーメッセージを表示したければ、`YYERROR'文よりも先に、 明示的にyyerrorを呼び出してください。 See 節 6. エラーからの回復

`YYRECOVERING'
このマクロの値は、字句解析器が文法エラーからの回復中ならば1、 そうでなければ0です。 See 節 6. エラーからの回復

`yychar'
現在の先読みトークンを含んでいる変数です (再入可能構文解析器では、yyparseの局所変数です)。 先読みトークンがない場合には、この変数に YYEMPTYという値が入っています。 See 節 Look-Ahead Tokens

`yyclearin;'
現在の先読みトークンを捨てます。エラー規則の中で有用です。 See 節 6. エラーからの回復

`yyerrok;'
後に続く文法エラーに対して、エラーメッセージの生成を再開します。 これは、エラー規則で特に重要です。 See 節 6. エラーからの回復

`@n'
現在の規則の第n要素の、行番号と列番号を含む、 配列変数のように働きます。 次のようなメンバがあります。

 
struct {
  int first_line, last_line;
  int first_column, last_column;
};

たとえば、第3要素の開始行番号を知るには、 `@3.first_line'とします。

この構造体のメンバを有効な情報にするためには、 yylexがそれぞれのトークンに対して、 情報を提供する必要があります。 一部分のメンバだけが必要ならば、 yylexはそのメンバの値を設定するだけでかまいません。 (13)

この機能を使うと、字句解析器が著しく遅くなります。


[ << ] [ >> ]           [表紙] [目次] [索引] [検索] [上端 / 下端] [?]