一、在算符優先語法分析的基礎上進行翻譯工作(即語義分析),每當將一個最左素短語歸約爲一個非終結符號時,就調用對應產生式的語義子程序,去完成相應的語義翻譯工作,這步歸約使用的產生式對非終結符號不加區分(即將所有的非終結符號用一個通用的非終結符號表示)。
判斷是句子的情況下,輸出:四元式序列(*,val,count,T1)
算符優先分析前期準備:https://blog.csdn.net/lfy905805357/article/details/89333111
二、code
#include <iostream>
#include "stdio.h"
#include "stdlib.h"
#include <string>
#include <map>
#include <sstream>
using namespace std;
char Data[20][20]; //算符優先關係
char s[100]; //模擬符號棧s
string str[100]; //用來類似模擬符號棧s,不過該棧爲字符串(用於同時給非終結符加上下標,例如P1)
char lable[20]; //文法終極符集
char input[100]; //文法輸入符號串
char In_str[20][10]; //用於輸入串的分析
char text[20][10];
int k;
char a;
int j;
char q;
int r; //文法規則個數
int r1; //轉化後文法規則個數
char st[10][30]; //用來存儲文法規則
char first[10][10]; //文法非終結符FIRSTVT集
char last[10][10]; //文法非終結符LASTVT集
int fflag[10] = { 0 }; //標誌第i個非終結符的FIRSTVT集是否已求出
int lflag[10] = { 0 }; //標誌第i個非終結符的LASTVT集是否已求出
map<char,int> charmap; //用來判斷該字符的下標是幾
map<string,string> stringmap; //用來下標,值健對查找
stringstream ss; //利用該stringstream來進行其他類型轉換爲字符串
string qua[100][4]; //存儲四元組
int quaCounts=0; //四元組個數
int deal(); //對輸入串的分析
int deal1(); //對輸入串的分析(包括求四元組)
int Terminator(char c); //判斷字符c是否是終極符
int getIndex(char c); //求字符c在算符優先關係表中的下標
void out(int j, int k, char *s); //打印s棧
void out1(int j, int k, string *str); //打印str棧
void firstvt(char c); //求非終結符c的FIRSTVT集
void lastvt(char c); //求非終結符c的LASTVT集
void table(); //創建文法優先關係表
bool check(char *in); //判斷輸入是不是都是終結符構成的句子
int main()
{
int i, j, k = 0;
printf("請輸入文法規則數:");
cin >> r;
printf("請輸入文法規則:\n");
for (i = 0; i<r; i++)
{
cin >> st[i]; //存儲文法規則,初始化FIRSTVT集和LASTVT集*/
first[i][0] = 0; /*first[i][0]和last[i][0]分別表示st[i][0]非終極
符的FIRSTVT集和LASTVT集中元素的個數*/
last[i][0] = 0;
}
for (i = 0; i<r; i++) //判斷文法是否合法
{
for (j = 0; st[i][j] != '\0'; j++)
{
if (st[i][0]<'A' || st[i][0]>'Z')
{
printf("不是算符文法!\n");
exit(-1);
}
if (st[i][j] >= 'A'&&st[i][j] <= 'Z')
{
if (st[i][j + 1] >= 'A'&&st[i][j + 1] <= 'Z')//兩非終結符不能相鄰
{
printf("不是算符文法!\n");
exit(-1);
}
}
}
}
for (i = 0; i<r; i++)
{
for (j = 0; st[i][j] != '\0'; j++)
{
if ((st[i][j]<'A' || st[i][j]>'Z') && st[i][j] != '-'&&st[i][j] != '>'&&st[i][j] != '|')
lable[k++] = st[i][j];
}
}
lable[k] = '#';//把井號終結符加入到終結符集合裏面
lable[k + 1] = '\0';
table();
printf("每個非終結符的FIRSTVT集爲:\n"); //輸出每個非終結符的FIRSTVT集
for (i = 0; i<r; i++)
{
printf("%c: ", st[i][0]);
for (j = 0; j<first[i][0]; j++)
{
printf("%c ", first[i][j + 1]);
}
printf("\n");
}
printf("每個非終結符的LASTVT集爲:\n"); //輸出每個非終結符的LASTVT集
for (i = 0; i<r; i++)
{
printf("%c: ", st[i][0]);
for (j = 0; j<last[i][0]; j++)
{
printf("%c ", last[i][j + 1]);
}
printf("\n");
}
printf("算符優先分析表如下:\n");
for (i = 0; lable[i] != '\0'; i++)
printf("\t%c", lable[i]);
printf("\n");
for (i = 0; i<k + 1; i++)
{
printf("%c\t", lable[i]);
for (j = 0; j<k + 1; j++)
{
printf("%c\t", Data[i][j]);
}
printf("\n");
}
printf("請輸入文法輸入符號串以#結束:");
while (cin >> input && input != "exit") {
int flag=deal1();//標記是否符合文法定義
if(flag==1&&check(input)){//檢測輸入串是否是終結符組成的句子
cout<<"該符號串是句子!"<<endl;
cout<<"\n四元式如下:"<<endl;
for(int i=0;i<quaCounts;i++){
cout<<"產生【"<<(i+1)<<"】:";
cout<<"("+qua[i][0]+","+qua[i][1]+","+qua[i][2]+","+qua[i][3]+")"<<endl;
}
}
quaCounts=0;//重新歸零
printf("請輸入文法輸入符號串以#結束:");
}
system("pause");
}
//判斷輸入是不是都是終結符,判斷是不是句子
bool check(char *in){
for(int i=0;in[i]!='\0';i++){
if(!Terminator(in[i]))//逐個判斷是不是終結符
return false;
}
return true;
}
void table()
{
//char text[20][10];
int i, j, k, t, l, x = 0, y = 0;
int m, n;
x = 0;
//求firstvt集和lastvt集
for (i = 0; i<r; i++)
{
firstvt(st[i][0]);
lastvt(st[i][0]);
}
//轉化文法,化爲單候選式文法
for (i = 0; i<r; i++)
{
text[x][y] = st[i][0];
y++;
for (j = 1; st[i][j] != '\0'; j++)
{
if (st[i][j] == '|')
{
text[x][y] = '\0';
x++;
y = 0;
text[x][y] = st[i][0];
y++;
text[x][y++] = '-';
text[x][y++] = '>';
}
else
{
text[x][y] = st[i][j];
y++;
}
}
text[x][y] = '\0';
x++;
y = 0;
}
r1 = x;//更改轉換後文法的數目
printf("轉化後的文法爲:\n");
for (i = 0; i<x; i++) //輸出轉化後的文法規則串
{
printf("%s\n", text[i]);
}
/*求每個終結符的推導結果(去掉"->"
後的轉化文法,用於最後的規約)*/
for (i = 0; i<x; i++)
{
In_str[i][0] = text[i][0];
for (j = 3, l = 1; text[i][j] != '\0'; j++, l++)
In_str[i][l] = text[i][j];
In_str[i][l] = '\0';
}
//利用書上算法構造算符優先分析表
//firstvt[0][0]第一位爲firstvt集元素數目
//lastvt集也一樣
for (i = 0; i<x; i++)
{
for (j = 1; text[i][j + 1] != '\0'; j++)
{
if (Terminator(text[i][j]) && Terminator(text[i][j + 1]))
{
m = getIndex(text[i][j]);
n = getIndex(text[i][j + 1]);
Data[m][n] = '=';
}
if (text[i][j + 2] != '\0'&&Terminator(text[i][j]) && Terminator(text[i][j + 2]) && !Terminator(text[i][j + 1]))
{
m = getIndex(text[i][j]);
n = getIndex(text[i][j + 2]);
Data[m][n] = '=';
}
if (Terminator(text[i][j]) && !Terminator(text[i][j + 1]))
{
for (k = 0; k<r; k++)
{
if (st[k][0] == text[i][j + 1])
break;
}
m = getIndex(text[i][j]);
for (t = 0; t<first[k][0]; t++)
{
n = getIndex(first[k][t + 1]);
Data[m][n] = '<';
}
}
if (!Terminator(text[i][j]) && Terminator(text[i][j + 1]))
{
for (k = 0; k<r; k++)
{
if (st[k][0] == text[i][j])
break;
}
n = getIndex(text[i][j + 1]);
for (t = 0; t<last[k][0]; t++)
{
m = getIndex(last[k][t + 1]);
Data[m][n] = '>';
}
}
}
}
//將#E#開始文法也加上去求出其對應的firstvt和lastvt集。
m = getIndex('#');
for (t = 0; t<first[0][0]; t++)
{
n = getIndex(first[0][t + 1]);
Data[m][n] = '<';
}
n = getIndex('#');
for (t = 0; t<last[0][0]; t++)
{
m = getIndex(last[0][t + 1]);
Data[m][n] = '>';
}
Data[n][n] = '=';
}
void firstvt(char c) //求FIRSTVT集
{
int i, j, k, m, n;
for (i = 0; i<r; i++)
{
if (st[i][0] == c)
break;
}
if (fflag[i] == 0)
{
n = first[i][0] + 1;
m = 0;
do
{
if (m == 2 || st[i][m] == '|')
{
if (Terminator(st[i][m + 1]))
{
first[i][n] = st[i][m + 1];
n++;
}
else
{
if (Terminator(st[i][m + 2]))
{
first[i][n] = st[i][m + 2];
n++;
}
if (st[i][m + 1] != c)
{
firstvt(st[i][m + 1]);
for (j = 0; j<r; j++)
{
if (st[j][0] == st[i][m + 1])
break;
}
for (k = 0; k<first[j][0]; k++)
{
int t;
for (t = 0; t<n; t++)
{
if (first[i][t] == first[j][k + 1])
break;
}
if (t == n)
{
first[i][n] = first[j][k + 1];
n++;
}
}
}
}
}
m++;
} while (st[i][m] != '\0');
first[i][n] = '\0';
first[i][0] = --n;//第一位值存firstvt集中元素的個數(字符形式)
fflag[i] = 1;//表明該非終極符的firstvt集求出來了
}
}
void lastvt(char c) //求LASTVT集
{
int i, j, k, m, n;
for (i = 0; i<r; i++)
{
if (st[i][0] == c)
break;
}
if (lflag[i] == 0)
{
n = last[i][0] + 1;
m = 0;
do
{
if (st[i][m + 1] == '\0' || st[i][m + 1] == '|')
{
if (Terminator(st[i][m]))
{
last[i][n] = st[i][m];
n++;
}
else
{
if (Terminator(st[i][m - 1]))
{
last[i][n] = st[i][m - 1];
n++;
}
if (st[i][m] != c)
{
lastvt(st[i][m]);
for (j = 0; j<r; j++)
{
if (st[j][0] == st[i][m])
break;
}
for (k = 0; k<last[j][0]; k++)
{
int t;
for (t = 0; t<n; t++)
{
if (last[i][t] == last[j][k + 1])
break;
}
if (t == n)
{
last[i][n] = last[j][k + 1];
n++;
}
}
}
}
}
m++;
} while (st[i][m] != '\0');
last[i][n] = '\0';
last[i][0] = --n;
lflag[i] = 1;
}
}
int deal()
{
int i, j;
int x, y;
int z; //輸入串的長度
k = 1;
s[k] = '#'; //棧置初值
for (i = 0; input[i] != '\0'; i++); //計算輸入串的長度
z = i--;
i = 0;
while ((a = input[i]) != '\0')
{
//j指向最接近棧頂的終極符
if (Terminator(s[k]))
j = k;
else
j = k - 1;
x = getIndex(s[j]);//獲取最接近棧頂的終極符在終極符表中位置
y = getIndex(a);//獲取第一個輸入符號在終極符表中位置
if (Data[x][y] == '>')//歸約
{
out(1, k, s);
printf("%c", a);
out(i + 1, z, input);
printf("規約\n");
do
{
q = s[j];
if (Terminator(s[j - 1]))
j = j - 1;
else j = j - 2;
x = getIndex(s[j]);
y = getIndex(q);
} while (Data[x][y] != '<');
int m, n, N;
for (m = j + 1; m <= k; m++)
{
for (N = 0; N<r1; N++)
for (n = 1; In_str[N][n] != '\0'; n++)
{
if (!Terminator(s[m]) && !Terminator(In_str[N][n]))
{
if (Terminator(s[m + 1]) && Terminator(In_str[N][n + 1])
&& s[m + 1] == In_str[N][n + 1])
{
s[j + 1] = In_str[N][0];
break;
}
}
else
if (Terminator(s[m]))
if (s[m] == In_str[N][n])
{
s[j + 1] = In_str[N][0];
break;
}
}
}
k = j + 1;
if (k == 2 && a == '#')
{
out(1, k, s);
printf("%c", a);
out(i + 1, z, input);
printf("結束\n");
printf("\n輸入串符合文法的定義!\n");
return 1; //輸入串符合文法的定義
}
}
else
if (Data[x][y] == '<' || Data[x][y] == '=')
{ //移進
out(1, k, s);
printf("%c", a);
out(i + 1, z, input);
printf("移進\n");
k++;
s[k] = a;
i++;
}
else
{
printf("\n輸入串不符合文法的定義!\n");
return 0;
}
}
printf("\n輸入串不符合文法的定義!\n");
return 0;
}
int deal1()
{
int i, j;
int x, y;
int z; //輸入串的長度
k = 1;
s[k] = '#'; //s棧置初值
str[k]="#"; //類似str棧置初值
for (i = 0; input[i] != '\0'; i++); //計算輸入串的長度
z = i--;
i = 0;
while ((a = input[i]) != '\0')
{
//j指向最接近棧頂的終極符
if (Terminator(s[k]))
j = k;
else
j = k - 1;
x = getIndex(s[j]);//獲取最接近棧頂的終極符在終極符表中位置
y = getIndex(a);//獲取第一個輸入符號在終極符表中位置
if (Data[x][y] == '>')//歸約
{
out1(1, k, str);
printf("%c", a);
out(i + 1, z, input);
printf("規約");
do
{
q = s[j];
if (Terminator(s[j - 1]))
j = j - 1;
else j = j - 2;
x = getIndex(s[j]);
y = getIndex(q);
} while (Data[x][y] != '<');
int m, n, N;
for (m = j + 1; m <= k; m++)
{
for (N = 0; N<r1; N++)
for (n = 1; In_str[N][n] != '\0'; n++)
{
if (!Terminator(s[m]) && !Terminator(In_str[N][n]))
{
if (Terminator(s[m + 1]) && Terminator(In_str[N][n + 1])
&& s[m + 1] == In_str[N][n + 1])
{
//printf("%s",text[N]);
//printf("%c%d->%c%d%c%c%d",In_str[N][0],m,s[m],m,s[m+1],s[m+2],m+2);
//printf("s[%d]==%c&In_str[%d][%d]==%c&s[%d]==%c&In_str[%d][%d]==%c",m,s[m],N,n,In_str[N][n],m+1,s[m+1],N,n+1,In_str[N][n+1]);
string avl=str[m];//提前保留還未規約更新的值用於輸出
s[j + 1] = In_str[N][0];//規約代替例如T->E+P
charmap[In_str[N][0]]+=1;//利用map存儲記憶該X的下標,例如X1->i
ss<<In_str[N][0];//利用ss將char轉換爲string
ss<<charmap[In_str[N][0]];//對應數字下標也轉入string方便入棧str
str[j+1]=ss.str()+"\0";//str棧內對應s棧一樣壓入對應值,不同點非終結符X加入下標X1
ss.str("");//ss清空
cout<<str[j+1]<<"->"<<avl<<str[m+1]<<str[m+2];//輸出規約對應表達式(推導式)
stringmap[str[j+1]]=str[j+1];//將轉換對應值用map存儲,例如X1=i或X1=X1那麼stringmap[X1]="i",用於四元組輸出求值
qua[quaCounts][0]=str[m+1];//存入四元組計算符,例如+
qua[quaCounts][1]=stringmap[avl];//存入四元組第一個計算數,例如E1
qua[quaCounts][2]=stringmap[str[m+2]];//存入四元組第二個計算數,例如P1
qua[quaCounts][3]=stringmap[str[j+1]];//存入四元組計算結果,例如T1
cout<<"\t\t";
cout<<"產生【"<<(quaCounts+1)<<"】:";
cout<<"("+qua[quaCounts][0]+","+qua[quaCounts][1]+","+qua[quaCounts][2]+","+qua[quaCounts][3]+")";//輸出對應直接生成的四元組
quaCounts++;//四元組數目加一
goto outer;//直接跳出全部循環
}
}
else{
if(Terminator(s[m])&&s[m]=='('&&Terminator(s[m+2])&&s[m+2]==')'){//專門爲T->(E)這種情況而設定
if (s[m] == In_str[N][n])
{
//printf("%c%d->%c",In_str[N][0],m,In_str[N][n]);
string avl=str[m];//提前保留還未規約更新的值用於輸出
s[j + 1] = In_str[N][0];//規約代替T->(E)
charmap[s[j + 1]]+=1;//利用map存儲記憶該X的下標,例如X1->i
ss<<s[j+1];//利用ss將char轉換爲string
ss<<charmap[s[j + 1]];//對應數字下標也轉入string方便入棧str
str[j+1]=ss.str()+"\0";//str棧內對應s棧一樣壓入對應值,不同點非終結符X加入下標X1
ss.str("");//ss清空
cout<<str[j+1]<<"->"<<avl<<str[m+1]<<str[m+2];//輸出規約對應表達式(推導式)
stringmap[str[j+1]]=str[j+1];//將轉換對應值用map存儲,例如X1=i或X1=X1那麼stringmap[X1]="i",用於四元組輸出求值
goto outer;//直接跳出全部循環
}
}
else if (Terminator(s[m]))//若爲終結符例如X->i
if (s[m] == In_str[N][n])
{
//printf("%c%d->%c",In_str[N][0],m,In_str[N][n]);
s[j + 1] = In_str[N][0];//將終結符i利用X規約代替
charmap[s[j + 1]]+=1;//利用map存儲記憶該X的下標,例如X1->i
ss<<s[j+1];//利用ss將char轉換爲string
ss<<charmap[s[j + 1]];//對應數字下標也轉入string方便入棧str
str[j+1]=ss.str()+"\0";//str棧內對應s棧一樣壓入對應值,不同點非終結符X加入下標X1
ss.str("");//ss清空
cout<<str[j+1]<<"->"<<In_str[N][n];//輸出規約對應表達式(推導式)
ss<<In_str[N][n];//利用ss將char轉換爲string
stringmap[str[j+1]]=ss.str();//將轉換對應值用map存儲,例如X1->i那麼stringmap[X1]="i",用於四元組輸出求值
ss.str("");//ss清空
goto outer;//直接跳出全部循環
}
}
}
}
outer://循環跳出位置
k = j + 1;//棧頂位置k更新
printf("\n");//下一行
if (k == 2 && a == '#')//規約完畢,只剩#X符號
{
out1(1, k, str);
printf("%c", a);
out(i + 1, z, input);
printf("結束\n");
printf("\n輸入串符合文法的定義!\n");
return 1; //輸入串符合文法的定義
}
}
else
if (Data[x][y] == '<' || Data[x][y] == '=')
{ //移進
out1(1, k, str);//輸出當前範圍內的棧str
printf("%c", a);
out(i + 1, z, input);
printf("移進\n");
k++;
s[k] = a;//移進賦值
//類似的棧str也對應移進賦值,此處借用ss將char轉換爲string類型
ss<<a;
str[k]=ss.str()+"\0";
ss.str("");//清空ss
i++;
}
else
{
printf("\n輸入串不符合文法的定義!\n");
return 0;
}
}
printf("\n輸入串不符合文法的定義!\n");
return 0;
}
void out(int j, int k, char *s)
{
int n = 0;
int i;
for (i = j; i <= k; i++)
{
printf("%c", s[i]);
n++;
}
for (; n<15; n++)
{
printf(" ");
}
}
void out1(int j, int k, string *str)
{
int n = 0;
int i;
for (i = j; i <= k; i++)//輸出到k位置範圍內的棧str
{
cout<<str[i];
n=n+str[i].size();
}
for (; n<15; n++)//差額空格,水平製表
{
cout<<" ";
}
}
int getIndex(char c) //求字符c在文法終極符表中的下標
{
int i;
for (i = 0; lable[i] != '\0'; i++)
{
if (c == lable[i])
return i;
}
return -1;
}
int Terminator(char c) //判斷字符c是否是終極符
{
int i;
for (i = 0; lable[i] != '\0'; i++)
{
if (c == lable[i])
return 1;
}
return 0;
}
三、結果