// // 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/boostorg/url // #ifndef BOOST_URL_GRAMMAR_RANGE_RULE_HPP #define BOOST_URL_GRAMMAR_RANGE_RULE_HPP #include #include #include #include #include #include #include #include #include #include // ::max_align_t namespace boost { namespace urls { namespace grammar { /** A forward range of parsed elements Objects of this type are forward ranges returned when parsing using the @ref range_rule. Iteration is performed by re-parsing the underlying character buffer. Ownership of the buffer is not transferred; the caller is responsible for ensuring that the lifetime of the buffer extends until it is no longer referenced by the range. @note The implementation may use temporary, recycled storage for type-erasure. Objects of type `range` are intended to be used ephemerally. That is, for short durations such as within a function scope. If it is necessary to store the range for a long period of time or with static storage duration, it is necessary to copy the contents to an object of a different type. @tparam T The value type of the range @see @ref parse, @ref range_rule. */ template class range { // buffer size for type-erased rule static constexpr std::size_t BufferSize = 128; struct small_buffer { alignas(alignof(::max_align_t)) unsigned char buf[BufferSize]; void const* addr() const noexcept { return buf; } void* addr() noexcept { return buf; } }; small_buffer sb_; string_view s_; std::size_t n_ = 0; //-------------------------------------------- struct any_rule; template struct impl1; template< class R0, class R1, bool> struct impl2; template< class R0, class R1> friend struct range_rule_t; any_rule& get() noexcept { return *reinterpret_cast< any_rule*>(sb_.addr()); } any_rule const& get() const noexcept { return *reinterpret_cast< any_rule const*>( sb_.addr()); } template 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 */ using value_type = T; /** The type of each element of the range */ using reference = T const&; /** The type of each element of the range */ using const_reference = T const&; /** Provided for compatibility, unused */ using pointer = void const*; /** The type used to represent unsigned integers */ using size_type = std::size_t; /** The type used to represent signed integers */ using difference_type = std::ptrdiff_t; /** A constant, forward iterator to elements of the range */ class iterator; /** A constant, forward iterator to elements of the range */ using const_iterator = iterator; /** Destructor */ ~range(); /** Constructor Default-constructed ranges have zero elements. @par Exception Safety Throws nothing. */ range() noexcept; /** Constructor The new range references the same underlying character buffer. Ownership is not transferred; the caller is responsible for ensuring that the lifetime of the buffer extends until it is no longer referenced. The moved-from object becomes as if default-constructed. @par Exception Safety Throws nothing. */ range(range&&) noexcept; /** Constructor The copy references the same underlying character buffer. Ownership is not transferred; the caller is responsible for ensuring that the lifetime of the buffer extends until it is no longer referenced. @par Exception Safety Throws nothing. */ range(range const&) noexcept; /** Constructor After the move, this references the same underlying character buffer. Ownership is not transferred; the caller is responsible for ensuring that the lifetime of the buffer extends until it is no longer referenced. The moved-from object becomes as if default-constructed. @par Exception Safety Throws nothing. */ range& operator=(range&&) noexcept; /** Assignment The copy references the same underlying character buffer. Ownership is not transferred; the caller is responsible for ensuring that the lifetime of the buffer extends until it is no longer referenced. @par Exception Safety Throws nothing. */ range& operator=(range const&) noexcept; /** Return an iterator to the beginning */ iterator begin() const noexcept; /** Return an iterator to the end */ iterator end() const noexcept; /** Return true if the range is empty */ bool empty() const noexcept { return n_ == 0; } /** Return the number of elements in the range */ std::size_t size() const noexcept { return n_; } /** Return the matching part of the string */ string_view string() const noexcept { return s_; } }; //------------------------------------------------ #ifndef BOOST_URL_DOCS template< class R0, class R1 = void> struct range_rule_t; #endif //------------------------------------------------ /** Match a repeating number of elements Elements are matched using the passed rule.
Normally when the rule returns an error, the range ends and the input is rewound to one past the last character that matched successfully. However, if the rule returns the special value @ref error::end_of_range, the input is not rewound. This allows for rules which consume input without producing elements in the range. For example, to relax the grammar for a comma-delimited list by allowing extra commas in between elements. @par Value Type @code using value_type = range< typename Rule::value_type >; @endcode @par Example Rules are used with the function @ref parse. @code // range = 1*( ";" token ) result< range > rv = parse( ";alpha;xray;charlie", range_rule( tuple_rule( squelch( delim_rule( ';' ) ), token_rule( alpha_chars ) ), 1 ) ); @endcode @par BNF @code range = *next @endcode @par Specification @li 3.6. Variable Repetition (rfc5234) @param next The rule to use for matching each element. The range extends until this rule returns an error. @param N The minimum number of elements for the range to be valid. If omitted, this defaults to zero. @param M The maximum number of elements for the range to be valid. If omitted, this defaults to unlimited. @see @ref alpha_chars, @ref delim_rule, @ref error::end_of_range, @ref parse, @ref range, @ref tuple_rule, @ref squelch. */ #ifdef BOOST_URL_DOCS template constexpr __implementation_defined__ range_rule( Rule next, std::size_t N = 0, std::size_t M = std::size_t(-1)) noexcept; #else template struct range_rule_t { using value_type = range; result parse( char const*& it, char const* end) const; private: constexpr range_rule_t( R const& next, std::size_t N, std::size_t M) noexcept : next_(next) , N_(N) , M_(M) { } template friend constexpr range_rule_t range_rule( R_ const& next, std::size_t N, std::size_t M) noexcept; R const next_; std::size_t N_; std::size_t M_; }; template constexpr range_rule_t range_rule( Rule const& next, std::size_t N = 0, std::size_t M = std::size_t(-1)) noexcept { // If you get a compile error here it // means that your rule does not meet // the type requirements. Please check // the documentation. static_assert( is_rule::value, "Rule requirements not met"); return range_rule_t{ next, N, M}; } #endif //------------------------------------------------ /** Match a repeating number of elements Two rules are used for match. The rule `first` is used for matching the first element, while the `next` rule is used to match every subsequent element.
Normally when the rule returns an error, the range ends and the input is rewound to one past the last character that matched successfully. However, if the rule returns the special value @ref error::end_of_range, the input is not rewound. This allows for rules which consume input without producing elements in the range. For example, to relax the grammar for a comma-delimited list by allowing extra commas in between elements. @par Value Type @code using value_type = range< typename Rule::value_type >; @endcode @par Example Rules are used with the function @ref parse. @code // range = [ token ] *( "," token ) result< range< string_view > > rv = parse( "whiskey,tango,foxtrot", range_rule( token_rule( alpha_chars ), // first tuple_rule( // next squelch( delim_rule(',') ), token_rule( alpha_chars ) ) ) ); @endcode @par BNF @code range = <1>*<1>first / first *next @endcode @par Specification @li 3.6. Variable Repetition (rfc5234) @param first The rule to use for matching the first element. If this rule returns an error, the range is empty. @param next The rule to use for matching each subsequent element. The range extends until this rule returns an error. @param N The minimum number of elements for the range to be valid. If omitted, this defaults to zero. @param M The maximum number of elements for the range to be valid. If omitted, this defaults to unlimited. @see @ref alpha_chars, @ref delim_rule, @ref error::end_of_range, @ref parse, @ref range, @ref tuple_rule, @ref squelch. */ #ifdef BOOST_URL_DOCS template< class Rule1, class Rule2> constexpr __implementation_defined__ range_rule( Rule1 first, Rule2 next, std::size_t N = 0, std::size_t M = std::size_t(-1)) noexcept; #else template struct range_rule_t { using value_type = range; result parse( char const*& it, char const* end) const; private: constexpr range_rule_t( R0 const& first, R1 const& next, std::size_t N, std::size_t M) noexcept : first_(first) , next_(next) , N_(N) , M_(M) { } template< class R0_, class R1_> friend constexpr auto range_rule( R0_ const& first, R1_ const& next, std::size_t N, std::size_t M) noexcept -> #if 1 typename std::enable_if< ! std::is_integral::value, range_rule_t>::type; #else range_rule_t; #endif R0 const first_; R1 const next_; std::size_t N_; std::size_t M_; }; template< class Rule1, class Rule2> constexpr auto range_rule( Rule1 const& first, Rule2 const& next, std::size_t N = 0, std::size_t M = std::size_t(-1)) noexcept -> #if 1 typename std::enable_if< ! std::is_integral::value, range_rule_t>::type #else range_rule_t #endif { // If you get a compile error here it // means that your rule does not meet // the type requirements. Please check // the documentation. static_assert( is_rule::value, "Rule requirements not met"); static_assert( is_rule::value, "Rule requirements not met"); // If you get a compile error here it // means that your rules do not have // the exact same value_type. Please // check the documentation. static_assert( std::is_same< typename Rule1::value_type, typename Rule2::value_type>::value, "Rule requirements not met"); return range_rule_t{ first, next, N, M}; } #endif } // grammar } // urls } // boost #include #endif