Skip to content

Latest commit

 

History

History
34 lines (18 loc) · 5.41 KB

File metadata and controls

34 lines (18 loc) · 5.41 KB

3.2评估策略

评估也是解析器实现(无论它们解释的是哪种语言)分歧最大的地方。评估时有很多不同的策略可供选择源代码。我已经在本书中的介绍中暗示了这一点,我们在那里简要介绍了这一点看看不同的解析器架构。既然我们到了这里,掌握了AST,如何处理它以及如何评估我们这颗闪亮的树的问题比以往任何时候都重要。因此再次查看不同的选项是值得的。

在我们开始之前,同样值得注意的是,解释器和编译器之间的界限是模糊的。解析器的概念是不会留下可执行文件的东西(与编译器相反,编译器可以留下可执行文件)在查看时变得非常模糊在现实世界和高度优化的编程语言实现中。

话虽如此,处理AST的最明显和最经典的选择是解释它。遍历AST,访问每一个节点并执行节点表示的操作:打印一个字符串,添加两个数字,执行一个函数的主题——一切都在运行中。以这种方式工作的解释器成为“tree-walking 解释器”并且是解释器的原型。有时它们的评估步骤之前是重写AST的小优化(例如删除未使用的变量绑定)或将其转换为另一个更适合的中间表示(IR)递归和重复评估。

其他的解析器也遍历AST,但不是解析AST它自己,它首先转换它到字节码。字节码是另一种AST的IR并且一个非常密集的IR。精确的格式以及由哪些操作码(构成字节码的指令)组成取决于客机和主机编程语言。但总的来说,操作码与大多数汇编语言的助记符非常相似。可以肯定地说,大多数字节符定义都包含用于push和pop堆栈操作的操作码。

但字节码不是自然的机器码,也不是汇编语言。不能也不会由操作系统和运行解释器的机器的CPU执行。相反,它由虚拟机解释,这是解释器的一部分。就像VMWare意义和VirtualBox模拟真实机器和CPU,这些虚拟机模拟机器理解这种特殊的字节码格式。这种方法可以产生很好的性能。

这种策略的变化根本不涉及AST。解析器不是构建AST,而是直接发出字节码。现在,我们还在讨论解释器还是编译器?发出字节码然后被解释(或者我们应该说:“执行”?)是一种编译形式吗?我告诉过你:这条线变得模糊了。为了让它更加模糊,考虑一下:一些编程语言的实现解析源代码,构建一个AST并将这个AST转换为字节码。但是,不是直接在虚拟机中执行字节码指定的操作,而是在字节码执行之前,虚拟机将字节码编译为本机机器代码-恰到好处。这成为JIT(即“即时”)解释器/编译器。

其他人跳过编译为字节码。它们递归地遍历AST,但在执行它特定的分支之前,接天被编译为本地机器代码。然后被执行。再一次,“即时”。

对此的一个轻微变化是一种混合解释模式,其中解释器递归地评估 AST,并且只有在多次评估 AST 的特定分支后,才将分支编译为机器代码。

很美妙,不是吗?如此不同的方式去运行关于评估的工作,如此多的曲折和变化。

选择哪种策略在很大程度上取决于性能和可移植性需求、正在解释的语言以及您愿意走多远。递归评估AST的树遍历解释器可能是所有方法中最慢的,但易于构建、扩展、推理并且与它实现的语言一样可移植。

编译为字节码并使用虚拟机来评估所述字节码的解释器会快很多。 但也更复杂,也更难构建。 将 JIT 编译与机器代码混合在一起,现在如果您希望解释器在 ARM 和 x86 CPU 上工作,您还需要支持多种机器架构。

所有这些可选项将会被找到在真实世界的编程语言中。并且大多情况下,所选择的方法随着语言的生命周期而改变。Ruby就是一个很好的例子。直到升级至1.8版本,解释器都是一个树遍历解释器,在遍历它的同时执行AST。但是在1.9版本中切换到了虚拟机架构。现在Ruby解释器解析源代码,构建一个AST,然后将该AST编译成字节码,然后在虚拟机中执行。性能提升是巨大的。

WebKit JavaScript 引擎 JavaScriptCore 及其名为“Squirrelfish”的解释器也使用 AST 遍历和直接执行作为其方法。 然后在 2008 年转向虚拟机和字节码解释。 现在引擎有四个 (!) 不同的 JIT 编译阶段,它们在解释程序的生命周期中的不同时间启动——这取决于程序的哪个部分需要最佳性能。

另一个例子是Lua。Lua编程语言的主要实现最初作为编译器编译为字节码并在基于寄存器的虚拟机中执行字节码的解释器。在第一次发布12年后,该语言的另一个实现诞生了:LuaJIT。LuaJIT的创建者Mike Pall的明确目标是创建最快的Lua实现。他做到了。通过JIT将密集的字节格式编译为针对不同架构的高度优化的机器代码,LuaJIT实现在每个基准测试中都击败了原始的Lua。不仅仅是一点,不;有时快50倍。

所以,所有的解释器最初都都是从最小规模开始的,还有改进的余地。这正是我们要做的。有很多方法可以构建更快的解释器,但不一定是最容易理解的方法。我们在这里学习,理解并能够建立我们的工作。

< 3.1赋予符号意义 > 3.3一个Tree-Walking解释器