<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">我怎么也没想到来公司了竟然是做编译原理相关的工作。 毕竟没有学过编译原理 ,看到正则表达式就晕了。T_T</span>
刚刚拿到flex & bison 这本书的时候 感觉一头雾水,完全知道干什么的。第一天看了词法分析工具flex。直接用flex写出统计单词,行数,字符数的程序,比起直接用C++去模拟,简单的多了。顿时就感觉这个东西好叼。然后就看到了bison 这个这个东西, 后来才懂flex & bison 这2个东西 一个时词法分析,一个时语法分析。2个东西在一起就可以写编译器了。。。。
* wc程序 */ %option noyywrap %{ int chars = 0; int words =0; int lines = 0; %} %% [a-zA-Z]+ { ++words;chars += strlen(yytext) ;} \n { ++lines ; ++chars;} . { ++chars ; } %% int main(int argc,char* argv[]) { yylex(); printf("%d %d %d\n",lines,words,chars); return 0; }
虽然写一个计算器其实时很简单的,但是还是整整用了2天才写出一个完成功能的计算器 。。。。
首先是写词法分析分析器 calc1-2.l ,包含了调试的代码
/*calc1-2.l*/ %{ #define YYSTYPE double #include "calc1-2.tab.h" #include <stdlib.h> #ifdef debug YYSTYPE yylval; #endif %} %% "+" { return ADD; } "-" { return SUB; } "*" { return MUL; } "/" { return DIV; } "|" { return ABS; } "(" { return OP; } ")" { return CP; } ([0-9]*\.?[0-9]+|[0-9]+\.) { yylval = atof(yytext); return NUMBER;} "sqrt" { return SQRT; } "**" { return SQR; } \n { return EOL; } [ \t] {} "//".* {} %% #ifdef debug int main(int argc ,char* argv[]) { int token; while(token = yylex()) { printf("%d",token); if(token ==NUMBER) { printf("= %f\n",yylval); } else printf("\n"); } return 0; } #endif
然后就是些语法分析器。我想实现的功能比较多,所以刚开始写规则的时候有点乱,后来理清楚了,才运行正确。
语法分析器 calc1-2.y
/* calc1-2.y*/ %{ #define YYSTYPE double #include<stdio.h> #include<math.h> %} %token NUMBER %token ADD SUB MUL DIV ABS %token EOL %token OP CP %token SQR SQRT %% calclist: | calclist exp EOL { printf("ans = %f\n>>> " ,$2 ); } ; exp: factor { $$ = $1; } | exp ADD factor { $$ = $1 + $3 ; } | exp SUB factor { $$ = $1 - $3 ; } ; factor: tmp { $$ =$1; } | factor MUL tmp { $$ = $1 * $3; } | factor DIV tmp { $$ = $1 / $3; } ; tmp: term { $$ = $1; } | SUB tmp { $$ = -$2; } | tmp SQR tmp { $$ = pow($1,$3); /*printf("%d %d %d\n",$1,$2,$3);*/} ; term: NUMBER { $$ = $1;} | ABS exp ABS { $$ = fabs($2);} | OP exp CP { $$ = $2; } | SQRT OP exp CP { $$ = sqrt($3) ;/*printf(" %d %d %d\n",$1,$2,$3);*/ } ; %% int main() { printf(">>> "); while(1) yyparse(); return 0; } yyerror(char *s) { fprintf(stderr,"error : %s\n",s); }
关于编译,看到了makfile这个东西,所以就去看了一点基础的。 用makefile 可以实现自动化编译。
/*makefile*/ calc1-2: calc1-2.l calc1-2.y bison -d calc1-2.y flex -o calc1-2.lex.c calc1-2.l cc -o $@ calc1-2.tab.c calc1-2.lex.c -ll calc1-2.lex:calc1-2.y calc1-2.l bison -d calc1-2.y flex -o calc1-2.lex.c calc1-2.l cc -D debug -o $@ calc1-2.lex.c -ll clean: rm calc1-2.lex.c rm calc1-2.tab.h rm calc1-2.tab.c rm calc1-2
编译 calc1-2 没有调试的
➜ calc1-2 make calc1-2 bison -d calc1-2.y calc1-2.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr] flex -o calc1-2.lex.c calc1-2.l cc -o calc1-2 calc1-2.tab.c calc1-2.lex.c -ll calc1-2.tab.c:1141:16: warning: implicit declaration of function 'yylex' is invalid in C99 [-Wimplicit-function-declaration] yychar = yylex (); ^ calc1-2.tab.c:1354:7: warning: implicit declaration of function 'yyerror' is invalid in C99 [-Wimplicit-function-declaration] yyerror (YY_("syntax error")); ^ calc1-2.y:46:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int] yyerror(char *s) ^ calc1-2.y:49:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ 4 warnings generated.
编译带调试功能的calc1-2.lex
➜ calc1-2 make calc1-2.lex bison -d calc1-2.y calc1-2.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr] flex -o calc1-2.lex.c calc1-2.l cc -D debug -o calc1-2.lex calc1-2.lex.c -ll calc1-2.l:32:17: warning: using the result of an assignment as a condition without parentheses [-Wparentheses] while(token = yylex()) ~~~~~~^~~~~~~~~ calc1-2.l:32:17: note: place parentheses around the assignment to silence this warning while(token = yylex()) ^ ( ) calc1-2.l:32:17: note: use '==' to turn this assignment into an equality comparison while(token = yylex()) ^ == 1 warning generated.
运行calc1-2 测试
➜ calc1-2 ./calc1-2 >>> 5/2 + 2 ans = 4.500000 >>> sqrt(3)**2 ans = 3.000000 >>> 1+4**0.5 ans = 3.000000 >>> ^C
➜ calc1-2 make clean rm calc1-2.lex.c rm calc1-2.tab.h rm calc1-2.tab.c rm calc1-2