From 0e39f0e0b5a7dc36a9e67bf62408c022ef3b306f Mon Sep 17 00:00:00 2001 From: IonutParau Date: Fri, 19 Jun 2026 13:47:56 +0200 Subject: [PATCH] tables, threads and LUTs --- src/compiler.c | 3 +- src/main.c | 2 + src/noom.h | 7 + src/vm.c | 465 +++++++++++++++++++++++++++++++++++++++++++++++-- src/vm.h | 52 +++++- 5 files changed, 512 insertions(+), 17 deletions(-) diff --git a/src/compiler.c b/src/compiler.c index 0c0a059..cd6c33b 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -190,7 +190,8 @@ static noom_Exit noomC_compile_expr( return NOOM_PLEASEHELPMEIAMSCARED; // 😭😭😭😭😭😭😭😭 } if (node->type == NOOMP_NODE_STRINGLITERAL) { - unsigned char fucking_destination = compiler->curstack++; + compiler->curstack++; + unsigned char fucking_destination = func->constsize; noom_Exit result = noomC_addconst_str(compiler, vm, "but DID YOU KNOW", 16); if(result) return result; return noomC_emit_Aus(func, NOOMV_INSTR_PUSHCONST, 0, fucking_destination); diff --git a/src/main.c b/src/main.c index b9cf3f8..d4f87cc 100644 --- a/src/main.c +++ b/src/main.c @@ -112,6 +112,8 @@ int the_theoretical_function_to_execute_your_code_that_should_be_replaced_later( printf("%s %d ", dis.name, inst.a); switch(dis.arg) { + case NOOMV_DIS_NONE: + break; case NOOMV_DIS_BC: printf("%d, %d", inst.b, inst.c); break; diff --git a/src/noom.h b/src/noom.h index bb3619b..055929c 100644 --- a/src/noom.h +++ b/src/noom.h @@ -227,6 +227,13 @@ noom_Exit noom_return(noom_LuaVM* vm, noom_uint_t retc); noom_Exit noom_yieldk(noom_LuaVM* vm, noom_uint_t n, noom_KFunction* f, void* ctx); #define noom_yield(vm, n) noom_yieldk((vm), (n), 0, 0) +// push the current error handler +noom_Exit noom_pusherrhandler(noom_LuaVM *vm); +// pops the top value and sets it as the error handler. +// nil is a perfectly fine error handler, it just does not get called. +// In the case of an error, noom_callk() will also call this error handler. +noom_Exit noom_seterrhandler(noom_LuaVM *vm); + // Like a noom_call, except instead of calling, it resumes coroutines. // This cannot yield but allows yields. noom_Exit noom_resume(noom_LuaVM* vm, noom_uint_t argc, noom_uint_t retc); diff --git a/src/vm.c b/src/vm.c index fd2ce3d..5964771 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1,10 +1,17 @@ #include "vm.h" #include "helper.h" +#include #include +const noomV_Value noomV_nil = { + .tag = NOOMV_VNIL, + .isptr = 0, + .autoclose = 0, +}; + noomV_DisInfo noomV_disInfo[NOOMV_INSTR_NOP2] = { - [NOOMV_INSTR_PUSHNIL] = { - .name = "PUSHNIL", + [NOOMV_INSTR_PUSHVAL] = { + .name = "PUSHVAL", .arg = NOOMV_DIS_uD, }, [NOOMV_INSTR_PUSHCONST] = { @@ -15,10 +22,136 @@ noomV_DisInfo noomV_disInfo[NOOMV_INSTR_NOP2] = { .name = "PUSHGLOBAL", .arg = NOOMV_DIS_uD, }, + [NOOMV_INSTR_PUSHINT] = { + .name = "PUSHINT", + .arg = NOOMV_DIS_sD, + }, + [NOOMV_INSTR_PUSHNIL] = { + .name = "PUSHNIL", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_PUSHBOOLS] = { + .name = "PUSHBOOLS", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_PUSHUPVAL] = { + .name = "PUSHUPVAL", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_PUSHARGS] = { + .name = "PUSHARGS", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_CREATETABLE] = { + .name = "CREATETABLE", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_PUSHCLOSURE] = { + .name = "PUSHCLOSURE", + .arg = NOOMV_DIS_uD, + }, [NOOMV_INSTR_CALL] = { .name = "CALL", .arg = NOOMV_DIS_uD, }, + [NOOMV_INSTR_GETTABLE] = { + .name = "GETTABLE", + .arg = NOOMV_DIS_NONE, + }, + // pops (table, field, value), and sets `table[field] = value` + [NOOMV_INSTR_SETTABLE] = { + .name = "SETTABLE", + .arg = NOOMV_DIS_NONE, + }, + [NOOMV_INSTR_SETLIST] = { + .name = "SETLIST", + .arg = NOOMV_DIS_uD, + }, + // pops table, pushes `table[consts[op.uD]]`, an optimization for indexing fields + [NOOMV_INSTR_GETFIELD] = { + .name = "GETFIELD", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_SETFIELD] = { + .name = "SETFIELD", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_GETUPFIELD] = { + .name = "GETUPFIELD", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_ISNTR_SETUPFIELD] = { + .name = "SETUPFIELD", + .arg = NOOMV_DIS_uD, + }, + + [NOOMV_INSTR_OP] = { + .name = "OP", + .arg = NOOMV_DIS_BC, + }, + + [NOOMV_INSTR_SETVAL] = { + .name = "SETVAL", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_SETUPVAL] = { + .name = "SETUPVAL", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_SETGLOBAL] = { + .name = "SETGLOBAL", + .arg = NOOMV_DIS_uD, + }, + + [NOOMV_INSTR_RET] = { + .name = "RET", + .arg = NOOMV_DIS_uD, + }, + + [NOOMV_INSTR_JMP] = { + .name = "JMP", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_CJMP] = { + .name = "CJMP", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_CNJMP] = { + .name = "CNJMP", + .arg = NOOMV_DIS_uD, + }, + + // For bullshit + + // Take the top value without popping it, push `value[consts[op.uD]]` like GETFIELD would, then swap the top 2 values. + // This is for a:foo() and such + [NOOMV_INSTR_GETMETHOD] = { + .name = "GETMETHOD", + .arg = NOOMV_DIS_uD, + }, + // rotate the the top op.a items by op.sD + [NOOMV_INSTR_ROTATE] = { + .name = "ROTATE", + .arg = NOOMV_DIS_sD, + }, + [NOOMV_INSTR_POP] = { + .name = "POP", + .arg = NOOMV_DIS_uD, + }, + [NOOMV_INSTR_SETSTACK] = { + .name = "SETSTACK", + .arg = NOOMV_DIS_uD, + }, + + [NOOMV_INSTR_CONCAT] = { + .name = "CONCAT", + .arg = NOOMV_DIS_uD, + }, + + [NOOMV_INSTR_CLOSE] = { + .name = "CLOSE", + .arg = NOOMV_DIS_uD, + }, }; noomV_Object* noomV_allocObj(noom_LuaVM* vm, noomV_ObjTag tag, noom_uint_t size) { @@ -38,6 +171,8 @@ noomV_String* noomV_allocStr(noom_LuaVM* vm, const char* str, noom_uint_t len) { if (s == 0) return 0; noom_memcpy(s->data, str, len); s->data[len] = '\0'; + s->len = len; + s->hash = 0; return s; } @@ -63,19 +198,253 @@ noomV_Function* noomV_allocFunc(noom_LuaVM* vm, noomV_String* chunkname) { return f; } -noomV_Table* noomV_allocTable(noom_LuaVM* vm) { - noomV_Table* t = (noomV_Table*)noomV_allocObj(vm, NOOMV_OTABLE, sizeof(noomV_Table)); - if (t == 0) return 0; - t->meta = 0; - t->entries = 0; - t->used = 0; +noomV_Table *noomV_allocTable(noom_LuaVM *vm, noom_uint_t arraylen, noom_uint_t fields) { + noomV_Table *t = (noomV_Table *)noomV_allocObj(vm, NOOMV_OTABLE, sizeof(noomV_Table)); + if(t == 0) return 0; + // not computed t->len = 0; - t->entrydata = 0; + // no metatable + t->meta = 0; + // no entries filled + t->used = 0; + // bit of space between fields in case of hash collisions to not blast them into O(N) hell + noom_uint_t cap = fields * 4 + arraylen; + t->entries = 0; + t->entrydata = noom_alloc(sizeof(noomV_Value) * 2 * cap); + // dw about freeing t, its in the VM heap so it'll get GC'd out + if(t->entrydata == 0) return 0; + t->entries = cap; + for(size_t i = 0; i < cap*2; i++) { + t->entrydata[i].tag = NOOMV_VNIL; + } return t; } +noom_uint_t noomV_rawhashValue(noomV_Value v) { + switch(v.tag) { + case NOOMV_VNIL: + return 0; + case NOOMV_VINT: + return v.integer; + case NOOMV_VBOOL: + return v.boolean ? 1 : 0; + case NOOMV_VNUM: + // TODO: see if we should bitcast instead + return v.number; + case NOOMV_VCFUNC: + return (noom_uint_t)v.cfunc; + case NOOMV_VLUSER: + return (noom_uint_t)v.lightuserdata; + case NOOMV_VOBJ: { + noomV_Object *o = v.obj; + if(o->tag != NOOMV_OSTR) return (noom_uint_t)o; + + noomV_String *s = (noomV_String *)o; + if(s->hash == 0) { + // https://en.wikipedia.org/wiki/Jenkins_hash_function#one_at_a_time + noom_uint_t hash = 0; + for(noom_uint_t i = 0; i < s->len; i++) { + hash += (unsigned char)s->data[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + s->hash = hash; + } + return s->hash; + } + } +} + +noom_bool_t noomV_isNil(noomV_Value key) { return key.tag == NOOMV_VNIL; } + +noom_bool_t noomV_isLegalKey(noomV_Value key) { + if(key.tag == NOOMV_VNIL) return 0; + if(key.tag == NOOMV_VNUM) { + noom_float_t n = key.number; + // true for all except NaN, which is an illegal key! + return n == n; + } + return 1; +} + +noom_bool_t noomV_rawequalValue(noomV_Value a, noomV_Value b) { + // special case: integers and numbers + if(a.tag == NOOMV_VINT && b.tag == NOOMV_VNUM) { + return a.integer == b.number; + } + if(a.tag == NOOMV_VNUM && b.tag == NOOMV_VINT) { + return a.number == b.integer; + } + if(a.tag != b.tag) return 0; + switch(a.tag) { + case NOOMV_VNIL: + return 1; + case NOOMV_VINT: + return a.integer == b.integer; + case NOOMV_VBOOL: + return (a.boolean != 0) == (b.boolean != 0); + case NOOMV_VNUM: + return a.number == b.number; + case NOOMV_VCFUNC: + return a.cfunc == b.cfunc; + case NOOMV_VLUSER: + return a.lightuserdata == b.lightuserdata; + case NOOMV_VOBJ: { + noomV_Object *aObj = a.obj; + noomV_Object *bObj = b.obj; + if(aObj == bObj) return 1; + if(aObj->tag != bObj->tag) return 0; + if(aObj->tag != NOOMV_OSTR) return aObj == bObj; + + noomV_String *strA = (noomV_String *)aObj; + noomV_String *strB = (noomV_String *)bObj; + if(strA->len != strB->len) return 0; + if(strA->hash != 0 && strB->hash != 0 && strA->hash != strB->hash) return 0; + for(noom_uint_t i = 0; i < strA->len; i++) { + if(strA->data[i] != strB->data[i]) return 0; + } + return 1; + } + } +} + +noomV_Value noomV_rawgetTable(noomV_Table *t, noomV_Value key) { + if(!noomV_isLegalKey(key)) return noomV_nil; + noom_uint_t hash = noomV_rawhashValue(key); + noom_uint_t start = hash % t->entries; + for(noom_uint_t i = 0; i < t->entries; i++) { + noom_uint_t idx = (start + i) % t->entries; + // unallocated entry, bye-bye + if(t->entrydata[idx].tag == NOOMV_VNIL) break; + // isptr on a key means its a tombstone + if(t->entrydata[idx].isptr) continue; + if(noomV_rawequalValue(t->entrydata[idx], key)) { + return t->entrydata[idx + t->entries]; + } + } + return noomV_nil; +} + +noomV_Value noomV_rawgetiTable(noomV_Table *t, noom_int_t idx) { + return noomV_rawgetTable(t, (noomV_Value) { .tag = NOOMV_VINT, .integer = idx }); +} + +// TODO: implement +// used should be at most ~80% of entrydata because +// that's standard practice and we're not computer scientists +noom_Exit noomV_rawsetTable(noom_LuaVM *vm, noomV_Table *t, noomV_Value key, noomV_Value val) { + if(!noomV_isLegalKey(key)) { + noomV_setErrorStr(vm, vm->currentThread, "illegal key"); + return NOOM_ERUNTIME; + } + noom_uint_t target = t->entries * 80 / 100; + if(t->used > target) { + // TODO: resize hashtable + } + // prevent awkward bugs + key.isptr = 0; + key.autoclose = 0; + val.isptr = 0; + val.autoclose = 0; + noom_uint_t hash = noomV_rawhashValue(key); + noom_uint_t idx = hash % t->entries; + while(1) { + noomV_Value key2 = t->entrydata[idx]; + // tombstone! + if(key2.isptr) { + t->entrydata[idx] = key; + t->entrydata[idx + t->entries] = val; + return NOOM_OK; + } + if(noomV_rawequalValue(key, key2)) { + // HOLY SHIET!!!!! + t->entrydata[idx + t->entries] = val; + return NOOM_OK; + } + idx++; + idx %= t->entries; + } + return NOOM_OK; +} + +noom_uint_t noomV_rawlenTable(noomV_Table *t) { + noom_uint_t guess = t->len; + // TODO: optimize with a bsearch + while(1) { + if(!noomV_isNil(noomV_rawgetiTable(t, guess+1))) guess++; + if(guess == 0) break; + if(noomV_isNil(noomV_rawgetiTable(t, guess))) guess--; + } + return t->len = guess; +} + +noomV_Thread *noomV_allocCoroutine(noom_LuaVM *vm) { + noomV_Thread *thrd = (noomV_Thread *)noomV_allocObj(vm, NOOMV_OTHREAD, sizeof(noomV_Thread)); + if(thrd == 0) return 0; + thrd->resumedBy = 0; + thrd->resuming = 0; + thrd->calldepth = 0; + thrd->stacklen = 0; + thrd->stackcap = 32; + thrd->callcap = 4; + thrd->errObj = noomV_nil; + + thrd->calls = noom_alloc(sizeof(noomV_CallFrame) * thrd->callcap); + if(thrd->calls == 0) return 0; + thrd->stack = noom_alloc(sizeof(noomV_Value) * thrd->stackcap); + if(thrd->stack == 0) return 0; + + return thrd; +} + +void noomV_setErrorStr(noom_LuaVM *vm, noomV_Thread *coro, const char *str) { + noomV_String *s = noomV_allocStr(vm, str, noom_strlen(str)); + if(s == 0) { + coro->errObj = vm->oomVal; + return; + } + coro->errObj.tag = NOOMV_VOBJ; + coro->errObj.obj = &s->obj; +} + +noom_Exit noomV_setErrorFromExit(noom_LuaVM *vm, noomV_Thread *coro, noom_Exit exit) { + switch(exit) { + case NOOM_OK: + coro->errObj = noomV_nil; + break; + case NOOM_PLEASEHELPMEIAMSCARED: + noomV_setErrorStr(vm, coro, "temporary error"); + break; + case NOOM_IAMNOTSCAREDJUSTLAZYTHISTIME: + noomV_setErrorStr(vm, coro, "programmer laziness error"); + break; + case NOOM_EINTERNAL: + noomV_setErrorStr(vm, coro, "unrecoverable internal error"); + break; + case NOOM_ENOMEM: + coro->errObj = vm->oomVal; + break; + case NOOM_ERUNTIME: + break; + case NOOM_ENOSTACK: + noomV_setErrorStr(vm, coro, "stack overflow"); + break; + case NOOM_EIO: + noomV_setErrorStr(vm, coro, "I/O error"); + break; + case NOOM_ELIMIT: + noomV_setErrorStr(vm, coro, "buffer overflow"); + break; + case NOOM_EERROR: + break; + } + return exit; +} + void noomV_freeObj(noomV_Object* obj) { - printf("%d\n", obj->tag); if(obj->tag == NOOMV_OFUNC) { noomV_Function *f = (noomV_Function *)obj; noom_free(f->consts); @@ -83,21 +452,97 @@ void noomV_freeObj(noomV_Object* obj) { noom_free(f->locals); noom_free(f->code); } + if(obj->tag == NOOMV_OTABLE) { + noomV_Table *t = (noomV_Table *)obj; + noom_free(t->entrydata); + } + if(obj->tag == NOOMV_OTHREAD) { + noomV_Thread *thrd = (noomV_Thread *)obj; + noom_free(thrd->stack); + noom_free(thrd->calls); + } noom_free(obj); } +noom_Exit noomV_pushCallFrame(noom_LuaVM *vm, noomV_Thread *coro, noomV_CallFrame cf); + +noomV_CallFrame *noomV_topCallFrame(noomV_Thread *coro) { + if(coro->calldepth == 0) return 0; + return &coro->calls[coro->calldepth - 1]; +} + +noom_Exit noomV_setThreadStackSize(noom_LuaVM *vm, noomV_Thread *coro, noom_int_t stack) { + if(stack < 0) { + noomV_setErrorStr(vm, coro, "stack underflow"); + return NOOM_ERUNTIME; + } + if(stack > NOOM_MAXSTACK) return NOOM_ELIMIT; + if(stack > coro->stackcap) { + noom_uint_t newCap = coro->stackcap; + while(newCap < stack) newCap *= 2; + + noomV_Value *newStack = noom_realloc(coro->stack, sizeof(noomV_Value) * coro->stackcap); + if(newStack == 0) return NOOM_ENOMEM; + coro->stack = newStack; + coro->stackcap = newCap; + } + + // NIL-ify + if(coro->stacklen < stack) { + for(int i = coro->stacklen; i < stack; i++) { + coro->stack[i] = noomV_nil; + } + return NOOM_OK; + } + // close. TODO: actually loop to run __close! + // Preferably figure out some way to do a linked list of TBC values + coro->stacklen = stack; + return NOOM_OK; +} + noom_LuaVM* noom_createVM(noom_LuaVersion version) { noom_LuaVM* vm = noom_alloc(sizeof(*vm)); if (vm == 0) return 0; // initialize the universe to NULL, handles partial OOMs nicely vm->heap = 0; vm->graySet = 0; + vm->oomVal.tag = NOOMV_VOBJ; + vm->oomVal.obj = 0; vm->globals = 0; vm->registry = 0; vm->mainThread = 0; vm->currentThread = 0; vm->version = version; + + const char *oomStr = "out of memory"; + vm->oomVal.obj = (noomV_Object *)noomV_allocStr(vm, oomStr, noom_strlen(oomStr)); + if(vm->oomVal.obj == 0) goto rip; + vm->registry = noomV_allocTable(vm, 0, 1); + if(vm->registry == 0) goto rip; + vm->globals = noomV_allocTable(vm, 0, 32); + if(vm->globals == 0) goto rip; + + vm->mainThread = noomV_allocCoroutine(vm); + if(vm->mainThread == 0) goto rip; + + vm->currentThread = vm->mainThread; + + // goal: put _G in the registry as the _G key + noomV_String *_G = noomV_allocStr(vm, "_G", 2); + if(_G == 0) goto rip; + + noomV_Value _GKey = { .tag = NOOMV_VOBJ, .obj = &_G->obj, .isptr = 0, .autoclose = 0 }; + noomV_Value _GVal = { .tag = NOOMV_VOBJ, .obj = &vm->globals->obj, .isptr = 0, .autoclose = 0 }; + + noom_Exit err = noomV_rawsetTable(vm, vm->registry, _GKey, _GVal); + if(err) goto rip; + + assert(noomV_rawequalValue(noomV_rawgetTable(vm->registry, _GKey), _GVal)); + return vm; +rip: + noom_destroyVM(vm); + return 0; } void noom_destroyVM(noom_LuaVM* vm) { diff --git a/src/vm.h b/src/vm.h index ce017f7..09a23d0 100644 --- a/src/vm.h +++ b/src/vm.h @@ -20,6 +20,7 @@ typedef enum noomV_ObjTag { NOOMV_OTABLE, NOOMV_OUSERDATA, NOOMV_OPOINTER, + NOOMV_OTHREAD, } noomV_ObjTag; typedef struct noomV_Object { @@ -49,6 +50,7 @@ typedef struct noomV_Value { noom_bool_t boolean; noom_int_t integer; noom_float_t number; + noom_CFunction *cfunc; void* lightuserdata; noomV_Object* obj; struct noomV_Pointer* ptr; @@ -57,6 +59,7 @@ typedef struct noomV_Value { typedef struct noomV_String { noomV_Object obj; + noom_uint_t hash; noom_uint_t len; // Includes NUL-terminator! char data[]; @@ -105,12 +108,12 @@ typedef enum noomV_Opcode : unsigned char { NOOMV_INSTR_PUSHARGS, // Pushes a new table. op.uD is the initial capacity NOOMV_INSTR_CREATETABLE, - // Pushes a closure, using the prototype in protos[op.uD] + // Pushes a closure, using the prototype in `protos[op.uD]` NOOMV_INSTR_PUSHCLOSURE, // High-level ops - // Call the function stored at stack[op.a]. All values after that are treated at arguments. op.uD is the expected return count plus one, and if op.uD is 0, + // Call the function stored at `stack[op.a]`. All values after that are treated at arguments. op.uD is the expected return count plus one, and if op.uD is 0, // all returns are pushed. NOOMV_INSTR_CALL, @@ -147,7 +150,8 @@ typedef enum noomV_Opcode : unsigned char { // Control flow - // returns from a function. The amount returned is from op.uD + // returns from a function. The first value returned is in op.uD. + // If that is out of bounds, nothing is returned. NOOMV_INSTR_RET, // computes T = op.a << 16 + op.uD, jumps to T. @@ -164,7 +168,7 @@ typedef enum noomV_Opcode : unsigned char { NOOMV_INSTR_GETMETHOD, // rotate the the top op.a items by op.sD NOOMV_INSTR_ROTATE, - // pop op.uD values + // pop op.uD+1 values NOOMV_INSTR_POP, // set the stack size to op.uD, inserting nils if need be, good for labels NOOMV_INSTR_SETSTACK, @@ -181,6 +185,7 @@ typedef enum noomV_Opcode : unsigned char { typedef struct noomV_DisInfo { const char *name; enum { + NOOMV_DIS_NONE, NOOMV_DIS_BC, NOOMV_DIS_uD, NOOMV_DIS_sD, @@ -303,13 +308,18 @@ typedef struct noomV_Function { unsigned char localsize; } noomV_Function; +// exactly 64 bytes, aka one cache line. +// TODO: ensure it is 64-byte aligned for a perfect 1:1 struct to cache line ratio for perf typedef struct noomV_CallFrame { // stack index of function - unsigned int funcIdx; + noom_uint_t funcIdx; + noom_uint_t returnCount; noom_bool_t isC; + noomV_Value errhandler; + noomV_Pointer **upvals; union { struct { - // + // program counter unsigned int pc; // amount of varargs unsigned int varargc; @@ -325,6 +335,7 @@ typedef struct noomV_Thread { noomV_Object obj; unsigned int stacklen, calldepth; unsigned int stackcap, callcap; + noomV_Value errObj; // can have pointers! noomV_Value* stack; noomV_CallFrame* calls; @@ -335,6 +346,7 @@ typedef struct noomV_Thread { struct noom_LuaVM { noomV_Object* heap; noomV_Object* graySet; + noomV_Value oomVal; noomV_Table* globals; noomV_Table* registry; noomV_Thread* mainThread; @@ -342,10 +354,38 @@ struct noom_LuaVM { noom_LuaVersion version; }; +// for conveniently passing around nil +extern const noomV_Value noomV_nil; + // Allocating objects noomV_Object* noomV_allocObj(noom_LuaVM* vm, noomV_ObjTag tag, noom_uint_t size); noomV_String* noomV_allocStr(noom_LuaVM* vm, const char* str, noom_uint_t len); noomV_Function* noomV_allocFunc(noom_LuaVM* vm, noomV_String* chunkname); +noomV_Table *noomV_allocTable(noom_LuaVM *vm, noom_uint_t arraylen, noom_uint_t fields); +noom_uint_t noomV_rawhashValue(noomV_Value v); +noom_bool_t noomV_isNil(noomV_Value key); +noom_bool_t noomV_isLegalKey(noomV_Value key); +noom_bool_t noomV_rawequalValue(noomV_Value a, noomV_Value b); +noomV_Value noomV_rawgetTable(noomV_Table *t, noomV_Value key); +noomV_Value noomV_rawgetiTable(noomV_Table *t, noom_int_t idx); +noom_Exit noomV_rawsetTable(noom_LuaVM *vm, noomV_Table *t, noomV_Value key, noomV_Value val); +noom_uint_t noomV_rawlenTable(noomV_Table *t); + +noomV_Thread *noomV_allocCoroutine(noom_LuaVM *vm); + +void noomV_setErrorStr(noom_LuaVM *vm, noomV_Thread *coro, const char *str); +// if exit is ERUNTIME or EERROR, does nothing as it assumes the error is already set! +// It will clear any errors on NOOM_OK!!! +// Returns the exit for convenience +noom_Exit noomV_setErrorFromExit(noom_LuaVM *vm, noomV_Thread *coro, noom_Exit exit); + void noomV_freeObj(noomV_Object* obj); +noom_Exit noomV_pushCallFrame(noom_LuaVM *vm, noomV_Thread *coro, noomV_CallFrame cf); + +// can return NULL if we are not in a call, such as in C API setup. +noomV_CallFrame *noomV_topCallFrame(noomV_Thread *coro); + +noom_Exit noomV_setThreadStackSize(noom_LuaVM *vm, noomV_Thread *coro, noom_int_t stack); + #endif