Mercurial > minori
comparison dep/toml11/toml/region.hpp @ 318:3b355fa948c7
config: use TOML instead of INI
unfortunately, INI is not enough, and causes some paths including
semicolons to break with our current storage of the library folders.
so, I decided to switch to TOML which does support real arrays...
| author | Paper <paper@paper.us.eu.org> |
|---|---|
| date | Wed, 12 Jun 2024 05:25:41 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 317:b1f4d1867ab1 | 318:3b355fa948c7 |
|---|---|
| 1 // Copyright Toru Niina 2017. | |
| 2 // Distributed under the MIT License. | |
| 3 #ifndef TOML11_REGION_HPP | |
| 4 #define TOML11_REGION_HPP | |
| 5 #include <memory> | |
| 6 #include <vector> | |
| 7 #include <algorithm> | |
| 8 #include <initializer_list> | |
| 9 #include <iterator> | |
| 10 #include <iomanip> | |
| 11 #include <cassert> | |
| 12 #include "color.hpp" | |
| 13 | |
| 14 namespace toml | |
| 15 { | |
| 16 namespace detail | |
| 17 { | |
| 18 | |
| 19 // helper function to avoid std::string(0, 'c') or std::string(iter, iter) | |
| 20 template<typename Iterator> | |
| 21 std::string make_string(Iterator first, Iterator last) | |
| 22 { | |
| 23 if(first == last) {return "";} | |
| 24 return std::string(first, last); | |
| 25 } | |
| 26 inline std::string make_string(std::size_t len, char c) | |
| 27 { | |
| 28 if(len == 0) {return "";} | |
| 29 return std::string(len, c); | |
| 30 } | |
| 31 | |
| 32 // region_base is a base class of location and region that are defined below. | |
| 33 // it will be used to generate better error messages. | |
| 34 struct region_base | |
| 35 { | |
| 36 region_base() = default; | |
| 37 virtual ~region_base() = default; | |
| 38 region_base(const region_base&) = default; | |
| 39 region_base(region_base&& ) = default; | |
| 40 region_base& operator=(const region_base&) = default; | |
| 41 region_base& operator=(region_base&& ) = default; | |
| 42 | |
| 43 virtual bool is_ok() const noexcept {return false;} | |
| 44 virtual char front() const noexcept {return '\0';} | |
| 45 | |
| 46 virtual std::string str() const {return std::string("unknown region");} | |
| 47 virtual std::string name() const {return std::string("unknown file");} | |
| 48 virtual std::string line() const {return std::string("unknown line");} | |
| 49 virtual std::string line_num() const {return std::string("?");} | |
| 50 | |
| 51 // length of the region | |
| 52 virtual std::size_t size() const noexcept {return 0;} | |
| 53 // number of characters in the line before the region | |
| 54 virtual std::size_t before() const noexcept {return 0;} | |
| 55 // number of characters in the line after the region | |
| 56 virtual std::size_t after() const noexcept {return 0;} | |
| 57 | |
| 58 virtual std::vector<std::string> comments() const {return {};} | |
| 59 // ```toml | |
| 60 // # comment_before | |
| 61 // key = "value" # comment_inline | |
| 62 // ``` | |
| 63 }; | |
| 64 | |
| 65 // location represents a position in a container, which contains a file content. | |
| 66 // it can be considered as a region that contains only one character. | |
| 67 // | |
| 68 // it contains pointer to the file content and iterator that points the current | |
| 69 // location. | |
| 70 struct location final : public region_base | |
| 71 { | |
| 72 using const_iterator = typename std::vector<char>::const_iterator; | |
| 73 using difference_type = typename std::iterator_traits<const_iterator>::difference_type; | |
| 74 using source_ptr = std::shared_ptr<const std::vector<char>>; | |
| 75 | |
| 76 location(std::string source_name, std::vector<char> cont) | |
| 77 : source_(std::make_shared<std::vector<char>>(std::move(cont))), | |
| 78 line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) | |
| 79 {} | |
| 80 location(std::string source_name, const std::string& cont) | |
| 81 : source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())), | |
| 82 line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) | |
| 83 {} | |
| 84 | |
| 85 location(const location&) = default; | |
| 86 location(location&&) = default; | |
| 87 location& operator=(const location&) = default; | |
| 88 location& operator=(location&&) = default; | |
| 89 ~location() = default; | |
| 90 | |
| 91 bool is_ok() const noexcept override {return static_cast<bool>(source_);} | |
| 92 char front() const noexcept override {return *iter_;} | |
| 93 | |
| 94 // this const prohibits codes like `++(loc.iter())`. | |
| 95 std::add_const<const_iterator>::type iter() const noexcept {return iter_;} | |
| 96 | |
| 97 const_iterator begin() const noexcept {return source_->cbegin();} | |
| 98 const_iterator end() const noexcept {return source_->cend();} | |
| 99 | |
| 100 // XXX `location::line_num()` used to be implemented using `std::count` to | |
| 101 // count a number of '\n'. But with a long toml file (typically, 10k lines), | |
| 102 // it becomes intolerably slow because each time it generates error messages, | |
| 103 // it counts '\n' from thousands of characters. To workaround it, I decided | |
| 104 // to introduce `location::line_number_` member variable and synchronize it | |
| 105 // to the location changes the point to look. So an overload of `iter()` | |
| 106 // which returns mutable reference is removed and `advance()`, `retrace()` | |
| 107 // and `reset()` is added. | |
| 108 void advance(difference_type n = 1) noexcept | |
| 109 { | |
| 110 this->line_number_ += static_cast<std::size_t>( | |
| 111 std::count(this->iter_, std::next(this->iter_, n), '\n')); | |
| 112 this->iter_ += n; | |
| 113 return; | |
| 114 } | |
| 115 void retrace(difference_type n = 1) noexcept | |
| 116 { | |
| 117 this->line_number_ -= static_cast<std::size_t>( | |
| 118 std::count(std::prev(this->iter_, n), this->iter_, '\n')); | |
| 119 this->iter_ -= n; | |
| 120 return; | |
| 121 } | |
| 122 void reset(const_iterator rollback) noexcept | |
| 123 { | |
| 124 // since c++11, std::distance works in both ways for random-access | |
| 125 // iterators and returns a negative value if `first > last`. | |
| 126 if(0 <= std::distance(rollback, this->iter_)) // rollback < iter | |
| 127 { | |
| 128 this->line_number_ -= static_cast<std::size_t>( | |
| 129 std::count(rollback, this->iter_, '\n')); | |
| 130 } | |
| 131 else // iter < rollback [[unlikely]] | |
| 132 { | |
| 133 this->line_number_ += static_cast<std::size_t>( | |
| 134 std::count(this->iter_, rollback, '\n')); | |
| 135 } | |
| 136 this->iter_ = rollback; | |
| 137 return; | |
| 138 } | |
| 139 | |
| 140 std::string str() const override {return make_string(1, *this->iter());} | |
| 141 std::string name() const override {return source_name_;} | |
| 142 | |
| 143 std::string line_num() const override | |
| 144 { | |
| 145 return std::to_string(this->line_number_); | |
| 146 } | |
| 147 | |
| 148 std::string line() const override | |
| 149 { | |
| 150 return make_string(this->line_begin(), this->line_end()); | |
| 151 } | |
| 152 | |
| 153 const_iterator line_begin() const noexcept | |
| 154 { | |
| 155 using reverse_iterator = std::reverse_iterator<const_iterator>; | |
| 156 return std::find(reverse_iterator(this->iter()), | |
| 157 reverse_iterator(this->begin()), '\n').base(); | |
| 158 } | |
| 159 const_iterator line_end() const noexcept | |
| 160 { | |
| 161 return std::find(this->iter(), this->end(), '\n'); | |
| 162 } | |
| 163 | |
| 164 // location is always points a character. so the size is 1. | |
| 165 std::size_t size() const noexcept override | |
| 166 { | |
| 167 return 1u; | |
| 168 } | |
| 169 std::size_t before() const noexcept override | |
| 170 { | |
| 171 const auto sz = std::distance(this->line_begin(), this->iter()); | |
| 172 assert(sz >= 0); | |
| 173 return static_cast<std::size_t>(sz); | |
| 174 } | |
| 175 std::size_t after() const noexcept override | |
| 176 { | |
| 177 const auto sz = std::distance(this->iter(), this->line_end()); | |
| 178 assert(sz >= 0); | |
| 179 return static_cast<std::size_t>(sz); | |
| 180 } | |
| 181 | |
| 182 source_ptr const& source() const& noexcept {return source_;} | |
| 183 source_ptr&& source() && noexcept {return std::move(source_);} | |
| 184 | |
| 185 private: | |
| 186 | |
| 187 source_ptr source_; | |
| 188 std::size_t line_number_; | |
| 189 std::string source_name_; | |
| 190 const_iterator iter_; | |
| 191 }; | |
| 192 | |
| 193 // region represents a range in a container, which contains a file content. | |
| 194 // | |
| 195 // it contains pointer to the file content and iterator that points the first | |
| 196 // and last location. | |
| 197 struct region final : public region_base | |
| 198 { | |
| 199 using const_iterator = typename std::vector<char>::const_iterator; | |
| 200 using source_ptr = std::shared_ptr<const std::vector<char>>; | |
| 201 | |
| 202 // delete default constructor. source_ never be null. | |
| 203 region() = delete; | |
| 204 | |
| 205 explicit region(const location& loc) | |
| 206 : source_(loc.source()), source_name_(loc.name()), | |
| 207 first_(loc.iter()), last_(loc.iter()) | |
| 208 {} | |
| 209 explicit region(location&& loc) | |
| 210 : source_(loc.source()), source_name_(loc.name()), | |
| 211 first_(loc.iter()), last_(loc.iter()) | |
| 212 {} | |
| 213 | |
| 214 region(const location& loc, const_iterator f, const_iterator l) | |
| 215 : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) | |
| 216 {} | |
| 217 region(location&& loc, const_iterator f, const_iterator l) | |
| 218 : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) | |
| 219 {} | |
| 220 | |
| 221 region(const region&) = default; | |
| 222 region(region&&) = default; | |
| 223 region& operator=(const region&) = default; | |
| 224 region& operator=(region&&) = default; | |
| 225 ~region() = default; | |
| 226 | |
| 227 region& operator+=(const region& other) | |
| 228 { | |
| 229 // different regions cannot be concatenated | |
| 230 assert(this->source_ == other.source_ && this->last_ == other.first_); | |
| 231 | |
| 232 this->last_ = other.last_; | |
| 233 return *this; | |
| 234 } | |
| 235 | |
| 236 bool is_ok() const noexcept override {return static_cast<bool>(source_);} | |
| 237 char front() const noexcept override {return *first_;} | |
| 238 | |
| 239 std::string str() const override {return make_string(first_, last_);} | |
| 240 std::string line() const override | |
| 241 { | |
| 242 if(this->contain_newline()) | |
| 243 { | |
| 244 return make_string(this->line_begin(), | |
| 245 std::find(this->line_begin(), this->last(), '\n')); | |
| 246 } | |
| 247 return make_string(this->line_begin(), this->line_end()); | |
| 248 } | |
| 249 std::string line_num() const override | |
| 250 { | |
| 251 return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); | |
| 252 } | |
| 253 | |
| 254 std::size_t size() const noexcept override | |
| 255 { | |
| 256 const auto sz = std::distance(first_, last_); | |
| 257 assert(sz >= 0); | |
| 258 return static_cast<std::size_t>(sz); | |
| 259 } | |
| 260 std::size_t before() const noexcept override | |
| 261 { | |
| 262 const auto sz = std::distance(this->line_begin(), this->first()); | |
| 263 assert(sz >= 0); | |
| 264 return static_cast<std::size_t>(sz); | |
| 265 } | |
| 266 std::size_t after() const noexcept override | |
| 267 { | |
| 268 const auto sz = std::distance(this->last(), this->line_end()); | |
| 269 assert(sz >= 0); | |
| 270 return static_cast<std::size_t>(sz); | |
| 271 } | |
| 272 | |
| 273 bool contain_newline() const noexcept | |
| 274 { | |
| 275 return std::find(this->first(), this->last(), '\n') != this->last(); | |
| 276 } | |
| 277 | |
| 278 const_iterator line_begin() const noexcept | |
| 279 { | |
| 280 using reverse_iterator = std::reverse_iterator<const_iterator>; | |
| 281 return std::find(reverse_iterator(this->first()), | |
| 282 reverse_iterator(this->begin()), '\n').base(); | |
| 283 } | |
| 284 const_iterator line_end() const noexcept | |
| 285 { | |
| 286 return std::find(this->last(), this->end(), '\n'); | |
| 287 } | |
| 288 | |
| 289 const_iterator begin() const noexcept {return source_->cbegin();} | |
| 290 const_iterator end() const noexcept {return source_->cend();} | |
| 291 const_iterator first() const noexcept {return first_;} | |
| 292 const_iterator last() const noexcept {return last_;} | |
| 293 | |
| 294 source_ptr const& source() const& noexcept {return source_;} | |
| 295 source_ptr&& source() && noexcept {return std::move(source_);} | |
| 296 | |
| 297 std::string name() const override {return source_name_;} | |
| 298 | |
| 299 std::vector<std::string> comments() const override | |
| 300 { | |
| 301 // assuming the current region (`*this`) points a value. | |
| 302 // ```toml | |
| 303 // a = "value" | |
| 304 // ^^^^^^^- this region | |
| 305 // ``` | |
| 306 using rev_iter = std::reverse_iterator<const_iterator>; | |
| 307 | |
| 308 std::vector<std::string> com{}; | |
| 309 { | |
| 310 // find comments just before the current region. | |
| 311 // ```toml | |
| 312 // # this should be collected. | |
| 313 // # this also. | |
| 314 // a = value # not this. | |
| 315 // ``` | |
| 316 | |
| 317 // # this is a comment for `a`, not array elements. | |
| 318 // a = [1, 2, 3, 4, 5] | |
| 319 if(this->first() == std::find_if(this->line_begin(), this->first(), | |
| 320 [](const char c) noexcept -> bool {return c == '[' || c == '{';})) | |
| 321 { | |
| 322 auto iter = this->line_begin(); // points the first character | |
| 323 while(iter != this->begin()) | |
| 324 { | |
| 325 iter = std::prev(iter); | |
| 326 | |
| 327 // range [line_start, iter) represents the previous line | |
| 328 const auto line_start = std::find( | |
| 329 rev_iter(iter), rev_iter(this->begin()), '\n').base(); | |
| 330 const auto comment_found = std::find(line_start, iter, '#'); | |
| 331 if(comment_found == iter) | |
| 332 { | |
| 333 break; // comment not found. | |
| 334 } | |
| 335 | |
| 336 // exclude the following case. | |
| 337 // > a = "foo" # comment // <-- this is not a comment for b but a. | |
| 338 // > b = "current value" | |
| 339 if(std::all_of(line_start, comment_found, | |
| 340 [](const char c) noexcept -> bool { | |
| 341 return c == ' ' || c == '\t'; | |
| 342 })) | |
| 343 { | |
| 344 // unwrap the first '#' by std::next. | |
| 345 auto s = make_string(std::next(comment_found), iter); | |
| 346 if(!s.empty() && s.back() == '\r') {s.pop_back();} | |
| 347 com.push_back(std::move(s)); | |
| 348 } | |
| 349 else | |
| 350 { | |
| 351 break; | |
| 352 } | |
| 353 iter = line_start; | |
| 354 } | |
| 355 } | |
| 356 } | |
| 357 | |
| 358 if(com.size() > 1) | |
| 359 { | |
| 360 std::reverse(com.begin(), com.end()); | |
| 361 } | |
| 362 | |
| 363 { | |
| 364 // find comments just after the current region. | |
| 365 // ```toml | |
| 366 // # not this. | |
| 367 // a = value # this one. | |
| 368 // a = [ # not this (technically difficult) | |
| 369 // | |
| 370 // ] # and this. | |
| 371 // ``` | |
| 372 // The reason why it's difficult is that it requires parsing in the | |
| 373 // following case. | |
| 374 // ```toml | |
| 375 // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`. | |
| 376 // # ... | |
| 377 // ] # this is apparently a comment for a. | |
| 378 // | |
| 379 // b = [ | |
| 380 // 3.14 ] # there is no way to add a comment to `3.14` currently. | |
| 381 // | |
| 382 // c = [ | |
| 383 // 3.14 # do this if you need a comment here. | |
| 384 // ] | |
| 385 // ``` | |
| 386 const auto comment_found = | |
| 387 std::find(this->last(), this->line_end(), '#'); | |
| 388 if(comment_found != this->line_end()) // '#' found | |
| 389 { | |
| 390 // table = {key = "value"} # what is this for? | |
| 391 // the above comment is not for "value", but {key="value"}. | |
| 392 if(comment_found == std::find_if(this->last(), comment_found, | |
| 393 [](const char c) noexcept -> bool { | |
| 394 return !(c == ' ' || c == '\t' || c == ','); | |
| 395 })) | |
| 396 { | |
| 397 // unwrap the first '#' by std::next. | |
| 398 auto s = make_string(std::next(comment_found), this->line_end()); | |
| 399 if(!s.empty() && s.back() == '\r') {s.pop_back();} | |
| 400 com.push_back(std::move(s)); | |
| 401 } | |
| 402 } | |
| 403 } | |
| 404 return com; | |
| 405 } | |
| 406 | |
| 407 private: | |
| 408 | |
| 409 source_ptr source_; | |
| 410 std::string source_name_; | |
| 411 const_iterator first_, last_; | |
| 412 }; | |
| 413 | |
| 414 } // detail | |
| 415 } // toml | |
| 416 #endif// TOML11_REGION_H |
