编译原理与技术lab2

编译原理与技术 实验2

目的和要求

目的:掌握Lex源代码的编写;按照给定词法规则,编写Lex代码,并编译生成对应词法分析器,识别出单词符号作为输出,发现其中的词法错误。
要求:(1)从键盘上/文件中输入源程序;(2)处理每个单词,计算单词的值和种别;(3)输出单词值和种别。

实验步骤

运行code/example/目录下的示例代码

在路径下依次输入

1
2
3
flex lex.l
gcc .\lex.yy.c -o lexer
.\lexer.exe

img1

可见输入对应的程序代码后词法分析器正常运作

根据以下给定的词法规则和输出格式,编写Lex代码,生成词法分析器

IDN,INT,FLOAT

最简单的三个,写好正则表达式即可

1
2
3
([a-zA-Z]|_)([a-zA-Z]|[0-9]|_)*        { printf("%s\t<IDN,%s>\n", yytext,yytext); }
[0-9]+ { printf("%s\t<INT,%s>\n", yytext,yytext); }
[0-9]+\.[0-9]*|\.[0-9]+ { printf("%s\t<FLOAT,%s>\n", yytext, yytext); }

自行测试如下

img2

KW

这部分相对原来的样例代码多了两个问题:如何确定对应的text对应哪个数字标识? 如何匹配大小写的区分?

第一个问题:如何确定对应的text对应哪个数字标识。由于数据量较少,我们直接写在了输出里,其实可以开一个数组一一对应text与数字的关系。

第二个问题:如何匹配大小写的区分,将单词拆分为正则表达式的或操作,覆盖所有可能的大小写形式,其实也可以写一个将所有输入转换为小写形式的函数,转换之后再进行操作。

1
2
3
4
5
6
7
8
[iI][nN][tT]       {  printf("%s\t<KW,%d>\n", yytext, 1);  }
[vV][oO][iI][dD] { printf("%s\t<KW,%d>\n", yytext, 2); }
[rR][eE][tT][uU][rR][nN] { printf("%s\t<KW,%d>\n", yytext, 3); }
[cC][oO][nN][sS][tT] { printf("%s\t<KW,%d>\n", yytext,4); }
[mM][aA][iI][nN] { printf("%s\t<KW,%d>\n", yytext, 5); }
[fF][lL][oO][aA][tT] { printf("%s\t<KW,%d>\n", yytext, 6); }
[iI][fF] { printf("%s\t<KW,%d>\n", yytext,7); }
[eE][lL][sS][eE] { printf("%s\t<KW,%d>\n", yytext, 8); }

img3

OP,SE

转换一下输出格式即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"+"     { printf("%s\t<OP,%d>\n", yytext, 9); }
"-" { printf("%s\t<OP,%d>\n", yytext, 10); }
"*" { printf("%s\t<OP,%d>\n", yytext, 11); }
"/" { printf("%s\t<OP,%d>\n", yytext, 12); }
"%" { printf("%s\t<OP,%d>\n", yytext, 13); }
"=" { printf("%s\t<OP,%d>\n", yytext, 14); }
">" { printf("%s\t<OP,%d>\n", yytext, 15); }
"<" { printf("%s\t<OP,%d>\n", yytext, 16); }
"==" { printf("%s\t<OP,%d>\n", yytext, 17); }
"<=" { printf("%s\t<OP,%d>\n", yytext, 18); }
">=" { printf("%s\t<OP,%d>\n", yytext, 19); }
"!=" { printf("%s\t<OP,%d>\n", yytext, 20); }
"&&" { printf("%s\t<OP,%d>\n", yytext, 21); }
"||" { printf("%s\t<OP,%d>\n", yytext, 22); }

"(" { printf("%s\t<SE,%d>\n", yytext, 23); }
")" { printf("%s\t<SE,%d>\n", yytext, 24); }
"{" { printf("%s\t<SE,%d>\n", yytext, 25); }
"}" { printf("%s\t<SE,%d>\n", yytext, 26); }
";" { printf("%s\t<SE,%d>\n", yytext, 27); }
"," { printf("%s\t<SE,%d>\n", yytext, 28); }

img4

ERROR

唯一的一个棘手的问题,我该如何知道当前出错符号的行和列呢?
维护一个行与列的变量,
让每一次读取之后都给行加上对应的text的length,读取空格加1,读取换行列加1,行置为0。

直接贴出完整代码

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
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

long long col=1;
long long row=1;

%}

%%

[iI][nN][tT] { printf("%s\t<KW,%d>\n", yytext, 1); col+=yyleng;}
[vV][oO][iI][dD] { printf("%s\t<KW,%d>\n", yytext, 2); col+=yyleng;}
[rR][eE][tT][uU][rR][nN] { printf("%s\t<KW,%d>\n", yytext, 3); col+=yyleng; }
[cC][oO][nN][sS][tT] { printf("%s\t<KW,%d>\n", yytext,4); col+=yyleng; }
[mM][aA][iI][nN] { printf("%s\t<KW,%d>\n", yytext, 5); col+=yyleng; }
[fF][lL][oO][aA][tT] { printf("%s\t<KW,%d>\n", yytext, 6); col+=yyleng; }
[iI][fF] { printf("%s\t<KW,%d>\n", yytext,7); col+=yyleng;}
[eE][lL][sS][eE] { printf("%s\t<KW,%d>\n", yytext, 8); col+=yyleng;}

"+" { printf("%s\t<OP,%d>\n", yytext, 9); col+=yyleng;}
"-" { printf("%s\t<OP,%d>\n", yytext, 10); col+=yyleng;}
"*" { printf("%s\t<OP,%d>\n", yytext, 11); col+=yyleng;}
"/" { printf("%s\t<OP,%d>\n", yytext, 12);col+=yyleng; }
"%" { printf("%s\t<OP,%d>\n", yytext, 13); col+=yyleng;}
"=" { printf("%s\t<OP,%d>\n", yytext, 14); col+=yyleng;}
">" { printf("%s\t<OP,%d>\n", yytext, 15); col+=yyleng;}
"<" { printf("%s\t<OP,%d>\n", yytext, 16); col+=yyleng;}
"==" { printf("%s\t<OP,%d>\n", yytext, 17); col+=yyleng;}
"<=" { printf("%s\t<OP,%d>\n", yytext, 18);col+=yyleng; }
">=" { printf("%s\t<OP,%d>\n", yytext, 19); col+=yyleng;}
"!=" { printf("%s\t<OP,%d>\n", yytext, 20); col+=yyleng;}
"&&" { printf("%s\t<OP,%d>\n", yytext, 21); col+=yyleng;}
"||" { printf("%s\t<OP,%d>\n", yytext, 22); col+=yyleng;}

"(" { printf("%s\t<SE,%d>\n", yytext, 23); col+=yyleng;}
")" { printf("%s\t<SE,%d>\n", yytext, 24); col+=yyleng;}
"{" { printf("%s\t<SE,%d>\n", yytext, 25); col+=yyleng;}
"}" { printf("%s\t<SE,%d>\n", yytext, 26); col+=yyleng;}
";" { printf("%s\t<SE,%d>\n", yytext, 27); col+=yyleng;}
"," { printf("%s\t<SE,%d>\n", yytext, 28); col+=yyleng;}

([a-zA-Z]|_)([a-zA-Z]|[0-9]|_)* { printf("%s\t<IDN,%s>\n", yytext,yytext); col+=yyleng;}
[0-9]+ { printf("%s\t<INT,%s>\n", yytext,yytext); col+=yyleng;}
[0-9]+\.[0-9]*|\.[0-9]+ { printf("%s\t<FLOAT,%s>\n", yytext, yytext); col+=yyleng;}
\n {col=1; row+=1;}
[ \t\r] {col+=yyleng;}
. {
printf("%s\t<ERROR,%d,%d>\n", yytext, row, col);
col += yyleng;
}

%%

int yywrap(void) {
return 1;
}

int main(void) {
printf("Enter text (Ctrl+Z to quit):\n");
yylex();
return 0;
}

重点在于每次操作后col加上yyleng,以及对换行和空格的处理。

img5

自行构造测试用例(包括正确程序和错误程序),输出对应符号表

在此之前,我们重写main函数,使其能够在命令行直接使用其他文件进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main(int argc, char *argv[]) {
if (argc > 1) {
yyin = fopen(argv[1], "r");
if (!yyin) {
fprintf(stderr, "无法打开文件: %s\n", argv[1]);
return 1;
}
} else {
yyin = stdin;
printf("Enter text (Ctrl+Z to quit):\n");
}

yylex();

if (argc > 1) {
fclose(yyin);
}
return 0;
}

test1:正确程序

原程序

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
int main() {
int a = 10;
float b = 3.14;
float c = .5;
const int MAX = 100;

if (a > 5) {
return 1;
} else {
return 0;
}

float result = a * b + c;
int x = 10, y = 20;

if (x == y && a != b) {
x = x + y;
}

if (x <= y || a >= b) {
y = y % 3;
}
void testFunction() {
int local_var = 42;
}
}

测试结果

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
PS C:\Users\X.J\Desktop\lab2\code\work> ./lexer test1.cmm
int <KW,1>
main <KW,5>
( <SE,23>
) <SE,24>
{ <SE,25>
int <KW,1>
a <IDN,a>
= <OP,14>
10 <INT,10>
; <SE,27>
float <KW,6>
b <IDN,b>
= <OP,14>
3.14 <FLOAT,3.14>
; <SE,27>
float <KW,6>
c <IDN,c>
= <OP,14>
.5 <FLOAT,.5>
; <SE,27>
const <KW,4>
int <KW,1>
MAX <IDN,MAX>
= <OP,14>
100 <INT,100>
; <SE,27>
if <KW,7>
( <SE,23>
a <IDN,a>
> <OP,15>
5 <INT,5>
) <SE,24>
{ <SE,25>
return <KW,3>
1 <INT,1>
; <SE,27>
} <SE,26>
else <KW,8>
{ <SE,25>
return <KW,3>
0 <INT,0>
; <SE,27>
} <SE,26>
float <KW,6>
result <IDN,result>
= <OP,14>
a <IDN,a>
* <OP,11>
b <IDN,b>
+ <OP,9>
c <IDN,c>
; <SE,27>
int <KW,1>
x <IDN,x>
= <OP,14>
10 <INT,10>
, <SE,28>
y <IDN,y>
= <OP,14>
20 <INT,20>
; <SE,27>
if <KW,7>
( <SE,23>
x <IDN,x>
== <OP,17>
y <IDN,y>
&& <OP,21>
a <IDN,a>
!= <OP,20>
b <IDN,b>
) <SE,24>
{ <SE,25>
x <IDN,x>
= <OP,14>
x <IDN,x>
+ <OP,9>
y <IDN,y>
; <SE,27>
} <SE,26>
if <KW,7>
( <SE,23>
x <IDN,x>
<= <OP,18>
y <IDN,y>
|| <OP,22>
a <IDN,a>
>= <OP,19>
b <IDN,b>
) <SE,24>
{ <SE,25>
y <IDN,y>
= <OP,14>
y <IDN,y>
% <OP,13>
3 <INT,3>
; <SE,27>
} <SE,26>
void <KW,2>
testFunction <IDN,testFunction>
( <SE,23>
) <SE,24>
{ <SE,25>
int <KW,1>
local_var <IDN,local_var>
= <OP,14>
42 <INT,42>
; <SE,27>
} <SE,26>
} <SE,26>

test2:错误程序

原程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main() {
int abc = 10;

int a @ b = 5;
float c # d = 3.14;

float e = 3.14.15;
float f = .;

int x = 10 <> 20;
int y = 10 & 20;

currency $price = 100;

int missing_semicolon = 42

char* str = "hello";
}

测试结果

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
66
67
68
69
70
PS C:\Users\X.J\Desktop\lab2\code\work> ./lexer test2.cmm
int <KW,1>
main <KW,5>
( <SE,23>
) <SE,24>
{ <SE,25>
int <KW,1>
abc <IDN,abc>
= <OP,14>
10 <INT,10>
; <SE,27>
int <KW,1>
a <IDN,a>
@ <ERROR,4,11>
b <IDN,b>
= <OP,14>
5 <INT,5>
; <SE,27>
float <KW,6>
c <IDN,c>
# <ERROR,5,13>
d <IDN,d>
= <OP,14>
3.14 <FLOAT,3.14>
; <SE,27>
float <KW,6>
e <IDN,e>
= <OP,14>
3.14 <FLOAT,3.14>
.15 <FLOAT,.15>
; <SE,27>
float <KW,6>
f <IDN,f>
= <OP,14>
. <ERROR,8,15>
; <SE,27>
int <KW,1>
x <IDN,x>
= <OP,14>
10 <INT,10>
< <OP,16>
> <OP,15>
20 <INT,20>
; <SE,27>
int <KW,1>
y <IDN,y>
= <OP,14>
10 <INT,10>
& <ERROR,11,16>
20 <INT,20>
; <SE,27>
currency <IDN,currency>
$ <ERROR,13,14>
price <IDN,price>
= <OP,14>
100 <INT,100>
; <SE,27>
int <KW,1>
missing_semicolon <IDN,missing_semicolon>
= <OP,14>
42 <INT,42>
char <IDN,char>
* <OP,11>
str <IDN,str>
= <OP,14>
" <ERROR,17,17>
hello <IDN,hello>
" <ERROR,17,23>
; <SE,27>
} <SE,26>

运行code/test/目录下的测试用例,检查通过情况

test1:

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
PS C:\Users\X.J\Desktop\lab2\code\work> ./lexer ../test/test1.sy
int <KW,1>
main <KW,5>
( <SE,23>
) <SE,24>
{ <SE,25>
int <KW,1>
a <IDN,a>
= <OP,14>
1 <INT,1>
, <SE,28>
b <IDN,b>
= <OP,14>
2 <INT,2>
; <SE,27>
a <IDN,a>
= <OP,14>
a <IDN,a>
+ <OP,9>
b <IDN,b>
- <OP,10>
1 <INT,1>
; <SE,27>
if <KW,7>
( <SE,23>
a <IDN,a>
== <OP,17>
2 <INT,2>
) <SE,24>
{ <SE,25>
return <KW,3>
0 <INT,0>
; <SE,27>
} <SE,26>
else <KW,8>
{ <SE,25>
b <IDN,b>
= <OP,14>
b <IDN,b>
* <OP,11>
2 <INT,2>
/ <OP,12>
1 <INT,1>
% <OP,13>
2 <INT,2>
; <SE,27>
return <KW,3>
1 <INT,1>
; <SE,27>
} <SE,26>
} <SE,26>

test2:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
PS C:\Users\X.J\Desktop\lab2\code\work> ./lexer ../test/test2.sy
void <KW,2>
main <KW,5>
( <SE,23>
) <SE,24>
{ <SE,25>
const <KW,4>
int <KW,1>
N <IDN,N>
= <OP,14>
10 <INT,10>
, <SE,28>
M <IDN,M>
= <OP,14>
2 <INT,2>
; <SE,27>
float <KW,6>
rate <IDN,rate>
= <OP,14>
0.5 <FLOAT,0.5>
, <SE,28>
base <IDN,base>
= <OP,14>
3.14 <FLOAT,3.14>
; <SE,27>
int <KW,1>
x <IDN,x>
= <OP,14>
0 <INT,0>
, <SE,28>
y <IDN,y>
= <OP,14>
1 <INT,1>
; <SE,27>
x <IDN,x>
= <OP,14>
x <IDN,x>
+ <OP,9>
N <IDN,N>
- <OP,10>
M <IDN,M>
* <OP,11>
2 <INT,2>
/ <OP,12>
1 <INT,1>
% <OP,13>
2 <INT,2>
; <SE,27>
if <KW,7>
( <SE,23>
( <SE,23>
x <IDN,x>
>= <OP,19>
y <IDN,y>
&& <OP,21>
rate <IDN,rate>
!= <OP,20>
0.0 <FLOAT,0.0>
) <SE,24>
|| <OP,22>
( <SE,23>
N <IDN,N>
< <OP,16>
M <IDN,M>
) <SE,24>
) <SE,24>
{ <SE,25>
y <IDN,y>
= <OP,14>
( <SE,23>
y <IDN,y>
+ <OP,9>
1 <INT,1>
) <SE,24>
; <SE,27>
} <SE,26>
else <KW,8>
{ <SE,25>
y <IDN,y>
= <OP,14>
( <SE,23>
y <IDN,y>
- <OP,10>
1 <INT,1>
) <SE,24>
; <SE,27>
} <SE,26>
x <IDN,x>
= <OP,14>
x <IDN,x>
> <OP,15>
y <IDN,y>
; <SE,27>
x <IDN,x>
= <OP,14>
x <IDN,x>
< <OP,16>
y <IDN,y>
; <SE,27>
x <IDN,x>
= <OP,14>
( <SE,23>
x <IDN,x>
== <OP,17>
y <IDN,y>
) <SE,24>
; <SE,27>
x <IDN,x>
= <OP,14>
( <SE,23>
x <IDN,x>
<= <OP,18>
y <IDN,y>
) <SE,24>
; <SE,27>
x <IDN,x>
= <OP,14>
( <SE,23>
x <IDN,x>
>= <OP,19>
y <IDN,y>
) <SE,24>
; <SE,27>
^ <ERROR,20,3>
& <ERROR,21,3>
: <ERROR,22,3>
? <ERROR,22,5>
return <KW,3>
; <SE,27>
} <SE,26>

test3:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
PS C:\Users\X.J\Desktop\lab2\code\work> ./lexer ../test/test3.sy
VoID <KW,2>
MaIn <KW,5>
( <SE,23>
) <SE,24>
{ <SE,25>
@ <ERROR,1,28>
CoNsT <KW,4>
InT <KW,1>
_X1 <IDN,_X1>
= <OP,14>
42 <INT,42>
, <SE,28>
y <IDN,y>
= <OP,14>
0 <INT,0>
; <SE,27>
# <ERROR,2,35>
FLOAT <KW,6>
Pi <IDN,Pi>
= <OP,14>
3.14 <FLOAT,3.14>
, <SE,28>
rate <IDN,rate>
= <OP,14>
0.5 <FLOAT,0.5>
; <SE,27>
$ <ERROR,3,35>
int <KW,1>
A <IDN,A>
= <OP,14>
1 <INT,1>
, <SE,28>
B <IDN,B>
= <OP,14>
2 <INT,2>
; <SE,27>
^ <ERROR,4,35>
y <IDN,y>
= <OP,14>
y <IDN,y>
+ <OP,9>
_X1 <IDN,_X1>
* <OP,11>
2 <INT,2>
- <OP,10>
5 <INT,5>
/ <OP,12>
3 <INT,3>
% <OP,13>
2 <INT,2>
; <SE,27>
| <ERROR,6,35>
A <IDN,A>
= <OP,14>
A <IDN,A>
+ <OP,9>
B <IDN,B>
; <SE,27>
& <ERROR,7,35>
if <KW,7>
( <SE,23>
( <SE,23>
y <IDN,y>
>= <OP,19>
10 <INT,10>
&& <OP,21>
y <IDN,y>
!= <OP,20>
0 <INT,0>
) <SE,24>
|| <OP,22>
( <SE,23>
A <IDN,A>
< <OP,16>
B <IDN,B>
) <SE,24>
) <SE,24>
{ <SE,25>
y <IDN,y>
= <OP,14>
y <IDN,y>
+ <OP,9>
1 <INT,1>
; <SE,27>
} <SE,26>
else <KW,8>
{ <SE,25>
y <IDN,y>
= <OP,14>
y <IDN,y>
- <OP,10>
1 <INT,1>
; <SE,27>
} <SE,26>
if <KW,7>
( <SE,23>
Pi <IDN,Pi>
> <OP,15>
1.0 <FLOAT,1.0>
&& <OP,21>
rate <IDN,rate>
<= <OP,18>
1.0 <FLOAT,1.0>
) <SE,24>
{ <SE,25>
A <IDN,A>
= <OP,14>
( <SE,23>
A <IDN,A>
+ <OP,9>
B <IDN,B>
) <SE,24>
* <OP,11>
( <SE,23>
_X1 <IDN,_X1>
- <OP,10>
3 <INT,3>
) <SE,24>
; <SE,27>
} <SE,26>
else <KW,8>
{ <SE,25>
if <KW,7>
( <SE,23>
A <IDN,A>
== <OP,17>
B <IDN,B>
) <SE,24>
{ <SE,25>
B <IDN,B>
= <OP,14>
B <IDN,B>
+ <OP,9>
1 <INT,1>
; <SE,27>
} <SE,26>
else <KW,8>
{ <SE,25>
B <IDN,B>
= <OP,14>
B <IDN,B>
- <OP,10>
1 <INT,1>
; <SE,27>
} <SE,26>
} <SE,26>
return <KW,3>
; <SE,27>
} <SE,26>