/*============================================================================= | |
Copyright (c) 2001-2011 Joel de Guzman | |
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_CHAR_APRIL_16_2006_1051AM) | |
#define BOOST_SPIRIT_CHAR_APRIL_16_2006_1051AM | |
#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/detail/get_encoding.hpp> | |
#include <boost/spirit/home/support/char_set/basic_chset.hpp> | |
#include <boost/spirit/home/qi/char/char_parser.hpp> | |
#include <boost/spirit/home/qi/char/char_class.hpp> | |
#include <boost/spirit/home/qi/meta_compiler.hpp> | |
#include <boost/spirit/home/qi/auxiliary/lazy.hpp> | |
#include <boost/spirit/home/qi/detail/enable_lit.hpp> | |
#include <boost/fusion/include/at.hpp> | |
#include <boost/mpl/if.hpp> | |
#include <boost/mpl/assert.hpp> | |
#include <boost/mpl/identity.hpp> | |
#include <boost/utility/enable_if.hpp> | |
#include <boost/type_traits/remove_const.hpp> | |
#include <string> | |
#if defined(_MSC_VER) | |
#pragma once | |
#endif | |
namespace boost { namespace spirit | |
{ | |
/////////////////////////////////////////////////////////////////////////// | |
// Enablers | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename CharEncoding> | |
struct use_terminal<qi::domain | |
, terminal< | |
tag::char_code<tag::char_, CharEncoding> // enables char_ | |
> | |
> : mpl::true_ {}; | |
template <typename CharEncoding, typename A0> | |
struct use_terminal<qi::domain | |
, terminal_ex< | |
tag::char_code<tag::char_, CharEncoding> // enables char_('x'), char_("x") | |
, fusion::vector1<A0> // and char_("a-z0-9") | |
> | |
> : mpl::true_ {}; | |
template <typename CharEncoding, typename A0, typename A1> | |
struct use_terminal<qi::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< // and char_("a-z0-9") | |
qi::domain | |
, tag::char_code<tag::char_, CharEncoding> | |
, 1 // arity | |
> : mpl::true_ {}; | |
template <typename CharEncoding> // enables *lazy* char_('a','z') | |
struct use_lazy_terminal< | |
qi::domain | |
, tag::char_code<tag::char_, CharEncoding> | |
, 2 // arity | |
> : mpl::true_ {}; | |
template <> | |
struct use_terminal<qi::domain, char> // enables 'x' | |
: mpl::true_ {}; | |
template <> | |
struct use_terminal<qi::domain, char[2]> // enables "x" | |
: mpl::true_ {}; | |
template <> | |
struct use_terminal<qi::domain, wchar_t> // enables wchar_t | |
: mpl::true_ {}; | |
template <> | |
struct use_terminal<qi::domain, wchar_t[2]> // enables L"x" | |
: mpl::true_ {}; | |
// enables lit(...) | |
template <typename A0> | |
struct use_terminal<qi::domain | |
, terminal_ex<tag::lit, fusion::vector1<A0> > | |
, typename enable_if<traits::is_char<A0> >::type> | |
: mpl::true_ {}; | |
}} | |
namespace boost { namespace spirit { namespace qi | |
{ | |
using spirit::lit; // lit('x') is equivalent to 'x' | |
using spirit::lit_type; | |
/////////////////////////////////////////////////////////////////////////// | |
// Parser for a single character | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename CharEncoding, bool no_attribute, bool no_case = false> | |
struct literal_char | |
: char_parser< | |
literal_char<CharEncoding, no_attribute, false> | |
, typename CharEncoding::char_type | |
, typename mpl::if_c<no_attribute, unused_type | |
, typename CharEncoding::char_type>::type> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
template <typename Char> | |
literal_char(Char ch) | |
: ch(static_cast<char_type>(ch)) {} | |
template <typename Context, typename Iterator> | |
struct attribute | |
{ | |
typedef typename mpl::if_c< | |
no_attribute, unused_type, char_type>::type | |
type; | |
}; | |
template <typename CharParam, typename Context> | |
bool test(CharParam ch_, Context&) const | |
{ | |
return traits::ischar<CharParam, char_encoding>::call(ch_) && | |
ch == char_type(ch_); | |
} | |
template <typename Context> | |
info what(Context& /*context*/) const | |
{ | |
return info("literal-char", char_encoding::toucs4(ch)); | |
} | |
char_type ch; | |
}; | |
template <typename CharEncoding, bool no_attribute> | |
struct literal_char<CharEncoding, no_attribute, true> // case insensitive | |
: char_parser< | |
literal_char<CharEncoding, no_attribute, true> | |
, typename mpl::if_c<no_attribute, unused_type | |
, typename CharEncoding::char_type>::type> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
literal_char(char_type ch) | |
: lo(static_cast<char_type>(char_encoding::tolower(ch))) | |
, hi(static_cast<char_type>(char_encoding::toupper(ch))) {} | |
template <typename Context, typename Iterator> | |
struct attribute | |
{ | |
typedef typename mpl::if_c< | |
no_attribute, unused_type, char_type>::type | |
type; | |
}; | |
template <typename CharParam, typename Context> | |
bool test(CharParam ch_, Context&) const | |
{ | |
if (!traits::ischar<CharParam, char_encoding>::call(ch_)) | |
return false; | |
char_type ch = char_type(ch_); // optimize for token based parsing | |
return this->lo == ch || this->hi == ch; | |
} | |
template <typename Context> | |
info what(Context& /*context*/) const | |
{ | |
return info("no-case-literal-char", char_encoding::toucs4(lo)); | |
} | |
char_type lo, hi; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// Parser for a character range | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename CharEncoding, bool no_case = false> | |
struct char_range | |
: char_parser<char_range<CharEncoding, false>, typename CharEncoding::char_type> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
char_range(char_type from, char_type to) | |
: from(from), to(to) {} | |
template <typename CharParam, typename Context> | |
bool test(CharParam ch_, Context&) const | |
{ | |
if (!traits::ischar<CharParam, char_encoding>::call(ch_)) | |
return false; | |
char_type ch = char_type(ch_); // optimize for token based parsing | |
return !(ch < from) && !(to < ch); | |
} | |
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; | |
}; | |
template <typename CharEncoding> | |
struct char_range<CharEncoding, true> // case insensitive | |
: char_parser<char_range<CharEncoding, true>, typename CharEncoding::char_type> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
char_range(char_type from, char_type to) | |
: from_lo(static_cast<char_type>(char_encoding::tolower(from))) | |
, to_lo(static_cast<char_type>(char_encoding::tolower(to))) | |
, from_hi(static_cast<char_type>(char_encoding::toupper(from))) | |
, to_hi(static_cast<char_type>(char_encoding::toupper(to))) | |
{} | |
template <typename CharParam, typename Context> | |
bool test(CharParam ch_, Context&) const | |
{ | |
if (!traits::ischar<CharParam, char_encoding>::call(ch_)) | |
return false; | |
char_type ch = char_type(ch_); // optimize for token based parsing | |
return (!(ch < from_lo) && !(to_lo < ch)) | |
|| (!(ch < from_hi) && !(to_hi < ch)) | |
; | |
} | |
template <typename Context> | |
info what(Context& /*context*/) const | |
{ | |
info result("no-case-char-range", char_encoding::toucs4(from_lo)); | |
boost::get<std::string>(result.value) += '-'; | |
boost::get<std::string>(result.value) += to_utf8(char_encoding::toucs4(to_lo)); | |
return result; | |
} | |
char_type from_lo, to_lo, from_hi, to_hi; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// Parser for a character set | |
/////////////////////////////////////////////////////////////////////////// | |
template <typename CharEncoding, bool no_attribute, bool no_case = false> | |
struct char_set | |
: char_parser<char_set<CharEncoding, no_attribute, false> | |
, typename mpl::if_c<no_attribute, unused_type | |
, typename CharEncoding::char_type>::type> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
template <typename String> | |
char_set(String const& str) | |
{ | |
using spirit::detail::cast_char; | |
typedef typename | |
remove_const< | |
typename traits::char_type_of<String>::type | |
>::type | |
in_type; | |
BOOST_SPIRIT_ASSERT_MSG(( | |
(sizeof(char_type) >= sizeof(in_type)) | |
), cannot_convert_string, (String)); | |
in_type const* definition = | |
(in_type const*)traits::get_c_string(str); | |
in_type ch = *definition++; | |
while (ch) | |
{ | |
in_type next = *definition++; | |
if (next == '-') | |
{ | |
next = *definition++; | |
if (next == 0) | |
{ | |
chset.set(cast_char<char_type>(ch)); | |
chset.set('-'); | |
break; | |
} | |
chset.set( | |
cast_char<char_type>(ch), | |
cast_char<char_type>(next) | |
); | |
} | |
else | |
{ | |
chset.set(cast_char<char_type>(ch)); | |
} | |
ch = next; | |
} | |
} | |
template <typename CharParam, typename Context> | |
bool test(CharParam ch, Context&) const | |
{ | |
return traits::ischar<CharParam, char_encoding>::call(ch) && | |
chset.test(char_type(ch)); | |
} | |
template <typename Context> | |
info what(Context& /*context*/) const | |
{ | |
return info("char-set"); | |
} | |
support::detail::basic_chset<char_type> chset; | |
}; | |
template <typename CharEncoding, bool no_attribute> | |
struct char_set<CharEncoding, no_attribute, true> // case insensitive | |
: char_parser<char_set<CharEncoding, no_attribute, true> | |
, typename mpl::if_c<no_attribute, unused_type | |
, typename CharEncoding::char_type>::type> | |
{ | |
typedef typename CharEncoding::char_type char_type; | |
typedef CharEncoding char_encoding; | |
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)); | |
char_type const* definition = | |
(char_type const*)traits::get_c_string(str); | |
char_type ch = *definition++; | |
while (ch) | |
{ | |
char_type next = *definition++; | |
if (next == '-') | |
{ | |
next = *definition++; | |
if (next == 0) | |
{ | |
chset.set(static_cast<char_type>(CharEncoding::tolower(ch))); | |
chset.set(static_cast<char_type>(CharEncoding::toupper(ch))); | |
chset.set('-'); | |
break; | |
} | |
chset.set(static_cast<char_type>(CharEncoding::tolower(ch)) | |
, static_cast<char_type>(CharEncoding::tolower(next))); | |
chset.set(static_cast<char_type>(CharEncoding::toupper(ch)) | |
, static_cast<char_type>(CharEncoding::toupper(next))); | |
} | |
else | |
{ | |
chset.set(static_cast<char_type>(CharEncoding::tolower(ch))); | |
chset.set(static_cast<char_type>(CharEncoding::toupper(ch))); | |
} | |
ch = next; | |
} | |
} | |
template <typename CharParam, typename Context> | |
bool test(CharParam ch, Context&) const | |
{ | |
return traits::ischar<CharParam, char_encoding>::call(ch) && | |
chset.test(char_type(ch)); | |
} | |
template <typename Context> | |
info what(Context& /*context*/) const | |
{ | |
return info("no-case-char-set"); | |
} | |
support::detail::basic_chset<char_type> chset; | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// Parser generators: make_xxx function (objects) | |
/////////////////////////////////////////////////////////////////////////// | |
namespace detail | |
{ | |
template <typename Modifiers, typename Encoding> | |
struct basic_literal | |
{ | |
static bool const no_case = | |
has_modifier< | |
Modifiers | |
, tag::char_code_base<tag::no_case> | |
>::value; | |
static bool const no_attr = | |
!has_modifier< | |
Modifiers | |
, tag::lazy_eval | |
>::value; | |
typedef literal_char< | |
typename spirit::detail::get_encoding_with_case< | |
Modifiers, Encoding, no_case>::type | |
, no_attr | |
, no_case> | |
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]); | |
} | |
}; | |
} | |
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> {}; | |
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> {}; | |
template <typename CharEncoding, typename Modifiers> | |
struct make_primitive< | |
terminal<tag::char_code<tag::char_, CharEncoding> >, Modifiers> | |
{ | |
typedef typename | |
spirit::detail::get_encoding<Modifiers, CharEncoding>::type | |
char_encoding; | |
typedef tag::char_code<tag::char_, char_encoding> tag; | |
typedef char_class<tag> result_type; | |
result_type operator()(unused_type, unused_type) const | |
{ | |
return result_type(); | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
// char_('x') | |
template <typename CharEncoding, typename Modifiers, typename A0> | |
struct make_primitive< | |
terminal_ex< | |
tag::char_code<tag::char_, CharEncoding> | |
, fusion::vector1<A0> > | |
, Modifiers> | |
{ | |
static bool const no_case = | |
has_modifier<Modifiers, tag::char_code_base<tag::no_case> >::value; | |
typedef typename | |
spirit::detail::get_encoding<Modifiers, CharEncoding>::type | |
char_encoding; | |
typedef typename | |
mpl::if_< | |
traits::is_string<A0> | |
, char_set<char_encoding, false, no_case> | |
, literal_char<char_encoding, false, no_case> | |
>::type | |
result_type; | |
template <typename Terminal> | |
result_type operator()(Terminal const& term, unused_type) const | |
{ | |
return result_type(fusion::at_c<0>(term.args)); | |
} | |
}; | |
// lit('x') | |
template <typename Modifiers, typename A0> | |
struct make_primitive< | |
terminal_ex<tag::lit, fusion::vector1<A0> > | |
, Modifiers | |
, typename enable_if<traits::is_char<A0> >::type> | |
{ | |
static bool const no_case = | |
has_modifier< | |
Modifiers | |
, tag::char_code_base<tag::no_case> | |
>::value; | |
typedef typename traits::char_encoding_from_char< | |
typename traits::char_type_of<A0>::type>::type encoding; | |
typedef literal_char< | |
typename spirit::detail::get_encoding_with_case< | |
Modifiers, encoding, no_case>::type | |
, true, no_case> | |
result_type; | |
template <typename Terminal> | |
result_type operator()(Terminal const& term, unused_type) const | |
{ | |
return result_type(fusion::at_c<0>(term.args)); | |
} | |
}; | |
/////////////////////////////////////////////////////////////////////////// | |
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 no_case = | |
has_modifier<Modifiers, tag::char_code_base<tag::no_case> >::value; | |
typedef typename | |
spirit::detail::get_encoding<Modifiers, CharEncoding>::type | |
char_encoding; | |
typedef literal_char<char_encoding, false, no_case> result_type; | |
template <typename Terminal> | |
result_type operator()(Terminal const& term, unused_type) const | |
{ | |
return result_type(fusion::at_c<0>(term.args)[0]); | |
} | |
}; | |
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 no_case = | |
has_modifier<Modifiers, tag::char_code_base<tag::no_case> >::value; | |
typedef typename | |
spirit::detail::get_encoding<Modifiers, CharEncoding>::type | |
char_encoding; | |
typedef char_range<char_encoding, no_case> 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 no_case = | |
has_modifier<Modifiers, tag::char_code_base<tag::no_case> >::value; | |
typedef typename | |
spirit::detail::get_encoding<Modifiers, CharEncoding>::type | |
char_encoding; | |
typedef char_range<char_encoding, no_case> 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] | |
); | |
} | |
}; | |
}}} | |
#endif |