![cd44461f1c060226a878284ab25d85e4.png](https://i-blog.csdnimg.cn/blog_migrate/333eda33a1cccdbcddd7e614dd40ce4b.jpeg)
首先说明,本人之前并无任何编译原理的理论基础,以后也不打算在这方面进行深入研究,所以,解释器的开发过程中的各个特性的实现,完全靠本人发挥想象力 =_= (当然也查了一些资料,并不是完全凭空造的)
这个解释器是学习、实验性质的,它的名称叫做 Toy —— 对,你没有看错,它真的叫 Toy。项目地址部署在 github 上,请 点击这里 查看源代码和文档。Toy 脚本语言的中英文文档都在这里,里面有详细的讲解。
前言:为什么要写这篇文章?
主要是因为我觉得自己好像利用闲暇时间做了一个不小的工程(^^),所以有必要记录一下自己的在这个项目中的想法——至少记录一下自己到底做了什么。毕竟这只是一个业余时间里做着玩的小项目,难登大雅之堂,也很难有广泛的应用价值。不过项目虽小,却也投入了我不少精力,如果不好好记录一下自己在这个小项目中所收获的东西,那么一段时间以后如果不碰了,时间会把记忆中的一切细节都抹去,时间和精力也就都白费了。
一、为什么要写一个脚本语言解释器?
自己在学习和实践中陆陆续续实现了不少算法,比如动态数组、数值线性代数的各个算法(以及如何调用 LAPACK / BLAS 程序)等等。文件夹中所积攒的这些小程序,在很多时候都非常有用,不过放在一起就显得很凌乱不成体系。有一天,看着自己文件夹中这些后缀名为.c
的小文件们,我就在想,如果能够做一套命令集去调用它们,并且以后自己在学习工作中如果实现了什么新算法,就把它封装成新的命令添加到自己的命令集中去,岂不快哉?这就是我萌生要写一个解释器的想法的最初原因。
那为什么一定是“解释器”而不是“编译器”呢?因为很简单,我对编译原理的各种高深、优秀的理论毫无兴趣。我只是想做一个可以和计算机进行交互的实用小工具而已,这就是 Toy 项目的初衷。
然后我就开始设计类似于 Linux shell 的命令集。并且花了一天的时间把我自己积攒的所有算法都封装在了一个叫 toy 的命令下面。通过给 toy 传入不同的参数,我就可以调用不同的算法。
做这个其实挺简单的,我只花了一天就做好了。不过做完之后才发觉,这玩意用起来不太方便。比如我想对一个矩阵进行 Cholesky 分解,那我需要现在一个文本文件中把矩阵写好,然后调用诸如
toy -cholesky -f xxxx
那如果是要把分解之后的结果带入到矩阵乘法运算中去呢?难道我还要设计管道?这个时候我意识到,更方便的做法是开发一个类似编程语言一样的东西,把我的这些小算法封装成此编程语言的函数 ,需要时调用。这样,不同命令的结果可以更加方便地进行交互。
So,我开心地 develop 起了一个新的编程语言 -_-
二、后来呢?
我记得自己大二的时候曾经在编程课的作业里做过一个解析算术运算的程序,那大概就是我唯一与解释器发生关系的时刻了。除此以外,我完全没有任何经验。
所以一切靠发挥“想象力”(就是怎么方便怎么来的意思)。语法设计呢!什么?语法还要设计?
参照 Lisp 的原理,我很快就在草稿纸上设计好了新 Toy 的语法。因为是自己开发的语言,所以支持的全都是一些自己用得最多最熟悉的语法要素。第一遍的解释器是使用 Java 写成。因为是 Java,所以开发过程非常快,只用了一周的时间就写完了。看着自己开发的语言,在只有少量 bug 的情况下顺畅地运行了,情感上来说是一件很自豪的事(虽然理智告诉我,我将是这个星球上第一个也是最后一个使用它的人)。
Toy 解释器原型的开发使我理解了不少编程语言的细节,这些东西是我在以前从来不曾想过的。一个最重要的冲击是,任何一门流行的编程语言都是真的牛啊!你所常用的任何一种语言要素,不论它多么简单自然,它都不是凭空而来的,都对应着某段代码 —— 言下之意,都是需要实现的。做完了原型之后,我开始认真思考这个解释器的语法规则,需要包含哪些要素,而舍弃哪些要素。当然这是一个需要不断尝试总结的过程。
基于 Java 的原型,我开始尝试使用 C 语言对 Java 代码进行翻译,这才逐渐相一个比较完备的编程语言需要的基本要素靠拢。整个开发过程是断断续续的,完全抽空闲时间来做,期间好多次想放弃,但我总是告诉自己,已经花了这么多精力了,不搞出一个完成度还说得过去的半成品,然后在知乎上写一篇文章假模假样总结一番,怎么对得起自己投入进去的这么多沉没成本?
三、心得体会
我对 Toy 的前途有过期待,不过激情冷却之后才会意识到自己的想法多么的不切实际。市面上优秀的编程语言太多了,没有人有必要使用一个业余开发者做的玩具产品,对吧。想明白了这一点,我才知道了这个项目真正的用途是什么:
就如当初开发它的初衷一样,Toy 的意义就是服务我个人的算法学习和实践,让我以后在实现了一个新算法之后,可以非常方便地把它封装进 Toy 当中进行测试。例如有一天我在写其他程序的时候实现了 Guass 某某积分,或者某某优化算法,那就可以立刻把这部分代码拿过来封装进 Toy 里作为它的一个新功能,而 Toy 的源代码则成为了我的一个算法仓库(可以随时测试那种),随着时间的积累变得越来越丰富。想想也挺激动的呢。