Skip to content

Latest commit

 

History

History
37 lines (23 loc) · 2.27 KB

2_9.md

File metadata and controls

37 lines (23 loc) · 2.27 KB

第二阶段: 一个 bug 引发的

这是一个比较深刻的广泛的 bug。 :-)

如下的代码能编译,也符合之前对语言的定义。 但运行起来,会有一个运行时错误。可以看到,是一个栈的状态错误:到程序结束的时候,栈上有一个多余的值。

let a
a = 2
a + 1
let b = a + 3
b
Function #0 reading/validation error: At instruction End(@10): Unexpected stack height 1, expected 0

这个值是由第三行的a+1遗留下来的。凡是“求值”表达式,单独一行的,都会引起这个问题。原因可以形象地理解为:有值产生,却没人用这个值,那么这个值就遗留在栈上了。

解决办法看来有两个方向:

  1. 禁止一个求值表达式单独成为一行。
  2. 如果求值表达式单独成行,编译的时候附加一个Drop指令,把多余的值丢弃掉。

这两个方向其实是同一种思路。都需要完成一件事,定义“一行”。当“一行”里仅有一个求值表达式的时候,采取某种措施。 在我们之前的语言定义里没有“一行”的概念。现在需要增加一个——Statement。之前的概念表达式Expr,与“行”是有区别的,一个行可以包含一个或多个表达式,也可以包含表达式和其他一些部件,比如if语句,就包含条件、分支一、分支二等多个表达式。

“行”对于表达能力来说,没有新的增强。从某种角度来说,它只是一个编译单位,只是为了方便编译规则的定义而出现的。

回归到现在这个bug,我们选择解决方法 2,即单独成行的求值表达式,一律添加一个Drop指令。对于最后一行,虽然形式上也是一个单独成行表达式,但比较特殊,除了表达式本身的语义以外,还有一个返回值的语义,这里改下语言定义,最后一个返回值,必须是return xxx

另外,还从表达式Expr中特化出了求值表达式EvalExpr,作为简单计算的表达式的统称,比如字面量、变量取值、算数计算等。因为这类表达式,与广泛意义的表达式,在用途上是不一样的。上面的例子是其中一个,还有一个典型的例子:if语句的条件表达式,只能是求值表达式。