diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 293c63f46..346dbf64f 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -164,6 +164,12 @@ CXX_GUARD_START .comment = DOCSTRING \ } \ }, +#define mSCRIPT_DEFINE_CLASS_DOCSTRING(DOCSTRING) { \ + .type = mSCRIPT_CLASS_INIT_CLASS_DOCSTRING, \ + .info = { \ + .comment = DOCSTRING \ + } \ +}, #define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \ .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index e0a369c46..1914bd1fe 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -125,6 +125,7 @@ enum mScriptTypeBase { enum mScriptClassInitType { mSCRIPT_CLASS_INIT_END = 0, + mSCRIPT_CLASS_INIT_CLASS_DOCSTRING, mSCRIPT_CLASS_INIT_DOCSTRING, mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, mSCRIPT_CLASS_INIT_INHERIT, @@ -218,6 +219,7 @@ struct mScriptTypeClass { bool init; const struct mScriptClassInitDetails* details; const struct mScriptType* parent; + const char* docstring; struct Table instanceMembers; struct Table castToMembers; struct mScriptClassMember* alloc; // TODO diff --git a/src/core/scripting.c b/src/core/scripting.c index b0c37d0e0..2b9ea3190 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -261,6 +261,10 @@ mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, size, mScriptMemoryDomai mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WRAPPER, name, mScriptMemoryDomainName, 0); mSCRIPT_DEFINE_STRUCT(mScriptMemoryDomain) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "An object used for access directly to a memory domain, e.g. the cartridge, " + "instead of through a whole address space, as with the functions directly on struct::mCore." + ) mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given offset") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read8) mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given offset") @@ -385,6 +389,9 @@ mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoad mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, screenshot, mCoreTakeScreenshot, 0); mSCRIPT_DEFINE_STRUCT(mCore) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "An instance of an emulator core." + ) mSCRIPT_DEFINE_DOCSTRING("Get which platform is being emulated") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, platform) mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame") @@ -522,8 +529,13 @@ mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WRAPPER, _get, _mScriptCoreAda mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0); mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "A wrapper around a struct::mCore object that exposes more functionality. " + "It can be implicity cast to a Core object, and exposes the same methods. " + "Please see the documentation on struct::mCore for details on those methods." + ) mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(mScriptCoreAdapter, PS(mCore), _core, core) - mSCRIPT_DEFINE_DOCSTRING("A table containing a platform-specific set of memory adapters") + mSCRIPT_DEFINE_DOCSTRING("A table containing a platform-specific set of struct::mScriptMemoryDomain objects") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptCoreAdapter, TABLE, memory) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptCoreAdapter) mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptCoreAdapter) @@ -598,6 +610,9 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, error, mScriptConsoleError, 1 mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptConsole, S(mScriptTextBuffer), createBuffer, _mScriptConsoleCreateBuffer, 1, CHARP, name); mSCRIPT_DEFINE_STRUCT(mScriptConsole) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "A singleton object `console` that can be used for presenting textual information to the user via a console." + ) mSCRIPT_DEFINE_DOCSTRING("Print a log to the console") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, log) mSCRIPT_DEFINE_DOCSTRING("Print a warning to the console") @@ -645,6 +660,10 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, advance, 1, S32, adv); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setName, 1, CHARP, name); mSCRIPT_DEFINE_STRUCT(mScriptTextBuffer) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "An object that can be used to present texual data to the user. It is displayed monospaced, " + "and text can be edited after sending by moving the cursor or clearing the buffer." + ) mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(mScriptTextBuffer, deinit) mSCRIPT_DEFINE_DOCSTRING("Get the current x position of the cursor") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getX) diff --git a/src/script/docgen.c b/src/script/docgen.c index 2c832cd67..31d8363c8 100644 --- a/src/script/docgen.c +++ b/src/script/docgen.c @@ -59,6 +59,34 @@ void addTypesFromTable(struct Table* table) { } while(HashTableIteratorNext(table, &iter)); } +void printchomp(const char* string, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + + const char* start = string; + char lineBuffer[1024]; + while (true) { + const char* end = strchr(start, '\n'); + if (end) { + size_t size = end - start; + if (sizeof(lineBuffer) - 1 < size) { + size = sizeof(lineBuffer) - 1; + } + strncpy(lineBuffer, start, size); + lineBuffer[size] = '\0'; + printf("%s%s\n", indent, lineBuffer); + } else { + printf("%s%s\n", indent, start); + break; + } + start = end + 1; + if (!*end) { + break; + } + } +} + bool printval(const struct mScriptValue* value, char* buffer, size_t bufferSize) { struct mScriptValue sval; switch (value->type->base) { @@ -122,6 +150,14 @@ void explainClass(struct mScriptTypeClass* cls, int level) { if (cls->parent) { printf("%sparent: %s\n", indent, cls->parent->name); } + if (cls->docstring) { + if (strchr(cls->docstring, '\n')) { + printf("%scomment: |-\n", indent); + printchomp(cls->docstring, level + 1); + } else { + printf("%scomment: \"%s\"\n", indent, cls->docstring); + } + } printf("%smembers:\n", indent); const char* docstring = NULL; @@ -137,7 +173,7 @@ void explainClass(struct mScriptTypeClass* cls, int level) { printf("%s %s:\n", indent, details->info.member.name); if (docstring) { printf("%s comment: \"%s\"\n", indent, docstring); - docstring = NULL; + docstring = NULL; } printf("%s type: %s\n", indent, details->info.member.type->name); break; @@ -211,7 +247,7 @@ void explainTypeTuple(struct mScriptTypeTuple* tuple, int level) { size_t i; for (i = 0; i < tuple->count; ++i) { if (tuple->names[i]) { - printf("%s- name: %s\n", indent, tuple->names[i]); + printf("%s- name: %s\n", indent, tuple->names[i]); printf("%s type: %s\n", indent, tuple->entries[i]->name); } else { printf("%s- type: %s\n", indent, tuple->entries[i]->name); @@ -274,10 +310,57 @@ void explainType(struct mScriptType* type, int level) { } } +bool call(struct mScriptValue* obj, const char* method, struct mScriptFrame* frame) { + struct mScriptValue fn; + if (!mScriptObjectGet(obj, method, &fn)) { + return false; + } + struct mScriptValue* this = mScriptListAppend(&frame->arguments); + this->type = mSCRIPT_TYPE_MS_WRAPPER; + this->refs = mSCRIPT_VALUE_UNREF; + this->flags = 0; + this->value.opaque = obj; + return mScriptInvoke(&fn, frame); +} + void explainCore(struct mCore* core) { + struct mScriptValue wrapper; mScriptContextAttachCore(&context, core); struct mScriptValue* emu = mScriptContextGetGlobal(&context, "emu"); - explainValue(emu, 1); + addType(emu->type); + if (mScriptObjectGet(emu, "memory", &wrapper)) { + struct mScriptValue* memory = mScriptValueUnwrap(&wrapper); + struct TableIterator iter; + printf(" memory:\n"); + if (mScriptTableIteratorStart(memory, &iter)) { + do { + struct mScriptValue* name = mScriptTableIteratorGetKey(memory, &iter); + struct mScriptValue* value = mScriptTableIteratorGetValue(memory, &iter); + + printf(" %s:\n", name->value.string->buffer); + value = mScriptContextAccessWeakref(&context, value); + + struct mScriptFrame frame; + uint32_t baseVal; + struct mScriptValue* shortName; + + mScriptFrameInit(&frame); + call(value, "base", &frame); + mScriptPopU32(&frame.returnValues, &baseVal); + mScriptFrameDeinit(&frame); + + mScriptFrameInit(&frame); + call(value, "name", &frame); + shortName = mScriptValueUnwrap(mScriptListGetPointer(&frame.returnValues, 0)); + mScriptFrameDeinit(&frame); + + printf(" base: 0x%x\n", baseVal); + printf(" name: \"%s\"\n", shortName->value.string->buffer); + + mScriptValueDeref(shortName); + } while (mScriptTableIteratorNext(memory, &iter)); + } + } mScriptContextDetachCore(&context); } diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 1a739c160..cae385735 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -25,8 +25,11 @@ mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function); mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) -mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type") -mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "A singleton object `callbacks` used for managing callbacks." + ) + mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type") + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add) mSCRIPT_DEFINE_END; void mScriptContextAttachStdlib(struct mScriptContext* context) { diff --git a/src/script/types.c b/src/script/types.c index 0c20b9257..b22ee1ad8 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -931,6 +931,11 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript switch (detail->type) { case mSCRIPT_CLASS_INIT_END: break; + case mSCRIPT_CLASS_INIT_CLASS_DOCSTRING: + if (!child) { + cls->docstring = detail->info.comment; + } + break; case mSCRIPT_CLASS_INIT_DOCSTRING: docstring = detail->info.comment; break;