From 39658dfd714a872f76c646837522d7296b4d43b3 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Mon, 20 Feb 2023 19:49:37 -0500 Subject: [PATCH] docs: Automatically generate documentation from goal_src code (#2214) This automatically generates documentation from goal_src docstrings, think doxygen/java-docs/rust docs/etc. It mostly supports everything already, but here are the following things that aren't yet complete: - file descriptions - high-level documentation to go along with this (think pure markdown docs describing overall systems that would be co-located in goal_src for organizational purposes) - enums - states - std-lib functions (all have empty strings right now for docs anyway) The job of the new `gen-docs` function is solely to generate a bunch of JSON data which should give you everything you need to generate some decent documentation (outputting markdown/html/pdf/etc). It is not it's responsibility to do that nice formatting -- this is by design to intentionally delegate that responsibility elsewhere. Side-note, this is about 12-15MB of minified json for jak 2 so far :) In our normal "goal_src has changed" action -- we will generate this data, and the website can download it -- use the information to generate the documentation at build time -- and it will be included in the site. Likewise, if we wanted to include docs along with releases for offline viewing, we could do so in a similar fashion (just write a formatting script to generate said documentation). Lastly this work somewhat paves the way for doing more interesting things in the LSP like: - whats the docstring for this symbol? - autocompleting function arguments - type checking function arguments - where is this symbol defined? - etc Fixes #2215 --- .github/workflows/inform-pages-repo.yaml | 68 ++- .gitignore | 4 + common/goos/Interpreter.h | 3 + common/goos/ParseHelpers.cpp | 1 + common/type_system/Type.cpp | 2 +- common/util/Trie.h | 10 + common/util/string_util.cpp | 12 + common/util/string_util.h | 1 + goal_src/goal-lib.gc | 15 +- goal_src/goos-lib.gs | 31 +- goal_src/jak1/kernel/gcommon.gc | 2 +- goalc/CMakeLists.txt | 2 +- goalc/compiler/Compiler.cpp | 6 +- goalc/compiler/Compiler.h | 9 +- goalc/compiler/SymbolInfo.h | 124 ++++-- goalc/compiler/compilation/Atoms.cpp | 401 +++++++++--------- .../compiler/compilation/CompilerControl.cpp | 212 ++++++++- goalc/compiler/compilation/Define.cpp | 15 +- goalc/compiler/compilation/Macro.cpp | 15 +- goalc/compiler/compilation/Type.cpp | 4 +- goalc/compiler/docs/DocTypes.cpp | 184 ++++++++ goalc/compiler/docs/DocTypes.h | 151 +++++++ goalc/main.cpp | 1 + 23 files changed, 994 insertions(+), 279 deletions(-) create mode 100644 goalc/compiler/docs/DocTypes.cpp create mode 100644 goalc/compiler/docs/DocTypes.h diff --git a/.github/workflows/inform-pages-repo.yaml b/.github/workflows/inform-pages-repo.yaml index 9bdb97ad23..4f75ad6226 100644 --- a/.github/workflows/inform-pages-repo.yaml +++ b/.github/workflows/inform-pages-repo.yaml @@ -8,15 +8,75 @@ on: - 'goal_src/**' jobs: - inform: - name: Inform Pages Repo + gen-docs: + name: Generate Documentation if: github.repository == 'open-goal/jak-project' - runs-on: ubuntu-latest - timeout-minutes: 10 + runs-on: ubuntu-20.04 + timeout-minutes: 45 + env: # overrides: https://github.com/mbitsnbites/buildcache/blob/master/doc/configuration.md + BUILDCACHE_MAX_CACHE_SIZE: 1000000000 # 1gb + BUILDCACHE_COMPRESS_FORMAT: ZSTD + BUILDCACHE_COMPRESS_LEVEL: 19 + BUILDCACHE_DIRECT_MODE: true + BUILDCACHE_LOG_FILE: ${{ github.workspace }}/buildcache.log steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Install Package Dependencies + run: > + sudo apt install build-essential cmake + clang gcc g++ lcov make nasm libxrandr-dev + libxinerama-dev libxcursor-dev libpulse-dev + libxi-dev zip ninja-build + + - name: Setup Buildcache + uses: mikehardy/buildcache-action@v2.1.0 + with: + cache_key: linux-ubuntu-20.04-${{ inputs.cachePrefix }}-${{ inputs.cmakePreset }} + buildcache_tag: v0.28.1 + + - name: CMake Generation + env: + CC: clang + CXX: clang++ + run: | + cmake -B build --preset=${{ inputs.cmakePreset }} \ + -DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache + + - name: Build Project + run: cmake --build build --parallel $((`nproc`)) --target goalc + + - name: Generate Docs For Jak 1 + run: | + ./build/goalc/goalc --cmd "(begin (make-group \"all-code\") (gen-docs \"${PWD}\"))" --game jak1 + + - name: Generate Docs For Jak 2 + run: | + ./build/goalc/goalc --cmd "(begin (make-group \"all-code\") (gen-docs \"${PWD}\"))" --game jak2 + + - name: Upload Docs Artifact + uses: actions/upload-artifact@v3 + with: + name: opengoal-docs + if-no-files-found: error + path: | + ./jak1*.json + ./jak2*.json + + - name: Get Artifact ID + env: + GITHUB_TOKEN: ${{ secrets.BOT_PAT }} + id: get-artifact-id + run: | # grab the id of the artifact we just created + echo "ARTIFACT_ID=$(gh api -H 'Accept: application/vnd.github+json' ${{github.event.workflow_run.artifacts_url}} --jq .artifacts[].id)" >> $GITHUB_ENV + echo "artifact id is ${{ env.ARTIFACT_ID }}" + - name: Send Dispatch uses: peter-evans/repository-dispatch@v2 with: token: ${{ secrets.BOT_PAT }} repository: 'open-goal/open-goal.github.io' event-type: updateProgress + client-payload: '{"docArtifactId": "${{ env.ARTIFACT_ID }}"}' diff --git a/.gitignore b/.gitignore index 0448a6e5ae..f25afd3fa1 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,7 @@ __pycache__/ # backup files from OpenMaya *.bak + +# docs +/jak1-*.json +/jak2-*.json diff --git a/common/goos/Interpreter.h b/common/goos/Interpreter.h index 1374c39eb6..1b837f5335 100644 --- a/common/goos/Interpreter.h +++ b/common/goos/Interpreter.h @@ -62,8 +62,11 @@ class Interpreter { const std::unordered_map>>& named); Object eval_pair(const Object& o, const std::shared_ptr& env); + + public: ArgumentSpec parse_arg_spec(const Object& form, Object& rest); + private: Object quasiquote_helper(const Object& form, const std::shared_ptr& env); IntType number_to_integer(const Object& obj); diff --git a/common/goos/ParseHelpers.cpp b/common/goos/ParseHelpers.cpp index 0963bd4ebd..74cd581e08 100644 --- a/common/goos/ParseHelpers.cpp +++ b/common/goos/ParseHelpers.cpp @@ -53,6 +53,7 @@ bool va_check( for (size_t i = 0; i < unnamed.size(); i++) { if (unnamed[i].has_value() && unnamed[i] != args.unnamed[i].type) { + // special case -- an empty list is a valid pair *err_string = fmt::format("Argument {} has type {} but {} was expected\nArgument is: {}", i, object_type_to_string(args.unnamed[i].type), object_type_to_string(unnamed[i].value()), args.unnamed[i].print()); diff --git a/common/type_system/Type.cpp b/common/type_system/Type.cpp index e2c17a0f7e..39c1550956 100644 --- a/common/type_system/Type.cpp +++ b/common/type_system/Type.cpp @@ -462,7 +462,7 @@ std::string Type::print_method_info() const { void Type::add_state(const std::string& name, const TypeSpec& type) { if (!m_states.insert({name, type}).second) { - throw std::runtime_error(fmt::format("State {} is multiply defined", name)); + throw std::runtime_error(fmt::format("State {} is already defined in type", name)); } } diff --git a/common/util/Trie.h b/common/util/Trie.h index 0767f164f9..6b808ba623 100644 --- a/common/util/Trie.h +++ b/common/util/Trie.h @@ -35,6 +35,9 @@ class Trie { // Get all objects starting with the given prefix. std::vector lookup_prefix(const std::string& str) const; + + // Get all nodes in the tree. + std::vector get_all_nodes() const; ~Trie(); private: @@ -178,3 +181,10 @@ template std::vector Trie::lookup_prefix(const std::string& str) const { return m_root.lookup_prefix(str.c_str()); } + +template +std::vector Trie::get_all_nodes() const { + std::vector result; + m_root.get_all_children(result); + return result; +} diff --git a/common/util/string_util.cpp b/common/util/string_util.cpp index 4ed1d4ac0c..ee73aaa789 100644 --- a/common/util/string_util.cpp +++ b/common/util/string_util.cpp @@ -81,4 +81,16 @@ std::string diff(const std::string& lhs, const std::string& rhs) { std::vector split(const ::std::string& str, char delimiter) { return google_diff::split_string(str, delimiter); } + +std::vector regex_get_capture_groups(const std::string& str, + const std::string& regex) { + std::vector groups; + std::smatch matches; + if (std::regex_search(str, matches, std::regex(regex))) { + for (size_t i = 1; i < matches.size(); i++) { + groups.push_back(matches[i].str()); + } + } + return groups; +} } // namespace str_util diff --git a/common/util/string_util.h b/common/util/string_util.h index 79a7af55d8..6e773fa83f 100644 --- a/common/util/string_util.h +++ b/common/util/string_util.h @@ -19,4 +19,5 @@ std::string diff(const std::string& lhs, const std::string& rhs); /// Default splits on \n characters std::vector split(const ::std::string& str, char delimiter = '\n'); std::string join(const std::vector& strs, const std::string& join_with); +std::vector regex_get_capture_groups(const std::string& str, const std::string& regex); } // namespace str_util diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 4b9d705318..ba9fb698ab 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -208,6 +208,7 @@ ;;;;;;;;;;;;;;;;;;; ;; GOAL Syntax ;;;;;;;;;;;;;;;;;;; + ;; Bind vars in body (defmacro let (bindings &rest body) `((lambda :inline-only #t ,(apply first bindings) ,@body) @@ -237,15 +238,11 @@ ;; Define a new function (defmacro defun (name bindings &rest body) - (if (and (> (length body) 1) ;; more than one thing in function - (string? (first body)) ;; first thing is a string - ) - ;; then it's a docstring and we ignore it. - `(define ,name (lambda :name ,name ,bindings ,@(cdr body))) - ;; otherwise don't ignore it. - `(define ,name (lambda :name ,name ,bindings ,@body)) - ) - ) + (let ((docstring "")) + (when (and (> (length body) 1) (string? (first body))) + (set! docstring (first body)) + (set! body (cdr body))) + `(define ,name ,docstring (lambda :name ,name ,bindings ,@body)))) ;; the compiler can't figure out types of a recursive function without ;; first knowing the return type, so we use this form to forward declare diff --git a/goal_src/goos-lib.gs b/goal_src/goos-lib.gs index 37d4f00793..617472e31a 100644 --- a/goal_src/goos-lib.gs +++ b/goal_src/goos-lib.gs @@ -240,7 +240,7 @@ `(let ((,(car bindings) ,(cadr bindings))) (while (not (null? ,(car bindings))) ,@body - + (set! ,(car bindings) (cdr ,(car bindings))) ) ) @@ -344,19 +344,18 @@ ;; Bootstrap GOAL macro system - ;; goal macro to define a goal macro (defgmacro defmacro (name args &rest body) `(begin - (add-macro-to-autocomplete ,name) - ,(if (and - (> (length body) 1) ;; more than one thing in function - (string? (first body)) ;; first thing is a string - ) + ,(if (and (> (length body) 1) (string? (first body))) ;; then it's a docstring and we ignore it. - `(seval (defgmacro ,name ,args ,@(cdr body))) + `(begin + (update-macro-metadata ,name ,(first body) ,args) + (seval (defgmacro ,name ,args ,@(cdr body)))) ;; otherwise don't ignore it. - `(seval (defgmacro ,name ,args ,@body)) + `(begin + (update-macro-metadata ,name "" ,args) + (seval (defgmacro ,name ,args ,@body))) ) ) ) @@ -381,30 +380,30 @@ (defsmacro doenum (bindings &rest body) ;; (doenum (name-var val-var 'enum &rest result) &rest body) - + (with-gensyms (enum-vals) `(let ((,enum-vals (get-enum-vals ,(third bindings)))) - + (while (not (null? ,enum-vals)) (let ((,(first bindings) (caar ,enum-vals)) ;; name (,(second bindings) (cdar ,enum-vals)) ;; value ) ,@body ) - + (set! ,enum-vals (cdr ,enum-vals)) ) - + ,@(cdddr bindings) - + ) ) - + ) (desfun enum-max (enum) "get the highest value in an enum" - + (let ((max-val -999999999999)) (doenum (name val enum) (when (> val max-val) diff --git a/goal_src/jak1/kernel/gcommon.gc b/goal_src/jak1/kernel/gcommon.gc index 16136243ea..30d43c898d 100644 --- a/goal_src/jak1/kernel/gcommon.gc +++ b/goal_src/jak1/kernel/gcommon.gc @@ -1465,4 +1465,4 @@ ,@body ) ) - ) \ No newline at end of file + ) diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 2c61857bf1..455cf492a4 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -54,7 +54,7 @@ add_library(compiler regalloc/Allocator.cpp regalloc/allocator_interface.cpp regalloc/Allocator_v2.cpp - ) + compiler/docs/DocTypes.cpp) target_link_libraries(compiler common Zydis tiny_gltf) diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 265bbd7ff7..831f6f9ea9 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -54,8 +54,10 @@ Compiler::Compiler(GameVersion version, } // add built-in forms to symbol info - for (auto& builtin : g_goal_forms) { - m_symbol_info.add_builtin(builtin.first); + for (const auto& [builtin_name, builtin_info] : g_goal_forms) { + SymbolInfo::Metadata sym_meta; + sym_meta.docstring = builtin_info.first; + m_symbol_info.add_builtin(builtin_name, sym_meta); } // load auto-complete history, only if we are running in the interactive mode. diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 00487a727e..cf14b4588c 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -105,6 +105,7 @@ class Compiler { listener::Listener m_listener; goos::Interpreter m_goos; Debugger m_debugger; + std::unordered_map m_macro_specs; std::unordered_map m_symbol_types; std::unordered_map m_global_constants; std::unordered_map m_inlineable_functions; @@ -608,14 +609,13 @@ class Compiler { Val* compile_reload(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_get_info(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_autocomplete(const goos::Object& form, const goos::Object& rest, Env* env); - Val* compile_add_macro_to_autocomplete(const goos::Object& form, - const goos::Object& rest, - Env* env); + Val* compile_update_macro_metadata(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_load_project(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_make(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_print_debug_compiler_stats(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_gen_docs(const goos::Object& form, const goos::Object& rest, Env* env); // ControlFlow Condition compile_condition(const goos::Object& condition, Env* env, bool invert); @@ -709,5 +709,6 @@ class Compiler { extern const std::unordered_map< std::string, - Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)> + std::pair> g_goal_forms; diff --git a/goalc/compiler/SymbolInfo.h b/goalc/compiler/SymbolInfo.h index 56f4070b15..f042b26317 100644 --- a/goalc/compiler/SymbolInfo.h +++ b/goalc/compiler/SymbolInfo.h @@ -8,6 +8,8 @@ #include "common/util/Assert.h" #include "common/util/Trie.h" +#include "goalc/compiler/Val.h" + /*! * Info about a single symbol, representing one of: * - Global variable @@ -19,6 +21,12 @@ */ class SymbolInfo { public: + struct Metadata { + std::string docstring = ""; + }; + + // TODO - states + // TODO - enums enum class Kind { GLOBAL_VAR, FWD_DECLARED_SYM, @@ -31,11 +39,16 @@ class SymbolInfo { INVALID }; - static SymbolInfo make_global(const std::string& name, const goos::Object& defining_form) { + static SymbolInfo make_global(const std::string& name, + const goos::Object& defining_form, + const std::optional meta = {}) { SymbolInfo info; info.m_kind = Kind::GLOBAL_VAR; info.m_name = name; info.m_def_form = defining_form; + if (meta) { + info.m_meta = *meta; + } return info; } @@ -48,66 +61,101 @@ class SymbolInfo { return info; } - static SymbolInfo make_function(const std::string& name, const goos::Object& defining_form) { + static SymbolInfo make_function(const std::string& name, + const std::vector args, + const goos::Object& defining_form, + const std::optional meta = {}) { SymbolInfo info; info.m_kind = Kind::FUNCTION; info.m_name = name; info.m_def_form = defining_form; + if (meta) { + info.m_meta = *meta; + } + info.m_args = args; return info; } - static SymbolInfo make_type(const std::string& name, const goos::Object& defining_form) { + static SymbolInfo make_type(const std::string& name, + const goos::Object& defining_form, + const std::optional meta = {}) { SymbolInfo info; info.m_kind = Kind::TYPE; info.m_name = name; info.m_def_form = defining_form; + if (meta) { + info.m_meta = *meta; + } return info; } - static SymbolInfo make_constant(const std::string& name, const goos::Object& defining_form) { + static SymbolInfo make_constant(const std::string& name, + const goos::Object& defining_form, + const std::optional meta = {}) { SymbolInfo info; info.m_kind = Kind::CONSTANT; info.m_name = name; info.m_def_form = defining_form; + if (meta) { + info.m_meta = *meta; + } return info; } - static SymbolInfo make_macro(const std::string& name, const goos::Object& defining_form) { + static SymbolInfo make_macro(const std::string& name, + const goos::Object& defining_form, + const std::optional meta = {}) { SymbolInfo info; info.m_kind = Kind::MACRO; info.m_name = name; info.m_def_form = defining_form; + if (meta) { + info.m_meta = *meta; + } return info; } - static SymbolInfo make_builtin(const std::string& name) { + static SymbolInfo make_builtin(const std::string& name, const std::optional meta = {}) { SymbolInfo info; info.m_kind = Kind::LANGUAGE_BUILTIN; info.m_name = name; + if (meta) { + info.m_meta = *meta; + } return info; } static SymbolInfo make_method(const std::string& method_name, - const std::string& type_name, + const std::vector args, + const MethodInfo& method_info, const goos::Object& defining_form) { SymbolInfo info; info.m_kind = Kind::METHOD; info.m_name = method_name; - info.m_type_of_method = type_name; + info.m_method_info = method_info; info.m_def_form = defining_form; + info.m_meta.docstring = + info.m_method_info.docstring.has_value() ? info.m_method_info.docstring.value() : ""; + info.m_args = args; return info; } const std::string& name() const { return m_name; } - const std::string& type() const { return m_type_of_method; } + const MethodInfo& method_info() const { return m_method_info; } Kind kind() const { return m_kind; } const goos::Object& src_form() const { return m_def_form; } + const Metadata& meta() const { return m_meta; } + const std::vector& args() const { return m_args; } private: Kind m_kind = Kind::INVALID; goos::Object m_def_form; std::string m_name; - std::string m_type_of_method; + MethodInfo m_method_info; + Metadata m_meta; + std::vector m_args; + + std::string m_return_type; }; /*! @@ -117,38 +165,55 @@ class SymbolInfo { class SymbolInfoMap { public: SymbolInfoMap() = default; - void add_global(const std::string& name, const goos::Object& defining_form) { - m_map[name]->push_back(SymbolInfo::make_global(name, defining_form)); + void add_global(const std::string& name, + const goos::Object& defining_form, + const std::optional meta = {}) { + m_map[name]->push_back(SymbolInfo::make_global(name, defining_form, meta)); } void add_fwd_dec(const std::string& name, const goos::Object& defining_form) { m_map[name]->push_back(SymbolInfo::make_fwd_declared_sym(name, defining_form)); } - void add_function(const std::string& name, const goos::Object& defining_form) { - m_map[name]->push_back(SymbolInfo::make_function(name, defining_form)); + // The m_symbol_types container stores TypeSpecs -- this does have argument information but not + // the names, which is why they have to be explicitly provided + void add_function(const std::string& name, + const std::vector args, + const goos::Object& defining_form, + const std::optional meta = {}) { + m_map[name]->push_back(SymbolInfo::make_function(name, args, defining_form, meta)); } - void add_type(const std::string& name, const goos::Object& defining_form) { - m_map[name]->push_back(SymbolInfo::make_type(name, defining_form)); + void add_type(const std::string& name, + const goos::Object& defining_form, + const std::optional meta = {}) { + m_map[name]->push_back(SymbolInfo::make_type(name, defining_form, meta)); } - void add_constant(const std::string& name, const goos::Object& defining_form) { - m_map[name]->push_back(SymbolInfo::make_constant(name, defining_form)); + void add_constant(const std::string& name, + const goos::Object& defining_form, + const std::optional meta = {}) { + m_map[name]->push_back(SymbolInfo::make_constant(name, defining_form, meta)); } - void add_macro(const std::string& name, const goos::Object& defining_form) { - m_map[name]->push_back(SymbolInfo::make_macro(name, defining_form)); + void add_macro(const std::string& name, + const goos::Object& defining_form, + const std::optional meta = {}) { + m_map[name]->push_back(SymbolInfo::make_macro(name, defining_form, meta)); } - void add_builtin(const std::string& name) { - m_map[name]->push_back(SymbolInfo::make_builtin(name)); + void add_builtin(const std::string& name, const std::optional meta = {}) { + m_map[name]->push_back(SymbolInfo::make_builtin(name, meta)); } + // The m_symbol_types container stores TypeSpecs -- this does have argument information but not + // the names, which is why they have to be explicitly provided void add_method(const std::string& method_name, - const std::string& type_name, + const std::vector args, + const MethodInfo& method_info, const goos::Object& defining_form) { - m_map[method_name]->push_back(SymbolInfo::make_method(method_name, type_name, defining_form)); + m_map[method_name]->push_back( + SymbolInfo::make_method(method_name, args, method_info, defining_form)); } std::vector* lookup_exact_name(const std::string& name) const { @@ -168,6 +233,17 @@ class SymbolInfoMap { int symbol_count() const { return m_map.size(); } + std::vector get_all_symbols() const { + std::vector info; + auto lookup = m_map.get_all_nodes(); + for (auto& x : lookup) { + for (auto& y : *x) { + info.push_back(y); + } + } + return info; + } + private: Trie> m_map; }; diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 2ff6bd9a71..a9906028c5 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -11,75 +11,76 @@ */ const std::unordered_map< std::string, - Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)> + std::pair> g_goal_forms = { // INLINE ASM - {".nop", &Compiler::compile_nop}, - {".ret", &Compiler::compile_asm_ret}, - {".push", &Compiler::compile_asm_push}, - {".pop", &Compiler::compile_asm_pop}, - {"rlet", &Compiler::compile_rlet}, - {".jr", &Compiler::compile_asm_jr}, - {".sub", &Compiler::compile_asm_sub}, - {".add", &Compiler::compile_asm_add}, - {".load-sym", &Compiler::compile_asm_load_sym}, - {".mov", &Compiler::compile_asm_mov}, + {".nop", {"A simple no-op, does nothing", &Compiler::compile_nop}}, + {".ret", {"", &Compiler::compile_asm_ret}}, + {".push", {"", &Compiler::compile_asm_push}}, + {".pop", {"", &Compiler::compile_asm_pop}}, + {"rlet", {"", &Compiler::compile_rlet}}, + {".jr", {"", &Compiler::compile_asm_jr}}, + {".sub", {"", &Compiler::compile_asm_sub}}, + {".add", {"", &Compiler::compile_asm_add}}, + {".load-sym", {"", &Compiler::compile_asm_load_sym}}, + {".mov", {"", &Compiler::compile_asm_mov}}, // INLINE ASM - VECTOR FLOAT OPERATIONS - {".lvf", &Compiler::compile_asm_lvf}, - {".svf", &Compiler::compile_asm_svf}, - {".mov.vf", &Compiler::compile_asm_mov_vf}, - {".blend.vf", &Compiler::compile_asm_blend_vf}, + {".lvf", {"", &Compiler::compile_asm_lvf}}, + {".svf", {"", &Compiler::compile_asm_svf}}, + {".mov.vf", {"", &Compiler::compile_asm_mov_vf}}, + {".blend.vf", {"", &Compiler::compile_asm_blend_vf}}, - {".nop.vf", &Compiler::compile_asm_nop_vf}, - {".wait.vf", &Compiler::compile_asm_wait_vf}, + {".nop.vf", {"", &Compiler::compile_asm_nop_vf}}, + {".wait.vf", {"", &Compiler::compile_asm_wait_vf}}, - {".xor.vf", &Compiler::compile_asm_xor_vf}, - {".xor.p", &Compiler::compile_asm_xorp}, + {".xor.vf", {"", &Compiler::compile_asm_xor_vf}}, + {".xor.p", {"", &Compiler::compile_asm_xorp}}, - {".max.vf", &Compiler::compile_asm_max_vf}, - {".max.x.vf", &Compiler::compile_asm_max_x_vf}, - {".max.y.vf", &Compiler::compile_asm_max_y_vf}, - {".max.z.vf", &Compiler::compile_asm_max_z_vf}, - {".max.w.vf", &Compiler::compile_asm_max_w_vf}, + {".max.vf", {"", &Compiler::compile_asm_max_vf}}, + {".max.x.vf", {"", &Compiler::compile_asm_max_x_vf}}, + {".max.y.vf", {"", &Compiler::compile_asm_max_y_vf}}, + {".max.z.vf", {"", &Compiler::compile_asm_max_z_vf}}, + {".max.w.vf", {"", &Compiler::compile_asm_max_w_vf}}, - {".min.vf", &Compiler::compile_asm_min_vf}, - {".min.x.vf", &Compiler::compile_asm_min_x_vf}, - {".min.y.vf", &Compiler::compile_asm_min_y_vf}, - {".min.z.vf", &Compiler::compile_asm_min_z_vf}, - {".min.w.vf", &Compiler::compile_asm_min_w_vf}, + {".min.vf", {"", &Compiler::compile_asm_min_vf}}, + {".min.x.vf", {"", &Compiler::compile_asm_min_x_vf}}, + {".min.y.vf", {"", &Compiler::compile_asm_min_y_vf}}, + {".min.z.vf", {"", &Compiler::compile_asm_min_z_vf}}, + {".min.w.vf", {"", &Compiler::compile_asm_min_w_vf}}, - {".add.vf", &Compiler::compile_asm_add_vf}, - {".add.x.vf", &Compiler::compile_asm_add_x_vf}, - {".add.y.vf", &Compiler::compile_asm_add_y_vf}, - {".add.z.vf", &Compiler::compile_asm_add_z_vf}, - {".add.w.vf", &Compiler::compile_asm_add_w_vf}, + {".add.vf", {"", &Compiler::compile_asm_add_vf}}, + {".add.x.vf", {"", &Compiler::compile_asm_add_x_vf}}, + {".add.y.vf", {"", &Compiler::compile_asm_add_y_vf}}, + {".add.z.vf", {"", &Compiler::compile_asm_add_z_vf}}, + {".add.w.vf", {"", &Compiler::compile_asm_add_w_vf}}, - {".sub.vf", &Compiler::compile_asm_sub_vf}, - {".sub.x.vf", &Compiler::compile_asm_sub_x_vf}, - {".sub.y.vf", &Compiler::compile_asm_sub_y_vf}, - {".sub.z.vf", &Compiler::compile_asm_sub_z_vf}, - {".sub.w.vf", &Compiler::compile_asm_sub_w_vf}, + {".sub.vf", {"", &Compiler::compile_asm_sub_vf}}, + {".sub.x.vf", {"", &Compiler::compile_asm_sub_x_vf}}, + {".sub.y.vf", {"", &Compiler::compile_asm_sub_y_vf}}, + {".sub.z.vf", {"", &Compiler::compile_asm_sub_z_vf}}, + {".sub.w.vf", {"", &Compiler::compile_asm_sub_w_vf}}, - {".mul.vf", &Compiler::compile_asm_mul_vf}, - {".mul.x.vf", &Compiler::compile_asm_mul_x_vf}, - {".mul.y.vf", &Compiler::compile_asm_mul_y_vf}, - {".mul.z.vf", &Compiler::compile_asm_mul_z_vf}, - {".mul.w.vf", &Compiler::compile_asm_mul_w_vf}, + {".mul.vf", {"", &Compiler::compile_asm_mul_vf}}, + {".mul.x.vf", {"", &Compiler::compile_asm_mul_x_vf}}, + {".mul.y.vf", {"", &Compiler::compile_asm_mul_y_vf}}, + {".mul.z.vf", {"", &Compiler::compile_asm_mul_z_vf}}, + {".mul.w.vf", {"", &Compiler::compile_asm_mul_w_vf}}, - {".add.mul.vf", &Compiler::compile_asm_mul_add_vf}, - {".add.mul.x.vf", &Compiler::compile_asm_mul_add_x_vf}, - {".add.mul.y.vf", &Compiler::compile_asm_mul_add_y_vf}, - {".add.mul.z.vf", &Compiler::compile_asm_mul_add_z_vf}, - {".add.mul.w.vf", &Compiler::compile_asm_mul_add_w_vf}, + {".add.mul.vf", {"", &Compiler::compile_asm_mul_add_vf}}, + {".add.mul.x.vf", {"", &Compiler::compile_asm_mul_add_x_vf}}, + {".add.mul.y.vf", {"", &Compiler::compile_asm_mul_add_y_vf}}, + {".add.mul.z.vf", {"", &Compiler::compile_asm_mul_add_z_vf}}, + {".add.mul.w.vf", {"", &Compiler::compile_asm_mul_add_w_vf}}, - {".sub.mul.vf", &Compiler::compile_asm_mul_sub_vf}, - {".sub.mul.x.vf", &Compiler::compile_asm_mul_sub_x_vf}, - {".sub.mul.y.vf", &Compiler::compile_asm_mul_sub_y_vf}, - {".sub.mul.z.vf", &Compiler::compile_asm_mul_sub_z_vf}, - {".sub.mul.w.vf", &Compiler::compile_asm_mul_sub_w_vf}, + {".sub.mul.vf", {"", &Compiler::compile_asm_mul_sub_vf}}, + {".sub.mul.x.vf", {"", &Compiler::compile_asm_mul_sub_x_vf}}, + {".sub.mul.y.vf", {"", &Compiler::compile_asm_mul_sub_y_vf}}, + {".sub.mul.z.vf", {"", &Compiler::compile_asm_mul_sub_z_vf}}, + {".sub.mul.w.vf", {"", &Compiler::compile_asm_mul_sub_w_vf}}, - {".abs.vf", &Compiler::compile_asm_abs_vf}, + {".abs.vf", {"", &Compiler::compile_asm_abs_vf}}, // NOTE - to compute the Outer Product with the VU, two back to back instructions were used // involving the ACC // However, we can be better than that and just provide a single instruction @@ -87,183 +88,184 @@ const std::unordered_map< // operations, we'll need to implement them separately. // // ...and they did - {".outer.product.vf", &Compiler::compile_asm_outer_product_vf}, - {".outer.product.a.vf", &Compiler::compile_asm_outer_product_a_vf}, - {".outer.product.b.vf", &Compiler::compile_asm_outer_product_b_vf}, + {".outer.product.vf", {"", &Compiler::compile_asm_outer_product_vf}}, + {".outer.product.a.vf", {"", &Compiler::compile_asm_outer_product_a_vf}}, + {".outer.product.b.vf", {"", &Compiler::compile_asm_outer_product_b_vf}}, - {".div.vf", &Compiler::compile_asm_div_vf}, - {".sqrt.vf", &Compiler::compile_asm_sqrt_vf}, - {".isqrt.vf", &Compiler::compile_asm_inv_sqrt_vf}, - {".itof.vf", &Compiler::compile_asm_itof_vf}, - {".ftoi.vf", &Compiler::compile_asm_ftoi_vf}, + {".div.vf", {"", &Compiler::compile_asm_div_vf}}, + {".sqrt.vf", {"", &Compiler::compile_asm_sqrt_vf}}, + {".isqrt.vf", {"", &Compiler::compile_asm_inv_sqrt_vf}}, + {".itof.vf", {"", &Compiler::compile_asm_itof_vf}}, + {".ftoi.vf", {"", &Compiler::compile_asm_ftoi_vf}}, - {".pw.sll", &Compiler::compile_asm_pw_sll}, - {".pw.srl", &Compiler::compile_asm_pw_srl}, - {".pw.sra", &Compiler::compile_asm_pw_sra}, + {".pw.sll", {"", &Compiler::compile_asm_pw_sll}}, + {".pw.srl", {"", &Compiler::compile_asm_pw_srl}}, + {".pw.sra", {"", &Compiler::compile_asm_pw_sra}}, - {".por", &Compiler::compile_asm_por}, - {".pxor", &Compiler::compile_asm_pxor}, - {".pnor", &Compiler::compile_asm_pnor}, - {".pand", &Compiler::compile_asm_pand}, + {".por", {"", &Compiler::compile_asm_por}}, + {".pxor", {"", &Compiler::compile_asm_pxor}}, + {".pnor", {"", &Compiler::compile_asm_pnor}}, + {".pand", {"", &Compiler::compile_asm_pand}}, - {".pceqb", &Compiler::compile_asm_pceqb}, - {".pceqh", &Compiler::compile_asm_pceqh}, - {".pceqw", &Compiler::compile_asm_pceqw}, + {".pceqb", {"", &Compiler::compile_asm_pceqb}}, + {".pceqh", {"", &Compiler::compile_asm_pceqh}}, + {".pceqw", {"", &Compiler::compile_asm_pceqw}}, - {".pcgtb", &Compiler::compile_asm_pcgtb}, - {".pcgth", &Compiler::compile_asm_pcgth}, - {".pcgtw", &Compiler::compile_asm_pcgtw}, + {".pcgtb", {"", &Compiler::compile_asm_pcgtb}}, + {".pcgth", {"", &Compiler::compile_asm_pcgth}}, + {".pcgtw", {"", &Compiler::compile_asm_pcgtw}}, - {".pextlb", &Compiler::compile_asm_pextlb}, - {".pextlh", &Compiler::compile_asm_pextlh}, - {".pextlw", &Compiler::compile_asm_pextlw}, + {".pextlb", {"", &Compiler::compile_asm_pextlb}}, + {".pextlh", {"", &Compiler::compile_asm_pextlh}}, + {".pextlw", {"", &Compiler::compile_asm_pextlw}}, - {".pextub", &Compiler::compile_asm_pextub}, - {".pextuh", &Compiler::compile_asm_pextuh}, - {".pextuw", &Compiler::compile_asm_pextuw}, + {".pextub", {"", &Compiler::compile_asm_pextub}}, + {".pextuh", {"", &Compiler::compile_asm_pextuh}}, + {".pextuw", {"", &Compiler::compile_asm_pextuw}}, - {".pcpyld", &Compiler::compile_asm_pcpyld}, - {".pcpyud", &Compiler::compile_asm_pcpyud}, - {".pceqw", &Compiler::compile_asm_pceqw}, - {".ppach", &Compiler::compile_asm_ppach}, - {".ppacb", &Compiler::compile_asm_ppacb}, - {".psubw", &Compiler::compile_asm_psubw}, + {".pcpyld", {"", &Compiler::compile_asm_pcpyld}}, + {".pcpyud", {"", &Compiler::compile_asm_pcpyud}}, + {".pceqw", {"", &Compiler::compile_asm_pceqw}}, + {".ppach", {"", &Compiler::compile_asm_ppach}}, + {".ppacb", {"", &Compiler::compile_asm_ppacb}}, + {".psubw", {"", &Compiler::compile_asm_psubw}}, // BLOCK FORMS - {"top-level", &Compiler::compile_top_level}, - {"begin", &Compiler::compile_begin}, - {"block", &Compiler::compile_block}, - {"return-from", &Compiler::compile_return_from}, - {"label", &Compiler::compile_label}, - {"goto", &Compiler::compile_goto}, - {"nop!", &Compiler::compile_nop}, + {"top-level", {"", &Compiler::compile_top_level}}, + {"begin", {"", &Compiler::compile_begin}}, + {"block", {"", &Compiler::compile_block}}, + {"return-from", {"", &Compiler::compile_return_from}}, + {"label", {"", &Compiler::compile_label}}, + {"goto", {"", &Compiler::compile_goto}}, + {"nop!", {"", &Compiler::compile_nop}}, // COMPILER CONTROL - {"repl-help", &Compiler::compile_repl_help}, - {"repl-keybinds", &Compiler::compile_repl_keybinds}, - {":clear", &Compiler::compile_repl_clear_screen}, - {"gs", &Compiler::compile_gs}, - {":exit", &Compiler::compile_exit}, - {"asm-file", &Compiler::compile_asm_file}, - {"asm-data-file", &Compiler::compile_asm_data_file}, - {"asm-text-file", &Compiler::compile_asm_text_file}, - {"listen-to-target", &Compiler::compile_listen_to_target}, - {"reset-target", &Compiler::compile_reset_target}, - {":status", &Compiler::compile_poke}, - {"in-package", &Compiler::compile_in_package}, - {"reload", &Compiler::compile_reload}, - {"get-info", &Compiler::compile_get_info}, - {"autocomplete", &Compiler::compile_autocomplete}, - {"add-macro-to-autocomplete", &Compiler::compile_add_macro_to_autocomplete}, - {"load-project", &Compiler::compile_load_project}, - {"make", &Compiler::compile_make}, - {"print-debug-compiler-stats", &Compiler::compile_print_debug_compiler_stats}, + {"repl-help", {"", &Compiler::compile_repl_help}}, + {"repl-keybinds", {"", &Compiler::compile_repl_keybinds}}, + {":clear", {"", &Compiler::compile_repl_clear_screen}}, + {"gs", {"", &Compiler::compile_gs}}, + {":exit", {"", &Compiler::compile_exit}}, + {"asm-file", {"", &Compiler::compile_asm_file}}, + {"asm-data-file", {"", &Compiler::compile_asm_data_file}}, + {"asm-text-file", {"", &Compiler::compile_asm_text_file}}, + {"listen-to-target", {"", &Compiler::compile_listen_to_target}}, + {"reset-target", {"", &Compiler::compile_reset_target}}, + {":status", {"", &Compiler::compile_poke}}, + {"in-package", {"", &Compiler::compile_in_package}}, + {"reload", {"", &Compiler::compile_reload}}, + {"get-info", {"", &Compiler::compile_get_info}}, + {"autocomplete", {"", &Compiler::compile_autocomplete}}, + {"update-macro-metadata", {"", &Compiler::compile_update_macro_metadata}}, + {"load-project", {"", &Compiler::compile_load_project}}, + {"make", {"", &Compiler::compile_make}}, + {"print-debug-compiler-stats", {"", &Compiler::compile_print_debug_compiler_stats}}, + {"gen-docs", {"", &Compiler::compile_gen_docs}}, // CONDITIONAL COMPILATION - {"#cond", &Compiler::compile_gscond}, - {"defglobalconstant", &Compiler::compile_defglobalconstant}, - {"seval", &Compiler::compile_seval}, + {"#cond", {"", &Compiler::compile_gscond}}, + {"defglobalconstant", {"", &Compiler::compile_defglobalconstant}}, + {"seval", {"", &Compiler::compile_seval}}, // CONTROL FLOW - {"cond", &Compiler::compile_cond}, - {"when-goto", &Compiler::compile_when_goto}, - {"and", &Compiler::compile_and_or}, - {"or", &Compiler::compile_and_or}, + {"cond", {"", &Compiler::compile_cond}}, + {"when-goto", {"", &Compiler::compile_when_goto}}, + {"and", {"", &Compiler::compile_and_or}}, + {"or", {"", &Compiler::compile_and_or}}, // DEFINITION - {"define", &Compiler::compile_define}, - {"define-extern", &Compiler::compile_define_extern}, - {"set!", &Compiler::compile_set}, + {"define", {"", &Compiler::compile_define}}, + {"define-extern", {"", &Compiler::compile_define_extern}}, + {"set!", {"", &Compiler::compile_set}}, // DEBUGGING - {"dbs", &Compiler::compile_dbs}, - {"dbg", &Compiler::compile_dbg}, - {"dbgc", &Compiler::compile_dbg_and_continue}, - {":cont", &Compiler::compile_cont}, - {":stop", &Compiler::compile_stop}, - {":break", &Compiler::compile_break}, - {":dump-all-mem", &Compiler::compile_dump_all}, - {":pm", &Compiler::compile_pm}, - {":di", &Compiler::compile_di}, - {":disasm", &Compiler::compile_disasm}, - {":bp", &Compiler::compile_bp}, - {":ubp", &Compiler::compile_ubp}, - {":sym-name", &Compiler::compile_d_sym_name}, + {"dbs", {"", &Compiler::compile_dbs}}, + {"dbg", {"", &Compiler::compile_dbg}}, + {"dbgc", {"", &Compiler::compile_dbg_and_continue}}, + {":cont", {"", &Compiler::compile_cont}}, + {":stop", {"", &Compiler::compile_stop}}, + {":break", {"", &Compiler::compile_break}}, + {":dump-all-mem", {"", &Compiler::compile_dump_all}}, + {":pm", {"", &Compiler::compile_pm}}, + {":di", {"", &Compiler::compile_di}}, + {":disasm", {"", &Compiler::compile_disasm}}, + {":bp", {"", &Compiler::compile_bp}}, + {":ubp", {"", &Compiler::compile_ubp}}, + {":sym-name", {"", &Compiler::compile_d_sym_name}}, // TYPE - {"deftype", &Compiler::compile_deftype}, - {"defmethod", &Compiler::compile_defmethod}, - {"defenum", &Compiler::compile_defenum}, - {"->", &Compiler::compile_deref}, - {"&", &Compiler::compile_addr_of}, - {"the-as", &Compiler::compile_the_as}, - {"the", &Compiler::compile_the}, - {"print-type", &Compiler::compile_print_type}, - {"new", &Compiler::compile_new}, - {"car", &Compiler::compile_car}, - {"cdr", &Compiler::compile_cdr}, - {"method-of-type", &Compiler::compile_method_of_type}, - {"method-of-object", &Compiler::compile_method_of_object}, - {"declare-type", &Compiler::compile_declare_type}, - {"none", &Compiler::compile_none}, - {"size-of", &Compiler::compile_size_of}, - {"psize-of", &Compiler::compile_psize_of}, + {"deftype", {"", &Compiler::compile_deftype}}, + {"defmethod", {"", &Compiler::compile_defmethod}}, + {"defenum", {"", &Compiler::compile_defenum}}, + {"->", {"", &Compiler::compile_deref}}, + {"&", {"", &Compiler::compile_addr_of}}, + {"the-as", {"", &Compiler::compile_the_as}}, + {"the", {"", &Compiler::compile_the}}, + {"print-type", {"", &Compiler::compile_print_type}}, + {"new", {"", &Compiler::compile_new}}, + {"car", {"", &Compiler::compile_car}}, + {"cdr", {"", &Compiler::compile_cdr}}, + {"method-of-type", {"", &Compiler::compile_method_of_type}}, + {"method-of-object", {"", &Compiler::compile_method_of_object}}, + {"declare-type", {"", &Compiler::compile_declare_type}}, + {"none", {"", &Compiler::compile_none}}, + {"size-of", {"", &Compiler::compile_size_of}}, + {"psize-of", {"", &Compiler::compile_psize_of}}, // LAMBDA - {"lambda", &Compiler::compile_lambda}, - {"declare", &Compiler::compile_declare}, - {"inline", &Compiler::compile_inline}, - {"local-vars", &Compiler::compile_local_vars}, - {"declare-file", &Compiler::compile_declare_file}, - // {"with-inline", &Compiler::compile_with_inline}, - // {"get-ra-ptr", &Compiler::compile_get_ra_ptr}, + {"lambda", {"", &Compiler::compile_lambda}}, + {"declare", {"", &Compiler::compile_declare}}, + {"inline", {"", &Compiler::compile_inline}}, + {"local-vars", {"", &Compiler::compile_local_vars}}, + {"declare-file", {"", &Compiler::compile_declare_file}}, + // {"with-inline", {"", &Compiler::compile_with_inline}}, + // {"get-ra-ptr", {"", &Compiler::compile_get_ra_ptr}}, // MACRO - {"quote", &Compiler::compile_quote}, - {"mlet", &Compiler::compile_mlet}, - {"defconstant", &Compiler::compile_defconstant}, + {"quote", {"", &Compiler::compile_quote}}, + {"mlet", {"", &Compiler::compile_mlet}}, + {"defconstant", {"", &Compiler::compile_defconstant}}, // OBJECT - // {"current-method-type", &Compiler::compile_current_method_type}, + // {"current-method-type", {"", &Compiler::compile_current_method_type}}, // MATH - {"+", &Compiler::compile_add}, - {"-", &Compiler::compile_sub}, - {"*", &Compiler::compile_mul}, - {"imul64", &Compiler::compile_imul64}, - {"/", &Compiler::compile_div}, - {"shl", &Compiler::compile_shl}, - {"shr", &Compiler::compile_shr}, - {"sar", &Compiler::compile_sar}, - {"mod", &Compiler::compile_mod}, - {"logior", &Compiler::compile_logior}, - {"logxor", &Compiler::compile_logxor}, - {"logand", &Compiler::compile_logand}, - {"lognot", &Compiler::compile_lognot}, - {"=", &Compiler::compile_condition_as_bool}, - {"!=", &Compiler::compile_condition_as_bool}, - {"eq?", &Compiler::compile_condition_as_bool}, - {"neq?", &Compiler::compile_condition_as_bool}, - {"not", &Compiler::compile_condition_as_bool}, - {"<=", &Compiler::compile_condition_as_bool}, - {">=", &Compiler::compile_condition_as_bool}, - {"<", &Compiler::compile_condition_as_bool}, - {">", &Compiler::compile_condition_as_bool}, - {"&+", &Compiler::compile_pointer_add}, - {"fmax", &Compiler::compile_fmax}, - {"fmin", &Compiler::compile_fmin}, - {"sqrtf-no-fabs", &Compiler::compile_sqrtf}, + {"+", {"", &Compiler::compile_add}}, + {"-", {"", &Compiler::compile_sub}}, + {"*", {"", &Compiler::compile_mul}}, + {"imul64", {"", &Compiler::compile_imul64}}, + {"/", {"", &Compiler::compile_div}}, + {"shl", {"", &Compiler::compile_shl}}, + {"shr", {"", &Compiler::compile_shr}}, + {"sar", {"", &Compiler::compile_sar}}, + {"mod", {"", &Compiler::compile_mod}}, + {"logior", {"", &Compiler::compile_logior}}, + {"logxor", {"", &Compiler::compile_logxor}}, + {"logand", {"", &Compiler::compile_logand}}, + {"lognot", {"", &Compiler::compile_lognot}}, + {"=", {"", &Compiler::compile_condition_as_bool}}, + {"!=", {"", &Compiler::compile_condition_as_bool}}, + {"eq?", {"", &Compiler::compile_condition_as_bool}}, + {"neq?", {"", &Compiler::compile_condition_as_bool}}, + {"not", {"", &Compiler::compile_condition_as_bool}}, + {"<=", {"", &Compiler::compile_condition_as_bool}}, + {">=", {"", &Compiler::compile_condition_as_bool}}, + {"<", {"", &Compiler::compile_condition_as_bool}}, + {">", {"", &Compiler::compile_condition_as_bool}}, + {"&+", {"", &Compiler::compile_pointer_add}}, + {"fmax", {"", &Compiler::compile_fmax}}, + {"fmin", {"", &Compiler::compile_fmin}}, + {"sqrtf-no-fabs", {"", &Compiler::compile_sqrtf}}, // BUILDER (build-dgo/build-cgo?) - {"build-dgos", &Compiler::compile_build_dgo}, + {"build-dgos", {"", &Compiler::compile_build_dgo}}, // UTIL - {"set-config!", &Compiler::compile_set_config}, + {"set-config!", {"", &Compiler::compile_set_config}}, // STATE - {"define-state-hook", &Compiler::compile_define_state_hook}, - {"go-hook", &Compiler::compile_go_hook}, - {"define-virtual-state-hook", &Compiler::compile_define_virtual_state_hook}, + {"define-state-hook", {"", &Compiler::compile_define_state_hook}}, + {"go-hook", {"", &Compiler::compile_go_hook}}, + {"define-virtual-state-hook", {"", &Compiler::compile_define_virtual_state_hook}}, }; Val* Compiler::compile_no_const_prop(const goos::Object& code, Env* env) { @@ -301,11 +303,11 @@ Val* Compiler::compile(const goos::Object& code, Env* env) { */ Val* Compiler::compile_pair(const goos::Object& code, Env* env) { auto pair = code.as_pair(); - auto head = pair->car; - auto rest = pair->cdr; + auto& head = pair->car; if (head.is_symbol()) { auto head_sym = head.as_symbol(); + auto& rest = pair->cdr; // first try as a macro goos::Object macro_obj; if (try_getting_macro_from_goos(head, ¯o_obj)) { @@ -315,7 +317,8 @@ Val* Compiler::compile_pair(const goos::Object& code, Env* env) { // next try as a goal compiler form auto kv_gfs = g_goal_forms.find(head_sym->name); if (kv_gfs != g_goal_forms.end()) { - return ((*this).*(kv_gfs->second))(code, rest, env); + auto& [docstring, func] = kv_gfs->second; + return ((*this).*(func))(code, rest, env); } // next try as an enum diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 8b50bed433..9dda8e8c24 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -14,6 +14,8 @@ #include "goalc/compiler/Compiler.h" #include "goalc/compiler/IR.h" +#include "goalc/compiler/SymbolInfo.h" +#include "goalc/compiler/docs/DocTypes.h" #include "goalc/data_compiler/dir_tpages.h" #include "goalc/data_compiler/game_count.h" #include "goalc/data_compiler/game_text_common.h" @@ -355,7 +357,8 @@ std::string Compiler::make_symbol_info_description(const SymbolInfo& info) { case SymbolInfo::Kind::LANGUAGE_BUILTIN: return fmt::format("[Built-in Form] {}\n", info.name()); case SymbolInfo::Kind::METHOD: - return fmt::format("[Method] Type: {} Method Name: {} Defined: {}", info.type(), info.name(), + return fmt::format("[Method] Type: {} Method Name: {} Defined: {}", + info.method_info().defined_in_type, info.name(), m_goos.reader.db.get_info_for(info.src_form())); case SymbolInfo::Kind::TYPE: return fmt::format("[Type] Name: {} Defined: {}", info.name(), @@ -550,13 +553,28 @@ Val* Compiler::compile_autocomplete(const goos::Object& form, const goos::Object return get_none(); } -Val* Compiler::compile_add_macro_to_autocomplete(const goos::Object& form, - const goos::Object& rest, - Env* env) { +Val* Compiler::compile_update_macro_metadata(const goos::Object& form, + const goos::Object& rest, + Env* env) { (void)env; auto args = get_va(form, rest); - va_check(form, args, {goos::ObjectType::SYMBOL}, {}); - m_symbol_info.add_macro(args.unnamed.at(0).as_symbol()->name, form); + // We have to manually check the args here, as an empty list is considered something distinct from + // a pair + if (args.unnamed.size() != 3 || args.unnamed.at(0).type != goos::ObjectType::SYMBOL || + args.unnamed.at(1).type != goos::ObjectType::STRING || + (args.unnamed.at(2).type != goos::ObjectType::PAIR && + args.unnamed.at(2).type != goos::ObjectType::EMPTY_LIST)) { + throw_compiler_error(form, "Invalid arguments provided to `update-macro-metadata"); + } + + auto& name = args.unnamed.at(0).as_symbol()->name; + + auto arg_spec = m_goos.parse_arg_spec(form, args.unnamed.at(2)); + m_macro_specs[name] = arg_spec; + + SymbolInfo::Metadata sym_meta; + sym_meta.docstring = args.unnamed.at(1).as_string()->data; + m_symbol_info.add_macro(name, form, sym_meta); return get_none(); } @@ -616,3 +634,185 @@ Val* Compiler::compile_print_debug_compiler_stats(const goos::Object& form, return get_none(); } + +Val* Compiler::compile_gen_docs(const goos::Object& form, const goos::Object& rest, Env*) { + auto args = get_va(form, rest); + va_check(form, args, {goos::ObjectType::STRING}, {}); + + const auto& doc_path = fs::path(args.unnamed.at(0).as_string()->data); + lg::info("Saving docs to: {}", doc_path.string()); + + const auto symbols = m_symbol_info.get_all_symbols(); + + std::unordered_map all_symbols; + std::unordered_map file_docs; + + lg::info("Processing {} symbols...", symbols.size()); + int count = 0; + for (const auto& sym_info : symbols) { + count++; + if (count % 100 == 0 || count == symbols.size()) { + lg::info("Processing [{}/{}] symbols...", count, symbols.size()); + } + std::optional def_loc; + const auto& goos_info = m_goos.reader.db.get_short_info_for(sym_info.src_form()); + if (goos_info) { + Docs::DefinitionLocation new_def_loc; + new_def_loc.filename = file_util::convert_to_unix_path_separators(file_util::split_path_at( + goos_info->filename, {"goal_src", version_to_game_name(m_version)})); + new_def_loc.line_idx = goos_info->line_idx_to_display; + new_def_loc.char_idx = goos_info->pos_in_line; + def_loc = new_def_loc; + } + + Docs::SymbolDocumentation sym_doc; + sym_doc.name = sym_info.name(); + sym_doc.description = sym_info.meta().docstring; + sym_doc.kind = sym_info.kind(); + sym_doc.def_location = def_loc; + + if (all_symbols.count(sym_info.name()) > 1) { + lg::error("A symbol was defined twice, how did this happen? {}", sym_info.name()); + } else { + all_symbols.emplace(sym_info.name(), sym_doc); + } + + Docs::FileDocumentation file_doc; + std::string file_doc_key; + if (!goos_info) { + file_doc_key = "unknown"; + } else { + file_doc_key = file_util::convert_to_unix_path_separators( + file_util::split_path_at(goos_info->filename, {"goal_src"})); + } + + if (file_docs.count(file_doc_key) != 0) { + file_doc = file_docs.at(file_doc_key); + } else { + file_doc = Docs::FileDocumentation(); + } + + // TODO - states / enums / built-ins + if (sym_info.kind() == SymbolInfo::Kind::GLOBAL_VAR || + sym_info.kind() == SymbolInfo::Kind::CONSTANT) { + Docs::VariableDocumentation var; + var.name = sym_info.name(); + var.description = sym_info.meta().docstring; + if (sym_info.kind() == SymbolInfo::Kind::CONSTANT) { + var.type = "unknown"; // Unfortunately, constants are not properly typed + } else { + var.type = m_symbol_types.at(var.name).base_type(); + } + var.def_location = def_loc; + if (sym_info.kind() == SymbolInfo::Kind::GLOBAL_VAR) { + file_doc.global_vars.push_back(var); + } else { + file_doc.constants.push_back(var); + } + } else if (sym_info.kind() == SymbolInfo::Kind::FUNCTION) { + Docs::FunctionDocumentation func; + func.name = sym_info.name(); + func.description = sym_info.meta().docstring; + func.def_location = def_loc; + func.args = Docs::get_args_from_docstring(sym_info.args(), func.description); + // The last arg in the typespec is the return type + const auto& func_type = m_symbol_types.at(func.name); + func.return_type = func_type.last_arg().base_type(); + file_doc.functions.push_back(func); + } else if (sym_info.kind() == SymbolInfo::Kind::TYPE) { + Docs::TypeDocumentation type; + type.name = sym_info.name(); + type.description = sym_info.meta().docstring; + type.def_location = def_loc; + const auto& type_info = m_ts.lookup_type(type.name); + type.parent_type = type_info->get_parent(); + type.size = type_info->get_size_in_memory(); + type.method_count = type_info->get_methods_defined_for_type().size(); + if (m_ts.typecheck_and_throw(m_ts.make_typespec("structure"), m_ts.make_typespec(type.name), + "", false, false, false)) { + auto struct_info = dynamic_cast(type_info); + for (const auto& field : struct_info->fields()) { + Docs::FieldDocumentation field_doc; + field_doc.name = field.name(); + field_doc.description = ""; + field_doc.type = field.type().base_type(); + field_doc.is_array = field.is_array(); + field_doc.is_inline = field.is_inline(); + field_doc.is_dynamic = field.is_dynamic(); + type.fields.push_back(field_doc); + } + } + for (const auto& method : type_info->get_methods_defined_for_type()) { + // Check to see if it's a state + if (m_ts.typecheck_and_throw(m_ts.make_typespec("state"), method.type, "", false, false, + false)) { + Docs::TypeStateDocumentation state_doc; + state_doc.id = method.id; + state_doc.is_virtual = true; + state_doc.name = method.name; + type.states.push_back(state_doc); + } else { + Docs::TypeMethodDocumentation method_doc; + method_doc.id = method.id; + method_doc.name = method.name; + method_doc.is_override = method.overrides_parent; + type.methods.push_back(method_doc); + } + } + for (const auto& [state_name, state_info] : type_info->get_states_declared_for_type()) { + Docs::TypeStateDocumentation state_doc; + state_doc.name = state_name; + state_doc.is_virtual = false; + type.states.push_back(state_doc); + } + file_doc.types.push_back(type); + } else if (sym_info.kind() == SymbolInfo::Kind::MACRO) { + Docs::MacroDocumentation macro_doc; + macro_doc.name = sym_info.name(); + macro_doc.description = sym_info.meta().docstring; + macro_doc.def_location = def_loc; + const auto& arg_spec = m_macro_specs[macro_doc.name]; + for (const auto& arg : arg_spec.unnamed) { + macro_doc.args.push_back(arg); + } + for (const auto& arg : arg_spec.named) { + std::optional def_value; + if (arg.second.has_default) { + def_value = arg.second.default_value.print(); + } + macro_doc.kwargs.push_back({arg.first, def_value}); + } + if (!arg_spec.rest.empty()) { + macro_doc.variadic_arg = arg_spec.rest; + } + file_doc.macros.push_back(macro_doc); + } else if (sym_info.kind() == SymbolInfo::Kind::METHOD) { + Docs::MethodDocumentation method_doc; + method_doc.name = sym_info.name(); + method_doc.description = sym_info.meta().docstring; + method_doc.def_location = def_loc; + const auto& method_info = sym_info.method_info(); + method_doc.id = method_info.id; + method_doc.type = sym_info.method_info().defined_in_type; + method_doc.is_override = method_info.overrides_parent; + method_doc.args = Docs::get_args_from_docstring(sym_info.args(), method_doc.description); + // The last arg in the typespec is the return type + const auto& method_type = method_info.type; + method_doc.return_type = method_type.last_arg().base_type(); + method_doc.is_builtin = method_doc.id <= 9; + file_doc.methods.push_back(method_doc); + } + file_docs[file_doc_key] = file_doc; + } + + json symbol_map_data(all_symbols); + file_util::write_text_file( + doc_path / fmt::format("{}-symbol-map.json", version_to_game_name(m_version)), + symbol_map_data.dump()); + json file_docs_data(file_docs); + file_util::write_text_file( + doc_path / fmt::format("{}-file-docs.json", version_to_game_name(m_version)), + file_docs_data.dump()); + + return get_none(); +} diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp index 4a0da76713..21013d0ba3 100644 --- a/goalc/compiler/compilation/Define.cpp +++ b/goalc/compiler/compilation/Define.cpp @@ -11,10 +11,10 @@ */ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); + SymbolInfo::Metadata sym_meta; // Grab the docstring (if it's there) and then rip it out so we can do the normal validation if (args.unnamed.size() == 3 && args.unnamed.at(1).is_string()) { - // TODO - docstring - actually use it! - // std::string docstring = args.unnamed.at(1).as_string()->data; + sym_meta.docstring = args.unnamed.at(1).as_string()->data; args.unnamed.erase(args.unnamed.begin() + 1); } @@ -44,7 +44,14 @@ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest if ((as_lambda->func && as_lambda->func->settings.allow_inline) || !as_lambda->func) { m_inlineable_functions[sym.as_symbol()] = as_lambda; } - m_symbol_info.add_function(symbol_string(sym), form); + // Most defines come via macro invokations, we want the TRUE defining form location + // if we can get it + if (env->macro_expand_env()) { + m_symbol_info.add_function(symbol_string(sym), as_lambda->lambda.params, + env->macro_expand_env()->root_form(), sym_meta); + } else { + m_symbol_info.add_function(symbol_string(sym), as_lambda->lambda.params, form, sym_meta); + } } if (!sym_val->settable()) { @@ -66,7 +73,7 @@ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest } } - m_symbol_info.add_global(symbol_string(sym), form); + m_symbol_info.add_global(symbol_string(sym), form, sym_meta); env->emit(form, std::make_unique(sym_val, in_gpr)); return in_gpr; diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index f324b77b18..edde94ac3a 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -44,7 +44,9 @@ Val* Compiler::compile_goos_macro(const goos::Object& o, auto compile_env_for_macro = env->function_env()->alloc_env(env, name.as_symbol(), macro->body, o); try { - return compile(goos_result, compile_env_for_macro); + const auto& compile_result = compile(goos_result, compile_env_for_macro); + m_macro_specs.emplace(macro->name, macro->args); + return compile_result; } catch (CompilerException& ce) { if (ce.print_err_stack) { bool good_info = false; @@ -168,13 +170,14 @@ Val* Compiler::compile_define_constant(const goos::Object& form, rest = &pair_cdr(*rest); // check for potential docstring - // TODO - docstring - actually do something with this! + SymbolInfo::Metadata sym_meta; if (rest->is_pair() && pair_car(*rest).is_string() && !pair_cdr(*rest).is_empty_list()) { - // std::string docstring = pair_car(*rest).as_string()->data; + std::string docstring = pair_car(*rest).as_string()->data; + sym_meta.docstring = docstring; rest = &pair_cdr(*rest); } - auto value = pair_car(*rest); + auto& value = pair_car(*rest); rest = &rest->as_pair()->cdr; if (!rest->is_empty_list()) { @@ -203,7 +206,9 @@ Val* Compiler::compile_define_constant(const goos::Object& form, m_goos.global_environment.as_env()->vars[sym] = value; } - m_symbol_info.add_constant(sym->name, form); + // TODO - eventually, it'd be nice if global constants were properly typed + // and this information was propagated + m_symbol_info.add_constant(sym->name, form, sym_meta); return get_none(); } diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index 78d84db984..616de29829 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -612,15 +612,13 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _ } place->set_type(lambda_ts); - m_symbol_info.add_method(symbol_string(method_name), symbol_string(type_name), form); - - // TODO! auto info = m_ts.define_method(symbol_string(type_name), symbol_string(method_name), lambda_ts, {}); auto type_obj = compile_get_symbol_value(form, symbol_string(type_name), env)->to_gpr(form, env); auto id_val = compile_integer(info.id, env)->to_gpr(form, env); auto method_val = place->to_gpr(form, env); auto method_set_val = compile_get_symbol_value(form, "method-set!", env)->to_gpr(form, env); + m_symbol_info.add_method(symbol_string(method_name), lambda.params, info, form); return compile_real_function_call(form, method_set_val, {type_obj, id_val, method_val}, env); } diff --git a/goalc/compiler/docs/DocTypes.cpp b/goalc/compiler/docs/DocTypes.cpp new file mode 100644 index 0000000000..419a25448c --- /dev/null +++ b/goalc/compiler/docs/DocTypes.cpp @@ -0,0 +1,184 @@ +#include "DocTypes.h" + +#include "common/log/log.h" +#include "common/util/string_util.h" + +namespace Docs { + +void to_json(json& j, const DefinitionLocation& obj) { + j = json{{"filename", obj.filename}, {"line_idx", obj.line_idx}, {"char_idx", obj.char_idx}}; +} + +void to_json(json& j, const BuiltinDocumentation& obj) { + j = json{{"name", obj.name}, {"description", obj.description}}; +} + +void to_json(json& j, const VariableDocumentation& obj) { + j = json{{"name", obj.name}, + {"description", obj.description}, + {"type", obj.type}, + {"is_constant", obj.is_constant}}; + if (obj.def_location) { + j["def_location"] = obj.def_location.value(); + } +} + +void to_json(json& j, const ArgumentDocumentation& obj) { + j = json{{"name", obj.name}, + {"description", obj.description}, + {"type", obj.type}, + {"is_mutated", obj.is_mutated}, + {"is_unused", obj.is_unused}}; +} + +void to_json(json& j, const FunctionDocumentation& obj) { + j = json{{"name", obj.name}, + {"description", obj.description}, + {"return_type", obj.return_type}, + {"args", obj.args}, + {"is_unused", obj.is_unused}}; + if (obj.def_location) { + j["def_location"] = obj.def_location.value(); + } +} + +void to_json(json& j, const FieldDocumentation& obj) { + j = json{ + {"name", obj.name}, {"description", obj.description}, {"type", obj.type}, + {"is_array", obj.is_array}, {"is_dynamic", obj.is_dynamic}, {"is_inline", obj.is_inline}}; +} + +void to_json(json& j, const TypeMethodDocumentation& obj) { + j = json{{"name", obj.name}, {"id", obj.id}, {"is_override", obj.is_override}}; +} + +void to_json(json& j, const TypeStateDocumentation& obj) { + j = json{{"name", obj.name}, {"is_virtual", obj.is_virtual}}; + if (obj.id) { + j["id"] = obj.id.value(); + } +} + +void to_json(json& j, const TypeDocumentation& obj) { + j = json{{"name", obj.name}, + {"description", obj.description}, + {"parent_type", obj.parent_type}, + {"size", obj.size}, + {"fields", obj.fields}, + {"method_count", obj.method_count}, + {"methods", obj.methods}, + {"states", obj.states}}; + if (obj.def_location) { + j["def_location"] = obj.def_location.value(); + } +} + +void to_json(json& j, const MethodDocumentation& obj) { + j = json{{"name", obj.name}, + {"id", obj.id}, + {"is_builtin", obj.is_builtin}, + {"description", obj.description}, + {"type", obj.type}, + {"is_override", obj.is_override}, + {"is_unused", obj.is_unused}, + {"args", obj.args}, + {"return_type", obj.return_type}}; + if (obj.def_location) { + j["def_location"] = obj.def_location.value(); + } +} + +void to_json(json& j, const FileDocumentation& obj) { + j = json{{"description", obj.description}, + {"global_vars", obj.global_vars}, + {"functions", obj.functions}, + {"types", obj.types}, + {"constants", obj.constants}, + {"methods", obj.methods}, + {"macros", obj.macros}}; +} + +void to_json(json& j, const SymbolDocumentation& obj) { + j = json{{"description", obj.description}, + {"name", obj.name}, + {"kind", obj.kind}, + {"forward_declared_in", obj.forward_declared_in}}; + if (obj.def_location) { + j["def_location"] = obj.def_location.value(); + } +} + +void to_json(json& j, const MacroDocumentation& obj) { + j = json{{"name", obj.name}, {"args", obj.args}, {"description", obj.description}}; + auto kwargs = json{}; + for (const auto& kwarg : obj.kwargs) { + kwargs[kwarg.first] = kwarg.second.has_value() ? kwarg.second.value() : ""; + } + j["kwargs"] = kwargs; + if (obj.variadic_arg) { + j["variadic_arg"] = obj.variadic_arg.value(); + } + if (obj.def_location) { + j["def_location"] = obj.def_location.value(); + } +} + +std::vector get_args_from_docstring(std::vector args, + std::string docstring) { + std::vector arg_docs; + for (const auto& arg : args) { + ArgumentDocumentation arg_doc; + arg_doc.name = arg.name; + // TODO - is this type reliable? + arg_doc.type = arg.type.base_type(); + arg_docs.push_back(arg_doc); + } + if (docstring.empty()) { + return arg_docs; + } + auto lines = str_util::split(docstring); + for (const auto& line : lines) { + if (str_util::starts_with(line, "@param")) { + // Get the info from the @param line + const auto& tokens = + str_util::regex_get_capture_groups(line, "(@param.)\\s?([^\\s]*)\\s(.*)"); + if (tokens.size() != 3) { + lg::warn("invalid docstring line - {}, skipping", line); + continue; + } + const auto& param_type = str_util::trim(tokens[0]); + const auto& param_name = str_util::trim(tokens[1]); + const auto& param_description = str_util::trim(tokens[2]); + // First, let's find the appropriate arg_doc -- if we can't, skip it as well! + std::optional find_arg_doc; + for (const auto& arg : arg_docs) { + if (arg.name == param_name) { + find_arg_doc = arg; + } + } + if (!find_arg_doc) { + lg::warn("@param name '{}', doesn't correspond with an arg name in line '{}', skipping", + param_name, line); + continue; + } + auto& arg_doc = find_arg_doc.value(); + arg_doc.description = param_description; + if (param_type == "@param") { + // a normal arg, nothing fancy + } else if (param_type == "@param_") { + // it's unused + arg_doc.is_unused = true; + } else if (param_type == "@param!") { + // the params value is mutated within the function body + arg_doc.is_mutated = true; + } else if (param_type == "@param?") { + // the param is optional -- there are checks to see if it was provided or not so its safe to + // pass "nothing" + arg_doc.is_optional = true; + } + } + } + return arg_docs; +} + +} // namespace Docs diff --git a/goalc/compiler/docs/DocTypes.h b/goalc/compiler/docs/DocTypes.h new file mode 100644 index 0000000000..d237cd127b --- /dev/null +++ b/goalc/compiler/docs/DocTypes.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include + +#include "goalc/compiler/SymbolInfo.h" + +#include "third-party/json.hpp" + +using json = nlohmann::json; + +namespace Docs { + +struct DefinitionLocation { + std::string filename; + int line_idx = -1; + int char_idx = -1; +}; +void to_json(json& j, const DefinitionLocation& obj); + +struct BuiltinDocumentation { + std::string name; + std::string description = ""; +}; +void to_json(json& j, const BuiltinDocumentation& obj); + +struct VariableDocumentation { + std::string name; + std::string description = ""; + std::string type; + std::optional def_location; + bool is_constant; +}; +void to_json(json& j, const VariableDocumentation& obj); + +struct ArgumentDocumentation { + std::string name; + std::string type; + // Below is parsed from the functions docstring + std::string description = ""; + bool is_mutated = false; + bool is_optional = false; + bool is_unused = false; +}; +void to_json(json& j, const ArgumentDocumentation& obj); + +struct FunctionDocumentation { + std::string name; + std::string description; + std::optional def_location; + // TODO - need to track function calls to determine this, obviously cant be determined from just + // the definition + // + // If this is done, that also solves the issue of reference-finding + bool is_unused = false; + std::vector args; + std::string return_type; +}; +void to_json(json& j, const FunctionDocumentation& obj); + +struct FieldDocumentation { + std::string name; + std::string description = ""; + std::string type; + bool is_array = false; + bool is_dynamic = false; + bool is_inline = false; +}; +void to_json(json& j, const FieldDocumentation& obj); + +struct TypeMethodDocumentation { + int id; + std::string name; + bool is_override = false; +}; +void to_json(json& j, const TypeMethodDocumentation& obj); + +struct TypeStateDocumentation { + bool is_virtual; + std::optional id; + std::string name; +}; +void to_json(json& j, const TypeStateDocumentation& obj); + +struct TypeDocumentation { + std::string name; + std::string description = ""; + std::string parent_type; + std::optional def_location; + int size; + std::vector fields = {}; + int method_count; + std::vector methods = {}; + std::vector states = {}; +}; +void to_json(json& j, const TypeDocumentation& obj); + +struct MethodDocumentation { + int id; + bool is_builtin; + std::string name; + std::string description = ""; + std::string type; + std::optional def_location; + // TODO - need to track function calls to determine this, obviously cant be determined from just + // the definition + // + // If this is done, that also solves the issue of reference-finding + bool is_unused = false; + bool is_override = false; + std::vector args; + std::string return_type; +}; +void to_json(json& j, const MethodDocumentation& obj); + +struct MacroDocumentation { + std::string name; + std::string description = ""; + std::vector args; + std::vector>> kwargs; + std::optional variadic_arg; + std::optional def_location = {}; +}; +void to_json(json& j, const MacroDocumentation& obj); + +struct FileDocumentation { + std::string description = ""; + std::vector global_vars; + std::vector functions; + std::vector types; + std::vector constants; + std::vector macros; + std::vector methods; + // TODO - enums and states +}; +void to_json(json& j, const FileDocumentation& obj); + +struct SymbolDocumentation { + // TODO - forward declared symbols + std::string name; + std::string description = ""; + SymbolInfo::Kind kind; + std::optional def_location = {}; + std::vector forward_declared_in = {}; +}; +void to_json(json& j, const SymbolDocumentation& obj); + +std::vector get_args_from_docstring(std::vector args, + std::string docstring); + +} // namespace Docs diff --git a/goalc/main.cpp b/goalc/main.cpp index c1af2bbd7f..204d7f9634 100644 --- a/goalc/main.cpp +++ b/goalc/main.cpp @@ -110,6 +110,7 @@ int main(int argc, char** argv) { } } catch (std::exception& e) { lg::error("Compiler Fatal Error: {}", e.what()); + return 1; } // Otherwise, start the REPL normally