32.1. parser - 访问Python解析树

parser模块提供了一个到Python的内部解析器和字节码编译器的接口。此接口的主要目的是允许Python代码编辑Python表达式的分析树,并从中创建可执行代码。这比尝试将任意Python代码片段解析和修改为字符串更好,因为解析是以与形成应用程序的代码相同的方式执行的。它也更快。

注意

从Python 2.5开始,使用ast模块在抽象语法树(AST)生成和编译阶段中切入更为方便。

有一些要注意的事情,这个模块对于使用创建的数据结构很重要。这不是编辑Python代码的解析树的教程,但是提供了使用parser模块的一些示例。

最重要的是,需要很好地理解由内部解析器处理的Python语法。有关语言语法的完整信息,请参阅The Python Language Reference解析器本身是从标准Python分发中的文件Grammar/Grammar中定义的语法规范创建的。由此模块创建的ST对象中存储的解析树是由expr()suite()函数创建时内部解析器的实际输出,如下所述。sequence2st()创建的ST对象忠实地模拟这些结构。请注意,被认为“正确”的序列的值将随着Python的一个版本而变化,因为语言的正式语法被修改。但是,将代码从一个Python版本传输到另一个作为源文本将始终允许在目标版本中创建正确的解析树,唯一的限制是迁移到旧版本的解释器将不支持更多的最近的语言结构。解析树通常不是从一个版本到另一个版本是兼容的,而源代码总是向前兼容的。

st2list()st2tuple()返回的序列的每个元素都有一个简单的形式。表示语法中的非终端元素的序列总是具有大于1的长度。第一个元素是标识语法中的生产的整数。这些整数在C头文件Include/graminit.h和Python模块symbol中给出符号名称。序列的每个附加元素表示在输入字符串中识别的生产的组件:这些总是具有与父相同形式的序列。应该注意的这种结构的一个重要方面是用于标识父节点类型的关键字,例如if_stmt中的关键字if被包括在节点树中无需任何特殊处理。例如,if关键字由元组(1, 'if')表示,其中1是与所有NAME令牌相关联的数值,包括用户定义的变量和函数名称。在请求线路号信息时返回的备选形式中,相同的令牌可以表示为(1, 'if', 12) t3>,其中12表示找到终端符号的行号。

终端元素以非常相同的方式表示,但没有任何子元素,并且添加了被识别的源文本。上面的if关键字的示例是有代表性的。各种类型的终端符号在C头文件Include/token.h和Python模块token中定义。

ST对象不需要支持该模块的功能,但是被提供用于三个目的:允许应用分摊处理复杂解析树的成本,以提供解析树表示,其与Python相比节省存储器空间列表或元组表示,并且易于在C中创建操纵解析树的附加模块。可以在Python中创建一个简单的“包装器”类,以隐藏ST对象的使用。

parser模块定义了几个不同目的的函数。最重要的目的是创建ST对象并将ST对象转换为其他表示形式,例如解析树和编译代码对象,但也有用于查询由ST对象表示的解析树类型的函数。

也可以看看

模块symbol
有用的常数表示解析树的内部节点。
模块token
有用的常数表示解析树的叶节点和用于测试节点值的函数。

32.1.1. Creating ST Objects

ST对象可以从源代码或从解析树创建。当从源创建ST对象时,使用不同的函数创建'eval''exec'表单。

parser.expr(source)

expr()函数解析参数source,就像它是compile(source, 'file)的输入。 py', 'eval')如果解析成功,将创建一个ST对象来保存内部解析树表示,否则会产生一个适当的异常。

parser.suite(source)

suite()函数解析参数source,就像它是compile(source, 'file)的输入。 py', 'exec')如果解析成功,将创建一个ST对象来保存内部解析树表示,否则会产生一个适当的异常。

parser.sequence2st(sequence)

此函数接受表示为序列的解析树,并在可能的情况下构建内部表示。如果它可以验证树符合Python语法,并且所有节点都是Python主机版本中的有效节点类型,则从内部表示创建ST对象并将其返回给调用。如果创建内部表示时出现问题,或者无法验证树,则会引发ParserError异常。以这种方式创建的ST对象不应被假定为正确编译;当ST对象传递给compilest()时,编译引发的正常异常可能仍然启动。这可能指示与语法无关的问题(例如MemoryError异常),但也可能是由于诸如解析del f(0),它转义了Python解析器,但被字节码编译器检查。

表示终端令牌的序列可以表示为(1, 'name')形式的两个元素列表或三元素列表形式为(1, 'name', 56)如果存在第三个元素,则假定它是一个有效的行号。可以为输入树中的终端符号的任何子集指定行号。

parser.tuple2st(sequence)

这与sequence2st()具有相同的功能。保留此入口点以实现向后兼容性。

32.1.2. Converting ST Objects

ST对象,无论用于创建它们的输入如何,都可以被转换为表示为列表或元组树的分析树,或者可以被编译为可执行代码对象。可以使用或不使用行编号信息来提取解析树。

parser.st2list(st, line_info=False, col_info=False)

此函数在st中接受来自调用者的ST对象,并返回表示等效解析树的Python列表。所得到的列表表示可以用于检查或以列表形式创建新的解析树。只要存储器可用于构建列表表示,此函数不会失败。如果解析树仅用于检查,则应使用st2tuple()来减少内存消耗和碎片。当需要列表表示时,此函数显着快于检索元组表示并将其转换为嵌套列表。

如果line_info为真,则对于所有终端令牌将包括行号信息作为表示令牌的列表的第三元素。请注意,提供的行号指定令牌结束的行如果标志为假或省略,则省略此信息。

parser.st2tuple(st, line_info=False, col_info=False)

此函数在st中接受来自调用者的ST对象,并返回表示等效解析树的Python元组。除了返回元组而不是列表,此函数与st2list()相同。

如果line_info为真,则对于所有终端令牌将包括行号信息作为表示令牌的列表的第三元素。如果标志为假或省略,则省略此信息。

parser.compilest(st, filename='<syntax-tree>')

Python字节编译器可以在ST对象上调用以产生代码对象,这些对象可以用作调用内建exec()eval()函数的一部分。此函数提供了编译器的接口,使用由filename参数指定的源文件名将内部解析树从st传递到解析器。filename提供的默认值表示源是ST对象。

编译ST对象可能会导致与编译相关的异常;示例将是由del f(0)的解析树导致的SyntaxError:此语句被考虑在Python的正式语法内的法律,但不是一个法律语言结构。为此条件引发的SyntaxError实际上是由Python字节编译器正常生成的,这就是为什么此时可由parser模块引发的原因。编译失败的大多数原因可以通过检查解析树以编程方式诊断。

32.1.3. Queries on ST Objects

提供了两个功能,其允许应用程序确定ST是否被创建为表达式或套件。这些函数都不能用于确定是否通过expr()suite()从源代码创建ST,或者通过sequence2st()

parser.isexpr(st)

st表示'eval'形式时,此函数返回true,否则返回false。这是有用的,因为代码对象通常不能使用现有的内建函数查询此信息。注意,由compilest()创建的代码对象不能像这样查询,并且与内建compile()函数创建的对象完全相同。

parser.issuite(st)

此函数镜像isexpr(),因为它报告ST对象是否表示'exec'形式,通常称为“套件”。此函数等效于而不是 isexpr(st),因为将来可能支持其他句法片段。

32.1.4. Exceptions and Error Handling

解析器模块定义单个异常,但也可能传递来自Python运行时环境的其他部分的其他内建异常。有关可以引发的异常的信息,请参阅每个函数。

exception parser.ParserError

在解析器模块中发生故障时引发异常。这通常用于验证失败,而不是在正常解析期间产生的内建SyntaxError异常参数是描述失败原因的字符串或包含导致传递给sequence2st()的解析树失败的序列的元组和一个解释字符串。调用sequence2st()需要能够处理任何类型的异常,而对模块中其他函数的调用只需要知道简单的字符串值。

注意,函数compilest()expr()suite()可能引发通常由解析和编译过程引发的异常。这些包括内置异常MemoryErrorOverflowErrorSyntaxErrorSystemError在这些情况下,这些例外具有通常与它们相关联的所有含义。有关详细信息,请参阅各功能的说明。

32.1.5. ST Objects

ST对象之间支持有序和等式比较。还支持使用ST对象(使用pickle模块)。

parser.STType

expr()suite()sequence2st()返回的对象类型。

ST对象有以下方法:

ST.compile(filename='<syntax-tree>')

compilest(st, filename)相同。

ST.isexpr()

isexpr(st)相同。

ST.issuite()

issuite(st)相同。

ST.tolist(line_info=False, col_info=False)

st2list(st, line_info, col_info)相同。

ST.totuple(line_info=False, col_info=False)

st2tuple(st, line_info, col_info)相同。

32.1.6. Example: Emulation of compile()

虽然许多有用的操作可能发生在解析和字节码生成之间,但最简单的操作是不做任何事。为此,使用parser模块产生中间数据结构相当于该代码

>>> code = compile('a + 5', 'file.py', 'eval')
>>> a = 5
>>> eval(code)
10

使用parser模块的等效操作稍长一些,并允许将中间内部解析树保留为ST对象:

>>> import parser
>>> st = parser.expr('a + 5')
>>> code = st.compile('file.py')
>>> a = 5
>>> eval(code)
10

需要ST和代码对象的应用程序可以将此代码打包成容易获得的函数:

import parser

def load_suite(source_string):
    st = parser.suite(source_string)
    return st, st.compile()

def load_expression(source_string):
    st = parser.expr(source_string)
    return st, st.compile()