23 Jumping Back and Forth
1 JVM用了什么机制来实现的if?这章来回答。
2 Lox是个结构化编程的语言。但是clox bytecode 不是
即使字节码是无结构的,我们也要确保编译器只生成与 Lox 本身保持相同结构和嵌套的简洁代码。
3 cpu也这样。他底下就是在jump goto。在底层,goto是唯一的 control flow.
为啥底层的bytecode 不能是结构化编程呢?
4 if 这么搞
expression计算弄完之后,会将值放在栈顶。用这个来判断是否执行if 的body
5 OP_JUMP_IF_FALSE
这个指令有个operand。来决定如果是false,ip要偏移多少
当写入这个指令时,没有编译完body。所以不知道要偏移多少
6 backpatching 方法来搞顶偏移量
我们先给OP_JUMP_IF_FALSE
这个指令的operand 一个placeholder。
但编译完body之后。我们可以把这个placeholder的值换一下。
7 用这个函数来实现
因为稍后会有两条不同的指令使用这个辅助指令。我们使用两个字节作为跳转偏移操作数。16 位偏移量可让我们跳转多达 65,535 字节的代码.
compile 完if的body。用这个函数来把真的数填上
这个-2 就是为了上面那两个0xff
8 接下来这样就是实现了
如果时false,就跳过if 的body
9 这里可以用c的if 来实现
Let’s assume we have a function falsey()
that takes a Lox Value and returns 1 if it’s falsey or 0 otherwise.
10 else 分支
如果if时true。那么运行完if 的true body(也就是then brach)后还要跳过else
11 then branch后,还要跳转一下
如果有else。在else分支 编译后 要patch一下 这个jump的位置
这个jump指令无条件
12 清理 if 的判断值 (留在了栈上)/每个statement对栈应该无影响。
这里显示的加了pop 操作。这里时为了logical operator可以复用OP_JUMP_IF_FALSE
指令。
最终的逻辑是这样的。不管写不写else都有个隐式的else
然后这样debug code
13 logical operator(and 、or)和+ - 不一样。因为判断是会短路。像是if的变种
and 操作
运行到and_,左侧已经compile完了。这个值如果是false,就不用往右边看了。
如果是true,就不用看这个值了,所以pop以下,就看右边的值就行了。
这时能看出来why
OP_JUMP_IF_FALSE
会把值留在stack的顶部。当左侧是false,这个值就是整个expression的结果。
expression都要有返回值的。这里其实可以创建一个会pop出来的值的指令。
14 or操作
如果左边的值为真,那么我们就跳过右边的操作数。因此,我们需要在值为真时跳转。本来可以加个新的指令,但是这里没加。
15
如果左侧operand是false,则小跳以下。跳过的是无条件跳越到右侧operand。
如果左侧operand是true,则执行下一条无条件跳到右侧operand。(这不是最好的实现。)
这里为啥左侧是true的时候,要pop?为了和右边计算。
当这个值为true的时候,这个值留在栈顶。就当返回值了。
这里的pop的意思是不要这个值了。因为0 或上任何数都是那个数
16 while
先计算while()里的expression。然后看这个值是否是false。
如果false。就跳过下面的statement啥的。然后pop出这个expression的值
如果true。先pop出这个expression的值。然后运行statement。
17 但这次有个loop
loopstart用来记录expression的位置。loop时每次都要计算一下这个表达式。
18 然后有这个emitJump()
and patchJump()的结合
+2 时OP_LOOP的两个offset,也要加上。
OP_LOOP
和OP_JUMP
在语义上并无区别,估计是不想改这个函数
还有一个是+ 一个是-
19
20 for loop的特点
这次还会用 已经有的东西来搞
如果只是for(;;)
21 初始化clause
我们利用var
关键字的存在来区分变量声明和表达式。对于表达式,我们调用expressionStatement(
) 而不是 expression()
。它会查找分号(我们在这里也需要分号),并发出OP_POP
指令来丢弃值。我们不希望初始化程序在堆栈上留下任何东西。
如果for()里定义了变量那么这里就有了scope
22 condition clause
因为这个condition可有可无。所以如果初始化后下个字符不是;那么就是有这个内容。for(int i =0;i<10;i++)
如果条件是false,就跳出来。把判断值也pop。
然后在后面把这个跳出的offset给补上。
23 increment clause 有难度。因为他是先编译但在for 的body运行后才运行。
首先,我们发出一个无条件跳转,跳过 increment clause’s code,跳到循环的主体。
接下来,我们编译increment clause本身。这通常是一个赋值(执行它只是为了它的副作用)。也要发出一个 pop 来丢弃它的值。
最后的部分
首先,我们发出一条循环指令。这是一个主循环,它将我们带回for
循环的顶部--如果有条件表达式的话,就在条件表达式之前。该循环紧接在递增之后,因为递增在每次循环迭代结束时执行。
上面的statement后 还有个emitloop(loopStart);
这里这个emitloop会代码jump到increment clause code。而不是loop的top。
如果没有increment clause code,则会跳到loop的top。
24 现在clox 图灵完备了。只多了3个指令。牛逼。
25 why goto不好?