From 3d42e1ec7d3f708924a3870bffb2276dacf7f0eb Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 12 Nov 2024 20:35:38 +0100 Subject: [PATCH 1/2] chore: Move cli.h So it can be used to parse compile_commands.json files. Signed-off-by: Roberto Raggi --- src/frontend/cxx/cli.cc | 5 +- src/frontend/cxx/cxx_document.h | 3 +- src/frontend/cxx/frontend.cc | 2 +- src/frontend/cxx/lsp_server.h | 3 +- src/parser/cxx/cli.cc | 386 +++++++++++++++++++++++++++++ src/{frontend => parser}/cxx/cli.h | 0 6 files changed, 391 insertions(+), 8 deletions(-) create mode 100644 src/parser/cxx/cli.cc rename src/{frontend => parser}/cxx/cli.h (100%) diff --git a/src/frontend/cxx/cli.cc b/src/frontend/cxx/cli.cc index 6887f812..4a76bc08 100644 --- a/src/frontend/cxx/cli.cc +++ b/src/frontend/cxx/cli.cc @@ -18,16 +18,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "cli.h" - +#include #include #include -#include #include #include #include -#include #include #include diff --git a/src/frontend/cxx/cxx_document.h b/src/frontend/cxx/cxx_document.h index b3add991..dff31299 100644 --- a/src/frontend/cxx/cxx_document.h +++ b/src/frontend/cxx/cxx_document.h @@ -20,14 +20,13 @@ #pragma once +#include #include #include #include #include -#include "cli.h" - namespace cxx::lsp { class CxxDocument { diff --git a/src/frontend/cxx/frontend.cc b/src/frontend/cxx/frontend.cc index 64c143b0..2c793f17 100644 --- a/src/frontend/cxx/frontend.cc +++ b/src/frontend/cxx/frontend.cc @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,6 @@ #include #include "ast_printer.h" -#include "cli.h" #include "lsp_server.h" #include "verify_diagnostics_client.h" diff --git a/src/frontend/cxx/lsp_server.h b/src/frontend/cxx/lsp_server.h index 7e727894..d5d91107 100644 --- a/src/frontend/cxx/lsp_server.h +++ b/src/frontend/cxx/lsp_server.h @@ -30,9 +30,10 @@ #include #endif +#include + #include -#include "cli.h" #include "sync_queue.h" namespace cxx::lsp { diff --git a/src/parser/cxx/cli.cc b/src/parser/cxx/cli.cc new file mode 100644 index 00000000..4a76bc08 --- /dev/null +++ b/src/parser/cxx/cli.cc @@ -0,0 +1,386 @@ +// Copyright (c) 2024 Roberto Raggi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace cxx { + +auto to_string(const CLIMatch& match) -> std::string { + struct Process { + auto operator()(const CLIFlag& o) const -> std::string { + return std::format("{}=true", std::get<0>(o)); + } + + auto operator()(const CLIOption& o) const -> std::string { + return std::format("{}={}", std::get<0>(o), std::get<1>(o)); + } + + auto operator()(const CLIPositional& o) const -> std::string { + return std::format("{}", std::get<0>(o)); + } + }; + return std::visit(Process(), match); +} + +namespace { + +enum struct CLIOptionDescrKind { + kFlag, + kJoined, + kSeparated, +}; + +enum struct CLIOptionVisibility : bool { + kDefault, + kExperimental, +}; + +struct CLIOptionDescr { + std::string option; + std::string arg; + std::string help; + CLIOptionDescrKind kind; + bool CLI::* flag = nullptr; + CLIOptionVisibility visibility{CLIOptionVisibility::kDefault}; + + CLIOptionDescr(std::string option, std::string arg, std::string help, + CLIOptionDescrKind kind, + CLIOptionVisibility visibility = CLIOptionVisibility::kDefault) + : option(std::move(option)), + arg(std::move(arg)), + help(std::move(help)), + kind(kind), + visibility(visibility) {} + + CLIOptionDescr(std::string option, std::string help, + CLIOptionDescrKind kind = CLIOptionDescrKind::kFlag, + CLIOptionVisibility visibility = CLIOptionVisibility::kDefault) + : option(std::move(option)), + help(std::move(help)), + kind(kind), + visibility(visibility) {} + + CLIOptionDescr(std::string option, std::string help, bool CLI::* flag, + CLIOptionVisibility visibility = CLIOptionVisibility::kDefault) + : option(std::move(option)), + help(std::move(help)), + kind(CLIOptionDescrKind::kFlag), + flag(flag), + visibility(visibility) {} +}; + +std::vector options{ + {"--help", "Display this information", &CLI::opt_help}, + + {"-D", "[=]", + "Define a with as its value. If just is given, " + " is taken to be 1", + CLIOptionDescrKind::kSeparated}, + + {"-I", "", "Add to the end of the main include path", + CLIOptionDescrKind::kSeparated}, + + {"-L", "", "Add to the end of the library path", + CLIOptionDescrKind::kSeparated}, + + {"-U", "", "Undefine ", CLIOptionDescrKind::kSeparated}, + + {"-std", "", "Assume that the input sources are for ", + CLIOptionDescrKind::kJoined}, + + {"--sysroot", "", + "Use as the root directory for headers and libraries", + CLIOptionDescrKind::kJoined}, + + {"-E", "Preprocess only; do not compile, assemble or link", &CLI::opt_E}, + + {"-Eonly", "Just run preprocessor, no output (for timings)", + &CLI::opt_Eonly}, + + {"-P", "Omit line markers", &CLI::opt_P}, + + {"-H", "Print the name of each header file used to the standard output", + &CLI::opt_H}, + + {"-dM", "Print macro definitions in -E mode instead of normal output", + &CLI::opt_dM}, + + {"-S", "Only run preprocess and compilation steps", &CLI::opt_S, + CLIOptionVisibility::kExperimental}, + + {"-c", "Compile and assemble, but do not link", &CLI::opt_c, + CLIOptionVisibility::kExperimental}, + + {"-o", "", "Place output into ", + CLIOptionDescrKind::kSeparated}, + + {"-x", "Specify the language from the compiler driver", + CLIOptionDescrKind::kSeparated}, + + {"-fcheck", "Enable type checker (WIP)", &CLI::opt_fcheck}, + + {"-fsyntax-only", "Check only the syntax", &CLI::opt_fsyntax_only}, + + {"-fstatic-assert", "Enable static asserts", &CLI::opt_fstatic_assert, + CLIOptionVisibility::kExperimental}, + + {"-ftemplates", "Enable templates (WIP)", &CLI::opt_ftemplates}, + + {"-fno-reflect", "Disable reflection", &CLI::opt_fno_reflect}, + + {"-emit-ast", "Emit AST files for source inputs", &CLI::opt_emit_ast}, + + {"-ast-dump", "Build ASTs and then debug dump them", &CLI::opt_ast_dump}, + + {"-ir-dump", "Dump the IR", &CLI::opt_ir_dump}, + + {"-dump-symbols", "Dump the symbol tables", &CLI::opt_dump_symbols}, + + {"-dump-tokens", "Run preprocessor, dump internal rep of tokens", + &CLI::opt_dump_tokens}, + + {"-nostdinc", + "Disable standard #include directories for the C standard library", + &CLI::opt_nostdinc}, + + {"-nostdinc++", + "Disable standard #include directories for the C++ standard library", + &CLI::opt_nostdincpp}, + + {"-winsdkdir", "", "Path to the the Windows SDK", + CLIOptionDescrKind::kSeparated}, + + {"-vctoolsdir", "", "Path to the Visual Studio Tools", + CLIOptionDescrKind::kSeparated}, + + {"-winsdkversion", "", "Version of the Windows SDK", + CLIOptionDescrKind::kSeparated}, + + {"-toolchain", "", + "Set the toolchain to 'linux', 'darwin', 'wasm32', or 'windows'. Defaults " + "to wasm32.", + CLIOptionDescrKind::kSeparated}, + + {"-arch", "", + "Set the architecture to 'x86_64', 'aarch64', 'wasm32'. Defaults to the " + "host architecture.", + CLIOptionDescrKind::kSeparated}, + + {"-verify", "Verify the diagnostic messages", &CLI::opt_verify}, + + {"-lsp", "Start Language Server", &CLI::opt_lsp}, + + {"-lsp-test", "Start Language Server in testing mode", &CLI::opt_lsp_test}, + + {"-j", "", "Run jobs in parallel.", CLIOptionDescrKind::kSeparated}, + + {"-v", "Show commands to run and use verbose output", &CLI::opt_v}, + +}; + +#ifndef CXX_NO_FILESYSTEM + +/** + * Retuns the system path found in the PATH environment variable. + */ +auto getSystemPaths() -> std::vector { + std::vector paths; + + char sep = ':'; + + // if on windows use ';' as separator +#ifdef _WIN32 + sep = ';'; +#endif + + if (auto path = getenv("PATH")) { + std::istringstream iss(path); + std::string token; + + while (std::getline(iss, token, sep)) { + paths.push_back(fs::path(token)); + } + } + + return paths; +} + +#endif + +} // namespace + +CLI::CLI() = default; + +auto CLI::count(const std::string& flag) const -> int { + int n = 0; + for (const auto& match : result_) { + if (auto opt = std::get_if(&match)) { + if (std::get<0>(*opt) == flag) ++n; + } + } + return n; +} + +auto CLI::getSingle(const std::string& opt) const + -> std::optional { + auto value = get(opt); + return !value.empty() ? std::optional{value.back()} : std::nullopt; +} + +auto CLI::get(const std::string& opt) const -> std::vector { + std::vector result; + for (const auto& match : result_) { + if (auto p = std::get_if(&match)) { + if (std::get<0>(*p) == opt) result.push_back(std::get<1>(*p)); + } + } + return result; +} + +auto CLI::positionals() const -> std::vector { + std::vector result; + for (const auto& match : result_) { + if (auto p = std::get_if(&match)) { + result.push_back(std::get<0>(*p)); + } + } + return result; +} + +void CLI::parse(int& argc, char**& argv) { + app_name = argv[0]; + +#ifndef CXX_NO_FILESYSTEM + if (fs::path(app_name).remove_filename().empty()) { + for (auto path : getSystemPaths()) { + if (fs::exists(path / app_name)) { + app_name = (path / app_name).string(); + break; + } + } + } + + while (fs::is_symlink(app_name)) { + app_name = fs::read_symlink(app_name).string(); + } +#endif + + for (int i = 1; i < argc;) { + const std::string arg(argv[i++]); + + if (!arg.starts_with("-") || arg == "-") { + result_.emplace_back(CLIPositional(arg)); + continue; + } + + const auto eq = arg.find_first_of('='); + + if (eq) { + const auto name = arg.substr(0, eq); + const auto value = arg.substr(eq + 1); + + auto it = std::find_if( + options.begin(), options.end(), [&](const CLIOptionDescr& o) { + return o.kind == CLIOptionDescrKind::kJoined && o.option == name; + }); + + if (it != options.end()) { + result_.emplace_back(CLIOption(name, value)); + continue; + } + } + + auto it = + std::find_if(options.begin(), options.end(), + [&](const CLIOptionDescr& o) { return o.option == arg; }); + + if (it != options.end()) { + if (it->kind == CLIOptionDescrKind::kFlag) { + if (auto flag = it->flag) this->*flag = true; + result_.emplace_back(CLIFlag(arg)); + continue; + } + + if (it->kind == CLIOptionDescrKind::kSeparated) { + if (i < argc) { + result_.emplace_back(CLIOption(arg, argv[i++])); + continue; + } + + std::cerr << std::format("missing argument after '{}'\n", arg); + continue; + } + } + + it = std::find_if( + options.begin(), options.end(), [&](const CLIOptionDescr& o) { + return o.kind == CLIOptionDescrKind::kSeparated && + o.option.length() == 2 && arg.starts_with(o.option); + }); + + if (it != options.end()) { + result_.emplace_back(CLIOption(it->option, arg.substr(2))); + continue; + } + + std::cerr << std::format("unsupported option '{}'\n", arg); + } +} + +void CLI::showHelp() { + std::cerr << std::format("Usage: cxx [options] file...\n"); + std::cerr << std::format("Options:\n"); + for (const auto& opt : options) { + if (opt.visibility == CLIOptionVisibility::kExperimental) { + continue; + } + + std::string info; + switch (opt.kind) { + case CLIOptionDescrKind::kSeparated: { + if (opt.arg.empty()) { + info = opt.option; + } else { + info = std::format("{} {}", opt.option, opt.arg); + } + break; + } + case CLIOptionDescrKind::kJoined: { + info = std::format("{}={}", opt.option, opt.arg); + break; + } + case CLIOptionDescrKind::kFlag: { + info = std::format("{}", opt.option); + break; + } + } // switch + std::cerr << std::format(" {:<28} {}\n", info, opt.help); + } +} + +} // namespace cxx diff --git a/src/frontend/cxx/cli.h b/src/parser/cxx/cli.h similarity index 100% rename from src/frontend/cxx/cli.h rename to src/parser/cxx/cli.h From dfc1ade2d18498b5bed20ba8c9f1f81d8d8cf039 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Tue, 12 Nov 2024 20:44:41 +0100 Subject: [PATCH 2/2] chore: Move the lsp server to src/lsp module So it can be used by the emscripten bindings. Signed-off-by: Roberto Raggi --- src/frontend/CMakeLists.txt | 11 ----------- src/frontend/cxx/frontend.cc | 2 +- src/lsp/CMakeLists.txt | 9 +++++++++ src/{frontend/cxx => lsp/cxx/lsp}/cxx_document.cc | 0 src/{frontend/cxx => lsp/cxx/lsp}/cxx_document.h | 0 src/{frontend/cxx => lsp/cxx/lsp}/lsp_server.cc | 0 src/{frontend/cxx => lsp/cxx/lsp}/lsp_server.h | 0 src/{frontend/cxx => lsp/cxx/lsp}/sync_queue.cc | 0 src/{frontend/cxx => lsp/cxx/lsp}/sync_queue.h | 0 src/parser/CMakeLists.txt | 4 ++++ 10 files changed, 14 insertions(+), 12 deletions(-) rename src/{frontend/cxx => lsp/cxx/lsp}/cxx_document.cc (100%) rename src/{frontend/cxx => lsp/cxx/lsp}/cxx_document.h (100%) rename src/{frontend/cxx => lsp/cxx/lsp}/lsp_server.cc (100%) rename src/{frontend/cxx => lsp/cxx/lsp}/lsp_server.h (100%) rename src/{frontend/cxx => lsp/cxx/lsp}/sync_queue.cc (100%) rename src/{frontend/cxx => lsp/cxx/lsp}/sync_queue.h (100%) diff --git a/src/frontend/CMakeLists.txt b/src/frontend/CMakeLists.txt index d2257eb4..8ff7068e 100644 --- a/src/frontend/CMakeLists.txt +++ b/src/frontend/CMakeLists.txt @@ -23,17 +23,6 @@ add_executable(cxx ${SOURCES}) target_link_libraries(cxx PRIVATE cxx-parser cxx-lsp) -target_compile_definitions(cxx PRIVATE - CXX_VERSION="${CMAKE_PROJECT_VERSION}" -) - -# if cmake founds the Threads package, link with it -if(Threads_FOUND) - target_link_libraries(cxx PRIVATE Threads::Threads) -else() - target_compile_definitions(cxx PRIVATE CXX_NO_THREADS) -endif() - if(EMSCRIPTEN) target_link_options(cxx PRIVATE "SHELL:-s EXIT_RUNTIME=1" diff --git a/src/frontend/cxx/frontend.cc b/src/frontend/cxx/frontend.cc index 2c793f17..c227a31f 100644 --- a/src/frontend/cxx/frontend.cc +++ b/src/frontend/cxx/frontend.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -43,7 +44,6 @@ #include #include "ast_printer.h" -#include "lsp_server.h" #include "verify_diagnostics_client.h" namespace { diff --git a/src/lsp/CMakeLists.txt b/src/lsp/CMakeLists.txt index 816bbb10..0d72708c 100644 --- a/src/lsp/CMakeLists.txt +++ b/src/lsp/CMakeLists.txt @@ -26,6 +26,15 @@ add_library(cxx-lsp ${CXX_LSP_INCLUDE_HEADER_FILES} ) +target_link_libraries(cxx-lsp PUBLIC cxx-parser) + +# if cmake founds the Threads package, link with it +if(Threads_FOUND) + target_link_libraries(cxx-lsp PUBLIC Threads::Threads) +else() + target_compile_definitions(cxx-lsp PUBLIC CXX_NO_THREADS) +endif() + target_include_directories(cxx-lsp PUBLIC $ $ diff --git a/src/frontend/cxx/cxx_document.cc b/src/lsp/cxx/lsp/cxx_document.cc similarity index 100% rename from src/frontend/cxx/cxx_document.cc rename to src/lsp/cxx/lsp/cxx_document.cc diff --git a/src/frontend/cxx/cxx_document.h b/src/lsp/cxx/lsp/cxx_document.h similarity index 100% rename from src/frontend/cxx/cxx_document.h rename to src/lsp/cxx/lsp/cxx_document.h diff --git a/src/frontend/cxx/lsp_server.cc b/src/lsp/cxx/lsp/lsp_server.cc similarity index 100% rename from src/frontend/cxx/lsp_server.cc rename to src/lsp/cxx/lsp/lsp_server.cc diff --git a/src/frontend/cxx/lsp_server.h b/src/lsp/cxx/lsp/lsp_server.h similarity index 100% rename from src/frontend/cxx/lsp_server.h rename to src/lsp/cxx/lsp/lsp_server.h diff --git a/src/frontend/cxx/sync_queue.cc b/src/lsp/cxx/lsp/sync_queue.cc similarity index 100% rename from src/frontend/cxx/sync_queue.cc rename to src/lsp/cxx/lsp/sync_queue.cc diff --git a/src/frontend/cxx/sync_queue.h b/src/lsp/cxx/lsp/sync_queue.h similarity index 100% rename from src/frontend/cxx/sync_queue.h rename to src/lsp/cxx/lsp/sync_queue.h diff --git a/src/parser/CMakeLists.txt b/src/parser/CMakeLists.txt index 7921b24a..c7202d4d 100644 --- a/src/parser/CMakeLists.txt +++ b/src/parser/CMakeLists.txt @@ -27,6 +27,10 @@ add_library(cxx-parser ${SOURCES} pp_keywords-priv.h ) +target_compile_definitions(cxx-parser PUBLIC + CXX_VERSION="${CMAKE_PROJECT_VERSION}" +) + target_include_directories(cxx-parser PRIVATE ${CMAKE_CURRENT_BINARY_DIR} PUBLIC $