Press "Enter" to skip to content

如何构建一种编程语言:通往成功的(艰难)道路

坦率地说,困难远不止如此。

我之前关于我的编程语言的文章概述了它的语法,并提供了我构建它的总体思路。

但是我决定写另一篇专门讲述我走向最终结果的旅程的文章,因为说实话,这个旅程肯定有更多的低谷而不是高峰,而我必须克服的挑战真的令人畏惧。希望这篇文章也能成为那些进行类似项目并面临类似困难的人的动力,让他们不要放弃,相反要知道有人曾经处于与他们相同的位置,并最终实现了他们的目标。

但是为什么你应该关心呢?

毕竟,你们所有人的背景都不尽相同,无论是数据科学,还是老式的Python和Java。

嗯,即使你对构建自己的编程语言不感兴趣,但在这个经验中提升的许多软技能肯定与你们所有人相关(并且希望能起到激励作用),特别是如果你对编程感兴趣的话。

你写程序的时候有多少次,每一行看起来都正确,但你却得到了一个看起来几乎不可能调试的神秘错误,这时你开始失去动力,想要放弃。

Photo by Tim Gouw on Unsplash

嗯,构建这个语言时,我太多次面对这些司空见惯的问题,而我最终是如何克服它们并坚持下来的方式,希望在你阅读这篇文章的过程中传达出一个令人难忘的信息!

入门

这个初始想法是在我上八年级(或九年级,记不清了)的时候出现的,但当时我几乎不理解条件语句和迭代循环,所以我把它忘了。

但多年后,同样的想法在我的脑海中重新燃起,只是我不知道该如何开始。

Photo by Towfiqu barbhuiya on Unsplash

于是,像我不知道该怎么做的其他事情一样,我上网搜索了

“如何制作自己的编程语言”。

嗯,结果相当有希望。我花了很长时间逐行阅读每一篇文章,并掌握了其中的要点:

有两种类型的语言:解释型和编译型。解释型语言建设起来更简单,但速度较慢。另一方面,编译型语言将代码转换为机器代码,然后执行它(编译型语言通常更快)。

构建语言的一般过程是:

  • 定义语言的目的和语法:对我来说很容易(我知道我想构建一个与数学相关的语言),为了减少工作量并使语言尽可能高级,我决定只保留几个(基本和有用的)函数和两个变量,一个函数和一个浮点数。
  • 构建词法分析器和语法分析器:词法分析器是任何语言的第一个组件,它将代码中的每个单词/短语分为不同的类别(如关键字、运算符、注释等)。但单独来看,这些词素是没有意义的,为了理解它们,需要构建一个语法分析器。语法分析器确保词素遵循语言的语法,并将它们放入抽象语法树(AST)中。
  • 执行代码:AST 中的每个节点表示我们原始词法分析器收集到的一个令牌,其中还包括函数名。因此,运行我们的 AST 允许我们逐行执行写入的代码;这是解释型编程语言的一般基础。

但编译器需要一些额外的步骤;你需要使用像LLVM和libgccjit这样的流行库,将代码转换为编译后的可执行文件。

编译还是解释?

一方面,我可以创建一个解释性语言,这是相当具有挑战性的,需要我投入大量的时间和精力。另一方面,我可以创建一个编译语言,这更具有挑战性,并且需要我投入更多的时间和精力。

但是建立自己的编译语言的想法似乎太吸引人了,我陷入了困境。

建议:不要这样做。

虽然有关使用LLVM构建编译器的资源可用(强烈推荐观看有关LLVM的100秒视频,既有趣又有教育意义),但由于其复杂性,我很快在他们的文档中迷失了方向。

我敢打赌,你可能比学习使用LLVM构建编译器的基础知识更快地学习Java的基础知识。

Emile Perron在Unsplash上的照片

浪费了一大堆时间(还是没有)?

不幸的是,我花了两个多月的时间试图应付LLVM和libgccjit。后者表现得更有前途,我甚至为编译器编写了代码,然而,我面临了大量与设置libgccjit包相关的问题;我甚至从GitHub上下载了整个源代码,并将其放在与我的编译器代码相同的目录中。但是,仍然没有任何作用。我在Reddit和StackOverflow的无数线程中浏览,但毫无结果。

当StackOverflow无法帮助你时,你就知道出了大问题。

快要放弃了

这时候,我真的陷入了困境,因为我已经很久没有取得任何进展了,我开始贬低我所做的所有工作,比如从头开始构建词法分析器和语法分析器,而不使用流行的lex/yacc和flex/Bison工具。这时候,我问自己:“我应该把这一切都丢进(虚拟的)垃圾桶吗?”我很想说是的,但是我不会轻易放弃,尤其是涉及到编码相关的项目。

于是我出去散了散步,清理了一下思绪,然后回到了起点,这次的目标是构建一个解释性语言。

最终完成它。

一旦我决定构建一个解释器,我还做出了一个困难的决定,放弃C语言,回归Python。在正常情况下,最好使用低级语言来构建解释性语言,因为它们的速度可以弥补解释器的缓慢。

但我当时处于绝望的时刻,需要不择手段。

回到印度后,我度过了接下来的一周没有睡眠,每天工作24小时(如果不算早餐、午餐和晚餐的话,大约21小时,我吃得很慢)。

接下来发生的事情是我第一次经历的,也可能永远不会再次发生——我的代码在第一次尝试时就成功了!没有调试,没有更多的困惑。

当我运行我的源代码时,每个组件:从词法分析器到语法分析器和抽象语法树都正常工作,并正确执行每一行代码。

the blowup在Unsplash上的照片

反思和最后一点

上面的引语听起来可能很陈词滥调,但这并不减弱它的有效性;在构建AdvAnalysis时,我面临的失败远多于成功,但最终我站在了顶峰。

虽然你可以了解构建自己的编程语言时需要注意的事项(例如编译与解释,遵循的流程),但我得到的主要启示(并希望你也有)是,无论在你的程序中遇到多么令人沮丧的加密错误,最终它总是按照你的期望工作,只要你有耐心并返回原点,采用不同的方法。

说实话,作为一位编程爱好者,你可能已经知道这个了。:)

Leave a Reply

Your email address will not be published. Required fields are marked *