题目
一、实验目的
通过本次实验,加深对语法分析的理解,学会编制语法分析器。
二、实验任务
用C或C++语言编写一门语言的语法分析器。
三、实验内容
(1)语言确定:C-语言,其定义在《编译原理及实践》附录A中。也可选择其它语言,不过要有该语言的详细定义(可仿照C-语言)。一旦选定,不能更改,因为要在以后继续实现编译器的其它部分。鼓励自己定义一门语言。也可选择TINY语言,但需要使用与TINY现有语法分析代码不同的分析算法实现,并在实验报告中写清原理。
(2)完成C-语言的BNF文法到EBNF文法的转换。通过这一转换,消除左递归,提取左公因子,将文法改写为LL(1)文法,以适用于自顶向下的语法分析。规划需要将哪些非终结符写成递归下降函数。
(3)为每一个将要写成递归下降函数的非终结符,如:变量声明、函数声明、语句序列、语句、表达式等,定义其抽象语法子树的形式结构,然后定义C-语言的语法树的数据结构。
(4)仿照前面学习的语法分析器,编写选定语言的语法分析器。可以自行选择使用递归下降、LL(0)、LR(0)、SLR、LR(1)中的任意一种方法实现。
(5)准备2~3个测试用例,测试并解释程序的运行结果。
分析及代码
此时实验加深了对LL1算法的理解,以及实验过程中对输出格式的对齐和制表符的使用有个更加深刻的认识。
算法设计:
首先是定义一个结构体用来表示放在预测分析表中的产生式便于查找
引用文本
struct Grid{
int n; //产生式右侧符号的数量
int pn; //产生式的编号
string x[maxn]; //产生式右边的符号
Grid() { n = 0; pn = -1; }//对结构体进行初始化
Grid(int n, int pn): n(n), pn(pn) {}
};
为将代码进行分离,防止变量不同.cpp文件的引用重复定义,所以采用类的形式来定义成员行数及其变量,这样在后面的引用中得到的都只是一个类的对象而不会有重复引用的问题
算法主要涉及的函数:
void printTable(); //打印预测分析表
void printQueue(queue);//打印剩余符号串
void printStack(stack);//打印符号栈
void printStep(int, int);//打印选择产生式
void init();//对开始符号栈,符号串队列的初始化
//符号栈,剩余字符串队列都需要设置为LL1的私有变量
queue ready, done;//分别用两个队列来保存剩余字符串和已经匹配成功的字符串
stack st;//符号栈
string production[maxn]; //用一个字符创数组production[]保存产生式
Grid predictTable[maxn][maxn]; //预测分析表第一维非终结符,第二维终结符
map<string, int> t2i; //用map集合来随时读取终结符转下标
map<string, int> n2i; //用map集合来随时读取非终结符转下标
void getTable(string); //读取文法产生式,终结符,非终结符,构造预测分析表
void getCode(string);//读取Tinty语言代码文件
void analyse(string, string);//进行LL(1) 语法解析
主要代码:
#include <bits/stdc++.h>
#include "PARSE.h"
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
LL1::LL1()
{
for (int i = 0; i < maxn; ++i)
production[i] = "NULL";
for (int i = 0; i < maxn; ++i)
for (int j = 0; j < maxn; ++j)
predictTable[i][j] = Grid();
}
void LL1::getTable(string FILE)
{
ifstream infile;
infile.open(&FILE[0]);
string s;
infile >> pdnum; //34锟斤拷锟斤拷锟斤拷式
infile.get();
for (int i = 0; i < pdnum; ++i)
getline(infile, production[i]);
infile >> ternum; //21锟斤拷锟角猴拷(8锟斤拷锟斤拷锟斤拷锟斤拷,10锟斤拷锟斤拷锟斤拷锟斤拷锟?3锟斤拷锟斤拷锟斤拷(锟斤拷1锟斤拷锟斤拷锟斤拷NUM,1锟斤拷锟斤拷识锟斤拷ID,1锟斤拷锟侥硷拷锟斤拷锟斤拷锟斤拷ENDFILE))
for (int i = 0; i < ternum; ++i)
{
infile >> s;
t2i[s] = i;
}
infile >> ntnum; //20锟斤拷锟斤拷锟秸斤拷锟?
for (int i = 0; i < ntnum; ++i)
{
infile >> s;
n2i[s] = i;
}
for (int i = 0; i < pdnum; ++i)
{
int ntn, n, t; //锟斤拷锟秸斤拷锟斤拷锟斤拷, 锟斤拷锟斤拷式锟揭诧拷锟侥凤拷锟脚革拷锟斤拷, 锟斤拷锟斤拷式锟斤拷SELECT锟斤拷
string ss[maxn]; //锟斤拷前锟斤拷锟斤拷式锟揭诧拷锟侥凤拷锟斤拷
infile >> ntn >> n;
for (int j = 0; j < n; ++j)
infile >> ss[j];
infile >> t;
for (int j = 0; j < t; ++j)
{
infile >> s;
predictTable[ntn][t2i[s]] = Grid(n, i); //锟斤拷锟斤拷锟斤拷目
for (int k = 0; k < n; ++k)
predictTable[ntn][t2i[s]].x[k] = ss[k];
}
}
infile.close();
}
void LL1::printTable()
{
cout << "Production: " << endl;
for (int i = 0; i < pdnum; ++i)
cout << production[i] << endl;
cout << endl;
for (int i = 0; i < ntnum; ++i)
{
cout << i << ": ";
for (int j = 0; j < ternum; ++j)
{
if (!predictTable[i][j].n)
cout << "E\t";
else
{
//cout << predictTable[i][j].n << ',' << predictTable[i][j].pn;
for (int k = 0; k < predictTable[i][j].n; ++k)
cout << predictTable[i][j].x[k] << ' ';
cout << '\t';
}
}
cout << endl;
}
}
void LL1::printQueue(queue<string> q)
{
while (!q.empty())
{
cout << q.front() << ' ';
q.pop();
}
}
void LL1::printStack(stack<string> s)
{
while (!s.empty())
{
cout << s.top() << ' ';
s.pop();
}
}
void LL1::printStep(int i, int pi) //锟斤拷印锟叫间步锟斤拷
{
cout << "STEP #" << i << endl;
cout << "DONE: ";
printQueue(done);
cout << endl;
cout << "STACK: ";
printStack(st);
cout << endl;
cout << "READY: ";
printQueue(ready);
cout << endl;
cout << "ACTION: ";
if (~pi)
cout << production[pi] << endl
<< endl;
}
void LL1::getCode(string FILE)
{
freopen(&FILE[0], "r", stdin);
TokenType token;
while (!EOFFlag && (token = getToken()) != ERROR)
{
ready.push(getTokenString(token));
}
}
void LL1::init()
{
while (!ready.empty())
ready.pop();
while (!done.empty())
done.pop();
while (!st.empty())
st.pop();
}
void LL1::analyse(string TABLE, string CODE)
{
init();
getTable(TABLE);
getCode(CODE);
st.push("ENDFILE"); //锟斤拷#压栈
st.push("program"); //锟斤拷锟斤拷始锟斤拷锟斤拷压栈
printStep(0, -1); //锟斤拷印锟斤拷始状态
cout << endl
<< endl;
int cnt = 1;
string X;
while ((X = st.top()) != "ENDFILE") //锟斤拷锟斤拷锟斤拷锟秸伙拷锟?
{
string a = ready.front(); //锟斤拷前锟斤拷锟斤拷
if (X == a) //锟斤拷锟斤拷锟斤拷
{
done.push(a); //锟斤拷锟斤拷前锟斤拷锟斤拷诺锟斤拷锟狡ワ拷锟斤拷锟斤拷
ready.pop(); //锟斤拷锟斤拷
st.pop(); //匹锟斤拷晒锟斤拷锟斤拷锟秸?
printStep(cnt++, -1);
cout << "Match " << X << endl
<< endl; //锟斤拷印匹锟戒动锟斤拷
}
else if (t2i.count(X)) //锟斤拷锟絏 != a锟斤拷锟斤拷X锟角革拷锟秸斤拷锟斤拷锟剿碉拷锟绞э拷锟?
{
cout << "ERROR1" << endl;
break;
}
else if (!predictTable[n2i[X]][t2i[a]].n) //锟斤拷锟皆わ拷锟斤拷锟斤拷锟斤拷锟矫达拷为锟秸o拷锟斤拷说锟斤拷ERROR
{
cout << "ERROR2" << endl;
break;
}
else //锟斤拷锟皆わ拷锟斤拷锟斤拷锟斤拷锟矫达拷锟斤拷为锟秸o拷说锟斤拷锟斤拷锟斤拷状态转锟斤拷
{
st.pop(); //锟饺斤拷锟斤拷前状态锟斤拷栈
for (int i = predictTable[n2i[X]][t2i[a]].n - 1; i >= 0; --i) //锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷栈
{
string stoken = predictTable[n2i[X]][t2i[a]].x[i];
if (stoken != "$") //锟斤拷锟斤拷强锟斤拷志筒锟斤拷锟秸伙拷锟?
st.push(predictTable[n2i[X]][t2i[a]].x[i]);
}
printStep(cnt++, predictTable[n2i[X]][t2i[a]].pn);
}
}
}
结果
输入文件:
LL1分析过程以及输出文件: