]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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_ERROR_REPORTING_MAY_19_2014_00405PM) | |
8 | #define BOOST_SPIRIT_X3_ERROR_REPORTING_MAY_19_2014_00405PM | |
9 | ||
7c673cae | 10 | #include <boost/spirit/home/x3/support/ast/position_tagged.hpp> |
20effc67 | 11 | #include <boost/spirit/home/x3/support/utility/utf8.hpp> |
7c673cae FG |
12 | #include <ostream> |
13 | ||
14 | // Clang-style error handling utilities | |
15 | ||
16 | namespace boost { namespace spirit { namespace x3 | |
17 | { | |
18 | // tag used to get our error handler from the context | |
19 | struct error_handler_tag; | |
20 | ||
21 | template <typename Iterator> | |
22 | class error_handler | |
23 | { | |
24 | public: | |
25 | ||
26 | typedef Iterator iterator_type; | |
27 | ||
28 | error_handler( | |
29 | Iterator first, Iterator last, std::ostream& err_out | |
30 | , std::string file = "", int tabs = 4) | |
31 | : err_out(err_out) | |
32 | , file(file) | |
33 | , tabs(tabs) | |
34 | , pos_cache(first, last) {} | |
35 | ||
36 | typedef void result_type; | |
37 | ||
38 | void operator()(Iterator err_pos, std::string const& error_message) const; | |
39 | void operator()(Iterator err_first, Iterator err_last, std::string const& error_message) const; | |
40 | void operator()(position_tagged pos, std::string const& message) const | |
41 | { | |
42 | auto where = pos_cache.position_of(pos); | |
43 | (*this)(where.begin(), where.end(), message); | |
44 | } | |
45 | ||
46 | template <typename AST> | |
47 | void tag(AST& ast, Iterator first, Iterator last) | |
48 | { | |
49 | return pos_cache.annotate(ast, first, last); | |
50 | } | |
51 | ||
52 | boost::iterator_range<Iterator> position_of(position_tagged pos) const | |
53 | { | |
54 | return pos_cache.position_of(pos); | |
55 | } | |
56 | ||
92f5a8d4 TL |
57 | position_cache<std::vector<Iterator>> const& get_position_cache() const |
58 | { | |
59 | return pos_cache; | |
60 | } | |
61 | ||
7c673cae FG |
62 | private: |
63 | ||
64 | void print_file_line(std::size_t line) const; | |
65 | void print_line(Iterator line_start, Iterator last) const; | |
66 | void print_indicator(Iterator& line_start, Iterator last, char ind) const; | |
67 | void skip_whitespace(Iterator& err_pos, Iterator last) const; | |
68 | void skip_non_whitespace(Iterator& err_pos, Iterator last) const; | |
69 | Iterator get_line_start(Iterator first, Iterator pos) const; | |
70 | std::size_t position(Iterator i) const; | |
71 | ||
72 | std::ostream& err_out; | |
73 | std::string file; | |
74 | int tabs; | |
75 | position_cache<std::vector<Iterator>> pos_cache; | |
76 | }; | |
77 | ||
78 | template <typename Iterator> | |
79 | void error_handler<Iterator>::print_file_line(std::size_t line) const | |
80 | { | |
81 | if (file != "") | |
82 | { | |
7c673cae | 83 | err_out << "In file " << file << ", "; |
7c673cae FG |
84 | } |
85 | else | |
86 | { | |
87 | err_out << "In "; | |
88 | } | |
89 | ||
90 | err_out << "line " << line << ':' << std::endl; | |
91 | } | |
92 | ||
93 | template <typename Iterator> | |
94 | void error_handler<Iterator>::print_line(Iterator start, Iterator last) const | |
95 | { | |
96 | auto end = start; | |
97 | while (end != last) | |
98 | { | |
99 | auto c = *end; | |
100 | if (c == '\r' || c == '\n') | |
101 | break; | |
102 | else | |
103 | ++end; | |
104 | } | |
105 | typedef typename std::iterator_traits<Iterator>::value_type char_type; | |
106 | std::basic_string<char_type> line{start, end}; | |
20effc67 | 107 | err_out << x3::to_utf8(line) << std::endl; |
7c673cae FG |
108 | } |
109 | ||
110 | template <typename Iterator> | |
111 | void error_handler<Iterator>::print_indicator(Iterator& start, Iterator last, char ind) const | |
112 | { | |
113 | for (; start != last; ++start) | |
114 | { | |
115 | auto c = *start; | |
116 | if (c == '\r' || c == '\n') | |
117 | break; | |
118 | else if (c == '\t') | |
119 | for (int i = 0; i < tabs; ++i) | |
120 | err_out << ind; | |
121 | else | |
122 | err_out << ind; | |
123 | } | |
124 | } | |
125 | ||
126 | template <typename Iterator> | |
127 | void error_handler<Iterator>::skip_whitespace(Iterator& err_pos, Iterator last) const | |
128 | { | |
129 | // make sure err_pos does not point to white space | |
130 | while (err_pos != last) | |
131 | { | |
132 | char c = *err_pos; | |
133 | if (std::isspace(c)) | |
134 | ++err_pos; | |
135 | else | |
136 | break; | |
137 | } | |
138 | } | |
139 | ||
140 | template <typename Iterator> | |
141 | void error_handler<Iterator>::skip_non_whitespace(Iterator& err_pos, Iterator last) const | |
142 | { | |
143 | // make sure err_pos does not point to white space | |
144 | while (err_pos != last) | |
145 | { | |
146 | char c = *err_pos; | |
147 | if (std::isspace(c)) | |
148 | break; | |
149 | else | |
150 | ++err_pos; | |
151 | } | |
152 | } | |
153 | ||
154 | template <class Iterator> | |
155 | inline Iterator error_handler<Iterator>::get_line_start(Iterator first, Iterator pos) const | |
156 | { | |
157 | Iterator latest = first; | |
158 | for (Iterator i = first; i != pos; ++i) | |
159 | if (*i == '\r' || *i == '\n') | |
160 | latest = i; | |
161 | return latest; | |
162 | } | |
163 | ||
164 | template <typename Iterator> | |
165 | std::size_t error_handler<Iterator>::position(Iterator i) const | |
166 | { | |
167 | std::size_t line { 1 }; | |
168 | typename std::iterator_traits<Iterator>::value_type prev { 0 }; | |
169 | ||
170 | for (Iterator pos = pos_cache.first(); pos != i; ++pos) { | |
171 | auto c = *pos; | |
172 | switch (c) { | |
173 | case '\n': | |
174 | if (prev != '\r') ++line; | |
175 | break; | |
176 | case '\r': | |
11fdf7f2 | 177 | ++line; |
7c673cae FG |
178 | break; |
179 | default: | |
180 | break; | |
181 | } | |
182 | prev = c; | |
183 | } | |
184 | ||
185 | return line; | |
186 | } | |
187 | ||
188 | template <typename Iterator> | |
189 | void error_handler<Iterator>::operator()( | |
190 | Iterator err_pos, std::string const& error_message) const | |
191 | { | |
192 | Iterator first = pos_cache.first(); | |
193 | Iterator last = pos_cache.last(); | |
194 | ||
195 | // make sure err_pos does not point to white space | |
196 | skip_whitespace(err_pos, last); | |
197 | ||
198 | print_file_line(position(err_pos)); | |
199 | err_out << error_message << std::endl; | |
200 | ||
201 | Iterator start = get_line_start(first, err_pos); | |
202 | if (start != first) | |
203 | ++start; | |
204 | print_line(start, last); | |
205 | print_indicator(start, err_pos, '_'); | |
206 | err_out << "^_" << std::endl; | |
207 | } | |
208 | ||
209 | template <typename Iterator> | |
210 | void error_handler<Iterator>::operator()( | |
211 | Iterator err_first, Iterator err_last, std::string const& error_message) const | |
212 | { | |
213 | Iterator first = pos_cache.first(); | |
214 | Iterator last = pos_cache.last(); | |
215 | ||
216 | // make sure err_pos does not point to white space | |
217 | skip_whitespace(err_first, last); | |
218 | ||
219 | print_file_line(position(err_first)); | |
220 | err_out << error_message << std::endl; | |
221 | ||
222 | Iterator start = get_line_start(first, err_first); | |
223 | if (start != first) | |
224 | ++start; | |
225 | print_line(start, last); | |
226 | print_indicator(start, err_first, ' '); | |
227 | print_indicator(start, err_last, '~'); | |
228 | err_out << " <<-- Here" << std::endl; | |
229 | } | |
230 | ||
231 | }}} | |
232 | ||
233 | #endif |