返回

算术表达式解析系列之文法规则实现优先级

前端

算术表达式解析系列之文法规则实现优先级

本篇是算术是表达式解析系列文章之一。建议先阅读上篇,本篇不重复介绍一些基本的概念。本篇将使用跟上一篇相似的方式,采用递归下降的方式来解析语法。但是对于优先级的处理,将直接从文法层面做处理。这种方式,从学习和理解的角度上来讲,可能会更简单一些。不过在性能方面,会更差一些。

算术表达式解析系列文章分为以下几篇:

  • 算术表达式解析系列之一:整体设计
  • 算术表达式解析系列之二:词法分析
  • 算术表达式解析系列之三:语法分析(本篇)
  • 算术表达式解析系列之四:语义分析
  • 算术表达式解析系列之五:优化

在《算术表达式解析系列之一:整体设计》中,我们提到过,可以通过递归下降的方式来解析算术表达式。在《算术表达式解析系列之二:词法分析》中,我们也已经实现了词法分析器。现在,我们只需要实现一个语法分析器,就可以完整地实现算术表达式的解析了。

一、文法

在开始实现语法分析器之前,我们先来看看算术表达式的文法。算术表达式的文法如下:

E -> T
E -> E + T
E -> E - T
T -> F
T -> T * F
T -> T / F
F -> ( E )
F -> num

其中,E 表示表达式,T 表示项,F 表示因子,num 表示数字。

二、优先级

在算术表达式中,不同的运算符具有不同的优先级。例如,乘号和除号的优先级高于加号和减号。为了处理优先级,我们需要对文法进行一些修改。

修改后的文法如下:

E -> T E'
E' -> + T E'
E' -> - T E'
E' -> ε
T -> F T'
T' -> * F T'
T' -> / F T'
T' -> ε
F -> ( E )
F -> num

其中,ε 表示空串。

在修改后的文法中,我们引入了两个新的非终结符 E' 和 T'。E' 和 T' 分别表示 E 和 T 的后缀。后缀可以是空串,也可以是运算符和另一个后缀的组合。

三、递归下降分析器

现在,我们就可以开始实现递归下降分析器了。递归下降分析器是一种自顶向下的语法分析器。它从文法的开始符号出发,逐层向下推导,直到推导出输入字符串。

递归下降分析器的实现如下:

def E():
    T()
    E_()

def E_():
    if token.type == '+':
        token.next()
        T()
        E_()
    elif token.type == '-':
        token.next()
        T()
        E_()
    else:
        pass

def T():
    F()
    T_()

def T_():
    if token.type == '*':
        token.next()
        F()
        T_()
    elif token.type == '/':
        token.next()
        F()
        T_()
    else:
        pass

def F():
    if token.type == '(':
        token.next()
        E()
        if token.type == ')':
            token.next()
        else:
            raise SyntaxError('Expected )')
    elif token.type == 'num':
        token.next()
    else:
        raise SyntaxError('Expected ( or num')

在递归下降分析器中,每个函数对应一个非终结符。函数的实现就是对非终结符的推导过程。

四、测试

现在,我们就可以测试一下递归下降分析器了。我们使用以下算术表达式作为测试用例:

1 + 2 * 3 - 4 / 5

递归下降分析器的输出如下:

E -> T E'
T -> F T'
F -> num
T' -> * F T'
F -> num
T' -> ε
E' -> - T E'
T -> F T'
F -> ( E )
E -> T E'
T -> F T'
F -> num
T' -> ε
E' -> / F T'
F -> num
T' -> ε
E' -> ε

从输出中可以看出,递归下降分析器正确地解析了算术表达式。

五、总结

本篇介绍了如何使用递归下降的方式来解析算术表达式。我们首先介绍了算术表达式的文法,然后对文法进行了修改,以处理优先级。最后,我们实现了递归下降分析器,并使用了一个算术表达式作为测试用例。