2022年 11月 5日

python的词法_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的起始位置。

在字符串模式下,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)