编译方法实验报告
姓
班
名
级
学
号
指 导 教 师
实 验 名 称
扫描器的设计、中间代码生成器设计
开 设 学 期
实 验 时 间
评 定 成 绩
2 0 1 3 - 2 0 1 4 第 一 学 期
第
9
周
评 定 人 签 字
评 定 日 期
东北大学软件学院
2013 年 10 月
实验 1 扫描器的设计
一、 实验目的
熟悉并实现一个扫描器(词法分析程序)。
二、 实验内容
(1) 设计扫描器的有限自动机(识别器);
(2) 设计翻译、生成 Token 的算法(翻译器);
(3) 编写代码并上机调试运行通过。
输入——源程序文件或源程序字符串;
·输出——相应的 Token 序列;
·
关键字表和界符表;
符号表和常数表;
三、 实验原理及基本步骤
实验流程图解:
单词
扫描器
Token
(1) 有限自动机的状态转换图
d
e
d
+① d ② . ③ d ④ e ⑤
l
b
l/d
⑧
-1
12
-1/+/-
⑨ b ⑩
-1
13
-1
14
+|-
d
⑥ d ⑦
d
-1/+/-
-1/+/-
11
-
-
-
-
-1
15
-
其中:d 为数字,l 为字母,b 为界符,-1 代表其它符号(如在状态 8 处遇到
了非字母或数字的其它符号,会变换到状态 12)。
状态转换矩阵:
d
·
E|e
3
5
5
+|-
11
11
6
11
l
8
8
b
9
10
-1
15
11
11
11
12
14
13
2
2
4
4
7
7
7
8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(2) 关键字表和界符表
Program
Begin
End
Var
While
Do
Repeat
Until
For
To
If
Then
Else
;
:
(
)
,
:=
+
-
*
/
>
>=
==
<
<=
(3) 数据设计:
①状态转换矩阵:int aut[10][7]={ 2, 0, 0, 0, 8, 9, 15,
2, 3, 5,11, 0, 0, 11,
4, 0, 0, 0, 0, 0, 0,
4, 0, 5,11, 0, 0, 11,
7, 0, 0, 6, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0,
7, 0, 0,11, 0, 0, 11,
8, 0, 0, 0, 8, 0, 12,
0, 0, 0, 0, 0, 10, 14,
0, 0, 0, 0, 0, 0, 13};
②关键字表:
char keywords[30][12]={“program”,”begin”,”end”,”var”,”while”,”do”,
”repeat”,”until”,”for”,”to”,”if”,”then”,”else”,
“;”, ”:”, ”(“, ”)”, ”,”, ”:=”, ”+”, ”-“, ”*”, ”/”,
”>”, ”>=”, ”==”, “<”, “<=”};
//表中存有源程序中的标
③符号表:char ID[50][12];
识符
④常数表:float C[20];
⑤其它变量: struct token
{ int code;
int value};
struct token tok[100];
int s;
int n,p,m,e,t;
//Token 结构
//Token 数组
//当前状态
//尾数值,指数值,小数位数,指数
符号,类型
float num;
char w[50];
int i;
char strTOKEN[12];
//常数值
//源程序缓冲区
//源程序指针,当前字符为 w[i]
//当前已经识别出的单词
(4) 语义动作:
·q1:n=m=p=t=0; e=1; num=0; 其它变量初始化;
·q2:n=10*n+(w[i]);
·q3:t=1;
·q4:n=10*n+( w[i]); m++;
·q5:t=1;
·q6:if ‘-‘ then e=-1;
·q7:p=10*p+(w[i]);
·q8:将 w[i]中的符号拼接到 strTOKEN 的尾部;
·q9:将 w[i]中的符号拼接到 strTOKEN 的尾部;
·q10:将 w[i]中的符号拼接到 strTOKEN 的尾部;
//标识符的编码为 1,值为其在常数表中的位置;常数的编码为 2,
值为其在符号表中的位置;关键字和界符的编码为其在关键字表中的位
置,值为 0。
q11:num=n*10e*p-m;
t[i].code=2;
//计算常数值
t[i].value=InsertConst(num);
// 生 成 常 数
Token
·q12:code=Reserve(strTOKEN);
//查关键字表
if
code
then
{
t[i].code=code;
t[i].value=0;
} //生成关键字 Token
else
{
t[i].code=1;
t[i].value=InsertID(strTOKEN);
//生成标识符 Token
·q13:code=Reserve(strTOKEN);
}
//查界符表
code
t[i].code=code;
t[i].value=0;
//生成界符 Token
if
then
{
}
else
{
}
strTOKEN[strlen(strTOKEN)-1]=’\0’;
源程序缓冲区指针减 1;
code=Reserve(strTOKEN);
t[i].code=code;
t[i].value=0;
//查界符表
//生成界符 Token
//单界符
·q14:code=Reserve(strTOKEN);
t[i].code=code;
t[i].value=0;
//查界符表
//生成界符 Token
·q15:stop.
(5) 查状态变换表
int find(int s,char ch)
//s 是当前状态,ch 是当前字符,返回值是转换
后状态
{
}
查状态转换矩阵 aut[10][7];
返回新状态;
四、 数据结构设计
主程序流程:
初始化;
打开用户源程序文件;
while (文件未结束)
{
读入一行到 w[i],i=0;
do
{
//处理一行,每次处理一个单词
滤空格,直到第一个非空的 w[i];
i--;
s=1;
while (s!=0)
{
act(s);
if (s>=11 && s<=14)
break;
i++;
s=find(s, w[i]);
}
if (s==0)
//处理一个单词开始
//拼单词并生成相应 Token
//执行 qs
//一个单词处理结束
//getchar()
词法错误;
}while (w[i]!=换行符);
}
关闭用户源程序文件;
生成 Token 文件;
输出关键字表;
输出 Token 序列;
输出符号表;
输出常数表;
五、 关键代码分析(带注释)及运行结果
//滤空格
//判定单词类别
1. 判定单词类型:
while (w[i]==' ')
i++;
if (w[i]>='a' && w[i]<='z')
{
ptr=col2;
num_map=2;
}
else if (w[i]>='0' && w[i]<='9')
{
ptr=col1;
num_map=4;
}
else if (strchr(col3[0].str,w[i])==NULL)
{
printf("非法字符%c\n",w[i]);
i++;
continue;
}
else
{
ptr=col3;
num_map=1;
}
2. 处理单词:
s=1;
while (s!=0)
{
act(s);
if (s>=11 && s<=14)
break;
i++;
s=find(s,w[i]);
}
if (s==0)
{
//开始处理一个单词
//getchar()
strTOKEN[i_str]='\0';
printf("词法错误:%s\n",strTOKEN);
}
3. 输出关键字表的结果:
printf("关键字表:");
for (i=0;i<30;i++)
printf("%s ",keywords[i]);
printf("\n");
printf("Token 序列:");
for (i=0;i
if (strchr((p+i)->str,ch))
{
col=(p+i)->col;
break;
}
//printf("aut[s][col]: %d\n",aut[s][col]);
return aut[s][col];
}
5. 判断有多少个常数
int InsertConst(double num)//判断有多少个常数用的
{
int i;
for (i=0;i