您的位置:首页 > 编程语言 > Python开发

Python programming with goto

2010-05-05 15:03 363 查看
豆瓣Python版有人想要goto。虽说看不出来goto有什么特别吸引力,但是为了实现Python社区对广大人民群众做出的“Python是世界上除Lisp外最牛b的编程语言”的庄严承诺,同时也为了复习一下一段时间没用Python知识,写了一个goto模块。使用如下例:

1 from goto import *
2
3 @patch
4 def f2():
5 goto(10)
6 print 'should not see this'
7 label(10)
8 for i in range(1,99999):
9 print i
if i == 5:
goto('out')
label('out')

f2()用法是:

1. from goto import *。注意暂时不支持import goto,不是不能实现,是暂时没时间写。

2.对需要使用goto的函数,前面加个@patch

3.用label(x)和goto(x)的形式写label和goto。x可以是数字或字符串。

goto模块的代码如下:

goto.pyimport dis,pdb

#dummy functions serving as target of bytecode patching
def goto(label):
pass

def label(label):
pass

#
def decode_bytecode(fun):
"""Input: a function
Ouput: a list of pairs (opcode, arguments)"""
c = fun.func_code.co_code
n = len(c)
i = 0
while i < n:
op = c[i]
i += 1
arguments = ""
if ord(op) >= dis.HAVE_ARGUMENT:
arguments = c[i : i+2]
i += 2
yield (op, arguments)

def sample():
goto(200)
if 1 == 2:
sample()
else:
print 'sample'

def test_decode(fun):
for op,arg in decode_bytecode(fun):
if arg=='':
print dis.opname[ord(op)]
else:
print dis.opname[ord(op)] +' '+str(ord(arg[0]))+' '+str(ord(arg[1]))

def match_pattern(seq, i, p):
"""
try to match pattern p to seq[i:], return None if match failed
seq: output of decode_bytecode
p -> [instr, instr, ...]
instr -> (opcode, arg, arg) opcode is a opcode string
arg -> '' I don't give a damn about this arg
arg -> integer match arg with number
arg -> string the arg is key of the returned match dict from which the arg value can be extracted
arg -> lambda lambda is evaluated with the argument, false return means failed match
"""
#pdb.set_trace()
m = {}

for op, arg1, arg2 in p:
if i==len(seq):
return None

if dis.opmap[op] != ord(seq[i][0]):
return None

if arg1 == '':
pass
else:
if seq[i][1] == '': return None

a1 = ord(seq[i][1][0])
if type(arg1) is str:
m[arg1]=a1
elif type(arg1) is int:
if arg1 != a1: return None
elif not arg1(a1):
return None

#don't need arg2 in this program

i+=1

return m

def int_to_bytecode_arg(i):
return chr(i % 256) +\
chr(i // 256)

def patch(fun):
NOP = chr(dis.opmap['NOP'])
co = fun.func_code
old = list(decode_bytecode(fun))
new = [] #a list of characters

#mapping from label to bytecode offset
label_table={}
#if a goto(label) is seen but label is not seen
#record for the number the bytecode offset of the
#argument for JUMP_ABSOLUTE for later patching
goto_table={}

i=0
#pdb.set_trace()
while i<len(old):
m= match_pattern(old, i,
[('LOAD_GLOBAL','fun_name',''),
('LOAD_CONST','label',''),
('CALL_FUNCTION',1,''),
('POP_TOP','','')])
if m:
stmt = co.co_names[m['fun_name']]
label = co.co_consts[m['label']]

if m and stmt == 'goto':
# we have a goto statement
if label_table.has_key(label):
arg = int_to_bytecode_arg(label_table[label])
else:
arg = '\xff\xff'
goto_table[label] =\
goto_table.get(label, [])+[len(new)+1]
new += chr(dis.opmap['JUMP_ABSOLUTE'])
new += arg
#todo
#this is to maintain proper bytecode offset to
#source code line number mapping. A better way
#would be fixing the mapping instead of using
#placeholders
new += NOP*7
i += 4
elif m and stmt == 'label':
# we have a label statement
label_table[label]=len(new)
if goto_table.has_key(label):
for offset in goto_table[label]:
new[offset: offset+2]=int_to_bytecode_arg(len(new))
del goto_table[label]
new += NOP*10
i += 4
else:
# emit as-is
new += old[i][0] #the opcode
new += old[i][1] #its args if it has
i += 1

if len(goto_table):
#todo: output line number
raise Exception('missing label')

import types
newcode = types.CodeType(co.co_argcount,
co.co_nlocals,
co.co_stacksize,
co.co_flags,
''.join(new),
co.co_consts,
co.co_names,
co.co_varnames,
co.co_filename,
co.co_name,
co.co_firstlineno,
co.co_lnotab)
return types.FunctionType(newcode,fun.func_globals)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: