mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-05 08:10:05 +00:00
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:
parent
c510c9aebf
commit
ab86e19cfe
@ -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] ================================================================
|
||||
|
@ -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
|
||||
|
34
src/cobj.c
34
src/cobj.c
@ -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);
|
||||
}
|
||||
|
39
src/cparse.c
39
src/cparse.c
@ -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},
|
||||
|
81
src/cvm.c
81
src/cvm.c
@ -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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
} 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))
|
||||
// get the field from the object
|
||||
if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val))
|
||||
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;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user