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 集中,直至计算完成。

使用方法:

  1. 准备文法的产生式集合(production1),以及已计算好的 FIRST 集(firsts1)。
  2. 创建空的 FOLLOW 集字典(follows1)和已访问非终结符集合(visited)。
  3. 调用 GetFollow 函数,传入要计算 FOLLOW 集的目标非终结符(symbol)、产生式集合(production1)、FIRST 集(firsts1)、FOLLOW 集字典(follows1)和已访问非终结符集合(visited)。
  4. GetFollow 函数会返回目标非终结符的 FOLLOW 集,保存在 follows1 字典中。

算法的核心思想:

  1. 递归计算: GetFollow 函数会递归调用自身,计算该非终结符后面的非终结符的 FOLLOW 集,直到到达产生式末尾或遇到终结符。
  2. FOLLOW 集的定义规则: 根据 FOLLOW 集的定义,算法会将符合条件的符号(如产生式末尾的非终结符的 FOLLOW 集、该非终结符后面的终结符或该非终结符后面的非终结符的 FIRST 集)加入目标非终结符的 FOLLOW 集中。
  3. 避免重复计算: visited 集合用来记录已访问过的非终结符,避免重复计算它们的 FOLLOW 集。

注意:

  1. 该算法假设 production1 字典中存储的产生式是正确的,并已经进行了必要的预处理(例如,消除了左递归)。
  2. IsTerminalIsReachEmpty 函数用于判断符号是否为终结符以及是否可以推出空串,需要根据具体的文法和实现进行定义。
  3. 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 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录