Scripting: Add lambdas with 0 arguments and 0 return values

This commit is contained in:
Vicki Pfau 2023-04-24 03:27:43 -07:00
parent 44ab21ab35
commit b8261a0c66
3 changed files with 122 additions and 0 deletions

View File

@ -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);

View File

@ -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),
)

View File

@ -12,6 +12,11 @@
#include <mgba-util/string.h>
#include <mgba-util/table.h>
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;