LR 分析器实现:C# 代码详解及原理步骤

本文将深入解析两段 C# 代码,阐述 LR 分析器实现的原理及步骤。代码展示了如何使用 LR 分析表对输入句子进行语法分析,并通过一个简单的例子进行说明。

代码 1:按钮点击事件处理

private void button6_Click(object sender, EventArgs e)//分析句子
{
    if (textBox1.Text.Length == 0)
    {
        MessageBox.Show('输入为空,分析失败');
        return;
    }

    // 分析句子
    for (int i = 0; i < textBox1.Text.Length; i++)
    {
        if (!lr.exist(lr.Echar, textBox1.Text[i]))
        {
            MessageBox.Show('含有未知字符!');
            return;
        }
    }
    step = 0;//单步操作置零

    listView3.Clear();
    listView3.Columns.Clear();
    listView3.Items.Clear();
    listView3.View = View.Details;

    string[] text = { '步骤', '状态栈', '符号栈', '输入串', '所用产生式' };
    int[] width = { 45, 70, 70, 70, 100 };
    for (int i = 0; i < text.Count(); i++)
    {
        listView3.Columns.Add(text[i], width[i]);
    }
    lr.sen_Analyze(textBox1.Text);//调用

    if (lr.Success)//判断
        MessageBox.Show('分析成功,是该文法的一个句子!');
    else
        MessageBox.Show('分析失败!');

    listView3.GridLines = true;
    button7.Enabled = true;
    button8.Enabled = true;
}

代码 1 中的 button6_Click 函数用于处理分析句子的按钮点击事件。

  1. 输入检查: 首先判断输入是否为空,若为空则弹出提示框并返回。
  2. 字符校验: 遍历输入串中的每一个字符,若存在未知字符则弹出提示框并返回。
  3. 初始化列表视图: 清空 listView3 并设置其为 View.Details 格式,添加列名称和设置列宽。
  4. 调用分析函数: 调用 LR.cs 中的 sen_Analyze 函数进行句子分析。
  5. 显示分析结果: 根据 sen_Analyze 的分析结果弹出相应的提示框。
  6. 启用按钮: 启用单步执行和一键执行按钮。

代码 2:LR 分析过程实现

public void sen_Analyze(string text)
{
    Jz = new Analyze();
    text += '#';
    Stack<int> Status = new Stack<int>();
    Stack<char> Symbol = new Stack<char>();
    Status.Push(0);
    Symbol.Push('#');
    Jz.stack_state.Add("0 ");
    Jz.stack_symbol.Add("#");
    Jz.Input_str.Add(text);
    Jz.Tran_pro.Add("Initial State");

    int p = 0;
    while (true)
    {
        if (LRAna[Status.Peek()][FindID(Echar, text[p])].error)//遇到ERROR 程序结束 分析失败
        {
            //ERROR();
            Success = false;
            break;
        }
        else if (LRAna[Status.Peek()][FindID(Echar, text[p])].type == 'A')//遇到ACC,程序结束 分析成功
        {
            // MessageBox.Show("分析成功,是该文法的一个句子!");
            Success = true;
            Jz.stack_state.Add(Jz.stack_state[Jz.stack_state.Count - 1]);//分析成功时 三个属性都不需要改变 加入上一次的内容即可
            Jz.stack_symbol.Add(Jz.stack_symbol[Jz.stack_symbol.Count - 1]);
            Jz.Input_str.Add(Jz.Input_str[Jz.Input_str.Count - 1]);
            Jz.Tran_pro.Add("分析成功!完毕!");
            break;
        }
        if (LRAna[Status.Peek()][FindID(Echar, text[p])].type == 'S')//移进
        {
            Symbol.Push(text[p]);
            Status.Push(LRAna[Status.Peek()][FindID(Echar, text[p])].id);
            Jz.stack_state.Add(Jz.stack_state[Jz.stack_state.Count - 1] + Status.Peek().ToString() + " ");//加入状态 Sj 的 j
            Jz.stack_symbol.Add(Jz.stack_symbol[Jz.stack_symbol.Count - 1] + text[p].ToString());//移进当前字符

            string tempstr = "";
            for (int ti = p + 1; ti < text.Length; ti++)//克隆剩余部分字符串
            {
                tempstr += text[ti];
            }
            Jz.Input_str.Add(tempstr);//克隆后的字符串入数组
            Jz.Tran_pro.Add("移进");
            p++;
        }
        else if (LRAna[Status.Peek()][FindID(Echar, text[p])].type == 'r')//归约
        {
            int len = LRproNum[LRAna[Status.Peek()][FindID(Echar, text[p])].id].Right.Length;//取第j条产生式 len为长度
            char st = LRproNum[LRAna[Status.Peek()][FindID(Echar, text[p])].id].Left[0];//左部就一个字符 转换成char
            string Output = st + "->";
            string waitout = "";//用来翻转出栈的顺序
            for (int i = 0; i < len; i++)
            {
                Status.Pop();
                waitout += Symbol.Peek();
                Symbol.Pop();

            }
            for (int i = waitout.Length - 1; i >= 0; i--)
            {
                Output += waitout[i];
            }

            if (LRAna[Status.Peek()][Echar.Count + FindID(Nchar, st)].error)//遇到ERROR 程序结束 分析失败
            {
                //ERROR();
                Success = false;
                break;
            }
            else//GOTO表
            {
                Symbol.Push(st);
                Status.Push(LRAna[Status.Peek()][Echar.Count + FindID(Nchar, st)].id);//非终结符

            }
            string tempstr = "", tempstr2 = "";
            int cnt = 0, cnt2 = 0;//cnt2记录上一行空格数 也就是状态数量 因为10算俩字符 需要加判断 cnt在循环时记录已经添加了几个状态 只在新行需要添加cnt2-len个状态
            for (int i = 0; i < Jz.stack_state[Jz.stack_state.Count - 1].Length; i++)
            {
                if (Jz.stack_state[Jz.stack_state.Count - 1][i] == ' ')
                    cnt2++;
            }

            for (int ti = 0; cnt < cnt2 - len; ti++)//由于有出栈情况 重新构造一个字符串 *2是因为有空格 
            {
                if (Jz.stack_state[Jz.stack_state.Count - 1][ti] == ' ')
                    cnt++;
                tempstr += Jz.stack_state[Jz.stack_state.Count - 1][ti];
            }

            for (int ti = 0; ti < Jz.stack_symbol[Jz.stack_symbol.Count - 1].Length - len; ti++)//由于有出栈情况 重新构造一个字符串 
            {
                tempstr2 += Jz.stack_symbol[Jz.stack_symbol.Count - 1][ti];
            }
            Jz.stack_state.Add(tempstr + Status.Peek().ToString() + " ");
            Jz.stack_symbol.Add(tempstr2 + st);//移进归约后的非终结符
            Jz.Input_str.Add(Jz.Input_str[Jz.Input_str.Count - 1]);//不变
            Jz.Tran_pro.Add("归约用" + Output);
        }

    }
}

代码 2 中的 sen_Analyze 函数实现了 LR 分析表的分析过程。

  1. 初始化: 初始化 Jz 对象,添加结束标记 #,创建状态栈和符号栈,并将初始状态 0 和结束标记 # 分别入栈。
  2. 添加初始信息: 将初始状态、符号、输入串及所用产生式加入对应的数组中。
  3. 分析循环: 进入分析循环,根据 LR 分析表进行移进归约操作,并将分析过程中产生的信息加入对应的数组中。

分析循环中的具体步骤:

  • 遇到 ERROR: 若遇到 ERROR 则分析失败,退出循环。
  • 遇到 ACC: 若遇到 ACC 则分析成功,加入相应的信息并退出循环。
  • 遇到移进操作: 将字符入符号栈,状态入状态栈,并加入相应的信息。
  • 遇到归约操作: 根据产生式进行出栈操作,并将产生式左部非终结符入符号栈,状态入状态栈,并加入相应的信息。

原理步骤

LR 分析器是一种自下而上的语法分析器,其工作原理如下:

  1. 构建 LR 分析表: 根据文法构造 LR 分析表,该表包含状态、输入符号、动作(移进、归约、接受或错误)和 GOTO 表信息。
  2. 初始化状态栈和符号栈: 将初始状态和结束标记入栈。
  3. 分析循环: 根据当前状态和输入符号,从 LR 分析表中查找对应动作,并进行相应的操作。
    • 移进操作: 将输入符号和对应的状态入栈。
    • 归约操作: 根据产生式进行出栈操作,并将产生式左部非终结符和对应的状态入栈。
    • 接受操作: 当遇到 ACC 状态时,分析成功。
    • 错误操作: 当遇到 ERROR 状态时,分析失败。
  4. 重复步骤 3 直至分析成功或失败。

总结

代码 1 的 button6_Click 函数调用了代码 2 的 sen_Analyze 函数进行 LR 分析表的分析过程,并将分析结果显示在 listView3 中。sen_Analyze 函数实现了 LR 分析表的分析过程,根据输入串和 LR 分析表进行移进归约操作,并将分析过程中产生的信息加入对应的数组中。

通过本文的讲解,读者可以更深入地理解 LR 分析器的原理和实现过程,并为进一步学习编译原理和语法分析打下基础

LR 分析器实现:C# 代码详解及原理步骤

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

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