SLR 文法分析表构造代码详解:移进-归约和归约-归约冲突处理

本文将详细介绍 SLR 文法分析表构造的代码实现,并重点讲解如何处理移进-归约和归约-归约冲突。

SLR 分析表构造代码

以下代码展示了 SLR 分析表构造的实现,主要是在 LRAnaly() 函数中进行修改。

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++)
    {
        SLRNode item = LRobjNum[proitemset[Gy_itemset[i]].Container[0]];
        if (item.pos == item.production.Right.Length) // 如果是完整的归约项目
        {
            HashSet<char> followSet = Follow(item.production.Left); // 获取该产生式左部的Follow集
            foreach (char c in followSet)
            {
                int CID = FindID(Echar, c);
                SLRAna[Gy_itemset[i]][CID] = new Table('r', item.production.Index); // 添加归约状态
            }
        }
    }

    for (int i = 0; i < Pindex; i++)
    {
        if (isFinalsymbol(dfa[i].symbol))//symbol为非终结符  添加状态N
        {
            int CID = FindID(Nchar, dfa[i].symbol);
            tnode = new Table('N', dfa[i].to);
            SLRAna[dfa[i].from][CID + Echar.Count] = tnode;
        }
        else //不是归约项目 添加状态S
        {
            int CID = FindID(Echar, dfa[i].symbol);
            if (SLRAna[dfa[i].from][CID].type == 'r') // 如果存在归约状态
            {
                HashSet<char> followSet = Follow(LRproNum[dfa[i].to].Left); // 获取该产生式左部的Follow集
                foreach (char c in followSet)
                {
                    int followCID = FindID(Echar, c);
                    SLRAna[dfa[i].from][followCID] = new Table('r', LRproNum[dfa[i].to].Index); // 添加归约状态
                }
            }
            else
            {
                tnode = new Table('S', dfa[i].to);
                SLRAna[dfa[i].from][CID] = tnode;
            }
        }
    }
}

Follow 集和 First 集的计算

在上述代码中,我们使用了 Follow() 函数和 First() 函数来计算 Follow 集和 First 集。

public HashSet<char> Follow(char symbol)
{
    HashSet<char> followSet = new HashSet<char>();
    if (symbol == LRproNum[0].Left) // 如果是文法起始符号
    {
        followSet.Add('#');
    }
    for (int i = 0; i < proitemset.Count; i++)
    {
        for (int j = 0; j < proitemset[i].Container.Count; j++)
        {
            SLRNode item = LRobjNum[proitemset[i].Container[j]];
            for (int k = item.pos + 1; k <= item.production.Right.Length; k++)
            {
                if (k == item.production.Right.Length) // 如果是完整的归约项目
                {
                    HashSet<char> tempSet = Follow(item.production.Left);
                    followSet.UnionWith(tempSet);
                }
                else
                {
                    HashSet<char> firstSet = First(item.production.Right[k]);
                    if (firstSet.Contains('#')) // 如果该符号可以推导出空串
                    {
                        firstSet.Remove('#');
                        followSet.UnionWith(firstSet);
                        if (k == item.pos + 1) // 如果是该产生式右部的第一个符号
                        {
                            HashSet<char> tempSet = Follow(item.production.Left);
                            followSet.UnionWith(tempSet);
                        }
                    }
                    else
                    {
                        followSet.UnionWith(firstSet);
                        break;
                    }
                }
            }
        }
    }
    return followSet;
}

public HashSet<char> First(char symbol)
{
    HashSet<char> firstSet = new HashSet<char>();
    if (isFinalsymbol(symbol)) // 如果是终结符
    {
        firstSet.Add(symbol);
    }
    else // 如果是非终结符
    {
        for (int i = 0; i < proitemset.Count; i++)
        {
            if (LRproNum[proitemset[i].Container[0]].Left == symbol) // 如果找到了该非终结符对应的产生式
            {
                SLRNode item = LRobjNum[proitemset[i].Container[0]];
                for (int j = 0; j < item.production.Right.Length; j++)
                {
                    HashSet<char> tempSet = First(item.production.Right[j]);
                    firstSet.UnionWith(tempSet);
                    if (!tempSet.Contains('#')) // 如果该符号不能推导出空串
                    {
                        break;
                    }
                    if (j == item.production.Right.Length - 1) // 如果是该产生式右部的最后一个符号
                    {
                        firstSet.Add('#');
                    }
                }
                break;
            }
        }
    }
    return firstSet;
}

处理冲突

在 SLR 分析表构造过程中,需要考虑移进-归约冲突和归约-归约冲突。

  • 移进-归约冲突: 当某个项目集合中存在归约状态,同时也有移进状态时,就会发生移进-归约冲突。处理方法是将该状态改为归约状态。
  • 归约-归约冲突: 当某个项目集合中存在多个归约状态时,就会发生归约-归约冲突。处理方法是将该状态改为其中一个归约状态,或者根据具体情况进行其他处理。

代码中的冲突处理

在上述代码中,我们通过以下方式处理冲突:

  1. 在添加移进状态时,如果该位置已经存在归约状态,则使用该产生式左部的 Follow 集进行判断,如果存在相同的终结符,则将其修改为归约状态。
  2. 如果某个项目集合中存在多个归约状态,则使用所有产生式左部的 Follow 集进行比较,如果存在相同的终结符,则将其修改为归约状态。

总结

本文详细介绍了 SLR 文法分析表构造的代码实现,并重点讲解了如何处理移进-归约和归约-归约冲突,希望对读者理解 SLR 分析表构造有所帮助。

需要注意的是,SLR 分析表构造方法无法处理所有文法,对于存在冲突无法解决的文法,需要使用更强大的分析方法,例如 LALR 或 LR(1) 分析方法。

SLR 文法分析表构造代码详解:移进-归约和归约-归约冲突处理

原文地址: https://www.cveoy.top/t/topic/f0ul 著作权归作者所有。请勿转载和采集!

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