词法分析器是编译器的前端,负责将源代码字符串分解为具有独立语义的最小单位,即计算机识别的单词。 本文通过实验报告阐明了该词法分析器的需求,并介绍了如何用代码方式实现简单的词法分析器。 另一种方法是在flex中自动生成词法分析器。
一、实验目的:
通过设计编制调试具体的词法分析程序,加深对词法分析原理的理解。 掌握在扫描编程语言源程序的过程中分解成各种单词的词法分析方法。
二.实验内容
编制单词获取程序,从文本输入的源程序中识别出各自具有独立意义的单词,即关键词、标识符、整数、小数、字符串、分隔符、运算符7种。 依次输出每个单词的内部代码和单词符号本身的文本串。 发生错误时,显示“Error”,可以跳过错误部分继续显示。
注意:单词类型的大小写不敏感。 这意味着不区分大小写。
三.实验要求
1、字句规则
关键字: program、const、var、integer、decimal、string、procedure、begin、end、if、then、else、while、do、call、call
标识符:由字母或以“_”开头的字符、数字串或“_”组成的任意长度的符号串。
整数:数字串。
小数:数字串数字串
字符串:由“”对包围的任意长度的符号字符串。 注:可以是多行。
分隔符(,),),),);空间
运算符:=,=,=,-,*,/
2、设计词法分析函数getToken (),完成以下功能:
getToken ) )在每次调用时分析单词;
返回单词类别、单词自身的文本串、源文件中单词的矩阵编号;
3、编写测试程序,反复调用函数getToken (),输出单词信息。
我的实现思路是:
首先将所有源代码读入缓冲区。 这个步骤有三种方法。 一个字一个字地读,一行一行地读,全部读取。 其中,1的代码可读性有点差,所以这里不采用。 逐行阅读是最难实现的,因为它处理换行符字符串问题。 全部读入后,只用单词判断,没有换行的区别,识别一个单词后,重新开始识别下一个单词。
getToken )从缓冲区的第一个字符开始,按照上图的状态转移图向后读,直到单词被分析。
输出此单词的判定类别,返回,继续读取一个字符。
源代码如下所示。
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
#include
#include
#include
usingnamespacestd;
#define WORD_LENGTH 130 // 单词最大长度
#define WORD_OF_PROGRAM 1000 //最大单词数
charprog[WORD_OF_PROGRAM*WORD_LENGTH],token[WORD_LENGTH];
charch;
intsyn,p,m=0,n,row;
doublesum=0;//类型为整数或者小数的时候,用于保存源数据
intsyn_of_rwtab;//遍历关键字数组
intlocate_line;//单词在行中的位置
char*rwtab[18]={"program","const","var","integer",
"decimal","string","procedure","begin",
"end ","if","then","else","while","do",
"call","read","write","not"};//保存关键字
voidgetToken(){
/*
这里一共有7类,每一类都有一个if判断
*/
for(n=0;n
ch=prog[p++];
//****************第6类 分隔符***********************************//
if(ch==' '||ch==';'||
ch=='{'||ch=='}'||
ch=='('||ch==')'){
syn=6;
token[0]=ch;
}
//***************2 标识符 1 关键字*********************************//
elseif((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||ch=='_')//可能是标示符或者变量名
{
m=0;
while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||ch=='_')
{
token[m++]=ch;
ch=prog[p++];
}
token[m++]=' ';
p--;
syn=2;
for(n=0;n<18;n++)//将识别出来的字符和已定义的标示符作比较,
if(strcmp(token,rwtab[n])==0)
{
syn=1;
syn_of_rwtab=n;
break;
}
}
//*****************3 整数 4小数********************************//
elseif((ch>='0'&&ch<='9'))
{
boolflag=false;//是否是小数
sum=0;
while((ch>='0'&&ch<='9'))
{
sum=sum*10+ch-'0';
ch=prog[p++];
}
if(ch=='.'){
flag=true;
ch=prog[p++];
doubletag=0.1;//记录小数的位数
while((ch>='0'&&ch<='9')){
sum+=(ch-'0')*tag;
tag=tag*0.1;
ch=prog[p++];
}
}//if
p--;
if(flag)syn=4;
elsesyn=3;
if(sum>32767)
syn=-1;
}
//********************5 判断是不是字符串****************************//
elseif(ch=='"'){
syn=5;
m=0;
token[m++]=ch;
while((ch=prog[p++])!='"')
token[m++]=ch;
token[m]=ch;
// p--;
}
//*******************7 运算符*******************//
elseswitch(ch)
{
case'
m=0;
token[m++]=ch;
ch=prog[p++];
if(ch=='>')
{
syn=7;
token[m++]=ch;
}
elseif(ch=='=')
{
syn=7;
token[m++]=ch;
}
else
{
syn=7;
p--;
}
break;
case'>':
m=0;
token[m++]=ch;
ch=prog[p++];
if(ch=='=')
{
syn=7;
token[m++]=ch;
}
else
{
syn=7;
p--;
}
break;
case':':
m=0;token[m++]=ch;
ch=prog[p++];
if(ch=='=')
{
syn=7;
token[m++]=ch;
}
else
{
syn=7;
p--;
}
break;
case'*':syn=7;token[0]=ch;break;
case'/':syn=7;token[0]=ch;break;
case'+':syn=7;token[0]=ch;break;
case'-':syn=7;token[0]=ch;break;
case'=':syn=7;token[0]=ch;break;
case'#':syn=0;token[0]=ch;break;
case'n':syn=-2;locate_line=0;break;
default:syn=-1;break;
}
}
intmain()
{
p=0;
row=1;
cout<
do
{
cin.get(ch);
prog[p++]=ch;
}
while(ch!='#');
inttemp=0;//不区分大小写,全部转换成小写。
while(prog[temp]!='#'){
if(prog[temp]<='Z'&&prog[temp]>='A')
prog[temp]+='a'-'A';
temp++;
}
p=0;
do
{
getToken();
locate_line++;
switch(syn)
{
case1:cout<
case2:cout<
case5:cout<
case6:cout<
case3:cout<
case4:cout<
case-1:cout<
case-2:row=row++;break;
}
}
while(syn!=0);
}
参考:一条鱼、zydls(更详细的词法分析器讲解)