一、实验目的
通过设计、开发一个高级语言的递归下降语法分析程序,加深对相关课堂教学内容(包括自顶向下语法分析、FIRST集、FOLLOW集、SELECT集、对某一输入串的分析过程)的理解,提高语法分析方法的实践能力。
二、实验要求
编写一个递归下降语法分析器。
三、实验设备与环境
1.硬件:处理器:12th Gen Intel(R) Core(TM) i5-12500H(16 CPUs),~2.5GHZ
2.软件:dev c++
四、实验内容
五、实验步骤
语法分析的设计与实验采用递归下降法的思想。
为每个非终结符编制一个子程序,子程序的名字表示一个产生式左部的非终结符,程序体则是按该产生式右部的符号串顺序编写的。每匹配一个终结符,则再读入下一个符号,对于产生式右部的每个非终结符,则调用相应子程序。当一个非终结符对应多个候选式时,子程序体按可选集决定选用哪个候选式。
实验步骤
1.分析文法,消除左递归,避免自顶向下分析工作陷入无限循环;消除回溯,避免产生冗余操作。
2.计算出FIRST集、FOLLOW集和SELECT集;
3.根据递归下降语法分析的理论设计相应代码;
4.上机调试,修复bug并完善实验设计;
5.调试完成,完成实验。
产生式 | FIRST集 | FOLLOW集 | SELECT集 |
S->AaB | {a,f,g} | {#} | {a,f,g} |
S->Bb | {d,e,b} | {d,e,b} | |
A->aD | {a} | {a} | {a} |
A->D | {f,g} | {f,g} | |
B->d | {d} | {b,#} | {d} |
B->e | {e} | {e} | |
B-> | {} | {b,#} | |
D->fD | {f} | {a} | {f} |
D->g | {g} | {g} |
void print() //打印输出每一步骤的具体信息
void error() //如果识别错误时,进行报错
void match(string x) //匹配终结符
void D() //非终结符D的调用程序
void B() //非终结符B的调用程序
void A() //非终结符A的调用程序
void S() //非终结符S的调用程序
int main() //字符串的输入以及分析过程的入口
六、实验结果及分析
测试一:
测试二:
测试三:
从开始符号S依次入栈,识别输入串的第一个字符,匹配所用的产生式,再将产生式右部依次入栈。如果符号栈的栈顶元素成功匹配输入串最左端,则将栈顶元素弹出,继续识别下一个字符。如果栈顶元素无法匹配输入串的最左端,则再进行判断、弹出栈顶元素、产生式右部入栈等操作;直到栈顶为终结符且与输入串能成功匹配。重复以上操作,直至符号栈为空且输入串全部识别完成。最终分析成功。
对于以上三组测试样例,均包含了所有产生式以及各种情况,分析较为准确。
七、实验小结和思考
本次实验我充分掌握了递归下降分析法在运行中的实际过程,掌握语法分析的基本方法和技巧。
对于实验结果与预期结果也较为相符,输出符号串的分析过程,包括分析栈、输入串、所用产生式等,对于非法字符也会进行报错。
实验中也会遇到一些问题:对于指向输入串下一个字符的指针总是会指向前一个或后一个,而导致分析错误。为了解决问题,通过查找资料并结合实际情况进行调试和修改,在匹配字符完成后将指针下移,就避免了重复分析或漏分析。测试时也会出现样例不充分,导致无法发现潜在错误,因此,要对程序进行全面测试。
八、源程序清单
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
//存放FIRST集
string s1="afg";
string s2="deb";
string d="fg";
string b="b#";
string b0="";
int i=0,st; //st表示当前步骤,i指向当前字符串位置,j
string chuan;
string q="#";
void print(){
cout<<st<<"\t"<<q<<"\t";
for(int k=i;k<=chuan.length();k++){
cout<<chuan[k];
}
cout<<"\t";
st++;
}
void error(){
cout<<"Error!!"<<endl;
}
void match(string x){
print();
cout<<x<<"匹配"<<endl;
q.pop_back();
i++;
}
void D(){
char token=chuan[i];
if(token=='f')
{
print();
cout<<"D->fD"<<endl;
q.pop_back();
q+="Df";
match("f");
D();
}
else if(token=='g')
{
print();
cout<<"D->g"<<endl;
q.pop_back();
q+="g";
match("g");
}
else error();
}
void B(){
char token=chuan[i];
if(token=='d')
{
print();
cout<<"B->d"<<endl;
q.pop_back();
q+="d";
match("d");
}
else if(token=='e')
{
print();
cout<<"B->e"<<endl;
q.pop_back();
q+="d";
match("e");
}
else if(token=='#'||token=='b')
{
print();
cout<<"B->epsilon"<<endl;
q.pop_back();
return;
}
else error();
}
void A(){
char token=chuan[i];
if(token=='a')
{
print();
cout<<"A->aD"<<endl;
q.pop_back();
q+="Da";
match("a");
D();
}
else if(d.find(token)!=string::npos)
{
print();
cout<<"A->D"<<endl;
q.pop_back();
q+="D";
D();
}
else error();
}
void S(){
char token=chuan[i];
if(s1.find(token)!=string::npos) //查找该字符是否在S的FIRST集中,如果在,则移进该字符。
{
q+="S";
print();
cout<<"S->AaB"<<endl;
q.pop_back(); //弹出栈顶元素,移进产生式右部元素
q+="BaA";
A();
match("a");
B();
if(i<chuan.length()-1) { //如果退出S时,输入串还未移进完毕,则说明分析失败
print();cout<<"分析失败!";
return;
}
print();cout<<"分析成功";
}
else if(s2.find(token)!=string::npos)
{
q+="S";
print();
cout<<"S->Bb"<<endl;
q.pop_back();
q+="bB";
B();
match("b");
if(i<chuan.length()-1) {
print();cout<<"分析失败!";
return;
}
print();cout<<"分析成功";
}
else error();
}
int main(){
cout<<"请输入符号串:";
cin>>chuan;
chuan+="#";
st=1;
cout<<"步骤\t符号栈\t输入串\t所用产生式"<<endl;
S();
return 0;
}