From d7b4d104d3f22650b863a89bc5525b683932367a Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 8 Oct 2019 23:15:03 +0900 Subject: [PATCH 01/58] refactor: reduce checking; just check once --- toml/parser.hpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 6519740..9f31be8 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1384,9 +1384,10 @@ parse_inline_table(location& loc) { return err(kv_r.unwrap_err()); } - const std::vector& keys = kv_r.unwrap().first.first; - const region& key_reg = kv_r.unwrap().first.second; - const value_type& val = kv_r.unwrap().second; + const auto& kvpair = kv_r.unwrap(); + const std::vector& keys = kvpair.first.first; + const region& key_reg = kvpair.first.second; + const value_type& val = kvpair.second; const auto inserted = insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg); @@ -1771,9 +1772,10 @@ parse_ml_table(location& loc) if(const auto kv = parse_key_value_pair(loc)) { - const std::vector& keys = kv.unwrap().first.first; - const region& key_reg = kv.unwrap().first.second; - const value_type& val = kv.unwrap().second; + const auto& kvpair = kv.unwrap(); + const std::vector& keys = kvpair.first.first; + const region& key_reg = kvpair.first.second; + const value_type& val = kvpair.second; const auto inserted = insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg); if(!inserted) @@ -1889,8 +1891,9 @@ result parse_toml_file(location& loc) const auto tab = parse_ml_table(loc); if(!tab){return err(tab.unwrap_err());} - const auto& keys = tabkey.unwrap().first; - const auto& reg = tabkey.unwrap().second; + const auto& tk = tabkey.unwrap(); + const auto& keys = tk.first; + const auto& reg = tk.second; const auto inserted = insert_nested_key(data, value_type(tab.unwrap(), reg), @@ -1905,8 +1908,9 @@ result parse_toml_file(location& loc) const auto tab = parse_ml_table(loc); if(!tab){return err(tab.unwrap_err());} - const auto& keys = tabkey.unwrap().first; - const auto& reg = tabkey.unwrap().second; + const auto& tk = tabkey.unwrap(); + const auto& keys = tk.first; + const auto& reg = tk.second; const auto inserted = insert_nested_key(data, value_type(tab.unwrap(), reg), keys.begin(), keys.end(), reg); From b4b35ea33edff3b611206ce6982c2855b9f88597 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 8 Oct 2019 23:23:53 +0900 Subject: [PATCH 02/58] feat: allow 0-prefix exponent if the flag is on If unreleased feature is activated, zero-prefixes in an exponent part of a floating point is allowed. If the flag TOML11_UNRELEASED_TOML_FEATURES is turned on, we don't need to check whether there is a zero prefix in the exponent part that is formatted by a standard library. --- toml/serializer.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/toml/serializer.hpp b/toml/serializer.hpp index f1fa762..44fae97 100644 --- a/toml/serializer.hpp +++ b/toml/serializer.hpp @@ -118,8 +118,14 @@ struct serializer { return token; // there is no exponent part. just return it. } - - // zero-prefix in an exponent is NOT allowed in TOML. +#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES + // Although currently it is not released yet, TOML will allow + // zero-prefix in an exponent part such as 1.234e+01. + // The following code removes the zero prefixes. + // If the feature is activated, the following codes can be skipped. + return token; +#endif + // zero-prefix in an exponent is NOT allowed in TOML v0.5.0. // remove it if it exists. bool sign_exists = false; std::size_t zero_prefix = 0; From 06086a9ff75ecc5f9372d4c34dfc69d8190a6873 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 9 Oct 2019 21:09:38 +0900 Subject: [PATCH 03/58] doc: add note about value::at --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 222de08..9248db4 100644 --- a/README.md +++ b/README.md @@ -470,6 +470,10 @@ If an invalid key (integer for a table, string for an array), it throws `toml::type_error` for the conversion. If the provided key is out-of-range, it throws `std::out_of_range`. +Note that, although `std::string` has `at()` member function, `toml::value::at` +throws if the contained type is a string. Because `std::string` does not +contain `toml::value`. + ## Checking value type You can check the type of a value by `is_xxx` function. From 258e62f8f31df7b1f20cf57d355f941850139aa0 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 9 Oct 2019 21:51:14 +0900 Subject: [PATCH 04/58] feat: add operator+= to toml::string --- toml/string.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/toml/string.hpp b/toml/string.hpp index 98b355b..0551ccd 100644 --- a/toml/string.hpp +++ b/toml/string.hpp @@ -2,6 +2,7 @@ // Distributed under the MIT License. #ifndef TOML11_STRING_HPP #define TOML11_STRING_HPP +#include #include #include #if __cplusplus >= 201703L @@ -45,6 +46,11 @@ struct string operator std::string const& () const& noexcept {return str;} operator std::string&& () && noexcept {return std::move(str);} + string& operator+=(const char* rhs) {str += rhs; return *this;} + string& operator+=(const char rhs) {str += rhs; return *this;} + string& operator+=(const std::string& rhs) {str += rhs; return *this;} + string& operator+=(const string& rhs) {str += rhs.str; return *this;} + #if __cplusplus >= 201703L explicit string(std::string_view s): kind(string_t::basic), str(s){} string(std::string_view s, string_t k): kind(k), str(s){} @@ -54,6 +60,8 @@ struct string explicit operator std::string_view() const noexcept {return std::string_view(str);} + + string& operator+=(const std::string_view& rhs) {str += rhs; return *this;} #endif string_t kind; From 54eced6c82dafb17958052f585895df8e3265047 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 9 Oct 2019 21:51:33 +0900 Subject: [PATCH 05/58] test: add test for toml::string::operator+= --- tests/test_string.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/test_string.cpp b/tests/test_string.cpp index f007bdf..e03d412 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -111,3 +111,44 @@ BOOST_AUTO_TEST_CASE(test_literal_ml_string) } } +BOOST_AUTO_TEST_CASE(test_string_add_assign) +{ + // string literal + { + toml::string str("foo"); + str += "bar"; + BOOST_TEST(str.str == "foobar"); + } + // std::string + { + toml::string str("foo"); + std::string str2("bar"); + str += str2; + BOOST_TEST(str.str == "foobar"); + } + // toml::string + { + toml::string str("foo"); + toml::string str2("bar"); + str += str2; + BOOST_TEST(str.str == "foobar"); + } +#if __cplusplus >= 201703L + // std::string_view + { + toml::string str("foo"); + str += std::string_view("bar"); + BOOST_TEST(str == "foobar"); + } +#endif + // std::string += toml::string + { + std::string str("foo"); + toml::string str2("bar"); + str += str2; + BOOST_TEST(str == "foobar"); + } + + +} + From 5bba73a8caf7b75f6313a5cdc0f68ffd1383d76c Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 15 Oct 2019 20:27:27 +0900 Subject: [PATCH 06/58] ci: use addons in OS X on Travis.CI --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0ddf2e8..19ecfda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -216,6 +216,12 @@ matrix: language: cpp compiler: clang env: CXX_STANDARD=11 + addons: + homebrew: + update: true + packages: + - cmake + - boost script: - | @@ -224,8 +230,6 @@ script: travis_retry wget "https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz" tar xf cmake-3.14.5-Linux-x86_64.tar.gz -C cmake --strip-components=1 export PATH=${TRAVIS_BUILD_DIR}/cmake/bin:${PATH} - else - brew upgrade cmake boost fi - cmake --version - mkdir build From ea24a91f4cc51068530dddcaf6199760ece3f19f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 15 Oct 2019 20:32:09 +0900 Subject: [PATCH 07/58] ci: use `sourceline` --- .travis.yml | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 19ecfda..d98a18f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' packages: - g++-5 @@ -21,7 +21,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' packages: - g++-6 @@ -33,7 +33,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' packages: - g++-7 @@ -45,7 +45,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' packages: - g++-8 @@ -57,7 +57,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' packages: - g++-8 @@ -69,7 +69,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' packages: - g++-8 @@ -81,7 +81,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' packages: - g++-8 @@ -93,8 +93,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.7 + - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' packages: - g++-8 @@ -107,7 +106,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - llvm-toolchain-trusty-4.0 - sourceline: 'ppa:mhier/libboost-latest' packages: @@ -121,7 +120,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - llvm-toolchain-trusty-5.0 - sourceline: 'ppa:mhier/libboost-latest' packages: @@ -135,7 +134,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - llvm-toolchain-trusty-6.0 - sourceline: 'ppa:mhier/libboost-latest' packages: @@ -149,7 +148,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - llvm-toolchain-trusty-7 - sourceline: 'ppa:mhier/libboost-latest' packages: @@ -163,7 +162,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - llvm-toolchain-trusty-8 - sourceline: 'ppa:mhier/libboost-latest' packages: @@ -177,7 +176,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - llvm-toolchain-trusty-8 - sourceline: 'ppa:mhier/libboost-latest' packages: @@ -191,7 +190,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - llvm-toolchain-trusty-8 - sourceline: 'ppa:mhier/libboost-latest' packages: @@ -205,7 +204,7 @@ matrix: addons: apt: sources: - - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntu-toolchain-r/test' - llvm-toolchain-trusty-8 - sourceline: 'ppa:mhier/libboost-latest' packages: From 3dc3b001ffa266e7d5f90928aaa35c986c1fb8eb Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 15 Oct 2019 21:31:45 +0900 Subject: [PATCH 08/58] ci: update clang 3.x from 3.7 to 3.9 --- .travis.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index d98a18f..47de972 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,15 +89,16 @@ matrix: - os: linux language: cpp compiler: clang - env: COMPILER="clang++-3.7" CXX_STANDARD=11 TOML_HEAD=OFF + env: COMPILER="clang++-3.9" CXX_STANDARD=11 TOML_HEAD=OFF addons: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-3.9 packages: - g++-8 - - clang-3.7 + - clang-3.9 - boost1.70 - os: linux language: cpp @@ -107,8 +108,8 @@ matrix: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - - llvm-toolchain-trusty-4.0 - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-4.0 packages: - g++-8 - clang-4.0 @@ -121,8 +122,8 @@ matrix: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - - llvm-toolchain-trusty-5.0 - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-5.0 packages: - g++-8 - clang-5.0 @@ -135,8 +136,8 @@ matrix: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - - llvm-toolchain-trusty-6.0 - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-6.0 packages: - g++-8 - clang-6.0 @@ -149,8 +150,8 @@ matrix: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - - llvm-toolchain-trusty-7 - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-7 packages: - g++-8 - clang-7 @@ -163,8 +164,8 @@ matrix: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - - llvm-toolchain-trusty-8 - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-8 packages: - g++-8 - clang-8 @@ -177,8 +178,8 @@ matrix: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - - llvm-toolchain-trusty-8 - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-8 packages: - g++-8 - clang-8 @@ -191,8 +192,8 @@ matrix: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - - llvm-toolchain-trusty-8 - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-8 packages: - clang-8 - g++-8 @@ -205,8 +206,8 @@ matrix: apt: sources: - sourceline: 'ppa:ubuntu-toolchain-r/test' - - llvm-toolchain-trusty-8 - sourceline: 'ppa:mhier/libboost-latest' + - llvm-toolchain-trusty-8 packages: - clang-8 - g++-8 From 354cfc979a3cbfbcd2b83df349e9fb222b400442 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 15 Oct 2019 21:40:37 +0900 Subject: [PATCH 09/58] ci: cache brew directory --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index 47de972..3542fc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -216,6 +216,9 @@ matrix: language: cpp compiler: clang env: CXX_STANDARD=11 + cache: + directories: + - $HOME/Library/Caches/Homebrew addons: homebrew: update: true @@ -238,3 +241,10 @@ script: - cmake -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_CXX_STANDARD=$CXX_STANDARD -DTOML11_USE_UNRELEASED_TOML_FEATURES=${TOML_HEAD} .. - make - ctest --output-on-failure + +# https://stackoverflow.com/a/53331571 +before_cache: +- | + if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then + brew cleanup + fi From 37769e28f086f8ee7e8595b18c25ac11a8198d03 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 30 Oct 2019 16:32:45 +0900 Subject: [PATCH 10/58] fix #88: check if input is null-terminated or not --- toml/parser.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/toml/parser.hpp b/toml/parser.hpp index 9f31be8..5dbef6c 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1949,6 +1949,11 @@ parse(std::istream& is, const std::string& fname = "unknown file") std::vector letters(static_cast(fsize)); is.read(letters.data(), fsize); + if(letters.back() == '\0') + { + letters.pop_back(); + } + detail::location> loc(std::move(fname), std::move(letters)); From f98615d0df12b872df69b977826eac7be1da4a00 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 30 Oct 2019 16:48:39 +0900 Subject: [PATCH 11/58] fix: check file content is empty or not --- toml/parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 5dbef6c..2805d80 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1949,7 +1949,7 @@ parse(std::istream& is, const std::string& fname = "unknown file") std::vector letters(static_cast(fsize)); is.read(letters.data(), fsize); - if(letters.back() == '\0') + if(!letters.empty() && letters.back() == '\0') { letters.pop_back(); } From 56812114c324691d5f4341793827f7eb97631594 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 31 Oct 2019 21:58:28 +0900 Subject: [PATCH 12/58] refactor: simplify inclusion dependencies --- toml/region.hpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index e5ed659..de6d04b 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -2,7 +2,6 @@ // Distributed under the MIT License. #ifndef TOML11_REGION_HPP #define TOML11_REGION_HPP -#include "exception.hpp" #include #include #include @@ -231,11 +230,10 @@ struct region final : public region_base region& operator+=(const region& other) { - if(this->begin() != other.begin() || this->end() != other.end() || - this->last_ != other.first_) - { - throw internal_error("invalid region concatenation"); - } + // different regions cannot be concatenated + assert(this->begin() == other.begin() && this->end() == other.end() && + this->last_ == other.first_); + this->last_ = other.last_; return *this; } From 8e589ff4d749e74f111e96e8ad9ffa2c6c17e007 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 31 Oct 2019 22:04:16 +0900 Subject: [PATCH 13/58] feat: add source_location to (syntax_|type_)error --- toml/exception.hpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/toml/exception.hpp b/toml/exception.hpp index 1a3cd0c..08541b0 100644 --- a/toml/exception.hpp +++ b/toml/exception.hpp @@ -2,6 +2,7 @@ // Distributed under the MIT License. #ifndef TOML11_EXCEPTION_HPP #define TOML11_EXCEPTION_HPP +#include "source_location.hpp" #include #include @@ -18,32 +19,41 @@ struct exception : public std::exception struct syntax_error : public toml::exception { public: - explicit syntax_error(const std::string& what_arg) : what_(what_arg){} - explicit syntax_error(const char* what_arg) : what_(what_arg){} + explicit syntax_error(const std::string& what_arg, const source_location& loc) + : what_(what_arg), loc_(loc) + {} virtual ~syntax_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} + source_location const& location() const noexcept {return loc_;} + protected: std::string what_; + source_location loc_; }; struct type_error : public toml::exception { public: - explicit type_error(const std::string& what_arg) : what_(what_arg){} - explicit type_error(const char* what_arg) : what_(what_arg){} + explicit type_error(const std::string& what_arg, const source_location& loc) + : what_(what_arg), loc_(loc) + {} virtual ~type_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} + source_location const& location() const noexcept {return loc_;} + protected: std::string what_; + source_location loc_; }; struct internal_error : public toml::exception { public: - explicit internal_error(const std::string& what_arg) : what_(what_arg){} - explicit internal_error(const char* what_arg) : what_(what_arg){} + explicit internal_error(const std::string& what_arg) + : what_(what_arg) + {} virtual ~internal_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} protected: From 3ca712a8dada865de3755d64e2cf22bf2f982fe0 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 31 Oct 2019 22:21:24 +0900 Subject: [PATCH 14/58] feat: check line_num before converting it to int --- toml/source_location.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/toml/source_location.hpp b/toml/source_location.hpp index 051f4e5..f85c51e 100644 --- a/toml/source_location.hpp +++ b/toml/source_location.hpp @@ -48,7 +48,11 @@ struct source_location { if(reg) { - line_num_ = static_cast(std::stoul(reg->line_num())); + if(reg->line_num() != detail::region_base().line_num()) + { + line_num_ = static_cast( + std::stoul(reg->line_num())); + } column_num_ = static_cast(reg->before() + 1); region_size_ = static_cast(reg->size()); file_name_ = reg->name(); From 41eb1d6887aaad65d819f85255a12d51facdca42 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 31 Oct 2019 22:23:31 +0900 Subject: [PATCH 15/58] feat: pass source_location to exception --- toml/get.hpp | 2 +- toml/literal.hpp | 3 ++- toml/parser.hpp | 49 +++++++++++++++++++++++++++--------------------- toml/value.hpp | 2 +- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index 7445ff0..2973e35 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -190,7 +190,7 @@ get(const basic_value& v) "bad_cast to std::chrono::system_clock::time_point", { {std::addressof(detail::get_region(v)), concat_to_string("the actual type is ", v.type())} - })); + }), v.location()); } } } diff --git a/toml/literal.hpp b/toml/literal.hpp index 783d309..14b6883 100644 --- a/toml/literal.hpp +++ b/toml/literal.hpp @@ -76,7 +76,8 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len) } else // none of them. { - throw ::toml::syntax_error(data.unwrap_err()); + throw ::toml::syntax_error(data.unwrap_err(), + source_location(std::addressof(loc))); } } diff --git a/toml/parser.hpp b/toml/parser.hpp index 2805d80..6d89866 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -278,7 +278,7 @@ std::string read_utf8_codepoint(const region& reg, "toml::read_utf8_codepoint: codepoints in the range " "[0xD800, 0xDFFF] are not valid UTF-8.", {{ std::addressof(loc), "not a valid UTF-8 codepoint" - }})); + }}), source_location(std::addressof(loc))); } assert(codepoint < 0xD800 || 0xDFFF < codepoint); // 1110yyyy 10yxxxxx 10xxxxxx @@ -298,7 +298,8 @@ std::string read_utf8_codepoint(const region& reg, { throw syntax_error(format_underline("[error] toml::read_utf8_codepoint:" " input codepoint is too large.", - {{std::addressof(loc), "should be in [0x00..0x10FFFF]"}})); + {{std::addressof(loc), "should be in [0x00..0x10FFFF]"}}), + source_location(std::addressof(loc))); } return character; } @@ -929,7 +930,7 @@ parse_array(location& loc) std::addressof(get_region(val.unwrap())), "value has different type, " + stringize(val.unwrap().type()) } - })); + }), source_location(std::addressof(loc))); } retval.push_back(std::move(val.unwrap())); } @@ -942,7 +943,7 @@ parse_array(location& loc) "value having invalid format appeared in an array", { {std::addressof(array_start_loc), "array starts here"}, {std::addressof(loc), "it is not a valid value."} - })); + }), source_location(std::addressof(loc))); } using lex_array_separator = sequence, character<','>>; @@ -965,14 +966,15 @@ parse_array(location& loc) " missing array separator `,` after a value", { {std::addressof(array_start_loc), "array starts here"}, {std::addressof(loc), "should be `,`"} - })); + }), source_location(std::addressof(loc))); } } } loc.reset(first); throw syntax_error(format_underline("[error] toml::parse_array: " "array did not closed by `]`", - {{std::addressof(loc), "should be closed"}})); + {{std::addressof(loc), "should be closed"}}), + source_location(std::addressof(loc))); } template @@ -1175,7 +1177,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, "table already defined"}, {std::addressof(get_region(v)), "this conflicts with the previous table"} - })); + }), v.location()); } else if(!(tab->at(k).is_array())) { @@ -1188,7 +1190,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, " value already exists")}, {std::addressof(get_region(v)), "while inserting this array-of-tables"} - })); + }), v.location()); } // the above if-else-if checks tab->at(k) is an array auto& a = tab->at(k).as_array(); @@ -1203,7 +1205,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, " value already exists")}, {std::addressof(get_region(v)), "while inserting this array-of-tables"} - })); + }), v.location()); } // avoid conflicting array of table like the following. // ```toml @@ -1231,7 +1233,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, " value has static size")}, {std::addressof(get_region(v)), "appending it to the statically sized array"} - })); + }), v.location()); } a.push_back(v); return ok(true); @@ -1259,7 +1261,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, "table already exists here"}, {std::addressof(get_region(v)), "table defined twice"} - })); + }), v.location()); } // to allow the following toml file. // [a.b.c] @@ -1286,7 +1288,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, "array of tables defined here"}, {std::addressof(get_region(v)), "table conflicts with the previous array of table"} - })); + }), v.location()); } else { @@ -1297,7 +1299,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, "value already exists here"}, {std::addressof(get_region(v)), "value defined twice"} - })); + }), v.location()); } } tab->insert(std::make_pair(k, v)); @@ -1333,7 +1335,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, {std::addressof(get_region(a.back())), concat_to_string("actual type is ", a.back().type())}, {std::addressof(get_region(v)), "inserting this"} - })); + }), v.location()); } tab = std::addressof(a.back().as_table()); } @@ -1346,7 +1348,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, {std::addressof(get_region(tab->at(k))), concat_to_string("actual type is ", tab->at(k).type())}, {std::addressof(get_region(v)), "inserting this"} - })); + }), v.location()); } } } @@ -1412,20 +1414,23 @@ parse_inline_table(location& loc) { throw syntax_error(format_underline("[error] " "toml::parse_inline_table: missing curly brace `}`", - {{std::addressof(loc), "should be `}`"}})); + {{std::addressof(loc), "should be `}`"}}), + source_location(std::addressof(loc))); } else { throw syntax_error(format_underline("[error] " "toml::parse_inline_table: missing table separator `,` ", - {{std::addressof(loc), "should be `,`"}})); + {{std::addressof(loc), "should be `,`"}}), + source_location(std::addressof(loc))); } } } loc.reset(first); throw syntax_error(format_underline("[error] toml::parse_inline_table: " "inline table did not closed by `}`", - {{std::addressof(loc), "should be closed"}})); + {{std::addressof(loc), "should be closed"}}), + source_location(std::addressof(loc))); } template @@ -1668,7 +1673,8 @@ parse_table_key(location& loc) { throw syntax_error(format_underline("[error] " "toml::parse_table_key: newline required after [table.key]", - {{std::addressof(loc), "expected newline"}})); + {{std::addressof(loc), "expected newline"}}), + source_location(std::addressof(loc))); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); @@ -1722,7 +1728,8 @@ parse_array_table_key(location& loc) { throw syntax_error(format_underline("[error] toml::" "parse_array_table_key: newline required after [[table.key]]", - {{std::addressof(loc), "expected newline"}})); + {{std::addressof(loc), "expected newline"}}), + source_location(std::addressof(loc))); } } return ok(std::make_pair(keys.unwrap().first, token.unwrap())); @@ -1976,7 +1983,7 @@ parse(std::istream& is, const std::string& fname = "unknown file") const auto data = detail::parse_toml_file(loc); if(!data) { - throw syntax_error(data.unwrap_err()); + throw syntax_error(data.unwrap_err(), source_location(std::addressof(loc))); } return data.unwrap(); } diff --git a/toml/value.hpp b/toml/value.hpp index cdb9506..e2f5cf8 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -35,7 +35,7 @@ throw_bad_cast(value_t actual, const ::toml::basic_value& v) "[error] toml::value bad_cast to ", Expected), { {std::addressof(get_region(v)), concat_to_string("the actual type is ", actual)} - })); + }), v.location()); } // switch by `value_t` and call the corresponding `value::as_xxx()`. {{{ From 6a15e8360fde19bcd43ac02d5adf0dbc7bf38cc2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 31 Oct 2019 23:13:49 +0900 Subject: [PATCH 16/58] refactor: remove redundant namespace specifier --- toml/parser.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 6d89866..f1e311e 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -29,7 +29,7 @@ parse_boolean(location& loc) else if(reg.str() == "false") {return ok(std::make_pair(false, reg));} else // internal error. { - throw toml::internal_error(format_underline( + throw internal_error(format_underline( "[error] toml::parse_boolean: internal error", {{std::addressof(reg), "invalid token"}})); } @@ -56,7 +56,7 @@ parse_binary_integer(location& loc) else if(*i == '_'){/* do nothing. */} else // internal error. { - throw toml::internal_error(format_underline( + throw internal_error(format_underline( "[error] toml::parse_integer: internal error", {{std::addressof(token.unwrap()), "invalid token"}})); } From 2ee69fc420330320e3348ac6826628417d91d68e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 1 Nov 2019 13:34:15 +0900 Subject: [PATCH 17/58] fix: improve error messages about strings a bit --- toml/parser.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index f1e311e..a1c0150 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -414,7 +414,7 @@ parse_ml_basic_string(location& loc) { loc.reset(first); return err(format_underline("[error] toml::parse_ml_basic_string: " - "the next token is not a multiline string", + "the next token is not a valid multiline string", {{std::addressof(loc), "here"}})); } } @@ -454,7 +454,7 @@ parse_basic_string(location& loc) if(inner_loc.iter() == inner_loc.end()) { throw internal_error(format_underline("[error] " - "parse_ml_basic_string: unexpected end of region", + "parse_basic_string: unexpected end of region", {{std::addressof(inner_loc), "not sufficient token"}})); } quot = lex_quotation_mark::invoke(inner_loc); @@ -465,7 +465,7 @@ parse_basic_string(location& loc) { loc.reset(first); // rollback return err(format_underline("[error] toml::parse_basic_string: " - "the next token is not a string", + "the next token is not a valid string", {{std::addressof(loc), "here"}})); } } @@ -506,7 +506,7 @@ parse_ml_literal_string(location& loc) { loc.reset(first); // rollback return err(format_underline("[error] toml::parse_ml_literal_string: " - "the next token is not a multiline literal string", + "the next token is not a valid multiline literal string", {{std::addressof(loc), "here"}})); } } @@ -545,7 +545,7 @@ parse_literal_string(location& loc) { loc.reset(first); // rollback return err(format_underline("[error] toml::parse_literal_string: " - "the next token is not a literal string", + "the next token is not a valid literal string", {{std::addressof(loc), "here"}})); } } From 6862264bde14642e557ecc81f83e34a90ffe037e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 1 Nov 2019 20:47:25 +0900 Subject: [PATCH 18/58] feat: use the first char as the top-level region A bit related to #89 --- toml/parser.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index a1c0150..ec7ffb6 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1836,12 +1836,12 @@ result parse_toml_file(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return ok(value_type(table_type{})); + return ok(value_type(table_type{}, /* empty file has no region ...*/)); } // put the first line as a region of a file - const region file(loc, loc.iter(), - std::find(loc.iter(), loc.end(), '\n')); + // Here first != loc.end(), so taking std::next is okay + const region file(loc, first, std::next(loc.iter())); // The first successive comments that are separated from the first value // by an empty line are for a file itself. From bc3eb9d967d523e19ba9b3a7a551bd12d8a36369 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 1 Nov 2019 21:14:33 +0900 Subject: [PATCH 19/58] feat: add src_loc to all the exceptions including internal_error. --- toml/exception.hpp | 20 +++++----- toml/parser.hpp | 95 ++++++++++++++++++++++++++++++---------------- 2 files changed, 73 insertions(+), 42 deletions(-) diff --git a/toml/exception.hpp b/toml/exception.hpp index 08541b0..1b0005e 100644 --- a/toml/exception.hpp +++ b/toml/exception.hpp @@ -12,50 +12,50 @@ namespace toml struct exception : public std::exception { public: + exception(const source_location& loc): loc_(loc) {} virtual ~exception() noexcept override = default; virtual const char* what() const noexcept override {return "";} + virtual source_location const& location() const noexcept {return loc_;} + + protected: + source_location loc_; }; struct syntax_error : public toml::exception { public: explicit syntax_error(const std::string& what_arg, const source_location& loc) - : what_(what_arg), loc_(loc) + : exception(loc), what_(what_arg) {} virtual ~syntax_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} - source_location const& location() const noexcept {return loc_;} - protected: std::string what_; - source_location loc_; }; struct type_error : public toml::exception { public: explicit type_error(const std::string& what_arg, const source_location& loc) - : what_(what_arg), loc_(loc) + : exception(loc), what_(what_arg) {} virtual ~type_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} - source_location const& location() const noexcept {return loc_;} - protected: std::string what_; - source_location loc_; }; struct internal_error : public toml::exception { public: - explicit internal_error(const std::string& what_arg) - : what_(what_arg) + explicit internal_error(const std::string& what_arg, const source_location& loc) + : exception(loc), what_(what_arg) {} virtual ~internal_error() noexcept override = default; virtual const char* what() const noexcept override {return what_.c_str();} + protected: std::string what_; }; diff --git a/toml/parser.hpp b/toml/parser.hpp index ec7ffb6..ab52edc 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -31,7 +31,8 @@ parse_boolean(location& loc) { throw internal_error(format_underline( "[error] toml::parse_boolean: internal error", - {{std::addressof(reg), "invalid token"}})); + {{std::addressof(reg), "invalid token"}}), + source_location(std::addressof(reg))); } } loc.reset(first); //rollback @@ -58,7 +59,8 @@ parse_binary_integer(location& loc) { throw internal_error(format_underline( "[error] toml::parse_integer: internal error", - {{std::addressof(token.unwrap()), "invalid token"}})); + {{std::addressof(token.unwrap()), "invalid token"}}), + source_location(std::addressof(loc))); } } return ok(std::make_pair(retval, token.unwrap())); @@ -378,7 +380,8 @@ parse_ml_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: invalid token", - {{std::addressof(inner_loc), "should be \"\"\""}})); + {{std::addressof(inner_loc), "should be \"\"\""}}), + source_location(std::addressof(inner_loc))); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); @@ -404,7 +407,8 @@ parse_ml_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_basic_string: unexpected end of region", - {{std::addressof(inner_loc), "not sufficient token"}})); + {{std::addressof(inner_loc), "not sufficient token"}}), + source_location(std::addressof(inner_loc))); } delim = lex_ml_basic_string_delim::invoke(inner_loc); } @@ -433,7 +437,8 @@ parse_basic_string(location& loc) if(!quot) { throw internal_error(format_underline("[error] parse_basic_string: " - "invalid token", {{std::addressof(inner_loc), "should be \""}})); + "invalid token", {{std::addressof(inner_loc), "should be \""}}), + source_location(std::addressof(inner_loc))); } std::string retval; @@ -455,7 +460,8 @@ parse_basic_string(location& loc) { throw internal_error(format_underline("[error] " "parse_basic_string: unexpected end of region", - {{std::addressof(inner_loc), "not sufficient token"}})); + {{std::addressof(inner_loc), "not sufficient token"}}), + source_location(std::addressof(inner_loc))); } quot = lex_quotation_mark::invoke(inner_loc); } @@ -484,7 +490,8 @@ parse_ml_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_literal_string: invalid token", - {{std::addressof(inner_loc), "should be '''"}})); + {{std::addressof(inner_loc), "should be '''"}}), + source_location(std::addressof(inner_loc))); } // immediate newline is ignored (if exists) /* discard return value */ lex_newline::invoke(inner_loc); @@ -496,7 +503,8 @@ parse_ml_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_ml_literal_string: invalid token", - {{std::addressof(inner_loc), "should be '''"}})); + {{std::addressof(inner_loc), "should be '''"}}), + source_location(std::addressof(inner_loc))); } return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), @@ -525,7 +533,8 @@ parse_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_literal_string: invalid token", - {{std::addressof(inner_loc), "should be '"}})); + {{std::addressof(inner_loc), "should be '"}}), + source_location(std::addressof(inner_loc))); } const auto body = repeat::invoke(inner_loc); @@ -535,7 +544,8 @@ parse_literal_string(location& loc) { throw internal_error(format_underline("[error] " "parse_literal_string: invalid token", - {{std::addressof(inner_loc), "should be '"}})); + {{std::addressof(inner_loc), "should be '"}}), + source_location(std::addressof(inner_loc))); } return ok(std::make_pair( toml::string(body.unwrap().str(), toml::string_t::literal), @@ -596,7 +606,8 @@ parse_local_date(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_inner_local_date: invalid year format", - {{std::addressof(inner_loc), "should be `-`"}})); + {{std::addressof(inner_loc), "should be `-`"}}), + source_location(std::addressof(inner_loc))); } inner_loc.advance(); const auto m = lex_date_month::invoke(inner_loc); @@ -604,7 +615,8 @@ parse_local_date(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_date: invalid month format", - {{std::addressof(inner_loc), "should be `-`"}})); + {{std::addressof(inner_loc), "should be `-`"}}), + source_location(std::addressof(inner_loc))); } inner_loc.advance(); const auto d = lex_date_mday::invoke(inner_loc); @@ -612,7 +624,8 @@ parse_local_date(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_date: invalid day format", - {{std::addressof(inner_loc), "here"}})); + {{std::addressof(inner_loc), "here"}}), + source_location(std::addressof(inner_loc))); } return ok(std::make_pair(local_date( static_cast(from_string(y.unwrap().str(), 0)), @@ -643,7 +656,8 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid year format", - {{std::addressof(inner_loc), "should be `:`"}})); + {{std::addressof(inner_loc), "should be `:`"}}), + source_location(std::addressof(inner_loc))); } inner_loc.advance(); const auto m = lex_time_minute::invoke(inner_loc); @@ -651,7 +665,8 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid month format", - {{std::addressof(inner_loc), "should be `:`"}})); + {{std::addressof(inner_loc), "should be `:`"}}), + source_location(std::addressof(inner_loc))); } inner_loc.advance(); const auto s = lex_time_second::invoke(inner_loc); @@ -659,7 +674,8 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid second format", - {{std::addressof(inner_loc), "here"}})); + {{std::addressof(inner_loc), "here"}}), + source_location(std::addressof(inner_loc))); } local_time time( from_string(h.unwrap().str(), 0), @@ -695,7 +711,8 @@ parse_local_time(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid subsecond format", - {{std::addressof(inner_loc), "here"}})); + {{std::addressof(inner_loc), "here"}}), + source_location(std::addressof(inner_loc))); } } return ok(std::make_pair(time, token.unwrap())); @@ -721,14 +738,16 @@ parse_local_datetime(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - {{std::addressof(inner_loc), "date, not datetime"}})); + {{std::addressof(inner_loc), "date, not datetime"}}), + source_location(std::addressof(inner_loc))); } const char delim = *(inner_loc.iter()); if(delim != 'T' && delim != 't' && delim != ' ') { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - {{std::addressof(inner_loc), "should be `T` or ` ` (space)"}})); + {{std::addressof(inner_loc), "should be `T` or ` ` (space)"}}), + source_location(std::addressof(inner_loc))); } inner_loc.advance(); const auto time = parse_local_time(inner_loc); @@ -736,7 +755,8 @@ parse_local_datetime(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_local_datetime: invalid datetime format", - {{std::addressof(inner_loc), "invalid time fomrat"}})); + {{std::addressof(inner_loc), "invalid time fomrat"}}), + source_location(std::addressof(inner_loc))); } return ok(std::make_pair( local_datetime(date.unwrap().first, time.unwrap().first), @@ -763,7 +783,8 @@ parse_offset_datetime(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_offset_datetime: invalid datetime format", - {{std::addressof(inner_loc), "date, not datetime"}})); + {{std::addressof(inner_loc), "date, not datetime"}}), + source_location(std::addressof(inner_loc))); } time_offset offset(0, 0); if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) @@ -784,7 +805,8 @@ parse_offset_datetime(location& loc) { throw internal_error(format_underline("[error]: " "toml::parse_offset_datetime: invalid datetime format", - {{std::addressof(inner_loc), "should be `Z` or `+HH:MM`"}})); + {{std::addressof(inner_loc), "should be `Z` or `+HH:MM`"}}), + source_location(std::addressof(inner_loc))); } return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), token.unwrap())); @@ -842,7 +864,8 @@ parse_key(location& loc) { throw internal_error(format_underline("[error] " "toml::detail::parse_key: dotted key contains invalid key", - {{std::addressof(inner_loc), k.unwrap_err()}})); + {{std::addressof(inner_loc), k.unwrap_err()}}), + source_location(std::addressof(inner_loc))); } lex_ws::invoke(inner_loc); @@ -858,7 +881,8 @@ parse_key(location& loc) { throw internal_error(format_underline("[error] toml::parse_key: " "dotted key contains invalid key ", - {{std::addressof(inner_loc), "should be `.`"}})); + {{std::addressof(inner_loc), "should be `.`"}}), + source_location(std::addressof(inner_loc))); } } return ok(std::make_pair(keys, reg)); @@ -1396,7 +1420,8 @@ parse_inline_table(location& loc) if(!inserted) { throw internal_error("[error] toml::parse_inline_table: " - "failed to insert value into table: " + inserted.unwrap_err()); + "failed to insert value into table: " + inserted.unwrap_err(), + source_location(std::addressof(loc))); } using lex_table_separator = sequence, character<','>>; @@ -1640,7 +1665,8 @@ parse_table_key(location& loc) { throw internal_error(format_underline("[error] " "toml::parse_table_key: no `[`", - {{std::addressof(inner_loc), "should be `[`"}})); + {{std::addressof(inner_loc), "should be `[`"}}), + source_location(std::addressof(inner_loc))); } // to skip [ a . b . c ] // ^----------- this whitespace @@ -1650,7 +1676,8 @@ parse_table_key(location& loc) { throw internal_error(format_underline("[error] " "toml::parse_table_key: invalid key", - {{std::addressof(inner_loc), "not key"}})); + {{std::addressof(inner_loc), "not key"}}), + source_location(std::addressof(inner_loc))); } // to skip [ a . b . c ] // ^-- this whitespace @@ -1660,7 +1687,8 @@ parse_table_key(location& loc) { throw internal_error(format_underline("[error] " "toml::parse_table_key: no `]`", - {{std::addressof(inner_loc), "should be `]`"}})); + {{std::addressof(inner_loc), "should be `]`"}}), + source_location(std::addressof(inner_loc))); } // after [table.key], newline or EOF(empty table) requried. @@ -1699,7 +1727,8 @@ parse_array_table_key(location& loc) { throw internal_error(format_underline("[error] " "toml::parse_array_table_key: no `[[`", - {{std::addressof(inner_loc), "should be `[[`"}})); + {{std::addressof(inner_loc), "should be `[[`"}}), + source_location(std::addressof(inner_loc))); } lex_ws::invoke(inner_loc); const auto keys = parse_key(inner_loc); @@ -1707,7 +1736,8 @@ parse_array_table_key(location& loc) { throw internal_error(format_underline("[error] " "toml::parse_array_table_key: invalid key", - {{std::addressof(inner_loc), "not a key"}})); + {{std::addressof(inner_loc), "not a key"}}), + source_location(std::addressof(inner_loc))); } lex_ws::invoke(inner_loc); const auto close = lex_array_table_close::invoke(inner_loc); @@ -1715,7 +1745,8 @@ parse_array_table_key(location& loc) { throw internal_error(format_underline("[error] " "toml::parse_table_key: no `]]`", - {{std::addressof(inner_loc), "should be `]]`"}})); + {{std::addressof(inner_loc), "should be `]]`"}}), + source_location(std::addressof(inner_loc))); } // after [[table.key]], newline or EOF(empty table) requried. @@ -1836,7 +1867,7 @@ result parse_toml_file(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return ok(value_type(table_type{}, /* empty file has no region ...*/)); + return ok(value_type(table_type{}/* empty file has no region ...*/)); } // put the first line as a region of a file From 047611764c1da8aaf49ce4764009b6efbf55712e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 1 Nov 2019 21:15:20 +0900 Subject: [PATCH 20/58] fix: silly typo --- toml/parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index ec7ffb6..bc0550d 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1836,7 +1836,7 @@ result parse_toml_file(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return ok(value_type(table_type{}, /* empty file has no region ...*/)); + return ok(value_type(table_type{} /*, empty file has no region ...*/)); } // put the first line as a region of a file From c774beb79ac5260f5d0200e939b04d5d33ccc624 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 2 Nov 2019 13:03:22 +0900 Subject: [PATCH 21/58] doc: write about source_location in exceptions --- README.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9248db4..077a2f0 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ int main() - [TOML literal](#toml-literal) - [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) - [Formatting user-defined error messages](#formatting-user-defined-error-messages) +- [Obtaining location information](#obtaining-location-information) +- [Exceptions](#exceptions) - [Serializing TOML data](#serializing-toml-data) - [Underlying types](#underlying-types) - [Unreleased TOML features](#unreleased-toml-features) @@ -351,6 +353,7 @@ The above code works with the following toml file. # NOT {"physical": {"color": "orange"}}. ``` + ## Casting a toml value ### `toml::get` @@ -1258,7 +1261,7 @@ you will get an error message like this. | ~~ maximum number here ``` -### Obtaining location information +## Obtaining location information You can also format error messages in your own way by using `source_location`. @@ -1283,6 +1286,35 @@ const toml::value v = /*...*/; const toml::source_location loc = v.location(); ``` +## Exceptions + +All the exceptions thrown by toml11 inherits `toml::exception` that inherits +`std::exception`. + +```cpp +namespace toml { +struct exception : public std::exception {/**/}; +struct syntax_error : public toml::exception {/**/}; +struct type_error : public toml::exception {/**/}; +struct internal_error : public toml::exception {/**/}; +} // toml +``` + +`toml::exception` has `toml::exception::location()` member function that returns +`toml::source_location`, in addition to `what()`. + +```cpp +namespace toml { +struct exception : public std::exception +{ + // ... + source_location const& location() const noexcept; +}; +} // toml +``` + +It represents where the error occurs. + ## Serializing TOML data toml11 enables you to serialize data into toml format. From 9af2d654178bdeec97d956ae6fb4ae52cb9ac016 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 8 Nov 2019 15:36:58 +0900 Subject: [PATCH 22/58] feat: allow heterogenous array if TOML11_USE_UNRELEASED_TOML_FEATURES is activated. In TOML v0.5.0, heterogenous arrays are not allowed. But after some discussions in toml-lang/toml, it is decided to allow it in the next release (toml-lang/toml/pull/676). To support it, disable type check in parse_array function. --- toml/parser.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/toml/parser.hpp b/toml/parser.hpp index 9fd7097..0efccf0 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -938,6 +938,7 @@ parse_array(location& loc) if(auto val = parse_value(loc)) { +#ifndef TOML11_USE_UNRELEASED_TOML_FEATURES if(!retval.empty() && retval.front().type() != val.as_ok().type()) { auto array_start_loc = loc; @@ -956,6 +957,7 @@ parse_array(location& loc) } }), source_location(std::addressof(loc))); } +#endif retval.push_back(std::move(val.unwrap())); } else From 160537360c235894cbea5b28847b87f9ff1ea272 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 8 Nov 2019 15:39:14 +0900 Subject: [PATCH 23/58] test: deactivate error check for hetero array if TOML11_USE_UNRELEASED_TOML_FEATURES is activated. --- tests/test_error_detection.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_error_detection.cpp b/tests/test_error_detection.cpp index c936209..49dff6e 100644 --- a/tests/test_error_detection.cpp +++ b/tests/test_error_detection.cpp @@ -80,10 +80,14 @@ BOOST_AUTO_TEST_CASE(test_detect_conflicting_value) BOOST_AUTO_TEST_CASE(test_detect_inhomogeneous_array) { +#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES + BOOST_TEST_MESSAGE("heterogeneous array will be allowed in the next release"); +#else std::istringstream stream(std::string( "a = [1, 1.0]\n" )); BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error); +#endif } BOOST_AUTO_TEST_CASE(test_detect_appending_array_of_table) From 0a66be3257fde6d41d22e6f5fbcaa80ab225e519 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 8 Nov 2019 15:52:31 +0900 Subject: [PATCH 24/58] test: add test for parsing heterogeneous array this test case is activated only if TOML11_USE_UNRELEASED_TOML_FEATURES is defined. --- tests/test_parse_array.cpp | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_parse_array.cpp b/tests/test_parse_array.cpp index 9cfbc4d..2dafdb9 100644 --- a/tests/test_parse_array.cpp +++ b/tests/test_parse_array.cpp @@ -128,3 +128,47 @@ BOOST_AUTO_TEST_CASE(test_multiline_array_value) TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::value(a)); } } + +BOOST_AUTO_TEST_CASE(test_heterogeneous_array) +{ +#ifndef TOML11_USE_UNRELEASED_TOML_FEATURES + BOOST_TEST_MESSAGE("In strict TOML v0.5.0, heterogeneous arrays are not allowed."); +#else + { + array a(5); + a[0] = toml::value("foo"); + a[1] = toml::value(3.14); + a[2] = toml::value(42); + a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; + a[4] = toml::value{{"key", "value"}}; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", 3.14, 42, [\"array\", \"of\", \"hetero-array\", 1], {key = \"value\"}]", toml::value(a)); + } + { + array a(5); + a[0] = toml::value("foo"); + a[1] = toml::value(3.14); + a[2] = toml::value(42); + a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; + a[4] = toml::value{{"key", "value"}}; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",\n 3.14,\n 42,\n [\"array\", \"of\", \"hetero-array\", 1],\n {key = \"value\"},\n]", toml::value(a)); + } + { + array a(5); + a[0] = toml::value("foo"); + a[1] = toml::value(3.14); + a[2] = toml::value(42); + a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; + a[4] = toml::value{{"key", "value"}}; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",#comment\n 3.14,#comment\n 42,#comment\n [\"array\", \"of\", \"hetero-array\", 1],#comment\n {key = \"value\"},#comment\n]#comment", toml::value(a)); + } + { + array a(5); + a[0] = toml::value("foo"); + a[1] = toml::value(3.14); + a[2] = toml::value(42); + a[3] = toml::value{toml::value("array"), toml::value("of"), toml::value("hetero-array"), toml::value(1)}; + a[4] = toml::value{{"key", "value"}}; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",\n 3.14,\n 42,\n [\"array\",\n \"of\",\n \"hetero-array\",\n 1],\n {key = \"value\"},\n]", toml::value(a)); + } +#endif +} From adf8fa92341481e0e81567c98c3e1f8757627c4d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 8 Nov 2019 16:12:18 +0900 Subject: [PATCH 25/58] fix: fix typos in an error message in toml::get when converting toml::value to std::tuple --- toml/get.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index 2973e35..2023425 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -357,8 +357,8 @@ get(const basic_value& v) if(ar.size() != std::tuple_size::value) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[erorr] toml::get specified std::tuple with ", - std::tuple_size::value, "elements, but there are ", ar.size(), + "[error] toml::get specified std::tuple with ", + std::tuple_size::value, " elements, but there are ", ar.size(), " elements in toml array."), { {std::addressof(detail::get_region(v)), "here"} })); From 281206dcc6967bfae50ddd32a94f98ce75702254 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 8 Nov 2019 16:11:32 +0900 Subject: [PATCH 26/58] doc: add heterogeneous array to README --- README.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/README.md b/README.md index 077a2f0..56c3f3f 100644 --- a/README.md +++ b/README.md @@ -1455,9 +1455,77 @@ There are some unreleased features in toml-lang/toml:master. Currently, the following features are available after defining `TOML11_USE_UNRELEASED_TOML_FEATURES` macro flag. +To use those features, `#define` `TOML11_USE_UNRELEASED_TOML_FEATURES` before +including `toml.hpp` or pass `-DTOML11_USE_UNRELEASED_TOML_FEATURES` to your +compiler. + - Leading zeroes in exponent parts of floats are permitted. - e.g. `1.0e+01`, `5e+05` + - [toml-lang/toml/PR/656](https://github.com/toml-lang/toml/pull/656) - Allow raw tab characters in basic strings and multi-line basic strings. + - [toml-lang/toml/PR/627](https://github.com/toml-lang/toml/pull/627) +- Allow heterogeneous arrays + - [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676) + +### Note about heterogeneous arrays + +Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value` +does not. + +```cpp +// this won't be compiled +toml::value v{ + "foo", 3.14, 42, {1,2,3,4,5}, {{"key", "value"}} +} +``` + +There is a workaround for this issue. By explicitly converting values into +`toml::value`, you can initialize `toml::value` with a heterogeneous array. + +```cpp +// OK! +toml::value v{ + toml::value("foo"), toml::value(3.14), toml::value(42), + toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} +} +``` + +The reason why the first example is not allowed is the following. +Let's assume that you are initializing a `toml::value` with a table. + +```cpp + // # expecting TOML table. +toml::value v{ // [v] + {"answer", 42}, // answer = 42 + {"pi", 3.14}, // pi = 3.14 + {"foo", "bar"} // foo = "bar" +}; +``` + +This is indistinguishable from a (heterogeneous) TOML array definition. + +```toml +v = [ + ["answer", 42], + ["pi", 3.14], + ["foo", "bar"], +] +``` + +This means that the above C++ code makes constructor's overload resolution +ambiguous. So a constructor that allows both "table as an initializer-list" and +"heterogeneous array as an initializer-list" cannot be implemented. + +Thus, although it is painful, you need to explicitly cast values into +`toml::value` when you initialize heterogeneous array in C++ code. + +```cpp +// You need to do this when you want to initialize hetero array. +toml::value v{ + toml::value("foo"), toml::value(3.14), toml::value(42), + toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} +} +``` ## Breaking Changes from v2 From c2d0351e6929a7cc6eeae4ff4c4a0cd59890d050 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Nov 2019 17:24:51 +0900 Subject: [PATCH 27/58] feat: add (ANSI) terminal colorize operators --- toml/color.hpp | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 toml/color.hpp diff --git a/toml/color.hpp b/toml/color.hpp new file mode 100644 index 0000000..514632b --- /dev/null +++ b/toml/color.hpp @@ -0,0 +1,78 @@ +#ifndef TOML11_COLOR_HPP +#define TOML11_COLOR_HPP +#include +#include + +namespace toml +{ + +// put ANSI escape sequence to ostream +namespace color_ansi +{ +namespace detail +{ +inline int colorize_index() +{ + static const int index = std::ios_base::xalloc(); + return index; +} +} // detail + +inline std::ostream& colorize(std::ostream& os) +{ + // by default, it is zero. + os.iword(detail::colorize_index()) = 1; + return os; +} +inline std::ostream& nocolorize(std::ostream& os) +{ + os.iword(detail::colorize_index()) = 0; + return os; +} +inline std::ostream& reset (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;} +inline std::ostream& bold (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;} +inline std::ostream& grey (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;} +inline std::ostream& red (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;} +inline std::ostream& green (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;} +inline std::ostream& yellow (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;} +inline std::ostream& blue (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;} +inline std::ostream& magenta(std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;} +inline std::ostream& cyan (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} +inline std::ostream& white (std::ostream& os) +{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} +} // color_ansi + +// do nothing. +namespace nocolor +{ +inline std::ostream& colorize (std::ostream& os) noexcept {return os;} +inline std::ostream& nocolorize(std::ostream& os) noexcept {return os;} +inline std::ostream& reset (std::ostream& os) noexcept {return os;} +inline std::ostream& bold (std::ostream& os) noexcept {return os;} +inline std::ostream& grey (std::ostream& os) noexcept {return os;} +inline std::ostream& red (std::ostream& os) noexcept {return os;} +inline std::ostream& green (std::ostream& os) noexcept {return os;} +inline std::ostream& yellow (std::ostream& os) noexcept {return os;} +inline std::ostream& blue (std::ostream& os) noexcept {return os;} +inline std::ostream& magenta (std::ostream& os) noexcept {return os;} +inline std::ostream& cyan (std::ostream& os) noexcept {return os;} +inline std::ostream& white (std::ostream& os) noexcept {return os;} +} // nocolor + +#ifdef TOML11_COLORIZE_ERROR_MESSAGE +namespace color = color_ansi; +#else +namespace color = nocolor; +#endif + +} // toml +#endif// TOML11_COLOR_HPP From 87bebbc37d00e20ee2e091204cae00d06d7fee4e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Nov 2019 17:25:17 +0900 Subject: [PATCH 28/58] feat: put color to the internal error messages --- toml/region.hpp | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index de6d04b..73fc076 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -9,6 +9,7 @@ #include #include #include +#include "color.hpp" namespace toml { @@ -432,7 +433,13 @@ inline std::string format_underline(const std::string& message, )->first->line_num().size()); std::ostringstream retval; - retval << message << '\n'; + +#ifdef TOML11_COLORIZE_ERROR_MESSAGE + retval << color::colorize; // turn on ANSI color +#endif + + retval << color::bold << color::red << "[error] " << color::reset + << color::bold << message << color::reset << '\n'; for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter) { @@ -440,34 +447,41 @@ inline std::string format_underline(const std::string& message, if(iter != reg_com.begin() && std::prev(iter)->first->name() == iter->first->name()) { - retval << "\n ...\n"; + retval << color::bold << color::blue << "\n ...\n" << color::reset; } else // if filename differs, print " --> filename.toml" { if(iter != reg_com.begin()) {retval << '\n';} - retval << " --> " << iter->first->name() << '\n'; + retval << color::bold << color::blue << " --> " << color::reset + << iter->first->name() << '\n'; + // add one almost-empty line for readability + retval << make_string(static_cast(line_num_width + 1), ' ') + << color::bold << color::blue << " | " << color::reset << '\n'; } const region_base* const reg = iter->first; const std::string& comment = iter->second; - retval << ' ' << std::setw(line_num_width) << reg->line_num(); - retval << " | " << reg->line() << '\n'; - retval << make_string(static_cast(line_num_width + 1), ' '); - retval << " | " << make_string(reg->before(), ' '); + retval << ' ' << std::setw(line_num_width) << color::bold << color::blue + << reg->line_num() << " | " << color::reset << reg->line() << '\n'; + + retval << make_string(static_cast(line_num_width + 1), ' ') + << color::bold << color::blue << " | " << color::reset + << make_string(reg->before(), ' '); if(reg->size() == 1) { // invalid // ^------ - retval << '^'; - retval << make_string(reg->after(), '-'); + retval << color::bold << color::red + << '^' << make_string(reg->after(), '-') << color::reset; } else { // invalid // ~~~~~~~ const auto underline_len = std::min(reg->size(), reg->line().size()); - retval << make_string(underline_len, '~'); + retval << color::bold << color::red + << make_string(underline_len, '~') << color::reset; } retval << ' '; retval << comment; @@ -477,10 +491,10 @@ inline std::string format_underline(const std::string& message, { retval << '\n'; retval << make_string(static_cast(line_num_width + 1), ' '); - retval << " | "; + retval << color::bold << color::blue << " | " << color::reset; for(const auto help : helps) { - retval << "\nHint: "; + retval << color::bold << "\nHint: " << color::reset; retval << help; } } From af116991b6e77435cf050b424776fc2de248946f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Nov 2019 17:35:23 +0900 Subject: [PATCH 29/58] fix: remove overlapping [error] sign --- toml/get.hpp | 20 +++--- toml/parser.hpp | 162 ++++++++++++++++++++++++------------------------ toml/value.hpp | 2 +- 3 files changed, 92 insertions(+), 92 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index 2023425..ce2ea54 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -186,7 +186,7 @@ get(const basic_value& v) } default: { - throw type_error(detail::format_underline("[error] toml::value " + throw type_error(detail::format_underline("toml::value: " "bad_cast to std::chrono::system_clock::time_point", { {std::addressof(detail::get_region(v)), concat_to_string("the actual type is ", v.type())} @@ -301,7 +301,7 @@ get(const basic_value& v) if(ar.size() != container.size()) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[erorr] toml::get specified container size is ", container.size(), + "toml::get: specified container size is ", container.size(), " but there are ", ar.size(), " elements in toml array."), { {std::addressof(detail::get_region(v)), "here"} })); @@ -326,7 +326,7 @@ get(const basic_value& v) if(ar.size() != 2) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[erorr] toml::get specified std::pair but there are ", ar.size(), + "toml::get: specified std::pair but there are ", ar.size(), " elements in toml array."), { {std::addressof(detail::get_region(v)), "here"} })); @@ -357,7 +357,7 @@ get(const basic_value& v) if(ar.size() != std::tuple_size::value) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] toml::get specified std::tuple with ", + "toml::get: specified std::tuple with ", std::tuple_size::value, " elements, but there are ", ar.size(), " elements in toml array."), { {std::addressof(detail::get_region(v)), "here"} @@ -430,7 +430,7 @@ basic_value const& find(const basic_value& v, const key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), { + "key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } @@ -444,7 +444,7 @@ basic_value& find(basic_value& v, const key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), { + "key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } @@ -458,7 +458,7 @@ basic_value find(basic_value&& v, const key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), { + "key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } @@ -477,7 +477,7 @@ find(const basic_value& v, const key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), { + "key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } @@ -493,7 +493,7 @@ find(basic_value& v, const key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), { + "key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } @@ -509,7 +509,7 @@ find(basic_value&& v, const key& ky) if(tab.count(ky) == 0) { throw std::out_of_range(detail::format_underline(concat_to_string( - "[error] key \"", ky, "\" not found"), { + "key \"", ky, "\" not found"), { {std::addressof(detail::get_region(v)), "in this table"} })); } diff --git a/toml/parser.hpp b/toml/parser.hpp index 0efccf0..be92143 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -30,13 +30,13 @@ parse_boolean(location& loc) else // internal error. { throw internal_error(format_underline( - "[error] toml::parse_boolean: internal error", + "toml::parse_boolean: internal error", {{std::addressof(reg), "invalid token"}}), source_location(std::addressof(reg))); } } loc.reset(first); //rollback - return err(format_underline("[error] toml::parse_boolean: ", + return err(format_underline("toml::parse_boolean: ", {{std::addressof(loc), "the next token is not a boolean"}})); } @@ -58,7 +58,7 @@ parse_binary_integer(location& loc) else // internal error. { throw internal_error(format_underline( - "[error] toml::parse_integer: internal error", + "toml::parse_integer: internal error", {{std::addressof(token.unwrap()), "invalid token"}}), source_location(std::addressof(loc))); } @@ -66,7 +66,7 @@ parse_binary_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); - return err(format_underline("[error] toml::parse_binary_integer:", + return err(format_underline("toml::parse_binary_integer:", {{std::addressof(loc), "the next token is not an integer"}})); } @@ -87,7 +87,7 @@ parse_octal_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); - return err(format_underline("[error] toml::parse_octal_integer:", + return err(format_underline("toml::parse_octal_integer:", {{std::addressof(loc), "the next token is not an integer"}})); } @@ -108,7 +108,7 @@ parse_hexadecimal_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); - return err(format_underline("[error] toml::parse_hexadecimal_integer", + return err(format_underline("toml::parse_hexadecimal_integer", {{std::addressof(loc), "the next token is not an integer"}})); } @@ -131,13 +131,13 @@ parse_integer(location& loc) if(std::isdigit(*second)) { - return err(format_underline("[error] toml::parse_integer: " + return err(format_underline("toml::parse_integer: " "leading zero in an Integer is not allowed.", {{std::addressof(loc), "leading zero"}})); } else if(std::isalpha(*second)) { - return err(format_underline("[error] toml::parse_integer: " + return err(format_underline("toml::parse_integer: " "unknown integer prefix appeared.", {{std::addressof(loc), "none of 0x, 0o, 0b"}})); } @@ -154,7 +154,7 @@ parse_integer(location& loc) return ok(std::make_pair(retval, token.unwrap())); } loc.reset(first); - return err(format_underline("[error] toml::parse_integer: ", + return err(format_underline("toml::parse_integer: ", {{std::addressof(loc), "the next token is not an integer"}})); } @@ -243,7 +243,7 @@ parse_floating(location& loc) return ok(std::make_pair(v, token.unwrap())); } loc.reset(first); - return err(format_underline("[error] toml::parse_floating: ", + return err(format_underline("toml::parse_floating: ", {{std::addressof(loc), "the next token is not a float"}})); } @@ -276,7 +276,7 @@ std::string read_utf8_codepoint(const region& reg, { if(0xD800 <= codepoint && codepoint <= 0xDFFF) { - throw syntax_error(format_underline("[error] " + throw syntax_error(format_underline( "toml::read_utf8_codepoint: codepoints in the range " "[0xD800, 0xDFFF] are not valid UTF-8.", {{ std::addressof(loc), "not a valid UTF-8 codepoint" @@ -298,7 +298,7 @@ std::string read_utf8_codepoint(const region& reg, } else // out of UTF-8 region { - throw syntax_error(format_underline("[error] toml::read_utf8_codepoint:" + throw syntax_error(format_underline("toml::read_utf8_codepoint:" " input codepoint is too large.", {{std::addressof(loc), "should be in [0x00..0x10FFFF]"}}), source_location(std::addressof(loc))); @@ -333,7 +333,7 @@ result parse_escape_sequence(location& loc) } else { - return err(format_underline("[error] parse_escape_sequence: " + return err(format_underline("parse_escape_sequence: " "invalid token found in UTF-8 codepoint uXXXX.", {{std::addressof(loc), "here"}})); } @@ -346,14 +346,14 @@ result parse_escape_sequence(location& loc) } else { - return err(format_underline("[error] parse_escape_sequence: " + return err(format_underline("parse_escape_sequence: " "invalid token found in UTF-8 codepoint Uxxxxxxxx", {{std::addressof(loc), "here"}})); } } } - const auto msg = format_underline("[error] parse_escape_sequence: " + const auto msg = format_underline("parse_escape_sequence: " "unknown escape sequence appeared.", {{std::addressof(loc), "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, /* Hints = */{"if you want to write backslash as just one backslash, " @@ -378,7 +378,7 @@ parse_ml_basic_string(location& loc) auto delim = lex_ml_basic_string_delim::invoke(inner_loc); if(!delim) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "parse_ml_basic_string: invalid token", {{std::addressof(inner_loc), "should be \"\"\""}}), source_location(std::addressof(inner_loc))); @@ -405,7 +405,7 @@ parse_ml_basic_string(location& loc) } if(inner_loc.iter() == inner_loc.end()) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "parse_ml_basic_string: unexpected end of region", {{std::addressof(inner_loc), "not sufficient token"}}), source_location(std::addressof(inner_loc))); @@ -417,7 +417,7 @@ parse_ml_basic_string(location& loc) else { loc.reset(first); - return err(format_underline("[error] toml::parse_ml_basic_string: " + return err(format_underline("toml::parse_ml_basic_string: " "the next token is not a valid multiline string", {{std::addressof(loc), "here"}})); } @@ -436,7 +436,7 @@ parse_basic_string(location& loc) auto quot = lex_quotation_mark::invoke(inner_loc); if(!quot) { - throw internal_error(format_underline("[error] parse_basic_string: " + throw internal_error(format_underline("parse_basic_string: " "invalid token", {{std::addressof(inner_loc), "should be \""}}), source_location(std::addressof(inner_loc))); } @@ -458,7 +458,7 @@ parse_basic_string(location& loc) } if(inner_loc.iter() == inner_loc.end()) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "parse_basic_string: unexpected end of region", {{std::addressof(inner_loc), "not sufficient token"}}), source_location(std::addressof(inner_loc))); @@ -470,7 +470,7 @@ parse_basic_string(location& loc) else { loc.reset(first); // rollback - return err(format_underline("[error] toml::parse_basic_string: " + return err(format_underline("toml::parse_basic_string: " "the next token is not a valid string", {{std::addressof(loc), "here"}})); } @@ -488,7 +488,7 @@ parse_ml_literal_string(location& loc) const auto open = lex_ml_literal_string_delim::invoke(inner_loc); if(!open) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "parse_ml_literal_string: invalid token", {{std::addressof(inner_loc), "should be '''"}}), source_location(std::addressof(inner_loc))); @@ -501,7 +501,7 @@ parse_ml_literal_string(location& loc) const auto close = lex_ml_literal_string_delim::invoke(inner_loc); if(!close) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "parse_ml_literal_string: invalid token", {{std::addressof(inner_loc), "should be '''"}}), source_location(std::addressof(inner_loc))); @@ -513,7 +513,7 @@ parse_ml_literal_string(location& loc) else { loc.reset(first); // rollback - return err(format_underline("[error] toml::parse_ml_literal_string: " + return err(format_underline("toml::parse_ml_literal_string: " "the next token is not a valid multiline literal string", {{std::addressof(loc), "here"}})); } @@ -531,7 +531,7 @@ parse_literal_string(location& loc) const auto open = lex_apostrophe::invoke(inner_loc); if(!open) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "parse_literal_string: invalid token", {{std::addressof(inner_loc), "should be '"}}), source_location(std::addressof(inner_loc))); @@ -542,7 +542,7 @@ parse_literal_string(location& loc) const auto close = lex_apostrophe::invoke(inner_loc); if(!close) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "parse_literal_string: invalid token", {{std::addressof(inner_loc), "should be '"}}), source_location(std::addressof(inner_loc))); @@ -554,7 +554,7 @@ parse_literal_string(location& loc) else { loc.reset(first); // rollback - return err(format_underline("[error] toml::parse_literal_string: " + return err(format_underline("toml::parse_literal_string: " "the next token is not a valid literal string", {{std::addressof(loc), "here"}})); } @@ -588,7 +588,7 @@ parse_string(location& loc) return parse_literal_string(loc); } } - return err(format_underline("[error] toml::parse_string: ", + return err(format_underline("toml::parse_string: ", {{std::addressof(loc), "the next token is not a string"}})); } @@ -836,7 +836,7 @@ parse_simple_key(location& loc) const auto reg = bare.unwrap(); return ok(std::make_pair(reg.str(), reg)); } - return err(format_underline("[error] toml::parse_simple_key: ", + return err(format_underline("toml::parse_simple_key: ", {{std::addressof(loc), "the next token is not a simple key"}})); } @@ -862,7 +862,7 @@ parse_key(location& loc) } else { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "toml::detail::parse_key: dotted key contains invalid key", {{std::addressof(inner_loc), k.unwrap_err()}}), source_location(std::addressof(inner_loc))); @@ -879,7 +879,7 @@ parse_key(location& loc) } else { - throw internal_error(format_underline("[error] toml::parse_key: " + throw internal_error(format_underline("toml::parse_key: " "dotted key contains invalid key ", {{std::addressof(inner_loc), "should be `.`"}}), source_location(std::addressof(inner_loc))); @@ -895,7 +895,7 @@ parse_key(location& loc) return ok(std::make_pair(std::vector(1, smpl.unwrap().first), smpl.unwrap().second)); } - return err(format_underline("[error] toml::parse_key: ", + return err(format_underline("toml::parse_key: ", {{std::addressof(loc), "is not a valid key"}})); } @@ -913,11 +913,11 @@ parse_array(location& loc) const auto first = loc.iter(); if(loc.iter() == loc.end()) { - return err("[error] toml::parse_array: input is empty"); + return err("toml::parse_array: input is empty"); } if(*loc.iter() != '[') { - return err("[error] toml::parse_array: token is not an array"); + return err("toml::parse_array: token is not an array"); } loc.advance(); @@ -944,7 +944,7 @@ parse_array(location& loc) auto array_start_loc = loc; array_start_loc.reset(first); - throw syntax_error(format_underline("[error] toml::parse_array: " + throw syntax_error(format_underline("toml::parse_array: " "type of elements should be the same each other.", { {std::addressof(array_start_loc), "array starts here"}, { @@ -965,7 +965,7 @@ parse_array(location& loc) auto array_start_loc = loc; array_start_loc.reset(first); - throw syntax_error(format_underline("[error] toml::parse_array: " + throw syntax_error(format_underline("toml::parse_array: " "value having invalid format appeared in an array", { {std::addressof(array_start_loc), "array starts here"}, {std::addressof(loc), "it is not a valid value."} @@ -988,7 +988,7 @@ parse_array(location& loc) auto array_start_loc = loc; array_start_loc.reset(first); - throw syntax_error(format_underline("[error] toml::parse_array:" + throw syntax_error(format_underline("toml::parse_array:" " missing array separator `,` after a value", { {std::addressof(array_start_loc), "array starts here"}, {std::addressof(loc), "should be `,`"} @@ -997,7 +997,7 @@ parse_array(location& loc) } } loc.reset(first); - throw syntax_error(format_underline("[error] toml::parse_array: " + throw syntax_error(format_underline("toml::parse_array: " "array did not closed by `]`", {{std::addressof(loc), "should be closed"}}), source_location(std::addressof(loc))); @@ -1019,7 +1019,7 @@ parse_key_value_pair(location& loc) if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) { loc.reset(first); - msg = format_underline("[error] toml::parse_key_value_pair: " + msg = format_underline("toml::parse_key_value_pair: " "empty key is not allowed.", {{std::addressof(loc), "key expected before '='"}}); } @@ -1035,7 +1035,7 @@ parse_key_value_pair(location& loc) const auto line_end = std::find(loc.iter(), loc.end(), '\n'); if(std::find(loc.iter(), line_end, '=') != line_end) { - msg = format_underline("[error] toml::parse_key_value_pair: " + msg = format_underline("toml::parse_key_value_pair: " "invalid format for key", {{std::addressof(loc), "invalid character in key"}}, {"Did you forget '.' to separate dotted-key?", @@ -1043,7 +1043,7 @@ parse_key_value_pair(location& loc) } else // if not, the error is lack of key-value separator. { - msg = format_underline("[error] toml::parse_key_value_pair: " + msg = format_underline("toml::parse_key_value_pair: " "missing key-value separator `=`", {{std::addressof(loc), "should be `=`"}}); } @@ -1061,7 +1061,7 @@ parse_key_value_pair(location& loc) if(sequence, maybe, lex_newline>::invoke(loc)) { loc.reset(after_kvsp); - msg = format_underline("[error] toml::parse_key_value_pair: " + msg = format_underline("toml::parse_key_value_pair: " "missing value after key-value separator '='", {{std::addressof(loc), "expected value, but got nothing"}}); } @@ -1196,7 +1196,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, { // show special err msg for conflicting table throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: array of table (\"", + "toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") cannot be defined"), { {std::addressof(get_region(tab->at(k))), @@ -1208,7 +1208,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, else if(!(tab->at(k).is_array())) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: array of table (\"", + "toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" " existing value"), { {std::addressof(get_region(tab->at(k))), @@ -1223,7 +1223,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, if(!(a.front().is_table())) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: array of table (\"", + "toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" " existing value"), { {std::addressof(get_region(tab->at(k))), @@ -1251,7 +1251,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, if(detail::get_region(a.front()).str().substr(0,2) != "[[") { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: array of table (\"", + "toml::insert_value: array of table (\"", format_dotted_keys(first, last), "\") collides with" " existing array-of-tables"), { {std::addressof(get_region(tab->at(k))), @@ -1280,7 +1280,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, tab->at(k), first, iter, last)) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: table (\"", + "toml::insert_value: table (\"", format_dotted_keys(first, last), "\") already exists."), { {std::addressof(get_region(tab->at(k))), @@ -1308,7 +1308,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, tab->at(k).as_array().front().is_table()) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: array of tables (\"", + "toml::insert_value: array of tables (\"", format_dotted_keys(first, last), "\") already exists."), { {std::addressof(get_region(tab->at(k))), "array of tables defined here"}, @@ -1319,7 +1319,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, else { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: value (\"", + "toml::insert_value: value (\"", format_dotted_keys(first, last), "\") already exists."), { {std::addressof(get_region(tab->at(k))), "value already exists here"}, @@ -1355,7 +1355,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, if(!a.back().is_table()) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: target (", + "toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), ") is neither table nor an array of tables"), { {std::addressof(get_region(a.back())), @@ -1368,7 +1368,7 @@ insert_nested_key(typename Value::table_type& root, const Value& v, else { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: target (", + "toml::insert_value: target (", format_dotted_keys(first, std::next(iter)), ") is neither table nor an array of tables"), { {std::addressof(get_region(tab->at(k))), @@ -1392,7 +1392,7 @@ parse_inline_table(location& loc) table_type retval; if(!(loc.iter() != loc.end() && *loc.iter() == '{')) { - return err(format_underline("[error] toml::parse_inline_table: ", + return err(format_underline("toml::parse_inline_table: ", {{std::addressof(loc), "the next token is not an inline table"}})); } loc.advance(); @@ -1421,7 +1421,7 @@ parse_inline_table(location& loc) insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg); if(!inserted) { - throw internal_error("[error] toml::parse_inline_table: " + throw internal_error("toml::parse_inline_table: " "failed to insert value into table: " + inserted.unwrap_err(), source_location(std::addressof(loc))); } @@ -1439,14 +1439,14 @@ parse_inline_table(location& loc) } else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n') { - throw syntax_error(format_underline("[error] " + throw syntax_error(format_underline( "toml::parse_inline_table: missing curly brace `}`", {{std::addressof(loc), "should be `}`"}}), source_location(std::addressof(loc))); } else { - throw syntax_error(format_underline("[error] " + throw syntax_error(format_underline( "toml::parse_inline_table: missing table separator `,` ", {{std::addressof(loc), "should be `,`"}}), source_location(std::addressof(loc))); @@ -1454,7 +1454,7 @@ parse_inline_table(location& loc) } } loc.reset(first); - throw syntax_error(format_underline("[error] toml::parse_inline_table: " + throw syntax_error(format_underline("toml::parse_inline_table: " "inline table did not closed by `}`", {{std::addressof(loc), "should be closed"}}), source_location(std::addressof(loc))); @@ -1480,7 +1480,7 @@ result guess_number_type(const location& l) if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-' || *loc.iter() == 'Z' || *loc.iter() == 'z')) { - return err(format_underline("[error] bad offset: should be [+-]HH:MM or Z", + return err(format_underline("bad offset: should be [+-]HH:MM or Z", {{std::addressof(loc), "[+-]HH:MM or Z"}}, {"pass: +09:00, -05:30", "fail: +9:00, -5:30"})); } @@ -1500,14 +1500,14 @@ result guess_number_type(const location& l) const auto c = *loc.iter(); if(c == 'T' || c == 't') { - return err(format_underline("[error] bad time: should be HH:MM:SS.subsec", + return err(format_underline("bad time: should be HH:MM:SS.subsec", {{std::addressof(loc), "HH:MM:SS.subsec"}}, {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", "fail: 1979-05-27T7:32:00, 1979-05-27 17:32"})); } if('0' <= c && c <= '9') { - return err(format_underline("[error] bad time: missing T", + return err(format_underline("bad time: missing T", {{std::addressof(loc), "T or space required here"}}, {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); @@ -1516,7 +1516,7 @@ result guess_number_type(const location& l) ('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9')) { loc.advance(); - return err(format_underline("[error] bad time: should be HH:MM:SS.subsec", + return err(format_underline("bad time: should be HH:MM:SS.subsec", {{std::addressof(loc), "HH:MM:SS.subsec"}}, {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); @@ -1533,7 +1533,7 @@ result guess_number_type(const location& l) { if(loc.iter() != loc.end() && *loc.iter() == '_') { - return err(format_underline("[error] bad float: `_` should be surrounded by digits", + return err(format_underline("bad float: `_` should be surrounded by digits", {{std::addressof(loc), "here"}}, {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); @@ -1549,7 +1549,7 @@ result guess_number_type(const location& l) const auto c = *loc.iter(); if(c == '_') { - return err(format_underline("[error] bad integer: `_` should be surrounded by digits", + return err(format_underline("bad integer: `_` should be surrounded by digits", {{std::addressof(loc), "here"}}, {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", "fail: 1__000, 0123"})); @@ -1558,21 +1558,21 @@ result guess_number_type(const location& l) { // leading zero. point '0' loc.retrace(); - return err(format_underline("[error] bad integer: leading zero", + return err(format_underline("bad integer: leading zero", {{std::addressof(loc), "here"}}, {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", "fail: 1__000, 0123"})); } if(c == ':' || c == '-') { - return err(format_underline("[error] bad datetime: invalid format", + return err(format_underline("bad datetime: invalid format", {{std::addressof(loc), "here"}}, {"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z", "fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"})); } if(c == '.' || c == 'e' || c == 'E') { - return err(format_underline("[error] bad float: invalid format", + return err(format_underline("bad float: invalid format", {{std::addressof(loc), "here"}}, {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); @@ -1582,19 +1582,19 @@ result guess_number_type(const location& l) } if(loc.iter() != loc.end() && *loc.iter() == '.') { - return err(format_underline("[error] bad float: invalid format", + return err(format_underline("bad float: invalid format", {{std::addressof(loc), "integer part required before this"}}, {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); } if(loc.iter() != loc.end() && *loc.iter() == '_') { - return err(format_underline("[error] bad number: `_` should be surrounded by digits", + return err(format_underline("bad number: `_` should be surrounded by digits", {{std::addressof(loc), "`_` is not surrounded by digits"}}, {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", "fail: 1__000, 0123"})); } - return err(format_underline("[error] bad format: unknown value appeared", + return err(format_underline("bad format: unknown value appeared", {{std::addressof(loc), "here"}})); } @@ -1623,7 +1623,7 @@ result parse_value(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return err(format_underline("[error] toml::parse_value: input is empty", + return err(format_underline("toml::parse_value: input is empty", {{std::addressof(loc), ""}})); } @@ -1646,7 +1646,7 @@ result parse_value(location& loc) case value_t::table : {return parse_inline_table(loc);} default: { - const auto msg = format_underline("[error] toml::parse_value: " + const auto msg = format_underline("toml::parse_value: " "unknown token appeared", {{std::addressof(loc), "unknown"}}); loc.reset(first); return err(msg); @@ -1665,7 +1665,7 @@ parse_table_key(location& loc) const auto open = lex_std_table_open::invoke(inner_loc); if(!open || inner_loc.iter() == inner_loc.end()) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "toml::parse_table_key: no `[`", {{std::addressof(inner_loc), "should be `[`"}}), source_location(std::addressof(inner_loc))); @@ -1676,7 +1676,7 @@ parse_table_key(location& loc) const auto keys = parse_key(inner_loc); if(!keys) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "toml::parse_table_key: invalid key", {{std::addressof(inner_loc), "not key"}}), source_location(std::addressof(inner_loc))); @@ -1687,7 +1687,7 @@ parse_table_key(location& loc) const auto close = lex_std_table_close::invoke(inner_loc); if(!close) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "toml::parse_table_key: no `]`", {{std::addressof(inner_loc), "should be `]`"}}), source_location(std::addressof(inner_loc))); @@ -1701,7 +1701,7 @@ parse_table_key(location& loc) const auto nl = lex_newline_after_table_key::invoke(loc); if(!nl) { - throw syntax_error(format_underline("[error] " + throw syntax_error(format_underline( "toml::parse_table_key: newline required after [table.key]", {{std::addressof(loc), "expected newline"}}), source_location(std::addressof(loc))); @@ -1711,7 +1711,7 @@ parse_table_key(location& loc) } else { - return err(format_underline("[error] toml::parse_table_key: " + return err(format_underline("toml::parse_table_key: " "not a valid table key", {{std::addressof(loc), "here"}})); } } @@ -1727,7 +1727,7 @@ parse_array_table_key(location& loc) const auto open = lex_array_table_open::invoke(inner_loc); if(!open || inner_loc.iter() == inner_loc.end()) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "toml::parse_array_table_key: no `[[`", {{std::addressof(inner_loc), "should be `[[`"}}), source_location(std::addressof(inner_loc))); @@ -1736,7 +1736,7 @@ parse_array_table_key(location& loc) const auto keys = parse_key(inner_loc); if(!keys) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "toml::parse_array_table_key: invalid key", {{std::addressof(inner_loc), "not a key"}}), source_location(std::addressof(inner_loc))); @@ -1745,7 +1745,7 @@ parse_array_table_key(location& loc) const auto close = lex_array_table_close::invoke(inner_loc); if(!close) { - throw internal_error(format_underline("[error] " + throw internal_error(format_underline( "toml::parse_table_key: no `]]`", {{std::addressof(inner_loc), "should be `]]`"}}), source_location(std::addressof(inner_loc))); @@ -1759,7 +1759,7 @@ parse_array_table_key(location& loc) const auto nl = lex_newline_after_table_key::invoke(loc); if(!nl) { - throw syntax_error(format_underline("[error] toml::" + throw syntax_error(format_underline("toml::" "parse_array_table_key: newline required after [[table.key]]", {{std::addressof(loc), "expected newline"}}), source_location(std::addressof(loc))); @@ -1769,7 +1769,7 @@ parse_array_table_key(location& loc) } else { - return err(format_underline("[error] toml::parse_array_table_key: " + return err(format_underline("toml::parse_array_table_key: " "not a valid table key", {{std::addressof(loc), "here"}})); } } @@ -1844,7 +1844,7 @@ parse_ml_table(location& loc) { const auto before2 = loc.iter(); lex_ws::invoke(loc); // skip whitespace - const auto msg = format_underline("[error] toml::parse_table: " + const auto msg = format_underline("toml::parse_table: " "invalid line format", {{std::addressof(loc), concat_to_string( "expected newline, but got '", show_char(*loc.iter()), "'.")}}); loc.reset(before2); diff --git a/toml/value.hpp b/toml/value.hpp index e2f5cf8..64f8b5c 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -32,7 +32,7 @@ template& v) { throw type_error(detail::format_underline(concat_to_string( - "[error] toml::value bad_cast to ", Expected), { + "toml::value: bad_cast to ", Expected), { {std::addressof(get_region(v)), concat_to_string("the actual type is ", actual)} }), v.location()); From 821eb9632bf674798d934c2d14cbe0d425df2863 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Nov 2019 17:59:47 +0900 Subject: [PATCH 30/58] feat: add a macro-dependent constant --- toml/color.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/toml/color.hpp b/toml/color.hpp index 514632b..747d0c6 100644 --- a/toml/color.hpp +++ b/toml/color.hpp @@ -3,6 +3,12 @@ #include #include +#ifdef TOML11_COLORIZE_ERROR_MESSAGE +#define TOML11_COLORED_MESSAGE_ACTIVATED true +#else +#define TOML11_COLORED_MESSAGE_ACTIVATED false +#endif + namespace toml { From d47174954fb5c42900dd5e1102e637303f097823 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Nov 2019 18:00:28 +0900 Subject: [PATCH 31/58] feat: colorize user-defined msg by format_error --- toml/value.hpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/toml/value.hpp b/toml/value.hpp index 64f8b5c..f0fc5ca 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -1843,25 +1843,27 @@ operator>=(const basic_value& lhs, const basic_value& rhs) template class T, template class A> inline std::string format_error(const std::string& err_msg, const basic_value& v, const std::string& comment, - std::vector hints = {}) + std::vector hints = {}, + const bool colorize = TOML11_COLORED_MESSAGE_ACTIVATED) { return detail::format_underline(err_msg, std::vector>{ {std::addressof(detail::get_region(v)), comment} - }, std::move(hints)); + }, std::move(hints), colorize); } template class T, template class A> inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, - std::vector hints = {}) + std::vector hints = {}, + const bool colorize = TOML11_COLORED_MESSAGE_ACTIVATED) { return detail::format_underline(err_msg, std::vector>{ {std::addressof(detail::get_region(v1)), comment1}, {std::addressof(detail::get_region(v2)), comment2} - }, std::move(hints)); + }, std::move(hints), colorize); } template class T, template class A> @@ -1869,14 +1871,15 @@ inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, const toml::basic_value& v3, const std::string& comment3, - std::vector hints = {}) + std::vector hints = {}, + const bool colorize = TOML11_COLORED_MESSAGE_ACTIVATED) { return detail::format_underline(err_msg, std::vector>{ {std::addressof(detail::get_region(v1)), comment1}, {std::addressof(detail::get_region(v2)), comment2}, {std::addressof(detail::get_region(v3)), comment3} - }, std::move(hints)); + }, std::move(hints), colorize); } template Date: Wed, 13 Nov 2019 18:01:47 +0900 Subject: [PATCH 32/58] feat: add runtime colorize flag --- toml/region.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 73fc076..7a55cfb 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -419,7 +419,8 @@ struct region final : public region_base // to show a better error message. inline std::string format_underline(const std::string& message, const std::vector>& reg_com, - const std::vector& helps = {}) + const std::vector& helps = {}, + const bool colorize = TOML11_COLORED_MESSAGE_ACTIVATED) { assert(!reg_com.empty()); @@ -434,9 +435,10 @@ inline std::string format_underline(const std::string& message, std::ostringstream retval; -#ifdef TOML11_COLORIZE_ERROR_MESSAGE - retval << color::colorize; // turn on ANSI color -#endif + if(colorize) + { + retval << color::colorize; // turn on ANSI color + } retval << color::bold << color::red << "[error] " << color::reset << color::bold << message << color::reset << '\n'; From bbe33e87d99470f2582c7ee1fe3713be61983d12 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Nov 2019 18:05:37 +0900 Subject: [PATCH 33/58] feat: detect [error] prefix duplication --- toml/region.hpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 7a55cfb..8169342 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -440,8 +440,21 @@ inline std::string format_underline(const std::string& message, retval << color::colorize; // turn on ANSI color } - retval << color::bold << color::red << "[error] " << color::reset - << color::bold << message << color::reset << '\n'; + // XXX + // Here, before `colorize` support, it does not output `[error] ` prefix + // automatically. So some user may output it manually and this change may + // duplicate the prefix. To avoid it, check the first 8 characters and + // if it is "[error] ", it removes that part from the message shown. + if(message.size() > 8 && message.substr(0, 8) == "[error] ") + { + retval << color::bold << color::red << "[error] " << color::reset + << color::bold << message.substr(8) << color::reset << '\n'; + } + else + { + retval << color::bold << color::red << "[error] " << color::reset + << color::bold << message << color::reset << '\n'; + } for(auto iter = reg_com.begin(); iter != reg_com.end(); ++iter) { From 571baa2c26f960ab3606765fedc68ad21d9583ce Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Nov 2019 18:08:31 +0900 Subject: [PATCH 34/58] refactor: remove nocolor:: operations since color can be on-off at runtime --- toml/color.hpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/toml/color.hpp b/toml/color.hpp index 747d0c6..479c26a 100644 --- a/toml/color.hpp +++ b/toml/color.hpp @@ -57,28 +57,8 @@ inline std::ostream& white (std::ostream& os) {if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} } // color_ansi -// do nothing. -namespace nocolor -{ -inline std::ostream& colorize (std::ostream& os) noexcept {return os;} -inline std::ostream& nocolorize(std::ostream& os) noexcept {return os;} -inline std::ostream& reset (std::ostream& os) noexcept {return os;} -inline std::ostream& bold (std::ostream& os) noexcept {return os;} -inline std::ostream& grey (std::ostream& os) noexcept {return os;} -inline std::ostream& red (std::ostream& os) noexcept {return os;} -inline std::ostream& green (std::ostream& os) noexcept {return os;} -inline std::ostream& yellow (std::ostream& os) noexcept {return os;} -inline std::ostream& blue (std::ostream& os) noexcept {return os;} -inline std::ostream& magenta (std::ostream& os) noexcept {return os;} -inline std::ostream& cyan (std::ostream& os) noexcept {return os;} -inline std::ostream& white (std::ostream& os) noexcept {return os;} -} // nocolor - -#ifdef TOML11_COLORIZE_ERROR_MESSAGE +// ANSI escape sequence is the only and default colorization method currently namespace color = color_ansi; -#else -namespace color = nocolor; -#endif } // toml #endif// TOML11_COLOR_HPP From f290390c630fa66713e0bcc35a3ee082134236b9 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 13 Nov 2019 18:14:42 +0900 Subject: [PATCH 35/58] fix: consider the no-space cases like [error]: --- toml/region.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 8169342..b6ba119 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -441,13 +441,13 @@ inline std::string format_underline(const std::string& message, } // XXX - // Here, before `colorize` support, it does not output `[error] ` prefix + // Here, before `colorize` support, it does not output `[error]` prefix // automatically. So some user may output it manually and this change may - // duplicate the prefix. To avoid it, check the first 8 characters and - // if it is "[error] ", it removes that part from the message shown. - if(message.size() > 8 && message.substr(0, 8) == "[error] ") + // duplicate the prefix. To avoid it, check the first 7 characters and + // if it is "[error]", it removes that part from the message shown. + if(message.size() > 7 && message.substr(0, 7) == "[error]") { - retval << color::bold << color::red << "[error] " << color::reset + retval << color::bold << color::red << "[error]" << color::reset << color::bold << message.substr(8) << color::reset << '\n'; } else From 6f7539dc6a5dd2c6172bf2d56b1ca69abdd55da6 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 14 Nov 2019 15:31:27 +0900 Subject: [PATCH 36/58] fix: deduplicate [error] prefix in the messages --- toml/parser.hpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index be92143..9e511ad 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -312,7 +312,7 @@ result parse_escape_sequence(location& loc) const auto first = loc.iter(); if(first == loc.end() || *first != '\\') { - return err(format_underline("[error]: toml::parse_escape_sequence: ", {{ + return err(format_underline("toml::parse_escape_sequence: ", {{ std::addressof(loc), "the next token is not a backslash \"\\\""}})); } loc.advance(); @@ -604,7 +604,7 @@ parse_local_date(location& loc) const auto y = lex_date_fullyear::invoke(inner_loc); if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_inner_local_date: invalid year format", {{std::addressof(inner_loc), "should be `-`"}}), source_location(std::addressof(inner_loc))); @@ -613,7 +613,7 @@ parse_local_date(location& loc) const auto m = lex_date_month::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_date: invalid month format", {{std::addressof(inner_loc), "should be `-`"}}), source_location(std::addressof(inner_loc))); @@ -622,7 +622,7 @@ parse_local_date(location& loc) const auto d = lex_date_mday::invoke(inner_loc); if(!d) { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_date: invalid day format", {{std::addressof(inner_loc), "here"}}), source_location(std::addressof(inner_loc))); @@ -637,7 +637,7 @@ parse_local_date(location& loc) else { loc.reset(first); - return err(format_underline("[error]: toml::parse_local_date: ", + return err(format_underline("toml::parse_local_date: ", {{std::addressof(loc), "the next token is not a local_date"}})); } } @@ -654,7 +654,7 @@ parse_local_time(location& loc) const auto h = lex_time_hour::invoke(inner_loc); if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_time: invalid year format", {{std::addressof(inner_loc), "should be `:`"}}), source_location(std::addressof(inner_loc))); @@ -663,7 +663,7 @@ parse_local_time(location& loc) const auto m = lex_time_minute::invoke(inner_loc); if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_time: invalid month format", {{std::addressof(inner_loc), "should be `:`"}}), source_location(std::addressof(inner_loc))); @@ -672,7 +672,7 @@ parse_local_time(location& loc) const auto s = lex_time_second::invoke(inner_loc); if(!s) { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_time: invalid second format", {{std::addressof(inner_loc), "here"}}), source_location(std::addressof(inner_loc))); @@ -709,7 +709,7 @@ parse_local_time(location& loc) { if(before_secfrac != inner_loc.iter()) { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_time: invalid subsecond format", {{std::addressof(inner_loc), "here"}}), source_location(std::addressof(inner_loc))); @@ -720,7 +720,7 @@ parse_local_time(location& loc) else { loc.reset(first); - return err(format_underline("[error]: toml::parse_local_time: ", + return err(format_underline("toml::parse_local_time: ", {{std::addressof(loc), "the next token is not a local_time"}})); } } @@ -736,7 +736,7 @@ parse_local_datetime(location& loc) const auto date = parse_local_date(inner_loc); if(!date || inner_loc.iter() == inner_loc.end()) { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_datetime: invalid datetime format", {{std::addressof(inner_loc), "date, not datetime"}}), source_location(std::addressof(inner_loc))); @@ -744,7 +744,7 @@ parse_local_datetime(location& loc) const char delim = *(inner_loc.iter()); if(delim != 'T' && delim != 't' && delim != ' ') { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_datetime: invalid datetime format", {{std::addressof(inner_loc), "should be `T` or ` ` (space)"}}), source_location(std::addressof(inner_loc))); @@ -753,7 +753,7 @@ parse_local_datetime(location& loc) const auto time = parse_local_time(inner_loc); if(!time) { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_local_datetime: invalid datetime format", {{std::addressof(inner_loc), "invalid time fomrat"}}), source_location(std::addressof(inner_loc))); @@ -765,7 +765,7 @@ parse_local_datetime(location& loc) else { loc.reset(first); - return err(format_underline("[error]: toml::parse_local_datetime: ", + return err(format_underline("toml::parse_local_datetime: ", {{std::addressof(loc), "the next token is not a local_datetime"}})); } } @@ -781,7 +781,7 @@ parse_offset_datetime(location& loc) const auto datetime = parse_local_datetime(inner_loc); if(!datetime || inner_loc.iter() == inner_loc.end()) { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_offset_datetime: invalid datetime format", {{std::addressof(inner_loc), "date, not datetime"}}), source_location(std::addressof(inner_loc))); @@ -803,7 +803,7 @@ parse_offset_datetime(location& loc) } else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') { - throw internal_error(format_underline("[error]: " + throw internal_error(format_underline( "toml::parse_offset_datetime: invalid datetime format", {{std::addressof(inner_loc), "should be `Z` or `+HH:MM`"}}), source_location(std::addressof(inner_loc))); @@ -814,7 +814,7 @@ parse_offset_datetime(location& loc) else { loc.reset(first); - return err(format_underline("[error]: toml::parse_offset_datetime: ", + return err(format_underline("toml::parse_offset_datetime: ", {{std::addressof(loc), "the next token is not a offset_datetime"}})); } } @@ -1958,7 +1958,7 @@ result parse_toml_file(location& loc) continue; } - return err(format_underline("[error]: toml::parse_toml_file: " + return err(format_underline("toml::parse_toml_file: " "unknown line appeared", {{std::addressof(loc), "unknown format"}})); } From 2fd466a3c34e7e07ab5234cfcf40010b2ef45d48 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 14 Nov 2019 15:32:17 +0900 Subject: [PATCH 37/58] fix: skip only the prefix, keep spaces --- toml/region.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/region.hpp b/toml/region.hpp index b6ba119..18c5248 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -448,7 +448,7 @@ inline std::string format_underline(const std::string& message, if(message.size() > 7 && message.substr(0, 7) == "[error]") { retval << color::bold << color::red << "[error]" << color::reset - << color::bold << message.substr(8) << color::reset << '\n'; + << color::bold << message.substr(7) << color::reset << '\n'; } else { From 7e62dad6dc12701d5633d1509451947e575f5f41 Mon Sep 17 00:00:00 2001 From: blockparty Date: Thu, 21 Nov 2019 05:51:31 -0600 Subject: [PATCH 38/58] Check if features are defined --- toml/datetime.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index dc52396..371f93b 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -20,7 +20,7 @@ namespace toml namespace detail { // TODO: find more sophisticated way to handle this -#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || _POSIX_SOURCE +#if _POSIX_C_SOURCE >= 1 || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) inline std::tm localtime_s(const std::time_t* src) { std::tm dst; From 6d41a1adb9a34d81c63945d49c568102d4906ef8 Mon Sep 17 00:00:00 2001 From: blockparty Date: Fri, 22 Nov 2019 05:59:55 -0600 Subject: [PATCH 39/58] Suppress unused variable warning --- toml/combinator.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/toml/combinator.hpp b/toml/combinator.hpp index 1c6ce25..aa82012 100644 --- a/toml/combinator.hpp +++ b/toml/combinator.hpp @@ -45,6 +45,7 @@ inline std::string show_char(const char c) buf.fill('\0'); const auto r = std::snprintf( buf.data(), buf.size(), "0x%02x", static_cast(c) & 0xFF); + (void) r; // Unused variable warning assert(r == static_cast(buf.size()) - 1); return std::string(buf.data()); } From 28519f571216eddbb914d4ae16893ed57b7fc26c Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 2 Dec 2019 17:08:00 +0900 Subject: [PATCH 40/58] doc: add colorize section to README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 56c3f3f..9486428 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ int main() - [Formatting user-defined error messages](#formatting-user-defined-error-messages) - [Obtaining location information](#obtaining-location-information) - [Exceptions](#exceptions) +- [Colorize Error Messages](#colorize-error-messages) - [Serializing TOML data](#serializing-toml-data) - [Underlying types](#underlying-types) - [Unreleased TOML features](#unreleased-toml-features) @@ -1315,6 +1316,14 @@ struct exception : public std::exception It represents where the error occurs. +## Colorize Error Messages + +By `#define TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from +`toml::parse` and `toml::find|get` will be colorized. + +Note that it is hard to see when the message is written in a file, not terminal, +because it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code). + ## Serializing TOML data toml11 enables you to serialize data into toml format. From 5a8d368927fab570a68f768ff027285f7c6403c1 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 6 Dec 2019 20:33:15 +0900 Subject: [PATCH 41/58] feat: add thread-safe detail::gmtime_s --- toml/datetime.hpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 371f93b..ba241b1 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -28,6 +28,13 @@ inline std::tm localtime_s(const std::time_t* src) if (!result) { throw std::runtime_error("localtime_r failed."); } return dst; } +inline std::tm gmtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::gmtime_r(src, &dst); + if (!result) { throw std::runtime_error("gmtime_r failed."); } + return dst; +} #elif _MSC_VER inline std::tm localtime_s(const std::time_t* src) { @@ -36,13 +43,26 @@ inline std::tm localtime_s(const std::time_t* src) if (result) { throw std::runtime_error("localtime_s failed."); } return dst; } -#else +inline std::tm gmtime_s(const std::time_t* src) +{ + std::tm dst; + const auto result = ::gmtime_s(&dst, src); + if (result) { throw std::runtime_error("gmtime_s failed."); } + return dst; +} +#else // fallback. not threadsafe inline std::tm localtime_s(const std::time_t* src) { const auto result = std::localtime(src); if (!result) { throw std::runtime_error("localtime failed."); } return *result; } +inline std::tm gmtime_s(const std::time_t* src) +{ + const auto result = std::gmtime(src); + if (!result) { throw std::runtime_error("gmtime failed."); } + return *result; +} #endif } // detail From 62c01f9826a496cc9691c996c83897b96c274775 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 6 Dec 2019 20:57:51 +0900 Subject: [PATCH 42/58] fix: consider timezone correctly explicitly set tm.tm_isdst = 0 and use UTC offset --- toml/datetime.hpp | 63 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index ba241b1..4be9f57 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -470,37 +470,64 @@ struct offset_datetime : date(ld.date), time(ld.time), offset(get_local_offset()) {} explicit offset_datetime(const std::chrono::system_clock::time_point& tp) - : offset_datetime(local_datetime(tp)) - {} + : offset(0, 0) // use gmtime + { + const auto timet = std::chrono::system_clock::to_time_t(tp); + const auto tm = detail::gmtime_s(&timet); + this->date = local_date(tm); + this->time = local_time(tm); + } explicit offset_datetime(const std::time_t& t) - : offset_datetime(local_datetime(t)) - {} + : offset(0, 0) // use gmtime + { + const auto tm = detail::gmtime_s(&t); + this->date = local_date(tm); + this->time = local_time(tm); + } explicit offset_datetime(const std::tm& t) - : offset_datetime(local_datetime(t)) - {} + : offset(0, 0) // assume gmtime + { + this->date = local_date(t); + this->time = local_time(t); + } operator std::chrono::system_clock::time_point() const { // get date-time using internal_duration = typename std::chrono::system_clock::time_point::duration; - std::chrono::system_clock::time_point tp = - std::chrono::system_clock::time_point(this->date) + - std::chrono::duration_cast( - std::chrono::nanoseconds(this->time)); - // get date-time in UTC. let's say we are in +09:00 (JPN). - // writing 12:00:00 in +09:00 means 03:00:00Z. to represent - // 12:00:00Z, first we need to add +09:00. + // std::mktime returns date as **local** time zone. + std::tm t; + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(this->date.year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = 0; // do not consider DST; explicitly turn it off. + // all the offset info, including DST, should be in the offset part. + + std::chrono::system_clock::time_point tp = + std::chrono::system_clock::from_time_t(std::mktime(&t)) + + std::chrono::duration_cast( + std::chrono::nanoseconds(this->time)); + + // get date-time in UTC. + // Since mktime uses local time zone, it should be corrected. + // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if + // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need + // to add `+09:00` to `03:00:00Z`. const auto ofs = get_local_offset(); tp += std::chrono::hours (ofs.hour); tp += std::chrono::minutes(ofs.minute); - // here, tp represents 12:00:00 in UTC but we have offset information. - // we need to subtract it. For example, let's say the input is - // 12:00:00-08:00. now we have tp = 12:00:00Z as a result of the above - // conversion. But the actual time we need to return is 20:00:00Z - // because of -08:00. + // We got `12:00:00Z` by correcting local timezone applied by mktime. + // Then we will apply the offset. Let's say `12:00:00-08:00` is given. + // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. + // So we need to subtract the offset. tp -= std::chrono::minutes(this->offset); return tp; } From 89714fb24b5f1ecc1756f555f8eed416dc7c2eb7 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 6 Dec 2019 21:15:31 +0900 Subject: [PATCH 43/58] doc: note about local timezone and datetime --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 56c3f3f..f2b0d2d 100644 --- a/README.md +++ b/README.md @@ -699,6 +699,7 @@ date information, but it can be converted to `std::chrono::duration` that represents a duration from the beginning of the day, `00:00:00.000`. ```toml +# sample.toml date = 2018-12-23 time = 12:30:00 l_dt = 2018-12-23T12:30:00 @@ -715,8 +716,12 @@ const auto o_dt = toml::get(data.at("o_dt const auto time = toml::get(data.at("time")); // 12 * 60 + 30 min ``` -toml11 defines its own datetime classes. -You can see the definitions in [toml/datetime.hpp](toml/datetime.hpp). +`local_date` and `local_datetime` are assumed to be in the local timezone when +they are converted into `time_point`. On the other hand, `offset_datetime` only +uses the offset part of the data and it does not take local timezone into account. + +To contain datetime data, toml11 defines its own datetime types. +For more detail, you can see the definitions in [toml/datetime.hpp](toml/datetime.hpp). ## Getting with a fallback @@ -1447,8 +1452,6 @@ This feature is introduced to make it easy to write a custom serializer. Because `std::chrono::system_clock::time_point` is a __time point__, not capable of representing a Local Time independent from a specific day. -It is recommended to get `datetime`s as `std::chrono` classes through `toml::get`. - ## Unreleased TOML features There are some unreleased features in toml-lang/toml:master. From b246f5ac5c5928055f6b3dbc12421c6a26c59fd1 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 8 Dec 2019 22:38:49 +1100 Subject: [PATCH 44/58] fix: combine date and time to convert loc datetime Normally DST begins at A.M. 3 or 4. If we re-use conversion operator of local_date and local_time independently, the conversion fails if it is the day when DST begins or ends. Since local_date considers the time is 00:00 A.M. and local_time does not consider DST because it does not have any date information. We need to consider both date and time information at the same time to convert it correctly. --- toml/datetime.hpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 4be9f57..6d609df 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -398,10 +398,31 @@ struct local_datetime { using internal_duration = typename std::chrono::system_clock::time_point::duration; + + // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator + // of local_date and local_time independently, the conversion fails if + // it is the day when DST begins or ends. Since local_date considers the + // time is 00:00 A.M. and local_time does not consider DST because it + // does not have any date information. We need to consider both date and + // time information at the same time to convert it correctly. + + std::tm t; + t.tm_sec = static_cast(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); + t.tm_mday = static_cast(this->date.day); + t.tm_mon = static_cast(this->date.month); + t.tm_year = static_cast(this->date.year) - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + // std::mktime returns date as local time zone. no conversion needed - auto dt = std::chrono::system_clock::time_point(this->date); + auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); dt += std::chrono::duration_cast( - std::chrono::nanoseconds(this->time)); + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); return dt; } From 331de4ea5d551f6979390158478825c110886f48 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 8 Dec 2019 22:44:12 +1100 Subject: [PATCH 45/58] fix: use datetime info while getting time offset to convert offset_datetime to system_clock::time_point. --- toml/datetime.hpp | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 6d609df..01b513c 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -488,7 +488,8 @@ struct offset_datetime : date(dt.date), time(dt.time), offset(o) {} explicit offset_datetime(const local_datetime& ld) - : date(ld.date), time(ld.time), offset(get_local_offset()) + : date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) + // use the current local timezone offset {} explicit offset_datetime(const std::chrono::system_clock::time_point& tp) : offset(0, 0) // use gmtime @@ -518,30 +519,33 @@ struct offset_datetime using internal_duration = typename std::chrono::system_clock::time_point::duration; - // std::mktime returns date as **local** time zone. + // first, convert it to local date-time information in the same way as + // local_datetime does. later we will use time_t to adjust time offset. std::tm t; - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; + t.tm_sec = static_cast(this->time.second); + t.tm_min = static_cast(this->time.minute); + t.tm_hour = static_cast(this->time.hour); t.tm_mday = static_cast(this->date.day); t.tm_mon = static_cast(this->date.month); t.tm_year = static_cast(this->date.year) - 1900; t.tm_wday = 0; // the value will be ignored t.tm_yday = 0; // the value will be ignored - t.tm_isdst = 0; // do not consider DST; explicitly turn it off. - // all the offset info, including DST, should be in the offset part. + t.tm_isdst = -1; + const std::time_t tp_loc = std::mktime(std::addressof(t)); - std::chrono::system_clock::time_point tp = - std::chrono::system_clock::from_time_t(std::mktime(&t)) + - std::chrono::duration_cast( - std::chrono::nanoseconds(this->time)); + auto tp = std::chrono::system_clock::from_time_t(tp_loc); + tp += std::chrono::duration_cast( + std::chrono::milliseconds(this->time.millisecond) + + std::chrono::microseconds(this->time.microsecond) + + std::chrono::nanoseconds (this->time.nanosecond)); - // get date-time in UTC. // Since mktime uses local time zone, it should be corrected. // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need // to add `+09:00` to `03:00:00Z`. - const auto ofs = get_local_offset(); + // Here, it uses the time_t converted from date-time info to handle + // daylight saving time. + const auto ofs = get_local_offset(std::addressof(tp_loc)); tp += std::chrono::hours (ofs.hour); tp += std::chrono::minutes(ofs.minute); @@ -568,11 +572,10 @@ struct offset_datetime private: - static time_offset get_local_offset() + static time_offset get_local_offset(const std::time_t* tp) { - // get current timezone - const auto tmp1 = std::time(nullptr); - const auto t = detail::localtime_s(&tmp1); + // get local timezone with the same date-time information as mktime + const auto t = detail::localtime_s(tp); std::array buf; const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 From 8fbeaabfd9cb2d64c66f8c1ec79b2837f6575c41 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 10 Dec 2019 00:00:05 +0900 Subject: [PATCH 46/58] feat: add operator[] to access table/array --- toml/value.hpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/toml/value.hpp b/toml/value.hpp index e2f5cf8..1b492a4 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -1579,6 +1579,14 @@ class basic_value { return this->as_table().at(k); } + value_type& operator[](const key& k) + { + if(this->is_uninitialized()) + { + *this = table_type{}; + } + return this->as_table()[k]; + } value_type& at(const std::size_t idx) { @@ -1589,6 +1597,15 @@ class basic_value return this->as_array().at(idx); } + value_type& operator[](const std::size_t idx) noexcept + { + return this->as_array(std::nothrow)[idx]; + } + value_type const& operator[](const std::size_t idx) const noexcept + { + return this->as_array(std::nothrow)[idx]; + } + source_location location() const { return source_location(this->region_info_.get()); From 0c084b3a5cfe352ffbbe14e6ca64bc34647a5ba6 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 10 Dec 2019 00:08:40 +0900 Subject: [PATCH 47/58] test: add test: accessing via bracket operator --- tests/test_value.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_value.cpp b/tests/test_value.cpp index 8c8c70c..c2f02db 100644 --- a/tests/test_value.cpp +++ b/tests/test_value.cpp @@ -934,3 +934,35 @@ BOOST_AUTO_TEST_CASE(test_value_at) BOOST_CHECK_THROW(v1.at(5), std::out_of_range); } } + +BOOST_AUTO_TEST_CASE(test_value_bracket) +{ + { + toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; + + BOOST_TEST(v1["foo"].as_integer() == 42); + BOOST_TEST(v1["bar"].as_floating() == 3.14); + BOOST_TEST(v1["baz"].as_string() == "qux"); + + v1["qux"] = 54; + BOOST_TEST(v1["qux"].as_integer() == 54); + } + { + toml::value v1; + v1["foo"] = 42; + + BOOST_TEST(v1.is_table()); + BOOST_TEST(v1["foo"].as_integer() == 42); + } + { + toml::value v1{1,2,3,4,5}; + + BOOST_TEST(v1[0].as_integer() == 1); + BOOST_TEST(v1[1].as_integer() == 2); + BOOST_TEST(v1[2].as_integer() == 3); + BOOST_TEST(v1[3].as_integer() == 4); + BOOST_TEST(v1[4].as_integer() == 5); + + BOOST_CHECK_THROW(v1["foo"], toml::type_error); + } +} From a41dc08025370c5a50b26d037574fa9d1b58b435 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 10 Dec 2019 20:06:01 +0900 Subject: [PATCH 48/58] doc: add document of operator[] --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index f2b0d2d..a198c92 100644 --- a/README.md +++ b/README.md @@ -477,6 +477,40 @@ Note that, although `std::string` has `at()` member function, `toml::value::at` throws if the contained type is a string. Because `std::string` does not contain `toml::value`. +### `operator[]` + +You can also access to the element of a table and an array by +`toml::basic_value::operator[]`. + +```cpp +const toml::value v{1,2,3,4,5}; +std::cout << v[2].as_integer() << std::endl; // 3 + +const toml::value v{{"foo", 42}, {"bar", 3.14}}; +std::cout << v["foo"].as_integer() << std::endl; // 42 +``` + +When you access to a `toml::value` that is not initialized yet via +`operator[](const std::string&)`, the `toml::value` will be a table, +just like the `std::map`. + +```cpp +toml::value v; // not initialized as a table. +v["foo"] = 42; // OK. `v` will be a table. +``` + +Contrary, if you access to a `toml::value` that contains an array via `operator[]`, +it does not check anything. It converts `toml::value` without type check and then +access to the n-th element without boundary check, just like the `std::vector::operator[]`. + +```cpp +toml::value v; // not initialized as an array +v[2] = 42; // error! UB +``` + +Please make sure that the `toml::value` has an array inside when you access to +its element via `operator[]`. + ## Checking value type You can check the type of a value by `is_xxx` function. From 3190c1da9fdfca865701f3b78c367836189afeb9 Mon Sep 17 00:00:00 2001 From: OGAWA KenIchi Date: Wed, 11 Dec 2019 17:47:16 +0900 Subject: [PATCH 49/58] fix: suppress warning on Intel C++ Compiler --- toml/traits.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/traits.hpp b/toml/traits.hpp index c0c5ee9..ec05f08 100644 --- a/toml/traits.hpp +++ b/toml/traits.hpp @@ -105,7 +105,7 @@ struct has_into_toml_method : decltype(has_into_toml_method_impl::check(nullptr)){}; #ifdef __INTEL_COMPILER -#undef decltype(...) +#undef decltype #endif // --------------------------------------------------------------------------- From 4688c235f5f248a695b32d27f60014cbb15be960 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 12 Dec 2019 17:38:29 +0900 Subject: [PATCH 50/58] refactor: rename internal macro value --- toml/color.hpp | 4 ++-- toml/region.hpp | 2 +- toml/value.hpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/toml/color.hpp b/toml/color.hpp index 479c26a..d85a848 100644 --- a/toml/color.hpp +++ b/toml/color.hpp @@ -4,9 +4,9 @@ #include #ifdef TOML11_COLORIZE_ERROR_MESSAGE -#define TOML11_COLORED_MESSAGE_ACTIVATED true +#define TOML11_ERROR_MESSAGE_COLORIZED true #else -#define TOML11_COLORED_MESSAGE_ACTIVATED false +#define TOML11_ERROR_MESSAGE_COLORIZED false #endif namespace toml diff --git a/toml/region.hpp b/toml/region.hpp index 18c5248..3c3f7c1 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -420,7 +420,7 @@ struct region final : public region_base inline std::string format_underline(const std::string& message, const std::vector>& reg_com, const std::vector& helps = {}, - const bool colorize = TOML11_COLORED_MESSAGE_ACTIVATED) + const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) { assert(!reg_com.empty()); diff --git a/toml/value.hpp b/toml/value.hpp index a9d2ba4..b5a4e86 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -1861,7 +1861,7 @@ template class T, template clas inline std::string format_error(const std::string& err_msg, const basic_value& v, const std::string& comment, std::vector hints = {}, - const bool colorize = TOML11_COLORED_MESSAGE_ACTIVATED) + const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) { return detail::format_underline(err_msg, std::vector>{ @@ -1874,7 +1874,7 @@ inline std::string format_error(const std::string& err_msg, const toml::basic_value& v1, const std::string& comment1, const toml::basic_value& v2, const std::string& comment2, std::vector hints = {}, - const bool colorize = TOML11_COLORED_MESSAGE_ACTIVATED) + const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) { return detail::format_underline(err_msg, std::vector>{ @@ -1889,7 +1889,7 @@ inline std::string format_error(const std::string& err_msg, const toml::basic_value& v2, const std::string& comment2, const toml::basic_value& v3, const std::string& comment3, std::vector hints = {}, - const bool colorize = TOML11_COLORED_MESSAGE_ACTIVATED) + const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) { return detail::format_underline(err_msg, std::vector>{ From d48d454a61a711c41c4dee4ed66c5d14e731f63f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 12 Dec 2019 17:55:20 +0900 Subject: [PATCH 51/58] doc: modify README a bit --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9bf0836..3db66b1 100644 --- a/README.md +++ b/README.md @@ -1357,10 +1357,11 @@ It represents where the error occurs. ## Colorize Error Messages -By `#define TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from -`toml::parse` and `toml::find|get` will be colorized. +By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from +`toml::parse` and `toml::find|get` will be colorized. By default, this feature +is turned off. -Note that it is hard to see when the message is written in a file, not terminal, +Note that the message would be messy when it is written to a file, not a terminal because it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code). ## Serializing TOML data From fecd50dfebf5365990e5cbba5e0a21ce3cc319ef Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 12 Dec 2019 18:02:16 +0900 Subject: [PATCH 52/58] doc: add contributors --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a198c92..4b67340 100644 --- a/README.md +++ b/README.md @@ -1635,6 +1635,11 @@ I appreciate the help of the contributors who introduced the great feature to th - Added installation script to CMake - J.C. Moyer (@jcmoyer) - Fixed an example code in the documentation +- Jt Freeman (@blockparty-sh) + - Fixed feature test macro around `localtime_s` + - Suppress warnings in Debug mode +- OGAWA Kenichi (@kenichiice) + - Suppress warnings on intel compiler ## Licensing terms From f31dc6ba37bcfdc230bad60fc5966861b1c1680d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 12 Dec 2019 18:09:13 +0900 Subject: [PATCH 53/58] doc: add example of hints in an error message --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 4b67340..da1ddda 100644 --- a/README.md +++ b/README.md @@ -1300,6 +1300,27 @@ you will get an error message like this. | ~~ maximum number here ``` +You can print hints at the end of the message. + +```cpp +std::vector hints; +hints.push_back("positive number means n >= 0."); +hints.push_back("negative number is not positive."); +std::cerr << toml::format_error("[error] value should be positive", + data.at("num"), "positive number required", hints) + << std::endl; +``` + +```console +[error] value should be positive + --> example.toml + 2 | num = 42 + | ~~ positive number required + | +Hint: positive number means n >= 0. +Hint: negative number is not positive. +``` + ## Obtaining location information You can also format error messages in your own way by using `source_location`. From f4ac286b0f9570b697b5e85c46d57b6e9c37b911 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 12 Dec 2019 21:31:21 +0900 Subject: [PATCH 54/58] doc: add description about format_error --- README.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3db66b1..b9d5d31 100644 --- a/README.md +++ b/README.md @@ -1361,8 +1361,23 @@ By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from `toml::parse` and `toml::find|get` will be colorized. By default, this feature is turned off. -Note that the message would be messy when it is written to a file, not a terminal -because it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code). +The message would be messy when it is written to a file, not a terminal because +it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code). + +Without `TOML11_COLORIZE_ERROR_MESSAGE`, you can still colorize user-defined +error message by passing `true` to the `toml::format_error` function. +If you define `TOML11_COLORIZE_ERROR_MESSAGE`, the value is `true` by default. +If not, the defalut value would be `false`. + +```cpp +std::cerr << toml::format_error("[error] value should be positive", + data.at("num"), "positive number required", + hints, /*colorize = */ true) << std::endl; +``` + +Note: It colorize `[error]` in red. That means that it detects `[error]` prefix +at the front of the error message. If there is no `[error]` prefix, +`format_error` adds it to the error message. ## Serializing TOML data From a945bd6eaceafc2897f2ad9d67655114154541a2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 13 Dec 2019 16:47:33 +0900 Subject: [PATCH 55/58] ci: pass TOML11_COLORIZE_ERROR_MESSAGE on CircleCI --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2db5ab3..0867ff4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ jobs: command: | g++ --version cd tests/ - g++ -std=c++11 -O2 -Wall -Wextra -Werror -I../ check_toml_test.cpp -o check_toml_test + g++ -std=c++11 -O2 -Wall -Wextra -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_toml_test.cpp -o check_toml_test go get github.com/BurntSushi/toml-test $GOPATH/bin/toml-test ./check_toml_test test_serialization: @@ -24,7 +24,7 @@ jobs: command: | g++ --version cd tests/ - g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check_serialization.cpp -o check_serialization + g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check_serialization.cpp -o check_serialization git clone https://github.com/BurntSushi/toml-test.git cp check_serialization toml-test/tests/valid cd toml-test/tests/valid @@ -47,7 +47,7 @@ jobs: command: | g++ --version cd tests/ - g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -I../ check.cpp -o check + g++ -std=c++11 -O2 -Wall -Wextra -Wpedantic -Werror -DTOML11_COLORIZE_ERROR_MESSAGE -I../ check.cpp -o check git clone https://github.com/BurntSushi/toml-test.git cp check toml-test/tests/invalid cp check toml-test/tests/valid From 08bf5ffbdfd8df51f61ba882114cc75b639713e8 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 13 Dec 2019 17:18:32 +0900 Subject: [PATCH 56/58] doc: put screenshot to colorize-error-message --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index b9d5d31..20537ba 100644 --- a/README.md +++ b/README.md @@ -1361,6 +1361,39 @@ By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from `toml::parse` and `toml::find|get` will be colorized. By default, this feature is turned off. +With the following toml file taken from `toml-lang/toml/tests/hard_example.toml`, + +```toml +[error] +array = [ + "This might most likely happen in multiline arrays", + Like here, + "or here, + and here" + ] End of array comment, forgot the # +``` + +the error message would be like this. + +![error-message-1](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-1.png) + +With the following, + +```toml +[error] +# array = [ +# "This might most likely happen in multiline arrays", +# Like here, +# "or here, +# and here" +# ] End of array comment, forgot the # +number = 3.14 pi <--again forgot the # +``` + +the error message would be like this. + +![error-message-2](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-2.png) + The message would be messy when it is written to a file, not a terminal because it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code). From 8567f09cbf36e60e1b1aefab7ef45d410e6b1198 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Fri, 13 Dec 2019 20:25:52 +0900 Subject: [PATCH 57/58] chore: update version info in CMake --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52a82e3..e0315eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,8 @@ enable_testing() project(toml11) set(toml11_VERSION_MAYOR 3) -set(toml11_VERSION_MINOR 0) -set(toml11_VERSION_PATCH 1) +set(toml11_VERSION_MINOR 2) +set(toml11_VERSION_PATCH 0) set(toml11_VERSION "${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}" ) From d3513c0f847981166dfaa8a00ffa26713d2ef144 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 17 Dec 2019 19:35:26 +0900 Subject: [PATCH 58/58] fix: fmt line num in err msg correctly --- toml/region.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 3c3f7c1..973bf48 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -476,8 +476,9 @@ inline std::string format_underline(const std::string& message, const region_base* const reg = iter->first; const std::string& comment = iter->second; - retval << ' ' << std::setw(line_num_width) << color::bold << color::blue - << reg->line_num() << " | " << color::reset << reg->line() << '\n'; + retval << ' ' << color::bold << color::blue << std::setw(line_num_width) + << std::right << reg->line_num() << " | " << color::reset + << reg->line() << '\n'; retval << make_string(static_cast(line_num_width + 1), ' ') << color::bold << color::blue << " | " << color::reset