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
This commit is contained in:
Tyler Wilding
2023-02-20 19:49:37 -05:00
committed by GitHub
parent 65f2146d73
commit 39658dfd71
23 changed files with 994 additions and 279 deletions
+64 -4
View File
@@ -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 }}"}'
+4
View File
@@ -64,3 +64,7 @@ __pycache__/
# backup files from OpenMaya
*.bak
# docs
/jak1-*.json
/jak2-*.json
+3
View File
@@ -62,8 +62,11 @@ class Interpreter {
const std::unordered_map<std::string, std::pair<bool, std::optional<ObjectType>>>& named);
Object eval_pair(const Object& o, const std::shared_ptr<EnvironmentObject>& env);
public:
ArgumentSpec parse_arg_spec(const Object& form, Object& rest);
private:
Object quasiquote_helper(const Object& form, const std::shared_ptr<EnvironmentObject>& env);
IntType number_to_integer(const Object& obj);
+1
View File
@@ -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());
+1 -1
View File
@@ -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));
}
}
+10
View File
@@ -35,6 +35,9 @@ class Trie {
// Get all objects starting with the given prefix.
std::vector<T*> lookup_prefix(const std::string& str) const;
// Get all nodes in the tree.
std::vector<T*> get_all_nodes() const;
~Trie();
private:
@@ -178,3 +181,10 @@ template <typename T>
std::vector<T*> Trie<T>::lookup_prefix(const std::string& str) const {
return m_root.lookup_prefix(str.c_str());
}
template <typename T>
std::vector<T*> Trie<T>::get_all_nodes() const {
std::vector<T*> result;
m_root.get_all_children(result);
return result;
}
+12
View File
@@ -81,4 +81,16 @@ std::string diff(const std::string& lhs, const std::string& rhs) {
std::vector<std::string> split(const ::std::string& str, char delimiter) {
return google_diff::split_string(str, delimiter);
}
std::vector<std::string> regex_get_capture_groups(const std::string& str,
const std::string& regex) {
std::vector<std::string> 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
+1
View File
@@ -19,4 +19,5 @@ std::string diff(const std::string& lhs, const std::string& rhs);
/// Default splits on \n characters
std::vector<std::string> split(const ::std::string& str, char delimiter = '\n');
std::string join(const std::vector<std::string>& strs, const std::string& join_with);
std::vector<std::string> regex_get_capture_groups(const std::string& str, const std::string& regex);
} // namespace str_util
+6 -9
View File
@@ -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
+15 -16
View File
@@ -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)
+1 -1
View File
@@ -1465,4 +1465,4 @@
,@body
)
)
)
)
+1 -1
View File
@@ -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)
+4 -2
View File
@@ -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.
+5 -4
View File
@@ -105,6 +105,7 @@ class Compiler {
listener::Listener m_listener;
goos::Interpreter m_goos;
Debugger m_debugger;
std::unordered_map<std::string, goos::ArgumentSpec> m_macro_specs;
std::unordered_map<std::string, TypeSpec> m_symbol_types;
std::unordered_map<goos::HeapObject*, goos::Object> m_global_constants;
std::unordered_map<goos::HeapObject*, LambdaVal*> 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<std::string,
Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)>>
g_goal_forms;
+100 -24
View File
@@ -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<Metadata> 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<GoalArg> args,
const goos::Object& defining_form,
const std::optional<Metadata> 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<Metadata> 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<Metadata> 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<Metadata> 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<Metadata> 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<GoalArg> 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<GoalArg>& 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<GoalArg> 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<SymbolInfo::Metadata> 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<GoalArg> args,
const goos::Object& defining_form,
const std::optional<SymbolInfo::Metadata> 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<SymbolInfo::Metadata> 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<SymbolInfo::Metadata> 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<SymbolInfo::Metadata> 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<SymbolInfo::Metadata> 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<GoalArg> 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<SymbolInfo>* lookup_exact_name(const std::string& name) const {
@@ -168,6 +233,17 @@ class SymbolInfoMap {
int symbol_count() const { return m_map.size(); }
std::vector<SymbolInfo> get_all_symbols() const {
std::vector<SymbolInfo> info;
auto lookup = m_map.get_all_nodes();
for (auto& x : lookup) {
for (auto& y : *x) {
info.push_back(y);
}
}
return info;
}
private:
Trie<std::vector<SymbolInfo>> m_map;
};
+202 -199
View File
@@ -11,75 +11,76 @@
*/
const std::unordered_map<
std::string,
Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)>
std::pair<std::string,
Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)>>
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, &macro_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
+206 -6
View File
@@ -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<std::string, Docs::SymbolDocumentation> all_symbols;
std::unordered_map<std::string, Docs::FileDocumentation> 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<Docs::DefinitionLocation> 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<StructureType*>(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<std::string> 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();
}
+11 -4
View File
@@ -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<IR_SetSymbolValue>(sym_val, in_gpr));
return in_gpr;
+10 -5
View File
@@ -44,7 +44,9 @@ Val* Compiler::compile_goos_macro(const goos::Object& o,
auto compile_env_for_macro =
env->function_env()->alloc_env<MacroExpandEnv>(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();
}
+1 -3
View File
@@ -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);
}
+184
View File
@@ -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<ArgumentDocumentation> get_args_from_docstring(std::vector<GoalArg> args,
std::string docstring) {
std::vector<ArgumentDocumentation> 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<ArgumentDocumentation> 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
+151
View File
@@ -0,0 +1,151 @@
#pragma once
#include <optional>
#include <string>
#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<DefinitionLocation> 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<DefinitionLocation> 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<ArgumentDocumentation> 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<int> 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<DefinitionLocation> def_location;
int size;
std::vector<FieldDocumentation> fields = {};
int method_count;
std::vector<TypeMethodDocumentation> methods = {};
std::vector<TypeStateDocumentation> 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<DefinitionLocation> 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<ArgumentDocumentation> args;
std::string return_type;
};
void to_json(json& j, const MethodDocumentation& obj);
struct MacroDocumentation {
std::string name;
std::string description = "";
std::vector<std::string> args;
std::vector<std::pair<std::string, std::optional<std::string>>> kwargs;
std::optional<std::string> variadic_arg;
std::optional<DefinitionLocation> def_location = {};
};
void to_json(json& j, const MacroDocumentation& obj);
struct FileDocumentation {
std::string description = "";
std::vector<VariableDocumentation> global_vars;
std::vector<FunctionDocumentation> functions;
std::vector<TypeDocumentation> types;
std::vector<VariableDocumentation> constants;
std::vector<MacroDocumentation> macros;
std::vector<MethodDocumentation> 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<DefinitionLocation> def_location = {};
std::vector<DefinitionLocation> forward_declared_in = {};
};
void to_json(json& j, const SymbolDocumentation& obj);
std::vector<ArgumentDocumentation> get_args_from_docstring(std::vector<GoalArg> args,
std::string docstring);
} // namespace Docs
+1
View File
@@ -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