`
jiang5495
  • 浏览: 89617 次
  • 性别: 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)在设计函数时,需要根据文法的右部符号串的顺序编写函数体,...

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

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

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

    E →T E’ E’→+ T E’ | λ TF→ T’ T’→* F T’ | λ F →id| ( E ) 符号串 i + i * i # 的LL[1]分析过程:

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

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

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

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

    top_down.rar_top down_top-down_语法分析值

    自上而下的语法分析方法 能对算术表达式进行语法分析并计算出表达式的值

    编译原理 语法分析程序、LL(1)文法

    根据算术表达式的语法定义,设计相应的产生式规则...该程序能够对输入字符串进行自上而下无回溯的语法分析即预测分析,并输出“语法正确”或“语法错误”的结果,该程序能够实现First集、Follow集和构造预测表的算法。

    递归下降语法分析程序设计.doc

    该程序设计采用递归下降分析程序,通过自上而下的分析方式,来实现对赋值语句的语法分析。 递归下降分析算法 递归下降分析算法是一种自上而下的分析方法,它通过对语法规则的递归应用,来实现对输入语句的语法分析...

    词法分析器

    掌握语法分析的两类基本方法:自上而下的分析与自下而上的分析,并通过对自上而下的分析的编码实现,理解其执行过程以及相关限制。 实验内容:实现下述我们定义的语言的语法分析器。这种语言的程序结构很简单,语法...

    递归下降子程序的编写

    1. 预习自上而下语法分析小节的内容。 2. 学生自己考虑使用的开发环境,如 VC++,熟悉开发环境。 三、实验内容 我们将选择一个算术表达式文法:E→TE’,E’ → +TE’|ε,T→FT’,T’ →*FT’ |ε,F→(E) |i,...

    预测分析程序的设计与实现报告

    根据算术表达式的语法定义,设计相应的产生式规则,并改造为LL(1)文法。根据此文法构造预测分析表和预测分析程序。该程序能够对输入字符串进行自上而下无回溯的语法分析,并输出“语法正确”或“语法错误”的结果。

    递归下降分析器设计实现分析范文.doc

    1. 掌握自上而下语法分析的要求与特点。 2. 掌握递归下降语法分析的基本原理和方法。 3. 掌握相应数据结构的设计方法。 实验内容 编程实现给定算术表达式的递归下降分析器。算术表达式文法如下: E --> E+T|T T -...

    《编译原理》——期末复习.docx

    17.3 算术表达式和赋值语句 175 17.4 单元测试 178 十八、语义分析和中间代码产生_2 179 18.1 布尔表达式的作用和文法描述 179 18.2 做控制用布尔表达式的翻译(回填) 181 18.3 控制流语句的翻译 186 18.4 控制流...

    程序设计语言编译原理 (陈火旺)

    7.3.1 简单算术表达式及赋值语句 7.3.2数组元素的引用 7.3.3 记录中域的引用 7.4布尔表达式的翻译 7.4.1数值表示法 7.4.2作为条件控制的布尔式翻译 7.5控制语句的翻译 7.5.1控制流语句 7.5.2标号与goto语句...

    编译原理实验三 递归下降分析

    递归下降分析实验报告 ...通过本次实验,我们加深了对递归下降分析法的理解,并掌握了递归下降分析程序的编写和实现。该实验为我们提供了一个实践递归下降分析法的机会,提高了我们对编译原理的理解和掌握。

Global site tag (gtag.js) - Google Analytics