added basic objects

This commit is contained in:
CPunch 2020-11-03 22:10:51 -06:00
parent fe93a0b715
commit e1d33855c3
9 changed files with 120 additions and 25 deletions

View File

@ -1,8 +1,8 @@
# make clean && make && ./bin/cosmo
CC=clang
CFLAGS=-fPIE -O3 #-g3
LDFLAGS=#-fsanitize=address
CFLAGS=-fPIE -g3 #-O3
LDFLAGS=-fsanitize=address
OUT=bin/cosmo
CHDR=\

View File

@ -116,6 +116,12 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
}
case OP_CLOSE:
return simpleInstruction("OP_CLOSE", offset);
case OP_NEWOBJECT:
return shortOperandInstruction("OP_NEWOBJECT", chunk, offset);
case OP_GETOBJECT:
return simpleInstruction("OP_GETOBJECT", offset);
case OP_SETOBJECT:
return simpleInstruction("OP_SETOBJECT", offset);
case OP_ADD:
return simpleInstruction("OP_ADD", offset);
case OP_SUB:

View File

@ -80,7 +80,7 @@ void markArray(CState *state, CValueArray *array) {
void blackenObject(CState *state, CObj *obj) {
switch (obj->type) {
case COBJ_STRING:
case COBJ_TABLE: // TODO: when metatables are added, make sure they're marked
case COBJ_OBJECT: // TODO: when metatables are added, make sure they're marked
case COBJ_CFUNCTION:
// stubbed
break;

View File

@ -42,10 +42,10 @@ void cosmoO_freeObject(CState *state, CObj* obj) {
cosmoM_free(state, CObjString, objStr);
break;
}
case COBJ_TABLE: {
CObjTable *objTbl = (CObjTable*)obj;
case COBJ_OBJECT: {
CObjObject *objTbl = (CObjObject*)obj;
cosmoT_clearTable(state, &objTbl->tbl);
cosmoM_free(state, CObjTable, objTbl);
cosmoM_free(state, CObjObject, objTbl);
break;
}
case COBJ_UPVALUE: {
@ -88,10 +88,10 @@ bool cosmoO_equalObject(CObj* obj1, CObj* obj2) {
}
}
CObjTable *cosmoO_newTable(CState *state) {
CObjTable *tbl = (CObjTable*)cosmoO_allocateObject(state, sizeof(CObjTable), COBJ_TABLE);
CObjObject *cosmoO_newObject(CState *state, int startCap) {
CObjObject *tbl = (CObjObject*)cosmoO_allocateObject(state, sizeof(CObjObject), COBJ_OBJECT);
cosmoT_initTable(state, &tbl->tbl, 8); // start the table at 8
cosmoT_initTable(state, &tbl->tbl, startCap);
return tbl;
}
@ -188,9 +188,9 @@ CObjString *cosmoO_toString(CState *state, CObj *val) {
CObjFunction *func = (CObjFunction*)val;
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK));
}
case COBJ_TABLE: { // TODO: maybe not safe??
case COBJ_OBJECT: { // TODO: maybe not safe??
char buf[64];
int sz = sprintf(buf, "<tbl> %p", val) + 1; // +1 for the null character
int sz = sprintf(buf, "<obj> %p", val) + 1; // +1 for the null character
return cosmoO_copyString(state, buf, sz);
}
default:
@ -205,8 +205,8 @@ void printObject(CObj *o) {
printf("\"%.*s\"", objStr->length, objStr->str);
break;
}
case COBJ_TABLE: {
printf("<tbl> %p", o);
case COBJ_OBJECT: {
printf("<obj> %p", o);
return;
}
case COBJ_UPVALUE: {

View File

@ -10,7 +10,7 @@ typedef struct CState CState;
typedef enum {
COBJ_STRING,
COBJ_TABLE,
COBJ_OBJECT,
COBJ_FUNCTION,
COBJ_CFUNCTION,
// internal use
@ -35,11 +35,11 @@ typedef struct CObjString {
uint32_t hash; // for hashtable lookup
} CObjString;
typedef struct CObjTable {
typedef struct CObjObject {
CommonHeader; // "is a" CObj
CTable tbl;
//struct CObjTable *meta; // metatable, used to describe table behavior
} CObjTable;
//struct CObjObject *meta; // metaobject, used to describe object behavior
} CObjObject;
typedef struct CObjFunction {
CommonHeader; // "is a" CObj
@ -69,13 +69,13 @@ typedef struct CObjUpval {
} CObjUpval;
#define IS_STRING(x) isObjType(x, COBJ_STRING)
#define IS_TABLE(x) isObjType(x, COBJ_TABLE)
#define IS_TABLE(x) isObjType(x, COBJ_OBJECT)
#define IS_FUNCTION(x) isObjType(x, COBJ_FUNCTION)
#define IS_CFUNCTION(x) isObjType(x, COBJ_CFUNCTION)
#define IS_CLOSURE(x) isObjType(x, COBJ_CLOSURE)
#define cosmoV_readString(x) ((CObjString*)cosmoV_readObj(x))
#define cosmoV_readTable(x) ((CObjTable*)cosmoV_readObj(x))
#define cosmoV_readObject(x) ((CObjObject*)cosmoV_readObj(x))
#define cosmoV_readFunction(x) ((CObjFunction*)cosmoV_readObj(x))
#define cosmoV_readCFunction(x) (((CObjCFunction*)cosmoV_readObj(x))->cfunc)
#define cosmoV_readClosure(x) ((CObjClosure*)cosmoV_readObj(x))
@ -89,7 +89,7 @@ void cosmoO_freeObject(CState *state, CObj* obj);
bool cosmoO_equalObject(CObj* obj1, CObj* obj2);
CObjTable *cosmoO_newTable(CState *state);
CObjObject *cosmoO_newObject(CState *state, int startCap);
CObjFunction *cosmoO_newFunction(CState *state);
CObjCFunction *cosmoO_newCFunction(CState *state, CosmoCFunction func);
CObjClosure *cosmoO_newClosure(CState *state, CObjFunction *func);

View File

@ -29,6 +29,9 @@ typedef enum {
OP_CALL, // calls top[-uint8_t]
OP_CLOSURE,
OP_CLOSE,
OP_NEWOBJECT,
OP_GETOBJECT,
OP_SETOBJECT,
// ARITHMETIC
OP_ADD,

View File

@ -30,6 +30,7 @@ typedef enum {
PREC_TERM, // + -
PREC_FACTOR, // * /
PREC_UNARY, // ! -
PREC_OBJ, // {}
PREC_CALL, // . ()
PREC_PRIMARY // everything else
} Precedence;
@ -502,13 +503,41 @@ static void call_(CParseState *pstate, bool canAssign) {
}
}
static void object(CParseState *pstate, bool canAssign) {
// already consumed the beginning '{'
int entries = 0;
consume(pstate, TOKEN_RIGHT_BRACE, "Expected '}' to end object definition!");
writeu8(pstate, OP_NEWOBJECT);
writeu8(pstate, entries);
valuePushed(pstate, 1);
}
static void dot(CParseState *pstate, bool canAssign) {
consume(pstate, TOKEN_IDENTIFIER, "Expect property name after '.'.");
uint8_t name = identifierConstant(pstate, &pstate->previous);
writeu8(pstate, OP_LOADCONST);
writeu16(pstate, name);
valuePushed(pstate, 1);
if (canAssign && match(pstate, TOKEN_EQUAL)) {
expression(pstate);
writeu8(pstate, OP_SETOBJECT);
valuePopped(pstate, 3); // pops key, value & object
} else {
writeu8(pstate, OP_GETOBJECT);
valuePopped(pstate, 1); // pops key & object but also pushes the field so total popped is 1
}
}
ParseRule ruleTable[] = {
[TOKEN_LEFT_PAREN] = {group, call_, PREC_CALL},
[TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE},
[TOKEN_LEFT_BRACE] = {NULL, NULL, PREC_NONE},
[TOKEN_LEFT_BRACE] = {object, NULL, PREC_OBJ},
[TOKEN_RIGHT_BRACE] = {NULL, NULL, PREC_NONE},
[TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
[TOKEN_DOT] = {NULL, NULL, PREC_NONE},
[TOKEN_DOT] = {NULL, dot, PREC_CALL},
[TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT},
[TOKEN_MINUS] = {unary, binary, PREC_TERM},
[TOKEN_PLUS] = {NULL, binary, PREC_TERM},

View File

@ -8,13 +8,13 @@
#define MAX_TABLE_FILL 0.75
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) {
tbl->capacity = startCap;
tbl->capacity = startCap != 0 ? startCap : ARRAY_START; // sanity check :P
tbl->count = 0;
tbl->table = NULL; // to let out GC know we're initalizing
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * startCap);
tbl->table = cosmoM_xmalloc(state, sizeof(CTableEntry) * tbl->capacity);
// init everything to NIL
for (int i = 0; i < startCap; i++) {
for (int i = 0; i < tbl->capacity; i++) {
tbl->table[i].key = cosmoV_newNil();
tbl->table[i].val = cosmoV_newNil();
}

View File

@ -317,6 +317,63 @@ int cosmoV_execute(CState *state) {
cosmoV_pop(state);
break;
}
case OP_NEWOBJECT: {
uint8_t entries = READBYTE();
StkPtr key, val;
CObjObject *newObj = cosmoO_newObject(state, entries * 3); // start the table with enough space to hopefully prevent reallocation since that's costly
cosmoV_pushValue(state, cosmoV_newObj(newObj)); // so our GC doesn't free our new object
for (int i = 0; i < entries; i++) {
val = cosmoV_getTop(state, (i*2) + 2);
key = cosmoV_getTop(state, (i*2) + 1);
// set key/value pair
CValue *newVal = cosmoT_insert(state, &newObj->tbl, *key);
*newVal = *val;
}
// once done, pop everything off the stack + push new object
cosmoV_setTop(state, (entries * 2) + 1);
cosmoV_pushValue(state, cosmoV_newObj(newObj));
break;
}
case OP_GETOBJECT: {
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 object
// sanity check
if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) {
runtimeError(state, "Couldn't get from non-object!");
break;
}
CObjObject *object = (CObjObject*)temp->val.obj;
CValue val; // to hold our value
cosmoT_get(&object->tbl, *key, &val);
cosmoV_setTop(state, 2); // pops the object & the key
cosmoV_pushValue(state, val); // pushes the field result
break;
}
case OP_SETOBJECT: {
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 (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) {
runtimeError(state, "Couldn't set a field on a non-object!");
break;
}
CObjObject *object = (CObjObject*)temp->val.obj;
CValue *newVal = cosmoT_insert(state, &object->tbl, *key);
*newVal = *value;
// pop everything off the stack
cosmoV_setTop(state, 3);
break;
}
case OP_ADD: { // pop 2 values off the stack & try to add them together
BINARYOP(cosmoV_newNumber, +);
break;