打字猴:1.70043669e+09
1700436690
1700436691
1700436692 就能区别开上述两种情形。(PLUS和PLUSPLUS是数学符号的名称,这样C语言代码就比较容易处理。)
1700436693
1700436694 图5-4展示了在创建C语言程序时如何使用Yacc和Lex。该程序是某种语言的编译器。Yacc为语法分析器生成一个C语言文件,Lex为词法分析器生成另一个C语言文件。这两个C语言文件与其他包含语义的C语言文件组合在一起,由C语言编译器编译成可执行程序。这张图是用Pic程序制作的,Pic的结构也正是如此。
1700436695
1700436696
1700436697
1700436698
1700436699 图5-4 使用Yacc和Lex创建编译器
1700436700
1700436701 尽管Lex如此简单而强大,但长期以来,它并没有像Yacc那样得到广泛应用。也许这是因为对于相对缺乏经验的程序员来说,为复杂的语言编写语法分析器令人生畏,而编写词法分析器则不然。但是,亲手编写词法分析器,无论看起来多么简单直接,都未必是个好主意。
1700436702
1700436703 我在awk脚本语言上的经验(本章后文将讨论)可以借鉴。awk的第1版实现使用Yacc处理语法,使用Lex分解输入内容。然而,当我们试图将awk移植到非Unix环境时,Lex要么无法使用,要么虽然能用但生成了错误的词法分析器。几年后,我勉力用C语言重写了awk的词法部分,这样它就可以移植到所有环境中。但在之后的几年里,那段手工写出来的词法代码产生了许多缺陷和小问题,这些麻烦在Lex生成的版本中原本不存在。
1700436704
1700436705 这证明了一个普遍规则:程序帮你写的代码会比你自己手写的更正确、更可靠。如果改进了生成器,例如能生成更好的代码,那么每个人都会受益;相反,对手写程序的改进并不能改善其他程序。像Yacc和Lex这样的工具是这一规则的极好例子,Unix也提供了许多其他工具。编写程序的程序总是值得尝试。就像道格·麦基尔罗伊所言,“任何你必须重复做的事都有待自动化。”
1700436706
1700436707 3. Make
1700436708
1700436709
1700436710
1700436711
1700436712
1700436713 多数大型程序都由多个源文件组成,这些源文件必须被编译并连接在一起,才能创建可执行程序。这通常可以通过执行单个命令来完成,如用cc *.c来编译一个C语言程序的所有源文件。但是,在20世纪70年代,计算机速度非常慢。在对某个文件进行修改后,重新编译包含多文件的程序,花费的时间可能会以分钟计而非以秒计。更有效率的做法是,只重新编译修改后的文件,并将结果与之前编译的其他文件连接起来。
1700436714
1700436715 然而,记住哪些文件最近编译过,哪些需要重新编译,是件麻烦事,很容易出错。有一天,史蒂夫·约翰逊向斯图·费尔德曼(图5-5)诉苦说,在花了几个小时调试无果后,他才意识到,问题出在没重新编译他修改过的一个文件。
1700436716
1700436717
1700436718
1700436719
1700436720 图5-5 斯图·费尔德曼,约1981年(杰勒德·霍尔兹曼供图)
1700436721
1700436722 凑巧的是,斯图也有同样经历。他也曾白费劲调试过肯定正确只是没重新编译的程序。他想出了妙招:使用某种规格语言来描述程序的各个部分是如何相互依赖的。他写的Make程序分析这些规格,并根据文件修改时间来做尽可能少的重新编译,使所有东西都能同步向前。Make的第1版实现是在1976年。
1700436723
1700436724 “我在周末写出Make,然后在下个周末用宏重新写了一遍(因为内置代码列表太长了)。我没有修正tab-in-column-1[5]问题,因为很快就有了十几个忠实用户群,我不想惹恼他们。”
1700436725
1700436726 Make一下子就成功了,因为它能避免愚蠢的链接错误,同时尽可能提高编译效率。对于那些不仅仅编译C语言,而且涉及更复杂处理的程序来说,它也是福音。例如,使用Yacc和Lex时,必须先分别运行Yacc和Lex,创建C语言文件,然后才进行编译,如图5-4所示。makefile文件记录了编译新版本程序所需的所有处理步骤,也可以描述如何完成相关任务,如运行Lint、做备份和输出文档。makefile文件有点像shell脚本,但它采用声明式语言:说明依赖关系和如何更新组件,但不会明确检查文件创建时间。
1700436727
1700436728 斯图提到的“tab-in-column-1”问题,标新立异又笨拙地限制了makefile文件格式。这可以说是设计缺陷,也说明成功程序都会面临的普遍问题:如果程序很好,它就会吸引用户,然后就很难用任何不兼容的方式来修改。Unix和大多数其他系统都充斥着最初就存在的瑕疵,根深蒂固,无法修复。
1700436729
1700436730 Make也是本节主题的范例:与其手写代码或手写操作序列,不如创建一套符号或规格,声明必须做什么,然后写程序来解释规格。这种方法用数据代替了代码,几乎总能见效。
1700436731
1700436732 时至今日,Yacc、Lex和Make还是常用工具,因为它们解决了程序员依然面对的重要问题,而且解决得如此之好,以至于它们的设计,有时甚至是原始实现,至今仍在使用。
1700436733
1700436734 说句题外话,我第一次见到斯图是在1967年左右。当时我在普林斯顿大学读硕士,而他则是本科生,为贝尔实验室兼职研发Multics。在麻省理工学院获得天体物理学博士学位后,斯图加入1127中心。他1984年去了Bellcore,然后到IBM任职,再后来加入谷歌公司。对我来说有点幸运的是,有几个夏天我去谷歌公司做访问学者时,他是在我上面几级的经理。
1700436735
1700436736
1700436737
1700436738
1700436739 UNIX传奇:历史与回忆 [:1700434701]
[ 上一页 ]  [ :1.70043669e+09 ]  [ 下一页 ]