diff --git a/include/yaml-cpp/exceptions.h b/include/yaml-cpp/exceptions.h index 9e19637..04babd7 100644 --- a/include/yaml-cpp/exceptions.h +++ b/include/yaml-cpp/exceptions.h @@ -55,6 +55,13 @@ namespace YAML 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_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 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"; @@ -66,6 +73,24 @@ namespace YAML const char * const EXPECTED_VALUE_TOKEN = "expected value token"; const char * const UNEXPECTED_KEY_TOKEN = "unexpected key token"; const char * const UNEXPECTED_VALUE_TOKEN = "unexpected value token"; + + 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 { @@ -91,6 +116,72 @@ namespace YAML : 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 BadConversion: public RepresentationException { + public: + BadConversion() + : RepresentationException(Mark::null(), ErrorMsg::BAD_CONVERSION) {} + }; + + template + class TypedBadConversion: public BadConversion { + public: + TypedBadConversion() + : BadConversion() {} + }; + + class BadDereference: public RepresentationException { + public: + BadDereference() + : RepresentationException(Mark::null(), ErrorMsg::BAD_DEREFERENCE) {} + }; + + class BadSubscript: public RepresentationException { + public: + BadSubscript() + : RepresentationException(Mark::null(), ErrorMsg::BAD_SUBSCRIPT) {} + }; + + class BadPushback: public RepresentationException { + public: + BadPushback() + : RepresentationException(Mark::null(), ErrorMsg::BAD_PUSHBACK) {} + }; + class EmitterException: public Exception { public: EmitterException(const std::string& msg_) diff --git a/include/yaml-cpp/node/convert.h b/include/yaml-cpp/node/convert.h new file mode 100644 index 0000000..f78332a --- /dev/null +++ b/include/yaml-cpp/node/convert.h @@ -0,0 +1,209 @@ +#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; + } + }; + + 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)\ + template<>\ + struct convert {\ + static Node encode(const type& rhs) {\ + std::stringstream stream;\ + 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 >> 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 = -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;\ + }\ + } + + YAML_DEFINE_CONVERT_STREAMABLE(int); + YAML_DEFINE_CONVERT_STREAMABLE(unsigned); + YAML_DEFINE_CONVERT_STREAMABLE(short); + YAML_DEFINE_CONVERT_STREAMABLE(unsigned short); + YAML_DEFINE_CONVERT_STREAMABLE(long); + YAML_DEFINE_CONVERT_STREAMABLE(unsigned long); + YAML_DEFINE_CONVERT_STREAMABLE(long long); + YAML_DEFINE_CONVERT_STREAMABLE(unsigned long long); + + YAML_DEFINE_CONVERT_STREAMABLE(char); + YAML_DEFINE_CONVERT_STREAMABLE(unsigned char); + + YAML_DEFINE_CONVERT_STREAMABLE(float); + YAML_DEFINE_CONVERT_STREAMABLE(double); + YAML_DEFINE_CONVERT_STREAMABLE(long double); + +#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[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) + rhs[it->first.as()] = it->second.as(); + 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) + rhs.push_back(it->as()); + 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) + rhs.push_back(it->as()); + 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..80ed9a4 --- /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..e3d17af --- /dev/null +++ b/include/yaml-cpp/node/detail/impl.h @@ -0,0 +1,142 @@ +#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 >::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; + } + + 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; + } + + 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..dc699f4 --- /dev/null +++ b/include/yaml-cpp/node/detail/iterator.h @@ -0,0 +1,64 @@ +#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< + iterator_base, + 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..c4efb2c --- /dev/null +++ b/include/yaml-cpp/node/detail/iterator_fwd.h @@ -0,0 +1,27 @@ +#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..243a81a --- /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..846e279 --- /dev/null +++ b/include/yaml-cpp/node/detail/node.h @@ -0,0 +1,126 @@ +#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); } + + 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..c0be2b8 --- /dev/null +++ b/include/yaml-cpp/node/detail/node_data.h @@ -0,0 +1,105 @@ +#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); + + 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); + + 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..294921b --- /dev/null +++ b/include/yaml-cpp/node/detail/node_iterator.h @@ -0,0 +1,139 @@ +#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..d1b06c5 --- /dev/null +++ b/include/yaml-cpp/node/detail/node_ref.h @@ -0,0 +1,65 @@ +#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); } + + 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..7abf80b --- /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..00ae1f6 --- /dev/null +++ b/include/yaml-cpp/node/impl.h @@ -0,0 +1,355 @@ +#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_pNode(0) + { + } + + inline Node::Node(NodeType::value type): 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_pMemory(new detail::memory_holder), m_pNode(&m_pMemory->create_node()) + { + Assign(rhs); + } + + inline Node::Node(const detail::iterator_value& rhs): m_pMemory(rhs.m_pMemory), m_pNode(rhs.m_pNode) + { + } + + inline Node::Node(const Node& rhs): m_pMemory(rhs.m_pMemory), m_pNode(rhs.m_pNode) + { + } + + inline Node::Node(detail::node& node, detail::shared_memory_holder pMemory): m_pMemory(pMemory), m_pNode(&node) + { + } + + inline Node::~Node() + { + } + + inline void Node::EnsureNodeExists() const + { + 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 + { + return m_pNode ? m_pNode->is_defined() : true; + } + + inline NodeType::value Node::Type() const + { + 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 + { + return as_if(*this)(); + } + + template + inline const T Node::as(const S& fallback) const + { + return as_if(*this)(fallback); + } + + inline const std::string& Node::Scalar() const + { + return m_pNode ? m_pNode->scalar() : detail::node_data::empty_scalar; + } + + inline const std::string& Node::Tag() const + { + return m_pNode ? m_pNode->tag() : detail::node_data::empty_scalar; + } + + inline void Node::SetTag(const std::string& tag) + { + EnsureNodeExists(); + m_pNode->set_tag(tag); + } + + // assignment + inline bool Node::is(const Node& rhs) const + { + if(!m_pNode || !rhs.m_pNode) + return false; + return m_pNode->is(*rhs.m_pNode); + } + + template + inline Node& Node::operator=(const T& rhs) + { + Assign(rhs); + return *this; + } + + template + inline void Node::Assign(const T& rhs) + { + AssignData(convert::encode(rhs)); + } + + template<> + inline void Node::Assign(const std::string& rhs) + { + EnsureNodeExists(); + m_pNode->set_scalar(rhs); + } + + inline void Node::Assign(const char *rhs) + { + EnsureNodeExists(); + m_pNode->set_scalar(rhs); + } + + inline void Node::Assign(char *rhs) + { + EnsureNodeExists(); + m_pNode->set_scalar(rhs); + } + + inline Node& Node::operator=(const Node& rhs) + { + if(is(rhs)) + return *this; + AssignNode(rhs); + return *this; + } + + inline void Node::AssignData(const Node& rhs) + { + EnsureNodeExists(); + rhs.EnsureNodeExists(); + + m_pNode->set_data(*rhs.m_pNode); + m_pMemory->merge(*rhs.m_pMemory); + } + + inline void Node::AssignNode(const Node& rhs) + { + 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 + { + return m_pNode ? m_pNode->size() : 0; + } + + inline const_iterator Node::begin() const + { + return m_pNode ? const_iterator(m_pNode->begin(), m_pMemory) : const_iterator(); + } + + inline iterator Node::begin() + { + return m_pNode ? iterator(m_pNode->begin(), m_pMemory) : iterator(); + } + + inline const_iterator Node::end() const + { + return m_pNode ? const_iterator(m_pNode->end(), m_pMemory) : const_iterator(); + } + + inline iterator Node::end() + { + return m_pNode ? iterator(m_pNode->end(), m_pMemory) : iterator(); + } + + // sequence + template + inline void Node::push_back(const T& rhs) + { + push_back(Node(rhs)); + } + + inline void Node::push_back(const Node& rhs) + { + EnsureNodeExists(); + rhs.EnsureNodeExists(); + + m_pNode->push_back(*rhs.m_pNode, m_pMemory); + m_pMemory->merge(*rhs.m_pMemory); + } + + // indexing + template + inline const Node Node::operator[](const Key& key) const + { + EnsureNodeExists(); + detail::node& value = static_cast(*m_pNode).get(key, m_pMemory); + return Node(value, m_pMemory); + } + + template + inline Node Node::operator[](const Key& key) + { + EnsureNodeExists(); + detail::node& value = m_pNode->get(key, m_pMemory); + return Node(value, m_pMemory); + } + + template + inline bool Node::remove(const Key& key) + { + EnsureNodeExists(); + return m_pNode->remove(key, m_pMemory); + } + + inline const Node Node::operator[](const Node& key) const + { + 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) + { + 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) + { + EnsureNodeExists(); + key.EnsureNodeExists(); + return m_pNode->remove(*key.m_pNode, m_pMemory); + } + + inline const Node Node::operator[](const char *key) const + { + return operator[](std::string(key)); + } + + inline Node Node::operator[](const char *key) + { + return operator[](std::string(key)); + } + + inline bool Node::remove(const char *key) + { + return remove(std::string(key)); + } + + inline const Node Node::operator[](char *key) const + { + return operator[](static_cast(key)); + } + + inline Node Node::operator[](char *key) + { + return operator[](static_cast(key)); + } + + inline bool Node::remove(char *key) + { + return remove(static_cast(key)); + } + + // 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..4cc4719 --- /dev/null +++ b/include/yaml-cpp/node/iterator.h @@ -0,0 +1,28 @@ +#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) {} + explicit iterator_value(const Node& key, const Node& value): 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..e230e4a --- /dev/null +++ b/include/yaml-cpp/node/node.h @@ -0,0 +1,113 @@ +#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 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); + + // 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); + + const Node operator[](const char *key) const; + Node operator[](const char *key); + bool remove(const char *key); + + const Node operator[](char *key) const; + Node operator[](char *key); + bool remove(char *key); + + private: + 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: + mutable detail::shared_memory_holder m_pMemory; + mutable detail::node *m_pNode; + }; + + bool operator==(const Node& lhs, const Node& rhs); + + 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..82dbdc1 --- /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..316dbd2 --- /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..5ac8041 --- /dev/null +++ b/include/yaml-cpp/node/type.h @@ -0,0 +1,14 @@ +#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 ffc21f5..f71cdff 100644 --- a/include/yaml-cpp/parser.h +++ b/include/yaml-cpp/parser.h @@ -31,7 +31,9 @@ namespace YAML void Load(std::istream& in); bool HandleNextDocument(EventHandler& eventHandler); - + + bool GetNextDocument(Node& document); // old API only + void PrintTokens(std::ostream& out); private: diff --git a/include/yaml-cpp/yaml.h b/include/yaml-cpp/yaml.h index 0c219b3..4e63408 100644 --- a/include/yaml-cpp/yaml.h +++ b/include/yaml-cpp/yaml.h @@ -10,4 +10,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..dc715f7 --- /dev/null +++ b/src/convert.cpp @@ -0,0 +1,83 @@ +#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::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;imerge(*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_data.cpp b/src/node_data.cpp new file mode 100644 index 0000000..6f82f7b --- /dev/null +++ b/src/node_data.cpp @@ -0,0 +1,295 @@ +#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;icreate_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..6735f73 --- /dev/null +++ b/src/nodebuilder.cpp @@ -0,0 +1,138 @@ +#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..cce91d3 --- /dev/null +++ b/src/nodebuilder.h @@ -0,0 +1,58 @@ +#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..721f9c6 --- /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) + { + 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()); + 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..d142115 --- /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..3a33931 --- /dev/null +++ b/src/parse.cpp @@ -0,0 +1,64 @@ +#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()); + 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()); + return LoadAll(fin); + } +} diff --git a/src/singledocparser.cpp b/src/singledocparser.cpp index 4cad59e..47759c3 100644 --- a/src/singledocparser.cpp +++ b/src/singledocparser.cpp @@ -253,7 +253,6 @@ namespace YAML throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP_FLOW); Token& token = m_scanner.peek(); - const Mark mark = token.mark; // first check for end if(token.type == Token::FLOW_MAP_END) { m_scanner.pop(); @@ -265,7 +264,7 @@ namespace YAML m_scanner.pop(); HandleNode(eventHandler); } else { - eventHandler.OnNull(mark, NullAnchor); + eventHandler.OnNull(token.mark, NullAnchor); } // now grab value (optional) @@ -273,7 +272,7 @@ namespace YAML m_scanner.pop(); HandleNode(eventHandler); } else { - eventHandler.OnNull(mark, NullAnchor); + eventHandler.OnNull(token.mark, NullAnchor); } // now eat the separator (or could be a map end, which we ignore - but if it's neither, then it's a bad node) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8ee63d0..98777be 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,8 +1,8 @@ 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 8da5b70..f16d25a 100644 --- a/test/emittertests.cpp +++ b/test/emittertests.cpp @@ -1,6 +1,5 @@ #include "tests.h" #include "yaml-cpp/yaml.h" -#include "yaml-cpp/eventhandler.h" #include namespace Test @@ -1000,21 +999,6 @@ namespace Test } 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; std::string desiredOutput; @@ -1024,10 +1008,7 @@ namespace Test 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) { std::cout << "Emitter test failed: " << name << "\n"; diff --git a/test/new-api/nodetests.cpp b/test/new-api/nodetests.cpp new file mode 100644 index 0000000..ee40110 --- /dev/null +++ b/test/new-api/nodetests.cpp @@ -0,0 +1,433 @@ +#include "nodetests.h" +#include "yaml-cpp/yaml.h" +#include +#include + +namespace { + struct TEST { + TEST(): ok(false) {} + TEST(bool ok_): ok(ok_) {} + TEST(const char *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(...) { return " Expression threw something other than " #exc ": " #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 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(), std::runtime_error); + 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 \"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4K\"]"); + 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; + } + + 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; + } + } + + void RunNodeTest(TEST (*test)(), const std::string& name, int& passed, int& total) { + TEST ret; + try { + ret = test(); + } catch(...) { + ret.ok = false; + } + 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::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); + RunNodeTest(&Node::ForEach, "for each", passed, total); + RunNodeTest(&Node::ForEachMap, "for each map", 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..1d50ca6 --- /dev/null +++ b/test/new-api/parsertests.cpp @@ -0,0 +1,8 @@ +#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..328ae7a --- /dev/null +++ b/test/new-api/spectests.cpp @@ -0,0 +1,1365 @@ +#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..733e782 --- /dev/null +++ b/test/nodetests.h @@ -0,0 +1,13 @@ +#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/parsertests.h b/test/parsertests.h new file mode 100644 index 0000000..f3de1b8 --- /dev/null +++ b/test/parsertests.h @@ -0,0 +1,13 @@ +#ifndef PARSERTESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define PARSERTESTS_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 RunParserTests(); +} + +#endif // PARSERTESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/test/tests.cpp b/test/tests.cpp index ce8d338..efe82fc 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -1,5 +1,7 @@ #include "tests.h" #include "emittertests.h" +#include "nodetests.h" +#include "parsertests.h" #include "spectests.h" #include "yaml-cpp/yaml.h" #include @@ -12,12 +14,18 @@ namespace Test void RunAll() { bool passed = true; + if(!RunParserTests()) + passed = false; + if(!RunEmitterTests()) passed = false; if(!RunSpecTests()) passed = false; - + + if(!RunNodeTests()) + passed = false; + if(passed) std::cout << "All tests passed!\n"; }