原标题|向PEG语法作者添加操作|GuidovanRossum(Python之父)译者|作者)声明|本翻译以交流学习为目的,基于CCBY-NC-SA4.0许可协议。为便于阅读,内容略有修改。如果您还可以向语法规则添加(一些)语义,那么语法会更好。特别是对于我正在构建的Python解析器,我需要控制为每个备选方案返回哪些AST节点,因为指定了AST的格式。[这是我的PEG系列的第6部分。其余请参考系列概述】(译注:本系列翻译已开源在Github,项目地址:https://github.com/chinesehuazhou/guido_blog_translation)很多语法都有约定,支持添加动作到规则,通常是{花括号括起来的一段代码}。更准确地说,行动与备选方案相关联。动作块中的代码通常与编写编译器的语言(例如C)相同,只是增加了在备选方案中引用条目的功能。在Python最初的pgen中,我没有添加这个功能,但是对于这个新项目,我想使用它。对于本系列博文中开发的简化解析器生成器,我们是这样做的。通常,操作的语法如下:rule:itemitemitem{action1}|itemitem{action2}因为它会使语法冗长,解析器生成器通常支持跨行拆分规则,例如:rule:itemitemitem{action1}|itemitem{action2}它会使解析器复杂化,但可读性更重要,所以我会使用这种方式。一个永恒的问题是何时执行动作块。在Yacc/Bison中,由于没有回溯,只要解析器识别出规则,就会执行操作块。每个动作都是立即执行的,这意味着即使该操作具有全局副作用,它仍然会顺利执行(例如更新符号表或其他编译器数据结构)。在PEG解析器中,由于无限回溯,我们有另一个选择:延迟所有操作,直到所有内容都被解析。这对我的目的没有用,因为我想在解析期间构造一个AST。只要识别到动作对应的alternative,就执行,但是要求操作代码是幂等的(即无论执行多少次,效果都一样)。这意味着可以执行一个动作,但其结果最终将被丢弃。缓存动作的结果,以便仅在第一次在给定位置识别备选方案时执行相应的动作。我选择第三个选项——就像我们用packrat算法缓存东西一样,我们也可以缓存操作的结果。关于{braces}里面的内容,约定俗成是用C语言,用$符号来指代可识别的备选项(例如$1指代第一项),赋值给$$表示结果行动。这对我来说似乎很陈旧(我记得在Algol-60中使用对函数名称的赋值来指定返回值),所以我会做一些更Pythonic的事情:在括号内,你需要放置一个表达式,其值为操作的值和项目引用是给出项目文本的简单名称。例如,这是一个简单的加减法计算器:start:exprNEWLINE{expr}expr:expr'+'term{expr+term}|expr'-'term{expr-term}|term{term}term:NUMBER{float(number.string)}当我们运行时,给定输入100+50-38-70,它会识别零件并计算答案,计算为((100+50)-38)-70,结果当然是42。一个小细节:在术语action中,变量number包含一个TokenInfo对象,因此该action必须使用其.string属性来获取字符串形式的标识符。同一个规则名称在alternative中多次出现怎么办?对于出现在同一备选方案中的规则,解析器生成器通过向后续规则添加1、2等来赋予唯一名称。例如:factor:atom'**'atom{atom**atom1}|atom{atom}它的实现很无聊,所以我请你查看代码并亲自看看。试试这个:python3.8-mstory5.driverstory5/calc.txt-gstory5.calc。CalcParser可视化现在支持使用左右箭头键来回移动!本文内容及示例代码授权协议:CCBY-NC-SA4.0公众号【Python猫】,本号连载一系列优质文章,包括喵星哲学猫系列,Python进阶系列,不错图书推荐系列、技术写作、优质英文推荐及翻译等,欢迎关注。
