mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-22 07:20: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
|
# 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
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
130
src/cobj.c
130
src/cobj.c
@ -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>";
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
42
src/cparse.c
42
src/cparse.c
@ -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
141
src/cvm.c
@ -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;
|
||||||
|
@ -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, ...);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user