mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-12-16 03:08:52 +08:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e3f8f9105 | ||
|
|
17a15d3c18 | ||
|
|
42cc111b05 | ||
|
|
5e0ee32854 | ||
|
|
2c5cc431fe | ||
|
|
970f7cb36a | ||
|
|
b924e70e3c | ||
|
|
7782258e68 | ||
|
|
08859c36d0 | ||
|
|
d3de136562 | ||
|
|
43183e2ad1 | ||
|
|
e9144b41fb | ||
|
|
2fb8793f1a | ||
|
|
6c8a53915a | ||
|
|
db2d33ca4b | ||
|
|
935da51769 | ||
|
|
be0d4bd0a9 | ||
|
|
9b472a6c72 | ||
|
|
1ead14589e | ||
|
|
b13065b1b5 | ||
|
|
a6581ee66b | ||
|
|
0dafa7ee42 | ||
|
|
908b91079b | ||
|
|
fce6ff317e | ||
|
|
fd50b11523 | ||
|
|
9090b8273c | ||
|
|
382e3dc3ab | ||
|
|
f7bfcdd7aa | ||
|
|
2e41a26785 | ||
|
|
f3378f0ac1 | ||
|
|
12ee73d6a9 | ||
|
|
503baf52ed | ||
|
|
2deb75052c | ||
|
|
290dca3d67 | ||
|
|
f283a257d2 | ||
|
|
3d86f3a4e1 | ||
|
|
dc5a8069a9 | ||
|
|
4f31b90665 | ||
|
|
5d8c573357 | ||
|
|
6e1e5ccd84 | ||
|
|
f2d9fd1d1f | ||
|
|
97c8cbdaf5 | ||
|
|
05ceb5ae79 | ||
|
|
96cfdb260a | ||
|
|
0fec125688 | ||
|
|
a6d38c1ec0 |
38
.github/workflows/main.yml
vendored
38
.github/workflows/main.yml
vendored
@@ -58,6 +58,8 @@ jobs:
|
|||||||
- {compiler: '3.9', standard: '20'}
|
- {compiler: '3.9', standard: '20'}
|
||||||
- {compiler: '4.0', standard: '20'}
|
- {compiler: '4.0', standard: '20'}
|
||||||
- {compiler: '5.0', standard: '20'}
|
- {compiler: '5.0', standard: '20'}
|
||||||
|
- {compiler: '6.0', standard: '20'}
|
||||||
|
- {compiler: '7', standard: '20'}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -76,13 +78,38 @@ jobs:
|
|||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
|
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_C_COMPILER=clang-${{ matrix.compiler }} -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd build && cmake --build .
|
cd build && cmake --build .
|
||||||
- name: Test
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
cd build && ctest --output-on-failure
|
cd build && ctest --output-on-failure
|
||||||
|
|
||||||
|
build-osx:
|
||||||
|
runs-on: macos-10.15
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
standard: ['11', '14', '17', '20']
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Install
|
||||||
|
run: |
|
||||||
|
brew install boost
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }}
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cd build && cmake --build .
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
cd build && ctest --output-on-failure
|
||||||
|
|
||||||
build-windows-msvc:
|
build-windows-msvc:
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
strategy:
|
strategy:
|
||||||
@@ -94,6 +121,13 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
- name: Install
|
||||||
|
run: |
|
||||||
|
(New-Object System.Net.WebClient).DownloadFile("https://github.com/actions/boost-versions/releases/download/1.72.0-20200608.4/boost-1.72.0-win32-msvc14.2-x86_64.tar.gz", "$env:TEMP\\boost.tar.gz")
|
||||||
|
7z.exe x "$env:TEMP\\boost.tar.gz" -o"$env:TEMP\\boostArchive" -y | Out-Null
|
||||||
|
7z.exe x "$env:TEMP\\boostArchive" -o"$env:TEMP\\boost" -y | Out-Null
|
||||||
|
Push-Location -Path "$env:TEMP\\boost"
|
||||||
|
Invoke-Expression .\\setup.ps1
|
||||||
- uses: ilammy/msvc-dev-cmd@v1
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
- name: Configure
|
- name: Configure
|
||||||
shell: cmd
|
shell: cmd
|
||||||
@@ -101,7 +135,7 @@ jobs:
|
|||||||
file --mime-encoding tests/test_literals.cpp
|
file --mime-encoding tests/test_literals.cpp
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ../ -G "NMake Makefiles" -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DBoost_ADDITIONAL_VERSIONS=1.72.0 -DBoost_USE_MULTITHREADED=ON -DBoost_ARCHITECTURE=-x64 -DBoost_NO_BOOST_CMAKE=ON -DBOOST_ROOT=%BOOST_ROOT_1_72_0%
|
cmake ../ -G "NMake Makefiles" -Dtoml11_BUILD_TEST=ON -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DBoost_NO_BOOST_CMAKE=ON -DBOOST_ROOT="C:\\hostedtoolcache\\windows\\Boost\\1.72.0\\x86_64"
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
project(toml11 VERSION 3.5.0)
|
project(toml11 VERSION 3.6.1)
|
||||||
|
|
||||||
option(toml11_BUILD_TEST "Build toml tests" OFF)
|
option(toml11_BUILD_TEST "Build toml tests" OFF)
|
||||||
option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF)
|
option(toml11_TEST_WITH_ASAN "use LLVM address sanitizer" OFF)
|
||||||
|
|||||||
45
README.md
45
README.md
@@ -11,7 +11,7 @@ toml11
|
|||||||
|
|
||||||
toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library.
|
toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library.
|
||||||
|
|
||||||
- It is compatible to the latest version of [TOML v1.0.0-rc.2](https://toml.io/en/v1.0.0-rc.2).
|
- It is compatible to the latest version of [TOML v1.0.0](https://toml.io/en/v1.0.0).
|
||||||
- It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
|
- It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test).
|
||||||
- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
|
- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11).
|
||||||
- It has configurable container. You can use any random-access containers and key-value maps as backend containers.
|
- It has configurable container. You can use any random-access containers and key-value maps as backend containers.
|
||||||
@@ -28,6 +28,10 @@ toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
// ```toml
|
||||||
|
// title = "an example toml file"
|
||||||
|
// nums = [3, 1, 4, 1, 5]
|
||||||
|
// ```
|
||||||
auto data = toml::parse("example.toml");
|
auto data = toml::parse("example.toml");
|
||||||
|
|
||||||
// find a value with the specified type from a table
|
// find a value with the specified type from a table
|
||||||
@@ -37,9 +41,9 @@ int main()
|
|||||||
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
|
std::vector<int> nums = toml::find<std::vector<int>>(data, "nums");
|
||||||
|
|
||||||
// access with STL-like manner
|
// access with STL-like manner
|
||||||
if(not data.at("a").contains("b"))
|
if(not data.contains("foo"))
|
||||||
{
|
{
|
||||||
data["a"]["b"] = "c";
|
data["foo"] = "bar";
|
||||||
}
|
}
|
||||||
|
|
||||||
// pass a fallback
|
// pass a fallback
|
||||||
@@ -1303,9 +1307,9 @@ struct foo
|
|||||||
double b;
|
double b;
|
||||||
std::string c;
|
std::string c;
|
||||||
|
|
||||||
toml::table into_toml() const // you need to mark it const.
|
toml::value into_toml() const // you need to mark it const.
|
||||||
{
|
{
|
||||||
return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}};
|
return toml::value{{"a", this->a}, {"b", this->b}, {"c", this->c}};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // ext
|
} // ext
|
||||||
@@ -1332,9 +1336,9 @@ namespace toml
|
|||||||
template<>
|
template<>
|
||||||
struct into<ext::foo>
|
struct into<ext::foo>
|
||||||
{
|
{
|
||||||
static toml::table into_toml(const ext::foo& f)
|
static toml::value into_toml(const ext::foo& f)
|
||||||
{
|
{
|
||||||
return toml::table{{"a", f.a}, {"b", f.b}, {"c", f.c}};
|
return toml::value{{"a", f.a}, {"b", f.b}, {"c", f.c}};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // toml
|
} // toml
|
||||||
@@ -1346,6 +1350,27 @@ toml::value v(f);
|
|||||||
Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and
|
Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and
|
||||||
`toml::array` are okay to return from `into_toml`.
|
`toml::array` are okay to return from `into_toml`.
|
||||||
|
|
||||||
|
You can also return a custom `toml::basic_value` from `toml::into`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace toml
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct into<ext::foo>
|
||||||
|
{
|
||||||
|
static toml::basic_value<toml::preserve_comments> into_toml(const ext::foo& f)
|
||||||
|
{
|
||||||
|
toml::basic_value<toml::preserve_comments> v{{"a", f.a}, {"b", f.b}, {"c", f.c}};
|
||||||
|
v.comments().push_back(" comment");
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // toml
|
||||||
|
```
|
||||||
|
|
||||||
|
But note that, if this `basic_value` would be assigned into other `toml::value`
|
||||||
|
that discards `comments`, the comments would be dropped.
|
||||||
|
|
||||||
## Formatting user-defined error messages
|
## Formatting user-defined error messages
|
||||||
|
|
||||||
When you encounter an error after you read the toml value, you may want to
|
When you encounter an error after you read the toml value, you may want to
|
||||||
@@ -1848,6 +1873,12 @@ I appreciate the help of the contributors who introduced the great feature to th
|
|||||||
- Fix include path in README
|
- Fix include path in README
|
||||||
- Mohammed Alyousef (@MoAlyousef)
|
- Mohammed Alyousef (@MoAlyousef)
|
||||||
- Made testing optional in CMake
|
- Made testing optional in CMake
|
||||||
|
- Ivan Shynkarenka (@chronoxor)
|
||||||
|
- Fix compilation error in `<filesystem>` with MinGW
|
||||||
|
- Alex Merry (@amerry)
|
||||||
|
- Add missing include files
|
||||||
|
- sneakypete81 (@sneakypete81)
|
||||||
|
- Fix typo in error message
|
||||||
|
|
||||||
|
|
||||||
## Licensing terms
|
## Licensing terms
|
||||||
|
|||||||
@@ -138,6 +138,43 @@ BOOST_AUTO_TEST_CASE(test_comment_both)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_comments_on_implicit_values)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const std::string file = R"(
|
||||||
|
# comment for the first element of array-of-tables.
|
||||||
|
[[array-of-tables]]
|
||||||
|
foo = "bar"
|
||||||
|
)";
|
||||||
|
std::istringstream iss(file);
|
||||||
|
const auto v = toml::parse<toml::preserve_comments>(iss);
|
||||||
|
|
||||||
|
const auto aot = toml::find(v, "array-of-tables");
|
||||||
|
const auto elm = aot.at(0);
|
||||||
|
BOOST_TEST(aot.comments().empty());
|
||||||
|
BOOST_TEST(elm.comments().size() == 1);
|
||||||
|
BOOST_TEST(elm.comments().front() == " comment for the first element of array-of-tables.");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string file = R"(
|
||||||
|
# comment for the array itself
|
||||||
|
array-of-tables = [
|
||||||
|
# comment for the first element of array-of-tables.
|
||||||
|
{foo = "bar"}
|
||||||
|
]
|
||||||
|
)";
|
||||||
|
std::istringstream iss(file);
|
||||||
|
const auto v = toml::parse<toml::preserve_comments>(iss);
|
||||||
|
|
||||||
|
const auto aot = toml::find(v, "array-of-tables");
|
||||||
|
const auto elm = aot.at(0);
|
||||||
|
BOOST_TEST(aot.comments().size() == 1);
|
||||||
|
BOOST_TEST(aot.comments().front() == " comment for the array itself");
|
||||||
|
BOOST_TEST(elm.comments().size() == 1);
|
||||||
|
BOOST_TEST(elm.comments().front() == " comment for the first element of array-of-tables.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_discard_comment)
|
BOOST_AUTO_TEST_CASE(test_discard_comment)
|
||||||
{
|
{
|
||||||
const std::string file = R"(
|
const std::string file = R"(
|
||||||
|
|||||||
@@ -70,9 +70,9 @@ struct from<extlib::foo>
|
|||||||
template<>
|
template<>
|
||||||
struct into<extlib::foo>
|
struct into<extlib::foo>
|
||||||
{
|
{
|
||||||
static toml::table into_toml(const extlib::foo& f)
|
static toml::value into_toml(const extlib::foo& f)
|
||||||
{
|
{
|
||||||
return toml::table{{"a", f.a}, {"b", f.b}};
|
return toml::value{{"a", f.a}, {"b", f.b}};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
template<typename Comment,
|
template<typename Comment,
|
||||||
template<typename ...> class Table,
|
template<typename ...> class Table,
|
||||||
@@ -303,3 +304,60 @@ BOOST_AUTO_TEST_CASE(test_format_key)
|
|||||||
BOOST_TEST("\"special-chars-\\\\-\\\"-\\b-\\f-\\r-\\n-\\t\"" == toml::format_key(key));
|
BOOST_TEST("\"special-chars-\\\\-\\\"-\\b-\\f-\\r-\\n-\\t\"" == toml::format_key(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In toml11, an implicitly-defined value does not have any comments.
|
||||||
|
// So, in the following file,
|
||||||
|
// ```toml
|
||||||
|
// # comment
|
||||||
|
// [[array-of-tables]]
|
||||||
|
// foo = "bar"
|
||||||
|
// ```
|
||||||
|
// The array named "array-of-tables" does not have the comment, but the first
|
||||||
|
// element of the array has. That means that, the above file is equivalent to
|
||||||
|
// the following.
|
||||||
|
// ```toml
|
||||||
|
// array-of-tables = [
|
||||||
|
// # comment
|
||||||
|
// {foo = "bar"},
|
||||||
|
// ]
|
||||||
|
// ```
|
||||||
|
// If the array itself has a comment (value_has_comment_ == true), we should try
|
||||||
|
// to make it inline.
|
||||||
|
// ```toml
|
||||||
|
// # comment about array
|
||||||
|
// array-of-tables = [
|
||||||
|
// # comment about table element
|
||||||
|
// {foo = "bar"}
|
||||||
|
// ]
|
||||||
|
// ```
|
||||||
|
// If it is formatted as a multiline table, the two comments becomes
|
||||||
|
// indistinguishable.
|
||||||
|
// ```toml
|
||||||
|
// # comment about array
|
||||||
|
// # comment about table element
|
||||||
|
// [[array-of-tables]]
|
||||||
|
// foo = "bar"
|
||||||
|
// ```
|
||||||
|
// So we need to try to make it inline, and it force-inlines regardless
|
||||||
|
// of the line width limit.
|
||||||
|
// It may fail if the element of a table has comment. In that case,
|
||||||
|
// the array-of-tables will be formatted as a multiline table.
|
||||||
|
BOOST_AUTO_TEST_CASE(test_distinguish_comment)
|
||||||
|
{
|
||||||
|
const std::string str = R"(# comment about array itself
|
||||||
|
array_of_table = [
|
||||||
|
# comment about the first element (table)
|
||||||
|
{key = "value"},
|
||||||
|
])";
|
||||||
|
std::istringstream iss(str);
|
||||||
|
const auto data = toml::parse<toml::preserve_comments>(iss);
|
||||||
|
const auto serialized = toml::format(data, /*width = */ 0);
|
||||||
|
|
||||||
|
std::istringstream reparse(serialized);
|
||||||
|
const auto parsed = toml::parse<toml::preserve_comments>(reparse);
|
||||||
|
|
||||||
|
BOOST_TEST(parsed.at("array_of_table").comments().size() == 1u);
|
||||||
|
BOOST_TEST(parsed.at("array_of_table").comments().front() == " comment about array itself");
|
||||||
|
BOOST_TEST(parsed.at("array_of_table").at(0).comments().size() == 1u);
|
||||||
|
BOOST_TEST(parsed.at("array_of_table").at(0).comments().front() == " comment about the first element (table)");
|
||||||
|
}
|
||||||
|
|||||||
4
toml.hpp
4
toml.hpp
@@ -34,8 +34,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TOML11_VERSION_MAJOR 3
|
#define TOML11_VERSION_MAJOR 3
|
||||||
#define TOML11_VERSION_MINOR 5
|
#define TOML11_VERSION_MINOR 6
|
||||||
#define TOML11_VERSION_PATCH 0
|
#define TOML11_VERSION_PATCH 1
|
||||||
|
|
||||||
#include "toml/parser.hpp"
|
#include "toml/parser.hpp"
|
||||||
#include "toml/literal.hpp"
|
#include "toml/literal.hpp"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#define TOML11_COMMENTS_HPP
|
#define TOML11_COMMENTS_HPP
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|||||||
@@ -338,8 +338,10 @@ get(const basic_value<C, M, V>& v)
|
|||||||
{v.location(), "here"}
|
{v.location(), "here"}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
std::transform(ar.cbegin(), ar.cend(), container.begin(),
|
for(std::size_t i=0; i<ar.size(); ++i)
|
||||||
[](const value& x){return ::toml::get<value_type>(x);});
|
{
|
||||||
|
container[i] = ::toml::get<value_type>(ar[i]);
|
||||||
|
}
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ inline namespace toml_literals
|
|||||||
{
|
{
|
||||||
|
|
||||||
// implementation
|
// implementation
|
||||||
inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
|
||||||
|
literal_internal_impl(::toml::detail::location loc)
|
||||||
{
|
{
|
||||||
|
using value_type = ::toml::basic_value<
|
||||||
|
::toml::discard_comments, std::unordered_map, std::vector>;
|
||||||
// if there are some comments or empty lines, skip them.
|
// if there are some comments or empty lines, skip them.
|
||||||
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
|
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
|
||||||
::toml::detail::maybe<::toml::detail::lex_ws>,
|
::toml::detail::maybe<::toml::detail::lex_ws>,
|
||||||
@@ -50,7 +53,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
|||||||
// If it is neither a table-key or a array-of-table-key, it may be a value.
|
// If it is neither a table-key or a array-of-table-key, it may be a value.
|
||||||
if(!is_table_key && !is_aots_key)
|
if(!is_table_key && !is_aots_key)
|
||||||
{
|
{
|
||||||
if(auto data = ::toml::detail::parse_value<::toml::value>(loc))
|
if(auto data = ::toml::detail::parse_value<value_type>(loc))
|
||||||
{
|
{
|
||||||
return data.unwrap();
|
return data.unwrap();
|
||||||
}
|
}
|
||||||
@@ -67,7 +70,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
|||||||
// It is a valid toml file.
|
// It is a valid toml file.
|
||||||
// It should be parsed as if we parse a file with this content.
|
// It should be parsed as if we parse a file with this content.
|
||||||
|
|
||||||
if(auto data = ::toml::detail::parse_toml_file<::toml::value>(loc))
|
if(auto data = ::toml::detail::parse_toml_file<value_type>(loc))
|
||||||
{
|
{
|
||||||
return data.unwrap();
|
return data.unwrap();
|
||||||
}
|
}
|
||||||
@@ -78,7 +81,8 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ::toml::value operator"" _toml(const char* str, std::size_t len)
|
inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
|
||||||
|
operator"" _toml(const char* str, std::size_t len)
|
||||||
{
|
{
|
||||||
::toml::detail::location loc(
|
::toml::detail::location loc(
|
||||||
std::string("TOML literal encoded in a C++ code"),
|
std::string("TOML literal encoded in a C++ code"),
|
||||||
@@ -91,7 +95,8 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
|
|||||||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||||
// value of u8"" literal has been changed from char to char8_t and char8_t is
|
// value of u8"" literal has been changed from char to char8_t and char8_t is
|
||||||
// NOT compatible to char
|
// NOT compatible to char
|
||||||
inline ::toml::value operator"" _toml(const char8_t* str, std::size_t len)
|
inline ::toml::basic_value<::toml::discard_comments, std::unordered_map, std::vector>
|
||||||
|
operator"" _toml(const char8_t* str, std::size_t len)
|
||||||
{
|
{
|
||||||
::toml::detail::location loc(
|
::toml::detail::location loc(
|
||||||
std::string("TOML literal encoded in a C++ code"),
|
std::string("TOML literal encoded in a C++ code"),
|
||||||
|
|||||||
@@ -13,12 +13,14 @@
|
|||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include "value.hpp"
|
#include "value.hpp"
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
#ifndef TOML11_DISABLE_STD_FILESYSTEM
|
||||||
|
#ifdef __cpp_lib_filesystem
|
||||||
#if __has_include(<filesystem>)
|
#if __has_include(<filesystem>)
|
||||||
#define TOML11_HAS_STD_FILESYSTEM
|
#define TOML11_HAS_STD_FILESYSTEM
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#endif // has_include(<string_view>)
|
#endif // has_include(<string_view>)
|
||||||
#endif // cplusplus >= C++17
|
#endif // __cpp_lib_filesystem
|
||||||
|
#endif // TOML11_DISABLE_STD_FILESYSTEM
|
||||||
|
|
||||||
namespace toml
|
namespace toml
|
||||||
{
|
{
|
||||||
@@ -930,7 +932,7 @@ parse_key(location& loc)
|
|||||||
return ok(std::make_pair(std::vector<key>(1, smpl.unwrap().first),
|
return ok(std::make_pair(std::vector<key>(1, smpl.unwrap().first),
|
||||||
smpl.unwrap().second));
|
smpl.unwrap().second));
|
||||||
}
|
}
|
||||||
return err(format_underline("toml::parse_key: an invalid key appeaed.",
|
return err(format_underline("toml::parse_key: an invalid key appeared.",
|
||||||
{{source_location(loc), "is not a valid key"}}, {
|
{{source_location(loc), "is not a valid key"}}, {
|
||||||
"bare keys : non-empty strings composed only of [A-Za-z0-9_-].",
|
"bare keys : non-empty strings composed only of [A-Za-z0-9_-].",
|
||||||
"quoted keys: same as \"basic strings\" or 'literal strings'.",
|
"quoted keys: same as \"basic strings\" or 'literal strings'.",
|
||||||
@@ -1315,7 +1317,41 @@ insert_nested_key(typename Value::table_type& root, const Value& v,
|
|||||||
}
|
}
|
||||||
else // if not, we need to create the array of table
|
else // if not, we need to create the array of table
|
||||||
{
|
{
|
||||||
value_type aot(array_type(1, v), key_reg);
|
// XXX: Consider the following array of tables.
|
||||||
|
// ```toml
|
||||||
|
// # This is a comment.
|
||||||
|
// [[aot]]
|
||||||
|
// foo = "bar"
|
||||||
|
// ```
|
||||||
|
// Here, the comment is for `aot`. But here, actually two
|
||||||
|
// values are defined. An array that contains tables, named
|
||||||
|
// `aot`, and the 0th element of the `aot`, `{foo = "bar"}`.
|
||||||
|
// Those two are different from each other. But both of them
|
||||||
|
// points to the same portion of the TOML file, `[[aot]]`,
|
||||||
|
// so `key_reg.comments()` returns `# This is a comment`.
|
||||||
|
// If it is assigned as a comment of `aot` defined here, the
|
||||||
|
// comment will be duplicated. Both the `aot` itself and
|
||||||
|
// the 0-th element will have the same comment. This causes
|
||||||
|
// "duplication of the same comments" bug when the data is
|
||||||
|
// serialized.
|
||||||
|
// Next, consider the following.
|
||||||
|
// ```toml
|
||||||
|
// # comment 1
|
||||||
|
// aot = [
|
||||||
|
// # coment 2
|
||||||
|
// {foo = "bar"},
|
||||||
|
// ]
|
||||||
|
// ```
|
||||||
|
// In this case, we can distinguish those two comments. So
|
||||||
|
// here we need to add "comment 1" to the `aot` and
|
||||||
|
// "comment 2" to the 0th element of that.
|
||||||
|
// To distinguish those two, we check the key region.
|
||||||
|
std::vector<std::string> comments{/* empty by default */};
|
||||||
|
if(key_reg.str().substr(0, 2) != "[[")
|
||||||
|
{
|
||||||
|
comments = key_reg.comments();
|
||||||
|
}
|
||||||
|
value_type aot(array_type(1, v), key_reg, std::move(comments));
|
||||||
tab->insert(std::make_pair(k, aot));
|
tab->insert(std::make_pair(k, aot));
|
||||||
return ok(true);
|
return ok(true);
|
||||||
}
|
}
|
||||||
@@ -1384,7 +1420,8 @@ insert_nested_key(typename Value::table_type& root, const Value& v,
|
|||||||
// [x.y.z]
|
// [x.y.z]
|
||||||
if(tab->count(k) == 0)
|
if(tab->count(k) == 0)
|
||||||
{
|
{
|
||||||
(*tab)[k] = value_type(table_type{}, key_reg);
|
// a table that is defined implicitly doesn't have any comments.
|
||||||
|
(*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/});
|
||||||
}
|
}
|
||||||
|
|
||||||
// type checking...
|
// type checking...
|
||||||
@@ -1674,11 +1711,24 @@ inline result<value_t, std::string> guess_value_type(const location& loc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Value, typename T>
|
||||||
|
result<Value, std::string>
|
||||||
|
parse_value_helper(result<std::pair<T, region>, std::string> rslt)
|
||||||
|
{
|
||||||
|
if(rslt.is_ok())
|
||||||
|
{
|
||||||
|
auto comments = rslt.as_ok().second.comments();
|
||||||
|
return ok(Value(std::move(rslt.as_ok()), std::move(comments)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return err(std::move(rslt.as_err()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Value>
|
template<typename Value>
|
||||||
result<Value, std::string> parse_value(location& loc)
|
result<Value, std::string> parse_value(location& loc)
|
||||||
{
|
{
|
||||||
using value_type = Value;
|
|
||||||
|
|
||||||
const auto first = loc.iter();
|
const auto first = loc.iter();
|
||||||
if(first == loc.end())
|
if(first == loc.end())
|
||||||
{
|
{
|
||||||
@@ -1691,18 +1741,19 @@ result<Value, std::string> parse_value(location& loc)
|
|||||||
{
|
{
|
||||||
return err(type.unwrap_err());
|
return err(type.unwrap_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(type.unwrap())
|
switch(type.unwrap())
|
||||||
{
|
{
|
||||||
case value_t::boolean : {return parse_boolean(loc); }
|
case value_t::boolean : {return parse_value_helper<Value>(parse_boolean(loc) );}
|
||||||
case value_t::integer : {return parse_integer(loc); }
|
case value_t::integer : {return parse_value_helper<Value>(parse_integer(loc) );}
|
||||||
case value_t::floating : {return parse_floating(loc); }
|
case value_t::floating : {return parse_value_helper<Value>(parse_floating(loc) );}
|
||||||
case value_t::string : {return parse_string(loc); }
|
case value_t::string : {return parse_value_helper<Value>(parse_string(loc) );}
|
||||||
case value_t::offset_datetime: {return parse_offset_datetime(loc);}
|
case value_t::offset_datetime: {return parse_value_helper<Value>(parse_offset_datetime(loc) );}
|
||||||
case value_t::local_datetime : {return parse_local_datetime(loc); }
|
case value_t::local_datetime : {return parse_value_helper<Value>(parse_local_datetime(loc) );}
|
||||||
case value_t::local_date : {return parse_local_date(loc); }
|
case value_t::local_date : {return parse_value_helper<Value>(parse_local_date(loc) );}
|
||||||
case value_t::local_time : {return parse_local_time(loc); }
|
case value_t::local_time : {return parse_value_helper<Value>(parse_local_time(loc) );}
|
||||||
case value_t::array : {return parse_array<value_type>(loc); }
|
case value_t::array : {return parse_value_helper<Value>(parse_array<Value>(loc) );}
|
||||||
case value_t::table : {return parse_inline_table<value_type>(loc);}
|
case value_t::table : {return parse_value_helper<Value>(parse_inline_table<Value>(loc));}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
const auto msg = format_underline("toml::parse_value: "
|
const auto msg = format_underline("toml::parse_value: "
|
||||||
@@ -1993,7 +2044,7 @@ result<Value, std::string> parse_toml_file(location& loc)
|
|||||||
const auto& reg = tk.second;
|
const auto& reg = tk.second;
|
||||||
|
|
||||||
const auto inserted = insert_nested_key(data,
|
const auto inserted = insert_nested_key(data,
|
||||||
value_type(tab.unwrap(), reg),
|
value_type(tab.unwrap(), reg, reg.comments()),
|
||||||
keys.begin(), keys.end(), reg,
|
keys.begin(), keys.end(), reg,
|
||||||
/*is_array_of_table=*/ true);
|
/*is_array_of_table=*/ true);
|
||||||
if(!inserted) {return err(inserted.unwrap_err());}
|
if(!inserted) {return err(inserted.unwrap_err());}
|
||||||
@@ -2010,7 +2061,8 @@ result<Value, std::string> parse_toml_file(location& loc)
|
|||||||
const auto& reg = tk.second;
|
const auto& reg = tk.second;
|
||||||
|
|
||||||
const auto inserted = insert_nested_key(data,
|
const auto inserted = insert_nested_key(data,
|
||||||
value_type(tab.unwrap(), reg), keys.begin(), keys.end(), reg);
|
value_type(tab.unwrap(), reg, reg.comments()),
|
||||||
|
keys.begin(), keys.end(), reg);
|
||||||
if(!inserted) {return err(inserted.unwrap_err());}
|
if(!inserted) {return err(inserted.unwrap_err());}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -2019,10 +2071,7 @@ result<Value, std::string> parse_toml_file(location& loc)
|
|||||||
"unknown line appeared", {{source_location(loc), "unknown format"}}));
|
"unknown line appeared", {{source_location(loc), "unknown format"}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value v(std::move(data), file);
|
return ok(Value(std::move(data), file, comments));
|
||||||
v.comments() = comments;
|
|
||||||
|
|
||||||
return ok(std::move(v));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ struct region final : public region_base
|
|||||||
{
|
{
|
||||||
// unwrap the first '#' by std::next.
|
// unwrap the first '#' by std::next.
|
||||||
auto str = make_string(std::next(comment_found), iter);
|
auto str = make_string(std::next(comment_found), iter);
|
||||||
if(str.back() == '\r') {str.pop_back();}
|
if(!str.empty() && str.back() == '\r') {str.pop_back();}
|
||||||
com.push_back(std::move(str));
|
com.push_back(std::move(str));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -397,7 +397,7 @@ struct region final : public region_base
|
|||||||
{
|
{
|
||||||
// unwrap the first '#' by std::next.
|
// unwrap the first '#' by std::next.
|
||||||
auto str = make_string(std::next(comment_found), this->line_end());
|
auto str = make_string(std::next(comment_found), this->line_end());
|
||||||
if(str.back() == '\r') {str.pop_back();}
|
if(!str.empty() && str.back() == '\r') {str.pop_back();}
|
||||||
com.push_back(std::move(str));
|
com.push_back(std::move(str));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,8 +97,10 @@ struct serializer
|
|||||||
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
|
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
|
||||||
const bool can_be_inlined = false,
|
const bool can_be_inlined = false,
|
||||||
const bool no_comment = false,
|
const bool no_comment = false,
|
||||||
std::vector<toml::key> ks = {})
|
std::vector<toml::key> ks = {},
|
||||||
|
const bool value_has_comment = false)
|
||||||
: can_be_inlined_(can_be_inlined), no_comment_(no_comment),
|
: can_be_inlined_(can_be_inlined), no_comment_(no_comment),
|
||||||
|
value_has_comment_(value_has_comment && !no_comment),
|
||||||
float_prec_(float_prec), width_(w), keys_(std::move(ks))
|
float_prec_(float_prec), width_(w), keys_(std::move(ks))
|
||||||
{}
|
{}
|
||||||
~serializer() = default;
|
~serializer() = default;
|
||||||
@@ -120,7 +122,7 @@ struct serializer
|
|||||||
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
|
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
|
||||||
|
|
||||||
std::string token(buf.begin(), std::prev(buf.end()));
|
std::string token(buf.begin(), std::prev(buf.end()));
|
||||||
if(token.back() == '.') // 1. => 1.0
|
if(!token.empty() && token.back() == '.') // 1. => 1.0
|
||||||
{
|
{
|
||||||
token += '0';
|
token += '0';
|
||||||
}
|
}
|
||||||
@@ -244,88 +246,14 @@ struct serializer
|
|||||||
|
|
||||||
std::string operator()(const array_type& v) const
|
std::string operator()(const array_type& v) const
|
||||||
{
|
{
|
||||||
if(!v.empty() && v.front().is_table())// v is an array of tables
|
|
||||||
{
|
|
||||||
// if it's not inlined, we need to add `[[table.key]]`.
|
|
||||||
// but if it can be inlined,
|
|
||||||
// ```
|
|
||||||
// table.key = [
|
|
||||||
// {...},
|
|
||||||
// # comment
|
|
||||||
// {...},
|
|
||||||
// ]
|
|
||||||
// ```
|
|
||||||
if(this->can_be_inlined_)
|
|
||||||
{
|
|
||||||
std::string token;
|
|
||||||
if(!keys_.empty())
|
|
||||||
{
|
|
||||||
token += format_key(keys_.back());
|
|
||||||
token += " = ";
|
|
||||||
}
|
|
||||||
bool failed = false;
|
|
||||||
token += "[\n";
|
|
||||||
for(const auto& item : v)
|
|
||||||
{
|
|
||||||
// if an element of the table has a comment, the table
|
|
||||||
// cannot be inlined.
|
|
||||||
if(this->has_comment_inside(item.as_table()))
|
|
||||||
{
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!no_comment_)
|
|
||||||
{
|
|
||||||
for(const auto& c : item.comments())
|
|
||||||
{
|
|
||||||
token += '#';
|
|
||||||
token += c;
|
|
||||||
token += '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto t = this->make_inline_table(item.as_table());
|
|
||||||
|
|
||||||
if(t.size() + 1 > width_ || // +1 for the last comma {...},
|
|
||||||
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
|
|
||||||
{
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
token += t;
|
|
||||||
token += ",\n";
|
|
||||||
}
|
|
||||||
if(!failed)
|
|
||||||
{
|
|
||||||
token += "]\n";
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
// if failed, serialize them as [[array.of.tables]].
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string token;
|
|
||||||
for(const auto& item : v)
|
|
||||||
{
|
|
||||||
if(!no_comment_)
|
|
||||||
{
|
|
||||||
for(const auto& c : item.comments())
|
|
||||||
{
|
|
||||||
token += '#';
|
|
||||||
token += c;
|
|
||||||
token += '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token += "[[";
|
|
||||||
token += format_keys(keys_);
|
|
||||||
token += "]]\n";
|
|
||||||
token += this->make_multiline_table(item.as_table());
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
if(v.empty())
|
if(v.empty())
|
||||||
{
|
{
|
||||||
return std::string("[]");
|
return std::string("[]");
|
||||||
}
|
}
|
||||||
|
if(this->is_array_of_tables(v))
|
||||||
|
{
|
||||||
|
return make_array_of_tables(v);
|
||||||
|
}
|
||||||
|
|
||||||
// not an array of tables. normal array.
|
// not an array of tables. normal array.
|
||||||
// first, try to make it inline if none of the elements have a comment.
|
// first, try to make it inline if none of the elements have a comment.
|
||||||
@@ -376,7 +304,7 @@ struct serializer
|
|||||||
token += '\n';
|
token += '\n';
|
||||||
}
|
}
|
||||||
token += toml::visit(*this, item);
|
token += toml::visit(*this, item);
|
||||||
if(token.back() == '\n') {token.pop_back();}
|
if(!token.empty() && token.back() == '\n') {token.pop_back();}
|
||||||
token += ",\n";
|
token += ",\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -384,7 +312,7 @@ struct serializer
|
|||||||
next_elem += toml::visit(*this, item);
|
next_elem += toml::visit(*this, item);
|
||||||
|
|
||||||
// comma before newline.
|
// comma before newline.
|
||||||
if(next_elem.back() == '\n') {next_elem.pop_back();}
|
if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();}
|
||||||
|
|
||||||
// if current line does not exceeds the width limit, continue.
|
// if current line does not exceeds the width limit, continue.
|
||||||
if(current_line.size() + next_elem.size() + 1 < this->width_)
|
if(current_line.size() + next_elem.size() + 1 < this->width_)
|
||||||
@@ -411,7 +339,10 @@ struct serializer
|
|||||||
}
|
}
|
||||||
if(!current_line.empty())
|
if(!current_line.empty())
|
||||||
{
|
{
|
||||||
if(current_line.back() != '\n') {current_line += '\n';}
|
if(!current_line.empty() && current_line.back() != '\n')
|
||||||
|
{
|
||||||
|
current_line += '\n';
|
||||||
|
}
|
||||||
token += current_line;
|
token += current_line;
|
||||||
}
|
}
|
||||||
token += "]\n";
|
token += "]\n";
|
||||||
@@ -557,8 +488,10 @@ struct serializer
|
|||||||
for(const auto& item : v)
|
for(const auto& item : v)
|
||||||
{
|
{
|
||||||
if(is_first) {is_first = false;} else {token += ',';}
|
if(is_first) {is_first = false;} else {token += ',';}
|
||||||
token += visit(serializer((std::numeric_limits<std::size_t>::max)(),
|
token += visit(serializer(
|
||||||
this->float_prec_, true), item);
|
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
|
||||||
|
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
|
||||||
|
/*has_comment*/ !item.comments().empty()), item);
|
||||||
}
|
}
|
||||||
token += ']';
|
token += ']';
|
||||||
return token;
|
return token;
|
||||||
@@ -577,8 +510,10 @@ struct serializer
|
|||||||
if(is_first) {is_first = false;} else {token += ',';}
|
if(is_first) {is_first = false;} else {token += ',';}
|
||||||
token += format_key(kv.first);
|
token += format_key(kv.first);
|
||||||
token += '=';
|
token += '=';
|
||||||
token += visit(serializer((std::numeric_limits<std::size_t>::max)(),
|
token += visit(serializer(
|
||||||
this->float_prec_, true), kv.second);
|
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
|
||||||
|
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
|
||||||
|
/*has_comment*/ !kv.second.comments().empty()), kv.second);
|
||||||
}
|
}
|
||||||
token += '}';
|
token += '}';
|
||||||
return token;
|
return token;
|
||||||
@@ -588,8 +523,16 @@ struct serializer
|
|||||||
{
|
{
|
||||||
std::string token;
|
std::string token;
|
||||||
|
|
||||||
// print non-table stuff first. because after printing [foo.bar], the
|
// print non-table elements first.
|
||||||
// remaining non-table values will be assigned into [foo.bar], not [foo]
|
// ```toml
|
||||||
|
// [foo] # a table we're writing now here
|
||||||
|
// key = "value" # <- non-table element, "key"
|
||||||
|
// # ...
|
||||||
|
// [foo.bar] # <- table element, "bar"
|
||||||
|
// ```
|
||||||
|
// because after printing [foo.bar], the remaining non-table values will
|
||||||
|
// be assigned into [foo.bar], not [foo]. Those values should be printed
|
||||||
|
// earlier.
|
||||||
for(const auto& kv : v)
|
for(const auto& kv : v)
|
||||||
{
|
{
|
||||||
if(kv.second.is_table() || is_array_of_tables(kv.second))
|
if(kv.second.is_table() || is_array_of_tables(kv.second))
|
||||||
@@ -597,21 +540,16 @@ struct serializer
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!kv.second.comments().empty() && !no_comment_)
|
token += write_comments(kv.second);
|
||||||
{
|
|
||||||
for(const auto& c : kv.second.comments())
|
|
||||||
{
|
|
||||||
token += '#';
|
|
||||||
token += c;
|
|
||||||
token += '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto key_and_sep = format_key(kv.first) + " = ";
|
const auto key_and_sep = format_key(kv.first) + " = ";
|
||||||
const auto residual_width = (this->width_ > key_and_sep.size()) ?
|
const auto residual_width = (this->width_ > key_and_sep.size()) ?
|
||||||
this->width_ - key_and_sep.size() : 0;
|
this->width_ - key_and_sep.size() : 0;
|
||||||
token += key_and_sep;
|
token += key_and_sep;
|
||||||
token += visit(serializer(residual_width, this->float_prec_, true),
|
token += visit(serializer(residual_width, this->float_prec_,
|
||||||
kv.second);
|
/*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
|
||||||
|
/*has_comment*/ !kv.second.comments().empty()), kv.second);
|
||||||
|
|
||||||
if(token.back() != '\n')
|
if(token.back() != '\n')
|
||||||
{
|
{
|
||||||
token += '\n';
|
token += '\n';
|
||||||
@@ -637,8 +575,8 @@ struct serializer
|
|||||||
ks.push_back(kv.first);
|
ks.push_back(kv.first);
|
||||||
|
|
||||||
auto tmp = visit(serializer(this->width_, this->float_prec_,
|
auto tmp = visit(serializer(this->width_, this->float_prec_,
|
||||||
!multiline_table_printed, this->no_comment_, ks),
|
!multiline_table_printed, this->no_comment_, ks,
|
||||||
kv.second);
|
/*has_comment*/ !kv.second.comments().empty()), kv.second);
|
||||||
|
|
||||||
if((!multiline_table_printed) &&
|
if((!multiline_table_printed) &&
|
||||||
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
|
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
|
||||||
@@ -651,31 +589,142 @@ struct serializer
|
|||||||
tmp += '\n';
|
tmp += '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!kv.second.comments().empty() && !no_comment_)
|
token += write_comments(kv.second);
|
||||||
{
|
|
||||||
for(const auto& c : kv.second.comments())
|
|
||||||
{
|
|
||||||
token += '#';
|
|
||||||
token += c;
|
|
||||||
token += '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token += tmp;
|
token += tmp;
|
||||||
}
|
}
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string make_array_of_tables(const array_type& v) const
|
||||||
|
{
|
||||||
|
// if it's not inlined, we need to add `[[table.key]]`.
|
||||||
|
// but if it can be inlined, we can format it as the following.
|
||||||
|
// ```
|
||||||
|
// table.key = [
|
||||||
|
// {...},
|
||||||
|
// # comment
|
||||||
|
// {...},
|
||||||
|
// ]
|
||||||
|
// ```
|
||||||
|
// This function checks if inlinization is possible or not, and then
|
||||||
|
// format the array-of-tables in a proper way.
|
||||||
|
//
|
||||||
|
// Note about comments:
|
||||||
|
//
|
||||||
|
// If the array itself has a comment (value_has_comment_ == true), we
|
||||||
|
// should try to make it inline.
|
||||||
|
// ```toml
|
||||||
|
// # comment about array
|
||||||
|
// array = [
|
||||||
|
// # comment about table element
|
||||||
|
// {of = "table"}
|
||||||
|
// ]
|
||||||
|
// ```
|
||||||
|
// If it is formatted as a multiline table, the two comments becomes
|
||||||
|
// indistinguishable.
|
||||||
|
// ```toml
|
||||||
|
// # comment about array
|
||||||
|
// # comment about table element
|
||||||
|
// [[array]]
|
||||||
|
// of = "table"
|
||||||
|
// ```
|
||||||
|
// So we need to try to make it inline, and it force-inlines regardless
|
||||||
|
// of the line width limit.
|
||||||
|
// It may fail if the element of a table has comment. In that case,
|
||||||
|
// the array-of-tables will be formatted as a multiline table.
|
||||||
|
if(this->can_be_inlined_ || this->value_has_comment_)
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
if(!keys_.empty())
|
||||||
|
{
|
||||||
|
token += format_key(keys_.back());
|
||||||
|
token += " = ";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool failed = false;
|
||||||
|
token += "[\n";
|
||||||
|
for(const auto& item : v)
|
||||||
|
{
|
||||||
|
// if an element of the table has a comment, the table
|
||||||
|
// cannot be inlined.
|
||||||
|
if(this->has_comment_inside(item.as_table()))
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// write comments for the table itself
|
||||||
|
token += write_comments(item);
|
||||||
|
|
||||||
|
const auto t = this->make_inline_table(item.as_table());
|
||||||
|
|
||||||
|
if(t.size() + 1 > width_ || // +1 for the last comma {...},
|
||||||
|
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
|
||||||
|
{
|
||||||
|
// if the value itself has a comment, ignore the line width limit
|
||||||
|
if( ! this->value_has_comment_)
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token += t;
|
||||||
|
token += ",\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ! failed)
|
||||||
|
{
|
||||||
|
token += "]\n";
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
// if failed, serialize them as [[array.of.tables]].
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string token;
|
||||||
|
for(const auto& item : v)
|
||||||
|
{
|
||||||
|
token += write_comments(item);
|
||||||
|
token += "[[";
|
||||||
|
token += format_keys(keys_);
|
||||||
|
token += "]]\n";
|
||||||
|
token += this->make_multiline_table(item.as_table());
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string write_comments(const value_type& v) const
|
||||||
|
{
|
||||||
|
std::string retval;
|
||||||
|
if(this->no_comment_) {return retval;}
|
||||||
|
|
||||||
|
for(const auto& c : v.comments())
|
||||||
|
{
|
||||||
|
retval += '#';
|
||||||
|
retval += c;
|
||||||
|
retval += '\n';
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_array_of_tables(const value_type& v) const
|
bool is_array_of_tables(const value_type& v) const
|
||||||
{
|
{
|
||||||
if(!v.is_array()) {return false;}
|
if(!v.is_array() || v.as_array().empty()) {return false;}
|
||||||
const auto& a = v.as_array();
|
return is_array_of_tables(v.as_array());
|
||||||
return !a.empty() && a.front().is_table();
|
}
|
||||||
|
bool is_array_of_tables(const array_type& v) const
|
||||||
|
{
|
||||||
|
// Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
|
||||||
|
// check all the element in an array to check if the array is an array
|
||||||
|
// of tables.
|
||||||
|
return std::all_of(v.begin(), v.end(), [](const value_type& elem) {
|
||||||
|
return elem.is_table();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool can_be_inlined_;
|
bool can_be_inlined_;
|
||||||
bool no_comment_;
|
bool no_comment_;
|
||||||
|
bool value_has_comment_;
|
||||||
int float_prec_;
|
int float_prec_;
|
||||||
std::size_t width_;
|
std::size_t width_;
|
||||||
std::vector<toml::key> keys_;
|
std::vector<toml::key> keys_;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#ifndef TOML11_SOURCE_LOCATION_HPP
|
#ifndef TOML11_SOURCE_LOCATION_HPP
|
||||||
#define TOML11_SOURCE_LOCATION_HPP
|
#define TOML11_SOURCE_LOCATION_HPP
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "region.hpp"
|
#include "region.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -1047,10 +1047,10 @@ class basic_value
|
|||||||
//
|
//
|
||||||
// Those constructors take detail::region that contains parse result.
|
// Those constructors take detail::region that contains parse result.
|
||||||
|
|
||||||
basic_value(boolean b, detail::region reg)
|
basic_value(boolean b, detail::region reg, std::vector<std::string> cm)
|
||||||
: type_(value_t::boolean),
|
: type_(value_t::boolean),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->boolean_, b);
|
assigner(this->boolean_, b);
|
||||||
}
|
}
|
||||||
@@ -1058,68 +1058,75 @@ class basic_value
|
|||||||
detail::conjunction<
|
detail::conjunction<
|
||||||
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
|
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
|
||||||
>::value, std::nullptr_t>::type = nullptr>
|
>::value, std::nullptr_t>::type = nullptr>
|
||||||
basic_value(T i, detail::region reg)
|
basic_value(T i, detail::region reg, std::vector<std::string> cm)
|
||||||
: type_(value_t::integer),
|
: type_(value_t::integer),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->integer_, static_cast<integer>(i));
|
assigner(this->integer_, static_cast<integer>(i));
|
||||||
}
|
}
|
||||||
template<typename T, typename std::enable_if<
|
template<typename T, typename std::enable_if<
|
||||||
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
|
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
|
||||||
basic_value(T f, detail::region reg)
|
basic_value(T f, detail::region reg, std::vector<std::string> cm)
|
||||||
: type_(value_t::floating),
|
: type_(value_t::floating),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->floating_, static_cast<floating>(f));
|
assigner(this->floating_, static_cast<floating>(f));
|
||||||
}
|
}
|
||||||
basic_value(toml::string s, detail::region reg)
|
basic_value(toml::string s, detail::region reg,
|
||||||
|
std::vector<std::string> cm)
|
||||||
: type_(value_t::string),
|
: type_(value_t::string),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->string_, std::move(s));
|
assigner(this->string_, std::move(s));
|
||||||
}
|
}
|
||||||
basic_value(const local_date& ld, detail::region reg)
|
basic_value(const local_date& ld, detail::region reg,
|
||||||
|
std::vector<std::string> cm)
|
||||||
: type_(value_t::local_date),
|
: type_(value_t::local_date),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->local_date_, ld);
|
assigner(this->local_date_, ld);
|
||||||
}
|
}
|
||||||
basic_value(const local_time& lt, detail::region reg)
|
basic_value(const local_time& lt, detail::region reg,
|
||||||
|
std::vector<std::string> cm)
|
||||||
: type_(value_t::local_time),
|
: type_(value_t::local_time),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->local_time_, lt);
|
assigner(this->local_time_, lt);
|
||||||
}
|
}
|
||||||
basic_value(const local_datetime& ldt, detail::region reg)
|
basic_value(const local_datetime& ldt, detail::region reg,
|
||||||
|
std::vector<std::string> cm)
|
||||||
: type_(value_t::local_datetime),
|
: type_(value_t::local_datetime),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->local_datetime_, ldt);
|
assigner(this->local_datetime_, ldt);
|
||||||
}
|
}
|
||||||
basic_value(const offset_datetime& odt, detail::region reg)
|
basic_value(const offset_datetime& odt, detail::region reg,
|
||||||
|
std::vector<std::string> cm)
|
||||||
: type_(value_t::offset_datetime),
|
: type_(value_t::offset_datetime),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->offset_datetime_, odt);
|
assigner(this->offset_datetime_, odt);
|
||||||
}
|
}
|
||||||
basic_value(const array_type& ary, detail::region reg)
|
basic_value(const array_type& ary, detail::region reg,
|
||||||
|
std::vector<std::string> cm)
|
||||||
: type_(value_t::array),
|
: type_(value_t::array),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->array_, ary);
|
assigner(this->array_, ary);
|
||||||
}
|
}
|
||||||
basic_value(const table_type& tab, detail::region reg)
|
basic_value(const table_type& tab, detail::region reg,
|
||||||
|
std::vector<std::string> cm)
|
||||||
: type_(value_t::table),
|
: type_(value_t::table),
|
||||||
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
region_info_(std::make_shared<detail::region>(std::move(reg))),
|
||||||
comments_(region_info_->comments())
|
comments_(std::move(cm))
|
||||||
{
|
{
|
||||||
assigner(this->table_, tab);
|
assigner(this->table_, tab);
|
||||||
}
|
}
|
||||||
@@ -1127,8 +1134,10 @@ class basic_value
|
|||||||
template<typename T, typename std::enable_if<
|
template<typename T, typename std::enable_if<
|
||||||
detail::is_exact_toml_type<T, value_type>::value,
|
detail::is_exact_toml_type<T, value_type>::value,
|
||||||
std::nullptr_t>::type = nullptr>
|
std::nullptr_t>::type = nullptr>
|
||||||
basic_value(std::pair<T, detail::region> parse_result)
|
basic_value(std::pair<T, detail::region> parse_result, std::vector<std::string> comments)
|
||||||
: basic_value(std::move(parse_result.first), std::move(parse_result.second))
|
: basic_value(std::move(parse_result.first),
|
||||||
|
std::move(parse_result.second),
|
||||||
|
std::move(comments))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// type checking and casting ============================================
|
// type checking and casting ============================================
|
||||||
|
|||||||
Reference in New Issue
Block a user