// ---------------------------------------------------------------------------
// - 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 <Class*> (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 <Class*> (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 <Closure*> (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 <Closure*> (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 <Closure*> (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;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1