14a0e18c57134379af245c43cc523565
Python的词法分析

吐槽:出的题目没有大佬做吗?俺很伤心哇

从本周开始我们正式进入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的起始位置。

在字符串模式下,bufcurinp都是相同的

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)
top Created with Sketch.