编译原理课程实验——语法分析
参考原文: https://blog.csdn.net/SL_World/article/details/80589011
一.实验目的
掌握掌握语法分析的基本概念和基本方法,正确理解LL(1)分析法
二、算法设计思想
输入:
输出:预测分析器接受i+i*i+i的动作结果输出如下:
算法流程描述:
LL(1)文法是一种自上而下的分析,使用最左推导,从左至右扫描输入串,且对每次最左推导只需向前看一个输入符号,便可确定当前所应当选择的算法规则,分析器结构图如下(参考课本介绍所作):
1)程序中写入某个文法的所有产生式;
2)分别提取终结符VT与非终结符VN(即扫描产生式);
3)构造所有非终结符的First集,由程序自动构造LL(1)分析表 ;
4)预测分析表:
遍历每一个产生式;
如果右部的第一个字符tmp是终结符且不是空串,更新预测分析表;
如果右部的第一个字符是空串,遍历左部的Follow集更新预测分析表;
如果右部的第一个字符是非终结符,遍历它的First集更新预测分析表;
5)从键盘读入要识别的符号串;
6)由程序根据分析表进行预测分析符号串:
规定f1为符号栈栈顶字符,f2为剩余输入串的第一个字符;
若f1=f2=“#”,则分析成功,停止分析Ø 若f1=f2≠”#”,则把f1从栈顶弹出,让f2指向下一个输入符号;
若f1是一个非终结符,则查看分析表,若table[f1][f2]中有值,则f1出栈,并且产生式的右部反序进栈;
再把产生式的右部符号推进栈的同时应做这个产生式相应得语义动作,若M[A,a]中存放着”出错标志”,则调用出错诊察程序error;
7)得出接受符号串的动作结果并打印。
文法中非终结符的First与Follow集及右部产生式的First集:
得出Select集:
Select(E->TE’)={(,i}
Select(T’->FT’)={}
///此处的F前面有乘号*,{}中也是*(由于原语句中不识别*,这里作注释哈)
Select(E’->+TE’)={+}
Select(T’->e)={),#}
Select(E’->e)={),#}
Select(F->(E))={(}
Select(T->FT’)={(,i}
Select(F->i)={i}
下文无关文法的产生式A→α, A∈VN,α∈V*,
若α不能推导出ε,则SELECT(A→α)=FIRST(α);
若α能推导出ε则SELECT(A→α)=(FIRST(α)–{ε})∪FOLLOW(A)。
实验中LL(1)文法的分析表如下表:
三、实验结果与分析
实验结果与分析如下:
以输入符号串i+ii为例,预测分析器接受i+ii的动作结果如下:
结果分析:
1)输入符号串i+ii;
2)文本结束符“#”入栈,开始符号“E”入栈,程序根据预测分析表分析;
3)两栈顶字符判断、对比分析表选择需要的产生式:
a.E与i,一个非终结符,一个终结符对比分析表,E出栈,将产生式E->TE’右部逆序入栈;
b.T与i对比,查分析表,T出栈,将产生式T->FT’右部逆序入栈;
c.F与i对比,查分析表,F出栈,将产生式F->i右部字符i入栈;
d.i与i对比,都是终结符,i匹配,两栈顶i分别出栈;
e.T’与+对比,查分析表,得产生式T’->e,右部为空T’直接出栈,无入栈;
f.E’与+对比,查分析表,E’出栈,将产生式E’->+TE’右部逆序入栈;
g.+与+对比,都是终结符,+匹配,两栈顶+分别出栈;
h.T与i对比,查分析表,F出栈,将产生式T->FT’右部逆序入栈;
i.F与i对比,查分析表,F出栈,将产生式F->i右部字符i入栈;
j.i与i对比,都是终结符,i匹配,两栈顶i分别出栈;
k.T’与对比,查分析表,F出栈,将产生式T’->FT’右部逆序入栈;
l.与对比,都是终结符,匹配,两栈顶分别出栈;
m.F与i对比,查分析表,F出栈,将产生式F->i右部字符i入栈;
n.i与i对比,都是终结符,i匹配,两栈顶i分别出栈;
o.T’与#对比,查分析表,得产生式T’->e,右部为空T’直接出栈,无入栈;
p.E’与对比,查分析表,得产生式E’->e,右部为空E’直接出栈,无入栈;
4)符号串i+i*i分析完成,达到接受状态,打印结果。
四、程序源码
程序源码如下:
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<stack>
#include<set>
#include<cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
void input_G(); //LL(1)文法输入函数以及VN和VT之间的分离
void Select();
bool Judge_LL1();
void Predict();
void Analyze(string str);
string char2str(char ch);
string p[100]; //表示产生式
int n_chan; //产生式的个数
int n_VN;
vector<string> VN; //非终结符集,和set_VN存储相同,但向量存储便于随机访问
vector<string> VT; //终结符集
set<string> set_VN; //非终结符集合,和VN存储相同,但集合存储便于查找元素和去重
set<string> set_VT; //终结符集合
string all_left[100]; //产生式所有左部VN
string all_right[100]; //产生式所有右部VN+VN
string S; //开始符号
//map 一对一的key,value对,通过迭代器来实现
map<string,int> First; //VN映射到FIRST集合下标
map<string,int> Follow; //VN映射到FOLLOW集合下标
set<string> SELECT[100];
vector<string> predict[100]; //预测分析表
string kong = "error"; //预测分析表空白处填写字符串
int step = 0; //预测分析过程步骤序数
int main() {
input_G();
Select();
Predict();
cout<<"请输入字符串(输入'-'结束)(如i+i):";
string str;
cin>>str;
while(str[str.length()-1]!='-'){
Analyze(str);
cout<<endl<<"请继续输入字符串(输入'-'结束)(如i+i):";
cin>>str;
}
return 0;
}
///输入合法文法&&分离VT和VN
void input_G(){
string tmp; //用于拼接字符串
string tmp1;
n_chan = 8;
p[0] = "E->TE'";
p[1] = "E'->+TE'";
p[2] = "E'->e";
p[3] = "T->FT'";
p[4] = "T'->*FT'";
p[5] = "T'->e";
p[6] = "F->(E)";
p[7] = "F->i";
///提取非终结符VN的循环
for(int i=0;i<n_chan;i++){
tmp = "";
tmp1 = "";
int j=0;
while(p[i][j]!='-'){
tmp += p[i][j]; //因为非终结符可能是形如E'之类的两个字符,所以需要拼接,遇到箭头就结束
j++;
}
j += 2;
for(;j<(int)p[i].length();j++)
tmp1 += p[i][j];
cout<<p[i]<<endl;
all_left[i] = tmp; //存储每个产生式的左部
all_right[i] = tmp1; //存储每个产生式的右部
set_VN.insert(tmp); //将每行产生式拼接好的非终结符插入到集合中
}
n_VN = set_VN.size(); //读取VN个数
///提取终结符VT的循环
for(int i=0;i<n_chan;i++){ //n_chan是产生式的个数
int chan_len = p[i].length(); //每个产生式的长度
for(int j=0;j<chan_len;j++)
if(p[i][j]=='>'){
tmp = "";
for(int k=j+1;k<chan_len;k++){ //k记录"->"之后的下标
tmp1 = p[i][k];
if(set_VN.count(tmp1)==0){ //count==0表示该字符不在集合中,即∈VT
tmp += p[i][k]; //拼接字符串
tmp1 = p[i][k+1];
if((k==chan_len-1)||((k<chan_len-1)&&set_VN.count(tmp1)==1)){
//判断(已是最后一个字符||后一个字符是VN则之前的字符串是一个VT)
set_VT.insert(tmp);
tmp = "";
}
}
}
break; //本行产生式已扫描结束
}
}
set_VT.erase("'"); //VT集合中删除"'"和"e"符号
//通过集合迭代器,将其中元素转移到向量VN中(因为集合不易随机存取,但可以筛除重复元素)
for(set<string>::iterator it=set_VN.begin();it!=set_VN.end();it++)
VN.push_back(*it);
for(int i=0;i<(int)VN.size();i++){
First[VN[i]] = i; ///每个VN映射到FIRST集合下标
}
for(set<string>::iterator it=set_VT.begin();it!=set_VT.end();it++)
VT.push_back(*it);
for(int i=0;i<(int)VT.size();i++){
if(VT[i]=="e")
Follow["#"] = i;
else
Follow[VT[i]] = i;
}
}
///SELECT集
void Select(){
SELECT[0].insert("(");
SELECT[0].insert("i");
SELECT[1].insert("+");
SELECT[2].insert(")");
SELECT[2].insert("#");
SELECT[3].insert("(");
SELECT[3].insert("i");
SELECT[4].insert("*");
SELECT[5].insert("+");
SELECT[5].insert(")");
SELECT[5].insert("#");
SELECT[6].insert("(");
SELECT[7].insert("i");
}
///预测分析表
void Predict(){
bool flag;
for(int i=0;i<(int)VT.size();i++)
if(VT[i]=="e"){
VT[i] = "#";
break;
}
for(int i=0;i<n_VN;i++){
for(int j=0;j<(int)VT.size();j++){
flag = true;
for(int k=0;k<n_chan;k++)
if(all_left[k]==VN[i]&&SELECT[k].count(VT[j])==1){
predict[i].push_back(all_right[k]);
flag = false;
break;
}
if(flag) predict[i].push_back(kong);
}
}
}
///分析输入字符串
void Analyze(string str){
S = VN[0]; //将VN集中首元素定为开始符号元素
vector<string> analy; //分析向量(用向量代替可以随机访问,便于显示)
vector<string> input; //输入串向量(用向量代替可以随机访问,便于显示)
string fun; //存储在预测分析表中找到的表达式
analy.push_back("#");
analy.push_back(S);
input.push_back("#");
for(int i=(int)str.length()-1;i>=0;i--)
input.push_back(char2str(str[i]));
///**************初始化分割线**************
cout<<endl<<"步骤\t分析栈\t\t剩余输入串\t推导所用产生式"<<endl;
while(true){
cout<<++step<<"\t";
for(int i=0;i<(int)analy.size();i++){
cout<<analy[i];
}
cout<<"\t\t";
for(int i=(int)input.size()-1;i>=0;i--){
cout<<input[i];
}
if(input.back()=="#"&&analy.back()=="#"){ //向量尾元素,模拟栈顶元素
cout<<"\t\t接受";
break;
}else if(input.back()==analy.back()){ //两个栈顶元素同是一个VT则均出栈一个元素
cout<<"\t\t\""<<input.back()<<"\"匹配";
analy.pop_back();
input.pop_back();
}//两个都是VT但不相等,则出错
else if(set_VT.count(input.back())==1&&set_VT.count(analy.back())==1&&input.back()!=analy.back()){
cout<<"出错:两栈顶算符不匹配!"<<endl;
break;
}//分析栈顶元素是VN
else if(set_VN.count(analy.back())==1){
fun = predict[First[analy.back()]][Follow[input.back()]];
if(fun!=kong){ //能在预测分析表中找到表达式
cout<<"\t\t"<<analy.back()<<"->"<<fun;
analy.pop_back(); //模拟出栈
for(int i=(int)fun.length()-1;i>=0;i--){ //倒序入栈
if(fun[i]=='\''){
analy.push_back(char2str(fun[i-1])+char2str(fun[i]));
i--;
}else if(fun[i]=='e') break;
else
analy.push_back(char2str(fun[i]));
}
}else{
cout<<endl<<"出错:未在预测表中找到产生式!"<<endl;
break;
}
}//两个都是VT但不在终结符集中
else if(set_VT.count(input.back())==0){
cout<<endl<<"出错:该字符不属于该文法!"<<endl;
break;
}
cout<<endl;
}
}
///字符转换成字符串函数
string char2str(char ch){
char t[] = {ch,'\0'};
return t;
}