libstdc++
regex_executor.tcc
Go to the documentation of this file.
1// class template regex -*- C++ -*-
2
3// Copyright (C) 2013-2026 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library. This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
25/**
26 * @file bits/regex_executor.tcc
27 * This is an internal header file, included by other library headers.
28 * Do not attempt to use it directly. @headername{regex}
29 */
30
31namespace std _GLIBCXX_VISIBILITY(default)
32{
33_GLIBCXX_BEGIN_NAMESPACE_VERSION
34
35#pragma GCC diagnostic push
36#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
37namespace __detail
38{
39 template<typename _BiIter, typename _Alloc, typename _TraitsT>
40 bool _Executor<_BiIter, _Alloc, _TraitsT>::
41 _M_search()
42 {
43 if (_M_search_from_first())
44 return true;
46 return false;
48 while (_M_begin != _M_end)
49 {
50 ++_M_begin;
51 if (_M_search_from_first())
52 return true;
53 }
54 return false;
55 }
56
57 enum _ExecutorFrameOpcode : unsigned char
58 {
59 _S_fopcode_next,
60 _S_fopcode_fallback_next,
61 _S_fopcode_rep_once_more,
62 _S_fopcode_fallback_rep_once_more,
63 _S_fopcode_posix_alternative,
64 _S_fopcode_merge_sol,
65 _S_fopcode_restore_cur_results,
66 _S_fopcode_restore_rep_count,
67 _S_fopcode_decrement_rep_count,
68 };
69
70#pragma GCC diagnostic push
71#pragma GCC diagnostic ignored "-Wpedantic" // anon struct
72 struct _ExecutorFrameBase
73 {
74 _ExecutorFrameBase(_ExecutorFrameOpcode __op, _StateIdT __i)
75 : _M_op(__op), _M_state_id(__i)
76 { }
77
78 _ExecutorFrameOpcode _M_op;
79 union {
80 unsigned char _M_byte0 = 0;
81 struct { // Used by restore_rep_count frame
82 unsigned char _M_count : 2;
83 };
84 struct { // Used by restore_cur_results frame
85 unsigned char _M_subexpr_end : 1;
86 unsigned char _M_matched : 1;
87 };
88 };
89 unsigned char _M_bytes[6];
90 _StateIdT _M_state_id;
91 };
92#pragma GCC diagnostic pop
93
94 template<typename _BiIter, bool _Trivial /* = is_trivially_copyable<_BiIter>::value */>
95 struct _ExecutorFrame : _ExecutorFrameBase
96 {
97 _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i)
98 : _ExecutorFrameBase(__op, __i)
99 { }
100
101 _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i, _BiIter __p)
102 : _ExecutorFrameBase(__op, __i), _M_pos(__p)
103 { }
104
105 _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i, long __v)
106 : _ExecutorFrameBase(__op, __i), _M_val(__v)
107 { }
108
109 // _M_pos and _M_val are mutually exclusive, which the optimized
110 // partial specialization below depends on.
111 _BiIter _M_pos = _BiIter();
112 long _M_val = 0;
113 };
114
115 // Space-optimized partial specialization for when the input iterator is
116 // trivially copyable.
117 template<typename _BiIter>
118 struct _ExecutorFrame<_BiIter, true> : _ExecutorFrameBase
119 {
120 _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i)
121 : _ExecutorFrameBase(__op, __i)
122 { }
123
124 _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i, _BiIter __p)
125 : _ExecutorFrameBase(__op, __i), _M_pos(__p)
126 { }
127
128 _ExecutorFrame(_ExecutorFrameOpcode __op, _StateIdT __i, long __v)
129 : _ExecutorFrameBase(__op, __i), _M_val(__v)
130 { }
131
132 union {
133 _BiIter _M_pos;
134 long _M_val;
135 };
136 };
137
138 // The _M_main function operates in different modes, DFS mode or BFS mode,
139 // indicated by _M_search_mode, and dispatches to either _M_main_dfs or
140 // _M_main_bfs.
141 //
142 // ------------------------------------------------------------
143 //
144 // DFS mode:
145 //
146 // It applies a Depth-First-Search (aka backtracking) on given NFA and input
147 // string.
148 // At the very beginning the executor stands in the start state, then it
149 // tries every possible state transition in current state recursively. Some
150 // state transitions consume input string, say, a single-char-matcher or a
151 // back-reference matcher; some don't, like assertion or other anchor nodes.
152 // When the input is exhausted and/or the current state is an accepting
153 // state, the whole executor returns true.
154 //
155 // TODO: This approach is exponentially slow for certain input.
156 // Try to compile the NFA to a DFA.
157 //
158 // Time complexity: \Omega(match_length), O(2^(_M_nfa.size()))
159 // Space complexity: \theta(match_results.size() + match_length)
160 //
161 template<typename _BiIter, typename _Alloc, typename _TraitsT>
162 bool _Executor<_BiIter, _Alloc, _TraitsT>::
163 _M_main_dfs(_Match_mode __match_mode)
164 {
165 _M_has_sol = false;
166 *_M_get_sol_pos() = _BiIter();
167 _M_cur_results = _M_results;
168 _M_dfs<_Search_mode::_Dfs>(__match_mode, _M_start);
169 return _M_has_sol;
170 }
171
172 // Return whether a prefix search at _M_current might still match after
173 // looking only through the non-consuming front of the NFA.
174 //
175 // This is not a general implementation. It is deliberately small and
176 // conservative: when it reaches a construct whose first consuming character
177 // is hard to know cheaply, it returns true and lets the normal executor run.
178 // The important fast paths are the common negative cases.
179 //
180 // Examples:
181 // * Pattern "[0-9]+" at input 'x': the first _S_opcode_match rejects 'x',
182 // so a full DFS search would only allocate/pop frames to fail. Return
183 // false and let regex_search advance the starting position.
184 //
185 // * Pattern "[01]?[0-9]" at input '9': the optional [01] branch rejects,
186 // but the skip branch can consume '9'. Return true and let DFS decide
187 // the full match.
188 //
189 // * Pattern "foo|bar" at input 'b': one alternative rejects, the other can
190 // start with 'b'. Return true.
191 template<typename _BiIter, typename _Alloc, typename _TraitsT>
192 bool _Executor<_BiIter, _Alloc, _TraitsT>::
193 _M_maybe_start_match(_StateIdT __i, size_t __depth)
194 {
195 // Depth is bounded by the NFA size so epsilon cycles cannot make the
196 // precheck recurse forever. Hitting the bound means "unknown", not
197 // "no match", so stay conservative and run the real executor. This is
198 // important for patterns such as "(a*)*" where epsilon paths can cycle
199 // before a consuming state is reached.
200 if (__depth > _M_nfa.size())
201 return true;
202
203 // An invalid edge is a real dead end for the explored path.
204 if (__i == _S_invalid_state_id)
205 return false;
206
207 const auto& __state = _M_nfa[__i];
208 switch (__state._M_opcode())
209 {
210 case _S_opcode_match:
211 return __state._M_matches(*_M_current);
212
213 case _S_opcode_accept:
214 // Empty matches are possible, so the full executor must decide.
215 return true;
216
217 case _S_opcode_subexpr_begin:
218 case _S_opcode_subexpr_end:
219 case _S_opcode_dummy:
220 // Captures and dummy states do not consume input, so they cannot
221 // affect the first-character decision. Continue along the only
222 // successor.
223 return _M_maybe_start_match(__state._M_next, __depth + 1);
224
225 case _S_opcode_line_begin_assertion:
226 // Assertions do not consume characters, but they can reject the
227 // current position. For "^abc" at a non-begin position, there is no
228 // need to run DFS merely to discover that ^ fails.
229 return _M_at_begin()
230 && _M_maybe_start_match(__state._M_next, __depth + 1);
231
232 case _S_opcode_line_end_assertion:
233 // Same idea for "$": if the assertion does not hold here, this
234 // starting position cannot match via this path.
235 return _M_at_end()
236 && _M_maybe_start_match(__state._M_next, __depth + 1);
237
238 case _S_opcode_word_boundary:
239 // Word-boundary assertions are also checked before the first
240 // consuming state. For "\bfoo" in the middle of "xfoo", this path
241 // rejects before testing 'f'.
242 return _M_word_boundary() == !__state._M_neg
243 && _M_maybe_start_match(__state._M_next, __depth + 1);
244
245 case _S_opcode_alternative:
246 // A branch might match if either arm can start with *_M_current.
247 // Example: "foo|bar" at 'b' rejects the "foo" arm but keeps the
248 // search because the "bar" arm is viable.
249 return _M_maybe_start_match(__state._M_alt, __depth + 1)
250 || _M_maybe_start_match(__state._M_next, __depth + 1);
251
252 case _S_opcode_repeat:
253 // Repeats can either enter the body or skip to the exit, so inspect
254 // both paths. This matters for constructs such as "[01]?[0-9]": at
255 // '9' the optional first digit can be skipped, while at 'x' both
256 // paths reject.
257 return _M_maybe_start_match(__state._M_alt, __depth + 1)
258 || _M_maybe_start_match(__state._M_next, __depth + 1);
259
260 case _S_opcode_backref:
261 case _S_opcode_subexpr_lookahead:
262 default:
263 return true;
264 }
265 }
266
267 // ------------------------------------------------------------
268 //
269 // BFS mode:
270 //
271 // Russ Cox's article (http://swtch.com/~rsc/regexp/regexp1.html)
272 // explained this algorithm clearly.
273 //
274 // It first computes epsilon closure (states that can be achieved without
275 // consuming characters) for every state that's still matching,
276 // using the same DFS algorithm, but doesn't re-enter states (using
277 // _M_visited to check), nor follow _S_opcode_match.
278 //
279 // Then apply DFS using every _S_opcode_match (in _M_match_queue)
280 // as the start state.
281 //
282 // It significantly reduces potential duplicate states, so has a better
283 // upper bound; but it requires more overhead.
284 //
285 // Time complexity: \Omega(match_length * match_results.size())
286 // O(match_length * _M_nfa.size() * match_results.size())
287 // Space complexity: \Omega(_M_nfa.size() + match_results.size())
288 // O(_M_nfa.size() * match_results.size())
289 template<typename _BiIter, typename _Alloc, typename _TraitsT>
290 bool _Executor<_BiIter, _Alloc, _TraitsT>::
291 _M_main_bfs(_Match_mode __match_mode)
292 {
293 _M_match_queue.emplace_back(_M_start, _M_results);
294 bool __ret = false;
295 while (1)
296 {
297 _M_has_sol = false;
298 if (_M_match_queue.empty())
299 break;
300 std::fill_n(_M_visited_states, _M_nfa.size(), false);
301 auto __old_queue = std::move(_M_match_queue);
302 auto __alloc = _M_cur_results.get_allocator();
303 for (auto& __task : __old_queue)
304 {
305 _M_cur_results = _ResultsVec(std::move(__task.second), __alloc);
306 _M_dfs<_Search_mode::_Bfs>(__match_mode, __task.first);
307 }
308 if (__match_mode == _Match_mode::_Prefix)
309 __ret |= _M_has_sol;
310 if (_M_current == _M_end)
311 break;
312 ++_M_current;
313 }
314 if (__match_mode == _Match_mode::_Exact)
315 __ret = _M_has_sol;
316 _M_match_queue.clear();
317 return __ret;
318 }
319
320 // Return whether now match the given sub-NFA.
321 template<typename _BiIter, typename _Alloc, typename _TraitsT>
322 bool _Executor<_BiIter, _Alloc, _TraitsT>::
323 _M_lookahead(_StateIdT __next)
324 {
325 // Backreferences may refer to captured content.
326 // We may want to make this faster by not copying,
327 // but let's not be clever prematurely.
328 _ResultsVec __what(_M_cur_results);
329 _Executor __sub(_M_current, _M_end, __what, _M_re, _M_flags,
330 bool(_M_search_mode));
331 __sub._M_start = __next;
332 if (__sub._M_search_from_first())
333 {
334 for (size_t __i = 0; __i < __what.size(); __i++)
335 if (__what[__i].matched)
336 _M_cur_results[__i] = __what[__i];
337 return true;
338 }
339 return false;
340 }
341
342 // __rep_count records how many times (__rep_count.second)
343 // this node is visited under certain input iterator
344 // (__rep_count.first). This prevent the executor from entering
345 // infinite loop by refusing to continue when it's already been
346 // visited more than twice. It's `twice` instead of `once` because
347 // we need to spare one more time for potential group capture.
348 //
349 // If the node cannot be re-entered anymore from the current state then return
350 // _S_invalid_state_id otherwise return the current state without going
351 // through a vector, allowing the caller to decide what to do with the state
352 // This is beneficial for DFS since DFS can continue with the next state
353 // immediately
354 template<typename _BiIter, typename _Alloc, typename _TraitsT>
355 _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
356 _M_rep_once_more(_Match_mode, _StateIdT __i)
357 {
358 const auto& __state = _M_nfa[__i];
359 auto& __rep_count = _M_rep_count[__i];
360 if (__rep_count.second == 0 || __rep_count.first != _M_current)
361 {
362 _M_frames.emplace_back(_S_fopcode_restore_rep_count,
363 __i, __rep_count.first);
364 _M_frames.back()._M_count = __rep_count.second;
365 __rep_count.first = _M_current;
366 __rep_count.second = 1;
367 return __state._M_alt;
368 }
369 else
370 {
371 if (__rep_count.second < 2)
372 {
373 __rep_count.second++;
374 _M_frames.emplace_back(_S_fopcode_decrement_rep_count, __i);
375 return __state._M_alt;
376 }
377 }
378 return _S_invalid_state_id;
379 }
380
381 // Try to consume the common repeat body shape
382 // repeat -> match -> repeat
383 // without going through the generic state dispatch again.
384 template<typename _BiIter, typename _Alloc, typename _TraitsT>
385#ifdef __OPTIMIZE__
386 [[__gnu__::__always_inline__]]
387#endif
388 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
389 _M_match_simple_repeat_body(_StateIdT __next, _StateIdT __repeat)
390 {
391 if (__next == _S_invalid_state_id)
392 return _S_invalid_state_id;
393
394 const auto& __state = _M_nfa[__next];
395 if (__state._M_opcode() != _S_opcode_match
396 || __state._M_next != __repeat)
397 return __next;
398
399 if (_M_current == _M_end || !__state._M_matches(*_M_current))
400 return _S_invalid_state_id;
401
402 ++_M_current;
403 return __repeat;
404 }
405
406 // _M_alt branch is "match once more", while _M_next is "get me out
407 // of this quantifier". Executing _M_next first or _M_alt first don't
408 // mean the same thing, and we need to choose the correct order under
409 // given greedy mode.
410 template<typename _BiIter, typename _Alloc, typename _TraitsT>
411 template<_Search_mode __search_mode>
412#ifdef __OPTIMIZE__
413 [[__gnu__::__always_inline__]]
414#endif
415 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
416 _M_handle_repeat(_Match_mode __match_mode, _StateIdT __i)
417 {
418 const auto& __state = _M_nfa[__i];
419 // Greedy.
420 if (!__state._M_neg)
421 {
422 if constexpr (__search_mode == _Search_mode::_Dfs)
423 // If it's DFS executor and already accepted, we're done.
424 _M_frames.emplace_back(_S_fopcode_fallback_next, __state._M_next,
425 _M_current);
426 else
427 _M_frames.emplace_back(_S_fopcode_next, __state._M_next);
428 _StateIdT __next = _M_rep_once_more(__match_mode, __i);
429 if constexpr (__search_mode == _Search_mode::_Dfs)
430 return _M_match_simple_repeat_body(__next, __i);
431 else
432 return __next;
433 }
434 else // Non-greedy mode
435 {
436 if constexpr (__search_mode == _Search_mode::_Dfs)
437 {
438 // vice-versa.
439 _M_frames.emplace_back(_S_fopcode_fallback_rep_once_more, __i,
440 _M_current);
441 return __state._M_next;
442 }
443 else
444 {
445 // DON'T attempt anything, because there's already another
446 // state with higher priority accepted. This state cannot
447 // be better by attempting its next node.
448 if (!_M_has_sol)
449 {
450 // DON'T attempt anything if it's already accepted. An
451 // accepted state *must* be better than a solution that
452 // matches a non-greedy quantifier one more time.
453 _M_frames.emplace_back(_S_fopcode_fallback_rep_once_more, __i);
454 return __state._M_next;
455 }
456 }
457 }
458 return _S_invalid_state_id;
459 }
460
461 template<typename _BiIter, typename _Alloc, typename _TraitsT>
462 template<_Search_mode __search_mode>
463#ifdef __OPTIMIZE__
464 [[__gnu__::__always_inline__]]
465#endif
466 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
467 _M_handle_subexpr_begin(_Match_mode, _StateIdT __i)
468 {
469 const auto& __state = _M_nfa[__i];
470 auto& __res = _M_cur_results[__state._M_subexpr];
471 if (_M_nfa._M_has_backref
472 || __state._M_subexpr != 0
473 || __search_mode != _Search_mode::_Dfs)
474 _M_frames.emplace_back(_S_fopcode_restore_cur_results,
475 static_cast<_StateIdT>(__state._M_subexpr),
476 __res.first);
477 __res.first = _M_current;
478 return __state._M_next;
479 }
480
481 template<typename _BiIter, typename _Alloc, typename _TraitsT>
482 template<_Search_mode __search_mode>
483#ifdef __OPTIMIZE__
484 [[__gnu__::__always_inline__]]
485#endif
486 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
487 _M_handle_subexpr_end(_Match_mode, _StateIdT __i)
488 {
489 const auto& __state = _M_nfa[__i];
490 auto& __res = _M_cur_results[__state._M_subexpr];
491 if (_M_nfa._M_has_backref
492 || __state._M_subexpr != 0
493 || __search_mode != _Search_mode::_Dfs)
494 {
495 _M_frames.emplace_back(_S_fopcode_restore_cur_results,
496 static_cast<_StateIdT>(__state._M_subexpr),
497 __res.second);
498 _M_frames.back()._M_subexpr_end = true;
499 _M_frames.back()._M_matched = __res.matched;
500 }
501
502 __res.second = _M_current;
503 __res.matched = true;
504 return __state._M_next;
505 }
506
507 template<typename _BiIter, typename _Alloc, typename _TraitsT>
508 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
509 _M_handle_line_begin_assertion(_Match_mode, _StateIdT __i)
510 {
511 const auto& __state = _M_nfa[__i];
512 if (_M_at_begin())
513 return __state._M_next;
514 return _S_invalid_state_id;
515 }
516
517 template<typename _BiIter, typename _Alloc, typename _TraitsT>
518 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
519 _M_handle_line_end_assertion(_Match_mode, _StateIdT __i)
520 {
521 const auto& __state = _M_nfa[__i];
522 if (_M_at_end())
523 return __state._M_next;
524 return _S_invalid_state_id;
525 }
526
527 template<typename _BiIter, typename _Alloc, typename _TraitsT>
528 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
529 _M_handle_word_boundary(_Match_mode, _StateIdT __i)
530 {
531 const auto& __state = _M_nfa[__i];
532 if (_M_word_boundary() == !__state._M_neg)
533 return __state._M_next;
534 return _S_invalid_state_id;
535 }
536
537 // Here __state._M_alt offers a single start node for a sub-NFA.
538 // We recursively invoke our algorithm to match the sub-NFA.
539 template<typename _BiIter, typename _Alloc, typename _TraitsT>
540 _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
541 _M_handle_subexpr_lookahead(_Match_mode, _StateIdT __i)
542 {
543 const auto& __state = _M_nfa[__i];
544 if (_M_lookahead(__state._M_alt) == !__state._M_neg)
545 return __state._M_next;
546 return _S_invalid_state_id;
547 }
548
549 template<typename _BiIter, typename _Alloc, typename _TraitsT>
550 template<_Search_mode __search_mode>
551#ifdef __OPTIMIZE__
552 [[__gnu__::__always_inline__]]
553#endif
554 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
555 _M_handle_match(_Match_mode, _StateIdT __i)
556 {
557 const auto& __state = _M_nfa[__i];
558 if (_M_current == _M_end)
559 return _S_invalid_state_id;
560 if constexpr (__search_mode == _Search_mode::_Dfs)
561 {
562 if (__state._M_matches(*_M_current))
563 {
564 ++_M_current;
565 return __state._M_next;
566 }
567 }
568 else
569 if (__state._M_matches(*_M_current))
570 _M_match_queue.emplace_back(__state._M_next, _M_cur_results);
571
572 return _S_invalid_state_id;
573 }
574
575 template<typename _BiIter, typename _TraitsT>
576 struct _Backref_matcher
577 {
578 _Backref_matcher(bool /* __icase */, const _TraitsT& __traits)
579 : _M_traits(__traits) { }
580
581 bool
582 _M_apply(_BiIter __expected_begin,
583 _BiIter __expected_end, _BiIter __actual_begin,
584 _BiIter __actual_end)
585 {
586 return _M_traits.transform(__expected_begin, __expected_end)
587 == _M_traits.transform(__actual_begin, __actual_end);
588 }
589
590 const _TraitsT& _M_traits;
591 };
592
593 template<typename _BiIter, typename _CharT>
594 struct _Backref_matcher<_BiIter, std::regex_traits<_CharT>>
595 {
596 using _TraitsT = std::regex_traits<_CharT>;
597 _Backref_matcher(bool __icase, const _TraitsT& __traits)
598 : _M_icase(__icase), _M_traits(__traits) { }
599
600 bool
601 _M_apply(_BiIter __expected_begin,
602 _BiIter __expected_end, _BiIter __actual_begin,
603 _BiIter __actual_end)
604 {
605 if (!_M_icase)
606 return _GLIBCXX_STD_A::__equal4(__expected_begin, __expected_end,
607 __actual_begin, __actual_end);
608 typedef std::ctype<_CharT> __ctype_type;
609 const auto& __fctyp = use_facet<__ctype_type>(_M_traits.getloc());
610 return _GLIBCXX_STD_A::__equal4(__expected_begin, __expected_end,
611 __actual_begin, __actual_end,
612 [this, &__fctyp](_CharT __lhs, _CharT __rhs)
613 {
614 return __fctyp.tolower(__lhs)
615 == __fctyp.tolower(__rhs);
616 });
617 }
618
619 bool _M_icase;
620 const _TraitsT& _M_traits;
621 };
622
623 // First fetch the matched result from _M_cur_results as __submatch;
624 // then compare it with
625 // (_M_current, _M_current + (__submatch.second - __submatch.first)).
626 // If matched, keep going; else just return and try another state.
627 template<typename _BiIter, typename _Alloc, typename _TraitsT>
628 _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
629 _M_handle_backref(_Match_mode, _StateIdT __i)
630 {
631 __glibcxx_assert(_M_search_mode == _Search_mode::_Dfs);
632
633 const auto& __state = _M_nfa[__i];
634 auto& __submatch = _M_cur_results[__state._M_backref_index];
635 if (!__submatch.matched)
636 return _S_invalid_state_id;
637 auto __last = _M_current;
638 for (auto __tmp = __submatch.first;
639 __last != _M_end && __tmp != __submatch.second;
640 ++__tmp)
641 ++__last;
642 if (_Backref_matcher<_BiIter, _TraitsT>(
643 _M_re.flags() & regex_constants::icase,
644 _M_re._M_automaton->_M_traits)._M_apply(
645 __submatch.first, __submatch.second, _M_current, __last))
646 {
647 _M_current = __last;
648 return __state._M_next;
649 }
650
651 return _S_invalid_state_id;
652 }
653
654 template<typename _BiIter, typename _Alloc, typename _TraitsT>
655 template<_Search_mode __search_mode>
656#ifdef __OPTIMIZE__
657 [[__gnu__::__always_inline__]]
658#endif
659 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
660 _M_handle_accept(_Match_mode __match_mode, _StateIdT)
661 {
662 if constexpr (__search_mode == _Search_mode::_Dfs)
663 {
664 __glibcxx_assert(!_M_has_sol);
665 if (__match_mode == _Match_mode::_Exact)
666 _M_has_sol = _M_current == _M_end;
667 else
668 _M_has_sol = true;
669 if (_M_current == _M_begin
670 && (_M_flags & regex_constants::match_not_null))
671 _M_has_sol = false;
672 if (_M_has_sol)
673 {
674 if (_M_nfa._M_flags & regex_constants::ECMAScript)
675 _M_results = _M_cur_results;
676 else // POSIX
677 {
678 __glibcxx_assert(_M_get_sol_pos());
679 // Here's POSIX's logic: match the longest one. However
680 // we never know which one (lhs or rhs of "|") is longer
681 // unless we try both of them and compare the results.
682 // The member variable _M_sol_pos records the end
683 // position of the last successful match. It's better
684 // to be larger, because POSIX regex is always greedy.
685 // TODO: This could be slow.
686 if (*_M_get_sol_pos() == _BiIter()
687 || std::distance(_M_begin, *_M_get_sol_pos())
688 < std::distance(_M_begin, _M_current))
689 {
690 *_M_get_sol_pos() = _M_current;
691 _M_results = _M_cur_results;
692 }
693 }
694 }
695 }
696 else
697 {
698 if (_M_current == _M_begin
699 && (_M_flags & regex_constants::match_not_null))
700 return _S_invalid_state_id;
701 if (__match_mode == _Match_mode::_Prefix || _M_current == _M_end)
702 if (!_M_has_sol)
703 {
704 _M_has_sol = true;
705 _M_results = _M_cur_results;
706 }
707 }
708 return _S_invalid_state_id;
709 }
710
711 template<typename _BiIter, typename _Alloc, typename _TraitsT>
712#ifdef __OPTIMIZE__
713 [[__gnu__::__always_inline__]]
714#endif
715 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
716 _M_handle_alternative(_Match_mode, _StateIdT __i)
717 {
718 const auto& __state = _M_nfa[__i];
719 if (_M_nfa._M_flags & regex_constants::ECMAScript)
720 {
721 // TODO: Fix BFS support. It is wrong.
722 // Pick lhs if it matches. Only try rhs if it doesn't.
723 _M_frames.emplace_back(_S_fopcode_fallback_next, __state._M_next,
724 _M_current);
725 return __state._M_alt;
726 }
727 else
728 {
729 // Try both and compare the result.
730 // See "case _S_opcode_accept:" handling above.
731 _M_frames.emplace_back(_S_fopcode_posix_alternative, __state._M_next,
732 _M_current);
733 return __state._M_alt;
734 }
735 }
736
737 template<typename _BiIter, typename _Alloc, typename _TraitsT>
738 template<_Search_mode __search_mode>
739#ifdef __OPTIMIZE__
740 [[__gnu__::__always_inline__]]
741#endif
742 inline _StateIdT _Executor<_BiIter, _Alloc, _TraitsT>::
743 _M_node(_Match_mode __match_mode, _StateIdT __i)
744 {
745 // DFS has no _M_visited implementation as such don't even have the branch
746 // or the check in the call graph.
747 if constexpr (__search_mode == _Search_mode::_Bfs)
748 if (_M_visited(__i))
749 return _S_invalid_state_id;
750
751 _StateIdT __next = _S_invalid_state_id;
752 switch (_M_nfa[__i]._M_opcode())
753 {
754 case _S_opcode_repeat:
755 __next = _M_handle_repeat<__search_mode>(__match_mode, __i); break;
756 case _S_opcode_subexpr_begin:
757 __next = _M_handle_subexpr_begin<__search_mode>(__match_mode, __i);
758 break;
759 case _S_opcode_subexpr_end:
760 __next = _M_handle_subexpr_end<__search_mode>(__match_mode, __i);
761 break;
762 case _S_opcode_line_begin_assertion:
763 __next = _M_handle_line_begin_assertion(__match_mode, __i); break;
764 case _S_opcode_line_end_assertion:
765 __next = _M_handle_line_end_assertion(__match_mode, __i); break;
766 case _S_opcode_word_boundary:
767 __next = _M_handle_word_boundary(__match_mode, __i); break;
768 case _S_opcode_subexpr_lookahead:
769 __next = _M_handle_subexpr_lookahead(__match_mode, __i); break;
770 case _S_opcode_match:
771 __next = _M_handle_match<__search_mode>(__match_mode, __i); break;
772 case _S_opcode_backref:
773 if constexpr (__search_mode == _Search_mode::_Dfs)
774 __next = _M_handle_backref(__match_mode, __i);
775 else
776 __builtin_unreachable();
777 break;
778 case _S_opcode_accept:
779 __next = _M_handle_accept<__search_mode>(__match_mode, __i); break;
780 case _S_opcode_alternative:
781 __next = _M_handle_alternative(__match_mode, __i); break;
782 default:
783 __glibcxx_assert(false);
784 }
785 return __next;
786 }
787
788 template<typename _BiIter, typename _Alloc, typename _TraitsT>
789 template<_Search_mode __search_mode>
790 void _Executor<_BiIter, _Alloc, _TraitsT>::
791 _M_dfs(_Match_mode __match_mode, _StateIdT __start)
792 {
793 _StateIdT __next = __start;
794
795 while (true)
796 {
797 // Follow immediate successors without re-entering the frame
798 // loop until we fail. This avoids the needless state save and
799 // restore through memory.
800 while (__next != _S_invalid_state_id)
801 __next = _M_node<__search_mode>(__match_mode, __next);
802
803 if (_M_frames.empty())
804 break;
805
806 _ExecutorFrame<_BiIter> __frame = std::move(_M_frames.back());
807 _M_frames.pop_back();
808
809 switch (__frame._M_op)
810 {
811 case _S_fopcode_fallback_next:
812 if (_M_has_sol)
813 break;
814 if constexpr (__search_mode == _Search_mode::_Dfs)
815 _M_current = __frame._M_pos;
816 [[__fallthrough__]];
817 case _S_fopcode_next:
818 __next = __frame._M_state_id;
819 break;
820
821 case _S_fopcode_fallback_rep_once_more:
822 if (_M_has_sol)
823 break;
824 if constexpr (__search_mode == _Search_mode::_Dfs)
825 _M_current = __frame._M_pos;
826 [[__fallthrough__]];
827 case _S_fopcode_rep_once_more:
828 __next = _M_rep_once_more(__match_mode, __frame._M_state_id);
829 break;
830
831 case _S_fopcode_posix_alternative:
832 _M_frames.emplace_back(_S_fopcode_merge_sol, 0, _M_has_sol);
833 __next = __frame._M_state_id;
834 if constexpr (__search_mode == _Search_mode::_Dfs)
835 _M_current = __frame._M_pos;
836 _M_has_sol = false;
837 break;
838
839 case _S_fopcode_merge_sol:
840 _M_has_sol |= __frame._M_val;
841 break;
842
843 case _S_fopcode_restore_cur_results:
844 if (!__frame._M_subexpr_end)
845 _M_cur_results[__frame._M_state_id].first = __frame._M_pos;
846 else
847 {
848 _M_cur_results[__frame._M_state_id].second = __frame._M_pos;
849 _M_cur_results[__frame._M_state_id].matched = __frame._M_matched;
850 }
851 break;
852
853 case _S_fopcode_restore_rep_count:
854 _M_rep_count[__frame._M_state_id].first = __frame._M_pos;
855 _M_rep_count[__frame._M_state_id].second = __frame._M_count;
856 break;
857
858 case _S_fopcode_decrement_rep_count:
859 _M_rep_count[__frame._M_state_id].second--;
860 break;
861 }
862 }
863 }
864
865 // Return whether now is at some word boundary.
866 template<typename _BiIter, typename _Alloc, typename _TraitsT>
867 bool _Executor<_BiIter, _Alloc, _TraitsT>::
868 _M_word_boundary() const
869 {
870 if (_M_current == _M_begin && (_M_flags & regex_constants::match_not_bow))
871 return false;
872 if (_M_current == _M_end && (_M_flags & regex_constants::match_not_eow))
873 return false;
874
875 bool __left_is_word = false;
876 if (_M_current != _M_begin
877 || (_M_flags & regex_constants::match_prev_avail))
878 {
879 auto __prev = _M_current;
880 if (_M_is_word(*std::prev(__prev)))
881 __left_is_word = true;
882 }
883 bool __right_is_word =
884 _M_current != _M_end && _M_is_word(*_M_current);
885
886 return __left_is_word != __right_is_word;
887 }
888} // namespace __detail
889#pragma GCC diagnostic pop
890
891_GLIBCXX_END_NAMESPACE_VERSION
892} // namespace
constexpr std::remove_reference< _Tp >::type && move(_Tp &&__t) noexcept
Convert a value to an rvalue.
Definition move.h:138
const _Facet & use_facet(const locale &__loc)
Return a facet.
ISO C++ entities toplevel namespace is std.
constexpr iterator_traits< _InputIterator >::difference_type distance(_InputIterator __first, _InputIterator __last)
A generalization of pointer arithmetic.
Implementation details not part of the namespace std interface.
constexpr match_flag_type match_not_bow
constexpr syntax_option_type ECMAScript
constexpr match_flag_type match_continuous
constexpr syntax_option_type icase
constexpr match_flag_type match_prev_avail
constexpr match_flag_type match_not_eow
constexpr match_flag_type match_not_null