如何开始创建一个JVM编程语言?

100
我使用Lex和Bison在C中创建了一个编译器,用于支持循环、函数声明内函数、递归调用等动态类型编程语言。我还创建了一个虚拟机来运行编译器生成的中间代码。
我正在考虑将其编译为Java字节码而不是我的自有中间代码。
我看到已经有人提问如何创建JVM语言,但我觉得答案不够详细。
所以这里是我的问题:
  1. 我猜想要为JVM创建一种语言,必须阅读JVM规范书籍,除了Dragon Book之外,你还能推荐哪些书籍?我主要关心如何创建JVM语言的书籍或教程,而不是一般的编译器。
  2. 有许多Java库可以读取、写入和更改.class文件,例如jclasslibbcelgnu bytecode等。你会推荐哪一个?此外,你是否知道做同样工作的C库?
  3. 我正在考虑看看可能针对JVM的另一种语言,例如Clojure、Jython或JRuby。但所有这些语言都非常高级和复杂(为它们创建编译器)。我正在寻找一种更简单的(我不介意它是否未知或未使用)针对JVM的编程语言,并且具有开源编译器。有什么建议吗?
9个回答

68

我也建议使用ASM,但可以看看Jasmin。我在大学项目中使用它(或者说不得不使用它),效果非常好。我使用Java和Jasmin编写了一个词法分析器-解析器-分析器-优化器-生成器组合来编译编程语言,并生成了JVM代码。我将代码上传到这里,有趣的部分应该是源代码本身。在bytecode/InsanelyFastByteCodeCreator.java文件夹中,您可以找到一段代码,它将AST树转换为Jasmin汇编程序的输入格式。这非常简单。

源语言(由词法分析器-解析器-分析器转换为AST的语言)是Java的子集,称为MiniJava。它缺少一些“复杂”的特性,如继承、构造函数、静态方法、私有字段和方法。这些特性都不难实现,但还有另一个任务要编写x86后端(即生成机器汇编程序),如果没有处理一些这些功能的JVM,那么这些任务往往会变得困难。

如果你对奇怪的类名感到困惑:大学项目的任务是将AST转换为SSA图(表示输入代码),优化图,然后将其转换为Java字节码。这大约是该项目的3/4,而InsanlyFastByteCodeCreator只是一个测试所有东西的捷径。

看看Jon Meyer和Troy Downing的“Java虚拟机”书籍。这本书非常引用Jasmin汇编程序;它对理解JVM内部非常有帮助。


谢谢您的回答,我会看一下Jasmin。如果您能上传源代码让我查看,我将不胜感激。至于您推荐的书籍,它似乎很有趣,但已经绝版而且相当古老 :(. - functional
这本书二手非常便宜。我找到一本只要几美元。 - namin
请看一下我上面的编辑,如果您有任何问题,我很乐意帮助。 - theomega
1
“源代码本身”的链接已经失效。虽然我猜这在8年后是可以预料的。 - Llew Vallis
1
@LlewVallis,如果我理解所有信息正确的话,代码似乎在这里:github.com/replimoc/compiler - U880D

16

我上学期学习了一门“编译器构建”课程,我们的项目正是您想做的那个。

我使用的语言是Scala,它运行在JVM上,但支持许多Java不支持的高级特性(仍然与纯Java JVM完全兼容)。

为了输出Java字节码,我使用了Scala CAFEBABE库。文档良好,您不必深入了解Java类就可以理解该如何操作。

除了教材,我认为您可以通过参加我们在课程期间完成的实验来获取更多信息。


这听起来是一门很棒的课程。你介意分享你的笔记或代码吗? - Pedro
1
没问题,我会查找我的备份并在此发布链接,以便您可以尽快下载。 - Kami
1
太好了,我一直在寻找一个针对JVM的实践编译器课程,并且所有材料都可以在线自学。 - namin

5
上个周末,我正在思考将我的玩具语言移植到JVM的同样问题。
我只花了几个小时搜索信息,所以请谨慎参考这些参考资料。
  • 语言实现模式。我不喜欢antlr,但这本书看起来非常好。如果你也不喜欢antlr,那么有一本关于解析的非常好的书,名为“解析技术:实用指南”。

    学习构建配置文件读取器、数据读取器、模型驱动代码生成器、源到源翻译器、源分析器和解释器。您不需要计算机科学背景——ANTLR创始人Terence Parr通过将语言实现分解成最常见的设计模式来揭示其神秘面纱。逐个模式,您将学习实现自己的计算机语言所需的关键技能。

    第10章在30页内(对我来说太快了)涵盖了这些主题。但是还有其他章节可能会引起您的兴趣。

    • 10 构建字节码解释器
      • 10.1 编程字节码解释器……
      • 10.2 定义汇编语言语法
      • 10.3 字节码机器架构……
      • 10.4 接下来去哪里……
      • P.26. 字节码汇编器……
      • P.27. 基于堆栈的字节码解释器……
      • P.28. 基于寄存器的字节码解释器
      http://pragprog.com/titles/tpdsl/language-implementation-patterns
    • Lua 5.0的实现。这是一篇关于基于寄存器的字节码机器的好论文。即使只是为了看看,也要去读一下。

    • 小块的Lisp。这本书教你如何编写两个编译器,它们将编译成C的方案。这本书可以从中学到很多东西。我有一本这本书的副本,对于任何对lisp感兴趣的人来说都非常好,但可能不是你喜欢的。

      这是整个Lisp系列语言(即Lisp、Scheme和相关方言)的语义和实现的全面说明。它描述了11个解释器和2个编译器……

    http://www.amazon.com/Lisp-Small-Pieces-Christian-Queinnec/dp/0521562473

检查Dalvik7虚拟机,它是一种基于寄存器的虚拟机。DVM操作由Java编译器编译的Java类文件转换而来的字节码。

有一个关于这个主题的邮件列表,jvm-languages。

你计划将代码上传到任何地方吗?我想看一下。


你打算上传代码到哪里吗?我对那段代码并不感到自豪 :( ... 我可能会重写整个代码。无论如何,如果我这样做了,我会让你知道的。非常感谢你的建议。 - functional

5

ASM 可以用于生成字节码。要开始,请查看从手册中生成元素的主题。


4
我在考虑看一下可能针对JVM的另一种语言,比如Clojure、Jython或JRuby。但是这些语言都非常高级和复杂(为它们创建编译器)。建议您可以看一下Lua编程语言,有像LuaJ这样的JVM实现。 一个轻量级、快速、以Java为中心的Lua解释器,适用于J2ME和J2SE,具有基本、字符串、表格、包、数学、io、os、调试和协程包的库,还有编译器、luajava绑定和JSR-233可插拔脚本引擎绑定。 (不要与使用JNI方法的本机库的LuaJava混淆。)

2
我建议你先学习JVM汇编的工作原理,如果你还不了解的话。
许多指令的形式为?name,其中?i表示该指令适用于整数类型,a表示该指令适用于引用类型。
基本上,JVM是一台没有寄存器的堆栈机,所以所有指令都直接在堆栈上处理数据。你可以使用?push / ?pop来推入/弹出数据,并使用?store / ?load在本地变量之间移动数据(通过偏移引用的堆栈位置)和堆栈顶部。其他一些重要的指令是invoke???if_???
对于我们大学的编译器课程,我们使用Jasmin来汇编程序。我不知道这是否是最好的方法,但至少它是一个容易入手的地方。 这里有一个指令参考,针对旧版JVM,可能比新版更少一些指令。

2

当然,可以使用Java编写新语言。通过Java反射-API,您可以实现很多功能。如果速度不太重要,我会优先选择Java而不是ASM。在Java中编程更容易,出错的概率也更小(个人意见)。看看用Java完全编写的RPN语言7th


1

首先,我会暂且停下来,修改我的编译器,输出实际的Java代码而不是Java字节码(这意味着我需要创建更多的翻译程序而非编译器),然后使用方便的任何Java环境编译Java输出(这可能会比我自己的编译器生成更好的目标代码)。

您可以使用相同的技术(例如编译到C#)生成CLI字节码,或编译到Pascal以生成P-code等。

不清楚您为什么考虑Java代码而不使用您自己的VM,但如果是出于性能的原因,当然还应该考虑编译成实际的机器码。


编译为JVM将使代码可以比编译为本机代码更广泛地运行。此外,编译为字节码将使代码能够执行一些在Java语言本身中不可能实现的操作。 - supercat

0

现在,我建议使用Truffle作为完美的起点。一旦你有了AST,你可以使用Truffle的工具和Graal进行编译。自JDK9以来,Graal编译器可以直接从JDK本身使用。在我看来,Truffle的API是最友好的,通过利用Graal,你可以朝着Java本身的方向前进。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接