编译原理实验指导书.docx
《编译原理实验指导书.docx》由会员分享,可在线阅读,更多相关《编译原理实验指导书.docx(17页珍藏版)》请在冰豆网上搜索。
![编译原理实验指导书.docx](https://file1.bdocx.com/fileroot1/2023-2/1/80669132-da64-4129-a8f5-4a2906e67d82/80669132-da64-4129-a8f5-4a2906e67d821.gif)
编译原理实验指导书
编译原理
实验指导书
《编译原理》课程组
2010年12月
概述
一、本课程实践的目的和任务
编译原理是一门实践性很强的课程,只有通过实践,才能真正掌握。
实际的编译程序是十分复杂的,有时由多达十几万条指令组成。
为此,编译原理的实践教学,采用简化编译过程的办法,选择最关键的3个环节──词法分析、语法分析(包括语义处理、产生无优化的目标指令)、连接调试,进行编程和调试训练。
每个环节作为一个实践课题。
先分别编程调试,再连接在一起总调。
二、实践方法
任何一个实用的高级语言,其语法都比较复杂,如选其作为源语言,很难实践全过程。
故本实践将定义一个简化的语言──C/C++语言的一个子集作为源语言,分3个课题,设计调试出它的编译程序。
前后贯穿这一条主线进行实践。
每次都可利用课余时间编程,利用上机时间进行输入和调试。
建议使用C/C++语言或C语言。
三、实践报告的规范和要求
每个课题完成后写出实践报告。
实践报告包括程序设计时考虑的算法和方法;调试过程中出现的问题和解决的措施;打印出程序清单和调试时所用的源程序。
四、简化的C/C++语言子集的定义
⒈C/C++语言子集的语法定义
〈C/C++子集程序〉→〈变量说明〉〈分程序〉。
〈变量说明〉→〈空〉|VAR〈变量表〉:
INTEGER;
〈变量表〉→〈变量〉|〈变量〉,〈变量表〉
〈变量〉→〈标识符〉
〈分程序〉→BEGIN〈语句组〉END
〈语句组〉→〈语句〉|〈语句〉;〈语句组〉
〈语句〉→〈赋值语句〉|〈条件语句〉|〈WHILE语句〉|〈分程序〉
〈赋值语句〉→〈变量〉:
=〈算术表达式〉
〈条件语句〉→IF〈布尔表达式〉THEN〈语句〉ELSE〈语句〉
〈WHILE语句〉→WHILE〈布尔表达式〉DO〈语句〉
〈算术表达式〉→〈项〉|〈算术表达式〉+〈项〉|〈算术表达式〉-〈项〉
〈项〉→〈初等量〉|〈项〉*〈初等量〉|〈项〉/〈初等量〉
〈初等量〉→〈无符号数〉|〈变量〉|(〈算术表达式〉)
〈关系表达式〉→〈算术表达式〉〈关系运算符〉〈算术表达式〉
〈标识符〉→〈字母〉|〈标识符〉〈字母〉|〈标识符〉〈数字〉
〈无符号数〉→〈数字〉|〈无符号数〉〈数字〉
〈关系运算符〉→〈|〈=|=|〉=|〉|〈〉
〈字母〉→A│B│C│D│E│F│G│H│I│J│K│L│M│N│O│P│Q│R│S│T│
│U│V│W│X│Y│Z
〈数字〉→1│2│3│4│5│6│7│8│9│0
实验1词法分析
介绍词法分析程序的编程与调试。
(2学时)
一、目的与要求
1)目的
通过设计调试词法分析程序,实现从源程序中分出各种单词的方法;加深对课堂教学的理解;提高词法分析方法的实践能力。
2)要求
⑴掌握从源程序文件中读取有效字符的方法和产生源程序的内部表示文件的方法。
⑵掌握词法分析的实现方法。
⑶上机调试编出的词法分析程序。
二、实践题
⒈题目
用C/C++语言编写前述C/C++子集的词法分析程序。
1)主程序设计考虑,(参阅后面给出的程序框架)
主程序的说明部分为各种表格和变量安排空间。
数组k为关键字表,每个数组元素存放一个关键字。
采用定长的方式,较短的关键字后面补空格。
P数组存放分界符。
为了简单起见,分界符、算术运算符和关系运算符都放在p表中(学生编程时,应建立算术运算符表和关系运算符表,并且各有类号),合并成一类。
id和ci数组分别存放标识符和常数。
instring数组为输入源程序的单词缓存。
outtoken记录为输出内部表示缓存。
还有一些为造表填表设置的变量。
主程序开始后,先以人工方式输入关键字,造k表;再输入分界符等造p表。
主程序的工作部分设计成便于调试的循环结构。
每个循环处理一个单词;接收键盘上送来的一个单词;调用词法分析过程;输出每个单词的内部码。
2)词法分析过程考虑
该过程取名为lexical,它根据输入单词的第一个字符(有时还需读第二个字符),判断单词类,产生类号:
以字符k表示关键字;i表示标识符;c表示常数;p表示分界符;s表示运算符(学生编程时类号分别为1,2,3,4,5)。
对于标识符和常数,需分别与标识符表和常数表中已登记的元素相比较,如表中已有该元素,则记录其在表中的位置,如未出现过,将标识符按顺序填入数组id中,将常数变为二进制形式存入数组中ci中,并记录其在表中的位置。
lexical过程中嵌有两个小过程:
一个名为getchar,其功能为从instring中按顺序取出一个字符,并将其指针pint加1;另一个名为error,当出现错误时,调用这个过程,输出错误编号。
将词法分析程序设计成独(入口)立一遍扫描源程序的结构。
其流程图见图1。
图1词法分析程序流程图
⒉要求
⑴所有识别出的单词都用两个字节的等长表示,称为内部码。
第一个字节为t,第二个字节为i。
t为单词的种类。
关键字的t=1;分界符的t=2;算术运算符的t=3;关系运算符的t=4;无符号数的t=5;标识符的t=6。
i为该单词在各自表中的指针或内部码值。
表1-1为关键字表;表1-2为分界符表;表1-3为算术运算符的i值;表1-4为关系运算符的i值。
表1-1关键字表
表1-2分界符表
指针1
关键字
指针1
分界符
0
MAIN
0
,
1
{
1
;
2
}
2
.
3
IF
3
:
=
4
THEN
4
(
5
ELSE
5
)
6
FOR
7
WHILE
表1-3算术运算符
表1-4关系运算符
i值
算术运算符
i值
关系运算符
00H
<
10H
+
01H
<=
11H
-
02H
=
20H
*
03H
>
21H
/
04H
>=
05H
<>
⑵表1-1只供参考,具体实现时要求参考C语言手册中的保留字表选择一个子集。
常数表和标识符表是在编译过程中建立起来的。
其i值是根据它们在源程序中出现的顺序确定的。
常数分析程序、关键字和标识符分析程序、其他单词分析程序请参阅范例自行设计。
下面给出部分程序的算法框架,设计实现时需要补充完善。
三、程序框架
plexical(input,output);
CONST
keylen=10;
identlen=10;
outreco=RECORD
charty;
integerpoint;
END;{outreco}
Integercip,ip,pint,i,j,l,m,errorx;
CHARcharl,instring[10],p[16,2];
integerci[10];
stringk[keylen],id[keylen];
stringtoken;
outrecoouttoken;
OFchar;
PROCEDURElexical
{
integerl,m,num;
booleanb;
PROCEDUREgetchar
{
charl:
=instring[pint];
pint:
=pint+1
}/*getchar*/
PROCEDUREerror
{
putchar('error',errorx)
}/*error*/
{
FOR(1:
=1;inentlen)token[1]:
='';
getchar;
WHILEchar1=''DOgetchar;
IFchar1IN['a'..'z']
THEN
{/*处理标识符*/
m:
=1;
WHILE(char1IN['a'..'z'])OR(char1IN['0'..'9'])DO
{
IFm<=identlen
THEN
{
token[m]:
=char1;
m:
=m+1
};
getchar
}/*while*/
pint:
=pint-1;
1:
=1;
b:
=false;
WHILE(1<=keylen)AND(NOTb)DO
{
b:
=true;
i:
=1;
WHILE(i<=identlen)ANDbDO
IFk[1][i]=token[i]
THENi:
=i+1
ELSEb:
=false;
IFNOTbTHENl:
=l+1
}
IF1<=keylen
THEN
{
outtoken.ty:
='k';
outtoken.point:
=1
}
ELSE
{l:
=1;
b:
=false;
WHILE(l<=ip)AND(NOTb)DO
{
b:
=true;
i:
=1;
WHILE(i<=identlen)ANDbDO
IFid[1][i]=token[i]
THENi:
=i+1
ELSEb:
=false;
IFNOTbTHENl:
=l+1;
};
IFNOTbTHENl:
=l+1;
IF1>ip
THEN
{
ip:
=ip+1;
FORm:
=1TOidentlenDOid[ip][m]:
=token[m];
outtoken.ty:
='i';
outtoken.point:
=1
}
}
}
ELSE
IFchar1IN['0'..'9']
THEN
{处理常数}/*integer*/
ELSE
IFchar1IN[',',';','.',':
','(',')']
{处理分界符}
ELSE
IFcharIN['+','-','*','/','.','<','=','>']
THEN
{处理运算符}
ELSE{errorx:
=2;error}
}/*lexica1*/
{
out('k-table,input!
');
FOR(1:
=1;keyicn)
{
FOR(m:
=1;identlen)read(k[1,m])
Read
}
FOR(l:
=1;identlen)id[1,m]:
=''
out('p-table,input!
')
FOR(1:
=1;11)
{
FOR(m:
=1;2)read(p[1,m])
Read
}
ip:
=0;
cip:
=1;
pint:
=1;
L1:
out('source,input!
');
FOR(j:
=1;identlen)Read(instring[j])
lexical
out(outtoken.ty)
out(outtoken.point)
FOR(l:
=1;identlen)out(token[1])
out;
GotoL1
END.
实验2NFA确定化输入:
NFA;输出DFA(2学时)
要求:
对于自动机的数据结构自行确定,在实验报告中加以说明即可。
实验3语法分析(4-6学时)
一、目的与要求
㈠目的
通过设计、编制、调试一个典型的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。
㈡要求
选择最有代表性的语法分析方法,如算符优先法、递归子程序法和LR分析法;选择对各种常见程序语言都用的语法结构,如赋值语句,特别是表达式,作为分析对象,并且与所选语法分析方法要比较贴切。
二、实习题
⒈题目
分析对象的BNF定义如下:
〈算术表达式〉∷=〈项〉|〈算术表达式〉+〈项〉|〈算术表达式〉-〈项〉
〈项〉∷=〈因式〉|〈项〉*〈因式〉|〈项〉/〈因式〉
〈因式〉∷=〈变量〉│(〈算术表达式〉)
〈变量〉∷=〈字母〉
〈字母〉∷=A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
(a)
(b)(c)
(d)
(e)(f)
图2递归下降法分析表达式之框图
(a)ZC过程;(b)E过程;(c)T过程;
(d)F过程;(e)函数过程SYM;(f)过程ADVANCE
⒉算法用递归下降法分析上述算术表达式的框图,如图2所示。
这里,ZC过程为总控程序,主要完成:
⑴通知外界键入算术表达式;
⑵控制E过程分析算术表达式;
⑶根据分析结果之正误,分别通知外界不同的信息。
ZC过程被设计成可以分析无穷多个算术表达式。
E、T和F三个过程分别对应〈算术表达式〉、〈项〉和〈因式〉三个产生式的处理。
它们用到两个公共过程。
一个是函数过程SYM,它负责从输入字符串ST中取出下一个字符,并存入SYM中等待分析。
另一个过程ADVANCE负责剔除ST中的首字符。
⒋小结
⑴实习前的准备
按实习目的和要求,用C/C++语言编写一个语法分析程序,同时考虑相应的数据结构。
⑵调试
调试例子应包括符合语法规则的算术表达式,以及分析程序能够判别的若干错例。
⑶输出
对于所输入的算术表达式,不论对错,都应有明确的信息告诉外界。
⑷扩充
有余力的同学,可适当扩大分析对象。
譬如:
①算术表达式中变量名可以是一般标识符,还可含一般常数、数组元素、函数调用等等。
②除算术表达式外,还可扩充分析布尔、字符、位等不同类型的各种表达式。
③加强语法检查,尽量多和确切地指出各种错误。
⑸编写上机实习报告。
实验4语义分析
一、目的与要求
㈠目的
通过上机实习,加深对语法制导翻译原理的理解,掌握将语法分析所识别的语法范畴变换为某种中间代码的语义翻译方法。
㈡要求
⑴选用目前世界上普遍采用的语义分析方法──语法制导翻译技术。
⑵语义分析对象重点考虑经过语法分析后已是正确的语法范畴,实习重点是语义子程序。
⑶中间代码选用比较常见的形式,例如四元式。
⑷实习时间为2-4小时。
㈢题目的选择
语法制导翻译是在语法分析的基础上增加语义操作来实现翻译的。
原则上每个产生式对应一个语义子程序。
在语法分析的过程中,当一个产生式获得匹配或进行归约时,相应的语义子程序便开始工作,生成中间代码,查填有关表格,检查并报告源程序中的错误,修改编译程序某些变量的值。
高级语言的语法结构类型很多,从实习的角度可分为以下六类:
⑴说明语句。
如各种数据类型说明(整型、实型、布尔型、字符型、复型、双精度型、枚举、子界、数组、集合、文件、记录、指针等),各种数据空间特性说明(如公用语句,共名语句,等价语句等),初值语句。
实习重点是内存空间的分配方法。
⑵顺序结构语句。
典型代表是各类表达式(如算术表达式、布尔表达式、字符表达式、位表达)及相应的赋值语句。
实习重点是算术表达式的翻译方法。
⑶控制结构语句。
常见的有转移语句、条件语句和各种分叉语句。
实习重点是拉链返填的方法。
⑷子程序结构。
指子程序、函数、过程这类结构的定义和调用。
实习重点是哑实结合的方法。
⑸循环结构。
如计数循环、条件循环等。
实习重点是循环化简的方法。
⑹格式语句。
主要指输入输出语句的格式加工。
实习重点是数据编辑的方法。
根据教学要求可从以上六类中选择一至三类实习。
二、实习题
⒈题目在对简化的算术表达式进行语法分析的同时生成四元式。
⒉算法根据题意,所求的四元式生成程序核心部分(指表达式、项和因式的处理)的算法,可用类C语言算法描述如下:
PROCEDUREE;
{
EIPLACE:
=T;
WHILESYM='+'OR'-'DO
{
ADVANCE;
E2PLACE:
=T;
T1:
=NEWTEMP;
GEN(±,E1PLACE,E2PLACE,T1);
E1PLACE:
=T1
};
RETURN(E1PLACE)
}.
PROCEDURET;
{
T1PLACE:
=F;
WHILESYM='*'OR''DO
{
ADVANCE:
T2PLACE:
=F;
T1:
=NEWTEMP;
GEN(*/,T1PLACE,T2PLACE,T1);
T1PLACE:
=T1
};
RETURN(T1PLACE)
}
PROCEDUREF:
{
IFSYM=标识符
THEN
{
ADVANCE;
RETURN(ENTRY(i))
}
ELSE
IFSYM='('
THEN
{
ADVANCE;
PLACE:
=E;
IFSYM=')'
THEN
{
ADVANCE;
RETURN(PLACE)
ELSEERROR
}
ELSEERROR
}
这里:
E──表达式;
T──项;
F──因式;
ADVANCE──将输入串指针调整至指向下一个输入字符;
NEWTEMP──分配一个新的工作单元;
GEN──将一个四元式填入四元式表;
ENTRY──查找名表,并获得名字所在位置值。
3.具体要求
(1)完善并用程序实现2中的翻译算法
(2)用算符优先分析法或LR分析法实现所要求的翻译
(3)调试、运行翻译程序,观察中间代码的生成过程并写出实习报告
结束语
编译程序从输入高级语言源程序到输出目标代码(比如汇编语言),是一个庞大的系统工程,是一个有机整体。
每个实验在单独进行的同时,要注意其前驱工作和后继工作,比如语法分析器的输入是词法分析器的输出,而语法分析器的输出将作为代码优化或目标代码生成器的输入。
本课程结束之后还有一周的课程设计,课程设计的基本要求就是将词法分析程序与语法分析程序连在一起进行。
此外,根据教学要求的不同,还可增加其它的实验,比如语义程序设计、目标代码生成器的设计与实现等。