实验二 编写语法分析程序
一、实验目的
通过设计、编写、调试一个递归下降语法分析程序,实现对词法分析程序所提供的单词
序列进行语法检查和结构分析,掌握递归下降语法分析方法。
二、实验设计
1、简述递归下降分析的基本原理
将文法的每一个非终结符 U 的文法规则看作是识别 U 的一个过程定义,为每个非
终结符构造一个子程序,以完成该非终结符所对应的语法成分的分析和识别任务。如果
U 的文法规则的右部只有一个候选式,则按从左向右的顺序依次构造规则 U 的识别过
程。当右部有多个候选式时根据每个候选式的第一个符号确定该候选式分支。如果有终
结符,判断是否与输入符号相等,相等则识别成功,读下一个字符,不等则说明输入串
有语法错误。如果是非终结符则调用该非终结符对应的子程序。
2、简述递归下降分析程序设计的基本过程
(1)看文法是否有左递归,是否存在二义性问题
(2)消除二义性,左递归改为右递归
(3)求出改造后文法的所有右部符号串的 FIRST 集,如果有ε产生式,求出该式子
对应的左部非终结符的 FOLLOW 集
(4)为改写后文法的每一个非终结符构造相应的子程序
3、本实验的设计过程:改写文法,右部符号串的 FIRST 集和相关非终结符的 FOLLOW
集,产生式的特殊处理方法及理由。
(1)改写文法:
<2>.
→ | ε
改写后: → A ,A →A|ε
<4>.→| ε
改写后: → B , B →B| ε
<14>. →
additive_expr >
改写后: →E
E→ε|(>|<|>=|<=|==|!=)< additive_expr >
<15>.< additive_expr>→< additive_expr>+|< additive_expr>-|< term >
改写后:< additive_expr> →< term >C , C →+C|-C|ε
<16>.< term >→< term >*|< term >/|< factor >
additive_expr
|<
>(>|<|>=|<=|==|!=)<
改写后:< term > →< factor >D
D →*
D|/D|ε
(2)右部符号串的 FIRST 集
}
<1>.First({})= {
<2>.First(A)=First(A)= { int ,ε }
{
First(A)=First( int ID;)= { int }
<3>.First(int ID;)={int}
<4>.First(B)={ if,while,for,read,write,{, ; ,ID,NUM,( }
First(B) ={ if,while,for,read,write,{, ; , ID , ε}
<5>.First()={if}
First()={while}
First()={for}
First()={read}
First()={write}
First(< compound_stat >)={{}
First()={ ;,ID,NUM,( }
<6>.First(if () )={ if }
First(if () else < statement >)={ if }
<7>.First(while () < statement >)={ while }
<8>.First(for (;;))={ for }
<9>.First(write ;)={ write }
<10>.First(read ID;)={ read }
<11>.First({})={{}
<12>.First(< expression >;)= {ID,NUM,(}
First(;)={;}
<13>.First(ID=)={ID}
.First()={ID,NUM,(}
<14>.First(E)={ID,NUM,(}
First((>|<|>=|<=|==|!=)< additive_expr >)={>,<,>=,<=,==,!=}
First(ε)={ ε}
<15>.First(< term >C)={ID,NUM,(}
First(+C)={+}
First(-C)={-}
<16>.First(< factor >D)={ID,NUM,(}
First(*
D)={*}
First(/D)={/}
First(ε)={ε}
<17>.First((< expression >))={ ( }
First(ID)=ID
First(NUM)=NUM
(3)相关非终结符的 FOLLOW 集
<2>. → A , A →A|ε
Follow(A) ={ if,while,for,read,write,{,},;,ID,(,NUM}
<4>. → B , B →B| ε
Follow(B) ={ } }
<14>. →E , E→ε|(>|<|>=|<=|==|!=)<
additive_expr >
Follow(E)={;}
<15>.< additive_expr> →< term >C , C →+C|-C|ε
Follow(C)={;, ), (>|<|>=|<=|==|!=)}
<16>.< term > →< factor >D , D →*D|/D|ε
Follow(D)={+,-, <,>,>=,<=,==,!=, ; , )}
(4)产生式的特殊处理方法
对产生式< expression > → ID=| ,采取超前读一个符
号的方法。因为当前符号是“ID”的时候,不能确定选择哪个子程序进行分析,
所以再读一个符号 ch1,如果 ch1 是“=”,选择 ID=,再读一个符号
调用对应表达式。如果不是,文件指针回到读到“ID”的位置,调用
对应的子程序。
4、分析程序的设计框架,错误信息的输出等
(1).程序设计框架
每个非终结符对应一个函数。读一个符号,然后进入函数,在函数内判断输入
符号是否在其右部符号串的 FIRST 集中,如果当前符号在其 FIRST 集中并且是非
终结符,读一个符号,进入下一个函数,若是终结符,读一个符号。如果右部有ε,
判断输入符号是否在左部非终结符的 FOLLOW 集中,如果在,return。如果以上两
点都不满足,既不在 FIRST 集也不在 FOLLOW 集里,则输出错误提示。
(2).、流程图
(3).错误信息输出
根据程序分析情况输出错误提示:第 n 行缺少------;
三、实验过程
1、完成整个实验的先后步骤
(1)改造文法,左递归改为右递归
(2)求右部符号串的 FIRST 集
(3)求相关非终结符的 FOLLOW 集
(4)编写程序
(5)调试、修改程序
2、实验调试记录(问题表现,分析原因,解决方案,解决结果)
(1)程序始终停留在 for 循环那一句,分析的原因是 expression 对应的函数有问题,可
能是超前读没有写对。解决方案:修改 expression 的超前读方法,检查整个程序有没有少读
了字符的地方 结果 得到了正确的输出。
(2)不能检查出最后的}不能匹配 分析原因 getsymbol 方法中采用的是 fscanf 读取字
符,遇到空格或换行就好自动停止读符号,所以字符会一直保持为最后那个不是空格或换行
的字符,解决方法 将读字符的方法改为用字符数组读取的方式 结果 可以获得最后的空格
或换行,判断}没有匹配。
三、实验结果
测试代码为:
测试结果为有九处错误
(1)第四行:int 2b; int 后面读到的是 NUM,所以缺少 ID
去掉 2 后继续测试
(2)第五行:int c 分析到下一行才能发现缺少;,所以行数为 6
添加;后继续测试
(3)第六行:for (i=1; i <= 10 i=i+1)
i<=10 后缺少;,添加后继续测试
(4)第九行:a=a+i 同样为读到第十行才能发现缺少;
添加;继续测试
(5)第十四行:while(x<=)
修改为 x<=y 继续测试
(6)第十六行:C=a+b+(x*y; 缺少)
添加)后继续测试
(7)第二十六行:write c 缺少;
添加;后继续测试
(8)第二十七行:缺少}
添加}继续测试
(9)第二十七行:缺少}
添加}后继续测试,无语法错误。
缺少两个}不能匹配。
四、讨论与分析
(1)语法规则中 2 4 6 13 14 15 16 不满足递归下降无回溯的条件,6 和 13 采取超前读
一个符号的方法解决,2、4、14、15、16 则采取将左递归改为右递归的方法。
(2)不是所有的文法都可以改写为满足递归下降分析条件的文法,不如不符合 LL(1)
文法规则的文法。如 A→aB|C C→ab B →cd 该文法右部有两个选择,但是
FIRST(aB)与 FIRST(C)相交不为空,所以不是 LL(1)文法,不可以改写成满
足地柜下降条件的文法
(3)改写文法后使文法变得不直观,也会增加代码量。有时候写着写着就有点蒙圈。
五、附录:关键代码
getsymbol() 每次读取词法分析后结果的一行,得到输入字符和行数
void getsymbol()
{
char ch1;
int i=0;
int j=0;
int k=0;
memset(ch,'\0',30);
memset(ch2,'\0',30);
memset(ch3,'\0',30);
ch1=fgetc(fp);
if(ch1!=EOF)
{
while(ch1!='\t')
{
strncpy(&ch[i++],&ch1,1);
ch1=fgetc(fp);
}
ch1=fgetc(fp);
while(ch1!='\t')
{
strncpy(&ch2[j++],&ch1,1);
ch1=fgetc(fp);
}
ch1=fgetc(fp);
while(ch1!='\n')
{
strncpy(&ch3[k++],&ch1,1);
ch1=fgetc(fp);
}
int len=strlen(ch3);
n=0;
for(int x=0;x
}
program()
void program()
{
if(strcmp("{",ch)==0)
{
getsymbol();
declaration_list();
statement_list();
if(strcmp("}",ch)==0)
{
return ;
}
else
{
}
printf("第%d 行缺少}\n",n);
exit(0);
}
else
{
}
printf("第%d 行缺少{",n);
exit(0);
}
declaration_list()
void declaration_list()
{
if(strcmp("int",ch)==0)
{
A();
}