Compare commits

..

5 Commits

Author SHA1 Message Date
ToruNiina
bfe5e50acf test: add missing include files 2020-02-07 21:00:11 +09:00
ToruNiina
05077dee0c test: add test_visit 2020-02-07 15:58:15 +09:00
ToruNiina
b9f1726e26 feat: add multi-argument toml::visit(visitor, ...) 2020-02-07 15:57:34 +09:00
ToruNiina
c583e38ebf fix: include visit.hpp from serializer 2020-02-07 15:57:09 +09:00
ToruNiina
388e9db32b refactor: move visit() from value.hpp to visit.hpp 2020-01-31 22:48:37 +09:00
14 changed files with 363 additions and 344 deletions

View File

@@ -5,7 +5,7 @@ project(toml11)
set(toml11_VERSION_MAYOR 3)
set(toml11_VERSION_MINOR 3)
set(toml11_VERSION_PATCH 1)
set(toml11_VERSION_PATCH 0)
set(toml11_VERSION
"${toml11_VERSION_MAYOR}.${toml11_VERSION_MINOR}.${toml11_VERSION_PATCH}"
)
@@ -16,9 +16,11 @@ option(toml11_TEST_WITH_UBSAN "use LLVM undefined behavior sanitizer" OFF)
include(CheckCXXCompilerFlag)
if("${CMAKE_VERSION}" VERSION_GREATER 3.1)
set(CMAKE_CXX_STANDARD 11 CACHE STRING "The C++ standard whose features are requested to build all targets.")
set(CMAKE_CXX_STANDARD_REQUIRED ON CACHE BOOL "Boolean describing whether the value of CXX_STANDARD is a requirement.")
set(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Boolean specifying whether compiler specific extensions are requested.")
set(CMAKE_CXX_EXTENSIONS OFF)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
set(CXX_STANDARD_REQUIRED ON)
else()
# Manually check for C++11 compiler flag.
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)

View File

@@ -1783,10 +1783,6 @@ I appreciate the help of the contributors who introduced the great feature to th
- Suppress warnings in Debug mode
- OGAWA Kenichi (@kenichiice)
- Suppress warnings on intel compiler
- Jordan Williams (@jwillikers)
- Fixed clang range-loop-analysis warnings
- Fixed feature test macro to suppress -Wundef
- Use cache variables in CMakeLists.txt
## Licensing terms

View File

@@ -34,6 +34,7 @@ set(TEST_NAMES
test_error_detection
test_format_error
test_extended_conversions
test_visit
)
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
@@ -48,8 +49,6 @@ CHECK_CXX_COMPILER_FLAG("-Wduplicated-branches" COMPILER_SUPPORTS_WDUPLICATED_BR
CHECK_CXX_COMPILER_FLAG("-Wlogical-op" COMPILER_SUPPORTS_WLOGICAL_OP)
CHECK_CXX_COMPILER_FLAG("-Wuseless-cast" COMPILER_SUPPORTS_WUSELESS_CAST)
CHECK_CXX_COMPILER_FLAG("-Wdouble-promotion" COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
CHECK_CXX_COMPILER_FLAG("-Wrange-loop-analysis" COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
CHECK_CXX_COMPILER_FLAG("-Wundef" COMPILER_SUPPORTS_WUNDEF)
if(COMPILER_SUPPORTS_WALL)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
@@ -84,12 +83,6 @@ endif()
if(COMPILER_SUPPORTS_WDOUBLE_PROMOTION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdouble-promotion")
endif()
if(COMPILER_SUPPORTS_WRANGE_LOOP_ANALYSIS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wrange-loop-analysis")
endif()
if(COMPILER_SUPPORTS_WUNDEF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wundef")
endif()
option(TOML11_USE_UNRELEASED_TOML_FEATURES
"use features in toml-lang/toml master while testing" OFF)

View File

@@ -84,7 +84,9 @@ struct json_serializer
{
if(!is_first) {std::cout << ", ";}
is_first = false;
std::cout << this->format_key(elem.first) << ':';
std::cout << toml::format(toml::value(elem.first),
std::numeric_limits<std::size_t>::max());
std::cout << ':';
toml::visit(*this, elem.second);
}
std::cout << '}';
@@ -110,12 +112,6 @@ struct json_serializer
}
return retval;
}
std::string format_key(const std::string& s) const
{
const auto quote("\"");
return quote + escape_string(s) + quote;
}
};
int main()

View File

@@ -57,22 +57,6 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"");
TOML11_TEST_LEX_ACCEPT(lex_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"");
}
BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -99,16 +83,4 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_LEX_ACCEPT(lex_string,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''That's still pointless', she said.'''",
"''''That's still pointless', she said.'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''");
TOML11_TEST_LEX_ACCEPT(lex_string,
"''''This,' she said, 'is just a pointless statement.''''",
"''''This,' she said, 'is just a pointless statement.''''");
}

View File

@@ -69,9 +69,6 @@ BOOST_AUTO_TEST_CASE(test_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"\" And when \\\"'s are in the along with # \\\"\"",
string(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
string("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_basic_string_value)
@@ -97,9 +94,6 @@ BOOST_AUTO_TEST_CASE(test_basic_string_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\" And when \\\"'s are in the along with # \\\"\"",
value(" And when \"'s are in the along with # \"", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"Here are fifteen apostrophes: '''''''''''''''\"",
value("Here are fifteen apostrophes: '''''''''''''''", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_ml_basic_string)
@@ -110,18 +104,6 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
string("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
string("Here are two quotation marks: \"\". Simple enough.", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
string("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL(parse_string,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
string("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_ml_basic_string_value)
@@ -132,19 +114,6 @@ BOOST_AUTO_TEST_CASE(test_ml_basic_string_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\\\n The quick brown \\\n\n fox jumps over \\\n the lazy dog.\\\n \"\"\"",
value("The quick brown fox jumps over the lazy dog.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"",
value("Here are two quotation marks: \"\". Simple enough.", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are three quotation marks: \"\"\\\".\"\"\"",
value("Here are three quotation marks: \"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::basic));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"\"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"",
value("\"This,\" she said, \"is just a pointless statement.\"", string_t::basic));
}
BOOST_AUTO_TEST_CASE(test_literal_string)
@@ -187,15 +156,6 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string)
TOML11_TEST_PARSE_EQUAL(parse_string,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
string("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"''''That's still pointless', she said.'''",
string("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
string("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL(parse_string,
"''''This,' she said, 'is just a pointless statement.''''",
string("'This,' she said, 'is just a pointless statement.'", string_t::literal));
}
BOOST_AUTO_TEST_CASE(test_ml_literal_string_value)
@@ -206,15 +166,6 @@ BOOST_AUTO_TEST_CASE(test_ml_literal_string_value)
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''",
value("The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"''''That's still pointless', she said.'''",
value("'That's still pointless', she said.", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"'''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".'''",
value("Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", string_t::literal));
TOML11_TEST_PARSE_EQUAL_VALUE(parse_value<toml::value>,
"''''This,' she said, 'is just a pointless statement.''''",
value("'This,' she said, 'is just a pointless statement.'", string_t::literal));
}
BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence)

172
tests/test_visit.cpp Normal file
View File

@@ -0,0 +1,172 @@
#define BOOST_TEST_MODULE "test_visit"
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
#include <boost/test/unit_test.hpp>
#else
#define BOOST_TEST_NO_LIB
#include <boost/test/included/unit_test.hpp>
#endif
#include <toml.hpp>
#include <map>
#include <deque>
using test_value_types = std::tuple<
toml::value,
toml::basic_value<toml::preserve_comments>,
toml::basic_value<toml::discard_comments, std::map, std::deque>,
toml::basic_value<toml::preserve_comments, std::map, std::deque>
>;
template<typename Value>
struct visitor1
{
std::string operator()(const toml::boolean&) const {return "boolean";}
std::string operator()(const toml::integer&) const {return "integer";}
std::string operator()(const toml::floating&) const {return "floating";}
std::string operator()(const toml::string&) const {return "string";}
std::string operator()(const toml::local_time&) const {return "local_time";}
std::string operator()(const toml::local_date&) const {return "local_date";}
std::string operator()(const toml::local_datetime&) const {return "local_datetime";}
std::string operator()(const toml::offset_datetime&) const {return "offset_datetime";}
std::string operator()(const typename Value::array_type&) const {return "array";}
std::string operator()(const typename Value::table_type&) const {return "table";}
};
BOOST_AUTO_TEST_CASE_TEMPLATE(test_visit_one, value_type, test_value_types)
{
{
const value_type v(true);
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "boolean");
}
{
const value_type v(42);
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "integer");
}
{
const value_type v(3.14);
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "floating");
}
{
const value_type v("foo");
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "string");
}
{
const value_type v(toml::local_date(2018, toml::month_t::Apr, 22));
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "local_date");
}
{
const value_type v(toml::local_time(12, 34, 56));
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "local_time");
}
{
const value_type v(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56)));
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "local_datetime");
}
{
const value_type v(toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56),
toml::time_offset(9, 0)));
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "offset_datetime");
}
{
const value_type v{1,2,3,4,5};
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "array");
}
{
const value_type v{
{"foo", 42}, {"bar", "baz"}
};
BOOST_TEST(toml::visit(visitor1<value_type>{}, v) == "table");
}
}
template<typename Value>
struct visitor2
{
template<typename T1, typename T2>
std::string operator()(const T1& v1, const T2& v2) const
{
visitor1<Value> vis;
return vis(v1) + "+" + vis(v2);
}
};
BOOST_AUTO_TEST_CASE_TEMPLATE(test_visit_two, value_type, test_value_types)
{
std::vector<value_type> vs;
vs.push_back(value_type(true));
vs.push_back(value_type(42));
vs.push_back(value_type(3.14));
vs.push_back(value_type("foo"));
vs.push_back(value_type(toml::local_date(2018, toml::month_t::Apr, 22)));
vs.push_back(value_type(toml::local_time(12, 34, 56)));
vs.push_back(value_type(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56))));
vs.push_back(value_type(toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56),
toml::time_offset(9, 0))));
vs.push_back(value_type{1,2,3,4,5});
vs.push_back(value_type{{"foo", 42}, {"bar", "baz"}});
for(const auto& v1 : vs)
{
const auto t1 = toml::visit(visitor1<value_type>{}, v1);
for(const auto& v2 : vs)
{
const auto t2 = toml::visit(visitor1<value_type>{}, v2);
BOOST_TEST(toml::visit(visitor2<value_type>{}, v1, v2) ==
t1 + "+" + t2);
}
}
}
template<typename Value>
struct visitor3
{
template<typename T1, typename T2, typename T3>
std::string operator()(const T1& v1, const T2& v2, const T3& v3) const
{
visitor1<Value> vis;
return vis(v1) + "+" + vis(v2) + "+" + vis(v3);
}
};
BOOST_AUTO_TEST_CASE_TEMPLATE(test_visit_three, value_type, test_value_types)
{
std::vector<value_type> vs;
vs.push_back(value_type(true));
vs.push_back(value_type(42));
vs.push_back(value_type(3.14));
vs.push_back(value_type("foo"));
vs.push_back(value_type(toml::local_date(2018, toml::month_t::Apr, 22)));
vs.push_back(value_type(toml::local_time(12, 34, 56)));
vs.push_back(value_type(toml::local_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56))));
vs.push_back(value_type(toml::offset_datetime(
toml::local_date(2018, toml::month_t::Apr, 22),
toml::local_time(12, 34, 56),
toml::time_offset(9, 0))));
vs.push_back(value_type{1,2,3,4,5});
vs.push_back(value_type{{"foo", 42}, {"bar", "baz"}});
for(const auto& v1 : vs)
{
const auto t1 = toml::visit(visitor1<value_type>{}, v1);
for(const auto& v2 : vs)
{
const auto t2 = toml::visit(visitor1<value_type>{}, v2);
for(const auto& v3 : vs)
{
const auto t3 = toml::visit(visitor1<value_type>{}, v3);
BOOST_TEST(toml::visit(visitor3<value_type>{}, v1, v2, v3) ==
t1 + "+" + t2 + "+" + t3);
}
}
}
}

View File

@@ -20,7 +20,7 @@ namespace toml
namespace detail
{
// TODO: find more sophisticated way to handle this
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_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;
@@ -35,7 +35,7 @@ inline std::tm gmtime_s(const std::time_t* src)
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#elif defined(_MSC_VER)
#elif _MSC_VER
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;

View File

@@ -154,53 +154,12 @@ using lex_basic_string = sequence<lex_quotation_mark,
repeat<lex_basic_char, unlimited>,
lex_quotation_mark>;
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
// are allowed to be used.
// After this, the following strings are *explicitly* allowed.
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
// - One or two `"`s can appear just before or after the delimiter.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// str7 = """"This," she said, "is just a pointless statement.""""
// ```
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
// the above example. It is difficult to recognize `"` at the end of string body
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
// invalid `"`. Like this:
// ```console
// what(): [error] toml::parse_table: invalid line format
// --> hoge.toml
// |
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
// | ^- expected newline, but got '"'.
// ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// splitted into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body.
//
// Note: This feature is a "clarification". Therefore this change is considered
// as a spec that has been defined since the time when the multi-line
// basic string was introduced. Although it is a post-v0.5.0 changes,
// this change will be activated regardless of the flag,
// `TOML11_USE_UNRELEASED_TOML_FEATURES`.
//
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
using lex_ml_basic_string_open = lex_ml_basic_string_delim;
using lex_ml_basic_string_close = sequence<
repeat<lex_quotation_mark, exactly<3>>,
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
in_range<0x0a, 0x1F>, // is tab
character<0x5C>, // backslash
character<0x7F>, // DEL
character<0x5C>,
character<0x7F>,
lex_ml_basic_string_delim>>;
#else // TOML v0.5.0
using lex_ml_basic_unescaped = exclude<either<in_range<0x00,0x1F>,
@@ -217,9 +176,9 @@ using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
lex_ml_basic_escaped_newline>,
unlimited>;
using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
using lex_ml_basic_string = sequence<lex_ml_basic_string_delim,
lex_ml_basic_body,
lex_ml_basic_string_close>;
lex_ml_basic_string_delim>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x19>, character<0x27>>>;
@@ -228,13 +187,7 @@ using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>,
lex_apostrophe>;
// the same reason as above.
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
using lex_ml_literal_string_close = sequence<
repeat<lex_apostrophe, exactly<3>>,
maybe<lex_apostrophe>, maybe<lex_apostrophe>
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x1F>,
@@ -242,9 +195,9 @@ using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
unlimited>;
using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
using lex_ml_literal_string = sequence<lex_ml_literal_string_delim,
lex_ml_literal_body,
lex_ml_literal_string_close>;
lex_ml_literal_string_delim>;
using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>;

View File

@@ -375,7 +375,7 @@ parse_ml_basic_string(location<Container>& loc)
std::string retval;
retval.reserve(token.unwrap().size());
auto delim = lex_ml_basic_string_open::invoke(inner_loc);
auto delim = lex_ml_basic_string_delim::invoke(inner_loc);
if(!delim)
{
throw internal_error(format_underline(
@@ -410,26 +410,7 @@ parse_ml_basic_string(location<Container>& loc)
{{std::addressof(inner_loc), "not sufficient token"}}),
source_location(std::addressof(inner_loc)));
}
delim = lex_ml_basic_string_close::invoke(inner_loc);
}
// `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s
// at just before the delimiter. Here, we need to attach `"`s at the
// end of the string body, if it exists.
// For detail, see the definition of `lex_ml_basic_string_close`.
assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(),
[](const char c) noexcept {return c == '\"';}));
switch(delim.unwrap().size())
{
case 3: {break;}
case 4: {retval += "\""; break;}
case 5: {retval += "\"\""; break;}
default:
{
throw internal_error(format_underline(
"parse_ml_basic_string: closing delimiter has invalid length",
{{std::addressof(inner_loc), "end of this"}}),
source_location(std::addressof(inner_loc)));
}
delim = lex_ml_basic_string_delim::invoke(inner_loc);
}
return ok(std::make_pair(toml::string(retval), token.unwrap()));
}
@@ -504,7 +485,7 @@ parse_ml_literal_string(location<Container>& loc)
{
location<std::string> inner_loc(loc.name(), token.unwrap().str());
const auto open = lex_ml_literal_string_open::invoke(inner_loc);
const auto open = lex_ml_literal_string_delim::invoke(inner_loc);
if(!open)
{
throw internal_error(format_underline(
@@ -517,7 +498,7 @@ parse_ml_literal_string(location<Container>& loc)
const auto body = lex_ml_literal_body::invoke(inner_loc);
const auto close = lex_ml_literal_string_close::invoke(inner_loc);
const auto close = lex_ml_literal_string_delim::invoke(inner_loc);
if(!close)
{
throw internal_error(format_underline(
@@ -525,29 +506,9 @@ parse_ml_literal_string(location<Container>& loc)
{{std::addressof(inner_loc), "should be '''"}}),
source_location(std::addressof(inner_loc)));
}
// `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s
// at just before the delimiter. Here, we need to attach `'`s at the
// end of the string body, if it exists.
// For detail, see the definition of `lex_ml_basic_string_close`.
std::string retval = body.unwrap().str();
assert(std::all_of(close.unwrap().first(), close.unwrap().last(),
[](const char c) noexcept {return c == '\'';}));
switch(close.unwrap().size())
{
case 3: {break;}
case 4: {retval += "'"; break;}
case 5: {retval += "''"; break;}
default:
{
throw internal_error(format_underline(
"parse_ml_literal_string: closing delimiter has invalid length",
{{std::addressof(inner_loc), "end of this"}}),
source_location(std::addressof(inner_loc)));
}
}
return ok(std::make_pair(toml::string(retval, toml::string_t::literal),
token.unwrap()));
return ok(std::make_pair(
toml::string(body.unwrap().str(), toml::string_t::literal),
token.unwrap()));
}
else
{

View File

@@ -511,7 +511,7 @@ inline std::string format_underline(const std::string& message,
retval << '\n';
retval << make_string(static_cast<std::size_t>(line_num_width + 1), ' ');
retval << color::bold << color::blue << " | " << color::reset;
for(const auto& help : helps)
for(const auto help : helps)
{
retval << color::bold << "\nHint: " << color::reset;
retval << help;

View File

@@ -4,6 +4,7 @@
#define TOML11_SERIALIZER_HPP
#include "value.hpp"
#include "lexer.hpp"
#include "visit.hpp"
#include <limits>
#include <cstdio>
@@ -119,17 +120,12 @@ struct serializer
return token; // there is no exponent part. just return it.
}
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
// Although currently it is not released yet as a tagged version,
// TOML will allow zero-prefix in an exponent part, such as `1.234e+01`.
// ```toml
// num1 = 1.234e+1 # OK in TOML v0.5.0
// num2 = 1.234e+01 # error in TOML v0.5.0 but will be allowed soon
// ```
// To avoid `e+01`, the following `else` section removes the zero
// prefixes in the exponent part.
// If the feature is activated, it can be skipped.
// 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;
#else
#endif
// zero-prefix in an exponent is NOT allowed in TOML v0.5.0.
// remove it if it exists.
bool sign_exists = false;
@@ -148,29 +144,17 @@ struct serializer
zero_prefix);
}
return token;
#endif
}
std::string operator()(const string_type& s) const
{
if(s.kind == string_t::basic)
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend())
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
{
// if linefeed or double-quote is contained,
// make it multiline basic string.
const auto escaped = this->escape_ml_basic_string(s.str);
std::string open("\"\"\"");
std::string close("\"\"\"");
if(escaped.find('\n') != std::string::npos ||
this->width_ < escaped.size() + 6)
{
// if the string body contains newline or is enough long,
// add newlines after and before delimiters.
open += "\n";
close = std::string("\\\n") + close;
}
return open + escaped + close;
// if linefeed is contained, make it multiline-string.
const std::string open("\"\"\"\n");
const std::string close("\\\n\"\"\"");
return open + this->escape_ml_basic_string(s.str) + close;
}
// no linefeed. try to make it oneline-string.
@@ -211,11 +195,7 @@ struct serializer
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
{
std::string open("'''");
if(this->width_ + 6 < s.str.size())
{
open += '\n'; // the first newline is ignored by TOML spec
}
const std::string open("'''\n");
const std::string close("'''");
return open + s.str + close;
}
@@ -510,9 +490,7 @@ struct serializer
switch(*i)
{
case '\\': {retval += "\\\\"; break;}
// One or two consecutive "s are allowed.
// Later we will check there are no three consecutive "s.
// case '\"': {retval += "\\\""; break;}
case '\"': {retval += "\\\""; break;}
case '\b': {retval += "\\b"; break;}
case '\t': {retval += "\\t"; break;}
case '\f': {retval += "\\f"; break;}
@@ -533,23 +511,6 @@ struct serializer
default: {retval += *i; break;}
}
}
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
// 3 consecutive `"`s are considered as a closing delimiter.
// We need to check if there are 3 or more consecutive `"`s and insert
// backslash to break them down into several short `"`s like the `str6`
// in the following example.
// ```toml
// str4 = """Here are two quotation marks: "". Simple enough."""
// # str5 = """Here are three quotation marks: """.""" # INVALID
// str5 = """Here are three quotation marks: ""\"."""
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
// ```
auto found_3_quotes = retval.find("\"\"\"");
while(found_3_quotes != std::string::npos)
{
retval.replace(found_3_quotes, 3, "\"\"\\\"");
found_3_quotes = retval.find("\"\"\"");
}
return retval;
}
@@ -619,7 +580,7 @@ struct serializer
// print non-table stuff first. because after printing [foo.bar], the
// remaining non-table values will be assigned into [foo.bar], not [foo]
for(const auto& kv : v)
for(const auto kv : v)
{
if(kv.second.is_table() || is_array_of_tables(kv.second))
{

View File

@@ -1995,77 +1995,5 @@ inline std::string format_error(const std::string& err_msg,
}, std::move(hints), colorize);
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, const toml::boolean&>
visit(Visitor&& visitor, const toml::basic_value<C, T, A>& v)
{
switch(v.type())
{
case value_t::boolean : {return visitor(v.as_boolean ());}
case value_t::integer : {return visitor(v.as_integer ());}
case value_t::floating : {return visitor(v.as_floating ());}
case value_t::string : {return visitor(v.as_string ());}
case value_t::offset_datetime: {return visitor(v.as_offset_datetime());}
case value_t::local_datetime : {return visitor(v.as_local_datetime ());}
case value_t::local_date : {return visitor(v.as_local_date ());}
case value_t::local_time : {return visitor(v.as_local_time ());}
case value_t::array : {return visitor(v.as_array ());}
case value_t::table : {return visitor(v.as_table ());}
case value_t::empty : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, toml::boolean&>
visit(Visitor&& visitor, toml::basic_value<C, T, A>& v)
{
switch(v.type())
{
case value_t::boolean : {return visitor(v.as_boolean ());}
case value_t::integer : {return visitor(v.as_integer ());}
case value_t::floating : {return visitor(v.as_floating ());}
case value_t::string : {return visitor(v.as_string ());}
case value_t::offset_datetime: {return visitor(v.as_offset_datetime());}
case value_t::local_datetime : {return visitor(v.as_local_datetime ());}
case value_t::local_date : {return visitor(v.as_local_date ());}
case value_t::local_time : {return visitor(v.as_local_time ());}
case value_t::array : {return visitor(v.as_array ());}
case value_t::table : {return visitor(v.as_table ());}
case value_t::empty : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, toml::boolean&&>
visit(Visitor&& visitor, toml::basic_value<C, T, A>&& v)
{
switch(v.type())
{
case value_t::boolean : {return visitor(std::move(v.as_boolean ()));}
case value_t::integer : {return visitor(std::move(v.as_integer ()));}
case value_t::floating : {return visitor(std::move(v.as_floating ()));}
case value_t::string : {return visitor(std::move(v.as_string ()));}
case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));}
case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));}
case value_t::local_date : {return visitor(std::move(v.as_local_date ()));}
case value_t::local_time : {return visitor(std::move(v.as_local_time ()));}
case value_t::array : {return visitor(std::move(v.as_array ()));}
case value_t::table : {return visitor(std::move(v.as_table ()));}
case value_t::empty : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
}// toml
#endif// TOML11_VALUE

134
toml/visit.hpp Normal file
View File

@@ -0,0 +1,134 @@
// Copyright Toru Niina 2019.
// Distributed under the MIT License.
#ifndef TOML11_VISIT_HPP
#define TOML11_VISIT_HPP
#include "value.hpp"
namespace toml
{
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, const toml::boolean&>
visit(Visitor&& visitor, const toml::basic_value<C, T, A>& v)
{
switch(v.type())
{
case value_t::boolean : {return visitor(v.as_boolean ());}
case value_t::integer : {return visitor(v.as_integer ());}
case value_t::floating : {return visitor(v.as_floating ());}
case value_t::string : {return visitor(v.as_string ());}
case value_t::offset_datetime: {return visitor(v.as_offset_datetime());}
case value_t::local_datetime : {return visitor(v.as_local_datetime ());}
case value_t::local_date : {return visitor(v.as_local_date ());}
case value_t::local_time : {return visitor(v.as_local_time ());}
case value_t::array : {return visitor(v.as_array ());}
case value_t::table : {return visitor(v.as_table ());}
case value_t::empty : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, toml::boolean&>
visit(Visitor&& visitor, toml::basic_value<C, T, A>& v)
{
switch(v.type())
{
case value_t::boolean : {return visitor(v.as_boolean ());}
case value_t::integer : {return visitor(v.as_integer ());}
case value_t::floating : {return visitor(v.as_floating ());}
case value_t::string : {return visitor(v.as_string ());}
case value_t::offset_datetime: {return visitor(v.as_offset_datetime());}
case value_t::local_datetime : {return visitor(v.as_local_datetime ());}
case value_t::local_date : {return visitor(v.as_local_date ());}
case value_t::local_time : {return visitor(v.as_local_time ());}
case value_t::array : {return visitor(v.as_array ());}
case value_t::table : {return visitor(v.as_table ());}
case value_t::empty : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
template<typename Visitor, typename C,
template<typename ...> class T, template<typename ...> class A>
detail::return_type_of_t<Visitor, toml::boolean&&>
visit(Visitor&& visitor, toml::basic_value<C, T, A>&& v)
{
switch(v.type())
{
case value_t::boolean : {return visitor(std::move(v.as_boolean ()));}
case value_t::integer : {return visitor(std::move(v.as_integer ()));}
case value_t::floating : {return visitor(std::move(v.as_floating ()));}
case value_t::string : {return visitor(std::move(v.as_string ()));}
case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));}
case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));}
case value_t::local_date : {return visitor(std::move(v.as_local_date ()));}
case value_t::local_time : {return visitor(std::move(v.as_local_time ()));}
case value_t::array : {return visitor(std::move(v.as_array ()));}
case value_t::table : {return visitor(std::move(v.as_table ()));}
case value_t::empty : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
namespace detail
{
template<typename Result, typename Visitor, typename Value>
struct visitor
{
template<typename ... Ts>
Result operator()(Ts&& ... args)
{
return vis(value, args ...);
}
Visitor vis;
Value value;
};
template<typename Result, typename Visitor, typename Value>
visitor<Result, Visitor, Value> make_visitor(Visitor&& vis, Value&& val)
{
return visitor<Result, Visitor, Value>{
std::forward<Visitor>(vis), std::forward<Value>(val)
};
}
} // detail
template<typename Visitor, typename Value, typename ... Values>
auto visit(Visitor&& visitor, Value&& v, Values&& ... vs)
-> detail::enable_if_t<detail::conjunction<
detail::is_basic_value<Value>, detail::is_basic_value<Values> ...
>::value, decltype(visitor(std::forward<Value >(v ).as_boolean(),
std::forward<Values>(vs).as_boolean()...))>
{
using result_t = decltype(visitor(v.as_boolean(), vs.as_boolean()...));
using detail::make_visitor;
switch(v.type())
{
case value_t::boolean : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_boolean ()), std::forward<Values>(vs)...);}
case value_t::integer : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_integer ()), std::forward<Values>(vs)...);}
case value_t::floating : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_floating ()), std::forward<Values>(vs)...);}
case value_t::string : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_string ()), std::forward<Values>(vs)...);}
case value_t::offset_datetime: {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_offset_datetime()), std::forward<Values>(vs)...);}
case value_t::local_datetime : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_local_datetime ()), std::forward<Values>(vs)...);}
case value_t::local_date : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_local_date ()), std::forward<Values>(vs)...);}
case value_t::local_time : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_local_time ()), std::forward<Values>(vs)...);}
case value_t::array : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_array ()), std::forward<Values>(vs)...);}
case value_t::table : {return visit(make_visitor<result_t>(std::forward<Visitor>(visitor), v.as_table ()), std::forward<Values>(vs)...);}
case value_t::empty : break;
default: break;
}
throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value "
"does not have any valid basic_value.", v, "here"));
}
} // toml
#endif// TOML11_VISIT_HPP