自制脚本语言[5.1] if-else分支语句

注意
本文最后更新于 2023-07-30,文中内容可能已过时。

本篇来尝试描述下if-else语句如何进行实现。

基本内容

语法树部分

先掏出之前给读者看过的这张图, 然后笔者来解释一下其中的内容。

if-else语法树

显而易见, 左侧部分是源代码, 右侧部分是一个大致的AST图示。

前两个部分就是一个赋值语句, 一个声明语句。 没什么好描述的。

由图示可知下面的内容。

  • if-else 语句 拆分成了一个单独的if语句和一个单独的else语句
  • if语句继续拆分成一个condition部分,一个执行代码块部分。
  • 所以我们现在就有了三个小部分。
    • if.condition if的 条件部分
    • if.block if的代码块部分
    • else else 语句的代码块部分
  • 这三个小块的逻辑也比较简单。 if.condition == true的时候执行if.block 否则执行else

直接在AST上进行运算

如果像现在的langX一样直接在语法树上进行运算的话, 按照下面的方式处理应该就可以了。

笔者现在不建议这样做,因为字节码在进行跳转的时候应该比这种方式简单太多了。

  1. 先运算if.condition 部分的代码, 然后把结果保存
  2. if块的地方判断结果, 并把if.condition的结果设置成自己的结果
    1. 如果if.condition部分是true 则执行if.block。 否则什么都不做
  3. if_else部分 判断if节点的值, 如果为false,则执行else部分

使用字节码进行运算

使用字节码尝试实现的话,核心部分应该在于跳转,所以加上适当的跳转字节码即可。

就像我们前面说过的, 字节码是一条一条指令进行执行的, 所以我们再适当的地方插入适当的跳转指令即可。

我们先来看几个自定义指令基于栈的指令。

  • cjmp 有条件的跳转指令 全写应该是:condition jump
    • 此指令存在一个参数, 表示要跳到的指令序号。
    • 由于序号在本文中难以确定, 且可能会变化, 所以使用标签来代指。
    • 这个指令首先获取栈顶元素, 并将该元素出栈
    • 之后判断栈顶元素的值, 如果值是false的话, 就跳转, 否则此指令无任何效果
  • jmp 无条件的跳转指令
    • 此指令存在一个参数, 表示要跳到的指令序号。
    • 由于序号在本文中难以确定, 且可能会变化, 所以使用标签来代指。
    • 直接跳转到指定的行数
  • seti 设置一个变量的值为一个整形数字
    • seti [varName] [intValue]
  • dec 声明一个变量
  • abci i 结尾的表示有一个参数是直接的int值。 很可能是第二个参数。
  • label: 使用冒号结尾的当做一个标签, 去掉冒号的部分即为标签的名字。
  • 在真正的字节码里面应该是不直接使用变量名的, 而是会把变量名转换成数组的索引。 笔者目前只用于示意说明,所以这样写了。
  • 附加阅读 字节码列表
    • 字节码列表 里面的字节码进行了适当的优化, 所以和本篇内容略微有些出入。

下面看一段模拟指令。 (伪代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 赋值和声明
seti a 1
dec b 

# if.condition
# gt = greater than(大于)
# 此指令会将一个bool 类型的结果压栈
gti a 0
cjmp else_start

# if.block
seti b 1
jmp end_of_if

# else
else_start:
seti b 2

# out of if-else
end_of_if:

这部分的逻辑其实并不算特别复杂。只不过在字节码层面可能没有那么明确的区分 分支。

并且在这个示例中使用了一个反转的做法,当条件值为false的时候才进行跳转, 否则什么都不做。

笔者直接把if.block的语句放在了if.condition下面, 是接着给的,当条件满足的时候,直接挨个执行指令, 然后在if.block结尾的地方加上一个无条件跳转语句, 跳出当前的if-else部分。

字节码部分可能不是那么好懂, 不过没关系, 后面会有更多类似的内容, 看懂了就会懂了。

0%