C指针原理教程之编译原理-小型计算器实现

时间:2021-05-20

1、打开cygwin,进入home目录,home目录在WINDOWS系统的cygwin安装目录映射为home目录。

2、首先,在home目录中新建文件夹,在文件夹中放置如下内容的test1.l

%{int chars=0;int words=0;int lines=0;%}%%[a-zA-Z]+ {words++;chars+=strlen(yytext);}\n {chars++;lines++;}. {chars++;}%%main(int argc,char**argv){ yylex(); printf("%d%d%d\n",lines,words,chars);}

然后调用flex生成词法分析器

Administrator@2012-20121224HD /home/flexlinux$ cd /homeAdministrator@2012-20121224HD /home$ cd flexlinuxAdministrator@2012-20121224HD /home/flexlinux$ flex test1.lAdministrator@2012-20121224HD /home/flexlinux$

可以看到目录中的lex.yy.c就是刚生成的C源码,可分析词法。

Administrator@2012-20121224HD /home/flexlinux$ lslex.yy.c test1.l

二、flex和bison联合工作

1 、我们开始构造一个计算器程序。

创建flex代码

%{enum yytokentype{ NUMBER=258, ADD=259, SUB=260, MUL=261, DIV=262, ABS=263, EOL=264};int yylval;%}%%"+" {return ADD;}"-" {return SUB;}"*" {return MUL;}"/" {return DIV;}"|" {return ABS;}[0-9]+ {yylval=atoi(yytext);return NUMBER;}\n {return EOL;}[ \t] {}. {printf("非法字符 %c\n",*yytext);}%%main(int argc,char**argv){ int tok; while(tok=yylex()){ printf("%d",tok); if (tok==NUMBER) printf("=%d\n",yylval); else printf("\n"); }}

2、编译

Administrator@2012-20121224HD /home/flexlinux$ flex test2.lAdministrator@2012-20121224HD /home/flexlinux$ gcc lex.yy.c -lfl

3、运行

Administrator@2012-20121224HD /home/flexlinux$ ./a- 12 66260258=12258=66264Administrator@2012-20121224HD /home/flexlinux$ ./a/ 56 2 + |32262258=56258=2259263258=32264Administrator@2012-20121224HD /home/flexlinux$

(2)计算器的BISON程序

%{#include <stdio.h>%}%token NUMBER%token ADD SUB MUL DIV ABS%token EOL%%calclist: |calclist exp EOL{printf ("=%d\n",$2);} ;exp:factor {$$ = $1;} |exp ADD factor{$$=$1+$3;} |exp SUB factor{$$=$1-$3;} ;factor:term {$$=$1;} |factor MUL term{$$=$1*$3;} |factor DIV term{$$=$1/$3;} ;term:NUMBER {$$=$1;} |ABS term {$$=$2>=0?$2:-$2;} ;%%main(int argc,char **argv){yyparse();}yyerror(char *s){ fprintf(stderr,"error:%s\n",s);}$ bison -d test2.yt$ lstest2.tab.c test2.tab.h test2.y test2.y~

然后,修改刚才的flex文件,将其命名为test21.l

test2.tab.h中包含了记号编号的定义和yylval的定义,因此,将其第一部分的相关定义删除,并改为:

/计算器/

%{

#include "test2.tab.h"

%}

然后删除,其第三部分的main函数。

最后,进行编译。

bison -d test2.yflex test21.lgcc test2.tab.c lex.yy.c -lfl

可以测试一下

root@myhaspl:~# ./a.out12 + 36 * 2=8412 / 6 + 2 * 3=8

(2)扩充计算器

加入对括号和注释的支持,

首先修改flex文件,在第二部分加入更多的词法规则(对于注释直接忽略):

"(" {return LEFTBRACKET;}

")" {return RIGHTBRACKET;}

"#". /忽略注释*/

然后,修改bison文件,在第二部分加入更多的语法规则:

term:NUMBER {$$=$1;}

|ABS term {$$=$2>=0?$2:-$2;}

|LEFTBRACKET exp RIGHTBRACKET {$$=$2;}

;

我们的注释以“#”表示

测试结果

myhaspl@myhaspl:~/flex_bison/2$ makebison -d calculator.yflex calculator.lgcc calculator.tab.c lex.yy.c -lflmyhaspl@myhaspl:~/flex_bison/2$ lsa.out calculator.tab.c calculator.y makefilecalculator.l calculator.tab.h lex.yy.cmyhaspl@myhaspl:~/flex_bison/2$ ./a.out12-36*10/(1+2+3)#compute=-48^Cmyhaspl@myhaspl:~/flex_bison/2$

前面都是以键盘输入 的方式进行计算器运算,我们下面以文件方式提供给该解释器进行计算,首先,将flex文件改为(将其中中文去除,然后对于非法字符的出现进行忽略):

%{#include "calculator.tab.h"%}%%"+" {return ADD;}"-" {return SUB;}"" {return MUL;}"/" {return DIV;}"|" {return ABS;}"(" {return LEFTBRACKET;}")" {return RIGHTBRACKET;}"#". /comment/[0-9]+ {yylval=atoi(yytext);return NUMBER;}\n {return EOL;}[ \t] /blank/. /invalid char/%

接着,改bison文件,加入对文件的读写

%{#include <stdio.h>%}%token NUMBER%token ADD SUB MUL DIV ABS LEFTBRACKET RIGHTBRACKET%token EOL %%calclist: |calclist exp EOL{printf ("=%d\n",$2);} ; exp:factor {$$ = $1;} |exp ADD factor{$$=$1+$3;} |exp SUB factor{$$=$1-$3;} ; factor:term {$$=$1;} |factor MUL term{$$=$1*$3;} |factor DIV term{$$=$1/$3;} ;term:NUMBER {$$=$1;} |ABS term {$$=$2>=0?$2:-$2;} |LEFTBRACKET exp RIGHTBRACKET {$$=$2;} ;%%main(int argc,char **argv){int i;if (argc<2){ yyparse();}else{ for(i=1;i<argc;i++) { FILE *f=fopen(argv[i],"r"); if (!f){ perror(argv[i]); return (1); } yyrestart(f); yyparse(); fclose(f); }}}yyerror(char *s){ fprintf(stderr,"error:%s\n",s);}

最后 测试一下

root@myhaspl:~/test/3# makebison -d calculator.yflex calculator.lgcc calculator.tab.c lex.yy.c -lflroot@myhaspl:~/test/3# ./a.out mycpt1.cpt mycpt2.cpt=158=-8root@myhaspl:~/test/3#

其中两个CPT文件内容类似 为:

12*66/(10-5)

我们接着完善这个计算器程序,让算式能显示出来,修改calculator.l

通过加入printf语句,打印词法分析器解析到的字符。比如 :

..................

[0-9]+ {yylval=atoi(yytext);printf("%d",yylval);return NUMBER;}

\n {return EOL;}

[ \t] /blank/

. /invalid char/

%%

然后编译执行。

root@myhaspl:~/test/4# makebison -d calculator.yflex calculator.lgcc calculator.tab.c lex.yy.c -lflroot@myhaspl:~/test/4# ./a.out12+6612+66=78^Croot@myhaspl:~/test/4# ./a.out mycpt1.cpt mycpt2.cpt 12*66/(10-5)=15877/(10+1)-15=-8

接下来加上读取的行号,将结果的显示更加人性化

flex文件要改:

\n {printf("<line:%4d>",yylineno);yylineno++;return EOL;}

然后,bison文件也改:

calclist:
|calclist exp EOL{printf ("the result is:%d\n",$2);}
;

最后 ,编译运行测试一下。

root@myhaspl:~/test/4# makebison -d calculator.yflex calculator.lgcc calculator.tab.c lex.yy.c -lflroot@myhaspl:~/test/4# ./a.out mycpt1.cpt mycpt2.cpt1266/(10-5)<line: 1>the result is:15812/22-8<line: 2>the result is:-877(6-2)<line: 3>the result is:30877/(10+1)-15<line: 4>the result is:-8root@myhaspl:~/test/4#

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章