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