代码之家  ›  专栏  ›  技术社区  ›  Robin Hoksbergen

解释器基于堆栈的VM的替代方案

  •  3
  • Robin Hoksbergen  · 技术社区  · 10 年前

    当为另一种语言构建解释器时,通常建议创建一个基于堆栈的虚拟机,它可以解释实际解释器生成的字节码。然后,解释器将由两部分组成:翻译器和虚拟机本身,翻译器将指令从高级语言转换为虚拟机的字节码。

    我的问题是:对于解释性语言,有哪些替代方案?例如,跳过虚拟机,并使用C语言中的函数实现所有指令,这是可能的(也是可行的)吗?在某种程度上,在我看来这应该是可能的,但也许你最终会实现某种类型的最小VM,以实现更复杂的功能。还有其他选择吗?

    2 回复  |  直到 9 年前
        1
  •  2
  •   Some programmer dude    10 年前

    建议制作基于堆栈的VM,因为它们很容易制作。

    另一种常见的VM类型是基于寄存器的,其中值存储在寄存器中而不是堆栈中。

    口译员和虚拟机还有许多其他变体。您可以有一个生成解析树的编译器和一个解释这些树的解释器(但如果它是使用递归函数实现的,那么可以说它仍然是一个基于堆栈的VM)。


    使编译器生成另一种语言的代码而不是生成某种类型的机器代码(用于VM或真实机器)也并不罕见。C语言是这些类型编译器的通用目标语言,因为C语言及其编译器无处不在。但是,你不再有VM或解释器,你只有一个编译器/翻译器。

        2
  •  1
  •   uliwitness    10 年前

    你的建议有点可能。C实际上不允许您操作堆栈,并且当您调用函数时,它不知道它周围的本地变量,因此您需要在堆上分配一块内存,以保留一些用于脚本语言本地变量的伪“堆栈空间”,并将其传递给每个函数(或将其填充到线程全局)。对于脚本语言的函数调用,还需要该堆栈的基指针。

    一旦你做到了这一点,你已经完成了获得基于堆栈的语言所需要做的大部分工作。所以你最好做剩下的。要使用实际的堆栈和基指针,你必须降到机器语言级别。

    如果您的语言是基于寄存器的,那么它仍然需要一个用于访问局部变量的堆栈(它只是不太经常使用它),那么您就不需要将它用于指令参数。如果我可以犯罪地简化,基于3地址注册器的虚拟机是基于堆栈的虚拟机的一种特殊情况。

    字节码解释器的另一种方法是让指令包含指令ID,然后将其用作函数指针数组的索引,每个函数指针实现一条指令。

    显然,这样做会影响性能。如果你的指令做得足够简单,你可以通过直接在机器代码中实现它们来节省CPU周期,并且省去函数调用的开销(通常可以忽略不计),甚至可以使用真正的堆栈而不是假的堆栈。

    这取决于你的需要。对于大多数情况,特别是如果这是您的第一个解析器/解释器/VM,我建议使用函数指针数组和伪堆栈。它很简单,不太难调试,而且在现代机器上足够快。你以后总是可以进去写一个优化的版本,做不同的事情。

    例如,一种方法是为函数调用生成足够的机器代码,然后在生成的机器代码中插入指向此类函数的指针。因此,每个脚本都成为一个编译代码块,但不必编写完整的编译器。从那时起,您可以通过为单个关键指令生成汇编程序来改进这些指令,将不太常用的东西保留为函数。这稍微改善了代码的局部性,这是一个微小的微优化可以帮助。但只是其中之一。

    哦,大约一个月前,我写了一篇博客,从初学者的角度介绍如何制作编译器(和字节码解释器),这可能会有所帮助: http://orangejuiceliberationfront.com/how-to-write-a-compiler/