diff --git a/include/ctre/evaluation.hpp b/include/ctre/evaluation.hpp index 017b9097..26966609 100644 --- a/include/ctre/evaluation.hpp +++ b/include/ctre/evaluation.hpp @@ -447,6 +447,230 @@ constexpr CTRE_FORCE_INLINE R evaluate(const Iterator begin, Iterator current, c // property matching +// pattern analysis - returns the minimum and maximum # of characters in order for a regex to match a string +// -1 is considered INF, -2 is finite (but perhaps too large to store), all other values are exact counts +constexpr CTRE_FORCE_INLINE size_t saturate_limit(const size_t& lhs, const size_t& rhs) { + const constexpr size_t inf = size_t{ 0 } -1; + const constexpr size_t lim = size_t{ 0 } -2; + size_t ret = inf; + if (lhs == inf || rhs == inf) { + return ret; + } else { + ret = lhs + rhs; + ret = ret < lhs ? lim : ret == inf ? lim : ret; + } + return ret; +} + +constexpr CTRE_FORCE_INLINE size_t mult_saturate_limit(const size_t& lhs, const size_t& rhs) { + const constexpr size_t inf = size_t{ 0 } -1; + const constexpr size_t lim = size_t{ 0 } -2; + size_t ret = inf; + if (lhs == inf || rhs == inf) { + return ret; + } else if (lhs == 0 || rhs == 0) { + return ret = 0; + } else { + if (lhs > (SIZE_MAX / rhs)) + return ret = lim; + ret = lhs * rhs; + ret = ret == inf ? lim : ret; + return ret; + } +} +//a custom std::pair to overload some handy operations that we'll perform w/ a fold +struct analysis_results : std::pair { + constexpr inline CTRE_FORCE_INLINE operator bool() const noexcept { + return first; + } + constexpr auto CTRE_FORCE_INLINE operator+(analysis_results other) const noexcept { + return analysis_results{std::make_pair( + saturate_limit(first, other.first), + saturate_limit(second, other.second) + )}; + } + constexpr auto CTRE_FORCE_INLINE operator||(analysis_results other) const noexcept { + return analysis_results{std::make_pair( + std::min(first, other.first), + std::max(second, other.second) + )}; + } +}; + +template +static constexpr auto trampoline_analysis(Pattern) noexcept; + +template +static constexpr auto trampoline_analysis(ctll::list) noexcept; + +template +static constexpr auto trampoline_analysis(T, R captures) noexcept; + +//processing for each type + +//repeat +template +static constexpr auto _analyze(repeat, R captures) noexcept { + analysis_results ret{ std::make_pair(0ULL, 0ULL) }; + if constexpr (sizeof...(Content)) { + ret = trampoline_analysis(ctll::list(), captures); + ret.first = mult_saturate_limit(ret.first, A); + ret.second = mult_saturate_limit(ret.second, B); + } + return ret; +} + +//note: all * ? + operations are specialized variations of repeat {A,B} +//lazy_repeat +template +static constexpr auto _analyze(lazy_repeat, R captures) noexcept { + return _analyze(repeat(), captures); +} + +//possessive_repeat +template +static constexpr auto _analyze(possessive_repeat, R captures) noexcept { + return _analyze(repeat(), captures); +} + +//star +template +static constexpr auto _analyze(star, R captures) noexcept { + return _analyze(repeat<0ULL, ~(0ULL), Content...>(), captures); +} + +//lazy_star +template +static constexpr auto _analyze(lazy_star, R captures) noexcept { + return _analyze(repeat<0ULL, ~(0ULL), Content...>(), captures); +} + +//possessive_star +template +static constexpr auto _analyze(possessive_star, R captures) noexcept { + return _analyze(repeat<0ULL, ~(0ULL), Content...>(), captures); +} + +//optional +template +static constexpr auto _analyze(optional, R captures) noexcept { + return _analyze(repeat<0ULL, 1ULL, Content...>(), captures); +} + +//lazy_optional +template +static constexpr auto _analyze(lazy_optional, R captures) noexcept { + return _analyze(repeat<0ULL, 1ULL, Content...>(), captures); +} + +//back_reference +template +static constexpr auto _analyze(back_reference, R captures) noexcept { + const auto ref = captures.template get(); + analysis_results ret{ std::make_pair(0ULL, 0ULL) }; + if constexpr (size(ref.get_expression())) { + ret = trampoline_analysis(ref.get_expression(), captures); + } + return ret; +} + +//back_reference_with_name +template +static constexpr auto _analyze(back_reference_with_name, R captures) noexcept { + const auto ref = captures.template get(); + analysis_results ret{ std::make_pair(0ULL, 0ULL) }; + if constexpr (size(ref.get_expression())) { + ret = trampoline_analysis(ref.get_expression(), captures); + } + return ret; +} + +//select, this is specialized, we need to take the minimum of all minimums and maximum of all maximums +template +static constexpr auto _analyze(select, R captures) noexcept { + analysis_results ret = trampoline_select_analysis(ctll::list(), captures); + return ret; +} + +//character, any character contributes exactly one to both counts +template +static constexpr auto _analyze(character, R captures) noexcept { + analysis_results ret{ std::make_pair(1ULL, 1ULL) }; + return ret; +} + +//strings, any string contributes the # of characters it contains (if we have an empty string that'll be 0) +template +static constexpr auto _analyze(string, R captures) noexcept { + analysis_results ret{ std::make_pair(sizeof...(Str), sizeof...(Str)) }; + return ret; +} + +//we'll process anything that has contents as a regex +//ctll::list +template +static constexpr auto _analyze(ctll::list,R captures) noexcept { + analysis_results ret = trampoline_analysis(ctll::list(), captures); + return ret; +} + +//sequence +template +static constexpr auto _analyze(sequence, R captures) noexcept { + analysis_results ret = trampoline_analysis(ctll::list(), captures); + return ret; +} + +//capture +template +static constexpr auto _analyze(capture, R captures) noexcept { + analysis_results ret = trampoline_analysis(ctll::list(), captures); + return ret; +} + +//capture_with_name +template +static constexpr auto _analyze(capture_with_name, R captures) noexcept { + analysis_results ret = trampoline_analysis(ctll::list(), captures); + return ret; +} + +//everything else, anything we haven't matched already isn't supported and will contribute 0 +template +static constexpr auto _analyze(T, R captures) noexcept { + analysis_results ret{ std::make_pair(0ULL, 0ULL) }; + return ret; +} +//note: ctll::list wraps patterns just like sequences, we'll treat anything that looks like a regex w/ ctll::list +template +static constexpr auto trampoline_analysis(ctll::list, R captures) noexcept { + //fold, for every argument in a ctll::list, calculate its contribution to the limits + auto r = ((_analyze(Patterns(), captures)) + ...); + //note any reordering of parameters will result in the same limits + return r; +} + +template +static constexpr auto trampoline_select_analysis(ctll::list, R captures) noexcept { + //fold, each argument in a selection of regexes we take the minimum and maximum of all values + auto r = ((trampoline_analysis(Patterns(), captures)) || ...); + //note again, order is unimportant + return r; +} + +template +static constexpr auto pattern_analysis(ctll::list) noexcept { + using return_type = decltype(regex_results(std::declval::iterator>(), find_captures(pattern))); + return trampoline_analysis(ctll::list(), return_type{}); +} + +template +static constexpr auto pattern_analysis(Pattern pattern = {}) noexcept { + using return_type = decltype(regex_results(std::declval::iterator>(), find_captures(pattern))); + return trampoline_analysis(ctll::list(), return_type{}); +} + + } #endif diff --git a/include/ctre/find_captures.hpp b/include/ctre/find_captures.hpp index 338d266f..6e914bc8 100644 --- a/include/ctre/find_captures.hpp +++ b/include/ctre/find_captures.hpp @@ -112,12 +112,12 @@ template constexpr auto template constexpr auto find_captures(ctll::list, Tail...>, ctll::list) noexcept { - return find_captures(ctll::list(), ctll::list>()); + return find_captures(ctll::list(), ctll::list>>()); } template constexpr auto find_captures(ctll::list, Tail...>, ctll::list) noexcept { - return find_captures(ctll::list(), ctll::list>()); + return find_captures(ctll::list(), ctll::list>>()); } diff --git a/include/ctre/return_type.hpp b/include/ctre/return_type.hpp index 7a5b6458..2d2de586 100644 --- a/include/ctre/return_type.hpp +++ b/include/ctre/return_type.hpp @@ -13,7 +13,7 @@ struct not_matched_tag_t { }; static constexpr inline auto not_matched = not_matched_tag_t{}; -template struct captured_content { +template struct captured_content { template class storage { Iterator _begin{}; Iterator _end{}; @@ -21,7 +21,7 @@ template struct captured_content { bool _matched{false}; public: using char_type = typename std::iterator_traits::value_type; - + using content_type = Content; using name = Name; constexpr CTRE_FORCE_INLINE storage() noexcept {} @@ -86,6 +86,10 @@ template struct captured_content { constexpr CTRE_FORCE_INLINE static size_t get_id() noexcept { return Id; } + + constexpr CTRE_FORCE_INLINE static content_type get_expression() noexcept { + return {}; + } }; };