手把手教你使用递归下降法实现语法分析器


前言

        经过这段时间对编译原理中语法分析的学习,掌握了一定语法分析器设计的理论知识,下面就利用这些理论知识,使用递归下降分析实现一个简单的语法分析器。

在这里插入图片描述

一、实验内容

        对下列算术表达式的文法编写递归下降分析程序,要求对输入的任意符号串进行语法分析:
        (1)E->TE’
        (2)E’->+TE’ | —TE’ |ε
        (3)T->FT’
        (4)T’->*FT’ | /FT’ |ε
        (5)F->(E) | id | num
        注:文法中id表示标识符(此处标识符的定义与实验一中标识符的定义相同),num表示数字(简单处理可以认为是整数)

二、实验要求

1、 输入/输出格式
        输入一个字符串,输出该字符串是否为正确的句子。
        例如:输入a+xyz*10+(c/d),输出“正确的表达式”
        输入a+*xyz,输出“错误的表达式”
2、上述要求仅为基本要求,可以在此基础上扩充
        例如数字可以允许是浮点数,增加错误处理,输出错误信息等。
         例:输入a+*xyz,输出“表达式错误:缺少运算数”
        进一步可以指出出错的位置。

三、设计

1、首符集(FIRST)

1、定义

在这里插入图片描述

2、算法

在这里插入图片描述

3、计算上述语法的首符集

在这里插入图片描述

2、尾符集(FOLLOW)

1、定义

在这里插入图片描述

2、算法

在这里插入图片描述

3、计算上述语法的尾符集

在这里插入图片描述

3、可选集(SELECT)

1、定义

在这里插入图片描述

2、算法

在这里插入图片描述

3、计算上述语法的可选集

在这里插入图片描述

4、语法分析器类(Parser)设计

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReDescentAnaly
{
    /// <summary>
    /// 该类用来描述递归下降法设计的语法分析器
    /// </summary>
    class Parser
    {
        char elem;  //记录逐个获取的表达式字符
        int i = 0;  //记录获取的位置
        bool err = false; //false正确  true错误

        //记录要进行判断的表达式
        String express;
        public String Express
        {
            get { return express; }
            set { express = value; }
        }

        //记录错误原因
        String errReson;
        public String ErrReson
        {
            get { return errReson; }
        }

        //记录错误位置
        int errLie;
        public int ErrLie
        {
            get { return errLie; }
        }

        //逐个获取表达式中的字符
        public void GetElem()
        {
            elem = express.ToCharArray()[i];
            if (elem != '$')
            {
                i++;
            }
        }

        //(1)E->TE'
        public void E()
        {
            T();
            E1();
        }

        //(2)E'->+TE' | —TE' |ε
        public void E1()
        {
            if ((elem == '+')||(elem == '-'))
            {
                GetElem();
                T();
                E1();
            }
        }

        //(3)T->FT'
        public void T()
        {
            F();
            T1();
        }

        //(4)T'->*FT' | /FT' |ε
        public void T1()
        {
            if ((elem == '*') || (elem == '/'))
            {
                GetElem();
                F();
                T1();
            }
        }

        //(5)F->(E) | id | num 
        public void F()
        {
            if(elem=='(')   //左括号
            {
                GetElem();
                E();
                if(elem==')') //判断完'('后,要判断之后之后的串中是否存在')'
                {
            	    GetElem();
                }
                else
                {
            	    Error("缺少‘)’");
                }
            
            }
            else if(elem>='0'&&elem<='9') //数字
            {
                int flag=0;
                for(;(elem>='0'&&elem<='9')||elem=='.';)
                {
                    if(elem>='0'&&elem<='9')
                    {
                        GetElem();
                    }
                    if(elem=='.')
                    {
                       GetElem();
                       flag++;
                    }
                    if(flag>=2)
                    {
                        Error("小数点过多");
                        break;
                    }
                    if(elem>='a'&&elem<='z')
                    {
                	    Error("运算数后不能带有字母");
                        break;
                     }
                    if (elem == '(')
                    {
                        Error("运算数后不能直接带上左括号");
                        break;
                    }
               }
            }
            else if(elem>='a'&&elem<='z') //字母
            {
                for(;(elem>='a'&&elem<='z');)
                {
                    GetElem();
                }
                if (elem == '(')
                {
                    Error("标识符后不能直接带有左括号");
                }
            }
            else
            {
                Error("缺少运算数");
            }
        }

        ///错误处理
        public void Error(string reson)
        {    
            if (err == false)
            {
                err = true;
                errReson = reson;
                errLie = i;
            }
        }
        //判断语法检验结果是否正确
        public bool IsError()
        {
            return err;
        }

        //判断表达式是否为空
        public bool IsEmpty()
        {
            return express.ToCharArray()[0]=='$';
        }
    }
}

5、语法分析器的界面实现

1、界面设计

在这里插入图片描述

        1、表达式输入框:这是一个TextBox控件,可读可写,然后将其取名为textBox1
        2、分析结果输出框:这是一个TextBox控件,设置ReadOnly属性为true,使其成为只读不可写,然后将其取名为textBox2
        3、分析按钮:这是一个Button控件,为其绑定点击事件Analy,然后将其取名为AnalyBtn

2、结合Parser类实现语法分析器

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ReDescentAnaly
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        private void Analy(object sender, EventArgs e)
        {
            string s = textBox1.Text + "$";
            Parser parser = new Parser();
            parser.Express = s;
            parser.GetElem();     //获取表达式的首个字符
            if (parser.IsEmpty()) //如果表达式为空
            {
                textBox2.Text = "表达式不能为空";
            }
            else //表达式不为空
            {
                parser.E(); //进行语法分析
                if (!parser.IsError())//正确
                {
                    textBox2.Text = "正确的表达式";
                }
                else //错误
                {
                    textBox2.Text = "表达式错误:" + parser.ErrReson + ",错误位置在第" + parser.ErrLie + "个字符处。";
                }
            } 
        }
    }
}

四、测试方案及测试结果

1、 测试表达式:a+xyz*10+(c/d)
在这里插入图片描述
2、 测试表达式:a+*xyz
在这里插入图片描述
3、 测试表达式:3.14*5+(10/b)
在这里插入图片描述

结语

        本次内容的介绍就到这里,希望这篇文章能够给努力学习的你一些帮助,感谢各位人才的:点赞、收藏和评论,我们下次见。

在这里插入图片描述

附录

1、基础知识讲解视频
2、源码下载链接

  • 10
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值