Skip to content

Instantly share code, notes, and snippets.

@darius
Created May 2, 2014 20:01
Show Gist options
  • Select an option

  • Save darius/d181b1e98f598e9dee94 to your computer and use it in GitHub Desktop.

Select an option

Save darius/d181b1e98f598e9dee94 to your computer and use it in GitHub Desktop.
# (Python 2)
# Developed from an example by Dave Long, but don't blame him!
from dis import cmp_op, dis, hascompare, hasjabs, hasjrel, opmap, HAVE_ARGUMENT
from itertools import chain
import types
class Clutch(object):
def __init__(self, *args, **kwargs):
for arg in args: setattr(self, arg.__name__, arg)
self.__dict__.update(kwargs.iteritems())
# assembly objects
def assemble(assembly):
return ''.join(assembly.encode(0, dict(assembly.locate(0))))
class Assembly(Clutch):
def __add__(self, other): return Chain(self, other)
def Chain(assembly1, assembly2):
def locate(start):
return chain(assembly1.locate(start),
assembly2.locate(start + assembly1.length))
def encode(start, addresses):
return chain(assembly1.encode(start, addresses),
assembly2.encode(start + assembly1.length, addresses))
return Assembly(locate, encode,
length = assembly1.length + assembly2.length)
def Label(tag):
def locate(start): return ((tag, start),)
def encode(start, addresses): return ''
return Assembly(locate, encode, length = 0)
def make_insn(encoded):
def locate(start): return ()
def encode(start, addresses): return encoded
return Assembly(locate, encode, length=len(encoded))
def make_jump_insn(opcode, tag):
def locate(start): return ()
def encode(start, addresses):
base = 0 if opcode in hasjabs else start+3
return insn_encode(opcode, addresses[tag] - base)
return Assembly(locate, encode, length=3)
def insn_encode(opcode, arg):
return '%c%c%c' % (opcode, arg%256, arg//256)
# Opcodes
def denotation(opcode):
if opcode < HAVE_ARGUMENT:
return make_insn(chr(opcode))
elif opcode in hascompare:
return lambda arg: make_insn(insn_encode(opcode, cmp_op.index(arg)))
elif opcode in hasjrel or opcode in hasjabs:
return lambda tag: make_jump_insn(opcode, tag)
else:
return lambda arg: make_insn(insn_encode(opcode, arg))
op = Clutch(**{name: denotation(opcode) for name, opcode in opmap.items()})
# Example
def make_bop(name, assembly, doc_string):
return types.FunctionType(
types.CodeType(2, # no kwonly in 2.7?
2, 16, 0,
assemble(assembly),
(doc_string,1), (), ('x','y'),
'<stdin>', name, 1, '',
(), ()),
{})
def example(f):
print f.__doc__
print dis(f)
for i in range(3):
print [f(i,j) for j in range(3)]
print
def cond(test, then, else_):
yes, after = 'yes', 'after'
return (test + op.POP_JUMP_IF_TRUE(yes)
+ else_ + op.JUMP_FORWARD(after)
+ Label(yes) + then + Label(after))
def while_(test, body):
return (op.JUMP_FORWARD('testing')
+ Label('again') + body
+ Label('testing') + test + op.POP_JUMP_IF_TRUE('again'))
min0 = (cond(op.LOAD_FAST(0) + op.LOAD_FAST(1) + op.COMPARE_OP('<='),
op.LOAD_FAST(0),
op.LOAD_FAST(1))
+ op.RETURN_VALUE)
min1 = (while_(op.LOAD_FAST(0) + op.LOAD_FAST(1) + op.COMPARE_OP('>'),
op.LOAD_FAST(0) + op.LOAD_CONST(1) + op.BINARY_SUBTRACT + op.STORE_FAST(0))
+ op.LOAD_FAST(0) + op.RETURN_VALUE)
## example(make_bop("min1", min1, "min via while_"))
#. min via while_
#. 1 0 JUMP_FORWARD 10 (to 13)
#. >> 3 LOAD_FAST 0 (x)
#. 6 LOAD_CONST 1 (1)
#. 9 BINARY_SUBTRACT
#. 10 STORE_FAST 0 (x)
#. >> 13 LOAD_FAST 0 (x)
#. 16 LOAD_FAST 1 (y)
#. 19 COMPARE_OP 4 (>)
#. 22 POP_JUMP_IF_TRUE 3
#. 25 LOAD_FAST 0 (x)
#. 28 RETURN_VALUE
#. None
#. [0, 0, 0]
#. [0, 1, 1]
#. [0, 1, 2]
#.
## example(make_bop("min0", min0, "min via cond"))
#. min via cond
#. 1 0 LOAD_FAST 0 (x)
#. 3 LOAD_FAST 1 (y)
#. 6 COMPARE_OP 1 (<=)
#. 9 POP_JUMP_IF_TRUE 18
#. 12 LOAD_FAST 1 (y)
#. 15 JUMP_FORWARD 3 (to 21)
#. >> 18 LOAD_FAST 0 (x)
#. >> 21 RETURN_VALUE
#. None
#. [0, 0, 0]
#. [0, 1, 1]
#. [0, 1, 2]
#.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment