原文:Let’s Build A Simple Interpreter. Part 8
译文:
引言与目标
今天我们将讨论一元运算符,即一元加(+)和一元减(-)运算符。
今天的很多内容都基于前一篇文章的内容,因此如果你需要复习,可以回到第七部分重温一下。记住:反复练习是学习的关键。
今天我们将完成以下任务:
- 扩展语法以处理一元加和一元减运算符
- 添加一个新的
UnaryOp
抽象语法树(AST)节点类 - 扩展解析器以生成包含
UnaryOp
节点的AST - 扩展解释器,添加新的
visit_UnaryOp
方法来解释一元运算符 让我们开始吧!
目前为止,我们只处理了二元运算符(+、-、*、/),这些运算符作用于两个操作数。
一元运算符
那么什么是一元运算符?一元运算符是只作用于一个操作数的运算符。
以下是关于一元加和一元减运算符的规则:
- 一元减号(-)运算符对其操作数取反
- 一元加号(+)运算符不改变其操作数
- 一元运算符的优先级高于二元运算符 +、-、* 和 /
在表达式“+ - 3”中,第一个‘+’表示一元加运算,第二个‘-’表示一元减运算。表达式“+ - 3”相当于“+ (- (3))”,结果等于**-3**。你也可以说表达式中的-3是一个负整数,但在这里我们将其视为一元减运算符,操作数为正整数3。
让我们看看另一个表达式,“5 - - 2”:
在表达式“5 - - 2”中,第一个‘-’表示二元减法运算,而第二个‘-’表示一元减运算,即取反。
再来看几个例子:
现在我们需要更新语法,以支持一元加和一元减运算符。我们将修改 factor
规则并添加一元运算符,因为一元运算符的优先级高于二元运算符 +、-、* 和 /。
这是当前的 factor
规则:
这是更新后的 factor
规则,以处理一元加和一元减运算符:
如你所见,我扩展了 factor
规则,使其能够自引用,从而可以推导出类似“- - - + - 3”这样的合法表达式,表达式中包含了多个一元运算符。
以下是完整的语法,现在它可以推导出包含一元加和一元减运算符的表达式:
实现与代码解析
下一步,我们将添加一个AST节点类来表示一元运算符。
实现代码如下:
1 | class UnaryOp(AST): |
构造函数接受两个参数:op
表示一元运算符的token(加号或减号),expr
表示一个AST节点。
更新后的语法修改了 factor
规则,因此我们也需要在解析器中的 factor
方法中进行相应的调整。我们将在该方法中添加代码以处理 “(PLUS | MINUS) factor” 子规则:
1 | def factor(self): |
现在我们需要扩展 Interpreter
类,添加 visit_UnaryOp
方法来解释一元节点:
1 | def visit_UnaryOp(self, node): |
继续前进!
接下来,我们手动构建表达式“5 - - - 2”的AST,并将其传递给解释器,以验证新的 visit_UnaryOp
方法是否正常工作。你可以在Python shell中执行以下操作:
1 | from spi import BinOp, UnaryOp, Num, MINUS, INTEGER, Token |
从视觉上来看,上述AST的结构如下:
你可以从GitHub直接下载本文解释器的完整源代码。试试看,看看你更新后的基于树的解释器是否能够正确地求解包含一元运算符的算术表达式。
实践与扩展
这是一个示例会话:
1 | python spi.py |
我还更新了 genastdot.py
工具,以支持一元运算符。以下是几个带有一元运算符的表达式生成的AST图像示例:
1 | $ python genastdot.py "- 3" > ast.dot && dot -Tpng -o ast.png ast.dot |
1 | $ python genastdot.py "+ 3" > ast.dot && dot -Tpng -o ast.png ast.dot |
1 | $ python genastdot.py "5 - - - + - 3" > ast.dot && dot -Tpng -o ast.png ast.dot |
1 | $ python genastdot.py "5 - - - + - (3 + 4) - +2" \ |
现在来个新练习:
安装 Free Pascal,编译并运行 testunary.pas
,验证其结果与 spi
解释器的结果是否一致。
今天的内容到此为止。在下一篇文章中,我们将讨论赋值语句。敬请期待,我们下次再见!
推荐阅读书目列表
以下是我推荐的几本书,它们将帮助你学习解释器和编译器:
- 《Language Implementation Patterns: Create Your Own Domain-Specific and General Programming Languages (Pragmatic Programmers)》
- 《Writing Compilers and Interpreters: A Software Engineering Approach》
- 《Modern Compiler Implementation in Java》
- 《Modern Compiler Design》
- 《Compilers: Principles, Techniques, and Tools (2nd Edition)》