编译原理-语法分析器的设计与实现(递归下降)

一、学习目的

语法分析是编译程序中的核心部分。本次学习通过设计一个典型的自顶向下语法分析程序——LL(1) 语法分析程序或算符优先文法分析,进一步理解并掌握语法分析的原理和实现技术。

二、原理

语法分析的主要任务是“组词成句”,将词法分析给出的单词序列按语法规则构成更大的语法单位,如“程序、语句、表达式”等;或者说,语法分析的作用是用来判断给定输入串是否为合乎文法的句子。

按照生成语法树的方向不同,常用的语法分析方法有两类:自顶向下分析和自底向上分析。自顶向下分析也称面向目标的分析方法,也就是从文法的开始符出发,试图推导出与输入单词串相匹配的句子,分为确定的自顶向下分析和不确定的自顶向下分析两种,LL分析法属于确定的自顶向下分析。自底向上分析也称移进 — 归约分析方法,从输入单词串开始,试图归约到文法的开始符。

预测分析法(LL(1)方法)的基本思想是:从文法开始符S 出发,从左到右扫描源程序,每次通过向前查看 1 个字符,选择合适的产生式,生成句子的最左推导。预测分析法有两种实现思路,一种是采用递归思想,针对每个非终结符号构建处理函数。第二种是采用预测分析表的思想,根据当前非终结符和终结符使用产生式进行推导。算符优先分析是一种自下向上的分析方法,首先根据规则构建算符之间的规约优先关系,然后每次对不同串进行规约,直至规约到文法的开始符号。

三、实验说明

3.1学习选题

题目:递归分析法设计与实现

3.2实验内容及理解

递归下降分析法是一种自顶向下的语法分析方法,通过递归调用与文法中非终结符对应的子程序,逐步解析输入字符串。分析程序由一组子程序构成,每个非终结符按照其产生式的结构写出相应的子程序,每个子程序对应一个非终结符,且每个子程序的名字用该终结符执行。开始执行时,从文法的开始符号对应的子程序开始,根据文法规则递归调用其他子程序,直到分析完成或发现语法错误。根据分析结果返回匹配情况。

3.3学习目标

LL(1)文法的递归分析法的目标如下:

1.对语法规则有明确的定义;在递归分析法中,文法由程序之间的相互调用形成递归而构成,我们需要在每个子程序中给出该符号的相关选择就可以了

2.编写的分析程序能够进行正确的语法分析;

3.对于遇到的语法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成语法分析过程。

四、设计实现

4.1主要变量及存储结构

表1 主要变量

主要变量

存储结构

作用

str

Char类型数组

用于存储输入的字符串

index_a

int类型

用于跟踪当前在分析的输入字符串的位置

len

int类型

用于存储输入字符串的长度

表 2  函数功能与调用关系

函数名

功能描述

调用关系

E

处理符号 E 的语法规则,即:

E->TG

调用 T 和 G

G

处理符号 G 的语法规则,即:

G->+TG | -TG | e

在符号 ‘+ ’ 或 ‘- ’ 后调

用 T 和 G

T

处理符号 T 的语法规则,即:

T->FS

调用 F 和 S

S

处理符号 S 的语法规则,即:

S->*FS | /FS | e

在符号 ‘* ’ 或 ‘/ ’ 后调

用 F 和 S

F

处理符号 F 的语法规则,即:

F->(E) | i

处理 ‘( ’ 和 ‘) ’,识别’i ’,

判断输入字符串的正确性

4.2算法思路与方法实现

4.2.1算法思路

  1. 在main函数中,用户输入一个字符串,并调用 E函数来开始对输入字符串进行分析。
    int main()
    {
        int len;
        cin >> str;
        len = strlen(str);
        str[len + 1] = '\0';
        E();
        cout << str;
        cout << "为合法符号串" << endl;
        index_a = 0;
        return 0;
    }
  2. E函数定义了文法的起始符号E,根据文法规则,E可以推导为TG。所以E函数调用了T函数,然后调用了X函数。
    void E()//E->TG;
    {
        T();//T
        X();//G
    }
  3. X函数定义了非终结符G的产生式,即G->+TG|e。根据产生式,当输入字符串当前位置是+或-,则调用T函数,然后递归调用X函数;当输入字符串的最后一个字符为 '#' 时,意味着输入字符串已经被完整地分析,并且符合了文法规则。此时程序输出当前字符串,并显示它为合法符号串,然后通过 exit(0) 退出程序。
    void X() //G->+TG | e
    {
        if (str[index_a] == '+'||str[index_a] == '-')
        {
            index_a++;
            T();
            X();
        }
        else if(str[index_a] == '#'){
            cout << str;
    	    cout << "为合法符号串" << endl;
            exit(0);
        }
    }
  4. T函数定义了非终结符T的产生式,即T->FS。所以T函数调用了F函数,然后调用了Y函数。
    void T() //T->FS
    {
        F();//F
        Y();//S
    
    }
  5. Y函数定义了非终结符S的产生式,即S->*FS| e。当输入字符串当前位置是*或/,则调用F函数,然后递归调用Y函数;当输入字符串的最后一个字符为 '#' 时,意味着输入字符串已经被完整地分析,并且符合了文法规则。此时程序输出当前字符串,并显示它为合法符号串,然后通过 exit(0) 退出程序。
    void Y()//S->*FS | e
    {
        if(str[index_a]=='*'||str[index_a]=='/'){
            index_a++;
            F();
            Y();
        }
        else if(str[index_a] == '#'){
            cout << str;
    	    cout << "为合法符号串" << endl;
            exit(0);
        }
    }
  6. F函数定义了非终结符F的产生式,即F->(E) | i。首先检查当前字符是否为'(',如果是,则递归调用E()函数继续分析括号内的表达式。如果在此过程中遇到非法字符或者括号不匹配,则输出提示信息并退出程序。如果当前字符不是'(',则检查是否为'i',如果是,则继续向后移动一个字符。如果既不是'('也不是'i',则输出提示信息并退出程序。
    void F() //F->(E) | i
    {
        if (str[index_a] == '(') {
            index_a++;
            E();
            if (str[index_a] == ')') {
                index_a++;
            } else {
                cout << "非法的符号串!";
                exit(0); //程序退出
            }
        } else if (str[index_a] == 'i') {
            index_a++;
        }
         else {
            cout << "非法的符号串!";
            exit(0);
        }
    }

    4.3测试数据和运行结果

    4.3.1 测试数据为i+i*i#

    预测结果:i+i*i#为合法符号串

    分析过程:

    E->TG

    E->FS+TG

    E->ie+FSG

    E->i+i*FSG

    E->i+i*iee

    E->i+i*i

    与测试输入相同,为合法符号串

    图1 测试集1的运行结果截图

    4.3.2 测试数据为i+i/i#

    预测结果:i+i/i#为合法符号串

    分析过程:

    E->TG

    E->FS+TG

    E->ie+FSG

    E->i+i/FSG

    E->i+i/iee

    E->i+i/i

    与测试输入相同,为合法符号串

    图2 测试集2的运行结果截图

    4.3.3测试数据为i+i*#

    预测结果:非法的符号串!

    *后没有任何终结符,不符合所给产生式,为非法的符号串

    图3 测试集3的运行结果截图

    5.1改进设想

    5.1.1 命名规范

    递归下降LL(1)分析程序中的每个非终结符都对应着一个子程序,一般子程序的命名与非终结符是相同的。但是实验平台事先给出的各个子程序的命名与非终结符却不对应,导致在编写代码时十分的别扭,可以把每个子函数的名字都改成与非终结符相同的符号。

  7. 图5 平台提供的未与非终结符不一致的子函数命名截图

    图6 设想的与非终结符一致的子函数命名截图

  8. #总代码
    #include <iostream>
    #include<stdio.h>
    #include<cstring>
    using namespace std;
    char str[10];
    int index_a = 0;
    void E();			//E->TG;
    void X();			//G->+TG | e
    void T();			//T->FS
    void Y();			//S->*FS | e
    void F();			//F->(E) | i
    int main()
    {
    	int len;
    	cin >> str;
    	len = strlen(str);
    	str[len + 1] = '\0';
    	E();
    	cout << str;
    	cout << "为合法符号串" << endl;
    	index_a = 0;
    	return 0;
    }
    /*******Beign*******/
    /*构造递归关系及子程序*/
    void E()//E->TG;
    {
        T();//T
        X();//G
    }
    
    void X() //G->+TG | e
    {
        if (str[index_a] == '+'||str[index_a] == '-')
        {
            index_a++;
            T();
            X();
        }
        else if(str[index_a] == '#'){
            cout << str;
    	    cout << "为合法符号串" << endl;
            exit(0);
        }
    }
    void T() //T->FS
    {
        F();//F
        Y();//S
    
    }
    void Y()//S->*FS | e
    {
        if(str[index_a]=='*'||str[index_a]=='/'){
            index_a++;
            F();
            Y();
        }
        else if(str[index_a] == '#'){
            cout << str;
    	    cout << "为合法符号串" << endl;
            exit(0);
        }
    }
    void F() //F->(E) | i
    {
        if (str[index_a] == '(') {
            index_a++;
            E();
            if (str[index_a] == ')') {
                index_a++;
            } else {
                cout << "非法的符号串!";
                exit(0); //程序退出
            }
        } else if (str[index_a] == 'i') {
            index_a++;
        }
         else {
            cout << "非法的符号串!";
            exit(0);
        }
    }
    /*******End*******/
    

  • 12
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值