mirror of
				https://github.com/CPunch/Cosmo.git
				synced 2025-10-31 04:50:12 +00:00 
			
		
		
		
	added clang-format
This commit is contained in:
		
							
								
								
									
										26
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | ... | ||||||
|  |  | ||||||
| @@ -9,7 +9,7 @@ proto Test | |||||||
| end | end | ||||||
|  |  | ||||||
| // stressing the GC | // stressing the GC | ||||||
| for (var i = 0; ; i++) do | for (var i = 0; i < 100000; i++) do | ||||||
|     var x = Test("Hello world " .. i) |     var x = Test("Hello world " .. i) | ||||||
|     x:print() |     x:print() | ||||||
| end | end | ||||||
							
								
								
									
										303
									
								
								src/cbaselib.c
									
									
									
									
									
								
							
							
						
						
									
										303
									
								
								src/cbaselib.c
									
									
									
									
									
								
							| @@ -1,15 +1,18 @@ | |||||||
| #include "cbaselib.h" | #include "cbaselib.h" | ||||||
| #include "cvm.h" |  | ||||||
| #include "cvalue.h" |  | ||||||
| #include "cobj.h" |  | ||||||
| #include "cmem.h" | #include "cmem.h" | ||||||
|  | #include "cobj.h" | ||||||
|  | #include "cvalue.h" | ||||||
|  | #include "cvm.h" | ||||||
|  |  | ||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <sys/time.h> | #include <sys/time.h> | ||||||
|  |  | ||||||
| // ================================================================ [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++) { |     for (int i = 0; i < nargs; i++) { | ||||||
|         if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString |         if (IS_REF(args[i])) { // if its a CObj*, generate the CObjString | ||||||
|             CObjString *str = cosmoV_toString(state, args[i]); |             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 |     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) { |     if (nargs < 1 || nargs > 2) { | ||||||
|         cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs); |         cosmoV_error(state, "assert() expected 1 or 2 arguments, got %d!", nargs); | ||||||
|         return 0; // nothing pushed onto the stack to return |         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 (!IS_BOOLEAN(args[0]) || (nargs == 2 && !IS_STRING(args[1]))) { | ||||||
|         if (nargs == 2) { |         if (nargs == 2) { | ||||||
|             cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); |             cosmoV_typeError(state, "assert()", "<boolean>, <string>", "%s, %s", | ||||||
|  |                              cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|         } else { |         } else { | ||||||
|             cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0])); |             cosmoV_typeError(state, "assert()", "<boolean>", "%s", cosmoV_typeStr(args[0])); | ||||||
|         } |         } | ||||||
| @@ -44,7 +49,8 @@ int cosmoB_assert(CState *state, int nargs, CValue *args) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_type(CState *state, int nargs, CValue *args) { | int cosmoB_type(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "type() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "type() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -55,7 +61,8 @@ int cosmoB_type(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; // 1 return value, the type string :D |     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) { |     if (nargs < 1) { | ||||||
|         cosmoV_error(state, "pcall() expected at least 1 argument!"); |         cosmoV_error(state, "pcall() expected at least 1 argument!"); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -75,7 +82,8 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) { | |||||||
|     return 2; |     return 2; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_tonumber(CState *state, int nargs, CValue *args) { | int cosmoB_tonumber(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "tonumber() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -85,7 +93,8 @@ int cosmoB_tonumber(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_tostring(CState *state, int nargs, CValue *args) { | int cosmoB_tostring(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "tostring() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -95,7 +104,8 @@ int cosmoB_tostring(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_loadstring(CState *state, int nargs, CValue *args) { | int cosmoB_loadstring(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -113,7 +123,8 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args) { | |||||||
|     return 2; // <boolean>, <closure> or <error> |     return 2; // <boolean>, <closure> or <error> | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_error(CState *state, int nargs, CValue *args) { | int cosmoB_error(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "error() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "error() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -129,28 +140,13 @@ int cosmoB_error(CState *state, int nargs, CValue *args) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoB_loadLibrary(CState *state) { | void cosmoB_loadLibrary(CState *state) | ||||||
|     const char *identifiers[] = { | { | ||||||
|         "print", |     const char *identifiers[] = {"print",    "assert",   "type",       "pcall", | ||||||
|         "assert", |                                  "tonumber", "tostring", "loadstring", "error"}; | ||||||
|         "type", |  | ||||||
|         "pcall", |  | ||||||
|         "tonumber", |  | ||||||
|         "tostring", |  | ||||||
|         "loadstring", |  | ||||||
|         "error" |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     CosmoCFunction baseLib[] = { |     CosmoCFunction baseLib[] = {cosmoB_print,    cosmoB_assert,   cosmoB_type,       cosmoB_pcall, | ||||||
|         cosmoB_print, |                                 cosmoB_tonumber, cosmoB_tostring, cosmoB_loadstring, cosmoB_error}; | ||||||
|         cosmoB_assert, |  | ||||||
|         cosmoB_type, |  | ||||||
|         cosmoB_pcall, |  | ||||||
|         cosmoB_tonumber, |  | ||||||
|         cosmoB_tostring, |  | ||||||
|         cosmoB_loadstring, |  | ||||||
|         cosmoB_error |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     int i; |     int i; | ||||||
|     for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) { |     for (i = 0; i < sizeof(identifiers) / sizeof(identifiers[0]); i++) { | ||||||
| @@ -167,9 +163,11 @@ void cosmoB_loadLibrary(CState *state) { | |||||||
|     cosmoB_loadMathLib(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) { |     if (nargs == 2) { | ||||||
|         CObj *obj = cosmoV_readRef(args[0]); // object to set proto too |         CObj *obj = cosmoV_readRef(args[0]); // object to set proto too | ||||||
|         CObjObject *proto = cosmoV_readObject(args[1]); |         CObjObject *proto = cosmoV_readObject(args[1]); | ||||||
| @@ -182,7 +180,8 @@ int cosmoB_osetProto(CState *state, int nargs, CValue *args) { | |||||||
|     return 0; // nothing |     return 0; // nothing | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_ogetProto(CState *state, int nargs, CValue *args) { | int cosmoB_ogetProto(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "Expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "Expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -193,14 +192,16 @@ int cosmoB_ogetProto(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; // 1 result |     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) { |     if (nargs != 2) { | ||||||
|         cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs); |         cosmoV_error(state, "object.ischild() expected 2 arguments, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) { |     if (!IS_REF(args[0]) || !IS_OBJECT(args[1])) { | ||||||
|         cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); |         cosmoV_typeError(state, "object.ischild()", "<reference obj>, <object>", "%s, %s", | ||||||
|  |                          cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -212,14 +213,11 @@ int cosmoB_oisChild(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| COSMO_API void cosmoB_loadObjLib(CState *state) { | COSMO_API void cosmoB_loadObjLib(CState *state) | ||||||
|     const char *identifiers[] = { | { | ||||||
|         "ischild" |     const char *identifiers[] = {"ischild"}; | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     CosmoCFunction objLib[] = { |     CosmoCFunction objLib[] = {cosmoB_oisChild}; | ||||||
|         cosmoB_oisChild |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     // make object library object |     // make object library object | ||||||
|     cosmoV_pushString(state, "object"); |     cosmoV_pushString(state, "object"); | ||||||
| @@ -256,10 +254,12 @@ COSMO_API void cosmoB_loadObjLib(CState *state) { | |||||||
|     cosmoV_register(state, 1); |     cosmoV_register(state, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ================================================================ [OS.*] ================================================================ | // ================================================================ [OS.*] | ||||||
|  | // ================================================================ | ||||||
|  |  | ||||||
| // os.read() | // os.read() | ||||||
| int cosmoB_osRead(CState *state, int nargs, CValue *args) { | int cosmoB_osRead(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "os.read() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -303,7 +303,8 @@ int cosmoB_osRead(CState *state, int nargs, CValue *args) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // os.time() | // os.time() | ||||||
| int cosmoB_osTime(CState *state, int nargs, CValue *args) { | int cosmoB_osTime(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     struct timeval time; |     struct timeval time; | ||||||
|     if (nargs > 0) { |     if (nargs > 0) { | ||||||
|         cosmoV_error(state, "os.time() expected no arguments, got %d!", nargs); |         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() | // os.system() | ||||||
| int cosmoB_osSystem(CState *state, int nargs, CValue *args) { | int cosmoB_osSystem(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs); |         cosmoV_error(state, "os.system() expects 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -332,18 +334,11 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| COSMO_API void cosmoB_loadOSLib(CState *state) { | COSMO_API void cosmoB_loadOSLib(CState *state) | ||||||
|     const char *identifiers[] = { | { | ||||||
|         "read", |     const char *identifiers[] = {"read", "time", "system"}; | ||||||
|         "time", |  | ||||||
|         "system" |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     CosmoCFunction osLib[] = { |     CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem}; | ||||||
|         cosmoB_osRead, |  | ||||||
|         cosmoB_osTime, |  | ||||||
|         cosmoB_osSystem |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     cosmoV_pushString(state, "os"); |     cosmoV_pushString(state, "os"); | ||||||
|  |  | ||||||
| @@ -357,13 +352,16 @@ COSMO_API void cosmoB_loadOSLib(CState *state) { | |||||||
|     cosmoV_register(state, 1); // register the os.* object to the global table |     cosmoV_register(state, 1); // register the os.* object to the global table | ||||||
| } | } | ||||||
|  |  | ||||||
| // ================================================================ [STRING.*] ================================================================ | // ================================================================ [STRING.*] | ||||||
|  | // ================================================================ | ||||||
|  |  | ||||||
| // string.sub | // string.sub | ||||||
| int cosmoB_sSub(CState *state, int nargs, CValue *args) { | int cosmoB_sSub(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs == 2) { |     if (nargs == 2) { | ||||||
|         if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { |         if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { | ||||||
|             cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); |             cosmoV_typeError(state, "string.sub()", "<string>, <number>", "%s, %s", | ||||||
|  |                              cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -372,14 +370,17 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { | |||||||
|  |  | ||||||
|         // make sure we stay within memory |         // make sure we stay within memory | ||||||
|         if (indx < 0 || indx >= str->length) { |         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; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx)); |         cosmoV_pushLString(state, str->str + ((int)indx), str->length - ((int)indx)); | ||||||
|     } else if (nargs == 3) { |     } else if (nargs == 3) { | ||||||
|         if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) { |         if (!IS_STRING(args[0]) || !IS_NUMBER(args[1]) || !IS_NUMBER(args[2])) { | ||||||
|             cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); |             cosmoV_typeError(state, "string.sub()", "<string>, <number>, <number>", "%s, %s, %s", | ||||||
|  |                              cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), | ||||||
|  |                              cosmoV_typeStr(args[2])); | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -389,7 +390,9 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { | |||||||
|  |  | ||||||
|         // make sure we stay within memory |         // make sure we stay within memory | ||||||
|         if (indx + length < 0 || indx + length >= str->length || indx < 0 || indx >= str->length) { |         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; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -403,10 +406,12 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // string.find | // string.find | ||||||
| int cosmoB_sFind(CState *state, int nargs, CValue *args) { | int cosmoB_sFind(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs == 2) { |     if (nargs == 2) { | ||||||
|         if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { |         if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { | ||||||
|             cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); |             cosmoV_typeError(state, "string.find()", "<string>, <string>", "%s, %s", | ||||||
|  |                              cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -425,7 +430,9 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) { | |||||||
|         cosmoV_pushNumber(state, indx - str->str); |         cosmoV_pushNumber(state, indx - str->str); | ||||||
|     } else if (nargs == 3) { |     } else if (nargs == 3) { | ||||||
|         if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) { |         if (!IS_STRING(args[0]) || !IS_STRING(args[1]) || !IS_NUMBER(args[2])) { | ||||||
|             cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); |             cosmoV_typeError(state, "string.find()", "<string>, <string>, <number>", "%s, %s, %s", | ||||||
|  |                              cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), | ||||||
|  |                              cosmoV_typeStr(args[2])); | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -452,14 +459,16 @@ int cosmoB_sFind(CState *state, int nargs, CValue *args) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // string.split | // string.split | ||||||
| int cosmoB_sSplit(CState *state, int nargs, CValue *args) { | int cosmoB_sSplit(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 2) { |     if (nargs != 2) { | ||||||
|         cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs); |         cosmoV_error(state, "string.split() expected 2 arguments, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { |     if (!IS_STRING(args[0]) || !IS_STRING(args[1])) { | ||||||
|         cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); |         cosmoV_typeError(state, "string.split()", "<string>, <string>", "%s, %s", | ||||||
|  |                          cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -475,7 +484,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) { | |||||||
|         nIndx = strstr(indx, ptrn->str); |         nIndx = strstr(indx, ptrn->str); | ||||||
|  |  | ||||||
|         cosmoV_pushNumber(state, nEntries++); |         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; |         indx = nIndx + ptrn->length; | ||||||
|     } while (nIndx != NULL); |     } while (nIndx != NULL); | ||||||
| @@ -486,7 +496,8 @@ int cosmoB_sSplit(CState *state, int nargs, CValue *args) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // string.byte | // string.byte | ||||||
| int cosmoB_sByte(CState *state, int nargs, CValue *args) { | int cosmoB_sByte(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "string.byte() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -500,8 +511,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) { | |||||||
|     CObjString *str = cosmoV_readString(args[0]); |     CObjString *str = cosmoV_readString(args[0]); | ||||||
|  |  | ||||||
|     if (str->length < 1) { |     if (str->length < 1) { | ||||||
|         // the length of the string is less than 1, in the future I might throw an error for this, but  |         // the length of the string is less than 1, in the future I might throw an error for this, | ||||||
|         // for now im going to copy lua and just return a nil |         // but for now im going to copy lua and just return a nil | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -511,7 +522,8 @@ int cosmoB_sByte(CState *state, int nargs, CValue *args) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // string.char | // string.char | ||||||
| int cosmoB_sChar(CState *state, int nargs, CValue *args) { | int cosmoB_sChar(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "string.char() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -522,7 +534,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) { | |||||||
|         return 0; |         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]); |     int num = (int)cosmoV_readNumber(args[0]); | ||||||
|     char c = num; |     char c = num; | ||||||
|  |  | ||||||
| @@ -536,7 +549,8 @@ int cosmoB_sChar(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_sLen(CState *state, int nargs, CValue *args) { | int cosmoB_sLen(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs < 1) { |     if (nargs < 1) { | ||||||
|         cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "string.len() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -552,7 +566,8 @@ int cosmoB_sLen(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_sRep(CState *state, int nargs, CValue *args) { | int cosmoB_sRep(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 2) { |     if (nargs != 2) { | ||||||
|         cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs); |         cosmoV_error(state, "string.rep() expected 2 arguments, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -560,7 +575,8 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) { | |||||||
|  |  | ||||||
|     // expects <string>, <number> |     // expects <string>, <number> | ||||||
|     if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { |     if (!IS_STRING(args[0]) || !IS_NUMBER(args[1])) { | ||||||
|         cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); |         cosmoV_typeError(state, "string.rep", "<string>, <number>", "%s, %s", | ||||||
|  |                          cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -588,26 +604,12 @@ int cosmoB_sRep(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoB_loadStrLib(CState *state) { | void cosmoB_loadStrLib(CState *state) | ||||||
|     const char *identifiers[] = { | { | ||||||
|         "sub", |     const char *identifiers[] = {"sub", "find", "split", "byte", "char", "len", "rep"}; | ||||||
|         "find", |  | ||||||
|         "split", |  | ||||||
|         "byte", |  | ||||||
|         "char", |  | ||||||
|         "len", |  | ||||||
|         "rep" |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     CosmoCFunction strLib[] = { |     CosmoCFunction strLib[] = {cosmoB_sSub,  cosmoB_sFind, cosmoB_sSplit, cosmoB_sByte, | ||||||
|         cosmoB_sSub, |                                cosmoB_sChar, cosmoB_sLen,  cosmoB_sRep}; | ||||||
|         cosmoB_sFind, |  | ||||||
|         cosmoB_sSplit, |  | ||||||
|         cosmoB_sByte, |  | ||||||
|         cosmoB_sChar, |  | ||||||
|         cosmoB_sLen, |  | ||||||
|         cosmoB_sRep |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     // make string library object |     // make string library object | ||||||
|     cosmoV_pushString(state, "string"); |     cosmoV_pushString(state, "string"); | ||||||
| @@ -626,10 +628,12 @@ void cosmoB_loadStrLib(CState *state) { | |||||||
|     cosmoV_register(state, 1); |     cosmoV_register(state, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ================================================================ [MATH] ================================================================ | // ================================================================ [MATH] | ||||||
|  | // ================================================================ | ||||||
|  |  | ||||||
| // math.abs | // math.abs | ||||||
| int cosmoB_mAbs(CState *state, int nargs, CValue *args) { | int cosmoB_mAbs(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.abs() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -645,7 +649,8 @@ int cosmoB_mAbs(CState *state, int nargs, CValue *args) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // math.floor | // math.floor | ||||||
| int cosmoB_mFloor(CState *state, int nargs, CValue *args) { | int cosmoB_mFloor(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.floor() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -661,7 +666,8 @@ int cosmoB_mFloor(CState *state, int nargs, CValue *args) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // math.ceil | // math.ceil | ||||||
| int cosmoB_mCeil(CState *state, int nargs, CValue *args) { | int cosmoB_mCeil(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.ceil() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -684,7 +690,8 @@ int cosmoB_mCeil(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_mSin(CState *state, int nargs, CValue *args) { | int cosmoB_mSin(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.sin() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -699,7 +706,8 @@ int cosmoB_mSin(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_mCos(CState *state, int nargs, CValue *args) { | int cosmoB_mCos(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.cos() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -714,7 +722,8 @@ int cosmoB_mCos(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_mTan(CState *state, int nargs, CValue *args) { | int cosmoB_mTan(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.tan() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -729,7 +738,8 @@ int cosmoB_mTan(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_mASin(CState *state, int nargs, CValue *args) { | int cosmoB_mASin(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.asin() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -744,7 +754,8 @@ int cosmoB_mASin(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_mACos(CState *state, int nargs, CValue *args) { | int cosmoB_mACos(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.acos() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -759,7 +770,8 @@ int cosmoB_mACos(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_mATan(CState *state, int nargs, CValue *args) { | int cosmoB_mATan(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.atan() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -774,7 +786,8 @@ int cosmoB_mATan(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_mRad(CState *state, int nargs, CValue *args) { | int cosmoB_mRad(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.rad() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -790,7 +803,8 @@ int cosmoB_mRad(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_mDeg(CState *state, int nargs, CValue *args) { | int cosmoB_mDeg(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs); |         cosmoV_error(state, "math.deg() expected 1 argument, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -806,34 +820,14 @@ int cosmoB_mDeg(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoB_loadMathLib(CState *state) { | void cosmoB_loadMathLib(CState *state) | ||||||
|     const char *identifiers[] = { | { | ||||||
|         "abs", |     const char *identifiers[] = {"abs",  "floor", "ceil", "sin", "cos", "tan", | ||||||
|         "floor", |                                  "asin", "acos",  "atan", "rad", "deg"}; | ||||||
|         "ceil", |  | ||||||
|         "sin", |  | ||||||
|         "cos", |  | ||||||
|         "tan", |  | ||||||
|         "asin", |  | ||||||
|         "acos", |  | ||||||
|         "atan", |  | ||||||
|         "rad", |  | ||||||
|         "deg" |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     CosmoCFunction mathLib[] = { |     CosmoCFunction mathLib[] = {cosmoB_mAbs,  cosmoB_mFloor, cosmoB_mCeil, cosmoB_mSin, | ||||||
|         cosmoB_mAbs, |                                 cosmoB_mCos,  cosmoB_mTan,   cosmoB_mASin, cosmoB_mACos, | ||||||
|         cosmoB_mFloor, |                                 cosmoB_mATan, cosmoB_mRad,   cosmoB_mDeg}; | ||||||
|         cosmoB_mCeil, |  | ||||||
|         cosmoB_mSin, |  | ||||||
|         cosmoB_mCos, |  | ||||||
|         cosmoB_mTan, |  | ||||||
|         cosmoB_mASin, |  | ||||||
|         cosmoB_mACos, |  | ||||||
|         cosmoB_mATan, |  | ||||||
|         cosmoB_mRad, |  | ||||||
|         cosmoB_mDeg |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     // make math library object |     // make math library object | ||||||
|     cosmoV_pushString(state, "math"); |     cosmoV_pushString(state, "math"); | ||||||
| @@ -852,24 +846,28 @@ void cosmoB_loadMathLib(CState *state) { | |||||||
|     cosmoV_register(state, 1); |     cosmoV_register(state, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| // ================================================================ [VM.*] ================================================================ | // ================================================================ [VM.*] | ||||||
|  | // ================================================================ | ||||||
|  |  | ||||||
| // vm.__getter["globals"] | // 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 |     // 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; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
| // vm.__setter["globals"] | // vm.__setter["globals"] | ||||||
| int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) { | int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 2) { |     if (nargs != 2) { | ||||||
|         cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs); |         cosmoV_error(state, "Expected 2 argumenst, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!IS_TABLE(args[1])) { |     if (!IS_TABLE(args[1])) { | ||||||
|         cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); |         cosmoV_typeError(state, "vm.__setter[\"globals\"]", "<object>, <table>", "%s, %s", | ||||||
|  |                          cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -879,14 +877,16 @@ int cosmoB_vsetGlobal(CState *state, int nargs, CValue *args) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) { | int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 2) { |     if (nargs != 2) { | ||||||
|         cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); |         cosmoV_error(state, "Expected 2 arguments, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!IS_NUMBER(args[1])) { |     if (!IS_NUMBER(args[1])) { | ||||||
|         cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); |         cosmoV_typeError(state, "baseProtos.__index", "<object>, <number>", "%s, %s", | ||||||
|  |                          cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1])); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -905,14 +905,17 @@ int cosmoB_vindexBProto(CState *state, int nargs, CValue *args) { | |||||||
|     return 1; // 1 value pushed, 1 value returned |     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) { |     if (nargs != 3) { | ||||||
|         cosmoV_error(state, "Expected 3 arguments, got %d!", nargs); |         cosmoV_error(state, "Expected 3 arguments, got %d!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) { |     if (!IS_NUMBER(args[1]) || !IS_OBJECT(args[2])) { | ||||||
|         cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>", "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), cosmoV_typeStr(args[2])); |         cosmoV_typeError(state, "baseProtos.__newindex", "<object>, <number>, <object>", | ||||||
|  |                          "%s, %s, %s", cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]), | ||||||
|  |                          cosmoV_typeStr(args[2])); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -929,7 +932,8 @@ int cosmoB_vnewindexBProto(CState *state, int nargs, CValue *args) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // vm.collect() | // 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) |     // first, unfreeze the state (we start frozen on entry to any C Function) | ||||||
|     cosmoM_unfreezeGC(state); |     cosmoM_unfreezeGC(state); | ||||||
|  |  | ||||||
| @@ -943,7 +947,8 @@ int cosmoB_vcollect(CState *state, int nargs, CValue *args) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoB_loadVM(CState *state) { | void cosmoB_loadVM(CState *state) | ||||||
|  | { | ||||||
|     // make vm.* object |     // make vm.* object | ||||||
|     cosmoV_pushString(state, "vm"); |     cosmoV_pushString(state, "vm"); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -32,7 +32,8 @@ COSMO_API void cosmoB_loadOSLib(CState *state); | |||||||
|     - string.char & <string>:char() |     - string.char & <string>:char() | ||||||
|     - string.rep & <string>:rep() |     - string.rep & <string>: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", " ")` |         `"hello world":split(" ")` is equivalent to `string.split("hello world", " ")` | ||||||
| */ | */ | ||||||
| COSMO_API void cosmoB_loadStrLib(CState *state); | COSMO_API void cosmoB_loadStrLib(CState *state); | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								src/cchunk.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/cchunk.c
									
									
									
									
									
								
							| @@ -1,16 +1,19 @@ | |||||||
| #include "cmem.h" |  | ||||||
| #include "cchunk.h" | #include "cchunk.h" | ||||||
|  |  | ||||||
|  | #include "cmem.h" | ||||||
|  | #include "cobj.h" | ||||||
| #include "cvalue.h" | #include "cvalue.h" | ||||||
| #include "cvm.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)); |     CChunk *chunk = cosmoM_xmalloc(state, sizeof(CChunk)); | ||||||
|     initChunk(state, chunk, startCapacity); |     initChunk(state, chunk, startCapacity); | ||||||
|     return chunk; |     return chunk; | ||||||
| } | } | ||||||
|  |  | ||||||
| void initChunk(CState* state, CChunk *chunk, size_t startCapacity) { | void initChunk(CState *state, CChunk *chunk, size_t startCapacity) | ||||||
|  | { | ||||||
|     chunk->capacity = startCapacity; |     chunk->capacity = startCapacity; | ||||||
|     chunk->lineCapacity = startCapacity; |     chunk->lineCapacity = startCapacity; | ||||||
|     chunk->count = 0; |     chunk->count = 0; | ||||||
| @@ -21,7 +24,8 @@ void initChunk(CState* state, CChunk *chunk, size_t startCapacity) { | |||||||
|     initValArray(state, &chunk->constants, ARRAY_START); |     initValArray(state, &chunk->constants, ARRAY_START); | ||||||
| } | } | ||||||
|  |  | ||||||
| void cleanChunk(CState* state, CChunk *chunk) { | void cleanChunk(CState *state, CChunk *chunk) | ||||||
|  | { | ||||||
|     // first, free the chunk buffer |     // first, free the chunk buffer | ||||||
|     cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity); |     cosmoM_freearray(state, INSTRUCTION, chunk->buf, chunk->capacity); | ||||||
|     // then the line info |     // then the line info | ||||||
| @@ -30,13 +34,15 @@ void cleanChunk(CState* state, CChunk *chunk) { | |||||||
|     cleanValArray(state, &chunk->constants); |     cleanValArray(state, &chunk->constants); | ||||||
| } | } | ||||||
|  |  | ||||||
| void freeChunk(CState* state, CChunk *chunk) { | void freeChunk(CState *state, CChunk *chunk) | ||||||
|  | { | ||||||
|     cleanChunk(state, chunk); |     cleanChunk(state, chunk); | ||||||
|     // now, free the wrapper struct |     // now, free the wrapper struct | ||||||
|     cosmoM_free(state, CChunk, chunk); |     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 |     // before adding the constant, check if we already have it | ||||||
|     for (size_t i = 0; i < chunk->constants.count; i++) { |     for (size_t i = 0; i < chunk->constants.count; i++) { | ||||||
|         if (cosmoV_equal(state, value, chunk->constants.values[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 |     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? |     // does the buffer need to be reallocated? | ||||||
|     cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); |     cosmoM_growarray(state, INSTRUCTION, chunk->buf, chunk->count, chunk->capacity); | ||||||
|     cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); |     cosmoM_growarray(state, int, chunk->lineInfo, chunk->count, chunk->lineCapacity); | ||||||
| @@ -61,7 +69,8 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line) { | |||||||
|     chunk->buf[chunk->count++] = i; |     chunk->buf[chunk->count++] = i; | ||||||
| } | } | ||||||
|  |  | ||||||
| void writeu16Chunk(CState* state, CChunk *chunk, uint16_t i, int line) { | void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line) | ||||||
|  | { | ||||||
|     INSTRUCTION *buffer = (INSTRUCTION *)(&i); |     INSTRUCTION *buffer = (INSTRUCTION *)(&i); | ||||||
|     int sz = sizeof(uint16_t) / sizeof(INSTRUCTION); |     int sz = sizeof(uint16_t) / sizeof(INSTRUCTION); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								src/cchunk.h
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/cchunk.h
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | |||||||
| #ifndef CCHUNK_H | #ifndef CCHUNK_H | ||||||
| #define CCHUNK_H | #define CCHUNK_H | ||||||
|  |  | ||||||
| #include "cosmo.h" |  | ||||||
|  |  | ||||||
| #include "coperators.h" | #include "coperators.h" | ||||||
|  | #include "cosmo.h" | ||||||
| #include "cvalue.h" | #include "cvalue.h" | ||||||
|  |  | ||||||
| struct CChunk { | struct CChunk | ||||||
|  | { | ||||||
|     size_t capacity;       // the amount of space we've allocated for |     size_t capacity;       // the amount of space we've allocated for | ||||||
|     size_t count;          // the space we're currently using |     size_t count;          // the space we're currently using | ||||||
|     INSTRUCTION *buf;      // whole chunk |     INSTRUCTION *buf;      // whole chunk | ||||||
| @@ -26,11 +26,13 @@ void writeu8Chunk(CState* state, CChunk *chunk, INSTRUCTION i, int line); | |||||||
| void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line); | void writeu16Chunk(CState *state, CChunk *chunk, uint16_t i, int line); | ||||||
|  |  | ||||||
| // read from chunk | // read from chunk | ||||||
| static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) { | static inline INSTRUCTION readu8Chunk(CChunk *chunk, int offset) | ||||||
|  | { | ||||||
|     return chunk->buf[offset]; |     return chunk->buf[offset]; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline uint16_t readu16Chunk(CChunk *chunk, int offset) { | static inline uint16_t readu16Chunk(CChunk *chunk, int offset) | ||||||
|  | { | ||||||
|     return *((uint16_t *)(&chunk->buf[offset])); |     return *((uint16_t *)(&chunk->buf[offset])); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								src/cdebug.c
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								src/cdebug.c
									
									
									
									
									
								
							| @@ -1,49 +1,62 @@ | |||||||
| #include "cdebug.h" | #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++) |     for (int i = 0; i < indent; i++) | ||||||
|         printf("\t"); |         printf("\t"); | ||||||
| } | } | ||||||
|  |  | ||||||
| int simpleInstruction(const char *name, int offset) { | int simpleInstruction(const char *name, int offset) | ||||||
|  | { | ||||||
|     printf("%s", name); |     printf("%s", name); | ||||||
|     return offset + 1; // consume opcode |     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)); |     printf("%-16s [%03d]", name, readu8Chunk(chunk, offset + 1)); | ||||||
|     return offset + 2; |     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)); |     printf("%-16s [%05d]", name, readu16Chunk(chunk, offset + 1)); | ||||||
|     return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); |     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; |     int jmp = ((int)readu16Chunk(chunk, offset + 1)) * dir; | ||||||
|     printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp); |     printf("%-16s [%05d] - jumps to %04d", name, jmp, offset + 3 + jmp); | ||||||
|     return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); |     return offset + 1 + (sizeof(uint16_t) / sizeof(INSTRUCTION)); | ||||||
| } | } | ||||||
|  |  | ||||||
| int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) { | int u8u8OperandInstruction(const char *name, CChunk *chunk, int offset) | ||||||
|     printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), readu8Chunk(chunk, offset + 2)); | { | ||||||
|  |     printf("%-16s [%03d] [%03d]", name, readu8Chunk(chunk, offset + 1), | ||||||
|  |            readu8Chunk(chunk, offset + 2)); | ||||||
|     return offset + 3; // op + u8 + u8 |     return offset + 3; // op + u8 + u8 | ||||||
| } | } | ||||||
|  |  | ||||||
| int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) { | int u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) | ||||||
|     printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), readu16Chunk(chunk, offset + 2)); | { | ||||||
|  |     printf("%-16s [%03d] [%05d]", name, readu8Chunk(chunk, offset + 1), | ||||||
|  |            readu16Chunk(chunk, offset + 2)); | ||||||
|     return offset + 4; // op + u8 + u16 |     return offset + 4; // op + u8 + u16 | ||||||
| } | } | ||||||
|  |  | ||||||
| int u8u8u16OperandInstruction(const char *name, CChunk *chunk, int offset) { | 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)); | { | ||||||
|  |     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 |     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); |     int index = readu16Chunk(chunk, offset + 1); | ||||||
|     printf("%-16s [%05d] - ", name, index); |     printf("%-16s [%05d] - ", name, index); | ||||||
|     CValue val = chunk->constants.values[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 | // 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); |     printIndent(indent); | ||||||
|     printf("===[[ disasm for %s ]]===\n", name); |     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); |     printIndent(indent); | ||||||
|     printf("%04d ", offset); |     printf("%04d ", offset); | ||||||
|  |  | ||||||
| @@ -124,7 +139,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         // print the chunk |         // print the chunk | ||||||
|             disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, indent+1); |         disasmChunk(&cobjFunc->chunk, cobjFunc->name == NULL ? UNNAMEDCHUNK : cobjFunc->name->str, | ||||||
|  |                     indent + 1); | ||||||
|         return offset; |         return offset; | ||||||
|     } |     } | ||||||
|     case OP_CLOSE: |     case OP_CLOSE: | ||||||
| @@ -204,6 +220,5 @@ int disasmInstr(CChunk *chunk, int offset, int indent) { | |||||||
|         return 1; |         return 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										178
									
								
								src/clex.c
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								src/clex.c
									
									
									
									
									
								
							| @@ -1,4 +1,5 @@ | |||||||
| #include "clex.h" | #include "clex.h" | ||||||
|  |  | ||||||
| #include "cmem.h" | #include "cmem.h" | ||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
| @@ -29,44 +30,52 @@ CReservedWord reservedWords[] = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| // returns true if current token is a heap allocated buffer | // returns true if current token is a heap allocated buffer | ||||||
| static bool isBuffer(CLexState *state) { | static bool isBuffer(CLexState *state) | ||||||
|  | { | ||||||
|     return state->buffer != NULL; |     return state->buffer != NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| // marks the current token as heap allocated & allocates the buffer | // marks the current token as heap allocated & allocates the buffer | ||||||
| static void makeBuffer(CLexState *state) { | static void makeBuffer(CLexState *state) | ||||||
|     state->buffer = cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer | { | ||||||
|  |     state->buffer = | ||||||
|  |         cosmoM_xmalloc(state->cstate, sizeof(char) * 32); // start with a 32 character long buffer | ||||||
|     state->bufCount = 0; |     state->bufCount = 0; | ||||||
|     state->bufCap = 32; |     state->bufCap = 32; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void resetBuffer(CLexState *state) { | static void resetBuffer(CLexState *state) | ||||||
|  | { | ||||||
|     state->buffer = NULL; |     state->buffer = NULL; | ||||||
|     state->bufCount = 0; |     state->bufCount = 0; | ||||||
|     state->bufCap = 0; |     state->bufCap = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| // cancels the token heap buffer and frees it | // 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); |     cosmoM_freearray(state->cstate, char, state->buffer, state->bufCap); | ||||||
|  |  | ||||||
|     resetBuffer(state); |     resetBuffer(state); | ||||||
| } | } | ||||||
|  |  | ||||||
| // adds character to buffer | // 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); |     cosmoM_growarray(state->cstate, char, state->buffer, state->bufCount, state->bufCap); | ||||||
|  |  | ||||||
|     state->buffer[state->bufCount++] = c; |     state->buffer[state->bufCount++] = c; | ||||||
| } | } | ||||||
|  |  | ||||||
| // saves the current character to the buffer, grows the buffer as needed | // 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); |     appendBuffer(state, *state->currentChar); | ||||||
| } | } | ||||||
|  |  | ||||||
| // resets the lex state buffer & returns the allocated buffer as a null terminated string | // 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 |     // append the null terminator | ||||||
|     appendBuffer(state, '\0'); |     appendBuffer(state, '\0'); | ||||||
|  |  | ||||||
| @@ -84,7 +93,8 @@ static char *cutBuffer(CLexState *state, int *length) { | |||||||
|     return cosmoM_reallocate(state->cstate, buf, cap, count); |     return cosmoM_reallocate(state->cstate, buf, cap, count); | ||||||
| } | } | ||||||
|  |  | ||||||
| static CToken makeToken(CLexState *state, CTokenType type) { | static CToken makeToken(CLexState *state, CTokenType type) | ||||||
|  | { | ||||||
|     CToken token; |     CToken token; | ||||||
|     token.type = type; |     token.type = type; | ||||||
|     token.line = state->line; |     token.line = state->line; | ||||||
| @@ -101,7 +111,8 @@ static CToken makeToken(CLexState *state, CTokenType type) { | |||||||
|     return token; |     return token; | ||||||
| } | } | ||||||
|  |  | ||||||
| static CToken makeError(CLexState *state, const char *msg) { | static CToken makeError(CLexState *state, const char *msg) | ||||||
|  | { | ||||||
|     CToken token; |     CToken token; | ||||||
|     token.type = TOKEN_ERROR; |     token.type = TOKEN_ERROR; | ||||||
|     token.start = (char *)msg; |     token.start = (char *)msg; | ||||||
| @@ -114,19 +125,23 @@ static CToken makeError(CLexState *state, const char *msg) { | |||||||
|     return token; |     return token; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline bool isEnd(CLexState *state) { | static inline bool isEnd(CLexState *state) | ||||||
|  | { | ||||||
|     return *state->currentChar == '\0'; |     return *state->currentChar == '\0'; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline bool isNumerical(char c) { | static inline bool isNumerical(char c) | ||||||
|  | { | ||||||
|     return c >= '0' && c <= '9'; |     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 '_' |     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) |     if (isEnd(state) || *state->currentChar != expected) | ||||||
|         return false; |         return false; | ||||||
|  |  | ||||||
| @@ -135,35 +150,41 @@ static bool match(CLexState *state, char expected) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| char peek(CLexState *state) { | char peek(CLexState *state) | ||||||
|  | { | ||||||
|     return *state->currentChar; |     return *state->currentChar; | ||||||
| } | } | ||||||
|  |  | ||||||
| static char peekNext(CLexState *state) { | static char peekNext(CLexState *state) | ||||||
|  | { | ||||||
|     if (isEnd(state)) |     if (isEnd(state)) | ||||||
|         return '\0'; |         return '\0'; | ||||||
|  |  | ||||||
|     return state->currentChar[1]; |     return state->currentChar[1]; | ||||||
| } | } | ||||||
|  |  | ||||||
| char next(CLexState *state) { | char next(CLexState *state) | ||||||
|  | { | ||||||
|     if (isEnd(state)) |     if (isEnd(state)) | ||||||
|         return '\0'; // return a null terminator |         return '\0'; // return a null terminator | ||||||
|     state->currentChar++; |     state->currentChar++; | ||||||
|     return state->currentChar[-1]; |     return state->currentChar[-1]; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool isHex(char c) { | bool isHex(char c) | ||||||
|  | { | ||||||
|     return isNumerical(c) || ('A' <= c && 'F' >= c) || ('a' <= c && 'f' >= 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; |     int length = state->currentChar - state->startChar; | ||||||
|  |  | ||||||
|     // check against reserved word list |     // check against reserved word list | ||||||
|     for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) { |     for (size_t i = 0; i < sizeof(reservedWords) / sizeof(CReservedWord); i++) { | ||||||
|         // it matches the reserved word |         // 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; |             return reservedWords[i].type; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -171,7 +192,8 @@ CTokenType identifierType(CLexState *state) { | |||||||
|     return TOKEN_IDENTIFIER; |     return TOKEN_IDENTIFIER; | ||||||
| } | } | ||||||
|  |  | ||||||
| void skipWhitespace(CLexState *state) { | void skipWhitespace(CLexState *state) | ||||||
|  | { | ||||||
|     while (true) { |     while (true) { | ||||||
|         char c = peek(state); |         char c = peek(state); | ||||||
|         switch (c) { |         switch (c) { | ||||||
| @@ -184,14 +206,19 @@ void skipWhitespace(CLexState *state) { | |||||||
|             break; |             break; | ||||||
|         case '/': // consume comments |         case '/': // consume comments | ||||||
|             if (peekNext(state) == '/') { |             if (peekNext(state) == '/') { | ||||||
|                     // skip to next line (also let \n be consumed on the next iteration to properly handle that) |                 // skip to next line (also let \n be consumed on the next iteration to properly | ||||||
|                     while (!isEnd(state) && peek(state) != '\n') // if it's not a newline or the end of the source |                 // handle that) | ||||||
|  |                 while (!isEnd(state) && | ||||||
|  |                        peek(state) != '\n') // if it's not a newline or the end of the source | ||||||
|                     next(state); |                     next(state); | ||||||
|  |  | ||||||
|                 // keep consuming whitespace |                 // keep consuming whitespace | ||||||
|                 break; |                 break; | ||||||
|             } else if (peekNext(state) == '*') { // multiline comments |             } 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 |                 while (!isEnd(state) && | ||||||
|  |                        !(peek(state) == '*' && | ||||||
|  |                          peekNext(state) == | ||||||
|  |                              '/')) // if it's the end of the comment or the end of the source | ||||||
|                     next(state); |                     next(state); | ||||||
|  |  | ||||||
|                 // consume the '*/' |                 // consume the '*/' | ||||||
| @@ -208,7 +235,8 @@ void skipWhitespace(CLexState *state) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| CToken parseString(CLexState *state) { | CToken parseString(CLexState *state) | ||||||
|  | { | ||||||
|     makeBuffer(state); // buffer mode |     makeBuffer(state); // buffer mode | ||||||
|     while (peek(state) != '"' && !isEnd(state)) { |     while (peek(state) != '"' && !isEnd(state)) { | ||||||
|         switch (peek(state)) { |         switch (peek(state)) { | ||||||
| @@ -218,10 +246,19 @@ CToken parseString(CLexState *state) { | |||||||
|             next(state); // consume the '\' character |             next(state); // consume the '\' character | ||||||
|  |  | ||||||
|             switch (peek(state)) { |             switch (peek(state)) { | ||||||
|                     case 'r': case 'n': appendBuffer(state, '\n'); break; |             case 'r': | ||||||
|                     case 't': appendBuffer(state, '\t'); break; |             case 'n': | ||||||
|                     case '\\': appendBuffer(state, '\\'); break; |                 appendBuffer(state, '\n'); | ||||||
|                     case '"': appendBuffer(state, '"'); break; |                 break; | ||||||
|  |             case 't': | ||||||
|  |                 appendBuffer(state, '\t'); | ||||||
|  |                 break; | ||||||
|  |             case '\\': | ||||||
|  |                 appendBuffer(state, '\\'); | ||||||
|  |                 break; | ||||||
|  |             case '"': | ||||||
|  |                 appendBuffer(state, '"'); | ||||||
|  |                 break; | ||||||
|             case 'x':        // hexadecimal character encoding |             case 'x':        // hexadecimal character encoding | ||||||
|                 next(state); // skip 'x' |                 next(state); // skip 'x' | ||||||
|  |  | ||||||
| @@ -282,7 +319,8 @@ CToken parseString(CLexState *state) { | |||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                         return makeError(state, "Unknown special character!"); // TODO: maybe a more descriptive error? |                 return makeError( | ||||||
|  |                     state, "Unknown special character!"); // TODO: maybe a more descriptive error? | ||||||
|             } |             } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -303,7 +341,8 @@ CToken parseString(CLexState *state) { | |||||||
|     return makeToken(state, TOKEN_STRING); |     return makeToken(state, TOKEN_STRING); | ||||||
| } | } | ||||||
|  |  | ||||||
| CToken parseNumber(CLexState *state) {     | CToken parseNumber(CLexState *state) | ||||||
|  | { | ||||||
|     switch (peek(state)) { |     switch (peek(state)) { | ||||||
|     case 'x': // hexadecimal number |     case 'x': // hexadecimal number | ||||||
|         next(state); |         next(state); | ||||||
| @@ -325,7 +364,6 @@ CToken parseNumber(CLexState *state) { | |||||||
|         // if it is a number, fall through and parse normally |         // if it is a number, fall through and parse normally | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     // consume number |     // consume number | ||||||
|     while (isNumerical(peek(state))) { |     while (isNumerical(peek(state))) { | ||||||
|         next(state); |         next(state); | ||||||
| @@ -342,7 +380,8 @@ CToken parseNumber(CLexState *state) { | |||||||
|     return makeToken(state, TOKEN_NUMBER); |     return makeToken(state, TOKEN_NUMBER); | ||||||
| } | } | ||||||
|  |  | ||||||
| CToken parseIdentifier(CLexState *state) { | CToken parseIdentifier(CLexState *state) | ||||||
|  | { | ||||||
|     // read literal |     // read literal | ||||||
|     while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state)) |     while ((isAlpha(peek(state)) || isNumerical(peek(state))) && !isEnd(state)) | ||||||
|         next(state); |         next(state); | ||||||
| @@ -350,7 +389,8 @@ CToken parseIdentifier(CLexState *state) { | |||||||
|     return makeToken(state, identifierType(state)); // is it a reserved word? |     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)); |     CLexState *state = cosmoM_xmalloc(cstate, sizeof(CLexState)); | ||||||
|     state->startChar = (char *)source; |     state->startChar = (char *)source; | ||||||
|     state->currentChar = (char *)source; |     state->currentChar = (char *)source; | ||||||
| @@ -364,11 +404,13 @@ CLexState *cosmoL_newLexState(CState *cstate, const char *source) { | |||||||
|     return state; |     return state; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoL_freeLexState(CState *state, CLexState *lstate) { | void cosmoL_freeLexState(CState *state, CLexState *lstate) | ||||||
|  | { | ||||||
|     cosmoM_free(state, CLexState, lstate); |     cosmoM_free(state, CLexState, lstate); | ||||||
| } | } | ||||||
|  |  | ||||||
| CToken cosmoL_scanToken(CLexState *state) { | CToken cosmoL_scanToken(CLexState *state) | ||||||
|  | { | ||||||
|     skipWhitespace(state); |     skipWhitespace(state); | ||||||
|  |  | ||||||
|     state->startChar = state->currentChar; |     state->startChar = state->currentChar; | ||||||
| @@ -380,37 +422,59 @@ CToken cosmoL_scanToken(CLexState *state) { | |||||||
|  |  | ||||||
|     switch (c) { |     switch (c) { | ||||||
|     // single character tokens |     // single character tokens | ||||||
|         case '(': return makeToken(state, TOKEN_LEFT_PAREN); |     case '(': | ||||||
|         case ')': return makeToken(state, TOKEN_RIGHT_PAREN); |         return makeToken(state, TOKEN_LEFT_PAREN); | ||||||
|         case '{': return makeToken(state, TOKEN_LEFT_BRACE); |     case ')': | ||||||
|         case '}': return makeToken(state, TOKEN_RIGHT_BRACE); |         return makeToken(state, TOKEN_RIGHT_PAREN); | ||||||
|         case '[': return makeToken(state, TOKEN_LEFT_BRACKET); |     case '{': | ||||||
|         case ']': return makeToken(state, TOKEN_RIGHT_BRACKET); |         return makeToken(state, TOKEN_LEFT_BRACE); | ||||||
|         case ';': return makeToken(state, TOKEN_EOS); |     case '}': | ||||||
|         case ',': return makeToken(state, TOKEN_COMMA); |         return makeToken(state, TOKEN_RIGHT_BRACE); | ||||||
|         case ':': return makeToken(state, TOKEN_COLON); |     case '[': | ||||||
|         case '*': return makeToken(state, TOKEN_STAR); |         return makeToken(state, TOKEN_LEFT_BRACKET); | ||||||
|         case '%': return makeToken(state, TOKEN_PERCENT); |     case ']': | ||||||
|         case '^': return makeToken(state, TOKEN_CARROT); |         return makeToken(state, TOKEN_RIGHT_BRACKET); | ||||||
|         case '#': return makeToken(state, TOKEN_POUND); |     case ';': | ||||||
|         case '/': return makeToken(state, TOKEN_SLASH); |         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 |     // two character tokens | ||||||
|     case '+': |     case '+': | ||||||
|         return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS); |         return match(state, '+') ? makeToken(state, TOKEN_PLUS_PLUS) : makeToken(state, TOKEN_PLUS); | ||||||
|     case '-': |     case '-': | ||||||
|             return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) : makeToken(state, TOKEN_MINUS); |         return match(state, '-') ? makeToken(state, TOKEN_MINUS_MINUS) | ||||||
|  |                                  : makeToken(state, TOKEN_MINUS); | ||||||
|     case '.': |     case '.': | ||||||
|             return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) : makeToken(state, TOKEN_DOT_DOT)) : makeToken(state, TOKEN_DOT); |         return match(state, '.') ? (match(state, '.') ? makeToken(state, TOKEN_DOT_DOT_DOT) | ||||||
|  |                                                       : makeToken(state, TOKEN_DOT_DOT)) | ||||||
|  |                                  : makeToken(state, TOKEN_DOT); | ||||||
|     case '!': |     case '!': | ||||||
|             return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) : makeToken(state, TOKEN_BANG); |         return match(state, '=') ? makeToken(state, TOKEN_BANG_EQUAL) | ||||||
|  |                                  : makeToken(state, TOKEN_BANG); | ||||||
|     case '=': |     case '=': | ||||||
|             return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) : makeToken(state, TOKEN_EQUAL); |         return match(state, '=') ? makeToken(state, TOKEN_EQUAL_EQUAL) | ||||||
|  |                                  : makeToken(state, TOKEN_EQUAL); | ||||||
|     case '>': |     case '>': | ||||||
|             return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) : makeToken(state, TOKEN_GREATER); |         return match(state, '=') ? makeToken(state, TOKEN_GREATER_EQUAL) | ||||||
|  |                                  : makeToken(state, TOKEN_GREATER); | ||||||
|     case '<': |     case '<': | ||||||
|             return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) : makeToken(state, TOKEN_LESS); |         return match(state, '=') ? makeToken(state, TOKEN_LESS_EQUAL) | ||||||
|  |                                  : makeToken(state, TOKEN_LESS); | ||||||
|     // literals |     // literals | ||||||
|         case '"': return parseString(state); |     case '"': | ||||||
|  |         return parseString(state); | ||||||
|     default: |     default: | ||||||
|         if (isNumerical(c)) |         if (isNumerical(c)) | ||||||
|             return parseNumber(state); |             return parseNumber(state); | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								src/clex.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/clex.h
									
									
									
									
									
								
							| @@ -3,7 +3,8 @@ | |||||||
|  |  | ||||||
| #include "cosmo.h" | #include "cosmo.h" | ||||||
|  |  | ||||||
| typedef enum { | typedef enum | ||||||
|  | { | ||||||
|     // single character tokens |     // single character tokens | ||||||
|     TOKEN_LEFT_PAREN, |     TOKEN_LEFT_PAREN, | ||||||
|     TOKEN_RIGHT_PAREN, |     TOKEN_RIGHT_PAREN, | ||||||
| @@ -72,23 +73,27 @@ typedef enum { | |||||||
|     TOKEN_EOF |     TOKEN_EOF | ||||||
| } CTokenType; | } CTokenType; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct | ||||||
|  | { | ||||||
|     CTokenType type; |     CTokenType type; | ||||||
|     const char *word; |     const char *word; | ||||||
|     int len; |     int len; | ||||||
| } CReservedWord; | } CReservedWord; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct | ||||||
|  | { | ||||||
|     CTokenType type; |     CTokenType type; | ||||||
|     char *start; |     char *start; | ||||||
|     int length; |     int length; | ||||||
|     int line; |     int line; | ||||||
| } CToken; | } CToken; | ||||||
|  |  | ||||||
| typedef struct { | typedef struct | ||||||
|  | { | ||||||
|     char *currentChar; |     char *currentChar; | ||||||
|     char *startChar; |     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 bufCount; | ||||||
|     size_t bufCap; |     size_t bufCap; | ||||||
|     int line;     // current line |     int line;     // current line | ||||||
|   | |||||||
							
								
								
									
										76
									
								
								src/cmem.c
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								src/cmem.c
									
									
									
									
									
								
							| @@ -1,13 +1,15 @@ | |||||||
| #include "cmem.h" | #include "cmem.h" | ||||||
| #include "cstate.h" |  | ||||||
| #include "cvalue.h" |  | ||||||
| #include "ctable.h" |  | ||||||
| #include "cparse.h" |  | ||||||
| #include "cobj.h" |  | ||||||
| #include "cbaselib.h" | #include "cbaselib.h" | ||||||
|  | #include "cobj.h" | ||||||
|  | #include "cparse.h" | ||||||
|  | #include "cstate.h" | ||||||
|  | #include "ctable.h" | ||||||
|  | #include "cvalue.h" | ||||||
|  |  | ||||||
| // realloc wrapper | // 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; |     state->allocatedBytes += newSize - oldSize; | ||||||
|  |  | ||||||
|     if (newSize == 0) { // it needs to be freed |     if (newSize == 0) { // it needs to be freed | ||||||
| @@ -39,7 +41,8 @@ void *cosmoM_reallocate(CState* state, void *buf, size_t oldSize, size_t newSize | |||||||
|     return newBuf; |     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) { |     if (!(cosmoM_isFrozen(state)) && state->allocatedBytes + needed > state->nextGC) { | ||||||
|         cosmoM_collectGarbage(state); // cya lol |         cosmoM_collectGarbage(state); // cya lol | ||||||
|         return true; |         return true; | ||||||
| @@ -51,7 +54,8 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) { | |||||||
| void markObject(CState *state, CObj *obj); | void markObject(CState *state, CObj *obj); | ||||||
| void markValue(CState *state, CValue val); | 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 |     if (tbl->table == NULL) // table is still being initialized | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
| @@ -64,14 +68,17 @@ void markTable(CState *state, CTable *tbl) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // frees white members from the table | // 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 |     if (tbl->table == NULL) // table is still being initialized | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     int cap = tbl->capacityMask + 1; |     int cap = tbl->capacityMask + 1; | ||||||
|     for (int i = 0; i < cap; i++) { |     for (int i = 0; i < cap; i++) { | ||||||
|         CTableEntry *entry = &tbl->table[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); |             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 |     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++) { |     for (size_t i = 0; i < array->count; i++) { | ||||||
|         markValue(state, array->values[i]); |         markValue(state, array->values[i]); | ||||||
|     } |     } | ||||||
| @@ -87,7 +95,8 @@ void markArray(CState *state, CValueArray *array) { | |||||||
|  |  | ||||||
| // mark all references associated with the object | // mark all references associated with the object | ||||||
| // black = keep, white = discard | // black = keep, white = discard | ||||||
| void blackenObject(CState *state, CObj *obj) { | void blackenObject(CState *state, CObj *obj) | ||||||
|  | { | ||||||
|     markObject(state, (CObj *)obj->proto); |     markObject(state, (CObj *)obj->proto); | ||||||
|     switch (obj->type) { |     switch (obj->type) { | ||||||
|     case COBJ_STRING: |     case COBJ_STRING: | ||||||
| @@ -152,7 +161,8 @@ void blackenObject(CState *state, CObj *obj) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void markObject(CState *state, CObj *obj) { | void markObject(CState *state, CObj *obj) | ||||||
|  | { | ||||||
|     if (obj == NULL || obj->isMarked) // skip if NULL or already marked |     if (obj == NULL || obj->isMarked) // skip if NULL or already marked | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
| @@ -169,25 +179,29 @@ void markObject(CState *state, CObj *obj) { | |||||||
|         return; |         return; | ||||||
|  |  | ||||||
|     // we can use cosmoM_growarray because we lock the GC when we entered in cosmoM_collectGarbage |     // 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; |     state->grayStack.array[state->grayStack.count++] = obj; | ||||||
| } | } | ||||||
|  |  | ||||||
| void markValue(CState *state, CValue val) { | void markValue(CState *state, CValue val) | ||||||
|  | { | ||||||
|     if (IS_REF(val)) |     if (IS_REF(val)) | ||||||
|         markObject(state, cosmoV_readRef(val)); |         markObject(state, cosmoV_readRef(val)); | ||||||
| } | } | ||||||
|  |  | ||||||
| // trace our gray references | // trace our gray references | ||||||
| void traceGrays(CState *state) { | void traceGrays(CState *state) | ||||||
|  | { | ||||||
|     while (state->grayStack.count > 0) { |     while (state->grayStack.count > 0) { | ||||||
|         CObj *obj = state->grayStack.array[--state->grayStack.count]; |         CObj *obj = state->grayStack.array[--state->grayStack.count]; | ||||||
|         blackenObject(state, obj); |         blackenObject(state, obj); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void sweep(CState *state) { | void sweep(CState *state) | ||||||
|  | { | ||||||
|     CObj *prev = NULL; |     CObj *prev = NULL; | ||||||
|     CObj *object = state->objects; |     CObj *object = state->objects; | ||||||
|     while (object != NULL) { |     while (object != NULL) { | ||||||
| @@ -210,7 +224,8 @@ void sweep(CState *state) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void markUserRoots(CState *state) { | void markUserRoots(CState *state) | ||||||
|  | { | ||||||
|     CObj *root = state->userRoots; |     CObj *root = state->userRoots; | ||||||
|  |  | ||||||
|     // traverse userRoots and mark all the object |     // 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 |     // mark all values on the stack | ||||||
|     for (StkPtr value = state->stack; value < state->top; value++) { |     for (StkPtr value = state->stack; value < state->top; value++) { | ||||||
|         markValue(state, *value); |         markValue(state, *value); | ||||||
| @@ -254,7 +270,8 @@ void markRoots(CState *state) { | |||||||
|     traceGrays(state); |     traceGrays(state); | ||||||
| } | } | ||||||
|  |  | ||||||
| COSMO_API void cosmoM_collectGarbage(CState *state) { | COSMO_API void cosmoM_collectGarbage(CState *state) | ||||||
|  | { | ||||||
| #ifdef GC_DEBUG | #ifdef GC_DEBUG | ||||||
|     printf("-- GC start\n"); |     printf("-- GC start\n"); | ||||||
|     size_t start = state->allocatedBytes; |     size_t start = state->allocatedBytes; | ||||||
| @@ -263,26 +280,32 @@ COSMO_API void cosmoM_collectGarbage(CState *state) { | |||||||
|  |  | ||||||
|     markRoots(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 |     // now finally, free all the unmarked objects | ||||||
|     sweep(state); |     sweep(state); | ||||||
|  |  | ||||||
|     // set our next GC event |     // set our next GC event | ||||||
|     cosmoM_updateThreshhold(state); |     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 | #ifdef GC_DEBUG | ||||||
|     printf("-- GC end, reclaimed %ld bytes (started at %ld, ended at %ld), next garbage collection scheduled at %ld bytes\n", |     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); |            start - state->allocatedBytes, start, state->allocatedBytes, state->nextGC); | ||||||
|     getchar(); // pauses execution |     getchar(); // pauses execution | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| COSMO_API void cosmoM_updateThreshhold(CState *state) { | COSMO_API void cosmoM_updateThreshhold(CState *state) | ||||||
|  | { | ||||||
|     state->nextGC = state->allocatedBytes * HEAP_GROW_FACTOR; |     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 |     // first, check and make sure this root doesn't already exist in the list | ||||||
|     CObj *root = state->userRoots; |     CObj *root = state->userRoots; | ||||||
|     while (root != NULL) { |     while (root != NULL) { | ||||||
| @@ -297,7 +320,8 @@ COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot) { | |||||||
|     state->userRoots = newRoot; |     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 *prev = NULL; | ||||||
|     CObj *root = state->userRoots; |     CObj *root = state->userRoots; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								src/cmem.h
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/cmem.h
									
									
									
									
									
								
							| @@ -2,7 +2,6 @@ | |||||||
| #define CMEME_C // meme lol | #define CMEME_C // meme lol | ||||||
|  |  | ||||||
| #include "cosmo.h" | #include "cosmo.h" | ||||||
|  |  | ||||||
| #include "cstate.h" | #include "cstate.h" | ||||||
|  |  | ||||||
| // #define GC_STRESS | // #define GC_STRESS | ||||||
| @@ -14,7 +13,8 @@ | |||||||
|  |  | ||||||
| #ifdef GC_DEBUG | #ifdef GC_DEBUG | ||||||
| #    define cosmoM_freearray(state, type, buf, capacity)                                           \ | #    define cosmoM_freearray(state, type, buf, capacity)                                           \ | ||||||
|     printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__, __LINE__); \ |         printf("freeing array %p [size %lu] at %s:%d\n", buf, sizeof(type) * capacity, __FILE__,   \ | ||||||
|  |                __LINE__);                                                                          \ | ||||||
|         cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) |         cosmoM_reallocate(state, buf, sizeof(type) * capacity, 0) | ||||||
| #else | #else | ||||||
| #    define cosmoM_freearray(state, type, buf, capacity)                                           \ | #    define cosmoM_freearray(state, type, buf, capacity)                                           \ | ||||||
| @@ -33,12 +33,10 @@ | |||||||
|         printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__);           \ |         printf("freeing %p [size %lu] at %s:%d\n", x, sizeof(type), __FILE__, __LINE__);           \ | ||||||
|         cosmoM_reallocate(state, x, sizeof(type), 0) |         cosmoM_reallocate(state, x, sizeof(type), 0) | ||||||
| #else | #else | ||||||
| #define cosmoM_free(state, type, x) \ | #    define cosmoM_free(state, type, x) cosmoM_reallocate(state, x, sizeof(type), 0) | ||||||
|     cosmoM_reallocate(state, x, sizeof(type), 0) |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define cosmoM_isFrozen(state) \ | #define cosmoM_isFrozen(state) (state->freezeGC > 0) | ||||||
|     (state->freezeGC > 0) |  | ||||||
|  |  | ||||||
| // if debugging, print the locations of when the state is frozen/unfrozen | // if debugging, print the locations of when the state is frozen/unfrozen | ||||||
| #ifdef GC_DEBUG | #ifdef GC_DEBUG | ||||||
| @@ -53,8 +51,7 @@ | |||||||
| #else | #else | ||||||
|  |  | ||||||
| // freeze's the garbage collector until cosmoM_unfreezeGC is called | // freeze's the garbage collector until cosmoM_unfreezeGC is called | ||||||
| #define cosmoM_freezeGC(state) \ | #    define cosmoM_freezeGC(state) state->freezeGC++ | ||||||
|     state->freezeGC++ |  | ||||||
|  |  | ||||||
| // unfreeze's the garbage collector and tries to run a garbage collection cycle | // unfreeze's the garbage collector and tries to run a garbage collection cycle | ||||||
| #    define cosmoM_unfreezeGC(state)                                                               \ | #    define cosmoM_unfreezeGC(state)                                                               \ | ||||||
| @@ -64,7 +61,8 @@ | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| COSMO_API void *cosmoM_reallocate(CState *state, void *buf, size_t oldSize, size_t newSize); | 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_collectGarbage(CState *state); | ||||||
| COSMO_API void cosmoM_updateThreshhold(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); | 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) | // 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); |     return cosmoM_reallocate(state, NULL, 0, sz); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										225
									
								
								src/cobj.c
									
									
									
									
									
								
							
							
						
						
									
										225
									
								
								src/cobj.c
									
									
									
									
									
								
							| @@ -1,16 +1,18 @@ | |||||||
|  | #include "cobj.h" | ||||||
|  |  | ||||||
|  | #include "clex.h" | ||||||
|  | #include "cmem.h" | ||||||
| #include "cstate.h" | #include "cstate.h" | ||||||
| #include "ctable.h" | #include "ctable.h" | ||||||
| #include "cobj.h" |  | ||||||
| #include "cmem.h" |  | ||||||
| #include "cvm.h" | #include "cvm.h" | ||||||
| #include "clex.h" |  | ||||||
|  |  | ||||||
| #include <string.h> |  | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
|  | #include <string.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  |  | ||||||
| // we don't actually hash the whole string :eyes: | // 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; |     uint32_t hash = sz; | ||||||
|     size_t step = (sz >> 5) + 1; |     size_t step = (sz >> 5) + 1; | ||||||
|  |  | ||||||
| @@ -20,7 +22,8 @@ uint32_t hashString(const char *str, size_t sz) { | |||||||
|     return hash; |     return hash; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) { | CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) | ||||||
|  | { | ||||||
|     CObj *obj = (CObj *)cosmoM_xmalloc(state, sz); |     CObj *obj = (CObj *)cosmoM_xmalloc(state, sz); | ||||||
|     obj->type = type; |     obj->type = type; | ||||||
|     obj->isMarked = false; |     obj->isMarked = false; | ||||||
| @@ -36,7 +39,8 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type) { | |||||||
|     return obj; |     return obj; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoO_free(CState *state, CObj *obj) { | void cosmoO_free(CState *state, CObj *obj) | ||||||
|  | { | ||||||
| #ifdef GC_DEBUG | #ifdef GC_DEBUG | ||||||
|     printf("freeing %p [", obj); |     printf("freeing %p [", obj); | ||||||
|     printObject(obj); |     printObject(obj); | ||||||
| @@ -98,15 +102,18 @@ void cosmoO_free(CState *state, CObj *obj) { | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case COBJ_MAX: |     case COBJ_MAX: | ||||||
|         default: { /* stubbed, should never happen */ } |     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; |     CObjObject *proto1, *proto2; | ||||||
|     CValue eq1, eq2; |     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; |         return true; | ||||||
|  |  | ||||||
|     // its not the same type, maybe both <ref>'s have the same '__equal' metamethod in their protos? |     // its not the same type, maybe both <ref>'s have the same '__equal' metamethod in their protos? | ||||||
| @@ -116,8 +123,9 @@ bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) { | |||||||
|     switch (obj1->type) { |     switch (obj1->type) { | ||||||
|     case COBJ_STRING: { |     case COBJ_STRING: { | ||||||
|         /* |         /* | ||||||
|                 we already compared the pointers at the top of the function, this prevents the `__equal` metamethod |             we already compared the pointers at the top of the function, this prevents the `__equal` | ||||||
|                 from being checked. If you plan on using `__equal` with strings just remove this case! |            metamethod from being checked. If you plan on using `__equal` with strings just remove | ||||||
|  |            this case! | ||||||
|         */ |         */ | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @@ -148,11 +156,17 @@ bool cosmoO_equal(CState *state, CObj *obj1, CObj *obj2) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
| _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... |     // this is pretty expensive (bad lookup caching helps a lot), but it only all gets run if both | ||||||
|     // it should stay light for the majority of cases |     // objects have protos & both have the `__equal` metamethod defined so... it should stay light | ||||||
|     if ((proto1 = cosmoO_grabProto(obj1)) != NULL && (proto2 = cosmoO_grabProto(obj2)) != NULL && // make sure both protos exist |     // for the majority of cases | ||||||
|         cosmoO_getIString(state, proto1, ISTRING_EQUAL, &eq1) && // grab the `__equal` metamethod from the first proto, if fail abort |     if ((proto1 = cosmoO_grabProto(obj1)) != NULL && | ||||||
|         cosmoO_getIString(state, proto2, ISTRING_EQUAL, &eq2) && // grab the `__equal` metamethod from the second proto, if fail abort |         (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 |         cosmoV_equal(state, eq1, eq2)) { // compare the two `__equal` metamethods | ||||||
|  |  | ||||||
|         // now finally, call the `__equal` metamethod (<object>, <object>) |         // now finally, call the `__equal` metamethod (<object>, <object>) | ||||||
| @@ -164,7 +178,8 @@ _eqFail: | |||||||
|  |  | ||||||
|         // check return value and make sure it's a boolean |         // check return value and make sure it's a boolean | ||||||
|         if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) { |         if (!IS_BOOLEAN(*cosmoV_getTop(state, 0))) { | ||||||
|             cosmoV_error(state, "__equal expected to return <boolean>, got %s!", cosmoV_typeStr(*cosmoV_pop(state))); |             cosmoV_error(state, "__equal expected to return <boolean>, got %s!", | ||||||
|  |                          cosmoV_typeStr(*cosmoV_pop(state))); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -175,7 +190,8 @@ _eqFail: | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjObject *cosmoO_newObject(CState *state) { | CObjObject *cosmoO_newObject(CState *state) | ||||||
|  | { | ||||||
|     CObjObject *obj = (CObjObject *)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT); |     CObjObject *obj = (CObjObject *)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT); | ||||||
|     obj->istringFlags = 0; |     obj->istringFlags = 0; | ||||||
|     obj->userP = NULL; // reserved for C API |     obj->userP = NULL; // reserved for C API | ||||||
| @@ -188,14 +204,16 @@ CObjObject *cosmoO_newObject(CState *state) { | |||||||
|     return obj; |     return obj; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjStream *cosmoO_newStream(CState *state, int fd) { | CObjStream *cosmoO_newStream(CState *state, int fd) | ||||||
|  | { | ||||||
|     CObjStream *strm = (CObjStream *)cosmoO_allocateBase(state, sizeof(CObjStream), COBJ_STREAM); |     CObjStream *strm = (CObjStream *)cosmoO_allocateBase(state, sizeof(CObjStream), COBJ_STREAM); | ||||||
|     strm->fd = fd; |     strm->fd = fd; | ||||||
|  |  | ||||||
|     return strm; |     return strm; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjTable *cosmoO_newTable(CState *state) { | CObjTable *cosmoO_newTable(CState *state) | ||||||
|  | { | ||||||
|     CObjTable *obj = (CObjTable *)cosmoO_allocateBase(state, sizeof(CObjTable), COBJ_TABLE); |     CObjTable *obj = (CObjTable *)cosmoO_allocateBase(state, sizeof(CObjTable), COBJ_TABLE); | ||||||
|  |  | ||||||
|     // init the table (might cause a GC event) |     // init the table (might cause a GC event) | ||||||
| @@ -206,8 +224,10 @@ CObjTable *cosmoO_newTable(CState *state) { | |||||||
|     return obj; |     return obj; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjFunction *cosmoO_newFunction(CState *state) { | CObjFunction *cosmoO_newFunction(CState *state) | ||||||
|     CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION); | { | ||||||
|  |     CObjFunction *func = | ||||||
|  |         (CObjFunction *)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION); | ||||||
|     func->args = 0; |     func->args = 0; | ||||||
|     func->upvals = 0; |     func->upvals = 0; | ||||||
|     func->variadic = false; |     func->variadic = false; | ||||||
| @@ -218,13 +238,16 @@ CObjFunction *cosmoO_newFunction(CState *state) { | |||||||
|     return func; |     return func; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) { | CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func) | ||||||
|     CObjCFunction *cfunc = (CObjCFunction*)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION); | { | ||||||
|  |     CObjCFunction *cfunc = | ||||||
|  |         (CObjCFunction *)cosmoO_allocateBase(state, sizeof(CObjCFunction), COBJ_CFUNCTION); | ||||||
|     cfunc->cfunc = func; |     cfunc->cfunc = func; | ||||||
|     return cfunc; |     return cfunc; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjError *cosmoO_newError(CState *state, CValue err) { | CObjError *cosmoO_newError(CState *state, CValue err) | ||||||
|  | { | ||||||
|     CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); |     CObjError *cerror = (CObjError *)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR); | ||||||
|     cerror->err = err; |     cerror->err = err; | ||||||
|     cerror->frameCount = state->frameCount; |     cerror->frameCount = state->frameCount; | ||||||
| @@ -240,14 +263,16 @@ CObjError *cosmoO_newError(CState *state, CValue err) { | |||||||
|     return cerror; |     return cerror; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj) { | CObjMethod *cosmoO_newMethod(CState *state, CValue func, CObj *obj) | ||||||
|  | { | ||||||
|     CObjMethod *method = (CObjMethod *)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD); |     CObjMethod *method = (CObjMethod *)cosmoO_allocateBase(state, sizeof(CObjMethod), COBJ_METHOD); | ||||||
|     method->func = func; |     method->func = func; | ||||||
|     method->obj = obj; |     method->obj = obj; | ||||||
|     return method; |     return method; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) { | CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) | ||||||
|  | { | ||||||
|     // initialize array of pointers |     // initialize array of pointers | ||||||
|     CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval *) * func->upvals); |     CObjUpval **upvalues = cosmoM_xmalloc(state, sizeof(CObjUpval *) * func->upvals); | ||||||
|  |  | ||||||
| @@ -255,7 +280,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) { | |||||||
|         upvalues[i] = NULL; |         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->function = func; | ||||||
|     closure->upvalues = upvalues; |     closure->upvalues = upvalues; | ||||||
|     closure->upvalueCount = func->upvals; |     closure->upvalueCount = func->upvals; | ||||||
| @@ -263,7 +289,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func) { | |||||||
|     return closure; |     return closure; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) { | CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) | ||||||
|  | { | ||||||
|     CObjUpval *upval = (CObjUpval *)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE); |     CObjUpval *upval = (CObjUpval *)cosmoO_allocateBase(state, sizeof(CObjUpval), COBJ_UPVALUE); | ||||||
|     upval->val = val; |     upval->val = val; | ||||||
|     upval->closed = cosmoV_newNil(); |     upval->closed = cosmoV_newNil(); | ||||||
| @@ -272,7 +299,8 @@ CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val) { | |||||||
|     return upval; |     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); |     uint32_t hash = hashString(str, length); | ||||||
|     CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); |     CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); | ||||||
|  |  | ||||||
| @@ -287,29 +315,34 @@ CObjString *cosmoO_copyString(CState *state, const char *str, size_t length) { | |||||||
|     return cosmoO_allocateString(state, buf, length, hash); |     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!) | // length shouldn't include the null terminator! str should be a null terminated string! (char array | ||||||
| CObjString *cosmoO_takeString(CState *state, char *str, size_t length) { | // should also have been allocated using cosmoM_xmalloc!) | ||||||
|  | CObjString *cosmoO_takeString(CState *state, char *str, size_t length) | ||||||
|  | { | ||||||
|     uint32_t hash = hashString(str, length); |     uint32_t hash = hashString(str, length); | ||||||
|  |  | ||||||
|     CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); |     CObjString *lookup = cosmoT_lookupString(&state->strings, str, length, hash); | ||||||
|  |  | ||||||
|     // have we already interned this string? |     // have we already interned this string? | ||||||
|     if (lookup != NULL) { |     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 lookup; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return cosmoO_allocateString(state, str, length, hash); |     return cosmoO_allocateString(state, str, length, hash); | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t 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 *strObj = (CObjString *)cosmoO_allocateBase(state, sizeof(CObjString), COBJ_STRING); | ||||||
|     strObj->isIString = false; |     strObj->isIString = false; | ||||||
|     strObj->str = (char *)str; |     strObj->str = (char *)str; | ||||||
|     strObj->length = sz; |     strObj->length = sz; | ||||||
|     strObj->hash = hash; |     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) |     // 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); |     cosmoV_pushRef(state, (CObj *)strObj); | ||||||
|     cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj)); |     cosmoT_insert(state, &state->strings, cosmoV_newRef((CObj *)strObj)); | ||||||
|     cosmoV_pop(state); |     cosmoV_pop(state); | ||||||
| @@ -317,7 +350,8 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin | |||||||
|     return strObj; |     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; |     StkPtr start = state->top; | ||||||
|     const char *end; |     const char *end; | ||||||
|     char c; |     char c; | ||||||
| @@ -362,12 +396,14 @@ CObjString *cosmoO_pushVFString(CState *state, const char *format, va_list args) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     cosmoV_pushString(state, format); // push the rest of the string |     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 |     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 |     return cosmoV_readString(*start);  // start should be state->top - 1 | ||||||
| } | } | ||||||
|  |  | ||||||
| // walks the protos of obj and checks for proto | // 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; |     CObjObject *curr = obj->proto; | ||||||
|  |  | ||||||
|     while (curr != NULL) { |     while (curr != NULL) { | ||||||
| @@ -382,9 +418,12 @@ bool cosmoO_isDescendant(CObj *obj, CObjObject *proto) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // returns false if error thrown | // returns false if error thrown | ||||||
| bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *val, CObj *obj) { | 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)) { |     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_pushValue(state, *val);              // push function | ||||||
|             cosmoV_pushRef(state, (CObj *)obj);         // push object |             cosmoV_pushRef(state, (CObj *)obj);         // push object | ||||||
|             if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument |             if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // call the function with the 1 argument | ||||||
| @@ -393,7 +432,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v | |||||||
|             return true; |             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; |             return true; | ||||||
|  |  | ||||||
|         *val = cosmoV_newNil(); |         *val = cosmoV_newNil(); | ||||||
| @@ -403,7 +443,8 @@ bool cosmoO_getRawObject(CState *state, CObjObject *proto, CValue key, CValue *v | |||||||
|     return true; |     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; |     CValue ret; | ||||||
|  |  | ||||||
|     // if the object is locked, throw an error |     // if the object is locked, throw an error | ||||||
| @@ -413,7 +454,8 @@ void cosmoO_setRawObject(CState *state, CObjObject *proto, CValue key, CValue va | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // check for __setters |     // check for __setters | ||||||
|     if (cosmoO_getIString(state, proto, ISTRING_SETTER, &ret) && IS_TABLE(ret) && cosmoT_get(state, &cosmoV_readTable(ret)->tbl, key, &ret)) { |     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_pushValue(state, ret);       // push function | ||||||
|         cosmoV_pushRef(state, (CObj *)obj); // push object |         cosmoV_pushRef(state, (CObj *)obj); // push object | ||||||
|         cosmoV_pushValue(state, val);       // push new value |         cosmoV_pushValue(state, val);       // push new value | ||||||
| @@ -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; |     object->userP = p; | ||||||
| } | } | ||||||
|  |  | ||||||
| void *cosmoO_getUserP(CObjObject *object) { | void *cosmoO_getUserP(CObjObject *object) | ||||||
|  | { | ||||||
|     return object->userP; |     return object->userP; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoO_setUserI(CObjObject *object, int i) { | void cosmoO_setUserI(CObjObject *object, int i) | ||||||
|  | { | ||||||
|     object->userI = i; |     object->userI = i; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoO_getUserI(CObjObject *object) { | int cosmoO_getUserI(CObjObject *object) | ||||||
|  | { | ||||||
|     return object->userI; |     return object->userI; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoO_setUserT(CObjObject *object, int t) { | void cosmoO_setUserT(CObjObject *object, int t) | ||||||
|  | { | ||||||
|     object->userT = t; |     object->userT = t; | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoO_getUserT(CObjObject *object) { | int cosmoO_getUserT(CObjObject *object) | ||||||
|  | { | ||||||
|     return object->userT; |     return object->userT; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoO_lock(CObjObject *object) { | void cosmoO_lock(CObjObject *object) | ||||||
|  | { | ||||||
|     object->isLocked = true; |     object->isLocked = true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoO_unlock(CObjObject *object) { | void cosmoO_unlock(CObjObject *object) | ||||||
|  | { | ||||||
|     object->isLocked = false; |     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)) |     if (readFlag(object->istringFlags, flag)) | ||||||
|         return false; // it's been cached as bad |         return false; // it's been cached as bad | ||||||
|  |  | ||||||
| @@ -478,7 +529,8 @@ bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) { | |||||||
|     return true; // :) |     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; |     CObjObject *obj = object; | ||||||
|  |  | ||||||
|     do { |     do { | ||||||
| @@ -489,7 +541,8 @@ 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 |     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)) { |     if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) { | ||||||
|         cosmoV_pushValue(state, *val);              // push function |         cosmoV_pushValue(state, *val);              // push function | ||||||
|         cosmoV_pushRef(state, (CObj *)object);      // push object |         cosmoV_pushRef(state, (CObj *)object);      // push object | ||||||
| @@ -505,7 +558,8 @@ bool cosmoO_indexObject(CState *state, CObjObject *object, CValue key, CValue *v | |||||||
|     return false; |     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 |     CValue ret; // return value for cosmoO_getIString | ||||||
|  |  | ||||||
|     if (cosmoO_getIString(state, object, ISTRING_NEWINDEX, &ret)) { |     if (cosmoO_getIString(state, object, ISTRING_NEWINDEX, &ret)) { | ||||||
| @@ -521,7 +575,8 @@ bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjString *cosmoO_toString(CState *state, CObj *obj) { | CObjString *cosmoO_toString(CState *state, CObj *obj) | ||||||
|  | { | ||||||
|     CObjObject *protoObject = cosmoO_grabProto(obj); |     CObjObject *protoObject = cosmoO_grabProto(obj); | ||||||
|     CValue res; |     CValue res; | ||||||
|  |  | ||||||
| @@ -535,7 +590,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) { | |||||||
|         // make sure the __tostring function returned a string |         // make sure the __tostring function returned a string | ||||||
|         StkPtr ret = cosmoV_getTop(state, 0); |         StkPtr ret = cosmoV_getTop(state, 0); | ||||||
|         if (!IS_STRING(*ret)) { |         if (!IS_STRING(*ret)) { | ||||||
|             cosmoV_error(state, "__tostring expected to return <string>, got %s!", cosmoV_typeStr(*ret)); |             cosmoV_error(state, "__tostring expected to return <string>, got %s!", | ||||||
|  |                          cosmoV_typeStr(*ret)); | ||||||
|             return cosmoO_copyString(state, "<err>", 5); |             return cosmoO_copyString(state, "<err>", 5); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -554,12 +610,14 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) { | |||||||
|     } |     } | ||||||
|     case COBJ_FUNCTION: { |     case COBJ_FUNCTION: { | ||||||
|         CObjFunction *func = (CObjFunction *)obj; |         CObjFunction *func = (CObjFunction *)obj; | ||||||
|             return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));  |         return func->name != NULL ? func->name | ||||||
|  |                                   : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); | ||||||
|     } |     } | ||||||
|     case COBJ_CFUNCTION: { |     case COBJ_CFUNCTION: { | ||||||
|         CObjCFunction *cfunc = (CObjCFunction *)obj; |         CObjCFunction *cfunc = (CObjCFunction *)obj; | ||||||
|         char buf[64]; |         char buf[64]; | ||||||
|             int sz = sprintf(buf, "<c function> %p", (void*)cfunc->cfunc) + 1; // +1 for the null character |         int sz = | ||||||
|  |             sprintf(buf, "<c function> %p", (void *)cfunc->cfunc) + 1; // +1 for the null character | ||||||
|         return cosmoO_copyString(state, buf, sz); |         return cosmoO_copyString(state, buf, sz); | ||||||
|     } |     } | ||||||
|     case COBJ_OBJECT: { |     case COBJ_OBJECT: { | ||||||
| @@ -584,7 +642,8 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) { | cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) | ||||||
|  | { | ||||||
|     CObjObject *proto = cosmoO_grabProto(obj); |     CObjObject *proto = cosmoO_grabProto(obj); | ||||||
|     CValue res; |     CValue res; | ||||||
|  |  | ||||||
| @@ -596,7 +655,8 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) { | |||||||
|  |  | ||||||
|         StkPtr temp = cosmoV_getTop(state, 0); |         StkPtr temp = cosmoV_getTop(state, 0); | ||||||
|         if (!IS_NUMBER(*temp)) { |         if (!IS_NUMBER(*temp)) { | ||||||
|             cosmoV_error(state, "__tonumber expected to return <number>, got %s!", cosmoV_typeStr(*temp)); |             cosmoV_error(state, "__tonumber expected to return <number>, got %s!", | ||||||
|  |                          cosmoV_typeStr(*temp)); | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -615,19 +675,22 @@ cosmo_Number cosmoO_toNumber(CState *state, CObj *obj) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| int cosmoO_count(CState *state, CObj *obj) { | int cosmoO_count(CState *state, CObj *obj) | ||||||
|  | { | ||||||
|     CObjObject *proto = cosmoO_grabProto(obj); |     CObjObject *proto = cosmoO_grabProto(obj); | ||||||
|     CValue res; |     CValue res; | ||||||
|  |  | ||||||
|     if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) { |     if (proto != NULL && cosmoO_getIString(state, proto, ISTRING_COUNT, &res)) { | ||||||
|         cosmoV_pushValue(state, 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, we expect 1 return value of type <number> |         if (cosmoV_call(state, 1, 1) != | ||||||
|  |             COSMOVM_OK) // call res, we expect 1 return value of type <number> | ||||||
|             return 0; |             return 0; | ||||||
|  |  | ||||||
|         StkPtr ret = cosmoV_getTop(state, 0); |         StkPtr ret = cosmoV_getTop(state, 0); | ||||||
|         if (!IS_NUMBER(*ret)) { |         if (!IS_NUMBER(*ret)) { | ||||||
|             cosmoV_error(state, "__count expected to return <number>, got %s!", cosmoV_typeStr(*ret)); |             cosmoV_error(state, "__count expected to return <number>, got %s!", | ||||||
|  |                          cosmoV_typeStr(*ret)); | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -651,7 +714,8 @@ int cosmoO_count(CState *state, CObj *obj) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void printObject(CObj *o) { | void printObject(CObj *o) | ||||||
|  | { | ||||||
|     switch (o->type) { |     switch (o->type) { | ||||||
|     case COBJ_STRING: { |     case COBJ_STRING: { | ||||||
|         CObjString *objStr = (CObjString *)o; |         CObjString *objStr = (CObjString *)o; | ||||||
| @@ -709,16 +773,25 @@ void printObject(CObj *o) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| const char *cosmoO_typeStr(CObj* obj) { | const char *cosmoO_typeStr(CObj *obj) | ||||||
|  | { | ||||||
|     switch (obj->type) { |     switch (obj->type) { | ||||||
|         case COBJ_STRING:       return "<string>"; |     case COBJ_STRING: | ||||||
|         case COBJ_OBJECT:       return "<object>"; |         return "<string>"; | ||||||
|         case COBJ_TABLE:        return "<table>"; |     case COBJ_OBJECT: | ||||||
|         case COBJ_FUNCTION:     return "<function>"; |         return "<object>"; | ||||||
|         case COBJ_CFUNCTION:    return "<c function>"; |     case COBJ_TABLE: | ||||||
|         case COBJ_METHOD:       return "<method>"; |         return "<table>"; | ||||||
|         case COBJ_CLOSURE:      return "<closure>"; |     case COBJ_FUNCTION: | ||||||
|         case COBJ_UPVALUE:      return "<upvalue>"; |         return "<function>"; | ||||||
|  |     case COBJ_CFUNCTION: | ||||||
|  |         return "<c function>"; | ||||||
|  |     case COBJ_METHOD: | ||||||
|  |         return "<method>"; | ||||||
|  |     case COBJ_CLOSURE: | ||||||
|  |         return "<closure>"; | ||||||
|  |     case COBJ_UPVALUE: | ||||||
|  |         return "<upvalue>"; | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         return "<unkn obj>"; // TODO: maybe panic? could be a malformed object :eyes: |         return "<unkn obj>"; // TODO: maybe panic? could be a malformed object :eyes: | ||||||
|   | |||||||
							
								
								
									
										67
									
								
								src/cobj.h
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								src/cobj.h
									
									
									
									
									
								
							| @@ -3,7 +3,8 @@ | |||||||
|  |  | ||||||
| #include "cosmo.h" | #include "cosmo.h" | ||||||
|  |  | ||||||
| typedef enum CObjType { | typedef enum CObjType | ||||||
|  | { | ||||||
|     COBJ_STRING, |     COBJ_STRING, | ||||||
|     COBJ_OBJECT, |     COBJ_OBJECT, | ||||||
|     COBJ_TABLE, |     COBJ_TABLE, | ||||||
| @@ -18,10 +19,10 @@ typedef enum CObjType { | |||||||
|     COBJ_MAX |     COBJ_MAX | ||||||
| } CObjType; | } CObjType; | ||||||
|  |  | ||||||
| #include "cstate.h" |  | ||||||
| #include "cchunk.h" | #include "cchunk.h" | ||||||
| #include "cvalue.h" | #include "cstate.h" | ||||||
| #include "ctable.h" | #include "ctable.h" | ||||||
|  | #include "cvalue.h" | ||||||
|  |  | ||||||
| #define CommonHeader       CObj _obj | #define CommonHeader       CObj _obj | ||||||
| #define readFlag(x, flag)  (x & (1u << flag)) | #define readFlag(x, flag)  (x & (1u << flag)) | ||||||
| @@ -29,7 +30,8 @@ typedef enum CObjType { | |||||||
|  |  | ||||||
| typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args); | typedef int (*CosmoCFunction)(CState *state, int argCount, CValue *args); | ||||||
|  |  | ||||||
| struct CObj { | struct CObj | ||||||
|  | { | ||||||
|     struct CObj *next; |     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 |     struct CObjObject *proto; // protoobject, describes the behavior of the object | ||||||
| @@ -37,7 +39,8 @@ struct CObj { | |||||||
|     bool isMarked; // for the GC |     bool isMarked; // for the GC | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjString { | struct CObjString | ||||||
|  | { | ||||||
|     CommonHeader;  // "is a" CObj |     CommonHeader;  // "is a" CObj | ||||||
|     char *str;     // NULL termincated string |     char *str;     // NULL termincated string | ||||||
|     uint32_t hash; // for hashtable lookup |     uint32_t hash; // for hashtable lookup | ||||||
| @@ -45,12 +48,14 @@ struct CObjString { | |||||||
|     bool isIString; |     bool isIString; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjStream { | struct CObjStream | ||||||
|  | { | ||||||
|     CommonHeader; // "is a" CObj |     CommonHeader; // "is a" CObj | ||||||
|     int fd; // handle to file descriptor, on POSIX compliant OSes this can also be a socket :pog: |     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 |     CommonHeader; // "is a" CObj | ||||||
|     CValue err;   // error string |     CValue err;   // error string | ||||||
|     CCallFrame *frames; |     CCallFrame *frames; | ||||||
| @@ -59,11 +64,14 @@ struct CObjError { | |||||||
|     bool parserError; // if true, cosmoV_printError will format the error to the lexer |     bool parserError; // if true, cosmoV_printError will format the error to the lexer | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjObject { | struct CObjObject | ||||||
|  | { | ||||||
|     CommonHeader; // "is a" CObj |     CommonHeader; // "is a" CObj | ||||||
|     CTable tbl; |     CTable tbl; | ||||||
|     cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like __init, __index, etc.) |     cosmo_Flag istringFlags; // enables us to have a much faster lookup for reserved IStrings (like | ||||||
|     union { // userdata (NULL by default) |                              // __init, __index, etc.) | ||||||
|  |     union | ||||||
|  |     { // userdata (NULL by default) | ||||||
|         void *userP; |         void *userP; | ||||||
|         int userI; |         int userI; | ||||||
|     }; |     }; | ||||||
| @@ -71,12 +79,14 @@ struct CObjObject { | |||||||
|     bool isLocked; |     bool isLocked; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjTable { // table, a wrapper for CTable | struct CObjTable | ||||||
|  | {                 // table, a wrapper for CTable | ||||||
|     CommonHeader; // "is a" CObj |     CommonHeader; // "is a" CObj | ||||||
|     CTable tbl; |     CTable tbl; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjFunction { | struct CObjFunction | ||||||
|  | { | ||||||
|     CommonHeader; // "is a" CObj |     CommonHeader; // "is a" CObj | ||||||
|     CChunk chunk; |     CChunk chunk; | ||||||
|     CObjString *name; |     CObjString *name; | ||||||
| @@ -86,25 +96,29 @@ struct CObjFunction { | |||||||
|     bool variadic; |     bool variadic; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjCFunction { | struct CObjCFunction | ||||||
|  | { | ||||||
|     CommonHeader; // "is a" CObj |     CommonHeader; // "is a" CObj | ||||||
|     CosmoCFunction cfunc; |     CosmoCFunction cfunc; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjClosure { | struct CObjClosure | ||||||
|  | { | ||||||
|     CommonHeader; // "is a" CObj |     CommonHeader; // "is a" CObj | ||||||
|     CObjFunction *function; |     CObjFunction *function; | ||||||
|     CObjUpval **upvalues; |     CObjUpval **upvalues; | ||||||
|     int upvalueCount; |     int upvalueCount; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjMethod { | struct CObjMethod | ||||||
|  | { | ||||||
|     CommonHeader; // "is a " CObj |     CommonHeader; // "is a " CObj | ||||||
|     CValue func; |     CValue func; | ||||||
|     CObj *obj; // obj this method is bound too |     CObj *obj; // obj this method is bound too | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct CObjUpval { | struct CObjUpval | ||||||
|  | { | ||||||
|     CommonHeader; // "is a" CObj |     CommonHeader; // "is a" CObj | ||||||
|     CValue closed; |     CValue closed; | ||||||
|     CValue *val; |     CValue *val; | ||||||
| @@ -134,12 +148,14 @@ struct CObjUpval { | |||||||
|  |  | ||||||
| #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; |     return IS_REF(val) && cosmoV_readRef(val)->type == type; | ||||||
| } | } | ||||||
|  |  | ||||||
| // just protects against macro expansion | // 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); |     return IS_CLOSURE(val) || IS_CFUNCTION(val) || IS_METHOD(val); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -160,7 +176,8 @@ CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func); | |||||||
| CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val); | CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val); | ||||||
|  |  | ||||||
| // grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned) | // grabs the base proto of the CObj* (if CObj is a CObjObject, that is returned) | ||||||
| static inline CObjObject *cosmoO_grabProto(CObj *obj) { | static inline CObjObject *cosmoO_grabProto(CObj *obj) | ||||||
|  | { | ||||||
|     return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto; |     return obj->type == COBJ_OBJECT ? (CObjObject *)obj : obj->proto; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -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_indexObject(CState *state, CObjObject *object, CValue key, CValue *val); | ||||||
| bool cosmoO_newIndexObject(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); | void cosmoO_setUserP(CObjObject *object, void *p); | ||||||
| // gets the user-defined pointer | // gets the user-defined pointer | ||||||
| void *cosmoO_getUserP(CObjObject *object); | 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); | void cosmoO_setUserI(CObjObject *object, int i); | ||||||
| // gets the user-defined integer | // gets the user-defined integer | ||||||
| int cosmoO_getUserI(CObjObject *object); | int cosmoO_getUserI(CObjObject *object); | ||||||
| @@ -189,10 +208,12 @@ void cosmoO_unlock(CObjObject *object); | |||||||
| // internal string | // internal string | ||||||
| bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val); | 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); | 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); | CObjString *cosmoO_takeString(CState *state, char *str, size_t length); | ||||||
|  |  | ||||||
| // allocates a CObjStruct pointing directly to *str | // allocates a CObjStruct pointing directly to *str | ||||||
|   | |||||||
| @@ -5,7 +5,8 @@ | |||||||
|  |  | ||||||
| // instructions | // instructions | ||||||
|  |  | ||||||
| typedef enum { | typedef enum | ||||||
|  | { | ||||||
|     // STACK/STATE MANIPULATION |     // STACK/STATE MANIPULATION | ||||||
|     OP_LOADCONST, // pushes const[uint8_t] to the stack |     OP_LOADCONST, // pushes const[uint8_t] to the stack | ||||||
|     OP_SETGLOBAL, // pops and sets global[const[uint16_t]] |     OP_SETGLOBAL, // pops and sets global[const[uint16_t]] | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								src/cosmo.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/cosmo.h
									
									
									
									
									
								
							| @@ -1,18 +1,18 @@ | |||||||
| #ifndef COSMOMAIN_H | #ifndef COSMOMAIN_H | ||||||
| #define COSMOMAIN_H | #define COSMOMAIN_H | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <assert.h> | ||||||
|  | #include <stdbool.h> | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
| #include <assert.h> |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|     SAFE_STACK: |     SAFE_STACK: | ||||||
|         if undefined, the stack will not be checked for stack overflows. This may improve performance, however  |         if undefined, the stack will not be checked for stack overflows. This may improve | ||||||
|     this will produce undefined behavior as you reach the stack limit (and may cause a seg fault!). It is recommended to keep this enabled. |    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 SAFE_STACK | ||||||
| // #define NAN_BOXXED | // #define NAN_BOXXED | ||||||
| @@ -54,7 +54,6 @@ typedef uint8_t INSTRUCTION; | |||||||
| #define UNNAMEDCHUNK    "_main" | #define UNNAMEDCHUNK    "_main" | ||||||
| #define COSMOASSERT(x)  assert(x) | #define COSMOASSERT(x)  assert(x) | ||||||
|  |  | ||||||
| #define CERROR(err) \ | #define CERROR(err)     printf("%s : %s\n", "[ERROR]", err) | ||||||
|     printf("%s : %s\n", "[ERROR]", err) |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										480
									
								
								src/cparse.c
									
									
									
									
									
								
							
							
						
						
									
										480
									
								
								src/cparse.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,10 +1,11 @@ | |||||||
| #ifndef CPARSE_H | #ifndef CPARSE_H | ||||||
| #define CPARSE_H | #define CPARSE_H | ||||||
|  |  | ||||||
| #include "cosmo.h" |  | ||||||
| #include "clex.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 | // 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); | CObjFunction *cosmoP_compileString(CState *state, const char *source, const char *module); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								src/cstate.c
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/cstate.c
									
									
									
									
									
								
							| @@ -1,12 +1,14 @@ | |||||||
| #include "cstate.h" | #include "cstate.h" | ||||||
|  |  | ||||||
| #include "cchunk.h" | #include "cchunk.h" | ||||||
|  | #include "cmem.h" | ||||||
| #include "cobj.h" | #include "cobj.h" | ||||||
| #include "cvm.h" | #include "cvm.h" | ||||||
| #include "cmem.h" |  | ||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| CState *cosmoV_newState() { | CState *cosmoV_newState() | ||||||
|  | { | ||||||
|     // we use C's malloc because we don't want to trigger a GC with an invalid state |     // we use C's malloc because we don't want to trigger a GC with an invalid state | ||||||
|     CState *state = malloc(sizeof(CState)); |     CState *state = malloc(sizeof(CState)); | ||||||
|  |  | ||||||
| @@ -73,7 +75,8 @@ CState *cosmoV_newState() { | |||||||
|     return state; |     return state; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoV_freeState(CState *state) { | void cosmoV_freeState(CState *state) | ||||||
|  | { | ||||||
| #ifdef GC_DEBUG | #ifdef GC_DEBUG | ||||||
|     printf("state %p is being free'd!\n", state); |     printf("state %p is being free'd!\n", state); | ||||||
| #endif | #endif | ||||||
| @@ -100,15 +103,16 @@ void cosmoV_freeState(CState *state) { | |||||||
|     // TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later |     // TODO: yeah idk, it looks like im missing 520 bytes somewhere? i'll look into it later | ||||||
|     /*#ifdef GC_DEBUG |     /*#ifdef GC_DEBUG | ||||||
|         if (state->allocatedBytes != sizeof(CState)) { |         if (state->allocatedBytes != sizeof(CState)) { | ||||||
|         printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", sizeof(CState), state->allocatedBytes); |             printf("state->allocatedBytes doesn't match expected value (%lu), got %lu!", | ||||||
|         exit(0); |     sizeof(CState), state->allocatedBytes); exit(0); | ||||||
|         } |         } | ||||||
|     #endif*/ |     #endif*/ | ||||||
|     free(state); |     free(state); | ||||||
| } | } | ||||||
|  |  | ||||||
| // expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value | // 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++) { |     for (int i = 0; i < pairs; i++) { | ||||||
|         StkPtr key = cosmoV_getTop(state, 1); |         StkPtr key = cosmoV_getTop(state, 1); | ||||||
|         StkPtr val = cosmoV_getTop(state, 0); |         StkPtr val = cosmoV_getTop(state, 0); | ||||||
| @@ -120,7 +124,8 @@ void cosmoV_register(CState *state, int pairs) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoV_printStack(CState *state) { | void cosmoV_printStack(CState *state) | ||||||
|  | { | ||||||
|     printf("==== [[ stack dump ]] ====\n"); |     printf("==== [[ stack dump ]] ====\n"); | ||||||
|     for (CValue *top = state->top - 1; top >= state->stack; top--) { |     for (CValue *top = state->top - 1; top >= state->stack; top--) { | ||||||
|         printf("%d: ", (int)(top - state->stack)); |         printf("%d: ", (int)(top - state->stack)); | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								src/cstate.h
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/cstate.h
									
									
									
									
									
								
							| @@ -1,18 +1,20 @@ | |||||||
| #ifndef CSTATE_H | #ifndef CSTATE_H | ||||||
| #define CSTATE_H | #define CSTATE_H | ||||||
|  |  | ||||||
| #include "cosmo.h" |  | ||||||
| #include "cobj.h" | #include "cobj.h" | ||||||
| #include "cvalue.h" | #include "cosmo.h" | ||||||
| #include "ctable.h" | #include "ctable.h" | ||||||
|  | #include "cvalue.h" | ||||||
|  |  | ||||||
| struct CCallFrame { | struct CCallFrame | ||||||
|  | { | ||||||
|     CObjClosure *closure; |     CObjClosure *closure; | ||||||
|     INSTRUCTION *pc; |     INSTRUCTION *pc; | ||||||
|     CValue *base; |     CValue *base; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef enum IStringEnum { | typedef enum IStringEnum | ||||||
|  | { | ||||||
|     ISTRING_INIT,     // __init |     ISTRING_INIT,     // __init | ||||||
|     ISTRING_TOSTRING, // __tostring |     ISTRING_TOSTRING, // __tostring | ||||||
|     ISTRING_TONUMBER, // __tonumber |     ISTRING_TONUMBER, // __tonumber | ||||||
| @@ -25,24 +27,29 @@ typedef enum IStringEnum { | |||||||
|     ISTRING_ITER,     // __iter |     ISTRING_ITER,     // __iter | ||||||
|     ISTRING_NEXT,     // __next |     ISTRING_NEXT,     // __next | ||||||
|     ISTRING_RESERVED, // __reserved |     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 |     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; | } IStringEnum; | ||||||
|  |  | ||||||
| typedef struct ArrayCObj { | typedef struct ArrayCObj | ||||||
|  | { | ||||||
|     CObj **array; |     CObj **array; | ||||||
|     int count; |     int count; | ||||||
|     int capacity; |     int capacity; | ||||||
| } ArrayCObj; | } ArrayCObj; | ||||||
|  |  | ||||||
| struct CState { | struct CState | ||||||
|  | { | ||||||
|     bool panic; |     bool panic; | ||||||
|     int freezeGC; // when > 0, GC events will be ignored (for internal use) |     int freezeGC; // when > 0, GC events will be ignored (for internal use) | ||||||
|     int frameCount; |     int frameCount; | ||||||
|  |  | ||||||
|     CObjError *error; // NULL, unless panic is true |     CObjError *error; // NULL, unless panic is true | ||||||
|     CObj *objects;    // tracks all of our allocated objects |     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 |     CObj *userRoots;  // user definable roots, this holds CObjs that should be considered "roots", | ||||||
|     ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but *have been* found |                       // 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 allocatedBytes; | ||||||
|     size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event |     size_t nextGC; // when allocatedBytes reaches this threshhold, trigger a GC event | ||||||
|  |  | ||||||
| @@ -52,7 +59,8 @@ struct CState { | |||||||
|  |  | ||||||
|     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] |     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 |     CCallFrame callFrame[FRAME_MAX]; // call frames | ||||||
|     CValue stack[STACK_MAX];         // stack |     CValue stack[STACK_MAX];         // stack | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										89
									
								
								src/ctable.c
									
									
									
									
									
								
							
							
						
						
									
										89
									
								
								src/ctable.c
									
									
									
									
									
								
							| @@ -1,7 +1,8 @@ | |||||||
| #include "ctable.h" | #include "ctable.h" | ||||||
|  |  | ||||||
| #include "cmem.h" | #include "cmem.h" | ||||||
| #include "cvalue.h" |  | ||||||
| #include "cobj.h" | #include "cobj.h" | ||||||
|  | #include "cvalue.h" | ||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| @@ -10,12 +11,15 @@ | |||||||
| #define MIN_TABLE_CAPACITY ARRAY_START | #define MIN_TABLE_CAPACITY ARRAY_START | ||||||
|  |  | ||||||
| // bit-twiddling hacks, gets the next power of 2 | // bit-twiddling hacks, gets the next power of 2 | ||||||
| unsigned int nextPow2(unsigned int x) { | unsigned int nextPow2(unsigned int x) | ||||||
|     if (x <= ARRAY_START - 1) return ARRAY_START; // sanity check | { | ||||||
|  |     if (x <= ARRAY_START - 1) | ||||||
|  |         return ARRAY_START; // sanity check | ||||||
|     x--; |     x--; | ||||||
|  |  | ||||||
|     int power = 2; |     int power = 2; | ||||||
|     while (x >>= 1) power <<= 1; |     while (x >>= 1) | ||||||
|  |         power <<= 1; | ||||||
|  |  | ||||||
|     if (power < ARRAY_START) |     if (power < ARRAY_START) | ||||||
|         return ARRAY_START; |         return ARRAY_START; | ||||||
| @@ -23,7 +27,8 @@ unsigned int nextPow2(unsigned int x) { | |||||||
|     return power; |     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 |     startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P | ||||||
|  |  | ||||||
|     tbl->capacityMask = startCap - 1; |     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; |     int cap = from->capacityMask + 1; | ||||||
|     for (int i = 0; i < cap; i++) { |     for (int i = 0; i < cap; i++) { | ||||||
|         CTableEntry *entry = &from->table[i]; |         CTableEntry *entry = &from->table[i]; | ||||||
| @@ -51,11 +57,13 @@ 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)); |     cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1)); | ||||||
| } | } | ||||||
|  |  | ||||||
| uint32_t getObjectHash(CObj *obj) { | uint32_t getObjectHash(CObj *obj) | ||||||
|  | { | ||||||
|     switch (obj->type) { |     switch (obj->type) { | ||||||
|     case COBJ_STRING: |     case COBJ_STRING: | ||||||
|         return ((CObjString *)obj)->hash; |         return ((CObjString *)obj)->hash; | ||||||
| @@ -64,7 +72,8 @@ uint32_t getObjectHash(CObj *obj) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| uint32_t getValueHash(CValue *val) { | uint32_t getValueHash(CValue *val) | ||||||
|  | { | ||||||
|     switch (GET_TYPE(*val)) { |     switch (GET_TYPE(*val)) { | ||||||
|     case COSMO_TREF: |     case COSMO_TREF: | ||||||
|         return getObjectHash(cosmoV_readRef(*val)); |         return getObjectHash(cosmoV_readRef(*val)); | ||||||
| @@ -76,7 +85,8 @@ uint32_t getValueHash(CValue *val) { | |||||||
|             return 0; |             return 0; | ||||||
|  |  | ||||||
|         memcpy(buf, &num, sizeof(buf)); |         memcpy(buf, &num, sizeof(buf)); | ||||||
|             for (size_t i = 0; i < sizeof(cosmo_Number)/sizeof(uint32_t); i++) buf[0] += buf[i]; |         for (size_t i = 0; i < sizeof(cosmo_Number) / sizeof(uint32_t); i++) | ||||||
|  |             buf[0] += buf[i]; | ||||||
|         return buf[0]; |         return buf[0]; | ||||||
|     } |     } | ||||||
|     // TODO: add support for other types |     // TODO: add support for other types | ||||||
| @@ -86,9 +96,11 @@ uint32_t getValueHash(CValue *val) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // mask should always be (capacity - 1) | // 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 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; |     CTableEntry *tomb = NULL; | ||||||
|  |  | ||||||
|     // keep looking for an open slot in the entries array |     // keep looking for an open slot in the entries array | ||||||
| @@ -112,7 +124,8 @@ 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)) |     if (canShrink && cosmoT_checkShrink(state, tbl)) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
| @@ -122,7 +135,8 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin | |||||||
|  |  | ||||||
|     cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC |     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; |         return; | ||||||
|  |  | ||||||
|     CTableEntry *entries = cosmoM_xmalloc(state, size); |     CTableEntry *entries = cosmoM_xmalloc(state, size); | ||||||
| @@ -157,10 +171,14 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin | |||||||
|     tbl->tombstones = 0; |     tbl->tombstones = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool cosmoT_checkShrink(CState *state, CTable *tbl) { | bool cosmoT_checkShrink(CState *state, CTable *tbl) | ||||||
|  | { | ||||||
|     // if count > 8 and active entries < tombstones |     // 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 |     if (tbl->count > MIN_TABLE_CAPACITY && | ||||||
|         resizeTbl(state, tbl, nextPow2(tbl->count - tbl->tombstones) * GROW_FACTOR, false); // shrink based on active entries to the next pow of 2 |         (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; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -168,7 +186,8 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // returns a pointer to the allocated value | // 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 |     // make sure we have enough space allocated | ||||||
|     int cap = tbl->capacityMask + 1; |     int cap = tbl->capacityMask + 1; | ||||||
|     if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) { |     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 |     // 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->key)) { | ||||||
|         if (IS_NIL(entry->val)) // is it empty? |         if (IS_NIL(entry->val)) // is it empty? | ||||||
| @@ -191,7 +211,8 @@ COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) { | |||||||
|     return &entry->val; |     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 |     // sanity check | ||||||
|     if (tbl->count == 0) { |     if (tbl->count == 0) { | ||||||
|         *val = cosmoV_newNil(); |         *val = cosmoV_newNil(); | ||||||
| @@ -205,8 +226,10 @@ bool cosmoT_get(CState *state, CTable *tbl, CValue key, CValue *val) { | |||||||
|     return !(IS_NIL(entry->key)); |     return !(IS_NIL(entry->key)); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool cosmoT_remove(CState* state, CTable *tbl, CValue key) { | bool cosmoT_remove(CState *state, CTable *tbl, CValue key) | ||||||
|     if (tbl->count == 0) return 0; // sanity check | { | ||||||
|  |     if (tbl->count == 0) | ||||||
|  |         return 0; // sanity check | ||||||
|  |  | ||||||
|     CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); |     CTableEntry *entry = findEntry(state, tbl->table, tbl->capacityMask, key); | ||||||
|     if (IS_NIL(entry->key)) // sanity check |     if (IS_NIL(entry->key)) // sanity check | ||||||
| @@ -214,20 +237,26 @@ bool cosmoT_remove(CState* state, CTable *tbl, CValue key) { | |||||||
|  |  | ||||||
|     // crafts tombstone |     // crafts tombstone | ||||||
|     entry->key = cosmoV_newNil(); // this has to be nil |     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++; |     tbl->tombstones++; | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| // returns the active entry count | // returns the active entry count | ||||||
| COSMO_API int cosmoT_count(CTable *tbl) { | COSMO_API int cosmoT_count(CTable *tbl) | ||||||
|  | { | ||||||
|     return tbl->count - tbl->tombstones; |     return tbl->count - tbl->tombstones; | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash) { | 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 |     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 |     // keep looking for an open slot in the entries array | ||||||
|     while (true) { |     while (true) { | ||||||
| @@ -236,7 +265,8 @@ 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) |         // check if it's an empty slot (meaning we dont have it in the table) | ||||||
|         if (IS_NIL(entry->key) && IS_NIL(entry->val)) { |         if (IS_NIL(entry->key) && IS_NIL(entry->val)) { | ||||||
|             return NULL; |             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! |             // it's a match! | ||||||
|             return (CObjString *)cosmoV_readRef(entry->key); |             return (CObjString *)cosmoV_readRef(entry->key); | ||||||
|         } |         } | ||||||
| @@ -246,7 +276,8 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32 | |||||||
| } | } | ||||||
|  |  | ||||||
| // for debugging purposes | // for debugging purposes | ||||||
| void cosmoT_printTable(CTable *tbl, const char *name) { | void cosmoT_printTable(CTable *tbl, const char *name) | ||||||
|  | { | ||||||
|     printf("==== [[%s]] ====\n", name); |     printf("==== [[%s]] ====\n", name); | ||||||
|     int cap = tbl->capacityMask + 1; |     int cap = tbl->capacityMask + 1; | ||||||
|     for (int i = 0; i < cap; i++) { |     for (int i = 0; i < cap; i++) { | ||||||
|   | |||||||
| @@ -1,17 +1,20 @@ | |||||||
| #ifndef CTABLE_H | #ifndef CTABLE_H | ||||||
| #define 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 "cosmo.h" | ||||||
| #include "cvalue.h" | #include "cvalue.h" | ||||||
|  |  | ||||||
| typedef struct CTableEntry { | typedef struct CTableEntry | ||||||
|  | { | ||||||
|     CValue key; |     CValue key; | ||||||
|     CValue val; |     CValue val; | ||||||
| } CTableEntry; | } CTableEntry; | ||||||
|  |  | ||||||
| typedef struct CTable { | typedef struct CTable | ||||||
|  | { | ||||||
|     int count; |     int count; | ||||||
|     int capacityMask; // +1 to get the capacity |     int capacityMask; // +1 to get the capacity | ||||||
|     int tombstones; |     int tombstones; | ||||||
|   | |||||||
							
								
								
									
										58
									
								
								src/cvalue.c
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								src/cvalue.c
									
									
									
									
									
								
							| @@ -1,40 +1,50 @@ | |||||||
| #include "cosmo.h" |  | ||||||
| #include "cmem.h" |  | ||||||
| #include "cvalue.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->count = 0; | ||||||
|     val->capacity = startCapacity; |     val->capacity = startCapacity; | ||||||
|     val->values = NULL; |     val->values = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cleanValArray(CState *state, CValueArray *array) { | void cleanValArray(CState *state, CValueArray *array) | ||||||
|  | { | ||||||
|     cosmoM_freearray(state, CValue, array->values, array->capacity); |     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); |     cosmoM_growarray(state, CValue, array->values, array->count, array->capacity); | ||||||
|  |  | ||||||
|     array->values[array->count++] = val; |     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? |     if (GET_TYPE(valA) != GET_TYPE(valB)) // are they the same type? | ||||||
|         return false; |         return false; | ||||||
|  |  | ||||||
|     // compare |     // compare | ||||||
|     switch (GET_TYPE(valA)) { |     switch (GET_TYPE(valA)) { | ||||||
|         case COSMO_TBOOLEAN: return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB); |     case COSMO_TBOOLEAN: | ||||||
|         case COSMO_TNUMBER: return cosmoV_readNumber(valA) == cosmoV_readNumber(valB); |         return cosmoV_readBoolean(valA) == cosmoV_readBoolean(valB); | ||||||
|         case COSMO_TREF: return cosmoO_equal(state, cosmoV_readRef(valA), cosmoV_readRef(valB)); |     case COSMO_TNUMBER: | ||||||
|         case COSMO_TNIL: return true; |         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: |     default: | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjString *cosmoV_toString(CState *state, CValue val) { | CObjString *cosmoV_toString(CState *state, CValue val) | ||||||
|  | { | ||||||
|     switch (GET_TYPE(val)) { |     switch (GET_TYPE(val)) { | ||||||
|     case COSMO_TNUMBER: { |     case COSMO_TNUMBER: { | ||||||
|         char buf[32]; |         char buf[32]; | ||||||
| @@ -42,7 +52,8 @@ CObjString *cosmoV_toString(CState *state, CValue val) { | |||||||
|         return cosmoO_copyString(state, (char *)&buf, size); |         return cosmoO_copyString(state, (char *)&buf, size); | ||||||
|     } |     } | ||||||
|     case COSMO_TBOOLEAN: { |     case COSMO_TBOOLEAN: { | ||||||
|             return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) : cosmoO_copyString(state, "false", 5);             |         return cosmoV_readBoolean(val) ? cosmoO_copyString(state, "true", 4) | ||||||
|  |                                        : cosmoO_copyString(state, "false", 5); | ||||||
|     } |     } | ||||||
|     case COSMO_TREF: { |     case COSMO_TREF: { | ||||||
|         return cosmoO_toString(state, cosmoV_readRef(val)); |         return cosmoO_toString(state, cosmoV_readRef(val)); | ||||||
| @@ -55,7 +66,8 @@ CObjString *cosmoV_toString(CState *state, CValue val) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| cosmo_Number cosmoV_toNumber(CState *state, CValue val) { | cosmo_Number cosmoV_toNumber(CState *state, CValue val) | ||||||
|  | { | ||||||
|     switch (GET_TYPE(val)) { |     switch (GET_TYPE(val)) { | ||||||
|     case COSMO_TNUMBER: { |     case COSMO_TNUMBER: { | ||||||
|         return cosmoV_readNumber(val); |         return cosmoV_readNumber(val); | ||||||
| @@ -72,19 +84,25 @@ cosmo_Number cosmoV_toNumber(CState *state, CValue val) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| const char *cosmoV_typeStr(CValue val) { | const char *cosmoV_typeStr(CValue val) | ||||||
|  | { | ||||||
|     switch (GET_TYPE(val)) { |     switch (GET_TYPE(val)) { | ||||||
|         case COSMO_TNIL:        return "<nil>"; |     case COSMO_TNIL: | ||||||
|         case COSMO_TBOOLEAN:    return "<bool>"; |         return "<nil>"; | ||||||
|         case COSMO_TNUMBER:     return "<number>"; |     case COSMO_TBOOLEAN: | ||||||
|         case COSMO_TREF:        return cosmoO_typeStr(cosmoV_readRef(val)); |         return "<bool>"; | ||||||
|  |     case COSMO_TNUMBER: | ||||||
|  |         return "<number>"; | ||||||
|  |     case COSMO_TREF: | ||||||
|  |         return cosmoO_typeStr(cosmoV_readRef(val)); | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         return "<unkn val>"; |         return "<unkn val>"; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void printValue(CValue val) { | void printValue(CValue val) | ||||||
|  | { | ||||||
|     switch (GET_TYPE(val)) { |     switch (GET_TYPE(val)) { | ||||||
|     case COSMO_TNUMBER: |     case COSMO_TNUMBER: | ||||||
|         printf("%g", cosmoV_readNumber(val)); |         printf("%g", cosmoV_readNumber(val)); | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								src/cvalue.h
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								src/cvalue.h
									
									
									
									
									
								
							| @@ -3,7 +3,8 @@ | |||||||
|  |  | ||||||
| #include "cosmo.h" | #include "cosmo.h" | ||||||
|  |  | ||||||
| typedef enum { | typedef enum | ||||||
|  | { | ||||||
|     COSMO_TNUMBER, // number has to be 0 because NaN box |     COSMO_TNUMBER, // number has to be 0 because NaN box | ||||||
|     COSMO_TBOOLEAN, |     COSMO_TBOOLEAN, | ||||||
|     COSMO_TREF, |     COSMO_TREF, | ||||||
| @@ -18,8 +19,8 @@ typedef double cosmo_Number; | |||||||
|  |  | ||||||
| #ifdef NAN_BOXXED | #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 |         NaN box, this is great for fitting more in the cpu cache on x86_64 or ARM64 architectures. | ||||||
|     two articles: |    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/ |     https://leonardschuetz.ch/blog/nan-boxing/ and https://piotrduperas.com/posts/nan-boxing/ | ||||||
|  |  | ||||||
| @@ -27,7 +28,8 @@ typedef double cosmo_Number; | |||||||
|  |  | ||||||
|     TL;DR: we can store payloads in the NaN value in the IEEE 754 standard. |     TL;DR: we can store payloads in the NaN value in the IEEE 754 standard. | ||||||
| */ | */ | ||||||
| union CValue { | union CValue | ||||||
|  | { | ||||||
|     uint64_t data; |     uint64_t data; | ||||||
|     cosmo_Number num; |     cosmo_Number num; | ||||||
| }; | }; | ||||||
| @@ -43,7 +45,8 @@ union CValue { | |||||||
| #    define MASK_QUIETNAN   ((uint64_t)0x7ff8000000000000) | #    define MASK_QUIETNAN   ((uint64_t)0x7ff8000000000000) | ||||||
|  |  | ||||||
| #    define GET_TYPE(x)                                                                            \ | #    define GET_TYPE(x)                                                                            \ | ||||||
|     ((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48) : COSMO_TNUMBER) |         ((((x).data & MASK_QUIETNAN) == MASK_QUIETNAN) ? (((x).data & MASK_TYPE) >> 48)            \ | ||||||
|  |                                                        : COSMO_TNUMBER) | ||||||
|  |  | ||||||
| #    define SIG_MASK              (MASK_QUIETNAN | MASK_TYPE) | #    define SIG_MASK              (MASK_QUIETNAN | MASK_TYPE) | ||||||
| #    define BOOL_SIG              (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48)) | #    define BOOL_SIG              (MASK_QUIETNAN | ((uint64_t)(COSMO_TBOOLEAN) << 48)) | ||||||
| @@ -68,9 +71,11 @@ union CValue { | |||||||
| /* | /* | ||||||
|     Tagged union, this is the best platform independent solution |     Tagged union, this is the best platform independent solution | ||||||
| */ | */ | ||||||
| struct CValue { | struct CValue | ||||||
|  | { | ||||||
|     CosmoType type; |     CosmoType type; | ||||||
|     union { |     union | ||||||
|  |     { | ||||||
|         cosmo_Number num; |         cosmo_Number num; | ||||||
|         bool b; // boolean |         bool b; // boolean | ||||||
|         CObj *obj; |         CObj *obj; | ||||||
| @@ -103,7 +108,8 @@ struct CValue { | |||||||
|  |  | ||||||
| typedef CValue *StkPtr; | typedef CValue *StkPtr; | ||||||
|  |  | ||||||
| struct CValueArray { | struct CValueArray | ||||||
|  | { | ||||||
|     size_t capacity; |     size_t capacity; | ||||||
|     size_t count; |     size_t count; | ||||||
|     CValue *values; |     CValue *values; | ||||||
| @@ -117,6 +123,7 @@ void printValue(CValue val); | |||||||
| COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB); | COSMO_API bool cosmoV_equal(CState *state, CValue valA, CValue valB); | ||||||
| COSMO_API CObjString *cosmoV_toString(CState *state, CValue val); | COSMO_API CObjString *cosmoV_toString(CState *state, CValue val); | ||||||
| COSMO_API cosmo_Number cosmoV_toNumber(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 | #endif | ||||||
|   | |||||||
							
								
								
									
										254
									
								
								src/cvm.c
									
									
									
									
									
								
							
							
						
						
									
										254
									
								
								src/cvm.c
									
									
									
									
									
								
							| @@ -1,15 +1,16 @@ | |||||||
| #include "cvm.h" | #include "cvm.h" | ||||||
| #include "cstate.h" |  | ||||||
| #include "cdebug.h" | #include "cdebug.h" | ||||||
| #include "cmem.h" | #include "cmem.h" | ||||||
| #include "cparse.h" | #include "cparse.h" | ||||||
|  | #include "cstate.h" | ||||||
|  |  | ||||||
|  | #include <math.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
| #include <math.h> | COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) | ||||||
|  | { | ||||||
| COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) { |  | ||||||
|     va_list args; |     va_list args; | ||||||
|     va_start(args, format); |     va_start(args, format); | ||||||
|     cosmoO_pushVFString(state, format, args); |     cosmoO_pushVFString(state, format, args); | ||||||
| @@ -17,7 +18,8 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // inserts val at state->top - indx - 1, moving everything else up | // 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); |     StkPtr tmp = cosmoV_getTop(state, indx); | ||||||
|  |  | ||||||
|     // moves everything up |     // moves everything up | ||||||
| @@ -28,7 +30,8 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val) { | |||||||
|     state->top++; |     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; |     CObjFunction *func; | ||||||
|  |  | ||||||
|     if ((func = cosmoP_compileString(state, src, name)) != NULL) { |     if ((func = cosmoP_compileString(state, src, name)) != NULL) { | ||||||
| @@ -36,7 +39,8 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char * | |||||||
| #ifdef VM_DEBUG | #ifdef VM_DEBUG | ||||||
|         disasmChunk(&func->chunk, func->module->str, 0); |         disasmChunk(&func->chunk, func->module->str, 0); | ||||||
| #endif | #endif | ||||||
|         // push function onto the stack so it doesn't it cleaned up by the GC, at the same stack location put our closure |         // 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_pushRef(state, (CObj *)func); | ||||||
|         *(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func)); |         *(cosmoV_getTop(state, 0)) = cosmoV_newRef(cosmoO_newClosure(state, func)); | ||||||
|         return true; |         return true; | ||||||
| @@ -48,7 +52,8 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char * | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| COSMO_API void cosmoV_printError(CState *state, CObjError *err) { | COSMO_API void cosmoV_printError(CState *state, CObjError *err) | ||||||
|  | { | ||||||
|     // print stack trace |     // print stack trace | ||||||
|     for (int i = 0; i < err->frameCount; i++) { |     for (int i = 0; i < err->frameCount; i++) { | ||||||
|         CCallFrame *frame = &err->frames[i]; |         CCallFrame *frame = &err->frames[i]; | ||||||
| @@ -57,8 +62,11 @@ COSMO_API void cosmoV_printError(CState *state, CObjError *err) { | |||||||
|  |  | ||||||
|         int line = chunk->lineInfo[frame->pc - chunk->buf - 1]; |         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 |         if (i == err->frameCount - 1 && | ||||||
|             fprintf(stderr, "Objection in %.*s on [line %d] in ", function->module->length, function->module->str, line); |             !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 |         else | ||||||
|             fprintf(stderr, "[line %d] in ", line); |             fprintf(stderr, "[line %d] in ", line); | ||||||
|  |  | ||||||
| @@ -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 |     takes value on top of the stack and wraps an CObjError around it, state->error is set to that | ||||||
|     the value on the stack is *expected* to be a string, but not required, so |    value the value on the stack is *expected* to be a string, but not required, so yes, this means | ||||||
|     yes, this means you could throw a nil value if you really wanted too.. |    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); |     StkPtr temp = cosmoV_getTop(state, 0); | ||||||
|  |  | ||||||
|     CObjError *error = cosmoO_newError(state, *temp); |     CObjError *error = cosmoO_newError(state, *temp); | ||||||
| @@ -93,7 +102,8 @@ CObjError* cosmoV_throw(CState *state) { | |||||||
|     return error; |     return error; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoV_error(CState *state, const char *format, ...) { | void cosmoV_error(CState *state, const char *format, ...) | ||||||
|  | { | ||||||
|     if (state->panic) |     if (state->panic) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
| @@ -110,11 +120,13 @@ void cosmoV_error(CState *state, const char *format, ...) { | |||||||
|     cosmoV_throw(state); |     cosmoV_throw(state); | ||||||
| } | } | ||||||
|  |  | ||||||
| CObjUpval *captureUpvalue(CState *state, CValue *local) { | CObjUpval *captureUpvalue(CState *state, CValue *local) | ||||||
|  | { | ||||||
|     CObjUpval *prev = NULL; |     CObjUpval *prev = NULL; | ||||||
|     CObjUpval *upvalue = state->openUpvalues; |     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; |         prev = upvalue; | ||||||
|         upvalue = upvalue->next; |         upvalue = upvalue->next; | ||||||
|     } |     } | ||||||
| @@ -136,8 +148,11 @@ CObjUpval *captureUpvalue(CState *state, CValue *local) { | |||||||
|     return newUpval; |     return newUpval; | ||||||
| } | } | ||||||
|  |  | ||||||
| void closeUpvalues(CState *state, CValue *local) { | 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 | { | ||||||
|  |     while (state->openUpvalues != NULL && | ||||||
|  |            state->openUpvalues->val >= | ||||||
|  |                local) { // for every upvalue that points to the local or anything above it | ||||||
|         CObjUpval *upvalue = state->openUpvalues; |         CObjUpval *upvalue = state->openUpvalues; | ||||||
|         upvalue->closed = *upvalue->val; |         upvalue->closed = *upvalue->val; | ||||||
|         upvalue->val = &upvalue->closed; // upvalue now points to itself :P |         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 | #ifdef SAFE_STACK | ||||||
|     if (state->frameCount >= FRAME_MAX) { |     if (state->frameCount >= FRAME_MAX) { | ||||||
|         cosmoV_error(state, "Callframe overflow!"); |         cosmoV_error(state, "Callframe overflow!"); | ||||||
| @@ -159,15 +175,19 @@ void pushCallFrame(CState *state, CObjClosure *closure, int args) { | |||||||
|     frame->closure = closure; |     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) | // offset is the offset of the callframe base we set the state->top back too (useful for passing | ||||||
| void popCallFrame(CState *state, int offset) { | // values in the stack as arguments, like methods) | ||||||
|     closeUpvalues(state, state->callFrame[state->frameCount - 1].base); // close any upvalue still open | 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->top = state->callFrame[state->frameCount - 1].base + offset; // resets the stack | ||||||
|     state->frameCount--; |     state->frameCount--; | ||||||
| } | } | ||||||
|  |  | ||||||
| void cosmoV_concat(CState *state, int vals) { | void cosmoV_concat(CState *state, int vals) | ||||||
|  | { | ||||||
|     StkPtr start = state->top - vals; |     StkPtr start = state->top - vals; | ||||||
|     StkPtr end = cosmoV_getTop(state, 0); |     StkPtr end = cosmoV_getTop(state, 0); | ||||||
|  |  | ||||||
| @@ -197,21 +217,24 @@ 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: |     returns: | ||||||
|         false: state paniced during C Function, error is at state->error |         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); |     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); |     cosmoM_freezeGC(state); | ||||||
|     int nres = cfunc(state, args, savedBase + 1); |     int nres = cfunc(state, args, savedBase + 1); | ||||||
|     cosmoM_unfreezeGC(state); |     cosmoM_unfreezeGC(state); | ||||||
|  |  | ||||||
|  |  | ||||||
|     // caller function wasn't expecting this many return values, cap it |     // caller function wasn't expecting this many return values, cap it | ||||||
|     if (nres > nresults) |     if (nres > nresults) | ||||||
|         nres = nresults; |         nres = nresults; | ||||||
| @@ -226,7 +249,8 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre | |||||||
|         return false; |         return false; | ||||||
|  |  | ||||||
|     // push the return value back onto the stack |     // 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 |     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 |     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 |     // now, if the caller function expected more return values, push nils onto the stack | ||||||
| @@ -237,13 +261,16 @@ static bool callCFunction(CState *state, CosmoCFunction cfunc, int args, int nre | |||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|     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: |     returns: | ||||||
|         false: state paniced, error is at state->error |         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; |     CObjFunction *func = closure->function; | ||||||
|  |  | ||||||
|     // if the function is variadic and theres more args than parameters, push the args into a table |     // if the function is variadic and theres more args than parameters, push the args into a table | ||||||
| @@ -263,7 +290,9 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, | |||||||
|  |  | ||||||
|         pushCallFrame(state, closure, func->args + 1); |         pushCallFrame(state, closure, func->args + 1); | ||||||
|     } else if (args != func->args) { // mismatched args |     } 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; |         return false; | ||||||
|     } else { |     } else { | ||||||
|         // load function into callframe |         // load function into callframe | ||||||
| @@ -298,7 +327,8 @@ static bool rawCall(CState *state, CObjClosure *closure, int args, int nresults, | |||||||
|     return true; |     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 | #ifdef VM_DEBUG | ||||||
|     printf("\n"); |     printf("\n"); | ||||||
|     printIndent(state->frameCount - 1); |     printIndent(state->frameCount - 1); | ||||||
| @@ -343,7 +373,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset) | |||||||
|             cosmoV_pushRef(state, (CObj *)newObj); |             cosmoV_pushRef(state, (CObj *)newObj); | ||||||
|  |  | ||||||
|             // push the nils to fill up the expected return values |             // 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 |             for (int i = 0; i < nresults - 1; | ||||||
|  |                  i++) { // -1 since the we already pushed the important value | ||||||
|                 cosmoV_pushValue(state, cosmoV_newNil()); |                 cosmoV_pushValue(state, cosmoV_newNil()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -357,7 +388,8 @@ bool callCValue(CState *state, CValue func, int args, int nresults, int offset) | |||||||
|     return true; |     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 |     // first, set the first argument to the object | ||||||
|     StkPtr temp = cosmoV_getTop(state, args); |     StkPtr temp = cosmoV_getTop(state, args); | ||||||
|     *temp = cosmoV_newRef(obj); |     *temp = cosmoV_newRef(obj); | ||||||
| @@ -365,8 +397,10 @@ bool invokeMethod(CState* state, CObj *obj, CValue func, int args, int nresults, | |||||||
|     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 | // wraps cosmoV_call in a protected state, CObjError will be pushed onto the stack if function call | ||||||
| COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) { | // failed, else return values are passed | ||||||
|  | COSMOVMRESULT cosmoV_pcall(CState *state, int args, int nresults) | ||||||
|  | { | ||||||
|     StkPtr base = cosmoV_getTop(state, args); |     StkPtr base = cosmoV_getTop(state, args); | ||||||
|  |  | ||||||
|     if (!callCValue(state, *base, args, nresults, 0)) { |     if (!callCValue(state, *base, args, nresults, 0)) { | ||||||
| @@ -388,23 +422,27 @@ 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: |     returns: | ||||||
|         COSMOVM_OK: callable object exited normally |         COSMOVM_OK: callable object exited normally | ||||||
|         COSMOVM_RUNTIME_ERR: an error occurred, grab the error from state->error |         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 |     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; |     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)); |     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; |     StkPtr key, val; | ||||||
|     CObjObject *newObj = cosmoO_newObject(state); |     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 | ||||||
| @@ -424,7 +462,8 @@ COSMO_API CObjObject* cosmoV_makeObject(CState *state, int pairs) { | |||||||
|     return 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; |     bool replaced = state->protoObjects[objType] != NULL; | ||||||
|     state->protoObjects[objType] = obj; |     state->protoObjects[objType] = obj; | ||||||
|  |  | ||||||
| @@ -441,7 +480,8 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO | |||||||
|     return replaced; |     return replaced; | ||||||
| } | } | ||||||
|  |  | ||||||
| COSMO_API void cosmoV_makeTable(CState *state, int pairs) { | COSMO_API void cosmoV_makeTable(CState *state, int pairs) | ||||||
|  | { | ||||||
|     StkPtr key, val; |     StkPtr key, val; | ||||||
|     CObjTable *newObj = cosmoO_newTable(state); |     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 | ||||||
| @@ -460,13 +500,15 @@ COSMO_API void cosmoV_makeTable(CState *state, int pairs) { | |||||||
|     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); |     CObjObject *object = cosmoO_grabProto(_obj); | ||||||
|  |  | ||||||
|     // no proto to get from |     // no proto to get from | ||||||
|     if (object == NULL) { |     if (object == NULL) { | ||||||
|         CObjString *field = cosmoV_toString(state, key); |         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(); |         *val = cosmoV_newNil(); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @@ -483,13 +525,15 @@ bool cosmoV_rawget(CState *state, CObj *_obj, CValue key, CValue *val) { | |||||||
|     return false; |     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); |     CObjObject *object = cosmoO_grabProto(_obj); | ||||||
|  |  | ||||||
|     // no proto to set to |     // no proto to set to | ||||||
|     if (object == NULL) { |     if (object == NULL) { | ||||||
|         CObjString *field = cosmoV_toString(state, key); |         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; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -497,7 +541,8 @@ bool cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| COSMO_API bool cosmoV_get(CState *state) { | COSMO_API bool cosmoV_get(CState *state) | ||||||
|  | { | ||||||
|     CValue val; |     CValue val; | ||||||
|     StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first |     StkPtr obj = cosmoV_getTop(state, 1); // object was pushed first | ||||||
|     StkPtr key = cosmoV_getTop(state, 0); // then the key |     StkPtr key = cosmoV_getTop(state, 0); // then the key | ||||||
| @@ -517,7 +562,8 @@ COSMO_API bool cosmoV_get(CState *state) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // yes, this would technically make it possible to set fields of types other than <string>. go crazy | // yes, this would technically make it possible to set fields of types other than <string>. go crazy | ||||||
| COSMO_API bool cosmoV_set(CState *state) { | COSMO_API bool cosmoV_set(CState *state) | ||||||
|  | { | ||||||
|     StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first |     StkPtr obj = cosmoV_getTop(state, 2); // object was pushed first | ||||||
|     StkPtr key = cosmoV_getTop(state, 1); // then the key |     StkPtr key = cosmoV_getTop(state, 1); // then the key | ||||||
|     StkPtr val = cosmoV_getTop(state, 0); // and finally the value |     StkPtr val = cosmoV_getTop(state, 0); // and finally the value | ||||||
| @@ -535,7 +581,8 @@ COSMO_API bool cosmoV_set(CState *state) { | |||||||
|     return true; |     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)) |     if (!cosmoV_rawget(state, obj, key, val)) | ||||||
|         return false; |         return false; | ||||||
|  |  | ||||||
| @@ -551,7 +598,8 @@ COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *va | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| int _tbl__next(CState *state, int nargs, CValue *args) { | int _tbl__next(CState *state, int nargs, CValue *args) | ||||||
|  | { | ||||||
|     if (nargs != 1) { |     if (nargs != 1) { | ||||||
|         cosmoV_error(state, "Expected 1 parameter, %d received!", nargs); |         cosmoV_error(state, "Expected 1 parameter, %d received!", nargs); | ||||||
|         return 0; |         return 0; | ||||||
| @@ -569,7 +617,8 @@ int _tbl__next(CState *state, int nargs, CValue *args) { | |||||||
|     cosmoO_getIString(state, obj, ISTRING_RESERVED, &val); |     cosmoO_getIString(state, obj, ISTRING_RESERVED, &val); | ||||||
|  |  | ||||||
|     if (!IS_TABLE(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); | ||||||
| @@ -582,7 +631,8 @@ int _tbl__next(CState *state, int nargs, CValue *args) { | |||||||
|     } while (IS_NIL(entry->key) && index < cap); |     } while (IS_NIL(entry->key) && index < cap); | ||||||
|     cosmoO_setUserI(obj, index); // update the userdata |     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->key); | ||||||
|         cosmoV_pushValue(state, entry->val); |         cosmoV_pushValue(state, entry->val); | ||||||
|         return 2; // we pushed 2 values onto the stack for the return values |         return 2; // we pushed 2 values onto the stack for the return values | ||||||
| @@ -598,12 +648,14 @@ int _tbl__next(CState *state, int nargs, CValue *args) { | |||||||
|         cosmoV_setTop(state, 2); /* pop the 2 values */                                            \ |         cosmoV_setTop(state, 2); /* pop the 2 values */                                            \ | ||||||
|         cosmoV_pushValue(state, typeConst(cosmoV_readNumber(*valA) op cosmoV_readNumber(*valB)));  \ |         cosmoV_pushValue(state, typeConst(cosmoV_readNumber(*valA) op cosmoV_readNumber(*valB)));  \ | ||||||
|     } else {                                                                                       \ |     } else {                                                                                       \ | ||||||
|         cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); \ |         cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA),             \ | ||||||
|  |                      cosmoV_typeStr(*valB));                                                       \ | ||||||
|         return -1;                                                                                 \ |         return -1;                                                                                 \ | ||||||
|     } \ |     } | ||||||
|  |  | ||||||
| // returns -1 if panic | // returns -1 if panic | ||||||
| int cosmoV_execute(CState *state) { | int cosmoV_execute(CState *state) | ||||||
|  | { | ||||||
|     CCallFrame *frame = &state->callFrame[state->frameCount - 1];         // grabs the current frame |     CCallFrame *frame = &state->callFrame[state->frameCount - 1];         // grabs the current frame | ||||||
|     CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :) |     CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :) | ||||||
|  |  | ||||||
| @@ -613,7 +665,8 @@ int cosmoV_execute(CState *state) { | |||||||
|     while (!state->panic) { |     while (!state->panic) { | ||||||
| #ifdef VM_DEBUG | #ifdef VM_DEBUG | ||||||
|         cosmoV_printStack(state); |         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"); |         printf("\n"); | ||||||
| #endif | #endif | ||||||
|         switch (READBYTE()) { |         switch (READBYTE()) { | ||||||
| @@ -736,7 +789,8 @@ int cosmoV_execute(CState *state) { | |||||||
|                 val = cosmoV_getTop(state, i + 1); |                 val = cosmoV_getTop(state, i + 1); | ||||||
|  |  | ||||||
|                 // set key/value pair |                 // set key/value pair | ||||||
|                     CValue *newVal = cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1)); |                 CValue *newVal = | ||||||
|  |                     cosmoT_insert(state, &newObj->tbl, cosmoV_newNumber(pairs - i - 1)); | ||||||
|                 *newVal = *val; |                 *newVal = *val; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -761,14 +815,16 @@ int cosmoV_execute(CState *state) { | |||||||
|  |  | ||||||
|             if (proto != NULL) { |             if (proto != NULL) { | ||||||
|                 // check for __index metamethod |                 // check for __index metamethod | ||||||
|                     if (!cosmoO_indexObject(state, proto, *key, &val)) // if returns false, cosmoV_error was called |                 if (!cosmoO_indexObject(state, proto, *key, | ||||||
|  |                                         &val)) // if returns false, cosmoV_error was called | ||||||
|                     return -1; |                     return -1; | ||||||
|             } else if (obj->type == COBJ_TABLE) { |             } else if (obj->type == COBJ_TABLE) { | ||||||
|                 CObjTable *tbl = (CObjTable *)obj; |                 CObjTable *tbl = (CObjTable *)obj; | ||||||
|  |  | ||||||
|                 cosmoT_get(state, &tbl->tbl, *key, &val); |                 cosmoT_get(state, &tbl->tbl, *key, &val); | ||||||
|             } else { |             } else { | ||||||
|                     cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); |                 cosmoV_error(state, "No proto defined! Couldn't __index from type %s", | ||||||
|  |                              cosmoV_typeStr(*temp)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -791,7 +847,8 @@ int cosmoV_execute(CState *state) { | |||||||
|             CObjObject *proto = cosmoO_grabProto(obj); |             CObjObject *proto = cosmoO_grabProto(obj); | ||||||
|  |  | ||||||
|             if (proto != NULL) { |             if (proto != NULL) { | ||||||
|                     if (!cosmoO_newIndexObject(state, proto, *key, *value)) // if it returns false, cosmoV_error was called |                 if (!cosmoO_newIndexObject(state, proto, *key, | ||||||
|  |                                            *value)) // if it returns false, cosmoV_error was called | ||||||
|                     return -1; |                     return -1; | ||||||
|             } else if (obj->type == COBJ_TABLE) { |             } else if (obj->type == COBJ_TABLE) { | ||||||
|                 CObjTable *tbl = (CObjTable *)obj; |                 CObjTable *tbl = (CObjTable *)obj; | ||||||
| @@ -799,7 +856,8 @@ int cosmoV_execute(CState *state) { | |||||||
|  |  | ||||||
|                 *newVal = *value; // set the index |                 *newVal = *value; // set the index | ||||||
|             } else { |             } else { | ||||||
|                     cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", cosmoV_typeStr(*temp)); |                 cosmoV_error(state, "No proto defined! Couldn't __newindex from type %s", | ||||||
|  |                              cosmoV_typeStr(*temp)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -823,7 +881,8 @@ int cosmoV_execute(CState *state) { | |||||||
|                     return -1; |                     return -1; | ||||||
|             } else { |             } else { | ||||||
|                 CObjString *field = cosmoV_toString(state, constants[ident]); |                 CObjString *field = cosmoV_toString(state, constants[ident]); | ||||||
|                     cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, cosmoV_typeStr(*temp)); |                 cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, | ||||||
|  |                              cosmoV_typeStr(*temp)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -842,7 +901,8 @@ int cosmoV_execute(CState *state) { | |||||||
|                     return -1; |                     return -1; | ||||||
|             } else { |             } else { | ||||||
|                 CObjString *field = cosmoV_toString(state, constants[ident]); |                 CObjString *field = cosmoV_toString(state, constants[ident]); | ||||||
|                     cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp)); |                 cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, | ||||||
|  |                              cosmoV_typeStr(*temp)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -855,13 +915,15 @@ int cosmoV_execute(CState *state) { | |||||||
|             StkPtr temp = cosmoV_getTop(state, 0); // that should be the object |             StkPtr temp = cosmoV_getTop(state, 0); // that should be the object | ||||||
|             uint16_t ident = READUINT();           // use for the key |             uint16_t ident = READUINT();           // use for the key | ||||||
|  |  | ||||||
|                 // this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of just cosmoV_get |             // this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of | ||||||
|  |             // just cosmoV_get | ||||||
|             if (IS_REF(*temp)) { |             if (IS_REF(*temp)) { | ||||||
|                 if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val)) |                 if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val)) | ||||||
|                     return -1; |                     return -1; | ||||||
|             } else { |             } else { | ||||||
|                 CObjString *field = cosmoV_toString(state, constants[ident]); |                 CObjString *field = cosmoV_toString(state, constants[ident]); | ||||||
|                     cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp)); |                 cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, | ||||||
|  |                              cosmoV_typeStr(*temp)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -895,7 +957,8 @@ int cosmoV_execute(CState *state) { | |||||||
|             StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table |             StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table | ||||||
|  |  | ||||||
|             if (!IS_REF(*temp)) { |             if (!IS_REF(*temp)) { | ||||||
|                     cosmoV_error(state, "Couldn't iterate over non-iterator type %s!", cosmoV_typeStr(*temp)); |                 cosmoV_error(state, "Couldn't iterate over non-iterator type %s!", | ||||||
|  |                              cosmoV_typeStr(*temp)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -909,18 +972,23 @@ int cosmoV_execute(CState *state) { | |||||||
|                     cosmoV_pop(state); // pop the object from the stack |                     cosmoV_pop(state); // pop the object from the stack | ||||||
|                     cosmoV_pushValue(state, val); |                     cosmoV_pushValue(state, val); | ||||||
|                     cosmoV_pushRef(state, (CObj *)obj); |                     cosmoV_pushRef(state, (CObj *)obj); | ||||||
|                         if (cosmoV_call(state, 1, 1) != COSMOVM_OK) // we expect 1 return value on the stack, the iterable object |                     if (cosmoV_call(state, 1, 1) != | ||||||
|  |                         COSMOVM_OK) // we expect 1 return value on the stack, the iterable object | ||||||
|                         return -1; |                         return -1; | ||||||
|  |  | ||||||
|                     StkPtr iObj = cosmoV_getTop(state, 0); |                     StkPtr iObj = cosmoV_getTop(state, 0); | ||||||
|  |  | ||||||
|                     if (!IS_OBJECT(*iObj)) { |                     if (!IS_OBJECT(*iObj)) { | ||||||
|                             cosmoV_error(state, "Expected iterable object! '__iter' returned %s, expected <object>!", cosmoV_typeStr(*iObj)); |                         cosmoV_error( | ||||||
|  |                             state, | ||||||
|  |                             "Expected iterable object! '__iter' returned %s, expected <object>!", | ||||||
|  |                             cosmoV_typeStr(*iObj)); | ||||||
|                         return -1; |                         return -1; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     // get __next method and place it at the top of the stack |                     // 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); |                     cosmoV_getMethod(state, cosmoV_readRef(*iObj), | ||||||
|  |                                      cosmoV_newRef(state->iStrings[ISTRING_NEXT]), iObj); | ||||||
|                 } else { |                 } else { | ||||||
|                     cosmoV_error(state, "Expected iterable object! '__iter' not defined!"); |                     cosmoV_error(state, "Expected iterable object! '__iter' not defined!"); | ||||||
|                     return -1; |                     return -1; | ||||||
| @@ -944,7 +1012,8 @@ int cosmoV_execute(CState *state) { | |||||||
|                 cosmoV_setTop(state, 2);               // pops the object & the tbl |                 cosmoV_setTop(state, 2);               // pops the object & the tbl | ||||||
|                 cosmoV_pushRef(state, (CObj *)method); // pushes the method for OP_NEXT |                 cosmoV_pushRef(state, (CObj *)method); // pushes the method for OP_NEXT | ||||||
|             } else { |             } else { | ||||||
|                     cosmoV_error(state, "No proto defined! Couldn't get from type %s", cosmoO_typeStr(obj)); |                 cosmoV_error(state, "No proto defined! Couldn't get from type %s", | ||||||
|  |                              cosmoO_typeStr(obj)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -956,7 +1025,8 @@ int cosmoV_execute(CState *state) { | |||||||
|             StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack |             StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack | ||||||
|  |  | ||||||
|             if (!IS_METHOD(*temp)) { |             if (!IS_METHOD(*temp)) { | ||||||
|                     cosmoV_error(state, "Expected '__next' to be a method, got type %s!", cosmoV_typeStr(*temp)); |                 cosmoV_error(state, "Expected '__next' to be a method, got type %s!", | ||||||
|  |                              cosmoV_typeStr(*temp)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -964,7 +1034,8 @@ int cosmoV_execute(CState *state) { | |||||||
|             if (cosmoV_call(state, 0, nresults) != COSMOVM_OK) |             if (cosmoV_call(state, 0, nresults) != COSMOVM_OK) | ||||||
|                 return -1; |                 return -1; | ||||||
|  |  | ||||||
|                 if (IS_NIL(*(cosmoV_getTop(state, 0)))) { // __next returned a nil, which means to exit the loop |             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 |                 cosmoV_setTop(state, nresults); // pop the return values | ||||||
|                 frame->pc += jump; |                 frame->pc += jump; | ||||||
|             } |             } | ||||||
| @@ -991,9 +1062,11 @@ int cosmoV_execute(CState *state) { | |||||||
|             StkPtr valB = cosmoV_getTop(state, 0); |             StkPtr valB = cosmoV_getTop(state, 0); | ||||||
|             if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { |             if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { | ||||||
|                 cosmoV_setTop(state, 2); /* pop the 2 values */ |                 cosmoV_setTop(state, 2); /* pop the 2 values */ | ||||||
|                     cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB)))); |                 cosmoV_pushValue(state, cosmoV_newNumber(fmod(cosmoV_readNumber(*valA), | ||||||
|  |                                                               cosmoV_readNumber(*valB)))); | ||||||
|             } else { |             } else { | ||||||
|                     cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); |                 cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), | ||||||
|  |                              cosmoV_typeStr(*valB)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|             continue; |             continue; | ||||||
| @@ -1003,9 +1076,11 @@ int cosmoV_execute(CState *state) { | |||||||
|             StkPtr valB = cosmoV_getTop(state, 0); |             StkPtr valB = cosmoV_getTop(state, 0); | ||||||
|             if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { |             if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { | ||||||
|                 cosmoV_setTop(state, 2); /* pop the 2 values */ |                 cosmoV_setTop(state, 2); /* pop the 2 values */ | ||||||
|                     cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA), cosmoV_readNumber(*valB)))); |                 cosmoV_pushValue(state, cosmoV_newNumber(pow(cosmoV_readNumber(*valA), | ||||||
|  |                                                              cosmoV_readNumber(*valB)))); | ||||||
|             } else { |             } else { | ||||||
|                     cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); |                 cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), | ||||||
|  |                              cosmoV_typeStr(*valB)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|             continue; |             continue; | ||||||
| @@ -1119,7 +1194,8 @@ int cosmoV_execute(CState *state) { | |||||||
|                     cosmoV_pushValue(state, val); // pushes old value onto the stack :) |                     cosmoV_pushValue(state, val); // pushes old value onto the stack :) | ||||||
|  |  | ||||||
|                     // call __newindex |                     // call __newindex | ||||||
|                         if (!cosmoO_newIndexObject(state, proto, *key, cosmoV_newNumber(cosmoV_readNumber(val) + inc))) |                     if (!cosmoO_newIndexObject(state, proto, *key, | ||||||
|  |                                                cosmoV_newNumber(cosmoV_readNumber(val) + inc))) | ||||||
|                         return -1; |                         return -1; | ||||||
|                 } else |                 } else | ||||||
|                     return -1; // cosmoO_indexObject failed and threw an error |                     return -1; // cosmoO_indexObject failed and threw an error | ||||||
| @@ -1137,7 +1213,8 @@ int cosmoV_execute(CState *state) { | |||||||
|                 cosmoV_pushValue(state, *val); // pushes old value onto the stack :) |                 cosmoV_pushValue(state, *val); // pushes old value onto the stack :) | ||||||
|                 *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index |                 *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); // sets table index | ||||||
|             } else { |             } else { | ||||||
|                     cosmoV_error(state, "No proto defined! Couldn't __index from type %s", cosmoV_typeStr(*temp)); |                 cosmoV_error(state, "No proto defined! Couldn't __index from type %s", | ||||||
|  |                              cosmoV_typeStr(*temp)); | ||||||
|                 return -1; |                 return -1; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -1163,7 +1240,8 @@ int cosmoV_execute(CState *state) { | |||||||
|                 // check that it's a number value |                 // check that it's a number value | ||||||
|                 if (IS_NUMBER(val)) { |                 if (IS_NUMBER(val)) { | ||||||
|                     cosmoV_pushValue(state, val); // pushes old value onto the stack :) |                     cosmoV_pushValue(state, val); // pushes old value onto the stack :) | ||||||
|                         if (!cosmoV_rawset(state, obj, ident, cosmoV_newNumber(cosmoV_readNumber(val) + inc))) |                     if (!cosmoV_rawset(state, obj, ident, | ||||||
|  |                                        cosmoV_newNumber(cosmoV_readNumber(val) + inc))) | ||||||
|                         return -1; |                         return -1; | ||||||
|                 } else { |                 } else { | ||||||
|                     cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); |                     cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); | ||||||
| @@ -1201,9 +1279,15 @@ int cosmoV_execute(CState *state) { | |||||||
|             NUMBEROP(cosmoV_newBoolean, <=) |             NUMBEROP(cosmoV_newBoolean, <=) | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|             case OP_TRUE:   cosmoV_pushBoolean(state, true); continue; |         case OP_TRUE: | ||||||
|             case OP_FALSE:  cosmoV_pushBoolean(state, false); continue; |             cosmoV_pushBoolean(state, true); | ||||||
|             case OP_NIL:    cosmoV_pushValue(state, cosmoV_newNil()); continue; |             continue; | ||||||
|  |         case OP_FALSE: | ||||||
|  |             cosmoV_pushBoolean(state, false); | ||||||
|  |             continue; | ||||||
|  |         case OP_NIL: | ||||||
|  |             cosmoV_pushValue(state, cosmoV_newNil()); | ||||||
|  |             continue; | ||||||
|         case OP_RETURN: { |         case OP_RETURN: { | ||||||
|             uint8_t res = READBYTE(); |             uint8_t res = READBYTE(); | ||||||
|             return res; |             return res; | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								src/cvm.h
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								src/cvm.h
									
									
									
									
									
								
							| @@ -1,14 +1,15 @@ | |||||||
| #ifndef COSMOVM_H | #ifndef COSMOVM_H | ||||||
| #define COSMOVM_H | #define COSMOVM_H | ||||||
|  |  | ||||||
| #include <string.h> |  | ||||||
|  |  | ||||||
| #include "cosmo.h" | #include "cosmo.h" | ||||||
| #include "cstate.h" | #include "cstate.h" | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
| // #define VM_DEBUG | // #define VM_DEBUG | ||||||
|  |  | ||||||
| typedef enum { | typedef enum | ||||||
|  | { | ||||||
|     COSMOVM_OK, |     COSMOVM_OK, | ||||||
|     COSMOVM_RUNTIME_ERR, |     COSMOVM_RUNTIME_ERR, | ||||||
|     COSMOVM_BUILDTIME_ERR |     COSMOVM_BUILDTIME_ERR | ||||||
| @@ -29,15 +30,16 @@ COSMO_API void cosmoV_error(CState *state, const char *format, ...); | |||||||
| COSMO_API void cosmo_insert(CState *state, int indx, CValue val); | 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 |     Sets the default proto objects for the passed objType. Also walks through the object heap and | ||||||
|     for the passed objType if that CObj* has no proto. |    updates protos for the passed objType if that CObj* has no proto. | ||||||
|  |  | ||||||
|     returns true if replacing a previously registered proto object for this type |     returns true if replacing a previously registered proto object for this type | ||||||
| */ | */ | ||||||
| COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj); | COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj); | ||||||
|  |  | ||||||
| /* | /* | ||||||
|     compiles string into a <closure>, if successful, <closure> will be pushed onto the stack otherwise the <error> will be pushed. |     compiles string into a <closure>, if successful, <closure> will be pushed onto the stack | ||||||
|  |    otherwise the <error> will be pushed. | ||||||
|  |  | ||||||
|     returns: |     returns: | ||||||
|         false : <error> is at the top of the stack |         false : <error> is at the top of the stack | ||||||
| @@ -48,28 +50,32 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char * | |||||||
| /* | /* | ||||||
|     expects object to be pushed, then the key. |     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 |     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); | COSMO_API bool cosmoV_get(CState *state); | ||||||
|  |  | ||||||
| /* | /* | ||||||
|     expects object to be pushed, then the key, and finally the new value. |     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 |     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); | 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); | 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 | // clears the stack, callstack and restores the state into a usable state after a calloverflow or | ||||||
| // (keeps the global table intact) | // another hard to recover error (keeps the global table intact) | ||||||
| COSMO_API bool cosmoV_restore(CState *state); | COSMO_API bool cosmoV_restore(CState *state); | ||||||
|  |  | ||||||
| // nice to have wrappers | // 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) | // pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the | ||||||
| static inline void cosmoV_pushValue(CState *state, CValue val) { | // SAFE_STACK macro on) | ||||||
|  | static inline void cosmoV_pushValue(CState *state, CValue val) | ||||||
|  | { | ||||||
| #ifdef SAFE_STACK | #ifdef SAFE_STACK | ||||||
|     ptrdiff_t stackSize = state->top - state->stack; |     ptrdiff_t stackSize = state->top - state->stack; | ||||||
|  |  | ||||||
| @@ -91,51 +97,61 @@ static inline void cosmoV_pushValue(CState *state, CValue val) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // sets stack->top to stack->top - indx | // 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; |     state->top -= indx; | ||||||
|     return state->top; |     return state->top; | ||||||
| } | } | ||||||
|  |  | ||||||
| // returns stack->top - indx - 1 | // 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)]; |     return &state->top[-(indx + 1)]; | ||||||
| } | } | ||||||
|  |  | ||||||
| // pops 1 value off the stack, returns the popped value | // 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); |     return cosmoV_setTop(state, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| // pushes a cosmo_Number to the stack | // 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)); |     cosmoV_pushValue(state, cosmoV_newNumber(num)); | ||||||
| } | } | ||||||
|  |  | ||||||
| // pushes a boolean to the stack | // 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)); |     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)); |     cosmoV_pushValue(state, cosmoV_newRef(obj)); | ||||||
| } | } | ||||||
|  |  | ||||||
| // pushes a C Function to the stack | // pushes a C Function to the stack | ||||||
| static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) { | static inline void cosmoV_pushCFunction(CState *state, CosmoCFunction func) | ||||||
|  | { | ||||||
|     cosmoV_pushRef(state, (CObj *)cosmoO_newCFunction(state, func)); |     cosmoV_pushRef(state, (CObj *)cosmoO_newCFunction(state, func)); | ||||||
| } | } | ||||||
|  |  | ||||||
| // len is the length of the string without the NULL terminator | // len is the length of the string without the NULL terminator | ||||||
| static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) { | static inline void cosmoV_pushLString(CState *state, const char *str, size_t len) | ||||||
|  | { | ||||||
|     cosmoV_pushRef(state, (CObj *)cosmoO_copyString(state, str, len)); |     cosmoV_pushRef(state, (CObj *)cosmoO_copyString(state, str, len)); | ||||||
| } | } | ||||||
|  |  | ||||||
| // accepts a null terminated string and copys the buffer to the VM heap | // 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)); |     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()); |     cosmoV_pushValue(state, cosmoV_newNil()); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user