实验目的与内容
实验目的
- 理解语法分析的设计方法与实现原理;
- 熟悉算符优先文法的基本概念,包括算符优先矩阵、最左素短语、优先函数等;
- 掌握算符优先文法句型最左素短语的确定方法。
- 理解算符优先分析算法的实现过程。
实验内容
实验旨在深入学习语法分析的理论与实践,特别关注算符优先文法。通过专题1词法分析程序的输出,实现算符优先分析算法,包括构造优先关系矩阵或优先函数、判断算术表达式是否符合文法定义、检测输入错误。实验要求设计至少四个测试用例,测试结果要尽可能完备。选做部分涉及根据文法构造算符优先关系矩阵,并将其整合到分析程序中。整体目的是理论与实践相结合,加深对语法分析方法和算符优先文法的理解。
文法如下:
G[E]:E→E+T∣E-T∣T
T→T*F∣T/F∣F
F→(E)∣i
实验要求
设计说明
终结符号i为用户定义的简单变量,即标识符的定义。重点解决算符优先矩阵的构造方法和算符优先算法的实现。
设计要求
- 构造该算符优先文法的优先关系矩阵或优先函数;
- 输入串应是词法分析的输出二元式序列,即某算术表达式“专题1”的输出结果。输出为输入串是否为该文法定义的算术表达式的判断结果。
- 算符优先分析过程应能发现输入串出错。
- 设计至少四个测试用例(尽可能完备,正确和出错),并给出测试结果;
- 选做:考虑根据算符优先文法构造算符优先关系矩阵,包括FIRSTVT和LASTVT集合,并添加到你的算符优先分析程序中。
实验环境和工具
实验背景与环境
在实验一中,我们已经完成了对输入程序的词法分析,得到了对应的二元式文件。现在的任务是设计一个语法分析器,通过算符优先分析的方式判断输入的程序语句是否符合给定的文法规则。
语言选择与编译器:C++/g++
编码格式:GBK
IDE
Visual Studio Code(VSC)作为一款开源、轻量级、跨平台的代码编辑器,具有一系列优势,因此成为许多开发者的首选之一。使用VSC的原因如下:
- 跨平台支持: Visual Studio Code 支持 Windows、macOS 和 Linux 操作系统,使得开发者可以在不同平台上保持一致的开发环境,提高跨平台开发的效率。
- 轻量高效: VSC 是一款轻量级的代码编辑器,启动速度快,占用系统资源少。
- 强大的扩展生态系统: VSC具有丰富的扩展插件,开发者可以根据自己的需要选择并安装扩展,定制化编辑器,使其满足特定的开发需求。这也促使了VSC的生态系统的不断发展和壮大。
- 语言支持广泛: VSC支持多种编程语言,并且对不同语言的语法高亮、自动补全等功能提供了良好的支持。这使得它适用于多种开发场景,从前端到后端,从移动端到云端。
- 强大的调试功能: VSC提供了强大的调试工具,支持多种语言的调试,并且能够与多种调试器集成,使得开发者能够更轻松地进行代码调试。
实验过程
实验步骤
- 定义算数表达式文法:
制定算符优先文法,实质上描述了算数表达式的计算方式。
- 构建firstVT()和lastVT()集合:
开发算法计算firstVT()和lastVT()集合,用于确定文法中非终结符号的FIRST集合和LAST集合。
- 构建优先符号表:
创建并完善优先符号表,包括操作符和终结符之间的优先关系。
- 构建词法分析程序::
设计并实现词法分析程序,将输入的算数表达式转化为二元式序列。
- 编写main函数:
开发主函数,接收用户输入的算术表达式,利用算符优先文法进行判别。
- 算法主体思想:
- 利用栈存储已读取的输入符号,通过优先关系指导移动和归约语法分析器的动作。
- 如果栈顶终结符与下一个输入符之间的优先关系是 < 或 =,表示尚未发现句柄的右端,语法分析器移动。
- 如果是 > 关系,触发归约操作,表示找到句柄右端。
- 算法执行步骤:
- 初始化输入栈和符号栈。
- 读入第一个输入符号。
- 若栈顶终结符和下一个输入符之间的优先关系是 < 或 =,移动,读入下一个符号。
- 若关系是 >,触发归约操作,将符合文法规则的部分替换为相应的非终结符。
- 重复步骤3-4,直至栈中只有文法的开始符号且输入串被完全处理。
- 若最终栈中只有文法的开始符号,且输入串被完全处理,则算术表达式符合文法定义。
完整代码
#include <iostream>
#include <fstream>
#include <set>
#include <map>
#include <vector>
#include <string>
#include<cstring>
using namespace std;
set<string> Vt; // 终结符集合
set<string> Vn; // 非终结符集合
map<string, set<string>> FirstVt; // FIRSTVT集合
map<string, set<string>> LastVt; // LASTVY集合
map<string,int>VtIndex; //分析表中Vt的下标
vector<vector<string>> table; // LL(1)分析表
// 定义文法规则(拓广文法)
map<string, vector<string>> grammar = {
{"S", {"#E#"}},
{"E", {"E+T","E-T","T"}},
{"T", {"T*F","T/F","F"}},
{"F",{"(E)","i"}}
};
// 判断是否是非终结符
bool isVn(const string& symbol) {
return Vn.count(symbol);
}
// 判断是否是终结符
bool isVt(const string& symbol) {
return Vt.count(symbol);
}
// 计算 FirstVT 集
map<string, set<string>> calculateFirstVtSet()
{
map<string, set<string>> firstSet;
// 初始化非终结符的 First 集
for (map<string,vector<string>>::iterator entry=grammar.begin();entry!=grammar.end();entry++)
{
firstSet[entry->first] = {};
for(vector<string>::iterator production = entry->second.begin(); production!=entry->second.end(); production++) //若每个右部第一个或第二个字母是终结符则直接加入
{
string s = *production;
if(isVt(s.substr(0,1)))
firstSet[entry->first].insert(s.substr(0,1));
else if(isVt(s.substr(1,1)))
firstSet[entry->first].insert(s.substr(1,1));
}
}
bool changes = true;
while (changes)
{
changes = false;
for (map<string,vector<string>>::iterator entry=grammar.begin();entry!=grammar.end();entry++)
{
string nonTerminal = entry->first;
int originalSize = firstSet[nonTerminal].size();
for (vector<string>::iterator it = entry->second.begin(); it!=entry->second.end(); it++)
{
string production = *it; // 一个产生式右部
string vn = production.substr(0,1);
if(isVn(vn))// 如果是非终结符
{
for(set<string>::iterator fi = firstSet[vn].begin(); fi!= firstSet[vn].end();fi++)
firstSet[nonTerminal].insert(*fi);
}
}
if (firstSet[nonTerminal].size() != originalSize) //若first改变,则继续迭代
{
changes = true;
}
}
}
return firstSet;
}
// 计算 LastVt 集
map<string, set<string>> calculateLastVtSet()
{
map<string, set<string>> LastSet;
// 初始化非终结符的 LastVt 集
for (map<string,vector<string>>::iterator entry=grammar.begin();entry!=grammar.end();entry++)
{
LastSet[entry->first] = {};
for(vector<string>::iterator production = entry->second.begin(); production!=entry->second.end(); production++) //若每个右部第一个或第二个字母是终结符则直接加入
{
string s = *production;
int len = s.length() - 1;
if(isVt(s.substr(len,1)))
LastSet[entry->first].insert(s.substr(len,1));
else if(len>1&&isVt(s.substr(len-1,1)))
LastSet[entry->first].insert(s.substr(len-1,1));
}
}
bool changes = true;
while (changes)
{
changes = false;
for (map<string,vector<string>>::iterator entry=grammar.begin();entry!=grammar.end();entry++)
{
string nonTerminal = entry->first;
int originalSize = LastSet[nonTerminal].size();
for (vector<string>::iterator it = entry->second.begin(); it!=entry->second.end(); it++)
{
string production = *it; // 一个产生式右部
string vn = production.substr(production.length()-1,1);
if(isVn(vn))// 如果是非终结符
{
for(set<string>::iterator fi = LastSet[vn].begin(); fi!= LastSet[vn].end();fi++)
LastSet[nonTerminal].insert(*fi);
}
}
if (LastSet[nonTerminal].size() != originalSize) //若first改变,则继续迭代
{
changes = true;
}
}
}
return LastSet;
}
// 构造OPM
void parseTable()
{
for (map<string, vector<string>>::iterator it = grammar.begin(); it != grammar.end(); it++)
{
for (vector<string>::iterator tmp = it->second.begin(); tmp != it->second.end(); tmp++)
{
string str = *tmp; // 右部
for(int i=0;i+1<str.size();i++)
{
if(i+2<str.size())
{
if(isVt(str.substr(i,1))&&isVn(str.substr(i+1,1))&&isVt(str.substr(i+2,1)))
table[VtIndex[str.substr(i,1)]][VtIndex[str.substr(i+2,1)]] = "=";
}
if(isVt(str.substr(i,1)))
{
if(isVt(str.substr(i+1,1)))
table[VtIndex[str.substr(i,1)]][VtIndex[str.substr(i+1,1)]] = "=";
else if(isVn(str.substr(i+1,1)))
{
for(set<string>::iterator fi = FirstVt[str.substr(i+1,1)].begin();fi!=FirstVt[str.substr(i+1,1)].end();fi++)
table[VtIndex[str.substr(i,1)]][VtIndex[*fi]] = "<";
}
}
else if(isVn(str.substr(i,1)))
{
if(isVt(str.substr(i+1,1)))
{
for(set<string>::iterator la = LastVt[str.substr(i,1)].begin();la!=LastVt[str.substr(i,1)].end();la++)
table[VtIndex[*la]][VtIndex[str.substr(i+1,1)]] = ">";
}
}
}
}
}
}
// 读取二元式文件并进行OPT分析
void analyzeExpression(const char* expressionFile)
{
cout<<"读取文件可知输入语句:"<<endl;
FILE *fp;
char buf[1024];
string shizi,like;
if((fp = fopen(expressionFile,"r"))!=NULL)
{
while(fgets(buf,1024,fp) != NULL)
{
int len = strlen(buf);
buf[len-1] = '\0'; /*去掉换行符*/
printf("%s \n",buf);
if(buf[2]=='0') // 说明为标识符
{
like += 'i';
}
for(int i=5;i<len-2;i++)
{
shizi= shizi + buf[i];
if(buf[2]!='0')
{
like+=buf[i];
}
}
}
}
shizi += '#';
like += '#';
fclose(fp);
cout<<"输入的语句为:"<<shizi<<endl;
cout<<"可以理解为:"<<like<<endl;
cout << "OPT分析结果:" << endl;
string expression = like;
string result = "ACCEPT";
string topOfStack = "#";
while ((expression.size()>0) && result == "ACCEPT")
{
string nextSymbol = expression.substr(0,1);
if(!isVt(nextSymbol)&&!isVn(nextSymbol))
{
result = "REJECT";
cout << "Error: undefined symbol! :\""<< nextSymbol <<"\""<< endl;
break;
}
if (isVt(topOfStack.substr(0,1)))
{
string op = table[VtIndex[topOfStack.substr(0,1)]][VtIndex[nextSymbol]];
if (op=="="||op=="<")
{
cout << "Push: " << nextSymbol << endl;
expression = expression.substr(1);
topOfStack = nextSymbol+topOfStack;
}
else if(op == ">")
{
cout << "Apply production: " << endl;
int j = 1;
string Q=topOfStack.substr(0,1);
while(isVn(topOfStack.substr(j,1))||(isVt(topOfStack.substr(j,1))&&table[VtIndex[topOfStack.substr(j,1)]][VtIndex[Q]]!="<"))
{
if(isVn(topOfStack.substr(j,1)))
j++;
else
{
Q = topOfStack.substr(j,1);
j++;
}
}
topOfStack = "N"+topOfStack.substr(j);
}
else
{
result = "REJECT";
cout << "Error: No op relationship! " << nextSymbol << endl;
}
}
else
{
string op = table[VtIndex[topOfStack.substr(1,1)]][VtIndex[nextSymbol]];
if (op=="="||op=="<")
{
cout << "Push: " << nextSymbol << endl;
expression = expression.substr(1);
topOfStack = nextSymbol+topOfStack;
}
else if(op == ">")
{
cout << "Apply production: " << endl;
int j = 2;
while(isVn(topOfStack.substr(j,1))||(isVt(topOfStack.substr(j,1))&&table[VtIndex[topOfStack.substr(j,1)]][VtIndex[topOfStack.substr(0,1)]]!="<"))
j++;
topOfStack = "N"+topOfStack.substr(j);
}
else
{
result = "REJECT";
cout << "Error: No op relationship! " << nextSymbol << endl;
}
}
cout<<topOfStack<<"\t\t\t"<<expression<<endl;
}
if (topOfStack.size()==3&&expression.size()==0&&result == "ACCEPT")
{
cout << "Expression Accepted" << endl;
}
else
{
cout << "Expression Rejected" << endl;
}
cout << "--------------------------" << endl;
}
int main()
{
int cntVt = 0;
// 记录Vn集合和Vt集合及其在分析表中的下标
for (map<string, vector<string>>::iterator it = grammar.begin(); it != grammar.end(); ++it)
{
pair<string,vector<string>> temp = *it;
Vn.insert(temp.first);
vector<string> production = temp.second;
for(vector<string>::iterator iit = production.begin();iit!=production.end();iit++)
{
string ss= *iit;
for (int j = 0; j < ss.length(); j++)
{
//记录产生式中的非终结符和终结符
if (ss[j] >= 'A' && ss[j] <= 'Z')
{ // 大写字母
if (ss[j + 1] == '\'')
{ // 有'则读入俩作为一个非终结符
Vn.insert(ss.substr(j, 2));
j++;
}
else
{
Vn.insert(ss.substr(j, 1));
}
}
else
{
Vt.insert(ss.substr(j, 1)); // 是终结符
string v = ss.substr(j, 1) ;
if(v != "" && VtIndex[v]==0)
VtIndex[v] = cntVt++;
}
}
}
}
table.resize(Vt.size(), vector<string>(Vt.size(), ""));
Vn.insert("N");
// 求FirstVt集合
FirstVt = calculateFirstVtSet();
// 求LastVt集合
LastVt = calculateLastVtSet();
// 输出Vn集合
cout << "Vn集合: ";
for (set<string>::iterator it = Vn.begin(); it != Vn.end(); it++)
cout << *it << ' ';
cout<<endl;
// 输出Vt集合
cout << "Vt集合: ";
for (set<string>::iterator it = Vt.begin(); it != Vt.end(); it++)
cout << *it << ' ';
cout<<endl;
// 输出FirstVt集合
cout << "FirstVt集合: " << endl;
for (map<string, set<string>>::iterator it = FirstVt.begin(); it != FirstVt.end(); it++)
{
cout << it->first << ": ";
for (set<string>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) {
cout << '\"'<< *it2 <<'\"'<< " ";
}
cout << endl;
}
// 输出LastVt集合
cout << "LastVt集合: " << endl;
for (map<string, set<string>>::iterator it = LastVt.begin(); it != LastVt.end(); it++) {
cout << it->first << ": ";
for (set<string>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++) {
cout << '\"'<< *it2 <<'\"'<< " ";
}
cout << endl;
}
// 构造OPM分析表
parseTable();
// 输出OPM分析表
cout << "-------------OPM分析表------------------" << endl;
cout<<"--------------------------------------------"<<endl;
cout<<"\t|";
vector<string> vtt;
for(set<string>::iterator vt=Vt.begin();vt!=Vt.end();vt++)
{
cout<<*vt<<"\t|";
vtt.push_back(*vt);
}
cout<<endl;
for (set<string>::iterator vt=Vt.begin();vt!=Vt.end();vt++)
{
cout<<*vt<<"\t|";
for (int j = 0; j < Vt.size(); j++)
{
cout << table[VtIndex[*vt]][VtIndex[vtt[j]]] << "\t|";
}
cout << endl;
}
// 读取二元式文件并进行OPT分析
analyzeExpression("lab4_4_out.txt");
return 0;
}
测试
运行实验代码,用户先输入文法个数,之后输入具体文法,程序根据输入进行FIRSTVT集、LASTVT集和算符优先表的计算,之后再对句子进行文法判别。
先输入文法得到以下结果:
然后通过修改代码参数来修改要识别的语句。
Test1
用例:i*i+i
运行结果:
测试结果符合预期结果,能够对句子i*i+i进行正确判别