作者回复: 各种文法规则的设计经验的积累,属于"最佳实践"的范畴。我建议大家不仅仅是要懂原理,还能掌握一些最佳实践,说起某个语法现象的时候,随后就能写出几个文法来。
能有这种实操能力,才算是把理论落到实际了。这些“最佳实践”,属于你自己积累的领域经验,这也是你为什么会更有竞争力的原因。
这些经验,只有动手,多看别人的,才能积累。一般没有书籍专门讲这个,顶多是以示例的方式呈现。
作者回复: 在编译领域,有一个事情,叫做自举(bootstraping),也就是这门语言的编译器可以用自己这门语言编写。这是语言迈向成熟的标志。一般前面的版本,是要借助别的语言编写编译器,但后面就应该用自己的语言来编译了。
著名的语言都实现了自举。比如,go语言的编译器是用go编写的(早期版本应该是用C语言写的编译器。能实现自举,还是go发展历程上的一个历程碑)。
最早的语言的编译器,那肯定是用汇编写。到一定程度后再自举。
作者回复: 对。你提的问题很好。
说明你思考的很深入了。
“+”执行加法运算,是由计算机语言的语义规定的。比如,你可以再让“+”去做字符串连接,这也是语义上的规定。
所以,计算机语言之间真正的差别,其实在语义上。
词法分析、语法分析完毕以后,只是搭起一个数据结构。至于基于这个结构可以干什么,还必须附加语义。你可以在这个AST上附加一些“动作指令”,比如对AST遍历的时候,遍历到“+”,就把两边加起来。这就是属性计算做的事情。我们把value作为一个属性,用一些规则来计算属性。说起来,属性计算还是大师高德纳提出来的。
你再沿着自己的思路深入下去,你可能自己把高德纳大师想到的也都想出来了。
看来你对编译原理的直觉很好:-D
作者回复: 谢谢提意见。我们会收集大家的意见,在课件版本迭代时提升表述水平!
作者回复: 为你的动手实践点赞!
其实原因我在文稿里已经说了。
我们实现一个算法的时候,是有确定的顺序来匹配的。所以,即使是二义性文法,在某种算法下也可以正常解析。
严格的非二义性文法要求得比较高。它要求是算法无关的。也就是不管你用最左还是最右推导,得出的结果是一样的。
关键点,在于把“文法”和“算法”这两件事区分开。文法是二义的,用某个具体算法却不一定是二义的。
其余的部分,你可以再看看文稿,是否能理解。Antlr是LL算法,最左推导、深度优先。如果你一时看不明白,也没关系,因为到后面我还会专门讲LL算法。
作者回复: 谢谢你的建议。有的代码文件确实很长,查找不太容易。我后面优化一下代码链接!
作者回复: 我记着你这个需求。
我看看能否把这个点插到某一讲中。
作者回复: 你的进度有点快!
playscript-cpp我还没有整理好。
如果你着急看后端的东西,建议你先做两件事情:
1.用Antlr将.g4文件生成c++代码,测试一下在C++中运行是否OK。
2.下载和安装LLVM,做做教程里的例子,有一个是c++的例子。
好消息是,这两个项目都是用cmake管理的。
作者回复: 点赞!
动手出真知!
作者回复: 如果是手写编译器,就很容易处理。在处理*号的时候加一点代码进行上下文的分析就好了。
像Antlr这样编译器生成工具,支持你在做解析的时候嵌入自己的代码,进行与上下文有关的分析。分析的结果,会反馈回来影响编译过程。
所以,在词法或语法分析时就开始进行上下文的分析(或语义分析),是一个普遍使用的技巧。
如果你想深入了解一下这个问题,推荐你看一下这篇论文:
https://www.antlr.org/papers/predicated-parsing.pdf
这里面还有其他一些例子。
作者回复: 首先,关于Antlr的详细语法,你可以看一下它的作者的一本书:《the definitive antlr 4 reference》,应该也有中文版的。
另外,你可以搜一下EBNF的语法,因为antlr的语法基本上就是EBNF的语法,跟正则表达式的语法也很像,然后又加了一些元素,比如给某些部分做了命名。
bop=('+'|'-')是给('+'|'-')起了个名称,便于引用。
最后,当你动手实践的时候,这些困难就都不存在了。你就是对它们陌生。用多了就不陌生了!
作者回复: 转义字符,比如:\t是tab。
作者回复: 回头把代码分拆整理一下。
作者回复: 在PlayScript.g4中:
statement
: blockLabel=block
blockLabel就是给block起个别名而已。可以在StatementContext中访问blockLabel属性,来获得一个block子节点。
作者回复: 是的,只是个别名。
它会变成ExpressionStatement的一个属性。通过这个属性,可以获得一个子节点。
作者回复: playscript的java版我没有维护多个版本。所以里面有很多特性是后面几讲里的。
但是,你可以先关注本讲的内容。比如,(1)比较完整的语法规则文件;(2)ASTEvaluator.java如何完成计算的整体流程。
一些其他的细节,听完后面几讲以后,应该就能明白了。
作者回复: 这两个生成的结果一样。Antlr会知道其实'+'是你已经在词法规则中声明了的,知道这里等价于ADD。
如果词法规则里没有把'+'定义成ADD,也没关系,Antlr会自动添加词法规则,但名称就是它自己起的了。
作者回复: 我相信你的消化能力:-D
作者回复: 这是给block起了个别名,这样在生成的AST节点StatementContext中,就会有blockLabel这个属性,来访问这个下级节点。
就是为了编程方便的。