05 | 语法分析(三):实现一门简单的脚本语言
05 | 语法分析(三):实现一门简单的脚本语言
讲述:宫文学
时长13:37大小12.48M
增加所需要的语法规则
让脚本语言支持变量
解析赋值语句
理解递归下降算法中的回溯
实现一个简单的 REPL
课程小结
一课一思
赞 18
提建议
精选留言(34)
- janey2019-08-27像这里用java实现了一种脚本语言,但是这些java语句又是怎么被计算机识别的呢?
作者回复: java语句当然是由java的编译器来识别了。 不过你提了一个重要的事情。 在编译领域,有一个事情,叫做自举(bootstraping),也就是这门语言的编译器可以用自己这门语言编写。这是语言迈向成熟的标志。一般前面的版本,是要借助别的语言编写编译器,但后面就应该用自己的语言来编译了。 著名的语言都实现了自举。比如,go语言的编译器是用go编写的(早期版本不是。能实现自举,还是go发展历程上的一个历程碑),jdk里面自带了java语言的编译器,本身也是用java写的。
共 3 条评论26 - 风2019-09-29C语言实现: https://github.com/KiLuYa/simpleScript C语言,没有现成的数据结构,没有 try catch throw 处理错误的机制,没有虚拟机的垃圾回收机制,感觉实现起来比Java要麻烦很多,尤其是繁琐的错误码判断,以及程序流程在多分支下的内存的手动申请和释放。 遇到过一个国外公司的产品,它提供了脚本语言,但用户写程序,如果某一行有个语法bug,编译报错时,它会报连续十几行的错。学完这节内容就知道,应该是它的parser没有在检测到语法错误时停下来,还傻傻地带着错误继续parse,直到所有token都被处理掉。展开
作者回复: 看你用C语言做了很多实践,非常好! 针对你的问题,也跟你探讨一下: 1.每门语言都有它的优势。很多编译器都是用C/C++编写的,比如可以更灵活的对内存管理,就是优势呀。JVM的实现,也没法用Java,还是要用比较底层的语言。 2.错误处理这个问题比较复杂。最好的情况,是我们知道哪一个小范围是有错的,对这个部分报错,但其他部分继续处理。比如,你调用一个函数时,监测出参数的数量错了,但其他部分仍然可以继续去解析和处理。如果碰到一个错误,就完全停下,那也不行。这样做IDE的时候,就不够友好和智能。
共 2 条评论11 - 缺个豆饼吗2019-10-08https://github.com/yuguomin/my-compiler 老师,作业来啦~
作者回复: 棒!TypeScript版本的!
5 - ct2019-08-24根据老师讲解,实现了一个 golang 的版本 https://repl.it/@catplanet007/simple-interpreter
作者回复: 你用的这个在线工具很酷。可以提供一个运行环境直接跑!很棒! 我玩了好一会 :-D
6 - LDxy2019-08-24正则表达式匹配文本的时候也会导致回溯吧?好像还有可能因此导致严重的性能问题
作者回复: 对。如果正则表达式的内部实现是基于NFA的,就会有这个问题。 NFA和DFA这个知识点不适合在前期讲,会把初学者搞晕。我准备在后面找个机会放入这个知识点。
4 - wj2019-08-24老师, 还有个问题, 借此文问一下, 词法分析\语法分析等和机器学习有什么交集吗? 我有个场景想比较两个java文件的匹配度, 或者两段代码的匹配度, 不知道机器学习在这个场景是否可以应用, 以及如何应用呢?谢谢~
作者回复: 你提了一个好问题。 其实,人工智能的发展史经历了两个不同的路径。 早期,更多的是演绎逻辑。就是人为制定规则,比如自然语言翻译的规则,并不断执行这些规则。 第二条路径,是最近复兴的机器学习的方法。它更多的是归纳逻辑。机器学习是通过数据的训练,把规则归纳出来。这些归纳出来的规则目前还是比较黑盒的,人比较难以解读,但却很有用,更加准确。 你的需求场景用这两种方法应该都能解决,只不过落地时还要考虑很多细节和限制因素。
4 - Fan2020-12-10😂,又看了一次。建议老师可以把这两个专栏的内容集结后出书。
作者回复: 你看第二遍了?为你点赞!看来你对编译是真有兴趣,可以考虑把这个方向变成自己的技术专长,去干一点有深度的事情。 书的话,已经在整理,进度有点慢:-( 因为我又给自己挖了坑,想达到两个目标: 1.要求更加浅显易懂,再复杂的问题,也要简单说明白; 2.里面的例子用自己设计的语言。 所以... 我后面加快进度:-)
共 3 条评论4 - Smallfly2019-09-04本讲 Swift 版本实现: https://github.com/iostalks/PlayWithCompiler/tree/lecture-5 欢迎参考。
作者回复: 点赞!
共 2 条评论2 - 中年男子2019-08-25有了前几讲的基础,这一讲很轻松搞定,根据宫老师的java代码我实现了C++版本,其中一些不太清晰的概念通过代码也理解了,老师真的很棒!
作者回复: 谢谢肯定! 编译原理这门课,是学原理可能学不懂。但真正动手,其实都能写出来。早期写编译器的先驱并没有编译原理课。 并且,很多具体实现过程,是可以偏离死搬教条的原理的。比如,理想情况下要设计无二义性文法。实际应用中,只要针对某个具体算法是无二义的就行了。能实际有用才是硬道理。
2 - 安排2019-08-24有点感觉了,哈哈😄
作者回复: 加油!
2 - Amber2019-11-21表达式负数怎么处理呢?
作者回复: 负数有两种处理办法: 1.在词法分析阶段,就把它作为一个字面量提取出来。这有一定的难度。比如,a - 3和 a - -3要能准确地把减号和负号区分开。但也不是不能做到。 2.把减号作为一元运算符处理。 示例用的语法规则,是按照第二种方式处理的。
共 2 条评论1 - Sudouble2019-10-18跟着课程做,一下就明白了。打卡第五节课 https://github.com/potterhere/TheBeautyOfCompiling/tree/master/w5_ReadEvalPrintLoop
作者回复: 看到你的github空间里有好几个项目,学习力很强! 另外,能否把你的项目整个cmake文件,便于我编译运行?:)
共 2 条评论2 - 阿尔伯特2019-09-29https://github.com/albertabc/compiler 继续攒代码。我在老师前面几节的基础上写的本讲的一个sample。老师在本讲重点讲解了回溯。但是我在实现中仔细想了想。 exp -> or | or = exp 上次课的第一条语法规则其实是针对表达式的,但是这条规则,事实上是合并了表达式语句和赋值语句。所以本节的新的语法规则是不是可以就优化掉。 这里一旦不区分普通表达式,和赋值语句,也就避免了一次回溯。 从中,是不是可以有这样的推论,就好像用EBNF,可以通过语法规则的变换来避免左递归,也同样用规则来减少回溯? 谢谢老师。展开
作者回复: 首先,赋值在C和Java里都是表达式,跟加法表达式没啥区别,它也是有值的。 第二,确实可以通过语法规则的设计来避免冲突,包括避免回溯。
1 - 曾经瘦过2019-09-17学完了这部分之后 感觉 其实编译没有想的那么复杂,通过递归下降,对所有的可能做了处理.不符合的可能回溯 去匹配其他的. 有点感觉了,希望可以一步一步啃下编译原理
作者回复: 这个“感觉”很重要。保持好。遇到挫折也不要在意!
1 - nil2019-09-11老师你好,看到回溯这个关键字,让我想起学生时代解八皇后问题,用的就是试探&回溯。也是通过八皇后体会到了递归的美妙。递归思维比较符合人的思维,而循环更符合计算机。看了老师的一系列文章,现在对编译原理没有这么惧怕了,一旦揭开技术神秘的面纱后,展现在眼前的只剩下一片美妙!加油!
作者回复: 感谢分享! 一起加油!
1 - windpiaoxue2019-08-27参考03-05实现的c语言版本 https://github.com/windpiaoxue/simple_script.git
作者回复: 看了,非常不错!点赞! 连README.md都写得很清晰。 你学习的效率很高呀! 而且看来你有C语言的基础,所以到时学后端技术你也会毫不费力呀!
1 - 许童童2019-08-23老师讲得好啊,不要给自己设天花板,不断努力,成功最终会属于你。
作者回复: 是的。 很多时候,做某件事情真正的阻力是畏惧,是根本不去做...
1 - 雲至2019-08-23老师 那个verbase是什么意思呀
作者回复: 是verbose吧?也就是启动“话痨”模式,打印输出等多信息。 有一些linux命令习惯上会用-v参数来表达这个意思 :-D
共 2 条评论1 - 邹仁2022-03-21因为老师这边用的是Java,然后我比较喜欢用C++,看到代码中的SimpleASTNode这个类没有源码,于是我自己写了文法在C++上实现了这个解析器,也是支持变量声明和赋值表达式等运算,AST的打印 https://github.com/arqqqq/SimpleScript
- 严长友2021-12-22实现到表达式和简单语句部分,处理else if时遇了困难,过来学习学习 http://hello.321zou.com/play/playground.html