支持數學函數的計算器程序
爲計算器生成的另一個指令是添加用於平方根、指數和對數的數學函數。以如下方式處理輸入:
s2=sqrt(2)
s2=1.41421
二種實現方式:
方式一、
爲每個函數添加單獨的規則
例:
語法分析添加:
%token SQRT LOG EXP
。。。
%%
expression: ...
| SQRT '(' expression ')' { $$=sqrt($3); }
| LOG '(' expression ')' { $$=log($3); }
| EXP '(' expression ')' { $$=exo($3); }
詞法分析添加:
sqrt return SQRT;
log return LOG;
exp return EXP;
[A-Za-z][A-Za-z0-9]* { ...... }
(匹配函數名的特定模式要在前面,它們要比一般標識符匹配的要早。)
這種實現的缺點:
1、每個函數硬編碼到詞法分析程序和語法分析程序中,這樣會比較冗長,而且很難添加更多的函數;
2、函數名是保留字,不能將sqrt再做爲變量名;
方式二:
從詞法分析程序中拿出函數名採用的特定模式並將它們放入符號表。給每個符號表條目添加新的字段funcptr,如果這個條目是函數名,那麼它就是調用c函數的指針。
struct symtab {
char *name;
double (*funcprt)();
double value;
}symtab[NSYMS];
在開始分析程序前必須先將函數名放進符號表,所以需要在main函數中添加代碼,調用函數addfunc()將每個函數名添加到符號表,然後調用yyparse()。
int main(){
extern double sqrt(),exp(),log();
addfunc("sqrt",sqrt);
addfunc("exp",exp);
addfunc("log",log);
yyparse();
return 0;
}
void addfunc(char *name,double (*func)()){
struct symtab *sp=symlook(name);
sp->funcptr=func;
}
最終的程序代碼:
最終的計算器頭文件ch3hdr2.h
#define NSYMS 20 /* maximum number of symbols */
struct symtab {
char *name;
double (*funcptr)();
double value;
}symtab[NSYMS];
struct symtab *symlook(char *s);
最終的計算器語法分析程序ch3-5.y
%{
#include "stdio.h"
#include "ch3hdr2.h"
#include <string.h>
#include <math.h>
%}
%union {
double dval;
struct symtab *symp;
}
%token <symp> NAME
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%type <dval> expression
%%
statement_list: statement '\n'
| statement_list statement '\n'
statement: NAME '=' expression { $1->value=$3; }
| expression { printf("=%g\n",$1); }
;
expression: expression '+' expression { $$=$1+$3; }
| expression '-' expression { $$=$1-$3; }
| expression '*' expression { $$=$1*$3; }
| expression '/' expression
{ if($3==0.0)
yyerror("divided by zero.\n");
else
$$=$1 / $3;
}
| '-' expression %prec UMINUS { $$ = -$2; }
| '(' expression ')' { $$ = $2; }
| NUMBER { $$ = $1; }
| NAME { $$ = $1->value; }
| NAME '(' expression ')'
{
if($1->funcptr)
$$=($1->funcptr)($3);
else {
printf("%s is not a function.\n",$1->name);
$$=0.0;
}
}
;
%%
void yyerror(char *s);
struct symtab *symlook(char *s){
char *p;
struct symtab *sp;
for(sp=symtab;sp<&symtab[NSYMS];sp++){
if(sp->name && !strcmp(sp->name,s))
return sp;
if(!sp->name){
sp->name=strdup(s);
return sp;
}
}
yyerror("Too many symbols.");
exit(1);
}
void addfunc(char *name,double (*func)()){
struct symtab *sp=symlook(name);
sp->funcptr=func;
}
int main(){
extern double sqrt(),exp(),log();
addfunc("sqrt",sqrt);
addfunc("exp",exp);
addfunc("log",log);
yyparse();
return 0;
}
void yyerror(char *s){
fprintf(stderr,"%s\n",s);
}
最終的詞法分析程序ch3-5.l
%{
#include "y.tab.h"
#include "ch3hdr.h"
#include <math.h>
#undef yywrap
%}
%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) {
yylval.dval = atof(yytext);
return NUMBER;
}
[ \t] ;
[A-Za-z][A-Za-z0-9]* {
struct symtab *sp=symlook(yytext);
yylval.symp=sp;
return NAME;
}
"$" { return 0; }
\n |
. return yytext[0];
%%
int yywrap()
{
return 1;
}
程序編譯及運行:
[postgre@host132 ch3]$ yacc -d ch3-5.y
[postgre@host132 ch3]$ lex ch3-5.l
[postgre@host132 ch3]$ cc -o test lex.yy.c y.tab.c -lm
[postgre@host132 ch3]$ ./test
sqrt(3)
=1.73205
foo(3)
foo is not a function.
=0
sqrt=5
sqrt(sqrt)
=2.23607
log(4)
=1.38629
sqrt(4)
=2