Compare commits

...

7 Commits

Author SHA1 Message Date
f51e072922 removed more debug prints 2023-09-05 14:45:03 -05:00
b66858d286 removed debug prints, oops 2023-09-05 14:43:50 -05:00
34c55eee87 fix makefile 2023-09-05 14:42:29 -05:00
a2d0b79c2e documented the new os.open and file objects 2023-09-05 14:41:59 -05:00
4de274d760 added os.open(); new file object
eg.
```lua
local err, file = os.open("LICENSE.md")
if err then
    // do error handling stuff
end

// passing "a" to read() will read the whole file
print(file:read("a"))
```
2023-09-05 14:38:24 -05:00
074e8b52cc removed 'roots', replaced with a registry table
- removed cosmoM_addRoot
- removed cosmoM_removeRoot
- renamed cosmoV_register to cosmoV_addGlobals
- added cosmoV_addRegistry
- added cosmoV_getRegistry
- added cosmoV_setProto
- added cosmoV_isValueUserType
2023-09-05 14:35:29 -05:00
56161a32e7 capture freezeGC in CPanic 2023-09-05 02:23:31 -05:00
14 changed files with 279 additions and 138 deletions

View File

@ -1,7 +1,7 @@
# make clean && make && ./bin/cosmo # make clean && make && ./bin/cosmo
CC=clang CC=clang
CFLAGS=-fPIE -Wall -Isrc -O3 -std=c99 #-g -fsanitize=address CFLAGS=-fPIE -Wall -Isrc -O3 #-g -fsanitize=address
LDFLAGS=-lm #-fsanitize=address LDFLAGS=-lm #-fsanitize=address
OUT=bin/cosmo OUT=bin/cosmo

View File

@ -59,7 +59,12 @@ Includes functions that interact with the operating system.
| Name | Type | Behavior | Example | | Name | Type | Behavior | Example |
| ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ | | ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ |
| os.read | `(path<string>)` -> `<string>` or `<nil>` | Returns a file's contents or nil if it doesn't exist/an error occurred | `os.read("path")` -> `Hello, World!`| | os.open | `(path<string>)` -> `<bool>, <obj>` | Opens a file at `path` and returns a file object. If the file does not exist, it will be created. | `os.open("test.txt")` -> `true, <file>` |
| os.time | `()` -> `<number>` | Returns the system time in Epoch format | `os.time()` -> `1.61691e+09` | | os.time | `()` -> `<number>` | Returns the system time in Epoch format | `os.time()` -> `1.61691e+09` |
| os.system | `(cmd<string>)` -> `<number>` | Runs a system command as if it were a terminal and returns the exit code | `os.system("mkdir test")` -> `0` | | os.system | `(cmd<string>)` -> `<number>` | Runs a system command as if it were a terminal and returns the exit code | `os.system("mkdir test")` -> `0` |
> -> means 'returns' > -> means 'returns'
File objects have the following methods:
| Name | Type | Behavior | Example |
| ------------ | ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------ |
| file:read | `(amt<number> or "a")` -> `<string>` | Reads `amt` bytes from the file and returns them as a string. If `"a"` is passed, the entire file is read. | `file:read("a")` -> `"Hello world!"` |

9
examples/reader.cosmo Normal file
View File

@ -0,0 +1,9 @@
local err, file = os.open("LICENSE.md")
print("made file")
if err then
print("failed to open file")
end
print(file)
print(file:read("a"))

4
main.c
View File

@ -75,7 +75,7 @@ static void repl(CState *state)
cosmoV_pushString(state, "input"); cosmoV_pushString(state, "input");
cosmoV_pushCFunction(state, cosmoB_input); cosmoV_pushCFunction(state, cosmoB_input);
cosmoV_register(state, 2); cosmoV_addGlobals(state, 2);
while (_ACTIVE) { while (_ACTIVE) {
if (!(line = linenoise("> "))) { // better than gets() if (!(line = linenoise("> "))) { // better than gets()
@ -130,7 +130,7 @@ static bool runFile(CState *state, const char *fileName)
cosmoV_pushString(state, "input"); cosmoV_pushString(state, "input");
cosmoV_pushCFunction(state, cosmoB_input); cosmoV_pushCFunction(state, cosmoB_input);
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
ret = interpret(state, script, fileName); ret = interpret(state, script, fileName);

View File

@ -69,7 +69,7 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args)
bool res = cosmoV_pcall(state, nargs - 1, 1); bool res = cosmoV_pcall(state, nargs - 1, 1);
// insert false before the result // insert false before the result
cosmo_insert(state, 0, cosmoV_newBoolean(res)); cosmoV_insert(state, 0, cosmoV_newBoolean(res));
return 2; return 2;
} }
@ -105,7 +105,7 @@ int cosmoB_loadstring(CState *state, int nargs, CValue *args)
CObjString *str = cosmoV_readString(args[0]); CObjString *str = cosmoV_readString(args[0]);
bool res = cosmoV_compileString(state, str->str, ""); bool res = cosmoV_compileString(state, str->str, "");
cosmo_insert(state, 0, cosmoV_newBoolean(res)); cosmoV_insert(state, 0, cosmoV_newBoolean(res));
return 2; // <boolean>, <closure> or <error> return 2; // <boolean>, <closure> or <error>
} }
@ -139,7 +139,7 @@ void cosmoB_loadLibrary(CState *state)
} }
// register all the pushed c functions and the strings as globals // register all the pushed c functions and the strings as globals
cosmoV_register(state, i); cosmoV_addGlobals(state, i);
// load other libraries // load other libraries
cosmoB_loadObjLib(state); cosmoB_loadObjLib(state);
@ -230,52 +230,129 @@ COSMO_API void cosmoB_loadObjLib(CState *state)
cosmoV_registerProtoObject(state, COBJ_OBJECT, obj); cosmoV_registerProtoObject(state, COBJ_OBJECT, obj);
// register "object" to the global table // register "object" to the global table
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
} }
// ================================================================ [OS.*] // ================================================================ [OS.*]
// os.read() int fileB_read(CState *state, int nargs, CValue *args)
int cosmoB_osRead(CState *state, int nargs, CValue *args)
{ {
if (nargs != 2) {
cosmoV_error(state, "file:read() expected 2 arguments, got %d!", nargs);
}
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "file:read()", "<file>, <number> or \"a\"", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
CObjObject *fileObj = cosmoV_readObject(args[0]);
FILE *file = cosmoO_getUserP(fileObj);
if (IS_NUMBER(args[1])) {
cosmo_Number length = cosmoV_readNumber(args[1]);
// make sure the length is within the bounds of the file
if (length < 0) {
cosmoV_error(state, "file:read() expected length to be >= 0, got %d!", length);
}
// allocate a buffer for the read data
char *buffer = cosmoM_xmalloc(state, length + 1);
// read the data
fread(buffer, sizeof(char), length, file);
// push the read data
CValue temp = cosmoV_newRef(cosmoO_takeString(state, buffer, length));
cosmoV_pushValue(state, temp);
} else if (IS_STRING(args[1])) {
if (strcmp(cosmoV_readCString(args[1]), "a") == 0) {
// get the length of the file
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
// allocate a buffer for the read data
char *buffer = cosmoM_xmalloc(state, length + 1);
// read the data
fread(buffer, sizeof(char), length, file);
buffer[length] = '\0'; // write the NULL terminator
// push the read data
CValue temp = cosmoV_newRef(cosmoO_takeString(state, buffer, length));
cosmoV_pushValue(state, temp);
} else {
cosmoV_error(state, "file:read() expected \"a\" or <number>, got %s!",
cosmoV_readCString(args[1]));
}
} else {
cosmoV_typeError(state, "file:read()", "<file>, <number> or \"a\"", "%s, %s",
cosmoV_typeStr(args[0]), cosmoV_typeStr(args[1]));
}
return 1;
}
int fileB_gc(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, "file:read() expected 1 argument, got %d!", nargs);
}
if (!cosmoV_isValueUserType(state, args[0], COSMO_USER_FILE)) {
cosmoV_typeError(state, "file:__gc()", "<file>", "%s",
cosmoV_typeStr(args[0]));
}
CObjObject *fileObj = cosmoV_readObject(args[0]);
FILE *file = cosmoO_getUserP(fileObj);
fclose(file);
return 0;
}
CObjObject *pushFileObj(CState *state, FILE *file)
{
CObjObject *fileObj = cosmoO_newObject(state);
cosmoV_pushRef(state, (CObj *)fileObj);
cosmoO_setUserP(fileObj, file);
cosmoO_setUserT(fileObj, COSMO_USER_FILE);
// grab and set proto
cosmoV_pushRef(state, (CObj *)fileObj);
cosmoV_pushString(state, "file");
cosmoV_getRegistry(state);
cosmoV_setProto(state);
cosmoO_lock(fileObj);
return fileObj;
}
int cosmoB_osOpen(CState *state, int nargs, CValue *args)
{
FILE *file;
if (nargs != 1) {
cosmoV_error(state, "os.open() expected 1 argument, got %d!", nargs);
} }
if (!IS_STRING(args[0])) { if (!IS_STRING(args[0])) {
cosmoV_typeError(state, "os.read()", "<string>", "%s", cosmoV_typeStr(args[0])); cosmoV_typeError(state, "os.open()", "<string>", "%s", cosmoV_typeStr(args[0]));
} }
CObjString *str = cosmoV_readString(args[0]); const char *filePath = cosmoV_readCString(args[0]);
file = fopen(filePath, "rb");
// open file
FILE *file = fopen(str->str, "rb");
char *buf;
size_t size, bRead;
if (file == NULL) { if (file == NULL) {
// return nil, file doesn't exist cosmoV_pushBoolean(state, true);
return 0; cosmoV_pushFString(state, "Failed to open %s!", filePath);
return 2;
} }
// grab the size of the file cosmoV_pushBoolean(state, false);
fseek(file, 0L, SEEK_END); pushFileObj(state, file);
size = ftell(file); return 2;
rewind(file);
buf = cosmoM_xmalloc(state, size + 1); // +1 for the NULL terminator
bRead = fread(buf, sizeof(char), size, file); // read the file into the buffer
if (bRead < size) {
// an error occured! we don't need to really throw an error, returning a nil is good enough
return 0;
}
buf[bRead] = '\0'; // place the NULL terminator at the end of the buffer
// push the string to the stack to return
cosmoV_pushValue(state, cosmoV_newRef(cosmoO_takeString(state, buf, bRead)));
return 1;
} }
// os.time() // os.time()
@ -309,9 +386,8 @@ int cosmoB_osSystem(CState *state, int nargs, CValue *args)
COSMO_API void cosmoB_loadOS(CState *state) COSMO_API void cosmoB_loadOS(CState *state)
{ {
const char *identifiers[] = {"read", "time", "system"}; const char *identifiers[] = {"open", "time", "system"};
CosmoCFunction osLib[] = {cosmoB_osOpen, cosmoB_osTime, cosmoB_osSystem};
CosmoCFunction osLib[] = {cosmoB_osRead, cosmoB_osTime, cosmoB_osSystem};
cosmoV_pushString(state, "os"); cosmoV_pushString(state, "os");
@ -322,7 +398,25 @@ COSMO_API void cosmoB_loadOS(CState *state)
} }
cosmoV_makeObject(state, i); cosmoV_makeObject(state, i);
cosmoV_register(state, 1); // register the os.* object to the global table cosmoV_addGlobals(state, 1); // register the os.* object to the global table
// make file proto
cosmoV_pushString(state, "file");
CObjObject *fileProto = cosmoO_newObject(state);
cosmoV_pushRef(state, (CObj *)fileProto);
cosmoV_pushRef(state, (CObj *)fileProto);
cosmoV_pushString(state, "read");
cosmoV_pushCFunction(state, fileB_read);
cosmoV_set(state);
cosmoV_pushRef(state, (CObj *)fileProto);
cosmoV_pushString(state, "__gc");
cosmoV_pushCFunction(state, fileB_gc);
cosmoV_set(state);
cosmoV_addRegistry(state, 1);
} }
// ================================================================ [STRING.*] // ================================================================ [STRING.*]
@ -578,7 +672,7 @@ void cosmoB_loadStrLib(CState *state)
cosmoV_registerProtoObject(state, COBJ_STRING, obj); cosmoV_registerProtoObject(state, COBJ_STRING, obj);
// register "string" to the global table // register "string" to the global table
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
} }
// ================================================================ [MATH] // ================================================================ [MATH]
@ -773,7 +867,7 @@ void cosmoB_loadMathLib(CState *state)
// make the object and register it as a global to the state // make the object and register it as a global to the state
cosmoV_makeObject(state, i); cosmoV_makeObject(state, i);
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
} }
// ================================================================ [VM.*] // ================================================================ [VM.*]
@ -924,5 +1018,5 @@ void cosmoB_loadVM(CState *state)
cosmoV_makeObject(state, 5); // makes the vm object cosmoV_makeObject(state, 5); // makes the vm object
// register "vm" to the global table // register "vm" to the global table
cosmoV_register(state, 1); cosmoV_addGlobals(state, 1);
} }

View File

@ -3,6 +3,13 @@
#include "cstate.h" #include "cstate.h"
enum
{
COSMO_USER_NONE, // CObjObject is not a userdata object
COSMO_USER_FILE, // CObjObject is a file object (see cosmoB_osOpen)
COSMO_USER_START // the first user type for user-defined userdata
};
/* loads all of the base library, including: /* loads all of the base library, including:
- base library ("print", "assert", "type", "pcall", "loadstring", etc.) - base library ("print", "assert", "type", "pcall", "loadstring", etc.)
- object library - object library

View File

@ -6,6 +6,7 @@
#include "cstate.h" #include "cstate.h"
#include "ctable.h" #include "ctable.h"
#include "cvalue.h" #include "cvalue.h"
#include "cvm.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)
@ -246,22 +247,23 @@ static void sweep(CState *state)
prev->next = object; prev->next = object;
} }
// call __gc on the object
CObjObject *protoObject = cosmoO_grabProto(oldObj);
CValue res;
// use user-defined __gc
if (protoObject != NULL && cosmoO_getIString(state, protoObject, ISTRING_GC, &res)) {
cosmoV_pushValue(state, res);
cosmoV_pushRef(state, (CObj *)oldObj);
cosmoV_call(state, 1, 0);
}
cosmoO_free(state, oldObj); cosmoO_free(state, oldObj);
} }
} }
} }
static void markUserRoots(CState *state)
{
CObj *root = state->userRoots;
// traverse userRoots and mark all the object
while (root != NULL) {
markObject(state, root);
root = root->nextRoot;
}
}
static void markRoots(CState *state) static void markRoots(CState *state)
{ {
// mark all values on the stack // mark all values on the stack
@ -285,8 +287,7 @@ static void markRoots(CState *state)
for (int i = 0; i < ISTRING_MAX; i++) for (int i = 0; i < ISTRING_MAX; i++)
markObject(state, (CObj *)state->iStrings[i]); markObject(state, (CObj *)state->iStrings[i]);
// mark the user defined roots markTable(state, &state->registry);
markUserRoots(state);
for (int i = 0; i < COBJ_MAX; i++) for (int i = 0; i < COBJ_MAX; i++)
markObject(state, (CObj *)state->protoObjects[i]); markObject(state, (CObj *)state->protoObjects[i]);
@ -323,44 +324,3 @@ 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)
{
// first, check and make sure this root doesn't already exist in the list
CObj *root = state->userRoots;
while (root != NULL) {
if (root == newRoot) // found in the list, abort
return;
root = root->nextRoot;
}
// adds root to userRoot linked list
newRoot->nextRoot = state->userRoots;
state->userRoots = newRoot;
}
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot)
{
CObj *prev = NULL;
CObj *root = state->userRoots;
// traverse the userRoot linked list
while (root != NULL) {
if (root == oldRoot) { // found root in list
// remove from the linked list
if (prev == NULL) {
state->userRoots = root->nextRoot;
} else {
prev->nextRoot = root->nextRoot;
}
root->nextRoot = NULL;
break;
}
prev = root;
root = root->nextRoot;
}
}

View File

@ -67,13 +67,6 @@ COSMO_API bool cosmoM_checkGarbage(CState *state,
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);
// lets the VM know you are holding a reference to a CObj and to not free it
// NOTE: prefer to use the stack when possible
COSMO_API void cosmoM_addRoot(CState *state, CObj *newRoot);
// lets the VM know this root is no longer held in a reference and is able to be freed
COSMO_API void cosmoM_removeRoot(CState *state, CObj *oldRoot);
// wrapper for cosmoM_reallocate so we can track our memory usage // wrapper for cosmoM_reallocate so we can track our memory usage
static inline void *cosmoM_xmalloc(CState *state, size_t sz) static inline void *cosmoM_xmalloc(CState *state, size_t sz)
{ {

View File

@ -31,7 +31,6 @@ CObj *cosmoO_allocateBase(CState *state, size_t sz, CObjType type)
obj->next = state->objects; obj->next = state->objects;
state->objects = obj; state->objects = obj;
obj->nextRoot = NULL;
#ifdef GC_DEBUG #ifdef GC_DEBUG
printf("allocated %s %p\n", cosmoO_typeStr(obj), obj); printf("allocated %s %p\n", cosmoO_typeStr(obj), obj);
#endif #endif
@ -51,9 +50,9 @@ void cosmoO_free(CState *state, CObj *obj)
break; break;
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
CObjObject *objTbl = (CObjObject *)obj; CObjObject *objObj = (CObjObject *)obj;
cosmoT_clearTable(state, &objTbl->tbl); cosmoT_clearTable(state, &objObj->tbl);
cosmoM_free(state, CObjObject, objTbl); cosmoM_free(state, CObjObject, objObj);
break; break;
} }
case COBJ_TABLE: { case COBJ_TABLE: {
@ -492,6 +491,7 @@ 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))

View File

@ -34,7 +34,6 @@ 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 CObjObject *proto; // protoobject, describes the behavior of the object struct CObjObject *proto; // protoobject, describes the behavior of the object
CObjType type; CObjType type;
bool isMarked; // for the GC bool isMarked; // for the GC

View File

@ -12,6 +12,7 @@ CPanic *cosmoV_newPanic(CState *state)
CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic)); CPanic *panic = cosmoM_xmalloc(state, sizeof(CPanic));
panic->top = state->top; panic->top = state->top;
panic->frameCount = state->frameCount; panic->frameCount = state->frameCount;
panic->freezeGC = state->freezeGC;
panic->prev = state->panic; panic->prev = state->panic;
state->panic = panic; state->panic = panic;
@ -41,7 +42,6 @@ CState *cosmoV_newState()
// GC // GC
state->objects = NULL; state->objects = NULL;
state->userRoots = NULL;
state->grayStack.count = 0; state->grayStack.count = 0;
state->grayStack.capacity = 2; state->grayStack.capacity = 2;
state->grayStack.array = NULL; state->grayStack.array = NULL;
@ -61,11 +61,13 @@ CState *cosmoV_newState()
state->iStrings[i] = NULL; state->iStrings[i] = NULL;
cosmoT_initTable(state, &state->strings, 16); // init string table cosmoT_initTable(state, &state->strings, 16); // init string table
cosmoT_initTable(state, &state->registry, 16);
state->globals = cosmoO_newTable(state); // init global table state->globals = cosmoO_newTable(state); // init global table
// setup all strings used by the VM // setup all strings used by the VM
state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6); state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6);
state->iStrings[ISTRING_GC] = cosmoO_copyString(state, "__gc", 4);
state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10); state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10);
state->iStrings[ISTRING_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10); state->iStrings[ISTRING_TONUMBER] = cosmoO_copyString(state, "__tonumber", 10);
state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7); state->iStrings[ISTRING_INDEX] = cosmoO_copyString(state, "__index", 7);
@ -119,6 +121,8 @@ void cosmoV_freeState(CState *state)
// free our string table (the string table includes the internal VM strings) // free our string table (the string table includes the internal VM strings)
cosmoT_clearTable(state, &state->strings); cosmoT_clearTable(state, &state->strings);
cosmoT_clearTable(state, &state->registry);
// free our gray stack & finally free the state structure // free our gray stack & finally free the state structure
cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity); cosmoM_freeArray(state, CObj *, state->grayStack.array, state->grayStack.capacity);
@ -132,7 +136,7 @@ void cosmoV_freeState(CState *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_addGlobals(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);
@ -145,6 +149,48 @@ void cosmoV_register(CState *state, int pairs)
} }
} }
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
void cosmoV_addRegistry(CState *state, int pairs)
{
for (int i = 0; i < pairs; i++) {
StkPtr key = cosmoV_getTop(state, 1);
StkPtr val = cosmoV_getTop(state, 0);
CValue *oldVal = cosmoT_insert(state, &state->registry, *key);
*oldVal = *val;
cosmoV_setTop(state, 2); // pops the 2 values off the stack
}
}
// expects 1 key on the stack, pushes result
void cosmoV_getRegistry(CState *state) {
CValue key = *cosmoV_pop(state);
CValue val;
if (!cosmoT_get(state, &state->registry, key, &val)) {
cosmoV_error(state, "failed to grab %s from registry", cosmoV_typeStr(key));
}
cosmoV_pushValue(state, val);
}
void cosmoV_setProto(CState *state) {
StkPtr objVal = cosmoV_getTop(state, 1);
StkPtr protoVal = cosmoV_getTop(state, 0);
if (!IS_REF(*objVal) || !IS_OBJECT(*protoVal)) {
cosmoV_error(state, "cannot set %s to proto of type %s", cosmoV_typeStr(*objVal), cosmoV_typeStr(*protoVal));
}
// actually set the protos
CObj *obj = cosmoV_readRef(*objVal);
CObjObject *proto = cosmoV_readObject(*protoVal);
obj->proto = proto;
cosmoV_setTop(state, 2);
}
void cosmoV_printStack(CState *state) void cosmoV_printStack(CState *state)
{ {
printf("==== [[ stack dump ]] ====\n"); printf("==== [[ stack dump ]] ====\n");

View File

@ -18,6 +18,7 @@ struct CCallFrame
typedef enum IStringEnum typedef enum IStringEnum
{ {
ISTRING_INIT, // __init ISTRING_INIT, // __init
ISTRING_GC, // __gc
ISTRING_TOSTRING, // __tostring ISTRING_TOSTRING, // __tostring
ISTRING_TONUMBER, // __tonumber ISTRING_TONUMBER, // __tonumber
ISTRING_EQUAL, // __equals ISTRING_EQUAL, // __equals
@ -46,6 +47,7 @@ typedef struct CPanic
StkPtr top; StkPtr top;
struct CPanic *prev; struct CPanic *prev;
int frameCount; int frameCount;
int freezeGC;
} CPanic; } CPanic;
struct CState struct CState
@ -55,6 +57,7 @@ struct CState
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 CObjString *iStrings[ISTRING_MAX]; // strings used internally by the VM, eg. __init, __index
CTable strings; CTable strings;
CTable registry;
ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but ArrayCObj grayStack; // keeps track of which objects *haven't yet* been traversed in our GC, but
// *have been* found // *have been* found
@ -62,14 +65,12 @@ struct CState
CObjTable *globals; CObjTable *globals;
CValue *top; // top of the stack CValue *top; // top of the stack
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
CPanic *panic; CPanic *panic;
int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount;
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
int freezeGC; // when > 0, GC events will be ignored (for internal use)
int frameCount;
}; };
CPanic *cosmoV_newPanic(CState *state); CPanic *cosmoV_newPanic(CState *state);
@ -79,7 +80,16 @@ COSMO_API CState *cosmoV_newState();
COSMO_API void cosmoV_freeState(CState *state); COSMO_API void cosmoV_freeState(CState *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
COSMO_API void cosmoV_register(CState *state, int pairs); COSMO_API void cosmoV_addGlobals(CState *state, int pairs);
// expects 2*pairs values on the stack, each pair should consist of 1 key and 1 value
COSMO_API void cosmoV_addRegistry(CState *state, int pairs);
// expects 1 key on the stack, pushes result
COSMO_API void cosmoV_getRegistry(CState *state);
// expects <object>->proto = <object> (2 total) to be on the stack
COSMO_API void cosmoV_setProto(CState *state);
COSMO_API void cosmoV_printStack(CState *state); COSMO_API void cosmoV_printStack(CState *state);

View File

@ -12,7 +12,7 @@
#define cosmoV_protect(panic) setjmp(panic->jmp) == 0 #define cosmoV_protect(panic) setjmp(panic->jmp) == 0
COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...) void cosmoV_pushFString(CState *state, const char *format, ...)
{ {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
@ -21,7 +21,7 @@ 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) void cosmoV_insert(CState *state, int indx, CValue val)
{ {
StkPtr tmp = cosmoV_getTop(state, indx); StkPtr tmp = cosmoV_getTop(state, indx);
@ -33,7 +33,7 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val)
state->top++; state->top++;
} }
COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud) bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
{ {
CObjFunction *func; CObjFunction *func;
@ -54,7 +54,7 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
// returns false if failed, error will be on the top of the stack. true if successful, closure will // returns false if failed, error will be on the top of the stack. true if successful, closure will
// be on the top of the stack // be on the top of the stack
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) bool cosmoV_compileString(CState *state, const char *src, const char *name)
{ {
CObjFunction *func; CObjFunction *func;
CPanic *panic = cosmoV_newPanic(state); CPanic *panic = cosmoV_newPanic(state);
@ -77,7 +77,7 @@ 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) 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++) {
@ -122,6 +122,7 @@ void cosmoV_throw(CState *state)
if (state->panic) { if (state->panic) {
state->top = state->panic->top; state->top = state->panic->top;
state->frameCount = state->panic->frameCount; state->frameCount = state->panic->frameCount;
state->freezeGC = state->panic->freezeGC;
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
longjmp(state->panic->jmp, 1); longjmp(state->panic->jmp, 1);
} else { } else {
@ -438,7 +439,7 @@ 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) CObjObject *cosmoV_makeObject(CState *state, int pairs)
{ {
StkPtr key, val; StkPtr key, val;
CObjObject *newObj = cosmoO_newObject(state); CObjObject *newObj = cosmoO_newObject(state);
@ -459,7 +460,7 @@ COSMO_API CObjObject *cosmoV_makeObject(CState *state, int pairs)
return newObj; return newObj;
} }
COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjObject *obj) 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;
@ -468,7 +469,7 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
CObj *curr = state->objects; CObj *curr = state->objects;
while (curr != NULL) { while (curr != NULL) {
// update the proto // update the proto
if (curr->type == objType && curr->proto != NULL) { if (curr != (CObj *)obj && curr->type == objType && curr->proto != NULL) {
curr->proto = obj; curr->proto = obj;
} }
curr = curr->next; curr = curr->next;
@ -477,7 +478,7 @@ COSMO_API bool cosmoV_registerProtoObject(CState *state, CObjType objType, CObjO
return replaced; return replaced;
} }
COSMO_API void cosmoV_makeTable(CState *state, int pairs) void cosmoV_makeTable(CState *state, int pairs)
{ {
StkPtr key, val; StkPtr key, val;
CObjTable *newObj = cosmoO_newTable(state); CObjTable *newObj = cosmoO_newTable(state);
@ -529,7 +530,7 @@ void cosmoV_rawset(CState *state, CObj *_obj, CValue key, CValue val)
cosmoO_setRawObject(state, object, key, val, _obj); cosmoO_setRawObject(state, object, key, val, _obj);
} }
COSMO_API void cosmoV_get(CState *state) void 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
@ -547,7 +548,7 @@ COSMO_API void 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 void cosmoV_set(CState *state) void 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
@ -563,7 +564,7 @@ COSMO_API void cosmoV_set(CState *state)
cosmoV_setTop(state, 3); cosmoV_setTop(state, 3);
} }
COSMO_API void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val)
{ {
cosmoV_rawget(state, obj, key, val); cosmoV_rawget(state, obj, key, val);
@ -577,6 +578,19 @@ COSMO_API void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *va
} }
} }
bool cosmoV_isValueUserType(CState *state, CValue val, int userType) {
if (!IS_OBJECT(val)) {
return false;
}
CObjObject *obj = cosmoV_readObject(val);
if (obj->userT != userType) {
return false;
}
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) {
@ -835,7 +849,8 @@ int cosmoV_execute(CState *state)
CObj *obj = cosmoV_readRef(*temp); CObj *obj = cosmoV_readRef(*temp);
CObjObject *proto = cosmoO_grabProto(obj); CObjObject *proto = cosmoO_grabProto(obj);
CValue val; // to hold our value CValue val = cosmoV_newNil(); // to hold our value
if (proto != NULL) { if (proto != NULL) {
// check for __index metamethod // check for __index metamethod
@ -906,7 +921,7 @@ int cosmoV_execute(CState *state)
} }
CASE(OP_GETOBJECT) : CASE(OP_GETOBJECT) :
{ {
CValue val; // to hold our value CValue val = cosmoV_newNil(); // to hold our value
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(frame); // use for the key uint16_t ident = READUINT(frame); // use for the key
@ -924,7 +939,7 @@ int cosmoV_execute(CState *state)
} }
CASE(OP_GETMETHOD) : CASE(OP_GETMETHOD) :
{ {
CValue val; // to hold our value CValue val = cosmoV_newNil(); // to hold our value
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(frame); // use for the key uint16_t ident = READUINT(frame); // use for the key

View File

@ -36,11 +36,11 @@ COSMO_API void cosmoV_pushFString(CState *state, const char *format, ...);
COSMO_API void cosmoV_printError(CState *state, CObjError *err); COSMO_API void cosmoV_printError(CState *state, CObjError *err);
COSMO_API void cosmoV_throw(CState *state); COSMO_API void cosmoV_throw(CState *state);
COSMO_API void cosmoV_error(CState *state, const char *format, ...); COSMO_API void cosmoV_error(CState *state, const char *format, ...);
COSMO_API void cosmo_insert(CState *state, int indx, CValue val); COSMO_API void cosmoV_insert(CState *state, int indx, CValue val);
/* /*
Sets the default proto objects for the passed objType. Also walks through the object heap and Sets the default proto objects for the passed objType. Also walks through the object heap and
updates protos for the passed objType if that CObj* has no proto. 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
*/ */
@ -72,13 +72,16 @@ COSMO_API bool cosmoV_undump(CState *state, cosmo_Reader reader, const void *ud)
COSMO_API void cosmoV_get(CState *state); COSMO_API void cosmoV_get(CState *state);
/* /*
expects object to be pushed, then the key, and finally the new value. pops the key & object expects object to be pushed, then the key, and finally the new value. pops the object, key & value
*/ */
COSMO_API void cosmoV_set(CState *state); COSMO_API void 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 void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val); COSMO_API void cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
// check if the value at the top of the stack is a <obj> user type
COSMO_API bool cosmoV_isValueUserType(CState *state, CValue val, int userType);
// 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 // pushes a raw CValue to the stack, might throw an error if the stack is overflowed (with the