1 /*==============================================================================
2     Copyright (c) 2001-2010 Joel de Guzman
3     Copyright (c) 2010 Thomas Heller
4 
5     Distributed under the Boost Software License, Version 1.0. (See accompanying
6     file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 ==============================================================================*/
8 #ifndef BOOST_PHOENIX_STATEMENT_SWITCH_HPP
9 #define BOOST_PHOENIX_STATEMENT_SWITCH_HPP
10 
11 #include <boost/phoenix/core/limits.hpp>
12 #include <boost/fusion/iterator/advance.hpp>
13 #include <boost/phoenix/core/call.hpp>
14 #include <boost/phoenix/core/expression.hpp>
15 #include <boost/phoenix/core/meta_grammar.hpp>
16 #include <boost/phoenix/core/is_nullary.hpp>
17 #include <boost/phoenix/support/iterate.hpp>
18 #include <boost/proto/make_expr.hpp>
19 #include <boost/proto/fusion.hpp>
20 
21 #ifdef _MSC_VER
22 #pragma warning(push)
23 #pragma warning(disable: 4065) // switch statement contains 'default' but no 'case' labels
24 #endif
25 
26 BOOST_PHOENIX_DEFINE_EXPRESSION(
27     (boost)(phoenix)(switch_case)
28   , (proto::terminal<proto::_>)
29     (meta_grammar)
30 )
31 
32 BOOST_PHOENIX_DEFINE_EXPRESSION(
33     (boost)(phoenix)(switch_default_case)
34   , (meta_grammar)
35 )
36 
37 namespace boost { namespace phoenix
38 {
39     namespace detail
40     {
41         struct switch_case_grammar;
42         struct switch_case_with_default_grammar;
43         struct switch_grammar
44             : proto::or_<
45                 proto::when<
46                     detail::switch_case_grammar
47                   , mpl::false_()
48                 >
49               , proto::when<
50                     detail::switch_case_with_default_grammar
51                   , mpl::true_()
52                 >
53             >
54         {};
55     }
56 
57     namespace detail
58     {
59         struct switch_case_is_nullary
60             : proto::or_<
61                 proto::when<
62                     proto::comma<
63                         switch_case_is_nullary
64                       , proto::or_<phoenix::rule::switch_default_case, phoenix::rule::switch_case>
65                     >
66                   , mpl::and_<
67                         switch_case_is_nullary(
68                             proto::_child_c<0>
69                           , proto::_state
70                         )
71                       , switch_case_is_nullary(
72                             proto::_child_c<1>
73                           , proto::_state
74                         )
75                     >()
76                 >
77               , proto::when<
78                     proto::or_<phoenix::rule::switch_default_case, phoenix::rule::switch_case>
79                   , evaluator(proto::_child_c<0>, proto::_state)
80                 >
81             >
82         {};
83 
84         struct switch_case_grammar
85             : proto::or_<
86                 proto::comma<switch_case_grammar, phoenix::rule::switch_case>
87               , proto::when<phoenix::rule::switch_case, proto::_>
88             >
89         {};
90 
91         struct switch_case_with_default_grammar
92             : proto::or_<
93                 proto::comma<switch_case_grammar, phoenix::rule::switch_default_case>
94               , proto::when<phoenix::rule::switch_default_case, proto::_>
95             >
96         {};
97 
98         struct switch_size
99             : proto::or_<
100                 proto::when<
101                     proto::comma<switch_size, proto::_>
102                   , mpl::next<switch_size(proto::_left)>()
103                 >
104               , proto::when<proto::_, mpl::int_<1>()>
105             >
106         {};
107     }
108 }}
109 
110 BOOST_PHOENIX_DEFINE_EXPRESSION(
111     (boost)(phoenix)(switch_)
112   , (meta_grammar)           // Cond
113     (detail::switch_grammar) // Cases
114 )
115 
116 namespace boost { namespace phoenix {
117 
118     template <typename Dummy>
119     struct is_nullary::when<rule::switch_, Dummy>
120         : proto::and_<
121             evaluator(proto::_child_c<0>, _context)
122           , detail::switch_case_is_nullary(proto::_child_c<1>, _context)
123         >
124     {};
125 
126     struct switch_eval
127     {
128         typedef void result_type;
129 
130         template <typename Context>
131         result_type
operator ()boost::phoenix::switch_eval132         operator()(Context const &) const
133         {
134         }
135 
136         template <typename Cond, typename Cases, typename Context>
137         result_type
operator ()boost::phoenix::switch_eval138         operator()(Cond const & cond, Cases const & cases, Context const & ctx) const
139         {
140             this->evaluate(
141                     ctx
142                   , cond
143                   , cases
144                   , typename detail::switch_size::impl<Cases, int, proto::empty_env>::result_type()
145                   , typename detail::switch_grammar::impl<Cases, int, proto::empty_env>::result_type()
146                 );
147         }
148 
149         private:
150             template <typename Context, typename Cond, typename Cases>
151             result_type
evaluateboost::phoenix::switch_eval152             evaluate(
153                 Context const & ctx
154               , Cond const & cond
155               , Cases const & cases
156               , mpl::int_<1>
157               , mpl::false_
158             ) const
159             {
160                 typedef
161                     typename proto::result_of::value<
162                         typename proto::result_of::child_c<
163                             Cases
164                           , 0
165                         >::type
166                     >::type
167                     case_label;
168 
169                 switch(boost::phoenix::eval(cond, ctx))
170                 {
171                     case case_label::value:
172                         boost::phoenix::eval(proto::child_c<1>(cases), ctx);
173                 }
174             }
175 
176             template <typename Context, typename Cond, typename Cases>
177             result_type
evaluateboost::phoenix::switch_eval178             evaluate(
179                 Context const & ctx
180               , Cond const & cond
181               , Cases const & cases
182               , mpl::int_<1>
183               , mpl::true_
184             ) const
185             {
186                 switch(boost::phoenix::eval(cond, ctx))
187                 {
188                     default:
189                         boost::phoenix::eval(proto::child_c<0>(cases), ctx);
190                 }
191             }
192 
193             // Bring in the evaluation functions
194             #include <boost/phoenix/statement/detail/switch.hpp>
195     };
196 
197     template <typename Dummy>
198     struct default_actions::when<rule::switch_, Dummy>
199         : call<switch_eval>
200     {};
201 
202     template <int N, typename A>
203     inline
204     typename proto::result_of::make_expr<
205         tag::switch_case
206       , proto::basic_default_domain
207       , mpl::int_<N>
208       , A
209     >::type const
case_(A const & a)210     case_(A const & a)
211     {
212         return
213             proto::make_expr<
214                 tag::switch_case
215               , proto::basic_default_domain
216             >(
217                 mpl::int_<N>()
218               , a
219             );
220     }
221 
222     template <typename A>
223     inline
224     typename proto::result_of::make_expr<
225         tag::switch_default_case
226       , proto::basic_default_domain
227       , A
228     >::type const
default_(A const & a)229     default_(A const& a)
230     {
231         return
232             proto::make_expr<
233                 tag::switch_default_case
234               , proto::basic_default_domain
235             >(a);
236     }
237 
238     template <typename Cond>
239     struct switch_gen
240     {
switch_genboost::phoenix::switch_gen241         switch_gen(Cond const& cond_) : cond(cond_) {}
242 
243         template <typename Cases>
244         typename expression::switch_<
245             Cond
246           , Cases
247         >::type
operator []boost::phoenix::switch_gen248         operator[](Cases const& cases) const
249         {
250             return
251                 this->generate(
252                     cases
253                   , proto::matches<Cases, detail::switch_grammar>()
254                 );
255         }
256 
257         private:
258             Cond const& cond;
259 
260             template <typename Cases>
261             typename expression::switch_<
262                 Cond
263               , Cases
264             >::type
generateboost::phoenix::switch_gen265             generate(Cases const & cases, mpl::true_) const
266             {
267                 return expression::switch_<Cond, Cases>::make(cond, cases);
268             }
269 
270             template <typename Cases>
271             typename expression::switch_<
272                 Cond
273               , Cases
274             >::type
generateboost::phoenix::switch_gen275             generate(Cases const &, mpl::false_) const
276             {
277                 BOOST_MPL_ASSERT_MSG(
278                     false
279                   , INVALID_SWITCH_CASE_STATEMENT
280                   , (Cases)
281                 );
282             }
283     };
284 
285     template <typename Cond>
286     inline
287     switch_gen<Cond> const
switch_(Cond const & cond)288     switch_(Cond const& cond)
289     {
290         return switch_gen<Cond>(cond);
291     }
292 
293 }}
294 
295 #ifdef _MSC_VER
296 #pragma warning(pop)
297 #endif
298 
299 #endif
300 
301