libstdc++
regex_compiler.tcc
Go to the documentation of this file.
00001 // class template regex -*- C++ -*-
00002 
00003 // Copyright (C) 2013-2019 Free Software Foundation, Inc.
00004 //
00005 // This file is part of the GNU ISO C++ Library.  This library is free
00006 // software; you can redistribute it and/or modify it under the
00007 // terms of the GNU General Public License as published by the
00008 // Free Software Foundation; either version 3, or (at your option)
00009 // any later version.
00010 
00011 // This library is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 // GNU General Public License for more details.
00015 
00016 // Under Section 7 of GPL version 3, you are granted additional
00017 // permissions described in the GCC Runtime Library Exception, version
00018 // 3.1, as published by the Free Software Foundation.
00019 
00020 // You should have received a copy of the GNU General Public License and
00021 // a copy of the GCC Runtime Library Exception along with this program;
00022 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
00023 // <http://www.gnu.org/licenses/>.
00024 
00025 /**
00026  *  @file bits/regex_compiler.tcc
00027  *  This is an internal header file, included by other library headers.
00028  *  Do not attempt to use it directly. @headername{regex}
00029  */
00030 
00031 // FIXME make comments doxygen format.
00032 
00033 /*
00034 // This compiler refers to "Regular Expression Matching Can Be Simple And Fast"
00035 // (http://swtch.com/~rsc/regexp/regexp1.html),
00036 // but doesn't strictly follow it.
00037 //
00038 // When compiling, states are *chained* instead of tree- or graph-constructed.
00039 // It's more like structured programs: there's if statement and loop statement.
00040 //
00041 // For alternative structure (say "a|b"), aka "if statement", two branches
00042 // should be constructed. However, these two shall merge to an "end_tag" at
00043 // the end of this operator:
00044 //
00045 //                branch1
00046 //              /        \
00047 // => begin_tag            end_tag =>
00048 //              \        /
00049 //                branch2
00050 //
00051 // This is the difference between this implementation and that in Russ's
00052 // article.
00053 //
00054 // That's why we introduced dummy node here ------ "end_tag" is a dummy node.
00055 // All dummy nodes will be eliminated at the end of compilation.
00056 */
00057 
00058 namespace std _GLIBCXX_VISIBILITY(default)
00059 {
00060 _GLIBCXX_BEGIN_NAMESPACE_VERSION
00061 
00062 namespace __detail
00063 {
00064   template<typename _TraitsT>
00065     _Compiler<_TraitsT>::
00066     _Compiler(_IterT __b, _IterT __e,
00067               const typename _TraitsT::locale_type& __loc, _FlagT __flags)
00068     : _M_flags((__flags
00069                 & (regex_constants::ECMAScript
00070                    | regex_constants::basic
00071                    | regex_constants::extended
00072                    | regex_constants::grep
00073                    | regex_constants::egrep
00074                    | regex_constants::awk))
00075                ? __flags
00076                : __flags | regex_constants::ECMAScript),
00077       _M_scanner(__b, __e, _M_flags, __loc),
00078       _M_nfa(make_shared<_RegexT>(__loc, _M_flags)),
00079       _M_traits(_M_nfa->_M_traits),
00080       _M_ctype(std::use_facet<_CtypeT>(__loc))
00081     {
00082       _StateSeqT __r(*_M_nfa, _M_nfa->_M_start());
00083       __r._M_append(_M_nfa->_M_insert_subexpr_begin());
00084       this->_M_disjunction();
00085       if (!_M_match_token(_ScannerT::_S_token_eof))
00086         __throw_regex_error(regex_constants::error_paren);
00087       __r._M_append(_M_pop());
00088       __glibcxx_assert(_M_stack.empty());
00089       __r._M_append(_M_nfa->_M_insert_subexpr_end());
00090       __r._M_append(_M_nfa->_M_insert_accept());
00091       _M_nfa->_M_eliminate_dummy();
00092     }
00093 
00094   template<typename _TraitsT>
00095     void
00096     _Compiler<_TraitsT>::
00097     _M_disjunction()
00098     {
00099       this->_M_alternative();
00100       while (_M_match_token(_ScannerT::_S_token_or))
00101         {
00102           _StateSeqT __alt1 = _M_pop();
00103           this->_M_alternative();
00104           _StateSeqT __alt2 = _M_pop();
00105           auto __end = _M_nfa->_M_insert_dummy();
00106           __alt1._M_append(__end);
00107           __alt2._M_append(__end);
00108           // __alt2 is state._M_next, __alt1 is state._M_alt. The executor
00109           // executes _M_alt before _M_next, as well as executing left
00110           // alternative before right one.
00111           _M_stack.push(_StateSeqT(*_M_nfa,
00112                                    _M_nfa->_M_insert_alt(
00113                                      __alt2._M_start, __alt1._M_start, false),
00114                                    __end));
00115         }
00116     }
00117 
00118   template<typename _TraitsT>
00119     void
00120     _Compiler<_TraitsT>::
00121     _M_alternative()
00122     {
00123       if (this->_M_term())
00124         {
00125           _StateSeqT __re = _M_pop();
00126           this->_M_alternative();
00127           __re._M_append(_M_pop());
00128           _M_stack.push(__re);
00129         }
00130       else
00131         _M_stack.push(_StateSeqT(*_M_nfa, _M_nfa->_M_insert_dummy()));
00132     }
00133 
00134   template<typename _TraitsT>
00135     bool
00136     _Compiler<_TraitsT>::
00137     _M_term()
00138     {
00139       if (this->_M_assertion())
00140         return true;
00141       if (this->_M_atom())
00142         {
00143           while (this->_M_quantifier());
00144           return true;
00145         }
00146       return false;
00147     }
00148 
00149   template<typename _TraitsT>
00150     bool
00151     _Compiler<_TraitsT>::
00152     _M_assertion()
00153     {
00154       if (_M_match_token(_ScannerT::_S_token_line_begin))
00155         _M_stack.push(_StateSeqT(*_M_nfa, _M_nfa->_M_insert_line_begin()));
00156       else if (_M_match_token(_ScannerT::_S_token_line_end))
00157         _M_stack.push(_StateSeqT(*_M_nfa, _M_nfa->_M_insert_line_end()));
00158       else if (_M_match_token(_ScannerT::_S_token_word_bound))
00159         // _M_value[0] == 'n' means it's negative, say "not word boundary".
00160         _M_stack.push(_StateSeqT(*_M_nfa, _M_nfa->
00161               _M_insert_word_bound(_M_value[0] == 'n')));
00162       else if (_M_match_token(_ScannerT::_S_token_subexpr_lookahead_begin))
00163         {
00164           auto __neg = _M_value[0] == 'n';
00165           this->_M_disjunction();
00166           if (!_M_match_token(_ScannerT::_S_token_subexpr_end))
00167             __throw_regex_error(regex_constants::error_paren,
00168                                 "Parenthesis is not closed.");
00169           auto __tmp = _M_pop();
00170           __tmp._M_append(_M_nfa->_M_insert_accept());
00171           _M_stack.push(
00172               _StateSeqT(
00173                 *_M_nfa,
00174                 _M_nfa->_M_insert_lookahead(__tmp._M_start, __neg)));
00175         }
00176       else
00177         return false;
00178       return true;
00179     }
00180 
00181   template<typename _TraitsT>
00182     bool
00183     _Compiler<_TraitsT>::
00184     _M_quantifier()
00185     {
00186       bool __neg = (_M_flags & regex_constants::ECMAScript);
00187       auto __init = [this, &__neg]()
00188         {
00189           if (_M_stack.empty())
00190             __throw_regex_error(regex_constants::error_badrepeat,
00191                                 "Nothing to repeat before a quantifier.");
00192           __neg = __neg && _M_match_token(_ScannerT::_S_token_opt);
00193         };
00194       if (_M_match_token(_ScannerT::_S_token_closure0))
00195         {
00196           __init();
00197           auto __e = _M_pop();
00198           _StateSeqT __r(*_M_nfa,
00199                          _M_nfa->_M_insert_repeat(_S_invalid_state_id,
00200                                                   __e._M_start, __neg));
00201           __e._M_append(__r);
00202           _M_stack.push(__r);
00203         }
00204       else if (_M_match_token(_ScannerT::_S_token_closure1))
00205         {
00206           __init();
00207           auto __e = _M_pop();
00208           __e._M_append(_M_nfa->_M_insert_repeat(_S_invalid_state_id,
00209                                                  __e._M_start, __neg));
00210           _M_stack.push(__e);
00211         }
00212       else if (_M_match_token(_ScannerT::_S_token_opt))
00213         {
00214           __init();
00215           auto __e = _M_pop();
00216           auto __end = _M_nfa->_M_insert_dummy();
00217           _StateSeqT __r(*_M_nfa,
00218                          _M_nfa->_M_insert_repeat(_S_invalid_state_id,
00219                                                   __e._M_start, __neg));
00220           __e._M_append(__end);
00221           __r._M_append(__end);
00222           _M_stack.push(__r);
00223         }
00224       else if (_M_match_token(_ScannerT::_S_token_interval_begin))
00225         {
00226           if (_M_stack.empty())
00227             __throw_regex_error(regex_constants::error_badrepeat,
00228                                 "Nothing to repeat before a quantifier.");
00229           if (!_M_match_token(_ScannerT::_S_token_dup_count))
00230             __throw_regex_error(regex_constants::error_badbrace,
00231                                 "Unexpected token in brace expression.");
00232           _StateSeqT __r(_M_pop());
00233           _StateSeqT __e(*_M_nfa, _M_nfa->_M_insert_dummy());
00234           long __min_rep = _M_cur_int_value(10);
00235           bool __infi = false;
00236           long __n;
00237 
00238           // {3
00239           if (_M_match_token(_ScannerT::_S_token_comma))
00240             if (_M_match_token(_ScannerT::_S_token_dup_count)) // {3,7}
00241               __n = _M_cur_int_value(10) - __min_rep;
00242             else
00243               __infi = true;
00244           else
00245             __n = 0;
00246           if (!_M_match_token(_ScannerT::_S_token_interval_end))
00247             __throw_regex_error(regex_constants::error_brace,
00248                                 "Unexpected end of brace expression.");
00249 
00250           __neg = __neg && _M_match_token(_ScannerT::_S_token_opt);
00251 
00252           for (long __i = 0; __i < __min_rep; ++__i)
00253             __e._M_append(__r._M_clone());
00254 
00255           if (__infi)
00256             {
00257               auto __tmp = __r._M_clone();
00258               _StateSeqT __s(*_M_nfa,
00259                              _M_nfa->_M_insert_repeat(_S_invalid_state_id,
00260                                                       __tmp._M_start, __neg));
00261               __tmp._M_append(__s);
00262               __e._M_append(__s);
00263             }
00264           else
00265             {
00266               if (__n < 0)
00267                 __throw_regex_error(regex_constants::error_badbrace,
00268                                     "Invalid range in brace expression.");
00269               auto __end = _M_nfa->_M_insert_dummy();
00270               // _M_alt is the "match more" branch, and _M_next is the
00271               // "match less" one. Switch _M_alt and _M_next of all created
00272               // nodes. This is a hack but IMO works well.
00273               std::stack<_StateIdT> __stack;
00274               for (long __i = 0; __i < __n; ++__i)
00275                 {
00276                   auto __tmp = __r._M_clone();
00277                   auto __alt = _M_nfa->_M_insert_repeat(__tmp._M_start,
00278                                                         __end, __neg);
00279                   __stack.push(__alt);
00280                   __e._M_append(_StateSeqT(*_M_nfa, __alt, __tmp._M_end));
00281                 }
00282               __e._M_append(__end);
00283               while (!__stack.empty())
00284                 {
00285                   auto& __tmp = (*_M_nfa)[__stack.top()];
00286                   __stack.pop();
00287                   std::swap(__tmp._M_next, __tmp._M_alt);
00288                 }
00289             }
00290           _M_stack.push(__e);
00291         }
00292       else
00293         return false;
00294       return true;
00295     }
00296 
00297 #define __INSERT_REGEX_MATCHER(__func, ...)\
00298         do {\
00299           if (!(_M_flags & regex_constants::icase))\
00300             if (!(_M_flags & regex_constants::collate))\
00301               __func<false, false>(__VA_ARGS__);\
00302             else\
00303               __func<false, true>(__VA_ARGS__);\
00304           else\
00305             if (!(_M_flags & regex_constants::collate))\
00306               __func<true, false>(__VA_ARGS__);\
00307             else\
00308               __func<true, true>(__VA_ARGS__);\
00309         } while (false)
00310 
00311   template<typename _TraitsT>
00312     bool
00313     _Compiler<_TraitsT>::
00314     _M_atom()
00315     {
00316       if (_M_match_token(_ScannerT::_S_token_anychar))
00317         {
00318           if (!(_M_flags & regex_constants::ECMAScript))
00319             __INSERT_REGEX_MATCHER(_M_insert_any_matcher_posix);
00320           else
00321             __INSERT_REGEX_MATCHER(_M_insert_any_matcher_ecma);
00322         }
00323       else if (_M_try_char())
00324         __INSERT_REGEX_MATCHER(_M_insert_char_matcher);
00325       else if (_M_match_token(_ScannerT::_S_token_backref))
00326         _M_stack.push(_StateSeqT(*_M_nfa, _M_nfa->
00327                                  _M_insert_backref(_M_cur_int_value(10))));
00328       else if (_M_match_token(_ScannerT::_S_token_quoted_class))
00329         __INSERT_REGEX_MATCHER(_M_insert_character_class_matcher);
00330       else if (_M_match_token(_ScannerT::_S_token_subexpr_no_group_begin))
00331         {
00332           _StateSeqT __r(*_M_nfa, _M_nfa->_M_insert_dummy());
00333           this->_M_disjunction();
00334           if (!_M_match_token(_ScannerT::_S_token_subexpr_end))
00335             __throw_regex_error(regex_constants::error_paren,
00336                                 "Parenthesis is not closed.");
00337           __r._M_append(_M_pop());
00338           _M_stack.push(__r);
00339         }
00340       else if (_M_match_token(_ScannerT::_S_token_subexpr_begin))
00341         {
00342           _StateSeqT __r(*_M_nfa, _M_nfa->_M_insert_subexpr_begin());
00343           this->_M_disjunction();
00344           if (!_M_match_token(_ScannerT::_S_token_subexpr_end))
00345             __throw_regex_error(regex_constants::error_paren,
00346                                 "Parenthesis is not closed.");
00347           __r._M_append(_M_pop());
00348           __r._M_append(_M_nfa->_M_insert_subexpr_end());
00349           _M_stack.push(__r);
00350         }
00351       else if (!_M_bracket_expression())
00352         return false;
00353       return true;
00354     }
00355 
00356   template<typename _TraitsT>
00357     bool
00358     _Compiler<_TraitsT>::
00359     _M_bracket_expression()
00360     {
00361       bool __neg =
00362         _M_match_token(_ScannerT::_S_token_bracket_neg_begin);
00363       if (!(__neg || _M_match_token(_ScannerT::_S_token_bracket_begin)))
00364         return false;
00365       __INSERT_REGEX_MATCHER(_M_insert_bracket_matcher, __neg);
00366       return true;
00367     }
00368 #undef __INSERT_REGEX_MATCHER
00369 
00370   template<typename _TraitsT>
00371   template<bool __icase, bool __collate>
00372     void
00373     _Compiler<_TraitsT>::
00374     _M_insert_any_matcher_ecma()
00375     {
00376       _M_stack.push(_StateSeqT(*_M_nfa,
00377         _M_nfa->_M_insert_matcher
00378           (_AnyMatcher<_TraitsT, true, __icase, __collate>
00379             (_M_traits))));
00380     }
00381 
00382   template<typename _TraitsT>
00383   template<bool __icase, bool __collate>
00384     void
00385     _Compiler<_TraitsT>::
00386     _M_insert_any_matcher_posix()
00387     {
00388       _M_stack.push(_StateSeqT(*_M_nfa,
00389         _M_nfa->_M_insert_matcher
00390           (_AnyMatcher<_TraitsT, false, __icase, __collate>
00391             (_M_traits))));
00392     }
00393 
00394   template<typename _TraitsT>
00395   template<bool __icase, bool __collate>
00396     void
00397     _Compiler<_TraitsT>::
00398     _M_insert_char_matcher()
00399     {
00400       _M_stack.push(_StateSeqT(*_M_nfa,
00401         _M_nfa->_M_insert_matcher
00402           (_CharMatcher<_TraitsT, __icase, __collate>
00403             (_M_value[0], _M_traits))));
00404     }
00405 
00406   template<typename _TraitsT>
00407   template<bool __icase, bool __collate>
00408     void
00409     _Compiler<_TraitsT>::
00410     _M_insert_character_class_matcher()
00411     {
00412       __glibcxx_assert(_M_value.size() == 1);
00413       _BracketMatcher<_TraitsT, __icase, __collate> __matcher
00414         (_M_ctype.is(_CtypeT::upper, _M_value[0]), _M_traits);
00415       __matcher._M_add_character_class(_M_value, false);
00416       __matcher._M_ready();
00417       _M_stack.push(_StateSeqT(*_M_nfa,
00418         _M_nfa->_M_insert_matcher(std::move(__matcher))));
00419     }
00420 
00421   template<typename _TraitsT>
00422   template<bool __icase, bool __collate>
00423     void
00424     _Compiler<_TraitsT>::
00425     _M_insert_bracket_matcher(bool __neg)
00426     {
00427       _BracketMatcher<_TraitsT, __icase, __collate> __matcher(__neg, _M_traits);
00428       pair<bool, _CharT> __last_char; // Optional<_CharT>
00429       __last_char.first = false;
00430       if (!(_M_flags & regex_constants::ECMAScript))
00431         {
00432           if (_M_try_char())
00433             {
00434               __last_char.first = true;
00435               __last_char.second = _M_value[0];
00436             }
00437           else if (_M_match_token(_ScannerT::_S_token_bracket_dash))
00438             {
00439               __last_char.first = true;
00440               __last_char.second = '-';
00441             }
00442         }
00443       while (_M_expression_term(__last_char, __matcher));
00444       if (__last_char.first)
00445         __matcher._M_add_char(__last_char.second);
00446       __matcher._M_ready();
00447       _M_stack.push(_StateSeqT(
00448                       *_M_nfa,
00449                       _M_nfa->_M_insert_matcher(std::move(__matcher))));
00450     }
00451 
00452   template<typename _TraitsT>
00453   template<bool __icase, bool __collate>
00454     bool
00455     _Compiler<_TraitsT>::
00456     _M_expression_term(pair<bool, _CharT>& __last_char,
00457                        _BracketMatcher<_TraitsT, __icase, __collate>& __matcher)
00458     {
00459       if (_M_match_token(_ScannerT::_S_token_bracket_end))
00460         return false;
00461 
00462       const auto __push_char = [&](_CharT __ch)
00463       {
00464         if (__last_char.first)
00465           __matcher._M_add_char(__last_char.second);
00466         else
00467           __last_char.first = true;
00468         __last_char.second = __ch;
00469       };
00470       const auto __flush = [&]
00471       {
00472         if (__last_char.first)
00473           {
00474             __matcher._M_add_char(__last_char.second);
00475             __last_char.first = false;
00476           }
00477       };
00478 
00479       if (_M_match_token(_ScannerT::_S_token_collsymbol))
00480         {
00481           auto __symbol = __matcher._M_add_collate_element(_M_value);
00482           if (__symbol.size() == 1)
00483             __push_char(__symbol[0]);
00484           else
00485             __flush();
00486         }
00487       else if (_M_match_token(_ScannerT::_S_token_equiv_class_name))
00488         {
00489           __flush();
00490           __matcher._M_add_equivalence_class(_M_value);
00491         }
00492       else if (_M_match_token(_ScannerT::_S_token_char_class_name))
00493         {
00494           __flush();
00495           __matcher._M_add_character_class(_M_value, false);
00496         }
00497       else if (_M_try_char())
00498         __push_char(_M_value[0]);
00499       // POSIX doesn't allow '-' as a start-range char (say [a-z--0]),
00500       // except when the '-' is the first or last character in the bracket
00501       // expression ([--0]). ECMAScript treats all '-' after a range as a
00502       // normal character. Also see above, where _M_expression_term gets called.
00503       //
00504       // As a result, POSIX rejects [-----], but ECMAScript doesn't.
00505       // Boost (1.57.0) always uses POSIX style even in its ECMAScript syntax.
00506       // Clang (3.5) always uses ECMAScript style even in its POSIX syntax.
00507       //
00508       // It turns out that no one reads BNFs ;)
00509       else if (_M_match_token(_ScannerT::_S_token_bracket_dash))
00510         {
00511           if (!__last_char.first)
00512             {
00513               if (!(_M_flags & regex_constants::ECMAScript))
00514                 {
00515                   if (_M_match_token(_ScannerT::_S_token_bracket_end))
00516                     {
00517                       __push_char('-');
00518                       return false;
00519                     }
00520                   __throw_regex_error(
00521                     regex_constants::error_range,
00522                     "Unexpected dash in bracket expression. For POSIX syntax, "
00523                     "a dash is not treated literally only when it is at "
00524                     "beginning or end.");
00525                 }
00526               __push_char('-');
00527             }
00528           else
00529             {
00530               if (_M_try_char())
00531                 {
00532                   __matcher._M_make_range(__last_char.second, _M_value[0]);
00533                   __last_char.first = false;
00534                 }
00535               else if (_M_match_token(_ScannerT::_S_token_bracket_dash))
00536                 {
00537                   __matcher._M_make_range(__last_char.second, '-');
00538                   __last_char.first = false;
00539                 }
00540               else
00541                 {
00542                   if (_M_scanner._M_get_token()
00543                       != _ScannerT::_S_token_bracket_end)
00544                     __throw_regex_error(
00545                       regex_constants::error_range,
00546                       "Character is expected after a dash.");
00547                   __push_char('-');
00548                 }
00549             }
00550         }
00551       else if (_M_match_token(_ScannerT::_S_token_quoted_class))
00552         {
00553           __flush();
00554           __matcher._M_add_character_class(_M_value,
00555                                            _M_ctype.is(_CtypeT::upper,
00556                                                        _M_value[0]));
00557         }
00558       else
00559         __throw_regex_error(regex_constants::error_brack,
00560                             "Unexpected character in bracket expression.");
00561 
00562       return true;
00563     }
00564 
00565   template<typename _TraitsT>
00566     bool
00567     _Compiler<_TraitsT>::
00568     _M_try_char()
00569     {
00570       bool __is_char = false;
00571       if (_M_match_token(_ScannerT::_S_token_oct_num))
00572         {
00573           __is_char = true;
00574           _M_value.assign(1, _M_cur_int_value(8));
00575         }
00576       else if (_M_match_token(_ScannerT::_S_token_hex_num))
00577         {
00578           __is_char = true;
00579           _M_value.assign(1, _M_cur_int_value(16));
00580         }
00581       else if (_M_match_token(_ScannerT::_S_token_ord_char))
00582         __is_char = true;
00583       return __is_char;
00584     }
00585 
00586   template<typename _TraitsT>
00587     bool
00588     _Compiler<_TraitsT>::
00589     _M_match_token(_TokenT token)
00590     {
00591       if (token == _M_scanner._M_get_token())
00592         {
00593           _M_value = _M_scanner._M_get_value();
00594           _M_scanner._M_advance();
00595           return true;
00596         }
00597       return false;
00598     }
00599 
00600   template<typename _TraitsT>
00601     int
00602     _Compiler<_TraitsT>::
00603     _M_cur_int_value(int __radix)
00604     {
00605       long __v = 0;
00606       for (typename _StringT::size_type __i = 0;
00607            __i < _M_value.length(); ++__i)
00608         __v =__v * __radix + _M_traits.value(_M_value[__i], __radix);
00609       return __v;
00610     }
00611 
00612   template<typename _TraitsT, bool __icase, bool __collate>
00613     bool
00614     _BracketMatcher<_TraitsT, __icase, __collate>::
00615     _M_apply(_CharT __ch, false_type) const
00616     {
00617       return [this, __ch]
00618       {
00619         if (std::binary_search(_M_char_set.begin(), _M_char_set.end(),
00620                                _M_translator._M_translate(__ch)))
00621           return true;
00622         auto __s = _M_translator._M_transform(__ch);
00623         for (auto& __it : _M_range_set)
00624           if (_M_translator._M_match_range(__it.first, __it.second, __s))
00625             return true;
00626         if (_M_traits.isctype(__ch, _M_class_set))
00627           return true;
00628         if (std::find(_M_equiv_set.begin(), _M_equiv_set.end(),
00629                       _M_traits.transform_primary(&__ch, &__ch+1))
00630             != _M_equiv_set.end())
00631           return true;
00632         for (auto& __it : _M_neg_class_set)
00633           if (!_M_traits.isctype(__ch, __it))
00634             return true;
00635         return false;
00636       }() ^ _M_is_non_matching;
00637     }
00638 } // namespace __detail
00639 
00640 _GLIBCXX_END_NAMESPACE_VERSION
00641 } // namespace