2
0
mirror of https://github.com/boostorg/url.git synced 2026-02-13 00:42:15 +00:00

Range work and rule refactoring

This commit is contained in:
Vinnie Falco
2022-08-01 13:41:07 -07:00
parent b70f9745a7
commit ced6e9fcec
53 changed files with 867 additions and 582 deletions

View File

@@ -42,7 +42,7 @@
#include <boost/url/rfc/gen_delim_chars.hpp>
#include <boost/url/rfc/ipv4_address_rule.hpp>
#include <boost/url/rfc/ipv6_address_rule.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/rfc/path_rules.hpp>
#include <boost/url/rfc/pchars.hpp>
#include <boost/url/rfc/pct_encoded_rule.hpp>
#include <boost/url/rfc/query_rule.hpp>

View File

@@ -0,0 +1,162 @@
/*
Copyright 2018 Glen Joseph Fernandes
(glenjofe@gmail.com)
Copyright 2022 Vinnie Falco
(vinnie dot falco at gmail dot com)
* Added constexpr
* removed pre-C++11 support
Distributed under the Boost Software License, Version 1.0.
(http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef BOOST_URL_DETAIL_EMPTY_VALUE_HPP
#define BOOST_URL_DETAIL_EMPTY_VALUE_HPP
#include <boost/config.hpp>
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
#include <utility>
#endif
#if defined(BOOST_GCC_VERSION) && (BOOST_GCC_VERSION >= 40700)
#define BOOST_DETAIL_EMPTY_VALUE_BASE
#elif defined(BOOST_INTEL) && defined(_MSC_VER) && (_MSC_VER >= 1800)
#define BOOST_DETAIL_EMPTY_VALUE_BASE
#elif defined(BOOST_MSVC) && (BOOST_MSVC >= 1800)
#define BOOST_DETAIL_EMPTY_VALUE_BASE
#elif defined(BOOST_CLANG) && !defined(__CUDACC__)
#if __has_feature(is_empty) && __has_feature(is_final)
#define BOOST_DETAIL_EMPTY_VALUE_BASE
#endif
#endif
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4510)
#endif
namespace boost {
namespace urls {
namespace detail {
template<class T>
struct use_empty_value_base {
enum {
#if defined(BOOST_DETAIL_EMPTY_VALUE_BASE)
value = __is_empty(T) && !__is_final(T)
#else
value = false
#endif
};
};
struct empty_init_t { };
namespace empty_ {
template<
class T,
unsigned N = 0,
bool E = use_empty_value_base<T>::value>
class empty_value
{
T value_;
public:
typedef T type;
constexpr
empty_value() = default;
constexpr
empty_value(
empty_init_t) noexcept
: value_()
{
}
template<
class U, class... Args>
constexpr
empty_value(
empty_init_t,
U&& value,
Args&&... args) noexcept
: value_(
std::forward<U>(value),
std::forward<Args>(args)...)
{
}
T const&
get() const noexcept
{
return value_;
}
T&
get() noexcept
{
return value_;
}
};
//------------------------------------------------
template<class T, unsigned N>
class empty_value<T, N, true>
: T
{
public:
typedef T type;
constexpr
empty_value() noexcept = default;
constexpr
empty_value(
empty_init_t) noexcept
: T()
{
}
template<
class U, class... Args>
constexpr
empty_value(
empty_init_t,
U&& value,
Args&&... args) noexcept
: T(std::forward<U>(value),
std::forward<Args>(args)...)
{
}
T const&
get() const noexcept
{
return *this;
}
T&
get() noexcept
{
return *this;
}
};
} /* empty_ */
using empty_::empty_value;
constexpr empty_init_t empty_init{};
} // detail
} // urls
} // boost
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif

View File

@@ -12,7 +12,7 @@
#define BOOST_URL_DETAIL_IMPL_SEGMENTS_ENCODED_ITERATOR_IMPL_IPP
#include <boost/url/detail/segments_encoded_iterator_impl.hpp>
#include <boost/url/rfc/detail/path_increment.hpp>
#include <boost/url/rfc/detail/path_rules.hpp>
#include <boost/assert.hpp>
namespace boost {
@@ -72,8 +72,8 @@ increment() noexcept
// "/" segment
auto rv = grammar::parse(
next_, end_,
detail::path_increment);
if(rv == grammar::error::end)
detail::slash_segment_rule);
if(rv.has_error())
{
next_ = nullptr;
return;
@@ -103,7 +103,7 @@ decrement() noexcept
next_ = pos_;
auto rv = grammar::parse(
next_, end_,
detail::path_increment);
detail::slash_segment_rule);
s_ = rv->encoded();
return;
}
@@ -113,7 +113,7 @@ decrement() noexcept
// "/" segment
auto rv = grammar::parse(
next_, end_,
detail::path_increment);
detail::slash_segment_rule);
s_ = rv->encoded();
}
else
@@ -121,7 +121,7 @@ decrement() noexcept
// segment-nz
auto rv = grammar::parse(
next_, end_,
detail::path_increment);
detail::slash_segment_rule);
s_ = rv->encoded();
}
}

View File

@@ -12,7 +12,7 @@
#define BOOST_URL_DETAIL_IMPL_SEGMENTS_ITERATOR_IMPL_IPP
#include <boost/url/detail/segments_iterator_impl.hpp>
#include <boost/url/rfc/detail/segment_rule.hpp>
#include <boost/url/rfc/path_rules.hpp>
#include <boost/assert.hpp>
namespace boost {
@@ -39,7 +39,7 @@ segments_iterator_impl(
pos_ += n;
t_ = grammar::parse(
next_, end_,
detail::segment_rule).value();
segment_rule).value();
}
segments_iterator_impl::
@@ -76,8 +76,8 @@ increment() noexcept
// "/" segment
auto rv = grammar::parse(
next_, end_,
detail::path_increment);
if(rv == grammar::error::end)
detail::slash_segment_rule);
if(rv.has_error())
{
next_ = nullptr;
return;
@@ -108,7 +108,7 @@ decrement() noexcept
next_ = pos_;
t_ = grammar::parse(
next_, end_,
detail::path_increment).value();
detail::slash_segment_rule).value();
return;
}
next_ = pos_;
@@ -117,14 +117,14 @@ decrement() noexcept
// "/" segment
t_ = grammar::parse(
next_, end_,
detail::path_increment).value();
detail::slash_segment_rule).value();
}
else
{
// segment-nz
t_ = grammar::parse(
next_, end_,
detail::path_increment).value();
detail::slash_segment_rule).value();
}
}

View File

@@ -126,14 +126,14 @@ apply_authority(
void
url_impl::
apply_path(
parsed_path const& t) noexcept
string_view s,
std::size_t nseg) noexcept
{
auto s = t.path;
set_size(id_path, s.size());
// VFALCO we are decoding twice
decoded_[id_path] =
pct_decode_bytes_unchecked(t.path);
nseg_ = detail::path_segments(
t.path, t.count);
pct_decode_bytes_unchecked(s);
nseg_ = detail::path_segments(s, nseg);
}
void

View File

@@ -11,8 +11,8 @@
#define BOOST_URL_DETAIL_OVER_ALLOCATOR_HPP
#include <boost/config.hpp>
#include <boost/url/detail/empty_value.hpp>
#include <boost/assert.hpp>
#include <boost/core/empty_value.hpp>
#include <boost/type_traits/is_final.hpp>
#include <boost/type_traits/type_with_alignment.hpp>
#ifdef BOOST_NO_CXX11_ALLOCATOR
@@ -41,7 +41,7 @@ using allocator_traits = std::allocator_traits<Alloc>;
template<class T, class Allocator>
class over_allocator
: private empty_value<Allocator>
: private detail::empty_value<Allocator>
{
template<class U, class OtherAlloc>
friend class over_allocator;
@@ -75,16 +75,16 @@ public:
over_allocator(
std::size_t extra,
Allocator const& alloc)
: empty_value<Allocator>(
boost::empty_init_t{}, alloc)
: detail::empty_value<Allocator>(
detail::empty_init, alloc)
, extra_(extra)
{
}
template<class U>
over_allocator(over_allocator<U, Allocator> const& other) noexcept
: empty_value<Allocator>(
boost::empty_init_t{}, other.get())
: detail::empty_value<Allocator>(
detail::empty_init, other.get())
, extra_(other.extra_)
{
}

View File

@@ -18,11 +18,6 @@
#include <boost/assert.hpp>
#include <cstdint>
// VFALCO These structs used to be forward
// declared, but the parsers now use a
// nested type.
#include <boost/url/rfc/paths_rule.hpp>
namespace boost {
namespace urls {
@@ -93,7 +88,7 @@ struct url_impl : parts_base
unsigned char const*, pct_encoded_view const&) noexcept;
void apply_port(string_view, unsigned short) noexcept;
void apply_authority(authority_view const&) noexcept;
void apply_path(parsed_path const&) noexcept;
void apply_path(string_view, std::size_t) noexcept;
void apply_query(string_view, std::size_t) noexcept;
void apply_frag(pct_encoded_view) noexcept;
};

View File

@@ -35,7 +35,15 @@ enum class error
success = 0,
/**
* A list parser reached the end.
* A rule reached the end of a range
*
* This indicates that the input was consumed
* when parsing a @ref range. The @ref range_rule
* will avoid rewinding the input buffer when
* this error is returned. Thus the consumed
* characters will be considered part of the
* range without contributing additional
* elements.
*/
end,

View File

@@ -32,7 +32,7 @@ parse(
result<value_type>
{
auto const it0 = it;
auto rv = r_.parse(it, end);
auto rv = this->get().parse(it, end);
if( rv.has_error() ||
it != it0)
return rv;

View File

@@ -11,6 +11,7 @@
#define BOOST_URL_GRAMMAR_IMPL_RANGE_HPP
#include <boost/url/detail/except.hpp>
#include <boost/url/detail/empty_value.hpp>
#include <boost/url/grammar/error.hpp>
#include <boost/assert.hpp>
#include <iterator>
@@ -38,6 +39,10 @@ struct range<T>::
void
destroy() const noexcept = 0;
virtual
any_rule const*
move(void*) const noexcept = 0;
virtual
any_rule const*
copy(void*) const noexcept = 0;
@@ -55,6 +60,153 @@ struct range<T>::
char const*) const noexcept = 0;
};
//------------------------------------------------
template<class T>
template<class R>
struct range<T>::impl1
: any_rule
, private urls::detail::empty_value<R>
{
explicit
impl1(R const& next) noexcept
: urls::detail::empty_value<R>(
urls::detail::empty_init,
next)
{
}
private:
void
destroy() const noexcept override
{
static constexpr auto small =
sizeof(*this) <= BufferSize;
if(small)
this->~impl1();
else
delete this;
}
any_rule const*
move(void* dest)
const noexcept override
{
static constexpr auto small =
sizeof(*this) <= BufferSize;
if(small)
return ::new(dest) impl1(*this);
return this;
}
any_rule const*
copy(void* dest)
const noexcept override
{
static constexpr auto small =
sizeof(*this) <= BufferSize;
if(small)
return ::new(dest) impl1(*this);
return new impl1(*this);
}
result<T>
first(
char const*& it,
char const* end)
const noexcept override
{
return grammar::parse(
it, end, this->get());
}
result<T>
next(
char const*& it,
char const* end)
const noexcept override
{
return grammar::parse(
it, end, this->get());
}
};
//------------------------------------------------
template<class T>
template<class R0, class R1>
struct range<T>::impl2
: any_rule
, private urls::detail::empty_value<R0, 0>
, private urls::detail::empty_value<R1, 1>
{
impl2(
R0 const& first,
R1 const& next) noexcept
: urls::detail::empty_value<R0,0>(
urls::detail::empty_init, first)
, urls::detail::empty_value<R1,1>(
urls::detail::empty_init, next)
{
}
private:
void
destroy() const noexcept override
{
static constexpr auto small =
sizeof(*this) <= BufferSize;
if(small)
this->~impl2();
else
delete this;
}
any_rule const*
move(void* dest)
const noexcept override
{
static constexpr auto small =
sizeof(*this) <= BufferSize;
if(small)
return ::new(dest) impl2(*this);
return this;
}
any_rule const*
copy(void* dest)
const noexcept override
{
static constexpr auto small =
sizeof(*this) <= BufferSize;
if(small)
return ::new(dest) impl2(*this);
return new impl2(*this);
}
result<T>
first(
char const*& it,
char const* end)
const noexcept override
{
return grammar::parse(it, end,
urls::detail::empty_value<
R0,0>::get());
}
result<T>
next(
char const*& it,
char const* end)
const noexcept override
{
return grammar::parse(it, end,
urls::detail::empty_value<
R1,1>::get());
}
};
//------------------------------------------------
//
// iterator
@@ -114,12 +266,7 @@ public:
r_->s_.size();
rv_ = r_->pr_->next(p_, end);
if(rv_.has_error())
{
BOOST_ASSERT(
rv_.error() ==
error::end);
p_ = nullptr;
}
return *this;
}
@@ -148,44 +295,103 @@ private:
r_->s_.size();
rv_ = r_->pr_->first(p_, end);
if(rv_.has_error())
{
BOOST_ASSERT(
rv_.error() ==
error::end);
p_ = nullptr;
}
}
constexpr
iterator(
range<T> const& r,
int) noexcept
: p_(nullptr)
, r_(&r)
: r_(&r)
, p_(nullptr)
{
}
};
//------------------------------------------------
template<class T>
template<class R>
range<T>::
range(
string_view s,
std::size_t n,
R const& next)
: s_(s)
, n_(n)
{
using U = impl1<R>;
static constexpr auto small =
sizeof(U) <= BufferSize;
if(small)
{
pr_ = ::new(reinterpret_cast<
void*>(buf_)) U(next);
}
else
{
pr_ = new U(next);
}
}
//------------------------------------------------
template<class T>
template<
class R0, class R1>
range<T>::
range(
string_view s,
std::size_t n,
R0 const& first,
R1 const& next)
: s_(s)
, n_(n)
{
using U = impl2<R0, R1>;
static constexpr auto small =
sizeof(U) <= BufferSize;
if(small)
{
pr_ = ::new(reinterpret_cast<
void*>(buf_)) U(first, next);
}
else
{
pr_ = new U(first, next);
}
}
//------------------------------------------------
template<class T>
range<T>::
~range()
{
if(pr_)
pr_->destroy();
}
template<class T>
range<T>::
range(
range&& other) noexcept
: s_(other.s_)
, n_(other.n_)
{
if(other.pr_)
{
if(pr_ == reinterpret_cast<
any_rule*>(buf_))
pr_->~any_rule();
else
pr_->destroy();
pr_ = other.pr_->move(buf_);
other.pr_ = nullptr;
}
}
template<class T>
range<T>::
range(
range const& other) noexcept
range const& other)
: s_(other.s_)
, n_(other.n_)
{
@@ -193,6 +399,51 @@ range(
pr_ = other.pr_->copy(buf_);
}
template<class T>
auto
range<T>::
operator=(
range&& other) noexcept ->
range&
{
if(pr_)
pr_->destroy();
if(other.pr_)
{
s_ = other.s_;
n_ = other.n_;
pr_ = other.pr_->move(buf_);
other.s_ = {};
other.n_ = 0;
other.pr_ = nullptr;
}
else
{
s_ = {};
n_ = 0;
pr_ = nullptr;
}
return *this;
}
template<class T>
auto
range<T>::
operator=(
range const& other) ->
range&
{
range tmp(other);
if(pr_)
pr_->destroy();
s_ = tmp.s_;
n_ = tmp.n_;
pr_ = tmp.pr_;
tmp.pr_ = nullptr;
return *this;
}
template<class T>
auto
range<T>::
@@ -213,142 +464,6 @@ end() const noexcept ->
//------------------------------------------------
template<class T>
template<class R>
range<T>::
range(
string_view s,
std::size_t n,
R const& next)
: s_(s)
, n_(n)
{
struct impl : any_rule
{
explicit
impl(
R const& next) noexcept
: next_(next)
{
}
private:
R const next_;
void
destroy() const noexcept override
{
delete this;
}
any_rule const*
copy(void* dest)
const noexcept override
{
static constexpr auto small =
sizeof(*this) <= BufferSize;
if(small)
return ::new(dest) impl(*this);
return new impl(*this);
}
result<T>
first(
char const*& it,
char const* end)
const noexcept override
{
return grammar::parse(
it, end, next_);
}
result<T>
next(
char const*& it,
char const* end)
const noexcept override
{
return grammar::parse(
it, end, next_);
}
};
pr_ = ::new(reinterpret_cast<
void*>(buf_)) impl(next);
}
//------------------------------------------------
template<class T>
template<
class R0, class R1>
range<T>::
range(
string_view s,
std::size_t n,
R0 const& first,
R1 const& next)
: s_(s)
, n_(n)
{
struct impl : any_rule
{
impl(
R0 const& first,
R1 const& next) noexcept
: first_(first)
, next_(next)
{
}
private:
R0 const first_;
R1 const next_;
void
destroy() const noexcept override
{
delete this;
}
any_rule const*
copy(void* dest)
const noexcept override
{
static constexpr auto small =
sizeof(*this) <= BufferSize;
if(small)
return ::new(dest) impl(*this);
return new impl(*this);
}
result<T>
first(
char const*& it,
char const* end)
const noexcept override
{
return grammar::parse(
it, end, first_);
}
result<T>
next(
char const*& it,
char const* end)
const noexcept override
{
return grammar::parse(
it, end, next_);
}
};
pr_ = ::new(reinterpret_cast<
void*>(buf_)) impl(first, next);
}
//------------------------------------------------
template<class R>
auto
range_rule_t<R>::
@@ -361,12 +476,16 @@ parse(
std::size_t n = 0;
auto const it0 = it;
auto it1 = it;
auto rv = (grammar::parse)(
it, end, next_);
if(rv.has_error())
{
if(rv.error() != error::end)
return rv.error();
{
// rewind unless error::end
it = it1;
}
if(n < N_)
{
// too few
@@ -382,12 +501,16 @@ parse(
for(;;)
{
++n;
it1 = it;
rv = (grammar::parse)(
it, end, next_);
if(rv.has_error())
{
if(rv.error() != error::end)
return rv.error();
{
// rewind unless error::end
it = it1;
}
break;
}
if(n > M_)
@@ -418,12 +541,16 @@ parse(
std::size_t n = 0;
auto const it0 = it;
auto it1 = it;
auto rv = (grammar::parse)(
it, end, first_);
if(rv.has_error())
{
if(rv.error() != error::end)
return rv.error();
{
// rewind unless error::end
it = it1;
}
if(n < N_)
{
// too few
@@ -439,12 +566,16 @@ parse(
for(;;)
{
++n;
it1 = it;
rv = (grammar::parse)(
it, end, next_);
if(rv.has_error())
{
if(rv.error() != error::end)
return rv.error();
{
// rewind unless error::end
it = it1;
}
break;
}
if(n > M_)

View File

@@ -272,7 +272,7 @@ parse(
result<value_type>
{
detail::parse_sequence<
IsList, R0, Rn...> t(rn_);
IsList, R0, Rn...> t(this->get());
t.apply(it, end);
return t.make_result();
}

View File

@@ -89,7 +89,7 @@ parse(
result<value_type>
{
return detail::parse_variant(
it, end, rn_,
it, end, this->get(),
std::integral_constant<
std::size_t, 0>{},
std::true_type{});

View File

@@ -12,6 +12,7 @@
#include <boost/url/detail/config.hpp>
#include <boost/url/result.hpp>
#include <boost/url/detail/empty_value.hpp>
#include <boost/url/grammar/type_traits.hpp>
namespace boost {
@@ -47,6 +48,7 @@ not_empty_rule( Rule r );
#else
template<class R>
struct not_empty_rule_t
: urls::detail::empty_value<R>
{
using value_type =
typename R::value_type;
@@ -69,11 +71,10 @@ private:
constexpr
not_empty_rule_t(
R const& r) noexcept
: r_(r)
: urls::detail::empty_value<R>(
urls::detail::empty_init, r)
{
}
R const r_;
};
template<class R>

View File

@@ -13,6 +13,7 @@
#include <boost/url/detail/config.hpp>
#include <boost/url/optional.hpp>
#include <boost/url/result.hpp>
#include <boost/url/detail/empty_value.hpp>
#include <boost/assert.hpp>
namespace boost {
@@ -57,6 +58,7 @@ optional_rule( Rule r ) noexcept;
#else
template<class Rule>
class optional_rule_t
: private urls::detail::empty_value<Rule>
{
public:
using value_type = optional<
@@ -69,7 +71,7 @@ public:
result<value_type>
{
auto const it0 = it;
auto rv = r_.parse(it, end);
auto rv = this->get().parse(it, end);
if(! rv.has_error())
return value_type(*rv);
it = it0;
@@ -88,11 +90,10 @@ private:
constexpr
optional_rule_t(
Rule const& r) noexcept
: r_(r)
: urls::detail::empty_value<Rule>(
urls::detail::empty_init, r)
{
}
Rule const r_;
};
template<class Rule>

View File

@@ -54,11 +54,35 @@ class range
struct any_rule;
template<class R>
struct impl1;
template<class R0, class R1>
struct impl2;
string_view s_;
std::size_t n_ = 0;
char buf_[BufferSize];
any_rule const* pr_ = nullptr;
template<
class R0, class R1>
friend struct range_rule_t;
template<class R>
range(
string_view s,
std::size_t n,
R const& r);
template<
class R0, class R1>
range(
string_view s,
std::size_t n,
R0 const& first,
R1 const& next);
public:
/** The type of each element of the range
*/
@@ -96,12 +120,19 @@ public:
*/
~range();
#ifndef BOOST_URL_DOCS
#if BOOST_WORKAROUND( BOOST_GCC_VERSION, < 50000 ) || \
BOOST_WORKAROUND( BOOST_CLANG_VERSION, < 40000 )
/** Constructor
Default-constructed ranges have
zero elements.
*/
range() noexcept = default;
#endif
#endif
/** Constructor
After construction, the moved-from
object will be the empty range.
*/
range(range&&) noexcept;
/** Constructor
@@ -111,7 +142,17 @@ public:
for ensuring that the lifetime of the
buffer extends until no longer in use.
*/
range(range const&) noexcept;
range(range const&);
/** Assignment
*/
range&
operator=(range const&);
/** Assignment
*/
range&
operator=(range&&) noexcept;
/** Return an iterator to the beginning
*/
@@ -144,25 +185,6 @@ public:
{
return n_ == 0;
}
private:
template<class R>
range(
string_view s,
std::size_t n,
R const& r);
template<
class R0, class R1>
range(
string_view s,
std::size_t n,
R0 const& first,
R1 const& next);
template<
class R0, class R1>
friend struct range_rule_t;
};
//------------------------------------------------
@@ -185,7 +207,7 @@ struct range_rule_t;
@par BNF
@code
range = <N>*<M>rule
range = <N>*<M>next
@endcode
@par Specification
@@ -276,8 +298,8 @@ range_rule(
@par BNF
@code
range = <1>*<1>rule1
/ rule1 <N-1>*<M-1>rule2
range = <1>*<1>first
/ first <N-1>*<M-1>next
@endcode
@par Specification

View File

@@ -14,6 +14,7 @@
#include <boost/url/grammar/error.hpp>
#include <boost/url/grammar/detail/tuple.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/url/detail/empty_value.hpp>
#include <tuple>
namespace boost {
@@ -69,6 +70,8 @@ template<
class R0,
class... Rn>
class sequence_rule_t
: urls::detail::empty_value<
detail::tuple<R0, Rn...>>
{
using T = mp11::mp_remove<
std::tuple<
@@ -104,11 +107,12 @@ private:
sequence_rule_t(
R0 const& r0,
Rn const&... rn) noexcept
: rn_(r0, rn...)
: urls::detail::empty_value<
detail::tuple<R0, Rn...>>(
urls::detail::empty_init,
r0, rn...)
{
}
detail::tuple<R0, Rn...> const rn_;
};
template<

View File

@@ -13,6 +13,7 @@
#include <boost/url/detail/config.hpp>
#include <boost/url/result.hpp>
#include <boost/url/variant.hpp>
#include <boost/url/detail/empty_value.hpp>
#include <boost/url/grammar/detail/tuple.hpp>
namespace boost {
@@ -69,6 +70,8 @@ variant_rule( Rules... rn ) noexcept;
template<
class R0, class... Rn>
class variant_rule_t
: private urls::detail::empty_value<
detail::tuple<R0, Rn...>>
{
public:
using value_type = variant<
@@ -97,11 +100,12 @@ private:
variant_rule_t(
R0 const& r0,
Rn const&... rn) noexcept
: rn_(r0, rn...)
: urls::detail::empty_value<
detail::tuple<R0, Rn...>>(
urls::detail::empty_init,
r0, rn...)
{
}
detail::tuple<R0, Rn...> rn_;
};
template<

View File

@@ -13,7 +13,6 @@
#include <boost/url/detail/except.hpp>
#include <boost/url/detail/segments_encoded_iterator_impl.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <cstdint>
#include <iterator>

View File

@@ -13,11 +13,11 @@
#include <boost/url/segments_view.hpp>
#include <boost/url/error.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/rfc/query_rule.hpp>
#include <boost/url/detail/except.hpp>
#include <boost/url/detail/path.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/url/rfc/path_rules.hpp>
#include <boost/url/rfc/query_rule.hpp>
#include <ostream>
namespace boost {

View File

@@ -12,9 +12,6 @@
#define BOOST_URL_IMPL_SEGMENTS_VIEW_IPP
#include <boost/url/segments_view.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/assert.hpp>
#include <ostream>
namespace boost {

View File

@@ -20,7 +20,7 @@
#include <boost/url/detail/except.hpp>
#include <boost/url/detail/print.hpp>
#include <boost/url/rfc/authority_rule.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/rfc/path_rules.hpp>
#include <boost/url/rfc/query_rule.hpp>
#include <boost/url/rfc/detail/charsets.hpp>
#include <boost/url/rfc/detail/fragment_rule.hpp>

View File

@@ -18,7 +18,7 @@
#include <boost/url/grammar/parse.hpp>
#include <boost/url/grammar/ascii.hpp>
#include <boost/url/rfc/authority_rule.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/rfc/path_rules.hpp>
#include <boost/url/rfc/pct_encoded_rule.hpp>
#include <boost/url/rfc/query_rule.hpp>
#include <boost/url/rfc/relative_ref_rule.hpp>

View File

@@ -33,7 +33,7 @@ namespace detail {
*/
constexpr auto fragment_rule =
pct_encoded_rule(
pchars + '/' + '?');
&detail::fragment_chars);
/** Rule for fragment-part

View File

@@ -11,9 +11,9 @@
#define BOOST_URL_RFC_DETAIL_HIER_PART_RULE_HPP
#include <boost/url/detail/config.hpp>
#include <boost/url/result.hpp>
#include <boost/url/pct_encoded_view.hpp>
#include <boost/url/rfc/authority_rule.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/grammar/range_rule.hpp>
namespace boost {
namespace urls {
@@ -39,7 +39,9 @@ struct hier_part_rule_t
{
bool has_authority = false;
authority_view authority;
parsed_path path;
// VFALCO This doesn't belong here
grammar::range<pct_encoded_view> path;
};
BOOST_URL_DECL

View File

@@ -11,8 +11,8 @@
#define BOOST_URL_RFC_DETAIL_IMPL_HIER_PART_RULE_IPP
#include <boost/url/rfc/detail/hier_part_rule.hpp>
#include <boost/url/rfc/path_rules.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/grammar/parse.hpp>
namespace boost {
@@ -43,9 +43,7 @@ parse(
path_rootless_rule);
if(rv.has_value())
{
auto const& v = *rv;
t.path.path = v.string();
t.path.count = v.size();
t.path = std::move(*rv);
return t;
}
it = it0;
@@ -62,9 +60,7 @@ parse(
path_absolute_rule);
if(! rv)
return rv.error();
auto const& p = *rv;
t.path.path = p.string();
t.path.count = p.size();
t.path = std::move(*rv);
t.has_authority = false;
return t;
}
@@ -86,10 +82,7 @@ parse(
it, end, path_abempty_rule);
if(! rv)
return rv.error();
auto const& v = *rv;
t.path.path = v.string();
t.path.count = v.size();
t.path = std::move(*rv);
t.has_authority = true;
}

View File

@@ -0,0 +1,45 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/CPPAlliance/url
//
#ifndef BOOST_URL_RFC_DETAIL_IMPL_PATH_RULES_IPP
#define BOOST_URL_RFC_DETAIL_IMPL_PATH_RULES_IPP
#include <boost/url/rfc/detail/path_rules.hpp>
#include <boost/url/rfc/path_rules.hpp>
#include <boost/url/rfc/pct_encoded_rule.hpp>
#include <boost/url/grammar/error.hpp>
#include <boost/url/grammar/parse.hpp>
namespace boost {
namespace urls {
namespace detail {
auto
segment_ns_rule_t::
parse(
char const*& it,
char const* end) const noexcept ->
result<value_type>
{
if(it == end)
return value_type{};
if(*it == '/')
return grammar::error::syntax;
auto rv = grammar::parse(
it, end, segment_rule);
if(rv.has_error())
return rv.error();
return *rv;
}
} // detail
} // urls
} // boost
#endif

View File

@@ -11,7 +11,7 @@
#define BOOST_URL_RFC_DETAIL_IMPL_RELATIVE_PART_RULE_IPP
#include <boost/url/rfc/detail/relative_part_rule.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/rfc/path_rules.hpp>
#include <boost/url/grammar/parse.hpp>
namespace boost {
@@ -42,9 +42,7 @@ parse(
if(rv.has_value())
{
// path-noscheme
auto const& v = *rv;
t.path.path = v.string();
t.path.count = v.size();
t.path = std::move(*rv);
t.has_authority = false;
return t;
}
@@ -60,9 +58,7 @@ parse(
auto rv = grammar::parse(
it, end,
path_absolute_rule);
auto const& v = *rv;
t.path.path = v.string();
t.path.count = v.size();
t.path = std::move(*rv);
t.has_authority = false;
return t;
}
@@ -81,9 +77,7 @@ parse(
it, end, path_abempty_rule);
if(! rv)
return rv.error();
auto const& v = *rv;
t.path.path = v.string();
t.path.count = v.size();
t.path = std::move(*rv);
t.has_authority = true;
}
return t;

View File

@@ -38,7 +38,7 @@ parse(
// user
auto rv = grammar::parse(
it, end,
pct_encoded_rule(uchars));
pct_encoded_rule(&uchars));
if(! rv)
return rv.error();
t.user = *rv;
@@ -56,7 +56,7 @@ parse(
// pass
rv = grammar::parse(
it, end,
pct_encoded_rule(pwchars));
pct_encoded_rule(&pwchars));
if(! rv)
return rv.error();

View File

@@ -1,53 +0,0 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/CPPAlliance/url
//
#ifndef BOOST_URL_RFC_DETAIL_PATH_INCREMENT_IPP
#define BOOST_URL_RFC_DETAIL_PATH_INCREMENT_IPP
#include <boost/url/pct_encoded_view.hpp>
#include <boost/url/rfc/detail/segment_rule.hpp>
#include <boost/url/grammar/char_rule.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/url/grammar/sequence_rule.hpp>
#include <utility>
namespace boost {
namespace urls {
namespace detail {
struct path_increment_t
{
using value_type = pct_encoded_view;
result<value_type>
parse(
char const *&it,
char const* end) const noexcept
{
auto it0 = it;
auto rv = grammar::parse(
it, end,
grammar::sequence_rule(
grammar::char_rule('/'),
segment_rule));
if(rv.has_value())
return *rv;
it = it0;
return BOOST_URL_ERR(
grammar::error::end);
}
};
constexpr path_increment_t path_increment{};
} // detail
} // urls
} // boost
#endif

View File

@@ -7,10 +7,11 @@
// Official repository: https://github.com/CPPAlliance/url
//
#ifndef BOOST_URL_RFC_DETAIL_SEGMENT_RULE_HPP
#define BOOST_URL_RFC_DETAIL_SEGMENT_RULE_HPP
#ifndef BOOST_URL_RFC_DETAIL_PATH_RULES_HPP
#define BOOST_URL_RFC_DETAIL_PATH_RULES_HPP
#include <boost/url/grammar/not_empty_rule.hpp>
#include <boost/url/grammar/char_rule.hpp>
#include <boost/url/grammar/sequence_rule.hpp>
#include <boost/url/rfc/pchars.hpp>
#include <boost/url/rfc/pct_encoded_rule.hpp>
@@ -30,16 +31,10 @@ namespace detail {
>3.3. Path (rfc3986)</a>
@see
@ref path_abempty_rule,
@ref path_absolute_rule,
@ref path_noscheme_rule,
@ref path_rootless_rule,
@ref pchars,
@ref segment_nz_rule,
@ref segment_nz_nc_rule
@ref parse.
*/
constexpr auto segment_rule =
pct_encoded_rule(pchars);
pct_encoded_rule(&pchars);
//------------------------------------------------
@@ -65,7 +60,7 @@ constexpr auto segment_rule =
*/
constexpr auto segment_nz_rule =
grammar::not_empty_rule(
pct_encoded_rule(pchars));
pct_encoded_rule(&pchars));
//------------------------------------------------
@@ -82,20 +77,52 @@ constexpr auto segment_nz_rule =
>3.3. Path (rfc3986)</a>
@see
@ref path_abempty_rule,
@ref path_absolute_rule,
@ref path_noscheme_rule,
@ref path_rootless_rule,
@ref pchars,
@ref segment_rule,
@ref segment_nz_rule,
@ref sub_delim_chars,
@ref unreserved_chars
@ref parse.
*/
constexpr auto segment_nz_nc_rule =
grammar::not_empty_rule(
pct_encoded_rule(pchars - ':'));
//------------------------------------------------
/** Rule for slash-segment
@par BNF
@code
slash-segment = "/" segment
@endcode
*/
constexpr auto slash_segment_rule =
grammar::sequence_rule(
grammar::char_rule('/'),
pct_encoded_rule(&pchars));
//------------------------------------------------
// VFALCO This is an alternate rule not found
// in the rfc3986, to reformulate the rfc path
// BNFs into something the range_rule can use.
/** Rule for segment-ns
*/
#ifdef BOOST_URL_DOCS
constexpr __implementation_defined__ segment_ns_rule;
#else
struct segment_ns_rule_t
{
using value_type = pct_encoded_view;
BOOST_URL_DECL
result<value_type>
parse(
char const*& it,
char const* end) const noexcept;
};
constexpr segment_ns_rule_t segment_ns_rule{};
#endif
} // detail
} // urls
} // boost

View File

@@ -12,8 +12,9 @@
#include <boost/url/detail/config.hpp>
#include <boost/url/result.hpp>
#include <boost/url/pct_encoded_view.hpp>
#include <boost/url/rfc/authority_rule.hpp>
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/grammar/range_rule.hpp>
namespace boost {
namespace urls {
@@ -45,7 +46,7 @@ struct relative_part_rule_t
{
bool has_authority = false;
decltype(authority_rule)::value_type authority;
parsed_path path;
grammar::range<pct_encoded_view> path;
};
auto

View File

@@ -53,7 +53,9 @@ parse(
return rv.error();
if(rv->has_authority)
u.apply_authority(rv->authority);
u.apply_path(rv->path);
u.apply_path(
rv->path.string(),
rv->path.size());
}
// [ "?" query ]

View File

@@ -1,122 +0,0 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/CPPAlliance/url
//
#ifndef BOOST_URL_RFC_IMPL_PATHS_RULE_IPP
#define BOOST_URL_RFC_IMPL_PATHS_RULE_IPP
#include <boost/url/rfc/paths_rule.hpp>
#include <boost/url/rfc/pct_encoded_rule.hpp>
#include <boost/url/rfc/detail/charsets.hpp>
#include <boost/url/rfc/detail/path_increment.hpp>
#include <boost/url/grammar/char_rule.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/url/grammar/range_rule.hpp>
#include <boost/url/grammar/sequence_rule.hpp>
namespace boost {
namespace urls {
auto
path_abempty_rule_t::
parse(
char const*& it,
char const* end
) const noexcept ->
result<value_type>
{
return grammar::parse(
it, end, grammar::range_rule(
detail::path_increment));
}
//------------------------------------------------
auto
path_absolute_rule_t::
parse(
char const*& it,
char const* end
) const noexcept ->
result<value_type>
{
struct begin
{
using value_type = pct_encoded_view;
result<value_type>
parse(
char const *&it,
char const* end) const noexcept
{
if(it == end)
{
// expected '/'
return error::missing_path_segment;
}
if(*it != '/')
{
// expected '/'
return error::missing_path_separator;
}
++it;
if(it == end)
return pct_encoded_view{};
if(*it == '/')
{
// can't begin with "//"
return error::empty_path_segment;
}
return grammar::parse(
it, end, detail::segment_rule);
}
};
return grammar::parse(
it, end, grammar::range_rule(
begin{}, detail::path_increment));
}
//------------------------------------------------
auto
path_noscheme_rule_t::
parse(
char const*& it,
char const* end
) const noexcept ->
result<value_type>
{
return grammar::parse(
it, end,
grammar::range_rule(
detail::segment_nz_nc_rule,
detail::path_increment));
}
//------------------------------------------------
auto
path_rootless_rule_t::
parse(
char const*& it,
char const* end
) const noexcept ->
result<value_type>
{
return grammar::parse(
it, end,
grammar::range_rule(
detail::segment_nz_rule,
detail::path_increment));
}
} // urls
} // boost
#endif

View File

@@ -16,13 +16,15 @@
namespace boost {
namespace urls {
namespace detail {
template<class CharSet>
auto
pct_encoded_rule_t<CharSet>::
parse(
parse_pct_encoded(
char const*& it,
char const* end) const noexcept ->
result<value_type>
char const* end,
CharSet const& cs) noexcept ->
result<pct_encoded_view>
{
auto const start = it;
// VFALCO TODO
@@ -32,7 +34,7 @@ parse(
skip:
it0 = it;
it = grammar::find_if_not(
it0, end, cs_);
it0, end, cs);
n += it - it0;
if(it == end)
goto finish;
@@ -71,10 +73,38 @@ skip:
goto skip;
}
finish:
return detail::access::construct(
return access::construct(
string_view(start, it - start), n);
}
} // detail
//------------------------------------------------
template<class CharSet>
auto
pct_encoded_rule_t<CharSet>::
parse(
char const*& it,
char const* end) const noexcept ->
result<value_type>
{
return detail::parse_pct_encoded(
it, end, this->get());
}
template<class CharSet>
auto
pct_encoded_ref_rule_t<CharSet>::
parse(
char const*& it,
char const* end) const noexcept ->
result<value_type>
{
return detail::parse_pct_encoded(
it, end, *cs_);
}
} // urls
} // boost

View File

@@ -56,7 +56,7 @@ struct query_param_rule_t
// key
{
auto rv = grammar::parse(it, end,
pct_encoded_rule(detail::key_chars));
pct_encoded_rule(&detail::key_chars));
if(! rv)
return rv.error();
t.key = *rv;
@@ -78,7 +78,7 @@ struct query_param_rule_t
// value
{
auto rv = grammar::parse(it, end,
pct_encoded_rule(detail::value_chars));
pct_encoded_rule(&detail::value_chars));
if(! rv)
return rv.error();
t.value = *rv;
@@ -115,10 +115,7 @@ parse(
it, end,
grammar::char_rule('&'));
if(! rv)
{
// end of list
return grammar::error::end;
}
return rv.error();
}
return grammar::parse(it, end,

View File

@@ -42,7 +42,9 @@ parse(
return rv.error();
if(rv->has_authority)
u.apply_authority(rv->authority);
u.apply_path(rv->path);
u.apply_path(
rv->path.string(),
rv->path.size());
}
// [ "?" query ]

View File

@@ -55,7 +55,9 @@ parse(
return rv.error();
if(rv->has_authority)
u.apply_authority(rv->authority);
u.apply_path(rv->path);
u.apply_path(
rv->path.string(),
rv->path.size());
}
// [ "?" query ]

View File

@@ -7,32 +7,23 @@
// Official repository: https://github.com/CPPAlliance/url
//
#ifndef BOOST_URL_RFC_PATHS_RULE_HPP
#define BOOST_URL_RFC_PATHS_RULE_HPP
#ifndef BOOST_URL_RFC_PATH_RULES_HPP
#define BOOST_URL_RFC_PATH_RULES_HPP
#include <boost/url/detail/config.hpp>
#include <boost/url/error_code.hpp>
#include <boost/url/grammar/char_rule.hpp>
#include <boost/url/grammar/not_empty_rule.hpp>
#include <boost/url/grammar/range_rule.hpp>
#include <boost/url/grammar/sequence_rule.hpp>
#include <boost/url/rfc/pchars.hpp>
#include <boost/url/rfc/pct_encoded_rule.hpp>
#include <boost/url/rfc/detail/path_rules.hpp>
#include <cstddef>
namespace boost {
namespace urls {
/** Information about a parsed path
*/
struct parsed_path
{
/** The encoded string representing the path
*/
string_view path;
/** The number of segments in the path
*/
std::size_t count = 0;
};
/** Rule for path-abempty grammar
@par BNF
@@ -47,21 +38,11 @@ struct parsed_path
#ifdef BOOST_URL_DOCS
constexpr __implementation_defined__ path_abempty_rule;
#else
struct path_abempty_rule_t
{
using value_type =
grammar::range<
pct_encoded_view>;
BOOST_URL_DECL
result<value_type>
parse(
char const*& it,
char const* end
) const noexcept;
};
constexpr path_abempty_rule_t path_abempty_rule{};
constexpr auto path_abempty_rule =
grammar::range_rule(
grammar::sequence_rule(
grammar::char_rule('/'),
detail::segment_rule));
#endif
//------------------------------------------------
@@ -80,21 +61,13 @@ constexpr path_abempty_rule_t path_abempty_rule{};
#ifdef BOOST_URL_DOCS
constexpr __implementation_defined__ path_absolute_rule;
#else
struct path_absolute_rule_t
{
using value_type =
grammar::range<
pct_encoded_view>;
BOOST_URL_DECL
result<value_type>
parse(
char const*& it,
char const* end
) const noexcept;
};
constexpr path_absolute_rule_t path_absolute_rule{};
constexpr auto path_absolute_rule =
grammar::range_rule(
grammar::sequence_rule(
grammar::char_rule('/'),
detail::segment_ns_rule),
detail::slash_segment_rule,
1);
#endif
//------------------------------------------------
@@ -113,21 +86,11 @@ constexpr path_absolute_rule_t path_absolute_rule{};
#ifdef BOOST_URL_DOCS
constexpr __implementation_defined__ path_noscheme_rule;
#else
struct path_noscheme_rule_t
{
using value_type =
grammar::range<
pct_encoded_view>;
BOOST_URL_DECL
result<value_type>
parse(
char const*& it,
char const* end
) const noexcept;
};
constexpr path_noscheme_rule_t path_noscheme_rule{};
constexpr auto path_noscheme_rule =
grammar::range_rule(
detail::segment_nz_nc_rule,
detail::slash_segment_rule,
1);
#endif
//------------------------------------------------
@@ -152,21 +115,11 @@ constexpr path_noscheme_rule_t path_noscheme_rule{};
#ifdef BOOST_URL_DOCS
constexpr __implementation_defined__ path_rootless_rule;
#else
struct path_rootless_rule_t
{
using value_type =
grammar::range<
pct_encoded_view>;
BOOST_URL_DECL
result<value_type>
parse(
char const*& it,
char const* end
) const noexcept;
};
constexpr path_rootless_rule_t path_rootless_rule{};
constexpr auto path_rootless_rule =
grammar::range_rule(
detail::segment_nz_rule,
detail::slash_segment_rule,
1);
#endif
} // urls

View File

@@ -15,6 +15,7 @@
#include <boost/url/pct_encoding.hpp>
#include <boost/url/result.hpp>
#include <boost/url/string_view.hpp>
#include <boost/url/detail/empty_value.hpp>
#include <boost/url/grammar/charset.hpp>
#include <boost/url/grammar/error.hpp>
#include <boost/static_assert.hpp>
@@ -78,13 +79,21 @@ namespace urls {
@ref pct_encoded_view
*/
#ifdef BOOST_URL_DOCS
/**@{*/
template<class CharSet>
constexpr
__implementation_defined__
pct_encoded_rule( CharSet cs ) noexcept;
pct_encoded_rule( CharSet const& cs ) noexcept;
template<class CharSet>
constexpr
__implementation_defined__
pct_encoded_rule( CharSet const* cs ) noexcept;
/**@}*/
#else
template<class CharSet>
struct pct_encoded_rule_t
: private detail::empty_value<CharSet>
{
using value_type = pct_encoded_view;
@@ -102,16 +111,44 @@ struct pct_encoded_rule_t
char const* end) const noexcept;
private:
CharSet cs_;
constexpr
pct_encoded_rule_t(
CharSet const& cs) noexcept
: cs_(cs)
: detail::empty_value<CharSet>(
detail::empty_init, cs)
{
}
};
template<class CharSet>
struct pct_encoded_ref_rule_t
{
using value_type = pct_encoded_view;
template<class CharSet_>
friend
constexpr
auto
pct_encoded_rule(
CharSet_ const* cs) noexcept ->
pct_encoded_ref_rule_t<CharSet_>;
result<value_type>
parse(
char const*& it,
char const* end) const noexcept;
private:
constexpr
pct_encoded_ref_rule_t(
CharSet const& cs) noexcept
: cs_(&cs)
{
}
CharSet const* cs_;
};
template<class CharSet>
constexpr
auto
@@ -129,6 +166,25 @@ pct_encoded_rule(
return pct_encoded_rule_t<CharSet>(cs);
}
template<class CharSet>
constexpr
auto
pct_encoded_rule(
CharSet const* cs) noexcept ->
pct_encoded_ref_rule_t<CharSet>
{
// If an error occurs here it means that
// the value of your type does not meet
// the requirements. Please check the
// documentation!
static_assert(
grammar::is_charset<CharSet>::value,
"CharSet requirements not met");
return pct_encoded_ref_rule_t<CharSet>(*cs);
}
#endif
} // urls

View File

@@ -69,7 +69,6 @@ in a translation unit of the program.
#include <boost/url/rfc/impl/authority_rule.ipp>
#include <boost/url/rfc/impl/ipv4_address_rule.ipp>
#include <boost/url/rfc/impl/ipv6_address_rule.ipp>
#include <boost/url/rfc/impl/paths_rule.ipp>
#include <boost/url/rfc/impl/query_rule.ipp>
#include <boost/url/rfc/impl/relative_ref_rule.ipp>
#include <boost/url/rfc/impl/uri_rule.ipp>
@@ -80,6 +79,7 @@ in a translation unit of the program.
#include <boost/url/rfc/detail/impl/host_rule.ipp>
#include <boost/url/rfc/detail/impl/ip_literal_rule.ipp>
#include <boost/url/rfc/detail/impl/ipvfuture_rule.ipp>
#include <boost/url/rfc/detail/impl/path_rules.ipp>
#include <boost/url/rfc/detail/impl/port_rule.ipp>
#include <boost/url/rfc/detail/impl/relative_part_rule.ipp>
#include <boost/url/rfc/detail/impl/scheme_rule.ipp>