diff --git a/CMakeLists.txt b/CMakeLists.txt index 9260989..d1dda5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ include_directories (${GREMLIN_SOURCE_DIR}/libs/glfw/include) include_directories (${GREMLIN_SOURCE_DIR}/libs/spark/include) include_directories (${GREMLIN_SOURCE_DIR}/libs/enet/include) include_directories (${GREMLIN_SOURCE_DIR}/libs/bullet) +include_directories (${GREMLIN_SOURCE_DIR}/libs/yaml-cpp/include) include_directories (${GREMLIN_SOURCE_DIR}/libs/trimeshloader/include) find_package(OpenGL) @@ -24,6 +25,7 @@ find_package(OpenGL) add_subdirectory (libs/enet) add_subdirectory (libs/pugixml) add_subdirectory (libs/bullet) +add_subdirectory (libs/yaml-cpp) add_subdirectory (libs/trimeshloader) add_subdirectory (src/common) add_subdirectory (src/server) diff --git a/libs/yaml-cpp/CMakeLists.txt b/libs/yaml-cpp/CMakeLists.txt new file mode 100644 index 0000000..ab47f50 --- /dev/null +++ b/libs/yaml-cpp/CMakeLists.txt @@ -0,0 +1,52 @@ +set(YAML_CPP_VERSION_MAJOR "0") +set(YAML_CPP_VERSION_MINOR "2") +set(YAML_CPP_VERSION_PATCH "5") +set(YAML_CPP_VERSION "${YAML_CPP_VERSION_MAJOR}.${YAML_CPP_VERSION_MINOR}.${YAML_CPP_VERSION_PATCH}") + +include_directories(include/yaml-cpp) +add_library(yaml-cpp + src/aliascontent.cpp + src/conversion.cpp + src/emitter.cpp + src/emitterstate.cpp + src/emitterstate.h + src/emitterutils.cpp + src/emitterutils.h + src/exp.cpp + src/exp.h + src/indentation.h + src/iterator.cpp + src/iterpriv.h + src/ltnode.h + src/map.cpp + src/map.h + src/node.cpp + src/null.cpp + src/ostream.cpp + src/parser.cpp + src/parserstate.cpp + src/parserstate.h + src/regex.cpp + src/regex.h + src/regeximpl.h + src/scalar.cpp + src/scalar.h + src/scanner.cpp + src/scanner.h + src/scanscalar.cpp + src/scanscalar.h + src/scantag.cpp + src/scantag.h + src/scantoken.cpp + src/sequence.cpp + src/sequence.h + src/setting.h + src/simplekey.cpp + src/stream.cpp + src/stream.h + src/streamcharsource.h + src/stringsource.h + src/tag.cpp + src/tag.h + src/token.h +) diff --git a/libs/yaml-cpp/include/yaml-cpp/conversion.h b/libs/yaml-cpp/include/yaml-cpp/conversion.h new file mode 100644 index 0000000..3a03053 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/conversion.h @@ -0,0 +1,31 @@ +#pragma once + +#ifndef CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "null.h" +#include "traits.h" +#include +#include + +namespace YAML +{ + inline bool Convert(const std::string& input, std::string& output) { + output = input; + return true; + } + + bool Convert(const std::string& input, bool& output); + bool Convert(const std::string& input, _Null& output); + + template + inline bool Convert(const std::string& input, T& output, typename enable_if >::type * = 0) { + std::stringstream stream(input); + stream.unsetf(std::ios::dec); + stream >> output; + return !!stream; + } +} + +#endif // CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/emitter.h b/libs/yaml-cpp/include/yaml-cpp/emitter.h new file mode 100644 index 0000000..a353e71 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/emitter.h @@ -0,0 +1,146 @@ +#pragma once + +#ifndef EMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define EMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "emittermanip.h" +#include "ostream.h" +#include "null.h" +#include +#include +#include + +namespace YAML +{ + class EmitterState; + + class Emitter + { + public: + Emitter(); + ~Emitter(); + + // output + const char *c_str() const; + unsigned size() const; + + // state checking + bool good() const; + const std::string GetLastError() const; + + // global setters + bool SetOutputCharset(EMITTER_MANIP value); + bool SetStringFormat(EMITTER_MANIP value); + bool SetBoolFormat(EMITTER_MANIP value); + bool SetIntBase(EMITTER_MANIP value); + bool SetSeqFormat(EMITTER_MANIP value); + bool SetMapFormat(EMITTER_MANIP value); + bool SetIndent(unsigned n); + bool SetPreCommentIndent(unsigned n); + bool SetPostCommentIndent(unsigned n); + + // local setters + Emitter& SetLocalValue(EMITTER_MANIP value); + Emitter& SetLocalIndent(const _Indent& indent); + + // overloads of write + Emitter& Write(const std::string& str); + Emitter& Write(bool b); + Emitter& Write(const _Alias& alias); + Emitter& Write(const _Anchor& anchor); + Emitter& Write(const _Tag& tag); + Emitter& Write(const _Comment& comment); + Emitter& Write(const _Null& null); + + template + Emitter& WriteIntegralType(T value); + + template + Emitter& WriteStreamable(T value); + + private: + void PreWriteIntegralType(std::stringstream& str); + void PostWriteIntegralType(const std::stringstream& str); + + private: + enum ATOMIC_TYPE { AT_SCALAR, AT_SEQ, AT_BLOCK_SEQ, AT_FLOW_SEQ, AT_MAP, AT_BLOCK_MAP, AT_FLOW_MAP }; + + void PreAtomicWrite(); + bool GotoNextPreAtomicState(); + void PostAtomicWrite(); + void EmitSeparationIfNecessary(); + + void EmitBeginSeq(); + void EmitEndSeq(); + void EmitBeginMap(); + void EmitEndMap(); + void EmitKey(); + void EmitValue(); + + private: + ostream m_stream; + std::auto_ptr m_pState; + }; + + template + inline Emitter& Emitter::WriteIntegralType(T value) + { + if(!good()) + return *this; + + std::stringstream str; + PreWriteIntegralType(str); + str << value; + PostWriteIntegralType(str); + return *this; + } + + template + inline Emitter& Emitter::WriteStreamable(T value) + { + if(!good()) + return *this; + + PreAtomicWrite(); + EmitSeparationIfNecessary(); + + std::stringstream str; + str << value; + m_stream << str.str(); + + PostAtomicWrite(); + return *this; + } + + // overloads of insertion + inline Emitter& operator << (Emitter& emitter, const std::string& v) { return emitter.Write(v); } + inline Emitter& operator << (Emitter& emitter, bool v) { return emitter.Write(v); } + inline Emitter& operator << (Emitter& emitter, const _Alias& v) { return emitter.Write(v); } + inline Emitter& operator << (Emitter& emitter, const _Anchor& v) { return emitter.Write(v); } + inline Emitter& operator << (Emitter& emitter, const _Tag& v) { return emitter.Write(v); } + inline Emitter& operator << (Emitter& emitter, const _Comment& v) { return emitter.Write(v); } + inline Emitter& operator << (Emitter& emitter, const _Null& v) { return emitter.Write(v); } + + inline Emitter& operator << (Emitter& emitter, const char *v) { return emitter.Write(std::string(v)); } + + inline Emitter& operator << (Emitter& emitter, int v) { return emitter.WriteIntegralType(v); } + inline Emitter& operator << (Emitter& emitter, unsigned int v) { return emitter.WriteIntegralType(v); } + inline Emitter& operator << (Emitter& emitter, short v) { return emitter.WriteIntegralType(v); } + inline Emitter& operator << (Emitter& emitter, unsigned short v) { return emitter.WriteIntegralType(v); } + inline Emitter& operator << (Emitter& emitter, long v) { return emitter.WriteIntegralType(v); } + inline Emitter& operator << (Emitter& emitter, unsigned long v) { return emitter.WriteIntegralType(v); } + + inline Emitter& operator << (Emitter& emitter, float v) { return emitter.WriteStreamable(v); } + inline Emitter& operator << (Emitter& emitter, double v) { return emitter.WriteStreamable(v); } + + inline Emitter& operator << (Emitter& emitter, EMITTER_MANIP value) { + return emitter.SetLocalValue(value); + } + + inline Emitter& operator << (Emitter& emitter, _Indent indent) { + return emitter.SetLocalIndent(indent); + } +} + +#endif // EMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/emittermanip.h b/libs/yaml-cpp/include/yaml-cpp/emittermanip.h new file mode 100644 index 0000000..c88054f --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/emittermanip.h @@ -0,0 +1,104 @@ +#pragma once + +#ifndef EMITTERMANIP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define EMITTERMANIP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include + +namespace YAML +{ + enum EMITTER_MANIP { + // general manipulators + Auto, + + // output character set + EmitNonAscii, + EscapeNonAscii, + + // string manipulators + // Auto, // duplicate + SingleQuoted, + DoubleQuoted, + Literal, + + // bool manipulators + YesNoBool, // yes, no + TrueFalseBool, // true, false + OnOffBool, // on, off + UpperCase, // TRUE, N + LowerCase, // f, yes + CamelCase, // No, Off + LongBool, // yes, On + ShortBool, // y, t + + // int manipulators + Dec, + Hex, + Oct, + + // sequence manipulators + BeginSeq, + EndSeq, + Flow, + Block, + + // map manipulators + BeginMap, + EndMap, + Key, + Value, + // Flow, // duplicate + // Block, // duplicate + // Auto, // duplicate + LongKey + }; + + struct _Indent { + _Indent(int value_): value(value_) {} + int value; + }; + + inline _Indent Indent(int value) { + return _Indent(value); + } + + struct _Alias { + _Alias(const std::string& content_): content(content_) {} + std::string content; + }; + + inline _Alias Alias(const std::string content) { + return _Alias(content); + } + + struct _Anchor { + _Anchor(const std::string& content_): content(content_) {} + std::string content; + }; + + inline _Anchor Anchor(const std::string content) { + return _Anchor(content); + } + + struct _Tag { + _Tag(const std::string& content_): content(content_), verbatim(true) {} + std::string content; + bool verbatim; + }; + + inline _Tag VerbatimTag(const std::string& content) { + return _Tag(content); + } + + struct _Comment { + _Comment(const std::string& content_): content(content_) {} + std::string content; + }; + + inline _Comment Comment(const std::string content) { + return _Comment(content); + } +} + +#endif // EMITTERMANIP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/exceptions.h b/libs/yaml-cpp/include/yaml-cpp/exceptions.h new file mode 100644 index 0000000..f81db59 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/exceptions.h @@ -0,0 +1,161 @@ +#pragma once + +#ifndef EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "mark.h" +#include "traits.h" +#include +#include +#include + +namespace YAML +{ + // error messages + namespace ErrorMsg + { + const std::string YAML_DIRECTIVE_ARGS = "YAML directives must have exactly one argument"; + const std::string YAML_VERSION = "bad YAML version: "; + const std::string YAML_MAJOR_VERSION = "YAML major version too large"; + const std::string REPEATED_YAML_DIRECTIVE= "repeated YAML directive"; + const std::string TAG_DIRECTIVE_ARGS = "TAG directives must have exactly two arguments"; + const std::string REPEATED_TAG_DIRECTIVE = "repeated TAG directive"; + const std::string CHAR_IN_TAG_HANDLE = "illegal character found while scanning tag handle"; + const std::string TAG_WITH_NO_SUFFIX = "tag handle with no suffix"; + const std::string END_OF_VERBATIM_TAG = "end of verbatim tag not found"; + const std::string END_OF_MAP = "end of map not found"; + const std::string END_OF_MAP_FLOW = "end of map flow not found"; + const std::string END_OF_SEQ = "end of sequence not found"; + const std::string END_OF_SEQ_FLOW = "end of sequence flow not found"; + const std::string MULTIPLE_TAGS = "cannot assign multiple tags to the same node"; + const std::string MULTIPLE_ANCHORS = "cannot assign multiple anchors to the same node"; + const std::string MULTIPLE_ALIASES = "cannot assign multiple aliases to the same node"; + const std::string ALIAS_CONTENT = "aliases can't have any content, *including* tags"; + const std::string INVALID_HEX = "bad character found while scanning hex number"; + const std::string INVALID_UNICODE = "invalid unicode: "; + const std::string INVALID_ESCAPE = "unknown escape character: "; + const std::string UNKNOWN_TOKEN = "unknown token"; + const std::string DOC_IN_SCALAR = "illegal document indicator in scalar"; + const std::string EOF_IN_SCALAR = "illegal EOF in scalar"; + const std::string CHAR_IN_SCALAR = "illegal character in scalar"; + const std::string TAB_IN_INDENTATION = "illegal tab when looking for indentation"; + const std::string FLOW_END = "illegal flow end"; + const std::string BLOCK_ENTRY = "illegal block entry"; + const std::string MAP_KEY = "illegal map key"; + const std::string MAP_VALUE = "illegal map value"; + const std::string ALIAS_NOT_FOUND = "alias not found after *"; + const std::string ANCHOR_NOT_FOUND = "anchor not found after &"; + const std::string CHAR_IN_ALIAS = "illegal character found while scanning alias"; + const std::string CHAR_IN_ANCHOR = "illegal character found while scanning anchor"; + const std::string ZERO_INDENT_IN_BLOCK = "cannot set zero indentation for a block scalar"; + const std::string CHAR_IN_BLOCK = "unexpected character in block scalar"; + const std::string AMBIGUOUS_ANCHOR = "cannot assign the same alias to multiple nodes"; + const std::string UNKNOWN_ANCHOR = "the referenced anchor is not defined"; + + const std::string INVALID_SCALAR = "invalid scalar"; + const std::string KEY_NOT_FOUND = "key not found"; + const std::string BAD_DEREFERENCE = "bad dereference"; + + const std::string UNMATCHED_GROUP_TAG = "unmatched group tag"; + const std::string UNEXPECTED_END_SEQ = "unexpected end sequence token"; + const std::string UNEXPECTED_END_MAP = "unexpected end map token"; + const std::string SINGLE_QUOTED_CHAR = "invalid character in single-quoted string"; + const std::string INVALID_ANCHOR = "invalid anchor"; + const std::string INVALID_ALIAS = "invalid alias"; + const std::string INVALID_TAG = "invalid tag"; + const std::string EXPECTED_KEY_TOKEN = "expected key token"; + const std::string EXPECTED_VALUE_TOKEN = "expected value token"; + const std::string UNEXPECTED_KEY_TOKEN = "unexpected key token"; + const std::string 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) { + return KEY_NOT_FOUND + ": " + key; + } + + 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::exception { + public: + Exception(const Mark& mark_, const std::string& msg_) + : mark(mark_), msg(msg_) { + std::stringstream output; + output << "yaml-cpp: error at line " << mark.line+1 << ", column " << mark.column+1 << ": " << msg; + what_ = output.str(); + } + virtual ~Exception() throw() {} + virtual const char *what() const throw() { return what_.c_str(); } + + Mark mark; + std::string msg; + + private: + std::string what_; + }; + + class ParserException: public Exception { + public: + ParserException(const Mark& mark_, const std::string& msg_) + : 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 BadDereference: public RepresentationException { + public: + BadDereference() + : RepresentationException(Mark::null(), ErrorMsg::BAD_DEREFERENCE) {} + }; + + class EmitterException: public Exception { + public: + EmitterException(const std::string& msg_) + : Exception(Mark::null(), msg_) {} + }; +} + +#endif // EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/iterator.h b/libs/yaml-cpp/include/yaml-cpp/iterator.h new file mode 100644 index 0000000..bb9141f --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/iterator.h @@ -0,0 +1,36 @@ +#pragma once + +#ifndef ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +namespace YAML +{ + class Node; + struct IterPriv; + + class Iterator + { + public: + Iterator(); + Iterator(IterPriv *pData); + Iterator(const Iterator& rhs); + ~Iterator(); + + Iterator& operator = (const Iterator& rhs); + Iterator& operator ++ (); + Iterator operator ++ (int); + const Node& operator * () const; + const Node *operator -> () const; + const Node& first() const; + const Node& second() const; + + friend bool operator == (const Iterator& it, const Iterator& jt); + friend bool operator != (const Iterator& it, const Iterator& jt); + + private: + IterPriv *m_pData; + }; +} + +#endif // ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/mark.h b/libs/yaml-cpp/include/yaml-cpp/mark.h new file mode 100644 index 0000000..7c3dfb3 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/mark.h @@ -0,0 +1,22 @@ +#pragma once + +#ifndef MARK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define MARK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +namespace YAML +{ + struct Mark { + Mark(): pos(0), line(0), column(0) {} + + static const Mark null() { return Mark(-1, -1, -1); } + + int pos; + int line, column; + + private: + Mark(int pos_, int line_, int column_): pos(pos_), line(line_), column(column_) {} + }; +} + +#endif // MARK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/node.h b/libs/yaml-cpp/include/yaml-cpp/node.h new file mode 100644 index 0000000..d3cf791 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/node.h @@ -0,0 +1,141 @@ +#pragma once + +#ifndef NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "conversion.h" +#include "exceptions.h" +#include "iterator.h" +#include "mark.h" +#include "noncopyable.h" +#include +#include +#include +#include +#include + +namespace YAML +{ + class Content; + class Scanner; + class Emitter; + struct ParserState; + + enum CONTENT_TYPE { CT_NONE, CT_SCALAR, CT_SEQUENCE, CT_MAP }; + + class Node: private noncopyable + { + public: + Node(); + ~Node(); + + void Clear(); + std::auto_ptr Clone() const; + void Parse(Scanner *pScanner, ParserState& state); + + CONTENT_TYPE GetType() const; + + // file location of start of this node + const Mark GetMark() const { return m_mark; } + + // accessors + Iterator begin() const; + Iterator end() const; + std::size_t size() const; + + // extraction of scalars + bool GetScalar(std::string& s) const; + + // we can specialize this for other values + template + bool Read(T& value) const; + + template + const T Read() const; + + template + operator T() const; + + template + friend void operator >> (const Node& node, T& value); + + // retrieval for maps and sequences + template + const Node *FindValue(const T& key) const; + + template + const Node& operator [] (const T& key) const; + + // specific to maps + const Node *FindValue(const char *key) const; + const Node& operator [] (const char *key) const; + + // for anchors/aliases + const Node *Identity() const { return m_pIdentity; } + bool IsAlias() const { return m_alias; } + bool IsReferenced() const { return m_referenced; } + + // for tags + const std::string GetTag() const { return IsAlias() ? m_pIdentity->GetTag() : m_tag; } + + // emitting + friend Emitter& operator << (Emitter& out, const Node& node); + + // ordering + int Compare(const Node& rhs) const; + friend bool operator < (const Node& n1, const Node& n2); + + private: + // helper for sequences + template friend struct _FindFromNodeAtIndex; + const Node *FindAtIndex(std::size_t i) const; + + // helper for maps + template + const Node& GetValue(const T& key) const; + + template + const Node *FindValueForKey(const T& key) const; + + // helper for cloning + Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent); + + // helpers for parsing + void ParseHeader(Scanner *pScanner, ParserState& state); + void ParseTag(Scanner *pScanner, ParserState& state); + void ParseAnchor(Scanner *pScanner, ParserState& state); + void ParseAlias(Scanner *pScanner, ParserState& state); + + private: + Mark m_mark; + std::string m_anchor, m_tag; + Content *m_pContent; + bool m_alias; + const Node *m_pIdentity; + mutable bool m_referenced; + }; + + // comparisons with auto-conversion + template + bool operator == (const T& value, const Node& node); + + template + bool operator == (const Node& node, const T& value); + + template + bool operator != (const T& value, const Node& node); + + template + bool operator != (const Node& node, const T& value); + + bool operator == (const char *value, const Node& node); + bool operator == (const Node& node, const char *value); + bool operator != (const char *value, const Node& node); + bool operator != (const Node& node, const char *value); +} + +#include "nodeimpl.h" +#include "nodereadimpl.h" + +#endif // NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/nodeimpl.h b/libs/yaml-cpp/include/yaml-cpp/nodeimpl.h new file mode 100644 index 0000000..e6cffc3 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/nodeimpl.h @@ -0,0 +1,118 @@ +#pragma once + +#ifndef NODEIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODEIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "nodeutil.h" + +namespace YAML +{ + // implementation of templated things + template + inline const T Node::Read() const { + T value; + *this >> value; + return value; + } + + template + Node::operator T() const { + return Read(); + } + + template + inline void operator >> (const Node& node, T& value) { + if(!ConvertScalar(node, value)) + throw InvalidScalar(node.m_mark); + } + + template + inline const Node *Node::FindValue(const T& key) const { + switch(GetType()) { + case CT_MAP: + return FindValueForKey(key); + case CT_SEQUENCE: + return FindFromNodeAtIndex(*this, key); + default: + return 0; + } + } + + template + inline const Node *Node::FindValueForKey(const T& key) const { + for(Iterator it=begin();it!=end();++it) { + T t; + if(it.first().Read(t)) { + if(key == t) + return &it.second(); + } + } + + return 0; + } + + template + inline const Node& Node::GetValue(const T& key) const { + if(!m_pContent) + throw BadDereference(); + + const Node *pValue = FindValue(key); + if(!pValue) + throw MakeTypedKeyNotFound(m_mark, key); + + return *pValue; + } + + template + inline const Node& Node::operator [] (const T& key) const { + return GetValue(key); + } + + inline const Node *Node::FindValue(const char *key) const { + return FindValue(std::string(key)); + } + + inline const Node& Node::operator [] (const char *key) const { + return GetValue(std::string(key)); + } + + template + inline bool operator == (const T& value, const Node& node) { + return value == node.operator T(); + } + + template + inline bool operator == (const Node& node, const T& value) { + return value == node.operator T(); + } + + template + inline bool operator != (const T& value, const Node& node) { + return value != node.operator T(); + } + + template + inline bool operator != (const Node& node, const T& value) { + return value != node.operator T(); + } + + inline bool operator == (const char *value, const Node& node) { + return std::string(value) == node; + } + + inline bool operator == (const Node& node, const char *value) { + return std::string(value) == node; + } + + inline bool operator != (const char *value, const Node& node) { + return std::string(value) != node; + } + + inline bool operator != (const Node& node, const char *value) { + return std::string(value) != node; + } + +} + +#endif // NODEIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/nodereadimpl.h b/libs/yaml-cpp/include/yaml-cpp/nodereadimpl.h new file mode 100644 index 0000000..d315660 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/nodereadimpl.h @@ -0,0 +1,78 @@ +#pragma once + +namespace YAML +{ + // implementation for Node::Read + // (the goal is to call ConvertScalar if we can, and fall back to operator >> if not) + // thanks to litb from stackoverflow.com + // http://stackoverflow.com/questions/1386183/how-to-call-a-templated-function-if-it-exists-and-something-else-otherwise/1386390#1386390 + + // Note: this doesn't work on gcc 3.2, but does on gcc 3.4 and above. I'm not sure about 3.3. + +#if __GNUC__ && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ <= 3)) + // trick doesn't work? Just fall back to ConvertScalar. + // This means that we can't use any user-defined types as keys in a map + template + inline bool Node::Read(T& value) const { + return ConvertScalar(*this, value); + } +#else + // usual case: the trick! + template + struct read_impl; + + // ConvertScalar available + template<> + struct read_impl { + template + static bool read(const Node& node, T& value) { + return ConvertScalar(node, value); + } + }; + + // ConvertScalar not available + template<> + struct read_impl { + template + static bool read(const Node& node, T& value) { + try { + node >> value; + } catch(const Exception&) { + return false; + } + return true; + } + }; + + namespace fallback { + // sizeof > 1 + struct flag { char c[2]; }; + flag Convert(...); + + int operator,(flag, flag); + + template + char operator,(flag, T const&); + + char operator,(int, flag); + int operator,(char, flag); + } + + template + inline bool Node::Read(T& value) const { + using namespace fallback; + + return read_impl::read(*this, value); + } +#endif // done with trick + + // the main conversion function + template + inline bool ConvertScalar(const Node& node, T& value) { + std::string scalar; + if(!node.GetScalar(scalar)) + return false; + + return Convert(scalar, value); + } +} diff --git a/libs/yaml-cpp/include/yaml-cpp/nodeutil.h b/libs/yaml-cpp/include/yaml-cpp/nodeutil.h new file mode 100644 index 0000000..1be6b69 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/nodeutil.h @@ -0,0 +1,60 @@ +#pragma once + +#ifndef NODEUTIL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODEUTIL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +namespace YAML +{ + template + struct is_same_type { + enum { value = false }; + }; + + template + struct is_same_type { + enum { value = true }; + }; + + template + struct is_index_type_with_check { + enum { value = false }; + }; + + template <> struct is_index_type_with_check { enum { value = true }; }; + +#define MAKE_INDEX_TYPE(Type) \ + template <> struct is_index_type_with_check::value> { enum { value = true }; } + + MAKE_INDEX_TYPE(int); + MAKE_INDEX_TYPE(unsigned); + MAKE_INDEX_TYPE(short); + MAKE_INDEX_TYPE(unsigned short); + MAKE_INDEX_TYPE(long); + MAKE_INDEX_TYPE(unsigned long); + +#undef MAKE_INDEX_TYPE + + template + struct is_index_type: public is_index_type_with_check {}; + + // messing around with template stuff to get the right overload for operator [] for a sequence + template + struct _FindFromNodeAtIndex { + const Node *pRet; + _FindFromNodeAtIndex(const Node&, const T&): pRet(0) {} + }; + + template + struct _FindFromNodeAtIndex { + const Node *pRet; + _FindFromNodeAtIndex(const Node& node, const T& key): pRet(node.FindAtIndex(static_cast(key))) {} + }; + + template + inline const Node *FindFromNodeAtIndex(const Node& node, const T& key) { + return _FindFromNodeAtIndex::value>(node, key).pRet; + } +} + +#endif // NODEUTIL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/noncopyable.h b/libs/yaml-cpp/include/yaml-cpp/noncopyable.h new file mode 100644 index 0000000..577b547 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/noncopyable.h @@ -0,0 +1,22 @@ +#pragma once + +#ifndef NONCOPYABLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NONCOPYABLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +namespace YAML +{ + // this is basically boost::noncopyable + class noncopyable + { + protected: + noncopyable() {} + ~noncopyable() {} + + private: + noncopyable(const noncopyable&); + const noncopyable& operator = (const noncopyable&); + }; +} + +#endif // NONCOPYABLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/null.h b/libs/yaml-cpp/include/yaml-cpp/null.h new file mode 100644 index 0000000..3857683 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/null.h @@ -0,0 +1,21 @@ +#pragma once + +#ifndef NULL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NULL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +namespace YAML +{ + class Node; + + struct _Null {}; + inline bool operator == (const _Null&, const _Null&) { return true; } + inline bool operator != (const _Null&, const _Null&) { return false; } + + bool IsNull(const Node& node); + + extern _Null Null; +} + +#endif // NULL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/libs/yaml-cpp/include/yaml-cpp/ostream.h b/libs/yaml-cpp/include/yaml-cpp/ostream.h new file mode 100644 index 0000000..a5912bb --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/ostream.h @@ -0,0 +1,38 @@ +#pragma once + +#ifndef OSTREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define OSTREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include + +namespace YAML +{ + class ostream + { + public: + ostream(); + ~ostream(); + + void reserve(unsigned size); + void put(char ch); + const char *str() const { return m_buffer; } + + unsigned row() const { return m_row; } + unsigned col() const { return m_col; } + unsigned pos() const { return m_pos; } + + private: + char *m_buffer; + unsigned m_pos; + unsigned m_size; + + unsigned m_row, m_col; + }; + + ostream& operator << (ostream& out, const char *str); + ostream& operator << (ostream& out, const std::string& str); + ostream& operator << (ostream& out, char ch); +} + +#endif // OSTREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/parser.h b/libs/yaml-cpp/include/yaml-cpp/parser.h new file mode 100644 index 0000000..fec63ea --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/parser.h @@ -0,0 +1,46 @@ +#pragma once + +#ifndef PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "node.h" +#include "noncopyable.h" +#include +#include +#include +#include +#include + +namespace YAML +{ + class Scanner; + struct ParserState; + struct Token; + + class Parser: private noncopyable + { + public: + Parser(); + Parser(std::istream& in); + ~Parser(); + + operator bool() const; + + void Load(std::istream& in); + bool GetNextDocument(Node& document); + void PrintTokens(std::ostream& out); + + private: + void ParseDirectives(); + void HandleDirective(const Token& token); + void HandleYamlDirective(const Token& token); + void HandleTagDirective(const Token& token); + + private: + std::auto_ptr m_pScanner; + std::auto_ptr m_pState; + }; +} + +#endif // PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/stlemitter.h b/libs/yaml-cpp/include/yaml-cpp/stlemitter.h new file mode 100644 index 0000000..c2f86ff --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/stlemitter.h @@ -0,0 +1,44 @@ +#pragma once + +#ifndef STLEMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define STLEMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include +#include + +namespace YAML +{ + template + inline Emitter& operator << (Emitter& emitter, const std::vector & v) { + typedef typename std::vector vec; + emitter << BeginSeq; + for(typename vec::const_iterator it=v.begin();it!=v.end();++it) + emitter << *it; + emitter << EndSeq; + return emitter; + } + + template + inline Emitter& operator << (Emitter& emitter, const std::list & v) { + typedef typename std::list list; + emitter << BeginSeq; + for(typename list::const_iterator it=v.begin();it!=v.end();++it) + emitter << *it; + emitter << EndSeq; + return emitter; + } + + template + inline Emitter& operator << (Emitter& emitter, const std::map & m) { + typedef typename std::map map; + emitter << BeginMap; + for(typename map::const_iterator it=m.begin();it!=m.end();++it) + emitter << Key << it->first << Value << it->second; + emitter << EndMap; + return emitter; + } +} + +#endif // STLEMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/stlnode.h b/libs/yaml-cpp/include/yaml-cpp/stlnode.h new file mode 100644 index 0000000..cfe2f03 --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/stlnode.h @@ -0,0 +1,36 @@ +#pragma once + +#ifndef STLNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define STLNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include + +namespace YAML +{ + template + void operator >> (const Node& node, std::vector& v) + { + v.clear(); + v.resize(node.size()); + for(unsigned i=0;i> v[i]; + } + + + template + void operator >> (const Node& node, std::map& m) + { + m.clear(); + for(Iterator it=node.begin();it!=node.end();++it) { + K k; + V v; + it.first() >> k; + it.second() >> v; + m[k] = v; + } + } +} + +#endif // STLNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/include/yaml-cpp/traits.h b/libs/yaml-cpp/include/yaml-cpp/traits.h new file mode 100644 index 0000000..46db01b --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/traits.h @@ -0,0 +1,50 @@ +#pragma once + +#ifndef TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +namespace YAML +{ + template + struct is_numeric { enum { value = false }; }; + + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + template <> struct is_numeric { enum { value = true }; }; + + template + struct enable_if_c { + typedef T type; + }; + + template + struct enable_if_c {}; + + template + struct enable_if : public enable_if_c {}; + + template + struct disable_if_c { + typedef T type; + }; + + template + struct disable_if_c {}; + + template + struct disable_if : public disable_if_c {}; +} + +#endif // TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/libs/yaml-cpp/include/yaml-cpp/yaml.h b/libs/yaml-cpp/include/yaml-cpp/yaml.h new file mode 100644 index 0000000..d1e83ca --- /dev/null +++ b/libs/yaml-cpp/include/yaml-cpp/yaml.h @@ -0,0 +1,15 @@ +#pragma once + +#ifndef YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "parser.h" +#include "node.h" +#include "stlnode.h" +#include "iterator.h" +#include "emitter.h" +#include "stlemitter.h" +#include "exceptions.h" + +#endif // YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/license.txt b/libs/yaml-cpp/license.txt new file mode 100644 index 0000000..68a6f66 --- /dev/null +++ b/libs/yaml-cpp/license.txt @@ -0,0 +1,19 @@ +Copyright (c) 2008 Jesse Beder. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/libs/yaml-cpp/src/aliascontent.cpp b/libs/yaml-cpp/src/aliascontent.cpp new file mode 100644 index 0000000..111938b --- /dev/null +++ b/libs/yaml-cpp/src/aliascontent.cpp @@ -0,0 +1,93 @@ +#include "aliascontent.h" + +namespace YAML +{ + AliasContent::AliasContent(Content* pNodeContent) + : m_pRef(pNodeContent) + { + } + + Content *AliasContent::Clone() const + { + return 0; // TODO: how to clone an alias? + } + + void AliasContent::Parse(Scanner * /*pScanner*/, ParserState& /*state*/) + { + } + + void AliasContent::Write(Emitter&) const + { + // no content (just an alias) + } + + bool AliasContent::GetBegin(std::vector ::const_iterator& i) const + { + return m_pRef->GetBegin(i); + } + + bool AliasContent::GetBegin(std::map ::const_iterator& i) const + { + return m_pRef->GetBegin(i); + } + + bool AliasContent::GetEnd(std::vector ::const_iterator& i) const + { + return m_pRef->GetEnd(i); + } + + bool AliasContent::GetEnd(std::map ::const_iterator& i) const + { + return m_pRef->GetEnd(i); + } + + Node* AliasContent::GetNode(std::size_t n) const + { + return m_pRef->GetNode(n); + } + + std::size_t AliasContent::GetSize() const + { + return m_pRef->GetSize(); + } + + bool AliasContent::IsScalar() const + { + return m_pRef->IsScalar(); + } + + bool AliasContent::IsMap() const + { + return m_pRef->IsMap(); + } + + bool AliasContent::IsSequence() const + { + return m_pRef->IsSequence(); + } + + bool AliasContent::GetScalar(std::string& scalar) const + { + return m_pRef->GetScalar(scalar); + } + + int AliasContent::Compare(Content *pContent) + { + return m_pRef->Compare(pContent); + } + + int AliasContent::Compare(Scalar *pScalar) + { + return m_pRef->Compare(pScalar); + } + + int AliasContent::Compare(Sequence *pSequence) + { + return m_pRef->Compare(pSequence); + } + + int AliasContent::Compare(Map *pMap) + { + return m_pRef->Compare(pMap); + } +} diff --git a/libs/yaml-cpp/src/aliascontent.h b/libs/yaml-cpp/src/aliascontent.h new file mode 100644 index 0000000..a69c28b --- /dev/null +++ b/libs/yaml-cpp/src/aliascontent.h @@ -0,0 +1,43 @@ +#pragma once + +#ifndef ALIASCONTENT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define ALIASCONTENT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "content.h" + +namespace YAML +{ + class AliasContent : public Content + { + public: + AliasContent(Content *pNodeContent); + + virtual Content *Clone() const; + + virtual void Parse(Scanner* pScanner, ParserState& state); + virtual void Write(Emitter&) const; + + virtual bool GetBegin(std::vector ::const_iterator&) const; + virtual bool GetBegin(std::map ::const_iterator&) const; + virtual bool GetEnd(std::vector ::const_iterator&) const; + virtual bool GetEnd(std::map ::const_iterator&) const; + virtual Node* GetNode(std::size_t) const; + virtual std::size_t GetSize() const; + virtual bool IsScalar() const; + virtual bool IsMap() const; + virtual bool IsSequence() const; + + virtual bool GetScalar(std::string& s) const; + + virtual int Compare(Content *); + virtual int Compare(Scalar *); + virtual int Compare(Sequence *); + virtual int Compare(Map *); + + private: + Content* m_pRef; + }; +} + +#endif // ALIASCONTENT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/content.h b/libs/yaml-cpp/src/content.h new file mode 100644 index 0000000..d6e0e8c --- /dev/null +++ b/libs/yaml-cpp/src/content.h @@ -0,0 +1,57 @@ +#pragma once + +#ifndef CONTENT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define CONTENT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include +#include "parserstate.h" +#include "exceptions.h" +#include "ltnode.h" + +namespace YAML +{ + class Scanner; + class Parser; + class Node; + class Scalar; + class Sequence; + class Map; + class Emitter; + + class Content + { + public: + Content() {} + virtual ~Content() {} + + virtual Content *Clone() const = 0; + + virtual void Parse(Scanner *pScanner, ParserState& state) = 0; + virtual void Write(Emitter& out) const = 0; + + virtual bool GetBegin(std::vector ::const_iterator&) const { return false; } + virtual bool GetBegin(std::map ::const_iterator&) const { return false; } + virtual bool GetEnd(std::vector ::const_iterator&) const { return false; } + virtual bool GetEnd(std::map ::const_iterator&) const { return false; } + virtual Node *GetNode(std::size_t) const { return 0; } + virtual std::size_t GetSize() const { return 0; } + virtual bool IsScalar() const { return false; } + virtual bool IsMap() const { return false; } + virtual bool IsSequence() const { return false; } + + // extraction + virtual bool GetScalar(std::string&) const { return false; } + + // ordering + virtual int Compare(Content *) { return 0; } + virtual int Compare(Scalar *) { return 0; } + virtual int Compare(Sequence *) { return 0; } + virtual int Compare(Map *) { return 0; } + + protected: + }; +} + +#endif // CONTENT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/conversion.cpp b/libs/yaml-cpp/src/conversion.cpp new file mode 100644 index 0000000..3b76ef2 --- /dev/null +++ b/libs/yaml-cpp/src/conversion.cpp @@ -0,0 +1,89 @@ +#include "conversion.h" +#include + +//////////////////////////////////////////////////////////////// +// Specializations for converting a string to specific types + +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 + +namespace YAML +{ + Emitter::Emitter(): m_pState(new EmitterState) + { + } + + Emitter::~Emitter() + { + } + + const char *Emitter::c_str() const + { + return m_stream.str(); + } + + unsigned Emitter::size() const + { + return m_stream.pos(); + } + + // state checking + bool Emitter::good() const + { + return m_pState->good(); + } + + const std::string Emitter::GetLastError() const + { + return m_pState->GetLastError(); + } + + // global setters + bool Emitter::SetOutputCharset(EMITTER_MANIP value) + { + return m_pState->SetOutputCharset(value, GLOBAL); + } + + bool Emitter::SetStringFormat(EMITTER_MANIP value) + { + return m_pState->SetStringFormat(value, GLOBAL); + } + + bool Emitter::SetBoolFormat(EMITTER_MANIP value) + { + bool ok = false; + if(m_pState->SetBoolFormat(value, GLOBAL)) + ok = true; + if(m_pState->SetBoolCaseFormat(value, GLOBAL)) + ok = true; + if(m_pState->SetBoolLengthFormat(value, GLOBAL)) + ok = true; + return ok; + } + + bool Emitter::SetIntBase(EMITTER_MANIP value) + { + return m_pState->SetIntFormat(value, GLOBAL); + } + + bool Emitter::SetSeqFormat(EMITTER_MANIP value) + { + return m_pState->SetFlowType(GT_SEQ, value, GLOBAL); + } + + bool Emitter::SetMapFormat(EMITTER_MANIP value) + { + bool ok = false; + if(m_pState->SetFlowType(GT_MAP, value, GLOBAL)) + ok = true; + if(m_pState->SetMapKeyFormat(value, GLOBAL)) + ok = true; + return ok; + } + + bool Emitter::SetIndent(unsigned n) + { + return m_pState->SetIndent(n, GLOBAL); + } + + bool Emitter::SetPreCommentIndent(unsigned n) + { + return m_pState->SetPreCommentIndent(n, GLOBAL); + } + + bool Emitter::SetPostCommentIndent(unsigned n) + { + return m_pState->SetPostCommentIndent(n, GLOBAL); + } + + // SetLocalValue + // . Either start/end a group, or set a modifier locally + Emitter& Emitter::SetLocalValue(EMITTER_MANIP value) + { + if(!good()) + return *this; + + switch(value) { + case BeginSeq: + EmitBeginSeq(); + break; + case EndSeq: + EmitEndSeq(); + break; + case BeginMap: + EmitBeginMap(); + break; + case EndMap: + EmitEndMap(); + break; + case Key: + EmitKey(); + break; + case Value: + EmitValue(); + break; + default: + m_pState->SetLocalValue(value); + break; + } + return *this; + } + + Emitter& Emitter::SetLocalIndent(const _Indent& indent) + { + m_pState->SetIndent(indent.value, LOCAL); + return *this; + } + + // GotoNextPreAtomicState + // . Runs the state machine, emitting if necessary, and returns 'true' if done (i.e., ready to emit an atom) + bool Emitter::GotoNextPreAtomicState() + { + if(!good()) + return true; + + unsigned curIndent = m_pState->GetCurIndent(); + + EMITTER_STATE curState = m_pState->GetCurState(); + switch(curState) { + // document-level + case ES_WAITING_FOR_DOC: + m_stream << "---"; + m_pState->RequireSeparation(); + m_pState->SwitchState(ES_WRITING_DOC); + return true; + case ES_WRITING_DOC: + return true; + + // block sequence + case ES_WAITING_FOR_BLOCK_SEQ_ENTRY: + m_stream << IndentTo(curIndent) << "-"; + m_pState->RequireSeparation(); + m_pState->SwitchState(ES_WRITING_BLOCK_SEQ_ENTRY); + return true; + case ES_WRITING_BLOCK_SEQ_ENTRY: + return true; + case ES_DONE_WITH_BLOCK_SEQ_ENTRY: + m_stream << '\n'; + m_pState->SwitchState(ES_WAITING_FOR_BLOCK_SEQ_ENTRY); + return false; + + // flow sequence + case ES_WAITING_FOR_FLOW_SEQ_ENTRY: + m_pState->SwitchState(ES_WRITING_FLOW_SEQ_ENTRY); + return true; + case ES_WRITING_FLOW_SEQ_ENTRY: + return true; + case ES_DONE_WITH_FLOW_SEQ_ENTRY: + m_stream << ','; + m_pState->RequireSeparation(); + m_pState->SwitchState(ES_WAITING_FOR_FLOW_SEQ_ENTRY); + return false; + + // block map + case ES_WAITING_FOR_BLOCK_MAP_ENTRY: + m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN); + return true; + case ES_WAITING_FOR_BLOCK_MAP_KEY: + if(m_pState->CurrentlyInLongKey()) { + m_stream << IndentTo(curIndent) << '?'; + m_pState->RequireSeparation(); + } + m_pState->SwitchState(ES_WRITING_BLOCK_MAP_KEY); + return true; + case ES_WRITING_BLOCK_MAP_KEY: + return true; + case ES_DONE_WITH_BLOCK_MAP_KEY: + m_pState->SetError(ErrorMsg::EXPECTED_VALUE_TOKEN); + return true; + case ES_WAITING_FOR_BLOCK_MAP_VALUE: + if(m_pState->CurrentlyInLongKey()) + m_stream << IndentTo(curIndent); + m_stream << ':'; + m_pState->RequireSeparation(); + m_pState->SwitchState(ES_WRITING_BLOCK_MAP_VALUE); + return true; + case ES_WRITING_BLOCK_MAP_VALUE: + return true; + case ES_DONE_WITH_BLOCK_MAP_VALUE: + m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN); + return true; + + // flow map + case ES_WAITING_FOR_FLOW_MAP_ENTRY: + m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN); + return true; + case ES_WAITING_FOR_FLOW_MAP_KEY: + m_pState->SwitchState(ES_WRITING_FLOW_MAP_KEY); + if(m_pState->CurrentlyInLongKey()) { + EmitSeparationIfNecessary(); + m_stream << '?'; + m_pState->RequireSeparation(); + } + return true; + case ES_WRITING_FLOW_MAP_KEY: + return true; + case ES_DONE_WITH_FLOW_MAP_KEY: + m_pState->SetError(ErrorMsg::EXPECTED_VALUE_TOKEN); + return true; + case ES_WAITING_FOR_FLOW_MAP_VALUE: + m_stream << ':'; + m_pState->RequireSeparation(); + m_pState->SwitchState(ES_WRITING_FLOW_MAP_VALUE); + return true; + case ES_WRITING_FLOW_MAP_VALUE: + return true; + case ES_DONE_WITH_FLOW_MAP_VALUE: + m_pState->SetError(ErrorMsg::EXPECTED_KEY_TOKEN); + return true; + default: + assert(false); + } + + assert(false); + return true; + } + + // PreAtomicWrite + // . Depending on the emitter state, write to the stream to get it + // in position to do an atomic write (e.g., scalar, sequence, or map) + void Emitter::PreAtomicWrite() + { + if(!good()) + return; + + while(!GotoNextPreAtomicState()) + ; + } + + // PostAtomicWrite + // . Clean up + void Emitter::PostAtomicWrite() + { + if(!good()) + return; + + EMITTER_STATE curState = m_pState->GetCurState(); + switch(curState) { + // document-level + case ES_WRITING_DOC: + m_pState->SwitchState(ES_DONE_WITH_DOC); + break; + + // block seq + case ES_WRITING_BLOCK_SEQ_ENTRY: + m_pState->SwitchState(ES_DONE_WITH_BLOCK_SEQ_ENTRY); + break; + + // flow seq + case ES_WRITING_FLOW_SEQ_ENTRY: + m_pState->SwitchState(ES_DONE_WITH_FLOW_SEQ_ENTRY); + break; + + // block map + case ES_WRITING_BLOCK_MAP_KEY: + m_pState->SwitchState(ES_DONE_WITH_BLOCK_MAP_KEY); + break; + case ES_WRITING_BLOCK_MAP_VALUE: + m_pState->SwitchState(ES_DONE_WITH_BLOCK_MAP_VALUE); + break; + + // flow map + case ES_WRITING_FLOW_MAP_KEY: + m_pState->SwitchState(ES_DONE_WITH_FLOW_MAP_KEY); + break; + case ES_WRITING_FLOW_MAP_VALUE: + m_pState->SwitchState(ES_DONE_WITH_FLOW_MAP_VALUE); + break; + default: + assert(false); + }; + + m_pState->ClearModifiedSettings(); + } + + // EmitSeparationIfNecessary + void Emitter::EmitSeparationIfNecessary() + { + if(!good()) + return; + + if(m_pState->RequiresSeparation()) + m_stream << ' '; + m_pState->UnsetSeparation(); + } + + // EmitBeginSeq + void Emitter::EmitBeginSeq() + { + if(!good()) + return; + + // must have a long key if we're emitting a sequence + m_pState->StartLongKey(); + + PreAtomicWrite(); + + EMITTER_STATE curState = m_pState->GetCurState(); + EMITTER_MANIP flowType = m_pState->GetFlowType(GT_SEQ); + if(flowType == Block) { + if(curState == ES_WRITING_BLOCK_SEQ_ENTRY || + curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE || + curState == ES_WRITING_DOC + ) { + m_stream << "\n"; + m_pState->UnsetSeparation(); + } + m_pState->PushState(ES_WAITING_FOR_BLOCK_SEQ_ENTRY); + } else if(flowType == Flow) { + EmitSeparationIfNecessary(); + m_stream << "["; + m_pState->PushState(ES_WAITING_FOR_FLOW_SEQ_ENTRY); + } else + assert(false); + + m_pState->BeginGroup(GT_SEQ); + } + + // EmitEndSeq + void Emitter::EmitEndSeq() + { + if(!good()) + return; + + if(m_pState->GetCurGroupType() != GT_SEQ) + return m_pState->SetError(ErrorMsg::UNEXPECTED_END_SEQ); + + EMITTER_STATE curState = m_pState->GetCurState(); + FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); + if(flowType == FT_BLOCK) { + // Note: block sequences are *not* allowed to be empty, but we convert it + // to a flow sequence if it is + assert(curState == ES_DONE_WITH_BLOCK_SEQ_ENTRY || curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY); + if(curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY) { + // Note: only one of these will actually output anything for a given situation + EmitSeparationIfNecessary(); + unsigned curIndent = m_pState->GetCurIndent(); + m_stream << IndentTo(curIndent); + + m_stream << "[]"; + } + } else if(flowType == FT_FLOW) { + // Note: flow sequences are allowed to be empty + assert(curState == ES_DONE_WITH_FLOW_SEQ_ENTRY || curState == ES_WAITING_FOR_FLOW_SEQ_ENTRY); + m_stream << "]"; + } else + assert(false); + + m_pState->PopState(); + m_pState->EndGroup(GT_SEQ); + + PostAtomicWrite(); + } + + // EmitBeginMap + void Emitter::EmitBeginMap() + { + if(!good()) + return; + + // must have a long key if we're emitting a map + m_pState->StartLongKey(); + + PreAtomicWrite(); + + EMITTER_STATE curState = m_pState->GetCurState(); + EMITTER_MANIP flowType = m_pState->GetFlowType(GT_MAP); + if(flowType == Block) { + if(curState == ES_WRITING_BLOCK_SEQ_ENTRY || + curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE || + curState == ES_WRITING_DOC + ) { + m_stream << "\n"; + m_pState->UnsetSeparation(); + } + m_pState->PushState(ES_WAITING_FOR_BLOCK_MAP_ENTRY); + } else if(flowType == Flow) { + EmitSeparationIfNecessary(); + m_stream << "{"; + m_pState->PushState(ES_WAITING_FOR_FLOW_MAP_ENTRY); + } else + assert(false); + + m_pState->BeginGroup(GT_MAP); + } + + // EmitEndMap + void Emitter::EmitEndMap() + { + if(!good()) + return; + + if(m_pState->GetCurGroupType() != GT_MAP) + return m_pState->SetError(ErrorMsg::UNEXPECTED_END_MAP); + + EMITTER_STATE curState = m_pState->GetCurState(); + FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); + if(flowType == FT_BLOCK) { + // Note: block sequences are *not* allowed to be empty, but we convert it + // to a flow sequence if it is + assert(curState == ES_DONE_WITH_BLOCK_MAP_VALUE || curState == ES_WAITING_FOR_BLOCK_MAP_ENTRY); + if(curState == ES_WAITING_FOR_BLOCK_MAP_ENTRY) { + // Note: only one of these will actually output anything for a given situation + EmitSeparationIfNecessary(); + unsigned curIndent = m_pState->GetCurIndent(); + m_stream << IndentTo(curIndent); + m_stream << "{}"; + } + } else if(flowType == FT_FLOW) { + // Note: flow maps are allowed to be empty + assert(curState == ES_DONE_WITH_FLOW_MAP_VALUE || curState == ES_WAITING_FOR_FLOW_MAP_ENTRY); + m_stream << "}"; + } else + assert(false); + + m_pState->PopState(); + m_pState->EndGroup(GT_MAP); + + PostAtomicWrite(); + } + + // EmitKey + void Emitter::EmitKey() + { + if(!good()) + return; + + EMITTER_STATE curState = m_pState->GetCurState(); + FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); + if(curState != ES_WAITING_FOR_BLOCK_MAP_ENTRY && curState != ES_DONE_WITH_BLOCK_MAP_VALUE + && curState != ES_WAITING_FOR_FLOW_MAP_ENTRY && curState != ES_DONE_WITH_FLOW_MAP_VALUE) + return m_pState->SetError(ErrorMsg::UNEXPECTED_KEY_TOKEN); + + if(flowType == FT_BLOCK) { + if(curState == ES_DONE_WITH_BLOCK_MAP_VALUE) + m_stream << '\n'; + unsigned curIndent = m_pState->GetCurIndent(); + m_stream << IndentTo(curIndent); + m_pState->SwitchState(ES_WAITING_FOR_BLOCK_MAP_KEY); + } else if(flowType == FT_FLOW) { + if(curState == ES_DONE_WITH_FLOW_MAP_VALUE) { + m_stream << ','; + m_pState->RequireSeparation(); + } + m_pState->SwitchState(ES_WAITING_FOR_FLOW_MAP_KEY); + } else + assert(false); + + if(m_pState->GetMapKeyFormat() == LongKey) + m_pState->StartLongKey(); + else if(m_pState->GetMapKeyFormat() == Auto) + m_pState->StartSimpleKey(); + else + assert(false); + } + + // EmitValue + void Emitter::EmitValue() + { + if(!good()) + return; + + EMITTER_STATE curState = m_pState->GetCurState(); + FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); + if(curState != ES_DONE_WITH_BLOCK_MAP_KEY && curState != ES_DONE_WITH_FLOW_MAP_KEY) + return m_pState->SetError(ErrorMsg::UNEXPECTED_VALUE_TOKEN); + + if(flowType == FT_BLOCK) { + if(m_pState->CurrentlyInLongKey()) + m_stream << '\n'; + m_pState->SwitchState(ES_WAITING_FOR_BLOCK_MAP_VALUE); + } else if(flowType == FT_FLOW) { + m_pState->SwitchState(ES_WAITING_FOR_FLOW_MAP_VALUE); + } else + assert(false); + } + + // ******************************************************************************************* + // overloads of Write + + Emitter& Emitter::Write(const std::string& str) + { + if(!good()) + return *this; + + // literal scalars must use long keys + if(m_pState->GetStringFormat() == Literal && m_pState->GetCurGroupFlowType() != FT_FLOW) + m_pState->StartLongKey(); + + PreAtomicWrite(); + EmitSeparationIfNecessary(); + + bool escapeNonAscii = m_pState->GetOutputCharset() == EscapeNonAscii; + EMITTER_MANIP strFmt = m_pState->GetStringFormat(); + FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); + unsigned curIndent = m_pState->GetCurIndent(); + + switch(strFmt) { + case Auto: + Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii); + break; + case SingleQuoted: + if(!Utils::WriteSingleQuotedString(m_stream, str)) { + m_pState->SetError(ErrorMsg::SINGLE_QUOTED_CHAR); + return *this; + } + break; + case DoubleQuoted: + Utils::WriteDoubleQuotedString(m_stream, str, escapeNonAscii); + break; + case Literal: + if(flowType == FT_FLOW) + Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii); + else + Utils::WriteLiteralString(m_stream, str, curIndent + m_pState->GetIndent()); + break; + default: + assert(false); + } + + PostAtomicWrite(); + return *this; + } + + void Emitter::PreWriteIntegralType(std::stringstream& str) + { + PreAtomicWrite(); + EmitSeparationIfNecessary(); + + EMITTER_MANIP intFmt = m_pState->GetIntFormat(); + switch(intFmt) { + case Dec: + str << std::dec; + break; + case Hex: + str << std::hex; + break; + case Oct: + str << std::oct; + break; + default: + assert(false); + } + } + + void Emitter::PostWriteIntegralType(const std::stringstream& str) + { + m_stream << str.str(); + PostAtomicWrite(); + } + + Emitter& Emitter::Write(bool b) + { + if(!good()) + return *this; + + PreAtomicWrite(); + EmitSeparationIfNecessary(); + + // set up all possible bools to write + struct BoolName { std::string trueName, falseName; }; + struct BoolFormatNames { BoolName upper, lower, camel; }; + struct BoolTypes { BoolFormatNames yesNo, trueFalse, onOff; }; + + static const BoolTypes boolTypes = { + { { "YES", "NO" }, { "yes", "no" }, { "Yes", "No" } }, + { { "TRUE", "FALSE" }, { "true", "false" }, { "True", "False" } }, + { { "ON", "OFF" }, { "on", "off" }, { "On", "Off" } } + }; + + // select the right one + EMITTER_MANIP boolFmt = m_pState->GetBoolFormat(); + EMITTER_MANIP boolLengthFmt = m_pState->GetBoolLengthFormat(); + EMITTER_MANIP boolCaseFmt = m_pState->GetBoolCaseFormat(); + + const BoolFormatNames& fmtNames = (boolFmt == YesNoBool ? boolTypes.yesNo : boolFmt == TrueFalseBool ? boolTypes.trueFalse : boolTypes.onOff); + const BoolName& boolName = (boolCaseFmt == UpperCase ? fmtNames.upper : boolCaseFmt == LowerCase ? fmtNames.lower : fmtNames.camel); + const std::string& name = (b ? boolName.trueName : boolName.falseName); + + // and say it! + // TODO: should we disallow writing OnOffBool with ShortBool? (it'll just print "o" for both, which is silly) + if(boolLengthFmt == ShortBool) + m_stream << name[0]; + else + m_stream << name; + + PostAtomicWrite(); + return *this; + } + + Emitter& Emitter::Write(const _Alias& alias) + { + if(!good()) + return *this; + + PreAtomicWrite(); + EmitSeparationIfNecessary(); + if(!Utils::WriteAlias(m_stream, alias.content)) { + m_pState->SetError(ErrorMsg::INVALID_ALIAS); + return *this; + } + PostAtomicWrite(); + return *this; + } + + Emitter& Emitter::Write(const _Anchor& anchor) + { + if(!good()) + return *this; + + PreAtomicWrite(); + EmitSeparationIfNecessary(); + if(!Utils::WriteAnchor(m_stream, anchor.content)) { + m_pState->SetError(ErrorMsg::INVALID_ANCHOR); + return *this; + } + m_pState->RequireSeparation(); + // Note: no PostAtomicWrite() because we need another value for this node + return *this; + } + + Emitter& Emitter::Write(const _Tag& tag) + { + if(!good()) + return *this; + + PreAtomicWrite(); + EmitSeparationIfNecessary(); + if(!Utils::WriteTag(m_stream, tag.content)) { + m_pState->SetError(ErrorMsg::INVALID_TAG); + return *this; + } + m_pState->RequireSeparation(); + // Note: no PostAtomicWrite() because we need another value for this node + return *this; + } + + Emitter& Emitter::Write(const _Comment& comment) + { + if(!good()) + return *this; + + m_stream << Indentation(m_pState->GetPreCommentIndent()); + Utils::WriteComment(m_stream, comment.content, m_pState->GetPostCommentIndent()); + return *this; + } + + Emitter& Emitter::Write(const _Null& /*null*/) + { + if(!good()) + return *this; + + PreAtomicWrite(); + EmitSeparationIfNecessary(); + m_stream << "~"; + PostAtomicWrite(); + return *this; + } +} + diff --git a/libs/yaml-cpp/src/emitterstate.cpp b/libs/yaml-cpp/src/emitterstate.cpp new file mode 100644 index 0000000..2906679 --- /dev/null +++ b/libs/yaml-cpp/src/emitterstate.cpp @@ -0,0 +1,277 @@ +#include "emitterstate.h" +#include "exceptions.h" + +namespace YAML +{ + EmitterState::EmitterState(): m_isGood(true), m_curIndent(0), m_requiresSeparation(false) + { + // start up + m_stateStack.push(ES_WAITING_FOR_DOC); + + // set default global manipulators + m_charset.set(EmitNonAscii); + m_strFmt.set(Auto); + m_boolFmt.set(TrueFalseBool); + m_boolLengthFmt.set(LongBool); + m_boolCaseFmt.set(LowerCase); + m_intFmt.set(Dec); + m_indent.set(2); + m_preCommentIndent.set(2); + m_postCommentIndent.set(1); + m_seqFmt.set(Block); + m_mapFmt.set(Block); + m_mapKeyFmt.set(Auto); + } + + EmitterState::~EmitterState() + { + while(!m_groups.empty()) + _PopGroup(); + } + + std::auto_ptr EmitterState::_PopGroup() + { + if(m_groups.empty()) + return std::auto_ptr (0); + + std::auto_ptr pGroup(m_groups.top()); + m_groups.pop(); + return pGroup; + } + + // SetLocalValue + // . We blindly tries to set all possible formatters to this value + // . Only the ones that make sense will be accepted + void EmitterState::SetLocalValue(EMITTER_MANIP value) + { + SetOutputCharset(value, LOCAL); + SetStringFormat(value, LOCAL); + SetBoolFormat(value, LOCAL); + SetBoolCaseFormat(value, LOCAL); + SetBoolLengthFormat(value, LOCAL); + SetIntFormat(value, LOCAL); + SetFlowType(GT_SEQ, value, LOCAL); + SetFlowType(GT_MAP, value, LOCAL); + SetMapKeyFormat(value, LOCAL); + } + + void EmitterState::BeginGroup(GROUP_TYPE type) + { + unsigned lastIndent = (m_groups.empty() ? 0 : m_groups.top()->indent); + m_curIndent += lastIndent; + + std::auto_ptr pGroup(new Group(type)); + + // transfer settings (which last until this group is done) + pGroup->modifiedSettings = m_modifiedSettings; + + // set up group + pGroup->flow = GetFlowType(type); + pGroup->indent = GetIndent(); + pGroup->usingLongKey = (GetMapKeyFormat() == LongKey ? true : false); + + m_groups.push(pGroup.release()); + } + + void EmitterState::EndGroup(GROUP_TYPE type) + { + if(m_groups.empty()) + return SetError(ErrorMsg::UNMATCHED_GROUP_TAG); + + // get rid of the current group + { + std::auto_ptr pFinishedGroup = _PopGroup(); + if(pFinishedGroup->type != type) + return SetError(ErrorMsg::UNMATCHED_GROUP_TAG); + } + + // reset old settings + unsigned lastIndent = (m_groups.empty() ? 0 : m_groups.top()->indent); + assert(m_curIndent >= lastIndent); + m_curIndent -= lastIndent; + + // some global settings that we changed may have been overridden + // by a local setting we just popped, so we need to restore them + m_globalModifiedSettings.restore(); + } + + GROUP_TYPE EmitterState::GetCurGroupType() const + { + if(m_groups.empty()) + return GT_NONE; + + return m_groups.top()->type; + } + + FLOW_TYPE EmitterState::GetCurGroupFlowType() const + { + if(m_groups.empty()) + return FT_NONE; + + return (m_groups.top()->flow == Flow ? FT_FLOW : FT_BLOCK); + } + + bool EmitterState::CurrentlyInLongKey() + { + if(m_groups.empty()) + return false; + return m_groups.top()->usingLongKey; + } + + void EmitterState::StartLongKey() + { + if(!m_groups.empty()) + m_groups.top()->usingLongKey = true; + } + + void EmitterState::StartSimpleKey() + { + if(!m_groups.empty()) + m_groups.top()->usingLongKey = false; + } + + void EmitterState::ClearModifiedSettings() + { + m_modifiedSettings.clear(); + } + + bool EmitterState::SetOutputCharset(EMITTER_MANIP value, FMT_SCOPE scope) + { + switch(value) { + case EmitNonAscii: + case EscapeNonAscii: + _Set(m_charset, value, scope); + return true; + default: + return false; + } + } + + bool EmitterState::SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope) + { + switch(value) { + case Auto: + case SingleQuoted: + case DoubleQuoted: + case Literal: + _Set(m_strFmt, value, scope); + return true; + default: + return false; + } + } + + bool EmitterState::SetBoolFormat(EMITTER_MANIP value, FMT_SCOPE scope) + { + switch(value) { + case OnOffBool: + case TrueFalseBool: + case YesNoBool: + _Set(m_boolFmt, value, scope); + return true; + default: + return false; + } + } + + bool EmitterState::SetBoolLengthFormat(EMITTER_MANIP value, FMT_SCOPE scope) + { + switch(value) { + case LongBool: + case ShortBool: + _Set(m_boolLengthFmt, value, scope); + return true; + default: + return false; + } + } + + bool EmitterState::SetBoolCaseFormat(EMITTER_MANIP value, FMT_SCOPE scope) + { + switch(value) { + case UpperCase: + case LowerCase: + case CamelCase: + _Set(m_boolCaseFmt, value, scope); + return true; + default: + return false; + } + } + + bool EmitterState::SetIntFormat(EMITTER_MANIP value, FMT_SCOPE scope) + { + switch(value) { + case Dec: + case Hex: + case Oct: + _Set(m_intFmt, value, scope); + return true; + default: + return false; + } + } + + bool EmitterState::SetIndent(unsigned value, FMT_SCOPE scope) + { + if(value == 0) + return false; + + _Set(m_indent, value, scope); + return true; + } + + bool EmitterState::SetPreCommentIndent(unsigned value, FMT_SCOPE scope) + { + if(value == 0) + return false; + + _Set(m_preCommentIndent, value, scope); + return true; + } + + bool EmitterState::SetPostCommentIndent(unsigned value, FMT_SCOPE scope) + { + if(value == 0) + return false; + + _Set(m_postCommentIndent, value, scope); + return true; + } + + bool EmitterState::SetFlowType(GROUP_TYPE groupType, EMITTER_MANIP value, FMT_SCOPE scope) + { + switch(value) { + case Block: + case Flow: + _Set(groupType == GT_SEQ ? m_seqFmt : m_mapFmt, value, scope); + return true; + default: + return false; + } + } + + EMITTER_MANIP EmitterState::GetFlowType(GROUP_TYPE groupType) const + { + // force flow style if we're currently in a flow + FLOW_TYPE flowType = GetCurGroupFlowType(); + if(flowType == FT_FLOW) + return Flow; + + // otherwise, go with what's asked of use + return (groupType == GT_SEQ ? m_seqFmt.get() : m_mapFmt.get()); + } + + bool EmitterState::SetMapKeyFormat(EMITTER_MANIP value, FMT_SCOPE scope) + { + switch(value) { + case Auto: + case LongKey: + _Set(m_mapKeyFmt, value, scope); + return true; + default: + return false; + } + } +} + diff --git a/libs/yaml-cpp/src/emitterstate.h b/libs/yaml-cpp/src/emitterstate.h new file mode 100644 index 0000000..b5be269 --- /dev/null +++ b/libs/yaml-cpp/src/emitterstate.h @@ -0,0 +1,205 @@ +#pragma once + +#ifndef EMITTERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define EMITTERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "setting.h" +#include "emittermanip.h" +#include +#include +#include +#include + +namespace YAML +{ + enum FMT_SCOPE { + LOCAL, + GLOBAL + }; + + enum GROUP_TYPE { + GT_NONE, + GT_SEQ, + GT_MAP + }; + + enum FLOW_TYPE { + FT_NONE, + FT_FLOW, + FT_BLOCK + }; + + enum NODE_STATE { + NS_START, + NS_READY_FOR_ATOM, + NS_END + }; + + enum EMITTER_STATE { + ES_WAITING_FOR_DOC, + ES_WRITING_DOC, + ES_DONE_WITH_DOC, + + // block seq + ES_WAITING_FOR_BLOCK_SEQ_ENTRY, + ES_WRITING_BLOCK_SEQ_ENTRY, + ES_DONE_WITH_BLOCK_SEQ_ENTRY, + + // flow seq + ES_WAITING_FOR_FLOW_SEQ_ENTRY, + ES_WRITING_FLOW_SEQ_ENTRY, + ES_DONE_WITH_FLOW_SEQ_ENTRY, + + // block map + ES_WAITING_FOR_BLOCK_MAP_ENTRY, + ES_WAITING_FOR_BLOCK_MAP_KEY, + ES_WRITING_BLOCK_MAP_KEY, + ES_DONE_WITH_BLOCK_MAP_KEY, + ES_WAITING_FOR_BLOCK_MAP_VALUE, + ES_WRITING_BLOCK_MAP_VALUE, + ES_DONE_WITH_BLOCK_MAP_VALUE, + + // flow map + ES_WAITING_FOR_FLOW_MAP_ENTRY, + ES_WAITING_FOR_FLOW_MAP_KEY, + ES_WRITING_FLOW_MAP_KEY, + ES_DONE_WITH_FLOW_MAP_KEY, + ES_WAITING_FOR_FLOW_MAP_VALUE, + ES_WRITING_FLOW_MAP_VALUE, + ES_DONE_WITH_FLOW_MAP_VALUE + }; + + class EmitterState + { + public: + EmitterState(); + ~EmitterState(); + + // basic state checking + bool good() const { return m_isGood; } + const std::string GetLastError() const { return m_lastError; } + void SetError(const std::string& error) { m_isGood = false; m_lastError = error; } + + // main state of the machine + EMITTER_STATE GetCurState() const { return m_stateStack.top(); } + void SwitchState(EMITTER_STATE state) { PopState(); PushState(state); } + void PushState(EMITTER_STATE state) { m_stateStack.push(state); } + void PopState() { m_stateStack.pop(); } + + void SetLocalValue(EMITTER_MANIP value); + + // group handling + void BeginGroup(GROUP_TYPE type); + void EndGroup(GROUP_TYPE type); + + GROUP_TYPE GetCurGroupType() const; + FLOW_TYPE GetCurGroupFlowType() const; + int GetCurIndent() const { return m_curIndent; } + + bool CurrentlyInLongKey(); + void StartLongKey(); + void StartSimpleKey(); + + bool RequiresSeparation() const { return m_requiresSeparation; } + void RequireSeparation() { m_requiresSeparation = true; } + void UnsetSeparation() { m_requiresSeparation = false; } + + void ClearModifiedSettings(); + + // formatters + bool SetOutputCharset(EMITTER_MANIP value, FMT_SCOPE scope); + EMITTER_MANIP GetOutputCharset() const { return m_charset.get(); } + + bool SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope); + EMITTER_MANIP GetStringFormat() const { return m_strFmt.get(); } + + bool SetBoolFormat(EMITTER_MANIP value, FMT_SCOPE scope); + EMITTER_MANIP GetBoolFormat() const { return m_boolFmt.get(); } + + bool SetBoolLengthFormat(EMITTER_MANIP value, FMT_SCOPE scope); + EMITTER_MANIP GetBoolLengthFormat() const { return m_boolLengthFmt.get(); } + + bool SetBoolCaseFormat(EMITTER_MANIP value, FMT_SCOPE scope); + EMITTER_MANIP GetBoolCaseFormat() const { return m_boolCaseFmt.get(); } + + bool SetIntFormat(EMITTER_MANIP value, FMT_SCOPE scope); + EMITTER_MANIP GetIntFormat() const { return m_intFmt.get(); } + + bool SetIndent(unsigned value, FMT_SCOPE scope); + int GetIndent() const { return m_indent.get(); } + + bool SetPreCommentIndent(unsigned value, FMT_SCOPE scope); + int GetPreCommentIndent() const { return m_preCommentIndent.get(); } + bool SetPostCommentIndent(unsigned value, FMT_SCOPE scope); + int GetPostCommentIndent() const { return m_postCommentIndent.get(); } + + bool SetFlowType(GROUP_TYPE groupType, EMITTER_MANIP value, FMT_SCOPE scope); + EMITTER_MANIP GetFlowType(GROUP_TYPE groupType) const; + + bool SetMapKeyFormat(EMITTER_MANIP value, FMT_SCOPE scope); + EMITTER_MANIP GetMapKeyFormat() const { return m_mapKeyFmt.get(); } + + private: + template + void _Set(Setting& fmt, T value, FMT_SCOPE scope); + + private: + // basic state ok? + bool m_isGood; + std::string m_lastError; + + // other state + std::stack m_stateStack; + + Setting m_charset; + Setting m_strFmt; + Setting m_boolFmt; + Setting m_boolLengthFmt; + Setting m_boolCaseFmt; + Setting m_intFmt; + Setting m_indent; + Setting m_preCommentIndent, m_postCommentIndent; + Setting m_seqFmt; + Setting m_mapFmt; + Setting m_mapKeyFmt; + + SettingChanges m_modifiedSettings; + SettingChanges m_globalModifiedSettings; + + struct Group { + Group(GROUP_TYPE type_): type(type_), usingLongKey(false), indent(0) {} + + GROUP_TYPE type; + EMITTER_MANIP flow; + bool usingLongKey; + int indent; + + SettingChanges modifiedSettings; + }; + + std::auto_ptr _PopGroup(); + + std::stack m_groups; + unsigned m_curIndent; + bool m_requiresSeparation; + }; + + template + void EmitterState::_Set(Setting& fmt, T value, FMT_SCOPE scope) { + switch(scope) { + case LOCAL: + m_modifiedSettings.push(fmt.set(value)); + break; + case GLOBAL: + fmt.set(value); + m_globalModifiedSettings.push(fmt.set(value)); // this pushes an identity set, so when we restore, + // it restores to the value here, and not the previous one + break; + default: + assert(false); + } + } +} + +#endif // EMITTERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/emitterutils.cpp b/libs/yaml-cpp/src/emitterutils.cpp new file mode 100644 index 0000000..62a7ef3 --- /dev/null +++ b/libs/yaml-cpp/src/emitterutils.cpp @@ -0,0 +1,316 @@ +#include "emitterutils.h" +#include "exp.h" +#include "indentation.h" +#include "exceptions.h" +#include "stringsource.h" +#include +#include + +namespace YAML +{ + namespace Utils + { + namespace { + enum {REPLACEMENT_CHARACTER = 0xFFFD}; + + bool IsAnchorChar(int ch) { // test for ns-anchor-char + switch (ch) { + case ',': case '[': case ']': case '{': case '}': // c-flow-indicator + case ' ': case '\t': // s-white + case 0xFEFF: // c-byte-order-mark + case 0xA: case 0xD: // b-char + return false; + case 0x85: + return true; + } + + if (ch < 0x20) + return false; + + if (ch < 0x7E) + return true; + + if (ch < 0xA0) + return false; + if (ch >= 0xD800 && ch <= 0xDFFF) + return false; + if ((ch & 0xFFFE) == 0xFFFE) + return false; + if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) + return false; + if (ch > 0x10FFFF) + return false; + + return true; + } + + int Utf8BytesIndicated(char ch) { + int byteVal = static_cast(ch); + switch (byteVal >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + return 1; + case 12: case 13: + return 2; + case 14: + return 3; + case 15: + return 4; + default: + return -1; + } + } + + bool IsTrailingByte(char ch) { + return (ch & 0xC0) == 0x80; + } + + bool GetNextCodePointAndAdvance(int& codePoint, std::string::const_iterator& first, std::string::const_iterator last) { + if (first == last) + return false; + + int nBytes = Utf8BytesIndicated(*first); + if (nBytes < 1) { + // Bad lead byte + ++first; + codePoint = REPLACEMENT_CHARACTER; + return true; + } + + if (nBytes == 1) { + codePoint = *first++; + return true; + } + + // Gather bits from trailing bytes + codePoint = static_cast(*first) & ~(0xFF << (7 - nBytes)); + ++first; + --nBytes; + for (; nBytes > 0; ++first, --nBytes) { + if ((first == last) || !IsTrailingByte(*first)) { + codePoint = REPLACEMENT_CHARACTER; + break; + } + codePoint <<= 6; + codePoint |= *first & 0x3F; + } + + // Check for illegal code points + if (codePoint > 0x10FFFF) + codePoint = REPLACEMENT_CHARACTER; + else if (codePoint >= 0xD800 && codePoint <= 0xDFFF) + codePoint = REPLACEMENT_CHARACTER; + else if ((codePoint & 0xFFFE) == 0xFFFE) + codePoint = REPLACEMENT_CHARACTER; + else if (codePoint >= 0xFDD0 && codePoint <= 0xFDEF) + codePoint = REPLACEMENT_CHARACTER; + return true; + } + + void WriteCodePoint(ostream& out, int codePoint) { + if (codePoint < 0 || codePoint > 0x10FFFF) { + codePoint = REPLACEMENT_CHARACTER; + } + if (codePoint < 0x7F) { + out << static_cast(codePoint); + } else if (codePoint < 0x7FF) { + out << static_cast(0xC0 | (codePoint >> 6)) + << static_cast(0x80 | (codePoint & 0x3F)); + } else if (codePoint < 0xFFFF) { + out << static_cast(0xE0 | (codePoint >> 12)) + << static_cast(0x80 | ((codePoint >> 6) & 0x3F)) + << static_cast(0x80 | (codePoint & 0x3F)); + } else { + out << static_cast(0xF0 | (codePoint >> 18)) + << static_cast(0x80 | ((codePoint >> 12) & 0x3F)) + << static_cast(0x80 | ((codePoint >> 6) & 0x3F)) + << static_cast(0x80 | (codePoint & 0x3F)); + } + } + + bool IsValidPlainScalar(const std::string& str, bool inFlow, bool allowOnlyAscii) { + // first check the start + const RegEx& start = (inFlow ? Exp::PlainScalarInFlow() : Exp::PlainScalar()); + if(!start.Matches(str)) + return false; + + // and check the end for plain whitespace (which can't be faithfully kept in a plain scalar) + if(!str.empty() && *str.rbegin() == ' ') + return false; + + // then check until something is disallowed + const RegEx& disallowed = (inFlow ? Exp::EndScalarInFlow() : Exp::EndScalar()) + || (Exp::BlankOrBreak() + Exp::Comment()) + || Exp::NotPrintable() + || Exp::Utf8_ByteOrderMark() + || Exp::Break() + || Exp::Tab(); + StringCharSource buffer(str.c_str(), str.size()); + while(buffer) { + if(disallowed.Matches(buffer)) + return false; + if(allowOnlyAscii && (0x7F < static_cast(buffer[0]))) + return false; + ++buffer; + } + + return true; + } + + void WriteDoubleQuoteEscapeSequence(ostream& out, int codePoint) { + static const char hexDigits[] = "0123456789abcdef"; + + char escSeq[] = "\\U00000000"; + int digits = 8; + if (codePoint < 0xFF) { + escSeq[1] = 'x'; + digits = 2; + } else if (codePoint < 0xFFFF) { + escSeq[1] = 'u'; + digits = 4; + } + + // Write digits into the escape sequence + int i = 2; + for (; digits > 0; --digits, ++i) { + escSeq[i] = hexDigits[(codePoint >> (4 * (digits - 1))) & 0xF]; + } + + escSeq[i] = 0; // terminate with NUL character + out << escSeq; + } + + bool WriteAliasName(ostream& out, const std::string& str) { + int codePoint; + for(std::string::const_iterator i = str.begin(); + GetNextCodePointAndAdvance(codePoint, i, str.end()); + ) + { + if (!IsAnchorChar(codePoint)) + return false; + + WriteCodePoint(out, codePoint); + } + return true; + } + } + + bool WriteString(ostream& out, const std::string& str, bool inFlow, bool escapeNonAscii) + { + if(IsValidPlainScalar(str, inFlow, escapeNonAscii)) { + out << str; + return true; + } else + return WriteDoubleQuotedString(out, str, escapeNonAscii); + } + + bool WriteSingleQuotedString(ostream& out, const std::string& str) + { + out << "'"; + int codePoint; + for(std::string::const_iterator i = str.begin(); + GetNextCodePointAndAdvance(codePoint, i, str.end()); + ) + { + if (codePoint == '\n') + return false; // We can't handle a new line and the attendant indentation yet + + if (codePoint == '\'') + out << "''"; + else + WriteCodePoint(out, codePoint); + } + out << "'"; + return true; + } + + bool WriteDoubleQuotedString(ostream& out, const std::string& str, bool escapeNonAscii) + { + out << "\""; + int codePoint; + for(std::string::const_iterator i = str.begin(); + GetNextCodePointAndAdvance(codePoint, i, str.end()); + ) + { + if (codePoint == '\"') + out << "\\\""; + else if (codePoint == '\\') + out << "\\\\"; + else if (codePoint < 0x20 || (codePoint >= 0x80 && codePoint <= 0xA0)) // Control characters and non-breaking space + WriteDoubleQuoteEscapeSequence(out, codePoint); + else if (codePoint == 0xFEFF) // Byte order marks (ZWNS) should be escaped (YAML 1.2, sec. 5.2) + WriteDoubleQuoteEscapeSequence(out, codePoint); + else if (escapeNonAscii && codePoint > 0x7E) + WriteDoubleQuoteEscapeSequence(out, codePoint); + else + WriteCodePoint(out, codePoint); + } + out << "\""; + return true; + } + + bool WriteLiteralString(ostream& out, const std::string& str, int indent) + { + out << "|\n"; + out << IndentTo(indent); + int codePoint; + for(std::string::const_iterator i = str.begin(); + GetNextCodePointAndAdvance(codePoint, i, str.end()); + ) + { + if (codePoint == '\n') + out << "\n" << IndentTo(indent); + else + WriteCodePoint(out, codePoint); + } + return true; + } + + bool WriteComment(ostream& out, const std::string& str, int postCommentIndent) + { + unsigned curIndent = out.col(); + out << "#" << Indentation(postCommentIndent); + int codePoint; + for(std::string::const_iterator i = str.begin(); + GetNextCodePointAndAdvance(codePoint, i, str.end()); + ) + { + if(codePoint == '\n') + out << "\n" << IndentTo(curIndent) << "#" << Indentation(postCommentIndent); + else + WriteCodePoint(out, codePoint); + } + return true; + } + + bool WriteAlias(ostream& out, const std::string& str) + { + out << "*"; + return WriteAliasName(out, str); + } + + bool WriteAnchor(ostream& out, const std::string& str) + { + out << "&"; + return WriteAliasName(out, str); + } + + bool WriteTag(ostream& out, const std::string& str) + { + out << "!<"; + StringCharSource buffer(str.c_str(), str.size()); + while(buffer) { + int n = Exp::URI().Match(buffer); + if(n <= 0) + return false; + + while(--n >= 0) { + out << buffer[0]; + ++buffer; + } + } + out << ">"; + return true; + } + } +} + diff --git a/libs/yaml-cpp/src/emitterutils.h b/libs/yaml-cpp/src/emitterutils.h new file mode 100644 index 0000000..7ceb6ff --- /dev/null +++ b/libs/yaml-cpp/src/emitterutils.h @@ -0,0 +1,25 @@ +#pragma once + +#ifndef EMITTERUTILS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define EMITTERUTILS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "ostream.h" +#include + +namespace YAML +{ + namespace Utils + { + bool WriteString(ostream& out, const std::string& str, bool inFlow, bool escapeNonAscii); + bool WriteSingleQuotedString(ostream& out, const std::string& str); + bool WriteDoubleQuotedString(ostream& out, const std::string& str, bool escapeNonAscii); + bool WriteLiteralString(ostream& out, const std::string& str, int indent); + bool WriteComment(ostream& out, const std::string& str, int postCommentIndent); + bool WriteAlias(ostream& out, const std::string& str); + bool WriteAnchor(ostream& out, const std::string& str); + bool WriteTag(ostream& out, const std::string& str); + } +} + +#endif // EMITTERUTILS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/exp.cpp b/libs/yaml-cpp/src/exp.cpp new file mode 100644 index 0000000..8dd3479 --- /dev/null +++ b/libs/yaml-cpp/src/exp.cpp @@ -0,0 +1,113 @@ +#include "exp.h" +#include "exceptions.h" +#include + +namespace YAML +{ + namespace Exp + { + unsigned ParseHex(const std::string& str, const Mark& mark) + { + unsigned value = 0; + for(std::size_t i=0;i(ch)); + } + + // Escape + // . Translates the next 'codeLength' characters into a hex number and returns the result. + // . Throws if it's not actually hex. + std::string Escape(Stream& in, int codeLength) + { + // grab string + std::string str; + for(int i=0;i= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) { + std::stringstream msg; + msg << ErrorMsg::INVALID_UNICODE << value; + throw ParserException(in.mark(), msg.str()); + } + + // now break it up into chars + if(value <= 0x7F) + return Str(value); + else if(value <= 0x7FF) + return Str(0xC0 + (value >> 6)) + Str(0x80 + (value & 0x3F)); + else if(value <= 0xFFFF) + return Str(0xE0 + (value >> 12)) + Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F)); + else + return Str(0xF0 + (value >> 18)) + Str(0x80 + ((value >> 12) & 0x3F)) + + Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F)); + } + + // Escape + // . Escapes the sequence starting 'in' (it must begin with a '\' or single quote) + // and returns the result. + // . Throws if it's an unknown escape character. + std::string Escape(Stream& in) + { + // eat slash + char escape = in.get(); + + // switch on escape character + char ch = in.get(); + + // first do single quote, since it's easier + if(escape == '\'' && ch == '\'') + return "\'"; + + // now do the slash (we're not gonna check if it's a slash - you better pass one!) + switch(ch) { + case '0': return std::string(1, '\x00'); + case 'a': return "\x07"; + case 'b': return "\x08"; + case 't': + case '\t': return "\x09"; + case 'n': return "\x0A"; + case 'v': return "\x0B"; + case 'f': return "\x0C"; + case 'r': return "\x0D"; + case 'e': return "\x1B"; + case ' ': return "\x20"; + case '\"': return "\""; + case '\'': return "\'"; + case '\\': return "\\"; + case '/': return "/"; + case 'N': return "\x85"; + case '_': return "\xA0"; + case 'L': return "\xE2\x80\xA8"; // LS (#x2028) + case 'P': return "\xE2\x80\xA9"; // PS (#x2029) + case 'x': return Escape(in, 2); + case 'u': return Escape(in, 4); + case 'U': return Escape(in, 8); + } + + std::stringstream msg; + throw ParserException(in.mark(), ErrorMsg::INVALID_ESCAPE + ch); + } + } +} diff --git a/libs/yaml-cpp/src/exp.h b/libs/yaml-cpp/src/exp.h new file mode 100644 index 0000000..8b84214 --- /dev/null +++ b/libs/yaml-cpp/src/exp.h @@ -0,0 +1,190 @@ +#pragma once + +#ifndef EXP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define EXP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "regex.h" +#include +#include +#include "stream.h" + +namespace YAML +{ + //////////////////////////////////////////////////////////////////////////////// + // Here we store a bunch of expressions for matching different parts of the file. + + namespace Exp + { + // misc + inline const RegEx& Space() { + static const RegEx e = RegEx(' '); + return e; + } + inline const RegEx& Tab() { + static const RegEx e = RegEx('\t'); + return e; + } + inline const RegEx& Blank() { + static const RegEx e = Space() || Tab(); + return e; + } + inline const RegEx& Break() { + static const RegEx e = RegEx('\n') || RegEx("\r\n"); + return e; + } + inline const RegEx& BlankOrBreak() { + static const RegEx e = Blank() || Break(); + return e; + } + inline const RegEx& Digit() { + static const RegEx e = RegEx('0', '9'); + return e; + } + inline const RegEx& Alpha() { + static const RegEx e = RegEx('a', 'z') || RegEx('A', 'Z'); + return e; + } + inline const RegEx& AlphaNumeric() { + static const RegEx e = Alpha() || Digit(); + return e; + } + inline const RegEx& Word() { + static const RegEx e = AlphaNumeric() || RegEx('-'); + return e; + } + inline const RegEx& Hex() { + static const RegEx e = Digit() || RegEx('A', 'F') || RegEx('a', 'f'); + return e; + } + // Valid Unicode code points that are not part of c-printable (YAML 1.2, sec. 5.1) + inline const RegEx& NotPrintable() { + static const RegEx e = RegEx(0) || + RegEx("\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x7F", REGEX_OR) || + RegEx(0x0E, 0x1F) || + (RegEx('\xC2') + (RegEx('\x80', '\x84') || RegEx('\x86', '\x9F'))); + return e; + } + inline const RegEx& Utf8_ByteOrderMark() { + static const RegEx e = RegEx("\xEF\xBB\xBF"); + return e; + } + + // actual tags + + inline const RegEx& DocStart() { + static const RegEx e = RegEx("---") + (BlankOrBreak() || RegEx()); + return e; + } + inline const RegEx& DocEnd() { + static const RegEx e = RegEx("...") + (BlankOrBreak() || RegEx()); + return e; + } + inline const RegEx& DocIndicator() { + static const RegEx e = DocStart() || DocEnd(); + return e; + } + inline const RegEx& BlockEntry() { + static const RegEx e = RegEx('-') + (BlankOrBreak() || RegEx()); + return e; + } + inline const RegEx& Key() { + static const RegEx e = RegEx('?'); + return e; + } + inline const RegEx& KeyInFlow() { + static const RegEx e = RegEx('?') + BlankOrBreak(); + return e; + } + inline const RegEx& Value() { + static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx()); + return e; + } + inline const RegEx& ValueInFlow() { + static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx(",}", REGEX_OR)); + return e; + } + inline const RegEx& ValueInJSONFlow() { + static const RegEx e = RegEx(':'); + return e; + } + inline const RegEx Comment() { + static const RegEx e = RegEx('#'); + return e; + } + inline const RegEx& AnchorEnd() { + static const RegEx e = RegEx("?:,]}%@`", REGEX_OR) || BlankOrBreak(); + return e; + } + inline const RegEx& URI() { + static const RegEx e = Word() || RegEx("#;/?:@&=+$,_.!~*'()[]", REGEX_OR) || (RegEx('%') + Hex() + Hex()); + return e; + } + inline const RegEx& Tag() { + static const RegEx e = Word() || RegEx("#;/?:@&=+$_.~*'", REGEX_OR) || (RegEx('%') + Hex() + Hex()); + return e; + } + + // Plain scalar rules: + // . Cannot start with a blank. + // . Can never start with any of , [ ] { } # & * ! | > \' \" % @ ` + // . In the block context - ? : must be not be followed with a space. + // . In the flow context ? is illegal and : and - must not be followed with a space. + inline const RegEx& PlainScalar() { + static const RegEx e = !(BlankOrBreak() || RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-?:", REGEX_OR) + Blank())); + return e; + } + inline const RegEx& PlainScalarInFlow() { + static const RegEx e = !(BlankOrBreak() || RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-:", REGEX_OR) + Blank())); + return e; + } + inline const RegEx& EndScalar() { + static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx()); + return e; + } + inline const RegEx& EndScalarInFlow() { + static const RegEx e = (RegEx(':') + (BlankOrBreak() || RegEx(",]}", REGEX_OR))) || RegEx(",?[]{}", REGEX_OR); + return e; + } + + inline const RegEx& EscSingleQuote() { + static const RegEx e = RegEx("\'\'"); + return e; + } + inline const RegEx& EscBreak() { + static const RegEx e = RegEx('\\') + Break(); + return e; + } + + inline const RegEx& ChompIndicator() { + static const RegEx e = RegEx("+-", REGEX_OR); + return e; + } + inline const RegEx& Chomp() { + static const RegEx e = (ChompIndicator() + Digit()) || (Digit() + ChompIndicator()) || ChompIndicator() || Digit(); + return e; + } + + // and some functions + std::string Escape(Stream& in); + } + + namespace Keys + { + const char Directive = '%'; + const char FlowSeqStart = '['; + const char FlowSeqEnd = ']'; + const char FlowMapStart = '{'; + const char FlowMapEnd = '}'; + const char FlowEntry = ','; + const char Alias = '*'; + const char Anchor = '&'; + const char Tag = '!'; + const char LiteralScalar = '|'; + const char FoldedScalar = '>'; + const char VerbatimTagStart = '<'; + const char VerbatimTagEnd = '>'; + } +} + +#endif // EXP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/indentation.h b/libs/yaml-cpp/src/indentation.h new file mode 100644 index 0000000..2e2c967 --- /dev/null +++ b/libs/yaml-cpp/src/indentation.h @@ -0,0 +1,36 @@ +#pragma once + +#ifndef INDENTATION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define INDENTATION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "ostream.h" +#include + +namespace YAML +{ + struct Indentation { + Indentation(unsigned n_): n(n_) {} + unsigned n; + }; + + inline ostream& operator << (ostream& out, const Indentation& indent) { + for(unsigned i=0;itype == IterPriv::IT_SEQ) + ++m_pData->seqIter; + else if(m_pData->type == IterPriv::IT_MAP) + ++m_pData->mapIter; + + return *this; + } + + Iterator Iterator::operator ++ (int) + { + Iterator temp = *this; + + if(m_pData->type == IterPriv::IT_SEQ) + ++m_pData->seqIter; + else if(m_pData->type == IterPriv::IT_MAP) + ++m_pData->mapIter; + + return temp; + } + + const Node& Iterator::operator * () const + { + if(m_pData->type == IterPriv::IT_SEQ) + return **m_pData->seqIter; + + throw BadDereference(); + } + + const Node *Iterator::operator -> () const + { + if(m_pData->type == IterPriv::IT_SEQ) + return *m_pData->seqIter; + + throw BadDereference(); + } + + const Node& Iterator::first() const + { + if(m_pData->type == IterPriv::IT_MAP) + return *m_pData->mapIter->first; + + throw BadDereference(); + } + + const Node& Iterator::second() const + { + if(m_pData->type == IterPriv::IT_MAP) + return *m_pData->mapIter->second; + + throw BadDereference(); + } + + bool operator == (const Iterator& it, const Iterator& jt) + { + if(it.m_pData->type != jt.m_pData->type) + return false; + + if(it.m_pData->type == IterPriv::IT_SEQ) + return it.m_pData->seqIter == jt.m_pData->seqIter; + else if(it.m_pData->type == IterPriv::IT_MAP) + return it.m_pData->mapIter == jt.m_pData->mapIter; + + return true; + } + + bool operator != (const Iterator& it, const Iterator& jt) + { + return !(it == jt); + } +} diff --git a/libs/yaml-cpp/src/iterpriv.h b/libs/yaml-cpp/src/iterpriv.h new file mode 100644 index 0000000..d091fbc --- /dev/null +++ b/libs/yaml-cpp/src/iterpriv.h @@ -0,0 +1,31 @@ +#pragma once + +#ifndef ITERPRIV_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define ITERPRIV_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "ltnode.h" +#include +#include + +namespace YAML +{ + class Node; + + // IterPriv + // . The implementation for iterators - essentially a union of sequence and map iterators. + struct IterPriv + { + IterPriv(): type(IT_NONE) {} + IterPriv(std::vector ::const_iterator it): type(IT_SEQ), seqIter(it) {} + IterPriv(std::map ::const_iterator it): type(IT_MAP), mapIter(it) {} + + enum ITER_TYPE { IT_NONE, IT_SEQ, IT_MAP }; + ITER_TYPE type; + + std::vector ::const_iterator seqIter; + std::map ::const_iterator mapIter; + }; +} + +#endif // ITERPRIV_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/ltnode.h b/libs/yaml-cpp/src/ltnode.h new file mode 100644 index 0000000..c1672de --- /dev/null +++ b/libs/yaml-cpp/src/ltnode.h @@ -0,0 +1,16 @@ +#pragma once + +#ifndef LTNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define LTNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +namespace YAML +{ + class Node; + + struct ltnode { + bool operator()(const Node *pNode1, const Node *pNode2) const; + }; +} + +#endif // LTNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/map.cpp b/libs/yaml-cpp/src/map.cpp new file mode 100644 index 0000000..2db38b9 --- /dev/null +++ b/libs/yaml-cpp/src/map.cpp @@ -0,0 +1,238 @@ +#include "map.h" +#include "node.h" +#include "scanner.h" +#include "token.h" +#include "exceptions.h" +#include "emitter.h" + +namespace YAML +{ + Map::Map() + { + } + + Map::Map(const node_map& data) + { + for(node_map::const_iterator it=data.begin();it!=data.end();++it) { + std::auto_ptr pKey = it->first->Clone(); + std::auto_ptr pValue = it->second->Clone(); + AddEntry(pKey, pValue); + } + } + + Map::~Map() + { + Clear(); + } + + void Map::Clear() + { + for(node_map::const_iterator it=m_data.begin();it!=m_data.end();++it) { + delete it->first; + delete it->second; + } + m_data.clear(); + } + + Content *Map::Clone() const + { + return new Map(m_data); + } + + bool Map::GetBegin(std::map ::const_iterator& it) const + { + it = m_data.begin(); + return true; + } + + bool Map::GetEnd(std::map ::const_iterator& it) const + { + it = m_data.end(); + return true; + } + + std::size_t Map::GetSize() const + { + return m_data.size(); + } + + void Map::Parse(Scanner *pScanner, ParserState& state) + { + Clear(); + + // split based on start token + switch(pScanner->peek().type) { + case Token::BLOCK_MAP_START: ParseBlock(pScanner, state); break; + case Token::FLOW_MAP_START: ParseFlow(pScanner, state); break; + case Token::KEY: ParseCompact(pScanner, state); break; + case Token::VALUE: ParseCompactWithNoKey(pScanner, state); break; + default: break; + } + } + + void Map::ParseBlock(Scanner *pScanner, ParserState& state) + { + // eat start token + pScanner->pop(); + state.PushCollectionType(ParserState::BLOCK_MAP); + + while(1) { + if(pScanner->empty()) + throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP); + + Token token = pScanner->peek(); + if(token.type != Token::KEY && token.type != Token::VALUE && token.type != Token::BLOCK_MAP_END) + throw ParserException(token.mark, ErrorMsg::END_OF_MAP); + + if(token.type == Token::BLOCK_MAP_END) { + pScanner->pop(); + break; + } + + std::auto_ptr pKey(new Node), pValue(new Node); + + // grab key (if non-null) + if(token.type == Token::KEY) { + pScanner->pop(); + pKey->Parse(pScanner, state); + } + + // now grab value (optional) + if(!pScanner->empty() && pScanner->peek().type == Token::VALUE) { + pScanner->pop(); + pValue->Parse(pScanner, state); + } + + AddEntry(pKey, pValue); + } + + state.PopCollectionType(ParserState::BLOCK_MAP); + } + + void Map::ParseFlow(Scanner *pScanner, ParserState& state) + { + // eat start token + pScanner->pop(); + state.PushCollectionType(ParserState::FLOW_MAP); + + while(1) { + if(pScanner->empty()) + throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP_FLOW); + + Token& token = pScanner->peek(); + // first check for end + if(token.type == Token::FLOW_MAP_END) { + pScanner->pop(); + break; + } + + std::auto_ptr pKey(new Node), pValue(new Node); + + // grab key (if non-null) + if(token.type == Token::KEY) { + pScanner->pop(); + pKey->Parse(pScanner, state); + } + + // now grab value (optional) + if(!pScanner->empty() && pScanner->peek().type == Token::VALUE) { + pScanner->pop(); + pValue->Parse(pScanner, state); + } + + // now eat the separator (or could be a map end, which we ignore - but if it's neither, then it's a bad node) + Token& nextToken = pScanner->peek(); + if(nextToken.type == Token::FLOW_ENTRY) + pScanner->pop(); + else if(nextToken.type != Token::FLOW_MAP_END) + throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW); + + AddEntry(pKey, pValue); + } + + state.PopCollectionType(ParserState::FLOW_MAP); + } + + // ParseCompact + // . Single "key: value" pair in a flow sequence + void Map::ParseCompact(Scanner *pScanner, ParserState& state) + { + state.PushCollectionType(ParserState::COMPACT_MAP); + std::auto_ptr pKey(new Node), pValue(new Node); + + // grab key + pScanner->pop(); + pKey->Parse(pScanner, state); + + // now grab value (optional) + if(!pScanner->empty() && pScanner->peek().type == Token::VALUE) { + pScanner->pop(); + pValue->Parse(pScanner, state); + } + + AddEntry(pKey, pValue); + state.PopCollectionType(ParserState::COMPACT_MAP); + } + + // ParseCompactWithNoKey + // . Single ": value" pair in a flow sequence + void Map::ParseCompactWithNoKey(Scanner *pScanner, ParserState& state) + { + state.PushCollectionType(ParserState::COMPACT_MAP); + std::auto_ptr pKey(new Node), pValue(new Node); + + // grab value + pScanner->pop(); + pValue->Parse(pScanner, state); + + AddEntry(pKey, pValue); + state.PopCollectionType(ParserState::COMPACT_MAP); + } + + void Map::AddEntry(std::auto_ptr pKey, std::auto_ptr pValue) + { + node_map::const_iterator it = m_data.find(pKey.get()); + if(it != m_data.end()) + return; + + m_data[pKey.release()] = pValue.release(); + } + + void Map::Write(Emitter& out) const + { + out << BeginMap; + for(node_map::const_iterator it=m_data.begin();it!=m_data.end();++it) + out << Key << *it->first << Value << *it->second; + out << EndMap; + } + + int Map::Compare(Content *pContent) + { + return -pContent->Compare(this); + } + + int Map::Compare(Map *pMap) + { + node_map::const_iterator it = m_data.begin(), jt = pMap->m_data.begin(); + while(1) { + if(it == m_data.end()) { + if(jt == pMap->m_data.end()) + return 0; + else + return -1; + } + if(jt == pMap->m_data.end()) + return 1; + + int cmp = it->first->Compare(*jt->first); + if(cmp != 0) + return cmp; + + cmp = it->second->Compare(*jt->second); + if(cmp != 0) + return cmp; + } + + return 0; + } +} diff --git a/libs/yaml-cpp/src/map.h b/libs/yaml-cpp/src/map.h new file mode 100644 index 0000000..6887880 --- /dev/null +++ b/libs/yaml-cpp/src/map.h @@ -0,0 +1,55 @@ +#pragma once + +#ifndef MAP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define MAP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "content.h" +#include +#include + +namespace YAML +{ + class Node; + + class Map: public Content + { + private: + typedef std::map node_map; + + public: + Map(); + Map(const node_map& data); + virtual ~Map(); + + void Clear(); + virtual Content *Clone() const; + + virtual bool GetBegin(std::map ::const_iterator& it) const; + virtual bool GetEnd(std::map ::const_iterator& it) const; + virtual std::size_t GetSize() const; + virtual void Parse(Scanner *pScanner, ParserState& state); + virtual void Write(Emitter& out) const; + + virtual bool IsMap() const { return true; } + + // ordering + virtual int Compare(Content *pContent); + virtual int Compare(Scalar *) { return 1; } + virtual int Compare(Sequence *) { return 1; } + virtual int Compare(Map *pMap); + + private: + void ParseBlock(Scanner *pScanner, ParserState& state); + void ParseFlow(Scanner *pScanner, ParserState& state); + void ParseCompact(Scanner *pScanner, ParserState& state); + void ParseCompactWithNoKey(Scanner *pScanner, ParserState& state); + + void AddEntry(std::auto_ptr pKey, std::auto_ptr pValue); + + private: + node_map m_data; + }; +} + +#endif // MAP_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/node.cpp b/libs/yaml-cpp/src/node.cpp new file mode 100644 index 0000000..24d3635 --- /dev/null +++ b/libs/yaml-cpp/src/node.cpp @@ -0,0 +1,303 @@ +#include "node.h" +#include "token.h" +#include "scanner.h" +#include "content.h" +#include "parser.h" +#include "scalar.h" +#include "sequence.h" +#include "map.h" +#include "aliascontent.h" +#include "iterpriv.h" +#include "emitter.h" +#include "tag.h" +#include + +namespace YAML +{ + // the ordering! + bool ltnode::operator ()(const Node *pNode1, const Node *pNode2) const + { + return *pNode1 < *pNode2; + } + + Node::Node(): m_pContent(0), m_alias(false), m_pIdentity(this), m_referenced(true) + { + } + + Node::Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent) + : m_mark(mark), m_anchor(anchor), m_tag(tag), m_pContent(0), m_alias(false), m_pIdentity(this), m_referenced(false) + { + if(pContent) + m_pContent = pContent->Clone(); + } + + Node::~Node() + { + Clear(); + } + + void Node::Clear() + { + delete m_pContent; + m_pContent = 0; + m_alias = false; + m_referenced = false; + m_anchor.clear(); + m_tag.clear(); + } + + std::auto_ptr Node::Clone() const + { + if(m_alias) + throw std::runtime_error("yaml-cpp: Can't clone alias"); // TODO: what to do about aliases? + + return std::auto_ptr (new Node(m_mark, m_anchor, m_tag, m_pContent)); + } + + void Node::Parse(Scanner *pScanner, ParserState& state) + { + Clear(); + + // an empty node *is* a possibility + if(pScanner->empty()) + return; + + // save location + m_mark = pScanner->peek().mark; + + // special case: a value node by itself must be a map, with no header + if(pScanner->peek().type == Token::VALUE) { + m_pContent = new Map; + m_pContent->Parse(pScanner, state); + return; + } + + ParseHeader(pScanner, state); + + // is this an alias? if so, its contents are an alias to + // a previously defined anchor + if(m_alias) { + // the scanner throws an exception if it doesn't know this anchor name + const Node *pReferencedNode = pScanner->Retrieve(m_anchor); + m_pIdentity = pReferencedNode; + + // mark the referenced node for the sake of the client code + pReferencedNode->m_referenced = true; + + // use of an Alias object keeps the referenced content from + // being deleted twice + Content *pAliasedContent = pReferencedNode->m_pContent; + if(pAliasedContent) + m_pContent = new AliasContent(pAliasedContent); + + return; + } + + // now split based on what kind of node we should be + switch(pScanner->peek().type) { + case Token::SCALAR: + m_pContent = new Scalar; + break; + case Token::FLOW_SEQ_START: + case Token::BLOCK_SEQ_START: + m_pContent = new Sequence; + break; + case Token::FLOW_MAP_START: + case Token::BLOCK_MAP_START: + m_pContent = new Map; + break; + case Token::KEY: + // compact maps can only go in a flow sequence + if(state.GetCurCollectionType() == ParserState::FLOW_SEQ) + m_pContent = new Map; + break; + default: + break; + } + + // Have to save anchor before parsing to allow for aliases as + // contained node (recursive structure) + if(!m_anchor.empty()) + pScanner->Save(m_anchor, this); + + if(m_pContent) + m_pContent->Parse(pScanner, state); + } + + // ParseHeader + // . Grabs any tag, alias, or anchor tokens and deals with them. + void Node::ParseHeader(Scanner *pScanner, ParserState& state) + { + while(1) { + if(pScanner->empty()) + return; + + switch(pScanner->peek().type) { + case Token::TAG: ParseTag(pScanner, state); break; + case Token::ANCHOR: ParseAnchor(pScanner, state); break; + case Token::ALIAS: ParseAlias(pScanner, state); break; + default: return; + } + } + } + + void Node::ParseTag(Scanner *pScanner, ParserState& state) + { + Token& token = pScanner->peek(); + if(m_tag != "") + throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS); + + Tag tag(token); + m_tag = tag.Translate(state); + pScanner->pop(); + } + + void Node::ParseAnchor(Scanner *pScanner, ParserState& /*state*/) + { + Token& token = pScanner->peek(); + if(m_anchor != "") + throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS); + + m_anchor = token.value; + m_alias = false; + pScanner->pop(); + } + + void Node::ParseAlias(Scanner *pScanner, ParserState& /*state*/) + { + Token& token = pScanner->peek(); + if(m_anchor != "") + throw ParserException(token.mark, ErrorMsg::MULTIPLE_ALIASES); + if(m_tag != "") + throw ParserException(token.mark, ErrorMsg::ALIAS_CONTENT); + + m_anchor = token.value; + m_alias = true; + pScanner->pop(); + } + + CONTENT_TYPE Node::GetType() const + { + if(!m_pContent) + return CT_NONE; + + if(m_pContent->IsScalar()) + return CT_SCALAR; + else if(m_pContent->IsSequence()) + return CT_SEQUENCE; + else if(m_pContent->IsMap()) + return CT_MAP; + + return CT_NONE; + } + + // begin + // Returns an iterator to the beginning of this (sequence or map). + Iterator Node::begin() const + { + if(!m_pContent) + return Iterator(); + + std::vector ::const_iterator seqIter; + if(m_pContent->GetBegin(seqIter)) + return Iterator(new IterPriv(seqIter)); + + std::map ::const_iterator mapIter; + if(m_pContent->GetBegin(mapIter)) + return Iterator(new IterPriv(mapIter)); + + return Iterator(); + } + + // end + // . Returns an iterator to the end of this (sequence or map). + Iterator Node::end() const + { + if(!m_pContent) + return Iterator(); + + std::vector ::const_iterator seqIter; + if(m_pContent->GetEnd(seqIter)) + return Iterator(new IterPriv(seqIter)); + + std::map ::const_iterator mapIter; + if(m_pContent->GetEnd(mapIter)) + return Iterator(new IterPriv(mapIter)); + + return Iterator(); + } + + // size + // . Returns the size of this node, if it's a sequence node. + // . Otherwise, returns zero. + std::size_t Node::size() const + { + if(!m_pContent) + return 0; + + return m_pContent->GetSize(); + } + + const Node *Node::FindAtIndex(std::size_t i) const + { + if(!m_pContent) + return 0; + + return m_pContent->GetNode(i); + } + + bool Node::GetScalar(std::string& s) const + { + if(!m_pContent) { + if(m_tag.empty()) + s = "~"; + else + s = ""; + return true; + } + + return m_pContent->GetScalar(s); + } + + Emitter& operator << (Emitter& out, const Node& node) + { + // write anchor/alias + if(node.m_anchor != "") { + if(node.m_alias) + out << Alias(node.m_anchor); + else + out << Anchor(node.m_anchor); + } + + if(node.m_tag != "") + out << VerbatimTag(node.m_tag); + + // write content + if(node.m_pContent) + node.m_pContent->Write(out); + else if(!node.m_alias) + out << Null; + + return out; + } + + int Node::Compare(const Node& rhs) const + { + // Step 1: no content is the smallest + if(!m_pContent) { + if(rhs.m_pContent) + return -1; + else + return 0; + } + if(!rhs.m_pContent) + return 1; + + return m_pContent->Compare(rhs.m_pContent); + } + + bool operator < (const Node& n1, const Node& n2) + { + return n1.Compare(n2) < 0; + } +} diff --git a/libs/yaml-cpp/src/null.cpp b/libs/yaml-cpp/src/null.cpp new file mode 100644 index 0000000..faa05fb --- /dev/null +++ b/libs/yaml-cpp/src/null.cpp @@ -0,0 +1,12 @@ +#include "null.h" +#include "node.h" + +namespace YAML +{ + _Null Null; + + bool IsNull(const Node& node) + { + return node.Read(Null); + } +} diff --git a/libs/yaml-cpp/src/ostream.cpp b/libs/yaml-cpp/src/ostream.cpp new file mode 100644 index 0000000..a766f9c --- /dev/null +++ b/libs/yaml-cpp/src/ostream.cpp @@ -0,0 +1,64 @@ +#include "ostream.h" +#include + +namespace YAML +{ + ostream::ostream(): m_buffer(0), m_pos(0), m_size(0), m_row(0), m_col(0) + { + reserve(1024); + } + + ostream::~ostream() + { + delete [] m_buffer; + } + + void ostream::reserve(unsigned size) + { + if(size <= m_size) + return; + + char *newBuffer = new char[size]; + std::memset(newBuffer, 0, size * sizeof(char)); + std::memcpy(newBuffer, m_buffer, m_size * sizeof(char)); + delete [] m_buffer; + m_buffer = newBuffer; + m_size = size; + } + + void ostream::put(char ch) + { + if(m_pos >= m_size - 1) // an extra space for the NULL terminator + reserve(m_size * 2); + + m_buffer[m_pos] = ch; + m_pos++; + + if(ch == '\n') { + m_row++; + m_col = 0; + } else + m_col++; + } + + ostream& operator << (ostream& out, const char *str) + { + std::size_t length = std::strlen(str); + for(std::size_t i=0;i +#include + +namespace YAML +{ + Parser::Parser() + { + } + + Parser::Parser(std::istream& in) + { + Load(in); + } + + Parser::~Parser() + { + } + + Parser::operator bool() const + { + return m_pScanner.get() && !m_pScanner->empty(); + } + + void Parser::Load(std::istream& in) + { + m_pScanner.reset(new Scanner(in)); + m_pState.reset(new ParserState); + } + + // GetNextDocument + // . Reads the next document in the queue (of tokens). + // . Throws a ParserException on error. + bool Parser::GetNextDocument(Node& document) + { + if(!m_pScanner.get()) + return false; + + // clear node + document.Clear(); + + // first read directives + ParseDirectives(); + + // we better have some tokens in the queue + if(m_pScanner->empty()) + return false; + + // first eat doc start (optional) + if(m_pScanner->peek().type == Token::DOC_START) + m_pScanner->pop(); + + // now parse our root node + document.Parse(m_pScanner.get(), *m_pState); + + // and finally eat any doc ends we see + while(!m_pScanner->empty() && m_pScanner->peek().type == Token::DOC_END) + m_pScanner->pop(); + + // clear anchors from the scanner, which are no longer relevant + m_pScanner->ClearAnchors(); + + return true; + } + + // ParseDirectives + // . Reads any directives that are next in the queue. + void Parser::ParseDirectives() + { + bool readDirective = false; + + while(1) { + if(m_pScanner->empty()) + break; + + Token& token = m_pScanner->peek(); + if(token.type != Token::DIRECTIVE) + break; + + // we keep the directives from the last document if none are specified; + // but if any directives are specific, then we reset them + if(!readDirective) + m_pState.reset(new ParserState); + + readDirective = true; + HandleDirective(token); + m_pScanner->pop(); + } + } + + void Parser::HandleDirective(const Token& token) + { + if(token.value == "YAML") + HandleYamlDirective(token); + else if(token.value == "TAG") + HandleTagDirective(token); + } + + // HandleYamlDirective + // . Should be of the form 'major.minor' (like a version number) + void Parser::HandleYamlDirective(const Token& token) + { + if(token.params.size() != 1) + throw ParserException(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS); + + if(!m_pState->version.isDefault) + throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE); + + std::stringstream str(token.params[0]); + str >> m_pState->version.major; + str.get(); + str >> m_pState->version.minor; + if(!str || str.peek() != EOF) + throw ParserException(token.mark, ErrorMsg::YAML_VERSION + token.params[0]); + + if(m_pState->version.major > 1) + throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION); + + m_pState->version.isDefault = false; + // TODO: warning on major == 1, minor > 2? + } + + // HandleTagDirective + // . Should be of the form 'handle prefix', where 'handle' is converted to 'prefix' in the file. + void Parser::HandleTagDirective(const Token& token) + { + if(token.params.size() != 2) + throw ParserException(token.mark, ErrorMsg::TAG_DIRECTIVE_ARGS); + + const std::string& handle = token.params[0]; + const std::string& prefix = token.params[1]; + if(m_pState->tags.find(handle) != m_pState->tags.end()) + throw ParserException(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE); + + m_pState->tags[handle] = prefix; + } + + void Parser::PrintTokens(std::ostream& out) + { + if(!m_pScanner.get()) + return; + + while(1) { + if(m_pScanner->empty()) + break; + + out << m_pScanner->peek() << "\n"; + m_pScanner->pop(); + } + } +} diff --git a/libs/yaml-cpp/src/parserstate.cpp b/libs/yaml-cpp/src/parserstate.cpp new file mode 100644 index 0000000..ebd6609 --- /dev/null +++ b/libs/yaml-cpp/src/parserstate.cpp @@ -0,0 +1,24 @@ +#include "parserstate.h" + +namespace YAML +{ + ParserState::ParserState() + { + // version + version.isDefault = true; + version.major = 1; + version.minor = 2; + } + + const std::string ParserState::TranslateTagHandle(const std::string& handle) const + { + std::map ::const_iterator it = tags.find(handle); + if(it == tags.end()) { + if(handle == "!!") + return "tag:yaml.org,2002:"; + return handle; + } + + return it->second; + } +} diff --git a/libs/yaml-cpp/src/parserstate.h b/libs/yaml-cpp/src/parserstate.h new file mode 100644 index 0000000..0b849c3 --- /dev/null +++ b/libs/yaml-cpp/src/parserstate.h @@ -0,0 +1,37 @@ +#pragma once + +#ifndef PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include +#include +#include + +namespace YAML +{ + struct Version { + bool isDefault; + int major, minor; + }; + + struct ParserState + { + enum COLLECTION_TYPE { NONE, BLOCK_MAP, BLOCK_SEQ, FLOW_MAP, FLOW_SEQ, COMPACT_MAP }; + + ParserState(); + + const std::string TranslateTagHandle(const std::string& handle) const; + COLLECTION_TYPE GetCurCollectionType() const { if(collectionStack.empty()) return NONE; return collectionStack.top(); } + + void PushCollectionType(COLLECTION_TYPE type) { collectionStack.push(type); } + void PopCollectionType(COLLECTION_TYPE type) { assert(type == GetCurCollectionType()); collectionStack.pop(); } + + Version version; + std::map tags; + std::stack collectionStack; + }; +} + +#endif // PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/regex.cpp b/libs/yaml-cpp/src/regex.cpp new file mode 100644 index 0000000..b35b1f4 --- /dev/null +++ b/libs/yaml-cpp/src/regex.cpp @@ -0,0 +1,60 @@ +#include "regex.h" + +namespace YAML +{ + // constructors + RegEx::RegEx(): m_op(REGEX_EMPTY) + { + } + + RegEx::RegEx(REGEX_OP op): m_op(op) + { + } + + RegEx::RegEx(char ch): m_op(REGEX_MATCH), m_a(ch) + { + } + + RegEx::RegEx(char a, char z): m_op(REGEX_RANGE), m_a(a), m_z(z) + { + } + + RegEx::RegEx(const std::string& str, REGEX_OP op): m_op(op) + { + for(std::size_t i=0;i +#include + +namespace YAML +{ + class Stream; + + enum REGEX_OP { REGEX_EMPTY, REGEX_MATCH, REGEX_RANGE, REGEX_OR, REGEX_AND, REGEX_NOT, REGEX_SEQ }; + + // simplified regular expressions + // . Only straightforward matches (no repeated characters) + // . Only matches from start of string + class RegEx + { + public: + RegEx(); + RegEx(char ch); + RegEx(char a, char z); + RegEx(const std::string& str, REGEX_OP op = REGEX_SEQ); + ~RegEx() {} + + friend RegEx operator ! (const RegEx& ex); + friend RegEx operator || (const RegEx& ex1, const RegEx& ex2); + friend RegEx operator && (const RegEx& ex1, const RegEx& ex2); + friend RegEx operator + (const RegEx& ex1, const RegEx& ex2); + + bool Matches(char ch) const; + bool Matches(const std::string& str) const; + bool Matches(const Stream& in) const; + template bool Matches(const Source& source) const; + + int Match(const std::string& str) const; + int Match(const Stream& in) const; + template int Match(const Source& source) const; + + private: + RegEx(REGEX_OP op); + + template bool IsValidSource(const Source& source) const; + template int MatchUnchecked(const Source& source) const; + + template int MatchOpEmpty(const Source& source) const; + template int MatchOpMatch(const Source& source) const; + template int MatchOpRange(const Source& source) const; + template int MatchOpOr(const Source& source) const; + template int MatchOpAnd(const Source& source) const; + template int MatchOpNot(const Source& source) const; + template int MatchOpSeq(const Source& source) const; + + private: + REGEX_OP m_op; + char m_a, m_z; + std::vector m_params; + }; +} + +#include "regeximpl.h" + +#endif // REGEX_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/regeximpl.h b/libs/yaml-cpp/src/regeximpl.h new file mode 100644 index 0000000..41a052e --- /dev/null +++ b/libs/yaml-cpp/src/regeximpl.h @@ -0,0 +1,178 @@ +#pragma once + +#ifndef REGEXIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define REGEXIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "stream.h" +#include "stringsource.h" +#include "streamcharsource.h" + +namespace YAML +{ + // query matches + inline bool RegEx::Matches(char ch) const { + std::string str; + str += ch; + return Matches(str); + } + + inline bool RegEx::Matches(const std::string& str) const { + return Match(str) >= 0; + } + + inline bool RegEx::Matches(const Stream& in) const { + return Match(in) >= 0; + } + + template + inline bool RegEx::Matches(const Source& source) const { + return Match(source) >= 0; + } + + // Match + // . Matches the given string against this regular expression. + // . Returns the number of characters matched. + // . Returns -1 if no characters were matched (the reason for + // not returning zero is that we may have an empty regex + // which is ALWAYS successful at matching zero characters). + // . REMEMBER that we only match from the start of the buffer! + inline int RegEx::Match(const std::string& str) const + { + StringCharSource source(str.c_str(), str.size()); + return Match(source); + } + + inline int RegEx::Match(const Stream& in) const + { + StreamCharSource source(in); + return Match(source); + } + + template + inline bool RegEx::IsValidSource(const Source& source) const + { + return source; + } + + template<> + inline bool RegEx::IsValidSource(const StringCharSource&source) const + { + return source || m_op == REGEX_EMPTY; + } + + template + inline int RegEx::Match(const Source& source) const + { + return IsValidSource(source) ? MatchUnchecked(source) : -1; + } + + template + inline int RegEx::MatchUnchecked(const Source& source) const + { + switch(m_op) { + case REGEX_EMPTY: + return MatchOpEmpty(source); + case REGEX_MATCH: + return MatchOpMatch(source); + case REGEX_RANGE: + return MatchOpRange(source); + case REGEX_OR: + return MatchOpOr(source); + case REGEX_AND: + return MatchOpAnd(source); + case REGEX_NOT: + return MatchOpNot(source); + case REGEX_SEQ: + return MatchOpSeq(source); + } + + return -1; + } + + ////////////////////////////////////////////////////////////////////////////// + // Operators + // Note: the convention MatchOp* is that we can assume IsSourceValid(source). + // So we do all our checks *before* we call these functions + + // EmptyOperator + template + inline int RegEx::MatchOpEmpty(const Source& source) const { + return source[0] == Stream::eof() ? 0 : -1; + } + + template <> + inline int RegEx::MatchOpEmpty(const StringCharSource& source) const { + return !source ? 0 : -1; // the empty regex only is successful on the empty string + } + + // MatchOperator + template + inline int RegEx::MatchOpMatch(const Source& source) const { + if(source[0] != m_a) + return -1; + return 1; + } + + // RangeOperator + template + inline int RegEx::MatchOpRange(const Source& source) const { + if(m_a > source[0] || m_z < source[0]) + return -1; + return 1; + } + + // OrOperator + template + inline int RegEx::MatchOpOr(const Source& source) const { + for(std::size_t i=0;i= 0) + return n; + } + return -1; + } + + // AndOperator + // Note: 'AND' is a little funny, since we may be required to match things + // of different lengths. If we find a match, we return the length of + // the FIRST entry on the list. + template + inline int RegEx::MatchOpAnd(const Source& source) const { + int first = -1; + for(std::size_t i=0;i + inline int RegEx::MatchOpNot(const Source& source) const { + if(m_params.empty()) + return -1; + if(m_params[0].MatchUnchecked(source) >= 0) + return -1; + return 1; + } + + // SeqOperator + template + inline int RegEx::MatchOpSeq(const Source& source) const { + int offset = 0; + for(std::size_t i=0;ipeek(); + m_data = token.value; + pScanner->pop(); + } + + void Scalar::Write(Emitter& out) const + { + out << m_data; + } + + int Scalar::Compare(Content *pContent) + { + return -pContent->Compare(this); + } + + int Scalar::Compare(Scalar *pScalar) + { + if(m_data < pScalar->m_data) + return -1; + else if(m_data > pScalar->m_data) + return 1; + else + return 0; + } +} + diff --git a/libs/yaml-cpp/src/scalar.h b/libs/yaml-cpp/src/scalar.h new file mode 100644 index 0000000..1baecaa --- /dev/null +++ b/libs/yaml-cpp/src/scalar.h @@ -0,0 +1,44 @@ +#pragma once + +#ifndef SCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define SCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "content.h" +#include + +namespace YAML +{ + class Scalar: public Content + { + public: + Scalar(); + Scalar(const std::string& data); + virtual ~Scalar(); + + virtual Content *Clone() const; + + virtual void Parse(Scanner *pScanner, ParserState& state); + virtual void Write(Emitter& out) const; + + virtual bool IsScalar() const { return true; } + + // extraction + virtual bool GetScalar(std::string& scalar) const { + scalar = m_data; + return true; + } + + // ordering + virtual int Compare(Content *pContent); + virtual int Compare(Scalar *pScalar); + virtual int Compare(Sequence *) { return -1; } + virtual int Compare(Map *) { return -1; } + + protected: + std::string m_data; + }; +} + +#endif // SCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/libs/yaml-cpp/src/scanner.cpp b/libs/yaml-cpp/src/scanner.cpp new file mode 100644 index 0000000..33052c2 --- /dev/null +++ b/libs/yaml-cpp/src/scanner.cpp @@ -0,0 +1,423 @@ +#include "scanner.h" +#include "token.h" +#include "exceptions.h" +#include "exp.h" +#include +#include + +namespace YAML +{ + Scanner::Scanner(std::istream& in) + : INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), m_canBeJSONFlow(false) + { + } + + Scanner::~Scanner() + { + for(unsigned i=0;i 0) + INPUT.ResetColumn(); + + PopAllIndents(); + PopAllSimpleKeys(); + + m_simpleKeyAllowed = false; + m_endedStream = true; + } + + Token *Scanner::PushToken(Token::TYPE type) + { + m_tokens.push(Token(type, INPUT.mark())); + return &m_tokens.back(); + } + + Token::TYPE Scanner::GetStartTokenFor(IndentMarker::INDENT_TYPE type) const + { + switch(type) { + case IndentMarker::SEQ: return Token::BLOCK_SEQ_START; + case IndentMarker::MAP: return Token::BLOCK_MAP_START; + case IndentMarker::NONE: assert(false); break; + } + assert(false); + } + + // PushIndentTo + // . Pushes an indentation onto the stack, and enqueues the + // proper token (sequence start or mapping start). + // . Returns the indent marker it generates (if any). + Scanner::IndentMarker *Scanner::PushIndentTo(int column, IndentMarker::INDENT_TYPE type) + { + // are we in flow? + if(InFlowContext()) + return 0; + + std::auto_ptr pIndent(new IndentMarker(column, type)); + IndentMarker& indent = *pIndent; + const IndentMarker& lastIndent = *m_indents.top(); + + // is this actually an indentation? + if(indent.column < lastIndent.column) + return 0; + if(indent.column == lastIndent.column && !(indent.type == IndentMarker::SEQ && lastIndent.type == IndentMarker::MAP)) + return 0; + + // push a start token + indent.pStartToken = PushToken(GetStartTokenFor(type)); + + // and then the indent + m_indents.push(&indent); + m_indentRefs.push_back(pIndent.release()); + return m_indentRefs.back(); + } + + // PopIndentToHere + // . Pops indentations off the stack until we reach the current indentation level, + // and enqueues the proper token each time. + // . Then pops all invalid indentations off. + void Scanner::PopIndentToHere() + { + // are we in flow? + if(InFlowContext()) + return; + + // now pop away + while(!m_indents.empty()) { + const IndentMarker& indent = *m_indents.top(); + if(indent.column < INPUT.column()) + break; + if(indent.column == INPUT.column() && !(indent.type == IndentMarker::SEQ && !Exp::BlockEntry().Matches(INPUT))) + break; + + PopIndent(); + } + + while(!m_indents.empty() && m_indents.top()->status == IndentMarker::INVALID) + PopIndent(); + } + + // PopAllIndents + // . Pops all indentations (except for the base empty one) off the stack, + // and enqueues the proper token each time. + void Scanner::PopAllIndents() + { + // are we in flow? + if(InFlowContext()) + return; + + // now pop away + while(!m_indents.empty()) { + const IndentMarker& indent = *m_indents.top(); + if(indent.type == IndentMarker::NONE) + break; + + PopIndent(); + } + } + + // PopIndent + // . Pops a single indent, pushing the proper token + void Scanner::PopIndent() + { + const IndentMarker& indent = *m_indents.top(); + m_indents.pop(); + + if(indent.status != IndentMarker::VALID) { + InvalidateSimpleKey(); + return; + } + + if(indent.type == IndentMarker::SEQ) + m_tokens.push(Token(Token::BLOCK_SEQ_END, INPUT.mark())); + else if(indent.type == IndentMarker::MAP) + m_tokens.push(Token(Token::BLOCK_MAP_END, INPUT.mark())); + } + + // GetTopIndent + int Scanner::GetTopIndent() const + { + if(m_indents.empty()) + return 0; + return m_indents.top()->column; + } + + // Save + // . Saves a pointer to the Node object referenced by a particular anchor + // name. + void Scanner::Save(const std::string& anchor, Node* value) + { + m_anchors[anchor] = value; + } + + // Retrieve + // . Retrieves a pointer previously saved for an anchor name. + // . Throws an exception if the anchor has not been defined. + const Node *Scanner::Retrieve(const std::string& anchor) const + { + typedef std::map map; + + map::const_iterator itNode = m_anchors.find(anchor); + + if(m_anchors.end() == itNode) + ThrowParserException(ErrorMsg::UNKNOWN_ANCHOR); + + return itNode->second; + } + + // ThrowParserException + // . Throws a ParserException with the current token location + // (if available). + // . Does not parse any more tokens. + void Scanner::ThrowParserException(const std::string& msg) const + { + Mark mark = Mark::null(); + if(!m_tokens.empty()) { + const Token& token = m_tokens.front(); + mark = token.mark; + } + throw ParserException(mark, msg); + } + + void Scanner::ClearAnchors() + { + m_anchors.clear(); + } +} + diff --git a/libs/yaml-cpp/src/scanner.h b/libs/yaml-cpp/src/scanner.h new file mode 100644 index 0000000..28ce96d --- /dev/null +++ b/libs/yaml-cpp/src/scanner.h @@ -0,0 +1,135 @@ +#pragma once + +#ifndef SCANNER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define SCANNER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include +#include +#include +#include +#include +#include "stream.h" +#include "token.h" + +namespace YAML +{ + class Node; + class RegEx; + + class Scanner + { + public: + Scanner(std::istream& in); + ~Scanner(); + + // token queue management (hopefully this looks kinda stl-ish) + bool empty(); + void pop(); + Token& peek(); + + // anchor management + void Save(const std::string& anchor, Node* value); + const Node *Retrieve(const std::string& anchor) const; + void ClearAnchors(); + + private: + struct IndentMarker { + enum INDENT_TYPE { MAP, SEQ, NONE }; + enum STATUS { VALID, INVALID, UNKNOWN }; + IndentMarker(int column_, INDENT_TYPE type_): column(column_), type(type_), status(VALID), pStartToken(0) {} + + int column; + INDENT_TYPE type; + STATUS status; + Token *pStartToken; + }; + + enum FLOW_MARKER { FLOW_MAP, FLOW_SEQ }; + + private: + // scanning + void EnsureTokensInQueue(); + void ScanNextToken(); + void ScanToNextToken(); + void StartStream(); + void EndStream(); + Token *PushToken(Token::TYPE type); + + bool InFlowContext() const { return !m_flows.empty(); } + bool InBlockContext() const { return m_flows.empty(); } + int GetFlowLevel() const { return m_flows.size(); } + + Token::TYPE GetStartTokenFor(IndentMarker::INDENT_TYPE type) const; + IndentMarker *PushIndentTo(int column, IndentMarker::INDENT_TYPE type); + void PopIndentToHere(); + void PopAllIndents(); + void PopIndent(); + int GetTopIndent() const; + + // checking input + bool CanInsertPotentialSimpleKey() const; + bool ExistsActiveSimpleKey() const; + void InsertPotentialSimpleKey(); + void InvalidateSimpleKey(); + bool VerifySimpleKey(); + void PopAllSimpleKeys(); + + void ThrowParserException(const std::string& msg) const; + + bool IsWhitespaceToBeEaten(char ch); + const RegEx& GetValueRegex() const; + + struct SimpleKey { + SimpleKey(const Mark& mark_, int flowLevel_); + + void Validate(); + void Invalidate(); + + Mark mark; + int flowLevel; + IndentMarker *pIndent; + Token *pMapStart, *pKey; + }; + + // and the tokens + void ScanDirective(); + void ScanDocStart(); + void ScanDocEnd(); + void ScanBlockSeqStart(); + void ScanBlockMapSTart(); + void ScanBlockEnd(); + void ScanBlockEntry(); + void ScanFlowStart(); + void ScanFlowEnd(); + void ScanFlowEntry(); + void ScanKey(); + void ScanValue(); + void ScanAnchorOrAlias(); + void ScanTag(); + void ScanPlainScalar(); + void ScanQuotedScalar(); + void ScanBlockScalar(); + + private: + // the stream + Stream INPUT; + + // the output (tokens) + std::queue m_tokens; + + // state info + bool m_startedStream, m_endedStream; + bool m_simpleKeyAllowed; + bool m_canBeJSONFlow; + std::stack m_simpleKeys; + std::stack m_indents; + std::vector m_indentRefs; // for "garbage collection" + std::stack m_flows; + std::map m_anchors; + }; +} + +#endif // SCANNER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/libs/yaml-cpp/src/scanscalar.cpp b/libs/yaml-cpp/src/scanscalar.cpp new file mode 100644 index 0000000..50b8bf7 --- /dev/null +++ b/libs/yaml-cpp/src/scanscalar.cpp @@ -0,0 +1,188 @@ +#include "scanscalar.h" +#include "scanner.h" +#include "exp.h" +#include "exceptions.h" +#include "token.h" + +namespace YAML +{ + // ScanScalar + // . This is where the scalar magic happens. + // + // . We do the scanning in three phases: + // 1. Scan until newline + // 2. Eat newline + // 3. Scan leading blanks. + // + // . Depending on the parameters given, we store or stop + // and different places in the above flow. + std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) + { + bool foundNonEmptyLine = false; + bool pastOpeningBreak = (params.fold == FOLD_FLOW); + bool emptyLine = false, moreIndented = false; + int foldedNewlineCount = 0; + bool foldedNewlineStartedMoreIndented = false; + std::string scalar; + params.leadingSpaces = false; + + while(INPUT) { + // ******************************** + // Phase #1: scan until line ending + + std::size_t lastNonWhitespaceChar = scalar.size(); + bool escapedNewline = false; + while(!params.end.Matches(INPUT) && !Exp::Break().Matches(INPUT)) { + if(!INPUT) + break; + + // document indicator? + if(INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) { + if(params.onDocIndicator == BREAK) + break; + else if(params.onDocIndicator == THROW) + throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR); + } + + foundNonEmptyLine = true; + pastOpeningBreak = true; + + // escaped newline? (only if we're escaping on slash) + if(params.escape == '\\' && Exp::EscBreak().Matches(INPUT)) { + // eat escape character and get out (but preserve trailing whitespace!) + INPUT.get(); + lastNonWhitespaceChar = scalar.size(); + escapedNewline = true; + break; + } + + // escape this? + if(INPUT.peek() == params.escape) { + scalar += Exp::Escape(INPUT); + lastNonWhitespaceChar = scalar.size(); + continue; + } + + // otherwise, just add the damn character + char ch = INPUT.get(); + scalar += ch; + if(ch != ' ' && ch != '\t') + lastNonWhitespaceChar = scalar.size(); + } + + // eof? if we're looking to eat something, then we throw + if(!INPUT) { + if(params.eatEnd) + throw ParserException(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR); + break; + } + + // doc indicator? + if(params.onDocIndicator == BREAK && INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) + break; + + // are we done via character match? + int n = params.end.Match(INPUT); + if(n >= 0) { + if(params.eatEnd) + INPUT.eat(n); + break; + } + + // do we remove trailing whitespace? + if(params.fold == FOLD_FLOW) + scalar.erase(lastNonWhitespaceChar); + + // ******************************** + // Phase #2: eat line ending + n = Exp::Break().Match(INPUT); + INPUT.eat(n); + + // ******************************** + // Phase #3: scan initial spaces + + // first the required indentation + while(INPUT.peek() == ' ' && (INPUT.column() < params.indent || (params.detectIndent && !foundNonEmptyLine))) + INPUT.eat(1); + + // update indent if we're auto-detecting + if(params.detectIndent && !foundNonEmptyLine) + params.indent = std::max(params.indent, INPUT.column()); + + // and then the rest of the whitespace + while(Exp::Blank().Matches(INPUT)) { + // we check for tabs that masquerade as indentation + if(INPUT.peek() == '\t'&& INPUT.column() < params.indent && params.onTabInIndentation == THROW) + throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION); + + if(!params.eatLeadingWhitespace) + break; + + INPUT.eat(1); + } + + // was this an empty line? + bool nextEmptyLine = Exp::Break().Matches(INPUT); + bool nextMoreIndented = Exp::Blank().Matches(INPUT); + if(params.fold == FOLD_BLOCK && foldedNewlineCount == 0 && nextEmptyLine) + foldedNewlineStartedMoreIndented = moreIndented; + + // for block scalars, we always start with a newline, so we should ignore it (not fold or keep) + if(pastOpeningBreak) { + switch(params.fold) { + case DONT_FOLD: + scalar += "\n"; + break; + case FOLD_BLOCK: + if(!emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented && INPUT.column() >= params.indent) + scalar += " "; + else if(nextEmptyLine) + foldedNewlineCount++; + else + scalar += "\n"; + + if(!nextEmptyLine && foldedNewlineCount > 0) { + scalar += std::string(foldedNewlineCount - 1, '\n'); + if(foldedNewlineStartedMoreIndented || nextMoreIndented) + scalar += "\n"; + foldedNewlineCount = 0; + } + break; + case FOLD_FLOW: + if(nextEmptyLine) + scalar += "\n"; + else if(!emptyLine && !nextEmptyLine && !escapedNewline) + scalar += " "; + break; + } + } + + emptyLine = nextEmptyLine; + moreIndented = nextMoreIndented; + pastOpeningBreak = true; + + // are we done via indentation? + if(!emptyLine && INPUT.column() < params.indent) { + params.leadingSpaces = true; + break; + } + } + + // post-processing + if(params.trimTrailingSpaces) { + std::size_t pos = scalar.find_last_not_of(' '); + if(pos < scalar.size()) + scalar.erase(pos + 1); + } + + if(params.chomp == STRIP || params.chomp == CLIP) { + std::size_t pos = scalar.find_last_not_of('\n'); + if(params.chomp == CLIP && pos + 1 < scalar.size()) + scalar.erase(pos + 2); + else if(params.chomp == STRIP && pos < scalar.size()) + scalar.erase(pos + 1); + } + + return scalar; + } +} diff --git a/libs/yaml-cpp/src/scanscalar.h b/libs/yaml-cpp/src/scanscalar.h new file mode 100644 index 0000000..1f92b9e --- /dev/null +++ b/libs/yaml-cpp/src/scanscalar.h @@ -0,0 +1,43 @@ +#pragma once + +#ifndef SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include "regex.h" +#include "stream.h" + +namespace YAML +{ + enum CHOMP { STRIP = -1, CLIP, KEEP }; + enum ACTION { NONE, BREAK, THROW }; + enum FOLD { DONT_FOLD, FOLD_BLOCK, FOLD_FLOW }; + + struct ScanScalarParams { + ScanScalarParams(): eatEnd(false), indent(0), detectIndent(false), eatLeadingWhitespace(0), escape(0), fold(DONT_FOLD), + trimTrailingSpaces(0), chomp(CLIP), onDocIndicator(NONE), onTabInIndentation(NONE), leadingSpaces(false) {} + + // input: + RegEx end; // what condition ends this scalar? + bool eatEnd; // should we eat that condition when we see it? + int indent; // what level of indentation should be eaten and ignored? + bool detectIndent; // should we try to autodetect the indent? + bool eatLeadingWhitespace; // should we continue eating this delicious indentation after 'indent' spaces? + char escape; // what character do we escape on (i.e., slash or single quote) (0 for none) + FOLD fold; // how do we fold line ends? + bool trimTrailingSpaces; // do we remove all trailing spaces (at the very end) + CHOMP chomp; // do we strip, clip, or keep trailing newlines (at the very end) + // Note: strip means kill all, clip means keep at most one, keep means keep all + ACTION onDocIndicator; // what do we do if we see a document indicator? + ACTION onTabInIndentation; // what do we do if we see a tab where we should be seeing indentation spaces + + // output: + bool leadingSpaces; + }; + + std::string ScanScalar(Stream& INPUT, ScanScalarParams& info); +} + +#endif // SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/libs/yaml-cpp/src/scantag.cpp b/libs/yaml-cpp/src/scantag.cpp new file mode 100644 index 0000000..f08218e --- /dev/null +++ b/libs/yaml-cpp/src/scantag.cpp @@ -0,0 +1,84 @@ +#include "scanner.h" +#include "regex.h" +#include "exp.h" +#include "exceptions.h" + +namespace YAML +{ + const std::string ScanVerbatimTag(Stream& INPUT) + { + std::string tag; + + // eat the start character + INPUT.get(); + + while(INPUT) { + if(INPUT.peek() == Keys::VerbatimTagEnd) { + // eat the end character + INPUT.get(); + return tag; + } + + int n = Exp::URI().Match(INPUT); + if(n <= 0) + break; + + tag += INPUT.get(n); + } + + throw ParserException(INPUT.mark(), ErrorMsg::END_OF_VERBATIM_TAG); + } + + const std::string ScanTagHandle(Stream& INPUT, bool& canBeHandle) + { + std::string tag; + canBeHandle = true; + Mark firstNonWordChar; + + while(INPUT) { + if(INPUT.peek() == Keys::Tag) { + if(!canBeHandle) + throw ParserException(firstNonWordChar, ErrorMsg::CHAR_IN_TAG_HANDLE); + break; + } + + int n = 0; + if(canBeHandle) { + n = Exp::Word().Match(INPUT); + if(n <= 0) { + canBeHandle = false; + firstNonWordChar = INPUT.mark(); + } + } + + if(!canBeHandle) + n = Exp::Tag().Match(INPUT); + + if(n <= 0) + break; + + tag += INPUT.get(n); + } + + return tag; + } + + const std::string ScanTagSuffix(Stream& INPUT) + { + std::string tag; + + while(INPUT) { + int n = Exp::Tag().Match(INPUT); + if(n <= 0) + break; + + tag += INPUT.get(n); + } + + if(tag.empty()) + throw ParserException(INPUT.mark(), ErrorMsg::TAG_WITH_NO_SUFFIX); + + return tag; + } +} + diff --git a/libs/yaml-cpp/src/scantag.h b/libs/yaml-cpp/src/scantag.h new file mode 100644 index 0000000..77b315d --- /dev/null +++ b/libs/yaml-cpp/src/scantag.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include "stream.h" + +namespace YAML +{ + const std::string ScanVerbatimTag(Stream& INPUT); + const std::string ScanTagHandle(Stream& INPUT, bool& canBeHandle); + const std::string ScanTagSuffix(Stream& INPUT); +} + +#endif // SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + diff --git a/libs/yaml-cpp/src/scantoken.cpp b/libs/yaml-cpp/src/scantoken.cpp new file mode 100644 index 0000000..3e7e1cc --- /dev/null +++ b/libs/yaml-cpp/src/scantoken.cpp @@ -0,0 +1,434 @@ +#include "scanner.h" +#include "token.h" +#include "exceptions.h" +#include "exp.h" +#include "scanscalar.h" +#include "scantag.h" +#include "tag.h" +#include + +namespace YAML +{ + /////////////////////////////////////////////////////////////////////// + // Specialization for scanning specific tokens + + // Directive + // . Note: no semantic checking is done here (that's for the parser to do) + void Scanner::ScanDirective() + { + std::string name; + std::vector params; + + // pop indents and simple keys + PopAllIndents(); + PopAllSimpleKeys(); + + m_simpleKeyAllowed = false; + m_canBeJSONFlow = false; + + // store pos and eat indicator + Token token(Token::DIRECTIVE, INPUT.mark()); + INPUT.eat(1); + + // read name + while(INPUT && !Exp::BlankOrBreak().Matches(INPUT)) + token.value += INPUT.get(); + + // read parameters + while(1) { + // first get rid of whitespace + while(Exp::Blank().Matches(INPUT)) + INPUT.eat(1); + + // break on newline or comment + if(!INPUT || Exp::Break().Matches(INPUT) || Exp::Comment().Matches(INPUT)) + break; + + // now read parameter + std::string param; + while(INPUT && !Exp::BlankOrBreak().Matches(INPUT)) + param += INPUT.get(); + + token.params.push_back(param); + } + + m_tokens.push(token); + } + + // DocStart + void Scanner::ScanDocStart() + { + PopAllIndents(); + PopAllSimpleKeys(); + m_simpleKeyAllowed = false; + m_canBeJSONFlow = false; + + // eat + Mark mark = INPUT.mark(); + INPUT.eat(3); + m_tokens.push(Token(Token::DOC_START, mark)); + } + + // DocEnd + void Scanner::ScanDocEnd() + { + PopAllIndents(); + PopAllSimpleKeys(); + m_simpleKeyAllowed = false; + m_canBeJSONFlow = false; + + // eat + Mark mark = INPUT.mark(); + INPUT.eat(3); + m_tokens.push(Token(Token::DOC_END, mark)); + } + + // FlowStart + void Scanner::ScanFlowStart() + { + // flows can be simple keys + InsertPotentialSimpleKey(); + m_simpleKeyAllowed = true; + m_canBeJSONFlow = false; + + // eat + Mark mark = INPUT.mark(); + char ch = INPUT.get(); + FLOW_MARKER flowType = (ch == Keys::FlowSeqStart ? FLOW_SEQ : FLOW_MAP); + m_flows.push(flowType); + Token::TYPE type = (flowType == FLOW_SEQ ? Token::FLOW_SEQ_START : Token::FLOW_MAP_START); + m_tokens.push(Token(type, mark)); + } + + // FlowEnd + void Scanner::ScanFlowEnd() + { + if(InBlockContext()) + throw ParserException(INPUT.mark(), ErrorMsg::FLOW_END); + + // we might have a solo entry in the flow context + if(InFlowContext()) { + if(m_flows.top() == FLOW_MAP && VerifySimpleKey()) + m_tokens.push(Token(Token::VALUE, INPUT.mark())); + else if(m_flows.top() == FLOW_SEQ) + InvalidateSimpleKey(); + } + + m_simpleKeyAllowed = false; + m_canBeJSONFlow = true; + + // eat + Mark mark = INPUT.mark(); + char ch = INPUT.get(); + + // check that it matches the start + FLOW_MARKER flowType = (ch == Keys::FlowSeqEnd ? FLOW_SEQ : FLOW_MAP); + if(m_flows.top() != flowType) + throw ParserException(mark, ErrorMsg::FLOW_END); + m_flows.pop(); + + Token::TYPE type = (flowType ? Token::FLOW_SEQ_END : Token::FLOW_MAP_END); + m_tokens.push(Token(type, mark)); + } + + // FlowEntry + void Scanner::ScanFlowEntry() + { + // we might have a solo entry in the flow context + if(InFlowContext()) { + if(m_flows.top() == FLOW_MAP && VerifySimpleKey()) + m_tokens.push(Token(Token::VALUE, INPUT.mark())); + else if(m_flows.top() == FLOW_SEQ) + InvalidateSimpleKey(); + } + + m_simpleKeyAllowed = true; + m_canBeJSONFlow = false; + + // eat + Mark mark = INPUT.mark(); + INPUT.eat(1); + m_tokens.push(Token(Token::FLOW_ENTRY, mark)); + } + + // BlockEntry + void Scanner::ScanBlockEntry() + { + // we better be in the block context! + if(InFlowContext()) + throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); + + // can we put it here? + if(!m_simpleKeyAllowed) + throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); + + PushIndentTo(INPUT.column(), IndentMarker::SEQ); + m_simpleKeyAllowed = true; + m_canBeJSONFlow = false; + + // eat + Mark mark = INPUT.mark(); + INPUT.eat(1); + m_tokens.push(Token(Token::BLOCK_ENTRY, mark)); + } + + // Key + void Scanner::ScanKey() + { + // handle keys diffently in the block context (and manage indents) + if(InBlockContext()) { + if(!m_simpleKeyAllowed) + throw ParserException(INPUT.mark(), ErrorMsg::MAP_KEY); + + PushIndentTo(INPUT.column(), IndentMarker::MAP); + } + + // can only put a simple key here if we're in block context + m_simpleKeyAllowed = InBlockContext(); + + // eat + Mark mark = INPUT.mark(); + INPUT.eat(1); + m_tokens.push(Token(Token::KEY, mark)); + } + + // Value + void Scanner::ScanValue() + { + // and check that simple key + bool isSimpleKey = VerifySimpleKey(); + m_canBeJSONFlow = false; + + if(isSimpleKey) { + // can't follow a simple key with another simple key (dunno why, though - it seems fine) + m_simpleKeyAllowed = false; + } else { + // handle values diffently in the block context (and manage indents) + if(InBlockContext()) { + if(!m_simpleKeyAllowed) + throw ParserException(INPUT.mark(), ErrorMsg::MAP_VALUE); + + PushIndentTo(INPUT.column(), IndentMarker::MAP); + } + + // can only put a simple key here if we're in block context + m_simpleKeyAllowed = InBlockContext(); + } + + // eat + Mark mark = INPUT.mark(); + INPUT.eat(1); + m_tokens.push(Token(Token::VALUE, mark)); + } + + // AnchorOrAlias + void Scanner::ScanAnchorOrAlias() + { + bool alias; + std::string name; + + // insert a potential simple key + InsertPotentialSimpleKey(); + m_simpleKeyAllowed = false; + m_canBeJSONFlow = false; + + // eat the indicator + Mark mark = INPUT.mark(); + char indicator = INPUT.get(); + alias = (indicator == Keys::Alias); + + // now eat the content + while(Exp::AlphaNumeric().Matches(INPUT)) + name += INPUT.get(); + + // we need to have read SOMETHING! + if(name.empty()) + throw ParserException(INPUT.mark(), alias ? ErrorMsg::ALIAS_NOT_FOUND : ErrorMsg::ANCHOR_NOT_FOUND); + + // and needs to end correctly + if(INPUT && !Exp::AnchorEnd().Matches(INPUT)) + throw ParserException(INPUT.mark(), alias ? ErrorMsg::CHAR_IN_ALIAS : ErrorMsg::CHAR_IN_ANCHOR); + + // and we're done + Token token(alias ? Token::ALIAS : Token::ANCHOR, mark); + token.value = name; + m_tokens.push(token); + } + + // Tag + void Scanner::ScanTag() + { + // insert a potential simple key + InsertPotentialSimpleKey(); + m_simpleKeyAllowed = false; + m_canBeJSONFlow = false; + + Token token(Token::TAG, INPUT.mark()); + + // eat the indicator + INPUT.get(); + + if(INPUT && INPUT.peek() == Keys::VerbatimTagStart){ + std::string tag = ScanVerbatimTag(INPUT); + + token.value = tag; + token.data = Tag::VERBATIM; + } else { + bool canBeHandle; + token.value = ScanTagHandle(INPUT, canBeHandle); + token.data = (token.value.empty() ? Tag::SECONDARY_HANDLE : Tag::PRIMARY_HANDLE); + + // is there a suffix? + if(canBeHandle && INPUT.peek() == Keys::Tag) { + // eat the indicator + INPUT.get(); + token.params.push_back(ScanTagSuffix(INPUT)); + token.data = Tag::NAMED_HANDLE; + } + } + + m_tokens.push(token); + } + + // PlainScalar + void Scanner::ScanPlainScalar() + { + std::string scalar; + + // set up the scanning parameters + ScanScalarParams params; + params.end = (InFlowContext() ? Exp::EndScalarInFlow() : Exp::EndScalar()) || (Exp::BlankOrBreak() + Exp::Comment()); + params.eatEnd = false; + params.indent = (InFlowContext() ? 0 : GetTopIndent() + 1); + params.fold = FOLD_FLOW; + params.eatLeadingWhitespace = true; + params.trimTrailingSpaces = true; + params.chomp = STRIP; + params.onDocIndicator = BREAK; + params.onTabInIndentation = THROW; + + // insert a potential simple key + InsertPotentialSimpleKey(); + + Mark mark = INPUT.mark(); + scalar = ScanScalar(INPUT, params); + + // can have a simple key only if we ended the scalar by starting a new line + m_simpleKeyAllowed = params.leadingSpaces; + m_canBeJSONFlow = false; + + // finally, check and see if we ended on an illegal character + //if(Exp::IllegalCharInScalar.Matches(INPUT)) + // throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_SCALAR); + + Token token(Token::SCALAR, mark); + token.value = scalar; + m_tokens.push(token); + } + + // QuotedScalar + void Scanner::ScanQuotedScalar() + { + std::string scalar; + + // peek at single or double quote (don't eat because we need to preserve (for the time being) the input position) + char quote = INPUT.peek(); + bool single = (quote == '\''); + + // setup the scanning parameters + ScanScalarParams params; + params.end = (single ? RegEx(quote) && !Exp::EscSingleQuote() : RegEx(quote)); + params.eatEnd = true; + params.escape = (single ? '\'' : '\\'); + params.indent = 0; + params.fold = FOLD_FLOW; + params.eatLeadingWhitespace = true; + params.trimTrailingSpaces = false; + params.chomp = CLIP; + params.onDocIndicator = THROW; + + // insert a potential simple key + InsertPotentialSimpleKey(); + + Mark mark = INPUT.mark(); + + // now eat that opening quote + INPUT.get(); + + // and scan + scalar = ScanScalar(INPUT, params); + m_simpleKeyAllowed = false; + m_canBeJSONFlow = true; + + Token token(Token::SCALAR, mark); + token.value = scalar; + m_tokens.push(token); + } + + // BlockScalarToken + // . These need a little extra processing beforehand. + // . We need to scan the line where the indicator is (this doesn't count as part of the scalar), + // and then we need to figure out what level of indentation we'll be using. + void Scanner::ScanBlockScalar() + { + std::string scalar; + + ScanScalarParams params; + params.indent = 1; + params.detectIndent = true; + + // eat block indicator ('|' or '>') + Mark mark = INPUT.mark(); + char indicator = INPUT.get(); + params.fold = (indicator == Keys::FoldedScalar ? FOLD_BLOCK : DONT_FOLD); + + // eat chomping/indentation indicators + params.chomp = CLIP; + int n = Exp::Chomp().Match(INPUT); + for(int i=0;i= 0) + params.indent += GetTopIndent(); + + params.eatLeadingWhitespace = false; + params.trimTrailingSpaces = false; + params.onTabInIndentation = THROW; + + scalar = ScanScalar(INPUT, params); + + // simple keys always ok after block scalars (since we're gonna start a new line anyways) + m_simpleKeyAllowed = true; + m_canBeJSONFlow = false; + + Token token(Token::SCALAR, mark); + token.value = scalar; + m_tokens.push(token); + } +} diff --git a/libs/yaml-cpp/src/sequence.cpp b/libs/yaml-cpp/src/sequence.cpp new file mode 100644 index 0000000..0f96e27 --- /dev/null +++ b/libs/yaml-cpp/src/sequence.cpp @@ -0,0 +1,169 @@ +#include "sequence.h" +#include "node.h" +#include "scanner.h" +#include "token.h" +#include "emitter.h" +#include + +namespace YAML +{ + Sequence::Sequence() + { + + } + + Sequence::Sequence(const std::vector& data) + { + for(std::size_t i=0;iClone().release()); + } + + Sequence::~Sequence() + { + Clear(); + } + + void Sequence::Clear() + { + for(std::size_t i=0;i::const_iterator& it) const + { + it = m_data.begin(); + return true; + } + + bool Sequence::GetEnd(std::vector ::const_iterator& it) const + { + it = m_data.end(); + return true; + } + + Node *Sequence::GetNode(std::size_t i) const + { + if(i < m_data.size()) + return m_data[i]; + return 0; + } + + std::size_t Sequence::GetSize() const + { + return m_data.size(); + } + + void Sequence::Parse(Scanner *pScanner, ParserState& state) + { + Clear(); + + // split based on start token + switch(pScanner->peek().type) { + case Token::BLOCK_SEQ_START: ParseBlock(pScanner, state); break; + case Token::FLOW_SEQ_START: ParseFlow(pScanner, state); break; + default: break; + } + } + + void Sequence::ParseBlock(Scanner *pScanner, ParserState& state) + { + // eat start token + pScanner->pop(); + state.PushCollectionType(ParserState::BLOCK_SEQ); + + while(1) { + if(pScanner->empty()) + throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ); + + Token token = pScanner->peek(); + if(token.type != Token::BLOCK_ENTRY && token.type != Token::BLOCK_SEQ_END) + throw ParserException(token.mark, ErrorMsg::END_OF_SEQ); + + pScanner->pop(); + if(token.type == Token::BLOCK_SEQ_END) + break; + + Node *pNode = new Node; + m_data.push_back(pNode); + + // check for null + if(!pScanner->empty()) { + const Token& token = pScanner->peek(); + if(token.type == Token::BLOCK_ENTRY || token.type == Token::BLOCK_SEQ_END) + continue; + } + + pNode->Parse(pScanner, state); + } + + state.PopCollectionType(ParserState::BLOCK_SEQ); + } + + void Sequence::ParseFlow(Scanner *pScanner, ParserState& state) + { + // eat start token + pScanner->pop(); + state.PushCollectionType(ParserState::FLOW_SEQ); + + while(1) { + if(pScanner->empty()) + throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ_FLOW); + + // first check for end + if(pScanner->peek().type == Token::FLOW_SEQ_END) { + pScanner->pop(); + break; + } + + // then read the node + Node *pNode = new Node; + m_data.push_back(pNode); + pNode->Parse(pScanner, state); + + // now eat the separator (or could be a sequence end, which we ignore - but if it's neither, then it's a bad node) + Token& token = pScanner->peek(); + if(token.type == Token::FLOW_ENTRY) + pScanner->pop(); + else if(token.type != Token::FLOW_SEQ_END) + throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW); + } + + state.PopCollectionType(ParserState::FLOW_SEQ); + } + + void Sequence::Write(Emitter& out) const + { + out << BeginSeq; + for(std::size_t i=0;iCompare(this); + } + + int Sequence::Compare(Sequence *pSeq) + { + std::size_t n = m_data.size(), m = pSeq->m_data.size(); + if(n < m) + return -1; + else if(n > m) + return 1; + + for(std::size_t i=0;iCompare(*pSeq->m_data[i]); + if(cmp != 0) + return cmp; + } + + return 0; + } +} diff --git a/libs/yaml-cpp/src/sequence.h b/libs/yaml-cpp/src/sequence.h new file mode 100644 index 0000000..49bdc83 --- /dev/null +++ b/libs/yaml-cpp/src/sequence.h @@ -0,0 +1,49 @@ +#pragma once + +#ifndef SEQUENCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define SEQUENCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "content.h" +#include + +namespace YAML +{ + class Node; + + class Sequence: public Content + { + public: + Sequence(); + Sequence(const std::vector& data); + virtual ~Sequence(); + + void Clear(); + virtual Content *Clone() const; + + virtual bool GetBegin(std::vector ::const_iterator& it) const; + virtual bool GetEnd(std::vector ::const_iterator& it) const; + virtual Node *GetNode(std::size_t i) const; + virtual std::size_t GetSize() const; + + virtual void Parse(Scanner *pScanner, ParserState& state); + virtual void Write(Emitter& out) const; + + virtual bool IsSequence() const { return true; } + + // ordering + virtual int Compare(Content *pContent); + virtual int Compare(Scalar *) { return 1; } + virtual int Compare(Sequence *pSeq); + virtual int Compare(Map *) { return -1; } + + private: + void ParseBlock(Scanner *pScanner, ParserState& state); + void ParseFlow(Scanner *pScanner, ParserState& state); + + protected: + std::vector m_data; + }; +} + +#endif // SEQUENCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/setting.h b/libs/yaml-cpp/src/setting.h new file mode 100644 index 0000000..5619c59 --- /dev/null +++ b/libs/yaml-cpp/src/setting.h @@ -0,0 +1,103 @@ +#pragma once + +#ifndef SETTING_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define SETTING_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include +#include +#include "noncopyable.h" + +namespace YAML +{ + class SettingChangeBase; + + template + class Setting + { + public: + Setting(): m_value() {} + + const T get() const { return m_value; } + std::auto_ptr set(const T& value); + void restore(const Setting& oldSetting) { + m_value = oldSetting.get(); + } + + private: + T m_value; + }; + + class SettingChangeBase + { + public: + virtual ~SettingChangeBase() {} + virtual void pop() = 0; + }; + + template + class SettingChange: public SettingChangeBase + { + public: + SettingChange(Setting *pSetting): m_pCurSetting(pSetting) { + // copy old setting to save its state + m_oldSetting = *pSetting; + } + + virtual void pop() { + m_pCurSetting->restore(m_oldSetting); + } + + private: + Setting *m_pCurSetting; + Setting m_oldSetting; + }; + + template + inline std::auto_ptr Setting::set(const T& value) { + std::auto_ptr pChange(new SettingChange (this)); + m_value = value; + return pChange; + } + + class SettingChanges: private noncopyable + { + public: + SettingChanges() {} + ~SettingChanges() { clear(); } + + void clear() { + restore(); + + for(setting_changes::const_iterator it=m_settingChanges.begin();it!=m_settingChanges.end();++it) + delete *it; + m_settingChanges.clear(); + } + + void restore() { + for(setting_changes::const_iterator it=m_settingChanges.begin();it!=m_settingChanges.end();++it) + (*it)->pop(); + } + + void push(std::auto_ptr pSettingChange) { + m_settingChanges.push_back(pSettingChange.release()); + } + + // like std::auto_ptr - assignment is transfer of ownership + SettingChanges& operator = (SettingChanges& rhs) { + if(this == &rhs) + return *this; + + clear(); + m_settingChanges = rhs.m_settingChanges; + rhs.m_settingChanges.clear(); + return *this; + } + + private: + typedef std::vector setting_changes; + setting_changes m_settingChanges; + }; +} + +#endif // SETTING_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/simplekey.cpp b/libs/yaml-cpp/src/simplekey.cpp new file mode 100644 index 0000000..d8d1932 --- /dev/null +++ b/libs/yaml-cpp/src/simplekey.cpp @@ -0,0 +1,139 @@ +#include "scanner.h" +#include "token.h" +#include "exceptions.h" +#include "exp.h" + +namespace YAML +{ + Scanner::SimpleKey::SimpleKey(const Mark& mark_, int flowLevel_) + : mark(mark_), flowLevel(flowLevel_), pIndent(0), pMapStart(0), pKey(0) + { + } + + void Scanner::SimpleKey::Validate() + { + // Note: pIndent will *not* be garbage here; + // we "garbage collect" them so we can + // always refer to them + if(pIndent) + pIndent->status = IndentMarker::VALID; + if(pMapStart) + pMapStart->status = Token::VALID; + if(pKey) + pKey->status = Token::VALID; + } + + void Scanner::SimpleKey::Invalidate() + { + if(pIndent) + pIndent->status = IndentMarker::INVALID; + if(pMapStart) + pMapStart->status = Token::INVALID; + if(pKey) + pKey->status = Token::INVALID; + } + + // CanInsertPotentialSimpleKey + bool Scanner::CanInsertPotentialSimpleKey() const + { + if(!m_simpleKeyAllowed) + return false; + + return !ExistsActiveSimpleKey(); + } + + // ExistsActiveSimpleKey + // . Returns true if there's a potential simple key at our flow level + // (there's allowed at most one per flow level, i.e., at the start of the flow start token) + bool Scanner::ExistsActiveSimpleKey() const + { + if(m_simpleKeys.empty()) + return false; + + const SimpleKey& key = m_simpleKeys.top(); + return key.flowLevel == GetFlowLevel(); + } + + // InsertPotentialSimpleKey + // . If we can, add a potential simple key to the queue, + // and save it on a stack. + void Scanner::InsertPotentialSimpleKey() + { + if(!CanInsertPotentialSimpleKey()) + return; + + SimpleKey key(INPUT.mark(), GetFlowLevel()); + + // first add a map start, if necessary + if(InBlockContext()) { + key.pIndent = PushIndentTo(INPUT.column(), IndentMarker::MAP); + if(key.pIndent) { + key.pIndent->status = IndentMarker::UNKNOWN; + key.pMapStart = key.pIndent->pStartToken; + key.pMapStart->status = Token::UNVERIFIED; + } + } + + // then add the (now unverified) key + m_tokens.push(Token(Token::KEY, INPUT.mark())); + key.pKey = &m_tokens.back(); + key.pKey->status = Token::UNVERIFIED; + + m_simpleKeys.push(key); + } + + // InvalidateSimpleKey + // . Automatically invalidate the simple key in our flow level + void Scanner::InvalidateSimpleKey() + { + if(m_simpleKeys.empty()) + return; + + // grab top key + SimpleKey& key = m_simpleKeys.top(); + if(key.flowLevel != GetFlowLevel()) + return; + + key.Invalidate(); + m_simpleKeys.pop(); + } + + // VerifySimpleKey + // . Determines whether the latest simple key to be added is valid, + // and if so, makes it valid. + bool Scanner::VerifySimpleKey() + { + if(m_simpleKeys.empty()) + return false; + + // grab top key + SimpleKey key = m_simpleKeys.top(); + + // only validate if we're in the correct flow level + if(key.flowLevel != GetFlowLevel()) + return false; + + m_simpleKeys.pop(); + + bool isValid = true; + + // needs to be less than 1024 characters and inline + if(INPUT.line() != key.mark.line || INPUT.pos() - key.mark.pos > 1024) + isValid = false; + + // invalidate key + if(isValid) + key.Validate(); + else + key.Invalidate(); + + return isValid; + } + + void Scanner::PopAllSimpleKeys() + { + while(!m_simpleKeys.empty()) + m_simpleKeys.pop(); + } +} + diff --git a/libs/yaml-cpp/src/stream.cpp b/libs/yaml-cpp/src/stream.cpp new file mode 100644 index 0000000..0d1426a --- /dev/null +++ b/libs/yaml-cpp/src/stream.cpp @@ -0,0 +1,454 @@ +#include "stream.h" +#include +#include "exp.h" + +#ifndef YAML_PREFETCH_SIZE +#define YAML_PREFETCH_SIZE 2048 +#endif + +#define S_ARRAY_SIZE( A ) (sizeof(A)/sizeof(*(A))) +#define S_ARRAY_END( A ) ((A) + S_ARRAY_SIZE(A)) + +#define CP_REPLACEMENT_CHARACTER (0xFFFD) + +namespace YAML +{ + enum UtfIntroState { + uis_start, + uis_utfbe_b1, + uis_utf32be_b2, + uis_utf32be_bom3, + uis_utf32be, + uis_utf16be, + uis_utf16be_bom1, + uis_utfle_bom1, + uis_utf16le_bom2, + uis_utf32le_bom3, + uis_utf16le, + uis_utf32le, + uis_utf8_imp, + uis_utf16le_imp, + uis_utf32le_imp3, + uis_utf8_bom1, + uis_utf8_bom2, + uis_utf8, + uis_error + }; + + enum UtfIntroCharType { + uict00, + uictBB, + uictBF, + uictEF, + uictFE, + uictFF, + uictAscii, + uictOther, + uictMax + }; + + static bool s_introFinalState[] = { + false, //uis_start + false, //uis_utfbe_b1 + false, //uis_utf32be_b2 + false, //uis_utf32be_bom3 + true, //uis_utf32be + true, //uis_utf16be + false, //uis_utf16be_bom1 + false, //uis_utfle_bom1 + false, //uis_utf16le_bom2 + false, //uis_utf32le_bom3 + true, //uis_utf16le + true, //uis_utf32le + false, //uis_utf8_imp + false, //uis_utf16le_imp + false, //uis_utf32le_imp3 + false, //uis_utf8_bom1 + false, //uis_utf8_bom2 + true, //uis_utf8 + true, //uis_error + }; + + static UtfIntroState s_introTransitions[][uictMax] = { + // uict00, uictBB, uictBF, uictEF, uictFE, uictFF, uictAscii, uictOther + {uis_utfbe_b1, uis_utf8, uis_utf8, uis_utf8_bom1, uis_utf16be_bom1, uis_utfle_bom1, uis_utf8_imp, uis_utf8}, + {uis_utf32be_b2, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf16be, uis_utf8}, + {uis_utf32be, uis_utf8, uis_utf8, uis_utf8, uis_utf32be_bom3, uis_utf8, uis_utf8, uis_utf8}, + {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf32be, uis_utf8, uis_utf8}, + {uis_utf32be, uis_utf32be, uis_utf32be, uis_utf32be, uis_utf32be, uis_utf32be, uis_utf32be, uis_utf32be}, + {uis_utf16be, uis_utf16be, uis_utf16be, uis_utf16be, uis_utf16be, uis_utf16be, uis_utf16be, uis_utf16be}, + {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf16be, uis_utf8, uis_utf8}, + {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf16le_bom2, uis_utf8, uis_utf8, uis_utf8}, + {uis_utf32le_bom3, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le}, + {uis_utf32le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le}, + {uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le}, + {uis_utf32le, uis_utf32le, uis_utf32le, uis_utf32le, uis_utf32le, uis_utf32le, uis_utf32le, uis_utf32le}, + {uis_utf16le_imp, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8}, + {uis_utf32le_imp3, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le}, + {uis_utf32le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le}, + {uis_utf8, uis_utf8_bom2, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8}, + {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8}, + {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8}, + }; + + static char s_introUngetCount[][uictMax] = { + // uict00, uictBB, uictBF, uictEF, uictFE, uictFF, uictAscii, uictOther + {0, 1, 1, 0, 0, 0, 0, 1}, + {0, 2, 2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 0, 3, 3, 3}, + {4, 4, 4, 4, 4, 0, 4, 4}, + {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 0, 2, 2}, + {2, 2, 2, 2, 0, 2, 2, 2}, + {0, 1, 1, 1, 1, 1, 1, 1}, + {0, 2, 2, 2, 2, 2, 2, 2}, + {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1}, + {0, 2, 2, 2, 2, 2, 2, 2}, + {0, 3, 3, 3, 3, 3, 3, 3}, + {4, 4, 4, 4, 4, 4, 4, 4}, + {2, 0, 2, 2, 2, 2, 2, 2}, + {3, 3, 0, 3, 3, 3, 3, 3}, + {1, 1, 1, 1, 1, 1, 1, 1}, + }; + + inline UtfIntroCharType IntroCharTypeOf(std::istream::int_type ch) + { + if (std::istream::traits_type::eof() == ch) { + return uictOther; + } + + switch (ch) { + case 0: return uict00; + case 0xBB: return uictBB; + case 0xBF: return uictBF; + case 0xEF: return uictEF; + case 0xFE: return uictFE; + case 0xFF: return uictFF; + } + + if ((ch > 0) && (ch < 0xFF)) { + return uictAscii; + } + + return uictOther; + } + + inline char Utf8Adjust(unsigned long ch, unsigned char lead_bits, unsigned char rshift) + { + const unsigned char header = ((1 << lead_bits) - 1) << (8 - lead_bits); + const unsigned char mask = (0xFF >> (lead_bits + 1)); + return static_cast(static_cast( + header | ((ch >> rshift) & mask) + )); + } + + inline void QueueUnicodeCodepoint(std::deque& q, unsigned long ch) + { + // We are not allowed to queue the Stream::eof() codepoint, so + // replace it with CP_REPLACEMENT_CHARACTER + if (static_cast(Stream::eof()) == ch) + { + ch = CP_REPLACEMENT_CHARACTER; + } + + if (ch < 0x80) + { + q.push_back(Utf8Adjust(ch, 0, 0)); + } + else if (ch < 0x800) + { + q.push_back(Utf8Adjust(ch, 2, 6)); + q.push_back(Utf8Adjust(ch, 1, 0)); + } + else if (ch < 0x10000) + { + q.push_back(Utf8Adjust(ch, 3, 12)); + q.push_back(Utf8Adjust(ch, 1, 6)); + q.push_back(Utf8Adjust(ch, 1, 0)); + } + else + { + q.push_back(Utf8Adjust(ch, 4, 18)); + q.push_back(Utf8Adjust(ch, 1, 12)); + q.push_back(Utf8Adjust(ch, 1, 6)); + q.push_back(Utf8Adjust(ch, 1, 0)); + } + } + + Stream::Stream(std::istream& input) + : m_input(input), m_nPushedBack(0), + m_pPrefetched(new unsigned char[YAML_PREFETCH_SIZE]), + m_nPrefetchedAvailable(0), m_nPrefetchedUsed(0) + { + typedef std::istream::traits_type char_traits; + + if(!input) + return; + + // Determine (or guess) the character-set by reading the BOM, if any. See + // the YAML specification for the determination algorithm. + char_traits::int_type intro[4]; + int nIntroUsed = 0; + UtfIntroState state = uis_start; + for (; !s_introFinalState[state]; ) { + std::istream::int_type ch = input.get(); + intro[nIntroUsed++] = ch; + UtfIntroCharType charType = IntroCharTypeOf(ch); + UtfIntroState newState = s_introTransitions[state][charType]; + int nUngets = s_introUngetCount[state][charType]; + if (nUngets > 0) { + for (; nUngets > 0; --nUngets) { + if (char_traits::eof() != intro[--nIntroUsed]) { + m_bufPushback[m_nPushedBack++] = + char_traits::to_char_type(intro[nIntroUsed]); + } + } + } + state = newState; + } + + switch (state) { + case uis_utf8: m_charSet = utf8; break; + case uis_utf16le: m_charSet = utf16le; break; + case uis_utf16be: m_charSet = utf16be; break; + case uis_utf32le: m_charSet = utf32le; break; + case uis_utf32be: m_charSet = utf32be; break; + default: m_charSet = utf8; break; + } + + ReadAheadTo(0); + } + + Stream::~Stream() + { + delete[] m_pPrefetched; + } + + char Stream::peek() const + { + if (m_readahead.empty()) + { + return Stream::eof(); + } + + return m_readahead[0]; + } + + Stream::operator bool() const + { + return m_input.good() || (!m_readahead.empty() && m_readahead[0] != Stream::eof()); + } + + // get + // . Extracts a character from the stream and updates our position + char Stream::get() + { + char ch = peek(); + AdvanceCurrent(); + m_mark.column++; + + if(ch == '\n') { + m_mark.column = 0; + m_mark.line++; + } + + return ch; + } + + // get + // . Extracts 'n' characters from the stream and updates our position + std::string Stream::get(int n) + { + std::string ret; + ret.reserve(n); + for(int i=0;i i; + } + + void Stream::StreamInUtf8() const + { + unsigned char b = GetNextByte(); + if (m_input.good()) + { + m_readahead.push_back(b); + } + } + + void Stream::StreamInUtf16() const + { + unsigned long ch = 0; + unsigned char bytes[2]; + int nBigEnd = (m_charSet == utf16be) ? 0 : 1; + + bytes[0] = GetNextByte(); + bytes[1] = GetNextByte(); + if (!m_input.good()) + { + return; + } + ch = (static_cast(bytes[nBigEnd]) << 8) | + static_cast(bytes[1 ^ nBigEnd]); + + if (ch >= 0xDC00 && ch < 0xE000) + { + // Trailing (low) surrogate...ugh, wrong order + QueueUnicodeCodepoint(m_readahead, CP_REPLACEMENT_CHARACTER); + return; + } + else if (ch >= 0xD800 && ch < 0xDC00) + { + // ch is a leading (high) surrogate + + // Four byte UTF-8 code point + + // Read the trailing (low) surrogate + for (;;) + { + bytes[0] = GetNextByte(); + bytes[1] = GetNextByte(); + if (!m_input.good()) + { + QueueUnicodeCodepoint(m_readahead, CP_REPLACEMENT_CHARACTER); + return; + } + unsigned long chLow = (static_cast(bytes[nBigEnd]) << 8) | + static_cast(bytes[1 ^ nBigEnd]); + if (chLow < 0xDC00 || ch >= 0xE000) + { + // Trouble...not a low surrogate. Dump a REPLACEMENT CHARACTER into the stream. + QueueUnicodeCodepoint(m_readahead, CP_REPLACEMENT_CHARACTER); + + // Deal with the next UTF-16 unit + if (chLow < 0xD800 || ch >= 0xE000) + { + // Easiest case: queue the codepoint and return + QueueUnicodeCodepoint(m_readahead, ch); + return; + } + else + { + // Start the loop over with the new high surrogate + ch = chLow; + continue; + } + } + + // Select the payload bits from the high surrogate + ch &= 0x3FF; + ch <<= 10; + + // Include bits from low surrogate + ch |= (chLow & 0x3FF); + + // Add the surrogacy offset + ch += 0x10000; + } + } + + QueueUnicodeCodepoint(m_readahead, ch); + } + + inline char* ReadBuffer(unsigned char* pBuffer) + { + return reinterpret_cast(pBuffer); + } + + unsigned char Stream::GetNextByte() const + { + if (m_nPushedBack) + { + return m_bufPushback[--m_nPushedBack]; + } + + if (m_nPrefetchedUsed >= m_nPrefetchedAvailable) + { + std::streambuf *pBuf = m_input.rdbuf(); + m_nPrefetchedAvailable = pBuf->sgetn(ReadBuffer(m_pPrefetched), + YAML_PREFETCH_SIZE); + m_nPrefetchedUsed = 0; + if (!m_nPrefetchedAvailable) + { + m_input.setstate(std::ios_base::eofbit); + } + + if (0 == m_nPrefetchedAvailable) + { + return 0; + } + } + + return m_pPrefetched[m_nPrefetchedUsed++]; + } + + void Stream::StreamInUtf32() const + { + static int indexes[2][4] = { + {3, 2, 1, 0}, + {0, 1, 2, 3} + }; + + unsigned long ch = 0; + unsigned char bytes[4]; + int* pIndexes = (m_charSet == utf32be) ? indexes[1] : indexes[0]; + + bytes[0] = GetNextByte(); + bytes[1] = GetNextByte(); + bytes[2] = GetNextByte(); + bytes[3] = GetNextByte(); + if (!m_input.good()) + { + return; + } + + for (int i = 0; i < 4; ++i) + { + ch <<= 8; + ch |= bytes[pIndexes[i]]; + } + + QueueUnicodeCodepoint(m_readahead, ch); + } +} diff --git a/libs/yaml-cpp/src/stream.h b/libs/yaml-cpp/src/stream.h new file mode 100644 index 0000000..5d78551 --- /dev/null +++ b/libs/yaml-cpp/src/stream.h @@ -0,0 +1,80 @@ +#pragma once + +#ifndef STREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define STREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "noncopyable.h" +#include "mark.h" +#include +#include +#include +#include +#include + +namespace YAML +{ + static const size_t MAX_PARSER_PUSHBACK = 8; + + class Stream: private noncopyable + { + public: + friend class StreamCharSource; + + Stream(std::istream& input); + ~Stream(); + + operator bool() const; + bool operator !() const { return !static_cast (*this); } + + char peek() const; + char get(); + std::string get(int n); + void eat(int n = 1); + + static char eof() { return 0x04; } + + const Mark mark() const { return m_mark; } + int pos() const { return m_mark.pos; } + int line() const { return m_mark.line; } + int column() const { return m_mark.column; } + void ResetColumn() { m_mark.column = 0; } + + private: + enum CharacterSet {utf8, utf16le, utf16be, utf32le, utf32be}; + + std::istream& m_input; + Mark m_mark; + + CharacterSet m_charSet; + unsigned char m_bufPushback[MAX_PARSER_PUSHBACK]; + mutable size_t m_nPushedBack; + mutable std::deque m_readahead; + unsigned char* const m_pPrefetched; + mutable size_t m_nPrefetchedAvailable; + mutable size_t m_nPrefetchedUsed; + + void AdvanceCurrent(); + char CharAt(size_t i) const; + bool ReadAheadTo(size_t i) const; + bool _ReadAheadTo(size_t i) const; + void StreamInUtf8() const; + void StreamInUtf16() const; + void StreamInUtf32() const; + unsigned char GetNextByte() const; + }; + + // CharAt + // . Unchecked access + inline char Stream::CharAt(size_t i) const { + return m_readahead[i]; + } + + inline bool Stream::ReadAheadTo(size_t i) const { + if(m_readahead.size() > i) + return true; + return _ReadAheadTo(i); + } +} + +#endif // STREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/streamcharsource.h b/libs/yaml-cpp/src/streamcharsource.h new file mode 100644 index 0000000..10debc8 --- /dev/null +++ b/libs/yaml-cpp/src/streamcharsource.h @@ -0,0 +1,46 @@ +#pragma once + +#ifndef STREAMCHARSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define STREAMCHARSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "noncopyable.h" +#include + +namespace YAML +{ + class StreamCharSource + { + public: + StreamCharSource(const Stream& stream): m_offset(0), m_stream(stream) {} + StreamCharSource(const StreamCharSource& source): m_offset(source.m_offset), m_stream(source.m_stream) {} + ~StreamCharSource() {} + + operator bool() const; + char operator [] (std::size_t i) const { return m_stream.CharAt(m_offset + i); } + bool operator !() const { return !static_cast(*this); } + + const StreamCharSource operator + (int i) const; + + private: + std::size_t m_offset; + const Stream& m_stream; + + StreamCharSource& operator = (const StreamCharSource&); // non-assignable + }; + + inline StreamCharSource::operator bool() const { + return m_stream.ReadAheadTo(m_offset); + } + + inline const StreamCharSource StreamCharSource::operator + (int i) const { + StreamCharSource source(*this); + if(static_cast (source.m_offset) + i >= 0) + source.m_offset += i; + else + source.m_offset = 0; + return source; + } +} + +#endif // STREAMCHARSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/stringsource.h b/libs/yaml-cpp/src/stringsource.h new file mode 100644 index 0000000..20d56ae --- /dev/null +++ b/libs/yaml-cpp/src/stringsource.h @@ -0,0 +1,45 @@ +#pragma once + +#ifndef STRINGSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define STRINGSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include + +namespace YAML +{ + class StringCharSource + { + public: + StringCharSource(const char *str, std::size_t size): m_str(str), m_size(size), m_offset(0) {} + + operator bool() const { return m_offset < m_size; } + char operator [] (std::size_t i) const { return m_str[m_offset + i]; } + bool operator !() const { return !static_cast(*this); } + + const StringCharSource operator + (int i) const { + StringCharSource source(*this); + if(static_cast (source.m_offset) + i >= 0) + source.m_offset += i; + else + source.m_offset = 0; + return source; + } + + StringCharSource& operator ++ () { + ++m_offset; + return *this; + } + + StringCharSource& operator += (std::size_t offset) { + m_offset += offset; + return *this; + } + private: + const char *m_str; + std::size_t m_size; + std::size_t m_offset; + }; +} + +#endif // STRINGSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/tag.cpp b/libs/yaml-cpp/src/tag.cpp new file mode 100644 index 0000000..694a4f3 --- /dev/null +++ b/libs/yaml-cpp/src/tag.cpp @@ -0,0 +1,50 @@ +#include "tag.h" +#include "token.h" +#include "parserstate.h" +#include + +namespace YAML +{ + Tag::Tag(const Token& token): type(static_cast(token.data)) + { + switch(type) { + case VERBATIM: + value = token.value; + break; + case PRIMARY_HANDLE: + value = token.value; + break; + case SECONDARY_HANDLE: + value = token.value; + break; + case NAMED_HANDLE: + handle = token.value; + value = token.params[0]; + break; + case NON_SPECIFIC: + break; + default: + assert(false); + } + } + + const std::string Tag::Translate(const ParserState& state) + { + switch(type) { + case VERBATIM: + return value; + case PRIMARY_HANDLE: + return state.TranslateTagHandle("!") + value; + case SECONDARY_HANDLE: + return state.TranslateTagHandle("!!") + value; + case NAMED_HANDLE: + return state.TranslateTagHandle("!" + handle + "!") + value; + case NON_SPECIFIC: + // TODO: + return "!"; + default: + assert(false); + } + } +} + diff --git a/libs/yaml-cpp/src/tag.h b/libs/yaml-cpp/src/tag.h new file mode 100644 index 0000000..b448994 --- /dev/null +++ b/libs/yaml-cpp/src/tag.h @@ -0,0 +1,26 @@ +#pragma once + +#ifndef TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include + +namespace YAML +{ + struct Token; + struct ParserState; + + struct Tag { + enum TYPE { + VERBATIM, PRIMARY_HANDLE, SECONDARY_HANDLE, NAMED_HANDLE, NON_SPECIFIC + }; + + Tag(const Token& token); + const std::string Translate(const ParserState& state); + + TYPE type; + std::string handle, value; + }; +} + +#endif // TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/libs/yaml-cpp/src/token.h b/libs/yaml-cpp/src/token.h new file mode 100644 index 0000000..cf7c0fe --- /dev/null +++ b/libs/yaml-cpp/src/token.h @@ -0,0 +1,82 @@ +#pragma once + +#ifndef TOKEN_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define TOKEN_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + + +#include "mark.h" +#include +#include +#include + +namespace YAML +{ + const std::string TokenNames[] = { + "DIRECTIVE", + "DOC_START", + "DOC_END", + "BLOCK_SEQ_START", + "BLOCK_MAP_START", + "BLOCK_SEQ_END", + "BLOCK_MAP_END", + "BLOCK_ENTRY", + "FLOW_SEQ_START", + "FLOW_MAP_START", + "FLOW_SEQ_END", + "FLOW_MAP_END", + "FLOW_MAP_COMPACT", + "FLOW_ENTRY", + "KEY", + "VALUE", + "ANCHOR", + "ALIAS", + "TAG", + "SCALAR" + }; + + struct Token { + // enums + enum STATUS { VALID, INVALID, UNVERIFIED }; + enum TYPE { + DIRECTIVE, + DOC_START, + DOC_END, + BLOCK_SEQ_START, + BLOCK_MAP_START, + BLOCK_SEQ_END, + BLOCK_MAP_END, + BLOCK_ENTRY, + FLOW_SEQ_START, + FLOW_MAP_START, + FLOW_SEQ_END, + FLOW_MAP_END, + FLOW_MAP_COMPACT, + FLOW_ENTRY, + KEY, + VALUE, + ANCHOR, + ALIAS, + TAG, + SCALAR + }; + + // data + Token(TYPE type_, const Mark& mark_): status(VALID), type(type_), mark(mark_), data(0) {} + + friend std::ostream& operator << (std::ostream& out, const Token& token) { + out << TokenNames[token.type] << std::string(": ") << token.value; + for(std::size_t i=0;i params; + int data; + }; +} + +#endif // TOKEN_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/client/Client.cpp b/src/client/Client.cpp index b4cb825..743c29d 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -320,17 +320,17 @@ void Client::accelerate(double x, double y, double z) { if (game.local_player == 0) return; player_accelerate_message_t msg; - msg.msg_id = MESSAGE_PLAYER_ACCELERATE; msg.player_id = game.local_player->id; msg.x = x; msg.y = y; msg.z = z; - network.sendMessage((uint8_t*) &msg, sizeof(msg)); + YAML::Emitter m; + m << msg; + network.send(m); } void Client::drop_bomb(double rx, double ry, double rz, double ttl) { bomb_drop_meesage_t msg; - msg.msg_id = MESSAGE_BOMB_DROP; msg.x = game.local_player->x + rx * 20; msg.y = game.local_player->y + ry * 20; msg.z = game.local_player->z + rz * 20; @@ -338,7 +338,9 @@ void Client::drop_bomb(double rx, double ry, double rz, double ttl) { msg.vy = game.local_player->vy + ry * 100; msg.vz = game.local_player->vz + rz * 100; msg.ttl = ttl; - network.sendMessage((uint8_t*) &msg, sizeof(msg)); + YAML::Emitter m; + m << msg; + network.send(m); } void Client::loadConsoleFont() { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 8510bef..a909e7f 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -9,11 +9,11 @@ add_library( common ) add_dependencies( common - enet pugixml bullet trimeshloader + enet pugixml bullet trimeshloader yaml-cpp ) target_link_libraries(common - enet pugixml bullet trimeshloader + enet pugixml bullet trimeshloader yaml-cpp ) if (WIN32) diff --git a/src/common/Network.cpp b/src/common/Network.cpp index 221d134..35bfaa4 100644 --- a/src/common/Network.cpp +++ b/src/common/Network.cpp @@ -11,6 +11,7 @@ #include #include +#include Network::Network(Game *game) : game(game), client_peer(0) { @@ -80,7 +81,7 @@ void Network::sendGameUpdates() { if (game->player[i].status == 0) continue; player_update_message_t msg; - msg.msg_id = MESSAGE_PLAYER_UPDATE; + //msg.msg_id = MESSAGE_PLAYER_UPDATE; msg.player_id = game->player[i].id; btVector3 v = game->player[i].body->getLinearVelocity(); btVector3 p = game->player[i].body->getWorldTransform().getOrigin(); @@ -97,7 +98,7 @@ void Network::sendGameUpdates() { } for (i = 0; i < game->teams.size(); i++) { team_update_message_t msg; - msg.msg_id = MESSAGE_TEAM_UPDATE; + //msg.msg_id = MESSAGE_TEAM_UPDATE; msg.team_id = game->teams[i].id; msg.points = game->teams[i].points; msg.wins = game->teams[i].wins; @@ -108,121 +109,141 @@ void Network::sendGameUpdates() { enet_host_flush(host); } +template +T *_message_cast(void *p, size_t l) { + if (sizeof(T) != l) { + std::cout << sizeof(T) << std::endl; + std::cout << l << std::endl; + throw(typeid(T).name()); + } + return (T *) p; +} + void Network::dispatch(enet_uint8 *data, size_t length) { +#if 0 message_t *msg = (message_t *) data; switch (msg->msg_id) { - case MESSAGE_PLAYER_SPAWN: { - player_spawn_message_t *sm = (player_spawn_message_t *) data; - Team *team = game->getTeam(sm->team_id); - player_t *player = game->spawnPlayerWithId(team, sm->player_id); - break; - } - case MESSAGE_PLAYER_KILL: { - player_kill_message_t *sm = (player_kill_message_t *) data; - player_t *player = game->getPlayer(sm->player_id); - player->status = 0; - player->team = 0; - break; - } - case MESSAGE_ACCEPT: { - accept_message_t *am = (accept_message_t *) data; - game->local_player = game->getPlayer(am->player_id); - std::cout << "[Network] accpeted player " << am->player_id << std::endl; - break; - } - case MESSAGE_PLAYER_UPDATE: { - player_update_message_t *um = (player_update_message_t *) data; - player_t *player = game->getPlayer(um->player_id); + case MESSAGE_PLAYER_SPAWN: { + player_spawn_message_t *sm = _message_cast ( + data, length); + Team *team = game->getTeam(sm->team_id); + player_t *player = game->spawnPlayerWithId(team, sm->player_id); + break; + } + case MESSAGE_PLAYER_KILL: { + player_kill_message_t *sm = _message_cast (data, + length); + player_t *player = game->getPlayer(sm->player_id); + player->status = 0; + player->team = 0; + break; + } + case MESSAGE_ACCEPT: { + accept_message_t *am = _message_cast (data, length); + game->local_player = game->getPlayer(am->player_id); + std::cout << "[Network] accpeted player " << am->player_id << std::endl; + break; + } + case MESSAGE_PLAYER_UPDATE: { + player_update_message_t *um = _message_cast ( + data, length); + player_t *player = game->getPlayer(um->player_id); #if 1 - btVector3 v = player->body->getLinearVelocity(); - btVector3 p = player->body->getWorldTransform().getOrigin(); - btVector3 v1(um->vx, um->vy, um->vz); - btVector3 p1(um->x, um->y, um->z); - player->body->activate(true); - player->body->setLinearVelocity(v1); - player->body->getWorldTransform().setOrigin((2 * p + p1) / 3); - //player->body->applyCentralForce((v1 -v)/10.0); + btVector3 v = player->body->getLinearVelocity(); + btVector3 p = player->body->getWorldTransform().getOrigin(); + btVector3 v1(um->vx, um->vy, um->vz); + btVector3 p1(um->x, um->y, um->z); + player->body->activate(true); + player->body->setLinearVelocity(v1); + player->body->getWorldTransform().setOrigin((2 * p + p1) / 3); + //player->body->applyCentralForce((v1 -v)/10.0); #endif #if 0 - std::cout << " v:" << um->vx << " " << um->vy << " " << um -> vz - << std::endl; - std::cout << " p:" << um->x << " " << um->y << " " << um -> z - << std::endl; - double threshold = 0.1; - double dx = um->x - player->x; - double dy = um->y - player->y; - double dz = um->z - player->z; - std::cout << " d:" << dx << " " << dy << " " << dz << std::endl; - if (fabs(dx) < threshold) { - player->x += 0.1 * dx; - } else { - player->x = um->x; - } - if (fabs(dy) < threshold) { - player->y += 0.1 * dy; - } else { - player->y = um->y; - } - if (fabs(dz) < threshold) { - player->z += 0.1 * dz; - } else { - player->z = um->z; - } - player->vx = um->vx; - player->vy = um->vy; - player->vz = um->vz; + std::cout << " v:" << um->vx << " " << um->vy << " " << um -> vz + << std::endl; + std::cout << " p:" << um->x << " " << um->y << " " << um -> z + << std::endl; + double threshold = 0.1; + double dx = um->x - player->x; + double dy = um->y - player->y; + double dz = um->z - player->z; + std::cout << " d:" << dx << " " << dy << " " << dz << std::endl; + if (fabs(dx) < threshold) { + player->x += 0.1 * dx; + } else { + player->x = um->x; + } + if (fabs(dy) < threshold) { + player->y += 0.1 * dy; + } else { + player->y = um->y; + } + if (fabs(dz) < threshold) { + player->z += 0.1 * dz; + } else { + player->z = um->z; + } + player->vx = um->vx; + player->vy = um->vy; + player->vz = um->vz; #endif - player->points = um->points; - break; - } - case MESSAGE_POINT_UPDATE: { - point_update_mesage_t *msg = (point_update_mesage_t *) data; - point_t *p = &game->point[msg->point_index]; - p->status = msg->status; - p->x = msg->x; - p->y = msg->y; - p->z = msg->z; - break; - } - case MESSAGE_PLAYER_ACCELERATE: { - player_accelerate_message_t *um = (player_accelerate_message_t *) data; - player_t *player = game->getPlayer(um->player_id); - player->vx += um->x; - player->vy += um->y; - player->vz += um->z; - player->body->activate(true); - player->body->applyCentralImpulse(btVector3(um->x, um->y, um->z)); - break; - } - case MESSAGE_BOMB_DROP: { - bomb_drop_meesage_t *m = (bomb_drop_meesage_t *) data; - bomb_t *bomb = game->spawn_bomb(); - if (bomb == NULL) - return; - bomb->x = m->x + m->vx * 0.0001; - bomb->y = m->y + m->vy * 0.0001; - bomb->z = m->z + m->vz * 0.0001; - bomb->vx = m->vx; - bomb->vy = m->vy; - bomb->vz = m->vz; - bomb->ttl = m->ttl; - if (client_peer == NULL) { - ENetPacket * packet = enet_packet_create(data, length, - ENET_PACKET_FLAG_RELIABLE); - enet_host_broadcast(host, 0, packet); + std::cout << um->z << std::endl; + player->points = um->points; + break; } - break; - } - case MESSAGE_TEAM_UPDATE: { - team_update_message_t *m = (team_update_message_t *) data; - Team *team = game->getTeam(m->team_id); - if (team == NULL) + case MESSAGE_POINT_UPDATE: { + point_update_mesage_t *msg = _message_cast ( + data, length); + point_t *p = &game->point[msg->point_index]; + p->status = msg->status; + p->x = msg->x; + p->y = msg->y; + p->z = msg->z; + break; + } + case MESSAGE_PLAYER_ACCELERATE: { + player_accelerate_message_t *um = _message_cast< + player_accelerate_message_t> (data, length); + player_t *player = game->getPlayer(um->player_id); + player->vx += um->x; + player->vy += um->y; + player->vz += um->z; + player->body->activate(true); + player->body->applyCentralImpulse(btVector3(um->x, um->y, um->z)); + break; + } + case MESSAGE_BOMB_DROP: { + bomb_drop_meesage_t *m = _message_cast (data, + length); + bomb_t *bomb = game->spawn_bomb(); + if (bomb == NULL) return; - team->points = m->points; - team->wins = m->wins; - break; - } + bomb->x = m->x + m->vx * 0.0001; + bomb->y = m->y + m->vy * 0.0001; + bomb->z = m->z + m->vz * 0.0001; + bomb->vx = m->vx; + bomb->vy = m->vy; + bomb->vz = m->vz; + bomb->ttl = m->ttl; + if (client_peer == NULL) { + ENetPacket * packet = enet_packet_create(data, length, + ENET_PACKET_FLAG_RELIABLE); + enet_host_broadcast(host, 0, packet); + } + break; + } + case MESSAGE_TEAM_UPDATE: { + team_update_message_t *m = _message_cast (data, + length); + Team *team = game->getTeam(m->team_id); + if (team == NULL) + return; + team->points = m->points; + team->wins = m->wins; + break; + } }; +#endif } void Network::service(uint32_t timeout) { @@ -238,12 +259,12 @@ void Network::service(uint32_t timeout) { std::cout << "A new client connected from " << event.peer->address.host << " " << event.peer->address.port << std::endl; - +#if 0 // bring new client up to date size_t i; for (i = 0; i < GAME_PLAYER_COUNT; i++) { if (game->player[i].status == 0) - continue; + continue; // send player spawn message player_spawn_message_t spwan_msg; spwan_msg.msg_id = MESSAGE_PLAYER_SPAWN; @@ -256,7 +277,7 @@ void Network::service(uint32_t timeout) { for (i = 0; i < GAME_POINT_COUNT; i++) { if (game->point[i].status == 0) - continue; + continue; point_update_mesage_t msg; msg.msg_id = MESSAGE_POINT_UPDATE; msg.point_index = i; @@ -289,7 +310,7 @@ void Network::service(uint32_t timeout) { packet = enet_packet_create(&msg, sizeof(msg), ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, packet); - +#endif // send state } break; @@ -299,14 +320,12 @@ void Network::service(uint32_t timeout) { enet_packet_destroy(event.packet); break; } - case ENET_EVENT_TYPE_DISCONNECT: - //printf("%s disconected.\n", event.peer -> data); - { + case ENET_EVENT_TYPE_DISCONNECT: { /* Reset the peer's client information. */ player_t *player = (player_t *) event.peer->data; player->status = 0; player->team = 0; - +#if 0 // send player spawn message player_kill_message_t msg; msg.msg_id = MESSAGE_PLAYER_KILL; @@ -314,6 +333,7 @@ void Network::service(uint32_t timeout) { ENetPacket * packet = enet_packet_create(&msg, sizeof(msg), ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(host, 0, packet); +#endif } event.peer->data = NULL; break; @@ -322,6 +342,56 @@ void Network::service(uint32_t timeout) { } +#define EMIT_START(msg) YAML::Emitter& operator <<(YAML::Emitter& out, const msg& m) { out << YAML::Key << "id" << YAML::Value << msg::id; +#define EMIT_FIELD(name) out << YAML::Key << #name << YAML::Value << m.name; +#define EMIT_END() out << YAML::EndMap;return out;} + +EMIT_START(player_spawn_message_t) + EMIT_FIELD(team_id) + EMIT_FIELD(player_id) + EMIT_END() + +EMIT_START(player_accelerate_message_t) + EMIT_FIELD(player_id) + EMIT_FIELD(x) + EMIT_FIELD(y) + EMIT_FIELD(z) + EMIT_END() + +EMIT_START(bomb_drop_meesage_t) + EMIT_FIELD(x) + EMIT_FIELD(y) + EMIT_FIELD(z) + EMIT_FIELD(vx) + EMIT_FIELD(vy) + EMIT_FIELD(vz) + EMIT_FIELD(ttl) + EMIT_END() + +#define PARSE_START(msg) void operator >>(const YAML::Node& node, msg& m) { +#define PARSE_FIELD(name) node[#name] >> m.name; +#define PARSE_END() } + +PARSE_START(player_spawn_message_t) PARSE_FIELD(team_id) + PARSE_FIELD(player_id)PARSE_END() + +//void Network::sendMessage(message_t &m) { +// if (client_peer) { +// msgpack::sbuffer b; +// msgpack::packer packer(b); +// m.pack(packer); +// ENetPacket * packet = enet_packet_create(b.data(), b.size(), +// ENET_PACKET_FLAG_RELIABLE); +// enet_peer_send(client_peer, 0, packet); +// } else { +// // dispatch +// } +//} + +void Network::send(const YAML::Emitter &em) { + sendMessage((uint8_t *) em.c_str(), em.size()); +} + void Network::sendMessage(uint8_t *data, size_t length) { if (client_peer) { ENetPacket * packet = enet_packet_create(data, length, diff --git a/src/common/Network.h b/src/common/Network.h index 2c7ce7c..a52f87b 100644 --- a/src/common/Network.h +++ b/src/common/Network.h @@ -11,7 +11,8 @@ #include "Game.h" #include "common.h" -#include +#include "enet/enet.h" +#include "yaml-cpp/yaml.h" #include @@ -25,70 +26,90 @@ #define MESSAGE_POINT_UPDATE 7 #define MESSAGE_TEAM_UPDATE 8 -typedef struct message_t { - uint16_t msg_id; -} message_t; +struct player_spawn_message_t { + enum { + id = 0 + }; + size_t team_id; + size_t player_id; +}; -typedef struct player_spawn_message_t { - uint16_t msg_id; - uint8_t team_id; +struct player_kill_message_t { + enum { + id = 1 + }; uint16_t player_id; -} player_spawn_message_t; +}; -typedef struct player_kill_message_t { - uint16_t msg_id; +struct accept_message_t { + enum { + id = 2 + }; uint16_t player_id; -} player_kill_message_t; +}; -typedef struct accept_message_t { - uint16_t msg_id; +struct player_update_message_t { + enum { + id = 3 + }; uint16_t player_id; -} accept_message_t; - -typedef struct player_update_message_t { - uint16_t msg_id; - uint16_t player_id; - unsigned int session; + // unsigned int session; double x, y, z; double vx, vy, vz; uint16_t points; -} player_update_message_t; +}; -typedef struct player_accelerate_message_t { - uint16_t msg_id; +struct player_accelerate_message_t { + enum { + id = 4 + }; uint16_t player_id; double x, y, z; -} player_accelerate_message_t; +}; -typedef struct bomb_drop_meesage_t { - uint16_t msg_id; +struct bomb_drop_meesage_t { + enum { + id = 5 + }; double x, y, z; double vx, vy, vz; double ttl; -} bomb_drop_meesage_t; +}; -typedef struct bomb_update_meesage_t { - uint16_t msg_id; +struct bomb_update_meesage_t { + enum { + id = 5 + }; uint16_t bomb_index; double x, y, z; double vx, vy, vz; double ttl; -} bomb_update_meesage_t; +}; -typedef struct point_update_mesage_t { - uint16_t msg_id; +struct point_update_mesage_t { + enum { + id = 7 + }; uint16_t point_index; uint8_t status; double x, y, z; -} point_update_meesage_t; +}; struct team_update_message_t { - uint16_t msg_id; + enum { + id = 8 + }; uint16_t team_id; uint16_t points; uint16_t wins; }; +#define EMIT_DEFINE(msg) YAML::Emitter& operator <<(YAML::Emitter& out, const msg& m); + +EMIT_DEFINE(player_spawn_message_t) +EMIT_DEFINE(player_accelerate_message_t) +EMIT_DEFINE(bomb_drop_meesage_t) + class Network { public: Network(Game* game); @@ -98,6 +119,7 @@ public: void service(uint32_t timeout); void sendGameUpdates(); void sendMessage(uint8_t *data, size_t length); + void send(const YAML::Emitter &em); protected: Game *game; ENetHost *host;