支持数学函数的计算器程序
为计算器生成的另一个指令是添加用于平方根、指数和对数的数学函数。以如下方式处理输入:
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