在这个上下文中,“编译”一词指的是一种通常被认为是直接翻译成本地机器语言或格式的语言。典型的情况包括C和C++。但是,请注意,这两种语言也可以被解释执行。C解释器的一个例子是Pico C,它处理C的子集。
因此,真正的问题是关于环境的。一种语言可以在编译或解释环境中运行。通过以下测试可以清楚地区分这两种情况:
Does the language possess a "command level" mode
in which forward references are inherently impossible?
请花一点时间考虑这个问题。解释性语言是实时读取其规范的语言。前向引用是指在规范制定时不存在的东西。由于机器还没有(尚未)具备预知或时间旅行(即“时间循环逻辑”)的能力,因此这些引用本质上是无法解决的。
如果将这样的级别定义为语言的强制部分,则可以说该语言是解释性的;否则,可以说它是编译型的。BASIC是解释性的,因为它的某些命令直接引用了这个层次(例如“list”命令)。同样,高级AI语言Prolog也是一个解释性语言,因为它也拥有直接引用这个层次的命令。例如,“?-”命令本身就是一个实际的提示;但它的数据库命令也涉及并维护命令级别层的当前状态。
然而,这并不排除解释性语言的某些部分受到编译或编译器使用的方法的影响,或者编译型语言在命令模式下运行。实际上,以C或C++等语言为例,调试器就是这样的。
大多数定义了命令级别层的语言,通常需要编译成某种形式。特别是,如果该语言满足以下条件,则几乎强制性地至少部分编译成某种形式:
Does the language possess a facility for user-defined codelets,
for instance: subroutines, functions, lambdas, etc.?
原因很简单:在定义代码之前使用它,你要把它放在哪里,以及以什么格式?直接保存和运行是极其低效的,因此通常会将其转换为另一种形式,即:(a) 语言内部的标准形式(在这种情况下,语言的其余部分可以被视为标准形式所在的减少子集语言的“语法糖”),(b) 转换成语言外部的标准形式(即“字节码”),或者(c) 两者的组合 - 它可能首先进行语言内部规范化,然后再将其转换为字节码。
因此,大多数“解释型”语言都会被编译成其他形式。唯一真正的问题是:(1) 它们被编译成什么,以及(2) 编译后的代码何时/如何运行 - 这与上述“命令级”模式的问题有关。
如果代码块被编译成与目标无关的形式 - 这通常是所谓的“字节码”时,它并不是以通常所说的意义上的“编译”方式进行的。通常情况下,“编译”一词指的是将语言翻译成运行该语言的机器本地语言。在这种情况下,将会有与语言可能运行的机器类型一样多的翻译器 - 翻译器本质上是依赖于机器的。
这两种情况并不是互斥的。因此,字节码翻译器可以出现为本地代码编译的阶段,通过这个阶段,解释性语言的代码块被直接翻译并存储到运行该语言的机器的本地语言中。这就是所谓的“即时编译”(或JIT)。
区分有点模糊。即使是编译型语言,如C或C ++,也可能在具有编译或甚至预编译的代码块的系统上运行,这些代码块在程序运行时加载。
我对JS还不够了解,无法做出任何明确的陈述 - 除了从观察中可以推断出的内容。
首先,由于JS代码存储为代码块,并且通常在需要使用时在Web客户端上运行,因此很可能会将代码块编译(或预编译)成中间字节码形式。
其次,出于安全考虑,不太可能直接编译成正在运行的机器的本地代码,因为这可能通过提供漏洞来泄露恶意代码。这就是浏览器应该遵守的“沙盒”功能。
第三,JavaScript通常不像Basic或者Prolog那样被直接用作语言。然而,在许多(甚至大多数)实现中,它确实有一个“调试”模式。例如,浏览器可能允许普通用户查看和编辑/调试JS代码。尽管如此,除了出现在Web浏览器本身中的命令层之外,实际上并没有命令层。这里未解决的问题是浏览器是否允许JS代码中的前向引用。如果是这样,那么它实际上并不是一个命令级别的环境。但这可能取决于浏览器。例如,它可能在启动任何JS代码之前加载整个网页,而不是在页面加载时尝试实时运行JS,这样前向引用就是可能的。
第四,如果该语言想要在执行速度方面高效,它将具有某种形式的JIT - 但这将需要对JS编译器本身进行严格验证,以确保没有任何东西可以通过JIT从“沙箱”溜出到主机机器上的禁止代码中。
我相信有JS编辑器/解释器存在,只是为了有一种开发JS的方式。但我不知道命令层的任何参考是否是JS规范的强制部分。如果存在这样的规范,那么我们可以称它为真正的解释语言。否则,它就像一种旨在实时运行的解释语言,但允许直接编译成正在运行的机器的本地代码,因此跨越了两种语言类型之间的边界。
对我来说,这个问题最近变得尖锐,当我试图将一个旧的基于文本的游戏(月球着陆器)直接从(解释的)FOCAL语言翻译成C-BC(一种类似于POSIX BC的扩展,其源代码位于GitHub上,链接在这里https://github.com/RockBrentwood/CBC)。C-BC和POSIX BC一样是解释性的,但允许用户定义代码块,因此BC的实现通常定义一个“字节码”语言(历史上:这是“dc”)。
FOCAL语言有一个运行时语言 - 理论上可以编译,但也有一个命令层子集(例如“库”或“擦除”命令),它不允许尚未定义的前向引用,尽管运行时语言允许前向引用。
与GNU-BC不同,C-BC具有goto语句和标签,因此可以直接翻译游戏。然而,在命令级别(在BC文件中是文件范围的顶级),这是不可能的,因为文件代码的顶级 - 就BC解释器而言 - 引用可能尚不存在的东西,因为程序也可能正在实时输入用户。相反,整个源代码必须被包含在{...}括号中 - 它首先被编译成字节码,然后在执行之前被完全编译。因此,这是一个用户定义的代码块的示例,也是大多数解释语言必须具有一些编译功能的教科书式例子。