From 9955d0d19b9c0d3fa3e58f7d7a35d8e45ea281e3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 30 Aug 2021 19:48:40 -0700 Subject: [PATCH] Scripting: Start string bringup --- include/mgba/script/types.h | 20 ++++++ src/script/test/types.c | 89 ++++++++++++++++++++++++++- src/script/types.c | 117 +++++++++++++++++++++++++++++++++++- 3 files changed, 224 insertions(+), 2 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 84c4e4ea1..37679bee7 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -20,6 +20,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_S32 int32_t #define mSCRIPT_TYPE_C_U32 uint32_t #define mSCRIPT_TYPE_C_F32 float +#define mSCRIPT_TYPE_C_STR mScriptString* +#define mSCRIPT_TYPE_C_CHARP const char* #define mSCRIPT_TYPE_C_PTR void* #define mSCRIPT_TYPE_C_TABLE Table* #define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* @@ -27,6 +29,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_S32 s32 #define mSCRIPT_TYPE_FIELD_U32 u32 #define mSCRIPT_TYPE_FIELD_F32 f32 +#define mSCRIPT_TYPE_FIELD_STR opaque +#define mSCRIPT_TYPE_FIELD_CHARP opaque #define mSCRIPT_TYPE_FIELD_PTR opaque #define mSCRIPT_TYPE_FIELD_TABLE opaque #define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque @@ -34,6 +38,8 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_S32 (&mSTSInt32) #define mSCRIPT_TYPE_MS_U32 (&mSTUInt32) #define mSCRIPT_TYPE_MS_F32 (&mSTFloat32) +#define mSCRIPT_TYPE_MS_STR (&mSTString) +#define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr) #define mSCRIPT_TYPE_MS_TABLE (&mSTTable) #define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) @@ -43,6 +49,8 @@ CXX_GUARD_START #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_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) +#define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, 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)) @@ -166,6 +174,7 @@ CXX_GUARD_START #define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_S32, s32, VALUE) #define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_U32, u32, VALUE) #define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F32, f32, VALUE) +#define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CHARP, opaque, VALUE) enum { mSCRIPT_TYPE_VOID = 0, @@ -187,6 +196,8 @@ extern const struct mScriptType mSTVoid; extern const struct mScriptType mSTSInt32; extern const struct mScriptType mSTUInt32; extern const struct mScriptType mSTFloat32; +extern const struct mScriptType mSTString; +extern const struct mScriptType mSTCharPtr; extern const struct mScriptType mSTTable; struct mScriptTypeTuple { @@ -229,6 +240,12 @@ struct mScriptValue { DECLARE_VECTOR(mScriptList, struct mScriptValue) +struct mScriptString { + size_t length; // Size of the string in code points + size_t size; // Size of the buffer in bytes, excluding NULL byte terminator + char* buffer; // UTF-8 representation of the string +}; + struct mScriptFrame { struct mScriptList arguments; struct mScriptList returnValues; @@ -238,12 +255,15 @@ struct mScriptFrame { struct mScriptFunction { struct mScriptTypeFunction signature; bool (*call)(struct mScriptFrame*); + void* context; }; struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type); void mScriptValueRef(struct mScriptValue* val); void mScriptValueDeref(struct mScriptValue* val); +struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); + 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); diff --git a/src/script/test/types.c b/src/script/test/types.c index 03fc2c160..2d3309482 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -41,6 +41,10 @@ static int subInts(int a, int b) { return a - b; } +static int isHello(const char* str) { + return strcmp(str, "hello") == 0; +} + mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32); @@ -48,6 +52,7 @@ 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); +mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP); M_TEST_DEFINE(voidArgs) { struct mScriptFrame frame; @@ -400,6 +405,33 @@ M_TEST_DEFINE(f32Equality) { assert_true(f32A.type->equal(&f32A, &u32)); } +M_TEST_DEFINE(stringEquality) { + struct mScriptValue* stringA = mScriptStringCreateFromUTF8("hello"); + struct mScriptValue* stringB = mScriptStringCreateFromUTF8("world"); + struct mScriptValue* stringC = mScriptStringCreateFromUTF8("hello"); + struct mScriptValue charpA = mSCRIPT_MAKE_CHARP("hello"); + struct mScriptValue charpB = mSCRIPT_MAKE_CHARP("world"); + + assert_true(stringA->type->equal(stringA, stringC)); + assert_false(stringA->type->equal(stringA, stringB)); + + assert_true(stringA->type->equal(stringA, &charpA)); + assert_false(stringA->type->equal(stringA, &charpB)); + + assert_true(charpA.type->equal(&charpA, stringA)); + assert_false(charpA.type->equal(&charpA, stringB)); + + charpB = mSCRIPT_MAKE_CHARP("hello"); + assert_true(charpA.type->equal(&charpA, &charpB)); + + charpB = mSCRIPT_MAKE_CHARP("world"); + assert_false(charpA.type->equal(&charpA, &charpB)); + + mScriptValueDeref(stringA); + mScriptValueDeref(stringB); + mScriptValueDeref(stringC); +} + M_TEST_DEFINE(hashTableBasic) { struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); assert_non_null(table); @@ -429,6 +461,57 @@ M_TEST_DEFINE(hashTableBasic) { mScriptValueDeref(table); } +M_TEST_DEFINE(hashTableString) { + 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 key = mSCRIPT_MAKE_CHARP("key"); + struct mScriptValue badKey = mSCRIPT_MAKE_CHARP("bad"); + + assert_true(mScriptTableInsert(table, &key, intValue)); + assert_int_equal(intValue->refs, 2); + + struct mScriptValue* lookupValue = mScriptTableLookup(table, &key); + assert_non_null(lookupValue); + assert_ptr_equal(lookupValue, intValue); + + lookupValue = mScriptTableLookup(table, &badKey); + assert_null(lookupValue); + + assert_true(mScriptTableRemove(table, &key)); + assert_int_equal(intValue->refs, 1); + + mScriptValueDeref(intValue); + mScriptValueDeref(table); +} + +M_TEST_DEFINE(stringIsHello) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CHARP, "hello"); + assert_true(mScriptInvoke(&boundIsHello, &frame)); + int val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 1); + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(stringIsNotHello) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + mSCRIPT_PUSH(&frame.arguments, CHARP, "world"); + assert_true(mScriptInvoke(&boundIsHello, &frame)); + int val; + assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_int_equal(val, 0); + mScriptFrameDeinit(&frame); +} + M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(voidArgs), cmocka_unit_test(voidFunc), @@ -445,4 +528,8 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(s32Equality), cmocka_unit_test(u32Equality), cmocka_unit_test(f32Equality), - cmocka_unit_test(hashTableBasic)) + cmocka_unit_test(stringEquality), + cmocka_unit_test(hashTableBasic), + cmocka_unit_test(hashTableString), + cmocka_unit_test(stringIsHello), + cmocka_unit_test(stringIsNotHello)) diff --git a/src/script/types.c b/src/script/types.c index f3dd09bf8..57edf070a 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -5,12 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include static void _allocTable(struct mScriptValue*); static void _freeTable(struct mScriptValue*); static void _deinitTableValue(void*); +static void _allocString(struct mScriptValue*); +static void _freeString(struct mScriptValue*); +static uint32_t _hashString(const struct mScriptValue*); + static uint32_t _hashScalar(const struct mScriptValue*); static uint32_t _valHash(const void* val, size_t len, uint32_t seed); @@ -22,6 +27,8 @@ static bool _typeEqual(const struct mScriptValue*, const struct mScriptValue*); static bool _s32Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _u32Equal(const struct mScriptValue*, const struct mScriptValue*); static bool _f32Equal(const struct mScriptValue*, const struct mScriptValue*); +static bool _charpEqual(const struct mScriptValue*, const struct mScriptValue*); +static bool _stringEqual(const struct mScriptValue*, const struct mScriptValue*); const struct mScriptType mSTVoid = { .base = mSCRIPT_TYPE_VOID, @@ -63,6 +70,26 @@ const struct mScriptType mSTFloat32 = { .equal = _f32Equal, }; +const struct mScriptType mSTString = { + .base = mSCRIPT_TYPE_STRING, + .size = sizeof(struct mScriptString), + .name = "string", + .alloc = _allocString, + .free = _freeString, + .hash = _hashString, + .equal = _stringEqual, +}; + +const struct mScriptType mSTCharPtr = { + .base = mSCRIPT_TYPE_STRING, + .size = sizeof(char*), + .name = "charptr", + .alloc = NULL, + .free = NULL, + .hash = _hashString, + .equal = _charpEqual, +}; + const struct mScriptType mSTTable = { .base = mSCRIPT_TYPE_TABLE, .size = sizeof(struct Table), @@ -95,6 +122,35 @@ void _deinitTableValue(void* val) { mScriptValueDeref(val); } +static void _allocString(struct mScriptValue* val) { + struct mScriptString* string = calloc(1, sizeof(*string)); + string->size = 0; + string->buffer = NULL; + val->value.opaque = string; +} + +static void _freeString(struct mScriptValue* val) { + struct mScriptString* string = val->value.opaque; + if (string->size) { + free(string->buffer); + } + free(string); +} + +static uint32_t _hashString(const struct mScriptValue* val) { + const char* buffer = 0; + size_t size = 0; + if (val->type == &mSTString) { + struct mScriptString* string = val->value.opaque; + buffer = string->buffer; + size = string->size; + } else if (val->type == &mSTCharPtr) { + buffer = val->value.opaque; + size = strlen(buffer); + } + return hash32(buffer, size, 0); +} + uint32_t _hashScalar(const struct mScriptValue* val) { // From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key uint32_t x; @@ -136,7 +192,6 @@ void _valDeref(void* val) { bool _typeEqual(const struct mScriptValue* a, const struct mScriptValue* b) { return a->type == b->type; - } bool _s32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { @@ -205,6 +260,58 @@ bool _f32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { return a->value.f32 == val; } +bool _charpEqual(const struct mScriptValue* a, const struct mScriptValue* b) { + const char* valA = a->value.opaque; + const char* valB; + size_t lenA; + size_t lenB; + if (b->type->base != mSCRIPT_TYPE_STRING) { + return false; + } + if (b->type == &mSTCharPtr) { + valB = b->value.opaque; + lenB = strlen(valB); + } else if (b->type == &mSTString) { + struct mScriptString* stringB = b->value.opaque; + valB = stringB->buffer; + lenB = stringB->size; + } else { + // TODO: Allow casting + return false; + } + lenA = strlen(valA); + if (lenA != lenB) { + return false; + } + return strncmp(valA, valB, lenA) == 0; +} + +bool _stringEqual(const struct mScriptValue* a, const struct mScriptValue* b) { + struct mScriptString* stringA = a->value.opaque; + const char* valA = stringA->buffer; + const char* valB; + size_t lenA = stringA->size; + size_t lenB; + if (b->type->base != mSCRIPT_TYPE_STRING) { + return false; + } + if (b->type == &mSTCharPtr) { + valB = b->value.opaque; + lenB = strlen(valB); + } else if (b->type == &mSTString) { + struct mScriptString* stringB = b->value.opaque; + valB = stringB->buffer; + lenB = stringB->size; + } else { + // TODO: Allow casting + return false; + } + if (lenA != lenB) { + return false; + } + return strncmp(valA, valB, lenA) == 0; +} + struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) { // TODO: Use an arena instead of just the generic heap struct mScriptValue* val = malloc(sizeof(*val)); @@ -241,6 +348,14 @@ void mScriptValueDeref(struct mScriptValue* val) { free(val); } +struct mScriptValue* mScriptStringCreateFromUTF8(const char* string) { + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); + struct mScriptString* internal = val->value.opaque; + internal->size = strlen(string); + internal->buffer = strdup(string); + return val; +} + bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value) { if (table->type != mSCRIPT_TYPE_MS_TABLE) { return false;