Hello,

I spent most of the last week reading the pdfs from the vpri.org site
and I am bowled over.  It's amazing what you're accomplishing.
Congratulations.

I am trying to understand the COLA architecture by way of building a
model of it in the python language.  I think I've got the basic object
model down okay, but I'm having trouble grokking the eval/transform
system.  I suspect there's an elegant twist there I'm just missing.

The attached files are:
co.py - a python version of the basic object model.
la.py - an attempt at making the transformation engine.
rootbeer.py - a "port" of la.py to the co.py object model.

Could someone take a look at those, or just fill in some detail about
section 5 "Behaviour: symbolic expressions and transformations" from
http://www.vpri.org/pdf/rn2006001a_colaswp.pdf "Accessible
Language-Based Environments of Recursive Theories"? I think I'm
missing something about how the transformation engine is supposed to
work.  I dug through the Jolt(?) code but I could locate the magic.

Thank you in advance, and thank you all for doing such incredible
work.  The object model alone is breathtaking.

Warm regards,
~Simon

-- 
"The history of mankind for the last four centuries is rather like
that of an imprisoned sleeper, stirring clumsily and uneasily while
the prison that restrains and shelters him catches fire, not waking
but incorporating the crackling and warmth of the fire with ancient
and incongruous dreams, than like that of a man consciously awake to
danger and opportunity."  --H. P. Wells, "A Short History of the
World"
'''

A simple model of an Object Model as per:

"Open Reusable Object Models", Ian Piumarta, Alessandro Warth
http://www.vpri.org/pdf/tr2006003a_objmod.pdf

This departs in several ways from the OM described in that paper. It's
meant mainly as a rough sketch done to better understand the ideas.

There is no specific symbol type, we just use strings. The vtable also
has no specific type, it's just an Object.  Last, I lean on the python
dict type to act as the vtable's storage.
'''

class Object:
    '''
    Root of the whole system, has a vtable and some data.
    '''
    def __init__(self, vtable):
        self.vtable = vtable
        self.data = None

def send(obj, name, *args, **keyword_args):
    method = bind(obj, name)
    return method(obj, *args, **keyword_args)

def bind(obj, name):
    vt = obj.vtable
    if vt is obj and name == 'lookup':
        return lookup(obj, name)
    return send(vt, 'lookup', name)

def addMethod(vtable, name, implementation):
    vtable.data[name] = implementation

def lookup(vtable, name):
    try:
        return vtable.data[name]
    except KeyError:
        if vtable.parent is not None:
            return send(vtable.parent, 'lookup', name)
    # Return None if nothing is found, will cause exception higher up.

def allocate(vtable):
    return Object(vtable)

def delegated(vtable):
    if vtable is None:
        child = allocate(None)
    else:
        child = allocate(vtable.vtable)
    child.parent = vtable
    child.data = {} # Map names to methods.
    return child


def bootstrap():

    # Create the VTable's VTable.
    vtvt = delegated(None)

    # VTable is its own VTable.
    vtvt.vtable = vtvt

    # Create a VTable for Objects. (Use delegated(None) so it has no
    # parent.  In other words Object, not VTable, is the root of the
    # system.)
    object_vt = delegated(None)

    # Manually tell it it's a VTable.
    object_vt.vtable = vtvt

    # VTable is a kind of Object. Thus its VTable's parent is Object's
    # VTable and since it's its own VTable we just set its parent here.
    vtvt.parent = object_vt

    # Give VTable a lookup method by directly calling addMethod().
    addMethod(vtvt, 'lookup', lookup)

    # Now the send() and bind() machinery will work.
    assert lookup is send(vtvt, 'lookup', 'lookup')

    # Add addMethod() to the VTable.
    addMethod(vtvt, 'addMethod', addMethod)

    # We can add the rest using send().
    send(vtvt, 'addMethod', 'allocate', allocate)
    send(vtvt, 'addMethod', 'delegated', delegated)

    # We're done.

    return object_vt, vtvt
#!/usr/bin/env python
'''
This is my crude attempt to understand section 5 of 
"Accessible Language-Based Environments of Recursive Theories", Ian
Piumarta - http://www.vpri.org/pdf/rn2006001a_colaswp.pdf

I don't quite grok it yet.

This is sort of a "pure python" version of what I think should be going
on. The __main__ clause below tries to exercise the Eval() call in a
simple but hopefully useful way, and then rootbeer.py tries to "port" the
same code to use OM objects from co.py (although the OM ast object simply
wraps the AST python object from this module.

The part I don't quite get is how a "transform property" is associated
with the symbols, or in other words, how the get_property() function is
supposed to work.  I suspect there's a lovely hidden elegance there that
will come out if I play with this long enough. Something to do with the
way transform() is applied to symbols vs normal objects...  Dunno.
'''

# Main engine: Eval() and transform().

def Eval(tree, context):
    if tree is None:
        return None
    t = typeof(tree)
    method = transform(t, context)
    result = method(tree, context)
    return Eval(result, context)


def transform(tree_type, context):
    if isSymbol(tree_type):
        return get_property(context, tree_type)
    else:
        return Eval(tree_type, context)


##===-----

# Engine depends on these helper functions.

def typeof(thing):
    return thing.symbol

def isSymbol(t):
    return isinstance(t, basestring)

def get_property(context, symbol):
    return getattr(context, symbol, null)

##===-----

# Which expect an AST with a symbol attribute and zero or more children.

class AST(list):
    def __init__(self, symbol, *values):
        self.symbol = symbol
        self.extend(values)

def null(atom, context):
    print 'null', atom, context

##===-----




# Let's play with it.

if __name__ == '__main__':

    # Create a fev different AST types.
    def makeLiteral(value): return AST('literal', value)
    def makeSequence(*values): return AST('sequence', *values)
    def makeOp(op): return AST('op', op)


    # Instantiate some.
    cats = makeLiteral("Cats")
    i23 = makeLiteral(23)

    from operator import mul
    mul = makeOp(mul)

    proggy = makeSequence(cats, i23, mul)


    # Now we need some contexts to evaluate this AST tree within.

    # Shared ability to evaluate sequences of ASTs.
    class Context:
        def sequence(self, thing, context):
            for item in thing:
                Eval(item, context)


    # Print everything
    class PrintContext(Context):
        def literal(self, thing, context): print thing[0]
        op = literal


    # Act like a sort of interpreter.
    class ActiveContext(Context):

        accumulator = []

        def literal(self, thing, context):
            self.accumulator.append(thing[0])

        def op(self, thing, context):
            op = thing[0]
            a, b = self.accumulator[:2]
            result = op(a, b)
            self.accumulator[:2] = [result]


    # Run in a "print out" context...
    Eval(proggy, PrintContext())

    print '------------------'

    # Run in an "interpret" context...
    c = ActiveContext()
    Eval(proggy, c)
    print c.accumulator
#!/usr/bin/env python
'''
Playing with integrating la.py and co.py

So this is basically an attempt to understand:

"Accessible Language-Based Environments of Recursive Theories", Ian
Piumarta - http://www.vpri.org/pdf/rn2006001a_colaswp.pdf

by implementing [part of] it in python. The co module implements the
object model as simply and directly as I could, and the la module tries
to implement the transformation engine using that object module.

I'm not quite getting it.  This works, but I think there's a much more
elegant system just out of sight and the secret's in the way you dispatch
the transform method to objects vs symbols. I'm not sure though.
'''
from co import bootstrap, send
from la import AST, Eval


# Create a universe to play in...
object_vt, vtvt = bootstrap()


##########################################

# Let Objects tell you if they derive from a certain type.
def isA(thing, family):
    '''Return bool indicating if thing "is a" family vtable.'''
    vt = thing.vtable
    while vt is not None:
        if vt is family:
            return True
        vt = vt.parent
    return False

send(object_vt, 'addMethod', 'isA', isA)


##########################################

# Create a Symbol object type.
symbol_vt = send(vtvt, 'delegated')

# Let Objects tell you their symbol/type.
def typeOf(obj):
    try:
        return obj.data.symbol
    except AttributeError:
        pass # ??

# Let Objects tell you if they are Symbols or not.
def isSymbol(symbol):
    return send(symbol, 'isA', symbol_vt)

# Allow for getting and setting names on symbols. Symbols simply add an
# attribute to themselves at runtime. Don't call getName() on a symbol
# that hasn't had setName() called first... Contrast this to the ast
# object that actually wraps the AST python object from the la.py module.
def getName(symbol):
    return symbol.name
def setName(symbol, name):
    symbol.name = name

send(object_vt, 'addMethod', 'typeOf', typeOf)
send(object_vt, 'addMethod', 'isSymbol', isSymbol)

send(symbol_vt, 'addMethod', 'getName', getName)
send(symbol_vt, 'addMethod', 'setName', setName)


# Some symbols:
LIT = send(symbol_vt, 'allocate'); send(LIT, 'setName', 'literal')
SEQ = send(symbol_vt, 'allocate'); send(SEQ, 'setName', 'sequence')
OP = send(symbol_vt, 'allocate'); send(OP, 'setName', 'op')


##########################################

# Create AST object type.
AST_vt = send(vtvt, 'delegated')

# Give ASTs an init method.  Notice that this OM object wraps the python
# object?  Isn't that cool?  It's just like the paper says, you can host
# alien objects no problem.
def initAST(ast, symbol, *values):
    ast.data = AST(symbol, *values)

send(AST_vt, 'addMethod', 'init', initAST)

# Create a few different AST types with some handy factory functions.
def makeLiteral(value):
    ast = send(AST_vt, 'allocate')
    send(ast, 'init', LIT, value)
    return ast

def makeSequence(*values):
    ast = send(AST_vt, 'allocate')
    send(ast, 'init', SEQ, *values)
    return ast

def makeOp(value):
    ast = send(AST_vt, 'allocate')
    send(ast, 'init', OP, value)
    return ast


##########################################

# Now we need a context to evaluate this AST tree within.  This is the
# part I think I'm messing up: the way that symbols and contexts are
# supposed to interact to provide the actual transform methods via the
# get_property() function.
context_vt = send(vtvt, 'delegated')

def sequence(thing, context):
    for item in thing.data:
        Eval(item, context)

def emit(thing, context):
    print thing.data[0]

send(context_vt, 'addMethod', 'sequence', lambda context: sequence)
send(context_vt, 'addMethod', 'literal', lambda context: emit)
send(context_vt, 'addMethod', 'op', lambda context: emit)

print_context = send(context_vt, 'allocate')


##########################################

# One last thing to do before this will work, replace the following three
# functions in la with these versions.

def typeof(thing):
    return send(thing, 'typeOf')

def isSymbol(thing):
    return send(thing, 'isSymbol')

def get_property(context, symbol):
    # context is a vtable, symbol is a selector, a method name, so...
#    name = send(symbol, 'getName')
#    method = send(context, name)
#    return method
    return send(context, send(symbol, 'getName'))

# We're ready to plug these in to the la machinery.
import la
la.typeof = typeof
la.isSymbol = isSymbol
la.get_property = get_property

# Eval() and transform() should use the above functions now.

##########################################

# Instantiate some ASTs.
from operator import mul
cats = makeLiteral("Cats")
i23 = makeLiteral(23)
mul = makeOp(mul)
proggy = makeSequence(cats, i23, mul)

# Run in a "print out" context...
Eval(proggy, print_context)

_______________________________________________
fonc mailing list
[email protected]
http://vpri.org/mailman/listinfo/fonc

Reply via email to