计算文法中单个非终结符的 FOLLOW 集算法
private void GetFollow(string symbol, Dictionary<string, List<string>> production1, Dictionary<string, List<string>> firsts1, Dictionary<string, List<string>> follows1, HashSet<string> visited)
{
// 如果该非终结符的 FOLLOW 集已经被计算出,则直接返回
if (follows1.ContainsKey(symbol))
{
return;
}
follows1.Add(symbol, new List<string>());
visited.Add(symbol);
// 如果该非终结符是文法的开始符号,则将 $ 加入其 FOLLOW 集中
if (symbol.Equals(startSymbol))
{
if (!follows1[symbol].Contains('$'))
follows1[symbol].Add('$');
}
// 遍历产生式,计算 FOLLOW 集
foreach (var key in production1.Keys)
{
foreach (var prod in production1[key])
{
if (prod.Contains(symbol))
{
int index = prod.IndexOf(symbol);
// 如果该非终结符位于产生式末尾,则将该产生式左侧非终结符的 FOLLOW 集加入其 FOLLOW 集中
if (index == prod.Length - 1)
{
if (!visited.Contains(key))
{
GetFollow(key, production1, firsts1, follows1, visited);
}
foreach (var f in follows1[key])
{
if (!follows1[symbol].Contains(f))
follows1[symbol].Add(f);
}
}
// 如果该非终结符后面是终结符,则将该终结符加入其 FOLLOW 集中
else if (IsTerminal(prod[index + 1]))
{
if (!follows1[symbol].Contains(prod[index + 1].ToString()))
follows1[symbol].Add(prod[index + 1].ToString());
}
// 如果该非终结符后面是非终结符,则将该非终结符的 FIRST 集中除空串外的所有符号加入其 FOLLOW 集中
else
{
foreach (var f in firsts1[prod[index + 1].ToString()])
{
if (!f.Equals('#') && !follows1[symbol].Contains(f))
follows1[symbol].Add(f);
}
// 如果该非终结符后面的所有非终结符的 FIRST 集都包含空串,则将该产生式左侧非终结符的 FOLLOW 集加入其 FOLLOW 集中
if (IsReachEmpty(prod[index + 1].ToString(), production1))
{
if (!visited.Contains(key))
{
GetFollow(key, production1, firsts1, follows1, visited);
}
foreach (var f in follows1[key])
{
if (!follows1[symbol].Contains(f))
follows1[symbol].Add(f);
}
}
}
}
}
}
}
该算法通过递归和循环遍历产生式,计算给定文法中单个非终结符的 FOLLOW 集。它根据 FOLLOW 集的定义规则,逐步将符合条件的符号加入目标非终结符的 FOLLOW 集中,直至计算完成。
使用方法:
- 准备文法的产生式集合(
production1),以及已计算好的 FIRST 集(firsts1)。 - 创建空的 FOLLOW 集字典(
follows1)和已访问非终结符集合(visited)。 - 调用
GetFollow函数,传入要计算 FOLLOW 集的目标非终结符(symbol)、产生式集合(production1)、FIRST 集(firsts1)、FOLLOW 集字典(follows1)和已访问非终结符集合(visited)。 GetFollow函数会返回目标非终结符的 FOLLOW 集,保存在follows1字典中。
算法的核心思想:
- 递归计算:
GetFollow函数会递归调用自身,计算该非终结符后面的非终结符的 FOLLOW 集,直到到达产生式末尾或遇到终结符。 - FOLLOW 集的定义规则: 根据 FOLLOW 集的定义,算法会将符合条件的符号(如产生式末尾的非终结符的 FOLLOW 集、该非终结符后面的终结符或该非终结符后面的非终结符的 FIRST 集)加入目标非终结符的 FOLLOW 集中。
- 避免重复计算:
visited集合用来记录已访问过的非终结符,避免重复计算它们的 FOLLOW 集。
注意:
- 该算法假设
production1字典中存储的产生式是正确的,并已经进行了必要的预处理(例如,消除了左递归)。 IsTerminal和IsReachEmpty函数用于判断符号是否为终结符以及是否可以推出空串,需要根据具体的文法和实现进行定义。startSymbol表示文法的开始符号,需要提前定义。
示例: 假设文法的产生式为:
S -> AB
A -> a | Aa
B -> b
那么调用 GetFollow('A', production1, firsts1, follows1, visited) 后,follows1['A'] 将包含 {'b', '$'},因为 A 的 FOLLOW 集包含 B 的 FIRST 集(b)和 S 的 FOLLOW 集($)。
原文地址: https://www.cveoy.top/t/topic/f1Tp 著作权归作者所有。请勿转载和采集!