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 { <key> : <value>, <otherkey> : <othervalue> } eg. { "hello" : "world", "foo" : 1337 }

The Lexer & Parser was extended to add the TOKEN_COLON ':' token.
This commit is contained in:
CPunch 2020-12-09 20:32:42 -06:00
parent 6445dae04c
commit 181ef8a18c
11 changed files with 290 additions and 90 deletions

View File

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

View File

@ -118,6 +118,12 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
} }
case OP_CLOSE: case OP_CLOSE:
return simpleInstruction("OP_CLOSE", offset); 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: case OP_NEWOBJECT:
return u16OperandInstruction("OP_NEWOBJECT", chunk, offset); return u16OperandInstruction("OP_NEWOBJECT", chunk, offset);
case OP_GETOBJECT: case OP_GETOBJECT:
@ -164,6 +170,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
return u8u16OperandInstruction("OP_INCGLOBAL", chunk, offset); return u8u16OperandInstruction("OP_INCGLOBAL", chunk, offset);
case OP_INCUPVAL: case OP_INCUPVAL:
return u8u8OperandInstruction("OP_INCLOCAL", chunk, offset); return u8u8OperandInstruction("OP_INCLOCAL", chunk, offset);
case OP_INCINDEX:
return u8OperandInstruction("OP_INCINDEX", chunk, offset);
case OP_INCOBJECT: case OP_INCOBJECT:
return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset); return u8u16OperandInstruction("OP_INCOBJECT", chunk, offset);
case OP_RETURN: case OP_RETURN:

View File

@ -303,6 +303,7 @@ CToken cosmoL_scanToken(CLexState *state) {
case ']': return makeToken(state, TOKEN_RIGHT_BRACKET); case ']': return makeToken(state, TOKEN_RIGHT_BRACKET);
case ';': return makeToken(state, TOKEN_EOS); case ';': return makeToken(state, TOKEN_EOS);
case ',': return makeToken(state, TOKEN_COMMA); case ',': return makeToken(state, TOKEN_COMMA);
case ':': return makeToken(state, TOKEN_COLON);
case '*': return makeToken(state, TOKEN_STAR); case '*': return makeToken(state, TOKEN_STAR);
case '#': return makeToken(state, TOKEN_POUND); case '#': return makeToken(state, TOKEN_POUND);
case '/': return makeToken(state, TOKEN_SLASH); case '/': return makeToken(state, TOKEN_SLASH);

View File

@ -12,6 +12,7 @@ typedef enum {
TOKEN_LEFT_BRACKET, TOKEN_LEFT_BRACKET,
TOKEN_RIGHT_BRACKET, TOKEN_RIGHT_BRACKET,
TOKEN_COMMA, TOKEN_COMMA,
TOKEN_COLON,
TOKEN_DOT, TOKEN_DOT,
TOKEN_DOT_DOT, TOKEN_DOT_DOT,
TOKEN_MINUS, TOKEN_MINUS,

View File

@ -97,6 +97,11 @@ void blackenObject(CState *state, CObj *obj) {
markObject(state, (CObj*)cobj->proto); markObject(state, (CObj*)cobj->proto);
break; break;
} }
case COBJ_DICT: { // dictionaries are just wrappers for CTable
CObjDict *dict = (CObjDict*)obj;
markTable(state, &dict->tbl);
break;
}
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
markValue(state, ((CObjUpval*)obj)->closed); markValue(state, ((CObjUpval*)obj)->closed);
break; break;

View File

@ -51,6 +51,12 @@ void cosmoO_free(CState *state, CObj* obj) {
cosmoM_free(state, CObjObject, objTbl); cosmoM_free(state, CObjObject, objTbl);
break; break;
} }
case COBJ_DICT: {
CObjDict *dict = (CObjDict*)obj;
cosmoT_clearTable(state, &dict->tbl);
cosmoM_free(state, CObjDict, dict);
break;
}
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
cosmoM_free(state, CObjUpval, obj); cosmoM_free(state, CObjUpval, obj);
break; break;
@ -107,6 +113,17 @@ CObjObject *cosmoO_newObject(CState *state) {
return obj; 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 *cosmoO_newFunction(CState *state) {
CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION); CObjFunction *func = (CObjFunction*)cosmoO_allocateBase(state, sizeof(CObjFunction), COBJ_FUNCTION);
func->args = 0; func->args = 0;
@ -208,13 +225,7 @@ CObjString *cosmoO_allocateString(CState *state, const char *str, size_t sz, uin
bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val) { 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 (!cosmoT_get(&object->tbl, key, val)) { // if the field doesn't exist in the object, check the proto
if (object->proto != NULL) { // sanity check if (cosmoO_getIString(state, object, ISTRING_GETTER, val) && IS_OBJECT(*val) && cosmoO_getObject(state, cosmoV_readObject(*val), key, val)) {
// 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, *val); // push function
cosmoV_pushValue(state, cosmoV_newObj(object)); // push object cosmoV_pushValue(state, cosmoV_newObj(object)); // push object
cosmoV_call(state, 1); // call the function with the 1 argument cosmoV_call(state, 1); // call the function with the 1 argument
@ -222,18 +233,9 @@ bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val
return true; return true;
} }
// then check for __index, if that exists, call it if (object->proto != NULL && cosmoO_getObject(state, object->proto, key, val))
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; return true;
} return false; // no protoobject to check against / key not founc
}
return false; // no protoobject to check against
} }
return true; return true;
@ -242,28 +244,15 @@ bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val
void cosmoO_setObject(CState *state, CObjObject *object, CValue key, CValue val) { void cosmoO_setObject(CState *state, CObjObject *object, CValue key, CValue val) {
CValue ret; CValue ret;
// if there's a prototype, check for the tag methods!
if (object->proto != NULL) {
// first check for __setters // first check for __setters
if (cosmoO_getIString(state, object->proto, ISTRING_SETTER, &ret) && IS_OBJECT(ret) && cosmoO_getObject(state, cosmoV_readObject(ret), key, &ret)) { 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, ret); // push function
cosmoV_pushValue(state, cosmoV_newObj(object)); // push object cosmoV_pushValue(state, cosmoV_newObj(object)); // push object
cosmoV_pushValue(state, val); // push new value cosmoV_pushValue(state, val); // push new value
cosmoV_call(state, 2); cosmoV_call(state, 2);
cosmoV_pop(state); // pop return value 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; return;
} }
}
object->istringFlags = 0; // reset cache object->istringFlags = 0; // reset cache
if (IS_NIL(val)) { // if we're setting an index to nil, we can safely mark that as a tombstone if (IS_NIL(val)) { // if we're setting an index to nil, we can safely mark that as a tombstone
@ -282,11 +271,11 @@ void *cosmoO_getUserData(CState *state, CObjObject *object) {
return object->user; 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)) if (readFlag(object->istringFlags, flag))
return false; // it's been cached as bad 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! // mark it bad!
setFlagOn(object->istringFlags, flag); setFlagOn(object->istringFlags, flag);
return false; return false;
@ -295,11 +284,59 @@ bool cosmoO_getIString(CState *state, CObjObject *object, int flag, CValue *val)
return true; // :) 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) { CObjString *cosmoO_toString(CState *state, CObj *obj) {
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: { case COBJ_STRING: {
return (CObjString*)obj; 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: { case COBJ_FUNCTION: {
CObjFunction *func = (CObjFunction*)obj; CObjFunction *func = (CObjFunction*)obj;
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); 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, "<obj> %p", obj) + 1; // +1 for the null character int sz = sprintf(buf, "<obj> %p", obj) + 1; // +1 for the null character
return cosmoO_copyString(state, buf, sz); return cosmoO_copyString(state, buf, sz);
} }
case COBJ_DICT: {
char buf[64];
int sz = sprintf(buf, "<dict> %p", obj) + 1; // +1 for the null character
return cosmoO_copyString(state, buf, sz);
}
default: default:
return cosmoO_copyString(state, "<unkn obj>", 10); return cosmoO_copyString(state, "<unkn obj>", 10);
} }
@ -323,7 +365,12 @@ void printObject(CObj *o) {
} }
case COBJ_OBJECT: { case COBJ_OBJECT: {
printf("<obj> %p", o); printf("<obj> %p", o);
return; break;
}
case COBJ_DICT: {
CObjDict *dict = (CObjDict*)o;
printf("<dict> %p", dict);
break;
} }
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
CObjUpval *upval = (CObjUpval*)o; CObjUpval *upval = (CObjUpval*)o;
@ -331,6 +378,11 @@ void printObject(CObj *o) {
printValue(*upval->val); printValue(*upval->val);
break; break;
} }
case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)o;
printObject((CObj*)closure->function); // just print the function
break;
}
case COBJ_FUNCTION: { case COBJ_FUNCTION: {
CObjFunction *objFunc = (CObjFunction*)o; CObjFunction *objFunc = (CObjFunction*)o;
if (objFunc->name != NULL) if (objFunc->name != NULL)
@ -350,13 +402,8 @@ void printObject(CObj *o) {
printValue(method->func); printValue(method->func);
break; break;
} }
case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)o;
printObject((CObj*)closure->function); // just print the function
break;
}
default: default:
printf("<unkn obj>"); printf("<unkn obj %p>", o);
} }
} }
@ -364,6 +411,7 @@ const char *cosmoO_typeStr(CObj* obj) {
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: return "<string>"; case COBJ_STRING: return "<string>";
case COBJ_OBJECT: return "<object>"; case COBJ_OBJECT: return "<object>";
case COBJ_DICT: return "<dictionary>";
case COBJ_FUNCTION: return "<function>"; case COBJ_FUNCTION: return "<function>";
case COBJ_CFUNCTION: return "<c function>"; case COBJ_CFUNCTION: return "<c function>";
case COBJ_METHOD: return "<method>"; case COBJ_METHOD: return "<method>";

View File

@ -13,6 +13,7 @@ typedef uint32_t cosmo_Flag;
typedef enum { typedef enum {
COBJ_STRING, COBJ_STRING,
COBJ_OBJECT, COBJ_OBJECT,
COBJ_DICT, // dictionary
COBJ_FUNCTION, COBJ_FUNCTION,
COBJ_CFUNCTION, COBJ_CFUNCTION,
// internal use // internal use
@ -49,6 +50,11 @@ typedef struct CObjObject {
struct CObjObject *proto; // protoobject, describes the behavior of the object struct CObjObject *proto; // protoobject, describes the behavior of the object
} CObjObject; } CObjObject;
typedef struct CObjDict { // dictionary, a wrapper for CTable
CommonHeader; // "is a" CObj
CTable tbl;
} CObjDict;
typedef struct CObjFunction { typedef struct CObjFunction {
CommonHeader; // "is a" CObj CommonHeader; // "is a" CObj
CChunk chunk; CChunk chunk;
@ -109,6 +115,7 @@ void cosmoO_free(CState *state, CObj* obj);
bool cosmoO_equal(CObj* obj1, CObj* obj2); bool cosmoO_equal(CObj* obj1, CObj* obj2);
CObjObject *cosmoO_newObject(CState *state); CObjObject *cosmoO_newObject(CState *state);
CObjDict *cosmoO_newDictionary(CState *state);
CObjFunction *cosmoO_newFunction(CState *state); CObjFunction *cosmoO_newFunction(CState *state);
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func); CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func);
CObjMethod *cosmoO_newMethod(CState *state, CObjClosure *func, CObjObject *obj); 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); bool cosmoO_getObject(CState *state, CObjObject *object, CValue key, CValue *val);
void cosmoO_setObject(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_setUserData(CState *state, CObjObject *object, void *p);
void *cosmoO_getUserData(CState *state, CObjObject *object); void *cosmoO_getUserData(CState *state, CObjObject *object);

View File

@ -29,9 +29,12 @@ typedef enum {
OP_CALL, // calls top[-uint8_t] OP_CALL, // calls top[-uint8_t]
OP_CLOSURE, OP_CLOSURE,
OP_CLOSE, OP_CLOSE,
OP_NEWDICT,
OP_INDEX,
OP_NEWINDEX,
OP_NEWOBJECT, OP_NEWOBJECT,
OP_GETOBJECT,
OP_SETOBJECT, OP_SETOBJECT,
OP_GETOBJECT,
OP_INVOKE, OP_INVOKE,
// ARITHMETIC // ARITHMETIC
@ -46,6 +49,7 @@ typedef enum {
OP_INCLOCAL, // pushes old value to stack, adds (uint8_t-128) to local[uint8_t] 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_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_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]] OP_INCOBJECT, // pushes old value to stack, adds (uint8_t-128) to obj[const[uint16_t]]
// EQUALITY // EQUALITY

View File

@ -558,39 +558,23 @@ static void object(CParseState *pstate, bool canAssign) {
if (!match(pstate, TOKEN_RIGHT_BRACE)) { if (!match(pstate, TOKEN_RIGHT_BRACE)) {
do { do {
if (match(pstate, TOKEN_IDENTIFIER)) {
uint16_t fieldIdent = identifierConstant(pstate, &pstate->previous);
// OP_NEWOBJECT expects the key on the stack before the value
writeu8(pstate, OP_LOADCONST);
writeu16(pstate, fieldIdent);
consume(pstate, TOKEN_EQUAL, "Invalid syntax!");
// parse field
expression(pstate);
valuePopped(pstate, 1);
} else if (match(pstate, TOKEN_LEFT_BRACKET)) {
// parse the key first // parse the key first
expression(pstate); // should parse until end bracket expression(pstate); // should parse until ':'
consume(pstate, TOKEN_RIGHT_BRACKET, "Expected ']' to end index definition."); consume(pstate, TOKEN_COLON, "Expected ':' to mark end of key and start of value!");
consume(pstate, TOKEN_EQUAL, "Expected '='.");
// now, parse the value (until comma) // now, parse the value (until comma)
expression(pstate); expression(pstate);
valuePopped(pstate, 2);
} else {
error(pstate, "Invalid syntax!");
}
// "pop" the 2 values
valuePopped(pstate, 2);
entries++; entries++;
} while (match(pstate, TOKEN_COMMA) && !pstate->hadError); } while (match(pstate, TOKEN_COMMA) && !pstate->hadError);
consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition."); 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); writeu16(pstate, entries);
valuePushed(pstate, 1); valuePushed(pstate, 1);
} }
@ -634,12 +618,19 @@ static void _index(CParseState *pstate, bool canAssign) {
if (canAssign && match(pstate, TOKEN_EQUAL)) { if (canAssign && match(pstate, TOKEN_EQUAL)) {
expression(pstate); expression(pstate);
writeu8(pstate, OP_SETOBJECT); writeu8(pstate, OP_NEWINDEX);
valuePopped(pstate, 3); // pops key, value & object 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 { } else {
writeu8(pstate, OP_GETOBJECT); writeu8(pstate, OP_INDEX);
valuePopped(pstate, 1); // pops key & object but also pushes the field so total popped is 1
} }
valuePopped(pstate, 1); // pops key & object but also pushes the field so total popped is 1
} }
static void increment(CParseState *pstate, int val) { static void increment(CParseState *pstate, int val) {
@ -716,6 +707,7 @@ ParseRule ruleTable[] = {
[TOKEN_LEFT_BRACKET] = {NULL, _index, PREC_CALL}, [TOKEN_LEFT_BRACKET] = {NULL, _index, PREC_CALL},
[TOKEN_RIGHT_BRACKET] = {NULL, NULL, PREC_NONE}, [TOKEN_RIGHT_BRACKET] = {NULL, NULL, PREC_NONE},
[TOKEN_COMMA] = {NULL, NULL, PREC_NONE}, [TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
[TOKEN_COLON] = {NULL, NULL, PREC_NONE},
[TOKEN_DOT] = {NULL, dot, PREC_CALL}, [TOKEN_DOT] = {NULL, dot, PREC_CALL},
[TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT}, [TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT},
[TOKEN_MINUS] = {unary, binary, PREC_TERM}, [TOKEN_MINUS] = {unary, binary, PREC_TERM},

141
src/cvm.c
View File

@ -240,6 +240,25 @@ COSMO_API void cosmoV_makeObject(CState *state, int pairs) {
cosmoV_pushValue(state, cosmoV_newObj(newObj)); 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) { COSMO_API bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val) {
if (cosmoO_getObject(state, object, key, val)) { if (cosmoO_getObject(state, object, key, val)) {
if (IS_OBJ(*val)) { if (IS_OBJ(*val)) {
@ -383,6 +402,70 @@ bool cosmoV_execute(CState *state) {
cosmoV_pop(state); cosmoV_pop(state);
break; 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: { case OP_NEWOBJECT: {
uint16_t pairs = READUINT(); uint16_t pairs = READUINT();
cosmoV_makeObject(state, pairs); cosmoV_makeObject(state, pairs);
@ -557,6 +640,52 @@ bool cosmoV_execute(CState *state) {
break; 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: { case OP_INCOBJECT: {
int8_t inc = READBYTE() - 128; // ammount we're incrementing by int8_t inc = READBYTE() - 128; // ammount we're incrementing by
uint16_t indx = READUINT(); uint16_t indx = READUINT();
@ -570,17 +699,19 @@ bool cosmoV_execute(CState *state) {
} }
CObjObject *object = (CObjObject*)cosmoV_readObj(*temp); 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 // pop the object off the stack
cosmoV_pop(state); cosmoV_pop(state);
// check that it's a number value // check that it's a number value
if (IS_NUMBER(*val)) { if (IS_NUMBER(val)) {
cosmoV_pushValue(state, *val); // pushes old value onto the stack :) cosmoV_pushValue(state, val); // pushes old value onto the stack :)
*val = cosmoV_newNumber(cosmoV_readNumber(*val) + inc); cosmoO_setObject(state, object, ident, cosmoV_newNumber(cosmoV_readNumber(val) + inc));
} else { } else {
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(val));
} }
break; break;

View File

@ -15,6 +15,7 @@ typedef enum {
// args = # of pass parameters, nresults = # of expected results // args = # of pass parameters, nresults = # of expected results
COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args); COSMO_API COSMOVMRESULT cosmoV_call(CState *state, int args);
COSMO_API void cosmoV_makeObject(CState *state, int pairs); 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 bool cosmoV_getObject(CState *state, CObjObject *object, CValue key, CValue *val);
COSMO_API void cosmoV_error(CState *state, const char *format, ...); COSMO_API void cosmoV_error(CState *state, const char *format, ...);