Compare commits

..

73 Commits

Author SHA1 Message Date
ToruNiina
b1827e6fca test: check immutability of inline tables 2019-12-20 19:46:54 +09:00
ToruNiina
18f84088b4 perf: avoid tmp str construction while checking 2019-12-19 22:13:47 +09:00
ToruNiina
c199bd8b49 feat: enable to access the 1st char of region 2019-12-19 22:13:33 +09:00
ToruNiina
5b35c1a74e fix: prohibit modification on inline table
According to toml-lang/toml:36d3091b3 "Clarify that inline tables are
immutable", check if it adds key-value pair to an inline table.
  This is one of the unreleased (after-0.5.0) toml feature. But this is
marked as "Clarify", so TOML-lang intended that inline tables are
immutable in all version.
2019-12-19 22:02:17 +09:00
ToruNiina
d3513c0f84 fix: fmt line num in err msg correctly 2019-12-17 19:35:26 +09:00
ToruNiina
8567f09cbf chore: update version info in CMake 2019-12-13 20:25:52 +09:00
ToruNiina
a6d24b02d5 Merge branch 'colorize-err-msg' 2019-12-13 20:23:27 +09:00
ToruNiina
08bf5ffbdf doc: put screenshot to colorize-error-message 2019-12-13 17:28:43 +09:00
ToruNiina
a945bd6eac ci: pass TOML11_COLORIZE_ERROR_MESSAGE on CircleCI 2019-12-13 16:47:33 +09:00
ToruNiina
f4ac286b0f doc: add description about format_error 2019-12-12 21:31:21 +09:00
ToruNiina
f31dc6ba37 doc: add example of hints in an error message 2019-12-12 18:09:13 +09:00
ToruNiina
fecd50dfeb doc: add contributors 2019-12-12 18:02:16 +09:00
ToruNiina
d48d454a61 doc: modify README a bit 2019-12-12 17:55:20 +09:00
ToruNiina
4688c235f5 refactor: rename internal macro value 2019-12-12 17:38:29 +09:00
ToruNiina
5c146857a3 Merge branch 'master' into colorize-err-msg 2019-12-11 22:49:38 +09:00
ToruNiina
bc51699415 Merge branch 'bracket-operator' 2019-12-11 19:36:21 +09:00
Toru Niina
8b923d56e9 Merge pull request #96 from kenichiice/icpc_warning2
Suppress warning on Intel C++ Compiler
2019-12-11 18:30:44 +09:00
OGAWA KenIchi
3190c1da9f fix: suppress warning on Intel C++ Compiler 2019-12-11 17:47:16 +09:00
ToruNiina
a41dc08025 doc: add document of operator[] 2019-12-10 20:06:01 +09:00
ToruNiina
0c084b3a5c test: add test: accessing via bracket operator 2019-12-10 00:08:40 +09:00
ToruNiina
8fbeaabfd9 feat: add operator[] to access table/array 2019-12-10 00:00:05 +09:00
ToruNiina
331de4ea5d fix: use datetime info while getting time offset
to convert offset_datetime to system_clock::time_point.
2019-12-08 22:44:12 +11:00
ToruNiina
b246f5ac5c 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.
2019-12-08 22:38:49 +11:00
ToruNiina
89714fb24b doc: note about local timezone and datetime 2019-12-06 21:15:31 +09:00
ToruNiina
62c01f9826 fix: consider timezone correctly
explicitly set tm.tm_isdst = 0 and use UTC offset
2019-12-06 20:57:51 +09:00
ToruNiina
5a8d368927 feat: add thread-safe detail::gmtime_s 2019-12-06 20:33:15 +09:00
ToruNiina
28519f5712 doc: add colorize section to README 2019-12-02 17:08:00 +09:00
Toru Niina
63fdbd25cf Merge pull request #95 from blockparty-sh/unused-variable-warning
Suppress unused variable warning
2019-11-23 12:59:21 +09:00
blockparty
6d41a1adb9 Suppress unused variable warning 2019-11-22 05:59:55 -06:00
Toru Niina
26a09b2f65 Merge pull request #93 from blockparty-sh/fix_compile_error
Check if features are defined
2019-11-21 21:57:26 +09:00
blockparty
7e62dad6dc Check if features are defined 2019-11-21 05:51:31 -06:00
ToruNiina
2fd466a3c3 fix: skip only the prefix, keep spaces 2019-11-14 15:32:17 +09:00
ToruNiina
6f7539dc6a fix: deduplicate [error] prefix in the messages 2019-11-14 15:31:27 +09:00
ToruNiina
f290390c63 fix: consider the no-space cases like [error]: 2019-11-13 18:14:42 +09:00
ToruNiina
571baa2c26 refactor: remove nocolor:: operations
since color can be on-off at runtime
2019-11-13 18:08:31 +09:00
ToruNiina
bbe33e87d9 feat: detect [error] prefix duplication 2019-11-13 18:05:37 +09:00
ToruNiina
4c5076f263 feat: add runtime colorize flag 2019-11-13 18:01:47 +09:00
ToruNiina
d47174954f feat: colorize user-defined msg by format_error 2019-11-13 18:00:55 +09:00
ToruNiina
821eb9632b feat: add a macro-dependent constant 2019-11-13 17:59:47 +09:00
ToruNiina
af116991b6 fix: remove overlapping [error] sign 2019-11-13 17:35:23 +09:00
ToruNiina
87bebbc37d feat: put color to the internal error messages 2019-11-13 17:35:05 +09:00
ToruNiina
c2d0351e69 feat: add (ANSI) terminal colorize operators 2019-11-13 17:24:51 +09:00
ToruNiina
1526b9feee Merge branch 'heterogeneous-array' 2019-11-09 11:03:18 +09:00
ToruNiina
281206dcc6 doc: add heterogeneous array to README 2019-11-08 19:03:16 +09:00
ToruNiina
adf8fa9234 fix: fix typos in an error message in toml::get
when converting toml::value to std::tuple
2019-11-08 16:12:18 +09:00
ToruNiina
0a66be3257 test: add test for parsing heterogeneous array
this test case is activated only if TOML11_USE_UNRELEASED_TOML_FEATURES
is defined.
2019-11-08 15:52:31 +09:00
ToruNiina
160537360c test: deactivate error check for hetero array
if TOML11_USE_UNRELEASED_TOML_FEATURES is activated.
2019-11-08 15:39:14 +09:00
ToruNiina
9af2d65417 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.
2019-11-08 15:36:58 +09:00
ToruNiina
429763377f Merge branch 'add-src-loc-to-exception' #87 2019-11-02 13:42:14 +09:00
ToruNiina
c774beb79a doc: write about source_location in exceptions 2019-11-02 13:03:50 +09:00
ToruNiina
8240fffeca Merge branch 'master' into add-src-loc-to-exception 2019-11-02 12:53:16 +09:00
ToruNiina
047611764c fix: silly typo 2019-11-01 21:15:20 +09:00
ToruNiina
bc3eb9d967 feat: add src_loc to all the exceptions
including internal_error.
2019-11-01 21:14:33 +09:00
ToruNiina
6862264bde feat: use the first char as the top-level region
A bit related to #89
2019-11-01 20:47:25 +09:00
ToruNiina
2ee69fc420 fix: improve error messages about strings a bit 2019-11-01 13:34:15 +09:00
ToruNiina
6a15e8360f refactor: remove redundant namespace specifier 2019-10-31 23:13:49 +09:00
ToruNiina
b4c6d26842 Merge origin/add-src-loc-to-exception #87 2019-10-31 23:11:53 +09:00
ToruNiina
41eb1d6887 feat: pass source_location to exception 2019-10-31 22:23:31 +09:00
ToruNiina
3ca712a8da feat: check line_num before converting it to int 2019-10-31 22:21:24 +09:00
ToruNiina
8e589ff4d7 feat: add source_location to (syntax_|type_)error 2019-10-31 22:04:16 +09:00
ToruNiina
56812114c3 refactor: simplify inclusion dependencies 2019-10-31 21:58:28 +09:00
ToruNiina
f98615d0df fix: check file content is empty or not 2019-10-30 16:49:49 +09:00
ToruNiina
37769e28f0 fix #88: check if input is null-terminated or not 2019-10-30 16:33:22 +09:00
ToruNiina
2acdec00aa Merge branch 'refactor-ci' 2019-10-15 23:13:44 +09:00
ToruNiina
354cfc979a ci: cache brew directory 2019-10-15 23:13:11 +09:00
ToruNiina
3dc3b001ff ci: update clang 3.x from 3.7 to 3.9 2019-10-15 23:13:06 +09:00
ToruNiina
ea24a91f4c ci: use sourceline 2019-10-15 20:32:09 +09:00
ToruNiina
5bba73a8ca ci: use addons in OS X on Travis.CI 2019-10-15 20:27:27 +09:00
ToruNiina
54eced6c82 test: add test for toml::string::operator+= 2019-10-09 21:51:33 +09:00
ToruNiina
258e62f8f3 feat: add operator+= to toml::string 2019-10-09 21:51:14 +09:00
ToruNiina
06086a9ff7 doc: add note about value::at 2019-10-09 21:09:38 +09:00
ToruNiina
b4b35ea33e 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.
2019-10-08 23:23:53 +09:00
ToruNiina
d7b4d104d3 refactor: reduce checking; just check once 2019-10-08 23:15:03 +09:00
22 changed files with 951 additions and 289 deletions

View File

@@ -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

View File

@@ -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
@@ -89,16 +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:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.7
- 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,9 +107,9 @@ matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-4.0
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-4.0
packages:
- g++-8
- clang-4.0
@@ -121,9 +121,9 @@ matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-5.0
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-5.0
packages:
- g++-8
- clang-5.0
@@ -135,9 +135,9 @@ matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-6.0
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-6.0
packages:
- g++-8
- clang-6.0
@@ -149,9 +149,9 @@ matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-7
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-7
packages:
- g++-8
- clang-7
@@ -163,9 +163,9 @@ matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-8
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- g++-8
- clang-8
@@ -177,9 +177,9 @@ matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-8
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- g++-8
- clang-8
@@ -191,9 +191,9 @@ matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-8
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
@@ -205,9 +205,9 @@ matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-8
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'ppa:mhier/libboost-latest'
- llvm-toolchain-trusty-8
packages:
- clang-8
- g++-8
@@ -216,6 +216,15 @@ matrix:
language: cpp
compiler: clang
env: CXX_STANDARD=11
cache:
directories:
- $HOME/Library/Caches/Homebrew
addons:
homebrew:
update: true
packages:
- cmake
- boost
script:
- |
@@ -224,8 +233,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
@@ -234,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

View File

@@ -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}"
)

235
README.md
View File

@@ -68,6 +68,9 @@ 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)
- [Colorize Error Messages](#colorize-error-messages)
- [Serializing TOML data](#serializing-toml-data)
- [Underlying types](#underlying-types)
- [Unreleased TOML features](#unreleased-toml-features)
@@ -351,6 +354,7 @@ The above code works with the following toml file.
# NOT {"physical": {"color": "orange"}}.
```
## Casting a toml value
### `toml::get`
@@ -470,6 +474,44 @@ 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`.
### `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.
@@ -692,6 +734,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
@@ -708,8 +751,12 @@ const auto o_dt = toml::get<std::chrono::system_clock::time_point>(data.at("o_dt
const auto time = toml::get<std::chrono::minutes>(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
@@ -1254,7 +1301,28 @@ you will get an error message like this.
| ~~ maximum number here
```
### Obtaining location information
You can print hints at the end of the message.
```cpp
std::vector<std::string> 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`.
@@ -1279,6 +1347,92 @@ 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.
## Colorize Error Messages
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).
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
toml11 enables you to serialize data into toml format.
@@ -1411,17 +1565,83 @@ 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.
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
@@ -1494,6 +1714,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

View File

@@ -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)

View File

@@ -128,3 +128,47 @@ BOOST_AUTO_TEST_CASE(test_multiline_array_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::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<toml::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<toml::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<toml::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<toml::value>, "[\"foo\",\n 3.14,\n 42,\n [\"array\",\n \"of\",\n \"hetero-array\",\n 1],\n {key = \"value\"},\n]", toml::value(a));
}
#endif
}

View File

@@ -46,3 +46,19 @@ BOOST_AUTO_TEST_CASE(test_inline_table_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>, "{type.name = \"pug\"}", value(t));
}
}
BOOST_AUTO_TEST_CASE(test_inline_table_immutability)
{
{
std::istringstream stream(std::string(
"a = {b = 1}\n"
"a.c = 2\n"));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
}
{
std::istringstream stream(std::string(
"a = {b = {c = 1}}\n"
"a.b.d = 2\n"));
BOOST_CHECK_THROW(toml::parse(stream), toml::syntax_error);
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}

64
toml/color.hpp Normal file
View File

@@ -0,0 +1,64 @@
#ifndef TOML11_COLOR_HPP
#define TOML11_COLOR_HPP
#include <ostream>
#include <cstdint>
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
#define TOML11_ERROR_MESSAGE_COLORIZED true
#else
#define TOML11_ERROR_MESSAGE_COLORIZED false
#endif
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
// ANSI escape sequence is the only and default colorization method currently
namespace color = color_ansi;
} // toml
#endif// TOML11_COLOR_HPP

View File

@@ -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<int>(c) & 0xFF);
(void) r; // Unused variable warning
assert(r == static_cast<int>(buf.size()) - 1);
return std::string(buf.data());
}

View File

@@ -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;
@@ -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
@@ -378,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<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(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<internal_duration>(
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;
}
@@ -447,40 +488,71 @@ 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_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<internal_duration>(
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.
const auto ofs = get_local_offset();
// 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 = static_cast<int>(this->time.second);
t.tm_min = static_cast<int>(this->time.minute);
t.tm_hour = static_cast<int>(this->time.hour);
t.tm_mday = static_cast<int>(this->date.day);
t.tm_mon = static_cast<int>(this->date.month);
t.tm_year = static_cast<int>(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;
const std::time_t tp_loc = std::mktime(std::addressof(t));
auto tp = std::chrono::system_clock::from_time_t(tp_loc);
tp += std::chrono::duration_cast<internal_duration>(
std::chrono::milliseconds(this->time.millisecond) +
std::chrono::microseconds(this->time.microsecond) +
std::chrono::nanoseconds (this->time.nanosecond));
// 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`.
// 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);
// 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;
}
@@ -500,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<char, 6> buf;
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0

View File

@@ -2,6 +2,7 @@
// Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include "source_location.hpp"
#include <stdexcept>
#include <string>
@@ -11,15 +12,21 @@ 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) : 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)
: exception(loc), what_(what_arg)
{}
virtual ~syntax_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
@@ -30,8 +37,9 @@ struct syntax_error : public toml::exception
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)
: exception(loc), what_(what_arg)
{}
virtual ~type_error() noexcept override = default;
virtual const char* what() const noexcept override {return what_.c_str();}
@@ -42,10 +50,12 @@ struct type_error : public toml::exception
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, 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_;
};

View File

@@ -186,11 +186,11 @@ get(const basic_value<C, M, V>& 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())}
}));
}), v.location());
}
}
}
@@ -301,7 +301,7 @@ get(const basic_value<C, M, V>& 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<C, M, V>& 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,8 +357,8 @@ get(const basic_value<C, M, V>& v)
if(ar.size() != std::tuple_size<T>::value)
{
throw std::out_of_range(detail::format_underline(concat_to_string(
"[erorr] toml::get specified std::tuple with ",
std::tuple_size<T>::value, "elements, but there are ", ar.size(),
"toml::get: specified std::tuple with ",
std::tuple_size<T>::value, " elements, but there are ", ar.size(),
" elements in toml array."), {
{std::addressof(detail::get_region(v)), "here"}
}));
@@ -430,7 +430,7 @@ basic_value<C, M, V> const& find(const basic_value<C, M, V>& 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<C, M, V>& find(basic_value<C, M, V>& 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<C, M, V> find(basic_value<C, M, V>&& 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<C, M, V>& 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<C, M, V>& 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<C, M, V>&& 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"}
}));
}

View File

@@ -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)));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@
// Distributed under the MIT License.
#ifndef TOML11_REGION_HPP
#define TOML11_REGION_HPP
#include "exception.hpp"
#include <memory>
#include <vector>
#include <algorithm>
@@ -10,6 +9,7 @@
#include <iterator>
#include <iomanip>
#include <cassert>
#include "color.hpp"
namespace toml
{
@@ -41,6 +41,7 @@ struct region_base
region_base& operator=(region_base&& ) = default;
virtual bool is_ok() const noexcept {return false;}
virtual char front() const noexcept {return '\0';}
virtual std::string str() const {return std::string("unknown region");}
virtual std::string name() const {return std::string("unknown file");}
@@ -89,6 +90,7 @@ struct location final : public region_base
~location() = default;
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *iter_;}
// this const prohibits codes like `++(loc.iter())`.
const const_iterator iter() const noexcept {return iter_;}
@@ -231,16 +233,16 @@ 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;
}
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
char front() const noexcept override {return *first_;}
std::string str() const override {return make_string(first_, last_);}
std::string line() const override
@@ -420,7 +422,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<std::pair<region_base const*, std::string>>& reg_com,
const std::vector<std::string>& helps = {})
const std::vector<std::string>& helps = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
assert(!reg_com.empty());
@@ -434,7 +437,27 @@ inline std::string format_underline(const std::string& message,
)->first->line_num().size());
std::ostringstream retval;
retval << message << '\n';
if(colorize)
{
retval << color::colorize; // turn on ANSI color
}
// 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 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
<< color::bold << message.substr(7) << 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)
{
@@ -442,34 +465,42 @@ 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<std::size_t>(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<std::size_t>(line_num_width + 1), ' ');
retval << " | " << make_string(reg->before(), ' ');
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<std::size_t>(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;
@@ -479,10 +510,10 @@ inline std::string format_underline(const std::string& message,
{
retval << '\n';
retval << make_string(static_cast<std::size_t>(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;
}
}

View File

@@ -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;

View File

@@ -48,7 +48,11 @@ struct source_location
{
if(reg)
{
line_num_ = static_cast<std::uint_least32_t>(std::stoul(reg->line_num()));
if(reg->line_num() != detail::region_base().line_num())
{
line_num_ = static_cast<std::uint_least32_t>(
std::stoul(reg->line_num()));
}
column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1);
region_size_ = static_cast<std::uint_least32_t>(reg->size());
file_name_ = reg->name();

View File

@@ -2,6 +2,7 @@
// Distributed under the MIT License.
#ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP
#include <algorithm>
#include <string>
#include <cstdint>
#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;

View File

@@ -105,7 +105,7 @@ struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER
#undef decltype(...)
#undef decltype
#endif
// ---------------------------------------------------------------------------

View File

@@ -32,10 +32,10 @@ template<value_t Expected,
throw_bad_cast(value_t actual, const ::toml::basic_value<C, T, A>& 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());
}
// switch by `value_t` and call the corresponding `value::as_xxx()`. {{{
@@ -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());
@@ -1843,25 +1860,27 @@ operator>=(const basic_value<C, T, A>& lhs, const basic_value<C, T, A>& rhs)
template<typename C, template<typename ...> class T, template<typename ...> class A>
inline std::string format_error(const std::string& err_msg,
const basic_value<C, T, A>& v, const std::string& comment,
std::vector<std::string> hints = {})
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v)), comment}
}, std::move(hints));
}, std::move(hints), colorize);
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
inline std::string format_error(const std::string& err_msg,
const toml::basic_value<C, T, A>& v1, const std::string& comment1,
const toml::basic_value<C, T, A>& v2, const std::string& comment2,
std::vector<std::string> hints = {})
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{std::addressof(detail::get_region(v1)), comment1},
{std::addressof(detail::get_region(v2)), comment2}
}, std::move(hints));
}, std::move(hints), colorize);
}
template<typename C, template<typename ...> class T, template<typename ...> class A>
@@ -1869,14 +1888,15 @@ inline std::string format_error(const std::string& err_msg,
const toml::basic_value<C, T, A>& v1, const std::string& comment1,
const toml::basic_value<C, T, A>& v2, const std::string& comment2,
const toml::basic_value<C, T, A>& v3, const std::string& comment3,
std::vector<std::string> hints = {})
std::vector<std::string> hints = {},
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
{
return detail::format_underline(err_msg,
std::vector<std::pair<detail::region_base const*, std::string>>{
{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<typename Visitor, typename C,