diff --git a/CMakeLists.txt b/CMakeLists.txt index cba9279..db16097 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() @@ -57,7 +57,7 @@ option(MSVC_STHREADED_RT "MSVC: Build with single-threaded static runtime libs ( 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) @@ -77,8 +77,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/dll.h b/include/yaml-cpp/dll.h index df04cc8..827e0f1 100644 --- a/include/yaml-cpp/dll.h +++ b/include/yaml-cpp/dll.h @@ -30,7 +30,7 @@ // #pragma message( "Defining YAML_CPP_API for DLL import" ) #define YAML_CPP_API __declspec(dllimport) #endif // yaml_cpp_EXPORTS -#else // YAML_CPP_DLL +#else // YAML_CPP_DLL #define YAML_CPP_API #endif // YAML_CPP_DLL 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..76ceee5 --- /dev/null +++ b/include/yaml-cpp/node/convert.h @@ -0,0 +1,275 @@ +#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 "yaml-cpp/binary.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/iterator.h" +#include "yaml-cpp/null.h" +#include +#include +#include +#include +#include + +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_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"); } + + 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..6c0d4b6 --- /dev/null +++ b/include/yaml-cpp/node/detail/iterator.h @@ -0,0 +1,66 @@ +#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..4584add --- /dev/null +++ b/include/yaml-cpp/node/detail/memory.h @@ -0,0 +1,39 @@ +#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 "yaml-cpp/node/ptr.h" +#include +#include + +namespace YAML { +namespace detail { +class memory { + public: + node& create_node(); + void merge(const memory& rhs); + + private: + typedef std::set Nodes; + Nodes m_nodes; +}; + +class 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..20be18c --- /dev/null +++ b/include/yaml-cpp/node/detail/node.h @@ -0,0 +1,147 @@ +#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/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(); } + + 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); + } + + // 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..50dd85a --- /dev/null +++ b/include/yaml-cpp/node/detail/node_data.h @@ -0,0 +1,114 @@ +#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 "yaml-cpp/dll.h" +#include "yaml-cpp/node/iterator.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/type.h" +#include +#include +#include +#include + +namespace YAML { +namespace detail { +class 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); + + 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; } + + // 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; + + // 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..6147f63 --- /dev/null +++ b/include/yaml-cpp/node/detail/node_iterator.h @@ -0,0 +1,157 @@ +#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) {} + explicit node_iterator_base(SeqIter seqIt) + : m_type(iterator_type::Sequence), m_seqIt(seqIt) {} + explicit node_iterator_base(MapIter mapIt, MapIter mapEnd) + : m_type(iterator_type::Map), 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..2b577a3 --- /dev/null +++ b/include/yaml-cpp/node/detail/node_ref.h @@ -0,0 +1,93 @@ +#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(); } + + 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); } + + // 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..2e4b98d --- /dev/null +++ b/include/yaml-cpp/node/emit.h @@ -0,0 +1,23 @@ +#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 + +namespace YAML { +class Emitter; +class Node; + +Emitter& operator<<(Emitter& out, const Node& node); +std::ostream& operator<<(std::ostream& out, const Node& node); + +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..425e37f --- /dev/null +++ b/include/yaml-cpp/node/impl.h @@ -0,0 +1,419 @@ +#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); +} + +// 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(); + 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(); + 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..75df149 --- /dev/null +++ b/include/yaml-cpp/node/node.h @@ -0,0 +1,128 @@ +#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 "yaml-cpp/dll.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/type.h" +#include "yaml-cpp/node/detail/iterator_fwd.h" +#include "yaml-cpp/node/detail/bool_type.h" +#include + +namespace YAML { +class 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); + + // 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; +}; + +bool operator==(const Node& lhs, const Node& rhs); + +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..4e3cb90 --- /dev/null +++ b/include/yaml-cpp/node/parse.h @@ -0,0 +1,28 @@ +#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 + +namespace YAML { +class Node; + +Node Load(const std::string& input); +Node Load(const char* input); +Node Load(std::istream& input); +Node LoadFile(const std::string& filename); + +std::vector LoadAll(const std::string& input); +std::vector LoadAll(const char* input); +std::vector LoadAll(std::istream& input); +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..da1bc11 --- /dev/null +++ b/include/yaml-cpp/node/type.h @@ -0,0 +1,22 @@ +#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 3bf3e0c..18389bb 100644 --- a/include/yaml-cpp/parser.h +++ b/include/yaml-cpp/parser.h @@ -14,10 +14,8 @@ namespace YAML { struct Directives; -struct Mark; struct Token; class EventHandler; -class Node; class Scanner; class YAML_CPP_API Parser : private noncopyable { diff --git a/include/yaml-cpp/yaml.h b/include/yaml-cpp/yaml.h index 9ca3bbf..4f09097 100644 --- a/include/yaml-cpp/yaml.h +++ b/include/yaml-cpp/yaml.h @@ -12,4 +12,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..50b15bc --- /dev/null +++ b/src/convert.cpp @@ -0,0 +1,73 @@ +#include "yaml-cpp/node/convert.h" +#include "yaml-cpp/node/impl.h" +#include + +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..1a02bf0 --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,24 @@ +#include "yaml-cpp/node/detail/memory.h" +#include "yaml-cpp/node/detail/node.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..db80be9 --- /dev/null +++ b/src/node_data.cpp @@ -0,0 +1,283 @@ +#include "yaml-cpp/node/detail/node_data.h" +#include "yaml-cpp/node/detail/memory.h" +#include "yaml-cpp/node/detail/node.h" +#include "yaml-cpp/exceptions.h" +#include + +namespace YAML { +namespace detail { +std::string node_data::empty_scalar; + +node_data::node_data() + : m_isDefined(false), m_type(NodeType::Null), 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_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..3ef2f7d --- /dev/null +++ b/src/nodebuilder.cpp @@ -0,0 +1,122 @@ +#include "nodebuilder.h" +#include "yaml-cpp/mark.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/impl.h" +#include + +namespace YAML { +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) { + detail::node& node = Push(anchor); + node.set_tag(tag); + node.set_type(NodeType::Sequence); +} + +void NodeBuilder::OnSequenceEnd() { Pop(); } + +void NodeBuilder::OnMapStart(const Mark& /* mark */, const std::string& tag, + anchor_t anchor) { + detail::node& node = Push(anchor); + node.set_type(NodeType::Map); + node.set_tag(tag); + 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..3600561 --- /dev/null +++ b/src/nodebuilder.h @@ -0,0 +1,60 @@ +#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 "yaml-cpp/eventhandler.h" +#include "yaml-cpp/node/ptr.h" +#include + +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); + virtual void OnSequenceEnd(); + + virtual void OnMapStart(const Mark& mark, const std::string& tag, + anchor_t anchor); + 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..1f07482 --- /dev/null +++ b/src/nodeevents.cpp @@ -0,0 +1,99 @@ +#include "nodeevents.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/impl.h" +#include "yaml-cpp/eventhandler.h" +#include "yaml-cpp/mark.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); + 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); + 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..98b26ba --- /dev/null +++ b/src/nodeevents.h @@ -0,0 +1,57 @@ +#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 "yaml-cpp/anchor.h" +#include "yaml-cpp/node/ptr.h" +#include +#include + +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..06292ae --- /dev/null +++ b/src/parse.cpp @@ -0,0 +1,67 @@ +#include "yaml-cpp/node/parse.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/node/impl.h" +#include "yaml-cpp/parser.h" +#include "nodebuilder.h" + +#include +#include + +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/src/stream.cpp b/src/stream.cpp index a79fc17..f285e0a 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -50,21 +50,21 @@ static bool s_introFinalState[] = {false, // uis_start false, // uis_utfbe_b1 false, // uis_utf32be_b2 false, // uis_utf32be_bom3 - true, // uis_utf32be - true, // uis_utf16be + true, // uis_utf32be + true, // uis_utf16be false, // uis_utf16be_bom1 false, // uis_utfle_bom1 false, // uis_utf16le_bom2 false, // uis_utf32le_bom3 - true, // uis_utf16le - true, // uis_utf32le + true, // uis_utf16le + true, // uis_utf32le false, // uis_utf8_imp false, // uis_utf16le_imp false, // uis_utf32le_imp3 false, // uis_utf8_bom1 false, // uis_utf8_bom2 - true, // uis_utf8 - true, // uis_error + true, // uis_utf8 + true, // uis_error }; static UtfIntroState s_introTransitions[][uictMax] = { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e885802..1f2df7c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,9 +3,9 @@ include_directories(gtest-1.7.0/include) file(GLOB test_headers [a-z_]*.h) file(GLOB test_sources [a-z_]*.cpp) -file(GLOB test_core_sources core/[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}) include_directories(${YAML_CPP_SOURCE_DIR}/test) diff --git a/test/emittertests.cpp b/test/emittertests.cpp index 0eb0f66..7dbb118 100644 --- a/test/emittertests.cpp +++ b/test/emittertests.cpp @@ -1,7 +1,6 @@ #include "tests.h" #include "handlermacros.h" #include "yaml-cpp/yaml.h" -#include "yaml-cpp/eventhandler.h" #include namespace Test { @@ -987,24 +986,6 @@ void InvalidAlias(YAML::Emitter& out, std::string& desiredError) { } namespace { -class NullEventHandler : public YAML::EventHandler { - 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 RunEmitterTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, int& passed, int& total) { YAML::Emitter out; @@ -1015,10 +996,7 @@ void RunEmitterTest(void (*test)(YAML::Emitter&, std::string&), if (output == desiredOutput) { try { - std::stringstream stream(output); - YAML::Parser parser; - NullEventHandler handler; - parser.HandleNextDocument(handler); + YAML::Node node = YAML::Load(output); passed++; } catch (const YAML::Exception& e) { diff --git a/test/new-api/nodetests.cpp b/test/new-api/nodetests.cpp new file mode 100644 index 0000000..8d77c6f --- /dev/null +++ b/test/new-api/nodetests.cpp @@ -0,0 +1,595 @@ +#include "nodetests.h" +#include "yaml-cpp/yaml.h" +#include + +#include + +#if BOOST_VERSION >= 103400 +#include +#endif + +namespace { +struct TEST { + TEST() : ok(false) {} + TEST(bool ok_) : ok(ok_) {} + TEST(const char* error_) : ok(false), error(error_) {} + TEST(const std::string& error_) : ok(false), error(error_) {} + + bool ok; + std::string error; +}; +} + +#define YAML_ASSERT(cond) \ + do { \ + if (!(cond)) \ + return " Assert failed: " #cond; \ + } while (false) + +#define YAML_ASSERT_THROWS(cond, exc) \ + do { \ + try { \ + (cond); \ + return " Expression did not throw: " #cond; \ + } \ + catch (const exc&) { \ + } \ + catch (const std::runtime_error& e) { \ + std::stringstream stream; \ + stream << " Expression threw runtime error ther than " #exc \ + ":\n " #cond "\n " << e.what(); \ + return stream.str(); \ + } \ + catch (...) { \ + return " Expression threw unknown exception, other than " #exc \ + ":\n " #cond; \ + } \ + } while (false) + +namespace Test { +namespace Node { +TEST SimpleScalar() { + YAML::Node node = YAML::Node("Hello, World!"); + YAML_ASSERT(node.IsScalar()); + YAML_ASSERT(node.as() == "Hello, World!"); + return true; +} + +TEST IntScalar() { + YAML::Node node = YAML::Node(15); + YAML_ASSERT(node.IsScalar()); + YAML_ASSERT(node.as() == 15); + return true; +} + +TEST SimpleAppendSequence() { + YAML::Node node; + node.push_back(10); + node.push_back("foo"); + node.push_back("monkey"); + YAML_ASSERT(node.IsSequence()); + YAML_ASSERT(node.size() == 3); + YAML_ASSERT(node[0].as() == 10); + YAML_ASSERT(node[1].as() == "foo"); + YAML_ASSERT(node[2].as() == "monkey"); + YAML_ASSERT(node.IsSequence()); + return true; +} + +TEST SimpleAssignSequence() { + YAML::Node node; + node[0] = 10; + node[1] = "foo"; + node[2] = "monkey"; + YAML_ASSERT(node.IsSequence()); + YAML_ASSERT(node.size() == 3); + YAML_ASSERT(node[0].as() == 10); + YAML_ASSERT(node[1].as() == "foo"); + YAML_ASSERT(node[2].as() == "monkey"); + YAML_ASSERT(node.IsSequence()); + return true; +} + +TEST SimpleMap() { + YAML::Node node; + node["key"] = "value"; + YAML_ASSERT(node.IsMap()); + YAML_ASSERT(node["key"].as() == "value"); + YAML_ASSERT(node.size() == 1); + return true; +} + +TEST MapWithUndefinedValues() { + YAML::Node node; + node["key"] = "value"; + node["undefined"]; + YAML_ASSERT(node.IsMap()); + YAML_ASSERT(node["key"].as() == "value"); + YAML_ASSERT(node.size() == 1); + + node["undefined"] = "monkey"; + YAML_ASSERT(node["undefined"].as() == "monkey"); + YAML_ASSERT(node.size() == 2); + + return true; +} + +TEST MapIteratorWithUndefinedValues() { + YAML::Node node; + node["key"] = "value"; + node["undefined"]; + + std::size_t count = 0; + for (YAML::const_iterator it = node.begin(); it != node.end(); ++it) + count++; + YAML_ASSERT(count == 1); + return true; +} + +TEST SimpleSubkeys() { + YAML::Node node; + node["device"]["udid"] = "12345"; + node["device"]["name"] = "iPhone"; + node["device"]["os"] = "4.0"; + node["username"] = "monkey"; + YAML_ASSERT(node["device"]["udid"].as() == "12345"); + YAML_ASSERT(node["device"]["name"].as() == "iPhone"); + YAML_ASSERT(node["device"]["os"].as() == "4.0"); + YAML_ASSERT(node["username"].as() == "monkey"); + return true; +} + +TEST 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); + + YAML::Node node; + node["primes"] = primes; + YAML_ASSERT(node["primes"].as >() == primes); + return true; +} + +TEST 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); + + YAML::Node node; + node["primes"] = primes; + YAML_ASSERT(node["primes"].as >() == primes); + return true; +} + +TEST StdMap() { + std::map squares; + squares[0] = 0; + squares[1] = 1; + squares[2] = 4; + squares[3] = 9; + squares[4] = 16; + + YAML::Node node; + node["squares"] = squares; + YAML_ASSERT((node["squares"].as >() == squares)); + return true; +} + +TEST StdPair() { + std::pair p; + p.first = 5; + p.second = "five"; + + YAML::Node node; + node["pair"] = p; + YAML_ASSERT((node["pair"].as >() == p)); + return true; +} + +TEST SimpleAlias() { + YAML::Node node; + node["foo"] = "value"; + node["bar"] = node["foo"]; + YAML_ASSERT(node["foo"].as() == "value"); + YAML_ASSERT(node["bar"].as() == "value"); + YAML_ASSERT(node["foo"] == node["bar"]); + YAML_ASSERT(node.size() == 2); + return true; +} + +TEST AliasAsKey() { + YAML::Node node; + node["foo"] = "value"; + YAML::Node value = node["foo"]; + node[value] = "foo"; + YAML_ASSERT(node["foo"].as() == "value"); + YAML_ASSERT(node[value].as() == "foo"); + YAML_ASSERT(node["value"].as() == "foo"); + YAML_ASSERT(node.size() == 2); + return true; +} + +TEST SelfReferenceSequence() { + YAML::Node node; + node[0] = node; + YAML_ASSERT(node.IsSequence()); + YAML_ASSERT(node.size() == 1); + YAML_ASSERT(node[0] == node); + YAML_ASSERT(node[0][0] == node); + YAML_ASSERT(node[0][0] == node[0]); + return true; +} + +TEST ValueSelfReferenceMap() { + YAML::Node node; + node["key"] = node; + YAML_ASSERT(node.IsMap()); + YAML_ASSERT(node.size() == 1); + YAML_ASSERT(node["key"] == node); + YAML_ASSERT(node["key"]["key"] == node); + YAML_ASSERT(node["key"]["key"] == node["key"]); + return true; +} + +TEST KeySelfReferenceMap() { + YAML::Node node; + node[node] = "value"; + YAML_ASSERT(node.IsMap()); + YAML_ASSERT(node.size() == 1); + YAML_ASSERT(node[node].as() == "value"); + return true; +} + +TEST SelfReferenceMap() { + YAML::Node node; + node[node] = node; + YAML_ASSERT(node.IsMap()); + YAML_ASSERT(node.size() == 1); + YAML_ASSERT(node[node] == node); + YAML_ASSERT(node[node][node] == node); + YAML_ASSERT(node[node][node] == node[node]); + return true; +} + +TEST TempMapVariable() { + YAML::Node node; + YAML::Node tmp = node["key"]; + tmp = "value"; + YAML_ASSERT(node.IsMap()); + YAML_ASSERT(node.size() == 1); + YAML_ASSERT(node["key"].as() == "value"); + return true; +} + +TEST TempMapVariableAlias() { + YAML::Node node; + YAML::Node tmp = node["key"]; + tmp = node["other"]; + node["other"] = "value"; + YAML_ASSERT(node.IsMap()); + YAML_ASSERT(node.size() == 2); + YAML_ASSERT(node["key"].as() == "value"); + YAML_ASSERT(node["other"].as() == "value"); + YAML_ASSERT(node["other"] == node["key"]); + return true; +} + +TEST Bool() { + YAML::Node node; + node[true] = false; + YAML_ASSERT(node.IsMap()); + YAML_ASSERT(node[true].as() == false); + return true; +} + +TEST AutoBoolConversion() { + YAML::Node node; + node["foo"] = "bar"; + YAML_ASSERT(static_cast(node["foo"])); + YAML_ASSERT(!node["monkey"]); + YAML_ASSERT(!!node["foo"]); + return true; +} + +TEST Reassign() { + YAML::Node node = YAML::Load("foo"); + node = YAML::Node(); + return true; +} + +TEST FallbackValues() { + YAML::Node node = YAML::Load("foo: bar\nx: 2"); + YAML_ASSERT(node["foo"].as() == "bar"); + YAML_ASSERT(node["foo"].as("hello") == "bar"); + YAML_ASSERT(node["baz"].as("hello") == "hello"); + YAML_ASSERT(node["x"].as() == 2); + YAML_ASSERT(node["x"].as(5) == 2); + YAML_ASSERT(node["y"].as(5) == 5); + return true; +} + +TEST NumericConversion() { + YAML::Node node = YAML::Load("[1.5, 1, .nan, .inf, -.inf, 0x15, 015]"); + YAML_ASSERT(node[0].as() == 1.5f); + YAML_ASSERT(node[0].as() == 1.5); + YAML_ASSERT_THROWS(node[0].as(), YAML::TypedBadConversion); + YAML_ASSERT(node[1].as() == 1); + YAML_ASSERT(node[1].as() == 1.0f); + YAML_ASSERT(node[2].as() != node[2].as()); + YAML_ASSERT(node[3].as() == std::numeric_limits::infinity()); + YAML_ASSERT(node[4].as() == -std::numeric_limits::infinity()); + YAML_ASSERT(node[5].as() == 21); + YAML_ASSERT(node[6].as() == 13); + return true; +} + +TEST Binary() { + YAML::Node node = YAML::Load( + "[!!binary \"SGVsbG8sIFdvcmxkIQ==\", !!binary " + "\"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieS" + "B0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG" + "x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbi" + "B0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZG" + "dlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS" + "4K\"]"); + YAML_ASSERT(node[0].as() == + YAML::Binary( + reinterpret_cast("Hello, World!"), 13)); + YAML_ASSERT(node[1].as() == + YAML::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)); + return true; +} + +TEST IterateSequence() { + YAML::Node node = YAML::Load("[1, 3, 5, 7]"); + int seq[] = {1, 3, 5, 7}; + int i = 0; + for (YAML::const_iterator it = node.begin(); it != node.end(); ++it) { + YAML_ASSERT(i < 4); + int x = seq[i++]; + YAML_ASSERT(it->as() == x); + } + YAML_ASSERT(i == 4); + return true; +} + +TEST IterateMap() { + YAML::Node node = YAML::Load("{a: A, b: B, c: C}"); + int i = 0; + for (YAML::const_iterator it = node.begin(); it != node.end(); ++it) { + YAML_ASSERT(i < 3); + i++; + YAML_ASSERT(it->first.as() + 'A' - 'a' == it->second.as()); + } + YAML_ASSERT(i == 3); + return true; +} + +#ifdef BOOST_FOREACH +TEST ForEach() { + YAML::Node node = YAML::Load("[1, 3, 5, 7]"); + int seq[] = {1, 3, 5, 7}; + int i = 0; + BOOST_FOREACH(const YAML::Node & item, node) { + int x = seq[i++]; + YAML_ASSERT(item.as() == x); + } + return true; +} + +TEST ForEachMap() { + YAML::Node node = YAML::Load("{a: A, b: B, c: C}"); + BOOST_FOREACH(const YAML::const_iterator::value_type & p, node) { + YAML_ASSERT(p.first.as() + 'A' - 'a' == p.second.as()); + } + return true; +} +#endif + +TEST CloneScalar() { + YAML::Node node = YAML::Load("!foo monkey"); + YAML::Node clone = Clone(node); + YAML_ASSERT(!(node == clone)); + YAML_ASSERT(node.as() == clone.as()); + YAML_ASSERT(node.Tag() == clone.Tag()); + return true; +} + +TEST CloneSeq() { + YAML::Node node = YAML::Load("[1, 3, 5, 7]"); + YAML::Node clone = Clone(node); + YAML_ASSERT(!(node == clone)); + YAML_ASSERT(clone.Type() == YAML::NodeType::Sequence); + YAML_ASSERT(node.size() == clone.size()); + for (std::size_t i = 0; i < node.size(); i++) + YAML_ASSERT(node[i].as() == clone[i].as()); + return true; +} + +TEST CloneMap() { + YAML::Node node = YAML::Load("{foo: bar}"); + YAML::Node clone = Clone(node); + YAML_ASSERT(!(node == clone)); + YAML_ASSERT(clone.Type() == YAML::NodeType::Map); + YAML_ASSERT(node.size() == clone.size()); + YAML_ASSERT(node["foo"].as() == clone["foo"].as()); + return true; +} + +TEST CloneAlias() { + YAML::Node node = YAML::Load("&foo [*foo]"); + YAML::Node clone = Clone(node); + YAML_ASSERT(!(node == clone)); + YAML_ASSERT(clone.Type() == YAML::NodeType::Sequence); + YAML_ASSERT(node.size() == clone.size()); + YAML_ASSERT(clone == clone[0]); + return true; +} + +TEST ForceInsertIntoMap() { + YAML::Node node; + node["a"] = "b"; + node.force_insert("x", "y"); + node.force_insert("a", 5); + YAML_ASSERT(node.size() == 3); + YAML_ASSERT(node.Type() == YAML::NodeType::Map); + bool ab = false; + bool a5 = false; + bool xy = false; + for (YAML::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; + } + YAML_ASSERT(ab); + YAML_ASSERT(a5); + YAML_ASSERT(xy); + return true; +} + +TEST ResetNode() { + YAML::Node node = YAML::Load("[1, 2, 3]"); + YAML_ASSERT(!node.IsNull()); + YAML::Node other = node; + node.reset(); + YAML_ASSERT(node.IsNull()); + YAML_ASSERT(!other.IsNull()); + node.reset(other); + YAML_ASSERT(!node.IsNull()); + YAML_ASSERT(other == node); + return true; +} + +TEST DereferenceIteratorError() { + YAML::Node node = YAML::Load("[{a: b}, 1, 2]"); + YAML_ASSERT_THROWS(node.begin()->first.as(), YAML::InvalidNode); + YAML_ASSERT((*node.begin()).IsMap() == true); + YAML_ASSERT(node.begin()->IsMap() == true); + YAML_ASSERT_THROWS((*node.begin()->begin()).IsDefined(), YAML::InvalidNode); + YAML_ASSERT_THROWS(node.begin()->begin()->IsDefined(), YAML::InvalidNode); + return true; +} + +TEST FloatingPrecision() { + const double x = 0.123456789; + YAML::Node node = YAML::Node(x); + YAML_ASSERT(node.as() == x); + return true; +} + +TEST EmitEmptyNode() { + YAML::Node node; + YAML::Emitter emitter; + emitter << node; + YAML_ASSERT(std::string(emitter.c_str()) == ""); + return true; +} + +TEST SpaceChar() { + YAML::Node node = YAML::Node(' '); + YAML_ASSERT(node.as() == ' '); + return true; +} +} + +void RunNodeTest(TEST (*test)(), const std::string& name, int& passed, + int& total) { + TEST ret; + try { + ret = test(); + } + catch (const std::exception& e) { + ret.ok = false; + ret.error = e.what(); + } + if (ret.ok) { + passed++; + } else { + std::cout << "Node test failed: " << name << "\n"; + if (ret.error != "") + std::cout << ret.error << "\n"; + } + total++; +} + +bool RunNodeTests() { + int passed = 0; + int total = 0; + + RunNodeTest(&Node::SimpleScalar, "simple scalar", passed, total); + RunNodeTest(&Node::IntScalar, "int scalar", passed, total); + RunNodeTest(&Node::SimpleAppendSequence, "simple append sequence", passed, + total); + RunNodeTest(&Node::SimpleAssignSequence, "simple assign sequence", passed, + total); + RunNodeTest(&Node::SimpleMap, "simple map", passed, total); + RunNodeTest(&Node::MapWithUndefinedValues, "map with undefined values", + passed, total); + RunNodeTest(&Node::MapIteratorWithUndefinedValues, + "map iterator with undefined values", passed, total); + RunNodeTest(&Node::SimpleSubkeys, "simple subkey", passed, total); + RunNodeTest(&Node::StdVector, "std::vector", passed, total); + RunNodeTest(&Node::StdList, "std::list", passed, total); + RunNodeTest(&Node::StdMap, "std::map", passed, total); + RunNodeTest(&Node::StdPair, "std::pair", passed, total); + RunNodeTest(&Node::SimpleAlias, "simple alias", passed, total); + RunNodeTest(&Node::AliasAsKey, "alias as key", passed, total); + RunNodeTest(&Node::SelfReferenceSequence, "self reference sequence", passed, + total); + RunNodeTest(&Node::ValueSelfReferenceMap, "value self reference map", passed, + total); + RunNodeTest(&Node::KeySelfReferenceMap, "key self reference map", passed, + total); + RunNodeTest(&Node::SelfReferenceMap, "self reference map", passed, total); + RunNodeTest(&Node::TempMapVariable, "temp map variable", passed, total); + RunNodeTest(&Node::TempMapVariableAlias, "temp map variable alias", passed, + total); + RunNodeTest(&Node::Bool, "bool", passed, total); + RunNodeTest(&Node::AutoBoolConversion, "auto bool conversion", passed, total); + RunNodeTest(&Node::Reassign, "reassign", passed, total); + RunNodeTest(&Node::FallbackValues, "fallback values", passed, total); + RunNodeTest(&Node::NumericConversion, "numeric conversion", passed, total); + RunNodeTest(&Node::Binary, "binary", passed, total); + RunNodeTest(&Node::IterateSequence, "iterate sequence", passed, total); + RunNodeTest(&Node::IterateMap, "iterate map", passed, total); +#ifdef BOOST_FOREACH + RunNodeTest(&Node::ForEach, "for each", passed, total); + RunNodeTest(&Node::ForEachMap, "for each map", passed, total); +#endif + RunNodeTest(&Node::CloneScalar, "clone scalar", passed, total); + RunNodeTest(&Node::CloneSeq, "clone seq", passed, total); + RunNodeTest(&Node::CloneMap, "clone map", passed, total); + RunNodeTest(&Node::CloneAlias, "clone alias", passed, total); + RunNodeTest(&Node::ForceInsertIntoMap, "force insert into map", passed, + total); + RunNodeTest(&Node::ResetNode, "reset node", passed, total); + RunNodeTest(&Node::DereferenceIteratorError, "dereference iterator error", + passed, total); + RunNodeTest(&Node::FloatingPrecision, "floating precision", passed, total); + RunNodeTest(&Node::EmitEmptyNode, "emit empty node", passed, total); + RunNodeTest(&Node::SpaceChar, "space char", passed, total); + + std::cout << "Node tests: " << passed << "/" << total << " passed\n"; + return passed == total; +} +} diff --git a/test/new-api/parsertests.cpp b/test/new-api/parsertests.cpp new file mode 100644 index 0000000..2662ab4 --- /dev/null +++ b/test/new-api/parsertests.cpp @@ -0,0 +1,5 @@ +#include "parsertests.h" + +namespace Test { +bool RunParserTests() { return true; } +} diff --git a/test/new-api/spectests.cpp b/test/new-api/spectests.cpp new file mode 100644 index 0000000..52b4e7e --- /dev/null +++ b/test/new-api/spectests.cpp @@ -0,0 +1,1425 @@ +#include "spectests.h" +#include "specexamples.h" +#include "yaml-cpp/yaml.h" +#include + +#define YAML_ASSERT(cond) \ + do { \ + if (!(cond)) \ + return " Assert failed: " #cond; \ + } while (false) + +namespace Test { +namespace Spec { +// 2.1 +TEST SeqScalars() { + YAML::Node doc = YAML::Load(ex2_1); + YAML_ASSERT(doc.IsSequence()); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].as() == "Mark McGwire"); + YAML_ASSERT(doc[1].as() == "Sammy Sosa"); + YAML_ASSERT(doc[2].as() == "Ken Griffey"); + return true; +} + +// 2.2 +TEST MappingScalarsToScalars() { + YAML::Node doc = YAML::Load(ex2_2); + YAML_ASSERT(doc.IsMap()); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["hr"].as() == "65"); + YAML_ASSERT(doc["avg"].as() == "0.278"); + YAML_ASSERT(doc["rbi"].as() == "147"); + return true; +} + +// 2.3 +TEST MappingScalarsToSequences() { + YAML::Node doc = YAML::Load(ex2_3); + YAML_ASSERT(doc.IsMap()); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["american"].size() == 3); + YAML_ASSERT(doc["american"][0].as() == "Boston Red Sox"); + YAML_ASSERT(doc["american"][1].as() == "Detroit Tigers"); + YAML_ASSERT(doc["american"][2].as() == "New York Yankees"); + YAML_ASSERT(doc["national"].size() == 3); + YAML_ASSERT(doc["national"][0].as() == "New York Mets"); + YAML_ASSERT(doc["national"][1].as() == "Chicago Cubs"); + YAML_ASSERT(doc["national"][2].as() == "Atlanta Braves"); + return true; +} + +// 2.4 +TEST SequenceOfMappings() { + YAML::Node doc = YAML::Load(ex2_4); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc[0].size() == 3); + YAML_ASSERT(doc[0]["name"].as() == "Mark McGwire"); + YAML_ASSERT(doc[0]["hr"].as() == "65"); + YAML_ASSERT(doc[0]["avg"].as() == "0.278"); + YAML_ASSERT(doc[1].size() == 3); + YAML_ASSERT(doc[1]["name"].as() == "Sammy Sosa"); + YAML_ASSERT(doc[1]["hr"].as() == "63"); + YAML_ASSERT(doc[1]["avg"].as() == "0.288"); + return true; +} + +// 2.5 +TEST SequenceOfSequences() { + YAML::Node doc = YAML::Load(ex2_5); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].size() == 3); + YAML_ASSERT(doc[0][0].as() == "name"); + YAML_ASSERT(doc[0][1].as() == "hr"); + YAML_ASSERT(doc[0][2].as() == "avg"); + YAML_ASSERT(doc[1].size() == 3); + YAML_ASSERT(doc[1][0].as() == "Mark McGwire"); + YAML_ASSERT(doc[1][1].as() == "65"); + YAML_ASSERT(doc[1][2].as() == "0.278"); + YAML_ASSERT(doc[2].size() == 3); + YAML_ASSERT(doc[2][0].as() == "Sammy Sosa"); + YAML_ASSERT(doc[2][1].as() == "63"); + YAML_ASSERT(doc[2][2].as() == "0.288"); + return true; +} + +// 2.6 +TEST MappingOfMappings() { + YAML::Node doc = YAML::Load(ex2_6); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["Mark McGwire"].size() == 2); + YAML_ASSERT(doc["Mark McGwire"]["hr"].as() == "65"); + YAML_ASSERT(doc["Mark McGwire"]["avg"].as() == "0.278"); + YAML_ASSERT(doc["Sammy Sosa"].size() == 2); + YAML_ASSERT(doc["Sammy Sosa"]["hr"].as() == "63"); + YAML_ASSERT(doc["Sammy Sosa"]["avg"].as() == "0.288"); + return true; +} + +// 2.7 +TEST TwoDocumentsInAStream() { + std::vector docs = YAML::LoadAll(ex2_7); + YAML_ASSERT(docs.size() == 2); + + { + YAML::Node doc = docs[0]; + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].as() == "Mark McGwire"); + YAML_ASSERT(doc[1].as() == "Sammy Sosa"); + YAML_ASSERT(doc[2].as() == "Ken Griffey"); + } + + { + YAML::Node doc = docs[1]; + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc[0].as() == "Chicago Cubs"); + YAML_ASSERT(doc[1].as() == "St Louis Cardinals"); + } + return true; +} + +// 2.8 +TEST PlayByPlayFeed() { + std::vector docs = YAML::LoadAll(ex2_8); + YAML_ASSERT(docs.size() == 2); + + { + YAML::Node doc = docs[0]; + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["time"].as() == "20:03:20"); + YAML_ASSERT(doc["player"].as() == "Sammy Sosa"); + YAML_ASSERT(doc["action"].as() == "strike (miss)"); + } + + { + YAML::Node doc = docs[1]; + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["time"].as() == "20:03:47"); + YAML_ASSERT(doc["player"].as() == "Sammy Sosa"); + YAML_ASSERT(doc["action"].as() == "grand slam"); + } + return true; +} + +// 2.9 +TEST SingleDocumentWithTwoComments() { + YAML::Node doc = YAML::Load(ex2_9); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["hr"].size() == 2); + YAML_ASSERT(doc["hr"][0].as() == "Mark McGwire"); + YAML_ASSERT(doc["hr"][1].as() == "Sammy Sosa"); + YAML_ASSERT(doc["rbi"].size() == 2); + YAML_ASSERT(doc["rbi"][0].as() == "Sammy Sosa"); + YAML_ASSERT(doc["rbi"][1].as() == "Ken Griffey"); + return true; +} + +// 2.10 +TEST SimpleAnchor() { + YAML::Node doc = YAML::Load(ex2_10); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["hr"].size() == 2); + YAML_ASSERT(doc["hr"][0].as() == "Mark McGwire"); + YAML_ASSERT(doc["hr"][1].as() == "Sammy Sosa"); + YAML_ASSERT(doc["rbi"].size() == 2); + YAML_ASSERT(doc["rbi"][0].as() == "Sammy Sosa"); + YAML_ASSERT(doc["rbi"][1].as() == "Ken Griffey"); + return true; +} + +// 2.11 +TEST MappingBetweenSequences() { + YAML::Node doc = YAML::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"); + + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc[tigers_cubs].size() == 1); + YAML_ASSERT(doc[tigers_cubs][0].as() == "2001-07-23"); + YAML_ASSERT(doc[yankees_braves].size() == 3); + YAML_ASSERT(doc[yankees_braves][0].as() == "2001-07-02"); + YAML_ASSERT(doc[yankees_braves][1].as() == "2001-08-12"); + YAML_ASSERT(doc[yankees_braves][2].as() == "2001-08-14"); + return true; +} + +// 2.12 +TEST CompactNestedMapping() { + YAML::Node doc = YAML::Load(ex2_12); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].size() == 2); + YAML_ASSERT(doc[0]["item"].as() == "Super Hoop"); + YAML_ASSERT(doc[0]["quantity"].as() == 1); + YAML_ASSERT(doc[1].size() == 2); + YAML_ASSERT(doc[1]["item"].as() == "Basketball"); + YAML_ASSERT(doc[1]["quantity"].as() == 4); + YAML_ASSERT(doc[2].size() == 2); + YAML_ASSERT(doc[2]["item"].as() == "Big Shoes"); + YAML_ASSERT(doc[2]["quantity"].as() == 1); + return true; +} + +// 2.13 +TEST InLiteralsNewlinesArePreserved() { + YAML::Node doc = YAML::Load(ex2_13); + YAML_ASSERT(doc.as() == + "\\//||\\/||\n" + "// || ||__"); + return true; +} + +// 2.14 +TEST InFoldedScalarsNewlinesBecomeSpaces() { + YAML::Node doc = YAML::Load(ex2_14); + YAML_ASSERT(doc.as() == + "Mark McGwire's year was crippled by a knee injury."); + return true; +} + +// 2.15 +TEST FoldedNewlinesArePreservedForMoreIndentedAndBlankLines() { + YAML::Node doc = YAML::Load(ex2_15); + YAML_ASSERT(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!"); + return true; +} + +// 2.16 +TEST IndentationDeterminesScope() { + YAML::Node doc = YAML::Load(ex2_16); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["name"].as() == "Mark McGwire"); + YAML_ASSERT(doc["accomplishment"].as() == + "Mark set a major league home run record in 1998.\n"); + YAML_ASSERT(doc["stats"].as() == + "65 Home Runs\n0.278 Batting Average\n"); + return true; +} + +// 2.17 +TEST QuotedScalars() { + YAML::Node doc = YAML::Load(ex2_17); + YAML_ASSERT(doc.size() == 6); + YAML_ASSERT(doc["unicode"].as() == "Sosa did fine.\xe2\x98\xba"); + YAML_ASSERT(doc["control"].as() == "\b1998\t1999\t2000\n"); + YAML_ASSERT(doc["hex esc"].as() == "\x0d\x0a is \r\n"); + YAML_ASSERT(doc["single"].as() == "\"Howdy!\" he cried."); + YAML_ASSERT(doc["quoted"].as() == " # Not a 'comment'."); + YAML_ASSERT(doc["tie-fighter"].as() == "|\\-*-/|"); + return true; +} + +// 2.18 +TEST MultiLineFlowScalars() { + YAML::Node doc = YAML::Load(ex2_18); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["plain"].as() == + "This unquoted scalar spans many lines."); + YAML_ASSERT(doc["quoted"].as() == + "So does this quoted scalar.\n"); + return true; +} + +// TODO: 2.19 - 2.22 schema tags + +// 2.23 +TEST VariousExplicitTags() { + YAML::Node doc = YAML::Load(ex2_23); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["not-date"].Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(doc["not-date"].as() == "2002-04-28"); + YAML_ASSERT(doc["picture"].Tag() == "tag:yaml.org,2002:binary"); + YAML_ASSERT(doc["picture"].as() == + "R0lGODlhDAAMAIQAAP//9/X\n" + "17unp5WZmZgAAAOfn515eXv\n" + "Pz7Y6OjuDg4J+fn5OTk6enp\n" + "56enmleECcgggoBADs=\n"); + YAML_ASSERT(doc["application specific tag"].Tag() == "!something"); + YAML_ASSERT(doc["application specific tag"].as() == + "The semantics of the tag\n" + "above may be different for\n" + "different documents."); + return true; +} + +// 2.24 +TEST GlobalTags() { + YAML::Node doc = YAML::Load(ex2_24); + YAML_ASSERT(doc.Tag() == "tag:clarkevans.com,2002:shape"); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].Tag() == "tag:clarkevans.com,2002:circle"); + YAML_ASSERT(doc[0].size() == 2); + YAML_ASSERT(doc[0]["center"].size() == 2); + YAML_ASSERT(doc[0]["center"]["x"].as() == 73); + YAML_ASSERT(doc[0]["center"]["y"].as() == 129); + YAML_ASSERT(doc[0]["radius"].as() == 7); + YAML_ASSERT(doc[1].Tag() == "tag:clarkevans.com,2002:line"); + YAML_ASSERT(doc[1].size() == 2); + YAML_ASSERT(doc[1]["start"].size() == 2); + YAML_ASSERT(doc[1]["start"]["x"].as() == 73); + YAML_ASSERT(doc[1]["start"]["y"].as() == 129); + YAML_ASSERT(doc[1]["finish"].size() == 2); + YAML_ASSERT(doc[1]["finish"]["x"].as() == 89); + YAML_ASSERT(doc[1]["finish"]["y"].as() == 102); + YAML_ASSERT(doc[2].Tag() == "tag:clarkevans.com,2002:label"); + YAML_ASSERT(doc[2].size() == 3); + YAML_ASSERT(doc[2]["start"].size() == 2); + YAML_ASSERT(doc[2]["start"]["x"].as() == 73); + YAML_ASSERT(doc[2]["start"]["y"].as() == 129); + YAML_ASSERT(doc[2]["color"].as() == "0xFFEEBB"); + YAML_ASSERT(doc[2]["text"].as() == "Pretty vector drawing."); + return true; +} + +// 2.25 +TEST UnorderedSets() { + YAML::Node doc = YAML::Load(ex2_25); + YAML_ASSERT(doc.Tag() == "tag:yaml.org,2002:set"); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["Mark McGwire"].IsNull()); + YAML_ASSERT(doc["Sammy Sosa"].IsNull()); + YAML_ASSERT(doc["Ken Griffey"].IsNull()); + return true; +} + +// 2.26 +TEST OrderedMappings() { + YAML::Node doc = YAML::Load(ex2_26); + YAML_ASSERT(doc.Tag() == "tag:yaml.org,2002:omap"); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].size() == 1); + YAML_ASSERT(doc[0]["Mark McGwire"].as() == 65); + YAML_ASSERT(doc[1].size() == 1); + YAML_ASSERT(doc[1]["Sammy Sosa"].as() == 63); + YAML_ASSERT(doc[2].size() == 1); + YAML_ASSERT(doc[2]["Ken Griffey"].as() == 58); + return true; +} + +// 2.27 +TEST Invoice() { + YAML::Node doc = YAML::Load(ex2_27); + YAML_ASSERT(doc.Tag() == "tag:clarkevans.com,2002:invoice"); + YAML_ASSERT(doc.size() == 8); + YAML_ASSERT(doc["invoice"].as() == 34843); + YAML_ASSERT(doc["date"].as() == "2001-01-23"); + YAML_ASSERT(doc["bill-to"].size() == 3); + YAML_ASSERT(doc["bill-to"]["given"].as() == "Chris"); + YAML_ASSERT(doc["bill-to"]["family"].as() == "Dumars"); + YAML_ASSERT(doc["bill-to"]["address"].size() == 4); + YAML_ASSERT(doc["bill-to"]["address"]["lines"].as() == + "458 Walkman Dr.\nSuite #292\n"); + YAML_ASSERT(doc["bill-to"]["address"]["city"].as() == + "Royal Oak"); + YAML_ASSERT(doc["bill-to"]["address"]["state"].as() == "MI"); + YAML_ASSERT(doc["bill-to"]["address"]["postal"].as() == "48046"); + YAML_ASSERT(doc["ship-to"].size() == 3); + YAML_ASSERT(doc["ship-to"]["given"].as() == "Chris"); + YAML_ASSERT(doc["ship-to"]["family"].as() == "Dumars"); + YAML_ASSERT(doc["ship-to"]["address"].size() == 4); + YAML_ASSERT(doc["ship-to"]["address"]["lines"].as() == + "458 Walkman Dr.\nSuite #292\n"); + YAML_ASSERT(doc["ship-to"]["address"]["city"].as() == + "Royal Oak"); + YAML_ASSERT(doc["ship-to"]["address"]["state"].as() == "MI"); + YAML_ASSERT(doc["ship-to"]["address"]["postal"].as() == "48046"); + YAML_ASSERT(doc["product"].size() == 2); + YAML_ASSERT(doc["product"][0].size() == 4); + YAML_ASSERT(doc["product"][0]["sku"].as() == "BL394D"); + YAML_ASSERT(doc["product"][0]["quantity"].as() == 4); + YAML_ASSERT(doc["product"][0]["description"].as() == + "Basketball"); + YAML_ASSERT(doc["product"][0]["price"].as() == "450.00"); + YAML_ASSERT(doc["product"][1].size() == 4); + YAML_ASSERT(doc["product"][1]["sku"].as() == "BL4438H"); + YAML_ASSERT(doc["product"][1]["quantity"].as() == 1); + YAML_ASSERT(doc["product"][1]["description"].as() == + "Super Hoop"); + YAML_ASSERT(doc["product"][1]["price"].as() == "2392.00"); + YAML_ASSERT(doc["tax"].as() == "251.42"); + YAML_ASSERT(doc["total"].as() == "4443.52"); + YAML_ASSERT( + doc["comments"].as() == + "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338."); + return true; +} + +// 2.28 +TEST LogFile() { + std::vector docs = YAML::LoadAll(ex2_28); + YAML_ASSERT(docs.size() == 3); + + { + YAML::Node doc = docs[0]; + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["Time"].as() == "2001-11-23 15:01:42 -5"); + YAML_ASSERT(doc["User"].as() == "ed"); + YAML_ASSERT(doc["Warning"].as() == + "This is an error message for the log file"); + } + + { + YAML::Node doc = docs[1]; + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["Time"].as() == "2001-11-23 15:02:31 -5"); + YAML_ASSERT(doc["User"].as() == "ed"); + YAML_ASSERT(doc["Warning"].as() == + "A slightly different error message."); + } + + { + YAML::Node doc = docs[2]; + YAML_ASSERT(doc.size() == 4); + YAML_ASSERT(doc["Date"].as() == "2001-11-23 15:03:17 -5"); + YAML_ASSERT(doc["User"].as() == "ed"); + YAML_ASSERT(doc["Fatal"].as() == "Unknown variable \"bar\""); + YAML_ASSERT(doc["Stack"].size() == 2); + YAML_ASSERT(doc["Stack"][0].size() == 3); + YAML_ASSERT(doc["Stack"][0]["file"].as() == "TopClass.py"); + YAML_ASSERT(doc["Stack"][0]["line"].as() == "23"); + YAML_ASSERT(doc["Stack"][0]["code"].as() == + "x = MoreObject(\"345\\n\")\n"); + YAML_ASSERT(doc["Stack"][1].size() == 3); + YAML_ASSERT(doc["Stack"][1]["file"].as() == "MoreClass.py"); + YAML_ASSERT(doc["Stack"][1]["line"].as() == "58"); + YAML_ASSERT(doc["Stack"][1]["code"].as() == "foo = bar"); + } + return true; +} + +// TODO: 5.1 - 5.2 BOM + +// 5.3 +TEST BlockStructureIndicators() { + YAML::Node doc = YAML::Load(ex5_3); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["sequence"].size() == 2); + YAML_ASSERT(doc["sequence"][0].as() == "one"); + YAML_ASSERT(doc["sequence"][1].as() == "two"); + YAML_ASSERT(doc["mapping"].size() == 2); + YAML_ASSERT(doc["mapping"]["sky"].as() == "blue"); + YAML_ASSERT(doc["mapping"]["sea"].as() == "green"); + return true; +} + +// 5.4 +TEST FlowStructureIndicators() { + YAML::Node doc = YAML::Load(ex5_4); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["sequence"].size() == 2); + YAML_ASSERT(doc["sequence"][0].as() == "one"); + YAML_ASSERT(doc["sequence"][1].as() == "two"); + YAML_ASSERT(doc["mapping"].size() == 2); + YAML_ASSERT(doc["mapping"]["sky"].as() == "blue"); + YAML_ASSERT(doc["mapping"]["sea"].as() == "green"); + return true; +} + +// 5.5 +TEST CommentIndicator() { + YAML::Node doc = YAML::Load(ex5_5); + YAML_ASSERT(doc.IsNull()); + return true; +} + +// 5.6 +TEST NodePropertyIndicators() { + YAML::Node doc = YAML::Load(ex5_6); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["anchored"].as() == + "value"); // TODO: assert tag + YAML_ASSERT(doc["alias"].as() == "value"); + return true; +} + +// 5.7 +TEST BlockScalarIndicators() { + YAML::Node doc = YAML::Load(ex5_7); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["literal"].as() == "some\ntext\n"); + YAML_ASSERT(doc["folded"].as() == "some text\n"); + return true; +} + +// 5.8 +TEST QuotedScalarIndicators() { + YAML::Node doc = YAML::Load(ex5_8); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["single"].as() == "text"); + YAML_ASSERT(doc["double"].as() == "text"); + return true; +} + +// TODO: 5.9 directive +// TODO: 5.10 reserved indicator + +// 5.11 +TEST LineBreakCharacters() { + YAML::Node doc = YAML::Load(ex5_11); + YAML_ASSERT(doc.as() == + "Line break (no glyph)\nLine break (glyphed)\n"); + return true; +} + +// 5.12 +TEST TabsAndSpaces() { + YAML::Node doc = YAML::Load(ex5_12); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["quoted"].as() == "Quoted\t"); + YAML_ASSERT(doc["block"].as() == + "void main() {\n" + "\tprintf(\"Hello, world!\\n\");\n" + "}"); + return true; +} + +// 5.13 +TEST EscapedCharacters() { + YAML::Node doc = YAML::Load(ex5_13); + YAML_ASSERT(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"); + return true; +} + +// 5.14 +TEST InvalidEscapedCharacters() { + try { + YAML::Load(ex5_14); + } + catch (const YAML::ParserException& e) { + YAML_ASSERT(e.msg == std::string(YAML::ErrorMsg::INVALID_ESCAPE) + "c"); + return true; + } + + return false; +} + +// 6.1 +TEST IndentationSpaces() { + YAML::Node doc = YAML::Load(ex6_1); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["Not indented"].size() == 2); + YAML_ASSERT(doc["Not indented"]["By one space"].as() == + "By four\n spaces\n"); + YAML_ASSERT(doc["Not indented"]["Flow style"].size() == 3); + YAML_ASSERT(doc["Not indented"]["Flow style"][0].as() == + "By two"); + YAML_ASSERT(doc["Not indented"]["Flow style"][1].as() == + "Also by two"); + YAML_ASSERT(doc["Not indented"]["Flow style"][2].as() == + "Still by two"); + return true; +} + +// 6.2 +TEST IndentationIndicators() { + YAML::Node doc = YAML::Load(ex6_2); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["a"].size() == 2); + YAML_ASSERT(doc["a"][0].as() == "b"); + YAML_ASSERT(doc["a"][1].size() == 2); + YAML_ASSERT(doc["a"][1][0].as() == "c"); + YAML_ASSERT(doc["a"][1][1].as() == "d"); + return true; +} + +// 6.3 +TEST SeparationSpaces() { + YAML::Node doc = YAML::Load(ex6_3); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc[0].size() == 1); + YAML_ASSERT(doc[0]["foo"].as() == "bar"); + YAML_ASSERT(doc[1].size() == 2); + YAML_ASSERT(doc[1][0].as() == "baz"); + YAML_ASSERT(doc[1][1].as() == "baz"); + return true; +} + +// 6.4 +TEST LinePrefixes() { + YAML::Node doc = YAML::Load(ex6_4); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["plain"].as() == "text lines"); + YAML_ASSERT(doc["quoted"].as() == "text lines"); + YAML_ASSERT(doc["block"].as() == "text\n \tlines\n"); + return true; +} + +// 6.5 +TEST EmptyLines() { + YAML::Node doc = YAML::Load(ex6_5); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["Folding"].as() == "Empty line\nas a line feed"); + YAML_ASSERT(doc["Chomping"].as() == "Clipped empty lines\n"); + return true; +} + +// 6.6 +TEST LineFolding() { + YAML::Node doc = YAML::Load(ex6_6); + YAML_ASSERT(doc.as() == "trimmed\n\n\nas space"); + return true; +} + +// 6.7 +TEST BlockFolding() { + YAML::Node doc = YAML::Load(ex6_7); + YAML_ASSERT(doc.as() == "foo \n\n\t bar\n\nbaz\n"); + return true; +} + +// 6.8 +TEST FlowFolding() { + YAML::Node doc = YAML::Load(ex6_8); + YAML_ASSERT(doc.as() == " foo\nbar\nbaz "); + return true; +} + +// 6.9 +TEST SeparatedComment() { + YAML::Node doc = YAML::Load(ex6_9); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["key"].as() == "value"); + return true; +} + +// 6.10 +TEST CommentLines() { + YAML::Node doc = YAML::Load(ex6_10); + YAML_ASSERT(doc.IsNull()); + return true; +} + +// 6.11 +TEST MultiLineComments() { + YAML::Node doc = YAML::Load(ex6_11); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["key"].as() == "value"); + return true; +} + +// 6.12 +TEST SeparationSpacesII() { + YAML::Node doc = YAML::Load(ex6_12); + + std::map sammy; + sammy["first"] = "Sammy"; + sammy["last"] = "Sosa"; + + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc[sammy].size() == 2); + YAML_ASSERT(doc[sammy]["hr"].as() == 65); + YAML_ASSERT(doc[sammy]["avg"].as() == "0.278"); + return true; +} + +// 6.13 +TEST ReservedDirectives() { + YAML::Node doc = YAML::Load(ex6_13); + YAML_ASSERT(doc.as() == "foo"); + return true; +} + +// 6.14 +TEST YAMLDirective() { + YAML::Node doc = YAML::Load(ex6_14); + YAML_ASSERT(doc.as() == "foo"); + return true; +} + +// 6.15 +TEST InvalidRepeatedYAMLDirective() { + try { + YAML::Load(ex6_15); + } + catch (const YAML::ParserException& e) { + YAML_ASSERT(e.msg == YAML::ErrorMsg::REPEATED_YAML_DIRECTIVE); + return true; + } + + return " No exception was thrown"; +} + +// 6.16 +TEST TagDirective() { + YAML::Node doc = YAML::Load(ex6_16); + YAML_ASSERT(doc.Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(doc.as() == "foo"); + return true; +} + +// 6.17 +TEST InvalidRepeatedTagDirective() { + try { + YAML::Load(ex6_17); + } + catch (const YAML::ParserException& e) { + if (e.msg == YAML::ErrorMsg::REPEATED_TAG_DIRECTIVE) + return true; + + throw; + } + + return " No exception was thrown"; +} + +// 6.18 +TEST PrimaryTagHandle() { + std::vector docs = YAML::LoadAll(ex6_18); + YAML_ASSERT(docs.size() == 2); + + { + YAML::Node doc = docs[0]; + YAML_ASSERT(doc.Tag() == "!foo"); + YAML_ASSERT(doc.as() == "bar"); + } + + { + YAML::Node doc = docs[1]; + YAML_ASSERT(doc.Tag() == "tag:example.com,2000:app/foo"); + YAML_ASSERT(doc.as() == "bar"); + } + return true; +} + +// 6.19 +TEST SecondaryTagHandle() { + YAML::Node doc = YAML::Load(ex6_19); + YAML_ASSERT(doc.Tag() == "tag:example.com,2000:app/int"); + YAML_ASSERT(doc.as() == "1 - 3"); + return true; +} + +// 6.20 +TEST TagHandles() { + YAML::Node doc = YAML::Load(ex6_20); + YAML_ASSERT(doc.Tag() == "tag:example.com,2000:app/foo"); + YAML_ASSERT(doc.as() == "bar"); + return true; +} + +// 6.21 +TEST LocalTagPrefix() { + std::vector docs = YAML::LoadAll(ex6_21); + YAML_ASSERT(docs.size() == 2); + + { + YAML::Node doc = docs[0]; + YAML_ASSERT(doc.Tag() == "!my-light"); + YAML_ASSERT(doc.as() == "fluorescent"); + } + + { + YAML::Node doc = docs[1]; + YAML_ASSERT(doc.Tag() == "!my-light"); + YAML_ASSERT(doc.as() == "green"); + } + return true; +} + +// 6.22 +TEST GlobalTagPrefix() { + YAML::Node doc = YAML::Load(ex6_22); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc[0].Tag() == "tag:example.com,2000:app/foo"); + YAML_ASSERT(doc[0].as() == "bar"); + return true; +} + +// 6.23 +TEST NodeProperties() { + YAML::Node doc = YAML::Load(ex6_23); + YAML_ASSERT(doc.size() == 2); + for (YAML::const_iterator it = doc.begin(); it != doc.end(); ++it) { + if (it->first.as() == "foo") { + YAML_ASSERT(it->first.Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(it->second.Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(it->second.as() == "bar"); + } else if (it->first.as() == "baz") { + YAML_ASSERT(it->second.as() == "foo"); + } else + return " unknown key"; + } + + return true; +} + +// 6.24 +TEST VerbatimTags() { + YAML::Node doc = YAML::Load(ex6_24); + YAML_ASSERT(doc.size() == 1); + for (YAML::const_iterator it = doc.begin(); it != doc.end(); ++it) { + YAML_ASSERT(it->first.Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(it->first.as() == "foo"); + YAML_ASSERT(it->second.Tag() == "!bar"); + YAML_ASSERT(it->second.as() == "baz"); + } + return true; +} + +// 6.25 +TEST InvalidVerbatimTags() { + YAML::Node doc = YAML::Load(ex6_25); + return " not implemented yet"; // TODO: check tags (but we probably will say + // these are valid, I think) +} + +// 6.26 +TEST TagShorthands() { + YAML::Node doc = YAML::Load(ex6_26); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].Tag() == "!local"); + YAML_ASSERT(doc[0].as() == "foo"); + YAML_ASSERT(doc[1].Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(doc[1].as() == "bar"); + YAML_ASSERT(doc[2].Tag() == "tag:example.com,2000:app/tag%21"); + YAML_ASSERT(doc[2].as() == "baz"); + return true; +} + +// 6.27 +TEST InvalidTagShorthands() { + bool threw = false; + try { + YAML::Load(ex6_27a); + } + catch (const YAML::ParserException& e) { + threw = true; + if (e.msg != YAML::ErrorMsg::TAG_WITH_NO_SUFFIX) + throw; + } + + if (!threw) + return " No exception was thrown for a tag with no suffix"; + + YAML::Load( + ex6_27b); // TODO: should we reject this one (since !h! is not declared)? + return " not implemented yet"; +} + +// 6.28 +TEST NonSpecificTags() { + YAML::Node doc = YAML::Load(ex6_28); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].as() == "12"); // TODO: check tags. How? + YAML_ASSERT(doc[1].as() == 12); + YAML_ASSERT(doc[2].as() == "12"); + return true; +} + +// 6.29 +TEST NodeAnchors() { + YAML::Node doc = YAML::Load(ex6_29); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["First occurrence"].as() == "Value"); + YAML_ASSERT(doc["Second occurrence"].as() == "Value"); + return true; +} + +// 7.1 +TEST AliasNodes() { + YAML::Node doc = YAML::Load(ex7_1); + YAML_ASSERT(doc.size() == 4); + YAML_ASSERT(doc["First occurrence"].as() == "Foo"); + YAML_ASSERT(doc["Second occurrence"].as() == "Foo"); + YAML_ASSERT(doc["Override anchor"].as() == "Bar"); + YAML_ASSERT(doc["Reuse anchor"].as() == "Bar"); + return true; +} + +// 7.2 +TEST EmptyNodes() { + YAML::Node doc = YAML::Load(ex7_2); + YAML_ASSERT(doc.size() == 2); + for (YAML::const_iterator it = doc.begin(); it != doc.end(); ++it) { + if (it->first.as() == "foo") { + YAML_ASSERT(it->second.Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(it->second.as() == ""); + } else if (it->first.as() == "") { + YAML_ASSERT(it->first.Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(it->second.as() == "bar"); + } else + return " unexpected key"; + } + return true; +} + +// 7.3 +TEST CompletelyEmptyNodes() { + YAML::Node doc = YAML::Load(ex7_3); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["foo"].IsNull()); + YAML_ASSERT(doc[YAML::Null].as() == "bar"); + return true; +} + +// 7.4 +TEST DoubleQuotedImplicitKeys() { + YAML::Node doc = YAML::Load(ex7_4); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["implicit block key"].size() == 1); + YAML_ASSERT(doc["implicit block key"][0].size() == 1); + YAML_ASSERT(doc["implicit block key"][0]["implicit flow key"] + .as() == "value"); + return true; +} + +// 7.5 +TEST DoubleQuotedLineBreaks() { + YAML::Node doc = YAML::Load(ex7_5); + YAML_ASSERT(doc.as() == + "folded to a space,\nto a line feed, or \t \tnon-content"); + return true; +} + +// 7.6 +TEST DoubleQuotedLines() { + YAML::Node doc = YAML::Load(ex7_6); + YAML_ASSERT(doc.as() == + " 1st non-empty\n2nd non-empty 3rd non-empty "); + return true; +} + +// 7.7 +TEST SingleQuotedCharacters() { + YAML::Node doc = YAML::Load(ex7_7); + YAML_ASSERT(doc.as() == "here's to \"quotes\""); + return true; +} + +// 7.8 +TEST SingleQuotedImplicitKeys() { + YAML::Node doc = YAML::Load(ex7_8); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["implicit block key"].size() == 1); + YAML_ASSERT(doc["implicit block key"][0].size() == 1); + YAML_ASSERT(doc["implicit block key"][0]["implicit flow key"] + .as() == "value"); + return true; +} + +// 7.9 +TEST SingleQuotedLines() { + YAML::Node doc = YAML::Load(ex7_9); + YAML_ASSERT(doc.as() == + " 1st non-empty\n2nd non-empty 3rd non-empty "); + return true; +} + +// 7.10 +TEST PlainCharacters() { + YAML::Node doc = YAML::Load(ex7_10); + YAML_ASSERT(doc.size() == 6); + YAML_ASSERT(doc[0].as() == "::vector"); + YAML_ASSERT(doc[1].as() == ": - ()"); + YAML_ASSERT(doc[2].as() == "Up, up, and away!"); + YAML_ASSERT(doc[3].as() == -123); + YAML_ASSERT(doc[4].as() == "http://example.com/foo#bar"); + YAML_ASSERT(doc[5].size() == 5); + YAML_ASSERT(doc[5][0].as() == "::vector"); + YAML_ASSERT(doc[5][1].as() == ": - ()"); + YAML_ASSERT(doc[5][2].as() == "Up, up, and away!"); + YAML_ASSERT(doc[5][3].as() == -123); + YAML_ASSERT(doc[5][4].as() == "http://example.com/foo#bar"); + return true; +} + +// 7.11 +TEST PlainImplicitKeys() { + YAML::Node doc = YAML::Load(ex7_11); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["implicit block key"].size() == 1); + YAML_ASSERT(doc["implicit block key"][0].size() == 1); + YAML_ASSERT(doc["implicit block key"][0]["implicit flow key"] + .as() == "value"); + return true; +} + +// 7.12 +TEST PlainLines() { + YAML::Node doc = YAML::Load(ex7_12); + YAML_ASSERT(doc.as() == + "1st non-empty\n2nd non-empty 3rd non-empty"); + return true; +} + +// 7.13 +TEST FlowSequence() { + YAML::Node doc = YAML::Load(ex7_13); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc[0].size() == 2); + YAML_ASSERT(doc[0][0].as() == "one"); + YAML_ASSERT(doc[0][1].as() == "two"); + YAML_ASSERT(doc[1].size() == 2); + YAML_ASSERT(doc[1][0].as() == "three"); + YAML_ASSERT(doc[1][1].as() == "four"); + return true; +} + +// 7.14 +TEST FlowSequenceEntries() { + YAML::Node doc = YAML::Load(ex7_14); + YAML_ASSERT(doc.size() == 5); + YAML_ASSERT(doc[0].as() == "double quoted"); + YAML_ASSERT(doc[1].as() == "single quoted"); + YAML_ASSERT(doc[2].as() == "plain text"); + YAML_ASSERT(doc[3].size() == 1); + YAML_ASSERT(doc[3][0].as() == "nested"); + YAML_ASSERT(doc[4].size() == 1); + YAML_ASSERT(doc[4]["single"].as() == "pair"); + return true; +} + +// 7.15 +TEST FlowMappings() { + YAML::Node doc = YAML::Load(ex7_15); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc[0].size() == 2); + YAML_ASSERT(doc[0]["one"].as() == "two"); + YAML_ASSERT(doc[0]["three"].as() == "four"); + YAML_ASSERT(doc[1].size() == 2); + YAML_ASSERT(doc[1]["five"].as() == "six"); + YAML_ASSERT(doc[1]["seven"].as() == "eight"); + return true; +} + +// 7.16 +TEST FlowMappingEntries() { + YAML::Node doc = YAML::Load(ex7_16); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["explicit"].as() == "entry"); + YAML_ASSERT(doc["implicit"].as() == "entry"); + YAML_ASSERT(doc[YAML::Null].IsNull()); + return true; +} + +// 7.17 +TEST FlowMappingSeparateValues() { + YAML::Node doc = YAML::Load(ex7_17); + YAML_ASSERT(doc.size() == 4); + YAML_ASSERT(doc["unquoted"].as() == "separate"); + YAML_ASSERT(doc["http://foo.com"].IsNull()); + YAML_ASSERT(doc["omitted value"].IsNull()); + YAML_ASSERT(doc[YAML::Null].as() == "omitted key"); + return true; +} + +// 7.18 +TEST FlowMappingAdjacentValues() { + YAML::Node doc = YAML::Load(ex7_18); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["adjacent"].as() == "value"); + YAML_ASSERT(doc["readable"].as() == "value"); + YAML_ASSERT(doc["empty"].IsNull()); + return true; +} + +// 7.19 +TEST SinglePairFlowMappings() { + YAML::Node doc = YAML::Load(ex7_19); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc[0].size() == 1); + YAML_ASSERT(doc[0]["foo"].as() == "bar"); + return true; +} + +// 7.20 +TEST SinglePairExplicitEntry() { + YAML::Node doc = YAML::Load(ex7_20); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc[0].size() == 1); + YAML_ASSERT(doc[0]["foo bar"].as() == "baz"); + return true; +} + +// 7.21 +TEST SinglePairImplicitEntries() { + YAML::Node doc = YAML::Load(ex7_21); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].size() == 1); + YAML_ASSERT(doc[0][0].size() == 1); + YAML_ASSERT(doc[0][0]["YAML"].as() == "separate"); + YAML_ASSERT(doc[1].size() == 1); + YAML_ASSERT(doc[1][0].size() == 1); + YAML_ASSERT(doc[1][0][YAML::Null].as() == "empty key entry"); + YAML_ASSERT(doc[2].size() == 1); + YAML_ASSERT(doc[2][0].size() == 1); + + std::map key; + key["JSON"] = "like"; + YAML_ASSERT(doc[2][0][key].as() == "adjacent"); + return true; +} + +// 7.22 +TEST InvalidImplicitKeys() { + try { + YAML::Load(ex7_22); + } + catch (const YAML::Exception& e) { + if (e.msg == YAML::ErrorMsg::END_OF_SEQ_FLOW) + return true; + + throw; + } + return " no exception thrown"; +} + +// 7.23 +TEST FlowContent() { + YAML::Node doc = YAML::Load(ex7_23); + YAML_ASSERT(doc.size() == 5); + YAML_ASSERT(doc[0].size() == 2); + YAML_ASSERT(doc[0][0].as() == "a"); + YAML_ASSERT(doc[0][1].as() == "b"); + YAML_ASSERT(doc[1].size() == 1); + YAML_ASSERT(doc[1]["a"].as() == "b"); + YAML_ASSERT(doc[2].as() == "a"); + YAML_ASSERT(doc[3].as() == 'b'); + YAML_ASSERT(doc[4].as() == "c"); + return true; +} + +// 7.24 +TEST FlowNodes() { + YAML::Node doc = YAML::Load(ex7_24); + YAML_ASSERT(doc.size() == 5); + YAML_ASSERT(doc[0].Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(doc[0].as() == "a"); + YAML_ASSERT(doc[1].as() == 'b'); + YAML_ASSERT(doc[2].as() == "c"); + YAML_ASSERT(doc[3].as() == "c"); + YAML_ASSERT(doc[4].Tag() == "tag:yaml.org,2002:str"); + YAML_ASSERT(doc[4].as() == ""); + return true; +} + +// 8.1 +TEST BlockScalarHeader() { + YAML::Node doc = YAML::Load(ex8_1); + YAML_ASSERT(doc.size() == 4); + YAML_ASSERT(doc[0].as() == "literal\n"); + YAML_ASSERT(doc[1].as() == " folded\n"); + YAML_ASSERT(doc[2].as() == "keep\n\n"); + YAML_ASSERT(doc[3].as() == " strip"); + return true; +} + +// 8.2 +TEST BlockIndentationHeader() { + YAML::Node doc = YAML::Load(ex8_2); + YAML_ASSERT(doc.size() == 4); + YAML_ASSERT(doc[0].as() == "detected\n"); + YAML_ASSERT(doc[1].as() == "\n\n# detected\n"); + YAML_ASSERT(doc[2].as() == " explicit\n"); + YAML_ASSERT(doc[3].as() == "\t\ndetected\n"); + return true; +} + +// 8.3 +TEST InvalidBlockScalarIndentationIndicators() { + { + bool threw = false; + try { + YAML::Load(ex8_3a); + } + catch (const YAML::Exception& e) { + if (e.msg != YAML::ErrorMsg::END_OF_SEQ) + throw; + + threw = true; + } + + if (!threw) + return " no exception thrown for less indented auto-detecting " + "indentation for a literal block scalar"; + } + + { + bool threw = false; + try { + YAML::Load(ex8_3b); + } + catch (const YAML::Exception& e) { + if (e.msg != YAML::ErrorMsg::END_OF_SEQ) + throw; + + threw = true; + } + + if (!threw) + return " no exception thrown for less indented auto-detecting " + "indentation for a folded block scalar"; + } + + { + bool threw = false; + try { + YAML::Load(ex8_3c); + } + catch (const YAML::Exception& e) { + if (e.msg != YAML::ErrorMsg::END_OF_SEQ) + throw; + + threw = true; + } + + if (!threw) + return " no exception thrown for less indented explicit indentation for " + "a literal block scalar"; + } + + return true; +} + +// 8.4 +TEST ChompingFinalLineBreak() { + YAML::Node doc = YAML::Load(ex8_4); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["strip"].as() == "text"); + YAML_ASSERT(doc["clip"].as() == "text\n"); + YAML_ASSERT(doc["keep"].as() == "text\n"); + return true; +} + +// 8.5 +TEST ChompingTrailingLines() { + YAML::Node doc = YAML::Load(ex8_5); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["strip"].as() == "# text"); + YAML_ASSERT(doc["clip"].as() == "# text\n"); + YAML_ASSERT(doc["keep"].as() == "# text\n"); // Note: I believe + // this is a bug in + // the YAML spec - + // it should be "# + // text\n\n" + return true; +} + +// 8.6 +TEST EmptyScalarChomping() { + YAML::Node doc = YAML::Load(ex8_6); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["strip"].as() == ""); + YAML_ASSERT(doc["clip"].as() == ""); + YAML_ASSERT(doc["keep"].as() == "\n"); + return true; +} + +// 8.7 +TEST LiteralScalar() { + YAML::Node doc = YAML::Load(ex8_7); + YAML_ASSERT(doc.as() == "literal\n\ttext\n"); + return true; +} + +// 8.8 +TEST LiteralContent() { + YAML::Node doc = YAML::Load(ex8_8); + YAML_ASSERT(doc.as() == "\n\nliteral\n \n\ntext\n"); + return true; +} + +// 8.9 +TEST FoldedScalar() { + YAML::Node doc = YAML::Load(ex8_9); + YAML_ASSERT(doc.as() == "folded text\n"); + return true; +} + +// 8.10 +TEST FoldedLines() { + YAML::Node doc = YAML::Load(ex8_10); + YAML_ASSERT(doc.as() == + "\nfolded line\nnext line\n * bullet\n\n * list\n * " + "lines\n\nlast line\n"); + return true; +} + +// 8.11 +TEST MoreIndentedLines() { + YAML::Node doc = YAML::Load(ex8_11); + YAML_ASSERT(doc.as() == + "\nfolded line\nnext line\n * bullet\n\n * list\n * " + "lines\n\nlast line\n"); + return true; +} + +// 8.12 +TEST EmptySeparationLines() { + YAML::Node doc = YAML::Load(ex8_12); + YAML_ASSERT(doc.as() == + "\nfolded line\nnext line\n * bullet\n\n * list\n * " + "lines\n\nlast line\n"); + return true; +} + +// 8.13 +TEST FinalEmptyLines() { + YAML::Node doc = YAML::Load(ex8_13); + YAML_ASSERT(doc.as() == + "\nfolded line\nnext line\n * bullet\n\n * list\n * " + "lines\n\nlast line\n"); + return true; +} + +// 8.14 +TEST BlockSequence() { + YAML::Node doc = YAML::Load(ex8_14); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["block sequence"].size() == 2); + YAML_ASSERT(doc["block sequence"][0].as() == "one"); + YAML_ASSERT(doc["block sequence"][1].size() == 1); + YAML_ASSERT(doc["block sequence"][1]["two"].as() == "three"); + return true; +} + +// 8.15 +TEST BlockSequenceEntryTypes() { + YAML::Node doc = YAML::Load(ex8_15); + YAML_ASSERT(doc.size() == 4); + YAML_ASSERT(doc[0].IsNull()); + YAML_ASSERT(doc[1].as() == "block node\n"); + YAML_ASSERT(doc[2].size() == 2); + YAML_ASSERT(doc[2][0].as() == "one"); + YAML_ASSERT(doc[2][1].as() == "two"); + YAML_ASSERT(doc[3].size() == 1); + YAML_ASSERT(doc[3]["one"].as() == "two"); + return true; +} + +// 8.16 +TEST BlockMappings() { + YAML::Node doc = YAML::Load(ex8_16); + YAML_ASSERT(doc.size() == 1); + YAML_ASSERT(doc["block mapping"].size() == 1); + YAML_ASSERT(doc["block mapping"]["key"].as() == "value"); + return true; +} + +// 8.17 +TEST ExplicitBlockMappingEntries() { + YAML::Node doc = YAML::Load(ex8_17); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["explicit key"].IsNull()); + YAML_ASSERT(doc["block key\n"].size() == 2); + YAML_ASSERT(doc["block key\n"][0].as() == "one"); + YAML_ASSERT(doc["block key\n"][1].as() == "two"); + return true; +} + +// 8.18 +TEST ImplicitBlockMappingEntries() { + YAML::Node doc = YAML::Load(ex8_18); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc["plain key"].as() == "in-line value"); + YAML_ASSERT(doc[YAML::Null].IsNull()); + YAML_ASSERT(doc["quoted key"].size() == 1); + YAML_ASSERT(doc["quoted key"][0].as() == "entry"); + return true; +} + +// 8.19 +TEST CompactBlockMappings() { + YAML::Node doc = YAML::Load(ex8_19); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc[0].size() == 1); + YAML_ASSERT(doc[0]["sun"].as() == "yellow"); + YAML_ASSERT(doc[1].size() == 1); + std::map key; + key["earth"] = "blue"; + YAML_ASSERT(doc[1][key].size() == 1); + YAML_ASSERT(doc[1][key]["moon"].as() == "white"); + return true; +} + +// 8.20 +TEST BlockNodeTypes() { + YAML::Node doc = YAML::Load(ex8_20); + YAML_ASSERT(doc.size() == 3); + YAML_ASSERT(doc[0].as() == "flow in block"); + YAML_ASSERT(doc[1].as() == "Block scalar\n"); + YAML_ASSERT(doc[2].size() == 1); + YAML_ASSERT(doc[2]["foo"].as() == "bar"); + return true; +} + +// 8.21 +TEST BlockScalarNodes() { + YAML::Node doc = YAML::Load(ex8_21); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["literal"].as() == "value"); // Note: I believe + // this is a bug in + // the YAML spec - + // it should be + // "value\n" + YAML_ASSERT(doc["folded"].as() == "value"); + YAML_ASSERT(doc["folded"].Tag() == "!foo"); + return true; +} + +// 8.22 +TEST BlockCollectionNodes() { + YAML::Node doc = YAML::Load(ex8_22); + YAML_ASSERT(doc.size() == 2); + YAML_ASSERT(doc["sequence"].size() == 2); + YAML_ASSERT(doc["sequence"][0].as() == "entry"); + YAML_ASSERT(doc["sequence"][1].size() == 1); + YAML_ASSERT(doc["sequence"][1][0].as() == "nested"); + YAML_ASSERT(doc["mapping"].size() == 1); + YAML_ASSERT(doc["mapping"]["foo"].as() == "bar"); + return true; +} +} +} diff --git a/test/nodetests.h b/test/nodetests.h new file mode 100644 index 0000000..6728758 --- /dev/null +++ b/test/nodetests.h @@ -0,0 +1,14 @@ +#ifndef NODETESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODETESTS_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 Test { +bool RunNodeTests(); +} + +#endif // NODETESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A6666 diff --git a/test/tests.cpp b/test/tests.cpp index 944b1ff..3c20995 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -1,5 +1,6 @@ #include "tests.h" #include "emittertests.h" +#include "nodetests.h" #include "parsertests.h" #include "spectests.h" #include "yaml-cpp/yaml.h" @@ -11,13 +12,16 @@ namespace Test { void RunAll() { bool passed = true; + if (!RunParserTests()) + passed = false; + if (!RunEmitterTests()) passed = false; if (!RunSpecTests()) passed = false; - if (!RunParserTests()) + if (!RunNodeTests()) passed = false; if (passed) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 2661c2e..28a3603 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -1,3 +1,6 @@ +add_executable(parse parse.cpp) +target_link_libraries(parse yaml-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..7794177 --- /dev/null +++ b/util/parse.cpp @@ -0,0 +1,61 @@ +#include "yaml-cpp/yaml.h" +#include "yaml-cpp/eventhandler.h" +#include +#include +#include + +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; +}