diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index faf4ca750..8e3b0b560 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -335,6 +335,8 @@ bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator void mScriptFrameInit(struct mScriptFrame* frame); void mScriptFrameDeinit(struct mScriptFrame* frame); +struct mScriptValue* mScriptLambdaCreate0(struct mScriptValue* fn, struct mScriptList* args); + void mScriptClassInit(struct mScriptTypeClass* cls); void mScriptClassDeinit(struct mScriptTypeClass* cls); diff --git a/src/script/test/types.c b/src/script/test/types.c index 25ec29809..5c66b3f76 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -82,6 +82,10 @@ static bool isNullStruct(struct Test* arg) { return !arg; } +static void increment(struct Test* t) { + ++t->a; +} + mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32, ignored); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, in); @@ -95,6 +99,7 @@ mSCRIPT_BIND_FUNCTION(boundIsSequential, S32, isSequential, 1, LIST, list); mSCRIPT_BIND_FUNCTION(boundIsNullCharp, BOOL, isNullCharp, 1, CHARP, arg); mSCRIPT_BIND_FUNCTION(boundIsNullStruct, BOOL, isNullStruct, 1, S(Test), arg); mSCRIPT_BIND_FUNCTION_WITH_DEFAULTS(boundAddIntWithDefaults, S32, addInts, 2, S32, a, S32, b); +mSCRIPT_BIND_VOID_FUNCTION(boundIncrement, increment, 1, S(Test), this); mSCRIPT_DEFINE_FUNCTION_BINDING_DEFAULTS(boundAddIntWithDefaults) mSCRIPT_NO_DEFAULT, @@ -1336,6 +1341,28 @@ M_TEST_DEFINE(nullStruct) { mScriptFrameDeinit(&frame); } +M_TEST_DEFINE(lambda0) { + struct mScriptList args; + struct Test t = { + .a = 0 + }; + + mScriptListInit(&args, 1); + mSCRIPT_PUSH(&args, S(Test), &t); + struct mScriptValue* fn = mScriptLambdaCreate0(&boundIncrement, &args); + assert_non_null(fn); + mScriptListDeinit(&args); + + struct mScriptFrame frame; + mScriptFrameInit(&frame); + assert_int_equal(t.a, 0); + assert_true(mScriptInvoke(fn, &frame)); + assert_int_equal(t.a, 1); + mScriptFrameDeinit(&frame); + + mScriptValueDeref(fn); +} + M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(voidArgs), cmocka_unit_test(voidFunc), @@ -1373,4 +1400,5 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(invokeList), cmocka_unit_test(nullString), cmocka_unit_test(nullStruct), + cmocka_unit_test(lambda0), ) diff --git a/src/script/types.c b/src/script/types.c index 34529a0df..e80c018fb 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -12,6 +12,11 @@ #include #include +struct mScriptLambda { + struct mScriptValue* fn; + struct mScriptList arguments; +}; + static void _allocList(struct mScriptValue*); static void _freeList(struct mScriptValue*); @@ -47,6 +52,10 @@ static bool _boolEqual(const struct mScriptValue*, const struct mScriptValue*); static bool _charpEqual(const struct mScriptValue*, const struct mScriptValue*); static bool _stringEqual(const struct mScriptValue*, const struct mScriptValue*); +static void _lambdaAlloc(struct mScriptValue* val); +static void _lambdaFree(struct mScriptValue* val); +static bool _callLambda0(struct mScriptFrame* frame, void* context); + const struct mScriptType mSTVoid = { .base = mSCRIPT_TYPE_VOID, .size = 0, @@ -268,6 +277,25 @@ const struct mScriptType mSTWeakref = { .hash = NULL, }; +const struct mScriptType mSTLambda0 = { + .base = mSCRIPT_TYPE_FUNCTION, + .size = sizeof(struct mScriptLambda), + .name = "lambda", + .details = { + .function = { + .parameters = { + .count = 0, + }, + .returnType = { + .count = 0, + }, + }, + }, + .alloc = _lambdaAlloc, + .free = _lambdaFree, + .hash = NULL, +}; + struct mScriptValue mScriptValueNull = { .type = &mSTVoid, .refs = mSCRIPT_VALUE_UNREF @@ -826,6 +854,32 @@ bool _stringEqual(const struct mScriptValue* a, const struct mScriptValue* b) { return strncmp(valA, valB, lenA) == 0; } +void _lambdaAlloc(struct mScriptValue* value) { + struct mScriptLambda* lambda = calloc(1, sizeof(*lambda)); + struct mScriptFunction* fn = calloc(1, sizeof(*fn)); + fn->context = lambda; + mScriptListInit(&lambda->arguments, 0); + value->value.opaque = fn; +} + +void _lambdaFree(struct mScriptValue* value) { + struct mScriptFunction* fn = value->value.opaque; + struct mScriptLambda* lambda = fn->context; + size_t i; + for (i = 0; i < mScriptListSize(&lambda->arguments); ++i) { + struct mScriptValue* val = mScriptListGetPointer(&lambda->arguments, i); + if (val->type->base != mSCRIPT_TYPE_WRAPPER) { + continue; + } + val = mScriptValueUnwrap(val); + mScriptValueDeref(val); + } + mScriptListDeinit(&lambda->arguments); + mScriptValueDeref(lambda->fn); + free(lambda); + free(fn); +} + struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) { // TODO: Use an arena instead of just the generic heap struct mScriptValue* val = malloc(sizeof(*val)); @@ -1074,6 +1128,44 @@ void mScriptFrameDeinit(struct mScriptFrame* frame) { mScriptListDeinit(&frame->arguments); } +struct mScriptValue* mScriptLambdaCreate0(struct mScriptValue* fn, struct mScriptList* args) { + struct mScriptValue* value = mScriptValueAlloc(&mSTLambda0); + struct mScriptFunction* lfn = value->value.opaque; + struct mScriptLambda* lambda = lfn->context; + lfn->call = _callLambda0; + lambda->fn = fn; + mScriptValueRef(fn); + if (args) { + mScriptListCopy(&lambda->arguments, args); + size_t i; + for (i = 0; i < mScriptListSize(args); ++i) { + struct mScriptValue* val = mScriptListGetPointer(args, i); + if (val->type->base != mSCRIPT_TYPE_WRAPPER) { + continue; + } + val = mScriptValueUnwrap(val); + mScriptValueRef(val); + } + } + return value; +} + +bool _callLambda0(struct mScriptFrame* frame, void* context) { + if (mScriptListSize(&frame->arguments)) { + return false; + } + struct mScriptLambda* lambda = context; + struct mScriptFrame subframe; + mScriptFrameInit(&subframe); + mScriptListCopy(&subframe.arguments, &lambda->arguments); + bool ok = mScriptInvoke(lambda->fn, &subframe); + if (mScriptListSize(&subframe.returnValues)) { + ok = false; + } + mScriptFrameDeinit(&subframe); + return ok; +} + static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScriptClassInitDetails* details, bool child) { const char* docstring = NULL;