JanC wrote:
[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

Reply via email to