boolobj.py

'''BOOL Objects. (Take many, many.)

classes:
    BoolDataBlock   (object)
    BoolCallFrame   (object)
    BoolPStack      (object)

    BoolTuple   (tuple)
    BoolParam   (BoolTuple)
    BoolAddr    (BoolTuple)
    BoolLst     (BoolTuple)
    BoolMsg     (BoolTuple)
    BoolTor     (BoolTuple)
    BoolAction  (BoolTuple)
    BoolObj     (BoolTuple)
    BoolRef     (BoolTuple)
    BoolModel   (BoolTuple)

    BoolContextObject (object)

Coder@Sonnack.com
June 20, 2017
'''
####################################################################################################
from __future__ import print_function
from sys import stdoutstderrargv
from datetime import datetime
from hashlib import md5 as StringHash
from logger import loggerinfodebugtrace
####################################################################################################
Log = logger('BOOL')

NUL = 'NUL'
PAR = 'PAR'
OBJ = 'OBJ'
REF = 'REF'
LST = 'LST'
MSG = 'MSG'
TOR = 'TOR'
ACT = 'ACT'
MDL = 'MDL'
SYS = 'SYS'
EXT = 'EXT'
DEV = 'DEV'

Meta = [NULPAROBJREFLSTMSGTORACTMDLSYSEXTDEV]

Here = '!'

##================================================================================================##
def meta_name (ix):
    '''Return a meta-type name given an integer index.'''
    if not isinstance(ix,int):
        raise KeyError('Invalid Index Type!')
    if 0 <= ix < len(Meta):
        return Meta[ix]
    raise IndexError('Index Out Of Range!')

def meta_index (mtype):
    '''Return an integer index given a meta-type name.'''
    if not isinstance(mtype,str):
        raise KeyError('Invalid Meta-Type!')
    if mtype in Meta:
        return Meta.index(mtype)
    raise KeyError('Unknown Meta-Type!')

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolDataBlock (object):
    '''Manager for data memory allocation.'''

    def __init__ (selfdatum):
        self.datum = datum
        self.block = []

    def __len__ (self):
        return len(self.block)

    def __str__ (self):
        return 'DataBlock: %d item%s [%s]' % (len(self.block), 's' if len(self.block) == 1 else ''type(self.datum))

    def new_datum (selfdatum=None):
        new_datum = self.datum if datum == None else datum
        try:
            ix = self.block.index(None)
            self.block[ix] = new_datum
            return ix
        except ValueError:
            ix = len(self.block)
            self.block.append(new_datum)
            return ix

    def del_datum (selfix):
        self._index_validate(ix)
        self.block[ix] = None

    def get_datum (selfix):
        self._index_validate(ix)
        return self.block[ix]

    def set_datum (selfixdatum):
        self._index_validate(ix)
        self.block[ix] = datum

    def _index_validate (selfix):
        if ix < 0:
            raise Exception('Data Index below zero!: %d' % ix)
        if len(self.block) <= ix:
            raise Exception('Data Index beyond DataBlock size!: %d' % ix)


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolCallFrame (object):

    def __init__ (selfactionparent=None):
        self.parent = parent
        self.action = action
        self.name = action.name
        self.data = action.dataslots * [None]
        self.retn_flag = 0  # return from Action if non-zero
        self.loop_flag = 0  # break from list if non-zero #TODO# implement "continue"
        self.nest_level = 0
        Log.trace(str(self))

    def __len__ (self):
        return len(self.data)

    def __str__ (self):
        return 'CallFrame: %s [%d]' % (self.namelen(self.data))

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolPStack (object):
    '''\
BOOL Parameter Stack.

The Parameter Stack carries parameters into and out of Actions.

Usually, input parameters are references to Objects, but they
can be new Objects created on the fly (for pass by value).

Output parameters are often Objects created in the Action that
need to be transferred to the calling frame. This requirement,
and the pass by value ability, require the parameter stack be
capable of containing entire Objects like Address Space does.
This is in addition to holding references to Objects in the
usual Address Space.
'''
    Log = logger('PStack')

    def __init__ (self):
        self.Log.trace('init')
        self.stack = []

    def __len__ (self):
        '''Return count of items in stack.'''
        return len(self.stack)

    def __getitem__ (selfix):
        '''Return a reference to an item in the stack.'''
        return self.stack[ix]

    def push (selfobj):
        self.Log.trace('PStak::push: %s' % str(obj))
        if obj:
            self.stack.append(obj)

    def peek (self):
        n = len(self)
        if 0 < n:
            obj = self.stack[n-1]
            self.Log.trace('PStak::peek: %s' % str(obj))
            return obj
        raise BufferError,'PStack is empty!'

    def pop (self):
        if len(self):
            obj = self.stack.pop()
            self.Log.trace('PStak::pop: %s' % str(obj))
            return obj
        raise BufferError,'PStack is empty!'


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolTuple (tuple):
    '''Bool (extended) Tuple base class.'''
    Log = logger('Meta')

    def __new__ (cls, *args):
        return tuple.__new__(clsargs)

    def __init__ (self, *args):
        tuple.__init__(selfargs)

    def __getattr__ (selfname):
        if name in ['mtype','meta','metatype','objtype']: return self[0]
        raise AttributeError(BoolTuple.E_ATTR_DEF % name)

    def __setattr__ (selfnamevalue):
        if name in ['mtype','meta','metatype','objtype']:
            raise AttributeError(BoolTuple.E_ATTR_RO % name)
        tuple.__setattr__(selfnamevalue)

    E_ATTR_RO  = 'Attribute "%s" is read-only'
    E_ATTR_DEF = 'Attribute "%s" is not defined.'

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolParam (BoolTuple):
    '''Bool (PStack) Parameter class.'''

    def __new__ (clsmodeldkey):
        return BoolTuple.__new__(clsPARmodeldkey)

    def __init__ (selfmodeldkey):
        BoolTuple.__init__(selfPARmodeldkey)

    def __getattr__ (selfname):
        if name == 'model': return self[1]
        if name == 'dkey': return self[2]
        raise AttributeError(BoolTuple.E_ATTR_DEF % name)

    def __setattr__ (selfnamevalue):
        if name in ['model','dkey']:
            raise AttributeError('Attribute "%s" is read-only' % name)
        BoolTuple.__setattr__(selfnamevalue)

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolAddr (BoolTuple):
    '''Bool Address class. Just a named tuple: (ns, offset)'''

    def __new__ (clsoffsetns=Here):
        return BoolTuple.__new__(cls'addr'nsoffset)

    def __init__ (selfoffsetns=Here):
        BoolTuple.__init__(self'addr'nsoffset)
        # Validate...
        if not isinstance(ns,str):
            raise TypeError,'Address namespace must be a string!'
        if not isinstance(offset,int):
            raise TypeError,'Address value must be an integer!'

    def __getattr__ (selfname):
        if name == 'ns': return self[1]
        if name == 'offset': return self[2]
        return BoolTuple.__getattr__(selfname)

    def __setattr__ (selfnamevalue):
        if name in ['ns','offset']:
            raise AttributeError('Attribute "%s" is read-only' % name)
        BoolTuple.__setattr__(selfnamevalue)

    def __str__ (self):
        return '[%s:%04d]' % (self.nsself.offset)

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolLst (BoolTuple):
    '''BOOL List Object class.'''

    def __new__ (cls, *args, **kwargs):
        list_len = len(args)
        gate_expr = kwargs['gate_expr'if 'gate_expr' in kwargs else None
        props = kwargs['props'if 'props' in kwargs else None
        args = args+(props,)
        return BoolTuple.__new__(clsLSTgate_exprlist_len, *args)

    def __init__ (self, *args, **kwargs):
        BoolTuple.__init__(self, *args)

    def __getattr__ (selfname):
        if name == 'gate': return self[1]
        if name == 'size': return self[2]
        if name == 'items': return self[3:3+self.size]
        if name == 'props': return self[3+self.size]
        return BoolTuple.__getattr__(selfname)

    def __setattr__ (selfnamevalue):
        if name in ['gate','size','items','props']:
            raise AttributeError(BoolTuple.E_ATTR_RO % name)
        BoolTuple.__setattr__(selfnamevalue)

    def __str__ (self):
        s = '(%s items=%d  gate=%s)'
        t = (self.mtypeself.sizeself.gate if self.gate else '()')
        return s % t

    def test_gate (self):
        if self.gate:
            self.Log.trace('List Gate: %s' % str(self.gate))
            # Evaluate it...
            BoolContext.send_message('Q:'self.gate)
            obj = BoolContext.ps_pop()
            self.Log.debug('List Gate result: %s' % str(obj))
            # Get its Truth value...
            BoolContext.invoke_model(obj.model'true')
            obj = BoolContext.ps_pop()
            if obj.model != 'bool':
                raise RuntimeError('Expression result not a *bool object: *%s' % obj.model)
            mdl = BoolContext.get_model(obj.model)
            return mdl.get_value(obj.dkey)
        return True

    def receive_message (selfmsgns=Here):
        self.Log.trace('%sList (items=%d)' % (msgself.size))
        if len(self.items) != self.size:
            raise Exception('Invalid list size or length! (size:%d, len:%d)' % (self.size,len(self.items)))
        # If there's a Gate Expression,...
        if self.test_gate():
            for list_item in self.items:
                BoolContext.send_message(msglist_item)
                if BoolContext.cf.loop_flag:
                    BoolContext.cf.loop_flag = 0
                    break

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolMsg (BoolTuple):
    '''BOOL Message Object class.'''

    def __new__ (clsnametargetparam):
        return BoolTuple.__new__(clsMSGnametargetparam)

    def __init__ (selfnametargetparam):
        BoolTuple.__init__(selfMSGnametargetparam)

    def __getattr__ (selfname):
        if name == 'name': return self[1]
        if name == 'target': return self[2]
        if name == 'param': return self[3]
        return BoolTuple.__getattr__(selfname)

    def __setattr__ (selfnamevalue):
        if name in ['name','target','param']:
            raise AttributeError(BoolTuple.E_ATTR_RO % name)
        BoolTuple.__setattr__(selfnamevalue)

    def __str__ (self):
        s = '(%s %s: %s %s)'
        t = (self.mtypeself.nameself.targetself.param if self.param else ';')
        return s % t

    def receive_message (selfmsgns=Here):
        self.Log.trace('%sMessage: "%s"' %  (msgself.name))
        # Evaluate Param...
        if self.param:
            BoolContext.send_message('Q:'self.param)
        # Evaluate Target...
        BoolContext.send_message('Q:'self.target)
        # Get target object...
        obj = BoolContext.ps_peek()
        self.Log.trace('MSG: target object: %s' % str(obj))
        # Apply Message...
        BoolContext.invoke_model(obj.modelself.name)
        if msg in 'x:X:':
            obj_addr = BoolContext.ps_pop()
            ##? destroy(obj_addr)

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolTor (BoolTuple):
    '''BOOL Actor ("'Tor") Object class.'''

    def __new__ (clsnameparams):
        return BoolTuple.__new__(clsTORnameparams)

    def __init__ (self, *args):
        BoolTuple.__init__(selfTOR, *args)

    def __getattr__ (selfname):
        if name == 'name': return self[1]
        if name == 'params': return self[2]
        return BoolTuple.__getattr__(selfname)

    def __setattr__ (selfnamevalue):
        if name in ['name','params']:
            raise AttributeError(BoolTuple.E_ATTR_RO % name)
        BoolTuple.__setattr__(selfnamevalue)

    def __str__ (self):
        p = str(self.params[0])
        n = len(self.params[1:])
        r = (' +%d' % nif 0 < n else ''
        s = '(%s @%s  %s%s)'
        t = (self.mtypeself.namepr)
        return s % t

    def receive_message (selfmsgns=Here):
        self.Log.trace('%sActor: "%s" (params: %d)' % (msgself.namelen(self.params)))
        # Invoke Action...
        BoolContext.invoke_action(self.nameself.params)
        if msg in 'x:X:':
            obj_addr = BoolContext.ps_pop()
            ##? destroy(obj_addr)

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolAction (BoolTuple):
    '''BOOL Action Object class.'''

    def __new__ (clsname,sig,dslotsinitlist,exitlist,clausescodelistnamtab={}, props=None):
        return BoolTuple.__new__(clsACTname,sig,dslotsinitlist,exitlist,clausescodelistnamtabprops)

    def __init__ (self, *args, **kwargs):
        BoolTuple.__init__(selfACT, *args)

    def __getattr__ (selfname):
        if name == 'name': return self[1]
        if name == 'sig': return self[2]
        if name == 'dataslots': return self[3]
        if name == 'initlist': return self[4]
        if name == 'exitlist': return self[5]
        if name == 'clauses': return self[6]
        if name == 'codeblock': return self[7]
        if name == 'namtab': return self[8]
        if name == 'props': return self[9]
        return BoolTuple.__getattr__(selfname)

    def __setattr__ (selfnamevalue):
        if name in ['name','sig','dataslots','initlist','exitlist','clauses','codeblock','namtab','props']:
            raise AttributeError(BoolTuple.E_ATTR_RO % name)
        BoolTuple.__setattr__(selfnamevalue)

    def __str__ (self):
        s = '(%s @%s  inits=%d exits=%d clauses=%d cs=%d ds=%d)'
        t = (self.mtypeself.namelen(self.initlist), len(self.exitlist), len(self.clauses), len(self.codeblock), self.dataslots)
        return s % t

    def receive_message (selfmsgns=Here):
        pass

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolObj (BoolTuple):
    '''BOOL Instance Object class.'''

    def __new__ (clsmodeldxinit_list=Noneprops=None):
        return BoolTuple.__new__(clsOBJmodeldxinit_listprops)

    def __init__ (self, *args, **kwargs):
        BoolTuple.__init__(selfOBJ, *args)

    def __getattr__ (selfname):
        if name == 'model': return self[1]
        if name == 'dx': return self[2]
        if name == 'init': return self[3]
        if name == 'props': return self[4]
        return BoolTuple.__getattr__(selfname)

    def __setattr__ (selfnamevalue):
        if name in ['model','dx','init','props']:
            raise AttributeError(BoolTuple.E_ATTR_RO % name)
        BoolTuple.__setattr__(selfnamevalue)

    def __str__ (self):
        s = '(%s *%s #%d =%s)'
        t = (self.mtypeself.modelself.dxstr(self.init))
        return s % t

    def initialize (self):
        '''Initialize Object (use Init-List if present).'''
        mdl = BoolContext.find_model(self.model)
        if self.props and ('value' in self.props):
            datum = self.props['value']
            dkey = mdl.new_value(datum)
            BoolContext.cf.data[self.dx] = dkey
            self.Log.trace('OBJ init-property: %s' % dkey)
            return
        if self.init:
            BoolContext.send_message('Q:'self.init)
            obj = BoolContext.ps_pop()
            BoolContext.cf.data[self.dx] = obj.dkey
            self.Log.trace('OBJ init-list: %s' % str(obj))
            return
        dkey = mdl.new_value()
        BoolContext.cf.data[self.dx] = dkey
        self.Log.trace('OBJ initialize: %s' % dkey)

    def ps_push (selfns):
        cf = self.__get_cf(ns)
        t = BoolParam(self.modelcf.data[self.dx])
        BoolContext.ps_push(t)

    def __get_cf (selfns):
        return BoolContext.cf if ns == Here else BoolContext.find_frame(ns)

    def receive_message (selfmsgns=Here):
        self.Log.trace('%sOBJ: <%s>' % (msgself.model))
        if msg in 'x:X:':
            self.initialize()
            return
        if msg in 'q:Q:':
            self.ps_push(ns)
            return
        if msg in 'n:N:name:Name:':
            # Push a (new) *string value set to object name (if known)...
            return
        self.ps_push(ns)
        BoolContext.invoke_model(self.modelmsg)

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolRef (BoolTuple):
    '''BOOL Reference Object class.'''

    def __new__ (clsmodeldx):
        return BoolTuple.__new__(clsREFmodeldx)

    def __init__ (self, *args):
        BoolTuple.__init__(selfREF, *args)

    def __getattr__ (selfname):
        if name == 'model': return self[1]
        if name == 'dx': return self[2]
        return BoolTuple.__getattr__(selfname)

    def __setattr__ (selfnamevalue):
        if name in ['model','dx']:
            raise AttributeError(BoolTuple.E_ATTR_RO % name)
        BoolTuple.__setattr__(selfnamevalue)

    def __str__ (self):
        return '(%s *%s #%d)' % (self.mtypeself.modelself.dx)

    def initialize (self):
        '''Initialize Reference.'''
        mdl = BoolContext.find_model(self.model)
        dkey = mdl.new_value()
        BoolContext.cf.data[self.dx] = dkey
        self.Log.trace('REF::Initialize: %s' % dkey)

    def receive_value (self):
        '''Pop a value from the PStack into the Reference.'''
        obj = BooContext.ps_pop()
        if self.model in ['any','one','list']:
            ref = self.__get_ref(Here)
            ref[1] = obj.model
            ref[2] = obj.dkey
            self.Log.trace('REF::ReceiveValue: %s' % ref)
            return
        if self.model != obj.model:
            raise Exception('Type Mismatch: *%s = *%s' % (self.modelobj.model))
        BoolContext.cf.data[self.dx] = obj.dkey

    def ps_push (selfns):
        if self.model in ['any','one','list']:
            ref = self.__get_ref(ns)
            t = BoolParam(ref[1], ref[2])
            BoolContext.ps_push(t)
            return
        cf = self.__get_cf(Here)
        t = BoolParam(self.modelcf.data[self.dx])
        BoolContext.ps_push(t)

    def __get_ref (selfns):
        cf = self.__get_cf(ns)
        mdl = BoolContext.find_model(self.model)
        return mdl.get_value(cf.data[self.dx])

    def __get_cf (selfns):
        return BoolContext.cf if ns == Here else BoolContext.find_frame(ns)

    def receive_message (selfmsgns=Here):
        # TODO #
        # Problem(s) with References:
        # Actions have params; Model-Actions have pushed values.
        # And they need to remember and handle different passed models!
        self.Log.trace('%sREF: <%s>' % (msgself.model))
        if msg in 'x:X:':
            self.initialize()
            return
        if msg in 'r:R:':
            self.receive_value()
            return
        if msg in 'q:Q:':
            self.ps_push(ns)
            return
        self.ps_push(ns)
        BoolContext.invoke_model(self.modelmsg)

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolModel (BoolTuple):
    '''BOOL Model Object class.'''

    def __new__ (clsnamedata_mgractionsnamtab={}, props=None):
        return BoolTuple.__new__(clsMDLnamedata_mgractionsnamtabprops)

    def __init__ (self, *args, **kwargs):
        BoolTuple.__init__(selfMDL, *args)

    def __getattr__ (selfname):
        if name == 'name': return self[1]
        if name == 'ds': return self[2]
        if name == 'actions': return self[3]
        if name == 'namtab': return self[4]
        if name == 'props': return self[5]
        return BoolTuple.__getattr__(selfname)

    def __setattr__ (selfnamevalue):
        if name in ['name','ds','actions','namtab','props']:
            raise AttributeError(BoolTuple.E_ATTR_RO % name)
        BoolTuple.__setattr__(selfnamevalue)

    def __str__ (self):
        s = '(%s *%s  ds=%s actions=%d)'
        t = (self.mtypeself.nameself.dslen(self.actions))
        return s % t

    def new_value (selfdatum=None):
        dkey = self.ds.new_datum(datum)
        self.Log.trace('Model::new-value: %s = %s' % (dkey,str(datum)))
        return dkey

    def get_value (selfdkey):
        self.Log.trace('Model::get-value: %s' % dkey)
        return self.ds.get_datum(dkey)

    def set_value (selfdkeydatum):
        self.Log.trace('Model::set-value: %s = %s' % (dkey,str(datum)))
        self.ds.set_datum(dkeydatum)

    def del_value (selfdkey):
        self.ds.del_datum(dkey)

    def find_action (selfmsg_name):
        if msg_name in self.actions:
            return self.actions[msg_name]
        return None

    def receive_message (selfmsgns=Here):
        pass

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class BoolContextObject (object):
    Log = logger('CTX')

    def __init__ (self):
        self.ps = BoolPStack()
        self.cf = BoolCallFrame(BoolAction('_root_','',0,[],[],[],[]))
        self.actions = {}
        self.models = {}

    def ps_push (selfobj):
        self.ps.push(obj)
    def ps_pop (self):
        return self.ps.pop()
    def ps_peek (self):
        return self.ps.peek()

    def ps_push_value (selfmodel_namedatumuse_mdl=None):
        mdl = use_mdl if use_mdl else self.find_model(model_name)
        val = mdl.new_value(datum)
        arg = BoolParam(mdl.nameval)
        self.ps_push(arg)

    def cf_push (selfaction):
        old_cf = self.cf
        self.cf = BoolCallFrame(actionparent=old_cf)
    def cf_pop (self):
        self.cf = self.cf.parent

    def load_actions (selfactions):
        for a in actions:
            self.actions[a.name] = a
    def load_models (selfmodels):
        for m in models:
            self.models[m.name] = m

    def find_action (selfaction_name):
        if action_name in self.actions:
            return self.actions[action_name]
        return None
    def find_model (selfmodel_name):
        if model_name in self.models:
            return self.models[model_name]
        return None

    def find_frame (selfname):
        cf = self.cf
        while cf.name != '_root_':
            if cf.name == name:
                return cf
            # Next Call Frame...
            cf = cf.parent
        return None

    def search_frame (selfname):
        self.Log.trace('search-frame: key=%s' % name)
        cf = self.cf
        while cf.name != '_root_':
            self.Log.trace('search-frame: CF=%s' % cf.name)
            act = cf.action
            if name in act.namtab:
                return act.namtab[name]
            # Next Call Frame...
            cf = cf.parent
        return None

    def find_object (selfaddress):
        self.Log.trace('FindObject: %s' % str(address))
        # Detect "NULL" addresses...
        if address.offset < 0:
            raise Exception('Illegal Address: %s' % str(address))
        # Assume the current Call Frame...
        cf = self.cf
        # But if not, find the CF...
        if address.ns != Here:
            while address.ns != cf.name:
                cf = cf.parent
                if cf.name == '_root_':
                    raise Exception('Address not found: %s'str(address))
        # Get Object...
        code = cf.action.codeblock
        if len(code) <= address.offset:
            raise Exception('Illegal Address: %s' % str(address))
        return code[address.offset]

    def send_message (selfmsgobj_addr):
        '''Send-Message (message-name, target-address)'''
        self.Log.debug('SendMesg: "%s" -> %s' % (msgobj_addr))
        if msg in 'z:Z:nop:':
            return
        if msg in 'w:W:where:addr:':
            self.ps_push(obj_addr)
            return
        obj = self.find_object(obj_addr)
        self.Log.trace('Dispatch Message to: %s' % str(obj))
        obj.receive_message(msgns=obj_addr.ns)

    def invoke_model (selfmdl_namemsg):
        '''Invoke-Model (model-name, message-name)'''
        self.Log.debug('Invoke-Model: "%s" msg=%s' % (mdl_name,msg))
        # Get the Model...
        mdl = self.find_model(mdl_name)
        if not mdl:
            raise Exception('Model Not Found: *%s' % model.name)
        # Invoke Model-Action...
        self.invoke_model_action(mdlmsg)

    def invoke_model_action (selfmdlmsg):
        '''Invoke-Model-Action (model-object, message-name)'''
        self.Log.info()
        self.Log.info('Invoke-Model-Action: "%s_%s"' % (mdl.name,msg))
        # Get Model-Action...
        act = mdl.find_action(msg)
        if not act:
            raise Exception('Model-Action Not Found: "%s:(*%s)"' % (msg,mdl.name))
        # Native Model-Actions...
        native = mdl.props and ('native' in mdl.props)
        if native:
            self.Log.trace('Native Model-Action: ENTER')
            act(mdlmsg)
            self.Log.trace('Native Model-Action: EXIT')
            self.Log.trace()
            return
        # New Call Frame...
        self.Log.trace('Model-Action: %s' % str(act))
        self.cf_push(act)
        for addr in act.initlist:
            self.send_message('X:'addr)
        for addr in act.exitlist:
            self.send_message('X:'addr)
        # Load passed params (on PStack) into Reference objects...
        for addr in act.initlist:
            self.send_message('R:'addr)
        # Execute Action Clause...
        main_clause = act.clauses[0]
        self.Log.debug('EXECUTE ACTION MAIN CLAUSE')
        self.Log.trace(main_clause)
        self.send_message('X:'main_clause[2])
        # Put exit values on the PStack...
        for addr in act.exitlist:
            self.send_message('Q:'addr)
        # Remove the Call Frame...
        self.cf_pop()
        self.Log.debug('exit Model-Action: "%s"' % act.name)
        self.Log.debug()

    def invoke_action (selfact_nameparams):
        '''Invoke-Action (action-name, params-list)'''
        self.Log.info()
        self.Log.info('Invoke-Action: "%s"' % act_name)
        act = self.find_action(act_name)
        if not act:
            raise Exception('Action Not Found: @%s' % act_name)
        # Native Actions...
        native = act.props and ('native' in act.props)
        if native:
            self.Log.trace('Native Action: ENTER')
            code = act.props['code']
            code(actparams)
            self.Log.trace('Native Action: EXIT')
            self.Log.trace()
            return
        # TODO #
        # Much, yet (e.g. multi-clause parameters!)...
        #
        # New Call Frame...
        self.Log.trace('Action: %s' % str(act))
        self.cf_push(act)
        for addr in act.initlist:
            self.send_message('X:'addr)
        for addr in act.exitlist:
            self.send_message('X:'addr)
        # Execute Action Clauses...
        clauses = list(act.clauses)
        # Main clause (always)...
        main_clause = clauses.pop(0)
        main_params = params.pop(0)
        if main_clause[0] != main_params[0]:
            raise Exception('Main Clause Parameter mismatch! [%s/%s]' % (main_clause[0]))
        self.Log.debug('EXECUTE ACTION MAIN CLAUSE')
        self.Log.trace(main_clause)
        self.Log.trace(main_params)
        self.send_message('X:'main_clause[2])
        # Sub-clauses (if provided by inputs)...
        if 0 < len(params):
            sub_params = params.pop(0)
            for sub_clause in clauses:
                while sub_clause[0] == sub_params[0]:
                    self.Log.debug('EXECUTE ACTION SUB CLAUSE')
                    self.Log.trace(sub_clause)
                    self.Log.trace(sub_params)
                    self.send_message('X:'sub_clause[2])
                    # More parameters?...
                    if len(params) == 0:
                        break
                    sub_params = params.pop(0)
            if 0 < len(params):
                raise Exception('Action did not use all parameter clauses!')
        # Put exit values on the PStack...
        for addr in act.exitlist:
            self.send_message('Q:'addr)
        # Remove the Call Frame...
        self.cf_pop()
        self.Log.debug('exit Action: "%s"' % act.name)
        self.Log.debug()

##------------------------------------------------------------------------------------------------##
BoolContext = BoolContextObject()


####################################################################################################
if __name__ == '__main__':
    pass
####################################################################################################
###eof###