一、 实验目的
根据算符优先分析法,对表达式进行语法分析,使其能够判断一个表达式是否正确。通过算符优先分析方法的实现,加深对自下而上语法分析方法的理解。
二、 实验要求
1、输入文法。可以是如下算术表达式的文法(你可以根据需要适当改变):
E→E+T|E-T|T
T→T*F|T/F|F
F→(E)|i
这里说明一下,我没有按这个格式来,而是一个一个输入,具体请参见最后的测试数据
2、对给定表达式进行分析,输出表达式正确与否的判断。
程序输入/输出示例:
输入:1+2;
输出:正确
输入:(1+2)/3+4-(5+6/7);
输出:正确
输入:((1-2)/3+4
输出:错误
输入:1+2-3+(*4/5)
输出:错误
三、实验步骤
1、参考数据结构
char *VN=0,*VT=0;//非终结符和终结符数组
char firstvt[N][N],lastvt[N][N],table[N][N];
typedef struct //符号对(P,a)
{
char Vn;
char Vt;
} VN_VT;
typedef struct //栈
{
VN_VT *top;
VN_VT *bollow;
int size;
}stack;
2、根据文法求FIRSTVT集和LASTVT集
给定一个上下文无关文法,根据算法设计一个程序,求文法中每个非终结符的FirstVT 集和LastVT 集。
算符描述如下:
/*求 FirstVT 集的算法*/
PROCEDURE insert(P,a);
IF not F[P,a] then
begin
F[P,a] = true; //(P,a)进栈
end;
Procedure FirstVT;
Begin
for 对每个非终结符 P和终结符 a do
F[P,a] = false
for 对每个形如 P a…或 P→Qa…的产生式 do
Insert(P,a)
while stack 非空
begin
栈顶项出栈,记为(Q,a)
for 对每条形如 P→Q…的产生式 do
insert(P,a)
end;
end.
同理,可构造计算LASTVT的算法。
3、构造算符优先分析表
依据文法和求出的相应FirstVT和 LastVT 集生成算符优先分析表。
算法描述如下:
for 每个形如 P->X1X2…Xn的产生式 do
for i =1 to n-1 do
begin
if Xi和Xi+1都是终结符 then
Xi = Xi+1
if i<= n-2, Xi和Xi+2 是终结符, 但Xi+1 为非终结符 then
Xi = Xi+2
if Xi为终结符, Xi+1为非终结符 then
for FirstVT 中的每个元素 a do
Xi < a ;
if Xi为非终结符, Xi+1为终结符 then
for LastVT 中的每个元素 a do
a > Xi+1 ;
end
4、构造总控程序
算法描述如下:
stack S;
k = 1; //符号栈S的使用深度
S[k] = ‘#’
REPEAT
把下一个输入符号读进a中;
If S[k]
VT then j = k else j = k-1;
While S[j] > a do
Begin
Repeat
Q = S[j];
if S[j-1]
VT then j = j-1 else j = j-2
until S[j] < Q;
把S[j+1]…S[k]归约为某个N,并输出归约为哪个符号;
K = j+1;
S[k] = N;
end of while
if S[j] < a or S[j] = a then
begin k = k+1; S[k] = a end
else error //调用出错诊察程序
until a = ‘#’
四、代码实现(使用C++实现)
源码
#include<iostream>
#include<cstring>
#include<string>
#include<stack>
using namespace std;
typedef struct{
string formula;//产生式
}grammarElement;
grammarElement gramOldSet[50];
int n=0;
string terSymbol;//终结符号
string non_ter;//非终结符号
string firstvt[50],lastvt[50];
char table[50][50];
typedef struct //符号对(P,a)
{
char Vn;
char Vt;
} VN_VT;
void FirstVTscan(char &non);
void LastVTscan(char &non);
void toTable();
void Control();
void leftsu(string &str,int &x,int &y);
bool tui(char &A,char &B);
int get_ter_ps(string &str);
string add(string s1,string s2);
void addF(char non_t,char ter);
void addL(char non_t,char ter);
int main(){
cout << "non_ter: ";
cin >> non_ter;
non_ter+= "X";
cout << "ter: ";
cin >> terSymbol;
terSymbol+= "#";
cout<<"num: ";
cin>> n;
for(int i=1;i<=n;i++){
cin>>gramOldSet[i].formula;
}
gramOldSet[0].formula ="X->##";
gramOldSet[0].formula.insert(4, 1, gramOldSet[1].formula[0]);
// FirstVT();
// LastVT();
for(int i=0;i<non_ter.size();i++){
FirstVTscan(non_ter[i]);
}
for(int i=0;i<non_ter.size();i++){
cout<<firstvt[i]<<endl;
}
cout<<endl;
for(int i=0;i<non_ter.size();i++){
LastVTscan(non_ter[i]);
}
for(int i=0;i<non_ter.size();i++){
cout<<lastvt[i]<<endl;
}
cout<<endl;
toTable();
for(int i=0;i<terSymbol.size();i++){
for(int j=0;j<terSymbol.size();j++){
cout.width(4);
cout<<table[i][j];
if(table[i][j]==0) cout<<" ";
}
cout<<endl;
}
Control();
return 0;
}
void Control(){
bool flag=true;
string an="#",inputs;
cin>>inputs;
inputs.push_back('#');
int p=0; //p是栈顶终结符的位置
int k=0;
while(flag){
cout.width(5);
cout<<left<<k;
k++;
p=get_ter_ps(an);
//查表,看关系是<=>
char c = table[terSymbol.find(an[p])][terSymbol.find(inputs[0])];
if(c=='=' && inputs[0] == '#'){
flag=false;
}
//移进
else if (c=='<'||c=='='){
an.push_back(inputs[0]);
inputs.erase(0,1);
//输出结果
cout.width(12);
cout<<left<<an;
cout.width(12);
cout<<right<<inputs<<endl;
// cout<<" 移进"<<endl;
}
else if (c=='>'){
int x=0,y=0;
leftsu(an,x,y);
// if(x==y){
for(int i=1;i<=n;i++){
if(gramOldSet[i].formula.find(an[x],3) != -1){
int size = gramOldSet[i].formula.size()-3;
int start = x-(gramOldSet[i].formula.find(an[x],3)-3); //归约的第一个字符开始的下标
bool is_this=true; //是否为当前文法
if(size==1){
an.pop_back();
an.push_back(gramOldSet[i].formula[0]);
break;
}
else if (size>1){
//看其他非终结符是否能推出an中的非终结符
for(int j=0;j<size;j++){
if(non_ter.find(an[start+j]) == -1 ){
continue;
}
else if ( tui(gramOldSet[i].formula[3+j], an[start+j]) ){
}else{
is_this = false;
break;
}
}
//
if(is_this){
//归约过程
for(int j=0;j<size;j++){
an.pop_back();
}
an.push_back(gramOldSet[i].formula[0]);
break;
}
}
}
}
// }
//输出结果
cout.width(12);
cout<<left<<an;
cout.width(12);
cout<<right<<inputs<<endl;
// cout<<" 归约"<<endl;
}
else if (c==0){
flag = false;
cout<<"error!"<<endl;
}
}
}
void FirstVTscan(char &non){
for(int i=0;i<non_ter.size();i++){
for(int j=0;j<=n;j++){
if(gramOldSet[j].formula[0]==non){
//P->a...
if( terSymbol.find(gramOldSet[j].formula[3])!=-1 ){
addF(gramOldSet[j].formula[0],gramOldSet[j].formula[3]);
}
//P->Q
else if ( gramOldSet[j].formula.size()>4 ){
//P->Qa...
if ( non_ter.find(gramOldSet[j].formula[3])!=-1 && terSymbol.find(gramOldSet[j].formula[4])!=-1 ){
addF(gramOldSet[j].formula[0],gramOldSet[j].formula[4]);
}
}
//P->Q...
if( non_ter.find(gramOldSet[j].formula[3])!=-1 && gramOldSet[j].formula[0]!=gramOldSet[j].formula[3] ){
char c=gramOldSet[j].formula[3];
FirstVTscan(c);
firstvt[non_ter.find(non)] = add(firstvt[non_ter.find(non)], firstvt[non_ter.find(gramOldSet[j].formula[3])]);
}
}
}
}
return ;
}
void LastVTscan(char &non){
//为什么加了&之后就不报错了ToT
//https://blog.youkuaiyun.com/mofushaohua_ln/article/details/77934368
for(int i=0;i<non_ter.size();i++){
for(int j=0;j<=n;j++){
int it = gramOldSet[j].formula.size()-1;
if( gramOldSet[j].formula[0]==non ){
//p->..a
if(terSymbol.find( gramOldSet[j].formula[it] ) != -1){
addL(non,gramOldSet[j].formula[it]);
}
//P->..aQ
else if(gramOldSet[j].formula.size()>4){
if(non_ter.find(gramOldSet[j].formula[it])!=-1 && terSymbol.find(gramOldSet[j].formula[it-1]) !=-1 ){
addL(non, gramOldSet[j].formula[it-1]);
}
}
//P->...Q
if(non_ter.find(gramOldSet[j].formula[it])!=-1 && gramOldSet[j].formula[0]!=gramOldSet[j].formula[it] ){
char c= gramOldSet[j].formula[it];
LastVTscan(c);
lastvt[non_ter.find(non)] = add(lastvt[non_ter.find(non)], lastvt[non_ter.find(gramOldSet[j].formula[it])]);
}
}
}
}
return ;
}
void toTable(){
for(int i=0;i<=n;i++){
int p=3,q=gramOldSet[i].formula.size()-1;
for(int j=p;j<=q-1;j++){
int p0=terSymbol.find(gramOldSet[i].formula[j]),p1=terSymbol.find(gramOldSet[i].formula[j+1]);
int q0=non_ter.find(gramOldSet[i].formula[j]),q1=non_ter.find(gramOldSet[i].formula[j+1]);
if(j<=q-2){
//...aQb...
if(p0!=-1 && q1!=-1 && terSymbol.find(gramOldSet[i].formula[j+2])!=-1 ){
table[p0][terSymbol.find(gramOldSet[i].formula[j+2])] = '=';
}
}
//...ab..
if(p0!=-1&& p1!=-1){
table[p0][p1] = '=';
}
//...aQ...
else if (p0!=-1 && q1!=-1){
for(int k=0;k<firstvt[q1].size(); k++){
table[p0][terSymbol.find(firstvt[q1][k])] = '<';
}
}
//...Qa...
else if(q0!=-1&& p1!=-1){
for(int k=0;k<lastvt[q0].size(); k++){
table[terSymbol.find(lastvt[q0][k])][p1] = '>';
}
}
}
}
}
//寻找最左素短语
void leftsu(string &str,int &x,int &y){
int z=1;
for(int i=1; i<str.size(); i++){
if(terSymbol.find(str[i])!= -1){
x=i; break;
}
}
y=x;
for(z=x+1 ; z<str.size(); z++){
if(terSymbol.find(str[z]) != -1){
if(table[terSymbol.find(str[y])][terSymbol.find(str[z])] == '<'){
x=z;
y=z;
}
else if (table[terSymbol.find(str[y])][terSymbol.find(str[z])] == '='){
y=z;
}
else if (table[terSymbol.find(str[y])][terSymbol.find(str[z])] == '>'){
break;
}
}
}
}
//A是否能推出B
bool tui(char &A,char &B){
if(A==B) return true;
for(int i=1;i<=n;i++){
if(gramOldSet[i].formula[0] == A && gramOldSet[i].formula.size() == 4){
if( gramOldSet[i].formula[3]==B){
return true;
}
else if( non_ter.find(gramOldSet[i].formula[3])!=-1){
return tui(gramOldSet[i].formula[3],B);
}
}
}
return false;
}
//寻找栈中第一个终结符
int get_ter_ps(string &str){
for(int i=str.size()-1; i>=0; i--){
if(terSymbol.find(str[i])!= -1) return i;
}
return -1;
}
//将s1与s2合并,返回s2
string add(string s1,string s2){
for(int i=0;i<s1.size();i++){
if(s2.find(s1[i])!=-1) continue;
else {
s2.push_back(s1[i]);
}
}
return s2;
}
//向firstvt集中添加
void addF(char non_t,char ter){
int p=non_ter.find(non_t);
if(firstvt[p].find(ter)==-1){
firstvt[p].push_back(ter);
}
}
//向lastvt集中添加
void addL(char non_t,char ter){
int p=non_ter.find(non_t);
if(lastvt[p].find(ter)==-1){
lastvt[p].push_back(ter);
}
}
测试数据:
ETFP
+*!()i
8
E->E+T
E->T
T->T*F
T->F
F->P!F
F->P
P->(E)
P->i
(i+i)*i