[an example of using decorators to control pyasm]
Another (perhaps wacky) approach would be to change the assembler source syntax enough to make it legal Python - in particular, this means parenthesizing the arguments - then it can just be stored in-line with other Python source. This has the additional benefit that one could write support functions to enable the source to be executed interactively in Python.
The following example uses the CPython opcodes, represented as Python functions. Python control structures 'while' and 'if' are used as assembler directives for flow.
Michael
>>> import ackermann >>> Ackermann = assemble(ackermann.ackSrc) [snip assembler output] >>> Ackermann <function ackSrc at 0x0185A570> >>> Ackermann(3,8) 2045
# ackermann.py --------------------------------------------------
def ackSrc(m,n):
"Compute Ackermann's function on a stack"
# Can be assembled to Python bytecode, or (not implemented yet)
# executed in Python with suitable support functions
LOAD_CONST("Return") LOAD_FAST(m) LOAD_FAST(n)
while condition(ROT_TWO(), DUP_TOP(), LOAD_CONST("Return"),
COMPARE_OP("!=")): if condition(POP_TOP(), DUP_TOP(), LOAD_CONST(0), COMPARE_OP("==")):
POP_TOP()
POP_TOP()
LOAD_CONST(1)
BINARY_ADD()else:
if condition(POP_TOP(), ROT_TWO(), DUP_TOP(), LOAD_CONST(0), COMPARE_OP("==")):
POP_TOP()
POP_TOP()
LOAD_CONST(1)
BINARY_SUBTRACT()
LOAD_CONST(1)
else:
POP_TOP()
DUP_TOP()
LOAD_CONST(1)
BINARY_SUBTRACT()
ROT_THREE()
POP_TOP()
DUP_TOP()
LOAD_CONST(1)
BINARY_SUBTRACT()
ROT_THREE()
ROT_TWO()
POP_TOP()
POP_TOP()
return
# ByteCode.py --------------------------------------------------
"""Python ByteCode Assembler
Author: Michael Spencer Version: 0 - Experiment 3/11/2005
Example usage: >>> import ackermann >>> Ackermann = assemble(ackermann.ackSrc) [snip assembler output] >>> Ackermann <function ackSrc at 0x0185A570> >>> Ackermann(3,8) 2045 """
import dis import compiler import compiler.ast as ast opmap = dis.opmap import new import inspect
class AbstractVisitor(object): """Standard depth-first AST walker - dispatches to methods based on Node class name""" def __init__(self): self._cache = {} # dispatch table
def visit(self, node,**kw):
#print "Visiting: %s" % node.__class__ if node is None: return None
cls = node.__class__
meth = self._cache.setdefault(cls,
getattr(self,'visit'+cls.__name__,self.default))
return meth(node, **kw) def default(self, node, **kw):
for child in node.getChildNodes():
self.visit(child, **kw)
visitExpression = default
class Assembler(AbstractVisitor): """Python bytecode assembler"""
def __init__(self):
self._cache = {} # dispatch table
self.i = 0 # Bytecode instruction counter self.co_varnames = []
self.co_consts = []
self.jumptable = {}
self.co_codelst = [] def emit(self, funcname, arg = None):
i = self.i
try:
opcode = opmap[funcname]
except KeyError:
raise SyntaxError, "Unknown operation: %s" % funcname
self.co_codelst.append(opcode)
if opcode > dis.HAVE_ARGUMENT:
print "%4s %4s %s %s" % (i, opcode, funcname.ljust(20), arg)
self.co_codelst.extend(self._getlohi(arg))
self.i = i + 3
else:
print "%4s %4s %s" % (i, opcode, funcname.ljust(20))self.i = i + 1
def getcodestring(self):
self._resolvejumps()
return "".join(map(chr, self.co_codelst)) def getcode(self):
return new.code(self.co_argcount, # argcount
self.co_argcount, # nlocals
10000, # stacksize
67, # flags
self.getcodestring(), # codestring
tuple(self.co_consts), # constants
tuple(self.co_varnames), # names
tuple(self.co_varnames), # varnames
"assembly", # filename
self.co_name, # name
0, # firstlineno
"" # lnotab
) def _getlohi(self, arg):
if isinstance(arg, int):
return arg % 256, arg / 256
else:
return None,None def _resolvejumps(self):
for origin, dest in self.jumptable.iteritems():
self.co_codelst[origin+1:origin+3] = self._getlohi(dest - origin -
3)def visitFunction(self, node, **kw):
self.co_name = node.name
self.co_argcount = len(node.argnames)
self.co_varnames.extend(node.argnames) print "def %s(%s)" % (node.name, node.argnames)
self.visit(node.code) def visitCallFunc(self,node,**kw):
funcname = node.node.name
if funcname == 'COMPARE_OP': # Special case
try:
comptype = node.args[0].value
arg = self.comparisons.index(comptype)
except ValueError:
raise SyntaxError, "Unknown comparison %s" % comptype
else:
args = [self.visit(arg) for arg in node.args]
if args:
arg = args[0]
else:
arg = None if funcname != "condition": # special case, emits no code
self.emit(funcname, arg)comparisons = list(dis.cmp_op)
def visitConst(self, node, **kw):
val = node.value
try:
return self.co_consts.index(val)
except ValueError:
self.co_consts.append(val)
return len(self.co_consts)-1 def visitName(self, node, **kw):
name = node.name
try:
return self.co_varnames.index(name)
except ValueError:
self.co_varnames.append(name)
return len(self.co_consts)-1 def visitIf(self, node, **kw):
"node.tests = [(condition, stmt),...] node.else_ = stmt"self.visit(node.tests[0][0]) # Get the predicate suite
jumpbase = self.i
self.emit("JUMP_IF_FALSE")
self.visit(node.tests[0][1]) if node.else_:
elsebase = self.i
self.emit("JUMP_FORWARD")
self.jumptable[jumpbase] = self.i
print ">> comefrom %s" % jumpbase self.visit(node.else_)
self.jumptable[elsebase] = self.i print ">> comefrom %s" % elsebase
else:
self.jumptable[jumpbase] = self.i
print ">> comefrom %s" % jumpbase def visitReturn(self, node, **kw):
self.emit("RETURN_VALUE")
def visitWhile(self, node, **kw):
loopstart = self.i
self.visit(node.test)
looptest = self.i
self.emit("JUMP_IF_FALSE") self.visit(node.body)
self.emit("JUMP_ABSOLUTE",loopstart)
print ">> comefrom %s" % looptest
self.jumptable[looptest] = self.i
def assemble(source): if type(source) == type(assemble): source = inspect.getsource(source)
tree = compiler.parse(source)
bc = Assembler()
bc.visit(tree)
return new.function(bc.getcode(),globals())
-- http://mail.python.org/mailman/listinfo/python-list
