声明:本程序只是笔者的第一次构造结果,存在非常多需要改进的地方,笔者会在github上持续重构,大家可以在下面地址中找到最新的重构代码。https://github.com/Ggmatch/The-principle-to-compile
算符优先分析法只考虑算符(广义为终结符)之间的优先关系,例如若有文法G为:
(1)E->E+E
(2)E->E*E
(3)E->i
对输入串i1+i2*i3的规约过程如表所示:
步骤 | 栈 S | 当前输入符 | 输入串剩余部分 | 动作 |
---|---|---|---|---|
(1) | # | i1 | +i2+i3# | 移进 |
(2) | #i1 | + | i2*i3# | 规约(3) |
(3) | #E | + | i2*i3# | 移进 |
(4) | #E+ | i2 | *i3# | 移进 |
(5) | #E+i2 | * | i3# | 规约(3) |
(6) | #E+E | * | i3# | 移进 |
(7) | #E+E* | i3 | # | 移进 |
(8) | #E+E*i3 | # | 规约(3) | |
(9) | #E+E*E | # | 规约(2) | |
(10) | #E+E*E | # | 规约(1) | |
(11) | #E | # | 接受 |
定义1:设有一文法G,如果G中没有形如A->…BC…的产生式,其中B和C为非终结符,则称G为算符文法也称OG文法。
定义2:设有一不含空产生式的算符文法G,如果对任意两个终结符对a,b之间至多只有优先级高于、低于和等于三种关系中的一种成立,则称G是一个算符优先文法。
定义一个布尔数组F[m,n](m为非终结符个数,n为终结符个数)和一个后进先出栈STACK。将所有的非终结符排序,用iA表示非终结符A的序号,再将所有的终结符排序,用iA表示非终结符A的序号,再将所有的终结符排序,用ja表示终结符a的序号。算法的目的是要使数组每一个元素最终取值满足:F[iA,ja]的值为真,当前仅当a属于FIRSTVT(A)。
构建FIRSTVT集的伪代码(LASTVT集类似)如下:
PROCEDURE INSERT(A,a);
IF NOT F[iA,ja] THEN
BEGIN
F[iA,ja] := TRUE
PUSH[A,a] ONTO STACK
END
/*主程序*/
BEGIN (MAIN)
FOR i从1到m,j从1到n
DO F[iA,ja] := FALSE;
FOR 每个形如 A->a... 或 A->Ba...的产生式
DO INSERT(A,a)
WHILE STACK非空 DO
BEGIN
把STACK的顶项记为(B,a)弹出去
FOR 每个形如 A->B...的产生式弹出去
INSERT(A,a)
END
END (MAIN)
FPR 每个产生式 A->X1X2...Xn DO
FOR i := 1 TO n-1 DO
BEGIN
IF Xi和X(i+1)均为终结符
THEN 置 Xi优先级等于X(i+1);
IF i<=n-2 且Xi和X(i+2)都为终结符,但X(i+1)为非终结符
THEN 置 Xi优先级等于X(i+2);
IF Xi为终结符而X(i+1)为非终结符
THEN FOR FIRSTVT(X(i+1))中的每个b DO 置 Xi优先级低于b;
IF Xi为非终结符而X(i+1)为终结符
THEN FOR LASTVT(Xi)中的每个a DO 置 a优先级高于X(i+1);
END
对每一终结符对逐一比较
如果a优先级高于b,而f(a)<=g(b)则令f(a)=g(b)+1。
如果a优先级低于b,而f(a)>=g(b)则令g(b)=f(a)+1。
如果a优先级等于b,而f(a)!=g(b)则令min{f(a),g(b)}=max{f(a),g(b)}。
重复第二步直到过程收敛。
最左素短语
设有文法G[S],其句型的素短语是一个短语,它至少包含一个终结符,并除自身外不包含其他素短语,最左边的素短语称最左素短语。形如NiaiN(i+1)a(i+1)…ajN(j+)满足:
(1)a(i-1)优先级低于ai
(2)ai/a(i+1)/…/aj优先级相同
(3)aj优先级高于a(j+1)
k := 1; //栈的深度
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='#'
#include
#include
#include
#include
#include
#include
using namespace std;
#define P_Amount 20
#define RMAX 20
#define CMAX 20
#define NT_AMOUNT 20
#define T_AMOUNT 20
//文法结构体
class G
{
public:
string T; //终结符的集合
string NT; //非终结符的集合
string begin; //文法开始符号
string P[P_Amount]; //产生式的集合
int P_Length; //产生式的个数
bool FIRSTVT[RMAX][CMAX]; //非终结符的FIRSTVT集合
bool LASTVT[RMAX][CMAX]; //非终结符的FIRSTVT集合
int PRIORITY_TABLE[NT_AMOUNT][NT_AMOUNT]; //优先表,表内元素只有4种类型的值,-1代表行<列,1代表行>列,0代表行=列,2代表出错
int FR; //记录F的有效行数
int FC; //记录F的有效列数
int f[T_AMOUNT]; //符号栈内的优先级,f优先函数
int g[T_AMOUNT]; //符号栈外的优先级,g优先函数
G()
{
//初始化
T = "";
NT = "";
begin = "";
P_Length = 0;
for (int i = 0; i < P_Amount; i++)
P[i] = "";
for (int i = 0; i < RMAX; i++)
{
for (int j = 0; j < CMAX; j++)
{
FIRSTVT[i][j] = false;
}
}
for (int i = 0; i < RMAX; i++)
{
for (int j = 0; j < CMAX; j++)
{
LASTVT[i][j] = false;
}
}
for (int i = 0; i < NT_AMOUNT; i++)
{
for (int j = 0; j < NT_AMOUNT; j++)
{
PRIORITY_TABLE[i][j] = 2; //默认为出错状态
}
}
//初始状态下f和g内的元素都置为1
for (int i = 0; i < T_AMOUNT; i++)
{
f[i] = 1;
g[i] = 1;
}
}
~G()
{
}
};
class Temp
{
public:
char NT;
char T;
};
void findor(string &str, char c, int *pos,int cap,int &len) //在str中寻找出所有|的位置,pos记录其位置,cap为位置数组的容量,len为pos的有效长度
{
for (int i = 0; i < cap; i++)
{
//初始化
pos[i] = 0;
}
int temp=0;
for (int i = 0; i < str.length(); i++)
{
int j=str.find(c, temp);
if (j == -1)
{
break;
}
pos[i] = j;
len++;
temp = j+1;
}
}
//根据产生式得到终结符集合与非终结符集合
void getSyms(string *strs, int len, string &T,string &NT) //strs为产生式集合,len为产生式个数
{
string strTemp="";
for (int i = 0; i < len; i++)
{
if (strTemp.find(strs[i][0]) != -1) //若是找到已存在的符号,跳过
continue;
strTemp += strs[i][0];
}
NT = strTemp;
for (int i = 0; i < len; i++)
{
for (int j = 3; j < strs[i].length(); j++)
{
if (strs[i][j] >= 'A' && strs[i][j] <= 'Z') //非终结符
{
if (NT.find(strs[i][j]) != 0) //该符号已存在,跳过
continue;
NT += strs[i][j];
}
else //终结符,可能是算符或i
{
if (T.find(strs[i][j]) != -1) //该符号已存在,跳过
continue;
T += strs[i][j];
}
}
}
}
void zero(string *strs, int &len) //strs为输入的文法表达式,len为表达式的个数
{
string StrTemp[P_Amount];
int lens,count=0; //count记录新生成表达式的个数,lens记录每个
int pos[10];
for (int i = 0; i < len; i++) //将所有产生式按|隔开,生成多个产生式,存储到StrTemp中
{
lens = 0;
findor(strs[i], '|', pos, 10, lens); //寻找这条表达式中所有“|”的位置,存放到pos中,10为其最大容量,lens为“|”个数
for (int z = 0; z < lens+1; z++) //将一个产生式按|隔开,生成多个产生式,存储到StrTemp中,(len+1)代表要生成的表达式个数
{
if (z == 0)
{
StrTemp[z + count] = strs[i].substr(0, 3) + strs[i].substr(3, (pos[z]-2 - 1));
continue;
}
StrTemp[z + count] = strs[i].substr(0, 3) + strs[i].substr(pos[z - 1]+1, (pos[z] - pos[z - 1] - 1));
if (z == lens)
{
StrTemp[count + lens] = strs[i].substr(0, 3) + strs[i].substr(pos[lens - 1]+1, (strs[i].length() - pos[lens - 1] - 1));
}
}
count += lens + 1;
}
//把StrTemp赋给strs
len = count;
for (int i = 0; i < count; i++)
{
strs[i] = StrTemp[i];
}
}
void test1(G &gm); //声明
void first(G &grammer,string strs[],int len) //grammer为需要构造的文法对象,strs为经过zero后的文法表达式,len为表达式的个数
{
grammer.begin = strs[0][0]; //开始符号
grammer.P_Length = len; //产生式的个数
for (int i = 0; i < len; i++) //得到产生式
{
grammer.P[i] = strs[i];
}
getSyms(strs, len, grammer.T, grammer.NT); //从产生式中得到非终结符集合与终结符集合
}
void test1(G &gm) //检测函数,这里检查文法数据结构的正确性
{
cout << endl << "检测:" << endl;
cout << "文法的开始符号:" << gm.begin << endl;
cout << "文法的非终结符集合:" << gm.NT << endl;
cout << "文法的终结符集合:" << gm.T << endl;
cout << "文法的产生式:" << endl;
for (int i = 0; i < gm.P_Length; i++)
{
cout << gm.P[i] << endl;
}
cout << endl;
}
void insert(G &gm, stack &stck, char A, char a) //
{
int iA = gm.NT.find(A);
int ia = gm.T.find(a);
Temp tmp;
tmp.NT = A;
tmp.T = a;
if (!gm.FIRSTVT[iA][ia])
{
gm.FIRSTVT[iA][ia] = true;
stck.push(tmp);
}
}
void insert1(G &gm, stack &stck, char A, char a) //
{
int iA = gm.NT.find(A);
int ia = gm.T.find(a);
Temp tmp;
tmp.NT = A;
tmp.T = a;
if (!gm.LASTVT[iA][ia])
{
gm.LASTVT[iA][ia] = true;
stck.push(tmp);
}
}
void test2(G &gm); //测试gm的FIRSTVT和LASTVT集合
void second(G &gm) //gm为文法的四元组
{
gm.FR = gm.NT.length();
gm.FC = gm.T.length();
stack stck;
Temp tmpBa;
//得到G的FIRSTVT集合
for (int i = 0; i < gm.P_Length; i++)
{
if (gm.P[i].length() - 4 == 0) //产生式右边只有一个符号
{
if (gm.P[i][3] < 'A' || gm.P[i][3]>'Z') //这个符号是终结符
{
insert(gm, stck, gm.P[i][0], gm.P[i][3]);
}
}
else if (gm.P[i][3] >= 'A' && gm.P[i][3] <= 'Z') //右部的第一个符号为非终结符,那么加入右部的第二个符号
{
insert(gm, stck, gm.P[i][0], gm.P[i][4]);
}
else //右部的第一个符号为终结符
{
insert(gm, stck, gm.P[i][0], gm.P[i][3]);
}
}
while (!stck.empty())
{
tmpBa = stck.top();
stck.pop();
for (int i = 0; i < gm.P_Length; i++)
{
if (gm.P[i][0] != tmpBa.NT&&gm.P[i][3] == tmpBa.NT) //找到形如A->B···的产生式,FIRSTVT(B)必然FIRSTVT(A)
{
insert(gm, stck, gm.P[i][0], tmpBa.T);
}
}
}
//得到G的LASTVT集合
for (int i = 0; i < gm.P_Length; i++)
{
if (gm.P[i].length() - 4 == 0) //产生式右边只有一个符号
{
if (gm.P[i][3] < 'A' || gm.P[i][3]>'Z') //这个符号是终结符
{
insert1(gm, stck, gm.P[i][0], gm.P[i][3]);
}
}
else if (gm.P[i][3] >= 'A' && gm.P[i][3] <= 'Z') //右部的最后一个符号为非终结符,那么加入右部的倒数第二个符号
{
insert1(gm, stck, gm.P[i][0], gm.P[i][gm.P[i].length()-2]);
}
else //右部的最后一个符号为终结符
{
insert1(gm, stck, gm.P[i][0], gm.P[i][gm.P[i].length()-1]);
}
}
while (!stck.empty())
{
tmpBa = stck.top();
stck.pop();
for (int i = 0; i < gm.P_Length; i++)
{
if (gm.P[i][0] != tmpBa.NT&&gm.P[i][gm.P[i].length()-1] == tmpBa.NT) //找到形如A->···B的产生式,LASTVT(B)必然LASTVT(A)
{
insert1(gm, stck, gm.P[i][0], tmpBa.T);
}
}
}
}
void test2(G &gm)
{
cout << "G的FIRSTVT集合:" << endl;
cout << "\t";
for (int i = 0; i < gm.T.length(); i++)
{
if (i == gm.T.length() - 1)
{
cout << gm.T[i] << endl;
break;
}
cout << gm.T[i] << "\t";
}
for (int i = 0; i < gm.FR; i++)
{
cout << gm.NT[i] << "\t";
for (int j = 0; j < gm.FC; j++)
{
if (j == gm.FC - 1)
{
cout << gm.FIRSTVT[i][j] << endl;
break;
}
cout << gm.FIRSTVT[i][j] << "\t";
}
}
cout << "G的LASTVT集合:" << endl;
cout << "\t";
for (int i = 0; i < gm.T.length(); i++)
{
if (i == gm.T.length() - 1)
{
cout << gm.T[i] << endl;
break;
}
cout << gm.T[i] << "\t";
}
for (int i = 0; i < gm.FR; i++)
{
cout << gm.NT[i] << "\t";
for (int j = 0; j < gm.FC; j++)
{
if (j == gm.FC - 1)
{
cout << gm.LASTVT[i][j] << endl;
break;
}
cout << gm.LASTVT[i][j] << "\t";
}
}
cout << endl;
}
void test3(G &gm); //测试third函数
void third(G &gm) //gm为文法的数据结构
{
for (int i = 0; i < gm.P_Length; i++)
{
int lens = gm.P[i].length();
for (int j = 3; j <= lens-2; j++)
{
if ((gm.P[i][j]<'A'||gm.P[i][j]>'Z')&&(gm.P[i][j + 1]<'A'||gm.P[i][j + 1]>'Z')) //Xi和X(i+1)都为终结符
{
int temp = gm.T.find(gm.P[i][j]);
int temp1 = gm.T.find(gm.P[i][j + 1]);
gm.PRIORITY_TABLE[temp][temp1] = 0; //置为同等优先级
}
if (j <= (lens - 3) && (gm.P[i][j]<'A' || gm.P[i][j]>'Z') && (gm.P[i][j + 2]<'A' || gm.P[i][j + 2]>'Z') && (gm.P[i][j + 1] >= 'A'&&gm.P[i][j + 1] <= 'Z')) //Xi和X(i+2)为终结符,但X(i+1)为非终结符
{
int temp = gm.T.find(gm.P[i][j]);
int temp1 = gm.T.find(gm.P[i][j + 2]);
gm.PRIORITY_TABLE[temp][temp1] = 0; //置为同等优先级
}
if ((gm.P[i][j]<'A' || gm.P[i][j]>'Z') && (gm.P[i][j + 1] >= 'A' && gm.P[i][j + 1] <= 'Z')) //Xi为终结符而X(i+1)为非终结符
{
for (int z = 0; z < gm.T.length(); z++)
{
if (gm.FIRSTVT[gm.NT.find(gm.P[i][j + 1])][z])
{
int temp = gm.T.find(gm.P[i][j]);
int temp1 = z;
gm.PRIORITY_TABLE[temp][temp1] = -1; //置于列优先于行
}
}
}
if ((gm.P[i][j+1]<'A' || gm.P[i][j+1]>'Z') && (gm.P[i][j] >= 'A' && gm.P[i][j] <= 'Z')) //X(i+1)为终结符而Xi为非终结符
{
for (int z = 0; z < gm.T.length(); z++)
{
if (gm.LASTVT[gm.NT.find(gm.P[i][j])][z])
{
int temp = z;
int temp1 = gm.T.find(gm.P[i][j+1]);
gm.PRIORITY_TABLE[temp][temp1] = 1; //置于行优先于列
}
}
}
}
}
}
void test3(G &gm)
{
cout << "G的优先分析表:" << endl;
cout << "\t";
for (int i = 0; i < gm.T.length(); i++)
{
if (i == gm.T.length() - 1)
{
cout << gm.T[i] << endl;
break;
}
cout << gm.T[i] << "\t";
}
int lens = gm.T.length();
for (int i = 0; i < lens; i++)
{
cout << gm.T[i] << "\t";
for (int j = 0; j < lens; j++)
{
if (j == gm.T.length() - 1)
{
cout << gm.PRIORITY_TABLE[i][j] << endl;
break;
}
cout << gm.PRIORITY_TABLE[i][j] << "\t";
}
}
}
void test4(G &gm); //检测优先函数f和g
void four(G &gm) //构造优先函数f和g
{
bool isChanged = true; //isChanged代表上一次迭代f和g函数有没有发生变化,有为true
int lens = gm.T.length();
while (isChanged)
{
isChanged = false;
for (int i = 0; i < lens; i++)
{
for (int j = 0; j < lens; j++)
{
if (gm.PRIORITY_TABLE[i][j] == 2)
continue;
if (gm.PRIORITY_TABLE[i][j] == 1 && (gm.f[i] <= gm.g[j]))
{
gm.f[i] = gm.g[j] + 1;
isChanged = true;
continue;
}
if (gm.PRIORITY_TABLE[i][j] == -1 && (gm.f[i] >= gm.g[j]))
{
gm.g[j] = gm.f[i] + 1;
isChanged = true;
continue;
}
if (gm.PRIORITY_TABLE[i][j] == 0 && (gm.f[i] != gm.g[j]))
{
if (gm.f[i] > gm.g[j])
{
gm.g[j] = gm.f[i];
}
else
{
gm.f[i] = gm.g[j];
}
isChanged = true;
continue;
}
}
}
}
}
void test4(G &gm) //检测four函数
{
cout << "\t";
for (int i = 0; i < gm.T.length(); i++)
{
if (i == gm.T.length() - 1)
{
cout << gm.T[i] << endl;
break;
}
cout << gm.T[i] << "\t";
}
int lens = gm.T.length();
cout << "f函数: ";
for (int i = 0; i < lens; i++)
{
if (i == lens - 1)
{
cout << gm.f[i] << endl;
break;
}
cout << gm.f[i] << "\t";
}
cout << "g函数: ";
for (int i = 0; i < lens; i++)
{
if (i == lens - 1)
{
cout << gm.g[i] << endl;
break;
}
cout << gm.g[i] << "\t";
}
}
void test5(bool b); //检测GuiYueQi是否识别出待规约串,b为true,则说明能识别
bool GuiYueQi(G &gm, string str) //gm为文法的数据结构,str为待规约串,能识别为true,否则为false
{
string strStck=""; //用string对象做符号栈,方便取值
strStck.append("#"); //初始化
int k = 0,num=0;
int j; //j表示最左素短语的开头位置
string a=""; //读入单元
string Q = ""; //作为待审查的最左素短语的开头符号
while (a!="#") //到了输入串的最后一个字符,跳出
{
a = str[num++]; //把下一个输入符号读进a中
if (gm.T.find(strStck[k]) != gm.T.npos) //如果栈顶的符号为终结符
{
j = k;
}
else
{
j = k - 1;
}
while (gm.f[gm.T.find(strStck[j])] > gm.g[gm.T.find(a)]) //往下找,直到找到栈内符号优先级小于或等于当前输入符号的优先级
{
do
{
Q = strStck[j];
if (gm.T.find(strStck[j - 1]) != gm.T.npos) //
{
j = j - 1;
}
else
{
j = j - 2;
}
} while (gm.f[gm.T.find(strStck[j])] >= gm.g[gm.T.find(Q)]);
//把strStck[j+1]~strStck[k]规约为某个N
strStck.erase(strStck.begin() + j + 1, strStck.begin() + k); //把规约的符号退栈
strStck.append("N"); //压入某个非终结符N
k = j + 1;
break;
}
if (gm.f[gm.T.find(strStck[j])] <= gm.g[gm.T.find(a)])
{
k = k + 1;
strStck.append(a);
}
else
{
return false;
}
}
return true; //
}
void test5(bool b)
{
if (b)
{
cout << "acc!" << endl;
}
else
{
cout << "fail!" << endl;
}
}
int main()
{
G gm;
string str[10];
int len; //输入的表达式个数
int i = 0;
//从文件读入文法
ifstream ifile("wenfa.txt"); //打开wenfa.txt文件
while (ifile)
{
ifile >> str[i++]; //读入文法
}
ifile.close(); //关闭文件
len = i - 1;
zero(str, len); //依据‘|’,拆分表达式
first(gm, str, len); //得到文法数据结构
//test1(gm); //检查zero()、first()函数
second(gm); //得到每个非终结符的FIRSTVT和LASTVT集合
//test2(gm); //检测second函数
third(gm); //得到优先分析表
//test3(gm); //检测third函数
four(gm); //得到优先函数f和g
//test4(gm); //检测four函数
string guiyuestr; //规约串
ifile.open("guiyuechuan.txt");
stringstream io;
io <> guiyuestr;
cout << "待规约串为:"<< guiyuestr <bool b = GuiYueQi(gm, guiyuestr); //规约器,规约str
test5(b); //检测待规约串被规约器处理后的结果
return 0;
}
上述代码中wenfa.txt与guiyuechuan.txt的内容如下,自己构建,并放在项目里c++文件所在处,要符合相对路径。
wenfa.txt:
S->#E#
E->E+T
E->T
T->T*F
T->F
F->P!F|P
P->(E)
P->i
guiyuechuan.txt:
i+i#
本文理论部分引用于 《编译原理(第二版)——张素琴——清华大学出版社》