一、存储结构
文法规则设计如下
class LinkNode
{
public:
//规则左部
string left;
//规则右部分 abc | def | hji,一个vector<string>元素代表一种可能
vector<vector<string>> right;
}
class Processor
{
public:
//存用户输入的语句
vector<string>input;
//开始节点索引
int startWordIndex;
//开始结点值
string startNode;
//存文法
vector<LinkNode> grammers;
//终结符号
vector<string> finalWord;
//非终结符号
vector<string> nonFinalWord;
//First集合
map<string, set<string>> first;
//Follow集合
map<string, set<string>> follow;
//文件流,用于写出日志
ofstream log;
}
文法规则分为左部和右部。
左部都是非终结符号
vector<vector> right;
右边可以看成是一个二维数组。
第一列存开始符号。每一行存对应的右部。
如下图
在输入阶段
从文件中读取文法规则。
再进行处理
规则有效性
1.不可到达规则。
发现某些非终结符不在任何规则的右部出现,该非终结符称为不可到达。
2. 不可终止
发现文法中,某些非终结符,由它不能导出终结符号串,则说明不能终止。
删除上面说的规则。
也包括:
3. A->A,这样的是有害规则
// A->A,这样的是有害规则
if(str.length() == 4 && str[0] == str[3])
{
log << "有害规则,已经删除\n";
continue;
}
// A->A,这样的是有害规则
if(str.length() == 4 && str[0] == str[3])
{
log << "有害规则,已经删除\n";
continue;
}
//设置开始结点的值,存储文法起点的值。
string startNode ;
** grammers就是 vector 和vector的嵌套**
文法规则中的或符号 |
用splitString(rights,“|”), 进行分割
横着拉出一条链,就是一句文法规则了。
vector temp.push_back(s + str[i]);
//E->abc|def从下标3开始,都是右边部分
string rights = str.substr(3);
vector<string> toNodes = splitString(rights, "|");
//新建一个文法规则的结点
LinkNode node(left);
//将分割好的字串添加到文法规则中
for(string str:toNodes)
{
vector<string> temp;
for(size_t i = 0; i < str.length(); i++)
{
string s = "";
temp.push_back(s + str[i]);
}
node.insert(temp);
}
grammers.push_back(node);
}
消除间接左递归
例如:
S -> Qc|c
Q -> Rb|b|Ra|Rs|Rkjkdfl
R -> Sa|a
可以发现上面是间接左递归
** 自顶向下的分析方法。**
把第三个式子右边代换第二个式子中的R。
在process环节已经把|删除了,每一个组合都单独保存。比如Rb, b, Rs, Rkjjkl
一个for循环, 把每一个组合前的第一个大写(非终结符号进行比较)
如果发现和其他规则中的左边的非终结符号相同, 则说明需要替换。
得到转换后的结果是:
Q-> (Sa|a)b| b| (Sa|a)a | (Sa|a)s |(Sa|a)kjkdfl
替换过程的代码:
/*
* 例子:S -> Qc|c
* R -> Sa|a
* Q -> Rbc|b
* 设 i : Q -> Rbc|b
* j : R -> Sa|a
*/
亮点是:
after.insert(after.end(),other.begin(),other.end());
这里第一个参数,是指的插入的位置, 后面表示区间,从other.begin() 开始到 other.end() 结束 。
vector< vector<string> > cont;
vector< vector<string> > &right1 = grammers[i].right;
vector< vector<string> > &right2 = grammers[j].right;
string check = grammers[j].left;
vector<vector<string>>::iterator it1 = right1.begin();
vector<vector<string>>::iterator it2 = right2.begin();
for(; it1 != right1.end(); it1++)
{
// i的右边第一个元素与j的非终结符号相同,代入
if(it1->at(0) == check)
{
// 将 j 代入 i
// 例如 此时 *it1 = {R,b,c}
// 而 right2 = {{S,a},{a}}
// 代入后 为 {S,a,b,c}、{a,b,c}
//先取出i的右边除了R外的所有元素
vector<string> other;
for(size_t k = 1; k < (*it1).size(); k++) // 将i中R之后的符号代入
{
other.push_back((*it1)[k]);
}
// 此时 other = <{b},{c}>
for(; it2 != right2.end(); it2++)
{
//用于临时存储带入后产生的式子
vector<string> after = *it2;//这里遍历right2,第一次{Sa},第二次{a}
//将other的内容分别插入after后面,因为other可能不只一个字符串。
after.insert(after.end(),other.begin(),other.end());
//代入后 为 {S,a,b,c}、{a,b,c}
cont.push_back(after);
}
}
}
//接下来需要修改i:Q->Rb|b的右边部分
//先把右边部分删掉
size_t nn = right1.size();
while(nn--)
{
//如果是Rbc,就删掉,因为cont中已经有替换好的部分{{S,a,b,c}{a, b}}了
//如果是b,就先删掉,并添加到cont中,因为cont中没有b
if(right1.begin()->at(0) != check)
cont.push_back(*right1.begin());
right1.erase(right1.begin());
}
//执行完后Q->{}
//然后把cont中的内容添加到Q->的右边
//Q - > Sabc | ab | b
for( size_t i = 0; i < cont.size(); i++)
{
right1.push_back(cont[i]); // 加入了这两个新的 Sabc | ab
}
以上步骤完成了替换工作
所有的递归都是直接左递归了。不存在间接左递归。
直接左递归的消除
按照下面的思路来做,比较机械。
也是自顶而下的处理逻辑
遇到直接左递归A,记录A后面的a字符
一般思路都是先变成间接左递归,再变成直接左递归。
下面是消除直接左递归的思路
我们会发现,其中每一个部分开头都是比较顶层的非终结符。
for(size_t i = 0; i < grammers.size(); i++){
//左边部分
string check = grammers[i].left;
//右边部分
vector<vector<string>> temp = grammers[i].right;
vector<vector<string>>::iterator it = temp.begin();
//新的结点
//如: A -> Aa|b
//那么新建一个结点A'
string tt = check + "'";
bool flag = true;
//判断这条规则是否包含左递归
for(; it != temp.end(); it++)
{
// 左 = 右边第一个,发现直接左递归
if(it->at(0) == check)
{
//新建一个文法结点A'
grammers.push_back(LinkNode(tt));
flag = false;
break;
}
}
//不包含左递归
if(flag)
continue;
//包含左递归的情况:
//临时存储A'的右边
vector<vector<string>> cont;
//指向最后一条文法结点
vector<vector<string>> &ss = grammers[grammers.size()-1].right;
vector<string> eplision;
eplision.push_back("@");
//ss: {{"@"}}
ss.push_back(eplision);
vector<vector<string>>&temp1 = grammers[i].right;
while(!temp1.empty())
{
//对于A->Aa|b,如果现在处理的是Aa
if(temp1.begin()->at(0) == check) // 发现左递归元素
{
vector<string> vt;
//就把a复制到vt中
//vt:{a}
vt.assign(temp1.begin()->begin()+1,
temp1.begin()->end());
//在末尾增加A'
//vt:{aA'}
vt.push_back(tt);
//ss:{{aA'},{"@"}}
ss.push_back(vt);
}
else // 否则,应该修改原语法,修改成:A->bA'
{
vector<string> vt;
//vt:{b}
vt.assign(temp1.begin()->begin(),
temp1.begin()->end());
//vt:{bA'}
vt.push_back(tt);
//cont:{{bA'}}
cont.push_back(vt);
}
temp1.erase(temp1.begin());
}
//这时A->{}
//cont:{{bA'}}
//用cont里面的内容取代A的右边
for(size_t i = 0; i < cont.size(); i++)
{
temp1.push_back(cont[i]);
}
}