Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 50 additions & 27 deletions stl/inc/regex
Original file line number Diff line number Diff line change
Expand Up @@ -1287,15 +1287,15 @@ _BITMASK_OPS(_EMPTY_ARGUMENT, _Node_flags)

enum _Node_type { // type flag for nfa nodes
_N_none,
_N_nop, // TRANSITION, ABI: preserved for binary compatibility, the parser no longer generates _N_nop nodes
_N_nop, // TRANSITION, ABI: preserved for binary compatibility, the parser no longer generates such nodes
_N_bol,
_N_eol,
_N_wbound,
_N_dot,
_N_str,
_N_class,
_N_group,
_N_end_group,
_N_group, // TRANSITION, ABI: preserved for binary compatibility, the parser no longer generates such nodes
_N_end_group, // TRANSITION, ABI: preserved for binary compatibility, the parser no longer generates such nodes
_N_assert,
_N_neg_assert,
_N_end_assert,
Expand Down Expand Up @@ -1607,15 +1607,15 @@ public:
void _Add_named_class(typename _RxTraits::char_class_type, _Rx_char_class_kind);
void _Add_equiv(const _Elem*, const _Elem*);
void _Add_coll(const _Elem*, const _Elem*);
_Node_base* _Begin_group();
void _End_group(_Node_base* _Back);
_Node_assert* _Begin_assert_group(bool _Neg);
void _End_assert_group(_Node_assert* _Assert_start);
_Node_base* _Begin_capture_group(unsigned int _Idx);
void _Add_backreference(unsigned int _Idx);
_Node_base* _Begin_if(_Node_base* _Start);
void _Else_if(_Node_base*, _Node_base*);
void _Add_rep(int _Min, int _Max, bool _Greedy);
void _Add_group_rep(_Node_base* _Start, int _Min, int _Max, bool _Greedy);
void _Add_nongroup_rep(int _Min, int _Max, bool _Greedy);
void _Negate();
_Root_node* _End_pattern();

Expand All @@ -1628,6 +1628,7 @@ private:
void _Add_char_to_array(_Elem _Ch);
void _Add_elts(_Node_class<_Elem, _RxTraits>*, typename _RxTraits::char_class_type, bool);
void _Char_to_elts(const _Elem*, const _Elem*, _Sequence<_Elem>**);
void _Generate_rep(_Node_base* _First_inner, int _Min, int _Max, bool _Greedy);

_Root_node* _Root;
_Node_base* _Current;
Expand Down Expand Up @@ -2238,6 +2239,7 @@ private:
void _Disjunction();
void _Calculate_loop_simplicity(_Node_base* _Nx, _Node_base* _Ne, _Node_rep* _Outer_rep, bool _Nonreentrant);

_Node_base* _Group_start{};
_FwdIt _Pat;
_FwdIt _End;
unsigned int _Grp_idx = 0;
Expand Down Expand Up @@ -3597,20 +3599,14 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_coll(const _Elem* const _First, c
_Char_to_elts(_First, _Last, _Cur);
}

template <class _FwdIt, class _Elem, class _RxTraits>
_Node_base* _Builder3<_FwdIt, _Elem, _RxTraits>::_Begin_group() { // add group node
return _New_node(_N_group);
}

template <class _FwdIt, class _Elem, class _RxTraits>
void _Builder3<_FwdIt, _Elem, _RxTraits>::_End_group(_Node_base* _Back) { // add end of group node
_Emit_str_node();
_Node_type _Elt;
if (_Back->_Kind == _N_group) {
_Elt = _N_end_group;
} else if (_Back->_Kind == _N_assert || _Back->_Kind == _N_neg_assert) {
if (_Back->_Kind == _N_assert || _Back->_Kind == _N_neg_assert) {
_Elt = _N_end_assert;
} else {
_STL_INTERNAL_CHECK(_Back->_Kind == _N_capture);
_Elt = _N_end_capture;
}

Expand Down Expand Up @@ -3685,8 +3681,19 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Else_if(_Node_base* const _Start, _No
}

template <class _FwdIt, class _Elem, class _RxTraits>
void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_rep(int _Min, int _Max, bool _Greedy) { // add repeat node
void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_group_rep(_Node_base* const _Start, int _Min, int _Max, bool _Greedy) {
// add repeat node around a group beginning after _Start
_Emit_str_node();
if (_Start != _Current) { // not an empty group
_Generate_rep(_Start->_Next, _Min, _Max, _Greedy);
}
}

template <class _FwdIt, class _Elem, class _RxTraits>
void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_nongroup_rep(int _Min, int _Max, bool _Greedy) {
// add repeat node around current node
if (!_Chars.empty()) {
// repeat applies to last character in character sequence only
const _Elem _Last_char = _Chars.back();
_Chars.pop_back();
_Emit_str_node();
Expand All @@ -3695,25 +3702,36 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_rep(int _Min, int _Max, bool _Gre
_Node->_Data._Insert2(_Last_char);
}

_Node_base* _Pos = _Current;
if (_Pos->_Kind == _N_end_group || _Pos->_Kind == _N_end_capture) {
_Pos = static_cast<_Node_end_group*>(_Pos)->_Back;
} else if (_Min == 0 && _Max == 1) {
return _Generate_rep(_Current, _Min, _Max, _Greedy);
}

template <class _FwdIt, class _Elem, class _RxTraits>
void _Builder3<_FwdIt, _Elem, _RxTraits>::_Generate_rep(_Node_base* _First_node, int _Min, int _Max, bool _Greedy) {
// generates a rep around the range from given start node to current node
if (_Min == 0 && _Max == 1 && _First_node == _Current && _First_node->_Kind != _N_assert) {
// Rewrite zero-or-one quantifiers as alternations to make the
// "simple loop" optimization more likely to engage.
//
// GH-5490: This rewrite becomes observably incorrect
// if the subexpression contains capture groups,
// so we don't apply it if the subexpression is surrounded
// by a capturing or non-capturing group.
// if the subexpression contains capture groups
// that may be matched at the end of a repetition.
// Since a capture group is represented by two nodes,
// a capture group can only appear in this range
// if it contains at least two nodes or an assertion.
// Captures in negative assertions are guaranteed
// to be unmatched at the end of a repetition,
// but those in positive assertions might be matched.
// Hence, we disable this optimization
// if the range consists of more than one node
// or a single positive assertion.
_Node_endif* _End = new _Node_endif;
_Node_if* _If_expr = new _Node_if(_End);
_Node_if* _If_empty_str = new _Node_if(_End);
_If_empty_str->_Next = _End;
_If_expr->_Child = _If_empty_str;

_Link_node(_End);
_Insert_node(_Pos, _If_expr);
_Insert_node(_First_node, _If_expr);

if (!_Greedy) {
_If_expr->_Next->_Prev = _If_empty_str;
Expand All @@ -3731,7 +3749,7 @@ void _Builder3<_FwdIt, _Elem, _RxTraits>::_Add_rep(int _Min, int _Max, bool _Gre
_Link_node(_Node0);
_Node_rep* _Nx = new _Node_rep(_Greedy, _Min, _Max, _Node0, _Root->_Loops++);
_Node0->_Begin_rep = _Nx;
_Insert_node(_Pos, _Nx);
_Insert_node(_First_node, _Nx);
}

template <class _FwdIt, class _Elem, class _RxTraits>
Expand Down Expand Up @@ -5532,9 +5550,7 @@ void _Parser3<_FwdIt, _Elem, _RxTraits>::_Do_capture_group() { // add capture gr

template <class _FwdIt, class _Elem, class _RxTraits>
void _Parser3<_FwdIt, _Elem, _RxTraits>::_Do_noncapture_group() { // add non-capture group
_Node_base* _Pos1 = _Nfa._Begin_group();
_Disjunction();
_Nfa._End_group(_Pos1);
}

template <class _FwdIt, class _Elem, class _RxTraits>
Expand All @@ -5551,6 +5567,7 @@ bool _Parser3<_FwdIt, _Elem, _RxTraits>::_Wrapped_disjunction() { // add disjunc
_Error(regex_constants::error_stack);
}

auto _Start = _Nfa._Getmark();
if (!(_L_flags & _L_empty_grp) && _Mchar == _Meta_rpar) {
_Error(regex_constants::error_paren);
} else if ((_L_flags & _L_nc_asrt) && _Mchar == _Meta_query) { // check for valid ECMAScript (?x ... ) group
Expand All @@ -5576,6 +5593,7 @@ bool _Parser3<_FwdIt, _Elem, _RxTraits>::_Wrapped_disjunction() { // add disjunc
_Do_capture_group();
}

_Group_start = _Start;
--_Disj_count;
return true;
}
Expand Down Expand Up @@ -5807,7 +5825,11 @@ void _Parser3<_FwdIt, _Elem, _RxTraits>::_Quantifier() { // check for quantifier
_Next();
}

_Nfa._Add_rep(_Min, _Max, _Greedy);
if (_Group_start) {
_Nfa._Add_group_rep(_Group_start, _Min, _Max, _Greedy);
} else {
_Nfa._Add_nongroup_rep(_Min, _Max, _Greedy);
}
}

template <class _FwdIt, class _Elem, class _RxTraits>
Expand Down Expand Up @@ -5874,7 +5896,8 @@ void _Parser3<_FwdIt, _Elem, _RxTraits>::_Alternative() { // check for valid alt
_Quantifier();
}

_Found = true;
_Group_start = nullptr;
_Found = true;
}
}

Expand Down
48 changes: 48 additions & 0 deletions tests/std/tests/VSO_0000000_regex_use/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2679,6 +2679,53 @@ void test_gh_6289() {
g_regexTester.should_not_match("abc", "a|b|c");
}

void test_gh_6359() {
// GH-6359: Avoid generating group nodes for non-capturing groups
g_regexTester.should_match("abc", "ab*c");
g_regexTester.should_match("abbbbc", "ab*c");
g_regexTester.should_match("ac", "ab*c");
g_regexTester.should_not_match("", "ab*c");
g_regexTester.should_not_match("a", "ab*c");
g_regexTester.should_not_match("c", "ab*c");

g_regexTester.should_match("abcd", "a(?:bc)*d");
g_regexTester.should_match("abcbcd", "a(?:bc)*d");
g_regexTester.should_match("ad", "a(?:bc)*d");
g_regexTester.should_not_match("", "a(?:bc)*d");
g_regexTester.should_not_match("a", "a(?:bc)*d");
g_regexTester.should_not_match("d", "a(?:bc)*d");
g_regexTester.should_not_match("abccd", "a(?:bc)*d");

g_regexTester.should_capture("ad", "a(bc)*d", "");
g_regexTester.should_not_match("", "a(bc)*d");
g_regexTester.should_not_match("a", "a(bc)*d");
g_regexTester.should_not_match("d", "a(bc)*d");
g_regexTester.should_not_match("abccd", "a(bc)*d");

{
test_regex re_with_capture_group{&g_regexTester, "^a(bc)*d$"};
re_with_capture_group.should_search_match_capture_groups("abcd", "abcd", match_default, {{1, 3}});
re_with_capture_group.should_search_match_capture_groups("abcbcd", "abcbcd", match_default, {{3, 5}});
}

g_regexTester.should_match("abcbcd", "a(?:(?:bc)+){2}d");
g_regexTester.should_match("abcbcbcbcd", "a(?:(?:bc)+){2}d");
g_regexTester.should_not_match("ad", "a(?:(?:bc)+){2}d");
g_regexTester.should_not_match("", "a(?:(?:bc)+){2}d");
g_regexTester.should_not_match("a", "a(?:(?:bc)+){2}d");
g_regexTester.should_not_match("d", "a(?:(?:bc)+){2}d");
g_regexTester.should_not_match("abcd", "a(?:(?:bc)+){2}d");
g_regexTester.should_not_match("abcbc", "a(?:(?:bc)+){2}d");
g_regexTester.should_not_match("bcbcd", "a(?:(?:bc)+){2}d");
g_regexTester.should_match("abcd", "a(?:(?:bc)*){2}d");

g_regexTester.should_match("bca", "(?:bc)+a+");
g_regexTester.should_match("bcbcaa", "(?:bc)+a+");
g_regexTester.should_not_match("bcabca", "(?:bc)+a+");

g_regexTester.should_capture("a", "(?:(?=(a)))?a", "");
}

int main() {
test_dev10_449367_case_insensitivity_should_work();
test_dev11_462743_regex_collate_should_not_disable_regex_icase();
Expand Down Expand Up @@ -2750,6 +2797,7 @@ int main() {
test_gh_6262();
test_gh_6267();
test_gh_6289();
test_gh_6359();

return g_regexTester.result();
}