6. 定义编程语言语法的语言 | 忽软忽硬

忽软忽硬
忽软忽硬
6. 定义编程语言语法的语言 | 忽软忽硬
/

离上一期电台太久了,主要原因是我感冒了,这次感冒可能是我有生以来最漫长的一次感冒,在治疗过程中,有一些好玩的事情值得记录一下,希望你不要得这种类型的感冒。先讲一下这个感冒,再讲编程的事情。

这次感冒是支原体感染,和普通的病毒感冒,还有流感不同,虽然症状有类似的地方,但是持续的时间会很久。普通的病毒感冒大概一周就好了,流感也就两周,但是这种支原体感染,要4到8周才会好,如果运气不好,还会得严重的肺炎。

起初,我以为顶多是流感,但是一直不好,尤其是到了晚上,咳的睡不着觉,我就去了医院。医院验血以后,说这是支原体感染,你要做好长期咳嗽的准备,要4到8周。然后就给开了一堆药,最主要的是一种抗生素,叫阿奇霉素。回家我就开始吃药,结果第一天吃了以后,胃疼。我看了药的说明书,说胃部不适是常见的副作用。我就坚持吃了一周,毕竟关云长刮骨疗伤,我也不能太怂,胃疼加咳嗽,最后实在是坚持不下去了,我又去了医院。我跟医生说,吃这个药,胃太痛了,能不能换个药。医生说,如果不能坚持的话,你去买进口药,还是阿奇霉素,是辉瑞的,目前医院没有进口的。

我就去了药店,买了一盒辉瑞的药,55一盒,里面是6粒。从医院买的国产药,是12粒,85一盒。如果只看每颗的平均价格的话,国产的要便宜一些。但是,国产的每天要吃两颗才能达到0.25g,辉瑞的每天吃一颗就好。这样算下来,还是辉瑞要便宜。

当然了,吃了以后管不管用我也不清楚,辉瑞吃完后,相当于没吃,咳嗽感觉也没马上减轻,支原体咱也看不见有没有被杀死。国产药吃了,肯定有用,至少能感觉到胃疼,晚上能疼醒,可能剿灭支原体的战场在胃里。

我吃了两盒后,咳嗽减轻了一些,但是都快2周了,也不见好。我又去了社区门诊,没去医院,因为觉得好了一些,想问问要不要复查一下。我进去以后,一个女医生问我怎么了?我说支原体感染。她马上警觉了起来,她问:结婚了么?我心想,我去,得个病还要问这个,又不是介绍对象,是不是看上我了?我如实的回答:结了。她说,你要和你对象一起治疗。我回答:为什么要一起治疗,我老婆也没症状啊,难道这个病也像新冠一样,有无症状感染者。她说:有没有症状,要做妇科检查才知道,所有和你有性关系的人,都要一起做检查,这病传染性很强。我彻底迷糊了,我把在医院的病例,拍的肺部的片子给她看。她不耐烦的说:你这是感冒,肺炎,不要说什么支原体感染。她也没给我开药,让我多喝点热水。

出了社区门诊,我拿出手机,搜了一下什么叫支原体感染,才发现,支原体感染是一种很常见的生殖系统的疾病。如果大家以后不幸得了导致肺炎的支原体感染,去医院的话,千万要说自己得了肺炎,而不要说自己被支原体感染了,要不容易引起误会。

好了,最近没录电台的原因就是这个,接下来讲编程语言的语法。

还是要回到历史的原点,FORTRAN语言的作者巴科斯获得过图灵奖,并不是因为他发明了FORTRAN语言——世界上第一门大规模被使用的编程语言——而是因为他对编程语言的语法做了开创性的工作。

这个美国花花公子巴科斯(Backus)和丹麦人诺尔(Naur),两个人完成了一种叫BNF的语法表示方法,这种语法表示方法可以表示任意的编程语言,当然,也包括各种网络协议,可以称之为编程语言的语言。如果你还不知道BNF或者BNF的各种扩展,那么,我也没法在电台里讲清楚。如果你是个对编程技术比较严肃的程序员——就是那种在公司里只知道干活,但是从来捞不到多少好处的人——你应该去了解一下NBF,就算你不了解BNF,其实你已经在使用BNF了,只是你不知道你在使用而已。

不管你用哪种编程语言,最终,这些语言都要被编译成计算机能懂的东西——也就是抽象语法树——这期间的转化过程中,就使用了BNF。

我们现在使用的编程语言,不论是C还是Java,Python或者Ruby,都有自己的语法树,这些语法树几乎是一样的,都是基于栈机器的语法。以深受广大编程爱好者喜欢的Python为例,当你引入Python自带的库dis以后,就可以看到你写的Python程序,编译成的语法树是个什么状态,还有Python语言自带的ast库,直接把语法树给你打印在屏幕上。其它的语言也都有类似的功能,什么先词法分析后语法分析,这就牵扯到《编译原理》了。

我就以数学(1+2)*3为例,在栈机器——也就是今天我们用的机器——中来表达,并不太容易,尤其是按照普通人正常的顺序来表达,要加上括号才能让这个表达式没有歧义,在数学上,我们称之为优先级。为了能在计算机中表达,我们发明了前缀,中缀和后缀表达式这三种方法。我们最熟悉的中缀表达式,实际上是歧义最多最难在计算机中表示的表达式。

有兴趣的人,可以看看你用的那个语言对这个表达式生成的是什么样的语法树,或者你看看你最讨厌的那个语言生成的是什么样的语法树。在python中,用ast库很容易看到结果。大家仔细看的话,会发现生成了一棵树。里面有什么Left有Right,还有op(操作符)等。如果大家学过《数据结构》,应该很容易看出用的是二叉树。

大家可以思考一个问题,如果你来表示二叉树的话,你会用什么方法?虽然我也猜不出来,但是你肯定想不出来用Python的方法,太复杂了。当然了,Python也不是独创,而是学的C语言的,C语言也不是原创,是学习的前面所说的BNF,只是把名字换了一下叫YACC。大家可以搜一下,这个YACC是个什么东西,这其实是一个实现了BNF的软件。YACC对C语言和Unix有极大的促进作用。

大家都知道所有的语言都要写一个hello world,如果说hello world是世界上被写的最多的软件——如果这个能叫软件的话,那么,第二多的软件应该是YACC。所有的语言都实现了不同版本的YACC,只是名字有些不同,比如Python实现的叫PLY,像GNU实现的版本叫Bison北美野牛),这个软件可以支持N多语言,包括C/C++,Java还有D语言等等。

如果以上我讲的内容你都知道,什么抽象语法树,你全部都懂。我相信你肯定没有听说过叫Lisp的语言,有个软件专门给Lisp语言生成抽象语法树的。这是为什么呢?原因很简单,Lisp不需要这个软件,因为Lisp的语法,写出来就是抽象语法树。Lisp的语法,学名叫S表达式。

据说当年在发明Lisp的时候,这个世界上第二被发明出来的语言,据说其作者麦卡锡没有想好如何处理生成的抽象代码树,就先用括号这么写吧。结果这么写习惯了,就觉得要什么语法,直接写抽象代码树得了,于是,Lisp成了世界上第一门不需要大量语法的编程语言,这也是为什么至今为止很多人推崇Lisp的原因。

在Lisp中,所有的结构都是用括号括起来的,这样避免了其它语言中可能出现的歧义,Lisp程序员不像其它程序员一样,需要记忆运算的优先级,Lisp语言中没有优先级这个概念。但是世界上只有一个Lisp语言,直接写抽象语法树,其它的语言都做不到Lisp这么特别,所以,我还是要讲一下其它的语言吧。

这期节目最重要的是讲BNF,如果你以前没听过,十分建议你搜一下这是个什么东西。如果你以前听过,我也没法用半小时讲清楚,我可以讲一下我那个年代是如何接触到这个东西的,能大家做点参考,好像现在已经不像以前我上大学的时候一样了,大学里已经不用BNF来折磨学生了。

我上大学的时候,是20多年前,那时候,有一种老师还没退休,他们就是传说中的老三界。老三界就是传说中的66,67和68年的高中生,他们本来可以高考的,结果上山下乡了,十年时间,没机会读书,然后,十年后,也就是77年参加高考,这样导致那一批高考的人,年龄相差巨大,上下差15岁。这批人,如果在那十年中,还坚持学习,而且是自学,一定对知识或者权力有某种执着的念头。

我分析过这个事情,那批人,只要进入大学,一定能有好的前途,这几乎是肯定的。混个20年,不能说个顶个是个部长吧,也一定很有出息。但是,这其中也有一部分人,没当上官,在大学里甚至连管理人员都没当上,而是当了个教学的岗位,只有两种情况,一种是他特别热爱教学,另一种是,他不太懂人情事故,否则搞个副校长当当,不比当个副教授爽么?

我那时候,就有几个老三界的老师,在教本科生。我不好评判他们,但是他们是骑自行车来教课的,教本科生,有四位老三界老师,被称之为“四大名捕”。所谓的四大名捕,就是有一分就给一分,如果你考试是59.5分,那么,也不会多给你0.5分。其中一门课程是《C语言》,这个老师姓张,那时候上课不像现在,有多媒体教室,他靠粉笔在黑板上讲C语言。

我那时候还不太逃课,尤其是编程的课,我还挺喜欢上的。这个张老师,也就是正经对着课本上了2次课,就开始按照自己的方法在黑板上写天书了。他教的就是BNF,他说C语言没什么好讲的,我来教你们BNF,然后讲了两三次BNF的课程,开始布置作业,让我们用BNF把FORTRAN 77给表示出来,当时我们在学的C语言,他开始让我们自学FORTRAN 77,并且用BNF表示出来。每个人一个作业本,不书面考试,因为当时电脑很珍贵,一个班里也就2-3台电脑,也不上机考试,所有的成绩,全靠平时的作业。

说实在的,我们所有人,全都不会做。就瞎搞,要不这个老师当不上领导呢,太较真了,所有的作业他亲自批改,连助教都不用,完全把大学生当小学生看待。大概搞了半学期的FORTRAN 77,用BNF把FORTRAN 77给表示出来,这实际上要求你精通FORTRAN 77的语法,否则你没法表示出来。然后,下半学期,再用BNF把C语言的语法表示出来。

过了很多年后再想到这件事,就会把当年的恐惧忽略掉,这门课挂了接近1/4的人,我没挂,收获还是挺多的。其实用BNF来精确的定义C语言,会把学生锻炼成一个编译器,出了错误你一点也不慌,因为有张老师的折磨,编译器的那点折磨不算什么。我上了张老师的两门必修课,一门《C语言》一门《编译原理》。当时十分想避开他的课,可惜这两门是必修课,回避不了。

还有一门四大名捕的课是《网络协议》课程,这个老师也是老三界,跟张老师差不多,骑自行车的副教授,每年要带一定课时的本科。也是那种不会搞人情事故的人。这个老师姓刘,我本家,但是他把我折磨死了,前面那个张老师,倒是没什么关系。

我上大学的时候,玩过电脑的人没多少,就算是玩过电脑,但是会上网的人,更少。上了两次课,他知道我们上学期已经被张老师教了BNF,他说,他要扩展BNF,类似于现在的ABNF吧。他上课的时候就问,你们谁有电子邮箱?会发电子邮件么?

当时我太虚荣了,我有电子邮件,那时候有个网站叫chinaren,我注册了一个2M的邮箱,在网页上发电子邮件,当时主要是发电子贺卡。我竟然举手了,当时还有几个傻蛋,也举手了。老师嘿嘿一笑,说,你们举手的这几个,用ABNF把电子邮件的协议表示一下,当时我差点脑子爆炸。没举手的那一些,也没什么好果子吃,用ABNF把HTTP协议表示一下。

说实在的,表示HTTP要简单一些,为什么呢?因为虽然我举手了,但是我只会在网页上发电子邮件,根本不会配置电子邮件服务器,也不会设置outlook的客户端。这样导致的结果是,我们几个举手的傻逼,根本不知道如果才能抓到电子邮件的数据包。而HTTP的人会好很多,至少,在网上上一抓就能抓到,而电子邮件协议,由于只会在网页上操作,抓的包也是HTTP的包。

这样搞下来,差点给当场死亡。我们几个人真是大眼瞪小眼,两眼一摸黑,等着挂科的到来。后来还是勉强的通过了,为了搞这个,自己学会了配置电子邮件发送服务器,叫sendmail,在redhat下配置。当时大家都用windows 98,刚开始搞个redhat,连装都装不上。反正,有人逼你,你总是可以把问题慢慢的搞定的。

我经常讲的一句话是:穷,可以克服一切困难。

比如说,我在本期电台里讲了BNF和ABNF,这个可以定义编程语言或者网络协议,而且是精确的定义,一点错误也不出。如果不够穷的人,就会觉得,这傻逼,搞这种没意义的事情干啥。如果够穷的人,这个穷,可能是导师的逼迫,可能是公司的逼迫,肯定能学会的。

如果大家对编程语法,或者网络协议有兴趣的话,可以用BNF或者ABNF来表示一下,可能会花一点时间,但是真能让你精确的理解你的编程语言,或者网络协议,这个是一点错都不能出的,如果出错了,就玩蛋了。

以我的经验是,每个编程语言的高阶的操作,经常是用自己写一个自己的parser,大家可以搜一下,python啊,go啊,rust啊,都搞这样的操作。除了Lisp,Lisp用不着搞这些弯弯绕,不用解释自己,我自己就是解析后的自己,Lisp搞的比较高端,如果要解析的话,像大家都应该知道的SICP这门课程,Lisp已经不满足于解析编程语言了,而是开始解析微积分等数学问题,相当于是把数学语法给表示出来。

本期的重点是BNF与ABNF,有兴趣的,自己搞半个月试试。

7人评论了“6. 定义编程语言语法的语言 | 忽软忽硬”

  1. 栋哥一定是太操劳了才得了支原体肺炎,一定要好好休息提高免疫力啊。我家丫头幼儿园三年里每个月都得一次,我刻骨铭心,但这样我都没被传上。

  2. 哈哈,我的父亲就是 78 年考上大学的,不过他是应届生。后来就一直是副教授,老是跟我们骂院里的领导。
    我很感激有这样一个父亲。

发表回复

您的电子邮箱地址不会被公开。