原题 | Adding Actions to a PEG Grammarpython
做者 | Guido van Rossum(Python之父)git
译者 | 豌豆花下猫(“Python猫”公众号做者)github
声明 | 本翻译是出于交流学习的目的,基于 CC BY-NC-SA 4.0 受权协议。为便于阅读,内容略有改动。算法
若是你在语法规则中还能够添加(某些)语义,那么语法就会更好。特别是对于我正在构建的 Python 解析器,我须要控制每一个备选项返回的 AST 节点,由于 AST 的格式已经规定好。缓存
【这是个人 PEG 系列的第 6 部分。其他部分请参阅系列概述 】(译注:本系列的译文已在 Github 开源,项目地址:github.com/chinesehuaz…)数据结构
许多语法都有支持给规则添加动做的约定,一般是 { 花括号 } 内的一段代码块。更确切地说,行动与备选项相关联。动做块中的代码一般与编写编译器的语言相同,如 C 语言,增长一些工具,用于引用备选项中的条目。在 Python 原始的 pgen 中,我没有添加此功能,但对于这个新项目,我但愿使用它。
函数
对于在这一系列博客文章中开发的简化版解析器生成器,下面是咱们采用的作法。工具
通常而言,动做的语法以下:学习
rule: item item item { action 1 } | item item { action 2 }复制代码
由于它会使语法变得冗长,因此解析器生成器一般支持跨行分割规则,例如:ui
rule: item item item { action 1 }
| item item { action 2}复制代码
它会使语法分析器变得复杂,但可读性更重要,因此我会使用这种方式。
一个永恒的问题是什么时候执行动做块。在 Yacc / Bison 中,由于没有回溯,一旦规则被解析器识别到,就会执行动做块。每一个动做会当即执行,这意味着即便操做具备全局反作用,仍是会顺利执行(例如更新符号表或其它编译器数据结构)。
在 PEG 解析器中,由于有无限回溯,咱们有其它的选择:
我要采用第三个选项——正好咱们用 packrat 算法缓存东西,因此咱们也能够缓存动做的结果。
关于 {花括号} 里面的内容,传统上是使用 C 语言,它约定用 $
符号来引用已识别的备选项(例如,$1
引用第一个条目),并赋值给 $$
以指示动做的结果。
在我看来这太老古董了(我记得曾在 Algol-60 中使用对函数名的赋值,来指定返回值),因此我会用一些更 Pythonic 的方式:在括号内,你须要放置一个单一的表达式,它的值是动做的值,而条目的引用则是一些简单的名称,给出着条目的文本。
举个例子,这是一个简单的计算器,能够做加减法:
start: expr NEWLINE { 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。
一个小细节:在term
的动做中,变量number
保存了一个TokenInfo
对象,所以该动做必须使用其.string
属性来获取字符串形式的标识符。
当一个备选项中屡次出现相同的规则名称时,咱们该怎么办?对同一备选项中出现的规则,解析器生成器会给出惟一的名称,即在随后出现的规则上添加 一、2 等等。例如:
factor: atom '**' atom { atom ** atom1 }
| atom { atom }复制代码
它的实现很无聊,因此我请大家 check out 代码 ,本身看看。试试这个:
python3.8 -m story5.driver story5/calc.txt -g story5.calc.CalcParser复制代码
可视化功能如今支持使用左右箭头键来回移动!
本文内容与示例代码的受权协议:CC BY-NC-SA 4.0
公众号【Python猫】, 本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写做、优质英文推荐与翻译等等,欢迎关注哦。