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 # make clean && make && ./bin/cosmo
CC=clang CC=clang
CFLAGS=-fPIE -O3 #-g3 CFLAGS=-fPIE -g3 #-O3
LDFLAGS=#-fsanitize=address LDFLAGS=-fsanitize=address
OUT=bin/cosmo OUT=bin/cosmo
CHDR=\ CHDR=\

View File

@ -116,6 +116,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_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: case OP_ADD:
return simpleInstruction("OP_ADD", offset); return simpleInstruction("OP_ADD", offset);
case OP_SUB: case OP_SUB:

View File

@ -80,7 +80,7 @@ void markArray(CState *state, CValueArray *array) {
void blackenObject(CState *state, CObj *obj) { void blackenObject(CState *state, CObj *obj) {
switch (obj->type) { switch (obj->type) {
case COBJ_STRING: 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: case COBJ_CFUNCTION:
// stubbed // stubbed
break; break;

View File

@ -42,10 +42,10 @@ void cosmoO_freeObject(CState *state, CObj* obj) {
cosmoM_free(state, CObjString, objStr); cosmoM_free(state, CObjString, objStr);
break; break;
} }
case COBJ_TABLE: { case COBJ_OBJECT: {
CObjTable *objTbl = (CObjTable*)obj; CObjObject *objTbl = (CObjObject*)obj;
cosmoT_clearTable(state, &objTbl->tbl); cosmoT_clearTable(state, &objTbl->tbl);
cosmoM_free(state, CObjTable, objTbl); cosmoM_free(state, CObjObject, objTbl);
break; break;
} }
case COBJ_UPVALUE: { case COBJ_UPVALUE: {
@ -88,10 +88,10 @@ bool cosmoO_equalObject(CObj* obj1, CObj* obj2) {
} }
} }
CObjTable *cosmoO_newTable(CState *state) { CObjObject *cosmoO_newObject(CState *state, int startCap) {
CObjTable *tbl = (CObjTable*)cosmoO_allocateObject(state, sizeof(CObjTable), COBJ_TABLE); 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; return tbl;
} }
@ -188,9 +188,9 @@ CObjString *cosmoO_toString(CState *state, CObj *val) {
CObjFunction *func = (CObjFunction*)val; CObjFunction *func = (CObjFunction*)val;
return func->name != NULL ? func->name : cosmoO_copyString(state, UNNAMEDCHUNK, strlen(UNNAMEDCHUNK)); 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]; 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); return cosmoO_copyString(state, buf, sz);
} }
default: default:
@ -205,8 +205,8 @@ void printObject(CObj *o) {
printf("\"%.*s\"", objStr->length, objStr->str); printf("\"%.*s\"", objStr->length, objStr->str);
break; break;
} }
case COBJ_TABLE: { case COBJ_OBJECT: {
printf("<tbl> %p", o); printf("<obj> %p", o);
return; return;
} }
case COBJ_UPVALUE: { case COBJ_UPVALUE: {

View File

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

View File

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

View File

@ -30,6 +30,7 @@ typedef enum {
PREC_TERM, // + - PREC_TERM, // + -
PREC_FACTOR, // * / PREC_FACTOR, // * /
PREC_UNARY, // ! - PREC_UNARY, // ! -
PREC_OBJ, // {}
PREC_CALL, // . () PREC_CALL, // . ()
PREC_PRIMARY // everything else PREC_PRIMARY // everything else
} Precedence; } 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[] = { ParseRule ruleTable[] = {
[TOKEN_LEFT_PAREN] = {group, call_, PREC_CALL}, [TOKEN_LEFT_PAREN] = {group, call_, PREC_CALL},
[TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE}, [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_RIGHT_BRACE] = {NULL, NULL, PREC_NONE},
[TOKEN_COMMA] = {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_DOT_DOT] = {NULL, concat, PREC_CONCAT},
[TOKEN_MINUS] = {unary, binary, PREC_TERM}, [TOKEN_MINUS] = {unary, binary, PREC_TERM},
[TOKEN_PLUS] = {NULL, binary, PREC_TERM}, [TOKEN_PLUS] = {NULL, binary, PREC_TERM},

View File

@ -8,13 +8,13 @@
#define MAX_TABLE_FILL 0.75 #define MAX_TABLE_FILL 0.75
void cosmoT_initTable(CState *state, CTable *tbl, int startCap) { 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->count = 0;
tbl->table = NULL; // to let out GC know we're initalizing 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 // 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].key = cosmoV_newNil();
tbl->table[i].val = cosmoV_newNil(); tbl->table[i].val = cosmoV_newNil();
} }

View File

@ -317,6 +317,63 @@ int cosmoV_execute(CState *state) {
cosmoV_pop(state); cosmoV_pop(state);
break; 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 case OP_ADD: { // pop 2 values off the stack & try to add them together
BINARYOP(cosmoV_newNumber, +); BINARYOP(cosmoV_newNumber, +);
break; break;