目的:
特定の文法に従ってLL(1)分析プログラムをコンパイルおよびデバッグして、入力シンボル文字列を分析します。この実験の目的は、予測分析LL(1)分析方法の理解を深めることです。
単純な要件:
少なくとも次の既知の文法を実行し、LL(1)分析方法を使用して入力記号文字列を分析します。
(1)E-> TG
(2)G-> + TG
(3)G->ε
(4)T-> FS
(5)S-> * FS
(6)S->ε
(7)F->(E)
(8)F-> I
高い要件:
(1)文法を手動で入力します。
(2)文法の非終端記号を示すFOLLOWセット。
(3)オプションのルールセットを表示します。
(4)予測分析テーブルを作成します。
(5)任意の入力記号文字列を分析する
実験手順:
最初のセットのアルゴリズムのアイデア:
プロダクションの右側の最初の文字が終端記号である場合、左側の最初のセットでカウントされます。プロダクションの右側の最初の文字が非終端記号である場合、次の手順を実行して、非終端記号の最初のセットの非終端記号$最初の左のセットの非記録部分の最初のセット
$が存在する場合、プロダクションポインターは、$がある場合は右を指し
、トラバーサルはプロダクションを停止します。プロダクション
の右端の
非終端記号に到達した場合は、次のプロダクションに移動し、左側の最初のセットに$を追加して、配列内で繰り返される最初のセットの終端記号を処理します。
アルゴリズム考えフォローセット:
文法Gの各非終端記号Aについて、FOLLOW(A)を構成する方法は、各フォローが増加しなくなるまで継続的に次のルールを使用することである。
開始シンボルSについて文法、#を設定FOLLOW(S);
A-> aBbがプロダクションの場合、FIRST(b)\(ε)をFOLLOW(B);に追加します。
A-> aBがプロダクションである場合、またはA-> aBbがプロダクションであり、b =>ε(つまり、ε∈FIRST(b))の場合、FOLLOW(A)をFOLLOW(B)に追加します。
予測分析テーブルを生成するアルゴリズムのアイデア:
分析テーブルMを構築するためのアルゴリズムは次のとおりです:
文法Gの各生成A-> aに対して2番目と3番目のステップを実行します;
各端末a∈FIRST(a)に対して、 A-> aがM [A、a]に追加されます
。ε∈FIRST(a)の場合、任意のb∈FOLLOW(A)およびA-> aがM [A、b]に追加されます。
すべて未定義のM [ A、a]にはエラーフラグが付いています。
シンボル文字列の分析プロセス:
予測分析プログラムのマスター制御プログラムは、STACKスタックのトップシンボルXと現在の入力シンボルに従っていつでも動作します。任意の(X、a)に対して、マスター制御プログラム
は、毎回続く3つの可能なアクションの1つ
。X= a =”#”の場合、分析は成功したと宣言され、分析プロセスは停止します
。X= a≠”#”の場合、Xは上部から排出されます。 STACKスタック、および次の1つの入力シンボルを指します
。Xが非終端シンボルの場合は、分析テーブルMを確認します。M[A、a]がXに関するプロダクションを格納している場合は、最初にXを上から追放します。 STACKスタック、次にプロダクションを配置する右のシンボル文字列が逆の順序で1つずつSTACKスタックにプッシュされます(右のシンボルがεの場合、スタックに何もプッシュされないことを意味します)。スタックに生成する場合は、それに応じて生成する必要があります。「エラーフラグ」がM [A、a]に格納されている場合は、エラー診断プログラムERRORを呼び出します。
実験コード:
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<stack>
#include<set>
#include<cstring>
using namespace std;
map<char, int>getnum; //map容器有键跟键对应的值两个变量,组成一个pair对象。
char getchar_[100]; //获得对应字符
vector<string>proce; //定义了一个容器向量 用来存储产生式的。
int table[100][100]; //构建预测分析表
int num = 0;
int numvt = 0; //numvt是终结符集合,0是‘#’,numvt表空字
string first[100];
string follow[200];
void readin()//将所需要的各类资料读取进去.
{
memset(table, -1, sizeof(table));//将table的地址空间全部置-1,memset是内存重置函数,第一个参数是要重置的首地址,第二个是要重置的结果,第三个是重置的大小,sizeof是计算table内存空间大小的意思
getnum['#'] = 0;
getchar_[0] = '#';
cout << "请输入所有的终结符:" << endl;
char x;
do
{
cin >> x;
getnum[x] = ++num;
getchar_[num] = x;
} while (cin.peek() != '\n');//cin.peek()的返回值是一个char型的字符,其返回值是指针指向的当前字符,但它只是观测指针停留在当前位置并不后移;
numvt = ++num;
getnum['@'] = numvt; //空字,因为ε无法显示所以用@来代替
getchar_[num] = ('@');
cout << "请输入所有非终结符:" << endl;
do
{
cin >> x;
getnum[x] = ++num;
getchar_[num] = x;
} while (cin.peek() != '\n');
cout << "输入产生式集合(空字用‘@’表示),以‘end’结束:" << endl;
string pro;
while (cin >> pro && pro != "end")
{
string ss;
ss += pro[0];
for (int i = 3; i < pro.size(); i++)//从3开始是因为前面有?->
{
if (pro[i] == '|')
{
proce.push_back(ss);
ss.clear();
ss += pro[0];
}
else
{
ss += pro[i];
}
}
proce.push_back(ss);//作用是字符串之后插入一个字符。
}
}
void bingji(string& a, string b) //a=a or b 取a,b并集赋值给a
{
set<char>se;
for (int i = 0; i < a.size(); i++)
se.insert(a[i]);
for (int i = 0; i < b.size(); i++)
se.insert(b[i]);
string ans;
set<char>::iterator it;//这条语句定义了一个名为it的变量,iterator为迭代器:提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。
for (it = se.begin(); it != se.end(); it++)
ans += *it;
a = ans;
}
string get_f(int vn, int& has_0) //推出vn能推出的不含空字的vt集合,并且判断vn能否推出空字
{
if (vn == numvt)has_0 = 1; //为@ 则赋值使得可以继续下去
if (vn < numvt)return first[vn];//为终结符,归入first中
string ans;
for (int i = 0; i < proce.size(); i++)
{
if (getnum[proce[i][0]] == vn)//如果产生式左部等于非终结符,就进入非终结符中继续查询
ans += get_f(getnum[proce[i][1]], has_0);
}
return ans;
}
void getfirst()
{
for (int i = 1; i <= numvt; i++) //终结符,first集是其本身。
{
first[i] += ('0' + i);
}
for (int j = 0; j < proce.size(); j++) //扫描所有产生式
{
int k = 0; //k扫瞄该产生式
int has_0 = 0;
do {
has_0 = 0;
k++;
if (k == proce[j].size()) //推到最后一个了,则附加空字
{
first[getnum[proce[j][0]]] += ('0' + numvt);
break;
} //合并之
bingji(first[getnum[proce[j][0]]], get_f(getnum[proce[j][k]], has_0));//如果前面没有空集,就将空集排除
} while (has_0); //到无法推出空字为止
}
}
void print_first()
{
cout << "first集:" << endl;
for (int i = numvt + 1; i <= num; i++)
{
cout << "first [" << getchar_[i] << "]: ";
for (int j = 0; j < first[i].size(); j++)
cout << getchar_[first[i][j] - '0'] << " ";
cout << endl;
}
cout << endl;
}
void getfollow()
{
bingji(follow[getnum[proce[0][0]]], "0"); //先添加‘#’;
for (int j = 0; j < proce.size(); j++) //扫所有产生式
{
for (int jj = 1; jj < proce[j].size(); jj++) //每个非终结符的follow集
{
if (getnum[proce[j][jj]] <= numvt)continue; //为终结符,vt无follow集
int k = jj; int has_0;
do
{
has_0 = 0;
k++;
if (k == proce[j].size()) //都能推出空字,follow集=产生式左边的vn,
{
bingji(follow[getnum[proce[j][jj]]], follow[getnum[proce[j][0]]]);
break;
}
bingji(follow[getnum[proce[j][jj]]], get_f(getnum[proce[j][k]], has_0));
} while (has_0);
}
}
}
void gettable() //得预测分析表
{
for (int i = 0; i < proce.size(); i++) //扫所有产生式
{
if (proce[i][1] == '@') //直接推出空字的,把follow集加进去(follow集=产生式左边的follow中元素填)
{
string flw = follow[getnum[proce[i][0]]];
for (int k = 0; k < flw.size(); k++)
{
table[getnum[proce[i][0]]][flw[k] - '0'] = i;
}
}
string temps = first[getnum[proce[i][1]]];
for (int j = 0; j < temps.size(); j++) //考察first集
{
if (temps[j] != ('0' + numvt))//没有空字终结符
{
table[getnum[proce[i][0]]][temps[j] - '0'] = i;//字符型减'0'可以得到整数型
}
else //有空字终结符的,考察follw集
{
string flw = follow[getnum[proce[i][1]]];
for (int k = 0; k < flw.size(); k++)
{
table[getnum[proce[i][0]]][flw[k] - '0'] = i;
}
}
}
}
}
string get_proce(int i) //由对应下标获得对应产生式。
{
if (i < 0)return " "; //无该产生式
string ans;
ans += proce[i][0];
ans += "->";
for (int j = 1; j < proce[i].size(); j++)
ans += proce[i][j];
return ans;
}
void print_table()
{
cout << "预测分析表:" << endl;
for (int i = 0; i < numvt; i++)
cout << '\t' << getchar_[i];
cout << endl;
for (int i = numvt + 1; i <= num; i++)
{
cout << getchar_[i];
for (int j = 0; j < numvt; j++)
{
cout << '\t' << get_proce(table[i][j]);
}
cout << endl;
}
cout << endl;
}
void print_follow()
{
cout << "follow集:" << endl;
for (int i = numvt + 1; i <= num; i++)
{
cout << "follow [" << getchar_[i] << "]: ";
for (int j = 0; j < follow[i].size(); j++)
cout << getchar_[follow[i][j] - '0'] << " ";
cout << endl;
}
cout << endl;
}
string word;
string shuchu;
bool analyze() //总控,分析字word的合法性,若合法,输出所有产生式。
{
stack<char>sta;
sta.push('#');
sta.push(proce[0][0]);
shuchu.push_back('#');
shuchu.push_back(proce[0][0]);
int i = 0;
while (!sta.empty())
{
int cur = sta.top();//取出栈顶元素
sta.pop(); //删除栈顶元素
if (cur == word[i]) //是终结符,推进
{
word[i] = NULL;
if (!shuchu.empty())
shuchu.pop_back();
i++;
}
else if (cur == '#') //成功,结束
{
return 1;
}
else if (table[getnum[cur]][getnum[word[i]]] != -1) //查表,最初预测分析表全定义为-1
{
int k = table[getnum[cur]][getnum[word[i]]];
cout << shuchu << " ";
cout << word << "#" << " ";
cout << proce[k][0] << "->";
for (int j = 1; j < proce[k].size(); j++)
cout << proce[k][j];
cout << endl; shuchu.pop_back();
for (int j = proce[k].size() - 1; j > 0; j--) //逆序入栈
{
if (proce[k][j] != '@') {
sta.push(proce[k][j]);
shuchu.push_back(proce[k][j]);
}
}
}
else //失败!
{
return 0;
}
}
return 1;
}
int main()
{
readin();
getfirst();
getfollow();
getfollow();
gettable();
print_first();
print_follow();
print_table();
cout << "请输入字:" << endl;
cin >> word;
if (analyze())
cout << "succeed!该字有效,所用产生式如上。" << endl;
else cout << "error!" << endl;
system("pause");
return 0;
}