Bytecode - 一款 Python 字节码编译工具

查看原文

bytecode 是一款 Python (3.4+) 的字节码编译工具,它可以让你写 bytecode,进而通过 exec(bytecode.to_code()) 执行它。

衍生思考:感觉比较有用的场景是写 DSL。用一个语法解析工具解析自定义的语法规则,然后将切分好的 Token 映射成 Bytecode,进而让 Python 解释器执行它。以下的小例子可以用于进制转换,分别展示了运行结果及其源代码。当然,只是为了验证下可行性,要想写出 Robust 的 DSL 代码,还需要很多细节上的考虑。

运行结果:

$ ./simple-pipe '15 | bin'
0b1111

$ ./simple-pipe '15 | oct'
0o17

$ ./simple-pipe '15 | hex'
0xf

源代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# Save as `simple-pipe` and chmod +x for it.
# `virtualenv venv; source venv/bin/activate; pip install ply bytecode

import sys
import inspect
from bytecode import Instr, Bytecode

tokens = ('PIPE', 'NAME', 'NUMBER')
t_PIPE = r'\|'
t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
t_ignore = " \t"

def t_NUMBER(t):
    r'\d+'
    t.value = int(t.value)
    return t

def t_error(t):
    print("Illegal character '%s'" % t.value[0])
    t.lexer.skip(1)

import ply.lex as lex
lex.lex()

def p_command_single(p):
    'command : expression'
    p[0] = p[1]

def p_command_group(p):
    'command : command PIPE expression'
    p[0] = p[3] + p[1] + [
        Instr("CALL_FUNCTION", 1),
    ]

def p_expression_int(p):
    'expression : NUMBER'
    p[0] = [
        Instr('LOAD_CONST', int(p[1]))
    ]

def p_expression_name(p):
    'expression : NAME'
    p[0] = [
        Instr('LOAD_NAME', p[1])
    ]


import ply.yacc as yacc
yacc.yacc()

s = sys.argv[1]
command_set = [Instr('LOAD_NAME', 'print')] +  yacc.parse(s) + [Instr('CALL_FUNCTION', 1), Instr('LOAD_CONST', None), Instr('RETURN_VALUE')]
exec(Bytecode(command_set).to_code())