python 编译过程 从py文件到bytecode

来源:互联网 发布:销售返利软件 编辑:程序博客网 时间:2024/06/08 15:00
在os 角度,文件最终由file_str 来表示一个文件,我们从
 mp_raw_code_t *raw_code = mp_raw_code_load_file(file_str);
作为今天追踪代码的起点。
raw_code 这个结构体我们已经看到好多次了,里面放了有bytecode 这么一个成员。
typedef struct _mp_raw_code_t {    mp_raw_code_kind_t kind : 3;    mp_uint_t scope_flags : 7;    mp_uint_t n_pos_args : 11;    union {        struct {            const byte *bytecode;            const mp_uint_t *const_table;            #if MICROPY_PERSISTENT_CODE_SAVE            mp_uint_t bc_len;            uint16_t n_obj;            uint16_t n_raw_code;            #endif        } u_byte;        struct {            void *fun_data;            const mp_uint_t *const_table;            mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc        } u_native;    } data;} mp_raw_code_t;

mp_raw_code_load_file从字面意思就可以看出,是要将一个(py)文件load到内存,官方说法就是将这个文件的内容跟我们现在的进程建立某种
联系,此时文件便成了这个进程的某种资源(财产)。
我们来看看此函数的实现
mp_raw_code_t *mp_raw_code_load_file(const char *filename) {    mp_lexer_file_buf_t fb;    fb.fd = open(filename, O_RDONLY, 0644);    int n = read(fb.fd, fb.buf, sizeof(fb.buf));    fb.len = n;    fb.pos = 0;    mp_reader_t reader;    reader.data = &fb;    reader.read_byte = file_buf_next_byte;    mp_raw_code_t *rc = mp_raw_code_load(&reader);    close(fb.fd);    return rc;}
第一个函数调用必然是打开文件, 打开文件,我就会得到一个文件描述符, 此函数位于syscalls.c 文件中
有兴趣的读者可以参考关于Linux 文件相关章节



如何由py文件生成lex
mp_lexer_t *lex = mp_lexer_new_from_file(file);

lex 定义在lexer.h里面
// this data structure is exposed for efficiency// public members are: source_name, tok_line, tok_column, tok_kind, vstrtypedef struct _mp_lexer_t {    qstr source_name;           // name of source    void *stream_data;          // data for stream    mp_lexer_stream_next_byte_t stream_next_byte;   // stream callback to get next byte    mp_lexer_stream_close_t stream_close;           // stream callback to free    unichar chr0, chr1, chr2;   // current cached characters from source    mp_uint_t line;             // current source line    mp_uint_t column;           // current source column    mp_int_t emit_dent;             // non-zero when there are INDENT/DEDENT tokens to emit    mp_int_t nested_bracket_level;  // >0 when there are nested brackets over multiple lines    mp_uint_t alloc_indent_level;    mp_uint_t num_indent_level;    uint16_t *indent_level;    mp_uint_t tok_line;         // token source line    mp_uint_t tok_column;       // token source column    mp_token_kind_t tok_kind;   // token kind    vstr_t vstr;                // token data} mp_lexer_t;

这个结构体大致分为了两部分,第一部分描述源文件相关信息,第二部分描述生成的token.
注释也说了,公开的成员只有source_name, tok_line, tok_column, tok_kind, vstr.
个人认为vstr是最重要的一个成员,最后拆出来的一个个token就是放在这个位置的。


我们继续往下读

mp_lexer_t *mp_lexer_new_from_file(const char *filename) {    int fd = open(filename, O_RDONLY, 0644);    if (fd < 0) {        return NULL;    }    return mp_lexer_new_from_fd(qstr_from_str(filename), fd, true);}
第一部分,只是得到一个fd. fd仅仅是一个int 型变量,主要就是在MinFd 和OPEN_MAX之间找到一个空闲的
/* Find and reserve a free File Descriptor.  Returns the first free File Descriptor greater than or equal to the,  already validated, fd specified by Minfd.  @return   Returns -1 if there are no free FDs.  Otherwise returns the            found fd.*/intFindFreeFD( int MinFd ){  struct __filedes    *Mfd;  int i;  int fd = -1;  Mfd = gMD->fdarray;  // Get an available fd  for(i=MinFd; i < OPEN_MAX; ++i) {    if(Mfd[i].f_iflags == 0) {      Mfd[i].f_iflags = FIF_LARVAL; // Temporarily mark this fd as reserved      fd = i;      break;    }  }  return fd;}


读取lex 下一个字符的时候,先将vstr resset.

void vstr_reset(vstr_t *vstr) {    vstr->len = 0;    vstr->had_error = false;}

如果第一个char 为MP_LEXER_EOF, 说明已经将源文件读完了。
STATIC bool is_end(mp_lexer_t *lex) {    return lex->chr0 == MP_LEXER_EOF;}



vstr 往后移动
char *vstr_add_len(vstr_t *vstr, size_t len) {    if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {        return NULL;    }    char *buf = vstr->buf + vstr->len;    vstr->len += len;    return buf;}


代码分析过程就是从stack 里面拿出一个个规则rule去匹配。
所以每次分析的第一步必然是 pop_rule();

那么第一个pop出来是谁push 进去的呢?
在parse 之前,就会根据输入类型来push 一个rule 进来。

    switch (input_kind) {        case MP_PARSE_SINGLE_INPUT: top_level_rule = RULE_single_input; break;        case MP_PARSE_EVAL_INPUT: top_level_rule = RULE_eval_input; break;        default: top_level_rule = RULE_file_input;    }    push_rule(&parser, lex->tok_line, rules[top_level_rule], 0);    // parse!

push 的意思是将某些内容保存起来,实例化一个 rule_stack_t . 然后给每个成员赋值。
STATIC void push_rule(parser_t *parser, size_t src_line, const rule_t *rule, size_t arg_i) {    if (parser->parse_error) {        return;    }    if (parser->rule_stack_top >= parser->rule_stack_alloc) {        rule_stack_t *rs = m_renew_maybe(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc + MICROPY_ALLOC_PARSE_RULE_INC, true);        if (rs == NULL) {            parser->parse_error = PARSE_ERROR_MEMORY;            return;        }        parser->rule_stack = rs;        parser->rule_stack_alloc += MICROPY_ALLOC_PARSE_RULE_INC;    }    rule_stack_t *rs = &parser->rule_stack[parser->rule_stack_top++];    rs->src_line = src_line;    rs->rule_id = rule->rule_id;    rs->arg_i = arg_i;}

我们从源文件或者str 中读出来供lexer吃,可是什么时候,是个头呢? 换句话说,读到啥时候,就不读了? 这个问题,由while 循环来解决。
        // get tail chars        while (!is_end(lex) && is_tail_of_identifier(lex)) {            vstr_add_byte(&lex->vstr, CUR_CHAR(lex));            next_char(lex);        }

当我们读到一行结尾,并且不是顶层缩进,那么,我们认为读到了程序末尾, 置tok_kind 为MP_TOKEN_END;

(is_end(lex)) {        if (indent_top(lex) > 0) {            lex->tok_kind = MP_TOKEN_NEWLINE;            lex->emit_dent = 0;            while (indent_top(lex) > 0) {                indent_pop(lex);                lex->emit_dent -= 1;            }        } else {            lex->tok_kind = MP_TOKEN_END;        }



0 0
原创粉丝点击