• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*=============================================================================
2     Copyright (c) 2009 Francois Barel
3     Copyright (c) 2001-2011 Joel de Guzman
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 #if !defined(BOOST_SPIRIT_REPOSITORY_QI_SUBRULE_AUGUST_06_2009_0239AM)
9 #define BOOST_SPIRIT_REPOSITORY_QI_SUBRULE_AUGUST_06_2009_0239AM
10 
11 #if defined(_MSC_VER)
12 #pragma once
13 #endif
14 
15 #include <boost/spirit/home/qi/domain.hpp>
16 #include <boost/spirit/home/qi/meta_compiler.hpp>
17 #include <boost/spirit/home/qi/parser.hpp>
18 #include <boost/spirit/home/qi/reference.hpp>
19 #include <boost/spirit/home/qi/nonterminal/detail/parameterized.hpp>
20 #include <boost/spirit/home/qi/nonterminal/detail/parser_binder.hpp>
21 #include <boost/spirit/home/support/argument.hpp>
22 #include <boost/spirit/home/support/assert_msg.hpp>
23 #include <boost/spirit/home/qi/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>
29 
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_reference.hpp>
48 #include <boost/type_traits/is_same.hpp>
49 #include <boost/type_traits/remove_reference.hpp>
50 
51 #if defined(BOOST_MSVC)
52 # pragma warning(push)
53 # pragma warning(disable: 4355) // 'this' : used in base member initializer list warning
54 #endif
55 
56 ///////////////////////////////////////////////////////////////////////////////
57 namespace boost { namespace spirit { namespace repository { namespace qi
58 {
59     ///////////////////////////////////////////////////////////////////////////
60     // subrule_group_parser:
61     // - parser representing a group of subrule definitions (one or more),
62     //   invokes first subrule on entry,
63     ///////////////////////////////////////////////////////////////////////////
64     template <typename Defs>
65     struct subrule_group_parser
66       : spirit::qi::parser<subrule_group_parser<Defs> >
67     {
68         // Fusion associative sequence, associating each subrule ID in this
69         // group (as an MPL integral constant) with its definition
70         typedef Defs defs_type;
71 
72         typedef subrule_group_parser<Defs> this_type;
73 
subrule_group_parserboost::spirit::repository::qi::subrule_group_parser74         explicit subrule_group_parser(Defs const& defs)
75           : defs(defs)
76         {
77         }
78 
79         // from a subrule ID, get the type of a reference to its definition
80         template <int ID>
81         struct def_type
82         {
83             typedef mpl::int_<ID> id_type;
84 
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>));
91 
92             typedef typename
93                 fusion::result_of::at_key<defs_type const, id_type>::type
94             type;
95         };
96 
97         // from a subrule ID, get a reference to its definition
98         template <int ID>
defboost::spirit::repository::qi::subrule_group_parser99         typename def_type<ID>::type def() const
100         {
101             return fusion::at_key<mpl::int_<ID> >(defs);
102         }
103 
104         template <typename Context, typename Iterator>
105         struct attribute
106             // Forward to first subrule.
107           : mpl::identity<
108                 typename remove_reference<
109                     typename fusion::result_of::front<Defs>::type
110                 >::type::second_type::attr_type> {};
111 
112         template <typename Iterator, typename Context
113           , typename Skipper, typename Attribute>
parseboost::spirit::repository::qi::subrule_group_parser114         bool parse(Iterator& first, Iterator const& last
115           , Context& context, Skipper const& skipper
116           , Attribute& attr) const
117         {
118             // Forward to first subrule.
119             return parse_subrule(fusion::front(defs).second
120               , first, last, context, skipper, attr);
121         }
122 
123         template <typename Iterator, typename Context
124           , typename Skipper, typename Attribute, typename Params>
parseboost::spirit::repository::qi::subrule_group_parser125         bool parse(Iterator& first, Iterator const& last
126           , Context& context, Skipper const& skipper
127           , Attribute& attr, Params const& params) const
128         {
129             // Forward to first subrule.
130             return parse_subrule(fusion::front(defs).second
131               , first, last, context, skipper, attr, params);
132         }
133 
134         template <int ID, typename Iterator, typename Context
135           , typename Skipper, typename Attribute>
parse_subrule_idboost::spirit::repository::qi::subrule_group_parser136         bool parse_subrule_id(Iterator& first, Iterator const& last
137           , Context& context, Skipper const& skipper
138           , Attribute& attr) const
139         {
140             return parse_subrule(def<ID>()
141               , first, last, context, skipper, attr);
142         }
143 
144         template <int ID, typename Iterator, typename Context
145           , typename Skipper, typename Attribute, typename Params>
parse_subrule_idboost::spirit::repository::qi::subrule_group_parser146         bool parse_subrule_id(Iterator& first, Iterator const& last
147           , Context& context, Skipper const& skipper
148           , Attribute& attr, Params const& params) const
149         {
150             return parse_subrule(def<ID>()
151               , first, last, context, skipper, attr, params);
152         }
153 
154         template <typename Def
155           , typename Iterator, typename Context
156           , typename Skipper, typename Attribute>
parse_subruleboost::spirit::repository::qi::subrule_group_parser157         bool parse_subrule(Def const& def
158           , Iterator& first, Iterator const& last
159           , Context& /*caller_context*/, Skipper const& skipper
160           , Attribute& attr) const
161         {
162             // compute context type for this subrule
163             typedef typename Def::locals_type subrule_locals_type;
164             typedef typename Def::attr_type subrule_attr_type;
165             typedef typename Def::attr_reference_type subrule_attr_reference_type;
166             typedef typename Def::parameter_types subrule_parameter_types;
167 
168             typedef
169                 subrule_context<
170                     this_type
171                   , fusion::cons<
172                         subrule_attr_reference_type, subrule_parameter_types>
173                   , subrule_locals_type
174                 >
175             context_type;
176 
177             // do down-stream transformation, provides attribute for
178             // rhs parser
179             typedef traits::transform_attribute<
180                 Attribute, subrule_attr_type, spirit::qi::domain>
181             transform;
182 
183             typename transform::type attr_ = transform::pre(attr);
184 
185             // If you are seeing a compilation error here, you are probably
186             // trying to use a subrule which has inherited attributes,
187             // without passing values for them.
188             context_type context(*this, attr_);
189 
190             if (def.binder(first, last, context, skipper))
191             {
192                 // do up-stream transformation, this integrates the results
193                 // back into the original attribute value, if appropriate
194                 transform::post(attr, attr_);
195                 return true;
196             }
197 
198             // inform attribute transformation of failed rhs
199             transform::fail(attr);
200             return false;
201         }
202 
203         template <typename Def
204           , typename Iterator, typename Context
205           , typename Skipper, typename Attribute, typename Params>
parse_subruleboost::spirit::repository::qi::subrule_group_parser206         bool parse_subrule(Def const& def
207           , Iterator& first, Iterator const& last
208           , Context& caller_context, Skipper const& skipper
209           , Attribute& attr, Params const& params) const
210         {
211             // compute context type for this subrule
212             typedef typename Def::locals_type subrule_locals_type;
213             typedef typename Def::attr_type subrule_attr_type;
214             typedef typename Def::attr_reference_type subrule_attr_reference_type;
215             typedef typename Def::parameter_types subrule_parameter_types;
216 
217             typedef
218                 subrule_context<
219                     this_type
220                   , fusion::cons<
221                         subrule_attr_reference_type, subrule_parameter_types>
222                   , subrule_locals_type
223                 >
224             context_type;
225 
226             // do down-stream transformation, provides attribute for
227             // rhs parser
228             typedef traits::transform_attribute<
229                 Attribute, subrule_attr_type, spirit::qi::domain>
230             transform;
231 
232             typename transform::type attr_ = transform::pre(attr);
233 
234             // If you are seeing a compilation error here, you are probably
235             // trying to use a subrule which has inherited attributes,
236             // passing values of incompatible types for them.
237             context_type context(*this, attr_, params, caller_context);
238 
239             if (def.binder(first, last, context, skipper))
240             {
241                 // do up-stream transformation, this integrates the results
242                 // back into the original attribute value, if appropriate
243                 transform::post(attr, attr_);
244                 return true;
245             }
246 
247             // inform attribute transformation of failed rhs
248             transform::fail(attr);
249             return false;
250         }
251 
252         template <typename Context>
whatboost::spirit::repository::qi::subrule_group_parser253         info what(Context& context) const
254         {
255             // Forward to first subrule.
256             return fusion::front(defs).second.binder.p.what(context);
257         }
258 
259         Defs defs;
260     };
261 
262     ///////////////////////////////////////////////////////////////////////////
263     // subrule_group:
264     // - a Proto terminal, so that a group behaves like any Spirit
265     //   expression.
266     ///////////////////////////////////////////////////////////////////////////
267     template <typename Defs>
268     struct subrule_group
269       : proto::extends<
270             typename proto::terminal<
271                 subrule_group_parser<Defs>
272             >::type
273           , subrule_group<Defs>
274         >
275     {
276         typedef subrule_group_parser<Defs> parser_type;
277         typedef typename proto::terminal<parser_type>::type terminal;
278 
279         static size_t const params_size =
280             // Forward to first subrule.
281             remove_reference<
282                 typename fusion::result_of::front<Defs>::type
283             >::type::second_type::params_size;
284 
subrule_groupboost::spirit::repository::qi::subrule_group285         explicit subrule_group(Defs const& defs)
286           : subrule_group::proto_extends(terminal::make(parser_type(defs)))
287         {
288         }
289 
parserboost::spirit::repository::qi::subrule_group290         parser_type const& parser() const { return proto::value(*this); }
291 
defsboost::spirit::repository::qi::subrule_group292         Defs const& defs() const { return parser().defs; }
293 
294         template <typename Defs2>
295         subrule_group<
296             typename fusion::result_of::as_map<
297                 typename fusion::result_of::join<
298                     Defs const, Defs2 const>::type>::type>
operator ,boost::spirit::repository::qi::subrule_group299         operator,(subrule_group<Defs2> const& other) const
300         {
301             typedef subrule_group<
302                 typename fusion::result_of::as_map<
303                     typename fusion::result_of::join<
304                         Defs const, Defs2 const>::type>::type> result_type;
305             return result_type(fusion::as_map(fusion::join(defs(), other.defs())));
306         }
307 
308         // non-const versions needed to suppress proto's comma op kicking in
309         template <typename Defs2>
310         friend subrule_group<
311             typename fusion::result_of::as_map<
312                 typename fusion::result_of::join<
313                     Defs const, Defs2 const>::type>::type>
314 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
operator ,(subrule_group && left,subrule_group<Defs2> && other)315         operator,(subrule_group&& left, subrule_group<Defs2>&& other)
316 #else
317         operator,(subrule_group& left, subrule_group<Defs2>& other)
318 #endif
319         {
320             return static_cast<subrule_group const&>(left)
321                 .operator,(static_cast<subrule_group<Defs2> const&>(other));
322         }
323 
324         // bring in the operator() overloads
get_parameterized_subjectboost::spirit::repository::qi::subrule_group325         parser_type const& get_parameterized_subject() const { return parser(); }
326         typedef parser_type parameterized_subject_type;
327         #include <boost/spirit/home/qi/nonterminal/detail/fcall.hpp>
328     };
329 
330     ///////////////////////////////////////////////////////////////////////////
331     // subrule_definition: holds one particular definition of a subrule
332     ///////////////////////////////////////////////////////////////////////////
333     template <
334         int ID_
335       , typename Locals
336       , typename Attr
337       , typename AttrRef
338       , typename Parameters
339       , size_t ParamsSize
340       , typename Subject
341       , bool Auto_
342     >
343     struct subrule_definition
344     {
345         typedef mpl::int_<ID_> id_type;
346         BOOST_STATIC_CONSTANT(int, ID = ID_);
347 
348         typedef Locals locals_type;
349         typedef Attr attr_type;
350         typedef AttrRef attr_reference_type;
351         typedef Parameters parameter_types;
352         static size_t const params_size = ParamsSize;
353 
354         typedef Subject subject_type;
355         typedef mpl::bool_<Auto_> auto_type;
356         BOOST_STATIC_CONSTANT(bool, Auto = Auto_);
357 
358         typedef spirit::qi::detail::parser_binder<
359             Subject, auto_type> binder_type;
360 
subrule_definitionboost::spirit::repository::qi::subrule_definition361         subrule_definition(Subject const& subject, std::string const& name)
362           : binder(subject), name(name)
363         {
364         }
365 
366         binder_type const binder;
367         std::string const name;
368     };
369 
370     ///////////////////////////////////////////////////////////////////////////
371     // subrule placeholder:
372     // - on subrule definition: helper for creation of subrule_group,
373     // - on subrule invocation: Proto terminal and parser.
374     ///////////////////////////////////////////////////////////////////////////
375     template <
376         int ID_
377       , typename T1 = unused_type
378       , typename T2 = unused_type
379     >
380     struct subrule
381       : proto::extends<
382             typename proto::terminal<
383                 spirit::qi::reference<subrule<ID_, T1, T2> const>
384             >::type
385           , subrule<ID_, T1, T2>
386         >
387       , spirit::qi::parser<subrule<ID_, T1, T2> >
388     {
389         typedef mpl::int_<ID_> id_type;
390         BOOST_STATIC_CONSTANT(int, ID = ID_);
391 
392         typedef subrule<ID_, T1, T2> this_type;
393         typedef spirit::qi::reference<this_type const> reference_;
394         typedef typename proto::terminal<reference_>::type terminal;
395         typedef proto::extends<terminal, this_type> base_type;
396 
397         typedef mpl::vector<T1, T2> template_params;
398 
399         // The subrule's locals_type: a sequence of types to be used as local variables
400         typedef typename
401             spirit::detail::extract_locals<template_params>::type
402         locals_type;
403 
404         // The subrule's encoding type
405         typedef typename
406             spirit::detail::extract_encoding<template_params>::type
407         encoding_type;
408 
409         // The subrule's signature
410         typedef typename
411             spirit::detail::extract_sig<template_params, encoding_type
412               , spirit::qi::domain>::type
413         sig_type;
414 
415         // This is the subrule's attribute type
416         typedef typename
417             spirit::detail::attr_from_sig<sig_type>::type
418         attr_type;
419         BOOST_STATIC_ASSERT_MSG(
420             !is_reference<attr_type>::value,
421             "Reference qualifier on Qi subrule attribute type is meaningless");
422         typedef attr_type& attr_reference_type;
423 
424         // parameter_types is a sequence of types passed as parameters to the subrule
425         typedef typename
426             spirit::detail::params_from_sig<sig_type>::type
427         parameter_types;
428 
429         static size_t const params_size =
430             fusion::result_of::size<parameter_types>::type::value;
431 
subruleboost::spirit::repository::qi::subrule432         explicit subrule(std::string const& name_ = "unnamed-subrule")
433           : base_type(terminal::make(reference_(*this)))
434           , name_(name_)
435         {
436         }
437 
438         // compute type of this subrule's definition for expr type Expr
439         template <typename Expr, bool Auto>
440         struct def_type_helper
441         {
442             // Report invalid expression error as early as possible.
443             // If you got an error_invalid_expression error message here,
444             // then the expression (Expr) is not a valid spirit qi expression.
445             BOOST_SPIRIT_ASSERT_MATCH(spirit::qi::domain, Expr);
446 
447             typedef typename result_of::compile<
448                 spirit::qi::domain, Expr>::type subject_type;
449 
450             typedef subrule_definition<
451                 ID_
452               , locals_type
453               , attr_type
454               , attr_reference_type
455               , parameter_types
456               , params_size
457               , subject_type
458               , Auto
459             > const type;
460         };
461 
462         // compute type of subrule group containing only this
463         // subrule's definition for expr type Expr
464         template <typename Expr, bool Auto>
465         struct group_type_helper
466         {
467             typedef typename def_type_helper<Expr, Auto>::type def_type;
468 
469             // create Defs map with only one entry: (ID -> def)
470             typedef typename
471 #ifndef BOOST_FUSION_HAS_VARIADIC_MAP
472                 fusion::result_of::make_map<id_type, def_type>::type
473 #else
474                 fusion::result_of::make_map<id_type>::template apply<def_type>::type
475 #endif
476             defs_type;
477 
478             typedef subrule_group<defs_type> type;
479         };
480 
481         template <typename Expr>
482         typename group_type_helper<Expr, false>::type
operator =boost::spirit::repository::qi::subrule483         operator=(Expr const& expr) const
484         {
485             typedef group_type_helper<Expr, false> helper;
486             typedef typename helper::def_type def_type;
487             typedef typename helper::type result_type;
488             return result_type(fusion::make_map<id_type>(
489                 def_type(compile<spirit::qi::domain>(expr), name_)));
490         }
491 
492 #define BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(lhs_ref, rhs_ref)        \
493         template <typename Expr>                                              \
494         friend typename group_type_helper<Expr, true>::type                   \
495         operator%=(subrule lhs_ref sr, Expr rhs_ref expr)                     \
496         {                                                                     \
497             typedef group_type_helper<Expr, true> helper;                     \
498             typedef typename helper::def_type def_type;                       \
499             typedef typename helper::type result_type;                        \
500             return result_type(fusion::make_map<id_type>(                     \
501                 def_type(compile<spirit::qi::domain>(expr), sr.name_)));      \
502         }                                                                     \
503         /**/
504 
505         // non-const versions needed to suppress proto's %= kicking in
BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATORboost::spirit::repository::qi::subrule506         BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, const&)
507 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
508         BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, &&)
509 #else
510         BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(const&, &)
511 #endif
512         BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, const&)
513 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
514         BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, &&)
515 #else
516         BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR(&, &)
517 #endif
518 
519 #undef BOOST_SPIRIT_SUBRULE_MODULUS_ASSIGN_OPERATOR
520 
521         std::string const& name() const
522         {
523             return name_;
524         }
525 
nameboost::spirit::repository::qi::subrule526         void name(std::string const& str)
527         {
528             name_ = str;
529         }
530 
531         template <typename Context, typename Iterator>
532         struct attribute
533         {
534             typedef attr_type type;
535         };
536 
537         template <typename Iterator, typename Group
538           , typename Attributes, typename Locals
539           , typename Skipper, typename Attribute>
parseboost::spirit::repository::qi::subrule540         bool parse(Iterator& first, Iterator const& last
541           , subrule_context<Group, Attributes, Locals>& context
542           , Skipper const& skipper, Attribute& attr) const
543         {
544             return context.group.template parse_subrule_id<ID_>(
545                 first, last, context, skipper, attr);
546         }
547 
548         template <typename Iterator, typename Context
549           , typename Skipper, typename Attribute>
parseboost::spirit::repository::qi::subrule550         bool parse(Iterator& /*first*/, Iterator const& /*last*/
551           , Context& /*context*/
552           , Skipper const& /*skipper*/, Attribute& /*attr*/) const
553         {
554             // If you are seeing a compilation error here, you are trying
555             // to use a subrule as a parser outside of a subrule group.
556             BOOST_SPIRIT_ASSERT_FAIL(Iterator
557               , subrule_used_outside_subrule_group, (id_type));
558 
559             return false;
560         }
561 
562         template <typename Iterator, typename Group
563           , typename Attributes, typename Locals
564           , typename Skipper, typename Attribute
565           , typename Params>
parseboost::spirit::repository::qi::subrule566         bool parse(Iterator& first, Iterator const& last
567           , subrule_context<Group, Attributes, Locals>& context
568           , Skipper const& skipper, Attribute& attr
569           , Params const& params) const
570         {
571             return context.group.template parse_subrule_id<ID_>(
572                 first, last, context, skipper, attr, params);
573         }
574 
575         template <typename Iterator, typename Context
576           , typename Skipper, typename Attribute
577           , typename Params>
parseboost::spirit::repository::qi::subrule578         bool parse(Iterator& /*first*/, Iterator const& /*last*/
579           , Context& /*context*/
580           , Skipper const& /*skipper*/, Attribute& /*attr*/
581           , Params const& /*params*/) const
582         {
583             // If you are seeing a compilation error here, you are trying
584             // to use a subrule as a parser outside of a subrule group.
585             BOOST_SPIRIT_ASSERT_FAIL(Iterator
586               , subrule_used_outside_subrule_group, (id_type));
587 
588             return false;
589         }
590 
591         template <typename Context>
whatboost::spirit::repository::qi::subrule592         info what(Context& /*context*/) const
593         {
594             return info(name_);
595         }
596 
597         // bring in the operator() overloads
get_parameterized_subjectboost::spirit::repository::qi::subrule598         this_type const& get_parameterized_subject() const { return *this; }
599         typedef this_type parameterized_subject_type;
600         #include <boost/spirit/home/qi/nonterminal/detail/fcall.hpp>
601 
602         std::string name_;
603     };
604 }}}}
605 
606 #if defined(BOOST_MSVC)
607 # pragma warning(pop)
608 #endif
609 
610 #endif
611