当前位置: 首页 > Linux

Python之父写过一篇文章:你为什么要创建pgen解析器?

时间:2023-04-06 07:13:36 Linux

花猫语:最近Python之父在Medium上开了博客,发表了一篇关于PEG解析器的文章(全文见我翻译)。据我所知,他有自己的博客,为什么要在Medium上写呢?出于好奇,我打开了他的旧博客。上一篇文章写于2018年5月,巧合的是写了pgen解析器,也就是他在新文中狠狠吐槽说要换掉的pgen。在这篇旧文中,Guido回忆了他创建pgen时的一些考虑。当时,创建一个新的解析器无疑是明智的,但时代变了,现在有了更好的选择。前不久,我们讲了Python中的GIL去除计划,内置电池的“手术”计划,以及print的进化故事。现在,它的解析器也在转型。Python是一门将近30岁的语言,它仍然生机勃勃。让我们一起祝福它,祝愿它的未来更加美好。本文首发于公众号【蟒猫】,未经授权请勿转载。原文地址:https://mp.weixin.qq.com/s/ov...原文标题|pgen的起源GuidovanRossum(Python之父)译者|公众号作者)原文|https://python-history.blogspot.com/2018/05/the-origins-of-pgen.html声明|转载仅供交流学习,欢迎转载,但请保留此文请勿用于商业或非法用途。DavidBeazley在美国PyCon2018上关于解析器生成器的演讲提醒我应该写下它的历史。这是一个简短的脑洞(也许我以后会解释)。(译注:让我大胆推测一下“脑残粉”。应该说,将个人记忆和Python的历史细节转化为文本,是一个存储和固化的过程,便于继承。而我做的翻译工作,它就是为了把这份丰富的文档传播给更多的Python爱好者。)其实pgen有两个,一个是原来的,用C语言写的,一个是用Python重写的,在lib2to3/pgen2下面。两个都是我写的。最早的其实是我为Python写的第一段代码。尽管从技术上讲我必须先编写词法分析器(pgen和Python共享词法分析器,但pgen不适用于大多数标记)。我想编写自己的解析器生成器的原因是当时有相当多的这些(我很熟悉)-基本上是Yacc(有一个GNU重写称为Bison,但我不确定我当时是否知道);或者自己写一个(这是大多数人所做的)。大学时用过Yacc,从《龙书》开始熟悉它的工作原理,但不知为何不太喜欢;IIRC关于LALR(1)语法的局限性,我很难解释清楚。(译注:1.龙书,原文为龙书,指的是《Compilers: Principles, Techniques, and Tools》。这是一本关于汇编原理的书,在汇编原理领域属于殿堂级存在。另外还有两个经典作品,书名是《虎书》、《鲸书》,这三者经常一起出现。2、IIRC,如果我没记错的话,如果我没记错的话。)我也熟悉LL(1)解析器,和已经写了一些递归下降LL(1)解析器——我非常喜欢它,而且我也熟悉LL(1)解析器的生成技术(同样,因为龙书),所以我有一个想法改进它并想尝试一下:使用正则表达式(某种程度上)而不是标准的BNF格式。龙书还教会了我如何将正则表达式转换为DFA,所以一旦我把这些东西结合起来,pgen就诞生了。[更新:请参阅下文,了解此基本原理的一个略有不同的版本。]我不熟悉更高级的技术,或者认为它们效率太低。(当时,我认为大多数从事解析器工作的人都是这样。)至于词法分析器,我决定不使用生成器——我对Lex的评价远低于Yacc,因为在Lex的版本中我很熟悉尝试扫描长于255字节的标记时会出现段错误(真的!)。另外,我认为缩进格式很难教给词法分析器生成器。(译注:1、这里的生成器不是Python语法中的生成器,而是用来生成分析器的工具。Lex是“LEXicalcompiler”的缩写,用来生成词法分析器;Yacc是“YetanotherCompiler“编译器”用于生成解析器。2、Segmentationfault,原文为segfault,全称segmentationfault,指的是由于越界访问内存空间而报错。)pgen2是一个完全不同的故事。我受雇于SanMateo的一家初创公司(即ElementalSecurity,该公司于2007年关闭,之后我离开加入了Google),在那里我的任务是设计一种自定义语言(目标是做出有关系统配置的安全决策),并具有相当大的自主权。我决定设计一些稍微Pythonic的东西,用Python实现,并决定重用pgen,但使用基于Python的后端,使用tokenize.py作为词法分析器。所以我用Python在pgen中重写了这些算法,然后继续构建其余的。管理层认为开放该工具的源代码很有意义,因此他们很快批准了它,不久之后(我可能那时已经转到谷歌了?),它对2to3也有意义。(因为输入格式与原始pgen相同,所以很容易使用它来生成Python解析器-我只是将语法文件提供给工具。:-)更新:创建pgen的原因,以及我不知道的更多故事我不太记得为什么这样做了,但我只是浏览了https://en.wikipedia.org/wiki…我可能认为这是一个新的(对我来说)规则,它通过添加解决冲突的方式没有帮助。例如,本页所说的左分解(将A->X|XYZ替换为A->XB;B->YZ|),我会重写为A->X[YZ]。如果我没记错的话,通过“正则表达式->NFA->DFA”的转换过程,分析引擎(本网页之前的syntacticAnalysis函数)仍然可以在这些规则导出的分析表上工作;我觉得这里需要有无空白产品的需求。(译注:“空白产品”,原文是emptyproductions,对应上一篇文章中的,意思是empty不是必需的。)我还记得解析引擎生成的解析树节点可能有很多子节点,例如对于上面的规则A->X[YZ],节点A可能有1个子节点(X)或者3个(XYZ)。需要在代码生成器中进行简单检查以确定它遇到了哪些可能的情况。(这已被证明是一把双刃剑,我们后来添加了一个由单独的生成器驱动的“解析树->AST”步骤,以简化字节码生成器。)所以我使用正则表达式的原因,很可能是为了使语法更易读:在使用必要的重写来解决冲突后,我发现语法不是那么可读(应该在此处插入《Python 之禅》语句:-),而正则表达式更符合我的看法经典语言的语法(奇怪命名的帮助规则、[可选]部分和重复的*除外)。正则表达式不会提高LL(1)的能力,也不会降低它。当然,我想说的所谓“正则表达式”其实就是EBNF——我不确定“EBNF”当时是否是一个定义明确的符号,它可能指的是BNF的任何扩展。将EBNF转换为BNF然后使用它会导致尴尬的多解析树节点问题,所以我认为这不会是一种改进。如果我要重新来过,我可能会选择一个更强大的解析引擎,可能是LALR(1)的某个版本(比如Yacc/Bison)。LALR(1)的某些方面比LL(1)更强大、更有用,例如关键字参数。在LL(1)中,规则“arg:[NAME=]expr”是无效的,因为NAME出现在表达式的第一组(FIRST-set)中,LL(1)算法无法处理这样的写法。如果我没记错的话,LALR(1)可以处理它。然而,关键字参数符号是在我编写pgen的第一个版本多年之后出现的,到那时我已经不想重做解析器了。2019年3月更新:Python3.8将放弃pgen的C版本,转而使用重写的pgen2版本。请参考https://github.com/python/cpy...)公众号【Python猫】,本号连载一系列优质文章,包括喵星哲学猫系列、Python进阶系列、好书推荐系列,技术写作,优质英文推荐及翻译等,欢迎关注。