1 // Copyright (c) 2001-2011 Hartmut Kaiser 2 // Copyright (c) 2010 Bryce Lelbach 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 7 #if !defined(BOOST_SPIRIT_KARMA_CHAR_FEB_21_2007_0543PM) 8 #define BOOST_SPIRIT_KARMA_CHAR_FEB_21_2007_0543PM 9 10 #if defined(_MSC_VER) 11 #pragma once 12 #endif 13 14 #include <boost/spirit/home/support/common_terminals.hpp> 15 #include <boost/spirit/home/support/string_traits.hpp> 16 #include <boost/spirit/home/support/info.hpp> 17 #include <boost/spirit/home/support/char_class.hpp> 18 #include <boost/spirit/home/support/detail/get_encoding.hpp> 19 #include <boost/spirit/home/support/char_set/basic_chset.hpp> 20 #include <boost/spirit/home/karma/domain.hpp> 21 #include <boost/spirit/home/karma/meta_compiler.hpp> 22 #include <boost/spirit/home/karma/delimit_out.hpp> 23 #include <boost/spirit/home/karma/char/char_generator.hpp> 24 #include <boost/spirit/home/karma/auxiliary/lazy.hpp> 25 #include <boost/spirit/home/karma/detail/get_casetag.hpp> 26 #include <boost/spirit/home/karma/detail/generate_to.hpp> 27 #include <boost/spirit/home/karma/detail/enable_lit.hpp> 28 #include <boost/fusion/include/at.hpp> 29 #include <boost/fusion/include/vector.hpp> 30 #include <boost/fusion/include/cons.hpp> 31 #include <boost/mpl/if.hpp> 32 #include <boost/mpl/assert.hpp> 33 #include <boost/mpl/bool.hpp> 34 #include <boost/utility/enable_if.hpp> 35 #include <string> 36 37 /////////////////////////////////////////////////////////////////////////////// 38 namespace boost { namespace spirit 39 { 40 /////////////////////////////////////////////////////////////////////////// 41 // Enablers 42 /////////////////////////////////////////////////////////////////////////// 43 template <typename CharEncoding> 44 struct use_terminal<karma::domain 45 , tag::char_code<tag::char_, CharEncoding> // enables char_ 46 > : mpl::true_ {}; 47 48 template <typename CharEncoding, typename A0> 49 struct use_terminal<karma::domain 50 , terminal_ex< 51 tag::char_code<tag::char_, CharEncoding> // enables char_('x'), char_("x") 52 , fusion::vector1<A0> 53 > 54 > : mpl::true_ {}; 55 56 template <typename A0> 57 struct use_terminal<karma::domain 58 , terminal_ex<tag::lit, fusion::vector1<A0> > // enables lit('x') 59 , typename enable_if<traits::is_char<A0> >::type> 60 : mpl::true_ {}; 61 62 template <typename CharEncoding, typename A0, typename A1> 63 struct use_terminal<karma::domain 64 , terminal_ex< 65 tag::char_code<tag::char_, CharEncoding> // enables char_('a','z') 66 , fusion::vector2<A0, A1> 67 > 68 > : mpl::true_ {}; 69 70 template <typename CharEncoding> // enables *lazy* char_('x'), char_("x") 71 struct use_lazy_terminal< 72 karma::domain 73 , tag::char_code<tag::char_, CharEncoding> 74 , 1 // arity 75 > : mpl::true_ {}; 76 77 template <> 78 struct use_terminal<karma::domain, char> // enables 'x' 79 : mpl::true_ {}; 80 81 template <> 82 struct use_terminal<karma::domain, char[2]> // enables "x" 83 : mpl::true_ {}; 84 85 template <> 86 struct use_terminal<karma::domain, wchar_t> // enables L'x' 87 : mpl::true_ {}; 88 89 template <> 90 struct use_terminal<karma::domain, wchar_t[2]> // enables L"x" 91 : mpl::true_ {}; 92 }} 93 94 /////////////////////////////////////////////////////////////////////////////// 95 namespace boost { namespace spirit { namespace karma 96 { 97 #ifndef BOOST_SPIRIT_NO_PREDEFINED_TERMINALS 98 using spirit::lit; // lit('x') is equivalent to 'x' 99 #endif 100 using spirit::lit_type; 101 102 /////////////////////////////////////////////////////////////////////////// 103 // 104 // any_char 105 // generates a single character from the associated attribute 106 // 107 // Note: this generator has to have an associated attribute 108 // 109 /////////////////////////////////////////////////////////////////////////// 110 template <typename CharEncoding, typename Tag> 111 struct any_char 112 : char_generator<any_char<CharEncoding, Tag>, CharEncoding, Tag> 113 { 114 typedef typename CharEncoding::char_type char_type; 115 typedef CharEncoding char_encoding; 116 117 template <typename Context, typename Unused> 118 struct attribute 119 { 120 typedef char_type type; 121 }; 122 123 // any_char has an attached parameter 124 template <typename Attribute, typename CharParam, typename Context> testboost::spirit::karma::any_char125 bool test(Attribute const& attr, CharParam& ch, Context&) const 126 { 127 ch = CharParam(attr); 128 return true; 129 } 130 131 // any_char has no attribute attached, it needs to have been 132 // initialized from a direct literal 133 template <typename CharParam, typename Context> testboost::spirit::karma::any_char134 bool test(unused_type, CharParam&, Context&) const 135 { 136 // It is not possible (doesn't make sense) to use char_ without 137 // providing any attribute, as the generator doesn't 'know' what 138 // character to output. The following assertion fires if this 139 // situation is detected in your code. 140 BOOST_SPIRIT_ASSERT_FAIL(CharParam, char_not_usable_without_attribute, ()); 141 return false; 142 } 143 144 template <typename Context> whatboost::spirit::karma::any_char145 static info what(Context const& /*context*/) 146 { 147 return info("any-char"); 148 } 149 }; 150 151 /////////////////////////////////////////////////////////////////////////// 152 // 153 // literal_char 154 // generates a single character given by a literal it was initialized 155 // from 156 // 157 /////////////////////////////////////////////////////////////////////////// 158 template <typename CharEncoding, typename Tag, bool no_attribute> 159 struct literal_char 160 : char_generator<literal_char<CharEncoding, Tag, no_attribute> 161 , CharEncoding, Tag> 162 { 163 typedef typename CharEncoding::char_type char_type; 164 typedef CharEncoding char_encoding; 165 literal_charboost::spirit::karma::literal_char166 literal_char(char_type ch) 167 : ch (spirit::char_class::convert<char_encoding>::to(Tag(), ch)) 168 {} 169 170 template <typename Context, typename Unused> 171 struct attribute 172 : mpl::if_c<no_attribute, unused_type, char_type> 173 {}; 174 175 // A char_('x') which additionally has an associated attribute emits 176 // its immediate literal only if it matches the attribute, otherwise 177 // it fails. 178 // any_char has an attached parameter 179 template <typename Attribute, typename CharParam, typename Context> testboost::spirit::karma::literal_char180 bool test(Attribute const& attr, CharParam& ch_, Context&) const 181 { 182 // fail if attribute isn't matched my immediate literal 183 ch_ = static_cast<char_type>(attr); 184 return attr == ch; 185 } 186 187 // A char_('x') without any associated attribute just emits its 188 // immediate literal 189 template <typename CharParam, typename Context> testboost::spirit::karma::literal_char190 bool test(unused_type, CharParam& ch_, Context&) const 191 { 192 ch_ = ch; 193 return true; 194 } 195 196 template <typename Context> whatboost::spirit::karma::literal_char197 info what(Context const& /*context*/) const 198 { 199 return info("literal-char", char_encoding::toucs4(ch)); 200 } 201 202 char_type ch; 203 }; 204 205 /////////////////////////////////////////////////////////////////////////// 206 // char range generator 207 template <typename CharEncoding, typename Tag> 208 struct char_range 209 : char_generator<char_range<CharEncoding, Tag>, CharEncoding, Tag> 210 { 211 typedef typename CharEncoding::char_type char_type; 212 typedef CharEncoding char_encoding; 213 char_rangeboost::spirit::karma::char_range214 char_range(char_type from, char_type to) 215 : from(spirit::char_class::convert<char_encoding>::to(Tag(), from)) 216 , to(spirit::char_class::convert<char_encoding>::to(Tag(), to)) 217 {} 218 219 // A char_('a', 'z') which has an associated attribute emits it only if 220 // it matches the character range, otherwise it fails. 221 template <typename Attribute, typename CharParam, typename Context> testboost::spirit::karma::char_range222 bool test(Attribute const& attr, CharParam& ch, Context&) const 223 { 224 // fail if attribute doesn't belong to character range 225 ch = attr; 226 return (from <= char_type(attr)) && (char_type(attr) <= to); 227 } 228 229 // A char_('a', 'z') without any associated attribute fails compiling 230 template <typename CharParam, typename Context> testboost::spirit::karma::char_range231 bool test(unused_type, CharParam&, Context&) const 232 { 233 // It is not possible (doesn't make sense) to use char_ generators 234 // without providing any attribute, as the generator doesn't 'know' 235 // what to output. The following assertion fires if this situation 236 // is detected in your code. 237 BOOST_SPIRIT_ASSERT_FAIL(CharParam 238 , char_range_not_usable_without_attribute, ()); 239 return false; 240 } 241 242 template <typename Context> whatboost::spirit::karma::char_range243 info what(Context& /*context*/) const 244 { 245 info result("char-range", char_encoding::toucs4(from)); 246 boost::get<std::string>(result.value) += '-'; 247 boost::get<std::string>(result.value) += to_utf8(char_encoding::toucs4(to)); 248 return result; 249 } 250 251 char_type from, to; 252 }; 253 254 /////////////////////////////////////////////////////////////////////////// 255 // character set generator 256 template <typename CharEncoding, typename Tag, bool no_attribute> 257 struct char_set 258 : char_generator<char_set<CharEncoding, Tag, no_attribute> 259 , CharEncoding, Tag> 260 { 261 typedef typename CharEncoding::char_type char_type; 262 typedef CharEncoding char_encoding; 263 264 template <typename Context, typename Unused> 265 struct attribute 266 : mpl::if_c<no_attribute, unused_type, char_type> 267 {}; 268 269 template <typename String> char_setboost::spirit::karma::char_set270 char_set(String const& str) 271 { 272 typedef typename traits::char_type_of<String>::type in_type; 273 274 BOOST_SPIRIT_ASSERT_MSG(( 275 (sizeof(char_type) == sizeof(in_type)) 276 ), cannot_convert_string, (String)); 277 278 typedef spirit::char_class::convert<char_encoding> convert_type; 279 280 char_type const* definition = 281 (char_type const*)traits::get_c_string(str); 282 char_type ch = convert_type::to(Tag(), *definition++); 283 while (ch) 284 { 285 char_type next = convert_type::to(Tag(), *definition++); 286 if (next == '-') 287 { 288 next = convert_type::to(Tag(), *definition++); 289 if (next == 0) 290 { 291 chset.set(ch); 292 chset.set('-'); 293 break; 294 } 295 chset.set(ch, next); 296 } 297 else 298 { 299 chset.set(ch); 300 } 301 ch = next; 302 } 303 } 304 305 // A char_("a-z") which has an associated attribute emits it only if 306 // it matches the character set, otherwise it fails. 307 template <typename Attribute, typename CharParam, typename Context> testboost::spirit::karma::char_set308 bool test(Attribute const& attr, CharParam& ch, Context&) const 309 { 310 // fail if attribute doesn't belong to character set 311 ch = attr; 312 return chset.test(char_type(attr)); 313 } 314 315 // A char_("a-z") without any associated attribute fails compiling 316 template <typename CharParam, typename Context> testboost::spirit::karma::char_set317 bool test(unused_type, CharParam&, Context&) const 318 { 319 // It is not possible (doesn't make sense) to use char_ generators 320 // without providing any attribute, as the generator doesn't 'know' 321 // what to output. The following assertion fires if this situation 322 // is detected in your code. 323 BOOST_SPIRIT_ASSERT_FAIL(CharParam 324 , char_set_not_usable_without_attribute, ()); 325 return false; 326 } 327 328 template <typename Context> whatboost::spirit::karma::char_set329 info what(Context& /*context*/) const 330 { 331 return info("char-set"); 332 } 333 334 support::detail::basic_chset<char_type> chset; 335 }; 336 337 /////////////////////////////////////////////////////////////////////////// 338 // Generator generators: make_xxx function (objects) 339 /////////////////////////////////////////////////////////////////////////// 340 namespace detail 341 { 342 template <typename Modifiers, typename Encoding> 343 struct basic_literal 344 { 345 static bool const lower = 346 has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; 347 static bool const upper = 348 has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; 349 350 typedef literal_char< 351 typename spirit::detail::get_encoding_with_case< 352 Modifiers, Encoding, lower || upper>::type 353 , typename get_casetag<Modifiers, lower || upper>::type 354 , true> 355 result_type; 356 357 template <typename Char> operator ()boost::spirit::karma::detail::basic_literal358 result_type operator()(Char ch, unused_type) const 359 { 360 return result_type(ch); 361 } 362 363 template <typename Char> operator ()boost::spirit::karma::detail::basic_literal364 result_type operator()(Char const* str, unused_type) const 365 { 366 return result_type(str[0]); 367 } 368 }; 369 } 370 371 // literals: 'x', "x" 372 template <typename Modifiers> 373 struct make_primitive<char, Modifiers> 374 : detail::basic_literal<Modifiers, char_encoding::standard> {}; 375 376 template <typename Modifiers> 377 struct make_primitive<char const(&)[2], Modifiers> 378 : detail::basic_literal<Modifiers, char_encoding::standard> {}; 379 380 // literals: L'x', L"x" 381 template <typename Modifiers> 382 struct make_primitive<wchar_t, Modifiers> 383 : detail::basic_literal<Modifiers, char_encoding::standard_wide> {}; 384 385 template <typename Modifiers> 386 struct make_primitive<wchar_t const(&)[2], Modifiers> 387 : detail::basic_literal<Modifiers, char_encoding::standard_wide> {}; 388 389 // char_ 390 template <typename CharEncoding, typename Modifiers> 391 struct make_primitive<tag::char_code<tag::char_, CharEncoding>, Modifiers> 392 { 393 static bool const lower = 394 has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; 395 static bool const upper = 396 has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; 397 398 typedef any_char< 399 typename spirit::detail::get_encoding_with_case< 400 Modifiers, CharEncoding, lower || upper>::type 401 , typename detail::get_casetag<Modifiers, lower || upper>::type 402 > result_type; 403 operator ()boost::spirit::karma::make_primitive404 result_type operator()(unused_type, unused_type) const 405 { 406 return result_type(); 407 } 408 }; 409 410 /////////////////////////////////////////////////////////////////////////// 411 namespace detail 412 { 413 template <typename CharEncoding, typename Modifiers, typename A0 414 , bool no_attribute> 415 struct make_char_direct 416 { 417 static bool const lower = 418 has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; 419 static bool const upper = 420 has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; 421 422 typedef typename spirit::detail::get_encoding_with_case< 423 Modifiers, CharEncoding, lower || upper>::type encoding; 424 typedef typename detail::get_casetag< 425 Modifiers, lower || upper>::type tag; 426 427 typedef typename mpl::if_< 428 traits::is_string<A0> 429 , char_set<encoding, tag, no_attribute> 430 , literal_char<encoding, tag, no_attribute> 431 >::type result_type; 432 433 template <typename Terminal> operator ()boost::spirit::karma::detail::make_char_direct434 result_type operator()(Terminal const& term, unused_type) const 435 { 436 return result_type(fusion::at_c<0>(term.args)); 437 } 438 }; 439 } 440 441 // char_(...), lit(...) 442 template <typename CharEncoding, typename Modifiers, typename A0> 443 struct make_primitive< 444 terminal_ex< 445 tag::char_code<tag::char_, CharEncoding> 446 , fusion::vector1<A0> > 447 , Modifiers> 448 : detail::make_char_direct<CharEncoding, Modifiers, A0, false> 449 {}; 450 451 template <typename Modifiers, typename A0> 452 struct make_primitive< 453 terminal_ex<tag::lit, fusion::vector1<A0> > 454 , Modifiers 455 , typename enable_if<traits::is_char<A0> >::type> 456 : detail::make_char_direct< 457 typename traits::char_encoding_from_char< 458 typename traits::char_type_of<A0>::type>::type 459 , Modifiers, A0, true> 460 {}; 461 462 /////////////////////////////////////////////////////////////////////////// 463 // char_("x") 464 template <typename CharEncoding, typename Modifiers, typename Char> 465 struct make_primitive< 466 terminal_ex< 467 tag::char_code<tag::char_, CharEncoding> 468 , fusion::vector1<Char(&)[2]> > // For single char strings 469 , Modifiers> 470 { 471 static bool const lower = 472 has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; 473 static bool const upper = 474 has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; 475 476 typedef literal_char< 477 typename spirit::detail::get_encoding_with_case< 478 Modifiers, CharEncoding, lower || upper>::type 479 , typename detail::get_casetag<Modifiers, lower || upper>::type 480 , false 481 > result_type; 482 483 template <typename Terminal> operator ()boost::spirit::karma::make_primitive484 result_type operator()(Terminal const& term, unused_type) const 485 { 486 return result_type(fusion::at_c<0>(term.args)[0]); 487 } 488 }; 489 490 /////////////////////////////////////////////////////////////////////////// 491 // char_('a', 'z') 492 template <typename CharEncoding, typename Modifiers, typename A0, typename A1> 493 struct make_primitive< 494 terminal_ex< 495 tag::char_code<tag::char_, CharEncoding> 496 , fusion::vector2<A0, A1> 497 > 498 , Modifiers> 499 { 500 static bool const lower = 501 has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; 502 static bool const upper = 503 has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; 504 505 typedef char_range< 506 typename spirit::detail::get_encoding_with_case< 507 Modifiers, CharEncoding, lower || upper>::type 508 , typename detail::get_casetag<Modifiers, lower || upper>::type 509 > result_type; 510 511 template <typename Terminal> operator ()boost::spirit::karma::make_primitive512 result_type operator()(Terminal const& term, unused_type) const 513 { 514 return result_type(fusion::at_c<0>(term.args) 515 , fusion::at_c<1>(term.args)); 516 } 517 }; 518 519 template <typename CharEncoding, typename Modifiers, typename Char> 520 struct make_primitive< 521 terminal_ex< 522 tag::char_code<tag::char_, CharEncoding> 523 , fusion::vector2<Char(&)[2], Char(&)[2]> // For single char strings 524 > 525 , Modifiers> 526 { 527 static bool const lower = 528 has_modifier<Modifiers, tag::char_code_base<tag::lower> >::value; 529 static bool const upper = 530 has_modifier<Modifiers, tag::char_code_base<tag::upper> >::value; 531 532 typedef char_range< 533 typename spirit::detail::get_encoding_with_case< 534 Modifiers, CharEncoding, lower || upper>::type 535 , typename detail::get_casetag<Modifiers, lower || upper>::type 536 > result_type; 537 538 template <typename Terminal> operator ()boost::spirit::karma::make_primitive539 result_type operator()(Terminal const& term, unused_type) const 540 { 541 return result_type(fusion::at_c<0>(term.args)[0] 542 , fusion::at_c<1>(term.args)[0]); 543 } 544 }; 545 }}} // namespace boost::spirit::karma 546 547 #endif 548