mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-05 08:10:05 +00:00
Minor table refactor, added cosmoV_compileString and loadstring() to baselib
cosmoV_compileString is recommended since it'll push the result (<error> or <closure>) onto the stack. also, fixed some GC-related bugs, so yay!
This commit is contained in:
parent
8dc8cef7dc
commit
c510c9aebf
@ -57,6 +57,24 @@ int cosmoB_pcall(CState *state, int nargs, CValue *args) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int cosmoB_loadstring(CState *state, int nargs, CValue *args) {
|
||||
if (nargs < 1) {
|
||||
cosmoV_error(state, "loadstring() expected 1 argument, got %d!", nargs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IS_STRING(args[0])) {
|
||||
cosmoV_typeError(state, "loadstring()", "<string>", "%s", cosmoV_typeStr(args[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CObjString *str = cosmoV_readString(args[0]);
|
||||
bool res = cosmoV_compileString(state, str->str, "");
|
||||
|
||||
cosmo_insert(state, 0, cosmoV_newBoolean(res));
|
||||
return 2; // <boolean>, <closure> or <error>
|
||||
}
|
||||
|
||||
// ================================================================ [STRING.*] ================================================================
|
||||
|
||||
// string.sub
|
||||
@ -103,23 +121,32 @@ int cosmoB_sSub(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
void cosmoB_loadLibrary(CState *state) {
|
||||
// print
|
||||
cosmoV_pushString(state, "print");
|
||||
cosmoV_pushCFunction(state, cosmoB_print);
|
||||
const char *identifiers[] = {
|
||||
"print",
|
||||
"assert",
|
||||
"type",
|
||||
"pcall",
|
||||
"loadstring"
|
||||
};
|
||||
|
||||
// assert (for unit testing)
|
||||
cosmoV_pushString(state, "assert");
|
||||
cosmoV_pushCFunction(state, cosmoB_assert);
|
||||
CosmoCFunction baseLib[] = {
|
||||
cosmoB_print,
|
||||
cosmoB_assert,
|
||||
cosmoB_type,
|
||||
cosmoB_pcall,
|
||||
cosmoB_loadstring
|
||||
};
|
||||
|
||||
// type
|
||||
cosmoV_pushString(state, "type");
|
||||
cosmoV_pushCFunction(state, cosmoB_type);
|
||||
int i;
|
||||
for (i = 0; i < sizeof(identifiers)/sizeof(identifiers[0]); i++) {
|
||||
cosmoV_pushString(state, identifiers[i]);
|
||||
cosmoV_pushCFunction(state, baseLib[i]);
|
||||
}
|
||||
|
||||
// pcall
|
||||
cosmoV_pushString(state, "pcall");
|
||||
cosmoV_pushCFunction(state, cosmoB_pcall);
|
||||
// register all the pushed c functions and the strings as globals
|
||||
cosmoV_register(state, i);
|
||||
|
||||
// string.
|
||||
// string.*
|
||||
cosmoV_pushString(state, "string");
|
||||
|
||||
// sub
|
||||
@ -127,19 +154,19 @@ void cosmoB_loadLibrary(CState *state) {
|
||||
cosmoV_pushCFunction(state, cosmoB_sSub);
|
||||
|
||||
cosmoV_makeTable(state, 1);
|
||||
// string.
|
||||
// string.*
|
||||
|
||||
// register these all to the global table
|
||||
cosmoV_register(state, 5);
|
||||
// register "string" to that table
|
||||
cosmoV_register(state, 1);
|
||||
|
||||
// make string object for CObjStrings
|
||||
|
||||
// sub
|
||||
cosmoV_pushString(state, "sub");
|
||||
cosmoV_pushCFunction(state, cosmoB_sSub);
|
||||
|
||||
cosmoV_makeObject(state, 1);
|
||||
|
||||
// grab the object from the stack and set the base protoObject
|
||||
StkPtr obj = cosmoV_pop(state);
|
||||
state->protoObjects[COBJ_STRING] = cosmoV_readObject(*obj);
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ void markTable(CState *state, CTable *tbl) {
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
for (int i = 0; i < tbl->capacity; i++) {
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
markValue(state, entry->key);
|
||||
markValue(state, entry->val);
|
||||
@ -67,7 +68,8 @@ void tableRemoveWhite(CState *state, CTable *tbl) {
|
||||
if (tbl->table == NULL) // table is still being initialized
|
||||
return;
|
||||
|
||||
for (int i = 0; i < tbl->capacity; i++) {
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (IS_OBJ(entry->key) && !(cosmoV_readObj(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);
|
||||
|
@ -1587,20 +1587,19 @@ CObjFunction* cosmoP_compileString(CState *state, const char *source, const char
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
|
||||
// the VM still expects a result on the stack
|
||||
cosmoV_pushValue(state, cosmoV_newNil());
|
||||
cosmoM_unfreezeGC(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CObjFunction* resFunc = compiler.function;
|
||||
|
||||
// VM expects the closure on the stack :P (we do this before ending the compiler so our GC doesn't free it)
|
||||
cosmoV_pushValue(state, cosmoV_newObj((CObj*)cosmoO_newClosure(state, resFunc)));
|
||||
|
||||
// finally free out parser states
|
||||
endCompiler(&parser);
|
||||
freeParseState(&parser);
|
||||
|
||||
// push the funciton onto the stack so if we cause an GC event, it won't be free'd
|
||||
cosmoV_pushValue(state, cosmoV_newObj(resFunc));
|
||||
cosmoM_unfreezeGC(state);
|
||||
cosmoV_pop(state);
|
||||
return resFunc;
|
||||
}
|
||||
|
12
src/cstate.c
12
src/cstate.c
@ -34,13 +34,17 @@ CState *cosmoV_newState() {
|
||||
|
||||
state->error = NULL;
|
||||
|
||||
cosmoT_initTable(state, &state->strings, 8); // init string table
|
||||
cosmoT_initTable(state, &state->globals, 8); // init global table
|
||||
// set default proto objects
|
||||
for (int i = 0; i < COBJ_MAX; i++)
|
||||
state->protoObjects[i] = NULL;
|
||||
|
||||
// first, set all strings to NULL so our GC doesn't read garbage data
|
||||
for (int i = 0; i < ISTRING_MAX; i++)
|
||||
state->iStrings[i] = NULL;
|
||||
|
||||
cosmoT_initTable(state, &state->strings, 8); // init string table
|
||||
cosmoT_initTable(state, &state->globals, 8); // init global table
|
||||
|
||||
// setup all strings used by the VM
|
||||
state->iStrings[ISTRING_INIT] = cosmoO_copyString(state, "__init", 6);
|
||||
state->iStrings[ISTRING_TOSTRING] = cosmoO_copyString(state, "__tostring", 10);
|
||||
@ -62,10 +66,6 @@ CState *cosmoV_newState() {
|
||||
for (int i = 0; i < ISTRING_MAX; i++)
|
||||
state->iStrings[i]->isIString = true;
|
||||
|
||||
// set default proto objects
|
||||
for (int i = 0; i < COBJ_MAX; i++)
|
||||
state->protoObjects[i] = NULL;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
44
src/ctable.c
44
src/ctable.c
@ -24,21 +24,24 @@ unsigned int nextPow2(unsigned int x) {
|
||||
}
|
||||
|
||||
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
|
||||
tbl->capacity = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
|
||||
startCap = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
|
||||
|
||||
tbl->capacityMask = startCap - 1;
|
||||
tbl->count = 0;
|
||||
tbl->tombstones = 0;
|
||||
tbl->table = NULL; // to let out GC know we're initalizing
|
||||
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * tbl->capacity);
|
||||
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap);
|
||||
|
||||
// init everything to NIL
|
||||
for (int i = 0; i < tbl->capacity; i++) {
|
||||
for (int i = 0; i < startCap; i++) {
|
||||
tbl->table[i].key = cosmoV_newNil();
|
||||
tbl->table[i].val = cosmoV_newNil();
|
||||
}
|
||||
}
|
||||
|
||||
void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
|
||||
for (int i = 0; i < from->capacity; i++) {
|
||||
int cap = from->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &from->table[i];
|
||||
|
||||
if (!(IS_NIL(entry->key))) {
|
||||
@ -49,7 +52,7 @@ void cosmoT_addTable(CState *state, CTable *from, CTable *to) {
|
||||
}
|
||||
|
||||
void cosmoT_clearTable(CState *state, CTable *tbl) {
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, tbl->capacity);
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, (tbl->capacityMask + 1));
|
||||
}
|
||||
|
||||
uint32_t getObjectHash(CObj *obj) {
|
||||
@ -57,7 +60,7 @@ uint32_t getObjectHash(CObj *obj) {
|
||||
case COBJ_STRING:
|
||||
return ((CObjString*)obj)->hash;
|
||||
default:
|
||||
return 0;
|
||||
return (uint32_t)obj; // just "hash" the pointer
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,13 +118,16 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
|
||||
size_t size = sizeof(CTableEntry) * newCapacity;
|
||||
int cachedCount = tbl->count;
|
||||
int newCount, oldCap;
|
||||
|
||||
cosmoM_checkGarbage(state, size); // if this allocation would cause a GC, run the GC
|
||||
|
||||
if (tbl->count < cachedCount) // the GC removed some objects from this table and resized it, ignore our resize event!
|
||||
return;
|
||||
|
||||
CTableEntry *entries = cosmoM_xmalloc(state, size);
|
||||
int newCount = 0;
|
||||
oldCap = tbl->capacityMask + 1;
|
||||
newCount = 0;
|
||||
|
||||
// set all nodes as NIL : NIL
|
||||
for (int i = 0; i < newCapacity; i++) {
|
||||
@ -130,7 +136,7 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
}
|
||||
|
||||
// move over old values to the new buffer
|
||||
for (int i = 0; i < tbl->capacity; i++) {
|
||||
for (int i = 0; i < oldCap; i++) {
|
||||
CTableEntry *oldEntry = &tbl->table[i];
|
||||
if (IS_NIL(oldEntry->key))
|
||||
continue; // skip empty keys
|
||||
@ -143,10 +149,10 @@ static void resizeTbl(CState *state, CTable *tbl, int newCapacity, bool canShrin
|
||||
}
|
||||
|
||||
// free the old table
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, tbl->capacity);
|
||||
cosmoM_freearray(state, CTableEntry, tbl->table, oldCap);
|
||||
|
||||
tbl->table = entries;
|
||||
tbl->capacity = newCapacity;
|
||||
tbl->capacityMask = newCapacity - 1;
|
||||
tbl->count = newCount;
|
||||
tbl->tombstones = 0;
|
||||
}
|
||||
@ -164,14 +170,15 @@ bool cosmoT_checkShrink(CState *state, CTable *tbl) {
|
||||
// returns a pointer to the allocated value
|
||||
COSMO_API CValue* cosmoT_insert(CState *state, CTable *tbl, CValue key) {
|
||||
// make sure we have enough space allocated
|
||||
if (tbl->count + 1 > (int)(tbl->capacity * MAX_TABLE_FILL)) {
|
||||
int cap = tbl->capacityMask + 1;
|
||||
if (tbl->count + 1 > (int)(cap * MAX_TABLE_FILL)) {
|
||||
// grow table
|
||||
int newCap = tbl->capacity * GROW_FACTOR;
|
||||
int newCap = cap * GROW_FACTOR;
|
||||
resizeTbl(state, tbl, newCap, true);
|
||||
}
|
||||
|
||||
// insert into the table
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacity - 1, key); // -1 for our capacity mask
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key); // -1 for our capacity mask
|
||||
|
||||
if (IS_NIL(entry->key)) {
|
||||
if (IS_NIL(entry->val)) // is it empty?
|
||||
@ -191,7 +198,7 @@ bool cosmoT_get(CTable *tbl, CValue key, CValue *val) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacity - 1, key);
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key);
|
||||
*val = entry->val;
|
||||
|
||||
// return if get was successful
|
||||
@ -201,7 +208,7 @@ bool cosmoT_get(CTable *tbl, CValue key, CValue *val) {
|
||||
bool cosmoT_remove(CState* state, CTable *tbl, CValue key) {
|
||||
if (tbl->count == 0) return 0; // sanity check
|
||||
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacity - 1, key);
|
||||
CTableEntry *entry = findEntry(tbl->table, tbl->capacityMask, key);
|
||||
if (IS_NIL(entry->key)) // sanity check
|
||||
return false;
|
||||
|
||||
@ -220,7 +227,7 @@ COSMO_API int cosmoT_count(CTable *tbl) {
|
||||
|
||||
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->capacity - 1); // 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 & tbl->capacityMask; // since we know the capacity will *always* be a power of 2, we can use bitwise & to perform a MUCH faster mod operation
|
||||
|
||||
// keep looking for an open slot in the entries array
|
||||
while (true) {
|
||||
@ -234,14 +241,15 @@ CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32
|
||||
return (CObjString*)cosmoV_readObj(entry->key);
|
||||
}
|
||||
|
||||
indx = (indx + 1) & (tbl->capacity - 1); // fast mod here too
|
||||
indx = (indx + 1) & tbl->capacityMask; // fast mod here too
|
||||
}
|
||||
}
|
||||
|
||||
// for debugging purposes
|
||||
void cosmoT_printTable(CTable *tbl, const char *name) {
|
||||
printf("==== [[%s]] ====\n", name);
|
||||
for (int i = 0; i < tbl->capacity; i++) {
|
||||
int cap = tbl->capacityMask + 1;
|
||||
for (int i = 0; i < cap; i++) {
|
||||
CTableEntry *entry = &tbl->table[i];
|
||||
if (!(IS_NIL(entry->key))) {
|
||||
printValue(entry->key);
|
||||
|
@ -13,20 +13,19 @@ typedef struct CTableEntry {
|
||||
|
||||
typedef struct CTable {
|
||||
int count;
|
||||
int capacity;
|
||||
int capacityMask; // +1 to get the capacity
|
||||
int tombstones;
|
||||
CTableEntry *table;
|
||||
} CTable;
|
||||
|
||||
COSMO_API void cosmoT_initTable(CState *state, CTable *tbl, int startCap);
|
||||
COSMO_API void cosmoT_clearTable(CState *state, CTable *tbl);
|
||||
COSMO_API void cosmoT_addTable(CState *state, CTable *from, CTable *to);
|
||||
COSMO_API int cosmoT_count(CTable *tbl);
|
||||
|
||||
bool cosmoT_checkShrink(CState *state, CTable *tbl);
|
||||
|
||||
CObjString *cosmoT_lookupString(CTable *tbl, const char *str, int length, uint32_t hash);
|
||||
COSMO_API CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key);
|
||||
CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key);
|
||||
bool cosmoT_get(CTable *tbl, CValue key, CValue *val);
|
||||
bool cosmoT_remove(CState *state, CTable *tbl, CValue key);
|
||||
|
||||
|
25
src/cvm.c
25
src/cvm.c
@ -2,6 +2,7 @@
|
||||
#include "cstate.h"
|
||||
#include "cdebug.h"
|
||||
#include "cmem.h"
|
||||
#include "cparse.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
@ -27,7 +28,26 @@ COSMO_API void cosmo_insert(CState *state, int indx, CValue val) {
|
||||
state->top++;
|
||||
}
|
||||
|
||||
void cosmoV_printError(CState *state, CObjError *err) {
|
||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name) {
|
||||
CObjFunction *func;
|
||||
|
||||
if ((func = cosmoP_compileString(state, src, name)) != NULL) {
|
||||
// success
|
||||
disasmChunk(&func->chunk, func->module->str, 0);
|
||||
|
||||
// push function onto the stack so it doesn't it cleaned up by the GC, at the same stack location put our closure
|
||||
cosmoV_pushValue(state, cosmoV_newObj(func));
|
||||
*(cosmoV_getTop(state, 0)) = cosmoV_newObj(cosmoO_newClosure(state, func));
|
||||
return true;
|
||||
}
|
||||
|
||||
// fail
|
||||
state->panic = false;
|
||||
cosmoV_pushValue(state, cosmoV_newObj(state->error));
|
||||
return false;
|
||||
}
|
||||
|
||||
COSMO_API void cosmoV_printError(CState *state, CObjError *err) {
|
||||
// print stack trace
|
||||
for (int i = 0; i < err->frameCount; i++) {
|
||||
CCallFrame *frame = &err->frames[i];
|
||||
@ -473,10 +493,11 @@ int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||
CObjTable *table = (CObjTable*)cosmoV_readObj(val);
|
||||
|
||||
// while the entry is invalid, go to the next entry
|
||||
int cap = table->tbl.capacityMask + 1;
|
||||
CTableEntry *entry;
|
||||
do {
|
||||
entry = &table->tbl.table[index++];
|
||||
} while (IS_NIL(entry->key) && index < table->tbl.capacity);
|
||||
} while (IS_NIL(entry->key) && index < cap);
|
||||
cosmoO_setUserI(state, obj, index); // update the userdata
|
||||
|
||||
if (!IS_NIL(entry->key)) { // if the entry is valid, return it's key and value pair
|
||||
|
@ -24,6 +24,15 @@ COSMO_API CObjError* cosmoV_throw(CState *state);
|
||||
COSMO_API void cosmoV_error(CState *state, const char *format, ...);
|
||||
COSMO_API void cosmo_insert(CState *state, int indx, CValue val);
|
||||
|
||||
/*
|
||||
compiles string into a <closure>, if successful, <closure> will be pushed onto the stack otherwise the <error> will be pushed.
|
||||
|
||||
returns:
|
||||
false : <error> is at the top of the stack
|
||||
true : <closure> is at the top of the stack
|
||||
*/
|
||||
COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *name);
|
||||
|
||||
COSMO_API bool cosmoV_get(CState *state, CObj *obj, CValue key, CValue *val);
|
||||
COSMO_API bool cosmoV_set(CState *state, CObj *obj, CValue key, CValue val);
|
||||
|
||||
|
@ -32,16 +32,14 @@ int cosmoB_input(CState *state, int nargs, CValue *args) {
|
||||
}
|
||||
|
||||
static void interpret(CState *state, const char *script, const char *mod) {
|
||||
// cosmoP_compileString pushes the result onto the stack (NIL or COBJ_CLOSURE)
|
||||
CObjFunction* func = cosmoP_compileString(state, script, mod);
|
||||
if (func != NULL) {
|
||||
disasmChunk(&func->chunk, func->name != NULL ? func->name->str : "_main", 0);
|
||||
|
||||
// cosmoV_compileString pushes the result onto the stack (COBJ_ERROR or COBJ_CLOSURE)
|
||||
if (cosmoV_compileString(state, script, mod)) {
|
||||
COSMOVMRESULT res = cosmoV_call(state, 0, 0); // 0 args being passed, 0 results expected
|
||||
|
||||
if (res == COSMOVM_RUNTIME_ERR)
|
||||
cosmoV_printError(state, state->error);
|
||||
} else {
|
||||
cosmoV_pop(state); // pop the error off the stack
|
||||
cosmoV_printError(state, state->error);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user