Scripting: Initial runtime bringup work

This commit is contained in:
Vicki Pfau 2021-07-13 20:04:17 -07:00
parent 7b4850024a
commit 1a33a71771
6 changed files with 797 additions and 1 deletions

View File

@ -733,6 +733,7 @@ if (USE_DISCORD_RPC)
endif()
if(ENABLE_SCRIPTING)
add_subdirectory(src/script)
list(APPEND ENABLES SCRIPTING)
if(BUILD_PYTHON)
@ -774,6 +775,11 @@ if(USE_DEBUGGERS)
list(APPEND FEATURES DEBUGGERS)
endif()
if(ENABLE_SCRIPTING)
list(APPEND FEATURE_SRC ${SCRIPT_SRC})
list(APPEND TEST_SRC ${SCRIPT_TEST_SRC})
endif()
foreach(FEATURE IN LISTS FEATURES)
list(APPEND FEATURE_DEFINES "USE_${FEATURE}")
endforeach()

250
include/mgba/script/types.h Normal file
View File

@ -0,0 +1,250 @@
/* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef M_SCRIPT_TYPES_H
#define M_SCRIPT_TYPES_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba-util/vector.h>
#define _mAPPLY(...) __VA_ARGS__
#define mSCRIPT_VALUE_UNREF -1
#define mSCRIPT_PARAMS_MAX 8
#define mSCRIPT_TYPE_C_S32 int32_t
#define mSCRIPT_TYPE_C_U32 uint32_t
#define mSCRIPT_TYPE_C_F32 float
#define mSCRIPT_TYPE_C_PTR void*
#define mSCRIPT_TYPE_C_TABLE Table*
#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT*
#define mSCRIPT_TYPE_FIELD_S32 s32
#define mSCRIPT_TYPE_FIELD_U32 u32
#define mSCRIPT_TYPE_FIELD_F32 f32
#define mSCRIPT_TYPE_FIELD_PTR opaque
#define mSCRIPT_TYPE_FIELD_TABLE opaque
#define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque
#define mSCRIPT_TYPE_MS_S32 (&mSTSInt32)
#define mSCRIPT_TYPE_MS_U32 (&mSTUInt32)
#define mSCRIPT_TYPE_MS_F32 (&mSTFloat32)
#define mSCRIPT_TYPE_MS_TABLE (&mSTTable)
#define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT)
#define _mSCRIPT_FIELD_NAME(V) (V)->name
#define mSCRIPT_TYPE_CMP_GENERIC(TYPE0, TYPE1) (TYPE0 == TYPE1)
#define mSCRIPT_TYPE_CMP_U32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U32, TYPE)
#define mSCRIPT_TYPE_CMP_S32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S32, TYPE)
#define mSCRIPT_TYPE_CMP_F32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F32, TYPE)
#define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE)
#define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME
#define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) _mAPPLY(mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1))
#define mSCRIPT_POP(STACK, TYPE, NAME) \
_mAPPLY(mSCRIPT_TYPE_C_ ## TYPE) NAME; \
do { \
struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \
if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \
return false; \
} \
NAME = _val->value. _mAPPLY(mSCRIPT_TYPE_FIELD_ ## TYPE); \
mScriptListResize(STACK, -1); \
} while (0)
#define mSCRIPT_POP_0(...)
#define mSCRIPT_POP_1(FRAME, T0) mSCRIPT_POP(FRAME, T0, p0)
#define mSCRIPT_POP_2(FRAME, T0, T1) mSCRIPT_POP(FRAME, T1, p1); mSCRIPT_POP_1(FRAME, T0)
#define mSCRIPT_POP_3(FRAME, T0, T1, T2) mSCRIPT_POP(FRAME, T2, p2); mSCRIPT_POP_2(FRAME, T0, T1)
#define mSCRIPT_POP_4(FRAME, T0, T1, T2, T3) mSCRIPT_POP(FRAME, T3, p3); mSCRIPT_POP_3(FRAME, T0, T1, T2)
#define mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4) mSCRIPT_POP(FRAME, T4, p4); mSCRIPT_POP_4(FRAME, T0, T1, T2, T3)
#define mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) mSCRIPT_POP(FRAME, T5, p5); mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4)
#define mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) mSCRIPT_POP(FRAME, T6, p6); mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5)
#define mSCRIPT_POP_8(FRAME, T0, T1, T2, T3, T4, T5, T6, T7) mSCRIPT_POP(FRAME, T7, p7); mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6)
#define mSCRIPT_PUSH(STACK, TYPE, NAME) \
do { \
struct mScriptValue* _val = mScriptListAppend(STACK); \
_val->type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE); \
_val->refs = mSCRIPT_VALUE_UNREF; \
_val->value._mAPPLY(mSCRIPT_TYPE_FIELD_ ## TYPE) = NAME; \
} while (0)
#define mSCRIPT_ARG_NAMES_0
#define mSCRIPT_ARG_NAMES_1 p0
#define mSCRIPT_ARG_NAMES_2 p0, p1
#define mSCRIPT_ARG_NAMES_3 p0, p1, p2
#define mSCRIPT_ARG_NAMES_4 p0, p1, p2, p3
#define mSCRIPT_ARG_NAMES_5 p0, p1, p2, p3, p4
#define mSCRIPT_ARG_NAMES_6 p0, p1, p2, p3, p4, p5
#define mSCRIPT_ARG_NAMES_7 p0, p1, p2, p3, p4, p5, p6
#define mSCRIPT_ARG_NAMES_8 p0, p1, p2, p3, p4, p5, p6, p7
#define mSCRIPT_PREFIX_0(PREFIX, ...)
#define mSCRIPT_PREFIX_1(PREFIX, T0) PREFIX ## T0
#define mSCRIPT_PREFIX_2(PREFIX, T0, T1) PREFIX ## T0, PREFIX ## T1
#define mSCRIPT_PREFIX_3(PREFIX, T0, T1, T2) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2
#define mSCRIPT_PREFIX_4(PREFIX, T0, T1, T2, T3) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3
#define mSCRIPT_PREFIX_5(PREFIX, T0, T1, T2, T3, T4) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4
#define mSCRIPT_PREFIX_6(PREFIX, T0, T1, T2, T3, T4, T5) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5
#define mSCRIPT_PREFIX_7(PREFIX, T0, T1, T2, T3, T4, T5, T6) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6
#define mSCRIPT_PREFIX_8(PREFIX, T0, T1, T2, T3, T4, T5, T6, T7) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6, PREFIX ## T7
#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(mSCRIPT_ARG_NAMES_ ## NPARAMS)
#define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \
_mAPPLY(mSCRIPT_TYPE_C_ ## RETURN) out = FUNCTION(mSCRIPT_ARG_NAMES_ ## NPARAMS); \
mSCRIPT_PUSH(&frame->returnValues, RETURN, out)
#define mSCRIPT_EXPORT_STRUCT(STRUCT) \
const struct mScriptType mSTStruct_ ## STRUCT = { \
.base = mSCRIPT_TYPE_OBJECT, \
.size = sizeof(struct STRUCT), \
.name = "struct::" #STRUCT, \
.alloc = NULL, \
.free = NULL, \
}
#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \
static bool _binding_ ## NAME(struct mScriptFrame* frame) { \
mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \
if (mScriptListSize(&frame->arguments)) { \
return false; \
} \
_mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \
return true; \
} \
const struct mScriptFunction NAME = { \
.signature = { \
.parameters = { \
.count = NPARAMS, \
.entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \
}, \
.returnType = { \
.count = 1, \
.entries = { mSCRIPT_TYPE_MS_ ## RETURN } \
}, \
}, \
.call = _binding_ ## NAME \
};
#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \
static bool _binding_ ## NAME(struct mScriptFrame* frame) { \
mSCRIPT_POP_ ## NPARAMS(&frame->arguments, __VA_ARGS__); \
if (mScriptListSize(&frame->arguments)) { \
return false; \
} \
_mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \
return true; \
} \
const struct mScriptFunction NAME = { \
.signature = { \
.parameters = { \
.count = NPARAMS, \
.entries = { _mAPPLY(mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_MS_, __VA_ARGS__)) } \
}, \
.returnType = { \
.count = 0, \
}, \
}, \
.call = _binding_ ## NAME \
};
enum {
mSCRIPT_TYPE_VOID = 0,
mSCRIPT_TYPE_SINT,
mSCRIPT_TYPE_UINT,
mSCRIPT_TYPE_FLOAT,
mSCRIPT_TYPE_STRING,
mSCRIPT_TYPE_FUNCTION,
mSCRIPT_TYPE_OPAQUE,
mSCRIPT_TYPE_OBJECT,
mSCRIPT_TYPE_TUPLE,
mSCRIPT_TYPE_LIST,
mSCRIPT_TYPE_TABLE,
};
struct Table;
struct mScriptType;
extern const struct mScriptType mSTVoid;
extern const struct mScriptType mSTSInt32;
extern const struct mScriptType mSTUInt32;
extern const struct mScriptType mSTFloat32;
extern const struct mScriptType mSTTable;
struct mScriptTypeTuple {
size_t count;
const struct mScriptType* entries[mSCRIPT_PARAMS_MAX];
};
struct mScriptTypeFunction {
struct mScriptTypeTuple parameters;
struct mScriptTypeTuple returnType;
// TODO: varargs, kwargs, defaults
};
struct mScriptValue;
struct mScriptType {
int base;
size_t size;
const char* name;
void (*alloc)(const struct mScriptType*, struct mScriptValue*);
void (*free)(const struct mScriptType*, struct mScriptValue*);
uint32_t (*hash)(const struct mScriptType*, struct mScriptValue*);
union {
struct mScriptTypeTuple tuple;
struct mScriptTypeFunction function;
void* opaque;
} details;
};
struct mScriptValue {
const struct mScriptType* type;
int refs;
union {
int32_t s32;
uint32_t u32;
float f32;
void* opaque;
} value;
};
DECLARE_VECTOR(mScriptList, struct mScriptValue)
struct mScriptFrame {
struct mScriptList arguments;
struct mScriptList returnValues;
// TODO: Exception/failure codes
};
struct mScriptFunction {
struct mScriptTypeFunction signature;
bool (*call)(struct mScriptFrame*);
};
struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type);
void mScriptValueRef(struct mScriptValue* val);
void mScriptValueDeref(struct mScriptValue* val);
bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value);
bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key);
struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key);
void mScriptFrameInit(struct mScriptFrame* frame);
void mScriptFrameDeinit(struct mScriptFrame* frame);
bool mScriptPopS32(struct mScriptList* list, int32_t* out);
bool mScriptPopU32(struct mScriptList* list, uint32_t* out);
bool mScriptPopF32(struct mScriptList* list, float* out);
bool mScriptPopPointer(struct mScriptList* list, void** out);
bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output);
bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame);
bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame);
#endif

View File

@ -28,4 +28,4 @@ source_group("mCore" FILES ${SOURCE_FILES})
source_group("mCore tests" FILES ${TEST_FILES})
export_directory(CORE SOURCE_FILES)
export_directory(CORE_TEST TEST_FILES)
export_directory(CORE_TEST TEST_FILES)

12
src/script/CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
include(ExportDirectory)
set(SOURCE_FILES
types.c)
set(TEST_FILES
test/types.c)
source_group("Scripting" FILES ${SOURCE_FILES})
source_group("Scripting tests" FILES ${TEST_FILES})
export_directory(SCRIPT SOURCE_FILES)
export_directory(SCRIPT_TEST TEST_FILES)

229
src/script/test/types.c Normal file
View File

@ -0,0 +1,229 @@
/* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/test/suite.h"
#include <mgba/script/types.h>
struct Test {
int32_t a;
};
mSCRIPT_EXPORT_STRUCT(Test);
static int voidOne(void) {
return 1;
}
static void discard(int ignored) {
UNUSED(ignored);
}
static int identityInt(int in) {
return in;
}
static float identityFloat(float in) {
return in;
}
static struct Test* identityStruct(struct Test* t) {
return t;
}
static int addInts(int a, int b) {
return a + b;
}
static int subInts(int a, int b) {
return a - b;
}
mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0);
mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32);
mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32);
mSCRIPT_BIND_FUNCTION(boundIdentityFloat, F32, identityFloat, 1, F32);
mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test));
mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32);
mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, S32);
M_TEST_DEFINE(voidArgs) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
assert_true(mScriptInvoke(&boundVoidOne, &frame));
int32_t val;
assert_true(mScriptPopS32(&frame.returnValues, &val));
assert_int_equal(val, 1);
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(voidFunc) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, S32, 1);
assert_true(mScriptInvoke(&boundDiscard, &frame));
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(identityFunctionS32) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, S32, 1);
assert_true(mScriptInvoke(&boundIdentityInt, &frame));
int32_t val;
assert_true(mScriptPopS32(&frame.returnValues, &val));
assert_int_equal(val, 1);
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(identityFunctionF32) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, F32, 3.125f);
assert_true(mScriptInvoke(&boundIdentityFloat, &frame));
float val;
assert_true(mScriptPopF32(&frame.returnValues, &val));
assert_float_equal(val, 3.125f, 0.f);
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(identityFunctionStruct) {
struct mScriptFrame frame;
struct Test v = {};
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, S(Test), &v);
assert_true(mScriptInvoke(&boundIdentityStruct, &frame));
struct Test* val;
assert_true(mScriptPopPointer(&frame.returnValues, (void**) &val));
assert_ptr_equal(val, &v);
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(addS32) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, S32, 1);
mSCRIPT_PUSH(&frame.arguments, S32, 2);
assert_true(mScriptInvoke(&boundAddInts, &frame));
int32_t val;
assert_true(mScriptPopS32(&frame.returnValues, &val));
assert_int_equal(val, 3);
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(subS32) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, S32, 2);
mSCRIPT_PUSH(&frame.arguments, S32, 1);
assert_true(mScriptInvoke(&boundSubInts, &frame));
int32_t val;
assert_true(mScriptPopS32(&frame.returnValues, &val));
assert_int_equal(val, 1);
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(wrongArgCountLo) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
assert_false(mScriptInvoke(&boundIdentityInt, &frame));
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(wrongArgCountHi) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, S32, 1);
mSCRIPT_PUSH(&frame.arguments, S32, 1);
assert_false(mScriptInvoke(&boundIdentityInt, &frame));
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(wrongArgType) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, S32, 1);
assert_false(mScriptInvoke(&boundIdentityStruct, &frame));
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(coerceToFloat) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, S32, 1);
assert_true(mScriptInvoke(&boundIdentityFloat, &frame));
float val;
assert_true(mScriptPopF32(&frame.returnValues, &val));
assert_float_equal(val, 1.f, 0.f);
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(coerceFromFloat) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
mSCRIPT_PUSH(&frame.arguments, F32, 1.25f);
assert_true(mScriptInvoke(&boundIdentityInt, &frame));
int val;
assert_true(mScriptPopS32(&frame.returnValues, &val));
assert_int_equal(val, 1);
mScriptFrameDeinit(&frame);
}
M_TEST_DEFINE(hashTableBasic) {
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
assert_non_null(table);
struct mScriptValue* intValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32);
assert_ptr_equal(intValue->type, mSCRIPT_TYPE_MS_S32);
assert_int_equal(intValue->value.s32, 0);
assert_int_equal(intValue->refs, 1);
struct mScriptValue intKey = {
.type = mSCRIPT_TYPE_MS_S32,
.value = {
.s32 = 1234
},
.refs = mSCRIPT_VALUE_UNREF
};
struct mScriptValue badKey = {
.type = mSCRIPT_TYPE_MS_S32,
.value = {
.s32 = 1235
},
.refs = mSCRIPT_VALUE_UNREF
};
assert_true(mScriptTableInsert(table, &intKey, intValue));
assert_int_equal(intValue->refs, 2);
struct mScriptValue* lookupValue = mScriptTableLookup(table, &intKey);
assert_non_null(lookupValue);
assert_ptr_equal(lookupValue, intValue);
lookupValue = mScriptTableLookup(table, &badKey);
assert_null(lookupValue);
assert_true(mScriptTableRemove(table, &intKey));
assert_int_equal(intValue->refs, 1);
mScriptValueDeref(intValue);
mScriptValueDeref(table);
}
M_TEST_SUITE_DEFINE(mScript,
cmocka_unit_test(voidArgs),
cmocka_unit_test(voidFunc),
cmocka_unit_test(identityFunctionS32),
cmocka_unit_test(identityFunctionF32),
cmocka_unit_test(identityFunctionStruct),
cmocka_unit_test(addS32),
cmocka_unit_test(subS32),
cmocka_unit_test(wrongArgCountLo),
cmocka_unit_test(wrongArgCountHi),
cmocka_unit_test(wrongArgType),
cmocka_unit_test(coerceToFloat),
cmocka_unit_test(coerceFromFloat),
cmocka_unit_test(hashTableBasic))

299
src/script/types.c Normal file
View File

@ -0,0 +1,299 @@
/* Copyright (c) 2013-2021 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/script/types.h>
#include <mgba-util/table.h>
static void _allocTable(const struct mScriptType*, struct mScriptValue*);
static void _freeTable(const struct mScriptType*, struct mScriptValue*);
static void _deinitTableValue(void*);
static uint32_t _hashScalar(const struct mScriptType*, struct mScriptValue*);
const struct mScriptType mSTVoid = {
.base = mSCRIPT_TYPE_VOID,
.size = 0,
.name = "void",
.alloc = NULL,
.free = NULL,
.hash = NULL,
};
const struct mScriptType mSTSInt32 = {
.base = mSCRIPT_TYPE_SINT,
.size = 4,
.name = "s32",
.alloc = NULL,
.free = NULL,
.hash = _hashScalar,
};
const struct mScriptType mSTUInt32 = {
.base = mSCRIPT_TYPE_UINT,
.size = 4,
.name = "u32",
.alloc = NULL,
.free = NULL,
.hash = _hashScalar,
};
const struct mScriptType mSTFloat32 = {
.base = mSCRIPT_TYPE_FLOAT,
.size = 4,
.name = "f32",
.alloc = NULL,
.free = NULL,
.hash = NULL,
};
const struct mScriptType mSTTable = {
.base = mSCRIPT_TYPE_TABLE,
.size = sizeof(struct Table),
.name = "table",
.alloc = _allocTable,
.free = _freeTable,
.hash = NULL,
};
DEFINE_VECTOR(mScriptList, struct mScriptValue)
void _allocTable(const struct mScriptType* type, struct mScriptValue* val) {
UNUSED(type);
val->value.opaque = malloc(sizeof(struct Table));
TableInit(val->value.opaque, 0, _deinitTableValue);
}
void _freeTable(const struct mScriptType* type, struct mScriptValue* val) {
UNUSED(type);
TableDeinit(val->value.opaque);
free(val->value.opaque);
}
void _deinitTableValue(void* val) {
mScriptValueDeref(val);
}
uint32_t _hashScalar(const struct mScriptType* type, struct mScriptValue* val) {
// From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
uint32_t x;
switch (type->base) {
case mSCRIPT_TYPE_SINT:
x = val->value.s32;
break;
case mSCRIPT_TYPE_UINT:
x = val->value.u32;
break;
}
x = ((x >> 16) ^ x) * 0x45D9F3B;
x = ((x >> 16) ^ x) * 0x45D9F3B;
x = (x >> 16) ^ x;
return x;
}
struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) {
// TODO: Use an arena instead of just the generic heap
struct mScriptValue* val = malloc(sizeof(*val));
val->refs = 1;
if (type->alloc) {
type->alloc(type, val);
} else {
val->value.opaque = NULL;
}
val->type = type;
return val;
}
void mScriptValueRef(struct mScriptValue* val) {
if (val->refs == INT_MAX) {
abort();
} else if (val->refs == mSCRIPT_VALUE_UNREF) {
return;
}
++val->refs;
}
void mScriptValueDeref(struct mScriptValue* val) {
--val->refs;
if (val->refs > 0) {
return;
} else if (val->refs < 0) {
val->refs = mSCRIPT_VALUE_UNREF;
}
if (val->type->free) {
val->type->free(val->type, val);
}
free(val);
}
bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value) {
if (table->type != mSCRIPT_TYPE_MS_TABLE) {
return false;
}
if (!key->type->hash) {
return false;
}
struct Table* t = table->value.opaque;
uint32_t hash = key->type->hash(key->type, key);
mScriptValueRef(value);
TableInsert(t, hash, value);
return true;
}
bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key) {
if (table->type != mSCRIPT_TYPE_MS_TABLE) {
return false;
}
if (!key->type->hash) {
return false;
}
struct Table* t = table->value.opaque;
uint32_t hash = key->type->hash(key->type, key);
TableRemove(t, hash);
return true;
}
struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key) {
if (table->type != mSCRIPT_TYPE_MS_TABLE) {
return false;
}
if (!key->type->hash) {
return false;
}
struct Table* t = table->value.opaque;
uint32_t hash = key->type->hash(key->type, key);
return TableLookup(t, hash);
}
void mScriptFrameInit(struct mScriptFrame* frame) {
mScriptListInit(&frame->arguments, 4);
mScriptListInit(&frame->returnValues, 1);
}
void mScriptFrameDeinit(struct mScriptFrame* frame) {
mScriptListDeinit(&frame->returnValues);
mScriptListDeinit(&frame->arguments);
}
bool mScriptPopS32(struct mScriptList* list, int32_t* out) {
mSCRIPT_POP(list, S32, val);
*out = val;
return true;
}
bool mScriptPopU32(struct mScriptList* list, uint32_t* out) {
mSCRIPT_POP(list, U32, val);
*out = val;
return true;
}
bool mScriptPopF32(struct mScriptList* list, float* out) {
mSCRIPT_POP(list, F32, val);
*out = val;
return true;
}
bool mScriptPopPointer(struct mScriptList* list, void** out) {
mSCRIPT_POP(list, PTR, val);
*out = val;
return true;
}
bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output) {
switch (type->base) {
case mSCRIPT_TYPE_VOID:
return false;
case mSCRIPT_TYPE_SINT:
switch (input->type->base) {
case mSCRIPT_TYPE_SINT:
output->value.s32 = input->value.s32;
break;
case mSCRIPT_TYPE_UINT:
output->value.s32 = input->value.u32;
break;
case mSCRIPT_TYPE_FLOAT:
switch (input->type->size) {
case 4:
output->value.s32 = input->value.f32;
break;
default:
return false;
}
break;
default:
return false;
}
break;
case mSCRIPT_TYPE_UINT:
switch (input->type->base) {
case mSCRIPT_TYPE_SINT:
output->value.u32 = input->value.s32;
break;
case mSCRIPT_TYPE_UINT:
output->value.u32 = input->value.u32;
break;
case mSCRIPT_TYPE_FLOAT:
switch (input->type->size) {
case 4:
output->value.u32 = input->value.f32;
break;
default:
return false;
}
break;
default:
return false;
}
break;
case mSCRIPT_TYPE_FLOAT:
switch (input->type->base) {
case mSCRIPT_TYPE_SINT:
output->value.f32 = input->value.s32;
break;
case mSCRIPT_TYPE_UINT:
output->value.f32 = input->value.u32;
break;
case mSCRIPT_TYPE_FLOAT:
switch (input->type->size) {
case 4:
output->value.f32 = input->value.f32;
break;
default:
return false;
}
break;
default:
return false;
}
break;
default:
return false;
}
output->type = type;
return true;
}
bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame) {
if (types->count != mScriptListSize(frame)) {
return false;
}
size_t i;
for (i = 0; i < types->count; ++i) {
if (types->entries[i] == mScriptListGetPointer(frame, i)->type) {
continue;
}
if (!mScriptCast(types->entries[i], mScriptListGetPointer(frame, i), mScriptListGetPointer(frame, i))) {
return false;
}
}
return true;
}
bool mScriptInvoke(const struct mScriptFunction* fn, struct mScriptFrame* frame) {
if (!mScriptCoerceFrame(&fn->signature.parameters, &frame->arguments)) {
return false;
}
return fn->call(frame);
}