1 /*=============================================================================
2     Copyright (c) 2001-2011 Joel de Guzman
3     Copyright (c) 2001-2011 Hartmut Kaiser
4     Copyright (c)      2010 Bryce Lelbach
5 
6     Distributed under the Boost Software License, Version 1.0. (See accompanying
7     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 ==============================================================================*/
9 #if !defined(BOOST_SPIRIT_CHAR_APRIL_16_2006_1051AM)
10 #define BOOST_SPIRIT_CHAR_APRIL_16_2006_1051AM
11 
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15 
16 #include <boost/spirit/home/support/common_terminals.hpp>
17 #include <boost/spirit/home/support/string_traits.hpp>
18 #include <boost/spirit/home/support/info.hpp>
19 #include <boost/spirit/home/support/detail/get_encoding.hpp>
20 #include <boost/spirit/home/support/char_set/basic_chset.hpp>
21 #include <boost/spirit/home/qi/char/char_parser.hpp>
22 #include <boost/spirit/home/qi/char/char_class.hpp>
23 #include <boost/spirit/home/qi/meta_compiler.hpp>
24 #include <boost/spirit/home/qi/auxiliary/lazy.hpp>
25 #include <boost/spirit/home/qi/detail/enable_lit.hpp>
26 #include <boost/fusion/include/at.hpp>
27 #include <boost/mpl/if.hpp>
28 #include <boost/mpl/assert.hpp>
29 #include <boost/mpl/identity.hpp>
30 #include <boost/utility/enable_if.hpp>
31 #include <boost/type_traits/remove_const.hpp>
32 #include <string>
33 
34 #if defined(_MSC_VER)
35 #pragma once
36 #endif
37 
38 namespace boost { namespace spirit
39 {
40     ///////////////////////////////////////////////////////////////////////////
41     // Enablers
42     ///////////////////////////////////////////////////////////////////////////
43     template <typename CharEncoding>
44     struct use_terminal<qi::domain
45       , terminal<
46             tag::char_code<tag::char_, CharEncoding>    // enables char_
47         >
48     > : mpl::true_ {};
49 
50     template <typename CharEncoding, typename A0>
51     struct use_terminal<qi::domain
52       , terminal_ex<
53             tag::char_code<tag::char_, CharEncoding>    // enables char_('x'), char_("x")
54           , fusion::vector1<A0>                         // and char_("a-z0-9")
55         >
56     > : mpl::true_ {};
57 
58     template <typename CharEncoding, typename A0, typename A1>
59     struct use_terminal<qi::domain
60       , terminal_ex<
61             tag::char_code<tag::char_, CharEncoding>    // enables char_('a','z')
62           , fusion::vector2<A0, A1>
63         >
64     > : mpl::true_ {};
65 
66     template <typename CharEncoding>                    // enables *lazy* char_('x'), char_("x")
67     struct use_lazy_terminal<                           // and char_("a-z0-9")
68         qi::domain
69       , tag::char_code<tag::char_, CharEncoding>
70       , 1 // arity
71     > : mpl::true_ {};
72 
73     template <typename CharEncoding>                    // enables *lazy* char_('a','z')
74     struct use_lazy_terminal<
75         qi::domain
76       , tag::char_code<tag::char_, CharEncoding>
77       , 2 // arity
78     > : mpl::true_ {};
79 
80     template <>
81     struct use_terminal<qi::domain, char>               // enables 'x'
82       : mpl::true_ {};
83 
84     template <>
85     struct use_terminal<qi::domain, char[2]>            // enables "x"
86       : mpl::true_ {};
87 
88     template <>
89     struct use_terminal<qi::domain, wchar_t>            // enables wchar_t
90       : mpl::true_ {};
91 
92     template <>
93     struct use_terminal<qi::domain, wchar_t[2]>         // enables L"x"
94       : mpl::true_ {};
95 
96     // enables lit(...)
97     template <typename A0>
98     struct use_terminal<qi::domain
99           , terminal_ex<tag::lit, fusion::vector1<A0> >
100           , typename enable_if<traits::is_char<A0> >::type>
101       : mpl::true_ {};
102 }}
103 
104 namespace boost { namespace spirit { namespace qi
105 {
106 #ifndef BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
107     using spirit::lit; // lit('x') is equivalent to 'x'
108 #endif
109     using spirit::lit_type;
110 
111     ///////////////////////////////////////////////////////////////////////////
112     // Parser for a single character
113     ///////////////////////////////////////////////////////////////////////////
114     template <typename CharEncoding, bool no_attribute, bool no_case = false>
115     struct literal_char
116       : char_parser<
117             literal_char<CharEncoding, no_attribute, false>
118           , typename CharEncoding::char_type
119           , typename mpl::if_c<no_attribute, unused_type
120               , typename CharEncoding::char_type>::type>
121     {
122         typedef typename CharEncoding::char_type char_type;
123         typedef CharEncoding char_encoding;
124 
125         template <typename Char>
literal_charboost::spirit::qi::literal_char126         literal_char(Char ch_)
127           : ch(static_cast<char_type>(ch_)) {}
128 
129         template <typename Context, typename Iterator>
130         struct attribute
131         {
132             typedef typename mpl::if_c<
133                 no_attribute, unused_type, char_type>::type
134             type;
135         };
136 
137         template <typename CharParam, typename Context>
testboost::spirit::qi::literal_char138         bool test(CharParam ch_, Context&) const
139         {
140             return traits::ischar<CharParam, char_encoding>::call(ch_) &&
141                    ch == char_type(ch_);
142         }
143 
144         template <typename Context>
whatboost::spirit::qi::literal_char145         info what(Context& /*context*/) const
146         {
147             return info("literal-char", char_encoding::toucs4(ch));
148         }
149 
150         char_type ch;
151     };
152 
153     template <typename CharEncoding, bool no_attribute>
154     struct literal_char<CharEncoding, no_attribute, true> // case insensitive
155       : char_parser<
156             literal_char<CharEncoding, no_attribute, true>
157           , typename mpl::if_c<no_attribute, unused_type
158               , typename CharEncoding::char_type>::type>
159     {
160         typedef typename CharEncoding::char_type char_type;
161         typedef CharEncoding char_encoding;
162 
literal_charboost::spirit::qi::literal_char163         literal_char(char_type ch)
164           : lo(static_cast<char_type>(char_encoding::tolower(ch)))
165           , hi(static_cast<char_type>(char_encoding::toupper(ch))) {}
166 
167         template <typename Context, typename Iterator>
168         struct attribute
169         {
170             typedef typename mpl::if_c<
171                 no_attribute, unused_type, char_type>::type
172             type;
173         };
174 
175         template <typename CharParam, typename Context>
testboost::spirit::qi::literal_char176         bool test(CharParam ch_, Context&) const
177         {
178             if (!traits::ischar<CharParam, char_encoding>::call(ch_))
179                 return false;
180 
181             char_type ch = char_type(ch_);  // optimize for token based parsing
182             return this->lo == ch || this->hi == ch;
183         }
184 
185         template <typename Context>
whatboost::spirit::qi::literal_char186         info what(Context& /*context*/) const
187         {
188             return info("no-case-literal-char", char_encoding::toucs4(lo));
189         }
190 
191         char_type lo, hi;
192     };
193 
194     ///////////////////////////////////////////////////////////////////////////
195     // Parser for a character range
196     ///////////////////////////////////////////////////////////////////////////
197     template <typename CharEncoding, bool no_case = false>
198     struct char_range
199       : char_parser<char_range<CharEncoding, false>, typename CharEncoding::char_type>
200     {
201         typedef typename CharEncoding::char_type char_type;
202         typedef CharEncoding char_encoding;
203 
char_rangeboost::spirit::qi::char_range204         char_range(char_type from_, char_type to_)
205           : from(from_), to(to_) {}
206 
207         template <typename CharParam, typename Context>
testboost::spirit::qi::char_range208         bool test(CharParam ch_, Context&) const
209         {
210             if (!traits::ischar<CharParam, char_encoding>::call(ch_))
211                 return false;
212 
213             char_type ch = char_type(ch_);  // optimize for token based parsing
214             return !(ch < from) && !(to < ch);
215         }
216 
217         template <typename Context>
whatboost::spirit::qi::char_range218         info what(Context& /*context*/) const
219         {
220             info result("char-range", char_encoding::toucs4(from));
221             boost::get<std::string>(result.value) += '-';
222             boost::get<std::string>(result.value) += to_utf8(char_encoding::toucs4(to));
223             return result;
224         }
225 
226         char_type from, to;
227     };
228 
229     template <typename CharEncoding>
230     struct char_range<CharEncoding, true> // case insensitive
231       : char_parser<char_range<CharEncoding, true>, typename CharEncoding::char_type>
232     {
233         typedef typename CharEncoding::char_type char_type;
234         typedef CharEncoding char_encoding;
235 
char_rangeboost::spirit::qi::char_range236         char_range(char_type from, char_type to)
237           : from_lo(static_cast<char_type>(char_encoding::tolower(from)))
238           , to_lo(static_cast<char_type>(char_encoding::tolower(to)))
239           , from_hi(static_cast<char_type>(char_encoding::toupper(from)))
240           , to_hi(static_cast<char_type>(char_encoding::toupper(to)))
241         {}
242 
243         template <typename CharParam, typename Context>
testboost::spirit::qi::char_range244         bool test(CharParam ch_, Context&) const
245         {
246             if (!traits::ischar<CharParam, char_encoding>::call(ch_))
247                 return false;
248 
249             char_type ch = char_type(ch_);  // optimize for token based parsing
250             return (!(ch < from_lo) && !(to_lo < ch))
251                 || (!(ch < from_hi) && !(to_hi < ch))
252             ;
253         }
254 
255         template <typename Context>
whatboost::spirit::qi::char_range256         info what(Context& /*context*/) const
257         {
258             info result("no-case-char-range", char_encoding::toucs4(from_lo));
259             boost::get<std::string>(result.value) += '-';
260             boost::get<std::string>(result.value) += to_utf8(char_encoding::toucs4(to_lo));
261             return result;
262         }
263 
264         char_type from_lo, to_lo, from_hi, to_hi;
265     };
266 
267     ///////////////////////////////////////////////////////////////////////////
268     // Parser for a character set
269     ///////////////////////////////////////////////////////////////////////////
270     template <typename CharEncoding, bool no_attribute, bool no_case = false>
271     struct char_set
272       : char_parser<char_set<CharEncoding, no_attribute, false>
273           , typename mpl::if_c<no_attribute, unused_type
274               , typename CharEncoding::char_type>::type>
275     {
276         typedef typename CharEncoding::char_type char_type;
277         typedef CharEncoding char_encoding;
278 
279         template <typename String>
char_setboost::spirit::qi::char_set280         char_set(String const& str)
281         {
282             using spirit::detail::cast_char;
283 
284             typedef typename
285                 remove_const<
286                     typename traits::char_type_of<String>::type
287                 >::type
288             in_type;
289 
290             BOOST_SPIRIT_ASSERT_MSG((
291                 (sizeof(char_type) >= sizeof(in_type))
292             ), cannot_convert_string, (String));
293 
294             in_type const* definition =
295                 (in_type const*)traits::get_c_string(str);
296             in_type ch = *definition++;
297             while (ch)
298             {
299                 in_type next = *definition++;
300                 if (next == '-')
301                 {
302                     next = *definition++;
303                     if (next == 0)
304                     {
305                         chset.set(cast_char<char_type>(ch));
306                         chset.set('-');
307                         break;
308                     }
309                     chset.set(
310                         cast_char<char_type>(ch),
311                         cast_char<char_type>(next)
312                     );
313                 }
314                 else
315                 {
316                     chset.set(cast_char<char_type>(ch));
317                 }
318                 ch = next;
319             }
320         }
321 
322         template <typename CharParam, typename Context>
testboost::spirit::qi::char_set323         bool test(CharParam ch, Context&) const
324         {
325             return traits::ischar<CharParam, char_encoding>::call(ch) &&
326                    chset.test(char_type(ch));
327         }
328 
329         template <typename Context>
whatboost::spirit::qi::char_set330         info what(Context& /*context*/) const
331         {
332             return info("char-set");
333         }
334 
335         support::detail::basic_chset<char_type> chset;
336     };
337 
338     template <typename CharEncoding, bool no_attribute>
339     struct char_set<CharEncoding, no_attribute, true> // case insensitive
340       : char_parser<char_set<CharEncoding, no_attribute, true>
341           , typename mpl::if_c<no_attribute, unused_type
342               , typename CharEncoding::char_type>::type>
343     {
344         typedef typename CharEncoding::char_type char_type;
345         typedef CharEncoding char_encoding;
346 
347         template <typename String>
char_setboost::spirit::qi::char_set348         char_set(String const& str)
349         {
350             typedef typename traits::char_type_of<String>::type in_type;
351 
352             BOOST_SPIRIT_ASSERT_MSG((
353                 (sizeof(char_type) == sizeof(in_type))
354             ), cannot_convert_string, (String));
355 
356             char_type const* definition =
357                 (char_type const*)traits::get_c_string(str);
358             char_type ch = *definition++;
359             while (ch)
360             {
361                 char_type next = *definition++;
362                 if (next == '-')
363                 {
364                     next = *definition++;
365                     if (next == 0)
366                     {
367                         chset.set(static_cast<char_type>(CharEncoding::tolower(ch)));
368                         chset.set(static_cast<char_type>(CharEncoding::toupper(ch)));
369                         chset.set('-');
370                         break;
371                     }
372                     chset.set(static_cast<char_type>(CharEncoding::tolower(ch))
373                       , static_cast<char_type>(CharEncoding::tolower(next)));
374                     chset.set(static_cast<char_type>(CharEncoding::toupper(ch))
375                       , static_cast<char_type>(CharEncoding::toupper(next)));
376                 }
377                 else
378                 {
379                     chset.set(static_cast<char_type>(CharEncoding::tolower(ch)));
380                     chset.set(static_cast<char_type>(CharEncoding::toupper(ch)));
381                 }
382                 ch = next;
383             }
384         }
385 
386         template <typename CharParam, typename Context>
testboost::spirit::qi::char_set387         bool test(CharParam ch, Context&) const
388         {
389             return traits::ischar<CharParam, char_encoding>::call(ch) &&
390                    chset.test(char_type(ch));
391         }
392 
393         template <typename Context>
whatboost::spirit::qi::char_set394         info what(Context& /*context*/) const
395         {
396             return info("no-case-char-set");
397         }
398 
399         support::detail::basic_chset<char_type> chset;
400     };
401 
402     ///////////////////////////////////////////////////////////////////////////
403     // Parser generators: make_xxx function (objects)
404     ///////////////////////////////////////////////////////////////////////////
405     namespace detail
406     {
407         template <typename Modifiers, typename Encoding>
408         struct basic_literal
409         {
410             static bool const no_case =
411                 has_modifier<
412                     Modifiers
413                   , tag::char_code_base<tag::no_case>
414                 >::value;
415 
416             static bool const no_attr =
417                 !has_modifier<
418                     Modifiers
419                   , tag::lazy_eval
420                 >::value;
421 
422             typedef literal_char<
423                 typename spirit::detail::get_encoding_with_case<
424                     Modifiers, Encoding, no_case>::type
425               , no_attr
426               , no_case>
427             result_type;
428 
429             template <typename Char>
operator ()boost::spirit::qi::detail::basic_literal430             result_type operator()(Char ch, unused_type) const
431             {
432                 return result_type(ch);
433             }
434 
435             template <typename Char>
operator ()boost::spirit::qi::detail::basic_literal436             result_type operator()(Char const* str, unused_type) const
437             {
438                 return result_type(str[0]);
439             }
440         };
441     }
442 
443     template <typename Modifiers>
444     struct make_primitive<char, Modifiers>
445       : detail::basic_literal<Modifiers, char_encoding::standard> {};
446 
447     template <typename Modifiers>
448     struct make_primitive<char const(&)[2], Modifiers>
449       : detail::basic_literal<Modifiers, char_encoding::standard> {};
450 
451     template <typename Modifiers>
452     struct make_primitive<wchar_t, Modifiers>
453       : detail::basic_literal<Modifiers, char_encoding::standard_wide> {};
454 
455     template <typename Modifiers>
456     struct make_primitive<wchar_t const(&)[2], Modifiers>
457       : detail::basic_literal<Modifiers, char_encoding::standard_wide> {};
458 
459     template <typename CharEncoding, typename Modifiers>
460     struct make_primitive<
461         terminal<tag::char_code<tag::char_, CharEncoding> >, Modifiers>
462     {
463         typedef typename
464             spirit::detail::get_encoding<Modifiers, CharEncoding>::type
465         char_encoding;
466 
467         typedef tag::char_code<tag::char_, char_encoding> tag;
468         typedef char_class<tag> result_type;
operator ()boost::spirit::qi::make_primitive469         result_type operator()(unused_type, unused_type) const
470         {
471             return result_type();
472         }
473     };
474 
475     ///////////////////////////////////////////////////////////////////////////
476     // char_('x')
477     template <typename CharEncoding, typename Modifiers, typename A0>
478     struct make_primitive<
479         terminal_ex<
480             tag::char_code<tag::char_, CharEncoding>
481           , fusion::vector1<A0> >
482       , Modifiers>
483     {
484         static bool const no_case =
485             has_modifier<Modifiers, tag::char_code_base<tag::no_case> >::value;
486 
487         typedef typename
488             spirit::detail::get_encoding<Modifiers, CharEncoding>::type
489         char_encoding;
490 
491         typedef typename
492             mpl::if_<
493                 traits::is_string<A0>
494               , char_set<char_encoding, false, no_case>
495               , literal_char<char_encoding, false, no_case>
496             >::type
497         result_type;
498 
499         template <typename Terminal>
operator ()boost::spirit::qi::make_primitive500         result_type operator()(Terminal const& term, unused_type) const
501         {
502             return result_type(fusion::at_c<0>(term.args));
503         }
504     };
505 
506     // lit('x')
507     template <typename Modifiers, typename A0>
508     struct make_primitive<
509         terminal_ex<tag::lit, fusion::vector1<A0> >
510       , Modifiers
511       , typename enable_if<traits::is_char<A0> >::type>
512     {
513         static bool const no_case =
514             has_modifier<
515                 Modifiers
516               , tag::char_code_base<tag::no_case>
517             >::value;
518 
519         typedef typename traits::char_encoding_from_char<
520                 typename traits::char_type_of<A0>::type>::type encoding;
521 
522         typedef literal_char<
523             typename spirit::detail::get_encoding_with_case<
524                 Modifiers, encoding, no_case>::type
525           , true, no_case>
526         result_type;
527 
528         template <typename Terminal>
operator ()boost::spirit::qi::make_primitive529         result_type operator()(Terminal const& term, unused_type) const
530         {
531             return result_type(fusion::at_c<0>(term.args));
532         }
533     };
534 
535     ///////////////////////////////////////////////////////////////////////////
536     template <typename CharEncoding, typename Modifiers, typename Char>
537     struct make_primitive<
538         terminal_ex<
539             tag::char_code<tag::char_, CharEncoding>
540           , fusion::vector1<Char(&)[2]> // For single char strings
541         >
542       , Modifiers>
543     {
544         static bool const no_case =
545             has_modifier<Modifiers, tag::char_code_base<tag::no_case> >::value;
546 
547         typedef typename
548             spirit::detail::get_encoding<Modifiers, CharEncoding>::type
549         char_encoding;
550 
551         typedef literal_char<char_encoding, false, no_case> result_type;
552 
553         template <typename Terminal>
operator ()boost::spirit::qi::make_primitive554         result_type operator()(Terminal const& term, unused_type) const
555         {
556             return result_type(fusion::at_c<0>(term.args)[0]);
557         }
558     };
559 
560     template <typename CharEncoding, typename Modifiers, typename A0, typename A1>
561     struct make_primitive<
562         terminal_ex<
563             tag::char_code<tag::char_, CharEncoding>
564           , fusion::vector2<A0, A1>
565         >
566       , Modifiers>
567     {
568         static bool const no_case =
569             has_modifier<Modifiers, tag::char_code_base<tag::no_case> >::value;
570 
571         typedef typename
572             spirit::detail::get_encoding<Modifiers, CharEncoding>::type
573         char_encoding;
574 
575         typedef char_range<char_encoding, no_case> result_type;
576 
577         template <typename Terminal>
operator ()boost::spirit::qi::make_primitive578         result_type operator()(Terminal const& term, unused_type) const
579         {
580             return result_type(
581                 fusion::at_c<0>(term.args)
582               , fusion::at_c<1>(term.args)
583             );
584         }
585     };
586 
587     template <typename CharEncoding, typename Modifiers, typename Char>
588     struct make_primitive<
589         terminal_ex<
590             tag::char_code<tag::char_, CharEncoding>
591           , fusion::vector2<Char(&)[2], Char(&)[2]> // For single char strings
592         >
593       , Modifiers>
594     {
595         static bool const no_case =
596             has_modifier<Modifiers, tag::char_code_base<tag::no_case> >::value;
597 
598         typedef typename
599             spirit::detail::get_encoding<Modifiers, CharEncoding>::type
600         char_encoding;
601 
602         typedef char_range<char_encoding, no_case> result_type;
603 
604         template <typename Terminal>
operator ()boost::spirit::qi::make_primitive605         result_type operator()(Terminal const& term, unused_type) const
606         {
607             return result_type(
608                 fusion::at_c<0>(term.args)[0]
609               , fusion::at_c<1>(term.args)[0]
610             );
611         }
612     };
613 }}}
614 
615 #endif
616