lex/yacc

lexソース

%%
"+"     return ADDOP;
"-"     return SUBOP;
"*"     return MULOP;
"/"     return DIVOP;
"("     return LP;
")"     return RP;
[0-9]+  { yylval = atoi(yytext);
          return NUMBER; }
[ \t]   ;
\n      return NL;
.       return yytext[0];
%%

lexが切り出した文字列はchar* yytextで参照できる.

yylvalに値をセットするとyaccから参照できる. 返値はyaccにどんな種類のトークンが入力されたかを教えるためのものなので, 例えば数字が入力された場合,返値としてはNUMBERを返しておき, 実際の値はyylvalにセットしてyaccに伝達するという方法をとる.

lexソースは,パターン アクションの並びで記述するが,アクションは複数行に分かれてもかまわないらしい. たぶん,行頭から始まっているとパターン,行頭以外から始まっている場合はアクションの継続行と解釈されるのだと思う.

yaccソース

%token NL
%token NUMBER
%token LP
%token RP
%token ADDOP SUBOP
%token MULOP DIVOP
%%
s      : list
       ;
list   : /* empty */
       | list expr NL    { printf("%d\n", $2); }
       ;
expr   : expr ADDOP expr  { $$ = $1 + $2; }
       | expr SUBOP expr  { $$ = $1 - $2; }
       | expr MULOP expr  { $$ = $1 * $2; }
       | expr DIVOP expr  { $$ = $1 / $2; }
       | LP expr RP       { $$ = $2; }
       | NUMBER           { $$ = $1; }
       ;
%%
#include "lex.yy.c"

乗除算は加減算より先に実行する(優先順位が高い)ので,乗除算用の%leftは加減算のものよりあとに指定する.

%leftは, そのトークンの結合規則が左から右である(同じ優先度のトークンが連続したときに左から右に評価する)ことを指示する. 同じ優先順位のトークンが複数表れた場合,もっとも左に出現したものから順に処理することを示す.

$$は構文規則の左辺,$nn = 1, 2, ...)は右辺の各トークンをあらわしている.

lex/yaccプログラムのコンパイル

$ yacc foo.y
$ lex foo.l
$ cc y.tab.c -ly -ll -o foo

lexが生成するlex.yy.cについては yaccソースで#include "lex.yy.c"と指定しているため, 上記のコマンド列には現れていない(明示的に指定する方法でもいいような気がするが,普通このようにするのが慣習のようだ).

参考資料

[1] Lex and YACC primer/HOW-TO
[2] Lex and YACC HOW-TO

はたいたかし
トップ > 開発ツール > yacc