Introduction
上篇文章解释了Python是如何使用PyParser生成CST的。回顾一下,Python执行代码要经过如下过程:
1. Tokenizer进行词法分析,把源程序分解为Token
2. Parser根据Token创建CST
3. CST被转换为AST
4. AST被编译为字节码
5. 执行字节码
当执行Python代码的时候,以代码存放在文件中的情况为例,Python会调用PyParser_ASTFromFile函数将文件的代码内容转换为AST:
mod_ty PyParser_ASTFromFile(FILE *fp, const char *filename, int start, char *ps1, char *ps2, PyCompilerFlags *flags, int *errcode, PyArena *arena) { mod_ty mod; perrdetail err; node *n = PyParser_ParseFileFlags(fp, filename, &_PyParser_Grammar, start, ps1, ps2, &err, PARSER_FLAGS(flags)); if (n) { mod = PyAST_FromNode(n, flags, filename, arena); PyNode_Free(n); return mod; } else { err_input(&err); if (errcode) *errcode = err.error; return NULL; } } |
在PyParser_ParseFileFlags把文件转换成CST之后,PyAST_FromNode函数会把CST转换成AST。此函数定义在include/ast.h中:
PyAPI_FUNC(mod_ty) PyAST_FromNode(const node *, PyCompilerFlags *flags, const char *, PyArena *); |
在分析此函数之前,我们先来看一下有关AST的一些基本的类型定义。
AST Types
AST所用到的类型均定义在Python_ast.h中,以stmt_ty类型为例:
enum _stmt_kind {FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3, Delete_kind=4, Assign_kind=5, AugAssign_kind=6, Print_kind=7, For_kind=8, While_kind=9, If_kind=10, With_kind=11, Raise_kind=12, TryExcept_kind=13, TryFinally_kind=14, Assert_kind=15, Import_kind=16, ImportFrom_kind=17, Exec_kind=18, Global_kind=19, Expr_kind=20, Pass_kind=21, Break_kind=22, Continue_kind=23}; struct _stmt { enum _stmt_kind kind; union { struct { identifier name; arguments_ty args; asdl_seq *body; asdl_seq *decorators; } FunctionDef;
struct { identifier name; asdl_seq *bases; asdl_seq *body; } ClassDef;
struct { expr_ty value; } Return;
// ... 过长,中间从略
struct { expr_ty value; } Expr;
} v; int lineno; int col_offset; };
typedef struct _stmt *stmt_ty; |
stmt_ty是语句结点类型,实际上是_stmt结构的指针。_stmt结构比较长,但有着很清晰的Pattern:
1. 第一个Field为kind,代表语句的类型。_stmt_kind定义了_stmt的所有可能的语句类型,从函数定义语句,类定义语句直到Continue语句共有23种类型。
2. 接下来是一个union v,每个成员均为一个struct,分别对应_stmt_kind中的一种类型,如_stmt.v.FunctionDef对应了_stmt_kind枚举中的FunctionDef_Kind,也就是说,当_stmt.kind == FunctionDef_Kind时,_stmt.v.FunctionDef中保存的就是对应的函数定义语句的具体内容。
3.