Scripting: Start string bringup

This commit is contained in:
Vicki Pfau 2021-08-30 19:48:40 -07:00
parent 8818bf4048
commit 9955d0d19b
3 changed files with 224 additions and 2 deletions

View File

@ -20,6 +20,8 @@ CXX_GUARD_START
#define mSCRIPT_TYPE_C_S32 int32_t #define mSCRIPT_TYPE_C_S32 int32_t
#define mSCRIPT_TYPE_C_U32 uint32_t #define mSCRIPT_TYPE_C_U32 uint32_t
#define mSCRIPT_TYPE_C_F32 float #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_PTR void*
#define mSCRIPT_TYPE_C_TABLE Table* #define mSCRIPT_TYPE_C_TABLE Table*
#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* #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_S32 s32
#define mSCRIPT_TYPE_FIELD_U32 u32 #define mSCRIPT_TYPE_FIELD_U32 u32
#define mSCRIPT_TYPE_FIELD_F32 f32 #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_PTR opaque
#define mSCRIPT_TYPE_FIELD_TABLE opaque #define mSCRIPT_TYPE_FIELD_TABLE opaque
#define mSCRIPT_TYPE_FIELD_S(STRUCT) 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_S32 (&mSTSInt32)
#define mSCRIPT_TYPE_MS_U32 (&mSTUInt32) #define mSCRIPT_TYPE_MS_U32 (&mSTUInt32)
#define mSCRIPT_TYPE_MS_F32 (&mSTFloat32) #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_TABLE (&mSTTable)
#define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT) #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_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_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_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_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_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_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_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_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_F32(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_F32, f32, VALUE)
#define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(mSCRIPT_TYPE_MS_CHARP, opaque, VALUE)
enum { enum {
mSCRIPT_TYPE_VOID = 0, mSCRIPT_TYPE_VOID = 0,
@ -187,6 +196,8 @@ extern const struct mScriptType mSTVoid;
extern const struct mScriptType mSTSInt32; extern const struct mScriptType mSTSInt32;
extern const struct mScriptType mSTUInt32; extern const struct mScriptType mSTUInt32;
extern const struct mScriptType mSTFloat32; extern const struct mScriptType mSTFloat32;
extern const struct mScriptType mSTString;
extern const struct mScriptType mSTCharPtr;
extern const struct mScriptType mSTTable; extern const struct mScriptType mSTTable;
struct mScriptTypeTuple { struct mScriptTypeTuple {
@ -229,6 +240,12 @@ struct mScriptValue {
DECLARE_VECTOR(mScriptList, 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 mScriptFrame {
struct mScriptList arguments; struct mScriptList arguments;
struct mScriptList returnValues; struct mScriptList returnValues;
@ -238,12 +255,15 @@ struct mScriptFrame {
struct mScriptFunction { struct mScriptFunction {
struct mScriptTypeFunction signature; struct mScriptTypeFunction signature;
bool (*call)(struct mScriptFrame*); bool (*call)(struct mScriptFrame*);
void* context;
}; };
struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type); struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type);
void mScriptValueRef(struct mScriptValue* val); void mScriptValueRef(struct mScriptValue* val);
void mScriptValueDeref(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 mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value);
bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key); bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key);
struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key); struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key);

View File

@ -41,6 +41,10 @@ static int subInts(int a, int b) {
return a - b; return a - b;
} }
static int isHello(const char* str) {
return strcmp(str, "hello") == 0;
}
mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0);
mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32);
mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 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(boundIdentityStruct, S(Test), identityStruct, 1, S(Test));
mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32); mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, S32);
mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, S32); mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, S32);
mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP);
M_TEST_DEFINE(voidArgs) { M_TEST_DEFINE(voidArgs) {
struct mScriptFrame frame; struct mScriptFrame frame;
@ -400,6 +405,33 @@ M_TEST_DEFINE(f32Equality) {
assert_true(f32A.type->equal(&f32A, &u32)); 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) { M_TEST_DEFINE(hashTableBasic) {
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
assert_non_null(table); assert_non_null(table);
@ -429,6 +461,57 @@ M_TEST_DEFINE(hashTableBasic) {
mScriptValueDeref(table); 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, M_TEST_SUITE_DEFINE(mScript,
cmocka_unit_test(voidArgs), cmocka_unit_test(voidArgs),
cmocka_unit_test(voidFunc), cmocka_unit_test(voidFunc),
@ -445,4 +528,8 @@ M_TEST_SUITE_DEFINE(mScript,
cmocka_unit_test(s32Equality), cmocka_unit_test(s32Equality),
cmocka_unit_test(u32Equality), cmocka_unit_test(u32Equality),
cmocka_unit_test(f32Equality), 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))

View File

@ -5,12 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/script/types.h> #include <mgba/script/types.h>
#include <mgba-util/hash.h>
#include <mgba-util/table.h> #include <mgba-util/table.h>
static void _allocTable(struct mScriptValue*); static void _allocTable(struct mScriptValue*);
static void _freeTable(struct mScriptValue*); static void _freeTable(struct mScriptValue*);
static void _deinitTableValue(void*); 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 _hashScalar(const struct mScriptValue*);
static uint32_t _valHash(const void* val, size_t len, uint32_t seed); 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 _s32Equal(const struct mScriptValue*, const struct mScriptValue*);
static bool _u32Equal(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 _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 = { const struct mScriptType mSTVoid = {
.base = mSCRIPT_TYPE_VOID, .base = mSCRIPT_TYPE_VOID,
@ -63,6 +70,26 @@ const struct mScriptType mSTFloat32 = {
.equal = _f32Equal, .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 = { const struct mScriptType mSTTable = {
.base = mSCRIPT_TYPE_TABLE, .base = mSCRIPT_TYPE_TABLE,
.size = sizeof(struct Table), .size = sizeof(struct Table),
@ -95,6 +122,35 @@ void _deinitTableValue(void* val) {
mScriptValueDeref(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) { 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 // From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
uint32_t x; uint32_t x;
@ -136,7 +192,6 @@ void _valDeref(void* val) {
bool _typeEqual(const struct mScriptValue* a, const struct mScriptValue* b) { bool _typeEqual(const struct mScriptValue* a, const struct mScriptValue* b) {
return a->type == b->type; return a->type == b->type;
} }
bool _s32Equal(const struct mScriptValue* a, const struct mScriptValue* b) { 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; 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) { struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type) {
// TODO: Use an arena instead of just the generic heap // TODO: Use an arena instead of just the generic heap
struct mScriptValue* val = malloc(sizeof(*val)); struct mScriptValue* val = malloc(sizeof(*val));
@ -241,6 +348,14 @@ void mScriptValueDeref(struct mScriptValue* val) {
free(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) { bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value) {
if (table->type != mSCRIPT_TYPE_MS_TABLE) { if (table->type != mSCRIPT_TYPE_MS_TABLE) {
return false; return false;