自作コンパイラの部屋 > yacc入門 > 何を書いたら良いか
yaccを利用する上では、最低限以下を記述する必要があります。
もちろん、先の電卓プログラムにも上記が含まれています。以下、順に説明することにしましょう。
1.YACC文法ファイル
電卓プログラムでは、parser.yが該当します。ここに構文規則を記述します。
YACC文法ファイルは、以下のように3つの部分からなります。見てのとおり、%%だけからなる行が、各部分の区切りになります。
YACC宣言部 %% 文法規則部 %% 追加のCプログラム |
さらに、YACC宣言部には、%{ ... %}で囲むことにより、C言語の宣言を置くことが出来ます。YACC文法ファイルの詳しい説明は後回しにして、ここではもう一度parser.yの内容を確認してみます。
| parser.y |
%{
#include <stdio.h> /* C宣言部 */
#include <stdlib.h> /* 通常は#includeが並ぶ */
#include <string.h>
#include "calc.h"
%}
%token NUM /* YACC宣言部 */
%left '-' '+' '*' '/'
%% /* 以下が文法規則部 */
input: /* empty*/
| input line
;
line: '\n'
| exp '\n' { printf("%d\n", $1); }
;
exp: NUM
| exp '+' exp { $$ = $1 + $3; }
| exp '-' exp { $$ = $1 - $3; }
| exp '*' exp { $$ = $1 * $3; }
| exp '/' exp { $$ = $1 / $3; }
;
%% /* 追加のCプログラムは空 */
|
2.字句解析関数:yylex()
字句解析用の関数は、yylex()という名前で作成します。これはyaccのお約束です。yylex()のプロトタイプは、
int yylex(void);です。見てのとおりyylex()は、ファイルなどから記号や数字、名前などの一塊(トークン)を読んで、トークンの種類を数値により返します。
| lex.c |
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef UNIX
#include "parser.tab.h"
#else
#include "parser.h"
#endif
int yylex() /* 字句解析ルーチンはyylexという関数にする */
{
int c = getchar();
if (isdigit(c)) { /* 一文字が数字だったら */
char buff[128]; /* atoi用のバッファ */
char *p = buff;
do {
*p++ = c; /* その一文字を記憶し */
c = getchar(); /* 次の文字を読む */
} while (isdigit(c)); /* まだ数字だったら繰り返し */
ungetc(c, stdin); /* 読み過ぎた文字を返しておく */
*p = '\0'; /* 終端 */
yylval = atoi(buff); /* yylval変数に値を覚える */
return NUM; /* NUMを返す */
} /* 数字でない場合は */
return c; /* 文字そのものを返す */
}
|
3.エラー表示関数:yyerror()
yaccユーザはエラー表示用の関数を用意することになっています。これはyyerrorという名前で作成します。これもyaccの約束です。yyerrorのプロトタイプは、
int yyerror(const char *s);です。但し、yyerror()の戻り値は無視されるので、0か何かを返しておけばよいでしょう(yyerrorの戻り型はどうもはっきりしませんが、少なくともkmyaccでは、intを仮定しているようです)。
int yyerror(const char *s) /* エラー表示 */
{
printf("ERROR: %s\n", s);
return 0;
}
|
4.パーサの呼び出し:yyparse()関数の呼び出し
yaccにより生成されるパーサ(構文解析プログラム)は、yyparse()という関数になります。このプロトタイプは以下の通りです。
int yypparse(void);yyparse()の戻り値は、正常の場合(コンパイルエラーがなかった場合)は0、そうでなければ1が返ります。それぞれ、YYACCEPT、YYABORTというマクロが定義されていますので、これらを利用したほうが見やすいでしょう。
int main() /* main:yaccの呼び出し */
{
return yyparse();
}
|
| 目次 |