SLR 文法分析表构造 - 详解及代码实现
SLR 文法分析表构造 - 详解及代码实现
本篇文章将深入探讨 SLR 文法分析表的构造过程,并提供详细的代码实现,帮助读者理解如何利用 SLR 算法构建分析表,以及如何处理移进-归约冲突和归约-归约冲突。
关键步骤
- 构建项目集族:通过对产生式进行扩展,生成项目集族,每个项目集代表分析过程中的一个状态。
- 构造 DFA:利用项目集族,构建 DFA(确定有限自动机),每个状态对应一个项目集,每个转移对应一个终结符或非终结符。
- 构建 Follow 集:计算每个非终结符的 Follow 集,用于判断归约时应采取的动作。
- 构建 SLR 分析表:根据项目集族、DFA 和 Follow 集,构建 SLR 分析表,表中每个元素对应一个状态和一个输入符号,决定应执行的操作(移进、归约或接受)。
代码实现
// SLRNode 类表示一个产生式,包含左部和右部
public class SLRNode
{
public string Left;
public string Right;
public SLRNode(string Left, string Right)
{
this.Left = Left;
this.Right = Right;
}
}
// SLRitemsets 类表示一个项目集,包含一个项目列表
public class SLRitemsets
{
public List<int> Container = new List<int>(100);
// 记录项目在项目集合中的序号
}
// DFA 结点
public struct DFA
{
public int from;
public char symbol;
public int to;
public DFA(int from, char symbol, int to)
{
this.from = from;
this.symbol = symbol;
this.to = to;
}
}
// 分析表 结点
public class Table
{
public bool error; // 是否为 ERROR
public char type; // 结点类型
public int id; // 数值
public Table()
{
this.error = true;
}
public Table(char type, int id)
{
this.type = type;
this.id = id;
this.error = false;
}
}
// ... 其他代码 ...
// SLR 分析函数
public void SLRAnaly()
{
Table tnode = new Table();
SLRAna = new Table[proitemset.Count][];
for (int i = 0; i < proitemset.Count; i++)
SLRAna[i] = new Table[Echar.Count + Nchar.Count];
for (int i = 0; i < proitemset.Count; i++) // 初始化 赋予 ERROR 属性
for (int j = 0; j < Echar.Count + Nchar.Count; j++) // 为终结符加 r 状态
SLRAna[i][j] = tnode;
tnode = new Table('A', 0);
SLRAna[1][FindID(Echar, '#')] = tnode; // 项目集 1 必定是接受项目 构建 [1][#]:acc 的情况 先直接赋值好 dfa 里没有
for (int i = 0; i < Gy_itemset.Count; i++) // 归约项目
{
foreach (int item in proitemset[Gy_itemset[i]].Container) // 遍历项目集中的项目
{
SLRNode node = SLRobjNum[item];
if (node.Right != "#") // 不是空产生式
{
char last = node.Right[node.Right.Length - 1]; // 取出最后一个符号
if (isFinalsymbol(last)) // 最后一个符号是终结符
{
int CID = FindID(Echar, last);
if (SLRAna[Gy_itemset[i]][CID].error) // 没有冲突
{
tnode = new Table('r', Find_pro(node)); // 添加状态 r
SLRAna[Gy_itemset[i]][CID] = tnode;
}
else // 冲突
{
RStr_ANA += "移进-归约冲突:" + Gy_itemset[i].ToString() + "," + last.ToString() + "\r\n";
}
}
else // 最后一个符号是非终结符
{
int CID = FindID(Nchar, last);
if (SLRAna[Gy_itemset[i]][CID + Echar.Count].error) // 没有冲突
{
tnode = new Table('r', Find_pro(node)); // 添加状态 r
SLRAna[Gy_itemset[i]][CID + Echar.Count] = tnode;
}
else // 冲突
{
RStr_ANA += "移进-归约冲突:" + Gy_itemset[i].ToString() + "," + last.ToString() + "\r\n";
}
}
}
else // 是空产生式
{
foreach (char c in Follow(node.Left)) // 遍历左部符号的 Follow 集
{
int CID;
if (isFinalsymbol(c)) // 是终结符
{
CID = FindID(Echar, c);
if (SLRAna[Gy_itemset[i]][CID].error) // 没有冲突
{
tnode = new Table('r', Find_pro(node)); // 添加状态 r
SLRAna[Gy_itemset[i]][CID] = tnode;
}
else // 冲突
{
RStr_ANA += "移进-归约冲突:" + Gy_itemset[i].ToString() + "," + c.ToString() + "\r\n";
}
}
else // 是非终结符
{
CID = FindID(Nchar, c);
if (SLRAna[Gy_itemset[i]][CID + Echar.Count].error) // 没有冲突
{
tnode = new Table('r', Find_pro(node)); // 添加状态 r
SLRAna[Gy_itemset[i]][CID + Echar.Count] = tnode;
}
else // 冲突
{
RStr_ANA += "移进-归约冲突:" + Gy_itemset[i].ToString() + "," + c.ToString() + "\r\n";
}
}
}
}
}
}
for (int i = 0; i < Pindex; i++) // 移进项目
{
if (isFinalsymbol(dfa[i].symbol)) // symbol 为非终结符
{
int CID = FindID(Nchar, dfa[i].symbol);
if (SLRAna[dfa[i].from][CID + Echar.Count].error) // 没有冲突
{
tnode = new Table('N', dfa[i].to);
SLRAna[dfa[i].from][CID + Echar.Count] = tnode;
}
else // 冲突
{
RStr_ANA += "归约-归约冲突:" + dfa[i].from.ToString() + "," + dfa[i].symbol.ToString() + "\r\n";
}
}
else // 不是归约项目
{
int CID = FindID(Echar, dfa[i].symbol);
if (SLRAna[dfa[i].from][CID].error) // 没有冲突
{
tnode = new Table('S', dfa[i].to);
SLRAna[dfa[i].from][CID] = tnode;
}
else // 冲突
{
RStr_ANA += "移进-移进冲突:" + dfa[i].from.ToString() + "," + dfa[i].symbol.ToString() + "\r\n";
}
}
}
}
// Follow 集构造函数
public void FollowConstruct()
{
FollowSet = new List<char>[Nchar.Count];
for (int i = 0; i < Nchar.Count; i++)
FollowSet[i] = new List<char>();
FollowSet[0].Add('#'); // 开始符号的 Follow 集中添加 #
bool changed = true;
while (changed) // 直到 Follow 集不再改变
{
changed = false;
for (int i = 0; i < SLRproNum.Count; i++) // 遍历所有产生式
{
string right = SLRproNum[i].Right;
for (int j = 0; j < right.Length; j++) // 遍历产生式的右部
{
char c = right[j];
if (isFinalsymbol(c)) // 是终结符,跳过
continue;
int index = FindID(Nchar, c);
string tmp = right.Substring(j + 1);
if (tmp.Length == 0) // A->αB
{
foreach (char ch in FollowSet[i]) // 将 Follow(A) 加入 Follow(B)
{
if (!FollowSet[index].Contains(ch))
{
FollowSet[index].Add(ch);
changed = true;
}
}
}
else // A->αBβ
{
List<char> first = First(tmp + FollowSet[i]); // 求出 First(βFollow(A))
foreach (char ch in first) // 将 First(βFollow(A)) 加入 Follow(B)
{
if (!FollowSet[index].Contains(ch))
{
FollowSet[index].Add(ch);
changed = true;
}
}
}
}
}
}
}
// ... 其他相关函数 ...
应对冲突
SLR 算法可能会遇到移进-归约冲突和归约-归约冲突。
- 移进-归约冲突:在某些情况下,对于同一个状态和同一个输入符号,分析表中既包含移进动作,也包含归约动作。这种情况通常是因为存在歧义的语法规则。解决方法是使用更强大的分析方法,例如 LALR 或 LR(1)。
- 归约-归约冲突:在某些情况下,对于同一个状态和同一个输入符号,分析表中包含多个归约动作。这种情况通常是因为语法规则存在重复。解决方法是修改语法规则,消除重复。
小结
本文详细介绍了 SLR 文法分析表的构造过程,并提供了关键代码实现。读者可以通过学习本文内容,掌握 SLR 算法的原理,并了解如何构建分析表以及如何处理冲突。SLR 算法是编译器构造中常用的分析方法,对理解编译原理具有重要意义。
原文地址: https://www.cveoy.top/t/topic/f0uS 著作权归作者所有。请勿转载和采集!