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

时间:2018-11-29 08:58:45

标签: parsing stack expression

我已经阅读了一些有关此的文章,并大致了解了最常用的内容。最简单和常用的方法似乎是反向波兰语表示法,如下所示。为了评估表达式,将前两个操作数压入堆栈,读取一条指令,如加法之类的操作,该运算符对堆栈的两个最高位元素进行操作,弹出两个最高位元素,结果为推入堆栈。但是,这似乎仅适用于我所看到的示例,但不适用于其他情况。以下是一些示例:

表达式:

(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项是推送的第一条,而不是第二项。但是,由于其他部分放在方括号中,因此应该首先对其进行评估,您如何解决这个问题?

我在发现的其他示例中也遇到了同样的问题。您如何正确排序值和运算?您需要向前看吗?还是有一种简单的方法可以遍历表达式并创建指令?从我阅读的内容来看,这似乎是评估表达式的最简单方法,并且它们似乎暗示着编写指令确实非常容易,就好像您可以跨表达式运行并为其创建适当的反向修饰符号一样。我无法理解如何以正确的方式对值和指令进行排序。

1 个答案:

答案 0 :(得分:1)

正如我们在评论中讨论的那样,求值顺序与运算符优先级无关。例如,在Java中,子表达式从左到右求值。在C中,未指定评估顺序。而是,它具有 sequence points 的概念,该概念基本上说:“无论需要完成什么,只要确保它在此分号之前发生即可。”

因此,可以按从左到右的方式构造您的反向抛光符号,而无需重新排列括号。如果您完全不确定如何构造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可能没有所有这些说明,但这只是如何有条件地计算表达式的一个示例)