7.2 CPython 解析器-分词器
Last updated
Last updated
编程语言对词法分析器(lexer)有不同的实现。有些语言会将词法分析器生成器(lexer generator)作为解析器生成器(parser generator)的一个补充。
CPython 有一个用 C 语言编写的解析器-分词器(parser-tokenizer)模块。
以下是与解析器-分词器相关的文件:
文件 | 用途 |
---|---|
解析器-分词器的入口点是:PyParser_ASTFromFileObject()其获取一个文件句柄、编译器标志和PyArena
实例并把文件对象转换为一个模块。
这个函数主要有两个步骤:
通过 PyParser_ParseFileObject() 函数转换成具象语法树(CST);
通过使用抽象语法树(AST)函数 PyAST_FromNodeObject() 转换为抽象语法树或模块。
PyParser_ParseFileObject() 函数有两个重要的任务:
通过调用 PyTokenizer_FromFile() 创建一个分词器状态实例 tok_state
;
通过调用 parsetok() 将单词符号转换为一棵具象语法树(由一系列 node 节点构成)。
解析器-分词器接收文本输入并循环执行分词器和解析器,直到光标到达文本末尾才结束(或者出现了一个语法错误)。
在执行前,解析器-分词器会先创建出一个 tok_state
实例,分词器会将所有的状态存放到这个临时的数据结构中。分词器状态包括当前光标位置、行等信息。
解析器-分词器通过调用 tok_get()
获取到下个单词符号(token)。解析器-分词器将生成的单词符号 ID 传递给解析器,其将使用解析器生成器生成的 DFA 在具象语法树上创建节点。
tok_get() 函数是整个 CPython 代码基线中最复杂的函数之一。其已经超过了 640 行并且包含了几十年的边缘案例、新语言特性和语法。
循环调用分词器和解析器的流程如下图所示:
由 PyParser_ParseFileObject() 函数返回的具象语法树根节点是下一阶段将具象语法树(CST)转换为抽象语法树(AST)的关键环节。
节点类型定义在 Include/node.h
文件中:
由于具象语法树是一棵包含语法、单词符号 ID 和符号的树,因此编译器很难基于 Python 语言做出快速决策。
在你学习 AST 之前,这里有个办法可以看到解析阶段的输出:CPython 有一个叫做 parser
的标准模块,其通过 Python API 暴露出一个 C 函数。
输出将会是一个数值,使用的是由 make regen-grammar
阶段产生的单词符号和符号数值,这些数值存储在 Include/token.h
头文件中:
为了更容易理解,你可以将所有这些符号和单词模块中的数字都放入到字典中,然后递归的将 parser.st2list()
的输出值替换为单词符号的名字。
你可以用一个简单的表达式来运行 lex()函数,比如:查看 a + 1 是如何表示成一棵语法分析树:
在输出中,你可以看到小写的符号,如:arith_expr
,以及大写的单词符号,如:NUMBER
。
Python/pythonrun.c
从输入执行解析器和编译器;
Parser/parsetok.c
解析器和分词器的实现;
Parser/tokenizer.c
分词器的实现;
Include/token.h
单词符号类型申明,由Tools/scripts/generate_token.py
文件生成;
Include/node.h
用于分词器的语法分析树节点接口和宏定义。