算术表达式解析系列之文法规则实现优先级
2024-02-22 06:24:25
算术表达式解析系列之文法规则实现优先级
本篇是算术是表达式解析系列文章之一。建议先阅读上篇,本篇不重复介绍一些基本的概念。本篇将使用跟上一篇相似的方式,采用递归下降的方式来解析语法。但是对于优先级的处理,将直接从文法层面做处理。这种方式,从学习和理解的角度上来讲,可能会更简单一些。不过在性能方面,会更差一些。
算术表达式解析系列文章分为以下几篇:
- 算术表达式解析系列之一:整体设计
- 算术表达式解析系列之二:词法分析
- 算术表达式解析系列之三:语法分析(本篇)
- 算术表达式解析系列之四:语义分析
- 算术表达式解析系列之五:优化
在《算术表达式解析系列之一:整体设计》中,我们提到过,可以通过递归下降的方式来解析算术表达式。在《算术表达式解析系列之二:词法分析》中,我们也已经实现了词法分析器。现在,我们只需要实现一个语法分析器,就可以完整地实现算术表达式的解析了。
一、文法
在开始实现语法分析器之前,我们先来看看算术表达式的文法。算术表达式的文法如下:
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' -> ε
从输出中可以看出,递归下降分析器正确地解析了算术表达式。
五、总结
本篇介绍了如何使用递归下降的方式来解析算术表达式。我们首先介绍了算术表达式的文法,然后对文法进行了修改,以处理优先级。最后,我们实现了递归下降分析器,并使用了一个算术表达式作为测试用例。