假设我编写了一种编程语言;对于同名,我会称之为 lang 。
为了开始写 lang 的漫长旅程,我决定开始写自己的语言。我实际上无法运行它,因为没有什么可以运行自己运行的程序。
所以我首先在Java中为 lang 编写另一个编译器。这一次,当我完成后,我决定将其转换为字节码,然后将其保留。我现在有一个有效的编译器,它将我的所有 lang 代码转换为字节码。
所以我决定将我的自编译器插入到我刚用Java编写的编译器中。然后我将自编译器转换为Bytecode,并丢弃Java编译器。我现在有一个 lang 编译器,纯粹编写,转换为字节码,随时可以使用。
这创建了一个可靠的程序,我理解所有这些,但我的问题是,相对于JVM的编译器设计,如果我决定发布我的语言更新怎么办?我如何更新字节码?我是否只是在旧版本中重新编写该语言的更新版本?
我问这个,因为这就是我想要做的。编写一个不存在的语言,然后首先使用Java创建一个编译器,将其引导到JVM。
它与使用C ++完成的操作相同。写了C with Classes,然后编写了C ++,最后C with Classes被放弃了,用于引导的C ++。但那么他们究竟是如何更新语言的呢?
答案 0 :(得分:3)
我将从您开发中的两种可能情况中回答这个问题。使用任何字节码语言,您都可以随时更新虚拟机或语言。
假设您首先要更新语言以获得新语法或更改当前语义。然后,您将当前编译的编译器保存在 lang (编译器 A )中,并编辑其源代码,以便正确编译新功能。然后使用旧的编译器编译编译器 B 。如有必要,您现在可以重写编译器以使用新功能,然后使用编译器 B 编译它以为您提供编译器 C 。
如果JVM发生变化怎么办?那么在这种情况下你保留一个旧版本的JVM,调整你的编译器以应对新的字节码更改,然后用旧的编译它(这类似于之前的编译器 B ) 。这将为您提供一个编译器,编译为新的字节码,但在旧的VM上运行。下一步是让它自己编译,现在你有了一个在新VM上运行的新编译器(类似于编译器 C )。
答案 1 :(得分:1)
我认为你的编译器是最好的解决方法。
我从语言的语法开始。
接下来是lexer / parser将我语言中的表达式转换为抽象语法树(AST)。 AST是表达式的正确中间表示。
您可以通过编写遍历AST的代码生成器为您选择的虚拟机或处理器发出字节码或汇编语言指令。
您的更新发生在哪里?
如果是语言基础,你必须修改语法和字节码发射。
如果您要优化字节码或移植到新处理器,则必须修改代码生成器。
答案 2 :(得分:1)
第一个 lang 编译器可以用 lang 的子集编写。而且您只需要一个子集(bootstrap)编译器(甚至是interoreter)。这可以用java编写。
稍后,可以用 lang 编写更广泛的编译器。较新的版本也可以。
你甚至可以编写一个将lang程序转换为java的翻译器,并使用它在lang中创建第一个翻译器,然后将其转换为字节码编译器。
答案 3 :(得分:-1)
新版本的编译器只能由前一版本编译。例如,要编译G ++ 4.6,您应该使用G ++ 4.5,而不是G ++ 1.0。
BTW没有必要为 lang 编写编译器。它即将证明一种语言是“成熟的”,但有些语言不需要证明这一点。