]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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> | |
125 | 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> | |
134 | 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> | |
145 | 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 | ||
166 | 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> | |
180 | bool test(Attribute const& attr, CharParam& ch_, Context&) const | |
181 | { | |
182 | // fail if attribute isn't matched my immediate literal | |
183 | ch_ = 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> | |
190 | bool test(unused_type, CharParam& ch_, Context&) const | |
191 | { | |
192 | ch_ = ch; | |
193 | return true; | |
194 | } | |
195 | ||
196 | template <typename Context> | |
197 | 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 | ||
214 | 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> | |
222 | 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> | |
231 | 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> | |
243 | 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> | |
270 | 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> | |
308 | 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> | |
317 | 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> | |
329 | 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> | |
358 | result_type operator()(Char ch, unused_type) const | |
359 | { | |
360 | return result_type(ch); | |
361 | } | |
362 | ||
363 | template <typename Char> | |
364 | 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 | ||
404 | 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> | |
434 | 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> | |
484 | 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> | |
512 | 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> | |
539 | 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 |