1 // Copyright (c) 2009 Francois Barel
2 // Copyright (c) 2001-2011 Joel de Guzman
3 // Copyright (c) 2001-2012 Hartmut Kaiser
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)
8 #if !defined(BOOST_SPIRIT_REPOSITORY_KARMA_SUBRULE_AUGUST_12_2009_0813PM)
9 #define BOOST_SPIRIT_REPOSITORY_KARMA_SUBRULE_AUGUST_12_2009_0813PM
15 #include <boost/spirit/home/karma/domain.hpp>
16 #include <boost/spirit/home/karma/meta_compiler.hpp>
17 #include <boost/spirit/home/karma/generator.hpp>
18 #include <boost/spirit/home/karma/reference.hpp>
19 #include <boost/spirit/home/karma/nonterminal/detail/generator_binder.hpp>
20 #include <boost/spirit/home/karma/nonterminal/detail/parameterized.hpp>
21 #include <boost/spirit/home/support/argument.hpp>
22 #include <boost/spirit/home/support/assert_msg.hpp>
23 #include <boost/spirit/home/karma/detail/attributes.hpp>
24 #include <boost/spirit/home/support/info.hpp>
25 #include <boost/spirit/home/support/unused.hpp>
26 #include <boost/spirit/home/support/nonterminal/extract_param.hpp>
27 #include <boost/spirit/home/support/nonterminal/locals.hpp>
28 #include <boost/spirit/repository/home/support/subrule_context.hpp>
30 #include <boost/static_assert.hpp>
31 #include <boost/fusion/include/as_map.hpp>
32 #include <boost/fusion/include/at_key.hpp>
33 #include <boost/fusion/include/cons.hpp>
34 #include <boost/fusion/include/front.hpp>
35 #include <boost/fusion/include/has_key.hpp>
36 #include <boost/fusion/include/join.hpp>
37 #include <boost/fusion/include/make_map.hpp>
38 #include <boost/fusion/include/make_vector.hpp>
39 #include <boost/fusion/include/size.hpp>
40 #include <boost/fusion/include/vector.hpp>
41 #include <boost/mpl/bool.hpp>
42 #include <boost/mpl/identity.hpp>
43 #include <boost/mpl/int.hpp>
44 #include <boost/mpl/vector.hpp>
45 #include <boost/proto/extends.hpp>
46 #include <boost/proto/traits.hpp>
47 #include <boost/type_traits/is_const.hpp>
48 #include <boost/type_traits/is_reference.hpp>
49 #include <boost/type_traits/is_same.hpp>
50 #include <boost/type_traits/remove_reference.hpp>
52 #if defined(BOOST_MSVC)
53 # pragma warning(push)
54 # pragma warning(disable: 4355) // 'this' : used in base member initializer list warning
57 ///////////////////////////////////////////////////////////////////////////////
58 namespace boost { namespace spirit { namespace repository { namespace karma
60 ///////////////////////////////////////////////////////////////////////////
61 // subrule_group_generator:
62 // - generator representing a group of subrule definitions (one or more),
63 // invokes first subrule on entry,
64 ///////////////////////////////////////////////////////////////////////////
65 template <typename Defs>
66 struct subrule_group_generator
67 : spirit::karma::generator<subrule_group_generator<Defs> >
69 // Fusion associative sequence, associating each subrule ID in this
70 // group (as an MPL integral constant) with its definition
71 typedef Defs defs_type;
73 typedef subrule_group_generator<Defs> this_type;
75 explicit subrule_group_generator(Defs const& defs)
79 // from a subrule ID, get the type of a reference to its definition
83 typedef mpl::int_<ID> id_type;
85 // If you are seeing a compilation error here, you are trying
86 // to use a subrule which was not defined in this group.
87 BOOST_SPIRIT_ASSERT_MSG(
88 (fusion::result_of::has_key<
89 defs_type const, id_type>::type::value)
90 , subrule_used_without_being_defined, (mpl::int_<ID>));
93 fusion::result_of::at_key<defs_type const, id_type>::type
97 // from a subrule ID, get a reference to its definition
99 typename def_type<ID>::type def() const
101 return fusion::at_key<mpl::int_<ID> >(defs);
104 template <typename Context, typename Iterator>
106 // Forward to first subrule.
108 typename remove_reference<
109 typename fusion::result_of::front<Defs>::type
110 >::type::second_type::attr_type> {};
112 template <typename OutputIterator, typename Context
113 , typename Delimiter, typename Attribute>
114 bool generate(OutputIterator& sink, Context& context
115 , Delimiter const& delimiter, Attribute const& attr) const
117 // Forward to first subrule.
118 return generate_subrule(fusion::front(defs).second
119 , sink, context, delimiter, attr);
122 template <typename OutputIterator, typename Context
123 , typename Delimiter, typename Attribute
125 bool generate(OutputIterator& sink, Context& context
126 , Delimiter const& delimiter, Attribute const& attr
127 , Params const& params) const
129 // Forward to first subrule.
130 return generate_subrule(fusion::front(defs).second
131 , sink, context, delimiter, attr, params);
134 template <int ID, typename OutputIterator, typename Context
135 , typename Delimiter, typename Attribute>
136 bool generate_subrule_id(OutputIterator& sink
137 , Context& context, Delimiter const& delimiter
138 , Attribute const& attr) const
140 return generate_subrule(def<ID>()
141 , sink, context, delimiter, attr);
144 template <int ID, typename OutputIterator, typename Context
145 , typename Delimiter, typename Attribute, typename Params>
146 bool generate_subrule_id(OutputIterator& sink
147 , Context& context, Delimiter const& delimiter
148 , Attribute const& attr, Params const& params) const
150 return generate_subrule(def<ID>()
151 , sink, context, delimiter, attr, params);
154 template <typename Def, typename OutputIterator, typename Context
155 , typename Delimiter, typename Attribute>
156 bool generate_subrule(Def const& def, OutputIterator& sink
157 , Context& /*caller_context*/, Delimiter const& delimiter
158 , Attribute const& attr) const
160 // compute context type for this subrule
161 typedef typename Def::locals_type subrule_locals_type;
162 typedef typename Def::attr_type subrule_attr_type;
163 typedef typename Def::attr_reference_type subrule_attr_reference_type;
164 typedef typename Def::parameter_types subrule_parameter_types;
170 subrule_attr_reference_type, subrule_parameter_types>
171 , subrule_locals_type
175 typedef traits::transform_attribute<Attribute const
176 , subrule_attr_type, spirit::karma::domain> transform;
178 // If you are seeing a compilation error here, you are probably
179 // trying to use a subrule which has inherited attributes,
180 // without passing values for them.
181 context_type context(*this, transform::pre(attr));
183 return def.binder(sink, context, delimiter);
186 template <typename Def, typename OutputIterator, typename Context
187 , typename Delimiter, typename Attribute, typename Params>
188 bool generate_subrule(Def const& def, OutputIterator& sink
189 , Context& caller_context, Delimiter const& delimiter
190 , Attribute const& attr, Params const& params) const
192 // compute context type for this subrule
193 typedef typename Def::locals_type subrule_locals_type;
194 typedef typename Def::attr_type subrule_attr_type;
195 typedef typename Def::attr_reference_type subrule_attr_reference_type;
196 typedef typename Def::parameter_types subrule_parameter_types;
202 subrule_attr_reference_type, subrule_parameter_types>
203 , subrule_locals_type
207 typedef traits::transform_attribute<Attribute const
208 , subrule_attr_type, spirit::karma::domain> transform;
210 // If you are seeing a compilation error here, you are probably
211 // trying to use a subrule which has inherited attributes,
212 // passing values of incompatible types for them.
213 context_type context(*this
214 , transform::pre(attr), params, caller_context);
216 return def.binder(sink, context, delimiter);
219 template <typename Context>
220 info what(Context& context) const
222 // Forward to first subrule.
223 return fusion::front(defs).second.binder.g.what(context);
229 ///////////////////////////////////////////////////////////////////////////
231 // - a Proto terminal, so that a group behaves like any Spirit
233 ///////////////////////////////////////////////////////////////////////////
234 template <typename Defs>
237 typename proto::terminal<
238 subrule_group_generator<Defs>
240 , subrule_group<Defs>
243 typedef subrule_group_generator<Defs> generator_type;
244 typedef typename proto::terminal<generator_type>::type terminal;
247 // Forward to first subrule.
249 typename fusion::result_of::front<Defs>::type
250 >::type::second_type::subject_type::properties {};
252 static size_t const params_size =
253 // Forward to first subrule.
255 typename fusion::result_of::front<Defs>::type
256 >::type::second_type::params_size;
258 explicit subrule_group(Defs const& defs)
259 : subrule_group::proto_extends(terminal::make(generator_type(defs)))
263 generator_type const& generator() const { return proto::value(*this); }
265 Defs const& defs() const { return generator().defs; }
267 template <typename Defs2>
269 typename fusion::result_of::as_map<
270 typename fusion::result_of::join<
271 Defs const, Defs2 const>::type>::type>
272 operator,(subrule_group<Defs2> const& other) const
274 typedef subrule_group<
275 typename fusion::result_of::as_map<
276 typename fusion::result_of::join<
277 Defs const, Defs2 const>::type>::type> result_type;
278 return result_type(fusion::as_map(fusion::join(defs(), other.defs())));
281 // non-const versions needed to suppress proto's comma op kicking in
282 template <typename Defs2>
283 friend subrule_group<
284 typename fusion::result_of::as_map<
285 typename fusion::result_of::join<
286 Defs const, Defs2 const>::type>::type>
287 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
288 operator,(subrule_group&& left, subrule_group<Defs2>&& other)
290 operator,(subrule_group& left, subrule_group<Defs2>& other)
293 return static_cast<subrule_group const&>(left)
294 .operator,(static_cast<subrule_group<Defs2> const&>(other));
297 // bring in the operator() overloads
298 generator_type const& get_parameterized_subject() const { return generator(); }
299 typedef generator_type parameterized_subject_type;
300 #include <boost/spirit/home/karma/nonterminal/detail/fcall.hpp>
303 ///////////////////////////////////////////////////////////////////////////
304 // subrule_definition: holds one particular definition of a subrule
305 ///////////////////////////////////////////////////////////////////////////
311 , typename Parameters
316 struct subrule_definition
318 typedef mpl::int_<ID_> id_type;
319 BOOST_STATIC_CONSTANT(int, ID = ID_);
321 typedef Locals locals_type;
322 typedef Attr attr_type;
323 typedef AttrRef attr_reference_type;
324 typedef Parameters parameter_types;
325 static size_t const params_size = ParamsSize;
327 typedef Subject subject_type;
328 typedef mpl::bool_<Auto_> auto_type;
329 BOOST_STATIC_CONSTANT(bool, Auto = Auto_);
331 typedef spirit::karma::detail::generator_binder<
332 Subject, auto_type> binder_type;
334 subrule_definition(Subject const& subject, std::string const& name)
335 : binder(subject), name(name)
339 binder_type const binder;
340 std::string const name;
343 ///////////////////////////////////////////////////////////////////////////
344 // subrule placeholder:
345 // - on subrule definition: helper for creation of subrule_group,
346 // - on subrule invocation: Proto terminal and generator.
347 ///////////////////////////////////////////////////////////////////////////
350 , typename T1 = unused_type
351 , typename T2 = unused_type
355 typename proto::terminal<
356 spirit::karma::reference<subrule<ID_, T1, T2> const>
358 , subrule<ID_, T1, T2>
360 , spirit::karma::generator<subrule<ID_, T1, T2> >
362 //FIXME should go fetch the real properties of this subrule's definition in the current context, but we don't
363 // have the context here (properties would need to be 'template<typename Context> struct properties' instead)
365 spirit::karma::generator_properties::all_properties> properties;
367 typedef mpl::int_<ID_> id_type;
368 BOOST_STATIC_CONSTANT(int, ID = ID_);
370 typedef subrule<ID_, T1, T2> this_type;
371 typedef spirit::karma::reference<this_type const> reference_;
372 typedef typename proto::terminal<reference_>::type terminal;
373 typedef proto::extends<terminal, this_type> base_type;
375 typedef mpl::vector<T1, T2> template_params;
377 // The subrule's locals_type: a sequence of types to be used as local variables
379 spirit::detail::extract_locals<template_params>::type
382 // The subrule's encoding type
384 spirit::detail::extract_encoding<template_params>::type
387 // The subrule's signature
389 spirit::detail::extract_sig<template_params, encoding_type
390 , spirit::karma::domain>::type
393 // This is the subrule's attribute type
395 spirit::detail::attr_from_sig<sig_type>::type
397 BOOST_STATIC_ASSERT_MSG(
398 !is_reference<attr_type>::value && !is_const<attr_type>::value,
399 "Const/reference qualifiers on Karma subrule attribute are meaningless");
400 typedef attr_type const& attr_reference_type;
402 // parameter_types is a sequence of types passed as parameters to the subrule
404 spirit::detail::params_from_sig<sig_type>::type
407 static size_t const params_size =
408 fusion::result_of::size<parameter_types>::type::value;
410 explicit subrule(std::string const& name_ = "unnamed-subrule")
411 : base_type(terminal::make(reference_(*this)))
416 // compute type of this subrule's definition for expr type Expr
417 template <typename Expr, bool Auto>
418 struct def_type_helper
420 // Report invalid expression error as early as possible.
421 // If you got an error_invalid_expression error message here,
422 // then the expression (Expr) is not a valid spirit karma expression.
423 BOOST_SPIRIT_ASSERT_MATCH(spirit::karma::domain, Expr);
425 typedef typename result_of::compile<
426 spirit::karma::domain, Expr>::type subject_type;
428 typedef subrule_definition<
432 , attr_reference_type
440 // compute type of subrule group containing only this
441 // subrule's definition for expr type Expr
442 template <typename Expr, bool Auto>
443 struct group_type_helper
445 typedef typename def_type_helper<Expr, Auto>::type def_type;
447 // create Defs map with only one entry: (ID -> def)
449 #ifndef BOOST_FUSION_HAS_VARIADIC_MAP
450 fusion::result_of::make_map<id_type, def_type>::type
452 fusion::result_of::make_map<id_type>::template apply<def_type>::type
456 typedef subrule_group<defs_type> type;
459 template <typename Expr>
460 typename group_type_helper<Expr, false>::type
461 operator=(Expr const& expr) const
463 typedef group_type_helper<Expr, false> helper;
464 typedef typename helper::def_type def_type;
465 typedef typename helper::type result_type;
466 return result_type(fusion::make_map<id_type>(
467 def_type(compile<spirit::karma::domain>(expr), name_)));
470 #define BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(lhs_ref, rhs_ref) \
471 template <typename Expr> \
472 friend typename group_type_helper<Expr, true>::type \
473 operator%=(subrule lhs_ref sr, Expr rhs_ref expr) \
475 typedef group_type_helper<Expr, true> helper; \
476 typedef typename helper::def_type def_type; \
477 typedef typename helper::type result_type; \
478 return result_type(fusion::make_map<id_type>( \
479 def_type(compile<spirit::karma::domain>(expr), sr.name_))); \
483 // non-const versions needed to suppress proto's %= kicking in
484 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, const&)
485 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
486 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, &&)
488 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, &)
490 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, const&)
491 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
492 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, &&)
494 BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, &)
497 #undef BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR
499 std::string const& name() const
504 void name(std::string const& str)
509 template <typename Context, typename Iterator>
512 typedef attr_type type;
515 template <typename OutputIterator, typename Group
516 , typename Attributes, typename Locals
517 , typename Delimiter, typename Attribute>
518 bool generate(OutputIterator& sink
519 , subrule_context<Group, Attributes, Locals>& context
520 , Delimiter const& delimiter, Attribute const& attr) const
522 return context.group.template generate_subrule_id<ID_>(
523 sink, context, delimiter, attr);
526 template <typename OutputIterator, typename Context
527 , typename Delimiter, typename Attribute>
528 bool generate(OutputIterator& /*sink*/
529 , Context& /*context*/
530 , Delimiter const& /*delimiter*/, Attribute const& /*attr*/) const
532 // If you are seeing a compilation error here, you are trying
533 // to use a subrule as a generator outside of a subrule group.
534 BOOST_SPIRIT_ASSERT_FAIL(OutputIterator
535 , subrule_used_outside_subrule_group, (id_type));
540 template <typename OutputIterator, typename Group
541 , typename Attributes, typename Locals
542 , typename Delimiter, typename Attribute
544 bool generate(OutputIterator& sink
545 , subrule_context<Group, Attributes, Locals>& context
546 , Delimiter const& delimiter, Attribute const& attr
547 , Params const& params) const
549 return context.group.template generate_subrule_id<ID_>(
550 sink, context, delimiter, attr, params);
553 template <typename OutputIterator, typename Context
554 , typename Delimiter, typename Attribute
556 bool generate(OutputIterator& /*sink*/
557 , Context& /*context*/
558 , Delimiter const& /*delimiter*/, Attribute const& /*attr*/
559 , Params const& /*params*/) const
561 // If you are seeing a compilation error here, you are trying
562 // to use a subrule as a generator outside of a subrule group.
563 BOOST_SPIRIT_ASSERT_FAIL(OutputIterator
564 , subrule_used_outside_subrule_group, (id_type));
569 template <typename Context>
570 info what(Context& /*context*/) const
575 // bring in the operator() overloads
576 this_type const& get_parameterized_subject() const { return *this; }
577 typedef this_type parameterized_subject_type;
578 #include <boost/spirit/home/karma/nonterminal/detail/fcall.hpp>
584 #if defined(BOOST_MSVC)
585 # pragma warning(pop)