mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-09-17 09:08:08 +08:00
Merge branch 'master' into v3
This commit is contained in:
@@ -1,7 +1,17 @@
|
|||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
project(toml11)
|
project(toml11)
|
||||||
|
|
||||||
|
set(toml11_VERSION_MAYOR 2)
|
||||||
|
set(toml11_VERSION_MINOR 3)
|
||||||
|
set(toml11_VERSION_PATCH 1)
|
||||||
|
set(toml11_VERSION
|
||||||
|
"${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}"
|
||||||
|
)
|
||||||
|
|
||||||
|
option(toml11_BUILD_TEST "Build toml tests" ON)
|
||||||
|
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
|
if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
@@ -34,5 +44,62 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories(${PROJECT_SOURCE_DIR})
|
# Set some common directories
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
set(toml11_install_cmake_dir ${CMAKE_INSTALL_LIBDIR}/cmake/toml11)
|
||||||
|
set(toml11_install_include_dir ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
|
set(toml11_config_dir ${CMAKE_CURRENT_BINARY_DIR}/cmake/)
|
||||||
|
set(toml11_config ${toml11_config_dir}/toml11Config.cmake)
|
||||||
|
set(toml11_config_version ${toml11_config_dir}/toml11ConfigVersion.cmake)
|
||||||
|
|
||||||
|
add_library(toml11 INTERFACE)
|
||||||
|
target_include_directories(toml11 INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||||
|
$<INSTALL_INTERFACE:${toml11_install_include_dir}>
|
||||||
|
)
|
||||||
|
add_library(toml11::toml11 ALIAS toml11)
|
||||||
|
|
||||||
|
# Write config and version config files
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
write_basic_package_version_file(
|
||||||
|
${toml11_config_version}
|
||||||
|
VERSION ${toml11_VERSION}
|
||||||
|
COMPATIBILITY SameMajorVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_package_config_file(
|
||||||
|
cmake/toml11Config.cmake.in
|
||||||
|
${toml11_config}
|
||||||
|
INSTALL_DESTINATION ${toml11_install_cmake_dir}
|
||||||
|
PATH_VARS toml11_install_cmake_dir
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install config files
|
||||||
|
install(FILES ${toml11_config} ${toml11_config_version}
|
||||||
|
DESTINATION ${toml11_install_cmake_dir}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install header files
|
||||||
|
install(
|
||||||
|
FILES toml.hpp
|
||||||
|
DESTINATION "${toml11_install_include_dir}"
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "toml"
|
||||||
|
DESTINATION "${toml11_install_include_dir}"
|
||||||
|
FILES_MATCHING PATTERN "*.hpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Export targets and install them
|
||||||
|
install(TARGETS toml11
|
||||||
|
EXPORT toml11Targets
|
||||||
|
)
|
||||||
|
install(EXPORT toml11Targets
|
||||||
|
FILE toml11Targets.cmake
|
||||||
|
DESTINATION ${toml11_install_cmake_dir}
|
||||||
|
NAMESPACE toml11::
|
||||||
|
)
|
||||||
|
|
||||||
|
if (toml11_BUILD_TEST)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
endif ()
|
||||||
|
20
README.md
20
README.md
@@ -150,6 +150,24 @@ terminate called after throwing an instance of 'toml::syntax_error'
|
|||||||
| ~~~~~~~ table defined twice
|
| ~~~~~~~ table defined twice
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When toml11 encounters a malformed value, it tries to detect what type it is.
|
||||||
|
Then it shows hints to fix the format. An error message while reading one of
|
||||||
|
the malformed files in [the language agnostic test suite](https://github.com/BurntSushi/toml-test).
|
||||||
|
is shown below.
|
||||||
|
|
||||||
|
```console
|
||||||
|
what(): [error] bad time: should be HH:MM:SS.subsec
|
||||||
|
--> ./datetime-malformed-no-secs.toml
|
||||||
|
1 | no-secs = 1987-07-05T17:45Z
|
||||||
|
| ^------- HH:MM:SS.subsec
|
||||||
|
|
|
||||||
|
Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999
|
||||||
|
Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32
|
||||||
|
```
|
||||||
|
|
||||||
|
You can find other examples in a job named `output_result` on
|
||||||
|
[CircleCI](https://circleci.com/gh/ToruNiina/toml11).
|
||||||
|
|
||||||
Since the error message generation is generally a difficult task, the current
|
Since the error message generation is generally a difficult task, the current
|
||||||
status is not ideal. If you encounter a weird error message, please let us know
|
status is not ideal. If you encounter a weird error message, please let us know
|
||||||
and contribute to improve the quality!
|
and contribute to improve the quality!
|
||||||
@@ -1340,6 +1358,8 @@ I appreciate the help of the contributors who introduced the great feature to th
|
|||||||
- Fixed Visual Studio 2019 warnings
|
- Fixed Visual Studio 2019 warnings
|
||||||
- @khoitd1997
|
- @khoitd1997
|
||||||
- Fixed warnings while type conversion
|
- Fixed warnings while type conversion
|
||||||
|
- @KerstinKeller
|
||||||
|
- Added installation script to CMake
|
||||||
|
|
||||||
## Licensing terms
|
## Licensing terms
|
||||||
|
|
||||||
|
2
cmake/toml11Config.cmake.in
Normal file
2
cmake/toml11Config.cmake.in
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
@PACKAGE_INIT@
|
||||||
|
include("@PACKAGE_toml11_install_cmake_dir@/toml11Targets.cmake")
|
@@ -87,7 +87,7 @@ add_definitions(-DUNITTEST_FRAMEWORK_LIBRARY_EXIST)
|
|||||||
|
|
||||||
foreach(TEST_NAME ${TEST_NAMES})
|
foreach(TEST_NAME ${TEST_NAMES})
|
||||||
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
|
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
|
||||||
target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
|
target_link_libraries(${TEST_NAME} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} toml11::toml11)
|
||||||
target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
|
target_include_directories(${TEST_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
|
||||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
|
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||||
|
|
||||||
@@ -104,3 +104,4 @@ endforeach(TEST_NAME)
|
|||||||
add_executable(test_multiple_translation_unit
|
add_executable(test_multiple_translation_unit
|
||||||
test_multiple_translation_unit_1.cpp
|
test_multiple_translation_unit_1.cpp
|
||||||
test_multiple_translation_unit_2.cpp)
|
test_multiple_translation_unit_2.cpp)
|
||||||
|
target_link_libraries(test_multiple_translation_unit toml11::toml11)
|
||||||
|
@@ -12,9 +12,8 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_find_for_value)
|
BOOST_AUTO_TEST_CASE(test_find)
|
||||||
{
|
{
|
||||||
// value itself is not a table
|
|
||||||
{
|
{
|
||||||
toml::value v(true);
|
toml::value v(true);
|
||||||
bool thrown = false;
|
bool thrown = false;
|
||||||
@@ -43,16 +42,6 @@ BOOST_AUTO_TEST_CASE(test_find_for_value)
|
|||||||
BOOST_CHECK(thrown);
|
BOOST_CHECK(thrown);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
toml::value v = toml::table{{"num", 42}};
|
|
||||||
BOOST_CHECK_EQUAL(42, toml::find<int>(v, "num"));
|
|
||||||
|
|
||||||
// reference that can be used to modify the content
|
|
||||||
auto& num = toml::find<toml::integer>(v, "num");
|
|
||||||
num = 54;
|
|
||||||
BOOST_CHECK_EQUAL(54, toml::find<int>(v, "num"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// recursively search tables
|
// recursively search tables
|
||||||
{
|
{
|
||||||
toml::value v = toml::table{
|
toml::value v = toml::table{
|
||||||
|
154
toml/parser.hpp
154
toml/parser.hpp
@@ -1422,41 +1422,156 @@ parse_inline_table(location<Container>& loc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
value_t guess_number_type(const location<Container>& l)
|
result<value_t, std::string> guess_number_type(const location<Container>& l)
|
||||||
{
|
{
|
||||||
|
// This function tries to find some (common) mistakes by checking characters
|
||||||
|
// that follows the last character of a value. But it is often difficult
|
||||||
|
// because some non-newline characters can appear after a value. E.g.
|
||||||
|
// spaces, tabs, commas (in an array or inline table), closing brackets
|
||||||
|
// (of an array or inline table), comment-sign (#). Since this function
|
||||||
|
// does not parse further, those characters are always allowed to be there.
|
||||||
location<Container> loc = l;
|
location<Container> loc = l;
|
||||||
|
|
||||||
if(lex_offset_date_time::invoke(loc)) {return value_t::offset_datetime;}
|
if(lex_offset_date_time::invoke(loc)) {return ok(value_t::offset_datetime);}
|
||||||
loc.reset(l.iter());
|
loc.reset(l.iter());
|
||||||
|
|
||||||
if(lex_local_date_time::invoke(loc)) {return value_t::local_datetime;}
|
if(lex_local_date_time::invoke(loc))
|
||||||
|
{
|
||||||
|
// bad offset may appear after this.
|
||||||
|
if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-'
|
||||||
|
|| *loc.iter() == 'Z' || *loc.iter() == 'z'))
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad offset: should be [+-]HH:MM or Z",
|
||||||
|
{{std::addressof(loc), "[+-]HH:MM or Z"}},
|
||||||
|
{"pass: +09:00, -05:30", "fail: +9:00, -5:30"}));
|
||||||
|
}
|
||||||
|
return ok(value_t::local_datetime);
|
||||||
|
}
|
||||||
loc.reset(l.iter());
|
loc.reset(l.iter());
|
||||||
|
|
||||||
if(lex_local_date::invoke(loc)) {return value_t::local_date;}
|
if(lex_local_date::invoke(loc))
|
||||||
|
{
|
||||||
|
// bad time may appear after this.
|
||||||
|
// A space is allowed as a delimiter between local time. But there are
|
||||||
|
// both cases in which a space becomes valid or invalid.
|
||||||
|
// - invalid: 2019-06-16 7:00:00
|
||||||
|
// - valid : 2019-06-16 07:00:00
|
||||||
|
if(loc.iter() != loc.end())
|
||||||
|
{
|
||||||
|
const auto c = *loc.iter();
|
||||||
|
if(c == 'T' || c == 't')
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad time: should be HH:MM:SS.subsec",
|
||||||
|
{{std::addressof(loc), "HH:MM:SS.subsec"}},
|
||||||
|
{"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999",
|
||||||
|
"fail: 1979-05-27T7:32:00, 1979-05-27 17:32"}));
|
||||||
|
}
|
||||||
|
if('0' <= c && c <= '9')
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad time: missing T",
|
||||||
|
{{std::addressof(loc), "T or space required here"}},
|
||||||
|
{"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999",
|
||||||
|
"fail: 1979-05-27T7:32:00, 1979-05-27 7:32"}));
|
||||||
|
}
|
||||||
|
if(c == ' ' && std::next(loc.iter()) != loc.end() &&
|
||||||
|
('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9'))
|
||||||
|
{
|
||||||
|
loc.advance();
|
||||||
|
return err(format_underline("[error] bad time: should be HH:MM:SS.subsec",
|
||||||
|
{{std::addressof(loc), "HH:MM:SS.subsec"}},
|
||||||
|
{"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999",
|
||||||
|
"fail: 1979-05-27T7:32:00, 1979-05-27 7:32"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok(value_t::local_date);
|
||||||
|
}
|
||||||
loc.reset(l.iter());
|
loc.reset(l.iter());
|
||||||
|
|
||||||
if(lex_local_time::invoke(loc)) {return value_t::local_time;}
|
if(lex_local_time::invoke(loc)) {return ok(value_t::local_time);}
|
||||||
loc.reset(l.iter());
|
loc.reset(l.iter());
|
||||||
|
|
||||||
if(lex_float::invoke(loc)) {return value_t::floating;}
|
if(lex_float::invoke(loc))
|
||||||
|
{
|
||||||
|
if(loc.iter() != loc.end() && *loc.iter() == '_')
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad float: `_` should be surrounded by digits",
|
||||||
|
{{std::addressof(loc), "here"}},
|
||||||
|
{"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan",
|
||||||
|
"fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"}));
|
||||||
|
}
|
||||||
|
return ok(value_t::floating);
|
||||||
|
}
|
||||||
loc.reset(l.iter());
|
loc.reset(l.iter());
|
||||||
|
|
||||||
return value_t::integer;
|
if(lex_integer::invoke(loc))
|
||||||
|
{
|
||||||
|
if(loc.iter() != loc.end())
|
||||||
|
{
|
||||||
|
const auto c = *loc.iter();
|
||||||
|
if(c == '_')
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad integer: `_` should be surrounded by digits",
|
||||||
|
{{std::addressof(loc), "here"}},
|
||||||
|
{"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755",
|
||||||
|
"fail: 1__000, 0123"}));
|
||||||
|
}
|
||||||
|
if('0' <= c && c <= '9')
|
||||||
|
{
|
||||||
|
// leading zero. point '0'
|
||||||
|
loc.retrace();
|
||||||
|
return err(format_underline("[error] bad integer: leading zero",
|
||||||
|
{{std::addressof(loc), "here"}},
|
||||||
|
{"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755",
|
||||||
|
"fail: 1__000, 0123"}));
|
||||||
|
}
|
||||||
|
if(c == ':' || c == '-')
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad datetime: invalid format",
|
||||||
|
{{std::addressof(loc), "here"}},
|
||||||
|
{"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z",
|
||||||
|
"fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"}));
|
||||||
|
}
|
||||||
|
if(c == '.' || c == 'e' || c == 'E')
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad float: invalid format",
|
||||||
|
{{std::addressof(loc), "here"}},
|
||||||
|
{"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan",
|
||||||
|
"fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok(value_t::integer);
|
||||||
|
}
|
||||||
|
if(loc.iter() != loc.end() && *loc.iter() == '.')
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad float: invalid format",
|
||||||
|
{{std::addressof(loc), "integer part required before this"}},
|
||||||
|
{"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan",
|
||||||
|
"fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"}));
|
||||||
|
}
|
||||||
|
if(loc.iter() != loc.end() && *loc.iter() == '_')
|
||||||
|
{
|
||||||
|
return err(format_underline("[error] bad number: `_` should be surrounded by digits",
|
||||||
|
{{std::addressof(loc), "`_` is not surrounded by digits"}},
|
||||||
|
{"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755",
|
||||||
|
"fail: 1__000, 0123"}));
|
||||||
|
}
|
||||||
|
return err(format_underline("[error] bad format: unknown value appeared",
|
||||||
|
{{std::addressof(loc), "here"}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Container>
|
template<typename Container>
|
||||||
value_t guess_value_type(const location<Container>& loc)
|
result<value_t, std::string> guess_value_type(const location<Container>& loc)
|
||||||
{
|
{
|
||||||
switch(*loc.iter())
|
switch(*loc.iter())
|
||||||
{
|
{
|
||||||
case '"' : {return value_t::string; }
|
case '"' : {return ok(value_t::string); }
|
||||||
case '\'': {return value_t::string; }
|
case '\'': {return ok(value_t::string); }
|
||||||
case 't' : {return value_t::boolean; }
|
case 't' : {return ok(value_t::boolean); }
|
||||||
case 'f' : {return value_t::boolean; }
|
case 'f' : {return ok(value_t::boolean); }
|
||||||
case '[' : {return value_t::array; }
|
case '[' : {return ok(value_t::array); }
|
||||||
case '{' : {return value_t::table; }
|
case '{' : {return ok(value_t::table); }
|
||||||
case 'i' : {return value_t::floating;} // inf.
|
case 'i' : {return ok(value_t::floating);} // inf.
|
||||||
case 'n' : {return value_t::floating;} // nan.
|
case 'n' : {return ok(value_t::floating);} // nan.
|
||||||
default : {return guess_number_type(loc);}
|
default : {return guess_number_type(loc);}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1473,7 +1588,12 @@ result<Value, std::string> parse_value(location<Container>& loc)
|
|||||||
{{std::addressof(loc), ""}}));
|
{{std::addressof(loc), ""}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(guess_value_type(loc))
|
const auto type = guess_value_type(loc);
|
||||||
|
if(!type)
|
||||||
|
{
|
||||||
|
return err(type.unwrap_err());
|
||||||
|
}
|
||||||
|
switch(type.unwrap())
|
||||||
{
|
{
|
||||||
case value_t::boolean : {return parse_boolean(loc); }
|
case value_t::boolean : {return parse_boolean(loc); }
|
||||||
case value_t::integer : {return parse_integer(loc); }
|
case value_t::integer : {return parse_integer(loc); }
|
||||||
|
Reference in New Issue
Block a user