1 /*=============================================================================
2     Copyright (c) 2001-2011 Joel de Guzman
3     Copyright (c) 2001-2011 Hartmut Kaiser
4     Copyright (c)      2011 Thomas Heller
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_TERMINAL_NOVEMBER_04_2008_0906AM)
10 #define BOOST_SPIRIT_TERMINAL_NOVEMBER_04_2008_0906AM
11 
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15 
16 #include <boost/config.hpp>
17 #include <boost/spirit/home/support/meta_compiler.hpp>
18 #include <boost/spirit/home/support/detail/make_vector.hpp>
19 #include <boost/spirit/home/support/unused.hpp>
20 #include <boost/spirit/home/support/detail/is_spirit_tag.hpp>
21 #include <boost/spirit/home/support/terminal_expression.hpp>
22 #include <boost/phoenix/core/as_actor.hpp>
23 #include <boost/phoenix/core/is_actor.hpp>
24 #include <boost/phoenix/core/terminal_fwd.hpp>
25 #include <boost/phoenix/core/value.hpp> // includes as_actor specialization
26 #include <boost/phoenix/function/function.hpp>
27 #include <boost/preprocessor/tuple/elem.hpp>
28 #include <boost/proto/extends.hpp>
29 #include <boost/proto/traits.hpp>
30 
31 namespace boost { namespace spirit
32 {
33     template <typename Terminal, typename Args>
34     struct terminal_ex
35     {
36         typedef Terminal terminal_type;
37         typedef Args args_type;
38 
terminal_exboost::spirit::terminal_ex39         terminal_ex(Args const& args_)
40           : args(args_) {}
terminal_exboost::spirit::terminal_ex41         terminal_ex(Args const& args_, Terminal const& term_)
42           : args(args_), term(term_) {}
43 
44         Args args;  // Args is guaranteed to be a fusion::vectorN so you
45                     // can use that template for detection and specialization
46         Terminal term;
47     };
48 
49     template <typename Terminal, typename Actor, int Arity>
50     struct lazy_terminal
51     {
52         typedef Terminal terminal_type;
53         typedef Actor actor_type;
54         static int const arity = Arity;
55 
lazy_terminalboost::spirit::lazy_terminal56         lazy_terminal(Actor const& actor_)
57           : actor(actor_) {}
lazy_terminalboost::spirit::lazy_terminal58         lazy_terminal(Actor const& actor_, Terminal const& term_)
59           : actor(actor_), term(term_) {}
60 
61         Actor actor;
62         Terminal term;
63     };
64 
65     template <typename Domain, typename Terminal, int Arity, typename Enable = void>
66     struct use_lazy_terminal : mpl::false_ {};
67 
68     template <typename Domain, typename Terminal, int Arity, typename Enable = void>
69     struct use_lazy_directive : mpl::false_ {};
70 
71     template <typename Terminal>
72     struct terminal;
73 
74     template <typename Domain, typename Terminal>
75     struct use_terminal<Domain, terminal<Terminal> >
76         : use_terminal<Domain, Terminal> {};
77 
78     template <typename Domain, typename Terminal, int Arity, typename Actor>
79     struct use_terminal<Domain, lazy_terminal<Terminal, Actor, Arity> >
80         : use_lazy_terminal<Domain, Terminal, Arity> {};
81 
82     template <typename Domain, typename Terminal, int Arity, typename Actor>
83     struct use_directive<Domain, lazy_terminal<Terminal, Actor, Arity> >
84         : use_lazy_directive<Domain, Terminal, Arity> {};
85 
86     template <
87         typename F
88       , typename A0 = unused_type
89       , typename A1 = unused_type
90       , typename A2 = unused_type
91       , typename Unused = unused_type
92     >
93     struct make_lazy;
94 
95     template <typename F, typename A0>
96     struct make_lazy<F, A0>
97     {
98         typedef typename
99             proto::terminal<
100                 lazy_terminal<
101                     typename F::terminal_type
102                   , typename phoenix::detail::expression::function_eval<F, A0>::type
103                   , 1 // arity
104                 >
105             >::type
106         result_type;
107         typedef result_type type;
108 
109         result_type
operator ()boost::spirit::make_lazy110         operator()(F f, A0 const& _0_) const
111         {
112             typedef typename result_type::proto_child0 child_type;
113             return result_type::make(child_type(
114                 phoenix::detail::expression::function_eval<F, A0>::make(f, _0_)
115               , f.proto_base().child0
116             ));
117         }
118     };
119 
120     template <typename F, typename A0, typename A1>
121     struct make_lazy<F, A0, A1>
122     {
123         typedef typename
124             proto::terminal<
125                lazy_terminal<
126                     typename F::terminal_type
127                   , typename phoenix::detail::expression::function_eval<F, A0, A1>::type
128                   , 2 // arity
129                 >
130             >::type
131         result_type;
132         typedef result_type type;
133 
134         result_type
operator ()boost::spirit::make_lazy135         operator()(F f, A0 const& _0_, A1 const& _1_) const
136         {
137             typedef typename result_type::proto_child0 child_type;
138             return result_type::make(child_type(
139                 phoenix::detail::expression::function_eval<F, A0, A1>::make(f, _0_, _1_)
140               , f.proto_base().child0
141             ));
142         }
143     };
144 
145     template <typename F, typename A0, typename A1, typename A2>
146     struct make_lazy<F, A0, A1, A2>
147     {
148         typedef typename
149             proto::terminal<
150                lazy_terminal<
151                     typename F::terminal_type
152                   , typename phoenix::detail::expression::function_eval<F, A0, A1, A2>::type
153                   , 3 // arity
154                 >
155             >::type
156         result_type;
157         typedef result_type type;
158 
159         result_type
operator ()boost::spirit::make_lazy160         operator()(F f, A0 const& _0_, A1 const& _1_, A2 const& _2_) const
161         {
162             typedef typename result_type::proto_child0 child_type;
163             return result_type::make(child_type(
164                 phoenix::detail::expression::function_eval<F, A0, A1, A2>::make(f, _0_, _1_, _2_)
165               , f.proto_base().child0
166             ));
167         }
168     };
169 
170     namespace detail
171     {
172         // Helper struct for SFINAE purposes
173         template <bool C> struct bool_;
174 
175         template <>
176         struct bool_<true> : mpl::bool_<true>
177         {
178             typedef bool_<true>* is_true;
179         };
180 
181         template <>
182         struct bool_<false> : mpl::bool_<false>
183         {
184             typedef bool_<false>* is_false;
185         };
186 
187         // Metafunction to detect if at least one arg is a Phoenix actor
188         template <
189             typename A0
190           , typename A1 = unused_type
191           , typename A2 = unused_type
192         >
193         struct contains_actor
194             : bool_<
195                   phoenix::is_actor<A0>::value
196                || phoenix::is_actor<A1>::value
197                || phoenix::is_actor<A2>::value
198               >
199         {};
200 
201         // to_lazy_arg: convert a terminal arg type to the type make_lazy needs
202         template <typename A>
203         struct to_lazy_arg
204           : phoenix::as_actor<A> // wrap A in a Phoenix actor if not already one
205         {};
206 
207         template <typename A>
208         struct to_lazy_arg<const A>
209           : to_lazy_arg<A>
210         {};
211 
212         template <typename A>
213         struct to_lazy_arg<A &>
214           : to_lazy_arg<A>
215         {};
216 
217         template <>
218         struct to_lazy_arg<unused_type>
219         {
220             // unused arg: make_lazy wants unused_type
221             typedef unused_type type;
222         };
223 
224         // to_nonlazy_arg: convert a terminal arg type to the type make_vector needs
225         template <typename A>
226         struct to_nonlazy_arg
227         {
228             // identity
229             typedef A type;
230         };
231 
232         template <typename A>
233         struct to_nonlazy_arg<const A>
234           : to_nonlazy_arg<A>
235         {};
236 
237         template <typename A>
238         struct to_nonlazy_arg<A &>
239           : to_nonlazy_arg<A>
240         {};
241 
242         // incomplete type: should not be appeared unused_type in nonlazy arg.
243         template <>
244         struct to_nonlazy_arg<unused_type>;
245     }
246 
247     template <typename Terminal>
248     struct terminal
249       : proto::extends<
250             typename proto::terminal<Terminal>::type
251           , terminal<Terminal>
252         >
253     {
254         typedef terminal<Terminal> this_type;
255         typedef Terminal terminal_type;
256 
257         typedef proto::extends<
258             typename proto::terminal<Terminal>::type
259           , terminal<Terminal>
260         > base_type;
261 
terminalboost::spirit::terminal262         terminal() {}
263 
terminalboost::spirit::terminal264         terminal(Terminal const& t)
265           : base_type(proto::terminal<Terminal>::type::make(t))
266         {}
267 
268 #if defined(BOOST_MSVC)
269 #pragma warning(push)
270 // warning C4348: 'boost::spirit::terminal<...>::result_helper': redefinition of default parameter: parameter 3, 4
271 #pragma warning(disable: 4348)
272 #endif
273 
274         template <
275             bool Lazy
276           , typename A0
277           , typename A1 = unused_type
278           , typename A2 = unused_type
279         >
280         struct result_helper;
281 
282 #if defined(BOOST_MSVC)
283 #pragma warning(pop)
284 #endif
285 
286         template <
287             typename A0
288         >
289         struct result_helper<false, A0>
290         {
291             typedef typename
292                 proto::terminal<
293                     terminal_ex<
294                         Terminal
295                       , typename detail::result_of::make_vector<
296                             typename detail::to_nonlazy_arg<A0>::type>::type>
297                 >::type
298             type;
299         };
300 
301         template <
302             typename A0
303           , typename A1
304         >
305         struct result_helper<false, A0, A1>
306         {
307             typedef typename
308                 proto::terminal<
309                     terminal_ex<
310                         Terminal
311                       , typename detail::result_of::make_vector<
312                             typename detail::to_nonlazy_arg<A0>::type
313                           , typename detail::to_nonlazy_arg<A1>::type>::type>
314                 >::type
315             type;
316         };
317 
318         template <
319             typename A0
320           , typename A1
321           , typename A2
322         >
323         struct result_helper<false, A0, A1, A2>
324         {
325             typedef typename
326                 proto::terminal<
327                     terminal_ex<
328                         Terminal
329                       , typename detail::result_of::make_vector<
330                             typename detail::to_nonlazy_arg<A0>::type
331                           , typename detail::to_nonlazy_arg<A1>::type
332                           , typename detail::to_nonlazy_arg<A2>::type>::type>
333                 >::type
334             type;
335         };
336 
337         template <
338             typename A0
339           , typename A1
340           , typename A2
341         >
342         struct result_helper<true, A0, A1, A2>
343         {
344             typedef typename
345                 make_lazy<this_type
346                   , typename detail::to_lazy_arg<A0>::type
347                   , typename detail::to_lazy_arg<A1>::type
348                   , typename detail::to_lazy_arg<A2>::type>::type
349             type;
350         };
351 
352         // FIXME: we need to change this to conform to the result_of protocol
353         template <
354             typename A0
355           , typename A1 = unused_type
356           , typename A2 = unused_type      // Support up to 3 args
357         >
358         struct result
359         {
360             typedef typename
361                 result_helper<
362                     detail::contains_actor<A0, A1, A2>::value
363                   , A0, A1, A2
364                 >::type
365             type;
366         };
367 
368         template <typename This, typename A0>
369         struct result<This(A0)>
370         {
371             typedef typename
372                 result_helper<
373                     detail::contains_actor<A0, unused_type, unused_type>::value
374                   , A0, unused_type, unused_type
375                 >::type
376             type;
377         };
378 
379         template <typename This, typename A0, typename A1>
380         struct result<This(A0, A1)>
381         {
382             typedef typename
383                 result_helper<
384                     detail::contains_actor<A0, A1, unused_type>::value
385                   , A0, A1, unused_type
386                 >::type
387             type;
388         };
389 
390 
391         template <typename This, typename A0, typename A1, typename A2>
392         struct result<This(A0, A1, A2)>
393         {
394             typedef typename
395                 result_helper<
396                      detail::contains_actor<A0, A1, A2>::value
397                    , A0, A1, A2
398                  >::type
399                  type;
400         };
401 
402         // Note: in the following overloads, SFINAE cannot
403         // be done on return type because of gcc bug #24915:
404         //   http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24915
405         // Hence an additional, fake argument is used for SFINAE,
406         // using a type which can never be a real argument type.
407 
408         // Non-lazy overloads. Only enabled when all
409         // args are immediates (no Phoenix actor).
410 
411         template <typename A0>
412         typename result<A0>::type
operator ()boost::spirit::terminal413         operator()(A0 const& _0_
414           , typename detail::contains_actor<A0>::is_false = 0) const
415         {
416             typedef typename result<A0>::type result_type;
417             typedef typename result_type::proto_child0 child_type;
418             return result_type::make(
419                 child_type(
420                     detail::make_vector(_0_)
421                   , this->proto_base().child0)
422             );
423         }
424 
425         template <typename A0, typename A1>
426         typename result<A0, A1>::type
operator ()boost::spirit::terminal427         operator()(A0 const& _0_, A1 const& _1_
428           , typename detail::contains_actor<A0, A1>::is_false = 0) const
429         {
430             typedef typename result<A0, A1>::type result_type;
431             typedef typename result_type::proto_child0 child_type;
432             return result_type::make(
433                 child_type(
434                     detail::make_vector(_0_, _1_)
435                   , this->proto_base().child0)
436             );
437         }
438 
439         template <typename A0, typename A1, typename A2>
440         typename result<A0, A1, A2>::type
operator ()boost::spirit::terminal441         operator()(A0 const& _0_, A1 const& _1_, A2 const& _2_
442           , typename detail::contains_actor<A0, A1, A2>::is_false = 0) const
443         {
444             typedef typename result<A0, A1, A2>::type result_type;
445             typedef typename result_type::proto_child0 child_type;
446             return result_type::make(
447                 child_type(
448                     detail::make_vector(_0_, _1_, _2_)
449                   , this->proto_base().child0)
450             );
451         }
452 
453         // Lazy overloads. Enabled when at
454         // least one arg is a Phoenix actor.
455         template <typename A0>
456         typename result<A0>::type
operator ()boost::spirit::terminal457         operator()(A0 const& _0_
458           , typename detail::contains_actor<A0>::is_true = 0) const
459         {
460             return make_lazy<this_type
461               , typename phoenix::as_actor<A0>::type>()(*this
462               , phoenix::as_actor<A0>::convert(_0_));
463         }
464 
465         template <typename A0, typename A1>
466         typename result<A0, A1>::type
operator ()boost::spirit::terminal467         operator()(A0 const& _0_, A1 const& _1_
468           , typename detail::contains_actor<A0, A1>::is_true = 0) const
469         {
470             return make_lazy<this_type
471               , typename phoenix::as_actor<A0>::type
472               , typename phoenix::as_actor<A1>::type>()(*this
473               , phoenix::as_actor<A0>::convert(_0_)
474               , phoenix::as_actor<A1>::convert(_1_));
475         }
476 
477         template <typename A0, typename A1, typename A2>
478         typename result<A0, A1, A2>::type
operator ()boost::spirit::terminal479         operator()(A0 const& _0_, A1 const& _1_, A2 const& _2_
480           , typename detail::contains_actor<A0, A1, A2>::is_true = 0) const
481         {
482             return make_lazy<this_type
483               , typename phoenix::as_actor<A0>::type
484               , typename phoenix::as_actor<A1>::type
485               , typename phoenix::as_actor<A2>::type>()(*this
486               , phoenix::as_actor<A0>::convert(_0_)
487               , phoenix::as_actor<A1>::convert(_1_)
488               , phoenix::as_actor<A2>::convert(_2_));
489         }
490 
491         // silence MSVC warning C4512: assignment operator could not be generated
492         BOOST_DELETED_FUNCTION(terminal& operator= (terminal const&))
493     };
494 
495     ///////////////////////////////////////////////////////////////////////////
496     namespace result_of
497     {
498         // Calculate the type of the compound terminal if generated by one of
499         // the spirit::terminal::operator() overloads above
500 
501         // The terminal type itself is passed through without modification
502         template <typename Tag>
503         struct terminal
504         {
505             typedef spirit::terminal<Tag> type;
506         };
507 
508         template <typename Tag, typename A0>
509         struct terminal<Tag(A0)>
510         {
511             typedef typename spirit::terminal<Tag>::
512                 template result<A0>::type type;
513         };
514 
515         template <typename Tag, typename A0, typename A1>
516         struct terminal<Tag(A0, A1)>
517         {
518             typedef typename spirit::terminal<Tag>::
519                 template result<A0, A1>::type type;
520         };
521 
522         template <typename Tag, typename A0, typename A1, typename A2>
523         struct terminal<Tag(A0, A1, A2)>
524         {
525             typedef typename spirit::terminal<Tag>::
526                 template result<A0, A1, A2>::type type;
527         };
528     }
529 
530     ///////////////////////////////////////////////////////////////////////////
531     // support for stateful tag types
532     namespace tag
533     {
534         template <
535             typename Data, typename Tag
536           , typename DataTag1 = unused_type, typename DataTag2 = unused_type>
537         struct stateful_tag
538         {
539             BOOST_SPIRIT_IS_TAG()
540 
541             typedef Data data_type;
542 
stateful_tagboost::spirit::tag::stateful_tag543             stateful_tag() {}
stateful_tagboost::spirit::tag::stateful_tag544             stateful_tag(data_type const& data) : data_(data) {}
545 
546             data_type data_;
547 
548             // silence MSVC warning C4512: assignment operator could not be generated
549             BOOST_DELETED_FUNCTION(stateful_tag& operator= (stateful_tag const&))
550         };
551     }
552 
553     template <
554         typename Data, typename Tag
555       , typename DataTag1 = unused_type, typename DataTag2 = unused_type>
556     struct stateful_tag_type
557       : spirit::terminal<tag::stateful_tag<Data, Tag, DataTag1, DataTag2> >
558     {
559         typedef tag::stateful_tag<Data, Tag, DataTag1, DataTag2> tag_type;
560 
stateful_tag_typeboost::spirit::stateful_tag_type561         stateful_tag_type() {}
stateful_tag_typeboost::spirit::stateful_tag_type562         stateful_tag_type(Data const& data)
563           : spirit::terminal<tag_type>(data)
564         {}
565 
566         // silence MSVC warning C4512: assignment operator could not be generated
567         BOOST_DELETED_FUNCTION(stateful_tag_type& operator= (stateful_tag_type const&))
568     };
569 
570     namespace detail
571     {
572         // extract expression if this is a Tag
573         template <typename StatefulTag>
574         struct get_stateful_data
575         {
576             typedef typename StatefulTag::data_type data_type;
577 
578             // is invoked if given tag is != Tag
579             template <typename Tag_>
callboost::spirit::detail::get_stateful_data580             static data_type call(Tag_) { return data_type(); }
581 
582             // this is invoked if given tag is same as'Tag'
callboost::spirit::detail::get_stateful_data583             static data_type const& call(StatefulTag const& t) { return t.data_; }
584         };
585     }
586 
587 }}
588 
589 namespace boost { namespace phoenix
590 {
591     template <typename Tag>
592     struct is_custom_terminal<Tag, typename Tag::is_spirit_tag>
593       : mpl::true_
594     {};
595 
596     template <typename Tag>
597     struct custom_terminal<Tag, typename Tag::is_spirit_tag>
598     {
599 #ifndef BOOST_PHOENIX_NO_SPECIALIZE_CUSTOM_TERMINAL
600         typedef void _is_default_custom_terminal; // fix for #7730
601 #endif
602 
603         typedef spirit::terminal<Tag> result_type;
604 
605         template <typename Context>
operator ()boost::phoenix::custom_terminal606         result_type operator()(Tag const & t, Context const &)
607         {
608             return spirit::terminal<Tag>(t);
609         }
610     };
611 }}
612 
613 // Define a spirit terminal. This macro may be placed in any namespace.
614 // Common placeholders are placed in the main boost::spirit namespace
615 // (see common_terminals.hpp)
616 
617 #define BOOST_SPIRIT_TERMINAL_X(x, y) ((x, y)) BOOST_SPIRIT_TERMINAL_Y
618 #define BOOST_SPIRIT_TERMINAL_Y(x, y) ((x, y)) BOOST_SPIRIT_TERMINAL_X
619 #define BOOST_SPIRIT_TERMINAL_X0
620 #define BOOST_SPIRIT_TERMINAL_Y0
621 
622 #ifndef BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
623 
624 #define BOOST_SPIRIT_TERMINAL_NAME(name, type_name)                             \
625     namespace tag { struct name { BOOST_SPIRIT_IS_TAG() }; }                    \
626     typedef boost::proto::terminal<tag::name>::type type_name;                  \
627     type_name const name = {{}};                                                \
628     inline void BOOST_PP_CAT(silence_unused_warnings_, name)() { (void) name; } \
629     /***/
630 
631 #else
632 
633 #define BOOST_SPIRIT_TERMINAL_NAME(name, type_name)                             \
634     namespace tag { struct name { BOOST_SPIRIT_IS_TAG() }; }                    \
635     typedef boost::proto::terminal<tag::name>::type type_name;                  \
636     /***/
637 
638 #endif
639 
640 #define BOOST_SPIRIT_TERMINAL(name)                                             \
641     BOOST_SPIRIT_TERMINAL_NAME(name, name ## _type)                             \
642     /***/
643 
644 #define BOOST_SPIRIT_DEFINE_TERMINALS_NAME_A(r, _, names)                       \
645     BOOST_SPIRIT_TERMINAL_NAME(                                                 \
646         BOOST_PP_TUPLE_ELEM(2, 0, names),                                       \
647         BOOST_PP_TUPLE_ELEM(2, 1, names)                                        \
648     )                                                                           \
649     /***/
650 
651 #define BOOST_SPIRIT_DEFINE_TERMINALS_NAME(seq)                                 \
652     BOOST_PP_SEQ_FOR_EACH(BOOST_SPIRIT_DEFINE_TERMINALS_NAME_A, _,              \
653         BOOST_PP_CAT(BOOST_SPIRIT_TERMINAL_X seq, 0))                           \
654     /***/
655 
656 // Define a spirit extended terminal. This macro may be placed in any namespace.
657 // Common placeholders are placed in the main boost::spirit namespace
658 // (see common_terminals.hpp)
659 
660 #ifndef BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
661 
662 #define BOOST_SPIRIT_TERMINAL_NAME_EX(name, type_name)                          \
663     namespace tag { struct name { BOOST_SPIRIT_IS_TAG() }; }                    \
664     typedef boost::spirit::terminal<tag::name> type_name;                       \
665     type_name const name = type_name();                                         \
666     inline void BOOST_PP_CAT(silence_unused_warnings_, name)() { (void) name; } \
667     /***/
668 
669 #else
670 
671 #define BOOST_SPIRIT_TERMINAL_NAME_EX(name, type_name)                          \
672     namespace tag { struct name { BOOST_SPIRIT_IS_TAG() }; }                    \
673     typedef boost::spirit::terminal<tag::name> type_name;                       \
674     /***/
675 
676 #endif
677 
678 #define BOOST_SPIRIT_TERMINAL_EX(name)                                          \
679     BOOST_SPIRIT_TERMINAL_NAME_EX(name, name ## _type)                          \
680     /***/
681 
682 #define BOOST_SPIRIT_DEFINE_TERMINALS_NAME_EX_A(r, _, names)                    \
683     BOOST_SPIRIT_TERMINAL_NAME_EX(                                              \
684         BOOST_PP_TUPLE_ELEM(2, 0, names),                                       \
685         BOOST_PP_TUPLE_ELEM(2, 1, names)                                        \
686     )                                                                           \
687     /***/
688 
689 #define BOOST_SPIRIT_DEFINE_TERMINALS_NAME_EX(seq)                              \
690     BOOST_PP_SEQ_FOR_EACH(BOOST_SPIRIT_DEFINE_TERMINALS_NAME_EX_A, _,           \
691         BOOST_PP_CAT(BOOST_SPIRIT_TERMINAL_X seq, 0))                           \
692     /***/
693 
694 #endif
695 
696 
697