You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
744 lines
27 KiB
744 lines
27 KiB
/////////////////////////////////////////////////////////////////////////////// |
|
/// \file regex_compiler.hpp |
|
/// Contains the definition of regex_compiler, a factory for building regex objects |
|
/// from strings. |
|
// |
|
// 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) |
|
|
|
#ifndef BOOST_XPRESSIVE_REGEX_COMPILER_HPP_EAN_10_04_2005 |
|
#define BOOST_XPRESSIVE_REGEX_COMPILER_HPP_EAN_10_04_2005 |
|
|
|
// MS compatible compilers support #pragma once |
|
#if defined(_MSC_VER) && (_MSC_VER >= 1020) |
|
# pragma once |
|
#endif |
|
|
|
#include <map> |
|
#include <boost/assert.hpp> |
|
#include <boost/next_prior.hpp> |
|
#include <boost/range/begin.hpp> |
|
#include <boost/range/end.hpp> |
|
#include <boost/mpl/assert.hpp> |
|
#include <boost/throw_exception.hpp> |
|
#include <boost/type_traits/is_same.hpp> |
|
#include <boost/type_traits/is_pointer.hpp> |
|
#include <boost/utility/enable_if.hpp> |
|
#include <boost/iterator/iterator_traits.hpp> |
|
#include <boost/xpressive/basic_regex.hpp> |
|
#include <boost/xpressive/detail/dynamic/parser.hpp> |
|
#include <boost/xpressive/detail/dynamic/parse_charset.hpp> |
|
#include <boost/xpressive/detail/dynamic/parser_enum.hpp> |
|
#include <boost/xpressive/detail/dynamic/parser_traits.hpp> |
|
#include <boost/xpressive/detail/core/linker.hpp> |
|
#include <boost/xpressive/detail/core/optimize.hpp> |
|
|
|
namespace boost { namespace xpressive |
|
{ |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// regex_compiler |
|
// |
|
/// \brief Class template regex_compiler is a factory for building basic_regex objects from a string. |
|
/// |
|
/// Class template regex_compiler is used to construct a basic_regex object from a string. The string |
|
/// should contain a valid regular expression. You can imbue a regex_compiler object with a locale, |
|
/// after which all basic_regex objects created with that regex_compiler object will use that locale. |
|
/// After creating a regex_compiler object, and optionally imbueing it with a locale, you can call the |
|
/// compile() method to construct a basic_regex object, passing it the string representing the regular |
|
/// expression. You can call compile() multiple times on the same regex_compiler object. Two basic_regex |
|
/// objects compiled from the same string will have different regex_id's. |
|
template<typename BidiIter, typename RegexTraits, typename CompilerTraits> |
|
struct regex_compiler |
|
{ |
|
typedef BidiIter iterator_type; |
|
typedef typename iterator_value<BidiIter>::type char_type; |
|
typedef regex_constants::syntax_option_type flag_type; |
|
typedef RegexTraits traits_type; |
|
typedef typename traits_type::string_type string_type; |
|
typedef typename traits_type::locale_type locale_type; |
|
typedef typename traits_type::char_class_type char_class_type; |
|
|
|
explicit regex_compiler(RegexTraits const &traits = RegexTraits()) |
|
: mark_count_(0) |
|
, hidden_mark_count_(0) |
|
, traits_(traits) |
|
, upper_(0) |
|
, self_() |
|
, rules_() |
|
{ |
|
this->upper_ = lookup_classname(this->rxtraits(), "upper"); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// imbue |
|
/// Specify the locale to be used by a regex_compiler. |
|
/// |
|
/// \param loc The locale that this regex_compiler should use. |
|
/// \return The previous locale. |
|
locale_type imbue(locale_type loc) |
|
{ |
|
locale_type oldloc = this->traits_.imbue(loc); |
|
this->upper_ = lookup_classname(this->rxtraits(), "upper"); |
|
return oldloc; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// getloc |
|
/// Get the locale used by a regex_compiler. |
|
/// |
|
/// \return The locale used by this regex_compiler. |
|
locale_type getloc() const |
|
{ |
|
return this->traits_.getloc(); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// compile |
|
/// Builds a basic_regex object from a range of characters. |
|
/// |
|
/// \param begin The beginning of a range of characters representing the |
|
/// regular expression to compile. |
|
/// \param end The end of a range of characters representing the |
|
/// regular expression to compile. |
|
/// \param flags Optional bitmask that determines how the pat string is |
|
/// interpreted. (See syntax_option_type.) |
|
/// \return A basic_regex object corresponding to the regular expression |
|
/// represented by the character range. |
|
/// \pre InputIter is a model of the InputIterator concept. |
|
/// \pre [begin,end) is a valid range. |
|
/// \pre The range of characters specified by [begin,end) contains a |
|
/// valid string-based representation of a regular expression. |
|
/// \throw regex_error when the range of characters has invalid regular |
|
/// expression syntax. |
|
template<typename InputIter> |
|
basic_regex<BidiIter> |
|
compile(InputIter begin, InputIter end, flag_type flags = regex_constants::ECMAScript) |
|
{ |
|
typedef typename iterator_category<InputIter>::type category; |
|
return this->compile_(begin, end, flags, category()); |
|
} |
|
|
|
/// \overload |
|
/// |
|
template<typename InputRange> |
|
typename disable_if<is_pointer<InputRange>, basic_regex<BidiIter> >::type |
|
compile(InputRange const &pat, flag_type flags = regex_constants::ECMAScript) |
|
{ |
|
return this->compile(boost::begin(pat), boost::end(pat), flags); |
|
} |
|
|
|
/// \overload |
|
/// |
|
basic_regex<BidiIter> |
|
compile(char_type const *begin, flag_type flags = regex_constants::ECMAScript) |
|
{ |
|
BOOST_ASSERT(0 != begin); |
|
char_type const *end = begin + std::char_traits<char_type>::length(begin); |
|
return this->compile(begin, end, flags); |
|
} |
|
|
|
/// \overload |
|
/// |
|
basic_regex<BidiIter> compile(char_type const *begin, std::size_t size, flag_type flags) |
|
{ |
|
BOOST_ASSERT(0 != begin); |
|
char_type const *end = begin + size; |
|
return this->compile(begin, end, flags); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// operator[] |
|
/// Return a reference to the named regular expression. If no such named |
|
/// regular expression exists, create a new regular expression and return |
|
/// a reference to it. |
|
/// |
|
/// \param name A std::string containing the name of the regular expression. |
|
/// \pre The string is not empty. |
|
/// \throw bad_alloc on allocation failure. |
|
basic_regex<BidiIter> &operator [](string_type const &name) |
|
{ |
|
BOOST_ASSERT(!name.empty()); |
|
return this->rules_[name]; |
|
} |
|
|
|
/// \overload |
|
/// |
|
basic_regex<BidiIter> const &operator [](string_type const &name) const |
|
{ |
|
BOOST_ASSERT(!name.empty()); |
|
return this->rules_[name]; |
|
} |
|
|
|
private: |
|
|
|
typedef detail::escape_value<char_type, char_class_type> escape_value; |
|
typedef detail::alternate_matcher<detail::alternates_vector<BidiIter>, RegexTraits> alternate_matcher; |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// compile_ |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
basic_regex<BidiIter> compile_(FwdIter begin, FwdIter end, flag_type flags, std::forward_iterator_tag) |
|
{ |
|
BOOST_MPL_ASSERT((is_same<char_type, typename iterator_value<FwdIter>::type>)); |
|
using namespace regex_constants; |
|
this->reset(); |
|
this->traits_.flags(flags); |
|
|
|
basic_regex<BidiIter> rextmp, *prex = &rextmp; |
|
FwdIter tmp = begin; |
|
|
|
// Check if this regex is a named rule: |
|
string_type name; |
|
if(token_group_begin == this->traits_.get_token(tmp, end) && |
|
BOOST_XPR_ENSURE_(tmp != end, error_paren, "mismatched parenthesis") && |
|
token_rule_assign == this->traits_.get_group_type(tmp, end, name)) |
|
{ |
|
begin = tmp; |
|
BOOST_XPR_ENSURE_ |
|
( |
|
begin != end && token_group_end == this->traits_.get_token(begin, end) |
|
, error_paren |
|
, "mismatched parenthesis" |
|
); |
|
prex = &this->rules_[name]; |
|
} |
|
|
|
this->self_ = detail::core_access<BidiIter>::get_regex_impl(*prex); |
|
|
|
// at the top level, a regex is a sequence of alternates |
|
detail::sequence<BidiIter> seq = this->parse_alternates(begin, end); |
|
BOOST_XPR_ENSURE_(begin == end, error_paren, "mismatched parenthesis"); |
|
|
|
// terminate the sequence |
|
seq += detail::make_dynamic<BidiIter>(detail::end_matcher()); |
|
|
|
// bundle the regex information into a regex_impl object |
|
detail::common_compile(seq.xpr().matchable(), *this->self_, this->rxtraits()); |
|
|
|
this->self_->traits_ = new detail::traits_holder<RegexTraits>(this->rxtraits()); |
|
this->self_->mark_count_ = this->mark_count_; |
|
this->self_->hidden_mark_count_ = this->hidden_mark_count_; |
|
|
|
// References changed, update dependencies. |
|
this->self_->tracking_update(); |
|
this->self_.reset(); |
|
return *prex; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// compile_ |
|
/// INTERNAL ONLY |
|
template<typename InputIter> |
|
basic_regex<BidiIter> compile_(InputIter begin, InputIter end, flag_type flags, std::input_iterator_tag) |
|
{ |
|
string_type pat(begin, end); |
|
return this->compile_(boost::begin(pat), boost::end(pat), flags, std::forward_iterator_tag()); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// reset |
|
/// INTERNAL ONLY |
|
void reset() |
|
{ |
|
this->mark_count_ = 0; |
|
this->hidden_mark_count_ = 0; |
|
this->traits_.flags(regex_constants::ECMAScript); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// regex_traits |
|
/// INTERNAL ONLY |
|
traits_type &rxtraits() |
|
{ |
|
return this->traits_.traits(); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// regex_traits |
|
/// INTERNAL ONLY |
|
traits_type const &rxtraits() const |
|
{ |
|
return this->traits_.traits(); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// parse_alternates |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
detail::sequence<BidiIter> parse_alternates(FwdIter &begin, FwdIter end) |
|
{ |
|
using namespace regex_constants; |
|
int count = 0; |
|
FwdIter tmp = begin; |
|
detail::sequence<BidiIter> seq; |
|
|
|
do switch(++count) |
|
{ |
|
case 1: |
|
seq = this->parse_sequence(tmp, end); |
|
break; |
|
case 2: |
|
seq = detail::make_dynamic<BidiIter>(alternate_matcher()) | seq; |
|
// fall-through |
|
default: |
|
seq |= this->parse_sequence(tmp, end); |
|
} |
|
while((begin = tmp) != end && token_alternate == this->traits_.get_token(tmp, end)); |
|
|
|
return seq; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// parse_group |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
detail::sequence<BidiIter> parse_group(FwdIter &begin, FwdIter end) |
|
{ |
|
using namespace regex_constants; |
|
int mark_nbr = 0; |
|
bool keeper = false; |
|
bool lookahead = false; |
|
bool lookbehind = false; |
|
bool negative = false; |
|
string_type name; |
|
|
|
detail::sequence<BidiIter> seq, seq_end; |
|
FwdIter tmp = FwdIter(); |
|
|
|
syntax_option_type old_flags = this->traits_.flags(); |
|
|
|
switch(this->traits_.get_group_type(begin, end, name)) |
|
{ |
|
case token_no_mark: |
|
// Don't process empty groups like (?:) or (?i) |
|
// BUGBUG this doesn't handle the degenerate (?:)+ correctly |
|
if(token_group_end == this->traits_.get_token(tmp = begin, end)) |
|
{ |
|
return this->parse_atom(begin = tmp, end); |
|
} |
|
break; |
|
|
|
case token_negative_lookahead: |
|
negative = true; // fall-through |
|
case token_positive_lookahead: |
|
lookahead = true; |
|
break; |
|
|
|
case token_negative_lookbehind: |
|
negative = true; // fall-through |
|
case token_positive_lookbehind: |
|
lookbehind = true; |
|
break; |
|
|
|
case token_independent_sub_expression: |
|
keeper = true; |
|
break; |
|
|
|
case token_comment: |
|
while(BOOST_XPR_ENSURE_(begin != end, error_paren, "mismatched parenthesis")) |
|
{ |
|
switch(this->traits_.get_token(begin, end)) |
|
{ |
|
case token_group_end: return this->parse_atom(begin, end); |
|
case token_escape: BOOST_XPR_ENSURE_(begin != end, error_escape, "incomplete escape sequence"); |
|
case token_literal: ++begin; |
|
default:; |
|
} |
|
} |
|
break; |
|
|
|
case token_recurse: |
|
BOOST_XPR_ENSURE_ |
|
( |
|
begin != end && token_group_end == this->traits_.get_token(begin, end) |
|
, error_paren |
|
, "mismatched parenthesis" |
|
); |
|
return detail::make_dynamic<BidiIter>(detail::regex_byref_matcher<BidiIter>(this->self_)); |
|
|
|
case token_rule_assign: |
|
BOOST_THROW_EXCEPTION( |
|
regex_error(error_badrule, "rule assignments must be at the front of the regex") |
|
); |
|
break; |
|
|
|
case token_rule_ref: |
|
{ |
|
typedef detail::core_access<BidiIter> access; |
|
BOOST_XPR_ENSURE_ |
|
( |
|
begin != end && token_group_end == this->traits_.get_token(begin, end) |
|
, error_paren |
|
, "mismatched parenthesis" |
|
); |
|
basic_regex<BidiIter> &rex = this->rules_[name]; |
|
shared_ptr<detail::regex_impl<BidiIter> > impl = access::get_regex_impl(rex); |
|
this->self_->track_reference(*impl); |
|
return detail::make_dynamic<BidiIter>(detail::regex_byref_matcher<BidiIter>(impl)); |
|
} |
|
|
|
case token_named_mark: |
|
mark_nbr = static_cast<int>(++this->mark_count_); |
|
for(std::size_t i = 0; i < this->self_->named_marks_.size(); ++i) |
|
{ |
|
BOOST_XPR_ENSURE_(this->self_->named_marks_[i].name_ != name, error_badmark, "named mark already exists"); |
|
} |
|
this->self_->named_marks_.push_back(detail::named_mark<char_type>(name, this->mark_count_)); |
|
seq = detail::make_dynamic<BidiIter>(detail::mark_begin_matcher(mark_nbr)); |
|
seq_end = detail::make_dynamic<BidiIter>(detail::mark_end_matcher(mark_nbr)); |
|
break; |
|
|
|
case token_named_mark_ref: |
|
BOOST_XPR_ENSURE_ |
|
( |
|
begin != end && token_group_end == this->traits_.get_token(begin, end) |
|
, error_paren |
|
, "mismatched parenthesis" |
|
); |
|
for(std::size_t i = 0; i < this->self_->named_marks_.size(); ++i) |
|
{ |
|
if(this->self_->named_marks_[i].name_ == name) |
|
{ |
|
mark_nbr = static_cast<int>(this->self_->named_marks_[i].mark_nbr_); |
|
return detail::make_backref_xpression<BidiIter> |
|
( |
|
mark_nbr, this->traits_.flags(), this->rxtraits() |
|
); |
|
} |
|
} |
|
BOOST_THROW_EXCEPTION(regex_error(error_badmark, "invalid named back-reference")); |
|
break; |
|
|
|
default: |
|
mark_nbr = static_cast<int>(++this->mark_count_); |
|
seq = detail::make_dynamic<BidiIter>(detail::mark_begin_matcher(mark_nbr)); |
|
seq_end = detail::make_dynamic<BidiIter>(detail::mark_end_matcher(mark_nbr)); |
|
break; |
|
} |
|
|
|
// alternates |
|
seq += this->parse_alternates(begin, end); |
|
seq += seq_end; |
|
BOOST_XPR_ENSURE_ |
|
( |
|
begin != end && token_group_end == this->traits_.get_token(begin, end) |
|
, error_paren |
|
, "mismatched parenthesis" |
|
); |
|
|
|
typedef detail::shared_matchable<BidiIter> xpr_type; |
|
if(lookahead) |
|
{ |
|
seq += detail::make_independent_end_xpression<BidiIter>(seq.pure()); |
|
detail::lookahead_matcher<xpr_type> lookahead(seq.xpr(), negative, seq.pure()); |
|
seq = detail::make_dynamic<BidiIter>(lookahead); |
|
} |
|
else if(lookbehind) |
|
{ |
|
seq += detail::make_independent_end_xpression<BidiIter>(seq.pure()); |
|
detail::lookbehind_matcher<xpr_type> lookbehind(seq.xpr(), seq.width().value(), negative, seq.pure()); |
|
seq = detail::make_dynamic<BidiIter>(lookbehind); |
|
} |
|
else if(keeper) // independent sub-expression |
|
{ |
|
seq += detail::make_independent_end_xpression<BidiIter>(seq.pure()); |
|
detail::keeper_matcher<xpr_type> keeper(seq.xpr(), seq.pure()); |
|
seq = detail::make_dynamic<BidiIter>(keeper); |
|
} |
|
|
|
// restore the modifiers |
|
this->traits_.flags(old_flags); |
|
return seq; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// parse_charset |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
detail::sequence<BidiIter> parse_charset(FwdIter &begin, FwdIter end) |
|
{ |
|
detail::compound_charset<traits_type> chset; |
|
|
|
// call out to a helper to actually parse the character set |
|
detail::parse_charset(begin, end, chset, this->traits_); |
|
|
|
return detail::make_charset_xpression<BidiIter> |
|
( |
|
chset |
|
, this->rxtraits() |
|
, this->traits_.flags() |
|
); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// parse_atom |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
detail::sequence<BidiIter> parse_atom(FwdIter &begin, FwdIter end) |
|
{ |
|
using namespace regex_constants; |
|
escape_value esc = { 0, 0, 0, detail::escape_char }; |
|
FwdIter old_begin = begin; |
|
|
|
switch(this->traits_.get_token(begin, end)) |
|
{ |
|
case token_literal: |
|
return detail::make_literal_xpression<BidiIter> |
|
( |
|
this->parse_literal(begin, end), this->traits_.flags(), this->rxtraits() |
|
); |
|
|
|
case token_any: |
|
return detail::make_any_xpression<BidiIter>(this->traits_.flags(), this->rxtraits()); |
|
|
|
case token_assert_begin_sequence: |
|
return detail::make_dynamic<BidiIter>(detail::assert_bos_matcher()); |
|
|
|
case token_assert_end_sequence: |
|
return detail::make_dynamic<BidiIter>(detail::assert_eos_matcher()); |
|
|
|
case token_assert_begin_line: |
|
return detail::make_assert_begin_line<BidiIter>(this->traits_.flags(), this->rxtraits()); |
|
|
|
case token_assert_end_line: |
|
return detail::make_assert_end_line<BidiIter>(this->traits_.flags(), this->rxtraits()); |
|
|
|
case token_assert_word_boundary: |
|
return detail::make_assert_word<BidiIter>(detail::word_boundary<mpl::true_>(), this->rxtraits()); |
|
|
|
case token_assert_not_word_boundary: |
|
return detail::make_assert_word<BidiIter>(detail::word_boundary<mpl::false_>(), this->rxtraits()); |
|
|
|
case token_assert_word_begin: |
|
return detail::make_assert_word<BidiIter>(detail::word_begin(), this->rxtraits()); |
|
|
|
case token_assert_word_end: |
|
return detail::make_assert_word<BidiIter>(detail::word_end(), this->rxtraits()); |
|
|
|
case token_escape: |
|
esc = this->parse_escape(begin, end); |
|
switch(esc.type_) |
|
{ |
|
case detail::escape_mark: |
|
return detail::make_backref_xpression<BidiIter> |
|
( |
|
esc.mark_nbr_, this->traits_.flags(), this->rxtraits() |
|
); |
|
case detail::escape_char: |
|
return detail::make_char_xpression<BidiIter> |
|
( |
|
esc.ch_, this->traits_.flags(), this->rxtraits() |
|
); |
|
case detail::escape_class: |
|
return detail::make_posix_charset_xpression<BidiIter> |
|
( |
|
esc.class_ |
|
, this->is_upper_(*begin++) |
|
, this->traits_.flags() |
|
, this->rxtraits() |
|
); |
|
} |
|
|
|
case token_group_begin: |
|
return this->parse_group(begin, end); |
|
|
|
case token_charset_begin: |
|
return this->parse_charset(begin, end); |
|
|
|
case token_invalid_quantifier: |
|
BOOST_THROW_EXCEPTION(regex_error(error_badrepeat, "quantifier not expected")); |
|
break; |
|
|
|
case token_quote_meta_begin: |
|
return detail::make_literal_xpression<BidiIter> |
|
( |
|
this->parse_quote_meta(begin, end), this->traits_.flags(), this->rxtraits() |
|
); |
|
|
|
case token_quote_meta_end: |
|
BOOST_THROW_EXCEPTION( |
|
regex_error( |
|
error_escape |
|
, "found quote-meta end without corresponding quote-meta begin" |
|
) |
|
); |
|
break; |
|
|
|
case token_end_of_pattern: |
|
break; |
|
|
|
default: |
|
begin = old_begin; |
|
break; |
|
} |
|
|
|
return detail::sequence<BidiIter>(); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// parse_quant |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
detail::sequence<BidiIter> parse_quant(FwdIter &begin, FwdIter end) |
|
{ |
|
BOOST_ASSERT(begin != end); |
|
detail::quant_spec spec = { 0, 0, false, &this->hidden_mark_count_ }; |
|
detail::sequence<BidiIter> seq = this->parse_atom(begin, end); |
|
|
|
// BUGBUG this doesn't handle the degenerate (?:)+ correctly |
|
if(!seq.empty() && begin != end && detail::quant_none != seq.quant()) |
|
{ |
|
if(this->traits_.get_quant_spec(begin, end, spec)) |
|
{ |
|
BOOST_ASSERT(spec.min_ <= spec.max_); |
|
|
|
if(0 == spec.max_) // quant {0,0} is degenerate -- matches nothing. |
|
{ |
|
seq = this->parse_quant(begin, end); |
|
} |
|
else |
|
{ |
|
seq.repeat(spec); |
|
} |
|
} |
|
} |
|
|
|
return seq; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// parse_sequence |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
detail::sequence<BidiIter> parse_sequence(FwdIter &begin, FwdIter end) |
|
{ |
|
detail::sequence<BidiIter> seq; |
|
|
|
while(begin != end) |
|
{ |
|
detail::sequence<BidiIter> seq_quant = this->parse_quant(begin, end); |
|
|
|
// did we find a quantified atom? |
|
if(seq_quant.empty()) |
|
break; |
|
|
|
// chain it to the end of the xpression sequence |
|
seq += seq_quant; |
|
} |
|
|
|
return seq; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// parse_literal |
|
// scan ahead looking for char literals to be globbed together into a string literal |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
string_type parse_literal(FwdIter &begin, FwdIter end) |
|
{ |
|
using namespace regex_constants; |
|
BOOST_ASSERT(begin != end); |
|
BOOST_ASSERT(token_literal == this->traits_.get_token(begin, end)); |
|
escape_value esc = { 0, 0, 0, detail::escape_char }; |
|
string_type literal(1, *begin); |
|
|
|
for(FwdIter prev = begin, tmp = ++begin; begin != end; prev = begin, begin = tmp) |
|
{ |
|
detail::quant_spec spec = { 0, 0, false, &this->hidden_mark_count_ }; |
|
if(this->traits_.get_quant_spec(tmp, end, spec)) |
|
{ |
|
if(literal.size() != 1) |
|
{ |
|
begin = prev; |
|
literal.erase(boost::prior(literal.end())); |
|
} |
|
return literal; |
|
} |
|
else switch(this->traits_.get_token(tmp, end)) |
|
{ |
|
case token_escape: |
|
esc = this->parse_escape(tmp, end); |
|
if(detail::escape_char != esc.type_) return literal; |
|
literal.insert(literal.end(), esc.ch_); |
|
break; |
|
case token_literal: |
|
literal.insert(literal.end(), *tmp++); |
|
break; |
|
default: |
|
return literal; |
|
} |
|
} |
|
|
|
return literal; |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
// parse_quote_meta |
|
// scan ahead looking for char literals to be globbed together into a string literal |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
string_type parse_quote_meta(FwdIter &begin, FwdIter end) |
|
{ |
|
using namespace regex_constants; |
|
FwdIter old_begin = begin, old_end; |
|
while(end != (old_end = begin)) |
|
{ |
|
switch(this->traits_.get_token(begin, end)) |
|
{ |
|
case token_quote_meta_end: return string_type(old_begin, old_end); |
|
case token_escape: BOOST_XPR_ENSURE_(begin != end, error_escape, "incomplete escape sequence"); |
|
case token_invalid_quantifier: |
|
case token_literal: ++begin; |
|
default:; |
|
} |
|
} |
|
return string_type(old_begin, begin); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////// |
|
// parse_escape |
|
/// INTERNAL ONLY |
|
template<typename FwdIter> |
|
escape_value parse_escape(FwdIter &begin, FwdIter end) |
|
{ |
|
BOOST_XPR_ENSURE_(begin != end, regex_constants::error_escape, "incomplete escape sequence"); |
|
|
|
// first, check to see if this can be a backreference |
|
if(0 < this->rxtraits().value(*begin, 10)) |
|
{ |
|
// Parse at most 3 decimal digits. |
|
FwdIter tmp = begin; |
|
int mark_nbr = detail::toi(tmp, end, this->rxtraits(), 10, 999); |
|
|
|
// If the resulting number could conceivably be a backref, then it is. |
|
if(10 > mark_nbr || mark_nbr <= static_cast<int>(this->mark_count_)) |
|
{ |
|
begin = tmp; |
|
escape_value esc = {0, mark_nbr, 0, detail::escape_mark}; |
|
return esc; |
|
} |
|
} |
|
|
|
// Not a backreference, defer to the parse_escape helper |
|
return detail::parse_escape(begin, end, this->traits_); |
|
} |
|
|
|
bool is_upper_(char_type ch) const |
|
{ |
|
return 0 != this->upper_ && this->rxtraits().isctype(ch, this->upper_); |
|
} |
|
|
|
std::size_t mark_count_; |
|
std::size_t hidden_mark_count_; |
|
CompilerTraits traits_; |
|
typename RegexTraits::char_class_type upper_; |
|
shared_ptr<detail::regex_impl<BidiIter> > self_; |
|
std::map<string_type, basic_regex<BidiIter> > rules_; |
|
}; |
|
|
|
}} // namespace boost::xpressive |
|
|
|
#endif
|
|
|