diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..93256d7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,26 @@ +--- +Language: Cpp +# BasedOnStyle: Mozilla +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: Right +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortBlocksOnASingleLine: Never +AllowShortIfStatementsOnASingleLine: Never +AlwaysBreakAfterReturnType: None +BreakBeforeBraces: Mozilla +IndentWidth: 4 +ColumnLimit: 100 +IncludeBlocks: Regroup +IndentPPDirectives: AfterHash +... + diff --git a/examples/stress.cosmo b/examples/stress.cosmo index a45cf3f..6c76cdf 100644 --- a/examples/stress.cosmo +++ b/examples/stress.cosmo @@ -9,7 +9,7 @@ proto Test end // stressing the GC -for (var i = 0; ; i++) do +for (var i = 0; i < 100000; i++) do var x = Test("Hello world " .. i) x:print() end \ No newline at end of file diff --git a/src/cbaselib.c b/src/cbaselib.c index fe2da59..450f854 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -1,15 +1,18 @@ #include "cbaselib.h" -#include "cvm.h" -#include "cvalue.h" -#include "cobj.h" + #include "cmem.h" +#include "cobj.h" +#include "cvalue.h" +#include "cvm.h" #include #include -// ================================================================ [BASELIB] ================================================================ +// ================================================================ [BASELIB] +// ================================================================ -int cosmoB_print(CState *state, int nargs, CValue *args) { +int cosmoB_print(CState *state, int nargs, CValue *args) +{ for (int i = 0; i < nargs; i++) { if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString CObjString *str = cosmoV_toString(state, args[i]); @@ -23,7 +26,8 @@ int cosmoB_print(CState *state, int nargs, CValue *args) { return 0; // print doesn't return any args } -int cosmoB_assert(CState *state, int nargs, CValue *args) { +int cosmoB_assert(CState *state, int nargs, CValue *args) +{ if (nargs < 1 || nargs > 2) { cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs); return 0; // nothing pushed onto the stack to return @@ -31,7 +35,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) { if (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) { if (nargs == 2) { - cosmoV_typeError(state, "assert()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "assert()", ", ", "%s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); } else { cosmoV_typeError(state, "assert()", "", "%s", cosmoV_typeStr(args[0])); } @@ -44,7 +49,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) { return 0; } -int cosmoB_type(CState *state, int nargs, CValue *args) { +int cosmoB_type(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "type() expected 1 argument, got %d!", nargs); return 0; @@ -55,7 +61,8 @@ int cosmoB_type(CState *state, int nargs, CValue *args) { return 1; // 1 return value, the type string :D } -int cosmoB_pcall(CState *state, int nargs, CValue *args) { +int cosmoB_pcall(CState *state, int nargs, CValue *args) +{ if (nargs < 1) { cosmoV_error(state, "pcall() expected at least 1 argument!"); return 0; @@ -65,7 +72,7 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) { cosmoM_unfreezeGC(state); // call the passed callable - COSMOVMRESULT res = cosmoV_pcall(state, nargs-1, 1); + COSMOVMRESULT res = cosmoV_pcall(state, nargs - 1, 1); // insert false before the result cosmo_insert(state, 0, cosmoV_newBoolean(res == COSMOVM_OK)); @@ -75,7 +82,8 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) { return 2; } -int cosmoB_tonumber(CState *state, int nargs, CValue *args) { +int cosmoB_tonumber(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs); return 0; @@ -85,17 +93,19 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_tostring(CState *state, int nargs, CValue *args) { +int cosmoB_tostring(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs); return 0; } - cosmoV_pushRef(state, (CObj*)cosmoV_toString(state, args[0])); + cosmoV_pushRef(state, (CObj *)cosmoV_toString(state, args[0])); return 1; } -int cosmoB_loadstring(CState *state, int nargs, CValue *args) { +int cosmoB_loadstring(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs); return 0; @@ -113,7 +123,8 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args) { return 2; // , or } -int cosmoB_error(CState *state, int nargs, CValue *args) { +int cosmoB_error(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "error() expected 1 argument, got %d!", nargs); return 0; @@ -129,31 +140,16 @@ int cosmoB_error(CState *state, int nargs, CValue *args) { return 0; } -void cosmoB_loadLibrary(CState *state) { - const char *identifiers[] = { - "print", - "assert", - "type", - "pcall", - "tonumber", - "tostring", - "loadstring", - "error" - }; +void cosmoB_loadLibrary(CState *state) +{ + const char *identifiers[] = {"print", "assert", "type", "pcall", + "tonumber", "tostring", "loadstring", "error"}; - CosmoCFunction baseLib[] = { - cosmoB_print, - cosmoB_assert, - cosmoB_type, - cosmoB_pcall, - cosmoB_tonumber, - cosmoB_tostring, - cosmoB_loadstring, - cosmoB_error - }; + CosmoCFunction baseLib[] = {cosmoB_print, cosmoB_assert, cosmoB_type, cosmoB_pcall, + cosmoB_tonumber, cosmoB_tostring, cosmoB_loadstring, cosmoB_error}; int i; - for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { + for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) { cosmoV_pushString(state, identifiers[i]); cosmoV_pushCFunction(state, baseLib[i]); } @@ -167,9 +163,11 @@ void cosmoB_loadLibrary(CState *state) { cosmoB_loadMathLib(state); } -// ================================================================ [OBJECT.*] ================================================================ +// ================================================================ [OBJECT.*] +// ================================================================ -int cosmoB_osetProto(CState *state, int nargs, CValue *args) { +int cosmoB_osetProto(CState *state, int nargs, CValue *args) +{ if (nargs == 2) { CObj *obj = cosmoV_readRef(args[0]); // object to set proto too CObjObject *proto = cosmoV_readObject(args[1]); @@ -182,25 +180,28 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args) { return 0; // nothing } -int cosmoB_ogetProto(CState *state, int nargs, CValue *args) { +int cosmoB_ogetProto(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "Expected 1 argument, got %d!", nargs); return 0; } - cosmoV_pushRef(state, (CObj*)cosmoV_readObject(args[0])->_obj.proto); // just return the proto + cosmoV_pushRef(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) { +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_REF(args[0]) || !IS_OBJECT(args[1])) { - cosmoV_typeError(state, "object.ischild()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "object.ischild()", ", ", "%s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); return 0; } @@ -212,14 +213,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args) { return 1; } -COSMO_API void cosmoB_loadObjLib(CState *state) { - const char *identifiers[] = { - "ischild" - }; +COSMO_API void cosmoB_loadObjLib(CState *state) +{ + const char *identifiers[] = {"ischild"}; - CosmoCFunction objLib[] = { - cosmoB_oisChild - }; + CosmoCFunction objLib[] = {cosmoB_oisChild}; // make object library object cosmoV_pushString(state, "object"); @@ -228,7 +226,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state) { cosmoV_pushString(state, "__getter"); // key & value pair - cosmoV_pushString(state, "__proto"); // key + cosmoV_pushString(state, "__proto"); // key cosmoV_pushCFunction(state, cosmoB_ogetProto); // value cosmoV_makeTable(state, 1); @@ -242,7 +240,7 @@ COSMO_API void cosmoB_loadObjLib(CState *state) { cosmoV_makeTable(state, 1); int i; - for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { + for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) { cosmoV_pushString(state, identifiers[i]); cosmoV_pushCFunction(state, objLib[i]); } @@ -256,10 +254,12 @@ COSMO_API void cosmoB_loadObjLib(CState *state) { cosmoV_register(state, 1); } -// ================================================================ [OS.*] ================================================================ +// ================================================================ [OS.*] +// ================================================================ // os.read() -int cosmoB_osRead(CState *state, int nargs, CValue *args) { +int cosmoB_osRead(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs); return 0; @@ -287,7 +287,7 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) { size = ftell(file); rewind(file); - buf = cosmoM_xmalloc(state, size + 1); // +1 for the NULL terminator + buf = cosmoM_xmalloc(state, size + 1); // +1 for the NULL terminator bRead = fread(buf, sizeof(char), size, file); // read the file into the buffer if (bRead < size) { @@ -303,7 +303,8 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) { } // os.time() -int cosmoB_osTime(CState *state, int nargs, CValue *args) { +int cosmoB_osTime(CState *state, int nargs, CValue *args) +{ struct timeval time; if (nargs > 0) { cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs); @@ -316,7 +317,8 @@ int cosmoB_osTime(CState *state, int nargs, CValue *args) { } // os.system() -int cosmoB_osSystem(CState *state, int nargs, CValue *args) { +int cosmoB_osSystem(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs); return 0; @@ -332,23 +334,16 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args) { return 1; } -COSMO_API void cosmoB_loadOSLib(CState *state) { - const char *identifiers[] = { - "read", - "time", - "system" - }; +COSMO_API void cosmoB_loadOSLib(CState *state) +{ + const char *identifiers[] = {"read", "time", "system"}; - CosmoCFunction osLib[] = { - cosmoB_osRead, - cosmoB_osTime, - cosmoB_osSystem - }; + CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem}; cosmoV_pushString(state, "os"); int i; - for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { + for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) { cosmoV_pushString(state, identifiers[i]); cosmoV_pushCFunction(state, osLib[i]); } @@ -357,13 +352,16 @@ COSMO_API void cosmoB_loadOSLib(CState *state) { cosmoV_register(state, 1); // register the os.* object to the global table } -// ================================================================ [STRING.*] ================================================================ +// ================================================================ [STRING.*] +// ================================================================ // string.sub -int cosmoB_sSub(CState *state, int nargs, CValue *args) { +int cosmoB_sSub(CState *state, int nargs, CValue *args) +{ if (nargs == 2) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { - cosmoV_typeError(state, "string.sub()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "string.sub()", ", ", "%s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); return 0; } @@ -372,14 +370,17 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { // make sure we stay within memory if (indx < 0 || indx >= str->length) { - cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, indx); + cosmoV_error(state, "string.sub() expected index to be 0-%d, got %d!", str->length - 1, + indx); return 0; } cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx)); } else if (nargs == 3) { if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) { - cosmoV_typeError(state, "string.sub()", ", , ", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); + cosmoV_typeError(state, "string.sub()", ", , ", "%s, %s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), + cosmoV_typeStr(args[2])); return 0; } @@ -389,7 +390,9 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { // make sure we stay within memory if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) { - cosmoV_error(state, "string.sub() expected subbed string goes out of bounds, max length is %d!", str->length); + cosmoV_error( + state, "string.sub() expected subbed string goes out of bounds, max length is %d!", + str->length); return 0; } @@ -403,10 +406,12 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { } // string.find -int cosmoB_sFind(CState *state, int nargs, CValue *args) { +int cosmoB_sFind(CState *state, int nargs, CValue *args) +{ if (nargs == 2) { if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { - cosmoV_typeError(state, "string.find()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "string.find()", ", ", "%s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); return 0; } @@ -425,7 +430,9 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) { cosmoV_pushNumber(state, indx - str->str); } else if (nargs == 3) { if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) { - cosmoV_typeError(state, "string.find()", ", , ", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); + cosmoV_typeError(state, "string.find()", ", , ", "%s, %s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), + cosmoV_typeStr(args[2])); return 0; } @@ -452,14 +459,16 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) { } // string.split -int cosmoB_sSplit(CState *state, int nargs, CValue *args) { +int cosmoB_sSplit(CState *state, int nargs, CValue *args) +{ if (nargs != 2) { cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs); return 0; } if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { - cosmoV_typeError(state, "string.split()", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "string.split()", ", ", "%s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); return 0; } @@ -475,7 +484,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) { nIndx = strstr(indx, ptrn->str); cosmoV_pushNumber(state, nEntries++); - cosmoV_pushLString(state, indx, nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx); + cosmoV_pushLString(state, indx, + nIndx == NULL ? str->length - (indx - str->str) : nIndx - indx); indx = nIndx + ptrn->length; } while (nIndx != NULL); @@ -486,7 +496,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) { } // string.byte -int cosmoB_sByte(CState *state, int nargs, CValue *args) { +int cosmoB_sByte(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs); return 0; @@ -496,12 +507,12 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) { cosmoV_typeError(state, "string.byte", "", "%s", cosmoV_typeStr(args[0])); return 0; } - + CObjString *str = cosmoV_readString(args[0]); if (str->length < 1) { - // the length of the string is less than 1, in the future I might throw an error for this, but - // for now im going to copy lua and just return a nil + // the length of the string is less than 1, in the future I might throw an error for this, + // but for now im going to copy lua and just return a nil return 0; } @@ -511,7 +522,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) { } // string.char -int cosmoB_sChar(CState *state, int nargs, CValue *args) { +int cosmoB_sChar(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs); return 0; @@ -522,7 +534,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) { return 0; } - // small side effect of truncating the number, but ignoring the decimal instead of throwing an error is the better option imo + // small side effect of truncating the number, but ignoring the decimal instead of throwing an + // error is the better option imo int num = (int)cosmoV_readNumber(args[0]); char c = num; @@ -536,7 +549,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_sLen(CState *state, int nargs, CValue *args) { +int cosmoB_sLen(CState *state, int nargs, CValue *args) +{ if (nargs < 1) { cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs); return 0; @@ -552,7 +566,8 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_sRep(CState *state, int nargs, CValue *args) { +int cosmoB_sRep(CState *state, int nargs, CValue *args) +{ if (nargs != 2) { cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs); return 0; @@ -560,7 +575,8 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) { // expects , if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { - cosmoV_typeError(state, "string.rep", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "string.rep", ", ", "%s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); return 0; } @@ -582,37 +598,23 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) { // write the NULL terminator newStr[length] = '\0'; - + // finally, push the resulting string onto the stack - cosmoV_pushRef(state, (CObj*)cosmoO_takeString(state, newStr, length)); + cosmoV_pushRef(state, (CObj *)cosmoO_takeString(state, newStr, length)); return 1; } -void cosmoB_loadStrLib(CState *state) { - const char *identifiers[] = { - "sub", - "find", - "split", - "byte", - "char", - "len", - "rep" - }; +void cosmoB_loadStrLib(CState *state) +{ + const char *identifiers[] = {"sub", "find", "split", "byte", "char", "len", "rep"}; - CosmoCFunction strLib[] = { - cosmoB_sSub, - cosmoB_sFind, - cosmoB_sSplit, - cosmoB_sByte, - cosmoB_sChar, - cosmoB_sLen, - cosmoB_sRep - }; + CosmoCFunction strLib[] = {cosmoB_sSub, cosmoB_sFind, cosmoB_sSplit, cosmoB_sByte, + cosmoB_sChar, cosmoB_sLen, cosmoB_sRep}; // make string library object cosmoV_pushString(state, "string"); int i; - for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { + for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) { cosmoV_pushString(state, identifiers[i]); cosmoV_pushCFunction(state, strLib[i]); } @@ -626,10 +628,12 @@ void cosmoB_loadStrLib(CState *state) { cosmoV_register(state, 1); } -// ================================================================ [MATH] ================================================================ +// ================================================================ [MATH] +// ================================================================ // math.abs -int cosmoB_mAbs(CState *state, int nargs, CValue *args) { +int cosmoB_mAbs(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs); return 0; @@ -645,7 +649,8 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args) { } // math.floor -int cosmoB_mFloor(CState *state, int nargs, CValue *args) { +int cosmoB_mFloor(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs); return 0; @@ -661,7 +666,8 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args) { } // math.ceil -int cosmoB_mCeil(CState *state, int nargs, CValue *args) { +int cosmoB_mCeil(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs); return 0; @@ -684,7 +690,8 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_mSin(CState *state, int nargs, CValue *args) { +int cosmoB_mSin(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs); return 0; @@ -699,7 +706,8 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_mCos(CState *state, int nargs, CValue *args) { +int cosmoB_mCos(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs); return 0; @@ -714,7 +722,8 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_mTan(CState *state, int nargs, CValue *args) { +int cosmoB_mTan(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs); return 0; @@ -729,7 +738,8 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_mASin(CState *state, int nargs, CValue *args) { +int cosmoB_mASin(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs); return 0; @@ -744,7 +754,8 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_mACos(CState *state, int nargs, CValue *args) { +int cosmoB_mACos(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs); return 0; @@ -759,7 +770,8 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_mATan(CState *state, int nargs, CValue *args) { +int cosmoB_mATan(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs); return 0; @@ -774,7 +786,8 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_mRad(CState *state, int nargs, CValue *args) { +int cosmoB_mRad(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs); return 0; @@ -790,7 +803,8 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args) { return 1; } -int cosmoB_mDeg(CState *state, int nargs, CValue *args) { +int cosmoB_mDeg(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs); return 0; @@ -806,39 +820,19 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args) { return 1; } -void cosmoB_loadMathLib(CState *state) { - const char *identifiers[] = { - "abs", - "floor", - "ceil", - "sin", - "cos", - "tan", - "asin", - "acos", - "atan", - "rad", - "deg" - }; +void cosmoB_loadMathLib(CState *state) +{ + const char *identifiers[] = {"abs", "floor", "ceil", "sin", "cos", "tan", + "asin", "acos", "atan", "rad", "deg"}; - CosmoCFunction mathLib[] = { - cosmoB_mAbs, - cosmoB_mFloor, - cosmoB_mCeil, - cosmoB_mSin, - cosmoB_mCos, - cosmoB_mTan, - cosmoB_mASin, - cosmoB_mACos, - cosmoB_mATan, - cosmoB_mRad, - cosmoB_mDeg - }; + CosmoCFunction mathLib[] = {cosmoB_mAbs, cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin, + cosmoB_mCos, cosmoB_mTan, cosmoB_mASin, cosmoB_mACos, + cosmoB_mATan, cosmoB_mRad, cosmoB_mDeg}; // make math library object cosmoV_pushString(state, "math"); int i; - for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) { + for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) { cosmoV_pushString(state, identifiers[i]); cosmoV_pushCFunction(state, mathLib[i]); } @@ -852,41 +846,47 @@ void cosmoB_loadMathLib(CState *state) { cosmoV_register(state, 1); } -// ================================================================ [VM.*] ================================================================ +// ================================================================ [VM.*] +// ================================================================ // vm.__getter["globals"] -int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args) { +int cosmoB_vgetGlobal(CState *state, int nargs, CValue *args) +{ // this function doesn't need to check anything, just return the global table - cosmoV_pushRef(state, (CObj*)state->globals); + cosmoV_pushRef(state, (CObj *)state->globals); return 1; } // vm.__setter["globals"] -int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) { +int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) +{ if (nargs != 2) { cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs); return 0; } if (!IS_TABLE(args[1])) { - cosmoV_typeError(state, "vm.__setter[\"globals\"]", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "vm.__setter[\"globals\"]", ",
", "%s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); return 0; } // this makes me very nervous ngl - CObjTable *tbl = (CObjTable*)cosmoV_readRef(args[1]); + CObjTable *tbl = (CObjTable *)cosmoV_readRef(args[1]); state->globals = tbl; return 0; } -int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) { +int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) +{ if (nargs != 2) { cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); return 0; } if (!IS_NUMBER(args[1])) { - cosmoV_typeError(state, "baseProtos.__index", ", ", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); + cosmoV_typeError(state, "baseProtos.__index", ", ", "%s, %s", + cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); return 0; } @@ -898,21 +898,24 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) { } if (state->protoObjects[indx] != NULL) - cosmoV_pushRef(state, (CObj*)state->protoObjects[indx]); + cosmoV_pushRef(state, (CObj *)state->protoObjects[indx]); else cosmoV_pushNil(state); - + return 1; // 1 value pushed, 1 value returned } -int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) { +int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) +{ if (nargs != 3) { cosmoV_error(state, "Expected 3 arguments, got %d!", nargs); return 0; } if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) { - cosmoV_typeError(state, "baseProtos.__newindex", ", , ", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); + cosmoV_typeError(state, "baseProtos.__newindex", ", , ", + "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), + cosmoV_typeStr(args[2])); return 0; } @@ -929,10 +932,11 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) { } // vm.collect() -int cosmoB_vcollect(CState *state, int nargs, CValue *args) { +int cosmoB_vcollect(CState *state, int nargs, CValue *args) +{ // first, unfreeze the state (we start frozen on entry to any C Function) cosmoM_unfreezeGC(state); - + // now force a garbage collection cosmoM_collectGarbage(state); @@ -943,7 +947,8 @@ int cosmoB_vcollect(CState *state, int nargs, CValue *args) { return 0; } -void cosmoB_loadVM(CState *state) { +void cosmoB_loadVM(CState *state) +{ // make vm.* object cosmoV_pushString(state, "vm"); diff --git a/src/cbaselib.h b/src/cbaselib.h index 7fc03a7..8319d5c 100644 --- a/src/cbaselib.h +++ b/src/cbaselib.h @@ -32,7 +32,8 @@ COSMO_API void cosmoB_loadOSLib(CState *state); - string.char & :char() - string.rep & :rep() - The base proto object for strings is also set, allowing you to invoke the string.* api through string objects, eg. + 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", " ")` */ COSMO_API void cosmoB_loadStrLib(CState *state); @@ -54,7 +55,7 @@ COSMO_API void cosmoB_loadMathLib(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__); +#define cosmoV_typeError(state, name, expectedTypes, formatStr, ...) \ + cosmoV_error(state, name " expected (" expectedTypes "), got (" formatStr ")!", __VA_ARGS__); #endif diff --git a/src/cchunk.c b/src/cchunk.c index 107e560..371c0ba 100644 --- a/src/cchunk.c +++ b/src/cchunk.c @@ -1,27 +1,31 @@ -#include "cmem.h" #include "cchunk.h" + +#include "cmem.h" +#include "cobj.h" #include "cvalue.h" #include "cvm.h" -#include "cobj.h" -CChunk *newChunk(CState* state, size_t startCapacity) { +CChunk *newChunk(CState *state, size_t startCapacity) +{ CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk)); initChunk(state, chunk, startCapacity); return chunk; } -void initChunk(CState* state, CChunk *chunk, size_t startCapacity) { +void initChunk(CState *state, CChunk *chunk, size_t startCapacity) +{ chunk->capacity = startCapacity; chunk->lineCapacity = startCapacity; chunk->count = 0; chunk->buf = NULL; // when writeByteChunk is called, it'll allocate the array for us chunk->lineInfo = NULL; - + // constants initValArray(state, &chunk->constants, ARRAY_START); } -void cleanChunk(CState* state, CChunk *chunk) { +void cleanChunk(CState *state, CChunk *chunk) +{ // first, free the chunk buffer cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity); // then the line info @@ -30,13 +34,15 @@ void cleanChunk(CState* state, CChunk *chunk) { cleanValArray(state, &chunk->constants); } -void freeChunk(CState* state, CChunk *chunk) { +void freeChunk(CState *state, CChunk *chunk) +{ cleanChunk(state, chunk); // now, free the wrapper struct cosmoM_free(state, CChunk, chunk); } -int addConstant(CState* state, CChunk *chunk, CValue value) { +int addConstant(CState *state, CChunk *chunk, CValue value) +{ // before adding the constant, check if we already have it for (size_t i = 0; i < chunk->constants.count; i++) { if (cosmoV_equal(state, value, chunk->constants.values[i])) @@ -49,9 +55,11 @@ int addConstant(CState* state, CChunk *chunk, CValue value) { return chunk->constants.count - 1; // return the index of the new constants } -// ================================================================ [WRITE TO CHUNK] ================================================================ +// ================================================================ [WRITE TO CHUNK] +// ================================================================ -void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) { +void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line) +{ // does the buffer need to be reallocated? cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); @@ -61,8 +69,9 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) { chunk->buf[chunk->count++] = i; } -void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line) { - INSTRUCTION *buffer = (INSTRUCTION*)(&i); +void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line) +{ + INSTRUCTION *buffer = (INSTRUCTION *)(&i); int sz = sizeof(uint16_t) / sizeof(INSTRUCTION); for (int i = 0; i < sz; i++) { diff --git a/src/cchunk.h b/src/cchunk.h index 8f97636..0074135 100644 --- a/src/cchunk.h +++ b/src/cchunk.h @@ -1,37 +1,39 @@ #ifndef CCHUNK_H #define CCHUNK_H -#include "cosmo.h" - #include "coperators.h" +#include "cosmo.h" #include "cvalue.h" -struct CChunk { - size_t capacity; // the amount of space we've allocated for - size_t count; // the space we're currently using - INSTRUCTION *buf; // whole chunk +struct CChunk +{ + size_t capacity; // the amount of space we've allocated for + size_t count; // the space we're currently using + INSTRUCTION *buf; // whole chunk CValueArray constants; // holds constants size_t lineCapacity; int *lineInfo; }; -CChunk *newChunk(CState* state, size_t startCapacity); -void initChunk(CState* state, CChunk *chunk, size_t startCapacity); -void cleanChunk(CState* state, CChunk *chunk); // frees everything but the struct -void freeChunk(CState* state, CChunk *chunk); // frees everything including the struct -int addConstant(CState* state, CChunk *chunk, CValue value); +CChunk *newChunk(CState *state, size_t startCapacity); +void initChunk(CState *state, CChunk *chunk, size_t startCapacity); +void cleanChunk(CState *state, CChunk *chunk); // frees everything but the struct +void freeChunk(CState *state, CChunk *chunk); // frees everything including the struct +int addConstant(CState *state, CChunk *chunk, CValue value); // write to chunk -void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line); -void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line); +void writeu8Chunk(CState *state, CChunk *chunk, INSTRUCTION i, int line); +void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line); // read from chunk -static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) { +static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) +{ return chunk->buf[offset]; } -static inline uint16_t readu16Chunk(CChunk *chunk, int offset) { - return *((uint16_t*)(&chunk->buf[offset])); +static inline uint16_t readu16Chunk(CChunk *chunk, int offset) +{ + return *((uint16_t *)(&chunk->buf[offset])); } #endif diff --git a/src/cdebug.c b/src/cdebug.c index a9db098..43d082c 100644 --- a/src/cdebug.c +++ b/src/cdebug.c @@ -1,49 +1,62 @@ #include "cdebug.h" -#include "cvalue.h" -#include "cobj.h" -void printIndent(int indent) { +#include "cobj.h" +#include "cvalue.h" + +void printIndent(int indent) +{ for (int i = 0; i < indent; i++) printf("\t"); } -int simpleInstruction(const char *name, int offset) { +int simpleInstruction(const char *name, int offset) +{ printf("%s", name); return offset + 1; // consume opcode } -int u8OperandInstruction(const char *name, CChunk *chunk, int offset) { +int u8OperandInstruction(const char *name, CChunk *chunk, int offset) +{ printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1)); return offset + 2; } -int u16OperandInstruction(const char *name, CChunk *chunk, int offset) { +int u16OperandInstruction(const char *name, CChunk *chunk, int offset) +{ printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1)); return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); } -int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir) { +int JumpInstruction(const char *name, CChunk *chunk, int offset, int dir) +{ int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir; printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp); return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); } -int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) { - printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2)); +int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) +{ + printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), + readu8Chunk(chunk, offset + 2)); return offset + 3; // op + u8 + u8 } -int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) { - printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu16Chunk(chunk, offset + 2)); +int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) +{ + printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), + readu16Chunk(chunk, offset + 2)); return offset + 4; // op + u8 + u16 } -int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) { - printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3)); +int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) +{ + printf("%-16s [%03d] [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), + readu8Chunk(chunk, offset + 2), readu16Chunk(chunk, offset + 3)); return offset + 5; // op + u8 + u8 + u16 } -int constInstruction(const char *name, CChunk *chunk, int offset) { +int constInstruction(const char *name, CChunk *chunk, int offset) +{ int index = readu16Chunk(chunk, offset + 1); printf("%-16s [%05d] - ", name, index); CValue val = chunk->constants.values[index]; @@ -55,7 +68,8 @@ int constInstruction(const char *name, CChunk *chunk, int offset) { // public methods in the cdebug.h header -void disasmChunk(CChunk *chunk, const char *name, int indent) { +void disasmChunk(CChunk *chunk, const char *name, int indent) +{ printIndent(indent); printf("===[[ disasm for %s ]]===\n", name); @@ -65,7 +79,8 @@ void disasmChunk(CChunk *chunk, const char *name, int indent) { } } -int disasmInstr(CChunk *chunk, int offset, int indent) { +int disasmInstr(CChunk *chunk, int offset, int indent) +{ printIndent(indent); printf("%04d ", offset); @@ -79,131 +94,131 @@ int disasmInstr(CChunk *chunk, int offset, int indent) { } switch (i) { - case OP_LOADCONST: - return constInstruction("OP_LOADCONST", chunk, offset); - case OP_SETGLOBAL: - return constInstruction("OP_SETGLOBAL", chunk, offset); - case OP_GETGLOBAL: - return constInstruction("OP_GETGLOBAL", chunk, offset); - case OP_SETLOCAL: - return u8OperandInstruction("OP_SETLOCAL", chunk, offset); - case OP_GETLOCAL: - return u8OperandInstruction("OP_GETLOCAL", chunk, offset); - case OP_SETUPVAL: - return u8OperandInstruction("OP_SETUPVAL", chunk, offset); - case OP_GETUPVAL: - return u8OperandInstruction("OP_GETUPVAL", chunk, offset); - case OP_PEJMP: - return JumpInstruction("OP_PEJMP", chunk, offset, 1); - case OP_EJMP: - return JumpInstruction("OP_EJMP", chunk, offset, 1); - case OP_JMP: - return JumpInstruction("OP_JMP", chunk, offset, 1); - case OP_JMPBACK: - return JumpInstruction("OP_JMPBACK", chunk, offset, -1); - case OP_POP: - return u8OperandInstruction("OP_POP", chunk, offset); - case OP_CALL: - return u8u8OperandInstruction("OP_CALL", chunk, offset); - case OP_CLOSURE: { - int index = readu16Chunk(chunk, offset + 1); - printf("%-16s [%05d] - ", "OP_CLOSURE", index); - CValue val = chunk->constants.values[index]; - CObjFunction *cobjFunc = (CObjFunction*)cosmoV_readRef(val); - offset += 3; // we consumed the opcode + u16 + case OP_LOADCONST: + return constInstruction("OP_LOADCONST", chunk, offset); + case OP_SETGLOBAL: + return constInstruction("OP_SETGLOBAL", chunk, offset); + case OP_GETGLOBAL: + return constInstruction("OP_GETGLOBAL", chunk, offset); + case OP_SETLOCAL: + return u8OperandInstruction("OP_SETLOCAL", chunk, offset); + case OP_GETLOCAL: + return u8OperandInstruction("OP_GETLOCAL", chunk, offset); + case OP_SETUPVAL: + return u8OperandInstruction("OP_SETUPVAL", chunk, offset); + case OP_GETUPVAL: + return u8OperandInstruction("OP_GETUPVAL", chunk, offset); + case OP_PEJMP: + return JumpInstruction("OP_PEJMP", chunk, offset, 1); + case OP_EJMP: + return JumpInstruction("OP_EJMP", chunk, offset, 1); + case OP_JMP: + return JumpInstruction("OP_JMP", chunk, offset, 1); + case OP_JMPBACK: + return JumpInstruction("OP_JMPBACK", chunk, offset, -1); + case OP_POP: + return u8OperandInstruction("OP_POP", chunk, offset); + case OP_CALL: + return u8u8OperandInstruction("OP_CALL", chunk, offset); + case OP_CLOSURE: { + int index = readu16Chunk(chunk, offset + 1); + printf("%-16s [%05d] - ", "OP_CLOSURE", index); + CValue val = chunk->constants.values[index]; + CObjFunction *cobjFunc = (CObjFunction *)cosmoV_readRef(val); + offset += 3; // we consumed the opcode + u16 - printValue(val); - printf("\n"); - - // list the upvalues/locals that are captured - for (int i = 0; i < cobjFunc->upvals; i++) { - uint8_t encoding = readu8Chunk(chunk, offset++); - uint8_t index = readu8Chunk(chunk, offset++); - printIndent(indent + 1); - printf("references %s [%d]\n", encoding == OP_GETLOCAL ? "local" : "upvalue", index); - } + printValue(val); + printf("\n"); - // print the chunk - disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, indent+1); - return offset; + // list the upvalues/locals that are captured + for (int i = 0; i < cobjFunc->upvals; i++) { + uint8_t encoding = readu8Chunk(chunk, offset++); + uint8_t index = readu8Chunk(chunk, offset++); + printIndent(indent + 1); + printf("references %s [%d]\n", encoding == OP_GETLOCAL ? "local" : "upvalue", index); } - case OP_CLOSE: - return simpleInstruction("OP_CLOSE", offset); - case OP_NEWTABLE: - return u16OperandInstruction("OP_NEWTABLE", chunk, offset); - case OP_NEWARRAY: - return u16OperandInstruction("OP_NEWARRAY", chunk, offset); - case OP_INDEX: - return simpleInstruction("OP_INDEX", offset); - case OP_NEWINDEX: - return simpleInstruction("OP_NEWINDEX", offset); - case OP_NEWOBJECT: - return u16OperandInstruction("OP_NEWOBJECT", chunk, offset); - case OP_SETOBJECT: - return constInstruction("OP_SETOBJECT", chunk, offset); - case OP_GETOBJECT: - return constInstruction("OP_GETOBJECT", chunk, offset); - case OP_GETMETHOD: - return constInstruction("OP_GETMETHOD", chunk, offset); - case OP_INVOKE: - return u8u8u16OperandInstruction("OP_INVOKE", chunk, offset); - case OP_ITER: - return simpleInstruction("OP_ITER", offset); - case OP_NEXT: - return u8u16OperandInstruction("OP_NEXT", chunk, offset); - case OP_ADD: - return simpleInstruction("OP_ADD", offset); - case OP_SUB: - return simpleInstruction("OP_SUB", offset); - case OP_MULT: - return simpleInstruction("OP_MULT", offset); - case OP_DIV: - return simpleInstruction("OP_DIV", offset); - case OP_MOD: - return simpleInstruction("OP_MOD", offset); - case OP_POW: - return simpleInstruction("OP_POW", offset); - case OP_TRUE: - return simpleInstruction("OP_TRUE", offset); - case OP_FALSE: - return simpleInstruction("OP_FALSE", offset); - case OP_NIL: - return simpleInstruction("OP_NIL", offset); - case OP_NOT: - return simpleInstruction("OP_NOT", offset); - case OP_EQUAL: - return simpleInstruction("OP_EQUAL", offset); - case OP_GREATER: - return simpleInstruction("OP_GREATER", offset); - case OP_GREATER_EQUAL: - return simpleInstruction("OP_GREATER_EQUAL", offset); - case OP_LESS: - return simpleInstruction("OP_LESS", offset); - case OP_LESS_EQUAL: - return simpleInstruction("OP_LESS_EQUAL", offset); - case OP_NEGATE: - return simpleInstruction("OP_NEGATE", offset); - case OP_COUNT: - return simpleInstruction("OP_COUNT", offset); - case OP_CONCAT: - return u8OperandInstruction("OP_CONCAT", chunk, offset); - case OP_INCLOCAL: - return u8u8OperandInstruction("OP_INCLOCAL", chunk, offset); - case OP_INCGLOBAL: - return u8u16OperandInstruction("OP_INCGLOBAL", chunk, offset); - case OP_INCUPVAL: - return u8u8OperandInstruction("OP_INCUPVAL", chunk, offset); - case OP_INCINDEX: - return u8OperandInstruction("OP_INCINDEX", chunk, offset); - case OP_INCOBJECT: - return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset); - case OP_RETURN: - return u8OperandInstruction("OP_RETURN", chunk, offset); - default: - printf("Unknown opcode! [%d]\n", i); - return 1; - } + // print the chunk + disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, + indent + 1); + return offset; + } + case OP_CLOSE: + return simpleInstruction("OP_CLOSE", offset); + case OP_NEWTABLE: + return u16OperandInstruction("OP_NEWTABLE", chunk, offset); + case OP_NEWARRAY: + return u16OperandInstruction("OP_NEWARRAY", chunk, offset); + case OP_INDEX: + return simpleInstruction("OP_INDEX", offset); + case OP_NEWINDEX: + return simpleInstruction("OP_NEWINDEX", offset); + case OP_NEWOBJECT: + return u16OperandInstruction("OP_NEWOBJECT", chunk, offset); + case OP_SETOBJECT: + return constInstruction("OP_SETOBJECT", chunk, offset); + case OP_GETOBJECT: + return constInstruction("OP_GETOBJECT", chunk, offset); + case OP_GETMETHOD: + return constInstruction("OP_GETMETHOD", chunk, offset); + case OP_INVOKE: + return u8u8u16OperandInstruction("OP_INVOKE", chunk, offset); + case OP_ITER: + return simpleInstruction("OP_ITER", offset); + case OP_NEXT: + return u8u16OperandInstruction("OP_NEXT", chunk, offset); + case OP_ADD: + return simpleInstruction("OP_ADD", offset); + case OP_SUB: + return simpleInstruction("OP_SUB", offset); + case OP_MULT: + return simpleInstruction("OP_MULT", offset); + case OP_DIV: + return simpleInstruction("OP_DIV", offset); + case OP_MOD: + return simpleInstruction("OP_MOD", offset); + case OP_POW: + return simpleInstruction("OP_POW", offset); + case OP_TRUE: + return simpleInstruction("OP_TRUE", offset); + case OP_FALSE: + return simpleInstruction("OP_FALSE", offset); + case OP_NIL: + return simpleInstruction("OP_NIL", offset); + case OP_NOT: + return simpleInstruction("OP_NOT", offset); + case OP_EQUAL: + return simpleInstruction("OP_EQUAL", offset); + case OP_GREATER: + return simpleInstruction("OP_GREATER", offset); + case OP_GREATER_EQUAL: + return simpleInstruction("OP_GREATER_EQUAL", offset); + case OP_LESS: + return simpleInstruction("OP_LESS", offset); + case OP_LESS_EQUAL: + return simpleInstruction("OP_LESS_EQUAL", offset); + case OP_NEGATE: + return simpleInstruction("OP_NEGATE", offset); + case OP_COUNT: + return simpleInstruction("OP_COUNT", offset); + case OP_CONCAT: + return u8OperandInstruction("OP_CONCAT", chunk, offset); + case OP_INCLOCAL: + return u8u8OperandInstruction("OP_INCLOCAL", chunk, offset); + case OP_INCGLOBAL: + return u8u16OperandInstruction("OP_INCGLOBAL", chunk, offset); + case OP_INCUPVAL: + return u8u8OperandInstruction("OP_INCUPVAL", chunk, offset); + case OP_INCINDEX: + return u8OperandInstruction("OP_INCINDEX", chunk, offset); + case OP_INCOBJECT: + return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset); + case OP_RETURN: + return u8OperandInstruction("OP_RETURN", chunk, offset); + default: + printf("Unknown opcode! [%d]\n", i); + return 1; + } return 1; } diff --git a/src/clex.c b/src/clex.c index 1640f47..c1bc874 100644 --- a/src/clex.c +++ b/src/clex.c @@ -1,72 +1,81 @@ #include "clex.h" + #include "cmem.h" #include CReservedWord reservedWords[] = { - {TOKEN_AND, "and", 3}, - {TOKEN_BREAK, "break", 5}, + { TOKEN_AND, "and", 3}, + { TOKEN_BREAK, "break", 5}, {TOKEN_CONTINUE, "continue", 8}, - {TOKEN_DO, "do", 2}, - {TOKEN_ELSE, "else", 4}, - {TOKEN_ELSEIF, "elseif", 6}, - {TOKEN_END, "end", 3}, - {TOKEN_FALSE, "false", 5}, - {TOKEN_FOR, "for", 3}, + { TOKEN_DO, "do", 2}, + { TOKEN_ELSE, "else", 4}, + { TOKEN_ELSEIF, "elseif", 6}, + { TOKEN_END, "end", 3}, + { TOKEN_FALSE, "false", 5}, + { TOKEN_FOR, "for", 3}, {TOKEN_FUNCTION, "function", 8}, - {TOKEN_IF, "if", 2}, - {TOKEN_IN, "in", 2}, - {TOKEN_LOCAL, "local", 5}, - {TOKEN_NIL, "nil", 3}, - {TOKEN_NOT, "not", 3}, - {TOKEN_OR, "or", 2}, - {TOKEN_PROTO, "proto", 5}, - {TOKEN_RETURN, "return", 6}, - {TOKEN_THEN, "then", 4}, - {TOKEN_TRUE, "true", 4}, - {TOKEN_VAR, "var", 3}, - {TOKEN_WHILE, "while", 5} + { TOKEN_IF, "if", 2}, + { TOKEN_IN, "in", 2}, + { TOKEN_LOCAL, "local", 5}, + { TOKEN_NIL, "nil", 3}, + { TOKEN_NOT, "not", 3}, + { TOKEN_OR, "or", 2}, + { TOKEN_PROTO, "proto", 5}, + { TOKEN_RETURN, "return", 6}, + { TOKEN_THEN, "then", 4}, + { TOKEN_TRUE, "true", 4}, + { TOKEN_VAR, "var", 3}, + { TOKEN_WHILE, "while", 5} }; // returns true if current token is a heap allocated buffer -static bool isBuffer(CLexState *state) { - return state->buffer != NULL; +static bool isBuffer(CLexState *state) +{ + return state->buffer != NULL; } // marks the current token as heap allocated & allocates the buffer -static void makeBuffer(CLexState *state) { - state->buffer = cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer +static void makeBuffer(CLexState *state) +{ + state->buffer = + cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer state->bufCount = 0; state->bufCap = 32; } -static void resetBuffer(CLexState *state) { +static void resetBuffer(CLexState *state) +{ state->buffer = NULL; state->bufCount = 0; state->bufCap = 0; } // cancels the token heap buffer and frees it -static void freeBuffer(CLexState *state) { +static void freeBuffer(CLexState *state) +{ cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap); resetBuffer(state); } // adds character to buffer -static void appendBuffer(CLexState *state, char c) { +static void appendBuffer(CLexState *state, char c) +{ cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap); state->buffer[state->bufCount++] = c; } // saves the current character to the buffer, grows the buffer as needed -static void saveBuffer(CLexState *state) { +static void saveBuffer(CLexState *state) +{ appendBuffer(state, *state->currentChar); } // resets the lex state buffer & returns the allocated buffer as a null terminated string -static char *cutBuffer(CLexState *state, int *length) { +static char *cutBuffer(CLexState *state, int *length) +{ // append the null terminator appendBuffer(state, '\0'); @@ -84,7 +93,8 @@ static char *cutBuffer(CLexState *state, int *length) { return cosmoM_reallocate(state->cstate, buf, cap, count); } -static CToken makeToken(CLexState *state, CTokenType type) { +static CToken makeToken(CLexState *state, CTokenType type) +{ CToken token; token.type = type; token.line = state->line; @@ -95,38 +105,43 @@ static CToken makeToken(CLexState *state, CTokenType type) { token.start = state->startChar; token.length = state->currentChar - state->startChar; // delta between start & current } - + state->lastType = type; return token; } -static CToken makeError(CLexState *state, const char *msg) { +static CToken makeError(CLexState *state, const char *msg) +{ CToken token; token.type = TOKEN_ERROR; - token.start = (char*)msg; + token.start = (char *)msg; token.length = strlen(msg); token.line = state->line; if (isBuffer(state)) freeBuffer(state); - + return token; } -static inline bool isEnd(CLexState *state) { +static inline bool isEnd(CLexState *state) +{ return *state->currentChar == '\0'; } -static inline bool isNumerical(char c) { +static inline bool isNumerical(char c) +{ return c >= '0' && c <= '9'; } -static bool isAlpha(char c) { +static bool isAlpha(char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; // identifiers can have '_' } -static bool match(CLexState *state, char expected) { +static bool match(CLexState *state, char expected) +{ if (isEnd(state) || *state->currentChar != expected) return false; @@ -135,35 +150,41 @@ static bool match(CLexState *state, char expected) { return true; } -char peek(CLexState *state) { +char peek(CLexState *state) +{ return *state->currentChar; } -static char peekNext(CLexState *state) { - if (isEnd(state)) +static char peekNext(CLexState *state) +{ + if (isEnd(state)) return '\0'; - + return state->currentChar[1]; } -char next(CLexState *state) { +char next(CLexState *state) +{ if (isEnd(state)) - return '\0'; // return a null terminator + return '\0'; // return a null terminator state->currentChar++; return state->currentChar[-1]; } -bool isHex(char c) { +bool isHex(char c) +{ return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= c); } -CTokenType identifierType(CLexState *state) { +CTokenType identifierType(CLexState *state) +{ int length = state->currentChar - state->startChar; // check against reserved word list for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) { // it matches the reserved word - if (reservedWords[i].len == length && memcmp(state->startChar, reservedWords[i].word, length) == 0) + if (reservedWords[i].len == length && + memcmp(state->startChar, reservedWords[i].word, length) == 0) return reservedWords[i].type; } @@ -171,161 +192,178 @@ CTokenType identifierType(CLexState *state) { return TOKEN_IDENTIFIER; } -void skipWhitespace(CLexState *state) { +void skipWhitespace(CLexState *state) +{ while (true) { char c = peek(state); switch (c) { - case '\n': // mark new line - state->line++; - case ' ': - case '\r': - case '\t': - next(state); // consume the whitespace + case '\n': // mark new line + state->line++; + case ' ': + case '\r': + case '\t': + next(state); // consume the whitespace + break; + case '/': // consume comments + if (peekNext(state) == '/') { + // skip to next line (also let \n be consumed on the next iteration to properly + // handle that) + while (!isEnd(state) && + peek(state) != '\n') // if it's not a newline or the end of the source + next(state); + + // keep consuming whitespace break; - case '/': // consume comments - if (peekNext(state) == '/') { - // skip to next line (also let \n be consumed on the next iteration to properly handle that) - while (!isEnd(state) && peek(state) != '\n') // if it's not a newline or the end of the source - next(state); - - // keep consuming whitespace - break; - } else if (peekNext(state) == '*') { // multiline comments - while (!isEnd(state) && !(peek(state) == '*' && peekNext(state) == '/')) // if it's the end of the comment or the end of the source - next(state); - - // consume the '*/' - next(state); + } else if (peekNext(state) == '*') { // multiline comments + while (!isEnd(state) && + !(peek(state) == '*' && + peekNext(state) == + '/')) // if it's the end of the comment or the end of the source next(state); - // keep consuming whitespace - break; - } - return; // it's a TOKEN_SLASH, let the main body handle that - default: // it's no longer whitespace, return! - return; + // consume the '*/' + next(state); + next(state); + + // keep consuming whitespace + break; + } + return; // it's a TOKEN_SLASH, let the main body handle that + default: // it's no longer whitespace, return! + return; } } } -CToken parseString(CLexState *state) { +CToken parseString(CLexState *state) +{ makeBuffer(state); // buffer mode while (peek(state) != '"' && !isEnd(state)) { switch (peek(state)) { - case '\n': // strings can't stretch across lines - return makeError(state, "Unterminated string!"); - case '\\': { // special character - next(state); // consume the '\' character + case '\n': // strings can't stretch across lines + return makeError(state, "Unterminated string!"); + case '\\': { // special character + next(state); // consume the '\' character - switch (peek(state)) { - case 'r': case 'n': appendBuffer(state, '\n'); break; - case 't': appendBuffer(state, '\t'); break; - case '\\': appendBuffer(state, '\\'); break; - case '"': appendBuffer(state, '"'); break; - case 'x': // hexadecimal character encoding - next(state); // skip 'x' + switch (peek(state)) { + case 'r': + case 'n': + appendBuffer(state, '\n'); + break; + case 't': + appendBuffer(state, '\t'); + break; + case '\\': + appendBuffer(state, '\\'); + break; + case '"': + appendBuffer(state, '"'); + break; + case 'x': // hexadecimal character encoding + next(state); // skip 'x' - if (isHex(peek(state))) { - char *numStart = state->currentChar; + if (isHex(peek(state))) { + char *numStart = state->currentChar; - // consume the hexnum - while (isHex(peek(state))) - next(state); - state->currentChar--; // since next() is called after - - unsigned int num = (unsigned int)strtoul(numStart, NULL, 16); + // consume the hexnum + while (isHex(peek(state))) + next(state); + state->currentChar--; // since next() is called after - if (num > 255) // sanity check - return makeError(state, "Character out of range! > 255!"); + unsigned int num = (unsigned int)strtoul(numStart, NULL, 16); - appendBuffer(state, num); - break; - } - - return makeError(state, "Unknown hexadecimal character encoding!"); - case 'b': // binary character encoding - next(state); // skip 'b' + if (num > 255) // sanity check + return makeError(state, "Character out of range! > 255!"); - if (peek(state) == '0' || peek(state) == '1') { - char *numStart = state->currentChar; - - // consume the bin - while (peek(state) == '0' || peek(state) == '1') - next(state); - state->currentChar--; // since next() is called after - - unsigned int num = (unsigned int)strtoul(numStart, NULL, 2); - - if (num > 255) // sanity check - return makeError(state, "Character out of range! > 255!"); - - appendBuffer(state, num); - break; - } - - return makeError(state, "Unknown binary character encoding!"); - default: { - if (isNumerical(peek(state))) { - char *numStart = state->currentChar; - - // consume the number - while (isNumerical(peek(state))) - next(state); - state->currentChar--; // since next() is called after - - unsigned int num = (unsigned int)strtoul(numStart, NULL, 10); - - if (num > 255) // sanity check - return makeError(state, "Character out of range! > 255!"); - - appendBuffer(state, num); - break; - } - - return makeError(state, "Unknown special character!"); // TODO: maybe a more descriptive error? - } + appendBuffer(state, num); + break; } - next(state); // consume special character - break; - } + return makeError(state, "Unknown hexadecimal character encoding!"); + case 'b': // binary character encoding + next(state); // skip 'b' + + if (peek(state) == '0' || peek(state) == '1') { + char *numStart = state->currentChar; + + // consume the bin + while (peek(state) == '0' || peek(state) == '1') + next(state); + state->currentChar--; // since next() is called after + + unsigned int num = (unsigned int)strtoul(numStart, NULL, 2); + + if (num > 255) // sanity check + return makeError(state, "Character out of range! > 255!"); + + appendBuffer(state, num); + break; + } + + return makeError(state, "Unknown binary character encoding!"); default: { - saveBuffer(state); // save the character! - next(state); // consume + if (isNumerical(peek(state))) { + char *numStart = state->currentChar; + + // consume the number + while (isNumerical(peek(state))) + next(state); + state->currentChar--; // since next() is called after + + unsigned int num = (unsigned int)strtoul(numStart, NULL, 10); + + if (num > 255) // sanity check + return makeError(state, "Character out of range! > 255!"); + + appendBuffer(state, num); + break; + } + + return makeError( + state, "Unknown special character!"); // TODO: maybe a more descriptive error? } + } + + next(state); // consume special character + break; + } + default: { + saveBuffer(state); // save the character! + next(state); // consume + } } } if (isEnd(state)) return makeError(state, "Unterminated string!"); - + next(state); // consume closing quote return makeToken(state, TOKEN_STRING); } -CToken parseNumber(CLexState *state) { +CToken parseNumber(CLexState *state) +{ switch (peek(state)) { - case 'x': // hexadecimal number + case 'x': // hexadecimal number + next(state); + + while (isHex(peek(state))) next(state); - while (isHex(peek(state))) - next(state); + return makeToken(state, TOKEN_HEXNUMBER); + case 'b': // binary number + next(state); - return makeToken(state, TOKEN_HEXNUMBER); - case 'b': // binary number + while (peek(state) == '0' || peek(state) == '1') next(state); - while (peek(state) == '0' || peek(state) == '1') - next(state); - - return makeToken(state, TOKEN_BINNUMBER); - default: // it's a one digit number!!!!! - if (!isNumerical(peek(state)) && !(peek(state) == '.')) - return makeToken(state, TOKEN_NUMBER); - // if it is a number, fall through and parse normally + return makeToken(state, TOKEN_BINNUMBER); + default: // it's a one digit number!!!!! + if (!isNumerical(peek(state)) && !(peek(state) == '.')) + return makeToken(state, TOKEN_NUMBER); + // if it is a number, fall through and parse normally } - // consume number while (isNumerical(peek(state))) { next(state); @@ -342,18 +380,20 @@ CToken parseNumber(CLexState *state) { return makeToken(state, TOKEN_NUMBER); } -CToken parseIdentifier(CLexState *state) { +CToken parseIdentifier(CLexState *state) +{ // read literal - while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state)) + while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state)) next(state); - + return makeToken(state, identifierType(state)); // is it a reserved word? } -CLexState *cosmoL_newLexState(CState *cstate, const char *source) { +CLexState *cosmoL_newLexState(CState *cstate, const char *source) +{ CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState)); - state->startChar = (char*)source; - state->currentChar = (char*)source; + state->startChar = (char *)source; + state->currentChar = (char *)source; state->line = 1; state->lastLine = 0; state->lastType = TOKEN_ERROR; @@ -364,58 +404,82 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) { return state; } -void cosmoL_freeLexState(CState *state, CLexState *lstate) { +void cosmoL_freeLexState(CState *state, CLexState *lstate) +{ cosmoM_free(state, CLexState, lstate); } -CToken cosmoL_scanToken(CLexState *state) { +CToken cosmoL_scanToken(CLexState *state) +{ skipWhitespace(state); state->startChar = state->currentChar; if (isEnd(state)) return makeToken(state, TOKEN_EOF); - + char c = next(state); switch (c) { - // single character tokens - case '(': return makeToken(state, TOKEN_LEFT_PAREN); - case ')': return makeToken(state, TOKEN_RIGHT_PAREN); - case '{': return makeToken(state, TOKEN_LEFT_BRACE); - case '}': return makeToken(state, TOKEN_RIGHT_BRACE); - case '[': return makeToken(state, TOKEN_LEFT_BRACKET); - case ']': return makeToken(state, TOKEN_RIGHT_BRACKET); - case ';': return makeToken(state, TOKEN_EOS); - case ',': return makeToken(state, TOKEN_COMMA); - case ':': return makeToken(state, TOKEN_COLON); - case '*': return makeToken(state, TOKEN_STAR); - case '%': return makeToken(state, TOKEN_PERCENT); - case '^': return makeToken(state, TOKEN_CARROT); - case '#': return makeToken(state, TOKEN_POUND); - case '/': return makeToken(state, TOKEN_SLASH); - // two character tokens - case '+': - return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS); - case '-': - return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS); - case '.': - return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) : makeToken(state, TOKEN_DOT_DOT)) : makeToken(state, TOKEN_DOT); - case '!': - return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG); - case '=': - return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) : makeToken(state, TOKEN_EQUAL); - case '>': - return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) : makeToken(state, TOKEN_GREATER); - case '<': - return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) : makeToken(state, TOKEN_LESS); - // literals - case '"': return parseString(state); - default: - if (isNumerical(c)) - return parseNumber(state); - if (isAlpha(c)) - return parseIdentifier(state); + // single character tokens + case '(': + return makeToken(state, TOKEN_LEFT_PAREN); + case ')': + return makeToken(state, TOKEN_RIGHT_PAREN); + case '{': + return makeToken(state, TOKEN_LEFT_BRACE); + case '}': + return makeToken(state, TOKEN_RIGHT_BRACE); + case '[': + return makeToken(state, TOKEN_LEFT_BRACKET); + case ']': + return makeToken(state, TOKEN_RIGHT_BRACKET); + case ';': + return makeToken(state, TOKEN_EOS); + case ',': + return makeToken(state, TOKEN_COMMA); + case ':': + return makeToken(state, TOKEN_COLON); + case '*': + return makeToken(state, TOKEN_STAR); + case '%': + return makeToken(state, TOKEN_PERCENT); + case '^': + return makeToken(state, TOKEN_CARROT); + case '#': + return makeToken(state, TOKEN_POUND); + case '/': + return makeToken(state, TOKEN_SLASH); + // two character tokens + case '+': + return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS); + case '-': + return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) + : makeToken(state, TOKEN_MINUS); + case '.': + return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) + : makeToken(state, TOKEN_DOT_DOT)) + : makeToken(state, TOKEN_DOT); + case '!': + return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) + : makeToken(state, TOKEN_BANG); + case '=': + return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) + : makeToken(state, TOKEN_EQUAL); + case '>': + return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) + : makeToken(state, TOKEN_GREATER); + case '<': + return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) + : makeToken(state, TOKEN_LESS); + // literals + case '"': + return parseString(state); + default: + if (isNumerical(c)) + return parseNumber(state); + if (isAlpha(c)) + return parseIdentifier(state); } return makeError(state, "Unknown symbol!"); diff --git a/src/clex.h b/src/clex.h index f8229a6..ba7a4ce 100644 --- a/src/clex.h +++ b/src/clex.h @@ -3,7 +3,8 @@ #include "cosmo.h" -typedef enum { +typedef enum +{ // single character tokens TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, @@ -72,26 +73,30 @@ typedef enum { TOKEN_EOF } CTokenType; -typedef struct { +typedef struct +{ CTokenType type; const char *word; int len; } CReservedWord; -typedef struct { +typedef struct +{ CTokenType type; char *start; int length; int line; } CToken; -typedef struct { +typedef struct +{ char *currentChar; char *startChar; - char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer & bufCount respectively + char *buffer; // if non-NULL & bufCount > 0, token->start & token->length will be set to buffer + // & bufCount respectively size_t bufCount; - size_t bufCap; - int line; // current line + size_t bufCap; + int line; // current line int lastLine; // line of the previous consumed token bool isEnd; CTokenType lastType; diff --git a/src/cmem.c b/src/cmem.c index a07868c..e6828f8 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -1,13 +1,15 @@ #include "cmem.h" -#include "cstate.h" -#include "cvalue.h" -#include "ctable.h" -#include "cparse.h" -#include "cobj.h" + #include "cbaselib.h" +#include "cobj.h" +#include "cparse.h" +#include "cstate.h" +#include "ctable.h" +#include "cvalue.h" // realloc wrapper -void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize) { +void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize) +{ state->allocatedBytes += newSize - oldSize; if (newSize == 0) { // it needs to be freed @@ -19,11 +21,11 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize if (!(cosmoM_isFrozen(state)) && newSize > oldSize) { cosmoM_collectGarbage(state); } -#ifdef GC_DEBUG +# ifdef GC_DEBUG else { printf("GC event ignored! state frozen! [%d]\n", state->freezeGC); } -#endif +# endif #else cosmoM_checkGarbage(state, 0); #endif @@ -39,7 +41,8 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize return newBuf; } -COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) { +COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) +{ if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) { cosmoM_collectGarbage(state); // cya lol return true; @@ -51,10 +54,11 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) { void markObject(CState *state, CObj *obj); void markValue(CState *state, CValue val); -void markTable(CState *state, CTable *tbl) { +void markTable(CState *state, CTable *tbl) +{ if (tbl->table == NULL) // table is still being initialized return; - + int cap = tbl->capacityMask + 1; for (int i = 0; i < cap; i++) { CTableEntry *entry = &tbl->table[i]; @@ -64,14 +68,17 @@ void markTable(CState *state, CTable *tbl) { } // frees white members from the table -void tableRemoveWhite(CState *state, CTable *tbl) { +void tableRemoveWhite(CState *state, CTable *tbl) +{ if (tbl->table == NULL) // table is still being initialized return; - + int cap = tbl->capacityMask + 1; for (int i = 0; i < cap; i++) { CTableEntry *entry = &tbl->table[i]; - if (IS_REF(entry->key) && !(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white (unmarked), remove it from the table + if (IS_REF(entry->key) && + !(cosmoV_readRef(entry->key))->isMarked) { // if the key is a object and it's white + // (unmarked), remove it from the table cosmoT_remove(state, tbl, entry->key); } } @@ -79,7 +86,8 @@ void tableRemoveWhite(CState *state, CTable *tbl) { cosmoT_checkShrink(state, tbl); // recovers the memory we're no longer using } -void markArray(CState *state, CValueArray *array) { +void markArray(CState *state, CValueArray *array) +{ for (size_t i = 0; i < array->count; i++) { markValue(state, array->values[i]); } @@ -87,72 +95,74 @@ void markArray(CState *state, CValueArray *array) { // mark all references associated with the object // black = keep, white = discard -void blackenObject(CState *state, CObj *obj) { - markObject(state, (CObj*)obj->proto); +void blackenObject(CState *state, CObj *obj) +{ + markObject(state, (CObj *)obj->proto); switch (obj->type) { - case COBJ_STRING: - case COBJ_CFUNCTION: - // stubbed - break; - case COBJ_OBJECT: { - // mark everything this object is keeping track of - CObjObject *cobj = (CObjObject*)obj; - markTable(state, &cobj->tbl); - break; - } - case COBJ_TABLE: { // tables are just wrappers for CTable - CObjTable *tbl = (CObjTable*)obj; - markTable(state, &tbl->tbl); - break; - } - case COBJ_UPVALUE: { - markValue(state, ((CObjUpval*)obj)->closed); - break; - } - case COBJ_FUNCTION: { - CObjFunction *func = (CObjFunction*)obj; - markObject(state, (CObj*)func->name); - markObject(state, (CObj*)func->module); - markArray(state, &func->chunk.constants); + case COBJ_STRING: + case COBJ_CFUNCTION: + // stubbed + break; + case COBJ_OBJECT: { + // mark everything this object is keeping track of + CObjObject *cobj = (CObjObject *)obj; + markTable(state, &cobj->tbl); + break; + } + case COBJ_TABLE: { // tables are just wrappers for CTable + CObjTable *tbl = (CObjTable *)obj; + markTable(state, &tbl->tbl); + break; + } + case COBJ_UPVALUE: { + markValue(state, ((CObjUpval *)obj)->closed); + break; + } + case COBJ_FUNCTION: { + CObjFunction *func = (CObjFunction *)obj; + markObject(state, (CObj *)func->name); + markObject(state, (CObj *)func->module); + markArray(state, &func->chunk.constants); - break; - } - case COBJ_METHOD: { - CObjMethod *method = (CObjMethod*)obj; - markValue(state, method->func); - markObject(state, (CObj*)method->obj); - break; - } - case COBJ_ERROR: { - CObjError *err = (CObjError*)obj; - markValue(state, err->err); + break; + } + case COBJ_METHOD: { + CObjMethod *method = (CObjMethod *)obj; + markValue(state, method->func); + markObject(state, (CObj *)method->obj); + break; + } + case COBJ_ERROR: { + CObjError *err = (CObjError *)obj; + markValue(state, err->err); - // mark callframes - for (int i = 0; i < err->frameCount; i++) - markObject(state, (CObj*)err->frames[i].closure); + // mark callframes + for (int i = 0; i < err->frameCount; i++) + markObject(state, (CObj *)err->frames[i].closure); - break; + break; + } + case COBJ_CLOSURE: { + CObjClosure *closure = (CObjClosure *)obj; + markObject(state, (CObj *)closure->function); + + // mark all upvalues + for (int i = 0; i < closure->upvalueCount; i++) { + markObject(state, (CObj *)closure->upvalues[i]); } - case COBJ_CLOSURE: { - CObjClosure *closure = (CObjClosure*)obj; - markObject(state, (CObj*)closure->function); - // mark all upvalues - for (int i = 0; i < closure->upvalueCount; i++) { - markObject(state, (CObj*)closure->upvalues[i]); - } - - break; - } - default: + break; + } + default: #ifdef GC_DEBUG - printf("Unknown type in blackenObject with %p, type %d\n", (void*)obj, obj->type); + printf("Unknown type in blackenObject with %p, type %d\n", (void *)obj, obj->type); #endif - break; + break; } } -void markObject(CState *state, CObj *obj) { +void markObject(CState *state, CObj *obj) +{ if (obj == NULL || obj->isMarked) // skip if NULL or already marked return; @@ -165,33 +175,37 @@ void markObject(CState *state, CObj *obj) { #endif // they don't need to be added to the gray stack, they don't reference any other CObjs - if (obj->type == COBJ_CFUNCTION || obj->type == COBJ_STRING) + if (obj->type == COBJ_CFUNCTION || obj->type == COBJ_STRING) return; // we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage - cosmoM_growarray(state, CObj*, state->grayStack.array, state->grayStack.count, state->grayStack.capacity); + cosmoM_growarray(state, CObj *, state->grayStack.array, state->grayStack.count, + state->grayStack.capacity); state->grayStack.array[state->grayStack.count++] = obj; } -void markValue(CState *state, CValue val) { +void markValue(CState *state, CValue val) +{ if (IS_REF(val)) markObject(state, cosmoV_readRef(val)); } // trace our gray references -void traceGrays(CState *state) { +void traceGrays(CState *state) +{ while (state->grayStack.count > 0) { - CObj* obj = state->grayStack.array[--state->grayStack.count]; + CObj *obj = state->grayStack.array[--state->grayStack.count]; blackenObject(state, obj); } } -void sweep(CState *state) { +void sweep(CState *state) +{ CObj *prev = NULL; CObj *object = state->objects; while (object != NULL) { - if (object->isMarked) { // skip over it + if (object->isMarked) { // skip over it object->isMarked = false; // rest to white prev = object; object = object->next; @@ -210,7 +224,8 @@ void sweep(CState *state) { } } -void markUserRoots(CState *state) { +void markUserRoots(CState *state) +{ CObj *root = state->userRoots; // traverse userRoots and mark all the object @@ -220,7 +235,8 @@ void markUserRoots(CState *state) { } } -void markRoots(CState *state) { +void markRoots(CState *state) +{ // mark all values on the stack for (StkPtr value = state->stack; value < state->top; value++) { markValue(state, *value); @@ -228,33 +244,34 @@ void markRoots(CState *state) { // mark all active callframe closures for (int i = 0; i < state->frameCount; i++) { - markObject(state, (CObj*)state->callFrame[i].closure); + markObject(state, (CObj *)state->callFrame[i].closure); } // mark all open upvalues for (CObjUpval *upvalue = state->openUpvalues; upvalue != NULL; upvalue = upvalue->next) { - markObject(state, (CObj*)upvalue); + markObject(state, (CObj *)upvalue); } - markObject(state, (CObj*)state->globals); + markObject(state, (CObj *)state->globals); // mark all internal strings for (int i = 0; i < ISTRING_MAX; i++) - markObject(state, (CObj*)state->iStrings[i]); + markObject(state, (CObj *)state->iStrings[i]); // mark the user defined roots markUserRoots(state); // mark other misc. internally reserved objects - markObject(state, (CObj*)state->error); + markObject(state, (CObj *)state->error); for (int i = 0; i < COBJ_MAX; i++) - markObject(state, (CObj*)state->protoObjects[i]); + markObject(state, (CObj *)state->protoObjects[i]); traceGrays(state); } -COSMO_API void cosmoM_collectGarbage(CState *state) { +COSMO_API void cosmoM_collectGarbage(CState *state) +{ #ifdef GC_DEBUG printf("-- GC start\n"); size_t start = state->allocatedBytes; @@ -263,41 +280,48 @@ COSMO_API void cosmoM_collectGarbage(CState *state) { markRoots(state); - tableRemoveWhite(state, &state->strings); // make sure we aren't referencing any strings that are about to be freed + tableRemoveWhite( + state, + &state->strings); // make sure we aren't referencing any strings that are about to be freed // now finally, free all the unmarked objects sweep(state); // set our next GC event cosmoM_updateThreshhold(state); - state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC event (if GC_STRESS is defined) + state->freezeGC--; // we don't want to use cosmoM_unfreezeGC because that might trigger a GC + // event (if GC_STRESS is defined) #ifdef GC_DEBUG - printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n", - start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); + printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection " + "scheduled at %ld bytes\n", + start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); getchar(); // pauses execution #endif } -COSMO_API void cosmoM_updateThreshhold(CState *state) { +COSMO_API void cosmoM_updateThreshhold(CState *state) +{ state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR; } -COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) { +COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) +{ // first, check and make sure this root doesn't already exist in the list CObj *root = state->userRoots; while (root != NULL) { if (root == newRoot) // found in the list, abort return; - + root = root->nextRoot; } - + // adds root to userRoot linked list newRoot->nextRoot = state->userRoots; state->userRoots = newRoot; } -COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot) { +COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot) +{ CObj *prev = NULL; CObj *root = state->userRoots; diff --git a/src/cmem.h b/src/cmem.h index 1ac7cdb..a55f25c 100644 --- a/src/cmem.h +++ b/src/cmem.h @@ -2,69 +2,67 @@ #define CMEME_C // meme lol #include "cosmo.h" - #include "cstate.h" -//#define GC_STRESS -//#define GC_DEBUG -// arrays *must* grow by a factor of 2 -#define GROW_FACTOR 2 +// #define GC_STRESS +// #define GC_DEBUG +// arrays *must* grow by a factor of 2 +#define GROW_FACTOR 2 #define HEAP_GROW_FACTOR 2 -#define ARRAY_START 8 +#define ARRAY_START 8 #ifdef GC_DEBUG -#define cosmoM_freearray(state, type, buf, capacity) \ - printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, __LINE__); \ - cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) +# define cosmoM_freearray(state, type, buf, capacity) \ + printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, \ + __LINE__); \ + cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) #else -#define cosmoM_freearray(state, type, buf, capacity) \ - cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) +# define cosmoM_freearray(state, type, buf, capacity) \ + cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) #endif -#define cosmoM_growarray(state, type, buf, count, capacity) \ - if (count >= capacity || buf == NULL) { \ - int old = capacity; \ - capacity = old * GROW_FACTOR; \ - buf = (type*)cosmoM_reallocate(state, buf, sizeof(type) *old, sizeof(type) *capacity); \ +#define cosmoM_growarray(state, type, buf, count, capacity) \ + if (count >= capacity || buf == NULL) { \ + int old = capacity; \ + capacity = old * GROW_FACTOR; \ + buf = (type *)cosmoM_reallocate(state, buf, sizeof(type) * old, sizeof(type) * capacity); \ } #ifdef GC_DEBUG -#define cosmoM_free(state, type, x) \ - printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \ - cosmoM_reallocate(state, x, sizeof(type), 0) +# define cosmoM_free(state, type, x) \ + printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__); \ + cosmoM_reallocate(state, x, sizeof(type), 0) #else -#define cosmoM_free(state, type, x) \ - cosmoM_reallocate(state, x, sizeof(type), 0) +# define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0) #endif -#define cosmoM_isFrozen(state) \ - (state->freezeGC > 0) +#define cosmoM_isFrozen(state) (state->freezeGC > 0) // if debugging, print the locations of when the state is frozen/unfrozen #ifdef GC_DEBUG -#define cosmoM_freezeGC(state) \ - state->freezeGC++; \ - printf("freezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC) +# define cosmoM_freezeGC(state) \ + state->freezeGC++; \ + printf("freezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC) -#define cosmoM_unfreezeGC(state) \ - state->freezeGC--; \ - printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \ - cosmoM_checkGarbage(state, 0) +# define cosmoM_unfreezeGC(state) \ + state->freezeGC--; \ + printf("unfreezing state at %s:%d [%d]\n", __FILE__, __LINE__, state->freezeGC); \ + cosmoM_checkGarbage(state, 0) #else // freeze's the garbage collector until cosmoM_unfreezeGC is called -#define cosmoM_freezeGC(state) \ - state->freezeGC++ +# define cosmoM_freezeGC(state) state->freezeGC++ // unfreeze's the garbage collector and tries to run a garbage collection cycle -#define cosmoM_unfreezeGC(state) \ - state->freezeGC--; \ - cosmoM_checkGarbage(state, 0) +# define cosmoM_unfreezeGC(state) \ + state->freezeGC--; \ + cosmoM_checkGarbage(state, 0) -#endif +#endif COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize); -COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed); // returns true if GC event was triggered +COSMO_API bool cosmoM_checkGarbage(CState *state, + size_t needed); // returns true if GC event was triggered COSMO_API void cosmoM_collectGarbage(CState *state); COSMO_API void cosmoM_updateThreshhold(CState *state); @@ -75,7 +73,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot); COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot); // wrapper for cosmoM_reallocate so we can track our memory usage (it's also safer :P) -static inline void *cosmoM_xmalloc(CState *state, size_t sz) { +static inline void *cosmoM_xmalloc(CState *state, size_t sz) +{ return cosmoM_reallocate(state, NULL, 0, sz); } diff --git a/src/cobj.c b/src/cobj.c index 467cb19..22354b7 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -1,27 +1,30 @@ +#include "cobj.h" + +#include "clex.h" +#include "cmem.h" #include "cstate.h" #include "ctable.h" -#include "cobj.h" -#include "cmem.h" #include "cvm.h" -#include "clex.h" -#include #include +#include #include // we don't actually hash the whole string :eyes: -uint32_t hashString(const char *str, size_t sz) { +uint32_t hashString(const char *str, size_t sz) +{ uint32_t hash = sz; - size_t step = (sz>>5)+1; + size_t step = (sz >> 5) + 1; - for (size_t i = sz; i >= step; i-=step) - hash = ((hash << 5) + (hash>>2)) + str[i-1]; + for (size_t i = sz; i >= step; i -= step) + hash = ((hash << 5) + (hash >> 2)) + str[i - 1]; return hash; } -CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) { - CObj* obj = (CObj*)cosmoM_xmalloc(state, sz); +CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) +{ + CObj *obj = (CObj *)cosmoM_xmalloc(state, sz); obj->type = type; obj->isMarked = false; obj->proto = state->protoObjects[type]; @@ -36,77 +39,81 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) { return obj; } -void cosmoO_free(CState *state, CObj *obj) { +void cosmoO_free(CState *state, CObj *obj) +{ #ifdef GC_DEBUG printf("freeing %p [", obj); printObject(obj); printf("]\n"); #endif - switch(obj->type) { - case COBJ_STRING: { - CObjString *objStr = (CObjString*)obj; - cosmoM_freearray(state, char, objStr->str, objStr->length + 1); - cosmoM_free(state, CObjString, objStr); - break; - } - case COBJ_OBJECT: { - CObjObject *objTbl = (CObjObject*)obj; - cosmoT_clearTable(state, &objTbl->tbl); - cosmoM_free(state, CObjObject, objTbl); - break; - } - case COBJ_STREAM: { - CObjStream *objStrm = (CObjStream*)obj; - close(objStrm->fd); - cosmoM_free(state, CObjStream, objStrm); - break; - } - case COBJ_TABLE: { - CObjTable *tbl = (CObjTable*)obj; - cosmoT_clearTable(state, &tbl->tbl); - cosmoM_free(state, CObjTable, tbl); - break; - } - case COBJ_UPVALUE: { - cosmoM_free(state, CObjUpval, obj); - break; - } - case COBJ_FUNCTION: { - CObjFunction *objFunc = (CObjFunction*)obj; - cleanChunk(state, &objFunc->chunk); - cosmoM_free(state, CObjFunction, objFunc); - break; - } - case COBJ_CFUNCTION: { - cosmoM_free(state, CObjCFunction, obj); - break; - } - case COBJ_METHOD: { - cosmoM_free(state, CObjMethod, obj); // we don't own the closure or the object so /shrug - break; - } - case COBJ_ERROR: { - CObjError *err = (CObjError*)obj; - cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount); - cosmoM_free(state, CObjError, obj); - break; - } - case COBJ_CLOSURE: { - CObjClosure* closure = (CObjClosure*)obj; - cosmoM_freearray(state, CObjUpval*, closure->upvalues, closure->upvalueCount); - cosmoM_free(state, CObjClosure, closure); - break; - } - case COBJ_MAX: - default: { /* stubbed, should never happen */ } + switch (obj->type) { + case COBJ_STRING: { + CObjString *objStr = (CObjString *)obj; + cosmoM_freearray(state, char, objStr->str, objStr->length + 1); + cosmoM_free(state, CObjString, objStr); + break; + } + case COBJ_OBJECT: { + CObjObject *objTbl = (CObjObject *)obj; + cosmoT_clearTable(state, &objTbl->tbl); + cosmoM_free(state, CObjObject, objTbl); + break; + } + case COBJ_STREAM: { + CObjStream *objStrm = (CObjStream *)obj; + close(objStrm->fd); + cosmoM_free(state, CObjStream, objStrm); + break; + } + case COBJ_TABLE: { + CObjTable *tbl = (CObjTable *)obj; + cosmoT_clearTable(state, &tbl->tbl); + cosmoM_free(state, CObjTable, tbl); + break; + } + case COBJ_UPVALUE: { + cosmoM_free(state, CObjUpval, obj); + break; + } + case COBJ_FUNCTION: { + CObjFunction *objFunc = (CObjFunction *)obj; + cleanChunk(state, &objFunc->chunk); + cosmoM_free(state, CObjFunction, objFunc); + break; + } + case COBJ_CFUNCTION: { + cosmoM_free(state, CObjCFunction, obj); + break; + } + case COBJ_METHOD: { + cosmoM_free(state, CObjMethod, obj); // we don't own the closure or the object so /shrug + break; + } + case COBJ_ERROR: { + CObjError *err = (CObjError *)obj; + cosmoM_freearray(state, CCallFrame, err->frames, err->frameCount); + cosmoM_free(state, CObjError, obj); + break; + } + case COBJ_CLOSURE: { + CObjClosure *closure = (CObjClosure *)obj; + cosmoM_freearray(state, CObjUpval *, closure->upvalues, closure->upvalueCount); + cosmoM_free(state, CObjClosure, closure); + break; + } + case COBJ_MAX: + default: { /* stubbed, should never happen */ + } } } -bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) { +bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) +{ CObjObject *proto1, *proto2; CValue eq1, eq2; - if (obj1 == obj2) // its the same reference, this compares strings for us since they're interned anyways :) + if (obj1 == obj2) // its the same reference, this compares strings for us since they're interned + // anyways :) return true; // its not the same type, maybe both 's have the same '__equal' metamethod in their protos? @@ -114,45 +121,52 @@ bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) { goto _eqFail; switch (obj1->type) { - case COBJ_STRING: { - /* - we already compared the pointers at the top of the function, this prevents the `__equal` metamethod - from being checked. If you plan on using `__equal` with strings just remove this case! - */ - return false; - } - case COBJ_CFUNCTION: { - CObjCFunction *cfunc1 = (CObjCFunction*)obj1; - CObjCFunction *cfunc2 = (CObjCFunction*)obj2; - if (cfunc1->cfunc == cfunc2->cfunc) - return true; - goto _eqFail; - } - case COBJ_METHOD: { - CObjMethod *method1 = (CObjMethod*)obj1; - CObjMethod *method2 = (CObjMethod*)obj2; - if (cosmoV_equal(state, method1->func, method2->func)) - return true; - goto _eqFail; - } - case COBJ_CLOSURE: { - CObjClosure *closure1 = (CObjClosure*)obj1; - CObjClosure *closure2 = (CObjClosure*)obj2; - // we just compare the function pointer - if (closure1->function == closure2->function) - return true; - goto _eqFail; - } - default: - goto _eqFail; + case COBJ_STRING: { + /* + we already compared the pointers at the top of the function, this prevents the `__equal` + metamethod from being checked. If you plan on using `__equal` with strings just remove + this case! + */ + return false; + } + case COBJ_CFUNCTION: { + CObjCFunction *cfunc1 = (CObjCFunction *)obj1; + CObjCFunction *cfunc2 = (CObjCFunction *)obj2; + if (cfunc1->cfunc == cfunc2->cfunc) + return true; + goto _eqFail; + } + case COBJ_METHOD: { + CObjMethod *method1 = (CObjMethod *)obj1; + CObjMethod *method2 = (CObjMethod *)obj2; + if (cosmoV_equal(state, method1->func, method2->func)) + return true; + goto _eqFail; + } + case COBJ_CLOSURE: { + CObjClosure *closure1 = (CObjClosure *)obj1; + CObjClosure *closure2 = (CObjClosure *)obj2; + // we just compare the function pointer + if (closure1->function == closure2->function) + return true; + goto _eqFail; + } + default: + goto _eqFail; } _eqFail: - // this is pretty expensive (bad lookup caching helps a lot), but it only all gets run if both objects have protos & both have the `__equal` metamethod defined so... - // it should stay light for the majority of cases - if ((proto1 = cosmoO_grabProto(obj1)) != NULL && (proto2 = cosmoO_grabProto(obj2)) != NULL && // make sure both protos exist - cosmoO_getIString(state, proto1, ISTRING_EQUAL, &eq1) && // grab the `__equal` metamethod from the first proto, if fail abort - cosmoO_getIString(state, proto2, ISTRING_EQUAL, &eq2) && // grab the `__equal` metamethod from the second proto, if fail abort + // this is pretty expensive (bad lookup caching helps a lot), but it only all gets run if both + // objects have protos & both have the `__equal` metamethod defined so... it should stay light + // for the majority of cases + if ((proto1 = cosmoO_grabProto(obj1)) != NULL && + (proto2 = cosmoO_grabProto(obj2)) != NULL && // make sure both protos exist + cosmoO_getIString( + state, proto1, ISTRING_EQUAL, + &eq1) && // grab the `__equal` metamethod from the first proto, if fail abort + cosmoO_getIString( + state, proto2, ISTRING_EQUAL, + &eq2) && // grab the `__equal` metamethod from the second proto, if fail abort cosmoV_equal(state, eq1, eq2)) { // compare the two `__equal` metamethods // now finally, call the `__equal` metamethod (, ) @@ -164,7 +178,8 @@ _eqFail: // check return value and make sure it's a boolean if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) { - cosmoV_error(state, "__equal expected to return , got %s!", cosmoV_typeStr(*cosmoV_pop(state))); + cosmoV_error(state, "__equal expected to return , got %s!", + cosmoV_typeStr(*cosmoV_pop(state))); return false; } @@ -175,39 +190,44 @@ _eqFail: return false; } -CObjObject *cosmoO_newObject(CState *state) { - CObjObject *obj = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT); +CObjObject *cosmoO_newObject(CState *state) +{ + CObjObject *obj = (CObjObject *)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT); obj->istringFlags = 0; obj->userP = NULL; // reserved for C API obj->userT = 0; obj->isLocked = false; - cosmoV_pushRef(state, (CObj*)obj); // so our GC can keep track of it + cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of it cosmoT_initTable(state, &obj->tbl, ARRAY_START); cosmoV_pop(state); return obj; } -CObjStream *cosmoO_newStream(CState *state, int fd) { - CObjStream *strm = (CObjStream*)cosmoO_allocateBase(state, sizeof(CObjStream), COBJ_STREAM); +CObjStream *cosmoO_newStream(CState *state, int fd) +{ + CObjStream *strm = (CObjStream *)cosmoO_allocateBase(state, sizeof(CObjStream), COBJ_STREAM); strm->fd = fd; return strm; } -CObjTable *cosmoO_newTable(CState *state) { - CObjTable *obj = (CObjTable*)cosmoO_allocateBase(state, sizeof(CObjTable), COBJ_TABLE); +CObjTable *cosmoO_newTable(CState *state) +{ + CObjTable *obj = (CObjTable *)cosmoO_allocateBase(state, sizeof(CObjTable), COBJ_TABLE); // init the table (might cause a GC event) - cosmoV_pushRef(state, (CObj*)obj); // so our GC can keep track of obj + cosmoV_pushRef(state, (CObj *)obj); // so our GC can keep track of obj cosmoT_initTable(state, &obj->tbl, ARRAY_START); cosmoV_pop(state); return obj; } -CObjFunction *cosmoO_newFunction(CState *state) { - CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION); +CObjFunction *cosmoO_newFunction(CState *state) +{ + CObjFunction *func = + (CObjFunction *)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION); func->args = 0; func->upvals = 0; func->variadic = false; @@ -218,14 +238,17 @@ CObjFunction *cosmoO_newFunction(CState *state) { return func; } -CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) { - CObjCFunction *cfunc = (CObjCFunction*)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION); +CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) +{ + CObjCFunction *cfunc = + (CObjCFunction *)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION); cfunc->cfunc = func; return cfunc; } -CObjError *cosmoO_newError(CState *state, CValue err) { - CObjError *cerror = (CObjError*)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); +CObjError *cosmoO_newError(CState *state, CValue err) +{ + CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); cerror->err = err; cerror->frameCount = state->frameCount; cerror->parserError = false; @@ -236,26 +259,29 @@ CObjError *cosmoO_newError(CState *state, CValue err) { // clone the call frame for (int i = 0; i < state->frameCount; i++) cerror->frames[i] = state->callFrame[i]; - + return cerror; } -CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj) { - CObjMethod *method = (CObjMethod*)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD); +CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj) +{ + CObjMethod *method = (CObjMethod *)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD); method->func = func; method->obj = obj; return method; } -CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) { +CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) +{ // initialize array of pointers - CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval*) * func->upvals); + CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval *) * func->upvals); for (int i = 0; i < func->upvals; i++) { upvalues[i] = NULL; } - CObjClosure *closure = (CObjClosure*)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE); + CObjClosure *closure = + (CObjClosure *)cosmoO_allocateBase(state, sizeof(CObjClosure), COBJ_CLOSURE); closure->function = func; closure->upvalues = upvalues; closure->upvalueCount = func->upvals; @@ -263,8 +289,9 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) { return closure; } -CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) { - CObjUpval *upval = (CObjUpval*)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE); +CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) +{ + CObjUpval *upval = (CObjUpval *)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE); upval->val = val; upval->closed = cosmoV_newNil(); upval->next = NULL; @@ -272,7 +299,8 @@ CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) { return upval; } -CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) { +CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) +{ uint32_t hash = hashString(str, length); CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); @@ -281,43 +309,49 @@ CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) { return lookup; char *buf = cosmoM_xmalloc(state, sizeof(char) * (length + 1)); // +1 for null terminator - memcpy(buf, str, length); // copy string to heap + memcpy(buf, str, length); // copy string to heap buf[length] = '\0'; // don't forget our null terminator return cosmoO_allocateString(state, buf, length, hash); } -// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!) -CObjString *cosmoO_takeString(CState *state, char *str, size_t length) { +// length shouldn't include the null terminator! str should be a null terminated string! (char array +// should also have been allocated using cosmoM_xmalloc!) +CObjString *cosmoO_takeString(CState *state, char *str, size_t length) +{ uint32_t hash = hashString(str, length); CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); // have we already interned this string? if (lookup != NULL) { - cosmoM_freearray(state, char, str, length + 1); // free our passed character array, it's unneeded! + cosmoM_freearray(state, char, str, + length + 1); // free our passed character array, it's unneeded! return lookup; } return cosmoO_allocateString(state, str, length, hash); } -CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash) { - CObjString *strObj = (CObjString*)cosmoO_allocateBase(state, sizeof(CObjString), COBJ_STRING); +CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash) +{ + CObjString *strObj = (CObjString *)cosmoO_allocateBase(state, sizeof(CObjString), COBJ_STRING); strObj->isIString = false; - strObj->str = (char*)str; + strObj->str = (char *)str; strObj->length = sz; strObj->hash = hash; - // we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we *want* a GC event to happen) - cosmoV_pushRef(state, (CObj*)strObj); - cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj*)strObj)); + // we push & pop the string so our GC can find it (we don't use freezeGC/unfreezeGC because we + // *want* a GC event to happen) + cosmoV_pushRef(state, (CObj *)strObj); + cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj)); cosmoV_pop(state); return strObj; } -CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) { +CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) +{ StkPtr start = state->top; const char *end; char c; @@ -325,56 +359,58 @@ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) while (true) { end = strchr(format, '%'); // grab the next occurrence of '%' - len = -1; // -1 means no length specified - if (end == NULL) // the end, no '%' found + len = -1; // -1 means no length specified + if (end == NULL) // the end, no '%' found break; // push the string before '%' cosmoV_pushLString(state, format, (end - format)); - reentry: + reentry: c = end[1]; // the character right after '%' switch (c) { - case 'd': // int - cosmoV_pushNumber(state, va_arg(args, int)); - break; - case 'f': // double - cosmoV_pushNumber(state, va_arg(args, double)); - break; - case 's': // char * - if (len >= 0) // the length is specified - cosmoV_pushLString(state, va_arg(args, char *), len); - else - cosmoV_pushString(state, va_arg(args, char *)); - break; - case '*': // length specifier - len = va_arg(args, int); - end++; // skip '*' - goto reentry; - default: { - char temp[2]; - temp[0] = '%'; - temp[1] = c; - cosmoV_pushLString(state, temp, 2); - } + case 'd': // int + cosmoV_pushNumber(state, va_arg(args, int)); + break; + case 'f': // double + cosmoV_pushNumber(state, va_arg(args, double)); + break; + case 's': // char * + if (len >= 0) // the length is specified + cosmoV_pushLString(state, va_arg(args, char *), len); + else + cosmoV_pushString(state, va_arg(args, char *)); + break; + case '*': // length specifier + len = va_arg(args, int); + end++; // skip '*' + goto reentry; + default: { + char temp[2]; + temp[0] = '%'; + temp[1] = c; + cosmoV_pushLString(state, temp, 2); + } } format = end + 2; // + 2 because of % and the following character } cosmoV_pushString(state, format); // push the rest of the string - cosmoV_concat(state, state->top - start); // use cosmoV_concat to concat all the strings on the stack - return cosmoV_readString(*start); // start should be state->top - 1 + cosmoV_concat(state, + state->top - start); // use cosmoV_concat to concat all the strings on the stack + 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) { +bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) +{ CObjObject *curr = obj->proto; while (curr != NULL) { - if (curr == proto) + if (curr == proto) return true; // found proto! return true - curr = ((CObj*)curr)->proto; + curr = ((CObj *)curr)->proto; } // we didn't find the proto @@ -382,20 +418,24 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) { } // returns false if error thrown -bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) { - if (!cosmoT_get(state, &proto->tbl, key, val)) { // if the field doesn't exist in the object, check the proto - if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) { - cosmoV_pushValue(state, *val); // push function - cosmoV_pushRef(state, (CObj*)obj); // push object +bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) +{ + if (!cosmoT_get(state, &proto->tbl, key, + val)) { // if the field doesn't exist in the object, check the proto + if (cosmoO_getIString(state, proto, ISTRING_GETTER, val) && IS_TABLE(*val) && + cosmoT_get(state, &cosmoV_readTable(*val)->tbl, key, val)) { + cosmoV_pushValue(state, *val); // push function + cosmoV_pushRef(state, (CObj *)obj); // push object if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument return false; *val = *cosmoV_pop(state); // set value to the return value of __index return true; } - - if (proto->_obj.proto != NULL && cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj)) + + if (proto->_obj.proto != NULL && + cosmoO_getRawObject(state, proto->_obj.proto, key, val, obj)) return true; - + *val = cosmoV_newNil(); return true; // no protoobject to check against / key not found } @@ -403,7 +443,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v return true; } -void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj) { +void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue val, CObj *obj) +{ CValue ret; // if the object is locked, throw an error @@ -413,10 +454,11 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va } // check for __setters - if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) { - cosmoV_pushValue(state, ret); // push function - cosmoV_pushRef(state, (CObj*)obj); // push object - cosmoV_pushValue(state, val); // push new value + if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && + cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) { + cosmoV_pushValue(state, ret); // push function + cosmoV_pushRef(state, (CObj *)obj); // push object + cosmoV_pushValue(state, val); // push new value cosmoV_call(state, 2, 0); return; } @@ -433,39 +475,48 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va } } -void cosmoO_setUserP(CObjObject *object, void *p) { +void cosmoO_setUserP(CObjObject *object, void *p) +{ object->userP = p; } -void *cosmoO_getUserP(CObjObject *object) { +void *cosmoO_getUserP(CObjObject *object) +{ return object->userP; } -void cosmoO_setUserI(CObjObject *object, int i) { +void cosmoO_setUserI(CObjObject *object, int i) +{ object->userI = i; } -int cosmoO_getUserI(CObjObject *object) { +int cosmoO_getUserI(CObjObject *object) +{ return object->userI; } -void cosmoO_setUserT(CObjObject *object, int t) { +void cosmoO_setUserT(CObjObject *object, int t) +{ object->userT = t; } -int cosmoO_getUserT(CObjObject *object) { +int cosmoO_getUserT(CObjObject *object) +{ return object->userT; } -void cosmoO_lock(CObjObject *object) { +void cosmoO_lock(CObjObject *object) +{ object->isLocked = true; } -void cosmoO_unlock(CObjObject *object) { +void cosmoO_unlock(CObjObject *object) +{ object->isLocked = false; } -bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) { +bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) +{ if (readFlag(object->istringFlags, flag)) return false; // it's been cached as bad @@ -478,7 +529,8 @@ bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) { return true; // :) } -bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) { +bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) +{ CObjObject *obj = object; do { @@ -489,11 +541,12 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) return false; // obj->proto was false, the istring doesn't exist in this object chain } -bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) { +bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val) +{ if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) { - cosmoV_pushValue(state, *val); // push function - cosmoV_pushRef(state, (CObj*)object); // push object - cosmoV_pushValue(state, key); // push key + cosmoV_pushValue(state, *val); // push function + cosmoV_pushRef(state, (CObj *)object); // push object + cosmoV_pushValue(state, key); // push key if (cosmoV_call(state, 2, 1) != COSMOVM_OK) // call the function with the 2 arguments return false; *val = *cosmoV_pop(state); // set value to the return value of __index @@ -505,13 +558,14 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v return false; } -bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val) { +bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val) +{ CValue ret; // return value for cosmoO_getIString if (cosmoO_getIString(state, object, ISTRING_NEWINDEX, &ret)) { - cosmoV_pushValue(state, ret); // push function - cosmoV_pushRef(state, (CObj*)object); // push object - cosmoV_pushValue(state, key); // push key & value pair + cosmoV_pushValue(state, ret); // push function + cosmoV_pushRef(state, (CObj *)object); // push object + cosmoV_pushValue(state, key); // push key & value pair cosmoV_pushValue(state, val); return cosmoV_call(state, 3, 0) == COSMOVM_OK; } else { // there's no __newindex function defined @@ -521,82 +575,88 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue return false; } -CObjString *cosmoO_toString(CState *state, CObj *obj) { +CObjString *cosmoO_toString(CState *state, CObj *obj) +{ CObjObject *protoObject = cosmoO_grabProto(obj); CValue res; // use user-defined __tostring if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_TOSTRING, &res)) { cosmoV_pushValue(state, res); - cosmoV_pushRef(state, (CObj*)obj); + cosmoV_pushRef(state, (CObj *)obj); if (cosmoV_call(state, 1, 1) != COSMOVM_OK) return cosmoO_copyString(state, "", 5); // make sure the __tostring function returned a string StkPtr ret = cosmoV_getTop(state, 0); if (!IS_STRING(*ret)) { - cosmoV_error(state, "__tostring expected to return , got %s!", cosmoV_typeStr(*ret)); + cosmoV_error(state, "__tostring expected to return , got %s!", + cosmoV_typeStr(*ret)); return cosmoO_copyString(state, "", 5); } // return string cosmoV_pop(state); - return (CObjString*)cosmoV_readRef(*ret); + return (CObjString *)cosmoV_readRef(*ret); } - switch (obj->type) { - case COBJ_STRING: { - return (CObjString*)obj; - } - case COBJ_CLOSURE: { // should be transparent to the user imo - CObjClosure *closure = (CObjClosure*)obj; - return cosmoO_toString(state, (CObj*)closure->function); - } - case COBJ_FUNCTION: { - CObjFunction *func = (CObjFunction*)obj; - return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); - } - case COBJ_CFUNCTION: { - CObjCFunction *cfunc = (CObjCFunction*)obj; - char buf[64]; - int sz = sprintf(buf, " %p", (void*)cfunc->cfunc) + 1; // +1 for the null character - return cosmoO_copyString(state, buf, sz); - } - case COBJ_OBJECT: { - char buf[64]; - int sz = sprintf(buf, " %p", (void*)obj) + 1; // +1 for the null character - return cosmoO_copyString(state, buf, sz); - } - case COBJ_ERROR: { - CObjError *err = (CObjError*)obj; - return cosmoV_toString(state, err->err); - } - case COBJ_TABLE: { - char buf[64]; - int sz = sprintf(buf, " %p", (void*)obj) + 1; // +1 for the null character - return cosmoO_copyString(state, buf, sz); - } - default: { - char buf[64]; - int sz = sprintf(buf, " %p", (void*)obj) + 1; // +1 for the null character - return cosmoO_copyString(state, buf, sz); - } + switch (obj->type) { + case COBJ_STRING: { + return (CObjString *)obj; + } + case COBJ_CLOSURE: { // should be transparent to the user imo + CObjClosure *closure = (CObjClosure *)obj; + return cosmoO_toString(state, (CObj *)closure->function); + } + case COBJ_FUNCTION: { + CObjFunction *func = (CObjFunction *)obj; + return func->name != NULL ? func->name + : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); + } + case COBJ_CFUNCTION: { + CObjCFunction *cfunc = (CObjCFunction *)obj; + char buf[64]; + int sz = + sprintf(buf, " %p", (void *)cfunc->cfunc) + 1; // +1 for the null character + return cosmoO_copyString(state, buf, sz); + } + case COBJ_OBJECT: { + char buf[64]; + int sz = sprintf(buf, " %p", (void *)obj) + 1; // +1 for the null character + return cosmoO_copyString(state, buf, sz); + } + case COBJ_ERROR: { + CObjError *err = (CObjError *)obj; + return cosmoV_toString(state, err->err); + } + case COBJ_TABLE: { + char buf[64]; + int sz = sprintf(buf, " %p", (void *)obj) + 1; // +1 for the null character + return cosmoO_copyString(state, buf, sz); + } + default: { + char buf[64]; + int sz = sprintf(buf, " %p", (void *)obj) + 1; // +1 for the null character + return cosmoO_copyString(state, buf, sz); + } } } -cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) { +cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) +{ CObjObject *proto = cosmoO_grabProto(obj); CValue res; if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_TONUMBER, &res)) { cosmoV_pushValue(state, res); - cosmoV_pushRef(state, (CObj*)obj); + cosmoV_pushRef(state, (CObj *)obj); if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, expect 1 return val of return 0; - + StkPtr temp = cosmoV_getTop(state, 0); if (!IS_NUMBER(*temp)) { - cosmoV_error(state, "__tonumber expected to return , got %s!", cosmoV_typeStr(*temp)); + cosmoV_error(state, "__tonumber expected to return , got %s!", + cosmoV_typeStr(*temp)); return 0; } @@ -606,28 +666,31 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) { } switch (obj->type) { - case COBJ_STRING: { - CObjString *str = (CObjString*)obj; - return strtod(str->str, NULL); - } - default: // maybe in the future throw an error? - return 0; + case COBJ_STRING: { + CObjString *str = (CObjString *)obj; + return strtod(str->str, NULL); + } + default: // maybe in the future throw an error? + return 0; } } -int cosmoO_count(CState *state, CObj *obj) { +int cosmoO_count(CState *state, CObj *obj) +{ CObjObject *proto = cosmoO_grabProto(obj); CValue res; if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) { cosmoV_pushValue(state, res); - cosmoV_pushRef(state, (CObj*)obj); - if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call res, we expect 1 return value of type + cosmoV_pushRef(state, (CObj *)obj); + if (cosmoV_call(state, 1, 1) != + COSMOVM_OK) // call res, we expect 1 return value of type return 0; StkPtr ret = cosmoV_getTop(state, 0); if (!IS_NUMBER(*ret)) { - cosmoV_error(state, "__count expected to return , got %s!", cosmoV_typeStr(*ret)); + cosmoV_error(state, "__count expected to return , got %s!", + cosmoV_typeStr(*ret)); return 0; } @@ -637,90 +700,100 @@ int cosmoO_count(CState *state, CObj *obj) { } switch (obj->type) { - case COBJ_TABLE: { // returns the # of entries in the hash table - CObjTable *tbl = (CObjTable*)obj; - return cosmoT_count(&tbl->tbl); - } - case COBJ_STRING: { // returns the length of the string - CObjString *str = (CObjString*)obj; - return str->length; - } - default: - cosmoV_error(state, "Couldn't get # (count) of %s!", cosmoO_typeStr(obj)); - return 0; + case COBJ_TABLE: { // returns the # of entries in the hash table + CObjTable *tbl = (CObjTable *)obj; + return cosmoT_count(&tbl->tbl); + } + case COBJ_STRING: { // returns the length of the string + CObjString *str = (CObjString *)obj; + return str->length; + } + default: + cosmoV_error(state, "Couldn't get # (count) of %s!", cosmoO_typeStr(obj)); + return 0; } } -void printObject(CObj *o) { +void printObject(CObj *o) +{ switch (o->type) { - case COBJ_STRING: { - CObjString *objStr = (CObjString*)o; - printf("%.*s", objStr->length, objStr->str); - break; - } - case COBJ_OBJECT: { - printf(" %p", (void*)o); - break; - } - case COBJ_TABLE: { - CObjTable *tbl = (CObjTable*)o; - printf(" %p", (void*)tbl); - break; - } - case COBJ_FUNCTION: { - CObjFunction *objFunc = (CObjFunction*)o; - if (objFunc->name != NULL) - printf(" %.*s", objFunc->name->length, objFunc->name->str); - else - printf(" %s", UNNAMEDCHUNK); - break; - } - case COBJ_CFUNCTION: { - CObjCFunction *objCFunc = (CObjCFunction*)o; - printf(" %p", (void*)objCFunc->cfunc); - break; - } - case COBJ_ERROR: { - CObjError *err = (CObjError*)o; - printf(" %p -> ", (void*)o); - printValue(err->err); - break; - } - case COBJ_METHOD: { - CObjMethod *method = (CObjMethod*)o; - printf(" %p -> ", (void*)method); - printValue(method->func); - break; - } - case COBJ_CLOSURE: { - CObjClosure *closure = (CObjClosure*)o; - printf(" %p -> ", (void*)closure); - printObject((CObj*)closure->function); // just print the function - break; - } - case COBJ_UPVALUE: { - CObjUpval *upval = (CObjUpval*)o; - printf(" %p -> ", (void*)upval->val); - printValue(*upval->val); - break; - } - default: - printf("", (void*)o); + case COBJ_STRING: { + CObjString *objStr = (CObjString *)o; + printf("%.*s", objStr->length, objStr->str); + break; + } + case COBJ_OBJECT: { + printf(" %p", (void *)o); + break; + } + case COBJ_TABLE: { + CObjTable *tbl = (CObjTable *)o; + printf(" %p", (void *)tbl); + break; + } + case COBJ_FUNCTION: { + CObjFunction *objFunc = (CObjFunction *)o; + if (objFunc->name != NULL) + printf(" %.*s", objFunc->name->length, objFunc->name->str); + else + printf(" %s", UNNAMEDCHUNK); + break; + } + case COBJ_CFUNCTION: { + CObjCFunction *objCFunc = (CObjCFunction *)o; + printf(" %p", (void *)objCFunc->cfunc); + break; + } + case COBJ_ERROR: { + CObjError *err = (CObjError *)o; + printf(" %p -> ", (void *)o); + printValue(err->err); + break; + } + case COBJ_METHOD: { + CObjMethod *method = (CObjMethod *)o; + printf(" %p -> ", (void *)method); + printValue(method->func); + break; + } + case COBJ_CLOSURE: { + CObjClosure *closure = (CObjClosure *)o; + printf(" %p -> ", (void *)closure); + printObject((CObj *)closure->function); // just print the function + break; + } + case COBJ_UPVALUE: { + CObjUpval *upval = (CObjUpval *)o; + printf(" %p -> ", (void *)upval->val); + printValue(*upval->val); + break; + } + default: + printf("", (void *)o); } } -const char *cosmoO_typeStr(CObj* obj) { +const char *cosmoO_typeStr(CObj *obj) +{ switch (obj->type) { - case COBJ_STRING: return ""; - case COBJ_OBJECT: return ""; - case COBJ_TABLE: return "
"; - case COBJ_FUNCTION: return ""; - case COBJ_CFUNCTION: return ""; - case COBJ_METHOD: return ""; - case COBJ_CLOSURE: return ""; - case COBJ_UPVALUE: return ""; + case COBJ_STRING: + return ""; + case COBJ_OBJECT: + return ""; + case COBJ_TABLE: + return "
"; + case COBJ_FUNCTION: + return ""; + case COBJ_CFUNCTION: + return ""; + case COBJ_METHOD: + return ""; + case COBJ_CLOSURE: + return ""; + case COBJ_UPVALUE: + return ""; - default: - return ""; // TODO: maybe panic? could be a malformed object :eyes: + default: + return ""; // TODO: maybe panic? could be a malformed object :eyes: } } diff --git a/src/cobj.h b/src/cobj.h index e2e4676..d46bbfc 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -3,7 +3,8 @@ #include "cosmo.h" -typedef enum CObjType { +typedef enum CObjType +{ COBJ_STRING, COBJ_OBJECT, COBJ_TABLE, @@ -18,52 +19,59 @@ typedef enum CObjType { COBJ_MAX } CObjType; -#include "cstate.h" #include "cchunk.h" -#include "cvalue.h" +#include "cstate.h" #include "ctable.h" +#include "cvalue.h" -#define CommonHeader CObj _obj -#define readFlag(x, flag) (x & (1u << flag)) -#define setFlagOn(x, flag) (x |= (1u << flag)) +#define CommonHeader CObj _obj +#define readFlag(x, flag) (x & (1u << flag)) +#define setFlagOn(x, flag) (x |= (1u << flag)) typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args); -struct CObj { +struct CObj +{ struct CObj *next; - struct CObj *nextRoot; // for the root linked list + struct CObj *nextRoot; // for the root linked list struct CObjObject *proto; // protoobject, describes the behavior of the object CObjType type; bool isMarked; // for the GC }; -struct CObjString { - CommonHeader; // "is a" CObj - char *str; // NULL termincated string +struct CObjString +{ + CommonHeader; // "is a" CObj + char *str; // NULL termincated string uint32_t hash; // for hashtable lookup int length; bool isIString; }; -struct CObjStream { +struct CObjStream +{ CommonHeader; // "is a" CObj int fd; // handle to file descriptor, on POSIX compliant OSes this can also be a socket :pog: }; -struct CObjError { +struct CObjError +{ CommonHeader; // "is a" CObj - CValue err; // error string + CValue err; // error string CCallFrame *frames; int frameCount; - int line; // reserved for parser errors + int line; // reserved for parser errors bool parserError; // if true, cosmoV_printError will format the error to the lexer }; -struct CObjObject { +struct CObjObject +{ CommonHeader; // "is a" CObj CTable tbl; - cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.) - union { // userdata (NULL by default) + cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like + // __init, __index, etc.) + union + { // userdata (NULL by default) void *userP; int userI; }; @@ -71,12 +79,14 @@ struct CObjObject { bool isLocked; }; -struct CObjTable { // table, a wrapper for CTable +struct CObjTable +{ // table, a wrapper for CTable CommonHeader; // "is a" CObj CTable tbl; }; -struct CObjFunction { +struct CObjFunction +{ CommonHeader; // "is a" CObj CChunk chunk; CObjString *name; @@ -86,25 +96,29 @@ struct CObjFunction { bool variadic; }; -struct CObjCFunction { +struct CObjCFunction +{ CommonHeader; // "is a" CObj CosmoCFunction cfunc; }; -struct CObjClosure { +struct CObjClosure +{ CommonHeader; // "is a" CObj CObjFunction *function; CObjUpval **upvalues; int upvalueCount; }; -struct CObjMethod { +struct CObjMethod +{ CommonHeader; // "is a " CObj CValue func; CObj *obj; // obj this method is bound too }; -struct CObjUpval { +struct CObjUpval +{ CommonHeader; // "is a" CObj CValue closed; CValue *val; @@ -113,38 +127,40 @@ struct CObjUpval { #undef CommonHeader -#define IS_STRING(x) isObjType(x, COBJ_STRING) -#define IS_OBJECT(x) isObjType(x, COBJ_OBJECT) -#define IS_STREAM(x) isObjType(x, COBJ_STREAM) -#define IS_TABLE(x) isObjType(x, COBJ_TABLE) -#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION) -#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION) -#define IS_METHOD(x) isObjType(x, COBJ_METHOD) -#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE) +#define IS_STRING(x) isObjType(x, COBJ_STRING) +#define IS_OBJECT(x) isObjType(x, COBJ_OBJECT) +#define IS_STREAM(x) isObjType(x, COBJ_STREAM) +#define IS_TABLE(x) isObjType(x, COBJ_TABLE) +#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION) +#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION) +#define IS_METHOD(x) isObjType(x, COBJ_METHOD) +#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE) -#define cosmoV_readString(x) ((CObjString*)cosmoV_readRef(x)) -#define cosmoV_readCString(x) (((CObjString*)cosmoV_readRef(x))->str) -#define cosmoV_readFD(x) (((CObjStream*)cosmoV_readRef(x))->fd) -#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readRef(x)) -#define cosmoV_readTable(x) ((CObjTable*)cosmoV_readRef(x)) -#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readRef(x)) -#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readRef(x))->cfunc) -#define cosmoV_readMethod(x) ((CObjMethod*)cosmoV_readRef(x)) -#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readRef(x)) +#define cosmoV_readString(x) ((CObjString *)cosmoV_readRef(x)) +#define cosmoV_readCString(x) (((CObjString *)cosmoV_readRef(x))->str) +#define cosmoV_readFD(x) (((CObjStream *)cosmoV_readRef(x))->fd) +#define cosmoV_readObject(x) ((CObjObject *)cosmoV_readRef(x)) +#define cosmoV_readTable(x) ((CObjTable *)cosmoV_readRef(x)) +#define cosmoV_readFunction(x) ((CObjFunction *)cosmoV_readRef(x)) +#define cosmoV_readCFunction(x) (((CObjCFunction *)cosmoV_readRef(x))->cfunc) +#define cosmoV_readMethod(x) ((CObjMethod *)cosmoV_readRef(x)) +#define cosmoV_readClosure(x) ((CObjClosure *)cosmoV_readRef(x)) -#define cosmoO_readCString(x) ((CObjString*)x)->str +#define cosmoO_readCString(x) ((CObjString *)x)->str -static inline bool isObjType(CValue val, CObjType type) { +static inline bool isObjType(CValue val, CObjType type) +{ return IS_REF(val) && cosmoV_readRef(val)->type == type; } // just protects against macro expansion -static inline bool IS_CALLABLE(CValue val) { +static inline bool IS_CALLABLE(CValue val) +{ return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val); -} +} -void cosmoO_free(CState *state, CObj* obj); -bool cosmoO_equal(CState *state, CObj* obj1, CObj* obj2); +void cosmoO_free(CState *state, CObj *obj); +bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2); // walks the protos of obj and checks for proto bool cosmoO_isDescendant(CObj *obj, CObjObject *proto); @@ -160,8 +176,9 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func); CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val); // grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned) -static inline CObjObject *cosmoO_grabProto(CObj *obj) { - return obj->type == COBJ_OBJECT ? (CObjObject*)obj : obj->proto; +static inline CObjObject *cosmoO_grabProto(CObj *obj) +{ + return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto; } bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj); @@ -169,11 +186,13 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *val); bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val); -// sets the user-defined pointer, if a user-define integer is already defined it will be over written +// sets the user-defined pointer, if a user-define integer is already defined it will be over +// written void cosmoO_setUserP(CObjObject *object, void *p); // gets the user-defined pointer void *cosmoO_getUserP(CObjObject *object); -// sets the user-defined integer, if a user-define pointer is already defined it will be over written +// sets the user-defined integer, if a user-define pointer is already defined it will be over +// written void cosmoO_setUserI(CObjObject *object, int i); // gets the user-defined integer int cosmoO_getUserI(CObjObject *object); @@ -189,10 +208,12 @@ void cosmoO_unlock(CObjObject *object); // internal string bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val); -// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap (length should not include the null terminator) +// copies the *str buffer to the heap and returns a CObjString struct which is also on the heap +// (length should not include the null terminator) CObjString *cosmoO_copyString(CState *state, const char *str, size_t length); -// length shouldn't include the null terminator! str should be a null terminated string! (char array should also have been allocated using cosmoM_xmalloc!) +// length shouldn't include the null terminator! str should be a null terminated string! (char array +// should also have been allocated using cosmoM_xmalloc!) CObjString *cosmoO_takeString(CState *state, char *str, size_t length); // allocates a CObjStruct pointing directly to *str @@ -209,7 +230,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t length, CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args); COSMO_API void printObject(CObj *o); -const char *cosmoO_typeStr(CObj* obj); +const char *cosmoO_typeStr(CObj *obj); CObjString *cosmoO_toString(CState *state, CObj *obj); cosmo_Number cosmoO_toNumber(CState *state, CObj *obj); diff --git a/src/coperators.h b/src/coperators.h index 18c7b56..febc762 100644 --- a/src/coperators.h +++ b/src/coperators.h @@ -3,24 +3,25 @@ #include "cosmo.h" -// instructions +// instructions -typedef enum { +typedef enum +{ // STACK/STATE MANIPULATION OP_LOADCONST, // pushes const[uint8_t] to the stack OP_SETGLOBAL, // pops and sets global[const[uint16_t]] OP_GETGLOBAL, // pushes global[const[uint16_t]] - OP_SETLOCAL, // pops and sets base[uint8_t] - OP_GETLOCAL, // pushes base[uint8_t] - OP_GETUPVAL, // pushes closure->upvals[uint8_t] - OP_SETUPVAL, // pops and sets closure->upvals[uint8_t] - OP_PEJMP, // pops, if false jumps uint16_t - OP_EJMP, // if peek(0) is falsey jumps uint16_t - OP_JMP, // always jumps uint16_t - OP_JMPBACK, // jumps -uint16_t - OP_POP, // - pops[uint8_t] from stack - OP_CALL, // calls top[-uint8_t] expecting uint8_t results - OP_CLOSURE, + OP_SETLOCAL, // pops and sets base[uint8_t] + OP_GETLOCAL, // pushes base[uint8_t] + OP_GETUPVAL, // pushes closure->upvals[uint8_t] + OP_SETUPVAL, // pops and sets closure->upvals[uint8_t] + OP_PEJMP, // pops, if false jumps uint16_t + OP_EJMP, // if peek(0) is falsey jumps uint16_t + OP_JMP, // always jumps uint16_t + OP_JMPBACK, // jumps -uint16_t + OP_POP, // - pops[uint8_t] from stack + OP_CALL, // calls top[-uint8_t] expecting uint8_t results + OP_CLOSURE, OP_CLOSE, OP_NEWTABLE, OP_NEWARRAY, // really just a table @@ -44,10 +45,10 @@ typedef enum { OP_NOT, OP_NEGATE, OP_COUNT, - OP_CONCAT, // concats uint8_t vars on the stack - OP_INCLOCAL, // pushes old value to stack, adds (uint8_t-128) to local[uint8_t] + OP_CONCAT, // concats uint8_t vars on the stack + OP_INCLOCAL, // pushes old value to stack, adds (uint8_t-128) to local[uint8_t] OP_INCGLOBAL, // pushes old value to stack, adds (uint8_t-128) to globals[const[uint16_t]] - OP_INCUPVAL, // pushes old value to stack, adds (uint8_t-128) to closure->upval[uint8_t] + OP_INCUPVAL, // pushes old value to stack, adds (uint8_t-128) to closure->upval[uint8_t] OP_INCINDEX, OP_INCOBJECT, // pushes old value to stack, adds (uint8_t-128) to obj[const[uint16_t]] diff --git a/src/cosmo.h b/src/cosmo.h index 9a9917d..332db81 100644 --- a/src/cosmo.h +++ b/src/cosmo.h @@ -1,21 +1,21 @@ #ifndef COSMOMAIN_H #define COSMOMAIN_H -#include +#include +#include #include #include -#include #include +#include -#include - -/* +/* SAFE_STACK: - if undefined, the stack will not be checked for stack overflows. This may improve performance, however - this will produce undefined behavior as you reach the stack limit (and may cause a seg fault!). It is recommended to keep this enabled. + if undefined, the stack will not be checked for stack overflows. This may improve + performance, however this will produce undefined behavior as you reach the stack limit (and may + cause a seg fault!). It is recommended to keep this enabled. */ #define SAFE_STACK -//#define NAN_BOXXED +// #define NAN_BOXXED // forward declare *most* stuff so our headers are cleaner typedef struct CState CState; @@ -54,7 +54,6 @@ typedef uint8_t INSTRUCTION; #define UNNAMEDCHUNK "_main" #define COSMOASSERT(x) assert(x) -#define CERROR(err) \ - printf("%s : %s\n", "[ERROR]", err) +#define CERROR(err) printf("%s : %s\n", "[ERROR]", err) #endif diff --git a/src/cparse.c b/src/cparse.c index 6809b57..0038034 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -1,42 +1,49 @@ #include "cparse.h" -#include "cstate.h" -#include "clex.h" + #include "cchunk.h" #include "cdebug.h" +#include "clex.h" #include "cmem.h" +#include "cstate.h" #include "cvm.h" -#include #include +#include -// we define all of this here because we only need it in this file, no need for it to be in the header /shrug +// we define all of this here because we only need it in this file, no need for it to be in the +// header /shrug -typedef struct { +typedef struct +{ CToken name; int depth; bool isCaptured; // is the Local referenced in an upvalue? } Local; -typedef struct { +typedef struct +{ uint8_t index; bool isLocal; } Upvalue; -typedef struct { - int *breaks; // this array is dynamically allocated - int scope; // if -1, there is no loop +typedef struct +{ + int *breaks; // this array is dynamically allocated + int scope; // if -1, there is no loop int startBytecode; // start index in the chunk of the loop - int breakCount; // # of breaks to patch + int breakCount; // # of breaks to patch int breakCapacity; } LoopState; -typedef enum { +typedef enum +{ FTYPE_FUNCTION, FTYPE_METHOD, FTYPE_SCRIPT } FunctionType; -typedef struct CCompilerState { +typedef struct CCompilerState +{ Local locals[256]; Upvalue upvalues[256]; LoopState loop; @@ -46,11 +53,12 @@ typedef struct CCompilerState { int localCount; int scopeDepth; int pushedValues; - int expectedValues; - struct CCompilerState* enclosing; + int expectedValues; + struct CCompilerState *enclosing; } CCompilerState; -typedef struct { +typedef struct +{ CLexState *lex; CCompilerState *compiler; CObjString *module; // name of the module @@ -61,31 +69,33 @@ typedef struct { bool panic; } CParseState; -typedef enum { +typedef enum +{ PREC_NONE, - PREC_ASSIGNMENT, // = - PREC_CONCAT, // .. - PREC_OR, // or - PREC_AND, // and - PREC_EQUALITY, // == != - PREC_COMPARISON, // < > <= >= - PREC_TERM, // + - - PREC_FACTOR, // * / - PREC_UNARY, // ! - - PREC_CALL, // . () - PREC_PRIMARY // everything else + PREC_ASSIGNMENT, // = + PREC_CONCAT, // .. + PREC_OR, // or + PREC_AND, // and + PREC_EQUALITY, // == != + PREC_COMPARISON, // < > <= >= + PREC_TERM, // + - + PREC_FACTOR, // * / + PREC_UNARY, // ! - + PREC_CALL, // . () + PREC_PRIMARY // everything else } Precedence; -typedef void (*ParseFunc)(CParseState* pstate, bool canAssign, Precedence curPrec); +typedef void (*ParseFunc)(CParseState *pstate, bool canAssign, Precedence curPrec); -typedef struct { +typedef struct +{ ParseFunc prefix; ParseFunc infix; Precedence level; } ParseRule; // returns true if first prefix was ran -static bool parsePrecedence(CParseState*, Precedence); +static bool parsePrecedence(CParseState *, Precedence); // returns # of pushed values onto the stack static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded); // returns # of pushed values onto the stack @@ -94,12 +104,15 @@ static void statement(CParseState *pstate); static void declaration(CParseState *pstate); static void function(CParseState *pstate, FunctionType type); static void expressionStatement(CParseState *pstate); -static ParseRule* getRule(CTokenType type); +static ParseRule *getRule(CTokenType type); static CObjFunction *endCompiler(CParseState *pstate); -// ================================================================ [FRONT END/TALK TO LEXER] ================================================================ +// ================================================================ [FRONT END/TALK TO LEXER] +// ================================================================ -static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, FunctionType type, CCompilerState *enclosing) { +static void initCompilerState(CParseState *pstate, CCompilerState *ccstate, FunctionType type, + CCompilerState *enclosing) +{ pstate->compiler = ccstate; ccstate->enclosing = enclosing; @@ -114,12 +127,16 @@ static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, Func ccstate->loop.scope = -1; // there is no loop yet - if (type != FTYPE_SCRIPT) - ccstate->function->name = cosmoO_copyString(pstate->state, pstate->previous.start, pstate->previous.length); - else - ccstate->function->name = cosmoO_copyString(pstate->state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); + if (type != FTYPE_SCRIPT) { + ccstate->function->name = + cosmoO_copyString(pstate->state, pstate->previous.start, pstate->previous.length); + } else { + ccstate->function->name = + cosmoO_copyString(pstate->state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); + } - // mark first local slot as used (this will hold the CObjFunction of the current function, or if it's a method it'll hold the currently bounded object) + // mark first local slot as used (this will hold the CObjFunction of the current function, or if + // it's a method it'll hold the currently bounded object) Local *local = &ccstate->locals[ccstate->localCount++]; local->depth = 0; local->isCaptured = false; @@ -127,7 +144,9 @@ static void initCompilerState(CParseState* pstate, CCompilerState *ccstate, Func local->name.length = 0; } -static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s, const char *source, const char *module) { +static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState *s, + const char *source, const char *module) +{ pstate->lex = cosmoL_newLexState(s, source); pstate->state = s; @@ -135,15 +154,17 @@ static void initParseState(CParseState *pstate, CCompilerState *ccstate, CState pstate->panic = false; pstate->compiler = ccstate; pstate->module = cosmoO_copyString(s, module, strlen(module)); - + initCompilerState(pstate, ccstate, FTYPE_SCRIPT, NULL); // enclosing starts as NULL } -static void freeParseState(CParseState *pstate) { +static void freeParseState(CParseState *pstate) +{ cosmoL_freeLexState(pstate->state, pstate->lex); } -static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args) { +static void errorAt(CParseState *pstate, CToken *token, const char *format, va_list args) +{ if (pstate->hadError) return; @@ -167,21 +188,24 @@ static void errorAt(CParseState *pstate, CToken *token, const char *format, va_l pstate->panic = true; } -static void errorAtCurrent(CParseState *pstate, const char *format, ...) { +static void errorAtCurrent(CParseState *pstate, const char *format, ...) +{ va_list args; va_start(args, format); errorAt(pstate, &pstate->current, format, args); va_end(args); } -static void error(CParseState *pstate, const char *format, ...) { +static void error(CParseState *pstate, const char *format, ...) +{ va_list args; va_start(args, format); errorAt(pstate, &pstate->previous, format, args); va_end(args); } -static void advance(CParseState *pstate) { +static void advance(CParseState *pstate) +{ pstate->previous = pstate->current; pstate->current = cosmoL_scanToken(pstate->lex); @@ -190,12 +214,14 @@ static void advance(CParseState *pstate) { } } -static bool check(CParseState *pstate, CTokenType type) { +static bool check(CParseState *pstate, CTokenType type) +{ return pstate->current.type == type; } // consumes the next token if it matches type, otherwise errors -static void consume(CParseState* pstate, CTokenType type, const char *msg) { +static void consume(CParseState *pstate, CTokenType type, const char *msg) +{ if (pstate->current.type == type) { // if token matches, consume the next token advance(pstate); return; @@ -204,7 +230,8 @@ static void consume(CParseState* pstate, CTokenType type, const char *msg) { errorAtCurrent(pstate, msg); } -static bool match(CParseState *pstate, CTokenType type) { +static bool match(CParseState *pstate, CTokenType type) +{ if (!check(pstate, type)) return false; @@ -213,36 +240,45 @@ static bool match(CParseState *pstate, CTokenType type) { return true; } -static bool identifiersEqual(CToken *idA, CToken *idB) { +static bool identifiersEqual(CToken *idA, CToken *idB) +{ return idA->length == idB->length && memcmp(idA->start, idB->start, idA->length) == 0; } -static void inline valuePushed(CParseState *pstate, int values) { +static void inline valuePushed(CParseState *pstate, int values) +{ pstate->compiler->pushedValues += values; } -static void inline valuePopped(CParseState *pstate, int values) { +static void inline valuePopped(CParseState *pstate, int values) +{ pstate->compiler->pushedValues -= values; } -static bool blockFollow(CToken token) { +static bool blockFollow(CToken token) +{ switch (token.type) { - case TOKEN_END: case TOKEN_ELSE: - case TOKEN_ELSEIF: case TOKEN_EOS: - return true; - default: - return false; + case TOKEN_END: + case TOKEN_ELSE: + case TOKEN_ELSEIF: + case TOKEN_EOS: + return true; + default: + return false; } } -// ================================================================ [WRITE TO CHUNK] ================================================================ +// ================================================================ [WRITE TO CHUNK] +// ================================================================ -CChunk* getChunk(CParseState *pstate) { +CChunk *getChunk(CParseState *pstate) +{ return &pstate->compiler->function->chunk; } // safely adds constant to chunk, checking for overflow -uint16_t makeConstant(CParseState *pstate, CValue val) { +uint16_t makeConstant(CParseState *pstate, CValue val) +{ int indx = addConstant(pstate->state, getChunk(pstate), val); if (indx > UINT16_MAX) { error(pstate, "UInt overflow! Too many constants in one chunk!"); @@ -252,34 +288,40 @@ uint16_t makeConstant(CParseState *pstate, CValue val) { return (uint16_t)indx; } -void writeu8(CParseState *pstate, INSTRUCTION i) { +void writeu8(CParseState *pstate, INSTRUCTION i) +{ writeu8Chunk(pstate->state, getChunk(pstate), i, pstate->previous.line); } -void writeu16(CParseState *pstate, uint16_t i) { +void writeu16(CParseState *pstate, uint16_t i) +{ writeu16Chunk(pstate->state, getChunk(pstate), i, pstate->previous.line); } -void writeConstant(CParseState *pstate, CValue val) { +void writeConstant(CParseState *pstate, CValue val) +{ writeu8(pstate, OP_LOADCONST); writeu16(pstate, makeConstant(pstate, val)); valuePushed(pstate, 1); } -int writeJmp(CParseState *pstate, INSTRUCTION i) { +int writeJmp(CParseState *pstate, INSTRUCTION i) +{ writeu8(pstate, i); writeu16(pstate, 0xFFFF); return getChunk(pstate)->count - 2; } -void writePop(CParseState *pstate, int times) { +void writePop(CParseState *pstate, int times) +{ writeu8(pstate, OP_POP); writeu8(pstate, times); } -void writeJmpBack(CParseState *pstate, int location) { +void writeJmpBack(CParseState *pstate, int location) +{ int jmp = (getChunk(pstate)->count - location) + 3; if (jmp > UINT16_MAX) @@ -290,7 +332,8 @@ void writeJmpBack(CParseState *pstate, int location) { } // patches offset operand at location -void patchJmp(CParseState *pstate, int index) { +void patchJmp(CParseState *pstate, int index) +{ unsigned int jump = getChunk(pstate)->count - index - 2; if (jump > UINT16_MAX) @@ -299,11 +342,14 @@ void patchJmp(CParseState *pstate, int index) { memcpy(&getChunk(pstate)->buf[index], &jump, sizeof(uint16_t)); } -static uint16_t identifierConstant(CParseState *pstate, CToken *name) { - return makeConstant(pstate, cosmoV_newRef((CObj*)cosmoO_copyString(pstate->state, name->start, name->length))); +static uint16_t identifierConstant(CParseState *pstate, CToken *name) +{ + return makeConstant( + pstate, cosmoV_newRef((CObj *)cosmoO_copyString(pstate->state, name->start, name->length))); } -static void addLocal(CParseState *pstate, CToken name) { +static void addLocal(CParseState *pstate, CToken name) +{ if (pstate->compiler->localCount > UINT8_MAX) { error(pstate, "UInt overflow! Too many locals in scope!"); return; @@ -315,7 +361,8 @@ static void addLocal(CParseState *pstate, CToken name) { local->isCaptured = false; } -static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx, bool isLocal) { +static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx, bool isLocal) +{ int upvals = ccstate->function->upvals; if (upvals > UINT8_MAX) { @@ -335,10 +382,13 @@ static int addUpvalue(CParseState *pstate, CCompilerState *ccstate, uint8_t indx return ccstate->function->upvals++; } -static int getLocal(CCompilerState *ccstate, CToken *name) { +static int getLocal(CCompilerState *ccstate, CToken *name) +{ for (int i = ccstate->localCount - 1; i >= 0; i--) { Local *local = &ccstate->locals[i]; - if (local->depth != -1 && identifiersEqual(name, &local->name)) { // if the identifer is initalized and it matches, use it! + if (local->depth != -1 && + identifiersEqual( + name, &local->name)) { // if the identifer is initalized and it matches, use it! return i; } } @@ -347,7 +397,8 @@ static int getLocal(CCompilerState *ccstate, CToken *name) { return -1; } -static int getUpvalue(CParseState *pstate, CCompilerState *ccstate, CToken *name) { +static int getUpvalue(CParseState *pstate, CCompilerState *ccstate, CToken *name) +{ if (ccstate->enclosing == NULL) // there's no upvalues to lookup! return -1; @@ -364,11 +415,13 @@ static int getUpvalue(CParseState *pstate, CCompilerState *ccstate, CToken *name return -1; // failed! } -static void markInitialized(CParseState *pstate, int local) { +static void markInitialized(CParseState *pstate, int local) +{ pstate->compiler->locals[local].depth = pstate->compiler->scopeDepth; } -static int parseArguments(CParseState *pstate) { +static int parseArguments(CParseState *pstate) +{ int args = 0; // there are args to parse! @@ -376,7 +429,7 @@ static int parseArguments(CParseState *pstate) { do { expression(pstate, 1, true); args++; - } while(match(pstate, TOKEN_COMMA)); + } while (match(pstate, TOKEN_COMMA)); } consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' to end call."); @@ -388,7 +441,8 @@ static int parseArguments(CParseState *pstate) { } // recovers stack (pops unneeded values, reports missing values) -static void alignStack(CParseState *pstate, int alignment) { +static void alignStack(CParseState *pstate, int alignment) +{ // realign the stack if (pstate->compiler->pushedValues > alignment) { writePop(pstate, pstate->compiler->pushedValues - alignment); @@ -400,96 +454,147 @@ static void alignStack(CParseState *pstate, int alignment) { } // last in precedence expression? -static bool isLast(CParseState *pstate, Precedence pType) { +static bool isLast(CParseState *pstate, Precedence pType) +{ return pType > getRule(pstate->current.type)->level; } -// ================================================================ [PARSER] ================================================================ +// ================================================================ [PARSER] +// ================================================================ -static void number(CParseState *pstate, bool canAssign, Precedence prec) { +static void number(CParseState *pstate, bool canAssign, Precedence prec) +{ cosmo_Number num = strtod(pstate->previous.start, NULL); writeConstant(pstate, cosmoV_newNumber(num)); } -static void hexnumber(CParseState *pstate, bool canAssign, Precedence prec) { +static void hexnumber(CParseState *pstate, bool canAssign, Precedence prec) +{ cosmo_Number num = strtol(pstate->previous.start + 2, NULL, 16); // +2 to skip the '0x' writeConstant(pstate, cosmoV_newNumber(num)); } -static void binnumber(CParseState *pstate, bool canAssign, Precedence prec) { +static void binnumber(CParseState *pstate, bool canAssign, Precedence prec) +{ cosmo_Number num = strtol(pstate->previous.start + 2, NULL, 2); // +2 to skip the '0b' writeConstant(pstate, cosmoV_newNumber(num)); } -static void string(CParseState *pstate, bool canAssign, Precedence prec) { - CObjString *strObj = cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length); - writeConstant(pstate, cosmoV_newRef((CObj*)strObj)); +static void string(CParseState *pstate, bool canAssign, Precedence prec) +{ + CObjString *strObj = + cosmoO_takeString(pstate->state, pstate->previous.start, pstate->previous.length); + writeConstant(pstate, cosmoV_newRef((CObj *)strObj)); } -static void literal(CParseState *pstate, bool canAssign, Precedence prec) { +static void literal(CParseState *pstate, bool canAssign, Precedence prec) +{ switch (pstate->previous.type) { - case TOKEN_TRUE: writeu8(pstate, OP_TRUE); break; - case TOKEN_FALSE: writeu8(pstate, OP_FALSE); break; - case TOKEN_NIL: writeu8(pstate, OP_NIL); break; - default: - break; + case TOKEN_TRUE: + writeu8(pstate, OP_TRUE); + break; + case TOKEN_FALSE: + writeu8(pstate, OP_FALSE); + break; + case TOKEN_NIL: + writeu8(pstate, OP_NIL); + break; + default: + break; } valuePushed(pstate, 1); } // parses prefix operators -static void unary(CParseState *pstate, bool canAssign, Precedence prec) { +static void unary(CParseState *pstate, bool canAssign, Precedence prec) +{ CTokenType type = pstate->previous.type; - int cachedLine = pstate->previous.line; // eval'ing the next expression might change the line number + int cachedLine = + pstate->previous.line; // eval'ing the next expression might change the line number // only eval the next *value* expressionPrecedence(pstate, 1, PREC_UNARY, true); - switch(type) { - case TOKEN_MINUS: writeu8Chunk(pstate->state, getChunk(pstate), OP_NEGATE, cachedLine); break; - case TOKEN_BANG: writeu8Chunk(pstate->state, getChunk(pstate), OP_NOT, cachedLine); break; - case TOKEN_POUND: writeu8Chunk(pstate->state, getChunk(pstate), OP_COUNT, cachedLine); break; - default: - error(pstate, "Unexpected unary operator!"); + switch (type) { + case TOKEN_MINUS: + writeu8Chunk(pstate->state, getChunk(pstate), OP_NEGATE, cachedLine); + break; + case TOKEN_BANG: + writeu8Chunk(pstate->state, getChunk(pstate), OP_NOT, cachedLine); + break; + case TOKEN_POUND: + writeu8Chunk(pstate->state, getChunk(pstate), OP_COUNT, cachedLine); + break; + default: + error(pstate, "Unexpected unary operator!"); } } // parses infix operators -static void binary(CParseState *pstate, bool canAssign, Precedence prec) { +static void binary(CParseState *pstate, bool canAssign, Precedence prec) +{ CTokenType type = pstate->previous.type; // already consumed - int cachedLine = pstate->previous.line; // eval'ing the next expression might change the line number + int cachedLine = + pstate->previous.line; // eval'ing the next expression might change the line number expressionPrecedence(pstate, 1, getRule(type)->level + 1, true); switch (type) { - // ARITH - case TOKEN_PLUS: writeu8Chunk(pstate->state, getChunk(pstate), OP_ADD, cachedLine); break; - case TOKEN_MINUS: writeu8Chunk(pstate->state, getChunk(pstate), OP_SUB, cachedLine); break; - case TOKEN_STAR: writeu8Chunk(pstate->state, getChunk(pstate), OP_MULT, cachedLine); break; - case TOKEN_SLASH: writeu8Chunk(pstate->state, getChunk(pstate), OP_DIV, cachedLine); break; - case TOKEN_PERCENT: writeu8Chunk(pstate->state, getChunk(pstate), OP_MOD, cachedLine); break; - case TOKEN_CARROT: writeu8Chunk(pstate->state, getChunk(pstate), OP_POW, cachedLine); break; - // EQUALITY - case TOKEN_EQUAL_EQUAL: writeu8Chunk(pstate->state, getChunk(pstate), OP_EQUAL, cachedLine); break; - case TOKEN_GREATER: writeu8Chunk(pstate->state, getChunk(pstate), OP_GREATER, cachedLine); break; - case TOKEN_LESS: writeu8Chunk(pstate->state, getChunk(pstate), OP_LESS, cachedLine); break; - case TOKEN_GREATER_EQUAL: writeu8Chunk(pstate->state, getChunk(pstate), OP_GREATER_EQUAL, cachedLine); break; - case TOKEN_LESS_EQUAL: writeu8Chunk(pstate->state, getChunk(pstate), OP_LESS_EQUAL, cachedLine); break; - case TOKEN_BANG_EQUAL: writeu8Chunk(pstate->state, getChunk(pstate), OP_EQUAL, cachedLine); writeu8Chunk(pstate->state, getChunk(pstate), OP_NOT, cachedLine); break; - default: - error(pstate, "Unexpected operator!"); + // ARITH + case TOKEN_PLUS: + writeu8Chunk(pstate->state, getChunk(pstate), OP_ADD, cachedLine); + break; + case TOKEN_MINUS: + writeu8Chunk(pstate->state, getChunk(pstate), OP_SUB, cachedLine); + break; + case TOKEN_STAR: + writeu8Chunk(pstate->state, getChunk(pstate), OP_MULT, cachedLine); + break; + case TOKEN_SLASH: + writeu8Chunk(pstate->state, getChunk(pstate), OP_DIV, cachedLine); + break; + case TOKEN_PERCENT: + writeu8Chunk(pstate->state, getChunk(pstate), OP_MOD, cachedLine); + break; + case TOKEN_CARROT: + writeu8Chunk(pstate->state, getChunk(pstate), OP_POW, cachedLine); + break; + // EQUALITY + case TOKEN_EQUAL_EQUAL: + writeu8Chunk(pstate->state, getChunk(pstate), OP_EQUAL, cachedLine); + break; + case TOKEN_GREATER: + writeu8Chunk(pstate->state, getChunk(pstate), OP_GREATER, cachedLine); + break; + case TOKEN_LESS: + writeu8Chunk(pstate->state, getChunk(pstate), OP_LESS, cachedLine); + break; + case TOKEN_GREATER_EQUAL: + writeu8Chunk(pstate->state, getChunk(pstate), OP_GREATER_EQUAL, cachedLine); + break; + case TOKEN_LESS_EQUAL: + writeu8Chunk(pstate->state, getChunk(pstate), OP_LESS_EQUAL, cachedLine); + break; + case TOKEN_BANG_EQUAL: + writeu8Chunk(pstate->state, getChunk(pstate), OP_EQUAL, cachedLine); + writeu8Chunk(pstate->state, getChunk(pstate), OP_NOT, cachedLine); + break; + default: + error(pstate, "Unexpected operator!"); } valuePopped(pstate, 1); // we pop 2 values off the stack and push 1 for a net pop of 1 value } -static void group(CParseState *pstate, bool canAssign, Precedence prec) { +static void group(CParseState *pstate, bool canAssign, Precedence prec) +{ expression(pstate, 1, true); consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')'"); } -static void _etterOP(CParseState *pstate, uint8_t op, int arg) { +static void _etterOP(CParseState *pstate, uint8_t op, int arg) +{ writeu8(pstate, op); if (op == OP_GETGLOBAL || op == OP_SETGLOBAL) // globals are stored with a u16 writeu16(pstate, arg); @@ -497,7 +602,9 @@ static void _etterOP(CParseState *pstate, uint8_t op, int arg) { writeu8(pstate, arg); } -static void namedVariable(CParseState *pstate, CToken name, bool canAssign, bool canIncrement, int expectedValues) { +static void namedVariable(CParseState *pstate, CToken name, bool canAssign, bool canIncrement, + int expectedValues) +{ uint8_t opGet, opSet, inc; int arg = getLocal(pstate->compiler, &name); @@ -552,8 +659,8 @@ static void namedVariable(CParseState *pstate, CToken name, bool canAssign, bool } else if (canIncrement && match(pstate, TOKEN_PLUS_PLUS)) { // i++ // now we increment the value writeu8(pstate, inc); - writeu8(pstate, 128 + 1); // setting signed values in an unsigned int - if (inc == OP_INCGLOBAL) // globals are stored with a u16 + writeu8(pstate, 128 + 1); // setting signed values in an unsigned int + if (inc == OP_INCGLOBAL) // globals are stored with a u16 writeu16(pstate, arg); else writeu8(pstate, arg); @@ -561,20 +668,21 @@ static void namedVariable(CParseState *pstate, CToken name, bool canAssign, bool } else if (canIncrement && match(pstate, TOKEN_MINUS_MINUS)) { // i-- // now we increment the value writeu8(pstate, inc); - writeu8(pstate, 128 - 1); // setting signed values in an unsigned int - if (inc == OP_INCGLOBAL) // globals are stored with a u16 + writeu8(pstate, 128 - 1); // setting signed values in an unsigned int + if (inc == OP_INCGLOBAL) // globals are stored with a u16 writeu16(pstate, arg); else writeu8(pstate, arg); valuePushed(pstate, 1); - } else { + } else { // getter _etterOP(pstate, opGet, arg); valuePushed(pstate, 1); } } -static void and_(CParseState *pstate, bool canAssign, Precedence prec) { +static void and_(CParseState *pstate, bool canAssign, Precedence prec) +{ int jump = writeJmp(pstate, OP_EJMP); // conditional jump without popping writePop(pstate, 1); @@ -584,7 +692,8 @@ static void and_(CParseState *pstate, bool canAssign, Precedence prec) { patchJmp(pstate, jump); } -static void or_(CParseState *pstate, bool canAssign, Precedence prec) { +static void or_(CParseState *pstate, bool canAssign, Precedence prec) +{ int elseJump = writeJmp(pstate, OP_EJMP); int endJump = writeJmp(pstate, OP_JMP); @@ -593,19 +702,22 @@ static void or_(CParseState *pstate, bool canAssign, Precedence prec) { valuePopped(pstate, 1); expressionPrecedence(pstate, 1, PREC_OR, true); - + patchJmp(pstate, endJump); } -static void anonFunction(CParseState *pstate, bool canAssign, Precedence prec) { +static void anonFunction(CParseState *pstate, bool canAssign, Precedence prec) +{ function(pstate, FTYPE_FUNCTION); } -static void variable(CParseState *pstate, bool canAssign, Precedence prec) { +static void variable(CParseState *pstate, bool canAssign, Precedence prec) +{ namedVariable(pstate, pstate->previous, canAssign, true, 0); } -static void concat(CParseState *pstate, bool canAssign, Precedence prec) { +static void concat(CParseState *pstate, bool canAssign, Precedence prec) +{ CTokenType type = pstate->previous.type; int vars = 1; // we already have something on the stack @@ -620,25 +732,29 @@ static void concat(CParseState *pstate, bool canAssign, Precedence prec) { valuePopped(pstate, vars - 1); // - 1 because we're pushing the concat result } -static void call_(CParseState *pstate, bool canAssign, Precedence prec) { +static void call_(CParseState *pstate, bool canAssign, Precedence prec) +{ // we enter having already consumed the '(' int returnNum = pstate->compiler->expectedValues; // grab our arguments uint8_t argCount = parseArguments(pstate); - valuePopped(pstate, argCount + 1); // all of these values will be popped off the stack when returned (+1 for the function) + valuePopped(pstate, argCount + 1); // all of these values will be popped off the stack when + // returned (+1 for the function) writeu8(pstate, OP_CALL); writeu8(pstate, argCount); - // if we're not the last token in this expression or we're expecting multiple values, we should return only 1 value!! + // if we're not the last token in this expression or we're expecting multiple values, we should + // return only 1 value!! if (!isLast(pstate, prec) || (returnNum > 1 && check(pstate, TOKEN_COMMA))) returnNum = 1; - + writeu8(pstate, returnNum); valuePushed(pstate, returnNum); } -static void table(CParseState *pstate, bool canAssign, Precedence prec) { +static void table(CParseState *pstate, bool canAssign, Precedence prec) +{ // enter having already consumed '[' int entries = 0; int tblType = 0; // 0 = we don't know yet / 1 = array-like table / 2 = dictionary-like table @@ -654,7 +770,8 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec) { // grab value expression(pstate, 1, true); - } else if ((check(pstate, TOKEN_COMMA) || check(pstate, TOKEN_RIGHT_BRACKET)) && tblType != 2) { + } else if ((check(pstate, TOKEN_COMMA) || check(pstate, TOKEN_RIGHT_BRACKET)) && + tblType != 2) { tblType = 1; // array-like } else { error(pstate, "Can't change table description type mid-definition!"); @@ -668,26 +785,27 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec) { } switch (tblType) { - case 1: // array-like - writeu8(pstate, OP_NEWARRAY); - writeu16(pstate, entries); - valuePopped(pstate, entries); - break; - case 2: // dictionary-like - writeu8(pstate, OP_NEWTABLE); - writeu16(pstate, entries); - valuePopped(pstate, entries * 2); - break; - default: // just make an empty table - writeu8(pstate, OP_NEWTABLE); - writeu16(pstate, 0); - break; + case 1: // array-like + writeu8(pstate, OP_NEWARRAY); + writeu16(pstate, entries); + valuePopped(pstate, entries); + break; + case 2: // dictionary-like + writeu8(pstate, OP_NEWTABLE); + writeu16(pstate, entries); + valuePopped(pstate, entries * 2); + break; + default: // just make an empty table + writeu8(pstate, OP_NEWTABLE); + writeu16(pstate, 0); + break; } valuePushed(pstate, 1); // table is now on the stack } -static void object(CParseState *pstate, bool canAssign, Precedence prec) { +static void object(CParseState *pstate, bool canAssign, Precedence prec) +{ // already consumed the beginning '{' int entries = 0; @@ -703,7 +821,7 @@ static void object(CParseState *pstate, bool canAssign, Precedence prec) { // now, parse the value (until comma) expression(pstate, 1, true); - + // "pop" the 1 value valuePopped(pstate, 1); entries++; @@ -717,7 +835,8 @@ static void object(CParseState *pstate, bool canAssign, Precedence prec) { valuePushed(pstate, 1); } -static void dot(CParseState *pstate, bool canAssign, Precedence prec) { +static void dot(CParseState *pstate, bool canAssign, Precedence prec) +{ consume(pstate, TOKEN_IDENTIFIER, "Expected property name after '.'."); uint16_t name = identifierConstant(pstate, &pstate->previous); @@ -726,7 +845,7 @@ static void dot(CParseState *pstate, bool canAssign, Precedence prec) { writeu8(pstate, OP_SETOBJECT); writeu16(pstate, name); - valuePopped(pstate, 2); // value & object + valuePopped(pstate, 2); // value & object } else if (match(pstate, TOKEN_PLUS_PLUS)) { // increment the field writeu8(pstate, OP_INCOBJECT); writeu8(pstate, 128 + 1); @@ -742,14 +861,15 @@ static void dot(CParseState *pstate, bool canAssign, Precedence prec) { } } -static void _index(CParseState *pstate, bool canAssign, Precedence prec) { +static void _index(CParseState *pstate, bool canAssign, Precedence prec) +{ expression(pstate, 1, true); consume(pstate, TOKEN_RIGHT_BRACKET, "Expected ']' to end index."); if (canAssign && match(pstate, TOKEN_EQUAL)) { expression(pstate, 1, true); writeu8(pstate, OP_NEWINDEX); - valuePopped(pstate, 2); // pops key, value & object + valuePopped(pstate, 2); // pops key, value & object } else if (match(pstate, TOKEN_PLUS_PLUS)) { // increment the field writeu8(pstate, OP_INCINDEX); writeu8(pstate, 128 + 1); @@ -759,11 +879,12 @@ static void _index(CParseState *pstate, bool canAssign, Precedence prec) { } else { writeu8(pstate, OP_INDEX); } - + valuePopped(pstate, 1); // pops key & object but also pushes the value so total popped is 1 } -static void invoke(CParseState *pstate, bool canAssign, Precedence prec) { +static void invoke(CParseState *pstate, bool canAssign, Precedence prec) +{ uint16_t name; consume(pstate, TOKEN_IDENTIFIER, "Expected method name after ':'."); @@ -773,16 +894,17 @@ static void invoke(CParseState *pstate, bool canAssign, Precedence prec) { int returnNum = pstate->compiler->expectedValues; uint8_t args = parseArguments(pstate); - // if we're not the last token in this expression or we're expecting multiple values, we should return only 1 value!! + // if we're not the last token in this expression or we're expecting multiple values, we + // should return only 1 value!! if (!isLast(pstate, prec) || (returnNum > 1 && check(pstate, TOKEN_COMMA))) returnNum = 1; - + writeu8(pstate, OP_INVOKE); writeu8(pstate, args); - writeu8(pstate, returnNum); + writeu8(pstate, returnNum); writeu16(pstate, name); - valuePopped(pstate, args+1); // args + function + valuePopped(pstate, args + 1); // args + function valuePushed(pstate, returnNum); } else { // just get the method writeu8(pstate, OP_GETMETHOD); @@ -792,7 +914,8 @@ static void invoke(CParseState *pstate, bool canAssign, Precedence prec) { // ++test.field[1] // this function is kind of spaghetti, feel free to rewrite (if you dare!) -static void walkIndexes(CParseState *pstate, int lastIndexType, uint16_t lastIdent, int val) { +static void walkIndexes(CParseState *pstate, int lastIndexType, uint16_t lastIdent, int val) +{ uint16_t ident = lastIdent; int indexType = lastIndexType; @@ -805,18 +928,18 @@ static void walkIndexes(CParseState *pstate, int lastIndexType, uint16_t lastIde indexType = 1; } else // end of indexes, break out of the loop break; - + switch (lastIndexType) { - case 0: // . - writeu8(pstate, OP_GETOBJECT); // grabs property - writeu16(pstate, lastIdent); - break; - case 1: // [] - writeu8(pstate, OP_INDEX); // so, that was a normal index, perform that - valuePopped(pstate, 1); // pops the key & table off the stack, but pushes the value - break; - default: // no previous index - break; + case 0: // . + writeu8(pstate, OP_GETOBJECT); // grabs property + writeu16(pstate, lastIdent); + break; + case 1: // [] + writeu8(pstate, OP_INDEX); // so, that was a normal index, perform that + valuePopped(pstate, 1); // pops the key & table off the stack, but pushes the value + break; + default: // no previous index + break; } if (indexType == 1) { // currently parsed token was a TOKEN_LEFT_BRACKET, meaning an index @@ -829,32 +952,34 @@ static void walkIndexes(CParseState *pstate, int lastIndexType, uint16_t lastIde } switch (indexType) { - case 0: // . - writeu8(pstate, OP_INCOBJECT); - writeu8(pstate, 128 + val); // setting signed values in an unsigned int - writeu16(pstate, ident); - valuePopped(pstate, 1); // popped the object off the stack - break; - case 1: // [] - writeu8(pstate, OP_INCINDEX); - writeu8(pstate, 128 + val); - valuePopped(pstate, 2); // popped the table & the key off the stack, but pushes the previous value - break; - default: // no previous index - break; + case 0: // . + writeu8(pstate, OP_INCOBJECT); + writeu8(pstate, 128 + val); // setting signed values in an unsigned int + writeu16(pstate, ident); + valuePopped(pstate, 1); // popped the object off the stack + break; + case 1: // [] + writeu8(pstate, OP_INCINDEX); + writeu8(pstate, 128 + val); + valuePopped(pstate, + 2); // popped the table & the key off the stack, but pushes the previous value + break; + default: // no previous index + break; } } -static void increment(CParseState *pstate, int val) { +static void increment(CParseState *pstate, int val) +{ CToken name = pstate->previous; - if (match(pstate, TOKEN_DOT)) { // object? + if (match(pstate, TOKEN_DOT)) { // object? namedVariable(pstate, name, false, false, 0); // just get the object consume(pstate, TOKEN_IDENTIFIER, "Expected property name after '.'."); uint16_t ident = identifierConstant(pstate, &pstate->previous); // walk the indexes walkIndexes(pstate, 0, ident, val); - } else if (match(pstate, TOKEN_LEFT_BRACKET)) { // table? + } else if (match(pstate, TOKEN_LEFT_BRACKET)) { // table? namedVariable(pstate, name, false, false, 0); // just get the table // grab key @@ -879,8 +1004,8 @@ static void increment(CParseState *pstate, int val) { } writeu8(pstate, op); - writeu8(pstate, 128 + val); // setting signed values in an unsigned int - if (op == OP_INCGLOBAL) // globals are stored with a u16 + writeu8(pstate, 128 + val); // setting signed values in an unsigned int + if (op == OP_INCGLOBAL) // globals are stored with a u16 writeu16(pstate, arg); else writeu8(pstate, arg); @@ -892,7 +1017,8 @@ static void increment(CParseState *pstate, int val) { } // ++i -static void preincrement(CParseState *pstate, bool canAssign, Precedence prec) { +static void preincrement(CParseState *pstate, bool canAssign, Precedence prec) +{ // expect identifier consume(pstate, TOKEN_IDENTIFIER, "Expected identifier after '++'"); @@ -900,13 +1026,16 @@ static void preincrement(CParseState *pstate, bool canAssign, Precedence prec) { } // --i -static void predecrement(CParseState *pstate, bool canAssign, Precedence prec) { +static void predecrement(CParseState *pstate, bool canAssign, Precedence prec) +{ // expect identifier consume(pstate, TOKEN_IDENTIFIER, "Expected identifier after '--'"); increment(pstate, -1); } +// clang-format off + ParseRule ruleTable[] = { [TOKEN_LEFT_PAREN] = {group, call_, PREC_CALL}, [TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE}, @@ -968,12 +1097,16 @@ ParseRule ruleTable[] = { [TOKEN_EOF] = {NULL, NULL, PREC_NONE} }; -static ParseRule* getRule(CTokenType type) { +// clang-format on + +static ParseRule *getRule(CTokenType type) +{ return &ruleTable[type]; } // returns true if it got past the first token (aka prefix wasn't null) -static bool parsePrecedence(CParseState *pstate, Precedence prec) { +static bool parsePrecedence(CParseState *pstate, Precedence prec) +{ advance(pstate); ParseFunc prefix = getRule(pstate->previous.type)->prefix; @@ -997,11 +1130,12 @@ static bool parsePrecedence(CParseState *pstate, Precedence prec) { return true; } -static void declareLocal(CParseState *pstate, bool forceLocal) { +static void declareLocal(CParseState *pstate, bool forceLocal) +{ if (pstate->compiler->scopeDepth == 0 && !forceLocal) return; - CToken* name = &pstate->previous; + CToken *name = &pstate->previous; // check if we already have a local with that identifier for (int i = 0; i < pstate->compiler->localCount; i++) { @@ -1018,7 +1152,8 @@ static void declareLocal(CParseState *pstate, bool forceLocal) { addLocal(pstate, *name); } -static uint16_t parseVariable(CParseState *pstate, const char* errorMessage, bool forceLocal) { +static uint16_t parseVariable(CParseState *pstate, const char *errorMessage, bool forceLocal) +{ consume(pstate, TOKEN_IDENTIFIER, errorMessage); declareLocal(pstate, forceLocal); @@ -1028,7 +1163,8 @@ static uint16_t parseVariable(CParseState *pstate, const char* errorMessage, boo return identifierConstant(pstate, &pstate->previous); } -static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal) { +static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal) +{ if (pstate->hadError) return; @@ -1037,16 +1173,17 @@ static void defineVariable(CParseState *pstate, uint16_t global, bool forceLocal valuePopped(pstate, 1); // the local stays on the stack! return; } - + writeu8(pstate, OP_SETGLOBAL); writeu16(pstate, global); valuePopped(pstate, 1); } -static void _proto(CParseState *pstate) { +static void _proto(CParseState *pstate) +{ int entries = 0; - + while (!match(pstate, TOKEN_END) && !match(pstate, TOKEN_EOF) && !pstate->hadError) { if (match(pstate, TOKEN_FUNCTION)) { // define method @@ -1071,7 +1208,8 @@ static void _proto(CParseState *pstate) { valuePushed(pstate, 1); } -static void protoDeclaration(CParseState *pstate) { +static void protoDeclaration(CParseState *pstate) +{ uint16_t var = parseVariable(pstate, "Expected identifer for proto!", false); // parse proto definiton @@ -1080,7 +1218,8 @@ static void protoDeclaration(CParseState *pstate) { defineVariable(pstate, var, false); } -static void localProto(CParseState *pstate) { +static void localProto(CParseState *pstate) +{ // parses the variable, forcefully marking it as a local uint16_t var = parseVariable(pstate, "Expected identifer for proto!", true); @@ -1090,18 +1229,21 @@ static void localProto(CParseState *pstate) { defineVariable(pstate, var, true); } -static void popLocals(CParseState *pstate, int toScope) { +static void popLocals(CParseState *pstate, int toScope) +{ if (pstate->hadError) return; // count the locals in scope to pop int localsToPop = 0; - while (pstate->compiler->localCount > 0 && pstate->compiler->locals[pstate->compiler->localCount - 1].depth > toScope) { + while (pstate->compiler->localCount > 0 && + pstate->compiler->locals[pstate->compiler->localCount - 1].depth > toScope) { Local *local = &pstate->compiler->locals[pstate->compiler->localCount - 1]; if (local->isCaptured) { // local needs to be closed over so other closures can reference it - // first though, if there are other locals in queue to pop first, go ahead and pop those :) + // first though, if there are other locals in queue to pop first, go ahead and pop those + // :) if (localsToPop > 0) { writePop(pstate, localsToPop); localsToPop = 0; @@ -1120,26 +1262,30 @@ static void popLocals(CParseState *pstate, int toScope) { } } -static void beginScope(CParseState *pstate) { +static void beginScope(CParseState *pstate) +{ pstate->compiler->scopeDepth++; } -static void endScope(CParseState *pstate) { +static void endScope(CParseState *pstate) +{ pstate->compiler->scopeDepth--; popLocals(pstate, pstate->compiler->scopeDepth); } // parses expressionStatements until a TOKEN_END is consumed -static void block(CParseState *pstate) { - while(!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) { +static void block(CParseState *pstate) +{ + while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) { declaration(pstate); } consume(pstate, TOKEN_END, "'end' expected to end block.'"); } -static void varDeclaration(CParseState *pstate, bool forceLocal, int expectedValues) { +static void varDeclaration(CParseState *pstate, bool forceLocal, int expectedValues) +{ uint16_t ident = parseVariable(pstate, "Expected identifer!", forceLocal); expectedValues++; @@ -1175,7 +1321,8 @@ static void varDeclaration(CParseState *pstate, bool forceLocal, int expectedVal defineVariable(pstate, ident, forceLocal); } -static void ifStatement(CParseState *pstate) { +static void ifStatement(CParseState *pstate) +{ expression(pstate, 1, true); consume(pstate, TOKEN_THEN, "Expect 'then' after expression."); @@ -1184,8 +1331,10 @@ static void ifStatement(CParseState *pstate) { // parse until 'end' or 'else' beginScope(pstate); - - while(!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) && !check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) && !check(pstate, TOKEN_ERROR)) { + + while (!check(pstate, TOKEN_END) && !check(pstate, TOKEN_ELSE) && + !check(pstate, TOKEN_ELSEIF) && !check(pstate, TOKEN_EOF) && + !check(pstate, TOKEN_ERROR)) { declaration(pstate); } @@ -1193,7 +1342,7 @@ static void ifStatement(CParseState *pstate) { if (match(pstate, TOKEN_ELSE)) { int elseJump = writeJmp(pstate, OP_JMP); - + // setup our jump patchJmp(pstate, jump); @@ -1217,7 +1366,8 @@ static void ifStatement(CParseState *pstate) { } } -static void startLoop(CParseState *pstate) { +static void startLoop(CParseState *pstate) +{ LoopState *lstate = &pstate->compiler->loop; lstate->scope = pstate->compiler->scopeDepth; lstate->breaks = cosmoM_xmalloc(pstate->state, sizeof(int) * ARRAY_START); @@ -1226,16 +1376,19 @@ static void startLoop(CParseState *pstate) { lstate->startBytecode = getChunk(pstate)->count; } -// this patches all the breaks -static void endLoop(CParseState *pstate) { +// this patches all the breaks +static void endLoop(CParseState *pstate) +{ while (pstate->compiler->loop.breakCount > 0) { patchJmp(pstate, pstate->compiler->loop.breaks[--pstate->compiler->loop.breakCount]); } - cosmoM_freearray(pstate->state, int, pstate->compiler->loop.breaks, pstate->compiler->loop.breakCapacity); + cosmoM_freearray(pstate->state, int, pstate->compiler->loop.breaks, + pstate->compiler->loop.breakCapacity); } -static void whileStatement(CParseState *pstate) { +static void whileStatement(CParseState *pstate) +{ LoopState cachedLoop = pstate->compiler->loop; startLoop(pstate); int jumpLocation = getChunk(pstate)->count; @@ -1246,7 +1399,7 @@ static void whileStatement(CParseState *pstate) { consume(pstate, TOKEN_DO, "expected 'do' after conditional expression."); int exitJump = writeJmp(pstate, OP_PEJMP); // pop equality jump - valuePopped(pstate, 1); // OP_PEJMP pops the conditional! + valuePopped(pstate, 1); // OP_PEJMP pops the conditional! beginScope(pstate); block(pstate); // parse until 'end' @@ -1259,7 +1412,8 @@ static void whileStatement(CParseState *pstate) { patchJmp(pstate, exitJump); } -static void function(CParseState *pstate, FunctionType type) { +static void function(CParseState *pstate, FunctionType type) +{ CCompilerState compiler; initCompilerState(pstate, &compiler, type, pstate->compiler); @@ -1273,10 +1427,11 @@ static void function(CParseState *pstate, FunctionType type) { do { if (check(pstate, TOKEN_DOT_DOT_DOT)) break; - + // add arg to function compiler.function->args++; - if (compiler.function->args > UINT16_MAX - 1) { // -1 since the function would already be on the stack + if (compiler.function->args > + UINT16_MAX - 1) { // -1 since the function would already be on the stack errorAtCurrent(pstate, "Too many parameters!"); } @@ -1287,7 +1442,8 @@ static void function(CParseState *pstate, FunctionType type) { } while (match(pstate, TOKEN_COMMA)); } - if (match(pstate, TOKEN_DOT_DOT_DOT)) { // marks a function as variadic, now we expect an identifer for the populated variadic table + if (match(pstate, TOKEN_DOT_DOT_DOT)) { // marks a function as variadic, now we expect an + // identifer for the populated variadic table uint16_t vari = parseVariable(pstate, "Expected identifier for variadic table!", true); defineVariable(pstate, vari, true); valuePushed(pstate, 1); @@ -1315,7 +1471,8 @@ static void function(CParseState *pstate, FunctionType type) { } } -static void functionDeclaration(CParseState *pstate) { +static void functionDeclaration(CParseState *pstate) +{ uint16_t var = parseVariable(pstate, "Expected identifer!", false); if (pstate->compiler->scopeDepth > 0) @@ -1326,7 +1483,8 @@ static void functionDeclaration(CParseState *pstate) { defineVariable(pstate, var, false); } -static void returnStatement(CParseState *pstate) { +static void returnStatement(CParseState *pstate) +{ if (pstate->compiler->type != FTYPE_FUNCTION && pstate->compiler->type != FTYPE_METHOD) { error(pstate, "Expected 'return' in function!"); return; @@ -1351,7 +1509,8 @@ static void returnStatement(CParseState *pstate) { valuePopped(pstate, rvalues); } -static void localFunction(CParseState *pstate) { +static void localFunction(CParseState *pstate) +{ uint16_t var = parseVariable(pstate, "Expected identifer!", true); markInitialized(pstate, var); @@ -1360,7 +1519,8 @@ static void localFunction(CParseState *pstate) { defineVariable(pstate, var, true); } -static void forEachLoop(CParseState *pstate) { +static void forEachLoop(CParseState *pstate) +{ beginScope(pstate); // mark a slot on the stack as reserved, we do this by declaring a local with no identifer @@ -1390,7 +1550,8 @@ static void forEachLoop(CParseState *pstate) { consume(pstate, TOKEN_DO, "Expected 'do' before loop block!"); - writeu8(pstate, OP_ITER); // checks if stack[top] is iterable and pushes the __next metamethod onto the stack for OP_NEXT to call + writeu8(pstate, OP_ITER); // checks if stack[top] is iterable and pushes the __next metamethod + // onto the stack for OP_NEXT to call // start loop scope LoopState cachedLoop = pstate->compiler->loop; @@ -1399,7 +1560,7 @@ static void forEachLoop(CParseState *pstate) { int loopStart = getChunk(pstate)->count; // OP_NEXT expected a uint8_t after the opcode for how many values __next is expected to return - writeu8(pstate, OP_NEXT); + writeu8(pstate, OP_NEXT); writeu8(pstate, values); // after the u8, is a u16 with how far to jump if __next returns nil @@ -1428,7 +1589,8 @@ static void forEachLoop(CParseState *pstate) { valuePopped(pstate, 1); } -static void forLoop(CParseState *pstate) { +static void forLoop(CParseState *pstate) +{ // first, check if the next token is an identifier. if it is, this is a for loop for an iterator if (check(pstate, TOKEN_IDENTIFIER)) { forEachLoop(pstate); @@ -1469,7 +1631,8 @@ static void forLoop(CParseState *pstate) { startLoop(pstate); int iteratorStart = getChunk(pstate)->count; - expressionPrecedence(pstate, 0, PREC_ASSIGNMENT, true); // any expression (including assignment) + expressionPrecedence(pstate, 0, PREC_ASSIGNMENT, + true); // any expression (including assignment) consume(pstate, TOKEN_RIGHT_PAREN, "Expected ')' after iterator"); writeJmpBack(pstate, loopStart); @@ -1480,7 +1643,7 @@ static void forLoop(CParseState *pstate) { consume(pstate, TOKEN_DO, "Expected 'do'"); beginScope(pstate); // fixes stack issues - block(pstate); // parses until 'end' + block(pstate); // parses until 'end' endScope(pstate); writeJmpBack(pstate, loopStart); @@ -1496,7 +1659,8 @@ static void forLoop(CParseState *pstate) { endScope(pstate); } -static void breakStatement(CParseState *pstate) { +static void breakStatement(CParseState *pstate) +{ if (pstate->compiler->loop.scope == -1) { error(pstate, "'break' cannot be used outside of a loop body!"); return; @@ -1508,11 +1672,13 @@ static void breakStatement(CParseState *pstate) { pstate->compiler->localCount = savedLocals; // add break to loop - cosmoM_growarray(pstate->state, int, pstate->compiler->loop.breaks, pstate->compiler->loop.breakCount, pstate->compiler->loop.breakCapacity); + cosmoM_growarray(pstate->state, int, pstate->compiler->loop.breaks, + pstate->compiler->loop.breakCount, pstate->compiler->loop.breakCapacity); pstate->compiler->loop.breaks[pstate->compiler->loop.breakCount++] = writeJmp(pstate, OP_JMP); } -static void continueStatement(CParseState *pstate) { +static void continueStatement(CParseState *pstate) +{ if (pstate->compiler->loop.scope == -1) { error(pstate, "'continue' cannot be used outside of a loop body!"); return; @@ -1527,7 +1693,8 @@ static void continueStatement(CParseState *pstate) { writeJmpBack(pstate, pstate->compiler->loop.startBytecode); } -static void synchronize(CParseState *pstate) { +static void synchronize(CParseState *pstate) +{ pstate->panic = false; while (pstate->current.type != TOKEN_EOF) { @@ -1538,7 +1705,8 @@ static void synchronize(CParseState *pstate) { } } -static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded) { +static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec, bool forceNeeded) +{ int lastExpected = pstate->compiler->expectedValues; int saved = pstate->compiler->pushedValues + needed; pstate->compiler->expectedValues = needed; @@ -1557,11 +1725,14 @@ static int expressionPrecedence(CParseState *pstate, int needed, Precedence prec return pstate->compiler->pushedValues - (saved - needed); } -static int expression(CParseState *pstate, int needed, bool forceNeeded) { - return expressionPrecedence(pstate, needed, PREC_ASSIGNMENT + 1, forceNeeded); // anything above assignments are an expression +static int expression(CParseState *pstate, int needed, bool forceNeeded) +{ + return expressionPrecedence(pstate, needed, PREC_ASSIGNMENT + 1, + forceNeeded); // anything above assignments are an expression } -static void expressionStatement(CParseState *pstate) { +static void expressionStatement(CParseState *pstate) +{ int savedPushed = pstate->compiler->pushedValues; if (match(pstate, TOKEN_VAR)) { @@ -1615,11 +1786,13 @@ static void expressionStatement(CParseState *pstate) { alignStack(pstate, savedPushed); } -static void statement(CParseState *pstate) { +static void statement(CParseState *pstate) +{ expressionStatement(pstate); } -static void declaration(CParseState *pstate) { +static void declaration(CParseState *pstate) +{ statement(pstate); // if we paniced, skip the whole statement! @@ -1627,7 +1800,8 @@ static void declaration(CParseState *pstate) { synchronize(pstate); } -static CObjFunction *endCompiler(CParseState *pstate) { +static CObjFunction *endCompiler(CParseState *pstate) +{ popLocals(pstate, pstate->compiler->scopeDepth + 1); // remove the locals from other scopes writeu8(pstate, OP_RETURN); writeu8(pstate, 0); @@ -1639,9 +1813,10 @@ static CObjFunction *endCompiler(CParseState *pstate) { return cachedCCState->function; } -// ================================================================ [API] ================================================================ +// ================================================================ [API] -CObjFunction* cosmoP_compileString(CState *state, const char *source, const char *module) { +CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module) +{ CParseState parser; CCompilerState compiler; cosmoM_freezeGC(state); // ignore all GC events while compiling @@ -1657,7 +1832,8 @@ CObjFunction* cosmoP_compileString(CState *state, const char *source, const char popLocals(&parser, 0); - if (parser.hadError) { // we don't free the function, the state already has a reference to it in it's linked list of objects! + if (parser.hadError) { // we don't free the function, the state already has a reference to it in + // it's linked list of objects! endCompiler(&parser); freeParseState(&parser); @@ -1665,14 +1841,14 @@ CObjFunction* cosmoP_compileString(CState *state, const char *source, const char return NULL; } - CObjFunction* resFunc = compiler.function; + CObjFunction *resFunc = compiler.function; // finally free out parser states endCompiler(&parser); freeParseState(&parser); // push the funciton onto the stack so if we cause an GC event, it won't be free'd - cosmoV_pushRef(state, (CObj*)resFunc); + cosmoV_pushRef(state, (CObj *)resFunc); cosmoM_unfreezeGC(state); cosmoV_pop(state); return resFunc; diff --git a/src/cparse.h b/src/cparse.h index 2185821..3c0da88 100644 --- a/src/cparse.h +++ b/src/cparse.h @@ -1,10 +1,11 @@ #ifndef CPARSE_H #define CPARSE_H -#include "cosmo.h" #include "clex.h" +#include "cosmo.h" -// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto the stack -CObjFunction* cosmoP_compileString(CState *state, const char *source, const char *module); +// compiles source into CChunk, if NULL is returned, a syntaxical error has occurred and pushed onto +// the stack +CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module); #endif diff --git a/src/cstate.c b/src/cstate.c index ebbfbc6..dc4d980 100644 --- a/src/cstate.c +++ b/src/cstate.c @@ -1,12 +1,14 @@ #include "cstate.h" + #include "cchunk.h" +#include "cmem.h" #include "cobj.h" #include "cvm.h" -#include "cmem.h" #include -CState *cosmoV_newState() { +CState *cosmoV_newState() +{ // we use C's malloc because we don't want to trigger a GC with an invalid state CState *state = malloc(sizeof(CState)); @@ -33,7 +35,7 @@ CState *cosmoV_newState() { state->openUpvalues = NULL; state->error = NULL; - + // set default proto objects for (int i = 0; i < COBJ_MAX; i++) state->protoObjects[i] = NULL; @@ -73,7 +75,8 @@ CState *cosmoV_newState() { return state; } -void cosmoV_freeState(CState *state) { +void cosmoV_freeState(CState *state) +{ #ifdef GC_DEBUG printf("state %p is being free'd!\n", state); #endif @@ -93,34 +96,36 @@ void cosmoV_freeState(CState *state) { // free our string table (the string table includes the internal VM strings) cosmoT_clearTable(state, &state->strings); - + // free our gray stack & finally free the state structure - cosmoM_freearray(state, CObj*, state->grayStack.array, state->grayStack.capacity); + cosmoM_freearray(state, CObj *, state->grayStack.array, state->grayStack.capacity); // TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later -/*#ifdef GC_DEBUG - if (state->allocatedBytes != sizeof(CState)) { - printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", sizeof(CState), state->allocatedBytes); - exit(0); - } -#endif*/ + /*#ifdef GC_DEBUG + if (state->allocatedBytes != sizeof(CState)) { + printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", + sizeof(CState), state->allocatedBytes); exit(0); + } + #endif*/ free(state); } // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value -void cosmoV_register(CState *state, int pairs) { +void cosmoV_register(CState *state, int pairs) +{ for (int i = 0; i < pairs; i++) { StkPtr key = cosmoV_getTop(state, 1); StkPtr val = cosmoV_getTop(state, 0); CValue *oldVal = cosmoT_insert(state, &state->globals->tbl, *key); *oldVal = *val; - + cosmoV_setTop(state, 2); // pops the 2 values off the stack } } -void cosmoV_printStack(CState *state) { +void cosmoV_printStack(CState *state) +{ printf("==== [[ stack dump ]] ====\n"); for (CValue *top = state->top - 1; top >= state->stack; top--) { printf("%d: ", (int)(top - state->stack)); diff --git a/src/cstate.h b/src/cstate.h index 31e60f8..27206c6 100644 --- a/src/cstate.h +++ b/src/cstate.h @@ -1,48 +1,55 @@ #ifndef CSTATE_H #define CSTATE_H -#include "cosmo.h" #include "cobj.h" -#include "cvalue.h" +#include "cosmo.h" #include "ctable.h" +#include "cvalue.h" -struct CCallFrame { +struct CCallFrame +{ CObjClosure *closure; INSTRUCTION *pc; - CValue* base; + CValue *base; }; -typedef enum IStringEnum { - ISTRING_INIT, // __init - ISTRING_TOSTRING, // __tostring - ISTRING_TONUMBER, // __tonumber - ISTRING_EQUAL, // __equals - ISTRING_INDEX, // __index - ISTRING_NEWINDEX, // __newindex - ISTRING_COUNT, // __count - ISTRING_GETTER, // __getter - ISTRING_SETTER, // __setter - ISTRING_ITER, // __iter - ISTRING_NEXT, // __next - ISTRING_RESERVED, // __reserved - ISTRING_MAX // if this becomes greater than 33, we are out of space in cosmo_Flag. you'll have to change that to uint64_t +typedef enum IStringEnum +{ + ISTRING_INIT, // __init + ISTRING_TOSTRING, // __tostring + ISTRING_TONUMBER, // __tonumber + ISTRING_EQUAL, // __equals + ISTRING_INDEX, // __index + ISTRING_NEWINDEX, // __newindex + ISTRING_COUNT, // __count + ISTRING_GETTER, // __getter + ISTRING_SETTER, // __setter + ISTRING_ITER, // __iter + ISTRING_NEXT, // __next + ISTRING_RESERVED, // __reserved + ISTRING_MAX // if this becomes greater than 33, we are out of space in cosmo_Flag. you'll have + // to change that to uint64_t } IStringEnum; -typedef struct ArrayCObj { +typedef struct ArrayCObj +{ CObj **array; int count; int capacity; } ArrayCObj; -struct CState { +struct CState +{ bool panic; int freezeGC; // when > 0, GC events will be ignored (for internal use) int frameCount; CObjError *error; // NULL, unless panic is true - CObj *objects; // tracks all of our allocated objects - CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", lets the VM know you are holding a reference to a CObj in your code - ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found + CObj *objects; // tracks all of our allocated objects + CObj *userRoots; // user definable roots, this holds CObjs that should be considered "roots", + // lets the VM know you are holding a reference to a CObj in your code + ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but + // *have been* found size_t allocatedBytes; size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event @@ -50,16 +57,17 @@ struct CState { CTable strings; CObjTable *globals; - CValue *top; // top of the stack + CValue *top; // top of the stack CObjObject *protoObjects[COBJ_MAX]; // proto object for each COBJ type [NULL = no default proto] - CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends + CObjString + *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index & friends CCallFrame callFrame[FRAME_MAX]; // call frames - CValue stack[STACK_MAX]; // stack + CValue stack[STACK_MAX]; // stack }; COSMO_API CState *cosmoV_newState(); // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value -COSMO_API void cosmoV_register(CState *state, int pairs); +COSMO_API void cosmoV_register(CState *state, int pairs); COSMO_API void cosmoV_freeState(CState *state); COSMO_API void cosmoV_printStack(CState *state); diff --git a/src/ctable.c b/src/ctable.c index 8cbf3a7..2251e58 100644 --- a/src/ctable.c +++ b/src/ctable.c @@ -1,21 +1,25 @@ #include "ctable.h" + #include "cmem.h" -#include "cvalue.h" #include "cobj.h" +#include "cvalue.h" #include -#define MAX_TABLE_FILL 0.75 +#define MAX_TABLE_FILL 0.75 // at 30% capacity with capacity > ARRAY_START, shrink the array #define MIN_TABLE_CAPACITY ARRAY_START // bit-twiddling hacks, gets the next power of 2 -unsigned int nextPow2(unsigned int x) { - if (x <= ARRAY_START - 1) return ARRAY_START; // sanity check +unsigned int nextPow2(unsigned int x) +{ + if (x <= ARRAY_START - 1) + return ARRAY_START; // sanity check x--; int power = 2; - while (x >>= 1) power <<= 1; + while (x >>= 1) + power <<= 1; if (power < ARRAY_START) return ARRAY_START; @@ -23,7 +27,8 @@ unsigned int nextPow2(unsigned int x) { return power; } -void cosmoT_initTable(CState *state, CTable *tbl, int startCap) { +void cosmoT_initTable(CState *state, CTable *tbl, int startCap) +{ startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P tbl->capacityMask = startCap - 1; @@ -39,7 +44,8 @@ void cosmoT_initTable(CState *state, CTable *tbl, int startCap) { } } -void cosmoT_addTable(CState *state, CTable *from, CTable *to) { +void cosmoT_addTable(CState *state, CTable *from, CTable *to) +{ int cap = from->capacityMask + 1; for (int i = 0; i < cap; i++) { CTableEntry *entry = &from->table[i]; @@ -51,44 +57,50 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to) { } } -void cosmoT_clearTable(CState *state, CTable *tbl) { +void cosmoT_clearTable(CState *state, CTable *tbl) +{ cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1)); } -uint32_t getObjectHash(CObj *obj) { - switch(obj->type) { - case COBJ_STRING: - return ((CObjString*)obj)->hash; - default: - return (uint32_t)obj; // just "hash" the pointer +uint32_t getObjectHash(CObj *obj) +{ + switch (obj->type) { + case COBJ_STRING: + return ((CObjString *)obj)->hash; + default: + return (uint32_t)obj; // just "hash" the pointer } } -uint32_t getValueHash(CValue *val) { +uint32_t getValueHash(CValue *val) +{ switch (GET_TYPE(*val)) { - case COSMO_TREF: - return getObjectHash(cosmoV_readRef(*val)); - case COSMO_TNUMBER: { - uint32_t buf[sizeof(cosmo_Number)/sizeof(uint32_t)]; - cosmo_Number num = cosmoV_readNumber(*val); + case COSMO_TREF: + return getObjectHash(cosmoV_readRef(*val)); + case COSMO_TNUMBER: { + uint32_t buf[sizeof(cosmo_Number) / sizeof(uint32_t)]; + cosmo_Number num = cosmoV_readNumber(*val); - if (num == 0) - return 0; - - memcpy(buf, &num, sizeof(buf)); - for (size_t i = 0; i < sizeof(cosmo_Number)/sizeof(uint32_t); i++) buf[0] += buf[i]; - return buf[0]; - } - // TODO: add support for other types - default: + if (num == 0) return 0; + + memcpy(buf, &num, sizeof(buf)); + for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++) + buf[0] += buf[i]; + return buf[0]; + } + // TODO: add support for other types + default: + return 0; } } // mask should always be (capacity - 1) -static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key) { +static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CValue key) +{ uint32_t hash = getValueHash(&key); - uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation + uint32_t indx = hash & mask; // since we know the capacity will *always* be a power of 2, we can + // use bitwise & to perform a MUCH faster mod operation CTableEntry *tomb = NULL; // keep looking for an open slot in the entries array @@ -112,17 +124,19 @@ static CTableEntry *findEntry(CState *state, CTableEntry *entries, int mask, CVa } } -static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrink) { +static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrink) +{ if (canShrink && cosmoT_checkShrink(state, tbl)) return; - + size_t size = sizeof(CTableEntry) * newCapacity; int cachedCount = tbl->count; int newCount, oldCap; cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC - if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it, ignore our resize event! + if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it, + // ignore our resize event! return; CTableEntry *entries = cosmoM_xmalloc(state, size); @@ -157,10 +171,14 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin tbl->tombstones = 0; } -bool cosmoT_checkShrink(CState *state, CTable *tbl) { - // if count > 8 and active entries < tombstones - if (tbl->count > MIN_TABLE_CAPACITY && (tbl->count - tbl->tombstones < tbl->tombstones || tbl->tombstones > 50)) { // TODO: 50 should be a threshhold - resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); // shrink based on active entries to the next pow of 2 +bool cosmoT_checkShrink(CState *state, CTable *tbl) +{ + // if count > 8 and active entries < tombstones + if (tbl->count > MIN_TABLE_CAPACITY && + (tbl->count - tbl->tombstones < tbl->tombstones || + tbl->tombstones > 50)) { // TODO: 50 should be a threshhold + resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, + false); // shrink based on active entries to the next pow of 2 return true; } @@ -168,7 +186,8 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) { } // returns a pointer to the allocated value -COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) { +COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key) +{ // make sure we have enough space allocated int cap = tbl->capacityMask + 1; if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) { @@ -178,7 +197,8 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) { } // insert into the table - CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask + CTableEntry *entry = + findEntry(state, tbl->table, tbl->capacityMask, key); // -1 for our capacity mask if (IS_NIL(entry->key)) { if (IS_NIL(entry->val)) // is it empty? @@ -191,22 +211,25 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) { return &entry->val; } -bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) { +bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) +{ // sanity check if (tbl->count == 0) { *val = cosmoV_newNil(); return false; } - + CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); *val = entry->val; - + // return if get was successful return !(IS_NIL(entry->key)); } -bool cosmoT_remove(CState* state, CTable *tbl, CValue key) { - if (tbl->count == 0) return 0; // sanity check +bool cosmoT_remove(CState *state, CTable *tbl, CValue key) +{ + if (tbl->count == 0) + return 0; // sanity check CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); if (IS_NIL(entry->key)) // sanity check @@ -214,20 +237,26 @@ bool cosmoT_remove(CState* state, CTable *tbl, CValue key) { // crafts tombstone entry->key = cosmoV_newNil(); // this has to be nil - entry->val = cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil + entry->val = + cosmoV_newBoolean(false); // doesn't really matter what this is, as long as it isn't nil tbl->tombstones++; return true; } // returns the active entry count -COSMO_API int cosmoT_count(CTable *tbl) { +COSMO_API int cosmoT_count(CTable *tbl) +{ return tbl->count - tbl->tombstones; } -CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash) { - if (tbl->count == 0) return 0; // sanity check - uint32_t indx = hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation +CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash) +{ + if (tbl->count == 0) + return 0; // sanity check + uint32_t indx = + hash & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we + // can use bitwise & to perform a MUCH faster mod operation // keep looking for an open slot in the entries array while (true) { @@ -236,9 +265,10 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32 // check if it's an empty slot (meaning we dont have it in the table) if (IS_NIL(entry->key) && IS_NIL(entry->val)) { return NULL; - } else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length && memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) { + } else if (IS_STRING(entry->key) && cosmoV_readString(entry->key)->length == length && + memcmp(cosmoV_readString(entry->key)->str, str, length) == 0) { // it's a match! - return (CObjString*)cosmoV_readRef(entry->key); + return (CObjString *)cosmoV_readRef(entry->key); } indx = (indx + 1) & tbl->capacityMask; // fast mod here too @@ -246,7 +276,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32 } // for debugging purposes -void cosmoT_printTable(CTable *tbl, const char *name) { +void cosmoT_printTable(CTable *tbl, const char *name) +{ printf("==== [[%s]] ====\n", name); int cap = tbl->capacityMask + 1; for (int i = 0; i < cap; i++) { diff --git a/src/ctable.h b/src/ctable.h index 3287c93..157cffb 100644 --- a/src/ctable.h +++ b/src/ctable.h @@ -1,17 +1,20 @@ #ifndef CTABLE_H #define CTABLE_H -/* TODO: rewrite this table implementation. compared to other languages (including python!) this table is verrryyyy slow */ +/* TODO: rewrite this table implementation. compared to other languages (including python!) this + * table is verrryyyy slow */ #include "cosmo.h" #include "cvalue.h" -typedef struct CTableEntry { +typedef struct CTableEntry +{ CValue key; CValue val; } CTableEntry; -typedef struct CTable { +typedef struct CTable +{ int count; int capacityMask; // +1 to get the capacity int tombstones; diff --git a/src/cvalue.c b/src/cvalue.c index 1ded646..52052cc 100644 --- a/src/cvalue.c +++ b/src/cvalue.c @@ -1,105 +1,123 @@ -#include "cosmo.h" -#include "cmem.h" #include "cvalue.h" -#include "cobj.h" -void initValArray(CState *state, CValueArray *val, size_t startCapacity) { +#include "cmem.h" +#include "cobj.h" +#include "cosmo.h" + +void initValArray(CState *state, CValueArray *val, size_t startCapacity) +{ val->count = 0; val->capacity = startCapacity; val->values = NULL; } -void cleanValArray(CState *state, CValueArray *array) { +void cleanValArray(CState *state, CValueArray *array) +{ cosmoM_freearray(state, CValue, array->values, array->capacity); } -void appendValArray(CState *state, CValueArray *array, CValue val) { +void appendValArray(CState *state, CValueArray *array, CValue val) +{ cosmoM_growarray(state, CValue, array->values, array->count, array->capacity); array->values[array->count++] = val; } -bool cosmoV_equal(CState *state, CValue valA, CValue valB) { +bool cosmoV_equal(CState *state, CValue valA, CValue valB) +{ if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type? return false; // compare switch (GET_TYPE(valA)) { - case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB); - case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(valB); - case COSMO_TREF: return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB)); - case COSMO_TNIL: return true; - default: - return false; + case COSMO_TBOOLEAN: + return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB); + case COSMO_TNUMBER: + return cosmoV_readNumber(valA) == cosmoV_readNumber(valB); + case COSMO_TREF: + return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB)); + case COSMO_TNIL: + return true; + default: + return false; } } -CObjString *cosmoV_toString(CState *state, CValue val) { +CObjString *cosmoV_toString(CState *state, CValue val) +{ switch (GET_TYPE(val)) { - case COSMO_TNUMBER: { - char buf[32]; - int size = snprintf((char*)&buf, 32, "%.14g", cosmoV_readNumber(val)); - return cosmoO_copyString(state, (char*)&buf, size); - } - case COSMO_TBOOLEAN: { - return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) : cosmoO_copyString(state, "false", 5); - } - case COSMO_TREF: { - return cosmoO_toString(state, cosmoV_readRef(val)); - } - case COSMO_TNIL: { - return cosmoO_copyString(state, "nil", 3); - } - default: - return cosmoO_copyString(state, "", 10); + case COSMO_TNUMBER: { + char buf[32]; + int size = snprintf((char *)&buf, 32, "%.14g", cosmoV_readNumber(val)); + return cosmoO_copyString(state, (char *)&buf, size); + } + case COSMO_TBOOLEAN: { + return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) + : cosmoO_copyString(state, "false", 5); + } + case COSMO_TREF: { + return cosmoO_toString(state, cosmoV_readRef(val)); + } + case COSMO_TNIL: { + return cosmoO_copyString(state, "nil", 3); + } + default: + return cosmoO_copyString(state, "", 10); } } -cosmo_Number cosmoV_toNumber(CState *state, CValue val) { - switch(GET_TYPE(val)) { - case COSMO_TNUMBER: { - return cosmoV_readNumber(val); - } - case COSMO_TBOOLEAN: { - return cosmoV_readBoolean(val) ? 1 : 0; - } - case COSMO_TREF: { - return cosmoO_toNumber(state, cosmoV_readRef(val)); - } - case COSMO_TNIL: // fall through - default: - return 0; - } -} - -const char *cosmoV_typeStr(CValue val) { +cosmo_Number cosmoV_toNumber(CState *state, CValue val) +{ switch (GET_TYPE(val)) { - case COSMO_TNIL: return ""; - case COSMO_TBOOLEAN: return ""; - case COSMO_TNUMBER: return ""; - case COSMO_TREF: return cosmoO_typeStr(cosmoV_readRef(val)); - - default: - return ""; + case COSMO_TNUMBER: { + return cosmoV_readNumber(val); + } + case COSMO_TBOOLEAN: { + return cosmoV_readBoolean(val) ? 1 : 0; + } + case COSMO_TREF: { + return cosmoO_toNumber(state, cosmoV_readRef(val)); + } + case COSMO_TNIL: // fall through + default: + return 0; } } -void printValue(CValue val) { +const char *cosmoV_typeStr(CValue val) +{ switch (GET_TYPE(val)) { - case COSMO_TNUMBER: - printf("%g", cosmoV_readNumber(val)); - break; - case COSMO_TBOOLEAN: - printf(cosmoV_readBoolean(val) ? "true" : "false"); - break; - case COSMO_TREF: { - printObject(cosmoV_readRef(val)); - break; - } - case COSMO_TNIL: - printf("nil"); - break; - default: - printf(""); + case COSMO_TNIL: + return ""; + case COSMO_TBOOLEAN: + return ""; + case COSMO_TNUMBER: + return ""; + case COSMO_TREF: + return cosmoO_typeStr(cosmoV_readRef(val)); + + default: + return ""; + } +} + +void printValue(CValue val) +{ + switch (GET_TYPE(val)) { + case COSMO_TNUMBER: + printf("%g", cosmoV_readNumber(val)); + break; + case COSMO_TBOOLEAN: + printf(cosmoV_readBoolean(val) ? "true" : "false"); + break; + case COSMO_TREF: { + printObject(cosmoV_readRef(val)); + break; + } + case COSMO_TNIL: + printf("nil"); + break; + default: + printf(""); } } diff --git a/src/cvalue.h b/src/cvalue.h index 469c285..aa46abf 100644 --- a/src/cvalue.h +++ b/src/cvalue.h @@ -3,7 +3,8 @@ #include "cosmo.h" -typedef enum { +typedef enum +{ COSMO_TNUMBER, // number has to be 0 because NaN box COSMO_TBOOLEAN, COSMO_TREF, @@ -18,8 +19,8 @@ typedef double cosmo_Number; #ifdef NAN_BOXXED /* - NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures. If you don't know how this works please reference these - two articles: + NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures. + If you don't know how this works please reference these two articles: https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/ @@ -27,83 +28,88 @@ typedef double cosmo_Number; TL;DR: we can store payloads in the NaN value in the IEEE 754 standard. */ -union CValue { +union CValue +{ uint64_t data; cosmo_Number num; }; -#define MASK_TYPE ((uint64_t)0x0007000000000000) -#define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff) +# define MASK_TYPE ((uint64_t)0x0007000000000000) +# define MASK_PAYLOAD ((uint64_t)0x0000ffffffffffff) // 3 bits (low bits) are reserved for the type -#define MAKE_PAYLOAD(x) ((uint64_t)(x) & MASK_PAYLOAD) -#define READ_PAYLOAD(x) ((x).data & MASK_PAYLOAD) +# define MAKE_PAYLOAD(x) ((uint64_t)(x)&MASK_PAYLOAD) +# define READ_PAYLOAD(x) ((x).data & MASK_PAYLOAD) // The bits that must be set to indicate a quiet NaN. -#define MASK_QUIETNAN ((uint64_t)0x7ff8000000000000) +# define MASK_QUIETNAN ((uint64_t)0x7ff8000000000000) -#define GET_TYPE(x) \ - ((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) : COSMO_TNUMBER) +# define GET_TYPE(x) \ + ((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) \ + : COSMO_TNUMBER) -#define SIG_MASK (MASK_QUIETNAN | MASK_TYPE) -#define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48)) -#define OBJ_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TREF) << 48)) -#define NIL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TNIL) << 48)) +# define SIG_MASK (MASK_QUIETNAN | MASK_TYPE) +# define BOOL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48)) +# define OBJ_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TREF) << 48)) +# define NIL_SIG (MASK_QUIETNAN | ((uint64_t)(COSMO_TNIL) << 48)) -#define cosmoV_newNumber(x) ((CValue){.num = x}) -#define cosmoV_newBoolean(x) ((CValue){.data = MAKE_PAYLOAD(x) | BOOL_SIG}) -#define cosmoV_newRef(x) ((CValue){.data = MAKE_PAYLOAD((uintptr_t)x) | OBJ_SIG}) -#define cosmoV_newNil() ((CValue){.data = NIL_SIG}) +# define cosmoV_newNumber(x) ((CValue){.num = x}) +# define cosmoV_newBoolean(x) ((CValue){.data = MAKE_PAYLOAD(x) | BOOL_SIG}) +# define cosmoV_newRef(x) ((CValue){.data = MAKE_PAYLOAD((uintptr_t)x) | OBJ_SIG}) +# define cosmoV_newNil() ((CValue){.data = NIL_SIG}) -#define cosmoV_readNumber(x) ((x).num) -#define cosmoV_readBoolean(x) ((bool)READ_PAYLOAD(x)) -#define cosmoV_readRef(x) ((CObj*)READ_PAYLOAD(x)) +# define cosmoV_readNumber(x) ((x).num) +# define cosmoV_readBoolean(x) ((bool)READ_PAYLOAD(x)) +# define cosmoV_readRef(x) ((CObj *)READ_PAYLOAD(x)) -#define IS_NUMBER(x) (((x).data & MASK_QUIETNAN) != MASK_QUIETNAN) -#define IS_BOOLEAN(x) (((x).data & SIG_MASK) == BOOL_SIG) -#define IS_NIL(x) (((x).data & SIG_MASK) == NIL_SIG) -#define IS_REF(x) (((x).data & SIG_MASK) == OBJ_SIG) +# define IS_NUMBER(x) (((x).data & MASK_QUIETNAN) != MASK_QUIETNAN) +# define IS_BOOLEAN(x) (((x).data & SIG_MASK) == BOOL_SIG) +# define IS_NIL(x) (((x).data & SIG_MASK) == NIL_SIG) +# define IS_REF(x) (((x).data & SIG_MASK) == OBJ_SIG) #else /* Tagged union, this is the best platform independent solution */ -struct CValue { +struct CValue +{ CosmoType type; - union { + union + { cosmo_Number num; bool b; // boolean CObj *obj; } val; }; -#define GET_TYPE(x) ((x).type) +# define GET_TYPE(x) ((x).type) // create CValues -#define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = (x)}}) -#define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = (x)}}) -#define cosmoV_newRef(x) ((CValue){COSMO_TREF, {.obj = (CObj*)(x)}}) -#define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}}) +# define cosmoV_newNumber(x) ((CValue){COSMO_TNUMBER, {.num = (x)}}) +# define cosmoV_newBoolean(x) ((CValue){COSMO_TBOOLEAN, {.b = (x)}}) +# define cosmoV_newRef(x) ((CValue){COSMO_TREF, {.obj = (CObj *)(x)}}) +# define cosmoV_newNil() ((CValue){COSMO_TNIL, {.num = 0}}) // read CValues -#define cosmoV_readNumber(x) ((cosmo_Number)(x).val.num) -#define cosmoV_readBoolean(x) ((bool)(x).val.b) +# define cosmoV_readNumber(x) ((cosmo_Number)(x).val.num) +# define cosmoV_readBoolean(x) ((bool)(x).val.b) // grabs the CObj* pointer from the CValue -#define cosmoV_readRef(x) ((CObj*)(x).val.obj) +# define cosmoV_readRef(x) ((CObj *)(x).val.obj) -#define IS_NUMBER(x) (GET_TYPE(x) == COSMO_TNUMBER) -#define IS_BOOLEAN(x) (GET_TYPE(x) == COSMO_TBOOLEAN) -#define IS_NIL(x) (GET_TYPE(x) == COSMO_TNIL) -#define IS_REF(x) (GET_TYPE(x) == COSMO_TREF) +# define IS_NUMBER(x) (GET_TYPE(x) == COSMO_TNUMBER) +# define IS_BOOLEAN(x) (GET_TYPE(x) == COSMO_TBOOLEAN) +# define IS_NIL(x) (GET_TYPE(x) == COSMO_TNIL) +# define IS_REF(x) (GET_TYPE(x) == COSMO_TREF) #endif -typedef CValue* StkPtr; +typedef CValue *StkPtr; -struct CValueArray { +struct CValueArray +{ size_t capacity; size_t count; CValue *values; @@ -117,6 +123,7 @@ void printValue(CValue val); COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB); COSMO_API CObjString *cosmoV_toString(CState *state, CValue val); COSMO_API cosmo_Number cosmoV_toNumber(CState *state, CValue val); -COSMO_API const char *cosmoV_typeStr(CValue val); // return constant char array for corresponding type +COSMO_API const char * +cosmoV_typeStr(CValue val); // return constant char array for corresponding type #endif diff --git a/src/cvm.c b/src/cvm.c index 3c6ab35..accb818 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -1,15 +1,16 @@ #include "cvm.h" -#include "cstate.h" + #include "cdebug.h" #include "cmem.h" #include "cparse.h" +#include "cstate.h" +#include #include #include -#include - -COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) { +COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) +{ va_list args; va_start(args, format); cosmoO_pushVFString(state, format, args); @@ -17,18 +18,20 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) { } // inserts val at state->top - indx - 1, moving everything else up -COSMO_API void cosmo_insert(CState *state, int indx, CValue val) { +COSMO_API void cosmo_insert(CState *state, int indx, CValue val) +{ StkPtr tmp = cosmoV_getTop(state, indx); // moves everything up for (StkPtr i = state->top; i > tmp; i--) - *i = *(i-1); - + *i = *(i - 1); + *tmp = val; state->top++; } -COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) { +COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) +{ CObjFunction *func; if ((func = cosmoP_compileString(state, src, name)) != NULL) { @@ -36,19 +39,21 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char * #ifdef VM_DEBUG disasmChunk(&func->chunk, func->module->str, 0); #endif - // push function onto the stack so it doesn't it cleaned up by the GC, at the same stack location put our closure - cosmoV_pushRef(state, (CObj*)func); + // push function onto the stack so it doesn't it cleaned up by the GC, at the same stack + // location put our closure + cosmoV_pushRef(state, (CObj *)func); *(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func)); return true; } // fail state->panic = false; - cosmoV_pushRef(state, (CObj*)state->error); + cosmoV_pushRef(state, (CObj *)state->error); return false; } -COSMO_API void cosmoV_printError(CState *state, CObjError *err) { +COSMO_API void cosmoV_printError(CState *state, CObjError *err) +{ // print stack trace for (int i = 0; i < err->frameCount; i++) { CCallFrame *frame = &err->frames[i]; @@ -57,11 +62,14 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) { int line = chunk->lineInfo[frame->pc - chunk->buf - 1]; - if (i == err->frameCount - 1 && !err->parserError) // it's the last call frame (and not a parser error), prepare for the objection to be printed - fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length, function->module->str, line); + if (i == err->frameCount - 1 && + !err->parserError) // it's the last call frame (and not a parser error), prepare for the + // objection to be printed + fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length, + function->module->str, line); else fprintf(stderr, "[line %d] in ", line); - + if (function->name == NULL) { // unnamed chunk fprintf(stderr, "%s\n", UNNAMEDCHUNK); } else { @@ -78,11 +86,12 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) { } /* - takes value on top of the stack and wraps an CObjError around it, state->error is set to that value - the value on the stack is *expected* to be a string, but not required, so - yes, this means you could throw a nil value if you really wanted too.. + takes value on top of the stack and wraps an CObjError around it, state->error is set to that + value the value on the stack is *expected* to be a string, but not required, so yes, this means + you could throw a nil value if you really wanted too.. */ -CObjError* cosmoV_throw(CState *state) { +CObjError *cosmoV_throw(CState *state) +{ StkPtr temp = cosmoV_getTop(state, 0); CObjError *error = cosmoO_newError(state, *temp); @@ -93,10 +102,11 @@ CObjError* cosmoV_throw(CState *state) { return error; } -void cosmoV_error(CState *state, const char *format, ...) { +void cosmoV_error(CState *state, const char *format, ...) +{ if (state->panic) return; - + // i set panic before calling cosmoO_pushVFString, since that can also call cosmoV_error state->panic = true; @@ -110,11 +120,13 @@ void cosmoV_error(CState *state, const char *format, ...) { cosmoV_throw(state); } -CObjUpval *captureUpvalue(CState *state, CValue *local) { +CObjUpval *captureUpvalue(CState *state, CValue *local) +{ CObjUpval *prev = NULL; CObjUpval *upvalue = state->openUpvalues; - while (upvalue != NULL && upvalue->val > local) { // while upvalue exists and is higher on the stack than local + while (upvalue != NULL && + upvalue->val > local) { // while upvalue exists and is higher on the stack than local prev = upvalue; upvalue = upvalue->next; } @@ -136,8 +148,11 @@ CObjUpval *captureUpvalue(CState *state, CValue *local) { return newUpval; } -void closeUpvalues(CState *state, CValue *local) { - while (state->openUpvalues != NULL && state->openUpvalues->val >= local) { // for every upvalue that points to the local or anything above it +void closeUpvalues(CState *state, CValue *local) +{ + while (state->openUpvalues != NULL && + state->openUpvalues->val >= + local) { // for every upvalue that points to the local or anything above it CObjUpval *upvalue = state->openUpvalues; upvalue->closed = *upvalue->val; upvalue->val = &upvalue->closed; // upvalue now points to itself :P @@ -145,7 +160,8 @@ void closeUpvalues(CState *state, CValue *local) { } } -void pushCallFrame(CState *state, CObjClosure *closure, int args) { +void pushCallFrame(CState *state, CObjClosure *closure, int args) +{ #ifdef SAFE_STACK if (state->frameCount >= FRAME_MAX) { cosmoV_error(state, "Callframe overflow!"); @@ -159,28 +175,32 @@ void pushCallFrame(CState *state, CObjClosure *closure, int args) { frame->closure = closure; } -// offset is the offset of the callframe base we set the state->top back too (useful for passing values in the stack as arguments, like methods) -void popCallFrame(CState *state, int offset) { - closeUpvalues(state, state->callFrame[state->frameCount - 1].base); // close any upvalue still open +// offset is the offset of the callframe base we set the state->top back too (useful for passing +// values in the stack as arguments, like methods) +void popCallFrame(CState *state, int offset) +{ + closeUpvalues(state, + state->callFrame[state->frameCount - 1].base); // close any upvalue still open state->top = state->callFrame[state->frameCount - 1].base + offset; // resets the stack state->frameCount--; } -void cosmoV_concat(CState *state, int vals) { +void cosmoV_concat(CState *state, int vals) +{ StkPtr start = state->top - vals; StkPtr end = cosmoV_getTop(state, 0); CObjString *result = cosmoV_toString(state, *start); for (StkPtr current = start + 1; current <= end; current++) { - cosmoV_pushRef(state, (CObj*)result); // so our GC can find our current result string + cosmoV_pushRef(state, (CObj *)result); // so our GC can find our current result string CObjString *otherStr = cosmoV_toString(state, *current); - cosmoV_pushRef(state, (CObj*)otherStr); // also so our GC won't free otherStr + cosmoV_pushRef(state, (CObj *)otherStr); // also so our GC won't free otherStr // concat the two strings together size_t sz = result->length + otherStr->length; char *buf = cosmoM_xmalloc(state, sz + 1); // +1 for null terminator - + memcpy(buf, result->str, result->length); memcpy(buf + result->length, otherStr->str, otherStr->length); buf[sz] = '\0'; @@ -190,66 +210,73 @@ void cosmoV_concat(CState *state, int vals) { } state->top = start; - cosmoV_pushRef(state, (CObj*)result); + cosmoV_pushRef(state, (CObj *)result); } int cosmoV_execute(CState *state); -bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults, int offset); +bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset); /* - calls a native C Function with # args on the stack, nresults are pushed onto the stack upon return. + calls a native C Function with # args on the stack, nresults are pushed onto the stack upon + return. returns: false: state paniced during C Function, error is at state->error - true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset + true: state->top is moved to base + offset + nresults, with nresults pushed onto the stack + from base + offset */ -static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) { +static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nresults, int offset) +{ StkPtr savedBase = cosmoV_getTop(state, args); - // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC + // we don't want a GC event during c api because we don't actually trust the user to know how to + // evade the GC cosmoM_freezeGC(state); int nres = cfunc(state, args, savedBase + 1); cosmoM_unfreezeGC(state); - // caller function wasn't expecting this many return values, cap it if (nres > nresults) nres = nresults; // remember where the return values are - StkPtr results = cosmoV_getTop(state, nres-1); + StkPtr results = cosmoV_getTop(state, nres - 1); state->top = savedBase + offset; // set stack // if the state paniced during the c function, return false if (state->panic) return false; - + // push the return value back onto the stack - memmove(state->top, results, sizeof(CValue) * nres); // copies the return values to the top of the stack - state->top += nres; // and make sure to move state->top to match + memmove(state->top, results, + sizeof(CValue) * nres); // copies the return values to the top of the stack + state->top += nres; // and make sure to move state->top to match // now, if the caller function expected more return values, push nils onto the stack for (int i = nres; i < nresults; i++) cosmoV_pushValue(state, cosmoV_newNil()); - + return true; } /* - calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon return. - + calls a raw closure object with # args on the stack, nresults are pushed onto the stack upon + return. + returns: false: state paniced, error is at state->error - true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack from base + offset + true: stack->top is moved to base + offset + nresults, with nresults pushed onto the stack + from base + offset */ -static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset) { +static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, int offset) +{ CObjFunction *func = closure->function; // if the function is variadic and theres more args than parameters, push the args into a table if (func->variadic && args >= func->args) { int extraArgs = args - func->args; - StkPtr variStart = cosmoV_getTop(state, extraArgs-1); + StkPtr variStart = cosmoV_getTop(state, extraArgs - 1); // push key & value pairs for (int i = 0; i < extraArgs; i++) { @@ -263,7 +290,9 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, pushCallFrame(state, closure, func->args + 1); } else if (args != func->args) { // mismatched args - cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args); + cosmoV_error(state, "Expected %d arguments for %s, got %d!", closure->function->args, + closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, + args); return false; } else { // load function into callframe @@ -277,7 +306,7 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, nres = nresults; // remember where the return values are - StkPtr results = cosmoV_getTop(state, nres-1); + StkPtr results = cosmoV_getTop(state, nres - 1); // pop the callframe and return results :) popCallFrame(state, offset); @@ -298,7 +327,8 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, return true; } -bool callCValue(CState *state, CValue func, int args, int nresults, int offset) { +bool callCValue(CState *state, CValue func, int args, int nresults, int offset) +{ #ifdef VM_DEBUG printf("\n"); printIndent(state->frameCount - 1); @@ -312,61 +342,65 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset) } switch (cosmoV_readRef(func)->type) { - case COBJ_CLOSURE: - return rawCall(state, cosmoV_readClosure(func), args, nresults, offset); - case COBJ_CFUNCTION: - return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); - case COBJ_METHOD: { - CObjMethod *method = (CObjMethod*)cosmoV_readRef(func); - return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1); - } - case COBJ_OBJECT: { // object is being instantiated, making another object - CObjObject *protoObj = (CObjObject*)cosmoV_readRef(func); - CValue ret; + case COBJ_CLOSURE: + return rawCall(state, cosmoV_readClosure(func), args, nresults, offset); + case COBJ_CFUNCTION: + return callCFunction(state, cosmoV_readCFunction(func), args, nresults, offset); + case COBJ_METHOD: { + CObjMethod *method = (CObjMethod *)cosmoV_readRef(func); + return invokeMethod(state, method->obj, method->func, args, nresults, offset + 1); + } + case COBJ_OBJECT: { // object is being instantiated, making another object + CObjObject *protoObj = (CObjObject *)cosmoV_readRef(func); + CValue ret; - cosmoV_pushRef(state, (CObj*)protoObj); // push proto to stack for GC to find - CObjObject *newObj = cosmoO_newObject(state); - newObj->_obj.proto = protoObj; - cosmoV_pop(state); // pop proto + cosmoV_pushRef(state, (CObj *)protoObj); // push proto to stack for GC to find + CObjObject *newObj = cosmoO_newObject(state); + newObj->_obj.proto = protoObj; + cosmoV_pop(state); // pop proto - // check if they defined an initializer (we accept 0 return values) - if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) { - if (!invokeMethod(state, (CObj*)newObj, ret, args, 0, offset + 1)) - return false; - } else { - // no default initializer - cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!"); + // check if they defined an initializer (we accept 0 return values) + if (cosmoO_getIString(state, protoObj, ISTRING_INIT, &ret)) { + if (!invokeMethod(state, (CObj *)newObj, ret, args, 0, offset + 1)) return false; - } - - if (nresults > 0) { - cosmoV_pushRef(state, (CObj*)newObj); - - // push the nils to fill up the expected return values - for (int i = 0; i < nresults - 1; i++) { // -1 since the we already pushed the important value - cosmoV_pushValue(state, cosmoV_newNil()); - } - } - break; - } - default: - cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func)); + } else { + // no default initializer + cosmoV_error(state, "Expected __init() in proto, object cannot be instantiated!"); return false; + } + + if (nresults > 0) { + cosmoV_pushRef(state, (CObj *)newObj); + + // push the nils to fill up the expected return values + for (int i = 0; i < nresults - 1; + i++) { // -1 since the we already pushed the important value + cosmoV_pushValue(state, cosmoV_newNil()); + } + } + break; + } + default: + cosmoV_error(state, "Cannot call non-callable type %s!", cosmoV_typeStr(func)); + return false; } return true; } -bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults, int offset) { +bool invokeMethod(CState *state, CObj *obj, CValue func, int args, int nresults, int offset) +{ // first, set the first argument to the object StkPtr temp = cosmoV_getTop(state, args); *temp = cosmoV_newRef(obj); - return callCValue(state, func, args+1, nresults, offset); + return callCValue(state, func, args + 1, nresults, offset); } -// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call failed, else return values are passed -COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) { +// wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call +// failed, else return values are passed +COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) +{ StkPtr base = cosmoV_getTop(state, args); if (!callCValue(state, *base, args, nresults, 0)) { @@ -374,10 +408,10 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) { state->panic = false; if (nresults > 0) { - cosmoV_pushRef(state, (CObj*)state->error); - + cosmoV_pushRef(state, (CObj *)state->error); + // push other expected results onto the stack - for (int i = 0; i < nresults-1; i++) + for (int i = 0; i < nresults - 1; i++) cosmoV_pushValue(state, cosmoV_newNil()); } @@ -388,30 +422,34 @@ COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) { } /* - calls a callable object at stack->top - args - 1, passing the # of args to the callable, and ensuring nresults are returned + calls a callable object at stack->top - args - 1, passing the # of args to the callable, and + ensuring nresults are returned returns: COSMOVM_OK: callable object exited normally COSMOVM_RUNTIME_ERR: an error occurred, grab the error from state->error */ -COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) { +COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults) +{ StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args return callCValue(state, *val, args, nresults, 0) ? COSMOVM_OK : COSMOVM_RUNTIME_ERR; } -static inline bool isFalsey(StkPtr val) { +static inline bool isFalsey(StkPtr val) +{ return IS_NIL(*val) || (IS_BOOLEAN(*val) && !cosmoV_readBoolean(*val)); } -COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) { +COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs) +{ StkPtr key, val; CObjObject *newObj = cosmoO_newObject(state); - cosmoV_pushRef(state, (CObj*)newObj); // so our GC doesn't free our new object + cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new object for (int i = 0; i < pairs; i++) { - val = cosmoV_getTop(state, (i*2) + 1); - key = cosmoV_getTop(state, (i*2) + 2); + val = cosmoV_getTop(state, (i * 2) + 1); + key = cosmoV_getTop(state, (i * 2) + 2); // set key/value pair CValue *newVal = cosmoT_insert(state, &newObj->tbl, *key); @@ -420,11 +458,12 @@ COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) { // once done, pop everything off the stack + push new object cosmoV_setTop(state, (pairs * 2) + 1); // + 1 for our object - cosmoV_pushRef(state, (CObj*)newObj); + cosmoV_pushRef(state, (CObj *)newObj); return newObj; } -COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) { +COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) +{ bool replaced = state->protoObjects[objType] != NULL; state->protoObjects[objType] = obj; @@ -441,14 +480,15 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO return replaced; } -COSMO_API void cosmoV_makeTable(CState *state, int pairs) { +COSMO_API void cosmoV_makeTable(CState *state, int pairs) +{ StkPtr key, val; CObjTable *newObj = cosmoO_newTable(state); - cosmoV_pushRef(state, (CObj*)newObj); // so our GC doesn't free our new table + cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new table for (int i = 0; i < pairs; i++) { - val = cosmoV_getTop(state, (i*2) + 1); - key = cosmoV_getTop(state, (i*2) + 2); + val = cosmoV_getTop(state, (i * 2) + 1); + key = cosmoV_getTop(state, (i * 2) + 2); // set key/value pair CValue *newVal = cosmoT_insert(state, &newObj->tbl, *key); @@ -457,22 +497,24 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs) { // once done, pop everything off the stack + push new table cosmoV_setTop(state, (pairs * 2) + 1); // + 1 for our table - cosmoV_pushRef(state, (CObj*)newObj); + cosmoV_pushRef(state, (CObj *)newObj); } -bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) { +bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) +{ CObjObject *object = cosmoO_grabProto(_obj); - + // no proto to get from if (object == NULL) { CObjString *field = cosmoV_toString(state, key); - cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str, cosmoO_typeStr(_obj)); + cosmoV_error(state, "No proto defined! Couldn't get field '%s' from type %s", field->str, + cosmoO_typeStr(_obj)); *val = cosmoV_newNil(); return false; } // push the object onto the stack so the GC can find it - cosmoV_pushRef(state, (CObj*)object); + cosmoV_pushRef(state, (CObj *)object); if (cosmoO_getRawObject(state, object, key, val, _obj)) { // *val now equals the response, pop the object cosmoV_pop(state); @@ -483,13 +525,15 @@ bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) { return false; } -bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) { +bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) +{ CObjObject *object = cosmoO_grabProto(_obj); // no proto to set to if (object == NULL) { CObjString *field = cosmoV_toString(state, key); - cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str, cosmoO_typeStr(_obj)); + cosmoV_error(state, "No proto defined! Couldn't set field '%s' to type %s", field->str, + cosmoO_typeStr(_obj)); return false; } @@ -497,7 +541,8 @@ bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) { return true; } -COSMO_API bool cosmoV_get(CState *state) { +COSMO_API bool cosmoV_get(CState *state) +{ CValue val; StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first StkPtr key = cosmoV_getTop(state, 0); // then the key @@ -516,8 +561,9 @@ COSMO_API bool cosmoV_get(CState *state) { return true; } -// yes, this would technically make it possible to set fields of types other than . go crazy -COSMO_API bool cosmoV_set(CState *state) { +// yes, this would technically make it possible to set fields of types other than . go crazy +COSMO_API bool cosmoV_set(CState *state) +{ StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first StkPtr key = cosmoV_getTop(state, 1); // then the key StkPtr val = cosmoV_getTop(state, 0); // and finally the value @@ -529,20 +575,21 @@ COSMO_API bool cosmoV_set(CState *state) { if (!cosmoV_rawset(state, cosmoV_readRef(*obj), *key, *val)) return false; - + // pop the obj, key & value cosmoV_setTop(state, 3); return true; } -COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) { +COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) +{ if (!cosmoV_rawget(state, obj, key, val)) return false; - + // if the result is callable, wrap it in an method if (IS_CALLABLE(*val)) { // push object to stack so the GC can find it - cosmoV_pushRef(state, (CObj*)obj); + cosmoV_pushRef(state, (CObj *)obj); CObjMethod *method = cosmoO_newMethod(state, *val, obj); cosmoV_pop(state); // pop the object *val = cosmoV_newRef(method); @@ -551,7 +598,8 @@ COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *va return true; } -int _tbl__next(CState *state, int nargs, CValue *args) { +int _tbl__next(CState *state, int nargs, CValue *args) +{ if (nargs != 1) { cosmoV_error(state, "Expected 1 parameter, %d received!", nargs); return 0; @@ -565,14 +613,15 @@ int _tbl__next(CState *state, int nargs, CValue *args) { CObjObject *obj = cosmoV_readObject(args[0]); int index = cosmoO_getUserI(obj); // we store the index in the userdata CValue val; - + cosmoO_getIString(state, obj, ISTRING_RESERVED, &val); if (!IS_TABLE(val)) { - return 0; // someone set the __reserved member to something else. this will exit the iterator loop + return 0; // someone set the __reserved member to something else. this will exit the + // iterator loop } - CObjTable *table = (CObjTable*)cosmoV_readRef(val); + CObjTable *table = (CObjTable *)cosmoV_readRef(val); // while the entry is invalid, go to the next entry int cap = table->tbl.capacityMask + 1; @@ -582,7 +631,8 @@ int _tbl__next(CState *state, int nargs, CValue *args) { } while (IS_NIL(entry->key) && index < cap); cosmoO_setUserI(obj, index); // update the userdata - if (index < cap && !IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair + if (index < cap && + !IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair cosmoV_pushValue(state, entry->key); cosmoV_pushValue(state, entry->val); return 2; // we pushed 2 values onto the stack for the return values @@ -591,626 +641,660 @@ int _tbl__next(CState *state, int nargs, CValue *args) { } } -#define NUMBEROP(typeConst, op) \ - StkPtr valA = cosmoV_getTop(state, 1); \ - StkPtr valB = cosmoV_getTop(state, 0); \ - if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { \ - cosmoV_setTop(state, 2); /* pop the 2 values */ \ - cosmoV_pushValue(state, typeConst(cosmoV_readNumber(*valA) op cosmoV_readNumber(*valB))); \ - } else { \ - cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); \ - return -1; \ - } \ +#define NUMBEROP(typeConst, op) \ + StkPtr valA = cosmoV_getTop(state, 1); \ + StkPtr valB = cosmoV_getTop(state, 0); \ + if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { \ + cosmoV_setTop(state, 2); /* pop the 2 values */ \ + cosmoV_pushValue(state, typeConst(cosmoV_readNumber(*valA) op cosmoV_readNumber(*valB))); \ + } else { \ + cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), \ + cosmoV_typeStr(*valB)); \ + return -1; \ + } // returns -1 if panic -int cosmoV_execute(CState *state) { - CCallFrame* frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame +int cosmoV_execute(CState *state) +{ + CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :) #define READBYTE() *frame->pc++ -#define READUINT() (frame->pc += 2, *(uint16_t*)(&frame->pc[-2])) +#define READUINT() (frame->pc += 2, *(uint16_t *)(&frame->pc[-2])) while (!state->panic) { #ifdef VM_DEBUG cosmoV_printStack(state); - disasmInstr(&frame->closure->function->chunk, frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1); + disasmInstr(&frame->closure->function->chunk, + frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1); printf("\n"); #endif switch (READBYTE()) { - case OP_LOADCONST: { // push const[uint] to stack - uint16_t indx = READUINT(); - cosmoV_pushValue(state, constants[indx]); - continue; - } - case OP_SETGLOBAL: { - uint16_t indx = READUINT(); - CValue ident = constants[indx]; // grabs identifier - CValue *val = cosmoT_insert(state, &state->globals->tbl, ident); - *val = *cosmoV_pop(state); // sets the value in the hash table - continue; - } - case OP_GETGLOBAL: { - uint16_t indx = READUINT(); - CValue ident = constants[indx]; // grabs identifier - CValue val; // to hold our value - cosmoT_get(state, &state->globals->tbl, ident, &val); - cosmoV_pushValue(state, val); // pushes the value to the stack - continue; - } - case OP_SETLOCAL: { - uint8_t indx = READBYTE(); - // set base to top of stack & pop - frame->base[indx] = *cosmoV_pop(state); - continue; - } - case OP_GETLOCAL: { - uint8_t indx = READBYTE(); - cosmoV_pushValue(state, frame->base[indx]); - continue; - } - case OP_GETUPVAL: { - uint8_t indx = READBYTE(); - cosmoV_pushValue(state, *frame->closure->upvalues[indx]->val); - continue; - } - case OP_SETUPVAL: { - uint8_t indx = READBYTE(); - *frame->closure->upvalues[indx]->val = *cosmoV_pop(state); - continue; - } - case OP_PEJMP: { // pop equality jump - uint16_t offset = READUINT(); + case OP_LOADCONST: { // push const[uint] to stack + uint16_t indx = READUINT(); + cosmoV_pushValue(state, constants[indx]); + continue; + } + case OP_SETGLOBAL: { + uint16_t indx = READUINT(); + CValue ident = constants[indx]; // grabs identifier + CValue *val = cosmoT_insert(state, &state->globals->tbl, ident); + *val = *cosmoV_pop(state); // sets the value in the hash table + continue; + } + case OP_GETGLOBAL: { + uint16_t indx = READUINT(); + CValue ident = constants[indx]; // grabs identifier + CValue val; // to hold our value + cosmoT_get(state, &state->globals->tbl, ident, &val); + cosmoV_pushValue(state, val); // pushes the value to the stack + continue; + } + case OP_SETLOCAL: { + uint8_t indx = READBYTE(); + // set base to top of stack & pop + frame->base[indx] = *cosmoV_pop(state); + continue; + } + case OP_GETLOCAL: { + uint8_t indx = READBYTE(); + cosmoV_pushValue(state, frame->base[indx]); + continue; + } + case OP_GETUPVAL: { + uint8_t indx = READBYTE(); + cosmoV_pushValue(state, *frame->closure->upvalues[indx]->val); + continue; + } + case OP_SETUPVAL: { + uint8_t indx = READBYTE(); + *frame->closure->upvalues[indx]->val = *cosmoV_pop(state); + continue; + } + case OP_PEJMP: { // pop equality jump + uint16_t offset = READUINT(); - if (isFalsey(cosmoV_pop(state))) { // pop, if the condition is false, jump! - frame->pc += offset; - } - continue; - } - case OP_EJMP: { // equality jump - uint16_t offset = READUINT(); - - if (isFalsey(cosmoV_getTop(state, 0))) { // if the condition is false, jump! - frame->pc += offset; - } - continue; - } - case OP_JMP: { // jump - uint16_t offset = READUINT(); + if (isFalsey(cosmoV_pop(state))) { // pop, if the condition is false, jump! frame->pc += offset; - continue; } - case OP_JMPBACK: { - uint16_t offset = READUINT(); - frame->pc -= offset; - continue; + continue; + } + case OP_EJMP: { // equality jump + uint16_t offset = READUINT(); + + if (isFalsey(cosmoV_getTop(state, 0))) { // if the condition is false, jump! + frame->pc += offset; } - case OP_POP: { // pops value off the stack - cosmoV_setTop(state, READBYTE()); - continue; + continue; + } + case OP_JMP: { // jump + uint16_t offset = READUINT(); + frame->pc += offset; + continue; + } + case OP_JMPBACK: { + uint16_t offset = READUINT(); + frame->pc -= offset; + continue; + } + case OP_POP: { // pops value off the stack + cosmoV_setTop(state, READBYTE()); + continue; + } + case OP_CALL: { + uint8_t args = READBYTE(); + uint8_t nres = READBYTE(); + if (cosmoV_call(state, args, nres) != COSMOVM_OK) { + return -1; } - case OP_CALL: { - uint8_t args = READBYTE(); - uint8_t nres = READBYTE(); - if (cosmoV_call(state, args, nres) != COSMOVM_OK) { + continue; + } + case OP_CLOSURE: { + uint16_t index = READUINT(); + CObjFunction *func = cosmoV_readFunction(constants[index]); + CObjClosure *closure = cosmoO_newClosure(state, func); + cosmoV_pushRef(state, (CObj *)closure); + + for (int i = 0; i < closure->upvalueCount; i++) { + uint8_t encoding = READBYTE(); + uint8_t index = READBYTE(); + if (encoding == OP_GETUPVAL) { + // capture upvalue from current frame's closure + closure->upvalues[i] = frame->closure->upvalues[index]; + } else { + // capture local + closure->upvalues[i] = captureUpvalue(state, frame->base + index); + } + } + + continue; + } + case OP_CLOSE: { + closeUpvalues(state, state->top - 1); + cosmoV_pop(state); + continue; + } + case OP_NEWTABLE: { + uint16_t pairs = READUINT(); + cosmoV_makeTable(state, pairs); + continue; + } + case OP_NEWARRAY: { + uint16_t pairs = READUINT(); + StkPtr val; + CObjTable *newObj = cosmoO_newTable(state); + cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new table + + for (int i = 0; i < pairs; i++) { + val = cosmoV_getTop(state, i + 1); + + // set key/value pair + CValue *newVal = + cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1)); + *newVal = *val; + } + + // once done, pop everything off the stack + push new table + cosmoV_setTop(state, pairs + 1); // + 1 for our table + cosmoV_pushRef(state, (CObj *)newObj); + continue; + } + case OP_INDEX: { + StkPtr key = cosmoV_getTop(state, 0); // key should be the top of the stack + StkPtr temp = cosmoV_getTop(state, 1); // after that should be the table + + // sanity check + if (!IS_REF(*temp)) { + cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp)); + return -1; + } + + CObj *obj = cosmoV_readRef(*temp); + CObjObject *proto = cosmoO_grabProto(obj); + CValue val; // to hold our value + + if (proto != NULL) { + // check for __index metamethod + if (!cosmoO_indexObject(state, proto, *key, + &val)) // if returns false, cosmoV_error was called + return -1; + } else if (obj->type == COBJ_TABLE) { + CObjTable *tbl = (CObjTable *)obj; + + cosmoT_get(state, &tbl->tbl, *key, &val); + } else { + cosmoV_error(state, "No proto defined! Couldn't __index from type %s", + cosmoV_typeStr(*temp)); + return -1; + } + + cosmoV_setTop(state, 2); // pops the table & the key + cosmoV_pushValue(state, val); // pushes the field result + continue; + } + case OP_NEWINDEX: { + StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack + StkPtr key = cosmoV_getTop(state, 1); + StkPtr temp = cosmoV_getTop(state, 2); // table is after the key + + // sanity check + if (!IS_REF(*temp)) { + cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); + return -1; + } + + CObj *obj = cosmoV_readRef(*temp); + CObjObject *proto = cosmoO_grabProto(obj); + + if (proto != NULL) { + if (!cosmoO_newIndexObject(state, proto, *key, + *value)) // if it returns false, cosmoV_error was called + return -1; + } else if (obj->type == COBJ_TABLE) { + CObjTable *tbl = (CObjTable *)obj; + CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key); + + *newVal = *value; // set the index + } else { + cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", + cosmoV_typeStr(*temp)); + return -1; + } + + // pop everything off the stack + cosmoV_setTop(state, 3); + continue; + } + case OP_NEWOBJECT: { + uint16_t pairs = READUINT(); + cosmoV_makeObject(state, pairs); + continue; + } + case OP_SETOBJECT: { + StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack + StkPtr temp = cosmoV_getTop(state, 1); // object is after the value + uint16_t ident = READUINT(); // use for the key + + // sanity check + if (IS_REF(*temp)) { + if (!cosmoV_rawset(state, cosmoV_readRef(*temp), constants[ident], *value)) + return -1; + } else { + CObjString *field = cosmoV_toString(state, constants[ident]); + cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, + cosmoV_typeStr(*temp)); + return -1; + } + + // pop everything off the stack + cosmoV_setTop(state, 2); + continue; + } + case OP_GETOBJECT: { + CValue val; // to hold our value + StkPtr temp = cosmoV_getTop(state, 0); // that should be the object + uint16_t ident = READUINT(); // use for the key + + // sanity check + if (IS_REF(*temp)) { + if (!cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val)) + return -1; + } else { + CObjString *field = cosmoV_toString(state, constants[ident]); + cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, + cosmoV_typeStr(*temp)); + return -1; + } + + cosmoV_setTop(state, 1); // pops the object + cosmoV_pushValue(state, val); // pushes the field result + continue; + } + case OP_GETMETHOD: { + CValue val; // to hold our value + StkPtr temp = cosmoV_getTop(state, 0); // that should be the object + uint16_t ident = READUINT(); // use for the key + + // this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of + // just cosmoV_get + if (IS_REF(*temp)) { + if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val)) + return -1; + } else { + CObjString *field = cosmoV_toString(state, constants[ident]); + cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, + cosmoV_typeStr(*temp)); + return -1; + } + + cosmoV_setTop(state, 1); // pops the object + cosmoV_pushValue(state, val); // pushes the field result + continue; + } + case OP_INVOKE: { + uint8_t args = READBYTE(); + uint8_t nres = READBYTE(); + uint16_t ident = READUINT(); + StkPtr temp = cosmoV_getTop(state, args); // grabs object from stack + CValue val; // to hold our value + + // sanity check + if (IS_REF(*temp)) { + // get the field from the object + if (!cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val)) + return -1; + + // now invoke the method! + invokeMethod(state, cosmoV_readRef(*temp), val, args, nres, 1); + } else { + cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp)); + return -1; + } + + continue; + } + case OP_ITER: { + StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table + + if (!IS_REF(*temp)) { + cosmoV_error(state, "Couldn't iterate over non-iterator type %s!", + cosmoV_typeStr(*temp)); + return -1; + } + + CObj *obj = cosmoV_readRef(*temp); + CObjObject *proto = cosmoO_grabProto(obj); + CValue val; + + if (proto != NULL) { + // grab __iter & call it + if (cosmoO_getIString(state, proto, ISTRING_ITER, &val)) { + cosmoV_pop(state); // pop the object from the stack + cosmoV_pushValue(state, val); + cosmoV_pushRef(state, (CObj *)obj); + if (cosmoV_call(state, 1, 1) != + COSMOVM_OK) // we expect 1 return value on the stack, the iterable object + return -1; + + StkPtr iObj = cosmoV_getTop(state, 0); + + if (!IS_OBJECT(*iObj)) { + cosmoV_error( + state, + "Expected iterable object! '__iter' returned %s, expected !", + cosmoV_typeStr(*iObj)); + return -1; + } + + // get __next method and place it at the top of the stack + cosmoV_getMethod(state, cosmoV_readRef(*iObj), + cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj); + } else { + cosmoV_error(state, "Expected iterable object! '__iter' not defined!"); return -1; } - continue; - } - case OP_CLOSURE: { - uint16_t index = READUINT(); - CObjFunction *func = cosmoV_readFunction(constants[index]); - CObjClosure *closure = cosmoO_newClosure(state, func); - cosmoV_pushRef(state, (CObj*)closure); + } else if (obj->type == COBJ_TABLE) { + CObjTable *tbl = (CObjTable *)obj; - for (int i = 0; i < closure->upvalueCount; i++) { - uint8_t encoding = READBYTE(); - uint8_t index = READBYTE(); - if (encoding == OP_GETUPVAL) { - // capture upvalue from current frame's closure - closure->upvalues[i] = frame->closure->upvalues[index]; - } else { - // capture local - closure->upvalues[i] = captureUpvalue(state, frame->base + index); - } - } - - continue; + cosmoV_pushRef(state, (CObj *)state->iStrings[ISTRING_RESERVED]); // key + cosmoV_pushRef(state, (CObj *)tbl); // value + + cosmoV_pushString(state, "__next"); // key + CObjCFunction *tbl_next = cosmoO_newCFunction(state, _tbl__next); + cosmoV_pushRef(state, (CObj *)tbl_next); // value + + CObjObject *obj = cosmoV_makeObject(state, 2); // pushes the new object to the stack + cosmoO_setUserI(obj, 0); // increment for iterator + + // make our CObjMethod for OP_NEXT to call + CObjMethod *method = cosmoO_newMethod(state, cosmoV_newRef(tbl_next), (CObj *)obj); + + cosmoV_setTop(state, 2); // pops the object & the tbl + cosmoV_pushRef(state, (CObj *)method); // pushes the method for OP_NEXT + } else { + cosmoV_error(state, "No proto defined! Couldn't get from type %s", + cosmoO_typeStr(obj)); + return -1; } - case OP_CLOSE: { - closeUpvalues(state, state->top - 1); + + continue; + } + case OP_NEXT: { + uint8_t nresults = READBYTE(); + uint16_t jump = READUINT(); + StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack + + if (!IS_METHOD(*temp)) { + cosmoV_error(state, "Expected '__next' to be a method, got type %s!", + cosmoV_typeStr(*temp)); + return -1; + } + + cosmoV_pushValue(state, *temp); + if (cosmoV_call(state, 0, nresults) != COSMOVM_OK) + return -1; + + if (IS_NIL(*(cosmoV_getTop( + state, 0)))) { // __next returned a nil, which means to exit the loop + cosmoV_setTop(state, nresults); // pop the return values + frame->pc += jump; + } + continue; + } + case OP_ADD: { // pop 2 values off the stack & try to add them together + NUMBEROP(cosmoV_newNumber, +); + continue; + } + case OP_SUB: { // pop 2 values off the stack & try to subtracts them + NUMBEROP(cosmoV_newNumber, -) + continue; + } + case OP_MULT: { // pop 2 values off the stack & try to multiplies them together + NUMBEROP(cosmoV_newNumber, *) + continue; + } + case OP_DIV: { // pop 2 values off the stack & try to divides them + NUMBEROP(cosmoV_newNumber, /) + continue; + } + case OP_MOD: { + StkPtr valA = cosmoV_getTop(state, 1); + StkPtr valB = cosmoV_getTop(state, 0); + if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { + cosmoV_setTop(state, 2); /* pop the 2 values */ + cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA), + cosmoV_readNumber(*valB)))); + } else { + cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), + cosmoV_typeStr(*valB)); + return -1; + } + continue; + } + case OP_POW: { + StkPtr valA = cosmoV_getTop(state, 1); + StkPtr valB = cosmoV_getTop(state, 0); + if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { + cosmoV_setTop(state, 2); /* pop the 2 values */ + cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA), + cosmoV_readNumber(*valB)))); + } else { + cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), + cosmoV_typeStr(*valB)); + return -1; + } + continue; + } + case OP_NOT: { + cosmoV_pushBoolean(state, isFalsey(cosmoV_pop(state))); + continue; + } + case OP_NEGATE: { // pop 1 value off the stack & try to negate + StkPtr val = cosmoV_getTop(state, 0); + + if (IS_NUMBER(*val)) { cosmoV_pop(state); - continue; + cosmoV_pushNumber(state, -(cosmoV_readNumber(*val))); + } else { + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); + return -1; } - case OP_NEWTABLE: { - uint16_t pairs = READUINT(); - cosmoV_makeTable(state, pairs); - continue; + continue; + } + case OP_COUNT: { + StkPtr temp = cosmoV_getTop(state, 0); + + if (!IS_REF(*temp)) { + cosmoV_error(state, "Expected non-primitive, got %s!", cosmoV_typeStr(*temp)); + return -1; } - case OP_NEWARRAY: { - uint16_t pairs = READUINT(); - StkPtr val; - CObjTable *newObj = cosmoO_newTable(state); - cosmoV_pushRef(state, (CObj*)newObj); // so our GC doesn't free our new table - for (int i = 0; i < pairs; i++) { - val = cosmoV_getTop(state, i + 1); + int count = cosmoO_count(state, cosmoV_readRef(*temp)); + cosmoV_pop(state); - // set key/value pair - CValue *newVal = cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1)); - *newVal = *val; - } + cosmoV_pushNumber(state, count); // pushes the count onto the stack + continue; + } + case OP_CONCAT: { + uint8_t vals = READBYTE(); + cosmoV_concat(state, vals); + continue; + } + case OP_INCLOCAL: { // this leaves the value on the stack + int8_t inc = READBYTE() - 128; // amount we're incrementing by + uint8_t indx = READBYTE(); + StkPtr val = &frame->base[indx]; - // once done, pop everything off the stack + push new table - cosmoV_setTop(state, pairs + 1); // + 1 for our table - cosmoV_pushRef(state, (CObj*)newObj); - continue; + // check that it's a number value + if (IS_NUMBER(*val)) { + cosmoV_pushValue(state, *val); // pushes old value onto the stack :) + *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); + } else { + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); + return -1; } - case OP_INDEX: { - StkPtr key = cosmoV_getTop(state, 0); // key should be the top of the stack - StkPtr temp = cosmoV_getTop(state, 1); // after that should be the table - // sanity check - if (!IS_REF(*temp)) { - cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp)); - return -1; - } + continue; + } + case OP_INCGLOBAL: { + int8_t inc = READBYTE() - 128; // amount we're incrementing by + uint16_t indx = READUINT(); + CValue ident = constants[indx]; // grabs identifier + CValue *val = cosmoT_insert(state, &state->globals->tbl, ident); - CObj *obj = cosmoV_readRef(*temp); - CObjObject *proto = cosmoO_grabProto(obj); - CValue val; // to hold our value - - if (proto != NULL) { - // check for __index metamethod - if (!cosmoO_indexObject(state, proto, *key, &val)) // if returns false, cosmoV_error was called - return -1; - } else if (obj->type == COBJ_TABLE) { - CObjTable *tbl = (CObjTable*)obj; - - cosmoT_get(state, &tbl->tbl, *key, &val); - } else { - cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); - return -1; - } - - cosmoV_setTop(state, 2); // pops the table & the key - cosmoV_pushValue(state, val); // pushes the field result - continue; + // check that it's a number value + if (IS_NUMBER(*val)) { + cosmoV_pushValue(state, *val); // pushes old value onto the stack :) + *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); + } else { + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); + return -1; } - case OP_NEWINDEX: { - StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack - StkPtr key = cosmoV_getTop(state, 1); - StkPtr temp = cosmoV_getTop(state, 2); // table is after the key - // sanity check - if (!IS_REF(*temp)) { - cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); - return -1; - } + continue; + } + case OP_INCUPVAL: { + int8_t inc = READBYTE() - 128; // amount we're incrementing by + uint8_t indx = READBYTE(); + CValue *val = frame->closure->upvalues[indx]->val; - CObj *obj = cosmoV_readRef(*temp); - CObjObject *proto = cosmoO_grabProto(obj); - - if (proto != NULL) { - if (!cosmoO_newIndexObject(state, proto, *key, *value)) // if it returns false, cosmoV_error was called - return -1; - } else if (obj->type == COBJ_TABLE) { - CObjTable *tbl = (CObjTable*)obj; - CValue *newVal = cosmoT_insert(state, &tbl->tbl, *key); - - *newVal = *value; // set the index - } else { - cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", cosmoV_typeStr(*temp)); - return -1; - } - - // pop everything off the stack - cosmoV_setTop(state, 3); - continue; + // check that it's a number value + if (IS_NUMBER(*val)) { + cosmoV_pushValue(state, *val); // pushes old value onto the stack :) + *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); + } else { + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); + return -1; } - case OP_NEWOBJECT: { - uint16_t pairs = READUINT(); - cosmoV_makeObject(state, pairs); - continue; + + continue; + } + case OP_INCINDEX: { + int8_t inc = READBYTE() - 128; // amount we're incrementing by + StkPtr temp = cosmoV_getTop(state, 1); // object should be above the key + StkPtr key = cosmoV_getTop(state, 0); // grabs key + + if (!IS_REF(*temp)) { + cosmoV_error(state, "Couldn't index non-indexable type %s!", cosmoV_typeStr(*temp)); + return -1; } - case OP_SETOBJECT: { - StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack - StkPtr temp = cosmoV_getTop(state, 1); // object is after the value - uint16_t ident = READUINT(); // use for the key - // sanity check - if (IS_REF(*temp)) { - if (!cosmoV_rawset(state, cosmoV_readRef(*temp), constants[ident], *value)) - return -1; - } else { - CObjString *field = cosmoV_toString(state, constants[ident]); - cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, cosmoV_typeStr(*temp)); - return -1; - } + CObj *obj = cosmoV_readRef(*temp); + CObjObject *proto = cosmoO_grabProto(obj); + CValue val; - // pop everything off the stack - cosmoV_setTop(state, 2); - continue; - } - case OP_GETOBJECT: { - CValue val; // to hold our value - StkPtr temp = cosmoV_getTop(state, 0); // that should be the object - uint16_t ident = READUINT(); // use for the key - - // sanity check - if (IS_REF(*temp)) { - if (!cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val)) - return -1; - } else { - CObjString *field = cosmoV_toString(state, constants[ident]); - cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp)); - return -1; - } - - cosmoV_setTop(state, 1); // pops the object - cosmoV_pushValue(state, val); // pushes the field result - continue; - } - case OP_GETMETHOD: { - CValue val; // to hold our value - StkPtr temp = cosmoV_getTop(state, 0); // that should be the object - uint16_t ident = READUINT(); // use for the key - - // this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of just cosmoV_get - if (IS_REF(*temp)) { - if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val)) - return -1; - } else { - CObjString *field = cosmoV_toString(state, constants[ident]); - cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp)); - return -1; - } - - cosmoV_setTop(state, 1); // pops the object - cosmoV_pushValue(state, val); // pushes the field result - continue; - } - case OP_INVOKE: { - uint8_t args = READBYTE(); - uint8_t nres = READBYTE(); - uint16_t ident = READUINT(); - StkPtr temp = cosmoV_getTop(state, args); // grabs object from stack - CValue val; // to hold our value - - // sanity check - if (IS_REF(*temp)) { - // get the field from the object - if (!cosmoV_rawget(state, cosmoV_readRef(*temp), constants[ident], &val)) - return -1; - - // now invoke the method! - invokeMethod(state, cosmoV_readRef(*temp), val, args, nres, 1); - } else { - cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp)); - return -1; - } - - continue; - } - case OP_ITER: { - StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table - - if (!IS_REF(*temp)) { - cosmoV_error(state, "Couldn't iterate over non-iterator type %s!", cosmoV_typeStr(*temp)); - return -1; - } - - CObj *obj = cosmoV_readRef(*temp); - CObjObject *proto = cosmoO_grabProto(obj); - CValue val; - - if (proto != NULL) { - // grab __iter & call it - if (cosmoO_getIString(state, proto, ISTRING_ITER, &val)) { - cosmoV_pop(state); // pop the object from the stack - cosmoV_pushValue(state, val); - cosmoV_pushRef(state, (CObj*)obj); - if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // we expect 1 return value on the stack, the iterable object - return -1; - - StkPtr iObj = cosmoV_getTop(state, 0); - - if (!IS_OBJECT(*iObj)) { - cosmoV_error(state, "Expected iterable object! '__iter' returned %s, expected !", cosmoV_typeStr(*iObj)); - return -1; - } - - // get __next method and place it at the top of the stack - cosmoV_getMethod(state, cosmoV_readRef(*iObj), cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj); - } else { - cosmoV_error(state, "Expected iterable object! '__iter' not defined!"); - return -1; - } - } else if (obj->type == COBJ_TABLE) { - CObjTable *tbl = (CObjTable*)obj; - - cosmoV_pushRef(state, (CObj*)state->iStrings[ISTRING_RESERVED]); // key - cosmoV_pushRef(state, (CObj*)tbl); // value - - cosmoV_pushString(state, "__next"); // key - CObjCFunction *tbl_next = cosmoO_newCFunction(state, _tbl__next); - cosmoV_pushRef(state, (CObj*)tbl_next); // value - - CObjObject *obj = cosmoV_makeObject(state, 2); // pushes the new object to the stack - cosmoO_setUserI(obj, 0); // increment for iterator - - // make our CObjMethod for OP_NEXT to call - CObjMethod *method = cosmoO_newMethod(state, cosmoV_newRef(tbl_next), (CObj*)obj); - - cosmoV_setTop(state, 2); // pops the object & the tbl - cosmoV_pushRef(state, (CObj*)method); // pushes the method for OP_NEXT - } else { - cosmoV_error(state, "No proto defined! Couldn't get from type %s", cosmoO_typeStr(obj)); - return -1; - } - - continue; - } - case OP_NEXT: { - uint8_t nresults = READBYTE(); - uint16_t jump = READUINT(); - StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack - - if (!IS_METHOD(*temp)) { - cosmoV_error(state, "Expected '__next' to be a method, got type %s!", cosmoV_typeStr(*temp)); - return -1; - } - - cosmoV_pushValue(state, *temp); - if (cosmoV_call(state, 0, nresults) != COSMOVM_OK) - return -1; - - if (IS_NIL(*(cosmoV_getTop(state, 0)))) { // __next returned a nil, which means to exit the loop - cosmoV_setTop(state, nresults); // pop the return values - frame->pc += jump; - } - continue; - } - case OP_ADD: { // pop 2 values off the stack & try to add them together - NUMBEROP(cosmoV_newNumber, +); - continue; - } - case OP_SUB: { // pop 2 values off the stack & try to subtracts them - NUMBEROP(cosmoV_newNumber, -) - continue; - } - case OP_MULT: { // pop 2 values off the stack & try to multiplies them together - NUMBEROP(cosmoV_newNumber, *) - continue; - } - case OP_DIV: { // pop 2 values off the stack & try to divides them - NUMBEROP(cosmoV_newNumber, /) - continue; - } - case OP_MOD: { - StkPtr valA = cosmoV_getTop(state, 1); - StkPtr valB = cosmoV_getTop(state, 0); - if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { - cosmoV_setTop(state, 2); /* pop the 2 values */ - cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB)))); - } else { - cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); - return -1; - } - continue; - } - case OP_POW: { - StkPtr valA = cosmoV_getTop(state, 1); - StkPtr valB = cosmoV_getTop(state, 0); - if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { - cosmoV_setTop(state, 2); /* pop the 2 values */ - cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB)))); - } else { - cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); - return -1; - } - continue; - } - case OP_NOT: { - cosmoV_pushBoolean(state, isFalsey(cosmoV_pop(state))); - continue; - } - case OP_NEGATE: { // pop 1 value off the stack & try to negate - StkPtr val = cosmoV_getTop(state, 0); - - if (IS_NUMBER(*val)) { - cosmoV_pop(state); - cosmoV_pushNumber(state, -(cosmoV_readNumber(*val))); - } else { - cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); - return -1; - } - continue; - } - case OP_COUNT: { - StkPtr temp = cosmoV_getTop(state, 0); - - if (!IS_REF(*temp)) { - cosmoV_error(state, "Expected non-primitive, got %s!", cosmoV_typeStr(*temp)); - return -1; - } - - int count = cosmoO_count(state, cosmoV_readRef(*temp)); - cosmoV_pop(state); - - cosmoV_pushNumber(state, count); // pushes the count onto the stack - continue; - } - case OP_CONCAT: { - uint8_t vals = READBYTE(); - cosmoV_concat(state, vals); - continue; - } - case OP_INCLOCAL: { // this leaves the value on the stack - int8_t inc = READBYTE() - 128; // amount we're incrementing by - uint8_t indx = READBYTE(); - StkPtr val = &frame->base[indx]; - - // check that it's a number value - if (IS_NUMBER(*val)) { - cosmoV_pushValue(state, *val); // pushes old value onto the stack :) - *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); - } else { - cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); - return -1; - } - - continue; - } - case OP_INCGLOBAL: { - int8_t inc = READBYTE() - 128; // amount we're incrementing by - uint16_t indx = READUINT(); - CValue ident = constants[indx]; // grabs identifier - CValue *val = cosmoT_insert(state, &state->globals->tbl, ident); - - // check that it's a number value - if (IS_NUMBER(*val)) { - cosmoV_pushValue(state, *val); // pushes old value onto the stack :) - *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); - } else { - cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); - return -1; - } - - continue; - } - case OP_INCUPVAL: { - int8_t inc = READBYTE() - 128; // amount we're incrementing by - uint8_t indx = READBYTE(); - CValue *val = frame->closure->upvalues[indx]->val; - - // check that it's a number value - if (IS_NUMBER(*val)) { - cosmoV_pushValue(state, *val); // pushes old value onto the stack :) - *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); - } else { - cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); - return -1; - } - - continue; - } - case OP_INCINDEX: { - int8_t inc = READBYTE() - 128; // amount we're incrementing by - StkPtr temp = cosmoV_getTop(state, 1); // object should be above the key - StkPtr key = cosmoV_getTop(state, 0); // grabs key - - if (!IS_REF(*temp)) { - cosmoV_error(state, "Couldn't index non-indexable type %s!", cosmoV_typeStr(*temp)); - return -1; - } - - CObj *obj = cosmoV_readRef(*temp); - CObjObject *proto = cosmoO_grabProto(obj); - CValue val; - - // call __index if the proto was found - if (proto != NULL) { - if (cosmoO_indexObject(state, proto, *key, &val)) { - if (!IS_NUMBER(val)) { - cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); - return -1; - } - - cosmoV_pushValue(state, val); // pushes old value onto the stack :) - - // call __newindex - if (!cosmoO_newIndexObject(state, proto, *key, cosmoV_newNumber(cosmoV_readNumber(val) + inc))) - return -1; - } else - return -1; // cosmoO_indexObject failed and threw an error - } else if (obj->type == COBJ_TABLE) { - CObjTable *tbl = (CObjTable*)obj; - CValue *val = cosmoT_insert(state, &tbl->tbl, *key); - - if (!IS_NUMBER(*val)) { - cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); - return -1; - } - - // pops tbl & key from stack - cosmoV_setTop(state, 2); - cosmoV_pushValue(state, *val); // pushes old value onto the stack :) - *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index - } else { - cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); - return -1; - } - - continue; - } - case OP_INCOBJECT: { - int8_t inc = READBYTE() - 128; // amount we're incrementing by - uint16_t indx = READUINT(); - StkPtr temp = cosmoV_getTop(state, 0); // object should be at the top of the stack - CValue ident = constants[indx]; // grabs identifier - - // sanity check - if (IS_REF(*temp)) { - CObj *obj = cosmoV_readRef(*temp); - CValue val; - - if (!cosmoV_rawget(state, obj, ident, &val)) - return -1; - - // pop the object off the stack - cosmoV_pop(state); - - // check that it's a number value - if (IS_NUMBER(val)) { - cosmoV_pushValue(state, val); // pushes old value onto the stack :) - if (!cosmoV_rawset(state, obj, ident, cosmoV_newNumber(cosmoV_readNumber(val) + inc))) - return -1; - } else { + // call __index if the proto was found + if (proto != NULL) { + if (cosmoO_indexObject(state, proto, *key, &val)) { + if (!IS_NUMBER(val)) { cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); return -1; } - } else { - cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp)); + + cosmoV_pushValue(state, val); // pushes old value onto the stack :) + + // call __newindex + if (!cosmoO_newIndexObject(state, proto, *key, + cosmoV_newNumber(cosmoV_readNumber(val) + inc))) + return -1; + } else + return -1; // cosmoO_indexObject failed and threw an error + } else if (obj->type == COBJ_TABLE) { + CObjTable *tbl = (CObjTable *)obj; + CValue *val = cosmoT_insert(state, &tbl->tbl, *key); + + if (!IS_NUMBER(*val)) { + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); return -1; } - continue; + // pops tbl & key from stack + cosmoV_setTop(state, 2); + cosmoV_pushValue(state, *val); // pushes old value onto the stack :) + *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index + } else { + cosmoV_error(state, "No proto defined! Couldn't __index from type %s", + cosmoV_typeStr(*temp)); + return -1; } - case OP_EQUAL: { - // pop vals - StkPtr valB = cosmoV_pop(state); - StkPtr valA = cosmoV_pop(state); - // compare & push - cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB)); - continue; + continue; + } + case OP_INCOBJECT: { + int8_t inc = READBYTE() - 128; // amount we're incrementing by + uint16_t indx = READUINT(); + StkPtr temp = cosmoV_getTop(state, 0); // object should be at the top of the stack + CValue ident = constants[indx]; // grabs identifier + + // sanity check + if (IS_REF(*temp)) { + CObj *obj = cosmoV_readRef(*temp); + CValue val; + + if (!cosmoV_rawget(state, obj, ident, &val)) + return -1; + + // pop the object off the stack + cosmoV_pop(state); + + // check that it's a number value + if (IS_NUMBER(val)) { + cosmoV_pushValue(state, val); // pushes old value onto the stack :) + if (!cosmoV_rawset(state, obj, ident, + cosmoV_newNumber(cosmoV_readNumber(val) + inc))) + return -1; + } else { + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); + return -1; + } + } else { + cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp)); + return -1; } - case OP_GREATER: { - NUMBEROP(cosmoV_newBoolean, >) - continue; - } - case OP_LESS: { - NUMBEROP(cosmoV_newBoolean, <) - continue; - } - case OP_GREATER_EQUAL: { - NUMBEROP(cosmoV_newBoolean, >=) - continue; - } - case OP_LESS_EQUAL: { - NUMBEROP(cosmoV_newBoolean, <=) - continue; - } - case OP_TRUE: cosmoV_pushBoolean(state, true); continue; - case OP_FALSE: cosmoV_pushBoolean(state, false); continue; - case OP_NIL: cosmoV_pushValue(state, cosmoV_newNil()); continue; - case OP_RETURN: { - uint8_t res = READBYTE(); - return res; - } - default: - CERROR("unknown opcode!"); - exit(0); + + continue; + } + case OP_EQUAL: { + // pop vals + StkPtr valB = cosmoV_pop(state); + StkPtr valA = cosmoV_pop(state); + + // compare & push + cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB)); + continue; + } + case OP_GREATER: { + NUMBEROP(cosmoV_newBoolean, >) + continue; + } + case OP_LESS: { + NUMBEROP(cosmoV_newBoolean, <) + continue; + } + case OP_GREATER_EQUAL: { + NUMBEROP(cosmoV_newBoolean, >=) + continue; + } + case OP_LESS_EQUAL: { + NUMBEROP(cosmoV_newBoolean, <=) + continue; + } + case OP_TRUE: + cosmoV_pushBoolean(state, true); + continue; + case OP_FALSE: + cosmoV_pushBoolean(state, false); + continue; + case OP_NIL: + cosmoV_pushValue(state, cosmoV_newNil()); + continue; + case OP_RETURN: { + uint8_t res = READBYTE(); + return res; + } + default: + CERROR("unknown opcode!"); + exit(0); } } diff --git a/src/cvm.h b/src/cvm.h index a3b0595..6e1fe52 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -1,14 +1,15 @@ #ifndef COSMOVM_H #define COSMOVM_H -#include - #include "cosmo.h" #include "cstate.h" -//#define VM_DEBUG +#include -typedef enum { +// #define VM_DEBUG + +typedef enum +{ COSMOVM_OK, COSMOVM_RUNTIME_ERR, COSMOVM_BUILDTIME_ERR @@ -19,25 +20,26 @@ COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args, int nresults); COSMO_API COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults); // pushes new object onto the stack & returns a pointer to the new object -COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs); +COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs); COSMO_API void cosmoV_makeTable(CState *state, int pairs); COSMO_API void cosmoV_concat(CState *state, int vals); COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...); COSMO_API void cosmoV_printError(CState *state, CObjError *err); -COSMO_API CObjError* cosmoV_throw(CState *state); +COSMO_API CObjError *cosmoV_throw(CState *state); COSMO_API void cosmoV_error(CState *state, const char *format, ...); COSMO_API void cosmo_insert(CState *state, int indx, CValue val); /* - Sets the default proto objects for the passed objType. Also walks through the object heap and updates protos - for the passed objType if that CObj* has no proto. + Sets the default proto objects for the passed objType. Also walks through the object heap and + updates protos for the passed objType if that CObj* has no proto. returns true if replacing a previously registered proto object for this type */ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj); /* - compiles string into a , if successful, will be pushed onto the stack otherwise the will be pushed. + compiles string into a , if successful, will be pushed onto the stack + otherwise the will be pushed. returns: false : is at the top of the stack @@ -46,30 +48,34 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name); /* - expects object to be pushed, then the key. - - returns false if an error was thrown, returns true if the value was pushed onto the stack and the object and key were popped + expects object to be pushed, then the key. + + returns false if an error was thrown, returns true if the value was pushed onto the stack and + the object and key were popped */ COSMO_API bool cosmoV_get(CState *state); /* - expects object to be pushed, then the key, and finally the new value. - - returns false if an error was thrown, returns true if the value was set and the object key, and value were popped + expects object to be pushed, then the key, and finally the new value. + + returns false if an error was thrown, returns true if the value was set and the object key, and + value were popped */ COSMO_API bool cosmoV_set(CState *state); -// wraps the closure into a CObjMethod, so the function is called as an invoked method +// wraps the closure into a CObjMethod, so the function is called as an invoked method COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val); -// clears the stack, callstack and restores the state into a usable state after a calloverflow or another hard to recover error -// (keeps the global table intact) +// clears the stack, callstack and restores the state into a usable state after a calloverflow or +// another hard to recover error (keeps the global table intact) COSMO_API bool cosmoV_restore(CState *state); // nice to have wrappers -// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the SAFE_STACK macro on) -static inline void cosmoV_pushValue(CState *state, CValue val) { +// pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the +// SAFE_STACK macro on) +static inline void cosmoV_pushValue(CState *state, CValue val) +{ #ifdef SAFE_STACK ptrdiff_t stackSize = state->top - state->stack; @@ -78,7 +84,7 @@ static inline void cosmoV_pushValue(CState *state, CValue val) { if (state->panic) { // we're in a panic state, let the 8 reserved slots be filled if (stackSize < STACK_MAX) *(state->top++) = val; - + return; } @@ -91,51 +97,61 @@ static inline void cosmoV_pushValue(CState *state, CValue val) { } // sets stack->top to stack->top - indx -static inline StkPtr cosmoV_setTop(CState *state, int indx) { +static inline StkPtr cosmoV_setTop(CState *state, int indx) +{ state->top -= indx; return state->top; } // returns stack->top - indx - 1 -static inline StkPtr cosmoV_getTop(CState *state, int indx) { +static inline StkPtr cosmoV_getTop(CState *state, int indx) +{ return &state->top[-(indx + 1)]; } // pops 1 value off the stack, returns the popped value -static inline StkPtr cosmoV_pop(CState *state) { +static inline StkPtr cosmoV_pop(CState *state) +{ return cosmoV_setTop(state, 1); } // pushes a cosmo_Number to the stack -static inline void cosmoV_pushNumber(CState *state, cosmo_Number num) { +static inline void cosmoV_pushNumber(CState *state, cosmo_Number num) +{ cosmoV_pushValue(state, cosmoV_newNumber(num)); } // pushes a boolean to the stack -static inline void cosmoV_pushBoolean(CState *state, bool b) { +static inline void cosmoV_pushBoolean(CState *state, bool b) +{ cosmoV_pushValue(state, cosmoV_newBoolean(b)); } -static inline void cosmoV_pushRef(CState *state, CObj *obj) { +static inline void cosmoV_pushRef(CState *state, CObj *obj) +{ cosmoV_pushValue(state, cosmoV_newRef(obj)); } // pushes a C Function to the stack -static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) { - cosmoV_pushRef(state, (CObj*)cosmoO_newCFunction(state, func)); +static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) +{ + cosmoV_pushRef(state, (CObj *)cosmoO_newCFunction(state, func)); } // len is the length of the string without the NULL terminator -static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) { - cosmoV_pushRef(state, (CObj*)cosmoO_copyString(state, str, len)); +static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) +{ + cosmoV_pushRef(state, (CObj *)cosmoO_copyString(state, str, len)); } // accepts a null terminated string and copys the buffer to the VM heap -static inline void cosmoV_pushString(CState *state, const char *str) { +static inline void cosmoV_pushString(CState *state, const char *str) +{ cosmoV_pushLString(state, str, strlen(str)); } -static inline void cosmoV_pushNil(CState *state) { +static inline void cosmoV_pushNil(CState *state) +{ cosmoV_pushValue(state, cosmoV_newNil()); }