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 
13 namespace boost { namespace spirit { namespace x3
14 {
15     struct position_tagged
16     {
17         // Use this to annotate an AST with the iterator position.
18         // These ids are used as a key to the position_cache (below)
19         // and marks the start and end of an AST node.
20         int id_first = -1;
21         int id_last = -1;
22     };
23 
24     template <typename Container>
25     class position_cache
26     {
27     public:
28 
29         typedef typename Container::value_type iterator_type;
30 
position_cache(iterator_type first,iterator_type last)31         position_cache(
32             iterator_type first
33           , iterator_type last)
34           : first_(first), last_(last) {}
35 
36         // This will catch all nodes inheriting from position_tagged
37         boost::iterator_range<iterator_type>
position_of(position_tagged const & ast) const38         position_of(position_tagged const& ast) const
39         {
40             return
41                 boost::iterator_range<iterator_type>(
42                     positions.at(ast.id_first) // throws if out of range
43                   , positions.at(ast.id_last)  // throws if out of range
44                 );
45         }
46 
47         // This will catch all nodes except those inheriting from position_tagged
48         template <typename AST>
49         boost::iterator_range<iterator_type>
position_of(AST const & ast) const50         position_of(AST const& ast) const
51         {
52             // returns an empty position
53             return boost::iterator_range<iterator_type>();
54         }
55 
56         // This will catch all nodes except those inheriting from position_tagged
57         template <typename AST>
annotate(AST & ast,iterator_type first,iterator_type last,mpl::false_)58         void annotate(AST& ast, iterator_type first, iterator_type last, mpl::false_)
59         {
60             // (no-op) no need for tags
61         }
62 
63         // This will catch all nodes inheriting from position_tagged
annotate(position_tagged & ast,iterator_type first,iterator_type last,mpl::true_)64         void annotate(position_tagged& ast, iterator_type first, iterator_type last, mpl::true_)
65         {
66             ast.id_first = int(positions.size());
67             positions.push_back(first);
68             ast.id_last = int(positions.size());
69             positions.push_back(last);
70         }
71 
72         template <typename AST>
annotate(AST & ast,iterator_type first,iterator_type last)73         void annotate(AST& ast, iterator_type first, iterator_type last)
74         {
75             annotate(ast, first, last, is_base_of<position_tagged, AST>());
76         }
77 
78         Container const&
get_positions() const79         get_positions() const
80         {
81             return positions;
82         }
83 
first() const84         iterator_type first() const { return first_; }
last() const85         iterator_type last() const { return last_; }
86 
87     private:
88 
89         Container positions;
90         iterator_type first_;
91         iterator_type last_;
92     };
93 
94 }}}
95 
96 #endif
97