diff --git a/src/cbaselib.c b/src/cbaselib.c index cf60cb9..f8d29c5 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -136,10 +136,99 @@ void cosmoB_loadLibrary(CState *state) { cosmoV_register(state, i); // load other libraries + cosmoB_loadObjLib(state); cosmoB_loadStrLib(state); cosmoB_loadMathLib(state); } +// ================================================================ [OBJECT.*] ================================================================ + +int cosmoB_osetProto(CState *state, int nargs, CValue *args) { + if (nargs == 2) { + CObj *obj = cosmoV_readObj(args[0]); // object to set proto too + CObjObject *proto = cosmoV_readObject(args[1]); + + obj->proto = proto; // boom done + } else { + cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); + } + + return 0; // nothing +} + +int cosmoB_ogetProto(CState *state, int nargs, CValue *args) { + if (nargs != 1) { + cosmoV_error(state, "Expected 1 argument, got %d!", nargs); + return 0; + } + + cosmoV_pushObj(state, (CObj*)cosmoV_readObject(args[0])->_obj.proto); // just return the proto + + return 1; // 1 result +} + +int cosmoB_oisChild(CState *state, int nargs, CValue *args) { + if (nargs != 2) { + cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs); + return 0; + } + + if (!IS_OBJ(args[0]) || !IS_OBJECT(args[1])) { + cosmoV_typeError(state, "oject.ischild()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + return 0; + } + + CObj *obj = cosmoV_readObj(args[0]); + CObjObject *proto = cosmoV_readObject(args[1]); + + // push result + cosmoV_pushBoolean(state, cosmoO_isDescendant(obj, proto)); + return 1; +} + +COSMO_API void cosmoB_loadObjLib(CState *state) { + const char *identifiers[] = { + "ischild" + }; + + CosmoCFunction objLib[] = { + cosmoB_oisChild + }; + + // make object library object + cosmoV_pushString(state, "object"); + + // make __getter object for debug proto + cosmoV_pushString(state, "__getter"); + + // key & value pair + cosmoV_pushString(state, "__proto"); // key + cosmoV_pushCFunction(state, cosmoB_ogetProto); // value + + cosmoV_makeTable(state, 1); + + // make __setter table + cosmoV_pushString(state, "__setter"); + + cosmoV_pushString(state, "__proto"); + cosmoV_pushCFunction(state, cosmoB_osetProto); + + cosmoV_makeTable(state, 1); + + int i; + for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { + cosmoV_pushString(state, identifiers[i]); + cosmoV_pushCFunction(state, objLib[i]); + } + + // make the object and set the protoobject for all runtime-allocated objects + CObjObject *obj = cosmoV_makeObject(state, i + 2); // + 2 for the getter/setter tables + cosmoV_registerProtoObject(state, COBJ_OBJECT, obj); + + // register "object" to the global table + cosmoV_register(state, 1); +} + // ================================================================ [STRING.*] ================================================================ // string.sub @@ -432,32 +521,6 @@ void cosmoB_loadMathLib(CState *state) { cosmoV_register(state, 1); } -// ================================================================ [DEBUG] ================================================================ - -int cosmoB_dsetProto(CState *state, int nargs, CValue *args) { - if (nargs == 2) { - CObj *obj = cosmoV_readObj(args[0]); // object to set proto too - CObjObject *proto = cosmoV_readObject(args[1]); - - obj->proto = proto; // boom done - } else { - cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); - } - - return 0; // nothing -} - -int cosmoB_dgetProto(CState *state, int nargs, CValue *args) { - if (nargs != 1) { - cosmoV_error(state, "Expected 1 argument, got %d!", nargs); - return 0; - } - - cosmoV_pushObj(state, (CObj*)cosmoV_readObject(args[0])->_obj.proto); // just return the proto - - return 1; // 1 result -} - // ================================================================ [VM.*] ================================================================ // vm.__getter["globals"] @@ -549,31 +612,7 @@ int cosmoB_vcollect(CState *state, int nargs, CValue *args) { return 0; } -void cosmoB_loadDebug(CState *state) { - // make __getter object for debug proto - cosmoV_pushString(state, "__getter"); - - // key & value pair - cosmoV_pushString(state, "__proto"); // key - cosmoV_pushCFunction(state, cosmoB_dgetProto); // value - - cosmoV_makeTable(state, 1); - - // make __setter object - cosmoV_pushString(state, "__setter"); - - cosmoV_pushString(state, "__proto"); - cosmoV_pushCFunction(state, cosmoB_dsetProto); - - cosmoV_makeTable(state, 1); - - // we call makeObject leting it know there are 2 sets of key & value pairs on the stack - CObjObject *obj = cosmoV_makeObject(state, 2); - - // set debug protos to the debug object - cosmoV_registerProtoObject(state, COBJ_OBJECT, obj); - cosmoV_pop(state); // pops the debug object - +void cosmoB_loadVM(CState *state) { // make vm.* object cosmoV_pushString(state, "vm"); @@ -612,5 +651,5 @@ void cosmoB_loadDebug(CState *state) { // register "vm" to the global table cosmoV_register(state, 1); - printf("[WARNING] the debug library has been loaded!\n"); + printf("[WARNING] the vm.* library has been loaded!\n"); } diff --git a/src/cbaselib.h b/src/cbaselib.h index 3b486cd..db49b08 100644 --- a/src/cbaselib.h +++ b/src/cbaselib.h @@ -5,17 +5,24 @@ /* loads all of the base library, including: - base library ("print", "assert", "type", "pcall", "loadstring", etc.) + - object library - string library - math library */ COSMO_API void cosmoB_loadLibrary(CState *state); +/* loads the base object library, including: + - object.ischild or :ischild() + +*/ +COSMO_API void cosmoB_loadObjLib(CState *state); + /* loads the base string library, including: - - string.sub - - stirng.find - - string.split - - string.byte - - string.char + - string.sub & :sub() + - stirng.find & :find() + - string.split & :split() + - string.byte & :byte() + - string.char & :char() The base proto object for strings is also set, allowing you to invoke the string.* api through string objects, eg. `"hello world":split(" ")` is equivalent to `string.split("hello world", " ")` @@ -29,17 +36,14 @@ COSMO_API void cosmoB_loadStrLib(CState *state); */ COSMO_API void cosmoB_loadMathLib(CState *state); -/* sets the base proto of all objects to the debug proto which allows for - - manipulation of the ProtoObject of objects through the '__proto' field - - additionally, the vm.* library is loaded, including: +/* loads the vm library, including: - manually setting/grabbing base protos of any object (vm.baseProtos) - manually setting/grabbing the global table (vm.globals) - manually invoking a garbage collection event (vm.collect()) for this reason, it is recommended to NOT load this library in production */ -COSMO_API void cosmoB_loadDebug(CState *state); +COSMO_API void cosmoB_loadVM(CState *state); #define cosmoV_typeError(state, name, expectedTypes, formatStr, ...) \ cosmoV_error(state, name " expected (" expectedTypes "), got (" formatStr ")!", __VA_ARGS__); diff --git a/src/cobj.c b/src/cobj.c index 2901fd9..fd18ba2 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -288,6 +288,20 @@ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) return cosmoV_readString(*start); // start should be state->top - 1 } +// walks the protos of obj and checks for proto +bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) { + CObjObject *curr = obj->proto; + + while (curr != NULL) { + if (curr == proto) + return true; // found proto! return true + + curr = ((CObj*)curr)->proto; + } + + // we didn't find the proto + return false; +} // returns false if error thrown bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) { diff --git a/src/cobj.h b/src/cobj.h index 08ca358..b83031b 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -155,6 +155,9 @@ static inline CObjObject *cosmoO_grabProto(CObj *obj) { return obj->type == COBJ_OBJECT ? (CObjObject*)obj : obj->proto; } +// walks the protos of obj and checks for proto +bool cosmoO_isDescendant(CObj *obj, CObjObject *proto); + bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj); void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj); bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val); diff --git a/src/cparse.c b/src/cparse.c index 02fce5b..ecc2db0 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -1433,7 +1433,7 @@ static void forLoop(CParseState *pstate) { // parse initializer if (!match(pstate, TOKEN_EOS)) { expressionStatement(pstate); - consume(pstate, TOKEN_EOS, "Expected ';' after initializer!"); + consume(pstate, TOKEN_EOS, "Expected ';' after initializer"); } // start loop scope diff --git a/src/main.c b/src/main.c index ecc1284..2e44349 100644 --- a/src/main.c +++ b/src/main.c @@ -52,7 +52,7 @@ static void repl() { CState *state = cosmoV_newState(); cosmoB_loadLibrary(state); - cosmoB_loadDebug(state); + cosmoB_loadVM(state); // add our custom REPL functions cosmoV_pushString(state, "quit");