// --------------------------------------------------------------------------- // - Instance.cpp - // - afnix engine - afnix instance class implementation - // --------------------------------------------------------------------------- // - This program is free software; you can redistribute it and/or modify - // - it provided that this copyright notice is kept intact. - // - - // - This program is distributed in the hope that it will be useful, but - // - without any warranty; without even the implied warranty of - // - merchantability or fitness for a particular purpose. In no event shall - // - the copyright holder be liable for any direct, indirect, incidental or - // - special damages arising in any way out of the use of this software. - // --------------------------------------------------------------------------- // - copyright (c) 1999-2007 amaury darsch - // --------------------------------------------------------------------------- #include "Cons.hpp" #include "Vector.hpp" #include "Method.hpp" #include "Closure.hpp" #include "Runnable.hpp" #include "Instance.hpp" #include "Exception.hpp" namespace afnix { // ------------------------------------------------------------------------- // - private section - // ------------------------------------------------------------------------- // the instance eval quarks static const long QUARK_THIS = String::intern ("this"); static const long QUARK_META = String::intern ("meta"); static const long QUARK_MUTE = String::intern ("mute"); static const long QUARK_SUPER = String::intern ("super"); static const long QUARK_PRESET = String::intern ("preset"); // ------------------------------------------------------------------------- // - class section - // ------------------------------------------------------------------------- // create a default instance Instance::Instance (void) { p_iset = nilp; p_meta = nilp; d_ctmta = false; p_super = nilp; d_ctsup = false; } // create an instance wih a meta class Instance::Instance (Class* meta) { p_iset = nilp; p_meta = nilp; d_ctmta = false; p_super = nilp; d_ctsup = false; setmeta (meta, false); } // destroy this instance Instance::~Instance (void) { if (p_iset != nilp) p_iset->reset (); Object::dref (p_iset); Object::dref (p_meta); Object::dref (p_super); } // return the class name String Instance::repr (void) const { return "Instance"; } // make this instance a shared object void Instance::mksho (void) { if (p_shared != nilp) return; Object::mksho (); if (p_meta != nilp) p_meta->mksho (); if (p_super != nilp) p_super->mksho (); if (p_iset != nilp) p_iset->mksho (); } // set the instance meta class Object* Instance::setmeta (Object* object, const bool flag) { // check for a class Class* meta = dynamic_cast (object); if ((meta == nilp) && (object != nilp)) { throw Exception ("type-error", "invalid object to set as meta class", Object::repr (object)); } // check for constant if (d_ctmta == true) { unlock (); throw Exception ("const-error", "const violation with meta class"); } // set the meta class Object::iref (meta); Object::dref (p_meta); p_meta = meta; d_ctmta = flag; unlock (); return meta; } // set the instance super value Object* Instance::setsuper (Object* object, const bool flag) { wrlock (); if (d_ctsup == true) { unlock (); throw Exception ("const-error", "const violation with super member"); } Object::iref (object); Object::dref (p_super); p_super = object; d_ctsup = flag; unlock (); return object; } // ------------------------------------------------------------------------- // - object section - // ------------------------------------------------------------------------- // create a new object in a generic way Object* Instance::mknew (Vector* argv) { // get the number of arguments long argc = (argv == nilp) ? 0 : argv->length (); // check 0 argument if (argc == 0) return new Instance; // illegal arguments throw Exception ("argument-error", "too many arguments with instance"); } // return true if the given quark is defined bool Instance::isquark (const long quark, const bool hflg) const { // check for local quarks if (quark == QUARK_THIS) return true; if (quark == QUARK_META) return true; if (quark == QUARK_MUTE) return true; if (quark == QUARK_SUPER) return true; if (quark == QUARK_PRESET) return true; // lock and check in the instance set rdlock (); if ((p_iset != nilp) && (p_iset->exists (quark) == true)) { unlock (); return true; } // check in the class if ((p_meta != nilp) && (p_meta->isquark (quark, hflg) == true)) { unlock (); return true; } // check in the super instance if hierarchical if (p_super != nilp) { bool result = hflg ? p_super->isquark (quark, hflg) : false; unlock (); return result; } // check in the instance base object bool result = hflg ? Object::isquark (quark, hflg) : false; unlock (); return result; } // preset the instance with a set of arguments Object* Instance::pdef (Runnable* robj, Nameset* nset, Cons* args) { wrlock (); // clean any remaining localset if (p_iset != nilp) { p_iset->reset (); Object::dref (p_iset); p_iset= nilp; } // check if we have a meta class if (p_meta == nilp) { unlock (); return nilp; } // evaluate the arguments in the original nameset Cons* carg = nilp; try { carg = Cons::mkcons (robj, nset, args); } catch (...) { delete carg; unlock (); throw; } // create the instance local set and bind this Object::iref (p_iset = new Localset); p_iset->symcst (QUARK_THIS, this); // bind the default symbols from the meta class try { const Qarray& mdata = p_meta->getmdata (); if (mdata.length () != 0) { long len = mdata.length (); for (long i = 0; i < len; i++) p_iset->symdef (mdata.get (i), (Object*) nilp); } } catch (...) { unlock (); throw; } // evaluate the preset method try { // get the form Object* iobj = p_meta->find (QUARK_PRESET); Object* form = (iobj == nilp) ? nilp : iobj->eval (robj, nset); // compute the result Object* result = nilp; if (form != nilp) { p_iset->setparent (nset); result = form->apply (robj, p_iset, carg); p_iset->setparent ((Nameset*) nilp); } robj->post (result); // clean the localset Object::iref (this); p_iset->remove (QUARK_THIS); Object::tref (this); // clean evaluated args and unlock delete carg; unlock (); return result; } catch (...) { // clean the localset Object::iref (this); p_iset->remove (QUARK_THIS); Object::tref (this); // clear evaluated args and unlock delete carg; unlock (); throw; } } // mute the instance with a set of arguments Object* Instance::mute (Runnable* robj, Nameset* nset, Cons* args) { wrlock (); try { // trivial check first if ((args == nilp) || (args->length () < 1)) { throw Exception ("argument-error", "missing or too many arguments with mute"); } // try to get the class Object* car = args->getcar (); Object* obj = (car == nilp) ? nilp : car->eval (robj, nset); Class* cls = dynamic_cast (obj); if (cls == nilp) { throw Exception ("type-error", "invalid object to set as meta class", Object::repr (obj)); } // set the meta class setmeta (cls, false); // call the preset method with the remaining arguments Cons* cdr = args->getcdr (); Object* result = pdef (robj, nset, cdr); robj->post (result); unlock (); return result; } catch (...) { unlock (); throw; } } // create a const object by quark Object* Instance::cdef (Runnable* robj, Nameset* nset, const long quark, Object* object) { // check for reserved quark if (quark == QUARK_META) return setmeta (object, true); // check for the super quark if (quark == QUARK_SUPER) return setsuper (object, true); wrlock (); try { // create a local instance set if needed if (p_iset == nilp) { Object::iref (p_iset = new Localset); if ((p_shared != nilp) && (p_iset != nilp)) p_iset->mksho (); } // get the object from the localset Object* result = p_iset->cdef (robj, nset, quark, object); robj->post (result); unlock (); return result; } catch (...) { unlock (); throw; } } // create an object by quark Object* Instance::vdef (Runnable* robj, Nameset* nset, const long quark, Object* object) { // check for reserved quark if (quark == QUARK_META) return setmeta (object, false); // check for super if (quark == QUARK_SUPER) return setsuper (object, false); wrlock (); try { // create a local instance set if needed if (p_iset == nilp) { Object::iref (p_iset = new Localset); if ((p_shared != nilp) && (p_iset!= nilp)) p_iset->mksho (); } // look in the instance local set if (p_iset != nilp) { Object* iobj = p_iset->find (quark); if (iobj != nilp) { Object* result = iobj->vdef (robj, nset, object); robj->post (result); unlock (); return result; } } // look in the class if (p_meta != nilp) { Object* cobj = p_meta->find (quark); if (cobj != nilp) { Object* result = cobj->vdef (robj, nset, object); robj->post (result); unlock (); return result; } } // bind locally if (p_iset != nilp) { Object* result = p_iset->vdef (robj, nset, quark, object); robj->post (result); unlock (); return result; } throw Exception ("instance-error", "cannot access local instance set"); } catch (...) { unlock (); throw; } } // evaluate an instance member Object* Instance::eval (Runnable* robj, Nameset* nset, const long quark) { rdlock (); // check for super if (quark == QUARK_SUPER) { Object* result = p_super; robj->post (result); unlock (); return result; } // check for meta and set-meta if (quark == QUARK_META) { Object* result = p_meta; robj->post (result); unlock (); return result; } if (quark == QUARK_MUTE) { unlock (); return new Method (quark, this, false); } unlock (); try { wrlock (); // create a local instance set if needed if (p_iset == nilp) { Object::iref (p_iset = new Localset); if ((p_shared != nilp) && (p_iset != nilp)) p_iset->mksho (); } unlock (); rdlock (); // check in the instance local set if (p_iset != nilp) { Object* obj = p_iset->find (quark); if (obj != nilp) { // evaluate the result object Object* result = obj->eval (robj, nset); // check for a closure if (dynamic_cast (result) == nilp) { robj->post (result); unlock (); return result; } // default to a method Object* method = new Method (result, this); robj->post (method); unlock (); return method; } } // check in the class if (p_meta != nilp) { Object* obj = p_meta->find (quark); if (obj != nilp) { // evaluate the result object Object* result = obj->eval (robj, nset); // check for a closure if (dynamic_cast (result) == nilp) { robj->post (result); unlock (); return result; } // default to a method Object* method = new Method (result, this); robj->post (method); unlock (); return method; } } // check in the super instance if (p_super != nilp) { // evaluate the result object Object* result = p_super->eval (robj, nset, quark); // check for a closure if (dynamic_cast (result) == nilp) { robj->post (result); unlock (); return result; } // default to a mathod Object* method = new Method (result, this); robj->post (method); unlock (); return method; } // last resort is a method Object* method = new Method (quark, this, true); robj->post (method); unlock (); return method; } catch (...) { unlock (); throw; } } // apply an object with a set of arguments by quark Object* Instance::apply (Runnable* robj, Nameset* nset, const long quark, Cons* args) { // check for set-meta quark if (quark == QUARK_MUTE) { wrlock (); try { Object* result = mute (robj, nset, args); robj->post (result); unlock (); return result; } catch (...) { unlock (); throw; } } // evaluate and apply Object* obj = eval (robj, nset, quark); return apply (robj, nset, obj, args); } // apply an object with an object within this instance Object* Instance::apply (Runnable* robj, Nameset* nset, Object* object, Cons* args) { // basic reject - as usual if (object == nilp) return nilp; // create a local instance set if needed wrlock (); Localset* lset = nilp; try { if (p_iset == nilp) { Object::iref (p_iset = new Localset); if ((p_shared != nilp) && (p_iset != nilp)) p_iset->mksho (); } // rebind the local set before the call lset = new Localset (p_iset); lset->setparent (nset); lset->symcst (QUARK_THIS, this); } catch (...) { unlock (); throw; } // let's make the call try { Object* result = object->apply (robj, lset, args); robj->post (result); lset->reset (); delete lset; unlock (); return result; } catch (...) { lset->reset (); delete lset; unlock (); throw; } } }