一、实验目的
理解 LR 语法分析方法的原理,设计相关数据结构和程序结构, 加深对自下而上语法分析方法的理解。
二、问题描述
需要实现的功能:
(1)输入文法:文法描述存储在文本文件中,文件名作为命令行 参数输入;
(2)输入文法的分析表(Action 表和 Goto 表):分析表数据存储 在文本文件中,文件名作为命令行参数输入;
(3)输入待分析的符号串:符号串存储在文本文件中,文件名 作为命令行参数输入。
(4)构造 LR 语法分析器的总控程序;
(5)对待分析符号串,输出其是否该文法正确句子的判断,并 输出文本形式的分析过程(标准输出设备)。
实现原理:
LR 方法的基本思想: 在归约过程中 记住“历史”:记住已移进和归约出的整个符号串; 展望“未来”:根据使用的规则预测未来可能遇到的输入符号 。根据记住的“历史”、展望的“未来”和“现实”的输入符号 三方面材料,来确定现在的栈顶符号串是否已经形成相对于某 个规则的句柄。
三、软件设计方法的选择
1、选用面向对象的软件设计方法;
2、开发语言:C++;
3、语言标准(-std):ISO C++11
4、编译器:g++;
5、开发环境:clion 集成开发环境
四、测试数据与测试效果
将如下字符输入到一个txt文件里,程序以文件方式读取
五、代码
#include "iostream"
#include <vector>
#include <fstream>
#include <sstream>
#include <map>
#include <stack>
#include "string"
#include "set"
using namespace std;
#ifndef BIANYI_LRALL_H
#define BIANYI_LRALL_H
class VN{ //非终结符集
private:
int n;
set<string> vn;
public:
int getN() const {
return n;
}
const set<string> &getVn() const {
return vn;
}
void setVn(const set<string> &vn) {
VN::vn = vn;
}
void setN(int n) {
VN::n = n;
}
};
class VT{ //终结符集
private:
int n;
set<string> vt;
public:
int getN() const {
return n;
}
void setN(int n) {
VT::n = n;
}
const set<string> &getVt() const {
return vt;
}
void setVt(const set<string> &vt) {
VT::vt = vt;
}
};
class P{ //产生式
private:
int n;
vector<string> p1; //存放左部
vector<vector<string>> p2; //存放右部
public:
const vector<vector<string>> &getP2() const {
return p2;
}
void setP2(const vector<vector<string>> &p2) {
P::p2 = p2;
}
const vector<string> &getP1() const {
return p1;
}
void setP1(const vector<string> &p1) {
P::p1 = p1;
}
int getN() const {
return n;
}
void setN(int n) {
P::n = n;
}
};
class S{ //开始符号
private:
string s;
public:
const string &getS() const {
return s;
}
void setS(const string &s) {
S::s = s;
}
};
class CFG{
public:
string filewenfa; //文法文件
VN vn;
VT vt;
P p;
S s;
public:
void setFilewenfa(const string &filewenfa) {
CFG::filewenfa = filewenfa;
}
void fileop() { //读文件
string fileline;
string str;
set<string> middle;
vector<string> p1;
vector<string> mid;
vector<vector<string>> p2;
ifstream fout(filewenfa);
for (int i = 1; getline(fout, fileline); i++) {
if (i == 1) {
vn.setN(atoi(fileline.c_str()));//非终结符个数
} else if (i == 2) { //非终结符集合
istringstream is(fileline);
while (is >> str) {
middle.insert(str);
}
vn.setVn(middle);
middle.clear();
} else if (i == 3) { //终结符个数
vt.setN(atoi(fileline.c_str()));
} else if (i == 4) { //终结符集合
istringstream is(fileline);
while (is >> str) {
middle.insert(str);
}
vt.setVt(middle);
middle.clear();
} else if (i == 5) { //规则个数
p.setN(atoi(fileline.c_str()));
} else if (i == p.getN() + 6) { //指定开始符
s.setS(fileline.c_str());
} else {
istringstream is(fileline);
for (int j = 0; is >> str; j++) {
if (j == 0) {
p1.push_back(str);
} else if (j == 1);
else {
mid.push_back(str);
}
}
p2.push_back(mid);
mid.clear();
}
}
fout.close();
p.setP1(p1);
p.setP2(p2);
}
};
class Program{
private:
string filewenfa; //文法文件
string fileAG; //Actiongto表
string filestr; //待分析串
CFG cfg;
vector<string> anstr; //储存待分析串
map<pair<int,int>,string> action; //action表
map<pair<int,int>,int> Goto; //goto表
map<string,int> X; //终结符编号
map<string,int> Y; //非终结符编号
public:
void setFileAg(const string &fileAg) {
fileAG = fileAg;
}
void setFilestr(const string &filestr) {
Program::filestr = filestr;
}
void setFilewenfa(const string &filewenfa) {
Program::filewenfa = filewenfa;
}
void fileAGop(){ //读分析表
int num;
string str;
pair<int,int> midp;
ifstream fout(this->fileAG);
string fileline;
for (int i = 1; getline(fout, fileline); i++) {
if (i == 1) {
num=atoi(fileline.c_str());
continue;
} else if(i!=1&&i<=num+1){
istringstream is(fileline);
is>>str;
midp.first=atoi(str.c_str());
str.clear();
is>>str;
midp.second=atoi(str.c_str());
str.clear();
is>>str;
this->action.emplace(midp,str);
str.clear();
} else if(i==num+2){
} else{
istringstream is(fileline);
is>>str;
midp.first=atoi(str.c_str());
str.clear();
is>>str;
midp.second=atoi(str.c_str());
str.clear();
is>>str;
this->Goto.emplace(midp,atoi(str.c_str()));
str.clear();
}
}
fout.close();
}
void filestrop(){ //读待分析串
ifstream fout(this->filestr);
stringstream buffer;
buffer<<fout.rdbuf();
fout.close();
string ss;
string contents(buffer.str());//将文件读入一个字符串
istringstream is(contents);
while (is >> ss) {
this->anstr.push_back(ss);
}
}
void initstr(){ //按分析表给非终结符和终结符编号
int i=0;
for(set<string>::iterator it=cfg.vt.getVt().begin();it!=cfg.vt.getVt().end();it++){
this->X.emplace(*it,i);
i++;
}
this->X.emplace("#",i);
i=0;
for(set<string>::iterator it=cfg.vn.getVn().begin();it!=cfg.vn.getVn().end();it++){
if(*it!=cfg.s.getS())this->Y.emplace(*it,i);
i++;
}
}
void fileop(){ //调用所有读文件操作
this->cfg.setFilewenfa(filewenfa);
this->cfg.fileop();
this->filestrop();
this->fileAGop();
initstr();
}
void allprogram(){
string sstr; // 中间字符串
string a; //待入栈符号
stack<int> state; //状态栈
stack<string> str; //符号栈
state.push(0); //初始状态入栈
str.push("#"); //结束符入符号栈
int k=1;
pair<int,int> midp; //中间pair
midp.first=state.top();
a=anstr[0];
midp.second= X[a];
cout<<"栈顶 "<<"输入 "<<"查表 "<<"动作 "<<endl;
while (action[midp]!="acc"){
cout<<state.top()<<" "<<str.top()<<" "<<a<<" ";
if(action[midp][0]=='s'){ //移进
sstr=action[midp];
sstr.erase(0,1);
state.push(atoi(sstr.c_str()));
cout<<action[midp]<<" "<<"进栈"<<" "<<atoi(sstr.c_str())<<" "<<a<<endl;
sstr.clear();
str.push(a);
a=anstr[k];
k++;
} else if(action[midp][0]=='r'){ //归约
sstr=action[midp];
sstr.erase(0,1);
int n=atoi(sstr.c_str()); //按照第n条规则归约
for(int i=0;i<cfg.p.getP2()[n].size();i++){ //n个状态和符号出栈
state.pop();
str.pop();
}
str.push(cfg.p.getP1()[n]); //归约的非终结符入栈
cout<<action[midp]<<" "<<"出栈"<<cfg.p.getP2()[n].size()<<"个符号和状态";
midp.first=state.top();
midp.second=Y[cfg.p.getP1()[n]];
if(Goto.count(midp)==1){ //查goto表状态转移
state.push(Goto[midp]);
}
cout<<"进栈"<<state.top()<<" "<<str.top()<<" ";
cout<<cfg.p.getP1()[n]<<"->";
for(int i=0;i<cfg.p.getP2()[n].size();i++){
cout<<cfg.p.getP2()[n][i];
}
cout<<endl;
} else{ //出错处理
cout<<"error!"<<endl;
break;
}
midp.first=state.top();
midp.second=X[a];
if(action[midp]=="acc"){
cout<<state.top()<<" "<<str.top()<<" "<<a<<" "<<action[midp]<<" "<<"成功接受!";
}
}
}
};
#endif //BIANYI_LRALL_H
int main(){
string filewenfa;
string fileAG;
string filestr;
cout<<"Please input file(absolute path; eg: D://filename.txt):"<<endl;
cin>>filewenfa;
cout<<"Please input file(absolute path; eg: D://filename.txt):"<<endl;
cin>>fileAG;
cout<<"Please input file(absolute path; eg: D://filename.txt):"<<endl;
cin>>filestr;
Program program;
program.setFilewenfa(filewenfa);
program.setFileAg(fileAG);
program.setFilestr(filestr);
program.fileop(); //读文件
program.allprogram(); //总控分析
return 0;
}