From 6edcb5b0ececa2ba3045adcdfd1457d564029345 Mon Sep 17 00:00:00 2001 From: anutosh491 Date: Thu, 6 Mar 2025 14:45:08 +0530 Subject: [PATCH 1/4] Split xinspect.hpp into interface and implementation --- CMakeLists.txt | 1 + src/xinspect.cpp | 256 +++++++++++++++++++++++++++++++++++++++++++++++ src/xinspect.hpp | 249 +++------------------------------------------ 3 files changed, 270 insertions(+), 236 deletions(-) create mode 100644 src/xinspect.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 24b4230f..a34b8169 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,6 +209,7 @@ set(XEUS_CPP_HEADERS set(XEUS_CPP_SRC src/xholder.cpp src/xinput.cpp + src/xinspect.cpp src/xinterpreter.cpp src/xoptions.cpp src/xparser.cpp diff --git a/src/xinspect.cpp b/src/xinspect.cpp new file mode 100644 index 00000000..11f3bdd4 --- /dev/null +++ b/src/xinspect.cpp @@ -0,0 +1,256 @@ +/************************************************************************************ + * Copyright (c) 2023, xeus-cpp contributors * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ************************************************************************************/ + +#include "xinspect.hpp" + +#include "clang/Interpreter/CppInterOp.h" + +namespace xcpp +{ + bool node_predicate::operator()(pugi::xml_node node) const + { + return static_cast(node.attribute("kind").value()) == kind + && static_cast(node.child("name").child_value()) == child_value; + } + + std::string class_member_predicate::get_filename(pugi::xml_node node) const + { + for (pugi::xml_node child : node.children()) + { + if (static_cast(child.attribute("kind").value()) == kind + && static_cast(child.child("name").child_value()) == child_value) + { + return child.child("anchorfile").child_value(); + } + } + return ""; + } + + bool class_member_predicate::operator()(pugi::xml_node node) const + { + auto parent = (static_cast(node.attribute("kind").value()) == "class" + || static_cast(node.attribute("kind").value()) == "struct") + && static_cast(node.child("name").child_value()) == class_name; + if (parent) + { + for (pugi::xml_node child : node.children()) + { + if (static_cast(child.attribute("kind").value()) == kind + && static_cast(child.child("name").child_value()) == child_value) + { + return true; + } + } + } + return false; + } + + std::string find_type_slow(const std::string& expression) + { + static unsigned long long var_count = 0; + + if (auto* type = Cpp::GetType(expression)) + { + return Cpp::GetQualifiedName(type); + } + + std::string id = "__Xeus_GetType_" + std::to_string(var_count++); + std::string using_clause = "using " + id + " = __typeof__(" + expression + ");\n"; + + if (!Cpp::Declare(using_clause.c_str(), false)) + { + Cpp::TCppScope_t lookup = Cpp::GetNamed(id, nullptr); + Cpp::TCppType_t lookup_ty = Cpp::GetTypeFromScope(lookup); + return Cpp::GetQualifiedCompleteName(Cpp::GetCanonicalType(lookup_ty)); + } + return ""; + } + + static nl::json read_tagconfs(const char* path) + { + nl::json result = nl::json::array(); + for (const auto& entry : std::filesystem::directory_iterator(path)) + { + if (entry.path().extension() != ".json") + { + continue; + } + std::ifstream i(entry.path()); + nl::json json_entry; + i >> json_entry; + result.emplace_back(std::move(json_entry)); + } + return result; + } + + std::pair is_inspect_request(const std::string& code, const std::regex& re) + { + std::smatch inspect; + if (std::regex_search(code, inspect, re)) + { + return std::make_pair(true, inspect); + } + return std::make_pair(false, inspect); + } + + void inspect(const std::string& code, nl::json& kernel_res) + { + std::string tagconf_dir = retrieve_tagconf_dir(); + std::string tagfiles_dir = retrieve_tagfile_dir(); + + nl::json tagconfs = read_tagconfs(tagconf_dir.c_str()); + + std::vector check{"class", "struct", "function"}; + + std::string url, tagfile; + + std::regex re_expression(R"((((?:\w*(?:\:{2}|\<.*\>|\(.*\)|\[.*\])?)\.?)*))"); + + std::smatch inspect = is_inspect_request(code, re_expression).second; + + std::string inspect_result; + + std::smatch method; + std::string to_inspect = inspect[1]; + + // Method or variable of class found (xxxx.yyyy) + if (std::regex_search(to_inspect, method, std::regex(R"((.*)\.(\w*)$)"))) + { + std::string type_name = find_type_slow(method[1]); + + if (!type_name.empty()) + { + for (nl::json::const_iterator it = tagconfs.cbegin(); it != tagconfs.cend(); ++it) + { + url = it->at("url"); + tagfile = it->at("tagfile"); + std::string filename = tagfiles_dir + "/" + tagfile; + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(filename.c_str()); + class_member_predicate predicate{type_name, "function", method[2]}; + auto node = doc.find_node(predicate); + if (!node.empty()) + { + inspect_result = url + predicate.get_filename(node); + } + } + } + } + else + { + std::string find_string; + + // check if we try to find the documentation of a namespace + // if yes, don't try to find the type using typeid + std::regex is_namespace(R"(\w+(\:{2}\w+)+)"); + std::smatch namespace_match; + if (std::regex_match(to_inspect, namespace_match, is_namespace)) + { + find_string = to_inspect; + } + else + { + std::string type_name = find_type_slow(to_inspect); + find_string = (type_name.empty()) ? to_inspect : type_name; + } + + for (nl::json::const_iterator it = tagconfs.cbegin(); it != tagconfs.cend(); ++it) + { + url = it->at("url"); + tagfile = it->at("tagfile"); + std::string filename = tagfiles_dir + "/" + tagfile; + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(filename.c_str()); + for (auto c : check) + { + node_predicate predicate{c, find_string}; + std::string node; + + if (c == "class" || c == "struct") + { + node = doc.find_node(predicate).child("filename").child_value(); + } + else + { + node = doc.find_node(predicate).child("anchorfile").child_value(); + } + + if (!node.empty()) + { + inspect_result = url + node; + } + } + } + } + + if (inspect_result.empty()) + { + std::cerr << "No documentation found for " << code << "\n"; + std::cout << std::flush; + kernel_res["found"] = false; + kernel_res["status"] = "error"; + kernel_res["ename"] = "No documentation found"; + kernel_res["evalue"] = ""; + kernel_res["traceback"] = nl::json::array(); + } + else + { + // Format html content. + std::string html_content = R"( + )"; + + // Note: Adding "?action=purge" suffix to force cppreference's + // Mediawiki to purge the HTTP cache. + + kernel_res["payload"] = nl::json::array(); + kernel_res["payload"][0] = nl::json::object( + {{"data", {{"text/plain", inspect_result}, {"text/html", html_content}}}, + {"source", "page"}, + {"start", 0}} + ); + kernel_res["user_expressions"] = nl::json::object(); + + std::cout << std::flush; + kernel_res["found"] = true; + kernel_res["status"] = "ok"; + } + } + + xintrospection::xintrospection() + { + pattern = spattern; + } + + void xintrospection::apply(const std::string& code, nl::json& kernel_res) + { + std::regex re(spattern + R"((.*))"); + std::smatch to_inspect; + std::regex_search(code, to_inspect, re); + inspect(to_inspect[1], kernel_res); + } + + std::unique_ptr xintrospection::clone() const + { + return std::make_unique(*this); + } +} diff --git a/src/xinspect.hpp b/src/xinspect.hpp index c512f35f..77664a1c 100644 --- a/src/xinspect.hpp +++ b/src/xinspect.hpp @@ -15,275 +15,52 @@ #include -#include "xeus-cpp/xbuffer.hpp" #include "xeus-cpp/xpreamble.hpp" #include "xeus-cpp/xutils.hpp" #include "xparser.hpp" -#include "clang/Interpreter/CppInterOp.h" - namespace xcpp { - struct node_predicate + struct XEUS_CPP_API node_predicate { std::string kind; std::string child_value; - bool operator()(pugi::xml_node node) const - { - return static_cast(node.attribute("kind").value()) == kind - && static_cast(node.child("name").child_value()) == child_value; - } + bool operator()(pugi::xml_node node) const; }; - struct class_member_predicate + struct XEUS_CPP_API class_member_predicate { std::string class_name; std::string kind; std::string child_value; - std::string get_filename(pugi::xml_node node) - { - for (pugi::xml_node child : node.children()) - { - if (static_cast(child.attribute("kind").value()) == kind - && static_cast(child.child("name").child_value()) == child_value) - { - return child.child("anchorfile").child_value(); - } - } - return ""; - } - - bool operator()(pugi::xml_node node) - { - auto parent = (static_cast(node.attribute("kind").value()) == "class" - || static_cast(node.attribute("kind").value()) == "struct") - && static_cast(node.child("name").child_value()) == class_name; - auto found = false; - if (parent) - { - for (pugi::xml_node child : node.children()) - { - if (static_cast(child.attribute("kind").value()) == kind - && static_cast(child.child("name").child_value()) == child_value) - { - found = true; - break; - } - } - } - return found; - } + std::string get_filename(pugi::xml_node node) const; + bool operator()(pugi::xml_node node) const; }; - std::string find_type_slow(const std::string& expression) { - static unsigned long long var_count = 0; - - if (auto *type = Cpp::GetType(expression)) - return Cpp::GetQualifiedName(type); - - // Here we might need to deal with integral types such as 3.14. + XEUS_CPP_API std::string find_type_slow(const std::string& expression); - std::string id = "__Xeus_GetType_" + std::to_string(var_count++); - std::string using_clause = "using " + id + " = __typeof__(" + expression + ");\n"; + static nl::json read_tagconfs(const char* path); - if (!Cpp::Declare(using_clause.c_str(), /*silent=*/false)) { - Cpp::TCppScope_t lookup = Cpp::GetNamed(id, nullptr); - Cpp::TCppType_t lookup_ty = Cpp::GetTypeFromScope(lookup); - return Cpp::GetQualifiedCompleteName(Cpp::GetCanonicalType(lookup_ty)); - } - return ""; - } + XEUS_CPP_API std::pair is_inspect_request(const std::string& code, const std::regex& re); - static nl::json read_tagconfs(const char* path) - { - nl::json result = nl::json::array(); - for (const auto &entry: std::filesystem::directory_iterator(path)) { - if (entry.path().extension() != ".json") - continue; - std::ifstream i(entry.path()); - nl::json json_entry; - i >> json_entry; - result.emplace_back(std::move(json_entry)); - } - return result; - } - - std::pair is_inspect_request(const std::string& code, - const std::regex& re) - { - std::smatch inspect; - if (std::regex_search(code, inspect, re)) - return std::make_pair(true, inspect); - return std::make_pair(false, inspect); - } + XEUS_CPP_API void inspect(const std::string& code, nl::json& kernel_res); - void inspect(const std::string& code, nl::json& kernel_res) - { - std::string tagconf_dir = retrieve_tagconf_dir(); - std::string tagfiles_dir = retrieve_tagfile_dir(); - - nl::json tagconfs = read_tagconfs(tagconf_dir.c_str()); - - std::vector check{"class", "struct", "function"}; - - std::string url, tagfile; - - std::regex re_expression(R"((((?:\w*(?:\:{2}|\<.*\>|\(.*\)|\[.*\])?)\.?)*))"); - - std::smatch inspect = is_inspect_request(code, re_expression).second; - - std::string inspect_result; - - std::smatch method; - std::string to_inspect = inspect[1]; - - // Method or variable of class found (xxxx.yyyy) - if (std::regex_search(to_inspect, method, std::regex(R"((.*)\.(\w*)$)"))) - { - std::string type_name = find_type_slow(method[1]); - - if (!type_name.empty()) - { - for (nl::json::const_iterator it = tagconfs.cbegin(); it != tagconfs.cend(); ++it) - { - url = it->at("url"); - tagfile = it->at("tagfile"); - std::string filename = tagfiles_dir + "/" + tagfile; - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(filename.c_str()); - class_member_predicate predicate{type_name, "function", method[2]}; - auto node = doc.find_node(predicate); - if (!node.empty()) - { - inspect_result = url + predicate.get_filename(node); - } - } - } - } - else - { - std::string find_string; - - // check if we try to find the documentation of a namespace - // if yes, don't try to find the type using typeid - std::regex is_namespace(R"(\w+(\:{2}\w+)+)"); - std::smatch namespace_match; - if (std::regex_match(to_inspect, namespace_match, is_namespace)) - { - find_string = to_inspect; - } - else - { - std::string type_name = find_type_slow(to_inspect); - find_string = (type_name.empty()) ? to_inspect : type_name; - } - - for (nl::json::const_iterator it = tagconfs.cbegin(); it != tagconfs.cend(); ++it) - { - url = it->at("url"); - tagfile = it->at("tagfile"); - std::string filename = tagfiles_dir + "/" + tagfile; - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(filename.c_str()); - for (auto c : check) - { - node_predicate predicate{c, find_string}; - std::string node; - - if (c == "class" || c == "struct") - { - node = doc.find_node(predicate).child("filename").child_value(); - } - else - { - node = doc.find_node(predicate).child("anchorfile").child_value(); - } - - if (!node.empty()) - { - inspect_result = url + node; - } - } - } - } - - if (inspect_result.empty()) - { - std::cerr << "No documentation found for " << code << "\n"; - std::cout << std::flush; - kernel_res["found"] = false; - kernel_res["status"] = "error"; - kernel_res["ename"] = "No documentation found"; - kernel_res["evalue"] = ""; - kernel_res["traceback"] = nl::json::array(); - } - else - { - // Format html content. - std::string html_content = R"( - )"; - - // Note: Adding "?action=purge" suffix to force cppreference's - // Mediawiki to purge the HTTP cache. - - kernel_res["payload"] = nl::json::array(); - kernel_res["payload"][0] = nl::json::object( - {{"data", {{"text/plain", inspect_result}, {"text/html", html_content}}}, - {"source", "page"}, - {"start", 0}} - ); - kernel_res["user_expressions"] = nl::json::object(); - - std::cout << std::flush; - kernel_res["found"] = true; - kernel_res["status"] = "ok"; - } - } - - class xintrospection : public xpreamble + class XEUS_CPP_API xintrospection : public xpreamble { public: using xpreamble::pattern; const std::string spattern = R"(^\?)"; - xintrospection() - { - pattern = spattern; - } + xintrospection(); - void apply(const std::string& code, nl::json& kernel_res) override - { - std::regex re(spattern + R"((.*))"); - std::smatch to_inspect; - std::regex_search(code, to_inspect, re); - inspect(to_inspect[1], kernel_res); - } + void apply(const std::string& code, nl::json& kernel_res) override; - [[nodiscard]] std::unique_ptr clone() const override - { - return std::make_unique(*this); - } + [[nodiscard]] std::unique_ptr clone() const override; }; - } #endif // XEUS_CPP_INSPECT_HPP From b39581533232357ac37f47c64c8c85bb35b85639 Mon Sep 17 00:00:00 2001 From: anutosh491 Date: Thu, 6 Mar 2025 15:46:16 +0530 Subject: [PATCH 2/4] Remove static --- src/xinspect.cpp | 2 +- src/xinspect.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xinspect.cpp b/src/xinspect.cpp index 11f3bdd4..657fab75 100644 --- a/src/xinspect.cpp +++ b/src/xinspect.cpp @@ -71,7 +71,7 @@ namespace xcpp return ""; } - static nl::json read_tagconfs(const char* path) + nl::json read_tagconfs(const char* path) { nl::json result = nl::json::array(); for (const auto& entry : std::filesystem::directory_iterator(path)) diff --git a/src/xinspect.hpp b/src/xinspect.hpp index 77664a1c..7bb5f9f7 100644 --- a/src/xinspect.hpp +++ b/src/xinspect.hpp @@ -42,7 +42,7 @@ namespace xcpp XEUS_CPP_API std::string find_type_slow(const std::string& expression); - static nl::json read_tagconfs(const char* path); + nl::json read_tagconfs(const char* path); XEUS_CPP_API std::pair is_inspect_request(const std::string& code, const std::regex& re); From 0f3474477462f6a619ca0a575f968e2a81163de5 Mon Sep 17 00:00:00 2001 From: anutosh491 Date: Thu, 6 Mar 2025 16:12:35 +0530 Subject: [PATCH 3/4] applied suggestions --- src/xinspect.cpp | 18 ++++++++++++++---- src/xinspect.hpp | 3 +-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/xinspect.cpp b/src/xinspect.cpp index 657fab75..93ae85d9 100644 --- a/src/xinspect.cpp +++ b/src/xinspect.cpp @@ -1,11 +1,17 @@ /************************************************************************************ - * Copyright (c) 2023, xeus-cpp contributors * + * Copyright (c) 2025, xeus-cpp contributors * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ************************************************************************************/ +#include +#include +#include +#include +#include + #include "xinspect.hpp" #include "clang/Interpreter/CppInterOp.h" @@ -33,9 +39,13 @@ namespace xcpp bool class_member_predicate::operator()(pugi::xml_node node) const { - auto parent = (static_cast(node.attribute("kind").value()) == "class" - || static_cast(node.attribute("kind").value()) == "struct") - && static_cast(node.child("name").child_value()) == class_name; + std::string node_kind = node.attribute("kind").value(); + std::string node_name = node.child("name").child_value(); + + bool is_class_or_struct = (node_kind == "class" || node_kind == "struct"); + bool name_matches = (node_name == class_name); + bool parent = is_class_or_struct && name_matches; + if (parent) { for (pugi::xml_node child : node.children()) diff --git a/src/xinspect.hpp b/src/xinspect.hpp index 7bb5f9f7..0ac92ad8 100644 --- a/src/xinspect.hpp +++ b/src/xinspect.hpp @@ -1,6 +1,5 @@ /************************************************************************************ - * Copyright (c) 2023, xeus-cpp contributors * - * Copyright (c) 2023, Martin Vassilev * + * Copyright (c) 2025, xeus-cpp contributors * * * * Distributed under the terms of the BSD 3-Clause License. * * * From 5f0fc53d3beafaa444f4669c531bb2dba29f2969 Mon Sep 17 00:00:00 2001 From: anutosh491 Date: Thu, 6 Mar 2025 16:14:06 +0530 Subject: [PATCH 4/4] formatting --- src/xinspect.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/xinspect.cpp b/src/xinspect.cpp index 93ae85d9..1cd54c36 100644 --- a/src/xinspect.cpp +++ b/src/xinspect.cpp @@ -6,11 +6,11 @@ * The full license is in the file LICENSE, distributed with this software. * ************************************************************************************/ -#include -#include -#include #include +#include #include +#include +#include #include "xinspect.hpp"