在核心开发者Sprint上进行PEG工作

在本文中,我将不讨论解析器生成器的新功能-在前面的部分中已经对它进行了足够的描述。 相反,我想告诉您上周我在Core Developer Sprint所做的事情,然后从内存中删除所有内容。 尽管大多数材料都与PEG相关。 因此,我必须展示一些代码来设置实现Python 3.9的PEG解析器的方向。



在过去的四年中,每年Python核心开发团队都会在异国情调的地方每周进行一次冲刺。 这些冲刺是由主办方和PSF赞助的。 在最初的两年中,我们访问了Mountain View的Facebook,去年Microsoft在Bellevue设立了生产线,并选择了伦敦的彭博社(Bloomberg)担任本次冲刺。 (我不得不说它看起来很酷。)核心开发人员Pablo Galindo Salgado的组织工作感到荣耀!


这次有30多个开发人员以及两个Padawans聚集了我们。 人们从事各种工作:从3.8阻止程序到新的PEP。 我希望PSF博客介绍我们的成就。 要点之一是未完成的PR数量少于1000,有100多个PR正在等待审核。 甚至还有一场与排行榜的比赛-前10名参与者上演了更多的其他人的PR。


对我而言,参加此类活动的主要原因始终是与我全年与之在线合作但每年仅见过一两次的人会面。 此冲刺发生在欧洲,因此我们看到了稍微不同的组成,这一点尤其重要。 尽管如此,我也会非常有成效地工作,我将在后面进行讨论。


在sprint的大部分时间里,我与Pablo和Emily Morhouse一起使用了基于PEG的解析器生成器,我希望有一天能取代当前的基于pgen的解析器生成器。 这与我写的生成器不同,但是非常相似。 Pablo已经为此版本做出了贡献。


冲刺的第一天,星期一,我主要研究本周期的第7条和第8条。 最初,我计划将它们一起发布,但是直到一天结束时都没有时间。 因此,他将其分为两个部分,并发表了专门用于创建元图的上半部分。 在星期五的下午,我终于找到了一些时间来完成第8部分。但是,由于仍然没有一个很好的例子,我仍然不得不省略剪辑故事。


在星期二,我开始研究Python的PEG语法。 在添加操作之前,它离代码比与抽象规范更近。 我们知道我们需要检查有关真实Python代码的开发语法。 因此,当我完成语法时,Emily正在编写用于批处理测试的脚本。 之后,我的工作流程如下:


  1. 运行脚本以使用Python代码测试某些目录
  2. 调查他跌倒的第一个问题
  3. 纠正语法以解决此问题
  4. 重复直到没有问题
  5. 转到下一个目录

我从我自己的pgen项目代码开始。 最后,我的语法能够分析pgen中使用的所有Python构造,然后我进入了标准库模块。 首先,关注Lib/test ,然后关注Lib/asyncio最后关注Lib ,即实际上是整个标准库(至少是用Python编写的标准库)。 到本周末,我已经能够庆祝:新分析器落入标准库中的唯一文件是编码错误的文件。 它们仅作为测试数据存在,以验证将以正确的方式处理编码问题。 以及一些Python 2文件,它们是lib2to3的测试用例


然后,我在Emily的脚本中添加了一些代码来计算解析器的运行时,看起来新的PEG解析器比旧的pgen解析器要快一点。 这并不意味着事情会更加艰难! 语法中有100多个规则(160行),并且要使其生成AST,我们需要为每个规则添加一个操作(请参阅第6部分)。


之前,我进行了一些实验,以查看添加操作后文件大小会增加多少。 我得出的结论是它将变大2-3倍,这是该实验的语法:


 start[mod_ty]: a=stmt* ENDMARKER{ Module(a, NULL, p->arena) } stmt[stmt_ty]: compound_stmt | simple_stmt compound_stmt[stmt_ty]: pass_stmt | if_stmt pass_stmt[stmt_ty]: a='pass' NEWLINE { _Py_Pass(EXTRA(a, a)) } if_stmt[stmt_ty]: | 'if' c=expr ':' t=suite e=[else_clause] { _Py_If(c, t, e, EXTRA(c, c)) } else_clause[asdl_seq*]: | 'elif' c=expr ':' t=suite e=[else_clause] { singleton_seq(p, _Py_If(c, t, e, EXTRA(c, c))) } | 'else' ':' s=suite { s } suite[asdl_seq*]: | a=simple_stmt { singleton_seq(p, a) } | NEWLINE INDENT b=stmt+ DEDENT { b } simple_stmt[stmt_ty]: a=expr_stmt NEWLINE { a } expr_stmt[stmt_ty]: a=expr { _Py_Expr(a, EXTRA(a, a)) } expr[expr_ty]: | l=expr '+' r=term { _Py_BinOp(l, Add, r, EXTRA(l, r)) } | l=expr '-' r=term { _Py_BinOp(l, Sub, r, EXTRA(l, r)) } | term term[expr_ty]: | l=term '*' r=factor { _Py_BinOp(l, Mult, r, EXTRA(l, r)) } | l=term '/' r=factor { _Py_BinOp(l, Div, r, EXTRA(l, r)) } | factor factor[expr_ty]: | l=primary '**' r=factor { _Py_BinOp(l, Pow, r, EXTRA(l, r)) } | primary primary[expr_ty]: | f=primary '(' e=expr ')' { _Py_Call(f, singleton_seq(p, e), NULL, EXTRA(f, e)) } | atom atom[expr_ty]: | '(' e=expr ')' { e } | NAME | NUMBER | STRING 

这里有很多事情我需要解释。


  • 这些动作是用C语言编写的。就像第6部分中的Python生成器一样,每个动作都是一个表达式。
  • 规则名称后方括号内的文本确定相应规则方法的结果类型。 例如, atom[expr_ty]表示将为atom返回expr_ty 。 如果查看CPython存储库中的Include/Python-ast.h ,您将看到此类型是用于表示内部AST中的表达式的结构
  • 如果替代方案仅包含一个元素,则可以省略该操作,因为其默认行为只是返回返回的AST节点。 否则,必须明确指定该动作。
  • 生成的C代码也需要一些处理。 例如,假设将包含一些CPython头文件。 例如,在expr_ty类型expr_ty地方,以及其他许多必要的事情。
  • 变量p包含一个指向生成的Parser使用的Parser结构的指针。 (是的,这意味着最好不要在p规则中指定单个元素,否则生成的C代码将无法编译!)
  • EXTRA(node1, node2)是一个宏,它扩展为一组必须传递给每个AST构造函数的附加参数。 这样在编写动作时可以节省大量时间-否则,您将必须指定开始和结束行号,列偏移量,以及指向用于分发的舞台的指针。 (AST节点不是Python对象,并使用更有效的布局。)
  • 由于EXTRA() C预处理器的某些有趣行为,我们不能使用宏来创建AST节点,而必须使用基本功能。 这就是为什么您看到例如_Py_Binop(...)而不是Binop(...) 。 将来,我会考虑如何以不同的方式解决它。
  • 对于重复元素( foo*foo+ ),代码生成器创建asdl_seq*类型的辅助规则。 这是AST用于表示重复的数据结构。 在几个地方,我们只需要从一个元素创建这样的重复,为此我们定义了辅助函数singleton_seq()

也许其中有些听起来很奇怪,我不会争论。 这是一个原型,其主要目的是证明从原理上讲,可以使用从PEG语法生成的解析器生成有效的AST。 所有这些工作无需对现有标记器或字节码编译器进行任何更改。 原型可以编译简单的表达式和if ,并且可以将生成的AST编译为字节码并执行。


作为此冲刺的一部分,我做的其他事情:


  • 我说服Lukasz Lang更改了PEP 585 (他对将来的类型提示的建议),将重点放在泛型上,而不是像以前那样集中在一组想法上。 新的PEP看起来好得多,在几天前的打字会议上 ,出席了Python类型检查实用程序开发人员(mypy,pytype和Pyre)的代表,他获得了普遍认可。 (这与理事会的认可不同!)
  • 帮助Yuri Selivanov开发了Frozenmap类型的API,他希望将其添加到stdlib。 其他一些贡献者也为设计做出了贡献-我认为我们完成了sprint,其中包含一些包含示例和API片段的板。 结果是PEP 603 ,目前正在积极讨论中 。 (注意:在CPython中,作为PEP 567contextvars模块)的实现的一部分,已存在提议的数据类型的实现。这是一个非常有趣的数据结构,即Hash Array Mapped Trie ,它结合了哈希表和前缀树。)
  • 尤里一如既往地充满想法。 他还致力于异常组(来自Trio的想法),他想在Python 3.9中的异步中实现。 我上次查看时似乎没有PEP,但我确实记得有一张充满图表的电路板。
  • 我们一直在积极讨论Lucas关于缩短Python发布周期的建议。 这导致了PEP 602 ,他建议在10月每年进行一次。 (这样做有一个很好的理由:这是由于Python会议和核心sprint的典型时间表所引起的。)仍然对此进行了很多讨论 。 至少有两个对价:在PEP 598中,尼克·科格兰 Nick Coglan)提供为期两年的发行版,同时允许修补程序中的新功能。 史蒂夫·道尔(Steve Dower)也希望每两年发布一次,但没有此功能(尚无PEP)。
  • 参加冲刺的三位理事会成员(Brett Cannon,Carol Willing和我)聚集在一起,讨论了我们对Python内核未来发展的愿景。 (我不想谈论太多,因为我们计划在美国的下一个PyCon上谈论它。但是,我们可能会建议开始筹款,以便PSF可以聘请多个开发人员来支持和加速内核开发)。
  • 我与参加仪式的人之一乔安娜·南耶基(Joanna Nanjeki)进行了有趣的午餐会。 她讲了一个故事,讲述了她8岁时如何了解互联网,并在母亲工作时将弟弟带到网吧。 她在那里找到了Google和电子邮件,并在第一周挂在那里。
  • 本周的主要酒精事件是一些Zombie Apocalypse鸡尾酒,我们中的一些人在Alchemist酒吧订购。 装在2升锥形瓶中,该瓶冒着大量假烟,这些假烟是由将干酒精倒入普通酒精混合物中而产生的,每杯饮料可容纳4人。
  • 星期五晚上,丽莎·罗奇(Lisa Roach)带我们去了她酒店附近的一家不错的印度餐厅。 这是通过四个地铁站进行的,这是一次真正的冒险(那是高峰时间,我们几乎几次失去了克里斯蒂安·海姆斯)。 食物值得!
  • 在某个时候,我们合影了。 它看起来很未来,但这确实是伦敦的风景。


在下一篇文章中,我希望与创建AST节点的操作分享一些进展。


本文和引用代码的许可: CC BY-NC-SA 4.0

Source: https://habr.com/ru/post/zh-CN471994/


All Articles