From fc34b727416dca840a72f80857a924b6c6d5a94e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 May 2022 22:01:18 -0700 Subject: [PATCH] Scripting: Add prototype documentation generator, outputs to YAML --- CMakeLists.txt | 7 + src/script/docgen.c | 348 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 355 insertions(+) create mode 100644 src/script/docgen.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 42aaa896c..48561819c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ if(NOT LIBMGBA_ONLY) set(BUILD_GL ON CACHE BOOL "Build with OpenGL") set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2") set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3") + set(BUILD_DOCGEN OFF CACHE BOOL "Build the scripting API documentation generator") set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") set(DISTBUILD OFF CACHE BOOL "Build distribution packages") @@ -89,6 +90,7 @@ if(NOT LIBMGBA_ONLY) set(WIN32_UNIX_PATHS OFF CACHE BOOL "Use Unix-like paths") mark_as_advanced(WIN32_UNIX_PATHS) endif() + mark_as_advanced(BUILD_DOCGEN) else() set(DISABLE_FRONTENDS ON) set(DISABLE_DEPS ON) @@ -961,6 +963,11 @@ if(BUILD_UPDATER) endif() endif() +if(ENABLE_SCRIPTING AND BUILD_DOCGEN) + add_executable(docgen ${CMAKE_CURRENT_SOURCE_DIR}/src/script/docgen.c) + target_link_libraries(docgen ${OS_LIB} ${PLATFORM_LIBRARY} ${BINARY_NAME}) +endif() + if(BUILD_SDL) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/sdl ${CMAKE_CURRENT_BINARY_DIR}/sdl) endif() diff --git a/src/script/docgen.c b/src/script/docgen.c new file mode 100644 index 000000000..2c832cd67 --- /dev/null +++ b/src/script/docgen.c @@ -0,0 +1,348 @@ +/* Copyright (c) 2013-2022 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 +#include +#include +#include + +struct mScriptContext context; +struct Table types; + +void explainValue(struct mScriptValue* value, int level); +void explainType(struct mScriptType* type, int level); + +void addTypesFromTuple(const struct mScriptTypeTuple*); +void addTypesFromTable(struct Table*); + +void addType(const struct mScriptType* type) { + if (HashTableLookup(&types, type->name) || type->isConst) { + return; + } + HashTableInsert(&types, type->name, (struct mScriptType*) type); + switch (type->base) { + case mSCRIPT_TYPE_FUNCTION: + addTypesFromTuple(&type->details.function.parameters); + addTypesFromTuple(&type->details.function.returnType); + break; + case mSCRIPT_TYPE_OBJECT: + mScriptClassInit(type->details.cls); + if (type->details.cls->parent) { + addType(type->details.cls->parent); + } + addTypesFromTable(&type->details.cls->instanceMembers); + break; + case mSCRIPT_TYPE_OPAQUE: + if (type->details.type) { + addType(type->details.type); + } + } +} + +void addTypesFromTuple(const struct mScriptTypeTuple* tuple) { + size_t i; + for (i = 0; i < tuple->count; ++i) { + addType(tuple->entries[i]); + } +} + +void addTypesFromTable(struct Table* table) { + struct TableIterator iter; + if (!HashTableIteratorStart(table, &iter)) { + return; + } + do { + struct mScriptClassMember* member = HashTableIteratorGetValue(table, &iter); + addType(member->type); + } while(HashTableIteratorNext(table, &iter)); +} + +bool printval(const struct mScriptValue* value, char* buffer, size_t bufferSize) { + struct mScriptValue sval; + switch (value->type->base) { + case mSCRIPT_TYPE_SINT: + if (value->type->size <= 4) { + snprintf(buffer, bufferSize, "%"PRId32, value->value.s32); + return true; + } + if (value->type->size == 8) { + snprintf(buffer, bufferSize, "%"PRId64, value->value.s64); + return true; + } + return false; + case mSCRIPT_TYPE_UINT: + if (value->type->size <= 4) { + snprintf(buffer, bufferSize, "%"PRIu32, value->value.u32); + return true; + } + if (value->type->size == 8) { + snprintf(buffer, bufferSize, "%"PRIu64, value->value.u64); + return true; + } + return false; + case mSCRIPT_TYPE_STRING: + if (!mScriptCast(mSCRIPT_TYPE_MS_CHARP, value, &sval)) { + return false; + } + if (sval.value.opaque) { + snprintf(buffer, bufferSize, "\"%s\"", sval.value.opaque); + } else { + snprintf(buffer, bufferSize, "null"); + } + return true; + } + return false; +} + +void explainTable(struct mScriptValue* value, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + + struct TableIterator iter; + if (mScriptTableIteratorStart(value, &iter)) { + do { + char keyval[1024]; + struct mScriptValue* k = mScriptTableIteratorGetKey(value, &iter); + printval(k, keyval, sizeof(keyval)); + printf("%s- key: %s\n", indent, keyval); + struct mScriptValue* v = mScriptTableIteratorGetValue(value, &iter); + explainValue(v, level + 1); + } while (mScriptTableIteratorNext(value, &iter)); + } +} + +void explainClass(struct mScriptTypeClass* cls, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + + if (cls->parent) { + printf("%sparent: %s\n", indent, cls->parent->name); + } + + printf("%smembers:\n", indent); + const char* docstring = NULL; + const struct mScriptClassInitDetails* details; + size_t i; + for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { + details = &cls->details[i]; + switch (details->type) { + case mSCRIPT_CLASS_INIT_DOCSTRING: + docstring = details->info.comment; + break; + case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER: + printf("%s %s:\n", indent, details->info.member.name); + if (docstring) { + printf("%s comment: \"%s\"\n", indent, docstring); + docstring = NULL; + } + printf("%s type: %s\n", indent, details->info.member.type->name); + break; + } + } +} + +void explainObject(struct mScriptValue* value, int level) { + char indent[(level + 2) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + + struct mScriptTypeClass* cls = value->type->details.cls; + const struct mScriptClassInitDetails* details; + size_t i; + for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { + struct mScriptValue member; + details = &cls->details[i]; + + if (cls->details[i].type != mSCRIPT_CLASS_INIT_INSTANCE_MEMBER) { + continue; + } + printf("%s%s:\n", indent, details->info.member.name); + addType(details->info.member.type); + if (mScriptObjectGet(value, details->info.member.name, &member)) { + struct mScriptValue* unwrappedMember; + if (member.type->base == mSCRIPT_TYPE_WRAPPER) { + unwrappedMember = mScriptValueUnwrap(&member); + explainValue(unwrappedMember, level + 2); + } else { + explainValue(&member, level + 2); + } + } + } +} + +void explainValue(struct mScriptValue* value, int level) { + char valstring[1024]; + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + value = mScriptContextAccessWeakref(&context, value); + addType(value->type); + printf("%stype: %s\n", indent, value->type->name); + switch (value->type->base) { + case mSCRIPT_TYPE_TABLE: + printf("%svalue:\n", indent); + explainTable(value, level); + break; + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_STRING: + printval(value, valstring, sizeof(valstring)); + printf("%svalue: %s\n", indent, valstring); + break; + case mSCRIPT_TYPE_OBJECT: + printf("%svalue:\n", indent); + explainObject(value, level); + break; + default: + break; + } +} + +void explainTypeTuple(struct mScriptTypeTuple* tuple, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + printf("%svariable: %s\n", indent, tuple->variable ? "yes" : "no"); + printf("%slist:\n", indent); + 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 type: %s\n", indent, tuple->entries[i]->name); + } else { + printf("%s- type: %s\n", indent, tuple->entries[i]->name); + } + if (tuple->defaults && tuple->defaults[i].type) { + char defaultValue[128]; + printval(&tuple->defaults[i], defaultValue, sizeof(defaultValue)); + printf("%s default: %s\n", indent, defaultValue); + } + } +} + +void explainType(struct mScriptType* type, int level) { + char indent[(level + 1) * 2 + 1]; + memset(indent, ' ', sizeof(indent) - 1); + indent[sizeof(indent) - 1] = '\0'; + printf("%sbase: ", indent); + switch (type->base) { + case mSCRIPT_TYPE_SINT: + puts("sint"); + break; + case mSCRIPT_TYPE_UINT: + puts("uint"); + break; + case mSCRIPT_TYPE_FLOAT: + puts("float"); + break; + case mSCRIPT_TYPE_STRING: + puts("string"); + break; + case mSCRIPT_TYPE_FUNCTION: + puts("function"); + printf("%sparameters:\n", indent); + explainTypeTuple(&type->details.function.parameters, level + 1); + printf("%sreturn:\n", indent); + explainTypeTuple(&type->details.function.returnType, level + 1); + break; + case mSCRIPT_TYPE_OPAQUE: + puts("opaque"); + break; + case mSCRIPT_TYPE_OBJECT: + puts("object"); + explainClass(type->details.cls, level); + break; + case mSCRIPT_TYPE_LIST: + puts("list"); + break; + case mSCRIPT_TYPE_TABLE: + puts("table"); + break; + case mSCRIPT_TYPE_WRAPPER: + puts("wrapper"); + break; + case mSCRIPT_TYPE_WEAKREF: + puts("weakref"); + break; + case mSCRIPT_TYPE_VOID: + puts("void"); + break; + } +} + +void explainCore(struct mCore* core) { + mScriptContextAttachCore(&context, core); + struct mScriptValue* emu = mScriptContextGetGlobal(&context, "emu"); + explainValue(emu, 1); + mScriptContextDetachCore(&context); +} + +int main(int argc, char* argv[]) { + mScriptContextInit(&context); + mScriptContextAttachStdlib(&context); + mScriptContextSetTextBufferFactory(&context, NULL, NULL); + HashTableInit(&types, 0, NULL); + + addType(mSCRIPT_TYPE_MS_S8); + addType(mSCRIPT_TYPE_MS_U8); + addType(mSCRIPT_TYPE_MS_S16); + addType(mSCRIPT_TYPE_MS_U16); + addType(mSCRIPT_TYPE_MS_S32); + addType(mSCRIPT_TYPE_MS_U32); + addType(mSCRIPT_TYPE_MS_F32); + addType(mSCRIPT_TYPE_MS_S64); + addType(mSCRIPT_TYPE_MS_U64); + addType(mSCRIPT_TYPE_MS_F64); + addType(mSCRIPT_TYPE_MS_STR); + addType(mSCRIPT_TYPE_MS_CHARP); + addType(mSCRIPT_TYPE_MS_LIST); + addType(mSCRIPT_TYPE_MS_TABLE); + addType(mSCRIPT_TYPE_MS_WRAPPER); + + puts("version:"); + printf(" string: \"%s\"\n", projectVersion); + printf(" commit: \"%s\"\n", gitCommit); + puts("root:"); + struct TableIterator iter; + if (HashTableIteratorStart(&context.rootScope, &iter)) { + do { + const char* name = HashTableIteratorGetKey(&context.rootScope, &iter); + printf(" %s:\n", name); + struct mScriptValue* value = HashTableIteratorGetValue(&context.rootScope, &iter); + explainValue(value, 1); + } while (HashTableIteratorNext(&context.rootScope, &iter)); + } + puts("emu:"); + struct mCore* core; + core = mCoreCreate(mPLATFORM_GBA); + if (core) { + puts(" gba:"); + core->init(core); + explainCore(core); + core->deinit(core); + } + core = mCoreCreate(mPLATFORM_GB); + if (core) { + puts(" gb:"); + core->init(core); + explainCore(core); + core->deinit(core); + } + puts("types:"); + if (HashTableIteratorStart(&types, &iter)) { + do { + const char* name = HashTableIteratorGetKey(&types, &iter); + printf(" %s:\n", name); + struct mScriptType* type = HashTableIteratorGetValue(&types, &iter); + explainType(type, 1); + } while (HashTableIteratorNext(&types, &iter)); + } + + HashTableDeinit(&types); + mScriptContextDeinit(&context); + return 0; +}