diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 38978be7d8..fd61aaa104 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -87,8 +87,7 @@ add_library(common util/term_util.cpp util/Timer.cpp util/unicode_util.cpp - versions/versions.cpp - "util/trie_map.h") + versions/versions.cpp) target_link_libraries(common fmt lzokay replxx libzstd_static tree-sitter sqlite3 libtinyfiledialogs) diff --git a/common/util/trie_map.h b/common/util/trie_map.h deleted file mode 100644 index 8b4b02baed..0000000000 --- a/common/util/trie_map.h +++ /dev/null @@ -1,160 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -// TrieMap class -template -class TrieMap { - private: - // TrieNode structure - struct TrieNode { - std::unordered_map> children; - std::vector> elements; - }; - - std::shared_ptr root; - - public: - TrieMap() : root(std::make_shared()) {} - - // Insert an element with a key into the TrieMap and return the inserted element - std::shared_ptr insert(const std::string& key, const T& element) { - std::shared_ptr shared_element = std::make_shared(element); - std::shared_ptr node = root; - for (char c : key) { - if (node->children.find(c) == node->children.end()) { - node->children[c] = std::make_shared(); - } - node = node->children[c]; - } - // Store element at the leaf node - node->elements.push_back(shared_element); - return shared_element; - } - - // Retrieve elements with a given prefix - std::vector> retrieve_with_prefix(const std::string& prefix) const { - std::vector> result; - std::shared_ptr node = root; - // Traverse to the node representing the prefix - for (char c : prefix) { - if (node->children.find(c) == node->children.end()) { - return result; // No elements with the given prefix - } - node = node->children[c]; - } - // Gather all elements stored at or below this node - retrieve_elements(node, result); - return result; - } - - // Retrieve elements with an exact key match - std::vector> retrieve_with_exact(const std::string& key) const { - std::vector> result; - std::shared_ptr node = root; - // Traverse to the node representing the key - for (char c : key) { - if (node->children.find(c) == node->children.end()) { - return result; // No elements with the given key - } - node = node->children[c]; - } - // Return elements stored at this node - return node->elements; - } - - // Remove the specified element from the TrieMap - void remove(const std::shared_ptr& element) { remove_element(root, element); } - - // Return the total number of elements stored in the TrieMap - int size() const { - int count = 0; - count_elements(root, count); - return count; - } - - // Return a vector containing shared pointers to all elements stored in the TrieMap - std::vector> get_all_elements() const { - std::vector> result; - get_all_elements_helper(root, result); - return result; - } - - private: - // Recursive function to retrieve elements stored at or below the given node - void retrieve_elements(std::shared_ptr node, - std::vector>& result) const { - // Add elements stored at this node to the result - for (const auto& element : node->elements) { - result.push_back(element); - } - // Recursively traverse children - for (const auto& child : node->children) { - retrieve_elements(child.second, result); - } - } - - // Recursive function to remove the specified element from the TrieMap - bool remove_element(std::shared_ptr node, const std::shared_ptr& element) { - // Remove the element if it exists at this node - auto& elements = node->elements; - auto it = std::find(elements.begin(), elements.end(), element); - if (it != elements.end()) { - elements.erase(it); - return true; - } - // Recursively search children - for (auto& child : node->children) { - if (remove_element(child.second, element)) { - // Remove child node if it's empty after removal - if (child.second->elements.empty() && child.second->children.empty()) { - node->children.erase(child.first); - } - return true; - } - } - return false; - } - - // Recursive function to count elements stored at or below the given node - void count_elements(std::shared_ptr node, int& count) const { - // Increment count by the number of elements stored at this node - count += node->elements.size(); - // Recursively traverse children - for (const auto& child : node->children) { - count_elements(child.second, count); - } - } - - // Recursive helper function to collect all elements stored in the TrieMap - void get_all_elements_helper(std::shared_ptr node, - std::vector>& result) const { - // Add elements stored at this node to the result - for (const auto& element : node->elements) { - result.push_back(element); - } - // Recursively traverse children - for (const auto& child : node->children) { - get_all_elements_helper(child.second, result); - } - } -}; - -// TrieMap trie_map; -// -//// Insert elements -// std::shared_ptr inserted_element_1 = trie_map.insert("apple", "A fruit"); -// std::shared_ptr inserted_element_2 = trie_map.insert("app", "An application"); -// std::shared_ptr inserted_element_3 = trie_map.insert("banana", "Another fruit"); -// std::shared_ptr inserted_element_4 = trie_map.insert("apple", "Another apple"); -// -//// Remove an element -// trie_map.remove(inserted_element_1); -// -//// Retrieve elements with a prefix -// std::vector> prefix_results = trie_map.retrieve_with_prefix("app"); diff --git a/common/util/trie_with_duplicates.h b/common/util/trie_with_duplicates.h new file mode 100644 index 0000000000..fe013a6473 --- /dev/null +++ b/common/util/trie_with_duplicates.h @@ -0,0 +1,134 @@ +#pragma once + +#include +#include +#include +#include + +// A normal Trie does not allow for duplicate keys, however this one does +// It allows for insertion and removal +// +// Note, keys assume only basic ASCII, which is _fine_ as OpenGOAL itself has this +// limitation as well. +template +class TrieWithDuplicates { + private: + struct TrieNode { + std::array, 256> children; + std::vector> elements; + }; + + std::unique_ptr root = std::make_unique(); + + public: + TrieWithDuplicates() {} + + T* insert(const std::string& key, const T& element) { + std::unique_ptr new_element = std::make_unique(element); + TrieNode* curr_node = root.get(); + for (const char character : key) { + auto& child = curr_node->children[(uint8_t)character]; + if (!child) { + child = std::make_unique(); + } + curr_node = child.get(); + } + curr_node->elements.push_back(std::move(new_element)); + return curr_node->elements.back().get(); + } + + std::vector retrieve_with_prefix(const std::string& prefix) const { + std::vector results; + TrieNode* curr_node = root.get(); + for (const char character : prefix) { + const auto& child = curr_node->children.at((uint8_t)character); + if (child == nullptr) { + return results; // tree ends, nothing found with that prefix + } else { + curr_node = child.get(); + } + } + retrieve_elements(curr_node, results); + return results; + } + + std::vector retrieve_with_exact(const std::string& key) const { + std::vector results; + TrieNode* curr_node = root.get(); + for (const char character : key) { + const auto& child = curr_node->children.at((uint8_t)character); + if (child == nullptr) { + return results; // tree ends, nothing found with that key + } else { + curr_node = child.get(); + } + } + for (const auto& element : curr_node->elements) { + results.push_back(element.get()); + } + return results; + } + + bool remove(const std::string& key, const T* to_be_removed) { + TrieNode* curr_node = root.get(); + for (const char character : key) { + const auto& child = curr_node->children.at((uint8_t)character); + if (child == nullptr) { + return false; // tree ends, nothing found with that key + } else { + curr_node = child.get(); + } + } + // Since the trie holds duplicates, we can't delete on the key alone + // now search to see which element is identical + auto it = curr_node->elements.begin(); + while (it != curr_node->elements.end()) { + if (it->get() == to_be_removed) { + it = curr_node->elements.erase(it); + return true; // we can assume that the same ptr isn't stored twice. + } else { + ++it; + } + } + return false; + } + + // Return the total number of elements stored in the TrieMap + int size() const { + int count = 0; + count_elements(root.get(), count); + return count; + } + + std::vector get_all_elements() const { + std::vector results; + get_all_elements_helper(root.get(), results); + return results; + } + + private: + void retrieve_elements(const TrieNode* node, std::vector& results) const { + for (const auto& element : node->elements) { + results.push_back(element.get()); + } + for (const auto& child : node->children) { + retrieve_elements(child.get(), results); + } + } + + void count_elements(const TrieNode* node, int& count) const { + count += node->elements.size(); + for (const auto& child : node->children) { + count_elements(child.get(), count); + } + } + + void get_all_elements_helper(const TrieNode* node, std::vector& result) const { + for (const auto& element : node->elements) { + result.push_back(element.get()); + } + for (const auto& child : node->children) { + get_all_elements_helper(child.get(), result); + } + } +}; diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index a4e1c56db5..4329e46496 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -97,13 +97,12 @@ class Compiler { std::vector> const& user_data); bool knows_object_file(const std::string& name); MakeSystem& make_system() { return m_make; } - std::vector> lookup_symbol_info_by_file( + std::vector lookup_symbol_info_by_file( const std::string& file_path) const; - std::vector> lookup_symbol_info_by_prefix( + std::vector lookup_symbol_info_by_prefix( const std::string& prefix) const; std::set lookup_symbol_names_starting_with(const std::string& prefix) const; - std::vector> lookup_exact_name_info( - const std::string& name) const; + std::vector lookup_exact_name_info(const std::string& name) const; std::optional lookup_typespec(const std::string& symbol_name); TypeSystem& type_system() { return m_ts; }; // TODO - rename these types / namespaces -- consolidate with SymbolInfo and whatever else tries @@ -321,7 +320,7 @@ class Compiler { int offset, Env* env); - std::string make_symbol_info_description(const std::shared_ptr info); + std::string make_symbol_info_description(const symbol_info::SymbolInfo* info); MathMode get_math_mode(const TypeSpec& ts); bool is_number(const TypeSpec& ts); diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index d7517ac006..0fb7e6eb9f 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -358,8 +358,7 @@ Val* Compiler::compile_reload(const goos::Object& form, const goos::Object& rest return get_none(); } -std::string Compiler::make_symbol_info_description( - const std::shared_ptr info) { +std::string Compiler::make_symbol_info_description(const symbol_info::SymbolInfo* info) { switch (info->m_kind) { case symbol_info::Kind::GLOBAL_VAR: return fmt::format("[Global Variable] Type: {} Defined: {}", @@ -588,12 +587,12 @@ Val* Compiler::compile_update_macro_metadata(const goos::Object& form, return get_none(); } -std::vector> Compiler::lookup_symbol_info_by_file( +std::vector Compiler::lookup_symbol_info_by_file( const std::string& file_path) const { return m_symbol_info.lookup_symbols_by_file(file_path); } -std::vector> Compiler::lookup_symbol_info_by_prefix( +std::vector Compiler::lookup_symbol_info_by_prefix( const std::string& prefix) const { return m_symbol_info.lookup_symbols_starting_with(prefix); } @@ -605,7 +604,7 @@ std::set Compiler::lookup_symbol_names_starting_with(const std::str return {}; } -std::vector> Compiler::lookup_exact_name_info( +std::vector Compiler::lookup_exact_name_info( const std::string& name) const { if (m_goos.reader.check_string_is_valid(name)) { return m_symbol_info.lookup_exact_name(name); diff --git a/goalc/compiler/symbol_info.cpp b/goalc/compiler/symbol_info.cpp index 3a6565a776..3b6b482013 100644 --- a/goalc/compiler/symbol_info.cpp +++ b/goalc/compiler/symbol_info.cpp @@ -57,8 +57,7 @@ void SymbolInfo::set_definition_location(const goos::TextDb* textdb) { } } -void SymbolInfoMap::add_symbol_to_file_index(const std::string& file_path, - std::shared_ptr symbol) { +void SymbolInfoMap::add_symbol_to_file_index(const std::string& file_path, SymbolInfo* symbol) { if (m_file_symbol_index.find(file_path) == m_file_symbol_index.end()) { m_file_symbol_index[file_path] = {}; } @@ -266,22 +265,20 @@ void SymbolInfoMap::add_method(const std::string& method_name, } } -std::vector> SymbolInfoMap::lookup_symbols_by_file( - const std::string& file_path) const { +std::vector SymbolInfoMap::lookup_symbols_by_file(const std::string& file_path) const { if (m_file_symbol_index.find(file_path) != m_file_symbol_index.end()) { return m_file_symbol_index.at(file_path); } return {}; } -std::vector> SymbolInfoMap::lookup_exact_name( - const std::string& name) const { +std::vector SymbolInfoMap::lookup_exact_name(const std::string& name) const { return m_symbol_map.retrieve_with_exact(name); } -std::vector> SymbolInfoMap::lookup_symbols_starting_with( +std::vector SymbolInfoMap::lookup_symbols_starting_with( const std::string& prefix) const { - std::vector> symbols; + std::vector symbols; const auto lookup = m_symbol_map.retrieve_with_prefix(prefix); for (const auto& result : lookup) { symbols.push_back(result); @@ -289,6 +286,10 @@ std::vector> SymbolInfoMap::lookup_symbols_starting_ return symbols; } +std::vector SymbolInfoMap::get_all_symbols() const { + return m_symbol_map.get_all_elements(); +} + std::set SymbolInfoMap::lookup_names_starting_with(const std::string& prefix) const { std::set names; const auto lookup = m_symbol_map.retrieve_with_prefix(prefix); @@ -302,15 +303,11 @@ int SymbolInfoMap::symbol_count() const { return m_symbol_map.size(); } -std::vector> SymbolInfoMap::get_all_symbols() const { - return m_symbol_map.get_all_elements(); -} - void SymbolInfoMap::evict_symbols_using_file_index(const std::string& file_path) { const auto standardized_path = file_util::convert_to_unix_path_separators(file_path); if (m_file_symbol_index.find(standardized_path) != m_file_symbol_index.end()) { for (const auto& symbol : m_file_symbol_index.at(standardized_path)) { - m_symbol_map.remove(symbol); + m_symbol_map.remove(symbol->m_name, symbol); } m_file_symbol_index.erase(standardized_path); } diff --git a/goalc/compiler/symbol_info.h b/goalc/compiler/symbol_info.h index 51eb3a9b92..5c2b3e80a0 100644 --- a/goalc/compiler/symbol_info.h +++ b/goalc/compiler/symbol_info.h @@ -6,7 +6,7 @@ #include "common/goos/Object.h" #include "common/util/Assert.h" -#include "common/util/trie_map.h" +#include "common/util/trie_with_duplicates.h" #include "goalc/compiler/Val.h" @@ -120,13 +120,13 @@ struct SymbolInfo { */ class SymbolInfoMap { goos::TextDb* m_textdb; - TrieMap m_symbol_map; + TrieWithDuplicates m_symbol_map; // Indexes references to symbols by the file they are defined within // This allows us to not only efficiently retrieve symbols by file, but also allows us to // cleanup symbols when files are re-compiled. - std::unordered_map>> m_file_symbol_index; + std::unordered_map> m_file_symbol_index; - void add_symbol_to_file_index(const std::string& file_path, std::shared_ptr symbol); + void add_symbol_to_file_index(const std::string& file_path, SymbolInfo* symbol); public: SymbolInfoMap(goos::TextDb* textdb) : m_textdb(textdb) {} @@ -156,14 +156,12 @@ class SymbolInfoMap { const std::vector& args, const MethodInfo& method_info, const goos::Object& defining_form); - std::vector> lookup_symbols_by_file( - const std::string& file_path) const; - std::vector> lookup_exact_name(const std::string& name) const; - std::vector> lookup_symbols_starting_with( - const std::string& prefix) const; + std::vector lookup_symbols_by_file(const std::string& file_path) const; + std::vector lookup_exact_name(const std::string& name) const; + std::vector lookup_symbols_starting_with(const std::string& prefix) const; std::set lookup_names_starting_with(const std::string& prefix) const; + std::vector get_all_symbols() const; int symbol_count() const; - std::vector> get_all_symbols() const; // Uses the per-file index to find and evict symbols globally // This should be done before re-compiling a file, symbols will be re-added to the DB if they are // found again diff --git a/goalc/main.cpp b/goalc/main.cpp index 3e4e1c9287..b080243c9d 100644 --- a/goalc/main.cpp +++ b/goalc/main.cpp @@ -8,7 +8,6 @@ #include "common/util/diff.h" #include "common/util/string_util.h" #include "common/util/term_util.h" -#include "common/util/trie_map.h" #include "common/util/unicode_util.h" #include "common/versions/versions.h" diff --git a/lsp/state/workspace.cpp b/lsp/state/workspace.cpp index 11421592df..4ea3bc4c3b 100644 --- a/lsp/state/workspace.cpp +++ b/lsp/state/workspace.cpp @@ -97,7 +97,7 @@ std::optional Workspace::determine_game_version_from_uri( return {}; } -std::vector> Workspace::get_symbols_starting_with( +std::vector Workspace::get_symbols_starting_with( const GameVersion game_version, const std::string& symbol_prefix) { if (m_compiler_instances.find(game_version) == m_compiler_instances.end()) { @@ -109,7 +109,7 @@ std::vector> Workspace::get_symbols_sta return compiler->lookup_symbol_info_by_prefix(symbol_prefix); } -std::optional> Workspace::get_global_symbol_info( +std::optional Workspace::get_global_symbol_info( const WorkspaceOGFile& file, const std::string& symbol_name) { if (m_compiler_instances.find(file.m_game_version) == m_compiler_instances.end()) { @@ -154,7 +154,7 @@ std::optional> Workspace::get_symbol_typeinfo( std::optional Workspace::get_symbol_def_location( const WorkspaceOGFile& file, - const std::shared_ptr symbol_info) { + const symbol_info::SymbolInfo* symbol_info) { const auto& def_loc = symbol_info->m_def_location; if (!def_loc) { return {}; @@ -181,7 +181,7 @@ Workspace::get_symbols_parent_type_path(const std::string& symbol_name, if (symbol_infos.empty()) { continue; } - std::shared_ptr symbol_info; + symbol_info::SymbolInfo* symbol_info; if (symbol_infos.size() > 1) { for (const auto& info : symbol_infos) { if (info->m_kind == symbol_info::Kind::TYPE) { @@ -415,8 +415,7 @@ void WorkspaceOGFile::parse_content(const std::string& content) { ts_parser_delete(parser); } -void WorkspaceOGFile::update_symbols( - const std::vector>& symbol_infos) { +void WorkspaceOGFile::update_symbols(const std::vector& symbol_infos) { m_symbols.clear(); // TODO - sorting by definition location would be nice (maybe VSCode already does this?) for (const auto& symbol_info : symbol_infos) { diff --git a/lsp/state/workspace.h b/lsp/state/workspace.h index 8253f551da..826129f214 100644 --- a/lsp/state/workspace.h +++ b/lsp/state/workspace.h @@ -50,7 +50,7 @@ class WorkspaceOGFile { std::vector m_diagnostics; void parse_content(const std::string& new_content); - void update_symbols(const std::vector>& symbol_infos); + void update_symbols(const std::vector& symbol_infos); std::optional get_symbol_at_position(const LSPSpec::Position position) const; std::vector search_for_forms_that_begin_with( std::vector prefix) const; @@ -139,17 +139,15 @@ class Workspace { std::optional get_definition_info_from_all_types( const std::string& symbol_name, const LSPSpec::DocumentUri& all_types_uri); - std::vector> get_symbols_starting_with( - const GameVersion game_version, - const std::string& symbol_prefix); - std::optional> get_global_symbol_info( - const WorkspaceOGFile& file, - const std::string& symbol_name); + std::vector get_symbols_starting_with(const GameVersion game_version, + const std::string& symbol_prefix); + std::optional get_global_symbol_info(const WorkspaceOGFile& file, + const std::string& symbol_name); std::optional> get_symbol_typeinfo(const WorkspaceOGFile& file, const std::string& symbol_name); std::optional get_symbol_def_location( const WorkspaceOGFile& file, - const std::shared_ptr symbol_info); + const symbol_info::SymbolInfo* symbol_info); std::vector> get_symbols_parent_type_path(const std::string& symbol_name, const GameVersion game_version); std::vector> get_types_subtypes(