diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ab0f1d..ee67fce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,8 +22,8 @@ include(CheckCXXCompilerFlag) project(YAML_CPP) set(YAML_CPP_VERSION_MAJOR "0") -set(YAML_CPP_VERSION_MINOR "3") -set(YAML_CPP_VERSION_PATCH "0") +set(YAML_CPP_VERSION_MINOR "5") +set(YAML_CPP_VERSION_PATCH "1") set(YAML_CPP_VERSION "${YAML_CPP_VERSION_MAJOR}.${YAML_CPP_VERSION_MINOR}.${YAML_CPP_VERSION_PATCH}") enable_testing() @@ -80,7 +80,7 @@ endfunction(add_sources) set(header_directory "include/yaml-cpp/") file(GLOB sources "src/[a-zA-Z]*.cpp") -file(GLOB public_headers "include/yaml-cpp/[a-zA-Z]*.h") +file(GLOB_RECURSE public_headers "include/yaml-cpp/[a-zA-Z]*.h") file(GLOB private_headers "src/[a-zA-Z]*.h") if(YAML_CPP_BUILD_CONTRIB) @@ -110,8 +110,11 @@ if(VERBOSE) message(STATUS "contrib_private_headers: ${contrib_private_headers}") endif() -include_directories(${YAML_CPP_SOURCE_DIR}/include) include_directories(${YAML_CPP_SOURCE_DIR}/src) +include_directories(${YAML_CPP_SOURCE_DIR}/include) + +find_package(Boost REQUIRED) +include_directories(${Boost_INCLUDE_DIRS}) ### diff --git a/include/yaml-cpp/exceptions.h b/include/yaml-cpp/exceptions.h index b56c5a9..cdf766e 100644 --- a/include/yaml-cpp/exceptions.h +++ b/include/yaml-cpp/exceptions.h @@ -66,6 +66,17 @@ const char* const AMBIGUOUS_ANCHOR = "cannot assign the same alias to multiple nodes"; const char* const UNKNOWN_ANCHOR = "the referenced anchor is not defined"; +const char* const INVALID_NODE = + "invalid node; this may result from using a map iterator as a sequence " + "iterator, or vice-versa"; +const char* const INVALID_SCALAR = "invalid scalar"; +const char* const KEY_NOT_FOUND = "key not found"; +const char* const BAD_CONVERSION = "bad conversion"; +const char* const BAD_DEREFERENCE = "bad dereference"; +const char* const BAD_SUBSCRIPT = "operator[] call on a scalar"; +const char* const BAD_PUSHBACK = "appending to a non-sequence"; +const char* const BAD_INSERT = "inserting in a non-convertible-to-map"; + const char* const UNMATCHED_GROUP_TAG = "unmatched group tag"; const char* const UNEXPECTED_END_SEQ = "unexpected end sequence token"; const char* const UNEXPECTED_END_MAP = "unexpected end map token"; @@ -74,6 +85,27 @@ const char* const SINGLE_QUOTED_CHAR = const char* const INVALID_ANCHOR = "invalid anchor"; const char* const INVALID_ALIAS = "invalid alias"; const char* const INVALID_TAG = "invalid tag"; +const char* const BAD_FILE = "bad file"; + +template +inline const std::string KEY_NOT_FOUND_WITH_KEY( + const T&, typename disable_if >::type* = 0) { + return KEY_NOT_FOUND; +} + +inline const std::string KEY_NOT_FOUND_WITH_KEY(const std::string& key) { + std::stringstream stream; + stream << KEY_NOT_FOUND << ": " << key; + return stream.str(); +} + +template +inline const std::string KEY_NOT_FOUND_WITH_KEY( + const T& key, typename enable_if >::type* = 0) { + std::stringstream stream; + stream << KEY_NOT_FOUND << ": " << key; + return stream.str(); +} } class Exception : public std::runtime_error { @@ -101,11 +133,95 @@ class ParserException : public Exception { : Exception(mark_, msg_) {} }; +class RepresentationException : public Exception { + public: + RepresentationException(const Mark& mark_, const std::string& msg_) + : Exception(mark_, msg_) {} +}; + +// representation exceptions +class InvalidScalar : public RepresentationException { + public: + InvalidScalar(const Mark& mark_) + : RepresentationException(mark_, ErrorMsg::INVALID_SCALAR) {} +}; + +class KeyNotFound : public RepresentationException { + public: + template + KeyNotFound(const Mark& mark_, const T& key_) + : RepresentationException(mark_, ErrorMsg::KEY_NOT_FOUND_WITH_KEY(key_)) { + } +}; + +template +class TypedKeyNotFound : public KeyNotFound { + public: + TypedKeyNotFound(const Mark& mark_, const T& key_) + : KeyNotFound(mark_, key_), key(key_) {} + virtual ~TypedKeyNotFound() throw() {} + + T key; +}; + +template +inline TypedKeyNotFound MakeTypedKeyNotFound(const Mark& mark, + const T& key) { + return TypedKeyNotFound(mark, key); +} + +class InvalidNode : public RepresentationException { + public: + InvalidNode() + : RepresentationException(Mark::null_mark(), ErrorMsg::INVALID_NODE) {} +}; + +class BadConversion : public RepresentationException { + public: + BadConversion() + : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_CONVERSION) {} +}; + +template +class TypedBadConversion : public BadConversion { + public: + TypedBadConversion() : BadConversion() {} +}; + +class BadDereference : public RepresentationException { + public: + BadDereference() + : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_DEREFERENCE) {} +}; + +class BadSubscript : public RepresentationException { + public: + BadSubscript() + : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_SUBSCRIPT) {} +}; + +class BadPushback : public RepresentationException { + public: + BadPushback() + : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_PUSHBACK) {} +}; + +class BadInsert : public RepresentationException { + public: + BadInsert() + : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_INSERT) {} +}; + class EmitterException : public Exception { public: EmitterException(const std::string& msg_) : Exception(Mark::null_mark(), msg_) {} }; + +class BadFile : public Exception { + public: + BadFile() : Exception(Mark::null_mark(), ErrorMsg::BAD_FILE) {} +}; } #endif // EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/convert.h b/include/yaml-cpp/node/convert.h new file mode 100644 index 0000000..dd74514 --- /dev/null +++ b/include/yaml-cpp/node/convert.h @@ -0,0 +1,286 @@ +#ifndef NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include +#include +#include +#include +#include + +#include "yaml-cpp/binary.h" +#include "yaml-cpp/node/impl.h" +#include "yaml-cpp/node/iterator.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/type.h" +#include "yaml-cpp/null.h" + +namespace YAML { +class Binary; +struct _Null; +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"; +} + +inline bool IsNaN(const std::string& input) { + return input == ".nan" || input == ".NaN" || input == ".NAN"; +} +} + +// std::string +template <> +struct convert { + static Node encode(const std::string& rhs) { return Node(rhs); } + + static bool decode(const Node& node, std::string& rhs) { + if (!node.IsScalar()) + return false; + rhs = node.Scalar(); + return true; + } +}; + +// C-strings can only be encoded +template <> +struct convert { + static Node encode(const char*& rhs) { return Node(rhs); } +}; + +template +struct convert { + static Node encode(const char (&rhs)[N]) { return Node(rhs); } +}; + +template <> +struct convert<_Null> { + static Node encode(const _Null& /* rhs */) { return Node(); } + + static bool decode(const Node& node, _Null& /* rhs */) { + return node.IsNull(); + } +}; + +#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; \ + } \ + } + +#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 + +// bool +template <> +struct convert { + static Node encode(bool rhs) { return rhs ? Node("true") : Node("false"); } + + YAML_CPP_API static bool decode(const Node& node, bool& rhs); +}; + +// std::map +template +struct convert > { + static Node encode(const std::map& rhs) { + Node node(NodeType::Map); + for (typename std::map::const_iterator it = rhs.begin(); + it != rhs.end(); ++it) + node.force_insert(it->first, it->second); + return node; + } + + static bool decode(const Node& node, std::map& rhs) { + if (!node.IsMap()) + return false; + + rhs.clear(); + for (const_iterator it = node.begin(); it != node.end(); ++it) +#if defined(__GNUC__) && __GNUC__ < 4 + // workaround for GCC 3: + rhs[it->first.template as()] = it->second.template as(); +#else + rhs[it->first.as()] = it->second.as(); +#endif + return true; + } +}; + +// std::vector +template +struct convert > { + static Node encode(const std::vector& rhs) { + Node node(NodeType::Sequence); + for (typename std::vector::const_iterator it = rhs.begin(); + it != rhs.end(); ++it) + node.push_back(*it); + return node; + } + + static bool decode(const Node& node, std::vector& rhs) { + if (!node.IsSequence()) + return false; + + rhs.clear(); + for (const_iterator it = node.begin(); it != node.end(); ++it) +#if defined(__GNUC__) && __GNUC__ < 4 + // workaround for GCC 3: + rhs.push_back(it->template as()); +#else + rhs.push_back(it->as()); +#endif + return true; + } +}; + +// std::list +template +struct convert > { + static Node encode(const std::list& rhs) { + Node node(NodeType::Sequence); + for (typename std::list::const_iterator it = rhs.begin(); + it != rhs.end(); ++it) + node.push_back(*it); + return node; + } + + static bool decode(const Node& node, std::list& rhs) { + if (!node.IsSequence()) + return false; + + rhs.clear(); + for (const_iterator it = node.begin(); it != node.end(); ++it) +#if defined(__GNUC__) && __GNUC__ < 4 + // workaround for GCC 3: + rhs.push_back(it->template as()); +#else + rhs.push_back(it->as()); +#endif + return true; + } +}; + +// std::pair +template +struct convert > { + static Node encode(const std::pair& rhs) { + Node node(NodeType::Sequence); + node.push_back(rhs.first); + node.push_back(rhs.second); + return node; + } + + static bool decode(const Node& node, std::pair& rhs) { + if (!node.IsSequence()) + return false; + if (node.size() != 2) + return false; + +#if defined(__GNUC__) && __GNUC__ < 4 + // workaround for GCC 3: + rhs.first = node[0].template as(); +#else + rhs.first = node[0].as(); +#endif +#if defined(__GNUC__) && __GNUC__ < 4 + // workaround for GCC 3: + rhs.second = node[1].template as(); +#else + rhs.second = node[1].as(); +#endif + return true; + } +}; + +// binary +template <> +struct convert { + static Node encode(const Binary& rhs) { + return Node(EncodeBase64(rhs.data(), rhs.size())); + } + + static bool decode(const Node& node, Binary& rhs) { + if (!node.IsScalar()) + return false; + + std::vector data = DecodeBase64(node.Scalar()); + if (data.empty() && !node.Scalar().empty()) + return false; + + rhs.swap(data); + return true; + } +}; +} + +#endif // NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/bool_type.h b/include/yaml-cpp/node/detail/bool_type.h new file mode 100644 index 0000000..2c80705 --- /dev/null +++ b/include/yaml-cpp/node/detail/bool_type.h @@ -0,0 +1,26 @@ +#ifndef NODE_DETAIL_BOOL_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_DETAIL_BOOL_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +namespace YAML { +namespace detail { +struct unspecified_bool { + struct NOT_ALLOWED; + static void true_value(NOT_ALLOWED*) {} +}; +typedef void (*unspecified_bool_type)(unspecified_bool::NOT_ALLOWED*); +} +} + +#define YAML_CPP_OPERATOR_BOOL() \ + operator YAML::detail::unspecified_bool_type() const { \ + return this->operator!() ? 0 \ + : &YAML::detail::unspecified_bool::true_value; \ + } + +#endif // NODE_DETAIL_BOOL_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/impl.h b/include/yaml-cpp/node/detail/impl.h new file mode 100644 index 0000000..43eaa38 --- /dev/null +++ b/include/yaml-cpp/node/detail/impl.h @@ -0,0 +1,176 @@ +#ifndef NODE_DETAIL_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_DETAIL_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/node/detail/node.h" +#include "yaml-cpp/node/detail/node_data.h" +#include + +namespace YAML { +namespace detail { +template +struct get_idx { + static node* get(const std::vector& /* sequence */, + const Key& /* key */, shared_memory_holder /* pMemory */) { + return 0; + } +}; + +template +struct get_idx< + Key, typename boost::enable_if_c::value && + !boost::is_same::value>::type> { + static node* get(const std::vector& sequence, const Key& key, + shared_memory_holder /* pMemory */) { + return key < sequence.size() ? sequence[key] : 0; + } + + static node* get(std::vector& sequence, const Key& key, + shared_memory_holder pMemory) { + if (key > sequence.size()) + return 0; + if (key == sequence.size()) + sequence.push_back(&pMemory->create_node()); + return sequence[key]; + } +}; + +template +struct get_idx >::type> { + static node* get(const std::vector& sequence, const Key& key, + shared_memory_holder pMemory) { + return key >= 0 ? get_idx::get( + sequence, static_cast(key), pMemory) + : 0; + } + static node* get(std::vector& sequence, const Key& key, + shared_memory_holder pMemory) { + return key >= 0 ? get_idx::get( + sequence, static_cast(key), pMemory) + : 0; + } +}; + +// indexing +template +inline node& node_data::get(const Key& key, + shared_memory_holder pMemory) const { + switch (m_type) { + case NodeType::Map: + break; + case NodeType::Undefined: + case NodeType::Null: + return pMemory->create_node(); + case NodeType::Sequence: + if (node* pNode = get_idx::get(m_sequence, key, pMemory)) + return *pNode; + return pMemory->create_node(); + case NodeType::Scalar: + throw BadSubscript(); + } + + for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) { + if (equals(*it->first, key, pMemory)) + return *it->second; + } + + return pMemory->create_node(); +} + +template +inline node& node_data::get(const Key& key, shared_memory_holder pMemory) { + switch (m_type) { + case NodeType::Map: + break; + case NodeType::Undefined: + case NodeType::Null: + case NodeType::Sequence: + if (node* pNode = get_idx::get(m_sequence, key, pMemory)) { + m_type = NodeType::Sequence; + return *pNode; + } + + convert_to_map(pMemory); + break; + case NodeType::Scalar: + throw BadSubscript(); + } + + for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) { + if (equals(*it->first, key, pMemory)) + return *it->second; + } + + node& k = convert_to_node(key, pMemory); + node& v = pMemory->create_node(); + insert_map_pair(k, v); + return v; +} + +template +inline bool node_data::remove(const Key& key, shared_memory_holder pMemory) { + if (m_type != NodeType::Map) + return false; + + for (node_map::iterator it = m_map.begin(); it != m_map.end(); ++it) { + if (equals(*it->first, key, pMemory)) { + m_map.erase(it); + return true; + } + } + + return false; +} + +// map +template +inline void node_data::force_insert(const Key& key, const Value& value, + shared_memory_holder pMemory) { + switch (m_type) { + case NodeType::Map: + break; + case NodeType::Undefined: + case NodeType::Null: + case NodeType::Sequence: + convert_to_map(pMemory); + break; + case NodeType::Scalar: + throw BadInsert(); + } + + node& k = convert_to_node(key, pMemory); + node& v = convert_to_node(value, pMemory); + insert_map_pair(k, v); +} + +template +inline bool node_data::equals(node& node, const T& rhs, + shared_memory_holder pMemory) { + T lhs; + if (convert::decode(Node(node, pMemory), lhs)) + return lhs == rhs; + return false; +} + +inline bool node_data::equals(node& node, const char* rhs, + shared_memory_holder pMemory) { + return equals(node, rhs, pMemory); +} + +template +inline node& node_data::convert_to_node(const T& rhs, + shared_memory_holder pMemory) { + Node value = convert::encode(rhs); + value.EnsureNodeExists(); + pMemory->merge(*value.m_pMemory); + return *value.m_pNode; +} +} +} + +#endif // NODE_DETAIL_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/iterator.h b/include/yaml-cpp/node/detail/iterator.h new file mode 100644 index 0000000..2c701af --- /dev/null +++ b/include/yaml-cpp/node/detail/iterator.h @@ -0,0 +1,65 @@ +#ifndef VALUE_DETAIL_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_DETAIL_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/dll.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/detail/node_iterator.h" +#include +#include + +namespace YAML { +namespace detail { +struct iterator_value; + +template +class iterator_base + : public boost::iterator_adaptor, node_iterator, V, + std::forward_iterator_tag, V> { + private: + template + friend class iterator_base; + struct enabler {}; + typedef typename iterator_base::base_type base_type; + + public: + typedef typename iterator_base::value_type value_type; + + public: + iterator_base() {} + explicit iterator_base(base_type rhs, shared_memory_holder pMemory) + : iterator_base::iterator_adaptor_(rhs), m_pMemory(pMemory) {} + + template + iterator_base(const iterator_base& rhs, + typename boost::enable_if, + enabler>::type = enabler()) + : iterator_base::iterator_adaptor_(rhs.base()), + m_pMemory(rhs.m_pMemory) {} + + private: + friend class boost::iterator_core_access; + + void increment() { this->base_reference() = boost::next(this->base()); } + + value_type dereference() const { + const typename base_type::value_type& v = *this->base(); + if (v.pNode) + return value_type(Node(*v, m_pMemory)); + if (v.first && v.second) + return value_type(Node(*v.first, m_pMemory), Node(*v.second, m_pMemory)); + return value_type(); + } + + private: + shared_memory_holder m_pMemory; +}; +} +} + +#endif // VALUE_DETAIL_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/iterator_fwd.h b/include/yaml-cpp/node/detail/iterator_fwd.h new file mode 100644 index 0000000..c54a258 --- /dev/null +++ b/include/yaml-cpp/node/detail/iterator_fwd.h @@ -0,0 +1,28 @@ +#ifndef VALUE_DETAIL_ITERATOR_FWD_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_DETAIL_ITERATOR_FWD_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/dll.h" +#include +#include +#include + +namespace YAML { +class node; + +namespace detail { +struct iterator_value; +template +class iterator_base; +} + +typedef detail::iterator_base iterator; +typedef detail::iterator_base const_iterator; +} + +#endif // VALUE_DETAIL_ITERATOR_FWD_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/memory.h b/include/yaml-cpp/node/detail/memory.h new file mode 100644 index 0000000..e3d344b --- /dev/null +++ b/include/yaml-cpp/node/detail/memory.h @@ -0,0 +1,48 @@ +#ifndef VALUE_DETAIL_MEMORY_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_DETAIL_MEMORY_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include +#include +#include + +#include "yaml-cpp/dll.h" +#include "yaml-cpp/node/ptr.h" + +namespace YAML { +namespace detail { +class node; +} // namespace detail +} // namespace YAML + +namespace YAML { +namespace detail { +class YAML_CPP_API memory { + public: + node& create_node(); + void merge(const memory& rhs); + + private: + typedef std::set Nodes; + Nodes m_nodes; +}; + +class YAML_CPP_API memory_holder { + public: + memory_holder() : m_pMemory(new memory) {} + + node& create_node() { return m_pMemory->create_node(); } + void merge(memory_holder& rhs); + + private: + boost::shared_ptr m_pMemory; +}; +} +} + +#endif // VALUE_DETAIL_MEMORY_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/node.h b/include/yaml-cpp/node/detail/node.h new file mode 100644 index 0000000..29605dd --- /dev/null +++ b/include/yaml-cpp/node/detail/node.h @@ -0,0 +1,155 @@ +#ifndef NODE_DETAIL_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_DETAIL_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/emitterstyle.h" +#include "yaml-cpp/dll.h" +#include "yaml-cpp/node/type.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/detail/node_ref.h" +#include +#include + +namespace YAML { +namespace detail { +class node : private boost::noncopyable { + public: + node() : m_pRef(new node_ref) {} + + bool is(const node& rhs) const { return m_pRef == rhs.m_pRef; } + const node_ref* ref() const { return m_pRef.get(); } + + bool is_defined() const { return m_pRef->is_defined(); } + NodeType::value type() const { return m_pRef->type(); } + + const std::string& scalar() const { return m_pRef->scalar(); } + const std::string& tag() const { return m_pRef->tag(); } + EmitterStyle::value style() const { return m_pRef->style(); } + + void mark_defined() { + if (is_defined()) + return; + + m_pRef->mark_defined(); + for (nodes::iterator it = m_dependencies.begin(); + it != m_dependencies.end(); ++it) + (*it)->mark_defined(); + m_dependencies.clear(); + } + + void add_dependency(node& rhs) { + if (is_defined()) + rhs.mark_defined(); + else + m_dependencies.insert(&rhs); + } + + void set_ref(const node& rhs) { + if (rhs.is_defined()) + mark_defined(); + m_pRef = rhs.m_pRef; + } + void set_data(const node& rhs) { + if (rhs.is_defined()) + mark_defined(); + m_pRef->set_data(*rhs.m_pRef); + } + + void set_type(NodeType::value type) { + if (type != NodeType::Undefined) + mark_defined(); + m_pRef->set_type(type); + } + void set_null() { + mark_defined(); + m_pRef->set_null(); + } + void set_scalar(const std::string& scalar) { + mark_defined(); + m_pRef->set_scalar(scalar); + } + void set_tag(const std::string& tag) { + mark_defined(); + m_pRef->set_tag(tag); + } + + // style + void set_style(EmitterStyle::value style) { + mark_defined(); + m_pRef->set_style(style); + } + + // size/iterator + std::size_t size() const { return m_pRef->size(); } + + const_node_iterator begin() const { + return static_cast(*m_pRef).begin(); + } + node_iterator begin() { return m_pRef->begin(); } + + const_node_iterator end() const { + return static_cast(*m_pRef).end(); + } + node_iterator end() { return m_pRef->end(); } + + // sequence + void push_back(node& node, shared_memory_holder pMemory) { + m_pRef->push_back(node, pMemory); + node.add_dependency(*this); + } + void insert(node& key, node& value, shared_memory_holder pMemory) { + m_pRef->insert(key, value, pMemory); + key.add_dependency(*this); + value.add_dependency(*this); + } + + // indexing + template + node& get(const Key& key, shared_memory_holder pMemory) const { + return static_cast(*m_pRef).get(key, pMemory); + } + template + node& get(const Key& key, shared_memory_holder pMemory) { + node& value = m_pRef->get(key, pMemory); + value.add_dependency(*this); + return value; + } + template + bool remove(const Key& key, shared_memory_holder pMemory) { + return m_pRef->remove(key, pMemory); + } + + node& get(node& key, shared_memory_holder pMemory) const { + return static_cast(*m_pRef).get(key, pMemory); + } + node& get(node& key, shared_memory_holder pMemory) { + node& value = m_pRef->get(key, pMemory); + key.add_dependency(*this); + value.add_dependency(*this); + return value; + } + bool remove(node& key, shared_memory_holder pMemory) { + return m_pRef->remove(key, pMemory); + } + + // map + template + void force_insert(const Key& key, const Value& value, + shared_memory_holder pMemory) { + m_pRef->force_insert(key, value, pMemory); + } + + private: + shared_node_ref m_pRef; + typedef std::set nodes; + nodes m_dependencies; +}; +} +} + +#endif // NODE_DETAIL_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/node_data.h b/include/yaml-cpp/node/detail/node_data.h new file mode 100644 index 0000000..b6c12b3 --- /dev/null +++ b/include/yaml-cpp/node/detail/node_data.h @@ -0,0 +1,128 @@ +#ifndef VALUE_DETAIL_NODE_DATA_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_DETAIL_NODE_DATA_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "yaml-cpp/dll.h" +#include "yaml-cpp/node/detail/node_iterator.h" +#include "yaml-cpp/node/iterator.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/type.h" + +namespace YAML { +namespace detail { +class node; +} // namespace detail +} // namespace YAML + +namespace YAML { +namespace detail { +class YAML_CPP_API node_data : private boost::noncopyable { + public: + node_data(); + + void mark_defined(); + void set_type(NodeType::value type); + void set_tag(const std::string& tag); + void set_null(); + void set_scalar(const std::string& scalar); + void set_style(EmitterStyle::value style); + + bool is_defined() const { return m_isDefined; } + NodeType::value type() const { + return m_isDefined ? m_type : NodeType::Undefined; + } + const std::string& scalar() const { return m_scalar; } + const std::string& tag() const { return m_tag; } + EmitterStyle::value style() const { return m_style; } + + // size/iterator + std::size_t size() const; + + const_node_iterator begin() const; + node_iterator begin(); + + const_node_iterator end() const; + node_iterator end(); + + // sequence + void push_back(node& node, shared_memory_holder pMemory); + void insert(node& key, node& value, shared_memory_holder pMemory); + + // indexing + template + node& get(const Key& key, shared_memory_holder pMemory) const; + template + node& get(const Key& key, shared_memory_holder pMemory); + template + bool remove(const Key& key, shared_memory_holder pMemory); + + node& get(node& key, shared_memory_holder pMemory) const; + node& get(node& key, shared_memory_holder pMemory); + bool remove(node& key, shared_memory_holder pMemory); + + // map + template + void force_insert(const Key& key, const Value& value, + shared_memory_holder pMemory); + + public: + static std::string empty_scalar; + + private: + void compute_seq_size() const; + void compute_map_size() const; + + void reset_sequence(); + void reset_map(); + + void insert_map_pair(node& key, node& value); + void convert_to_map(shared_memory_holder pMemory); + void convert_sequence_to_map(shared_memory_holder pMemory); + + template + static bool equals(node& node, const T& rhs, shared_memory_holder pMemory); + static bool equals(node& node, const char* rhs, shared_memory_holder pMemory); + + template + static node& convert_to_node(const T& rhs, shared_memory_holder pMemory); + + private: + bool m_isDefined; + NodeType::value m_type; + std::string m_tag; + EmitterStyle::value m_style; + + // scalar + std::string m_scalar; + + // sequence + typedef std::vector node_seq; + node_seq m_sequence; + + mutable std::size_t m_seqSize; + + // map + typedef std::map node_map; + node_map m_map; + + typedef std::pair kv_pair; + typedef std::list kv_pairs; + mutable kv_pairs m_undefinedPairs; +}; +} +} + +#endif // VALUE_DETAIL_NODE_DATA_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/node_iterator.h b/include/yaml-cpp/node/detail/node_iterator.h new file mode 100644 index 0000000..9669c81 --- /dev/null +++ b/include/yaml-cpp/node/detail/node_iterator.h @@ -0,0 +1,159 @@ +#ifndef VALUE_DETAIL_NODE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_DETAIL_NODE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/dll.h" +#include "yaml-cpp/node/ptr.h" +#include +#include +#include +#include +#include + +namespace YAML { +namespace detail { +struct iterator_type { + enum value { None, Sequence, Map }; +}; + +template +struct node_iterator_value : public std::pair { + typedef std::pair kv; + + node_iterator_value() : kv(), pNode(0) {} + explicit node_iterator_value(V& rhs) : kv(), pNode(&rhs) {} + explicit node_iterator_value(V& key, V& value) : kv(&key, &value), pNode(0) {} + + V& operator*() const { return *pNode; } + V& operator->() const { return *pNode; } + + V* pNode; +}; + +typedef std::vector node_seq; +typedef std::map node_map; + +template +struct node_iterator_type { + typedef node_seq::iterator seq; + typedef node_map::iterator map; +}; + +template +struct node_iterator_type { + typedef node_seq::const_iterator seq; + typedef node_map::const_iterator map; +}; + +template +class node_iterator_base + : public boost::iterator_facade< + node_iterator_base, node_iterator_value, + std::forward_iterator_tag, node_iterator_value > { + private: + struct enabler {}; + + public: + typedef typename node_iterator_type::seq SeqIter; + typedef typename node_iterator_type::map MapIter; + typedef node_iterator_value value_type; + + node_iterator_base() + : m_type(iterator_type::None), m_seqIt(), m_mapIt(), m_mapEnd() {} + explicit node_iterator_base(SeqIter seqIt) + : m_type(iterator_type::Sequence), + m_seqIt(seqIt), + m_mapIt(), + m_mapEnd() {} + explicit node_iterator_base(MapIter mapIt, MapIter mapEnd) + : m_type(iterator_type::Map), + m_seqIt(), + m_mapIt(mapIt), + m_mapEnd(mapEnd) { + m_mapIt = increment_until_defined(m_mapIt); + } + + template + node_iterator_base(const node_iterator_base& rhs, + typename boost::enable_if, + enabler>::type = enabler()) + : m_type(rhs.m_type), + m_seqIt(rhs.m_seqIt), + m_mapIt(rhs.m_mapIt), + m_mapEnd(rhs.m_mapEnd) {} + + private: + friend class boost::iterator_core_access; + template + friend class node_iterator_base; + + template + bool equal(const node_iterator_base& rhs) const { + if (m_type != rhs.m_type) + return false; + + switch (m_type) { + case iterator_type::None: + return true; + case iterator_type::Sequence: + return m_seqIt == rhs.m_seqIt; + case iterator_type::Map: + return m_mapIt == rhs.m_mapIt; + } + return true; + } + + void increment() { + switch (m_type) { + case iterator_type::None: + break; + case iterator_type::Sequence: + ++m_seqIt; + break; + case iterator_type::Map: + ++m_mapIt; + m_mapIt = increment_until_defined(m_mapIt); + break; + } + } + + value_type dereference() const { + switch (m_type) { + case iterator_type::None: + return value_type(); + case iterator_type::Sequence: + return value_type(**m_seqIt); + case iterator_type::Map: + return value_type(*m_mapIt->first, *m_mapIt->second); + } + return value_type(); + } + + MapIter increment_until_defined(MapIter it) { + while (it != m_mapEnd && !is_defined(it)) + ++it; + return it; + } + + bool is_defined(MapIter it) const { + return it->first->is_defined() && it->second->is_defined(); + } + + private: + typename iterator_type::value m_type; + + SeqIter m_seqIt; + MapIter m_mapIt, m_mapEnd; +}; + +typedef node_iterator_base node_iterator; +typedef node_iterator_base const_node_iterator; +} +} + +#endif // VALUE_DETAIL_NODE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/detail/node_ref.h b/include/yaml-cpp/node/detail/node_ref.h new file mode 100644 index 0000000..4530cf8 --- /dev/null +++ b/include/yaml-cpp/node/detail/node_ref.h @@ -0,0 +1,95 @@ +#ifndef VALUE_DETAIL_NODE_REF_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_DETAIL_NODE_REF_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/dll.h" +#include "yaml-cpp/node/type.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/detail/node_data.h" +#include + +namespace YAML { +namespace detail { +class node_ref : private boost::noncopyable { + public: + node_ref() : m_pData(new node_data) {} + + bool is_defined() const { return m_pData->is_defined(); } + NodeType::value type() const { return m_pData->type(); } + const std::string& scalar() const { return m_pData->scalar(); } + const std::string& tag() const { return m_pData->tag(); } + EmitterStyle::value style() const { return m_pData->style(); } + + void mark_defined() { m_pData->mark_defined(); } + void set_data(const node_ref& rhs) { m_pData = rhs.m_pData; } + + void set_type(NodeType::value type) { m_pData->set_type(type); } + void set_tag(const std::string& tag) { m_pData->set_tag(tag); } + void set_null() { m_pData->set_null(); } + void set_scalar(const std::string& scalar) { m_pData->set_scalar(scalar); } + void set_style(EmitterStyle::value style) { m_pData->set_style(style); } + + // size/iterator + std::size_t size() const { return m_pData->size(); } + + const_node_iterator begin() const { + return static_cast(*m_pData).begin(); + } + node_iterator begin() { return m_pData->begin(); } + + const_node_iterator end() const { + return static_cast(*m_pData).end(); + } + node_iterator end() { return m_pData->end(); } + + // sequence + void push_back(node& node, shared_memory_holder pMemory) { + m_pData->push_back(node, pMemory); + } + void insert(node& key, node& value, shared_memory_holder pMemory) { + m_pData->insert(key, value, pMemory); + } + + // indexing + template + node& get(const Key& key, shared_memory_holder pMemory) const { + return static_cast(*m_pData).get(key, pMemory); + } + template + node& get(const Key& key, shared_memory_holder pMemory) { + return m_pData->get(key, pMemory); + } + template + bool remove(const Key& key, shared_memory_holder pMemory) { + return m_pData->remove(key, pMemory); + } + + node& get(node& key, shared_memory_holder pMemory) const { + return static_cast(*m_pData).get(key, pMemory); + } + node& get(node& key, shared_memory_holder pMemory) { + return m_pData->get(key, pMemory); + } + bool remove(node& key, shared_memory_holder pMemory) { + return m_pData->remove(key, pMemory); + } + + // map + template + void force_insert(const Key& key, const Value& value, + shared_memory_holder pMemory) { + m_pData->force_insert(key, value, pMemory); + } + + private: + shared_node_data m_pData; +}; +} +} + +#endif // VALUE_DETAIL_NODE_REF_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/emit.h b/include/yaml-cpp/node/emit.h new file mode 100644 index 0000000..7c55af2 --- /dev/null +++ b/include/yaml-cpp/node/emit.h @@ -0,0 +1,25 @@ +#ifndef NODE_EMIT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_EMIT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include +#include + +#include "yaml-cpp/dll.h" + +namespace YAML { +class Emitter; +class Node; + +YAML_CPP_API Emitter& operator<<(Emitter& out, const Node& node); +YAML_CPP_API std::ostream& operator<<(std::ostream& out, const Node& node); + +YAML_CPP_API std::string Dump(const Node& node); +} + +#endif // NODE_EMIT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/impl.h b/include/yaml-cpp/node/impl.h new file mode 100644 index 0000000..3b1400a --- /dev/null +++ b/include/yaml-cpp/node/impl.h @@ -0,0 +1,434 @@ +#ifndef NODE_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/iterator.h" +#include "yaml-cpp/node/detail/memory.h" +#include "yaml-cpp/node/detail/node.h" +#include "yaml-cpp/exceptions.h" +#include + +namespace YAML { +inline Node::Node() : m_isValid(true), m_pNode(NULL) {} + +inline Node::Node(NodeType::value type) + : m_isValid(true), + m_pMemory(new detail::memory_holder), + m_pNode(&m_pMemory->create_node()) { + m_pNode->set_type(type); +} + +template +inline Node::Node(const T& rhs) + : m_isValid(true), + m_pMemory(new detail::memory_holder), + m_pNode(&m_pMemory->create_node()) { + Assign(rhs); +} + +inline Node::Node(const detail::iterator_value& rhs) + : m_isValid(rhs.m_isValid), + m_pMemory(rhs.m_pMemory), + m_pNode(rhs.m_pNode) {} + +inline Node::Node(const Node& rhs) + : m_isValid(rhs.m_isValid), + m_pMemory(rhs.m_pMemory), + m_pNode(rhs.m_pNode) {} + +inline Node::Node(Zombie) : m_isValid(false), m_pNode(NULL) {} + +inline Node::Node(detail::node& node, detail::shared_memory_holder pMemory) + : m_isValid(true), m_pMemory(pMemory), m_pNode(&node) {} + +inline Node::~Node() {} + +inline void Node::EnsureNodeExists() const { + if (!m_isValid) + throw InvalidNode(); + if (!m_pNode) { + m_pMemory.reset(new detail::memory_holder); + m_pNode = &m_pMemory->create_node(); + m_pNode->set_null(); + } +} + +inline bool Node::IsDefined() const { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? m_pNode->is_defined() : true; +} + +inline NodeType::value Node::Type() const { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? m_pNode->type() : NodeType::Null; +} + +// access + +// template helpers +template +struct as_if { + explicit as_if(const Node& node_) : node(node_) {} + const Node& node; + + const T operator()(const S& fallback) const { + if (!node.m_pNode) + return fallback; + + T t; + if (convert::decode(node, t)) + return t; + return fallback; + } +}; + +template +struct as_if { + explicit as_if(const Node& node_) : node(node_) {} + const Node& node; + + const std::string operator()(const S& fallback) const { + if (node.Type() != NodeType::Scalar) + return fallback; + return node.Scalar(); + } +}; + +template +struct as_if { + explicit as_if(const Node& node_) : node(node_) {} + const Node& node; + + const T operator()() const { + if (!node.m_pNode) + throw TypedBadConversion(); + + T t; + if (convert::decode(node, t)) + return t; + throw TypedBadConversion(); + } +}; + +template <> +struct as_if { + explicit as_if(const Node& node_) : node(node_) {} + const Node& node; + + const std::string operator()() const { + if (node.Type() != NodeType::Scalar) + throw TypedBadConversion(); + return node.Scalar(); + } +}; + +// access functions +template +inline const T Node::as() const { + if (!m_isValid) + throw InvalidNode(); + return as_if(*this)(); +} + +template +inline const T Node::as(const S& fallback) const { + if (!m_isValid) + throw InvalidNode(); + return as_if(*this)(fallback); +} + +inline const std::string& Node::Scalar() const { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? m_pNode->scalar() : detail::node_data::empty_scalar; +} + +inline const std::string& Node::Tag() const { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? m_pNode->tag() : detail::node_data::empty_scalar; +} + +inline void Node::SetTag(const std::string& tag) { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + m_pNode->set_tag(tag); +} + +inline EmitterStyle::value Node::Style() const { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? m_pNode->style() : EmitterStyle::Default; +} + +inline void Node::SetStyle(EmitterStyle::value style) { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + m_pNode->set_style(style); +} + +// assignment +inline bool Node::is(const Node& rhs) const { + if (!m_isValid || !rhs.m_isValid) + throw InvalidNode(); + if (!m_pNode || !rhs.m_pNode) + return false; + return m_pNode->is(*rhs.m_pNode); +} + +template +inline Node& Node::operator=(const T& rhs) { + if (!m_isValid) + throw InvalidNode(); + Assign(rhs); + return *this; +} + +inline void Node::reset(const YAML::Node& rhs) { + if (!m_isValid || !rhs.m_isValid) + throw InvalidNode(); + m_pMemory = rhs.m_pMemory; + m_pNode = rhs.m_pNode; +} + +template +inline void Node::Assign(const T& rhs) { + if (!m_isValid) + throw InvalidNode(); + AssignData(convert::encode(rhs)); +} + +template <> +inline void Node::Assign(const std::string& rhs) { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + m_pNode->set_scalar(rhs); +} + +inline void Node::Assign(const char* rhs) { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + m_pNode->set_scalar(rhs); +} + +inline void Node::Assign(char* rhs) { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + m_pNode->set_scalar(rhs); +} + +inline Node& Node::operator=(const Node& rhs) { + if (!m_isValid || !rhs.m_isValid) + throw InvalidNode(); + if (is(rhs)) + return *this; + AssignNode(rhs); + return *this; +} + +inline void Node::AssignData(const Node& rhs) { + if (!m_isValid || !rhs.m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + rhs.EnsureNodeExists(); + + m_pNode->set_data(*rhs.m_pNode); + m_pMemory->merge(*rhs.m_pMemory); +} + +inline void Node::AssignNode(const Node& rhs) { + if (!m_isValid || !rhs.m_isValid) + throw InvalidNode(); + rhs.EnsureNodeExists(); + + if (!m_pNode) { + m_pNode = rhs.m_pNode; + m_pMemory = rhs.m_pMemory; + return; + } + + m_pNode->set_ref(*rhs.m_pNode); + m_pMemory->merge(*rhs.m_pMemory); + m_pNode = rhs.m_pNode; +} + +// size/iterator +inline std::size_t Node::size() const { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? m_pNode->size() : 0; +} + +inline const_iterator Node::begin() const { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? const_iterator(m_pNode->begin(), m_pMemory) + : const_iterator(); +} + +inline iterator Node::begin() { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? iterator(m_pNode->begin(), m_pMemory) : iterator(); +} + +inline const_iterator Node::end() const { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? const_iterator(m_pNode->end(), m_pMemory) : const_iterator(); +} + +inline iterator Node::end() { + if (!m_isValid) + throw InvalidNode(); + return m_pNode ? iterator(m_pNode->end(), m_pMemory) : iterator(); +} + +// sequence +template +inline void Node::push_back(const T& rhs) { + if (!m_isValid) + throw InvalidNode(); + push_back(Node(rhs)); +} + +inline void Node::push_back(const Node& rhs) { + if (!m_isValid || !rhs.m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + rhs.EnsureNodeExists(); + + m_pNode->push_back(*rhs.m_pNode, m_pMemory); + m_pMemory->merge(*rhs.m_pMemory); +} + +// helpers for indexing +namespace detail { +template +struct to_value_t { + explicit to_value_t(const T& t_) : t(t_) {} + const T& t; + typedef const T& return_type; + + const T& operator()() const { return t; } +}; + +template <> +struct to_value_t { + explicit to_value_t(const char* t_) : t(t_) {} + const char* t; + typedef std::string return_type; + + const std::string operator()() const { return t; } +}; + +template <> +struct to_value_t { + explicit to_value_t(char* t_) : t(t_) {} + const char* t; + typedef std::string return_type; + + const std::string operator()() const { return t; } +}; + +template +struct to_value_t { + explicit to_value_t(const char* t_) : t(t_) {} + const char* t; + typedef std::string return_type; + + const std::string operator()() const { return t; } +}; + +// converts C-strings to std::strings so they can be copied +template +inline typename to_value_t::return_type to_value(const T& t) { + return to_value_t(t)(); +} +} + +// indexing +template +inline const Node Node::operator[](const Key& key) const { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + detail::node& value = static_cast(*m_pNode) + .get(detail::to_value(key), m_pMemory); + return Node(value, m_pMemory); +} + +template +inline Node Node::operator[](const Key& key) { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + detail::node& value = m_pNode->get(detail::to_value(key), m_pMemory); + return Node(value, m_pMemory); +} + +template +inline bool Node::remove(const Key& key) { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + return m_pNode->remove(detail::to_value(key), m_pMemory); +} + +inline const Node Node::operator[](const Node& key) const { + if (!m_isValid || !key.m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + key.EnsureNodeExists(); + m_pMemory->merge(*key.m_pMemory); + detail::node& value = + static_cast(*m_pNode).get(*key.m_pNode, m_pMemory); + return Node(value, m_pMemory); +} + +inline Node Node::operator[](const Node& key) { + if (!m_isValid || !key.m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + key.EnsureNodeExists(); + m_pMemory->merge(*key.m_pMemory); + detail::node& value = m_pNode->get(*key.m_pNode, m_pMemory); + return Node(value, m_pMemory); +} + +inline bool Node::remove(const Node& key) { + if (!m_isValid || !key.m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + key.EnsureNodeExists(); + return m_pNode->remove(*key.m_pNode, m_pMemory); +} + +// map +template +inline void Node::force_insert(const Key& key, const Value& value) { + if (!m_isValid) + throw InvalidNode(); + EnsureNodeExists(); + m_pNode->force_insert(detail::to_value(key), detail::to_value(value), + m_pMemory); +} + +// free functions +inline bool operator==(const Node& lhs, const Node& rhs) { return lhs.is(rhs); } +} + +#endif // NODE_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/iterator.h b/include/yaml-cpp/node/iterator.h new file mode 100644 index 0000000..366a9c8 --- /dev/null +++ b/include/yaml-cpp/node/iterator.h @@ -0,0 +1,31 @@ +#ifndef VALUE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/dll.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/detail/iterator_fwd.h" +#include "yaml-cpp/node/detail/iterator.h" +#include +#include +#include + +namespace YAML { +namespace detail { +struct iterator_value : public Node, std::pair { + iterator_value() {} + explicit iterator_value(const Node& rhs) + : Node(rhs), + std::pair(Node(Node::ZombieNode), Node(Node::ZombieNode)) {} + explicit iterator_value(const Node& key, const Node& value) + : Node(Node::ZombieNode), std::pair(key, value) {} +}; +} +} + +#endif // VALUE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/node.h b/include/yaml-cpp/node/node.h new file mode 100644 index 0000000..15de6f0 --- /dev/null +++ b/include/yaml-cpp/node/node.h @@ -0,0 +1,142 @@ +#ifndef NODE_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include + +#include "yaml-cpp/dll.h" +#include "yaml-cpp/emitterstyle.h" +#include "yaml-cpp/node/detail/bool_type.h" +#include "yaml-cpp/node/detail/iterator_fwd.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/type.h" + +namespace YAML { +namespace detail { +class node; +class node_data; +struct iterator_value; +} // namespace detail +} // namespace YAML + +namespace YAML { +class YAML_CPP_API Node { + public: + friend class NodeBuilder; + friend class NodeEvents; + friend struct detail::iterator_value; + friend class detail::node_data; + template + friend class detail::iterator_base; + template + friend struct as_if; + + typedef YAML::iterator iterator; + typedef YAML::const_iterator const_iterator; + + Node(); + explicit Node(NodeType::value type); + template + explicit Node(const T& rhs); + explicit Node(const detail::iterator_value& rhs); + Node(const Node& rhs); + ~Node(); + + NodeType::value Type() const; + bool IsDefined() const; + bool IsNull() const { return Type() == NodeType::Null; } + bool IsScalar() const { return Type() == NodeType::Scalar; } + bool IsSequence() const { return Type() == NodeType::Sequence; } + bool IsMap() const { return Type() == NodeType::Map; } + + // bool conversions + YAML_CPP_OPERATOR_BOOL(); + bool operator!() const { return !IsDefined(); } + + // access + template + const T as() const; + template + const T as(const S& fallback) const; + const std::string& Scalar() const; + + const std::string& Tag() const; + void SetTag(const std::string& tag); + + // style + // WARNING: This API might change in future releases. + EmitterStyle::value Style() const; + void SetStyle(EmitterStyle::value style); + + // assignment + bool is(const Node& rhs) const; + template + Node& operator=(const T& rhs); + Node& operator=(const Node& rhs); + void reset(const Node& rhs = Node()); + + // size/iterator + std::size_t size() const; + + const_iterator begin() const; + iterator begin(); + + const_iterator end() const; + iterator end(); + + // sequence + template + void push_back(const T& rhs); + void push_back(const Node& rhs); + + // indexing + template + const Node operator[](const Key& key) const; + template + Node operator[](const Key& key); + template + bool remove(const Key& key); + + const Node operator[](const Node& key) const; + Node operator[](const Node& key); + bool remove(const Node& key); + + // map + template + void force_insert(const Key& key, const Value& value); + + private: + enum Zombie { ZombieNode }; + explicit Node(Zombie); + explicit Node(detail::node& node, detail::shared_memory_holder pMemory); + + void EnsureNodeExists() const; + + template + void Assign(const T& rhs); + void Assign(const char* rhs); + void Assign(char* rhs); + + void AssignData(const Node& rhs); + void AssignNode(const Node& rhs); + + private: + bool m_isValid; + mutable detail::shared_memory_holder m_pMemory; + mutable detail::node* m_pNode; +}; + +YAML_CPP_API bool operator==(const Node& lhs, const Node& rhs); + +YAML_CPP_API Node Clone(const Node& node); + +template +struct convert; +} + +#endif // NODE_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/parse.h b/include/yaml-cpp/node/parse.h new file mode 100644 index 0000000..0ea4948 --- /dev/null +++ b/include/yaml-cpp/node/parse.h @@ -0,0 +1,30 @@ +#ifndef VALUE_PARSE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_PARSE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include +#include +#include + +#include "yaml-cpp/dll.h" + +namespace YAML { +class Node; + +YAML_CPP_API Node Load(const std::string& input); +YAML_CPP_API Node Load(const char* input); +YAML_CPP_API Node Load(std::istream& input); +YAML_CPP_API Node LoadFile(const std::string& filename); + +YAML_CPP_API std::vector LoadAll(const std::string& input); +YAML_CPP_API std::vector LoadAll(const char* input); +YAML_CPP_API std::vector LoadAll(std::istream& input); +YAML_CPP_API std::vector LoadAllFromFile(const std::string& filename); +} + +#endif // VALUE_PARSE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/ptr.h b/include/yaml-cpp/node/ptr.h new file mode 100644 index 0000000..64c8689 --- /dev/null +++ b/include/yaml-cpp/node/ptr.h @@ -0,0 +1,29 @@ +#ifndef VALUE_PTR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_PTR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "yaml-cpp/dll.h" +#include + +namespace YAML { +namespace detail { +class node; +class node_ref; +class node_data; +class memory; +class memory_holder; + +typedef boost::shared_ptr shared_node; +typedef boost::shared_ptr shared_node_ref; +typedef boost::shared_ptr shared_node_data; +typedef boost::shared_ptr shared_memory_holder; +typedef boost::shared_ptr shared_memory; +} +} + +#endif // VALUE_PTR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/type.h b/include/yaml-cpp/node/type.h new file mode 100644 index 0000000..9d55ca9 --- /dev/null +++ b/include/yaml-cpp/node/type.h @@ -0,0 +1,16 @@ +#ifndef VALUE_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define VALUE_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +namespace YAML { +struct NodeType { + enum value { Undefined, Null, Scalar, Sequence, Map }; +}; +} + +#endif // VALUE_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/parser.h b/include/yaml-cpp/parser.h index 7e065b2..24880e4 100644 --- a/include/yaml-cpp/parser.h +++ b/include/yaml-cpp/parser.h @@ -18,7 +18,6 @@ class EventHandler; class Node; class Scanner; struct Directives; -struct Mark; struct Token; class YAML_CPP_API Parser : private noncopyable { diff --git a/include/yaml-cpp/yaml.h b/include/yaml-cpp/yaml.h index a0d082b..7f515ef 100644 --- a/include/yaml-cpp/yaml.h +++ b/include/yaml-cpp/yaml.h @@ -13,4 +13,12 @@ #include "yaml-cpp/stlemitter.h" #include "yaml-cpp/exceptions.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/parse.h" +#include "yaml-cpp/node/emit.h" + #endif // YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/convert.cpp b/src/convert.cpp new file mode 100644 index 0000000..da3bc2f --- /dev/null +++ b/src/convert.cpp @@ -0,0 +1,75 @@ +#include + +#include "yaml-cpp/node/convert.h" + +namespace { +// we're not gonna mess with the mess that is all the isupper/etc. functions +bool IsLower(char ch) { return 'a' <= ch && ch <= 'z'; } +bool IsUpper(char ch) { return 'A' <= ch && ch <= 'Z'; } +char ToLower(char ch) { return IsUpper(ch) ? ch + 'a' - 'A' : ch; } + +std::string tolower(const std::string& str) { + std::string s(str); + std::transform(s.begin(), s.end(), s.begin(), ToLower); + return s; +} + +template +bool IsEntirely(const std::string& str, T func) { + for (std::size_t i = 0; i < str.size(); i++) + if (!func(str[i])) + return false; + + return true; +} + +// IsFlexibleCase +// . Returns true if 'str' is: +// . UPPERCASE +// . lowercase +// . Capitalized +bool IsFlexibleCase(const std::string& str) { + if (str.empty()) + return true; + + if (IsEntirely(str, IsLower)) + return true; + + bool firstcaps = IsUpper(str[0]); + std::string rest = str.substr(1); + return firstcaps && (IsEntirely(rest, IsLower) || IsEntirely(rest, IsUpper)); +} +} + +namespace YAML { +bool convert::decode(const Node& node, bool& rhs) { + if (!node.IsScalar()) + return false; + + // we can't use iostream bool extraction operators as they don't + // recognize all possible values in the table below (taken from + // http://yaml.org/type/bool.html) + static const struct { + std::string truename, falsename; + } names[] = { + {"y", "n"}, {"yes", "no"}, {"true", "false"}, {"on", "off"}, + }; + + if (!IsFlexibleCase(node.Scalar())) + return false; + + for (unsigned i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + if (names[i].truename == tolower(node.Scalar())) { + rhs = true; + return true; + } + + if (names[i].falsename == tolower(node.Scalar())) { + rhs = false; + return true; + } + } + + return false; +} +} diff --git a/src/emit.cpp b/src/emit.cpp new file mode 100644 index 0000000..5fb593b --- /dev/null +++ b/src/emit.cpp @@ -0,0 +1,25 @@ +#include "yaml-cpp/node/emit.h" +#include "yaml-cpp/emitfromevents.h" +#include "yaml-cpp/emitter.h" +#include "nodeevents.h" + +namespace YAML { +Emitter& operator<<(Emitter& out, const Node& node) { + EmitFromEvents emitFromEvents(out); + NodeEvents events(node); + events.Emit(emitFromEvents); + return out; +} + +std::ostream& operator<<(std::ostream& out, const Node& node) { + Emitter emitter(out); + emitter << node; + return out; +} + +std::string Dump(const Node& node) { + Emitter emitter; + emitter << node; + return emitter.c_str(); +} +} diff --git a/src/memory.cpp b/src/memory.cpp new file mode 100644 index 0000000..e5f8a9d --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,26 @@ +#include "yaml-cpp/node/detail/memory.h" +#include "yaml-cpp/node/detail/node.h" // IWYU pragma: keep +#include "yaml-cpp/node/ptr.h" + +namespace YAML { +namespace detail { + +void memory_holder::merge(memory_holder& rhs) { + if (m_pMemory == rhs.m_pMemory) + return; + + m_pMemory->merge(*rhs.m_pMemory); + rhs.m_pMemory = m_pMemory; +} + +node& memory::create_node() { + shared_node pNode(new node); + m_nodes.insert(pNode); + return *pNode; +} + +void memory::merge(const memory& rhs) { + m_nodes.insert(rhs.m_nodes.begin(), rhs.m_nodes.end()); +} +} +} diff --git a/src/node.cpp b/src/node.cpp new file mode 100644 index 0000000..2088e13 --- /dev/null +++ b/src/node.cpp @@ -0,0 +1,12 @@ +#include "yaml-cpp/node/node.h" +#include "nodebuilder.h" +#include "nodeevents.h" + +namespace YAML { +Node Clone(const Node& node) { + NodeEvents events(node); + NodeBuilder builder; + events.Emit(builder); + return builder.Root(); +} +} diff --git a/src/node_data.cpp b/src/node_data.cpp new file mode 100644 index 0000000..379c1ef --- /dev/null +++ b/src/node_data.cpp @@ -0,0 +1,295 @@ +#include +#include +#include + +#include "yaml-cpp/exceptions.h" +#include "yaml-cpp/node/detail/memory.h" +#include "yaml-cpp/node/detail/node.h" // IWYU pragma: keep +#include "yaml-cpp/node/detail/node_data.h" +#include "yaml-cpp/node/detail/node_iterator.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/type.h" + +namespace YAML { +namespace detail { + +std::string node_data::empty_scalar; + +node_data::node_data() + : m_isDefined(false), + m_type(NodeType::Null), + m_style(EmitterStyle::Default), + m_seqSize(0) {} + +void node_data::mark_defined() { + if (m_type == NodeType::Undefined) + m_type = NodeType::Null; + m_isDefined = true; +} + +void node_data::set_type(NodeType::value type) { + if (type == NodeType::Undefined) { + m_type = type; + m_isDefined = false; + return; + } + + m_isDefined = true; + if (type == m_type) + return; + + m_type = type; + + switch (m_type) { + case NodeType::Null: + break; + case NodeType::Scalar: + m_scalar.clear(); + break; + case NodeType::Sequence: + reset_sequence(); + break; + case NodeType::Map: + reset_map(); + break; + case NodeType::Undefined: + assert(false); + break; + } +} + +void node_data::set_tag(const std::string& tag) { m_tag = tag; } + +void node_data::set_style(EmitterStyle::value style) { m_style = style; } + +void node_data::set_null() { + m_isDefined = true; + m_type = NodeType::Null; +} + +void node_data::set_scalar(const std::string& scalar) { + m_isDefined = true; + m_type = NodeType::Scalar; + m_scalar = scalar; +} + +// size/iterator +std::size_t node_data::size() const { + if (!m_isDefined) + return 0; + + switch (m_type) { + case NodeType::Sequence: + compute_seq_size(); + return m_seqSize; + case NodeType::Map: + compute_map_size(); + return m_map.size() - m_undefinedPairs.size(); + default: + return 0; + } + return 0; +} + +void node_data::compute_seq_size() const { + while (m_seqSize < m_sequence.size() && m_sequence[m_seqSize]->is_defined()) + m_seqSize++; +} + +void node_data::compute_map_size() const { + kv_pairs::iterator it = m_undefinedPairs.begin(); + while (it != m_undefinedPairs.end()) { + kv_pairs::iterator jt = boost::next(it); + if (it->first->is_defined() && it->second->is_defined()) + m_undefinedPairs.erase(it); + it = jt; + } +} + +const_node_iterator node_data::begin() const { + if (!m_isDefined) + return const_node_iterator(); + + switch (m_type) { + case NodeType::Sequence: + return const_node_iterator(m_sequence.begin()); + case NodeType::Map: + return const_node_iterator(m_map.begin(), m_map.end()); + default: + return const_node_iterator(); + } +} + +node_iterator node_data::begin() { + if (!m_isDefined) + return node_iterator(); + + switch (m_type) { + case NodeType::Sequence: + return node_iterator(m_sequence.begin()); + case NodeType::Map: + return node_iterator(m_map.begin(), m_map.end()); + default: + return node_iterator(); + } +} + +const_node_iterator node_data::end() const { + if (!m_isDefined) + return const_node_iterator(); + + switch (m_type) { + case NodeType::Sequence: + return const_node_iterator(m_sequence.end()); + case NodeType::Map: + return const_node_iterator(m_map.end(), m_map.end()); + default: + return const_node_iterator(); + } +} + +node_iterator node_data::end() { + if (!m_isDefined) + return node_iterator(); + + switch (m_type) { + case NodeType::Sequence: + return node_iterator(m_sequence.end()); + case NodeType::Map: + return node_iterator(m_map.end(), m_map.end()); + default: + return node_iterator(); + } +} + +// sequence +void node_data::push_back(node& node, shared_memory_holder /* pMemory */) { + if (m_type == NodeType::Undefined || m_type == NodeType::Null) { + m_type = NodeType::Sequence; + reset_sequence(); + } + + if (m_type != NodeType::Sequence) + throw BadPushback(); + + m_sequence.push_back(&node); +} + +void node_data::insert(node& key, node& value, shared_memory_holder pMemory) { + switch (m_type) { + case NodeType::Map: + break; + case NodeType::Undefined: + case NodeType::Null: + case NodeType::Sequence: + convert_to_map(pMemory); + break; + case NodeType::Scalar: + throw BadSubscript(); + } + + insert_map_pair(key, value); +} + +// indexing +node& node_data::get(node& key, shared_memory_holder pMemory) const { + if (m_type != NodeType::Map) + return pMemory->create_node(); + + for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) { + if (it->first->is(key)) + return *it->second; + } + + return pMemory->create_node(); +} + +node& node_data::get(node& key, shared_memory_holder pMemory) { + switch (m_type) { + case NodeType::Map: + break; + case NodeType::Undefined: + case NodeType::Null: + case NodeType::Sequence: + convert_to_map(pMemory); + break; + case NodeType::Scalar: + throw BadSubscript(); + } + + for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) { + if (it->first->is(key)) + return *it->second; + } + + node& value = pMemory->create_node(); + insert_map_pair(key, value); + return value; +} + +bool node_data::remove(node& key, shared_memory_holder /* pMemory */) { + if (m_type != NodeType::Map) + return false; + + for (node_map::iterator it = m_map.begin(); it != m_map.end(); ++it) { + if (it->first->is(key)) { + m_map.erase(it); + return true; + } + } + + return false; +} + +void node_data::reset_sequence() { + m_sequence.clear(); + m_seqSize = 0; +} + +void node_data::reset_map() { + m_map.clear(); + m_undefinedPairs.clear(); +} + +void node_data::insert_map_pair(node& key, node& value) { + m_map[&key] = &value; + if (!key.is_defined() || !value.is_defined()) + m_undefinedPairs.push_back(kv_pair(&key, &value)); +} + +void node_data::convert_to_map(shared_memory_holder pMemory) { + switch (m_type) { + case NodeType::Undefined: + case NodeType::Null: + reset_map(); + m_type = NodeType::Map; + break; + case NodeType::Sequence: + convert_sequence_to_map(pMemory); + break; + case NodeType::Map: + break; + case NodeType::Scalar: + assert(false); + break; + } +} + +void node_data::convert_sequence_to_map(shared_memory_holder pMemory) { + assert(m_type == NodeType::Sequence); + + reset_map(); + for (std::size_t i = 0; i < m_sequence.size(); i++) { + std::stringstream stream; + stream << i; + + node& key = pMemory->create_node(); + key.set_scalar(stream.str()); + insert_map_pair(key, *m_sequence[i]); + } + + reset_sequence(); + m_type = NodeType::Map; +} +} +} diff --git a/src/nodebuilder.cpp b/src/nodebuilder.cpp new file mode 100644 index 0000000..69f9702 --- /dev/null +++ b/src/nodebuilder.cpp @@ -0,0 +1,130 @@ +#include +#include + +#include "nodebuilder.h" +#include "yaml-cpp/node/detail/node.h" +#include "yaml-cpp/node/impl.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/type.h" + +namespace YAML { +struct Mark; + +NodeBuilder::NodeBuilder() + : m_pMemory(new detail::memory_holder), m_pRoot(0), m_mapDepth(0) { + m_anchors.push_back(0); // since the anchors start at 1 +} + +NodeBuilder::~NodeBuilder() {} + +Node NodeBuilder::Root() { + if (!m_pRoot) + return Node(); + + return Node(*m_pRoot, m_pMemory); +} + +void NodeBuilder::OnDocumentStart(const Mark&) {} + +void NodeBuilder::OnDocumentEnd() {} + +void NodeBuilder::OnNull(const Mark& /* mark */, anchor_t anchor) { + detail::node& node = Push(anchor); + node.set_null(); + Pop(); +} + +void NodeBuilder::OnAlias(const Mark& /* mark */, anchor_t anchor) { + detail::node& node = *m_anchors[anchor]; + Push(node); + Pop(); +} + +void NodeBuilder::OnScalar(const Mark& /* mark */, const std::string& tag, + anchor_t anchor, const std::string& value) { + detail::node& node = Push(anchor); + node.set_scalar(value); + node.set_tag(tag); + Pop(); +} + +void NodeBuilder::OnSequenceStart(const Mark& /* mark */, + const std::string& tag, anchor_t anchor, + EmitterStyle::value style) { + detail::node& node = Push(anchor); + node.set_tag(tag); + node.set_type(NodeType::Sequence); + node.set_style(style); +} + +void NodeBuilder::OnSequenceEnd() { Pop(); } + +void NodeBuilder::OnMapStart(const Mark& /* mark */, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) { + detail::node& node = Push(anchor); + node.set_type(NodeType::Map); + node.set_tag(tag); + node.set_style(style); + m_mapDepth++; +} + +void NodeBuilder::OnMapEnd() { + assert(m_mapDepth > 0); + m_mapDepth--; + Pop(); +} + +detail::node& NodeBuilder::Push(anchor_t anchor) { + detail::node& node = m_pMemory->create_node(); + RegisterAnchor(anchor, node); + Push(node); + return node; +} + +void NodeBuilder::Push(detail::node& node) { + const bool needsKey = + (!m_stack.empty() && m_stack.back()->type() == NodeType::Map && + m_keys.size() < m_mapDepth); + + m_stack.push_back(&node); + if (needsKey) + m_keys.push_back(PushedKey(&node, false)); +} + +void NodeBuilder::Pop() { + assert(!m_stack.empty()); + if (m_stack.size() == 1) { + m_pRoot = m_stack[0]; + m_stack.pop_back(); + return; + } + + detail::node& node = *m_stack.back(); + m_stack.pop_back(); + + detail::node& collection = *m_stack.back(); + + if (collection.type() == NodeType::Sequence) { + collection.push_back(node, m_pMemory); + } else if (collection.type() == NodeType::Map) { + assert(!m_keys.empty()); + PushedKey& key = m_keys.back(); + if (key.second) { + collection.insert(*key.first, node, m_pMemory); + m_keys.pop_back(); + } else { + key.second = true; + } + } else { + assert(false); + m_stack.clear(); + } +} + +void NodeBuilder::RegisterAnchor(anchor_t anchor, detail::node& node) { + if (anchor) { + assert(anchor == m_anchors.size()); + m_anchors.push_back(&node); + } +} +} diff --git a/src/nodebuilder.h b/src/nodebuilder.h new file mode 100644 index 0000000..79b3201 --- /dev/null +++ b/src/nodebuilder.h @@ -0,0 +1,70 @@ +#ifndef NODE_NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include + +#include "yaml-cpp/anchor.h" +#include "yaml-cpp/emitterstyle.h" +#include "yaml-cpp/eventhandler.h" +#include "yaml-cpp/node/ptr.h" + +namespace YAML { +namespace detail { +class node; +} // namespace detail +struct Mark; +} // namespace YAML + +namespace YAML { +class Node; + +class NodeBuilder : public EventHandler { + public: + NodeBuilder(); + virtual ~NodeBuilder(); + + Node Root(); + + virtual void OnDocumentStart(const Mark& mark); + virtual void OnDocumentEnd(); + + virtual void OnNull(const Mark& mark, anchor_t anchor); + virtual void OnAlias(const Mark& mark, anchor_t anchor); + virtual void OnScalar(const Mark& mark, const std::string& tag, + anchor_t anchor, const std::string& value); + + virtual void OnSequenceStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style); + virtual void OnSequenceEnd(); + + virtual void OnMapStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style); + virtual void OnMapEnd(); + + private: + detail::node& Push(anchor_t anchor); + void Push(detail::node& node); + void Pop(); + void RegisterAnchor(anchor_t anchor, detail::node& node); + + private: + detail::shared_memory_holder m_pMemory; + detail::node* m_pRoot; + + typedef std::vector Nodes; + Nodes m_stack; + Nodes m_anchors; + + typedef std::pair PushedKey; + std::vector m_keys; + std::size_t m_mapDepth; +}; +} + +#endif // NODE_NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/nodeevents.cpp b/src/nodeevents.cpp new file mode 100644 index 0000000..82261fe --- /dev/null +++ b/src/nodeevents.cpp @@ -0,0 +1,101 @@ +#include "nodeevents.h" +#include "yaml-cpp/eventhandler.h" +#include "yaml-cpp/mark.h" +#include "yaml-cpp/node/detail/node.h" +#include "yaml-cpp/node/detail/node_iterator.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/type.h" + +namespace YAML { +void NodeEvents::AliasManager::RegisterReference(const detail::node& node) { + m_anchorByIdentity.insert(std::make_pair(node.ref(), _CreateNewAnchor())); +} + +anchor_t NodeEvents::AliasManager::LookupAnchor( + const detail::node& node) const { + AnchorByIdentity::const_iterator it = m_anchorByIdentity.find(node.ref()); + if (it == m_anchorByIdentity.end()) + return 0; + return it->second; +} + +NodeEvents::NodeEvents(const Node& node) + : m_pMemory(node.m_pMemory), m_root(node.m_pNode) { + if (m_root) + Setup(*m_root); +} + +void NodeEvents::Setup(const detail::node& node) { + int& refCount = m_refCount[node.ref()]; + refCount++; + if (refCount > 1) + return; + + if (node.type() == NodeType::Sequence) { + for (detail::const_node_iterator it = node.begin(); it != node.end(); ++it) + Setup(**it); + } else if (node.type() == NodeType::Map) { + for (detail::const_node_iterator it = node.begin(); it != node.end(); + ++it) { + Setup(*it->first); + Setup(*it->second); + } + } +} + +void NodeEvents::Emit(EventHandler& handler) { + AliasManager am; + + handler.OnDocumentStart(Mark()); + if (m_root) + Emit(*m_root, handler, am); + handler.OnDocumentEnd(); +} + +void NodeEvents::Emit(const detail::node& node, EventHandler& handler, + AliasManager& am) const { + anchor_t anchor = NullAnchor; + if (IsAliased(node)) { + anchor = am.LookupAnchor(node); + if (anchor) { + handler.OnAlias(Mark(), anchor); + return; + } + + am.RegisterReference(node); + anchor = am.LookupAnchor(node); + } + + switch (node.type()) { + case NodeType::Undefined: + break; + case NodeType::Null: + handler.OnNull(Mark(), anchor); + break; + case NodeType::Scalar: + handler.OnScalar(Mark(), node.tag(), anchor, node.scalar()); + break; + case NodeType::Sequence: + handler.OnSequenceStart(Mark(), node.tag(), anchor, node.style()); + for (detail::const_node_iterator it = node.begin(); it != node.end(); + ++it) + Emit(**it, handler, am); + handler.OnSequenceEnd(); + break; + case NodeType::Map: + handler.OnMapStart(Mark(), node.tag(), anchor, node.style()); + for (detail::const_node_iterator it = node.begin(); it != node.end(); + ++it) { + Emit(*it->first, handler, am); + Emit(*it->second, handler, am); + } + handler.OnMapEnd(); + break; + } +} + +bool NodeEvents::IsAliased(const detail::node& node) const { + RefCount::const_iterator it = m_refCount.find(node.ref()); + return it != m_refCount.end() && it->second > 1; +} +} diff --git a/src/nodeevents.h b/src/nodeevents.h new file mode 100644 index 0000000..49c18eb --- /dev/null +++ b/src/nodeevents.h @@ -0,0 +1,64 @@ +#ifndef NODE_NODEEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_NODEEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include +#include + +#include "yaml-cpp/anchor.h" +#include "yaml-cpp/node/ptr.h" + +namespace YAML { +namespace detail { +class node; +} // namespace detail +} // namespace YAML + +namespace YAML { +class EventHandler; +class Node; + +class NodeEvents { + public: + explicit NodeEvents(const Node& node); + + void Emit(EventHandler& handler); + + private: + class AliasManager { + public: + AliasManager() : m_curAnchor(0) {} + + void RegisterReference(const detail::node& node); + anchor_t LookupAnchor(const detail::node& node) const; + + private: + anchor_t _CreateNewAnchor() { return ++m_curAnchor; } + + private: + typedef std::map AnchorByIdentity; + AnchorByIdentity m_anchorByIdentity; + + anchor_t m_curAnchor; + }; + + void Setup(const detail::node& node); + void Emit(const detail::node& node, EventHandler& handler, + AliasManager& am) const; + bool IsAliased(const detail::node& node) const; + + private: + detail::shared_memory_holder m_pMemory; + detail::node* m_root; + + typedef std::map RefCount; + RefCount m_refCount; +}; +} + +#endif // NODE_NODEEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/parse.cpp b/src/parse.cpp new file mode 100644 index 0000000..1ef474d --- /dev/null +++ b/src/parse.cpp @@ -0,0 +1,68 @@ +#include "yaml-cpp/node/parse.h" + +#include +#include + +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/impl.h" +#include "yaml-cpp/parser.h" +#include "nodebuilder.h" + +namespace YAML { +Node Load(const std::string& input) { + std::stringstream stream(input); + return Load(stream); +} + +Node Load(const char* input) { + std::stringstream stream(input); + return Load(stream); +} + +Node Load(std::istream& input) { + Parser parser(input); + NodeBuilder builder; + if (!parser.HandleNextDocument(builder)) + return Node(); + + return builder.Root(); +} + +Node LoadFile(const std::string& filename) { + std::ifstream fin(filename.c_str()); + if (!fin) + throw BadFile(); + return Load(fin); +} + +std::vector LoadAll(const std::string& input) { + std::stringstream stream(input); + return LoadAll(stream); +} + +std::vector LoadAll(const char* input) { + std::stringstream stream(input); + return LoadAll(stream); +} + +std::vector LoadAll(std::istream& input) { + std::vector docs; + + Parser parser(input); + while (1) { + NodeBuilder builder; + if (!parser.HandleNextDocument(builder)) + break; + docs.push_back(builder.Root()); + } + + return docs; +} + +std::vector LoadAllFromFile(const std::string& filename) { + std::ifstream fin(filename.c_str()); + if (!fin) + throw BadFile(); + return LoadAll(fin); +} +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7f3bbe2..02b5c8f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,10 +14,10 @@ if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR endif() file(GLOB test_headers [a-z_]*.h) -file(GLOB test_sources [a-z_]*.cpp integration/[a-z_]*.cpp) -file(GLOB test_core_sources core/[a-z]*.cpp) +file(GLOB test_sources [a-z_]*.cpp integration/[a-z_]*.cpp node/[a-z_]*.cpp) +file(GLOB test_new_api_sources new-api/[a-z]*.cpp) -list(APPEND test_sources ${test_core_sources}) +list(APPEND test_sources ${test_new_api_sources}) add_sources(${test_sources} ${test_headers}) include_directories(${YAML_CPP_SOURCE_DIR}/test) diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp new file mode 100644 index 0000000..0083a2b --- /dev/null +++ b/test/integration/load_node_test.cpp @@ -0,0 +1,197 @@ +#include "yaml-cpp/yaml.h" // IWYU pragma: keep + +#include "gtest/gtest.h" + +namespace YAML { +namespace { +TEST(LoadNodeTest, Reassign) { + Node node = Load("foo"); + node = Node(); +} + +TEST(LoadNodeTest, FallbackValues) { + Node node = Load("foo: bar\nx: 2"); + EXPECT_EQ("bar", node["foo"].as()); + EXPECT_EQ("bar", node["foo"].as("hello")); + EXPECT_EQ("hello", node["baz"].as("hello")); + EXPECT_EQ(2, node["x"].as()); + EXPECT_EQ(2, node["x"].as(5)); + EXPECT_EQ(5, node["y"].as(5)); +} + +TEST(LoadNodeTest, NumericConversion) { + Node node = Load("[1.5, 1, .nan, .inf, -.inf, 0x15, 015]"); + EXPECT_EQ(1.5f, node[0].as()); + EXPECT_EQ(1.5, node[0].as()); + EXPECT_THROW(node[0].as(), TypedBadConversion); + EXPECT_EQ(1, node[1].as()); + EXPECT_EQ(1.0f, node[1].as()); + EXPECT_NE(node[2].as(), node[2].as()); + EXPECT_EQ(std::numeric_limits::infinity(), node[3].as()); + EXPECT_EQ(-std::numeric_limits::infinity(), node[4].as()); + EXPECT_EQ(21, node[5].as()); + EXPECT_EQ(13, node[6].as()); +} + +TEST(LoadNodeTest, Binary) { + Node node = Load( + "[!!binary \"SGVsbG8sIFdvcmxkIQ==\", !!binary " + "\"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieS" + "B0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG" + "x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbi" + "B0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZG" + "dlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS" + "4K\"]"); + EXPECT_EQ(Binary(reinterpret_cast("Hello, World!"), 13), + node[0].as()); + EXPECT_EQ(Binary(reinterpret_cast( + "Man is distinguished, not only by his reason, " + "but by this singular passion from other " + "animals, which is a lust of the mind, that by " + "a perseverance of delight in the continued and " + "indefatigable generation of knowledge, exceeds " + "the short vehemence of any carnal pleasure.\n"), + 270), + node[1].as()); +} + +TEST(LoadNodeTest, IterateSequence) { + Node node = Load("[1, 3, 5, 7]"); + int seq[] = {1, 3, 5, 7}; + int i = 0; + for (const_iterator it = node.begin(); it != node.end(); ++it) { + EXPECT_TRUE(i < 4); + int x = seq[i++]; + EXPECT_EQ(x, it->as()); + } + EXPECT_EQ(4, i); +} + +TEST(LoadNodeTest, IterateMap) { + Node node = Load("{a: A, b: B, c: C}"); + int i = 0; + for (const_iterator it = node.begin(); it != node.end(); ++it) { + EXPECT_TRUE(i < 3); + i++; + EXPECT_EQ(it->second.as(), it->first.as() + 'A' - 'a'); + } + EXPECT_EQ(3, i); +} + +#ifdef BOOST_FOREACH +TEST(LoadNodeTest, ForEach) { + Node node = Load("[1, 3, 5, 7]"); + int seq[] = {1, 3, 5, 7}; + int i = 0; + BOOST_FOREACH (const Node& item, node) { + int x = seq[i++]; + EXPECT_EQ(x, item.as()); + } +} + +TEST(LoadNodeTest, ForEachMap) { + Node node = Load("{a: A, b: B, c: C}"); + BOOST_FOREACH (const const_iterator::value_type& p, node) { + EXPECT_EQ(p.second.as(), p.first.as() + 'A' - 'a'); + } +} +#endif + +TEST(LoadNodeTest, CloneScalar) { + Node node = Load("!foo monkey"); + Node clone = Clone(node); + EXPECT_FALSE(clone == node); + EXPECT_EQ(clone.as(), node.as()); + EXPECT_EQ(clone.Tag(), node.Tag()); +} + +TEST(LoadNodeTest, CloneSeq) { + Node node = Load("[1, 3, 5, 7]"); + Node clone = Clone(node); + EXPECT_FALSE(clone == node); + EXPECT_EQ(NodeType::Sequence, clone.Type()); + EXPECT_EQ(clone.size(), node.size()); + for (std::size_t i = 0; i < node.size(); i++) { + EXPECT_EQ(clone[i].as(), node[i].as()); + } +} + +TEST(LoadNodeTest, CloneMap) { + Node node = Load("{foo: bar}"); + Node clone = Clone(node); + EXPECT_FALSE(clone == node); + EXPECT_EQ(NodeType::Map, clone.Type()); + EXPECT_EQ(clone.size(), node.size()); + EXPECT_EQ(clone["foo"].as(), node["foo"].as()); +} + +TEST(LoadNodeTest, CloneAlias) { + Node node = Load("&foo [*foo]"); + Node clone = Clone(node); + EXPECT_FALSE(clone == node); + EXPECT_EQ(NodeType::Sequence, clone.Type()); + EXPECT_EQ(clone.size(), node.size()); + EXPECT_EQ(clone[0], clone); +} + +TEST(LoadNodeTest, ForceInsertIntoMap) { + Node node; + node["a"] = "b"; + node.force_insert("x", "y"); + node.force_insert("a", 5); + EXPECT_EQ(3, node.size()); + EXPECT_EQ(NodeType::Map, node.Type()); + bool ab = false; + bool a5 = false; + bool xy = false; + for (const_iterator it = node.begin(); it != node.end(); ++it) { + if (it->first.as() == "a") { + if (it->second.as() == "b") + ab = true; + else if (it->second.as() == "5") + a5 = true; + } else if (it->first.as() == "x" && + it->second.as() == "y") + xy = true; + } + EXPECT_TRUE(ab); + EXPECT_TRUE(a5); + EXPECT_TRUE(xy); +} + +TEST(LoadNodeTest, ResetNode) { + Node node = Load("[1, 2, 3]"); + EXPECT_TRUE(!node.IsNull()); + Node other = node; + node.reset(); + EXPECT_TRUE(node.IsNull()); + EXPECT_TRUE(!other.IsNull()); + node.reset(other); + EXPECT_TRUE(!node.IsNull()); + EXPECT_EQ(node, other); +} + +TEST(LoadNodeTest, DereferenceIteratorError) { + Node node = Load("[{a: b}, 1, 2]"); + EXPECT_THROW(node.begin()->first.as(), InvalidNode); + EXPECT_EQ(true, (*node.begin()).IsMap()); + EXPECT_EQ(true, node.begin()->IsMap()); + EXPECT_THROW((*node.begin()->begin()).IsDefined(), InvalidNode); + EXPECT_THROW(node.begin()->begin()->IsDefined(), InvalidNode); +} + +TEST(NodeTest, EmitEmptyNode) { + Node node; + Emitter emitter; + emitter << node; + EXPECT_EQ("", std::string(emitter.c_str())); +} + +TEST(NodeTest, ParseNodeStyle) { + EXPECT_EQ(EmitterStyle::Flow, Load("[1, 2, 3]").Style()); + EXPECT_EQ(EmitterStyle::Flow, Load("{foo: bar}").Style()); + EXPECT_EQ(EmitterStyle::Block, Load("- foo\n- bar").Style()); + EXPECT_EQ(EmitterStyle::Block, Load("foo: bar").Style()); +} +} +} diff --git a/test/integration/node_spec_test.cpp b/test/integration/node_spec_test.cpp new file mode 100644 index 0000000..aedf38b --- /dev/null +++ b/test/integration/node_spec_test.cpp @@ -0,0 +1,1131 @@ +#include "specexamples.h" +#include "yaml-cpp/yaml.h" // IWYU pragma: keep + +#include "gtest/gtest.h" + +#define EXPECT_THROW_PARSER_EXCEPTION(statement, message) \ + ASSERT_THROW(statement, ParserException); \ + try { \ + statement; \ + } catch (const ParserException& e) { \ + EXPECT_EQ(e.msg, message); \ + } + +namespace YAML { +namespace { + +TEST(NodeSpecTest, Ex2_1_SeqScalars) { + Node doc = Load(ex2_1); + EXPECT_TRUE(doc.IsSequence()); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("Mark McGwire", doc[0].as()); + EXPECT_EQ("Sammy Sosa", doc[1].as()); + EXPECT_EQ("Ken Griffey", doc[2].as()); +} + +TEST(NodeSpecTest, Ex2_2_MappingScalarsToScalars) { + Node doc = Load(ex2_2); + EXPECT_TRUE(doc.IsMap()); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("65", doc["hr"].as()); + EXPECT_EQ("0.278", doc["avg"].as()); + EXPECT_EQ("147", doc["rbi"].as()); +} + +TEST(NodeSpecTest, Ex2_3_MappingScalarsToSequences) { + Node doc = Load(ex2_3); + EXPECT_TRUE(doc.IsMap()); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(3, doc["american"].size()); + EXPECT_EQ("Boston Red Sox", doc["american"][0].as()); + EXPECT_EQ("Detroit Tigers", doc["american"][1].as()); + EXPECT_EQ("New York Yankees", doc["american"][2].as()); + EXPECT_EQ(3, doc["national"].size()); + EXPECT_EQ("New York Mets", doc["national"][0].as()); + EXPECT_EQ("Chicago Cubs", doc["national"][1].as()); + EXPECT_EQ("Atlanta Braves", doc["national"][2].as()); +} + +TEST(NodeSpecTest, Ex2_4_SequenceOfMappings) { + Node doc = Load(ex2_4); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(3, doc[0].size()); + EXPECT_EQ("Mark McGwire", doc[0]["name"].as()); + EXPECT_EQ("65", doc[0]["hr"].as()); + EXPECT_EQ("0.278", doc[0]["avg"].as()); + EXPECT_EQ(3, doc[1].size()); + EXPECT_EQ("Sammy Sosa", doc[1]["name"].as()); + EXPECT_EQ("63", doc[1]["hr"].as()); + EXPECT_EQ("0.288", doc[1]["avg"].as()); +} + +TEST(NodeSpecTest, Ex2_5_SequenceOfSequences) { + Node doc = Load(ex2_5); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ(3, doc[0].size()); + EXPECT_EQ("name", doc[0][0].as()); + EXPECT_EQ("hr", doc[0][1].as()); + EXPECT_EQ("avg", doc[0][2].as()); + EXPECT_EQ(3, doc[1].size()); + EXPECT_EQ("Mark McGwire", doc[1][0].as()); + EXPECT_EQ("65", doc[1][1].as()); + EXPECT_EQ("0.278", doc[1][2].as()); + EXPECT_EQ(3, doc[2].size()); + EXPECT_EQ("Sammy Sosa", doc[2][0].as()); + EXPECT_EQ("63", doc[2][1].as()); + EXPECT_EQ("0.288", doc[2][2].as()); +} + +TEST(NodeSpecTest, Ex2_6_MappingOfMappings) { + Node doc = Load(ex2_6); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(2, doc["Mark McGwire"].size()); + EXPECT_EQ("65", doc["Mark McGwire"]["hr"].as()); + EXPECT_EQ("0.278", doc["Mark McGwire"]["avg"].as()); + EXPECT_EQ(2, doc["Sammy Sosa"].size()); + EXPECT_EQ("63", doc["Sammy Sosa"]["hr"].as()); + EXPECT_EQ("0.288", doc["Sammy Sosa"]["avg"].as()); +} + +TEST(NodeSpecTest, Ex2_7_TwoDocumentsInAStream) { + std::vector docs = LoadAll(ex2_7); + EXPECT_EQ(2, docs.size()); + + { + Node doc = docs[0]; + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("Mark McGwire", doc[0].as()); + EXPECT_EQ("Sammy Sosa", doc[1].as()); + EXPECT_EQ("Ken Griffey", doc[2].as()); + } + + { + Node doc = docs[1]; + EXPECT_EQ(2, doc.size()); + EXPECT_EQ("Chicago Cubs", doc[0].as()); + EXPECT_EQ("St Louis Cardinals", doc[1].as()); + } +} + +TEST(NodeSpecTest, Ex2_8_PlayByPlayFeed) { + std::vector docs = LoadAll(ex2_8); + EXPECT_EQ(2, docs.size()); + + { + Node doc = docs[0]; + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("20:03:20", doc["time"].as()); + EXPECT_EQ("Sammy Sosa", doc["player"].as()); + EXPECT_EQ("strike (miss)", doc["action"].as()); + } + + { + Node doc = docs[1]; + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("20:03:47", doc["time"].as()); + EXPECT_EQ("Sammy Sosa", doc["player"].as()); + EXPECT_EQ("grand slam", doc["action"].as()); + } +} + +TEST(NodeSpecTest, Ex2_9_SingleDocumentWithTwoComments) { + Node doc = Load(ex2_9); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(2, doc["hr"].size()); + EXPECT_EQ("Mark McGwire", doc["hr"][0].as()); + EXPECT_EQ("Sammy Sosa", doc["hr"][1].as()); + EXPECT_EQ(2, doc["rbi"].size()); + EXPECT_EQ("Sammy Sosa", doc["rbi"][0].as()); + EXPECT_EQ("Ken Griffey", doc["rbi"][1].as()); +} + +TEST(NodeSpecTest, Ex2_10_SimpleAnchor) { + Node doc = Load(ex2_10); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(2, doc["hr"].size()); + EXPECT_EQ("Mark McGwire", doc["hr"][0].as()); + EXPECT_EQ("Sammy Sosa", doc["hr"][1].as()); + EXPECT_EQ(2, doc["rbi"].size()); + EXPECT_EQ("Sammy Sosa", doc["rbi"][0].as()); + EXPECT_EQ("Ken Griffey", doc["rbi"][1].as()); +} + +TEST(NodeSpecTest, Ex2_11_MappingBetweenSequences) { + Node doc = Load(ex2_11); + + std::vector tigers_cubs; + tigers_cubs.push_back("Detroit Tigers"); + tigers_cubs.push_back("Chicago cubs"); + + std::vector yankees_braves; + yankees_braves.push_back("New York Yankees"); + yankees_braves.push_back("Atlanta Braves"); + + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(1, doc[tigers_cubs].size()); + EXPECT_EQ("2001-07-23", doc[tigers_cubs][0].as()); + EXPECT_EQ(3, doc[yankees_braves].size()); + EXPECT_EQ("2001-07-02", doc[yankees_braves][0].as()); + EXPECT_EQ("2001-08-12", doc[yankees_braves][1].as()); + EXPECT_EQ("2001-08-14", doc[yankees_braves][2].as()); +} + +TEST(NodeSpecTest, Ex2_12_CompactNestedMapping) { + Node doc = Load(ex2_12); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ(2, doc[0].size()); + EXPECT_EQ("Super Hoop", doc[0]["item"].as()); + EXPECT_EQ(1, doc[0]["quantity"].as()); + EXPECT_EQ(2, doc[1].size()); + EXPECT_EQ("Basketball", doc[1]["item"].as()); + EXPECT_EQ(4, doc[1]["quantity"].as()); + EXPECT_EQ(2, doc[2].size()); + EXPECT_EQ("Big Shoes", doc[2]["item"].as()); + EXPECT_EQ(1, doc[2]["quantity"].as()); +} + +TEST(NodeSpecTest, Ex2_13_InLiteralsNewlinesArePreserved) { + Node doc = Load(ex2_13); + EXPECT_TRUE(doc.as() == + "\\//||\\/||\n" + "// || ||__"); +} + +TEST(NodeSpecTest, Ex2_14_InFoldedScalarsNewlinesBecomeSpaces) { + Node doc = Load(ex2_14); + EXPECT_TRUE(doc.as() == + "Mark McGwire's year was crippled by a knee injury."); +} + +TEST(NodeSpecTest, + Ex2_15_FoldedNewlinesArePreservedForMoreIndentedAndBlankLines) { + Node doc = Load(ex2_15); + EXPECT_TRUE(doc.as() == + "Sammy Sosa completed another fine season with great stats.\n\n" + " 63 Home Runs\n" + " 0.288 Batting Average\n\n" + "What a year!"); +} + +TEST(NodeSpecTest, Ex2_16_IndentationDeterminesScope) { + Node doc = Load(ex2_16); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("Mark McGwire", doc["name"].as()); + EXPECT_TRUE(doc["accomplishment"].as() == + "Mark set a major league home run record in 1998.\n"); + EXPECT_TRUE(doc["stats"].as() == + "65 Home Runs\n0.278 Batting Average\n"); +} + +TEST(NodeSpecTest, Ex2_17_QuotedScalars) { + Node doc = Load(ex2_17); + EXPECT_EQ(6, doc.size()); + EXPECT_EQ("Sosa did fine.\xe2\x98\xba", doc["unicode"].as()); + EXPECT_EQ("\b1998\t1999\t2000\n", doc["control"].as()); + EXPECT_EQ("\x0d\x0a is \r\n", doc["hex esc"].as()); + EXPECT_EQ("\"Howdy!\" he cried.", doc["single"].as()); + EXPECT_EQ(" # Not a 'comment'.", doc["quoted"].as()); + EXPECT_EQ("|\\-*-/|", doc["tie-fighter"].as()); +} + +TEST(NodeSpecTest, Ex2_18_MultiLineFlowScalars) { + Node doc = Load(ex2_18); + EXPECT_EQ(2, doc.size()); + EXPECT_TRUE(doc["plain"].as() == + "This unquoted scalar spans many lines."); + EXPECT_TRUE(doc["quoted"].as() == + "So does this quoted scalar.\n"); +} + +// TODO: 2.19 - 2.22 schema tags + +TEST(NodeSpecTest, Ex2_23_VariousExplicitTags) { + Node doc = Load(ex2_23); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("tag:yaml.org,2002:str", doc["not-date"].Tag()); + EXPECT_EQ("2002-04-28", doc["not-date"].as()); + EXPECT_EQ("tag:yaml.org,2002:binary", doc["picture"].Tag()); + EXPECT_TRUE(doc["picture"].as() == + "R0lGODlhDAAMAIQAAP//9/X\n" + "17unp5WZmZgAAAOfn515eXv\n" + "Pz7Y6OjuDg4J+fn5OTk6enp\n" + "56enmleECcgggoBADs=\n"); + EXPECT_EQ("!something", doc["application specific tag"].Tag()); + EXPECT_TRUE(doc["application specific tag"].as() == + "The semantics of the tag\n" + "above may be different for\n" + "different documents."); +} + +TEST(NodeSpecTest, Ex2_24_GlobalTags) { + Node doc = Load(ex2_24); + EXPECT_EQ("tag:clarkevans.com,2002:shape", doc.Tag()); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("tag:clarkevans.com,2002:circle", doc[0].Tag()); + EXPECT_EQ(2, doc[0].size()); + EXPECT_EQ(2, doc[0]["center"].size()); + EXPECT_EQ(73, doc[0]["center"]["x"].as()); + EXPECT_EQ(129, doc[0]["center"]["y"].as()); + EXPECT_EQ(7, doc[0]["radius"].as()); + EXPECT_EQ("tag:clarkevans.com,2002:line", doc[1].Tag()); + EXPECT_EQ(2, doc[1].size()); + EXPECT_EQ(2, doc[1]["start"].size()); + EXPECT_EQ(73, doc[1]["start"]["x"].as()); + EXPECT_EQ(129, doc[1]["start"]["y"].as()); + EXPECT_EQ(2, doc[1]["finish"].size()); + EXPECT_EQ(89, doc[1]["finish"]["x"].as()); + EXPECT_EQ(102, doc[1]["finish"]["y"].as()); + EXPECT_EQ("tag:clarkevans.com,2002:label", doc[2].Tag()); + EXPECT_EQ(3, doc[2].size()); + EXPECT_EQ(2, doc[2]["start"].size()); + EXPECT_EQ(73, doc[2]["start"]["x"].as()); + EXPECT_EQ(129, doc[2]["start"]["y"].as()); + EXPECT_EQ("0xFFEEBB", doc[2]["color"].as()); + EXPECT_EQ("Pretty vector drawing.", doc[2]["text"].as()); +} + +TEST(NodeSpecTest, Ex2_25_UnorderedSets) { + Node doc = Load(ex2_25); + EXPECT_EQ("tag:yaml.org,2002:set", doc.Tag()); + EXPECT_EQ(3, doc.size()); + EXPECT_TRUE(doc["Mark McGwire"].IsNull()); + EXPECT_TRUE(doc["Sammy Sosa"].IsNull()); + EXPECT_TRUE(doc["Ken Griffey"].IsNull()); +} + +TEST(NodeSpecTest, Ex2_16_OrderedMappings) { + Node doc = Load(ex2_26); + EXPECT_EQ("tag:yaml.org,2002:omap", doc.Tag()); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ(1, doc[0].size()); + EXPECT_EQ(65, doc[0]["Mark McGwire"].as()); + EXPECT_EQ(1, doc[1].size()); + EXPECT_EQ(63, doc[1]["Sammy Sosa"].as()); + EXPECT_EQ(1, doc[2].size()); + EXPECT_EQ(58, doc[2]["Ken Griffey"].as()); +} + +TEST(NodeSpecTest, Ex2_27_Invoice) { + Node doc = Load(ex2_27); + EXPECT_EQ("tag:clarkevans.com,2002:invoice", doc.Tag()); + EXPECT_EQ(8, doc.size()); + EXPECT_EQ(34843, doc["invoice"].as()); + EXPECT_EQ("2001-01-23", doc["date"].as()); + EXPECT_EQ(3, doc["bill-to"].size()); + EXPECT_EQ("Chris", doc["bill-to"]["given"].as()); + EXPECT_EQ("Dumars", doc["bill-to"]["family"].as()); + EXPECT_EQ(4, doc["bill-to"]["address"].size()); + EXPECT_TRUE(doc["bill-to"]["address"]["lines"].as() == + "458 Walkman Dr.\nSuite #292\n"); + EXPECT_TRUE(doc["bill-to"]["address"]["city"].as() == + "Royal Oak"); + EXPECT_EQ("MI", doc["bill-to"]["address"]["state"].as()); + EXPECT_EQ("48046", doc["bill-to"]["address"]["postal"].as()); + EXPECT_EQ(3, doc["ship-to"].size()); + EXPECT_EQ("Chris", doc["ship-to"]["given"].as()); + EXPECT_EQ("Dumars", doc["ship-to"]["family"].as()); + EXPECT_EQ(4, doc["ship-to"]["address"].size()); + EXPECT_TRUE(doc["ship-to"]["address"]["lines"].as() == + "458 Walkman Dr.\nSuite #292\n"); + EXPECT_TRUE(doc["ship-to"]["address"]["city"].as() == + "Royal Oak"); + EXPECT_EQ("MI", doc["ship-to"]["address"]["state"].as()); + EXPECT_EQ("48046", doc["ship-to"]["address"]["postal"].as()); + EXPECT_EQ(2, doc["product"].size()); + EXPECT_EQ(4, doc["product"][0].size()); + EXPECT_EQ("BL394D", doc["product"][0]["sku"].as()); + EXPECT_EQ(4, doc["product"][0]["quantity"].as()); + EXPECT_TRUE(doc["product"][0]["description"].as() == + "Basketball"); + EXPECT_EQ("450.00", doc["product"][0]["price"].as()); + EXPECT_EQ(4, doc["product"][1].size()); + EXPECT_EQ("BL4438H", doc["product"][1]["sku"].as()); + EXPECT_EQ(1, doc["product"][1]["quantity"].as()); + EXPECT_TRUE(doc["product"][1]["description"].as() == + "Super Hoop"); + EXPECT_EQ("2392.00", doc["product"][1]["price"].as()); + EXPECT_EQ("251.42", doc["tax"].as()); + EXPECT_EQ("4443.52", doc["total"].as()); + EXPECT_EQ( + "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.", + doc["comments"].as()); +} + +TEST(NodeSpecTest, Ex2_28_LogFile) { + std::vector docs = LoadAll(ex2_28); + EXPECT_EQ(3, docs.size()); + + { + Node doc = docs[0]; + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("2001-11-23 15:01:42 -5", doc["Time"].as()); + EXPECT_EQ("ed", doc["User"].as()); + EXPECT_TRUE(doc["Warning"].as() == + "This is an error message for the log file"); + } + + { + Node doc = docs[1]; + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("2001-11-23 15:02:31 -5", doc["Time"].as()); + EXPECT_EQ("ed", doc["User"].as()); + EXPECT_TRUE(doc["Warning"].as() == + "A slightly different error message."); + } + + { + Node doc = docs[2]; + EXPECT_EQ(4, doc.size()); + EXPECT_EQ("2001-11-23 15:03:17 -5", doc["Date"].as()); + EXPECT_EQ("ed", doc["User"].as()); + EXPECT_EQ("Unknown variable \"bar\"", doc["Fatal"].as()); + EXPECT_EQ(2, doc["Stack"].size()); + EXPECT_EQ(3, doc["Stack"][0].size()); + EXPECT_EQ("TopClass.py", doc["Stack"][0]["file"].as()); + EXPECT_EQ("23", doc["Stack"][0]["line"].as()); + EXPECT_TRUE(doc["Stack"][0]["code"].as() == + "x = MoreObject(\"345\\n\")\n"); + EXPECT_EQ(3, doc["Stack"][1].size()); + EXPECT_EQ("MoreClass.py", doc["Stack"][1]["file"].as()); + EXPECT_EQ("58", doc["Stack"][1]["line"].as()); + EXPECT_EQ("foo = bar", doc["Stack"][1]["code"].as()); + } +} + +// TODO: 5.1 - 5.2 BOM + +TEST(NodeSpecTest, Ex5_3_BlockStructureIndicators) { + Node doc = Load(ex5_3); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(2, doc["sequence"].size()); + EXPECT_EQ("one", doc["sequence"][0].as()); + EXPECT_EQ("two", doc["sequence"][1].as()); + EXPECT_EQ(2, doc["mapping"].size()); + EXPECT_EQ("blue", doc["mapping"]["sky"].as()); + EXPECT_EQ("green", doc["mapping"]["sea"].as()); +} + +TEST(NodeSpecTest, Ex5_4_FlowStructureIndicators) { + Node doc = Load(ex5_4); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(2, doc["sequence"].size()); + EXPECT_EQ("one", doc["sequence"][0].as()); + EXPECT_EQ("two", doc["sequence"][1].as()); + EXPECT_EQ(2, doc["mapping"].size()); + EXPECT_EQ("blue", doc["mapping"]["sky"].as()); + EXPECT_EQ("green", doc["mapping"]["sea"].as()); +} + +TEST(NodeSpecTest, Ex5_5_CommentIndicator) { + Node doc = Load(ex5_5); + EXPECT_TRUE(doc.IsNull()); +} + +TEST(NodeSpecTest, Ex5_6_NodePropertyIndicators) { + Node doc = Load(ex5_6); + EXPECT_EQ(2, doc.size()); + EXPECT_TRUE(doc["anchored"].as() == + "value"); // TODO: assert tag + EXPECT_EQ("value", doc["alias"].as()); +} + +TEST(NodeSpecTest, Ex5_7_BlockScalarIndicators) { + Node doc = Load(ex5_7); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ("some\ntext\n", doc["literal"].as()); + EXPECT_EQ("some text\n", doc["folded"].as()); +} + +TEST(NodeSpecTest, Ex5_8_QuotedScalarIndicators) { + Node doc = Load(ex5_8); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ("text", doc["single"].as()); + EXPECT_EQ("text", doc["double"].as()); +} + +// TODO: 5.9 directive +// TODO: 5.10 reserved indicator + +TEST(NodeSpecTest, Ex5_11_LineBreakCharacters) { + Node doc = Load(ex5_11); + EXPECT_TRUE(doc.as() == + "Line break (no glyph)\nLine break (glyphed)\n"); +} + +TEST(NodeSpecTest, Ex5_12_TabsAndSpaces) { + Node doc = Load(ex5_12); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ("Quoted\t", doc["quoted"].as()); + EXPECT_TRUE(doc["block"].as() == + "void main() {\n" + "\tprintf(\"Hello, world!\\n\");\n" + "}"); +} + +TEST(NodeSpecTest, Ex5_13_EscapedCharacters) { + Node doc = Load(ex5_13); + EXPECT_TRUE(doc.as() == + "Fun with \x5C \x22 \x07 \x08 \x1B \x0C \x0A \x0D \x09 \x0B " + + std::string("\x00", 1) + + " \x20 \xA0 \x85 \xe2\x80\xa8 \xe2\x80\xa9 A A A"); +} + +TEST(NodeSpecTest, Ex5_14_InvalidEscapedCharacters) { + EXPECT_THROW_PARSER_EXCEPTION(Load(ex5_14), + std::string(ErrorMsg::INVALID_ESCAPE) + "c"); +} + +TEST(NodeSpecTest, Ex6_1_IndentationSpaces) { + Node doc = Load(ex6_1); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(2, doc["Not indented"].size()); + EXPECT_TRUE(doc["Not indented"]["By one space"].as() == + "By four\n spaces\n"); + EXPECT_EQ(3, doc["Not indented"]["Flow style"].size()); + EXPECT_TRUE(doc["Not indented"]["Flow style"][0].as() == + "By two"); + EXPECT_TRUE(doc["Not indented"]["Flow style"][1].as() == + "Also by two"); + EXPECT_TRUE(doc["Not indented"]["Flow style"][2].as() == + "Still by two"); +} + +TEST(NodeSpecTest, Ex6_2_IndentationIndicators) { + Node doc = Load(ex6_2); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(2, doc["a"].size()); + EXPECT_EQ("b", doc["a"][0].as()); + EXPECT_EQ(2, doc["a"][1].size()); + EXPECT_EQ("c", doc["a"][1][0].as()); + EXPECT_EQ("d", doc["a"][1][1].as()); +} + +TEST(NodeSpecTest, Ex6_3_SeparationSpaces) { + Node doc = Load(ex6_3); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(1, doc[0].size()); + EXPECT_EQ("bar", doc[0]["foo"].as()); + EXPECT_EQ(2, doc[1].size()); + EXPECT_EQ("baz", doc[1][0].as()); + EXPECT_EQ("baz", doc[1][1].as()); +} + +TEST(NodeSpecTest, Ex6_4_LinePrefixes) { + Node doc = Load(ex6_4); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("text lines", doc["plain"].as()); + EXPECT_EQ("text lines", doc["quoted"].as()); + EXPECT_EQ("text\n \tlines\n", doc["block"].as()); +} + +TEST(NodeSpecTest, Ex6_5_EmptyLines) { + Node doc = Load(ex6_5); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ("Empty line\nas a line feed", doc["Folding"].as()); + EXPECT_EQ("Clipped empty lines\n", doc["Chomping"].as()); +} + +TEST(NodeSpecTest, Ex6_6_LineFolding) { + Node doc = Load(ex6_6); + EXPECT_EQ("trimmed\n\n\nas space", doc.as()); +} + +TEST(NodeSpecTest, Ex6_7_BlockFolding) { + Node doc = Load(ex6_7); + EXPECT_EQ("foo \n\n\t bar\n\nbaz\n", doc.as()); +} + +TEST(NodeSpecTest, Ex6_8_FlowFolding) { + Node doc = Load(ex6_8); + EXPECT_EQ(" foo\nbar\nbaz ", doc.as()); +} + +TEST(NodeSpecTest, Ex6_9_SeparatedComment) { + Node doc = Load(ex6_9); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ("value", doc["key"].as()); +} + +TEST(NodeSpecTest, Ex6_10_CommentLines) { + Node doc = Load(ex6_10); + EXPECT_TRUE(doc.IsNull()); +} + +TEST(NodeSpecTest, Ex6_11_MultiLineComments) { + Node doc = Load(ex6_11); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ("value", doc["key"].as()); +} + +TEST(NodeSpecTest, Ex6_12_SeparationSpacesII) { + Node doc = Load(ex6_12); + + std::map sammy; + sammy["first"] = "Sammy"; + sammy["last"] = "Sosa"; + + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(2, doc[sammy].size()); + EXPECT_EQ(65, doc[sammy]["hr"].as()); + EXPECT_EQ("0.278", doc[sammy]["avg"].as()); +} + +TEST(NodeSpecTest, Ex6_13_ReservedDirectives) { + Node doc = Load(ex6_13); + EXPECT_EQ("foo", doc.as()); +} + +TEST(NodeSpecTest, Ex6_14_YAMLDirective) { + Node doc = Load(ex6_14); + EXPECT_EQ("foo", doc.as()); +} + +TEST(NodeSpecTest, Ex6_15_InvalidRepeatedYAMLDirective) { + EXPECT_THROW_PARSER_EXCEPTION(Load(ex6_15), + ErrorMsg::REPEATED_YAML_DIRECTIVE); +} + +TEST(NodeSpecTest, Ex6_16_TagDirective) { + Node doc = Load(ex6_16); + EXPECT_EQ("tag:yaml.org,2002:str", doc.Tag()); + EXPECT_EQ("foo", doc.as()); +} + +TEST(NodeSpecTest, Ex6_17_InvalidRepeatedTagDirective) { + EXPECT_THROW_PARSER_EXCEPTION(Load(ex6_17), ErrorMsg::REPEATED_TAG_DIRECTIVE); +} + +TEST(NodeSpecTest, Ex6_18_PrimaryTagHandle) { + std::vector docs = LoadAll(ex6_18); + EXPECT_EQ(2, docs.size()); + + { + Node doc = docs[0]; + EXPECT_EQ("!foo", doc.Tag()); + EXPECT_EQ("bar", doc.as()); + } + + { + Node doc = docs[1]; + EXPECT_EQ("tag:example.com,2000:app/foo", doc.Tag()); + EXPECT_EQ("bar", doc.as()); + } +} + +TEST(NodeSpecTest, Ex6_19_SecondaryTagHandle) { + Node doc = Load(ex6_19); + EXPECT_EQ("tag:example.com,2000:app/int", doc.Tag()); + EXPECT_EQ("1 - 3", doc.as()); +} + +TEST(NodeSpecTest, Ex6_20_TagHandles) { + Node doc = Load(ex6_20); + EXPECT_EQ("tag:example.com,2000:app/foo", doc.Tag()); + EXPECT_EQ("bar", doc.as()); +} + +TEST(NodeSpecTest, Ex6_21_LocalTagPrefix) { + std::vector docs = LoadAll(ex6_21); + EXPECT_EQ(2, docs.size()); + + { + Node doc = docs[0]; + EXPECT_EQ("!my-light", doc.Tag()); + EXPECT_EQ("fluorescent", doc.as()); + } + + { + Node doc = docs[1]; + EXPECT_EQ("!my-light", doc.Tag()); + EXPECT_EQ("green", doc.as()); + } +} + +TEST(NodeSpecTest, Ex6_22_GlobalTagPrefix) { + Node doc = Load(ex6_22); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ("tag:example.com,2000:app/foo", doc[0].Tag()); + EXPECT_EQ("bar", doc[0].as()); +} + +TEST(NodeSpecTest, Ex6_23_NodeProperties) { + Node doc = Load(ex6_23); + EXPECT_EQ(2, doc.size()); + for (const_iterator it = doc.begin(); it != doc.end(); ++it) { + if (it->first.as() == "foo") { + EXPECT_EQ("tag:yaml.org,2002:str", it->first.Tag()); + EXPECT_EQ("tag:yaml.org,2002:str", it->second.Tag()); + EXPECT_EQ("bar", it->second.as()); + } else if (it->first.as() == "baz") { + EXPECT_EQ("foo", it->second.as()); + } else + FAIL() << "unknown key"; + } +} + +TEST(NodeSpecTest, Ex6_24_VerbatimTags) { + Node doc = Load(ex6_24); + EXPECT_EQ(1, doc.size()); + for (const_iterator it = doc.begin(); it != doc.end(); ++it) { + EXPECT_EQ("tag:yaml.org,2002:str", it->first.Tag()); + EXPECT_EQ("foo", it->first.as()); + EXPECT_EQ("!bar", it->second.Tag()); + EXPECT_EQ("baz", it->second.as()); + } +} + +TEST(NodeSpecTest, DISABLED_Ex6_25_InvalidVerbatimTags) { + Node doc = Load(ex6_25); + // TODO: check tags (but we probably will say these are valid, I think) + FAIL() << "not implemented yet"; +} + +TEST(NodeSpecTest, Ex6_26_TagShorthands) { + Node doc = Load(ex6_26); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("!local", doc[0].Tag()); + EXPECT_EQ("foo", doc[0].as()); + EXPECT_EQ("tag:yaml.org,2002:str", doc[1].Tag()); + EXPECT_EQ("bar", doc[1].as()); + EXPECT_EQ("tag:example.com,2000:app/tag%21", doc[2].Tag()); + EXPECT_EQ("baz", doc[2].as()); +} + +TEST(NodeSpecTest, Ex6_27a_InvalidTagShorthands) { + EXPECT_THROW_PARSER_EXCEPTION(Load(ex6_27a), ErrorMsg::TAG_WITH_NO_SUFFIX); +} + +// TODO: should we reject this one (since !h! is not declared)? +TEST(NodeSpecTest, DISABLED_Ex6_27b_InvalidTagShorthands) { + Load(ex6_27b); + FAIL() << "not implemented yet"; +} + +TEST(NodeSpecTest, Ex6_28_NonSpecificTags) { + Node doc = Load(ex6_28); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("12", doc[0].as()); // TODO: check tags. How? + EXPECT_EQ(12, doc[1].as()); + EXPECT_EQ("12", doc[2].as()); +} + +TEST(NodeSpecTest, Ex6_29_NodeAnchors) { + Node doc = Load(ex6_29); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ("Value", doc["First occurrence"].as()); + EXPECT_EQ("Value", doc["Second occurrence"].as()); +} + +TEST(NodeSpecTest, Ex7_1_AliasNodes) { + Node doc = Load(ex7_1); + EXPECT_EQ(4, doc.size()); + EXPECT_EQ("Foo", doc["First occurrence"].as()); + EXPECT_EQ("Foo", doc["Second occurrence"].as()); + EXPECT_EQ("Bar", doc["Override anchor"].as()); + EXPECT_EQ("Bar", doc["Reuse anchor"].as()); +} + +TEST(NodeSpecTest, Ex7_2_EmptyNodes) { + Node doc = Load(ex7_2); + EXPECT_EQ(2, doc.size()); + for (const_iterator it = doc.begin(); it != doc.end(); ++it) { + if (it->first.as() == "foo") { + EXPECT_EQ("tag:yaml.org,2002:str", it->second.Tag()); + EXPECT_EQ("", it->second.as()); + } else if (it->first.as() == "") { + EXPECT_EQ("tag:yaml.org,2002:str", it->first.Tag()); + EXPECT_EQ("bar", it->second.as()); + } else + FAIL() << "unexpected key"; + } +} + +TEST(NodeSpecTest, Ex7_3_CompletelyEmptyNodes) { + Node doc = Load(ex7_3); + EXPECT_EQ(2, doc.size()); + EXPECT_TRUE(doc["foo"].IsNull()); + EXPECT_EQ("bar", doc[Null].as()); +} + +TEST(NodeSpecTest, Ex7_4_DoubleQuotedImplicitKeys) { + Node doc = Load(ex7_4); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(1, doc["implicit block key"].size()); + EXPECT_EQ(1, doc["implicit block key"][0].size()); + EXPECT_EQ( + "value", + doc["implicit block key"][0]["implicit flow key"].as()); +} + +TEST(NodeSpecTest, Ex7_5_DoubleQuotedLineBreaks) { + Node doc = Load(ex7_5); + EXPECT_TRUE(doc.as() == + "folded to a space,\nto a line feed, or \t \tnon-content"); +} + +TEST(NodeSpecTest, Ex7_6_DoubleQuotedLines) { + Node doc = Load(ex7_6); + EXPECT_TRUE(doc.as() == + " 1st non-empty\n2nd non-empty 3rd non-empty "); +} + +TEST(NodeSpecTest, Ex7_7_SingleQuotedCharacters) { + Node doc = Load(ex7_7); + EXPECT_EQ("here's to \"quotes\"", doc.as()); +} + +TEST(NodeSpecTest, Ex7_8_SingleQuotedImplicitKeys) { + Node doc = Load(ex7_8); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(1, doc["implicit block key"].size()); + EXPECT_EQ(1, doc["implicit block key"][0].size()); + EXPECT_EQ( + "value", + doc["implicit block key"][0]["implicit flow key"].as()); +} + +TEST(NodeSpecTest, Ex7_9_SingleQuotedLines) { + Node doc = Load(ex7_9); + EXPECT_TRUE(doc.as() == + " 1st non-empty\n2nd non-empty 3rd non-empty "); +} + +TEST(NodeSpecTest, Ex7_10_PlainCharacters) { + Node doc = Load(ex7_10); + EXPECT_EQ(6, doc.size()); + EXPECT_EQ("::vector", doc[0].as()); + EXPECT_EQ(": - ()", doc[1].as()); + EXPECT_EQ("Up, up, and away!", doc[2].as()); + EXPECT_EQ(-123, doc[3].as()); + EXPECT_EQ("http://example.com/foo#bar", doc[4].as()); + EXPECT_EQ(5, doc[5].size()); + EXPECT_EQ("::vector", doc[5][0].as()); + EXPECT_EQ(": - ()", doc[5][1].as()); + EXPECT_EQ("Up, up, and away!", doc[5][2].as()); + EXPECT_EQ(-123, doc[5][3].as()); + EXPECT_EQ("http://example.com/foo#bar", doc[5][4].as()); +} + +TEST(NodeSpecTest, Ex7_11_PlainImplicitKeys) { + Node doc = Load(ex7_11); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(1, doc["implicit block key"].size()); + EXPECT_EQ(1, doc["implicit block key"][0].size()); + EXPECT_EQ( + "value", + doc["implicit block key"][0]["implicit flow key"].as()); +} + +TEST(NodeSpecTest, Ex7_12_PlainLines) { + Node doc = Load(ex7_12); + EXPECT_TRUE(doc.as() == + "1st non-empty\n2nd non-empty 3rd non-empty"); +} + +TEST(NodeSpecTest, Ex7_13_FlowSequence) { + Node doc = Load(ex7_13); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(2, doc[0].size()); + EXPECT_EQ("one", doc[0][0].as()); + EXPECT_EQ("two", doc[0][1].as()); + EXPECT_EQ(2, doc[1].size()); + EXPECT_EQ("three", doc[1][0].as()); + EXPECT_EQ("four", doc[1][1].as()); +} + +TEST(NodeSpecTest, Ex7_14_FlowSequenceEntries) { + Node doc = Load(ex7_14); + EXPECT_EQ(5, doc.size()); + EXPECT_EQ("double quoted", doc[0].as()); + EXPECT_EQ("single quoted", doc[1].as()); + EXPECT_EQ("plain text", doc[2].as()); + EXPECT_EQ(1, doc[3].size()); + EXPECT_EQ("nested", doc[3][0].as()); + EXPECT_EQ(1, doc[4].size()); + EXPECT_EQ("pair", doc[4]["single"].as()); +} + +TEST(NodeSpecTest, Ex7_15_FlowMappings) { + Node doc = Load(ex7_15); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(2, doc[0].size()); + EXPECT_EQ("two", doc[0]["one"].as()); + EXPECT_EQ("four", doc[0]["three"].as()); + EXPECT_EQ(2, doc[1].size()); + EXPECT_EQ("six", doc[1]["five"].as()); + EXPECT_EQ("eight", doc[1]["seven"].as()); +} + +TEST(NodeSpecTest, Ex7_16_FlowMappingEntries) { + Node doc = Load(ex7_16); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("entry", doc["explicit"].as()); + EXPECT_EQ("entry", doc["implicit"].as()); + EXPECT_TRUE(doc[Null].IsNull()); +} + +TEST(NodeSpecTest, Ex7_17_FlowMappingSeparateValues) { + Node doc = Load(ex7_17); + EXPECT_EQ(4, doc.size()); + EXPECT_EQ("separate", doc["unquoted"].as()); + EXPECT_TRUE(doc["http://foo.com"].IsNull()); + EXPECT_TRUE(doc["omitted value"].IsNull()); + EXPECT_EQ("omitted key", doc[Null].as()); +} + +TEST(NodeSpecTest, Ex7_18_FlowMappingAdjacentValues) { + Node doc = Load(ex7_18); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("value", doc["adjacent"].as()); + EXPECT_EQ("value", doc["readable"].as()); + EXPECT_TRUE(doc["empty"].IsNull()); +} + +TEST(NodeSpecTest, Ex7_19_SinglePairFlowMappings) { + Node doc = Load(ex7_19); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(1, doc[0].size()); + EXPECT_EQ("bar", doc[0]["foo"].as()); +} + +TEST(NodeSpecTest, Ex7_20_SinglePairExplicitEntry) { + Node doc = Load(ex7_20); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(1, doc[0].size()); + EXPECT_EQ("baz", doc[0]["foo bar"].as()); +} + +TEST(NodeSpecTest, Ex7_21_SinglePairImplicitEntries) { + Node doc = Load(ex7_21); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ(1, doc[0].size()); + EXPECT_EQ(1, doc[0][0].size()); + EXPECT_EQ("separate", doc[0][0]["YAML"].as()); + EXPECT_EQ(1, doc[1].size()); + EXPECT_EQ(1, doc[1][0].size()); + EXPECT_EQ("empty key entry", doc[1][0][Null].as()); + EXPECT_EQ(1, doc[2].size()); + EXPECT_EQ(1, doc[2][0].size()); + + std::map key; + key["JSON"] = "like"; + EXPECT_EQ("adjacent", doc[2][0][key].as()); +} + +TEST(NodeSpecTest, Ex7_22_InvalidImplicitKeys) { + EXPECT_THROW_PARSER_EXCEPTION(Load(ex7_22), ErrorMsg::END_OF_SEQ_FLOW); +} + +TEST(NodeSpecTest, Ex7_23_FlowContent) { + Node doc = Load(ex7_23); + EXPECT_EQ(5, doc.size()); + EXPECT_EQ(2, doc[0].size()); + EXPECT_EQ("a", doc[0][0].as()); + EXPECT_EQ("b", doc[0][1].as()); + EXPECT_EQ(1, doc[1].size()); + EXPECT_EQ("b", doc[1]["a"].as()); + EXPECT_EQ("a", doc[2].as()); + EXPECT_EQ('b', doc[3].as()); + EXPECT_EQ("c", doc[4].as()); +} + +TEST(NodeSpecTest, Ex7_24_FlowNodes) { + Node doc = Load(ex7_24); + EXPECT_EQ(5, doc.size()); + EXPECT_EQ("tag:yaml.org,2002:str", doc[0].Tag()); + EXPECT_EQ("a", doc[0].as()); + EXPECT_EQ('b', doc[1].as()); + EXPECT_EQ("c", doc[2].as()); + EXPECT_EQ("c", doc[3].as()); + EXPECT_EQ("tag:yaml.org,2002:str", doc[4].Tag()); + EXPECT_EQ("", doc[4].as()); +} + +TEST(NodeSpecTest, Ex8_1_BlockScalarHeader) { + Node doc = Load(ex8_1); + EXPECT_EQ(4, doc.size()); + EXPECT_EQ("literal\n", doc[0].as()); + EXPECT_EQ(" folded\n", doc[1].as()); + EXPECT_EQ("keep\n\n", doc[2].as()); + EXPECT_EQ(" strip", doc[3].as()); +} + +TEST(NodeSpecTest, Ex8_2_BlockIndentationHeader) { + Node doc = Load(ex8_2); + EXPECT_EQ(4, doc.size()); + EXPECT_EQ("detected\n", doc[0].as()); + EXPECT_EQ("\n\n# detected\n", doc[1].as()); + EXPECT_EQ(" explicit\n", doc[2].as()); + EXPECT_EQ("\t\ndetected\n", doc[3].as()); +} + +TEST(NodeSpecTest, Ex8_3a_InvalidBlockScalarIndentationIndicators) { + EXPECT_THROW_PARSER_EXCEPTION(Load(ex8_3a), ErrorMsg::END_OF_SEQ); +} + +TEST(NodeSpecTest, Ex8_3b_InvalidBlockScalarIndentationIndicators) { + EXPECT_THROW_PARSER_EXCEPTION(Load(ex8_3b), ErrorMsg::END_OF_SEQ); +} + +TEST(NodeSpecTest, Ex8_3c_InvalidBlockScalarIndentationIndicators) { + EXPECT_THROW_PARSER_EXCEPTION(Load(ex8_3c), ErrorMsg::END_OF_SEQ); +} + +TEST(NodeSpecTest, Ex8_4_ChompingFinalLineBreak) { + Node doc = Load(ex8_4); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("text", doc["strip"].as()); + EXPECT_EQ("text\n", doc["clip"].as()); + EXPECT_EQ("text\n", doc["keep"].as()); +} + +TEST(NodeSpecTest, DISABLED_Ex8_5_ChompingTrailingLines) { + Node doc = Load(ex8_5); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("# text", doc["strip"].as()); + EXPECT_EQ("# text\n", doc["clip"].as()); + // NOTE: I believe this is a bug in the YAML spec - + // it should be "# text\n\n" + EXPECT_EQ("# text\n", doc["keep"].as()); +} + +TEST(NodeSpecTest, Ex8_6_EmptyScalarChomping) { + Node doc = Load(ex8_6); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("", doc["strip"].as()); + EXPECT_EQ("", doc["clip"].as()); + EXPECT_EQ("\n", doc["keep"].as()); +} + +TEST(NodeSpecTest, Ex8_7_LiteralScalar) { + Node doc = Load(ex8_7); + EXPECT_EQ("literal\n\ttext\n", doc.as()); +} + +TEST(NodeSpecTest, Ex8_8_LiteralContent) { + Node doc = Load(ex8_8); + EXPECT_EQ("\n\nliteral\n \n\ntext\n", doc.as()); +} + +TEST(NodeSpecTest, Ex8_9_FoldedScalar) { + Node doc = Load(ex8_9); + EXPECT_EQ("folded text\n", doc.as()); +} + +TEST(NodeSpecTest, Ex8_10_FoldedLines) { + Node doc = Load(ex8_10); + EXPECT_TRUE(doc.as() == + "\nfolded line\nnext line\n * bullet\n\n * list\n * " + "lines\n\nlast line\n"); +} + +TEST(NodeSpecTest, Ex8_11_MoreIndentedLines) { + Node doc = Load(ex8_11); + EXPECT_TRUE(doc.as() == + "\nfolded line\nnext line\n * bullet\n\n * list\n * " + "lines\n\nlast line\n"); +} + +TEST(NodeSpecTest, Ex8_12_EmptySeparationLines) { + Node doc = Load(ex8_12); + EXPECT_TRUE(doc.as() == + "\nfolded line\nnext line\n * bullet\n\n * list\n * " + "lines\n\nlast line\n"); +} + +TEST(NodeSpecTest, Ex8_13_FinalEmptyLines) { + Node doc = Load(ex8_13); + EXPECT_TRUE(doc.as() == + "\nfolded line\nnext line\n * bullet\n\n * list\n * " + "lines\n\nlast line\n"); +} + +TEST(NodeSpecTest, Ex8_14_BlockSequence) { + Node doc = Load(ex8_14); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(2, doc["block sequence"].size()); + EXPECT_EQ("one", doc["block sequence"][0].as()); + EXPECT_EQ(1, doc["block sequence"][1].size()); + EXPECT_EQ("three", doc["block sequence"][1]["two"].as()); +} + +TEST(NodeSpecTest, Ex8_15_BlockSequenceEntryTypes) { + Node doc = Load(ex8_15); + EXPECT_EQ(4, doc.size()); + EXPECT_TRUE(doc[0].IsNull()); + EXPECT_EQ("block node\n", doc[1].as()); + EXPECT_EQ(2, doc[2].size()); + EXPECT_EQ("one", doc[2][0].as()); + EXPECT_EQ("two", doc[2][1].as()); + EXPECT_EQ(1, doc[3].size()); + EXPECT_EQ("two", doc[3]["one"].as()); +} + +TEST(NodeSpecTest, Ex8_16_BlockMappings) { + Node doc = Load(ex8_16); + EXPECT_EQ(1, doc.size()); + EXPECT_EQ(1, doc["block mapping"].size()); + EXPECT_EQ("value", doc["block mapping"]["key"].as()); +} + +TEST(NodeSpecTest, Ex8_17_ExplicitBlockMappingEntries) { + Node doc = Load(ex8_17); + EXPECT_EQ(2, doc.size()); + EXPECT_TRUE(doc["explicit key"].IsNull()); + EXPECT_EQ(2, doc["block key\n"].size()); + EXPECT_EQ("one", doc["block key\n"][0].as()); + EXPECT_EQ("two", doc["block key\n"][1].as()); +} + +TEST(NodeSpecTest, Ex8_18_ImplicitBlockMappingEntries) { + Node doc = Load(ex8_18); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("in-line value", doc["plain key"].as()); + EXPECT_TRUE(doc[Null].IsNull()); + EXPECT_EQ(1, doc["quoted key"].size()); + EXPECT_EQ("entry", doc["quoted key"][0].as()); +} + +TEST(NodeSpecTest, Ex8_19_CompactBlockMappings) { + Node doc = Load(ex8_19); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(1, doc[0].size()); + EXPECT_EQ("yellow", doc[0]["sun"].as()); + EXPECT_EQ(1, doc[1].size()); + std::map key; + key["earth"] = "blue"; + EXPECT_EQ(1, doc[1][key].size()); + EXPECT_EQ("white", doc[1][key]["moon"].as()); +} + +TEST(NodeSpecTest, Ex8_20_BlockNodeTypes) { + Node doc = Load(ex8_20); + EXPECT_EQ(3, doc.size()); + EXPECT_EQ("flow in block", doc[0].as()); + EXPECT_EQ("Block scalar\n", doc[1].as()); + EXPECT_EQ(1, doc[2].size()); + EXPECT_EQ("bar", doc[2]["foo"].as()); +} + +TEST(NodeSpecTest, DISABLED_Ex8_21_BlockScalarNodes) { + Node doc = Load(ex8_21); + EXPECT_EQ(2, doc.size()); + // NOTE: I believe this is a bug in the YAML spec - + // it should be "value\n" + EXPECT_EQ("value", doc["literal"].as()); + EXPECT_EQ("value", doc["folded"].as()); + EXPECT_EQ("!foo", doc["folded"].Tag()); +} + +TEST(NodeSpecTest, Ex8_22_BlockCollectionNodes) { + Node doc = Load(ex8_22); + EXPECT_EQ(2, doc.size()); + EXPECT_EQ(2, doc["sequence"].size()); + EXPECT_EQ("entry", doc["sequence"][0].as()); + EXPECT_EQ(1, doc["sequence"][1].size()); + EXPECT_EQ("nested", doc["sequence"][1][0].as()); + EXPECT_EQ(1, doc["mapping"].size()); + EXPECT_EQ("bar", doc["mapping"]["foo"].as()); +} +} +} diff --git a/test/node/node_test.cpp b/test/node/node_test.cpp new file mode 100644 index 0000000..430d142 --- /dev/null +++ b/test/node/node_test.cpp @@ -0,0 +1,396 @@ +#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 "gtest/gtest.h" + +namespace YAML { +namespace { +TEST(NodeTest, SimpleScalar) { + Node node = Node("Hello, World!"); + EXPECT_TRUE(node.IsScalar()); + EXPECT_EQ("Hello, World!", node.as()); +} + +TEST(NodeTest, IntScalar) { + Node node = Node(15); + EXPECT_TRUE(node.IsScalar()); + EXPECT_EQ(15, node.as()); +} + +TEST(NodeTest, SimpleAppendSequence) { + Node node; + node.push_back(10); + node.push_back("foo"); + node.push_back("monkey"); + EXPECT_TRUE(node.IsSequence()); + EXPECT_EQ(3, node.size()); + EXPECT_EQ(10, node[0].as()); + EXPECT_EQ("foo", node[1].as()); + EXPECT_EQ("monkey", node[2].as()); + EXPECT_TRUE(node.IsSequence()); +} + +TEST(NodeTest, SimpleAssignSequence) { + Node node; + node[0] = 10; + node[1] = "foo"; + node[2] = "monkey"; + EXPECT_TRUE(node.IsSequence()); + EXPECT_EQ(3, node.size()); + EXPECT_EQ(10, node[0].as()); + EXPECT_EQ("foo", node[1].as()); + EXPECT_EQ("monkey", node[2].as()); + EXPECT_TRUE(node.IsSequence()); +} + +TEST(NodeTest, SimpleMap) { + Node node; + node["key"] = "value"; + EXPECT_TRUE(node.IsMap()); + EXPECT_EQ("value", node["key"].as()); + EXPECT_EQ(1, node.size()); +} + +TEST(NodeTest, MapWithUndefinedValues) { + Node node; + node["key"] = "value"; + node["undefined"]; + EXPECT_TRUE(node.IsMap()); + EXPECT_EQ("value", node["key"].as()); + EXPECT_EQ(1, node.size()); + + node["undefined"] = "monkey"; + EXPECT_EQ("monkey", node["undefined"].as()); + EXPECT_EQ(2, node.size()); +} + +TEST(NodeTest, MapIteratorWithUndefinedValues) { + Node node; + node["key"] = "value"; + node["undefined"]; + + std::size_t count = 0; + for (const_iterator it = node.begin(); it != node.end(); ++it) + count++; + EXPECT_EQ(1, count); +} + +TEST(NodeTest, SimpleSubkeys) { + Node node; + node["device"]["udid"] = "12345"; + node["device"]["name"] = "iPhone"; + node["device"]["os"] = "4.0"; + node["username"] = "monkey"; + EXPECT_EQ("12345", node["device"]["udid"].as()); + EXPECT_EQ("iPhone", node["device"]["name"].as()); + EXPECT_EQ("4.0", node["device"]["os"].as()); + EXPECT_EQ("monkey", node["username"].as()); +} + +TEST(NodeTest, StdVector) { + std::vector primes; + primes.push_back(2); + primes.push_back(3); + primes.push_back(5); + primes.push_back(7); + primes.push_back(11); + primes.push_back(13); + + Node node; + node["primes"] = primes; + EXPECT_EQ(primes, node["primes"].as >()); +} + +TEST(NodeTest, StdList) { + std::list primes; + primes.push_back(2); + primes.push_back(3); + primes.push_back(5); + primes.push_back(7); + primes.push_back(11); + primes.push_back(13); + + Node node; + node["primes"] = primes; + EXPECT_EQ(primes, node["primes"].as >()); +} + +TEST(NodeTest, StdMap) { + std::map squares; + squares[0] = 0; + squares[1] = 1; + squares[2] = 4; + squares[3] = 9; + squares[4] = 16; + + Node node; + node["squares"] = squares; + std::map actualSquares = node["squares"].as >(); + EXPECT_EQ(squares, actualSquares); +} + +TEST(NodeTest, StdPair) { + std::pair p; + p.first = 5; + p.second = "five"; + + Node node; + node["pair"] = p; + std::pair actualP = + node["pair"].as >(); + EXPECT_EQ(p, actualP); +} + +TEST(NodeTest, SimpleAlias) { + Node node; + node["foo"] = "value"; + node["bar"] = node["foo"]; + EXPECT_EQ("value", node["foo"].as()); + EXPECT_EQ("value", node["bar"].as()); + EXPECT_EQ(node["bar"], node["foo"]); + EXPECT_EQ(2, node.size()); +} + +TEST(NodeTest, AliasAsKey) { + Node node; + node["foo"] = "value"; + Node value = node["foo"]; + node[value] = "foo"; + EXPECT_EQ("value", node["foo"].as()); + EXPECT_EQ("foo", node[value].as()); + EXPECT_EQ("foo", node["value"].as()); + EXPECT_EQ(2, node.size()); +} + +TEST(NodeTest, SelfReferenceSequence) { + Node node; + node[0] = node; + EXPECT_TRUE(node.IsSequence()); + EXPECT_EQ(1, node.size()); + EXPECT_EQ(node, node[0]); + EXPECT_EQ(node, node[0][0]); + EXPECT_EQ(node[0], node[0][0]); +} + +TEST(NodeTest, ValueSelfReferenceMap) { + Node node; + node["key"] = node; + EXPECT_TRUE(node.IsMap()); + EXPECT_EQ(1, node.size()); + EXPECT_EQ(node, node["key"]); + EXPECT_EQ(node, node["key"]["key"]); + EXPECT_EQ(node["key"], node["key"]["key"]); +} + +TEST(NodeTest, KeySelfReferenceMap) { + Node node; + node[node] = "value"; + EXPECT_TRUE(node.IsMap()); + EXPECT_EQ(1, node.size()); + EXPECT_EQ("value", node[node].as()); +} + +TEST(NodeTest, SelfReferenceMap) { + Node node; + node[node] = node; + EXPECT_TRUE(node.IsMap()); + EXPECT_EQ(1, node.size()); + EXPECT_EQ(node, node[node]); + EXPECT_EQ(node, node[node][node]); + EXPECT_EQ(node[node], node[node][node]); +} + +TEST(NodeTest, TempMapVariable) { + Node node; + Node tmp = node["key"]; + tmp = "value"; + EXPECT_TRUE(node.IsMap()); + EXPECT_EQ(1, node.size()); + EXPECT_EQ("value", node["key"].as()); +} + +TEST(NodeTest, TempMapVariableAlias) { + Node node; + Node tmp = node["key"]; + tmp = node["other"]; + node["other"] = "value"; + EXPECT_TRUE(node.IsMap()); + EXPECT_EQ(2, node.size()); + EXPECT_EQ("value", node["key"].as()); + EXPECT_EQ("value", node["other"].as()); + EXPECT_EQ(node["key"], node["other"]); +} + +TEST(NodeTest, Bool) { + Node node; + node[true] = false; + EXPECT_TRUE(node.IsMap()); + EXPECT_EQ(false, node[true].as()); +} + +TEST(NodeTest, AutoBoolConversion) { +#ifdef _MSC_VER +#pragma warning(disable : 4800) +#endif + Node node; + node["foo"] = "bar"; + EXPECT_TRUE(static_cast(node["foo"])); + EXPECT_TRUE(!node["monkey"]); + EXPECT_TRUE(!!node["foo"]); +} + +TEST(NodeTest, FloatingPrecision) { + const double x = 0.123456789; + Node node = Node(x); + EXPECT_EQ(x, node.as()); +} + +TEST(NodeTest, SpaceChar) { + Node node = Node(' '); + EXPECT_EQ(' ', node.as()); +} + +TEST(NodeTest, CloneNull) { + Node node; + Node clone = Clone(node); + EXPECT_EQ(NodeType::Null, clone.Type()); +} + +TEST(NodeTest, KeyNodeExitsScope) { + Node node; + { + Node temp("Hello, world"); + node[temp] = 0; + } + for (Node::const_iterator it = node.begin(); it != node.end(); ++it) { + (void)it; + } +} + +TEST(NodeTest, DefaultNodeStyle) { + Node node; + EXPECT_EQ(EmitterStyle::Default, node.Style()); +} + +class NodeEmitterTest : public ::testing::Test { + protected: + void ExpectOutput(const std::string& output, const Node& node) { + Emitter emitter; + emitter << node; + ASSERT_TRUE(emitter.good()); + EXPECT_EQ(output, emitter.c_str()); + } +}; + +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); + + ExpectOutput("[1.01, 2.01, 3.01]", node); +} + +TEST_F(NodeEmitterTest, NestFlowSeqNode) { + Node node, cell0, cell1; + + cell0.push_back(1.01); + cell0.push_back(2.01); + cell0.push_back(3.01); + + cell1.push_back(4.01); + cell1.push_back(5.01); + cell1.push_back(6.01); + + 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); +} + +TEST_F(NodeEmitterTest, MixBlockFlowSeqNode) { + Node node, cell0, cell1; + + cell0.SetStyle(EmitterStyle::Flow); + cell0.push_back(1.01); + cell0.push_back(2.01); + cell0.push_back(3.01); + + cell1.push_back(4.01); + cell1.push_back(5.01); + cell1.push_back(6.01); + + node.SetStyle(EmitterStyle::Block); + 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); +} + +TEST_F(NodeEmitterTest, NestBlockFlowMapListNode) { + Node node, mapNode, blockNode; + + node.push_back(1.01); + node.push_back(2.01); + node.push_back(3.01); + + mapNode.SetStyle(EmitterStyle::Flow); + mapNode["position"] = node; + + blockNode.push_back(1.01); + blockNode.push_back(mapNode); + + ExpectOutput("- 1.01\n- {position: [1.01, 2.01, 3.01]}", blockNode); +} + +TEST_F(NodeEmitterTest, NestBlockMixMapListNode) { + Node node, mapNode, blockNode; + + node.push_back(1.01); + node.push_back(2.01); + node.push_back(3.01); + + mapNode.SetStyle(EmitterStyle::Flow); + mapNode["position"] = node; + + blockNode["scalar"] = 1.01; + blockNode["object"] = mapNode; + + ExpectOutput("scalar: 1.01\nobject: {position: [1.01, 2.01, 3.01]}", + blockNode); +} + +TEST_F(NodeEmitterTest, NestBlockMapListNode) { + Node node, mapNode; + + node.push_back(1.01); + node.push_back(2.01); + node.push_back(3.01); + + mapNode.SetStyle(EmitterStyle::Block); + mapNode["position"] = node; + + ExpectOutput("position:\n - 1.01\n - 2.01\n - 3.01", mapNode); +} + +TEST_F(NodeEmitterTest, NestFlowMapListNode) { + Node node, mapNode; + + node.push_back(1.01); + node.push_back(2.01); + node.push_back(3.01); + + mapNode.SetStyle(EmitterStyle::Flow); + mapNode["position"] = node; + + ExpectOutput("{position: [1.01, 2.01, 3.01]}", mapNode); +} +} +} diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 7b35b79..8a69631 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -1,3 +1,7 @@ +add_sources(parse.cpp) +add_executable(parse parse.cpp) +target_link_libraries(parse yaml-cpp) + add_sources(sandbox.cpp) add_executable(sandbox sandbox.cpp) target_link_libraries(sandbox yaml-cpp) diff --git a/util/parse.cpp b/util/parse.cpp new file mode 100644 index 0000000..ed9db4b --- /dev/null +++ b/util/parse.cpp @@ -0,0 +1,61 @@ +#include +#include +#include + +#include "yaml-cpp/eventhandler.h" +#include "yaml-cpp/yaml.h" // IWYU pragma: keep + +struct Params { + bool hasFile; + std::string fileName; +}; + +Params ParseArgs(int argc, char** argv) { + Params p; + + std::vector args(argv + 1, argv + argc); + + return p; +} + +class NullEventHandler : public YAML::EventHandler { + public: + virtual void OnDocumentStart(const YAML::Mark&) {} + virtual void OnDocumentEnd() {} + + virtual void OnNull(const YAML::Mark&, YAML::anchor_t) {} + virtual void OnAlias(const YAML::Mark&, YAML::anchor_t) {} + virtual void OnScalar(const YAML::Mark&, const std::string&, YAML::anchor_t, + const std::string&) {} + + virtual void OnSequenceStart(const YAML::Mark&, const std::string&, + YAML::anchor_t) {} + virtual void OnSequenceEnd() {} + + virtual void OnMapStart(const YAML::Mark&, const std::string&, + YAML::anchor_t) {} + virtual void OnMapEnd() {} +}; + +void parse(std::istream& input) { + try { + YAML::Node doc = YAML::Load(input); + std::cout << doc << "\n"; + } catch (const YAML::Exception& e) { + std::cerr << e.what() << "\n"; + } +} + +int main(int argc, char** argv) { + Params p = ParseArgs(argc, argv); + + if (argc > 1) { + std::ifstream fin; + fin.open(argv[1]); + parse(fin); + } else { + parse(std::cin); + } + + return 0; +}