電卓作ってみた。
昨日、Rubyのソースコードを見ていたら、構文解析部分はyaccで作られていることがわかった。僕でも電卓くらいなら作れそうだ!!という気分になったので、Flex&Bisonで電卓を作ってみた。
電卓といっても計算は正の整数のみ。演算子は+,-,*,/で、()の計算も出来ます。
calc.l
Flex(lex)は字句解析担当です。この時点で文字の入力を数値に変換してBisonにreturnします。
%{ #include <stdio.h> #include <stdlib.h> #include "calc.tab.h" %} NUMBER [0-9]+ SPACE [\t ] OP [+-/*()] CR [\n] OTHER . %% {NUMBER} { yylval = atoi(yytext); return NUMBER; } {OP} { return yytext[0]; } {CR} { return yytext[0]; } {SPACE} {} {OTHER} { printf("そんなのしらない\n"); exit(1); } %%
グローバル変数、yytextの中に入力文字、yyvalは$1に変換する値が入っているのでBisonに渡します。
calc.l
Bison(yacc)は構文解析担当。構文を解析するだけなので構文木は自分で作る必要がある。今回は数値のみなので、計算結果が直接$$に入る。
%{ #include <stdio.h> #include <string.h> void yyerror(const char *str) { fprintf(stderr,"error: %s\n",str); } int yywrap() { return 1; } int main() { yyparse(); } %} %token NUMBER %left '+' '-' %left '*' '/' %% lines : line | lines line ; line : '\n' | ex '\n' { printf("%d\n", $1); } ; ex : NUMBER | '(' ex ')' { $$ = $2; } | ex '+' ex { $$ = $1 + $3; } | ex '-' ex { $$ = $1 - $3; } | ex '*' ex { $$ = $1 * $3; } | ex '/' ex { $$ = $1 / $3; } ; %%
%left部分で演算子の優先順位を作ることが出来る。下の方が優先順位が高い。
Makefile
all : bison -d calc.y flex calc.l cc lex.yy.c calc.tab.c -o calc
実行結果は・・・
% make bison -d calc.y flex calc.l cc lex.yy.c calc.tab.c -o calc % ./calc (2 + 3) * (4 - 6) / 2 -5 2 * 3 + 4 * 5 26
演算子の優先順位を区別して、複雑な計算もこなします。マイナス記号には対応していないので、(0 - 1)として、-1を作り出してください(笑
Flex&Bisonを使うと、複雑な字句解析やら、構文解析を自動的に生成してくれるので、コンパイラ以外の用途にも色々使えそうです。RSSの解析とか。メールヘッダ読んだりとか。
再帰がいっぱい出てくるので、頭の中を整理するのが結構大変です。マイナスはまた今度やろうかな・・・。