diff --git a/codon/dsl/dsl.h b/codon/dsl/dsl.h index 4677c05c..901aea77 100644 --- a/codon/dsl/dsl.h +++ b/codon/dsl/dsl.h @@ -16,6 +16,22 @@ namespace codon { /// the DSL, like keywords and IR passes. class DSL { public: + /// General information about this plugin. + struct Info { + /// Extension name + std::string name; + /// Extension description + std::string description; + /// Extension version + std::string version; + /// Extension URL + std::string url; + /// Supported Codon versions (semver range) + std::string supported; + /// Plugin stdlib path + std::string stdlibPath; + }; + using KeywordCallback = std::function; @@ -32,17 +48,6 @@ public: virtual ~DSL() noexcept = default; - /// @return the name of this DSL - virtual std::string getName() const = 0; - - /// @param major major version number (i.e. X in X.Y.Z) - /// @param minor minor version number (i.e. Y in X.Y.Z) - /// @param patch patch version number (i.e. Z in X.Y.Z) - /// @return true if the given major, minor and patch versions are supported - virtual bool isVersionSupported(unsigned major, unsigned minor, unsigned patch) { - return true; - } - /// Registers this DSL's IR passes with the given pass manager. /// @param pm the pass manager to add the passes to /// @param debug true if compiling in debug mode diff --git a/codon/dsl/plugins.cpp b/codon/dsl/plugins.cpp index 3b149060..ac0e154a 100644 --- a/codon/dsl/plugins.cpp +++ b/codon/dsl/plugins.cpp @@ -1,29 +1,82 @@ #include "plugins.h" +#include + +#include "codon/parser/common.h" #include "codon/util/common.h" +#include "codon/util/semver/semver.h" +#include "codon/util/toml++/toml.h" namespace codon { +namespace { +bool isVersionSupported(const std::string &versionRange, int major, int minor, + int patch) { + try { + return semver::range::satisfies(semver::version(major, minor, patch), versionRange); + } catch (const std::invalid_argument &) { + return false; + } +} +} // namespace + +typedef std::unique_ptr LoadFunc(); +namespace fs = std::filesystem; PluginManager::Error PluginManager::load(const std::string &path) { - std::string errMsg; - auto handle = llvm::sys::DynamicLibrary::getPermanentLibrary(path.c_str(), &errMsg); +#if __APPLE__ + const std::string libExt = "dylib"; +#else + const std::string libExt = "so"; +#endif - if (!handle.isValid()) - return Error::NOT_FOUND; + fs::path tomlPath = path; + auto tml = toml::parse_file(path); + auto about = tml["about"]; + auto library = tml["library"]; - auto *entry = (LoadFunc *)handle.getAddressOfSymbol("load"); - if (!entry) - return Error::NO_ENTRYPOINT; + std::string cppLib = library["cpp"].value_or(""); + std::string dylibPath; + if (!cppLib.empty()) + dylibPath = fs::path(tomlPath) + .replace_filename(library["cpp"].value_or("lib")) + .replace_extension(libExt) + .string(); - auto dsl = (*entry)(); - plugins.push_back(std::make_unique(std::move(dsl), path, handle)); - return load(plugins.back()->dsl.get()); + std::string codonLib = library["codon"].value_or(""); + std::string stdlibPath; + if (!codonLib.empty()) + stdlibPath = fs::path(tomlPath).replace_filename(codonLib).string(); + + DSL::Info info = {about["name"].value_or(""), about["description"].value_or(""), + about["version"].value_or(""), about["url"].value_or(""), + about["supported"].value_or(""), stdlibPath}; + + if (!isVersionSupported(info.supported, CODON_VERSION_MAJOR, CODON_VERSION_MINOR, + CODON_VERSION_PATCH)) + return Error::UNSUPPORTED_VERSION; + + if (!dylibPath.empty()) { + std::string errMsg; + auto handle = + llvm::sys::DynamicLibrary::getPermanentLibrary(dylibPath.c_str(), &errMsg); + if (!handle.isValid()) + return Error::NOT_FOUND; + + auto *entry = (LoadFunc *)handle.getAddressOfSymbol("load"); + if (!entry) + return Error::NO_ENTRYPOINT; + + auto dsl = (*entry)(); + plugins.push_back(std::make_unique(std::move(dsl), info, handle)); + return load(plugins.back()->dsl.get()); + } else { + plugins.push_back(std::make_unique(std::make_unique(), info, + llvm::sys::DynamicLibrary())); + return Error::NONE; + } } PluginManager::Error PluginManager::load(DSL *dsl) { - if (!dsl || !dsl->isVersionSupported(CODON_VERSION_MAJOR, CODON_VERSION_MINOR, - CODON_VERSION_PATCH)) - return Error::UNSUPPORTED_VERSION; dsl->addIRPasses(pm, debug); return Error::NONE; } diff --git a/codon/dsl/plugins.h b/codon/dsl/plugins.h index 340fdb4c..a58b449f 100644 --- a/codon/dsl/plugins.h +++ b/codon/dsl/plugins.h @@ -15,14 +15,13 @@ namespace codon { struct Plugin { /// the associated DSL std::unique_ptr dsl; - /// plugin load path - std::string path; + /// plugin information + DSL::Info info; /// library handle llvm::sys::DynamicLibrary handle; - Plugin(std::unique_ptr dsl, const std::string &path, - const llvm::sys::DynamicLibrary &handle) - : dsl(std::move(dsl)), path(path), handle(handle) {} + Plugin(std::unique_ptr dsl, DSL::Info info, llvm::sys::DynamicLibrary handle) + : dsl(std::move(dsl)), info(std::move(info)), handle(std::move(handle)) {} }; /// Manager for loading, applying and unloading plugins. @@ -36,8 +35,6 @@ private: bool debug; public: - using LoadFunc = std::function()>; - /// Error codes when loading plugins enum Error { NONE = 0, NOT_FOUND, NO_ENTRYPOINT, UNSUPPORTED_VERSION }; diff --git a/codon/parser/cache.h b/codon/parser/cache.h index e682057f..490a95ea 100644 --- a/codon/parser/cache.h +++ b/codon/parser/cache.h @@ -192,6 +192,9 @@ struct Cache : public std::enable_shared_from_this { std::function> customExprStmts; + /// Plugin-added import paths + std::vector pluginImportPaths; + public: explicit Cache(std::string argv0 = ""); diff --git a/codon/parser/common.cpp b/codon/parser/common.cpp index 846acefb..751d5810 100644 --- a/codon/parser/common.cpp +++ b/codon/parser/common.cpp @@ -198,23 +198,29 @@ std::string executable_path(const char *argv0) { return std::string(argv0); } std::shared_ptr getImportFile(const std::string &argv0, const std::string &what, const std::string &relativeTo, - bool forceStdlib, - const std::string &module0) { + bool forceStdlib, const std::string &module0, + const std::vector &plugins) { using fmt::format; - auto getStdLibPaths = [](const std::string &argv0) { + auto getStdLibPaths = [](const std::string &argv0, + const std::vector &plugins) { std::vector paths; char abs[PATH_MAX + 1]; if (auto c = getenv("CODON_PATH")) { if (realpath(c, abs)) paths.push_back(abs); } - if (!argv0.empty()) + if (!argv0.empty()) { for (auto loci : {"../lib/codon/stdlib", "../stdlib", "stdlib"}) { strncpy(abs, executable_path(argv0.c_str()).c_str(), PATH_MAX); if (realpath(format("{}/{}", dirname(abs), loci).c_str(), abs)) paths.push_back(abs); } + } + for (auto &path : plugins) { + if (realpath(path.c_str(), abs)) + paths.push_back(abs); + } return paths; }; @@ -224,7 +230,7 @@ std::shared_ptr getImportFile(const std::string &argv0, auto getRoot = [&](const std::string &s) { bool isStdLib = false; std::string root; - for (auto &p : getStdLibPaths(argv0)) + for (auto &p : getStdLibPaths(argv0, plugins)) if (startswith(s, p)) { root = p; isStdLib = true; @@ -248,7 +254,7 @@ std::shared_ptr getImportFile(const std::string &argv0, paths.push_back(format("{}/{}.codon", parent, what)); paths.push_back(format("{}/{}/__init__.codon", parent, what)); } - for (auto &p : getStdLibPaths(argv0)) { + for (auto &p : getStdLibPaths(argv0, plugins)) { paths.push_back(format("{}/{}.codon", p, what)); paths.push_back(format("{}/{}/__init__.codon", p, what)); } diff --git a/codon/parser/common.h b/codon/parser/common.h index d6ec4b46..eb3abb47 100644 --- a/codon/parser/common.h +++ b/codon/parser/common.h @@ -184,7 +184,8 @@ std::shared_ptr getImportFile(const std::string &argv0, const std::string &what, const std::string &relativeTo, bool forceStdlib = false, - const std::string &module0 = ""); + const std::string &module0 = "", + const std::vector &plugins = {}); } // namespace ast } // namespace codon diff --git a/codon/parser/parser.cpp b/codon/parser/parser.cpp index e0bacd41..6b7efeb8 100644 --- a/codon/parser/parser.cpp +++ b/codon/parser/parser.cpp @@ -49,6 +49,9 @@ ir::Module *parse(const std::string &argv0, const std::string &file, auto cache = std::make_shared(argv0); if (plm) { for (auto *plugin : *plm) { + if (!plugin->info.stdlibPath.empty()) + cache->pluginImportPaths.push_back(plugin->info.stdlibPath); + for (auto &kw : plugin->dsl->getExprKeywords()) { cache->customExprStmts[kw.keyword] = kw.callback; } diff --git a/codon/parser/visitors/simplify/simplify_stmt.cpp b/codon/parser/visitors/simplify/simplify_stmt.cpp index 28dc643d..6feb2c32 100644 --- a/codon/parser/visitors/simplify/simplify_stmt.cpp +++ b/codon/parser/visitors/simplify/simplify_stmt.cpp @@ -373,7 +373,7 @@ void SimplifyVisitor::visit(ImportStmt *stmt) { path += dirs[i] + (i ? "/" : ""); // Fetch the import! auto file = getImportFile(ctx->cache->argv0, path, ctx->getFilename(), false, - ctx->cache->module0); + ctx->cache->module0, ctx->cache->pluginImportPaths); if (!file) error("cannot locate import '{}'", join(dirs, ".")); diff --git a/codon/util/semver/semver.h b/codon/util/semver/semver.h new file mode 100644 index 00000000..127b9462 --- /dev/null +++ b/codon/util/semver/semver.h @@ -0,0 +1,837 @@ +// _____ _ _ +// / ____| | | (_) +// | (___ ___ _ __ ___ __ _ _ __ | |_ _ ___ +// \___ \ / _ \ '_ ` _ \ / _` | '_ \| __| |/ __| +// ____) | __/ | | | | | (_| | | | | |_| | (__ +// |_____/ \___|_| |_| |_|\__,_|_| |_|\__|_|\___| +// __ __ _ _ _____ +// \ \ / / (_) (_) / ____|_ _ +// \ \ / /__ _ __ ___ _ ___ _ __ _ _ __ __ _ | | _| |_ _| |_ +// \ \/ / _ \ '__/ __| |/ _ \| '_ \| | '_ \ / _` | | | |_ _|_ _| +// \ / __/ | \__ \ | (_) | | | | | | | | (_| | | |____|_| |_| +// \/ \___|_| |___/_|\___/|_| |_|_|_| |_|\__, | \_____| +// https://github.com/Neargye/semver __/ | +// version 0.3.0 |___/ +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2018 - 2021 Daniil Goncharov . +// Copyright (c) 2020 - 2021 Alexander Gorbunov . +// +// 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. + +#ifndef NEARGYE_SEMANTIC_VERSIONING_HPP +#define NEARGYE_SEMANTIC_VERSIONING_HPP + +#define SEMVER_VERSION_MAJOR 0 +#define SEMVER_VERSION_MINOR 3 +#define SEMVER_VERSION_PATCH 0 + +#include +#include +#include +#include +#include +#include +#include +#if __has_include() +#include +#else +#include +#endif + +// Allow to disable exceptions. +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && \ + !defined(SEMVER_NOEXCEPTION) +#include +#define NEARGYE_THROW(exception) throw exception +#else +#include +#define NEARGYE_THROW(exception) std::abort() +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored \ + "-Wmissing-braces" // Ignore warning: suggest braces around initialization of + // subobject 'return {first, std::errc::invalid_argument};'. +#endif + +namespace semver { + +enum struct prerelease : std::uint8_t { alpha = 0, beta = 1, rc = 2, none = 3 }; + +#if __has_include() +struct from_chars_result : std::from_chars_result { + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } +}; + +struct to_chars_result : std::to_chars_result { + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } +}; +#else +struct from_chars_result { + const char *ptr; + std::errc ec; + + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } +}; + +struct to_chars_result { + char *ptr; + std::errc ec; + + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } +}; +#endif + +// Max version string length = 3() + 1(.) + 3() + 1(.) + 3() + 1(-) +// + 5() + 1(.) + 3() = 21. +inline constexpr auto max_version_string_length = std::size_t{21}; + +namespace detail { + +inline constexpr auto alpha = std::string_view{"alpha", 5}; +inline constexpr auto beta = std::string_view{"beta", 4}; +inline constexpr auto rc = std::string_view{"rc", 2}; + +// Min version string length = 1() + 1(.) + 1() + 1(.) + 1() = 5. +inline constexpr auto min_version_string_length = 5; + +constexpr char to_lower(char c) noexcept { + return (c >= 'A' && c <= 'Z') ? static_cast(c + ('a' - 'A')) : c; +} + +constexpr bool is_digit(char c) noexcept { return c >= '0' && c <= '9'; } + +constexpr bool is_space(char c) noexcept { return c == ' '; } + +constexpr bool is_operator(char c) noexcept { return c == '<' || c == '>' || c == '='; } + +constexpr bool is_dot(char c) noexcept { return c == '.'; } + +constexpr bool is_logical_or(char c) noexcept { return c == '|'; } + +constexpr bool is_hyphen(char c) noexcept { return c == '-'; } + +constexpr bool is_letter(char c) noexcept { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +constexpr std::uint8_t to_digit(char c) noexcept { + return static_cast(c - '0'); +} + +constexpr std::uint8_t length(std::uint8_t x) noexcept { + return x < 10 ? 1 : (x < 100 ? 2 : 3); +} + +constexpr std::uint8_t length(prerelease t) noexcept { + if (t == prerelease::alpha) { + return static_cast(alpha.length()); + } else if (t == prerelease::beta) { + return static_cast(beta.length()); + } else if (t == prerelease::rc) { + return static_cast(rc.length()); + } + + return 0; +} + +constexpr bool equals(const char *first, const char *last, + std::string_view str) noexcept { + for (std::size_t i = 0; first != last && i < str.length(); ++i, ++first) { + if (to_lower(*first) != to_lower(str[i])) { + return false; + } + } + + return true; +} + +constexpr char *to_chars(char *str, std::uint8_t x, bool dot = true) noexcept { + do { + *(--str) = static_cast('0' + (x % 10)); + x /= 10; + } while (x != 0); + + if (dot) { + *(--str) = '.'; + } + + return str; +} + +constexpr char *to_chars(char *str, prerelease t) noexcept { + const auto p = t == prerelease::alpha ? alpha + : t == prerelease::beta ? beta + : t == prerelease::rc ? rc + : std::string_view{}; + + if (p.size() > 0) { + for (auto it = p.rbegin(); it != p.rend(); ++it) { + *(--str) = *it; + } + *(--str) = '-'; + } + + return str; +} + +constexpr const char *from_chars(const char *first, const char *last, + std::uint8_t &d) noexcept { + if (first != last && is_digit(*first)) { + std::int32_t t = 0; + for (; first != last && is_digit(*first); ++first) { + t = t * 10 + to_digit(*first); + } + if (t <= (std::numeric_limits::max)()) { + d = static_cast(t); + return first; + } + } + + return nullptr; +} + +constexpr const char *from_chars(const char *first, const char *last, + prerelease &p) noexcept { + if (is_hyphen(*first)) { + ++first; + } + + if (equals(first, last, alpha)) { + p = prerelease::alpha; + return first + alpha.length(); + } else if (equals(first, last, beta)) { + p = prerelease::beta; + return first + beta.length(); + } else if (equals(first, last, rc)) { + p = prerelease::rc; + return first + rc.length(); + } + + return nullptr; +} + +constexpr bool check_delimiter(const char *first, const char *last, char d) noexcept { + return first != last && first != nullptr && *first == d; +} + +} // namespace detail + +struct version { + std::uint8_t major = 0; + std::uint8_t minor = 1; + std::uint8_t patch = 0; + prerelease prerelease_type = prerelease::none; + std::uint8_t prerelease_number = 0; + + constexpr version(std::uint8_t mj, std::uint8_t mn, std::uint8_t pt, + prerelease prt = prerelease::none, std::uint8_t prn = 0) noexcept + : major{mj}, minor{mn}, patch{pt}, prerelease_type{prt}, + prerelease_number{prt == prerelease::none ? static_cast(0) + : prn} {} + + explicit constexpr version(std::string_view str) + : version(0, 0, 0, prerelease::none, 0) { + from_string(str); + } + + constexpr version() = + default; // https://semver.org/#how-should-i-deal-with-revisions-in-the-0yz-initial-development-phase + + constexpr version(const version &) = default; + + constexpr version(version &&) = default; + + ~version() = default; + + version &operator=(const version &) = default; + + version &operator=(version &&) = default; + + [[nodiscard]] constexpr from_chars_result from_chars(const char *first, + const char *last) noexcept { + if (first == nullptr || last == nullptr || + (last - first) < detail::min_version_string_length) { + return {first, std::errc::invalid_argument}; + } + + auto next = first; + if (next = detail::from_chars(next, last, major); + detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, minor); + detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, patch); next == last) { + prerelease_type = prerelease::none; + prerelease_number = 0; + return {next, std::errc{}}; + } else if (detail::check_delimiter(next, last, '-')) { + if (next = detail::from_chars(next, last, prerelease_type); next == last) { + prerelease_number = 0; + return {next, std::errc{}}; + } else if (detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, prerelease_number); + next == last) { + return {next, std::errc{}}; + } + } + } + } + } + + return {first, std::errc::invalid_argument}; + } + + [[nodiscard]] constexpr to_chars_result to_chars(char *first, + char *last) const noexcept { + const auto length = string_length(); + if (first == nullptr || last == nullptr || (last - first) < length) { + return {last, std::errc::value_too_large}; + } + + auto next = first + length; + if (prerelease_type != prerelease::none) { + if (prerelease_number != 0) { + next = detail::to_chars(next, prerelease_number); + } + next = detail::to_chars(next, prerelease_type); + } + next = detail::to_chars(next, patch); + next = detail::to_chars(next, minor); + next = detail::to_chars(next, major, false); + + return {first + length, std::errc{}}; + } + + [[nodiscard]] constexpr bool from_string_noexcept(std::string_view str) noexcept { + return from_chars(str.data(), str.data() + str.length()); + } + + constexpr version &from_string(std::string_view str) { + if (!from_string_noexcept(str)) { + NEARGYE_THROW( + std::invalid_argument{"semver::version::from_string invalid version."}); + } + + return *this; + } + + [[nodiscard]] std::string to_string() const { + auto str = std::string(string_length(), '\0'); + if (!to_chars(str.data(), str.data() + str.length())) { + NEARGYE_THROW( + std::invalid_argument{"semver::version::to_string invalid version."}); + } + + return str; + } + + [[nodiscard]] constexpr std::uint8_t string_length() const noexcept { + // () + 1(.) + () + 1(.) + () + auto length = + detail::length(major) + detail::length(minor) + detail::length(patch) + 2; + if (prerelease_type != prerelease::none) { + // + 1(-) + () + length += detail::length(prerelease_type) + 1; + if (prerelease_number != 0) { + // + 1(.) + () + length += detail::length(prerelease_number) + 1; + } + } + + return static_cast(length); + } + + [[nodiscard]] constexpr int compare(const version &other) const noexcept { + if (major != other.major) { + return major - other.major; + } + + if (minor != other.minor) { + return minor - other.minor; + } + + if (patch != other.patch) { + return patch - other.patch; + } + + if (prerelease_type != other.prerelease_type) { + return static_cast(prerelease_type) - + static_cast(other.prerelease_type); + } + + if (prerelease_number != other.prerelease_number) { + return prerelease_number - other.prerelease_number; + } + + return 0; + } +}; + +[[nodiscard]] constexpr bool operator==(const version &lhs, + const version &rhs) noexcept { + return lhs.compare(rhs) == 0; +} + +[[nodiscard]] constexpr bool operator!=(const version &lhs, + const version &rhs) noexcept { + return lhs.compare(rhs) != 0; +} + +[[nodiscard]] constexpr bool operator>(const version &lhs, + const version &rhs) noexcept { + return lhs.compare(rhs) > 0; +} + +[[nodiscard]] constexpr bool operator>=(const version &lhs, + const version &rhs) noexcept { + return lhs.compare(rhs) >= 0; +} + +[[nodiscard]] constexpr bool operator<(const version &lhs, + const version &rhs) noexcept { + return lhs.compare(rhs) < 0; +} + +[[nodiscard]] constexpr bool operator<=(const version &lhs, + const version &rhs) noexcept { + return lhs.compare(rhs) <= 0; +} + +[[nodiscard]] constexpr version operator""_version(const char *str, + std::size_t length) { + return version{std::string_view{str, length}}; +} + +[[nodiscard]] constexpr bool valid(std::string_view str) noexcept { + return version{}.from_string_noexcept(str); +} + +[[nodiscard]] constexpr from_chars_result +from_chars(const char *first, const char *last, version &v) noexcept { + return v.from_chars(first, last); +} + +[[nodiscard]] constexpr to_chars_result to_chars(char *first, char *last, + const version &v) noexcept { + return v.to_chars(first, last); +} + +[[nodiscard]] constexpr std::optional +from_string_noexcept(std::string_view str) noexcept { + if (version v{}; v.from_string_noexcept(str)) { + return v; + } + + return std::nullopt; +} + +[[nodiscard]] constexpr version from_string(std::string_view str) { + return version{str}; +} + +[[nodiscard]] inline std::string to_string(const version &v) { return v.to_string(); } + +template +inline std::basic_ostream & +operator<<(std::basic_ostream &os, const version &v) { + for (const auto c : v.to_string()) { + os.put(c); + } + + return os; +} + +inline namespace comparators { + +enum struct comparators_option : std::uint8_t { + exclude_prerelease, + include_prerelease +}; + +[[nodiscard]] constexpr int +compare(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept { + if (option == comparators_option::exclude_prerelease) { + return version{lhs.major, lhs.minor, lhs.patch}.compare( + version{rhs.major, rhs.minor, rhs.patch}); + } + return lhs.compare(rhs); +} + +[[nodiscard]] constexpr bool +equal_to(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) == 0; +} + +[[nodiscard]] constexpr bool not_equal_to( + const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) != 0; +} + +[[nodiscard]] constexpr bool +greater(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) > 0; +} + +[[nodiscard]] constexpr bool greater_equal( + const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) >= 0; +} + +[[nodiscard]] constexpr bool +less(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) < 0; +} + +[[nodiscard]] constexpr bool less_equal( + const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) <= 0; +} + +} // namespace comparators + +namespace range { + +namespace detail { + +using namespace semver::detail; + +class range { +public: + constexpr explicit range(std::string_view str) noexcept : str_{str} {} + + constexpr bool satisfies(const version &ver, bool include_prerelease) const { + range_parser parser{str_}; + + auto is_logical_or = [&parser]() constexpr noexcept->bool { + return parser.current_token.type == range_token_type::logical_or; + }; + + auto is_operator = [&parser]() constexpr noexcept->bool { + return parser.current_token.type == range_token_type::range_operator; + }; + + auto is_number = [&parser]() constexpr noexcept->bool { + return parser.current_token.type == range_token_type::number; + }; + + const bool has_prerelease = ver.prerelease_type != prerelease::none; + + do { + if (is_logical_or()) { + parser.advance_token(range_token_type::logical_or); + } + + bool contains = true; + bool allow_compare = include_prerelease; + + while (is_operator() || is_number()) { + const auto range = parser.parse_range(); + const bool equal_without_tags = + equal_to(range.ver, ver, comparators_option::exclude_prerelease); + + if (has_prerelease && equal_without_tags) { + allow_compare = true; + } + + if (!range.satisfies(ver)) { + contains = false; + break; + } + } + + if (has_prerelease) { + if (allow_compare && contains) { + return true; + } + } else if (contains) { + return true; + } + + } while (is_logical_or()); + + return false; + } + +private: + enum struct range_operator : std::uint8_t { + less, + less_or_equal, + greater, + greater_or_equal, + equal + }; + + struct range_comparator { + range_operator op; + version ver; + + constexpr bool satisfies(const version &version) const { + switch (op) { + case range_operator::equal: + return version == ver; + case range_operator::greater: + return version > ver; + case range_operator::greater_or_equal: + return version >= ver; + case range_operator::less: + return version < ver; + case range_operator::less_or_equal: + return version <= ver; + default: + NEARGYE_THROW(std::invalid_argument{"semver::range unexpected operator."}); + } + } + }; + + enum struct range_token_type : std::uint8_t { + none, + number, + range_operator, + dot, + logical_or, + hyphen, + prerelease, + end_of_line + }; + + struct range_token { + range_token_type type = range_token_type::none; + std::uint8_t number = 0; + range_operator op = range_operator::equal; + prerelease prerelease_type = prerelease::none; + }; + + struct range_lexer { + std::string_view text; + std::size_t pos; + + constexpr explicit range_lexer(std::string_view text) noexcept + : text{text}, pos{0} {} + + constexpr range_token get_next_token() noexcept { + while (!end_of_line()) { + + if (is_space(text[pos])) { + advance(1); + continue; + } + + if (is_logical_or(text[pos])) { + advance(2); + return {range_token_type::logical_or}; + } + + if (is_operator(text[pos])) { + const auto op = get_operator(); + return {range_token_type::range_operator, 0, op}; + } + + if (is_digit(text[pos])) { + const auto number = get_number(); + return {range_token_type::number, number}; + } + + if (is_dot(text[pos])) { + advance(1); + return {range_token_type::dot}; + } + + if (is_hyphen(text[pos])) { + advance(1); + return {range_token_type::hyphen}; + } + + if (is_letter(text[pos])) { + const auto prerelease = get_prerelease(); + return {range_token_type::prerelease, 0, range_operator::equal, prerelease}; + } + } + + return {range_token_type::end_of_line}; + } + + constexpr bool end_of_line() const noexcept { return pos >= text.length(); } + + constexpr void advance(std::size_t i) noexcept { pos += i; } + + constexpr range_operator get_operator() noexcept { + if (text[pos] == '<') { + advance(1); + if (text[pos] == '=') { + advance(1); + return range_operator::less_or_equal; + } + return range_operator::less; + } else if (text[pos] == '>') { + advance(1); + if (text[pos] == '=') { + advance(1); + return range_operator::greater_or_equal; + } + return range_operator::greater; + } else if (text[pos] == '=') { + advance(1); + return range_operator::equal; + } + + return range_operator::equal; + } + + constexpr std::uint8_t get_number() noexcept { + const auto first = text.data() + pos; + const auto last = text.data() + text.length(); + if (std::uint8_t n{}; from_chars(first, last, n) != nullptr) { + advance(length(n)); + return n; + } + + return 0; + } + + constexpr prerelease get_prerelease() noexcept { + const auto first = text.data() + pos; + const auto last = text.data() + text.length(); + if (first > last) { + advance(1); + return prerelease::none; + } + + if (prerelease p{}; from_chars(first, last, p) != nullptr) { + advance(length(p)); + return p; + } + + advance(1); + + return prerelease::none; + } + }; + + struct range_parser { + range_lexer lexer; + range_token current_token; + + constexpr explicit range_parser(std::string_view str) + : lexer{str}, current_token{range_token_type::none} { + advance_token(range_token_type::none); + } + + constexpr void advance_token(range_token_type token_type) { + if (current_token.type != token_type) { + NEARGYE_THROW(std::invalid_argument{"semver::range unexpected token."}); + } + current_token = lexer.get_next_token(); + } + + constexpr range_comparator parse_range() { + if (current_token.type == range_token_type::number) { + const auto version = parse_version(); + return {range_operator::equal, version}; + } else if (current_token.type == range_token_type::range_operator) { + const auto range_operator = current_token.op; + advance_token(range_token_type::range_operator); + const auto version = parse_version(); + return {range_operator, version}; + } + + return {range_operator::equal, version{}}; + } + + constexpr version parse_version() { + const auto major = parse_number(); + + advance_token(range_token_type::dot); + const auto minor = parse_number(); + + advance_token(range_token_type::dot); + const auto patch = parse_number(); + + prerelease prerelease = prerelease::none; + std::uint8_t prerelease_number = 0; + + if (current_token.type == range_token_type::hyphen) { + advance_token(range_token_type::hyphen); + prerelease = parse_prerelease(); + advance_token(range_token_type::dot); + prerelease_number = parse_number(); + } + + return {major, minor, patch, prerelease, prerelease_number}; + } + + constexpr std::uint8_t parse_number() { + const auto token = current_token; + advance_token(range_token_type::number); + + return token.number; + } + + constexpr prerelease parse_prerelease() { + const auto token = current_token; + advance_token(range_token_type::prerelease); + + return token.prerelease_type; + } + }; + + std::string_view str_; +}; + +} // namespace detail + +enum struct satisfies_option : std::uint8_t { exclude_prerelease, include_prerelease }; + +constexpr bool +satisfies(const version &ver, std::string_view str, + satisfies_option option = satisfies_option::exclude_prerelease) { + switch (option) { + case satisfies_option::exclude_prerelease: + return detail::range{str}.satisfies(ver, false); + case satisfies_option::include_prerelease: + return detail::range{str}.satisfies(ver, true); + default: + NEARGYE_THROW(std::invalid_argument{"semver::range unexpected satisfies_option."}); + } +} + +} // namespace range + +// Version lib semver. +inline constexpr auto semver_version = + version{SEMVER_VERSION_MAJOR, SEMVER_VERSION_MINOR, SEMVER_VERSION_PATCH}; + +} // namespace semver + +#undef NEARGYE_THROW + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // NEARGYE_SEMANTIC_VERSIONING_HPP diff --git a/codon/util/toml++/toml.h b/codon/util/toml++/toml.h new file mode 100644 index 00000000..9cc7d6ad --- /dev/null +++ b/codon/util/toml++/toml.h @@ -0,0 +1,152 @@ +//# This file is a part of toml++ and is subject to the the terms of the MIT license. +//# Copyright (c) Mark Gillard +//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license +// text. +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_TOMLPLUSPLUS_H +#define INCLUDE_TOMLPLUSPLUS_H + +//# Note: most of these would be included transitively but +//# they're listed explicitly here because this file +//# is used as the source for generate_single_header.py. + +#include "toml_preprocessor.h" + +TOML_PUSH_WARNINGS; +TOML_DISABLE_SPAM_WARNINGS; + +#include "toml_array.h" +#include "toml_common.h" +#include "toml_date_time.h" +#include "toml_node.h" +#include "toml_node_view.h" +#include "toml_print_to_stream.h" +#include "toml_table.h" +#include "toml_utf8.h" +#include "toml_value.h" +#if TOML_PARSER +#include "toml_parse_error.h" +#include "toml_parse_result.h" +#include "toml_parser.h" +#include "toml_utf8_streams.h" +#endif // TOML_PARSER +#include "toml_default_formatter.h" +#include "toml_formatter.h" +#include "toml_json_formatter.h" + +#if TOML_IMPLEMENTATION +#include "toml_array.hpp" +#include "toml_node.hpp" +#include "toml_table.hpp" +#if TOML_PARSER +#include "toml_parser.hpp" +#include "toml_utf8_streams.hpp" +#endif // TOML_PARSER +#include "toml_default_formatter.hpp" +#include "toml_json_formatter.hpp" +#if !TOML_HEADER_ONLY +#include "toml_instantiations.hpp" +#endif // !TOML_HEADER_ONLY +#endif // TOML_IMPLEMENTATION + +TOML_POP_WARNINGS; // TOML_DISABLE_SPAM_WARNINGS + +// macro hygiene +#if TOML_UNDEF_MACROS +#undef TOML_ABI_NAMESPACES +#undef TOML_ABI_NAMESPACE_BOOL +#undef TOML_ABI_NAMESPACE_END +#undef TOML_ABI_NAMESPACE_START +#undef TOML_ABSTRACT_BASE +#undef TOML_ALWAYS_INLINE +#undef TOML_ANON_NAMESPACE +#undef TOML_ANON_NAMESPACE_END +#undef TOML_ANON_NAMESPACE_START +#undef TOML_ARM +#undef TOML_ASSERT +#undef TOML_ASSUME +#undef TOML_ASYMMETRICAL_EQUALITY_OPS +#undef TOML_ATTR +#undef TOML_CLANG +#undef TOML_COMPILER_EXCEPTIONS +#undef TOML_CONCAT +#undef TOML_CONCAT_1 +#undef TOML_CONSTEVAL +#undef TOML_CPP +#undef TOML_DISABLE_ARITHMETIC_WARNINGS +#undef TOML_DISABLE_CODE_ANALYSIS_WARNINGS +#undef TOML_DISABLE_INIT_WARNINGS +#undef TOML_DISABLE_SPAM_WARNINGS +#undef TOML_DISABLE_SHADOW_WARNINGS +#undef TOML_DISABLE_SUGGEST_WARNINGS +#undef TOML_DISABLE_SWITCH_WARNINGS +#undef TOML_DISABLE_WARNINGS +#undef TOML_ENABLE_WARNINGS +#undef TOML_EMPTY_BASES +#undef TOML_EVAL_BOOL_0 +#undef TOML_EVAL_BOOL_1 +#undef TOML_EXTERNAL_LINKAGE +#undef TOML_FLOAT128 +#undef TOML_FLOAT16 +#undef TOML_FLOAT_CHARCONV +#undef TOML_FP16 +#undef TOML_GCC +#undef TOML_HAS_ATTR +#undef TOML_HAS_CUSTOM_OPTIONAL_TYPE +#undef TOML_HAS_CHAR8 +#undef TOML_HAS_INCLUDE +#undef TOML_ICC +#undef TOML_ICC_CL +#undef TOML_IMPLEMENTATION +#undef TOML_IMPL_NAMESPACE_END +#undef TOML_IMPL_NAMESPACE_START +#undef TOML_INCLUDE_WINDOWS_H +#undef TOML_INT128 +#undef TOML_INTELLISENSE +#undef TOML_INTERNAL_LINKAGE +#undef TOML_INT_CHARCONV +#undef TOML_LANG_AT_LEAST +#undef TOML_LANG_EFFECTIVE_VERSION +#undef TOML_LANG_HIGHER_THAN +#undef TOML_LANG_UNRELEASED +#undef TOML_LAUNDER +#undef TOML_LIFETIME_HOOKS +#undef TOML_LIKELY +#undef TOML_MAKE_FLAGS_ +#undef TOML_MAKE_FLAGS +#undef TOML_MAKE_VERSION +#undef TOML_MAY_THROW +#undef TOML_MSVC +#undef TOML_NAMESPACE +#undef TOML_NAMESPACE_END +#undef TOML_NAMESPACE_START +#undef TOML_NEVER_INLINE +#undef TOML_NODISCARD_CTOR +#undef TOML_NO_DEFAULT_CASE +#undef TOML_PARSER_TYPENAME +#undef TOML_POP_WARNINGS +#undef TOML_PUSH_WARNINGS +#undef TOML_SA_LIST_BEG +#undef TOML_SA_LIST_END +#undef TOML_SA_LIST_NEW +#undef TOML_SA_LIST_NXT +#undef TOML_SA_LIST_SEP +#undef TOML_SA_NATIVE_VALUE_TYPE_LIST +#undef TOML_SA_NEWLINE +#undef TOML_SA_NODE_TYPE_LIST +#undef TOML_SA_UNWRAPPED_NODE_TYPE_LIST +#undef TOML_SA_VALUE_EXACT_FUNC_MESSAGE +#undef TOML_SA_VALUE_FUNC_MESSAGE +#undef TOML_SA_VALUE_MESSAGE_CONST_CHAR8 +#undef TOML_SA_VALUE_MESSAGE_U8STRING_VIEW +#undef TOML_SA_VALUE_MESSAGE_WSTRING +#undef TOML_SIMPLE_STATIC_ASSERT_MESSAGES +#undef TOML_TRIVIAL_ABI +#undef TOML_UINT128 +#undef TOML_UNLIKELY +#undef TOML_UNREACHABLE +#undef TOML_USING_ANON_NAMESPACE +#endif + +#endif // INCLUDE_TOMLPLUSPLUS_H diff --git a/codon/util/toml++/toml_array.h b/codon/util/toml++/toml_array.h new file mode 100644 index 00000000..53a5ce86 --- /dev/null +++ b/codon/util/toml++/toml_array.h @@ -0,0 +1,961 @@ +//# This file is a part of toml++ and is subject to the the terms of the MIT license. +//# Copyright (c) Mark Gillard +//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license +// text. +// SPDX-License-Identifier: MIT + +#pragma once +#include "toml_value.h" + +/// \cond +TOML_IMPL_NAMESPACE_START { + template class TOML_TRIVIAL_ABI array_iterator final { + private: + template friend class array_iterator; + friend class TOML_NAMESPACE::array; + + using raw_mutable_iterator = std::vector>::iterator; + using raw_const_iterator = std::vector>::const_iterator; + using raw_iterator = + std::conditional_t; + + mutable raw_iterator raw_; + + TOML_NODISCARD_CTOR + array_iterator(raw_mutable_iterator raw) noexcept : raw_{raw} {} + + template > + TOML_NODISCARD_CTOR array_iterator(raw_const_iterator raw) noexcept : raw_{raw} {} + + public: + using value_type = std::conditional_t; + using reference = value_type &; + using pointer = value_type *; + using difference_type = ptrdiff_t; + using iterator_category = + typename std::iterator_traits::iterator_category; + + TOML_NODISCARD_CTOR + array_iterator() noexcept = default; + + TOML_NODISCARD_CTOR + array_iterator(const array_iterator &) noexcept = default; + + array_iterator &operator=(const array_iterator &) noexcept = default; + + array_iterator &operator++() noexcept // ++pre + { + ++raw_; + return *this; + } + + array_iterator operator++(int) noexcept // post++ + { + array_iterator out{raw_}; + ++raw_; + return out; + } + + array_iterator &operator--() noexcept // --pre + { + --raw_; + return *this; + } + + array_iterator operator--(int) noexcept // post-- + { + array_iterator out{raw_}; + --raw_; + return out; + } + + [[nodiscard]] reference operator*() const noexcept { return *raw_->get(); } + + [[nodiscard]] pointer operator->() const noexcept { return raw_->get(); } + + array_iterator &operator+=(ptrdiff_t rhs) noexcept { + raw_ += rhs; + return *this; + } + + array_iterator &operator-=(ptrdiff_t rhs) noexcept { + raw_ -= rhs; + return *this; + } + + [[nodiscard]] friend array_iterator operator+(const array_iterator &lhs, + ptrdiff_t rhs) noexcept { + return {lhs.raw_ + rhs}; + } + + [[nodiscard]] friend array_iterator operator+(ptrdiff_t lhs, + const array_iterator &rhs) noexcept { + return {rhs.raw_ + lhs}; + } + + [[nodiscard]] friend array_iterator operator-(const array_iterator &lhs, + ptrdiff_t rhs) noexcept { + return {lhs.raw_ - rhs}; + } + + [[nodiscard]] friend ptrdiff_t operator-(const array_iterator &lhs, + const array_iterator &rhs) noexcept { + return lhs.raw_ - rhs.raw_; + } + + [[nodiscard]] friend bool operator==(const array_iterator &lhs, + const array_iterator &rhs) noexcept { + return lhs.raw_ == rhs.raw_; + } + + [[nodiscard]] friend bool operator!=(const array_iterator &lhs, + const array_iterator &rhs) noexcept { + return lhs.raw_ != rhs.raw_; + } + + [[nodiscard]] friend bool operator<(const array_iterator &lhs, + const array_iterator &rhs) noexcept { + return lhs.raw_ < rhs.raw_; + } + + [[nodiscard]] friend bool operator<=(const array_iterator &lhs, + const array_iterator &rhs) noexcept { + return lhs.raw_ <= rhs.raw_; + } + + [[nodiscard]] friend bool operator>(const array_iterator &lhs, + const array_iterator &rhs) noexcept { + return lhs.raw_ > rhs.raw_; + } + + [[nodiscard]] friend bool operator>=(const array_iterator &lhs, + const array_iterator &rhs) noexcept { + return lhs.raw_ >= rhs.raw_; + } + + [[nodiscard]] reference operator[](ptrdiff_t idx) const noexcept { + return *(raw_ + idx)->get(); + } + + TOML_DISABLE_WARNINGS; + + template > + operator array_iterator() const noexcept { + return array_iterator{raw_}; + } + + TOML_ENABLE_WARNINGS; + }; +} +TOML_IMPL_NAMESPACE_END; +/// \endcond + +TOML_NAMESPACE_START { + /// \brief A RandomAccessIterator for iterating over elements in a toml::array. + using array_iterator = impl::array_iterator; + + /// \brief A RandomAccessIterator for iterating over const elements in a toml::array. + using const_array_iterator = impl::array_iterator; + + /// \brief A TOML array. + /// + /// \detail The interface of this type is modeled after std::vector, with some + /// additional considerations made for the heterogeneous nature of a + /// TOML array. + /// + /// \godbolt{sjK4da} + /// + /// \cpp + /// + /// toml::table tbl = toml::parse(R"( + /// arr = [1, 2, 3, 4, 'five'] + /// )"sv); + /// + /// // get the element as an array + /// toml::array& arr = *tbl.get_as("arr"); + /// std::cout << arr << "\n"; + /// + /// // increment each element with visit() + /// for (auto&& elem : arr) + /// { + /// elem.visit([](auto&& el) noexcept + /// { + /// if constexpr (toml::is_number) + /// (*el)++; + /// else if constexpr (toml::is_string) + /// el = "six"sv; + /// }); + /// } + /// std::cout << arr << "\n"; + /// + /// // add and remove elements + /// arr.push_back(7); + /// arr.push_back(8.0f); + /// arr.push_back("nine"sv); + /// arr.erase(arr.cbegin()); + /// std::cout << arr << "\n"; + /// + /// // emplace elements + /// arr.emplace_back("ten"); + /// arr.emplace_back(11, 12.0); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 2, 3, 4, 'five' ] + /// [ 2, 3, 4, 5, 'six' ] + /// [ 3, 4, 5, 'six', 7, 8.0, 'nine' ] + /// [ 3, 4, 5, 'six', 7, 8.0, 'nine', 'ten', [ 11, 12.0 ] ] + /// \eout + class TOML_API array final : public node { + private: + /// \cond + + friend class TOML_PARSER_TYPENAME; + std::vector> elements; + + void preinsertion_resize(size_t idx, size_t count) noexcept; + + template void emplace_back_if_not_empty_view(T &&val) noexcept { + if constexpr (is_node_view) { + if (!val) + return; + } + elements.emplace_back(impl::make_node(static_cast(val))); + } + +#if TOML_LIFETIME_HOOKS + void lh_ctor() noexcept; + void lh_dtor() noexcept; +#endif + + [[nodiscard]] size_t total_leaf_count() const noexcept; + + void flatten_child(array &&child, size_t &dest_index) noexcept; + /// \endcond + + public: + using value_type = node; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = node &; + using const_reference = const node &; + + /// \brief A RandomAccessIterator for iterating over elements in a toml::array. + using iterator = array_iterator; + /// \brief A RandomAccessIterator for iterating over const elements in a + /// toml::array. + using const_iterator = const_array_iterator; + + /// \brief Default constructor. + TOML_NODISCARD_CTOR + array() noexcept; + + /// \brief Copy constructor. + TOML_NODISCARD_CTOR + array(const array &) noexcept; + + /// \brief Move constructor. + TOML_NODISCARD_CTOR + array(array &&other) noexcept; + + /// \brief Copy-assignment operator. + array &operator=(const array &) noexcept; + + /// \brief Move-assignment operator. + array &operator=(array &&rhs) noexcept; + + /// \brief Destructor. + ~array() noexcept override; + + /// \brief Constructs an array with one or more initial elements. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2.0, "three"sv, toml::array{ 4, 5 } }; + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 2.0, 'three', [ 4, 5 ] ] + /// \eout + /// + /// \remark \parblock If you need to construct an array with one child array + /// element, the array's move constructor will take precedence and perform + /// a move-construction instead. You can use toml::inserter to suppress + /// this behaviour: \cpp + /// // desired result: [ [ 42 ] ] + /// auto bad = toml::array{ toml::array{ 42 } } + /// auto good = toml::array{ toml::inserter{ toml::array{ 42 } } } + /// std::cout << "bad: " << bad << "\n"; + /// std::cout << "good:" << good << "\n"; + /// \ecpp + /// + /// \out + /// bad: [ 42 ] + /// good: [ [ 42 ] ] + /// \eout + /// + /// \endparblock + /// + /// \tparam ElemType One of the TOML node or value types (or a type + /// promotable to one). + /// \tparam ElemTypes One of the TOML node or value types (or a type + /// promotable to one). \param val The node or value used to initialize + /// element 0. \param vals The nodes or values used to initialize + /// elements 1...N. + template 0_sz) || + !std::is_same_v, array>>> + TOML_NODISCARD_CTOR explicit array(ElemType &&val, ElemTypes &&...vals) { + elements.reserve(sizeof...(ElemTypes) + 1_sz); + emplace_back_if_not_empty_view(static_cast(val)); + if constexpr (sizeof...(ElemTypes) > 0) { + (emplace_back_if_not_empty_view(static_cast(vals)), ...); + } + +#if TOML_LIFETIME_HOOKS + lh_ctor(); +#endif + } + + /// \name Type checks + /// @{ + + [[nodiscard]] node_type type() const noexcept override; + [[nodiscard]] bool is_table() const noexcept override; + [[nodiscard]] bool is_array() const noexcept override; + [[nodiscard]] bool is_value() const noexcept override; + [[nodiscard]] array *as_array() noexcept override; + [[nodiscard]] const array *as_array() const noexcept override; + [[nodiscard]] bool is_array_of_tables() const noexcept override; + + [[nodiscard]] bool is_homogeneous(node_type ntype) const noexcept override; + [[nodiscard]] bool is_homogeneous(node_type ntype, + node *&first_nonmatch) noexcept override; + [[nodiscard]] bool + is_homogeneous(node_type ntype, + const node *&first_nonmatch) const noexcept override; + + template + [[nodiscard]] bool is_homogeneous() const noexcept { + using type = impl::unwrap_node; + static_assert(std::is_void_v || + ((impl::is_native || + impl::is_one_of)&&!impl::is_cvref), + "The template type argument of array::is_homogeneous() must be " + "void or one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + return is_homogeneous(impl::node_type_of); + } + + /// @} + + /// \name Array operations + /// @{ + + /// \brief Gets a reference to the element at a specific index. + [[nodiscard]] node &operator[](size_t index) noexcept; + /// \brief Gets a reference to the element at a specific index. + [[nodiscard]] const node &operator[](size_t index) const noexcept; + + /// \brief Returns a reference to the first element in the array. + [[nodiscard]] node &front() noexcept; + /// \brief Returns a reference to the first element in the array. + [[nodiscard]] const node &front() const noexcept; + /// \brief Returns a reference to the last element in the array. + [[nodiscard]] node &back() noexcept; + /// \brief Returns a reference to the last element in the array. + [[nodiscard]] const node &back() const noexcept; + + /// \brief Returns an iterator to the first element. + [[nodiscard]] iterator begin() noexcept; + /// \brief Returns an iterator to the first element. + [[nodiscard]] const_iterator begin() const noexcept; + /// \brief Returns an iterator to the first element. + [[nodiscard]] const_iterator cbegin() const noexcept; + + /// \brief Returns an iterator to one-past-the-last element. + [[nodiscard]] iterator end() noexcept; + /// \brief Returns an iterator to one-past-the-last element. + [[nodiscard]] const_iterator end() const noexcept; + /// \brief Returns an iterator to one-past-the-last element. + [[nodiscard]] const_iterator cend() const noexcept; + + /// \brief Returns true if the array is empty. + [[nodiscard]] bool empty() const noexcept; + /// \brief Returns the number of elements in the array. + [[nodiscard]] size_t size() const noexcept; + /// \brief Reserves internal storage capacity up to a pre-determined number of + /// elements. + void reserve(size_t new_capacity); + /// \brief Removes all elements from the array. + void clear() noexcept; + + /// \brief Returns the maximum number of elements that can be stored in an array on + /// the current platform. + [[nodiscard]] size_t max_size() const noexcept; + /// \brief Returns the current max number of elements that may be held in the + /// array's internal storage. + [[nodiscard]] size_t capacity() const noexcept; + /// \brief Requests the removal of any unused internal storage capacity. + void shrink_to_fit(); + + /// \brief Inserts a new element at a specific position in the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 3 }; + /// arr.insert(arr.cbegin() + 1, "two"); + /// arr.insert(arr.cend(), toml::array{ 4, 5 }); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 'two', 3, [ 4, 5 ] ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, + /// or a native TOML value type (or a type + /// promotable to one). \param pos The insertion position. \param val + /// The node or value being inserted. + /// + /// \returns \conditional_return{Valid input} + /// An iterator to the newly-inserted element. + /// \conditional_return{Input is an empty toml::node_view} + /// end() + /// + /// \attention The return value will always be `end()` if the input value was an + /// empty toml::node_view, because no insertion can take place. This is + /// the only circumstance in which this can occur. + template + iterator insert(const_iterator pos, ElemType &&val) noexcept { + if constexpr (is_node_view) { + if (!val) + return end(); + } + return { + elements.emplace(pos.raw_, impl::make_node(static_cast(val)))}; + } + + /// \brief Repeatedly inserts a new element starting at a specific position in the + /// array. + /// + /// \detail \cpp + /// auto arr = toml::array{ + /// "with an evil twinkle in its eye the goose said", + /// "and immediately we knew peace was never an option." + /// }; + /// arr.insert(arr.cbegin() + 1, 3, "honk"); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ + /// 'with an evil twinkle in its eye the goose said', + /// 'honk', + /// 'honk', + /// 'honk', + /// 'and immediately we knew peace was never an option.' + /// ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, + /// or a native TOML value type (or a type + /// promotable to one). \param pos The insertion position. \param count + /// The number of times the node or value should be inserted. \param val + /// The node or value being inserted. + /// + /// \returns \conditional_return{Valid input} + /// An iterator to the newly-inserted element. + /// \conditional_return{count == 0} + /// A copy of pos + /// \conditional_return{Input is an empty toml::node_view} + /// end() + /// + /// \attention The return value will always be `end()` if the input value was an + /// empty toml::node_view, because no insertion can take place. This is + /// the only circumstance in which this can occur. + template + iterator insert(const_iterator pos, size_t count, ElemType &&val) noexcept { + if constexpr (is_node_view) { + if (!val) + return end(); + } + switch (count) { + case 0: + return {elements.begin() + (pos.raw_ - elements.cbegin())}; + case 1: + return insert(pos, static_cast(val)); + default: { + const auto start_idx = static_cast(pos.raw_ - elements.cbegin()); + preinsertion_resize(start_idx, count); + size_t i = start_idx; + for (size_t e = start_idx + count - 1_sz; i < e; i++) + elements[i].reset(impl::make_node(val)); + + //# potentially move the initial value into the last element + elements[i].reset(impl::make_node(static_cast(val))); + return {elements.begin() + static_cast(start_idx)}; + } + } + } + + /// \brief Inserts a range of elements into the array at a specific position. + /// + /// \tparam Iter An iterator type. Must satisfy ForwardIterator. + /// \param pos The insertion position. + /// \param first Iterator to the first node or value being inserted. + /// \param last Iterator to the one-past-the-last node or value being inserted. + /// + /// \returns \conditional_return{Valid input} + /// An iterator to the first newly-inserted element. + /// \conditional_return{first >= last} + /// A copy of pos + /// \conditional_return{All objects in the range were empty + /// toml::node_views} A copy of pos + template + iterator insert(const_iterator pos, Iter first, Iter last) noexcept { + const auto distance = std::distance(first, last); + if (distance <= 0) + return {elements.begin() + (pos.raw_ - elements.cbegin())}; + else { + auto count = distance; + using deref_type = decltype(*first); + if constexpr (is_node_view) { + for (auto it = first; it != last; it++) + if (!(*it)) + count--; + if (!count) + return {elements.begin() + (pos.raw_ - elements.cbegin())}; + } + const auto start_idx = static_cast(pos.raw_ - elements.cbegin()); + preinsertion_resize(start_idx, static_cast(count)); + size_t i = start_idx; + for (auto it = first; it != last; it++) { + if constexpr (is_node_view) { + if (!(*it)) + continue; + } + if constexpr (std::is_rvalue_reference_v) + elements[i++].reset(impl::make_node(std::move(*it))); + else + elements[i++].reset(impl::make_node(*it)); + } + return {elements.begin() + static_cast(start_idx)}; + } + } + + /// \brief Inserts a range of elements into the array at a specific position. + /// + /// \tparam ElemType toml::node_view, toml::table, toml::array, or a native + /// TOML value type (or a type promotable to + /// one). \param pos The insertion position. \param + /// ilist An initializer list containing the values to be inserted. + /// + /// \returns \conditional_return{Valid input} + /// An iterator to the first newly-inserted element. + /// \conditional_return{Input list is empty} + /// A copy of pos + /// \conditional_return{All objects in the list were empty + /// toml::node_views} + /// A copy of pos + template + iterator insert(const_iterator pos, + std::initializer_list ilist) noexcept { + return insert(pos, ilist.begin(), ilist.end()); + } + + /// \brief Emplaces a new element at a specific position in the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2 }; + /// + /// //add a string using std::string's substring constructor + /// arr.emplace(arr.cbegin() + 1, "this is not a drill"sv, 14, 5); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 'drill', 2 ] + /// \eout + /// + /// \tparam ElemType toml::table, toml::array, or any native TOML value type. + /// \tparam Args Value constructor argument types. + /// \param pos The insertion position. + /// \param args Arguments to forward to the value's constructor. + /// + /// \returns An iterator to the inserted element. + /// + /// \remarks There is no difference between insert() and emplace() + /// for trivial value types (floats, ints, bools). + template + iterator emplace(const_iterator pos, Args &&...args) noexcept { + using type = impl::unwrap_node; + static_assert((impl::is_native || + impl::is_one_of)&&!impl::is_cvref, + "Emplacement type parameter must be one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + return {elements.emplace( + pos.raw_, new impl::wrap_node{static_cast(args)...})}; + } + + /// \brief Removes the specified element from the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2, 3 }; + /// std::cout << arr << "\n"; + /// + /// arr.erase(arr.cbegin() + 1); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 2, 3 ] + /// [ 1, 3 ] + /// \eout + /// + /// \param pos Iterator to the element being erased. + /// + /// \returns Iterator to the first element immediately following the removed + /// element. + iterator erase(const_iterator pos) noexcept; + + /// \brief Removes the elements in the range [first, last) from the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, "bad", "karma" 2 }; + /// std::cout << arr << "\n"; + /// + /// arr.erase(arr.cbegin() + 1, arr.cbegin() + 3); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 'bad', 'karma', 3 ] + /// [ 1, 3 ] + /// \eout + /// + /// \param first Iterator to the first element being erased. + /// \param last Iterator to the one-past-the-last element being erased. + /// + /// \returns Iterator to the first element immediately following the last removed + /// element. + iterator erase(const_iterator first, const_iterator last) noexcept; + + /// \brief Resizes the array. + /// + /// \detail \godbolt{W5zqx3} + /// + /// \cpp + /// auto arr = toml::array{ 1, 2, 3 }; + /// std::cout << arr << "\n"; + /// + /// arr.resize(6, 42); + /// std::cout << arr << "\n"; + /// + /// arr.resize(2, 0); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 2, 3 ] + /// [ 1, 2, 3, 42, 42, 42 ] + /// [ 1, 2 ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::table, toml::array, or a native TOML + /// value type (or a type promotable to one). + /// + /// \param new_size The number of elements the array will + /// have after resizing. + /// \param default_init_val The node or value used to initialize new + /// elements if the array needs to grow. + template + void resize(size_t new_size, ElemType &&default_init_val) noexcept { + static_assert(!is_node_view, + "The default element type argument to toml::array::resize may not " + "be toml::node_view."); + + if (!new_size) + elements.clear(); + else if (new_size < elements.size()) + elements.resize(new_size); + else if (new_size > elements.size()) + insert(cend(), new_size - elements.size(), + static_cast(default_init_val)); + } + + /// \brief Shrinks the array to the given size. + /// + /// \detail \godbolt{rxEzK5} + /// + /// \cpp + /// auto arr = toml::array{ 1, 2, 3 }; + /// std::cout << arr << "\n"; + /// + /// arr.truncate(5); // no-op + /// std::cout << arr << "\n"; + /// + /// arr.truncate(1); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 2, 3 ] + /// [ 1, 2, 3 ] + /// [ 1] + /// \eout + /// + /// \remarks Does nothing if the requested size is larger than or equal to + /// the current size. + void truncate(size_t new_size); + + /// \brief Appends a new element to the end of the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2 }; + /// arr.push_back(3); + /// arr.push_back(4.0); + /// arr.push_back(toml::array{ 5, "six"sv }); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 2, 3, 4.0, [ 5, 'six' ] ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, + /// or a native TOML value type + /// \param val The node or value being added. + /// + /// \attention No insertion takes place if the input value is an empty + /// toml::node_view. This is the only circumstance in which + /// this can occur. + template void push_back(ElemType &&val) noexcept { + emplace_back_if_not_empty_view(static_cast(val)); + } + + /// \brief Emplaces a new element at the end of the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2 }; + /// arr.emplace_back(3, "four"sv); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 2, [ 3, 'four' ] ] + /// \eout + /// + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \tparam Args Value constructor argument types. + /// \param args Arguments to forward to the value's constructor. + /// + /// \returns A reference to the newly-constructed element. + /// + /// \remarks There is no difference between push_back() and emplace_back() + /// For trivial value types (floats, ints, bools). + template + decltype(auto) emplace_back(Args &&...args) noexcept { + using type = impl::unwrap_node; + static_assert((impl::is_native || + impl::is_one_of)&&!impl::is_cvref, + "Emplacement type parameter must be one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + auto nde = new impl::wrap_node{static_cast(args)...}; + elements.emplace_back(nde); + return *nde; + } + + /// \brief Removes the last element from the array. + void pop_back() noexcept; + + /// \brief Gets the element at a specific index. + /// + /// \detail \cpp + /// auto arr = toml::array{ 99, "bottles of beer on the wall" }; + /// std::cout << "element [0] exists: "sv << !!arr.get(0) << "\n"; + /// std::cout << "element [1] exists: "sv << !!arr.get(1) << "\n"; + /// std::cout << "element [2] exists: "sv << !!arr.get(2) << "\n"; + /// if (toml::node* val = arr.get(0)) + /// std::cout << "element [0] is an "sv << val->type() << "\n"; + /// + /// \ecpp + /// + /// \out + /// element [0] exists: true + /// element [1] exists: true + /// element [2] exists: false + /// element [0] is an integer + /// \eout + /// + /// \param index The element's index. + /// + /// \returns A pointer to the element at the specified index if one existed, + /// or nullptr. + [[nodiscard]] node *get(size_t index) noexcept; + + /// \brief Gets the element at a specific index (const overload). + /// + /// \param index The element's index. + /// + /// \returns A pointer to the element at the specified index if one existed, + /// or nullptr. + [[nodiscard]] const node *get(size_t index) const noexcept; + + /// \brief Gets the element at a specific index if it is a particular type. + /// + /// \detail \cpp + /// auto arr = toml::array{ 42, "is the meaning of life, apparently."sv }; + /// if (toml::value* val = arr.get_as(0)) + /// std::cout << "element [0] is an integer with value "sv << *val << "\n"; + /// + /// \ecpp + /// + /// \out + /// element [0] is an integer with value 42 + /// \eout + /// + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \param index The element's index. + /// + /// \returns A pointer to the selected element if it existed and was of the + /// specified type, or nullptr. + template + [[nodiscard]] impl::wrap_node *get_as(size_t index) noexcept { + if (auto val = get(index)) + return val->as(); + return nullptr; + } + + /// \brief Gets the element at a specific index if it is a particular type (const + /// overload). + /// + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \param index The element's index. + /// + /// \returns A pointer to the selected element if it existed and was of the + /// specified type, or nullptr. + template + [[nodiscard]] const impl::wrap_node *get_as(size_t index) const noexcept { + if (auto val = get(index)) + return val->as(); + return nullptr; + } + + /// \brief Flattens this array, recursively hoisting the contents of child arrays + /// up into itself. + /// + /// \detail \cpp + /// + /// auto arr = toml::array{ 1, 2, toml::array{ 3, 4, toml::array{ 5 } }, 6, + /// toml::array{} }; std::cout << arr << "\n"; + /// + /// arr.flatten(); + /// std::cout << arr << "\n"; + /// + /// \ecpp + /// + /// \out + /// [ 1, 2, [ 3, 4, [ 5 ] ], 6, [] ] + /// [ 1, 2, 3, 4, 5, 6 ] + /// \eout + /// + /// \remarks Arrays inside child tables are not flattened. + /// + /// \returns A reference to the array. + array &flatten() &; + + /// \brief Flattens this array, recursively hoisting the contents of child arrays + /// up into itself (rvalue overload). \returns An rvalue reference to the array. + array &&flatten() && { return static_cast(this->flatten()); } + + /// @} + + /// \name Equality + /// @{ + + /// \brief Equality operator. + /// + /// \param lhs The LHS array. + /// \param rhs The RHS array. + /// + /// \returns True if the arrays contained the same elements. + friend bool operator==(const array &lhs, const array &rhs) noexcept; + + /// \brief Inequality operator. + /// + /// \param lhs The LHS array. + /// \param rhs The RHS array. + /// + /// \returns True if the arrays did not contain the same elements. + friend bool operator!=(const array &lhs, const array &rhs) noexcept; + + private: + template + [[nodiscard]] static bool container_equality(const array &lhs, + const T &rhs) noexcept { + using element_type = std::remove_const_t; + static_assert(impl::is_native || + impl::is_losslessly_convertible_to_native, + "Container element type must be (or be promotable to) one of the " + "TOML value types"); + + if (lhs.size() != rhs.size()) + return false; + if (rhs.size() == 0_sz) + return true; + + size_t i{}; + for (auto &list_elem : rhs) { + const auto elem = lhs.get_as>(i++); + if (!elem || *elem != list_elem) + return false; + } + + return true; + } + + public: + /// \brief Initializer list equality operator. + template + [[nodiscard]] friend bool operator==(const array &lhs, + const std::initializer_list &rhs) noexcept { + return container_equality(lhs, rhs); + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const array &, const std::initializer_list &, + template + ); + + /// \brief Vector equality operator. + template + [[nodiscard]] friend bool operator==(const array &lhs, + const std::vector &rhs) noexcept { + return container_equality(lhs, rhs); + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const array &, const std::vector &, + template + ); + + /// @} + + /// \brief Prints the array out to a stream as formatted TOML. + template + friend std::basic_ostream &operator<<(std::basic_ostream &, + const array &); + // implemented in toml_default_formatter.h + }; +} +TOML_NAMESPACE_END; diff --git a/codon/util/toml++/toml_array.hpp b/codon/util/toml++/toml_array.hpp new file mode 100644 index 00000000..7c61b806 --- /dev/null +++ b/codon/util/toml++/toml_array.hpp @@ -0,0 +1,356 @@ +//# This file is a part of toml++ and is subject to the the terms of the MIT license. +//# Copyright (c) Mark Gillard +//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT + +#pragma once +//# {{ +#include "toml_preprocessor.h" +#if !TOML_IMPLEMENTATION + #error This is an implementation-only header. +#endif +//# }} + +#include "toml_array.h" + +TOML_NAMESPACE_START +{ + #if TOML_LIFETIME_HOOKS + + TOML_EXTERNAL_LINKAGE + void array::lh_ctor() noexcept + { + TOML_ARRAY_CREATED; + } + + TOML_EXTERNAL_LINKAGE + void array::lh_dtor() noexcept + { + TOML_ARRAY_DESTROYED; + } + + #endif + + TOML_EXTERNAL_LINKAGE + array::array() noexcept + { + #if TOML_LIFETIME_HOOKS + lh_ctor(); + #endif + } + + TOML_EXTERNAL_LINKAGE + array::array(const array& other) noexcept + : node( other ) + { + elements.reserve(other.elements.size()); + for (const auto& elem : other) + elements.emplace_back(impl::make_node(elem)); + + #if TOML_LIFETIME_HOOKS + lh_ctor(); + #endif + } + + TOML_EXTERNAL_LINKAGE + array::array(array&& other) noexcept + : node( std::move(other) ), + elements{ std::move(other.elements) } + { + #if TOML_LIFETIME_HOOKS + lh_ctor(); + #endif + } + + TOML_EXTERNAL_LINKAGE + array& array::operator= (const array& rhs) noexcept + { + if (&rhs != this) + { + node::operator=(rhs); + elements.clear(); + elements.reserve(rhs.elements.size()); + for (const auto& elem : rhs) + elements.emplace_back(impl::make_node(elem)); + } + return *this; + } + + TOML_EXTERNAL_LINKAGE + array& array::operator= (array&& rhs) noexcept + { + if (&rhs != this) + { + node::operator=(std::move(rhs)); + elements = std::move(rhs.elements); + } + return *this; + } + + TOML_EXTERNAL_LINKAGE + array::~array() noexcept + { + #if TOML_LIFETIME_HOOKS + lh_dtor(); + #endif + } + + TOML_EXTERNAL_LINKAGE + void array::preinsertion_resize(size_t idx, size_t count) noexcept + { + TOML_ASSERT(idx <= elements.size()); + TOML_ASSERT(count >= 1_sz); + const auto old_size = elements.size(); + const auto new_size = old_size + count; + const auto inserting_at_end = idx == old_size; + elements.resize(new_size); + if (!inserting_at_end) + { + for(size_t left = old_size, right = new_size - 1_sz; left --> idx; right--) + elements[right] = std::move(elements[left]); + } + } + + #define TOML_MEMBER_ATTR(attr) TOML_EXTERNAL_LINKAGE TOML_ATTR(attr) + + TOML_MEMBER_ATTR(const) node_type array::type() const noexcept { return node_type::array; } + TOML_MEMBER_ATTR(const) bool array::is_table() const noexcept { return false; } + TOML_MEMBER_ATTR(const) bool array::is_array() const noexcept { return true; } + TOML_MEMBER_ATTR(const) bool array::is_value() const noexcept { return false; } + TOML_MEMBER_ATTR(const) const array* array::as_array() const noexcept { return this; } + TOML_MEMBER_ATTR(const) array* array::as_array() noexcept { return this; } + + TOML_MEMBER_ATTR(pure) const node& array::operator[] (size_t index) const noexcept { return *elements[index]; } + TOML_MEMBER_ATTR(pure) node& array::operator[] (size_t index) noexcept { return *elements[index]; } + + TOML_MEMBER_ATTR(pure) const node& array::front() const noexcept { return *elements.front(); } + TOML_MEMBER_ATTR(pure) const node& array::back() const noexcept { return *elements.back(); } + TOML_MEMBER_ATTR(pure) node& array::front() noexcept { return *elements.front(); } + TOML_MEMBER_ATTR(pure) node& array::back() noexcept { return *elements.back(); } + + TOML_MEMBER_ATTR(pure) array::const_iterator array::begin() const noexcept { return { elements.begin() }; } + TOML_MEMBER_ATTR(pure) array::const_iterator array::end() const noexcept { return { elements.end() }; } + TOML_MEMBER_ATTR(pure) array::const_iterator array::cbegin() const noexcept { return { elements.cbegin() }; } + TOML_MEMBER_ATTR(pure) array::const_iterator array::cend() const noexcept { return { elements.cend() }; } + TOML_MEMBER_ATTR(pure) array::iterator array::begin() noexcept { return { elements.begin() }; } + TOML_MEMBER_ATTR(pure) array::iterator array::end() noexcept { return { elements.end() }; } + + TOML_MEMBER_ATTR(pure) size_t array::size() const noexcept { return elements.size(); } + TOML_MEMBER_ATTR(pure) size_t array::capacity() const noexcept { return elements.capacity(); } + TOML_MEMBER_ATTR(pure) bool array::empty() const noexcept { return elements.empty(); } + TOML_MEMBER_ATTR(const) size_t array::max_size() const noexcept { return elements.max_size(); } + + TOML_EXTERNAL_LINKAGE void array::reserve(size_t new_capacity) { elements.reserve(new_capacity); } + TOML_EXTERNAL_LINKAGE void array::clear() noexcept { elements.clear(); } + TOML_EXTERNAL_LINKAGE void array::shrink_to_fit() { elements.shrink_to_fit(); } + + #undef TOML_MEMBER_ATTR + + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype) const noexcept + { + if (elements.empty()) + return false; + + if (ntype == node_type::none) + ntype = elements[0]->type(); + + for (const auto& val : elements) + if (val->type() != ntype) + return false; + + return true; + } + + namespace impl + { + template + TOML_INTERNAL_LINKAGE + bool array_is_homogeneous(T& elements, node_type ntype, U& first_nonmatch) noexcept + { + if (elements.empty()) + { + first_nonmatch = {}; + return false; + } + if (ntype == node_type::none) + ntype = elements[0]->type(); + for (const auto& val : elements) + { + if (val->type() != ntype) + { + first_nonmatch = val.get(); + return false; + } + } + return true; + } + } + + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype, toml::node*& first_nonmatch) noexcept + { + return impl::array_is_homogeneous(elements, ntype, first_nonmatch); + } + + TOML_EXTERNAL_LINKAGE + bool array::is_homogeneous(node_type ntype, const toml::node*& first_nonmatch) const noexcept + { + return impl::array_is_homogeneous(elements, ntype, first_nonmatch); + } + + TOML_EXTERNAL_LINKAGE + void array::truncate(size_t new_size) + { + if (new_size < elements.size()) + elements.resize(new_size); + } + + TOML_EXTERNAL_LINKAGE + array::iterator array::erase(const_iterator pos) noexcept + { + return { elements.erase(pos.raw_) }; + } + + TOML_EXTERNAL_LINKAGE + array::iterator array::erase(const_iterator first, const_iterator last) noexcept + { + return { elements.erase(first.raw_, last.raw_) }; + } + + TOML_EXTERNAL_LINKAGE + void array::pop_back() noexcept + { + elements.pop_back(); + } + + TOML_EXTERNAL_LINKAGE + TOML_ATTR(pure) + node* array::get(size_t index) noexcept + { + return index < elements.size() ? elements[index].get() : nullptr; + } + + TOML_EXTERNAL_LINKAGE + TOML_ATTR(pure) + const node* array::get(size_t index) const noexcept + { + return index < elements.size() ? elements[index].get() : nullptr; + } + + TOML_EXTERNAL_LINKAGE + bool operator == (const array& lhs, const array& rhs) noexcept + { + if (&lhs == &rhs) + return true; + if (lhs.elements.size() != rhs.elements.size()) + return false; + for (size_t i = 0, e = lhs.elements.size(); i < e; i++) + { + const auto lhs_type = lhs.elements[i]->type(); + const node& rhs_ = *rhs.elements[i]; + const auto rhs_type = rhs_.type(); + if (lhs_type != rhs_type) + return false; + + const bool equal = lhs.elements[i]->visit([&](const auto& lhs_) noexcept + { + return lhs_ == *reinterpret_cast*>(&rhs_); + }); + if (!equal) + return false; + } + return true; + } + + TOML_EXTERNAL_LINKAGE + bool operator != (const array& lhs, const array& rhs) noexcept + { + return !(lhs == rhs); + } + + TOML_EXTERNAL_LINKAGE + size_t array::total_leaf_count() const noexcept + { + size_t leaves{}; + for (size_t i = 0, e = elements.size(); i < e; i++) + { + auto arr = elements[i]->as_array(); + leaves += arr ? arr->total_leaf_count() : 1_sz; + } + return leaves; + } + + TOML_EXTERNAL_LINKAGE + void array::flatten_child(array&& child, size_t& dest_index) noexcept + { + for (size_t i = 0, e = child.size(); i < e; i++) + { + auto type = child.elements[i]->type(); + if (type == node_type::array) + { + array& arr = *reinterpret_cast(child.elements[i].get()); + if (!arr.empty()) + flatten_child(std::move(arr), dest_index); + } + else + elements[dest_index++] = std::move(child.elements[i]); + } + } + + TOML_EXTERNAL_LINKAGE + array& array::flatten() & + { + if (elements.empty()) + return *this; + + bool requires_flattening = false; + size_t size_after_flattening = elements.size(); + for (size_t i = elements.size(); i --> 0_sz;) + { + auto arr = elements[i]->as_array(); + if (!arr) + continue; + size_after_flattening--; //discount the array itself + const auto leaf_count = arr->total_leaf_count(); + if (leaf_count > 0_sz) + { + requires_flattening = true; + size_after_flattening += leaf_count; + } + else + elements.erase(elements.cbegin() + static_cast(i)); + } + + if (!requires_flattening) + return *this; + + elements.reserve(size_after_flattening); + + size_t i = 0; + while (i < elements.size()) + { + auto arr = elements[i]->as_array(); + if (!arr) + { + i++; + continue; + } + + std::unique_ptr arr_storage = std::move(elements[i]); + const auto leaf_count = arr->total_leaf_count(); + if (leaf_count > 1_sz) + preinsertion_resize(i + 1_sz, leaf_count - 1_sz); + flatten_child(std::move(*arr), i); //increments i + } + + return *this; + } + + TOML_EXTERNAL_LINKAGE + bool array::is_array_of_tables() const noexcept + { + return is_homogeneous(node_type::table); + } +} +TOML_NAMESPACE_END; diff --git a/codon/util/toml++/toml_common.h b/codon/util/toml++/toml_common.h new file mode 100644 index 00000000..dcb114ab --- /dev/null +++ b/codon/util/toml++/toml_common.h @@ -0,0 +1,1038 @@ +//# This file is a part of toml++ and is subject to the the terms of the MIT license. +//# Copyright (c) Mark Gillard +//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license +// text. +// SPDX-License-Identifier: MIT + +#pragma once +#include "toml_preprocessor.h" + +//#==================================================================================================================== +//# INCLUDES +//#==================================================================================================================== + +TOML_DISABLE_WARNINGS; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !TOML_HAS_CUSTOM_OPTIONAL_TYPE +#include +#endif +TOML_ENABLE_WARNINGS; + +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 +#define TOML_LAUNDER(x) std::launder(x) +#else +#define TOML_LAUNDER(x) x +#endif + +#if defined(DOXYGEN) || (defined(__cpp_char8_t) && __cpp_char8_t >= 201811 && \ + defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201907) +#define TOML_HAS_CHAR8 1 +#else +#define TOML_HAS_CHAR8 0 +#endif + +//#==================================================================================================================== +//# ENVIRONMENT GROUND-TRUTHS +//#==================================================================================================================== + +/// \cond +#ifndef TOML_DISABLE_ENVIRONMENT_CHECKS +#define TOML_ENV_MESSAGE \ + "If you're seeing this error it's because you're building toml++ for an " \ + "environment that doesn't conform to " \ + "one of the 'ground truths' assumed by the library. Essentially this just means " \ + "that I don't have the " \ + "resources to test on more platforms, but I wish I did! You can try disabling the " \ + "checks by defining " \ + "TOML_DISABLE_ENVIRONMENT_CHECKS, but your mileage may vary. Please consider " \ + "filing an issue at " \ + "https://github.com/marzer/tomlplusplus/issues to help me improve support for your " \ + "target environment. Thanks!" + +static_assert(CHAR_BIT == 8, TOML_ENV_MESSAGE); +static_assert(FLT_RADIX == 2, TOML_ENV_MESSAGE); +static_assert('A' == 65, TOML_ENV_MESSAGE); +static_assert(sizeof(double) == 8, TOML_ENV_MESSAGE); +static_assert(std::numeric_limits::is_iec559, TOML_ENV_MESSAGE); +static_assert(std::numeric_limits::digits == 53, TOML_ENV_MESSAGE); +static_assert(std::numeric_limits::digits10 == 15, TOML_ENV_MESSAGE); + +#undef TOML_ENV_MESSAGE +#endif // !TOML_DISABLE_ENVIRONMENT_CHECKS +/// \endcond + +//#==================================================================================================================== +//# UNDOCUMENTED TYPEDEFS AND FORWARD DECLARATIONS +//#==================================================================================================================== + +/// \cond +// undocumented forward declarations are hidden from doxygen because they fuck it up =/ + +namespace toml // non-abi namespace; this is not an error +{ +using namespace std::string_literals; +using namespace std::string_view_literals; +using ::std::int16_t; +using ::std::int32_t; +using ::std::int64_t; +using ::std::int8_t; +using ::std::intptr_t; +using ::std::nullptr_t; +using ::std::ptrdiff_t; +using ::std::size_t; +using ::std::uint16_t; +using ::std::uint32_t; +using ::std::uint64_t; +using ::std::uint8_t; +using ::std::uint_least32_t; +using ::std::uint_least64_t; +using ::std::uintptr_t; + +// legacy typedefs +using string_char = char; +using string = std::string; +using string_view = std::string_view; +} // namespace toml + +TOML_NAMESPACE_START // abi namespace +{ + struct date; + struct time; + struct time_offset; + + TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt); + struct date_time; + TOML_ABI_NAMESPACE_END; + + class node; + class array; + class table; + + template class node_view; + template class value; + template class default_formatter; + template class json_formatter; + + [[nodiscard]] TOML_API bool operator==(const array &lhs, const array &rhs) noexcept; + [[nodiscard]] TOML_API bool operator!=(const array &lhs, const array &rhs) noexcept; + [[nodiscard]] TOML_API bool operator==(const table &lhs, const table &rhs) noexcept; + [[nodiscard]] TOML_API bool operator!=(const table &lhs, const table &rhs) noexcept; + + template + std::basic_ostream &operator<<(std::basic_ostream &, const array &); + template + std::basic_ostream &operator<<(std::basic_ostream &, const value &); + template + std::basic_ostream &operator<<(std::basic_ostream &, const table &); + template + std::basic_ostream &operator<<(std::basic_ostream &, default_formatter &); + template + std::basic_ostream &operator<<(std::basic_ostream &, default_formatter &&); + template + std::basic_ostream &operator<<(std::basic_ostream &, json_formatter &); + template + std::basic_ostream &operator<<(std::basic_ostream &, json_formatter &&); + template + inline std::basic_ostream &operator<<(std::basic_ostream &, + const node_view &); + + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex); +#if TOML_EXCEPTIONS + using parse_result = table; +#else + class parse_result; +#endif // TOML_EXCEPTIONS + TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS + + namespace impl { + template + using string_map = std::map>; // heterogeneous lookup + + template + using remove_cvref_t = std::remove_cv_t>; + + template + inline constexpr bool is_one_of = (false || ... || std::is_same_v); + + template + inline constexpr bool is_cvref = + std::is_reference_v || std::is_const_v || std::is_volatile_v; + + template + inline constexpr bool is_wide_string = + is_one_of, const wchar_t *, wchar_t *, std::wstring_view, + std::wstring>; + + template inline constexpr bool dependent_false = false; + +#if TOML_WINDOWS_COMPAT + [[nodiscard]] TOML_API std::string narrow(std::wstring_view) noexcept; + [[nodiscard]] TOML_API std::wstring widen(std::string_view) noexcept; +#if TOML_HAS_CHAR8 + [[nodiscard]] TOML_API std::wstring widen(std::u8string_view) noexcept; +#endif +#endif // TOML_WINDOWS_COMPAT + + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex); + class parser; + TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS + +#if TOML_ABI_NAMESPACES +#if TOML_EXCEPTIONS +#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::ex::parser +#else +#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::noex::parser +#endif +#else +#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::parser +#endif + } // namespace impl +} +TOML_NAMESPACE_END; + +/// \endcond + +//#==================================================================================================================== +//# TYPEDEFS AND FORWARD DECLARATIONS +//#==================================================================================================================== + +/// \brief The root namespace for all toml++ functions and types. +namespace toml {} + +TOML_NAMESPACE_START // abi namespace +{ + /// \brief Convenience literal operators for working with toml++. + /// + /// \detail This namespace exists so you can safely hoist the toml++ literal operators + /// into another scope without dragging in everything from the toml + /// namespace: \cpp + /// + /// #include + /// using namespace toml::literals; + /// + /// int main() + /// { + /// toml::table tbl = "vals = [1, 2, 3]"_toml; + /// + /// // ... do stuff with the table generated by the "_toml" literal ... + /// + /// return 0; + /// } + /// \ecpp + /// + inline namespace literals {} + +#if TOML_HAS_CUSTOM_OPTIONAL_TYPE + template using optional = TOML_OPTIONAL_TYPE; +#else + /// \brief The 'optional' type used throughout the library. + /// + /// \remarks By default this will be an alias for std::optional, but you can change + /// the optional type used by the library by defining + /// #TOML_OPTIONAL_TYPE. + template using optional = std::optional; +#endif + + /// \brief TOML node type identifiers. + enum class node_type : uint8_t { + none, ///< Not-a-node. + table, ///< The node is a toml::table. + array, ///< The node is a toml::array. + string, ///< The node is a toml::value. + integer, ///< The node is a toml::value. + floating_point, ///< The node is a toml::value. + boolean, ///< The node is a toml::value. + date, ///< The node is a toml::value. + time, ///< The node is a toml::value