Skip to content

Commit

Permalink
Add VersionSpec::str (#3081)
Browse files Browse the repository at this point in the history
* Add flat_bool_expr_tree:infix_for_each

* Add level to Version repr

* Add VersionSpec::str
  • Loading branch information
AntoinePrv authored Dec 22, 2023
1 parent 2d85759 commit 152857f
Show file tree
Hide file tree
Showing 11 changed files with 572 additions and 88 deletions.
84 changes: 24 additions & 60 deletions libmamba/include/mamba/specs/version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef MAMBA_SPECS_VERSION_HPP
#define MAMBA_SPECS_VERSION_HPP

#include <optional>
#include <string>
#include <string_view>
#include <vector>
Expand Down Expand Up @@ -110,8 +111,24 @@ namespace mamba::specs
[[nodiscard]] auto version() const noexcept -> const CommonVersion&;
[[nodiscard]] auto local() const noexcept -> const CommonVersion&;

/**
* A string representation of the version.
*
* May not always be the same as the parsed string (due to reconstruction) but reparsing
* this string will give the same version.
* ``v == Version::parse(v.str())``.
*/
[[nodiscard]] auto str() const -> std::string;

/**
* A string truncated of extended representation of the version.
*
* Represent the string with the desired number of parts.
* If the actual number of parts is larger, then the string is truncated.
* If the actual number of parts is smalle, then the string is expanded with zeros.
*/
[[nodiscard]] auto str(std::size_t level) const -> std::string;

[[nodiscard]] auto operator==(const Version& other) const -> bool;
[[nodiscard]] auto operator!=(const Version& other) const -> bool;
[[nodiscard]] auto operator<(const Version& other) const -> bool;
Expand Down Expand Up @@ -156,73 +173,20 @@ namespace mamba::specs
template <>
struct fmt::formatter<mamba::specs::VersionPartAtom>
{
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin())
{
// make sure that range is empty
if (ctx.begin() != ctx.end() && *ctx.begin() != '}')
{
throw fmt::format_error("Invalid format");
}
return ctx.begin();
}
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());

template <class FormatContext>
auto format(const ::mamba::specs::VersionPartAtom atom, FormatContext& ctx)
{
return fmt::format_to(ctx.out(), "{}{}", atom.numeral(), atom.literal());
}
auto format(const ::mamba::specs::VersionPartAtom atom, format_context& ctx)
-> decltype(ctx.out());
};

template <>
struct fmt::formatter<mamba::specs::Version>
{
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin())
{
// make sure that range is empty
if (ctx.begin() != ctx.end() && *ctx.begin() != '}')
{
throw fmt::format_error("Invalid format");
}
return ctx.begin();
}
std::optional<std::size_t> m_level;

template <class FormatContext>
auto format(const ::mamba::specs::Version v, FormatContext& ctx)
{
auto out = ctx.out();
if (v.epoch() != 0)
{
out = fmt::format_to(ctx.out(), "{}!", v.epoch());
}

auto format_version_to = [](auto l_out, const auto& version)
{
bool first = true;
for (const auto& part : version)
{
if (first)
{
first = false;
}
else
{
l_out = fmt::format_to(l_out, ".");
}
for (const auto& atom : part)
{
l_out = fmt::format_to(l_out, "{}", atom);
}
}
return l_out;
};
out = format_version_to(out, v.version());
if (!v.local().empty())
{
out = fmt::format_to(out, "+");
out = format_version_to(out, v.local());
}
return out;
}
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());

auto format(const ::mamba::specs::Version v, format_context& ctx) -> decltype(ctx.out());
};

#endif
50 changes: 50 additions & 0 deletions libmamba/include/mamba/specs/version_spec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
#include <string_view>
#include <variant>

#include <fmt/format.h>

#include "mamba/specs/version.hpp"
#include "mamba/util/flat_bool_expr_tree.hpp"

namespace mamba::specs
{
/**
* A stateful unary boolean function on the Version space.
*/
class VersionPredicate
{
public:
Expand All @@ -35,8 +40,13 @@ namespace mamba::specs
/** Construct an free interval. */
VersionPredicate() = default;

/**
* True if the predicate contains the given version.
*/
[[nodiscard]] auto contains(const Version& point) const -> bool;

[[nodiscard]] auto str() const -> std::string;

private:

struct free_interval
Expand Down Expand Up @@ -90,11 +100,23 @@ namespace mamba::specs
friend auto operator==(not_starts_with, not_starts_with) -> bool;
friend auto operator==(compatible_with, compatible_with) -> bool;
friend auto operator==(const VersionPredicate& lhs, const VersionPredicate& rhs) -> bool;
friend class ::fmt::formatter<VersionPredicate>;
};

auto operator==(const VersionPredicate& lhs, const VersionPredicate& rhs) -> bool;
auto operator!=(const VersionPredicate& lhs, const VersionPredicate& rhs) -> bool;

/**
* Represent a set of versions.
*
* Internally, a VersionSpec is a binary expression tree of union (or) or intersections (and)
* of the sets represented by VersionPredicate.
*
* The VersionSpec can itself be considered a complex predicate on the space of Version.
*
* Due to the complex nature of the expression system (comutativity, associativity, etc.), there
* is no easy way to say if two VersionSpecs are equal.
*/
class VersionSpec
{
public:
Expand Down Expand Up @@ -123,12 +145,24 @@ namespace mamba::specs
VersionSpec() = default;
explicit VersionSpec(tree_type&& tree) noexcept;

/**
* A string representation of the version spec.
*
* May not always be the same as the parsed string (due to reconstruction) but reparsing
* this string will give the same version spec.
*/
[[nodiscard]] auto str() const -> std::string;

/**
* True if the set described by the VersionSpec contains the given version.
*/
[[nodiscard]] auto contains(const Version& point) const -> bool;

private:

tree_type m_tree;

friend class ::fmt::formatter<VersionSpec>;
};

namespace version_spec_literals
Expand All @@ -137,4 +171,20 @@ namespace mamba::specs
}
}

template <>
struct fmt::formatter<mamba::specs::VersionPredicate>
{
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());

auto format(const ::mamba::specs::VersionPredicate& pred, format_context& ctx)
-> decltype(ctx.out());
};

template <>
struct fmt::formatter<mamba::specs::VersionSpec>
{
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());

auto format(const ::mamba::specs::VersionSpec& spec, format_context& ctx) -> decltype(ctx.out());
};
#endif
26 changes: 26 additions & 0 deletions libmamba/include/mamba/util/flat_binary_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ namespace mamba::util
[[nodiscard]] auto right(idx_type idx) const -> idx_type;
[[nodiscard]] auto root() const -> idx_type;

template <typename Visitor>
void dfs_raw(Visitor&& visitor, idx_type start) const;

private:

node_list m_nodes;
Expand Down Expand Up @@ -245,5 +248,28 @@ namespace mamba::util
{
return add_branch_impl(std::move(branch), left_child, right_child);
}

template <typename B, typename L>
template <typename Visitor>
void flat_binary_tree<B, L>::dfs_raw(Visitor&& visitor, idx_type start_idx) const
{
if (is_leaf(start_idx))
{
visitor.on_leaf(*this, start_idx);
}
else
{
const auto left_idx = left(start_idx);
const auto right_idx = right(start_idx);

visitor.on_branch_left_before(*this, start_idx, left_idx);
dfs_raw(visitor, left_idx);

visitor.on_branch_infix(*this, start_idx, left_idx, right_idx);

dfs_raw(visitor, right_idx);
visitor.on_branch_right_after(*this, start_idx, right_idx);
}
}
}
#endif
98 changes: 96 additions & 2 deletions libmamba/include/mamba/util/flat_bool_expr_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@

#include <cassert>
#include <functional>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
Expand Down Expand Up @@ -150,6 +148,14 @@ namespace mamba::util
using tree_type = flat_binary_tree<operator_type, variable_type>;
using size_type = typename tree_type::size_type;

struct LeftParenthesis
{
};

struct RightParenthesis
{
};

flat_bool_expr_tree() = default;
flat_bool_expr_tree(const flat_bool_expr_tree&) = default;
flat_bool_expr_tree(flat_bool_expr_tree&&) = default;
Expand All @@ -169,6 +175,9 @@ namespace mamba::util
[[nodiscard]] auto evaluate(UnaryFunc&& var_evaluator = {}, bool empty_val = true) const
-> bool;

template <typename UnaryFunc>
void infix_for_each(UnaryFunc&& func) const;

private:

using idx_type = typename tree_type::idx_type;
Expand All @@ -179,6 +188,42 @@ namespace mamba::util
tree_type m_tree = {};
};

template <typename V>
constexpr auto operator==(
typename flat_bool_expr_tree<V>::LeftParenthesis,
typename flat_bool_expr_tree<V>::LeftParenthesis
) -> bool
{
return true;
}

template <typename V>
constexpr auto operator!=(
typename flat_bool_expr_tree<V>::LeftParenthesis,
typename flat_bool_expr_tree<V>::LeftParenthesis
) -> bool
{
return false;
}

template <typename V>
constexpr auto operator==(
typename flat_bool_expr_tree<V>::RightParenthesis,
typename flat_bool_expr_tree<V>::RightParenthesis
) -> bool
{
return true;
}

template <typename V>
constexpr auto operator!=(
typename flat_bool_expr_tree<V>::RightParenthesis,
typename flat_bool_expr_tree<V>::RightParenthesis
) -> bool
{
return false;
}

/*************************************
* Implementation of PostfixParser *
*************************************/
Expand Down Expand Up @@ -551,5 +596,54 @@ namespace mamba::util
|| evaluate_impl(var_eval, m_tree.right(idx));
}
}

template <typename V>
template <typename UnaryFunc>
void flat_bool_expr_tree<V>::infix_for_each(UnaryFunc&& func) const
{
struct TreeVisitor
{
using idx_type = typename tree_type::idx_type;

void on_leaf(const tree_type& tree, idx_type idx)
{
m_func(tree.leaf(idx));
}

void on_branch_left_before(const tree_type& tree, idx_type, idx_type left_idx)
{
if (!tree.is_leaf(left_idx))
{
m_func(LeftParenthesis{});
}
}

void
on_branch_infix(const tree_type& tree, idx_type branch_idx, idx_type left_idx, idx_type right_idx)
{
if (!tree.is_leaf(left_idx))
{
m_func(RightParenthesis{});
}
m_func(tree.branch(branch_idx));
if (!tree.is_leaf(right_idx))
{
m_func(LeftParenthesis{});
}
}

void on_branch_right_after(const tree_type& tree, idx_type, idx_type right_idx)
{
if (!tree.is_leaf(right_idx))
{
m_func(RightParenthesis{});
}
}

UnaryFunc m_func;
} tree_visitor{ std::forward<UnaryFunc>(func) };

m_tree.dfs_raw(tree_visitor, m_tree.root());
}
}
#endif
Loading

0 comments on commit 152857f

Please sign in to comment.