自己动手构建编程语言:如何设计编译器、解释器和DSL
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.4 整体程序结构

在查看整个程序结构时,我们需要查看整个程序是如何组织起来并串在一起的,以及语言中有多少嵌套的争议性问题。这似乎是事后诸葛亮,但程序中源代码是如何及从何处开始执行的呢?在基于C的语言中,程序从执行main()函数开始,而在脚本语言中,源代码边读取边执行,不需要main()函数来启动程序。

程序结构还有一个基本问题:整个程序是否必须在一起翻译并运行?或者不同的包、类或函数可以单独编译,然后链接和(/或)加载在一起以运行程序?编程语言设计者可以通过将内容构建到语言中(如果它是内置的,则无须计算链接),要求在运行时显示整个程序的源代码,或者通过为一些大家熟知的标准执行格式生成代码,而其他链接器和加载程序将完成所有的艰苦工作,从而避免实现的复杂性。

也许与整个程序结构相关的最大设计问题是哪些结构可以嵌套,以及对嵌套有什么限制(如果有的话)。这也许最好用一个例子来说明。在1970年左右,有两种晦涩难懂的语言被发明出来,它们为争夺统治地位而斗争,这两种语言就是CPascal

C语言几乎是单调的,其程序就是一组链接在一起的函数,只能嵌套相对较小(细粒度)的东西:表达式、语句,以及结构体定义。

相比之下,Pascal语言更具嵌套性和递归性。程序中几乎所有东西都可以嵌套,尤其是函数也能以任意深度嵌入函数中。虽然C和Pascal在能力上大致相当,并且Pascal稍微领先一步,但是目前大学课程中最受欢迎的还是C语言。C最终还是赢了。为什么?事实证明嵌套增加了复杂性,同时没有增加多少价值,或者仅仅是因为美国公司的实力。

由于C的胜出,许多现代主流语言(我在这里特别想提到C++和Java)开始时几乎都是单调的。但随着时间的推移,它们增加了越来越多的嵌套。为什么?要么是因为隐藏的Pascal信徒潜伏在我们中间,要么是随着时间的推移,编程语言自然会添加一些新特性,直到它们被过度设计。Niklaus Wirth看到了这一点问题,主张回归软件的小型化和简单化,但言者谆谆,听者藐藐,他的编程语言中也支持很多嵌套。

作为一名初露头角的编程语言设计师,你的实际收获是什么?不要过度设计你的语言!要让编程语言尽可能简单!除非需要嵌套,否则不要嵌套。同时,作为一名语言实现者,要准备好在每次忽略这些建议时付出代价!

现在,是时候从Jzero和Unicon中提取出一些语言设计示例了。对于Jzero,因为它是Java的子集,所以设计要么是一个大空心汉堡[1](我们使用Java的设计),要么是一种减法设计:我们从Java中提取了什么来设计Jzero,它看起来怎么样,感觉如何?尽管早期曾试图维持其小型化,但Java仍是一种大型语言。作为设计的一部分,如果我们列出在Java中但不在Jzero中的所有内容,则那会是一个很长的列表。

由于页面空间和编程时间的限制,Jzero必须是非常小的Java子集。然而,在理想情况下,输入到Jzero的任何合法Java程序都不应令人尴尬地失效:它要么能编译并正确运行,要么输出有用的解释性消息,解释用到了Jzero不支持的Java特性。这样你就可以很容易地理解本书其余部分的内容,并有助于将你的期望保持在可控的范围内,2.5节将介绍更多细节,包括哪些特性在Jzero中得到支持,哪些没有。