本系列文章参考国外编程高手鲁斯兰的博客文章《Let’s Build A Simple Interpreter》。
本篇文章所实现的功能是在《一起来写一个简单的解释器(7)》的基础之上进行扩展,如果还没有掌握之前的知识内容,请先做复习。
以下,是我们即将添加的新功能:
- 扩展文法,能够处理一元运算(+-);
- 添加一个新的AST节点类“UnaryOperator”,构建一元运算符的节点;
- 扩展语法分析器,以便生成带有一元运算符节点的AST;
- 扩展解释器并添加一个新的“visit_UnaryOperator()”)方法来解释一元运算符。
一、扩展文法
之前的文法无法处理一元运算。
那么,什么叫一元运算呢?
例如“-3”或“+3”以及“5–2”等等。
简单来说,我们要做的就是支持正负数运算。
一元运算符(“+”或“-”)比二元运算符(“+”、“-”、“*”或“/”)具有更高的优先级。
例如“5–2”实际上就是“5-(-2)”,结果为“7”。
基于这个特点,我们文法的因子新增了正因子和负因子两种。
例如“-3”和“-(3+5)”都是因子。
也就是说,之前的文法片段:
factor:INTEGER|LPAREN expr RPAREN
需要扩展为:
factor:(PLUS|MINUS)factor|INTEGER|LPAREN expr RPAREN
就像“-(3+5)”这个表达式,通过修改前的文法无法解释,而通过新的文法则能够解释为:
MINUS LPAREN expr RPAREN
二、添加UnaryOperator类
一元运算符由运算符(+或-)和后方的表达式组成,所以一个一元运算符的节点对象,由运算符和表达式组成。
我们看一些带有一元运算符的表达式转换为AST的例子。
以“+-3”和“5+–2”为例,我们看一下它们对应的AST:
再来看一个更复杂的表达式“2+-(5*-4)-3”。
通过上面的示例,我们能够看出一元运算符的节点包含运算符还有子节点。
示例代码:
class UnaryOperator(AST): # 定义一元运算符节点类 def __init__(self, operator, expr): # 初始化 self.token = self.operator = operator # 运算符记号和运算符节点 self.expr = expr # 一元运算符的子表达式
三、修改factor()方法
根据新的文法,在factor()方法中我们需要添加对新因子的支持。
如果遇到运算符就将运算符和它的子表达式封装成一元运算符节点对象作为因子返回。
示例代码:
def factor(self): current_token = self.current_token if current_token.value_type == PLUS: # 如果是加法运算符 self.eat(PLUS) node = UnaryOperator(current_token, self.factor()) # 运算符记号和子表达式组成节点对象 return node elif current_token.value_type == MINUS: # 如果是减法运算符 self.eat(MINUS) node = UnaryOperator(current_token, self.factor()) # 运算符记号和子表达式组成节点对象 return node elif current_token.value_type == INTEGER: self.eat(INTEGER) return Num(current_token) elif current_token.value_type == LPAREN: self.eat(LPAREN) node = self.expr() self.eat(RPAREN) return node
四、添加访问一元运算符节点的visit_UnaryOperator()方法
当解释一元运算符节点对象时,如果运算符是加号,就对子表达式的结果进行正运算并返回;如果运算符是减号,就对子表达式的结果进行取负运算并返回。
示例代码:
def visit_UnaryOperator(self, node): # 访问一元运算符类型节点的方法 if node.operator.value_type == PLUS: # 如果运算符类型是加号 return +self.visit(node.expr) # 返回正的访问子节点的结果 if node.operator.value_type == MINUS: # 如果运算符类型是减号 return -self.visit(node.expr) # 返回负的访问子节点的结果
通过以上增加与修改,我们的解释器已经能够支持一元运算符的运算了。
练习:安装 Free Pascal,编译运行testunary.pas(点击下载)文件, 并验证计算同一算术表达式的结果是否和我们编写的解释器一致。
以Windows 7 64位系统为例:
1、下载Free Pascal安装文件:https://pan.baidu.com/s/1N-vHCopq0LrLhxNMiJQ0lw
2、下载testunary.pas,内容可以通过记事本打开查看(内容中包含Linux系统中执行的命令)。
3、安装Free Pascal(默认安装)。
4、打开CMD命令行终端,进入testunary.pas文件所在目录。
5、执行命令:ppcrossx64 testunary.pas && .\testunary
在下一篇文章中,我们将完成处理赋值语句的功能。
项目源代码下载:【点此下载】
转载请注明:魔力Python » 一起来写个简单的解释器(8)