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 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/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d464896 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: c++ +os: + - linux + - osx +compiler: + - clang + - gcc +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" + fi + fi +before_script: + - mkdir build + - cd build + - cmake .. +script: + - make + - test/run-tests diff --git a/CMakeLists.txt b/CMakeLists.txt index 00626c7..acef57a 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() @@ -114,8 +114,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}) @@ -152,8 +150,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 @@ -173,13 +171,17 @@ 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) 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) @@ -258,16 +260,16 @@ else() set(_share_dir share) 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(SHARE_INSTALL_DIR "${_share_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}" ) @@ -303,7 +305,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} @@ -319,9 +321,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) 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 diff --git a/README.md b/README.md index 1c0399e..c597667 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) [![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). 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`. @@ -50,7 +49,7 @@ yaml-cpp uses [CMake](http://www.cmake.org) to support cross-platform building. # 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. 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/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 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/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; 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/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/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/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/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/include/yaml-cpp/parser.h b/include/yaml-cpp/parser.h index 24880e4..0e8a73e 100644 --- a/include/yaml-cpp/parser.h +++ b/include/yaml-cpp/parser.h @@ -20,28 +20,66 @@ 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: - std::auto_ptr m_pScanner; - std::auto_ptr m_pDirectives; + std::unique_ptr m_pScanner; + std::unique_ptr m_pDirectives; }; } 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 diff --git a/src/emitterstate.cpp b/src/emitterstate.cpp index a0874ac..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,41 +124,49 @@ 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; - 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) + if (GetFlowType(type) == Block) { pGroup->flowType = FlowType::Block; - else + } else { pGroup->flowType = FlowType::Flow; + } pGroup->indent = GetIndent(); - m_groups.push(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::auto_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; @@ -167,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/emitterutils.cpp b/src/emitterutils.cpp index 34da1bc..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; } @@ -375,14 +372,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/src/exp.h b/src/exp.h index f248802..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; @@ -165,6 +169,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/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/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/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 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(); diff --git a/src/ptr_stack.h b/src/ptr_stack.h deleted file mode 100644 index f378ffc..0000000 --- a/src/ptr_stack.h +++ /dev/null @@ -1,53 +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" - -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(); - } - std::auto_ptr pop() { - std::auto_ptr t(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); } - const T& top(std::ptrdiff_t diff) const { - return **(m_data.end() - 1 + diff); - } - - private: - 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..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::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()); } -// 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,84 +278,80 @@ 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::auto_ptr pIndent(new IndentMarker(column, type)); + 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)); // and then the indent m_indents.push(&indent); - m_indentRefs.push_back(pIndent); + m_indentRefs.push_back(std::move(pIndent)); 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 { diff --git a/src/scanscalar.cpp b/src/scanscalar.cpp index 8253b8d..10e359d 100644 --- a/src/scanscalar.cpp +++ b/src/scanscalar.cpp @@ -28,22 +28,28 @@ 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)) { - if (!INPUT) + while (!params.end->Matches(INPUT) && !Exp::Break().Matches(INPUT)) { + if (!INPUT) { break; + } // document indicator? if (INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) { - if (params.onDocIndicator == BREAK) + 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; @@ -70,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); + int n = params.end->Match(INPUT); if (n >= 0) { - if (params.eatEnd) + if (params.eatEnd) { INPUT.eat(n); + } break; } @@ -107,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); } @@ -143,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; } } @@ -182,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/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 180ad00..fd8758d 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; @@ -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; 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.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; 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..74455a5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,9 +8,15 @@ 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") + + 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) 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) { 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 diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp index 80236af..ff0a3ae 100644 --- a/test/integration/load_node_test.cpp +++ b/test/integration/load_node_test.cpp @@ -198,5 +198,38 @@ 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); + } + } } + +TEST(NodeTest, LoadTildeAsNull) { + Node node = Load("~"); + ASSERT_TRUE(node.IsNull()); } + +} // namespace +} // namespace YAML 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)); +} +} 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") diff --git a/util/read.cpp b/util/read.cpp index fabee1a..b1f787e 100644 --- a/util/read.cpp +++ b/util/read.cpp @@ -1,3 +1,4 @@ +#include #include #include "yaml-cpp/emitterstyle.h" @@ -25,9 +26,77 @@ class NullEventHandler : public YAML::EventHandler { virtual void OnMapEnd() {} }; -int main() { - YAML::Parser parser(std::cin); +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) { + 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 { + 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; }