Merge fixes from Trunk.

Fixes #4721.

[SVN r66116]
This commit is contained in:
John Maddock
2010-10-20 12:11:18 +00:00
parent cfedb49110
commit 7170423870
13 changed files with 177 additions and 119 deletions

View File

@ -102,11 +102,13 @@ SOURCES =
lib boost_regex : ../src/$(SOURCES) icu_options lib boost_regex : ../src/$(SOURCES) icu_options
: :
<link>shared:<define>BOOST_REGEX_DYN_LINK=1 <link>shared:<define>BOOST_REGEX_DYN_LINK=1
<toolset>gcc-mw:<link>static #<toolset>gcc-mw:<link>static
<toolset>gcc-mingw:<link>static #<toolset>gcc-mingw:<link>static
<toolset>gcc-cygwin:<link>static <toolset>gcc-cygwin:<link>static
; ;
boost-install boost_regex ; boost-install boost_regex ;

View File

@ -14,8 +14,8 @@ project
<toolset>gcc:<warnings>all <toolset>gcc:<warnings>all
<toolset>gcc:<cxxflags>-Wextra <toolset>gcc:<cxxflags>-Wextra
<define>U_USING_ICU_NAMESPACE=0 <define>U_USING_ICU_NAMESPACE=0
<toolset>gcc-mw:<link>static #<toolset>gcc-mw:<link>static
<toolset>gcc-mingw:<link>static #<toolset>gcc-mingw:<link>static
<toolset>gcc-cygwin:<link>static <toolset>gcc-cygwin:<link>static
; ;
@ -78,3 +78,4 @@ test-suite regex-examples :

View File

@ -53,7 +53,7 @@ void bubble_down_one(I first, I last)
if(first != last) if(first != last)
{ {
I next = last - 1; I next = last - 1;
while((next != first) && !(*(next-1) < *next)) while((next != first) && (*next < *(next-1)))
{ {
(next-1)->swap(*next); (next-1)->swap(*next);
--next; --next;
@ -61,70 +61,59 @@ void bubble_down_one(I first, I last)
} }
} }
//
// Class named_subexpressions
// Contains information about named subexpressions within the regex.
//
template <class charT>
class named_subexpressions_base
{
public:
virtual int get_id(const charT* i, const charT* j)const = 0;
virtual int get_id(std::size_t h)const = 0;
#ifdef __GNUC__
// warning supression:
virtual ~named_subexpressions_base(){}
#endif
};
template <class Iterator> template <class Iterator>
inline std::size_t hash_value_from_capture_name(Iterator i, Iterator j) inline int hash_value_from_capture_name(Iterator i, Iterator j)
{ {
std::size_t r = boost::hash_range(i, j); std::size_t r = boost::hash_range(i, j);
r %= ((std::numeric_limits<int>::max)() - 10001); r %= ((std::numeric_limits<int>::max)() - 10001);
r += 10000; r += 10000;
return r; return static_cast<int>(r);
} }
template <class charT> class named_subexpressions
class named_subexpressions : public named_subexpressions_base<charT>
{ {
public:
struct name struct name
{ {
template <class charT>
name(const charT* i, const charT* j, int idx) name(const charT* i, const charT* j, int idx)
: /*n(i, j), */ index(idx) : index(idx)
{ {
hash = hash_value_from_capture_name(i, j); hash = hash_value_from_capture_name(i, j);
} }
name(std::size_t h, int idx) name(int h, int idx)
: index(idx), hash(h) : index(idx), hash(h)
{ {
} }
//std::vector<charT> n;
int index; int index;
std::size_t hash; int hash;
bool operator < (const name& other)const bool operator < (const name& other)const
{ {
return hash < other.hash; //std::lexicographical_compare(n.begin(), n.end(), other.n.begin(), other.n.end()); return hash < other.hash;
} }
bool operator == (const name& other)const bool operator == (const name& other)const
{ {
return hash == other.hash; //n == other.n; return hash == other.hash;
} }
void swap(name& other) void swap(name& other)
{ {
//n.swap(other.n);
std::swap(index, other.index); std::swap(index, other.index);
std::swap(hash, other.hash); std::swap(hash, other.hash);
} }
}; };
public:
typedef std::vector<name>::const_iterator const_iterator;
typedef std::pair<const_iterator, const_iterator> range_type;
named_subexpressions(){} named_subexpressions(){}
template <class charT>
void set_name(const charT* i, const charT* j, int index) void set_name(const charT* i, const charT* j, int index)
{ {
m_sub_names.push_back(name(i, j, index)); m_sub_names.push_back(name(i, j, index));
bubble_down_one(m_sub_names.begin(), m_sub_names.end()); bubble_down_one(m_sub_names.begin(), m_sub_names.end());
} }
template <class charT>
int get_id(const charT* i, const charT* j)const int get_id(const charT* i, const charT* j)const
{ {
name t(i, j, 0); name t(i, j, 0);
@ -135,72 +124,37 @@ public:
} }
return -1; return -1;
} }
int get_id(std::size_t h)const template <class charT>
range_type equal_range(const charT* i, const charT* j)const
{
name t(i, j, 0);
return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t);
}
int get_id(int h)const
{ {
name t(h, 0); name t(h, 0);
typename std::vector<name>::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t); std::vector<name>::const_iterator pos = std::lower_bound(m_sub_names.begin(), m_sub_names.end(), t);
if((pos != m_sub_names.end()) && (*pos == t)) if((pos != m_sub_names.end()) && (*pos == t))
{ {
return pos->index; return pos->index;
} }
return -1; return -1;
} }
range_type equal_range(int h)const
{
name t(h, 0);
return std::equal_range(m_sub_names.begin(), m_sub_names.end(), t);
}
private: private:
std::vector<name> m_sub_names; std::vector<name> m_sub_names;
}; };
template <class charT, class Other>
class named_subexpressions_converter : public named_subexpressions_base<charT>
{
boost::shared_ptr<named_subexpressions<Other> > m_converter;
public:
named_subexpressions_converter(boost::shared_ptr<named_subexpressions<Other> > s)
: m_converter(s) {}
int get_id(const charT* i, const charT* j)const
{
if(i == j)
return -1;
std::vector<Other> v;
while(i != j)
{
v.push_back(*i);
++i;
}
return m_converter->get_id(&v[0], &v[0] + v.size());
}
int get_id(std::size_t h)const
{
return m_converter->get_id(h);
}
};
template <class To>
inline boost::shared_ptr<named_subexpressions_base<To> > convert_to_named_subs_imp(
boost::shared_ptr<named_subexpressions<To> > s,
boost::integral_constant<bool,true> const&)
{
return s;
}
template <class To, class From>
inline boost::shared_ptr<named_subexpressions_base<To> > convert_to_named_subs_imp(
boost::shared_ptr<named_subexpressions<From> > s,
boost::integral_constant<bool,false> const&)
{
return boost::shared_ptr<named_subexpressions_converter<To, From> >(new named_subexpressions_converter<To, From>(s));
}
template <class To, class From>
inline boost::shared_ptr<named_subexpressions_base<To> > convert_to_named_subs(
boost::shared_ptr<named_subexpressions<From> > s)
{
typedef typename boost::is_same<To, From>::type tag_type;
return convert_to_named_subs_imp<To>(s, tag_type());
}
// //
// class regex_data: // class regex_data:
// represents the data we wish to expose to the matching algorithms. // represents the data we wish to expose to the matching algorithms.
// //
template <class charT, class traits> template <class charT, class traits>
struct regex_data : public named_subexpressions<charT> struct regex_data : public named_subexpressions
{ {
typedef regex_constants::syntax_option_type flag_type; typedef regex_constants::syntax_option_type flag_type;
typedef std::size_t size_type; typedef std::size_t size_type;
@ -672,7 +626,7 @@ public:
BOOST_ASSERT(0 != m_pimpl.get()); BOOST_ASSERT(0 != m_pimpl.get());
return m_pimpl->get_data(); return m_pimpl->get_data();
} }
boost::shared_ptr<re_detail::named_subexpressions<charT> > get_named_subs()const boost::shared_ptr<re_detail::named_subexpressions > get_named_subs()const
{ {
return m_pimpl; return m_pimpl;
} }

View File

@ -806,7 +806,13 @@ void basic_regex_creator<charT, traits>::fixup_recursions(re_syntax_base* state)
re_syntax_base* p = base; re_syntax_base* p = base;
std::ptrdiff_t idx = static_cast<re_jump*>(state)->alt.i; std::ptrdiff_t idx = static_cast<re_jump*>(state)->alt.i;
if(idx > 10000) if(idx > 10000)
idx = m_pdata->get_id(idx); {
//
// There may be more than one capture group with this hash, just do what Perl
// does and recurse to the leftmost:
//
idx = m_pdata->get_id(static_cast<int>(idx));
}
while(p) while(p)
{ {
if((p->type == syntax_element_startmark) && (static_cast<re_brace*>(p)->index == idx)) if((p->type == syntax_element_startmark) && (static_cast<re_brace*>(p)->index == idx))

View File

@ -820,7 +820,11 @@ escape_type_class_jump:
return false; return false;
} }
// maybe have \g{ddd} // maybe have \g{ddd}
if(this->m_traits.syntax_type(*m_position) == regex_constants::syntax_open_brace) regex_constants::syntax_type syn = this->m_traits.syntax_type(*m_position);
regex_constants::syntax_type syn_end = 0;
if((syn == regex_constants::syntax_open_brace)
|| (syn == regex_constants::escape_type_left_word)
|| (syn == regex_constants::escape_type_end_buffer))
{ {
if(++m_position == m_end) if(++m_position == m_end)
{ {
@ -828,6 +832,18 @@ escape_type_class_jump:
return false; return false;
} }
have_brace = true; have_brace = true;
switch(syn)
{
case regex_constants::syntax_open_brace:
syn_end = regex_constants::syntax_close_brace;
break;
case regex_constants::escape_type_left_word:
syn_end = regex_constants::escape_type_right_word;
break;
default:
syn_end = regex_constants::escape_type_end_buffer;
break;
}
} }
negative = (*m_position == static_cast<charT>('-')); negative = (*m_position == static_cast<charT>('-'));
if((negative) && (++m_position == m_end)) if((negative) && (++m_position == m_end))
@ -837,18 +853,20 @@ escape_type_class_jump:
} }
const charT* pc = m_position; const charT* pc = m_position;
int i = this->m_traits.toi(pc, m_end, 10); int i = this->m_traits.toi(pc, m_end, 10);
if(i < 0) if((i < 0) && syn_end)
{ {
// Check for a named capture: // Check for a named capture, get the leftmost one if there is more than one:
const charT* base = m_position; const charT* base = m_position;
while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace)) while((m_position != m_end) && (this->m_traits.syntax_type(*m_position) != syn_end))
{
++m_position; ++m_position;
i = this->m_pdata->get_id(base, m_position); }
i = hash_value_from_capture_name(base, m_position);
pc = m_position; pc = m_position;
} }
if(negative) if(negative)
i = 1 + m_mark_count - i; i = 1 + m_mark_count - i;
if((i > 0) && (this->m_backrefs & (1u << (i-1)))) if(((i > 0) && (this->m_backrefs & (1u << (i-1)))) || ((i > 10000) && (this->m_pdata->get_id(i) > 0) && (this->m_backrefs & (1u << (this->m_pdata->get_id(i)-1)))))
{ {
m_position = pc; m_position = pc;
re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_backref, sizeof(re_brace))); re_brace* pb = static_cast<re_brace*>(this->append_state(syntax_element_backref, sizeof(re_brace)));
@ -863,7 +881,7 @@ escape_type_class_jump:
m_position = pc; m_position = pc;
if(have_brace) if(have_brace)
{ {
if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != regex_constants::syntax_close_brace)) if((m_position == m_end) || (this->m_traits.syntax_type(*m_position) != syn_end))
{ {
fail(regex_constants::error_escape, m_position - m_base, incomplete_message); fail(regex_constants::error_escape, m_position - m_base, incomplete_message);
return false; return false;

View File

@ -38,7 +38,6 @@ namespace boost{
namespace re_detail{ namespace re_detail{
template <class charT>
class named_subexpressions; class named_subexpressions;
} }
@ -69,7 +68,7 @@ public:
typedef typename re_detail::regex_iterator_traits< typedef typename re_detail::regex_iterator_traits<
BidiIterator>::value_type char_type; BidiIterator>::value_type char_type;
typedef std::basic_string<char_type> string_type; typedef std::basic_string<char_type> string_type;
typedef re_detail::named_subexpressions_base<char_type> named_sub_type; typedef re_detail::named_subexpressions named_sub_type;
// construct/copy/destroy: // construct/copy/destroy:
explicit match_results(const Allocator& a = Allocator()) explicit match_results(const Allocator& a = Allocator())
@ -225,10 +224,15 @@ public:
// //
const_reference named_subexpression(const char_type* i, const char_type* j) const const_reference named_subexpression(const char_type* i, const char_type* j) const
{ {
//
// Scan for the leftmost *matched* subexpression with the specified named:
//
if(m_is_singular) if(m_is_singular)
raise_logic_error(); raise_logic_error();
int index = m_named_subs->get_id(i, j); re_detail::named_subexpressions::range_type r = m_named_subs->equal_range(i, j);
return index > 0 ? (*this)[index] : m_null; while((r.first != r.second) && ((*this)[r.first->index].matched == false))
++r.first;
return r.first != r.second ? (*this)[r.first->index] : m_null;
} }
template <class charT> template <class charT>
const_reference named_subexpression(const charT* i, const charT* j) const const_reference named_subexpression(const charT* i, const charT* j) const
@ -243,10 +247,20 @@ public:
} }
int named_subexpression_index(const char_type* i, const char_type* j) const int named_subexpression_index(const char_type* i, const char_type* j) const
{ {
//
// Scan for the leftmost *matched* subexpression with the specified named.
// If none found then return the leftmost expression with that name,
// otherwise an invalid index:
//
if(m_is_singular) if(m_is_singular)
raise_logic_error(); raise_logic_error();
int index = m_named_subs->get_id(i, j); re_detail::named_subexpressions::range_type s, r;
return index > 0 ? index : -20; s = r = m_named_subs->equal_range(i, j);
while((r.first != r.second) && ((*this)[r.first->index].matched == false))
++r.first;
if(r.first == r.second)
r = s;
return r.first != r.second ? r.first->index : -20;
} }
template <class charT> template <class charT>
int named_subexpression_index(const charT* i, const charT* j) const int named_subexpression_index(const charT* i, const charT* j) const

View File

@ -200,7 +200,7 @@ bool perl_matcher<BidiIterator, Allocator, traits>::match_imp()
m_match_flags |= regex_constants::match_all; m_match_flags |= regex_constants::match_all;
m_presult->set_size((m_match_flags & match_nosubs) ? 1 : re.mark_count(), search_base, last); m_presult->set_size((m_match_flags & match_nosubs) ? 1 : re.mark_count(), search_base, last);
m_presult->set_base(base); m_presult->set_base(base);
m_presult->set_named_subs(re_detail::convert_to_named_subs<typename match_results<BidiIterator>::char_type>(this->re.get_named_subs())); m_presult->set_named_subs(this->re.get_named_subs());
if(m_match_flags & match_posix) if(m_match_flags & match_posix)
m_result = *m_presult; m_result = *m_presult;
verify_options(re.flags(), m_match_flags); verify_options(re.flags(), m_match_flags);
@ -262,7 +262,7 @@ bool perl_matcher<BidiIterator, Allocator, traits>::find_imp()
pstate = re.get_first_state(); pstate = re.get_first_state();
m_presult->set_size((m_match_flags & match_nosubs) ? 1 : re.mark_count(), base, last); m_presult->set_size((m_match_flags & match_nosubs) ? 1 : re.mark_count(), base, last);
m_presult->set_base(base); m_presult->set_base(base);
m_presult->set_named_subs(re_detail::convert_to_named_subs<typename match_results<BidiIterator>::char_type>(this->re.get_named_subs())); m_presult->set_named_subs(this->re.get_named_subs());
m_match_flags |= regex_constants::match_init; m_match_flags |= regex_constants::match_init;
} }
else else
@ -588,8 +588,23 @@ bool perl_matcher<BidiIterator, Allocator, traits>::match_backref()
// in the match, this is in line with ECMAScript, but not Perl // in the match, this is in line with ECMAScript, but not Perl
// or PCRE. // or PCRE.
// //
BidiIterator i = (*m_presult)[static_cast<const re_brace*>(pstate)->index].first; int index = static_cast<const re_brace*>(pstate)->index;
BidiIterator j = (*m_presult)[static_cast<const re_brace*>(pstate)->index].second; if(index >= 10000)
{
named_subexpressions::range_type r = re.get_data().equal_range(index);
BOOST_ASSERT(r.first != r.second);
do
{
index = r.first->index;
++r.first;
}while((r.first != r.second) && ((*m_presult)[index].matched != true));
}
if((m_match_flags & match_perl) && !(*m_presult)[index].matched)
return false;
BidiIterator i = (*m_presult)[index].first;
BidiIterator j = (*m_presult)[index].second;
while(i != j) while(i != j)
{ {
if((position == last) || (traits_inst.translate(*position, icase) != traits_inst.translate(*i, icase))) if((position == last) || (traits_inst.translate(*position, icase) != traits_inst.translate(*i, icase)))
@ -713,7 +728,7 @@ inline bool perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref(
{ {
// return true if marked sub-expression N has been matched: // return true if marked sub-expression N has been matched:
int index = static_cast<const re_brace*>(pstate)->index; int index = static_cast<const re_brace*>(pstate)->index;
bool result; bool result = false;
if(index == 9999) if(index == 9999)
{ {
// Magic value for a (DEFINE) block: // Magic value for a (DEFINE) block:
@ -721,11 +736,25 @@ inline bool perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref(
} }
else if(index > 0) else if(index > 0)
{ {
// Have we matched subexpression "index"?
// Check if index is a hash value: // Check if index is a hash value:
if(index >= 10000) if(index >= 10000)
index = re.get_data().get_id(index); {
// Have we matched subexpression "index"? named_subexpressions::range_type r = re.get_data().equal_range(index);
result = (*m_presult)[index].matched; while(r.first != r.second)
{
if((*m_presult)[r.first->index].matched)
{
result = true;
break;
}
++r.first;
}
}
else
{
result = (*m_presult)[index].matched;
}
pstate = pstate->next.p; pstate = pstate->next.p;
} }
else else
@ -734,8 +763,20 @@ inline bool perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref(
// If index == 0 then check for any recursion at all, otherwise for recursion to -index-1. // If index == 0 then check for any recursion at all, otherwise for recursion to -index-1.
int idx = -index-1; int idx = -index-1;
if(idx >= 10000) if(idx >= 10000)
idx = re.get_data().get_id(idx); {
result = !recursion_stack.empty() && ((recursion_stack.back().idx == idx) || (index == 0)); named_subexpressions::range_type r = re.get_data().equal_range(idx);
int stack_index = recursion_stack.empty() ? -1 : recursion_stack.back().idx;
while(r.first != r.second)
{
result |= (stack_index == r.first->index);
if(result)break;
++r.first;
}
}
else
{
result = !recursion_stack.empty() && ((recursion_stack.back().idx == idx) || (index == 0));
}
pstate = pstate->next.p; pstate = pstate->next.p;
} }
return result; return result;

View File

@ -56,7 +56,7 @@ struct sub_match : public std::pair<BidiIterator, BidiIterator>
template <class T, class A> template <class T, class A>
operator std::basic_string<value_type, T, A> ()const operator std::basic_string<value_type, T, A> ()const
{ {
return std::basic_string<value_type, T, A>(this->first, this->second); return matched ? std::basic_string<value_type, T, A>(this->first, this->second) : std::basic_string<value_type, T, A>();
} }
#else #else
operator std::basic_string<value_type> ()const operator std::basic_string<value_type> ()const
@ -66,19 +66,22 @@ struct sub_match : public std::pair<BidiIterator, BidiIterator>
#endif #endif
difference_type BOOST_REGEX_CALL length()const difference_type BOOST_REGEX_CALL length()const
{ {
difference_type n = ::boost::re_detail::distance((BidiIterator)this->first, (BidiIterator)this->second); difference_type n = matched ? ::boost::re_detail::distance((BidiIterator)this->first, (BidiIterator)this->second) : 0;
return n; return n;
} }
std::basic_string<value_type> str()const std::basic_string<value_type> str()const
{ {
std::basic_string<value_type> result; std::basic_string<value_type> result;
std::size_t len = ::boost::re_detail::distance((BidiIterator)this->first, (BidiIterator)this->second); if(matched)
result.reserve(len);
BidiIterator i = this->first;
while(i != this->second)
{ {
result.append(1, *i); std::size_t len = ::boost::re_detail::distance((BidiIterator)this->first, (BidiIterator)this->second);
++i; result.reserve(len);
BidiIterator i = this->first;
while(i != this->second)
{
result.append(1, *i);
++i;
}
} }
return result; return result;
} }

View File

@ -100,7 +100,7 @@ BOOST_REGEX_DECL const char* BOOST_REGEX_CALL get_default_syntax(regex_constants
"p", "p",
"P", "P",
"N", "N",
"g", "gk",
"K", "K",
"R", "R",
}; };

View File

@ -17,8 +17,8 @@ project
<toolset>gcc:<cxxflags>-Wextra <toolset>gcc:<cxxflags>-Wextra
<toolset>gcc:<cxxflags>-Wshadow <toolset>gcc:<cxxflags>-Wshadow
<define>U_USING_ICU_NAMESPACE=0 <define>U_USING_ICU_NAMESPACE=0
<toolset>gcc-mw:<link>static #<toolset>gcc-mw:<link>static
<toolset>gcc-mingw:<link>static #<toolset>gcc-mingw:<link>static
<toolset>gcc-cygwin:<link>static <toolset>gcc-cygwin:<link>static
; ;
@ -177,3 +177,5 @@ test-suite regex
; ;
build-project ../example ; build-project ../example ;

View File

@ -907,5 +907,16 @@ void test_recursion()
// Bugs: // Bugs:
TEST_REGEX_SEARCH("namespace\\s+(\\w+)\\s+(\\{(?:[^{}]*(?:(?2)[^{}]*)*)?\\})", perl, "namespace one { namespace two { int foo(); } }", match_default, make_array(0, 46, 10, 13, 14, 46, -2, -2)); TEST_REGEX_SEARCH("namespace\\s+(\\w+)\\s+(\\{(?:[^{}]*(?:(?2)[^{}]*)*)?\\})", perl, "namespace one { namespace two { int foo(); } }", match_default, make_array(0, 46, 10, 13, 14, 46, -2, -2));
TEST_REGEX_SEARCH("namespace\\s+(\\w+)\\s+(\\{(?:[^{}]*(?:(?2)[^{}]*)*)?\\})", perl, "namespace one { namespace two { int foo(){} } { {{{ } } } } {}}", match_default, make_array(0, 64, 10, 13, 14, 64, -2, -2)); TEST_REGEX_SEARCH("namespace\\s+(\\w+)\\s+(\\{(?:[^{}]*(?:(?2)[^{}]*)*)?\\})", perl, "namespace one { namespace two { int foo(){} } { {{{ } } } } {}}", match_default, make_array(0, 64, 10, 13, 14, 64, -2, -2));
// Recursion to a named sub with a name that is used multiple times:
TEST_REGEX_SEARCH("(?:(?<A>a+)|(?<A>b+))\\.(?&A)", perl, "aaaa.aa", match_default, make_array(0, 7, 0, 4, -1, -1, -2, -2));
TEST_REGEX_SEARCH("(?:(?<A>a+)|(?<A>b+))\\.(?&A)", perl, "bbbb.aa", match_default, make_array(0, 7, -1, -1, 0, 4, -2, -2));
TEST_REGEX_SEARCH("(?:(?<A>a+)|(?<A>b+))\\.(?&A)", perl, "bbbb.bb", match_default, make_array(-2, -2));
// Back reference to a named sub with a name that is used multiple times:
TEST_REGEX_SEARCH("(?:(?<A>a+)|(?<A>b+))\\.\\k<A>", perl, "aaaa.aaaa", match_default, make_array(0, 9, 0, 4, -1, -1, -2, -2));
TEST_REGEX_SEARCH("(?:(?<A>a+)|(?<A>b+))\\.\\k<A>", perl, "bbbb.aaaa", match_default, make_array(-2, -2));
TEST_REGEX_SEARCH("(?:(?<A>a+)|(?<A>b+))\\.\\k<A>", perl, "aaaa.bbbb", match_default, make_array(-2, -2));
TEST_REGEX_SEARCH("(?:(?<A>a+)|(?<A>b+))\\.\\k<A>", perl, "bbbb.bbbb", match_default, make_array(0, 9, -1, -1, 0, 4, -2, -2));
TEST_REGEX_SEARCH("(?:(?<A>a+)|(?<A>b+)|c+)\\.\\k<A>", perl, "cccc.cccc", match_default, make_array(-2, -2));
} }

View File

@ -184,5 +184,11 @@ void test_replace()
TEST_REGEX_REPLACE("(?<one>a+)(?<two>b+)", perl, " ...aabb,,", match_default|format_no_copy, "$2", "bb"); TEST_REGEX_REPLACE("(?<one>a+)(?<two>b+)", perl, " ...aabb,,", match_default|format_no_copy, "$2", "bb");
TEST_REGEX_REPLACE("(?<one>a+)(?<two>b+)", perl, " ...aabb,,", match_default|format_no_copy, "d$+{one}c", "daac"); TEST_REGEX_REPLACE("(?<one>a+)(?<two>b+)", perl, " ...aabb,,", match_default|format_no_copy, "d$+{one}c", "daac");
TEST_REGEX_REPLACE("(?<one>a+)(?<two>b+)", perl, " ...aabb,,", match_default|format_no_copy, "c$+{two}d", "cbbd"); TEST_REGEX_REPLACE("(?<one>a+)(?<two>b+)", perl, " ...aabb,,", match_default|format_no_copy, "c$+{two}d", "cbbd");
TEST_REGEX_REPLACE("(?<one>a)(?<one>b)(c)", perl, " ...abc,,", match_default, "$1.$2.$3.$+{one}", " ...a.b.c.a,,");
TEST_REGEX_REPLACE("(?:(?<one>a)|(?<one>b))", perl, " ...a,,", match_default, "$1.$2.$+{one}", " ...a..a,,");
TEST_REGEX_REPLACE("(?:(?<one>a)|(?<one>b))", perl, " ...b,,", match_default, "$1.$2.$+{one}", " ....b.b,,");
TEST_REGEX_REPLACE("(?:(?<one>a)(?<one>b))", perl, " ...ab,,", match_default, "$1.$2.$+{one}", " ...a.b.a,,");
} }

View File

@ -49,7 +49,7 @@ void test_tricky_cases()
TEST_REGEX_SEARCH("(a)d|(b)c", perl, "abc", match_default, make_array(1, 3, -1, -1, 1, 2, -2, -2)); TEST_REGEX_SEARCH("(a)d|(b)c", perl, "abc", match_default, make_array(1, 3, -1, -1, 1, 2, -2, -2));
TEST_REGEX_SEARCH("_+((www)|(ftp)|(mailto)):_*", perl, "_wwwnocolon _mailto:", match_default, make_array(12, 20, 13, 19, -1, -1, -1, -1, 13, 19, -2, -2)); TEST_REGEX_SEARCH("_+((www)|(ftp)|(mailto)):_*", perl, "_wwwnocolon _mailto:", match_default, make_array(12, 20, 13, 19, -1, -1, -1, -1, 13, 19, -2, -2));
// subtleties of matching // subtleties of matching
TEST_REGEX_SEARCH("a(b)?c\\1d", perl, "acd", match_default, make_array(0, 3, -1, -1, -2, -2)); TEST_REGEX_SEARCH("a(b)?c\\1d", perl, "acd", match_default, make_array(-2, -2));
TEST_REGEX_SEARCH("a(b?c)+d", perl, "accd", match_default, make_array(0, 4, 2, 3, -2, -2)); TEST_REGEX_SEARCH("a(b?c)+d", perl, "accd", match_default, make_array(0, 4, 2, 3, -2, -2));
TEST_REGEX_SEARCH("(wee|week)(knights|night)", perl, "weeknights", match_default, make_array(0, 10, 0, 3, 3, 10, -2, -2)); TEST_REGEX_SEARCH("(wee|week)(knights|night)", perl, "weeknights", match_default, make_array(0, 10, 0, 3, 3, 10, -2, -2));
TEST_REGEX_SEARCH(".*", perl, "abc", match_default, make_array(0, 3, -2, 3, 3, -2, -2)); TEST_REGEX_SEARCH(".*", perl, "abc", match_default, make_array(0, 3, -2, 3, 3, -2, -2));