pymatlab_interp 解释器开发教程第五部分: 语句与控制流扩展。
pymatlab_interp 解释器开发教程 (5) 语句与控制流扩展
引言
之前已经完成了表达式求值的整个流程,并且实现了自动代码生成。
这一节就扩展一下语句和控制流。
语句
先说明一下表达式,语句和控制流的关系与区别。
-
表达式是编程语言中用于计算值的代码片段。 例如,在数学运算
2 + 3中,2 + 3就是一个表达式,其结果是一个数值5。表达式可以包含变量、函数调用、操作符等元素。 -
语句则是执行某些操作或指令的代码块。语句不会返回一个值,它们的目的在于改变程序的状态。 例如,赋值语句
x = 5将数字5赋值给变量x;打印语句print("Hello, world!")则会输出一段文本到屏幕上。 -
控制流指的是程序中指令的执行顺序。在大多数情况下,程序会从上到下依次执行每一行代码,但通过使用控制流结构,如条件语句和循环,可以改变这一顺序。
可以发现,表达式可以是一个语句,控制流也可以是语句。
控制流有两种典型的语句:
- 条件语句:(如
if,else if,else)允许程序基于特定条件选择性地执行代码块。 - 循环语句:(如
for,while)使代码块能够重复执行,直到满足某个条件为止。
为了实现控制流,需要对代码分块(划分作用域)。分块的过程也可以看作语句。
甚至内置的函数操作,比如我们可以让 DISP 打印直接作为一种语句执行。
接下来就扩展 parser 和 interpreter 实现这些。
Stmt 实现
为了表示源代码的结构,需要定义表示语句的节点。
下面是对Stmt类及其派生类的说明,它们代表了源代码中不同的语句类型:
-
Stmt基类:这是所有语句节点的基类,它包含了基本的初始化方法和一个accept方法,后者用于实现访问者模式。 -
Expression类:表示表达式语句,即一个单独的表达式作为语句执行, 例如一个赋值或计算表达式。这个类包含一个expression属性,该属性是一个Expr类型的实例,表示具体的表达式。 -
Block类:表示代码块语句,即一系列语句的集合,通常在控制流结构如if或while中使用。Block类包含一个statements属性,这是一个Stmt类型的列表,表示代码块中的所有语句。 -
Disp类:表示输出语句,如打印语句,它包含一个expression属性,表示要输出的表达式。 -
If类:表示条件语句,具有condition、then_branch和else_branch属性,分别表示条件表达式、真分支和假分支的语句。 -
While类:表示循环语句,包含condition和body属性,分别表示循环条件和循环体内的语句。
和之前的 Expr 一样,这些类的accept方法实现了访问者模式,允许通过传递一个访问者对象给accept方法,来访问和处理AST中的各个节点,而无需修改这些节点的类定义。
我们可以使用之前提到的自动生成代码的框架,用以下代码创建 Stmt 类:
1 | |
生成的 Stmt 如下:
1 | |
parser 扩展
parser 需要做如下扩展:
其中 statement 函数作为主入口点,根据当前解析到的令牌类型决定调用哪个子函数进行更深入的解析。
-
statement: 根据当前的输入解析不同类型的语句,如if语句、while语句、打印语句或简单的表达式语句。 -
assignment: 解析赋值语句。它首先尝试解析一个可能带有等号的表达式,如果遇到等号,则解析出赋值操作的右侧表达式,并构建一个Assign节点,表示赋值操作。 -
expression: 调用assignment来解析一个表达式。 -
expressionStatement: 解析一个表达式语句,以换行符结束,构建一个Expression节点。 -
block: 解析一个由多个语句组成的代码块,直到遇到指定的结束标记,返回一个Block节点,其中包含一系列语句。 -
printStatement: 解析一个打印语句,以换行符结束,构建一个Disp节点。 -
ifStatement: 解析一个if语句,包括条件表达式、then分支和可选的else分支,构建一个If节点。 -
whileStatement: 解析一个while循环语句,包括条件表达式和循环体,构建一个While节点。
代码实现如下:
1 | |
interpreter 扩展
解释器需要处理不同的语句类型,执行代码块,以及评估表达式的值。
因此 interpreter 需要的扩展如下:
-
interpret: 解释器的入口点,接收一个语句列表,依次执行每个语句。 -
execute: 执行单个语句。如果传入的语句不为空,它会调用stmt.accept(self)方法,利用访问者模式执行相应的语句处理逻辑。 -
executeBlock: 用于执行一组语句,即代码块。首先保存当前的环境,然后设置一个新的环境,在这个新环境中依次执行代码块中的每个语句,最后恢复原来的环境状态。 -
evaluate: 评估表达式的值,返回表达式的结果。这里使用expr.accept(self)调用访问者模式,基于表达式的类型执行相应的评估逻辑。 -
visit_Expression: 处理表达式语句,通过调用evaluate方法来评估并忽略其结果。这是因为表达式语句通常是为了其副作用(如赋值),而不是为了返回值。 -
visit_Block: 处理代码块语句,调用executeBlock方法执行其中的所有语句。 -
visit_Disp: 处理输出语句,先评估表达式的值,然后将其打印到标准输出。 -
visit_If: 处理条件语句,先评估条件表达式的值,如果条件为真,则执行then_branch;如果提供了else_branch且条件为假,则执行else_branch。 -
visit_While: 处理循环语句,评估条件表达式的值,只要条件为真,就持续执行循环体内的语句。
代码实现如下:
1 | |
interpreter测试
简单起见,直接在 __main__ 里测试,代码如下:
这里多写了几个测试样例,可以选择执行测试。
1 | |
这里我们给出 stmt_test_code3 的执行结果,对比源代码可知执行结果是正确的。
1 | |
附录:parser.py 完整代码
1 | |
附录:interpreter.py 完整代码
1 | |