diff --git a/src/cdebug.c b/src/cdebug.c index 7400425..957e9c0 100644 --- a/src/cdebug.c +++ b/src/cdebug.c @@ -154,6 +154,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) { return simpleInstruction("OP_LESS_EQUAL", offset); case OP_NEGATE: return simpleInstruction("OP_NEGATE", offset); + case OP_COUNT: + return simpleInstruction("OP_COUNT", offset); case OP_CONCAT: return u8OperandInstruction("OP_CONCAT", chunk, offset); case OP_INCLOCAL: diff --git a/src/clex.c b/src/clex.c index 447ee6c..a6fe6f2 100644 --- a/src/clex.c +++ b/src/clex.c @@ -5,6 +5,7 @@ CReservedWord reservedWords[] = { {TOKEN_AND, "and", 3}, + {TOKEN_CLASS, "class", 5}, {TOKEN_DO, "do", 2}, {TOKEN_ELSE, "else", 4}, {TOKEN_ELSEIF, "elseif", 6}, @@ -12,7 +13,6 @@ CReservedWord reservedWords[] = { {TOKEN_FALSE, "false", 5}, {TOKEN_FOR, "for", 3}, {TOKEN_FUNCTION, "function", 8}, - {TOKEN_CLASS, "class", 5}, {TOKEN_IF, "if", 2}, {TOKEN_LOCAL, "local", 5}, {TOKEN_NIL, "nil", 3}, @@ -304,6 +304,7 @@ CToken cosmoL_scanToken(CLexState *state) { case ';': return makeToken(state, TOKEN_EOS); case ',': return makeToken(state, TOKEN_COMMA); case '*': return makeToken(state, TOKEN_STAR); + case '#': return makeToken(state, TOKEN_POUND); case '/': return makeToken(state, TOKEN_SLASH); // two character tokens case '+': diff --git a/src/clex.h b/src/clex.h index cd4f003..aac2d65 100644 --- a/src/clex.h +++ b/src/clex.h @@ -20,6 +20,7 @@ typedef enum { TOKEN_PLUS_PLUS, TOKEN_SLASH, TOKEN_STAR, + TOKEN_POUND, TOKEN_EOS, // end of statement // equality operators diff --git a/src/cmem.c b/src/cmem.c index d96f8cf..fd20547 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -50,7 +50,6 @@ COSMO_API bool cosmoM_checkGarbage(CState *state, size_t needed) { return false; } - void markObject(CState *state, CObj *obj); void markValue(CState *state, CValue val); diff --git a/src/cobj.c b/src/cobj.c index 213ad72..bd6fc01 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -326,4 +326,19 @@ void printObject(CObj *o) { default: printf(""); } +} + +const char *cosmoO_typeStr(CObj* obj) { + switch (obj->type) { + case COBJ_STRING: return ""; + case COBJ_OBJECT: return ""; + case COBJ_FUNCTION: return ""; + case COBJ_CFUNCTION: return ""; + case COBJ_METHOD: return ""; + case COBJ_CLOSURE: return ""; + case COBJ_UPVALUE: return ""; + + default: + return ""; // TODO: maybe panic? could be a malformed object :eyes: + } } \ No newline at end of file diff --git a/src/cobj.h b/src/cobj.h index 484cb3d..ecbd500 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -129,6 +129,7 @@ CObjString *cosmoO_takeString(CState *state, char *str, size_t sz); CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uint32_t hash); COSMO_API void printObject(CObj *o); +const char *cosmoO_typeStr(CObj* obj); #define cosmoO_readCString(x) ((CObjString*)x)->str diff --git a/src/coperators.h b/src/coperators.h index 626303f..1b99f1b 100644 --- a/src/coperators.h +++ b/src/coperators.h @@ -41,6 +41,7 @@ typedef enum { OP_DIV, OP_NOT, OP_NEGATE, + OP_COUNT, OP_CONCAT, // concats uint8_t vars on the stack OP_INCLOCAL, // pushes old value to stack, adds (uint8_t-128) to local[uint8_t] OP_INCGLOBAL, // pushes old value to stack, adds (uint8_t-128) to globals[const[uint16_t]] diff --git a/src/cparse.c b/src/cparse.c index c5a8c87..4d97a1e 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -395,6 +395,7 @@ static void unary(CParseState *pstate, bool canAssign) { switch(type) { case TOKEN_MINUS: writeu8Chunk(pstate->state, getChunk(pstate), OP_NEGATE, cachedLine); break; case TOKEN_BANG: writeu8Chunk(pstate->state, getChunk(pstate), OP_NOT, cachedLine); break; + case TOKEN_POUND: writeu8Chunk(pstate->state, getChunk(pstate), OP_COUNT, cachedLine); break; default: error(pstate, "Unexpected unary operator!"); } @@ -718,6 +719,7 @@ ParseRule ruleTable[] = { [TOKEN_PLUS_PLUS] = {preincrement, NULL, PREC_TERM}, [TOKEN_SLASH] = {NULL, binary, PREC_FACTOR}, [TOKEN_STAR] = {NULL, binary, PREC_FACTOR}, + [TOKEN_POUND] = {unary, NULL, PREC_NONE}, [TOKEN_EOS] = {NULL, NULL, PREC_NONE}, [TOKEN_BANG] = {unary, NULL, PREC_NONE}, [TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY}, @@ -1219,9 +1221,11 @@ CObjFunction* cosmoP_compileString(CState *state, const char *source) { } 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); cosmoM_unfreezeGC(state); diff --git a/src/ctable.c b/src/ctable.c index 5e70fd4..24fa032 100644 --- a/src/ctable.c +++ b/src/ctable.c @@ -209,6 +209,11 @@ bool cosmoT_remove(CState* state, CTable *tbl, CValue key) { return true; } +// returns the active entry count +COSMO_API int cosmoT_count(CTable *tbl) { + return tbl->count - tbl->tombstones; +} + CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t 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 diff --git a/src/ctable.h b/src/ctable.h index 0da078a..66eaeb3 100644 --- a/src/ctable.h +++ b/src/ctable.h @@ -20,6 +20,7 @@ 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 CValue *cosmoT_insert(CState *state, CTable *tbl, CValue key); +COSMO_API int cosmoT_count(CTable *tbl); CObjString *cosmoT_lookupString(CTable *tbl, const char *str, size_t length, uint32_t hash); bool cosmoT_checkShrink(CState *state, CTable *tbl); diff --git a/src/cvalue.c b/src/cvalue.c index 800d47d..7f851ee 100644 --- a/src/cvalue.c +++ b/src/cvalue.c @@ -33,7 +33,7 @@ bool cosmoV_equal(CValue valA, CValue valB) { } } -COSMO_API CObjString *cosmoV_toString(CState *state, CValue val) { +CObjString *cosmoV_toString(CState *state, CValue val) { switch (val.type) { case COSMO_TNUMBER: { char buf[32]; @@ -54,6 +54,18 @@ COSMO_API CObjString *cosmoV_toString(CState *state, CValue val) { } } +const char *cosmoV_typeStr(CValue val) { + switch (val.type) { + case COSMO_TNIL: return ""; + case COSMO_TBOOLEAN: return ""; + case COSMO_TNUMBER: return ""; + case COSMO_TOBJ: return cosmoO_typeStr(val.val.obj); + + default: + return ""; + } +} + void printValue(CValue val) { switch (val.type) { case COSMO_TNUMBER: diff --git a/src/cvalue.h b/src/cvalue.h index 02dc190..17f614f 100644 --- a/src/cvalue.h +++ b/src/cvalue.h @@ -38,6 +38,7 @@ void appendValArray(CState *state, CValueArray *array, CValue val); void printValue(CValue val); COSMO_API bool cosmoV_equal(CValue valA, CValue valB); COSMO_API CObjString *cosmoV_toString(CState *state, CValue val); +COSMO_API const char *cosmoV_typeStr(CValue val); #define IS_NUMBER(x) (x.type == COSMO_TNUMBER) #define IS_BOOLEAN(x) (x.type == COSMO_TBOOLEAN) diff --git a/src/cvm.c b/src/cvm.c index c570bd0..acac5ee 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -154,7 +154,7 @@ bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args) { } else if (IS_CLOSURE(func)) { call(state, cosmoV_readClosure(func), args+1, 1); // offset = 1 so our stack is properly reset } else { - cosmoV_error(state, "Cannot call non-function value!"); + cosmoV_error(state, "Cannot invoke non-function type %s!", cosmoV_typeStr(func)); } return true; @@ -164,8 +164,8 @@ bool invokeMethod(CState* state, CObjObject *obj, CValue func, int args) { COSMOVMRESULT cosmoV_call(CState *state, int args) { StkPtr val = cosmoV_getTop(state, args); // function will always be right above the args - if (!(val->type == COSMO_TOBJ)) { - cosmoV_error(state, "Cannot call non-function value!"); + if (val->type != COSMO_TOBJ) { + cosmoV_error(state, "Cannot call non-function type %s!", cosmoV_typeStr(*val)); return COSMOVM_RUNTIME_ERR; } @@ -265,7 +265,7 @@ COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, C cosmoV_setTop(state, 2); /* pop the 2 values */ \ cosmoV_pushValue(state, typeConst((valA->val.num) op (valB->val.num))); \ } else { \ - cosmoV_error(state, "Expected number!"); \ + cosmoV_error(state, "Expected numbers, got %s and %s!", cosmoV_typeStr(*valA), cosmoV_typeStr(*valB)); \ } \ // returns false if panic @@ -393,8 +393,8 @@ bool cosmoV_execute(CState *state) { StkPtr temp = cosmoV_getTop(state, 1); // after that should be the object // sanity check - if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) { - cosmoV_error(state, "Couldn't get from non-object!"); + if (temp->type != COSMO_TOBJ || temp->val.obj->type != COBJ_OBJECT) { + cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp)); break; } @@ -412,8 +412,8 @@ bool cosmoV_execute(CState *state) { StkPtr temp = cosmoV_getTop(state, 2); // object is after the key // sanity check - if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) { - cosmoV_error(state, "Couldn't set a field on a non-object!"); + if (temp->type != COSMO_TOBJ || temp->val.obj->type != COBJ_OBJECT) { + cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp)); break; } @@ -430,8 +430,8 @@ bool cosmoV_execute(CState *state) { StkPtr temp = cosmoV_getTop(state, args+1); // grabs object from stack // sanity check - if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) { - cosmoV_error(state, "Couldn't get from non-object!"); + if (temp->type != COSMO_TOBJ || temp->val.obj->type != COBJ_OBJECT) { + cosmoV_error(state, "Couldn't get from non-object type %s!", cosmoV_typeStr(*temp)); break; } @@ -475,10 +475,23 @@ bool cosmoV_execute(CState *state) { cosmoV_pop(state); cosmoV_pushValue(state, cosmoV_newNumber(-(val->val.num))); } else { - cosmoV_error(state, "Expected number!"); + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); } break; } + case OP_COUNT: { // pop 1 value off the stack & if it's an object return the ammount of active entries it has + StkPtr temp = cosmoV_getTop(state, 0); + + if (temp->type != COSMO_TOBJ || ((CObj*)temp->val.obj)->type != COBJ_OBJECT) { + cosmoV_error(state, "Expected object, got %s!", cosmoV_typeStr(*temp)); + break; + } + + CObjObject *obj = (CObjObject*)temp->val.obj; + cosmoV_pop(state); + cosmoV_pushNumber(state, cosmoT_count(&obj->tbl)); // pushes the count onto the stack + break; + } case OP_CONCAT: { uint8_t vals = READBYTE(); StkPtr start = state->top - vals; @@ -508,7 +521,7 @@ bool cosmoV_execute(CState *state) { cosmoV_pushValue(state, *val); // pushes old value onto the stack :) *val = cosmoV_newNumber(val->val.num + inc); } else { - cosmoV_error(state, "Expected number!"); + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); } break; @@ -524,7 +537,7 @@ bool cosmoV_execute(CState *state) { cosmoV_pushValue(state, *val); // pushes old value onto the stack :) *val = cosmoV_newNumber(val->val.num + inc); } else { - cosmoV_error(state, "Expected number!"); + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); } break; @@ -539,7 +552,7 @@ bool cosmoV_execute(CState *state) { cosmoV_pushValue(state, *val); // pushes old value onto the stack :) *val = cosmoV_newNumber(val->val.num + inc); } else { - cosmoV_error(state, "Expected number!"); + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); } break; @@ -551,8 +564,8 @@ bool cosmoV_execute(CState *state) { CValue ident = constants[indx]; // grabs identifier // sanity check - if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) { - cosmoV_error(state, "Couldn't set a field on a non-object!"); + if (temp->type != COSMO_TOBJ || temp->val.obj->type != COBJ_OBJECT) { + cosmoV_error(state, "Couldn't set a field on non-object type %s!", cosmoV_typeStr(*temp)); break; } @@ -567,7 +580,7 @@ bool cosmoV_execute(CState *state) { cosmoV_pushValue(state, *val); // pushes old value onto the stack :) *val = cosmoV_newNumber(val->val.num + inc); } else { - cosmoV_error(state, "Expected number!"); + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); } break;