7.5 一个示例:添加一个约等于比较运算法
Last updated
Last updated
如果要将本章节所有内容串联起来,你可以在 Python 语言中添加一个新的语法特性并重新编译 CPython 来掌握本章节内容。
比较表达式会比较两个或多个值:
在比较表达式中用到的运算符被称为比较运算符。以下可能是一些你能认出来的比较运算符:
小于:<;
大于:>;
等于:==;
不等于:!=。
参见
PEP 207 为 Python 2.1 提出了在数据模型中的丰富比较运算符。此 PEP 包含了自定义 Python 类型实现比较方法的上下文、历史以及解释。
现在,让我们来添加另外一个比较运算符:约等于,由 ~=
表示,其将拥有如下行为:
如果你比较一个浮点数和一个整数,那么它会将浮点数转换为整数并比较结果;
如果你比较两个整数,那么它会使用普通的相等运算符。
新的运算符会在交互式解释器中返回如下结果:
为了添加新的运算符,首先你需要更新 CPython 语法。在 Grammar/python.gram
文件中,比较运算符被定义为一个符号(symbol):comp_op
:
代码不翻译
修改 compare_op_bitwise_or_pair
表达式以允许新的 ale_bitwise_or
对:
代码不翻译
在现有的 is_bitwise_or
表达式下定义新的 ale_bitwise_or
表达式:
这个新的类型定义了一个包含 '~=' 终结符号的命名表达式:ale_bitwise_or
。
函数调用 _PyPegen_cmpop_expr_pair(p, AlE, a)
是一个从 AST 获取 cmpop
节点的表达式。类型为 AlE
:Almost Equal。
接下来,在 Grammar/Tokens
中添加单词符号(token):
为了更新 C 语言中的语法和单词符号,你需要重新生成头文件。
在 MacOS 或 Linux上使用以下命令:
在 Windows 上运行 PCBuild
目录中的以下命令:
这些步骤会自动更新分词器(tokenizer)。比如:打开 Parser/token.c
源文件,你可以看到 PyToken_TwoChars() 函数已经被修改:
如果你在这个阶段重新编译 CPython 并且打开一个交互式解释器,那么你可以看到分词器已经可以成功识别到这个单词符号,但是此时 AST 还不知道如何处理它:
这个异常是由 Python/ast.c
文件中的 ast_for_comp_op() 函数引起,原因是交互式解释器还无法把 ALMOSTEQUAL
识别为一个比较运算符中的一个有效运算符。
Compare
是一个定义在 Parser/Python.asdl
文件中的一个表达式类型,其具有的属性:左表达式、操作符(ops
)列表以及一个要比较的表达式列表(comparators
):
在 Compare
定义中是对 cmpop
枚举的引用:
这是一个可以充当比较运算符的抽象语法树叶子节点的列表集合。我们定义的约等于运算符没在里面所以需要补充完整。更新这个选项集合来包含一个新类型:ALE
:
接下来,我们重新生成 AST 来更新 AST 的 C 头文件:
这个操作会更新 Include/Python-ast.h
文件中的比较运算符(_cmpop
)枚举,来包含 ALE
选项:
抽象语法树此时还没有能力将 ALMOSTEQUAL
单词符号和 ALE
比较运算符关联上。所以你还要为抽象语法树更新 C 语言代码。
找到 Python/ast.c
文件中的 ast_for_comp_op() 函数并找到运算符单词符号的 switch 语句。这个函数会返回 _cmop
枚举值中的一个。
添加两行代码,用于捕捉 ALMOSTEQUAL
单词符号并返回 AlE
比较运算符:
在这个阶段,分词器和 AST 可以解析代码,但是编译器还是不知道如何处理这个运算符。为了测试抽象语法树的表示,我们可以使用 ast.parse()
函数来探索表达式的第一个运算符:
这是我们 AlE
比较运算符类型的一个实例,因此抽象语法树才能正确的分析代码。
在下一个章节,你会继续学习 CPython 编译器是如何工作的,并继续修改约等于运算符来构建其执行逻辑。