Added ':' invoke operator

You're now forced to use ':' when calling a method on an object. Some of Lua's ideas really are just better in practice /shrug
This commit is contained in:
CPunch 2021-01-12 17:46:22 -06:00
parent c510c9aebf
commit ab86e19cfe
6 changed files with 91 additions and 100 deletions

View File

@ -153,22 +153,15 @@ void cosmoB_loadLibrary(CState *state) {
cosmoV_pushString(state, "sub");
cosmoV_pushCFunction(state, cosmoB_sSub);
cosmoV_makeTable(state, 1);
cosmoV_makeObject(state, 1);
// string.*
// register "string" to that table
cosmoV_register(state, 1);
// make string object for CObjStrings
// sub
cosmoV_pushString(state, "sub");
cosmoV_pushCFunction(state, cosmoB_sSub);
cosmoV_makeObject(state, 1);
// grab the object from the stack and set the base protoObject
StkPtr obj = cosmoV_pop(state);
StkPtr obj = cosmoV_getTop(state, 0);
state->protoObjects[COBJ_STRING] = cosmoV_readObject(*obj);
// register "string" to the global table
cosmoV_register(state, 1);
}
// ================================================================ [DEBUG] ================================================================

View File

@ -5,7 +5,7 @@
#include "cstate.h"
//#define GC_STRESS
#define GC_STRESS
//#define GC_DEBUG
// arrays will grow by a factor of 2
#define GROW_FACTOR 2

View File

@ -154,6 +154,7 @@ CObjError *cosmoO_newError(CState *state, CValue err) {
CObjError *cerror = (CObjError*)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
cerror->err = err;
cerror->frameCount = state->frameCount;
cerror->parserError = false;
// allocate the callframe
cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount);
@ -462,7 +463,7 @@ void printObject(CObj *o) {
switch (o->type) {
case COBJ_STRING: {
CObjString *objStr = (CObjString*)o;
printf("\"%.*s\"", objStr->length, objStr->str);
printf("%.*s", objStr->length, objStr->str);
break;
}
case COBJ_OBJECT: {
@ -474,17 +475,6 @@ void printObject(CObj *o) {
printf("<tbl> %p", (void*)tbl);
break;
}
case COBJ_UPVALUE: {
CObjUpval *upval = (CObjUpval*)o;
printf("<upvalue %p> -> ", (void*)upval->val);
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)
@ -498,12 +488,30 @@ void printObject(CObj *o) {
printf("<c function> %p", (void*)objCFunc->cfunc);
break;
}
case COBJ_ERROR: {
CObjError *err = (CObjError*)o;
printf("<error> %p -> ", (void*)o);
printValue(err->err);
break;
}
case COBJ_METHOD: {
CObjMethod *method = (CObjMethod*)o;
printf("<method> %p : ", (void*)method->obj);
printf("<method> %p -> ", (void*)method);
printValue(method->func);
break;
}
case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)o;
printf("<closure> %p -> ", (void*)closure);
printObject((CObj*)closure->function); // just print the function
break;
}
case COBJ_UPVALUE: {
CObjUpval *upval = (CObjUpval*)o;
printf("<upvalue> %p -> ", (void*)upval->val);
printValue(*upval->val);
break;
}
default:
printf("<unkn obj %p>", (void*)o);
}

View File

@ -631,8 +631,8 @@ static void table(CParseState *pstate, bool canAssign, Precedence prec) {
// grab value/key
expression(pstate, 1, true);
// they want to make a table with key:value
if (match(pstate, TOKEN_COLON) && tblType != 1) {
// they want to make a table with key = value
if (match(pstate, TOKEN_EQUAL) && tblType != 1) {
tblType = 2; // dictionary-like
// grab value
@ -718,14 +718,6 @@ static void dot(CParseState *pstate, bool canAssign, Precedence prec) {
writeu8(pstate, OP_INCOBJECT);
writeu8(pstate, 128 - 1);
writeu16(pstate, name);
} else if (match(pstate, TOKEN_LEFT_PAREN)) { // it's an invoked call
uint8_t args = parseArguments(pstate);
writeu8(pstate, OP_INVOKE);
writeu8(pstate, args);
writeu8(pstate, pstate->compiler->expectedValues);
writeu16(pstate, name);
valuePopped(pstate, args+1); // args + function
valuePushed(pstate, pstate->compiler->expectedValues);
} else {
writeu8(pstate, OP_GETOBJECT);
writeu16(pstate, name);
@ -754,6 +746,31 @@ static void _index(CParseState *pstate, bool canAssign, Precedence prec) {
valuePopped(pstate, 1); // pops key & object but also pushes the value so total popped is 1
}
static void invoke(CParseState *pstate, bool canAssign, Precedence prec) {
int returnNum = pstate->compiler->expectedValues;
uint16_t name;
uint8_t args;
consume(pstate, TOKEN_IDENTIFIER, "Expected method name after ':'.");
name = identifierConstant(pstate, &pstate->previous);
consume(pstate, TOKEN_LEFT_PAREN, "Expected '(' after method identifier!");
args = parseArguments(pstate);
// if we're not the last token in this expression or we're expecting multiple values, we should return only 1 value!!
if (!isLast(pstate, prec) || (returnNum > 1 && check(pstate, TOKEN_COMMA)))
returnNum = 1;
writeu8(pstate, OP_INVOKE);
writeu8(pstate, args);
writeu8(pstate, returnNum);
writeu16(pstate, name);
valuePopped(pstate, args+1); // args + function
valuePushed(pstate, returnNum);
}
// ++test.field[1]
// this function is kind of spaghetti, feel free to rewrite (if you dare!)
static void walkIndexes(CParseState *pstate, int lastIndexType, uint16_t lastIdent, int val) {
@ -879,7 +896,7 @@ ParseRule ruleTable[] = {
[TOKEN_LEFT_BRACKET] = {table, _index, PREC_CALL},
[TOKEN_RIGHT_BRACKET] = {NULL, NULL, PREC_NONE},
[TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
[TOKEN_COLON] = {NULL, NULL, PREC_NONE},
[TOKEN_COLON] = {NULL, invoke, PREC_CALL},
[TOKEN_DOT] = {NULL, dot, PREC_CALL},
[TOKEN_DOT_DOT] = {NULL, concat, PREC_CONCAT},
[TOKEN_DOT_DOT_DOT] = {NULL, NULL, PREC_NONE},

View File

@ -439,21 +439,16 @@ COSMO_API bool cosmoV_get(CState *state, CObj *_obj, CValue key, CValue *val) {
return false;
}
// push the object onto the stack so the GC can find it
cosmoV_pushValue(state, cosmoV_newObj(object));
if (cosmoO_getRawObject(state, object, key, val)) {
// is it a function? if so, make it a method to the current object
if (IS_OBJ(*val) && (cosmoV_readObj(*val)->type == COBJ_CLOSURE || cosmoV_readObj(*val)->type == COBJ_CFUNCTION)) {
CObjMethod *method = cosmoO_newMethod(state, *val, _obj);
*val = cosmoV_newObj(method);
} // else, just pass the raw object
// *val now equals the response, pop the object
cosmoV_pop(state);
return true;
}
cosmoV_pop(state);
return true;
return false;
}
COSMO_API bool cosmoV_set(CState *state, CObj *_obj, CValue key, CValue val) {
@ -469,6 +464,19 @@ COSMO_API bool cosmoV_set(CState *state, CObj *_obj, CValue key, CValue val) {
return true;
}
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val) {
if (!cosmoV_get(state, obj, key, val))
return false;
// if the result is callable, wrap it in an method
if (IS_CALLABLE(*val)) {
CObjMethod *method = cosmoO_newMethod(state, *val, obj);
*val = cosmoV_newObj(method);
}
return true;
}
int _tbl__next(CState *state, int nargs, CValue *args) {
if (nargs != 1) {
cosmoV_error(state, "Expected 1 parameter, %d received!", nargs);
@ -689,7 +697,7 @@ int cosmoV_execute(CState *state) {
}
}
cosmoV_setTop(state, 2); // pops the object & the key
cosmoV_setTop(state, 2); // pops the table & the key
cosmoV_pushValue(state, val); // pushes the field result
} else {
cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp));
@ -700,7 +708,7 @@ int cosmoV_execute(CState *state) {
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
StkPtr temp = cosmoV_getTop(state, 2); // table is after the key
// sanity check
if (IS_OBJ(*temp)) {
@ -748,19 +756,11 @@ int cosmoV_execute(CState *state) {
// sanity check
if (IS_OBJ(*temp)) {
switch (cosmoV_readObj(*temp)->type) {
case COBJ_TABLE: { // syntax sugar, makes "namespaces" possible
CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp);
cosmoT_get(&tbl->tbl, constants[ident], &val);
break;
}
default:
if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val))
return -1;
}
if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val))
return -1;
} else {
cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp));
CObjString *field = cosmoV_toString(state, constants[ident]);
cosmoV_error(state, "Couldn't get field '%s' from type %s!", field->str, cosmoV_typeStr(*temp));
return -1;
}
@ -775,20 +775,11 @@ int cosmoV_execute(CState *state) {
// sanity check
if (IS_OBJ(*temp)) {
switch (cosmoV_readObj(*temp)->type) {
case COBJ_TABLE: { // index and set the table
CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp);
CValue *newVal = cosmoT_insert(state, &tbl->tbl, constants[ident]);
*newVal = *value; // set the index
break;
}
default:
if (!cosmoV_set(state, cosmoV_readObj(*temp), constants[ident], *value))
return -1;
}
if (!cosmoV_set(state, cosmoV_readObj(*temp), constants[ident], *value))
return -1;
} else {
cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp));
CObjString *field = cosmoV_toString(state, constants[ident]);
cosmoV_error(state, "Couldn't set field '%s' on type %s!", field->str, cosmoV_typeStr(*temp));
return -1;
}
@ -796,7 +787,7 @@ int cosmoV_execute(CState *state) {
cosmoV_setTop(state, 2);
break;
}
case OP_INVOKE: { // this is an optimization made by the parser, instead of allocating a CObjMethod every time we want to invoke a method, we shrink it down into one minimal instruction!
case OP_INVOKE: {
uint8_t args = READBYTE();
uint8_t nres = READBYTE();
uint16_t ident = READUINT();
@ -805,32 +796,12 @@ int cosmoV_execute(CState *state) {
// sanity check
if (IS_OBJ(*temp)) {
switch (cosmoV_readObj(*temp)->type) {
case COBJ_TABLE: { // again, syntax sugar ("""""namespaces""""")
CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp);
cosmoT_get(&tbl->tbl, constants[ident], &val);
// call closure/cfunction
if (!callCValue(state, val, args, nres, 0))
return -1;
break;
}
default: {
CObjObject *object = cosmoO_grabProto(cosmoV_readObj(*temp));
if (object == NULL) {
cosmoV_error(state, "No proto defined! Couldn't get from type %s!", cosmoV_typeStr(*temp));
return -1;
}
cosmoO_getRawObject(state, object, constants[ident], &val); // we use cosmoO_getRawObject instead of the wrapper so we get the raw value from the object instead of the CObjMethod wrapper
// now invoke the method!
invokeMethod(state, cosmoV_readObj(*temp), val, args, nres, 1);
break;
}
}
// get the field from the object
if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val))
return -1;
// now invoke the method!
invokeMethod(state, cosmoV_readObj(*temp), val, args, nres, 1);
} else {
cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp));
return -1;
@ -895,7 +866,7 @@ int cosmoV_execute(CState *state) {
}
// get __next method and place it at the top of the stack
cosmoV_get(state, cosmoV_readObj(*iObj), cosmoV_newObj(state->iStrings[ISTRING_NEXT]), iObj);
cosmoV_getMethod(state, cosmoV_readObj(*iObj), cosmoV_newObj(state->iStrings[ISTRING_NEXT]), iObj);
} else {
cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
return -1;

View File

@ -35,6 +35,8 @@ COSMO_API bool cosmoV_compileString(CState *state, const char *src, const char *
COSMO_API bool cosmoV_get(CState *state, CObj *obj, CValue key, CValue *val);
COSMO_API bool cosmoV_set(CState *state, CObj *obj, CValue key, CValue val);
// wraps the closure into a CObjMethod, so the function is called as an invoked method
COSMO_API bool cosmoV_getMethod(CState *state, CObj *obj, CValue key, CValue *val);
// nice to have wrappers