//+--------------------------------------------------------------------------- // // File: basic_regexpr.hxx // // Contents: classes for regular expression pattern matching a-la perl // // Classes: basic_rpattern, basic_regexpr // // Functions: basic_regexpr::match // basic_regexpr::substitute // basic_regexpr::cbackrefs // basic_regexpr::backref // basic_regexpr::all_backrefs // basic_regexpr::backref_str // // Coupling: // // History: 12-11-1998 ericne Created // 01-05-2001 ericne Removed dependency on VC's choice // of STL iterator types. // //---------------------------------------------------------------------------- #pragma once // C4786 identifier was truncated to '255' characters in the debug information #pragma warning( disable : 4290 4786 ) #ifdef _MT #include // for CRITICAL_SECTION #endif #include #include #include #include #include #include #include #include // for _set_new_handler #include #include "syntax.h" namespace regex { // Called when an allocation fails inline int __cdecl my_new_handler( size_t ) { throw std::bad_alloc(); } // For pushing and popping the new handler class push_new_handler { _PNH m_pnh; public: push_new_handler( _PNH pnh ) { m_pnh = _set_new_handler( pnh ); } ~push_new_handler() { (void)_set_new_handler( m_pnh ); } }; class bad_regexpr : public std::runtime_error { public: explicit bad_regexpr(const std::string& _S) : std::runtime_error(_S) {} virtual ~bad_regexpr() {} }; // // Flags to control how matching occurs // enum REGEX_FLAGS { NOCASE = 0x0001, // ignore case GLOBAL = 0x0002, // match everywhere in the string MULTILINE = 0x0004, // ^ and $ can match internal line breaks SINGLELINE = 0x0008, // . can match newline character RIGHTMOST = 0x0010, // start matching at the right of the string NOBACKREFS = 0x0020, // only meaningful when used with GLOBAL and substitute FIRSTBACKREFS = 0x0040, // only meaningful when used with GLOBAL ALLBACKREFS = 0x0080, // only meaningful when used with GLOBAL CSTRINGS = 0x0100, // optimize pattern for use with null-terminated strings NORMALIZE = 0x0200 // Preprocess patterns: "\\n" => "\n", etc. }; // Forward declarations template< typename CI > struct match_param; template< typename CI > class match_group; template< typename CI > class match_wrapper; template< typename CI > class match_charset; template< typename CI > class basic_rpattern_base; // -------------------------------------------------------------------------- // // Class: width_type // // Description: represents the width of a sub-expression // // Methods: width_add - add two widths // width_mult - multiply two widths // width_type - ctor // width_type - ctor // operator= - assign a width // operator== - are widths equal // operator!= - are widths now equal // operator+ - add two widths // operator* - multiply two widths // // Members: m_min - smallest number of characters a sub-expr can span // m_max - largest number of characters a sub-expr can span // // History: 8/14/2000 - ericne - Created // // -------------------------------------------------------------------------- struct width_type { size_t m_min; size_t m_max; static size_t width_add( size_t a, size_t b ) { return ( -1 == a || -1 == b ? -1 : a + b ); } static size_t width_mult( size_t a, size_t b ) { return ( -1 == a || -1 == b ? -1 : a * b ); } width_type( size_t _min = 0, size_t _max = -1 ) : m_min(_min), m_max(_max) { } width_type( const width_type & that ) : m_min(that.m_min), m_max(that.m_max) { } width_type & operator=( const width_type & that ) { m_min = that.m_min; m_max = that.m_max; return *this; } bool operator==( const width_type & that ) const { return ( m_min == that.m_min && m_max == that.m_max ); } bool operator!=( const width_type & that ) const { return ( m_min != that.m_min || m_max != that.m_max ); } width_type operator+( const width_type & that ) const { return width_type( width_add( m_min, that.m_min ), width_add( m_max, that.m_max ) ); } width_type operator*( const width_type & that ) const { return width_type( width_mult( m_min, that.m_min ), width_mult( m_max, that.m_max ) ); } }; const width_type worst_width(0,-1); const width_type uninit_width(-1,-1); // -------------------------------------------------------------------------- // // Class: sub_expr // // Description: patterns are "compiled" into a directed graph of sub_expr // structs. Matching is accomplished by traversing this graph. // // Methods: sub_expr - construct a sub_expr // _match_this - does this sub_expr match at the given location // _width_this - what is the width of this sub_expr // ~sub_expr - virt dtor so cleanup happens correctly // _delete - delete this node in the graph and all nodes linked // next - pointer to the next node in the graph // next - pointer to the next node in the graph // match_next - match the rest of the graph // domatch - match_this and match_next // is_assertion - true if this sub_expr is a zero-width assertion // get_width - find the width of the graph at this sub_expr // // Members: m_pnext - pointer to the next node in the graph // // History: 8/14/2000 - ericne - Created // // -------------------------------------------------------------------------- template< typename CI > class sub_expr { sub_expr * m_pnext; protected: // Only derived classes and basic_rpattern can instantiate sub_expr's sub_expr( ) : m_pnext(NULL) { } // match this object only virtual bool _match_this( match_param & param, CI & icur ) const throw() { return true; } virtual width_type _width_this() throw() = 0; public: typedef typename std::iterator_traits::value_type char_type; friend class match_wrapper; // wrappers can access _match_this method virtual ~sub_expr() {} virtual void _delete() { if( m_pnext ) m_pnext->_delete(); delete this; } inline const sub_expr *const next() const { return m_pnext; } inline sub_expr * & next() { return m_pnext; } // Match all subsequent objects inline bool match_next( match_param & param, CI icur ) const throw() { return NULL == m_pnext || m_pnext->domatch( param, icur ); } // Match this object and all subsequent objects // If domatch returns false, it must not change any internal state virtual bool domatch( match_param & param, CI icur ) const throw() { return ( _match_this(param,icur) && match_next(param,icur) ); } virtual bool is_assertion() const throw() { return false; } width_type get_width() throw() { width_type this_width = _width_this(); if( NULL == m_pnext ) return this_width; width_type that_width = m_pnext->get_width(); return ( this_width + that_width ); } }; template< typename CI > void delete_sub_expr( sub_expr * psub ) { if( psub ) psub->_delete(); } template< typename CI, typename SY = perl_syntax::value_type> > class create_charset_helper { public: typedef std::iterator_traits::value_type char_type; static sub_expr * create_charset_aux( std::basic_string & str, std::basic_string::iterator & icur, unsigned flags ); }; // -------------------------------------------------------------------------- // // Class: auto_sub_ptr // // Description: Class for automatically cleaning up the structure associated // with a parsed pattern // // Methods: auto_sub_ptr - private copy ctor - not used // operator= - private assign operator - not used // operator T* - private implicit cast operator - not used // auto_sub_ptr - ctor // ~auto_sub_ptr - dtor, frees ptr // free_ptr - explicitly free pointer // release - relinquish ownership of ptr // operator= - take ownership of ptr // get - return ptr // get - return ptr // operator-> - method call through ptr // operator-> - method call through ptr // // Members: m_psub - sub_expr pointer // // History: 8/14/2000 - ericne - Created // // -------------------------------------------------------------------------- template< typename T > class auto_sub_ptr { T * m_psub; // hide these methods auto_sub_ptr( const auto_sub_ptr & ) {} auto_sub_ptr & operator=( const auto_sub_ptr & ) { return *this; } operator T*() const { return m_psub; } public: auto_sub_ptr( T * psub = NULL ) : m_psub( psub ) {} ~auto_sub_ptr() { free_ptr(); } void free_ptr() // deallocate { delete_sub_expr( m_psub ); } T * release() // relinquish ownership, but don't deallocate { T * psub = m_psub; m_psub = NULL; return psub; } auto_sub_ptr & operator=( T * psub ) { delete_sub_expr( m_psub ); m_psub = psub; return *this; } inline const T*const get() const { return m_psub; } inline T* & get() { return m_psub; } inline const T*const operator->() const { return m_psub; } inline T* operator->() { return m_psub; } }; template< typename CI > struct backref_tag : public std::pair { backref_tag( CI i1 = CI(0), CI i2 = CI(0) ) : std::pair(i1,i2), reserved(0) {} operator bool() const throw() { return first != CI(0) && second != CI(0); } bool operator!() const throw() { return ! operator bool(); } size_t reserved; // used for internal book-keeping }; template< typename CH > backref_tag< const CH * > _static_match_helper( const CH * szstr, const basic_rpattern_base< const CH * > & pat, std::vector< backref_tag< const CH * > > * prgbackrefs ) throw(); template< typename CH > size_t _static_count_helper( const CH * szstr, const basic_rpattern_base< const CH * > & pat ) throw(); // -------------------------------------------------------------------------- // // Class: basic_regexpr // // Description: string class that allows regular expression pattern matching // // Methods: basic_regexpr - ctor // match - static method for matching C-style strings // match - non-static method for matching C++-style strings // count - static method for couting matches in C-style strings // count - non-static method for counting matches in C++-style strin // substitute - perform substitutions in C++-style strings // cbackrefs - return the count of internally stored back-references // rstart - offset to start of n-th backref // rlength - lenght of n-th backref // backref - return the n-th backref // all_backrefs - return a vector of all saved backrefs // backref_str - return the string to which the backreferences refer // // Members: m_rgbackrefs - vector of backrefs // m_backref_str - temp string buffer // m_pbackref_str - pointer to the string containing the string to which // the backreferences refer (either *this or m_backref_str) // // Typedefs: backref_type - // backref_vector - // // History: 8/14/2000 - ericne - Created // // -------------------------------------------------------------------------- template< typename CH, typename TR = std::char_traits, typename AL = std::allocator > class basic_regexpr : public std::basic_string { public: basic_regexpr( const allocator_type & a = allocator_type() ) : std::basic_string( a ), m_pbackref_str( & m_backref_str ) {} basic_regexpr( const CH * p, const allocator_type & a = allocator_type() ) : std::basic_string( p, a ), m_pbackref_str( & m_backref_str ) {} basic_regexpr( const CH * p, size_type n, const allocator_type & a = allocator_type() ) : std::basic_string( p, n, a ), m_pbackref_str( & m_backref_str ) {} basic_regexpr( const std::basic_string & s, size_type pos = 0, size_type n = npos, const allocator_type & a = allocator_type() ) : std::basic_string( s, pos, n, a ), m_pbackref_str( & m_backref_str ) {} basic_regexpr( size_type n, CH ch, const allocator_type & a = allocator_type() ) : std::basic_string( n, ch, a ), m_pbackref_str( & m_backref_str ) {} basic_regexpr( const_iterator begin, const_iterator end, const allocator_type & a = allocator_type() ) : std::basic_string( begin, end, a ), m_pbackref_str( & m_backref_str ) {} // actually stores iterators into *m_pbackref_str: typedef backref_tag backref_type; typedef std::vector< backref_type > backref_vector; // stores pointers into the null-terminated C-stype string typedef backref_tag< const CH * > backref_type_c; typedef std::vector< backref_type_c > backref_vector_c; // returns $0, the first backref static backref_type_c match( const CH * szstr, const basic_rpattern_base< const CH * > & pat, backref_vector_c * prgbackrefs = NULL ) throw() { return _static_match_helper( szstr, pat, prgbackrefs ); } // returns $0, the first backref backref_type match( const basic_rpattern_base< const_iterator > & pat, size_type pos = 0, size_type len = npos ) const throw(); static size_t count( const CH * szstr, const basic_rpattern_base< const CH * > & pat ) throw() { return _static_count_helper( szstr, pat ); } size_t count( const basic_rpattern_base< const_iterator > & pat, size_type pos = 0, size_type len = npos ) const throw(); size_t substitute( const basic_rpattern_base< const_iterator > & pat, size_type pos = 0, size_type len = npos ) throw(std::bad_alloc); size_t cbackrefs() const throw() { return m_rgbackrefs.size(); } size_type rstart( size_t cbackref = 0 ) const throw(std::out_of_range) { return std::distance( m_pbackref_str->begin(), m_rgbackrefs.at( cbackref ).first ); } size_type rlength( size_t cbackref = 0 ) const throw(std::out_of_range) { return std::distance( m_rgbackrefs.at( cbackref ).first, m_rgbackrefs.at( cbackref ).second ); } backref_type backref( size_t cbackref ) const throw(std::out_of_range) { return m_rgbackrefs.at( cbackref ); } const backref_vector & all_backrefs() const throw() { return m_rgbackrefs; } const std::basic_string & backref_str() const throw() { return *m_pbackref_str; } protected: // save information about the backrefs // mutable because these can change in the "const" match() method. mutable backref_vector m_rgbackrefs; mutable std::basic_string m_backref_str; mutable const std::basic_string * m_pbackref_str; }; // -------------------------------------------------------------------------- // // Class: match_param // // Description: Struct that contains the state of the matching operation. // Passed by reference to all domatch and _match_this routines. // // Methods: match_param - ctor // match_param - ctor // // Members: ibegin - start of the string // istart - start of this iteration // istop - end of the string // prgbackrefs - pointer to backref array0 // // History: 8/14/2000 - ericne - Created // // -------------------------------------------------------------------------- template< typename CI > struct match_param { CI ibegin; CI istart; CI istop; std::vector< backref_tag< CI > > * prgbackrefs; match_param( CI _istart, CI _istop, std::vector< backref_tag< CI > > * _prgbackrefs ) : ibegin(_istart), istart(_istart), istop(_istop), prgbackrefs(_prgbackrefs) { } match_param( CI _ibegin, CI _istart, CI _istop, std::vector< backref_tag< CI > > * _prgbackrefs ) : ibegin(_ibegin), istart(_istart), istop(_istop), prgbackrefs(_prgbackrefs) { } }; // -------------------------------------------------------------------------- // // Class: subst_node // // Description: Substitution strings are parsed into an array of these // structures in order to speed up subst operations. // // Members: stype - type of this struct // subst_string - do a string substitution // subst_backref - do a bacref substitution // op - execute an operation // // History: 8/14/2000 - ericne - Created // // -------------------------------------------------------------------------- struct subst_node { enum subst_type { SUBST_STRING, SUBST_BACKREF, SUBST_OP }; enum { PREMATCH = -1, POSTMATCH = -2 }; enum op_type { UPPER_ON = SUBST_UPPER_ON, UPPER_NEXT = SUBST_UPPER_NEXT, LOWER_ON = SUBST_LOWER_ON, LOWER_NEXT = SUBST_LOWER_NEXT, ALL_OFF = SUBST_ALL_OFF }; subst_type stype; union { struct { size_t rstart; size_t rlength; } subst_string; size_t subst_backref; op_type op; }; }; // -------------------------------------------------------------------------- // // Class: basic_rpattern_base // // Description: // // Methods: basic_rpattern_base - ctor // flags - get the state of the flags // uses_backrefs - true if the backrefs are referenced // get_first_subexpression - return ptr to first sub_expr struct // get_width - get min/max nbr chars this pattern can match // loops - if false, we only need to try to match at 1st position // cgroups - number of visible groups // _cgroups_total - total number of groups, including hidden (?:) groups // get_pat - get string representing the pattern // get_subst - get string representing the substitution string // get_subst_list - get the list of subst nodes // _normalize_string - perform character escaping // _reset - reinitialize the pattern // // Members: m_fuses_backrefs - // m_floop - // m_cgroups - // m_cgroups_visible - // m_flags - // m_nwidth - // m_pat - // m_subst - // m_subst_list - // m_pfirst - // // Typedefs: char_type - // // History: 8/14/2000 - ericne - Created // // -------------------------------------------------------------------------- template< typename CI > class basic_rpattern_base { public: typedef std::iterator_traits::value_type char_type; basic_rpattern_base( unsigned flags = 0, const std::basic_string & pat = std::basic_string(), const std::basic_string & subst = std::basic_string() ) throw() : m_fuses_backrefs( false ), m_floop( true ), m_cgroups( 0 ), m_cgroups_visible( 0 ), m_flags( flags ), m_nwidth( uninit_width ), m_pat( pat ), m_subst( subst ), m_pfirst( NULL ) { } unsigned flags() const throw() { return m_flags; } bool uses_backrefs() const throw() { return m_fuses_backrefs; } const sub_expr * get_first_subexpression() const throw() { return m_pfirst.get(); } width_type get_width() const throw() { return m_nwidth; } bool loops() const throw() { return m_floop; } size_t cgroups() const throw() { return m_cgroups_visible; } size_t _cgroups_total() const throw() { return m_cgroups; } const std::basic_string & get_pat() const throw() { return m_pat; } const std::basic_string & get_subst() const throw() { return m_subst; } const std::list & get_subst_list() const throw() { return m_subst_list; } protected: void _normalize_string( std::basic_string & str ); void _reset() { m_fuses_backrefs = false; m_flags = 0; } bool m_fuses_backrefs; // true if the substitution uses backrefs bool m_floop; // false if m_pfirst->domatch only needs to be called once size_t m_cgroups; // number of groups (always at least one) size_t m_cgroups_visible; // number of visible groups unsigned m_flags; // flags used to customize search/replace width_type m_nwidth; // width of the pattern std::basic_string m_pat; // contains the unparsed pattern std::basic_string m_subst; // contains the unparsed substitution std::list m_subst_list; // used to speed up substitution auto_sub_ptr > m_pfirst; // first subexpression in pattern }; // -------------------------------------------------------------------------- // // Class: basic_rpattern // // Description: // // Methods: basic_rpattern - ctor // basic_rpattern - // basic_rpattern - // init - for (re)initializing a pattern // init - // set_substitution - set the substitution string // set_flags - set the flags // register_intrinsic_charset - bind an escape sequence to a user-def'd charset // purge_intrinsic_charsets - delete all user-def'd charsets // _get_next_group_nbr - return a monotomically increasing id // _find_next_group - parse the next group of the pattern // _find_next - parse the next sub_expr of the pattern // _find_atom - parse the next atom of the pattern // _quantify - quantify the sub_expr // _common_init - perform some common initialization tasks // _parse_subst - parse the substitution string // _add_subst_backref - add a backref node to the subst list // _reset - reinitialize the pattern // // Members: s_charset_map - for maintaining user-defined charsets // m_invisible_groups - list of hidden groups to be numbered last // // Typedefs: syntax_type - // // History: 8/14/2000 - ericne - Created // // -------------------------------------------------------------------------- template< typename CI, typename SY = perl_syntax::value_type> > class basic_rpattern : public basic_rpattern_base { public: friend class match_charset; typedef SY syntax_type; basic_rpattern() throw(); basic_rpattern( const std::basic_string & pat, unsigned flags=0 ) throw(bad_regexpr,std::bad_alloc); basic_rpattern( const std::basic_string & pat, const std::basic_string & subst, unsigned flags=0 ) throw(bad_regexpr,std::bad_alloc); void init( const std::basic_string & pat, unsigned flags=0 ) throw(bad_regexpr,std::bad_alloc); void init( const std::basic_string & pat, const std::basic_string & subst, unsigned flags=0 ) throw(bad_regexpr,std::bad_alloc); void set_substitution( const std::basic_string & subst ) throw(bad_regexpr,std::bad_alloc); void set_flags( unsigned flags ) throw(bad_regexpr,std::bad_alloc); class charset_map { struct charsets { sub_expr * rgpcharsets[2]; std::basic_string str_charset; charsets() throw() { memset( rgpcharsets, 0, sizeof( rgpcharsets ) ); } ~charsets() throw() { clean(); } void clean() throw() { for( int i=0; i < (sizeof(rgpcharsets)/sizeof(*rgpcharsets)); ++i ) delete_sub_expr( rgpcharsets[i] ); } match_charset * get_charset( unsigned flags ) throw(bad_regexpr,std::bad_alloc) { push_new_handler pnh( &my_new_handler ); // Since these charsets are only used while creating other charsets, // all flags besides NOCASE can safely be ignored here. bool index = ( NOCASE == ( NOCASE & flags ) ); if( NULL == rgpcharsets[ index ] ) { std::basic_string::iterator istart = str_charset.begin(); rgpcharsets[ index ] = create_charset_helper::create_charset_aux( str_charset, ++istart, flags ); } return (match_charset*) rgpcharsets[ index ]; } }; typedef std::map map_type; std::auto_ptr m_pmap; public: void put( char_type ch, const std::basic_string & str ) throw(bad_regexpr,std::bad_alloc) { // These characters cannot be bound to a user-defined intrinsic character set static const char_type rgIllegal[] = { '0','1','2','3','4','5','6','7','8','9','A','Z','z','Q', 'b','B','d','D','f','n','r','s','S','t','v','w','W','E' }; // So operator new throws bad_alloc on failure. push_new_handler pnh( &my_new_handler ); if( std::char_traits::find( rgIllegal, ARRAYSIZE( rgIllegal ), ch ) ) throw bad_regexpr( "illegal character specified for intrinsic character set." ); if( NULL == m_pmap.get() ) m_pmap = auto_ptr( new map_type ); // creates an empty entry if one does not already exist charsets & chrsts = (*m_pmap)[ch]; chrsts.clean(); chrsts.str_charset = str; // Try compiling the character set once to make sure it is properly formed: (void) chrsts.get_charset( 0 ); } match_charset * get( char_type ch, unsigned flags ) throw() { match_charset * pRet = NULL; if( NULL != m_pmap.get() ) { try { push_new_handler pnh( &my_new_handler ); map_type::iterator iter = m_pmap->find( ch ); if( iter != m_pmap->end() ) pRet = iter->second.get_charset( flags ); } catch(...) {} } return pRet; } void purge() throw() { if( NULL != m_pmap.get() ) delete m_pmap.release(); } }; static void register_intrinsic_charset( char_type ch, const std::basic_string & str ) throw(bad_regexpr,std::bad_alloc) { s_charset_map.put( ch, str ); } static void purge_intrinsic_charsets() throw() { s_charset_map.purge(); } protected: static charset_map s_charset_map; size_t _get_next_group_nbr() { return m_cgroups++; } match_group * _find_next_group( std::basic_string::iterator & ipat, unsigned & flags, std::vector*> & rggroups ); bool _find_next( std::basic_string::iterator & ipat, match_group * pgroup, unsigned & flags, std::vector*> & rggroups ); void _find_atom( std::basic_string::iterator & ipat, match_group * pgroup, unsigned flags ); void _quantify( auto_sub_ptr > & pnew, match_group * pnew_group, std::basic_string::iterator & ipat ); void _common_init( unsigned flags ); void _parse_subst(); void _add_subst_backref( subst_node & snode, size_t nbackref, size_t rstart ); void _reset(); std::list*> m_invisible_groups; // groups w/o backrefs }; inline std::ostream & operator<<( std::ostream & sout, const basic_regexpr::backref_type & br ) { for( std::string::const_iterator ithis = br.first; ithis != br.second; ++ithis ) sout.put( *ithis ); return sout; } inline std::wostream & operator<<( std::wostream & sout, const basic_regexpr::backref_type & br ) { for( std::wstring::const_iterator ithis = br.first; ithis != br.second; ++ithis ) sout.put( *ithis > UCHAR_MAX ? L'?' : *ithis ); return sout; } typedef basic_regexpr regexpr; typedef std::basic_string tstring; typedef basic_rpattern > perl_rpattern_c; typedef basic_rpattern > posix_rpattern_c; typedef basic_rpattern > perl_rpattern; typedef basic_rpattern > posix_rpattern; typedef perl_rpattern rpattern; // matches against std::string typedef perl_rpattern_c rpattern_c; // matches against null-terminated, c-style strings #ifdef _MT // // Define some classes and macros for creating function-local // static const rpatterns in a thread-safe way // template< typename PAT > class rpattern_destroyer { const bool & m_fConstructed; const PAT & m_refPat; public: rpattern_destroyer( const bool & fConstructed, const PAT & refPat ) : m_fConstructed( fConstructed ), m_refPat( refPat ) { } ~rpattern_destroyer() { if( m_fConstructed ) _Destroy( & m_refPat ); } }; class CRegExCritSect : private CRITICAL_SECTION { public: CRegExCritSect() { InitializeCriticalSection(this); } ~CRegExCritSect() { DeleteCriticalSection(this); } void Enter() { EnterCriticalSection(this); } void Leave() { LeaveCriticalSection(this); } }; extern CRegExCritSect g_objRegExCritSect; class CRegExLock { public: CRegExLock() { g_objRegExCritSect.Enter(); } ~CRegExLock() { g_objRegExCritSect.Leave(); } }; #define STATIC_RPATTERN_EX( type, var, params ) \ static unsigned char s_rgb_##var[ sizeof type ]; \ static bool s_f_##var = false; \ static const type & var = *reinterpret_cast( s_rgb_##var ); \ static const regex::rpattern_destroyer s_des_##var( s_f_##var, var ); \ if( ! s_f_##var ) \ { \ regex::CRegExLock objLock; \ if( ! s_f_##var ) \ { \ new( s_rgb_##var ) type params; \ s_f_##var = true; \ } \ } #else #define STATIC_RPATTERN_EX( type, var, params ) \ static const type var params; #endif #define STATIC_RPATTERN( var, params ) \ STATIC_RPATTERN_EX( regex::rpattern, var, params ) } // namespace regex