编译原理实验教案.docx
《编译原理实验教案.docx》由会员分享,可在线阅读,更多相关《编译原理实验教案.docx(41页珍藏版)》请在冰点文库上搜索。
编译原理实验教案
编译原理实验教案
授课教师:
程细才
适用专业:
计算机科学与技术
使用班级:
04计算机科学与技术1\2班
授课时间:
20XX年春季
授课学时:
54/44/10学时
使用教材:
编译原理
华中科技大学出版社
何炎祥主编
实验指导书:
编译原理实验指导书,
黄石理工学院计算机学院
实验教学进度表
周次
日期
实验课题
学时
实验报告次数
10
5.8-5.12
实验一 C语言子集编译程序
(1)
2
0
11
5.15-5.19
实验一 C语言子集编译程序
(2)
2
0
12
5.22-5.26
实验一 C语言子集编译程序(3)
2
1
13
5.29-6.2
实验二 LEX词法分析程序生成器
2
1
14
6.5-6.9
实验三 YACC语法分析程序生成器
2
1
实验一C语言子集编译程序
一、实验目的
用C语言对一个C语言的子集编制一个一遍扫描的编译程序,以加深对编译原理的理解,掌握编译程序的实现方法和技术。
1.设计、编制并调试一个词法分析程序,加深对词法分析原理的理解。
2.编制一个递归下降分析程序,并对C语言的简单子集进行分析。
3.通过上机实习,加深对语法制导翻译原理的理解,掌握将语法分析所识别的语法成分变换中间代码的语义翻译方法。
二、实验要求、内容及学时
词法分析部分:
2学时
(一)待分析的C语言子集的词法:
1.关键字
mainifelseintreturnvoidwhile
所有关键字都是小写。
2.专用符号
=+-*/<<=>>===!
=;:
{}[]()
3.其他标记ID和NUM
通过以下正规式定义其他标记:
ID→letter(letter|digit)*NUM→digit(digit)*
letter→a|…|z|A|…|Zdigit→0|…|9
4.空格由空白、制表符和换行符组成
空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段空格通常被忽略。
各种单词符号对应的类别码:
(采用一符一类别码,见下表)
单词符号
类别码
单词符号
类别码
单词符号
类别码
main
1
-
23
;
34
int
2
*
24
>
35
char
3
/
25
<
36
if
4
(
26
>=
37
else
5
)
27
<=
38
for
6
[
28
==
39
while
7
]
29
!
=
40
ID
10
{
30
‘\0’
1000
NUM
20
}
31
ERROR
-1
=
21
,
32
+
22
:
33
(二)词法分析程序的功能:
输入:
所给文法的源程序字符串。
输出:
二元组(syn,token或sum)构成的序列。
其中,
syn为单词类别码。
token为存放的单词自身字符串。
sum为整型常量。
具体实现时,可以将单词的二元组用结构进行处理。
例如:
对源程序
main()
{
inti=10;
while(i)i=i-1;
}
的源文件,经词法分析后输出如下序列:
(1,main)(26,()(27,))(30,{)(2,int)(10,i)(21,=)(20,10)
(34,;)(7,while)(26,()(10,i)(27,))(10,i)(21,=)(10,i)
(23,-)(20,1)(34,;)(31,})
(三)词法分析程序主要算法思想:
算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。
1.主程序示意结构图(如下):
置初值
调用扫描子程序
输出单词二元组
直至输入串结束
注:
①关键字表初值
关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字),当扫描程序识别出标识符时,查关键字表。
如能查到匹配的单词,则该单词为关键字,否则为一般标识符。
关键字表可处理为一个字符串数组(实际为指向字符数组的指针数组),其描述如下:
char*KEY_WORDS[8]=
{“main”,”int”,”char”,”if”,”else”,”for”,”while”};
为分析方便,这里把main作关键字处理。
②程序中需要用到的主要变量:
syn,token和sum。
2.扫描子程序(scaner)的算法思想
首先设置三个变量:
token用来存放构成单词符号的字符串;sum用来存放整型单词;syn用来存放单词的类别码。
扫描子程序主要部分N—S图如下:
变量初始化
忽略空格
文件是否结束
T
F
是否字母
T
F
拼字符串
是否数字
是否关键字
T
F
T
F
拼数
是否运算符、界符等符号
syn为对应关键字的类别码
syn=10
syn=20
T
F
给出相应的syn值
报错
语法分析部分:
2学时
(一)待分析的C语言子集的语法
用扩充的BNF表示如下:
1.<程序>→main()<语句块>
2.<语句块>→’{‘<语句串>’}’
3.<语句串>→<语句>{;<语句>};
4.<语句>→<赋值语句>|<条件语句>|<循环语句>
5.<赋值语句>→ID=<表达式>
6.<条件语句>→if(<条件表达式>)<语句块>
7.<循环语句>→while(<条件表达式>)<语句块>
8.<条件表达式>→<表达式><关系运算符><表达式>
9.<表达式>→<项>{+<项>|-<项>}
10.<项>→<项>{*<因子>|/<因子>}
11.<因子>→ID|NUM|(<表达式>)
12.<关系运算符>→<|<=|>|>=|==|!
=
(二)语法分析程序的主要算法思想
1.主程序结构示意图如下:
置初值
调用scaner读下一个单词符号
调用lrparser
结束
2.递归下降分析程序结构示意图如下:
注:
上接lrparser
是否单词串main()
T
F
调用scaner
出错处理
调用语句块分析函数
源程序是否结束
T
F
输出分析成功
出错处理
3.语句块分析结构示意图。
是否{
T
F
调用scaner
出错处理
调用语句串分析函数(过程)
是否}
T
F
出错处理
4.语句串分析结构示意图如下:
调用statement函数
当为;时
调用scaner
调用statement函数
出错处理
5.statement函数N—S图如下:
是否标识符
T
F
调用scaner
是否if
是否=
T
F
T
F
调用scaner
是否while
调用scaner
出错处理
调用condition
T
F
调用expression
调用语句块
调用scaner
出错处理
调用condition
调用语句块
6.expression函数结构示意图如下:
调用term
是否+、-
T
F
调用scaner
出错处理
调用term
7.term函数结构示意图如下:
调用factor
是否*、/
T
F
调用scaner
出错处理
调用factor
8.condition函数结构示意图如下:
调用expression
是否逻辑运算符
T
F
调用scaner
出错处理
调用expression
9.factor函数结构示意图如下:
是否标识符
T
F
调用scaner
是否数字
T
F
调用scaner
是否(
T
F
调用scaner
出错处理
调用expression
是否)
T
F
调用scaner
出错处理
语义分析部分:
2学时
(一)实验的输入和输出
输入是语法分析提供的正确的单词串,输出是四元式序列。
例如,对于语句串
i=2*3+4;
if(i>10){j=3;}
while(j>0)k=1;
输出的四元式序列如下:
1:
*,2,3,T1
2:
+,T1,4,T2
3:
=,T2,,i
4:
j>,i,10,6
5:
j,,,7
6:
=,3,,j
7:
j>,j,0,9
8:
j,,,11
9:
=,1,,k
10:
j,,,7
(二)算法思想
1.设置语义过程
①intgen(op,arg1,arg2,result)
该函数是将四元式(op,arg1,arg2,result)送到四元式表中。
②char*newtemp()
该函数回送一个新的临时变量名,临时变量名产生的顺序为:
T1,T2,……
③intmerg(p1,p2)
该函数将以p1和p2为头指针的两条链合并为一,合并后的链表首为返回值。
④intbp(p,t)
该函数的功能是把p所链接的每个四元式的第四区段都填为t。
2.主程序示意图如下:
置初值
调用scaner
……
调用lrparser
打印四元式列表
结束
3.函数lrparse在原来语法分析的基础上插入相应的语义动作。
将输入串翻译成四元式序列。
在实验中我们只对表达式、if语句和while语句进行翻译,其具体翻译程序见实例。
算符优先分析法部分:
(选作)
算符优先分析法特别有利于表达式的处理,宜于手工实现。
算符优先分析过程是自下而上的归约过程,但这种归约未必是严格的规范归约。
因此,所谓算符优先分析法就是定义算符之间的某种优先关系,并借助这种关系寻找句型的最左素短语进行归约。
算符优先分析法通常有两种:
优先矩阵法和优先函数法。
前者是提供一张算符优先关系表,后者提供两个优先函数(入栈优先函数f和比较优先函数g),优先函数法比优先矩阵法节约存储空间,所以较为普遍。
下面介绍使用优先函数法的分析过程。
分析过程:
先在算符栈置“$”,然后开始顺序扫描表达式。
若读来的单词符号是操作数,则直接进操作数栈,然后继续下一个单词符号。
分析过程从头开始,并重复进行;若读来的单词符号是运算符
,则将当前处于运算符栈顶的运算符
的入栈优先函数f与
的比较优先函数g进行比较。
1.若
,则
进算符栈,并继续顺序往下扫描,分析过程重复进行。
2.若
,则产生对操作数栈顶的若干项进行
运算的中间代码,并从运算符栈顶移去
,且从操作数栈顶移去相应若干项,然后把执行
运算的结果压入操作数栈。
接着以运算符栈新的项与
进行上述比较。
3.重复步骤1,2,直到“$”和“$”配对为止。
三、实验环境
DOS或Windows操作系统
TURBOC2.0或VisualC++
四、实验参考(参考代码)
#ifndef_GLOBALS_H
#define_GLOBALS_H
#include
#include
#include
#define_SYN_MAIN1
#define_SYN_INT2
#define_SYN_CHAR3
#define_SYN_IF4
#define_SYN_ELSE5
#define_SYN_FOR6
#define_SYN_WHILE7
#define_SYN_ID10
#define_SYN_NUM20
#define_SYN_ASSIGN21
#define_SYN_PLUS22
#define_SYN_MINUS23
#define_SYN_TIMES24
#define_SYN_DIVIDE25
#define_SYN_LPAREN26
#define_SYN_RPAREN27
#define_SYN_LEFTBRACKET128
#define_SYN_RIGHTBRACKET129
#define_SYN_LEFTBRACKET230
#define_SYN_RIGHTBRACKET231
#define_SYN_COMMA32
#define_SYN_COLON33
#define_SYN_SEMICOLON34
#define_SYN_LG35
#define_SYN_LT36
#define_SYN_ME37
#define_SYN_LE38
#define_SYN_EQ39
#define_SYN_NE40
#define_SYN_END1000
#define_SYN_ERROR-1
#defineMAXLENGTH255
#ifndef_SEMANTEM_H
#define_SEMANTEM_H
/*四元组的结构*/
typedefstructQUAD{
charop[MAXLENGTH];/*操作符*/
charargv1[MAXLENGTH];/*第一个操作数*/
charargv2[MAXLENGTH];/*第二个操作数*/
charresult[MAXLENGTH];/*运算结果*/
}QUATERNION;
voidlrparse(void);/*语法语义分析主函数*/
#endif
unionWORDCONTENT{
charT1[MAXLENGTH];
intT2;
charT3;
};
typedefstructWORD{
intsyn;
unionWORDCONTENTvalue;
}WORD;
#ifndef_SCAN_H
#define_SCAN_H
#define_TAB_LEGNTH4
#define_KEY_WORD_END"waitingforyouexpanding"
voidScaner(void);
#endif
QUATERNION*pQuad;
intnSuffix,nNXQ,ntc,nfc;
externWORDuWord;
externintgnColumn,gnRow;
FILE*fw;
char*strFileName;
char*strSource;
char*Expression(void);
char*Term(void);
char*Factor(void);
voidStatement_Block(int*nChain);
/*FILE*Source;*/
FILE*fw;
char*strSource;
voidDo_Tag(char*strSource);
voidDo_Digit(char*strSource);
voidDo_EndOfTag(char*strSource);
voidDo_EndOfDigit(char*strSource);
voidDo_EndOfEqual(char*strSource);
voidDo_EndOfPlus(char*strSource);
voidDo_EndOfSubtraction(char*strSource);
voidDo_EndOfMultiply(char*strSource);
voidDo_EndOfDivide(char*strSource);
voidDo_EndOfLParen(char*strSource);
voidDo_EndOfRParen(char*strSource);
voidDo_EndOfLeftBracket1(char*strSource);
voidDo_EndOfRightBracket1(char*strSource);
voidDo_EndOfLeftBracket2(char*strSource);
voidDo_EndOfRightBracket2(char*strSource);
voidDo_EndOfColon(char*strSource);
voidDo_EndOfComma(char*strSource);
voidDo_EndOfSemicolon(char*strSource);
voidDo_EndOfMore(char*strSource);
voidDo_EndOfLess(char*strSource);
voidDo_EndOfEnd(char*strSource);
voidPrintWord(WORDuWord);
voidApartWord(char*strSource);
voidPrintError(intnColumn,intnRow,charchInput);
voidScaner(void);
intgnColumn,gnRow,gnLocate,gnLocateStart;
WORDuWord;
char*KEY_WORDS[20]={"main","int","char","if","else","for",
"while","void",_KEY_WORD_END};
intIsDigit(charchInput)
//判断扫描的字符是否数字
{
if(chInput<='9'&&chInput>='0')return1;
elsereturn0;
}
intIsChar(charchInput)
//判断扫描的字符是否字母
{
if((chInput<='z'&&chInput>='a')||(chInput<='Z'&&chInput>='A'))
return1;
elsereturn0;
}
voidDo_Start(char*strSource)
//开始识别一个单词
{
gnLocateStart=gnLocate;
switch(strSource[gnLocate]){
case'+':
Do_EndOfPlus(strSource);break;
case'-':
Do_EndOfSubtraction(strSource);break;
case'*':
Do_EndOfMultiply(strSource);break;
case'/':
Do_EndOfDivide(strSource);break;
case'(':
Do_EndOfLParen(strSource);break;
case')':
Do_EndOfRParen(strSource);break;
case'[':
Do_EndOfLeftBracket1(strSource);break;
case']':
Do_EndOfRightBracket1(strSource);break;
case'{':
Do_EndOfLeftBracket2(strSource);break;
case'}':
Do_EndOfRightBracket2(strSource);break;
case':
':
Do_EndOfColon(strSource);break;
case',':
Do_EndOfComma(strSource);break;
case';':
Do_EndOfSemicolon(strSource);break;
case'>':
Do_EndOfMore(strSource);break;
case'<':
Do_EndOfLess(strSource);break;
case'=':
Do_EndOfEqual(strSource);break;
case'\0':
Do_EndOfEnd(strSource);break;
default:
if(IsChar(strSource[gnLocate]))
{
Do_Tag(strSource);
}
else
if(IsDigit(strSource[gnLocate]))
{
uWord.value.T2=strSource[gnLocate]-'0';
Do_Digit(strSource);
}
else
{
if(strSource[gnLocate]!
=''
&&strSource[gnLocate]!
='\t'
&&strSource[gnLocate]!
='\n'
&&strSource[gnLocate]!
='\r')
{
PrintError(gnColumn,gnRow,strSource[gnLocate]);
}
if(strSource[gnLocate]=='\n'
||strSource[gnLocate]=='\r')
{
gnColumn++;
gnRow=1;
}
else
if(strSource[gnLocate]=='\t')
{
gnColumn+=_TAB_LEGNTH;
}
else
gnRow++;
gnLocate++;
Do_Start(strSource);
}
break;
}
return;
}
voidDo_Tag(char*strSource)
//识别标识符的中间状态
{
gnLocate++;
gnRow++;
if(IsChar(strSource[gnLocate])||IsDigit(strSource[gnLocate]))
{
Do_Tag(strSource);
}
else
Do_EndOfTag(strSource);
return;
}
voidDo_Digit(char*strSource)
//识别整数的中间状态
{
gnLocate++;
gnRow++;
if(IsDigit(strSource[gnLocate]))
{
uWord.value.T2=uWord.value.T2*10+strSource[gnLocate]-'0';
Do_Digit(strSource);
}
elseDo_EndOfDigit(strSource);
return;
}
voidDo_EndOfTag(char*strSource)
//识别标识符的最后状态
{
intnLoo