| // class template regex -*- C++ -*- | |
| // Copyright (C) 2013-2026 Free Software Foundation, Inc. | |
| // | |
| // This file is part of the GNU ISO C++ Library. This library is free | |
| // software; you can redistribute it and/or modify it under the | |
| // terms of the GNU General Public License as published by the | |
| // Free Software Foundation; either version 3, or (at your option) | |
| // any later version. | |
| // This library is distributed in the hope that it will be useful, | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| // GNU General Public License for more details. | |
| // Under Section 7 of GPL version 3, you are granted additional | |
| // permissions described in the GCC Runtime Library Exception, version | |
| // 3.1, as published by the Free Software Foundation. | |
| // You should have received a copy of the GNU General Public License and | |
| // a copy of the GCC Runtime Library Exception along with this program; | |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| // <http://www.gnu.org/licenses/>. | |
| /** | |
| * @file bits/regex.tcc | |
| * This is an internal header file, included by other library headers. | |
| * Do not attempt to use it directly. @headername{regex} | |
| */ | |
| namespace std _GLIBCXX_VISIBILITY(default) | |
| { | |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION | |
| namespace __detail | |
| { | |
| /// @cond undocumented | |
| // Result of merging regex_match and regex_search. | |
| // | |
| // __policy now can be _S_auto (auto dispatch) and _S_alternate (use | |
| // the other one if possible, for test purpose). | |
| // | |
| // That __match_mode is true means regex_match, else regex_search. | |
| template<typename _BiIter, typename _Alloc, | |
| typename _CharT, typename _TraitsT> | |
| bool | |
| __regex_algo_impl(_BiIter __s, | |
| _BiIter __e, | |
| match_results<_BiIter, _Alloc>& __m, | |
| const basic_regex<_CharT, _TraitsT>& __re, | |
| regex_constants::match_flag_type __flags, | |
| _RegexExecutorPolicy __policy, | |
| bool __match_mode) | |
| { | |
| if (__re._M_automaton == nullptr) | |
| return false; | |
| typename match_results<_BiIter, _Alloc>::_Unchecked& __res = __m; | |
| __m._M_begin = __s; | |
| __m._M_resize(__re._M_automaton->_M_sub_count()); | |
| bool __ret; | |
| bool __use_dfs = true; | |
| if ((__re.flags() & regex_constants::__polynomial) | |
| || (__policy == _RegexExecutorPolicy::_S_alternate | |
| && !__re._M_automaton->_M_has_backref)) | |
| __use_dfs = false; | |
| _Executor<_BiIter, _Alloc, _TraitsT> | |
| __executor(__s, __e, __res, __re, __flags, __use_dfs); | |
| if (__match_mode) | |
| __ret = __executor._M_match(); | |
| else | |
| __ret = __executor._M_search(); | |
| if (__ret) | |
| { | |
| for (auto& __it : __res) | |
| if (!__it.matched) | |
| __it.first = __it.second = __e; | |
| auto& __pre = __m._M_prefix(); | |
| auto& __suf = __m._M_suffix(); | |
| if (__match_mode) | |
| { | |
| __pre.matched = false; | |
| __pre.first = __s; | |
| __pre.second = __s; | |
| __suf.matched = false; | |
| __suf.first = __e; | |
| __suf.second = __e; | |
| } | |
| else | |
| { | |
| __pre.first = __s; | |
| __pre.second = __res[0].first; | |
| __pre.matched = (__pre.first != __pre.second); | |
| __suf.first = __res[0].second; | |
| __suf.second = __e; | |
| __suf.matched = (__suf.first != __suf.second); | |
| } | |
| } | |
| else | |
| { | |
| __m._M_establish_failed_match(__e); | |
| } | |
| return __ret; | |
| } | |
| inline void | |
| __lookup_collatename(string& __name) noexcept | |
| { | |
| static const char* const __collatenames[] = | |
| { | |
| "NUL", | |
| "SOH", | |
| "STX", | |
| "ETX", | |
| "EOT", | |
| "ENQ", | |
| "ACK", | |
| "alert", | |
| "backspace", | |
| "tab", | |
| "newline", | |
| "vertical-tab", | |
| "form-feed", | |
| "carriage-return", | |
| "SO", | |
| "SI", | |
| "DLE", | |
| "DC1", | |
| "DC2", | |
| "DC3", | |
| "DC4", | |
| "NAK", | |
| "SYN", | |
| "ETB", | |
| "CAN", | |
| "EM", | |
| "SUB", | |
| "ESC", | |
| "IS4", | |
| "IS3", | |
| "IS2", | |
| "IS1", | |
| "space", | |
| "exclamation-mark", | |
| "quotation-mark", | |
| "number-sign", | |
| "dollar-sign", | |
| "percent-sign", | |
| "ampersand", | |
| "apostrophe", | |
| "left-parenthesis", | |
| "right-parenthesis", | |
| "asterisk", | |
| "plus-sign", | |
| "comma", | |
| "hyphen", | |
| "period", | |
| "slash", | |
| "zero", | |
| "one", | |
| "two", | |
| "three", | |
| "four", | |
| "five", | |
| "six", | |
| "seven", | |
| "eight", | |
| "nine", | |
| "colon", | |
| "semicolon", | |
| "less-than-sign", | |
| "equals-sign", | |
| "greater-than-sign", | |
| "question-mark", | |
| "commercial-at", | |
| "A", | |
| "B", | |
| "C", | |
| "D", | |
| "E", | |
| "F", | |
| "G", | |
| "H", | |
| "I", | |
| "J", | |
| "K", | |
| "L", | |
| "M", | |
| "N", | |
| "O", | |
| "P", | |
| "Q", | |
| "R", | |
| "S", | |
| "T", | |
| "U", | |
| "V", | |
| "W", | |
| "X", | |
| "Y", | |
| "Z", | |
| "left-square-bracket", | |
| "backslash", | |
| "right-square-bracket", | |
| "circumflex", | |
| "underscore", | |
| "grave-accent", | |
| "a", | |
| "b", | |
| "c", | |
| "d", | |
| "e", | |
| "f", | |
| "g", | |
| "h", | |
| "i", | |
| "j", | |
| "k", | |
| "l", | |
| "m", | |
| "n", | |
| "o", | |
| "p", | |
| "q", | |
| "r", | |
| "s", | |
| "t", | |
| "u", | |
| "v", | |
| "w", | |
| "x", | |
| "y", | |
| "z", | |
| "left-curly-bracket", | |
| "vertical-line", | |
| "right-curly-bracket", | |
| "tilde", | |
| "DEL", | |
| }; | |
| for (const auto& __it : __collatenames) | |
| if (__name == __it) | |
| { | |
| __name.assign(1, static_cast<char>(&__it - __collatenames)); | |
| return; | |
| } | |
| __name.clear(); | |
| } | |
| /// @endcond | |
| } // namespace __detail | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr | |
| template<typename _Ch_type> | |
| template<typename _Fwd_iter> | |
| typename regex_traits<_Ch_type>::string_type | |
| regex_traits<_Ch_type>:: | |
| lookup_collatename(_Fwd_iter __first, _Fwd_iter __last) const | |
| { | |
| // TODO Add digraph support: | |
| // http://boost.sourceforge.net/libs/regex/doc/collating_names.html | |
| if constexpr (is_same<char_type, char>::value) | |
| { | |
| string __s(__first, __last); | |
| __detail::__lookup_collatename(__s); | |
| return __s; | |
| } | |
| else | |
| { | |
| typedef std::ctype<char_type> __ctype_type; | |
| const __ctype_type& __fctyp(use_facet<__ctype_type>(_M_locale)); | |
| string __s; | |
| for (; __first != __last; ++__first) | |
| __s += __fctyp.narrow(*__first, 0); | |
| __detail::__lookup_collatename(__s); | |
| if (__s.empty()) | |
| return string_type(); | |
| else | |
| return string_type(1, __fctyp.widen(__s[0])); | |
| } | |
| } | |
| template<typename _Ch_type> | |
| template<typename _Fwd_iter> | |
| typename regex_traits<_Ch_type>::char_class_type | |
| regex_traits<_Ch_type>:: | |
| lookup_classname(_Fwd_iter __first, _Fwd_iter __last, bool __icase) const | |
| { | |
| if constexpr (__is_any_random_access_iter<_Fwd_iter>::value) | |
| if ((__last - __first) > 6) [[__unlikely__]] | |
| return {}; // "xdigit" is the longest classname | |
| typedef std::ctype<char_type> __ctype_type; | |
| const __ctype_type& __fctyp(use_facet<__ctype_type>(_M_locale)); | |
| auto __read_ch = [&]() -> char { | |
| if (__first == __last) | |
| return '\0'; | |
| char __c = __fctyp.narrow(__fctyp.tolower(*__first), 0); | |
| ++__first; | |
| return __c; | |
| }; | |
| auto __match = [&](const char* __s) -> bool { | |
| do | |
| if (__read_ch() != *__s) | |
| return false; | |
| while (*++__s); | |
| return __first == __last; | |
| }; | |
| switch(__read_ch()) | |
| { | |
| case 'a': | |
| if (__read_ch() == 'l') | |
| switch (__read_ch()) | |
| { | |
| case 'n': | |
| if (__match("um")) // "alnum" | |
| return ctype_base::alnum; | |
| break; | |
| case 'p': | |
| if (__match("ha")) // "alpha" | |
| return ctype_base::alpha; | |
| break; | |
| } | |
| break; | |
| case 'b': | |
| if (__match("lank")) // "blank" | |
| return ctype_base::blank; | |
| break; | |
| case 'c': | |
| if (__match("ntrl")) // "cntrl" | |
| return ctype_base::cntrl; | |
| break; | |
| case 'd': | |
| if (__first == __last || __match("igit")) // "d" or "digit" | |
| return ctype_base::digit; | |
| break; | |
| case 'g': | |
| if (__match("raph")) // "graph" | |
| return ctype_base::graph; | |
| break; | |
| case 'l': | |
| if (__match("ower")) // "lower" | |
| return __icase ? ctype_base::alpha : ctype_base::lower; | |
| break; | |
| case 'p': | |
| switch (__read_ch()) | |
| { | |
| case 'r': | |
| if (__match("int")) // "print" | |
| return ctype_base::print; | |
| break; | |
| case 'u': | |
| if (__match("nct")) // "punct" | |
| return ctype_base::punct; | |
| break; | |
| } | |
| break; | |
| case 's': | |
| if (__first == __last || __match("pace")) // "s" or "space" | |
| return ctype_base::space; | |
| break; | |
| case 'u': | |
| if (__match("pper")) // "upper" | |
| return __icase ? ctype_base::alpha : ctype_base::upper; | |
| break; | |
| case 'w': | |
| if (__first == __last) // "w" | |
| return {ctype_base::alnum, char_class_type::_S_under}; | |
| break; | |
| case 'x': | |
| if (__match("digit")) // "xdigit" | |
| return ctype_base::xdigit; | |
| break; | |
| } | |
| return {}; | |
| } | |
| template<typename _Ch_type> | |
| bool | |
| regex_traits<_Ch_type>:: | |
| isctype(_Ch_type __c, char_class_type __f) const | |
| { | |
| typedef std::ctype<char_type> __ctype_type; | |
| const __ctype_type& __fctyp(use_facet<__ctype_type>(_M_locale)); | |
| return __fctyp.is(__f._M_base, __c) | |
| // [[:w:]] | |
| || ((__f._M_extended & _RegexMask::_S_under) | |
| && __c == __fctyp.widen('_')); | |
| } | |
| template<typename _Ch_type> | |
| int | |
| regex_traits<_Ch_type>:: | |
| value(_Ch_type __ch, int __radix) const | |
| { | |
| if constexpr (sizeof(_Ch_type) > 1) | |
| { | |
| const auto& __ctyp = std::use_facet<ctype<_Ch_type>>(_M_locale); | |
| const char __c = __ctyp.narrow(__ch, '\0'); | |
| return regex_traits<char>{}.value(__c, __radix); | |
| } | |
| else | |
| { | |
| const char __c = static_cast<char>(__ch); | |
| const char __max_digit = __radix == 8 ? '7' : '9'; | |
| if ('0' <= __c && __c <= __max_digit) | |
| return __c - '0'; | |
| if (__radix < 16) | |
| return -1; | |
| switch (__c) | |
| { | |
| case 'a': | |
| case 'A': | |
| return 10; | |
| case 'b': | |
| case 'B': | |
| return 11; | |
| case 'c': | |
| case 'C': | |
| return 12; | |
| case 'd': | |
| case 'D': | |
| return 13; | |
| case 'e': | |
| case 'E': | |
| return 14; | |
| case 'f': | |
| case 'F': | |
| return 15; | |
| default: | |
| return -1; | |
| } | |
| } | |
| } | |
| #pragma GCC diagnostic pop | |
| template<typename _Bi_iter, typename _Alloc> | |
| template<typename _Out_iter> | |
| _Out_iter | |
| match_results<_Bi_iter, _Alloc>:: | |
| format(_Out_iter __out, | |
| const match_results<_Bi_iter, _Alloc>::char_type* __fmt_first, | |
| const match_results<_Bi_iter, _Alloc>::char_type* __fmt_last, | |
| match_flag_type __flags) const | |
| { | |
| __glibcxx_assert( ready() ); | |
| regex_traits<char_type> __traits; | |
| typedef std::ctype<char_type> __ctype_type; | |
| const __ctype_type& | |
| __fctyp(use_facet<__ctype_type>(__traits.getloc())); | |
| auto __output = [&](size_t __idx) | |
| { | |
| auto& __sub = (*this)[__idx]; | |
| if (__sub.matched) | |
| __out = std::copy(__sub.first, __sub.second, __out); | |
| }; | |
| if (__flags & regex_constants::format_sed) | |
| { | |
| bool __escaping = false; | |
| for (; __fmt_first != __fmt_last; __fmt_first++) | |
| { | |
| if (__escaping) | |
| { | |
| __escaping = false; | |
| if (__fctyp.is(__ctype_type::digit, *__fmt_first)) | |
| __output(__traits.value(*__fmt_first, 10)); | |
| else | |
| *__out++ = *__fmt_first; | |
| continue; | |
| } | |
| if (*__fmt_first == '\\') | |
| { | |
| __escaping = true; | |
| continue; | |
| } | |
| if (*__fmt_first == '&') | |
| { | |
| __output(0); | |
| continue; | |
| } | |
| *__out++ = *__fmt_first; | |
| } | |
| if (__escaping) | |
| *__out++ = '\\'; | |
| } | |
| else | |
| { | |
| while (1) | |
| { | |
| auto __next = std::find(__fmt_first, __fmt_last, '$'); | |
| if (__next == __fmt_last) | |
| break; | |
| __out = std::copy(__fmt_first, __next, __out); | |
| auto __eat = [&](char __ch) -> bool | |
| { | |
| if (*__next == __ch) | |
| { | |
| ++__next; | |
| return true; | |
| } | |
| return false; | |
| }; | |
| if (++__next == __fmt_last) | |
| *__out++ = '$'; | |
| else if (__eat('$')) | |
| *__out++ = '$'; | |
| else if (__eat('&')) | |
| __output(0); | |
| else if (__eat('`')) | |
| { | |
| auto& __sub = _M_prefix(); | |
| if (__sub.matched) | |
| __out = std::copy(__sub.first, __sub.second, __out); | |
| } | |
| else if (__eat('\'')) | |
| { | |
| auto& __sub = _M_suffix(); | |
| if (__sub.matched) | |
| __out = std::copy(__sub.first, __sub.second, __out); | |
| } | |
| else if (__fctyp.is(__ctype_type::digit, *__next)) | |
| { | |
| long __num = __traits.value(*__next, 10); | |
| if (++__next != __fmt_last | |
| && __fctyp.is(__ctype_type::digit, *__next)) | |
| { | |
| __num *= 10; | |
| __num += __traits.value(*__next++, 10); | |
| } | |
| if (0 <= __num && size_t(__num) < this->size()) | |
| __output(__num); | |
| } | |
| else | |
| *__out++ = '$'; | |
| __fmt_first = __next; | |
| } | |
| __out = std::copy(__fmt_first, __fmt_last, __out); | |
| } | |
| return __out; | |
| } | |
| template<typename _Out_iter, typename _Bi_iter, | |
| typename _Rx_traits, typename _Ch_type> | |
| _Out_iter | |
| __regex_replace(_Out_iter __out, _Bi_iter __first, _Bi_iter __last, | |
| const basic_regex<_Ch_type, _Rx_traits>& __e, | |
| const _Ch_type* __fmt, size_t __len, | |
| regex_constants::match_flag_type __flags) | |
| { | |
| typedef regex_iterator<_Bi_iter, _Ch_type, _Rx_traits> _IterT; | |
| _IterT __i(__first, __last, __e, __flags); | |
| _IterT __end; | |
| if (__i == __end) | |
| { | |
| if (!(__flags & regex_constants::format_no_copy)) | |
| __out = std::copy(__first, __last, __out); | |
| } | |
| else | |
| { | |
| sub_match<_Bi_iter> __last; | |
| for (; __i != __end; ++__i) | |
| { | |
| if (!(__flags & regex_constants::format_no_copy)) | |
| __out = std::copy(__i->prefix().first, __i->prefix().second, | |
| __out); | |
| __out = __i->format(__out, __fmt, __fmt + __len, __flags); | |
| __last = __i->suffix(); | |
| if (__flags & regex_constants::format_first_only) | |
| break; | |
| } | |
| if (!(__flags & regex_constants::format_no_copy)) | |
| __out = std::copy(__last.first, __last.second, __out); | |
| } | |
| return __out; | |
| } | |
| template<typename _Bi_iter, | |
| typename _Ch_type, | |
| typename _Rx_traits> | |
| bool | |
| regex_iterator<_Bi_iter, _Ch_type, _Rx_traits>:: | |
| operator==(const regex_iterator& __rhs) const noexcept | |
| { | |
| if (_M_pregex == nullptr && __rhs._M_pregex == nullptr) | |
| return true; | |
| return _M_pregex == __rhs._M_pregex | |
| && _M_begin == __rhs._M_begin | |
| && _M_end == __rhs._M_end | |
| && _M_flags == __rhs._M_flags | |
| && _M_match[0] == __rhs._M_match[0]; | |
| } | |
| template<typename _Bi_iter, | |
| typename _Ch_type, | |
| typename _Rx_traits> | |
| regex_iterator<_Bi_iter, _Ch_type, _Rx_traits>& | |
| regex_iterator<_Bi_iter, _Ch_type, _Rx_traits>:: | |
| operator++() | |
| { | |
| // In all cases in which the call to regex_search returns true, | |
| // match.prefix().first shall be equal to the previous value of | |
| // match[0].second, and for each index i in the half-open range | |
| // [0, match.size()) for which match[i].matched is true, | |
| // match[i].position() shall return distance(begin, match[i].first). | |
| // [28.12.1.4.5] | |
| if (_M_match[0].matched) | |
| { | |
| auto __start = _M_match[0].second; | |
| auto __prefix_first = _M_match[0].second; | |
| if (_M_match[0].first == _M_match[0].second) | |
| { | |
| if (__start == _M_end) | |
| { | |
| _M_pregex = nullptr; | |
| return *this; | |
| } | |
| else | |
| { | |
| if (regex_search(__start, _M_end, _M_match, *_M_pregex, | |
| _M_flags | |
| | regex_constants::match_not_null | |
| | regex_constants::match_continuous)) | |
| { | |
| __glibcxx_assert(_M_match[0].matched); | |
| auto& __prefix = _M_match._M_prefix(); | |
| __prefix.first = __prefix_first; | |
| __prefix.matched = __prefix.first != __prefix.second; | |
| // [28.12.1.4.5] | |
| _M_match._M_begin = _M_begin; | |
| return *this; | |
| } | |
| else | |
| ++__start; | |
| } | |
| } | |
| _M_flags |= regex_constants::match_prev_avail; | |
| if (regex_search(__start, _M_end, _M_match, *_M_pregex, _M_flags)) | |
| { | |
| __glibcxx_assert(_M_match[0].matched); | |
| auto& __prefix = _M_match._M_prefix(); | |
| __prefix.first = __prefix_first; | |
| __prefix.matched = __prefix.first != __prefix.second; | |
| // [28.12.1.4.5] | |
| _M_match._M_begin = _M_begin; | |
| } | |
| else | |
| _M_pregex = nullptr; | |
| } | |
| return *this; | |
| } | |
| template<typename _Bi_iter, | |
| typename _Ch_type, | |
| typename _Rx_traits> | |
| regex_token_iterator<_Bi_iter, _Ch_type, _Rx_traits>& | |
| regex_token_iterator<_Bi_iter, _Ch_type, _Rx_traits>:: | |
| operator=(const regex_token_iterator& __rhs) | |
| { | |
| _M_position = __rhs._M_position; | |
| _M_subs = __rhs._M_subs; | |
| _M_n = __rhs._M_n; | |
| _M_suffix = __rhs._M_suffix; | |
| _M_has_m1 = __rhs._M_has_m1; | |
| _M_normalize_result(); | |
| return *this; | |
| } | |
| template<typename _Bi_iter, | |
| typename _Ch_type, | |
| typename _Rx_traits> | |
| bool | |
| regex_token_iterator<_Bi_iter, _Ch_type, _Rx_traits>:: | |
| operator==(const regex_token_iterator& __rhs) const | |
| { | |
| if (_M_end_of_seq() && __rhs._M_end_of_seq()) | |
| return true; | |
| if (_M_suffix.matched && __rhs._M_suffix.matched | |
| && _M_suffix == __rhs._M_suffix) | |
| return true; | |
| if (_M_end_of_seq() || _M_suffix.matched | |
| || __rhs._M_end_of_seq() || __rhs._M_suffix.matched) | |
| return false; | |
| return _M_position == __rhs._M_position | |
| && _M_n == __rhs._M_n | |
| && _M_subs == __rhs._M_subs; | |
| } | |
| template<typename _Bi_iter, | |
| typename _Ch_type, | |
| typename _Rx_traits> | |
| regex_token_iterator<_Bi_iter, _Ch_type, _Rx_traits>& | |
| regex_token_iterator<_Bi_iter, _Ch_type, _Rx_traits>:: | |
| operator++() | |
| { | |
| _Position __prev = _M_position; | |
| if (_M_suffix.matched) | |
| *this = regex_token_iterator(); | |
| else if (_M_n + 1 < _M_subs.size()) | |
| { | |
| _M_n++; | |
| _M_result = &_M_current_match(); | |
| } | |
| else | |
| { | |
| _M_n = 0; | |
| ++_M_position; | |
| if (_M_position != _Position()) | |
| _M_result = &_M_current_match(); | |
| else if (_M_has_m1 && __prev->suffix().length() != 0) | |
| { | |
| _M_suffix.matched = true; | |
| _M_suffix.first = __prev->suffix().first; | |
| _M_suffix.second = __prev->suffix().second; | |
| _M_result = &_M_suffix; | |
| } | |
| else | |
| *this = regex_token_iterator(); | |
| } | |
| return *this; | |
| } | |
| template<typename _Bi_iter, | |
| typename _Ch_type, | |
| typename _Rx_traits> | |
| void | |
| regex_token_iterator<_Bi_iter, _Ch_type, _Rx_traits>:: | |
| _M_init(_Bi_iter __a, _Bi_iter __b) | |
| { | |
| _M_has_m1 = false; | |
| for (auto __it : _M_subs) | |
| if (__it == -1) | |
| { | |
| _M_has_m1 = true; | |
| break; | |
| } | |
| if (_M_position != _Position()) | |
| _M_result = &_M_current_match(); | |
| else if (_M_has_m1) | |
| { | |
| _M_suffix.matched = true; | |
| _M_suffix.first = __a; | |
| _M_suffix.second = __b; | |
| _M_result = &_M_suffix; | |
| } | |
| else | |
| _M_result = nullptr; | |
| } | |
| _GLIBCXX_END_NAMESPACE_VERSION | |
| } // namespace |