diff --git a/.gitignore b/.gitignore index 567609b..941c73b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,24 @@ -build/ +# CMake +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake + +/libyaml-cpp.a +/test/gtest-1.8.0/googlemock/gtest/libgtest.a +/test/gtest-1.8.0/googlemock/gtest/libgtest_main.a +/test/gtest-1.8.0/googlemock/libgmock.a +/test/gtest-1.8.0/googlemock/libgmock_main.a +/test/run-tests +/util/parse +/util/read +/util/sandbox +/yaml-cpp-config-version.cmake +/yaml-cpp-config.cmake +/yaml-cpp-targets.cmake +/yaml-cpp.pc diff --git a/include/yaml-cpp/node/convert.h b/include/yaml-cpp/node/convert.h index 45a878a..ec52c79 100644 --- a/include/yaml-cpp/node/convert.h +++ b/include/yaml-cpp/node/convert.h @@ -8,9 +8,11 @@ #endif #include +#include #include #include #include +#include #include #include @@ -24,24 +26,32 @@ namespace YAML { class Binary; struct _Null; -template +template struct convert; + } // namespace YAML namespace YAML { namespace conversion { -inline bool IsInfinity(const std::string& input) { - return input == ".inf" || input == ".Inf" || input == ".INF" || - input == "+.inf" || input == "+.Inf" || input == "+.INF"; -} -inline bool IsNegativeInfinity(const std::string& input) { - return input == "-.inf" || input == "-.Inf" || input == "-.INF"; -} +static const std::regex re_true("true|True|TRUE"); +static const std::regex re_false("false|False|FALSE"); +static const std::regex re_decimal("[-+]?[0-9]+"); +static const std::regex re_octal("0o[0-7]+"); +static const std::regex re_hex("0x[0-9a-fA-F]+"); +static const std::regex re_float( + "[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?"); +static const std::regex re_inf("[-+]?(\\.inf|\\.Inf|\\.INF)"); +static const std::regex re_nan("\\.nan|\\.NaN|\\.NAN"); -inline bool IsNaN(const std::string& input) { - return input == ".nan" || input == ".NaN" || input == ".NAN"; -} +template +struct is_character { + using value_type = bool; + static constexpr bool value = + (std::is_same::value || std::is_same::value || + std::is_same::value || std::is_same::value); + using type = std::integral_constant; +}; } // Node @@ -76,7 +86,7 @@ struct convert { template struct convert { - static Node encode(const char(&rhs)[N]) { return Node(rhs); } + static Node encode(const char (&rhs)[N]) { return Node(rhs); } }; template <> @@ -88,70 +98,160 @@ struct convert<_Null> { } }; -#define YAML_DEFINE_CONVERT_STREAMABLE(type, negative_op) \ - template <> \ - struct convert { \ - static Node encode(const type& rhs) { \ - std::stringstream stream; \ - stream.precision(std::numeric_limits::digits10 + 1); \ - stream << rhs; \ - return Node(stream.str()); \ - } \ - \ - static bool decode(const Node& node, type& rhs) { \ - if (node.Type() != NodeType::Scalar) \ - return false; \ - const std::string& input = node.Scalar(); \ - std::stringstream stream(input); \ - stream.unsetf(std::ios::dec); \ - if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) \ - return true; \ - if (std::numeric_limits::has_infinity) { \ - if (conversion::IsInfinity(input)) { \ - rhs = std::numeric_limits::infinity(); \ - return true; \ - } else if (conversion::IsNegativeInfinity(input)) { \ - rhs = negative_op std::numeric_limits::infinity(); \ - return true; \ - } \ - } \ - \ - if (std::numeric_limits::has_quiet_NaN && \ - conversion::IsNaN(input)) { \ - rhs = std::numeric_limits::quiet_NaN(); \ - return true; \ - } \ - \ - return false; \ - } \ +// character types +template +struct convert::value>::type> { + static Node encode(const type& rhs) { return Node(std::string(1,rhs)); } + + static bool decode(const Node& node, type& rhs) { + if (node.Type() != NodeType::Scalar) + return false; + const std::string& input = node.Scalar(); + if (input.length() != 1) { + return false; + } + rhs = input[0]; + return true; + } +}; + +// signed integral (sans char) +template +struct convert< + type, typename std::enable_if< + std::is_integral::value && std::is_signed::value && + !conversion::is_character::value>::type> { + static Node encode(const type& rhs) { return Node(std::to_string(rhs)); } + + static bool decode(const Node& node, type& rhs) { + if (node.Type() != NodeType::Scalar) + return false; + const std::string& input = node.Scalar(); + long long num; + if (std::regex_match(input, conversion::re_decimal)) { + try { + num = std::stoll(input); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_octal)) { + try { + num = std::stoll(input.substr(2), nullptr, 8); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_hex)) { + try { + num = std::stoll(input.substr(2), nullptr, 16); + } catch (const std::out_of_range&) { + return false; + } + } else { + return false; + } + if (num > std::numeric_limits::max() || + num < std::numeric_limits::min()) { + return false; + } + rhs = num; + return true; + } +}; + +// unsigned integral (sans char) +template +struct convert< + type, typename std::enable_if< + std::is_integral::value && std::is_unsigned::value && + !conversion::is_character::value>::type> { + static Node encode(const type& rhs) { return Node(std::to_string(rhs)); } + + static bool decode(const Node& node, type& rhs) { + if (node.Type() != NodeType::Scalar) + return false; + const std::string& input = node.Scalar(); + unsigned long long num; + if (std::regex_match(input, conversion::re_decimal)) { + try { + num = std::stoull(input); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_octal)) { + try { + num = std::stoull(input.substr(2), nullptr, 8); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_hex)) { + try { + num = std::stoull(input.substr(2), nullptr, 16); + } catch (const std::out_of_range&) { + return false; + } + } else { + return false; + } + if (num > std::numeric_limits::max() || + num < std::numeric_limits::min()) { + return false; + } + rhs = num; + return true; + } +}; + +// floating point +template +struct convert< + type, typename std::enable_if::value>::type> { + static Node encode(const type& rhs) { + if (std::isnan(rhs)) { + return Node(".nan"); + } + if (std::isinf(rhs)) { + if (std::signbit(rhs)) { + return Node("-.inf"); + } + return Node(".inf"); + } + std::stringstream stream; + stream.precision(std::numeric_limits::max_digits10); + stream << rhs; + auto str = stream.str(); + if (std::regex_match(str, conversion::re_decimal)) { + return Node(str + "."); // Disambiguate float from int + } + return Node(str); } -#define YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(type) \ - YAML_DEFINE_CONVERT_STREAMABLE(type, -) - -#define YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(type) \ - YAML_DEFINE_CONVERT_STREAMABLE(type, +) - -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(int); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(short); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long long); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned short); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long long); - -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(char); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(signed char); -YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned char); - -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(float); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(double); -YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long double); - -#undef YAML_DEFINE_CONVERT_STREAMABLE_SIGNED -#undef YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED -#undef YAML_DEFINE_CONVERT_STREAMABLE + static bool decode(const Node& node, type& rhs) { + if (node.Type() != NodeType::Scalar) + return false; + const std::string& input = node.Scalar(); + long double num; + if (std::regex_match(input, conversion::re_float)) { + try { + num = std::stold(input); + } catch (const std::out_of_range&) { + return false; + } + } else if (std::regex_match(input, conversion::re_inf)) { + if (input[0] == '-') { + num = -std::numeric_limits::infinity(); + } else { + num = std::numeric_limits::infinity(); + } + } else if (std::regex_match(input, conversion::re_nan)) { + num = std::numeric_limits::quiet_NaN(); + } else { + return false; + } + rhs = num; + return true; + } +}; // bool template <> diff --git a/include/yaml-cpp/node/node.h b/include/yaml-cpp/node/node.h index 1ded7d2..e855863 100644 --- a/include/yaml-cpp/node/node.h +++ b/include/yaml-cpp/node/node.h @@ -138,7 +138,7 @@ YAML_CPP_API bool operator==(const Node& lhs, const Node& rhs); YAML_CPP_API Node Clone(const Node& node); -template +template struct convert; } diff --git a/src/emitterstate.cpp b/src/emitterstate.cpp index 3542aaf..d5f39e7 100644 --- a/src/emitterstate.cpp +++ b/src/emitterstate.cpp @@ -24,8 +24,8 @@ EmitterState::EmitterState() m_seqFmt.set(Block); m_mapFmt.set(Block); m_mapKeyFmt.set(Auto); - m_floatPrecision.set(std::numeric_limits::digits10 + 1); - m_doublePrecision.set(std::numeric_limits::digits10 + 1); + m_floatPrecision.set(std::numeric_limits::max_digits10); + m_doublePrecision.set(std::numeric_limits::max_digits10); } EmitterState::~EmitterState() {} @@ -349,7 +349,7 @@ bool EmitterState::SetMapKeyFormat(EMITTER_MANIP value, FmtScope::value scope) { } bool EmitterState::SetFloatPrecision(std::size_t value, FmtScope::value scope) { - if (value > std::numeric_limits::digits10 + 1) + if (value > std::numeric_limits::max_digits10) return false; _Set(m_floatPrecision, value, scope); return true; @@ -357,7 +357,7 @@ bool EmitterState::SetFloatPrecision(std::size_t value, FmtScope::value scope) { bool EmitterState::SetDoublePrecision(std::size_t value, FmtScope::value scope) { - if (value > std::numeric_limits::digits10 + 1) + if (value > std::numeric_limits::max_digits10) return false; _Set(m_doublePrecision, value, scope); return true; diff --git a/test/integration/emitter_test.cpp b/test/integration/emitter_test.cpp index 2780838..f6176b1 100644 --- a/test/integration/emitter_test.cpp +++ b/test/integration/emitter_test.cpp @@ -904,7 +904,7 @@ TEST_F(EmitterTest, DefaultPrecision) { out << 1.234f; out << 3.14159265358979; out << EndSeq; - ExpectEmit("- 1.234\n- 3.14159265358979"); + ExpectEmit("- 1.23399997\n- 3.14159265358979"); } TEST_F(EmitterTest, SetPrecision) { diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp index 02bb8fe..7f6a65c 100644 --- a/test/integration/load_node_test.cpp +++ b/test/integration/load_node_test.cpp @@ -20,7 +20,7 @@ TEST(LoadNodeTest, FallbackValues) { } TEST(LoadNodeTest, NumericConversion) { - Node node = Load("[1.5, 1, .nan, .inf, -.inf, 0x15, 015]"); + Node node = Load("[1.5, 1, .nan, .inf, -.inf, 0x15, 0o15]"); EXPECT_EQ(1.5f, node[0].as()); EXPECT_EQ(1.5, node[0].as()); EXPECT_THROW(node[0].as(), TypedBadConversion); diff --git a/test/node/node_test.cpp b/test/node/node_test.cpp index 485ad09..0223b2c 100644 --- a/test/node/node_test.cpp +++ b/test/node/node_test.cpp @@ -1,10 +1,10 @@ #include "yaml-cpp/emitter.h" -#include "yaml-cpp/node/emit.h" -#include "yaml-cpp/node/node.h" -#include "yaml-cpp/node/impl.h" #include "yaml-cpp/node/convert.h" -#include "yaml-cpp/node/iterator.h" #include "yaml-cpp/node/detail/impl.h" +#include "yaml-cpp/node/emit.h" +#include "yaml-cpp/node/impl.h" +#include "yaml-cpp/node/iterator.h" +#include "yaml-cpp/node/node.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -393,6 +393,8 @@ class NodeEmitterTest : public ::testing::Test { protected: void ExpectOutput(const std::string& output, const Node& node) { Emitter emitter; + // ASSERT_TRUE(emitter.SetFloatPrecision(3)); + // ASSERT_TRUE(emitter.SetDoublePrecision(3)); emitter << node; ASSERT_TRUE(emitter.good()); EXPECT_EQ(output, emitter.c_str()); @@ -410,29 +412,32 @@ class NodeEmitterTest : public ::testing::Test { TEST_F(NodeEmitterTest, SimpleFlowSeqNode) { Node node; node.SetStyle(EmitterStyle::Flow); - node.push_back(1.01); - node.push_back(2.01); - node.push_back(3.01); + node.push_back(0.32); + node.push_back(0.64); + node.push_back(10.24); - ExpectOutput("[1.01, 2.01, 3.01]", node); + ExpectOutput("[0.32000000000000001, 0.64000000000000001, 10.24]", node); } TEST_F(NodeEmitterTest, NestFlowSeqNode) { Node node, cell0, cell1; - cell0.push_back(1.01); - cell0.push_back(2.01); - cell0.push_back(3.01); + cell0.push_back(1.08); + cell0.push_back(2.08); + cell0.push_back(3.08); - cell1.push_back(4.01); - cell1.push_back(5.01); - cell1.push_back(6.01); + cell1.push_back(4.08); + cell1.push_back(5.08); + cell1.push_back(6.08); node.SetStyle(EmitterStyle::Flow); node.push_back(cell0); node.push_back(cell1); - ExpectOutput("[[1.01, 2.01, 3.01], [4.01, 5.01, 6.01]]", node); + ExpectOutput( + "[[1.0800000000000001, 2.0800000000000001, 3.0800000000000001], " + "[4.0800000000000001, 5.0800000000000001, 6.0800000000000001]]", + node); } TEST_F(NodeEmitterTest, MixBlockFlowSeqNode) { @@ -451,7 +456,13 @@ TEST_F(NodeEmitterTest, MixBlockFlowSeqNode) { node.push_back(cell0); node.push_back(cell1); - ExpectOutput("- [1.01, 2.01, 3.01]\n-\n - 4.01\n - 5.01\n - 6.01", node); + ExpectOutput( + R"(- [1.01, 2.0099999999999998, 3.0099999999999998] +- + - 4.0099999999999998 + - 5.0099999999999998 + - 6.0099999999999998)", + node); } TEST_F(NodeEmitterTest, NestBlockFlowMapListNode) { @@ -467,7 +478,9 @@ TEST_F(NodeEmitterTest, NestBlockFlowMapListNode) { blockNode.push_back(1.01); blockNode.push_back(mapNode); - ExpectOutput("- 1.01\n- {position: [1.01, 2.01, 3.01]}", blockNode); + ExpectOutput( + "- 1.01\n- {position: [1.01, 2.0099999999999998, 3.0099999999999998]}", + blockNode); } TEST_F(NodeEmitterTest, NestBlockMixMapListNode) { @@ -484,8 +497,10 @@ TEST_F(NodeEmitterTest, NestBlockMixMapListNode) { blockNode["object"] = mapNode; ExpectAnyOutput(blockNode, - "scalar: 1.01\nobject: {position: [1.01, 2.01, 3.01]}", - "object: {position: [1.01, 2.01, 3.01]}\nscalar: 1.01"); + "scalar: 1.01\nobject: {position: [1.01, 2.0099999999999998, " + "3.0099999999999998]}", + "object: {position: [1.01, 2.0099999999999998, " + "3.0099999999999998]}\nscalar: 1.01"); } TEST_F(NodeEmitterTest, NestBlockMapListNode) { @@ -498,7 +513,9 @@ TEST_F(NodeEmitterTest, NestBlockMapListNode) { mapNode.SetStyle(EmitterStyle::Block); mapNode["position"] = node; - ExpectOutput("position:\n - 1.01\n - 2.01\n - 3.01", mapNode); + ExpectOutput( + "position:\n - 1.01\n - 2.0099999999999998\n - 3.0099999999999998", + mapNode); } TEST_F(NodeEmitterTest, NestFlowMapListNode) { @@ -511,7 +528,8 @@ TEST_F(NodeEmitterTest, NestFlowMapListNode) { mapNode.SetStyle(EmitterStyle::Flow); mapNode["position"] = node; - ExpectOutput("{position: [1.01, 2.01, 3.01]}", mapNode); + ExpectOutput("{position: [1.01, 2.0099999999999998, 3.0099999999999998]}", + mapNode); } } }