Merge branch 'master' into doxygen-update

This commit is contained in:
theNerd247 2016-05-14 17:55:24 -04:00
commit c60d8596f5
47 changed files with 995 additions and 364 deletions

View File

@ -30,7 +30,7 @@ PenaltyReturnTypeOnItsOwnLine: 200
PointerBindsToType: true
SpacesBeforeTrailingComments: 2
Cpp11BracedListStyle: true
Standard: Auto
Standard: Cpp11
IndentWidth: 2
TabWidth: 8
UseTab: Never

50
.codedocs Normal file
View File

@ -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 =

23
.travis.yml Normal file
View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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.
*
* <p>Efficient implementation that can make assumptions about how
* {@code anchor_t} values are assigned by the {@link Parser} class.
*/
template <class T>
class AnchorDict {
public:

View File

@ -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

View File

@ -122,7 +122,7 @@ class YAML_CPP_API Emitter : private noncopyable {
bool CanEmitNewline() const;
private:
std::auto_ptr<EmitterState> m_pState;
std::unique_ptr<EmitterState> m_pState;
ostream_wrapper m_stream;
};

View File

@ -9,7 +9,7 @@
#include "yaml-cpp/node/detail/node.h"
#include "yaml-cpp/node/detail/node_data.h"
#include <boost/type_traits.hpp>
#include <type_traits>
namespace YAML {
namespace detail {
@ -22,9 +22,9 @@ struct get_idx {
};
template <typename Key>
struct get_idx<
Key, typename boost::enable_if_c<boost::is_unsigned<Key>::value &&
!boost::is_same<Key, bool>::value>::type> {
struct get_idx<Key,
typename std::enable_if<std::is_unsigned<Key>::value &&
!std::is_same<Key, bool>::value>::type> {
static node* get(const std::vector<node*>& sequence, const Key& key,
shared_memory_holder /* pMemory */) {
return key < sequence.size() ? sequence[key] : 0;
@ -41,7 +41,7 @@ struct get_idx<
};
template <typename Key>
struct get_idx<Key, typename boost::enable_if<boost::is_signed<Key> >::type> {
struct get_idx<Key, typename std::enable_if<std::is_signed<Key>::value>::type> {
static node* get(const std::vector<node*>& sequence, const Key& key,
shared_memory_holder pMemory) {
return key >= 0 ? get_idx<std::size_t>::get(

View File

@ -10,45 +10,68 @@
#include "yaml-cpp/dll.h"
#include "yaml-cpp/node/ptr.h"
#include "yaml-cpp/node/detail/node_iterator.h"
#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/utility.hpp>
#include <cstddef>
#include <iterator>
namespace YAML {
namespace detail {
struct iterator_value;
template <typename V>
class iterator_base
: public boost::iterator_adaptor<iterator_base<V>, node_iterator, V,
std::forward_iterator_tag, V> {
class iterator_base : public std::iterator<std::forward_iterator_tag, V,
std::ptrdiff_t, V*, V> {
private:
template <typename>
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 <class W>
iterator_base(const iterator_base<W>& rhs,
typename boost::enable_if<boost::is_convertible<W*, V*>,
enabler>::type = enabler())
: iterator_base::iterator_adaptor_(rhs.base()),
m_pMemory(rhs.m_pMemory) {}
typename std::enable_if<std::is_convertible<W*, V*>::value,
enabler>::type = enabler())
: m_iterator(rhs.m_iterator), m_pMemory(rhs.m_pMemory) {}
private:
friend class boost::iterator_core_access;
iterator_base<V>& operator++() {
++m_iterator;
return *this;
}
void increment() { this->base_reference() = boost::next(this->base()); }
iterator_base<V> operator++(int) {
iterator_base<V> iterator_pre(*this);
++(*this);
return iterator_pre;
}
value_type dereference() const {
const typename base_type::value_type& v = *this->base();
template <typename W>
bool operator==(const iterator_base<W>& rhs) {
return m_iterator == rhs.m_iterator;
}
template <typename W>
bool operator!=(const iterator_base<W>& 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;
};
}

View File

@ -13,7 +13,6 @@
#include <vector>
namespace YAML {
class node;
namespace detail {
struct iterator_value;

View File

@ -13,13 +13,14 @@
#include "yaml-cpp/node/ptr.h"
#include "yaml-cpp/node/detail/node_ref.h"
#include <set>
#include <boost/utility.hpp>
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)

View File

@ -7,8 +7,6 @@
#pragma once
#endif
#include <boost/noncopyable.hpp>
#include <boost/utility.hpp>
#include <list>
#include <map>
#include <string>
@ -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);

View File

@ -9,8 +9,9 @@
#include "yaml-cpp/dll.h"
#include "yaml-cpp/node/ptr.h"
#include <boost/iterator/iterator_facade.hpp>
#include <boost/utility/enable_if.hpp>
#include <cstddef>
#include <iterator>
#include <memory>
#include <map>
#include <utility>
#include <vector>
@ -52,12 +53,20 @@ struct node_iterator_type<const V> {
template <typename V>
class node_iterator_base
: public boost::iterator_facade<
node_iterator_base<V>, node_iterator_value<V>,
std::forward_iterator_tag, node_iterator_value<V> > {
: public std::iterator<std::forward_iterator_tag, node_iterator_value<V>,
std::ptrdiff_t, node_iterator_value<V>*,
node_iterator_value<V> > {
private:
struct enabler {};
struct proxy {
explicit proxy(const node_iterator_value<V>& x) : m_ref(x) {}
node_iterator_value<V>* operator->() { return std::addressof(m_ref); }
operator node_iterator_value<V>*() { return std::addressof(m_ref); }
node_iterator_value<V> m_ref;
};
public:
typedef typename node_iterator_type<V>::seq SeqIter;
typedef typename node_iterator_type<V>::map MapIter;
@ -80,20 +89,18 @@ class node_iterator_base
template <typename W>
node_iterator_base(const node_iterator_base<W>& rhs,
typename boost::enable_if<boost::is_convertible<W*, V*>,
enabler>::type = enabler())
typename std::enable_if<std::is_convertible<W*, V*>::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 <typename>
friend class node_iterator_base;
template <typename W>
bool equal(const node_iterator_base<W>& rhs) const {
bool operator==(const node_iterator_base<W>& rhs) const {
if (m_type != rhs.m_type)
return false;
@ -108,7 +115,12 @@ class node_iterator_base
return true;
}
void increment() {
template <typename W>
bool operator!=(const node_iterator_base<W>& rhs) const {
return !(*this == rhs);
}
node_iterator_base<V>& 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<V> operator++(int) {
node_iterator_base<V> 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;

View File

@ -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 <boost/utility.hpp>
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(); }

View File

@ -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

View File

@ -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<Node> 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<Node> 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<Node> 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<Node> LoadAllFromFile(const std::string& filename);
}
} // namespace YAML
#endif // VALUE_PARSE_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@ -8,7 +8,7 @@
#endif
#include "yaml-cpp/dll.h"
#include <boost/shared_ptr.hpp>
#include <memory>
namespace YAML {
namespace detail {
@ -18,11 +18,11 @@ class node_data;
class memory;
class memory_holder;
typedef boost::shared_ptr<node> shared_node;
typedef boost::shared_ptr<node_ref> shared_node_ref;
typedef boost::shared_ptr<node_data> shared_node_data;
typedef boost::shared_ptr<memory_holder> shared_memory_holder;
typedef boost::shared_ptr<memory> shared_memory;
typedef std::shared_ptr<node> shared_node;
typedef std::shared_ptr<node_ref> shared_node_ref;
typedef std::shared_ptr<node_data> shared_node_data;
typedef std::shared_ptr<memory_holder> shared_memory_holder;
typedef std::shared_ptr<memory> shared_memory;
}
}

View File

@ -8,6 +8,7 @@
#endif
#include "yaml-cpp/dll.h"
#include <string>
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;
}

View File

@ -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<Scanner> m_pScanner;
std::auto_ptr<Directives> m_pDirectives;
std::unique_ptr<Scanner> m_pScanner;
std::unique_ptr<Directives> m_pDirectives;
};
}

View File

@ -22,4 +22,4 @@ std::string Dump(const Node& node) {
emitter << node;
return emitter.c_str();
}
}
} // namespace YAML

View File

@ -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<Group> pGroup(new Group(type));
// TODO: Create move constructors for settings types to simplify transfer
std::unique_ptr<Group> 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<Group> pFinishedGroup = m_groups.pop();
if (pFinishedGroup->type != type)
std::unique_ptr<Group> 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(); }

View File

@ -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 <cassert>
#include <vector>
#include <stack>
#include <memory>
#include <stack>
#include <stdexcept>
#include <vector>
namespace YAML {
struct FmtScope {
@ -174,7 +174,7 @@ class EmitterState {
}
};
ptr_stack<Group> m_groups;
std::vector<std::unique_ptr<Group>> m_groups;
std::size_t m_curIndent;
bool m_hasAnchor;
bool m_hasTag;

View File

@ -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);

View File

@ -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;

View File

@ -1,5 +1,5 @@
#include <assert.h>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <iterator>
#include <sstream>
#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;

View File

@ -2,4 +2,8 @@
namespace YAML {
_Null Null;
bool IsNullString(const std::string& str) {
return str.empty() || str == "~" || str == "null" || str == "Null" || str == "NULL";
}
}

View File

@ -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<Node> 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<Node> LoadAll(std::istream& input) {
std::vector<Node> LoadAllFromFile(const std::string& filename) {
std::ifstream fin(filename.c_str());
if (!fin)
if (!fin) {
throw BadFile();
}
return LoadAll(fin);
}
}
} // namespace YAML

View File

@ -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();

View File

@ -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 <cstddef>
#include <cstdlib>
#include <memory>
#include <vector>
#include "yaml-cpp/noncopyable.h"
template <typename T>
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> t) {
m_data.push_back(NULL);
m_data.back() = t.release();
}
std::auto_ptr<T> pop() {
std::auto_ptr<T> 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<T*> m_data;
};
#endif // PTR_STACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@ -16,33 +16,35 @@
namespace YAML {
// TODO: This class is no longer needed
template <typename T>
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> t) {
m_data.push_back(NULL);
m_data.back() = t.release();
void push_back(std::unique_ptr<T>&& 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<T*> m_data;
std::vector<std::unique_ptr<T>> m_data;
};
}

View File

@ -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<IndentMarker> pIndent(new IndentMarker(-1, IndentMarker::NONE));
m_indentRefs.push_back(pIndent);
std::unique_ptr<IndentMarker> 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<IndentMarker> pIndent(new IndentMarker(column, type));
std::unique_ptr<IndentMarker> 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

View File

@ -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 {

View File

@ -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;

View File

@ -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?

View File

@ -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;

View File

@ -20,7 +20,7 @@ class Setting {
Setting() : m_value() {}
const T get() const { return m_value; }
std::auto_ptr<SettingChangeBase> set(const T& value);
std::unique_ptr<SettingChangeBase> set(const T& value);
void restore(const Setting<T>& oldSetting) { m_value = oldSetting.get(); }
private:
@ -49,8 +49,8 @@ class SettingChange : public SettingChangeBase {
};
template <typename T>
inline std::auto_ptr<SettingChangeBase> Setting<T>::set(const T& value) {
std::auto_ptr<SettingChangeBase> pChange(new SettingChange<T>(this));
inline std::unique_ptr<SettingChangeBase> Setting<T>::set(const T& value) {
std::unique_ptr<SettingChangeBase> pChange(new SettingChange<T>(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<SettingChangeBase> pSettingChange) {
m_settingChanges.push_back(pSettingChange.release());
void push(std::unique_ptr<SettingChangeBase> 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<SettingChangeBase*> setting_changes;
typedef std::vector<std::unique_ptr<SettingChangeBase>> setting_changes;
setting_changes m_settingChanges;
};
}

View File

@ -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;

View File

@ -53,7 +53,7 @@ class SingleDocParser : private noncopyable {
private:
Scanner& m_scanner;
const Directives& m_directives;
std::auto_ptr<CollectionStack> m_pCollectionStack;
std::unique_ptr<CollectionStack> m_pCollectionStack;
typedef std::map<std::string, anchor_t> Anchors;
Anchors m_anchors;

View File

@ -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)

View File

@ -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) {

View File

@ -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

View File

@ -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<ParserExceptionTestCase> 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

177
test/regex_test.cpp Normal file
View File

@ -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));
}
}

View File

@ -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")

View File

@ -1,3 +1,4 @@
#include <fstream>
#include <iostream>
#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<char>(in)),
std::istreambuf_iterator<char>());
}
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;
}