自作コンパイラの部屋 > 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(); } |
![]() |
目次 |