mirror of
https://github.com/open-goal/jak-project
synced 2026-07-07 06:05:15 -04:00
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:
@@ -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 }}"}'
|
||||
|
||||
@@ -64,3 +64,7 @@ __pycache__/
|
||||
|
||||
# backup files from OpenMaya
|
||||
*.bak
|
||||
|
||||
# docs
|
||||
/jak1-*.json
|
||||
/jak2-*.json
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -1465,4 +1465,4 @@
|
||||
,@body
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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, ¯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
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user