Python中执行程序可分为5个步骤:
Tokenizer进行词法分析,把源程序分解为Token
Parser根据Token创建CST
CST被转换为AST
AST被编译为字节码
执行字节码
本文将介绍Python程序执行的第一步,也就是词法分析。词法分析简单来说就是把源程序的字符分解组合成Token。比如sum=0可以分解成3
个token,'sum', '=',
'0'。程序中的whitespace通常只作为分隔符用,最终会被忽略掉,因此没有出现在token的列表中。不过在Python之中,由于语法规则的
关系,Tab/Space需要用来分析程序的缩进,因此Python中对于Whitespace的处理比一般C/C++编译器的处理会要稍微复杂一些。
在Python中词法分析的实现在Parser目录下的tokenizer.h和tokenizer.cpp。Python的其他部分会直接调用tokenizer.h中定义的函数,如下:
extern
struct tok_state *PyTokenizer_FromString(const char
*); extern struct
tok_state *PyTokenizer_FromFile(FILE *, char *, char
*); extern void
PyTokenizer_Free(struct tok_state *); extern int
PyTokenizer_Get(struct tok_state *, char **, char **);
这些函数均以PyTokenizer开头。这是Python源代码中的一个约定。虽然Python是用C语言实现的,其实现方式借鉴了很多面对对象
的思想。拿词法分析来说,这四个函数均可以看作PyTokenizer的成员函数。头两个函数PyTokenizer_FromXXXX可以看作是构造函
数,返回PyTokenizer的instance。PyTokenizer对象内部状态,也就是成员变量,储存在tok_state之中。
PyTokenizer_Free可以看作是析构函数,负责释放PyTokenizer,也就是tok_state所占用的内存。
PyTokenizer_Get则是PyTokenizer的一个成员函数,负责取得在字符流中下一个Token。这两个函数均需要传入
tok_state的指针,和C++中需要隐含传入this指针给成员函数的道理是一致的。可以看到,OO的思想其实是和语言无关的,即使是C这样的结构
化的语言,也可以写出面对对象的程序。
tok_state
tok_state等价于PyTokenizer这个class本身的状态,也就是内部的私有成员的集合。部分定义如下:
struct
tok_state {
char
*buf; char
*cur; char
*inp; char
*end; char
*start; int
done; FILE
*fp; int
tabsize; int
indent; int
indstack[MAXINDENT]; int
atbol; int
pendin; char
*prompt,
*nextprompt; int
lineno; int
level; };
最重要的是buf, cur, inp, end, start。这些field直接决定了缓冲区的内容:
buf是缓冲区的开始。假如PyTokenizer处于字符串模式,那么buf指向字符串本身,否则,指向文件读入的缓冲区。
cur指向缓冲区中下一个字符。
inp指向缓冲区中有效数据的结束位置。PyTokenizer是以行为单位进行处理的,每一行的内容存入从buf到inp之间,包括\n。一般情况下
,PyTokenizer会直接从缓冲区中取下一个字符,一旦到达inp所指向的位置,就会准备取下一行。当PyTokenizer处于不同模式下面,具
体的行为会稍有不同。
end是缓冲区的结束,在字符串模式下没有用到。
start指向当前token的开始位置,如果现在还没有开始分析token,start为NULL。
PyTokenzer_FromString &
PyTokenizer_FromFile
PyTokenizer_FromString &
PyTokenizer_FromFile可以说是PyTokenizer的构造函数。从这两个函数的命名可以看出,PyTokenizer支持两种模
式:字符串和文件。由于标准输入STDIN也可以看作是文件,因此实际上PyTokenizer支持3种模式