1 /*=============================================================================
2 Copyright (c) 2014 Joel de Guzman
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
10 #ifndef BOOST_SPIRIT_X3_NO_FILESYSTEM
11 #include <boost/filesystem/path.hpp>
14 #include <boost/locale/encoding_utf.hpp>
15 #include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
18 // Clang-style error handling utilities
20 namespace boost { namespace spirit { namespace x3
22 // tag used to get our error handler from the context
23 struct error_handler_tag;
25 template <typename Iterator>
30 typedef Iterator iterator_type;
33 Iterator first, Iterator last, std::ostream& err_out
34 , std::string file = "", int tabs = 4)
38 , pos_cache(first, last) {}
40 typedef void result_type;
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
46 auto where = pos_cache.position_of(pos);
47 (*this)(where.begin(), where.end(), message);
50 template <typename AST>
51 void tag(AST& ast, Iterator first, Iterator last)
53 return pos_cache.annotate(ast, first, last);
56 boost::iterator_range<Iterator> position_of(position_tagged pos) const
58 return pos_cache.position_of(pos);
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;
71 std::ostream& err_out;
74 position_cache<std::vector<Iterator>> pos_cache;
77 template <typename Iterator>
78 void error_handler<Iterator>::print_file_line(std::size_t line) const
82 #ifdef BOOST_SPIRIT_X3_NO_FILESYSTEM
83 err_out << "In file " << file << ", ";
85 namespace fs = boost::filesystem;
86 err_out << "In file " << fs::path(file).generic_string() << ", ";
94 err_out << "line " << line << ':' << std::endl;
97 template <typename Iterator>
98 void error_handler<Iterator>::print_line(Iterator start, Iterator last) const
104 if (c == '\r' || c == '\n')
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;
114 template <typename Iterator>
115 void error_handler<Iterator>::print_indicator(Iterator& start, Iterator last, char ind) const
117 for (; start != last; ++start)
120 if (c == '\r' || c == '\n')
123 for (int i = 0; i < tabs; ++i)
130 template <typename Iterator>
131 void error_handler<Iterator>::skip_whitespace(Iterator& err_pos, Iterator last) const
133 // make sure err_pos does not point to white space
134 while (err_pos != last)
144 template <typename Iterator>
145 void error_handler<Iterator>::skip_non_whitespace(Iterator& err_pos, Iterator last) const
147 // make sure err_pos does not point to white space
148 while (err_pos != last)
158 template <class Iterator>
159 inline Iterator error_handler<Iterator>::get_line_start(Iterator first, Iterator pos) const
161 Iterator latest = first;
162 for (Iterator i = first; i != pos; ++i)
163 if (*i == '\r' || *i == '\n')
168 template <typename Iterator>
169 std::size_t error_handler<Iterator>::position(Iterator i) const
171 std::size_t line { 1 };
172 typename std::iterator_traits<Iterator>::value_type prev { 0 };
174 for (Iterator pos = pos_cache.first(); pos != i; ++pos) {
178 if (prev != '\r') ++line;
181 if (prev != '\n') ++line;
192 template <typename Iterator>
193 void error_handler<Iterator>::operator()(
194 Iterator err_pos, std::string const& error_message) const
196 Iterator first = pos_cache.first();
197 Iterator last = pos_cache.last();
199 // make sure err_pos does not point to white space
200 skip_whitespace(err_pos, last);
202 print_file_line(position(err_pos));
203 err_out << error_message << std::endl;
205 Iterator start = get_line_start(first, err_pos);
208 print_line(start, last);
209 print_indicator(start, err_pos, '_');
210 err_out << "^_" << std::endl;
213 template <typename Iterator>
214 void error_handler<Iterator>::operator()(
215 Iterator err_first, Iterator err_last, std::string const& error_message) const
217 Iterator first = pos_cache.first();
218 Iterator last = pos_cache.last();
220 // make sure err_pos does not point to white space
221 skip_whitespace(err_first, last);
223 print_file_line(position(err_first));
224 err_out << error_message << std::endl;
226 Iterator start = get_line_start(first, err_first);
229 print_line(start, last);
230 print_indicator(start, err_first, ' ');
231 print_indicator(start, err_last, '~');
232 err_out << " <<-- Here" << std::endl;