一、实验目的
理解LL语法分析方法的原理,掌握LL语法分析器的构造,设计分析器数据结构和程序结构,加深对自上而下语法分析方法的理解。
二、实验内容
需要实现的功能:
1)构造文法的LL预测分析表;
2)构造LL语法分析器的总控程序;
3)输入文法:文法描述存储在文本文件中(编码格式ANSI),文件名作为命令行参数输入;
4)输入待分析的符号串:符号串存储在文本文件中(编码格式ANSI),文件名作为命令行参数输入。
5)输出文法的LL预测分析表到标准输出设备;
6)输出分析结果:输出待分析符号串是否为该文法正确句子的判断,并输出文本形式的分析过程(标准输出设备)。
三、实验要求
1)文法描述文件和符号串文件的格式参见文档《实验用文件结构.doc》;
2)使用《文法构造实验一》、《文法构造实验二》的结果。
3)文法描述文件和符号串文件是两个不同的文本文件,都作为命令行参数进行输入,文法描述文件名是第1个参数,符号串文件名是第2个参数。
四、实验输入输出样例
描述:
算术表达式文法G =(VN,VT,P,S)其中:
VN = { S, T, T’ }
VT = { a, ^, (, ) ,}
P = { S → a | ^ |(T)
T → ST’
T’ → ,ST’ | e }
S = S
输入(文法描述文件):
3
S T T'
5
a ^ ( ) ,
6
S -> a
S -> ^
S -> ( T )
T -> S T'
T' -> , S T'
T' -> ε
S
输入(符号串文件):
( a , a ) #
输出(标准输出设备):
预测分析表:
a ^ ( ) , #
-----+-----+-----+-----+-----+-----+-----
S p0 p1 p2
T p3 p3 p3
T' p5 p4
-----+-----+-----+-----+-----+-----+-----
(a,a)#分析过程:
初始化:#入栈,S入栈;
01:出栈X=S, 输入c=(,查表,M[X,c]=S->(T),产生式右部逆序入栈;
02:出栈X=(, 输入c=(,匹配,输入指针后移;
03:出栈X=T, 输入c=a,查表,M[X,c]=T->ST’,产生式右部逆序入栈;
04:出栈X=S, 输入c=a,查表,M[X,c]=S->a,产生式右部逆序入栈;
05:出栈X=a, 输入c=a,匹配,输入指针后移;
06:出栈X=T’,输入c=,,查表,M[X,c]=T’->,ST’, 产生式右部逆序入栈;
07:出栈X=,, 输入c=,,匹配,输入指针后移;
08:出栈X=S, 输入c=a,查表,M[X,c]=S->a,产生式右部逆序入栈;
09:出栈X=a, 输入c=a,匹配,输入指针后移;
10:出栈X=T’,输入c=),查表,M[X,c]=T’->e;
11:出栈X=), 输入c=),匹配,输入指针后移;
12:出栈X=#, 输入c=#,匹配,成功。
五、实验报告
语言与环境
语言:c++
语言标准(-std):ISO C++11
编译器:g++
LL语法分析器原理描述
主要步骤:
1计算First集、Follow集和Select集
2构造预测分析表
3实现总控程序
预测分析器工作模型:
总控程序在分析过程中根据栈顶符号 X 和当前输入指针 IP指向的符号a 来决定下一步动作。
每一步都将执行下列三种动作之一:
若X是一个终极符,日X=a=#,则宣布分析成功:
若X是一个终极符,且X=a≠#,则将栈顶符号X(终极符)弹出,让IP 指针指向下一个输入符号;若X是一个非终极符,则查看分析表M。
如果 M[A, a] 中是一条产生式,则先将栈顶符号X(非终极符)弹出,然后把该产生式右端符号串按反序(从右到左)压入栈中(ε串不入栈),同时可能还要执行一系列的动作,比如建立语法树、进行语义计算等;
如果M[A, a]中是出错标志或空白,则执行相应的出错处理程序。
设计模型
采用面向对象的方法;
共设计了5个类,分别为VN,VT,P,S,Table;VN,VT,P,S用来存储文法文件,Table用来实现功能。
主程序调用Table类中函数实现:
table.setFilename1(filename1);//
table.setFilename2(filename2);//
table.fileop1();//读入文法文件
table.fileop2();//读入符号串文件
table.First();//计算First集
table.Follow();//计算Follow集
table.Select();//计算Select集
table.ftbale();//构造预测分析表
table.outputftable();//输出预测分析表
table.analyze();//实现分析过程
在Table类中有如下模块:
系统实现
采用面向对象的方法实现;
主要用到的算法:
1、First集的计算
对每个文法符号X∈VT U VN,构造FIRST(X)。
若X∈VT,则FIRST(X) = {X}。
若X∈VN,且有规则X→a...,a∈VT,则a∈FIRST(X)。
若X∈VN,且有规则X→ε,则ε∈FIRST(X)。
若有规则X →Y1Y2...Yn,对任意的i(1≤i≤n),
当Y1,Y2,...,Yi-1∈ VN且Y1Y2...Yi-1 →ε(即所有的FIRST(Yj) 都含有 ε,1≤j≤i-1),则把 FIRST(Y)中的所有的非ε元素加到 FIRST(X)中;
特别地,若Y1Y2...Yn →ε(即所有的FIRST(Y)) 都含有 ε,1≤j≤n),则ε∈FIRST(X)。
反复使用上面的规则,直到每个FIRST 集不再增大为止。
2、Follow集的计算
对每个非终极符X∈VN,构造 FOLLOW(X)。对文法的开始符号S,置 #于 FOLLOW(S)中;
若有规则A→αBβ,则把FIRST(B)-{ ε} 加到 FOLLOW(B) 中;
若有规则A→αB,或规则A→αBβ且β→ε,即ε∈FIRST(B),则把 FOLLOW(A) 加至 FOLLOW(B) 中。
反复使用上面的规则,直到每个非终结符的FOLLOW 集不再增大为止。
3、Select集的计算
假设A→α是文法G 的任一规则,定义规则A→α的选择集合SELECT 为:
若α→ε,SELECT(A→α)=FIRST(α) -{ ε}U FOLLOW(A)
若α推不出ε,SELECT(A→α)=FIRST(α)
4、预测分析表的构造
构造文法G 的每一个非终结符的FIRST 集和FOLLOW 集
构造分析表M[A,a]
对文法G 的每个规则A→ a,执行第(2)步和第(3)步:
对每个终极符a∈FIRST(a),则置M[A,a] =A→α;
若ε∈FIRST(α),对每个b∈FOLLOW(A),则置M[A , b]=A→α;
把所有无定义的M[A,a] 标上“出错标志”(表中用空白表示)。
5、总控程序实现
BEGIN
首先把#和文法开始符号依次推进STACK栈;
把第一个输入符号读进a;
WHILE FLAG DO
BEGIN
把STACK 栈顶符号弹出放进X中;
IF X ∈ VT THEN
IF X = a THEN 把下一个符号读进a
ELSE ERROR
ELSE IF X = # THEN
IF X = a THEN FLAG := FALSE
ELSE ERROR
ELSE IF M[A ,a] = {X → X1X2...Xk } THEN
把Xk,X-1,...,X压入STACK 栈 /* 若是ε,不压栈*/
ELSE ERROR
END OF WHILE
STOP/* 分析成功,过程结束*/
END
测试数据及测试结果
两个输入文件:
运行结果:
实验总结
问题及解决:
这个实验借助于上几个实验的结果,所以做起来不是那么的吃力。由于在做实验5的时候,我已经对我的文法的数据结构做了优化调整,所以在做这个实验时直接拿来用就行。在一个实验中我已经分别计算得到了First集、Follow集和Select集,所以我根据Select集构造了预测分析表,之后有预测分析表实现分析过程。在构造预测分析表时比较困难的是设计预测分析表的数据结构,我先后尝试了几个,觉得都不合适之后选用的是map<pair<string, string>, int>这样的一个数据结构,很好的解决了问题,但是在输出预测分析表时又遇到了无法对齐以及输出乱码各类的问题,当然这些只是技术上的问题,通过百度就解决了。在最后一步实现分析过程的时候,我遇到了一个令我头大的问题——内存爆炸;实在子想不到为何会遇到这种问题。之后经过一下午的调试,发现是在分析过程中有个对一个vector访问时访问到了内存之外的部分,修改之后程序终于完工。
程序的评价:
在做这个实验时,并没有对我之前的做改动,而是加了两个函数,按照老师的说法,我设计的数据结构还是可以的。我觉得不足的地方是我写的程序有点臃肿,跑起来总感觉有点慢,下一步还可以继续精简一下。
体会和收获:
每次做实验的过程可以帮助我深刻的理解老师讲的知识,即使上课没听懂的,在实验过程中也得搞懂才做的下去。对我个人而言,我喜欢在做实验的过程中去学习巩固理论知识,比如,通过实现分析过程,让我对预测分析法的整个过程有了深刻的认识。而且做编译原理实验让我更加熟练的掌握c++。通过自己思考独立完成一个实验让我很愉悦。
六、代码
#include <fstream>
#include "sstream"
#include <iomanip>
#include <stack>
#include "iostream"
#include "set"
#include "string"
#include "map"
#include "vector"
using namespace std;
#ifndef BIANYI_WENFA_H
#define BIANYI_WENFA_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 Table { //创建一个Table类
private:
string filename1;
string filename2;
map<int, set<string>> select; //select集
map<pair<string, string>, int> M; //预测分析表
vector<string> str; //符号串
VN vn;
VT vt;
P p;
S s;
public:
void setFilename1(const string &filename1) {
Table::filename1 = filename1;
}
void setFilename2(const string &filename2) {
Table::filename2 = filename2;
}
void fileop1() { //读文法文件
string fileline;
string str;
set<string> middle;
vector<string> p1;
vector<string> mid;
vector<vector<string>> p2;
ifstream fout(filename1);
for (int i = 1; getline(fout, fileline); i++) {
if (i == 1) {
vn.setN(atoi(fileline.c_str()));//非终结符个数
} else if (i == 2) { //非终结符集合
istringstream is(fileline.c_str());
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.c_str());
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();
}
}
p.setP1(p1);
p.setP2(p2);
fout.close();
}
void fileop2(){ //读符号串文件
vector<string> hh;
ifstream fou(filename2);
stringstream buffer;
buffer<<fou.rdbuf();
fou.close();
string ss;
string contents(buffer.str());//将文件读入一个字符串
istringstream is(contents);
while (is >> ss) {
hh.push_back(ss);
}
this->str=hh;
}
set<string> First1(string str) { //求单个非终结符First集
set<string> first;
set<string> second; //中间集合
if (vt.getVt().count(str) == 1) { //终结符本身即为其First集
first.insert(str);
return first;
}
for (int i = 0; i < p.getP1().size(); i++) {
if (str == p.getP1()[i]) { //在左部集找到该非终结符
if (vt.getVt().count(p.getP2()[i][0]) == 1 ||
p.getP2()[i][0] == "ε") { //如果产生式右部第一个字符为终结符或ε,则直接加入其First集
first.insert(p.getP2()[i][0]);
} else if (vn.getVn().count(p.getP2()[i][0]) == 1) { //右部第一个为非终结符
for (int j = 0; j < p.getP2()[i].size(); j++) { //产生式右部遍历
if (vt.getVt().count(p.getP2()[i][j]) == 1) { //如果是终结符,加入first集,结束遍历
first.insert(p.getP2()[i][j]);
break;
} else if (vn.getVn().count(p.getP2()[i][j]) == 1 && j != p.getP2()[i].size() - 1) { //如果是非终结符
second = First1(p.getP2()[i][j]); //递归调用
if (second.count("ε") == 0) {
first.insert(second.begin(), second.end());
second.clear();
break;
} else {
second.erase("ε");
first.insert(second.begin(), second.end());
second.clear();
}
} else if (vn.getVn().count(p.getP2()[i][j]) == 1 && j == p.getP2()[i].size() - 1) {
second = First1(p.getP2()[i][j]);
first.insert(second.begin(), second.end());
second.clear();
}
}
}
}
}
return first;
}
void First() { //求每一个非终结符First集
map<string, set<string>> first;
for (set<string>::iterator it = vn.getVn().begin(); it != vn.getVn().end(); it++) {
first.emplace(*it, First1(*it));
}
}
set<string> Follow1(string str) { //求单个非终结符的Follow集
set<string> follow;
set<string> second;
if (str == s.getS()) {
follow.insert("#"); //若是开始符,则将#加入
}
for (int i = 0; i < p.getP2().size(); i++) { //遍历产生式右部
for (int j = 0; j < p.getP2()[i].size(); j++) {
if (str == p.getP2()[i][j]) {
if (j == p.getP2()[i].size() - 1) { //若其后无元素,则将产生式左部follow集加入
if (p.getP1()[i] != str) {
second = Follow1(p.getP1()[i]); //递归调用
follow.insert(second.begin(), second.end());
second.clear();
}
} else {
if (vn.getVn().count(p.getP2()[i][j + 1]) == 1) {
for (int k = j + 1; k < p.getP2()[i].size(); k++) {
second = First1(p.getP2()[i][k]);
if (second.count("ε") == 0) {
follow.insert(second.begin(), second.end());
second.clear();
break;
} else {
if (k == p.getP2()[i].size() - 1) {
second.erase("ε");
follow.insert(second.begin(), second.end());
second.clear();
if (p.getP1()[i] != p.getP2()[i][k]) {
second = Follow1(p.getP1()[i]); //递归调用
follow.insert(second.begin(), second.end());
second.clear();
}
} else {
second.erase("ε");
follow.insert(second.begin(), second.end());
second.clear();
}
}
}
} else if (vt.getVt().count(p.getP2()[i][j + 1])) {
follow.insert(p.getP2()[i][j + 1]);
break;
}
}
}
}
}
return follow;
}
void Follow() { //求所有非终结符的follow
map<string, set<string>> follow;
for (set<string>::iterator it = vn.getVn().begin(); it != vn.getVn().end(); it++) {
follow.emplace(*it, Follow1(*it));
}
}
void Select() { //Select集
set<string> second;
set<string> sle;
for (int i = 0; i < p.getP2().size(); i++) { //遍历产生式右部
if (p.getP2()[i][0] == "ε") { //若为空串,则将左部follow加入
second = Follow1(p.getP1()[i]);
sle.insert(second.begin(), second.end());
second.clear();
select.emplace(i, sle);
sle.clear();
continue;
}
for (int j = 0; j < p.getP2()[i].size(); j++) { // 计算产生式右部first集
second = First1(p.getP2()[i][j]);
if (second.count("ε") == 0) {
sle.insert(second.begin(), second.end());
second.clear();
break;
} else {
if (j == p.getP2()[i].size() - 1) {
sle.insert(Follow1(p.getP1()[i]).begin(), Follow1(p.getP1()[i]).end());
}
second.erase("ε");
sle.insert(second.begin(), second.end());
second.clear();
}
}
select.emplace(i, sle);
sle.clear();
}
this->select = select;
}
void ftbale() { //求预测分析表
map<pair<string, string>, int> M;
pair<string, string> m;
for (int i = 0; i < select.size(); i++) {
for (set<string>::iterator it = select[i].begin(); it != select[i].end(); it++) {
m.first = p.getP1()[i];
m.second = *it;
M.emplace(m, i);
}
}
this->M = M;
}
void outputftable() { //输出预测分析表
vector<vector<string>> fta(vn.getN() + 1, vector<string>(vt.getN() + 2));
pair<string, string> mid;
int i = 1, j = 1;
fta[0][0] = " ";
for (set<string>::iterator it = vn.getVn().begin(); it != vn.getVn().end(); it++) {
fta[i][0] = *it;
i++;
}
for (set<string>::iterator it = vt.getVt().begin(); it != vt.getVt().end(); it++) {
fta[0][j] = *it;
j++;
}
fta[0][fta[0].size() - 1] = "#";
for (int i = 1; i < fta.size(); i++) {
for (int j = 1; j < fta[i].size(); j++) {
mid.first = fta[i][0];
mid.second = fta[0][j];
if (M.count(mid) == 1)fta[i][j] = to_string(M[mid]);
else
fta[i][j] = "-1";
}
}
cout << "[ftable]" << endl;
for (int i = 0; i < fta.size(); i++) {
for (int j = 0; j < fta[i].size(); j++) {
if(i==0)cout<<left<<setw(10)<<fta[i][j];
else if(j==0)cout<<left<<setw(10)<<fta[i][j];
else if (fta[i][j] == "-1")cout<<left<<setw(10)<<" ";
else {
int k = atoi(fta[i][j].c_str());
string vv;
vv=p.getP1()[k]+"->";
for (int l = 0; l < p.getP2()[k].size(); l++) {
vv+=p.getP2()[k][l];
}
cout<<left<<setw(10)<<vv;
}
}
cout << endl;
}
}
bool isin(string X,string a){ //查预测分析表
pair<string,string> midp;
midp.first=X;
midp.second=a;
if(M.count(midp)==1 ){
return true;
}
return false;
}
void analyze(){ //LL(1)分析过程
stack<string> stk;
string a;
string X;
stk.push("#");
stk.push(s.getS());
a=str[0];
int i=1,falg=1;
int j=1;
cout<<"初始化:#入栈,S入栈;"<<endl;
while(falg){
X=stk.top();
stk.pop();
if(vt.getVt().count(X)==1){
if(X==a){
cout<<j<<":出栈X="<<X<<",输入a="<<a<<",匹配,"<<"输入指针后移;"<<endl;j++;
a=str[i];i++;
}
else{
falg=0;
cout<<"失败";
}
} else if(X=="#"){
if(X==a){
cout<<j<<":出栈X="<<X<<",输入a="<<a<<",匹配,成功。"<<endl;j++;
falg=0;
}
else{
falg=0;
cout<<"失败";
}
} else if(isin(X,a)){
pair<string,string> midp;
midp.first=X;
midp.second=a;
int k=M[midp];
if(p.getP2()[k][0]=="ε"){
cout<<j<<":出栈X="<<X<<",输入a="<<a<<",查表,M[X,a]="<<X<<"->ε"<<"产生式右部逆序入栈;"<<endl;j++;
continue;
}
cout<<j<<":出栈X="<<X<<",输入a="<<a<<",查表,M[X,a]="<<X<<"->";
for(int i=0;i<p.getP2()[k].size();i++){
cout<<p.getP2()[k][i];
}
cout<<"产生式右部逆序入栈;"<<endl;j++;
for(int i=p.getP2()[k].size()-1;i>=0;i--){
stk.push(p.getP2()[k][i]);
}
}
else{
falg=0;
cout<<"失败";
}
}
}
};
#endif //BIANYI_WENFA_H
int main(){
string filename1;
string filename2;
Table table;
cout<<"Please input file(absolute path; eg: D://filename.txt):"<<endl; //E://by//exp6.txt
cin>>filename1;
cout<<"Please input file(absolute path; eg: D://filename.txt):"<<endl; //E://by//exp6.1.txt
cin>>filename2;
table.setFilename1(filename1);
table.setFilename2(filename2);
table.fileop1();
table.fileop2();
table.First();
table.Follow();
table.Select();
table.ftbale();
table.outputftable();
table.analyze();
return 0;
}