You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
547 lines
20 KiB
547 lines
20 KiB
// 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 |
|
{ |
|
#ifndef BOOST_SPIRIT_NO_PREDEFINED_TERMINALS |
|
using spirit::lit; // lit('x') is equivalent to 'x' |
|
#endif |
|
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
|
|
|