07 | 编译器前端工具(二):用Antlr重构脚本语言
07 | 编译器前端工具(二):用Antlr重构脚本语言
讲述:宫文学
时长15:34大小14.27M
完善表达式(Expression)的语法
完善各类语句(Statement)的语法
1. 研究一下 if 语句
2. 解决二义性文法
3. 研究一下 for 语句
用 Vistor 模式升级脚本解释器
课程小结
一课一思
赞 9
提建议
精选留言(36)
- 李懂2019-08-29现在都是用一门语言去实现这些功能,我想知道最开始的语言是怎么实现分析的呢!有一点鸡生蛋蛋生鸡!
作者回复: 在编译领域,有一个事情,叫做自举(bootstraping),也就是这门语言的编译器可以用自己这门语言编写。这是语言迈向成熟的标志。一般前面的版本,是要借助别的语言编写编译器,但后面就应该用自己的语言来编译了。 著名的语言都实现了自举。比如,go语言的编译器是用go编写的(早期版本应该是用C语言写的编译器。能实现自举,还是go发展历程上的一个历程碑)。 最早的语言的编译器,那肯定是用汇编写。到一定程度后再自举。
共 2 条评论20 - Spring2019-08-28老师,你好。请教一下,词法,语法解析后生成 AST 后,计算机怎么指导我的AST 中的“+” 就是执行 add 的计算呢?这其中是不是还有还存在一个中间层?
作者回复: 对。你提的问题很好。 说明你思考的很深入了。 “+”执行加法运算,是由计算机语言的语义规定的。比如,你可以再让“+”去做字符串连接,这也是语义上的规定。 所以,计算机语言之间真正的差别,其实在语义上。 词法分析、语法分析完毕以后,只是搭起一个数据结构。至于基于这个结构可以干什么,还必须附加语义。你可以在这个AST上附加一些“动作指令”,比如对AST遍历的时候,遍历到“+”,就把两边加起来。这就是属性计算做的事情。我们把value作为一个属性,用一些规则来计算属性。说起来,属性计算还是大师高德纳提出来的。 你再沿着自己的思路深入下去,你可能自己把高德纳大师想到的也都想出来了。 看来你对编译原理的直觉很好:-D
17 - Void_seT2019-08-28老师,目前的学习过程中,比如表达式语法规则、语句语法规则等,虽然能知道它们表示了什么,但是并不知道它是怎么凭空产生的;请问:这种规则是相对比较固定的,我们要使用时,可以参照“标准”的规则文法进行修改呢?还是要自己掌握各种类型语法规则的各个组成细节,以便于在写语法规则时可以信手拈来呢?如果需要熟练掌握语法规则的各个组成细节,目前的工作如果还用不到生成“小编译器”这种技能,也就是没有练习或高强度的训练时间的话,是否需要现在就硬啃下这块硬骨头(因为怕长时间不使用,将来真正要使用时,还是要重新再训练一遍)?展开
作者回复: 各种文法规则的设计经验的积累,属于"最佳实践"的范畴。我建议大家不仅仅是要懂原理,还能掌握一些最佳实践,说起某个语法现象的时候,随后就能写出几个文法来。 能有这种实操能力,才算是把理论落到实际了。这些“最佳实践”,属于你自己积累的领域经验,这也是你为什么会更有竞争力的原因。 这些经验,只有动手,多看别人的,才能积累。一般没有书籍专门讲这个,顶多是以示例的方式呈现。
15 - 宇智波芭芭干2019-09-10学习时总感觉节奏在老师那边,自己的思路并不连贯,对于初学者容易出现断片。在极客时间其它老师那里也同步购买了linux以及网络协议,另外一边通过故事的形式通熟易懂的讲解了一些底层知识原理,学习也是相当顺畅有兴趣,而这里不知道为啥就是顺畅不起来,差距不是一般的大。
作者回复: 谢谢提意见。我们会收集大家的意见,在课件版本迭代时提升表述水平!
共 7 条评论7 - windpiaoxue2019-08-31老师您好 例如下面这个规则: stmt -> if expr stmt | if expr stmt else stmt | other 我测试了一下,antlr使用上面这个规则可以正确的处理悬挂else的问题, antlr在处理这种二义性问题的时候,是依据什么来处理的。展开
作者回复: 为你的动手实践点赞! 其实原因我在文稿里已经说了。 我们实现一个算法的时候,是有确定的顺序来匹配的。所以,即使是二义性文法,在某种算法下也可以正常解析。 严格的非二义性文法要求得比较高。它要求是算法无关的。也就是不管你用最左还是最右推导,得出的结果是一样的。 关键点,在于把“文法”和“算法”这两件事区分开。文法是二义的,用某个具体算法却不一定是二义的。 其余的部分,你可以再看看文稿,是否能理解。Antlr是LL算法,最左推导、深度优先。如果你一时看不明白,也没关系,因为到后面我还会专门讲LL算法。
7 - Geek_6304e32022-02-11老师,后面的用 Vistor 模式升级脚本解释器开始有点看不懂,不懂java,有JavaScript版本的吗?文章一些代码都是直接说这样写,但是我不知道这样写之后在哪里关联运行起来。共 1 条评论4
- Geek_6304e32022-02-11visitor这些解释器要怎么执行呢?2
- Geek_6304e32022-02-11visitExpression方法实在哪个文件生成的?文中没有说。2
- fung2019-11-05老师,这一段看不懂咋办,有的救吗?看不懂这些语法啊,能解析下吗?或有其他资料介绍吗?谢谢 expression : primary | expression bop='.' ( IDENTIFIER | functionCall | THIS ) | expression '[' expression ']' | functionCall | expression postfix=('++' | '--') | prefix=('+'|'-'|'++'|'--') expression | prefix=('~'|'!') expression | expression bop=('*'|'/'|'%') expression | expression bop=('+'|'-') expression | expression ('<' '<' | '>' '>' '>' | '>' '>') expression | expression bop=('<=' | '>=' | '>' | '<') expression .......展开
作者回复: 首先,关于Antlr的详细语法,你可以看一下它的作者的一本书:《the definitive antlr 4 reference》,应该也有中文版的。 另外,你可以搜一下EBNF的语法,因为antlr的语法基本上就是EBNF的语法,跟正则表达式的语法也很像,然后又加了一些元素,比如给某些部分做了命名。 bop=('+'|'-')是给('+'|'-')起了个名称,便于引用。 最后,当你动手实践的时候,这些困难就都不存在了。你就是对它们陌生。用多了就不陌生了!
共 2 条评论2 - shantelle2019-10-25宫老师你好,请问这个匹配的是什么内容呢 '\\' [btnfr"'\\]
作者回复: 转义字符,比如:\t是tab。
2 - zhj2019-10-16现在拿到的ASTEvaluator,都裹扎了编译器相关的代码,这里才看到Ast树,这边没有很好的 版本迭代吗,上来直接就是讲课同步的代码,看的云里雾里,没法循序渐进
作者回复: 回头把代码分拆整理一下。
2 - 石维康2019-08-28statement : blockLabel=block | IF parExpression statement (ELSE statement)? | FOR '(' forControl ')' statement | WHILE parExpression statement | DO statement WHILE parExpression ';' | SWITCH parExpression '{' switchBlockStatementGroup* switchLabel* '}' | RETURN expression? ';' | BREAK IDENTIFIER? ';' | SEMI | statementExpression=expression ';' ; 请问" : blockLabel=block"这个规则如何解释?谢谢!展开
作者回复: 这是给block起了个别名,这样在生成的AST节点StatementContext中,就会有blockLabel这个属性,来访问这个下级节点。 就是为了编程方便的。
2 - Geek_6304e32022-02-11老师,你给的java版本的项目跑不起来。。不懂java1
- 草戊2019-12-15老师,有些语言的部分文法是上下文有关,比如说必须是第七列写*号来注释。对于这样的语言分析,有什么好的建议吗?在parser前先做预处理变换?
作者回复: 如果是手写编译器,就很容易处理。在处理*号的时候加一点代码进行上下文的分析就好了。 像Antlr这样编译器生成工具,支持你在做解析的时候嵌入自己的代码,进行与上下文有关的分析。分析的结果,会反馈回来影响编译过程。 所以,在词法或语法分析时就开始进行上下文的分析(或语义分析),是一个普遍使用的技巧。 如果你想深入了解一下这个问题,推荐你看一下这篇论文: https://www.antlr.org/papers/predicated-parsing.pdf 这里面还有其他一些例子。
1 - cry soul2019-10-05建议老师用用git搭好tag来表示每个课程到到哪部分源码,不然需要读好几篇才能自己尝试。
作者回复: 谢谢你的建议。有的代码文件确实很长,查找不太容易。我后面优化一下代码链接!
1 - 李懂2019-08-29JavaScript中的this是咋实现的,这个一直处于迷糊当中,好想弄清楚,不同语言之间语意的差别,学完语意能理解么😇 😇 😇 ,看了很多课程,都很失望,都是再讲几种场景,怎么指向,没实质的改变!
作者回复: 我记着你这个需求。 我看看能否把这个点插到某一讲中。
共 4 条评论1 - 中年男子2019-08-28编译 git 里 PlayScript-cpp, 我这里报错, PlayScriptJit.h 这个文件, 搞了半天没搞懂 In file included from /Users/shiny/learn/PlayWithCompiler/playscript-cpp/src/PlayScript.cpp:5: [build] In file included from /Users/shiny/learn/PlayWithCompiler/playscript-cpp/src/grammar/IRGen.h:28: [build] /Users/shiny/learn/PlayWithCompiler/playscript-cpp/src/grammar/PlayScriptJIT.h:33:31: error: unknown type name 'LegacyRTDyldObjectLinkingLayer'; did you mean 'RTDyldObjectLinkingLayer'? [build] using ObjLayerT = LegacyRTDyldObjectLinkingLayer; [build] ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [build] RTDyldObjectLinkingLayer展开
作者回复: 你的进度有点快! playscript-cpp我还没有整理好。 如果你着急看后端的东西,建议你先做两件事情: 1.用Antlr将.g4文件生成c++代码,测试一下在C++中运行是否OK。 2.下载和安装LLVM,做做教程里的例子,有一个是c++的例子。 好消息是,这两个项目都是用cmake管理的。
1 - 许童童2019-08-28难度越来越大了,要好好消化才行。
作者回复: 我相信你的消化能力:-D
1 - ( ̄_ ̄ )2019-08-28写了一晚上终于用c语言模仿着实现了第二节课的内容 https://github.com/hongningexpro/Play_with_Compiler/tree/master/01-Simple_Lexer
作者回复: 点赞! 动手出真知!
1 - 三省2023-02-09 来自北京每个visit返回的rtn是哪几个单词的缩写呢?