From 36fd93a8d5fec69fe75ae8215541359276f0606c Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Sun, 10 Jan 2016 12:08:42 -0600 Subject: [PATCH 01/37] Fix formatting when writing " as a character. --- src/emitterutils.cpp | 6 ++++-- test/integration/emitter_test.cpp | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/emitterutils.cpp b/src/emitterutils.cpp index 34da1bc..4a4c982 100644 --- a/src/emitterutils.cpp +++ b/src/emitterutils.cpp @@ -375,14 +375,16 @@ bool WriteLiteralString(ostream_wrapper& out, const std::string& str, bool WriteChar(ostream_wrapper& out, char ch) { if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) { out << ch; - } else if ((0x20 <= ch && ch <= 0x7e) || ch == ' ') { - out << "\"" << ch << "\""; + } else if (ch == '\"') { + out << "\"\\\"\""; } else if (ch == '\t') { out << "\"\\t\""; } else if (ch == '\n') { out << "\"\\n\""; } else if (ch == '\b') { out << "\"\\b\""; + } else if ((0x20 <= ch && ch <= 0x7e) || ch == ' ') { + out << "\"" << ch << "\""; } else { out << "\""; WriteDoubleQuoteEscapeSequence(out, ch); diff --git a/test/integration/emitter_test.cpp b/test/integration/emitter_test.cpp index cb7156a..e65ed85 100644 --- a/test/integration/emitter_test.cpp +++ b/test/integration/emitter_test.cpp @@ -962,6 +962,14 @@ TEST_F(EmitterTest, QuoteNull) { ExpectEmit("\"null\""); } +TEST_F(EmitterTest, ValueOfDoubleQuote) { + out << YAML::BeginMap; + out << YAML::Key << "foo" << YAML::Value << '"'; + out << YAML::EndMap; + + ExpectEmit("foo: \"\\\"\""); +} + class EmitterErrorTest : public ::testing::Test { protected: void ExpectEmitError(const std::string& expectedError) { From b57efe94e7d445713c29f863adb8c23438eaa217 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Sun, 10 Jan 2016 12:11:40 -0600 Subject: [PATCH 02/37] Bump version to 0.5.3. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1897775..d4a8e29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ project(YAML_CPP) set(YAML_CPP_VERSION_MAJOR "0") set(YAML_CPP_VERSION_MINOR "5") -set(YAML_CPP_VERSION_PATCH "2") +set(YAML_CPP_VERSION_PATCH "3") set(YAML_CPP_VERSION "${YAML_CPP_VERSION_MAJOR}.${YAML_CPP_VERSION_MINOR}.${YAML_CPP_VERSION_PATCH}") enable_testing() From 4376ebacaad3d740ad95545b01084ddf545bb5f4 Mon Sep 17 00:00:00 2001 From: Jiri Hoogland Date: Thu, 10 Dec 2015 10:33:00 -0500 Subject: [PATCH 03/37] Add PIC compile flag for GNU/CLang. - ensures shared library code is position-independent. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d4a8e29..0edf87f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,10 @@ if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR # set(GCC_EXTRA_OPTIONS "") # + if(BUILD_SHARED_LIBS) + set(GCC_EXTRA_OPTIONS "${GCC_EXTRA_OPTIONS} -fPIC") + endif() + # set(FLAG_TESTED "-Wextra") check_cxx_compiler_flag(${FLAG_TESTED} FLAG_WEXTRA) if(FLAG_WEXTRA) From 24fa1b33805c9a91df0f32c46c28e314dd7ad96f Mon Sep 17 00:00:00 2001 From: Matt Blair Date: Mon, 27 Apr 2015 16:58:38 -0400 Subject: [PATCH 04/37] Replace Boost usage with C++11 features - Adds 'std=c++11' compiler flags - Replaces boost::type_traits with std::type_traits - Replaces boost::shared_ptr with std::shared_ptr - Replaces std::auto_ptr with std::unique_ptr - Replaces raw pointers with std::unique_ptr in ptr_vector, ptr_stack, and SettingChanges - Replaces boost::noncopyable with deleted copy and assignment operators - Replaces boost::next with std::next - Replaces boost::enable_if with std::enable_if - Replaces boost::is_convertible with std::is_convertible - Replaces ptrdiff_t with std::ptrdiff_t - Replaces boost::iterator_facade and boost::iterator_adaptor with std::iterator, borrowing the 'proxy reference' technique from boost - Removes Boost dependency from CMakeLists - Formats changed files using clang-format --- CMakeLists.txt | 4 +- include/yaml-cpp/emitter.h | 2 +- include/yaml-cpp/node/detail/impl.h | 10 ++-- include/yaml-cpp/node/detail/iterator.h | 60 ++++++++++++++------ include/yaml-cpp/node/detail/node.h | 9 ++- include/yaml-cpp/node/detail/node_data.h | 6 +- include/yaml-cpp/node/detail/node_iterator.h | 45 +++++++++++---- include/yaml-cpp/node/detail/node_ref.h | 5 +- include/yaml-cpp/node/ptr.h | 12 ++-- include/yaml-cpp/parser.h | 4 +- src/emitterstate.cpp | 12 ++-- src/node_data.cpp | 8 +-- src/ptr_stack.h | 31 +++++----- src/ptr_vector.h | 20 ++++--- src/scanner.cpp | 8 +-- src/setting.h | 24 ++++---- src/singledocparser.h | 2 +- test/CMakeLists.txt | 2 +- util/CMakeLists.txt | 3 + 19 files changed, 160 insertions(+), 107 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0edf87f..bc3db31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,8 +113,6 @@ endif() include_directories(${YAML_CPP_SOURCE_DIR}/src) include_directories(${YAML_CPP_SOURCE_DIR}/include) -find_package(Boost REQUIRED) -include_directories(${Boost_INCLUDE_DIRS}) ### @@ -181,7 +179,7 @@ if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR set(GCC_EXTRA_OPTIONS "${GCC_EXTRA_OPTIONS} ${FLAG_TESTED}") endif() # - set(yaml_cxx_flags "-Wall ${GCC_EXTRA_OPTIONS} -pedantic -Wno-long-long ${yaml_cxx_flags}") + set(yaml_cxx_flags "-Wall ${GCC_EXTRA_OPTIONS} -pedantic -Wno-long-long -std=c++11 ${yaml_cxx_flags}") ### Make specific if(${CMAKE_BUILD_TOOL} MATCHES make OR ${CMAKE_BUILD_TOOL} MATCHES gmake) diff --git a/include/yaml-cpp/emitter.h b/include/yaml-cpp/emitter.h index cc49659..5bffc25 100644 --- a/include/yaml-cpp/emitter.h +++ b/include/yaml-cpp/emitter.h @@ -122,7 +122,7 @@ class YAML_CPP_API Emitter : private noncopyable { bool CanEmitNewline() const; private: - std::auto_ptr m_pState; + std::unique_ptr m_pState; ostream_wrapper m_stream; }; diff --git a/include/yaml-cpp/node/detail/impl.h b/include/yaml-cpp/node/detail/impl.h index f6d218c..69f4476 100644 --- a/include/yaml-cpp/node/detail/impl.h +++ b/include/yaml-cpp/node/detail/impl.h @@ -9,7 +9,7 @@ #include "yaml-cpp/node/detail/node.h" #include "yaml-cpp/node/detail/node_data.h" -#include +#include namespace YAML { namespace detail { @@ -22,9 +22,9 @@ struct get_idx { }; template -struct get_idx< - Key, typename boost::enable_if_c::value && - !boost::is_same::value>::type> { +struct get_idx::value && + !std::is_same::value>::type> { static node* get(const std::vector& sequence, const Key& key, shared_memory_holder /* pMemory */) { return key < sequence.size() ? sequence[key] : 0; @@ -41,7 +41,7 @@ struct get_idx< }; template -struct get_idx >::type> { +struct get_idx::value>::type> { static node* get(const std::vector& sequence, const Key& key, shared_memory_holder pMemory) { return key >= 0 ? get_idx::get( diff --git a/include/yaml-cpp/node/detail/iterator.h b/include/yaml-cpp/node/detail/iterator.h index 2c701af..cc5f9d3 100644 --- a/include/yaml-cpp/node/detail/iterator.h +++ b/include/yaml-cpp/node/detail/iterator.h @@ -10,45 +10,68 @@ #include "yaml-cpp/dll.h" #include "yaml-cpp/node/ptr.h" #include "yaml-cpp/node/detail/node_iterator.h" -#include -#include +#include +#include namespace YAML { namespace detail { struct iterator_value; template -class iterator_base - : public boost::iterator_adaptor, node_iterator, V, - std::forward_iterator_tag, V> { +class iterator_base : public std::iterator { + private: template friend class iterator_base; struct enabler {}; - typedef typename iterator_base::base_type base_type; + typedef node_iterator base_type; + + struct proxy { + explicit proxy(const V& x) : m_ref(x) {} + V* operator->() { return std::addressof(m_ref); } + operator V*() { return std::addressof(m_ref); } + + V m_ref; + }; public: typedef typename iterator_base::value_type value_type; public: - iterator_base() {} + iterator_base() : m_iterator(), m_pMemory() {} explicit iterator_base(base_type rhs, shared_memory_holder pMemory) - : iterator_base::iterator_adaptor_(rhs), m_pMemory(pMemory) {} + : m_iterator(rhs), m_pMemory(pMemory) {} template iterator_base(const iterator_base& rhs, - typename boost::enable_if, - enabler>::type = enabler()) - : iterator_base::iterator_adaptor_(rhs.base()), - m_pMemory(rhs.m_pMemory) {} + typename std::enable_if::value, + enabler>::type = enabler()) + : m_iterator(rhs.m_iterator), m_pMemory(rhs.m_pMemory) {} - private: - friend class boost::iterator_core_access; + iterator_base& operator++() { + ++m_iterator; + return *this; + } - void increment() { this->base_reference() = boost::next(this->base()); } + iterator_base operator++(int) { + iterator_base iterator_pre(*this); + ++(*this); + return iterator_pre; + } - value_type dereference() const { - const typename base_type::value_type& v = *this->base(); + template + bool operator==(const iterator_base& rhs) { + return m_iterator == rhs.m_iterator; + } + + template + bool operator!=(const iterator_base& rhs) { + return m_iterator != rhs.m_iterator; + } + + value_type operator*() const { + const typename base_type::value_type& v = *m_iterator; if (v.pNode) return value_type(Node(*v, m_pMemory)); if (v.first && v.second) @@ -56,7 +79,10 @@ class iterator_base return value_type(); } + proxy operator->() const { return proxy(**this); } + private: + base_type m_iterator; shared_memory_holder m_pMemory; }; } diff --git a/include/yaml-cpp/node/detail/node.h b/include/yaml-cpp/node/detail/node.h index bbb497d..3154a52 100644 --- a/include/yaml-cpp/node/detail/node.h +++ b/include/yaml-cpp/node/detail/node.h @@ -13,13 +13,14 @@ #include "yaml-cpp/node/ptr.h" #include "yaml-cpp/node/detail/node_ref.h" #include -#include namespace YAML { namespace detail { -class node : private boost::noncopyable { +class node { public: node() : m_pRef(new node_ref) {} + node(const node&) = delete; + node& operator=(const node&) = delete; bool is(const node& rhs) const { return m_pRef == rhs.m_pRef; } const node_ref* ref() const { return m_pRef.get(); } @@ -65,9 +66,7 @@ class node : private boost::noncopyable { m_pRef->set_data(*rhs.m_pRef); } - void set_mark(const Mark& mark) { - m_pRef->set_mark(mark); - } + void set_mark(const Mark& mark) { m_pRef->set_mark(mark); } void set_type(NodeType::value type) { if (type != NodeType::Undefined) diff --git a/include/yaml-cpp/node/detail/node_data.h b/include/yaml-cpp/node/detail/node_data.h index 6030867..64bbc05 100644 --- a/include/yaml-cpp/node/detail/node_data.h +++ b/include/yaml-cpp/node/detail/node_data.h @@ -7,8 +7,6 @@ #pragma once #endif -#include -#include #include #include #include @@ -29,9 +27,11 @@ class node; namespace YAML { namespace detail { -class YAML_CPP_API node_data : private boost::noncopyable { +class YAML_CPP_API node_data { public: node_data(); + node_data(const node_data&) = delete; + node_data& operator=(const node_data&) = delete; void mark_defined(); void set_mark(const Mark& mark); diff --git a/include/yaml-cpp/node/detail/node_iterator.h b/include/yaml-cpp/node/detail/node_iterator.h index 9669c81..a33049a 100644 --- a/include/yaml-cpp/node/detail/node_iterator.h +++ b/include/yaml-cpp/node/detail/node_iterator.h @@ -9,8 +9,9 @@ #include "yaml-cpp/dll.h" #include "yaml-cpp/node/ptr.h" -#include -#include +#include +#include +#include #include #include #include @@ -52,12 +53,20 @@ struct node_iterator_type { template class node_iterator_base - : public boost::iterator_facade< - node_iterator_base, node_iterator_value, - std::forward_iterator_tag, node_iterator_value > { + : public std::iterator, + std::ptrdiff_t, node_iterator_value*, + node_iterator_value > { private: struct enabler {}; + struct proxy { + explicit proxy(const node_iterator_value& x) : m_ref(x) {} + node_iterator_value* operator->() { return std::addressof(m_ref); } + operator node_iterator_value*() { return std::addressof(m_ref); } + + node_iterator_value m_ref; + }; + public: typedef typename node_iterator_type::seq SeqIter; typedef typename node_iterator_type::map MapIter; @@ -80,20 +89,18 @@ class node_iterator_base template node_iterator_base(const node_iterator_base& rhs, - typename boost::enable_if, - enabler>::type = enabler()) + typename std::enable_if::value, + enabler>::type = enabler()) : m_type(rhs.m_type), m_seqIt(rhs.m_seqIt), m_mapIt(rhs.m_mapIt), m_mapEnd(rhs.m_mapEnd) {} - private: - friend class boost::iterator_core_access; template friend class node_iterator_base; template - bool equal(const node_iterator_base& rhs) const { + bool operator==(const node_iterator_base& rhs) const { if (m_type != rhs.m_type) return false; @@ -108,7 +115,12 @@ class node_iterator_base return true; } - void increment() { + template + bool operator!=(const node_iterator_base& rhs) const { + return !(*this == rhs); + } + + node_iterator_base& operator++() { switch (m_type) { case iterator_type::None: break; @@ -120,9 +132,16 @@ class node_iterator_base m_mapIt = increment_until_defined(m_mapIt); break; } + return *this; } - value_type dereference() const { + node_iterator_base operator++(int) { + node_iterator_base iterator_pre(*this); + ++(*this); + return iterator_pre; + } + + value_type operator*() const { switch (m_type) { case iterator_type::None: return value_type(); @@ -134,6 +153,8 @@ class node_iterator_base return value_type(); } + proxy operator->() const { return proxy(**this); } + MapIter increment_until_defined(MapIter it) { while (it != m_mapEnd && !is_defined(it)) ++it; diff --git a/include/yaml-cpp/node/detail/node_ref.h b/include/yaml-cpp/node/detail/node_ref.h index 26b1872..d8a94f8 100644 --- a/include/yaml-cpp/node/detail/node_ref.h +++ b/include/yaml-cpp/node/detail/node_ref.h @@ -11,13 +11,14 @@ #include "yaml-cpp/node/type.h" #include "yaml-cpp/node/ptr.h" #include "yaml-cpp/node/detail/node_data.h" -#include namespace YAML { namespace detail { -class node_ref : private boost::noncopyable { +class node_ref { public: node_ref() : m_pData(new node_data) {} + node_ref(const node_ref&) = delete; + node_ref& operator=(const node_ref&) = delete; bool is_defined() const { return m_pData->is_defined(); } const Mark& mark() const { return m_pData->mark(); } diff --git a/include/yaml-cpp/node/ptr.h b/include/yaml-cpp/node/ptr.h index 64c8689..ce085dd 100644 --- a/include/yaml-cpp/node/ptr.h +++ b/include/yaml-cpp/node/ptr.h @@ -8,7 +8,7 @@ #endif #include "yaml-cpp/dll.h" -#include +#include namespace YAML { namespace detail { @@ -18,11 +18,11 @@ class node_data; class memory; class memory_holder; -typedef boost::shared_ptr shared_node; -typedef boost::shared_ptr shared_node_ref; -typedef boost::shared_ptr shared_node_data; -typedef boost::shared_ptr shared_memory_holder; -typedef boost::shared_ptr shared_memory; +typedef std::shared_ptr shared_node; +typedef std::shared_ptr shared_node_ref; +typedef std::shared_ptr shared_node_data; +typedef std::shared_ptr shared_memory_holder; +typedef std::shared_ptr shared_memory; } } diff --git a/include/yaml-cpp/parser.h b/include/yaml-cpp/parser.h index 24880e4..edbfd8f 100644 --- a/include/yaml-cpp/parser.h +++ b/include/yaml-cpp/parser.h @@ -40,8 +40,8 @@ class YAML_CPP_API Parser : private noncopyable { void HandleTagDirective(const Token& token); private: - std::auto_ptr m_pScanner; - std::auto_ptr m_pDirectives; + std::unique_ptr m_pScanner; + std::unique_ptr m_pDirectives; }; } diff --git a/src/emitterstate.cpp b/src/emitterstate.cpp index a0874ac..aa3e784 100644 --- a/src/emitterstate.cpp +++ b/src/emitterstate.cpp @@ -124,10 +124,14 @@ void EmitterState::StartedGroup(GroupType::value type) { const int lastGroupIndent = (m_groups.empty() ? 0 : m_groups.top().indent); m_curIndent += lastGroupIndent; - std::auto_ptr pGroup(new Group(type)); + // TODO: Create move constructors for settings types to simplify transfer + std::unique_ptr pGroup(new Group(type)); // transfer settings (which last until this group is done) - pGroup->modifiedSettings = m_modifiedSettings; + // + // NB: if pGroup->modifiedSettings == m_modifiedSettings, + // m_modifiedSettings is not changed! + pGroup->modifiedSettings = std::move(m_modifiedSettings); // set up group if (GetFlowType(type) == Block) @@ -136,7 +140,7 @@ void EmitterState::StartedGroup(GroupType::value type) { pGroup->flowType = FlowType::Flow; pGroup->indent = GetIndent(); - m_groups.push(pGroup); + m_groups.push(std::move(pGroup)); } void EmitterState::EndedGroup(GroupType::value type) { @@ -149,7 +153,7 @@ void EmitterState::EndedGroup(GroupType::value type) { // get rid of the current group { - std::auto_ptr pFinishedGroup = m_groups.pop(); + std::unique_ptr pFinishedGroup = m_groups.pop(); if (pFinishedGroup->type != type) return SetError(ErrorMsg::UNMATCHED_GROUP_TAG); } diff --git a/src/node_data.cpp b/src/node_data.cpp index a1ca900..a862946 100644 --- a/src/node_data.cpp +++ b/src/node_data.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include "yaml-cpp/exceptions.h" @@ -28,9 +28,7 @@ void node_data::mark_defined() { m_isDefined = true; } -void node_data::set_mark(const Mark& mark) { - m_mark = mark; -} +void node_data::set_mark(const Mark& mark) { m_mark = mark; } void node_data::set_type(NodeType::value type) { if (type == NodeType::Undefined) { @@ -104,7 +102,7 @@ void node_data::compute_seq_size() const { void node_data::compute_map_size() const { kv_pairs::iterator it = m_undefinedPairs.begin(); while (it != m_undefinedPairs.end()) { - kv_pairs::iterator jt = boost::next(it); + kv_pairs::iterator jt = std::next(it); if (it->first->is_defined() && it->second->is_defined()) m_undefinedPairs.erase(it); it = jt; diff --git a/src/ptr_stack.h b/src/ptr_stack.h index f378ffc..b71eb99 100644 --- a/src/ptr_stack.h +++ b/src/ptr_stack.h @@ -14,40 +14,45 @@ #include "yaml-cpp/noncopyable.h" +// TODO: This class is no longer needed template class ptr_stack : private YAML::noncopyable { public: ptr_stack() {} - ~ptr_stack() { clear(); } void clear() { - for (std::size_t i = 0; i < m_data.size(); i++) - delete m_data[i]; m_data.clear(); } std::size_t size() const { return m_data.size(); } bool empty() const { return m_data.empty(); } - void push(std::auto_ptr t) { - m_data.push_back(NULL); - m_data.back() = t.release(); + void push(std::unique_ptr&& t) { + m_data.push_back(std::move(t)); } - std::auto_ptr pop() { - std::auto_ptr t(m_data.back()); + std::unique_ptr pop() { + std::unique_ptr t(std::move(m_data.back())); m_data.pop_back(); return t; } - T& top() { return *m_data.back(); } - const T& top() const { return *m_data.back(); } - T& top(std::ptrdiff_t diff) { return **(m_data.end() - 1 + diff); } + T& top() { + return *(m_data.back().get()); + } + const T& top() const { + return *(m_data.back().get()); + } + + T& top(std::ptrdiff_t diff) { + return *((m_data.end() - 1 + diff)->get()); + } + const T& top(std::ptrdiff_t diff) const { - return **(m_data.end() - 1 + diff); + return *((m_data.end() - 1 + diff)->get()); } private: - std::vector m_data; + std::vector> m_data; }; #endif // PTR_STACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/ptr_vector.h b/src/ptr_vector.h index a546a89..3ebed28 100644 --- a/src/ptr_vector.h +++ b/src/ptr_vector.h @@ -16,33 +16,35 @@ namespace YAML { +// TODO: This class is no longer needed template class ptr_vector : private YAML::noncopyable { public: ptr_vector() {} - ~ptr_vector() { clear(); } void clear() { - for (std::size_t i = 0; i < m_data.size(); i++) - delete m_data[i]; m_data.clear(); } std::size_t size() const { return m_data.size(); } bool empty() const { return m_data.empty(); } - void push_back(std::auto_ptr t) { - m_data.push_back(NULL); - m_data.back() = t.release(); + void push_back(std::unique_ptr&& t) { + m_data.push_back(std::move(t)); } T& operator[](std::size_t i) { return *m_data[i]; } const T& operator[](std::size_t i) const { return *m_data[i]; } - T& back() { return *m_data.back(); } - const T& back() const { return *m_data.back(); } + T& back() { + return *(m_data.back().get()); + } + + const T& back() const { + return *(m_data.back().get()); + } private: - std::vector m_data; + std::vector> m_data; }; } diff --git a/src/scanner.cpp b/src/scanner.cpp index 680c73b..767b8e1 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -233,8 +233,8 @@ const RegEx& Scanner::GetValueRegex() const { void Scanner::StartStream() { m_startedStream = true; m_simpleKeyAllowed = true; - std::auto_ptr pIndent(new IndentMarker(-1, IndentMarker::NONE)); - m_indentRefs.push_back(pIndent); + std::unique_ptr pIndent(new IndentMarker(-1, IndentMarker::NONE)); + m_indentRefs.push_back(std::move(pIndent)); m_indents.push(&m_indentRefs.back()); } @@ -281,7 +281,7 @@ Scanner::IndentMarker* Scanner::PushIndentTo(int column, if (InFlowContext()) return 0; - std::auto_ptr pIndent(new IndentMarker(column, type)); + std::unique_ptr pIndent(new IndentMarker(column, type)); IndentMarker& indent = *pIndent; const IndentMarker& lastIndent = *m_indents.top(); @@ -298,7 +298,7 @@ Scanner::IndentMarker* Scanner::PushIndentTo(int column, // and then the indent m_indents.push(&indent); - m_indentRefs.push_back(pIndent); + m_indentRefs.push_back(std::move(pIndent)); return &m_indentRefs.back(); } diff --git a/src/setting.h b/src/setting.h index 3ff8c20..b78d40e 100644 --- a/src/setting.h +++ b/src/setting.h @@ -20,7 +20,7 @@ class Setting { Setting() : m_value() {} const T get() const { return m_value; } - std::auto_ptr set(const T& value); + std::unique_ptr set(const T& value); void restore(const Setting& oldSetting) { m_value = oldSetting.get(); } private: @@ -49,8 +49,8 @@ class SettingChange : public SettingChangeBase { }; template -inline std::auto_ptr Setting::set(const T& value) { - std::auto_ptr pChange(new SettingChange(this)); +inline std::unique_ptr Setting::set(const T& value) { + std::unique_ptr pChange(new SettingChange(this)); m_value = value; return pChange; } @@ -62,10 +62,6 @@ class SettingChanges : private noncopyable { void clear() { restore(); - - for (setting_changes::const_iterator it = m_settingChanges.begin(); - it != m_settingChanges.end(); ++it) - delete *it; m_settingChanges.clear(); } @@ -75,23 +71,23 @@ class SettingChanges : private noncopyable { (*it)->pop(); } - void push(std::auto_ptr pSettingChange) { - m_settingChanges.push_back(pSettingChange.release()); + void push(std::unique_ptr pSettingChange) { + m_settingChanges.push_back(std::move(pSettingChange)); } - // like std::auto_ptr - assignment is transfer of ownership - SettingChanges& operator=(SettingChanges& rhs) { + // like std::unique_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(); + std::swap(m_settingChanges, rhs.m_settingChanges); + return *this; } private: - typedef std::vector setting_changes; + typedef std::vector> setting_changes; setting_changes m_settingChanges; }; } diff --git a/src/singledocparser.h b/src/singledocparser.h index ed0aad5..2b92067 100644 --- a/src/singledocparser.h +++ b/src/singledocparser.h @@ -53,7 +53,7 @@ class SingleDocParser : private noncopyable { private: Scanner& m_scanner; const Directives& m_directives; - std::auto_ptr m_pCollectionStack; + std::unique_ptr m_pCollectionStack; typedef std::map Anchors; Anchors m_anchors; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 61f1f7f..cf06c9a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,7 +10,7 @@ endif() if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - set(yaml_test_flags "-Wno-c99-extensions -Wno-variadic-macros -Wno-sign-compare") + set(yaml_test_flags "-Wno-c99-extensions -Wno-variadic-macros -Wno-sign-compare -std=c++11") endif() file(GLOB test_headers [a-z_]*.h) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 8a69631..2286627 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -1,11 +1,14 @@ add_sources(parse.cpp) add_executable(parse parse.cpp) target_link_libraries(parse yaml-cpp) +set_target_properties(parse PROPERTIES COMPILE_FLAGS "-std=c++11") add_sources(sandbox.cpp) add_executable(sandbox sandbox.cpp) target_link_libraries(sandbox yaml-cpp) +set_target_properties(sandbox PROPERTIES COMPILE_FLAGS "-std=c++11") add_sources(read.cpp) add_executable(read read.cpp) target_link_libraries(read yaml-cpp) +set_target_properties(read PROPERTIES COMPILE_FLAGS "-std=c++11") From e92321aee52fd27566601f9cca53ba90b41e15c1 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Tue, 12 Jan 2016 08:51:30 -0600 Subject: [PATCH 05/37] Update current version. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54263ef..5ca95fd 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ cmake [-G generator] [-DBUILD_SHARED_LIBS=ON|OFF] .. # Recent Release # -[yaml-cpp 0.5.2](https://github.com/jbeder/yaml-cpp/releases/tag/release-0.5.2) has been released! This is a bug fix release. +[yaml-cpp 0.5.3](https://github.com/jbeder/yaml-cpp/releases/tag/release-0.5.3) has been released! This is a bug fix release. It also will be the last release that uses Boost; futures releases will require C++11 instead. [yaml-cpp 0.3.0](https://github.com/jbeder/yaml-cpp/releases/tag/release-0.3.0) is still available if you want the old API. From dfbb388409fcfb9f6d2ef07cb0ef5bacadd08ca7 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Sat, 30 Jan 2016 17:36:47 -0600 Subject: [PATCH 06/37] Remove unnecessary forward declaration. --- include/yaml-cpp/node/detail/iterator_fwd.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/yaml-cpp/node/detail/iterator_fwd.h b/include/yaml-cpp/node/detail/iterator_fwd.h index c54a258..5f1ffe7 100644 --- a/include/yaml-cpp/node/detail/iterator_fwd.h +++ b/include/yaml-cpp/node/detail/iterator_fwd.h @@ -13,7 +13,6 @@ #include namespace YAML { -class node; namespace detail { struct iterator_value; From a5b72f7ae69e5a28434fca02faba6f48ca0d09a1 Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Fri, 8 Jan 2016 10:57:53 -0800 Subject: [PATCH 07/37] read benchmark: accept a filename as an argument On my Macbook Pro, reading from standard input incurs a bunch of locking overhead, which complicates profiling and (IMO) adds noise to results. This adds the option to read from a file, which doesn't incur this overhead. --- util/read.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/util/read.cpp b/util/read.cpp index fabee1a..4734e88 100644 --- a/util/read.cpp +++ b/util/read.cpp @@ -1,3 +1,4 @@ +#include #include #include "yaml-cpp/emitterstyle.h" @@ -25,9 +26,19 @@ class NullEventHandler : public YAML::EventHandler { virtual void OnMapEnd() {} }; -int main() { - YAML::Parser parser(std::cin); +void run(YAML::Parser& parser) { NullEventHandler handler; parser.HandleNextDocument(handler); +} + +int main(int argc, char** argv) { + if (argc > 1) { + std::ifstream in(argv[1]); + YAML::Parser parser(in); + run(parser); + } else { + YAML::Parser parser(std::cin); + run(parser); + } return 0; } From 9e37409b4b40e4ca5b026ea9de7ea9784da80ff2 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Sat, 30 Jan 2016 18:28:27 -0600 Subject: [PATCH 08/37] Add features to read binary: Flag -n N repeats parsing N times. Flag -c (--cache) caches the input in a string and uses that to parse. --- util/read.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/util/read.cpp b/util/read.cpp index 4734e88..b1f787e 100644 --- a/util/read.cpp +++ b/util/read.cpp @@ -26,19 +26,77 @@ class NullEventHandler : public YAML::EventHandler { virtual void OnMapEnd() {} }; -void run(YAML::Parser& parser) { +void run(std::istream& in) { + YAML::Parser parser(in); NullEventHandler handler; parser.HandleNextDocument(handler); } +void usage() { std::cerr << "Usage: read [-n N] [-c, --cache] [filename]\n"; } + +std::string read_stream(std::istream& in) { + return std::string((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); +} + int main(int argc, char** argv) { - if (argc > 1) { - std::ifstream in(argv[1]); - YAML::Parser parser(in); - run(parser); + int N = 1; + bool cache = false; + std::string filename; + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + if (arg == "-n") { + i++; + if (i >= argc) { + usage(); + return -1; + } + N = std::atoi(argv[i]); + if (N <= 0) { + usage(); + return -1; + } + } else if (arg == "-c" || arg == "--cache") { + cache = true; + } else { + filename = argv[i]; + if (i + 1 != argc) { + usage(); + return -1; + } + } + } + + if (N > 1 && !cache && filename == "") { + usage(); + return -1; + } + + if (cache) { + std::string input; + if (filename != "") { + std::ifstream in(filename); + input = read_stream(in); + } else { + input = read_stream(std::cin); + } + std::istringstream in(input); + for (int i = 0; i < N; i++) { + in.seekg(std::ios_base::beg); + run(in); + } } else { - YAML::Parser parser(std::cin); - run(parser); + if (filename != "") { + std::ifstream in(filename); + for (int i = 0; i < N; i++) { + in.seekg(std::ios_base::beg); + run(in); + } + } else { + for (int i = 0; i < N; i++) { + run(std::cin); + } + } } return 0; } From 8c35a8ffabea1c989d9f449bff053081fdc987e1 Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Thu, 7 Jan 2016 15:02:38 -0800 Subject: [PATCH 09/37] Cache scalar regexes This improves performance on the test.yaml attached to #158 by about 35% on my machine (0.39s -> 0.25s), as measured by `time build/util/parse < test.yaml > /dev/null`. --- src/exp.h | 9 +++++++++ src/scantoken.cpp | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/exp.h b/src/exp.h index f248802..ba82874 100644 --- a/src/exp.h +++ b/src/exp.h @@ -165,6 +165,15 @@ inline const RegEx& EndScalarInFlow() { return e; } +inline const RegEx& ScanScalarEndInFlow() { + static const RegEx e = (EndScalarInFlow() || (BlankOrBreak() + Comment())); + return e; +} + +inline const RegEx& ScanScalarEnd() { + static const RegEx e = EndScalar() || (BlankOrBreak() + Comment()); + return e; +} inline const RegEx& EscSingleQuote() { static const RegEx e = RegEx("\'\'"); return e; diff --git a/src/scantoken.cpp b/src/scantoken.cpp index 180ad00..79e7682 100644 --- a/src/scantoken.cpp +++ b/src/scantoken.cpp @@ -297,8 +297,8 @@ void Scanner::ScanPlainScalar() { // set up the scanning parameters ScanScalarParams params; - params.end = (InFlowContext() ? Exp::EndScalarInFlow() : Exp::EndScalar()) || - (Exp::BlankOrBreak() + Exp::Comment()); + params.end = + (InFlowContext() ? Exp::ScanScalarEndInFlow() : Exp::ScanScalarEnd()); params.eatEnd = false; params.indent = (InFlowContext() ? 0 : GetTopIndent() + 1); params.fold = FOLD_FLOW; From 005a6a19ee0af3e284637abe9ecea5501993fc4c Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Thu, 7 Jan 2016 15:16:52 -0800 Subject: [PATCH 10/37] Avoid copying cached RegExes for scalars This improves performance on the test.yaml attached to #158 by about 25% on my machine as compared to the previous commit (0.25s -> 0.20s), as measured by `time build/util/parse < test.yaml > /dev/null`. --- src/exp.h | 4 ++++ src/scanscalar.cpp | 8 ++++++-- src/scanscalar.h | 6 ++++-- src/scantoken.cpp | 5 +++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/exp.h b/src/exp.h index ba82874..50b0220 100644 --- a/src/exp.h +++ b/src/exp.h @@ -20,6 +20,10 @@ namespace YAML { namespace Exp { // misc +inline const RegEx& Empty() { + static const RegEx e; + return e; +} inline const RegEx& Space() { static const RegEx e = RegEx(' '); return e; diff --git a/src/scanscalar.cpp b/src/scanscalar.cpp index 8253b8d..62ac875 100644 --- a/src/scanscalar.cpp +++ b/src/scanscalar.cpp @@ -28,13 +28,17 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { std::string scalar; params.leadingSpaces = false; + if (!params.end) { + params.end = &Exp::Empty(); + } + 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)) { + while (!params.end->Matches(INPUT) && !Exp::Break().Matches(INPUT)) { if (!INPUT) break; @@ -87,7 +91,7 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { break; // are we done via character match? - int n = params.end.Match(INPUT); + int n = params.end->Match(INPUT); if (n >= 0) { if (params.eatEnd) INPUT.eat(n); diff --git a/src/scanscalar.h b/src/scanscalar.h index 62da13c..c3a574a 100644 --- a/src/scanscalar.h +++ b/src/scanscalar.h @@ -19,7 +19,8 @@ enum FOLD { DONT_FOLD, FOLD_BLOCK, FOLD_FLOW }; struct ScanScalarParams { ScanScalarParams() - : eatEnd(false), + : end(nullptr), + eatEnd(false), indent(0), detectIndent(false), eatLeadingWhitespace(0), @@ -32,7 +33,8 @@ struct ScanScalarParams { leadingSpaces(false) {} // input: - RegEx end; // what condition ends this scalar? + const RegEx* end; // what condition ends this scalar? + // unowned. 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? diff --git a/src/scantoken.cpp b/src/scantoken.cpp index 79e7682..fd8758d 100644 --- a/src/scantoken.cpp +++ b/src/scantoken.cpp @@ -298,7 +298,7 @@ void Scanner::ScanPlainScalar() { // set up the scanning parameters ScanScalarParams params; params.end = - (InFlowContext() ? Exp::ScanScalarEndInFlow() : Exp::ScanScalarEnd()); + (InFlowContext() ? &Exp::ScanScalarEndInFlow() : &Exp::ScanScalarEnd()); params.eatEnd = false; params.indent = (InFlowContext() ? 0 : GetTopIndent() + 1); params.fold = FOLD_FLOW; @@ -338,7 +338,8 @@ void Scanner::ScanQuotedScalar() { // setup the scanning parameters ScanScalarParams params; - params.end = (single ? RegEx(quote) && !Exp::EscSingleQuote() : RegEx(quote)); + RegEx end = (single ? RegEx(quote) && !Exp::EscSingleQuote() : RegEx(quote)); + params.end = &end; params.eatEnd = true; params.escape = (single ? '\'' : '\\'); params.indent = 0; From 57805dfd6a741e55477ea8d4d5b3b6f0272d1f0e Mon Sep 17 00:00:00 2001 From: Rodrigo Hernandez Date: Tue, 26 Jan 2016 11:45:00 -0600 Subject: [PATCH 11/37] Removed quoted variables to avoid CMP0054 policy warnings on CMake 3.3.0. --- CMakeLists.txt | 4 ++-- test/CMakeLists.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc3db31..5b326a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,8 +148,8 @@ if(WIN32) endif() # GCC or Clang specialities -if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR - "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR + CMAKE_CXX_COMPILER_ID MATCHES "Clang") ### General stuff if(WIN32) set(CMAKE_SHARED_LIBRARY_PREFIX "") # DLLs do not have a "lib" prefix diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cf06c9a..74c2594 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,8 +8,8 @@ if(WIN32 AND BUILD_SHARED_LIBS) add_definitions("-DGTEST_LINKED_AS_SHARED_LIBRARY") endif() -if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU" OR - "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR + CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(yaml_test_flags "-Wno-c99-extensions -Wno-variadic-macros -Wno-sign-compare -std=c++11") endif() From f327b565ba89c4ecea07e0d984bc7210465d8768 Mon Sep 17 00:00:00 2001 From: "Craig M. Brandenburg" Date: Wed, 16 Mar 2016 10:41:12 -0700 Subject: [PATCH 12/37] Fix line-wrapping in comment in dll.h --- include/yaml-cpp/dll.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/include/yaml-cpp/dll.h b/include/yaml-cpp/dll.h index 827e0f1..a32c06b 100644 --- a/include/yaml-cpp/dll.h +++ b/include/yaml-cpp/dll.h @@ -8,16 +8,12 @@ #endif // The following ifdef block is the standard way of creating macros which make -// exporting -// from a DLL simpler. All files within this DLL are compiled with the -// yaml_cpp_EXPORTS -// symbol defined on the command line. this symbol should not be defined on any -// project -// that uses this DLL. This way any other project whose source files include -// this file see -// YAML_CPP_API functions as being imported from a DLL, whereas this DLL sees -// symbols -// defined with this macro as being exported. +// exporting from a DLL simpler. All files within this DLL are compiled with the +// yaml_cpp_EXPORTS symbol defined on the command line. This symbol should not +// be defined on any project that uses this DLL. This way any other project +// whose source files include this file see YAML_CPP_API functions as being +// imported from a DLL, whereas this DLL sees symbols defined with this macro as +// being exported. #undef YAML_CPP_API #ifdef YAML_CPP_DLL // Using or Building YAML-CPP DLL (definition defined From 0a1352525aa4decefa6cc5e8ae9f0962175f5753 Mon Sep 17 00:00:00 2001 From: Scott Wolchok Date: Thu, 7 Jan 2016 19:39:09 -0800 Subject: [PATCH 13/37] add some tests for RegEx --- test/regex_test.cpp | 177 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 test/regex_test.cpp diff --git a/test/regex_test.cpp b/test/regex_test.cpp new file mode 100644 index 0000000..7589d2e --- /dev/null +++ b/test/regex_test.cpp @@ -0,0 +1,177 @@ +#include "gtest/gtest.h" +#include "regex_yaml.h" +#include "stream.h" + +using YAML::RegEx; +using YAML::Stream; + +namespace { +const auto MIN_CHAR = Stream::eof() + 1; + +TEST(RegExTest, Empty) { + RegEx empty; + EXPECT_TRUE(empty.Matches(std::string())); + EXPECT_EQ(0, empty.Match(std::string())); + for (int i = MIN_CHAR; i < 128; ++i) { + auto str = std::string(1, char(i)); + EXPECT_FALSE(empty.Matches(str)); + EXPECT_EQ(-1, empty.Match(str)); + } +} + +TEST(RegExTest, Range) { + for (int i = MIN_CHAR; i < 128; ++i) { + for (int j = MIN_CHAR; j < 128; ++j) { + RegEx ex((char)i, (char)j); + for (int k = MIN_CHAR; k < 128; ++k) { + auto str = std::string(1, char(k)); + if (i <= k && k <= j) { + EXPECT_TRUE(ex.Matches(str)); + EXPECT_EQ(1, ex.Match(str)); + } else { + EXPECT_FALSE(ex.Matches(str)); + EXPECT_EQ(-1, ex.Match(str)); + } + } + } + } +} + +TEST(RegExTest, EmptyString) { + RegEx ex = RegEx(std::string()); + EXPECT_TRUE(ex.Matches(std::string())); + EXPECT_EQ(0, ex.Match(std::string())); + + // Matches anything, unlike RegEx()! + EXPECT_TRUE(ex.Matches(std::string("hello"))); + EXPECT_EQ(0, ex.Match(std::string("hello"))); +} + +TEST(RegExTest, SingleCharacterString) { + for (int i = MIN_CHAR; i < 128; ++i) { + RegEx ex(std::string(1, (char)i)); + for (int j = MIN_CHAR; j < 128; ++j) { + auto str = std::string(1, char(j)); + if (j == i) { + EXPECT_TRUE(ex.Matches(str)); + EXPECT_EQ(1, ex.Match(str)); + // Match at start of string only! + std::string prefixed = + std::string(1, i + 1) + std::string("prefix: ") + str; + EXPECT_FALSE(ex.Matches(prefixed)); + EXPECT_EQ(-1, ex.Match(prefixed)); + } else { + EXPECT_FALSE(ex.Matches(str)); + EXPECT_EQ(-1, ex.Match(str)); + } + } + } +} + +TEST(RegExTest, MultiCharacterString) { + RegEx ex(std::string("ab")); + + EXPECT_FALSE(ex.Matches(std::string("a"))); + EXPECT_EQ(-1, ex.Match(std::string("a"))); + + EXPECT_TRUE(ex.Matches(std::string("ab"))); + EXPECT_EQ(2, ex.Match(std::string("ab"))); + EXPECT_TRUE(ex.Matches(std::string("abba"))); + EXPECT_EQ(2, ex.Match(std::string("abba"))); + + // match at start of string only! + EXPECT_FALSE(ex.Matches(std::string("baab"))); + EXPECT_EQ(-1, ex.Match(std::string("baab"))); +} + +TEST(RegExTest, OperatorNot) { + RegEx ex = !RegEx(std::string("ab")); + + EXPECT_TRUE(ex.Matches(std::string("a"))); + EXPECT_EQ(1, ex.Match(std::string("a"))); + + EXPECT_FALSE(ex.Matches(std::string("ab"))); + EXPECT_EQ(-1, ex.Match(std::string("ab"))); + EXPECT_FALSE(ex.Matches(std::string("abba"))); + EXPECT_EQ(-1, ex.Match(std::string("abba"))); + + // match at start of string only! + EXPECT_TRUE(ex.Matches(std::string("baab"))); + // Operator not causes only one character to be matched. + EXPECT_EQ(1, ex.Match(std::string("baab"))); +} + +TEST(RegExTest, OperatorOr) { + for (int i = MIN_CHAR; i < 127; ++i) { + for (int j = i + 1; j < 128; ++j) { + auto iStr = std::string(1, char(i)); + auto jStr = std::string(1, char(j)); + RegEx ex1 = RegEx(iStr) || RegEx(jStr); + RegEx ex2 = RegEx(jStr) || RegEx(iStr); + + for (int k = MIN_CHAR; k < 128; ++k) { + auto str = std::string(1, char(k)); + if (i == k || j == k) { + EXPECT_TRUE(ex1.Matches(str)); + EXPECT_TRUE(ex2.Matches(str)); + EXPECT_EQ(1, ex1.Match(str)); + EXPECT_EQ(1, ex2.Match(str)); + } else { + EXPECT_FALSE(ex1.Matches(str)); + EXPECT_FALSE(ex2.Matches(str)); + EXPECT_EQ(-1, ex1.Match(str)); + EXPECT_EQ(-1, ex2.Match(str)); + } + } + } + } +} + +TEST(RegExTest, OperatorOrShortCircuits) { + RegEx ex1 = RegEx(std::string("aaaa")) || RegEx(std::string("aa")); + RegEx ex2 = RegEx(std::string("aa")) || RegEx(std::string("aaaa")); + + EXPECT_TRUE(ex1.Matches(std::string("aaaaa"))); + EXPECT_EQ(4, ex1.Match(std::string("aaaaa"))); + + EXPECT_TRUE(ex2.Matches(std::string("aaaaa"))); + EXPECT_EQ(2, ex2.Match(std::string("aaaaa"))); +} + +TEST(RegExTest, OperatorAnd) { + RegEx emptySet = RegEx('a') && RegEx(); + EXPECT_FALSE(emptySet.Matches(std::string("a"))); +} + +TEST(RegExTest, OperatorAndShortCircuits) { + RegEx ex1 = RegEx(std::string("aaaa")) && RegEx(std::string("aa")); + RegEx ex2 = RegEx(std::string("aa")) && RegEx(std::string("aaaa")); + + EXPECT_TRUE(ex1.Matches(std::string("aaaaa"))); + EXPECT_EQ(4, ex1.Match(std::string("aaaaa"))); + + EXPECT_TRUE(ex2.Matches(std::string("aaaaa"))); + EXPECT_EQ(2, ex2.Match(std::string("aaaaa"))); +} + +TEST(RegExTest, OperatorPlus) { + RegEx ex = RegEx(std::string("hello ")) + RegEx(std::string("there")); + + EXPECT_TRUE(ex.Matches(std::string("hello there"))); + EXPECT_FALSE(ex.Matches(std::string("hello "))); + EXPECT_FALSE(ex.Matches(std::string("there"))); + EXPECT_EQ(11, ex.Match(std::string("hello there"))); +} + +TEST(RegExTest, StringOr) { + std::string str = "abcde"; + RegEx ex = RegEx(str, YAML::REGEX_OR); + + for (size_t i = 0; i < str.size(); ++i) { + EXPECT_TRUE(ex.Matches(str.substr(i, 1))); + EXPECT_EQ(1, ex.Match(str.substr(i, 1))); + } + + EXPECT_EQ(1, ex.Match(str)); +} +} From 178c8d1f276afeec006e2c4e1dfa979c1f9d2a3c Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Sat, 26 Mar 2016 16:01:00 -0400 Subject: [PATCH 14/37] Add test for parser exceptions for incomplete JSON. --- test/integration/load_node_test.cpp | 30 ++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp index 80236af..6136307 100644 --- a/test/integration/load_node_test.cpp +++ b/test/integration/load_node_test.cpp @@ -198,5 +198,33 @@ TEST(NodeTest, ParseNodeStyle) { EXPECT_EQ(EmitterStyle::Block, Load("- foo\n- bar").Style()); EXPECT_EQ(EmitterStyle::Block, Load("foo: bar").Style()); } + +struct ParserExceptionTestCase { + std::string name; + std::string input; + std::string expected_exception; +}; + +TEST(NodeTest, IncompleteJson) { + std::vector tests = { + {"JSON map without value", "{\"access\"", ErrorMsg::END_OF_MAP_FLOW}, + {"JSON map with colon but no value", "{\"access\":", + ErrorMsg::END_OF_MAP_FLOW}, + {"JSON map with unclosed value quote", "{\"access\":\"", + ErrorMsg::END_OF_MAP_FLOW}, + {"JSON map without end brace", "{\"access\":\"abc\"", + ErrorMsg::END_OF_MAP_FLOW}, + }; + for (const ParserExceptionTestCase test : tests) { + try { + Load(test.input); + FAIL() << "Expected exception " << test.expected_exception << " for " + << test.name << ", input: " << test.input; + } catch (const ParserException& e) { + EXPECT_EQ(test.expected_exception, e.msg); + } + } } -} + +} // namespace +} // namespace YAML From 500db60f899ae6845039d4eca503133d0db81dbd Mon Sep 17 00:00:00 2001 From: Paul Novotny Date: Wed, 25 Nov 2015 11:33:36 -0500 Subject: [PATCH 15/37] Include cmake files in install This adds yaml-cpp-config.cmake, yaml-cpp-config-version.cmake, and yaml-cpp-targets.cmake to the cmake install. As a result, cmake's find_package can easily find yaml-cpp for software that depends on yaml-cpp. Add code to install cmake files to $CMAKE_INSTALL_PREFIX/CMake on Windows, which is the de-facto standard. Closes jbeder/yaml-cpp#336 jbeder/yaml-cpp#127 --- CMakeLists.txt | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b326a3..cbaad07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,15 +256,15 @@ else() set(_library_dir lib) endif() -set(INCLUDE_INSTALL_ROOT_DIR include) +set(INCLUDE_INSTALL_ROOT_DIR ${CMAKE_INSTALL_PREFIX}/include) set(INCLUDE_INSTALL_DIR ${INCLUDE_INSTALL_ROOT_DIR}/yaml-cpp) -set(LIB_INSTALL_DIR "${_library_dir}${LIB_SUFFIX}") +set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${_library_dir}${LIB_SUFFIX}") set(_INSTALL_DESTINATIONS - RUNTIME DESTINATION bin + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION "lib${LIB_SUFFIX}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" ) @@ -300,7 +300,7 @@ if(MSVC) endif() endif() -install(TARGETS yaml-cpp ${_INSTALL_DESTINATIONS}) +install(TARGETS yaml-cpp EXPORT yaml-cpp-targets ${_INSTALL_DESTINATIONS}) install( DIRECTORY ${header_directory} DESTINATION ${INCLUDE_INSTALL_DIR} @@ -316,9 +316,27 @@ set(EXPORT_TARGETS yaml-cpp CACHE INTERNAL "export targets") set(CONFIG_INCLUDE_DIRS "${YAML_CPP_SOURCE_DIR}/include") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/yaml-cpp-config.cmake.in "${PROJECT_BINARY_DIR}/yaml-cpp-config.cmake" @ONLY) + +if(WIN32 AND NOT CYGWIN) + set(INSTALL_CMAKE_DIR ${CMAKE_INSTALL_PREFIX}/CMake) +else() + set(INSTALL_CMAKE_DIR ${LIB_INSTALL_DIR}/cmake/yaml-cpp) +endif() + +file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}" "${INCLUDE_INSTALL_ROOT_DIR}") +set(CONFIG_INCLUDE_DIRS "\${YAML_CPP_CMAKE_DIR}/${REL_INCLUDE_DIR}") +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/yaml-cpp-config.cmake.in + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/yaml-cpp-config.cmake" @ONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/yaml-cpp-config-version.cmake.in "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" @ONLY) +install(FILES + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/yaml-cpp-config.cmake" + "${PROJECT_BINARY_DIR}/yaml-cpp-config-version.cmake" + DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) +install(EXPORT yaml-cpp-targets DESTINATION ${INSTALL_CMAKE_DIR}) + if(UNIX) set(PC_FILE ${CMAKE_BINARY_DIR}/yaml-cpp.pc) configure_file("yaml-cpp.pc.cmake" ${PC_FILE} @ONLY) From 34bd1a7083e5875e6a4b2d4f61c0b356cc5d53fc Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Mon, 28 Mar 2016 23:47:21 -0500 Subject: [PATCH 16/37] Update CONTRIBUTING.md --- CONTRIBUTING.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 41f50dc..cd09a1a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,18 @@ This project is formatted with [clang-format][fmt] using the style file at the root of the repository. Please run clang-format before sending a pull request. -In general, try to follow the style of surrounding code. +In general, try to follow the style of surrounding code. We mostly follow the [Google C++ style guide][cpp-style]. + +Commit messages should be in the imperative mood, as described in the [Git contributing file][git-contrib]: + +> Describe your changes in imperative mood, e.g. "make xyzzy do frotz" +> instead of "[This patch] makes xyzzy do frotz" or "[I] changed xyzzy +> to do frotz", as if you are giving orders to the codebase to change +> its behaviour. [fmt]: http://clang.llvm.org/docs/ClangFormat.html +[cpp-style]: https://google.github.io/styleguide/cppguide.html +[git-contrib]: http://git.kernel.org/cgit/git/git.git/tree/Documentation/SubmittingPatches?id=HEAD # Tests From 52bcefa1f18947bac98288b00188551046edb948 Mon Sep 17 00:00:00 2001 From: TripleWhy Date: Sat, 2 Apr 2016 00:14:59 +0200 Subject: [PATCH 17/37] Make null handling YAML 1.2 compliant. --- include/yaml-cpp/null.h | 2 ++ src/emitterutils.cpp | 7 ++----- src/null.cpp | 4 ++++ src/singledocparser.cpp | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/yaml-cpp/null.h b/include/yaml-cpp/null.h index 5dbda9a..b9521d4 100644 --- a/include/yaml-cpp/null.h +++ b/include/yaml-cpp/null.h @@ -8,6 +8,7 @@ #endif #include "yaml-cpp/dll.h" +#include namespace YAML { class Node; @@ -17,6 +18,7 @@ inline bool operator==(const _Null&, const _Null&) { return true; } inline bool operator!=(const _Null&, const _Null&) { return false; } YAML_CPP_API bool IsNull(const Node& node); // old API only +YAML_CPP_API bool IsNullString(const std::string& str); extern YAML_CPP_API _Null Null; } diff --git a/src/emitterutils.cpp b/src/emitterutils.cpp index 4a4c982..42c2a39 100644 --- a/src/emitterutils.cpp +++ b/src/emitterutils.cpp @@ -9,6 +9,7 @@ #include "stringsource.h" #include "yaml-cpp/binary.h" // IWYU pragma: keep #include "yaml-cpp/ostream_wrapper.h" +#include "yaml-cpp/null.h" namespace YAML { namespace Utils { @@ -152,12 +153,8 @@ void WriteCodePoint(ostream_wrapper& out, int codePoint) { bool IsValidPlainScalar(const std::string& str, FlowType::value flowType, bool allowOnlyAscii) { - if (str.empty()) { - return false; - } - // check against null - if (str == "null") { + if (IsNullString(str)) { return false; } diff --git a/src/null.cpp b/src/null.cpp index 1b24b70..7158bd4 100644 --- a/src/null.cpp +++ b/src/null.cpp @@ -2,4 +2,8 @@ namespace YAML { _Null Null; + +bool IsNullString(const std::string& str) { + return str.empty() || str == "~" || str == "null" || str == "Null" || str == "NULL"; +} } diff --git a/src/singledocparser.cpp b/src/singledocparser.cpp index cde1d20..a27c1c3 100644 --- a/src/singledocparser.cpp +++ b/src/singledocparser.cpp @@ -11,6 +11,7 @@ #include "yaml-cpp/eventhandler.h" #include "yaml-cpp/exceptions.h" // IWYU pragma: keep #include "yaml-cpp/mark.h" +#include "yaml-cpp/null.h" namespace YAML { SingleDocParser::SingleDocParser(Scanner& scanner, const Directives& directives) @@ -75,7 +76,7 @@ void SingleDocParser::HandleNode(EventHandler& eventHandler) { const Token& token = m_scanner.peek(); - if (token.type == Token::PLAIN_SCALAR && token.value == "null") { + if (token.type == Token::PLAIN_SCALAR && IsNullString(token.value)) { eventHandler.OnNull(mark, anchor); m_scanner.pop(); return; From d155b0d0d2f7cd5dea355ae50eb609c0a06dd962 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 17:32:11 -0500 Subject: [PATCH 18/37] Add .travis.yml for running test/run-tests. --- .travis.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c6accf8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: c++ +compiler: + - clang + - gcc +before_script: + - mkdir build + - cd build + - cmake .. +script: + - make + - test/run-tests From c5f6482325587cd5b3ec9fb602f92494855d058c Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 17:53:46 -0500 Subject: [PATCH 19/37] Use gcc 4.7 with travis. gcc 4.6 (the default) does have some C++11 support, but its flag is -std=c++0x. Since 4.7 was released in 2012, I think it's reasonable to require it. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index c6accf8..099bcde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,11 @@ language: c++ compiler: - clang - gcc +before_install: + - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + - sudo apt-get update -qq + - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.7; fi + - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi before_script: - mkdir build - cd build From 0535811c18ba25fe7e35def9b75584aa62aa0bcc Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 17:58:36 -0500 Subject: [PATCH 20/37] Fix gcc version typo in travis config. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 099bcde..e2c694d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ before_install: - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get update -qq - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.7; fi - - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi + - if [ "$CXX" = "g++" ]; then export CXX="g++-4.7" CC="gcc-4.7"; fi before_script: - mkdir build - cd build From 2176fd994ec69bdb1ef805bfc7d74a2052f9cfa5 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 18:11:37 -0500 Subject: [PATCH 21/37] Add osx to travis config. --- .travis.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e2c694d..1bbfd54 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,20 @@ language: c++ +os: + - linux + - osx compiler: - clang - gcc before_install: - - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - - sudo apt-get update -qq - - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.7; fi - - if [ "$CXX" = "g++" ]; then export CXX="g++-4.7" CC="gcc-4.7"; fi + - | + if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + && sudo apt-get update -qq + if [ "$CXX" = "g++" ]; then + sudo apt-get install -qq g++-4.7 + && export CXX="g++-4.7" CC="gcc-4.7" + fi + fi before_script: - mkdir build - cd build From 2a02cee1c878b97be931e69f873ab44c42fac724 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 18:17:58 -0500 Subject: [PATCH 22/37] Fix typos in travis config. Hooray for testing travis configs via pushing to master. --- .travis.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1bbfd54..d464896 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,11 +8,10 @@ compiler: before_install: - | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - && sudo apt-get update -qq - if [ "$CXX" = "g++" ]; then - sudo apt-get install -qq g++-4.7 - && export CXX="g++-4.7" CC="gcc-4.7" + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y \ + && sudo apt-get update -qq \ + && if [ "$CXX" == "g++" ]; then + sudo apt-get install -qq g++-4.7 && export CXX="g++-4.7" CC="gcc-4.7" fi fi before_script: From ca34a379c78bc64707e30d8c9c53531205d4c857 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 18:26:12 -0500 Subject: [PATCH 23/37] Add build status to README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ca95fd..de36125 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ -# yaml-cpp +# yaml-cpp [![Build Status](https://travis-ci.org/jbeder/yaml-cpp.svg?branch=master)](https://travis-ci.org/jbeder/yaml-cpp) yaml-cpp is a [YAML](http://www.yaml.org/) parser and emitter in C++ matching the [YAML 1.2 spec](http://www.yaml.org/spec/1.2/spec.html). To get a feel for how it can be used, see the [Tutorial](https://github.com/jbeder/yaml-cpp/wiki/Tutorial) or [How to Emit YAML](https://github.com/jbeder/yaml-cpp/wiki/How-To-Emit-YAML). For the old API (version < 0.5.0), see [How To Parse A Document](https://github.com/jbeder/yaml-cpp/wiki/How-To-Parse-A-Document-(Old-API)). - # Problems? # If you find a bug, post an [issue](https://github.com/jbeder/yaml-cpp/issues)! If you have questions about how to use yaml-cpp, please post it on http://stackoverflow.com and tag it `yaml-cpp`. From cc229e87b3b421f281ad703f45a23869e674aa73 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 18:26:52 -0500 Subject: [PATCH 24/37] Turn off travis email notifications. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index d464896..90f82ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,3 +21,5 @@ before_script: script: - make - test/run-tests +notifications: + email: false From bfb5703ce92b18e1f2d974a7c567a003184b64b0 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 20:01:01 -0500 Subject: [PATCH 25/37] Add test to verify that ~ is loaded as null. --- test/integration/load_node_test.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp index 6136307..ff0a3ae 100644 --- a/test/integration/load_node_test.cpp +++ b/test/integration/load_node_test.cpp @@ -226,5 +226,10 @@ TEST(NodeTest, IncompleteJson) { } } +TEST(NodeTest, LoadTildeAsNull) { + Node node = Load("~"); + ASSERT_TRUE(node.IsNull()); +} + } // namespace } // namespace YAML From 51b59d11baa3b4109d0b9211bbdcac397648a16c Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 20:06:28 -0500 Subject: [PATCH 26/37] Update .clang-format to use C++ 11 style. --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index d6e1b22..d6d46fb 100644 --- a/.clang-format +++ b/.clang-format @@ -30,7 +30,7 @@ PenaltyReturnTypeOnItsOwnLine: 200 PointerBindsToType: true SpacesBeforeTrailingComments: 2 Cpp11BracedListStyle: true -Standard: Auto +Standard: Cpp11 IndentWidth: 2 TabWidth: 8 UseTab: Never From 0d810ad6d5dca43965cdfa3a41a979e0a4a5a301 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Sat, 2 Apr 2016 10:11:01 +0900 Subject: [PATCH 27/37] Add missing include. --- include/yaml-cpp/node/detail/node_iterator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/yaml-cpp/node/detail/node_iterator.h b/include/yaml-cpp/node/detail/node_iterator.h index a33049a..4dcf3ec 100644 --- a/include/yaml-cpp/node/detail/node_iterator.h +++ b/include/yaml-cpp/node/detail/node_iterator.h @@ -9,6 +9,7 @@ #include "yaml-cpp/dll.h" #include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/detail/node.h" #include #include #include From 883a1e8540268d31e6327b4e865b87d84c328ea0 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 20:18:11 -0500 Subject: [PATCH 28/37] Revert "Add missing include." Reason: broken build. This reverts commit 0d810ad6d5dca43965cdfa3a41a979e0a4a5a301. --- include/yaml-cpp/node/detail/node_iterator.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/yaml-cpp/node/detail/node_iterator.h b/include/yaml-cpp/node/detail/node_iterator.h index 4dcf3ec..a33049a 100644 --- a/include/yaml-cpp/node/detail/node_iterator.h +++ b/include/yaml-cpp/node/detail/node_iterator.h @@ -9,7 +9,6 @@ #include "yaml-cpp/dll.h" #include "yaml-cpp/node/ptr.h" -#include "yaml-cpp/node/detail/node.h" #include #include #include From 091ddfa52d62d42021a00790aa7d2521af60c7e5 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Fri, 1 Apr 2016 20:27:26 -0500 Subject: [PATCH 29/37] Turn travis email notifications back on. Apparently the default is on_success: change on_failure: always which seems reasonable. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 90f82ef..d464896 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,5 +21,3 @@ before_script: script: - make - test/run-tests -notifications: - email: false From 7d2873ce9f2202ea21b6a8c5ecbc9fe38032c229 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Sat, 2 Apr 2016 16:02:26 -0500 Subject: [PATCH 30/37] Fix scalar parsing when a line starts with a comment. --- src/scanscalar.cpp | 77 ++++++++++++++++++++----------- test/integration/handler_test.cpp | 30 ++++++++++++ 2 files changed, 81 insertions(+), 26 deletions(-) diff --git a/src/scanscalar.cpp b/src/scanscalar.cpp index 62ac875..10e359d 100644 --- a/src/scanscalar.cpp +++ b/src/scanscalar.cpp @@ -39,15 +39,17 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { std::size_t lastNonWhitespaceChar = scalar.size(); bool escapedNewline = false; while (!params.end->Matches(INPUT) && !Exp::Break().Matches(INPUT)) { - if (!INPUT) + if (!INPUT) { break; + } // document indicator? if (INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) { - if (params.onDocIndicator == BREAK) + if (params.onDocIndicator == BREAK) { break; - else if (params.onDocIndicator == THROW) + } else if (params.onDocIndicator == THROW) { throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR); + } } foundNonEmptyLine = true; @@ -74,27 +76,31 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { // otherwise, just add the damn character char ch = INPUT.get(); scalar += ch; - if (ch != ' ' && ch != '\t') + if (ch != ' ' && ch != '\t') { lastNonWhitespaceChar = scalar.size(); + } } // eof? if we're looking to eat something, then we throw if (!INPUT) { - if (params.eatEnd) + 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)) + Exp::DocIndicator().Matches(INPUT)) { break; + } // are we done via character match? int n = params.end->Match(INPUT); if (n >= 0) { - if (params.eatEnd) + if (params.eatEnd) { INPUT.eat(n); + } break; } @@ -111,23 +117,33 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { // Phase #3: scan initial spaces // first the required indentation - while (INPUT.peek() == ' ' && (INPUT.column() < params.indent || - (params.detectIndent && !foundNonEmptyLine))) + while (INPUT.peek() == ' ' && + (INPUT.column() < params.indent || + (params.detectIndent && !foundNonEmptyLine)) && + !params.end->Matches(INPUT)) { INPUT.eat(1); + } // update indent if we're auto-detecting - if (params.detectIndent && !foundNonEmptyLine) + 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) + params.onTabInIndentation == THROW) { throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION); + } - if (!params.eatLeadingWhitespace) + if (!params.eatLeadingWhitespace) { break; + } + + if (params.end->Matches(INPUT)) { + break; + } INPUT.eat(1); } @@ -147,26 +163,29 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { break; case FOLD_BLOCK: if (!emptyLine && !nextEmptyLine && !moreIndented && - !nextMoreIndented && INPUT.column() >= params.indent) + !nextMoreIndented && INPUT.column() >= params.indent) { scalar += " "; - else if (nextEmptyLine) + } else if (nextEmptyLine) { foldedNewlineCount++; - else + } else { scalar += "\n"; + } if (!nextEmptyLine && foldedNewlineCount > 0) { scalar += std::string(foldedNewlineCount - 1, '\n'); if (foldedNewlineStartedMoreIndented || - nextMoreIndented | !foundNonEmptyLine) + nextMoreIndented | !foundNonEmptyLine) { scalar += "\n"; + } foldedNewlineCount = 0; } break; case FOLD_FLOW: - if (nextEmptyLine) + if (nextEmptyLine) { scalar += "\n"; - else if (!emptyLine && !nextEmptyLine && !escapedNewline) + } else if (!emptyLine && !nextEmptyLine && !escapedNewline) { scalar += " "; + } break; } } @@ -186,35 +205,41 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { if (params.trimTrailingSpaces) { std::size_t pos = scalar.find_last_not_of(' '); if (lastEscapedChar != std::string::npos) { - if (pos < lastEscapedChar || pos == std::string::npos) + if (pos < lastEscapedChar || pos == std::string::npos) { pos = lastEscapedChar; + } } - if (pos < scalar.size()) + if (pos < scalar.size()) { scalar.erase(pos + 1); + } } switch (params.chomp) { case CLIP: { std::size_t pos = scalar.find_last_not_of('\n'); if (lastEscapedChar != std::string::npos) { - if (pos < lastEscapedChar || pos == std::string::npos) + if (pos < lastEscapedChar || pos == std::string::npos) { pos = lastEscapedChar; + } } - if (pos == std::string::npos) + if (pos == std::string::npos) { scalar.erase(); - else if (pos + 1 < scalar.size()) + } else if (pos + 1 < scalar.size()) { scalar.erase(pos + 2); + } } break; case STRIP: { std::size_t pos = scalar.find_last_not_of('\n'); if (lastEscapedChar != std::string::npos) { - if (pos < lastEscapedChar || pos == std::string::npos) + if (pos < lastEscapedChar || pos == std::string::npos) { pos = lastEscapedChar; + } } - if (pos == std::string::npos) + if (pos == std::string::npos) { scalar.erase(); - else if (pos < scalar.size()) + } else if (pos < scalar.size()) { scalar.erase(pos + 1); + } } break; default: break; diff --git a/test/integration/handler_test.cpp b/test/integration/handler_test.cpp index bb91cee..6011460 100644 --- a/test/integration/handler_test.cpp +++ b/test/integration/handler_test.cpp @@ -42,5 +42,35 @@ TEST_F(HandlerTest, NullStringScalar) { EXPECT_CALL(handler, OnDocumentEnd()); Parse("foo: null"); } + +TEST_F(HandlerTest, CommentOnNewlineOfMapValueWithNoSpaces) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse("key: value\n# comment"); } + +TEST_F(HandlerTest, CommentOnNewlineOfMapValueWithOneSpace) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse("key: value\n # comment"); } + +TEST_F(HandlerTest, CommentOnNewlineOfMapValueWithManySpace) { + EXPECT_CALL(handler, OnDocumentStart(_)); + EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block)); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "key")); + EXPECT_CALL(handler, OnScalar(_, "?", 0, "value")); + EXPECT_CALL(handler, OnMapEnd()); + EXPECT_CALL(handler, OnDocumentEnd()); + Parse("key: value\n # comment"); +} +} // namespace +} // namespace YAML From f35f4a19fa052fe648a5b91077697b8606af55af Mon Sep 17 00:00:00 2001 From: SirLoxley Date: Thu, 12 May 2016 06:00:34 +0200 Subject: [PATCH 31/37] Fix for broken build on cygwin using gcc - "error '[fileno, strdup, fdopen]' are not in scope" (#374) --- test/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 74c2594..74455a5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,7 +10,13 @@ endif() if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(yaml_test_flags "-Wno-c99-extensions -Wno-variadic-macros -Wno-sign-compare -std=c++11") + set(yaml_test_flags "-Wno-c99-extensions -Wno-variadic-macros -Wno-sign-compare") + + if(CMAKE_COMPILER_IS_GNUCXX) + set(yaml_test_flags "${yaml_test_flags} -std=gnu++11") + else() + set(yaml_test_flags "${yaml_test_flags} -std=c++11") + endif() endif() file(GLOB test_headers [a-z_]*.h) From 6e79997bb2075f51638464324a55a19764fbf797 Mon Sep 17 00:00:00 2001 From: Paul Novotny Date: Thu, 12 May 2016 09:55:45 -0400 Subject: [PATCH 32/37] Use CodeDocs.xyz for Doxygen documentation (#375) Add the .codedocs config file and badge to the README.md file. --- .codedocs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 .codedocs diff --git a/.codedocs b/.codedocs new file mode 100644 index 0000000..e03a28c --- /dev/null +++ b/.codedocs @@ -0,0 +1,50 @@ +# CodeDocs.xyz Configuration File + +# Optional project name, if left empty the GitHub repository name will be used. +PROJECT_NAME = + +# One or more directories and files that contain example code to be included. +EXAMPLE_PATH = + +# One or more directories and files to exclude from documentation generation. +# Use relative paths with respect to the repository root directory. +EXCLUDE = test/gmock-1.7.0/ + +# One or more wildcard patterns to exclude files and directories from document +# generation. +EXCLUDE_PATTERNS = + +# One or more symbols to exclude from document generation. Symbols can be +# namespaces, classes, or functions. +EXCLUDE_SYMBOLS = + +# Override the default parser (language) used for each file extension. +EXTENSION_MAPPING = + +# Set the wildcard patterns used to filter out the source-files. +# If left blank the default is: +# *.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox, *.py, +# *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. +FILE_PATTERNS = + +# Hide undocumented class members. +HIDE_UNDOC_MEMBERS = + +# Hide undocumented classes. +HIDE_UNDOC_CLASSES = + +# Specify a markdown page whose contents should be used as the main page +# (index.html). This will override a page marked as \mainpage. For example, a +# README.md file usually serves as a useful main page. +USE_MDFILE_AS_MAINPAGE = README.md + +# Specify external repository to link documentation with. +# This is similar to Doxygen's TAGFILES option, but will automatically link to +# tags of other repositories already using CodeDocs. List each repository to +# link with by giving its location in the form of owner/repository. +# For example: +# TAGLINKS = doxygen/doxygen CodeDocs/osg +# Note: these repositories must already be built on CodeDocs. +TAGLINKS = diff --git a/README.md b/README.md index de36125..58fde68 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# yaml-cpp [![Build Status](https://travis-ci.org/jbeder/yaml-cpp.svg?branch=master)](https://travis-ci.org/jbeder/yaml-cpp) +# yaml-cpp [![Build Status](https://travis-ci.org/jbeder/yaml-cpp.svg?branch=master)](https://travis-ci.org/jbeder/yaml-cpp) [![Documentation](https://codedocs.xyz/jbeder/yaml-cpp.svg)](https://codedocs.xyz/jbeder/yaml-cpp/) yaml-cpp is a [YAML](http://www.yaml.org/) parser and emitter in C++ matching the [YAML 1.2 spec](http://www.yaml.org/spec/1.2/spec.html). From a45a61742bcae31934ed3691ac70dba46d7cb2cb Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Thu, 12 May 2016 22:44:59 -0500 Subject: [PATCH 33/37] Remove ptr_stack, in favor of vector>. (Not stack> because it wasn't quite a stack; we needed to get the second-to-last element sometimes.) --- src/emitterstate.cpp | 61 +++++++++++++++++++++++++------------------- src/emitterstate.h | 8 +++--- src/ptr_stack.h | 58 ----------------------------------------- 3 files changed, 39 insertions(+), 88 deletions(-) delete mode 100644 src/ptr_stack.h diff --git a/src/emitterstate.cpp b/src/emitterstate.cpp index aa3e784..e5555e6 100644 --- a/src/emitterstate.cpp +++ b/src/emitterstate.cpp @@ -53,28 +53,31 @@ void EmitterState::SetNonContent() { m_hasNonContent = true; } void EmitterState::SetLongKey() { assert(!m_groups.empty()); - if (m_groups.empty()) + if (m_groups.empty()) { return; + } - assert(m_groups.top().type == GroupType::Map); - m_groups.top().longKey = true; + assert(m_groups.back()->type == GroupType::Map); + m_groups.back()->longKey = true; } void EmitterState::ForceFlow() { assert(!m_groups.empty()); - if (m_groups.empty()) + if (m_groups.empty()) { return; + } - m_groups.top().flowType = FlowType::Flow; + m_groups.back()->flowType = FlowType::Flow; } void EmitterState::StartedNode() { if (m_groups.empty()) { m_docCount++; } else { - m_groups.top().childCount++; - if (m_groups.top().childCount % 2 == 0) - m_groups.top().longKey = false; + m_groups.back()->childCount++; + if (m_groups.back()->childCount % 2 == 0) { + m_groups.back()->longKey = false; + } } m_hasAnchor = false; @@ -121,7 +124,7 @@ void EmitterState::StartedScalar() { void EmitterState::StartedGroup(GroupType::value type) { StartedNode(); - const int lastGroupIndent = (m_groups.empty() ? 0 : m_groups.top().indent); + const int lastGroupIndent = (m_groups.empty() ? 0 : m_groups.back()->indent); m_curIndent += lastGroupIndent; // TODO: Create move constructors for settings types to simplify transfer @@ -134,32 +137,36 @@ void EmitterState::StartedGroup(GroupType::value type) { pGroup->modifiedSettings = std::move(m_modifiedSettings); // set up group - if (GetFlowType(type) == Block) + if (GetFlowType(type) == Block) { pGroup->flowType = FlowType::Block; - else + } else { pGroup->flowType = FlowType::Flow; + } pGroup->indent = GetIndent(); - m_groups.push(std::move(pGroup)); + m_groups.push_back(std::move(pGroup)); } void EmitterState::EndedGroup(GroupType::value type) { if (m_groups.empty()) { - if (type == GroupType::Seq) + if (type == GroupType::Seq) { return SetError(ErrorMsg::UNEXPECTED_END_SEQ); - else + } else { return SetError(ErrorMsg::UNEXPECTED_END_MAP); + } } // get rid of the current group { - std::unique_ptr pFinishedGroup = m_groups.pop(); - if (pFinishedGroup->type != type) + std::unique_ptr pFinishedGroup = std::move(m_groups.back()); + m_groups.pop_back(); + if (pFinishedGroup->type != type) { return SetError(ErrorMsg::UNMATCHED_GROUP_TAG); + } } // reset old settings - std::size_t lastIndent = (m_groups.empty() ? 0 : m_groups.top().indent); + std::size_t lastIndent = (m_groups.empty() ? 0 : m_groups.back()->indent); assert(m_curIndent >= lastIndent); m_curIndent -= lastIndent; @@ -171,37 +178,39 @@ void EmitterState::EndedGroup(GroupType::value type) { } EmitterNodeType::value EmitterState::CurGroupNodeType() const { - if (m_groups.empty()) + if (m_groups.empty()) { return EmitterNodeType::NoType; + } - return m_groups.top().NodeType(); + return m_groups.back()->NodeType(); } GroupType::value EmitterState::CurGroupType() const { - return m_groups.empty() ? GroupType::NoType : m_groups.top().type; + return m_groups.empty() ? GroupType::NoType : m_groups.back()->type; } FlowType::value EmitterState::CurGroupFlowType() const { - return m_groups.empty() ? FlowType::NoType : m_groups.top().flowType; + return m_groups.empty() ? FlowType::NoType : m_groups.back()->flowType; } int EmitterState::CurGroupIndent() const { - return m_groups.empty() ? 0 : m_groups.top().indent; + return m_groups.empty() ? 0 : m_groups.back()->indent; } std::size_t EmitterState::CurGroupChildCount() const { - return m_groups.empty() ? m_docCount : m_groups.top().childCount; + return m_groups.empty() ? m_docCount : m_groups.back()->childCount; } bool EmitterState::CurGroupLongKey() const { - return m_groups.empty() ? false : m_groups.top().longKey; + return m_groups.empty() ? false : m_groups.back()->longKey; } int EmitterState::LastIndent() const { - if (m_groups.size() <= 1) + if (m_groups.size() <= 1) { return 0; + } - return m_curIndent - m_groups.top(-1).indent; + return m_curIndent - m_groups[m_groups.size() - 2]->indent; } void EmitterState::ClearModifiedSettings() { m_modifiedSettings.clear(); } diff --git a/src/emitterstate.h b/src/emitterstate.h index 2ddec76..aba390c 100644 --- a/src/emitterstate.h +++ b/src/emitterstate.h @@ -7,15 +7,15 @@ #pragma once #endif -#include "ptr_stack.h" #include "setting.h" #include "yaml-cpp/emitterdef.h" #include "yaml-cpp/emittermanip.h" + #include -#include -#include #include +#include #include +#include namespace YAML { struct FmtScope { @@ -174,7 +174,7 @@ class EmitterState { } }; - ptr_stack m_groups; + std::vector> m_groups; std::size_t m_curIndent; bool m_hasAnchor; bool m_hasTag; diff --git a/src/ptr_stack.h b/src/ptr_stack.h deleted file mode 100644 index b71eb99..0000000 --- a/src/ptr_stack.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef PTR_STACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 -#define PTR_STACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 - -#if defined(_MSC_VER) || \ - (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ - (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 -#pragma once -#endif - -#include -#include -#include -#include - -#include "yaml-cpp/noncopyable.h" - -// TODO: This class is no longer needed -template -class ptr_stack : private YAML::noncopyable { - public: - ptr_stack() {} - - void clear() { - m_data.clear(); - } - - std::size_t size() const { return m_data.size(); } - bool empty() const { return m_data.empty(); } - - void push(std::unique_ptr&& t) { - m_data.push_back(std::move(t)); - } - std::unique_ptr pop() { - std::unique_ptr t(std::move(m_data.back())); - m_data.pop_back(); - return t; - } - - T& top() { - return *(m_data.back().get()); - } - const T& top() const { - return *(m_data.back().get()); - } - - T& top(std::ptrdiff_t diff) { - return *((m_data.end() - 1 + diff)->get()); - } - - const T& top(std::ptrdiff_t diff) const { - return *((m_data.end() - 1 + diff)->get()); - } - - private: - std::vector> m_data; -}; - -#endif // PTR_STACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 From 148da4711428460aff3ee20dea61f6b7190f71f1 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Thu, 12 May 2016 23:05:28 -0500 Subject: [PATCH 34/37] Update documentation for Scanner and AnchorDict, and fix formatting. --- include/yaml-cpp/contrib/anchordict.h | 12 +- src/scanner.cpp | 164 +++++++++++++------------- src/scanner.h | 59 ++++++++- 3 files changed, 146 insertions(+), 89 deletions(-) diff --git a/include/yaml-cpp/contrib/anchordict.h b/include/yaml-cpp/contrib/anchordict.h index b4677e2..78db9ec 100644 --- a/include/yaml-cpp/contrib/anchordict.h +++ b/include/yaml-cpp/contrib/anchordict.h @@ -12,11 +12,13 @@ #include "../anchor.h" namespace YAML { -/// AnchorDict -/// . An object that stores and retrieves values correlating to anchor_t -/// values. -/// . Efficient implementation that can make assumptions about how anchor_t -/// values are assigned by the Parser class. +/** + * An object that stores and retrieves values correlating to {@link anchor_t} + * values. + * + *

Efficient implementation that can make assumptions about how + * {@code anchor_t} values are assigned by the {@link Parser} class. + */ template class AnchorDict { public: diff --git a/src/scanner.cpp b/src/scanner.cpp index 767b8e1..3c9e7af 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -16,23 +16,17 @@ Scanner::Scanner(std::istream& in) Scanner::~Scanner() {} -// empty -// . Returns true if there are no more tokens to be read bool Scanner::empty() { EnsureTokensInQueue(); return m_tokens.empty(); } -// pop -// . Simply removes the next token on the queue. void Scanner::pop() { EnsureTokensInQueue(); if (!m_tokens.empty()) m_tokens.pop(); } -// peek -// . Returns (but does not remove) the next token on the queue. Token& Scanner::peek() { EnsureTokensInQueue(); assert(!m_tokens.empty()); // should we be asserting here? I mean, we really @@ -49,21 +43,17 @@ Token& Scanner::peek() { return m_tokens.front(); } -// mark -// . Returns the current mark in the stream Mark Scanner::mark() const { return INPUT.mark(); } -// EnsureTokensInQueue -// . Scan until there's a valid token at the front of the queue, -// or we're sure the queue is empty. void Scanner::EnsureTokensInQueue() { while (1) { if (!m_tokens.empty()) { Token& token = m_tokens.front(); // if this guy's valid, then we're done - if (token.status == Token::VALID) + if (token.status == Token::VALID) { return; + } // here's where we clean up the impossible tokens if (token.status == Token::INVALID) { @@ -75,23 +65,23 @@ void Scanner::EnsureTokensInQueue() { } // no token? maybe we've actually finished - if (m_endedStream) + if (m_endedStream) { return; + } // no? then scan... ScanNextToken(); } } -// ScanNextToken -// . The main scanning function; here we branch out and -// scan whatever the next token should be. void Scanner::ScanNextToken() { - if (m_endedStream) + if (m_endedStream) { return; + } - if (!m_startedStream) + if (!m_startedStream) { return StartStream(); + } // get rid of whitespace, etc. (in between tokens it should be irrelevent) ScanToNextToken(); @@ -104,85 +94,102 @@ void Scanner::ScanNextToken() { // ***** // end of stream - if (!INPUT) + if (!INPUT) { return EndStream(); + } - if (INPUT.column() == 0 && INPUT.peek() == Keys::Directive) + if (INPUT.column() == 0 && INPUT.peek() == Keys::Directive) { return ScanDirective(); + } // document token - if (INPUT.column() == 0 && Exp::DocStart().Matches(INPUT)) + if (INPUT.column() == 0 && Exp::DocStart().Matches(INPUT)) { return ScanDocStart(); + } - if (INPUT.column() == 0 && Exp::DocEnd().Matches(INPUT)) + if (INPUT.column() == 0 && Exp::DocEnd().Matches(INPUT)) { return ScanDocEnd(); + } // flow start/end/entry - if (INPUT.peek() == Keys::FlowSeqStart || INPUT.peek() == Keys::FlowMapStart) + if (INPUT.peek() == Keys::FlowSeqStart || + INPUT.peek() == Keys::FlowMapStart) { return ScanFlowStart(); + } - if (INPUT.peek() == Keys::FlowSeqEnd || INPUT.peek() == Keys::FlowMapEnd) + if (INPUT.peek() == Keys::FlowSeqEnd || INPUT.peek() == Keys::FlowMapEnd) { return ScanFlowEnd(); + } - if (INPUT.peek() == Keys::FlowEntry) + if (INPUT.peek() == Keys::FlowEntry) { return ScanFlowEntry(); + } // block/map stuff - if (Exp::BlockEntry().Matches(INPUT)) + if (Exp::BlockEntry().Matches(INPUT)) { return ScanBlockEntry(); + } - if ((InBlockContext() ? Exp::Key() : Exp::KeyInFlow()).Matches(INPUT)) + if ((InBlockContext() ? Exp::Key() : Exp::KeyInFlow()).Matches(INPUT)) { return ScanKey(); + } - if (GetValueRegex().Matches(INPUT)) + if (GetValueRegex().Matches(INPUT)) { return ScanValue(); + } // alias/anchor - if (INPUT.peek() == Keys::Alias || INPUT.peek() == Keys::Anchor) + if (INPUT.peek() == Keys::Alias || INPUT.peek() == Keys::Anchor) { return ScanAnchorOrAlias(); + } // tag - if (INPUT.peek() == Keys::Tag) + if (INPUT.peek() == Keys::Tag) { return ScanTag(); + } // special scalars if (InBlockContext() && (INPUT.peek() == Keys::LiteralScalar || - INPUT.peek() == Keys::FoldedScalar)) + INPUT.peek() == Keys::FoldedScalar)) { return ScanBlockScalar(); + } - if (INPUT.peek() == '\'' || INPUT.peek() == '\"') + if (INPUT.peek() == '\'' || INPUT.peek() == '\"') { return ScanQuotedScalar(); + } // plain scalars if ((InBlockContext() ? Exp::PlainScalar() : Exp::PlainScalarInFlow()) - .Matches(INPUT)) + .Matches(INPUT)) { return ScanPlainScalar(); + } // don't know what it is! throw ParserException(INPUT.mark(), ErrorMsg::UNKNOWN_TOKEN); } -// ScanToNextToken -// . Eats input until we reach the next token-like thing. void Scanner::ScanToNextToken() { while (1) { // first eat whitespace while (INPUT && IsWhitespaceToBeEaten(INPUT.peek())) { - if (InBlockContext() && Exp::Tab().Matches(INPUT)) + if (InBlockContext() && Exp::Tab().Matches(INPUT)) { m_simpleKeyAllowed = false; + } INPUT.eat(1); } // then eat a comment if (Exp::Comment().Matches(INPUT)) { // eat until line break - while (INPUT && !Exp::Break().Matches(INPUT)) + while (INPUT && !Exp::Break().Matches(INPUT)) { INPUT.eat(1); + } } // if it's NOT a line break, then we're done! - if (!Exp::Break().Matches(INPUT)) + if (!Exp::Break().Matches(INPUT)) { break; + } // otherwise, let's eat the line break and keep going int n = Exp::Break().Match(INPUT); @@ -192,8 +199,9 @@ void Scanner::ScanToNextToken() { InvalidateSimpleKey(); // new line - we may be able to accept a simple key now - if (InBlockContext()) + if (InBlockContext()) { m_simpleKeyAllowed = true; + } } } @@ -210,40 +218,39 @@ void Scanner::ScanToNextToken() { // that they can't contribute to indentation, so once you've seen a tab in a // line, you can't start a simple key bool Scanner::IsWhitespaceToBeEaten(char ch) { - if (ch == ' ') + if (ch == ' ') { return true; + } - if (ch == '\t') + if (ch == '\t') { return true; + } return false; } -// GetValueRegex -// . Get the appropriate regex to check if it's a value token const RegEx& Scanner::GetValueRegex() const { - if (InBlockContext()) + if (InBlockContext()) { return Exp::Value(); + } return m_canBeJSONFlow ? Exp::ValueInJSONFlow() : Exp::ValueInFlow(); } -// StartStream -// . Set the initial conditions for starting a stream. void Scanner::StartStream() { m_startedStream = true; m_simpleKeyAllowed = true; - std::unique_ptr pIndent(new IndentMarker(-1, IndentMarker::NONE)); + std::unique_ptr pIndent( + new IndentMarker(-1, IndentMarker::NONE)); m_indentRefs.push_back(std::move(pIndent)); m_indents.push(&m_indentRefs.back()); } -// EndStream -// . Close out the stream, finish up, etc. void Scanner::EndStream() { // force newline - if (INPUT.column() > 0) + if (INPUT.column() > 0) { INPUT.ResetColumn(); + } PopAllIndents(); PopAllSimpleKeys(); @@ -271,27 +278,26 @@ Token::TYPE Scanner::GetStartTokenFor(IndentMarker::INDENT_TYPE type) const { throw std::runtime_error("yaml-cpp: internal error, invalid indent type"); } -// 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()) + if (InFlowContext()) { return 0; + } std::unique_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) + if (indent.column < lastIndent.column) { return 0; + } if (indent.column == lastIndent.column && !(indent.type == IndentMarker::SEQ && - lastIndent.type == IndentMarker::MAP)) + lastIndent.type == IndentMarker::MAP)) { return 0; + } // push a start token indent.pStartToken = PushToken(GetStartTokenFor(type)); @@ -302,53 +308,50 @@ Scanner::IndentMarker* Scanner::PushIndentTo(int column, 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()) + if (InFlowContext()) { return; + } // now pop away while (!m_indents.empty()) { const IndentMarker& indent = *m_indents.top(); - if (indent.column < INPUT.column()) + if (indent.column < INPUT.column()) { break; + } if (indent.column == INPUT.column() && !(indent.type == IndentMarker::SEQ && - !Exp::BlockEntry().Matches(INPUT))) + !Exp::BlockEntry().Matches(INPUT))) { break; + } PopIndent(); } - while (!m_indents.empty() && m_indents.top()->status == IndentMarker::INVALID) + 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()) + if (InFlowContext()) { return; + } // now pop away while (!m_indents.empty()) { const IndentMarker& indent = *m_indents.top(); - if (indent.type == IndentMarker::NONE) + 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(); @@ -358,23 +361,20 @@ void Scanner::PopIndent() { return; } - if (indent.type == IndentMarker::SEQ) + if (indent.type == IndentMarker::SEQ) { m_tokens.push(Token(Token::BLOCK_SEQ_END, INPUT.mark())); - else if (indent.type == IndentMarker::MAP) + } 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()) + if (m_indents.empty()) { return 0; + } return m_indents.top()->column; } -// 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_mark(); if (!m_tokens.empty()) { @@ -383,4 +383,4 @@ void Scanner::ThrowParserException(const std::string& msg) const { } throw ParserException(mark, msg); } -} +} // namespace YAML diff --git a/src/scanner.h b/src/scanner.h index b0ac6d9..7bb2ccc 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -24,15 +24,24 @@ namespace YAML { class Node; class RegEx; +/** + * A scanner transforms a stream of characters into a stream of tokens. + */ class Scanner { public: - Scanner(std::istream &in); + explicit Scanner(std::istream &in); ~Scanner(); - // token queue management (hopefully this looks kinda stl-ish) + /** Returns true if there are no more tokens to be read. */ bool empty(); + + /** Removes the next token in the queue. */ void pop(); + + /** Returns, but does not remove, the next token in the queue. */ Token &peek(); + + /** Returns the current mark in the input stream. */ Mark mark() const; private: @@ -52,11 +61,29 @@ class Scanner { private: // scanning + + /** + * Scans until there's a valid token at the front of the queue, or the queue + * is empty. The state can be checked by {@link #empty}, and the next token + * retrieved by {@link #peek}. + */ void EnsureTokensInQueue(); + + /** + * The main scanning function; this method branches out to scan whatever the + * next token should be. + */ void ScanNextToken(); + + /** Eats the input stream until it reaches the next token-like thing. */ void ScanToNextToken(); + + /** Sets the initial conditions for starting a stream. */ void StartStream(); + + /** Closes out the stream, finish up, etc. */ void EndStream(); + Token *PushToken(Token::TYPE type); bool InFlowContext() const { return !m_flows.empty(); } @@ -64,9 +91,29 @@ class Scanner { std::size_t GetFlowLevel() const { return m_flows.size(); } Token::TYPE GetStartTokenFor(IndentMarker::INDENT_TYPE type) const; + + /** + * Pushes an indentation onto the stack, and enqueues the proper token + * (sequence start or mapping start). + * + * @return the indent marker it generates (if any). + */ IndentMarker *PushIndentTo(int column, IndentMarker::INDENT_TYPE type); + + /** + * Pops indentations off the stack until it reaches the current indentation + * level, and enqueues the proper token each time. Then pops all invalid + * indentations off. + */ void PopIndentToHere(); + + /** + * Pops all indentations (except for the base empty one) off the stack, and + * enqueues the proper token each time. + */ void PopAllIndents(); + + /** Pops a single indent, pushing the proper token. */ void PopIndent(); int GetTopIndent() const; @@ -78,9 +125,17 @@ class Scanner { bool VerifySimpleKey(); void PopAllSimpleKeys(); + /** + * Throws a ParserException with the current token location (if available), + * and does not parse any more tokens. + */ void ThrowParserException(const std::string &msg) const; bool IsWhitespaceToBeEaten(char ch); + + /** + * Returns the appropriate regex to check if the next token is a value token. + */ const RegEx &GetValueRegex() const; struct SimpleKey { From 6c569e58b0ca46dcbf7bbef821b1f4da8918f8fb Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Thu, 12 May 2016 23:13:55 -0500 Subject: [PATCH 35/37] Update docs, formatting for Parser. --- include/yaml-cpp/parser.h | 42 +++++++++++++++++++++++++++++++-- src/parser.cpp | 49 ++++++++++++++++++++------------------- 2 files changed, 65 insertions(+), 26 deletions(-) diff --git a/include/yaml-cpp/parser.h b/include/yaml-cpp/parser.h index edbfd8f..0e8a73e 100644 --- a/include/yaml-cpp/parser.h +++ b/include/yaml-cpp/parser.h @@ -20,23 +20,61 @@ class Scanner; struct Directives; struct Token; +/** + * A parser turns a stream of bytes into one stream of "events" per YAML + * document in the input stream. + */ class YAML_CPP_API Parser : private noncopyable { public: + /** Constructs an empty parser (with no input. */ Parser(); - Parser(std::istream& in); + + /** + * Constructs a parser from the given input stream. The input stream must + * live as long as the parser. + */ + explicit Parser(std::istream& in); + ~Parser(); - operator bool() const; + /** Evaluates to true if the parser has some valid input to be read. */ + explicit operator bool() const; + /** + * Resets the parser with the given input stream. Any existing state is + * erased. + */ void Load(std::istream& in); + + /** + * Handles the next document by calling events on the {@param eventHandler}. + * + * @throw a ParserException on error. + * @return false if there are no more documents + */ bool HandleNextDocument(EventHandler& eventHandler); void PrintTokens(std::ostream& out); private: + /** + * Reads any directives that are next in the queue, setting the internal + * {@code m_pDirectives} state. + */ void ParseDirectives(); + void HandleDirective(const Token& token); + + /** + * Handles a "YAML" directive, which should be of the form 'major.minor' (like + * a version number). + */ void HandleYamlDirective(const Token& token); + + /** + * Handles a "TAG" directive, which should be of the form 'handle prefix', + * where 'handle' is converted to 'prefix' in the file. + */ void HandleTagDirective(const Token& token); private: diff --git a/src/parser.cpp b/src/parser.cpp index 538d0be..cd69f39 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -26,40 +26,38 @@ void Parser::Load(std::istream& in) { m_pDirectives.reset(new Directives); } -// HandleNextDocument -// . Handles the next document -// . Throws a ParserException on error. -// . Returns false if there are no more documents bool Parser::HandleNextDocument(EventHandler& eventHandler) { if (!m_pScanner.get()) return false; ParseDirectives(); - if (m_pScanner->empty()) + if (m_pScanner->empty()) { return false; + } SingleDocParser sdp(*m_pScanner, *m_pDirectives); sdp.HandleDocument(eventHandler); return true; } -// ParseDirectives -// . Reads any directives that are next in the queue. void Parser::ParseDirectives() { bool readDirective = false; while (1) { - if (m_pScanner->empty()) + if (m_pScanner->empty()) { break; + } Token& token = m_pScanner->peek(); - if (token.type != Token::DIRECTIVE) + 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) + if (!readDirective) { m_pDirectives.reset(new Directives); + } readDirective = true; HandleDirective(token); @@ -68,58 +66,61 @@ void Parser::ParseDirectives() { } void Parser::HandleDirective(const Token& token) { - if (token.value == "YAML") + if (token.value == "YAML") { HandleYamlDirective(token); - else if (token.value == "TAG") + } 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) + if (token.params.size() != 1) { throw ParserException(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS); + } - if (!m_pDirectives->version.isDefault) + if (!m_pDirectives->version.isDefault) { throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE); + } std::stringstream str(token.params[0]); str >> m_pDirectives->version.major; str.get(); str >> m_pDirectives->version.minor; - if (!str || str.peek() != EOF) + if (!str || str.peek() != EOF) { throw ParserException( token.mark, std::string(ErrorMsg::YAML_VERSION) + token.params[0]); + } - if (m_pDirectives->version.major > 1) + if (m_pDirectives->version.major > 1) { throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION); + } m_pDirectives->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_pDirectives->tags.find(handle) != m_pDirectives->tags.end()) + if (m_pDirectives->tags.find(handle) != m_pDirectives->tags.end()) { throw ParserException(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE); + } m_pDirectives->tags[handle] = prefix; } void Parser::PrintTokens(std::ostream& out) { - if (!m_pScanner.get()) + if (!m_pScanner.get()) { return; + } while (1) { - if (m_pScanner->empty()) + if (m_pScanner->empty()) { break; + } out << m_pScanner->peek() << "\n"; m_pScanner->pop(); From 3392ab980e38b2d737decd3410dfe886e0ec3b47 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Thu, 12 May 2016 23:20:03 -0500 Subject: [PATCH 36/37] Update doc, formatting for parse.h. --- include/yaml-cpp/node/parse.h | 50 ++++++++++++++++++++++++++++++++++- src/parse.cpp | 14 ++++++---- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/include/yaml-cpp/node/parse.h b/include/yaml-cpp/node/parse.h index 0ea4948..7745fd7 100644 --- a/include/yaml-cpp/node/parse.h +++ b/include/yaml-cpp/node/parse.h @@ -16,15 +16,63 @@ namespace YAML { class Node; +/** + * Loads the input string as a single YAML document. + * + * @throws {@link ParserException} if it is malformed. + */ YAML_CPP_API Node Load(const std::string& input); + +/** + * Loads the input string as a single YAML document. + * + * @throws {@link ParserException} if it is malformed. + */ YAML_CPP_API Node Load(const char* input); + +/** + * Loads the input stream as a single YAML document. + * + * @throws {@link ParserException} if it is malformed. + */ YAML_CPP_API Node Load(std::istream& input); + +/** + * Loads the input file as a single YAML document. + * + * @throws {@link ParserException} if it is malformed. + * @throws {@link BadFile} if the file cannot be loaded. + */ YAML_CPP_API Node LoadFile(const std::string& filename); +/** + * Loads the input string as a list of YAML documents. + * + * @throws {@link ParserException} if it is malformed. + */ YAML_CPP_API std::vector LoadAll(const std::string& input); + +/** + * Loads the input string as a list of YAML documents. + * + * @throws {@link ParserException} if it is malformed. + */ YAML_CPP_API std::vector LoadAll(const char* input); + +/** + * Loads the input stream as a list of YAML documents. + * + * @throws {@link ParserException} if it is malformed. + */ YAML_CPP_API std::vector LoadAll(std::istream& input); + +/** + * Loads the input file as a list of YAML documents. + * + * @throws {@link ParserException} if it is malformed. + * @throws {@link BadFile} if the file cannot be loaded. + */ YAML_CPP_API std::vector LoadAllFromFile(const std::string& filename); -} +} // namespace YAML #endif // VALUE_PARSE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/parse.cpp b/src/parse.cpp index 1ef474d..0b2ae4a 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -22,16 +22,18 @@ Node Load(const char* input) { Node Load(std::istream& input) { Parser parser(input); NodeBuilder builder; - if (!parser.HandleNextDocument(builder)) + if (!parser.HandleNextDocument(builder)) { return Node(); + } return builder.Root(); } Node LoadFile(const std::string& filename) { std::ifstream fin(filename.c_str()); - if (!fin) + if (!fin) { throw BadFile(); + } return Load(fin); } @@ -51,8 +53,9 @@ std::vector LoadAll(std::istream& input) { Parser parser(input); while (1) { NodeBuilder builder; - if (!parser.HandleNextDocument(builder)) + if (!parser.HandleNextDocument(builder)) { break; + } docs.push_back(builder.Root()); } @@ -61,8 +64,9 @@ std::vector LoadAll(std::istream& input) { std::vector LoadAllFromFile(const std::string& filename) { std::ifstream fin(filename.c_str()); - if (!fin) + if (!fin) { throw BadFile(); + } return LoadAll(fin); } -} +} // namespace YAML From 728e26e42645d4d70ca65522990f915f47b47a50 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Thu, 12 May 2016 23:23:05 -0500 Subject: [PATCH 37/37] Update doc, formatting for emit.h. --- include/yaml-cpp/node/emit.h | 9 ++++++++- src/emit.cpp | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/yaml-cpp/node/emit.h b/include/yaml-cpp/node/emit.h index 7c55af2..9154b51 100644 --- a/include/yaml-cpp/node/emit.h +++ b/include/yaml-cpp/node/emit.h @@ -16,10 +16,17 @@ namespace YAML { class Emitter; class Node; +/** + * Emits the node to the given {@link Emitter}. If there is an error in writing, + * {@link Emitter#good} will return false. + */ YAML_CPP_API Emitter& operator<<(Emitter& out, const Node& node); + +/** Emits the node to the given output stream. */ YAML_CPP_API std::ostream& operator<<(std::ostream& out, const Node& node); +/** Converts the node to a YAML string. */ YAML_CPP_API std::string Dump(const Node& node); -} +} // namespace YAML #endif // NODE_EMIT_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/emit.cpp b/src/emit.cpp index 5fb593b..67f68a9 100644 --- a/src/emit.cpp +++ b/src/emit.cpp @@ -22,4 +22,4 @@ std::string Dump(const Node& node) { emitter << node; return emitter.c_str(); } -} +} // namespace YAML