From 181ef8a18c46e4297bf1ad46326e768a632cb1e1 Mon Sep 17 00:00:00 2001 From: CPunch Date: Wed, 9 Dec 2020 20:32:42 -0600 Subject: [PATCH] Added dictionaries {} Objects are now separate from {} dictionaries the . operator now only indexes fields on objects, the [] operator can only be used on objects if the __index or __newindex functions are defined Additionally 4 new instructions have been added to the VM: OP_NEWDICT, OP_INDEX, OP_INCINDEX, and OP_NEWINDEX. The syntax to create a dictionary is as follows { : , : } eg. { "hello" : "world", "foo" : 1337 } The Lexer & Parser was extended to add the TOKEN_COLON ':' token. --- Makefile | 2 +- src/cdebug.c | 8 +++ src/clex.c | 1 + src/clex.h | 1 + src/cmem.c | 5 ++ src/cobj.c | 158 ++++++++++++++++++++++++++++++----------------- src/cobj.h | 9 +++ src/coperators.h | 6 +- src/cparse.c | 48 ++++++-------- src/cvm.c | 141 ++++++++++++++++++++++++++++++++++++++++-- src/cvm.h | 1 + 11 files changed, 290 insertions(+), 90 deletions(-) diff --git a/Makefile b/Makefile index 00eae31..50cd723 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # make clean && make && ./bin/cosmo CC=clang -CFLAGS=-fPIE -O3 -Wall +CFLAGS=-fPIE -Wall -O3 LDFLAGS=#-fsanitize=address OUT=bin/cosmo diff --git a/src/cdebug.c b/src/cdebug.c index 9211057..de59b3b 100644 --- a/src/cdebug.c +++ b/src/cdebug.c @@ -118,6 +118,12 @@ int disasmInstr(CChunk *chunk, int offset, int indent) { } case OP_CLOSE: return simpleInstruction("OP_CLOSE", offset); + case OP_NEWDICT: + return u16OperandInstruction("OP_NEWDICT", chunk, offset); + case OP_INDEX: + return simpleInstruction("OP_INDEX", offset); + case OP_NEWINDEX: + return simpleInstruction("OP_NEWINDEX", offset); case OP_NEWOBJECT: return u16OperandInstruction("OP_NEWOBJECT", chunk, offset); case OP_GETOBJECT: @@ -164,6 +170,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) { return u8u16OperandInstruction("OP_INCGLOBAL", chunk, offset); case OP_INCUPVAL: return u8u8OperandInstruction("OP_INCLOCAL", chunk, offset); + case OP_INCINDEX: + return u8OperandInstruction("OP_INCINDEX", chunk, offset); case OP_INCOBJECT: return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset); case OP_RETURN: diff --git a/src/clex.c b/src/clex.c index 7cca720..c386437 100644 --- a/src/clex.c +++ b/src/clex.c @@ -303,6 +303,7 @@ CToken cosmoL_scanToken(CLexState *state) { case ']': return makeToken(state, TOKEN_RIGHT_BRACKET); case ';': 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_POUND); case '/': return makeToken(state, TOKEN_SLASH); diff --git a/src/clex.h b/src/clex.h index f3b6547..8963d8b 100644 --- a/src/clex.h +++ b/src/clex.h @@ -12,6 +12,7 @@ typedef enum { TOKEN_LEFT_BRACKET, TOKEN_RIGHT_BRACKET, TOKEN_COMMA, + TOKEN_COLON, TOKEN_DOT, TOKEN_DOT_DOT, TOKEN_MINUS, diff --git a/src/cmem.c b/src/cmem.c index ef9c65a..eb6f03a 100644 --- a/src/cmem.c +++ b/src/cmem.c @@ -97,6 +97,11 @@ void blackenObject(CState *state, CObj *obj) { markObject(state, (CObj*)cobj->proto); break; } + case COBJ_DICT: { // dictionaries are just wrappers for CTable + CObjDict *dict = (CObjDict*)obj; + markTable(state, &dict->tbl); + break; + } case COBJ_UPVALUE: { markValue(state, ((CObjUpval*)obj)->closed); break; diff --git a/src/cobj.c b/src/cobj.c index 7419b30..0998b18 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -51,6 +51,12 @@ void cosmoO_free(CState *state, CObj* obj) { cosmoM_free(state, CObjObject, objTbl); break; } + case COBJ_DICT: { + CObjDict *dict = (CObjDict*)obj; + cosmoT_clearTable(state, &dict->tbl); + cosmoM_free(state, CObjDict, dict); + break; + } case COBJ_UPVALUE: { cosmoM_free(state, CObjUpval, obj); break; @@ -107,6 +113,17 @@ CObjObject *cosmoO_newObject(CState *state) { return obj; } +CObjDict *cosmoO_newDictionary(CState *state) { + CObjDict *obj = (CObjDict*)cosmoO_allocateBase(state, sizeof(CObjDict), COBJ_DICT); + + // init the table (might cause a GC event) + cosmoV_pushValue(state, cosmoV_newObj(obj)); // so our GC can keep track of obj + cosmoT_initTable(state, &obj->tbl, ARRAY_START); + cosmoV_pop(state); + + return obj; +} + CObjFunction *cosmoO_newFunction(CState *state) { CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION); func->args = 0; @@ -208,32 +225,17 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val) { if (!cosmoT_get(&object->tbl, key, val)) { // if the field doesn't exist in the object, check the proto - if (object->proto != NULL) { // sanity check - // first though, check for a member of the proto object, if it fails then lookup __getters - if (cosmoO_getObject(state, object->proto, key, val)) - return true; - - // if this fails or the key isn't in that table then we default to __index - if (cosmoO_getIString(state, object->proto, ISTRING_GETTER, val) && IS_OBJECT(*val) && cosmoO_getObject(state, cosmoV_readObject(*val), key, val)) { - cosmoV_pushValue(state, *val); // push function - cosmoV_pushValue(state, cosmoV_newObj(object)); // push object - cosmoV_call(state, 1); // call the function with the 1 argument - *val = *cosmoV_pop(state); // set value to the return value of __index - return true; - } - - // then check for __index, if that exists, call it - if (cosmoO_getIString(state, object->proto, ISTRING_INDEX, val)) { - cosmoV_pushValue(state, *val); // push function - cosmoV_pushValue(state, cosmoV_newObj(object)); // push object - cosmoV_pushValue(state, key); // push key - cosmoV_call(state, 2); // call the function with the 2 arguments - *val = *cosmoV_pop(state); // set value to the return value of __index - return true; - } + if (cosmoO_getIString(state, object, ISTRING_GETTER, val) && IS_OBJECT(*val) && cosmoO_getObject(state, cosmoV_readObject(*val), key, val)) { + cosmoV_pushValue(state, *val); // push function + cosmoV_pushValue(state, cosmoV_newObj(object)); // push object + cosmoV_call(state, 1); // call the function with the 1 argument + *val = *cosmoV_pop(state); // set value to the return value of __index + return true; } - return false; // no protoobject to check against + if (object->proto != NULL && cosmoO_getObject(state, object->proto, key, val)) + return true; + return false; // no protoobject to check against / key not founc } return true; @@ -242,27 +244,14 @@ bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val void cosmoO_setObject(CState *state, CObjObject *object, CValue key, CValue val) { CValue ret; - // if there's a prototype, check for the tag methods! - if (object->proto != NULL) { - // first check for __setters - if (cosmoO_getIString(state, object->proto, ISTRING_SETTER, &ret) && IS_OBJECT(ret) && cosmoO_getObject(state, cosmoV_readObject(ret), key, &ret)) { - cosmoV_pushValue(state, ret); // push function - cosmoV_pushValue(state, cosmoV_newObj(object)); // push object - cosmoV_pushValue(state, val); // push new value - cosmoV_call(state, 2); - cosmoV_pop(state); // pop return value - } - - // then check for __newindex in the prototype - if (cosmoO_getIString(state, object->proto, ISTRING_NEWINDEX, &ret)) { - cosmoV_pushValue(state, ret); // push function - cosmoV_pushValue(state, cosmoV_newObj(object)); // push object - cosmoV_pushValue(state, key); // push key & value pair - cosmoV_pushValue(state, val); - cosmoV_call(state, 3); - cosmoV_pop(state); // pop return value - return; - } + // first check for __setters + if (cosmoO_getIString(state, object, ISTRING_SETTER, &ret) && IS_OBJECT(ret) && cosmoO_getObject(state, cosmoV_readObject(ret), key, &ret)) { + cosmoV_pushValue(state, ret); // push function + cosmoV_pushValue(state, cosmoV_newObj(object)); // push object + cosmoV_pushValue(state, val); // push new value + cosmoV_call(state, 2); + cosmoV_pop(state); // pop return value + return; } object->istringFlags = 0; // reset cache @@ -282,11 +271,11 @@ void *cosmoO_getUserData(CState *state, CObjObject *object) { return object->user; } -bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) { +bool rawgetIString(CState *state, CObjObject *object, int flag, CValue *val) { if (readFlag(object->istringFlags, flag)) return false; // it's been cached as bad - - if (!cosmoO_getObject(state, object, cosmoV_newObj(state->iStrings[flag]), val)) { + + if (!cosmoT_get(&object->tbl, cosmoV_newObj(state->iStrings[flag]), val)) { // mark it bad! setFlagOn(object->istringFlags, flag); return false; @@ -295,11 +284,59 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) return true; // :) } +bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val) { + CObjObject *obj = object; + + do { + if (rawgetIString(state, obj, flag, val)) + return true; + } while ((obj = obj->proto) != NULL); // sets obj to it's proto and compares it to NULL + + 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) { + if (cosmoO_getIString(state, object, ISTRING_INDEX, val)) { + cosmoV_pushValue(state, *val); // push function + cosmoV_pushValue(state, cosmoV_newObj(object)); // push object + cosmoV_pushValue(state, key); // push key + cosmoV_call(state, 2); // call the function with the 2 arguments + *val = *cosmoV_pop(state); // set value to the return value of __index + return true; + } else { // there's no __index function defined! + cosmoV_error(state, "Couldn't index object without __index function!"); + } + + return false; +} + +bool cosmoO_newIndexObject(CState *state, CObjObject *object, CValue key, CValue val) { + CValue ret; // return value for cosmoO_getIString + + if (cosmoO_getIString(state, object, ISTRING_NEWINDEX, &ret)) { + cosmoV_pushValue(state, ret); // push function + cosmoV_pushValue(state, cosmoV_newObj(object)); // push object + cosmoV_pushValue(state, key); // push key & value pair + cosmoV_pushValue(state, val); + cosmoV_call(state, 3); + cosmoV_pop(state); // pop return value + return true; + } else { // there's no __newindex function defined + cosmoV_error(state, "Couldn't set index on object without __newindex function!"); + } + + return false; +} + CObjString *cosmoO_toString(CState *state, CObj *obj) { switch (obj->type) { case COBJ_STRING: { return (CObjString*)obj; } + case COBJ_CLOSURE: { // should be transparent to the user imo + CObjClosure *closure = (CObjClosure*)obj; + return cosmoO_toString(state, (CObj*)closure->function); + } case COBJ_FUNCTION: { CObjFunction *func = (CObjFunction*)obj; return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); @@ -309,6 +346,11 @@ CObjString *cosmoO_toString(CState *state, CObj *obj) { int sz = sprintf(buf, " %p", obj) + 1; // +1 for the null character return cosmoO_copyString(state, buf, sz); } + case COBJ_DICT: { + char buf[64]; + int sz = sprintf(buf, " %p", obj) + 1; // +1 for the null character + return cosmoO_copyString(state, buf, sz); + } default: return cosmoO_copyString(state, "", 10); } @@ -323,7 +365,12 @@ void printObject(CObj *o) { } case COBJ_OBJECT: { printf(" %p", o); - return; + break; + } + case COBJ_DICT: { + CObjDict *dict = (CObjDict*)o; + printf(" %p", dict); + break; } case COBJ_UPVALUE: { CObjUpval *upval = (CObjUpval*)o; @@ -331,6 +378,11 @@ void printObject(CObj *o) { printValue(*upval->val); break; } + case COBJ_CLOSURE: { + CObjClosure *closure = (CObjClosure*)o; + printObject((CObj*)closure->function); // just print the function + break; + } case COBJ_FUNCTION: { CObjFunction *objFunc = (CObjFunction*)o; if (objFunc->name != NULL) @@ -350,13 +402,8 @@ void printObject(CObj *o) { printValue(method->func); break; } - case COBJ_CLOSURE: { - CObjClosure *closure = (CObjClosure*)o; - printObject((CObj*)closure->function); // just print the function - break; - } default: - printf(""); + printf("", o); } } @@ -364,6 +411,7 @@ const char *cosmoO_typeStr(CObj* obj) { switch (obj->type) { case COBJ_STRING: return ""; case COBJ_OBJECT: return ""; + case COBJ_DICT: return ""; case COBJ_FUNCTION: return ""; case COBJ_CFUNCTION: return ""; case COBJ_METHOD: return ""; diff --git a/src/cobj.h b/src/cobj.h index e2911df..a4bfce4 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -13,6 +13,7 @@ typedef uint32_t cosmo_Flag; typedef enum { COBJ_STRING, COBJ_OBJECT, + COBJ_DICT, // dictionary COBJ_FUNCTION, COBJ_CFUNCTION, // internal use @@ -49,6 +50,11 @@ typedef struct CObjObject { struct CObjObject *proto; // protoobject, describes the behavior of the object } CObjObject; +typedef struct CObjDict { // dictionary, a wrapper for CTable + CommonHeader; // "is a" CObj + CTable tbl; +} CObjDict; + typedef struct CObjFunction { CommonHeader; // "is a" CObj CChunk chunk; @@ -109,6 +115,7 @@ void cosmoO_free(CState *state, CObj* obj); bool cosmoO_equal(CObj* obj1, CObj* obj2); CObjObject *cosmoO_newObject(CState *state); +CObjDict *cosmoO_newDictionary(CState *state); CObjFunction *cosmoO_newFunction(CState *state); CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func); CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj); @@ -119,6 +126,8 @@ CObjUpval *cosmoO_newUpvalue(CState *state, CValue *val); bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val); void cosmoO_setObject(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); void cosmoO_setUserData(CState *state, CObjObject *object, void *p); void *cosmoO_getUserData(CState *state, CObjObject *object); diff --git a/src/coperators.h b/src/coperators.h index 1b99f1b..1206d42 100644 --- a/src/coperators.h +++ b/src/coperators.h @@ -29,9 +29,12 @@ typedef enum { OP_CALL, // calls top[-uint8_t] OP_CLOSURE, OP_CLOSE, + OP_NEWDICT, + OP_INDEX, + OP_NEWINDEX, OP_NEWOBJECT, - OP_GETOBJECT, OP_SETOBJECT, + OP_GETOBJECT, OP_INVOKE, // ARITHMETIC @@ -46,6 +49,7 @@ typedef enum { 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]] OP_INCUPVAL, // pushes old value to stack, adds (uint8_t-128) to closure->upval[uint8_t] + OP_INCINDEX, // pushes old value to stack, adds (uint8_t-128) to dict[pop()] OP_INCOBJECT, // pushes old value to stack, adds (uint8_t-128) to obj[const[uint16_t]] // EQUALITY diff --git a/src/cparse.c b/src/cparse.c index 15a0370..61e1852 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -558,39 +558,23 @@ static void object(CParseState *pstate, bool canAssign) { if (!match(pstate, TOKEN_RIGHT_BRACE)) { do { - if (match(pstate, TOKEN_IDENTIFIER)) { - uint16_t fieldIdent = identifierConstant(pstate, &pstate->previous); + // parse the key first + expression(pstate); // should parse until ':' - // OP_NEWOBJECT expects the key on the stack before the value - writeu8(pstate, OP_LOADCONST); - writeu16(pstate, fieldIdent); + consume(pstate, TOKEN_COLON, "Expected ':' to mark end of key and start of value!"); - consume(pstate, TOKEN_EQUAL, "Invalid syntax!"); - - // parse field - expression(pstate); - valuePopped(pstate, 1); - } else if (match(pstate, TOKEN_LEFT_BRACKET)) { - // parse the key first - expression(pstate); // should parse until end bracket - - consume(pstate, TOKEN_RIGHT_BRACKET, "Expected ']' to end index definition."); - consume(pstate, TOKEN_EQUAL, "Expected '='."); - - // now, parse the value (until comma) - expression(pstate); - valuePopped(pstate, 2); - } else { - error(pstate, "Invalid syntax!"); - } + // now, parse the value (until comma) + expression(pstate); + // "pop" the 2 values + valuePopped(pstate, 2); entries++; } while (match(pstate, TOKEN_COMMA) && !pstate->hadError); consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition."); } - writeu8(pstate, OP_NEWOBJECT); + writeu8(pstate, OP_NEWDICT); // creates a dictionary with u16 entries writeu16(pstate, entries); valuePushed(pstate, 1); } @@ -634,12 +618,19 @@ static void _index(CParseState *pstate, bool canAssign) { if (canAssign && match(pstate, TOKEN_EQUAL)) { expression(pstate); - writeu8(pstate, OP_SETOBJECT); - valuePopped(pstate, 3); // pops key, value & object + writeu8(pstate, OP_NEWINDEX); + valuePopped(pstate, 2); // pops key, value & object + } else if (match(pstate, TOKEN_PLUS_PLUS)) { // increment the field + writeu8(pstate, OP_INCINDEX); + writeu8(pstate, 128 + 1); + } else if (match(pstate, TOKEN_MINUS_MINUS)) { // decrement the field + writeu8(pstate, OP_INCINDEX); + writeu8(pstate, 128 - 1); } else { - writeu8(pstate, OP_GETOBJECT); - valuePopped(pstate, 1); // pops key & object but also pushes the field so total popped is 1 + writeu8(pstate, OP_INDEX); } + + valuePopped(pstate, 1); // pops key & object but also pushes the field so total popped is 1 } static void increment(CParseState *pstate, int val) { @@ -716,6 +707,7 @@ ParseRule ruleTable[] = { [TOKEN_LEFT_BRACKET] = {NULL, _index, PREC_CALL}, [TOKEN_RIGHT_BRACKET] = {NULL, NULL, PREC_NONE}, [TOKEN_COMMA] = {NULL, NULL, PREC_NONE}, + [TOKEN_COLON] = {NULL, NULL, PREC_NONE}, [TOKEN_DOT] = {NULL, dot, PREC_CALL}, [TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT}, [TOKEN_MINUS] = {unary, binary, PREC_TERM}, diff --git a/src/cvm.c b/src/cvm.c index a5b4aba..db57f66 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -240,6 +240,25 @@ COSMO_API void cosmoV_makeObject(CState *state, int pairs) { cosmoV_pushValue(state, cosmoV_newObj(newObj)); } +COSMO_API void cosmoV_makeDictionary(CState *state, int pairs) { + StkPtr key, val; + CObjDict *newObj = cosmoO_newDictionary(state); + cosmoV_pushValue(state, cosmoV_newObj(newObj)); // so our GC doesn't free our new dictionary + + for (int i = 0; i < pairs; i++) { + val = cosmoV_getTop(state, (i*2) + 1); + key = cosmoV_getTop(state, (i*2) + 2); + + // set key/value pair + CValue *newVal = cosmoT_insert(state, &newObj->tbl, *key); + *newVal = *val; + } + + // once done, pop everything off the stack + push new dictionary + cosmoV_setTop(state, (pairs * 2) + 1); // + 1 for our dictionary + cosmoV_pushValue(state, cosmoV_newObj(newObj)); +} + COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val) { if (cosmoO_getObject(state, object, key, val)) { if (IS_OBJ(*val)) { @@ -383,6 +402,70 @@ bool cosmoV_execute(CState *state) { cosmoV_pop(state); break; } + case OP_NEWDICT: { + uint16_t pairs = READUINT(); + cosmoV_makeDictionary(state, pairs); + break; + } + case OP_INDEX: { + StkPtr key = cosmoV_getTop(state, 0); // key should be the top of the stack + StkPtr temp = cosmoV_getTop(state, 1); // after that should be the dictionary + + // sanity check + if (IS_OBJ(*temp)) { + CValue val; // to hold our value + + if (cosmoV_readObj(*temp)->type == COBJ_DICT) { + CObjDict *dict = (CObjDict*)cosmoV_readObj(*temp); + cosmoT_get(&dict->tbl, *key, &val); + } else if (cosmoV_readObj(*temp)->type == COBJ_OBJECT) { // check for __index! + CObjObject *object = (CObjObject*)cosmoV_readObj(*temp); + + if (!cosmoO_indexObject(state, object, *key, &val)) + break; + } else { + cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp)); + break; + } + + cosmoV_setTop(state, 2); // pops the object & the key + cosmoV_pushValue(state, val); // pushes the field result + } else { + cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp)); + } + + break; + } + case OP_NEWINDEX: { + StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack + StkPtr key = cosmoV_getTop(state, 1); + StkPtr temp = cosmoV_getTop(state, 2); // object is after the key + + // sanity check + if (IS_OBJ(*temp)) { + if (cosmoV_readObj(*temp)->type == COBJ_DICT) { + CObjDict *dict = (CObjDict*)cosmoV_readObj(*temp); + CValue *newVal = cosmoT_insert(state, &dict->tbl, *key); + + *newVal = *value; // set the index + } else if (cosmoV_readObj(*temp)->type == COBJ_OBJECT) { // check for __newindex! + CObjObject *object = (CObjObject*)cosmoV_readObj(*temp); + + if (!cosmoO_newIndexObject(state, object, *key, *value)) + break; + } else { + cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); + break; + } + + // pop everything off the stack + cosmoV_setTop(state, 3); + } else { + cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); + } + + break; + } case OP_NEWOBJECT: { uint16_t pairs = READUINT(); cosmoV_makeObject(state, pairs); @@ -557,6 +640,52 @@ bool cosmoV_execute(CState *state) { break; } + case OP_INCINDEX: { + int8_t inc = READBYTE() - 128; // ammount we're incrementing by + StkPtr temp = cosmoV_getTop(state, 1); // object should be above the key + StkPtr key = cosmoV_getTop(state, 0); // grabs key + + if (IS_OBJ(*temp)) { + if (cosmoV_readObj(*temp)->type == COBJ_DICT) { + CObjDict *dict = (CObjDict*)cosmoV_readObj(*temp); + CValue *val = cosmoT_insert(state, &dict->tbl, *key); + + // pops dict & key from stack + cosmoV_setTop(state, 2); + + if (IS_NUMBER(*val)) { + cosmoV_pushValue(state, *val); // pushes old value onto the stack :) + *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); + } else { + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); + break; + } + } else if (cosmoV_readObj(*temp)->type == COBJ_OBJECT) { // check for __newindex! + CObjObject *object = (CObjObject*)cosmoV_readObj(*temp); + CValue val; + + // call __index + if (cosmoO_indexObject(state, object, *key, &val)) { + if (IS_NUMBER(val)) { + cosmoV_pushValue(state, val); // pushes old value onto the stack :) + + // call __newindex + cosmoO_newIndexObject(state, object, *key, cosmoV_newNumber(cosmoV_readNumber(val) + inc)); + } else { + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); + break; + } + } + } else { + cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); + break; + } + } else { + cosmoV_error(state, "Couldn't set index with type %s!", cosmoV_typeStr(*temp)); + } + + break; + } case OP_INCOBJECT: { int8_t inc = READBYTE() - 128; // ammount we're incrementing by uint16_t indx = READUINT(); @@ -570,17 +699,19 @@ bool cosmoV_execute(CState *state) { } CObjObject *object = (CObjObject*)cosmoV_readObj(*temp); - CValue *val = cosmoT_insert(state, &object->tbl, ident); + CValue val; + + cosmoO_getObject(state, object, ident, &val); // pop the object off the stack cosmoV_pop(state); // check that it's a number value - if (IS_NUMBER(*val)) { - cosmoV_pushValue(state, *val); // pushes old value onto the stack :) - *val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); + if (IS_NUMBER(val)) { + cosmoV_pushValue(state, val); // pushes old value onto the stack :) + cosmoO_setObject(state, object, ident, cosmoV_newNumber(cosmoV_readNumber(val) + inc)); } else { - cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); + cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val)); } break; diff --git a/src/cvm.h b/src/cvm.h index 9e43754..b3bf9b7 100644 --- a/src/cvm.h +++ b/src/cvm.h @@ -15,6 +15,7 @@ typedef enum { // args = # of pass parameters, nresults = # of expected results COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args); COSMO_API void cosmoV_makeObject(CState *state, int pairs); +COSMO_API void cosmoV_makeDictionary(CState *state, int pairs); COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val); COSMO_API void cosmoV_error(CState *state, const char *format, ...);