跳转到内容

鹦鹉虚拟机/鹦鹉编译器工具

来自 Wikibooks,开放世界中的开放书籍

鹦鹉编译器工具

[编辑 | 编辑源代码]

本书的第一部分介绍了鹦鹉平台的一些基础知识,以及鹦鹉为其他高级语言提供的各种功能。需要注意的是,鹦鹉提供的功能和能力超出了大多数单个语言的需求。这是因为鹦鹉的目标是成为一个支持多种高级动态编程语言的平台,每种语言都具有不同的功能集。这些编程语言中的一些最新版本,例如 Perl 6 和 Python 3000,都计划了非常有趣的功能集,这些功能集无法被任何其他现有的解释器或虚拟机平台很好地支持。

到目前为止,PIR 和鹦鹉编程一直处于相对较低的级别,但鹦鹉的目标是支持高级语言。为了促进这一目标,鹦鹉提供了编译器设计人员可以用来快速轻松地创建下一代语言(如 Python 3000 和 Perl 6)需要的高级语言功能的工具。这些工具统称为鹦鹉编译器工具 (PCT)。PCT 是一套工具,人们可以使用它们在鹦鹉平台上快速轻松地实现新的编程语言。我们将在本章以及后续的一些章节中讨论它们。

解析和编译:鹦鹉的工作原理

[编辑 | 编辑源代码]

鹦鹉被设计成一个高度模块化的系统。这意味着许多组件可以根据需要进行互换。其中一些更改需要在编译时指定,但其他更改可以在运行时执行。

将程序输入鹦鹉需要经过多个步骤。以下是这些步骤的简要概述

解析器和词法分析器
鹦鹉的第一阶段是解析器和词法分析器(词法分析器是“词法分析仪”的简称)。我们将在本章后面以及未来的章节中更详细地讨论这些组件的操作。解析器和词法分析器读取 PIR 或 PASM 中的输入代码,并将其转换为称为抽象语法树 (AST) 的数据表示形式。AST 是一种以对计算机非常易于处理的方式表示程序指令的方法。
编译器
编译器单元将 AST 中的信息转换为鹦鹉字节码格式。字节码是一组二进制机器语言指令。从这里,鹦鹉可以直接执行字节码,或者可以将字节码保存到磁盘并在以后执行它。
优化器
优化器获取生成的字节码,并尝试使其更小、更快、更高效。经过正确优化的字节码通常比未优化的字节码执行速度更快。
JIT 编译器
JIT 是“Just In Time”的缩写,JIT 编译器尝试将鹦鹉字节码转换为本地机器代码。这通常会带来很大的速度提升,但高度依赖于平台,并且目前在任何系统上都无法运行。
解释器
一旦程序被转换为字节码,该字节码就会加载到解释器中,并在其中执行。

这只是对这些组件的非常简要的概述,我们将在后面的章节中更详细地讨论它们。然而,值得在这里注意的是,许多这些组件都是模块化的,如果要使用不同的组件,可以将其替换掉。例如,如果您已经为特定语言编写了一个解析器,则无需使用 PCT 重写解析器,而是可以将您现有的解析器加载到鹦鹉中。当然,您可能需要进行修改以确保您的自定义解析器输出正确的 AST,但这与从头开始完全重写语言解析器相比,是一个很小的代价。

PCT 设计流程

[编辑 | 编辑源代码]

PCT 包括创建新编程语言编译器所需的一些工具和设计步骤。以下是创建新编译器所需的一些步骤的简要介绍

  1. 创建语言外壳
  2. 创建语法文件
  3. 创建语法动作文件
  4. 创建必要的类、内置函数和 PMC
  5. 创建驱动程序

创建编译器后,可以使用它来运行用高级语言编写的程序。以下是运行编译器涉及的一些步骤

  1. 将语法编译成鹦鹉抽象语法树 (PAST)
  2. 将 PAST 编译成鹦鹉优化语法树 (POST)
  3. 将 POST 编译成鹦鹉字节码 (PBC) 或 PIR
  4. 在鹦鹉上运行 PBC 或 PIR

这应该可以让您大致了解创建编译器需要做什么以及编译器如何工作。我们将在本章以及本节接下来的几章中详细阐述每个步骤。

创建语言外壳

[编辑 | 编辑源代码]

新的语言外壳包含许多组件。有我们提到的语法和动作文件,但您还需要一个驱动程序来创建 HLLCompiler 对象并启动编译。此外,如果您想要任何内置函数或类,则需要编写它们。为了简化整个过程,您需要有一个 makefile 来处理编写语言的所有构建步骤。

幸运的是,有一个工具可以简化此过程,即mk_language_shell.plmk_language_shell.pl 是一个 Perl 5 程序,它创建创建新语言编译器所需的所有必要文件,并用一些有用的默认代码填充这些文件。它位于鹦鹉根文件夹中的tools/dev/文件夹中。要从您的 shell 运行此程序,请转到鹦鹉根文件夹并键入

tools/dev/mk_language_shell.pl <LANGUAGE_NAME> <PATH>

这里,<LANGUAGE_NAME> 是您新语言的名称,<PATH> 是您希望将其存储到的目录。按照惯例,所有语言项目都存储在languages/目录中。使用此目录可以使其他构建工具更容易找到它。

例如,如果我们要创建一个名为“mylanguage”的新语言,我们可以编写

tools/dev/mk_language_shell.pl mylanguage languages/mylanguage

这将创建所有必要的文件,包括语言项目的 makefile。请注意,随着时间的推移,许多这些默认文件(包括 makefile)都需要编辑或修改。您可以作为练习打开 makefile 并查看事物是如何构建的。如果您以前从未见过 makefile,那么这是一个了解它们是什么以及它们如何工作的机会。

语法和动作

[编辑 | 编辑源代码]
注意:PGE 实际上是Perl 6 语法引擎。Perl 6 编译器调用 PGE 来处理 Perl 6 源代码中的语法规则。

语法,通常是具有“.pg”文件扩展名的文件,使用鹦鹉语法引擎 (PGE) 进行编译。PGE 是鹦鹉的 Perl 6 规则引擎的实现。PGE 使用递归下降解析器,尽管某些组件(如表达式)可以使用自下而上的解析器来提高效率。如果您阅读过关于编译器构造的书,这应该对您有所帮助。如果没有,解析器的详细信息此时并不特别重要。

不幸的是,在我们进一步深入之前,我们需要介绍一些术语。熟悉语法和解析器的人可以跳过本节。其他所有人应该尝试通读它,因为它是有价值的相关信息。

一个称为词法分析器的工具读取输入文件并将文本块转换为称为“标记”的内容。然后,解析器将标记排列成特定的模式,称为“规则”。当规则成功应用于一组输入标记时,则称该规则“匹配”输入。将标记视为句子中的一个词。单独一个词可能没有太多意义。但是,如果您将多个词组合成一个句子,则意图就会变得清晰。解析器将一组标记组合在一起,并尝试将其形成一个“句子”或已知模式。如果找到有效的标记模式,则解析器成功。

在解析过程的每个步骤中,解析器都会从词法分析器接收一个标记。如果解析器拥有足够的标记来构成一个有效的模式,则解析成功。如果它没有足够的信息来形成有效的模式,它会请求下一个标记并再次尝试。大型模式被划分为较小的模式。标记组合成小的模式,小的模式组合成更大的标记。最终,整个代码文件将被简化为单个模式,解析器退出。

在每个步骤中,解析器可以选择使用标记中的信息执行操作。解析器将特定操作与不同的标记类型相关联。对开括号标记执行的操作将与对闭括号标记执行的操作不同。在PGE的情况下,操作是函数,通常用PIR或NQP编写,这些函数创建PAST节点。PAST节点存储在一个表示输入的大树中。这称为解析树。当解析器到达最终匹配并成功时,解析树将传递给工具包的后续阶段进行处理,并最终转换为Parrot字节码。

如前所述,在Parrot上实现新的语言被分解成几个部分。

  • 使用Perl 6语法规则编写语法文件。
  • 使用NQP编写语法操作文件。
  • 用PIR编写驱动程序。
  • 使用PIR(或C,用于PMC)编写内置函数、类和PMC。

一旦你创建了你的语言外壳,所有这些文件都将为你生成。你只需要在必要的文件中填写你的语法和操作,编写其余必要的内置代码,你应该就可以拥有一个工作的编译器。一旦你修改了这些文件以执行你需要的操作,还有一个额外的可选步骤你需要执行。

  • 编写一系列测试模块以验证你的语言是否正常运行。

我们稍后将讨论测试和测试框架。我们将在接下来的几章中讨论编写解析器和操作文件。

驱动程序

[编辑 | 编辑源代码]

驱动程序是编译器的主入口点,它需要执行许多任务。驱动程序的第一个也是最重要的任务是为相关的高级语言创建一个编译器对象,并将命令行参数传递给该编译器对象。编译器对象是一个HLLCompiler对象,HLLCompiler类包含解析命令行参数和初始化编译器所需的所有方法。有关HLLCompiler类的更多信息,请参见附录

驱动程序有许多任务。以下是这些任务,没有特定的顺序。

  1. 指定一个:main函数,该函数启动程序。
  2. 为给定的高级语言(HLL)创建一个HLLCompiler对象。
  3. 向HLLCompiler对象指定任何其他详细信息,以在解析阶段之前更改编译器的操作。
  4. 包含语言运行所需的类库和内置函数。对于大多数语言,这将至少包括一个库加载例程,该例程能够将其他库加载到Parrot中以供HLL及其编写的程序使用。
  5. 声明将与解析器一起使用或将由HLL程序使用的任何全局变量。

除了这些之外,语言设计者可能希望在主驱动程序中执行其他任务。

获取帮助

[编辑 | 编辑源代码]

在编写新的语言编译器时,你可以去许多地方获取帮助。Parrot存储库包含所有当前的Parrot文档,以POD格式。Perl 5程序员会熟悉POD,但其他用户可能不熟悉。POD是一种简单的文档格式,在Perl代码中被视为多行注释。像pod2html这样的特殊程序可以用于将POD文件转换为其他文件类型以进行展示,例如HTML。

languages/目录中有很多语言。如果你正在尝试为你的语言实现某个特定功能,很有可能你可以找到另一个语言如何实现该功能的现有示例。一个非常有用的工具,尤其是在构建PAST节点树或用PIR编写函数时,是Parrot的--target=指令。此指令允许你指定输出转储格式。例如,如果你进入languages/perl6/目录,你可以输入以下内容:

../../parrot perl6.pbc --target=pir

此命令将输出你输入的任何Perl 6指令的PIR。这些选项适用于Parrot,因此所有语言都将使用它们,而不仅仅是Perl 6。以下是一些你可能想尝试的其他目标。

  • pir:打印代码生成的PIR结果。
  • pasm:打印PASM代码结果。
  • past:打印从代码生成的PAST节点树。
  • parse:打印代码的解析树。

尝试所有这些,看看使用不同的语言可以获得什么样的结果。

如果你已经在POD文档和现有代码示例中寻找帮助,那么可能是时候找到一个真正的人来询问了。Parrot开发者和爱好者聚集在#parrot(irc.perl.org)聊天室。Perl 6开发者和爱好者聚集在#perl6(freenode)聊天室。

其他资源和联系方式可在http://www.parrotcode.org/resources.html找到。


上一页 鹦鹉虚拟机 下一页
Parrot调试器 Parrot语法引擎
华夏公益教科书