1、打开cygwin,进入home目录,home目录在WINDOWS系统的cygwin安装目录映射为home目录。
2、首先,在home目录中新建文件夹,在文件夹中放置如下内容的test1.l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/*统计字数*/ %{ 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生成词法分析器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Administrator@2012-20121224HD /home/flexlinux $ cd /home Administrator@2012-20121224HD /home $ cd flexlinux Administrator@2012-20121224HD /home/flexlinux $ flex test1.l Administrator@2012-20121224HD /home/flexlinux $ |
可以看到目录中的lex.yy.c就是刚生成的C源码,可分析词法。
1
2
3
4
5
|
Administrator@2012-20121224HD /home/flexlinux $ ls lex.yy.c test1.l |
二、flex和bison联合工作
1 、我们开始构造一个计算器程序。
创建flex代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
/*计算器*/ %{ 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、编译
1
2
3
4
5
6
7
|
Administrator@2012-20121224HD /home/flexlinux $ flex test2.l Administrator@2012-20121224HD /home/flexlinux $ gcc lex.yy.c -lfl |
3、运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
Administrator@2012-20121224HD /home/flexlinux $ . /a - 12 66 260 258=12 258=66 264 Administrator@2012-20121224HD /home/flexlinux $ . /a / 56 2 + |32 262 258=56 258=2 259 263 258=32 264 Administrator@2012-20121224HD /home/flexlinux $ |
(2)计算器的BISON程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
%{ #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.y t$ ls test2.tab.c test2.tab.h test2.y test2.y~ |
然后,修改刚才的flex文件,将其命名为test21.l
test2.tab.h中包含了记号编号的定义和yylval的定义,因此,将其第一部分的相关定义删除,并改为:
/计算器/
%{
#include "test2.tab.h"
%}
然后删除,其第三部分的main函数。
最后,进行编译。
1
2
3
4
5
|
bison -d test2.y flex test21.l gcc test2.tab.c lex.yy.c -lfl |
可以测试一下
1
2
3
4
5
6
7
8
9
|
root@myhaspl:~ # ./a.out 12 + 36 * 2 =84 12 / 6 + 2 * 3 =8 |
(2)扩充计算器
加入对括号和注释的支持,
首先修改flex文件,在第二部分加入更多的词法规则(对于注释直接忽略):
"(" {return LEFTBRACKET;}
")" {return RIGHTBRACKET;}
"#". /忽略注释*/
然后,修改bison文件,在第二部分加入更多的语法规则:
term:NUMBER {$$=$1;}
|ABS term {$$=$2>=0?$2:-$2;}
|LEFTBRACKET exp RIGHTBRACKET {$$=$2;}
;
我们的注释以“#”表示
测试结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
myhaspl@myhaspl:~ /flex_bison/2 $ make bison -d calculator.y flex calculator.l gcc calculator.tab.c lex.yy.c -lfl myhaspl@myhaspl:~ /flex_bison/2 $ ls a.out calculator.tab.c calculator.y makefile calculator.l calculator.tab.h lex.yy.c myhaspl@myhaspl:~ /flex_bison/2 $ . /a .out 12-36*10/(1+2+3) #compute =-48 ^C myhaspl@myhaspl:~ /flex_bison/2 $ |
前面都是以键盘输入 的方式进行计算器运算,我们下面以文件方式提供给该解释器进行计算,首先,将flex文件改为(将其中中文去除,然后对于非法字符的出现进行忽略):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
%{ #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文件,加入对文件的读写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
%{ #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); } |
最后 测试一下
1
2
3
4
5
6
7
8
|
root@myhaspl:~ /test/3 # make bison -d calculator.y flex calculator.l gcc calculator.tab.c lex.yy.c -lfl root@myhaspl:~ /test/3 # ./a.out mycpt1.cpt mycpt2.cpt =158 =-8 root@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/
%%
然后编译执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
root@myhaspl:~ /test/4 # make bison -d calculator.y flex calculator.l gcc calculator.tab.c lex.yy.c -lfl root@myhaspl:~ /test/4 # ./a.out 12+66 12+66=78 ^C root@myhaspl:~ /test/4 # ./a.out mycpt1.cpt mycpt2.cpt 12*66/(10-5)=158 77/(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);}
;
最后 ,编译运行测试一下。
1
2
3
4
5
6
7
8
9
10
|
root@myhaspl:~ /test/4 # make bison -d calculator.y flex calculator.l gcc calculator.tab.c lex.yy.c -lfl root@myhaspl:~ /test/4 # ./a.out mycpt1.cpt mycpt2.cpt 1266/(10-5)<line: 1>the result is:158 12 /22-8 <line: 2>the result is:-8 77(6-2)<line: 3>the result is:308 77/(10+1)-15<line: 4>the result is:-8 root@myhaspl:~ /test/4 # |