数式の評価を便利な道具で #1
実際のところ原始的にやる気がなければいくらでも便利な道具はある。
激減するわけではないがそれなりに少ない作業量で済ませるなら、
解析器は定番のyacc/lex(実際に使用したのはbison/flex)で作成すればいい。
また、多倍長精度演算はGMPに任せた。
まず字句解析器。
字句定義のヘッダファイル名はbisonの-yオプションなしのデフォルト動作による。
calc.l
%{ #include <gmp.h> #include "calc.tab.h" #define YY_USER_INIT init_yylval() void init_yylval(void); int yylex(void); void init_yylval(void) { mpz_init(yylval.val); } %} %option nounput %option noyywrap %% [ \t] {} [0-9]+ { mpz_set_str(yylval.val, yytext, 10); return UINT; } .|\n { return yytext[0]; } <<EOF>> { mpz_clear(yylval.val); return -1; } %%
そして構文解析器。
YYSTYPEをdefineするのではなく%union宣言をわざわざ使っているのは、
defineでは字句定義のヘッダファイルにYYSTYPEの定義が挿入されないことにもある。
使用したbisonのバージョンは2.4.1であり、最新版の2.5なら大丈夫なのかも。
calc.y
%{ #include <stdio.h> #include <gmp.h> int yylex(void); void yyerror(char *s); %} %union { mpz_t val; } %token <val> UINT %left '+' %left '*' %type <val> expr %% stmt: /* empty */ | stmt '\n' | stmt expr '\n' { gmp_printf("%Zd\n", $2); mpz_clear($2); } | error '\n' { yyerrok; } ; expr: UINT { mpz_init($$); mpz_set($$, $1); } | expr '+' expr { mpz_init($$); mpz_add($$, $1, $3); mpz_clears($1, $3, NULL); } | expr '*' expr { mpz_init($$); mpz_mul($$, $1, $3); mpz_clears($1, $3, NULL); } ; %% int main(void) { yyparse(); return 0; } void yyerror(char *s) { gmp_printf("%s\n", s); }
ところで、yyvalに対してmpz_initの複数回呼び出しをしているけれど大丈夫なのか不知。
大丈夫だとは思うけどちょっと調べてみよう。