1 /*=============================================================================
2     Copyright (c) 2014 Joel de Guzman
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_X3_POSITION_TAGGED_MAY_01_2014_0321PM)
8 #define BOOST_SPIRIT_X3_POSITION_TAGGED_MAY_01_2014_0321PM
9 
10 #include <boost/range.hpp>
11 #include <boost/type_traits/is_base_of.hpp>
12 #include <boost/core/enable_if.hpp>
13 
14 namespace boost { namespace spirit { namespace x3
15 {
16     struct position_tagged
17     {
18         // Use this to annotate an AST with the iterator position.
19         // These ids are used as a key to the position_cache (below)
20         // and marks the start and end of an AST node.
21         int id_first = -1;
22         int id_last = -1;
23     };
24 
25     template <typename Container>
26     class position_cache
27     {
28     public:
29 
30         typedef typename Container::value_type iterator_type;
31 
position_cache(iterator_type first,iterator_type last)32         position_cache(
33             iterator_type first
34           , iterator_type last)
35           : first_(first), last_(last) {}
36 
37         // This will catch all nodes inheriting from position_tagged
38         boost::iterator_range<iterator_type>
position_of(position_tagged const & ast) const39         position_of(position_tagged const& ast) const
40         {
41             return
42                 boost::iterator_range<iterator_type>(
43                     positions.at(ast.id_first) // throws if out of range
44                   , positions.at(ast.id_last)  // throws if out of range
45                 );
46         }
47 
48         // This will catch all nodes except those inheriting from position_tagged
49         template <typename AST>
50         typename boost::enable_if_c<
51             (!is_base_of<position_tagged, AST>::value)
52           , boost::iterator_range<iterator_type>
53         >::type
position_of(AST const &) const54         position_of(AST const& /* ast */) const
55         {
56             // returns an empty position
57             return boost::iterator_range<iterator_type>();
58         }
59 
60         // This will catch all nodes except those inheriting from position_tagged
61         template <typename AST>
annotate(AST &,iterator_type,iterator_type,mpl::false_)62         void annotate(AST& /* ast */, iterator_type /* first */, iterator_type /* last */, mpl::false_)
63         {
64             // (no-op) no need for tags
65         }
66 
67         // This will catch all nodes inheriting from position_tagged
annotate(position_tagged & ast,iterator_type first,iterator_type last,mpl::true_)68         void annotate(position_tagged& ast, iterator_type first, iterator_type last, mpl::true_)
69         {
70             ast.id_first = int(positions.size());
71             positions.push_back(first);
72             ast.id_last = int(positions.size());
73             positions.push_back(last);
74         }
75 
76         template <typename AST>
annotate(AST & ast,iterator_type first,iterator_type last)77         void annotate(AST& ast, iterator_type first, iterator_type last)
78         {
79             annotate(ast, first, last, is_base_of<position_tagged, AST>());
80         }
81 
82         Container const&
get_positions() const83         get_positions() const
84         {
85             return positions;
86         }
87 
first() const88         iterator_type first() const { return first_; }
last() const89         iterator_type last() const { return last_; }
90 
91     private:
92 
93         Container positions;
94         iterator_type first_;
95         iterator_type last_;
96     };
97 
98 }}}
99 
100 #endif
101