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