吐槽:出的题目没有大佬做吗?俺很伤心哇
从本周开始我们正式进入Python的编译和解释环节。今天我们先来谈谈Python运行环节前的词法执行环节。
由于这部分开始都比较复杂,我会采用实际的例子来逐步分析。两章一篇,第一部分讲大致流程,第二部分讲详细实现过程。
词法分析的流程
本质上,Python提供了两种执行Python的方式:
PyRun_File系列
PyRun_String系列
故名思义,前者接受一个py文件进行编译->解释执行,而后者直接对一个符合Python语法的字符串进行编译->解释执行的过程。
为了减少篇幅,我们以后者进行举例,假设我们有一串Python代码,如下所示:
k = 5
print(k)
当我们键入上述代码后,Python会初始化运行环境,设置一系列的环境参数,创建Python虚拟机和对应的线程。(这块等我们实际讲到Python的虚拟机再深入探讨)
准备工作都OK以后,主要的工作流程都在token.h/.c里面了,
tok_state
词法解析通俗来讲是一个非常巨大的状态迁移过程,比如我们要记录当前解析到什么字符,这个字符是否可以和上一个合并或者开始新的状态解析。如果是新的状态解析,之前解析的字符是否需要进行回退等等。
因此,Python为了维护解析整个Python代码,设计了tok_state这个结构体:
struct tok_state {
/* Input state; buf <= cur <= inp <= end */
/* NB an entire line is held in the buffer */
char *buf; /* Input buffer, or NULL; malloc’ed if fp != NULL */
char *cur; /* Next character in buffer */
char *inp; /* End of data in buffer */
char *end; /* End of input buffer if buf != NULL */
char *start; /* Start of current token if not NULL */
int done; /* E_OK normally, E_EOF at EOF, otherwise error code */
/* NB If done != E_OK, cur must be == inp!!! */
FILE *fp; /* Rest of input; NULL if tokenizing a string */
int tabsize; /* Tab spacing */
int indent; /* Current indentation index */
int indstack[MAXINDENT]; /* Stack of indents */
int atbol; /* Nonzero if at begin of new line */
int pendin; /* Pending indents (if > 0) or dedents (if < 0) */
char *prompt, *nextprompt; /* For interactive prompting */
int lineno; /* Current line number */
int level; /* () [] {} Parentheses nesting level */
/* Used to allow free continuations inside them */
/* Stuff for checking on different tab sizes */
const char *filename; /* For error messages */
int altwarning; /* Issue warning if alternate tabs don’t match */
int alterror; /* Issue error if alternate tabs don’t match */
int alttabsize; /* Alternate tab spacing */
int altindstack[MAXINDENT]; /* Stack of alternate indents */
/* Stuff for PEP 0263 */
int decoding_state; /* -1:decoding, 0:init, 1:raw */
int decoding_erred; /* whether erred in decoding */
int read_coding_spec; /* whether ‘coding:…’ has been read */
char *encoding;
int cont_line; /* whether we are in a continuation line. */
const char* line_start; /* pointer to start of current line */
#ifndef PGEN
PyObject *decoding_readline; /* codecs.open(…).readline */
PyObject *decoding_buffer;
#endif
const char* enc;
const char* str;
const char* input; /* Tokenizer’s newline translated copy of the string. */
};
第一眼看上去比较复杂,令人摸不着头脑,实际上我们只要关注如下几个字段即可:
buf,字符串本身(当然在文件模式下就指向文件的缓冲区)
cur,即将解析的下一个字符。
inp,词法分析处于效率考虑,读取字符串(文件内容)是以行为单位进行读取。
start,当前识别的token的起始位置。
在字符串模式下,buf,cur,inp都是相同的。
tok->buf = tok->cur = tok->end = tok->inp = (char*)str;
有一点需要注意的是,Python可以在文件头表明支持的encode模式,比如如下代码:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
print “你好,世界”;
因此,在处理词法分析之前,需要做相应的解码记录操作。
static const char *
decode_str(const char *input, int single, struct tok_state *tok)