02 | 正则文法和有限自动机:纯手工打造词法分析器
02 | 正则文法和有限自动机:纯手工打造词法分析器
讲述:宫文学
时长13:00大小14.90M
解析 age >= 45
初识正则表达式
解析 int age = 40,处理标识符和关键字规则的冲突
解析算术表达式
课程小结
一课一思
赞 62
提建议
精选留言(102)
- KnowNothing2019-08-16老师,在关键字和保留字的识别上,我认为有不需要加入中间状态的更简单的方式: 完成词法分析后,遍历所有ID token,如果其text在关键字和保留字集合内,将该token类型改成对应的关键字/保留字类型。 或者, 每当识别出一个ID token,都检查其text,如果是在关键字和保留字集合内,纠正type。
作者回复: 没错,可以的。 但是构造成有限自动机的话,程序就可以标准化处理。不需要再手写其他代码。比如正则表达式工具。 当然,如果纯手写词法分析器,不受任何标准算法的限制的话,那发挥空间就会大很多。 爱动脑的好同学!
共 14 条评论59 - 傲娇的小宝2019-08-18感觉有限状态机有点类似图灵机的工作方式。我一般只用正则匹配一下文件名或者某个字符串是否符合我的预期。
作者回复: 有限自动机是比较简单的一种自动机,对应于正则文法,也叫做3型文法。 比它强大的是下推自动机,对应于上下文无关文法,也叫做2型文法。 比它更强大的是线性有界自动机,对应于上下文相关文法,也叫1型文法。 图灵机的范围比它们都大,它对应0型文法。你任何能用产生式写出来的文法规则,都属于0型文法。 希望对你有帮助,了解有限自动机和图灵机的关系:)
共 2 条评论58 - 易林林2019-08-17宫老师,例子里面的词法分析大多是靠条件判断来实现,如果对一门完整的语言来进行分析的话,工作量会不会很大。我在想,是否有其他方式可以实现?
作者回复: 课程的示例代码的主要目的是把意思讲明白,我甚至把状态都用枚举表示,就是为了易读。性能不是第一考虑。 从性能的角度,词法分析可以用查表的方法实现状态迁移。在每个状态,接收什么字符,切换到另外的状态。那样更快,这是常用的方法。 不光词法分析可以这么做,语法分析也可以。基于表驱动。这时候,最重要的是构造那张表。代码的话,就不大看明白是啥意思。
48 - Fan2019-08-16宫老师,有没有一些词法分析的demo可以推荐看看呢?
作者回复: 最好的demo,就是现有语言的词法分析器,比如Java的、GNU c的,都能拿到源代码。比如Java的编译器在JDK的源代码里就有。 此外,我们在后端时会讲到LLVM工具。LLVM的文档里有一个小的教程,做了一个完整的前端。你也可以参考一下。http://llvm.org/docs/tutorial/MyFirstLanguageFrontend/LangImpl01.html 回头我整理一份清单放到github上,告诉大家去哪里下载。你的需求估计其他同学也有。 谢谢你!
共 2 条评论31 - 逗逼师父2019-08-17老师您好,我的疑问是,age>=45的有限状态机图中,为什么比较操作符不像标识符那样停留在同一个状态?我觉得>和>=都是属于比较操作符呀
作者回复: 很好的问题。 是这样的。从Token分类的角度,我们确实可以把这两个归为一类。 但如果把它们看做同一个状态,就会有一些问题。比如,接收到=号应该怎么处理呢?接收第一个=号,仍然处于比较操作符状态。那么接收第二个呢?问题是,有限状态机接收字符的时候,是没法数个数的。如果你要记个数,也就相当于在内部新增加了一个状态,还是等价于两个状态。我这么说你理解吗?
共 5 条评论22 - 诺亚2019-08-25isAlpha 的 alpha 好像没有字母的意思吧?用 alphabet 会不会比较合适点?
作者回复: 你很细心,所以我也仔细给你解答下:-) isAlpha是 is alphabetic 的缩写。isalpha()函数是好几个语言的标准库里都提供的,比如C、python等。 alphabet指的是整个字母表,不是字母表里的单个字母。
共 2 条评论19 - 宋健2020-03-24老师,我写完这一节激动的浑身发抖,自己果然实现了一个简单的词法分析器!老师讲得太棒了!
作者回复: 主要是你自己的功劳:) 在技术领域,有时候你会觉得某个领域高山仰止,其实你自己也可以成为高山上的一棵青松。知识这东西,就在那里,只要想学,没有可能学不会。一旦学会,没有可能再变得不会,是个只会增加的过程,这是多便宜的事情! 不过,学习过程中,肯定还是会遇到挫折的,会觉得难懂,会觉得坚持不下去。这也没关系。你吃的苦越多,进入的境界就越高,这都是值得的!
18 - 请叫我赓哥2019-08-28您好,老师,初步接触编译原理,您讲的手工打造词法分析,是用已知的java或c或其他语言实现,我想问一下,c语言的编译器是用什么实现的呢,或者其他语言的编译器?另外其他的词法分析器又是用什么语言编写的呢,谢谢
作者回复: 在编译领域,有一个事情,叫做自举(bootstraping),也就是这门语言的编译器可以用自己这门语言编写。这是语言迈向成熟的标志。一般前面的版本,是要借助别的语言编写编译器,但后面就应该用自己的语言来编译了。 著名的语言都实现了自举。比如,go语言的编译器是用go编写的(早期版本应该是用C语言写的编译器。能实现自举,还是go发展历程上的一个历程碑),jdk里面自带了java语言的编译器,本身也是用java写的。 更早的语言,那当然是用汇编写编译器。比尔盖茨当年就是用汇编写。 掌握编译原理之后,其实用什么语言都可以写。 这门课程的示例语言是playscript。我有计划后面把playscript实现自举。
共 2 条评论15 - 梓航(﹏)2019-08-17我是来提意见的,麻烦老师在讲示例的时候,把对应的github链接贴上,而不是在最后贴一个总的地址,我点进去一脸懵,哪个文件对应哪个例子啊?
作者回复: 好的,谢谢您提意见!已通知后台调整一下。 02课的文件是目录中的SimpleLexer.java文件。 另,如果到github的https://github.com/RichardGong/PlayWithCompiler项目主页,里面有每堂课的课件的介绍,供您参考。
15 - devna2019-08-16之前做的一个项目中有大量的历史遗留脚本是用Perl写的,于是主动去学了下Perl,不得不说,Perl是我见过所有语言里对正则表达式支持最强的,效率也是最高的(我想可能是因为Perl在语言核心内置了正则表达式引擎,不像其他语言,是通过各种模块支持的)。 后来从Perl了解到《精通正则表达式》这本书,买来看了下,确实是很好的一本书。虽然读完很久很多东西都忘了,但是对正则表达式的理解深刻了很多。
作者回复: great! 基于你已有的积累,是否可以进一步想想,能否自己写一个支持正则的工具?比如像grep、sed这些超级命令一样。那几个命令被认为是神级的命令,就是因为支持正则表达式,让它们能适应各种情况。
共 2 条评论14 - (╯‵□′)╯︵┻━...2019-10-03有回随手发现Google搜索可以使用正则表达式……然后感觉星星都亮了
作者回复: 嗯。等你学了算法篇第16讲,了解了正则表达式的机制后,可以设计点正则表达式测试一下谷歌的性能,看看能否把谷歌的服务器累趴下...
14 - xindoo2019-08-16正则表达式匹配3的任意倍数 https://www.zhihu.com/question/24824487
作者回复: 哇,真好玩! 点赞!
10 - 阿尔伯特2019-08-31Mark. https://github.com/albertabc/compiler/blob/master/main.cpp
作者回复: 下载,编译,并运行了。 你提供的cmake配置很赞,让编译很顺畅! 也看出你C++功底很扎实呀! 再次点赞!
共 2 条评论8 - 斐波那契2019-08-25老师 这个有限自动机的状态确认是不是以空格为分界点?比如 int age = 45 在读i时进入int1状态 然后读n进入int2状态然后读t进入int3状态然后读到一个空格结束此时是在关键字状态 所以说int是一个关键字 然后在重新初始化状态开始读取age直到读到;为止?
作者回复: 不完全是。 1. int和age之间是以空格分割。更准确的说,是以“即非字母又非数字的任何字符”作为分割点。因为只要是字母或数字,就会一直停留在标识符的状态里。 2. 等号前后可以不必有空格,算法仍然能正确的切分开。 这样说,能明白吗?
8 - 哆啦B梦จุ๊บ2019-11-20听了老师的讲解,感觉没有想象的那么难,不管好坏,先敲一版出来总是没错的: https://github.com/zyfandtmx/compile/blob/master/src/main/java/mycompiler/lexical/LexicalAnalysis.java
作者回复: 看到了,不错! 破山中贼易,破心中贼难。 任何高峰,都是能找到一条缓坡上去的。
4 - sheeeeep2019-08-16ts的实现,请老师和同学指正。https://github.com/sheeeeep/fundamentals-of-compiling/blob/Ch02/src/lexical-analyser.ts
作者回复: 非常好!要号召大家跟你学习! 太棒了! 你有精力的话,还可以再精进一下,参考一下成熟编译器的词法分析工具,从课程示例的代码的基础上再提升一个等级:) 比如说,另一个同学提到过,如何提升词法分析的性能什么的。
共 4 条评论4 - 冬瓜2019-08-16int 后面的 id 的正则是不是 [a-zA-Z_][0-9a-zA-Z_]*这样就行,为啥要将数字通过|连接呢?是不是有什么用意😳
作者回复: 就是个习惯而已,把数字和字符分两组。 我们在写正式的词法文件时,有时会这么写: Id: Charactor (Charactor | Digit)* Number: Digit+ Charactor: [a-zA-Z_] Digit: [0-9] 这时候,Digit在几个不同的规则中是复用的。
共 4 条评论4 - 鱼_XueTr2019-08-16正则表达式在做爬虫和文本处理中用过比较多。
作者回复: 是滴。 程序编译的第一阶段工作,本质也是文本处理。
4 - 许童童2019-08-16正则表达式在实现中一般有两种:NFA和DFA,一般语言用DFA,但会有回溯的性能问题,用的时候一定要注意。
作者回复: 如果要实现一个通用的正则表达式工具,这时候没办法手工构造DFA,会遇到你说的情况。 如果往深看一步,词法分析和我们后面讲的自顶向下的语法分析有共同之处,所以都会有回溯的可能性。 一般语言的词法,都不会太复杂,并且可以手工构造DFA,所以也就可以尽量避免回溯了。
3 - 恩佐2019-11-01https://github.com/shaojintian/learn_compiler 老师能看看我的lexer么?用golang写的 请老师和同学们指正,感谢
作者回复: 看了你的代码,挺不错的。如果再补充一下README,让人可以迅速build和测试你的项目就更好了。
共 2 条评论2