| /////////////////////////////////////////////////////////////////////////////// |
| /// \file match_results.hpp |
| /// Contains the definition of the match_results type and associated helpers. |
| /// The match_results type holds the results of a regex_match() or |
| /// regex_search() operation. |
| // |
| // Copyright 2008 Eric Niebler. 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) |
| // |
| // Acknowledgements: Thanks to Markus Schoepflin for helping to track down |
| // a tricky formatting bug on HP Tru64, and to Steven Watanabe for suggesting |
| // the fix. |
| |
| #ifndef BOOST_XPRESSIVE_MATCH_RESULTS_HPP_EAN_10_04_2005 |
| #define BOOST_XPRESSIVE_MATCH_RESULTS_HPP_EAN_10_04_2005 |
| |
| // MS compatible compilers support #pragma once |
| #if defined(_MSC_VER) && (_MSC_VER >= 1020) |
| # pragma once |
| #endif |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| #include <utility> |
| #include <iterator> |
| #include <typeinfo> |
| #include <algorithm> |
| #include <boost/config.hpp> |
| #include <boost/assert.hpp> |
| #include <boost/integer.hpp> |
| #include <boost/mpl/if.hpp> |
| #include <boost/mpl/not.hpp> |
| #include <boost/mpl/size_t.hpp> |
| #include <boost/mpl/assert.hpp> |
| #include <boost/intrusive_ptr.hpp> |
| #include <boost/throw_exception.hpp> |
| #include <boost/iterator_adaptors.hpp> |
| #include <boost/utility/enable_if.hpp> |
| #include <boost/detail/workaround.hpp> |
| #include <boost/numeric/conversion/converter.hpp> |
| #include <boost/optional.hpp> |
| #include <boost/range/end.hpp> |
| #include <boost/range/begin.hpp> |
| #include <boost/range/as_literal.hpp> |
| #include <boost/range/const_iterator.hpp> |
| #include <boost/type_traits/is_function.hpp> |
| #if BOOST_ITERATOR_ADAPTORS_VERSION >= 0x0200 |
| # include <boost/iterator/filter_iterator.hpp> |
| #endif |
| #include <boost/xpressive/regex_constants.hpp> |
| #include <boost/xpressive/detail/detail_fwd.hpp> |
| #include <boost/xpressive/detail/core/regex_impl.hpp> |
| #include <boost/xpressive/detail/core/sub_match_vector.hpp> |
| #include <boost/xpressive/detail/utility/sequence_stack.hpp> |
| #include <boost/xpressive/detail/core/results_cache.hpp> |
| #include <boost/xpressive/detail/utility/literals.hpp> |
| #include <boost/xpressive/detail/utility/algorithm.hpp> |
| #include <boost/xpressive/detail/utility/counted_base.hpp> |
| // Doxygen can't handle proto :-( |
| #ifndef BOOST_XPRESSIVE_DOXYGEN_INVOKED |
| # include <boost/proto/proto_fwd.hpp> |
| # include <boost/proto/traits.hpp> |
| # include <boost/proto/eval.hpp> |
| #endif |
| |
| namespace boost { namespace xpressive { namespace detail |
| { |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // type_info_less |
| // |
| struct type_info_less |
| { |
| bool operator()(std::type_info const *left, std::type_info const *right) const |
| { |
| return 0 != left->before(*right); |
| } |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // ActionArgBinding |
| // |
| struct ActionArgBinding |
| : proto::assign<proto::terminal<action_arg<proto::_, proto::_> >, proto::terminal<proto::_> > |
| { |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // results_extras |
| // |
| template<typename BidiIter> |
| struct results_extras |
| : counted_base<results_extras<BidiIter> > |
| { |
| sequence_stack<sub_match_impl<BidiIter> > sub_match_stack_; |
| results_cache<BidiIter> results_cache_; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // char_overflow_handler_ |
| // |
| struct char_overflow_handler_ |
| { |
| void operator ()(numeric::range_check_result result) const // throw(regex_error) |
| { |
| if(numeric::cInRange != result) |
| { |
| BOOST_THROW_EXCEPTION( |
| regex_error( |
| regex_constants::error_escape |
| , "character escape too large to fit in target character type" |
| ) |
| ); |
| } |
| } |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // transform_op enum |
| // |
| enum transform_op { None = 0, Upper = 1, Lower = 2 }; |
| enum transform_scope { Next = 0, Rest = 1 }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // case_converting_iterator |
| // |
| template<typename OutputIterator, typename Char> |
| struct case_converting_iterator |
| : std::iterator<std::output_iterator_tag, Char, void, void, case_converting_iterator<OutputIterator, Char> > |
| { |
| case_converting_iterator(OutputIterator const &out, traits<Char> const *tr) |
| : out_(out) |
| , traits_(tr) |
| , next_(None) |
| , rest_(None) |
| {} |
| |
| OutputIterator base() const |
| { |
| return this->out_; |
| } |
| |
| case_converting_iterator &operator ++() |
| { |
| ++this->out_; |
| this->next_ = None; |
| return *this; |
| } |
| |
| case_converting_iterator operator ++(int) |
| { |
| case_converting_iterator tmp(*this); |
| ++*this; |
| return tmp; |
| } |
| |
| case_converting_iterator &operator *() |
| { |
| return *this; |
| } |
| |
| friend bool set_transform(case_converting_iterator &iter, transform_op trans, transform_scope scope) |
| { |
| BOOST_ASSERT(scope == Next || scope == Rest); |
| if(scope == Next) |
| iter.next_ = trans; |
| else |
| iter.rest_ = trans; |
| return true; |
| } |
| |
| case_converting_iterator &operator =(Char ch) |
| { |
| switch(this->next_ ? this->next_ : this->rest_) |
| { |
| case Lower: |
| ch = this->traits_->tolower(ch); |
| break; |
| |
| case Upper: |
| ch = this->traits_->toupper(ch); |
| break; |
| |
| default:; |
| } |
| |
| *this->out_ = ch; |
| return *this; |
| } |
| |
| private: |
| OutputIterator out_; |
| traits<Char> const *traits_; |
| transform_op next_, rest_; |
| }; |
| |
| template<typename Iterator> |
| inline bool set_transform(Iterator &, transform_op, transform_scope) |
| { |
| return false; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // noop_output_iterator |
| // |
| template<typename Char> |
| struct noop_output_iterator |
| : std::iterator<std::output_iterator_tag, Char, void, void, noop_output_iterator<Char> > |
| { |
| noop_output_iterator &operator ++() |
| { |
| return *this; |
| } |
| |
| noop_output_iterator &operator ++(int) |
| { |
| return *this; |
| } |
| |
| noop_output_iterator &operator *() |
| { |
| return *this; |
| } |
| |
| noop_output_iterator &operator =(Char const &) |
| { |
| return *this; |
| } |
| }; |
| |
| struct any_type { any_type(...); }; |
| typedef char no_type; |
| typedef char (&unary_type)[2]; |
| typedef char (&binary_type)[3]; |
| typedef char (&ternary_type)[4]; |
| |
| no_type check_is_formatter(unary_type, binary_type, ternary_type); |
| |
| template<typename T> |
| unary_type check_is_formatter(T const &, binary_type, ternary_type); |
| |
| template<typename T> |
| binary_type check_is_formatter(unary_type, T const &, ternary_type); |
| |
| template<typename T, typename U> |
| binary_type check_is_formatter(T const &, U const &, ternary_type); |
| |
| template<typename T> |
| ternary_type check_is_formatter(unary_type, binary_type, T const &); |
| |
| template<typename T, typename U> |
| ternary_type check_is_formatter(T const &, binary_type, U const &); |
| |
| template<typename T, typename U> |
| ternary_type check_is_formatter(unary_type, T const &, U const &); |
| |
| template<typename T, typename U, typename V> |
| ternary_type check_is_formatter(T const &, U const &, V const &); |
| |
| struct unary_binary_ternary |
| { |
| typedef unary_type (*unary_fun)(any_type); |
| typedef binary_type (*binary_fun)(any_type, any_type); |
| typedef ternary_type (*ternary_fun)(any_type, any_type, any_type); |
| operator unary_fun(); |
| operator binary_fun(); |
| operator ternary_fun(); |
| }; |
| |
| template<typename Formatter, bool IsFunction = is_function<Formatter>::value> |
| struct formatter_wrapper |
| : Formatter |
| , unary_binary_ternary |
| { |
| formatter_wrapper(); |
| }; |
| |
| template<typename Formatter> |
| struct formatter_wrapper<Formatter, true> |
| : unary_binary_ternary |
| { |
| operator Formatter *(); |
| }; |
| |
| template<typename Formatter> |
| struct formatter_wrapper<Formatter *, false> |
| : unary_binary_ternary |
| { |
| operator Formatter *(); |
| }; |
| |
| template<typename Formatter, typename What, typename Out, typename Void = void> |
| struct formatter_arity |
| { |
| static formatter_wrapper<Formatter> &formatter; |
| static What &what; |
| static Out &out; |
| BOOST_STATIC_CONSTANT( |
| std::size_t |
| , value = sizeof( |
| check_is_formatter( |
| formatter(what) |
| , formatter(what, out) |
| , formatter(what, out, regex_constants::format_default) |
| ) |
| ) - 1 |
| ); |
| typedef mpl::size_t<value> type; |
| }; |
| |
| template<typename Formatter, typename What, typename Out> |
| struct formatter_arity<Formatter, What, Out, typename Formatter::proto_is_expr_> |
| : mpl::size_t<4> |
| {}; |
| |
| template<typename T> |
| struct is_char_ptr |
| : mpl::false_ |
| {}; |
| |
| template<typename T> |
| struct is_char_ptr<T *> |
| : mpl::not_<is_function<T> > |
| {}; |
| |
| #if BOOST_WORKAROUND(__GNUC__, == 4) && (__GNUC_MINOR__ == 0) |
| // work around gcc-4.0.1 compiler bug wrt function references |
| template<typename T> |
| typename mpl::if_<is_function<T>, T *, T const &>::type |
| as_callable(T const &t) |
| { |
| return t; |
| } |
| #endif |
| |
| } // detail |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // match_results |
| /// \brief Class template match_results\<\> holds the results of a regex_match() or a |
| /// regex_search() as a collection of sub_match objects. |
| /// |
| /// Class template match_results\<\> denotes a collection of sequences representing the result of |
| /// a regular expression match. Storage for the collection is allocated and freed as necessary by |
| /// the member functions of class match_results\<\>. |
| /// |
| /// The class template match_results\<\> conforms to the requirements of a Sequence, as specified |
| /// in (lib.sequence.reqmts), except that only operations defined for const-qualified Sequences are |
| /// supported. |
| template<typename BidiIter> |
| struct match_results |
| { |
| private: |
| /// INTERNAL ONLY |
| /// |
| struct dummy { int i_; }; |
| typedef int dummy::*bool_type; |
| |
| public: |
| typedef typename iterator_value<BidiIter>::type char_type; |
| typedef typename detail::string_type<char_type>::type string_type; |
| typedef std::size_t size_type; |
| typedef sub_match<BidiIter> value_type; |
| typedef typename iterator_difference<BidiIter>::type difference_type; |
| typedef value_type const &reference; |
| typedef value_type const &const_reference; |
| |
| typedef typename detail::sub_match_vector<BidiIter>::iterator iterator; |
| typedef typename detail::sub_match_vector<BidiIter>::const_iterator const_iterator; |
| typedef typename detail::nested_results<BidiIter> nested_results_type; |
| |
| /// \post regex_id() == 0 |
| /// \post size() == 0 |
| /// \post empty() == true |
| /// \post str() == string_type() |
| match_results() |
| : regex_id_(0) |
| , sub_matches_() |
| , base_() |
| , prefix_() |
| , suffix_() |
| , nested_results_() |
| , extras_ptr_() |
| , traits_() |
| , args_() |
| , named_marks_() |
| { |
| } |
| |
| /// \param that The match_results object to copy |
| /// \post regex_id() == that.regex_id(). |
| /// \post size() == that.size(). |
| /// \post empty() == that.empty(). |
| /// \post str(n) == that.str(n) for all positive integers n \< that.size(). |
| /// \post prefix() == that.prefix(). |
| /// \post suffix() == that.suffix(). |
| /// \post (*this)[n] == that[n] for all positive integers n \< that.size(). |
| /// \post length(n) == that.length(n) for all positive integers n \< that.size(). |
| /// \post position(n) == that.position(n) for all positive integers n \< that.size(). |
| match_results(match_results<BidiIter> const &that) |
| : regex_id_(that.regex_id_) |
| , sub_matches_() |
| , base_() |
| , prefix_() |
| , suffix_() |
| , nested_results_() |
| , extras_ptr_() |
| , traits_() |
| , args_(that.args_) |
| , named_marks_(that.named_marks_) |
| { |
| if(that) |
| { |
| extras_type &extras = this->get_extras_(); |
| std::size_t size = that.sub_matches_.size(); |
| detail::sub_match_impl<BidiIter> *sub_matches = extras.sub_match_stack_.push_sequence(size, detail::sub_match_impl<BidiIter>(*that.base_), detail::fill); |
| detail::core_access<BidiIter>::init_sub_match_vector(this->sub_matches_, sub_matches, size, that.sub_matches_); |
| |
| this->base_ = that.base_; |
| this->prefix_ = that.prefix_; |
| this->suffix_ = that.suffix_; |
| // BUGBUG this doesn't share the extras::sequence_stack |
| this->nested_results_ = that.nested_results_; |
| this->traits_ = that.traits_; |
| } |
| } |
| |
| ~match_results() |
| { |
| } |
| |
| /// \param that The match_results object to copy. |
| /// \post regex_id() == that.regex_id(). |
| /// \post size() == that.size(). |
| /// \post empty() == that.empty(). |
| /// \post str(n) == that.str(n) for all positive integers n \< that.size(). |
| /// \post prefix() == that.prefix(). |
| /// \post suffix() == that.suffix(). |
| /// \post (*this)[n] == that[n] for all positive integers n \< that.size(). |
| /// \post length(n) == that.length(n) for all positive integers n \< that.size(). |
| /// \post position(n) == that.position(n) for all positive integers n \< that.size(). |
| match_results<BidiIter> &operator =(match_results<BidiIter> const &that) |
| { |
| match_results<BidiIter>(that).swap(*this); |
| return *this; |
| } |
| |
| /// Returns one plus the number of marked sub-expressions in the regular |
| /// expression that was matched if *this represents the result of a |
| /// successful match. Otherwise returns 0. |
| size_type size() const |
| { |
| return this->sub_matches_.size(); |
| } |
| |
| /// Returns size() == 0. |
| /// |
| bool empty() const |
| { |
| return 0 == this->size(); |
| } |
| |
| /// Returns (*this)[sub].length(). |
| /// |
| difference_type length(size_type sub = 0) const |
| { |
| return this->sub_matches_[ sub ].length(); |
| } |
| |
| /// If !(*this)[sub].matched then returns -1. Otherwise returns std::distance(base, (*this)[sub].first), |
| /// where base is the start iterator of the sequence that was searched. [Note - unless this is part |
| /// of a repeated search with a regex_iterator then base is the same as prefix().first - end note] |
| difference_type position(size_type sub = 0) const |
| { |
| return this->sub_matches_[ sub ].matched ? std::distance(*this->base_, this->sub_matches_[ sub ].first) : -1; |
| } |
| |
| /// Returns (*this)[sub].str(). |
| /// |
| string_type str(size_type sub = 0) const |
| { |
| return this->sub_matches_[ sub ].str(); |
| } |
| |
| /// Returns a reference to the sub_match object representing the sequence that |
| /// matched marked sub-expression sub. If sub == 0 then returns a reference to |
| /// a sub_match object representing the sequence that matched the whole regular |
| /// expression. If sub >= size() then returns a sub_match object representing an |
| /// unmatched sub-expression. |
| template<typename Sub> |
| const_reference operator [](Sub const &sub) const |
| { |
| return this->at_(sub); |
| } |
| |
| /// Returns a reference to the sub_match object representing the character sequence from |
| /// the start of the string being matched/searched, to the start of the match found. |
| /// |
| /// \pre (*this)[0].matched is true |
| const_reference prefix() const |
| { |
| return this->prefix_ ? *this->prefix_ : this->sub_matches_[this->sub_matches_.size()]; |
| } |
| |
| /// Returns a reference to the sub_match object representing the character sequence from |
| /// the end of the match found to the end of the string being matched/searched. |
| /// |
| /// \pre (*this)[0].matched is true |
| const_reference suffix() const |
| { |
| return this->suffix_ ? *this->suffix_ : this->sub_matches_[this->sub_matches_.size()]; |
| } |
| |
| /// Returns a starting iterator that enumerates over all the marked sub-expression matches |
| /// stored in *this. |
| /// |
| const_iterator begin() const |
| { |
| return this->sub_matches_.begin(); |
| } |
| |
| /// Returns a terminating iterator that enumerates over all the marked sub-expression |
| /// matches stored in *this. |
| /// |
| const_iterator end() const |
| { |
| return this->sub_matches_.end(); |
| } |
| |
| /// Returns a true value if (*this)[0].matched, else returns a false value. |
| /// |
| operator bool_type() const |
| { |
| return (!this->empty() && this->sub_matches_[ 0 ].matched) ? &dummy::i_ : 0; |
| } |
| |
| /// Returns true if empty() || !(*this)[0].matched, else returns false. |
| /// |
| bool operator !() const |
| { |
| return this->empty() || !this->sub_matches_[ 0 ].matched; |
| } |
| |
| /// Returns the id of the basic_regex object most recently used with this match_results object. |
| /// |
| regex_id_type regex_id() const |
| { |
| return this->regex_id_; |
| } |
| |
| /// Returns a Sequence of nested match_results elements. |
| /// |
| nested_results_type const &nested_results() const |
| { |
| return this->nested_results_; |
| } |
| |
| /// If \c Format models \c ForwardRange or is a null-terminated string, this function |
| /// copies the character sequence in \c fmt to \c OutputIterator \c out. For each format |
| /// specifier or escape sequence in \c fmt, replace that sequence with either the character(s) it |
| /// represents, or the sequence within <tt>*this</tt> to which it refers. The bitmasks specified in flags |
| /// determines what format specifiers or escape sequences are recognized. By default, this is the |
| /// format used by ECMA-262, ECMAScript Language Specification, Chapter 15 part 5.4.11 String.prototype.replace. |
| /// |
| /// Otherwise, if \c Format models <tt>Callable\<match_results\<BidiIter\>, OutputIterator, regex_constants::match_flag_type\></tt>, |
| /// this function returns <tt>fmt(*this, out, flags)</tt>. |
| /// |
| /// Otherwise, if \c Format models <tt>Callable\<match_results\<BidiIter\>, OutputIterator\></tt>, this function |
| /// returns <tt>fmt(*this, out)</tt>. |
| /// |
| /// Otherwise, if \c Format models <tt>Callable\<match_results\<BidiIter\> \></tt>, this function |
| /// returns <tt>std::copy(x.begin(), x.end(), out)</tt>, where \c x is the result of |
| /// calling <tt>fmt(*this)</tt>. |
| template<typename Format, typename OutputIterator> |
| OutputIterator format |
| ( |
| OutputIterator out |
| , Format const &fmt |
| , regex_constants::match_flag_type flags = regex_constants::format_default |
| , typename disable_if<detail::is_char_ptr<Format> >::type * = 0 |
| ) const |
| { |
| // Is this a formatter object, or a format string? |
| typedef |
| typename detail::formatter_arity< |
| Format |
| , match_results<BidiIter> |
| , OutputIterator |
| >::type |
| arity; |
| |
| return this->format_(out, fmt, flags, arity()); |
| } |
| |
| /// \overload |
| /// |
| template<typename OutputIterator> |
| OutputIterator format |
| ( |
| OutputIterator out |
| , char_type const *fmt |
| , regex_constants::match_flag_type flags = regex_constants::format_default |
| ) const |
| { |
| return this->format_(out, boost::as_literal(fmt), flags, mpl::size_t<0>()); |
| } |
| |
| /// If \c Format models \c ForwardRange or is a null-terminated string, this function |
| /// returns a copy of the character sequence \c fmt. For each format specifier or escape sequence in \c fmt, |
| /// replace that sequence with either the character(s) it represents, or the sequence within |
| /// <tt>*this</tt> to which it refers. The bitmasks specified in \c flags determines what format specifiers |
| /// or escape sequences are recognized. By default this is the format used by ECMA-262, |
| /// ECMAScript Language Specification, Chapter 15 part 5.4.11 String.prototype.replace. |
| /// |
| /// Otherwise, if \c Format models <tt>Callable\<match_results\<BidiIter\>, OutputIterator, regex_constants::match_flag_type\></tt>, |
| /// this function returns a \c string_type object \c x populated by calling <tt>fmt(*this, out, flags)</tt>, |
| /// where \c out is a \c back_insert_iterator into \c x. |
| /// |
| /// Otherwise, if \c Format models <tt>Callable\<match_results\<BidiIter\>, OutputIterator\></tt>, this function |
| /// returns a \c string_type object \c x populated by calling <tt>fmt(*this, out)</tt>, |
| /// where \c out is a \c back_insert_iterator into \c x. |
| /// |
| /// Otherwise, if \c Format models <tt>Callable\<match_results\<BidiIter\> \></tt>, this function |
| /// returns <tt>fmt(*this)</tt>. |
| template<typename Format, typename OutputIterator> |
| string_type format |
| ( |
| Format const &fmt |
| , regex_constants::match_flag_type flags = regex_constants::format_default |
| , typename disable_if<detail::is_char_ptr<Format> >::type * = 0 |
| ) const |
| { |
| string_type result; |
| this->format(std::back_inserter(result), fmt, flags); |
| return result; |
| } |
| |
| /// \overload |
| /// |
| string_type format |
| ( |
| char_type const *fmt |
| , regex_constants::match_flag_type flags = regex_constants::format_default |
| ) const |
| { |
| string_type result; |
| this->format(std::back_inserter(result), fmt, flags); |
| return result; |
| } |
| |
| /// Swaps the contents of two match_results objects. Guaranteed not to throw. |
| /// \param that The match_results object to swap with. |
| /// \post *this contains the sequence of matched sub-expressions that were in that, |
| /// that contains the sequence of matched sub-expressions that were in *this. |
| /// \throw nothrow |
| void swap(match_results<BidiIter> &that) // throw() |
| { |
| using std::swap; |
| swap(this->regex_id_, that.regex_id_); |
| this->sub_matches_.swap(that.sub_matches_); |
| swap(this->base_, that.base_); |
| swap(this->prefix_, that.prefix_); |
| swap(this->suffix_, that.suffix_); |
| this->nested_results_.swap(that.nested_results_); |
| this->extras_ptr_.swap(that.extras_ptr_); |
| this->traits_.swap(that.traits_); |
| this->args_.swap(that.args_); |
| } |
| |
| /// TODO document me |
| /// |
| template<typename Arg> |
| match_results<BidiIter> &let(Arg const &arg) |
| { |
| typedef typename proto::result_of::left<Arg>::type left_type; |
| typedef typename proto::result_of::right<Arg>::type right_type; |
| typedef typename proto::result_of::value<left_type>::type arg_left_type; |
| typedef typename proto::result_of::value<right_type>::type arg_right_type; |
| BOOST_MPL_ASSERT((proto::matches<Arg, detail::ActionArgBinding>)); |
| BOOST_MPL_ASSERT((is_same<typename arg_left_type::type, arg_right_type>)); |
| this->args_[&typeid(proto::value(proto::left(arg)))] = &proto::value(proto::right(arg)); |
| return *this; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| match_results<BidiIter> const &operator ()(regex_id_type regex_id, size_type index = 0) const |
| { |
| // BUGBUG this is linear, make it O(1) |
| static match_results<BidiIter> const s_null; |
| |
| regex_id_filter_predicate<BidiIter> pred(regex_id); |
| typename nested_results_type::const_iterator |
| begin = this->nested_results_.begin() |
| , end = this->nested_results_.end() |
| , cur = detail::find_nth_if(begin, end, index, pred); |
| |
| return (cur == end) ? s_null : *cur; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| match_results<BidiIter> const &operator ()(basic_regex<BidiIter> const &rex, std::size_t index = 0) const |
| { |
| return (*this)(rex.regex_id(), index); |
| } |
| |
| private: |
| |
| friend struct detail::core_access<BidiIter>; |
| typedef detail::results_extras<BidiIter> extras_type; |
| |
| /// INTERNAL ONLY |
| /// |
| void init_ |
| ( |
| regex_id_type regex_id |
| , intrusive_ptr<detail::traits<char_type> const> const &tr |
| , detail::sub_match_impl<BidiIter> *sub_matches |
| , size_type size |
| , std::vector<detail::named_mark<char_type> > const &named_marks |
| ) |
| { |
| this->traits_ = tr; |
| this->regex_id_ = regex_id; |
| this->named_marks_ = named_marks; |
| detail::core_access<BidiIter>::init_sub_match_vector(this->sub_matches_, sub_matches, size); |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| extras_type &get_extras_() |
| { |
| if(!this->extras_ptr_) |
| { |
| this->extras_ptr_ = new extras_type; |
| } |
| |
| return *this->extras_ptr_; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| void set_prefix_suffix_(BidiIter begin, BidiIter end) |
| { |
| this->base_ = begin; |
| this->prefix_ = sub_match<BidiIter>(begin, this->sub_matches_[ 0 ].first, begin != this->sub_matches_[ 0 ].first); |
| this->suffix_ = sub_match<BidiIter>(this->sub_matches_[ 0 ].second, end, this->sub_matches_[ 0 ].second != end); |
| |
| typename nested_results_type::iterator ibegin = this->nested_results_.begin(); |
| typename nested_results_type::iterator iend = this->nested_results_.end(); |
| for( ; ibegin != iend; ++ibegin ) |
| { |
| ibegin->set_prefix_suffix_(begin, end); |
| } |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| void reset_() |
| { |
| detail::core_access<BidiIter>::init_sub_match_vector(this->sub_matches_, 0, 0); |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| void set_base_(BidiIter base) |
| { |
| this->base_ = base; |
| |
| typename nested_results_type::iterator ibegin = this->nested_results_.begin(); |
| typename nested_results_type::iterator iend = this->nested_results_.end(); |
| for( ; ibegin != iend; ++ibegin ) |
| { |
| ibegin->set_base_(base); |
| } |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| const_reference at_(size_type sub) const |
| { |
| return this->sub_matches_[ sub ]; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| const_reference at_(detail::basic_mark_tag const &mark) const |
| { |
| return this->sub_matches_[ detail::get_mark_number(mark) ]; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| const_reference at_(char_type const *name) const |
| { |
| for(std::size_t i = 0; i < this->named_marks_.size(); ++i) |
| { |
| if(this->named_marks_[i].name_ == name) |
| { |
| return this->sub_matches_[ this->named_marks_[i].mark_nbr_ ]; |
| } |
| } |
| BOOST_THROW_EXCEPTION( |
| regex_error(regex_constants::error_badmark, "invalid named back-reference") |
| ); |
| // Should never execute, but if it does, this returns |
| // a "null" sub_match. |
| return this->sub_matches_[this->sub_matches_.size()]; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| const_reference at_(string_type const &name) const |
| { |
| return (*this)[name.c_str()]; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename OutputIterator, typename ForwardRange> |
| OutputIterator format2_(OutputIterator out, ForwardRange const &result) const |
| { |
| return std::copy(boost::begin(result), boost::end(result), out); |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename OutputIterator, typename Char> |
| OutputIterator format2_(OutputIterator out, Char const *const &result) const |
| { |
| Char const *tmp = result; |
| BOOST_ASSERT(0 != tmp); |
| for(; 0 != *tmp; ++tmp, ++out) |
| { |
| *out = *tmp; |
| } |
| return out; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename OutputIterator, typename ForwardRange> |
| OutputIterator format_ |
| ( |
| OutputIterator out |
| , ForwardRange const &format |
| , regex_constants::match_flag_type flags |
| , mpl::size_t<0> |
| ) const |
| { |
| typedef typename range_const_iterator<ForwardRange>::type iterator; |
| iterator cur = boost::begin(format), end = boost::end(format); |
| |
| if(0 != (regex_constants::format_literal & flags)) |
| { |
| return std::copy(cur, end, out); |
| } |
| else if(0 != (regex_constants::format_perl & flags)) |
| { |
| return this->format_perl_(cur, end, out); |
| } |
| else if(0 != (regex_constants::format_sed & flags)) |
| { |
| return this->format_sed_(cur, end, out); |
| } |
| else if(0 != (regex_constants::format_all & flags)) |
| { |
| return this->format_all_(cur, end, out); |
| } |
| |
| return this->format_ecma_262_(cur, end, out); |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename OutputIterator, typename Callable1> |
| OutputIterator format_ |
| ( |
| OutputIterator out |
| , Callable1 const &format |
| , regex_constants::match_flag_type |
| , mpl::size_t<1> |
| ) const |
| { |
| #if BOOST_WORKAROUND(__GNUC__, == 4) && (__GNUC_MINOR__ == 0) |
| return this->format2_(out, detail::as_callable(format)(*this)); |
| #else |
| return this->format2_(out, format(*this)); |
| #endif |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename OutputIterator, typename Callable2> |
| OutputIterator format_ |
| ( |
| OutputIterator out |
| , Callable2 const &format |
| , regex_constants::match_flag_type |
| , mpl::size_t<2> |
| ) const |
| { |
| #if BOOST_WORKAROUND(__GNUC__, == 4) && (__GNUC_MINOR__ == 0) |
| return detail::as_callable(format)(*this, out); |
| #else |
| return format(*this, out); |
| #endif |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename OutputIterator, typename Callable3> |
| OutputIterator format_ |
| ( |
| OutputIterator out |
| , Callable3 const &format |
| , regex_constants::match_flag_type flags |
| , mpl::size_t<3> |
| ) const |
| { |
| #if BOOST_WORKAROUND(__GNUC__, == 4) && (__GNUC_MINOR__ == 0) |
| return detail::as_callable(format)(*this, out, flags); |
| #else |
| return format(*this, out, flags); |
| #endif |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename OutputIterator, typename Expr> |
| OutputIterator format_ |
| ( |
| OutputIterator out |
| , Expr const &format |
| , regex_constants::match_flag_type flags |
| , mpl::size_t<4> |
| ) const |
| { |
| detail::replacement_context<BidiIter> ctx(*this); |
| return this->format2_(out, proto::eval(format, ctx)); |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename ForwardIterator, typename OutputIterator> |
| OutputIterator format_ecma_262_(ForwardIterator cur, ForwardIterator end, OutputIterator out) const |
| { |
| while(cur != end) |
| { |
| switch(*cur) |
| { |
| case BOOST_XPR_CHAR_(char_type, '$'): |
| out = this->format_backref_(++cur, end, out); |
| break; |
| |
| default: |
| *out++ = *cur++; |
| break; |
| } |
| } |
| |
| return out; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename ForwardIterator, typename OutputIterator> |
| OutputIterator format_sed_(ForwardIterator cur, ForwardIterator end, OutputIterator out) const |
| { |
| while(cur != end) |
| { |
| switch(*cur) |
| { |
| case BOOST_XPR_CHAR_(char_type, '&'): |
| ++cur; |
| out = std::copy(this->sub_matches_[ 0 ].first, this->sub_matches_[ 0 ].second, out); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, '\\'): |
| out = this->format_escape_(++cur, end, out); |
| break; |
| |
| default: |
| *out++ = *cur++; |
| break; |
| } |
| } |
| |
| return out; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename ForwardIterator, typename OutputIterator> |
| OutputIterator format_perl_(ForwardIterator cur, ForwardIterator end, OutputIterator out) const |
| { |
| detail::case_converting_iterator<OutputIterator, char_type> iout(out, this->traits_.get()); |
| |
| while(cur != end) |
| { |
| switch(*cur) |
| { |
| case BOOST_XPR_CHAR_(char_type, '$'): |
| iout = this->format_backref_(++cur, end, iout); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, '\\'): |
| if(++cur != end && BOOST_XPR_CHAR_(char_type, 'g') == *cur) |
| { |
| iout = this->format_named_backref_(++cur, end, iout); |
| } |
| else |
| { |
| iout = this->format_escape_(cur, end, iout); |
| } |
| break; |
| |
| default: |
| *iout++ = *cur++; |
| break; |
| } |
| } |
| |
| return iout.base(); |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename ForwardIterator, typename OutputIterator> |
| OutputIterator format_all_(ForwardIterator cur, ForwardIterator end, OutputIterator out) const |
| { |
| detail::case_converting_iterator<OutputIterator, char_type> iout(out, this->traits_.get()); |
| iout = this->format_all_impl_(cur, end, iout); |
| BOOST_XPR_ENSURE_(cur == end |
| , regex_constants::error_paren, "unbalanced parentheses in format string"); |
| return iout.base(); |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename ForwardIterator, typename OutputIterator> |
| OutputIterator format_all_impl_(ForwardIterator &cur, ForwardIterator end, OutputIterator out, bool metacolon = false) const |
| { |
| int max = 0, sub = 0; |
| detail::noop_output_iterator<char_type> noop; |
| |
| while(cur != end) |
| { |
| switch(*cur) |
| { |
| case BOOST_XPR_CHAR_(char_type, '$'): |
| out = this->format_backref_(++cur, end, out); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, '\\'): |
| if(++cur != end && BOOST_XPR_CHAR_(char_type, 'g') == *cur) |
| { |
| out = this->format_named_backref_(++cur, end, out); |
| } |
| else |
| { |
| out = this->format_escape_(cur, end, out); |
| } |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, '('): |
| out = this->format_all_impl_(++cur, end, out); |
| BOOST_XPR_ENSURE_(BOOST_XPR_CHAR_(char_type, ')') == *(cur-1) |
| , regex_constants::error_paren, "unbalanced parentheses in format string"); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, '?'): |
| BOOST_XPR_ENSURE_(++cur != end |
| , regex_constants::error_subreg, "malformed conditional in format string"); |
| max = static_cast<int>(this->size() - 1); |
| sub = detail::toi(cur, end, *this->traits_, 10, max); |
| BOOST_XPR_ENSURE_(0 != sub, regex_constants::error_subreg, "invalid back-reference"); |
| if(this->sub_matches_[ sub ].matched) |
| { |
| out = this->format_all_impl_(cur, end, out, true); |
| if(BOOST_XPR_CHAR_(char_type, ':') == *(cur-1)) |
| this->format_all_impl_(cur, end, noop); |
| } |
| else |
| { |
| this->format_all_impl_(cur, end, noop, true); |
| if(BOOST_XPR_CHAR_(char_type, ':') == *(cur-1)) |
| out = this->format_all_impl_(cur, end, out); |
| } |
| return out; |
| |
| case BOOST_XPR_CHAR_(char_type, ':'): |
| if(metacolon) |
| { |
| case BOOST_XPR_CHAR_(char_type, ')'): |
| ++cur; |
| return out; |
| } |
| // else fall-through |
| |
| default: |
| *out++ = *cur++; |
| break; |
| } |
| } |
| |
| return out; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename ForwardIterator, typename OutputIterator> |
| OutputIterator format_backref_ |
| ( |
| ForwardIterator &cur |
| , ForwardIterator end |
| , OutputIterator out |
| ) const |
| { |
| if(cur == end) |
| { |
| *out++ = BOOST_XPR_CHAR_(char_type, '$'); |
| } |
| else if(BOOST_XPR_CHAR_(char_type, '$') == *cur) |
| { |
| *out++ = *cur++; |
| } |
| else if(BOOST_XPR_CHAR_(char_type, '&') == *cur) // whole match |
| { |
| ++cur; |
| out = std::copy(this->sub_matches_[ 0 ].first, this->sub_matches_[ 0 ].second, out); |
| } |
| else if(BOOST_XPR_CHAR_(char_type, '`') == *cur) // prefix |
| { |
| ++cur; |
| out = std::copy(this->prefix().first, this->prefix().second, out); |
| } |
| else if(BOOST_XPR_CHAR_(char_type, '\'') == *cur) // suffix |
| { |
| ++cur; |
| out = std::copy(this->suffix().first, this->suffix().second, out); |
| } |
| else if(-1 != this->traits_->value(*cur, 10)) // a sub-match |
| { |
| int max = static_cast<int>(this->size() - 1); |
| int sub = detail::toi(cur, end, *this->traits_, 10, max); |
| BOOST_XPR_ENSURE_(0 != sub, regex_constants::error_subreg, "invalid back-reference"); |
| if(this->sub_matches_[ sub ].matched) |
| out = std::copy(this->sub_matches_[ sub ].first, this->sub_matches_[ sub ].second, out); |
| } |
| else |
| { |
| *out++ = BOOST_XPR_CHAR_(char_type, '$'); |
| *out++ = *cur++; |
| } |
| |
| return out; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename ForwardIterator, typename OutputIterator> |
| OutputIterator format_escape_ |
| ( |
| ForwardIterator &cur |
| , ForwardIterator end |
| , OutputIterator out |
| ) const |
| { |
| using namespace regex_constants; |
| ForwardIterator tmp; |
| // define an unsigned type the same size as char_type |
| typedef typename boost::uint_t<CHAR_BIT * sizeof(char_type)>::least uchar_t; |
| BOOST_MPL_ASSERT_RELATION(sizeof(uchar_t), ==, sizeof(char_type)); |
| typedef numeric::conversion_traits<uchar_t, int> converstion_traits; |
| numeric::converter<int, uchar_t, converstion_traits, detail::char_overflow_handler_> converter; |
| |
| if(cur == end) |
| { |
| *out++ = BOOST_XPR_CHAR_(char_type, '\\'); |
| return out; |
| } |
| |
| char_type ch = *cur++; |
| switch(ch) |
| { |
| case BOOST_XPR_CHAR_(char_type, 'a'): |
| *out++ = BOOST_XPR_CHAR_(char_type, '\a'); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'e'): |
| *out++ = converter(27); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'f'): |
| *out++ = BOOST_XPR_CHAR_(char_type, '\f'); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'n'): |
| *out++ = BOOST_XPR_CHAR_(char_type, '\n'); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'r'): |
| *out++ = BOOST_XPR_CHAR_(char_type, '\r'); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 't'): |
| *out++ = BOOST_XPR_CHAR_(char_type, '\t'); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'v'): |
| *out++ = BOOST_XPR_CHAR_(char_type, '\v'); |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'x'): |
| BOOST_XPR_ENSURE_(cur != end, error_escape, "unexpected end of format found"); |
| if(BOOST_XPR_CHAR_(char_type, '{') == *cur) |
| { |
| BOOST_XPR_ENSURE_(++cur != end, error_escape, "unexpected end of format found"); |
| tmp = cur; |
| *out++ = converter(detail::toi(cur, end, *this->traits_, 16, 0xffff)); |
| BOOST_XPR_ENSURE_(4 == std::distance(tmp, cur) && cur != end && BOOST_XPR_CHAR_(char_type, '}') == *cur++ |
| , error_escape, "invalid hex escape : must be \\x { HexDigit HexDigit HexDigit HexDigit }"); |
| } |
| else |
| { |
| tmp = cur; |
| *out++ = converter(detail::toi(cur, end, *this->traits_, 16, 0xff)); |
| BOOST_XPR_ENSURE_(2 == std::distance(tmp, cur), error_escape |
| , "invalid hex escape : must be \\x HexDigit HexDigit"); |
| } |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'c'): |
| BOOST_XPR_ENSURE_(cur != end, error_escape, "unexpected end of format found"); |
| BOOST_XPR_ENSURE_ |
| ( |
| this->traits_->in_range(BOOST_XPR_CHAR_(char_type, 'a'), BOOST_XPR_CHAR_(char_type, 'z'), *cur) |
| || this->traits_->in_range(BOOST_XPR_CHAR_(char_type, 'A'), BOOST_XPR_CHAR_(char_type, 'Z'), *cur) |
| , error_escape |
| , "invalid escape control letter; must be one of a-z or A-Z" |
| ); |
| // Convert to character according to ECMA-262, section 15.10.2.10: |
| *out++ = converter(*cur % 32); |
| ++cur; |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'l'): |
| if(!set_transform(out, detail::Lower, detail::Next)) |
| { |
| *out++ = BOOST_XPR_CHAR_(char_type, 'l'); |
| } |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'L'): |
| if(!set_transform(out, detail::Lower, detail::Rest)) |
| { |
| *out++ = BOOST_XPR_CHAR_(char_type, 'L'); |
| } |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'u'): |
| if(!set_transform(out, detail::Upper, detail::Next)) |
| { |
| *out++ = BOOST_XPR_CHAR_(char_type, 'u'); |
| } |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'U'): |
| if(!set_transform(out, detail::Upper, detail::Rest)) |
| { |
| *out++ = BOOST_XPR_CHAR_(char_type, 'U'); |
| } |
| break; |
| |
| case BOOST_XPR_CHAR_(char_type, 'E'): |
| if(!set_transform(out, detail::None, detail::Rest)) |
| { |
| *out++ = BOOST_XPR_CHAR_(char_type, 'E'); |
| } |
| break; |
| |
| default: |
| // BUGBUG what about backreferences like \12 ? |
| if(0 < this->traits_->value(ch, 10)) |
| { |
| int sub = this->traits_->value(ch, 10); |
| if(this->sub_matches_[ sub ].matched) |
| out = std::copy(this->sub_matches_[ sub ].first, this->sub_matches_[ sub ].second, out); |
| } |
| else |
| { |
| *out++ = ch; |
| } |
| break; |
| } |
| |
| return out; |
| } |
| |
| /// INTERNAL ONLY |
| /// |
| template<typename ForwardIterator, typename OutputIterator> |
| OutputIterator format_named_backref_ |
| ( |
| ForwardIterator &cur |
| , ForwardIterator end |
| , OutputIterator out |
| ) const |
| { |
| using namespace regex_constants; |
| BOOST_XPR_ENSURE_(cur != end && BOOST_XPR_CHAR_(char_type, '<') == *cur++ |
| , error_badmark, "invalid named back-reference"); |
| ForwardIterator begin = cur; |
| for(; cur != end && BOOST_XPR_CHAR_(char_type, '>') != *cur; ++cur) |
| {} |
| BOOST_XPR_ENSURE_(cur != begin && cur != end && BOOST_XPR_CHAR_(char_type, '>') == *cur |
| , error_badmark, "invalid named back-reference"); |
| |
| string_type name(begin, cur++); |
| for(std::size_t i = 0; i < this->named_marks_.size(); ++i) |
| { |
| if(this->named_marks_[i].name_ == name) |
| { |
| std::size_t sub = this->named_marks_[i].mark_nbr_; |
| return std::copy(this->sub_matches_[ sub ].first, this->sub_matches_[ sub ].second, out); |
| } |
| } |
| |
| BOOST_THROW_EXCEPTION(regex_error(error_badmark, "invalid named back-reference")); |
| // Should never get here |
| return out; |
| } |
| |
| regex_id_type regex_id_; |
| detail::sub_match_vector<BidiIter> sub_matches_; |
| boost::optional<BidiIter> base_; |
| boost::optional<sub_match<BidiIter> > prefix_; |
| boost::optional<sub_match<BidiIter> > suffix_; |
| nested_results_type nested_results_; |
| intrusive_ptr<extras_type> extras_ptr_; |
| intrusive_ptr<detail::traits<char_type> const> traits_; |
| detail::action_args_type args_; |
| std::vector<detail::named_mark<char_type> > named_marks_; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // regex_id_filter_predicate |
| // |
| template<typename BidiIter> |
| struct regex_id_filter_predicate |
| : std::unary_function<match_results<BidiIter>, bool> |
| { |
| regex_id_filter_predicate(regex_id_type regex_id) |
| : regex_id_(regex_id) |
| { |
| } |
| |
| bool operator ()(match_results<BidiIter> const &res) const |
| { |
| return this->regex_id_ == res.regex_id(); |
| } |
| |
| private: |
| |
| regex_id_type regex_id_; |
| }; |
| |
| }} // namespace boost::xpressive |
| |
| #ifdef BOOST_HAS_CONCEPTS |
| // Better living through concepts. :-P |
| namespace std |
| { |
| template<typename Iter_, typename Char_> |
| concept_map OutputIterator< |
| boost::xpressive::detail::case_converting_iterator<Iter_, Char_> |
| , Char_ |
| > |
| {}; |
| |
| template<typename Char_> |
| concept_map OutputIterator< |
| boost::xpressive::detail::noop_output_iterator<Char_> |
| , Char_ |
| > |
| {}; |
| } |
| #endif |
| |
| #endif |