1 /*==============================================================================
2     Copyright (c) 2001-2011 Joel de Guzman
3     Copyright (c) 2010      Bryce Lelbach
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 
9 #if !defined(BOOST_SPIRIT_SUPPORT_LINE_POS_ITERATOR)
10 #define BOOST_SPIRIT_SUPPORT_LINE_POS_ITERATOR
11 
12 #include <boost/iterator/iterator_adaptor.hpp>
13 #include <boost/range/iterator_range.hpp>
14 
15 namespace boost { namespace spirit
16 {
17     //[line_pos_iterator_class
18     /*`The `line_pos_iterator` is a lightweight line position iterator.
19        This iterator adapter only stores the current line number, nothing else.
20        Unlike __classic__'s `position_iterator`, it does not store the
21        column number and does not need an end iterator. The current column can
22        be computed, if needed. */
23     //`[heading Class Reference]
24     template <class Iterator>
25     class line_pos_iterator : public boost::iterator_adaptor<
26         line_pos_iterator<Iterator>  // Derived
27       , Iterator                     // Base
28       , boost::use_default           // Value
29       , boost::forward_traversal_tag // CategoryOrTraversal
30     > {
31     public:
32         line_pos_iterator();
33 
34         explicit line_pos_iterator(Iterator);
35 
36         std::size_t position() const;
37 
38     private:
39         friend class boost::iterator_core_access;
40 
41         void increment();
42 
43         std::size_t line; // The line position.
44         typename std::iterator_traits<Iterator>::value_type prev;
45     };
46     //]
47 
48     template <class Iterator>
line_pos_iterator()49     line_pos_iterator<Iterator>::line_pos_iterator() :
50         line_pos_iterator::iterator_adaptor_(), line(1), prev(0) { }
51 
52     template <class Iterator>
line_pos_iterator(Iterator base)53     line_pos_iterator<Iterator>::line_pos_iterator(Iterator base) :
54         line_pos_iterator::iterator_adaptor_(base), line(1), prev(0) { }
55 
56     template <class Iterator>
position() const57     std::size_t line_pos_iterator<Iterator>::position() const
58     {
59         return line;
60     }
61 
62     template<class Iterator>
increment()63     void line_pos_iterator<Iterator>::increment()
64     {
65         typename std::iterator_traits<Iterator>::reference
66           ref = *(this->base());
67 
68         switch (ref) {
69           case '\r':
70             if (prev != '\n')
71               ++line;
72             break;
73           case '\n':
74             if (prev != '\r')
75               ++line;
76             break;
77           default:
78             break;
79         }
80 
81         prev = ref;
82         ++this->base_reference();
83     }
84 
85     //[line_pos_iterator_utilities
86     //`[heading get_line]
87     template <class Iterator>
88     inline std::size_t get_line(Iterator);
89     /*`Get the line position. Returns -1 if Iterator is not a
90        `line_pos_iterator`. */
91 
92     //`[heading get_line_start]
93     template <class Iterator>
94     inline Iterator get_line_start(Iterator lower_bound, Iterator current);
95     /*`Get an iterator to the beginning of the line. Applicable to any
96        iterator. */
97 
98     //`[heading get_current_line]
99     template <class Iterator>
100     inline iterator_range<Iterator>
101     get_current_line(Iterator lower_bound, Iterator current,
102                      Iterator upper_bound);
103     /*`Get an `iterator_range` containing the current line. Applicable to any
104        iterator. */
105 
106     //`[heading get_column]
107     template <class Iterator>
108     inline std::size_t get_column(Iterator lower_bound, Iterator current,
109                                   std::size_t tabs = 4);
110     /*`Get the current column. Applicable to any iterator. */
111     //]
112 
113     template <class Iterator>
get_line(Iterator)114     inline std::size_t get_line(Iterator)
115     {
116         return -1;
117     }
118 
119     template <class Iterator>
get_line(line_pos_iterator<Iterator> i)120     inline std::size_t get_line(line_pos_iterator<Iterator> i)
121     {
122         return i.position();
123     }
124 
125     template <class Iterator>
get_line_start(Iterator lower_bound,Iterator current)126     inline Iterator get_line_start(Iterator lower_bound, Iterator current)
127     {
128         Iterator latest = lower_bound;
129 
130         for (Iterator i = lower_bound; i != current; ++i) {
131           switch (*i) {
132             case '\r':
133             case '\n':
134               latest = i;
135           }
136         }
137 
138         return latest;
139     }
140 
141     template <class Iterator>
142     inline iterator_range<Iterator>
get_current_line(Iterator lower_bound,Iterator current,Iterator upper_bound)143     get_current_line(Iterator lower_bound,
144                      Iterator current,
145                      Iterator upper_bound)
146     {
147         Iterator first = get_line_start(lower_bound, current);
148         Iterator last = get_line_start(current, upper_bound);
149 
150         if (last == current)
151           last = upper_bound;
152 
153         return iterator_range<Iterator>(first, last);
154     }
155 
156     template <class Iterator>
get_column(Iterator lower_bound,Iterator current,std::size_t tabs)157     inline std::size_t get_column(Iterator lower_bound,
158                                   Iterator current,
159                                   std::size_t tabs)
160     {
161         std::size_t column = 1;
162         Iterator first = get_line_start(lower_bound, current);
163 
164         for (Iterator i = first; i != current; ++i) {
165           switch (*i) {
166             case '\t':
167               column += tabs - (column - 1) % tabs;
168               break;
169             default:
170               ++column;
171           }
172         }
173 
174         return column;
175     }
176 
177 }}
178 
179 #endif // BOOST_SPIRIT_SUPPORT_LINE_POS_ITERATOR
180 
181