96778997973编译原理实验指导书新Word格式文档下载.docx
《96778997973编译原理实验指导书新Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《96778997973编译原理实验指导书新Word格式文档下载.docx(59页珍藏版)》请在冰豆网上搜索。
{intx,a,b。
floaty,c,d。
x=a+b。
y=c/d。
if(x>
y>
x=10。
else
y=100。
}
将该源程序的源文件经词法分析后输出以二元组形式表示的单词符号序列。
3.编写的程序具有一定的查错能力。
提交的实验报告中要有实验名称、实验目的、实验内容、实验程序清单、调试过程和运行结果,程序的主要部分作出功能说明,并有实验收获体会或改进意见等内容。
4.实验前请仔细阅读实验预习提示,提示中程序仅供参考。
5.本实验建议学时数为4学时。
【实验预习提示】
1.词法分析器的功能和输出格式
词法分析器的功能是输入以字符串表示的源程序,输出单词符号或单词符号序列。
词法分析器的单词符号常常表示成以下的二元组(单词的种别码,单词符号的属性值>
。
本实验中,采用的是一符一种种别码的方式。
2.调试程序文法的EBNF<
扩展巴科斯范式)表示如下
<
程序>
:
=main(>
语句串>
=<
语句>
{;
赋值语句>
|<
条件语句>
循环语句>
=<
变量>
表达式>
=if<
条件>
then<
=while<
关系运算符>
项>
{+<
|—<
因子>
{*<
|/<
无符号整数>
数字>
{<
标识符>
字母>
下划线>
=>
|>
=|<
=|==|!
=
=A|B|C|……|Z|a|b|c|……|z(要求不区分大小写>
=0|1|2|3|4|5|6|7|8|9
3.“超前搜索”方法
词法分析时,常常会用到超前搜索方法。
如当前待分析字符串为“a>
+”,当前字符为’>
’,此时,分析器倒底是将其分析为大于关系运算符还是大于等于关系运算符呢?
显然,只有知道下一个字符是什么才能下结论。
于是分析器读入下一个字符’+’,这时可知应将’>
’解释为大于运算符。
但此时,超前读了一个字符’+’,所以要回退一个字符,词法分析器才能正常运行。
在分析标识符,无符号整数等时也有类似情况。
4.词法分析程序主程序的算法思想
算法的基本思想是根据扫描到单词符号的第一个字符的种类,拼写出相应的单词符号,其实
现的基本任务是从字符串表示的源程序中识别出相应的具有独立意义的单词符号。
主程序示
意图如图1.1所示。
图1.1词法分析主程序示意图
其中设置初始值包括两个方面:
1)关键字表的初值
关键字作为特殊标识符处理,把它们预先安排在一张表中,称这张表为关键字表,当扫描程序
识别出标识符时,查关键字表,若能查找到匹配的单词,则该单词是关键字,否则为一般标识
符。
关键字表为一个字符串数组,其描述为:
char*KEY_WORDS[8]={“main”,”int”,”char”,”if”,”else”,”while”,”for”}。
2)程序中需要用到的主要变量为syn,token和sum。
5.参考部分源程序
[1]globals.h/*本头文件定义分析器需要的一些数据结构和宏等*/
#ifndef_GLOBALS_H
#define_GLOBALS_H
#include<
stdio.h>
stdlib.h>
string.h>
#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_NUM11/*整数的单词种别码*/
#define_SYN_PLUS13/*算术运算符“+”的单词种别码*/
#define_SYN_MINUS14/*算术运算符“-”的单词种别码*/
#define_SYN_TIMES15/*算术运算符“*”的单词种别码*/
#define_SYN_DIVIDE16/*算术运算符“/”的单词种别码*/
#define_SYN_ASSIGN17/*=*/
#define_SYN_SEMICOLON18/*。
*/
#define_SYN_LT20/*<
#define_SYN_NE21/*!
=*/
#define_SYN_LE22/*<
#define_SYN_LG23/*>
#define_SYN_ME24/*>
#define_SYN_EQ25/*=*/
#define_SYN_LPAREN28/*(*/
#define_SYN_RPAREN29/*>
#define_SYN_OVER0/*#即源程序结束标志*/
#defineMAXLENGTH255/*一行允许的字符个数*/
unionWORDCONTENT{/*存放单词内容的联合*/
charT1[MAXLENGTH]。
intT2。
charT3。
}。
typedefstructWORD{/*单词二元组*/
intsyn。
unionWORDCONTENTvalue。
}WORD。
#endif
[2]scan.h/*本头文件定义词法分析器的接口*/
#ifndef_SCAN_H
#define_SCAN_H
#define_TAB_LEGNTH4/*一个TAB占用的空格数*/
#define_KEY_WORD_END"
waitingforexpanding"
/*关键字结束标志*/
voidScaner(void>
/*函数scaner得到源程序里的下一个单词符号*/
[3]Symbol.c
#include"
basedata.h"
symbol.h"
#include<
conio.h>
ctype.h>
char*WORD[WORDLEN]={"
BEGIN"
"
CALL"
CONST"
DO"
"
END"
IF"
ODD"
PROCEDURE"
"
READ"
THEN"
VAR"
WHILE"
WRITE"
}。
//保留字字符串表,用于将保留字种别码转为字符串输出
SYMBOLWSYM[WORDLEN]={BEGINSYM,CALLSYM,CONSTSYM,
DOSYM,ENDSYM,IFSYM,ODDSYM,
PROCSYM,READSYM,THENSYM,
VARSYM,WHILESYM,WRITESYM}。
//保留字种别码表
char*SNAME[SYMBOLNUM]=
{"
NOL"
IDENT"
NUMBER"
PLUS"
MINUS"
TIMES"
"
SLASH"
ODDSYM"
EQL"
NEQ"
LSS"
LEQ"
GTR"
GEQ"
LPAREN"
RPAREN"
COMMA"
SEMICOLON"
PERIOD"
BECOMES"
BEGINSYM"
ENDSYM"
IFSYM"
THENSYM"
WHILESYM"
WRITESYM"
READSYM"
DOSYM"
CALLSYM"
CONSTSYM"
VARSYM"
PROCSYM"
//单词字符串表,用于将保留字种别码转为字符串输出
SYMBOLsym。
//最近已识的单词种别码
chartoken[MAXIDLEN+1]。
//最近已识别的单词
intnum。
//最近已识别的数字值
charch。
//最近已识别的字符
intcol=1,row=1。
//当前行和列值
FILE*fd。
//指向待编译文件
externFILE*fout。
//指向存放结果文件
voidGetchar(void>
{
ch=fgetc(fd>
if(ch!
=EOF&
&
ch!
='
\n'
>
col++。
return。
voidGetbc(void>
while(ch==SPACE||ch==TABLE||ch=='
{
if(ch=='
{row++。
col=1。
Getchar(>
}//为空字符则一直读至不为空字符
voidRetract(void>
fseek(fd,-1l,SEEK_CUR>
col--。
voidConcat(void>
chartemp[2]。
temp[0]=ch。
temp[1]='
\0'
strcat(token,temp>
intReserve(void>
inti,j。
chartemp[60]。
j=strlen(token>
for(i=0。
i<
j。
i++>
temp[i]=toupper(token[i]>
//将当前token字以大写形式存入temp中
}
temp[i]='
WORDLEN。
if(!
strcmp(WORD[i],temp>
break。
}//判断当前token是否是保留字
if(i>
=WORDLEN>
i=-1。
returni。
voidErrorsym(void>
fprintf(fout,
Thereiserror@row:
%5d,@col:
%5d"
row,col>
intGetsym(void>
intk。
intflag=TRUE。
Getbc(>
//滤掉白字符
strcpy(token,"
if(isalpha(ch>
//以字母开头则是标识符
num=0。
Concat(>
while(isalnum(ch>
{
Concat(>
Getchar(>
}
Retract(>
//由于超前搜索了,所以回退一个字符
k=Reserve(>
//判断此标识符是否是保留字
if(k!
=-1>
sym=WSYM[k]。
//将保留字种别码存入sym中
else
sym=IDENT。
//将一般标识符种别码存入sym中
}//endelsek!
=-1。
}//endofifisalpha
elseif(isdigit(ch>
//以数字开头则为无符号整数
while(isdigit(ch>
flag=FALSE。
while(isalnum(ch>
}//endofflag=FALSE
Retract(>
//回退
if(flag>
//若是无符号整数,则将整数值存于num中
{sym=NUMBER。
num=atoi(token>
}//endofifisdigit
switch(ch>
case'
+'
Concat(>
sym=PLUS。
break。
-'
sym=MINUS。
*'
sym=TIMES。
/'
sym=SLASH。
('
sym=LPAREN。
'
sym=RPAREN。
sym=EQL。
#'
sym=NEQ。
/*
ODDSYM,EQL,NEQ,LSS,LEQ,GTR,GEQ,LPAREN,
RPAREN,COMMA,SEMICOLON,PERIOD,BECOMES,
*/
'
sym=COMMA。
.'
sym=PERIOD。
sym=SEMICOLON。
Concat(>
Getchar(>
if(ch!
//若后不跟'
则回退
{sym=GTR。
else
{Concat(>
sym=GEQ。
break。
{sym=LSS。
sym=LEQ。
{flag=FALSE。
sym=BECOMES。
default:
flag=FALSE。
}//endofswitchelsechar
}//endofelsechar
returnflag。
[4]Testsym.c
externchar*WORD[WORDLEN]。
externintWSYM[WORDLEN]。
externchar*SNAME[SYMBOLNUM]。
externSYMBOLsym。
//lastreadedwordtype。
externchartoken[MAXIDLEN+1]。
//lastreadedword
externintnum。
//lastreadednum。
externcharch。
//lastreadedchar。
externintcol,row。
externFILE*fd。
FILE*fout。
voidInit(void>
voidQuit(void>
voidmain(>
intflag。
Init(>
fprintf(fout,"
\nTOKENSYMNUM"
do{
flag=Getsym(>
\n%10s%10s%d"
token,SNAME[sym],num>
elseif(ch!
=EOF>
\n%10s"
token>
Errorsym(>
}while(ch!
//反复调用Getsym(>
识别单词,将输出结果存入fout中
Quit(>
//======================================
chartemp[30]。
printf("
\nPleaseinputyourfilename:
gets(temp>
if((fd=fopen(temp,"
rt"
==NULL>
fprintf(stderr,"
Cannotopeninputfile%s.\n"
temp>
getch(>
return。
}//将fd指针指向待分析源文件
if((fout=fopen("
mydata.dat"
"
wt"
Cannotopeninputfile.\n"
}//将fout指向文件mydata.dat
fclose(fd>
fclose(fout>
实验二 熟悉FLEX使用方法
1.掌握FLEX基本使用方法
2.掌握如何将通过FLEX生成的C语言模块加入到自已的程序中
【实验要求】
1.编制FLEX源程序,分别统计文本文件a.txt中出现的标识符和整数个数,并显示之。
标识符定义为字母开头,后跟若干个字母,数字或下划线。
整数可以带+或-号,也可不带,且不以0开头。
非单词和非整数则忽略不记,将之滤掉不显示。
2.编制一FLEX源程序,分别求出文件hh.c中字母,数字,回车符的个数。
3.思考:
若main函数不在FLEX中实现,应该如何实现?
4.本次实验建议学时2学时。
参见附录一。
在看懂的基础上将之调试通过。
实验三 用FLEX自动生成PL/0词法分析器
熟练掌握FLEX,并通过其生成一个词法分析器
1.通过FLEX生成一词法分析器函数intGetsym(>
,其功能同实验一中词法分析器函数类似。
2.生成一工程文件,调用1中生成的函数Getsym(>
,对一指定的文件进行词法分析,要求分析出单词的类型和值。
并将分析结果存入一文件Mydata.dat中。
3.本实验建议学时4学时。
FLEX可自动生成函数intyylex(>
,则intGetsym(>
可通过调用yylex(>
实现。
1.由于FLEX生成的C程序模块lex.yy.c过于复杂,基本不可读,所以不要直接修改它,可将它看成一个“黑箱”,即不需要清楚知道其内部结构,只需要知道其接口即可。
可通过修改FLEX源程序间接修改之。
关于lex.yy.c中常用变量和函数,在附录中有详细说明。
2.编制一FLEX源程序,不妨取名为sym.l,通过FLEX生成lex.yy.c,并将之加入到工程文件中。
3.工程文件结构
生成一工程文件,不妨取名为test.prj,将文件Symbol.c,lex.yy.c,testsym.c加入之。
源程序参考如下:
[1]Basedata.h
同实验一中Basedata.h
[2]Symbol.h
#inclu