当有人编写一种新的编程语言时,他们把它写成什么?

我正在涉足 PHP,并且正在涉足浏览 SO,我感到有必要问一个多年来一直在思考的问题:

当您编写一种全新的编程语言时,您将如何编写它 进去?

这对我来说是鸡和蛋的问题。你是做什么的?对自己说 今天我要发明一种新的语言!,然后点火。记事本?所有的编译器都是建立在以前存在的语言之上的吗? 这样一来,我们就可以把所有设计出来的编程语言都绘制在一个庞大的分支树上,最终停留在... ... 我不知道,像 老?这样的地方

46696 次浏览

一般来说,你可以使用任何你喜欢的语言。例如,PHP 是用 C 编写的。如果你不能使用任何编译器,你将不得不使用汇编语言,手工编译成机器码。

最常见的答案是 C。大多数语言是用 C 语言实现的,或者混合使用回调和类似于 弯曲的“ lexer”以及类似于 YACC的编译器编译程式。这些语言用于一种目的——描述另一种语言的语法。有时,当涉及到编译语言时,它们首先是用 C 语言实现的。然后使用该语言的第一个版本创建一个新版本,以此类推。(比如 Haskell)

事实上,你可以用几乎任何你喜欢的语言写作。没有什么能阻止你用 Ruby 编写一个 C 编译器。“所有”您必须做的就是解析程序并发出相应的机器代码。如果您能够读/写文件,那么您的编程语言可能就足够了。

如果您在一个新的平台上从头开始,那么您可以进行交叉编译: 为您的新平台编写一个编译器,该编译器可以在 Java 或 x86上运行。在你的电脑上进行开发,然后将程序转移到你的新目标平台上。

最基本的编译器可能是汇编器和 C。

几乎任何一种语言,尽管使用一种适合于处理图形和其他复杂数据结构的语言会使许多事情变得更容易。出于性能方面的考虑,产品编译器通常是用 C 或 C + + 编写的,但是 OCaml、 SML、 Prolog 和 Lisp 等语言可以说更适合用于语言的原型开发。

在语言设计中也有几种“小语言”。例如,Lex 和 yacc 用于指定语法和语法,它们编译成 C (还有其他语言的端口,比如 ocamllex/ocamlyacc,以及许多其他类似的工具)

作为一个特例,新的 Lisp 方言通常是在现有的 Lisp 实现上构建的,因为它们可以搭载大多数相同的基础设施。在 Scheme 中编写 Scheme 解释器可以在一页代码下完成,此时可以轻松地添加新特性。

从根本上说,编译器只是一些程序,它们读入某些内容并将其转换为其他内容——将 LaTeX 源代码转换为 DVI,将 C 代码转换为汇编语言,然后转换为机器语言,将语法规范转换为用于解析器的 C 代码,等等。它的设计者指定了源格式的结构(解析)、这些结构的含义、如何简化数据(优化)以及生成的输出类型。译员读取源代码并直接执行。(口译员通常写起来更简单,但速度要慢得多。)

很多语言都是自举式的——这就是 写在自己身上。至于为什么你想这样做,它往往是一个好主意,以 吃你自己的狗粮

我引用的维基百科文章讨论了 鸡和蛋的问题。我想你会发现它非常有趣。

通常使用适合系统开发的通用编程语言,例如 C、 Haskell、 ML、 Lisp 等,但是选项列表很长。此外,通常还有一些特定于领域的语言用于语言实现,例如解析器和词法分析器生成器,中间语言如 LLVM等。可能还有一些 shell 脚本、测试框架和构建配置系统,例如 autoconf。

“编写一种新的编程语言”在技术上不涉及任何代码。它只是为你的语言的外观和工作方式提供了一个规范。一旦你对自己的语言有了一个概念,你就可以编写翻译和口译来使你的语言真正“工作”起来。

译者用一种语言输入一个程序,然后用另一种语言输出一个等效的程序。解释器用某种语言输入程序并运行它。

例如,C 编译器通常将 C 源代码(输入语言)转换为汇编语言程序(输出语言)。然后汇编程序采用汇编语言程序生成机器语言。一旦有了输出,就不需要翻译器来运行程序了。由于现在有了一个机器语言程序,CPU 就充当了解释器的角色。

许多语言的实现方式不同。例如,javac是一个将 Java 源代码转换为 JVM 字节码的转换器。JVM 是运行 Java 字节码的解释器[1]。在运行 javac并获得字节码之后,就不再需要 javac了。但是,无论何时想要运行程序,都需要 JVM。

事实上,不需要保留翻译器来运行一个程序,这使得“引导”您的语言成为可能,而不必让它最终运行在其他语言的“顶层”。

[1]大多数 JVM 在幕后进行翻译,但它们不是真正的翻译者,因为 JVM 的接口不是“输入语言-> 输出语言”。

这不是个愚蠢的问题,这是个很好的问题。

正如已经回答的那样,简短的回答是: “另一种语言。”

这就引出了一些有趣的问题 你那件特别的硬件?对于从事嵌入式设备工作的人来说,这是一个非常现实的问题。正如已经回答的“另一台计算机上的语言”。事实上,一些嵌入式设备永远不会得到一个编译器,他们的程序将总是在不同的计算机上编译。

但是你可以把它推得更远,那么第一个编写的程序呢?

第一个“高级语言”的编译器应该是用所谓的“汇编语言”编写的。汇编语言是这样一种语言,其中每条指令对应于 CPU 的一条指令。它非常低级的语言和非常冗长和非常劳动密集型的写作。

但即使是编写汇编语言也需要一个称为汇编程序的程序来将汇编语言转换成“机器语言”。我们可以追溯到更久以前。最早的汇编程序是用“机器代码”编写的。一个完全由二进制数组成的程序,它是计算机本身的原始语言的直接双射。

但还是没有结束。即使只有原始数字的文件 还是也需要翻译。你还是需要把这些原始数字输入电脑。

信不信由你,早期的电脑前面有一排开关。你打开开关,直到它们代表一个二进制数,然后你再打开另一个开关,把那个数字加载到计算机内存中。然后,你继续弹击开关,直到你已经加载了一个最小的计算机程序,可以读取程序从磁盘文件或穿孔卡片。你按下另一个开关,程序就开始运行了。上世纪80年代我上大学的时候,我看到过有这种能力的计算机,但是它们从来没有被赋予过在程序中加载开关的任务。

甚至更早的时候,计算机程序必须与 插头板硬连接!

许多语言首先是用另一种可用的语言编写的,然后重新实现自身并以那种方式引导(或者只是保持在外语中实现,比如 PHP 和 perl) ,但是有些语言,比如第一个汇编程序是手工编译成机器码的,比如第一个 C 编译器是手工编译成汇编程序的。

自从我读到它,我就对自力更生很感兴趣。为了了解更多,我试着自己编写我自己的 BF 超集,我称之为 EBF。EBF 的第一个版本有3个额外的原语,我手工编译了第一个二进制文件。当我这样做的时候,我发现了一个两步的节奏。我在一个版本中用当前语言实现了一个特性,并在一个很棒的版本中重写了代码以利用实现的特性。这种语言具有足够的表现力,可以用来制作 LISP 解释器

我有手工编译的版本连同源码在 第一次释放标签和代码是相当小的。最后一个版本在大小和代码上增加了12倍,并且允许更紧凑的代码,因此手工编译当前版本将很难做到正确。

埃德蒙 · 格里姆利 · 埃文斯用 他的十六进制语言做了类似的事情

自己做这件事情的有趣之处在于,你会明白为什么有些事情是这样的。我的代码是产品,如果小的增量调整,它看起来更像是进化而不是从头开始设计。我在今天读代码的时候会记住这一点,我认为这看起来有点不对劲。

大多数编译器都是 C 或类似 c 的程序,如果不是 c,那么汇编 lang 就是最好的选择。然而,当你从头开始编写一个新的 lang 时,如果你没有一个宏库或者来自原型语言的源代码,你就必须用什么语言来定义你自己的函数?您只需要向机器编写一个“ Form”,其源代码称为伪代码,它看起来像是面向对象的结构化 lang 规范(如 Fortran 基本算法 lisp)中的 bnf 语法。所以图像写一个十字代码,类似这些语言的语法,这是伪代码

甚至进一步的二进制,或者汇编操作必须转换成函数,即汇编器/编译器的工作,然后从数据和函数转换成对象,如果你没有一个源文件来查看这些对象的功能应该如何在你的语言实现中表示,那么你必须识别“查看”实现,或者定义你自己的函数,过程和数据结构,这需要很多知识,你需要问自己什么是函数。然后你的大脑就变成了语言模拟。这使得大师级程序员与其他程序员分离开来。

几个月前我也有这个问题。我读了一些文章,看了一些视频,帮助我开始写我自己的语言称为软。它还没有完成,但我从这次旅行中学到了很多东西。

您应该知道的基本事情是编译器在必须执行代码段时是如何工作的。编译器有很多阶段,比如词法分析、语义分析器、 AST (抽象语法树)等等。

我在我的新语言中所做的可以在这里找到 -http://www.singhajit.com/writing-a-new-programming-language/

如果你是第一次写一门语言,那么祝你一切顺利,你还有很长的路要走。

通常什么是编程语言?

编程语言只是与计算机交谈的一种方式。大致说来,一开始是因为计算机只能理解0和1(由于计算机是由晶体管构成的开关,只能采用两种状态,我们称这两种状态为0和1) ,而且对于我们人类来说,使用0和1是很困难的,所以计算机科学家决定做一个一对一的映射,从二进制(0,1)中的每一条指令到一种更加人类可读的形式,他们称之为汇编语言。

例如,如果我们有这样的指令:

11001101

在装配过程中,它被称为:

荷载 _ A15

这意味着将寄存器 a 的内容加载到内存位置15。正如我所说,这只是一个惯例,就像选择0和1两个状态的晶体管或任何其他在计算机上。这样,有一个程序与50个指令,记住汇编语言将更容易。因此,用户可以编写汇编代码,一些程序(在这种情况下是汇编程序)可以将代码翻译成二进制指令或机器语言。

但是随着计算机每天都在改进,有更多的空间用于更复杂的程序和更多的指令,比如说10000。

在这种情况下,像汇编这样的一对一映射不起作用,因此创建了其他高级编程语言。他们举例说,如果一个与 I/O 设备的关系用来在屏幕上打印用户创建的东西需要大约80个指令,让我们在这里做一些事情,我们可以把所有这些代码打包成一个库,并调用它,例如 printf,还可以创建另一个程序,将这个 printf 转换成相关的汇编代码,从那里汇编完成剩下的工作。所以他们称之为编译器。

所以现在每个想要在屏幕上打印东西的用户都不需要用二进制或汇编写所有的指令,他只需要输入 printf (“ something”) ,所有的程序,比如编译器和汇编器,就可以完成剩下的工作。现在,其他更长的代码也会以同样的方式打包,以方便其他人的工作,正如你看到的那样,你可以简化一千行代码到 Python 中的一个代码中,然后打包供其他人使用。

假设您在 python 中打包了许多不同的代码,并创建了一个模块(库、包或者任何您想要调用它的东西) ,然后您调用了这个模块 mgh (只是我的名字)。现在让我们假设我们已经创造了这个 mgh,任何人说:

import mgh
mgh.connect(ip,port.data)...

可以轻松地连接到指定 ip 和端口号的远程服务器,然后发送数据(或类似的东西)。现在人们可以用一行代码来完成所有的工作,但是这样做的结果是执行了大量的代码,这些代码都是从 mgh 文件中检索出来的。并打包它不是为了加快执行过程,而是为了方便其他程序员的工作。所以在这里,如果有人想要首先使用你的代码,他应该导入文件,然后 python 解释器会识别其中的所有代码,这样它就可以解释代码。

现在,如果你想创建一种编程语言并执行它,首先它需要一个翻译,例如,你创建一个程序,它可以理解语法并将其转换为 c,在这种情况下,在它被转换为 c 之后,其余的将由 c 编译器,然后汇编器,链接器,... 来处理。 即使你必须付出慢的代价,因为它必须首先转换成 c。

现在你可以做的另一件事就是创建一个程序把所有的代码转换成等价的汇编语言就像 c 语言一样但是在这种情况下,程序可以直接完成剩下的就由链接器完成了。我们知道这个程序叫做编译器。

所以我说的是,系统唯一能理解的代码是0,1,所以你应该设法把你的语法转换成0,1,现在在我们的操作系统中有很多不同的程序,像汇编程序,链接程序和... 已经被创建来告诉你,如果你能把你的代码转换成汇编程序,他们就能处理剩下的代码,或者像我说的,你甚至可以使用其他编程语言编译器,通过把你的代码转换成那种语言。