• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //  Copyright (c) 2001-2011 Joel de Guzman
2 //  Copyright (c) 2001-2011 Hartmut Kaiser
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_RULE_MAR_05_2007_0455PM)
8 #define BOOST_SPIRIT_KARMA_RULE_MAR_05_2007_0455PM
9 
10 #if defined(_MSC_VER)
11 #pragma once
12 #endif
13 
14 #include <boost/assert.hpp>
15 #include <boost/config.hpp>
16 #include <boost/function.hpp>
17 #include <boost/mpl/vector.hpp>
18 #include <boost/type_traits/is_same.hpp>
19 
20 #include <boost/fusion/include/vector.hpp>
21 #include <boost/fusion/include/size.hpp>
22 #include <boost/fusion/include/make_vector.hpp>
23 #include <boost/fusion/include/cons.hpp>
24 #include <boost/fusion/include/as_list.hpp>
25 #include <boost/fusion/include/as_vector.hpp>
26 
27 #include <boost/spirit/home/support/unused.hpp>
28 #include <boost/spirit/home/support/argument.hpp>
29 #include <boost/spirit/home/support/context.hpp>
30 #include <boost/spirit/home/support/info.hpp>
31 #include <boost/spirit/home/karma/delimit_out.hpp>
32 #include <boost/spirit/home/karma/detail/attributes.hpp>
33 #include <boost/spirit/home/support/nonterminal/extract_param.hpp>
34 #include <boost/spirit/home/support/nonterminal/locals.hpp>
35 #include <boost/spirit/home/karma/reference.hpp>
36 #include <boost/spirit/home/karma/detail/output_iterator.hpp>
37 #include <boost/spirit/home/karma/nonterminal/nonterminal_fwd.hpp>
38 #include <boost/spirit/home/karma/nonterminal/detail/generator_binder.hpp>
39 #include <boost/spirit/home/karma/nonterminal/detail/parameterized.hpp>
40 
41 #include <boost/static_assert.hpp>
42 #include <boost/proto/extends.hpp>
43 #include <boost/proto/traits.hpp>
44 #include <boost/type_traits/is_const.hpp>
45 #include <boost/type_traits/is_reference.hpp>
46 
47 #if defined(BOOST_MSVC)
48 # pragma warning(push)
49 # pragma warning(disable: 4127) // conditional expression is constant
50 # pragma warning(disable: 4355) // 'this' : used in base member initializer list warning
51 #endif
52 
53 namespace boost { namespace spirit { namespace karma
54 {
55     BOOST_PP_REPEAT(SPIRIT_ATTRIBUTES_LIMIT, SPIRIT_USING_ATTRIBUTE, _)
56 
57     using spirit::_pass_type;
58     using spirit::_val_type;
59     using spirit::_a_type;
60     using spirit::_b_type;
61     using spirit::_c_type;
62     using spirit::_d_type;
63     using spirit::_e_type;
64     using spirit::_f_type;
65     using spirit::_g_type;
66     using spirit::_h_type;
67     using spirit::_i_type;
68     using spirit::_j_type;
69 
70 #ifndef BOOST_SPIRIT_NO_PREDEFINED_TERMINALS
71 
72     using spirit::_pass;
73     using spirit::_val;
74     using spirit::_a;
75     using spirit::_b;
76     using spirit::_c;
77     using spirit::_d;
78     using spirit::_e;
79     using spirit::_f;
80     using spirit::_g;
81     using spirit::_h;
82     using spirit::_i;
83     using spirit::_j;
84 
85 #endif
86 
87     using spirit::info;
88     using spirit::locals;
89 
90     template <
91         typename OutputIterator, typename T1, typename T2, typename T3
92       , typename T4>
93     struct rule
94       : proto::extends<
95             typename proto::terminal<
96                 reference<rule<OutputIterator, T1, T2, T3, T4> const>
97             >::type
98           , rule<OutputIterator, T1, T2, T3, T4>
99         >
100       , generator<rule<OutputIterator, T1, T2, T3, T4> >
101     {
102         typedef mpl::int_<generator_properties::all_properties> properties;
103 
104         typedef OutputIterator iterator_type;
105         typedef rule<OutputIterator, T1, T2, T3, T4> this_type;
106         typedef reference<this_type const> reference_;
107         typedef typename proto::terminal<reference_>::type terminal;
108         typedef proto::extends<terminal, this_type> base_type;
109         typedef mpl::vector<T1, T2, T3, T4> template_params;
110 
111         // the output iterator is always wrapped by karma
112         typedef detail::output_iterator<OutputIterator, properties>
113             output_iterator;
114 
115         // locals_type is a sequence of types to be used as local variables
116         typedef typename
117             spirit::detail::extract_locals<template_params>::type
118         locals_type;
119 
120         // The delimiter-generator type
121         typedef typename
122             spirit::detail::extract_component<
123                 karma::domain, template_params>::type
124         delimiter_type;
125 
126         // The rule's encoding type
127         typedef typename
128             spirit::detail::extract_encoding<template_params>::type
129         encoding_type;
130 
131         // The rule's signature
132         typedef typename
133             spirit::detail::extract_sig<template_params, encoding_type, karma::domain>::type
134         sig_type;
135 
136         // This is the rule's attribute type
137         typedef typename
138             spirit::detail::attr_from_sig<sig_type>::type
139         attr_type;
140         BOOST_STATIC_ASSERT_MSG(
141             !is_reference<attr_type>::value && !is_const<attr_type>::value,
142             "Const/reference qualifiers on Karma rule attribute are meaningless");
143         typedef attr_type const& attr_reference_type;
144 
145         // parameter_types is a sequence of types passed as parameters to the rule
146         typedef typename
147             spirit::detail::params_from_sig<sig_type>::type
148         parameter_types;
149 
150         static size_t const params_size =
151             fusion::result_of::size<parameter_types>::type::value;
152 
153         // the context passed to the right hand side of a rule contains
154         // the attribute and the parameters for this particular rule invocation
155         typedef context<
156             fusion::cons<attr_reference_type, parameter_types>
157           , locals_type>
158         context_type;
159 
160         typedef function<
161             bool(output_iterator&, context_type&, delimiter_type const&)>
162         function_type;
163 
164         typedef typename
165             mpl::if_<
166                 is_same<encoding_type, unused_type>
167               , unused_type
168               , tag::char_code<tag::encoding, encoding_type>
169             >::type
170         encoding_modifier_type;
171 
ruleboost::spirit::karma::rule172         explicit rule(std::string const& name_ = "unnamed-rule")
173           : base_type(terminal::make(reference_(*this)))
174           , name_(name_)
175         {
176         }
177 
ruleboost::spirit::karma::rule178         rule(rule const& rhs)
179           : base_type(terminal::make(reference_(*this)))
180           , name_(rhs.name_)
181           , f(rhs.f)
182         {
183         }
184 
185         template <typename Auto, typename Expr>
defineboost::spirit::karma::rule186         static void define(rule& /* lhs */, Expr const& /* expr */, mpl::false_)
187         {
188             // Report invalid expression error as early as possible.
189             // If you got an error_invalid_expression error message here,
190             // then the expression (expr) is not a valid spirit karma expression.
191             BOOST_SPIRIT_ASSERT_MATCH(karma::domain, Expr);
192         }
193 
194         template <typename Auto, typename Expr>
defineboost::spirit::karma::rule195         static void define(rule& lhs, Expr const& expr, mpl::true_)
196         {
197             lhs.f = detail::bind_generator<Auto>(
198                 compile<karma::domain>(expr, encoding_modifier_type()));
199         }
200 
201         template <typename Expr>
ruleboost::spirit::karma::rule202         rule (Expr const& expr, std::string const& name_ = "unnamed-rule")
203           : base_type(terminal::make(reference_(*this)))
204           , name_(name_)
205         {
206             define<mpl::false_>(*this, expr, traits::matches<karma::domain, Expr>());
207         }
208 
operator =boost::spirit::karma::rule209         rule& operator=(rule const& rhs)
210         {
211             // The following assertion fires when you try to initialize a rule
212             // from an uninitialized one. Did you mean to refer to the right
213             // hand side rule instead of assigning from it? In this case you
214             // should write lhs = rhs.alias();
215             BOOST_ASSERT(rhs.f && "Did you mean rhs.alias() instead of rhs?");
216 
217             f = rhs.f;
218             name_ = rhs.name_;
219             return *this;
220         }
221 
nameboost::spirit::karma::rule222         std::string const& name() const
223         {
224             return name_;
225         }
226 
nameboost::spirit::karma::rule227         void name(std::string const& str)
228         {
229             name_ = str;
230         }
231 
232         template <typename Expr>
operator =boost::spirit::karma::rule233         rule& operator=(Expr const& expr)
234         {
235             define<mpl::false_>(*this, expr, traits::matches<karma::domain, Expr>());
236             return *this;
237         }
238 
239 // VC7.1 has problems to resolve 'rule' without explicit template parameters
240 #if !BOOST_WORKAROUND(BOOST_MSVC, < 1400)
241         // g++ 3.3 barfs if this is a member function :(
242         template <typename Expr>
operator %=(rule & r,Expr const & expr)243         friend rule& operator%=(rule& r, Expr const& expr)
244         {
245             define<mpl::true_>(r, expr, traits::matches<karma::domain, Expr>());
246             return r;
247         }
248 
249 #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
250         // non-const version needed to suppress proto's %= kicking in
251         template <typename Expr>
operator %=(rule & r,Expr & expr)252         friend rule& operator%=(rule& r, Expr& expr)
253         {
254             return r %= static_cast<Expr const&>(expr);
255         }
256 #else
257         // for rvalue references
258         template <typename Expr>
operator %=(rule & r,Expr && expr)259         friend rule& operator%=(rule& r, Expr&& expr)
260         {
261             define<mpl::true_>(r, expr, traits::matches<karma::domain, Expr>());
262             return r;
263         }
264 #endif
265 
266 #else
267         // both friend functions have to be defined out of class as VC7.1
268         // will complain otherwise
269         template <typename OutputIterator_, typename T1_, typename T2_
270           , typename T3_, typename T4_, typename Expr>
271         friend rule<OutputIterator_, T1_, T2_, T3_, T4_>& operator%=(
272             rule<OutputIterator_, T1_, T2_, T3_, T4_>&r, Expr const& expr);
273 
274         // non-const version needed to suppress proto's %= kicking in
275         template <typename OutputIterator_, typename T1_, typename T2_
276           , typename T3_, typename T4_, typename Expr>
277         friend rule<OutputIterator_, T1_, T2_, T3_, T4_>& operator%=(
278             rule<OutputIterator_, T1_, T2_, T3_, T4_>& r, Expr& expr);
279 #endif
280 
281         template <typename Context, typename Unused>
282         struct attribute
283         {
284             typedef attr_type type;
285         };
286 
287         template <typename Context, typename Delimiter, typename Attribute>
generateboost::spirit::karma::rule288         bool generate(output_iterator& sink, Context&, Delimiter const& delim
289           , Attribute const& attr) const
290         {
291             if (f)
292             {
293                 // Create an attribute if none is supplied.
294                 typedef traits::transform_attribute<
295                     Attribute const, attr_type, domain>
296                 transform;
297 
298                 typename transform::type attr_ = transform::pre(attr);
299 
300                 // If you are seeing a compilation error here, you are probably
301                 // trying to use a rule or a grammar which has inherited
302                 // attributes, without passing values for them.
303                 context_type context(attr_);
304 
305                 // If you are seeing a compilation error here stating that the
306                 // third parameter can't be converted to a karma::reference
307                 // then you are probably trying to use a rule or a grammar with
308                 // an incompatible delimiter type.
309                 if (f(sink, context, delim))
310                 {
311                     // do a post-delimit if this is an implied verbatim
312                     if (is_same<delimiter_type, unused_type>::value)
313                         karma::delimit_out(sink, delim);
314 
315                     return true;
316                 }
317             }
318             return false;
319         }
320 
321         template <typename Context, typename Delimiter, typename Attribute
322           , typename Params>
generateboost::spirit::karma::rule323         bool generate(output_iterator& sink, Context& caller_context
324           , Delimiter const& delim, Attribute const& attr
325           , Params const& params) const
326         {
327             if (f)
328             {
329                 // Create an attribute if none is supplied.
330                 typedef traits::transform_attribute<
331                     Attribute const, attr_type, domain>
332                 transform;
333 
334                 typename transform::type attr_ = transform::pre(attr);
335 
336                 // If you are seeing a compilation error here, you are probably
337                 // trying to use a rule or a grammar which has inherited
338                 // attributes, passing values of incompatible types for them.
339                 context_type context(attr_, params, caller_context);
340 
341                 // If you are seeing a compilation error here stating that the
342                 // third parameter can't be converted to a karma::reference
343                 // then you are probably trying to use a rule or a grammar with
344                 // an incompatible delimiter type.
345                 if (f(sink, context, delim))
346                 {
347                     // do a post-delimit if this is an implied verbatim
348                     if (is_same<delimiter_type, unused_type>::value)
349                         karma::delimit_out(sink, delim);
350 
351                     return true;
352                 }
353             }
354             return false;
355         }
356 
357         template <typename Context>
whatboost::spirit::karma::rule358         info what(Context& /*context*/) const
359         {
360             return info(name_);
361         }
362 
aliasboost::spirit::karma::rule363         reference_ alias() const
364         {
365             return reference_(*this);
366         }
367 
copyboost::spirit::karma::rule368         typename proto::terminal<this_type>::type copy() const
369         {
370             typename proto::terminal<this_type>::type result = {*this};
371             return result;
372         }
373 
374         // bring in the operator() overloads
get_parameterized_subjectboost::spirit::karma::rule375         rule const& get_parameterized_subject() const { return *this; }
376         typedef rule parameterized_subject_type;
377         #include <boost/spirit/home/karma/nonterminal/detail/fcall.hpp>
378 
379         std::string name_;
380         function_type f;
381     };
382 
383 #if BOOST_WORKAROUND(BOOST_MSVC, < 1400)
384     template <typename OutputIterator_, typename T1_, typename T2_
385       , typename T3_, typename T4_, typename Expr>
operator %=(rule<OutputIterator_,T1_,T2_,T3_,T4_> & r,Expr const & expr)386     rule<OutputIterator_, T1_, T2_, T3_, T4_>& operator%=(
387         rule<OutputIterator_, T1_, T2_, T3_, T4_>&r, Expr const& expr)
388     {
389         // Report invalid expression error as early as possible.
390         // If you got an error_invalid_expression error message here, then
391         // the expression (expr) is not a valid spirit karma expression.
392         BOOST_SPIRIT_ASSERT_MATCH(karma::domain, Expr);
393 
394         typedef typename
395             rule<OutputIterator_, T1_, T2_, T3_, T4_>::encoding_modifier_type
396         encoding_modifier_type;
397 
398         r.f = detail::bind_generator<mpl::true_>(
399             compile<karma::domain>(expr, encoding_modifier_type()));
400         return r;
401     }
402 
403     // non-const version needed to suppress proto's %= kicking in
404     template <typename OutputIterator_, typename T1_, typename T2_
405       , typename T3_, typename T4_, typename Expr>
operator %=(rule<OutputIterator_,T1_,T2_,T3_,T4_> & r,Expr & expr)406     rule<OutputIterator_, T1_, T2_, T3_, T4_>& operator%=(
407         rule<OutputIterator_, T1_, T2_, T3_, T4_>& r, Expr& expr)
408     {
409         return r %= static_cast<Expr const&>(expr);
410     }
411 #endif
412 }}}
413 
414 namespace boost { namespace spirit { namespace traits
415 {
416     namespace detail
417     {
418         template <typename RuleAttribute, typename Attribute>
419         struct nonterminal_handles_container
420           : mpl::and_<
421                 traits::is_container<RuleAttribute>
422               , is_convertible<Attribute, RuleAttribute> >
423         {};
424     }
425 
426     ///////////////////////////////////////////////////////////////////////////
427     template <
428         typename IteratorA, typename IteratorB, typename Attribute
429       , typename Context, typename T1, typename T2, typename T3, typename T4>
430     struct handles_container<
431             karma::rule<IteratorA, T1, T2, T3, T4>, Attribute, Context
432           , IteratorB>
433       : detail::nonterminal_handles_container<
434             typename attribute_of<
435                 karma::rule<IteratorA, T1, T2, T3, T4>
436               , Context, IteratorB
437             >::type, Attribute>
438     {};
439 }}}
440 
441 #if defined(BOOST_MSVC)
442 # pragma warning(pop)
443 #endif
444 
445 #endif
446