如何实现一个简单的堆栈机器



我读过几篇关于这方面的文章,大致了解了最常用的方法。最简单和常见的方法似乎是反向波兰记法,如下所示。为了计算表达式,前两个操作数被推送到堆栈上,读取指令,就像加法等操作一样,该运算符对堆栈的两个最顶层元素进行运算,弹出两个最高层元素,并将结果推送到堆栈。然而,这似乎只适用于我看到的例子,而在其他情况下则不然。以下是一些例子:

表达式:

(11 * 22 + 33 * 44) / 121

注意如下说明:

push 11
push 22
mult
push 33
push 44
mult
add
push 121
div

我理解这一点。同样,在另一篇文章中给出了另一个例子:

表达式:

(12 + 45) * 98

有说明:

push 98
push 12  
push 45  
+    
*

我也理解这一点。但假设在第一个例子中,我们想像下面这样扭转划分:

Instead of:
(11 * 22 + 33 * 44) / 121
have:
121 / (11 * 22 + 33 * 44)

现在,与其在最后向右推121,不如在开始时向右推,看起来像这样:

push 121
push 11
push 22
mult
push 33
push 44
mult
add
div

正如你所看到的,在最初的例子中,121是第一个被推的东西,而不是倒数第二个。然而,因为其他部分都在括号中,所以应该首先进行评估,你如何绕过这一点?

我发现的其他例子也有同样的问题。如何正确排序值和操作?你需要向前看吗?或者有没有一种简单的方法可以遍历表达式并创建指令?从我所读到的内容来看,这似乎是评估表达式的最简单的方法,它们似乎意味着编写指令真的很容易,就好像你可以遍历表达式并为其创建正确的反向抛光符号一样。我很难理解如何以正确的方式对值和指令进行排序。

正如我们在评论中所讨论的,求值顺序与运算符优先级无关。例如,在Java中,子表达式是从左到右计算的。在C中,未指定评估顺序。相反,它有一个序列点的概念,基本上说"无论你需要做什么,只要确保它发生在这个分号之前。">

所以你的反向抛光符号可以从左到右构建,而不需要重新排列括号。如果你根本不确定如何构建RPN,那就去读一本关于解析或编译器的书。《龙》一书以一个将表达式翻译成RPN的详细例子开场。

现在,有一个例子,评估顺序确实很重要。在大多数语言中,&&||短路,因此在某些情况下我们需要避免评估RHS。这可以通过跳转指令来完成。

例如,如果我们有1 > 2 && 2 < 3,我们可能会发出以下指令:

push 1
push 2
>
jmp_f lbl0  ; jump if false to label lbl0
push 2
push 3
<
&&
label lbl0

(当然,您的VM可能没有所有这些指令,但这只是如何有条件地评估表达式的一个示例(

最新更新