mirror of
https://github.com/CPunch/Cosmo.git
synced 2024-11-22 07:20: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_pushString(state, "sub");
|
||||||
cosmoV_pushCFunction(state, cosmoB_sSub);
|
cosmoV_pushCFunction(state, cosmoB_sSub);
|
||||||
|
|
||||||
cosmoV_makeTable(state, 1);
|
cosmoV_makeObject(state, 1);
|
||||||
// string.*
|
// 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
|
// 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);
|
state->protoObjects[COBJ_STRING] = cosmoV_readObject(*obj);
|
||||||
|
|
||||||
|
// register "string" to the global table
|
||||||
|
cosmoV_register(state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================================================================ [DEBUG] ================================================================
|
// ================================================================ [DEBUG] ================================================================
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include "cstate.h"
|
#include "cstate.h"
|
||||||
|
|
||||||
//#define GC_STRESS
|
#define GC_STRESS
|
||||||
//#define GC_DEBUG
|
//#define GC_DEBUG
|
||||||
// arrays will grow by a factor of 2
|
// arrays will grow by a factor of 2
|
||||||
#define GROW_FACTOR 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);
|
CObjError *cerror = (CObjError*)cosmoO_allocateBase(state, sizeof(CObjError), COBJ_ERROR);
|
||||||
cerror->err = err;
|
cerror->err = err;
|
||||||
cerror->frameCount = state->frameCount;
|
cerror->frameCount = state->frameCount;
|
||||||
|
cerror->parserError = false;
|
||||||
|
|
||||||
// allocate the callframe
|
// allocate the callframe
|
||||||
cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount);
|
cerror->frames = cosmoM_xmalloc(state, sizeof(CCallFrame) * cerror->frameCount);
|
||||||
@ -462,7 +463,7 @@ void printObject(CObj *o) {
|
|||||||
switch (o->type) {
|
switch (o->type) {
|
||||||
case COBJ_STRING: {
|
case COBJ_STRING: {
|
||||||
CObjString *objStr = (CObjString*)o;
|
CObjString *objStr = (CObjString*)o;
|
||||||
printf("\"%.*s\"", objStr->length, objStr->str);
|
printf("%.*s", objStr->length, objStr->str);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case COBJ_OBJECT: {
|
case COBJ_OBJECT: {
|
||||||
@ -474,17 +475,6 @@ void printObject(CObj *o) {
|
|||||||
printf("<tbl> %p", (void*)tbl);
|
printf("<tbl> %p", (void*)tbl);
|
||||||
break;
|
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: {
|
case COBJ_FUNCTION: {
|
||||||
CObjFunction *objFunc = (CObjFunction*)o;
|
CObjFunction *objFunc = (CObjFunction*)o;
|
||||||
if (objFunc->name != NULL)
|
if (objFunc->name != NULL)
|
||||||
@ -498,12 +488,30 @@ void printObject(CObj *o) {
|
|||||||
printf("<c function> %p", (void*)objCFunc->cfunc);
|
printf("<c function> %p", (void*)objCFunc->cfunc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case COBJ_ERROR: {
|
||||||
|
CObjError *err = (CObjError*)o;
|
||||||
|
printf("<error> %p -> ", (void*)o);
|
||||||
|
printValue(err->err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case COBJ_METHOD: {
|
case COBJ_METHOD: {
|
||||||
CObjMethod *method = (CObjMethod*)o;
|
CObjMethod *method = (CObjMethod*)o;
|
||||||
printf("<method> %p : ", (void*)method->obj);
|
printf("<method> %p -> ", (void*)method);
|
||||||
printValue(method->func);
|
printValue(method->func);
|
||||||
break;
|
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:
|
default:
|
||||||
printf("<unkn obj %p>", (void*)o);
|
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
|
// grab value/key
|
||||||
expression(pstate, 1, true);
|
expression(pstate, 1, true);
|
||||||
|
|
||||||
// they want to make a table with key:value
|
// they want to make a table with key = value
|
||||||
if (match(pstate, TOKEN_COLON) && tblType != 1) {
|
if (match(pstate, TOKEN_EQUAL) && tblType != 1) {
|
||||||
tblType = 2; // dictionary-like
|
tblType = 2; // dictionary-like
|
||||||
|
|
||||||
// grab value
|
// grab value
|
||||||
@ -718,14 +718,6 @@ static void dot(CParseState *pstate, bool canAssign, Precedence prec) {
|
|||||||
writeu8(pstate, OP_INCOBJECT);
|
writeu8(pstate, OP_INCOBJECT);
|
||||||
writeu8(pstate, 128 - 1);
|
writeu8(pstate, 128 - 1);
|
||||||
writeu16(pstate, name);
|
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 {
|
} else {
|
||||||
writeu8(pstate, OP_GETOBJECT);
|
writeu8(pstate, OP_GETOBJECT);
|
||||||
writeu16(pstate, name);
|
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
|
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]
|
// ++test.field[1]
|
||||||
// this function is kind of spaghetti, feel free to rewrite (if you dare!)
|
// 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) {
|
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_LEFT_BRACKET] = {table, _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_COLON] = {NULL, invoke, PREC_CALL},
|
||||||
[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_DOT_DOT_DOT] = {NULL, NULL, PREC_NONE},
|
[TOKEN_DOT_DOT_DOT] = {NULL, NULL, PREC_NONE},
|
||||||
|
95
src/cvm.c
95
src/cvm.c
@ -439,21 +439,16 @@ COSMO_API bool cosmoV_get(CState *state, CObj *_obj, CValue key, CValue *val) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// push the object onto the stack so the GC can find it
|
||||||
cosmoV_pushValue(state, cosmoV_newObj(object));
|
cosmoV_pushValue(state, cosmoV_newObj(object));
|
||||||
|
|
||||||
if (cosmoO_getRawObject(state, object, key, val)) {
|
if (cosmoO_getRawObject(state, object, key, val)) {
|
||||||
// is it a function? if so, make it a method to the current object
|
// *val now equals the response, pop the 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
|
|
||||||
|
|
||||||
cosmoV_pop(state);
|
cosmoV_pop(state);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cosmoV_pop(state);
|
cosmoV_pop(state);
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
COSMO_API bool cosmoV_set(CState *state, CObj *_obj, CValue key, CValue val) {
|
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;
|
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) {
|
int _tbl__next(CState *state, int nargs, CValue *args) {
|
||||||
if (nargs != 1) {
|
if (nargs != 1) {
|
||||||
cosmoV_error(state, "Expected 1 parameter, %d received!", nargs);
|
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
|
cosmoV_pushValue(state, val); // pushes the field result
|
||||||
} else {
|
} else {
|
||||||
cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp));
|
cosmoV_error(state, "Couldn't index type %s!", cosmoV_typeStr(*temp));
|
||||||
@ -700,7 +708,7 @@ int cosmoV_execute(CState *state) {
|
|||||||
case OP_NEWINDEX: {
|
case OP_NEWINDEX: {
|
||||||
StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack
|
StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack
|
||||||
StkPtr key = cosmoV_getTop(state, 1);
|
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
|
// sanity check
|
||||||
if (IS_OBJ(*temp)) {
|
if (IS_OBJ(*temp)) {
|
||||||
@ -748,19 +756,11 @@ int cosmoV_execute(CState *state) {
|
|||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (IS_OBJ(*temp)) {
|
if (IS_OBJ(*temp)) {
|
||||||
switch (cosmoV_readObj(*temp)->type) {
|
if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val))
|
||||||
case COBJ_TABLE: { // syntax sugar, makes "namespaces" possible
|
return -1;
|
||||||
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 {
|
} 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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,20 +775,11 @@ int cosmoV_execute(CState *state) {
|
|||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (IS_OBJ(*temp)) {
|
if (IS_OBJ(*temp)) {
|
||||||
switch (cosmoV_readObj(*temp)->type) {
|
if (!cosmoV_set(state, cosmoV_readObj(*temp), constants[ident], *value))
|
||||||
case COBJ_TABLE: { // index and set the table
|
return -1;
|
||||||
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 {
|
} 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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -796,7 +787,7 @@ int cosmoV_execute(CState *state) {
|
|||||||
cosmoV_setTop(state, 2);
|
cosmoV_setTop(state, 2);
|
||||||
break;
|
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 args = READBYTE();
|
||||||
uint8_t nres = READBYTE();
|
uint8_t nres = READBYTE();
|
||||||
uint16_t ident = READUINT();
|
uint16_t ident = READUINT();
|
||||||
@ -805,32 +796,12 @@ int cosmoV_execute(CState *state) {
|
|||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (IS_OBJ(*temp)) {
|
if (IS_OBJ(*temp)) {
|
||||||
switch (cosmoV_readObj(*temp)->type) {
|
// get the field from the object
|
||||||
case COBJ_TABLE: { // again, syntax sugar ("""""namespaces""""")
|
if (!cosmoV_get(state, cosmoV_readObj(*temp), constants[ident], &val))
|
||||||
CObjTable *tbl = (CObjTable*)cosmoV_readObj(*temp);
|
return -1;
|
||||||
|
|
||||||
cosmoT_get(&tbl->tbl, constants[ident], &val);
|
// now invoke the method!
|
||||||
|
invokeMethod(state, cosmoV_readObj(*temp), val, args, nres, 1);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp));
|
cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp));
|
||||||
return -1;
|
return -1;
|
||||||
@ -895,7 +866,7 @@ int cosmoV_execute(CState *state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get __next method and place it at the top of the stack
|
// 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 {
|
} else {
|
||||||
cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
|
cosmoV_error(state, "Expected iterable object! '__iter' not defined!");
|
||||||
return -1;
|
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_get(CState *state, CObj *obj, CValue key, CValue *val);
|
||||||
COSMO_API bool cosmoV_set(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
|
// nice to have wrappers
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user