啥,我也可以创造一门新的编程语言
啥,我也可以创造一门新的编程语言?!
一、演化
是的,因为混沌之初是没有任何编程语言的,所有已有的编程语言如Java\C#\PHP\C\C++,都是前人创造出来的,明白这一点,我们就可以得出标题,我们可以创造属于自己的编程语言,那我们如何实现呢?
我们从编程语言的由来,慢慢梳理出怎么去创造。
首先,我们可以知道,最初,由0,1来界定有无电流,再用与、或、非门来控制哪些电路能有电流通过,然后通过不同的电路实现实现不同的功能(如一条电路实现灯的开关,一条电路实现门的开关),从而实现用0,1=》控制有无电流=》某条电路是否有电来运行,当一个系统非常庞大,有很多很多的电路时,就出现了字节,用一串数字,每一位的数字都代表了某一条电路是否有电(这叫标志位),字节的长度,含义是由专门的协会根据使用场景和现实能力来定义的,比如:
1.往往字节的第一位为符号位,是因为显示中有正负之分,同时也因为我们用二进制来控制的电路,所以也出现了一些‘bug’,如正0和负0都表示0,按整数来说只有负数、正数、0。
2.基本类型所占字节数,即用来表示电流有无的位数,是固定的,如果出现一个很大/很小的数,现有的编程语言都无法直接表示出来,只能通过字符串等变通手段,这也是源于早期制定的规则,从而后面的编程语言都是在这些规则下制定的,所以越高级的语言往往这种弊端就越多(就如屎山也是一步步形成的)
所以早期计算机先辈们局限的选择和制定出必然有缺陷的规则,留下了一些弊端,从这也可以明白世界真的是一个巨大的草台班子,没有绝对完美的规则和完美的代码,没有崩,只是因为没有到规则的极限。
到此,我们了解了字节和一些底层原理,然后怎么出现编程语言的呢?因为随着硬件的发展,可以做出的系统,也变得越来越大,这个时候是不可能再用简单的业务逻辑能实现的了,简单的业务逻辑可以通过指定人去固定的操作,当人足够多,配合足够默契时,可以完成(如三体写的三体世界通过人来做逻辑电路一样),但是这样不能满足时代的发展(超高运算、超复杂业务逻辑),加上只要一个机器有电,则它下层机器就可以根据传输的有无电流判断,来执行操作,根据这个原理,可以用机器控制机器,因而出现机器码来到了汇编语言,又因为汇编语言的记忆复杂,诞生了低级语言(如C),但低级语言是面向过程的控制逻辑,而世界是由一个个对象组成的,因而诞生了高级语言,用于更好的定义现实世界的事物(语言越往低处走,越接近机器,机器效率越高,越往高处走,越接近现实世界,机器执行效率越低,但越接近人类语言)。
为什么没有讲其中的语法、语义、词法等,因为他们只是在现实需求不断变大,制定规则的人让编程语言更加规范,便于人和机器更好理解业务逻辑的产物,不是必须的,如果你想创建一套你自己看的懂的语言,也可以完全按照你自己定义的语法、语义、词法去写,甚至,你可以不规定这些,直接用机器码去写。
好,到了这里我们已经理清楚了编程语言的演变,现在开始说,怎么去创造一门新的语言。
第一,想要创造一门新的语言(高级语言),必然是要借助其他语言的来创造的,这里用C举例,我如果要创造一门语言x,首先明白:一种语言最初用另一种语言编写,然后可以用自己编写自己的原因在于编程语言的编写和发展过程。(这个过程叫自举)
第二,如何自举的?
从C编写了X语言,我们就可以用X语言去写功能代码了,只是写好的功能代码要先经过编译成C这个目标代码,再由C编程成机器码,再由机器识别。
创建一门新的编程语言并使其能够自举是一个相当复杂的过程,需要仔细考虑多个方面。下面是一些开发者可能会考虑的关键方面:
选择合适的基础语言 :最初,新语言的编译器可能会使用现有的成熟编程语言(如 C、C++、Python 等)编写。选择一个稳定、功能丰富且性能良好的基础语言是至关重要的。
设计语言的语法和语义:开发者需要仔细设计新语言的语法和语义规范,确保它易于理解、使用和学习。这包括定义关键字、操作符、数据类型、语句结构等。
编写编译器和工具链:开发者需要编写新语言的编译器和相关工具,包括词法分析器、语法分析器、语义分析器、代码生成器等。这些工具负责将源代码翻译成目标代码或者解释执行。
选择合适的编译器技术 :根据语言的特点和目标平台,开发者可能选择不同的编译器技术,如解释执行、静态编译、即时编译等。
实现语言的核心库和运行时环境:除了编译器和工具链,还需要实现新语言的核心库和运行时环境,包括标准库、内置函数、垃圾回收器等。
测试和调试:开发者需要对编译器和工具进行充分的测试和调试,确保其功能正确、稳定性好、性能优秀。
逐步改进和优化 :一旦初始版本完成,开发者需要不断收集用户反馈,改进语言的设计和实现,并进行性能优化和功能增强。
在考虑如何实现自举时,开发者通常会使用以下策略:
使用部分自举:在开始阶段,可以使用其他更成熟的语言来编写初始版本的编译器和工具链。然后,逐步改进和重写这些工具,直到它们能够使用新语言编写自己。
引入交叉编译器:开发者可以先实现一个能够将新语言编译成目标语言的交叉编译器,然后使用交叉编译器来编译新语言的编译器和工具链。
利用现有工具和库:在开发自举过程中,可以利用现有的工具和库来简化开发过程,如使用 Lex 和 Yacc 来生成词法分析器和语法分析器。
总的来说,实现自举是一个复杂而挑战性的任务,需要开发者具备深厚的编程语言和编译器知识,并且需要耐心和持续的努力。
二、具体实现
哈哈,上面的看着很头大吧?实际代码理解就很简单了,我们这里使用Java来实现:
再上一点点概念:
自举”(bootstrapping),它是一种通过使用已有的工具来构建新的工具的方法。在编程语言的背景下,自举是指使用交叉编译器(一个编译器,它能够将源代码编译成另一种不同于本身的目标语言的编译器)来构建一个新语言的编译器。
详细一点说就是:
初始阶段:使用 C 编写 X 语言的编译器
- 在初始阶段,X 语言的编译器是使用 C 语言编写的。这个编译器负责将 X 语言的源代码转换为等价的 C 语言代码。
过渡阶段:使用 X 语言编写 X 语言的编译器
- 通过使用初始阶段生成的 C 语言代码,开发者可以使用 X 语言本身来编写 X 语言的编译器。这意味着开发者不再依赖于 C 语言,而是使用 X 语言的语法和功能来构建编译器。
- 这个过渡阶段将逐步消除对 C 语言的依赖,使 X 语言的编译器完全由 X 语言本身构建。
最终阶段:直接编译为底层的机器码
- 一旦 X 语言的编译器完全由 X 语言本身构建,并且达到了稳定和成熟的状态,开发者可以直接将 X 语言的源代码编译为底层的机器码,而不再需要中间步骤转换为 C 语言。
- 这意味着 X 语言的编译器将直接将 X 语言的源代码转换为目标平台的机器码,而不需要借助其他语言。
总的来说,这个过程是一种渐进的自举过程,它逐步消除了对其他语言的依赖,使 X 语言能够自举并且直接生成底层的机器码。这种方法可以使 X 语言更加独立和灵活,提高其性能和效率。
代码演示(利用Java的接口、实现来具象化):
// 定义编译器接口 interface LanguageCompiler { void compile(String sourceCode); } // 实现编译器接口的 C 编译器类 class CCompiler implements LanguageCompiler { @Override public void compile(String sourceCode) { // 将源代码编译为 C 语言代码的实现 System.out.println("Compiling source code to C code..."); } } // X 语言编译器类,使用 C 编译器来编译自己的源代码 class XCompiler implements LanguageCompiler { private LanguageCompiler cCompiler; // 使用构造函数注入 C 编译器对象 public XCompiler(LanguageCompiler cCompiler) { this.cCompiler = cCompiler; } @Override public void compile(String sourceCode) { // 使用 C 编译器编译 X 语言源代码 cCompiler.compile(sourceCode); // X 编译器的其他编译逻辑 System.out.println("Compiling source code to X code..."); } } public class Main { public static void main(String[] args) { // 创建 C 编译器对象 LanguageCompiler cCompiler = new CCompiler(); // 使用 C 编译器来编译 X 语言的编译器 LanguageCompiler xCompiler = new XCompiler(cCompiler); // 编译 X 语言的编译器源代码 xCompiler.compile("X language source code"); } }
CCompiler
类实现了LanguageCompiler
接口,用于编译某种编程语言的源代码为 C 语言代码。然后,XCompiler
类也实现了LanguageCompiler
接口,它在编译 X 语言源代码时,利用了CCompiler
对象来编译中间代码,然后执行自己的编译逻辑。这里模范的是前期需要转成 C 来实现 X 语言写的功能逻辑。
下面写:
为了模拟这个过程,我们将确保在 X 语言编译器的实现中不再使用 C 编译器,而是直接使用 X 语言本身来完成编译器的功能。这样,X 语言编译器就可以完全独立于 C 语言。
Java 代码示例:
// 定义编译器接口 interface LanguageCompiler { void compile(String sourceCode); } // X 语言编译器类,直接使用 X 语言来实现编译器功能 class XCompiler implements LanguageCompiler { @Override public void compile(String sourceCode) { // X 语言编译器的编译逻辑,不再依赖于 C 编译器 System.out.println("Compiling source code to X code..."); } } public class Main { public static void main(String[] args) { // 创建 X 语言编译器对象 LanguageCompiler xCompiler = new XCompiler(); // 编译 X 语言的源代码 xCompiler.compile("X language source code"); } }
在这个示例中,
XCompiler
类实现了LanguageCompiler
接口,并且直接使用 X 语言本身来完成编译器的功能,而不再依赖于 C 编译器或者其他外部工具。这样,X 语言编译器就完全独立于 C 语言,实现了自举的过渡阶段。
到此自举完成,也就完成了完全自己的编程语言,脱离 C 之后的代码,具体的语义、词法涉及的更细节,点赞、收藏过 1000,手撕一份自己的编程语言源码出来!