6 * Use, modification and distribution are subject to the
7 * Boost Software License, Version 1.0. (See accompanying file
8 * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
13 * LOCATION: see http://www.boost.org for most recent version.
14 * FILE perl_matcher_common.cpp
15 * VERSION see <boost/version.hpp>
16 * DESCRIPTION: Definitions of perl_matcher member functions that are
17 * common to both the recursive and non-recursive versions.
20 #ifndef BOOST_REGEX_V4_PERL_MATCHER_COMMON_HPP
21 #define BOOST_REGEX_V4_PERL_MATCHER_COMMON_HPP
25 #pragma warning(disable: 4103)
27 #ifdef BOOST_HAS_ABI_HEADERS
28 # include BOOST_ABI_PREFIX
35 # pragma option push -w-8008 -w-8066
38 # pragma warning(push)
39 # pragma warning(disable: 4800)
43 namespace BOOST_REGEX_DETAIL_NS{
45 template <class BidiIterator, class Allocator, class traits>
46 void perl_matcher<BidiIterator, Allocator, traits>::construct_init(const basic_regex<char_type, traits>& e, match_flag_type f)
48 typedef typename regex_iterator_traits<BidiIterator>::iterator_category category;
49 typedef typename basic_regex<char_type, traits>::flag_type expression_flag_type;
53 // precondition failure: e is not a valid regex.
54 std::invalid_argument ex("Invalid regular expression object");
55 boost::throw_exception(ex);
59 estimate_max_state_count(static_cast<category*>(0));
60 expression_flag_type re_f = re.flags();
61 icase = re_f & regex_constants::icase;
62 if(!(m_match_flags & (match_perl|match_posix)))
64 if((re_f & (regbase::main_option_type|regbase::no_perl_ex)) == 0)
65 m_match_flags |= match_perl;
66 else if((re_f & (regbase::main_option_type|regbase::emacs_ex)) == (regbase::basic_syntax_group|regbase::emacs_ex))
67 m_match_flags |= match_perl;
68 else if((re_f & (regbase::main_option_type|regbase::literal)) == (regbase::literal))
69 m_match_flags |= match_perl;
71 m_match_flags |= match_posix;
73 if(m_match_flags & match_posix)
75 m_temp_match.reset(new match_results<BidiIterator, Allocator>());
76 m_presult = m_temp_match.get();
79 m_presult = &m_result;
80 #ifdef BOOST_REGEX_NON_RECURSIVE
83 #elif defined(BOOST_REGEX_RECURSIVE)
84 m_can_backtrack = true;
85 m_have_accept = false;
87 // find the value to use for matching word boundaries:
88 m_word_mask = re.get_data().m_word_mask;
89 // find bitmask to use for matching '.':
90 match_any_mask = static_cast<unsigned char>((f & match_not_dot_newline) ? BOOST_REGEX_DETAIL_NS::test_not_newline : BOOST_REGEX_DETAIL_NS::test_newline);
91 // Disable match_any if requested in the state machine:
92 if(e.get_data().m_disable_match_any)
93 m_match_flags &= ~regex_constants::match_any;
96 template <class BidiIterator, class Allocator, class traits>
97 void perl_matcher<BidiIterator, Allocator, traits>::estimate_max_state_count(std::random_access_iterator_tag*)
100 // How many states should we allow our machine to visit before giving up?
101 // This is a heuristic: it takes the greater of O(N^2) and O(NS^2)
102 // where N is the length of the string, and S is the number of states
103 // in the machine. It's tempting to up this to O(N^2S) or even O(N^2S^2)
104 // but these take unreasonably amounts of time to bale out in pathological
107 // Calculate NS^2 first:
109 static const std::ptrdiff_t k = 100000;
110 std::ptrdiff_t dist = boost::BOOST_REGEX_DETAIL_NS::distance(base, last);
113 std::ptrdiff_t states = re.size();
117 if((std::numeric_limits<std::ptrdiff_t>::max)() / dist < states)
119 max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);
123 if((std::numeric_limits<std::ptrdiff_t>::max)() - k < states)
125 max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);
130 max_state_count = states;
133 // Now calculate N^2:
136 if((std::numeric_limits<std::ptrdiff_t>::max)() / dist < states)
138 max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);
142 if((std::numeric_limits<std::ptrdiff_t>::max)() - k < states)
144 max_state_count = (std::min)((std::ptrdiff_t)BOOST_REGEX_MAX_STATE_COUNT, (std::numeric_limits<std::ptrdiff_t>::max)() - 2);
149 // N^2 can be a very large number indeed, to prevent things getting out
150 // of control, cap the max states:
152 if(states > BOOST_REGEX_MAX_STATE_COUNT)
153 states = BOOST_REGEX_MAX_STATE_COUNT;
155 // If (the possibly capped) N^2 is larger than our first estimate,
158 if(states > max_state_count)
159 max_state_count = states;
162 template <class BidiIterator, class Allocator, class traits>
163 inline void perl_matcher<BidiIterator, Allocator, traits>::estimate_max_state_count(void*)
165 // we don't know how long the sequence is:
166 max_state_count = BOOST_REGEX_MAX_STATE_COUNT;
169 #ifdef BOOST_REGEX_HAS_MS_STACK_GUARD
170 template <class BidiIterator, class Allocator, class traits>
171 inline bool perl_matcher<BidiIterator, Allocator, traits>::protected_call(
172 protected_proc_type proc)
174 ::boost::BOOST_REGEX_DETAIL_NS::concrete_protected_call
175 <perl_matcher<BidiIterator, Allocator, traits> >
177 return obj.execute();
182 template <class BidiIterator, class Allocator, class traits>
183 inline bool perl_matcher<BidiIterator, Allocator, traits>::match()
185 #ifdef BOOST_REGEX_HAS_MS_STACK_GUARD
186 return protected_call(&perl_matcher<BidiIterator, Allocator, traits>::match_imp);
192 template <class BidiIterator, class Allocator, class traits>
193 bool perl_matcher<BidiIterator, Allocator, traits>::match_imp()
195 // initialise our stack if we are non-recursive:
196 #ifdef BOOST_REGEX_NON_RECURSIVE
197 save_state_init init(&m_stack_base, &m_backup_state);
198 used_block_count = BOOST_REGEX_MAX_BLOCKS;
199 #if !defined(BOOST_NO_EXCEPTIONS)
204 // reset our state machine:
208 m_match_flags |= regex_constants::match_all;
209 m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), search_base, last);
210 m_presult->set_base(base);
211 m_presult->set_named_subs(this->re.get_named_subs());
212 if(m_match_flags & match_posix)
213 m_result = *m_presult;
214 verify_options(re.flags(), m_match_flags);
215 if(0 == match_prefix())
217 return (m_result[0].second == last) && (m_result[0].first == base);
219 #if defined(BOOST_REGEX_NON_RECURSIVE) && !defined(BOOST_NO_EXCEPTIONS)
223 // unwind all pushed states, apart from anything else this
224 // ensures that all the states are correctly destructed
225 // not just the memory freed.
226 while(unwind(true)){}
232 template <class BidiIterator, class Allocator, class traits>
233 inline bool perl_matcher<BidiIterator, Allocator, traits>::find()
235 #ifdef BOOST_REGEX_HAS_MS_STACK_GUARD
236 return protected_call(&perl_matcher<BidiIterator, Allocator, traits>::find_imp);
242 template <class BidiIterator, class Allocator, class traits>
243 bool perl_matcher<BidiIterator, Allocator, traits>::find_imp()
245 static matcher_proc_type const s_find_vtable[7] =
247 &perl_matcher<BidiIterator, Allocator, traits>::find_restart_any,
248 &perl_matcher<BidiIterator, Allocator, traits>::find_restart_word,
249 &perl_matcher<BidiIterator, Allocator, traits>::find_restart_line,
250 &perl_matcher<BidiIterator, Allocator, traits>::find_restart_buf,
251 &perl_matcher<BidiIterator, Allocator, traits>::match_prefix,
252 &perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit,
253 &perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit,
256 // initialise our stack if we are non-recursive:
257 #ifdef BOOST_REGEX_NON_RECURSIVE
258 save_state_init init(&m_stack_base, &m_backup_state);
259 used_block_count = BOOST_REGEX_MAX_BLOCKS;
260 #if !defined(BOOST_NO_EXCEPTIONS)
266 if((m_match_flags & regex_constants::match_init) == 0)
268 // reset our state machine:
269 search_base = position = base;
270 pstate = re.get_first_state();
271 m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), base, last);
272 m_presult->set_base(base);
273 m_presult->set_named_subs(this->re.get_named_subs());
274 m_match_flags |= regex_constants::match_init;
279 search_base = position = m_result[0].second;
280 // If last match was null and match_not_null was not set then increment
281 // our start position, otherwise we go into an infinite loop:
282 if(((m_match_flags & match_not_null) == 0) && (m_result.length() == 0))
290 m_presult->set_size((m_match_flags & match_nosubs) ? 1u : static_cast<typename results_type::size_type>(1u + re.mark_count()), search_base, last);
291 //if((base != search_base) && (base == backstop))
292 // m_match_flags |= match_prev_avail;
294 if(m_match_flags & match_posix)
296 m_result.set_size(static_cast<typename results_type::size_type>(1u + re.mark_count()), base, last);
297 m_result.set_base(base);
300 verify_options(re.flags(), m_match_flags);
301 // find out what kind of expression we have:
302 unsigned type = (m_match_flags & match_continuous) ?
303 static_cast<unsigned int>(regbase::restart_continue)
304 : static_cast<unsigned int>(re.get_restart_type());
306 // call the appropriate search routine:
307 matcher_proc_type proc = s_find_vtable[type];
308 return (this->*proc)();
310 #if defined(BOOST_REGEX_NON_RECURSIVE) && !defined(BOOST_NO_EXCEPTIONS)
314 // unwind all pushed states, apart from anything else this
315 // ensures that all the states are correctly destructed
316 // not just the memory freed.
317 while(unwind(true)){}
323 template <class BidiIterator, class Allocator, class traits>
324 bool perl_matcher<BidiIterator, Allocator, traits>::match_prefix()
326 m_has_partial_match = false;
327 m_has_found_match = false;
328 pstate = re.get_first_state();
329 m_presult->set_first(position);
332 if(!m_has_found_match && m_has_partial_match && (m_match_flags & match_partial))
334 m_has_found_match = true;
335 m_presult->set_second(last, 0, false);
337 if((m_match_flags & match_posix) == match_posix)
339 m_result.maybe_assign(*m_presult);
342 #ifdef BOOST_REGEX_MATCH_EXTRA
343 if(m_has_found_match && (match_extra & m_match_flags))
346 // we have a match, reverse the capture information:
348 for(unsigned i = 0; i < m_presult->size(); ++i)
350 typename sub_match<BidiIterator>::capture_sequence_type & seq = ((*m_presult)[i]).get_captures();
351 std::reverse(seq.begin(), seq.end());
355 if(!m_has_found_match)
356 position = restart; // reset search postion
357 #ifdef BOOST_REGEX_RECURSIVE
358 m_can_backtrack = true; // reset for further searches
360 return m_has_found_match;
363 template <class BidiIterator, class Allocator, class traits>
364 bool perl_matcher<BidiIterator, Allocator, traits>::match_literal()
366 unsigned int len = static_cast<const re_literal*>(pstate)->length;
367 const char_type* what = reinterpret_cast<const char_type*>(static_cast<const re_literal*>(pstate) + 1);
369 // compare string with what we stored in
371 for(unsigned int i = 0; i < len; ++i, ++position)
373 if((position == last) || (traits_inst.translate(*position, icase) != what[i]))
376 pstate = pstate->next.p;
380 template <class BidiIterator, class Allocator, class traits>
381 bool perl_matcher<BidiIterator, Allocator, traits>::match_start_line()
383 if(position == backstop)
385 if((m_match_flags & match_prev_avail) == 0)
387 if((m_match_flags & match_not_bol) == 0)
389 pstate = pstate->next.p;
395 else if(m_match_flags & match_single_line)
398 // check the previous value character:
399 BidiIterator t(position);
403 if(is_separator(*t) && !((*t == static_cast<char_type>('\r')) && (*position == static_cast<char_type>('\n'))) )
405 pstate = pstate->next.p;
409 else if(is_separator(*t))
411 pstate = pstate->next.p;
417 template <class BidiIterator, class Allocator, class traits>
418 bool perl_matcher<BidiIterator, Allocator, traits>::match_end_line()
422 if(m_match_flags & match_single_line)
424 // we're not yet at the end so *first is always valid:
425 if(is_separator(*position))
427 if((position != backstop) || (m_match_flags & match_prev_avail))
429 // check that we're not in the middle of \r\n sequence
430 BidiIterator t(position);
432 if((*t == static_cast<char_type>('\r')) && (*position == static_cast<char_type>('\n')))
437 pstate = pstate->next.p;
441 else if((m_match_flags & match_not_eol) == 0)
443 pstate = pstate->next.p;
449 template <class BidiIterator, class Allocator, class traits>
450 bool perl_matcher<BidiIterator, Allocator, traits>::match_wild()
454 if(is_separator(*position) && ((match_any_mask & static_cast<const re_dot*>(pstate)->mask) == 0))
456 if((*position == char_type(0)) && (m_match_flags & match_not_dot_null))
458 pstate = pstate->next.p;
463 template <class BidiIterator, class Allocator, class traits>
464 bool perl_matcher<BidiIterator, Allocator, traits>::match_word_boundary()
466 bool b; // indcates whether next character is a word character
469 // prev and this character must be opposites:
470 b = traits_inst.isctype(*position, m_word_mask);
474 b = (m_match_flags & match_not_eow) ? true : false;
476 if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))
478 if(m_match_flags & match_not_bow)
486 b ^= traits_inst.isctype(*position, m_word_mask);
491 pstate = pstate->next.p;
494 return false; // no match if we get to here...
497 template <class BidiIterator, class Allocator, class traits>
498 bool perl_matcher<BidiIterator, Allocator, traits>::match_within_word()
502 // both prev and this character must be m_word_mask:
503 bool prev = traits_inst.isctype(*position, m_word_mask);
506 if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))
511 b = traits_inst.isctype(*position, m_word_mask);
516 pstate = pstate->next.p;
523 template <class BidiIterator, class Allocator, class traits>
524 bool perl_matcher<BidiIterator, Allocator, traits>::match_word_start()
527 return false; // can't be starting a word if we're already at the end of input
528 if(!traits_inst.isctype(*position, m_word_mask))
529 return false; // next character isn't a word character
530 if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))
532 if(m_match_flags & match_not_bow)
533 return false; // no previous input
537 // otherwise inside buffer:
538 BidiIterator t(position);
540 if(traits_inst.isctype(*t, m_word_mask))
541 return false; // previous character not non-word
543 // OK we have a match:
544 pstate = pstate->next.p;
548 template <class BidiIterator, class Allocator, class traits>
549 bool perl_matcher<BidiIterator, Allocator, traits>::match_word_end()
551 if((position == backstop) && ((m_match_flags & match_prev_avail) == 0))
552 return false; // start of buffer can't be end of word
553 BidiIterator t(position);
555 if(traits_inst.isctype(*t, m_word_mask) == false)
556 return false; // previous character wasn't a word character
560 if(m_match_flags & match_not_eow)
561 return false; // end of buffer but not end of word
565 // otherwise inside buffer:
566 if(traits_inst.isctype(*position, m_word_mask))
567 return false; // next character is a word character
569 pstate = pstate->next.p;
570 return true; // if we fall through to here then we've succeeded
573 template <class BidiIterator, class Allocator, class traits>
574 bool perl_matcher<BidiIterator, Allocator, traits>::match_buffer_start()
576 if((position != backstop) || (m_match_flags & match_not_bob))
579 pstate = pstate->next.p;
583 template <class BidiIterator, class Allocator, class traits>
584 bool perl_matcher<BidiIterator, Allocator, traits>::match_buffer_end()
586 if((position != last) || (m_match_flags & match_not_eob))
589 pstate = pstate->next.p;
593 template <class BidiIterator, class Allocator, class traits>
594 bool perl_matcher<BidiIterator, Allocator, traits>::match_backref()
597 // Compare with what we previously matched.
598 // Note that this succeeds if the backref did not partisipate
599 // in the match, this is in line with ECMAScript, but not Perl
602 int index = static_cast<const re_brace*>(pstate)->index;
605 named_subexpressions::range_type r = re.get_data().equal_range(index);
606 BOOST_ASSERT(r.first != r.second);
609 index = r.first->index;
611 }while((r.first != r.second) && ((*m_presult)[index].matched != true));
614 if((m_match_flags & match_perl) && !(*m_presult)[index].matched)
617 BidiIterator i = (*m_presult)[index].first;
618 BidiIterator j = (*m_presult)[index].second;
621 if((position == last) || (traits_inst.translate(*position, icase) != traits_inst.translate(*i, icase)))
626 pstate = pstate->next.p;
630 template <class BidiIterator, class Allocator, class traits>
631 bool perl_matcher<BidiIterator, Allocator, traits>::match_long_set()
633 typedef typename traits::char_class_type char_class_type;
634 // let the traits class do the work:
637 BidiIterator t = re_is_set_member(position, last, static_cast<const re_set_long<char_class_type>*>(pstate), re.get_data(), icase);
640 pstate = pstate->next.p;
647 template <class BidiIterator, class Allocator, class traits>
648 bool perl_matcher<BidiIterator, Allocator, traits>::match_set()
652 if(static_cast<const re_set*>(pstate)->_map[static_cast<unsigned char>(traits_inst.translate(*position, icase))])
654 pstate = pstate->next.p;
661 template <class BidiIterator, class Allocator, class traits>
662 bool perl_matcher<BidiIterator, Allocator, traits>::match_jump()
664 pstate = static_cast<const re_jump*>(pstate)->alt.p;
668 template <class BidiIterator, class Allocator, class traits>
669 bool perl_matcher<BidiIterator, Allocator, traits>::match_combining()
673 if(is_combining(traits_inst.translate(*position, icase)))
676 while((position != last) && is_combining(traits_inst.translate(*position, icase)))
678 pstate = pstate->next.p;
682 template <class BidiIterator, class Allocator, class traits>
683 bool perl_matcher<BidiIterator, Allocator, traits>::match_soft_buffer_end()
685 if(m_match_flags & match_not_eob)
687 BidiIterator p(position);
688 while((p != last) && is_separator(traits_inst.translate(*p, icase)))++p;
691 pstate = pstate->next.p;
695 template <class BidiIterator, class Allocator, class traits>
696 bool perl_matcher<BidiIterator, Allocator, traits>::match_restart_continue()
698 if(position == search_base)
700 pstate = pstate->next.p;
706 template <class BidiIterator, class Allocator, class traits>
707 bool perl_matcher<BidiIterator, Allocator, traits>::match_backstep()
710 #pragma warning(push)
711 #pragma warning(disable:4127)
713 if( ::boost::is_random_access_iterator<BidiIterator>::value)
715 std::ptrdiff_t maxlen = ::boost::BOOST_REGEX_DETAIL_NS::distance(backstop, position);
716 if(maxlen < static_cast<const re_brace*>(pstate)->index)
718 std::advance(position, -static_cast<const re_brace*>(pstate)->index);
722 int c = static_cast<const re_brace*>(pstate)->index;
725 if(position == backstop)
730 pstate = pstate->next.p;
737 template <class BidiIterator, class Allocator, class traits>
738 inline bool perl_matcher<BidiIterator, Allocator, traits>::match_assert_backref()
740 // return true if marked sub-expression N has been matched:
741 int index = static_cast<const re_brace*>(pstate)->index;
745 // Magic value for a (DEFINE) block:
750 // Have we matched subexpression "index"?
751 // Check if index is a hash value:
754 named_subexpressions::range_type r = re.get_data().equal_range(index);
755 while(r.first != r.second)
757 if((*m_presult)[r.first->index].matched)
767 result = (*m_presult)[index].matched;
769 pstate = pstate->next.p;
773 // Have we recursed into subexpression "index"?
774 // If index == 0 then check for any recursion at all, otherwise for recursion to -index-1.
778 named_subexpressions::range_type r = re.get_data().equal_range(idx);
779 int stack_index = recursion_stack.empty() ? -1 : recursion_stack.back().idx;
780 while(r.first != r.second)
782 result |= (stack_index == r.first->index);
789 result = !recursion_stack.empty() && ((recursion_stack.back().idx == idx) || (index == 0));
791 pstate = pstate->next.p;
796 template <class BidiIterator, class Allocator, class traits>
797 bool perl_matcher<BidiIterator, Allocator, traits>::match_fail()
799 // Just force a backtrack:
803 template <class BidiIterator, class Allocator, class traits>
804 bool perl_matcher<BidiIterator, Allocator, traits>::match_accept()
806 if(!recursion_stack.empty())
808 return skip_until_paren(recursion_stack.back().idx);
812 return skip_until_paren(INT_MAX);
816 template <class BidiIterator, class Allocator, class traits>
817 bool perl_matcher<BidiIterator, Allocator, traits>::find_restart_any()
820 #pragma warning(push)
821 #pragma warning(disable:4127)
823 const unsigned char* _map = re.get_map();
826 // skip everything we can't match:
827 while((position != last) && !can_start(*position, _map, (unsigned char)mask_any) )
831 // run out of characters, try a null match if possible:
833 return match_prefix();
836 // now try and obtain a match:
849 template <class BidiIterator, class Allocator, class traits>
850 bool perl_matcher<BidiIterator, Allocator, traits>::find_restart_word()
853 #pragma warning(push)
854 #pragma warning(disable:4127)
856 // do search optimised for word starts:
857 const unsigned char* _map = re.get_map();
858 if((m_match_flags & match_prev_avail) || (position != base))
860 else if(match_prefix())
864 while((position != last) && traits_inst.isctype(*position, m_word_mask))
866 while((position != last) && !traits_inst.isctype(*position, m_word_mask))
871 if(can_start(*position, _map, (unsigned char)mask_any) )
885 template <class BidiIterator, class Allocator, class traits>
886 bool perl_matcher<BidiIterator, Allocator, traits>::find_restart_line()
888 // do search optimised for line starts:
889 const unsigned char* _map = re.get_map();
892 while(position != last)
894 while((position != last) && !is_separator(*position))
901 if(re.can_be_null() && match_prefix())
906 if( can_start(*position, _map, (unsigned char)mask_any) )
918 template <class BidiIterator, class Allocator, class traits>
919 bool perl_matcher<BidiIterator, Allocator, traits>::find_restart_buf()
921 if((position == base) && ((m_match_flags & match_not_bob) == 0))
922 return match_prefix();
926 template <class BidiIterator, class Allocator, class traits>
927 bool perl_matcher<BidiIterator, Allocator, traits>::find_restart_lit()
931 return false; // can't possibly match if we're at the end already
933 unsigned type = (m_match_flags & match_continuous) ?
934 static_cast<unsigned int>(regbase::restart_continue)
935 : static_cast<unsigned int>(re.get_restart_type());
937 const kmp_info<char_type>* info = access::get_kmp(re);
939 const char_type* x = info->pstr;
941 while (position != last)
943 while((j > -1) && (x[j] != traits_inst.translate(*position, icase)))
944 j = info->kmp_next[j];
949 if(type == regbase::restart_fixed_lit)
951 std::advance(position, -j);
953 std::advance(restart, len);
954 m_result.set_first(position);
955 m_result.set_second(restart);
962 std::advance(position, -j);
967 for(int k = 0; (restart != position) && (k < j); ++k, --restart)
968 {} // dwa 10/20/2000 - warning suppression for MWCW
972 j = 0; //we could do better than this...
977 if((m_match_flags & match_partial) && (position == last) && j)
979 // we need to check for a partial match:
981 std::advance(position, -j);
982 return match_prefix();
988 } // namespace BOOST_REGEX_DETAIL_NS
993 # pragma warning(pop)
1000 #pragma warning(push)
1001 #pragma warning(disable: 4103)
1003 #ifdef BOOST_HAS_ABI_HEADERS
1004 # include BOOST_ABI_SUFFIX
1007 #pragma warning(pop)