318
|
1 // Copyright Toru Niina 2019.
|
|
2 // Distributed under the MIT License.
|
|
3 #ifndef TOML11_SOURCE_LOCATION_HPP
|
|
4 #define TOML11_SOURCE_LOCATION_HPP
|
|
5 #include <cstdint>
|
|
6 #include <sstream>
|
|
7
|
|
8 #include "region.hpp"
|
|
9
|
|
10 namespace toml
|
|
11 {
|
|
12
|
|
13 // A struct to contain location in a toml file.
|
|
14 // The interface imitates std::experimental::source_location,
|
|
15 // but not completely the same.
|
|
16 //
|
|
17 // It would be constructed by toml::value. It can be used to generate
|
|
18 // user-defined error messages.
|
|
19 //
|
|
20 // - std::uint_least32_t line() const noexcept
|
|
21 // - returns the line number where the region is on.
|
|
22 // - std::uint_least32_t column() const noexcept
|
|
23 // - returns the column number where the region starts.
|
|
24 // - std::uint_least32_t region() const noexcept
|
|
25 // - returns the size of the region.
|
|
26 //
|
|
27 // +-- line() +-- region of interest (region() == 9)
|
|
28 // v .---+---.
|
|
29 // 12 | value = "foo bar"
|
|
30 // ^
|
|
31 // +-- column()
|
|
32 //
|
|
33 // - std::string const& file_name() const noexcept;
|
|
34 // - name of the file.
|
|
35 // - std::string const& line_str() const noexcept;
|
|
36 // - the whole line that contains the region of interest.
|
|
37 //
|
|
38 struct source_location
|
|
39 {
|
|
40 public:
|
|
41
|
|
42 source_location()
|
|
43 : line_num_(1), column_num_(1), region_size_(1),
|
|
44 file_name_("unknown file"), line_str_("")
|
|
45 {}
|
|
46
|
|
47 explicit source_location(const detail::region_base* reg)
|
|
48 : line_num_(1), column_num_(1), region_size_(1),
|
|
49 file_name_("unknown file"), line_str_("")
|
|
50 {
|
|
51 if(reg)
|
|
52 {
|
|
53 if(reg->line_num() != detail::region_base().line_num())
|
|
54 {
|
|
55 line_num_ = static_cast<std::uint_least32_t>(
|
|
56 std::stoul(reg->line_num()));
|
|
57 }
|
|
58 column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1);
|
|
59 region_size_ = static_cast<std::uint_least32_t>(reg->size());
|
|
60 file_name_ = reg->name();
|
|
61 line_str_ = reg->line();
|
|
62 }
|
|
63 }
|
|
64
|
|
65 explicit source_location(const detail::region& reg)
|
|
66 : line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
|
|
67 column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
|
|
68 region_size_(static_cast<std::uint_least32_t>(reg.size())),
|
|
69 file_name_(reg.name()),
|
|
70 line_str_ (reg.line())
|
|
71 {}
|
|
72 explicit source_location(const detail::location& loc)
|
|
73 : line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
|
|
74 column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
|
|
75 region_size_(static_cast<std::uint_least32_t>(loc.size())),
|
|
76 file_name_(loc.name()),
|
|
77 line_str_ (loc.line())
|
|
78 {}
|
|
79
|
|
80 ~source_location() = default;
|
|
81 source_location(source_location const&) = default;
|
|
82 source_location(source_location &&) = default;
|
|
83 source_location& operator=(source_location const&) = default;
|
|
84 source_location& operator=(source_location &&) = default;
|
|
85
|
|
86 std::uint_least32_t line() const noexcept {return line_num_;}
|
|
87 std::uint_least32_t column() const noexcept {return column_num_;}
|
|
88 std::uint_least32_t region() const noexcept {return region_size_;}
|
|
89
|
|
90 std::string const& file_name() const noexcept {return file_name_;}
|
|
91 std::string const& line_str() const noexcept {return line_str_;}
|
|
92
|
|
93 private:
|
|
94
|
|
95 std::uint_least32_t line_num_;
|
|
96 std::uint_least32_t column_num_;
|
|
97 std::uint_least32_t region_size_;
|
|
98 std::string file_name_;
|
|
99 std::string line_str_;
|
|
100 };
|
|
101
|
|
102 namespace detail
|
|
103 {
|
|
104
|
|
105 // internal error message generation.
|
|
106 inline std::string format_underline(const std::string& message,
|
|
107 const std::vector<std::pair<source_location, std::string>>& loc_com,
|
|
108 const std::vector<std::string>& helps = {},
|
|
109 const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
|
|
110 {
|
|
111 std::size_t line_num_width = 0;
|
|
112 for(const auto& lc : loc_com)
|
|
113 {
|
|
114 std::uint_least32_t line = lc.first.line();
|
|
115 std::size_t digit = 0;
|
|
116 while(line != 0)
|
|
117 {
|
|
118 line /= 10;
|
|
119 digit += 1;
|
|
120 }
|
|
121 line_num_width = (std::max)(line_num_width, digit);
|
|
122 }
|
|
123 // 1 is the minimum width
|
|
124 line_num_width = std::max<std::size_t>(line_num_width, 1);
|
|
125
|
|
126 std::ostringstream retval;
|
|
127
|
|
128 if(color::should_color() || colorize)
|
|
129 {
|
|
130 retval << color::colorize; // turn on ANSI color
|
|
131 }
|
|
132
|
|
133 // XXX
|
|
134 // Here, before `colorize` support, it does not output `[error]` prefix
|
|
135 // automatically. So some user may output it manually and this change may
|
|
136 // duplicate the prefix. To avoid it, check the first 7 characters and
|
|
137 // if it is "[error]", it removes that part from the message shown.
|
|
138 if(message.size() > 7 && message.substr(0, 7) == "[error]")
|
|
139 {
|
|
140 retval
|
|
141 #ifndef TOML11_NO_ERROR_PREFIX
|
|
142 << color::bold << color::red << "[error]" << color::reset
|
|
143 #endif
|
|
144 << color::bold << message.substr(7) << color::reset << '\n';
|
|
145 }
|
|
146 else
|
|
147 {
|
|
148 retval
|
|
149 #ifndef TOML11_NO_ERROR_PREFIX
|
|
150 << color::bold << color::red << "[error] " << color::reset
|
|
151 #endif
|
|
152 << color::bold << message << color::reset << '\n';
|
|
153 }
|
|
154
|
|
155 const auto format_one_location = [line_num_width]
|
|
156 (std::ostringstream& oss,
|
|
157 const source_location& loc, const std::string& comment) -> void
|
|
158 {
|
|
159 oss << ' ' << color::bold << color::blue
|
|
160 << std::setw(static_cast<int>(line_num_width))
|
|
161 << std::right << loc.line() << " | " << color::reset
|
|
162 << loc.line_str() << '\n';
|
|
163
|
|
164 oss << make_string(line_num_width + 1, ' ')
|
|
165 << color::bold << color::blue << " | " << color::reset
|
|
166 << make_string(loc.column()-1 /*1-origin*/, ' ');
|
|
167
|
|
168 if(loc.region() == 1)
|
|
169 {
|
|
170 // invalid
|
|
171 // ^------
|
|
172 oss << color::bold << color::red << "^---" << color::reset;
|
|
173 }
|
|
174 else
|
|
175 {
|
|
176 // invalid
|
|
177 // ~~~~~~~
|
|
178 const auto underline_len = (std::min)(
|
|
179 static_cast<std::size_t>(loc.region()), loc.line_str().size());
|
|
180 oss << color::bold << color::red
|
|
181 << make_string(underline_len, '~') << color::reset;
|
|
182 }
|
|
183 oss << ' ';
|
|
184 oss << comment;
|
|
185 return;
|
|
186 };
|
|
187
|
|
188 assert(!loc_com.empty());
|
|
189
|
|
190 // --> example.toml
|
|
191 // |
|
|
192 retval << color::bold << color::blue << " --> " << color::reset
|
|
193 << loc_com.front().first.file_name() << '\n';
|
|
194 retval << make_string(line_num_width + 1, ' ')
|
|
195 << color::bold << color::blue << " |\n" << color::reset;
|
|
196 // 1 | key value
|
|
197 // | ^--- missing =
|
|
198 format_one_location(retval, loc_com.front().first, loc_com.front().second);
|
|
199
|
|
200 // process the rest of the locations
|
|
201 for(std::size_t i=1; i<loc_com.size(); ++i)
|
|
202 {
|
|
203 const auto& prev = loc_com.at(i-1);
|
|
204 const auto& curr = loc_com.at(i);
|
|
205
|
|
206 retval << '\n';
|
|
207 // if the filenames are the same, print "..."
|
|
208 if(prev.first.file_name() == curr.first.file_name())
|
|
209 {
|
|
210 retval << color::bold << color::blue << " ...\n" << color::reset;
|
|
211 }
|
|
212 else // if filename differs, print " --> filename.toml" again
|
|
213 {
|
|
214 retval << color::bold << color::blue << " --> " << color::reset
|
|
215 << curr.first.file_name() << '\n';
|
|
216 retval << make_string(line_num_width + 1, ' ')
|
|
217 << color::bold << color::blue << " |\n" << color::reset;
|
|
218 }
|
|
219
|
|
220 format_one_location(retval, curr.first, curr.second);
|
|
221 }
|
|
222
|
|
223 if(!helps.empty())
|
|
224 {
|
|
225 retval << '\n';
|
|
226 retval << make_string(line_num_width + 1, ' ');
|
|
227 retval << color::bold << color::blue << " |" << color::reset;
|
|
228 for(const auto& help : helps)
|
|
229 {
|
|
230 retval << color::bold << "\nHint: " << color::reset;
|
|
231 retval << help;
|
|
232 }
|
|
233 }
|
|
234 return retval.str();
|
|
235 }
|
|
236
|
|
237 } // detail
|
|
238 } // toml
|
|
239 #endif// TOML11_SOURCE_LOCATION_HPP
|