From 9168f03289b37d4458c9387bc53b6ddfc9eb348c Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Thu, 4 Mar 2021 10:33:59 -0500 Subject: [PATCH] [Compiler] Add `get-info` (#306) * wip symbol info * finish implementation --- common/goos/TextDB.cpp | 19 +- common/goos/TextDB.h | 5 +- common/util/Trie.h | 185 ++++++++++++++++++ doc/changelog.md | 4 +- doc/goal_doc.md | 66 +++++++ goalc/compiler/Compiler.cpp | 7 +- goalc/compiler/Compiler.h | 12 ++ goalc/compiler/SymbolInfo.h | 170 ++++++++++++++++ goalc/compiler/compilation/Atoms.cpp | 10 +- .../compiler/compilation/CompilerControl.cpp | 69 +++++++ goalc/compiler/compilation/Define.cpp | 11 +- goalc/compiler/compilation/Macro.cpp | 4 + goalc/compiler/compilation/Type.cpp | 4 + test/test_common_util.cpp | 31 ++- 14 files changed, 583 insertions(+), 14 deletions(-) create mode 100644 common/util/Trie.h create mode 100644 goalc/compiler/SymbolInfo.h diff --git a/common/goos/TextDB.cpp b/common/goos/TextDB.cpp index d9baee9b46..0990dbd6bf 100644 --- a/common/goos/TextDB.cpp +++ b/common/goos/TextDB.cpp @@ -108,7 +108,7 @@ void TextDb::link(const Object& o, std::shared_ptr frag, int offset) /*! * Given an object, get a string representing where it's from. Or "?" if we can't find it. */ -std::string TextDb::get_info_for(const Object& o, bool* terminate_compiler_error) { +std::string TextDb::get_info_for(const Object& o, bool* terminate_compiler_error) const { if (o.is_pair()) { auto kv = map.find(o.heap_obj); if (kv != map.end()) { @@ -133,10 +133,25 @@ std::string TextDb::get_info_for(const Object& o, bool* terminate_compiler_error /*! * Given a source text and an offset, print a description of where it is. */ -std::string TextDb::get_info_for(const std::shared_ptr& frag, int offset) { +std::string TextDb::get_info_for(const std::shared_ptr& frag, int offset) const { std::string result = "text from " + frag->get_description() + ", line: " + std::to_string(frag->get_line_idx(offset) + 1) + "\n"; result += frag->get_line_containing_offset(offset) + "\n"; return result; } + +/*! + * Make child have the same location in the source as parent. For example, if parent generates + * code that we want to be associated with the parent's location in source. + * + * Note: this only has an effect if both parent and child are pair/list. Otherwise it does nothing. + */ +void TextDb::inherit_info(const Object& parent, const Object& child) { + if (parent.is_pair() && child.is_pair()) { + auto parent_kv = map.find(parent.heap_obj); + if (parent_kv != map.end()) { + map[child.heap_obj] = parent_kv->second; + } + } +} } // namespace goos diff --git a/common/goos/TextDB.h b/common/goos/TextDB.h index 77457863b1..abb7535206 100644 --- a/common/goos/TextDB.h +++ b/common/goos/TextDB.h @@ -91,8 +91,9 @@ class TextDb { public: void insert(const std::shared_ptr& frag); void link(const Object& o, std::shared_ptr frag, int offset); - std::string get_info_for(const Object& o, bool* terminate_compiler_error = nullptr); - std::string get_info_for(const std::shared_ptr& frag, int offset); + std::string get_info_for(const Object& o, bool* terminate_compiler_error = nullptr) const; + std::string get_info_for(const std::shared_ptr& frag, int offset) const; + void inherit_info(const Object& parent, const Object& child); private: std::vector> fragments; diff --git a/common/util/Trie.h b/common/util/Trie.h new file mode 100644 index 0000000000..5cc8e67550 --- /dev/null +++ b/common/util/Trie.h @@ -0,0 +1,185 @@ +#pragma once + +#include +#include +#include + +/*! + * A simple prefix tree. It works similarly to a map, but also supports fast lookups by prefix with + * the ability to lookup all entries with keys that begin with a given prefix. + * + * It owns the memory for the objects it stores. + * Doing an insert will create a copy of your object. + * + * Other that deleting the whole thing, there is no support for removing a node. + */ +template +class Trie { + public: + Trie() = default; + Trie(const Trie&) = delete; + Trie& operator=(const Trie&) = delete; + + // Insert an object, replacing an existing one if it exists + void insert(const std::string& str, const T& obj); + + // Get the object at the string. Default construct a new one if none exists. + T* operator[](const std::string& str); + + // Lookup an existing object. If none exists, return nullptr. + T* lookup(const std::string& str); + + // return the number of entries. + int size() const { return m_size; } + + // Get all objects starting with the given prefix. + std::vector lookup_prefix(const std::string& str); + ~Trie(); + + private: + static constexpr int CHAR_START = ' '; + static constexpr int CHAR_END = '~' + 1; + static constexpr int CHAR_SIZE = CHAR_END - CHAR_START; + + static int idx(char c) { + assert(c >= CHAR_START); + assert(c < CHAR_END); + return c - CHAR_START; + } + + struct Node { + T* value = nullptr; + Node* children[CHAR_SIZE] = {0}; + + void delete_children() { + if (value) { + delete value; + value = nullptr; + } + for (auto child : children) { + if (child) { + child->delete_children(); + delete child; + child = nullptr; + } + } + } + + /*! + * Return true if a new object was inserted. + */ + bool insert(const char* str, const T* obj) { + if (!*str) { + // we are the child! + if (value) { + delete value; + value = new T(*obj); + return false; // didn't change the count. + } else { + value = new T(*obj); + return true; // did change the count + } + + } else { + // still more to go + char first = *str; + if (!children[idx(first)]) { + children[idx(first)] = new Node(); + } + return children[idx(first)]->insert(str + 1, obj); + } + } + + T* bracket_operator(const char* str, bool* inserted) { + if (!*str) { + // we are the child! + if (!value) { + value = new T(); + *inserted = true; + } else { + *inserted = false; + } + + return value; + } else { + // still more to go + char first = *str; + if (!children[idx(first)]) { + children[idx(first)] = new Node(); + } + return children[idx(first)]->bracket_operator(str + 1, inserted); + } + } + + T* lookup(const char* str) { + if (!*str) { + return value; + } + if (children[idx(*str)]) { + return children[idx(*str)]->lookup(str + 1); + } + return nullptr; + } + + void get_all_children(std::vector& result) { + if (value) { + result.push_back(value); + } + for (auto child : children) { + if (child) { + child->get_all_children(result); + } + } + } + + std::vector lookup_prefix(const char* str) { + if (!*str) { + std::vector result; + get_all_children(result); + return result; + } else { + if (children[idx(*str)]) { + return children[idx(*str)]->lookup_prefix(str + 1); + } else { + return {}; + } + } + } + }; + + Node m_root; + int m_size = 0; +}; + +template +Trie::~Trie() { + m_root.delete_children(); + m_size = 0; +} + +template +void Trie::insert(const std::string& str, const T& obj) { + if (m_root.insert(str.c_str(), &obj)) { + m_size++; + } +} + +template +T* Trie::lookup(const std::string& str) { + return m_root.lookup(str.c_str()); +} + +template +T* Trie::operator[](const std::string& str) { + bool added = false; + auto result = m_root.bracket_operator(str.c_str(), &added); + if (added) { + m_size++; + } + return result; +} + +template +std::vector Trie::lookup_prefix(const std::string& str) { + return m_root.lookup_prefix(str.c_str()); +} \ No newline at end of file diff --git a/doc/changelog.md b/doc/changelog.md index 6a04645f70..a9c77ef594 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -116,4 +116,6 @@ ## V0.6 - There is no longer a separate compiler form for variable vs. constant shifts. Instead the compiler will pick the constant shift automatically when possible. The shifts are called `sar`, `shl` and `shr`, like the x86 instructions. -- Static type reference link data now has the correct number of methods. This prevents errors like `dkernel: trying to redefine a type 'game-info' with 29 methods when it had 12, try restarting` when you did not actually redefine the number of methods. \ No newline at end of file +- Static type reference link data now has the correct number of methods. This prevents errors like `dkernel: trying to redefine a type 'game-info' with 29 methods when it had 12, try restarting` when you did not actually redefine the number of methods. +- Added `get-info` to figure out what something is and where it's defined. +- Added `autocomplete` to get auto-completions for a prefix. \ No newline at end of file diff --git a/doc/goal_doc.md b/doc/goal_doc.md index 84438a27c0..b41bdfbf64 100644 --- a/doc/goal_doc.md +++ b/doc/goal_doc.md @@ -625,6 +625,72 @@ Reload the GOAL compiler ``` Disconnect from the target and reset all compiler state. This is equivalent to exiting the compiler and opening it again. +## `get-info` +Get information about something. +```lisp +(get-info ) +``` +Use `get-info` to see what something is and where it is defined. + +For example: +```lisp +;; get info about a global variable: +g > (get-info *kernel-context*) +[Global Variable] Type: kernel-context Defined: text from goal_src/kernel/gkernel.gc, line: 88 +(define *kernel-context* (new 'static 'kernel-context + +;; get info about a function. This particular function is forward declared, so there's an entry for that too. +;; global functions are also global variables, so there's a global variable entry as well. +g > (get-info fact) +[Forward-Declared] Name: fact Defined: text from goal_src/kernel/gcommon.gc, line: 1098 +(define-extern fact (function int int)) + +[Function] Name: fact Defined: text from kernel/gcommon.gc, line: 1099 +(defun fact ((x int)) + +[Global Variable] Type: (function int int) Defined: text from goal_src/kernel/gcommon.gc, line: 1099 +(defun fact ((x int)) + +;; get info about a type +g > (get-info kernel-context) +[Type] Name: kernel-context Defined: text from goal_src/kernel/gkernel-h.gc, line: 114 +(deftype kernel-context (basic) + +;; get info about a method +g > (get-info reset!) +[Method] Type: collide-sticky-rider-group Method Name: reset! Defined: text from goal_src/engine/collide/collide-shape-h.gc, line: 48 +(defmethod reset! collide-sticky-rider-group ((obj collide-sticky-rider-group)) +[Method] Type: collide-overlap-result Method Name: reset! Defined: text from goal_src/engine/collide/collide-shape-h.gc, line: 94 +(defmethod reset! collide-overlap-result ((obj collide-overlap-result)) +[Method] Type: load-state Method Name: reset! Defined: text from goal_src/engine/level/load-boundary.gc, line: 9 +(defmethod reset! load-state ((obj load-state)) + +;; get info about a constant +g > (get-info TWO_PI) +[Constant] Name: TWO_PI Value: (the-as float #x40c90fda) Defined: text from goal_src/engine/math/trigonometry.gc, line: 34 +(defconstant TWO_PI (the-as float #x40c90fda)) + +;; get info about a built-in form +g > (get-info asm-file) +[Built-in Form] asm-file +``` + +## `autocomplete` +Preview the results of the REPL autocomplete: +```lisp +(autcomplete ) +``` + +For example: +```lisp +g > (autocomplete *) + * + *16k-dead-pool* + *4k-dead-pool* +... +Autocomplete: 326/1474 symbols matched, took 1.29 ms +``` + ## `seval` Execute GOOS code. ```lisp diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 0967ba1b15..ab496ecb4e 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -15,9 +15,14 @@ Compiler::Compiler() : m_debugger(&m_listener) { m_global_env = std::make_unique(); m_none = std::make_unique(m_ts.make_typespec("none")); - // todo - compile library + // compile GOAL library Object library_code = m_goos.reader.read_from_file({"goal_src", "goal-lib.gc"}); compile_object_file("goal-lib", library_code, false); + + // add built-in forms to symbol info + for (auto& builtin : g_goal_forms) { + m_symbol_info.add_builtin(builtin.first); + } } ReplStatus Compiler::execute_repl() { diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index e5162be3b2..a68035bf41 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -16,6 +16,7 @@ #include "third-party/fmt/core.h" #include "third-party/fmt/color.h" #include "CompilerException.h" +#include "goalc/compiler/SymbolInfo.h" #include "Enum.h" enum MathMode { MATH_INT, MATH_BINT, MATH_FLOAT, MATH_INVALID }; @@ -185,6 +186,8 @@ class Compiler { int offset, Env* env); + std::string make_symbol_info_description(const SymbolInfo& info); + TypeSystem m_ts; std::unique_ptr m_global_env = nullptr; std::unique_ptr m_none = nullptr; @@ -199,6 +202,8 @@ class Compiler { std::unordered_map, LambdaVal*> m_inlineable_functions; CompilerSettings m_settings; bool m_throw_on_define_extern_redefinition = false; + SymbolInfoMap m_symbol_info; + MathMode get_math_mode(const TypeSpec& ts); bool is_number(const TypeSpec& ts); bool is_float(const TypeSpec& ts); @@ -427,6 +432,8 @@ class Compiler { Val* compile_in_package(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_build_dgo(const goos::Object& form, const goos::Object& rest, Env* env); 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); // ControlFlow Condition compile_condition(const goos::Object& condition, Env* env, bool invert); @@ -503,4 +510,9 @@ class Compiler { Val* compile_defenum(const goos::Object& form, const goos::Object& rest, Env* env); }; +extern const std::unordered_map< + std::string, + Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)> + g_goal_forms; + #endif // JAK_COMPILER_H diff --git a/goalc/compiler/SymbolInfo.h b/goalc/compiler/SymbolInfo.h new file mode 100644 index 0000000000..567ab0d50a --- /dev/null +++ b/goalc/compiler/SymbolInfo.h @@ -0,0 +1,170 @@ +#pragma once + +#include +#include +#include +#include +#include "common/util/Trie.h" +#include "common/goos/Object.h" + +/*! + * Info about a single symbol, representing one of: + * - Global variable + * - Global function + * - Type + * - Constant + * - Macro + * - Builtin keyword of the OpenGOAL language + */ +class SymbolInfo { + public: + enum class Kind { + GLOBAL_VAR, + FWD_DECLARED_SYM, + FUNCTION, + TYPE, + CONSTANT, + MACRO, + LANGUAGE_BUILTIN, + METHOD, + INVALID + }; + + static SymbolInfo make_global(const std::string& name, const goos::Object& defining_form) { + SymbolInfo info; + info.m_kind = Kind::GLOBAL_VAR; + info.m_name = name; + info.m_def_form = defining_form; + return info; + } + + static SymbolInfo make_fwd_declared_sym(const std::string& name, + const goos::Object& defining_form) { + SymbolInfo info; + info.m_kind = Kind::FWD_DECLARED_SYM; + info.m_name = name; + info.m_def_form = defining_form; + return info; + } + + static SymbolInfo make_function(const std::string& name, const goos::Object& defining_form) { + SymbolInfo info; + info.m_kind = Kind::FUNCTION; + info.m_name = name; + info.m_def_form = defining_form; + return info; + } + + static SymbolInfo make_type(const std::string& name, const goos::Object& defining_form) { + SymbolInfo info; + info.m_kind = Kind::TYPE; + info.m_name = name; + info.m_def_form = defining_form; + return info; + } + + static SymbolInfo make_constant(const std::string& name, const goos::Object& defining_form) { + SymbolInfo info; + info.m_kind = Kind::CONSTANT; + info.m_name = name; + info.m_def_form = defining_form; + return info; + } + + static SymbolInfo make_macro(const std::string& name, const goos::Object& defining_form) { + SymbolInfo info; + info.m_kind = Kind::MACRO; + info.m_name = name; + info.m_def_form = defining_form; + return info; + } + + static SymbolInfo make_builtin(const std::string& name) { + SymbolInfo info; + info.m_kind = Kind::LANGUAGE_BUILTIN; + info.m_name = name; + return info; + } + + static SymbolInfo make_method(const std::string& method_name, + const std::string& type_name, + 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_def_form = defining_form; + return info; + } + + const std::string& name() const { return m_name; } + const std::string& type() const { return m_type_of_method; } + Kind kind() const { return m_kind; } + const goos::Object& src_form() const { return m_def_form; } + + private: + Kind m_kind = Kind::INVALID; + goos::Object m_def_form; + std::string m_name; + std::string m_type_of_method; +}; + +/*! + * A map of symbol info. It internally stores the info in a prefix tree so you can quickly get + * a list of all symbols starting with a given prefix. + */ +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_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)); + } + + 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_constant(const std::string& name, const goos::Object& defining_form) { + m_map[name]->push_back(SymbolInfo::make_constant(name, defining_form)); + } + + 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_builtin(const std::string& name) { + m_map[name]->push_back(SymbolInfo::make_builtin(name)); + } + + void add_method(const std::string& method_name, + const std::string& type_name, + const goos::Object& defining_form) { + m_map[method_name]->push_back(SymbolInfo::make_method(method_name, type_name, defining_form)); + } + + std::vector* lookup_exact_name(const std::string& name) { return m_map.lookup(name); } + + std::set lookup_symbols_starting_with(const std::string& prefix) { + std::set result; + auto lookup = m_map.lookup_prefix(prefix); + for (auto& x : lookup) { + for (auto& y : *x) { + result.insert(y.name()); + } + } + return result; + } + + int symbol_count() const { return m_map.size(); } + + private: + Trie> m_map; +}; \ No newline at end of file diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index edd5963f6d..4301174a05 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -9,10 +9,10 @@ /*! * Main table for compiler forms */ -static const std::unordered_map< +const std::unordered_map< std::string, Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)> - goal_forms = { + g_goal_forms = { // INLINE ASM {".nop", &Compiler::compile_nop}, {".ret", &Compiler::compile_asm_ret}, @@ -114,6 +114,8 @@ static const std::unordered_map< {":status", &Compiler::compile_poke}, {"in-package", &Compiler::compile_in_package}, {"reload", &Compiler::compile_reload}, + {"get-info", &Compiler::compile_get_info}, + {"autocomplete", &Compiler::compile_autocomplete}, // CONDITIONAL COMPILATION {"#cond", &Compiler::compile_gscond}, @@ -247,8 +249,8 @@ Val* Compiler::compile_pair(const goos::Object& code, Env* env) { if (head.is_symbol()) { auto head_sym = head.as_symbol(); // first try as a goal compiler form - auto kv_gfs = goal_forms.find(head_sym->name); - if (kv_gfs != goal_forms.end()) { + 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); } diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 270b462b2b..ae70d78d09 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -391,3 +391,72 @@ Val* Compiler::compile_reload(const goos::Object& form, const goos::Object& rest m_want_reload = true; return get_none(); } + +std::string Compiler::make_symbol_info_description(const SymbolInfo& info) { + switch (info.kind()) { + case SymbolInfo::Kind::GLOBAL_VAR: + return fmt::format("[Global Variable] Type: {} Defined: {}", + m_symbol_types.at(info.name()).print(), + m_goos.reader.db.get_info_for(info.src_form())); + 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(), + m_goos.reader.db.get_info_for(info.src_form())); + case SymbolInfo::Kind::TYPE: + return fmt::format("[Type] Name: {} Defined: {}", info.name(), + m_goos.reader.db.get_info_for(info.src_form())); + case SymbolInfo::Kind::MACRO: + return fmt::format("[Macro] Name: {} Defined: {}", info.name(), + m_goos.reader.db.get_info_for(info.src_form())); + case SymbolInfo::Kind::CONSTANT: + return fmt::format( + "[Constant] Name: {} Value: {} Defined: {}", info.name(), + m_global_constants.at(m_goos.reader.symbolTable.intern(info.name())).print(), + m_goos.reader.db.get_info_for(info.src_form())); + case SymbolInfo::Kind::FUNCTION: + return fmt::format("[Function] Name: {} Defined: {}", info.name(), + m_goos.reader.db.get_info_for(info.src_form())); + case SymbolInfo::Kind::FWD_DECLARED_SYM: + return fmt::format("[Forward-Declared] Name: {} Defined: {}", info.name(), + m_goos.reader.db.get_info_for(info.src_form())); + default: + assert(false); + } +} + +Val* Compiler::compile_get_info(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}, {}); + + auto result = m_symbol_info.lookup_exact_name(args.unnamed.at(0).as_symbol()->name); + if (!result) { + fmt::print("No results found.\n"); + } else { + for (auto& info : *result) { + fmt::print("{}", make_symbol_info_description(info)); + } + } + + return get_none(); +} + +Val* Compiler::compile_autocomplete(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}, {}); + + Timer timer; + auto result = m_symbol_info.lookup_symbols_starting_with(args.unnamed.at(0).as_symbol()->name); + auto time = timer.getMs(); + + for (auto& x : result) { + fmt::print(" {}\n", x); + } + + fmt::print("Autocomplete: {}/{} symbols matched, took {:.2f} ms\n", result.size(), + m_symbol_info.symbol_count(), time); + + return get_none(); +} \ No newline at end of file diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp index 015cecaf56..62b2ea3de1 100644 --- a/goalc/compiler/compilation/Define.cpp +++ b/goalc/compiler/compilation/Define.cpp @@ -37,6 +37,11 @@ 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); + } + + if (!sym_val->settable()) { + throw_compiler_error(form, "Cannot define {} because it cannot be set.", sym_val->print()); } auto in_gpr = compiled_val->to_gpr(fe); @@ -53,9 +58,8 @@ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest } } - if (!sym_val->settable()) { - throw_compiler_error(form, "Cannot define {} because it cannot be set.", sym_val->print()); - } + m_symbol_info.add_global(symbol_string(sym), form); + fe->emit(std::make_unique(sym_val, in_gpr)); return in_gpr; } @@ -91,6 +95,7 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec } m_symbol_types[symbol_string(sym)] = new_type; + m_symbol_info.add_fwd_dec(symbol_string(sym), form); return get_none(); } diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index cc55da1ab1..6223312206 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -36,6 +36,8 @@ Val* Compiler::compile_goos_macro(const goos::Object& o, m_goos.goal_to_goos.enclosing_method_type = get_parent_env_of_type(env)->method_of_type_name; auto goos_result = m_goos.eval_list_return_last(macro->body, macro->body, mac_env); + // make the macro expanded form point to the source where the macro was used for error messages. + m_goos.reader.db.inherit_info(o, goos_result); m_goos.goal_to_goos.reset(); return compile_error_guard(goos_result, env); } @@ -146,6 +148,8 @@ 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); + return get_none(); } diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index 4ff942581d..500e6b4b5b 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -251,6 +251,8 @@ Val* Compiler::compile_deftype(const goos::Object& form, const goos::Object& res // Auto-generate (inspect) method generate_inspector_for_type(form, env, result.type_info); + m_symbol_info.add_type(result.type.base_type(), form); + // return none, making the value of (deftype..) unusable return get_none(); } @@ -382,6 +384,8 @@ 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); + auto info = m_ts.add_method(symbol_string(type_name), symbol_string(method_name), lambda_ts, false); auto type_obj = compile_get_symbol_value(form, symbol_string(type_name), env)->to_gpr(env); diff --git a/test/test_common_util.cpp b/test/test_common_util.cpp index 91b9f642c7..4c629e4ca5 100644 --- a/test/test_common_util.cpp +++ b/test/test_common_util.cpp @@ -1,12 +1,41 @@ #include "common/util/FileUtil.h" +#include "common/util/Trie.h" #include "gtest/gtest.h" +#include "test/all_jak1_symbols.h" #include #include -TEST(FileUtil, valid_path) { +TEST(CommonUtil, get_file_path) { std::vector test = {"cabbage", "banana", "apple"}; std::string sampleString = file_util::get_file_path(test); // std::cout << sampleString << std::endl; EXPECT_TRUE(true); } + +TEST(CommonUtil, Trie) { + Trie test; + + std::vector strings; + + for (auto x : all_syms) { + strings.push_back(x); + test.insert(strings.back(), strings.back()); + } + + auto cam_prefix = test.lookup_prefix("cam"); + EXPECT_EQ(cam_prefix.size(), 184); + EXPECT_EQ(test.lookup("not-in-the-list"), nullptr); + EXPECT_EQ(test.lookup("cam"), nullptr); + EXPECT_NE(test.lookup("energydoor-closed-till-near"), nullptr); + EXPECT_EQ(7941, test.lookup_prefix("").size()); + + EXPECT_TRUE(test.lookup("") == nullptr); + EXPECT_TRUE(test.lookup("p") == nullptr); + EXPECT_TRUE(test.lookup("pa") == nullptr); + EXPECT_TRUE(test.lookup("pat") == nullptr); + EXPECT_FALSE(test.lookup("path") == nullptr); + EXPECT_FALSE(test.lookup("path1") == nullptr); + EXPECT_TRUE(test.lookup("path-") == nullptr); + EXPECT_FALSE(test.lookup("path1-k") == nullptr); +} \ No newline at end of file