yacc學習筆記(五)支持數學函數的計算器程序

支持數學函數的計算器程序

爲計算器生成的另一個指令是添加用於平方根、指數和對數的數學函數。以如下方式處理輸入:
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章