用Lex设计词法分析器

实验内容

Exp2

使用lex为下述文法语言写一个词法分析器。

<程序> -> PROGRAM <标识符> ; <分程序>
<分程序> -> <变量说明> BEGIN <语句表> END.
<变量说明> -> VAR <变量说明表>;
<变量说明表> -><变量表>: <类型> | <变量表>: <类型>; <变量说明表>
<类型>- > INTEGER | REAL
<变量表> -> <变量> | <变量>, <变量表>
<语句表> -> <语句> | <语句>; <语句表>
<语句> -> <赋值语句> | <条件语句> | | <复合语句>
<赋值语句> -> <变量> := <算术表达式>
<条件语句> -> IF <关系表达式> THEN <语句> ELSE <语句>

-> WHILE <关系表达式> DO <语句>
<复合语句> -> BEGIN <语句表> END
<算术表达式> -> <项> | <算术表达式> + <项> | <算术表达式> - <项>
<项> -> <因式> | <项> * <因式> | <项> / <因式>
<因式> -> <变量> | <常数> | (<算术表达式>)
<关系表达式> -> <算术表达式> <关系符> <算术表达式>
<变量> -> <标识符>
<标识符> -> <标识符><字母> | <标识符><数字> | <字母>
<常数> -> <整数> | <浮点数>
<整数> -> <数字> | <数字> <整数>
<浮点数> -> .<整数> | <整数>.<整数>
<关系符> -> < | <= | = | > | >=| <>
<字母> -> A | B | …| X | Y | Z | a | b | …| x | y | z
<数字> -> 0|1|2|…|9

Exp3

  1. 要求每次调用词法分析函数yylex时,只返回一个记号(token);

  2. 为记号选择适当的属性值,并且每次词法分析函数返回记号前,都将记号的属性值存入全局变量yylval中。(yylval可以自己定义为全局变量);

  3. 记号属性值的选择:标识符的属性为标识符的名字字符串(例如,标识符name1的属性为字符串”name1”),整数的属性为整数值,浮点数的属性为浮点数值。其他记号属性值可自己选择。关键字可以省略属性。

  4. 注意:由于属性值需要存入yylval中,并且记号属性值的类型比较多(可能为字符串、整数、浮点数等),因此yylval必须能同时存放各种类型的值(提示:将yylval设置为union类型)。

  5. 在cygwin下用flex和gcc工具将实验调试通过,并能通过例子parser0中testcases目录下的test1.p测试例的测试。

实验过程

添加记号名

1
2
3
4
5
6
7
8
9
10
#define PROGRAM 36
#define BEGIN1 37
#define VAR 38
#define THEN 39
#define INT 40
#define FLOAT 41
#define END 42
#define REAL 43
#define COMMA 44
#define COLON 45

正规定义

1
2
3
4
5
int 		{digit}+(E[+-]?{digit}+)?
float {digit}*(\.{digit}+)(E[+-]?{digit}+)?
allow ((\\\\)|(\\\")|(\\\')|(\\{letter})|(\\{digit}))*
str ({allow}|[^\"\'\n\\])*\"
end END|END.

定义yylval

1
2
3
4
5
union data{
int inum;
float fnum;
char c[20];
}yylval;

完整代码

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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* 把讨厌的注释去掉 */

%{

#include <stdio.h>
#define LT 1
#define LE 2
#define GT 3
#define GE 4
#define EQ 5
#define NE 6

#define WHILE 18
#define DO 19
#define ID 20
//#define NUMBER 21
#define RELOP 22

#define NEWLINE 23
#define ERRORCHAR 24

#define LEFTBRACKET 25
#define RIGHTBRACKET 26
#define SEMICOLON 27
#define ASSIGN 28
#define ARITHMETIC 29
#define NOTE 30
#define IF 31
#define ELSE 32
#define LEFTBRACES 33
#define RIGHTBRACES 34
#define STRING 35
#define STRERROR 46

#define PROGRAM 36
#define BEGIN1 37
#define VAR 38
#define THEN 39
#define INT 40
#define FLOAT 41
#define END 42
#define REAL 43
#define COMMA 44
#define COLON 45

union data{
int inum;
float fnum;
char c[20];
}yylval;

%}

underline [_]
delim [ \t \n]
ws {delim}+
letter [A-Za-z]
digit [0-9]
id {underline}*{letter}({letter}|{digit})*
int {digit}+(E[+-]?{digit}+)?
float {digit}*(\.{digit}+)(E[+-]?{digit}+)?
allow ((\\\\)|(\\\")|(\\\')|(\\{letter})|(\\{digit}))*
str ({allow}|[^\"\'\n\\])*\"
end END|END.

/* 状态(或条件)定义可以定义在这里
* INITIAL是一个默认的状态,不需要定义
*/
%s COMMENT
%s NOTE1
%s QUOTE

%%

<INITIAL>"/*" {BEGIN COMMENT;}
<COMMENT>"*/" {BEGIN INITIAL;}
<COMMENT>.|\n {;}

<INITIAL>"//" {BEGIN NOTE1;}
<NOTE1>\n {BEGIN INITIAL;}
<NOTE1>. {;}

<INITIAL>"\"" {BEGIN QUOTE;}
<QUOTE>"\"" {BEGIN INITIAL;}
<QUOTE>"\n" {BEGIN INITIAL;}
<QUOTE>{str} {BEGIN INITIAL; return (STRING);}
<QUOTE>[^\"\n] {;}

<INITIAL>{ws} {;}

<INITIAL>PROGRAM {return (PROGRAM);}

<INITIAL>BEGIN {return (BEGIN1);}
<INITIAL>{end} {return (END);}
<INITIAL>WHILE {return (WHILE);}
<INITIAL>DO {return (DO);}
<INITIAL>IF {return (IF);}
<INITIAL>THEN {return (THEN);}
<INITIAL>ELSE {return (ELSE);}

<INITIAL>while {return (WHILE);}
<INITIAL>do {return (DO);}
<INITIAL>if {return (IF);}
<INITIAL>else {return (ELSE);}
<INITIAL>then {return (THEN);}

<INITIAL>VAR {return (VAR);}
<INITIAL>{int} {sscanf(yytext,"%d",&yylval.inum); return (INT);}
<INITIAL>{float} {sscanf(yytext,"%f",&yylval.fnum); return (FLOAT);}
<INITIAL>REAL {return (REAL);}


<INITIAL>{id} {sscanf(yytext,"%s",&yylval.c); return (ID);}
<INITIAL>"<" {return (RELOP);}
<INITIAL>"<=" {return (RELOP);}
<INITIAL>"==" {return (RELOP);}
<INITIAL>"!=" {return (RELOP);}
<INITIAL>">" {return (RELOP);}
<INITIAL>">=" {return (RELOP);}

<INITIAL>"(" {return (LEFTBRACKET);}
<INITIAL>")" {return (RIGHTBRACKET);}
<INITIAL>"{" {return (LEFTBRACES);}
<INITIAL>"}" {return (RIGHTBRACES);}
<INITIAL>";" {return (SEMICOLON);}
<INITIAL>"=" {return (ASSIGN);}
<INITIAL>":=" {return (ASSIGN);}
<INITIAL>":" {return (COLON);}
<INITIAL>"+" {return (ARITHMETIC);}
<INITIAL>"-" {return (ARITHMETIC);}
<INITIAL>"*" {return (ARITHMETIC);}
<INITIAL>"/" {return (ARITHMETIC);}
<INITIAL>"," {return (COMMA);}
<INITIAL>. {return ERRORCHAR;}

%%

int yywrap (){
return 1;
}

void writeout(int c){
switch(c){
case ERRORCHAR: fprintf(yyout, "(ERRORCHAR, \"%s\") ", yytext);break;

case PROGRAM: fprintf(yyout, "(PROGRAM, \"%s\") ", yytext);break;
case BEGIN1: fprintf(yyout, "(BEGIN, \"%s\") ", yytext);break;
case END: fprintf(yyout, "(END, \"%s\") ", yytext);break;
case IF: fprintf(yyout, "(IF, \"%s\") ", yytext);break;
case THEN: fprintf(yyout, "(THEN, \"%s\") ", yytext);break;
case ELSE: fprintf(yyout, "(ELSE, \"%s\") ", yytext);break;
case VAR: fprintf(yyout, "(VAR, \"%s\") ", yytext);break;
case INT: fprintf(yyout, "(INT, \"%s\") ", yytext);break;
case FLOAT: fprintf(yyout, "(FLOAT, \"%s\") ", yytext);break;
case REAL: fprintf(yyout, "(REAL, \"%s\") ", yytext);break;
case COMMA: fprintf(yyout, "(COMMA, \"%s\") ", yytext);break;
case COLON: fprintf(yyout, "(COLON, \"%s\") ", yytext);break;


case RELOP: fprintf(yyout, "(RELOP, \"%s\") ", yytext);break;
case WHILE: fprintf(yyout, "(WHILE, \"%s\") ", yytext);break;
case DO: fprintf(yyout, "(DO, \"%s\") ", yytext);break;
case ID: fprintf(yyout, "(ID, \"%s\") ", yytext);break;
case LEFTBRACKET: fprintf(yyout, "(LEFTBRACKET, \"%s\") ", yytext);break;
case RIGHTBRACKET: fprintf(yyout, "(RIGHTBRACKET, \"%s\") ", yytext);break;
case LEFTBRACES: fprintf(yyout, "(LEFTBRACES, \"%s\") ", yytext);break;
case RIGHTBRACES: fprintf(yyout, "(RIGHTBRACES, \"%s\") ", yytext);break;
case SEMICOLON: fprintf(yyout, "(SEMICOLON, \"%s\") ", yytext);break;
case ASSIGN: fprintf(yyout, "(ASSIGN, \"%s\") ", yytext);break;
case ARITHMETIC: fprintf(yyout, "(ARITHMETIC, \"%s\") ", yytext);break;
case NOTE: fprintf(yyout, "(NOTE, \"%s\") ", yytext);break;
case NEWLINE: fprintf(yyout, "\n");break;
case STRING: fprintf(yyout, "(STRING, \"%s\") ", yytext);break;
default:break;
}
return;
}


int main (int argc, char ** argv){
int c,j=0;
if (argc>=2){
if ((yyin = fopen(argv[1], "r")) == NULL){
printf("Can't open file %s\n", argv[1]);
return 1;
}
if (argc>=3){
yyout=fopen(argv[2], "w");
}
}

while (c = yylex()){

writeout(c);
j++;
if (j%5 == 0) writeout(NEWLINE);
}
if(argc>=2){
fclose(yyin);
if (argc>=3) fclose(yyout);
}
return 0;
}

实验结果

测试样例

运行结果