CPython的PEG解析器生成器
项目描述
这是什么?
PEgen是CPython中用于生成解释器所使用的解析器的解析器生成器。它允许从形式语法的描述中生成PEG解析器。
安装
使用pip
或您喜欢的PyPi包管理器进行安装。
pip install pegen
文档
文档可在此处找到。
如何生成解析器
给定与pegen
兼容的语法文件(您可以自己编写或从data
目录中的一个开始),您可以通过运行以下命令轻松生成解析器:
python -m pegen <path-to-grammar-file> -o parser.py
这将生成当前目录下名为parser.py
的文件。这可以用于使用我们刚才使用的语法解析代码
python parser.py <file-with-code-to-parse>
作为演示:从data/python.gram生成Python解析器,并使用生成的解析器解析和运行tests/demo.py
make demo
如何贡献
请参阅CONTRIBUTING.md文件中的说明。
与CPython的Pegen的差异
此存储库存在是为了分发一个可以通过PyPI安装的CPython使用的Python PEG解析器生成器版本,并进行了一些改进。尽管CPython中包含的官方PEG生成器可以生成Python和C代码,但此生成器分布只允许生成Python代码。这是由于CPython中包含的生成器生成的C代码包含了大量的实现细节和私有头文件,这些文件对于一般使用不可用。
Python 3.9及以后的官方PEG生成器现在包含在CPython存储库的Tools/peg_generator/下。我们旨在使此存储库与该版本的pegen
中的Python生成器保持同步。
另请参阅PEP 617。
存储库结构
- 《src》目录包含《pegen》源代码(包本身)。
- 《tests》目录包含《pegen》包的测试套件。
- 《data》目录包含一些与《pegen》兼容的示例语法。这包括Python语法的纯Python版本。
- 《docs》目录包含包的文档。
- 《scripts》目录包含一些有用的脚本,可用于可视化语法、基准测试和其他与生成器本身开发相关的用法。
- 《stories》目录包含Guido关于PEG解析器的系列文章的支持文件和示例。
快速语法概述
语法由一系列形式为的规则组成
rule_name: expression
可选地,规则名后面可以包含一个类型,它指定了与规则对应的Python函数的返回类型
rule_name[return_type]: expression
如果省略了返回类型,则返回Any
。
语法表达式
# 注释
Python样式的注释。
e1 e2
匹配e1,然后匹配e2。
rule_name: first_rule second_rule
e1 | e2
匹配e1或e2。
第一个备选方案也可以出现在规则名之后的行中,用于格式化。在这种情况下,第一个备选方案之前必须使用|,如下所示
rule_name[return_type]:
| first_alt
| second_alt
( e )
匹配e。
rule_name: (e)
一个稍微复杂且有用的例子包括使用分组运算符与重复运算符一起使用
rule_name: (e1 e2)*
[ e ]或e?
可选地匹配e。
rule_name: [e]
一个更有用的例子包括定义尾随逗号是可选的
rule_name: e (',' e)* [',']
e*
匹配零个或多个e的出现。
rule_name: (e1 e2)*
e+
匹配一个或多个e的出现。
rule_name: (e1 e2)+
s.e+
匹配一个或多个e的出现,由s分隔。生成的解析树不包含分隔符。这与(e (s e)*)
相同。
rule_name: ','.e+
&e
如果e可以被解析,则成功,而不消耗任何输入。
!e
如果e可以被解析,则失败,而不消耗任何输入。
从Python语法中取出的一个例子指定,一个主表达式由一个原子组成,后面不跟一个.
或一个(
或一个[
primary: atom !'.' !'(' !'['
~
提交到当前备选方案,即使它解析失败。
rule_name: '(' ~ some_rule ')' | some_alt
在这个例子中,如果解析了左括号,那么其他备选方案将不会考虑,即使some_rule或‘)’解析失败。
左递归
PEG解析器通常不支持左递归,但Pegen实现了一种技术,使用记忆缓存来允许左递归。这使我们能够编写不仅简单的左递归规则,还可以编写更复杂的规则,例如涉及间接左递归的规则
rule1: rule2 | 'a'
rule2: rule3 | 'b'
rule3: rule1 | 'c'
和“隐藏左递归”如:
rule: 'optional'? rule '@' some_other_rule
语法中的变量
可以通过在子表达式前加上一个标识符和一个=
符号来命名子表达式。然后可以在动作(见下文)中使用该名称::
rule_name[return_type]: '(' a=some_other_rule ')' { a }
语法动作
为了避免中间步骤,这些步骤模糊了语法和AST生成之间的关系,PEG解析器允许通过语法动作直接为规则生成AST节点。语法动作是特定于语言的表达式,当语法规则成功解析时将被评估。这些表达式可以用Python编写。作为具有Python动作的语法的例子,解析语法文件的解析器生成器是从具有Python动作的元语法文件启动的,这些动作生成语法树作为解析的结果。
在Python PEG语法的特定情况下,具有动作允许直接在语法中描述AST的组成,使其更清晰和易于维护。这个过程是通过使用一些辅助函数来支持的,这些函数将常见的AST对象操作和一些与语法不直接相关的其他所需操作抽象出来。
要指示这些动作,每个备选方案后面可以跟大括号内的动作代码,它指定了备选方案的返回值
rule_name[return_type]:
| first_alt1 first_alt2 { first_alt1 }
| second_alt1 second_alt2 { second_alt1 }
如果省略了动作,将生成默认动作
-
如果规则中只有一个名称,则返回该名称。
-
如果规则中有多个名称,则返回包含所有解析表达式的集合。
这种默认行为主要用于非常简单的情况和调试目的。
作为一个示例,这个简单的语法文件允许直接生成一个完整的解析器,该解析器可以解析简单的算术表达式,并返回有效的Python AST
start[ast.Module]: a=expr_stmt* ENDMARKER { ast.Module(body=a or []) }
expr_stmt: a=expr NEWLINE { ast.Expr(value=a, EXTRA) }
expr:
| l=expr '+' r=term { ast.BinOp(left=l, op=ast.Add(), right=r, EXTRA) }
| l=expr '-' r=term { ast.BinOp(left=l, op=ast.Sub(), right=r, EXTRA) }
| term
term:
| l=term '*' r=factor { ast.BinOp(left=l, op=ast.Mult(), right=r, EXTRA) }
| l=term '/' r=factor { ast.BinOp(left=l, op=ast.Div(), right=r, EXTRA) }
| factor
factor:
| '(' e=expr ')' { e }
| atom
atom:
| NAME
| NUMBER
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。