// Copyright (c) 2001-2011 Hartmut Kaiser | |
// Copyright (c) 2010 Bryce Lelbach | |
// | |
// Distributed under 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) | |
#if !defined(BOOST_SPIRIT_KARMA_CHAR_FEB_21_2007_0543PM) | |
#define BOOST_SPIRIT_KARMA_CHAR_FEB_21_2007_0543PM | |
#if defined(_MSC_VER) | |
#pragma once | |
#endif | |
#include <boost/spirit/home/support/common_terminals.hpp> | |
#include <boost/spirit/home/support/string_traits.hpp> | |
#include <boost/spirit/home/support/info.hpp> | |
#include <boost/spirit/home/support/char_class.hpp> | |
#include <boost/spirit/home/support/detail/get_encoding.hpp> | |
#include <boost/spirit/home/support/char_set/basic_chset.hpp> | |
#include <boost/spirit/home/karma/domain.hpp> | |
#include <boost/spirit/home/karma/meta_compiler.hpp> | |
#include <boost/spirit/home/karma/delimit_out.hpp> | |
#include <boost/spirit/home/karma/char/char_generator.hpp> | |
#include <boost/spirit/home/karma/auxiliary/lazy.hpp> | |
#include <boost/spirit/home/karma/detail/get_casetag.hpp> | |
#include <boost/spirit/home/karma/detail/generate_to.hpp> | |
#include <boost/spirit/home/karma/detail/enable_lit.hpp> | |
#include <boost/fusion/include/at.hpp> | |
#include <boost/fusion/include/vector.hpp> | |
#include <boost/fusion/include/cons.hpp> | |
#include <boost/mpl/if.hpp> | |
#include <boost/mpl/assert.hpp> | |
#include <boost/mpl/bool.hpp> | |
#include <boost/utility/enable_if.hpp> | |
#include <string> | |
/////////////////////////////////////////////////////////////////////////////// | |
namespace boost { namespace spirit | |
{ | |
/////////////////////////////////////////////////////////////////////////// | |
// Enablers | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename CharEncoding> | |
struct use_terminal<karma::domain | |
, tag::char_code<tag::char_, CharEncoding> // enables char_ | |
> : mpl::true_ {}; | |
template <typename CharEncoding, typename A0> | |
struct use_terminal<karma::domain | |
, terminal_ex< | |
tag::char_code<tag::char_, CharEncoding> // enables char_('x'), char_("x") | |
, fusion::vector1<A0> | |
> | |
> : mpl::true_ {}; | |
template <typename A0> | |
struct use_terminal<karma::domain | |
, terminal_ex<tag::lit, fusion::vector1<A0> > // enables lit('x') | |
, typename enable_if<traits::is_char<A0> >::type> | |
: mpl::true_ {}; | |
template <typename CharEncoding, typename A0, typename A1> | |
struct use_terminal<karma::domain | |
, terminal_ex< | |
tag::char_code<tag::char_, CharEncoding> // enables char_('a','z') | |
, fusion::vector2<A0, A1> | |
> | |
> : mpl::true_ {}; | |
template <typename CharEncoding> // enables *lazy* char_('x'), char_("x") | |
struct use_lazy_terminal< | |
karma::domain | |
, tag::char_code<tag::char_, CharEncoding> | |
, 1 // arity | |
> : mpl::true_ {}; | |
template <> | |
struct use_terminal<karma::domain, char> // enables 'x' | |
: mpl::true_ {}; | |
template <> | |
struct use_terminal<karma::domain, char[2]> // enables "x" | |
: mpl::true_ {}; | |
template <> | |
struct use_terminal<karma::domain, wchar_t> // enables L'x' | |
: mpl::true_ {}; | |
template <> | |
struct use_terminal<karma::domain, wchar_t[2]> // enables L"x" | |
: mpl::true_ {}; | |
}} | |
/////////////////////////////////////////////////////////////////////////////// | |
namespace boost { namespace spirit { namespace karma | |
{ | |
using spirit::lit; // lit('x') is equivalent to 'x' | |
using spirit::lit_type; | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// any_char | |
// generates a single character from the associated attribute | |
// | |
// Note: this generator has to have an associated attribute | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename CharEncoding, typename Tag> | |
struct any_char | |
: char_generator<any_char<CharEncoding, Tag>, CharEncoding, Tag> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
template <typename Context, typename Unused> | |
struct attribute | |
{ | |
typedef char_type type; | |
}; | |
// any_char has an attached parameter | |
template <typename Attribute, typename CharParam, typename Context> | |
bool test(Attribute const& attr, CharParam& ch, Context&) const | |
{ | |
ch = CharParam(attr); | |
return true; | |
} | |
// any_char has no attribute attached, it needs to have been | |
// initialized from a direct literal | |
template <typename CharParam, typename Context> | |
bool test(unused_type, CharParam&, Context&) const | |
{ | |
// It is not possible (doesn't make sense) to use char_ without | |
// providing any attribute, as the generator doesn't 'know' what | |
// character to output. The following assertion fires if this | |
// situation is detected in your code. | |
BOOST_SPIRIT_ASSERT_MSG(false, char_not_usable_without_attribute, ()); | |
return false; | |
} | |
template <typename Context> | |
static info what(Context const& /*context*/) | |
{ | |
return info("any-char"); | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// | |
// literal_char | |
// generates a single character given by a literal it was initialized | |
// from | |
// | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename CharEncoding, typename Tag, bool no_attribute> | |
struct literal_char | |
: char_generator<literal_char<CharEncoding, Tag, no_attribute> | |
, CharEncoding, Tag> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
literal_char(char_type ch) | |
: ch (spirit::char_class::convert<char_encoding>::to(Tag(), ch)) | |
{} | |
template <typename Context, typename Unused> | |
struct attribute | |
: mpl::if_c<no_attribute, unused_type, char_type> | |
{}; | |
// A char_('x') which additionally has an associated attribute emits | |
// its immediate literal only if it matches the attribute, otherwise | |
// it fails. | |
// any_char has an attached parameter | |
template <typename Attribute, typename CharParam, typename Context> | |
bool test(Attribute const& attr, CharParam& ch_, Context&) const | |
{ | |
// fail if attribute isn't matched my immediate literal | |
ch_ = attr; | |
return attr == ch; | |
} | |
// A char_('x') without any associated attribute just emits its | |
// immediate literal | |
template <typename CharParam, typename Context> | |
bool test(unused_type, CharParam& ch_, Context&) const | |
{ | |
ch_ = ch; | |
return true; | |
} | |
template <typename Context> | |
info what(Context const& /*context*/) const | |
{ | |
return info("literal-char", char_encoding::toucs4(ch)); | |
} | |
char_type ch; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// char range generator | |
template <typename CharEncoding, typename Tag> | |
struct char_range | |
: char_generator<char_range<CharEncoding, Tag>, CharEncoding, Tag> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
char_range(char_type from, char_type to) | |
: from(spirit::char_class::convert<char_encoding>::to(Tag(), from)) | |
, to(spirit::char_class::convert<char_encoding>::to(Tag(), to)) | |
{} | |
// A char_('a', 'z') which has an associated attribute emits it only if | |
// it matches the character range, otherwise it fails. | |
template <typename Attribute, typename CharParam, typename Context> | |
bool test(Attribute const& attr, CharParam& ch, Context&) const | |
{ | |
// fail if attribute doesn't belong to character range | |
ch = attr; | |
return (from <= char_type(attr)) && (char_type(attr) <= to); | |
} | |
// A char_('a', 'z') without any associated attribute fails compiling | |
template <typename CharParam, typename Context> | |
bool test(unused_type, CharParam&, Context&) const | |
{ | |
// It is not possible (doesn't make sense) to use char_ generators | |
// without providing any attribute, as the generator doesn't 'know' | |
// what to output. The following assertion fires if this situation | |
// is detected in your code. | |
BOOST_SPIRIT_ASSERT_MSG(false | |
, char_range_not_usable_without_attribute, ()); | |
return false; | |
} | |
template <typename Context> | |
info what(Context& /*context*/) const | |
{ | |
info result("char-range", char_encoding::toucs4(from)); | |
boost::get<std::string>(result.value) += '-'; | |
boost::get<std::string>(result.value) += to_utf8(char_encoding::toucs4(to)); | |
return result; | |
} | |
char_type from, to; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// character set generator | |
template <typename CharEncoding, typename Tag, bool no_attribute> | |
struct char_set | |
: char_generator<char_set<CharEncoding, Tag, no_attribute> | |
, CharEncoding, Tag> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
template <typename Context, typename Unused> | |
struct attribute | |
: mpl::if_c<no_attribute, unused_type, char_type> | |
{}; | |
template <typename String> | |
char_set(String const& str) | |
{ | |
typedef typename traits::char_type_of<String>::type in_type; | |
BOOST_SPIRIT_ASSERT_MSG(( | |
(sizeof(char_type) == sizeof(in_type)) | |
), cannot_convert_string, (String)); | |
typedef spirit::char_class::convert<char_encoding> convert_type; | |
char_type const* definition = | |
(char_type const*)traits::get_c_string(str); | |
char_type ch = convert_type::to(Tag(), *definition++); | |
while (ch) | |
{ | |
char_type next = convert_type::to(Tag(), *definition++); | |
if (next == '-') | |
{ | |
next = convert_type::to(Tag(), *definition++); | |
if (next == 0) | |
{ | |
chset.set(ch); | |
chset.set('-'); | |
break; | |
} | |
chset.set(ch, next); | |
} | |
else | |
{ | |
chset.set(ch); | |
} | |
ch = next; | |
} | |
} | |
// A char_("a-z") which has an associated attribute emits it only if | |
// it matches the character set, otherwise it fails. | |
template <typename Attribute, typename CharParam, typename Context> | |
bool test(Attribute const& attr, CharParam& ch, Context&) const | |
{ | |
// fail if attribute doesn't belong to character set | |
ch = attr; | |
return chset.test(char_type(attr)); | |
} | |
// A char_("a-z") without any associated attribute fails compiling | |
template <typename CharParam, typename Context> | |
bool test(unused_type, CharParam&, Context&) const | |
{ | |
// It is not possible (doesn't make sense) to use char_ generators | |
// without providing any attribute, as the generator doesn't 'know' | |
// what to output. The following assertion fires if this situation | |
// is detected in your code. | |
BOOST_SPIRIT_ASSERT_MSG(false | |
, char_set_not_usable_without_attribute, ()); | |
return false; | |
} | |
template <typename Context> | |
info what(Context& /*context*/) const | |
{ | |
return info("char-set"); | |
} | |
support::detail::basic_chset<char_type> chset; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// Generator generators: make_xxx function (objects) | |
/////////////////////////////////////////////////////////////////////////// | |
namespace detail | |
{ | |
template <typename Modifiers, typename Encoding> | |
struct basic_literal | |
{ | |
static bool const lower = | |
has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; | |
static bool const upper = | |
has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; | |
typedef literal_char< | |
typename spirit::detail::get_encoding_with_case< | |
Modifiers, Encoding, lower || upper>::type | |
, typename get_casetag<Modifiers, lower || upper>::type | |
, true> | |
result_type; | |
template <typename Char> | |
result_type operator()(Char ch, unused_type) const | |
{ | |
return result_type(ch); | |
} | |
template <typename Char> | |
result_type operator()(Char const* str, unused_type) const | |
{ | |
return result_type(str[0]); | |
} | |
}; | |
} | |
// literals: 'x', "x" | |
template <typename Modifiers> | |
struct make_primitive<char, Modifiers> | |
: detail::basic_literal<Modifiers, char_encoding::standard> {}; | |
template <typename Modifiers> | |
struct make_primitive<char const(&)[2], Modifiers> | |
: detail::basic_literal<Modifiers, char_encoding::standard> {}; | |
// literals: L'x', L"x" | |
template <typename Modifiers> | |
struct make_primitive<wchar_t, Modifiers> | |
: detail::basic_literal<Modifiers, char_encoding::standard_wide> {}; | |
template <typename Modifiers> | |
struct make_primitive<wchar_t const(&)[2], Modifiers> | |
: detail::basic_literal<Modifiers, char_encoding::standard_wide> {}; | |
// char_ | |
template <typename CharEncoding, typename Modifiers> | |
struct make_primitive<tag::char_code<tag::char_, CharEncoding>, Modifiers> | |
{ | |
static bool const lower = | |
has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; | |
static bool const upper = | |
has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; | |
typedef any_char< | |
typename spirit::detail::get_encoding_with_case< | |
Modifiers, CharEncoding, lower || upper>::type | |
, typename detail::get_casetag<Modifiers, lower || upper>::type | |
> result_type; | |
result_type operator()(unused_type, unused_type) const | |
{ | |
return result_type(); | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
namespace detail | |
{ | |
template <typename CharEncoding, typename Modifiers, typename A0 | |
, bool no_attribute> | |
struct make_char_direct | |
{ | |
static bool const lower = | |
has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; | |
static bool const upper = | |
has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; | |
typedef typename spirit::detail::get_encoding_with_case< | |
Modifiers, CharEncoding, lower || upper>::type encoding; | |
typedef typename detail::get_casetag< | |
Modifiers, lower || upper>::type tag; | |
typedef typename mpl::if_< | |
traits::is_string<A0> | |
, char_set<encoding, tag, no_attribute> | |
, literal_char<encoding, tag, no_attribute> | |
>::type result_type; | |
template <typename Terminal> | |
result_type operator()(Terminal const& term, unused_type) const | |
{ | |
return result_type(fusion::at_c<0>(term.args)); | |
} | |
}; | |
} | |
// char_(...), lit(...) | |
template <typename CharEncoding, typename Modifiers, typename A0> | |
struct make_primitive< | |
terminal_ex< | |
tag::char_code<tag::char_, CharEncoding> | |
, fusion::vector1<A0> > | |
, Modifiers> | |
: detail::make_char_direct<CharEncoding, Modifiers, A0, false> | |
{}; | |
template <typename Modifiers, typename A0> | |
struct make_primitive< | |
terminal_ex<tag::lit, fusion::vector1<A0> > | |
, Modifiers | |
, typename enable_if<traits::is_char<A0> >::type> | |
: detail::make_char_direct< | |
typename traits::char_encoding_from_char< | |
typename traits::char_type_of<A0>::type>::type | |
, Modifiers, A0, true> | |
{}; | |
/////////////////////////////////////////////////////////////////////////// | |
// char_("x") | |
template <typename CharEncoding, typename Modifiers, typename Char> | |
struct make_primitive< | |
terminal_ex< | |
tag::char_code<tag::char_, CharEncoding> | |
, fusion::vector1<Char(&)[2]> > // For single char strings | |
, Modifiers> | |
{ | |
static bool const lower = | |
has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; | |
static bool const upper = | |
has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; | |
typedef literal_char< | |
typename spirit::detail::get_encoding_with_case< | |
Modifiers, CharEncoding, lower || upper>::type | |
, typename detail::get_casetag<Modifiers, lower || upper>::type | |
, false | |
> result_type; | |
template <typename Terminal> | |
result_type operator()(Terminal const& term, unused_type) const | |
{ | |
return result_type(fusion::at_c<0>(term.args)[0]); | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// char_('a', 'z') | |
template <typename CharEncoding, typename Modifiers, typename A0, typename A1> | |
struct make_primitive< | |
terminal_ex< | |
tag::char_code<tag::char_, CharEncoding> | |
, fusion::vector2<A0, A1> | |
> | |
, Modifiers> | |
{ | |
static bool const lower = | |
has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; | |
static bool const upper = | |
has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; | |
typedef char_range< | |
typename spirit::detail::get_encoding_with_case< | |
Modifiers, CharEncoding, lower || upper>::type | |
, typename detail::get_casetag<Modifiers, lower || upper>::type | |
> result_type; | |
template <typename Terminal> | |
result_type operator()(Terminal const& term, unused_type) const | |
{ | |
return result_type(fusion::at_c<0>(term.args) | |
, fusion::at_c<1>(term.args)); | |
} | |
}; | |
template <typename CharEncoding, typename Modifiers, typename Char> | |
struct make_primitive< | |
terminal_ex< | |
tag::char_code<tag::char_, CharEncoding> | |
, fusion::vector2<Char(&)[2], Char(&)[2]> // For single char strings | |
> | |
, Modifiers> | |
{ | |
static bool const lower = | |
has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; | |
static bool const upper = | |
has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; | |
typedef char_range< | |
typename spirit::detail::get_encoding_with_case< | |
Modifiers, CharEncoding, lower || upper>::type | |
, typename detail::get_casetag<Modifiers, lower || upper>::type | |
> result_type; | |
template <typename Terminal> | |
result_type operator()(Terminal const& term, unused_type) const | |
{ | |
return result_type(fusion::at_c<0>(term.args)[0] | |
, fusion::at_c<1>(term.args)[0]); | |
} | |
}; | |
}}} // namespace boost::spirit::karma | |
#endif |