From 6c0a12148b504b8b4daaff49fe2ace7e5757445d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 2 Dec 2018 18:01:37 +0900 Subject: [PATCH 001/133] add result struct to handle errors aiming later updates and refactoring of parsers --- tests/CMakeLists.txt | 1 + tests/test_parser.cpp | 356 ++++++++++++++++++------------------ tests/test_result.cpp | 111 +++++++++++ toml/parser.hpp | 329 +++++++++++++++------------------ toml/result.hpp | 414 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 848 insertions(+), 363 deletions(-) create mode 100644 tests/test_result.cpp create mode 100644 toml/result.hpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 51ca609..e2056a1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,7 @@ set(TEST_NAMES test_traits test_utility + test_result test_value test_to_toml test_from_toml diff --git a/tests/test_parser.cpp b/tests/test_parser.cpp index 3a04c13..32ca96f 100644 --- a/tests/test_parser.cpp +++ b/tests/test_parser.cpp @@ -19,22 +19,22 @@ BOOST_AUTO_TEST_CASE(test_parse_basic_inline_string) const std::string source("\"simple\""); const std::string expected("simple"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("\"I'm a string. \\\"You can quote me\\\". Name\\tJos\\u00E9\\nLocation\\tSF.\""); const std::string expected("I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("dummy"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -48,30 +48,30 @@ BOOST_AUTO_TEST_CASE(test_parse_basic_multiline_string) const std::string source("\"\"\"\nRoses are red\nViolets are blue\"\"\""); const std::string expected("Roses are red\nViolets are blue"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\""); const std::string expected("The quick brown fox jumps over the lazy dog."); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("\"\"\"\nThe quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\""); const std::string expected("The quick brown fox jumps over the lazy dog."); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("dummy"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -84,38 +84,38 @@ BOOST_AUTO_TEST_CASE(test_parse_literal_inline_string) const std::string source("'C:\\Users\\nodejs\\templates'"); const std::string expected("C:\\Users\\nodejs\\templates"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("'\\\\ServerX\\admin$\\system32\\'"); const std::string expected("\\\\ServerX\\admin$\\system32\\"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("'Tom \"Dubs\" Preston-Werner'"); const std::string expected("Tom \"Dubs\" Preston-Werner"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("'<\\i\\c*\\s*>'"); const std::string expected("<\\i\\c*\\s*>"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("dummy"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -128,22 +128,22 @@ BOOST_AUTO_TEST_CASE(test_parse_literal_multiline_string) const std::string source("'''I [dw]on't need \\d{2} apples'''"); const std::string expected("I [dw]on't need \\d{2} apples"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("'''\nThe first newline is \ntrimmed in raw strings.\n All other whitespace\n is preserved.'''"); const std::string expected("The first newline is \ntrimmed in raw strings.\n All other whitespace\n is preserved."); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("dummy"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -156,38 +156,38 @@ BOOST_AUTO_TEST_CASE(test_parse_string) const std::string source("\"string\""); const std::string expected("string"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("\"\"\"string\"\"\""); const std::string expected("string"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("'string'"); const std::string expected("string"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("'''string'''"); const std::string expected("string"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("dummy"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -200,38 +200,38 @@ BOOST_AUTO_TEST_CASE(test_integer) const std::string source("42"); const toml::Integer expected(42); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("+42"); const toml::Integer expected(42); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("-42"); const toml::Integer expected(-42); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("-4_2"); const toml::Integer expected(-42); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("dummy"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -244,60 +244,60 @@ BOOST_AUTO_TEST_CASE(test_float) const std::string source("42.0"); const toml::Float expected(42.0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("+42.0"); const toml::Float expected(42.0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("-42.0"); const toml::Float expected(-42.0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("-4_2.0"); const toml::Float expected(-42.0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("-42e0"); const toml::Float expected(-42.0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("-42.0e0"); const toml::Float expected(-42.0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("dummy"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } { const std::string source("42"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -310,22 +310,22 @@ BOOST_AUTO_TEST_CASE(test_parse_boolean) const std::string source("true"); const toml::Boolean expected(true); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("false"); const toml::Boolean expected(false); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("dummy"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -340,30 +340,30 @@ BOOST_AUTO_TEST_CASE(test_parse_local_time) const std::string source("12:34:56"); const toml::Datetime expected(12, 34, 56, 0, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("12:34:56.7"); const toml::Datetime expected(12, 34, 56, 700, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("12:34:56.7891"); const toml::Datetime expected(12, 34, 56, 789, 100); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("10"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -376,14 +376,14 @@ BOOST_AUTO_TEST_CASE(test_parse_local_date) const std::string source("1979-09-27"); const toml::Datetime expected(1979, 9, 27); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("10"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -396,22 +396,22 @@ BOOST_AUTO_TEST_CASE(test_parse_local_date_time) const std::string source("1979-09-27T12:34:56"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("1979-09-27T12:34:56.789000"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("1000-11-11"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -424,16 +424,16 @@ BOOST_AUTO_TEST_CASE(test_parse_offset_date_time) const std::string source("1979-09-27T12:34:56Z"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 0, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("1979-09-27T12:34:56.789000Z"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, 0, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } @@ -441,16 +441,16 @@ BOOST_AUTO_TEST_CASE(test_parse_offset_date_time) const std::string source("1979-09-27T12:34:56+07:30"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 7, 30); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("1979-09-27T12:34:56.789000+07:30"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, 7, 30); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } @@ -458,22 +458,22 @@ BOOST_AUTO_TEST_CASE(test_parse_offset_date_time) const std::string source("1979-09-27T12:34:56-07:30"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, -7, -30); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("1979-09-27T12:34:56.789000-07:30"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, -7, -30); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("1000-11-11"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -486,38 +486,38 @@ BOOST_AUTO_TEST_CASE(test_parse_datetime) const std::string source("1979-09-27T12:34:56Z"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 0, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("1979-09-27T12:34:56"); const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("1979-09-27"); const toml::Datetime expected(1979, 9, 27); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("12:34:56"); const toml::Datetime expected(12, 34, 56, 0, 0); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK_EQUAL(result.first.get(), expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK_EQUAL(result.first.unwrap(), expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("12"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -531,56 +531,56 @@ BOOST_AUTO_TEST_CASE(test_parse_array) const std::string source("[1,2,3]"); const toml::Array expected{1, 2, 3}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[1, 2, 3]"); const toml::Array expected{1, 2, 3}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[ 1,2,3 ]"); const toml::Array expected{1, 2, 3}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[ 1 , 2 , 3 ]"); const toml::Array expected{1, 2, 3}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[ 1 \n,#comment\n 2 ,\n 3\n ]"); const toml::Array expected{1, 2, 3}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[ # empty array\n ]"); const toml::Array expected{}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[ \"] \", ' # ', \n']', # ] \n]"); const toml::Array expected{"] ", " # ", "]"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } @@ -588,8 +588,8 @@ BOOST_AUTO_TEST_CASE(test_parse_array) const std::string source("[ \"Test #11 ]proved that\", 'Experiment #9 was a success' ]"); const toml::Array expected{"Test #11 ]proved that", "Experiment #9 was a success"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } @@ -597,8 +597,8 @@ BOOST_AUTO_TEST_CASE(test_parse_array) const std::string source("[ \"Test #11 ]proved that\", 'Experiment #9 was a success' ]"); const toml::Array expected{"Test #11 ]proved that", "Experiment #9 was a success"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } @@ -606,8 +606,8 @@ BOOST_AUTO_TEST_CASE(test_parse_array) const std::string source("[ [1,2,3] , ['a', 'b', 'c'] ]"); const toml::Array expected{{1,2,3}, {"a", "b", "c"}}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } @@ -615,14 +615,14 @@ BOOST_AUTO_TEST_CASE(test_parse_array) const std::string source("[ {foo=1}, {foo=1, bar=2.0}, {foo=1, bar=2.0, baz='str'} ]"); const toml::Array expected{{{"foo", 1}}, {{"foo", 1}, {"bar", 2.0}}, {{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[dummy]"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -636,24 +636,24 @@ BOOST_AUTO_TEST_CASE(test_parse_inline_table) const std::string source("{foo=1,bar=2.0,baz='str'}"); const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("{ foo=1, bar=2.0, baz='str' }"); const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("{ foo = 1, bar = 2.0, baz = 'str' }"); const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } @@ -664,14 +664,14 @@ BOOST_AUTO_TEST_CASE(test_parse_inline_table) {"s", "str"}, {"a", {1, 2, 3}}, {"t", {{"foo", 1}}}}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("{dummy}"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.ok()); + BOOST_CHECK(!result.first.is_ok()); BOOST_CHECK(result.second == source.begin()); } } @@ -684,32 +684,32 @@ BOOST_AUTO_TEST_CASE(test_parse_barekey) const std::string source("hoge"); const toml::key expected("hoge"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("bare-key"); const toml::key expected("bare-key"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("bare_key"); const toml::key expected("bare_key"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("42"); const toml::key expected("42"); const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } } @@ -724,64 +724,64 @@ BOOST_AUTO_TEST_CASE(test_key_value_pair) const std::string source("key=1"); const std::pair expected{"key", 1}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("key =\t1"); const std::pair expected{"key", 1}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("key = true"); const std::pair expected{"key", true}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("key = -42"); const std::pair expected{"key", -42}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("key = -42.0"); const std::pair expected{"key", -42.}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("key = \"string\""); const std::pair expected{"key", "string"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("key = 1901-01-01T00:00:00"); const std::pair expected{"key", toml::Datetime(1901, 1,1,0,0,0,0,0)}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("key = [1,2,3]"); const std::pair expected{"key", {1,2,3}}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { @@ -789,8 +789,8 @@ BOOST_AUTO_TEST_CASE(test_key_value_pair) const std::pair expected{"key", {{"foo", 1}, {"bar", 2.0}, {"baz", "3"}}}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } } @@ -803,40 +803,40 @@ BOOST_AUTO_TEST_CASE(test_table_definition) const std::string source("[foo]"); const std::vector expected{"foo"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[foo.bar.baz]"); const std::vector expected{"foo", "bar", "baz"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[foo . bar. baz]"); const std::vector expected{"foo", "bar", "baz"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[foo . \"bar\" . baz]"); const std::vector expected{"foo", "bar", "baz"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[foo . \"b\\tar\" . baz]"); const std::vector expected{"foo", "b\tar", "baz"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } } @@ -849,40 +849,40 @@ BOOST_AUTO_TEST_CASE(test_array_of_table_definition) const std::string source("[[foo]]"); const std::vector expected{"foo"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[[foo.bar.baz]]"); const std::vector expected{"foo", "bar", "baz"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[[foo . bar. baz]]"); const std::vector expected{"foo", "bar", "baz"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[[foo . \"bar\" . baz]]"); const std::vector expected{"foo", "bar", "baz"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } { const std::string source("[[foo . \"b\\tar\" . baz]]"); const std::vector expected{"foo", "b\tar", "baz"}; const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.ok()); - BOOST_CHECK(result.first.get() == expected); + BOOST_CHECK(result.first.is_ok()); + BOOST_CHECK(result.first.unwrap() == expected); BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); } } diff --git a/tests/test_result.cpp b/tests/test_result.cpp new file mode 100644 index 0000000..fcc0ddd --- /dev/null +++ b/tests/test_result.cpp @@ -0,0 +1,111 @@ +#define BOOST_TEST_MODULE "test_result" +#include +#include +#include + +BOOST_AUTO_TEST_CASE(test_construct) +{ + { + auto s = toml::ok(42); + toml::result result(s); + BOOST_TEST(!!result); + BOOST_TEST(result.is_ok()); + BOOST_TEST(!result.is_err()); + BOOST_TEST(result.unwrap() == 42); + } + { + const auto s = toml::ok(42); + toml::result result(s); + BOOST_TEST(!!result); + BOOST_TEST(result.is_ok()); + BOOST_TEST(!result.is_err()); + BOOST_TEST(result.unwrap() == 42); + } + { + toml::result result(toml::ok(42)); + BOOST_TEST(!!result); + BOOST_TEST(result.is_ok()); + BOOST_TEST(!result.is_err()); + BOOST_TEST(result.unwrap() == 42); + } + + { + auto f = toml::err("foobar"); + toml::result result(f); + BOOST_TEST(!result); + BOOST_TEST(!result.is_ok()); + BOOST_TEST(result.is_err()); + BOOST_TEST(result.unwrap_err() == "foobar"); + } + { + const auto f = toml::err("foobar"); + toml::result result(f); + BOOST_TEST(!result); + BOOST_TEST(!result.is_ok()); + BOOST_TEST(result.is_err()); + BOOST_TEST(result.unwrap_err() == "foobar"); + } + { + toml::result result(toml::err("foobar")); + BOOST_TEST(!result); + BOOST_TEST(!result.is_ok()); + BOOST_TEST(result.is_err()); + BOOST_TEST(result.unwrap_err() == "foobar"); + } +} + +BOOST_AUTO_TEST_CASE(test_assignment) +{ + { + toml::result result(toml::err("foobar")); + result = toml::ok(42); + BOOST_TEST(!!result); + BOOST_TEST(result.is_ok()); + BOOST_TEST(!result.is_err()); + BOOST_TEST(result.unwrap() == 42); + } + { + toml::result result(toml::err("foobar")); + auto s = toml::ok(42); + result = s; + BOOST_TEST(!!result); + BOOST_TEST(result.is_ok()); + BOOST_TEST(!result.is_err()); + BOOST_TEST(result.unwrap() == 42); + } + { + toml::result result(toml::err("foobar")); + const auto s = toml::ok(42); + result = s; + BOOST_TEST(!!result); + BOOST_TEST(result.is_ok()); + BOOST_TEST(!result.is_err()); + BOOST_TEST(result.unwrap() == 42); + } + { + toml::result result(toml::err("foobar")); + result = toml::err("hoge"); + BOOST_TEST(!result); + BOOST_TEST(!result.is_ok()); + BOOST_TEST(result.is_err()); + BOOST_TEST(result.unwrap_err() == "hoge"); + } + { + toml::result result(toml::err("foobar")); + auto f = toml::err("hoge"); + result = f; + BOOST_TEST(!result); + BOOST_TEST(!result.is_ok()); + BOOST_TEST(result.is_err()); + BOOST_TEST(result.unwrap_err() == "hoge"); + } + { + toml::result result(toml::err("foobar")); + const auto f = toml::err("hoge"); + result = f; + BOOST_TEST(!result); + BOOST_TEST(!result.is_ok()); + BOOST_TEST(result.is_err()); + BOOST_TEST(result.unwrap_err() == "hoge"); + } +} diff --git a/toml/parser.hpp b/toml/parser.hpp index f470330..478078a 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -2,6 +2,7 @@ #define TOML11_PARSER #include "value.hpp" #include "acceptor.hpp" +#include "result.hpp" #include #include #include @@ -11,54 +12,11 @@ namespace toml { -namespace detail -{ - -// it is just an inferior vertion of boost/std::optional -template -struct result -{ - result() : ok_(false){} - ~result() = default; - result(const result& rhs) = default; - result(result&& rhs) = default; - result& operator=(const result& rhs) = default; - result& operator=(result&& rhs) = default; - - result(const T& v) : ok_(true), value_(v){} - result(T&& v) : ok_(true), value_(std::move(v)){} - result& operator=(const T& rhs){ok_ = true; value_ = rhs; return *this;} - result& operator=(T&& rhs) {ok_ = true; value_ = rhs; return *this;} - - template - result& operator=(const result& u) {ok_ = u.ok(); if(ok_)value_ = u.move(); return *this;} - template - result& operator=(result&& u) {ok_ = u.ok(); if(ok_)value_ = u.move(); return *this;} - template - result(const result& u): ok_(u.ok()){if(ok_)value_ = u.get();} - template - result(result&& u): ok_(u.ok()){if(ok_)value_ = u.move();} - - bool ok() const {return ok_;} - operator bool() const {return ok_;} - - T& get() {if(!ok_) throw std::logic_error("result::get"); return value_;} - T const& get() const {if(!ok_) throw std::logic_error("result::get"); return value_;} - T&& move() - {if(!ok_) throw std::logic_error("result::move"); ok_ = false; return std::move(value_);} - - private: - bool ok_; - T value_; -}; - -}//detail - struct parse_escape_sequence { typedef toml::character value_type; typedef toml::String string_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -67,32 +25,34 @@ struct parse_escape_sequence { const auto beg = iter; if(iter == end || *iter != '\\') - return std::make_pair(result_type{}, iter); + return std::make_pair(err("not an escape sequence"), iter); ++iter; switch(*iter) { - case '\\': return std::make_pair(string_type("\\"), std::next(iter)); - case '"' : return std::make_pair(string_type("\""), std::next(iter)); - case 'b' : return std::make_pair(string_type("\b"), std::next(iter)); - case 't' : return std::make_pair(string_type("\t"), std::next(iter)); - case 'n' : return std::make_pair(string_type("\n"), std::next(iter)); - case 'f' : return std::make_pair(string_type("\f"), std::next(iter)); - case 'r' : return std::make_pair(string_type("\r"), std::next(iter)); + case '\\': return std::make_pair(ok(string_type("\\")), std::next(iter)); + case '"' : return std::make_pair(ok(string_type("\"")), std::next(iter)); + case 'b' : return std::make_pair(ok(string_type("\b")), std::next(iter)); + case 't' : return std::make_pair(ok(string_type("\t")), std::next(iter)); + case 'n' : return std::make_pair(ok(string_type("\n")), std::next(iter)); + case 'f' : return std::make_pair(ok(string_type("\f")), std::next(iter)); + case 'r' : return std::make_pair(ok(string_type("\r")), std::next(iter)); case 'u' : { if(std::distance(iter, end) < 5) throw std::make_pair(iter, syntax_error( "invalid escape sequence: " + std::string(beg, end))); - return std::make_pair(utf8_to_char(make_codepoint( - string_type(iter+1, iter+5))), iter+5); + return std::make_pair(ok( + utf8_to_char(make_codepoint(string_type(iter+1, iter+5)))), + iter+5); } case 'U': { if(std::distance(iter, end) < 8) throw std::make_pair(iter, syntax_error( "invalid escape sequence: " + std::string(beg, end))); - return std::make_pair(utf8_to_char(make_codepoint( - string_type(iter+1, iter+9))), iter+9); + return std::make_pair(ok( + utf8_to_char(make_codepoint(string_type(iter+1, iter+9)))), + iter+9); } default: throw std::make_pair(iter, syntax_error( "unkwnon escape sequence: " + std::string(iter, end))); @@ -139,7 +99,7 @@ struct parse_escape_sequence struct parse_basic_inline_string { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -149,7 +109,7 @@ struct parse_basic_inline_string { const Iterator end = is_basic_inline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); if(std::distance(iter, end) < 2) throw internal_error("is_basic_inline_string"); @@ -161,9 +121,9 @@ struct parse_basic_inline_string if(*iter == '\\') { auto r = parse_escape_sequence::invoke(iter, last); - if(!r.first.ok()) + if(!r.first.is_ok()) throw internal_error("parse_basic_inline_string"); - result += r.first.move(); + result += r.first.unwrap(); iter = r.second; } else @@ -172,7 +132,7 @@ struct parse_basic_inline_string ++iter; } } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; @@ -180,7 +140,7 @@ struct parse_basic_multiline_string { typedef toml::character value_type; typedef toml::String string_type; - typedef detail::result result_type; + typedef result result_type; typedef is_chain_of, is_newline> is_line_ending_backslash; @@ -195,7 +155,7 @@ struct parse_basic_multiline_string { const Iterator end = is_basic_multiline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); if(std::distance(iter, end) < 6) throw internal_error("is_basic_inline_string"); @@ -214,9 +174,9 @@ struct parse_basic_multiline_string else { auto r = parse_escape_sequence::invoke(iter, last); - if(!r.first.ok()) + if(!r.first.is_ok()) throw internal_error("parse_basic_inline_string"); - result += r.first.move(); + result += r.first.unwrap(); iter = r.second; } } @@ -226,14 +186,14 @@ struct parse_basic_multiline_string ++iter; } } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; struct parse_literal_inline_string { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -243,7 +203,7 @@ struct parse_literal_inline_string { const Iterator end = is_literal_inline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); if(std::distance(iter, end) < 2) throw internal_error("is_literal_inline_string"); @@ -255,14 +215,14 @@ struct parse_literal_inline_string result.push_back(*iter); ++iter; } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; struct parse_literal_multiline_string { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; typedef is_chain_of, is_newline> is_line_ending_backslash; @@ -277,7 +237,7 @@ struct parse_literal_multiline_string { const Iterator end = is_literal_multiline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); if(std::distance(iter, end) < 6) throw internal_error("is_literal_multiline_string"); @@ -290,14 +250,14 @@ struct parse_literal_multiline_string result.push_back(*iter); ++iter; } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; struct parse_string { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -305,17 +265,17 @@ struct parse_string static std::pair invoke(Iterator iter, Iterator range_end) { - std::pair result; - if((result = parse_basic_inline_string::invoke(iter, range_end)).first.ok()) + std::pair result(err(""), Iterator()); + if((result = parse_basic_inline_string::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_basic_multiline_string::invoke(iter, range_end)).first.ok()) + else if((result = parse_basic_multiline_string::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_literal_inline_string::invoke(iter, range_end)).first.ok()) + else if((result = parse_literal_inline_string::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_literal_multiline_string::invoke(iter, range_end)).first.ok()) + else if((result = parse_literal_multiline_string::invoke(iter, range_end)).first.is_ok()) return result; else - return std::make_pair(result_type{}, iter); + return std::make_pair(err("does not match anything"), iter); } }; @@ -323,7 +283,7 @@ struct parse_integer { typedef toml::character value_type; typedef std::basic_string string_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -331,11 +291,11 @@ struct parse_integer static std::pair invoke(Iterator iter, Iterator range_end) { const Iterator end = is_integer::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); string_type result; result.resize(std::distance(iter, end)); std::copy_if(iter, end, result.begin(), [](value_type c){return c != '_';}); - return std::make_pair(std::stoll(result), end); + return std::make_pair(ok(std::stoll(result)), end); } }; @@ -343,7 +303,7 @@ struct parse_float { typedef toml::character value_type; typedef std::basic_string string_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -352,18 +312,18 @@ struct parse_float invoke(Iterator iter, Iterator range_end) { const Iterator end = is_float::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); string_type result; result.resize(std::distance(iter, end)); std::copy_if(iter, end, result.begin(), [](value_type c){return c != '_';}); try{ - return std::make_pair(std::stod(result), end); + return std::make_pair(ok(std::stod(result)), end); } catch(std::out_of_range& oor) { std::cout << "extremely large Float value appeared: " << result << "; it is negrected" << std::endl; - return std::make_pair(0, end); + return std::make_pair(ok(0.0), end); } } }; @@ -371,7 +331,7 @@ struct parse_float struct parse_boolean { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -380,8 +340,8 @@ struct parse_boolean invoke(Iterator iter, Iterator range_end) { const Iterator end = is_boolean::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - return std::make_pair((std::distance(iter, end) == 4), end); + if(iter == end) return std::make_pair(err("input is empty"), iter); + return std::make_pair(ok(std::distance(iter, end) == 4), end); } }; @@ -389,7 +349,7 @@ struct parse_local_time { typedef toml::character value_type; typedef std::basic_string string_type; - typedef detail::result result_type; + typedef result result_type; typedef typename toml::Datetime::number_type number_type; template using nums = is_repeat_of, N>; @@ -403,7 +363,7 @@ struct parse_local_time invoke(Iterator iter, Iterator range_end) { const Iterator end = is_local_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); toml::Datetime result; result.hour = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); @@ -432,7 +392,7 @@ struct parse_local_time result.year = toml::Datetime::undef; result.month = toml::Datetime::undef; result.day = toml::Datetime::undef; - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } template string_type; - typedef detail::result result_type; + typedef result result_type; template using nums = is_repeat_of, N>; typedef is_character delim; @@ -463,7 +423,7 @@ struct parse_local_date invoke(Iterator iter, Iterator range_end) { const Iterator end = is_local_date::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); toml::Datetime result; result.year = std::stoi(string_type(iter, nums<4>::invoke(iter, end))); @@ -479,7 +439,7 @@ struct parse_local_date result.second = toml::Datetime::undef; result.millisecond = toml::Datetime::undef; result.microsecond = toml::Datetime::undef; - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; @@ -487,7 +447,7 @@ struct parse_local_date_time { typedef toml::character value_type; typedef std::basic_string string_type; - typedef detail::result result_type; + typedef result result_type; template using nums = is_repeat_of, N>; typedef is_character delim; @@ -500,23 +460,23 @@ struct parse_local_date_time { const Iterator end = is_local_date_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); auto ld = parse_local_date::invoke(iter, end); - if(!ld.first.ok()) + if(!ld.first.is_ok()) throw std::make_pair(iter, syntax_error("invalid local datetime")); - toml::Datetime result(ld.first.move()); + toml::Datetime result(ld.first.unwrap()); iter = delim::invoke(ld.second, end);// 'T' const auto time = parse_local_time::invoke(iter, end); - result.hour = time.first.get().hour; - result.minute = time.first.get().minute; - result.second = time.first.get().second; - result.millisecond = time.first.get().millisecond; - result.microsecond = time.first.get().microsecond; + result.hour = time.first.unwrap().hour; + result.minute = time.first.unwrap().minute; + result.second = time.first.unwrap().second; + result.millisecond = time.first.unwrap().millisecond; + result.microsecond = time.first.unwrap().microsecond; result.offset_hour = toml::Datetime::nooffset; result.offset_minute = toml::Datetime::nooffset; - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; @@ -524,7 +484,7 @@ struct parse_offset_date_time { typedef toml::character value_type; typedef std::basic_string string_type; - typedef detail::result result_type; + typedef result result_type; template using nums = is_repeat_of, N>; typedef is_character delim; @@ -537,12 +497,12 @@ struct parse_offset_date_time { const Iterator end = is_offset_date_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); auto ldt = parse_local_date_time::invoke(iter, end); - if(!ldt.first.ok()) + if(!ldt.first.is_ok()) throw std::make_pair(iter, syntax_error("invalid offset datetime")); - toml::Datetime result(ldt.first.move()); + toml::Datetime result(ldt.first.unwrap()); iter = ldt.second; if(*iter == 'Z') { @@ -561,14 +521,14 @@ struct parse_offset_date_time result.offset_minute = sign * std::stoi(string_type(iter, nums<2>::invoke(iter, end))); } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; struct parse_datetime { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -576,17 +536,17 @@ struct parse_datetime static std::pair invoke(Iterator iter, Iterator range_end) { - std::pair result; - if((result = parse_offset_date_time::invoke(iter, range_end)).first.ok()) + std::pair result(err(""), Iterator()); + if((result = parse_offset_date_time::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_local_date_time::invoke(iter, range_end)).first.ok()) + else if((result = parse_local_date_time::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_local_date::invoke(iter, range_end)).first.ok()) + else if((result = parse_local_date::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_local_time::invoke(iter, range_end)).first.ok()) + else if((result = parse_local_time::invoke(iter, range_end)).first.is_ok()) return result; else - return std::make_pair(result_type{}, iter); + return std::make_pair(err("does not match anything"), iter); } }; @@ -594,7 +554,7 @@ template struct parse_fixed_type_array { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; typedef acceptorT acceptor_type; typedef parserT parser_type; typedef is_skippable_in_array skippable; @@ -606,7 +566,7 @@ struct parse_fixed_type_array { const Iterator end = is_fixed_type_array::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); toml::Array result; const Iterator last = std::prev(end); @@ -617,15 +577,15 @@ struct parse_fixed_type_array if(tmp == iter) throw std::make_pair(iter, syntax_error("parse_array")); auto next = parser_type::invoke(iter, last); - if(!next.first.ok()) + if(!next.first.is_ok()) throw std::make_pair(iter, syntax_error("parse_array")); - result.emplace_back(next.first.move()); + result.emplace_back(next.first.unwrap()); iter = tmp; iter = skippable::invoke(iter, last); iter = is_character::invoke(iter, last); iter = skippable::invoke(iter, last); } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; @@ -637,7 +597,7 @@ struct parse_array { typedef charT value_type; static_assert(std::is_same::value, ""); - typedef detail::result result_type; + typedef result result_type; typedef is_skippable_in_array skippable; template::invoke(iter, range_end)) - return std::make_pair(result_type{}, iter); + return std::make_pair(err("input is not an array"), iter); - std::pair result; + std::pair result(err(""), Iterator()); if((result = parse_fixed_type_array, - parse_boolean>::invoke(iter, range_end)).first.ok()) return result; + parse_boolean>::invoke(iter, range_end)).first.is_ok()) return result; else if((result = parse_fixed_type_array, - parse_string>::invoke(iter, range_end)).first.ok()) return result; + parse_string>::invoke(iter, range_end)).first.is_ok()) return result; else if((result = parse_fixed_type_array, - parse_datetime>::invoke(iter, range_end)).first.ok()) return result; + parse_datetime>::invoke(iter, range_end)).first.is_ok()) return result; else if((result = parse_fixed_type_array, - parse_float>::invoke(iter, range_end)).first.ok()) return result; + parse_float>::invoke(iter, range_end)).first.is_ok()) return result; else if((result = parse_fixed_type_array, - parse_integer>::invoke(iter, range_end)).first.ok()) return result; + parse_integer>::invoke(iter, range_end)).first.is_ok()) return result; else if((result = parse_fixed_type_array, - parse_array>::invoke(iter, range_end)).first.ok()) return result; + parse_array>::invoke(iter, range_end)).first.is_ok()) return result; else if((result = parse_fixed_type_array, - parse_inline_table>::invoke(iter, range_end)).first.ok()) + parse_inline_table>::invoke(iter, range_end)).first.is_ok()) return result; else if(skippable::invoke(std::next(iter), range_end) == // empty std::prev(is_array::invoke(iter, range_end)) ) return std::make_pair( - toml::Array{}, is_array::invoke(iter, range_end)); + ok(toml::Array{}), is_array::invoke(iter, range_end)); else throw std::make_pair(iter, syntax_error("no valid array here")); } }; @@ -678,7 +638,7 @@ struct parse_value { typedef charT value_type; static_assert(std::is_same::value, ""); - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -686,30 +646,30 @@ struct parse_value static std::pair invoke(Iterator iter, Iterator range_end) { - std::pair result; - if((result = parse_boolean::invoke(iter, range_end)).first.ok()) + std::pair result(err(""), Iterator()); + if((result = parse_boolean::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_string::invoke(iter, range_end)).first.ok()) + else if((result = parse_string::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_datetime::invoke(iter, range_end)).first.ok()) + else if((result = parse_datetime::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_float::invoke(iter, range_end)).first.ok()) + else if((result = parse_float::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_integer::invoke(iter, range_end)).first.ok()) + else if((result = parse_integer::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_array::invoke(iter, range_end)).first.ok()) + else if((result = parse_array::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_inline_table::invoke(iter, range_end)).first.ok()) + else if((result = parse_inline_table::invoke(iter, range_end)).first.is_ok()) return result; else - return std::make_pair(result_type{}, iter); + return std::make_pair(err("does not match any value type"), iter); } }; struct parse_barekey { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -718,15 +678,15 @@ struct parse_barekey invoke(Iterator iter, Iterator range_end) { const Iterator end = is_barekey::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); - return std::make_pair(toml::key(iter, end), end); + if(iter == end) return std::make_pair(err("input is empty"), iter); + return std::make_pair(ok(toml::key(iter, end)), end); } }; struct parse_key { typedef toml::character value_type; - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -734,12 +694,12 @@ struct parse_key static std::pair invoke(Iterator iter, Iterator range_end) { - std::pair result; - if((result = parse_barekey::invoke(iter, range_end)).first.ok()) + std::pair result(err(""), Iterator()); + if((result = parse_barekey::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_string::invoke(iter, range_end)).first.ok()) + else if((result = parse_string::invoke(iter, range_end)).first.is_ok()) return result; - else return std::make_pair(result_type{}, iter); + else return std::make_pair(err("does not match anything"), iter); } }; @@ -748,7 +708,7 @@ struct parse_key_value_pair { typedef charT value_type; static_assert(std::is_same::value, ""); - typedef detail::result> result_type; + typedef result, std::string> result_type; template::value_type, @@ -757,21 +717,21 @@ struct parse_key_value_pair invoke(Iterator iter, Iterator range_end) { auto tmp_key = parse_key::invoke(iter, range_end); - if(!tmp_key.first.ok()) - return std::make_pair(result_type{}, iter); + if(!tmp_key.first.is_ok()) + return std::make_pair(err(tmp_key.first.unwrap_err()), iter); iter = is_any_num_of_ws::invoke(tmp_key.second, range_end); if(*iter != '=') throw std::make_pair(iter, syntax_error("invalid key value pair")); iter = is_any_num_of_ws::invoke(std::next(iter), range_end); auto tmp_value = parse_value::invoke(iter, range_end); - if(!tmp_value.first.ok()) + if(!tmp_value.first.is_ok()) throw std::make_pair(iter, syntax_error("invalid key value pair")); iter = tmp_value.second; - return std::make_pair(std::make_pair( - tmp_key.first.move(), tmp_value.first.move()), + return std::make_pair(ok(std::make_pair( + tmp_key.first.unwrap(), tmp_value.first.unwrap())), is_any_num_of_ws::invoke(tmp_value.second, range_end)); } }; @@ -781,7 +741,7 @@ struct parse_inline_table { typedef charT value_type; static_assert(std::is_same::value, ""); - typedef detail::result result_type; + typedef result result_type; template::value_type, @@ -790,7 +750,7 @@ struct parse_inline_table invoke(Iterator iter, Iterator range_end) { const Iterator end = is_inline_table::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); iter = is_any_num_of_ws::invoke(std::next(iter), range_end); @@ -799,24 +759,24 @@ struct parse_inline_table while(iter != last) { auto tmp = parse_key_value_pair::invoke(iter, last); - if(!tmp.first.ok()) + if(!tmp.first.is_ok()) throw std::make_pair(iter, syntax_error("parse_inline_table")); - result.emplace(tmp.first.move()); + result.emplace(tmp.first.unwrap()); iter = tmp.second; iter = is_any_num_of_ws::invoke(iter, last); iter = is_character::invoke(iter, last); iter = is_any_num_of_ws::invoke(iter, last); } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; struct parse_table_definition { typedef toml::character value_type; - typedef detail::result> result_type; + typedef result, std::string> result_type; template::value_type, @@ -826,7 +786,7 @@ struct parse_table_definition { const Iterator end = is_table_definition::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); std::vector result; result.reserve(std::count(iter, end, '.')+1); @@ -836,9 +796,9 @@ struct parse_table_definition iter = is_any_num_of_ws::invoke(std::next(iter), last); auto tmp = parse_key::invoke(iter, last); - if(!tmp.first.ok()) + if(!tmp.first.is_ok()) throw std::make_pair(iter, syntax_error("table definition")); - result.emplace_back(tmp.first.move()); + result.emplace_back(tmp.first.unwrap()); iter = is_any_num_of_ws::invoke(tmp.second, last); while(iter != last) @@ -847,19 +807,19 @@ struct parse_table_definition iter = is_any_num_of_ws::invoke(iter, last); tmp = parse_key::invoke(iter, last); - if(!tmp.first.ok()) + if(!tmp.first.is_ok()) throw std::make_pair(iter, syntax_error("table definition")); - result.emplace_back(tmp.first.move()); + result.emplace_back(tmp.first.unwrap()); iter = is_any_num_of_ws::invoke(tmp.second, last); } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; struct parse_array_of_table_definition { typedef toml::character value_type; - typedef detail::result> result_type; + typedef result, std::string> result_type; template::value_type, @@ -869,7 +829,7 @@ struct parse_array_of_table_definition { const Iterator end = is_array_of_table_definition::invoke(iter, range_end); - if(iter == end) return std::make_pair(result_type{}, iter); + if(iter == end) return std::make_pair(err("input is empty"), iter); if(std::distance(iter, end) < 5) throw std::make_pair(iter, syntax_error("invalid array_of_table definition")); @@ -882,9 +842,9 @@ struct parse_array_of_table_definition iter = is_any_num_of_ws::invoke(iter, last); auto tmp = parse_key::invoke(iter, last); - if(!tmp.first.ok()) + if(!tmp.first.is_ok()) throw std::make_pair(iter, syntax_error("array of table definition")); - result.emplace_back(tmp.first.move()); + result.emplace_back(tmp.first.unwrap()); iter = is_any_num_of_ws::invoke(tmp.second, last); while(iter != last) @@ -893,12 +853,12 @@ struct parse_array_of_table_definition iter = is_any_num_of_ws::invoke(iter, last); tmp = parse_key::invoke(iter, last); - if(!tmp.first.ok()) + if(!tmp.first.is_ok()) throw std::make_pair(iter, syntax_error("array of table definition")); - result.emplace_back(tmp.first.move()); + result.emplace_back(tmp.first.unwrap()); iter = is_any_num_of_ws::invoke(tmp.second, last); } - return std::make_pair(result, end); + return std::make_pair(ok(result), end); } }; @@ -920,19 +880,20 @@ struct parse_data while(iter != end) { iter = skip_empty(iter, end); - std::pair>, Iterator> tabname; - if((tabname = parse_table_definition::invoke(iter, end)).first.ok()) + std::pair<::toml::result, std::string>, Iterator> + tabname(err(""), Iterator()); + if((tabname = parse_table_definition::invoke(iter, end)).first.is_ok()) { auto contents = parse_table_contents(tabname.second, end); push_table(result, std::move(contents.first), - tabname.first.get().begin(), tabname.first.get().end()); + tabname.first.unwrap().begin(), tabname.first.unwrap().end()); iter = contents.second; } - else if((tabname = parse_array_of_table_definition::invoke(iter, end)).first.ok()) + else if((tabname = parse_array_of_table_definition::invoke(iter, end)).first.is_ok()) { auto contents = parse_table_contents(tabname.second, end); push_array_of_table(result, std::move(contents.first), - tabname.first.get().begin(), tabname.first.get().end()); + tabname.first.unwrap().begin(), tabname.first.unwrap().end()); iter = contents.second; } else @@ -978,9 +939,9 @@ struct parse_data while(iter != end) { auto kv = parse_key_value_pair::invoke(iter, end); - if(!kv.first.ok()) return std::make_pair(table, iter); + if(!kv.first.is_ok()) return std::make_pair(table, iter); - table.emplace(kv.first.move()); + table.emplace(kv.first.unwrap()); iter = kv.second; iter = skip_empty(iter, end); } @@ -1124,7 +1085,5 @@ inline toml::Table parse(const std::basic_string& filename) return parse(ifs); } - - }// toml #endif// TOML11_PARSER diff --git a/toml/result.hpp b/toml/result.hpp new file mode 100644 index 0000000..a6276ef --- /dev/null +++ b/toml/result.hpp @@ -0,0 +1,414 @@ +#ifndef TOML11_RESULT_H +#define TOML11_RESULT_H +#include +#include +#include +#include + +namespace toml +{ + +template +struct success +{ + using value_type = T; + value_type value; + + explicit success(const value_type& v) + noexcept(std::is_nothrow_copy_constructible::value) + : value(v) + {} + explicit success(value_type&& v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template + explicit success(U&& v): value(std::forward(v)) {} + + template + explicit success(const success& v): value(v.value) {} + template + explicit success(success&& v): value(std::move(v.value)) {} + + ~success() = default; + success(const success&) = default; + success(success&&) = default; + success& operator=(const success&) = default; + success& operator=(success&&) = default; +}; + +template +struct failure +{ + using value_type = T; + value_type value; + + explicit failure(const value_type& v) + noexcept(std::is_nothrow_copy_constructible::value) + : value(v) + {} + explicit failure(value_type&& v) + noexcept(std::is_nothrow_move_constructible::value) + : value(std::move(v)) + {} + + template + explicit failure(U&& v): value(std::forward(v)) {} + + template + explicit failure(const failure& v): value(v.value) {} + template + explicit failure(failure&& v): value(std::move(v.value)) {} + + ~failure() = default; + failure(const failure&) = default; + failure(failure&&) = default; + failure& operator=(const failure&) = default; + failure& operator=(failure&&) = default; +}; + +template +success::type>::type> +ok(T&& v) +{ + return success< + typename std::remove_cv::type>::type + >(std::forward(v)); +} +template +failure::type>::type> +err(T&& v) +{ + return failure< + typename std::remove_cv::type>::type + >(std::forward(v)); +} + +inline success ok(const char* literal) +{ + return success(std::string(literal)); +} +inline failure err(const char* literal) +{ + return failure(std::string(literal)); +} + + +template +struct result +{ + using value_type = T; + using error_type = E; + using success_type = success; + using failure_type = failure; + + result(const success_type& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(s); + assert(tmp == std::addressof(this->succ)); + } + result(const failure_type& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(f); + assert(tmp == std::addressof(this->fail)); + } + result(success_type&& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); + assert(tmp == std::addressof(this->succ)); + } + result(failure_type&& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); + assert(tmp == std::addressof(this->fail)); + } + + template + result(const success& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); + assert(tmp == std::addressof(this->succ)); + } + template + result(const failure& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); + assert(tmp == std::addressof(this->fail)); + } + template + result(success&& s): is_ok_(true) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); + assert(tmp == std::addressof(this->succ)); + } + template + result(failure&& f): is_ok_(false) + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); + assert(tmp == std::addressof(this->fail)); + } + + result& operator=(const success_type& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(s); + assert(tmp == std::addressof(this->succ)); + return *this; + } + result& operator=(const failure_type& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(f); + assert(tmp == std::addressof(this->fail)); + return *this; + } + result& operator=(success_type&& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); + assert(tmp == std::addressof(this->succ)); + return *this; + } + result& operator=(failure_type&& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); + assert(tmp == std::addressof(this->fail)); + return *this; + } + + template + result& operator=(const success& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); + assert(tmp == std::addressof(this->succ)); + return *this; + } + template + result& operator=(const failure& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); + assert(tmp == std::addressof(this->fail)); + return *this; + } + template + result& operator=(success&& s) + { + this->cleanup(); + this->is_ok_ = true; + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); + assert(tmp == std::addressof(this->succ)); + return *this; + } + template + result& operator=(failure&& f) + { + this->cleanup(); + this->is_ok_ = false; + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); + assert(tmp == std::addressof(this->fail)); + return *this; + } + + ~result() noexcept {this->cleanup();} + + result(const result& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + } + result(result&& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + } + + template + result(const result& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + } + template + result(result&& other): is_ok_(other.is_ok()) + { + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + } + + result& operator=(const result& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + is_ok_ = other.is_ok(); + return *this; + } + result& operator=(result&& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + } + is_ok_ = other.is_ok(); + return *this; + } + + template + result& operator=(const result& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + assert(tmp == std::addressof(this->fail)); + } + is_ok_ = other.is_ok(); + return *this; + } + template + result& operator=(result&& other) + { + this->cleanup(); + if(other.is_ok()) + { + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); + assert(tmp == std::addressof(this->succ)); + } + else + { + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); + assert(tmp == std::addressof(this->fail)); + } + is_ok_ = other.is_ok(); + return *this; + } + + bool is_ok() const noexcept {return is_ok_;} + bool is_err() const noexcept {return !is_ok_;} + + operator bool() const noexcept {return is_ok_;} + + value_type& unwrap() & + { + if(is_err()) {throw std::runtime_error("result: bad unwrap");} + return this->succ.value; + } + value_type const& unwrap() const& + { + if(is_err()) {throw std::runtime_error("result: bad unwrap");} + return this->succ.value; + } + value_type&& unwrap() && + { + if(is_err()) {throw std::runtime_error("result: bad unwrap");} + return std::move(this->succ.value); + } + + error_type& unwrap_err() & + { + if(is_ok()) {throw std::runtime_error("result: bad unwrap_err");} + return this->fail.value; + } + error_type const& unwrap_err() const& + { + if(is_ok()) {throw std::runtime_error("result: bad unwrap_err");} + return this->fail.value; + } + error_type&& unwrap_err() && + { + if(is_ok()) {throw std::runtime_error("result: bad unwrap_err");} + return std::move(this->fail.value); + } + + value_type& as_ok() & noexcept {return this->succ.value;} + value_type const& as_ok() const& noexcept {return this->succ.value;} + value_type&& as_ok() && noexcept {return std::move(this->succ.value);} + + error_type& as_err() & noexcept {return this->fail.value;} + error_type const& as_err() const& noexcept {return this->fail.value;} + error_type&& as_err() && noexcept {return std::move(this->fail.value);} + + private: + + void cleanup() noexcept + { + if(this->is_ok_) {this->succ.~success_type();} + else {this->fail.~failure_type();} + return; + } + + private: + + bool is_ok_; + union + { + success_type succ; + failure_type fail; + }; +}; + + +} // toml11 +#endif// TOML11_RESULT_H From b53d11ce79d82e7ae2b34ac36c048960c6826c44 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 2 Dec 2018 18:25:09 +0900 Subject: [PATCH 002/133] ci: downgrade test modules, use BOOST_CHECK Because travis.CI installs old, out-of-date version of Boost, BOOST_TEST is not supported. we need to use BOOST_CHECK to link with it. --- tests/test_result.cpp | 96 +++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/tests/test_result.cpp b/tests/test_result.cpp index fcc0ddd..ea2249c 100644 --- a/tests/test_result.cpp +++ b/tests/test_result.cpp @@ -8,49 +8,49 @@ BOOST_AUTO_TEST_CASE(test_construct) { auto s = toml::ok(42); toml::result result(s); - BOOST_TEST(!!result); - BOOST_TEST(result.is_ok()); - BOOST_TEST(!result.is_err()); - BOOST_TEST(result.unwrap() == 42); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); } { const auto s = toml::ok(42); toml::result result(s); - BOOST_TEST(!!result); - BOOST_TEST(result.is_ok()); - BOOST_TEST(!result.is_err()); - BOOST_TEST(result.unwrap() == 42); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); } { toml::result result(toml::ok(42)); - BOOST_TEST(!!result); - BOOST_TEST(result.is_ok()); - BOOST_TEST(!result.is_err()); - BOOST_TEST(result.unwrap() == 42); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); } { auto f = toml::err("foobar"); toml::result result(f); - BOOST_TEST(!result); - BOOST_TEST(!result.is_ok()); - BOOST_TEST(result.is_err()); - BOOST_TEST(result.unwrap_err() == "foobar"); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "foobar"); } { const auto f = toml::err("foobar"); toml::result result(f); - BOOST_TEST(!result); - BOOST_TEST(!result.is_ok()); - BOOST_TEST(result.is_err()); - BOOST_TEST(result.unwrap_err() == "foobar"); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "foobar"); } { toml::result result(toml::err("foobar")); - BOOST_TEST(!result); - BOOST_TEST(!result.is_ok()); - BOOST_TEST(result.is_err()); - BOOST_TEST(result.unwrap_err() == "foobar"); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "foobar"); } } @@ -59,53 +59,53 @@ BOOST_AUTO_TEST_CASE(test_assignment) { toml::result result(toml::err("foobar")); result = toml::ok(42); - BOOST_TEST(!!result); - BOOST_TEST(result.is_ok()); - BOOST_TEST(!result.is_err()); - BOOST_TEST(result.unwrap() == 42); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); } { toml::result result(toml::err("foobar")); auto s = toml::ok(42); result = s; - BOOST_TEST(!!result); - BOOST_TEST(result.is_ok()); - BOOST_TEST(!result.is_err()); - BOOST_TEST(result.unwrap() == 42); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); } { toml::result result(toml::err("foobar")); const auto s = toml::ok(42); result = s; - BOOST_TEST(!!result); - BOOST_TEST(result.is_ok()); - BOOST_TEST(!result.is_err()); - BOOST_TEST(result.unwrap() == 42); + BOOST_CHECK(!!result); + BOOST_CHECK(result.is_ok()); + BOOST_CHECK(!result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap(), 42); } { toml::result result(toml::err("foobar")); result = toml::err("hoge"); - BOOST_TEST(!result); - BOOST_TEST(!result.is_ok()); - BOOST_TEST(result.is_err()); - BOOST_TEST(result.unwrap_err() == "hoge"); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge"); } { toml::result result(toml::err("foobar")); auto f = toml::err("hoge"); result = f; - BOOST_TEST(!result); - BOOST_TEST(!result.is_ok()); - BOOST_TEST(result.is_err()); - BOOST_TEST(result.unwrap_err() == "hoge"); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge"); } { toml::result result(toml::err("foobar")); const auto f = toml::err("hoge"); result = f; - BOOST_TEST(!result); - BOOST_TEST(!result.is_ok()); - BOOST_TEST(result.is_err()); - BOOST_TEST(result.unwrap_err() == "hoge"); + BOOST_CHECK(!result); + BOOST_CHECK(!result.is_ok()); + BOOST_CHECK(result.is_err()); + BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge"); } } From c38b9b7dc7ad60fde51fb9fbe6dfcce172728264 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 2 Dec 2018 20:52:04 +0900 Subject: [PATCH 003/133] add region and location to represent tokens location is almost same as an Iterator, but having shared_ptr that points the content. region is almost same as a range. by adding pointer to the content source, utility function to show the error message can be implemented easier. it is expected that this also makes easy to show error messages after parse (e.g., in the case of bad_get) --- toml/region.hpp | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 toml/region.hpp diff --git a/toml/region.hpp b/toml/region.hpp new file mode 100644 index 0000000..e9ced53 --- /dev/null +++ b/toml/region.hpp @@ -0,0 +1,136 @@ +#ifndef TOML11_REGION_H +#define TOML11_REGION_H +#include +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// location in a container, normally in a file content. +// shared_ptr points the resource that the iter points. +// it can be used not only for resource handling, but also error message. +template +struct location +{ + using const_iterator = typename Container::const_iterator; + + location(Container cont) + : source(std::make_shared(std::move(cont))), + iter(source->begin()) + {} + location(const location&) = default; + location(location&&) = default; + location& operator=(const location&) = default; + location& operator=(location&&) = default; + ~location() = default; + + std::shared_ptr source; + const_iterator iter; +}; + +// region in a container, normally in a file content. +// shared_ptr points the resource that the iter points. +// combinators returns this. +// it can be used not only for resource handling, but also error message. +template +struct region +{ + using const_iterator = typename Container::const_iterator; + + region(const location& loc) + : first(loc.iter), last(loc.iter), source(loc.source) + {} + region(location&& loc) + : first(loc.iter), last(loc.iter), source(std::move(loc.source)) + {} + + region(const region&) = default; + region(region&&) = default; + region& operator=(const region&) = default; + region& operator=(region&&) = default; + ~region() = default; + + const_iterator first, last; + std::shared_ptr source; +}; + +// to show a better error message. +template +std::string +format_underline(const region& reg, const std::string& msg) +{ + using const_iterator = typename region::const_iterator; + using reverse_iterator = std::reverse_iterator; + const auto line_begin = std::find( + reverse_iterator(reg.first), + reverse_iterator(reg.source->cbegin()), + '\n').base(); + const auto line_end = std::find(reg.last, reg.source->cend(), '\n'); + + const auto line_number = std::to_string( + 1 + std::count(reg.source->cbegin(), reg.first, '\n')); + + std::string retval; + retval += ' '; + retval += line_number; + retval += " | "; + retval += std::string(line_begin, line_end); + retval += '\n'; + retval += std::string(line_number.size() + 1, ' '); + retval += " | "; + retval += std::string(std::distance(line_begin, reg.first), ' '); + retval += std::string(std::distance(reg.first, reg.last), '~'); + retval += ' '; + retval += msg; + return retval; +} + +// to show a better error message. +template +std::string format_underline(const region& reg, + typename Container::const_iterator pos, + const std::string& msg) +{ + using const_iterator = typename region::const_iterator; + using reverse_iterator = std::reverse_iterator; + const auto line_begin = std::find( + reverse_iterator(reg.first), + reverse_iterator(reg.source->cbegin()), + '\n').base(); + const auto line_end = std::find(reg.last, reg.source->cend(), '\n'); + + const auto line_number = std::to_string( + 1 + std::count(reg.source->cbegin(), reg.first, '\n')); + + std::string retval; + retval += ' '; + retval += line_number; + retval += " | "; + retval += std::string(line_begin, line_end); + retval += '\n'; + retval += std::string(line_number.size() + 1, ' '); + retval += " | "; + retval += std::string(std::distance(line_begin, reg.first), ' '); + + if(std::distance(reg.first, std::prev(pos)) > 0) + { + retval += std::string(std::distance(reg.first, std::prev(pos)), '-'); + } + retval += '^'; + if(std::distance(pos, reg.last) > 0) + { + retval += std::string(std::distance(std::next(pos), reg.last), '-'); + } + + retval += ' '; + retval += msg; + return retval; +} + +} // detail +} // toml +#endif// TOML11_REGION_H From 9c1bfbd5ebf9d35fe5ba38a3d856f26df48e5a9f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 2 Dec 2018 21:03:08 +0900 Subject: [PATCH 004/133] make region::source immutable --- toml/region.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index e9ced53..5221697 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -28,7 +28,7 @@ struct location location& operator=(location&&) = default; ~location() = default; - std::shared_ptr source; + std::shared_ptr source; const_iterator iter; }; @@ -55,7 +55,7 @@ struct region ~region() = default; const_iterator first, last; - std::shared_ptr source; + std::shared_ptr source; }; // to show a better error message. From 2ee8ffab21cbef4304003a5b7dc02d452cb19715 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 2 Dec 2018 21:54:39 +0900 Subject: [PATCH 005/133] add begin/end to region --- toml/region.hpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 5221697..a96ba03 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -19,8 +19,8 @@ struct location using const_iterator = typename Container::const_iterator; location(Container cont) - : source(std::make_shared(std::move(cont))), - iter(source->begin()) + : source(std::make_shared(std::move(cont))), + begin(source->cbegin()), iter(source->cbegin()), end(source->cend()) {} location(const location&) = default; location(location&&) = default; @@ -29,7 +29,7 @@ struct location ~location() = default; std::shared_ptr source; - const_iterator iter; + const_iterator begin, iter, end; }; // region in a container, normally in a file content. @@ -42,10 +42,12 @@ struct region using const_iterator = typename Container::const_iterator; region(const location& loc) - : first(loc.iter), last(loc.iter), source(loc.source) + : begin(loc.begin), first(loc.iter), last(loc.iter), end(loc.end), + source(loc.source) {} region(location&& loc) - : first(loc.iter), last(loc.iter), source(std::move(loc.source)) + : begin(loc.begin), first(loc.iter), last(loc.iter), end(loc.end), + source(std::move(loc.source)) {} region(const region&) = default; @@ -54,7 +56,7 @@ struct region region& operator=(region&&) = default; ~region() = default; - const_iterator first, last; + const_iterator begin, first, last, end; std::shared_ptr source; }; @@ -67,12 +69,12 @@ format_underline(const region& reg, const std::string& msg) using reverse_iterator = std::reverse_iterator; const auto line_begin = std::find( reverse_iterator(reg.first), - reverse_iterator(reg.source->cbegin()), + reverse_iterator(reg.begin), '\n').base(); - const auto line_end = std::find(reg.last, reg.source->cend(), '\n'); + const auto line_end = std::find(reg.last, reg.end, '\n'); const auto line_number = std::to_string( - 1 + std::count(reg.source->cbegin(), reg.first, '\n')); + 1 + std::count(reg.begin, reg.first, '\n')); std::string retval; retval += ' '; @@ -99,12 +101,12 @@ std::string format_underline(const region& reg, using reverse_iterator = std::reverse_iterator; const auto line_begin = std::find( reverse_iterator(reg.first), - reverse_iterator(reg.source->cbegin()), + reverse_iterator(reg.begin), '\n').base(); - const auto line_end = std::find(reg.last, reg.source->cend(), '\n'); + const auto line_end = std::find(reg.last, reg.end, '\n'); const auto line_number = std::to_string( - 1 + std::count(reg.source->cbegin(), reg.first, '\n')); + 1 + std::count(reg.begin, reg.first, '\n')); std::string retval; retval += ' '; From 8bf97d8a0019ffad338795031ad9a0477de926fd Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 2 Dec 2018 23:04:49 +0900 Subject: [PATCH 006/133] add constructors that receive range to region --- toml/region.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/toml/region.hpp b/toml/region.hpp index a96ba03..df97f0d 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -50,6 +50,15 @@ struct region source(std::move(loc.source)) {} + region(const location& loc, const_iterator f, const_iterator l) + : begin(loc.begin), first(f), last(l), end(loc.end), + source(loc.source) + {} + region(location&& loc, const_iterator f, const_iterator l) + : begin(loc.begin), first(f), last(l), end(loc.end), + source(std::move(loc.source)) + {} + region(const region&) = default; region(region&&) = default; region& operator=(const region&) = default; From f83a8b450e68441a4f76ef3e71071a0bc80050b1 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 2 Dec 2018 23:05:15 +0900 Subject: [PATCH 007/133] add concat_to_string to utility for error messges --- toml/utility.hpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/toml/utility.hpp b/toml/utility.hpp index 0df0d07..2e7b0f0 100644 --- a/toml/utility.hpp +++ b/toml/utility.hpp @@ -3,6 +3,7 @@ #include "traits.hpp" #include #include +#include namespace toml { @@ -30,7 +31,7 @@ inline void resize_impl(T& container, std::size_t N, std::false_type) else throw std::invalid_argument("not resizable type"); } -} +} // detail template inline void resize(T& container, std::size_t N) @@ -39,5 +40,27 @@ inline void resize(T& container, std::size_t N) else return detail::resize_impl(container, N, detail::has_resize_method()); } +namespace detail +{ +inline std::string concat_to_string_impl(std::ostringstream& oss) +{ + return oss.str(); +} +template +std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) +{ + oss << std::forward(head); + return concat_to_string_impl(oss, std::forward(tail) ... ); +} +} // detail + +template +std::string concat_to_string(Ts&& ... args) +{ + std::ostringstream oss; + oss << std::boolalpha << std::fixed; + return detail::concat_to_string_impl(oss, std::forward(args) ...); +} + }// toml #endif // TOML11_UTILITY From 59588e3a10af176d84ddf8fbe60cd825c4113c5f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 2 Dec 2018 23:16:28 +0900 Subject: [PATCH 008/133] add static_assert and useful member funcs --- toml/region.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/toml/region.hpp b/toml/region.hpp index df97f0d..355c785 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -16,6 +16,7 @@ namespace detail template struct location { + static_assert(std::is_same::value,""); using const_iterator = typename Container::const_iterator; location(Container cont) @@ -39,6 +40,7 @@ struct location template struct region { + static_assert(std::is_same::value,""); using const_iterator = typename Container::const_iterator; region(const location& loc) @@ -65,6 +67,9 @@ struct region region& operator=(region&&) = default; ~region() = default; + std::string str() const {return std::string(first, last);} + std::size_t size() const {return std::distance(first, last);} + const_iterator begin, first, last, end; std::shared_ptr source; }; From 679e282e233be9bd7a8f90e60221a8e42bf4df38 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 3 Dec 2018 00:10:26 +0900 Subject: [PATCH 009/133] make variables in region/location read-only to avoid modifying mistakenly --- toml/region.hpp | 101 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 355c785..20bdda8 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -1,9 +1,9 @@ #ifndef TOML11_REGION_H #define TOML11_REGION_H +#include "exception.hpp" #include #include #include -#include namespace toml { @@ -18,19 +18,32 @@ struct location { static_assert(std::is_same::value,""); using const_iterator = typename Container::const_iterator; + using source_ptr = std::shared_ptr; location(Container cont) - : source(std::make_shared(std::move(cont))), - begin(source->cbegin()), iter(source->cbegin()), end(source->cend()) + : source_(std::make_shared(std::move(cont))), + begin_(source_->cbegin()), iter_(source_->cbegin()), + end_(source_->cend()) {} location(const location&) = default; location(location&&) = default; location& operator=(const location&) = default; location& operator=(location&&) = default; - ~location() = default; + ~location() = default; - std::shared_ptr source; - const_iterator begin, iter, end; + const_iterator& iter() noexcept {return iter_;} + const_iterator iter() const noexcept {return iter_;} + + const_iterator begin() const noexcept {return begin_;} + const_iterator end() const noexcept {return end_;} + + source_ptr const& source() const& noexcept {return source_;} + source_ptr&& source() && noexcept {return std::move(source_);} + + private: + + source_ptr source_; + const_iterator begin_, iter_, end_; }; // region in a container, normally in a file content. @@ -42,23 +55,24 @@ struct region { static_assert(std::is_same::value,""); using const_iterator = typename Container::const_iterator; + using source_ptr = std::shared_ptr; region(const location& loc) - : begin(loc.begin), first(loc.iter), last(loc.iter), end(loc.end), - source(loc.source) + : begin_(loc.begin()), first_(loc.iter()), + last_(loc.iter()), end_(loc.end()), source_(loc.source()) {} region(location&& loc) - : begin(loc.begin), first(loc.iter), last(loc.iter), end(loc.end), - source(std::move(loc.source)) + : begin_(loc.begin()), first_(loc.iter()), + last_(loc.iter()), end_(loc.end()), source_(std::move(loc.source())) {} region(const location& loc, const_iterator f, const_iterator l) - : begin(loc.begin), first(f), last(l), end(loc.end), - source(loc.source) + : begin_(loc.begin()), first_(f), last_(l), end_(loc.end()), + source_(loc.source()) {} region(location&& loc, const_iterator f, const_iterator l) - : begin(loc.begin), first(f), last(l), end(loc.end), - source(std::move(loc.source)) + : begin_(loc.begin()), first_(f), last_(l), end_(loc.end()), + source_(std::move(loc.source())) {} region(const region&) = default; @@ -67,11 +81,32 @@ struct region region& operator=(region&&) = default; ~region() = default; - std::string str() const {return std::string(first, last);} - std::size_t size() const {return std::distance(first, last);} + region& operator+=(const region& other) + { + if(this->begin_ != other.begin_ || this->end_ != other.end_ || + this->last_ != other.first_) + { + throw internal_error("invalid region concatenation"); + } + this->last_ = other.last_; + return *this; + } - const_iterator begin, first, last, end; - std::shared_ptr source; + std::string str() const {return std::string(first_, last_);} + std::size_t size() const {return std::distance(first_, last_);} + + const_iterator begin() const noexcept {return begin_;} + const_iterator end() const noexcept {return end_;} + const_iterator first() const noexcept {return first_;} + const_iterator last() const noexcept {return last_;} + + source_ptr const& source() const& noexcept {return source_;} + source_ptr&& source() && noexcept {return std::move(source_);} + + private: + + const_iterator begin_, first_, last_, end_; + source_ptr source_; }; // to show a better error message. @@ -82,13 +117,13 @@ format_underline(const region& reg, const std::string& msg) using const_iterator = typename region::const_iterator; using reverse_iterator = std::reverse_iterator; const auto line_begin = std::find( - reverse_iterator(reg.first), - reverse_iterator(reg.begin), + reverse_iterator(reg.first()), + reverse_iterator(reg.begin()), '\n').base(); - const auto line_end = std::find(reg.last, reg.end, '\n'); + const auto line_end = std::find(reg.last(), reg.end(), '\n'); const auto line_number = std::to_string( - 1 + std::count(reg.begin, reg.first, '\n')); + 1 + std::count(reg.begin(), reg.first(), '\n')); std::string retval; retval += ' '; @@ -98,8 +133,8 @@ format_underline(const region& reg, const std::string& msg) retval += '\n'; retval += std::string(line_number.size() + 1, ' '); retval += " | "; - retval += std::string(std::distance(line_begin, reg.first), ' '); - retval += std::string(std::distance(reg.first, reg.last), '~'); + retval += std::string(std::distance(line_begin, reg.first()), ' '); + retval += std::string(std::distance(reg.first(), reg.last()), '~'); retval += ' '; retval += msg; return retval; @@ -114,13 +149,13 @@ std::string format_underline(const region& reg, using const_iterator = typename region::const_iterator; using reverse_iterator = std::reverse_iterator; const auto line_begin = std::find( - reverse_iterator(reg.first), - reverse_iterator(reg.begin), + reverse_iterator(reg.first()), + reverse_iterator(reg.begin()), '\n').base(); - const auto line_end = std::find(reg.last, reg.end, '\n'); + const auto line_end = std::find(reg.last(), reg.end(), '\n'); const auto line_number = std::to_string( - 1 + std::count(reg.begin, reg.first, '\n')); + 1 + std::count(reg.begin(), reg.first(), '\n')); std::string retval; retval += ' '; @@ -130,16 +165,16 @@ std::string format_underline(const region& reg, retval += '\n'; retval += std::string(line_number.size() + 1, ' '); retval += " | "; - retval += std::string(std::distance(line_begin, reg.first), ' '); + retval += std::string(std::distance(line_begin, reg.first()), ' '); - if(std::distance(reg.first, std::prev(pos)) > 0) + if(std::distance(reg.first(), std::prev(pos)) > 0) { - retval += std::string(std::distance(reg.first, std::prev(pos)), '-'); + retval += std::string(std::distance(reg.first(), std::prev(pos)), '-'); } retval += '^'; - if(std::distance(pos, reg.last) > 0) + if(std::distance(pos, reg.last()) > 0) { - retval += std::string(std::distance(std::next(pos), reg.last), '-'); + retval += std::string(std::distance(std::next(pos), reg.last()), '-'); } retval += ' '; From 1f564ec04757c7ca0ed922bd956c3ef8b0851016 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 4 Dec 2018 20:29:39 +0900 Subject: [PATCH 010/133] add combinators to scan content --- toml/combinator.hpp | 354 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 toml/combinator.hpp diff --git a/toml/combinator.hpp b/toml/combinator.hpp new file mode 100644 index 0000000..a81eeed --- /dev/null +++ b/toml/combinator.hpp @@ -0,0 +1,354 @@ +#ifndef TOML11_COMBINATOR_HPP +#define TOML11_COMBINATOR_HPP +#include "traits.hpp" +#include "result.hpp" +#include "utility.hpp" +#include "region.hpp" +#include +#include +#include +#include +#include + +// they scans characters and returns region if it matches to the condition. +// when they fail, it does not change the location. +// in lexer.hpp, these are used. + +namespace toml +{ +namespace detail +{ + +// to output character as an error message. +inline std::string show_char(const char c) +{ + if(std::isgraph(c)) + { + return std::string(1, c); + } + else + { + std::ostringstream oss; + oss << std::hex << std::setfill('0') << std::setw(2) << "0x" + << static_cast(c); + return oss.str(); + } +} + +template +struct character +{ + static constexpr char target = C; + + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + if(loc.iter() == loc.end()) {return err("empty input");} + const auto first = loc.iter(); + + const char c = *(loc.iter()); + if(c != target) + { + return err(concat_to_string("expected '", show_char(target), + "' but got '", show_char(c), "'.")); + } + ++(loc.iter()); // update location + + return ok(region(loc, first, loc.iter())); + } + + static std::string pattern() {return show_char(target);} +}; +template +constexpr char character::target; + +// closed interval [Low, Up]. both Low and Up are included. +template +struct in_range +{ + // assuming ascii part of UTF-8... + static_assert(Low <= Up, "lower bound should be less than upper bound."); + + static constexpr char upper = Up; + static constexpr char lower = Low; + + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + if(loc.iter() == loc.end()) {return err("empty input");} + const auto first = loc.iter(); + + const char c = *(loc.iter()); + if(c < lower || upper < c) + { + return err(concat_to_string("expected character in range " + "[", show_char(lower), ", ", show_char(upper), "] but got ", + "'", show_char(c), "'.")); + } + + ++(loc.iter()); + return ok(region(loc, first, loc.iter())); + } + + static std::string pattern() + { + return concat_to_string("[",show_char(lower),"-",show_char(upper),"]"); + } +}; +template constexpr char in_range::upper; +template constexpr char in_range::lower; + +// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. +// for detecting invalid characters, like control sequences in toml string. +template +struct exclude +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + if(loc.iter() == loc.end()) {return err("empty input");} + auto first = loc.iter(); + + auto rslt = Combinator::invoke(loc); + if(rslt.is_ok()) + { + loc.iter() = first; // rollback + return err(concat_to_string( + "invalid pattern (", Combinator::pattern(), ") appeared ", + rslt.unwrap().str())); + } + loc.iter() = std::next(first); + return ok(region(loc, first, loc.iter())); + } + + static std::string pattern() + { + return concat_to_string("^(", Combinator::pattern(), ')'); + } +}; + +// increment `iter`, if matches. otherwise, just return empty string. +template +struct maybe +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + const auto rslt = Combinator::invoke(loc); + if(rslt.is_ok()) + { + return rslt; + } + return ok(region(loc)); + } + + static std::string pattern() + { + return concat_to_string('(', Combinator::pattern(), ")?"); + } +}; + +template +struct sequence; + +template +struct sequence +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + const auto first = loc.iter(); + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.iter() = first; + return err(rslt.unwrap_err()); + } + return sequence::invoke(loc, std::move(rslt.unwrap()), first); + } + + // called from the above function only, recursively. + template + static result, std::string> + invoke(location& loc, region reg, Iterator first) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.iter() = first; + return err(rslt.unwrap_err()); + } + reg += rslt.unwrap(); // concat regions + return sequence::invoke(loc, std::move(reg), first); + } + + static std::string pattern() + { + return concat_to_string(Head::pattern(), sequence::pattern()); + } +}; + +template +struct sequence +{ + // would be called from sequence::invoke only. + template + static result, std::string> + invoke(location& loc, region reg, Iterator first) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.iter() = first; + return err(rslt.unwrap_err()); + } + reg += rslt.unwrap(); // concat regions + return ok(reg); + } + static std::string pattern() {return Head::pattern();} +}; + +template +struct either; + +template +struct either +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + + const auto rslt = Head::invoke(loc); + if(rslt.is_ok()) {return rslt;} + return either::invoke(loc); + } + + static std::string pattern() + { + return concat_to_string('(', Head::pattern(), ")|", either::pattern()); + } +}; +template +struct either +{ + template + static result, std::string> invoke(location& loc) + { + static_assert(std::is_same::value, + "internal error: container::value_type should be `char`."); + return Head::invoke(loc); + } + static std::string pattern() + { + return concat_to_string('(', Head::pattern(), ')'); + } +}; + +template +struct repeat; + +template struct exactly{}; +template struct at_least{}; +struct unlimited{}; + +template +struct repeat> +{ + template + static result, std::string> invoke(location& loc) + { + region retval(loc); + const auto first = loc.iter(); + for(std::size_t i=0; i +struct repeat> +{ + template + static result, std::string> invoke(location& loc) + { + region retval(loc); + + const auto first = loc.iter(); + for(std::size_t i=0; i +struct repeat +{ + template + static result, std::string> invoke(location& loc) + { + region retval(loc); + while(true) + { + auto rslt = T::invoke(loc); + if(rslt.is_err()) + { + return ok(std::move(retval)); + } + retval += rslt.unwrap(); + } + } + static std::string pattern() {return concat_to_string('(', T::pattern(), ")*");} +}; + +} // detail +} // toml +#endif// TOML11_COMBINATOR_HPP From 17f3d9676696a627e8a82c96874c325ca9c76ed2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 4 Dec 2018 20:29:59 +0900 Subject: [PATCH 011/133] add lexers --- toml/lexer.hpp | 208 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 toml/lexer.hpp diff --git a/toml/lexer.hpp b/toml/lexer.hpp new file mode 100644 index 0000000..42e90ad --- /dev/null +++ b/toml/lexer.hpp @@ -0,0 +1,208 @@ +#ifndef TOML11_LEXER_HPP +#define TOML11_LEXER_HPP +#include "combinator.hpp" +#include +#include +#include +#include + +namespace toml +{ +namespace detail +{ + +// these scans contents from current location in a container of char +// and extract a region that matches their own pattern. +// to see the implementation of each component, see combinator.hpp. + +using lex_wschar = either, character<'\t'>>; +using lex_ws = repeat>; +using lex_newline = either, + sequence, character<'\n'>>>; +using lex_lower = in_range<'a', 'z'>; +using lex_upper = in_range<'A', 'Z'>; +using lex_alpha = either; +using lex_digit = in_range<'0', '9'>; +using lex_nonzero = in_range<'1', '9'>; +using lex_oct_dig = in_range<'0', '7'>; +using lex_bin_dig = in_range<'0', '1'>; +using lex_hex_dig = either, in_range<'a', 'f'>>; + +using lex_hex_prefix = sequence, character<'x'>>; +using lex_oct_prefix = sequence, character<'o'>>; +using lex_bin_prefix = sequence, character<'b'>>; +using lex_underscore = character<'_'>; +using lex_plus = character<'+'>; +using lex_minus = character<'-'>; +using lex_sign = either; + +// digit | nonzero 1*(digit | _ digit) +using lex_unsigned_dec_int = either>, at_least<1>>>, + lex_digit>; +// (+|-)? unsigned_dec_int +using lex_dec_int = sequence, lex_unsigned_dec_int>; + +// hex_prefix hex_dig *(hex_dig | _ hex_dig) +using lex_hex_int = sequence>, unlimited>>>; +// oct_prefix oct_dig *(oct_dig | _ oct_dig) +using lex_oct_int = sequence>, unlimited>>>; +// bin_prefix bin_dig *(bin_dig | _ bin_dig) +using lex_bin_int = sequence>, unlimited>>>; + +// (dec_int | hex_int | oct_int | bin_int) +using lex_integer = either; + +// =========================================================================== + +using lex_inf = sequence, character<'n'>, character<'f'>>; +using lex_nan = sequence, character<'a'>, character<'n'>>; +using lex_special_float = sequence, either>; +using lex_exponent_part = sequence, character<'E'>>, lex_dec_int>; + +using lex_zero_prefixable_int = sequence>, unlimited>>; +using lex_fractional_part = sequence, lex_zero_prefixable_int>; + +using lex_float = either>>>>; + +// =========================================================================== + +using lex_true = sequence, character<'r'>, + character<'u'>, character<'e'>>; +using lex_false = sequence, character<'a'>, character<'l'>, + character<'s'>, character<'e'>>; +using lex_boolean = either; + +// =========================================================================== + +using lex_date_fullyear = repeat>; +using lex_date_month = repeat>; +using lex_date_mday = repeat>; +using lex_time_delim = either, character<'t'>, character<' '>>; +using lex_time_hour = repeat>; +using lex_time_minute = repeat>; +using lex_time_second = repeat>; +using lex_time_secfrac = sequence, + repeat>>; + +using lex_time_numoffset = sequence, character<'-'>>, + sequence, + lex_time_minute>>; +using lex_time_offset = either, character<'z'>, + lex_time_numoffset>; + +using lex_partial_time = sequence, + lex_time_minute, character<':'>, + lex_time_second, maybe>; +using lex_full_date = sequence, + lex_date_month, character<'-'>, + lex_date_mday>; +using lex_full_time = sequence; + +using lex_offset_date_time = sequence; +using lex_local_date_time = sequence; +using lex_local_date = lex_full_date; +using lex_local_time = lex_partial_time; + +// =========================================================================== + +using lex_quotation_mark = character<'"'>; +using lex_basic_unescaped = exclude, + character<0x22>, character<0x5C>, + character<0x7F>>>; +using lex_escape = character<'\\'>; +using lex_escape_seq_char = either, character<'\\'>, + character<'/'>, character<'b'>, + character<'f'>, character<'n'>, + character<'r'>, character<'t'>, + sequence, + repeat>>, + sequence, + repeat>> + >; +using lex_escaped = sequence; +using lex_basic_char = either; +using lex_basic_string = sequence, + lex_quotation_mark>; + +using lex_ml_basic_string_delim = repeat>; +using lex_ml_basic_unescaped = exclude, + character<0x5C>, + character<0x7F>, + lex_ml_basic_string_delim>>; +using lex_ml_basic_char = either; +using lex_ml_basic_body = repeat, + lex_newline>>, unlimited>; +using lex_ml_basic_string = sequence; + +using lex_literal_char = exclude, + in_range<0x10, 0x19>, character<0x27>>>; +using lex_apostrophe = character<'\''>; +using lex_literal_string = sequence, + lex_apostrophe>; + +using lex_ml_literal_string_delim = repeat>; + +using lex_ml_literal_char = exclude, + in_range<0x10, 0x1F>, + character<0x7F>, + lex_ml_literal_string_delim>>; +using lex_ml_literal_body = repeat, + unlimited>; +using lex_ml_literal_string = sequence; + +using lex_string = either; + +// =========================================================================== + +using lex_comment_start_symbol = character<'#'>; +using lex_non_eol = either, exclude>>; +using lex_comment = sequence>; + +using lex_dot_sep = sequence, character<'.'>, maybe>; + +using lex_unquoted_key = repeat, character<'_'>>, + at_least<1>>; +using lex_quoted_key = either; +using lex_simple_key = either; +using lex_dotted_key = sequence, + at_least<1>> + >; +using lex_key = either; + +using lex_keyval_sep = sequence, + character<'='>, + maybe>; + +using lex_std_table_open = character<'['>; +using lex_std_table_close = character<']'>; +using lex_std_table = sequence; + +using lex_array_table_open = sequence; +using lex_array_table_close = sequence; +using lex_array_table = sequence; + +} // detail +} // toml +#endif // TOML_LEXER_HPP From 1a2fa6d53a815017fd15d2f8d602504735601eaa Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 4 Dec 2018 20:30:21 +0900 Subject: [PATCH 012/133] add test for lexers --- tests/CMakeLists.txt | 6 ++ tests/test_lex_aux.hpp | 42 ++++++++++++++ tests/test_lex_boolean.cpp | 23 ++++++++ tests/test_lex_datetime.cpp | 57 +++++++++++++++++++ tests/test_lex_floating.cpp | 84 +++++++++++++++++++++++++++ tests/test_lex_integer.cpp | 101 +++++++++++++++++++++++++++++++++ tests/test_lex_key_comment.cpp | 40 +++++++++++++ tests/test_lex_string.cpp | 78 +++++++++++++++++++++++++ 8 files changed, 431 insertions(+) create mode 100644 tests/test_lex_aux.hpp create mode 100644 tests/test_lex_boolean.cpp create mode 100644 tests/test_lex_datetime.cpp create mode 100644 tests/test_lex_floating.cpp create mode 100644 tests/test_lex_integer.cpp create mode 100644 tests/test_lex_key_comment.cpp create mode 100644 tests/test_lex_string.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e2056a1..b6a0e14 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,10 @@ set(TEST_NAMES + test_lex_boolean + test_lex_integer + test_lex_floating + test_lex_datetime + test_lex_string + test_lex_key_comment test_traits test_utility test_result diff --git a/tests/test_lex_aux.hpp b/tests/test_lex_aux.hpp new file mode 100644 index 0000000..ba1cea5 --- /dev/null +++ b/tests/test_lex_aux.hpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include + +#define TOML11_TEST_LEX_ACCEPT(lxr, tkn, expct) \ +do { \ + const std::string token (tkn); \ + const std::string expected(expct); \ + toml::detail::location loc(token); \ + const auto result = lxr::invoke(loc); \ + BOOST_CHECK(result.is_ok()); \ + if(result.is_ok()){ \ + const auto matched = result.unwrap().str(); \ + std::cerr << "matched -> "; \ + std::for_each(matched.begin(), matched.end(), [](const char c) { \ + std::cerr << std::setw(2) << std::hex << std::setfill('0'); \ + std::cerr << "0x" << static_cast(c) << ", "; \ + return; \ + }); \ + std::cerr << std::endl; \ + const auto region = result.unwrap(); \ + BOOST_CHECK_EQUAL(region.str(), expected); \ + BOOST_CHECK_EQUAL(region.str().size(), expected.size()); \ + BOOST_CHECK_EQUAL(std::distance(loc.begin(), loc.iter()), region.size()); \ + } else { \ + std::cerr << "lexer " << lxr::pattern() << " failed with input `"; \ + std::cerr << token << "`. expected `" << expected << "`\n"; \ + std::cerr << "reason: " << result.unwrap_err() << '\n'; \ + } \ +} while(false); \ +/**/ + +#define TOML11_TEST_LEX_REJECT(lxr, tkn) \ +do { \ + const std::string token (tkn); \ + toml::detail::location loc(token); \ + const auto result = lxr::invoke(loc); \ + BOOST_CHECK(result.is_err()); \ + BOOST_CHECK(loc.begin() == loc.iter()); \ +} while(false); /**/ diff --git a/tests/test_lex_boolean.cpp b/tests/test_lex_boolean.cpp new file mode 100644 index 0000000..c2e43a8 --- /dev/null +++ b/tests/test_lex_boolean.cpp @@ -0,0 +1,23 @@ +#define BOOST_TEST_MODULE "test_lex_boolean" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_boolean, "true", "true"); + TOML11_TEST_LEX_ACCEPT(lex_boolean, "false", "false"); + TOML11_TEST_LEX_ACCEPT(lex_boolean, "true # trailing", "true"); + TOML11_TEST_LEX_ACCEPT(lex_boolean, "false # trailing", "false"); +} + +BOOST_AUTO_TEST_CASE(test_invalid) +{ + TOML11_TEST_LEX_REJECT(lex_boolean, "TRUE"); + TOML11_TEST_LEX_REJECT(lex_boolean, "FALSE"); + TOML11_TEST_LEX_REJECT(lex_boolean, "True"); + TOML11_TEST_LEX_REJECT(lex_boolean, "False"); +} diff --git a/tests/test_lex_datetime.cpp b/tests/test_lex_datetime.cpp new file mode 100644 index 0000000..a790eeb --- /dev/null +++ b/tests/test_lex_datetime.cpp @@ -0,0 +1,57 @@ +#define BOOST_TEST_MODULE "test_lex_datetime" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_offset_datetime) +{ + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27T07:32:00Z", + "1979-05-27T07:32:00Z"); + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27T07:32:00-07:00", + "1979-05-27T07:32:00-07:00"); + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27T07:32:00.999999-07:00", + "1979-05-27T07:32:00.999999-07:00"); + + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27 07:32:00Z", + "1979-05-27 07:32:00Z"); + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27 07:32:00-07:00", + "1979-05-27 07:32:00-07:00"); + TOML11_TEST_LEX_ACCEPT(lex_offset_date_time, + "1979-05-27 07:32:00.999999-07:00", + "1979-05-27 07:32:00.999999-07:00"); +} + +BOOST_AUTO_TEST_CASE(test_local_datetime) +{ + TOML11_TEST_LEX_ACCEPT(lex_local_date_time, + "1979-05-27T07:32:00", + "1979-05-27T07:32:00"); + TOML11_TEST_LEX_ACCEPT(lex_local_date_time, + "1979-05-27T07:32:00.999999", + "1979-05-27T07:32:00.999999"); + + TOML11_TEST_LEX_ACCEPT(lex_local_date_time, + "1979-05-27 07:32:00", + "1979-05-27 07:32:00"); + TOML11_TEST_LEX_ACCEPT(lex_local_date_time, + "1979-05-27 07:32:00.999999", + "1979-05-27 07:32:00.999999"); +} + +BOOST_AUTO_TEST_CASE(test_local_date) +{ + TOML11_TEST_LEX_ACCEPT(lex_local_date, "1979-05-27", "1979-05-27"); +} +BOOST_AUTO_TEST_CASE(test_local_time) +{ + TOML11_TEST_LEX_ACCEPT(lex_local_time, "07:32:00", "07:32:00"); + TOML11_TEST_LEX_ACCEPT(lex_local_time, "07:32:00.999999", "07:32:00.999999"); +} diff --git a/tests/test_lex_floating.cpp b/tests/test_lex_floating.cpp new file mode 100644 index 0000000..79ce157 --- /dev/null +++ b/tests/test_lex_floating.cpp @@ -0,0 +1,84 @@ +#define BOOST_TEST_MODULE "test_lex_floating" +#include +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_fractional_valid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "1.0", "1.0" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "0.1", "0.1" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "0.001", "0.001" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "0.100", "0.100" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "+3.14", "+3.14" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "-3.14", "-3.14" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "3.1415_9265_3589", "3.1415_9265_3589" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "+3.1415_9265_3589", "+3.1415_9265_3589"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-3.1415_9265_3589", "-3.1415_9265_3589"); + TOML11_TEST_LEX_ACCEPT(lex_float, "123_456.789", "123_456.789" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "+123_456.789", "+123_456.789" ); + TOML11_TEST_LEX_ACCEPT(lex_float, "-123_456.789", "-123_456.789" ); +} + +BOOST_AUTO_TEST_CASE(test_fractional_invalid) +{ + TOML11_TEST_LEX_REJECT(lex_float, "0."); + TOML11_TEST_LEX_REJECT(lex_float, ".0"); + TOML11_TEST_LEX_REJECT(lex_float, "01.0"); + TOML11_TEST_LEX_REJECT(lex_float, "3,14"); + TOML11_TEST_LEX_REJECT(lex_float, "+-1.0"); + TOML11_TEST_LEX_REJECT(lex_float, "1._0"); +} + +BOOST_AUTO_TEST_CASE(test_exponential_valid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "1e10", "1e10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1e+10", "1e+10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1e-10", "1e-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+1e10", "+1e10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+1e+10", "+1e+10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+1e-10", "+1e-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-1e10", "-1e10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-1e+10", "-1e+10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-1e-10", "-1e-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "123e-10", "123e-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1E10", "1E10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1E+10", "1E+10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1E-10", "1E-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "123E-10", "123E-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-10", "1_2_3E-10"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1_2_3E-1_0", "1_2_3E-1_0"); +} + +BOOST_AUTO_TEST_CASE(test_exponential_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "1e1E0", "1e1"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1E1e0", "1E1"); +} + +BOOST_AUTO_TEST_CASE(test_both_valid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e23", "6.02e23"); + TOML11_TEST_LEX_ACCEPT(lex_float, "6.02e+23", "6.02e+23"); + TOML11_TEST_LEX_ACCEPT(lex_float, "1.112_650_06e-17", "1.112_650_06e-17"); +} + +BOOST_AUTO_TEST_CASE(test_both_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "1e1.0", "1e1"); + TOML11_TEST_LEX_REJECT(lex_float, "01e1.0"); +} + +BOOST_AUTO_TEST_CASE(test_special_floating_point) +{ + TOML11_TEST_LEX_ACCEPT(lex_float, "inf", "inf"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+inf", "+inf"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-inf", "-inf"); + + TOML11_TEST_LEX_ACCEPT(lex_float, "nan", "nan"); + TOML11_TEST_LEX_ACCEPT(lex_float, "+nan", "+nan"); + TOML11_TEST_LEX_ACCEPT(lex_float, "-nan", "-nan"); +} diff --git a/tests/test_lex_integer.cpp b/tests/test_lex_integer.cpp new file mode 100644 index 0000000..bb6dbb7 --- /dev/null +++ b/tests/test_lex_integer.cpp @@ -0,0 +1,101 @@ +#define BOOST_TEST_MODULE "test_lex_integer" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_decimal_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "1234", "1234" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "+1234", "+1234" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-1234", "-1234" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0", "0" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "1_2_3_4", "1_2_3_4" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "+1_2_3_4", "+1_2_3_4" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-1_2_3_4", "-1_2_3_4" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "123_456_789", "123_456_789"); +} + +BOOST_AUTO_TEST_CASE(test_decimal_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "123+45", "123"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "123-45", "123"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "01234", "0"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "123__45", "123"); + + TOML11_TEST_LEX_REJECT(lex_integer, "_1234"); +} + +BOOST_AUTO_TEST_CASE(test_hex_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEADBEEF", "0xDEADBEEF" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdeadbeef", "0xdeadbeef" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEADbeef", "0xDEADbeef" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD_BEEF", "0xDEAD_BEEF"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdead_beef", "0xdead_beef"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xdead_BEEF", "0xdead_BEEF"); + + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xFF", "0xFF" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0x00FF", "0x00FF" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0x0000FF", "0x0000FF"); +} + +BOOST_AUTO_TEST_CASE(test_hex_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xAPPLE", "0xA"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD+BEEF", "0xDEAD"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0xDEAD__BEEF", "0xDEAD"); + + TOML11_TEST_LEX_REJECT(lex_hex_int, "0x_DEADBEEF"); + TOML11_TEST_LEX_REJECT(lex_hex_int, "0x+DEADBEEF"); + TOML11_TEST_LEX_REJECT(lex_hex_int, "-0xFF" ); + TOML11_TEST_LEX_REJECT(lex_hex_int, "-0x00FF" ); + + TOML11_TEST_LEX_ACCEPT(lex_integer, "0x_DEADBEEF", "0" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0x+DEADBEEF", "0" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-0xFF" , "-0" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-0x00FF" , "-0" ); +} + +BOOST_AUTO_TEST_CASE(test_oct_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o777", "0o777" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o7_7_7", "0o7_7_7"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o007", "0o007" ); + +} + +BOOST_AUTO_TEST_CASE(test_oct_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o77+7", "0o77"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o1__0", "0o1"); + + TOML11_TEST_LEX_REJECT(lex_oct_int, "0o800" ); + TOML11_TEST_LEX_REJECT(lex_oct_int, "-0o777"); + TOML11_TEST_LEX_REJECT(lex_oct_int, "0o+777"); + TOML11_TEST_LEX_REJECT(lex_oct_int, "0o_10" ); + + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o800", "0"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "-0o777", "-0"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o+777", "0"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0o_10", "0"); +} + +BOOST_AUTO_TEST_CASE(test_bin_correct) +{ + TOML11_TEST_LEX_ACCEPT(lex_integer, "0b10000", "0b10000" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0b010000", "0b010000" ); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0b01_00_00", "0b01_00_00"); + TOML11_TEST_LEX_ACCEPT(lex_integer, "0b111111", "0b111111" ); +} + +BOOST_AUTO_TEST_CASE(test_bin_invalid) +{ + TOML11_TEST_LEX_ACCEPT(lex_bin_int, "0b11__11", "0b11"); + TOML11_TEST_LEX_ACCEPT(lex_bin_int, "0b11+11" , "0b11"); + + TOML11_TEST_LEX_REJECT(lex_bin_int, "-0b10000"); + TOML11_TEST_LEX_REJECT(lex_bin_int, "0b_1111" ); +} diff --git a/tests/test_lex_key_comment.cpp b/tests/test_lex_key_comment.cpp new file mode 100644 index 0000000..c64c72f --- /dev/null +++ b/tests/test_lex_key_comment.cpp @@ -0,0 +1,40 @@ +#define BOOST_TEST_MODULE "lex_key_comment_test" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_bare_key) +{ + TOML11_TEST_LEX_ACCEPT(lex_key, "barekey", "barekey"); + TOML11_TEST_LEX_ACCEPT(lex_key, "bare-key", "bare-key"); + TOML11_TEST_LEX_ACCEPT(lex_key, "bare_key", "bare_key"); + TOML11_TEST_LEX_ACCEPT(lex_key, "1234", "1234"); +} + +BOOST_AUTO_TEST_CASE(test_quoted_key) +{ + TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\""); + TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\""); + TOML11_TEST_LEX_ACCEPT(lex_key, "\"ʎǝʞ\"", "\"ʎǝʞ\""); + TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'"); + TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'"); +} + +BOOST_AUTO_TEST_CASE(test_dotted_key) +{ + TOML11_TEST_LEX_ACCEPT(lex_key, "physical.color", "physical.color"); + TOML11_TEST_LEX_ACCEPT(lex_key, "physical.shape", "physical.shape"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x.y.z.w", "x.y.z.w"); + TOML11_TEST_LEX_ACCEPT(lex_key, "site.\"google.com\"", "site.\"google.com\""); +} + +BOOST_AUTO_TEST_CASE(test_comment) +{ + TOML11_TEST_LEX_ACCEPT(lex_comment, "# hoge", "# hoge"); + TOML11_TEST_LEX_ACCEPT(lex_comment, "# \n", "# "); + TOML11_TEST_LEX_ACCEPT(lex_comment, "# \r\n", "# "); + TOML11_TEST_LEX_ACCEPT(lex_comment, "# # \n", "# # "); +} diff --git a/tests/test_lex_string.cpp b/tests/test_lex_string.cpp new file mode 100644 index 0000000..4c36b66 --- /dev/null +++ b/tests/test_lex_string.cpp @@ -0,0 +1,78 @@ +#define BOOST_TEST_MODULE "test_lex_string" +#include +#include +#include "test_lex_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"The quick brown fox jumps over the lazy dog\"", + "\"The quick brown fox jumps over the lazy dog\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\'The quick brown fox jumps over the lazy dog\'", + "\'The quick brown fox jumps over the lazy dog\'"); + TOML11_TEST_LEX_ACCEPT(lex_ml_basic_string, + "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", + "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\""); + TOML11_TEST_LEX_ACCEPT(lex_ml_literal_string, + "'''The quick brown fox \njumps over the lazy dog'''", + "'''The quick brown fox \njumps over the lazy dog'''"); +} + +BOOST_AUTO_TEST_CASE(test_basic_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", + "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"192.168.1.1\"", + "\"192.168.1.1\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"中国\"", + "\"中国\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"You'll hate me after this - #\"", + "\"You'll hate me after this - #\""); + TOML11_TEST_LEX_ACCEPT(lex_string, + "\" And when \\\"'s are in the string, along with # \\\"\"", + "\" And when \\\"'s are in the string, along with # \\\"\""); +} + +BOOST_AUTO_TEST_CASE(test_ml_basic_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", + "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\""); + 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 \"\"\""); +} + +BOOST_AUTO_TEST_CASE(test_literal_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "'C:\\Users\\nodejs\\templates'", + "'C:\\Users\\nodejs\\templates'"); + TOML11_TEST_LEX_ACCEPT(lex_string, + "'\\\\ServerX\\admin$\\system32\\'", + "'\\\\ServerX\\admin$\\system32\\'"); + TOML11_TEST_LEX_ACCEPT(lex_string, + "'Tom \"Dubs\" Preston-Werner'", + "'Tom \"Dubs\" Preston-Werner'"); + TOML11_TEST_LEX_ACCEPT(lex_string, + "'<\\i\\c*\\s*>'", + "'<\\i\\c*\\s*>'"); +} + +BOOST_AUTO_TEST_CASE(test_ml_literal_string) +{ + TOML11_TEST_LEX_ACCEPT(lex_string, + "'''I [dw]on't need \\d{2} apples'''", + "'''I [dw]on't need \\d{2} apples'''"); + 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'''"); +} From c3e1f68ef69c486930574c4507d13548229c87a0 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 4 Dec 2018 20:59:44 +0900 Subject: [PATCH 013/133] add u8 to the front of UTF-8 string literal explicitly set the character encoding for them, for compatibility --- tests/test_lex_key_comment.cpp | 2 +- tests/test_lex_string.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_lex_key_comment.cpp b/tests/test_lex_key_comment.cpp index c64c72f..291fbf1 100644 --- a/tests/test_lex_key_comment.cpp +++ b/tests/test_lex_key_comment.cpp @@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(test_quoted_key) { TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\""); TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\""); - TOML11_TEST_LEX_ACCEPT(lex_key, "\"ʎǝʞ\"", "\"ʎǝʞ\""); + TOML11_TEST_LEX_ACCEPT(lex_key, u8"\"ʎǝʞ\"", u8"\"ʎǝʞ\""); TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'"); TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'"); } diff --git a/tests/test_lex_string.cpp b/tests/test_lex_string.cpp index 4c36b66..a02d5de 100644 --- a/tests/test_lex_string.cpp +++ b/tests/test_lex_string.cpp @@ -31,8 +31,8 @@ BOOST_AUTO_TEST_CASE(test_basic_string) "\"192.168.1.1\"", "\"192.168.1.1\""); TOML11_TEST_LEX_ACCEPT(lex_string, - "\"中国\"", - "\"中国\""); + u8"\"中国\"", + u8"\"中国\""); TOML11_TEST_LEX_ACCEPT(lex_string, "\"You'll hate me after this - #\"", "\"You'll hate me after this - #\""); From c0ce5a2d7dedb5e35e7d9bd11df7f69c8f438915 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 4 Dec 2018 21:00:34 +0900 Subject: [PATCH 014/133] remove debug message from test code --- tests/test_lex_aux.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_lex_aux.hpp b/tests/test_lex_aux.hpp index ba1cea5..9babc0f 100644 --- a/tests/test_lex_aux.hpp +++ b/tests/test_lex_aux.hpp @@ -12,14 +12,6 @@ do { \ const auto result = lxr::invoke(loc); \ BOOST_CHECK(result.is_ok()); \ if(result.is_ok()){ \ - const auto matched = result.unwrap().str(); \ - std::cerr << "matched -> "; \ - std::for_each(matched.begin(), matched.end(), [](const char c) { \ - std::cerr << std::setw(2) << std::hex << std::setfill('0'); \ - std::cerr << "0x" << static_cast(c) << ", "; \ - return; \ - }); \ - std::cerr << std::endl; \ const auto region = result.unwrap(); \ BOOST_CHECK_EQUAL(region.str(), expected); \ BOOST_CHECK_EQUAL(region.str().size(), expected.size()); \ From c3a2cd8c1eadf73b4937434392453ccfde3aa5cc Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 4 Dec 2018 21:43:43 +0900 Subject: [PATCH 015/133] use hexcode instead of u8 string on windows --- tests/test_lex_key_comment.cpp | 5 +++++ tests/test_lex_string.cpp | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/tests/test_lex_key_comment.cpp b/tests/test_lex_key_comment.cpp index 291fbf1..2598c64 100644 --- a/tests/test_lex_key_comment.cpp +++ b/tests/test_lex_key_comment.cpp @@ -18,7 +18,12 @@ BOOST_AUTO_TEST_CASE(test_quoted_key) { TOML11_TEST_LEX_ACCEPT(lex_key, "\"127.0.0.1\"", "\"127.0.0.1\""); TOML11_TEST_LEX_ACCEPT(lex_key, "\"character encoding\"", "\"character encoding\""); +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_LEX_ACCEPT(lex_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", + "\"\xCA\x8E\xC7\x9D\xCA\x9E\""); +#else TOML11_TEST_LEX_ACCEPT(lex_key, u8"\"ʎǝʞ\"", u8"\"ʎǝʞ\""); +#endif TOML11_TEST_LEX_ACCEPT(lex_key, "'key2'", "'key2'"); TOML11_TEST_LEX_ACCEPT(lex_key, "'quoted \"value\"'", "'quoted \"value\"'"); } diff --git a/tests/test_lex_string.cpp b/tests/test_lex_string.cpp index a02d5de..fcf0727 100644 --- a/tests/test_lex_string.cpp +++ b/tests/test_lex_string.cpp @@ -30,9 +30,17 @@ BOOST_AUTO_TEST_CASE(test_basic_string) TOML11_TEST_LEX_ACCEPT(lex_string, "\"192.168.1.1\"", "\"192.168.1.1\""); + +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_LEX_ACCEPT(lex_string, + "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", + "\"\xE4\xB8\xAD\xE5\x9B\xBD\""); +#else TOML11_TEST_LEX_ACCEPT(lex_string, u8"\"中国\"", u8"\"中国\""); +#endif + TOML11_TEST_LEX_ACCEPT(lex_string, "\"You'll hate me after this - #\"", "\"You'll hate me after this - #\""); From 932a0646ce4101031c896a96cd14c2b2081f06ce Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 4 Dec 2018 21:55:45 +0900 Subject: [PATCH 016/133] force clamping character code in [0,256) --- toml/combinator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/combinator.hpp b/toml/combinator.hpp index a81eeed..4c69093 100644 --- a/toml/combinator.hpp +++ b/toml/combinator.hpp @@ -22,7 +22,7 @@ namespace detail // to output character as an error message. inline std::string show_char(const char c) { - if(std::isgraph(c)) + if(std::isgraph(*reinterpret_cast(std::addressof(c)))) { return std::string(1, c); } From 2b3c8887d60f4ac0986bc4c55aed1f2e5561b963 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 4 Dec 2018 22:17:20 +0900 Subject: [PATCH 017/133] add comment to confusing implementation --- toml/combinator.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/toml/combinator.hpp b/toml/combinator.hpp index 4c69093..eb282dd 100644 --- a/toml/combinator.hpp +++ b/toml/combinator.hpp @@ -22,6 +22,14 @@ namespace detail // to output character as an error message. inline std::string show_char(const char c) { + // It supress an error that occurs only in Debug mode of MSVC++ on Windows. + // I'm not completely sure but they check the value of char to be in the + // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes + // has negative value (if char has sign). So here it re-interprets c as + // unsigned char through pointer. In general, converting pointer to a + // pointer that has different type cause UB, but `(signed|unsigned)?char` + // are one of the exceptions. Converting pointer only to char and std::byte + // (c++17) are valid. if(std::isgraph(*reinterpret_cast(std::addressof(c)))) { return std::string(1, c); From aa05858de368b8eb5e9efbbbdb0072b965f8d339 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 5 Dec 2018 16:55:31 +0900 Subject: [PATCH 018/133] add source_name to location/region to show filename now error message prints the filename --- toml/region.hpp | 120 ++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 51 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 20bdda8..709ce98 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -4,12 +4,26 @@ #include #include #include +#include namespace toml { namespace detail { +// helper function to avoid std::string(0, 'c') +template +std::string make_string(Iterator first, Iterator last) +{ + if(first == last) {return "";} + return std::string(first, last); +} +inline std::string make_string(std::size_t len, char c) +{ + if(len == 0) {return "";} + return std::string(len, c); +} + // location in a container, normally in a file content. // shared_ptr points the resource that the iter points. // it can be used not only for resource handling, but also error message. @@ -20,10 +34,10 @@ struct location using const_iterator = typename Container::const_iterator; using source_ptr = std::shared_ptr; - location(Container cont) + location(std::string name, Container cont) : source_(std::make_shared(std::move(cont))), - begin_(source_->cbegin()), iter_(source_->cbegin()), - end_(source_->cend()) + source_name_(std::move(name)), begin_(source_->cbegin()), + iter_(source_->cbegin()), end_(source_->cend()) {} location(const location&) = default; location(location&&) = default; @@ -40,9 +54,12 @@ struct location source_ptr const& source() const& noexcept {return source_;} source_ptr&& source() && noexcept {return std::move(source_);} + std::string const& name() const noexcept {return source_name_;} + private: - source_ptr source_; + source_ptr source_; + std::string source_name_; const_iterator begin_, iter_, end_; }; @@ -58,21 +75,23 @@ struct region using source_ptr = std::shared_ptr; region(const location& loc) - : begin_(loc.begin()), first_(loc.iter()), - last_(loc.iter()), end_(loc.end()), source_(loc.source()) + : source_(loc.source()), source_name_(loc.name()), + begin_(loc.begin()), first_(loc.iter()), + last_(loc.iter()), end_(loc.end()) {} region(location&& loc) - : begin_(loc.begin()), first_(loc.iter()), - last_(loc.iter()), end_(loc.end()), source_(std::move(loc.source())) + : source_(loc.source()), source_name_(loc.name()), + begin_(loc.begin()), first_(loc.iter()), + last_(loc.iter()), end_(loc.end()) {} region(const location& loc, const_iterator f, const_iterator l) - : begin_(loc.begin()), first_(f), last_(l), end_(loc.end()), - source_(loc.source()) + : source_(loc.source()), source_name_(loc.name()), + begin_(loc.begin()), first_(f), last_(l), end_(loc.end()) {} region(location&& loc, const_iterator f, const_iterator l) - : begin_(loc.begin()), first_(f), last_(l), end_(loc.end()), - source_(std::move(loc.source())) + : source_(loc.source()), source_name_(loc.name()), + begin_(loc.begin()), first_(f), last_(l), end_(loc.end()) {} region(const region&) = default; @@ -92,7 +111,7 @@ struct region return *this; } - std::string str() const {return std::string(first_, last_);} + std::string str() const {return make_string(first_, last_);} std::size_t size() const {return std::distance(first_, last_);} const_iterator begin() const noexcept {return begin_;} @@ -103,82 +122,81 @@ struct region source_ptr const& source() const& noexcept {return source_;} source_ptr&& source() && noexcept {return std::move(source_);} + std::string const& name() const noexcept {return source_name_;} + private: + source_ptr source_; + std::string source_name_; const_iterator begin_, first_, last_, end_; - source_ptr source_; }; // to show a better error message. template std::string -format_underline(const region& reg, const std::string& msg) +format_underline(const std::string& message, const region& reg, + const std::string& comment_for_underline) { using const_iterator = typename region::const_iterator; using reverse_iterator = std::reverse_iterator; - const auto line_begin = std::find( - reverse_iterator(reg.first()), - reverse_iterator(reg.begin()), - '\n').base(); - const auto line_end = std::find(reg.last(), reg.end(), '\n'); + const auto line_begin = std::find(reverse_iterator(reg.first()), + reverse_iterator(reg.begin()), + '\n').base(); + const auto line_end = std::find(reg.last(), reg.end(), '\n'); const auto line_number = std::to_string( 1 + std::count(reg.begin(), reg.first(), '\n')); std::string retval; - retval += ' '; + retval += message; + retval += "\n --> "; + retval += reg.name(); + retval += "\n "; retval += line_number; retval += " | "; - retval += std::string(line_begin, line_end); + retval += make_string(line_begin, line_end); retval += '\n'; - retval += std::string(line_number.size() + 1, ' '); + retval += make_string(line_number.size() + 1, ' '); retval += " | "; - retval += std::string(std::distance(line_begin, reg.first()), ' '); - retval += std::string(std::distance(reg.first(), reg.last()), '~'); + retval += make_string(std::distance(line_begin, reg.first()), ' '); + retval += make_string(std::distance(reg.first(), reg.last()), '~'); retval += ' '; - retval += msg; + retval += comment_for_underline; return retval; } // to show a better error message. template -std::string format_underline(const region& reg, - typename Container::const_iterator pos, - const std::string& msg) +std::string +format_underline(const std::string& message, const location& loc, + const std::string& comment_for_underline) { - using const_iterator = typename region::const_iterator; + using const_iterator = typename location::const_iterator; using reverse_iterator = std::reverse_iterator; - const auto line_begin = std::find( - reverse_iterator(reg.first()), - reverse_iterator(reg.begin()), - '\n').base(); - const auto line_end = std::find(reg.last(), reg.end(), '\n'); + const auto line_begin = std::find(reverse_iterator(loc.iter()), + reverse_iterator(loc.begin()), + '\n').base(); + const auto line_end = std::find(loc.iter(), loc.end(), '\n'); const auto line_number = std::to_string( - 1 + std::count(reg.begin(), reg.first(), '\n')); + 1 + std::count(loc.begin(), loc.iter(), '\n')); std::string retval; - retval += ' '; + retval += message; + retval += "\n --> "; + retval += loc.name(); + retval += "\n "; retval += line_number; retval += " | "; - retval += std::string(line_begin, line_end); + retval += make_string(line_begin, line_end); retval += '\n'; - retval += std::string(line_number.size() + 1, ' '); + retval += make_string(line_number.size() + 1, ' '); retval += " | "; - retval += std::string(std::distance(line_begin, reg.first()), ' '); - - if(std::distance(reg.first(), std::prev(pos)) > 0) - { - retval += std::string(std::distance(reg.first(), std::prev(pos)), '-'); - } + retval += make_string(std::distance(line_begin, loc.iter()),' '); retval += '^'; - if(std::distance(pos, reg.last()) > 0) - { - retval += std::string(std::distance(std::next(pos), reg.last()), '-'); - } - + retval += make_string(std::distance(std::next(loc.iter()), line_end), '-'); retval += ' '; - retval += msg; + retval += comment_for_underline; return retval; } From f9a018b5ea3f04ebf575087e659dc82fe4f098f2 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 5 Dec 2018 17:19:37 +0900 Subject: [PATCH 019/133] add source_name to test_lex_aux macros --- tests/test_lex_aux.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_lex_aux.hpp b/tests/test_lex_aux.hpp index 9babc0f..8ee18e8 100644 --- a/tests/test_lex_aux.hpp +++ b/tests/test_lex_aux.hpp @@ -8,7 +8,7 @@ do { \ const std::string token (tkn); \ const std::string expected(expct); \ - toml::detail::location loc(token); \ + toml::detail::location loc("test", token); \ const auto result = lxr::invoke(loc); \ BOOST_CHECK(result.is_ok()); \ if(result.is_ok()){ \ @@ -27,7 +27,7 @@ do { \ #define TOML11_TEST_LEX_REJECT(lxr, tkn) \ do { \ const std::string token (tkn); \ - toml::detail::location loc(token); \ + toml::detail::location loc("test", token); \ const auto result = lxr::invoke(loc); \ BOOST_CHECK(result.is_err()); \ BOOST_CHECK(loc.begin() == loc.iter()); \ From 5e052237ba6e5bedd15e4c97948bea92906ec9a7 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 6 Dec 2018 01:20:11 +0900 Subject: [PATCH 020/133] add alias for snake_case types --- toml/types.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/toml/types.hpp b/toml/types.hpp index d822823..78fb149 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -22,6 +22,15 @@ using Datetime = basic_datetime; using Array = std::vector; using Table = std::unordered_map; +// alias for snake_case, consistency with STL/Boost, toml::key, toml::value +using boolean = Boolean; +using integer = Integer; +using floating = Float; // XXX float is keyword +using string = String; +using datetime = Datetime; +using array = Array; +using table = Table; + enum class value_t : std::uint8_t { Boolean = 1, From 1dddc6e26c251ed78a485b30863541cfb34afe7b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 6 Dec 2018 12:46:32 +0900 Subject: [PATCH 021/133] add missing std::move for ctors --- toml/result.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/toml/result.hpp b/toml/result.hpp index a6276ef..bdb4cae 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -238,12 +238,12 @@ struct result { if(other.is_ok()) { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); } else { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); } } @@ -267,12 +267,12 @@ struct result { if(other.is_ok()) { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); + auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); assert(tmp == std::addressof(this->succ)); } else { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); + auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); assert(tmp == std::addressof(this->fail)); } } From e3f6805629f6f5ab752d33329ac0be4feb18d229 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 6 Dec 2018 12:47:14 +0900 Subject: [PATCH 022/133] add conversion members to result --- tests/test_result.cpp | 257 ++++++++++++++++++++++++++++++++++++++++++ toml/result.hpp | 155 +++++++++++++++++++++++++ 2 files changed, 412 insertions(+) diff --git a/tests/test_result.cpp b/tests/test_result.cpp index ea2249c..472f76b 100644 --- a/tests/test_result.cpp +++ b/tests/test_result.cpp @@ -109,3 +109,260 @@ BOOST_AUTO_TEST_CASE(test_assignment) BOOST_CHECK_EQUAL(result.unwrap_err(), "hoge"); } } + +BOOST_AUTO_TEST_CASE(test_map) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map( + [](const int i) -> int { + return i * 2; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map( + [](std::unique_ptr i) -> int { + return *i; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map( + [](const int i) -> int { + return i * 2; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).map( + [](std::unique_ptr i) -> int { + return *i; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_map_err) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map_err( + [](const std::string s) -> std::string { + return s + s; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map_err( + [](const std::string s) -> std::string { + return s + s; + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(*(mapped.unwrap()), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map_err( + [](const std::string s) -> std::string { + return s + s; + }); + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } + { + toml::result> + result(toml::err(std::unique_ptr(new std::string("hoge")))); + const auto mapped = std::move(result).map_err( + [](std::unique_ptr p) -> std::string { + return *p; + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_map_or_else) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map_or_else( + [](const int i) -> int { + return i * 2; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map_or_else( + [](std::unique_ptr i) -> int { + return *i; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map_or_else( + [](const int i) -> int { + return i * 2; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 54); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).map_or_else( + [](std::unique_ptr i) -> int { + return *i; + }, 54); + + BOOST_CHECK_EQUAL(mapped, 54); + } +} + +BOOST_AUTO_TEST_CASE(test_and_then) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.and_then( + [](const int i) -> toml::result { + return toml::ok(i * 2); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42 * 2); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).and_then( + [](std::unique_ptr i) -> toml::result { + return toml::ok(*i); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.and_then( + [](const int i) -> toml::result { + return toml::ok(i * 2); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).and_then( + [](std::unique_ptr i) -> toml::result { + return toml::ok(*i); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hoge"); + } +} + +BOOST_AUTO_TEST_CASE(test_or_else) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.or_else( + [](const std::string& s) -> toml::result { + return toml::err(s + s); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap(), 42); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).or_else( + [](const std::string& s) -> toml::result, std::string> { + return toml::err(s + s); + }); + + BOOST_CHECK(!!mapped); + BOOST_CHECK(mapped.is_ok()); + BOOST_CHECK(!mapped.is_err()); + BOOST_CHECK_EQUAL(*mapped.unwrap(), 42); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.or_else( + [](const std::string& s) -> toml::result { + return toml::err(s + s); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = std::move(result).or_else( + [](const std::string& s) -> toml::result, std::string> { + return toml::err(s + s); + }); + + BOOST_CHECK(!mapped); + BOOST_CHECK(!mapped.is_ok()); + BOOST_CHECK(mapped.is_err()); + BOOST_CHECK_EQUAL(mapped.unwrap_err(), "hogehoge"); + } +} diff --git a/toml/result.hpp b/toml/result.hpp index bdb4cae..67249f0 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -8,6 +8,19 @@ namespace toml { +#if __cplusplus >= 201703L + +template +using return_type_of_t = std::invoke_result_t; + +#else +// result_of is deprecated after C++17 +template +using return_type_of_t = typename std::result_of::type; + +#endif + + template struct success { @@ -390,6 +403,142 @@ struct result error_type const& as_err() const& noexcept {return this->fail.value;} error_type&& as_err() && noexcept {return std::move(this->fail.value);} + + // prerequisities + // F: T -> U + // retval: result + template + result, error_type> + map(F&& f) & + { + if(this->is_ok()){return ok(f(this->as_ok()));} + return err(this->as_err()); + } + template + result, error_type> + map(F&& f) const& + { + if(this->is_ok()){return ok(f(this->as_ok()));} + return err(this->as_err()); + } + template + result, error_type> + map(F&& f) && + { + if(this->is_ok()){return ok(f(std::move(this->as_ok())));} + return err(std::move(this->as_err())); + } + + // prerequisities + // F: E -> F + // retval: result + template + result> + map_err(F&& f) & + { + if(this->is_err()){return err(f(this->as_err()));} + return ok(this->as_ok()); + } + template + result> + map_err(F&& f) const& + { + if(this->is_err()){return err(f(this->as_err()));} + return ok(this->as_ok()); + } + template + result> + map_err(F&& f) && + { + if(this->is_err()){return err(f(std::move(this->as_err())));} + return ok(std::move(this->as_ok())); + } + + // prerequisities + // F: T -> U + // retval: U + template + return_type_of_t + map_or_else(F&& f, U&& opt) & + { + if(this->is_err()){return std::forward(opt);} + return f(this->as_ok()); + } + template + return_type_of_t + map_or_else(F&& f, U&& opt) const& + { + if(this->is_err()){return std::forward(opt);} + return f(this->as_ok()); + } + template + return_type_of_t + map_or_else(F&& f, U&& opt) && + { + if(this->is_err()){return std::forward(opt);} + return f(std::move(this->as_ok())); + } + + // prerequisities: + // F: func T -> U + // toml::err(error_type) should be convertible to U. + // normally, type U is another result and E is convertible to F + template + return_type_of_t + and_then(F&& f) & + { + if(this->is_ok()){return f(this->as_ok());} + return err(this->as_err()); + } + template + return_type_of_t + and_then(F&& f) const& + { + if(this->is_ok()){return f(this->as_ok());} + return err(this->as_err()); + } + template + return_type_of_t + and_then(F&& f) && + { + if(this->is_ok()){return f(std::move(this->as_ok()));} + return err(std::move(this->as_err())); + } + + // prerequisities: + // F: func E -> U + // toml::ok(value_type) should be convertible to U. + // normally, type U is another result and T is convertible to S + template + return_type_of_t + or_else(F&& f) & + { + if(this->is_err()){return f(this->as_err());} + return ok(this->as_ok()); + } + template + return_type_of_t + or_else(F&& f) const& + { + if(this->is_err()){return f(this->as_err());} + return ok(this->as_ok()); + } + template + return_type_of_t + or_else(F&& f) && + { + if(this->is_err()){return f(std::move(this->as_err()));} + return ok(std::move(this->as_ok())); + } + + void swap(result& other) + { + result tmp(std::move(*this)); + *this = std::move(other); + other = std::move(tmp); + return ; + } + private: void cleanup() noexcept @@ -409,6 +558,12 @@ struct result }; }; +template +void swap(result& lhs, result& rhs) +{ + lhs.swap(rhs); + return; +} } // toml11 #endif// TOML11_RESULT_H From df314da7510ee565e4e6a3ca5a1d04701cb528c6 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 6 Dec 2018 17:03:57 +0900 Subject: [PATCH 023/133] change error message considering context combinators are used with other parser-combinators. in such cases, empty input means `not enough character`. --- toml/combinator.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/toml/combinator.hpp b/toml/combinator.hpp index eb282dd..30cd028 100644 --- a/toml/combinator.hpp +++ b/toml/combinator.hpp @@ -54,7 +54,7 @@ struct character static_assert(std::is_same::value, "internal error: container::value_type should be `char`."); - if(loc.iter() == loc.end()) {return err("empty input");} + if(loc.iter() == loc.end()) {return err("not sufficient characters");} const auto first = loc.iter(); const char c = *(loc.iter()); @@ -89,7 +89,7 @@ struct in_range static_assert(std::is_same::value, "internal error: container::value_type should be `char`."); - if(loc.iter() == loc.end()) {return err("empty input");} + if(loc.iter() == loc.end()) {return err("not sufficient characters");} const auto first = loc.iter(); const char c = *(loc.iter()); @@ -123,7 +123,7 @@ struct exclude static_assert(std::is_same::value, "internal error: container::value_type should be `char`."); - if(loc.iter() == loc.end()) {return err("empty input");} + if(loc.iter() == loc.end()) {return err("not sufficient characters");} auto first = loc.iter(); auto rslt = Combinator::invoke(loc); From b3b5682cc083f220c6cc4ba6d236e88e6f677b57 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 6 Dec 2018 17:15:19 +0900 Subject: [PATCH 024/133] add message to bad_unwrap --- toml/result.hpp | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/toml/result.hpp b/toml/result.hpp index 67249f0..fde9729 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -1,8 +1,11 @@ #ifndef TOML11_RESULT_H #define TOML11_RESULT_H #include +#include #include #include +#include +#include #include namespace toml @@ -365,17 +368,29 @@ struct result value_type& unwrap() & { - if(is_err()) {throw std::runtime_error("result: bad unwrap");} + if(is_err()) + { + throw std::runtime_error("result: bad unwrap: " + + format_error(this->as_err())); + } return this->succ.value; } value_type const& unwrap() const& { - if(is_err()) {throw std::runtime_error("result: bad unwrap");} + if(is_err()) + { + throw std::runtime_error("result: bad unwrap: " + + format_error(this->as_err())); + } return this->succ.value; } value_type&& unwrap() && { - if(is_err()) {throw std::runtime_error("result: bad unwrap");} + if(is_err()) + { + throw std::runtime_error("result: bad unwrap: " + + format_error(this->as_err())); + } return std::move(this->succ.value); } @@ -541,6 +556,18 @@ struct result private: + static std::string format_error(std::exception const& excpt) + { + return std::string(excpt.what()); + } + template::value, std::nullptr_t>::type = nullptr> + static std::string format_error(U const& others) + { + std::ostringstream oss; oss << others; + return oss.str(); + } + void cleanup() noexcept { if(this->is_ok_) {this->succ.~success_type();} From 5dbbc1fb1a76dd58e243d8998ae0a0faa619d324 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 6 Dec 2018 19:53:49 +0900 Subject: [PATCH 025/133] add escaped newline to lexer for multiline string to use it in parse_ml_basic_string --- toml/lexer.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/toml/lexer.hpp b/toml/lexer.hpp index 42e90ad..c025d81 100644 --- a/toml/lexer.hpp +++ b/toml/lexer.hpp @@ -137,10 +137,15 @@ using lex_ml_basic_unescaped = exclude, character<0x5C>, character<0x7F>, lex_ml_basic_string_delim>>; + +using lex_ml_basic_escaped_newline = sequence< + lex_escape, maybe, lex_newline, + repeat, unlimited>>; + using lex_ml_basic_char = either; using lex_ml_basic_body = repeat, - lex_newline>>, unlimited>; + lex_ml_basic_escaped_newline>, + unlimited>; using lex_ml_basic_string = sequence; From e05d0bdb84728330e675f4d3d7ce7245e9824015 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 6 Dec 2018 20:13:06 +0900 Subject: [PATCH 026/133] stop using distance(next(iter), last) under some condition, it causes serious error. --- toml/region.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toml/region.hpp b/toml/region.hpp index 709ce98..6d4a9df 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -194,9 +194,10 @@ format_underline(const std::string& message, const location& loc, retval += " | "; retval += make_string(std::distance(line_begin, loc.iter()),' '); retval += '^'; - retval += make_string(std::distance(std::next(loc.iter()), line_end), '-'); + retval += make_string(std::distance(loc.iter(), line_end), '-'); retval += ' '; retval += comment_for_underline; + return retval; } From 3ef33c1637339fc03b5ffc9cd121ecf764588d95 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 19:04:41 +0900 Subject: [PATCH 027/133] change almost everything about datetime --- tests/test_datetime.cpp | 125 +++++++-- toml/datetime.hpp | 597 ++++++++++++++++++++++++++++------------ 2 files changed, 530 insertions(+), 192 deletions(-) diff --git a/tests/test_datetime.cpp b/tests/test_datetime.cpp index b96bbb3..0fbc979 100644 --- a/tests/test_datetime.cpp +++ b/tests/test_datetime.cpp @@ -5,26 +5,117 @@ #define BOOST_TEST_NO_LIB #include #endif -#include -#include +#include -BOOST_AUTO_TEST_CASE(test_datetime_convertible) +BOOST_AUTO_TEST_CASE(test_local_date) { - const auto now = std::chrono::system_clock::now(); - toml::Datetime d1(now); - const std::chrono::system_clock::time_point cvt(d1); - toml::Datetime d2(cvt); + const toml::local_date date(2018, toml::month_t::Jan, 1); + const toml::local_date date1(date); + BOOST_CHECK_EQUAL(date, date1); - BOOST_CHECK_EQUAL(d1, d2); + const std::chrono::system_clock::time_point tp(date); + const toml::local_date date2(tp); + BOOST_CHECK_EQUAL(date, date2); - const auto time = std::chrono::system_clock::to_time_t(now); - toml::Datetime d3(time); - toml::Datetime d4(std::chrono::system_clock::from_time_t(time)); + const toml::local_date date3(2017, toml::month_t::Dec, 31); + BOOST_CHECK(date > date3); - BOOST_CHECK_EQUAL(d3, d4); - - std::this_thread::sleep_for(std::chrono::seconds(1)); - const auto later = std::chrono::system_clock::now(); - toml::Datetime d5(later); - BOOST_CHECK(d1 < d5); + std::ostringstream oss; + oss << date; + BOOST_CHECK_EQUAL(oss.str(), std::string("2018-01-01")); +} + +BOOST_AUTO_TEST_CASE(test_local_time) +{ + const toml::local_time time(12, 30, 45); + const toml::local_time time1(time); + BOOST_CHECK_EQUAL(time, time1); + + const std::chrono::microseconds dur(time); + std::chrono::microseconds us(0); + us += std::chrono::hours (12); + us += std::chrono::minutes(30); + us += std::chrono::seconds(45); + BOOST_CHECK_EQUAL(dur.count(), us.count()); + + const toml::local_time time3(12, 15, 45); + BOOST_CHECK(time > time3); + + { + std::ostringstream oss; + oss << time; + BOOST_CHECK_EQUAL(oss.str(), std::string("12:30:45")); + } + + { + const toml::local_time time4(12, 30, 45, 123, 456); + std::ostringstream oss; + oss << time4; + BOOST_CHECK_EQUAL(oss.str(), std::string("12:30:45.123456")); + } +} + +BOOST_AUTO_TEST_CASE(test_time_offset) +{ + const toml::time_offset time(9, 30); + const toml::time_offset time1(time); + BOOST_CHECK_EQUAL(time, time1); + + const std::chrono::minutes dur(time); + std::chrono::minutes m(0); + m += std::chrono::hours (9); + m += std::chrono::minutes(30); + BOOST_CHECK_EQUAL(dur.count(), m.count()); + + const toml::time_offset time2(9, 0); + BOOST_CHECK(time2 < time); + + std::ostringstream oss; + oss << time; + BOOST_CHECK_EQUAL(oss.str(), std::string("+09:30")); +} + +BOOST_AUTO_TEST_CASE(test_local_datetime) +{ + const toml::local_datetime dt(toml::local_date(2018, toml::month_t::Jan, 1), + toml::local_time(12, 30, 45)); + const toml::local_datetime dt1(dt); + BOOST_CHECK_EQUAL(dt, dt1); + + const std::chrono::system_clock::time_point tp(dt); + const toml::local_datetime dt2(tp); + BOOST_CHECK_EQUAL(dt, dt2); + + std::ostringstream oss; + oss << dt; + BOOST_CHECK_EQUAL(oss.str(), std::string("2018-01-01T12:30:45")); +} + +BOOST_AUTO_TEST_CASE(test_offset_datetime) +{ + const toml::offset_datetime dt(toml::local_date(2018, toml::month_t::Jan, 1), + toml::local_time(12, 30, 45), + toml::time_offset(9, 30)); + const toml::offset_datetime dt1(dt); + BOOST_CHECK_EQUAL(dt, dt1); + + const std::chrono::system_clock::time_point tp1(dt); + const toml::offset_datetime dt2(tp1); + const std::chrono::system_clock::time_point tp2(dt2); + BOOST_CHECK(tp1 == tp2); + + { + std::ostringstream oss; + oss << dt; + BOOST_CHECK_EQUAL(oss.str(), std::string("2018-01-01T12:30:45+09:30")); + } + { + const toml::offset_datetime dt3( + toml::local_date(2018, toml::month_t::Jan, 1), + toml::local_time(12, 30, 45), + toml::time_offset(0, 0)); + std::ostringstream oss; + oss << dt3; + BOOST_CHECK_EQUAL(oss.str(), std::string("2018-01-01T12:30:45Z")); + } } diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 1978286..c0d9e6e 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -7,225 +7,472 @@ namespace toml { -template -struct basic_datetime +enum class month_t : std::int8_t { - typedef unsignedT number_type; - typedef intT offset_type; - constexpr static unsignedT undef = std::numeric_limits::max(); - constexpr static intT nooffset = std::numeric_limits::max(); + Jan = 0, + Feb = 1, + Mar = 2, + Apr = 3, + May = 4, + Jun = 5, + Jul = 6, + Aug = 7, + Sep = 8, + Oct = 9, + Nov = 10, + Dec = 11 +}; - unsignedT year; - unsignedT month; - unsignedT day; - unsignedT hour; - unsignedT minute; - unsignedT second; - unsignedT millisecond; - unsignedT microsecond; - intT offset_hour; - intT offset_minute; +struct local_date +{ + std::int16_t year; // A.D. (like, 2018) + std::int8_t month; // [0, 11] + std::int8_t day; // [1, 31] - basic_datetime() = default; - ~basic_datetime() = default; - basic_datetime(const basic_datetime&) = default; - basic_datetime(basic_datetime&&) = default; - basic_datetime& operator=(const basic_datetime&) = default; - basic_datetime& operator=(basic_datetime&&) = default; - - basic_datetime(unsignedT y, unsignedT m, unsignedT d) - : year(y), month(m), day(d), hour(undef), minute(undef), second(undef), - millisecond(undef), microsecond(undef), - offset_hour(nooffset), offset_minute(nooffset) - {} - basic_datetime(unsignedT h, unsignedT m, unsignedT s, - unsignedT ms, unsignedT us) - : year(undef), month(undef), day(undef), hour(h), minute(m), second(s), - millisecond(ms), microsecond(us), - offset_hour(nooffset), offset_minute(nooffset) - {} - basic_datetime(unsignedT y, unsignedT mth, unsignedT d, - unsignedT h, unsignedT min, unsignedT s, - unsignedT ms, unsignedT us) - : year(y), month(mth), day(d), hour(h), minute(min), second(s), - millisecond(ms), microsecond(us), - offset_hour(nooffset), offset_minute(nooffset) - {} - basic_datetime(unsignedT y, unsignedT mth, unsignedT d, - unsignedT h, unsignedT min, unsignedT s, - unsignedT ss, unsignedT us, intT oh, intT om) - : year(y), month(mth), day(d), hour(h), minute(min), second(s), - millisecond(ss), microsecond(us), offset_hour(oh), offset_minute(om) + local_date(std::int16_t y, month_t m, std::int8_t d) + : year(y), month(static_cast(m)), day(d) {} - basic_datetime(std::chrono::system_clock::time_point tp); - basic_datetime(std::time_t t); + local_date(const std::tm& t) + : year(t.tm_year + 1900), month(t.tm_mon), day(t.tm_mday) + {} + + local_date(const std::chrono::system_clock::time_point& tp) + { + const auto t = std::chrono::system_clock::to_time_t(tp); + const auto tmp = std::localtime(&t); //XXX: not threadsafe! + assert(tmp); // if std::localtime fails, tmp is nullptr + const std::tm time = *tmp; + *this = local_date(time); + } + + local_date(const std::time_t t) + : local_date(std::chrono::system_clock::from_time_t(t)) + {} operator std::chrono::system_clock::time_point() const { - std::tm time; - if(this->year == undef || this->month == undef || this->day == undef) - { - const auto now = std::chrono::system_clock::now(); - const auto t = std::chrono::system_clock::to_time_t(now); - std::tm* t_ = std::localtime(&t); - time.tm_year = t_->tm_year; - time.tm_mon = t_->tm_mon; - time.tm_mday = t_->tm_mday; - } - else - { - time.tm_year = this->year - 1900; - time.tm_mon = this->month - 1; - time.tm_mday = this->day; - } - time.tm_hour = (this->hour == undef) ? 0 : this->hour; - time.tm_min = (this->minute == undef) ? 0 : this->minute; - time.tm_sec = (this->second == undef) ? 0 : this->second; - - auto tp = std::chrono::system_clock::from_time_t(std::mktime(&time)); - tp += std::chrono::milliseconds(this->millisecond); - tp += std::chrono::microseconds(this->microsecond); - // mktime regards the tm struct as localtime. so adding offset is not needed. - - return tp; + // std::mktime returns date as local time zone. no conversion needed + std::tm t; + t.tm_sec = 0; + t.tm_min = 0; + t.tm_hour = 0; + t.tm_mday = this->day; + t.tm_mon = this->month; + t.tm_year = this->year - 1900; + t.tm_wday = 0; // the value will be ignored + t.tm_yday = 0; // the value will be ignored + t.tm_isdst = -1; + return std::chrono::system_clock::from_time_t(std::mktime(&t)); } + operator std::time_t() const { return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); + std::chrono::system_clock::time_point(*this)); } + + local_date() = default; + ~local_date() = default; + local_date(local_date const&) = default; + local_date(local_date&&) = default; + local_date& operator=(local_date const&) = default; + local_date& operator=(local_date&&) = default; }; -template -basic_datetime::basic_datetime(std::chrono::system_clock::time_point tp) +inline bool operator==(const local_date& lhs, const local_date& rhs) { - const auto t = std::chrono::system_clock::to_time_t(tp); - std::tm *time = std::localtime(&t); - this->year = time->tm_year + 1900; - this->month = time->tm_mon + 1; - this->day = time->tm_mday; - this->hour = time->tm_hour; - this->minute = time->tm_min; - this->second = time->tm_sec; - auto t_ = std::chrono::system_clock::from_time_t(std::mktime(time)); - auto diff = tp - t_; - this->millisecond = std::chrono::duration_cast(diff).count() % 1000; - this->microsecond = std::chrono::duration_cast(diff).count() % 1000; - - std::tm *utc = std::gmtime(&t); - int total_offset = (this->hour - utc->tm_hour) * 60 + - (this->minute - utc->tm_min); - if(total_offset > 720) total_offset -= 1440; - else if(total_offset < -720) total_offset += 1440; - offset_hour = total_offset / 60; - offset_minute = total_offset - (offset_hour * 60); + return std::make_tuple(lhs.year, lhs.month, lhs.day) == + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +inline bool operator!=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_date& lhs, const local_date& rhs) +{ + return std::make_tuple(lhs.year, lhs.month, lhs.day) < + std::make_tuple(rhs.year, rhs.month, rhs.day); +} +inline bool operator<=(const local_date& lhs, const local_date& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_date& lhs, const local_date& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_date& lhs, const local_date& rhs) +{ + return !(lhs < rhs); } -template -basic_datetime::basic_datetime(std::time_t t) -{ - *this = basic_datetime(std::chrono::system_clock::from_time_t(t)); -} - - -template +template std::basic_ostream& -operator<<(std::basic_ostream& os, basic_datetime const& dt) +operator<<(std::basic_ostream& os, const local_date& date) { - bool date = false; - if(dt.year != basic_datetime::undef && - dt.month != basic_datetime::undef && - dt.day != basic_datetime::undef) + os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.month + 1) << '-'; + os << std::setfill('0') << std::setw(2) << static_cast(date.day ); + return os; +} + +struct local_time +{ + std::int8_t hour; // [0, 23] + std::int8_t minute; // [0, 59] + std::int8_t second; // [0, 60] + std::int16_t millisecond; // [0, 999] + std::int16_t microsecond; // [0, 999] + + local_time(std::int8_t h, std::int8_t m, std::int8_t s) + : hour(h), minute(m), second(s), millisecond(0), microsecond(0) + {} + + local_time(std::int8_t h, std::int8_t m, std::int8_t s, + std::int16_t ms, std::int16_t us) + : hour(h), minute(m), second(s), millisecond(ms), microsecond(us) + {} + + local_time(const std::tm& t) + : hour(t.tm_hour), minute(t.tm_min), second(t.tm_sec), + millisecond(0), microsecond(0) + {} + + operator std::chrono::microseconds() const { - os << std::setfill('0') << std::setw(4) << dt.year << '-' - << std::setfill('0') << std::setw(2) << dt.month << '-' - << std::setfill('0') << std::setw(2) << dt.day; - date = true; + return std::chrono::microseconds(this->microsecond) + + std::chrono::milliseconds(this->millisecond) + + std::chrono::seconds(this->second) + + std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); } - if(dt.hour != basic_datetime::undef && - dt.minute != basic_datetime::undef && - dt.second != basic_datetime::undef) + + local_time() = default; + ~local_time() = default; + local_time(local_time const&) = default; + local_time(local_time&&) = default; + local_time& operator=(local_time const&) = default; + local_time& operator=(local_time&&) = default; +}; + +inline bool operator==(const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond) == + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond); +} +inline bool operator!=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_time& lhs, const local_time& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond) < + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond); +} +inline bool operator<=(const local_time& lhs, const local_time& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_time& lhs, const local_time& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_time& lhs, const local_time& rhs) +{ + return !(lhs < rhs); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_time& time) +{ + os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.minute ) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.second ); + if(time.millisecond != 0 || time.microsecond != 0) { - if(date) os << 'T'; - os << std::setfill('0') << std::setw(2) << dt.hour << ':' - << std::setfill('0') << std::setw(2) << dt.minute << ':' - << std::setfill('0') << std::setw(2) << dt.second << '.' - << std::setfill('0') << std::setw(3) << dt.millisecond - << std::setfill('0') << std::setw(3) << dt.microsecond; - } - if(dt.offset_hour != basic_datetime::nooffset && - dt.offset_minute != basic_datetime::nooffset) - { - if(dt.offset_hour == 0 && dt.offset_minute == 0) - { - os << 'Z'; - } - else - { - char sign = ' '; - iT oh = dt.offset_hour; - iT om = dt.offset_minute; - om += oh * 60; - if(om > 0) sign = '+'; else sign='-'; - oh = om / 60; - om -= oh * 60; - os << sign << std::setfill('0') << std::setw(2) << std::abs(oh) << ':' - << std::setfill('0') << std::setw(2) << std::abs(om); - } + os << '.'; + os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); + os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); } return os; } -template -inline bool -operator==(basic_datetime const& lhs, basic_datetime const& rhs) +struct time_offset { - return lhs.year == rhs.year && lhs.month == rhs.month && - lhs.day == rhs.day && lhs.minute == rhs.minute && - lhs.second == rhs.second && lhs.millisecond == rhs.millisecond && - lhs.microsecond == rhs.microsecond && - lhs.offset_hour == rhs.offset_hour && - lhs.offset_minute == rhs.offset_minute; -} + std::int8_t hour; // [-12, 12] + std::int8_t minute; // [-59, 59] -template -inline bool -operator!=(basic_datetime const& lhs, basic_datetime const& rhs) + time_offset(std::int8_t h, std::int8_t m): hour(h), minute(m) {} + + operator std::chrono::minutes() const + { + return std::chrono::minutes(this->minute) + + std::chrono::hours(this->hour); + } + + time_offset() = default; + ~time_offset() = default; + time_offset(time_offset const&) = default; + time_offset(time_offset&&) = default; + time_offset& operator=(time_offset const&) = default; + time_offset& operator=(time_offset&&) = default; +}; + +inline bool operator==(const time_offset& lhs, const time_offset& rhs) +{ + return std::make_tuple(lhs.hour, lhs.minute) == + std::make_tuple(rhs.hour, rhs.minute); +} +inline bool operator!=(const time_offset& lhs, const time_offset& rhs) { return !(lhs == rhs); } - -template -inline bool -operator<(basic_datetime const& lhs, basic_datetime const& rhs) +inline bool operator< (const time_offset& lhs, const time_offset& rhs) { - return std::time_t(lhs) < std::time_t(rhs); + return std::make_tuple(lhs.hour, lhs.minute) < + std::make_tuple(rhs.hour, rhs.minute); +} +inline bool operator<=(const time_offset& lhs, const time_offset& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const time_offset& lhs, const time_offset& rhs) +{ + return !(lhs < rhs); } -template -inline bool -operator<=(basic_datetime const& lhs, basic_datetime const& rhs) +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const time_offset& offset) { - return std::time_t(lhs) <= std::time_t(rhs); + if(offset.hour == 0 && offset.minute == 0) + { + os << 'Z'; + return os; + } + if(static_cast(offset.hour) * static_cast(offset.minute) < 0) + { + const int min = static_cast(offset.hour) * 60 + offset.minute; + if(min < 0){os << '-';} else {os << '+';} + os << std::setfill('0') << std::setw(2) << min / 60 << ':'; + os << std::setfill('0') << std::setw(2) << min % 60; + return os; + } + if(offset.hour < 0){os << '-';} else {os << '+';} + os << std::setfill('0') << std::setw(2) << static_cast(offset.hour) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(offset.minute); + return os; } -template -inline bool -operator>(basic_datetime const& lhs, basic_datetime const& rhs) +struct local_datetime { - return std::time_t(lhs) > std::time_t(rhs); + local_date date; + local_time time; + + local_datetime(local_date d, local_time t): date(d), time(t) {} + + local_datetime(const std::tm& t): date(t), time(t){} + + local_datetime(const std::chrono::system_clock::time_point& tp) + { + const auto t = std::chrono::system_clock::to_time_t(tp); + const auto tmp = std::localtime(&t); //XXX: not threadsafe! + assert(tmp); // if std::localtime fails, tmp is nullptr + + std::tm time = *tmp; + this->date = local_date(time); + this->time = local_time(time); + + // std::tm lacks subsecond information, so diff between tp and tm + // can be used to get millisecond & microsecond information. + const auto t_diff = tp - + std::chrono::system_clock::from_time_t(std::mktime(&time)); + this->time.millisecond = std::chrono::duration_cast< + std::chrono::milliseconds>(t_diff).count(); + this->time.microsecond = std::chrono::duration_cast< + std::chrono::microseconds>(t_diff).count(); + } + + local_datetime(const std::time_t t) + : local_datetime(std::chrono::system_clock::from_time_t(t)) + {} + + operator std::chrono::system_clock::time_point() const + { + // std::mktime returns date as local time zone. no conversion needed + return std::chrono::system_clock::time_point(this->date) + + std::chrono::microseconds(this->time); + } + + operator std::time_t() const + { + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); + } + + local_datetime() = default; + ~local_datetime() = default; + local_datetime(local_datetime const&) = default; + local_datetime(local_datetime&&) = default; + local_datetime& operator=(local_datetime const&) = default; + local_datetime& operator=(local_datetime&&) = default; +}; + +inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) == + std::make_tuple(rhs.date, rhs.time); +} +inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time) < + std::make_tuple(rhs.date, rhs.time); +} +inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) +{ + return !(lhs < rhs); } -template -inline bool -operator>=(basic_datetime const& lhs, basic_datetime const& rhs) +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_datetime& dt) { - return std::time_t(lhs) >= std::time_t(rhs); + os << dt.date << 'T' << dt.time; + return os; } +struct offset_datetime +{ + local_date date; + local_time time; + time_offset offset; + + offset_datetime(local_date d, local_time t, time_offset o) + : date(d), time(t), offset(o) + {} + + offset_datetime(const local_datetime& ld) + : date(ld.date), time(ld.time), offset(get_local_offset()) + {} + + offset_datetime(const std::chrono::system_clock::time_point& tp) + : offset_datetime(local_datetime(tp)) + {} + + offset_datetime(const std::time_t& t) + : offset_datetime(local_datetime(t)) + {} + + offset_datetime(const std::tm& t) + : offset_datetime(local_datetime(t)) + {} + + operator std::chrono::system_clock::time_point() const + { + // get date-time in local timezone + std::chrono::system_clock::time_point tp = + std::chrono::system_clock::time_point(this->date) + + std::chrono::microseconds(this->time); + + // get date-time in UTC by subtracting current offset + const auto ofs = get_local_offset(); + tp -= std::chrono::hours (ofs.hour); + tp -= std::chrono::minutes(ofs.minute); + + // add offset defined in this struct + tp += std::chrono::minutes(this->offset); + return tp; + } + + operator std::time_t() const + { + return std::chrono::system_clock::to_time_t( + std::chrono::system_clock::time_point(*this)); + } + + offset_datetime() = default; + ~offset_datetime() = default; + offset_datetime(offset_datetime const&) = default; + offset_datetime(offset_datetime&&) = default; + offset_datetime& operator=(offset_datetime const&) = default; + offset_datetime& operator=(offset_datetime&&) = default; + + private: + + static time_offset get_local_offset() + { + // get current timezone + const auto tmp1 = std::time(nullptr); + const auto tmp2 = std::localtime(&tmp1); // XXX not threadsafe! + assert(tmp2); + std::tm t = *tmp2; + + std::array buf; + const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 + if(result != 5) + { + throw std::runtime_error("toml::offset_datetime: cannot obtain " + "timezone information of current env"); + } + const int ofs = std::atoi(buf.data()); + const int ofs_h = ofs / 100; + const int ofs_m = ofs - (ofs_h * 100); + return time_offset(ofs_h, ofs_m); + } +}; + +inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) == + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs == rhs); +} +inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return std::make_tuple(lhs.date, lhs.time, lhs.offset) < + std::make_tuple(rhs.date, rhs.time, rhs.offset); +} +inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return (lhs < rhs) || (lhs == rhs); +} +inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs <= rhs); +} +inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) +{ + return !(lhs < rhs); +} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const offset_datetime& dt) +{ + os << dt.date << 'T' << dt.time << dt.offset; + return os; +} }//toml #endif// TOML11_DATETIME From ae564bd8148bc9f54a08b6219679edaf59b07395 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 19:22:31 +0900 Subject: [PATCH 028/133] change include file of test_traits --- tests/test_traits.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_traits.cpp b/tests/test_traits.cpp index b40f626..70521ea 100644 --- a/tests/test_traits.cpp +++ b/tests/test_traits.cpp @@ -5,7 +5,7 @@ #define BOOST_TEST_NO_LIB #include #endif -#include +#include #include #include From 1e28cb2d1364899f042974487779597e29504b19 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 19:22:41 +0900 Subject: [PATCH 029/133] add test for concat_string --- tests/test_utility.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_utility.cpp b/tests/test_utility.cpp index 1512e4f..dd08f2f 100644 --- a/tests/test_utility.cpp +++ b/tests/test_utility.cpp @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(test_resize) thrown = true; } BOOST_CHECK(!thrown); - BOOST_CHECK_EQUAL(v.size(), 10); + BOOST_CHECK_EQUAL(v.size(), 10u); } { @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(test_resize) thrown = true; } BOOST_CHECK(!thrown); - BOOST_CHECK_EQUAL(a.size(), 15); + BOOST_CHECK_EQUAL(a.size(), 15u); } { @@ -62,3 +62,9 @@ BOOST_AUTO_TEST_CASE(test_resize) BOOST_CHECK(thrown); } } + +BOOST_AUTO_TEST_CASE(test_concat_to_string) +{ + const std::string cat = toml::concat_to_string("foo", "bar", 42); + BOOST_CHECK(cat == "foobar42"); +} From 25789d1450d376fe4617402231fda08f2b02733b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 19:23:09 +0900 Subject: [PATCH 030/133] set Datetime as offset_datetime prepare for TOML v0.5.0 --- toml/types.hpp | 84 +++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/toml/types.hpp b/toml/types.hpp index 78fb149..a1b94e6 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -1,6 +1,7 @@ #ifndef TOML11_TYPES_H #define TOML11_TYPES_H #include "datetime.hpp" +#include "traits.hpp" #include #include #include @@ -18,7 +19,7 @@ using Boolean = bool; using Integer = std::int64_t; using Float = double; using String = std::basic_string; -using Datetime = basic_datetime; +using Datetime = offset_datetime; using Array = std::vector; using Table = std::unordered_map; @@ -27,60 +28,64 @@ using boolean = Boolean; using integer = Integer; using floating = Float; // XXX float is keyword using string = String; -using datetime = Datetime; +// these are defined in datetime.hpp +// offset_datetime +// local_datetime +// local_date +// local_time using array = Array; using table = Table; enum class value_t : std::uint8_t { - Boolean = 1, - Integer = 2, - Float = 3, - String = 4, - Datetime = 5, - Array = 6, - Table = 7, - Empty = 0, - Unknown = 255, + Boolean = 1, + Integer = 2, + Float = 3, + String = 4, + Datetime = 5, + LocalDatetime = 6, + LocalDate = 7, + LocalTime = 8, + Array = 9, + Table = 10, + Empty = 0, + Unknown = 255, }; -template> +constexpr inline bool is_valid(value_t vt) +{ + return vt != value_t::Unknown; +} + +template inline std::basic_ostream& operator<<(std::basic_ostream& os, value_t t) { switch(t) { - case toml::value_t::Boolean : os << "Boolean"; return os; - case toml::value_t::Integer : os << "Integer"; return os; - case toml::value_t::Float : os << "Float"; return os; - case toml::value_t::String : os << "String"; return os; - case toml::value_t::Datetime: os << "Datetime"; return os; - case toml::value_t::Array : os << "Array"; return os; - case toml::value_t::Table : os << "Table"; return os; - case toml::value_t::Empty : os << "Empty"; return os; - case toml::value_t::Unknown : os << "Unknown"; return os; - default : os << "Nothing"; return os; + case toml::value_t::Boolean : os << "boolean"; return os; + case toml::value_t::Integer : os << "integer"; return os; + case toml::value_t::Float : os << "float"; return os; + case toml::value_t::String : os << "string"; return os; + case toml::value_t::Datetime : os << "offset_datetime"; return os; + case toml::value_t::LocalDatetime: os << "local_datetime"; return os; + case toml::value_t::LocalDate : os << "local_date"; return os; + case toml::value_t::LocalTime : os << "local_time"; return os; + case toml::value_t::Array : os << "array"; return os; + case toml::value_t::Table : os << "table"; return os; + case toml::value_t::Empty : os << "empty"; return os; + case toml::value_t::Unknown : os << "unknown"; return os; + default : os << "nothing"; return os; } } template, typename alloc = std::allocator> -inline std::basic_string -stringize(value_t t) +inline std::basic_string stringize(value_t t) { - switch(t) - { - case toml::value_t::Boolean : return "Boolean"; - case toml::value_t::Integer : return "Integer"; - case toml::value_t::Float : return "Float"; - case toml::value_t::String : return "String"; - case toml::value_t::Datetime: return "Datetime"; - case toml::value_t::Array : return "Array"; - case toml::value_t::Table : return "Table"; - case toml::value_t::Empty : return "Empty"; - case toml::value_t::Unknown : return "Unknown"; - default : return "Nothing"; - } + std::ostringstream oss; + oss << t; + return oss.str(); } namespace detail @@ -99,11 +104,6 @@ constexpr inline value_t check_type() value_t::Unknown; } -constexpr inline bool is_valid(value_t vt) -{ - return vt != value_t::Unknown; -} - template struct toml_default_type{}; template<> struct toml_default_type{typedef Boolean type;}; template<> struct toml_default_type{typedef Integer type;}; From 861444a02b7141d353b1d0d09fce0bb300c4bf5b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 19:23:53 +0900 Subject: [PATCH 031/133] disable some of the tests once --- tests/CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b6a0e14..da45062 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,20 +5,20 @@ set(TEST_NAMES test_lex_datetime test_lex_string test_lex_key_comment - test_traits + test_datetime test_utility test_result - test_value - test_to_toml - test_from_toml - test_get - test_get_or - test_value_operator - test_datetime - test_acceptor - test_parser - test_parse_file - test_parse_unicode + test_traits +# test_value +# test_to_toml +# test_from_toml +# test_get +# test_get_or +# test_value_operator +# test_acceptor +# test_parser +# test_parse_file +# test_parse_unicode ) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) From 66807d19d1feb424eaa3758271f587f85dc050e5 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 19:40:58 +0900 Subject: [PATCH 032/133] add specializations --- toml/types.hpp | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/toml/types.hpp b/toml/types.hpp index a1b94e6..76418e1 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -104,16 +104,19 @@ constexpr inline value_t check_type() value_t::Unknown; } -template struct toml_default_type{}; -template<> struct toml_default_type{typedef Boolean type;}; -template<> struct toml_default_type{typedef Integer type;}; -template<> struct toml_default_type{typedef Float type;}; -template<> struct toml_default_type{typedef String type;}; -template<> struct toml_default_type{typedef Datetime type;}; -template<> struct toml_default_type{typedef Array type;}; -template<> struct toml_default_type{typedef Table type;}; -template<> struct toml_default_type{typedef void type;}; -template<> struct toml_default_type{typedef void type;}; +template struct toml_default_type; +template<> struct toml_default_type {typedef Boolean type;}; +template<> struct toml_default_type {typedef Integer type;}; +template<> struct toml_default_type {typedef Float type;}; +template<> struct toml_default_type {typedef String type;}; +template<> struct toml_default_type {typedef offset_datetime type;}; +template<> struct toml_default_type{typedef local_datetime type;}; +template<> struct toml_default_type {typedef local_date type;}; +template<> struct toml_default_type {typedef local_time type;}; +template<> struct toml_default_type {typedef Array type;}; +template<> struct toml_default_type {typedef Table type;}; +template<> struct toml_default_type {typedef void type;}; +template<> struct toml_default_type {typedef void type;}; template struct is_exact_toml_type : disjunction< @@ -121,10 +124,17 @@ struct is_exact_toml_type : disjunction< std::is_same, std::is_same, std::is_same, - std::is_same, + std::is_same, + std::is_same, + std::is_same, + std::is_same, std::is_same, std::is_same >{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type : is_exact_toml_type{}; +template struct is_exact_toml_type: is_exact_toml_type{}; template struct is_map : conjunction< @@ -133,14 +143,23 @@ struct is_map : conjunction< has_key_type, has_mapped_type >{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; +template struct is_map : is_map{}; template struct is_container : conjunction< negation>, - negation>, + negation>, has_iterator, has_value_type >{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; +template struct is_container : is_container{}; + } // detail } // toml From 2b2a05148e76c73cb194214c4ec1aa863794d43f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 20:18:50 +0900 Subject: [PATCH 033/133] add from_string to utility --- tests/test_utility.cpp | 12 ++++++++++++ toml/utility.hpp | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/tests/test_utility.cpp b/tests/test_utility.cpp index dd08f2f..b879c54 100644 --- a/tests/test_utility.cpp +++ b/tests/test_utility.cpp @@ -68,3 +68,15 @@ BOOST_AUTO_TEST_CASE(test_concat_to_string) const std::string cat = toml::concat_to_string("foo", "bar", 42); BOOST_CHECK(cat == "foobar42"); } + +BOOST_AUTO_TEST_CASE(test_from_string) +{ + { + const std::string str("123"); + BOOST_CHECK_EQUAL(toml::from_string(str, 0), 123); + } + { + const std::string str("01"); + BOOST_CHECK_EQUAL(toml::from_string(str, 0), 1); + } +} diff --git a/toml/utility.hpp b/toml/utility.hpp index 2e7b0f0..26eff2d 100644 --- a/toml/utility.hpp +++ b/toml/utility.hpp @@ -62,5 +62,16 @@ std::string concat_to_string(Ts&& ... args) return detail::concat_to_string_impl(oss, std::forward(args) ...); } +template +T from_string(const std::string& str, U&& opt) +{ + T v(std::forward(opt)); + std::istringstream iss(str); + iss >> v; + return v; +} + + + }// toml #endif // TOML11_UTILITY From bb215836dc8118f3d25bc319fee6a1d4f4bc6d53 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 20:39:37 +0900 Subject: [PATCH 034/133] add missing header files --- toml/datetime.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index c0d9e6e..8b2970e 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -1,7 +1,12 @@ #ifndef TOML11_DATETIME #define TOML11_DATETIME #include +#include +#include +#include #include +#include +#include #include namespace toml From 8388664fc68e6742b63bf8482541111f0d02c67e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 8 Dec 2018 22:44:15 +0900 Subject: [PATCH 035/133] add map_err_or_else to result --- tests/test_result.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ toml/result.hpp | 25 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/tests/test_result.cpp b/tests/test_result.cpp index 472f76b..5d7bcdd 100644 --- a/tests/test_result.cpp +++ b/tests/test_result.cpp @@ -259,6 +259,49 @@ BOOST_AUTO_TEST_CASE(test_map_or_else) } } +BOOST_AUTO_TEST_CASE(test_map_err_or_else) +{ + { + const toml::result result(toml::ok(42)); + const auto mapped = result.map_err_or_else( + [](const std::string i) -> std::string { + return i + i; + }, "foobar"); + + BOOST_CHECK_EQUAL(mapped, "foobar"); + } + { + toml::result, std::string> + result(toml::ok(std::unique_ptr(new int(42)))); + const auto mapped = std::move(result).map_err_or_else( + [](const std::string i) -> std::string { + return i + i; + }, "foobar"); + + BOOST_CHECK_EQUAL(mapped, "foobar"); + } + { + const toml::result result(toml::err("hoge")); + const auto mapped = result.map_err_or_else( + [](const std::string i) -> std::string { + return i + i; + }, "foobar"); + + BOOST_CHECK_EQUAL(mapped, "hogehoge"); + } + { + toml::result, std::string> + result(toml::err("hoge")); + const auto mapped = result.map_err_or_else( + [](const std::string i) -> std::string { + return i + i; + }, "foobar"); + + BOOST_CHECK_EQUAL(mapped, "hogehoge"); + } +} + + BOOST_AUTO_TEST_CASE(test_and_then) { { diff --git a/toml/result.hpp b/toml/result.hpp index fde9729..ac08059 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -494,6 +494,31 @@ struct result return f(std::move(this->as_ok())); } + // prerequisities + // F: E -> U + // retval: U + template + return_type_of_t + map_err_or_else(F&& f, U&& opt) & + { + if(this->is_ok()){return std::forward(opt);} + return f(this->as_err()); + } + template + return_type_of_t + map_err_or_else(F&& f, U&& opt) const& + { + if(this->is_ok()){return std::forward(opt);} + return f(this->as_err()); + } + template + return_type_of_t + map_err_or_else(F&& f, U&& opt) && + { + if(this->is_ok()){return std::forward(opt);} + return f(std::move(this->as_err())); + } + // prerequisities: // F: func T -> U // toml::err(error_type) should be convertible to U. From 04854f9d21d3138a3fa262a7fcf3398d65596d94 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 00:00:15 +0900 Subject: [PATCH 036/133] stop having begin/end iterator in region/location --- toml/region.hpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 6d4a9df..5ab86f4 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -36,8 +36,7 @@ struct location location(std::string name, Container cont) : source_(std::make_shared(std::move(cont))), - source_name_(std::move(name)), begin_(source_->cbegin()), - iter_(source_->cbegin()), end_(source_->cend()) + source_name_(std::move(name)), iter_(source_->cbegin()) {} location(const location&) = default; location(location&&) = default; @@ -48,8 +47,8 @@ struct location const_iterator& iter() noexcept {return iter_;} const_iterator iter() const noexcept {return iter_;} - const_iterator begin() const noexcept {return begin_;} - const_iterator end() const noexcept {return end_;} + const_iterator begin() const noexcept {return source_->cbegin();} + const_iterator end() const noexcept {return source_->cend();} source_ptr const& source() const& noexcept {return source_;} source_ptr&& source() && noexcept {return std::move(source_);} @@ -60,7 +59,7 @@ struct location source_ptr source_; std::string source_name_; - const_iterator begin_, iter_, end_; + const_iterator iter_; }; // region in a container, normally in a file content. @@ -76,22 +75,18 @@ struct region region(const location& loc) : source_(loc.source()), source_name_(loc.name()), - begin_(loc.begin()), first_(loc.iter()), - last_(loc.iter()), end_(loc.end()) + first_(loc.iter()), last_(loc.iter()) {} region(location&& loc) : source_(loc.source()), source_name_(loc.name()), - begin_(loc.begin()), first_(loc.iter()), - last_(loc.iter()), end_(loc.end()) + first_(loc.iter()), last_(loc.iter()) {} region(const location& loc, const_iterator f, const_iterator l) - : source_(loc.source()), source_name_(loc.name()), - begin_(loc.begin()), first_(f), last_(l), end_(loc.end()) + : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) {} region(location&& loc, const_iterator f, const_iterator l) - : source_(loc.source()), source_name_(loc.name()), - begin_(loc.begin()), first_(f), last_(l), end_(loc.end()) + : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) {} region(const region&) = default; @@ -102,7 +97,7 @@ struct region region& operator+=(const region& other) { - if(this->begin_ != other.begin_ || this->end_ != other.end_ || + if(this->begin() != other.begin() || this->end() != other.end() || this->last_ != other.first_) { throw internal_error("invalid region concatenation"); @@ -114,8 +109,8 @@ struct region std::string str() const {return make_string(first_, last_);} std::size_t size() const {return std::distance(first_, last_);} - const_iterator begin() const noexcept {return begin_;} - const_iterator end() const noexcept {return end_;} + const_iterator begin() const noexcept {return source_->cbegin();} + const_iterator end() const noexcept {return source_->cend();} const_iterator first() const noexcept {return first_;} const_iterator last() const noexcept {return last_;} @@ -128,7 +123,7 @@ struct region source_ptr source_; std::string source_name_; - const_iterator begin_, first_, last_, end_; + const_iterator first_, last_; }; // to show a better error message. From 48f3b73b915e64b89be4951fdd69d22c89089077 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 00:11:07 +0900 Subject: [PATCH 037/133] add ctor(local_datetime, time_offset) to offset_datetime --- toml/datetime.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index c0d9e6e..768b1ca 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -361,26 +361,25 @@ operator<<(std::basic_ostream& os, const local_datetime& dt) struct offset_datetime { - local_date date; - local_time time; + local_date date; + local_time time; time_offset offset; offset_datetime(local_date d, local_time t, time_offset o) : date(d), time(t), offset(o) {} - + offset_datetime(const local_datetime& dt, time_offset o) + : date(dt.date), time(dt.time), offset(o) + {} offset_datetime(const local_datetime& ld) : date(ld.date), time(ld.time), offset(get_local_offset()) {} - offset_datetime(const std::chrono::system_clock::time_point& tp) : offset_datetime(local_datetime(tp)) {} - offset_datetime(const std::time_t& t) : offset_datetime(local_datetime(t)) {} - offset_datetime(const std::tm& t) : offset_datetime(local_datetime(t)) {} From f834e0d14291b7c09752bb1f1f53c91fa69add99 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 11:06:06 +0900 Subject: [PATCH 038/133] cosmetic: sort value_t in types.h --- toml/types.hpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/toml/types.hpp b/toml/types.hpp index 76418e1..ef413df 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -38,18 +38,18 @@ using table = Table; enum class value_t : std::uint8_t { - Boolean = 1, - Integer = 2, - Float = 3, - String = 4, - Datetime = 5, - LocalDatetime = 6, - LocalDate = 7, - LocalTime = 8, - Array = 9, - Table = 10, - Empty = 0, - Unknown = 255, + Empty = 0, + Boolean = 1, + Integer = 2, + Float = 3, + String = 4, + OffsetDatetime = 5, + LocalDatetime = 6, + LocalDate = 7, + LocalTime = 8, + Array = 9, + Table = 10, + Unknown = 255, }; constexpr inline bool is_valid(value_t vt) From 2b3a4d49a5c7816ea9bee38ef14b192864989142 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 12:41:38 +0900 Subject: [PATCH 039/133] add region_base to contain it in toml::value to make toml::get and toml::value::cast return better error messages --- toml/region.hpp | 99 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index 5ab86f4..a5d43a2 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -65,14 +65,38 @@ struct location // region in a container, normally in a file content. // shared_ptr points the resource that the iter points. // combinators returns this. -// it can be used not only for resource handling, but also error message. +// it will be used to generate better error messages. +struct region_base +{ + region_base() = default; + virtual ~region_base() = default; + region_base(const region_base&) = default; + region_base(region_base&& ) = default; + region_base& operator=(const region_base&) = default; + region_base& operator=(region_base&& ) = default; + + virtual bool is_ok() const noexcept {return false;} + + virtual std::string str() const {return std::string("");} + virtual std::string name() const {return std::string("");} + virtual std::string line() const {return std::string("");} + virtual std::string line_num() const {return std::string("?");} + + virtual std::size_t before() const noexcept {return 0;} + virtual std::size_t size() const noexcept {return 0;} + virtual std::size_t after() const noexcept {return 0;} +}; + template -struct region +struct region final : public region_base { static_assert(std::is_same::value,""); using const_iterator = typename Container::const_iterator; using source_ptr = std::shared_ptr; + // delete default constructor. source_ never be null. + region() = delete; + region(const location& loc) : source_(loc.source()), source_name_(loc.name()), first_(loc.iter()), last_(loc.iter()) @@ -106,18 +130,51 @@ struct region return *this; } - std::string str() const {return make_string(first_, last_);} - std::size_t size() const {return std::distance(first_, last_);} + bool is_ok() const noexcept override {return static_cast(source_);} - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - const_iterator first() const noexcept {return first_;} - const_iterator last() const noexcept {return last_;} + std::string str() const override {return make_string(first_, last_);} + std::string line() const override + { + return make_string(this->line_begin(), this->line_end()); + } + std::string line_num() const override + { + return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); + } + + std::size_t size() const noexcept override + { + return std::distance(first_, last_); + } + std::size_t before() const noexcept override + { + return std::distance(this->line_begin(), this->first()); + } + std::size_t after() const noexcept override + { + return std::distance(this->last(), this->line_end()); + } + + const_iterator line_begin() const noexcept + { + using reverse_iterator = std::reverse_iterator; + return std::find(reverse_iterator(this->first()), + reverse_iterator(this->begin()), '\n').base(); + } + const_iterator line_end() const noexcept + { + return std::find(this->last(), this->end(), '\n'); + } + + const_iterator begin() const noexcept {return source_->cbegin();} + const_iterator end() const noexcept {return source_->cend();} + const_iterator first() const noexcept {return first_;} + const_iterator last() const noexcept {return last_;} source_ptr const& source() const& noexcept {return source_;} source_ptr&& source() && noexcept {return std::move(source_);} - std::string const& name() const noexcept {return source_name_;} + std::string name() const override {return source_name_;} private: @@ -127,20 +184,12 @@ struct region }; // to show a better error message. -template -std::string -format_underline(const std::string& message, const region& reg, - const std::string& comment_for_underline) +inline std::string format_underline(const std::string& message, + const region_base& reg, const std::string& comment_for_underline) { - using const_iterator = typename region::const_iterator; - using reverse_iterator = std::reverse_iterator; - const auto line_begin = std::find(reverse_iterator(reg.first()), - reverse_iterator(reg.begin()), - '\n').base(); - const auto line_end = std::find(reg.last(), reg.end(), '\n'); - const auto line_number = std::to_string( - 1 + std::count(reg.begin(), reg.first(), '\n')); + const auto line = reg.line(); + const auto line_number = reg.line_num(); std::string retval; retval += message; @@ -149,12 +198,12 @@ format_underline(const std::string& message, const region& reg, retval += "\n "; retval += line_number; retval += " | "; - retval += make_string(line_begin, line_end); + retval += line; retval += '\n'; retval += make_string(line_number.size() + 1, ' '); retval += " | "; - retval += make_string(std::distance(line_begin, reg.first()), ' '); - retval += make_string(std::distance(reg.first(), reg.last()), '~'); + retval += make_string(reg.before(), ' '); + retval += make_string(reg.size(), '~'); retval += ' '; retval += comment_for_underline; return retval; @@ -189,7 +238,7 @@ format_underline(const std::string& message, const location& loc, retval += " | "; retval += make_string(std::distance(line_begin, loc.iter()),' '); retval += '^'; - retval += make_string(std::distance(loc.iter(), line_end), '-'); + retval += make_string(std::distance(loc.iter(), line_end), '~'); retval += ' '; retval += comment_for_underline; From 84676eab0ba67bd5608e5b3aac2ad3f324b12e52 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 13:05:09 +0900 Subject: [PATCH 040/133] improve quality of error message --- toml/region.hpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index a5d43a2..c97275d 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -3,6 +3,7 @@ #include "exception.hpp" #include #include +#include #include #include @@ -78,8 +79,8 @@ struct region_base virtual bool is_ok() const noexcept {return false;} virtual std::string str() const {return std::string("");} - virtual std::string name() const {return std::string("");} - virtual std::string line() const {return std::string("");} + virtual std::string name() const {return std::string("unknown location");} + virtual std::string line() const {return std::string("unknown line");} virtual std::string line_num() const {return std::string("?");} virtual std::size_t before() const noexcept {return 0;} @@ -187,7 +188,6 @@ struct region final : public region_base inline std::string format_underline(const std::string& message, const region_base& reg, const std::string& comment_for_underline) { - const auto line = reg.line(); const auto line_number = reg.line_num(); @@ -213,7 +213,8 @@ inline std::string format_underline(const std::string& message, template std::string format_underline(const std::string& message, const location& loc, - const std::string& comment_for_underline) + const std::string& comment_for_underline, + std::initializer_list helps = {}) { using const_iterator = typename location::const_iterator; using reverse_iterator = std::reverse_iterator; @@ -238,10 +239,20 @@ format_underline(const std::string& message, const location& loc, retval += " | "; retval += make_string(std::distance(line_begin, loc.iter()),' '); retval += '^'; - retval += make_string(std::distance(loc.iter(), line_end), '~'); + retval += make_string(std::distance(loc.iter(), line_end), '-'); retval += ' '; retval += comment_for_underline; - + if(helps.size() != 0) + { + retval += '\n'; + retval += make_string(line_number.size() + 1, ' '); + retval += " | "; + for(const auto help : helps) + { + retval += "\nHint: "; + retval += help; + } + } return retval; } From b0e7efa1e0a2d0ad6fa102b14d99f8099070157e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 13:38:57 +0900 Subject: [PATCH 041/133] make some constructors explicit --- toml/datetime.hpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 224dc30..bf455ba 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -38,11 +38,11 @@ struct local_date : year(y), month(static_cast(m)), day(d) {} - local_date(const std::tm& t) + explicit local_date(const std::tm& t) : year(t.tm_year + 1900), month(t.tm_mon), day(t.tm_mday) {} - local_date(const std::chrono::system_clock::time_point& tp) + explicit local_date(const std::chrono::system_clock::time_point& tp) { const auto t = std::chrono::system_clock::to_time_t(tp); const auto tmp = std::localtime(&t); //XXX: not threadsafe! @@ -51,7 +51,7 @@ struct local_date *this = local_date(time); } - local_date(const std::time_t t) + explicit local_date(const std::time_t t) : local_date(std::chrono::system_clock::from_time_t(t)) {} @@ -139,7 +139,7 @@ struct local_time : hour(h), minute(m), second(s), millisecond(ms), microsecond(us) {} - local_time(const std::tm& t) + explicit local_time(const std::tm& t) : hour(t.tm_hour), minute(t.tm_min), second(t.tm_sec), millisecond(0), microsecond(0) {} @@ -282,9 +282,9 @@ struct local_datetime local_datetime(local_date d, local_time t): date(d), time(t) {} - local_datetime(const std::tm& t): date(t), time(t){} + explicit local_datetime(const std::tm& t): date(t), time(t){} - local_datetime(const std::chrono::system_clock::time_point& tp) + explicit local_datetime(const std::chrono::system_clock::time_point& tp) { const auto t = std::chrono::system_clock::to_time_t(tp); const auto tmp = std::localtime(&t); //XXX: not threadsafe! @@ -304,7 +304,7 @@ struct local_datetime std::chrono::microseconds>(t_diff).count(); } - local_datetime(const std::time_t t) + explicit local_datetime(const std::time_t t) : local_datetime(std::chrono::system_clock::from_time_t(t)) {} @@ -376,16 +376,16 @@ struct offset_datetime offset_datetime(const local_datetime& dt, time_offset o) : date(dt.date), time(dt.time), offset(o) {} - offset_datetime(const local_datetime& ld) + explicit offset_datetime(const local_datetime& ld) : date(ld.date), time(ld.time), offset(get_local_offset()) {} - offset_datetime(const std::chrono::system_clock::time_point& tp) + explicit offset_datetime(const std::chrono::system_clock::time_point& tp) : offset_datetime(local_datetime(tp)) {} - offset_datetime(const std::time_t& t) + explicit offset_datetime(const std::time_t& t) : offset_datetime(local_datetime(t)) {} - offset_datetime(const std::tm& t) + explicit offset_datetime(const std::tm& t) : offset_datetime(local_datetime(t)) {} From ac3025d92f362860bdb25c7986aa15d9aa21e86f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 13:39:13 +0900 Subject: [PATCH 042/133] change default return value of region_base --- toml/region.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index c97275d..05a00a4 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -78,8 +78,8 @@ struct region_base virtual bool is_ok() const noexcept {return false;} - virtual std::string str() const {return std::string("");} - virtual std::string name() const {return std::string("unknown location");} + virtual std::string str() const {return std::string("unknown region");} + virtual std::string name() const {return std::string("unknown file");} virtual std::string line() const {return std::string("unknown line");} virtual std::string line_num() const {return std::string("?");} From 9fadf71a10c88917219cbfb8c182f4e3c96145f9 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 16:27:47 +0900 Subject: [PATCH 043/133] add constructor from duration to local_time --- toml/datetime.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index bf455ba..82d283b 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -144,6 +144,25 @@ struct local_time millisecond(0), microsecond(0) {} + template + explicit local_time(std::chrono::duration t) + { + const auto h = std::chrono::duration_cast(t); + this->hour = h.count(); + t -= h; + const auto m = std::chrono::duration_cast(t); + this->minute = m.count(); + t -= m; + const auto s = std::chrono::duration_cast(t); + this->second = s.count(); + t -= s; + const auto ms = std::chrono::duration_cast(t); + this->millisecond = ms.count(); + t -= ms; + const auto us = std::chrono::duration_cast(t); + this->microsecond = us.count(); + } + operator std::chrono::microseconds() const { return std::chrono::microseconds(this->microsecond) + From 80eafd1424db61c311ca91228d50043bef8a9321 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 16:34:47 +0900 Subject: [PATCH 044/133] add datetime variants and rearrange type-related functions --- toml/types.hpp | 117 ++++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 54 deletions(-) diff --git a/toml/types.hpp b/toml/types.hpp index ef413df..315f0e5 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -1,11 +1,10 @@ #ifndef TOML11_TYPES_H #define TOML11_TYPES_H #include "datetime.hpp" +#include "string.hpp" #include "traits.hpp" -#include #include #include -#include namespace toml { @@ -13,26 +12,24 @@ namespace toml using character = char; class value; -using key = std::basic_string; +using key = std::string; -using Boolean = bool; -using Integer = std::int64_t; -using Float = double; -using String = std::basic_string; -using Datetime = offset_datetime; -using Array = std::vector; -using Table = std::unordered_map; +using Boolean = bool; +using Integer = std::int64_t; +using Float = double; +using String = ::toml::string; +using Datetime = offset_datetime; +using OffsetDatetime = offset_datetime; +using LocalDatetime = local_datetime; +using LocalDate = local_date; +using LocalTime = local_time; +using Array = std::vector; +using Table = std::unordered_map; // alias for snake_case, consistency with STL/Boost, toml::key, toml::value using boolean = Boolean; using integer = Integer; -using floating = Float; // XXX float is keyword -using string = String; -// these are defined in datetime.hpp -// offset_datetime -// local_datetime -// local_date -// local_time +using floating = Float; // XXX `float` is keyword. we can't use it here using array = Array; using table = Table; @@ -52,30 +49,25 @@ enum class value_t : std::uint8_t Unknown = 255, }; -constexpr inline bool is_valid(value_t vt) -{ - return vt != value_t::Unknown; -} - template inline std::basic_ostream& operator<<(std::basic_ostream& os, value_t t) { switch(t) { - case toml::value_t::Boolean : os << "boolean"; return os; - case toml::value_t::Integer : os << "integer"; return os; - case toml::value_t::Float : os << "float"; return os; - case toml::value_t::String : os << "string"; return os; - case toml::value_t::Datetime : os << "offset_datetime"; return os; - case toml::value_t::LocalDatetime: os << "local_datetime"; return os; - case toml::value_t::LocalDate : os << "local_date"; return os; - case toml::value_t::LocalTime : os << "local_time"; return os; - case toml::value_t::Array : os << "array"; return os; - case toml::value_t::Table : os << "table"; return os; - case toml::value_t::Empty : os << "empty"; return os; - case toml::value_t::Unknown : os << "unknown"; return os; - default : os << "nothing"; return os; + case toml::value_t::Boolean : os << "boolean"; return os; + case toml::value_t::Integer : os << "integer"; return os; + case toml::value_t::Float : os << "float"; return os; + case toml::value_t::String : os << "string"; return os; + case toml::value_t::OffsetDatetime: os << "offset_datetime"; return os; + case toml::value_t::LocalDatetime : os << "local_datetime"; return os; + case toml::value_t::LocalDate : os << "local_date"; return os; + case toml::value_t::LocalTime : os << "local_time"; return os; + case toml::value_t::Array : os << "array"; return os; + case toml::value_t::Table : os << "table"; return os; + case toml::value_t::Empty : os << "empty"; return os; + case toml::value_t::Unknown : os << "unknown"; return os; + default : os << "nothing"; return os; } } @@ -90,33 +82,50 @@ inline std::basic_string stringize(value_t t) namespace detail { +template struct is_chrono_duration: std::false_type{}; +template +struct is_chrono_duration>: std::true_type{}; template constexpr inline value_t check_type() { - return std::is_same, toml::Boolean >::value ? value_t::Boolean : - std::is_integral>::value ? value_t::Integer : - std::is_floating_point>::value ? value_t::Float : - std::is_convertible, toml::String >::value ? value_t::String : - std::is_convertible, toml::Datetime>::value ? value_t::Datetime: - std::is_convertible, toml::Array >::value ? value_t::Array : - std::is_convertible, toml::Table >::value ? value_t::Table : + using type = typename std::remove_cv< + typename std::remove_reference::type + >::type; + return std::is_same::value ? value_t::Boolean : + std::is_integral::value ? value_t::Integer : + std::is_floating_point::value ? value_t::Float : + std::is_same::value ? value_t::String : + std::is_same::value ? value_t::String : + std::is_same::value ? value_t::LocalDate : + std::is_same::value ? value_t::LocalTime : + is_chrono_duration::value ? value_t::LocalTime : + std::is_same::value ? value_t::LocalDatetime : + std::is_same::value ? value_t::OffsetDatetime : + std::is_same::value ? value_t::OffsetDatetime : + std::is_convertible::value ? value_t::Array : + std::is_convertible::value ? value_t::Table : value_t::Unknown; } +constexpr inline bool is_valid(value_t vt) +{ + return vt != value_t::Unknown; +} + template struct toml_default_type; -template<> struct toml_default_type {typedef Boolean type;}; -template<> struct toml_default_type {typedef Integer type;}; -template<> struct toml_default_type {typedef Float type;}; -template<> struct toml_default_type {typedef String type;}; -template<> struct toml_default_type {typedef offset_datetime type;}; -template<> struct toml_default_type{typedef local_datetime type;}; -template<> struct toml_default_type {typedef local_date type;}; -template<> struct toml_default_type {typedef local_time type;}; -template<> struct toml_default_type {typedef Array type;}; -template<> struct toml_default_type {typedef Table type;}; -template<> struct toml_default_type {typedef void type;}; -template<> struct toml_default_type {typedef void type;}; +template<> struct toml_default_type {typedef boolean type;}; +template<> struct toml_default_type {typedef integer type;}; +template<> struct toml_default_type {typedef floating type;}; +template<> struct toml_default_type {typedef string type;}; +template<> struct toml_default_type{typedef offset_datetime type;}; +template<> struct toml_default_type {typedef local_datetime type;}; +template<> struct toml_default_type {typedef local_date type;}; +template<> struct toml_default_type {typedef local_time type;}; +template<> struct toml_default_type {typedef array type;}; +template<> struct toml_default_type {typedef table type;}; +template<> struct toml_default_type {typedef void type;}; +template<> struct toml_default_type {typedef void type;}; template struct is_exact_toml_type : disjunction< From d1d5ca6bf8e81c6c0acc27acebb2e22a01beb921 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 16:40:57 +0900 Subject: [PATCH 045/133] add toml::string to have basic/literal flag --- toml/string.hpp | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 toml/string.hpp diff --git a/toml/string.hpp b/toml/string.hpp new file mode 100644 index 0000000..bcda155 --- /dev/null +++ b/toml/string.hpp @@ -0,0 +1,131 @@ +#ifndef TOML11_STRING_H +#define TOML11_STRING_H +#include +#include + +namespace toml +{ + +enum class string_t : std::uint8_t +{ + basic = 0, + literal = 1, +}; + +struct string +{ + string() = default; + ~string() = default; + string(const string& s) = default; + string(string&& s) = default; + string& operator=(const string& s) = default; + string& operator=(string&& s) = default; + + string(const std::string& s): kind(string_t::basic), str(s){} + string(const std::string& s, string_t k): kind(k), str(s){} + string(const char* s): kind(string_t::basic), str(s){} + string(const char* s, string_t k): kind(k), str(s){} + + string(std::string&& s): kind(string_t::basic), str(std::move(s)){} + string(std::string&& s, string_t k): kind(k), str(std::move(s)){} + + string& operator=(const std::string& s) + {kind = string_t::basic; str = s; return *this;} + string& operator=(std::string&& s) + {kind = string_t::basic; str = std::move(s); return *this;} + + operator std::string& () & noexcept {return str;} + operator std::string const& () const& noexcept {return str;} + operator std::string&& () && noexcept {return std::move(str);} + + string_t kind; + std::string str; +}; + +inline bool operator==(const string& lhs, const string& rhs) +{ + return lhs.kind == rhs.kind && lhs.str == rhs.str; +} +inline bool operator!=(const string& lhs, const string& rhs) +{ + return !(lhs == rhs); +} +inline bool operator<(const string& lhs, const string& rhs) +{ + return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); +} +inline bool operator>(const string& lhs, const string& rhs) +{ + return rhs < lhs; +} +inline bool operator<=(const string& lhs, const string& rhs) +{ + return !(rhs < lhs); +} +inline bool operator>=(const string& lhs, const string& rhs) +{ + return !(lhs < rhs); +} + +inline bool +operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} +inline bool +operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} +inline bool +operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;} +inline bool +operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;} +inline bool +operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} +inline bool +operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} + +inline bool +operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} +inline bool +operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} +inline bool +operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;} +inline bool +operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;} +inline bool +operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} +inline bool +operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} + +inline bool +operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} +inline bool +operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} +inline bool +operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);} +inline bool +operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);} +inline bool +operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} +inline bool +operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} + +inline bool +operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} +inline bool +operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} +inline bool +operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;} +inline bool +operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;} +inline bool +operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} +inline bool +operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const string& str) +{ + os << str.str; + return os; +} + +} // toml +#endif// TOML11_STRING_H From 2696e4e6ba42165d11047aced7fa22fcd18d42b5 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 16:41:45 +0900 Subject: [PATCH 046/133] split storage from value also, quit inheritance in storage class --- toml/storage.hpp | 42 ++++++++++++++++++++++++++++++++++++++++++ toml/value.hpp | 34 ++-------------------------------- 2 files changed, 44 insertions(+), 32 deletions(-) create mode 100644 toml/storage.hpp diff --git a/toml/storage.hpp b/toml/storage.hpp new file mode 100644 index 0000000..4e3f114 --- /dev/null +++ b/toml/storage.hpp @@ -0,0 +1,42 @@ +#ifndef TOML11_STORAGE_HPP +#define TOML11_STORAGE_HPP +#include "utility.hpp" + +namespace toml +{ +namespace detail +{ + +// this contains pointer and deep-copy the content if copied. +// to avoid recursive pointer. +template +struct storage +{ + using value_type = T; + + storage(value_type const& v): ptr(toml::make_unique(v)) {} + storage(value_type&& v): ptr(toml::make_unique(std::move(v))) {} + ~storage() = default; + + storage(const storage& rhs): ptr(toml::make_unique(*rhs.ptr)) {} + storage& operator=(const storage& rhs) + { + this->ptr = toml::make_unique(*rhs.ptr); + return *this; + } + storage(storage&&) = default; + storage& operator=(storage&&) = default; + + bool is_ok() const noexcept {return static_cast(ptr);} + + value_type& value() & noexcept {return *ptr;} + value_type const& value() const& noexcept {return *ptr;} + value_type&& value() && noexcept {return std::move(*ptr);} + + private: + std::unique_ptr ptr; +}; + +} // detail +} // toml +#endif// TOML11_STORAGE_HPP diff --git a/toml/value.hpp b/toml/value.hpp index 67d0c6b..1fe42f8 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -3,6 +3,8 @@ #include "traits.hpp" #include "utility.hpp" #include "exception.hpp" +#include "storage.hpp" +#include "region.hpp" #include "types.hpp" #include #include @@ -13,38 +15,6 @@ namespace toml { -namespace detail -{ -struct storage_base -{ - storage_base(): type(toml::value_t::Empty){} - storage_base(toml::value_t t): type(t){} - virtual ~storage_base() = default; - toml::value_t type; -}; - -template -struct storage : public storage_base -{ - static_assert(std::is_same::value || - std::is_same::value, - "toml::detail::storage is for toml::Array or toml::Table!"); - typedef T value_type; - - storage() = default; - ~storage() noexcept override = default; - storage(storage const&) = default; - storage(storage&&) = default; - storage& operator=(storage const&) = default; - storage& operator=(storage&&) = default; - - storage(value_type const& v) : value(v){} - storage(value_type&& v) : value(std::move(v)){} - - value_type value; -}; -} // detail - template struct value_traits { From dc8ccdc45880f5b1944991c2eff355a034dd2164 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 18:00:46 +0900 Subject: [PATCH 047/133] construct much more tmp variables std::chrono::seconds -= std::chrono::milliseconds cannot be done bc it represents the duration as integer value and milliseconds are less than seconds. it causes compilation error when we pass a duration to toml::local_time. to avoid this, we need to type-cast the values to smaller duration, like sec -> msec --- toml/datetime.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 82d283b..eccd1df 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -145,21 +145,21 @@ struct local_time {} template - explicit local_time(std::chrono::duration t) + explicit local_time(const std::chrono::duration& t) { const auto h = std::chrono::duration_cast(t); this->hour = h.count(); - t -= h; - const auto m = std::chrono::duration_cast(t); + const auto t2 = t - h; + const auto m = std::chrono::duration_cast(t2); this->minute = m.count(); - t -= m; - const auto s = std::chrono::duration_cast(t); + const auto t3 = t2 - m; + const auto s = std::chrono::duration_cast(t3); this->second = s.count(); - t -= s; - const auto ms = std::chrono::duration_cast(t); + const auto t4 = t3 - s; + const auto ms = std::chrono::duration_cast(t4); this->millisecond = ms.count(); - t -= ms; - const auto us = std::chrono::duration_cast(t); + const auto t5 = t4 - ms; + const auto us = std::chrono::duration_cast(t5); this->microsecond = us.count(); } From e24039f4ef4b2bf55b2939b410338a8aa9bfbac3 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 18:03:20 +0900 Subject: [PATCH 048/133] update toml::value and improve test_value - enable to store new types - store source string if possible - refactoring --- tests/CMakeLists.txt | 2 +- tests/test_value.cpp | 758 ++++++++++++++++++++++--------- toml/value.hpp | 1016 +++++++++++++++++++++++++----------------- 3 files changed, 1160 insertions(+), 616 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index da45062..e514d4e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,7 +9,7 @@ set(TEST_NAMES test_utility test_result test_traits -# test_value + test_value # test_to_toml # test_from_toml # test_get diff --git a/tests/test_value.cpp b/tests/test_value.cpp index 1e24088..29f8cd6 100644 --- a/tests/test_value.cpp +++ b/tests/test_value.cpp @@ -5,231 +5,589 @@ #define BOOST_TEST_NO_LIB #include #endif -#include +#include #include #include -BOOST_AUTO_TEST_CASE(test_value_exact_constructor) +BOOST_AUTO_TEST_CASE(test_value_boolean) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); + toml::value v1(true); + toml::value v2(false); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), false); + + v1 = false; + v2 = true; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), false); + BOOST_CHECK_EQUAL(v2.cast(), true); + + toml::value v3(v1); + toml::value v4(v2); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Boolean); + BOOST_CHECK(v3.is(toml::value_t::Boolean)); + BOOST_CHECK(v4.is(toml::value_t::Boolean)); + BOOST_CHECK(v3.is()); + BOOST_CHECK(v4.is()); + + BOOST_CHECK_EQUAL(v3.cast(), false); + BOOST_CHECK_EQUAL(v4.cast(), true); + + toml::value v5(std::move(v1)); + toml::value v6(std::move(v2)); + + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Boolean); + BOOST_CHECK(v5.is(toml::value_t::Boolean)); + BOOST_CHECK(v6.is(toml::value_t::Boolean)); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_EQUAL(v5.cast(), false); + BOOST_CHECK_EQUAL(v6.cast(), true); + + v1 = 42; + v2 = 3.14; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); + BOOST_CHECK(v1.is(toml::value_t::Integer)); + BOOST_CHECK(v2.is(toml::value_t::Float)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), 42); + BOOST_CHECK_EQUAL(v2.cast(), 3.14); +} + +BOOST_AUTO_TEST_CASE(test_value_integer) +{ + toml::value v1(-42); + toml::value v2(42u); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v7.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Integer)); + BOOST_CHECK(v2.is(toml::value_t::Integer)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); - BOOST_CHECK_EQUAL(v1.cast(), b); - BOOST_CHECK_EQUAL(v2.cast(), i); - BOOST_CHECK_EQUAL(v3.cast(), f); - BOOST_CHECK_EQUAL(v4.cast(), s); - const auto& ar = v6.cast(); - BOOST_CHECK_EQUAL(ar.at(0).cast(), a.at(0).cast()); - BOOST_CHECK_EQUAL(ar.at(1).cast(), a.at(1).cast()); - BOOST_CHECK_EQUAL(ar.at(2).cast(), a.at(2).cast()); - BOOST_CHECK_EQUAL(ar.at(3).cast(), a.at(3).cast()); - BOOST_CHECK_EQUAL(ar.at(4).cast(), a.at(4).cast()); -} + BOOST_CHECK_EQUAL(v1.cast(), -42); + BOOST_CHECK_EQUAL(v2.cast(), 42u); -BOOST_AUTO_TEST_CASE(test_value_convertible_constructor) -{ - int i(42); - float f(3.14); - const char* s = "hoge"; - - toml::value v1(i); - toml::value v2(f); - toml::value v3(s); + v1 = 54; + v2 = -54; BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); + BOOST_CHECK(v1.is(toml::value_t::Integer)); + BOOST_CHECK(v2.is(toml::value_t::Integer)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); - BOOST_CHECK_EQUAL(v1.cast(), i); - BOOST_CHECK_EQUAL(v2.cast(), f); - BOOST_CHECK_EQUAL(v3.cast(), s); + BOOST_CHECK_EQUAL(v1.cast(), 54); + BOOST_CHECK_EQUAL(v2.cast(), -54); + + toml::value v3(v1); + toml::value v4(v2); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Integer); + BOOST_CHECK(v3.is(toml::value_t::Integer)); + BOOST_CHECK(v4.is(toml::value_t::Integer)); + BOOST_CHECK(v3.is()); + BOOST_CHECK(v4.is()); + + BOOST_CHECK_EQUAL(v3.cast(), 54); + BOOST_CHECK_EQUAL(v4.cast(), -54); + + toml::value v5(std::move(v1)); + toml::value v6(std::move(v2)); + + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Integer); + BOOST_CHECK(v5.is(toml::value_t::Integer)); + BOOST_CHECK(v6.is(toml::value_t::Integer)); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_EQUAL(v5.cast(), 54); + BOOST_CHECK_EQUAL(v6.cast(), -54); + + v1 = true; + v2 = false; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), false); } -BOOST_AUTO_TEST_CASE(test_value_copy_move_constructor) +BOOST_AUTO_TEST_CASE(test_value_float) { - toml::Array a; - toml::Table t; - toml::value v1(true); - toml::value v2(42); - toml::value v3(3.14); - toml::value v4("hoge"); - toml::value v5(std::chrono::system_clock::now()); - toml::value v6(a); - toml::value v7(t); - - toml::value u1(v1); - toml::value u2(v2); - toml::value u3(v3); - toml::value u4(v4); - toml::value u5(v5); - toml::value u6(v6); - toml::value u7(v7); - - BOOST_CHECK_EQUAL(u1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(u2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(u3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(u4.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(u5.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(u6.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(u7.type(), toml::value_t::Table); - - BOOST_CHECK_EQUAL(u1.cast(), true); - BOOST_CHECK_EQUAL(u2.cast(), 42); - BOOST_CHECK_EQUAL(u3.cast(), 3.14); - BOOST_CHECK_EQUAL(u4.cast(), "hoge"); - - toml::value w1(std::move(v1)); - toml::value w2(std::move(v2)); - toml::value w3(std::move(v3)); - toml::value w4(std::move(v4)); - toml::value w5(std::move(v5)); - toml::value w6(std::move(v6)); - toml::value w7(std::move(v7)); - - BOOST_CHECK_EQUAL(w1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(w2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(w3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(w4.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(w5.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(w6.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(w7.type(), toml::value_t::Table); - - BOOST_CHECK_EQUAL(w1.cast(), true); - BOOST_CHECK_EQUAL(w2.cast(), 42); - BOOST_CHECK_EQUAL(w3.cast(), 3.14); - BOOST_CHECK_EQUAL(w4.cast(), "hoge"); -} - -BOOST_AUTO_TEST_CASE(test_value_copy_move_substitution) -{ - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); - - v1 = i; - v2 = f; - v3 = s; - v4 = d; - v5 = a; - v6 = t; - v7 = b; - - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Table); - BOOST_CHECK_EQUAL(v7.type(), toml::value_t::Boolean); - - BOOST_CHECK_EQUAL(v7.cast(), b); - BOOST_CHECK_EQUAL(v1.cast(), i); - BOOST_CHECK_EQUAL(v2.cast(), f); - BOOST_CHECK_EQUAL(v3.cast(), s); - - const auto f_ = f; - const auto s_ = s; - const auto d_ = d; - const auto a_ = a; - const auto t_ = t; - const auto b_ = b; - const auto i_ = i; - - v1 = std::move(f); - v2 = std::move(s); - v3 = std::move(d); - v4 = std::move(a); - v5 = std::move(t); - v6 = std::move(b); - v7 = std::move(i); + toml::value v1(3.14); + toml::value v2(3.14f); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Table); - BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(v7.type(), toml::value_t::Integer); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); + BOOST_CHECK(v1.is(toml::value_t::Float)); + BOOST_CHECK(v2.is(toml::value_t::Float)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); - BOOST_CHECK_EQUAL(v6.cast(), b_); - BOOST_CHECK_EQUAL(v7.cast(), i_); - BOOST_CHECK_EQUAL(v1.cast(), f_); - BOOST_CHECK_EQUAL(v2.cast(), s_); + BOOST_CHECK_EQUAL(v1.cast(), 3.14); + BOOST_CHECK_CLOSE_FRACTION(v2.cast(), 3.14, 1e-2); + + v1 = 2.718f; + v2 = 2.718; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); + BOOST_CHECK(v1.is(toml::value_t::Float)); + BOOST_CHECK(v2.is(toml::value_t::Float)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_CLOSE_FRACTION(v1.cast(), 2.718, 1e-3); + BOOST_CHECK_EQUAL(v2.cast(), 2.718); + + toml::value v3(v1); + toml::value v4(v2); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Float); + BOOST_CHECK(v3.is(toml::value_t::Float)); + BOOST_CHECK(v4.is(toml::value_t::Float)); + BOOST_CHECK(v3.is()); + BOOST_CHECK(v4.is()); + + BOOST_CHECK_CLOSE_FRACTION(v3.cast(), 2.718, 1e-3); + BOOST_CHECK_EQUAL(v4.cast(), 2.718); + + toml::value v5(std::move(v1)); + toml::value v6(std::move(v2)); + + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Float); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Float); + BOOST_CHECK(v5.is(toml::value_t::Float)); + BOOST_CHECK(v6.is(toml::value_t::Float)); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_CLOSE_FRACTION(v5.cast(), 2.718, 1e-3); + BOOST_CHECK_EQUAL(v6.cast(), 2.718); + + v1 = true; + v2 = false; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), false); } -BOOST_AUTO_TEST_CASE(test_value_initializer_list) +BOOST_AUTO_TEST_CASE(test_value_string) { - toml::value v1{3,1,4,1,5}; - toml::value v2{{"hoge", 1}, {"piyo", 3.14}, {"fuga", "string"}}; + toml::value v1(std::string("foo")); + toml::value v2(std::string("foo"), toml::string_t::literal); + toml::value v3("foo"); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::String)); + BOOST_CHECK(v2.is(toml::value_t::String)); + BOOST_CHECK(v3.is(toml::value_t::String)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v1.cast(), "foo"); + BOOST_CHECK_EQUAL(v2.cast(), "foo"); + BOOST_CHECK_EQUAL(v3.cast(), "foo"); + + v1 = "bar"; + v2 = "bar"; + v3 = "bar"; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::String)); + BOOST_CHECK(v2.is(toml::value_t::String)); + BOOST_CHECK(v3.is(toml::value_t::String)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v1.cast(), "bar"); + BOOST_CHECK_EQUAL(v2.cast(), "bar"); + BOOST_CHECK_EQUAL(v3.cast(), "bar"); + + toml::value v4(v1); + toml::value v5(v2); + toml::value v6(v3); + + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::String); + BOOST_CHECK(v4.is(toml::value_t::String)); + BOOST_CHECK(v5.is(toml::value_t::String)); + BOOST_CHECK(v6.is(toml::value_t::String)); + BOOST_CHECK(v4.is()); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_EQUAL(v4.cast(), "bar"); + BOOST_CHECK_EQUAL(v5.cast(), "bar"); + BOOST_CHECK_EQUAL(v6.cast(), "bar"); + + v4.cast().str.at(2) = 'z'; + v5.cast().str.at(2) = 'z'; + v6.cast().str.at(2) = 'z'; + + BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v5.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v6.type(), toml::value_t::String); + BOOST_CHECK(v4.is(toml::value_t::String)); + BOOST_CHECK(v5.is(toml::value_t::String)); + BOOST_CHECK(v6.is(toml::value_t::String)); + BOOST_CHECK(v4.is()); + BOOST_CHECK(v5.is()); + BOOST_CHECK(v6.is()); + + BOOST_CHECK_EQUAL(v4.cast(), "baz"); + BOOST_CHECK_EQUAL(v5.cast(), "baz"); + BOOST_CHECK_EQUAL(v6.cast(), "baz"); + + v1 = true; + v2 = true; + v3 = true; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v3.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), true); + BOOST_CHECK_EQUAL(v3.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_local_date) +{ + toml::value v1(toml::local_date(2018, toml::month_t::Jan, 31)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); + BOOST_CHECK(v1.is(toml::value_t::LocalDate)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_date(2018, toml::month_t::Jan, 31)); + + v1 = toml::local_date(2018, toml::month_t::Apr, 1); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); + BOOST_CHECK(v1.is(toml::value_t::LocalDate)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_date(2018, toml::month_t::Apr, 1)); + + toml::value v2(v1); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDate); + BOOST_CHECK(v2.is(toml::value_t::LocalDate)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v2.cast(), + toml::local_date(2018, toml::month_t::Apr, 1)); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_local_time) +{ + toml::value v1(toml::local_time(12, 30, 45)); + toml::value v2(std::chrono::hours(12) + std::chrono::minutes(30) + + std::chrono::seconds(45)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalTime); + BOOST_CHECK(v1.is(toml::value_t::LocalTime)); + BOOST_CHECK(v2.is(toml::value_t::LocalTime)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_time(12, 30, 45)); + BOOST_CHECK_EQUAL(v2.cast(), + toml::local_time(12, 30, 45)); + BOOST_CHECK_EQUAL(v1.cast(), + v2.cast()); + + v1 = toml::local_time(1, 30, 0, /*ms*/ 100, /*us*/ 0); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); + BOOST_CHECK(v1.is(toml::value_t::LocalTime)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_time(1, 30, 0, 100, 0)); + + toml::value v3(v1); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::LocalTime); + BOOST_CHECK(v3.is(toml::value_t::LocalTime)); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v3.cast(), + toml::local_time(1, 30, 0, 100, 0)); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_local_datetime) +{ + toml::value v1(toml::local_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45) + )); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); + BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45))); + + v1 = toml::local_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); + BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30))); + + toml::value v2(v1); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDatetime); + BOOST_CHECK(v2.is(toml::value_t::LocalDatetime)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v2.cast(), + toml::local_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30))); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_offset_datetime) +{ + toml::value v1(toml::offset_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45), + toml::time_offset(9, 0) + )); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); + BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::offset_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45), + toml::time_offset(9, 0) + )); + + v1 = toml::offset_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30), + toml::time_offset(9, 0)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); + BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::offset_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30), + toml::time_offset(9, 0))); + + toml::value v2(v1); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::OffsetDatetime); + BOOST_CHECK(v2.is(toml::value_t::OffsetDatetime)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v2.cast(), + toml::offset_datetime( + toml::local_date(2018, toml::month_t::Apr, 1), + toml::local_time(1, 15, 30), + toml::time_offset(9, 0))); + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_array) +{ + std::vector v{1,2,3,4,5}; + toml::value v1(v); + toml::value v2{6,7,8,9,0}; BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Array)); + BOOST_CHECK(v1.is()); - const auto& ar = v1.cast(); - BOOST_CHECK_EQUAL(ar.at(0).cast(), 3); - BOOST_CHECK_EQUAL(ar.at(1).cast(), 1); - BOOST_CHECK_EQUAL(ar.at(2).cast(), 4); - BOOST_CHECK_EQUAL(ar.at(3).cast(), 1); - BOOST_CHECK_EQUAL(ar.at(4).cast(), 5); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); + BOOST_CHECK(v2.is(toml::value_t::Array)); + BOOST_CHECK(v2.is()); - const auto& tb = v2.cast(); + BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 1); + BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 2); + BOOST_CHECK_EQUAL(v1.cast().at(2).cast(), 3); + BOOST_CHECK_EQUAL(v1.cast().at(3).cast(), 4); + BOOST_CHECK_EQUAL(v1.cast().at(4).cast(), 5); - BOOST_CHECK_EQUAL(tb.at("hoge").type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(tb.at("piyo").type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(tb.at("fuga").type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v2.cast().at(0).cast(), 6); + BOOST_CHECK_EQUAL(v2.cast().at(1).cast(), 7); + BOOST_CHECK_EQUAL(v2.cast().at(2).cast(), 8); + BOOST_CHECK_EQUAL(v2.cast().at(3).cast(), 9); + BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 0); - BOOST_CHECK_EQUAL(tb.at("hoge").cast(), 1); - BOOST_CHECK_CLOSE_FRACTION(tb.at("piyo").cast(), 3.14, 1e-3); - BOOST_CHECK_EQUAL(tb.at("fuga").cast(), "string"); + v1 = {6,7,8,9,0}; + v2 = v; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); + BOOST_CHECK(v1.is(toml::value_t::Array)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); + BOOST_CHECK(v2.is(toml::value_t::Array)); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 6); + BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 7); + BOOST_CHECK_EQUAL(v1.cast().at(2).cast(), 8); + BOOST_CHECK_EQUAL(v1.cast().at(3).cast(), 9); + BOOST_CHECK_EQUAL(v1.cast().at(4).cast(), 0); + + BOOST_CHECK_EQUAL(v2.cast().at(0).cast(), 1); + BOOST_CHECK_EQUAL(v2.cast().at(1).cast(), 2); + BOOST_CHECK_EQUAL(v2.cast().at(2).cast(), 3); + BOOST_CHECK_EQUAL(v2.cast().at(3).cast(), 4); + BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 5); + + toml::value v3(v1); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Array); + BOOST_CHECK(v3.is(toml::value_t::Array)); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v3.cast().at(0).cast(), 6); + BOOST_CHECK_EQUAL(v3.cast().at(1).cast(), 7); + BOOST_CHECK_EQUAL(v3.cast().at(2).cast(), 8); + BOOST_CHECK_EQUAL(v3.cast().at(3).cast(), 9); + BOOST_CHECK_EQUAL(v3.cast().at(4).cast(), 0); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); +} + +BOOST_AUTO_TEST_CASE(test_value_table) +{ + toml::value v1{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Table)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 42); + BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 3.14); + BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "qux"); + + v1 = toml::table{{"foo", 2.71}, {"bar", 54}, {"baz", "quux"}}; + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Table)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 2.71); + BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 54); + BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "quux"); + + toml::value v3(v1); + + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Table); + BOOST_CHECK(v3.is(toml::value_t::Table)); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v3.cast().at("foo").cast(), 2.71); + BOOST_CHECK_EQUAL(v3.cast().at("bar").cast(), 54); + BOOST_CHECK_EQUAL(v3.cast().at("baz").cast().str, "quux"); + + v1 = true; + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast(), true); } diff --git a/toml/value.hpp b/toml/value.hpp index 1fe42f8..a2e668b 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -29,482 +29,658 @@ constexpr bool value_traits::is_toml_type; class value { - typedef std::unique_ptr storage_ptr; + template + static void assigner(T& dst, U&& v) + { + const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); + assert(tmp == std::addressof(dst)); + } + + using region_base = detail::region_base; public: - value() : type_(value_t::Empty){} - ~value(); + value() noexcept + : type_(value_t::Empty), + region_info_(std::make_shared(region_base{})) + {} - value(const value& v); - value(value&& v); - value& operator=(const value& v); - value& operator=(value&& v); + ~value() noexcept {this->cleanup();} + + value(const value& v): type_(v.type()), region_info_(v.region_info_) + { + switch(v.type()) + { + case value_t::Boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::Integer : assigner(integer_ , v.integer_ ); break; + case value_t::Float : assigner(floating_ , v.floating_ ); break; + case value_t::String : assigner(string_ , v.string_ ); break; + case value_t::OffsetDatetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::LocalDatetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::LocalDate : assigner(local_date_ , v.local_date_ ); break; + case value_t::LocalTime : assigner(local_time_ , v.local_time_ ); break; + case value_t::Array : assigner(array_ , v.array_ ); break; + case value_t::Table : assigner(table_ , v.table_ ); break; + default: break; + } + } + value(value&& v): type_(v.type()), region_info_(std::move(v.region_info_)) + { + switch(this->type_) + { + case value_t::Boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::Integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::Float : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::String : assigner(string_ , std::move(v.string_ )); break; + case value_t::OffsetDatetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::LocalDatetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::LocalDate : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::LocalTime : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::Array : assigner(array_ , std::move(v.array_ )); break; + case value_t::Table : assigner(table_ , std::move(v.table_ )); break; + default: break; + } + } + value& operator=(const value& v) + { + this->cleanup(); + this->region_info_ = v.region_info_; + this->type_ = v.type(); + switch(this->type_) + { + case value_t::Boolean : assigner(boolean_ , v.boolean_ ); break; + case value_t::Integer : assigner(integer_ , v.integer_ ); break; + case value_t::Float : assigner(floating_ , v.floating_ ); break; + case value_t::String : assigner(string_ , v.string_ ); break; + case value_t::OffsetDatetime: assigner(offset_datetime_, v.offset_datetime_); break; + case value_t::LocalDatetime : assigner(local_datetime_ , v.local_datetime_ ); break; + case value_t::LocalDate : assigner(local_date_ , v.local_date_ ); break; + case value_t::LocalTime : assigner(local_time_ , v.local_time_ ); break; + case value_t::Array : assigner(array_ , v.array_ ); break; + case value_t::Table : assigner(table_ , v.table_ ); break; + default: break; + } + return *this; + } + value& operator=(value&& v) + { + this->cleanup(); + this->region_info_ = std::move(v.region_info_); + this->type_ = v.type(); + switch(this->type_) + { + case value_t::Boolean : assigner(boolean_ , std::move(v.boolean_ )); break; + case value_t::Integer : assigner(integer_ , std::move(v.integer_ )); break; + case value_t::Float : assigner(floating_ , std::move(v.floating_ )); break; + case value_t::String : assigner(string_ , std::move(v.string_ )); break; + case value_t::OffsetDatetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; + case value_t::LocalDatetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; + case value_t::LocalDate : assigner(local_date_ , std::move(v.local_date_ )); break; + case value_t::LocalTime : assigner(local_time_ , std::move(v.local_time_ )); break; + case value_t::Array : assigner(array_ , std::move(v.array_ )); break; + case value_t::Table : assigner(table_ , std::move(v.table_ )); break; + default: break; + } + return *this; + } + + // boolean ============================================================== + + value(boolean b) + : type_(value_t::Boolean), + region_info_(std::make_shared(region_base{})) + { + assigner(this->boolean_, b); + } + + value& operator=(boolean b) + { + this->cleanup(); + this->type_ = value_t::Boolean; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->boolean_, b); + return *this; + } + + template + value(boolean b, detail::region reg) + : type_(value_t::Boolean), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->boolean_, b); + } + + // integer ============================================================== + + template, detail::negation>>::value, + std::nullptr_t>::type = nullptr> + value(T i) + : type_(value_t::Integer), + region_info_(std::make_shared(region_base{})) + { + assigner(this->integer_, static_cast(i)); + } + + template, + detail::negation> + >::value, std::nullptr_t>::type = nullptr> + value(T i, detail::region reg) + : type_(value_t::Integer), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->integer_, static_cast(i)); + } + + template, detail::negation>>::value, + std::nullptr_t>::type = nullptr> + value& operator=(T i) + { + this->cleanup(); + this->type_ = value_t::Integer; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->integer_, static_cast(i)); + return *this; + } + + // floating ============================================================= template::is_toml_type, std::nullptr_t>::type = nullptr> - value(T&& v); + std::is_floating_point::value, std::nullptr_t>::type = nullptr> + value(T f) + : type_(value_t::Float), + region_info_(std::make_shared(region_base{})) + { + assigner(this->floating_, f); + } + + template::value, std::nullptr_t>::type = nullptr> + value(T f, detail::region reg) + : type_(value_t::Float), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->floating_, f); + } template::is_toml_type, std::nullptr_t>::type = nullptr> - value& operator=(T&& v); + std::is_floating_point::value, std::nullptr_t>::type = nullptr> + value& operator=(T f) + { + this->cleanup(); + this->type_ = value_t::Float; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->floating_, f); + return *this; + } - template::is_toml_type, std::nullptr_t>::type = nullptr> - value(std::initializer_list init); + // string =============================================================== - value(std::initializer_list> init); + value(toml::string s) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, std::move(s)); + } + template + value(toml::string s, detail::region reg) + : type_(value_t::String), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->string_, std::move(s)); + } + value& operator=(toml::string s) + { + this->cleanup(); + this->type_ = value_t::String; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, s); + return *this; + } + + value(std::string s) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::move(s))); + } + value& operator=(std::string s) + { + this->cleanup(); + this->type_ = value_t::String; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, toml::string(std::move(s))); + return *this; + } + value(std::string s, string_t kind) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::move(s), kind)); + } + + value(const char* s) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::string(s))); + } + value& operator=(const char* s) + { + this->cleanup(); + this->type_ = value_t::String; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->string_, toml::string(std::string(s))); + return *this; + } + value(const char* s, string_t kind) + : type_(value_t::String), + region_info_(std::make_shared(region_base{})) + { + assigner(this->string_, toml::string(std::string(s), kind)); + } + + // local date =========================================================== + + value(const local_date& ld) + : type_(value_t::LocalDate), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_date_, ld); + } + template + value(const local_date& ld, detail::region reg) + : type_(value_t::LocalDate), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->local_date_, ld); + } + value& operator=(const local_date& ld) + { + this->cleanup(); + this->type_ = value_t::LocalDate; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_date_, ld); + return *this; + } + + // local time =========================================================== + + value(const local_time& lt) + : type_(value_t::LocalTime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_time_, lt); + } + template + value(const local_time& lt, detail::region reg) + : type_(value_t::LocalTime), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->local_time_, lt); + } + value& operator=(const local_time& lt) + { + this->cleanup(); + this->type_ = value_t::LocalTime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_time_, lt); + return *this; + } + template + value(const std::chrono::duration& dur) + : type_(value_t::LocalTime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_time_, local_time(dur)); + } + template + value& operator=(const std::chrono::duration& dur) + { + this->cleanup(); + this->type_ = value_t::LocalTime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_time_, local_time(dur)); + return *this; + } + + // local datetime ======================================================= + + value(const local_datetime& ldt) + : type_(value_t::LocalDatetime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->local_datetime_, ldt); + } + template + value(const local_datetime& ldt, detail::region reg) + : type_(value_t::LocalDatetime), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->local_datetime_, ldt); + } + value& operator=(const local_datetime& ldt) + { + this->cleanup(); + this->type_ = value_t::LocalDatetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->local_datetime_, ldt); + return *this; + } + + // offset datetime ====================================================== + + value(const offset_datetime& odt) + : type_(value_t::OffsetDatetime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->offset_datetime_, odt); + } + template + value(const offset_datetime& odt, detail::region reg) + : type_(value_t::OffsetDatetime), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->offset_datetime_, odt); + } + value& operator=(const offset_datetime& odt) + { + this->cleanup(); + this->type_ = value_t::OffsetDatetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->offset_datetime_, odt); + return *this; + } + value(const std::chrono::system_clock::time_point& tp) + : type_(value_t::OffsetDatetime), + region_info_(std::make_shared(region_base{})) + { + assigner(this->offset_datetime_, offset_datetime(tp)); + } + value& operator=(const std::chrono::system_clock::time_point& tp) + { + this->cleanup(); + this->type_ = value_t::OffsetDatetime; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->offset_datetime_, offset_datetime(tp)); + return *this; + } + + // array ================================================================ + + value(const array& ary) + : type_(value_t::Array), + region_info_(std::make_shared(region_base{})) + { + assigner(this->array_, ary); + } + template + value(const array& ary, detail::region reg) + : type_(value_t::Array), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->array_, ary); + } + value& operator=(const array& ary) + { + this->cleanup(); + this->type_ = value_t::Array; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->array_, ary); + return *this; + } + + template::is_toml_type, + std::nullptr_t>::type = nullptr> + value(std::initializer_list list) + : type_(value_t::Array), + region_info_(std::make_shared(region_base{})) + { + array ary; ary.reserve(list.size()); + for(auto& elem : list) {ary.emplace_back(std::move(elem));} + assigner(this->array_, std::move(ary)); + } + template::is_toml_type, + std::nullptr_t>::type = nullptr> + value& operator=(std::initializer_list list) + { + this->cleanup(); + this->type_ = value_t::Array; + this->region_info_ = std::make_shared(region_base{}); + + array ary; ary.reserve(list.size()); + for(auto& elem : list) {ary.emplace_back(std::move(elem));} + assigner(this->array_, std::move(ary)); + return *this; + } + + template::value, + std::nullptr_t>::type = nullptr> + value(T&& list) + : type_(value_t::Array), + region_info_(std::make_shared(region_base{})) + { + array ary; ary.reserve(list.size()); + for(auto& elem : list) {ary.emplace_back(std::move(elem));} + assigner(this->array_, std::move(ary)); + } + template::value, + std::nullptr_t>::type = nullptr> + value& operator=(T&& list) + { + this->cleanup(); + this->type_ = value_t::Array; + this->region_info_ = std::make_shared(region_base{}); + + array ary; ary.reserve(list.size()); + for(auto& elem : list) {ary.emplace_back(std::move(elem));} + assigner(this->array_, std::move(ary)); + return *this; + } + + // table ================================================================ + + value(const table& tab) + : type_(value_t::Table), + region_info_(std::make_shared(region_base{})) + { + assigner(this->table_, tab); + } + template + value(const table& tab, detail::region reg) + : type_(value_t::Table), + region_info_(std::make_shared(std::move(reg))) + { + assigner(this->table_, tab); + } + value& operator=(const table& tab) + { + this->cleanup(); + this->type_ = value_t::Table; + this->region_info_ = std::make_shared(region_base{}); + assigner(this->table_, tab); + return *this; + } + value(std::initializer_list> list) + : type_(value_t::Table), + region_info_(std::make_shared(region_base{})) + { + table tab; + for(const auto& elem : list) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + } + value& operator=(std::initializer_list> list) + { + this->cleanup(); + this->type_ = value_t::Array; + this->region_info_ = std::make_shared(region_base{}); + + table tab; + for(const auto& elem : list) {tab[elem.first] = elem.second;} + assigner(this->table_, std::move(tab)); + return *this; + } + + template + bool is() const noexcept {return value_traits::type_index == this->type_;} + bool is(value_t t) const noexcept {return t == this->type_;} value_t type() const {return type_;} template - typename detail::toml_default_type::type const& cast() const; + typename detail::toml_default_type::type& cast() &; template - typename detail::toml_default_type::type& cast(); + typename detail::toml_default_type::type const& cast() const&; + template + typename detail::toml_default_type::type&& cast() &&; private: - void switch_clean(value_t t); - template struct switch_assign; - template struct switch_cast; - - static bool should_be_cleaned(value_t vt) + void cleanup() noexcept { - return (vt == value_t::String) || (vt == value_t::Array) || - (vt == value_t::Table) || (vt == value_t::Datetime); + switch(this->type_) + { + case value_t::String : {string_.~string(); return;} + case value_t::Array : {array_.~array_storage(); return;} + case value_t::Table : {table_.~table_storage(); return;} + default : return; + } } + std::string + format_error(const std::string& msg, const std::string& com) const + { + return detail::format_underline(msg, *(this->region_info_), com); + } + + template + struct switch_cast; + private: + using array_storage = detail::storage; + using table_storage = detail::storage; + value_t type_; + + // for error message information. + std::shared_ptr region_info_; + union { - Boolean boolean_; - Integer integer_; - Float float_; - String string_; - Datetime datetime_; - storage_ptr storage_; //ptr to table or array + boolean boolean_; + integer integer_; + floating floating_; + string string_; + offset_datetime offset_datetime_; + local_datetime local_datetime_; + local_date local_date_; + local_time local_time_; + array_storage array_; + table_storage table_; }; }; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) - { - v.boolean_ = static_cast(val); - } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) - { - v.integer_ = static_cast(val); - } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) - { - v.float_ = static_cast(val); - } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) - { - new(&v.string_) String(val); - } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) - { - new(&v.datetime_) Datetime(val); - } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) - { - new(&v.storage_) storage_ptr( - toml::make_unique>(val)); - } -}; -template<> struct value::switch_assign -{ - template - static void invoke(value& v, valT&& val) - { - new(&v.storage_) storage_ptr( - toml::make_unique>(val)); - } -}; - template<> struct value::switch_cast { - static Boolean& invoke(value& v) {return v.boolean_;} + static Boolean& invoke(value& v) {return v.boolean_;} static Boolean const& invoke(value const& v) {return v.boolean_;} + static Boolean&& invoke(value&& v) {return std::move(v.boolean_);} }; template<> struct value::switch_cast { - static Integer& invoke(value& v) {return v.integer_;} + static Integer& invoke(value& v) {return v.integer_;} static Integer const& invoke(value const& v) {return v.integer_;} + static Integer&& invoke(value&& v) {return std::move(v.integer_);} }; template<> struct value::switch_cast { - static Float& invoke(value& v) {return v.float_;} - static Float const& invoke(value const& v) {return v.float_;} + static Float& invoke(value& v) {return v.floating_;} + static Float const& invoke(value const& v) {return v.floating_;} + static Float&& invoke(value&& v) {return std::move(v.floating_);} }; template<> struct value::switch_cast { - static String& invoke(value& v) {return v.string_;} + static String& invoke(value& v) {return v.string_;} static String const& invoke(value const& v) {return v.string_;} + static String&& invoke(value&& v) {return std::move(v.string_);} }; -template<> struct value::switch_cast +template<> struct value::switch_cast { - static Datetime& invoke(value& v) {return v.datetime_;} - static Datetime const& invoke(value const& v) {return v.datetime_;} + static OffsetDatetime& invoke(value& v) {return v.offset_datetime_;} + static OffsetDatetime const& invoke(value const& v) {return v.offset_datetime_;} + static OffsetDatetime&& invoke(value&& v) {return std::move(v.offset_datetime_);} +}; +template<> struct value::switch_cast +{ + static LocalDatetime& invoke(value& v) {return v.local_datetime_;} + static LocalDatetime const& invoke(value const& v) {return v.local_datetime_;} + static LocalDatetime&& invoke(value&& v) {return std::move(v.local_datetime_);} +}; +template<> struct value::switch_cast +{ + static LocalDate& invoke(value& v) {return v.local_date_;} + static LocalDate const& invoke(value const& v) {return v.local_date_;} + static LocalDate&& invoke(value&& v) {return std::move(v.local_date_);} +}; +template<> struct value::switch_cast +{ + static LocalTime& invoke(value& v) {return v.local_time_;} + static LocalTime const& invoke(value const& v) {return v.local_time_;} + static LocalTime&& invoke(value&& v) {return std::move(v.local_time_);} }; template<> struct value::switch_cast { - // switch_cast assumes tmeplate argument is correct. - // if not, the behaviour is undefined. - static Array& invoke(value& v) - { - return static_cast*>(v.storage_.get())->value; - } - static Array const& invoke(value const& v) - { - return static_cast*>(v.storage_.get())->value; - } + static Array& invoke(value& v) {return v.array_.value();} + static Array const& invoke(value const& v) {return v.array_.value();} + static Array&& invoke(value&& v) {return std::move(v.array_.value());} }; template<> struct value::switch_cast { - static Table& invoke(value& v) - { - return static_cast*>(v.storage_.get())->value; - } - static Table const& invoke(value const& v) - { - return static_cast*>(v.storage_.get())->value; - } + static Table& invoke(value& v) {return v.table_.value();} + static Table const& invoke(value const& v) {return v.table_.value();} + static Table&& invoke(value&& v) {return std::move(v.table_.value());} }; -inline void value::switch_clean(value_t t) -{ - switch(t) - { - case value_t::Boolean : {boolean_.~Boolean(); return;} - case value_t::Integer : {integer_.~Integer(); return;} - case value_t::Float : {float_.~Float(); return;} - case value_t::String : {string_.~String(); return;} - case value_t::Datetime : {datetime_.~Datetime(); return;} - case value_t::Array : {storage_.~storage_ptr(); return;} - case value_t::Table : {storage_.~storage_ptr(); return;} - case value_t::Empty : return; - case value_t::Unknown : assert(false); - default : assert(false); - } -} - -inline value::~value() -{ - switch_clean(this->type_); -} - -inline value::value(const value& v) : type_(v.type()) -{ - switch(v.type()) - { - case value_t::Boolean : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Integer : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Float : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::String : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Datetime: - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Array : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Table : - { - switch_assign::invoke( - *this, v.cast()); - break; - } - case value_t::Empty : break; - case value_t::Unknown : assert(false); - default: assert(false); - } -} - -inline value::value(value&& v) -{ - this->type_ = v.type_; - switch(this->type_) - { - case value_t::Boolean : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Integer : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Float : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::String : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Datetime: - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Array : - { - new(&this->storage_) storage_ptr(std::move(v.storage_)); - break; - } - case value_t::Table : - { - new(&this->storage_) storage_ptr(std::move(v.storage_)); - break; - } - case value_t::Empty : break; - case value_t::Unknown : assert(false); - default: assert(false); - } -} - -inline value& value::operator=(const value& v) -{ - if(should_be_cleaned(this->type_)) - { - this->switch_clean(this->type_); - } - this->type_ = v.type(); - - switch(this->type_) - { - case value_t::Boolean : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Integer : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Float : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::String : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Datetime: - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Array : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Table : - { - switch_assign::invoke(*this, - v.cast()); - break; - } - case value_t::Empty : break; - case value_t::Unknown : assert(false); - default: assert(false); - } - return *this; -} - -inline value& value::operator=(value&& v) -{ - if(should_be_cleaned(this->type_)) - { - this->switch_clean(this->type_); - } - this->type_ = v.type_; - - switch(this->type_) - { - case value_t::Boolean : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Integer : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Float : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::String : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Datetime: - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Array : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Table : - { - switch_assign::invoke(*this, - std::move(v.cast())); - break; - } - case value_t::Empty : break; - case value_t::Unknown : assert(false); - default: assert(false); - } - return *this; -} - -template::is_toml_type, std::nullptr_t>::type> -value::value(T&& v) : type_(toml::detail::check_type()) -{ - constexpr value_t kind = toml::detail::check_type(); - switch_assign::invoke(*this, std::forward(v)); -} - -template::is_toml_type, std::nullptr_t>::type> -value& value::operator=(T&& v) -{ - constexpr value_t kind = toml::detail::check_type(); - if(should_be_cleaned(this->type_)) - { - switch_clean(this->type_); - } - this->type_ = kind; - switch_assign::invoke(*this, std::forward(v)); - return *this; -} - -template::is_toml_type, std::nullptr_t>::type> -value::value(std::initializer_list init) - : type_(toml::value_t::Array) -{ - toml::Array arr; arr.reserve(init.size()); - for(auto&& item : init) - arr.emplace_back(std::move(item)); - switch_assign::invoke(*this, std::move(arr)); -} - -inline value::value( - std::initializer_list> init) - : type_(toml::value_t::Table) -{ - toml::Table tmp; - for(auto&& item : init) - tmp.emplace(std::move(item.first), std::move(item.second)); - switch_assign::invoke(*this, std::move(tmp)); -} - template -inline typename detail::toml_default_type::type const& -value::cast() const +typename detail::toml_default_type::type& value::cast() & { if(T != this->type_) - throw type_error("current type: " + stringize(this->type_) + - std::string(" is not query type: ") + stringize(T)); + { + throw type_error(this->format_error(concat_to_string( + "[error] toml::value bad_cast type-casting to ", T), + concat_to_string("the actual type is ", this->type_))); + } return switch_cast::invoke(*this); } template -inline typename detail::toml_default_type::type& -value::cast() +typename detail::toml_default_type::type const& value::cast() const& { if(T != this->type_) - throw type_error("current type: " + stringize(this->type_) + - std::string(" is not query type: ") + stringize(T)); + { + throw type_error(this->format_error(concat_to_string( + "[error] toml::value bad_cast type-casting to ", T), + concat_to_string("the actual type is ", this->type_))); + } return switch_cast::invoke(*this); } +template +typename detail::toml_default_type::type&& value::cast() && +{ + if(T != this->type_) + { + throw type_error(this->format_error(concat_to_string( + "[error] toml::value bad_cast type-casting to ", T), + concat_to_string("the actual type is ", this->type_))); + } + return switch_cast::invoke(std::move(*this)); +} inline bool operator==(const toml::value& lhs, const toml::value& rhs) { - if(lhs.type() != rhs.type()) return false; + if(lhs.type() != rhs.type()){return false;} switch(lhs.type()) { case value_t::Boolean : @@ -515,8 +691,14 @@ inline bool operator==(const toml::value& lhs, const toml::value& rhs) return lhs.cast() == rhs.cast(); case value_t::String : return lhs.cast() == rhs.cast(); - case value_t::Datetime: - return lhs.cast() == rhs.cast(); + case value_t::OffsetDatetime: + return lhs.cast() == rhs.cast(); + case value_t::LocalDatetime: + return lhs.cast() == rhs.cast(); + case value_t::LocalDate: + return lhs.cast() == rhs.cast(); + case value_t::LocalTime: + return lhs.cast() == rhs.cast(); case value_t::Array : return lhs.cast() == rhs.cast(); case value_t::Table : @@ -528,7 +710,7 @@ inline bool operator==(const toml::value& lhs, const toml::value& rhs) } inline bool operator<(const toml::value& lhs, const toml::value& rhs) { - if(lhs.type() != rhs.type()) return (lhs.type() < rhs.type()); + if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());} switch(lhs.type()) { case value_t::Boolean : @@ -539,8 +721,12 @@ inline bool operator<(const toml::value& lhs, const toml::value& rhs) return lhs.cast() < rhs.cast(); case value_t::String : return lhs.cast() < rhs.cast(); - case value_t::Datetime: - return lhs.cast() < rhs.cast(); + case value_t::LocalDatetime: + return lhs.cast() < rhs.cast(); + case value_t::LocalDate: + return lhs.cast() < rhs.cast(); + case value_t::LocalTime: + return lhs.cast() < rhs.cast(); case value_t::Array : return lhs.cast() < rhs.cast(); case value_t::Table : From c04b75b2e36b69f59b93b70703c6f13dc3eba083 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 18:08:04 +0900 Subject: [PATCH 049/133] consider LF in the range when writing error msg --- toml/region.hpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/toml/region.hpp b/toml/region.hpp index 05a00a4..b7fc9f9 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -76,13 +76,14 @@ struct region_base region_base& operator=(const region_base&) = default; region_base& operator=(region_base&& ) = default; - virtual bool is_ok() const noexcept {return false;} + virtual bool is_ok() const noexcept {return false;} virtual std::string str() const {return std::string("unknown region");} virtual std::string name() const {return std::string("unknown file");} virtual std::string line() const {return std::string("unknown line");} virtual std::string line_num() const {return std::string("?");} + virtual std::size_t before() const noexcept {return 0;} virtual std::size_t size() const noexcept {return 0;} virtual std::size_t after() const noexcept {return 0;} @@ -136,6 +137,11 @@ struct region final : public region_base std::string str() const override {return make_string(first_, last_);} std::string line() const override { + if(this->contain_newline()) + { + return make_string(this->line_begin(), + std::find(this->line_begin(), this->last(), '\n')); + } return make_string(this->line_begin(), this->line_end()); } std::string line_num() const override @@ -156,6 +162,11 @@ struct region final : public region_base return std::distance(this->last(), this->line_end()); } + bool contain_newline() const noexcept + { + return std::find(this->first(), this->last(), '\n') != this->last(); + } + const_iterator line_begin() const noexcept { using reverse_iterator = std::reverse_iterator; From 34c3d33936190cf38f865ce8f382ac9ef685c9b0 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 19:30:46 +0900 Subject: [PATCH 050/133] use vector instead of initializer_list --- toml/region.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toml/region.hpp b/toml/region.hpp index b7fc9f9..6dba5ea 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -2,6 +2,7 @@ #define TOML11_REGION_H #include "exception.hpp" #include +#include #include #include #include @@ -225,7 +226,7 @@ template std::string format_underline(const std::string& message, const location& loc, const std::string& comment_for_underline, - std::initializer_list helps = {}) + std::vector helps = {}) { using const_iterator = typename location::const_iterator; using reverse_iterator = std::reverse_iterator; From cf03a08632c18279557b160591931bb341bf7e07 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 19:32:30 +0900 Subject: [PATCH 051/133] re-write parser using result and new value wip. --- toml/parser.hpp | 2108 ++++++++++++++++++++++++----------------------- 1 file changed, 1068 insertions(+), 1040 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 478078a..63a4080 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1,1089 +1,1117 @@ -#ifndef TOML11_PARSER -#define TOML11_PARSER -#include "value.hpp" -#include "acceptor.hpp" +#ifndef TOML11_PARSER_HPP +#define TOML11_PARSER_HPP #include "result.hpp" -#include -#include -#include -#include -#include +#include "region.hpp" +#include "combinator.hpp" +#include "lexer.hpp" +#include "types.hpp" +#include "value.hpp" namespace toml { - -struct parse_escape_sequence +namespace detail { - typedef toml::character value_type; - typedef toml::String string_type; - typedef result result_type; - template::value_type, - value_type>::value>::type> - static std::pair invoke(Iterator iter, Iterator end) - { - const auto beg = iter; - if(iter == end || *iter != '\\') - return std::make_pair(err("not an escape sequence"), iter); - ++iter; - switch(*iter) - { - case '\\': return std::make_pair(ok(string_type("\\")), std::next(iter)); - case '"' : return std::make_pair(ok(string_type("\"")), std::next(iter)); - case 'b' : return std::make_pair(ok(string_type("\b")), std::next(iter)); - case 't' : return std::make_pair(ok(string_type("\t")), std::next(iter)); - case 'n' : return std::make_pair(ok(string_type("\n")), std::next(iter)); - case 'f' : return std::make_pair(ok(string_type("\f")), std::next(iter)); - case 'r' : return std::make_pair(ok(string_type("\r")), std::next(iter)); - case 'u' : - { - if(std::distance(iter, end) < 5) - throw std::make_pair(iter, syntax_error( - "invalid escape sequence: " + std::string(beg, end))); - return std::make_pair(ok( - utf8_to_char(make_codepoint(string_type(iter+1, iter+5)))), - iter+5); - } - case 'U': - { - if(std::distance(iter, end) < 8) - throw std::make_pair(iter, syntax_error( - "invalid escape sequence: " + std::string(beg, end))); - return std::make_pair(ok( - utf8_to_char(make_codepoint(string_type(iter+1, iter+9)))), - iter+9); - } - default: throw std::make_pair(iter, syntax_error( - "unkwnon escape sequence: " + std::string(iter, end))); - } - } - - static unsigned int make_codepoint(string_type str) - { - unsigned int codepoint; - std::basic_istringstream iss(str); - iss >> std::hex >> codepoint; - return codepoint; - } - - static string_type utf8_to_char(const unsigned int codepoint) - { - string_type character; - if(codepoint < 0x80) - { - character += static_cast(codepoint); - } - else if(codepoint < 0x800) - { - character += static_cast(0xC0| codepoint >> 6); - character += static_cast(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x10000) - { - character += static_cast(0xE0| codepoint >>12); - character += static_cast(0x80|(codepoint >>6&0x3F)); - character += static_cast(0x80|(codepoint & 0x3F)); - } - else - { - character += static_cast(0xF0| codepoint >>18); - character += static_cast(0x80|(codepoint >>12&0x3F)); - character += static_cast(0x80|(codepoint >>6 &0x3F)); - character += static_cast(0x80|(codepoint & 0x3F)); - } - return character; - } -}; - -struct parse_basic_inline_string +template +result parse_boolean(location& loc) { - typedef toml::character value_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) + const auto first = loc.iter(); + if(const auto token = lex_boolean::invoke(loc)) { - const Iterator end = - is_basic_inline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - if(std::distance(iter, end) < 2) - throw internal_error("is_basic_inline_string"); - - toml::String result; result.reserve(std::distance(iter, end)-2); - ++iter; - const Iterator last = std::prev(end); // ignore '"' - while(iter != last) + const auto reg = token.unwrap(); + if (reg.str() == "true") {return ok(true);} + else if(reg.str() == "false") {return ok(false);} + else // internal error. { - if(*iter == '\\') + throw toml::internal_error(format_underline( + "[error] toml::parse_boolean: internal error", reg, + "invalid token")); + } + } + loc.iter() = first; //rollback + return err(format_underline("[error] toml::parse_boolean", loc, + "token is not boolean", {"boolean is `true` or `false`"})); +} + +template +result parse_binary_integer(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_bin_int::invoke(loc)) + { + auto str = token.unwrap().str(); + assert(str.size() > 2); // minimum -> 0b1 + integer retval(0), base(1); + for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i) + { + if (*i == '1'){retval += base; base *= 2;} + else if(*i == '0'){base *= 2;} + else if(*i == '_'){/* do nothing. */} + else // internal error. { - auto r = parse_escape_sequence::invoke(iter, last); - if(!r.first.is_ok()) - throw internal_error("parse_basic_inline_string"); - result += r.first.unwrap(); - iter = r.second; + throw toml::internal_error(format_underline( + "[error] toml::parse_integer: internal error", + token.unwrap(), "invalid token")); + } + } + return ok(retval); + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_binary_integer", loc, + "token is not binary integer", {"binary integer is like: 0b0011"})); +} + +template +result parse_octal_integer(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_oct_int::invoke(loc)) + { + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix + + std::istringstream iss(str); + integer retval(0); + iss >> std::oct >> retval; + return ok(retval); + } + loc.iter() = first; + + return err(format_underline("[error] toml::parse_octal_integer", loc, + "token is not octal integer", {"octal integer is like: 0o775"})); +} + +template +result parse_hexadecimal_integer(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_hex_int::invoke(loc)) + { + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix + + std::istringstream iss(str); + integer retval(0); + iss >> std::hex >> retval; + return ok(retval); + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_hexadecimal_integer", loc, + "token is not hex integer", {"hex integer is like: 0xC0FFEE"})); +} + +template +result parse_integer(location& loc) +{ + const auto first = loc.iter(); + if(first != loc.end() && *first == '0') + { + if(const auto bin = parse_binary_integer (loc)) {return bin;} + if(const auto oct = parse_octal_integer (loc)) {return oct;} + if(const auto hex = parse_hexadecimal_integer(loc)) {return hex;} + } + + if(const auto token = lex_dec_int::invoke(loc)) + { + auto str = token.unwrap().str(); + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + + std::istringstream iss(str); + integer retval(0); + iss >> retval; + return ok(retval); + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_integer", loc, + "token is not integer", {"integer is like: +42", + "hex integer is like: 0xC0FFEE", "octal integer is like: 0o775", + "binary integer is like: 0b0011"})); +} + +template +result parse_floating(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_float::invoke(loc)) + { + auto str = token.unwrap().str(); + if(str == "inf" || str == "+inf") + { + if(std::numeric_limits::has_infinity) + { + return ok(std::numeric_limits::infinity()); } else { - result += *iter; - ++iter; + throw std::domain_error("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); } } - return std::make_pair(ok(result), end); - } -}; - -struct parse_basic_multiline_string -{ - typedef toml::character value_type; - typedef toml::String string_type; - typedef result result_type; - - typedef is_chain_of, is_newline> - is_line_ending_backslash; - typedef is_repeat_of, is_newline>, - repeat_infinite()> ws_nl_after_backslash_remover; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_basic_multiline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - if(std::distance(iter, end) < 6) - throw internal_error("is_basic_inline_string"); - - toml::String result; result.reserve(std::distance(iter, end)-6); - std::advance(iter, 3); - const Iterator last = end - 3; - iter = is_newline::invoke(iter, last); - while(iter != last) + else if(str == "-inf") { - if(*iter == '\\') + if(std::numeric_limits::has_infinity) { - if(is_line_ending_backslash::invoke(iter, last) != iter) + return ok(-std::numeric_limits::infinity()); + } + else + { + throw std::domain_error("toml::parse_floating: inf value found" + " but the current environment does not support inf. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } + } + else if(str == "nan" || str == "+nan") + { + if(std::numeric_limits::has_quiet_NaN) + { + return ok(std::numeric_limits::quiet_NaN()); + } + else if(std::numeric_limits::has_signaling_NaN) + { + return ok(std::numeric_limits::signaling_NaN()); + } + else + { + throw std::domain_error("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } + } + else if(str == "-nan") + { + if(std::numeric_limits::has_quiet_NaN) + { + return ok(-std::numeric_limits::quiet_NaN()); + } + else if(std::numeric_limits::has_signaling_NaN) + { + return ok(-std::numeric_limits::signaling_NaN()); + } + else + { + throw std::domain_error("toml::parse_floating: NaN value found" + " but the current environment does not support NaN. Please" + " make sure that the floating-point implementation conforms" + " IEEE 754/ISO 60559 international standard."); + } + } + str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); + std::istringstream iss(str); + floating v(0.0); + iss >> v; + return ok(v); + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_floating: ", loc, + "token is not a float", {"floating point is like: -3.14e+1"})); +} + +inline std::string read_utf8_codepoint(const std::string& str) +{ + std::uint_least32_t codepoint; + std::istringstream iss(str); + iss >> std::hex >> codepoint; + + std::string character; + if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. + { + character += static_cast(codepoint); + } + else if(codepoint < 0x800) //U+0080 ... U+07FF + { + // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 + character += static_cast(0xC0| codepoint >> 6); + character += static_cast(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x10000) // U+0800...U+FFFF + { + // 1110yyyy 10yxxxxx 10xxxxxx + character += static_cast(0xE0| codepoint >> 12); + character += static_cast(0x80|(codepoint >> 6 & 0x3F)); + character += static_cast(0x80|(codepoint & 0x3F)); + } + else if(codepoint < 0x200000) // U+10000 ... U+1FFFFF + { + if(0x10FFFF < codepoint) // out of Unicode region + { + std::cerr << "WARNING: input codepoint " << str << " is too large " + << "to decode as a unicode character. It should be in " + << "range [0x00 .. 0x10FFFF]. The result may not be able " + << "to be rendered to your screen." << std::endl; + } + // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx + character += static_cast(0xF0| codepoint >> 18); + character += static_cast(0x80|(codepoint >> 12 & 0x3F)); + character += static_cast(0x80|(codepoint >> 6 & 0x3F)); + character += static_cast(0x80|(codepoint & 0x3F)); + } + else // out of UTF-8 region + { + throw std::range_error("toml::read_utf8_codepoint: input codepoint `" + + str + "` is too large to decode as utf-8. It should be in range" + " 0x00 ... 0x1FFFFF."); + } + return character; +} + +template +result parse_escape_sequence(location& loc) +{ + const auto first = loc.iter(); + if(*first != '\\') + { + return err(format_underline("[error]: " + "toml::parse_escape_sequence: location does not points \"\\\"", + loc, "should be \"\\\"")); + } + ++loc.iter(); + switch(*loc.iter()) + { + case '\\':{++loc.iter(); return ok(std::string("\\"));} + case '"' :{++loc.iter(); return ok(std::string("\""));} + case 'b' :{++loc.iter(); return ok(std::string("\b"));} + case 't' :{++loc.iter(); return ok(std::string("\t"));} + case 'n' :{++loc.iter(); return ok(std::string("\n"));} + case 'f' :{++loc.iter(); return ok(std::string("\f"));} + case 'r' :{++loc.iter(); return ok(std::string("\r"));} + case 'u' : + { + ++loc.iter(); + if(const auto token = repeat>::invoke(loc)) + { + return ok(read_utf8_codepoint(token.unwrap().str())); + } + else + { + return err(format_underline("[error] parse_escape_sequence: " + "invalid token found in UTF-8 codepoint uXXXX.", + loc, token.unwrap_err())); + } + } + case 'U': + { + ++loc.iter(); + if(const auto token = repeat>::invoke(loc)) + { + return ok(read_utf8_codepoint(token.unwrap().str())); + } + else + { + return err(format_underline("[error] parse_escape_sequence: " + "invalid token found in UTF-8 codepoint Uxxxxxxxx", + loc, token.unwrap_err())); + } + } + } + + const auto msg = format_underline("[error] parse_escape_sequence: " + "unknown escape sequence appeared.", loc, "escape sequence is one of" + " \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx", {"if you want to write " + "backslash as just one backslash, use literal string like:", + "regex = '<\\i\\c*\\s*>'"}); + loc.iter() = first; + return err(msg); +} + +template +result +parse_ml_basic_string(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_ml_basic_string::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + std::string retval; + retval.reserve(inner_loc.source()->size()); + + auto delim = lex_ml_basic_string_delim::invoke(inner_loc); + if(!delim) + { + throw internal_error(format_underline("[error] " + "parse_ml_basic_string: invalid token", + inner_loc, "should be \"\"\"")); + } + // immediate newline is ignored (if exists) + /* discard return value */ lex_newline::invoke(inner_loc); + + delim = err("tmp"); + while(!delim) + { + using lex_unescaped_seq = repeat< + either, unlimited>; + if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) + { + retval += unescaped.unwrap().str(); + } + if(auto escaped = parse_escape_sequence(inner_loc)) + { + retval += escaped.unwrap(); + } + if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc)) + { + // ignore newline after escape until next non-ws char + } + if(inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error] " + "parse_ml_basic_string: unexpected end of region", + inner_loc, "not sufficient token")); + } + delim = lex_ml_basic_string_delim::invoke(inner_loc); + } + return ok(toml::string(retval)); + } + else + { + loc.iter() = first; + return err(token.unwrap_err()); + } +} + +template +result parse_basic_string(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_basic_string::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + auto quot = lex_quotation_mark::invoke(inner_loc); + if(!quot) + { + throw internal_error(format_underline("[error] parse_basic_string: " + "invalid token", inner_loc, "should be \"")); + } + + std::string retval; + retval.reserve(inner_loc.source()->size()); + + quot = err("tmp"); + while(!quot) + { + using lex_unescaped_seq = repeat; + if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) + { + retval += unescaped.unwrap().str(); + } + if(auto escaped = parse_escape_sequence(inner_loc)) + { + retval += escaped.unwrap(); + } + if(inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error] " + "parse_ml_basic_string: unexpected end of region", + inner_loc, "not sufficient token")); + } + quot = lex_quotation_mark::invoke(inner_loc); + } + return ok(toml::string(retval)); + } + else + { + loc.iter() = first; // rollback + return err(token.unwrap_err()); + } +} + +template +result +parse_ml_literal_string(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_ml_literal_string::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_ml_literal_string_delim::invoke(inner_loc); + if(!open) + { + throw internal_error(format_underline("[error] " + "parse_ml_literal_string: invalid token", + inner_loc, "should be '''")); + } + // immediate newline is ignored (if exists) + /* discard return value */ lex_newline::invoke(inner_loc); + + const auto body = lex_ml_literal_body::invoke(inner_loc); + + const auto close = lex_ml_literal_string_delim::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline("[error] " + "parse_ml_literal_string: invalid token", + inner_loc, "should be '''")); + } + return ok(toml::string(body.unwrap().str())); + } + else + { + loc.iter() = first; // rollback + return err(token.unwrap_err()); + } +} + +template +result parse_literal_string(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_literal_string::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_apostrophe::invoke(inner_loc); + if(!open) + { + throw internal_error(format_underline("[error] " + "parse_literal_string: invalid token", + inner_loc, "should be '")); + } + + const auto body = repeat::invoke(inner_loc); + + const auto close = lex_apostrophe::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline("[error] " + "parse_literal_string: invalid token", + inner_loc, "should be '")); + } + return ok(toml::string(body.unwrap().str())); + } + else + { + loc.iter() = first; // rollback + return err(token.unwrap_err()); + } +} + +template +result parse_string(location& loc) +{ + if(const auto rslt = parse_ml_basic_string(loc)) + { + return ok(rslt.unwrap()); + } + if(const auto rslt = parse_ml_literal_string(loc)) + { + return ok(rslt.unwrap()); + } + if(const auto rslt = parse_basic_string(loc)) + { + return ok(rslt.unwrap()); + } + if(const auto rslt = parse_literal_string(loc)) + { + return ok(rslt.unwrap()); + } + return err(format_underline("[error] toml::parse_string: not a string", + loc, "not a string")); +} + +template +result parse_local_date(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_local_date::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto y = lex_date_fullyear::invoke(inner_loc); + if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') + { + throw internal_error(format_underline("[error]: " + "toml::parse_inner_local_date: invalid year format", + inner_loc, y.map_err_or_else([](const std::string& msg) { + return msg; + }, "should be `-`"))); + } + ++inner_loc.iter(); + const auto m = lex_date_month::invoke(inner_loc); + if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_date: invalid month format", + inner_loc, m.map_err_or_else([](const std::string& msg) { + return msg; + }, "should be `-`"))); + } + ++inner_loc.iter(); + const auto d = lex_date_mday::invoke(inner_loc); + if(!d) + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_date: invalid day format", + inner_loc, d.unwrap_err())); + } + return ok(local_date( + static_cast(from_string(y.unwrap().str(), 0)), + static_cast( + static_cast(from_string(m.unwrap().str(), 0)-1)), + static_cast(from_string(d.unwrap().str(), 0)))); + } + else + { + auto msg = format_underline("[error]: toml::parse_local_date: " + "invalid format", loc, token.unwrap_err(), + {"local date is like: 1979-05-27"}); + loc.iter() = first; + return err(std::move(msg)); + } +} + +template +result parse_local_time(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_local_time::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto h = lex_time_hour::invoke(inner_loc); + if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_time: invalid year format", + inner_loc, h.map_err_or_else([](const std::string& msg) { + return msg; + }, "should be `:`"))); + } + ++inner_loc.iter(); + const auto m = lex_time_minute::invoke(inner_loc); + if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_time: invalid month format", + inner_loc, m.map_err_or_else([](const std::string& msg) { + return msg; + }, "should be `:`"))); + } + ++inner_loc.iter(); + const auto s = lex_time_second::invoke(inner_loc); + if(!s) + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_time: invalid second format", + inner_loc, s.unwrap_err())); + } + local_time time( + static_cast(from_string(h.unwrap().str(), 0)), + static_cast(from_string(m.unwrap().str(), 0)), + static_cast(from_string(s.unwrap().str(), 0)), 0, 0); + + const auto before_secfrac = inner_loc.iter(); + if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) + { + auto sf = secfrac.unwrap().str(); + sf.erase(sf.begin()); // sf.front() == '.' + switch(sf.size() % 3) + { + case 2: sf += '0'; break; + case 1: sf += "00"; break; + case 0: break; + default: break; + } + if(sf.size() >= 6) + { + time.millisecond = from_string(sf.substr(0, 3), 0); + time.microsecond = from_string(sf.substr(3, 3), 0); + } + else if(sf.size() >= 3) + { + time.millisecond = from_string(sf, 0); + time.microsecond = 0; + } + } + else + { + if(before_secfrac != loc.iter()) + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_time: invalid subsecond format", + inner_loc, secfrac.unwrap_err())); + } + } + return ok(time); + } + else + { + auto msg = format_underline("[error]: toml::parse_local_time: " + "invalid format", loc, token.unwrap_err(), + {"local time is like: 00:32:00.999999"}); + loc.iter() = first; + return err(std::move(msg)); + } +} + +template +result +parse_local_datetime(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_local_date_time::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + const auto date = parse_local_date(inner_loc); + if(!date || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_datetime: invalid datetime format", + inner_loc, date.map_err_or_else([](const std::string& msg){ + return msg; + }, "date, not datetime"))); + } + const char delim = *(inner_loc.iter()++); + if(delim != 'T' && delim != 't' && delim != ' ') + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_datetime: invalid datetime format", + inner_loc, "should be `T` or ` ` (space)")); + } + const auto time = parse_local_time(inner_loc); + if(!time) + { + throw internal_error(format_underline("[error]: " + "toml::parse_local_datetime: invalid datetime format", + inner_loc, "invalid time fomrat")); + } + return ok(local_datetime(date.unwrap(), time.unwrap())); + } + else + { + auto msg = format_underline("[error]: toml::parse_local_datetime: " + "invalid format", loc, token.unwrap_err(), + {"local datetime is like: 1979-05-27T00:32:00.999999"}); + loc.iter() = first; + return err(std::move(msg)); + } +} + +template +result +parse_offset_datetime(location& loc) +{ + const auto first = loc.iter(); + if(const auto token = lex_offset_date_time::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + const auto datetime = parse_local_datetime(inner_loc); + if(!datetime || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error]: " + "toml::parse_offset_datetime: invalid datetime format", + inner_loc, datetime.map_err_or_else([](const std::string& msg){ + return msg; + }, "date, not datetime"))); + } + time_offset offset(0, 0); + if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) + { + const auto str = ofs.unwrap().str(); + if(str.front() == '+') + { + offset.hour = static_cast(from_string(str.substr(1,2), 0)); + offset.minute = static_cast(from_string(str.substr(4,2), 0)); + } + else + { + offset.hour = -static_cast(from_string(str.substr(1,2), 0)); + offset.minute = -static_cast(from_string(str.substr(4,2), 0)); + } + } + else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') + { + throw internal_error(format_underline("[error]: " + "toml::parse_offset_datetime: invalid datetime format", + inner_loc, "should be `Z` or `+HH:MM`")); + } + return ok(offset_datetime(datetime.unwrap(), offset)); + } + else + { + auto msg = format_underline("[error]: toml::parse_offset_datetime: " + "invalid format", loc, token.unwrap_err(), + {"offset datetime is like: 1979-05-27T00:32:00-07:00", + "or in UTC (w/o offset) : 1979-05-27T00:32:00Z"}); + loc.iter() = first; + return err(std::move(msg)); + } +} + +template +result parse_simple_key(location& loc) +{ + if(const auto bstr = parse_basic_string(loc)) + { + return ok(bstr.unwrap().str); + } + if(const auto lstr = parse_literal_string(loc)) + { + return ok(lstr.unwrap().str); + } + if(const auto bare = lex_unquoted_key::invoke(loc)) + { + return ok(bare.unwrap().str()); + } + return err(format_underline("[error] toml::parse_simple_key: " + "the next token is not a simple key", loc, "not a key")); +} + +// dotted key become vector of keys +template +result, std::string> parse_key(location& loc) +{ + const auto first = loc.iter(); + // dotted key -> foo.bar.baz + if(const auto token = lex_dotted_key::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + std::vector keys; + + while(inner_loc.iter() != inner_loc.end()) + { + if(const auto k = parse_simple_key(inner_loc)) + { + keys.push_back(k.unwrap()); + } + else + { + throw internal_error(format_underline("[error] " + "toml::detail::parse_key: dotted key contains invalid key", + inner_loc, k.unwrap_err())); + } + + if(inner_loc.iter() == inner_loc.end()) + { + break; + } + else if(*inner_loc.iter() == '.') + { + ++inner_loc.iter(); // to skip `.` + } + else + { + throw internal_error(format_underline("[error] toml::parse_key: " + "dotted key contains invalid key ", inner_loc, + "should be `.`")); + } + } + return ok(keys); + } + loc.iter() = first; + + // simple key -> foo + if(const auto smpl = parse_simple_key(loc)) + { + return ok(std::vector(1, smpl.unwrap())); + } + return err(format_underline("toml::parse_key: the next token is not a key", + loc, "not a key")); +} + +// forward-decl to implement parse_array and parse_table +template +result parse_value(location&); + +template +result parse_array(location& loc) +{ + const auto first = loc.iter(); + if(loc.iter() == loc.end()) + { + return err("[error] toml::parse_array: input is empty"); + } + if(*loc.iter() != '[') + { + return err(format_underline("[error] toml::parse_array: " + "token is not an array", loc, "should be [")); + } + ++loc.iter(); + + using lex_ws_comment_newline = repeat< + either, unlimited>; + + array retval; + while(loc.iter() != loc.end()) + { + lex_ws_comment_newline::invoke(loc); // skip + + if(loc.iter() != loc.end() && *loc.iter() == ']') + { + ++loc.iter(); // skip ']' + return ok(retval); + } + + if(auto val = parse_value(loc)) + { + retval.push_back(std::move(val.unwrap())); + } + else + { + return err("[error] toml::parse_array: while reading an element of " + "an array\n" + val.unwrap_err()); + } + + using lex_array_separator = sequence, character<','>>; + const auto sp = lex_array_separator::invoke(loc); + if(!sp) + { + lex_ws_comment_newline::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == ']') + { + ++loc.iter(); // skip ']' + return ok(retval); + } + else + { + return err(format_underline("[error] toml::parse_array: " + "missing array separator `,`", loc, "should be `,`")); + } + } + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_array: " + "array did not closed by `]`", loc, "should be closed")); +} + +template +result, value>, std::string> +parse_key_value_pair(location& loc) +{ + const auto first = loc.iter(); + auto key = parse_key(loc); + if(!key) + { + loc.iter() = first; + return err("[error] toml::parse_key_value_pair: while reading key-value" + " pair" + key.unwrap_err()); + } + + const auto kvsp = lex_keyval_sep::invoke(loc); + if(!kvsp) + { + const auto msg = format_underline("[error] toml::parse_key_value_pair: " + "missing key-value separator `=`", loc, "should be `=`"); + loc.iter() = first; + return err(msg); + } + + auto val = parse_value(loc); + if(!val) + { + loc.iter() = first; + return err("[error] toml::parse_key_value_pair: while reading key-value" + " pair" + val.unwrap_err()); + } + return ok(std::make_pair(std::move(key.unwrap()), std::move(val.unwrap()))); +} + +// for error messages. +template +std::string format_dotted_keys(InputIterator first, const InputIterator last) +{ + static_assert(std::is_same::value_type>::value,""); + + std::string retval(*first++); + for(; first != last; ++first) + { + retval += '.'; + retval += *first; + } + return retval; +} + +template +result +insert_nested_key(table& root, const toml::value& v, + InputIterator iter, const InputIterator last, + const bool is_array_of_table = false) +{ + static_assert(std::is_same::value_type>::value,""); + + const auto first = iter; + assert(iter != last); + + table* tab = std::addressof(root); + for(; iter != last; ++iter) // search recursively + { + const key& k = *iter; + if(std::next(iter) == last) // k is the last key + { + // XXX if the value is array-of-tables, there can be several + // tables that are in the same array. in that case, we need to + // find the last element and insert it to there. + if(is_array_of_table) + { + if(tab->count(k) == 1) // there is already an array of table { - iter = ws_nl_after_backslash_remover::invoke(std::next(iter), last); + if(!(tab->at(k).is(value_t::Array))) + { + throw syntax_error("toml::detail::insert_nested_key: " + "target is not an array of table: " + + format_dotted_keys(first, last)); + } + array& a = tab->at(k).template cast(); + if(!(a.front().is(value_t::Table))) + { + throw syntax_error("toml::detail::insert_nested_key: " + "target is not an array of table: " + + format_dotted_keys(first, last)); + } + a.push_back(v); + return ok(true); } - else + else // if not, we need to create the array of table { - auto r = parse_escape_sequence::invoke(iter, last); - if(!r.first.is_ok()) - throw internal_error("parse_basic_inline_string"); - result += r.first.unwrap(); - iter = r.second; + array aot(1, v); // array having one table + tab->insert(std::make_pair(k, value(aot))); + return ok(true); } } - else + if(tab->count(k) == 1) { - result.push_back(*iter); - ++iter; + throw syntax_error("[error] toml::detail::insert_nested_key: " + "while inserting value to table: value already exists. " + + format_dotted_keys(first, last)); } - } - return std::make_pair(ok(result), end); - } -}; - -struct parse_literal_inline_string -{ - typedef toml::character value_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_literal_inline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - if(std::distance(iter, end) < 2) - throw internal_error("is_literal_inline_string"); - - toml::String result; result.reserve(std::distance(iter, end)-2); - ++iter; - const Iterator last = end - 1; - while(iter != last) - { - result.push_back(*iter); - ++iter; - } - return std::make_pair(ok(result), end); - } -}; - -struct parse_literal_multiline_string -{ - typedef toml::character value_type; - typedef result result_type; - - typedef is_chain_of, is_newline> - is_line_ending_backslash; - typedef is_repeat_of, is_newline>, - repeat_infinite()> ws_nl_after_backslash_remover; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_literal_multiline_string::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - if(std::distance(iter, end) < 6) - throw internal_error("is_literal_multiline_string"); - - toml::String result; result.reserve(std::distance(iter, end)-6); - std::advance(iter, 3); - const Iterator last = end - 3; - iter = is_newline::invoke(iter, last); // trim first newline if exist - while(iter != last) - { - result.push_back(*iter); - ++iter; - } - return std::make_pair(ok(result), end); - } -}; - -struct parse_string -{ - typedef toml::character value_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - std::pair result(err(""), Iterator()); - if((result = parse_basic_inline_string::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_basic_multiline_string::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_literal_inline_string::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_literal_multiline_string::invoke(iter, range_end)).first.is_ok()) - return result; - else - return std::make_pair(err("does not match anything"), iter); - } -}; - -struct parse_integer -{ - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair invoke(Iterator iter, Iterator range_end) - { - const Iterator end = is_integer::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - string_type result; result.resize(std::distance(iter, end)); - std::copy_if(iter, end, result.begin(), [](value_type c){return c != '_';}); - return std::make_pair(ok(std::stoll(result)), end); - } -}; - -struct parse_float -{ - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = is_float::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - string_type result; result.resize(std::distance(iter, end)); - std::copy_if(iter, end, result.begin(), [](value_type c){return c != '_';}); - try{ - return std::make_pair(ok(std::stod(result)), end); - } - catch(std::out_of_range& oor) - { - std::cout << "extremely large Float value appeared: " - << result << "; it is negrected" << std::endl; - return std::make_pair(ok(0.0), end); - } - } -}; - -struct parse_boolean -{ - typedef toml::character value_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = is_boolean::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - return std::make_pair(ok(std::distance(iter, end) == 4), end); - } -}; - -struct parse_local_time -{ - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef result result_type; - typedef typename toml::Datetime::number_type number_type; - template - using nums = is_repeat_of, N>; - typedef is_character delim; - typedef is_character fract; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = is_local_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - toml::Datetime result; - result.hour = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = delim::invoke(nums<2>::invoke(iter, end), end); - result.minute = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = delim::invoke(nums<2>::invoke(iter, end), end); - result.second = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = fract::invoke(nums<2>::invoke(iter, end), end); - if(iter == end) - { - result.millisecond = 0.0; - result.microsecond = 0.0; - } - else if(std::distance(iter, end) <= 3) - { - result.millisecond = parse_number(iter, end); - result.microsecond = 0.0; + tab->insert(std::make_pair(k, v)); + return ok(true); } else { - result.millisecond = parse_number(iter, iter + 3); - result.microsecond = parse_number(iter + 3, end); - } - result.offset_hour = toml::Datetime::nooffset; - result.offset_minute = toml::Datetime::nooffset; - result.year = toml::Datetime::undef; - result.month = toml::Datetime::undef; - result.day = toml::Datetime::undef; - return std::make_pair(ok(result), end); - } + // if there is no corresponding value, insert it first. + if(tab->count(k) == 0) {(*tab)[k] = table{};} - template::value_type, - value_type>::value>::type> - static number_type parse_number(Iterator iter, Iterator end) - { - if(std::distance(iter, end) > 3) end = iter + 3; - string_type str(iter, end); - while(str.size() < 3){str += '0';} - return std::stoi(str); - } -}; - -struct parse_local_date -{ - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef result result_type; - template - using nums = is_repeat_of, N>; - typedef is_character delim; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = is_local_date::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - toml::Datetime result; - result.year = std::stoi(string_type(iter, nums<4>::invoke(iter, end))); - iter = delim::invoke(nums<4>::invoke(iter, end), end); - result.month = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = delim::invoke(nums<2>::invoke(iter, end), end); - result.day = std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - - result.offset_hour = toml::Datetime::nooffset; - result.offset_minute = toml::Datetime::nooffset; - result.hour = toml::Datetime::undef; - result.minute = toml::Datetime::undef; - result.second = toml::Datetime::undef; - result.millisecond = toml::Datetime::undef; - result.microsecond = toml::Datetime::undef; - return std::make_pair(ok(result), end); - } -}; - -struct parse_local_date_time -{ - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef result result_type; - template - using nums = is_repeat_of, N>; - typedef is_character delim; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_local_date_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - auto ld = parse_local_date::invoke(iter, end); - if(!ld.first.is_ok()) - throw std::make_pair(iter, syntax_error("invalid local datetime")); - toml::Datetime result(ld.first.unwrap()); - iter = delim::invoke(ld.second, end);// 'T' - - const auto time = parse_local_time::invoke(iter, end); - result.hour = time.first.unwrap().hour; - result.minute = time.first.unwrap().minute; - result.second = time.first.unwrap().second; - result.millisecond = time.first.unwrap().millisecond; - result.microsecond = time.first.unwrap().microsecond; - result.offset_hour = toml::Datetime::nooffset; - result.offset_minute = toml::Datetime::nooffset; - return std::make_pair(ok(result), end); - } -}; - -struct parse_offset_date_time -{ - typedef toml::character value_type; - typedef std::basic_string string_type; - typedef result result_type; - template - using nums = is_repeat_of, N>; - typedef is_character delim; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_offset_date_time::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - auto ldt = parse_local_date_time::invoke(iter, end); - if(!ldt.first.is_ok()) - throw std::make_pair(iter, syntax_error("invalid offset datetime")); - toml::Datetime result(ldt.first.unwrap()); - iter = ldt.second; - if(*iter == 'Z') - { - result.offset_hour = 0; - result.offset_minute = 0; - } - else - { - if(*iter != '+' && *iter != '-') - throw std::make_pair(iter, syntax_error("invalid offset-datetime")); - const int sign = (*iter == '-') ? -1 : 1; - ++iter; - result.offset_hour = sign * - std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - iter = delim::invoke(nums<2>::invoke(iter, end), end); - result.offset_minute = sign * - std::stoi(string_type(iter, nums<2>::invoke(iter, end))); - } - return std::make_pair(ok(result), end); - } -}; - -struct parse_datetime -{ - typedef toml::character value_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - std::pair result(err(""), Iterator()); - if((result = parse_offset_date_time::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_local_date_time::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_local_date::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_local_time::invoke(iter, range_end)).first.is_ok()) - return result; - else - return std::make_pair(err("does not match anything"), iter); - } -}; - -template -struct parse_fixed_type_array -{ - typedef toml::character value_type; - typedef result result_type; - typedef acceptorT acceptor_type; - typedef parserT parser_type; - typedef is_skippable_in_array skippable; - - template::value_type, - value_type>::value>::type> - static std::pair invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_fixed_type_array::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - toml::Array result; - const Iterator last = std::prev(end); - iter = skippable::invoke(std::next(iter), last); - while(iter != last) - { - const Iterator tmp = acceptor_type::invoke(iter, last); - if(tmp == iter) - throw std::make_pair(iter, syntax_error("parse_array")); - auto next = parser_type::invoke(iter, last); - if(!next.first.is_ok()) - throw std::make_pair(iter, syntax_error("parse_array")); - result.emplace_back(next.first.unwrap()); - iter = tmp; - iter = skippable::invoke(iter, last); - iter = is_character::invoke(iter, last); - iter = skippable::invoke(iter, last); - } - return std::make_pair(ok(result), end); - } -}; - -template -struct parse_inline_table; - -template -struct parse_array -{ - typedef charT value_type; - static_assert(std::is_same::value, ""); - typedef result result_type; - typedef is_skippable_in_array skippable; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - if(iter == is_array::invoke(iter, range_end)) - return std::make_pair(err("input is not an array"), iter); - - std::pair result(err(""), Iterator()); - if((result = parse_fixed_type_array, - parse_boolean>::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_fixed_type_array, - parse_string>::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_fixed_type_array, - parse_datetime>::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_fixed_type_array, - parse_float>::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_fixed_type_array, - parse_integer>::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_fixed_type_array, - parse_array>::invoke(iter, range_end)).first.is_ok()) return result; - else if((result = parse_fixed_type_array, - parse_inline_table>::invoke(iter, range_end)).first.is_ok()) - return result; - else if(skippable::invoke(std::next(iter), range_end) == // empty - std::prev(is_array::invoke(iter, range_end)) - ) return std::make_pair( - ok(toml::Array{}), is_array::invoke(iter, range_end)); - else throw std::make_pair(iter, syntax_error("no valid array here")); - } -}; - -template -struct parse_value -{ - typedef charT value_type; - static_assert(std::is_same::value, ""); - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - std::pair result(err(""), Iterator()); - if((result = parse_boolean::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_string::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_datetime::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_float::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_integer::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_array::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_inline_table::invoke(iter, range_end)).first.is_ok()) - return result; - else - return std::make_pair(err("does not match any value type"), iter); - } -}; - -struct parse_barekey -{ - typedef toml::character value_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = is_barekey::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - return std::make_pair(ok(toml::key(iter, end)), end); - } -}; - -struct parse_key -{ - typedef toml::character value_type; - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - std::pair result(err(""), Iterator()); - if((result = parse_barekey::invoke(iter, range_end)).first.is_ok()) - return result; - else if((result = parse_string::invoke(iter, range_end)).first.is_ok()) - return result; - else return std::make_pair(err("does not match anything"), iter); - } -}; - -template -struct parse_key_value_pair -{ - typedef charT value_type; - static_assert(std::is_same::value, ""); - typedef result, std::string> result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - auto tmp_key = parse_key::invoke(iter, range_end); - if(!tmp_key.first.is_ok()) - return std::make_pair(err(tmp_key.first.unwrap_err()), iter); - iter = is_any_num_of_ws::invoke(tmp_key.second, range_end); - if(*iter != '=') - throw std::make_pair(iter, syntax_error("invalid key value pair")); - iter = is_any_num_of_ws::invoke(std::next(iter), range_end); - - auto tmp_value = parse_value::invoke(iter, range_end); - if(!tmp_value.first.is_ok()) - throw std::make_pair(iter, syntax_error("invalid key value pair")); - - iter = tmp_value.second; - - return std::make_pair(ok(std::make_pair( - tmp_key.first.unwrap(), tmp_value.first.unwrap())), - is_any_num_of_ws::invoke(tmp_value.second, range_end)); - } -}; - -template -struct parse_inline_table -{ - typedef charT value_type; - static_assert(std::is_same::value, ""); - typedef result result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = is_inline_table::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - iter = is_any_num_of_ws::invoke(std::next(iter), range_end); - - const Iterator last = std::prev(end); - toml::Table result; - while(iter != last) - { - auto tmp = parse_key_value_pair::invoke(iter, last); - if(!tmp.first.is_ok()) - throw std::make_pair(iter, syntax_error("parse_inline_table")); - - result.emplace(tmp.first.unwrap()); - iter = tmp.second; - - iter = is_any_num_of_ws::invoke(iter, last); - iter = is_character::invoke(iter, last); - iter = is_any_num_of_ws::invoke(iter, last); - } - return std::make_pair(ok(result), end); - } -}; - -struct parse_table_definition -{ - typedef toml::character value_type; - typedef result, std::string> result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_table_definition::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - std::vector result; - result.reserve(std::count(iter, end, '.')+1); - - const Iterator last = std::prev(end); - iter = is_any_num_of_ws::invoke(iter, last); - iter = is_any_num_of_ws::invoke(std::next(iter), last); - - auto tmp = parse_key::invoke(iter, last); - if(!tmp.first.is_ok()) - throw std::make_pair(iter, syntax_error("table definition")); - result.emplace_back(tmp.first.unwrap()); - iter = is_any_num_of_ws::invoke(tmp.second, last); - - while(iter != last) - { - iter = is_character::invoke(iter, last); - iter = is_any_num_of_ws::invoke(iter, last); - - tmp = parse_key::invoke(iter, last); - if(!tmp.first.is_ok()) - throw std::make_pair(iter, syntax_error("table definition")); - result.emplace_back(tmp.first.unwrap()); - iter = is_any_num_of_ws::invoke(tmp.second, last); - } - return std::make_pair(ok(result), end); - } -}; - -struct parse_array_of_table_definition -{ - typedef toml::character value_type; - typedef result, std::string> result_type; - - template::value_type, - value_type>::value>::type> - static std::pair - invoke(Iterator iter, Iterator range_end) - { - const Iterator end = - is_array_of_table_definition::invoke(iter, range_end); - if(iter == end) return std::make_pair(err("input is empty"), iter); - - if(std::distance(iter, end) < 5) - throw std::make_pair(iter, syntax_error("invalid array_of_table definition")); - - std::vector result; - result.reserve(std::count(iter, end, '.')+1); - - const Iterator last = end - 2; - iter = is_any_num_of_ws::invoke(iter, last) + 2; - iter = is_any_num_of_ws::invoke(iter, last); - - auto tmp = parse_key::invoke(iter, last); - if(!tmp.first.is_ok()) - throw std::make_pair(iter, syntax_error("array of table definition")); - result.emplace_back(tmp.first.unwrap()); - iter = is_any_num_of_ws::invoke(tmp.second, last); - - while(iter != last) - { - iter = is_character::invoke(iter, last); - iter = is_any_num_of_ws::invoke(iter, last); - - tmp = parse_key::invoke(iter, last); - if(!tmp.first.is_ok()) - throw std::make_pair(iter, syntax_error("array of table definition")); - result.emplace_back(tmp.first.unwrap()); - iter = is_any_num_of_ws::invoke(tmp.second, last); - } - return std::make_pair(ok(result), end); - } -}; - -struct parse_data -{ - typedef toml::character value_type; - typedef toml::Table result_type; - - template::value_type, - value_type>::value>::type> - static result_type invoke(Iterator iter, const Iterator end) - { - toml::Table result; - auto noname = parse_table_contents(iter, end); - result = std::move(noname.first); - iter = skip_empty(noname.second, end); - - while(iter != end) - { - iter = skip_empty(iter, end); - std::pair<::toml::result, std::string>, Iterator> - tabname(err(""), Iterator()); - if((tabname = parse_table_definition::invoke(iter, end)).first.is_ok()) + // type checking... + if(tab->at(k).is(value_t::Table)) { - auto contents = parse_table_contents(tabname.second, end); - push_table(result, std::move(contents.first), - tabname.first.unwrap().begin(), tabname.first.unwrap().end()); - iter = contents.second; + tab = std::addressof((*tab)[k].template cast()); } - else if((tabname = parse_array_of_table_definition::invoke(iter, end)).first.is_ok()) + else if(tab->at(k).is(value_t::Array)) // array-of-table case { - auto contents = parse_table_contents(tabname.second, end); - push_array_of_table(result, std::move(contents.first), - tabname.first.unwrap().begin(), tabname.first.unwrap().end()); - iter = contents.second; - } - else - throw std::make_pair(iter, syntax_error("parse_data: unknown line")); - } - return result; - } - - template::value_type, - value_type>::value>::type> - static Iterator - skip_empty(Iterator iter, Iterator end) - { - while(iter != end) - { - if(*iter == '#') - { - while(iter != end && - iter == is_newline::invoke(iter, end)){++iter;} - } - else if(iter == is_newline::invoke(iter, end) && - iter == is_whitespace::invoke(iter, end)) - { - return iter; + array& a = (*tab)[k].template cast(); + if(!a.back().is(value_t::Table)) + { + throw syntax_error("toml::detail::insert_nested_key: value " + "is not a table but an array: " + + format_dotted_keys(first, last)); + } + tab = std::addressof(a.back().template cast()); } else { - ++iter; + throw syntax_error("toml::detail::insert_nested_key: value " + "is not a table but an array: " + + format_dotted_keys(first, last)); } } - return iter; - } - - template::value_type, - value_type>::value>::type> - static std::pair - parse_table_contents(Iterator iter, Iterator end) - { - toml::Table table; - iter = skip_empty(iter, end); - while(iter != end) - { - auto kv = parse_key_value_pair::invoke(iter, end); - if(!kv.first.is_ok()) return std::make_pair(table, iter); - - table.emplace(kv.first.unwrap()); - iter = kv.second; - iter = skip_empty(iter, end); - } - return std::make_pair(table, iter); - } - - template::value_type, - toml::key>::value>::type> - static void - push_table(toml::Table& data, toml::Table&& v, Iterator iter, Iterator end) - { - if(iter == std::prev(end)) - { - if(data.count(*iter) == 1) - throw syntax_error("duplicate key: " + *iter); - data.emplace(*iter, std::move(v)); - return; - } - - if(data.count(*iter) == 0) - { - data.emplace(*iter, toml::Table()); - return push_table(data[*iter].template cast(), - std::move(v), std::next(iter), end); - } - else if(data[*iter].type() == value_t::Table) - { - return push_table(data[*iter].template cast(), - std::move(v), std::next(iter), end); - } - else if(data[*iter].type() == value_t::Array) - { - auto& ar = data[*iter].template cast(); - if(ar.empty()) ar.emplace_back(toml::Table{}); - if(ar.back().type() != value_t::Table) - throw syntax_error("assign table into array having non-table type: " + *iter); - return push_table(ar.back().template cast(), - std::move(v), std::next(iter), end); - } - else - throw syntax_error("assign table into not table: " + *iter); - } - - template::value_type, - toml::key>::value>::type> - static void - push_array_of_table(toml::Table& data, toml::Table&& v, - Iterator iter, Iterator end) - { - //XXX Iterator::value_type == toml::key - if(iter == std::prev(end)) - { - if(data.count(*iter) == 0) - data.emplace(*iter, toml::Array()); - else if(data.at(*iter).type() != value_t::Array) - throw syntax_error("duplicate key: " + *iter); - - data[*iter].template cast().emplace_back(std::move(v)); - return; - } - - if(data.count(*iter) == 0) - { - data.emplace(*iter, toml::Table()); - return push_array_of_table(data[*iter].template cast(), - std::move(v), std::next(iter), end); - } - else if(data[*iter].type() == value_t::Table) - { - return push_array_of_table(data[*iter].template cast(), - std::move(v), std::next(iter), end); - } - else if(data[*iter].type() == value_t::Array) - { - auto& ar = data[*iter].template cast(); - if(ar.empty()) ar.emplace_back(toml::Table{}); - if(ar.back().type() != value_t::Table) - throw syntax_error("assign table into array having non-table type: " + *iter); - return push_array_of_table(ar.back().template cast(), - std::move(v), std::next(iter), end); - } - else - throw syntax_error("assign array of table into not table: " + *iter); - } - -}; - -template> -toml::Table parse(std::basic_istream& is) -{ - const auto initial = is.tellg(); - is.seekg(0, std::ios::end); - const auto eofpos = is.tellg(); - const std::size_t size = eofpos - initial; - is.seekg(initial); - std::vector contents(size); - typedef std::vector::const_iterator iterator_type; - is.read(contents.data(), size); - try - { - return parse_data::invoke(contents.cbegin(), contents.cend()); - } - catch(std::pair iter_except) - { - std::cerr << "toml syntax error." << std::endl; - auto iter = iter_except.first; - const std::size_t nline = 1 + std::count(contents.cbegin(), iter, '\n'); - std::cerr << "processing at line " << nline << std::endl; - while(*iter != '\n' && iter != contents.cbegin()){--iter;} - ++iter; - while(*iter != '\n' && iter != contents.cend()) - { - std::cerr << *iter; ++iter; - } - std::cerr << std::endl; - - throw iter_except.second; } + return err(std::string("toml::detail::insert_nested_key: never reach here")); } -inline toml::Table parse(const char* filename) +template +result parse_inline_table(location& loc) { - std::ifstream ifs(filename, std::ios_base::in | std::ios_base::binary); - if(!ifs.good()) + const auto first = loc.iter(); + table retval; + if(!(loc.iter() != loc.end() && *loc.iter() == '{')) { - throw std::runtime_error("file open error: " + std::string(filename)); + return err(format_underline("[error] toml::parse_inline_table: " + "the next token is not an inline table", loc, "not `{`.")); } - return parse(ifs); + ++loc.iter(); + while(loc.iter() != loc.end()) + { + maybe::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == '}') + { + ++loc.iter(); // skip `}` + return ok(retval); + } + + const auto kv_r = parse_key_value_pair(loc); + if(!kv_r) + { + return err(kv_r.unwrap_err()); + } + const std::vector& keys = kv_r.unwrap().first; + const value& val = kv_r.unwrap().second; + + const auto inserted = + insert_nested_key(retval, val, keys.begin(), keys.end()); + if(!inserted) + { + throw internal_error("[error] toml::parse_inline_table: " + "failed to insert value into table: " + inserted.unwrap_err()); + } + + using lex_table_separator = sequence, character<','>>; + const auto sp = lex_table_separator::invoke(loc); + if(!sp) + { + maybe::invoke(loc); + if(loc.iter() != loc.end() && *loc.iter() == '}') + { + ++loc.iter(); // skip `}` + return ok(retval); + } + else + { + return err(format_underline("[error] toml:::parse_inline_table:" + " missing table separator `,` ", loc, "should be `,`")); + } + } + } + loc.iter() = first; + return err(format_underline("[error] toml::parse_inline_table: " + "inline table did not closed by `}`", loc, "should be closed")); } -template> -inline toml::Table parse(const std::basic_string& filename) +template +result parse_value(location& loc) { - std::ifstream ifs(filename, std::ios_base::in | std::ios_base::binary); - if(!ifs.good()) + const auto first = loc.iter(); + if(first == loc.end()) { - throw std::runtime_error("file open error: " + filename); + return err(std::string("toml::parse_value: input is empty")); } - return parse(ifs); + + std::vector helps; + if(auto r = parse_string(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_array(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_inline_table(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_boolean(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_offset_datetime(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_local_datetime(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_local_date(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_local_time(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_floating(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + if(auto r = parse_integer(loc)) {return ok(value(r.unwrap()));} + else {helps.push_back(r.unwrap_err());} + const auto msg = format_underline("[error] toml::parse_value: " + "unknown token appeared", loc, "unknown", std::move(helps)); + loc.iter() = first; + return err(msg); } -}// toml -#endif// TOML11_PARSER +} // detail +} // toml +#endif// TOML11_PARSER_HPP From ed155a5040873da9b34261a58c3f15c042d62443 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sun, 9 Dec 2018 21:54:47 +0900 Subject: [PATCH 052/133] remove help msgs in parse_value because the error message becomes too long --- toml/parser.hpp | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 63a4080..3689b85 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1082,30 +1082,19 @@ result parse_value(location& loc) const auto first = loc.iter(); if(first == loc.end()) { - return err(std::string("toml::parse_value: input is empty")); + return err(std::string("[error] toml::parse_value: input is empty")); } + if(auto r = parse_string (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_array (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_inline_table (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_boolean (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_offset_datetime(loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_local_datetime (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_local_date (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_local_time (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_floating (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_integer (loc)) {return ok(value(std::move(r.unwrap())));} - std::vector helps; - if(auto r = parse_string(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_array(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_inline_table(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_boolean(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_offset_datetime(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_local_datetime(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_local_date(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_local_time(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_floating(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} - if(auto r = parse_integer(loc)) {return ok(value(r.unwrap()));} - else {helps.push_back(r.unwrap_err());} const auto msg = format_underline("[error] toml::parse_value: " "unknown token appeared", loc, "unknown", std::move(helps)); loc.iter() = first; From 8e18aa9b1664ed173599ad219a64e2090615ce06 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 10 Dec 2018 00:14:46 +0900 Subject: [PATCH 053/133] add toml::parse --- toml/parser.hpp | 212 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 210 insertions(+), 2 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 3689b85..aa36d0b 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -632,7 +632,7 @@ result parse_local_time(location& loc) } else { - if(before_secfrac != loc.iter()) + if(before_secfrac != inner_loc.iter()) { throw internal_error(format_underline("[error]: " "toml::parse_local_time: invalid subsecond format", @@ -1096,11 +1096,219 @@ result parse_value(location& loc) if(auto r = parse_integer (loc)) {return ok(value(std::move(r.unwrap())));} const auto msg = format_underline("[error] toml::parse_value: " - "unknown token appeared", loc, "unknown", std::move(helps)); + "unknown token appeared", loc, "unknown"); loc.iter() = first; return err(msg); } +template +result, std::string> parse_table_key(location& loc) +{ + if(auto token = lex_std_table::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_std_table_open::invoke(inner_loc); + if(!open || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error] " + "toml::parse_table_key: no `[`", inner_loc, "should be `[`")); + } + const auto keys = parse_key(inner_loc); + if(!keys) + { + throw internal_error(format_underline("[error] " + "toml::parse_table_key: invalid key", inner_loc, "not key")); + } + const auto close = lex_std_table_close::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline("[error] " + "toml::parse_table_key: no `]`", inner_loc, "should be `]`")); + } + return keys; + } + else + { + return err(token.unwrap_err()); + } +} + +template +result, std::string> +parse_array_table_key(location& loc) +{ + if(auto token = lex_array_table::invoke(loc)) + { + location inner_loc(loc.name(), token.unwrap().str()); + + const auto open = lex_array_table_open::invoke(inner_loc); + if(!open || inner_loc.iter() == inner_loc.end()) + { + throw internal_error(format_underline("[error] " + "toml::parse_array_table_key: no `[[`", inner_loc, + "should be `[[`")); + } + const auto keys = parse_key(inner_loc); + if(!keys) + { + throw internal_error(format_underline("[error] " + "toml::parse_array_table_key: invalid key", inner_loc, + "not key")); + } + const auto close = lex_array_table_close::invoke(inner_loc); + if(!close) + { + throw internal_error(format_underline("[error] " + "toml::parse_table_key: no `]]`", inner_loc, "should be `]]`")); + } + return keys; + } + else + { + return err(token.unwrap_err()); + } +} + +// parse table body (key-value pairs until the iter hits the next [tablekey]) +template +result parse_ml_table(location& loc) +{ + const auto first = loc.iter(); + if(first == loc.end()) + { + return err(std::string("toml::parse_ml_table: input is empty")); + } + + using skip_line = repeat< + sequence, maybe, lex_newline>, unlimited>; + skip_line::invoke(loc); + + table tab; + while(loc.iter() != loc.end()) + { + lex_ws::invoke(loc); + const auto before = loc.iter(); + if(const auto tmp = parse_array_table_key(loc)) // next table found + { + loc.iter() = before; + return ok(tab); + } + if(const auto tmp = parse_table_key(loc)) // next table found + { + loc.iter() = before; + return ok(tab); + } + if(const auto kv = parse_key_value_pair(loc)) + { + const std::vector& keys = kv.unwrap().first; + const value& val = kv.unwrap().second; + const auto inserted = + insert_nested_key(tab, val, keys.begin(), keys.end()); + if(!inserted) + { + return err(inserted.unwrap_err()); + } + } + else + { + return err(kv.unwrap_err()); + } + + skip_line::invoke(loc); + // comment lines are skipped by the above function call. + // However, if the file ends with comment without newline, + // it might cause parsing error because skip_line matches + // `comment + newline`, not `comment` itself. to skip the + // last comment, call lex_comment one more time. + lex_comment::invoke(loc); + } + return ok(tab); +} + +template +result parse_toml_file(location& loc) +{ + const auto first = loc.iter(); + if(first == loc.end()) + { + return err(std::string("toml::detail::parse_toml_file: input is empty")); + } + + table data; + /* root object is also table, but without [tablename] */ + if(auto tab = parse_ml_table(loc)) + { + data = std::move(tab.unwrap()); + } + else // failed (empty table is also success) + { + return err(tab.unwrap_err()); + } + while(loc.iter() != loc.end()) + { + if(const auto tabkey = parse_array_table_key(loc)) + { + const auto tab = parse_ml_table(loc); + if(!tab){return err(tab.unwrap_err());} + + const auto inserted = insert_nested_key(data, tab.unwrap(), + tabkey.unwrap().begin(), tabkey.unwrap().end(), true); + if(!inserted) {return err(inserted.unwrap_err());} + + continue; + } + if(const auto tabkey = parse_table_key(loc)) + { + const auto tab = parse_ml_table(loc); + if(!tab){return err(tab.unwrap_err());} + + const auto inserted = insert_nested_key(data, tab.unwrap(), + tabkey.unwrap().begin(), tabkey.unwrap().end()); + if(!inserted) {return err(inserted.unwrap_err());} + + continue; + } + return err(format_underline("[error]: toml::parse_toml_file: " + "unknown line appeared", loc, "unknown format")); + } + return ok(data); +} + } // detail + +inline table parse(std::istream& is, std::string fname = "unknown file") +{ + const auto beg = is.tellg(); + is.seekg(0, std::ios::end); + const auto end = is.tellg(); + const auto fsize = end - beg; + is.seekg(beg); + + // read whole file as a sequence of char + std::vector letters(fsize); + is.read(letters.data(), fsize); + + detail::location> + loc(std::move(fname), std::move(letters)); + + const auto data = detail::parse_toml_file(loc); + if(!data) + { + throw syntax_error(data.unwrap_err()); + } + return data.unwrap(); +} + +inline table parse(const std::string& fname) +{ + std::ifstream ifs(fname.c_str()); + if(!ifs.good()) + { + throw std::runtime_error("toml::parse: file open error -> " + fname); + } + return parse(ifs, fname); +} + } // toml #endif// TOML11_PARSER_HPP From 8078c719fe05f61144e9534d3e806bc43409aab5 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 10 Dec 2018 00:15:41 +0900 Subject: [PATCH 054/133] remove old code --- toml/acceptor.hpp | 777 ---------------------------------------------- 1 file changed, 777 deletions(-) delete mode 100644 toml/acceptor.hpp diff --git a/toml/acceptor.hpp b/toml/acceptor.hpp deleted file mode 100644 index 11844ea..0000000 --- a/toml/acceptor.hpp +++ /dev/null @@ -1,777 +0,0 @@ -#ifndef TOML11_ACCEPTOR -#define TOML11_ACCEPTOR -#include -#include -#include -#include "exception.hpp" - -namespace toml -{ - -template -struct is_character -{ - typedef charT value_type; - constexpr static value_type target = c; - - template::value_type, - value_type>::value>::type> - constexpr static Iterator invoke(Iterator iter, Iterator end) - { - return iter == end ? iter : *iter == c ? std::next(iter) : iter; - } -}; - -template -struct is_in_range -{ - typedef charT value_type; - constexpr static value_type upper = up; - constexpr static value_type lower = lw; - static_assert(lower <= upper, "lower <= upper"); - - template::value_type, - value_type>::value>::type> - constexpr static Iterator invoke(Iterator iter, Iterator end) - { - return iter == end ? iter : - (lower <= *iter && *iter <= upper) ? std::next(iter) : iter; - } -}; - -template -struct is_one_of -{ - typedef typename headT::value_type value_type; - static_assert( - std::is_same::value_type>::value, - "different value_type"); - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = headT::invoke(iter, end); - return (tmp != iter) ? tmp : is_one_of::invoke(iter, end); - } -}; -template -struct is_one_of -{ - typedef typename tailT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = tailT::invoke(iter, end); - return (tmp != iter) ? tmp : iter; - } -}; - -// just a wrapper for maybe_ignored -template -struct is_ignorable -{ - typedef typename condT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = condT::invoke(iter, end); - return (tmp != iter) ? tmp : iter; - } -}; - -template -struct maybe_ignored : std::false_type{}; -template -struct maybe_ignored> : std::true_type{}; - -template -struct is_chain_of_impl -{ - typedef typename headT::value_type value_type; - static_assert(std::is_same::value_type>::value, - "different value_type"); - - constexpr static bool ignorable = maybe_ignored::value; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end, Iterator rollback) - { - const Iterator tmp = headT::invoke(iter, end); - return (tmp == iter && !ignorable) ? rollback : - is_chain_of_impl::invoke(tmp, end, rollback); - } -}; - -template -struct is_chain_of_impl -{ - typedef typename tailT::value_type value_type; - constexpr static bool ignorable = maybe_ignored::value; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end, Iterator rollback) - { - const Iterator tmp = tailT::invoke(iter, end); - return (tmp == iter) ? (ignorable ? iter : rollback) : tmp; - } -}; - -template -struct is_chain_of -{ - typedef typename is_chain_of_impl::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - return is_chain_of_impl::invoke(iter, end, iter); - } -}; - -constexpr inline std::size_t repeat_infinite(){return 0ul;} - -template -struct is_repeat_of -{ - typedef typename condT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator rollback = iter; - Iterator tmp; - for(auto i=0ul; i -struct is_repeat_of -{ - typedef typename condT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - Iterator tmp = condT::invoke(iter, end); - while(tmp != iter) - { - iter = tmp; - tmp = condT::invoke(iter, end); - } - return iter; - } -}; - -template -struct is_none_of -{ - typedef typename headT::value_type value_type; - static_assert( - std::is_same::value_type>::value, - "different value_type"); - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = headT::invoke(iter, end); - return (tmp != iter) ? iter : is_none_of::invoke(iter, end); - } -}; - -template -struct is_none_of -{ - typedef typename tailT::value_type value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - const Iterator tmp = tailT::invoke(iter, end); - return (tmp != iter) ? iter : std::next(iter); - } -}; - -template -struct is_not_but -{ - typedef typename notT::value_type value_type; - static_assert( - std::is_same::value, - "different value type"); - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - return (iter != notT::invoke(iter, end)) ? iter : butT::invoke(iter, end); - } -}; - -template -using is_space = is_character; -template -using is_tab = is_character; -template -using is_number = is_in_range; -template -using is_lowercase = is_in_range; -template -using is_uppercase = is_in_range; -template -using is_alphabet = is_one_of, is_uppercase>; -template -using is_hex = is_one_of, is_in_range, - is_in_range>; -template -using is_whitespace = is_one_of, is_tab>; -template -using is_any_num_of_ws = - is_ignorable, repeat_infinite()>>; - -template -using is_newline = is_one_of, - is_chain_of, is_character>>; -template -using is_barekey_component = is_one_of, is_number, - is_character, is_character>; -template -using is_barekey = is_repeat_of, repeat_infinite()>; -template -using is_comment = - is_chain_of< - is_character, - is_repeat_of>, repeat_infinite()>, - is_newline - >; - -template -using is_basic_inline_string_component = - is_one_of< - is_none_of< is_in_range, is_character, - is_character, is_newline>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character, - is_repeat_of, 4>>, - is_chain_of, is_character, - is_repeat_of, 8>> - >; -template -using is_basic_inline_string = - is_not_but< - is_repeat_of, 3>, // not multiline - is_chain_of< - is_character, - is_ignorable, - repeat_infinite()>>, - is_character - > - >; -template -using is_basic_multiline_string_component = - is_one_of< - is_none_of< is_in_range, - is_repeat_of, 3>, - is_character>, - is_newline, - is_chain_of, is_newline>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character>, - is_chain_of, is_character, - is_repeat_of, 4>>, - is_chain_of, is_character, - is_repeat_of, 8>> - >; -template -using is_basic_multiline_string = - is_chain_of< - is_repeat_of, 3>, - is_ignorable, - repeat_infinite()>>, - is_repeat_of, 3> - >; - -template -using is_literal_inline_string_component = - is_none_of, is_character>; - -template -using is_literal_inline_string = - is_not_but< - is_repeat_of, 3>, - is_chain_of< - is_character, - is_ignorable, - repeat_infinite()>>, - is_character - > - >; - -template -using is_literal_multiline_string_component = - is_one_of< - is_none_of, - is_repeat_of, 3>>, - is_newline - >; - -template -using is_literal_multiline_string = - is_chain_of< - is_repeat_of, 3>, - is_ignorable, - repeat_infinite()>>, - is_repeat_of, 3> - >; - -template -using is_string = - is_one_of< - is_basic_inline_string, - is_basic_multiline_string, - is_literal_inline_string, - is_literal_multiline_string - >; - - -template -using is_sign = is_one_of, is_character>; -template -using is_nonzero_number = is_in_range; - -template -using is_integer_component = - is_not_but< - is_repeat_of, 2>, - is_one_of< - is_character, is_number - > - >; -template -using is_integer = - is_chain_of< - is_ignorable>, - is_one_of< - is_character, - is_chain_of< - is_nonzero_number, - is_ignorable, - repeat_infinite()> - > - > - > - >; - -template -using is_fractional_part = - is_chain_of< - is_character, - is_repeat_of, repeat_infinite()> - >; -template -using is_exponent_part = - is_chain_of< - is_one_of, is_character>, - is_integer - >; -template -using is_float = - is_one_of< - is_chain_of< - is_integer, - is_fractional_part, - is_exponent_part - >, - is_chain_of< - is_integer, - is_fractional_part - >, - is_chain_of< - is_integer, - is_exponent_part - > - >; - -template -using is_boolean = - is_one_of< - is_chain_of< - is_character, - is_character, - is_character, - is_character - >, - is_chain_of< - is_character, - is_character, - is_character, - is_character, - is_character - > - >; - -template -using is_local_time = - is_chain_of< - is_repeat_of, 2>, - is_character, - is_repeat_of, 2>, - is_character, - is_repeat_of, 2>, - is_ignorable< - is_chain_of< - is_character, - is_repeat_of, repeat_infinite()> - > - > - >; - -template -using is_local_date = - is_chain_of< - is_repeat_of, 4>, - is_character, - is_repeat_of, 2>, - is_character, - is_repeat_of, 2> - >; - -template -using is_local_date_time = - is_chain_of< - is_local_date, - is_character, - is_local_time - >; - -template -using is_offset = - is_one_of< - is_character, - is_chain_of< - is_sign, - is_repeat_of, 2>, - is_character, - is_repeat_of, 2> - > - >; - -template -using is_offset_date_time = - is_chain_of< - is_local_date_time, - is_offset - >; - -template -using is_datetime = - is_one_of< - is_offset_date_time, - is_local_date_time, - is_local_date, - is_local_time - >; - -template -using is_fundamental_type = - is_one_of< - is_basic_inline_string, - is_basic_multiline_string, - is_literal_inline_string, - is_literal_multiline_string, - is_offset_date_time, - is_local_date_time, - is_local_date, - is_local_time, - is_boolean, - is_float, - is_integer - >; - -template -using is_skippable_in_array = - is_repeat_of< - is_one_of, is_newline, is_comment>, - repeat_infinite() - >; - -template -struct is_inline_table; - -template -using is_key = - is_one_of< - is_barekey, - is_string - >; - - -template -using is_fixed_type_array = - is_chain_of< - is_character, - is_ignorable< - is_repeat_of< - is_chain_of< - is_ignorable>, - is_array_component, - is_ignorable>, - is_character - >, - repeat_infinite() - > - >, - is_ignorable< - is_chain_of< - is_ignorable>, - is_array_component, - is_ignorable>, - is_ignorable> - > - >, - is_ignorable>, - is_character - >; - -template -struct is_array -{ - typedef charT value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - return is_one_of< - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array>, - is_fixed_type_array> - >::invoke(iter, end); - } -}; - -template -struct is_inline_table -{ - typedef charT value_type; - - template::value_type, - value_type>::value>::type> - static Iterator invoke(Iterator iter, Iterator end) - { - typedef is_one_of, - is_array, is_inline_table> is_component; - - typedef is_chain_of< - is_any_num_of_ws, - is_key, - is_any_num_of_ws, - is_character, - is_ignorable< - is_repeat_of< - is_chain_of< - is_any_num_of_ws, - is_inline_key_value_pair, - is_any_num_of_ws, - is_character - >, - repeat_infinite() - > - >, - is_ignorable< - is_chain_of< - is_any_num_of_ws, - is_inline_key_value_pair, - is_any_num_of_ws, - is_ignorable> - > - >, - is_any_num_of_ws, - is_character - > entity; - return entity::invoke(iter, end); - } -}; - -template -using is_value = - is_one_of, is_array, is_inline_table>; - -// [] -template -using is_table_definition = - is_chain_of< - is_any_num_of_ws, - is_character, - is_any_num_of_ws, - is_key, - is_ignorable< - is_repeat_of< - is_chain_of< - is_any_num_of_ws, - is_character, - is_any_num_of_ws, - is_key, - is_any_num_of_ws - >, - repeat_infinite()> - >, - is_character - >; - -template -using is_array_of_table_definition = - is_chain_of< - is_any_num_of_ws, - is_repeat_of, 2>, - is_any_num_of_ws, - is_key, - is_ignorable< - is_repeat_of< - is_chain_of< - is_any_num_of_ws, - is_character, - is_any_num_of_ws, - is_key, - is_any_num_of_ws - >, - repeat_infinite()> - >, - is_repeat_of, 2> - >; - -template -using is_key_value_pair = - is_chain_of< - is_any_num_of_ws, - is_key, - is_any_num_of_ws, - is_character parse_integer(location& loc) if(const auto bin = parse_binary_integer (loc)) {return bin;} if(const auto oct = parse_octal_integer (loc)) {return oct;} if(const auto hex = parse_hexadecimal_integer(loc)) {return hex;} + // else, maybe just zero. } if(const auto token = lex_dec_int::invoke(loc)) @@ -121,7 +127,7 @@ result parse_integer(location& loc) std::istringstream iss(str); integer retval(0); iss >> retval; - return ok(retval); + return ok(std::make_pair(retval, token.unwrap())); } loc.iter() = first; return err(format_underline("[error] toml::parse_integer", loc, @@ -131,7 +137,8 @@ result parse_integer(location& loc) } template -result parse_floating(location& loc) +result>, std::string> +parse_floating(location& loc) { const auto first = loc.iter(); if(const auto token = lex_float::invoke(loc)) @@ -141,7 +148,8 @@ result parse_floating(location& loc) { if(std::numeric_limits::has_infinity) { - return ok(std::numeric_limits::infinity()); + return ok(std::make_pair( + std::numeric_limits::infinity(), token.unwrap())); } else { @@ -155,7 +163,8 @@ result parse_floating(location& loc) { if(std::numeric_limits::has_infinity) { - return ok(-std::numeric_limits::infinity()); + return ok(std::make_pair( + -std::numeric_limits::infinity(), token.unwrap())); } else { @@ -169,11 +178,13 @@ result parse_floating(location& loc) { if(std::numeric_limits::has_quiet_NaN) { - return ok(std::numeric_limits::quiet_NaN()); + return ok(std::make_pair( + std::numeric_limits::quiet_NaN(), token.unwrap())); } else if(std::numeric_limits::has_signaling_NaN) { - return ok(std::numeric_limits::signaling_NaN()); + return ok(std::make_pair( + std::numeric_limits::signaling_NaN(), token.unwrap())); } else { @@ -187,11 +198,13 @@ result parse_floating(location& loc) { if(std::numeric_limits::has_quiet_NaN) { - return ok(-std::numeric_limits::quiet_NaN()); + return ok(std::make_pair( + -std::numeric_limits::quiet_NaN(), token.unwrap())); } else if(std::numeric_limits::has_signaling_NaN) { - return ok(-std::numeric_limits::signaling_NaN()); + return ok(std::make_pair( + -std::numeric_limits::signaling_NaN(), token.unwrap())); } else { @@ -205,7 +218,7 @@ result parse_floating(location& loc) std::istringstream iss(str); floating v(0.0); iss >> v; - return ok(v); + return ok(std::make_pair(v, token.unwrap())); } loc.iter() = first; return err(format_underline("[error] toml::parse_floating: ", loc, @@ -320,7 +333,7 @@ result parse_escape_sequence(location& loc) } template -result +result>, std::string> parse_ml_basic_string(location& loc) { const auto first = loc.iter(); @@ -366,7 +379,7 @@ parse_ml_basic_string(location& loc) } delim = lex_ml_basic_string_delim::invoke(inner_loc); } - return ok(toml::string(retval)); + return ok(std::make_pair(toml::string(retval), token.unwrap())); } else { @@ -376,7 +389,8 @@ parse_ml_basic_string(location& loc) } template -result parse_basic_string(location& loc) +result>, std::string> +parse_basic_string(location& loc) { const auto first = loc.iter(); if(const auto token = lex_basic_string::invoke(loc)) @@ -413,7 +427,7 @@ result parse_basic_string(location& loc) } quot = lex_quotation_mark::invoke(inner_loc); } - return ok(toml::string(retval)); + return ok(std::make_pair(toml::string(retval), token.unwrap())); } else { @@ -423,7 +437,7 @@ result parse_basic_string(location& loc) } template -result +result>, std::string> parse_ml_literal_string(location& loc) { const auto first = loc.iter(); @@ -450,7 +464,9 @@ parse_ml_literal_string(location& loc) "parse_ml_literal_string: invalid token", inner_loc, "should be '''")); } - return ok(toml::string(body.unwrap().str())); + return ok(std::make_pair( + toml::string(body.unwrap().str(), toml::string_t::literal), + token.unwrap())); } else { @@ -460,7 +476,8 @@ parse_ml_literal_string(location& loc) } template -result parse_literal_string(location& loc) +result>, std::string> +parse_literal_string(location& loc) { const auto first = loc.iter(); if(const auto token = lex_literal_string::invoke(loc)) @@ -484,7 +501,9 @@ result parse_literal_string(location& loc) "parse_literal_string: invalid token", inner_loc, "should be '")); } - return ok(toml::string(body.unwrap().str())); + return ok(std::make_pair( + toml::string(body.unwrap().str(), toml::string_t::literal), + token.unwrap())); } else { @@ -494,30 +513,20 @@ result parse_literal_string(location& loc) } template -result parse_string(location& loc) +result>, std::string> +parse_string(location& loc) { - if(const auto rslt = parse_ml_basic_string(loc)) - { - return ok(rslt.unwrap()); - } - if(const auto rslt = parse_ml_literal_string(loc)) - { - return ok(rslt.unwrap()); - } - if(const auto rslt = parse_basic_string(loc)) - { - return ok(rslt.unwrap()); - } - if(const auto rslt = parse_literal_string(loc)) - { - return ok(rslt.unwrap()); - } + if(const auto rslt = parse_ml_basic_string(loc)) {return rslt;} + if(const auto rslt = parse_ml_literal_string(loc)) {return rslt;} + if(const auto rslt = parse_basic_string(loc)) {return rslt;} + if(const auto rslt = parse_literal_string(loc)) {return rslt;} return err(format_underline("[error] toml::parse_string: not a string", loc, "not a string")); } template -result parse_local_date(location& loc) +result>, std::string> +parse_local_date(location& loc) { const auto first = loc.iter(); if(const auto token = lex_local_date::invoke(loc)) @@ -551,11 +560,12 @@ result parse_local_date(location& loc) "toml::parse_local_date: invalid day format", inner_loc, d.unwrap_err())); } - return ok(local_date( + return ok(std::make_pair(local_date( static_cast(from_string(y.unwrap().str(), 0)), static_cast( static_cast(from_string(m.unwrap().str(), 0)-1)), - static_cast(from_string(d.unwrap().str(), 0)))); + static_cast(from_string(d.unwrap().str(), 0))), + token.unwrap())); } else { @@ -568,7 +578,8 @@ result parse_local_date(location& loc) } template -result parse_local_time(location& loc) +result>, std::string> +parse_local_time(location& loc) { const auto first = loc.iter(); if(const auto token = lex_local_time::invoke(loc)) @@ -639,7 +650,7 @@ result parse_local_time(location& loc) inner_loc, secfrac.unwrap_err())); } } - return ok(time); + return ok(std::make_pair(time, token.unwrap())); } else { @@ -652,7 +663,7 @@ result parse_local_time(location& loc) } template -result +result>, std::string> parse_local_datetime(location& loc) { const auto first = loc.iter(); @@ -682,7 +693,9 @@ parse_local_datetime(location& loc) "toml::parse_local_datetime: invalid datetime format", inner_loc, "invalid time fomrat")); } - return ok(local_datetime(date.unwrap(), time.unwrap())); + return ok(std::make_pair( + local_datetime(date.unwrap().first, time.unwrap().first), + token.unwrap())); } else { @@ -695,7 +708,7 @@ parse_local_datetime(location& loc) } template -result +result>, std::string> parse_offset_datetime(location& loc) { const auto first = loc.iter(); @@ -732,7 +745,8 @@ parse_offset_datetime(location& loc) "toml::parse_offset_datetime: invalid datetime format", inner_loc, "should be `Z` or `+HH:MM`")); } - return ok(offset_datetime(datetime.unwrap(), offset)); + return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), + token.unwrap())); } else { @@ -750,11 +764,11 @@ result parse_simple_key(location& loc) { if(const auto bstr = parse_basic_string(loc)) { - return ok(bstr.unwrap().str); + return ok(bstr.unwrap().first.str); } if(const auto lstr = parse_literal_string(loc)) { - return ok(lstr.unwrap().str); + return ok(lstr.unwrap().first.str); } if(const auto bare = lex_unquoted_key::invoke(loc)) { @@ -821,7 +835,8 @@ template result parse_value(location&); template -result parse_array(location& loc) +result>, std::string> +parse_array(location& loc) { const auto first = loc.iter(); if(loc.iter() == loc.end()) @@ -846,7 +861,8 @@ result parse_array(location& loc) if(loc.iter() != loc.end() && *loc.iter() == ']') { ++loc.iter(); // skip ']' - return ok(retval); + return ok(std::make_pair(retval, + region(loc, first, loc.iter()))); } if(auto val = parse_value(loc)) @@ -867,7 +883,8 @@ result parse_array(location& loc) if(loc.iter() != loc.end() && *loc.iter() == ']') { ++loc.iter(); // skip ']' - return ok(retval); + return ok(std::make_pair(retval, + region(loc, first, loc.iter()))); } else { @@ -1019,7 +1036,8 @@ insert_nested_key(table& root, const toml::value& v, } template -result parse_inline_table(location& loc) +result>, std::string> +parse_inline_table(location& loc) { const auto first = loc.iter(); table retval; @@ -1035,7 +1053,8 @@ result parse_inline_table(location& loc) if(loc.iter() != loc.end() && *loc.iter() == '}') { ++loc.iter(); // skip `}` - return ok(retval); + return ok(std::make_pair( + retval, region(loc, first, loc.iter()))); } const auto kv_r = parse_key_value_pair(loc); @@ -1062,7 +1081,8 @@ result parse_inline_table(location& loc) if(loc.iter() != loc.end() && *loc.iter() == '}') { ++loc.iter(); // skip `}` - return ok(retval); + return ok(std::make_pair( + retval, region(loc, first, loc.iter()))); } else { @@ -1084,16 +1104,26 @@ result parse_value(location& loc) { return err(std::string("[error] toml::parse_value: input is empty")); } - if(auto r = parse_string (loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_array (loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_inline_table (loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_boolean (loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_offset_datetime(loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_local_datetime (loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_local_date (loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_local_time (loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_floating (loc)) {return ok(value(std::move(r.unwrap())));} - if(auto r = parse_integer (loc)) {return ok(value(std::move(r.unwrap())));} + if(auto r = parse_string (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_array (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_inline_table (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_boolean (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_offset_datetime(loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_local_datetime (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_local_date (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_local_time (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_floating (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} + if(auto r = parse_integer (loc)) + {return ok(value(std::move(r.unwrap().first), std::move(r.unwrap().second)));} const auto msg = format_underline("[error] toml::parse_value: " "unknown token appeared", loc, "unknown"); @@ -1102,7 +1132,8 @@ result parse_value(location& loc) } template -result, std::string> parse_table_key(location& loc) +result, region>, std::string> +parse_table_key(location& loc) { if(auto token = lex_std_table::invoke(loc)) { @@ -1126,7 +1157,7 @@ result, std::string> parse_table_key(location& loc) throw internal_error(format_underline("[error] " "toml::parse_table_key: no `]`", inner_loc, "should be `]`")); } - return keys; + return ok(std::make_pair(keys.unwrap(), token.unwrap())); } else { @@ -1135,7 +1166,7 @@ result, std::string> parse_table_key(location& loc) } template -result, std::string> +result, region>, std::string> parse_array_table_key(location& loc) { if(auto token = lex_array_table::invoke(loc)) @@ -1162,7 +1193,7 @@ parse_array_table_key(location& loc) throw internal_error(format_underline("[error] " "toml::parse_table_key: no `]]`", inner_loc, "should be `]]`")); } - return keys; + return ok(std::make_pair(keys.unwrap(), token.unwrap())); } else { @@ -1241,19 +1272,27 @@ result parse_toml_file(location& loc) { data = std::move(tab.unwrap()); } - else // failed (empty table is also success) + else // failed (empty table is regarded as success in parse_ml_table) { return err(tab.unwrap_err()); } while(loc.iter() != loc.end()) { + // here, the region of [table] is regarded as the table-key because + // the table body is normally too big and it is not so informative + // if the first key-value pair of the table is shown in the error + // message. if(const auto tabkey = parse_array_table_key(loc)) { const auto tab = parse_ml_table(loc); if(!tab){return err(tab.unwrap_err());} - const auto inserted = insert_nested_key(data, tab.unwrap(), - tabkey.unwrap().begin(), tabkey.unwrap().end(), true); + const auto& keys = tabkey.unwrap().first; + const auto& reg = tabkey.unwrap().second; + + const auto inserted = insert_nested_key(data, + toml::value(tab.unwrap(), reg), + keys.begin(), keys.end(), /*is_array_of_table=*/ true); if(!inserted) {return err(inserted.unwrap_err());} continue; @@ -1263,8 +1302,11 @@ result parse_toml_file(location& loc) const auto tab = parse_ml_table(loc); if(!tab){return err(tab.unwrap_err());} - const auto inserted = insert_nested_key(data, tab.unwrap(), - tabkey.unwrap().begin(), tabkey.unwrap().end()); + const auto& keys = tabkey.unwrap().first; + const auto& reg = tabkey.unwrap().second; + + const auto inserted = insert_nested_key(data, + toml::value(tab.unwrap(), reg), keys.begin(), keys.end()); if(!inserted) {return err(inserted.unwrap_err());} continue; From bf2158ae98d654f6474ea3819f9f59e1ad314fa4 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 10 Dec 2018 15:57:44 +0900 Subject: [PATCH 059/133] add map from exact toml type -> toml::value_t --- toml/types.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/toml/types.hpp b/toml/types.hpp index 315f0e5..fb16075 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -127,6 +127,29 @@ template<> struct toml_default_type {typedef table template<> struct toml_default_type {typedef void type;}; template<> struct toml_default_type {typedef void type;}; +template struct toml_value_t {static constexpr value_t value = value_t::Unknown ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Boolean ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Integer ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Float ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::String ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::OffsetDatetime;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::LocalDatetime ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::LocalDate ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::LocalTime ;}; +template<> struct toml_value_t{static constexpr value_t value = value_t::Array ;}; +template<> struct toml_value_t
{static constexpr value_t value = value_t::Table ;}; +template constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t::value; +constexpr value_t toml_value_t
::value; + template struct is_exact_toml_type : disjunction< std::is_same, From 3f991c475999925356dd1630e2cbaf3571b155d6 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 10 Dec 2018 15:58:20 +0900 Subject: [PATCH 060/133] improve power of get --- toml/get.hpp | 199 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 152 insertions(+), 47 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index cbd48a8..2c84de1 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -6,91 +6,175 @@ namespace toml { +// ============================================================================ +// exact toml::* type + template::value, std::nullptr_t>::type = nullptr> inline T& get(value& v) { - constexpr value_t kind = detail::check_type(); - return v.cast(); + return v.cast::value>(); } template::value, std::nullptr_t>::type = nullptr> inline T const& get(const value& v) { - constexpr value_t kind = detail::check_type(); - return v.cast(); + return v.cast::value>(); } +template::value, std::nullptr_t>::type = nullptr> +inline T && get(value&& v) +{ + return v.cast::value>(); +} + +// ============================================================================ +// integer convertible from toml::Integer + template>, - detail::negation>, std::is_integral + std::is_integral, // T is integral + detail::negation>, // but not bool + detail::negation> // but not toml::integer >::value, std::nullptr_t>::type = nullptr> inline T get(const value& v) { return static_cast(v.cast()); } + +// ============================================================================ +// floating point convertible from toml::Float + template>, std::is_floating_point + std::is_floating_point, // T is floating_point + detail::negation> // but not toml::Float >::value, std::nullptr_t>::type = nullptr> inline T get(const value& v) { return static_cast(v.cast()); } -// array-like type +// ============================================================================ +// std::string; toml uses its own toml::string, but it should be convertible to +// std::string seamlessly + +template::value, std::nullptr_t>::type = nullptr> +inline std::string& get(value& v) +{ + return v.cast().str; +} + +template::value, std::nullptr_t>::type = nullptr> +inline std::string const& get(const value& v) +{ + return v.cast().str; +} + +template::value, std::nullptr_t>::type = nullptr> +inline std::string get(value&& v) +{ + return std::move(v.cast().str); +} + + +// ============================================================================ +// forward declaration. ignore this. template>, detail::is_container + detail::is_container, // T is container + detail::has_resize_method, // T::resize(N) works + detail::negation> // but not toml::array + >::value, std::nullptr_t>::type = nullptr> +T get(const value& v); +template, // T is container + detail::negation>, // no T::resize() exists + detail::negation> // not toml::array + >::value, std::nullptr_t>::type = nullptr> +T get(const value& v); +template::value, std::nullptr_t>::type = nullptr> +T get(const value& v); +template::value, std::nullptr_t>::type = nullptr> +T get(const value& v); +template, // T is map + detail::negation> // but not toml::table + >::value, std::nullptr_t>::type = nullptr> +T get(const toml::value& v); + +// ============================================================================ +// array-like types; most likely STL container, like std::vector, etc. + +template, // T is container + detail::has_resize_method, // T::resize(N) works + detail::negation> // but not toml::array >::value, std::nullptr_t>::type = nullptr> T get(const value& v) { + using value_type = typename T::value_type; const auto& ar = v.cast(); - T tmp; - try - { - ::toml::resize(tmp, ar.size()); - } - catch(std::invalid_argument& iv) - { - throw type_error("toml::get: static array: size is not enough"); - } - std::transform(ar.cbegin(), ar.cend(), tmp.begin(), - [](value const& elem){return get(elem);}); - return tmp; + + T container; container.resize(ar.size()); + std::transform(ar.cbegin(), ar.cend(), container.begin(), + [](const value& x){return ::toml::get(x);}); + return container; } -// table-like case +// ============================================================================ +// array-like types; but does not have resize(); most likely std::array. + template>, detail::is_map + detail::is_container, // T is container + detail::negation>, // no T::resize() exists + detail::negation> // not toml::array >::value, std::nullptr_t>::type = nullptr> -T get(const toml::value& v) +T get(const value& v) { - const auto& tb = v.cast(); - T tmp; - for(const auto& kv : tb){tmp.insert(kv);} - return tmp; + using value_type = typename T::value_type; + const auto& ar = v.cast(); + + T container; + if(ar.size() != container.size()) + { + throw std::out_of_range(detail::format_error_for_value(v, concat_to_string( + "[erorr] toml::get specified container size is ", container.size(), + " but there are ", ar.size(), " elements in toml array."), "here")); + } + std::transform(ar.cbegin(), ar.cend(), container.begin(), + [](const value& x){return ::toml::get(x);}); + return container; } -// array -> pair -template::value, - std::nullptr_t>::type = nullptr> +// ============================================================================ +// std::pair. + +template::value, std::nullptr_t>::type = nullptr> T get(const value& v) { using first_type = typename T::first_type; using second_type = typename T::second_type; + const auto& ar = v.cast(); if(ar.size() != 2) { - throw std::out_of_range( - "toml::get: value does not have 2 elements."); + throw std::out_of_range(detail::format_error_for_value(v, concat_to_string( + "[erorr] toml::get specified std::pair but there are ", ar.size(), + " elements in toml array."), "here")); } - - T tmp; - tmp.first = get(ar.at(0)); - tmp.second = get(ar.at(1)); - return tmp; + return std::make_pair(::toml::get(ar.at(0)), + ::toml::get(ar.at(1))); } +// ============================================================================ +// std::tuple. + namespace detail { @@ -103,25 +187,46 @@ T get_tuple_impl(const toml::Array& a, index_sequence) } // detail -// array -> tuple -template::value, - std::nullptr_t>::type = nullptr> +template::value, std::nullptr_t>::type = nullptr> T get(const value& v) { const auto& ar = v.cast(); if(ar.size() != std::tuple_size::value) { - throw std::out_of_range( - "toml::get: array value does not have " + - std::to_string(std::tuple_size::value) + - std::string(" elements (array has ") + std::to_string(ar.size()) + - std::string(" elements).")); + throw std::out_of_range(detail::format_error_for_value(v, concat_to_string( + "[erorr] toml::get specified std::tuple with ", + std::tuple_size::value, "elements, but there are ", ar.size(), + " elements in toml array."), "here")); } return detail::get_tuple_impl(ar, detail::make_index_sequence::value>{}); } -// get_or ----------------------------------------------------------------- +// ============================================================================ +// map-like types; most likely STL map, like std::map or std::unordered_map. + +template, // T is map + detail::negation> // but not toml::table + >::value, std::nullptr_t>::type = nullptr> +T get(const toml::value& v) +{ + using key_type = typename T::key_type; + using mapped_type = typename T::mapped_type; + static_assert(std::is_convertible::value, + "toml::get only supports map type of which key_type is " + "convertible from std::string."); + T map; + for(const auto& kv : v.cast()) + { + map[key_type(kv.first)] = ::toml::get(kv.second); + } + return map; +} + +// ============================================================================ +// get_or template inline typename std::remove_cv::type>::type From ff83a6a477d171fe1721d1b2b5ef6d3cb65a3c47 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 10 Dec 2018 21:42:56 +0900 Subject: [PATCH 061/133] remove redundant part of error messages --- toml/parser.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 99eea49..7346cdb 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -907,8 +907,7 @@ parse_key_value_pair(location& loc) if(!key) { loc.iter() = first; - return err("[error] toml::parse_key_value_pair: while reading key-value" - " pair" + key.unwrap_err()); + return err(key.unwrap_err()); } const auto kvsp = lex_keyval_sep::invoke(loc); @@ -924,8 +923,7 @@ parse_key_value_pair(location& loc) if(!val) { loc.iter() = first; - return err("[error] toml::parse_key_value_pair: while reading key-value" - " pair" + val.unwrap_err()); + return err(val.unwrap_err()); } return ok(std::make_pair(std::move(key.unwrap()), std::move(val.unwrap()))); } From 4e57c5f5df4c1baf1af2d4b0f3c56239f442c7ef Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 10 Dec 2018 21:43:02 +0900 Subject: [PATCH 062/133] improve error message for invalid line like a = 12 = true, newline is expected after 12 --- toml/parser.hpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 7346cdb..8c9bbf4 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1209,8 +1209,9 @@ result parse_ml_table(location& loc) return err(std::string("toml::parse_ml_table: input is empty")); } + // XXX at lest one newline is needed using skip_line = repeat< - sequence, maybe, lex_newline>, unlimited>; + sequence, maybe, lex_newline>, at_least<1>>; skip_line::invoke(loc); table tab; @@ -1244,7 +1245,18 @@ result parse_ml_table(location& loc) return err(kv.unwrap_err()); } - skip_line::invoke(loc); + const auto newline = skip_line::invoke(loc); + if(!newline && loc.iter() != loc.end()) + { + const auto before = loc.iter(); + lex_ws::invoke(loc); // skip whitespace + const auto msg = format_underline("[error] toml::parse_table: " + "invalid line format", loc, concat_to_string( + "expected newline, but got '", show_char(*loc.iter()), "'.")); + loc.iter() = before; + return err(msg); + } + // comment lines are skipped by the above function call. // However, if the file ends with comment without newline, // it might cause parsing error because skip_line matches From 0759e757ae47a31e6b8adc21d79ad4f750f26350 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 10 Dec 2018 22:01:18 +0900 Subject: [PATCH 063/133] move is_chrono_duration from types to traits --- toml/traits.hpp | 5 +++++ toml/types.hpp | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/toml/traits.hpp b/toml/traits.hpp index 6a347e8..aec49b0 100644 --- a/toml/traits.hpp +++ b/toml/traits.hpp @@ -2,6 +2,7 @@ #define TOML11_TRAITS #include #include +#include #include namespace toml @@ -85,6 +86,10 @@ template struct is_std_tuple : std::false_type{}; template struct is_std_tuple> : std::true_type{}; +template struct is_chrono_duration: std::false_type{}; +template +struct is_chrono_duration>: std::true_type{}; + // to use toml::get> in C++11 template struct index_sequence{}; diff --git a/toml/types.hpp b/toml/types.hpp index fb16075..0e9637d 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -82,9 +82,6 @@ inline std::basic_string stringize(value_t t) namespace detail { -template struct is_chrono_duration: std::false_type{}; -template -struct is_chrono_duration>: std::true_type{}; template constexpr inline value_t check_type() From 75c136924b8bd6e02b15c72272e06c0d768bfef0 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Mon, 10 Dec 2018 22:06:06 +0900 Subject: [PATCH 064/133] add datetimes to chrono --- toml/get.hpp | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/toml/get.hpp b/toml/get.hpp index 2c84de1..430c4cc 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -80,9 +80,48 @@ inline std::string get(value&& v) return std::move(v.cast().str); } +// ============================================================================ +// std::chrono::duration from toml::local_time. + +template::value, std::nullptr_t>::type = nullptr> +inline T get(value& v) +{ + return std::chrono::duration_cast( + std::chrono::microseconds(v.cast())); +} // ============================================================================ -// forward declaration. ignore this. +// std::chrono::system_clock::time_point from toml::datetime variants + +template::value, + std::nullptr_t>::type = nullptr> +inline T get(value& v) +{ + switch(v.type()) + { + case value_t::LocalDate: + { + return std::chrono::system_clock::time_point( + v.cast()); + } + case value_t::LocalDatetime: + { + return std::chrono::system_clock::time_point( + v.cast()); + } + default: + { + return std::chrono::system_clock::time_point( + v.cast()); + } + } +} + +// ============================================================================ +// forward declaration to use this recursively. ignore this and go ahead. + template, // T is container detail::has_resize_method, // T::resize(N) works From d75a9770663bd72a6b451af1f7f082385880d317 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 11:35:07 +0900 Subject: [PATCH 065/133] improve error message for bad unwrap a bit --- toml/result.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/toml/result.hpp b/toml/result.hpp index ac08059..e0085f7 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -370,7 +370,7 @@ struct result { if(is_err()) { - throw std::runtime_error("result: bad unwrap: " + + throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return this->succ.value; @@ -379,7 +379,7 @@ struct result { if(is_err()) { - throw std::runtime_error("result: bad unwrap: " + + throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return this->succ.value; @@ -388,7 +388,7 @@ struct result { if(is_err()) { - throw std::runtime_error("result: bad unwrap: " + + throw std::runtime_error("toml::result: bad unwrap: " + format_error(this->as_err())); } return std::move(this->succ.value); @@ -396,17 +396,17 @@ struct result error_type& unwrap_err() & { - if(is_ok()) {throw std::runtime_error("result: bad unwrap_err");} + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return this->fail.value; } error_type const& unwrap_err() const& { - if(is_ok()) {throw std::runtime_error("result: bad unwrap_err");} + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return this->fail.value; } error_type&& unwrap_err() && { - if(is_ok()) {throw std::runtime_error("result: bad unwrap_err");} + if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} return std::move(this->fail.value); } From 38135940e97cfa7a8268881a1cf2fb496a3df207 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 11:35:35 +0900 Subject: [PATCH 066/133] add expect(toml::value) --- toml/get.hpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/toml/get.hpp b/toml/get.hpp index 430c4cc..056da64 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -276,5 +276,48 @@ get_or(const toml::Table& tab, const toml::key& ky, T&& opt) typename std::remove_reference::type>::type>(tab.find(ky)->second); } +// ============================================================================ +// expect + +template +auto expect(const toml::value& v) + -> result(v)), std::string> +{ + try + { + return ok(get(v)); + } + catch(const type_error& te) + { + return err(te.what()); + } +} +template +auto expect(toml::value& v) + -> result(v)), std::string> +{ + try + { + return ok(get(v)); + } + catch(const type_error& te) + { + return err(te.what()); + } +} +template +auto expect(toml::value&& v) + -> result(std::move(v))), std::string> +{ + try + { + return ok(get(std::move(v))); + } + catch(const type_error& te) + { + return err(te.what()); + } +} + } // toml #endif// TOML11_GET From 27b9334f103a678930173e2cbbd123aad4273f40 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 21:40:48 +0900 Subject: [PATCH 067/133] skip BOM if exists --- toml/parser.hpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/toml/parser.hpp b/toml/parser.hpp index 8c9bbf4..c36dd53 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -6,6 +6,7 @@ #include "lexer.hpp" #include "types.hpp" #include "value.hpp" +#include namespace toml { @@ -1344,6 +1345,22 @@ inline table parse(std::istream& is, std::string fname = "unknown file") detail::location> loc(std::move(fname), std::move(letters)); + // skip BOM if exists. + // XXX component of BOM (like 0xEF) exceeds the representable range of + // signed char, so on some (actually, most) of the environment, these cannot + // be compared to char. However, since we are always out of luck, we need to + // check our chars are equivalent to BOM. To do this, first we need to + // convert char to unsigned char to guarantee the comparability. + if(letters.size() >= 3) + { + std::array BOM; + std::memcpy(BOM.data(), letters.data(), 3); + if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) + { + loc.iter() += 3; // BOM found. skip. + } + } + const auto data = detail::parse_toml_file(loc); if(!data) { From 0253f49101d7a9c0e6f39a82e3abb51c132cd536 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 21:51:39 +0900 Subject: [PATCH 068/133] add a test for parser (WIP) --- tests/CMakeLists.txt | 14 +-- tests/test_parse_aux.hpp | 38 ++++++++ tests/test_parse_boolean.cpp | 24 ++++++ tests/test_parse_floating.cpp | 158 ++++++++++++++++++++++++++++++++++ tests/test_parse_integer.cpp | 92 ++++++++++++++++++++ 5 files changed, 321 insertions(+), 5 deletions(-) create mode 100644 tests/test_parse_aux.hpp create mode 100644 tests/test_parse_boolean.cpp create mode 100644 tests/test_parse_floating.cpp create mode 100644 tests/test_parse_integer.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e514d4e..e1ecd9e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,15 +1,19 @@ set(TEST_NAMES + test_datetime + test_utility + test_result + test_traits + test_value test_lex_boolean test_lex_integer test_lex_floating test_lex_datetime test_lex_string test_lex_key_comment - test_datetime - test_utility - test_result - test_traits - test_value + test_parse_boolean + test_parse_integer + test_parse_floating + # test_to_toml # test_from_toml # test_get diff --git a/tests/test_parse_aux.hpp b/tests/test_parse_aux.hpp new file mode 100644 index 0000000..956669a --- /dev/null +++ b/tests/test_parse_aux.hpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +// some of the parsers returns not only a value but also a region. +#define TOML11_TEST_PARSE_EQUAL(psr, tkn, expct) \ +do { \ + const std::string token(tkn); \ + toml::detail::location loc("test", token); \ + const auto result = psr(loc); \ + BOOST_CHECK(result.is_ok()); \ + if(result.is_ok()){ \ + BOOST_CHECK(result.unwrap().first == expct); \ + } else { \ + std::cerr << "parser " << #psr << " failed with input `"; \ + std::cerr << token << "`.\n"; \ + std::cerr << "reason: " << result.unwrap_err() << '\n'; \ + } \ +} while(false); \ +/**/ + +#define TOML11_TEST_PARSE_EQUAL_VALUE(tkn, expct) \ +do { \ + const std::string token(tkn); \ + toml::detail::location loc("test", token); \ + const auto result = toml::detail::parse_value(loc); \ + BOOST_CHECK(result.is_ok()); \ + if(result.is_ok()){ \ + BOOST_CHECK(result.unwrap() == expct); \ + } else { \ + std::cerr << "parse_value failed with input `"; \ + std::cerr << token << "`.\n"; \ + std::cerr << "reason: " << result.unwrap_err() << '\n'; \ + } \ +} while(false); \ +/**/ diff --git a/tests/test_parse_boolean.cpp b/tests/test_parse_boolean.cpp new file mode 100644 index 0000000..119ffde --- /dev/null +++ b/tests/test_parse_boolean.cpp @@ -0,0 +1,24 @@ +#define BOOST_TEST_MODULE "test_parse_boolean" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_boolean) +{ + TOML11_TEST_PARSE_EQUAL(parse_boolean, "true", true); + TOML11_TEST_PARSE_EQUAL(parse_boolean, "false", false); +} + +BOOST_AUTO_TEST_CASE(test_boolean_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE( "true", toml::value( true)); + TOML11_TEST_PARSE_EQUAL_VALUE("false", toml::value(false)); +} diff --git a/tests/test_parse_floating.cpp b/tests/test_parse_floating.cpp new file mode 100644 index 0000000..898dbf6 --- /dev/null +++ b/tests/test_parse_floating.cpp @@ -0,0 +1,158 @@ +#define BOOST_TEST_MODULE "parse_floating_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_fractional) +{ + TOML11_TEST_PARSE_EQUAL(parse_floating, "1.0", 1.0); + TOML11_TEST_PARSE_EQUAL(parse_floating, "0.1", 0.1); + TOML11_TEST_PARSE_EQUAL(parse_floating, "0.001", 0.001); + TOML11_TEST_PARSE_EQUAL(parse_floating, "0.100", 0.1); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+3.14", 3.14); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-3.14", -3.14); + TOML11_TEST_PARSE_EQUAL(parse_floating, "3.1415_9265_3589", 3.141592653589); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+3.1415_9265_3589", 3.141592653589); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-3.1415_9265_3589", -3.141592653589); + TOML11_TEST_PARSE_EQUAL(parse_floating, "123_456.789", 123456.789); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+123_456.789", 123456.789); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-123_456.789", -123456.789); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+0.0", 0.0); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-0.0", -0.0); +} + +BOOST_AUTO_TEST_CASE(test_fractional_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE("1.0", value( 1.0)); + TOML11_TEST_PARSE_EQUAL_VALUE("0.1", value( 0.1)); + TOML11_TEST_PARSE_EQUAL_VALUE("0.001", value( 0.001)); + TOML11_TEST_PARSE_EQUAL_VALUE("0.100", value( 0.1)); + TOML11_TEST_PARSE_EQUAL_VALUE("+3.14", value( 3.14)); + TOML11_TEST_PARSE_EQUAL_VALUE("-3.14", value(-3.14)); + TOML11_TEST_PARSE_EQUAL_VALUE("3.1415_9265_3589", value( 3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE("+3.1415_9265_3589", value( 3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE("-3.1415_9265_3589", value(-3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE("123_456.789", value( 123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE("+123_456.789", value( 123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE("-123_456.789", value(-123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE("+0.0", value( 0.0)); + TOML11_TEST_PARSE_EQUAL_VALUE("-0.0", value(-0.0)); +} + +BOOST_AUTO_TEST_CASE(test_exponential) +{ + TOML11_TEST_PARSE_EQUAL(parse_floating, "1e10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1e+10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1e-10", 1e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e+10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+1e-10", 1e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e10", -1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e+10", -1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-1e-10", -1e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "123e-10", 123e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1E10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1E+10", 1e10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1E-10", 1e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "123E-10", 123e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-10", 123e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1_2_3E-1_0", 123e-10); + TOML11_TEST_PARSE_EQUAL(parse_floating, "+0e0", 0.0); + TOML11_TEST_PARSE_EQUAL(parse_floating, "-0e0", -0.0); +} + +BOOST_AUTO_TEST_CASE(test_exponential_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE("1e10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE("1e+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE("1e-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE("+1e10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE("+1e+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE("+1e-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE("-1e10", value(-1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE("-1e+10", value(-1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE("-1e-10", value(-1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE("123e-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE("1E10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE("1E+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE("1E-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE("123E-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE("1_2_3E-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE("1_2_3E-1_0", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE("+0e0", value( 0.0)); + TOML11_TEST_PARSE_EQUAL_VALUE("-0e0", value(-0.0)); +} +BOOST_AUTO_TEST_CASE(test_fe) +{ + TOML11_TEST_PARSE_EQUAL(parse_floating, "6.02e23", 6.02e23); + TOML11_TEST_PARSE_EQUAL(parse_floating, "6.02e+23", 6.02e23); + TOML11_TEST_PARSE_EQUAL(parse_floating, "1.112_650_06e-17", 1.11265006e-17); +} +BOOST_AUTO_TEST_CASE(test_fe_vaule) +{ + TOML11_TEST_PARSE_EQUAL_VALUE("6.02e23", value(6.02e23)); + TOML11_TEST_PARSE_EQUAL_VALUE("6.02e+23", value(6.02e23)); + TOML11_TEST_PARSE_EQUAL_VALUE("1.112_650_06e-17", value(1.11265006e-17)); +} + +BOOST_AUTO_TEST_CASE(test_inf) +{ + { + const std::string token("inf"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isinf(r.unwrap().first)); + BOOST_CHECK(r.unwrap().first > 0.0); + } + { + const std::string token("+inf"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isinf(r.unwrap().first)); + BOOST_CHECK(r.unwrap().first > 0.0); + } + { + const std::string token("-inf"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isinf(r.unwrap().first)); + BOOST_CHECK(r.unwrap().first < 0.0); + } +} + +BOOST_AUTO_TEST_CASE(test_nan) +{ + { + const std::string token("nan"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isnan(r.unwrap().first)); + } + { + const std::string token("+nan"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isnan(r.unwrap().first)); + } + { + const std::string token("-nan"); + toml::detail::location loc("test", token); + const auto r = parse_floating(loc); + BOOST_CHECK(r.is_ok()); + BOOST_CHECK(std::isnan(r.unwrap().first)); + } +} diff --git a/tests/test_parse_integer.cpp b/tests/test_parse_integer.cpp new file mode 100644 index 0000000..e1d1f12 --- /dev/null +++ b/tests/test_parse_integer.cpp @@ -0,0 +1,92 @@ +#define BOOST_TEST_MODULE "parse_integer_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_decimal) +{ + TOML11_TEST_PARSE_EQUAL(parse_integer, "1234", 1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "+1234", 1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "-1234", -1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0", 0); + TOML11_TEST_PARSE_EQUAL(parse_integer, "1_2_3_4", 1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "+1_2_3_4", +1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "-1_2_3_4", -1234); + TOML11_TEST_PARSE_EQUAL(parse_integer, "123_456_789", 123456789); +} + +BOOST_AUTO_TEST_CASE(test_decimal_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE( "1234", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE( "+1234", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE( "-1234", toml::value( -1234)); + TOML11_TEST_PARSE_EQUAL_VALUE( "0", toml::value( 0)); + TOML11_TEST_PARSE_EQUAL_VALUE( "1_2_3_4", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE( "+1_2_3_4", toml::value( +1234)); + TOML11_TEST_PARSE_EQUAL_VALUE( "-1_2_3_4", toml::value( -1234)); + TOML11_TEST_PARSE_EQUAL_VALUE("123_456_789", toml::value(123456789)); +} + +BOOST_AUTO_TEST_CASE(test_hex) +{ + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEADBEEF", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdeadbeef", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEADbeef", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xDEAD_BEEF", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdead_beef", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xdead_BEEF", 0xDEADBEEF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0xFF", 0xFF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0x00FF", 0xFF); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0x0000FF", 0xFF); +} + +BOOST_AUTO_TEST_CASE(test_hex_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE("0xDEADBEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE("0xdeadbeef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE("0xDEADbeef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE("0xDEAD_BEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE("0xdead_beef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE("0xdead_BEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE("0xFF", value(0xFF)); + TOML11_TEST_PARSE_EQUAL_VALUE("0x00FF", value(0xFF)); + TOML11_TEST_PARSE_EQUAL_VALUE("0x0000FF", value(0xFF)); +} + +BOOST_AUTO_TEST_CASE(test_oct) +{ + TOML11_TEST_PARSE_EQUAL(parse_integer, "0o777", 64*7+8*7+7); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0o7_7_7", 64*7+8*7+7); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0o007", 7); +} + +BOOST_AUTO_TEST_CASE(test_oct_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE("0o777", value(64*7+8*7+7)); + TOML11_TEST_PARSE_EQUAL_VALUE("0o7_7_7", value(64*7+8*7+7)); + TOML11_TEST_PARSE_EQUAL_VALUE("0o007", value(7)); +} + +BOOST_AUTO_TEST_CASE(test_bin) +{ + TOML11_TEST_PARSE_EQUAL(parse_integer, "0b10000", 16); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0b010000", 16); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0b01_00_00", 16); + TOML11_TEST_PARSE_EQUAL(parse_integer, "0b111111", 63); +} + +BOOST_AUTO_TEST_CASE(test_bin_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE("0b10000", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE("0b010000", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE("0b01_00_00", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE("0b111111", value(63)); +} From 30556453230559170e5c19f2781a5f94a8c68fdb Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 21:52:53 +0900 Subject: [PATCH 069/133] remove old tests --- tests/CMakeLists.txt | 2 - tests/test_acceptor.cpp | 592 ---------------------------------- tests/test_value_operator.cpp | 98 ------ 3 files changed, 692 deletions(-) delete mode 100644 tests/test_acceptor.cpp delete mode 100644 tests/test_value_operator.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e1ecd9e..7234860 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,8 +18,6 @@ set(TEST_NAMES # test_from_toml # test_get # test_get_or -# test_value_operator -# test_acceptor # test_parser # test_parse_file # test_parse_unicode diff --git a/tests/test_acceptor.cpp b/tests/test_acceptor.cpp deleted file mode 100644 index dfce2eb..0000000 --- a/tests/test_acceptor.cpp +++ /dev/null @@ -1,592 +0,0 @@ -#define BOOST_TEST_MODULE "test_acceptor" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include - -BOOST_AUTO_TEST_CASE(test_conditions) -{ - { - const std::string tmp(" "); - const std::string dummy("dummy"); - BOOST_CHECK(toml::is_space::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_space::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - - { - const std::string tmp("\t"); - const std::string dummy("dummy"); - BOOST_CHECK(toml::is_tab::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_tab::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - - { - for(int i=0; i<10; ++i) - { - const std::string tmp = std::to_string(i); - const std::string dummy("dummy"); - BOOST_CHECK(toml::is_number::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_number::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - } - - { - for(char c='a'; c <= 'z'; ++c) - { - const std::string tmp(1, c); - const std::string dummy(1, std::toupper(c)); - BOOST_CHECK(toml::is_lowercase::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_lowercase::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - } - { - for(char c='A'; c <= 'Z'; ++c) - { - const std::string tmp(1, c); - const std::string dummy(1, std::tolower(c)); - BOOST_CHECK(toml::is_uppercase::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_uppercase::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - } - { - const std::string tmp(" "); - const std::string dummy("a"); - BOOST_CHECK(toml::is_whitespace::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_whitespace::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - { - const std::string tmp("\t"); - const std::string dummy("a"); - BOOST_CHECK(toml::is_whitespace::invoke(tmp.begin(), tmp.end()) == tmp.end()); - BOOST_CHECK(toml::is_whitespace::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } - { - const std::string tmp("hoge1-piyo2_fuga3"); - const std::string dummy(" \t"); - BOOST_CHECK(toml::is_barekey::invoke(tmp.begin(), tmp.end()) != tmp.begin()); - BOOST_CHECK(toml::is_barekey::invoke(dummy.begin(), dummy.end()) == dummy.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_basic_inline_string) -{ - using is_valid = toml::is_basic_inline_string; - { - const std::string simple("\"hoge1-piyo2_fuga3\""); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string quote("\"hoge1-\\\"piyo2\\\"_fuga3\""); - BOOST_CHECK(is_valid::invoke(quote.cbegin(), quote.cend()) == quote.cend()); - } - { - const std::string escape("\"I'm a string. \\\"You can quote me\\\". Name\\tJos\\u00E9\\nLocation\\tSF.\""); - BOOST_CHECK(is_valid::invoke(escape.cbegin(), escape.cend()) == escape.cend()); - } - { - const std::string empty("\"\""); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - - { - const std::string newline("\"newline\r\nhoge\""); - BOOST_CHECK(is_valid::invoke(newline.cbegin(), newline.cend()) == newline.cbegin()); - } - { - const std::string invalid_escape("\"foo\\abar\""); - BOOST_CHECK(is_valid::invoke(invalid_escape.cbegin(), invalid_escape.cend()) == invalid_escape.cbegin()); - } - { - const std::string invalid_character("\"foo\10bar\""); - BOOST_CHECK(is_valid::invoke(invalid_character.cbegin(), invalid_character.cend()) == invalid_character.cbegin()); - } - { - const std::string multi("\"\"\"multiline\"\"\""); - BOOST_CHECK(is_valid::invoke(multi.cbegin(), multi.cend()) == multi.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_basic_multiline_string) -{ - using is_valid = toml::is_basic_multiline_string; - { - const std::string simple("\"\"\"foobar\"\"\""); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string quote("\"\"\"hoge1-\"piyo2\"_fuga3\"\"\""); - BOOST_CHECK(is_valid::invoke(quote.cbegin(), quote.cend()) == quote.cend()); - } - { - const std::string newline("\"\"\"hoge1-\npiyo2_\r\nfuga3\"\"\""); - BOOST_CHECK(is_valid::invoke(newline.cbegin(), newline.cend()) == newline.cend()); - } - { - const std::string escape("\"\"\"I'm a string. \"You can quote me\". Name\\tJos\\u00E9\\nLocation\\tSF.\"\"\""); - BOOST_CHECK(is_valid::invoke(escape.cbegin(), escape.cend()) == escape.cend()); - } - { - const std::string empty("\"\"\"\"\"\""); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - { - const std::string ending_backslash("\"\"\"hoge\\\n piyo\\\n\"\"\""); - BOOST_CHECK(is_valid::invoke(ending_backslash.cbegin(), ending_backslash.cend()) == ending_backslash.cend()); - } - - { - const std::string invalid_escape("\"\"\"foo\\abar\"\"\""); - BOOST_CHECK(is_valid::invoke(invalid_escape.cbegin(), invalid_escape.cend()) == invalid_escape.cbegin()); - } - { - const std::string invalid_character("\"\"\"foo\10bar\"\"\""); - BOOST_CHECK(is_valid::invoke(invalid_character.cbegin(), invalid_character.cend()) == invalid_character.cbegin()); - } - { - const std::string single("\"singleline\""); - BOOST_CHECK(is_valid::invoke(single.cbegin(), single.cend()) == single.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_literal_inline_string) -{ - using is_valid = toml::is_literal_inline_string; - { - const std::string simple("'foobar'"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string nonescape("'C:\\Users\\nodejs\\templates'"); - BOOST_CHECK(is_valid::invoke(nonescape.cbegin(), nonescape.cend()) == nonescape.cend()); - } - { - const std::string empty("''"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - - { - const std::string quote("'hoge1-'piyo2'_fuga3'"); - BOOST_CHECK(is_valid::invoke(quote.cbegin(), quote.cend()) == quote.cbegin() + 8); - } - { - const std::string newline("'hoge1-\npiyo2_\r\nfuga3'"); - BOOST_CHECK(is_valid::invoke(newline.cbegin(), newline.cend()) == newline.cbegin()); - } - { - const std::string invalid_character("'foo\10bar'"); - BOOST_CHECK(is_valid::invoke(invalid_character.cbegin(), invalid_character.cend()) == invalid_character.cbegin()); - } - { - const std::string multi("'''multiline'''"); - BOOST_CHECK(is_valid::invoke(multi.cbegin(), multi.cend()) == multi.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_literal_multiline_string) -{ - using is_valid = toml::is_literal_multiline_string; - { - const std::string simple("'''foobar'''"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string quote("'''hoge1-'piyo2'_fuga3'''"); - BOOST_CHECK(is_valid::invoke(quote.cbegin(), quote.cend()) == quote.cend()); - } - { - const std::string nonescape("'''C:\\Users\\nodejs\\templates'''"); - BOOST_CHECK(is_valid::invoke(nonescape.cbegin(), nonescape.cend()) == nonescape.cend()); - } - { - const std::string newline("'''hoge1-\npiyo2_\r\nfuga3'''"); - BOOST_CHECK(is_valid::invoke(newline.cbegin(), newline.cend()) == newline.cend()); - } - { - const std::string empty("''''''"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - - { - const std::string invalid_character("'''foo\10bar'''"); - BOOST_CHECK(is_valid::invoke(invalid_character.cbegin(), invalid_character.cend()) == invalid_character.cbegin()); - } - { - const std::string single("'singleline'"); - BOOST_CHECK(is_valid::invoke(single.cbegin(), single.cend()) == single.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_integer) -{ - using is_valid = toml::is_integer; - { - const std::string simple("1"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - } - { - const std::string psign("+1234"); - BOOST_CHECK(is_valid::invoke(psign.cbegin(), psign.cend()) == psign.cend()); - const std::string nsign("-1234"); - BOOST_CHECK(is_valid::invoke(nsign.cbegin(), nsign.cend()) == nsign.cend()); - } - { - const std::string zero("0"); - BOOST_CHECK(is_valid::invoke(zero.cbegin(), zero.cend()) == zero.cend()); - } - { - const std::string us("1_2_3_4_5"); - BOOST_CHECK(is_valid::invoke(us.cbegin(), us.cend()) == us.cend()); - } - - { - const std::string f("12.34"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()+2); - } - { - const std::string f("12e34"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()+2); - } - { - const std::string ascii("1234a"); - BOOST_CHECK(is_valid::invoke(ascii.cbegin(), ascii.cend()) == ascii.cbegin()+4); - } -} - -BOOST_AUTO_TEST_CASE(test_float) -{ - using is_valid = toml::is_float; - { - const std::string simplef("1.0"); - BOOST_CHECK(is_valid::invoke(simplef.cbegin(), simplef.cend()) == simplef.cend()); - const std::string simplee("1e0"); - BOOST_CHECK(is_valid::invoke(simplee.cbegin(), simplee.cend()) == simplee.cend()); - const std::string both("6.626e-34"); - BOOST_CHECK(is_valid::invoke(both.cbegin(), both.cend()) == both.cend()); - } - { - const std::string psign("+1.0"); - BOOST_CHECK(is_valid::invoke(psign.cbegin(), psign.cend()) == psign.cend()); - const std::string nsign("-1.0"); - BOOST_CHECK(is_valid::invoke(nsign.cbegin(), nsign.cend()) == nsign.cend()); - } - { - const std::string psmall("+0.001"); - BOOST_CHECK(is_valid::invoke(psmall.cbegin(), psmall.cend()) == psmall.cend()); - const std::string nsmall("-0.001"); - BOOST_CHECK(is_valid::invoke(nsmall.cbegin(), nsmall.cend()) == nsmall.cend()); - } - { - const std::string zero("0.0"); - BOOST_CHECK(is_valid::invoke(zero.cbegin(), zero.cend()) == zero.cend()); - } - { - const std::string us("9_224_617.445_991_228_313"); - BOOST_CHECK(is_valid::invoke(us.cbegin(), us.cend()) == us.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_boolean) -{ - using is_valid = toml::is_boolean; - { - const std::string t("true"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cend()); - const std::string f("false"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cend()); - } - { - const std::string t("True"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cbegin()); - const std::string f("False"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_localtime) -{ - using is_valid = toml::is_local_time; - { - const std::string t("07:32:00"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cend()); - const std::string tf("07:32:00.0000"); - BOOST_CHECK(is_valid::invoke(tf.cbegin(), tf.cend()) == tf.cend()); - } - { - const std::string d("1907-32-00"); - BOOST_CHECK(is_valid::invoke(d.cbegin(), d.cend()) == d.cbegin()); - const std::string f("1907:32:00"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_localdate) -{ - using is_valid = toml::is_local_date; - { - const std::string d("1907-32-00"); - BOOST_CHECK(is_valid::invoke(d.cbegin(), d.cend()) == d.cend()); - } - { - const std::string t("07:32:00"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cbegin()); - const std::string f("1907:32:00"); - BOOST_CHECK(is_valid::invoke(f.cbegin(),f.cend()) == f.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_localdatetime) -{ - using is_valid = toml::is_local_date_time; - { - const std::string dt("1907-32-00T07:32:00"); - BOOST_CHECK(is_valid::invoke(dt.cbegin(), dt.cend()) == dt.cend()); - const std::string dtf("1907-32-00T07:32:00.0000"); - BOOST_CHECK(is_valid::invoke(dtf.cbegin(), dtf.cend()) == dtf.cend()); - } - { - const std::string d("1907-32-00"); - BOOST_CHECK(is_valid::invoke(d.cbegin(), d.cend()) == d.cbegin()); - const std::string t("07:32:00"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cbegin()); - const std::string f("1907-32-00 07:32:00"); - BOOST_CHECK(is_valid::invoke(f.cbegin(), f.cend()) == f.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_offsetdatetime) -{ - using is_valid = toml::is_offset_date_time; - { - const std::string dtZ("1907-32-00T07:32:00Z"); - BOOST_CHECK(is_valid::invoke(dtZ.cbegin(), dtZ.cend()) == dtZ.cend()); - const std::string dtfZ("1907-32-00T07:32:00.0000Z"); - BOOST_CHECK(is_valid::invoke(dtfZ.cbegin(), dtfZ.cend()) == dtfZ.cend()); - const std::string dtp("1907-32-00T07:32:00+12:34"); - BOOST_CHECK(is_valid::invoke(dtp.cbegin(), dtp.cend()) == dtp.cend()); - const std::string dtfp("1907-32-00T07:32:00.0000+12:34"); - BOOST_CHECK(is_valid::invoke(dtfp.cbegin(), dtfp.cend()) == dtfp.cend()); - const std::string dtn("1907-32-00T07:32:00-12:34"); - BOOST_CHECK(is_valid::invoke(dtn.cbegin(), dtn.cend()) == dtn.cend()); - const std::string dtfn("1907-32-00T07:32:00.0000-12:34"); - BOOST_CHECK(is_valid::invoke(dtfn.cbegin(), dtfn.cend()) == dtfn.cend()); - } - { - const std::string d("1907-32-00"); - BOOST_CHECK(is_valid::invoke(d.cbegin(), d.cend()) == d.cbegin()); - const std::string t("07:32:00"); - BOOST_CHECK(is_valid::invoke(t.cbegin(), t.cend()) == t.cbegin()); - const std::string l("1907-32-00T07:32:00"); - BOOST_CHECK(is_valid::invoke(l.cbegin(), l.cend()) == l.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_array) -{ - using is_valid = toml::is_array; - { - const std::string arr0("[]"); - BOOST_CHECK(is_valid::invoke(arr0.cbegin(), arr0.cend()) == arr0.cend()); - const std::string arr1("[1,2,3]"); - BOOST_CHECK(is_valid::invoke(arr1.cbegin(), arr1.cend()) == arr1.cend()); - const std::string arr2("[ 1,2,3 ]"); - BOOST_CHECK(is_valid::invoke(arr2.cbegin(), arr2.cend()) == arr2.cend()); - const std::string arr3("[ 1, 2, 3 ]"); - BOOST_CHECK(is_valid::invoke(arr3.cbegin(), arr3.cend()) == arr3.cend()); - const std::string arr4("[ 1, 2, 3, ]"); - BOOST_CHECK(is_valid::invoke(arr4.cbegin(), arr4.cend()) == arr4.cend()); - const std::string arr5("[ 1, 2, 3,]"); - BOOST_CHECK(is_valid::invoke(arr5.cbegin(), arr5.cend()) == arr5.cend()); - const std::string arr6("[ 1 , 2 , 3 ,]"); - BOOST_CHECK(is_valid::invoke(arr6.cbegin(), arr6.cend()) == arr6.cend()); - } - { - const std::string arr1("[\"red\", \"yellow\", \"green\"]"); - BOOST_CHECK(is_valid::invoke(arr1.cbegin(), arr1.cend()) == arr1.cend()); - const std::string arr2("[\"]\", \"#\", \" \"]"); - BOOST_CHECK(is_valid::invoke(arr2.cbegin(), arr2.cend()) == arr2.cend()); - const std::string arr3("[[1, 2, 3], ['a', 'b', 'c']]"); - BOOST_CHECK(is_valid::invoke(arr3.cbegin(), arr3.cend()) == arr3.cend()); - const std::string arr4("[{hoge = 1}, {piyo = 'a'}, {fuga = [1,2,3]}]"); - BOOST_CHECK(is_valid::invoke(arr4.cbegin(), arr4.cend()) == arr4.cend()); - } - { - const std::string arr1("[1,\n2,#comment\n3]"); - BOOST_CHECK(is_valid::invoke(arr1.cbegin(), arr1.cend()) == arr1.cend()); - const std::string arr2("[#c\n1,\n2,#comment\r\n3]"); - BOOST_CHECK(is_valid::invoke(arr2.cbegin(), arr2.cend()) == arr2.cend()); - } - - { - const std::string invalid("[1, 3.14, 'string']"); - BOOST_CHECK(is_valid::invoke(invalid.cbegin(), invalid.cend()) == invalid.cbegin()); - const std::string valid("[[1,2,3], [3.14, 2.71, 1.414], ['foo', 'bar']]"); - BOOST_CHECK(is_valid::invoke(valid.cbegin(), valid.cend()) == valid.cend()); - } - -} - -BOOST_AUTO_TEST_CASE(test_inline_table) -{ - using is_valid = toml::is_inline_table; - { - const std::string tab0("{}"); - BOOST_CHECK(is_valid::invoke(tab0.cbegin(), tab0.cend()) == tab0.cend()); - const std::string tab1("{hoge=1,piyo=2,fuga=3}"); - BOOST_CHECK(is_valid::invoke(tab1.cbegin(), tab1.cend()) == tab1.cend()); - const std::string tab2("{hoge=1, piyo=2, fuga=3}"); - BOOST_CHECK(is_valid::invoke(tab2.cbegin(), tab2.cend()) == tab2.cend()); - const std::string tab3("{ hoge=1, piyo=2, fuga=3 }"); - BOOST_CHECK(is_valid::invoke(tab3.cbegin(), tab3.cend()) == tab3.cend()); - const std::string tab4("{ hoge = 1, piyo = 2, fuga = 3 }"); - BOOST_CHECK(is_valid::invoke(tab4.cbegin(), tab4.cend()) == tab4.cend()); - const std::string tab5("{hoge = 1, piyo = 2, fuga = 3}"); - BOOST_CHECK(is_valid::invoke(tab5.cbegin(), tab5.cend()) == tab5.cend()); - const std::string tab6("{hoge = 1, piyo = 2, fuga = 3,}"); - BOOST_CHECK(is_valid::invoke(tab6.cbegin(), tab6.cend()) == tab6.cend()); - const std::string tab7("{hoge = 1, piyo = 2, fuga = 3, }"); - BOOST_CHECK(is_valid::invoke(tab7.cbegin(), tab7.cend()) == tab7.cend()); - } - { - const std::string tab0("{hoge = 1, piyo = 2.0}"); - BOOST_CHECK(is_valid::invoke(tab0.cbegin(), tab0.cend()) == tab0.cend()); - const std::string tab1("{hoge = [1,2,3], piyo = {fuga = {}}}"); - BOOST_CHECK(is_valid::invoke(tab1.cbegin(), tab1.cend()) == tab1.cend()); - const std::string tab2("{hoge = \"}\", piyo = \"#\"}"); - BOOST_CHECK(is_valid::invoke(tab2.cbegin(), tab2.cend()) == tab2.cend()); - const std::string tab3("{b=true, i=1, f=2.0, d=1907-03-02T07:32:00, s='str', a=[1,2,3], t={foo=1}}"); - BOOST_CHECK(is_valid::invoke(tab3.cbegin(), tab3.cend()) == tab3.cend()); - } - { - const std::string tab0("{hoge = \"}\",\n piyo = \"#\"}"); - BOOST_CHECK(is_valid::invoke(tab0.cbegin(), tab0.cend()) == tab0.cbegin()); - } -} - -BOOST_AUTO_TEST_CASE(test_table_definition) -{ - using is_valid = toml::is_table_definition; - { - const std::string simple("[hoge]"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - const std::string dotted("[hoge.piyo.fuga]"); - BOOST_CHECK(is_valid::invoke(dotted.cbegin(), dotted.cend()) == dotted.cend()); - const std::string spaced_dotted("[hoge . piyo .fuga. foo]"); - BOOST_CHECK(is_valid::invoke(spaced_dotted.cbegin(), spaced_dotted.cend()) == spaced_dotted.cend()); - const std::string quoted("[\"hoge\"]"); - BOOST_CHECK(is_valid::invoke(quoted.cbegin(), quoted.cend()) == quoted.cend()); - const std::string quoted_dot("[\"hoge\".'piyo'.fuga]"); - BOOST_CHECK(is_valid::invoke(quoted_dot.cbegin(), quoted_dot.cend()) == quoted_dot.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_array_of_table_definition) -{ - using is_valid = toml::is_array_of_table_definition; - { - const std::string simple("[[hoge]]"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - const std::string dotted("[[hoge.piyo.fuga]]"); - BOOST_CHECK(is_valid::invoke(dotted.cbegin(), dotted.cend()) == dotted.cend()); - const std::string spaced_dotted("[[hoge . piyo .fuga. foo]]"); - BOOST_CHECK(is_valid::invoke(spaced_dotted.cbegin(), spaced_dotted.cend()) == spaced_dotted.cend()); - const std::string quoted("[[\"hoge\"]]"); - BOOST_CHECK(is_valid::invoke(quoted.cbegin(), quoted.cend()) == quoted.cend()); - const std::string quoted_dot("[[\"hoge\".'piyo'.fuga]]"); - BOOST_CHECK(is_valid::invoke(quoted_dot.cbegin(), quoted_dot.cend()) == quoted_dot.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_key) -{ - using is_valid = toml::is_key; - { - const std::string simple("foobar"); - BOOST_CHECK(is_valid::invoke(simple.cbegin(), simple.cend()) == simple.cend()); - const std::string quoted("\"foo#bar.baz\\n\""); - BOOST_CHECK(is_valid::invoke(quoted.cbegin(), quoted.cend()) == quoted.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_value) -{ - using is_valid = toml::is_value; - { - const std::string boolean("true"); - BOOST_CHECK(is_valid::invoke(boolean.cbegin(), boolean.cend()) == boolean.cend()); - const std::string integer("-42"); - BOOST_CHECK(is_valid::invoke(integer.cbegin(), integer.cend()) == integer.cend()); - const std::string floating("-42e0"); - BOOST_CHECK(is_valid::invoke(floating.cbegin(), floating.cend()) == floating.cend()); - const std::string string("\"string\""); - BOOST_CHECK(is_valid::invoke(string.cbegin(), string.cend()) == string.cend()); - const std::string datetime("1901-01-01T00:00:00"); - BOOST_CHECK(is_valid::invoke(datetime.cbegin(), datetime.cend()) == datetime.cend()); - const std::string array("[1,2,3]"); - BOOST_CHECK(is_valid::invoke(array.cbegin(), array.cend()) == array.cend()); - const std::string table("{foo=1,bar=2.0,baz='3'}"); - BOOST_CHECK(is_valid::invoke(table.cbegin(), table.cend()) == table.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_key_value_pair) -{ - using is_valid = toml::is_key_value_pair; - { - const std::string kv("key=1"); - BOOST_CHECK(is_valid::invoke(kv.cbegin(), kv.cend()) == kv.cend()); - } - { - const std::string kv("key = 1"); - BOOST_CHECK(is_valid::invoke(kv.cbegin(), kv.cend()) == kv.cend()); - } - { - const std::string kv(" key = 1"); - BOOST_CHECK(is_valid::invoke(kv.cbegin(), kv.cend()) == kv.cend()); - } - { - const std::string kv(" key = 1 "); - BOOST_CHECK(is_valid::invoke(kv.cbegin(), kv.cend()) == kv.cend()); - } - { - const std::string boolean("key = true"); - BOOST_CHECK(is_valid::invoke(boolean.cbegin(), boolean.cend()) == boolean.cend()); - const std::string integer("key = -42"); - BOOST_CHECK(is_valid::invoke(integer.cbegin(), integer.cend()) == integer.cend()); - const std::string floating("key = -42.0"); - BOOST_CHECK(is_valid::invoke(floating.cbegin(), floating.cend()) == floating.cend()); - const std::string string("key = \"string\""); - BOOST_CHECK(is_valid::invoke(string.cbegin(), string.cend()) == string.cend()); - const std::string datetime("key = 1901-01-01T00:00:00"); - BOOST_CHECK(is_valid::invoke(datetime.cbegin(), datetime.cend()) == datetime.cend()); - const std::string array("key = [1,2,3]"); - BOOST_CHECK(is_valid::invoke(array.cbegin(), array.cend()) == array.cend()); - const std::string table("key = {foo=1,bar=2.0,baz='3'}"); - BOOST_CHECK(is_valid::invoke(table.cbegin(), table.cend()) == table.cend()); - } -} - -BOOST_AUTO_TEST_CASE(test_empty_line) -{ - using is_valid = toml::is_empty_line; - { - const std::string empty("\n"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - { - const std::string empty(" \n"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - { - const std::string empty("#comment\n"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } - { - const std::string empty(" #comment\n"); - BOOST_CHECK(is_valid::invoke(empty.cbegin(), empty.cend()) == empty.cend()); - } -} diff --git a/tests/test_value_operator.cpp b/tests/test_value_operator.cpp deleted file mode 100644 index a630c98..0000000 --- a/tests/test_value_operator.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#define BOOST_TEST_MODULE "test_value_operator" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include - -BOOST_AUTO_TEST_CASE(test_value_equal) -{ - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); - - toml::value u1(b); - toml::value u2(i); - toml::value u3(f); - toml::value u4(s); - toml::value u5(d); - toml::value u6(a); - toml::value u7(t); - - const bool b1 = v1 == u1; - const bool b2 = v2 == u2; - const bool b3 = v3 == u3; - const bool b4 = v4 == u4; - const bool b5 = v5 == u5; - const bool b6 = v6 == u6; - const bool b7 = v7 == u7; - - BOOST_CHECK(b1); - BOOST_CHECK(b2); - BOOST_CHECK(b3); - BOOST_CHECK(b4); - BOOST_CHECK(b5); - BOOST_CHECK(b6); - BOOST_CHECK(b7); - - { - const bool n1 = v1 != u1; - const bool n2 = v2 != u2; - const bool n3 = v3 != u3; - const bool n4 = v4 != u4; - const bool n5 = v5 != u5; - const bool n6 = v6 != u6; - const bool n7 = v7 != u7; - - BOOST_CHECK(!n1); - BOOST_CHECK(!n2); - BOOST_CHECK(!n3); - BOOST_CHECK(!n4); - BOOST_CHECK(!n5); - BOOST_CHECK(!n6); - BOOST_CHECK(!n7); - } - - { - const bool n1 = v1 == u2; - const bool n2 = v2 == u3; - const bool n3 = v3 == u4; - const bool n4 = v4 == u5; - const bool n5 = v5 == u6; - const bool n6 = v6 == u7; - const bool n7 = v7 == u1; - - BOOST_CHECK(!n1); - BOOST_CHECK(!n2); - BOOST_CHECK(!n3); - BOOST_CHECK(!n4); - BOOST_CHECK(!n5); - BOOST_CHECK(!n6); - BOOST_CHECK(!n7); - } -} From 247bcb071417e4f4ce19cff7336f57e846f239aa Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 22:17:28 +0900 Subject: [PATCH 070/133] show error message for inhomogenous array --- toml/parser.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/toml/parser.hpp b/toml/parser.hpp index c36dd53..97dadee 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -868,6 +868,13 @@ parse_array(location& loc) if(auto val = parse_value(loc)) { + if(!retval.empty() && retval.front().type() != val.as_ok().type()) + { + throw syntax_error(format_underline( + "[error] toml::parse_array: type of elements should be the " + "same each other.", region(loc, first, loc.iter()), + "inhomogenous types")); + } retval.push_back(std::move(val.unwrap())); } else From d7bba10fa3ae691fdfb024f90f12b7b02bd16415 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 22:21:22 +0900 Subject: [PATCH 071/133] improve error message in parse_key_value_pair --- toml/parser.hpp | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 97dadee..f96faba 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -914,17 +914,39 @@ parse_key_value_pair(location& loc) auto key = parse_key(loc); if(!key) { - loc.iter() = first; - return err(key.unwrap_err()); + std::string msg = std::move(key.unwrap_err()); + // if the next token is keyvalue-separator, it means that there are no + // key. then we need to show error as "empty key is not allowed". + if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) + { + loc.iter() = first; + msg = format_underline("[error] toml::parse_key_value_pair: " + "empty key is not allowed.", loc, "key expected before '='"); + } + return err(std::move(msg)); } const auto kvsp = lex_keyval_sep::invoke(loc); if(!kvsp) { - const auto msg = format_underline("[error] toml::parse_key_value_pair: " - "missing key-value separator `=`", loc, "should be `=`"); + std::string msg; + // if the line contains '=' after the invalid sequence, possibly the + // error is in the key (like, invalid character in bare key). + const auto line_end = std::find(loc.iter(), loc.end(), '\n'); + if(std::find(loc.iter(), line_end, '=') != line_end) + { + msg = format_underline("[error] toml::parse_key_value_pair: " + "invalid format for key", loc, "invalid character in key", { + "Did you forget '.' to separate dotted-key?", + "Allowed characters for bare key are [0-9a-zA-Z_-]."}); + } + else // if not, the error is lack of key-value separator. + { + msg = format_underline("[error] toml::parse_key_value_pair: " + "missing key-value separator `=`", loc, "should be `=`"); + } loc.iter() = first; - return err(msg); + return err(std::move(msg)); } auto val = parse_value(loc); From fc6a15440df7d64e7865344ddee6d5d312868326 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 22:22:07 +0900 Subject: [PATCH 072/133] enable format_underline to print hint with region --- toml/region.hpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/toml/region.hpp b/toml/region.hpp index 6dba5ea..b7e4abc 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -198,7 +198,8 @@ struct region final : public region_base // to show a better error message. inline std::string format_underline(const std::string& message, - const region_base& reg, const std::string& comment_for_underline) + const region_base& reg, const std::string& comment_for_underline, + std::vector helps = {}) { const auto line = reg.line(); const auto line_number = reg.line_num(); @@ -218,6 +219,17 @@ inline std::string format_underline(const std::string& message, retval += make_string(reg.size(), '~'); retval += ' '; retval += comment_for_underline; + if(helps.size() != 0) + { + retval += '\n'; + retval += make_string(line_number.size() + 1, ' '); + retval += " | "; + for(const auto help : helps) + { + retval += "\nHint: "; + retval += help; + } + } return retval; } From e79e6150f28f5e63f482b0fa88753e470a82c95e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 23:25:23 +0900 Subject: [PATCH 073/133] update testing macro --- tests/test_parse_aux.hpp | 4 +- tests/test_parse_boolean.cpp | 4 +- tests/test_parse_floating.cpp | 70 +++++++++++++++++------------------ tests/test_parse_integer.cpp | 48 ++++++++++++------------ 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/tests/test_parse_aux.hpp b/tests/test_parse_aux.hpp index 956669a..4ef9541 100644 --- a/tests/test_parse_aux.hpp +++ b/tests/test_parse_aux.hpp @@ -21,11 +21,11 @@ do { \ } while(false); \ /**/ -#define TOML11_TEST_PARSE_EQUAL_VALUE(tkn, expct) \ +#define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \ do { \ const std::string token(tkn); \ toml::detail::location loc("test", token); \ - const auto result = toml::detail::parse_value(loc); \ + const auto result = psr(loc); \ BOOST_CHECK(result.is_ok()); \ if(result.is_ok()){ \ BOOST_CHECK(result.unwrap() == expct); \ diff --git a/tests/test_parse_boolean.cpp b/tests/test_parse_boolean.cpp index 119ffde..5224a93 100644 --- a/tests/test_parse_boolean.cpp +++ b/tests/test_parse_boolean.cpp @@ -19,6 +19,6 @@ BOOST_AUTO_TEST_CASE(test_boolean) BOOST_AUTO_TEST_CASE(test_boolean_value) { - TOML11_TEST_PARSE_EQUAL_VALUE( "true", toml::value( true)); - TOML11_TEST_PARSE_EQUAL_VALUE("false", toml::value(false)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "true", toml::value( true)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "false", toml::value(false)); } diff --git a/tests/test_parse_floating.cpp b/tests/test_parse_floating.cpp index 898dbf6..e98dbe7 100644 --- a/tests/test_parse_floating.cpp +++ b/tests/test_parse_floating.cpp @@ -32,20 +32,20 @@ BOOST_AUTO_TEST_CASE(test_fractional) BOOST_AUTO_TEST_CASE(test_fractional_value) { - TOML11_TEST_PARSE_EQUAL_VALUE("1.0", value( 1.0)); - TOML11_TEST_PARSE_EQUAL_VALUE("0.1", value( 0.1)); - TOML11_TEST_PARSE_EQUAL_VALUE("0.001", value( 0.001)); - TOML11_TEST_PARSE_EQUAL_VALUE("0.100", value( 0.1)); - TOML11_TEST_PARSE_EQUAL_VALUE("+3.14", value( 3.14)); - TOML11_TEST_PARSE_EQUAL_VALUE("-3.14", value(-3.14)); - TOML11_TEST_PARSE_EQUAL_VALUE("3.1415_9265_3589", value( 3.141592653589)); - TOML11_TEST_PARSE_EQUAL_VALUE("+3.1415_9265_3589", value( 3.141592653589)); - TOML11_TEST_PARSE_EQUAL_VALUE("-3.1415_9265_3589", value(-3.141592653589)); - TOML11_TEST_PARSE_EQUAL_VALUE("123_456.789", value( 123456.789)); - TOML11_TEST_PARSE_EQUAL_VALUE("+123_456.789", value( 123456.789)); - TOML11_TEST_PARSE_EQUAL_VALUE("-123_456.789", value(-123456.789)); - TOML11_TEST_PARSE_EQUAL_VALUE("+0.0", value( 0.0)); - TOML11_TEST_PARSE_EQUAL_VALUE("-0.0", value(-0.0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1.0", value( 1.0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.1", value( 0.1)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.001", value( 0.001)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0.100", value( 0.1)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+3.14", value( 3.14)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-3.14", value(-3.14)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "3.1415_9265_3589", value( 3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+3.1415_9265_3589", value( 3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-3.1415_9265_3589", value(-3.141592653589)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123_456.789", value( 123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+123_456.789", value( 123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-123_456.789", value(-123456.789)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+0.0", value( 0.0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-0.0", value(-0.0)); } BOOST_AUTO_TEST_CASE(test_exponential) @@ -72,24 +72,24 @@ BOOST_AUTO_TEST_CASE(test_exponential) BOOST_AUTO_TEST_CASE(test_exponential_value) { - TOML11_TEST_PARSE_EQUAL_VALUE("1e10", value(1e10)); - TOML11_TEST_PARSE_EQUAL_VALUE("1e+10", value(1e10)); - TOML11_TEST_PARSE_EQUAL_VALUE("1e-10", value(1e-10)); - TOML11_TEST_PARSE_EQUAL_VALUE("+1e10", value(1e10)); - TOML11_TEST_PARSE_EQUAL_VALUE("+1e+10", value(1e10)); - TOML11_TEST_PARSE_EQUAL_VALUE("+1e-10", value(1e-10)); - TOML11_TEST_PARSE_EQUAL_VALUE("-1e10", value(-1e10)); - TOML11_TEST_PARSE_EQUAL_VALUE("-1e+10", value(-1e10)); - TOML11_TEST_PARSE_EQUAL_VALUE("-1e-10", value(-1e-10)); - TOML11_TEST_PARSE_EQUAL_VALUE("123e-10", value(123e-10)); - TOML11_TEST_PARSE_EQUAL_VALUE("1E10", value(1e10)); - TOML11_TEST_PARSE_EQUAL_VALUE("1E+10", value(1e10)); - TOML11_TEST_PARSE_EQUAL_VALUE("1E-10", value(1e-10)); - TOML11_TEST_PARSE_EQUAL_VALUE("123E-10", value(123e-10)); - TOML11_TEST_PARSE_EQUAL_VALUE("1_2_3E-10", value(123e-10)); - TOML11_TEST_PARSE_EQUAL_VALUE("1_2_3E-1_0", value(123e-10)); - TOML11_TEST_PARSE_EQUAL_VALUE("+0e0", value( 0.0)); - TOML11_TEST_PARSE_EQUAL_VALUE("-0e0", value(-0.0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1e-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1e-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e10", value(-1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e+10", value(-1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1e-10", value(-1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123e-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E+10", value(1e10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1E-10", value(1e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123E-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3E-10", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3E-1_0", value(123e-10)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+0e0", value( 0.0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-0e0", value(-0.0)); } BOOST_AUTO_TEST_CASE(test_fe) { @@ -99,9 +99,9 @@ BOOST_AUTO_TEST_CASE(test_fe) } BOOST_AUTO_TEST_CASE(test_fe_vaule) { - TOML11_TEST_PARSE_EQUAL_VALUE("6.02e23", value(6.02e23)); - TOML11_TEST_PARSE_EQUAL_VALUE("6.02e+23", value(6.02e23)); - TOML11_TEST_PARSE_EQUAL_VALUE("1.112_650_06e-17", value(1.11265006e-17)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "6.02e23", value(6.02e23)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "6.02e+23", value(6.02e23)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1.112_650_06e-17", value(1.11265006e-17)); } BOOST_AUTO_TEST_CASE(test_inf) diff --git a/tests/test_parse_integer.cpp b/tests/test_parse_integer.cpp index e1d1f12..6b5c515 100644 --- a/tests/test_parse_integer.cpp +++ b/tests/test_parse_integer.cpp @@ -25,14 +25,14 @@ BOOST_AUTO_TEST_CASE(test_decimal) BOOST_AUTO_TEST_CASE(test_decimal_value) { - TOML11_TEST_PARSE_EQUAL_VALUE( "1234", toml::value( 1234)); - TOML11_TEST_PARSE_EQUAL_VALUE( "+1234", toml::value( 1234)); - TOML11_TEST_PARSE_EQUAL_VALUE( "-1234", toml::value( -1234)); - TOML11_TEST_PARSE_EQUAL_VALUE( "0", toml::value( 0)); - TOML11_TEST_PARSE_EQUAL_VALUE( "1_2_3_4", toml::value( 1234)); - TOML11_TEST_PARSE_EQUAL_VALUE( "+1_2_3_4", toml::value( +1234)); - TOML11_TEST_PARSE_EQUAL_VALUE( "-1_2_3_4", toml::value( -1234)); - TOML11_TEST_PARSE_EQUAL_VALUE("123_456_789", toml::value(123456789)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1234", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1234", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1234", toml::value( -1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0", toml::value( 0)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1_2_3_4", toml::value( 1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "+1_2_3_4", toml::value( +1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "-1_2_3_4", toml::value( -1234)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "123_456_789", toml::value(123456789)); } BOOST_AUTO_TEST_CASE(test_hex) @@ -50,15 +50,15 @@ BOOST_AUTO_TEST_CASE(test_hex) BOOST_AUTO_TEST_CASE(test_hex_value) { - TOML11_TEST_PARSE_EQUAL_VALUE("0xDEADBEEF", value(0xDEADBEEF)); - TOML11_TEST_PARSE_EQUAL_VALUE("0xdeadbeef", value(0xDEADBEEF)); - TOML11_TEST_PARSE_EQUAL_VALUE("0xDEADbeef", value(0xDEADBEEF)); - TOML11_TEST_PARSE_EQUAL_VALUE("0xDEAD_BEEF", value(0xDEADBEEF)); - TOML11_TEST_PARSE_EQUAL_VALUE("0xdead_beef", value(0xDEADBEEF)); - TOML11_TEST_PARSE_EQUAL_VALUE("0xdead_BEEF", value(0xDEADBEEF)); - TOML11_TEST_PARSE_EQUAL_VALUE("0xFF", value(0xFF)); - TOML11_TEST_PARSE_EQUAL_VALUE("0x00FF", value(0xFF)); - TOML11_TEST_PARSE_EQUAL_VALUE("0x0000FF", value(0xFF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEADBEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdeadbeef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEADbeef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xDEAD_BEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdead_beef", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xdead_BEEF", value(0xDEADBEEF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0xFF", value(0xFF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0x00FF", value(0xFF)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0x0000FF", value(0xFF)); } BOOST_AUTO_TEST_CASE(test_oct) @@ -70,9 +70,9 @@ BOOST_AUTO_TEST_CASE(test_oct) BOOST_AUTO_TEST_CASE(test_oct_value) { - TOML11_TEST_PARSE_EQUAL_VALUE("0o777", value(64*7+8*7+7)); - TOML11_TEST_PARSE_EQUAL_VALUE("0o7_7_7", value(64*7+8*7+7)); - TOML11_TEST_PARSE_EQUAL_VALUE("0o007", value(7)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o777", value(64*7+8*7+7)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o7_7_7", value(64*7+8*7+7)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0o007", value(7)); } BOOST_AUTO_TEST_CASE(test_bin) @@ -85,8 +85,8 @@ BOOST_AUTO_TEST_CASE(test_bin) BOOST_AUTO_TEST_CASE(test_bin_value) { - TOML11_TEST_PARSE_EQUAL_VALUE("0b10000", value(16)); - TOML11_TEST_PARSE_EQUAL_VALUE("0b010000", value(16)); - TOML11_TEST_PARSE_EQUAL_VALUE("0b01_00_00", value(16)); - TOML11_TEST_PARSE_EQUAL_VALUE("0b111111", value(63)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b10000", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b010000", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b01_00_00", value(16)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "0b111111", value(63)); } From be8600abfa1e653aa2e64f3951b9d83306d655e8 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 23:25:44 +0900 Subject: [PATCH 074/133] add test for key and string --- tests/CMakeLists.txt | 2 + tests/test_parse_key.cpp | 63 ++++++++++++ tests/test_parse_string.cpp | 188 ++++++++++++++++++++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 tests/test_parse_key.cpp create mode 100644 tests/test_parse_string.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7234860..453b9b2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,8 @@ set(TEST_NAMES test_parse_boolean test_parse_integer test_parse_floating + test_parse_string + test_parse_key # test_to_toml # test_from_toml diff --git a/tests/test_parse_key.cpp b/tests/test_parse_key.cpp new file mode 100644 index 0000000..b7fcf5b --- /dev/null +++ b/tests/test_parse_key.cpp @@ -0,0 +1,63 @@ +#define BOOST_TEST_MODULE "parse_key_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_bare_key) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "barekey", std::vector(1, "barekey")); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare-key", std::vector(1, "bare-key")); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "bare_key", std::vector(1, "bare_key")); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "1234", std::vector(1, "1234")); +} + +BOOST_AUTO_TEST_CASE(test_quoted_key) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"127.0.0.1\"", std::vector(1, "127.0.0.1" )); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"character encoding\"", std::vector(1, "character encoding")); +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"\xCA\x8E\xC7\x9D\xCA\x9E\"", std::vector(1, "\xCA\x8E\xC7\x9D\xCA\x9E")); +#else + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "\"ʎǝʞ\"", std::vector(1, "ʎǝʞ" )); +#endif + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'key2'", std::vector(1, "key2" )); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "'quoted \"value\"'", std::vector(1, "quoted \"value\"" )); +} + +BOOST_AUTO_TEST_CASE(test_dotted_key) +{ + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "color"; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.color", keys); + } + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "shape"; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "physical.shape", keys); + } + { + std::vector keys(4); + keys[0] = "x"; + keys[1] = "y"; + keys[2] = "z"; + keys[3] = "w"; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "x.y.z.w", keys); + } + { + std::vector keys(2); + keys[0] = "site"; + keys[1] = "google.com"; + TOML11_TEST_PARSE_EQUAL_VALUE(parse_key, "site.\"google.com\"", keys); + } +} diff --git a/tests/test_parse_string.cpp b/tests/test_parse_string.cpp new file mode 100644 index 0000000..00cc31a --- /dev/null +++ b/tests/test_parse_string.cpp @@ -0,0 +1,188 @@ +#define BOOST_TEST_MODULE "parse_string_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"The quick brown fox jumps over the lazy dog\"", + string("The quick brown fox jumps over the lazy dog", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\'The quick brown fox jumps over the lazy dog\'", + string("The quick brown fox jumps over the lazy dog", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", + string("The quick brown fox jumps over the lazy dog", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'''The quick brown fox \njumps over the lazy dog'''", + string("The quick brown fox \njumps over the lazy dog", string_t::literal)); +} + +BOOST_AUTO_TEST_CASE(test_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"The quick brown fox jumps over the lazy dog\"", + toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\'The quick brown fox jumps over the lazy dog\'", + toml::value("The quick brown fox jumps over the lazy dog", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"\"\"The quick brown fox \\\njumps over the lazy dog\"\"\"", + toml::value("The quick brown fox jumps over the lazy dog", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'''The quick brown fox \njumps over the lazy dog'''", + toml::value("The quick brown fox \njumps over the lazy dog", string_t::literal)); +} + + +BOOST_AUTO_TEST_CASE(test_basic_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", + string("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"192.168.1.1\"", + string("192.168.1.1", string_t::basic)); + +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", + string("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); +#else + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"中国\"", + string("中国", string_t::basic)); +#endif + + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"You'll hate me after this - #\"", + string("You'll hate me after this - #", string_t::basic)); + 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)); +} + +BOOST_AUTO_TEST_CASE(test_basic_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"GitHub Cofounder & CEO\\nLikes tater tots and beer.\"", + value("GitHub Cofounder & CEO\nLikes tater tots and beer.", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"192.168.1.1\"", + value("192.168.1.1", string_t::basic)); +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, , + "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", + value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); +#else + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"中国\"", + value("中国", string_t::basic)); +#endif + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"You'll hate me after this - #\"", + value("You'll hate me after this - #", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\" And when \\\"'s are in the along with # \\\"\"", + value(" And when \"'s are in the along with # \"", string_t::basic)); +} + +BOOST_AUTO_TEST_CASE(test_ml_basic_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", + string("The quick brown fox jumps over the lazy dog.", string_t::basic)); + 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)); +} + +BOOST_AUTO_TEST_CASE(test_ml_basic_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\"", + value("The quick brown fox jumps over the lazy dog.", string_t::basic)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_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)); +} + +BOOST_AUTO_TEST_CASE(test_literal_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "'C:\\Users\\nodejs\\templates'", + string("C:\\Users\\nodejs\\templates", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'\\\\ServerX\\admin$\\system32\\'", + string("\\\\ServerX\\admin$\\system32\\", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'Tom \"Dubs\" Preston-Werner'", + string("Tom \"Dubs\" Preston-Werner", string_t::literal)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "'<\\i\\c*\\s*>'", + string("<\\i\\c*\\s*>", string_t::literal)); +} + +BOOST_AUTO_TEST_CASE(test_literal_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'C:\\Users\\nodejs\\templates'", + value("C:\\Users\\nodejs\\templates", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'\\\\ServerX\\admin$\\system32\\'", + value("\\\\ServerX\\admin$\\system32\\", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'Tom \"Dubs\" Preston-Werner'", + value("Tom \"Dubs\" Preston-Werner", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'<\\i\\c*\\s*>'", + value("<\\i\\c*\\s*>", string_t::literal)); +} + +BOOST_AUTO_TEST_CASE(test_ml_literal_string) +{ + TOML11_TEST_PARSE_EQUAL(parse_string, + "'''I [dw]on't need \\d{2} apples'''", + string("I [dw]on't need \\d{2} apples", string_t::literal)); + 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)); +} + +BOOST_AUTO_TEST_CASE(test_ml_literal_string_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, + "'''I [dw]on't need \\d{2} apples'''", + value("I [dw]on't need \\d{2} apples", string_t::literal)); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_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)); +} + +BOOST_AUTO_TEST_CASE(test_unicode_escape_sequence) +{ +#if defined(_MSC_VER) || defined(__INTEL_COMPILER) + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\\u03B1\\u03B2\\u03B3\"", + string("\xCE\xB1\xCE\xB2\xCE\xB3", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\\U0001D7AA\"", + string("\xF0\x9D\x9E\xAA", string_t::basic)); +#else + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\\u03B1\\u03B2\\u03B3\"", + string("αβγ", string_t::basic)); + TOML11_TEST_PARSE_EQUAL(parse_string, + "\"\\U0001D7AA\"", + string("𝞪", string_t::basic)); +#endif +} From fc3471434f31cf1a26b4be5f34cc3dff187d20f5 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 23:31:24 +0900 Subject: [PATCH 075/133] add test for array --- tests/CMakeLists.txt | 1 + tests/test_parse_array.cpp | 130 +++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 tests/test_parse_array.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 453b9b2..28bb812 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,7 @@ set(TEST_NAMES test_parse_integer test_parse_floating test_parse_string + test_parse_array test_parse_key # test_to_toml diff --git a/tests/test_parse_array.cpp b/tests/test_parse_array.cpp new file mode 100644 index 0000000..6350c3a --- /dev/null +++ b/tests/test_parse_array.cpp @@ -0,0 +1,130 @@ +#define BOOST_TEST_MODULE "parse_array_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_oneline_array) +{ + TOML11_TEST_PARSE_EQUAL(parse_array, "[]", array()); + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL(parse_array, "[3,1,4,1,5]", a); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\", \"bar\", \"baz\"]", a); + } + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL(parse_array, "[3,1,4,1,5,]", a); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\", \"bar\", \"baz\",]", a); + } +} + +BOOST_AUTO_TEST_CASE(test_oneline_array_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[]", toml::value(array())); + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,1,4,1,5]", toml::value(a)); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", \"bar\", \"baz\"]", toml::value(a)); + } + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,1,4,1,5,]", toml::value(a)); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\", \"bar\", \"baz\",]", toml::value(a)); + } +} + +BOOST_AUTO_TEST_CASE(test_multiline_array) +{ + TOML11_TEST_PARSE_EQUAL(parse_array, "[\n#comment\n]", array()); + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL(parse_array, "[3,\n1,\n4,\n1,\n5]", a); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\",\n\"bar\",\n\"baz\"]", a); + } + + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL(parse_array, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5]", a); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("b#r"); + a[2] = toml::value("b#z"); + TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); + } +} + +BOOST_AUTO_TEST_CASE(test_multiline_array_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\n#comment\n]", toml::value(array())); + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,\n1,\n4,\n1,\n5]", toml::value(a)); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("bar"); + a[2] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",\n\"bar\",\n\"baz\"]", toml::value(a)); + } + + { + array a(5); + a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); + a[3] = toml::value(1); a[4] = toml::value(5); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5]", toml::value(a)); + } + { + array a(3); + a[0] = toml::value("foo"); a[1] = toml::value("b#r"); + a[2] = toml::value("b#z"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", toml::value(a)); + } +} From e810c3d35edac1a8ffc5c6c95cbbd6fafde5e90b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 23:33:32 +0900 Subject: [PATCH 076/133] add tests for equivalent operator --- tests/test_value.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_value.cpp b/tests/test_value.cpp index 29f8cd6..702fa8d 100644 --- a/tests/test_value.cpp +++ b/tests/test_value.cpp @@ -39,6 +39,8 @@ BOOST_AUTO_TEST_CASE(test_value_boolean) toml::value v3(v1); toml::value v4(v2); + BOOST_CHECK(v3 == v1); + BOOST_CHECK(v4 == v2); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Boolean); BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Boolean); @@ -107,6 +109,8 @@ BOOST_AUTO_TEST_CASE(test_value_integer) toml::value v3(v1); toml::value v4(v2); + BOOST_CHECK(v3 == v1); + BOOST_CHECK(v4 == v2); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Integer); BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Integer); @@ -175,6 +179,8 @@ BOOST_AUTO_TEST_CASE(test_value_float) toml::value v3(v1); toml::value v4(v2); + BOOST_CHECK(v3 == v1); + BOOST_CHECK(v4 == v2); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); BOOST_CHECK_EQUAL(v4.type(), toml::value_t::Float); @@ -254,6 +260,9 @@ BOOST_AUTO_TEST_CASE(test_value_string) toml::value v4(v1); toml::value v5(v2); toml::value v6(v3); + BOOST_CHECK(v4 == v1); + BOOST_CHECK(v5 == v2); + BOOST_CHECK(v6 == v3); BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); BOOST_CHECK_EQUAL(v5.type(), toml::value_t::String); @@ -327,6 +336,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_date) toml::local_date(2018, toml::month_t::Apr, 1)); toml::value v2(v1); + BOOST_CHECK(v2 == v1); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDate); BOOST_CHECK(v2.is(toml::value_t::LocalDate)); @@ -371,6 +381,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_time) toml::local_time(1, 30, 0, 100, 0)); toml::value v3(v1); + BOOST_CHECK(v3 == v1); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::LocalTime); BOOST_CHECK(v3.is(toml::value_t::LocalTime)); @@ -416,6 +427,7 @@ BOOST_AUTO_TEST_CASE(test_value_local_datetime) toml::local_time(1, 15, 30))); toml::value v2(v1); + BOOST_CHECK(v2 == v1); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalDatetime); BOOST_CHECK(v2.is(toml::value_t::LocalDatetime)); @@ -468,6 +480,7 @@ BOOST_AUTO_TEST_CASE(test_value_offset_datetime) toml::time_offset(9, 0))); toml::value v2(v1); + BOOST_CHECK(v2 == v1); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::OffsetDatetime); BOOST_CHECK(v2.is(toml::value_t::OffsetDatetime)); @@ -535,6 +548,7 @@ BOOST_AUTO_TEST_CASE(test_value_array) BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 5); toml::value v3(v1); + BOOST_CHECK(v3 == v1); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Array); BOOST_CHECK(v3.is(toml::value_t::Array)); @@ -576,6 +590,7 @@ BOOST_AUTO_TEST_CASE(test_value_table) BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "quux"); toml::value v3(v1); + BOOST_CHECK(v3 == v1); BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Table); BOOST_CHECK(v3.is(toml::value_t::Table)); From 4d64c0d8af1f5a4d76a462b3c384843524a5fd09 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Tue, 11 Dec 2018 23:38:57 +0900 Subject: [PATCH 077/133] add tests for inline table --- tests/CMakeLists.txt | 1 + tests/test_parse_inline_table.cpp | 48 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/test_parse_inline_table.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 28bb812..d9bee1f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,6 +15,7 @@ set(TEST_NAMES test_parse_floating test_parse_string test_parse_array + test_parse_inline_table test_parse_key # test_to_toml diff --git a/tests/test_parse_inline_table.cpp b/tests/test_parse_inline_table.cpp new file mode 100644 index 0000000..57045bd --- /dev/null +++ b/tests/test_parse_inline_table.cpp @@ -0,0 +1,48 @@ +#define BOOST_TEST_MODULE "parse_inline_table_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_inline_table) +{ + TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{}", table()); + { + table t; + t["foo"] = toml::value(42); + t["bar"] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{foo = 42, bar = \"baz\"}", t); + } + { + table t; + table t_sub; + t_sub["name"] = toml::value("pug"); + t["type"] = toml::value(t_sub); + TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{type.name = \"pug\"}", t); + } +} + +BOOST_AUTO_TEST_CASE(test_inline_table_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{}", value(table())); + { + table t; + t["foo"] = toml::value(42); + t["bar"] = toml::value("baz"); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{foo = 42, bar = \"baz\"}", value(t)); + } + { + table t; + table t_sub; + t_sub["name"] = toml::value("pug"); + t["type"] = toml::value(t_sub); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "{type.name = \"pug\"}", value(t)); + } +} From 9f8e86524a31235006492c7add9a4b5ad99bd51a Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 01:26:56 +0900 Subject: [PATCH 078/133] add a lot of dotted keys to test_lex --- tests/test_lex_key_comment.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_lex_key_comment.cpp b/tests/test_lex_key_comment.cpp index 2598c64..be4b176 100644 --- a/tests/test_lex_key_comment.cpp +++ b/tests/test_lex_key_comment.cpp @@ -32,7 +32,15 @@ BOOST_AUTO_TEST_CASE(test_dotted_key) { TOML11_TEST_LEX_ACCEPT(lex_key, "physical.color", "physical.color"); TOML11_TEST_LEX_ACCEPT(lex_key, "physical.shape", "physical.shape"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x.y", "x.y"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x . y", "x . y"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x.y.z", "x.y.z"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x. y .z", "x. y .z"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x .y. z", "x .y. z"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x . y . z", "x . y . z"); TOML11_TEST_LEX_ACCEPT(lex_key, "x.y.z.w", "x.y.z.w"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x. y .z. w", "x. y .z. w"); + TOML11_TEST_LEX_ACCEPT(lex_key, "x . y . z . w", "x . y . z . w"); TOML11_TEST_LEX_ACCEPT(lex_key, "site.\"google.com\"", "site.\"google.com\""); } From 765ab97d8b004c17a3acc7c2f7b8f4b2bec58b20 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 01:27:10 +0900 Subject: [PATCH 079/133] add whitespace between [] and key [ a.b.c ] is allowed. also, [[ a . b ]] is allowed. dotted key matches `a.b.c` only, so the explicit whitespace is needed. --- toml/lexer.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/toml/lexer.hpp b/toml/lexer.hpp index c025d81..408d087 100644 --- a/toml/lexer.hpp +++ b/toml/lexer.hpp @@ -188,7 +188,8 @@ using lex_quoted_key = either; using lex_simple_key = either; using lex_dotted_key = sequence, - at_least<1>> + at_least<1> + > >; using lex_key = either; @@ -199,13 +200,17 @@ using lex_keyval_sep = sequence, using lex_std_table_open = character<'['>; using lex_std_table_close = character<']'>; using lex_std_table = sequence, lex_key, + maybe, lex_std_table_close>; using lex_array_table_open = sequence; using lex_array_table_close = sequence; using lex_array_table = sequence, lex_key, + maybe, lex_array_table_close>; } // detail From a1a81089c519fe4f33d2eb5f08f3ac9f6a8e5667 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 01:30:47 +0900 Subject: [PATCH 080/133] skip whitespace before/inside/after dotted-keys --- toml/parser.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index f96faba..fd06ece 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -784,7 +784,7 @@ template result, std::string> parse_key(location& loc) { const auto first = loc.iter(); - // dotted key -> foo.bar.baz + // dotted key -> foo.bar.baz whitespaces are allowed if(const auto token = lex_dotted_key::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); @@ -792,6 +792,7 @@ result, std::string> parse_key(location& loc) while(inner_loc.iter() != inner_loc.end()) { + lex_ws::invoke(inner_loc); if(const auto k = parse_simple_key(inner_loc)) { keys.push_back(k.unwrap()); @@ -803,6 +804,7 @@ result, std::string> parse_key(location& loc) inner_loc, k.unwrap_err())); } + lex_ws::invoke(inner_loc); if(inner_loc.iter() == inner_loc.end()) { break; @@ -1173,12 +1175,18 @@ parse_table_key(location& loc) throw internal_error(format_underline("[error] " "toml::parse_table_key: no `[`", inner_loc, "should be `[`")); } + // to skip [ a . b . c ] + // ^----------- this whitespace + lex_ws::invoke(inner_loc); const auto keys = parse_key(inner_loc); if(!keys) { throw internal_error(format_underline("[error] " "toml::parse_table_key: invalid key", inner_loc, "not key")); } + // to skip [ a . b . c ] + // ^-- this whitespace + lex_ws::invoke(inner_loc); const auto close = lex_std_table_close::invoke(inner_loc); if(!close) { From d3e88b3082897eab27272fa185b8bca4ea0b1991 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 01:31:36 +0900 Subject: [PATCH 081/133] add test case for table key --- tests/CMakeLists.txt | 1 + tests/test_parse_table_key.cpp | 111 +++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tests/test_parse_table_key.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d9bee1f..0c6c6b8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,6 +17,7 @@ set(TEST_NAMES test_parse_array test_parse_inline_table test_parse_key + test_parse_table_key # test_to_toml # test_from_toml diff --git a/tests/test_parse_table_key.cpp b/tests/test_parse_table_key.cpp new file mode 100644 index 0000000..fe91e71 --- /dev/null +++ b/tests/test_parse_table_key.cpp @@ -0,0 +1,111 @@ +#define BOOST_TEST_MODULE "parse_table_key_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_table_bare_key) +{ + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[barekey]", std::vector(1, "barekey")); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[bare-key]", std::vector(1, "bare-key")); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[bare_key]", std::vector(1, "bare_key")); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[1234]", std::vector(1, "1234")); +} + +BOOST_AUTO_TEST_CASE(test_table_quoted_key) +{ + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"127.0.0.1\"]", std::vector(1, "127.0.0.1" )); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"character encoding\"]", std::vector(1, "character encoding")); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[\"ʎǝʞ\"]", std::vector(1, "ʎǝʞ" )); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "['key2']", std::vector(1, "key2" )); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "['quoted \"value\"']", std::vector(1, "quoted \"value\"" )); +} + +BOOST_AUTO_TEST_CASE(test_table_dotted_key) +{ + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "color"; + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[physical.color]", keys); + } + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "shape"; + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[physical.shape]", keys); + } + { + std::vector keys(4); + keys[0] = "x"; + keys[1] = "y"; + keys[2] = "z"; + keys[3] = "w"; + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x.y.z.w]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x . y . z . w]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x. y .z. w]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[x .y. z .w]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[ x. y .z . w ]", keys); + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[ x . y . z . w ]", keys); + } + { + std::vector keys(2); + keys[0] = "site"; + keys[1] = "google.com"; + TOML11_TEST_PARSE_EQUAL(parse_table_key, "[site.\"google.com\"]", keys); + } +} + +BOOST_AUTO_TEST_CASE(test_array_of_table_bare_key) +{ + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[barekey]]", std::vector(1, "barekey")); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[bare-key]]", std::vector(1, "bare-key")); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[bare_key]]", std::vector(1, "bare_key")); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[1234]]", std::vector(1, "1234")); +} + +BOOST_AUTO_TEST_CASE(test_array_of_table_quoted_key) +{ + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"127.0.0.1\"]]", std::vector(1, "127.0.0.1" )); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"character encoding\"]]", std::vector(1, "character encoding")); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[\"ʎǝʞ\"]]", std::vector(1, "ʎǝʞ" )); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[['key2']]", std::vector(1, "key2" )); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[['quoted \"value\"']]", std::vector(1, "quoted \"value\"" )); +} + +BOOST_AUTO_TEST_CASE(test_array_of_table_dotted_key) +{ + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "color"; + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[physical.color]]", keys); + } + { + std::vector keys(2); + keys[0] = "physical"; + keys[1] = "shape"; + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[physical.shape]]", keys); + } + { + std::vector keys(4); + keys[0] = "x"; + keys[1] = "y"; + keys[2] = "z"; + keys[3] = "w"; + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x.y.z.w]]", keys); + } + { + std::vector keys(2); + keys[0] = "site"; + keys[1] = "google.com"; + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[site.\"google.com\"]]", keys); + } +} From b63dc1f3704c2ae77e785bb5e580f0a3df482239 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 01:36:20 +0900 Subject: [PATCH 082/133] remove extra comma --- tests/test_parse_string.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_parse_string.cpp b/tests/test_parse_string.cpp index 00cc31a..60f9991 100644 --- a/tests/test_parse_string.cpp +++ b/tests/test_parse_string.cpp @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(test_basic_string_value) "\"192.168.1.1\"", value("192.168.1.1", string_t::basic)); #if defined(_MSC_VER) || defined(__INTEL_COMPILER) - TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, , + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "\"\xE4\xB8\xAD\xE5\x9B\xBD\"", value("\xE4\xB8\xAD\xE5\x9B\xBD", string_t::basic)); #else From 5dea88001e6d9defe1ca10e32d3f6782f2b96e3d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 12:13:49 +0900 Subject: [PATCH 083/133] add test case for array-of-tables --- tests/test_parse_table_key.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_parse_table_key.cpp b/tests/test_parse_table_key.cpp index fe91e71..47df434 100644 --- a/tests/test_parse_table_key.cpp +++ b/tests/test_parse_table_key.cpp @@ -101,6 +101,12 @@ BOOST_AUTO_TEST_CASE(test_array_of_table_dotted_key) keys[2] = "z"; keys[3] = "w"; TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x.y.z.w]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x . y . z . w]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x. y .z. w]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[x .y. z .w]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[ x. y .z . w ]]", keys); + TOML11_TEST_PARSE_EQUAL(parse_array_table_key, "[[ x . y . z . w ]]", keys); + } { std::vector keys(2); From bcaf5baf887382e73d33fce307843fd626dce37e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 12:14:11 +0900 Subject: [PATCH 084/133] fix parse_array_of_table_key allow whitespace before and after [[ and ]] (like, [[ a.b ]]) --- toml/parser.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/toml/parser.hpp b/toml/parser.hpp index fd06ece..8cfb884 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1216,6 +1216,7 @@ parse_array_table_key(location& loc) "toml::parse_array_table_key: no `[[`", inner_loc, "should be `[[`")); } + lex_ws::invoke(inner_loc); const auto keys = parse_key(inner_loc); if(!keys) { @@ -1223,6 +1224,7 @@ parse_array_table_key(location& loc) "toml::parse_array_table_key: invalid key", inner_loc, "not key")); } + lex_ws::invoke(inner_loc); const auto close = lex_array_table_close::invoke(inner_loc); if(!close) { From 0c9b7859693eeb527ae7c53638d4e60521ec8382 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 16:11:37 +0900 Subject: [PATCH 085/133] add missing include file --- toml/get.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/toml/get.hpp b/toml/get.hpp index 056da64..ed355ac 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -1,5 +1,6 @@ #ifndef TOML11_GET #define TOML11_GET +#include "result.hpp" #include "value.hpp" #include From fcbfbd3a26ee96731586978f0c923a199148058c Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 16:12:10 +0900 Subject: [PATCH 086/133] supress warning about comparison between signed and unsigned --- tests/test_lex_aux.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_lex_aux.hpp b/tests/test_lex_aux.hpp index 8ee18e8..3778f27 100644 --- a/tests/test_lex_aux.hpp +++ b/tests/test_lex_aux.hpp @@ -15,7 +15,8 @@ do { \ const auto region = result.unwrap(); \ BOOST_CHECK_EQUAL(region.str(), expected); \ BOOST_CHECK_EQUAL(region.str().size(), expected.size()); \ - BOOST_CHECK_EQUAL(std::distance(loc.begin(), loc.iter()), region.size()); \ + BOOST_CHECK_EQUAL(static_cast(std::distance( \ + loc.begin(), loc.iter())), region.size()); \ } else { \ std::cerr << "lexer " << lxr::pattern() << " failed with input `"; \ std::cerr << token << "`. expected `" << expected << "`\n"; \ From dc060ba8404feeee526ca164510ac1c6d6bc4e16 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 17:22:41 +0900 Subject: [PATCH 087/133] add explicit std::move to toml::get(&&) --- toml/get.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index ed355ac..a27947e 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -26,9 +26,9 @@ inline T const& get(const value& v) template::value, std::nullptr_t>::type = nullptr> -inline T && get(value&& v) +inline T&& get(value&& v) { - return v.cast::value>(); + return std::move(v.cast::value>()); } // ============================================================================ From 5a20d55dd0d37d7e81e2a50af1fd2761883585ec Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 17:23:06 +0900 Subject: [PATCH 088/133] add test for toml::get --- tests/CMakeLists.txt | 2 +- tests/test_get.cpp | 318 +++++++++++++++++++++++++++++++------------ 2 files changed, 230 insertions(+), 90 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0c6c6b8..977382e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,10 +18,10 @@ set(TEST_NAMES test_parse_inline_table test_parse_key test_parse_table_key + test_get # test_to_toml # test_from_toml -# test_get # test_get_or # test_parser # test_parse_file diff --git a/tests/test_get.cpp b/tests/test_get.cpp index 728efab..56d0929 100644 --- a/tests/test_get.cpp +++ b/tests/test_get.cpp @@ -5,7 +5,8 @@ #define BOOST_TEST_NO_LIB #include #endif -#include +#include +#include #include #include #include @@ -15,98 +16,237 @@ BOOST_AUTO_TEST_CASE(test_get_exact) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); + { + toml::value v(true); + BOOST_CHECK_EQUAL(true, toml::get(v)); - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v6(a); - toml::value v7(t); + toml::get(v) = false; + BOOST_CHECK_EQUAL(false, toml::get(v)); + } + { + toml::value v(42); + BOOST_CHECK_EQUAL(toml::integer(42), toml::get(v)); - toml::Boolean u1 = toml::get(v1); - toml::Integer u2 = toml::get(v2); - toml::Float u3 = toml::get(v3); - toml::String u4 = toml::get(v4); - toml::Array u6 = toml::get(v6); - toml::Table u7 = toml::get(v7); + toml::get(v) = 54; + BOOST_CHECK_EQUAL(toml::integer(54), toml::get(v)); + } + { + toml::value v(3.14); + BOOST_CHECK_EQUAL(toml::floating(3.14), toml::get(v)); - BOOST_CHECK_EQUAL(u1, b); - BOOST_CHECK_EQUAL(u2, i); - BOOST_CHECK_EQUAL(u3, f); - BOOST_CHECK_EQUAL(u4, s); - BOOST_CHECK_EQUAL(u6.at(0).cast(), a.at(0).cast()); - BOOST_CHECK_EQUAL(u6.at(1).cast(), a.at(1).cast()); - BOOST_CHECK_EQUAL(u6.at(2).cast(), a.at(2).cast()); - BOOST_CHECK_EQUAL(u6.at(3).cast(), a.at(3).cast()); - BOOST_CHECK_EQUAL(u6.at(4).cast(), a.at(4).cast()); - BOOST_CHECK_EQUAL(u7.at("val1").cast(), true); - BOOST_CHECK_EQUAL(u7.at("val2").cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(u7.at("val3").cast(),3.14, 1e-3); - BOOST_CHECK_EQUAL(u7.at("val4").cast(), "piyo"); + toml::get(v) = 2.71; + BOOST_CHECK_EQUAL(toml::floating(2.71), toml::get(v)); + } + { + toml::value v("foo"); + BOOST_CHECK_EQUAL(toml::string("foo", toml::string_t::basic), + toml::get(v)); + + toml::get(v).str += "bar"; + BOOST_CHECK_EQUAL(toml::string("foobar", toml::string_t::basic), + toml::get(v)); + } + { + toml::value v("foo", toml::string_t::literal); + BOOST_CHECK_EQUAL(toml::string("foo", toml::string_t::literal), + toml::get(v)); + + toml::get(v).str += "bar"; + BOOST_CHECK_EQUAL(toml::string("foobar", toml::string_t::literal), + toml::get(v)); + } + { + toml::local_date d(2018, toml::month_t::Apr, 22); + toml::value v(d); + BOOST_CHECK(d == toml::get(v)); + + toml::get(v).year = 2017; + d.year = 2017; + BOOST_CHECK(d == toml::get(v)); + } + { + toml::local_time t(12, 30, 45); + toml::value v(t); + BOOST_CHECK(t == toml::get(v)); + + toml::get(v).hour = 9; + t.hour = 9; + BOOST_CHECK(t == toml::get(v)); + } + { + toml::local_datetime dt(toml::local_date(2018, toml::month_t::Apr, 22), + toml::local_time(12, 30, 45)); + toml::value v(dt); + BOOST_CHECK(dt == toml::get(v)); + + toml::get(v).date.year = 2017; + dt.date.year = 2017; + BOOST_CHECK(dt == toml::get(v)); + } + { + toml::offset_datetime dt(toml::local_datetime( + toml::local_date(2018, toml::month_t::Apr, 22), + toml::local_time(12, 30, 45)), toml::time_offset(9, 0)); + toml::value v(dt); + BOOST_CHECK(dt == toml::get(v)); + + toml::get(v).date.year = 2017; + dt.date.year = 2017; + BOOST_CHECK(dt == toml::get(v)); + } + { + toml::array vec; + vec.push_back(toml::value(42)); + vec.push_back(toml::value(54)); + toml::value v(vec); + BOOST_CHECK(vec == toml::get(v)); + + toml::get(v).push_back(toml::value(123)); + vec.push_back(toml::value(123)); + BOOST_CHECK(vec == toml::get(v)); + } + { + toml::table tab; + tab["key1"] = toml::value(42); + tab["key2"] = toml::value(3.14); + toml::value v(tab); + BOOST_CHECK(tab == toml::get(v)); + + toml::get(v)["key3"] = toml::value(123); + tab["key3"] = toml::value(123); + BOOST_CHECK(tab == toml::get(v)); + } } - -BOOST_AUTO_TEST_CASE(test_get_cast) +BOOST_AUTO_TEST_CASE(test_get_integer_type) { - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); - - const auto u2 = toml::get(v2); - const auto u3 = toml::get(v3); - const auto u4 = toml::get>(v6); - const auto u5 = toml::get >(v6); - const auto u6 = toml::get>(v6); - std::map u7 = toml::get>(v7); - - std::deque r4{2,7,1,8,2}; - std::list r5{2,7,1,8,2}; - std::array r6{{2,7,1,8,2}}; - - BOOST_CHECK_EQUAL(u2, 42ul); - BOOST_CHECK_CLOSE_FRACTION(u3, 3.14, 1e-3); - const bool dq = r4 == u4; - const bool ls = r5 == u5; - const bool ar = r6 == u6; - BOOST_CHECK(dq); - BOOST_CHECK(ls); - BOOST_CHECK(ar); - BOOST_CHECK_EQUAL(u7.at("val1").cast(), true); - BOOST_CHECK_EQUAL(u7.at("val2").cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(u7.at("val3").cast(),3.14, 1e-3); - BOOST_CHECK_EQUAL(u7.at("val4").cast(), "piyo"); + { + toml::value v(42); + BOOST_CHECK_EQUAL(int(42), toml::get(v)); + BOOST_CHECK_EQUAL(short(42), toml::get(v)); + BOOST_CHECK_EQUAL(char(42), toml::get(v)); + BOOST_CHECK_EQUAL(unsigned(42), toml::get(v)); + BOOST_CHECK_EQUAL(long(42), toml::get(v)); + BOOST_CHECK_EQUAL(std::int64_t(42), toml::get(v)); + BOOST_CHECK_EQUAL(std::uint64_t(42), toml::get(v)); + BOOST_CHECK_EQUAL(std::int16_t(42), toml::get(v)); + BOOST_CHECK_EQUAL(std::uint16_t(42), toml::get(v)); + } +} + +BOOST_AUTO_TEST_CASE(test_get_floating_type) +{ + { + toml::value v(3.14); + BOOST_CHECK_EQUAL(static_cast(3.14), toml::get(v)); + BOOST_CHECK_EQUAL(static_cast(3.14), toml::get(v)); + BOOST_CHECK_EQUAL(static_cast(3.14), toml::get(v)); + } +} + +BOOST_AUTO_TEST_CASE(test_get_string_type) +{ + { + toml::value v("foo", toml::string_t::basic); + BOOST_CHECK_EQUAL("foo", toml::get(v)); + toml::get(v) += "bar"; + BOOST_CHECK_EQUAL("foobar", toml::get(v)); + } + { + toml::value v("foo", toml::string_t::literal); + BOOST_CHECK_EQUAL("foo", toml::get(v)); + toml::get(v) += "bar"; + BOOST_CHECK_EQUAL("foobar", toml::get(v)); + } +} + +BOOST_AUTO_TEST_CASE(test_get_toml_array) +{ + toml::value v(toml::array(0)); + toml::get(v).push_back(toml::value(42)); + toml::get(v).push_back(toml::value(54)); + toml::get(v).push_back(toml::value(69)); + toml::get(v).push_back(toml::value(72)); + + const std::vector vec = toml::get>(v); + const std::list lst = toml::get>(v); + const std::deque deq = toml::get>(v); + + BOOST_CHECK_EQUAL(42, vec.at(0)); + BOOST_CHECK_EQUAL(54, vec.at(1)); + BOOST_CHECK_EQUAL(69, vec.at(2)); + BOOST_CHECK_EQUAL(72, vec.at(3)); + + std::list::const_iterator iter = lst.begin(); + BOOST_CHECK_EQUAL(static_cast(42), *(iter++)); + BOOST_CHECK_EQUAL(static_cast(54), *(iter++)); + BOOST_CHECK_EQUAL(static_cast(69), *(iter++)); + BOOST_CHECK_EQUAL(static_cast(72), *(iter++)); + + BOOST_CHECK_EQUAL(static_cast(42), deq.at(0)); + BOOST_CHECK_EQUAL(static_cast(54), deq.at(1)); + BOOST_CHECK_EQUAL(static_cast(69), deq.at(2)); + BOOST_CHECK_EQUAL(static_cast(72), deq.at(3)); + + std::array ary = toml::get>(v); + BOOST_CHECK_EQUAL(static_cast(42), ary.at(0)); + BOOST_CHECK_EQUAL(static_cast(54), ary.at(1)); + BOOST_CHECK_EQUAL(static_cast(69), ary.at(2)); + BOOST_CHECK_EQUAL(static_cast(72), ary.at(3)); + + std::tuple tpl = + toml::get>(v); + BOOST_CHECK_EQUAL(static_cast(42), std::get<0>(tpl)); + BOOST_CHECK_EQUAL(static_cast(54), std::get<1>(tpl)); + BOOST_CHECK_EQUAL(static_cast(69), std::get<2>(tpl)); + BOOST_CHECK_EQUAL(static_cast(72), std::get<3>(tpl)); + + toml::value p(toml::array{}); + toml::get(p).push_back(toml::value(3.14)); + toml::get(p).push_back(toml::value(2.71)); + std::pair pr = toml::get >(p); + BOOST_CHECK_EQUAL(3.14, pr.first); + BOOST_CHECK_EQUAL(2.71, pr.second); +} + +BOOST_AUTO_TEST_CASE(test_get_toml_array_of_array) +{ + toml::value v1(toml::array{}); + toml::get(v1).push_back(toml::value(42)); + toml::get(v1).push_back(toml::value(54)); + toml::get(v1).push_back(toml::value(69)); + toml::get(v1).push_back(toml::value(72)); + + toml::value v2(toml::array{}); + toml::get(v2).push_back(toml::value("foo")); + toml::get(v2).push_back(toml::value("bar")); + toml::get(v2).push_back(toml::value("baz")); + + toml::value v(toml::array(2)); + toml::get(v).at(0) = v1; + toml::get(v).at(1) = v2; + + std::pair, std::vector> p = + toml::get, std::vector>>(v); + + BOOST_CHECK_EQUAL(p.first.at(0), 42); + BOOST_CHECK_EQUAL(p.first.at(1), 54); + BOOST_CHECK_EQUAL(p.first.at(2), 69); + BOOST_CHECK_EQUAL(p.first.at(3), 72); + + BOOST_CHECK_EQUAL(p.second.at(0), "foo"); + BOOST_CHECK_EQUAL(p.second.at(1), "bar"); + BOOST_CHECK_EQUAL(p.second.at(2), "baz"); + + std::tuple, std::vector> t = + toml::get, std::vector>>(v); + + BOOST_CHECK_EQUAL(std::get<0>(t).at(0), 42); + BOOST_CHECK_EQUAL(std::get<0>(t).at(1), 54); + BOOST_CHECK_EQUAL(std::get<0>(t).at(2), 69); + BOOST_CHECK_EQUAL(std::get<0>(t).at(3), 72); + + BOOST_CHECK_EQUAL(std::get<1>(t).at(0), "foo"); + BOOST_CHECK_EQUAL(std::get<1>(t).at(1), "bar"); + BOOST_CHECK_EQUAL(std::get<1>(t).at(2), "baz"); } From 717e03cd4a5a7936c9faa8f9679322bd9a8dc113 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 17:55:34 +0900 Subject: [PATCH 089/133] add find-get overload functions --- toml/get.hpp | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 4 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index a27947e..1820585 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -265,17 +265,161 @@ T get(const toml::value& v) return map; } +// ============================================================================ +// find and get + +template +decltype(::toml::get(std::declval())) +get(const toml::table& tab, const toml::key& ky, + std::string tablename = "unknown table") +{ + if(tab.count(ky) == 0) + { + throw std::out_of_range(concat_to_string("[error] key \"", ky, + "\" not found in ", tablename)); + } + return ::toml::get(tab.at(ky)); +} +template +decltype(::toml::get(std::declval<::toml::value&>())) +get(toml::table& tab, const toml::key& ky, + std::string tablename = "unknown table") +{ + if(tab.count(ky) == 0) + { + throw std::out_of_range(concat_to_string("[error] key \"", ky, + "\" not found in ", tablename)); + } + return ::toml::get(tab[ky]); +} +template +decltype(::toml::get(std::declval<::toml::value&&>())) +get(toml::table&& tab, const toml::key& ky, + std::string tablename = "unknown table") +{ + if(tab.count(ky) == 0) + { + throw std::out_of_range(concat_to_string("[error] key \"", ky, + "\" not found in ", tablename)); + } + return ::toml::get(std::move(tab[ky])); +} + +template +decltype(::toml::get(std::declval())) +get(const toml::value& v, const toml::key& ky) +{ + const auto& tab = ::toml::get(v); + if(tab.count(ky) == 0) + { + throw std::out_of_range(detail::format_error_for_value(v, + concat_to_string("[error] key \"", ky, "\" not found"), + concat_to_string("in this table"))); + } + return ::toml::get(tab.at(ky)); +} +template +decltype(::toml::get(std::declval<::toml::value&>())) +get(toml::value& v, const toml::key& ky) +{ + auto& tab = ::toml::get(v); + if(tab.count(ky) == 0) + { + throw std::out_of_range(detail::format_error_for_value(v, + concat_to_string("[error] key \"", ky, "\" not found"), + concat_to_string("in this table"))); + } + return ::toml::get(tab.at(ky)); +} +template +decltype(::toml::get(std::declval<::toml::value&&>())) +get(toml::value&& v, const toml::key& ky) +{ + auto tab = ::toml::get(std::move(v)); + if(tab.count(ky) == 0) + { + throw std::out_of_range(detail::format_error_for_value(v, + concat_to_string("[error] key \"", ky, "\" not found"), + concat_to_string("in this table"))); + } + return ::toml::get(std::move(tab[ky])); +} + + // ============================================================================ // get_or template -inline typename std::remove_cv::type>::type -get_or(const toml::Table& tab, const toml::key& ky, T&& opt) +decltype(::toml::get::type>::type>( + std::declval())) +get_or(const toml::value& v, T&& opt) +{ + try + { + return get::type>::type>(v); + } + catch(...) + { + return std::forward(opt); + } +} +template +decltype(::toml::get::type>::type>( + std::declval())) +get_or(toml::value& v, T&& opt) +{ + try + { + return get::type>::type>(v); + } + catch(...) + { + return std::forward(opt); + } +} +template +decltype(::toml::get::type>::type>( + std::declval())) +get_or(toml::value&& v, T&& opt) +{ + try + { + return get::type>::type>(std::move(v)); + } + catch(...) + { + return std::forward(opt); + } +} + +template +auto get_or(const toml::table& tab, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) { if(tab.count(ky) == 0) {return std::forward(opt);} - return get::type>::type>(tab.find(ky)->second); + return ::toml::get_or(tab.at(ky), std::forward(opt)); } +template +auto get_or(toml::table& tab, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(tab[ky], std::forward(opt)); +} +template +auto get_or(toml::table&& tab, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(std::move(tab[ky]), std::forward(opt)); +} + // ============================================================================ // expect @@ -320,5 +464,85 @@ auto expect(toml::value&& v) } } +template +auto expect(const toml::value& v, const toml::key& k) + -> result(v, k)), std::string> +{ + try + { + return ok(get(v, k)); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template +auto expect(toml::value& v, const toml::key& k) + -> result(v, k)), std::string> +{ + try + { + return ok(get(v, k)); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template +auto expect(toml::value&& v, const toml::key& k) + -> result(std::move(v), k)), std::string> +{ + try + { + return ok(get(std::move(v), k)); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} + +template +auto expect(const toml::table& t, const toml::key& k, std::string tn) + -> result(t, k, std::move(tn))), std::string> +{ + try + { + return ok(get(t, k, std::move(tn))); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template +auto expect(toml::table& t, const toml::key& k, std::string tn) + -> result(t, k, std::move(tn))), std::string> +{ + try + { + return ok(get(t, k, std::move(tn))); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} +template +auto expect(toml::table&& t, const toml::key& k, std::string tn) + -> result(std::move(t), k, std::move(tn))), std::string> +{ + try + { + return ok(get(std::move(t), k, std::move(tn))); + } + catch(const std::exception& e) + { + return err(e.what()); + } +} + } // toml #endif// TOML11_GET From 5d29509d9851cf67ef010d70e0262c96ea30a491 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 18:58:54 +0900 Subject: [PATCH 090/133] remove duplicated default argument for SFINAE --- toml/get.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index 1820585..c2aece5 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -154,7 +154,7 @@ template, // T is container detail::has_resize_method, // T::resize(N) works detail::negation> // but not toml::array - >::value, std::nullptr_t>::type = nullptr> + >::value, std::nullptr_t>::type> T get(const value& v) { using value_type = typename T::value_type; @@ -173,7 +173,7 @@ template, // T is container detail::negation>, // no T::resize() exists detail::negation> // not toml::array - >::value, std::nullptr_t>::type = nullptr> + >::value, std::nullptr_t>::type> T get(const value& v) { using value_type = typename T::value_type; @@ -195,7 +195,7 @@ T get(const value& v) // std::pair. template::value, std::nullptr_t>::type = nullptr> + detail::is_std_pair::value, std::nullptr_t>::type> T get(const value& v) { using first_type = typename T::first_type; @@ -228,7 +228,7 @@ T get_tuple_impl(const toml::Array& a, index_sequence) } // detail template::value, std::nullptr_t>::type = nullptr> + detail::is_std_tuple::value, std::nullptr_t>::type> T get(const value& v) { const auto& ar = v.cast(); @@ -249,7 +249,7 @@ T get(const value& v) template, // T is map detail::negation> // but not toml::table - >::value, std::nullptr_t>::type = nullptr> + >::value, std::nullptr_t>::type> T get(const toml::value& v) { using key_type = typename T::key_type; From c33ad31981fa43b2f33a812c89234b51b9073c73 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 18:59:20 +0900 Subject: [PATCH 091/133] split lexer for escape sequence for unicode --- toml/lexer.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/toml/lexer.hpp b/toml/lexer.hpp index 408d087..060195e 100644 --- a/toml/lexer.hpp +++ b/toml/lexer.hpp @@ -117,14 +117,16 @@ using lex_basic_unescaped = exclude, character<0x22>, character<0x5C>, character<0x7F>>>; using lex_escape = character<'\\'>; +using lex_escape_unicode_short = sequence, + repeat>>; +using lex_escape_unicode_long = sequence, + repeat>>; using lex_escape_seq_char = either, character<'\\'>, character<'/'>, character<'b'>, character<'f'>, character<'n'>, character<'r'>, character<'t'>, - sequence, - repeat>>, - sequence, - repeat>> + lex_escape_unicode_short, + lex_escape_unicode_long >; using lex_escaped = sequence; using lex_basic_char = either; From 879b7d3bfffb701c8a3f610187cb03dc411bc6de Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 19:01:22 +0900 Subject: [PATCH 092/133] improve format of error message for utf-8 --- toml/parser.hpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 8cfb884..d630967 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -226,8 +226,10 @@ parse_floating(location& loc) "token is not a float", {"floating point is like: -3.14e+1"})); } -inline std::string read_utf8_codepoint(const std::string& str) +template +std::string read_utf8_codepoint(const region& reg) { + const auto str = reg.str().substr(1); std::uint_least32_t codepoint; std::istringstream iss(str); iss >> std::hex >> codepoint; @@ -254,10 +256,11 @@ inline std::string read_utf8_codepoint(const std::string& str) { if(0x10FFFF < codepoint) // out of Unicode region { - std::cerr << "WARNING: input codepoint " << str << " is too large " - << "to decode as a unicode character. It should be in " - << "range [0x00 .. 0x10FFFF]. The result may not be able " - << "to be rendered to your screen." << std::endl; + std::cerr << format_underline(concat_to_string("[warning] " + "input codepoint (", str, ") is too large to decode as " + "a unicode character. The result may not be able to render " + "to your screen."), reg, "should be in [0x00..0x10FFFF]") + << std::endl; } // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx character += static_cast(0xF0| codepoint >> 18); @@ -267,9 +270,9 @@ inline std::string read_utf8_codepoint(const std::string& str) } else // out of UTF-8 region { - throw std::range_error("toml::read_utf8_codepoint: input codepoint `" + - str + "` is too large to decode as utf-8. It should be in range" - " 0x00 ... 0x1FFFFF."); + throw std::range_error(format_underline(concat_to_string("[error] " + "input codepoint (", str, ") is too large to encode as utf-8."), + reg, "should be in [0x00..0x1FFFFF]")); } return character; } @@ -278,7 +281,7 @@ template result parse_escape_sequence(location& loc) { const auto first = loc.iter(); - if(*first != '\\') + if(first == loc.end() || *first != '\\') { return err(format_underline("[error]: " "toml::parse_escape_sequence: location does not points \"\\\"", @@ -296,10 +299,9 @@ result parse_escape_sequence(location& loc) case 'r' :{++loc.iter(); return ok(std::string("\r"));} case 'u' : { - ++loc.iter(); - if(const auto token = repeat>::invoke(loc)) + if(const auto token = lex_escape_unicode_short::invoke(loc)) { - return ok(read_utf8_codepoint(token.unwrap().str())); + return ok(read_utf8_codepoint(token.unwrap())); } else { @@ -310,10 +312,9 @@ result parse_escape_sequence(location& loc) } case 'U': { - ++loc.iter(); - if(const auto token = repeat>::invoke(loc)) + if(const auto token = lex_escape_unicode_long::invoke(loc)) { - return ok(read_utf8_codepoint(token.unwrap().str())); + return ok(read_utf8_codepoint(token.unwrap())); } else { @@ -341,7 +342,6 @@ parse_ml_basic_string(location& loc) if(const auto token = lex_ml_basic_string::invoke(loc)) { location inner_loc(loc.name(), token.unwrap().str()); - std::string retval; retval.reserve(inner_loc.source()->size()); From 0f83ee60398d9d27c5f39fe29d43a94b5f011e72 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 19:12:23 +0900 Subject: [PATCH 093/133] change temporaly loc from token to copy of loc location constructed from token string does not has correct line number information. to show an informative error message about UTF-8 and escape sequences, parse_(ml_)basic_string requires those information that can only be given from root location. --- toml/parser.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index d630967..2cd4e85 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -341,9 +341,11 @@ parse_ml_basic_string(location& loc) const auto first = loc.iter(); if(const auto token = lex_ml_basic_string::invoke(loc)) { - location inner_loc(loc.name(), token.unwrap().str()); + auto inner_loc = loc; + inner_loc.iter() = first; + std::string retval; - retval.reserve(inner_loc.source()->size()); + retval.reserve(token.unwrap().size()); auto delim = lex_ml_basic_string_delim::invoke(inner_loc); if(!delim) @@ -396,7 +398,8 @@ parse_basic_string(location& loc) const auto first = loc.iter(); if(const auto token = lex_basic_string::invoke(loc)) { - location inner_loc(loc.name(), token.unwrap().str()); + auto inner_loc = loc; + inner_loc.iter() = first; auto quot = lex_quotation_mark::invoke(inner_loc); if(!quot) @@ -406,7 +409,7 @@ parse_basic_string(location& loc) } std::string retval; - retval.reserve(inner_loc.source()->size()); + retval.reserve(token.unwrap().size()); quot = err("tmp"); while(!quot) From 5aae0b17c88cd305bb3c12e8f5af1b67de0f4bdb Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 19:14:27 +0900 Subject: [PATCH 094/133] change error message; require unicode codepoint before this, it recommends the range that can be represented by utf-8 but the range of valid unicode codepoint is narrower than that. for error message, it is good to recommend valid unicode codepoint. --- toml/parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 2cd4e85..15954a7 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -272,7 +272,7 @@ std::string read_utf8_codepoint(const region& reg) { throw std::range_error(format_underline(concat_to_string("[error] " "input codepoint (", str, ") is too large to encode as utf-8."), - reg, "should be in [0x00..0x1FFFFF]")); + reg, "should be in [0x00..0x10FFFF]")); } return character; } From b015e1ac5b50c3b409a40ffa70898d8649a71181 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 19:33:01 +0900 Subject: [PATCH 095/133] update README for the next version --- README.md | 482 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 314 insertions(+), 168 deletions(-) diff --git a/README.md b/README.md index e157ef4..3f89544 100644 --- a/README.md +++ b/README.md @@ -6,199 +6,248 @@ toml11 [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) -c++11 header-only toml parser depending only on c++11 standard library. +c++11 header-only toml parser depending only on c++ standard library. -compatible to TOML v0.4.0. +compatible to the latest version of TOML v0.5.0 after version 2.0.0. + +Are you looking for pre-C++11 compatible toml parser? Check out [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and work with C++98 & Boost. ## How to use +### installation + Just include the file after adding correct include path. ```cpp -#include +#include // that's all! now you can use it. int main() { - /* do something ... */ + const auto data = toml::parse("example.toml"); + const auto title = toml::get(data.at("title")); + std::cout << "the title is " << title << std::endl; } ``` -### decoding toml file +## decoding toml file -The only thing you have to do is passing filename to `toml::parse` function. +### parsing toml file + +The only thing you have to do is passing a filename to `toml::parse` function. ```cpp const std::string fname("sample.toml"); -const auto data = toml::parse(fname); +const toml::table data = toml::parse(fname); ``` In the case of file open error, it will throw `std::runtime_error`. -You can pass also `stream` to `toml::parse` function. +You can pass also `stream` to `toml::parse` function after checking the status. ```cpp std::ifstream ifs("sample.toml"); assert(ifs.good()); -const auto data = toml::parse(ifs); +const auto data = toml::parse(ifs /*, "filename" (optional)*/); ``` -If there are syntax error in the toml file, -`toml::parse` will throw `toml::syntax_error`. +To show a better error message, it is recommended to pass filename with `istream`. See also [in the case of syntax error](in-the-case-of-syntax-error) and [passing invalid type to toml::get](passing-invalid-type-to-tomlget). -#### toml::get() +### in the case of syntax error -Then you can obtain the various value from the `data` using `toml::get` function. +If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`. + +toml11 now has clean error messages inspired the Rust compiler and it looks like the following. + +```console +terminate called after throwing an instance of 'toml::syntax_error' + what(): [error] toml::parse_table: invalid line format # error description + --> example.toml # file name (and path, if any) + 3 | a = 42 = true # line num and content + | ^------ expected newline, but got '='. # error reason +``` + +It shows almost all the information to fix the error in the toml file like file name, line number, the line that is invalid, and the reason why it fails. + +Since the error message generation is generally a difficult task, the current status is not ideal. We need your help. If you encounter a weird error message, please let us know and improve the quality! + +### getting toml value + +After parsing successfully, you can obtain the values from the result of `toml::parse` (here, `data`) using `toml::get` function. ```toml -answer = 42 -pi = 3.14 +answer = 42 +pi = 3.14 numbers = [1,2,3] -time = 1979-05-27T07:32:00Z +time = 1979-05-27T07:32:00Z [tab] key = "value" ``` ``` cpp -const auto answer = toml::get(data.at("answer")); -const auto pi = toml::get(data.at("pi")); +const auto answer = toml::get(data.at("answer")); +const auto pi = toml::get(data.at("pi")); const auto numbers = toml::get>(data.at("numbers")); const auto timepoint = toml::get(data.at("time")); const auto tab = toml::get(data.at("tab")); -const auto key = toml::get(tab.at("key")); +const auto key = toml::get( tab.at("key")); ``` -You can set any kind of `container` class to obtain `toml::Array` except for -`map`-like class. +When you pass an exact TOML type that does not require type conversion, `toml::get` returns also a reference through which you can modify the content. + +```cpp +toml::get(data["answer"]) = 6 * 9; +std::cout << toml::get(data.at("answer")) << std::endl; // 54 +``` + +#### passing invalid type to toml::get + +If you choose the invalid type, `toml::type_error` will be thrown. Similar to the `syntax_error`, toml11 also displays informative error message. The error message when you choose `int` to get `string` value would be like this. + +```console +title = terminate called after throwing an instance of 'toml::type_error' + what(): [error] toml::value bad_cast to integer + --> example.toml + 3 | title = "TOML Example" + | ~~~~~~~~~~~~~~ the actual type is string +``` + +NOTE: In order to show this kind of error message, all the toml values has 1 shared_ptr that points the corresponding byte sequence and 2 iterator that points the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save the memory resource. + +#### finding value from table + +toml11 provides overloads to find a value from `toml::table`. Of course, you can do this in your own way with `toml::get` because it just searches and returns a value. + +```cpp +const auto data = toml::parse("example.toml"); +const auto num = toml::get(data, "num", /*optional*/"example.toml"); +``` + +If the value does not exists, it throws `std::out_of_range` with informative error message. + +```console +title = terminate called after throwing an instance of 'std::out_of_range' + what(): [error] key "num" not found in example.toml +``` + +You can use this with a `toml::value` that is expected to be a `toml::table`. It automatically casts the value to table. + +```cpp +const auto data = toml::parse("example.toml"); +const auto num = toml::get(data.at("table"), "num"); +// expecting the following example.toml +// [table] +// num = 42 +``` + +In this case, because the value `data.at("table")` knows the locatoin of itself, you don't need to pass where you find the value. `toml::get` will show you a great error message. + +```console +terminate called after throwing an instance of 'std::out_of_range' + what(): [error] key "num" not found + --> example.toml + 3 | [table] + | ~~~~~~~ in this table +``` + +If it's not a `toml::table`, error like "invalid type" would be thrown. + +### getting arrays + +You can set any kind of `container` class to obtain `toml::array` except for `map`-like classes. ``` cpp -const auto vc = toml::get>(data.at("numbers")); -const auto ls = toml::get>(data.at("numbers")); -const auto dq = toml::get>(data.at("numbers")); +const auto vc = toml::get >(data.at("numbers")); +const auto ls = toml::get >(data.at("numbers")); +const auto dq = toml::get >(data.at("numbers")); const auto ar = toml::get>(data.at("numbers")); -// if size of data.at("numbers") is larger than 3, it will throw toml::type_error -// because std::array is not resizable. +// if the size of data.at("numbers") is larger than that of std::array, +// it will throw toml::type_error because std::array is not resizable. ``` -If the type you passed as a template parameter is incorrect, -it will throw `toml::type_error`. - -``` cpp -const auto wrong1 = toml::get(data.at("integer")); // exception thrown! -const auto wrong2 = toml::get(data.at("integer")); // ditto -const auto wrong3 = toml::get(data.at("array")); // ditto -``` - -Although `toml::get` is convenient, it has additional copy-cost because it -copies data contained in `toml::value` to user-specified type. -Of course in some case this overhead is not ignorable. - -You can get reference pointing to contained value using `toml::value::cast()` like this. - -``` cpp -const auto& pi = data.at("pi").cast(); -const auto& tab = data.at("tab").cast(); -const auto& numbers = data.at("numbers").cast(); -``` - -Unfortunately, if you use `toml::value::cast` to get an array, you would need to -`cast` each element in `toml::Array` because `toml::Array` is represented as -an array of `toml::value`. +Surprisingly, you can get `toml::array` as `std::pair` and `std::tuple.` ```cpp -const auto& num0 = numbers.at(0).cast(); -const auto& num1 = numbers.at(1).cast(); -const auto& num2 = numbers.at(2).cast(); +const auto tp = toml::get>(data.at("numbers")); ``` -#### toml::get\_or +The case when you need this functionality is to get an array of arrays. -You can also set default value for `toml::get`. - -```cpp -toml::Table data; // empty table! - -const auto value1 = toml::get_or(data, "key1", 42); // value1 => int 42. - -toml::Integer i(123); -const auto value2 = toml::get_or(data, "key1", i); // value2 => toml::Integer 42. +```toml +aofa = [[1,2,3], ["foo", "bar", "baz"]] # toml allows this ``` -#### toml::value\_t - -When you don't know the exact type of toml-value, you can get `enum` type from -`toml::value`. +What is the corresponding C++ type? Obviously, it is a `std::pair` of `std::vector`s. ```cpp -int i; -double d; -std::string s; -std::vector a; -const auto t = data.at("something").type(); -switch(t) +const auto aofa = toml::get< + std::pair, std::vector> + >(data.at("aofa")); +``` + +If you don't know what the type is inside the array, you can use `toml::array` which is a `std::vector` of `toml::value`instead. + +```cpp +const auto aofa = toml::get(data.at("aofa")); +const auto first = toml::get(aofa.at(0)); +``` + +See also [expecting conversion](#expecting-conversion) and [checking-value-type](#checking-value-type). + +### getting tables + +`toml::table` is a key component of this library, that is an alias of a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`. `toml::parse` returns this as a result. + +Since it is just an alias of `std::unordered_map`, it has all the functionalities that `std::unordered_map` has, e.g. `operator[]`, `count`, and `find`. + +```cpp +toml::table data = toml::parse("example.toml"); +if(data.count("title") != 0) { - case toml::value_t::Integer: i = toml::get(data.at("something")); break; - case toml::value_t::Float : d = toml::get(data.at("something")); break; - case toml::value_t::String : s = toml::get(data.at("something")); break; - case toml::value_t::Array : a = toml::get>(data.at("something")); break; - default : throw std::runtime_error("unexpected type : " + toml::stringize(t)); + data["title"] = std::string("TOML example"); } ``` -Okey, but it is painful to write `switch-case` for many time. - -#### toml::from\_toml() - -The more sophisticated way is using `toml::from_toml` and `std::tie`. - -```cpp -int i = 0; -double d = 0.; -std::string s; -std::vector a; - -toml::from_toml(std::tie(i, d, s, a), data.at("something")); -``` - -Here, only matched value will be filled. -The others are left intact after calling `from_toml`. -It should be noted that `toml::from_toml` returns as usual even if there are no -matched type. - - -`from_toml` can be used also for single type. - -```cpp -int i; -toml::from_toml(i, data.at("something")); -``` - -Unlike `toml::get`, `toml::from_toml` does not require to specify the type -through the template argument because the type can be deduced from argument. - -#### toml::value - -In toml, `Array` is capable of having `Array` s and each of them possibly have -different types like this. +For the case that all the values of the table have the same type, toml11 allows you to convert `toml::table` to `map`s that contains the convertible type. ```toml -array_of_array = [[1,2,3,4,5], ["foo", "bar", "baz"]] +[tab] +key1 = "foo" # all the values are +key2 = "bar" # toml String ``` -In this case, you can use `toml::value` directly. - ```cpp -// use toml::value in a container -const auto a = toml::get>(data.at("array_of_array")); -// or you can use default toml::Array. -const auto a_ = toml::get(data.at("array_of_array")); -// you can obtain values from toml::value in the same way as described above. -const auto ns = toml::get>(a.at(0)); -const auto ss = toml::get>(a.at(1)); +const auto tab = toml::get>(data.at("tab")); +std::cout << tab["key1"] << std::endl; // foo +std::cout << tab["key2"] << std::endl; // bar ``` -#### Array of Table +### dotted keys -Of course, you can obtain `array of table` in the same way. +TOML v0.5.0 has a new feature named "dotted keys". You can chain keys to represent the structure of the data. + +```toml +physical.color = "orange" +physical.shape = "round" +``` + +This is equivalent to the following. + +```toml +[physical] +color = "orange" +shape = "round" +``` + +You can get both of the above formats with the same c++ code. + +```cpp +const auto physical = toml::get(data.at("physical")); +const auto color = toml::get(physical.at("color")); +``` + +### an array of tables + +An array of tables is just an array of tables. You can get completely in the same way as the other arrays and tables. ```toml array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}] @@ -212,61 +261,158 @@ key = "value6" ``` ```cpp -const auto aot1 = toml::get>(data.at("array_of_inline_table")) -const auto aot2 = toml::get>(data.at("array_of_table")) +const auto aot1 = toml::get>(data.at("array_of_inline_table")); +const auto aot2 = toml::get>(data.at("array_of_table")); ``` -## Documentation +### cost of conversion -The toml types and corresponding `enum` name are listed in the table below. -`value_t` is a scoped-enum defined in the namespace toml. +Although `toml::get` is convenient, it has additional copy-cost because it copies data contained in `toml::value` to user-specified type. Of course in some case this overhead is not ignorable. -| toml-type | c++ type | enum | -| --------- | ---------------------------------------------- | ------------------------- | -| Boolean | `bool` | `toml::value_t::Boolean` | -| Integer | `std::int64_t` | `toml::value_t::Integer` | -| Float | `double` | `toml::value_t::Float` | -| String | `std::string` | `toml::value_t::String` | -| Datetime | `toml::Datetime` | `toml::value_t::Datetime` | -| Array | `std::vector` | `toml::value_t::Array` | -| Table | `std::unordered_map` | `toml::value_t::Table` | +By passing the exact types, `toml::get` returns reference that has nealy zero overhead. -`Datetime` is the `struct` that is defined in this library. -Because `std::chrono::system_clock::time_point` is a __time point__, not capable -of representing a Local Time independent from a specific day. +``` cpp +const auto& tab = toml::get(data.at("tab")); +const auto& numbers = toml::get(data.at("numbers")); +``` -For user-convenience, `toml::Datetime` is _implicitly_ convertible to -`std::chrono::system_clock::time_point`. If `toml::Datetime` does not have any -Date information, the information will be generated from -`std::chrono::system_clock::now()` when cast is performed. - -The definition of Datetime struct is below. +Unfortunately, in this case you need to call `toml::get` each time you access to the element of `toml::array` because `toml::array` is an array of `toml::value`. ```cpp -namespace toml -{ -template -struct basic_datetime -{ - uintT year; // since 0. - uintT month; // [1-12] - uintT day; // [1-31] - uintT hour; // [0-23] - uintT minite; // [0-59] - uintT second; // [0-59] - uintT millisecond // [0-999] - uintT microsecond // [0-999] - intT offset_hour; // [-12 - +12] - intT offset_minute; // [-59 - +59] -}; +const auto& num0 = toml::get(numbers.at(0)); +const auto& num1 = toml::get(numbers.at(1)); +const auto& num2 = toml::get(numbers.at(2)); +``` -typedef basic_datetime Datetime; +### datetime and its variants + +TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, `local_datetime`, and `offset_datetime`. With toml11, you can convert `local_time` to your favorite `std::chrono::duration` and others to `std::chrono::system_clock::time_point`. + +```toml +time = 12:30:00 +date = 2018-12-23 +``` + +```cpp +const auto dur = toml::get(data.at("time")); // 12 * 60 + 30 min +const auto tp = toml::get(data.at("date")); +``` + +### getting with a fallback + + `toml::get_or` returns a default value if `toml::get` failed. + +```cpp +toml::table data; // empty table! +const auto value = toml::get_or(data, "key", 42); // value => int 42. +``` + +`toml::get_or` automatically deduces what type you want to get from the default value you passed. + +### expecting conversion + +By using `toml::expect`, you will get your expected value or an error message without throwing `toml::type_error`. + +```cpp +const auto value = toml::expect(data.at("title")); +if(value.is_ok()) { + std::cout << value.unwrap() << std::endl; +} else { + std::cout << value.unwrap_err() << std::endl; } ``` -It must be noted that the range of some values in `basic_datetime` is different -from `std::tm`. For example, month is in the range of `[1,12]` and year starts -from 0 (not 1900). +Also, you can pass a function object to modify the expected value. + +```cpp +const auto value = toml::expect(data.at("number")) + .map(// function that receives expected type (here, int) + [](const int number) -> double { + return number * 1.5 + 1.0; + }).unwrap_or(/*default value =*/ 3.14); +``` + +### checking value type + +When you don't know the exact type of toml-value, you can get `enum` type from `toml::value`. + +```cpp +switch(data.at("something").type()) +{ + case toml::value_t::Integer: /* do some stuff */; break; + case toml::value_t::Float : /* do some stuff */; break; + case toml::value_t::String : /* do some stuff */; break; + default : throw std::runtime_error( + "unexpected type : " + toml::stringize(data.at("something").type())); +} +``` + +### fill only the matched value + +The more sophisticated way is using `toml::from_toml` and `std::tie`. + +```cpp +toml::table data{{"something", toml::value("foo")}}; +int i = 0; +double d = 0.; +std::string s; +toml::from_toml(std::tie(i, d, s), data.at("something")); +std::cout << i << ", " << d << ", " << s << std::endl; // 0, 0, foo +``` + +Here, only matched value will be filled. The others are left intact after calling `from_toml`. +It should be noted that `toml::from_toml` returns as usual even if there are no matched type. + +`from_toml` can be used also for single type. + +```cpp +int i = 0; +toml::from_toml(i, data.at("something")); +``` + +### sanitizing UTF-8 codepoints + +toml11 shows warning if a value of an escape sequence to represent unicode character exceeds the range of unicode. + +```console +[warning] input codepoint (0011FFFF) is too large to decode as a unicode character. The result may not be able to render to your screen. + --> example.toml + 3 | exceeds_unicode = "\U0011FFFF example" + | ~~~~~~~~~ should be in [0x00..0x10FFFF] +``` + +Also, toml11 throws `std::domain_error` if the code point exceeds the range that can be represented by utf-8. + +```console +terminate called after throwing an instance of 'std::range_error' + what(): [error] input codepoint (0020FFFF) is too large to encode as utf-8. + --> example.toml + 3 | exceeds_utf8 = "\U0020FFFF example" + | ~~~~~~~~~ should be in [0x00..0x10FFFF] +``` + +## underlying types + +The toml types (can be used as `toml::*` in this library) and corresponding `enum` name are listed in the table below. + +| toml::type | underlying c++ type | enum | +| -------------- | -------------------------------------------- | ------------------------------- | +| Boolean | `bool` | `toml::value_t::Boolean` | +| Integer | `std::int64_t` | `toml::value_t::Integer` | +| Float | `double` | `toml::value_t::Float` | +| String | `toml::string` | `toml::value_t::String` | +| LocalDate | `toml::local_date` | `toml::value_t::LocalDate` | +| LocalTime | `toml::local_time` | `toml::value_t::LocalTime` | +| LocalDatetime | `toml::local_datetime` | `toml::value_t::LocalDatetime` | +| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offsetDatetime` | +| Array | `std::vector` | `toml::value_t::Array` | +| Table | `std::unordered_map` | `toml::value_t::Table` | + +`toml::string` is effectively the same as `std::string` but has an additional flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. Although `std::string` is not an exact toml type, but still you can get a reference that points internal `std::string` by using `toml::get()` for the convenience. + +`Datetime` variants are `struct` that are defined in this library. Because `std::chrono::system_clock::time_point` is a __time point__, not capable of representing a Local Time independent from a specific day. + +It is recommended to get `Datetime`s as `std::chrono` classes through `toml::get`. ## Contributors From d4a48652172b75bdd71422352bb5ef2bf2510773 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 19:35:33 +0900 Subject: [PATCH 096/133] fix some errors in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3f89544..ecc9ed4 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ assert(ifs.good()); const auto data = toml::parse(ifs /*, "filename" (optional)*/); ``` -To show a better error message, it is recommended to pass filename with `istream`. See also [in the case of syntax error](in-the-case-of-syntax-error) and [passing invalid type to toml::get](passing-invalid-type-to-tomlget). +To show a better error message, it is recommended to pass filename with `istream`. See also [in the case of syntax error](#in-the-case-of-syntax-error) and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget). ### in the case of syntax error @@ -104,7 +104,7 @@ std::cout << toml::get(data.at("answer")) << std::endl; // 54 If you choose the invalid type, `toml::type_error` will be thrown. Similar to the `syntax_error`, toml11 also displays informative error message. The error message when you choose `int` to get `string` value would be like this. ```console -title = terminate called after throwing an instance of 'toml::type_error' +terminate called after throwing an instance of 'toml::type_error' what(): [error] toml::value bad_cast to integer --> example.toml 3 | title = "TOML Example" @@ -125,7 +125,7 @@ const auto num = toml::get(data, "num", /*optional*/"example.toml"); If the value does not exists, it throws `std::out_of_range` with informative error message. ```console -title = terminate called after throwing an instance of 'std::out_of_range' +terminate called after throwing an instance of 'std::out_of_range' what(): [error] key "num" not found in example.toml ``` From 0924164f51830d072d1fd97708f6a8ca5e628e0c Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 19:36:57 +0900 Subject: [PATCH 097/133] add a note about the error message --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ecc9ed4..fe8b1d4 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ To show a better error message, it is recommended to pass filename with `istream If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`. -toml11 now has clean error messages inspired the Rust compiler and it looks like the following. +toml11 now has clean error messages inspired the Rust compiler and it looks like the following (comment after hash sign are actually not shown). ```console terminate called after throwing an instance of 'toml::syntax_error' From 27ad4e2d8f175e6c23dd5f2c7808cac456c9ea2e Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 19:39:04 +0900 Subject: [PATCH 098/133] cleanup headers --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fe8b1d4..3228adf 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Are you looking for pre-C++11 compatible toml parser? Check out [Boost.toml](htt ## How to use -### installation +## installation Just include the file after adding correct include path. @@ -29,9 +29,7 @@ int main() } ``` -## decoding toml file - -### parsing toml file +### decoding toml file The only thing you have to do is passing a filename to `toml::parse` function. From 03be08a2e6db531197d0eb41afd9c1efea5af889 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 20:28:11 +0900 Subject: [PATCH 099/133] fix conversion from offset_datetime to system_clock::time_point --- toml/datetime.hpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index eccd1df..a55b08b 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -410,18 +410,24 @@ struct offset_datetime operator std::chrono::system_clock::time_point() const { - // get date-time in local timezone + // get date-time std::chrono::system_clock::time_point tp = std::chrono::system_clock::time_point(this->date) + std::chrono::microseconds(this->time); - // get date-time in UTC by subtracting current offset + // get date-time in UTC. let's say we are in +09:00 (JPN). + // writing 12:00:00 in +09:00 means 03:00:00Z. to represent + // 12:00:00Z, first we need to add +09:00. const auto ofs = get_local_offset(); - tp -= std::chrono::hours (ofs.hour); - tp -= std::chrono::minutes(ofs.minute); + tp += std::chrono::hours (ofs.hour); + tp += std::chrono::minutes(ofs.minute); - // add offset defined in this struct - tp += std::chrono::minutes(this->offset); + // here, tp represents 12:00:00 in UTC but we have offset information. + // we need to subtract it. For example, let's say the input is + // 12:00:00-08:00. now we have tp = 12:00:00Z as a result of the above + // conversion. But the actual time we need to return is 20:00:00Z + // because of -08:00. + tp -= std::chrono::minutes(this->offset); return tp; } From 18a22eb3c415d702538eff440023ad06d85b78b9 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 20:28:31 +0900 Subject: [PATCH 100/133] add test cases for datetime variants --- tests/test_get.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/tests/test_get.cpp b/tests/test_get.cpp index 56d0929..a3b6449 100644 --- a/tests/test_get.cpp +++ b/tests/test_get.cpp @@ -250,3 +250,94 @@ BOOST_AUTO_TEST_CASE(test_get_toml_array_of_array) BOOST_CHECK_EQUAL(std::get<1>(t).at(1), "bar"); BOOST_CHECK_EQUAL(std::get<1>(t).at(2), "baz"); } + +BOOST_AUTO_TEST_CASE(test_get_toml_local_date) +{ + toml::value v1(toml::local_date{2018, toml::month_t::Apr, 1}); + const auto date = toml::get(v1); + + std::tm t; + t.tm_year = 2018 - 1900; + t.tm_mon = 4 - 1; + t.tm_mday = 1; + t.tm_hour = 0; + t.tm_min = 0; + t.tm_sec = 0; + const auto c = std::chrono::system_clock::from_time_t(std::mktime(&t)); + BOOST_CHECK(c == date); +} + +BOOST_AUTO_TEST_CASE(test_get_toml_local_time) +{ + toml::value v1(toml::local_time{12, 30, 45}); + const auto time = toml::get(v1); + BOOST_CHECK(time == std::chrono::hours(12) + + std::chrono::minutes(30) + std::chrono::seconds(45)); +} + +BOOST_AUTO_TEST_CASE(test_get_toml_local_datetime) +{ + toml::value v1(toml::local_datetime( + toml::local_date{2018, toml::month_t::Apr, 1}, + toml::local_time{12, 30, 45})); + + const auto date = toml::get(v1); + std::tm t; + t.tm_year = 2018 - 1900; + t.tm_mon = 4 - 1; + t.tm_mday = 1; + t.tm_hour = 12; + t.tm_min = 30; + t.tm_sec = 45; + const auto c = std::chrono::system_clock::from_time_t(std::mktime(&t)); + BOOST_CHECK(c == date); +} + +BOOST_AUTO_TEST_CASE(test_get_toml_offset_datetime) +{ + { + toml::value v1(toml::offset_datetime( + toml::local_date{2018, toml::month_t::Apr, 1}, + toml::local_time{12, 30, 0}, + toml::time_offset{9, 0})); + // 2018-04-01T12:30:00+09:00 + // == 2018-04-01T03:30:00Z + + const auto date = toml::get(v1); + const auto timet = std::chrono::system_clock::to_time_t(date); + + // get time_t as gmtime (2018-04-01T03:30:00Z) + const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! + BOOST_CHECK(tmp); + const auto tm = *tmp; + BOOST_CHECK_EQUAL(tm.tm_year + 1900, 2018); + BOOST_CHECK_EQUAL(tm.tm_mon + 1, 4); + BOOST_CHECK_EQUAL(tm.tm_mday, 1); + BOOST_CHECK_EQUAL(tm.tm_hour, 3); + BOOST_CHECK_EQUAL(tm.tm_min, 30); + BOOST_CHECK_EQUAL(tm.tm_sec, 0); + } + + { + toml::value v1(toml::offset_datetime( + toml::local_date{2018, toml::month_t::Apr, 1}, + toml::local_time{12, 30, 0}, + toml::time_offset{-8, 0})); + // 2018-04-01T12:30:00-08:00 + // == 2018-04-01T20:30:00Z + + const auto date = toml::get(v1); + const auto timet = std::chrono::system_clock::to_time_t(date); + + // get time_t as gmtime (2018-04-01T03:30:00Z) + const auto tmp = std::gmtime(std::addressof(timet)); // XXX not threadsafe! + BOOST_CHECK(tmp); + const auto tm = *tmp; + BOOST_CHECK_EQUAL(tm.tm_year + 1900, 2018); + BOOST_CHECK_EQUAL(tm.tm_mon + 1, 4); + BOOST_CHECK_EQUAL(tm.tm_mday, 1); + BOOST_CHECK_EQUAL(tm.tm_hour, 20); + BOOST_CHECK_EQUAL(tm.tm_min, 30); + BOOST_CHECK_EQUAL(tm.tm_sec, 0); + } +} From cfd82c95f0d48016a2a4f8843bf370236c2d4ffb Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 20:32:26 +0900 Subject: [PATCH 101/133] add test case for getting converted map --- tests/test_get.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_get.cpp b/tests/test_get.cpp index a3b6449..8a01757 100644 --- a/tests/test_get.cpp +++ b/tests/test_get.cpp @@ -251,6 +251,23 @@ BOOST_AUTO_TEST_CASE(test_get_toml_array_of_array) BOOST_CHECK_EQUAL(std::get<1>(t).at(2), "baz"); } +BOOST_AUTO_TEST_CASE(test_get_toml_table) +{ + toml::value v1(toml::table{ + {"key1", 1}, + {"key2", 2}, + {"key3", 3}, + {"key4", 4} + }); + + const auto v = toml::get>(v1); + BOOST_CHECK_EQUAL(v.at("key1"), 1); + BOOST_CHECK_EQUAL(v.at("key2"), 2); + BOOST_CHECK_EQUAL(v.at("key3"), 3); + BOOST_CHECK_EQUAL(v.at("key4"), 4); +} + + BOOST_AUTO_TEST_CASE(test_get_toml_local_date) { toml::value v1(toml::local_date{2018, toml::month_t::Apr, 1}); From 65cfa9d06b028197c57c423ac8cb5b7eb0479342 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 20:35:21 +0900 Subject: [PATCH 102/133] remove unused file because its not stable since toml::format is not stable now, remove once for the next release. --- toml/format.hpp | 386 ------------------------------------------------ 1 file changed, 386 deletions(-) delete mode 100644 toml/format.hpp diff --git a/toml/format.hpp b/toml/format.hpp deleted file mode 100644 index c4726dc..0000000 --- a/toml/format.hpp +++ /dev/null @@ -1,386 +0,0 @@ -#ifndef TOML11_FORMAT -#define TOML11_FORMAT -#include "value.hpp" -#include -#include -#include -#include -#include - -namespace toml -{ - -// synopsis -// toml::format("key", value, toml::make_inline(80)) -// toml::format("key", value, toml::forceinline) -// std::cout << toml::make_inline(80) << value; -// std::cout << toml::forceinline << value; - -template, - typename alloc = std::allocator> -std::basic_string -format(const value& v); - -template, - typename alloc = std::allocator> -std::basic_string -format(const value& v, std::size_t mk); - -template, - typename alloc = std::allocator> -std::basic_string -format(const toml::key& k, const value& v); - -template, - typename alloc = std::allocator> -std::basic_string -format(const toml::key& k, const value& v, std::size_t mk); - -template -struct format_impl; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::basic_string - operator()(const type& val) - { - return val ? "true" : "false"; - } -}; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::basic_string - operator()(const type& val) - { - return std::to_string(val); - } -}; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::basic_string - operator()(const type& val) - { - std::basic_ostringstream oss; - oss << std::showpoint << val; - if(oss.str().back() == '.') oss << '0'; - return oss.str(); - } -}; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::size_t max_length; - - format_impl() : max_length(80){} - format_impl(std::size_t mx) : max_length(mx){} - - std::basic_string - operator()(const type& val) - { - auto tmp = make_inline(val); - if(max_length == std::numeric_limits::max() || - tmp.size() <= max_length) return tmp; - return convert_multiline(std::move(tmp)); - } - - private: - - std::basic_string - make_inline(const std::basic_string& val) - { - std::basic_string str; - str += '"'; - for(const auto& c : val) - { - if('\0' < c && c < '\31') - { - switch(c) - { - case '\b': str += "\\b"; break; - case '\t': str += "\\t"; break; - case '\n': str += "\\n"; break; - case '\f': str += "\\f"; break; - case '\r': str += "\\r"; break; - default: - { - str += 'u'; - std::basic_ostringstream oss; - oss << std::setw(4) << std::setfill('0') << std::hex - << static_cast(c); - auto hexdig = oss.str(); - std::transform(hexdig.begin(), hexdig.end(), hexdig.begin(), ::toupper); - str += oss.str(); - break; - } - } - } - else if(c == '"') - { - str += "\\\""; - } - else if(c == '\\') - { - str += "\\\\"; - } - else - { - str += c; - } - } - str += '"'; - return str; - } - - std::basic_string - convert_multiline(std::basic_string&& val) - { - std::basic_string str; str.reserve(val.size() + 6); - str += "\"\"\"\n"; - std::size_t current = 0; - for(auto iter = val.begin()+1; iter != val.end()-1; ++iter) - { - if(*iter != '\\') - { - if(current + 1 == max_length) str += "\\\n"; - str += *iter; continue; - } - assert(std::next(iter) < val.end()-1); - if(*std::next(iter) == 'u') - { - if(current + 5 == max_length) str += "\\\n"; - assert(iter + 5 < val.end()-1); - str += *iter; ++iter; // u - str += *iter; ++iter; // 0 - str += *iter; ++iter; // 1 - str += *iter; ++iter; // 2 - str += *iter; continue;// 3 - } - if(current + 2 == max_length) str += "\\\n"; - str += *iter; ++iter; str += *iter; - } - str += "\"\"\""; - return str; - } - -}; - -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::basic_string - operator()(const type& val) - { - std::basic_ostringstream oss; - oss << val; - return oss.str(); - } -}; - -// TODO max length! -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::size_t max_length; - - format_impl() : max_length(80){} - format_impl(std::size_t mx) : max_length(mx){} - - std::basic_string - operator()(const type& val) - { - std::basic_string retval; - retval += '['; - for(const auto& item : val) - { - auto tmp = format(val, max_length - 1); - retval += tmp; - retval += ", "; - if(tmp.size() * 2 > max_length) retval += '\n'; - } - retval += ']'; - return retval; - } -}; - -// TODO max length && inline! -template<> struct format_impl -{ - typedef detail::toml_default_type::type type; - - std::size_t max_length; - - format_impl() : max_length(80){} - format_impl(std::size_t mx) : max_length(mx){} - - std::basic_string - operator()(const type& val) - { - std::basic_string retval; - for(const auto& item : val) - { - retval += item.first; - retval += " = "; - retval += format(item.second); - retval += '\n'; - } - return retval; - } -}; - -template -std::basic_string -format(const value& v) -{ - switch(v.type()) - { - case value_t::Boolean : return format_impl{}(v.template cast()); - case value_t::Integer : return format_impl{}(v.template cast()); - case value_t::Float : return format_impl{}(v.template cast()); - case value_t::String : return format_impl{}(v.template cast()); - case value_t::Datetime: return format_impl{}(v.template cast()); - case value_t::Array : return format_impl{}(v.template cast()); - case value_t::Table : return format_impl{}(v.template cast()); - case value_t::Empty : throw std::runtime_error("toml::format: empty value"); - case value_t::Unknown : throw std::runtime_error("toml::format: unknown value"); - default: throw std::logic_error("toml::format: unknown enum value"); - } -} - -template -std::basic_string -format(const value& v, std::size_t inl) -{ - switch(v.type()) - { - case value_t::Boolean : return format_impl{}(v.template cast()); - case value_t::Integer : return format_impl{}(v.template cast()); - case value_t::Float : return format_impl{}(v.template cast()); - case value_t::String : return format_impl{inl}(v.template cast()); - case value_t::Datetime: return format_impl{}(v.template cast()); - case value_t::Array : return format_impl{inl}(v.template cast()); - case value_t::Table : return format_impl{inl}(v.template cast()); - case value_t::Empty : throw std::runtime_error("toml::format: empty value"); - case value_t::Unknown : throw std::runtime_error("toml::format: unknown value"); - default: throw std::logic_error("toml::format: unknown enum value"); - } -} - -template -std::basic_string -format(std::basic_string key, const value& val) -{ - std::basic_string retval(std::move(key)); - retval += " = "; - retval += format(val); - return retval; -} - -template -std::basic_string -format(std::basic_string key, const value& val, std::size_t mk) -{ - std::basic_string retval(std::move(key)); - retval += " = "; - retval += format(val, mk); - return retval; -} - - -// ----------------------------- stream operators ----------------------------- - -namespace detail -{ - -template -struct inline_limit -{ - static_assert(std::is_same::value, "do not instantiate this"); - static const int index; - T limit; - inline_limit() = default; - ~inline_limit() = default; - constexpr inline_limit(T i): limit(i){} - constexpr operator T() const {return limit;} - - static void callback(std::ios_base::event ev, std::ios_base& ios, int idx) - { - void*& info = ios.pword(idx); - switch (ev) - { - case std::ios_base::erase_event: - { - delete static_cast(info); - break; - } - case std::ios_base::copyfmt_event: - { - info = new std::size_t(*static_cast(info)); - break; - } - case std::ios_base::imbue_event: - { - break; - } - } - } -}; - -template -const int inline_limit::index = std::ios_base::xalloc(); - -} //detail - -template> -std::basic_ostream& -operator<<(std::basic_ostream& os, - const detail::inline_limit& inl) -{ - void*& info = os.pword(detail::inline_limit::index); - if(!os.bad()) - { - if(info == nullptr) - { - os.register_callback(detail::inline_limit::callback, - detail::inline_limit::index); - info = new std::size_t(inl.limit); - } - else - { - *static_cast(info) = inl.limit; - } - } - return os; -} - -constexpr static detail::inline_limit forceinline( - std::numeric_limits::max()); - -inline detail::inline_limit make_inline(std::size_t sz) -{ - return detail::inline_limit(sz); -} - -template> -std::basic_ostream& -operator<<(std::basic_ostream& os, - const toml::value& v) -{ - std::size_t* info = - static_cast(os.pword(detail::inline_limit::index)); - return os << (info == nullptr ? toml::format(v) : toml::format(v, *info)); -} - -} -#endif // TOML11_FORMAT From 8e154cdd74c6f449522c85d480db3563ff1cbd8c Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 20:55:30 +0900 Subject: [PATCH 103/133] add test case for parsing datetime --- tests/CMakeLists.txt | 1 + tests/test_parse_datetime.cpp | 133 ++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 tests/test_parse_datetime.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 977382e..266a43c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,7 @@ set(TEST_NAMES test_parse_integer test_parse_floating test_parse_string + test_parse_datetime test_parse_array test_parse_inline_table test_parse_key diff --git a/tests/test_parse_datetime.cpp b/tests/test_parse_datetime.cpp new file mode 100644 index 0000000..7a4e2e7 --- /dev/null +++ b/tests/test_parse_datetime.cpp @@ -0,0 +1,133 @@ +#define BOOST_TEST_MODULE "parse_datetime_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_time) +{ + TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00", toml::local_time(7, 32, 0)); + TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.99", toml::local_time(7, 32, 0, 990, 0)); + TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.999", toml::local_time(7, 32, 0, 999, 0)); + TOML11_TEST_PARSE_EQUAL(parse_local_time, "07:32:00.999999", toml::local_time(7, 32, 0, 999, 999)); +} + +BOOST_AUTO_TEST_CASE(test_time_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00", toml::value(toml::local_time(7, 32, 0))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.99", toml::value(toml::local_time(7, 32, 0, 990, 0))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.999", toml::value(toml::local_time(7, 32, 0, 999, 0))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "07:32:00.999999", toml::value(toml::local_time(7, 32, 0, 999, 999))); +} + +BOOST_AUTO_TEST_CASE(test_date) +{ + TOML11_TEST_PARSE_EQUAL(parse_local_date, "1979-05-27", + toml::local_date(1979, toml::month_t::May, 27)); +} +BOOST_AUTO_TEST_CASE(test_date_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27", + value(toml::local_date(1979, toml::month_t::May, 27))); +} + +BOOST_AUTO_TEST_CASE(test_datetime) +{ + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00.99", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27T07:32:00.999999", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); + + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00.99", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27t07:32:00.999999", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); + + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00.99", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0))); + TOML11_TEST_PARSE_EQUAL(parse_local_datetime, "1979-05-27 07:32:00.999999", + toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999))); +} + +BOOST_AUTO_TEST_CASE(test_datetime_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); + + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00.99", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27t07:32:00.999999", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); + + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00.99", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 990, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27 07:32:00.999999", + toml::value(toml::local_datetime(toml::local_date(1979, toml::month_t::May, 27), toml::local_time(7, 32, 0, 999, 999)))); +} + +BOOST_AUTO_TEST_CASE(test_offset_datetime) +{ + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00Z", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0))); + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.99Z", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 990, 0), toml::time_offset(0, 0))); + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.999999Z", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 999, 999), toml::time_offset(0, 0))); + + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00+09:00", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(9, 0))); + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.99+09:00", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 990, 0), toml::time_offset(9, 0))); + TOML11_TEST_PARSE_EQUAL(parse_offset_datetime, "1979-05-27T07:32:00.999999+09:00", + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 999, 999), toml::time_offset(9, 0))); +} + +BOOST_AUTO_TEST_CASE(test_offset_datetime_value) +{ + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00Z", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99Z", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 990, 0), toml::time_offset(0, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999Z", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 999, 999), toml::time_offset(0, 0)))); + + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00+09:00", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(9, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.99+09:00", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 990, 0), toml::time_offset(9, 0)))); + TOML11_TEST_PARSE_EQUAL_VALUE(parse_value, "1979-05-27T07:32:00.999999+09:00", + toml::value(toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0, 999, 999), toml::time_offset(9, 0)))); +} From f64430af9219e1c980a3afbc4a042dd4edac9d3b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 20:55:58 +0900 Subject: [PATCH 104/133] remove old test; individual test cases are added test_parse_* is now available, old test_parser is not needed now --- tests/CMakeLists.txt | 1 - tests/test_parser.cpp | 935 ------------------------------------------ 2 files changed, 936 deletions(-) delete mode 100644 tests/test_parser.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 266a43c..d56e73b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,7 +24,6 @@ set(TEST_NAMES # test_to_toml # test_from_toml # test_get_or -# test_parser # test_parse_file # test_parse_unicode ) diff --git a/tests/test_parser.cpp b/tests/test_parser.cpp deleted file mode 100644 index 32ca96f..0000000 --- a/tests/test_parser.cpp +++ /dev/null @@ -1,935 +0,0 @@ -#define BOOST_TEST_MODULE "test_parser" -#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST -#include -#else -#define BOOST_TEST_NO_LIB -#include -#endif -#include -#include -#include -#include - - -BOOST_AUTO_TEST_CASE(test_parse_basic_inline_string) -{ - typedef toml::parse_basic_inline_string parser; - typedef toml::is_basic_inline_string acceptor; - { - const std::string source("\"simple\""); - const std::string expected("simple"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("\"I'm a string. \\\"You can quote me\\\". Name\\tJos\\u00E9\\nLocation\\tSF.\""); - const std::string expected("I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_basic_multiline_string) -{ - typedef toml::parse_basic_multiline_string parser; - typedef toml::is_basic_multiline_string acceptor; - { - //XXX ifdef windows platform - const std::string source("\"\"\"\nRoses are red\nViolets are blue\"\"\""); - const std::string expected("Roses are red\nViolets are blue"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("\"\"\"\nThe quick brown \\\n\n fox jumps over \\\n the lazy dog.\"\"\""); - const std::string expected("The quick brown fox jumps over the lazy dog."); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("\"\"\"\nThe quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\""); - const std::string expected("The quick brown fox jumps over the lazy dog."); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_literal_inline_string) -{ - typedef toml::parse_literal_inline_string parser; - typedef toml::is_literal_inline_string acceptor; - { - const std::string source("'C:\\Users\\nodejs\\templates'"); - const std::string expected("C:\\Users\\nodejs\\templates"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'\\\\ServerX\\admin$\\system32\\'"); - const std::string expected("\\\\ServerX\\admin$\\system32\\"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'Tom \"Dubs\" Preston-Werner'"); - const std::string expected("Tom \"Dubs\" Preston-Werner"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'<\\i\\c*\\s*>'"); - const std::string expected("<\\i\\c*\\s*>"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_literal_multiline_string) -{ - typedef toml::parse_literal_multiline_string parser; - typedef toml::is_literal_multiline_string acceptor; - { - const std::string source("'''I [dw]on't need \\d{2} apples'''"); - const std::string expected("I [dw]on't need \\d{2} apples"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'''\nThe first newline is \ntrimmed in raw strings.\n All other whitespace\n is preserved.'''"); - const std::string expected("The first newline is \ntrimmed in raw strings.\n All other whitespace\n is preserved."); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_string) -{ - typedef toml::parse_string parser; - typedef toml::is_string acceptor; - { - const std::string source("\"string\""); - const std::string expected("string"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("\"\"\"string\"\"\""); - const std::string expected("string"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'string'"); - const std::string expected("string"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("'''string'''"); - const std::string expected("string"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_integer) -{ - typedef toml::parse_integer parser; - typedef toml::is_integer acceptor; - { - const std::string source("42"); - const toml::Integer expected(42); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("+42"); - const toml::Integer expected(42); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-42"); - const toml::Integer expected(-42); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-4_2"); - const toml::Integer expected(-42); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_float) -{ - typedef toml::parse_float parser; - typedef toml::is_float acceptor; - { - const std::string source("42.0"); - const toml::Float expected(42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("+42.0"); - const toml::Float expected(42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-42.0"); - const toml::Float expected(-42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-4_2.0"); - const toml::Float expected(-42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-42e0"); - const toml::Float expected(-42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("-42.0e0"); - const toml::Float expected(-42.0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } - { - const std::string source("42"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_boolean) -{ - typedef toml::parse_boolean parser; - typedef toml::is_boolean acceptor; - { - const std::string source("true"); - const toml::Boolean expected(true); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("false"); - const toml::Boolean expected(false); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("dummy"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - - - -BOOST_AUTO_TEST_CASE(test_parse_local_time) -{ - typedef toml::parse_local_time parser; - typedef toml::is_local_time acceptor; - { - const std::string source("12:34:56"); - const toml::Datetime expected(12, 34, 56, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("12:34:56.7"); - const toml::Datetime expected(12, 34, 56, 700, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("12:34:56.7891"); - const toml::Datetime expected(12, 34, 56, 789, 100); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("10"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_local_date) -{ - typedef toml::parse_local_date parser; - typedef toml::is_local_date acceptor; - { - const std::string source("1979-09-27"); - const toml::Datetime expected(1979, 9, 27); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("10"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_local_date_time) -{ - typedef toml::parse_local_date_time parser; - typedef toml::is_local_date_time acceptor; - { - const std::string source("1979-09-27T12:34:56"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56.789000"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1000-11-11"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_offset_date_time) -{ - typedef toml::parse_offset_date_time parser; - typedef toml::is_offset_date_time acceptor; - { - const std::string source("1979-09-27T12:34:56Z"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56.789000Z"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("1979-09-27T12:34:56+07:30"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 7, 30); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56.789000+07:30"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, 7, 30); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("1979-09-27T12:34:56-07:30"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, -7, -30); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56.789000-07:30"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 789, 0, -7, -30); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1000-11-11"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_datetime) -{ - typedef toml::parse_datetime parser; - typedef toml::is_datetime acceptor; - { - const std::string source("1979-09-27T12:34:56Z"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27T12:34:56"); - const toml::Datetime expected(1979, 9, 27, 12, 34, 56, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("1979-09-27"); - const toml::Datetime expected(1979, 9, 27); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("12:34:56"); - const toml::Datetime expected(12, 34, 56, 0, 0); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK_EQUAL(result.first.unwrap(), expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("12"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - - -BOOST_AUTO_TEST_CASE(test_parse_array) -{ - typedef toml::parse_array parser; - typedef toml::is_array acceptor; - { - const std::string source("[1,2,3]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[1, 2, 3]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ 1,2,3 ]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ 1 , 2 , 3 ]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ 1 \n,#comment\n 2 ,\n 3\n ]"); - const toml::Array expected{1, 2, 3}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ # empty array\n ]"); - const toml::Array expected{}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[ \"] \", ' # ', \n']', # ] \n]"); - const toml::Array expected{"] ", " # ", "]"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("[ \"Test #11 ]proved that\", 'Experiment #9 was a success' ]"); - const toml::Array expected{"Test #11 ]proved that", "Experiment #9 was a success"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("[ \"Test #11 ]proved that\", 'Experiment #9 was a success' ]"); - const toml::Array expected{"Test #11 ]proved that", "Experiment #9 was a success"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("[ [1,2,3] , ['a', 'b', 'c'] ]"); - const toml::Array expected{{1,2,3}, {"a", "b", "c"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("[ {foo=1}, {foo=1, bar=2.0}, {foo=1, bar=2.0, baz='str'} ]"); - const toml::Array expected{{{"foo", 1}}, {{"foo", 1}, {"bar", 2.0}}, {{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[dummy]"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - - -BOOST_AUTO_TEST_CASE(test_parse_inline_table) -{ - typedef toml::parse_inline_table parser; - typedef toml::is_inline_table acceptor; - { - const std::string source("{foo=1,bar=2.0,baz='str'}"); - const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("{ foo=1, bar=2.0, baz='str' }"); - const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("{ foo = 1, bar = 2.0, baz = 'str' }"); - const toml::Table expected{{"foo", 1}, {"bar", 2.0}, {"baz", "str"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - - { - const std::string source("{b=true, i=1, f=2.0, d=1907-03-02T07:32:00, s='str', a=[1,2,3], t={foo=1}}"); - const toml::Table expected{{"b", true}, {"i", 1}, {"f", 2.0}, - {"d", toml::Datetime(1907,3,2,7,32,0,0,0)}, - {"s", "str"}, {"a", {1, 2, 3}}, - {"t", {{"foo", 1}}}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("{dummy}"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(!result.first.is_ok()); - BOOST_CHECK(result.second == source.begin()); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_barekey) -{ - typedef toml::parse_barekey parser; - typedef toml::is_barekey acceptor; - { - const std::string source("hoge"); - const toml::key expected("hoge"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("bare-key"); - const toml::key expected("bare-key"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("bare_key"); - const toml::key expected("bare_key"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("42"); - const toml::key expected("42"); - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } -} - - - -BOOST_AUTO_TEST_CASE(test_key_value_pair) -{ - typedef toml::parse_key_value_pair parser; - typedef toml::is_key_value_pair acceptor; - { - const std::string source("key=1"); - const std::pair expected{"key", 1}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key =\t1"); - const std::pair expected{"key", 1}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = true"); - const std::pair expected{"key", true}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = -42"); - const std::pair expected{"key", -42}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = -42.0"); - const std::pair expected{"key", -42.}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = \"string\""); - const std::pair expected{"key", "string"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = 1901-01-01T00:00:00"); - const std::pair expected{"key", toml::Datetime(1901, 1,1,0,0,0,0,0)}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = [1,2,3]"); - const std::pair expected{"key", {1,2,3}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("key = {foo=1,bar=2.0,baz='3'}"); - const std::pair expected{"key", - {{"foo", 1}, {"bar", 2.0}, {"baz", "3"}}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } -} - -BOOST_AUTO_TEST_CASE(test_table_definition) -{ - typedef toml::parse_table_definition parser; - typedef toml::is_table_definition acceptor; - { - const std::string source("[foo]"); - const std::vector expected{"foo"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[foo.bar.baz]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[foo . bar. baz]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[foo . \"bar\" . baz]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[foo . \"b\\tar\" . baz]"); - const std::vector expected{"foo", "b\tar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } -} - -BOOST_AUTO_TEST_CASE(test_array_of_table_definition) -{ - typedef toml::parse_array_of_table_definition parser; - typedef toml::is_array_of_table_definition acceptor; - { - const std::string source("[[foo]]"); - const std::vector expected{"foo"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[[foo.bar.baz]]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[[foo . bar. baz]]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[[foo . \"bar\" . baz]]"); - const std::vector expected{"foo", "bar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } - { - const std::string source("[[foo . \"b\\tar\" . baz]]"); - const std::vector expected{"foo", "b\tar", "baz"}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result.first.is_ok()); - BOOST_CHECK(result.first.unwrap() == expected); - BOOST_CHECK(result.second == acceptor::invoke(source.begin(), source.end())); - } -} - -BOOST_AUTO_TEST_CASE(test_parse_data) -{ - typedef toml::parse_data parser; - { - const std::string source("#hogehoge"); - const toml::Table expected{}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("key = 'value'"); - const toml::Table expected{{"key", "value"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("key = 'value' #hoge"); - const toml::Table expected{{"key", "value"}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("[table]\nkey = 'value' #hoge"); - const toml::Table expected{{"table", {{"key", "value"}} }}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("[table]\n\tkey = 'value'\n\t#hoge"); - const toml::Table expected{{"table", {{"key", "value"}} }}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("[table]\n\tkey = 'value'\n\t#hoge"); - const toml::Table expected{{"table", {{"key", "value"}} }}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } - { - const std::string source("[nested.table]\n\tkey = 'value'\n\t#hoge"); - const toml::Table expected{{"nested", {{"table", {{"key", "value"}}}}}}; - const auto result = parser::invoke(source.cbegin(), source.cend()); - BOOST_CHECK(result == expected); - } -} From a19c9b4a3980ecee774fcc0ec7c512e220cb4019 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 21:55:11 +0900 Subject: [PATCH 105/133] add test case for find & get --- tests/test_get.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_get.cpp b/tests/test_get.cpp index 8a01757..548f2dd 100644 --- a/tests/test_get.cpp +++ b/tests/test_get.cpp @@ -358,3 +358,35 @@ BOOST_AUTO_TEST_CASE(test_get_toml_offset_datetime) BOOST_CHECK_EQUAL(tm.tm_sec, 0); } } + + +BOOST_AUTO_TEST_CASE(test_find_and_get) +{ + { + toml::value v(true); + bool thrown = false; + try + { + toml::get(v, "key"); + } + catch(toml::type_error const& te) + { + thrown = true; + } + BOOST_CHECK(thrown); + } + + { + toml::table v{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::get(v, "num")); + toml::get(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::get(v, "num")); + } + + { + toml::value v = toml::table{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::get(v, "num")); + toml::get(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::get(v, "num")); + } +} From 47cd6f5a412bdf8f499a556718a60ead48f07f35 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 23:17:28 +0900 Subject: [PATCH 106/133] remove redundant error message --- toml/parser.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 15954a7..a965e1e 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -884,8 +884,7 @@ parse_array(location& loc) } else { - return err("[error] toml::parse_array: while reading an element of " - "an array\n" + val.unwrap_err()); + return err(val.unwrap_err()); } using lex_array_separator = sequence, character<','>>; From c69969733fcd7b56eb4dcc1d1b70e7010def3594 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 23:22:31 +0900 Subject: [PATCH 107/133] use carriage return depending on env --- toml/region.hpp | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/toml/region.hpp b/toml/region.hpp index b7e4abc..fb06915 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -201,18 +201,25 @@ inline std::string format_underline(const std::string& message, const region_base& reg, const std::string& comment_for_underline, std::vector helps = {}) { +#ifdef _WIN32 + const auto newline = "\r\n"; +#else + const char newline = '\n'; +#endif const auto line = reg.line(); const auto line_number = reg.line_num(); std::string retval; retval += message; - retval += "\n --> "; + retval += newline; + retval += " --> "; retval += reg.name(); - retval += "\n "; + retval += newline; + retval += ' '; retval += line_number; retval += " | "; retval += line; - retval += '\n'; + retval += newline; retval += make_string(line_number.size() + 1, ' '); retval += " | "; retval += make_string(reg.before(), ' '); @@ -221,12 +228,13 @@ inline std::string format_underline(const std::string& message, retval += comment_for_underline; if(helps.size() != 0) { - retval += '\n'; + retval += newline; retval += make_string(line_number.size() + 1, ' '); retval += " | "; for(const auto help : helps) { - retval += "\nHint: "; + retval += newline; + retval += "Hint: "; retval += help; } } @@ -240,6 +248,11 @@ format_underline(const std::string& message, const location& loc, const std::string& comment_for_underline, std::vector helps = {}) { +#ifdef _WIN32 + const auto newline = "\r\n"; +#else + const char newline = '\n'; +#endif using const_iterator = typename location::const_iterator; using reverse_iterator = std::reverse_iterator; const auto line_begin = std::find(reverse_iterator(loc.iter()), @@ -252,13 +265,15 @@ format_underline(const std::string& message, const location& loc, std::string retval; retval += message; - retval += "\n --> "; + retval += newline; + retval += " --> "; retval += loc.name(); - retval += "\n "; + retval += newline; + retval += ' '; retval += line_number; retval += " | "; retval += make_string(line_begin, line_end); - retval += '\n'; + retval += newline; retval += make_string(line_number.size() + 1, ' '); retval += " | "; retval += make_string(std::distance(line_begin, loc.iter()),' '); @@ -268,12 +283,13 @@ format_underline(const std::string& message, const location& loc, retval += comment_for_underline; if(helps.size() != 0) { - retval += '\n'; + retval += newline; retval += make_string(line_number.size() + 1, ' '); retval += " | "; for(const auto help : helps) { - retval += "\nHint: "; + retval += newline; + retval += "Hint: "; retval += help; } } From 5bfbbe35a6ac2274b05fa719a0b797040a9f9b54 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Wed, 12 Dec 2018 23:23:59 +0900 Subject: [PATCH 108/133] update README --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3228adf..e07a85a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ c++11 header-only toml parser depending only on c++ standard library. compatible to the latest version of TOML v0.5.0 after version 2.0.0. -Are you looking for pre-C++11 compatible toml parser? Check out [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and work with C++98 & Boost. +Are you looking for pre-C++11 compatible toml parser? Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and work with C++98 & Boost. ## How to use @@ -54,7 +54,7 @@ To show a better error message, it is recommended to pass filename with `istream If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`. -toml11 now has clean error messages inspired the Rust compiler and it looks like the following (comment after hash sign are actually not shown). +toml11 now has clean and informative error messages inspired by Rust and it looks like the following (comment after hash sign are actually not shown). ```console terminate called after throwing an instance of 'toml::syntax_error' @@ -64,9 +64,7 @@ terminate called after throwing an instance of 'toml::syntax_error' | ^------ expected newline, but got '='. # error reason ``` -It shows almost all the information to fix the error in the toml file like file name, line number, the line that is invalid, and the reason why it fails. - -Since the error message generation is generally a difficult task, the current status is not ideal. We need your help. If you encounter a weird error message, please let us know and improve the quality! +Since the error message generation is generally a difficult task, the current status is not ideal. toml11 need your help. If you encounter a weird error message, please let us know and improve the quality! ### getting toml value @@ -97,6 +95,9 @@ toml::get(data["answer"]) = 6 * 9; std::cout << toml::get(data.at("answer")) << std::endl; // 54 ``` +If the specified type requires conversion, you can't take a reference to the +return value. See also [underlying types](#underlying-types) + #### passing invalid type to toml::get If you choose the invalid type, `toml::type_error` will be thrown. Similar to the `syntax_error`, toml11 also displays informative error message. The error message when you choose `int` to get `string` value would be like this. From 83b588a8c86387af091552d010d7507ea5cb5d34 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 00:35:05 +0900 Subject: [PATCH 109/133] rename test code --- tests/{test_get_or.cpp => test_get_related_func.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_get_or.cpp => test_get_related_func.cpp} (100%) diff --git a/tests/test_get_or.cpp b/tests/test_get_related_func.cpp similarity index 100% rename from tests/test_get_or.cpp rename to tests/test_get_related_func.cpp From d370ae7d0d8d00231046bb739339a09013c7131f Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 00:35:43 +0900 Subject: [PATCH 110/133] set tm_isdst as negative value --- tests/test_get.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/test_get.cpp b/tests/test_get.cpp index 548f2dd..4f75651 100644 --- a/tests/test_get.cpp +++ b/tests/test_get.cpp @@ -271,7 +271,8 @@ BOOST_AUTO_TEST_CASE(test_get_toml_table) BOOST_AUTO_TEST_CASE(test_get_toml_local_date) { toml::value v1(toml::local_date{2018, toml::month_t::Apr, 1}); - const auto date = toml::get(v1); + const auto date = std::chrono::system_clock::to_time_t( + toml::get(v1)); std::tm t; t.tm_year = 2018 - 1900; @@ -280,8 +281,9 @@ BOOST_AUTO_TEST_CASE(test_get_toml_local_date) t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0; - const auto c = std::chrono::system_clock::from_time_t(std::mktime(&t)); - BOOST_CHECK(c == date); + t.tm_isdst = -1; + const auto c = std::mktime(&t); + BOOST_CHECK_EQUAL(c, date); } BOOST_AUTO_TEST_CASE(test_get_toml_local_time) @@ -298,7 +300,8 @@ BOOST_AUTO_TEST_CASE(test_get_toml_local_datetime) toml::local_date{2018, toml::month_t::Apr, 1}, toml::local_time{12, 30, 45})); - const auto date = toml::get(v1); + const auto date = std::chrono::system_clock::to_time_t( + toml::get(v1)); std::tm t; t.tm_year = 2018 - 1900; t.tm_mon = 4 - 1; @@ -306,8 +309,9 @@ BOOST_AUTO_TEST_CASE(test_get_toml_local_datetime) t.tm_hour = 12; t.tm_min = 30; t.tm_sec = 45; - const auto c = std::chrono::system_clock::from_time_t(std::mktime(&t)); - BOOST_CHECK(c == date); + t.tm_isdst = -1; + const auto c = std::mktime(&t); + BOOST_CHECK_EQUAL(c, date); } BOOST_AUTO_TEST_CASE(test_get_toml_offset_datetime) From c15cb15c4c799e2c370978e479b9ec8f6df8af47 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 00:38:04 +0900 Subject: [PATCH 111/133] simplify to_toml implementation --- toml/to_toml.hpp | 78 ++++++++++++------------------------------------ 1 file changed, 19 insertions(+), 59 deletions(-) diff --git a/toml/to_toml.hpp b/toml/to_toml.hpp index 9b8cde7..b8a5c29 100644 --- a/toml/to_toml.hpp +++ b/toml/to_toml.hpp @@ -5,76 +5,36 @@ namespace toml { -template::value, std::nullptr_t>::type = nullptr> -inline value to_toml(const T& x) +template +inline value to_toml(T&& x) { - return value(x); -} - -template>, std::is_integral - >::value, std::nullptr_t>::type = nullptr> -inline value to_toml(const T& x) -{ - return value(::toml::Integer(x)); -} - -template>, std::is_floating_point - >::value, std::nullptr_t>::type = nullptr> -inline value to_toml(const T& x) -{ - return value(::toml::Float(x)); -} - -inline value to_toml(const char* str) -{ - return value(::toml::String(str)); -} - -template>, detail::is_container - >::value, std::nullptr_t>::type = nullptr> -value to_toml(const T& x) -{ - Array tmp; - tmp.reserve(std::distance(std::begin(x), std::end(x))); - for(auto iter = std::begin(x); iter != std::end(x); ++iter) - { - tmp.emplace_back(*iter); - } - return value(std::move(tmp)); -} - -template>, detail::is_map - >::value, std::nullptr_t>::type = nullptr> -value to_toml(const T& x) -{ - Table tmp; - for(auto iter = std::begin(x); iter != std::end(x); ++iter) - { - tmp.emplace(iter->first, to_toml(iter->second)); - } - return value(std::move(tmp)); + return value(std::forward(x)); } template -inline value to_toml(std::initializer_list init) +inline value to_toml(T&& x, string_t kind) { - return value(std::move(init)); + return value(std::forward(x), kind); } -inline value to_toml(std::initializer_list> init) +inline value to_toml(local_date d, local_time t) { - return value(std::move(init)); + return value(local_datetime(d, t)); +} +inline value to_toml(local_date d, local_time t, time_offset ofs) +{ + return value(offset_datetime(d, t, ofs)); } -template -inline value to_toml(const value& x) +template +inline value to_toml(Ts&& ... xs) { - return x; + return value(toml::array{toml::value(std::forward(xs)) ... }); +} + +inline value to_toml(std::initializer_list> xs) +{ + return value(toml::table(xs.begin(), xs.end())); } } // toml From 2080b301109fb55e385f6116d61d70dca7995758 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 00:38:24 +0900 Subject: [PATCH 112/133] add test cases for test_to_toml --- tests/CMakeLists.txt | 3 +- tests/test_to_toml.cpp | 228 ++++++++++++++++++++++++++++------------- 2 files changed, 157 insertions(+), 74 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d56e73b..2140d54 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,8 +20,7 @@ set(TEST_NAMES test_parse_key test_parse_table_key test_get - -# test_to_toml + test_to_toml # test_from_toml # test_get_or # test_parse_file diff --git a/tests/test_to_toml.cpp b/tests/test_to_toml.cpp index dca9799..464a760 100644 --- a/tests/test_to_toml.cpp +++ b/tests/test_to_toml.cpp @@ -9,97 +9,181 @@ #include #include -BOOST_AUTO_TEST_CASE(test_to_toml_exact) +BOOST_AUTO_TEST_CASE(test_value_boolean) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - auto v1 = toml::to_toml(b); - auto v2 = toml::to_toml(i); - auto v3 = toml::to_toml(f); - auto v4 = toml::to_toml(s); - auto v5 = toml::to_toml(d); - auto v6 = toml::to_toml(a); - auto v7 = toml::to_toml(t); + toml::value v1 = toml::to_toml(true); + toml::value v2 = toml::to_toml(false); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); - BOOST_CHECK_EQUAL(v5.type(), toml::value_t::Datetime); - BOOST_CHECK_EQUAL(v6.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v7.type(), toml::value_t::Table); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Boolean); + BOOST_CHECK(v1.is(toml::value_t::Boolean)); + BOOST_CHECK(v2.is(toml::value_t::Boolean)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); - BOOST_CHECK_EQUAL(v1.cast(), b); - BOOST_CHECK_EQUAL(v2.cast(), i); - BOOST_CHECK_EQUAL(v3.cast(), f); - BOOST_CHECK_EQUAL(v4.cast(), s); - const auto& ar = v6.cast(); - BOOST_CHECK_EQUAL(ar.at(0).cast(), a.at(0).cast()); - BOOST_CHECK_EQUAL(ar.at(1).cast(), a.at(1).cast()); - BOOST_CHECK_EQUAL(ar.at(2).cast(), a.at(2).cast()); - BOOST_CHECK_EQUAL(ar.at(3).cast(), a.at(3).cast()); - BOOST_CHECK_EQUAL(ar.at(4).cast(), a.at(4).cast()); + BOOST_CHECK_EQUAL(v1.cast(), true); + BOOST_CHECK_EQUAL(v2.cast(), false); } -BOOST_AUTO_TEST_CASE(test_to_toml_castable) +BOOST_AUTO_TEST_CASE(test_value_integer) { - auto v1 = toml::to_toml(true); - auto v2 = toml::to_toml(42ul); - auto v3 = toml::to_toml(3.14f); - auto v4 = toml::to_toml("hoge"); + toml::value v1 = toml::to_toml(-42); + toml::value v2 = toml::to_toml(42u); - BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Boolean); + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Integer); BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(v3.type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(v4.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::Integer)); + BOOST_CHECK(v2.is(toml::value_t::Integer)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); - BOOST_CHECK_EQUAL(v1.cast(), true); - BOOST_CHECK_EQUAL(v2.cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(v3.cast(), 3.14, 1e-5); - BOOST_CHECK_EQUAL(v4.cast(), "hoge"); + BOOST_CHECK_EQUAL(v1.cast(), -42); + BOOST_CHECK_EQUAL(v2.cast(), 42u); } -BOOST_AUTO_TEST_CASE(test_to_toml_initializer_list) +BOOST_AUTO_TEST_CASE(test_value_float) { - toml::value v1 = toml::to_toml({3,1,4,1,5}); - toml::value v2 = toml::to_toml({{"hoge", 1}, {"piyo", 3.14}, {"fuga", "string"}}); + toml::value v1 = toml::to_toml(3.14); + toml::value v2 = toml::to_toml(3.14f); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Float); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Float); + BOOST_CHECK(v1.is(toml::value_t::Float)); + BOOST_CHECK(v2.is(toml::value_t::Float)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), 3.14); + BOOST_CHECK_CLOSE_FRACTION(v2.cast(), 3.14, 1e-2); +} + +BOOST_AUTO_TEST_CASE(test_value_string) +{ + toml::value v1 = toml::to_toml(std::string("foo")); + toml::value v2 = toml::to_toml(std::string("foo"), toml::string_t::literal); + toml::value v3 = toml::to_toml("foo"); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::String); + BOOST_CHECK_EQUAL(v3.type(), toml::value_t::String); + BOOST_CHECK(v1.is(toml::value_t::String)); + BOOST_CHECK(v2.is(toml::value_t::String)); + BOOST_CHECK(v3.is(toml::value_t::String)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + BOOST_CHECK(v3.is()); + + BOOST_CHECK_EQUAL(v1.cast(), "foo"); + BOOST_CHECK_EQUAL(v2.cast(), "foo"); + BOOST_CHECK_EQUAL(v3.cast(), "foo"); +} + +BOOST_AUTO_TEST_CASE(test_value_local_date) +{ + toml::value v1 = toml::to_toml(toml::local_date(2018, toml::month_t::Jan, 31)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDate); + BOOST_CHECK(v1.is(toml::value_t::LocalDate)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_date(2018, toml::month_t::Jan, 31)); +} + +BOOST_AUTO_TEST_CASE(test_value_local_time) +{ + toml::value v1 = toml::to_toml(toml::local_time(12, 30, 45)); + toml::value v2 = toml::to_toml(std::chrono::hours(12) + std::chrono::minutes(30) + + std::chrono::seconds(45)); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalTime); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::LocalTime); + BOOST_CHECK(v1.is(toml::value_t::LocalTime)); + BOOST_CHECK(v2.is(toml::value_t::LocalTime)); + BOOST_CHECK(v1.is()); + BOOST_CHECK(v2.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_time(12, 30, 45)); + BOOST_CHECK_EQUAL(v2.cast(), + toml::local_time(12, 30, 45)); + BOOST_CHECK_EQUAL(v1.cast(), + v2.cast()); +} + +BOOST_AUTO_TEST_CASE(test_value_local_datetime) +{ + toml::value v1 = toml::to_toml(toml::local_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45) + )); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::LocalDatetime); + BOOST_CHECK(v1.is(toml::value_t::LocalDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::local_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45))); +} + +BOOST_AUTO_TEST_CASE(test_value_offset_datetime) +{ + toml::value v1 = toml::to_toml(toml::offset_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45), + toml::time_offset(9, 0) + )); + + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::OffsetDatetime); + BOOST_CHECK(v1.is(toml::value_t::OffsetDatetime)); + BOOST_CHECK(v1.is()); + + BOOST_CHECK_EQUAL(v1.cast(), + toml::offset_datetime( + toml::local_date(2018, toml::month_t::Jan, 31), + toml::local_time(12, 30, 45), + toml::time_offset(9, 0) + )); +} + +BOOST_AUTO_TEST_CASE(test_value_array) +{ + std::vector v{1,2,3,4,5}; + toml::value v1 = toml::to_toml(v); + toml::value v2 = toml::to_toml(6,7,8,9,0); BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Array); - BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Array)); + BOOST_CHECK(v1.is()); - const auto& ar = v1.cast(); - BOOST_CHECK_EQUAL(ar.at(0).cast(), 3); - BOOST_CHECK_EQUAL(ar.at(1).cast(), 1); - BOOST_CHECK_EQUAL(ar.at(2).cast(), 4); - BOOST_CHECK_EQUAL(ar.at(3).cast(), 1); - BOOST_CHECK_EQUAL(ar.at(4).cast(), 5); + BOOST_CHECK_EQUAL(v2.type(), toml::value_t::Array); + BOOST_CHECK(v2.is(toml::value_t::Array)); + BOOST_CHECK(v2.is()); - const auto& tb = v2.cast(); - - BOOST_CHECK_EQUAL(tb.at("hoge").type(), toml::value_t::Integer); - BOOST_CHECK_EQUAL(tb.at("piyo").type(), toml::value_t::Float); - BOOST_CHECK_EQUAL(tb.at("fuga").type(), toml::value_t::String); - - BOOST_CHECK_EQUAL(tb.at("hoge").cast(), 1); - BOOST_CHECK_CLOSE_FRACTION(tb.at("piyo").cast(), 3.14, 1e-3); - BOOST_CHECK_EQUAL(tb.at("fuga").cast(), "string"); + BOOST_CHECK_EQUAL(v1.cast().at(0).cast(), 1); + BOOST_CHECK_EQUAL(v1.cast().at(1).cast(), 2); + BOOST_CHECK_EQUAL(v1.cast().at(2).cast(), 3); + BOOST_CHECK_EQUAL(v1.cast().at(3).cast(), 4); + BOOST_CHECK_EQUAL(v1.cast().at(4).cast(), 5); + BOOST_CHECK_EQUAL(v2.cast().at(0).cast(), 6); + BOOST_CHECK_EQUAL(v2.cast().at(1).cast(), 7); + BOOST_CHECK_EQUAL(v2.cast().at(2).cast(), 8); + BOOST_CHECK_EQUAL(v2.cast().at(3).cast(), 9); + BOOST_CHECK_EQUAL(v2.cast().at(4).cast(), 0); } +BOOST_AUTO_TEST_CASE(test_value_table) +{ + toml::value v1 = toml::to_toml({{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}); + BOOST_CHECK_EQUAL(v1.type(), toml::value_t::Table); + BOOST_CHECK(v1.is(toml::value_t::Table)); + BOOST_CHECK(v1.is()); + BOOST_CHECK_EQUAL(v1.cast().at("foo").cast(), 42); + BOOST_CHECK_EQUAL(v1.cast().at("bar").cast(), 3.14); + BOOST_CHECK_EQUAL(v1.cast().at("baz").cast().str, "qux"); +} From 901c299c40fb30c8de38d7a68710ac60323bd74b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 01:28:55 +0900 Subject: [PATCH 113/133] add unwrap_or to result --- toml/result.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/toml/result.hpp b/toml/result.hpp index e0085f7..90ff1d5 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -394,6 +394,25 @@ struct result return std::move(this->succ.value); } + template + value_type& unwrap_or(U& opt) & + { + if(is_err()) {return opt;} + return this->succ.value; + } + template + value_type const& unwrap_or(U const& opt) const& + { + if(is_err()) {return opt;} + return this->succ.value; + } + template + value_type&& unwrap_or(U&& opt) && + { + if(is_err()) {return std::move(opt);} + return std::move(this->succ.value); + } + error_type& unwrap_err() & { if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} From affa159c826b69aea0aff901b8ce745fe81b1a78 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 01:29:23 +0900 Subject: [PATCH 114/133] add get_or(value, key, opt) --- toml/get.hpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/toml/get.hpp b/toml/get.hpp index c2aece5..69c7c0f 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -398,6 +398,7 @@ get_or(toml::value&& v, T&& opt) } } + template auto get_or(const toml::table& tab, const toml::key& ky, T&& opt) -> decltype(get_or(std::declval(), std::forward(opt))) @@ -420,6 +421,33 @@ auto get_or(toml::table&& tab, const toml::key& ky, T&& opt) return ::toml::get_or(std::move(tab[ky]), std::forward(opt)); } +template +auto get_or(const toml::value& v, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(v.type() != toml::value_t::Table){return std::forward(opt);} + const auto& tab = toml::get(v); + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(tab.at(ky), std::forward(opt)); +} +template +auto get_or(toml::value& v, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(v.type() != toml::value_t::Table){return std::forward(opt);} + auto& tab = toml::get(v); + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(tab[ky], std::forward(opt)); +} +template +auto get_or(toml::value&& v, const toml::key& ky, T&& opt) + -> decltype(get_or(std::declval(), std::forward(opt))) +{ + if(v.type() != toml::value_t::Table){return std::forward(opt);} + auto tab = toml::get(std::move(v)); + if(tab.count(ky) == 0) {return std::forward(opt);} + return ::toml::get_or(std::move(tab[ky]), std::forward(opt)); +} // ============================================================================ // expect From be1a310ae5fcd6dbce676f91dcf31a884cd467e5 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 01:30:06 +0900 Subject: [PATCH 115/133] move test for find to get_related --- tests/test_get.cpp | 31 -------- tests/test_get_related_func.cpp | 122 ++++++++++++++------------------ 2 files changed, 55 insertions(+), 98 deletions(-) diff --git a/tests/test_get.cpp b/tests/test_get.cpp index 4f75651..3dc753c 100644 --- a/tests/test_get.cpp +++ b/tests/test_get.cpp @@ -363,34 +363,3 @@ BOOST_AUTO_TEST_CASE(test_get_toml_offset_datetime) } } - -BOOST_AUTO_TEST_CASE(test_find_and_get) -{ - { - toml::value v(true); - bool thrown = false; - try - { - toml::get(v, "key"); - } - catch(toml::type_error const& te) - { - thrown = true; - } - BOOST_CHECK(thrown); - } - - { - toml::table v{{"num", 42}}; - BOOST_CHECK_EQUAL(42, toml::get(v, "num")); - toml::get(v, "num") = 54; - BOOST_CHECK_EQUAL(54, toml::get(v, "num")); - } - - { - toml::value v = toml::table{{"num", 42}}; - BOOST_CHECK_EQUAL(42, toml::get(v, "num")); - toml::get(v, "num") = 54; - BOOST_CHECK_EQUAL(54, toml::get(v, "num")); - } -} diff --git a/tests/test_get_related_func.cpp b/tests/test_get_related_func.cpp index 4ca863f..70ba1ab 100644 --- a/tests/test_get_related_func.cpp +++ b/tests/test_get_related_func.cpp @@ -12,77 +12,65 @@ #include #include - -BOOST_AUTO_TEST_CASE(test_get_or_exist) +BOOST_AUTO_TEST_CASE(test_find_and_get) { - toml::Boolean raw_v1(true); - toml::Integer raw_v2(42); - toml::Float raw_v3(3.14); - toml::String raw_v4("hoge"); - toml::Array raw_v5{2,7,1,8,2}; - toml::Table raw_v6{{"key", 42}}; + { + toml::value v(true); + bool thrown = false; + try + { + toml::get(v, "key"); + } + catch(toml::type_error const& te) + { + thrown = true; + } + BOOST_CHECK(thrown); + } - toml::value v1(raw_v1); - toml::value v2(raw_v2); - toml::value v3(raw_v3); - toml::value v4(raw_v4); - toml::value v5(raw_v5); - toml::value v6(raw_v6); + { + toml::table v{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::get(v, "num")); + toml::get(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::get(v, "num")); + } - toml::Table table{ - {"value1", v1}, - {"value2", v2}, - {"value3", v3}, - {"value4", v4}, - {"value5", v5}, - {"value6", v6} - }; - - toml::Boolean u1 = toml::get_or(table, "value1", raw_v1); - toml::Integer u2 = toml::get_or(table, "value2", raw_v2); - toml::Float u3 = toml::get_or(table, "value3", raw_v3); - toml::String u4 = toml::get_or(table, "value4", raw_v4); - toml::Array u5 = toml::get_or(table, "value5", raw_v5); - toml::Table u6 = toml::get_or(table, "value6", raw_v6); - - BOOST_CHECK_EQUAL(u1, raw_v1); - BOOST_CHECK_EQUAL(u2, raw_v2); - BOOST_CHECK_EQUAL(u3, raw_v3); - BOOST_CHECK_EQUAL(u4, raw_v4); - BOOST_CHECK_EQUAL(u5.at(0).cast(), raw_v5.at(0).cast()); - BOOST_CHECK_EQUAL(u5.at(1).cast(), raw_v5.at(1).cast()); - BOOST_CHECK_EQUAL(u5.at(2).cast(), raw_v5.at(2).cast()); - BOOST_CHECK_EQUAL(u5.at(3).cast(), raw_v5.at(3).cast()); - BOOST_CHECK_EQUAL(u5.at(4).cast(), raw_v5.at(4).cast()); - BOOST_CHECK_EQUAL(u6.at("key").cast(), 42); + { + toml::value v = toml::table{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::get(v, "num")); + toml::get(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::get(v, "num")); + } } -BOOST_AUTO_TEST_CASE(test_get_or_empty) +BOOST_AUTO_TEST_CASE(test_get_or) { - toml::Boolean raw_v1(true); - toml::Integer raw_v2(42); - toml::Float raw_v3(3.14); - toml::String raw_v4("hoge"); - toml::Array raw_v5{2,7,1,8,2}; - toml::Table raw_v6{{"key", 42}}; - - toml::Table table; // empty! - - toml::Boolean u1 = toml::get_or(table, std::string("value1"), raw_v1); - toml::Integer u2 = toml::get_or(table, std::string("value2"), raw_v2); - toml::Float u3 = toml::get_or(table, std::string("value3"), raw_v3); - toml::String u4 = toml::get_or(table, std::string("value4"), raw_v4); - toml::Array u5 = toml::get_or(table, std::string("value5"), raw_v5); - toml::Table u6 = toml::get_or(table, std::string("value6"), raw_v6); - - BOOST_CHECK_EQUAL(u1, raw_v1); - BOOST_CHECK_EQUAL(u2, raw_v2); - BOOST_CHECK_EQUAL(u3, raw_v3); - BOOST_CHECK_EQUAL(u4, raw_v4); - BOOST_CHECK_EQUAL(u5.at(0).cast(), raw_v5.at(0).cast()); - BOOST_CHECK_EQUAL(u5.at(1).cast(), raw_v5.at(1).cast()); - BOOST_CHECK_EQUAL(u5.at(2).cast(), raw_v5.at(2).cast()); - BOOST_CHECK_EQUAL(u5.at(3).cast(), raw_v5.at(3).cast()); - BOOST_CHECK_EQUAL(u5.at(4).cast(), raw_v5.at(4).cast()); - BOOST_CHECK_EQUAL(u6.at("key").cast(), 42); + { + toml::table v{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::get_or(v, "num", 0)); + BOOST_CHECK_EQUAL(0, toml::get_or(v, "foo", 0)); + } + { + toml::value v = toml::table{{"num", 42}}; + BOOST_CHECK_EQUAL(42, toml::get_or(v, "num", 0)); + BOOST_CHECK_EQUAL(0, toml::get_or(v, "foo", 0)); + } + { + toml::value v1(42); + toml::value v2(3.14); + BOOST_CHECK_EQUAL(42, toml::get_or(v1, 0)); + BOOST_CHECK_EQUAL(0, toml::get_or(v2, 0)); + } +} + +BOOST_AUTO_TEST_CASE(test_expect) +{ + { + toml::value v1(42); + toml::value v2(3.14); + BOOST_CHECK_EQUAL(42, toml::expect(v1).unwrap_or(0)); + BOOST_CHECK_EQUAL( 0, toml::expect(v2).unwrap_or(0)); + BOOST_CHECK_EQUAL("42", toml::expect(v1).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none"))); + BOOST_CHECK_EQUAL("none", toml::expect(v2).map([](int i){return std::to_string(i);}).unwrap_or(std::string("none"))); + } } From f62bcb3077b576490be691ae10f5bc989c2a4003 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 01:30:25 +0900 Subject: [PATCH 116/133] update cmakelists --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2140d54..2230013 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,9 +20,9 @@ set(TEST_NAMES test_parse_key test_parse_table_key test_get + test_get_related_func test_to_toml # test_from_toml -# test_get_or # test_parse_file # test_parse_unicode ) From 27a80b121415aa287d4f7c39e7c1121f43c3bc3b Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 02:00:13 +0900 Subject: [PATCH 117/133] rename get(table, key) to find() --- tests/test_get_related_func.cpp | 16 ++++++++-------- toml/get.hpp | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/test_get_related_func.cpp b/tests/test_get_related_func.cpp index 70ba1ab..2feebdd 100644 --- a/tests/test_get_related_func.cpp +++ b/tests/test_get_related_func.cpp @@ -12,14 +12,14 @@ #include #include -BOOST_AUTO_TEST_CASE(test_find_and_get) +BOOST_AUTO_TEST_CASE(test_find) { { toml::value v(true); bool thrown = false; try { - toml::get(v, "key"); + toml::find(v, "key"); } catch(toml::type_error const& te) { @@ -30,16 +30,16 @@ BOOST_AUTO_TEST_CASE(test_find_and_get) { toml::table v{{"num", 42}}; - BOOST_CHECK_EQUAL(42, toml::get(v, "num")); - toml::get(v, "num") = 54; - BOOST_CHECK_EQUAL(54, toml::get(v, "num")); + BOOST_CHECK_EQUAL(42, toml::find(v, "num")); + toml::find(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::find(v, "num")); } { toml::value v = toml::table{{"num", 42}}; - BOOST_CHECK_EQUAL(42, toml::get(v, "num")); - toml::get(v, "num") = 54; - BOOST_CHECK_EQUAL(54, toml::get(v, "num")); + BOOST_CHECK_EQUAL(42, toml::find(v, "num")); + toml::find(v, "num") = 54; + BOOST_CHECK_EQUAL(54, toml::find(v, "num")); } } diff --git a/toml/get.hpp b/toml/get.hpp index 69c7c0f..13d32d7 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -270,8 +270,8 @@ T get(const toml::value& v) template decltype(::toml::get(std::declval())) -get(const toml::table& tab, const toml::key& ky, - std::string tablename = "unknown table") +find(const toml::table& tab, const toml::key& ky, + std::string tablename = "unknown table") { if(tab.count(ky) == 0) { @@ -282,8 +282,8 @@ get(const toml::table& tab, const toml::key& ky, } template decltype(::toml::get(std::declval<::toml::value&>())) -get(toml::table& tab, const toml::key& ky, - std::string tablename = "unknown table") +find(toml::table& tab, const toml::key& ky, + std::string tablename = "unknown table") { if(tab.count(ky) == 0) { @@ -294,8 +294,8 @@ get(toml::table& tab, const toml::key& ky, } template decltype(::toml::get(std::declval<::toml::value&&>())) -get(toml::table&& tab, const toml::key& ky, - std::string tablename = "unknown table") +find(toml::table&& tab, const toml::key& ky, + std::string tablename = "unknown table") { if(tab.count(ky) == 0) { @@ -307,7 +307,7 @@ get(toml::table&& tab, const toml::key& ky, template decltype(::toml::get(std::declval())) -get(const toml::value& v, const toml::key& ky) +find(const toml::value& v, const toml::key& ky) { const auto& tab = ::toml::get(v); if(tab.count(ky) == 0) @@ -320,7 +320,7 @@ get(const toml::value& v, const toml::key& ky) } template decltype(::toml::get(std::declval<::toml::value&>())) -get(toml::value& v, const toml::key& ky) +find(toml::value& v, const toml::key& ky) { auto& tab = ::toml::get(v); if(tab.count(ky) == 0) @@ -333,7 +333,7 @@ get(toml::value& v, const toml::key& ky) } template decltype(::toml::get(std::declval<::toml::value&&>())) -get(toml::value&& v, const toml::key& ky) +find(toml::value&& v, const toml::key& ky) { auto tab = ::toml::get(std::move(v)); if(tab.count(ky) == 0) From 77b237c53a67d23fe49f1f0f98ad6024c7144412 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 02:00:42 +0900 Subject: [PATCH 118/133] update README --- README.md | 78 +++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index e07a85a..fa4a5b8 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ std::cout << toml::get(data.at("answer")) << std::endl; // 54 ``` If the specified type requires conversion, you can't take a reference to the -return value. See also [underlying types](#underlying-types) +return value. See also [underlying types](#underlying-types). #### passing invalid type to toml::get @@ -112,44 +112,6 @@ terminate called after throwing an instance of 'toml::type_error' NOTE: In order to show this kind of error message, all the toml values has 1 shared_ptr that points the corresponding byte sequence and 2 iterator that points the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save the memory resource. -#### finding value from table - -toml11 provides overloads to find a value from `toml::table`. Of course, you can do this in your own way with `toml::get` because it just searches and returns a value. - -```cpp -const auto data = toml::parse("example.toml"); -const auto num = toml::get(data, "num", /*optional*/"example.toml"); -``` - -If the value does not exists, it throws `std::out_of_range` with informative error message. - -```console -terminate called after throwing an instance of 'std::out_of_range' - what(): [error] key "num" not found in example.toml -``` - -You can use this with a `toml::value` that is expected to be a `toml::table`. It automatically casts the value to table. - -```cpp -const auto data = toml::parse("example.toml"); -const auto num = toml::get(data.at("table"), "num"); -// expecting the following example.toml -// [table] -// num = 42 -``` - -In this case, because the value `data.at("table")` knows the locatoin of itself, you don't need to pass where you find the value. `toml::get` will show you a great error message. - -```console -terminate called after throwing an instance of 'std::out_of_range' - what(): [error] key "num" not found - --> example.toml - 3 | [table] - | ~~~~~~~ in this table -``` - -If it's not a `toml::table`, error like "invalid type" would be thrown. - ### getting arrays You can set any kind of `container` class to obtain `toml::array` except for `map`-like classes. @@ -331,6 +293,44 @@ const auto value = toml::expect(data.at("number")) }).unwrap_or(/*default value =*/ 3.14); ``` +### finding value from table + +toml11 provides utility function to find a value from `toml::table`. Of course, you can do this in your own way with `toml::get` because it just searches `unordered_map` and returns a value if exists. + +```cpp +const auto data = toml::parse("example.toml"); +const auto num = toml::find(data, "num", /*for err msg*/"example.toml"); +``` + +If the value does not exist, it throws `std::out_of_range` with informative error message. + +```console +terminate called after throwing an instance of 'std::out_of_range' + what(): [error] key "num" not found in example.toml +``` + +You can use this with a `toml::value` that is expected to be a `toml::table`. It automatically casts the value to table. + +```cpp +const auto data = toml::parse("example.toml"); +const auto num = toml::find(data.at("table"), "num"); +// expecting the following example.toml +// [table] +// num = 42 +``` + +In this case, because the value `data.at("table")` knows the locatoin of itself, you don't need to pass where you find the value. `toml::find` will show you a great error message. + +```console +terminate called after throwing an instance of 'std::out_of_range' + what(): [error] key "num" not found + --> example.toml + 3 | [table] + | ~~~~~~~ in this table +``` + +If it's not a `toml::table`, the same error as "invalid type" would be thrown. + ### checking value type When you don't know the exact type of toml-value, you can get `enum` type from `toml::value`. From 514f3c773ffca0c6e62a453a385bf3b2eb0b9921 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 02:24:03 +0900 Subject: [PATCH 119/133] set test_from_toml --- tests/CMakeLists.txt | 2 +- tests/test_from_toml.cpp | 218 ++++++++------------------------------- toml/from_toml.hpp | 2 +- 3 files changed, 47 insertions(+), 175 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2230013..7940376 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,7 +22,7 @@ set(TEST_NAMES test_get test_get_related_func test_to_toml -# test_from_toml + test_from_toml # test_parse_file # test_parse_unicode ) diff --git a/tests/test_from_toml.cpp b/tests/test_from_toml.cpp index b3953b3..45cfac5 100644 --- a/tests/test_from_toml.cpp +++ b/tests/test_from_toml.cpp @@ -12,180 +12,52 @@ #include #include -BOOST_AUTO_TEST_CASE(test_from_toml_exact) +BOOST_AUTO_TEST_CASE(test_from_toml) { - toml::Boolean b(true); - toml::Integer i(42); - toml::Float f(3.14); - toml::String s("hoge"); - toml::Datetime d(std::chrono::system_clock::now()); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); + toml::boolean b = false; + toml::integer i = 0; + toml::floating f = 0.; + toml::string s; + toml::local_date dt; + toml::array a; + toml::table t; + { + toml::value v(true); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK_EQUAL(b, true); + } + { + toml::value v(42); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK_EQUAL(i, 42); + } + { + toml::value v(3.14); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK_EQUAL(f, 3.14); + } + { + toml::value v("foo"); - toml::value v1(b); - toml::value v2(i); - toml::value v3(f); - toml::value v4(s); - toml::value v5(d); - toml::value v6(a); - toml::value v7(t); - - toml::Boolean u1; - toml::Integer u2; - toml::Float u3; - toml::String u4; - toml::Datetime u5; - toml::Array u6; - toml::Table u7; - - toml::from_toml(u1, v1); - toml::from_toml(u2, v2); - toml::from_toml(u3, v3); - toml::from_toml(u4, v4); - toml::from_toml(u5, v5); - toml::from_toml(u6, v6); - toml::from_toml(u7, v7); - - BOOST_CHECK_EQUAL(u1, b); - BOOST_CHECK_EQUAL(u2, i); - BOOST_CHECK_EQUAL(u3, f); - BOOST_CHECK_EQUAL(u4, s); - BOOST_CHECK_EQUAL(u6.at(0).cast(), a.at(0).cast()); - BOOST_CHECK_EQUAL(u6.at(1).cast(), a.at(1).cast()); - BOOST_CHECK_EQUAL(u6.at(2).cast(), a.at(2).cast()); - BOOST_CHECK_EQUAL(u6.at(3).cast(), a.at(3).cast()); - BOOST_CHECK_EQUAL(u6.at(4).cast(), a.at(4).cast()); - BOOST_CHECK_EQUAL(u7.at("val1").cast(), true); - BOOST_CHECK_EQUAL(u7.at("val2").cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(u7.at("val3").cast(),3.14, 1e-3); - BOOST_CHECK_EQUAL(u7.at("val4").cast(), "piyo"); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK_EQUAL(s, "foo"); + } + { + toml::value v(toml::local_date(2018, toml::month_t::Apr, 22)); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK(dt == toml::local_date(2018, toml::month_t::Apr, 22)); + } + { + toml::array ref{toml::value(42), toml::value(54)}; + toml::value v(ref); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK(ref == a); + } + { + toml::table ref{{"key1", 42}, {"key2", 3.14}}; + toml::value v(ref); + toml::from_toml(std::tie(b, i, f, s, dt, a, t), v); + BOOST_CHECK(ref == t); + } } -BOOST_AUTO_TEST_CASE(test_from_toml_cast) -{ - toml::Integer i(42); - toml::Float f(3.14); - toml::Array a{2, 7, 1, 8, 2}; - toml::Table t{{"val1", true}, {"val2", 42}, {"val3", 3.14}, {"val4", "piyo"}}; - - toml::value vi(i); - toml::value vf(f); - toml::value va(a); - toml::value vt(t); - - int u1; - std::size_t u2; - float u3; - std::list u4; - std::deque u5; - std::array u6; - std::map u7; - - std::list expect_list{2,7,1,8,2}; - std::deque expect_deque{2,7,1,8,2}; - std::array expect_array{{2,7,1,8,2}}; - - toml::from_toml(u1, vi); - toml::from_toml(u2, vi); - toml::from_toml(u3, vf); - toml::from_toml(u4, va); - toml::from_toml(u5, va); - toml::from_toml(u6, va); - toml::from_toml(u7, vt); - - BOOST_CHECK_EQUAL(u1, 42); - BOOST_CHECK_EQUAL(u2, 42ul); - BOOST_CHECK_CLOSE_FRACTION(u3, 3.14, 1e-3); - - const bool same_list = (u4 == expect_list); - const bool same_deque = (u5 == expect_deque); - const bool same_array = (u6 == expect_array); - BOOST_CHECK(same_list); - BOOST_CHECK(same_deque); - BOOST_CHECK(same_array); - - BOOST_CHECK_EQUAL(u7["val1"].cast(), true); - BOOST_CHECK_EQUAL(u7["val2"].cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(u7["val3"].cast(), 3.14, 1e-3); - BOOST_CHECK_EQUAL(u7["val4"].cast(), "piyo"); - -} - -BOOST_AUTO_TEST_CASE(test_from_toml_tie) -{ - toml::Boolean b(42); - toml::Integer i(42); - toml::Float f(3.14); - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::Table t; - t.emplace("val1", true); - t.emplace("val2", 42); - t.emplace("val3", 3.14); - t.emplace("val4", "piyo"); - - toml::value vb(b); - toml::value vi(i); - toml::value vf(f); - toml::value va(a); - toml::value vt(t); - - bool ub; - int ui; - float uf; - std::deque ua; - std::map ut; - - toml::from_toml(std::tie(ub, ui, uf, ua, ut), vb); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), vi); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), vf); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), va); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), va); - toml::from_toml(std::tie(ub, ui, uf, ua, ut), vt); - - BOOST_CHECK_EQUAL(ub, true); - BOOST_CHECK_EQUAL(ui, 42); - BOOST_CHECK_CLOSE_FRACTION(uf, 3.14, 1e-3); - BOOST_CHECK_EQUAL(ua.at(0), 2); - BOOST_CHECK_EQUAL(ua.at(1), 7); - BOOST_CHECK_EQUAL(ua.at(2), 1); - BOOST_CHECK_EQUAL(ua.at(3), 8); - BOOST_CHECK_EQUAL(ua.at(4), 2); - BOOST_CHECK_EQUAL(ut["val1"].cast(), true); - BOOST_CHECK_EQUAL(ut["val2"].cast(), 42); - BOOST_CHECK_CLOSE_FRACTION(ut["val3"].cast(), 3.14, 1e-3); - BOOST_CHECK_EQUAL(ut["val4"].cast(), "piyo"); -} - -BOOST_AUTO_TEST_CASE(test_from_toml_tuple) -{ - toml::Array a; - a.emplace_back(2); - a.emplace_back(7); - a.emplace_back(1); - a.emplace_back(8); - a.emplace_back(2); - toml::value v(a); - - std::tuple t; - toml::from_toml(t, v); - - BOOST_CHECK_EQUAL(std::get<0>(t), 2); - BOOST_CHECK_EQUAL(std::get<1>(t), 7); - BOOST_CHECK_EQUAL(std::get<2>(t), 1); - BOOST_CHECK_EQUAL(std::get<3>(t), 8); - BOOST_CHECK_EQUAL(std::get<4>(t), 2); -} diff --git a/toml/from_toml.hpp b/toml/from_toml.hpp index d9d8561..065768c 100644 --- a/toml/from_toml.hpp +++ b/toml/from_toml.hpp @@ -8,7 +8,7 @@ namespace toml template void from_toml(T& x, const toml::value& v) { - x = toml::get(v); + x = toml::get::type>(v); return; } From 4d7cfc9d1d7cda40e2349762e7068b0a05ed8f5d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 02:26:55 +0900 Subject: [PATCH 120/133] turn test_parse_file on the required change is; - change Datetime -> offset_datetime and construct correctly. --- tests/CMakeLists.txt | 2 +- tests/test_parse_file.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7940376..3167ef6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,7 +23,7 @@ set(TEST_NAMES test_get_related_func test_to_toml test_from_toml -# test_parse_file + test_parse_file # test_parse_unicode ) diff --git a/tests/test_parse_file.cpp b/tests/test_parse_file.cpp index 4222921..95f178c 100644 --- a/tests/test_parse_file.cpp +++ b/tests/test_parse_file.cpp @@ -22,7 +22,8 @@ BOOST_AUTO_TEST_CASE(test_example) BOOST_CHECK_EQUAL(toml::get(owner.at("bio")), "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_CHECK_EQUAL(toml::get(owner.at("dob")), - toml::Datetime(1979, 5, 27, 7, 32, 0, 0, 0, 0, 0)); + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } toml::Table database = toml::get(data.at("database")); @@ -90,7 +91,8 @@ BOOST_AUTO_TEST_CASE(test_example_stream) BOOST_CHECK_EQUAL(toml::get(owner.at("bio")), "GitHub Cofounder & CEO\nLikes tater tots and beer."); BOOST_CHECK_EQUAL(toml::get(owner.at("dob")), - toml::Datetime(1979, 5, 27, 7, 32, 0, 0, 0, 0, 0)); + toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27), + toml::local_time(7, 32, 0), toml::time_offset(0, 0))); } toml::Table database = toml::get(data.at("database")); From fb6d51954a348b80f914a0a8c783b46d95b31bec Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 02:28:52 +0900 Subject: [PATCH 121/133] turn test_parse_unicode on; no change required --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3167ef6..ba813eb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,7 +24,7 @@ set(TEST_NAMES test_to_toml test_from_toml test_parse_file -# test_parse_unicode + test_parse_unicode ) CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL) From 26e0d87d3ba69165e489a79c621e0884ff31f3d5 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 12:49:53 +0900 Subject: [PATCH 122/133] enable nanoseconds in datetimes --- tests/test_datetime.cpp | 12 +++--- toml/datetime.hpp | 90 ++++++++++++++++++++++++++--------------- toml/get.hpp | 2 +- 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/tests/test_datetime.cpp b/tests/test_datetime.cpp index 0fbc979..ae5ae51 100644 --- a/tests/test_datetime.cpp +++ b/tests/test_datetime.cpp @@ -31,12 +31,12 @@ BOOST_AUTO_TEST_CASE(test_local_time) const toml::local_time time1(time); BOOST_CHECK_EQUAL(time, time1); - const std::chrono::microseconds dur(time); - std::chrono::microseconds us(0); - us += std::chrono::hours (12); - us += std::chrono::minutes(30); - us += std::chrono::seconds(45); - BOOST_CHECK_EQUAL(dur.count(), us.count()); + const std::chrono::nanoseconds dur(time); + std::chrono::nanoseconds ns(0); + ns += std::chrono::hours (12); + ns += std::chrono::minutes(30); + ns += std::chrono::seconds(45); + BOOST_CHECK_EQUAL(dur.count(), ns.count()); const toml::local_time time3(12, 15, 45); BOOST_CHECK(time > time3); diff --git a/toml/datetime.hpp b/toml/datetime.hpp index a55b08b..8c6038e 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -31,15 +31,19 @@ enum class month_t : std::int8_t struct local_date { std::int16_t year; // A.D. (like, 2018) - std::int8_t month; // [0, 11] - std::int8_t day; // [1, 31] + std::uint8_t month; // [0, 11] + std::uint8_t day; // [1, 31] - local_date(std::int16_t y, month_t m, std::int8_t d) - : year(y), month(static_cast(m)), day(d) + local_date(int y, month_t m, int d) + : year (static_cast(y)), + month(static_cast(m)), + day (static_cast(d)) {} explicit local_date(const std::tm& t) - : year(t.tm_year + 1900), month(t.tm_mon), day(t.tm_mday) + : year (static_cast(t.tm_year + 1900)), + month(static_cast(t.tm_mon)), + day (static_cast(t.tm_mday)) {} explicit local_date(const std::chrono::system_clock::time_point& tp) @@ -124,24 +128,28 @@ operator<<(std::basic_ostream& os, const local_date& date) struct local_time { - std::int8_t hour; // [0, 23] - std::int8_t minute; // [0, 59] - std::int8_t second; // [0, 60] - std::int16_t millisecond; // [0, 999] - std::int16_t microsecond; // [0, 999] + std::uint8_t hour; // [0, 23] + std::uint8_t minute; // [0, 59] + std::uint8_t second; // [0, 60] + std::uint16_t millisecond; // [0, 999] + std::uint16_t microsecond; // [0, 999] + std::uint16_t nanosecond; // [0, 999] - local_time(std::int8_t h, std::int8_t m, std::int8_t s) - : hour(h), minute(m), second(s), millisecond(0), microsecond(0) - {} - - local_time(std::int8_t h, std::int8_t m, std::int8_t s, - std::int16_t ms, std::int16_t us) - : hour(h), minute(m), second(s), millisecond(ms), microsecond(us) + local_time(int h, int m, int s, + int ms = 0, int us = 0, int ns = 0) + : hour (static_cast(h)), + minute(static_cast(m)), + second(static_cast(s)), + millisecond(static_cast(ms)), + microsecond(static_cast(us)), + nanosecond (static_cast(ns)) {} explicit local_time(const std::tm& t) - : hour(t.tm_hour), minute(t.tm_min), second(t.tm_sec), - millisecond(0), microsecond(0) + : hour (static_cast(t.tm_hour)), + minute(static_cast(t.tm_min)), + second(static_cast(t.tm_sec)), + millisecond(0), microsecond(0), nanosecond(0) {} template @@ -161,11 +169,15 @@ struct local_time const auto t5 = t4 - ms; const auto us = std::chrono::duration_cast(t5); this->microsecond = us.count(); + const auto t6 = t5 - us; + const auto ns = std::chrono::duration_cast(t6); + this->nanosecond = ns.count(); } - operator std::chrono::microseconds() const + operator std::chrono::nanoseconds() const { - return std::chrono::microseconds(this->microsecond) + + return std::chrono::nanoseconds (this->nanosecond) + + std::chrono::microseconds(this->microsecond) + std::chrono::milliseconds(this->millisecond) + std::chrono::seconds(this->second) + std::chrono::minutes(this->minute) + @@ -182,8 +194,8 @@ struct local_time inline bool operator==(const local_time& lhs, const local_time& rhs) { - return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond) == - std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond); + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); } inline bool operator!=(const local_time& lhs, const local_time& rhs) { @@ -191,8 +203,8 @@ inline bool operator!=(const local_time& lhs, const local_time& rhs) } inline bool operator< (const local_time& lhs, const local_time& rhs) { - return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond) < - std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond); + return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < + std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); } inline bool operator<=(const local_time& lhs, const local_time& rhs) { @@ -211,14 +223,21 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, const local_time& time) { - os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.minute ) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.second ); - if(time.millisecond != 0 || time.microsecond != 0) + os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; + os << std::setfill('0') << std::setw(2) << static_cast(time.second); + if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) { os << '.'; os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); - os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); + if(time.microsecond != 0 || time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); + if(time.nanosecond != 0) + { + os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); + } + } } return os; } @@ -228,7 +247,10 @@ struct time_offset std::int8_t hour; // [-12, 12] std::int8_t minute; // [-59, 59] - time_offset(std::int8_t h, std::int8_t m): hour(h), minute(m) {} + time_offset(int h, int m) + : hour (static_cast(h)), + minute(static_cast(m)) + {} operator std::chrono::minutes() const { @@ -321,6 +343,8 @@ struct local_datetime std::chrono::milliseconds>(t_diff).count(); this->time.microsecond = std::chrono::duration_cast< std::chrono::microseconds>(t_diff).count(); + this->time.nanosecond = std::chrono::duration_cast< + std::chrono::nanoseconds >(t_diff).count(); } explicit local_datetime(const std::time_t t) @@ -331,7 +355,7 @@ struct local_datetime { // std::mktime returns date as local time zone. no conversion needed return std::chrono::system_clock::time_point(this->date) + - std::chrono::microseconds(this->time); + std::chrono::nanoseconds(this->time); } operator std::time_t() const @@ -413,7 +437,7 @@ struct offset_datetime // get date-time std::chrono::system_clock::time_point tp = std::chrono::system_clock::time_point(this->date) + - std::chrono::microseconds(this->time); + std::chrono::nanoseconds(this->time); // get date-time in UTC. let's say we are in +09:00 (JPN). // writing 12:00:00 in +09:00 means 03:00:00Z. to represent diff --git a/toml/get.hpp b/toml/get.hpp index 13d32d7..20944f4 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -89,7 +89,7 @@ template( - std::chrono::microseconds(v.cast())); + std::chrono::nanoseconds(v.cast())); } // ============================================================================ From 95d73a290fe5d6a21571d3975c4fda9716c78be5 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 13:07:48 +0900 Subject: [PATCH 123/133] add test case for reading dotted-keys --- tests/CMakeLists.txt | 1 + tests/test_parse_table.cpp | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/test_parse_table.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ba813eb..e9eabba 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,7 @@ set(TEST_NAMES test_parse_string test_parse_datetime test_parse_array + test_parse_table test_parse_inline_table test_parse_key test_parse_table_key diff --git a/tests/test_parse_table.cpp b/tests/test_parse_table.cpp new file mode 100644 index 0000000..13207b5 --- /dev/null +++ b/tests/test_parse_table.cpp @@ -0,0 +1,50 @@ +#define BOOST_TEST_MODULE "parse_table_test" +#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST +#include +#else +#define BOOST_TEST_NO_LIB +#include +#endif +#include +#include +#include "test_parse_aux.hpp" + +using namespace toml; +using namespace detail; + +BOOST_AUTO_TEST_CASE(test_normal_table) +{ + std::string table( + "key1 = \"value\"\n" + "key2 = 42\n" + "key3 = 3.14\n" + ); + location loc("test", table); + + const auto result = toml::detail::parse_ml_table(loc); + BOOST_CHECK(result.is_ok()); + const auto data = result.unwrap(); + + BOOST_CHECK_EQUAL(toml::get(data.at("key1")), "value"); + BOOST_CHECK_EQUAL(toml::get(data.at("key2")), 42); + BOOST_CHECK_EQUAL(toml::get(data.at("key3")), 3.14); +} + +BOOST_AUTO_TEST_CASE(test_nested_table) +{ + std::string table( + "a.b = \"value\"\n" + "a.c.d = 42\n" + ); + location loc("test", table); + + const auto result = toml::detail::parse_ml_table(loc); + BOOST_CHECK(result.is_ok()); + const auto data = result.unwrap(); + + const auto a = toml::get(data.at("a")); + const auto c = toml::get(a.at("c")); + + BOOST_CHECK_EQUAL(toml::get(a.at("b")), "value"); + BOOST_CHECK_EQUAL(toml::get(c.at("d")), 42); +} From 56287803e763e26ad2aa096cad0bf17ef4e578c1 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 13:21:26 +0900 Subject: [PATCH 124/133] update README a bit --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fa4a5b8..a0fdde6 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ toml11 now has clean and informative error messages inspired by Rust and it look ```console terminate called after throwing an instance of 'toml::syntax_error' what(): [error] toml::parse_table: invalid line format # error description - --> example.toml # file name (and path, if any) + --> example.toml # file name 3 | a = 42 = true # line num and content | ^------ expected newline, but got '='. # error reason ``` @@ -95,8 +95,7 @@ toml::get(data["answer"]) = 6 * 9; std::cout << toml::get(data.at("answer")) << std::endl; // 54 ``` -If the specified type requires conversion, you can't take a reference to the -return value. See also [underlying types](#underlying-types). +If the specified type requires conversion, you can't take a reference to the value. See also [underlying types](#underlying-types). #### passing invalid type to toml::get @@ -110,7 +109,7 @@ terminate called after throwing an instance of 'toml::type_error' | ~~~~~~~~~~~~~~ the actual type is string ``` -NOTE: In order to show this kind of error message, all the toml values has 1 shared_ptr that points the corresponding byte sequence and 2 iterator that points the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save the memory resource. +NOTE: In order to show this kind of error message, all the toml values has 1 shared_ptr that points the corresponding byte sequence and 2 iterators that point the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save the memory resource. ### getting arrays @@ -392,7 +391,7 @@ terminate called after throwing an instance of 'std::range_error' ## underlying types -The toml types (can be used as `toml::*` in this library) and corresponding `enum` name are listed in the table below. +The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below. | toml::type | underlying c++ type | enum | | -------------- | -------------------------------------------- | ------------------------------- | From 5125287ac73d85722cb9837eaee213add689e7f0 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 14:57:42 +0900 Subject: [PATCH 125/133] fix datetime conversion use internal duration type in std::chrono::system_clock::time_point --- toml/datetime.hpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 8c6038e..981d230 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -353,9 +353,13 @@ struct local_datetime operator std::chrono::system_clock::time_point() const { + using internal_duration = + typename std::chrono::system_clock::time_point::duration; // std::mktime returns date as local time zone. no conversion needed - return std::chrono::system_clock::time_point(this->date) + - std::chrono::nanoseconds(this->time); + auto dt = std::chrono::system_clock::time_point(this->date); + dt += std::chrono::duration_cast( + std::chrono::nanoseconds(this->time)); + return dt; } operator std::time_t() const @@ -435,9 +439,12 @@ struct offset_datetime operator std::chrono::system_clock::time_point() const { // get date-time + using internal_duration = + typename std::chrono::system_clock::time_point::duration; std::chrono::system_clock::time_point tp = std::chrono::system_clock::time_point(this->date) + - std::chrono::nanoseconds(this->time); + std::chrono::duration_cast( + std::chrono::nanoseconds(this->time)); // get date-time in UTC. let's say we are in +09:00 (JPN). // writing 12:00:00 in +09:00 means 03:00:00Z. to represent From 06f04af375b6a48d1040e3f4bc01250913d9587d Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 14:58:42 +0900 Subject: [PATCH 126/133] fix typoes in README --- README.md | 72 +++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index a0fdde6..f796abb 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ c++11 header-only toml parser depending only on c++ standard library. compatible to the latest version of TOML v0.5.0 after version 2.0.0. -Are you looking for pre-C++11 compatible toml parser? Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and work with C++98 & Boost. +Are you looking for pre-C++11 compatible toml parser? Try [Boost.toml](https://github.com/ToruNiina/Boost.toml)! It has almost the same functionality as this library and works with C++98 & Boost. ## How to use -## installation +## Installation -Just include the file after adding correct include path. +Just include the file after adding it to the include path. ```cpp #include // that's all! now you can use it. @@ -29,9 +29,9 @@ int main() } ``` -### decoding toml file +### Decoding toml file -The only thing you have to do is passing a filename to `toml::parse` function. +The only thing you have to do is to pass a filename to the `toml::parse` function. ```cpp const std::string fname("sample.toml"); @@ -40,7 +40,7 @@ const toml::table data = toml::parse(fname); In the case of file open error, it will throw `std::runtime_error`. -You can pass also `stream` to `toml::parse` function after checking the status. +You can also pass a `stream` to the `toml::parse` function after checking the status. ```cpp std::ifstream ifs("sample.toml"); @@ -48,9 +48,9 @@ assert(ifs.good()); const auto data = toml::parse(ifs /*, "filename" (optional)*/); ``` -To show a better error message, it is recommended to pass filename with `istream`. See also [in the case of syntax error](#in-the-case-of-syntax-error) and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget). +To show a better error message, it is recommended to pass a filename with `istream`. See also [in the case of syntax error](#in-the-case-of-syntax-error) and [passing invalid type to toml::get](#passing-invalid-type-to-tomlget). -### in the case of syntax error +### In the case of syntax error If there is a syntax error in a toml file, `toml::parse` will throw `toml::syntax_error`. @@ -64,9 +64,9 @@ terminate called after throwing an instance of 'toml::syntax_error' | ^------ expected newline, but got '='. # error reason ``` -Since the error message generation is generally a difficult task, the current status is not ideal. toml11 need your help. If you encounter a weird error message, please let us know and improve the quality! +Since the error message generation is generally a difficult task, the current status is not ideal. toml11 needs your help. If you encounter a weird error message, please let us know and contribute to improve the quality! -### getting toml value +### Getting a toml value After parsing successfully, you can obtain the values from the result of `toml::parse` (here, `data`) using `toml::get` function. @@ -97,9 +97,9 @@ std::cout << toml::get(data.at("answer")) << std::endl; // 54 If the specified type requires conversion, you can't take a reference to the value. See also [underlying types](#underlying-types). -#### passing invalid type to toml::get +#### Passing invalid type to toml::get -If you choose the invalid type, `toml::type_error` will be thrown. Similar to the `syntax_error`, toml11 also displays informative error message. The error message when you choose `int` to get `string` value would be like this. +If you choose the invalid type, `toml::type_error` will be thrown. Similar to the `syntax_error`, toml11 also displays informative error messages. The error message when you choose `int` to get `string` value would be like this. ```console terminate called after throwing an instance of 'toml::type_error' @@ -109,11 +109,11 @@ terminate called after throwing an instance of 'toml::type_error' | ~~~~~~~~~~~~~~ the actual type is string ``` -NOTE: In order to show this kind of error message, all the toml values has 1 shared_ptr that points the corresponding byte sequence and 2 iterators that point the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save the memory resource. +NOTE: In order to show this kind of error message, all the toml values have 1 shared_ptr that points the corresponding byte sequence and 2 iterators that point the range. It is recommended to destruct all the `toml::value` classes after configuring your application to save memory resources. -### getting arrays +### Getting arrays -You can set any kind of `container` class to obtain `toml::array` except for `map`-like classes. +You can set any kind of `container` class to obtain a `toml::array` except for `map`-like classes. ``` cpp const auto vc = toml::get >(data.at("numbers")); @@ -124,7 +124,7 @@ const auto ar = toml::get>(data.at("numbers")); // it will throw toml::type_error because std::array is not resizable. ``` -Surprisingly, you can get `toml::array` as `std::pair` and `std::tuple.` +Surprisingly, you can also get a `toml::array` as `std::pair` and `std::tuple.` ```cpp const auto tp = toml::get>(data.at("numbers")); @@ -144,7 +144,7 @@ const auto aofa = toml::get< >(data.at("aofa")); ``` -If you don't know what the type is inside the array, you can use `toml::array` which is a `std::vector` of `toml::value`instead. +If you don't know what the type is inside the array, you can use `toml::array`, which is a `std::vector` of `toml::value`, instead. ```cpp const auto aofa = toml::get(data.at("aofa")); @@ -153,9 +153,9 @@ const auto first = toml::get(aofa.at(0)); See also [expecting conversion](#expecting-conversion) and [checking-value-type](#checking-value-type). -### getting tables +### Getting tables -`toml::table` is a key component of this library, that is an alias of a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`. `toml::parse` returns this as a result. +`toml::table` is a key component of this library, which is an alias of a `std::unordered_map` from `toml::key (a.k.a. std::string)` to `toml::value`. `toml::parse` returns this as a result. Since it is just an alias of `std::unordered_map`, it has all the functionalities that `std::unordered_map` has, e.g. `operator[]`, `count`, and `find`. @@ -167,7 +167,7 @@ if(data.count("title") != 0) } ``` -For the case that all the values of the table have the same type, toml11 allows you to convert `toml::table` to `map`s that contains the convertible type. +When all the values of the table have the same type, toml11 allows you to convert a `toml::table` to a `map` that contains the convertible type. ```toml [tab] @@ -181,7 +181,7 @@ std::cout << tab["key1"] << std::endl; // foo std::cout << tab["key2"] << std::endl; // bar ``` -### dotted keys +### Dotted keys TOML v0.5.0 has a new feature named "dotted keys". You can chain keys to represent the structure of the data. @@ -205,9 +205,9 @@ const auto physical = toml::get(data.at("physical")); const auto color = toml::get(physical.at("color")); ``` -### an array of tables +### An array of tables -An array of tables is just an array of tables. You can get completely in the same way as the other arrays and tables. +An array of tables is just an array of tables. You can get it completely in the same way as the other arrays and tables. ```toml array_of_inline_table = [{key = "value1"}, {key = "value2"}, {key = "value3"}] @@ -225,9 +225,9 @@ const auto aot1 = toml::get>(data.at("array_of_inline_t const auto aot2 = toml::get>(data.at("array_of_table")); ``` -### cost of conversion +### Cost of conversion -Although `toml::get` is convenient, it has additional copy-cost because it copies data contained in `toml::value` to user-specified type. Of course in some case this overhead is not ignorable. +Although `toml::get` is convenient, it has additional copy-cost because it copies data contained in `toml::value` to the user-specified type. Of course in some case this overhead is not ignorable. By passing the exact types, `toml::get` returns reference that has nealy zero overhead. @@ -244,7 +244,7 @@ const auto& num1 = toml::get(numbers.at(1)); const auto& num2 = toml::get(numbers.at(2)); ``` -### datetime and its variants +### Datetime and its variants TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, `local_datetime`, and `offset_datetime`. With toml11, you can convert `local_time` to your favorite `std::chrono::duration` and others to `std::chrono::system_clock::time_point`. @@ -258,7 +258,7 @@ const auto dur = toml::get(data.at("time")); // 12 * 60 + const auto tp = toml::get(data.at("date")); ``` -### getting with a fallback +### Getting with a fallback `toml::get_or` returns a default value if `toml::get` failed. @@ -269,7 +269,7 @@ const auto value = toml::get_or(data, "key", 42); // value => int 42. `toml::get_or` automatically deduces what type you want to get from the default value you passed. -### expecting conversion +### Expecting conversion By using `toml::expect`, you will get your expected value or an error message without throwing `toml::type_error`. @@ -292,9 +292,9 @@ const auto value = toml::expect(data.at("number")) }).unwrap_or(/*default value =*/ 3.14); ``` -### finding value from table +### Finding value from table -toml11 provides utility function to find a value from `toml::table`. Of course, you can do this in your own way with `toml::get` because it just searches `unordered_map` and returns a value if exists. +toml11 provides utility function to find a value from `toml::table`. Of course, you can do this in your own way with `toml::get` because it just searches an `unordered_map` and returns a value if it exists. ```cpp const auto data = toml::parse("example.toml"); @@ -330,7 +330,7 @@ terminate called after throwing an instance of 'std::out_of_range' If it's not a `toml::table`, the same error as "invalid type" would be thrown. -### checking value type +### Checking value type When you don't know the exact type of toml-value, you can get `enum` type from `toml::value`. @@ -345,7 +345,7 @@ switch(data.at("something").type()) } ``` -### fill only the matched value +### Fill only the matched value The more sophisticated way is using `toml::from_toml` and `std::tie`. @@ -368,9 +368,9 @@ int i = 0; toml::from_toml(i, data.at("something")); ``` -### sanitizing UTF-8 codepoints +### Sanitizing UTF-8 codepoints -toml11 shows warning if a value of an escape sequence to represent unicode character exceeds the range of unicode. +toml11 shows warning if a value of an escape sequence used to represent unicode character exceeds the unicode range. ```console [warning] input codepoint (0011FFFF) is too large to decode as a unicode character. The result may not be able to render to your screen. @@ -389,7 +389,7 @@ terminate called after throwing an instance of 'std::range_error' | ~~~~~~~~~ should be in [0x00..0x10FFFF] ``` -## underlying types +## Underlying types The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below. @@ -406,7 +406,7 @@ The toml types (can be used as `toml::*` in this library) and corresponding `enu | Array | `std::vector` | `toml::value_t::Array` | | Table | `std::unordered_map` | `toml::value_t::Table` | -`toml::string` is effectively the same as `std::string` but has an additional flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. Although `std::string` is not an exact toml type, but still you can get a reference that points internal `std::string` by using `toml::get()` for the convenience. +`toml::string` is effectively the same as `std::string` but has an additional flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. Although `std::string` is not an exact toml type, still you can get a reference that points to internal `std::string` by using `toml::get()` for convenience. `Datetime` variants are `struct` that are defined in this library. Because `std::chrono::system_clock::time_point` is a __time point__, not capable of representing a Local Time independent from a specific day. From acc7b7870f1983a85cb248fd17f6f26da712c432 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 16:13:05 +0900 Subject: [PATCH 127/133] remove format_error_for_value and add get_region instead it is convenient to have get_region function that can access region_info in toml::value. get_region is placed in toml::detail and made friend of toml::value because I don't want to make toml::value::region_info public and keep it internal use only. --- toml/get.hpp | 31 ++++++++++++++++--------------- toml/parser.hpp | 1 + toml/value.hpp | 31 ++++++++++++------------------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/toml/get.hpp b/toml/get.hpp index 20944f4..1cf15ba 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -182,9 +182,10 @@ T get(const value& v) T container; if(ar.size() != container.size()) { - throw std::out_of_range(detail::format_error_for_value(v, concat_to_string( + throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified container size is ", container.size(), - " but there are ", ar.size(), " elements in toml array."), "here")); + " but there are ", ar.size(), " elements in toml array."), + detail::get_region(v), "here")); } std::transform(ar.cbegin(), ar.cend(), container.begin(), [](const value& x){return ::toml::get(x);}); @@ -204,9 +205,9 @@ T get(const value& v) const auto& ar = v.cast(); if(ar.size() != 2) { - throw std::out_of_range(detail::format_error_for_value(v, concat_to_string( + throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::pair but there are ", ar.size(), - " elements in toml array."), "here")); + " elements in toml array."), detail::get_region(v), "here")); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); @@ -234,10 +235,10 @@ T get(const value& v) const auto& ar = v.cast(); if(ar.size() != std::tuple_size::value) { - throw std::out_of_range(detail::format_error_for_value(v, concat_to_string( + throw std::out_of_range(detail::format_underline(concat_to_string( "[erorr] toml::get specified std::tuple with ", std::tuple_size::value, "elements, but there are ", ar.size(), - " elements in toml array."), "here")); + " elements in toml array."), detail::get_region(v), "here")); } return detail::get_tuple_impl(ar, detail::make_index_sequence::value>{}); @@ -312,9 +313,9 @@ find(const toml::value& v, const toml::key& ky) const auto& tab = ::toml::get(v); if(tab.count(ky) == 0) { - throw std::out_of_range(detail::format_error_for_value(v, - concat_to_string("[error] key \"", ky, "\" not found"), - concat_to_string("in this table"))); + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] key \"", ky, "\" not found"), detail::get_region(v), + "in this table")); } return ::toml::get(tab.at(ky)); } @@ -325,9 +326,9 @@ find(toml::value& v, const toml::key& ky) auto& tab = ::toml::get(v); if(tab.count(ky) == 0) { - throw std::out_of_range(detail::format_error_for_value(v, - concat_to_string("[error] key \"", ky, "\" not found"), - concat_to_string("in this table"))); + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] key \"", ky, "\" not found"), detail::get_region(v), + "in this table")); } return ::toml::get(tab.at(ky)); } @@ -338,9 +339,9 @@ find(toml::value&& v, const toml::key& ky) auto tab = ::toml::get(std::move(v)); if(tab.count(ky) == 0) { - throw std::out_of_range(detail::format_error_for_value(v, - concat_to_string("[error] key \"", ky, "\" not found"), - concat_to_string("in this table"))); + throw std::out_of_range(detail::format_underline(concat_to_string( + "[error] key \"", ky, "\" not found"), detail::get_region(v), + "in this table")); } return ::toml::get(std::move(tab[ky])); } diff --git a/toml/parser.hpp b/toml/parser.hpp index a965e1e..d6859b4 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -6,6 +6,7 @@ #include "lexer.hpp" #include "types.hpp" #include "value.hpp" +#include #include namespace toml diff --git a/toml/value.hpp b/toml/value.hpp index 8b98ca1..ca5b653 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -17,8 +17,8 @@ namespace toml namespace detail { -std::string // forward decl -format_error_for_value(const value&, const std::string&, const std::string&); +// to show error messages. not recommended for users. +region_base const& get_region(const value&); }// detail template @@ -555,14 +555,8 @@ class value } } - std::string - format_error(const std::string& msg, const std::string& com) const - { - return detail::format_underline(msg, *(this->region_info_), com); - } - - friend std::string detail::format_error_for_value( - const value&, const std::string&, const std::string&); + // for error messages + friend region_base const& detail::get_region(const value&); template struct switch_cast; @@ -594,10 +588,9 @@ class value namespace detail { -inline std::string format_error_for_value( - const value& v, const std::string& m, const std::string& c) +inline region_base const& get_region(const value& v) { - return v.format_error(m, c); + return *(v.region_info_); } }// detail @@ -668,8 +661,8 @@ typename detail::toml_default_type::type& value::cast() & { if(T != this->type_) { - throw type_error(this->format_error(concat_to_string( - "[error] toml::value bad_cast to ", T), + throw type_error(format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), *region_info_, concat_to_string("the actual type is ", this->type_))); } return switch_cast::invoke(*this); @@ -679,8 +672,8 @@ typename detail::toml_default_type::type const& value::cast() const& { if(T != this->type_) { - throw type_error(this->format_error(concat_to_string( - "[error] toml::value bad_cast to ", T), + throw type_error(format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), *region_info_, concat_to_string("the actual type is ", this->type_))); } return switch_cast::invoke(*this); @@ -690,8 +683,8 @@ typename detail::toml_default_type::type&& value::cast() && { if(T != this->type_) { - throw type_error(this->format_error(concat_to_string( - "[error] toml::value bad_cast to ", T), + throw type_error(format_underline(concat_to_string( + "[error] toml::value bad_cast to ", T), *region_info_, concat_to_string("the actual type is ", this->type_))); } return switch_cast::invoke(std::move(*this)); From e5c29c28709752a7cfa9fd65cab03073d3c6e688 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 17:07:03 +0900 Subject: [PATCH 128/133] enable to show err msg for 2 different location --- toml/region.hpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/toml/region.hpp b/toml/region.hpp index fb06915..e254673 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include namespace toml { @@ -241,6 +241,64 @@ inline std::string format_underline(const std::string& message, return retval; } +// to show a better error message. +inline std::string format_underline(const std::string& message, + const region_base& reg1, const std::string& comment_for_underline1, + const region_base& reg2, const std::string& comment_for_underline2, + std::vector helps = {}) +{ +#ifdef _WIN32 + const auto newline = "\r\n"; +#else + const char newline = '\n'; +#endif + const auto line1 = reg1.line(); + const auto line_number1 = reg1.line_num(); + const auto line2 = reg2.line(); + const auto line_number2 = reg2.line_num(); + const auto line_num_width = + std::max(line_number1.size(), line_number2.size()); + + std::ostringstream retval; + retval << message; + retval << newline; + retval << " --> "; + retval << reg1.name() << newline;; +// --------------------------------------- + retval << ' ' << std::setw(line_num_width) << line_number1; + retval << " | " << line1 << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | "; + retval << make_string(reg1.before(), ' '); + retval << make_string(reg1.size(), '~'); + retval << ' '; + retval << comment_for_underline1 << newline; +// --------------------------------------- + retval << " ..." << newline; + retval << ' ' << std::setw(line_num_width) << line_number2; + retval << " | " << line2 << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | "; + retval << make_string(reg2.before(), ' '); + retval << make_string(reg2.size(), '~'); + retval << ' '; + retval << comment_for_underline2; + if(helps.size() != 0) + { + retval << newline; + retval << make_string(line_num_width + 1, ' '); + retval << " | "; + for(const auto help : helps) + { + retval << newline; + retval << "Hint: "; + retval << help; + } + } + return retval.str(); +} + + // to show a better error message. template std::string From f60e93c36fb2b4a331ecfb7a2afc8c57cdcb3991 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 17:07:26 +0900 Subject: [PATCH 129/133] enable to assign value keeping region info --- toml/value.hpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/toml/value.hpp b/toml/value.hpp index ca5b653..b48031f 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -19,6 +19,8 @@ namespace detail { // to show error messages. not recommended for users. region_base const& get_region(const value&); +// ditto. +void assign_keeping_region(value&, value); }// detail template @@ -558,6 +560,9 @@ class value // for error messages friend region_base const& detail::get_region(const value&); + // to see why it's here, see detail::insert_nested_key. + friend void detail::assign_keeping_region(value&, value); + template struct switch_cast; @@ -592,6 +597,33 @@ inline region_base const& get_region(const value& v) { return *(v.region_info_); } +// If we keep region information after assigning another toml::* types, the +// error message become different from the actual value contained. +// To avoid this kind of confusing phenomena, the default assigners clear the +// old region_info_. But this functionality is actually needed deep inside of +// parser, so if you want to see the usecase, see toml::detail::insert_nested_key +// defined in toml/parser.hpp. +inline void assign_keeping_region(value& v, value other) +{ + v.cleanup(); // this keeps region info + // keep region_info_ intact + v.type_ = other.type(); + switch(v.type()) + { + case value_t::Boolean : ::toml::value::assigner(v.boolean_ , other.boolean_ ); break; + case value_t::Integer : ::toml::value::assigner(v.integer_ , other.integer_ ); break; + case value_t::Float : ::toml::value::assigner(v.floating_ , other.floating_ ); break; + case value_t::String : ::toml::value::assigner(v.string_ , other.string_ ); break; + case value_t::OffsetDatetime: ::toml::value::assigner(v.offset_datetime_, other.offset_datetime_); break; + case value_t::LocalDatetime : ::toml::value::assigner(v.local_datetime_ , other.local_datetime_ ); break; + case value_t::LocalDate : ::toml::value::assigner(v.local_date_ , other.local_date_ ); break; + case value_t::LocalTime : ::toml::value::assigner(v.local_time_ , other.local_time_ ); break; + case value_t::Array : ::toml::value::assigner(v.array_ , other.array_ ); break; + case value_t::Table : ::toml::value::assigner(v.table_ , other.table_ ); break; + default: break; + } + return; +} }// detail From e79069cc47e305d9ed2f4c578f261fdc60a6ccfe Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 17:09:38 +0900 Subject: [PATCH 130/133] enable to show err msg for invalid insertion like, with the following (invalid) toml file > a.b = "value" > a.b.c = 42 The error message becomes > terminate called after throwing an instance of 'toml::syntax_error' > what(): [error] toml::insert_value: target (a.b) is neither table nor > an array of tables > --> example.toml > 1 | a.b = "value" > | ~~~~~~~ actual type is string > ... > 2 | a.b.c = 42 > | ~~ inserting this --- toml/parser.hpp | 90 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index d6859b4..74d7c47 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1006,32 +1006,67 @@ insert_nested_key(table& root, const toml::value& v, { if(!(tab->at(k).is(value_t::Array))) { - throw syntax_error("toml::detail::insert_nested_key: " - "target is not an array of table: " + - format_dotted_keys(first, last)); + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: target value (\"", + format_dotted_keys(first, last), "\") is" + " not an array of tables"), get_region(tab->at(k)), + concat_to_string("actual type is ", tab->at(k).type()), + get_region(v), "this is an array of tables")); } array& a = tab->at(k).template cast(); if(!(a.front().is(value_t::Table))) { - throw syntax_error("toml::detail::insert_nested_key: " - "target is not an array of table: " + - format_dotted_keys(first, last)); + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: target value (\"", + format_dotted_keys(first, last), "\") is" + " not an array of tables"), get_region(tab->at(k)), + concat_to_string("actual type is ", tab->at(k).type()), + get_region(v), "this is an array of tables")); } a.push_back(v); return ok(true); } else // if not, we need to create the array of table { - array aot(1, v); // array having one table - tab->insert(std::make_pair(k, value(aot))); + toml::value aot(v); // copy region info from table to array + // update content by an array with one element + detail::assign_keeping_region(aot, + ::toml::value(toml::array(1, v))); + + tab->insert(std::make_pair(k, aot)); return ok(true); } } if(tab->count(k) == 1) { - throw syntax_error("[error] toml::detail::insert_nested_key: " - "while inserting value to table: value already exists. " + - format_dotted_keys(first, last)); + if(tab->at(k).is(value_t::Table) && v.is(value_t::Table)) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: table (\"", + format_dotted_keys(first, last), "\") already exists."), + get_region(tab->at(k)), "table already exists here", + get_region(v), "table defined twice")); + } + else if(v.is(value_t::Table) && + tab->at(k).is(value_t::Array) && + tab->at(k).cast().size() > 0 && + tab->at(k).cast().front().is(value_t::Table)) + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: array of tables (\"", + format_dotted_keys(first, last), "\") already exists."), + get_region(tab->at(k)), "array of tables defined here", + get_region(v), "table conflicts with the previous array" + " of table")); + } + else + { + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: value (\"", + format_dotted_keys(first, last), "\") already exists."), + get_region(tab->at(k)), "value already exists here", + get_region(v), "value inserted twice")); + } } tab->insert(std::make_pair(k, v)); return ok(true); @@ -1039,29 +1074,46 @@ insert_nested_key(table& root, const toml::value& v, else { // if there is no corresponding value, insert it first. - if(tab->count(k) == 0) {(*tab)[k] = table{};} + // related: you don't need to write + // # [x] + // # [x.y] + // to write + // [x.y.z] + if(tab->count(k) == 0) + { + // the region of [x.y] is the same as [x.y.z]. + (*tab)[k] = v; // copy region_info_ + detail::assign_keeping_region((*tab)[k], + ::toml::value(::toml::table{})); + } // type checking... if(tab->at(k).is(value_t::Table)) { tab = std::addressof((*tab)[k].template cast()); } - else if(tab->at(k).is(value_t::Array)) // array-of-table case + else if(tab->at(k).is(value_t::Array)) // inserting to array-of-tables? { array& a = (*tab)[k].template cast(); if(!a.back().is(value_t::Table)) { - throw syntax_error("toml::detail::insert_nested_key: value " - "is not a table but an array: " + - format_dotted_keys(first, last)); + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: target (", + format_dotted_keys(first, std::next(iter)), + ") is neither table nor an array of tables"), + get_region(a.back()), concat_to_string("actual type is ", + a.back().type()), get_region(v), "inserting this")); } tab = std::addressof(a.back().template cast()); } else { - throw syntax_error("toml::detail::insert_nested_key: value " - "is not a table but an array: " + - format_dotted_keys(first, last)); + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: target (", + format_dotted_keys(first, std::next(iter)), + ") is neither table nor an array of tables"), + get_region(tab->at(k)), concat_to_string("actual type is ", + tab->at(k).type()), get_region(v), "inserting this")); } } } From e86777d19c67d056efc8f79c534d2548e92e5672 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 20:17:57 +0900 Subject: [PATCH 131/133] improve error message for conflicting tables --- toml/parser.hpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 74d7c47..697a015 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1004,24 +1004,35 @@ insert_nested_key(table& root, const toml::value& v, { if(tab->count(k) == 1) // there is already an array of table { - if(!(tab->at(k).is(value_t::Array))) + if(tab->at(k).is(value_t::Table)) + { + // show special err msg for conflicting table + throw syntax_error(format_underline(concat_to_string( + "[error] toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") cannot insert" + "ed"), get_region(tab->at(k)), "table already defined", + get_region(v), "this conflicts with the previous table")); + } + else if(!(tab->at(k).is(value_t::Array))) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: target value (\"", - format_dotted_keys(first, last), "\") is" - " not an array of tables"), get_region(tab->at(k)), - concat_to_string("actual type is ", tab->at(k).type()), - get_region(v), "this is an array of tables")); + "[error] toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") collides with" + " existing value"), get_region(tab->at(k)), + concat_to_string("this ", tab->at(k).type(), "value" + "already exists"), get_region(v), "while inserting" + "this array-of-tables")); } array& a = tab->at(k).template cast(); if(!(a.front().is(value_t::Table))) { throw syntax_error(format_underline(concat_to_string( - "[error] toml::insert_value: target value (\"", - format_dotted_keys(first, last), "\") is" - " not an array of tables"), get_region(tab->at(k)), - concat_to_string("actual type is ", tab->at(k).type()), - get_region(v), "this is an array of tables")); + "[error] toml::insert_value: array of table (\"", + format_dotted_keys(first, last), "\") collides with" + " existing value"), get_region(tab->at(k)), + concat_to_string("this ", tab->at(k).type(), "value" + "already exists"), get_region(v), "while inserting" + "this array-of-tables")); } a.push_back(v); return ok(true); From 57de57a1def3576c8f6cfa21701f447176527404 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 20:37:40 +0900 Subject: [PATCH 132/133] improve error message for empty value --- toml/parser.hpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/toml/parser.hpp b/toml/parser.hpp index 697a015..df1e1d9 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -954,11 +954,25 @@ parse_key_value_pair(location& loc) return err(std::move(msg)); } + const auto after_kvsp = loc.iter(); // err msg auto val = parse_value(loc); if(!val) { + std::string msg; + loc.iter() = after_kvsp; + if(sequence, maybe, lex_newline>::invoke(loc)) + { + loc.iter() = after_kvsp; + msg = format_underline("[error] toml::parse_key_value_pair: " + "missing value after key-value separator '='", loc, + "expected value, but got nothing"); + } + else + { + msg = val.unwrap_err(); + } loc.iter() = first; - return err(val.unwrap_err()); + return err(msg); } return ok(std::make_pair(std::move(key.unwrap()), std::move(val.unwrap()))); } From fd21d5dd9502f7b9dd7129d125d4c5bed5e8ac10 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Thu, 13 Dec 2018 20:44:10 +0900 Subject: [PATCH 133/133] add simplest copyright notice --- toml/combinator.hpp | 2 ++ toml/datetime.hpp | 2 ++ toml/exception.hpp | 2 ++ toml/from_toml.hpp | 2 ++ toml/get.hpp | 2 ++ toml/lexer.hpp | 2 ++ toml/parser.hpp | 2 ++ toml/region.hpp | 2 ++ toml/result.hpp | 2 ++ toml/storage.hpp | 2 ++ toml/string.hpp | 2 ++ toml/to_toml.hpp | 2 ++ toml/traits.hpp | 2 ++ toml/types.hpp | 2 ++ toml/utility.hpp | 2 ++ toml/value.hpp | 2 ++ 16 files changed, 32 insertions(+) diff --git a/toml/combinator.hpp b/toml/combinator.hpp index 30cd028..ff528eb 100644 --- a/toml/combinator.hpp +++ b/toml/combinator.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_COMBINATOR_HPP #define TOML11_COMBINATOR_HPP #include "traits.hpp" diff --git a/toml/datetime.hpp b/toml/datetime.hpp index 981d230..a32968a 100644 --- a/toml/datetime.hpp +++ b/toml/datetime.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_DATETIME #define TOML11_DATETIME #include diff --git a/toml/exception.hpp b/toml/exception.hpp index 87316b6..f92a7c1 100644 --- a/toml/exception.hpp +++ b/toml/exception.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_EXCEPTION #define TOML11_EXCEPTION #include diff --git a/toml/from_toml.hpp b/toml/from_toml.hpp index 065768c..a9348eb 100644 --- a/toml/from_toml.hpp +++ b/toml/from_toml.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_FROM_TOML #define TOML11_FROM_TOML #include "get.hpp" diff --git a/toml/get.hpp b/toml/get.hpp index 1cf15ba..cddbe73 100644 --- a/toml/get.hpp +++ b/toml/get.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_GET #define TOML11_GET #include "result.hpp" diff --git a/toml/lexer.hpp b/toml/lexer.hpp index 060195e..4f170c5 100644 --- a/toml/lexer.hpp +++ b/toml/lexer.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_LEXER_HPP #define TOML11_LEXER_HPP #include "combinator.hpp" diff --git a/toml/parser.hpp b/toml/parser.hpp index df1e1d9..e83de1d 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_PARSER_HPP #define TOML11_PARSER_HPP #include "result.hpp" diff --git a/toml/region.hpp b/toml/region.hpp index e254673..8a673f6 100644 --- a/toml/region.hpp +++ b/toml/region.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_REGION_H #define TOML11_REGION_H #include "exception.hpp" diff --git a/toml/result.hpp b/toml/result.hpp index 90ff1d5..3b13acd 100644 --- a/toml/result.hpp +++ b/toml/result.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_RESULT_H #define TOML11_RESULT_H #include diff --git a/toml/storage.hpp b/toml/storage.hpp index 4e3f114..30408f9 100644 --- a/toml/storage.hpp +++ b/toml/storage.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_STORAGE_HPP #define TOML11_STORAGE_HPP #include "utility.hpp" diff --git a/toml/string.hpp b/toml/string.hpp index bcda155..eef932d 100644 --- a/toml/string.hpp +++ b/toml/string.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_STRING_H #define TOML11_STRING_H #include diff --git a/toml/to_toml.hpp b/toml/to_toml.hpp index b8a5c29..348ce47 100644 --- a/toml/to_toml.hpp +++ b/toml/to_toml.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_TO_TOML #define TOML11_TO_TOML #include "value.hpp" diff --git a/toml/traits.hpp b/toml/traits.hpp index aec49b0..fc4ef85 100644 --- a/toml/traits.hpp +++ b/toml/traits.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_TRAITS #define TOML11_TRAITS #include diff --git a/toml/types.hpp b/toml/types.hpp index 0e9637d..cd13edf 100644 --- a/toml/types.hpp +++ b/toml/types.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_TYPES_H #define TOML11_TYPES_H #include "datetime.hpp" diff --git a/toml/utility.hpp b/toml/utility.hpp index 26eff2d..66b1985 100644 --- a/toml/utility.hpp +++ b/toml/utility.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_UTILITY #define TOML11_UTILITY #include "traits.hpp" diff --git a/toml/value.hpp b/toml/value.hpp index b48031f..68c063a 100644 --- a/toml/value.hpp +++ b/toml/value.hpp @@ -1,3 +1,5 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. #ifndef TOML11_VALUE #define TOML11_VALUE #include "traits.hpp"