C指针原理教程之编译原理-小型计算器实现
1、打开cygwin,进入home目录,home目录在WINDOWS系统的cygwin安装目录映射为home目录。
2、首先,在home目录中新建文件夹,在文件夹中放置如下内容的test1.l
/*统计字数*/
%{
intchars=0;
intwords=0;
intlines=0;
%}
%%
[a-zA-Z]+{words++;chars+=strlen(yytext);}
\n{chars++;lines++;}
.{chars++;}
%%
main(intargc,char**argv)
{
yylex();
printf("%d%d%d\n",lines,words,chars);
}
然后调用flex生成词法分析器
Administrator@2012-20121224HD/home/flexlinux $cd/home Administrator@2012-20121224HD/home $cdflexlinux Administrator@2012-20121224HD/home/flexlinux $flextest1.l Administrator@2012-20121224HD/home/flexlinux $
可以看到目录中的lex.yy.c就是刚生成的C源码,可分析词法。
Administrator@2012-20121224HD/home/flexlinux $ls lex.yy.ctest1.l
二、flex和bison联合工作
1、我们开始构造一个计算器程序。
创建flex代码
/*计算器*/
%{
enumyytokentype{
NUMBER=258,
ADD=259,
SUB=260,
MUL=261,
DIV=262,
ABS=263,
EOL=264
};
intyylval;
%}
%%
"+"{returnADD;}
"-"{returnSUB;}
"*"{returnMUL;}
"/"{returnDIV;}
"|"{returnABS;}
[0-9]+{yylval=atoi(yytext);returnNUMBER;}
\n{returnEOL;}
[\t]{/*空白忽略*/}
.{printf("非法字符%c\n",*yytext);}
%%
main(intargc,char**argv)
{
inttok;
while(tok=yylex()){
printf("%d",tok);
if(tok==NUMBER)printf("=%d\n",yylval);
elseprintf("\n");
}
}
2、编译
Administrator@2012-20121224HD/home/flexlinux $flextest2.l Administrator@2012-20121224HD/home/flexlinux $gcclex.yy.c-lfl
3、运行
Administrator@2012-20121224HD/home/flexlinux $./a -1266 260 258=12 258=66 264 Administrator@2012-20121224HD/home/flexlinux $./a /562+|32 262 258=56 258=2 259 263 258=32 264 Administrator@2012-20121224HD/home/flexlinux $
(2)计算器的BISON程序
%{
#include
%}
%tokenNUMBER
%tokenADDSUBMULDIVABS
%tokenEOL
%%
calclist:/**/
|calclistexpEOL{printf("=%d\n",$2);}
;
exp:factor{$$=$1;}
|expADDfactor{$$=$1+$3;}
|expSUBfactor{$$=$1-$3;}
;
factor:term{$$=$1;}
|factorMULterm{$$=$1*$3;}
|factorDIVterm{$$=$1/$3;}
;
term:NUMBER{$$=$1;}
|ABSterm{$$=$2>=0?$2:-$2;}
;
%%
main(intargc,char**argv){
yyparse();
}
yyerror(char*s)
{
fprintf(stderr,"error:%s\n",s);
}
$bison-dtest2.y
t$ls
test2.tab.ctest2.tab.htest2.ytest2.y~
然后,修改刚才的flex文件,将其命名为test21.l
test2.tab.h中包含了记号编号的定义和yylval的定义,因此,将其第一部分的相关定义删除,并改为:
/计算器/
%{
#include"test2.tab.h"
%}
然后删除,其第三部分的main函数。
最后,进行编译。
bison-dtest2.y flextest21.l gcctest2.tab.clex.yy.c-lfl
可以测试一下
root@myhaspl:~#./a.out 12+36*2 =84 12/6+2*3 =8
(2)扩充计算器
加入对括号和注释的支持,
首先修改flex文件,在第二部分加入更多的词法规则(对于注释直接忽略):
"(" {returnLEFTBRACKET;}
")" {returnRIGHTBRACKET;}
"#"./忽略注释*/
然后,修改bison文件,在第二部分加入更多的语法规则:
term:NUMBER{$$=$1;}
|ABSterm{$$=$2>=0?$2:-$2;}
|LEFTBRACKETexpRIGHTBRACKET{$$=$2;}
;
我们的注释以“#”表示
测试结果
myhaspl@myhaspl:~/flex_bison/2$make bison-dcalculator.y flexcalculator.l gcccalculator.tab.clex.yy.c-lfl myhaspl@myhaspl:~/flex_bison/2$ls a.outcalculator.tab.ccalculator.ymakefile calculator.lcalculator.tab.hlex.yy.c myhaspl@myhaspl:~/flex_bison/2$./a.out 12-36*10/(1+2+3)#compute =-48 ^C myhaspl@myhaspl:~/flex_bison/2$
前面都是以键盘输入的方式进行计算器运算,我们下面以文件方式提供给该解释器进行计算,首先,将flex文件改为(将其中中文去除,然后对于非法字符的出现进行忽略):
%{
#include"calculator.tab.h"
%}
%%
"+"{returnADD;}
"-"{returnSUB;}
""{returnMUL;}
"/"{returnDIV;}
"|"{returnABS;}
"("{returnLEFTBRACKET;}
")"{returnRIGHTBRACKET;}
"#"./comment/
[0-9]+{yylval=atoi(yytext);returnNUMBER;}
\n{returnEOL;}
[\t]/blank/
./invalidchar/
%
接着,改bison文件,加入对文件的读写
%{
#include
%}
%tokenNUMBER
%tokenADDSUBMULDIVABSLEFTBRACKETRIGHTBRACKET
%tokenEOL
%%
calclist:/**/
|calclistexpEOL{printf("=%d\n",$2);}
;
exp:factor{$$=$1;}
|expADDfactor{$$=$1+$3;}
|expSUBfactor{$$=$1-$3;}
;
factor:term{$$=$1;}
|factorMULterm{$$=$1*$3;}
|factorDIVterm{$$=$1/$3;}
;
term:NUMBER{$$=$1;}
|ABSterm{$$=$2>=0?$2:-$2;}
|LEFTBRACKETexpRIGHTBRACKET{$$=$2;}
;
%%
main(intargc,char**argv){
inti;
if(argc<2){
yyparse();
}
else{
for(i=1;i
最后测试一下
root@myhaspl:~/test/3#make
bison-dcalculator.y
flexcalculator.l
gcccalculator.tab.clex.yy.c-lfl
root@myhaspl:~/test/3#./a.outmycpt1.cptmycpt2.cpt
=158
=-8
root@myhaspl:~/test/3#
其中两个CPT文件内容类似为:
12*66/(10-5)
我们接着完善这个计算器程序,让算式能显示出来,修改calculator.l
通过加入printf语句,打印词法分析器解析到的字符。比如:
..................
[0-9]+{yylval=atoi(yytext);printf("%d",yylval);returnNUMBER;}
\n {returnEOL;}
[\t]/blank/
./invalidchar/
%%
然后编译执行。
root@myhaspl:~/test/4#make
bison-dcalculator.y
flexcalculator.l
gcccalculator.tab.clex.yy.c-lfl
root@myhaspl:~/test/4#./a.out
12+66
12+66=78
^C
root@myhaspl:~/test/4#./a.outmycpt1.cptmycpt2.cpt
12*66/(10-5)=158
77/(10+1)-15=-8
接下来加上读取的行号,将结果的显示更加人性化
flex文件要改:
\n {printf("",yylineno);yylineno++;returnEOL;}
然后,bison文件也改:
calclist:/**/
|calclistexpEOL{printf("theresultis:%d\n",$2);}
;
最后,编译运行测试一下。
root@myhaspl:~/test/4#make
bison-dcalculator.y
flexcalculator.l
gcccalculator.tab.clex.yy.c-lfl
root@myhaspl:~/test/4#./a.outmycpt1.cptmycpt2.cpt
1266/(10-5)theresultis:158
12/22-8theresultis:-8
77(6-2)theresultis:308
77/(10+1)-15theresultis:-8
root@myhaspl:~/test/4#