2014-10-13 14:12:33 +02:00
|
|
|
#ifndef regex_hh_INCLUDED
|
|
|
|
#define regex_hh_INCLUDED
|
|
|
|
|
|
|
|
#include "string.hh"
|
2017-10-02 08:59:04 +02:00
|
|
|
#include "regex_impl.hh"
|
2014-10-13 14:12:33 +02:00
|
|
|
|
2017-10-09 08:37:43 +02:00
|
|
|
#define REGEX_CHECK_WITH_BOOST
|
|
|
|
|
|
|
|
#ifdef REGEX_CHECK_WITH_BOOST
|
|
|
|
#include "exception.hh"
|
|
|
|
#include "string_utils.hh"
|
|
|
|
#include "utf8_iterator.hh"
|
2014-10-13 14:12:33 +02:00
|
|
|
#include <boost/regex.hpp>
|
2017-10-09 08:37:43 +02:00
|
|
|
#endif
|
2014-10-13 14:12:33 +02:00
|
|
|
|
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2014-10-13 20:40:27 +02:00
|
|
|
// Regex that keeps track of its string representation
|
2017-10-09 08:04:14 +02:00
|
|
|
class Regex
|
2014-10-13 20:40:27 +02:00
|
|
|
{
|
2016-12-14 21:59:39 +01:00
|
|
|
public:
|
2014-10-13 20:40:27 +02:00
|
|
|
Regex() = default;
|
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
explicit Regex(StringView re, RegexCompileFlags flags = RegexCompileFlags::None);
|
2014-10-13 20:40:27 +02:00
|
|
|
bool empty() const { return m_str.empty(); }
|
2015-04-09 00:16:27 +02:00
|
|
|
bool operator==(const Regex& other) const { return m_str == other.m_str; }
|
|
|
|
bool operator!=(const Regex& other) const { return m_str != other.m_str; }
|
2014-10-13 20:40:27 +02:00
|
|
|
|
2015-03-10 20:33:46 +01:00
|
|
|
const String& str() const { return m_str; }
|
2014-10-13 20:40:27 +02:00
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
size_t mark_count() const { return m_impl->save_count / 2 - 1; }
|
|
|
|
|
2016-08-18 23:42:31 +02:00
|
|
|
static constexpr const char* option_type_name = "regex";
|
2016-08-06 10:05:50 +02:00
|
|
|
|
2017-10-06 07:40:27 +02:00
|
|
|
const CompiledRegex* impl() const { return m_impl.get(); }
|
2017-10-02 08:59:04 +02:00
|
|
|
|
2017-10-09 08:37:43 +02:00
|
|
|
#ifdef REGEX_CHECK_WITH_BOOST
|
2017-10-09 08:04:14 +02:00
|
|
|
using BoostImpl = boost::basic_regex<wchar_t, boost::c_regex_traits<wchar_t>>;
|
|
|
|
const BoostImpl& boost_impl() const { return m_boost_impl; }
|
2017-10-09 08:37:43 +02:00
|
|
|
#endif
|
2017-10-09 08:04:14 +02:00
|
|
|
|
2014-10-13 20:40:27 +02:00
|
|
|
private:
|
2017-10-06 07:40:27 +02:00
|
|
|
RefPtr<CompiledRegex> m_impl;
|
2017-10-09 08:04:14 +02:00
|
|
|
String m_str;
|
2017-10-09 08:37:43 +02:00
|
|
|
#ifdef REGEX_CHECK_WITH_BOOST
|
2017-10-09 08:04:14 +02:00
|
|
|
BoostImpl m_boost_impl;
|
2017-10-09 08:37:43 +02:00
|
|
|
#endif
|
2014-10-13 20:40:27 +02:00
|
|
|
};
|
2015-03-10 20:33:46 +01:00
|
|
|
|
2014-10-13 14:12:33 +02:00
|
|
|
template<typename Iterator>
|
2017-10-09 08:04:14 +02:00
|
|
|
struct MatchResults
|
2016-05-10 10:12:30 +02:00
|
|
|
{
|
|
|
|
struct SubMatch : std::pair<Iterator, Iterator>
|
|
|
|
{
|
|
|
|
SubMatch() = default;
|
2017-10-09 08:04:14 +02:00
|
|
|
SubMatch(Iterator begin, Iterator end)
|
|
|
|
: std::pair<Iterator, Iterator>{begin, end}, matched{begin != Iterator{}}
|
2016-05-10 10:12:30 +02:00
|
|
|
{}
|
|
|
|
|
|
|
|
bool matched = false;
|
|
|
|
};
|
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
struct iterator : std::iterator<std::bidirectional_iterator_tag, SubMatch, size_t, SubMatch*, SubMatch>
|
2016-05-10 10:12:30 +02:00
|
|
|
{
|
2017-10-09 08:04:14 +02:00
|
|
|
using It = typename Vector<Iterator>::const_iterator;
|
|
|
|
|
|
|
|
iterator() = default;
|
|
|
|
iterator(It it) : m_it{std::move(it)} {}
|
|
|
|
|
|
|
|
iterator& operator--() { m_it += 2; return *this; }
|
|
|
|
iterator& operator++() { m_it += 2; return *this; }
|
|
|
|
SubMatch operator*() const { return {*m_it, *(m_it+1)}; }
|
|
|
|
|
|
|
|
friend bool operator==(const iterator& lhs, const iterator& rhs) { return lhs.m_it == rhs.m_it; }
|
|
|
|
friend bool operator!=(const iterator& lhs, const iterator& rhs) { return lhs.m_it != rhs.m_it; }
|
|
|
|
private:
|
2016-05-10 10:12:30 +02:00
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
It m_it;
|
2016-05-10 10:12:30 +02:00
|
|
|
};
|
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
MatchResults() = default;
|
|
|
|
MatchResults(Vector<Iterator> values) : m_values{std::move(values)} {}
|
|
|
|
|
|
|
|
iterator begin() const { return iterator{m_values.begin()}; }
|
|
|
|
iterator cbegin() const { return iterator{m_values.cbegin()}; }
|
|
|
|
iterator end() const { return iterator{m_values.end()}; }
|
|
|
|
iterator cend() const { return iterator{m_values.cend()}; }
|
|
|
|
|
|
|
|
size_t size() const { return m_values.size() / 2; }
|
|
|
|
bool empty() const { return m_values.empty(); }
|
|
|
|
|
|
|
|
SubMatch operator[](size_t i) const
|
|
|
|
{
|
|
|
|
return i * 2 < m_values.size() ?
|
|
|
|
SubMatch{m_values[i*2], m_values[i*2+1]} : SubMatch{};
|
|
|
|
}
|
|
|
|
|
|
|
|
friend bool operator==(const MatchResults& lhs, const MatchResults& rhs)
|
|
|
|
{
|
|
|
|
return lhs.m_values == rhs.m_values;
|
|
|
|
}
|
2016-05-10 10:12:30 +02:00
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
friend bool operator!=(const MatchResults& lhs, const MatchResults& rhs)
|
|
|
|
{
|
|
|
|
return not (lhs == rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
void swap(MatchResults& other)
|
|
|
|
{
|
|
|
|
m_values.swap(other.m_values);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Vector<Iterator> m_values;
|
2016-05-10 10:12:30 +02:00
|
|
|
};
|
2014-10-13 14:12:33 +02:00
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
inline RegexExecFlags match_flags(bool bol, bool eol, bool bow, bool eow)
|
2015-12-23 22:43:07 +01:00
|
|
|
{
|
2017-10-09 08:04:14 +02:00
|
|
|
return (bol ? RegexExecFlags::None : RegexExecFlags::NotBeginOfLine) |
|
|
|
|
(eol ? RegexExecFlags::None : RegexExecFlags::NotEndOfLine) |
|
|
|
|
(bow ? RegexExecFlags::None : RegexExecFlags::NotBeginOfWord) |
|
|
|
|
(eow ? RegexExecFlags::None : RegexExecFlags::NotEndOfWord);
|
2015-12-23 22:43:07 +01:00
|
|
|
}
|
|
|
|
|
2017-10-09 08:37:43 +02:00
|
|
|
#ifdef REGEX_CHECK_WITH_BOOST
|
2017-10-02 08:59:04 +02:00
|
|
|
void regex_mismatch(const Regex& re);
|
|
|
|
|
|
|
|
template<typename It>
|
2017-10-09 08:04:14 +02:00
|
|
|
using RegexUtf8It = utf8::iterator<It, wchar_t, ssize_t>;
|
|
|
|
|
|
|
|
template<typename It>
|
|
|
|
void check_captures(const Regex& re, const boost::match_results<RegexUtf8It<It>>& res, const Vector<It>& captures)
|
2017-10-02 08:59:04 +02:00
|
|
|
{
|
|
|
|
if (res.size() > captures.size() * 2)
|
|
|
|
return regex_mismatch(re);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < res.size(); ++i)
|
|
|
|
{
|
|
|
|
if (not res[i].matched)
|
|
|
|
{
|
|
|
|
if (captures[i*2] != It{} or captures[i*2+1] != It{})
|
|
|
|
regex_mismatch(re);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res[i].first != captures[i*2])
|
|
|
|
regex_mismatch(re);
|
|
|
|
if (res[i].second != captures[i*2+1])
|
|
|
|
regex_mismatch(re);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
boost::regbase::flag_type convert_flags(RegexCompileFlags flags);
|
|
|
|
boost::regex_constants::match_flag_type convert_flags(RegexExecFlags flags);
|
2017-10-09 08:37:43 +02:00
|
|
|
#endif
|
2017-10-02 16:34:57 +02:00
|
|
|
|
2016-05-10 10:12:30 +02:00
|
|
|
template<typename It>
|
|
|
|
bool regex_match(It begin, It end, const Regex& re)
|
|
|
|
{
|
2016-09-06 14:55:14 +02:00
|
|
|
try
|
|
|
|
{
|
2017-10-09 08:04:14 +02:00
|
|
|
const bool matched = regex_match(begin, end, *re.impl());
|
2017-10-09 08:37:43 +02:00
|
|
|
#ifdef REGEX_CHECK_WITH_BOOST
|
2017-10-09 08:04:14 +02:00
|
|
|
if (not re.boost_impl().empty() and
|
|
|
|
matched != boost::regex_match<RegexUtf8It<It>>({begin, begin, end}, {end, begin, end},
|
|
|
|
re.boost_impl()))
|
2017-10-02 08:59:04 +02:00
|
|
|
regex_mismatch(re);
|
2017-10-09 08:37:43 +02:00
|
|
|
#endif
|
2017-10-02 08:59:04 +02:00
|
|
|
return matched;
|
2016-09-06 14:55:14 +02:00
|
|
|
}
|
|
|
|
catch (std::runtime_error& err)
|
|
|
|
{
|
|
|
|
throw runtime_error{format("Regex matching error: {}", err.what())};
|
|
|
|
}
|
2016-05-10 10:12:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename It>
|
|
|
|
bool regex_match(It begin, It end, MatchResults<It>& res, const Regex& re)
|
|
|
|
{
|
2016-09-06 14:55:14 +02:00
|
|
|
try
|
|
|
|
{
|
2017-10-02 08:59:04 +02:00
|
|
|
Vector<It> captures;
|
2017-10-09 08:04:14 +02:00
|
|
|
const bool matched = regex_match(begin, end, captures, *re.impl());
|
|
|
|
|
2017-10-09 08:37:43 +02:00
|
|
|
#ifdef REGEX_CHECK_WITH_BOOST
|
2017-10-09 08:04:14 +02:00
|
|
|
boost::match_results<RegexUtf8It<It>> boost_res;
|
|
|
|
if (not re.boost_impl().empty() and
|
|
|
|
matched != boost::regex_match<RegexUtf8It<It>>({begin, begin, end}, {end, begin, end},
|
|
|
|
boost_res, re.boost_impl()))
|
2017-10-02 08:59:04 +02:00
|
|
|
regex_mismatch(re);
|
2017-10-09 08:04:14 +02:00
|
|
|
if (not re.boost_impl().empty() and matched)
|
|
|
|
check_captures(re, boost_res, captures);
|
2017-10-09 08:37:43 +02:00
|
|
|
#endif
|
2017-10-09 08:04:14 +02:00
|
|
|
|
|
|
|
res = matched ? MatchResults<It>{std::move(captures)} : MatchResults<It>{};
|
2017-10-02 08:59:04 +02:00
|
|
|
return matched;
|
2016-09-06 14:55:14 +02:00
|
|
|
}
|
|
|
|
catch (std::runtime_error& err)
|
|
|
|
{
|
|
|
|
throw runtime_error{format("Regex matching error: {}", err.what())};
|
|
|
|
}
|
2016-05-10 10:12:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename It>
|
|
|
|
bool regex_search(It begin, It end, const Regex& re,
|
2017-10-09 08:04:14 +02:00
|
|
|
RegexExecFlags flags = RegexExecFlags::None)
|
2016-05-10 10:12:30 +02:00
|
|
|
{
|
2016-09-06 14:55:14 +02:00
|
|
|
try
|
|
|
|
{
|
2017-10-09 08:04:14 +02:00
|
|
|
const bool matched = regex_search(begin, end, *re.impl(), flags);
|
|
|
|
|
2017-10-09 08:37:43 +02:00
|
|
|
#ifdef REGEX_CHECK_WITH_BOOST
|
2017-10-09 08:04:14 +02:00
|
|
|
auto first = (flags & RegexExecFlags::PrevAvailable) ? begin-1 : begin;
|
|
|
|
if (not re.boost_impl().empty() and
|
|
|
|
matched != boost::regex_search<RegexUtf8It<It>>({begin, first, end}, {end, first, end},
|
|
|
|
re.boost_impl(), convert_flags(flags)))
|
2017-10-02 08:59:04 +02:00
|
|
|
regex_mismatch(re);
|
2017-10-09 08:37:43 +02:00
|
|
|
#endif
|
2017-10-02 08:59:04 +02:00
|
|
|
return matched;
|
2016-09-06 14:55:14 +02:00
|
|
|
}
|
|
|
|
catch (std::runtime_error& err)
|
|
|
|
{
|
|
|
|
throw runtime_error{format("Regex searching error: {}", err.what())};
|
|
|
|
}
|
2016-05-10 10:12:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename It>
|
|
|
|
bool regex_search(It begin, It end, MatchResults<It>& res, const Regex& re,
|
2017-10-09 08:04:14 +02:00
|
|
|
RegexExecFlags flags = RegexExecFlags::None)
|
2016-05-10 10:12:30 +02:00
|
|
|
{
|
2016-09-06 14:55:14 +02:00
|
|
|
try
|
|
|
|
{
|
2017-10-02 08:59:04 +02:00
|
|
|
Vector<It> captures;
|
2017-10-09 08:04:14 +02:00
|
|
|
const bool matched = regex_search(begin, end, captures, *re.impl(), flags);
|
|
|
|
|
2017-10-09 08:37:43 +02:00
|
|
|
#ifdef REGEX_CHECK_WITH_BOOST
|
2017-10-09 08:04:14 +02:00
|
|
|
auto first = (flags & RegexExecFlags::PrevAvailable) ? begin-1 : begin;
|
|
|
|
boost::match_results<RegexUtf8It<It>> boost_res;
|
|
|
|
if (not re.boost_impl().empty() and
|
|
|
|
matched != boost::regex_search<RegexUtf8It<It>>({begin, first, end}, {end, first, end},
|
|
|
|
boost_res, re.boost_impl(), convert_flags(flags)))
|
2017-10-02 08:59:04 +02:00
|
|
|
regex_mismatch(re);
|
2017-10-09 08:04:14 +02:00
|
|
|
if (not re.boost_impl().empty() and matched)
|
|
|
|
check_captures(re, boost_res, captures);
|
2017-10-09 08:37:43 +02:00
|
|
|
#endif
|
2017-10-09 08:04:14 +02:00
|
|
|
|
|
|
|
res = matched ? MatchResults<It>{std::move(captures)} : MatchResults<It>{};
|
2017-10-02 08:59:04 +02:00
|
|
|
return matched;
|
2016-09-06 14:55:14 +02:00
|
|
|
}
|
|
|
|
catch (std::runtime_error& err)
|
|
|
|
{
|
|
|
|
throw runtime_error{format("Regex searching error: {}", err.what())};
|
|
|
|
}
|
2016-05-10 10:12:30 +02:00
|
|
|
}
|
|
|
|
|
2014-10-13 14:12:33 +02:00
|
|
|
String option_to_string(const Regex& re);
|
|
|
|
void option_from_string(StringView str, Regex& re);
|
|
|
|
|
2017-10-02 16:35:31 +02:00
|
|
|
template<typename Iterator>
|
|
|
|
struct RegexIterator
|
|
|
|
{
|
|
|
|
using ValueType = MatchResults<Iterator>;
|
|
|
|
|
|
|
|
RegexIterator() = default;
|
|
|
|
RegexIterator(Iterator begin, Iterator end, const Regex& re,
|
2017-10-09 08:04:14 +02:00
|
|
|
RegexExecFlags flags = RegexExecFlags::None)
|
2017-10-02 16:35:31 +02:00
|
|
|
: m_regex{&re}, m_next_begin{begin}, m_begin{begin}, m_end{end}, m_flags{flags}
|
|
|
|
{
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
|
|
|
|
const ValueType& operator*() const { kak_assert(m_regex); return m_results; }
|
|
|
|
const ValueType* operator->() const { kak_assert(m_regex); return &m_results; }
|
|
|
|
|
|
|
|
RegexIterator& operator++()
|
|
|
|
{
|
|
|
|
next();
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend bool operator==(const RegexIterator& lhs, const RegexIterator& rhs)
|
|
|
|
{
|
|
|
|
if (lhs.m_regex == nullptr and rhs.m_regex == nullptr)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return lhs.m_regex == rhs.m_regex and
|
|
|
|
lhs.m_next_begin == rhs.m_next_begin and
|
|
|
|
lhs.m_end == rhs.m_end and
|
|
|
|
lhs.m_flags == rhs.m_flags and
|
|
|
|
lhs.m_results == rhs.m_results;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend bool operator!=(const RegexIterator& lhs, const RegexIterator& rhs)
|
|
|
|
{
|
|
|
|
return not (lhs == rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void next()
|
|
|
|
{
|
|
|
|
kak_assert(m_regex);
|
|
|
|
|
2017-10-09 08:04:14 +02:00
|
|
|
RegexExecFlags additional_flags{};
|
2017-10-02 16:35:31 +02:00
|
|
|
if (m_results.size() and m_results[0].first == m_results[0].second)
|
2017-10-09 08:04:14 +02:00
|
|
|
additional_flags |= RegexExecFlags::NotInitialNull;
|
2017-10-02 16:35:31 +02:00
|
|
|
if (m_begin != m_next_begin)
|
2017-10-09 08:04:14 +02:00
|
|
|
additional_flags |= RegexExecFlags::NotBeginOfSubject | RegexExecFlags::PrevAvailable;
|
2017-10-02 16:35:31 +02:00
|
|
|
|
|
|
|
if (not regex_search(m_next_begin, m_end, m_results, *m_regex,
|
|
|
|
m_flags | additional_flags))
|
|
|
|
m_regex = nullptr;
|
|
|
|
else
|
|
|
|
m_next_begin = m_results[0].second;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Regex* m_regex = nullptr;
|
|
|
|
MatchResults<Iterator> m_results;
|
|
|
|
Iterator m_next_begin{};
|
|
|
|
const Iterator m_begin{};
|
|
|
|
const Iterator m_end{};
|
2017-10-09 08:04:14 +02:00
|
|
|
const RegexExecFlags m_flags = RegexExecFlags::None;
|
2017-10-02 16:35:31 +02:00
|
|
|
};
|
|
|
|
|
2014-10-13 14:12:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif // regex_hh_INCLUDED
|