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 #include <boost/spirit/home/x3/support/ast/position_tagged.hpp> 11 #include <boost/spirit/home/x3/support/utility/utf8.hpp> 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 error_handler(Iterator first,Iterator last,std::ostream & err_out,std::string file="",int tabs=4)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; operator ()(position_tagged pos,std::string const & message) const40 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> tag(AST & ast,Iterator first,Iterator last)47 void tag(AST& ast, Iterator first, Iterator last) 48 { 49 return pos_cache.annotate(ast, first, last); 50 } 51 position_of(position_tagged pos) const52 boost::iterator_range<Iterator> position_of(position_tagged pos) const 53 { 54 return pos_cache.position_of(pos); 55 } 56 get_position_cache() const57 position_cache<std::vector<Iterator>> const& get_position_cache() const 58 { 59 return pos_cache; 60 } 61 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> print_file_line(std::size_t line) const79 void error_handler<Iterator>::print_file_line(std::size_t line) const 80 { 81 if (file != "") 82 { 83 err_out << "In file " << file << ", "; 84 } 85 else 86 { 87 err_out << "In "; 88 } 89 90 err_out << "line " << line << ':' << std::endl; 91 } 92 93 template <typename Iterator> print_line(Iterator start,Iterator last) const94 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}; 107 err_out << x3::to_utf8(line) << std::endl; 108 } 109 110 template <typename Iterator> print_indicator(Iterator & start,Iterator last,char ind) const111 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> skip_whitespace(Iterator & err_pos,Iterator last) const127 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> skip_non_whitespace(Iterator & err_pos,Iterator last) const141 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> get_line_start(Iterator first,Iterator pos) const155 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> position(Iterator i) const165 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': 177 ++line; 178 break; 179 default: 180 break; 181 } 182 prev = c; 183 } 184 185 return line; 186 } 187 188 template <typename Iterator> operator ()(Iterator err_pos,std::string const & error_message) const189 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> operator ()(Iterator err_first,Iterator err_last,std::string const & error_message) const210 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 234