電卓作ってみた。

昨日、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の解析とか。メールヘッダ読んだりとか。

再帰がいっぱい出てくるので、頭の中を整理するのが結構大変です。マイナスはまた今度やろうかな・・・。

追記

マニュアル見ずに書いたけど、マニュアルでもほぼ同じように作ってた!!

才能あるなぁ僕・・・(ほめすぎ

後で熟読しよう〜っと。