mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-12-22 14:30:05 +00:00
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:
parent
6445dae04c
commit
181ef8a18c
2
Makefile
2
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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -12,6 +12,7 @@ typedef enum {
|
||||
TOKEN_LEFT_BRACKET,
|
||||
TOKEN_RIGHT_BRACKET,
|
||||
TOKEN_COMMA,
|
||||
TOKEN_COLON,
|
||||
TOKEN_DOT,
|
||||
TOKEN_DOT_DOT,
|
||||
TOKEN_MINUS,
|
||||
|
@ -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;
|
||||
|
158
src/cobj.c
158
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, "<obj> %p", obj) + 1; // +1 for the null character
|
||||
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:
|
||||
return cosmoO_copyString(state, "<unkn obj>", 10);
|
||||
}
|
||||
@ -323,7 +365,12 @@ void printObject(CObj *o) {
|
||||
}
|
||||
case COBJ_OBJECT: {
|
||||
printf("<obj> %p", o);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
case COBJ_DICT: {
|
||||
CObjDict *dict = (CObjDict*)o;
|
||||
printf("<dict> %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("<unkn obj>");
|
||||
printf("<unkn obj %p>", o);
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,6 +411,7 @@ const char *cosmoO_typeStr(CObj* obj) {
|
||||
switch (obj->type) {
|
||||
case COBJ_STRING: return "<string>";
|
||||
case COBJ_OBJECT: return "<object>";
|
||||
case COBJ_DICT: return "<dictionary>";
|
||||
case COBJ_FUNCTION: return "<function>";
|
||||
case COBJ_CFUNCTION: return "<c function>";
|
||||
case COBJ_METHOD: return "<method>";
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
48
src/cparse.c
48
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},
|
||||
|
141
src/cvm.c
141
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;
|
||||
|
@ -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, ...);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user