1125 lines
33 KiB
C++
1125 lines
33 KiB
C++
/*
|
|
*
|
|
* Copyright (c) 1998-2009 John Maddock
|
|
* Copyright 2008 Eric Niebler.
|
|
*
|
|
* Use, modification and distribution are subject to the
|
|
* Boost Software License, Version 1.0. (See accompanying file
|
|
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* LOCATION: see http://www.boost.org for most recent version.
|
|
* FILE regex_format.hpp
|
|
* VERSION see <boost/version.hpp>
|
|
* DESCRIPTION: Provides formatting output routines for search and replace
|
|
* operations. Note this is an internal header file included
|
|
* by regex.hpp, do not include on its own.
|
|
*/
|
|
|
|
#ifndef BOOST_REGEX_FORMAT_HPP
|
|
#define BOOST_REGEX_FORMAT_HPP
|
|
|
|
#include <type_traits>
|
|
#include <functional>
|
|
|
|
namespace boost{
|
|
|
|
//
|
|
// Forward declaration:
|
|
//
|
|
template <class BidiIterator, class Allocator = typename std::vector<sub_match<BidiIterator> >::allocator_type >
|
|
class match_results;
|
|
|
|
namespace BOOST_REGEX_DETAIL_NS{
|
|
|
|
//
|
|
// struct trivial_format_traits:
|
|
// defines minimum localisation support for formatting
|
|
// in the case that the actual regex traits is unavailable.
|
|
//
|
|
template <class charT>
|
|
struct trivial_format_traits
|
|
{
|
|
typedef charT char_type;
|
|
|
|
static std::ptrdiff_t length(const charT* p)
|
|
{
|
|
return global_length(p);
|
|
}
|
|
static charT tolower(charT c)
|
|
{
|
|
return ::boost::BOOST_REGEX_DETAIL_NS::global_lower(c);
|
|
}
|
|
static charT toupper(charT c)
|
|
{
|
|
return ::boost::BOOST_REGEX_DETAIL_NS::global_upper(c);
|
|
}
|
|
static int value(const charT c, int radix)
|
|
{
|
|
int result = global_value(c);
|
|
return result >= radix ? -1 : result;
|
|
}
|
|
int toi(const charT*& p1, const charT* p2, int radix)const
|
|
{
|
|
return (int)global_toi(p1, p2, radix, *this);
|
|
}
|
|
};
|
|
|
|
#ifdef BOOST_REGEX_MSVC
|
|
# pragma warning(push)
|
|
#pragma warning(disable:26812)
|
|
#endif
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
class basic_regex_formatter
|
|
{
|
|
public:
|
|
typedef typename traits::char_type char_type;
|
|
basic_regex_formatter(OutputIterator o, const Results& r, const traits& t)
|
|
: m_traits(t), m_results(r), m_out(o), m_position(), m_end(), m_flags(), m_state(output_copy), m_restore_state(output_copy), m_have_conditional(false) {}
|
|
OutputIterator format(ForwardIter p1, ForwardIter p2, match_flag_type f);
|
|
OutputIterator format(ForwardIter p1, match_flag_type f)
|
|
{
|
|
return format(p1, p1 + m_traits.length(p1), f);
|
|
}
|
|
private:
|
|
typedef typename Results::value_type sub_match_type;
|
|
enum output_state
|
|
{
|
|
output_copy,
|
|
output_next_lower,
|
|
output_next_upper,
|
|
output_lower,
|
|
output_upper,
|
|
output_none
|
|
};
|
|
|
|
void put(char_type c);
|
|
void put(const sub_match_type& sub);
|
|
void format_all();
|
|
void format_perl();
|
|
void format_escape();
|
|
void format_conditional();
|
|
void format_until_scope_end();
|
|
bool handle_perl_verb(bool have_brace);
|
|
|
|
inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const std::integral_constant<bool, false>&)
|
|
{
|
|
std::vector<char_type> v(i, j);
|
|
return (i != j) ? this->m_results.named_subexpression(&v[0], &v[0] + v.size())
|
|
: this->m_results.named_subexpression(static_cast<const char_type*>(0), static_cast<const char_type*>(0));
|
|
}
|
|
inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j, const std::integral_constant<bool, true>&)
|
|
{
|
|
return this->m_results.named_subexpression(i, j);
|
|
}
|
|
inline typename Results::value_type const& get_named_sub(ForwardIter i, ForwardIter j)
|
|
{
|
|
typedef typename std::is_convertible<ForwardIter, const char_type*>::type tag_type;
|
|
return get_named_sub(i, j, tag_type());
|
|
}
|
|
inline int get_named_sub_index(ForwardIter i, ForwardIter j, const std::integral_constant<bool, false>&)
|
|
{
|
|
std::vector<char_type> v(i, j);
|
|
return (i != j) ? this->m_results.named_subexpression_index(&v[0], &v[0] + v.size())
|
|
: this->m_results.named_subexpression_index(static_cast<const char_type*>(0), static_cast<const char_type*>(0));
|
|
}
|
|
inline int get_named_sub_index(ForwardIter i, ForwardIter j, const std::integral_constant<bool, true>&)
|
|
{
|
|
return this->m_results.named_subexpression_index(i, j);
|
|
}
|
|
inline int get_named_sub_index(ForwardIter i, ForwardIter j)
|
|
{
|
|
typedef typename std::is_convertible<ForwardIter, const char_type*>::type tag_type;
|
|
return get_named_sub_index(i, j, tag_type());
|
|
}
|
|
#ifdef BOOST_REGEX_MSVC
|
|
// msvc-8.0 issues a spurious warning on the call to std::advance here:
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4244)
|
|
#endif
|
|
inline int toi(ForwardIter& i, ForwardIter j, int base, const std::integral_constant<bool, false>&)
|
|
{
|
|
if(i != j)
|
|
{
|
|
std::vector<char_type> v(i, j);
|
|
const char_type* start = &v[0];
|
|
const char_type* pos = start;
|
|
int r = (int)m_traits.toi(pos, &v[0] + v.size(), base);
|
|
std::advance(i, pos - start);
|
|
return r;
|
|
}
|
|
return -1;
|
|
}
|
|
#ifdef BOOST_REGEX_MSVC
|
|
#pragma warning(pop)
|
|
#endif
|
|
inline int toi(ForwardIter& i, ForwardIter j, int base, const std::integral_constant<bool, true>&)
|
|
{
|
|
return m_traits.toi(i, j, base);
|
|
}
|
|
inline int toi(ForwardIter& i, ForwardIter j, int base)
|
|
{
|
|
#if defined(_MSC_VER) && defined(__INTEL_COMPILER) && ((__INTEL_COMPILER == 9999) || (__INTEL_COMPILER == 1210))
|
|
// Workaround for Intel support issue #656654.
|
|
// See also https://svn.boost.org/trac/boost/ticket/6359
|
|
return toi(i, j, base, std::integral_constant<bool, false>());
|
|
#else
|
|
typedef typename std::is_convertible<ForwardIter, const char_type*&>::type tag_type;
|
|
return toi(i, j, base, tag_type());
|
|
#endif
|
|
}
|
|
|
|
const traits& m_traits; // the traits class for localised formatting operations
|
|
const Results& m_results; // the match_results being used.
|
|
OutputIterator m_out; // where to send output.
|
|
ForwardIter m_position; // format string, current position
|
|
ForwardIter m_end; // format string end
|
|
match_flag_type m_flags; // format flags to use
|
|
output_state m_state; // what to do with the next character
|
|
output_state m_restore_state; // what state to restore to.
|
|
bool m_have_conditional; // we are parsing a conditional
|
|
private:
|
|
basic_regex_formatter(const basic_regex_formatter&);
|
|
basic_regex_formatter& operator=(const basic_regex_formatter&);
|
|
};
|
|
#ifdef BOOST_REGEX_MSVC
|
|
# pragma warning(pop)
|
|
#endif
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
OutputIterator basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format(ForwardIter p1, ForwardIter p2, match_flag_type f)
|
|
{
|
|
m_position = p1;
|
|
m_end = p2;
|
|
m_flags = f;
|
|
format_all();
|
|
return m_out;
|
|
}
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_all()
|
|
{
|
|
// over and over:
|
|
while(m_position != m_end)
|
|
{
|
|
switch(*m_position)
|
|
{
|
|
case '&':
|
|
if(m_flags & ::boost::regex_constants::format_sed)
|
|
{
|
|
++m_position;
|
|
put(m_results[0]);
|
|
break;
|
|
}
|
|
put(*m_position++);
|
|
break;
|
|
case '\\':
|
|
format_escape();
|
|
break;
|
|
case '(':
|
|
if(m_flags & boost::regex_constants::format_all)
|
|
{
|
|
++m_position;
|
|
bool have_conditional = m_have_conditional;
|
|
m_have_conditional = false;
|
|
format_until_scope_end();
|
|
m_have_conditional = have_conditional;
|
|
if(m_position == m_end)
|
|
return;
|
|
BOOST_REGEX_ASSERT(*m_position == static_cast<char_type>(')'));
|
|
++m_position; // skip the closing ')'
|
|
break;
|
|
}
|
|
put(*m_position);
|
|
++m_position;
|
|
break;
|
|
case ')':
|
|
if(m_flags & boost::regex_constants::format_all)
|
|
{
|
|
return;
|
|
}
|
|
put(*m_position);
|
|
++m_position;
|
|
break;
|
|
case ':':
|
|
if((m_flags & boost::regex_constants::format_all) && m_have_conditional)
|
|
{
|
|
return;
|
|
}
|
|
put(*m_position);
|
|
++m_position;
|
|
break;
|
|
case '?':
|
|
if(m_flags & boost::regex_constants::format_all)
|
|
{
|
|
++m_position;
|
|
format_conditional();
|
|
break;
|
|
}
|
|
put(*m_position);
|
|
++m_position;
|
|
break;
|
|
case '$':
|
|
if((m_flags & format_sed) == 0)
|
|
{
|
|
format_perl();
|
|
break;
|
|
}
|
|
// not a special character:
|
|
BOOST_REGEX_FALLTHROUGH;
|
|
default:
|
|
put(*m_position);
|
|
++m_position;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_perl()
|
|
{
|
|
//
|
|
// On entry *m_position points to a '$' character
|
|
// output the information that goes with it:
|
|
//
|
|
BOOST_REGEX_ASSERT(*m_position == '$');
|
|
//
|
|
// see if this is a trailing '$':
|
|
//
|
|
if(++m_position == m_end)
|
|
{
|
|
--m_position;
|
|
put(*m_position);
|
|
++m_position;
|
|
return;
|
|
}
|
|
//
|
|
// OK find out what kind it is:
|
|
//
|
|
bool have_brace = false;
|
|
ForwardIter save_position = m_position;
|
|
switch(*m_position)
|
|
{
|
|
case '&':
|
|
++m_position;
|
|
put(this->m_results[0]);
|
|
break;
|
|
case '`':
|
|
++m_position;
|
|
put(this->m_results.prefix());
|
|
break;
|
|
case '\'':
|
|
++m_position;
|
|
put(this->m_results.suffix());
|
|
break;
|
|
case '$':
|
|
put(*m_position++);
|
|
break;
|
|
case '+':
|
|
if((++m_position != m_end) && (*m_position == '{'))
|
|
{
|
|
ForwardIter base = ++m_position;
|
|
while((m_position != m_end) && (*m_position != '}')) ++m_position;
|
|
if(m_position != m_end)
|
|
{
|
|
// Named sub-expression:
|
|
put(get_named_sub(base, m_position));
|
|
++m_position;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_position = --base;
|
|
}
|
|
}
|
|
put((this->m_results)[this->m_results.size() > 1 ? static_cast<int>(this->m_results.size() - 1) : 1]);
|
|
break;
|
|
case '{':
|
|
have_brace = true;
|
|
++m_position;
|
|
BOOST_REGEX_FALLTHROUGH;
|
|
default:
|
|
// see if we have a number:
|
|
{
|
|
std::ptrdiff_t len = std::distance(m_position, m_end);
|
|
//len = (std::min)(static_cast<std::ptrdiff_t>(2), len);
|
|
int v = this->toi(m_position, m_position + len, 10);
|
|
if((v < 0) || (have_brace && ((m_position == m_end) || (*m_position != '}'))))
|
|
{
|
|
// Look for a Perl-5.10 verb:
|
|
if(!handle_perl_verb(have_brace))
|
|
{
|
|
// leave the $ as is, and carry on:
|
|
m_position = --save_position;
|
|
put(*m_position);
|
|
++m_position;
|
|
}
|
|
break;
|
|
}
|
|
// otherwise output sub v:
|
|
put(this->m_results[v]);
|
|
if(have_brace)
|
|
++m_position;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
bool basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::handle_perl_verb(bool have_brace)
|
|
{
|
|
//
|
|
// We may have a capitalised string containing a Perl action:
|
|
//
|
|
static const char_type MATCH[] = { 'M', 'A', 'T', 'C', 'H' };
|
|
static const char_type PREMATCH[] = { 'P', 'R', 'E', 'M', 'A', 'T', 'C', 'H' };
|
|
static const char_type POSTMATCH[] = { 'P', 'O', 'S', 'T', 'M', 'A', 'T', 'C', 'H' };
|
|
static const char_type LAST_PAREN_MATCH[] = { 'L', 'A', 'S', 'T', '_', 'P', 'A', 'R', 'E', 'N', '_', 'M', 'A', 'T', 'C', 'H' };
|
|
static const char_type LAST_SUBMATCH_RESULT[] = { 'L', 'A', 'S', 'T', '_', 'S', 'U', 'B', 'M', 'A', 'T', 'C', 'H', '_', 'R', 'E', 'S', 'U', 'L', 'T' };
|
|
static const char_type LAST_SUBMATCH_RESULT_ALT[] = { '^', 'N' };
|
|
|
|
if(m_position == m_end)
|
|
return false;
|
|
if(have_brace && (*m_position == '^'))
|
|
++m_position;
|
|
|
|
std::ptrdiff_t max_len = m_end - m_position;
|
|
|
|
if((max_len >= 5) && std::equal(m_position, m_position + 5, MATCH))
|
|
{
|
|
m_position += 5;
|
|
if(have_brace)
|
|
{
|
|
if((m_position != m_end) && (*m_position == '}'))
|
|
++m_position;
|
|
else
|
|
{
|
|
m_position -= 5;
|
|
return false;
|
|
}
|
|
}
|
|
put(this->m_results[0]);
|
|
return true;
|
|
}
|
|
if((max_len >= 8) && std::equal(m_position, m_position + 8, PREMATCH))
|
|
{
|
|
m_position += 8;
|
|
if(have_brace)
|
|
{
|
|
if((m_position != m_end) && (*m_position == '}'))
|
|
++m_position;
|
|
else
|
|
{
|
|
m_position -= 8;
|
|
return false;
|
|
}
|
|
}
|
|
put(this->m_results.prefix());
|
|
return true;
|
|
}
|
|
if((max_len >= 9) && std::equal(m_position, m_position + 9, POSTMATCH))
|
|
{
|
|
m_position += 9;
|
|
if(have_brace)
|
|
{
|
|
if((m_position != m_end) && (*m_position == '}'))
|
|
++m_position;
|
|
else
|
|
{
|
|
m_position -= 9;
|
|
return false;
|
|
}
|
|
}
|
|
put(this->m_results.suffix());
|
|
return true;
|
|
}
|
|
if((max_len >= 16) && std::equal(m_position, m_position + 16, LAST_PAREN_MATCH))
|
|
{
|
|
m_position += 16;
|
|
if(have_brace)
|
|
{
|
|
if((m_position != m_end) && (*m_position == '}'))
|
|
++m_position;
|
|
else
|
|
{
|
|
m_position -= 16;
|
|
return false;
|
|
}
|
|
}
|
|
put((this->m_results)[this->m_results.size() > 1 ? static_cast<int>(this->m_results.size() - 1) : 1]);
|
|
return true;
|
|
}
|
|
if((max_len >= 20) && std::equal(m_position, m_position + 20, LAST_SUBMATCH_RESULT))
|
|
{
|
|
m_position += 20;
|
|
if(have_brace)
|
|
{
|
|
if((m_position != m_end) && (*m_position == '}'))
|
|
++m_position;
|
|
else
|
|
{
|
|
m_position -= 20;
|
|
return false;
|
|
}
|
|
}
|
|
put(this->m_results.get_last_closed_paren());
|
|
return true;
|
|
}
|
|
if((max_len >= 2) && std::equal(m_position, m_position + 2, LAST_SUBMATCH_RESULT_ALT))
|
|
{
|
|
m_position += 2;
|
|
if(have_brace)
|
|
{
|
|
if((m_position != m_end) && (*m_position == '}'))
|
|
++m_position;
|
|
else
|
|
{
|
|
m_position -= 2;
|
|
return false;
|
|
}
|
|
}
|
|
put(this->m_results.get_last_closed_paren());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_escape()
|
|
{
|
|
// skip the escape and check for trailing escape:
|
|
if(++m_position == m_end)
|
|
{
|
|
put(static_cast<char_type>('\\'));
|
|
return;
|
|
}
|
|
// now switch on the escape type:
|
|
switch(*m_position)
|
|
{
|
|
case 'a':
|
|
put(static_cast<char_type>('\a'));
|
|
++m_position;
|
|
break;
|
|
case 'f':
|
|
put(static_cast<char_type>('\f'));
|
|
++m_position;
|
|
break;
|
|
case 'n':
|
|
put(static_cast<char_type>('\n'));
|
|
++m_position;
|
|
break;
|
|
case 'r':
|
|
put(static_cast<char_type>('\r'));
|
|
++m_position;
|
|
break;
|
|
case 't':
|
|
put(static_cast<char_type>('\t'));
|
|
++m_position;
|
|
break;
|
|
case 'v':
|
|
put(static_cast<char_type>('\v'));
|
|
++m_position;
|
|
break;
|
|
case 'x':
|
|
if(++m_position == m_end)
|
|
{
|
|
put(static_cast<char_type>('x'));
|
|
return;
|
|
}
|
|
// maybe have \x{ddd}
|
|
if(*m_position == static_cast<char_type>('{'))
|
|
{
|
|
++m_position;
|
|
int val = this->toi(m_position, m_end, 16);
|
|
if(val < 0)
|
|
{
|
|
// invalid value treat everything as literals:
|
|
put(static_cast<char_type>('x'));
|
|
put(static_cast<char_type>('{'));
|
|
return;
|
|
}
|
|
if((m_position == m_end) || (*m_position != static_cast<char_type>('}')))
|
|
{
|
|
--m_position;
|
|
while(*m_position != static_cast<char_type>('\\'))
|
|
--m_position;
|
|
++m_position;
|
|
put(*m_position++);
|
|
return;
|
|
}
|
|
++m_position;
|
|
put(static_cast<char_type>(val));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
std::ptrdiff_t len = std::distance(m_position, m_end);
|
|
len = (std::min)(static_cast<std::ptrdiff_t>(2), len);
|
|
int val = this->toi(m_position, m_position + len, 16);
|
|
if(val < 0)
|
|
{
|
|
--m_position;
|
|
put(*m_position++);
|
|
return;
|
|
}
|
|
put(static_cast<char_type>(val));
|
|
}
|
|
break;
|
|
case 'c':
|
|
if(++m_position == m_end)
|
|
{
|
|
--m_position;
|
|
put(*m_position++);
|
|
return;
|
|
}
|
|
put(static_cast<char_type>(*m_position++ % 32));
|
|
break;
|
|
case 'e':
|
|
put(static_cast<char_type>(27));
|
|
++m_position;
|
|
break;
|
|
default:
|
|
// see if we have a perl specific escape:
|
|
if((m_flags & boost::regex_constants::format_sed) == 0)
|
|
{
|
|
bool breakout = false;
|
|
switch(*m_position)
|
|
{
|
|
case 'l':
|
|
++m_position;
|
|
m_restore_state = m_state;
|
|
m_state = output_next_lower;
|
|
breakout = true;
|
|
break;
|
|
case 'L':
|
|
++m_position;
|
|
m_state = output_lower;
|
|
breakout = true;
|
|
break;
|
|
case 'u':
|
|
++m_position;
|
|
m_restore_state = m_state;
|
|
m_state = output_next_upper;
|
|
breakout = true;
|
|
break;
|
|
case 'U':
|
|
++m_position;
|
|
m_state = output_upper;
|
|
breakout = true;
|
|
break;
|
|
case 'E':
|
|
++m_position;
|
|
m_state = output_copy;
|
|
breakout = true;
|
|
break;
|
|
}
|
|
if(breakout)
|
|
break;
|
|
}
|
|
// see if we have a \n sed style backreference:
|
|
std::ptrdiff_t len = std::distance(m_position, m_end);
|
|
len = (std::min)(static_cast<std::ptrdiff_t>(1), len);
|
|
int v = this->toi(m_position, m_position+len, 10);
|
|
if((v > 0) || ((v == 0) && (m_flags & ::boost::regex_constants::format_sed)))
|
|
{
|
|
put(m_results[v]);
|
|
break;
|
|
}
|
|
else if(v == 0)
|
|
{
|
|
// octal ecape sequence:
|
|
--m_position;
|
|
len = std::distance(m_position, m_end);
|
|
len = (std::min)(static_cast<std::ptrdiff_t>(4), len);
|
|
v = this->toi(m_position, m_position + len, 8);
|
|
BOOST_REGEX_ASSERT(v >= 0);
|
|
put(static_cast<char_type>(v));
|
|
break;
|
|
}
|
|
// Otherwise output the character "as is":
|
|
put(*m_position++);
|
|
break;
|
|
}
|
|
}
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_conditional()
|
|
{
|
|
if(m_position == m_end)
|
|
{
|
|
// oops trailing '?':
|
|
put(static_cast<char_type>('?'));
|
|
return;
|
|
}
|
|
int v;
|
|
if(*m_position == '{')
|
|
{
|
|
ForwardIter base = m_position;
|
|
++m_position;
|
|
v = this->toi(m_position, m_end, 10);
|
|
if(v < 0)
|
|
{
|
|
// Try a named subexpression:
|
|
while((m_position != m_end) && (*m_position != '}'))
|
|
++m_position;
|
|
v = this->get_named_sub_index(base + 1, m_position);
|
|
}
|
|
if((v < 0) || (*m_position != '}'))
|
|
{
|
|
m_position = base;
|
|
// oops trailing '?':
|
|
put(static_cast<char_type>('?'));
|
|
return;
|
|
}
|
|
// Skip trailing '}':
|
|
++m_position;
|
|
}
|
|
else
|
|
{
|
|
std::ptrdiff_t len = std::distance(m_position, m_end);
|
|
len = (std::min)(static_cast<std::ptrdiff_t>(2), len);
|
|
v = this->toi(m_position, m_position + len, 10);
|
|
}
|
|
if(v < 0)
|
|
{
|
|
// oops not a number:
|
|
put(static_cast<char_type>('?'));
|
|
return;
|
|
}
|
|
|
|
// output varies depending upon whether sub-expression v matched or not:
|
|
if(m_results[v].matched)
|
|
{
|
|
m_have_conditional = true;
|
|
format_all();
|
|
m_have_conditional = false;
|
|
if((m_position != m_end) && (*m_position == static_cast<char_type>(':')))
|
|
{
|
|
// skip the ':':
|
|
++m_position;
|
|
// save output state, then turn it off:
|
|
output_state saved_state = m_state;
|
|
m_state = output_none;
|
|
// format the rest of this scope:
|
|
format_until_scope_end();
|
|
// restore output state:
|
|
m_state = saved_state;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// save output state, then turn it off:
|
|
output_state saved_state = m_state;
|
|
m_state = output_none;
|
|
// format until ':' or ')':
|
|
m_have_conditional = true;
|
|
format_all();
|
|
m_have_conditional = false;
|
|
// restore state:
|
|
m_state = saved_state;
|
|
if((m_position != m_end) && (*m_position == static_cast<char_type>(':')))
|
|
{
|
|
// skip the ':':
|
|
++m_position;
|
|
// format the rest of this scope:
|
|
format_until_scope_end();
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::format_until_scope_end()
|
|
{
|
|
do
|
|
{
|
|
format_all();
|
|
if((m_position == m_end) || (*m_position == static_cast<char_type>(')')))
|
|
return;
|
|
put(*m_position++);
|
|
}while(m_position != m_end);
|
|
}
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::put(char_type c)
|
|
{
|
|
// write a single character to output
|
|
// according to which case translation mode we are in:
|
|
switch(this->m_state)
|
|
{
|
|
case output_none:
|
|
return;
|
|
case output_next_lower:
|
|
c = m_traits.tolower(c);
|
|
this->m_state = m_restore_state;
|
|
break;
|
|
case output_next_upper:
|
|
c = m_traits.toupper(c);
|
|
this->m_state = m_restore_state;
|
|
break;
|
|
case output_lower:
|
|
c = m_traits.tolower(c);
|
|
break;
|
|
case output_upper:
|
|
c = m_traits.toupper(c);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
*m_out = c;
|
|
++m_out;
|
|
}
|
|
|
|
template <class OutputIterator, class Results, class traits, class ForwardIter>
|
|
void basic_regex_formatter<OutputIterator, Results, traits, ForwardIter>::put(const sub_match_type& sub)
|
|
{
|
|
typedef typename sub_match_type::iterator iterator_type;
|
|
iterator_type i = sub.first;
|
|
while(i != sub.second)
|
|
{
|
|
put(*i);
|
|
++i;
|
|
}
|
|
}
|
|
|
|
template <class S>
|
|
class string_out_iterator
|
|
{
|
|
S* out;
|
|
public:
|
|
string_out_iterator(S& s) : out(&s) {}
|
|
string_out_iterator& operator++() { return *this; }
|
|
string_out_iterator& operator++(int) { return *this; }
|
|
string_out_iterator& operator*() { return *this; }
|
|
string_out_iterator& operator=(typename S::value_type v)
|
|
{
|
|
out->append(1, v);
|
|
return *this;
|
|
}
|
|
|
|
typedef std::ptrdiff_t difference_type;
|
|
typedef typename S::value_type value_type;
|
|
typedef value_type* pointer;
|
|
typedef value_type& reference;
|
|
typedef std::output_iterator_tag iterator_category;
|
|
};
|
|
|
|
template <class OutputIterator, class Iterator, class Alloc, class ForwardIter, class traits>
|
|
OutputIterator regex_format_imp(OutputIterator out,
|
|
const match_results<Iterator, Alloc>& m,
|
|
ForwardIter p1, ForwardIter p2,
|
|
match_flag_type flags,
|
|
const traits& t
|
|
)
|
|
{
|
|
if(flags & regex_constants::format_literal)
|
|
{
|
|
return BOOST_REGEX_DETAIL_NS::copy(p1, p2, out);
|
|
}
|
|
|
|
BOOST_REGEX_DETAIL_NS::basic_regex_formatter<
|
|
OutputIterator,
|
|
match_results<Iterator, Alloc>,
|
|
traits, ForwardIter> f(out, m, t);
|
|
return f.format(p1, p2, flags);
|
|
}
|
|
|
|
template <class T>
|
|
struct has_const_iterator
|
|
{
|
|
template <class U>
|
|
static typename U::const_iterator tester(U*);
|
|
static char tester(...);
|
|
|
|
static T* get();
|
|
|
|
static const bool value = sizeof(tester(get())) != sizeof(char);
|
|
};
|
|
|
|
struct any_type
|
|
{
|
|
template <class T>
|
|
any_type(const T&);
|
|
template <class T, class U>
|
|
any_type(const T&, const U&);
|
|
template <class T, class U, class V>
|
|
any_type(const T&, const U&, const V&);
|
|
};
|
|
typedef char no_type;
|
|
typedef char (&unary_type)[2];
|
|
typedef char (&binary_type)[3];
|
|
typedef char (&ternary_type)[4];
|
|
|
|
no_type check_is_formatter(unary_type, binary_type, ternary_type);
|
|
template<typename T>
|
|
unary_type check_is_formatter(T const &, binary_type, ternary_type);
|
|
template<typename T>
|
|
binary_type check_is_formatter(unary_type, T const &, ternary_type);
|
|
template<typename T, typename U>
|
|
binary_type check_is_formatter(T const &, U const &, ternary_type);
|
|
template<typename T>
|
|
ternary_type check_is_formatter(unary_type, binary_type, T const &);
|
|
template<typename T, typename U>
|
|
ternary_type check_is_formatter(T const &, binary_type, U const &);
|
|
template<typename T, typename U>
|
|
ternary_type check_is_formatter(unary_type, T const &, U const &);
|
|
template<typename T, typename U, typename V>
|
|
ternary_type check_is_formatter(T const &, U const &, V const &);
|
|
|
|
struct unary_binary_ternary
|
|
{
|
|
typedef unary_type (*unary_fun)(any_type);
|
|
typedef binary_type (*binary_fun)(any_type, any_type);
|
|
typedef ternary_type (*ternary_fun)(any_type, any_type, any_type);
|
|
operator unary_fun();
|
|
operator binary_fun();
|
|
operator ternary_fun();
|
|
};
|
|
|
|
template<typename Formatter, bool IsFunction = std::is_function<Formatter>::value>
|
|
struct formatter_wrapper
|
|
: Formatter
|
|
, unary_binary_ternary
|
|
{
|
|
formatter_wrapper(){}
|
|
};
|
|
|
|
template<typename Formatter>
|
|
struct formatter_wrapper<Formatter, true>
|
|
: unary_binary_ternary
|
|
{
|
|
operator Formatter *();
|
|
};
|
|
|
|
template<typename Formatter>
|
|
struct formatter_wrapper<Formatter *, false>
|
|
: unary_binary_ternary
|
|
{
|
|
operator Formatter *();
|
|
};
|
|
|
|
template <class T>
|
|
struct do_unwrap_reference
|
|
{
|
|
typedef T type;
|
|
};
|
|
template <class T>
|
|
struct do_unwrap_reference<std::reference_wrapper<T> >
|
|
{
|
|
typedef T type;
|
|
};
|
|
|
|
template <class T>
|
|
T& do_unwrap_ref(T& r) { return r; }
|
|
template <class T>
|
|
T& do_unwrap_ref(std::reference_wrapper<T> const& r) { return r.get(); }
|
|
|
|
template <class F, class M, class O>
|
|
struct format_traits_imp
|
|
{
|
|
private:
|
|
//
|
|
// F must be a pointer, a function, or a class with a function call operator:
|
|
//
|
|
static_assert((::std::is_pointer<F>::value || ::std::is_function<F>::value || ::std::is_class<F>::value), "The functor must be a pointer or a class with a function call operator");
|
|
static formatter_wrapper<typename do_unwrap_reference<F>::type> f;
|
|
static M m;
|
|
static O out;
|
|
static boost::regex_constants::match_flag_type flags;
|
|
public:
|
|
static const int value = sizeof(check_is_formatter(f(m), f(m, out), f(m, out, flags)));
|
|
};
|
|
|
|
template <class F, class M, class O>
|
|
struct format_traits
|
|
{
|
|
public:
|
|
//
|
|
// Type is std::integral_constant<int, N> where N is one of:
|
|
//
|
|
// 0 : F is a pointer to a presumably null-terminated string.
|
|
// 1 : F is a character-container such as a std::string.
|
|
// 2 : F is a Unary Functor.
|
|
// 3 : F is a Binary Functor.
|
|
// 4 : F is a Ternary Functor.
|
|
//
|
|
typedef typename std::conditional<
|
|
std::is_pointer<F>::value && !std::is_function<typename std::remove_pointer<F>::type>::value,
|
|
std::integral_constant<int, 0>,
|
|
typename std::conditional<
|
|
has_const_iterator<F>::value,
|
|
std::integral_constant<int, 1>,
|
|
std::integral_constant<int, format_traits_imp<F, M, O>::value>
|
|
>::type
|
|
>::type type;
|
|
//
|
|
// This static assertion will fail if the functor passed does not accept
|
|
// the same type of arguments passed.
|
|
//
|
|
static_assert( std::is_class<F>::value && !has_const_iterator<F>::value ? (type::value > 1) : true, "Argument mismatch in Functor type");
|
|
};
|
|
|
|
template <class Base, class Match>
|
|
struct format_functor3
|
|
{
|
|
format_functor3(Base b) : func(b) {}
|
|
template <class OutputIter>
|
|
OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f)
|
|
{
|
|
return do_unwrap_ref(func)(m, i, f);
|
|
}
|
|
template <class OutputIter, class Traits>
|
|
OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)
|
|
{
|
|
return (*this)(m, i, f);
|
|
}
|
|
private:
|
|
Base func;
|
|
format_functor3(const format_functor3&);
|
|
format_functor3& operator=(const format_functor3&);
|
|
};
|
|
|
|
template <class Base, class Match>
|
|
struct format_functor2
|
|
{
|
|
format_functor2(Base b) : func(b) {}
|
|
template <class OutputIter>
|
|
OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/)
|
|
{
|
|
return do_unwrap_ref(func)(m, i);
|
|
}
|
|
template <class OutputIter, class Traits>
|
|
OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)
|
|
{
|
|
return (*this)(m, i, f);
|
|
}
|
|
private:
|
|
Base func;
|
|
format_functor2(const format_functor2&);
|
|
format_functor2& operator=(const format_functor2&);
|
|
};
|
|
|
|
template <class Base, class Match>
|
|
struct format_functor1
|
|
{
|
|
format_functor1(Base b) : func(b) {}
|
|
|
|
template <class S, class OutputIter>
|
|
OutputIter do_format_string(const S& s, OutputIter i)
|
|
{
|
|
return std::copy(s.begin(), s.end(), i);
|
|
}
|
|
template <class S, class OutputIter>
|
|
inline OutputIter do_format_string(const S* s, OutputIter i)
|
|
{
|
|
while(s && *s)
|
|
{
|
|
*i = *s;
|
|
++i;
|
|
++s;
|
|
}
|
|
return i;
|
|
}
|
|
template <class OutputIter>
|
|
OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type /*f*/)
|
|
{
|
|
return do_format_string(do_unwrap_ref(func)(m), i);
|
|
}
|
|
template <class OutputIter, class Traits>
|
|
OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits&)
|
|
{
|
|
return (*this)(m, i, f);
|
|
}
|
|
private:
|
|
Base func;
|
|
format_functor1(const format_functor1&);
|
|
format_functor1& operator=(const format_functor1&);
|
|
};
|
|
|
|
template <class charT, class Match, class Traits>
|
|
struct format_functor_c_string
|
|
{
|
|
format_functor_c_string(const charT* ps) : func(ps) {}
|
|
|
|
template <class OutputIter>
|
|
OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits())
|
|
{
|
|
//typedef typename Match::char_type char_type;
|
|
const charT* end = func;
|
|
while(*end) ++end;
|
|
return regex_format_imp(i, m, func, end, f, t);
|
|
}
|
|
private:
|
|
const charT* func;
|
|
format_functor_c_string(const format_functor_c_string&);
|
|
format_functor_c_string& operator=(const format_functor_c_string&);
|
|
};
|
|
|
|
template <class Container, class Match, class Traits>
|
|
struct format_functor_container
|
|
{
|
|
format_functor_container(const Container& c) : func(c) {}
|
|
|
|
template <class OutputIter>
|
|
OutputIter operator()(const Match& m, OutputIter i, boost::regex_constants::match_flag_type f, const Traits& t = Traits())
|
|
{
|
|
//typedef typename Match::char_type char_type;
|
|
return BOOST_REGEX_DETAIL_NS::regex_format_imp(i, m, func.begin(), func.end(), f, t);
|
|
}
|
|
private:
|
|
const Container& func;
|
|
format_functor_container(const format_functor_container&);
|
|
format_functor_container& operator=(const format_functor_container&);
|
|
};
|
|
|
|
template <class Func, class Match, class OutputIterator, class Traits = BOOST_REGEX_DETAIL_NS::trivial_format_traits<typename Match::char_type> >
|
|
struct compute_functor_type
|
|
{
|
|
typedef typename format_traits<Func, Match, OutputIterator>::type tag;
|
|
typedef typename std::remove_cv< typename std::remove_pointer<Func>::type>::type maybe_char_type;
|
|
|
|
typedef typename std::conditional<
|
|
tag::value == 0, format_functor_c_string<maybe_char_type, Match, Traits>,
|
|
typename std::conditional<
|
|
tag::value == 1, format_functor_container<Func, Match, Traits>,
|
|
typename std::conditional<
|
|
tag::value == 2, format_functor1<Func, Match>,
|
|
typename std::conditional<
|
|
tag::value == 3, format_functor2<Func, Match>,
|
|
format_functor3<Func, Match>
|
|
>::type
|
|
>::type
|
|
>::type
|
|
>::type type;
|
|
};
|
|
|
|
} // namespace BOOST_REGEX_DETAIL_NS
|
|
|
|
template <class OutputIterator, class Iterator, class Allocator, class Functor>
|
|
inline OutputIterator regex_format(OutputIterator out,
|
|
const match_results<Iterator, Allocator>& m,
|
|
Functor fmt,
|
|
match_flag_type flags = format_all
|
|
)
|
|
{
|
|
return m.format(out, fmt, flags);
|
|
}
|
|
|
|
template <class Iterator, class Allocator, class Functor>
|
|
inline std::basic_string<typename match_results<Iterator, Allocator>::char_type> regex_format(const match_results<Iterator, Allocator>& m,
|
|
Functor fmt,
|
|
match_flag_type flags = format_all)
|
|
{
|
|
return m.format(fmt, flags);
|
|
}
|
|
|
|
} // namespace boost
|
|
|
|
#endif // BOOST_REGEX_FORMAT_HPP
|
|
|
|
|
|
|
|
|
|
|
|
|