`
jiang5495
  • 浏览: 93019 次
  • 性别: Icon_minigender_1
  • 来自: 湖南
社区版块
存档分类
最新评论

(转载)算术表达式的自上而下语法分析及其实现(上)

阅读更多
学过编译原理的同学大概都知道对一个句子进行自上而下语法分析的方法。我参考了陈火旺院士的《高级程序设计语言编译原理》,在这篇文章里我主要是站在编译原理的角度讲述一种语法分析程序的实现的方法,通过对一个典型的例子——算术表达式的分析,从而使大家了解构造一个实用的语法分析程序的方法,同时,也为广大程序员提供一种解决实际问题的思路。

本文包括以下内容:
1. 算术表达式的产生式;
2. 自上而下语法分析的算法和的产生式函数的构造;
3. 产生式函数的改进;
4. 语法分析中的出错处理;
5. 自上而下语法分析程序的实现。


1. 算术表达式的产生式

我在这里要实现的算术表达式要实现5种运算:加、减、乘、除和括号。比如一个简单的算术表达式的文法G1中包含以下产生式:
G1:
E -> E+E | E-E | E*E | E/E | (E) | i
为了明确运算符的优先权(括号的优先权高于乘除法,乘除法的优先权高于加减法),可改写文法G1如下:
改写后的文法G2:
E -> T+E | T-E | T
T -> F*T | F/T | F
F -> (E) | i
任何具有加、减、乘、除和括号运算优先权的算术表达式都可以通过上述文法中的产生式推导出来,比如对于行如i-i*(i+i)的算术表达式,有如下推导过程(其中i是数字或变量标示符,推导需要从开始符E开始推导,以下是最左推导):

E=> T-E => F-E => i-E => i-T => i-F*T => i-i*T => i-i*F => i-i*(E) => i-i*(T+E) =>i-i*(F+E) => i-i*(i+E) => i-i*(i+T) => i-i*(i+F) => i-i*(i+i)

在本文中,我们就使用文法G2中的产生式构造语法分析程序。

2.自上而下语法分析的算法和的产生式函数的构造

我们可以把一个对句子从开始符E到终结符的推导过程转化为一棵语法树,根节点(即开始符)在上、叶节点(即终结符)在下,自上而下的语法分析就是对这样一棵语法树“自上而下”地遍历过程。即,每次遍历从根节点(开始符)开始,通过各个中间节点(除开始符外非终结符)到达叶节点(终结符)。如果把每一个产生式做成一个函数,那么我们可以方便地通过对这些函数的递归调用和回溯来实现对语法树的遍历。那么对于文法G2中的3个产生式,我们需要3个函数:
void E_AddSub();            //对应于非终结符E的产生式
void T_MulDiv();               //对应于非终结符T的产生式
void F_Number();             //对应于非终结符F的产生式

我们通过对输入字符流的分析来实现自上而下的语法分析。在语法分析的过程中,我们需要一个输入字符缓冲区,用来存放输入的算术表达式字符串,需要一个字符指示器来指示当前正在分析的字符,还需要一个出错处理模块。在算法设计实现中,我们用到了3个全局成员:ch、advance和error,它们的含义如下:

ch                  当前指示器所指的字符
advance()     使指示器指向输入字符缓冲器中的下一个字符的函数
error()            出错处理程序函数

由此可以构造自上而下语法分析算法,首先分析产生式E -> T+E | T-E | T,不妨先把它分解成以下3个产生式:
E -> T+E
E -> T-E
E -> T
下面首先写出E -> T+E个语法分析函数:

//清单1:产生式E -> T+E的语法分析函数
void E_AddSub()
{
T_MulDiv();           //调用非终结符T的产生式函数分析T
If(ch==’+’)       //如果当前字符是’+’,
{
  advance();           //则取下一个字符
  E_AddSub();       //调用非终结符E的产生式函数分析E
}
else                     //如果不是’+’号
  error();                //则进行出错处理
}

看到上面函数中的算法,你大概已经可以想到产生式E -> T-E 的自上而下语法分析算法了,即把If(ch==’+’) 一句中的’+’改成’-‘号即可。下面是产生式E -> T 的算法,很简单:

//清单2:产生式E -> T的语法分析函数
void E_AddSub()
{
T_MulDiv();     //调用非终结符T的产生式函数分析T
}

大家可以看到,为每一个产生式写一个分析函数,通过它们之间的相互调用,即可实现对语法树的遍历,从而实现对句子的推导。由于E -> T+E、E -> T-E、E -> T三个产生式可以合并成E -> T+E | T-E | T,我们也可以把对应的三个产生式的函数合并成一个函数,由于有产生式E -> T ,所以在E的产生式函数中只调用非终结符T的分析函数就可以了,即使下一个字符不是’+’或’-‘也不必做错误处理,而E -> T+E | T-E的合并用一句分支语句if(ch==’+’||ch==’-‘)判断即可,这样,合并后E产生式的函数如下:

//清单3:产生式E -> T+E | T-E | T的分析函数
void E_AddSub()
{
T_MulDiv();                        //调用非终结符T的产生式函数分析T
If(ch==’+’ ||ch==’-‘)   //如果当前字符是’+’或’-‘,
                                         //如果是’+’,则用产生式E -> T+E推导,
                                         //如果是’-‘,则用产生式E -> T-E推导。
{
  advance();                        //则取下一个字符
  E_AddSub();                    //调用非终结符E的产生式函数分析E
}                                      //此时产生式E -> T+E | T-E的推导算法结束
                                        //如果下一个字符不是不是’+’或’-‘,
                                        //则本函数是根据产生式E -> T来进行推导的,不必进行错误处理。
}

同理,你也可以容易地写出产生式T -> F*T | F/T | F和F -> (E) | i的自上而下语法分析函数:

//清单4:产生式T -> F*T | F/T | F的分析函数
void T_MulDiv()
{
F_Number();                      //调用非终结符F的产生式函数分析F
If(ch==’*’ ||ch==’/‘)   //如果当前字符是’*’或’\‘,
                                        //如果是’*’,则用产生式T -> F*T推导,
                                        //如果是’\‘,则用产生式T -> F/T推导。
{
  advance();                       //则取下一个字符
  T_MulDiv();                      //调用非终结符T的产生式函数分析T
}                                      //此时产生式T -> F*T | F/T 的推导算法结束
                                        //如果下一个字符不是不是’*’或’/‘,
                                        //则本函数是根据产生式T -> F 来进行推导的,不必进行错误处理。
}

//清单5:产生式F -> (E) | i的分析函数
void F_Number()
{
if(ch==’(‘)                   //如果当前的指示器指示的字符是’(‘
{                                    //则根据产生式F -> (E)推导
  advance();                     //跳过’(‘,指示器指向下一个字符
  E_AddSub();                 //调用非终结符E的产生式函数分析E
  If(ch!=’)’)                  //判断下一个字符是否是’)’,
                                     //必须保证有右括号与左括号配对使用
   error();                        //如果出错,则进行出错处理。
  Advance();                   //如果有’)’,语法正确,跳过’)’
 
  return;                         //返回
}
if(ch是数字)                  //如果当前指示器指示的字符是数字
{                                  //则根据产生式F -> i推导
  advance();                   //跳过该数字,指示器指向下一个字符
}                                 //语法正确,完成F -> i推导
else                            //如果当前指示器指示的字符不是数字也不是’(‘
  error();                       //则出错,转向出错处理程序

return;                        //返回
}

由于,符合语法的句子的推导是从开始符E开始的,所以在进行语法分析时,需要在主程序中这样实现:

//清单6:主程序
int main()
{
………………………………
//对输入字符缓冲区和字符指示器初始化
//调用开始符E的分析函数开始自上而下语法分析:
E_AddSub();
//分析结束
………………………………
return 0;
}

按照此方法实现的上述函数实现了对语法树的自上到下的遍历,从而展示了自上而下语法分析的过程,然而,这些函数并没有实现具体的功能,比如执行算术表达式或计算求值的功能,在下面的几节里我会陆续考虑这些问题。

分享到:
评论

相关推荐

    算术表达式的自上而下语法分析及其实现

    ### 算术表达式的自上而下语法分析及其实现 #### 一、算术表达式的产生式 本文探讨了一种基于自上而下的语法分析技术,旨在通过解析简单的算术表达式来构建实用的语法分析程序。该文法模型能够支持五种基本运算:...

    算术表达式预测分析程序实现

    算术表达式预测分析程序实现 一、算术表达式预测分析程序设计 算术表达式预测分析程序设计是计算机科学中一个重要的研究领域,旨在开发出能够对给定算术表达式进行预测分析的程序。该程序的设计需要掌握自上而下...

    编译原理实验-递归下降的方法实现语法分析器

    (使用Python实现,注释详尽)在词法分析器的基础上,采用递归下降的方法实现算术表达式的语法分析器,以加深对自上而下语法分析过程的理解。 1、对算术表达式文法: E→TE' E'→+TE'| -TE' |ε T→FT' T'→*FT'| /...

    phrase.cpp

    编译原理实验(二)--语法分析:采用自上而下的方法实现算术表达式的语法分析器,以加深对自上而下语法分析过程的理解。 E→TE' E'→+TE'| -TE' |ε T→FT' T'→*FT'| /FT' |ε F→(E) | id | num

    编译原理:算术表达式递归下降分析程序设计[定义].pdf

    通过本次实验,掌握了自上而下语法分析法的特点和递归下降语法分析的基本原理和方法。递归下降分析法是一种简单、直观、易构造分析程序的方法,但它不适于文法过于复杂的场景。 在实验中,需要注意程序的每一步,...

    递归下降分析器设计与实现.pdf

    (1)将原算术表达式方法改写为LL(1)文法。 (2)为每个非终结符设计一个对应的函数,通过各函数之间的递归调用从而实现递归下降语法分析的功能。 (3)在设计函数时,需要根据文法的右部符号串的顺序编写函数体,...

    通过设计、编制、调试一个典型的语法分析程序

    * 调试调试例子应包括符合语法规则的算术表达式,以及分析程序能够判别的若干错例。 * 输出对于所输入的算术表达式,不论对错,都应有明确的信息告诉外界。 * 扩充有余力的同学,可适当扩大分析对象。 五、语法分析...

    编译原理实验报告算术表达式递归下降分析程序设计.pdf

    《编译原理》实验报告的主题聚焦于算术表达式的递归下降分析程序设计,这是一个典型的技术性任务,旨在让学生深入理解编译原理中的语法分析过程。实验的主要目标包括三个方面: 1. 掌握自上而下的语法分析方法,这...

    实验5-LL(1)语法分析程序的设计与实现(C语言).doc

    LL(1)语法分析程序的设计与实现(C语言) 一、实验目的: 本实验的目的是通过设计LL(1)语法分析程序来理解自顶向下的语法分析思想。LL(1)语法分析是一种常用的语法分析方法,它可以自动判断所给字符串是否为所给...

    编译原理:LL(1)语法分析器的设计

    ### 编译原理:LL(1)语法分析器的设计 #### 概述 本文将详细介绍一个简单的LL(1)语法分析器的设计与实现过程。...本篇介绍的LL(1)语法分析器提供了一个基本框架,可用于理解和实现更复杂的语法分析任务。

    编译原理的语法分析——LL(1)分析表的实现.docx

    这个文法允许计算简单的算术表达式,如加法和乘法。在消除左递归和提取公因子后,等价的LL(1)文法变为: E' → +TR | ε T → TR | T T' → *FM | ε F → (E) | i 在实验中,学生需要实现以下步骤: 1. **文法...

    LL(1)语法分析器.zip

    词法分析器的实现通常基于正则表达式或状态机,确保能够准确地识别和转换源代码中的各种元素。 进入正题,LL(1)语法分析器的“LL”代表“Left-to-right”,意味着它从左到右扫描输入序列;“1”表示“使用一个输入...

    LL(1)语法分析器(编译原理课程设计)

    词法分析器通常由正则表达式驱动,将输入文本转换为标记流,供后续的语法分析使用。 ### 3. LL(1)语法分析 LL(1)解析器的工作原理是基于文法的左递归消除和第一集计算。它从输入序列的第一个字符开始,根据文法规则...

    编译原理(四)–语法分析

    本章将重点介绍典型的语法分析方法及相关的概念和实现技术 语法分析分为: 自上而下:递归下降分析法(LL预测分析法—>推导 自下而上:算符优先分析法(LR分析法—>归约 4.1 语法分析器的功能 4.1.1 语法分析器任务 ...

    自上而下递归分析表达式正确性

    对每一个非终结符(分别代表一个语法单位)按其产生方式结构构造相应的语法子程序,以完成非终结符号所对应的语法单位的分析和识别任务。其中终结符号产生匹配命令,而非终结符号则产生过程调用命令。因为文法可以...

    top_down.rar_top down_top-down_语法分析值

    本文将深入探讨自上而下的语法分析方法,以及如何利用这种方法对算术表达式进行语法分析并计算其值。 自上而下的语法分析,也称为递归下降分析,是一种基于规则的解析技术,从输入符号串的起始符号开始,按照文法...

    编译原理中采用递归下降子程序方法实现语法分析的程序

    1. 预习自上而下语法分析小节的内容; 2. 学生自己考虑使用的开发环境,如 VC++,熟悉开发环境。 三、实验内容 针对算术表达式文法:E→TE’ E’ → +TE’|ε T→FT’ T’ →*FT’ |ε F→(E) |i 为其编写递归...

Global site tag (gtag.js) - Google Analytics