mirror of
https://github.com/jbeder/yaml-cpp.git
synced 2025-09-09 12:41:17 +00:00
Compare commits
58 Commits
travis-upd
...
yaml-cpp-0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0579ae3d97 | ||
![]() |
9ee3928754 | ||
![]() |
a6bbe0e50a | ||
![]() |
98acc5a887 | ||
![]() |
1c9abc8fa4 | ||
![]() |
11917babc7 | ||
![]() |
c2793a36d5 | ||
![]() |
bc9874c9ff | ||
![]() |
06b99f53db | ||
![]() |
c3df6d87d4 | ||
![]() |
51ce663085 | ||
![]() |
33315286ab | ||
![]() |
4571e8177a | ||
![]() |
c82d3129dd | ||
![]() |
370aceeaf8 | ||
![]() |
6d5cfab5fd | ||
![]() |
026a53fbe1 | ||
![]() |
1c2e767347 | ||
![]() |
3f381f13a0 | ||
![]() |
08aa252611 | ||
![]() |
27d8a0e302 | ||
![]() |
97d1ae547c | ||
![]() |
5a9ab177ba | ||
![]() |
72fe73a104 | ||
![]() |
a7a7908dc5 | ||
![]() |
0b0bf35c48 | ||
![]() |
dbb385bdf5 | ||
![]() |
b2cd008717 | ||
![]() |
6387cbc0ca | ||
![]() |
1bf9540a7d | ||
![]() |
4c90f2962e | ||
![]() |
4f6d0733c9 | ||
![]() |
f05947ae59 | ||
![]() |
66171449d4 | ||
![]() |
33c9f6e062 | ||
![]() |
7e4f290ef9 | ||
![]() |
ae811c3d86 | ||
![]() |
41001d1bf9 | ||
![]() |
a808c1f44a | ||
![]() |
4dbfeb0bbc | ||
![]() |
33316d531b | ||
![]() |
6701275f19 | ||
![]() |
cb7bb9821d | ||
![]() |
f05b2577ad | ||
![]() |
d9c35b6079 | ||
![]() |
4b98aedc16 | ||
![]() |
a98b8af448 | ||
![]() |
9fb5153487 | ||
![]() |
513ee81fbf | ||
![]() |
9f2781b527 | ||
![]() |
7083db28b3 | ||
![]() |
2f00649291 | ||
![]() |
b35f2a86a6 | ||
![]() |
99222fc938 | ||
![]() |
4edff1fa5d | ||
![]() |
70205f5d88 | ||
![]() |
9b8f35093e | ||
![]() |
1d8542ad32 |
@@ -1,5 +1,12 @@
|
||||
# 3.5 is actually available almost everywhere, but this a good minimum
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
|
||||
# enable MSVC_RUNTIME_LIBRARY target property
|
||||
# see https://cmake.org/cmake/help/latest/policy/CMP0091.html
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif()
|
||||
|
||||
project(YAML_CPP VERSION 0.7.0 LANGUAGES CXX)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
@@ -34,10 +41,11 @@ endif()
|
||||
set(build-shared $<BOOL:${YAML_BUILD_SHARED_LIBS}>)
|
||||
set(build-windows-dll $<AND:$<BOOL:${CMAKE_HOST_WIN32}>,${build-shared}>)
|
||||
set(not-msvc $<NOT:$<CXX_COMPILER_ID:MSVC>>)
|
||||
set(msvc-shared_rt $<BOOL:${YAML_MSVC_SHARED_RT}>)
|
||||
|
||||
if (NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY
|
||||
MultiThreaded$<$<CONFIG:Debug>:Debug>$<${build-shared}:DLL>)
|
||||
MultiThreaded$<$<CONFIG:Debug>:Debug>$<${msvc-shared_rt}:DLL>)
|
||||
endif()
|
||||
|
||||
set(contrib-pattern "src/contrib/*.cpp")
|
||||
@@ -109,11 +117,15 @@ target_sources(yaml-cpp
|
||||
$<$<BOOL:${YAML_CPP_BUILD_CONTRIB}>:${yaml-cpp-contrib-sources}>
|
||||
${yaml-cpp-sources})
|
||||
|
||||
if (NOT DEFINED CMAKE_DEBUG_POSTFIX)
|
||||
set(CMAKE_DEBUG_POSTFIX "d")
|
||||
endif()
|
||||
|
||||
set_target_properties(yaml-cpp PROPERTIES
|
||||
VERSION "${PROJECT_VERSION}"
|
||||
SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
|
||||
PROJECT_LABEL "yaml-cpp ${yaml-cpp-label-postfix}"
|
||||
DEBUG_POSTFIX d)
|
||||
DEBUG_POSTFIX "${CMAKE_DEBUG_POSTFIX}")
|
||||
|
||||
configure_package_config_file(
|
||||
"${PROJECT_SOURCE_DIR}/yaml-cpp-config.cmake.in"
|
||||
@@ -156,6 +168,7 @@ endif()
|
||||
if (YAML_CPP_CLANG_FORMAT_EXE)
|
||||
add_custom_target(format
|
||||
COMMAND clang-format --style=file -i $<TARGET_PROPERTY:yaml-cpp,SOURCES>
|
||||
COMMAND_EXPAND_LISTS
|
||||
COMMENT "Running clang-format"
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
@@ -53,3 +53,9 @@ cmake [-G generator] [-DYAML_BUILD_SHARED_LIBS=ON|OFF] ..
|
||||
# API Documentation
|
||||
|
||||
The autogenerated API reference is hosted on [CodeDocs](https://codedocs.xyz/jbeder/yaml-cpp/index.html)
|
||||
|
||||
# Third Party Integrations
|
||||
|
||||
The following projects are not officially supported:
|
||||
|
||||
- [Qt wrapper](https://gist.github.com/brcha/d392b2fe5f1e427cc8a6)
|
||||
|
28
appveyor.yml
Normal file
28
appveyor.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CMAKE_GENERATOR: Visual Studio 14 2015
|
||||
CMAKE_PLATFORM: win32
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CMAKE_GENERATOR: Visual Studio 14 2015
|
||||
CMAKE_PLATFORM: x64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 2017
|
||||
CMAKE_PLATFORM: win32
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 2017
|
||||
CMAKE_PLATFORM: x64
|
||||
|
||||
before_build:
|
||||
- cmd: mkdir build
|
||||
- cmd: cd build
|
||||
- cmd: cmake .. -G "%CMAKE_GENERATOR%" -DCMAKE_GENERATOR_PLATFORM=%CMAKE_PLATFORM%
|
||||
- cmd: cd ..
|
||||
|
||||
build_script:
|
||||
- cmake --build build
|
||||
test_script:
|
||||
- cmd: cd build
|
||||
- ctest
|
52
docs/Breaking-Changes.md
Normal file
52
docs/Breaking-Changes.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# The following is a list of breaking changes to yaml-cpp, by version #
|
||||
|
||||
# New API #
|
||||
|
||||
## HEAD ##
|
||||
|
||||
* Throws an exception when trying to parse a negative number as an unsigned integer.
|
||||
* Supports the `as<int8_t>`/`as<uint8_t>`, which throws an exception when the value exceeds the range of `int8_t`/`uint8_t`.
|
||||
|
||||
## 0.6.0 ##
|
||||
|
||||
* Requires C++11.
|
||||
|
||||
## 0.5.3 ##
|
||||
|
||||
_none_
|
||||
|
||||
## 0.5.2 ##
|
||||
|
||||
_none_
|
||||
|
||||
## 0.5.1 ##
|
||||
|
||||
* `Node::clear` was replaced by `Node::reset`, which takes an optional node, similar to smart pointers.
|
||||
|
||||
## 0.5.0 ##
|
||||
|
||||
Initial version of the new API.
|
||||
|
||||
# Old API #
|
||||
|
||||
## 0.3.0 ##
|
||||
|
||||
_none_
|
||||
|
||||
## 0.2.7 ##
|
||||
|
||||
* `YAML::Binary` now takes `const unsigned char *` for the binary data (instead of `const char *`).
|
||||
|
||||
## 0.2.6 ##
|
||||
|
||||
* `Node::GetType()` is now `Node::Type()`, and returns an enum `NodeType::value`, where:
|
||||
> > ` struct NodeType { enum value { Null, Scalar, Sequence, Map }; }; `
|
||||
* `Node::GetTag()` is now `Node::Tag()`
|
||||
* `Node::Identity()` is removed, and `Node::IsAlias()` and `Node::IsReferenced()` have been merged into `Node::IsAliased()`. The reason: there's no reason to distinguish an alias node from its anchor - whichever happens to be emitted first will be the anchor, and the rest will be aliases.
|
||||
* `Node::Read<T>` is now `Node::to<T>`. This wasn't a documented function, so it shouldn't break anything.
|
||||
* `Node`'s comparison operators (for example, `operator == (const Node&, const T&)`) have all been removed. These weren't documented either (they were just used for the tests), so this shouldn't break anything either.
|
||||
* The emitter no longer produces the document start by default - if you want it, you can supply it with the manipulator `YAML::BeginDoc`.
|
||||
|
||||
## 0.2.5 ##
|
||||
|
||||
This wiki was started with v0.2.5.
|
230
docs/How-To-Emit-YAML.md
Normal file
230
docs/How-To-Emit-YAML.md
Normal file
@@ -0,0 +1,230 @@
|
||||
## Contents ##
|
||||
|
||||
|
||||
|
||||
# Basic Emitting #
|
||||
|
||||
The model for emitting YAML is `std::ostream` manipulators. A `YAML::Emitter` objects acts as an output stream, and its output can be retrieved through the `c_str()` function (as in `std::string`). For a simple example:
|
||||
|
||||
```cpp
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
YAML::Emitter out;
|
||||
out << "Hello, World!";
|
||||
|
||||
std::cout << "Here's the output YAML:\n" << out.c_str(); // prints "Hello, World!"
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
# Simple Lists and Maps #
|
||||
|
||||
A `YAML::Emitter` object acts as a state machine, and we use manipulators to move it between states. Here's a simple sequence:
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginSeq;
|
||||
out << "eggs";
|
||||
out << "bread";
|
||||
out << "milk";
|
||||
out << YAML::EndSeq;
|
||||
```
|
||||
|
||||
produces
|
||||
|
||||
```yaml
|
||||
- eggs
|
||||
- bread
|
||||
- milk
|
||||
```
|
||||
|
||||
A simple map:
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "name";
|
||||
out << YAML::Value << "Ryan Braun";
|
||||
out << YAML::Key << "position";
|
||||
out << YAML::Value << "LF";
|
||||
out << YAML::EndMap;
|
||||
```
|
||||
|
||||
produces
|
||||
|
||||
```yaml
|
||||
name: Ryan Braun
|
||||
position: LF
|
||||
```
|
||||
|
||||
These elements can, of course, be nested:
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "name";
|
||||
out << YAML::Value << "Barack Obama";
|
||||
out << YAML::Key << "children";
|
||||
out << YAML::Value << YAML::BeginSeq << "Sasha" << "Malia" << YAML::EndSeq;
|
||||
out << YAML::EndMap;
|
||||
```
|
||||
|
||||
produces
|
||||
|
||||
```yaml
|
||||
name: Barack Obama
|
||||
children:
|
||||
- Sasha
|
||||
- Malia
|
||||
```
|
||||
|
||||
# Using Manipulators #
|
||||
|
||||
To deviate from standard formatting, you can use manipulators to modify the output format. For example,
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
out << YAML::Literal << "A\n B\n C";
|
||||
```
|
||||
|
||||
produces
|
||||
|
||||
```yaml
|
||||
|
|
||||
A
|
||||
B
|
||||
C
|
||||
```
|
||||
and
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
out << YAML::Flow;
|
||||
out << YAML::BeginSeq << 2 << 3 << 5 << 7 << 11 << YAML::EndSeq;
|
||||
```
|
||||
|
||||
produces
|
||||
|
||||
```yaml
|
||||
[2, 3, 5, 7, 11]
|
||||
```
|
||||
|
||||
Comments act like manipulators:
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "method";
|
||||
out << YAML::Value << "least squares";
|
||||
out << YAML::Comment("should we change this method?");
|
||||
out << YAML::EndMap;
|
||||
```
|
||||
|
||||
produces
|
||||
|
||||
```yaml
|
||||
method: least squares # should we change this method?
|
||||
```
|
||||
|
||||
And so do aliases/anchors:
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginSeq;
|
||||
out << YAML::Anchor("fred");
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "name" << YAML::Value << "Fred";
|
||||
out << YAML::Key << "age" << YAML::Value << "42";
|
||||
out << YAML::EndMap;
|
||||
out << YAML::Alias("fred");
|
||||
out << YAML::EndSeq;
|
||||
```
|
||||
|
||||
produces
|
||||
|
||||
```yaml
|
||||
- &fred
|
||||
name: Fred
|
||||
age: 42
|
||||
- *fred
|
||||
```
|
||||
|
||||
# STL Containers, and Other Overloads #
|
||||
We overload `operator <<` for `std::vector`, `std::list`, and `std::map`, so you can write stuff like:
|
||||
|
||||
```cpp
|
||||
std::vector <int> squares;
|
||||
squares.push_back(1);
|
||||
squares.push_back(4);
|
||||
squares.push_back(9);
|
||||
squares.push_back(16);
|
||||
|
||||
std::map <std::string, int> ages;
|
||||
ages["Daniel"] = 26;
|
||||
ages["Jesse"] = 24;
|
||||
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginSeq;
|
||||
out << YAML::Flow << squares;
|
||||
out << ages;
|
||||
out << YAML::EndSeq;
|
||||
```
|
||||
|
||||
produces
|
||||
|
||||
```yaml
|
||||
- [1, 4, 9, 16]
|
||||
-
|
||||
Daniel: 26
|
||||
Jesse: 24
|
||||
```
|
||||
|
||||
Of course, you can overload `operator <<` for your own types:
|
||||
|
||||
```cpp
|
||||
struct Vec3 { int x; int y; int z; };
|
||||
YAML::Emitter& operator << (YAML::Emitter& out, const Vec3& v) {
|
||||
out << YAML::Flow;
|
||||
out << YAML::BeginSeq << v.x << v.y << v.z << YAML::EndSeq;
|
||||
return out;
|
||||
}
|
||||
```
|
||||
and it'll play nicely with everything else.
|
||||
|
||||
# Using Existing Nodes #
|
||||
|
||||
We also overload `operator << ` for `YAML::Node`s in both APIs, so you can output existing Nodes. Of course, Nodes in the old API are read-only, so it's tricky to emit them if you want to modify them. So use the new API!
|
||||
|
||||
# Output Encoding #
|
||||
|
||||
The output is always UTF-8. By default, yaml-cpp will output as much as it can without escaping any characters. If you want to restrict the output to ASCII, use the manipulator `YAML::EscapeNonAscii`:
|
||||
|
||||
```cpp
|
||||
emitter.SetOutputCharset(YAML::EscapeNonAscii);
|
||||
```
|
||||
|
||||
# Lifetime of Manipulators #
|
||||
|
||||
Manipulators affect the **next** output item in the stream. If that item is a `BeginSeq` or `BeginMap`, the manipulator lasts until the corresponding `EndSeq` or `EndMap`. (However, within that sequence or map, you can override the manipulator locally, etc.; in effect, there's a "manipulator stack" behind the scenes.)
|
||||
|
||||
If you want to permanently change a setting, there are global setters corresponding to each manipulator, e.g.:
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
out.SetIndent(4);
|
||||
out.SetMapStyle(YAML::Flow);
|
||||
```
|
||||
|
||||
# When Something Goes Wrong #
|
||||
|
||||
If something goes wrong when you're emitting a document, it must be something like forgetting a `YAML::EndSeq`, or a misplaced `YAML::Key`. In this case, emitting silently fails (no more output is emitted) and an error flag is set. For example:
|
||||
|
||||
```cpp
|
||||
YAML::Emitter out;
|
||||
assert(out.good());
|
||||
out << YAML::Key;
|
||||
assert(!out.good());
|
||||
std::cout << "Emitter error: " << out.GetLastError() << "\n";
|
||||
```
|
265
docs/How-To-Parse-A-Document-(Old-API).md
Normal file
265
docs/How-To-Parse-A-Document-(Old-API).md
Normal file
@@ -0,0 +1,265 @@
|
||||
_The following describes the old API. For the new API, see the [Tutorial](https://github.com/jbeder/yaml-cpp/wiki/Tutorial)._
|
||||
|
||||
## Contents ##
|
||||
|
||||
|
||||
# Basic Parsing #
|
||||
|
||||
The parser accepts streams, not file names, so you need to first load the file. Since a YAML file can contain many documents, you can grab them one-by-one. A simple way to parse a YAML file might be:
|
||||
|
||||
```
|
||||
#include <fstream>
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
std::ifstream fin("test.yaml");
|
||||
YAML::Parser parser(fin);
|
||||
|
||||
YAML::Node doc;
|
||||
while(parser.GetNextDocument(doc)) {
|
||||
// ...
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
# Reading From the Document #
|
||||
|
||||
Suppose we have a document consisting only of a scalar. We can read that scalar like this:
|
||||
|
||||
```
|
||||
YAML::Node doc; // let's say we've already parsed this document
|
||||
std::string scalar;
|
||||
doc >> scalar;
|
||||
std::cout << "That scalar was: " << scalar << std::endl;
|
||||
```
|
||||
|
||||
How about sequences? Let's say our document now consists only of a sequences of scalars. We can use an iterator:
|
||||
|
||||
```
|
||||
YAML::Node doc; // already parsed
|
||||
for(YAML::Iterator it=doc.begin();it!=doc.end();++it) {
|
||||
std::string scalar;
|
||||
*it >> scalar;
|
||||
std::cout << "Found scalar: " << scalar << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
... or we can just loop through:
|
||||
|
||||
```
|
||||
YAML::Node doc; // already parsed
|
||||
for(unsigned i=0;i<doc.size();i++) {
|
||||
std::string scalar;
|
||||
doc[i] >> scalar;
|
||||
std::cout << "Found scalar: " << scalar << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
And finally maps. For now, let's say our document is a map with all keys/values being scalars. Again, we can iterate:
|
||||
|
||||
```
|
||||
YAML::Node doc; // already parsed
|
||||
for(YAML::Iterator it=doc.begin();it!=doc.end();++it) {
|
||||
std::string key, value;
|
||||
it.first() >> key;
|
||||
it.second() >> value;
|
||||
std::cout << "Key: " << key << ", value: " << value << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
Note that dereferencing a map iterator is undefined; instead, use the `first` and `second` methods to get the key and value nodes, respectively.
|
||||
|
||||
Alternatively, we can pick off the values one-by-one, if we know the keys:
|
||||
|
||||
```
|
||||
YAML::Node doc; // already parsed
|
||||
std::string name;
|
||||
doc["name"] >> name;
|
||||
int age;
|
||||
doc["age"] >> age;
|
||||
std::cout << "Found entry with name '" << name << "' and age '" << age << "'\n";
|
||||
```
|
||||
|
||||
One thing to be keep in mind: reading a map by key (as immediately above) requires looping through all entries until we find the right key, which is an O(n) operation. So if you're reading the entire map this way, it'll be O(n^2). For small n, this isn't a big deal, but I wouldn't recommend reading maps with a very large number of entries (>100, say) this way.
|
||||
|
||||
## Optional Keys ##
|
||||
|
||||
If you try to access a key that doesn't exist, `yaml-cpp` throws an exception (see [When Something Goes Wrong](https://github.com/jbeder/yaml-cpp/wiki/How-To-Parse-A-Document-(Old-API)#When_Something_Goes_Wrong). If you have optional keys, it's often easier to use `FindValue` instead of `operator[]`:
|
||||
|
||||
```
|
||||
YAML::Node doc; // already parsed
|
||||
if(const YAML::Node *pName = doc.FindValue("name")) {
|
||||
std::string name;
|
||||
*pName >> name;
|
||||
std::cout << "Key 'name' exists, with value '" << name << "'\n";
|
||||
} else {
|
||||
std::cout << "Key 'name' doesn't exist\n";
|
||||
}
|
||||
```
|
||||
|
||||
# Getting More Complicated #
|
||||
|
||||
The above three methods can be combined to read from an arbitrary document. But we can make life a lot easier. Suppose we're reading 3-vectors (i.e., vectors with three components), so we've got a structure looking like this:
|
||||
|
||||
```
|
||||
struct Vec3 {
|
||||
float x, y, z;
|
||||
};
|
||||
```
|
||||
|
||||
We can read this in one operation by overloading the extraction (>>) operator:
|
||||
|
||||
```
|
||||
void operator >> (const YAML::Node& node, Vec3& v)
|
||||
{
|
||||
node[0] >> v.x;
|
||||
node[1] >> v.y;
|
||||
node[2] >> v.z;
|
||||
}
|
||||
|
||||
// now it's a piece of cake to read it
|
||||
YAML::Node doc; // already parsed
|
||||
Vec3 v;
|
||||
doc >> v;
|
||||
std::cout << "Here's the vector: (" << v.x << ", " << v.y << ", " << v.z << ")\n";
|
||||
```
|
||||
|
||||
# A Complete Example #
|
||||
|
||||
Here's a complete example of how to parse a complex YAML file:
|
||||
|
||||
`monsters.yaml`
|
||||
|
||||
```
|
||||
- name: Ogre
|
||||
position: [0, 5, 0]
|
||||
powers:
|
||||
- name: Club
|
||||
damage: 10
|
||||
- name: Fist
|
||||
damage: 8
|
||||
- name: Dragon
|
||||
position: [1, 0, 10]
|
||||
powers:
|
||||
- name: Fire Breath
|
||||
damage: 25
|
||||
- name: Claws
|
||||
damage: 15
|
||||
- name: Wizard
|
||||
position: [5, -3, 0]
|
||||
powers:
|
||||
- name: Acid Rain
|
||||
damage: 50
|
||||
- name: Staff
|
||||
damage: 3
|
||||
```
|
||||
|
||||
`main.cpp`
|
||||
|
||||
```
|
||||
#include "yaml-cpp/yaml.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// our data types
|
||||
struct Vec3 {
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
struct Power {
|
||||
std::string name;
|
||||
int damage;
|
||||
};
|
||||
|
||||
struct Monster {
|
||||
std::string name;
|
||||
Vec3 position;
|
||||
std::vector <Power> powers;
|
||||
};
|
||||
|
||||
// now the extraction operators for these types
|
||||
void operator >> (const YAML::Node& node, Vec3& v) {
|
||||
node[0] >> v.x;
|
||||
node[1] >> v.y;
|
||||
node[2] >> v.z;
|
||||
}
|
||||
|
||||
void operator >> (const YAML::Node& node, Power& power) {
|
||||
node["name"] >> power.name;
|
||||
node["damage"] >> power.damage;
|
||||
}
|
||||
|
||||
void operator >> (const YAML::Node& node, Monster& monster) {
|
||||
node["name"] >> monster.name;
|
||||
node["position"] >> monster.position;
|
||||
const YAML::Node& powers = node["powers"];
|
||||
for(unsigned i=0;i<powers.size();i++) {
|
||||
Power power;
|
||||
powers[i] >> power;
|
||||
monster.powers.push_back(power);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::ifstream fin("monsters.yaml");
|
||||
YAML::Parser parser(fin);
|
||||
YAML::Node doc;
|
||||
parser.GetNextDocument(doc);
|
||||
for(unsigned i=0;i<doc.size();i++) {
|
||||
Monster monster;
|
||||
doc[i] >> monster;
|
||||
std::cout << monster.name << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
# When Something Goes Wrong #
|
||||
|
||||
... we throw an exception (all exceptions are derived from `YAML::Exception`). If there's a parsing exception (i.e., a malformed YAML document), we throw a `YAML::ParserException`:
|
||||
|
||||
```
|
||||
try {
|
||||
std::ifstream fin("test.yaml");
|
||||
YAML::Parser parser(fin);
|
||||
YAML::Node doc;
|
||||
parser.GetNextDocument(doc);
|
||||
// do stuff
|
||||
} catch(YAML::ParserException& e) {
|
||||
std::cout << e.what() << "\n";
|
||||
}
|
||||
```
|
||||
|
||||
If you make a programming error (say, trying to read a scalar from a sequence node, or grabbing a key that doesn't exist), we throw some kind of `YAML::RepresentationException`. To prevent this, you can check what kind of node something is:
|
||||
|
||||
```
|
||||
YAML::Node node;
|
||||
YAML::NodeType::value type = node.Type(); // should be:
|
||||
// YAML::NodeType::Null
|
||||
// YAML::NodeType::Scalar
|
||||
// YAML::NodeType::Sequence
|
||||
// YAML::NodeType::Map
|
||||
```
|
||||
|
||||
# Note about copying `YAML::Node` #
|
||||
|
||||
Currently `YAML::Node` is non-copyable, so you need to do something like
|
||||
|
||||
```
|
||||
const YAML::Node& node = doc["whatever"];
|
||||
```
|
||||
|
||||
This is intended behavior. If you want to copy a node, use the `Clone` function:
|
||||
|
||||
```
|
||||
std::auto_ptr<YAML::Node> pCopy = myOtherNode.Clone();
|
||||
```
|
||||
|
||||
The intent is that if you'd like to keep a `YAML::Node` around for longer than the document will stay in scope, you can clone it and store it as long as you like.
|
18
docs/Strings.md
Normal file
18
docs/Strings.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Encodings and `yaml-cpp` #
|
||||
|
||||
`yaml-cpp` will parse any file as specified by the [YAML 1.2 spec](http://www.yaml.org/spec/1.2/spec.html#id2570322). Internally, it stores all strings in UTF-8, and representation is done with UTF-8. This means that in
|
||||
|
||||
```
|
||||
std::string str;
|
||||
node >> str;
|
||||
```
|
||||
|
||||
`str` will be UTF-8. Similarly, if you're accessing a map by string key, you need to pass the key in UTF-8. If your application uses a different encoding, you need to convert to and from UTF-8 to work with `yaml-cpp`. (It's possible we'll add some small conversion functions, but for now it's restricted.)
|
||||
|
||||
---
|
||||
|
||||
For convenience, Richard Weeks has kindly provided a google gadget that converts Unicode to a string literal. It's a Google Gadget, so unfortunately it does not work on GitHub. Patches welcome to port it to a usable format here:
|
||||
|
||||
```
|
||||
<wiki:gadget url="http://hosting.gmodules.com/ig/gadgets/file/111180078345548400783/c-style-utf8-encoder.xml"/>
|
||||
```
|
201
docs/Tutorial.md
Normal file
201
docs/Tutorial.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Introduction #
|
||||
|
||||
A typical example, loading a configuration file, might look like this:
|
||||
|
||||
```cpp
|
||||
YAML::Node config = YAML::LoadFile("config.yaml");
|
||||
|
||||
if (config["lastLogin"]) {
|
||||
std::cout << "Last logged in: " << config["lastLogin"].as<DateTime>() << "\n";
|
||||
}
|
||||
|
||||
const std::string username = config["username"].as<std::string>();
|
||||
const std::string password = config["password"].as<std::string>();
|
||||
login(username, password);
|
||||
config["lastLogin"] = getCurrentDateTime();
|
||||
|
||||
std::ofstream fout("config.yaml");
|
||||
fout << config;
|
||||
```
|
||||
|
||||
# Basic Parsing and Node Editing #
|
||||
|
||||
All nodes in a YAML document (including the root) are represented by `YAML::Node`. You can check what kind it is:
|
||||
|
||||
```cpp
|
||||
YAML::Node node = YAML::Load("[1, 2, 3]");
|
||||
assert(node.Type() == YAML::NodeType::Sequence);
|
||||
assert(node.IsSequence()); // a shortcut!
|
||||
```
|
||||
|
||||
Collection nodes (sequences and maps) act somewhat like STL vectors and maps:
|
||||
|
||||
```cpp
|
||||
YAML::Node primes = YAML::Load("[2, 3, 5, 7, 11]");
|
||||
for (std::size_t i=0;i<primes.size();i++) {
|
||||
std::cout << primes[i].as<int>() << "\n";
|
||||
}
|
||||
// or:
|
||||
for (YAML::const_iterator it=primes.begin();it!=primes.end();++it) {
|
||||
std::cout << it->as<int>() << "\n";
|
||||
}
|
||||
|
||||
primes.push_back(13);
|
||||
assert(primes.size() == 6);
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```cpp
|
||||
YAML::Node lineup = YAML::Load("{1B: Prince Fielder, 2B: Rickie Weeks, LF: Ryan Braun}");
|
||||
for(YAML::const_iterator it=lineup.begin();it!=lineup.end();++it) {
|
||||
std::cout << "Playing at " << it->first.as<std::string>() << " is " << it->second.as<std::string>() << "\n";
|
||||
}
|
||||
|
||||
lineup["RF"] = "Corey Hart";
|
||||
lineup["C"] = "Jonathan Lucroy";
|
||||
assert(lineup.size() == 5);
|
||||
```
|
||||
|
||||
Querying for keys does **not** create them automatically (this makes handling optional map entries very easy)
|
||||
|
||||
```cpp
|
||||
YAML::Node node = YAML::Load("{name: Brewers, city: Milwaukee}");
|
||||
if (node["name"]) {
|
||||
std::cout << node["name"].as<std::string>() << "\n";
|
||||
}
|
||||
if (node["mascot"]) {
|
||||
std::cout << node["mascot"].as<std::string>() << "\n";
|
||||
}
|
||||
assert(node.size() == 2); // the previous call didn't create a node
|
||||
```
|
||||
|
||||
If you're not sure what kind of data you're getting, you can query the type of a node:
|
||||
|
||||
```cpp
|
||||
switch (node.Type()) {
|
||||
case Null: // ...
|
||||
case Scalar: // ...
|
||||
case Sequence: // ...
|
||||
case Map: // ...
|
||||
case Undefined: // ...
|
||||
}
|
||||
```
|
||||
|
||||
or ask directly whether it's a particular type, e.g.:
|
||||
|
||||
```cpp
|
||||
if (node.IsSequence()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
# Building Nodes #
|
||||
|
||||
You can build `YAML::Node` from scratch:
|
||||
|
||||
```cpp
|
||||
YAML::Node node; // starts out as null
|
||||
node["key"] = "value"; // it now is a map node
|
||||
node["seq"].push_back("first element"); // node["seq"] automatically becomes a sequence
|
||||
node["seq"].push_back("second element");
|
||||
|
||||
node["mirror"] = node["seq"][0]; // this creates an alias
|
||||
node["seq"][0] = "1st element"; // this also changes node["mirror"]
|
||||
node["mirror"] = "element #1"; // and this changes node["seq"][0] - they're really the "same" node
|
||||
|
||||
node["self"] = node; // you can even create self-aliases
|
||||
node[node["mirror"]] = node["seq"]; // and strange loops :)
|
||||
```
|
||||
|
||||
The above node is now:
|
||||
|
||||
```yaml
|
||||
&1
|
||||
key: value
|
||||
&2 seq: [&3 "element #1", second element]
|
||||
mirror: *3
|
||||
self: *1
|
||||
*3 : *2
|
||||
```
|
||||
|
||||
# How Sequences Turn Into Maps #
|
||||
|
||||
Sequences can be turned into maps by asking for non-integer keys. For example,
|
||||
|
||||
```cpp
|
||||
YAML::Node node = YAML::Load("[1, 2, 3]");
|
||||
node[1] = 5; // still a sequence, [1, 5, 3]
|
||||
node.push_back(-3) // still a sequence, [1, 5, 3, -3]
|
||||
node["key"] = "value"; // now it's a map! {0: 1, 1: 5, 2: 3, 3: -3, key: value}
|
||||
```
|
||||
|
||||
Indexing a sequence node by an index that's not in its range will _usually_ turn it into a map, but if the index is one past the end of the sequence, then the sequence will grow by one to accommodate it. (That's the **only** exception to this rule.) For example,
|
||||
|
||||
```cpp
|
||||
YAML::Node node = YAML::Load("[1, 2, 3]");
|
||||
node[3] = 4; // still a sequence, [1, 2, 3, 4]
|
||||
node[10] = 10; // now it's a map! {0: 1, 1: 2, 2: 3, 3: 4, 10: 10}
|
||||
```
|
||||
|
||||
# Converting To/From Native Data Types #
|
||||
|
||||
Yaml-cpp has built-in conversion to and from most built-in data types, as well as `std::vector`, `std::list`, and `std::map`. The following examples demonstrate when those conversions are used:
|
||||
|
||||
```cpp
|
||||
YAML::Node node = YAML::Load("{pi: 3.14159, [0, 1]: integers}");
|
||||
|
||||
// this needs the conversion from Node to double
|
||||
double pi = node["pi"].as<double>();
|
||||
|
||||
// this needs the conversion from double to Node
|
||||
node["e"] = 2.71828;
|
||||
|
||||
// this needs the conversion from Node to std::vector<int> (*not* the other way around!)
|
||||
std::vector<int> v;
|
||||
v.push_back(0);
|
||||
v.push_back(1);
|
||||
std::string str = node[v].as<std::string>();
|
||||
```
|
||||
|
||||
To use yaml-cpp with your own data types, you need to specialize the YAML::convert<> template class. For example, suppose you had a simple `Vec3` class:
|
||||
|
||||
```cpp
|
||||
struct Vec3 { double x, y, z; /* etc - make sure you have overloaded operator== */ };
|
||||
```
|
||||
|
||||
You could write
|
||||
|
||||
```cpp
|
||||
namespace YAML {
|
||||
template<>
|
||||
struct convert<Vec3> {
|
||||
static Node encode(const Vec3& rhs) {
|
||||
Node node;
|
||||
node.push_back(rhs.x);
|
||||
node.push_back(rhs.y);
|
||||
node.push_back(rhs.z);
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, Vec3& rhs) {
|
||||
if(!node.IsSequence() || node.size() != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rhs.x = node[0].as<double>();
|
||||
rhs.y = node[1].as<double>();
|
||||
rhs.z = node[2].as<double>();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Then you could use `Vec3` wherever you could use any other type:
|
||||
|
||||
```cpp
|
||||
YAML::Node node = YAML::Load("start: [1, 3, 0]");
|
||||
Vec3 v = node["start"].as<Vec3>();
|
||||
node["end"] = Vec3(2, -1, 0);
|
||||
```
|
1
docs/_config.yml
Normal file
1
docs/_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-slate
|
1
docs/index.md
Normal file
1
docs/index.md
Normal file
@@ -0,0 +1 @@
|
||||
To learn how to use the library, see the [Tutorial](https://github.com/jbeder/yaml-cpp/wiki/Tutorial) and [How To Emit YAML](https://github.com/jbeder/yaml-cpp/wiki/How-To-Emit-YAML)
|
77
include/yaml-cpp/depthguard.h
Normal file
77
include/yaml-cpp/depthguard.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000
|
||||
#define DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000
|
||||
|
||||
#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 "exceptions.h"
|
||||
|
||||
namespace YAML {
|
||||
|
||||
/**
|
||||
* @brief The DeepRecursion class
|
||||
* An exception class which is thrown by DepthGuard. Ideally it should be
|
||||
* a member of DepthGuard. However, DepthGuard is a templated class which means
|
||||
* that any catch points would then need to know the template parameters. It is
|
||||
* simpler for clients to not have to know at the catch point what was the
|
||||
* maximum depth.
|
||||
*/
|
||||
class DeepRecursion : public ParserException {
|
||||
public:
|
||||
virtual ~DeepRecursion() = default;
|
||||
|
||||
DeepRecursion(int depth, const Mark& mark_, const std::string& msg_);
|
||||
|
||||
// Returns the recursion depth when the exception was thrown
|
||||
int depth() const {
|
||||
return m_depth;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_depth = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The DepthGuard class
|
||||
* DepthGuard takes a reference to an integer. It increments the integer upon
|
||||
* construction of DepthGuard and decrements the integer upon destruction.
|
||||
*
|
||||
* If the integer would be incremented past max_depth, then an exception is
|
||||
* thrown. This is ideally geared toward guarding against deep recursion.
|
||||
*
|
||||
* @param max_depth
|
||||
* compile-time configurable maximum depth.
|
||||
*/
|
||||
template <int max_depth = 2000>
|
||||
class DepthGuard final {
|
||||
public:
|
||||
DepthGuard(int & depth_, const Mark& mark_, const std::string& msg_) : m_depth(depth_) {
|
||||
++m_depth;
|
||||
if ( max_depth <= m_depth ) {
|
||||
throw DeepRecursion{m_depth, mark_, msg_};
|
||||
}
|
||||
}
|
||||
|
||||
DepthGuard(const DepthGuard & copy_ctor) = delete;
|
||||
DepthGuard(DepthGuard && move_ctor) = delete;
|
||||
DepthGuard & operator=(const DepthGuard & copy_assign) = delete;
|
||||
DepthGuard & operator=(DepthGuard && move_assign) = delete;
|
||||
|
||||
~DepthGuard() {
|
||||
--m_depth;
|
||||
}
|
||||
|
||||
int current_depth() const {
|
||||
return m_depth;
|
||||
}
|
||||
|
||||
private:
|
||||
int & m_depth;
|
||||
};
|
||||
|
||||
} // namespace YAML
|
||||
|
||||
#endif // DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000
|
@@ -50,6 +50,7 @@ class YAML_CPP_API Emitter {
|
||||
bool SetOutputCharset(EMITTER_MANIP value);
|
||||
bool SetStringFormat(EMITTER_MANIP value);
|
||||
bool SetBoolFormat(EMITTER_MANIP value);
|
||||
bool SetNullFormat(EMITTER_MANIP value);
|
||||
bool SetIntBase(EMITTER_MANIP value);
|
||||
bool SetSeqFormat(EMITTER_MANIP value);
|
||||
bool SetMapFormat(EMITTER_MANIP value);
|
||||
@@ -58,6 +59,7 @@ class YAML_CPP_API Emitter {
|
||||
bool SetPostCommentIndent(std::size_t n);
|
||||
bool SetFloatPrecision(std::size_t n);
|
||||
bool SetDoublePrecision(std::size_t n);
|
||||
void RestoreGlobalModifiedSettings();
|
||||
|
||||
// local setters
|
||||
Emitter& SetLocalValue(EMITTER_MANIP value);
|
||||
@@ -123,6 +125,7 @@ class YAML_CPP_API Emitter {
|
||||
void SpaceOrIndentTo(bool requireSpace, std::size_t indent);
|
||||
|
||||
const char* ComputeFullBoolName(bool b) const;
|
||||
const char* ComputeNullName() const;
|
||||
bool CanEmitNewline() const;
|
||||
|
||||
private:
|
||||
|
@@ -19,6 +19,7 @@ enum EMITTER_MANIP {
|
||||
// output character set
|
||||
EmitNonAscii,
|
||||
EscapeNonAscii,
|
||||
EscapeAsJson,
|
||||
|
||||
// string manipulators
|
||||
// Auto, // duplicate
|
||||
@@ -26,6 +27,12 @@ enum EMITTER_MANIP {
|
||||
DoubleQuoted,
|
||||
Literal,
|
||||
|
||||
// null manipulators
|
||||
LowerNull,
|
||||
UpperNull,
|
||||
CamelNull,
|
||||
TildeNull,
|
||||
|
||||
// bool manipulators
|
||||
YesNoBool, // yes, no
|
||||
TrueFalseBool, // true, false
|
||||
|
@@ -100,6 +100,12 @@ inline const std::string KEY_NOT_FOUND_WITH_KEY(const std::string& key) {
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
inline const std::string KEY_NOT_FOUND_WITH_KEY(const char* key) {
|
||||
std::stringstream stream;
|
||||
stream << KEY_NOT_FOUND << ": " << key;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const std::string KEY_NOT_FOUND_WITH_KEY(
|
||||
const T& key, typename enable_if<is_numeric<T>>::type* = 0) {
|
||||
@@ -120,6 +126,12 @@ inline const std::string BAD_SUBSCRIPT_WITH_KEY(const std::string& key) {
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
inline const std::string BAD_SUBSCRIPT_WITH_KEY(const char* key) {
|
||||
std::stringstream stream;
|
||||
stream << BAD_SUBSCRIPT << " (key: \"" << key << "\")";
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const std::string BAD_SUBSCRIPT_WITH_KEY(
|
||||
const T& key, typename enable_if<is_numeric<T>>::type* = nullptr) {
|
||||
@@ -136,7 +148,7 @@ inline const std::string INVALID_NODE_WITH_KEY(const std::string& key) {
|
||||
stream << "invalid node; first invalid key: \"" << key << "\"";
|
||||
return stream.str();
|
||||
}
|
||||
}
|
||||
} // namespace ErrorMsg
|
||||
|
||||
class YAML_CPP_API Exception : public std::runtime_error {
|
||||
public:
|
||||
@@ -249,8 +261,7 @@ class YAML_CPP_API BadSubscript : public RepresentationException {
|
||||
public:
|
||||
template <typename Key>
|
||||
BadSubscript(const Mark& mark_, const Key& key)
|
||||
: RepresentationException(mark_,
|
||||
ErrorMsg::BAD_SUBSCRIPT_WITH_KEY(key)) {}
|
||||
: RepresentationException(mark_, ErrorMsg::BAD_SUBSCRIPT_WITH_KEY(key)) {}
|
||||
BadSubscript(const BadSubscript&) = default;
|
||||
~BadSubscript() YAML_CPP_NOEXCEPT override;
|
||||
};
|
||||
@@ -281,10 +292,12 @@ class YAML_CPP_API EmitterException : public Exception {
|
||||
|
||||
class YAML_CPP_API BadFile : public Exception {
|
||||
public:
|
||||
BadFile() : Exception(Mark::null_mark(), ErrorMsg::BAD_FILE) {}
|
||||
explicit BadFile(const std::string& filename)
|
||||
: Exception(Mark::null_mark(),
|
||||
std::string(ErrorMsg::BAD_FILE) + ": " + filename) {}
|
||||
BadFile(const BadFile&) = default;
|
||||
~BadFile() YAML_CPP_NOEXCEPT override;
|
||||
};
|
||||
}
|
||||
} // namespace YAML
|
||||
|
||||
#endif // EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
|
||||
|
@@ -74,12 +74,17 @@ struct convert<std::string> {
|
||||
// C-strings can only be encoded
|
||||
template <>
|
||||
struct convert<const char*> {
|
||||
static Node encode(const char*& rhs) { return Node(rhs); }
|
||||
static Node encode(const char* rhs) { return Node(rhs); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct convert<char*> {
|
||||
static Node encode(const char* rhs) { return Node(rhs); }
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
struct convert<const char[N]> {
|
||||
static Node encode(const char(&rhs)[N]) { return Node(rhs); }
|
||||
struct convert<char[N]> {
|
||||
static Node encode(const char* rhs) { return Node(rhs); }
|
||||
};
|
||||
|
||||
template <>
|
||||
@@ -113,6 +118,31 @@ typename std::enable_if<!std::is_floating_point<T>::value, void>::type
|
||||
inner_encode(const T& rhs, std::stringstream& stream){
|
||||
stream << rhs;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<(std::is_same<T, unsigned char>::value ||
|
||||
std::is_same<T, signed char>::value), bool>::type
|
||||
ConvertStreamTo(std::stringstream& stream, T& rhs) {
|
||||
int num;
|
||||
if ((stream >> std::noskipws >> num) && (stream >> std::ws).eof()) {
|
||||
if (num >= (std::numeric_limits<T>::min)() &&
|
||||
num <= (std::numeric_limits<T>::max)()) {
|
||||
rhs = (T)num;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!(std::is_same<T, unsigned char>::value ||
|
||||
std::is_same<T, signed char>::value), bool>::type
|
||||
ConvertStreamTo(std::stringstream& stream, T& rhs) {
|
||||
if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#define YAML_DEFINE_CONVERT_STREAMABLE(type, negative_op) \
|
||||
@@ -133,7 +163,10 @@ inner_encode(const T& rhs, std::stringstream& stream){
|
||||
const std::string& input = node.Scalar(); \
|
||||
std::stringstream stream(input); \
|
||||
stream.unsetf(std::ios::dec); \
|
||||
if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) { \
|
||||
if ((stream.peek() == '-') && std::is_unsigned<type>::value) { \
|
||||
return false; \
|
||||
} \
|
||||
if (conversion::ConvertStreamTo(stream, rhs)) { \
|
||||
return true; \
|
||||
} \
|
||||
if (std::numeric_limits<type>::has_infinity) { \
|
||||
@@ -193,81 +226,78 @@ struct convert<bool> {
|
||||
};
|
||||
|
||||
// std::map
|
||||
template <typename K, typename V>
|
||||
struct convert<std::map<K, V>> {
|
||||
static Node encode(const std::map<K, V>& rhs) {
|
||||
template <typename K, typename V, typename C, typename A>
|
||||
struct convert<std::map<K, V, C, A>> {
|
||||
static Node encode(const std::map<K, V, C, A>& rhs) {
|
||||
Node node(NodeType::Map);
|
||||
for (typename std::map<K, V>::const_iterator it = rhs.begin();
|
||||
it != rhs.end(); ++it)
|
||||
node.force_insert(it->first, it->second);
|
||||
for (const auto& element : rhs)
|
||||
node.force_insert(element.first, element.second);
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, std::map<K, V>& rhs) {
|
||||
static bool decode(const Node& node, std::map<K, V, C, A>& rhs) {
|
||||
if (!node.IsMap())
|
||||
return false;
|
||||
|
||||
rhs.clear();
|
||||
for (const_iterator it = node.begin(); it != node.end(); ++it)
|
||||
for (const auto& element : node)
|
||||
#if defined(__GNUC__) && __GNUC__ < 4
|
||||
// workaround for GCC 3:
|
||||
rhs[it->first.template as<K>()] = it->second.template as<V>();
|
||||
rhs[element.first.template as<K>()] = element.second.template as<V>();
|
||||
#else
|
||||
rhs[it->first.as<K>()] = it->second.as<V>();
|
||||
rhs[element.first.as<K>()] = element.second.as<V>();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// std::vector
|
||||
template <typename T>
|
||||
struct convert<std::vector<T>> {
|
||||
static Node encode(const std::vector<T>& rhs) {
|
||||
template <typename T, typename A>
|
||||
struct convert<std::vector<T, A>> {
|
||||
static Node encode(const std::vector<T, A>& rhs) {
|
||||
Node node(NodeType::Sequence);
|
||||
for (typename std::vector<T>::const_iterator it = rhs.begin();
|
||||
it != rhs.end(); ++it)
|
||||
node.push_back(*it);
|
||||
for (const auto& element : rhs)
|
||||
node.push_back(element);
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, std::vector<T>& rhs) {
|
||||
static bool decode(const Node& node, std::vector<T, A>& rhs) {
|
||||
if (!node.IsSequence())
|
||||
return false;
|
||||
|
||||
rhs.clear();
|
||||
for (const_iterator it = node.begin(); it != node.end(); ++it)
|
||||
for (const auto& element : node)
|
||||
#if defined(__GNUC__) && __GNUC__ < 4
|
||||
// workaround for GCC 3:
|
||||
rhs.push_back(it->template as<T>());
|
||||
rhs.push_back(element.template as<T>());
|
||||
#else
|
||||
rhs.push_back(it->as<T>());
|
||||
rhs.push_back(element.as<T>());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// std::list
|
||||
template <typename T>
|
||||
struct convert<std::list<T>> {
|
||||
static Node encode(const std::list<T>& rhs) {
|
||||
template <typename T, typename A>
|
||||
struct convert<std::list<T,A>> {
|
||||
static Node encode(const std::list<T,A>& rhs) {
|
||||
Node node(NodeType::Sequence);
|
||||
for (typename std::list<T>::const_iterator it = rhs.begin();
|
||||
it != rhs.end(); ++it)
|
||||
node.push_back(*it);
|
||||
for (const auto& element : rhs)
|
||||
node.push_back(element);
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, std::list<T>& rhs) {
|
||||
static bool decode(const Node& node, std::list<T,A>& rhs) {
|
||||
if (!node.IsSequence())
|
||||
return false;
|
||||
|
||||
rhs.clear();
|
||||
for (const_iterator it = node.begin(); it != node.end(); ++it)
|
||||
for (const auto& element : node)
|
||||
#if defined(__GNUC__) && __GNUC__ < 4
|
||||
// workaround for GCC 3:
|
||||
rhs.push_back(it->template as<T>());
|
||||
rhs.push_back(element.template as<T>());
|
||||
#else
|
||||
rhs.push_back(it->as<T>());
|
||||
rhs.push_back(element.as<T>());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "yaml-cpp/node/detail/node.h"
|
||||
#include "yaml-cpp/node/detail/node_data.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
namespace YAML {
|
||||
@@ -104,7 +106,11 @@ inline bool node::equals(const T& rhs, shared_memory_holder pMemory) {
|
||||
}
|
||||
|
||||
inline bool node::equals(const char* rhs, shared_memory_holder pMemory) {
|
||||
return equals<std::string>(rhs, pMemory);
|
||||
std::string lhs;
|
||||
if (convert<std::string>::decode(Node(*this, std::move(pMemory)), lhs)) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// indexing
|
||||
@@ -125,13 +131,11 @@ inline node* node_data::get(const Key& key,
|
||||
throw BadSubscript(m_mark, key);
|
||||
}
|
||||
|
||||
for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
if (it->first->equals(key, pMemory)) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) {
|
||||
return m.first->equals(key, pMemory);
|
||||
});
|
||||
|
||||
return nullptr;
|
||||
return it != m_map.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
@@ -153,10 +157,12 @@ inline node& node_data::get(const Key& key, shared_memory_holder pMemory) {
|
||||
throw BadSubscript(m_mark, key);
|
||||
}
|
||||
|
||||
for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
if (it->first->equals(key, pMemory)) {
|
||||
return *it->second;
|
||||
}
|
||||
auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) {
|
||||
return m.first->equals(key, pMemory);
|
||||
});
|
||||
|
||||
if (it != m_map.end()) {
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
node& k = convert_to_node(key, pMemory);
|
||||
@@ -169,7 +175,9 @@ template <typename Key>
|
||||
inline bool node_data::remove(const Key& key, shared_memory_holder pMemory) {
|
||||
if (m_type == NodeType::Sequence) {
|
||||
return remove_idx<Key>::remove(m_sequence, key, m_seqSize);
|
||||
} else if (m_type == NodeType::Map) {
|
||||
}
|
||||
|
||||
if (m_type == NodeType::Map) {
|
||||
kv_pairs::iterator it = m_undefinedPairs.begin();
|
||||
while (it != m_undefinedPairs.end()) {
|
||||
kv_pairs::iterator jt = std::next(it);
|
||||
@@ -179,11 +187,13 @@ inline bool node_data::remove(const Key& key, shared_memory_holder pMemory) {
|
||||
it = jt;
|
||||
}
|
||||
|
||||
for (node_map::iterator iter = m_map.begin(); iter != m_map.end(); ++iter) {
|
||||
if (iter->first->equals(key, pMemory)) {
|
||||
m_map.erase(iter);
|
||||
return true;
|
||||
}
|
||||
auto iter = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) {
|
||||
return m.first->equals(key, pMemory);
|
||||
});
|
||||
|
||||
if (iter != m_map.end()) {
|
||||
m_map.erase(iter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,11 +20,11 @@ namespace detail {
|
||||
class node {
|
||||
private:
|
||||
struct less {
|
||||
bool operator ()(const node* l, const node* r) {return l->m_index < r->m_index;}
|
||||
bool operator ()(const node* l, const node* r) const {return l->m_index < r->m_index;}
|
||||
};
|
||||
|
||||
public:
|
||||
node() : m_pRef(new node_ref), m_dependencies{} {}
|
||||
node() : m_pRef(new node_ref), m_dependencies{}, m_index{} {}
|
||||
node(const node&) = delete;
|
||||
node& operator=(const node&) = delete;
|
||||
|
||||
|
@@ -60,8 +60,8 @@ class YAML_CPP_API node_data {
|
||||
node_iterator end();
|
||||
|
||||
// sequence
|
||||
void push_back(node& node, shared_memory_holder pMemory);
|
||||
void insert(node& key, node& value, shared_memory_holder pMemory);
|
||||
void push_back(node& node, const shared_memory_holder& pMemory);
|
||||
void insert(node& key, node& value, const shared_memory_holder& pMemory);
|
||||
|
||||
// indexing
|
||||
template <typename Key>
|
||||
@@ -71,9 +71,9 @@ class YAML_CPP_API node_data {
|
||||
template <typename Key>
|
||||
bool remove(const Key& key, shared_memory_holder pMemory);
|
||||
|
||||
node* get(node& key, shared_memory_holder pMemory) const;
|
||||
node& get(node& key, shared_memory_holder pMemory);
|
||||
bool remove(node& key, shared_memory_holder pMemory);
|
||||
node* get(node& key, const shared_memory_holder& pMemory) const;
|
||||
node& get(node& key, const shared_memory_holder& pMemory);
|
||||
bool remove(node& key, const shared_memory_holder& pMemory);
|
||||
|
||||
// map
|
||||
template <typename Key, typename Value>
|
||||
@@ -91,8 +91,8 @@ class YAML_CPP_API node_data {
|
||||
void reset_map();
|
||||
|
||||
void insert_map_pair(node& key, node& value);
|
||||
void convert_to_map(shared_memory_holder pMemory);
|
||||
void convert_sequence_to_map(shared_memory_holder pMemory);
|
||||
void convert_to_map(const shared_memory_holder& pMemory);
|
||||
void convert_sequence_to_map(const shared_memory_holder& pMemory);
|
||||
|
||||
template <typename T>
|
||||
static node& convert_to_node(const T& rhs, shared_memory_holder pMemory);
|
||||
|
@@ -110,6 +110,8 @@ struct as_if<std::string, S> {
|
||||
const Node& node;
|
||||
|
||||
std::string operator()(const S& fallback) const {
|
||||
if (node.Type() == NodeType::Null)
|
||||
return "null";
|
||||
if (node.Type() != NodeType::Scalar)
|
||||
return fallback;
|
||||
return node.Scalar();
|
||||
@@ -138,6 +140,8 @@ struct as_if<std::string, void> {
|
||||
const Node& node;
|
||||
|
||||
std::string operator()() const {
|
||||
if (node.Type() == NodeType::Null)
|
||||
return "null";
|
||||
if (node.Type() != NodeType::Scalar)
|
||||
throw TypedBadConversion<std::string>(node.Mark());
|
||||
return node.Scalar();
|
||||
@@ -311,51 +315,6 @@ inline void Node::push_back(const Node& rhs) {
|
||||
m_pMemory->merge(*rhs.m_pMemory);
|
||||
}
|
||||
|
||||
// helpers for indexing
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct to_value_t {
|
||||
explicit to_value_t(const T& t_) : t(t_) {}
|
||||
const T& t;
|
||||
using return_type = const T &;
|
||||
|
||||
const T& operator()() const { return t; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct to_value_t<const char*> {
|
||||
explicit to_value_t(const char* t_) : t(t_) {}
|
||||
const char* t;
|
||||
using return_type = std::string;
|
||||
|
||||
const std::string operator()() const { return t; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct to_value_t<char*> {
|
||||
explicit to_value_t(char* t_) : t(t_) {}
|
||||
const char* t;
|
||||
using return_type = std::string;
|
||||
|
||||
const std::string operator()() const { return t; }
|
||||
};
|
||||
|
||||
template <std::size_t N>
|
||||
struct to_value_t<char[N]> {
|
||||
explicit to_value_t(const char* t_) : t(t_) {}
|
||||
const char* t;
|
||||
using return_type = std::string;
|
||||
|
||||
const std::string operator()() const { return t; }
|
||||
};
|
||||
|
||||
// converts C-strings to std::strings so they can be copied
|
||||
template <typename T>
|
||||
inline typename to_value_t<T>::return_type to_value(const T& t) {
|
||||
return to_value_t<T>(t)();
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template<typename Key>
|
||||
std::string key_to_string(const Key& key) {
|
||||
return streamable_to_string<Key, is_streamable<std::stringstream, Key>::value>().impl(key);
|
||||
@@ -365,8 +324,8 @@ std::string key_to_string(const Key& key) {
|
||||
template <typename Key>
|
||||
inline const Node Node::operator[](const Key& key) const {
|
||||
EnsureNodeExists();
|
||||
detail::node* value = static_cast<const detail::node&>(*m_pNode).get(
|
||||
detail::to_value(key), m_pMemory);
|
||||
detail::node* value =
|
||||
static_cast<const detail::node&>(*m_pNode).get(key, m_pMemory);
|
||||
if (!value) {
|
||||
return Node(ZombieNode, key_to_string(key));
|
||||
}
|
||||
@@ -376,14 +335,14 @@ inline const Node Node::operator[](const Key& key) const {
|
||||
template <typename Key>
|
||||
inline Node Node::operator[](const Key& key) {
|
||||
EnsureNodeExists();
|
||||
detail::node& value = m_pNode->get(detail::to_value(key), m_pMemory);
|
||||
detail::node& value = m_pNode->get(key, m_pMemory);
|
||||
return Node(value, m_pMemory);
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
inline bool Node::remove(const Key& key) {
|
||||
EnsureNodeExists();
|
||||
return m_pNode->remove(detail::to_value(key), m_pMemory);
|
||||
return m_pNode->remove(key, m_pMemory);
|
||||
}
|
||||
|
||||
inline const Node Node::operator[](const Node& key) const {
|
||||
@@ -416,8 +375,7 @@ inline bool Node::remove(const Node& key) {
|
||||
template <typename Key, typename Value>
|
||||
inline void Node::force_insert(const Key& key, const Value& value) {
|
||||
EnsureNodeExists();
|
||||
m_pNode->force_insert(detail::to_value(key), detail::to_value(value),
|
||||
m_pMemory);
|
||||
m_pNode->force_insert(key, value, m_pMemory);
|
||||
}
|
||||
|
||||
// free functions
|
||||
|
@@ -16,8 +16,8 @@ namespace YAML {
|
||||
template <typename Seq>
|
||||
inline Emitter& EmitSeq(Emitter& emitter, const Seq& seq) {
|
||||
emitter << BeginSeq;
|
||||
for (typename Seq::const_iterator it = seq.begin(); it != seq.end(); ++it)
|
||||
emitter << *it;
|
||||
for (const auto& v : seq)
|
||||
emitter << v;
|
||||
emitter << EndSeq;
|
||||
return emitter;
|
||||
}
|
||||
@@ -39,10 +39,9 @@ inline Emitter& operator<<(Emitter& emitter, const std::set<T>& v) {
|
||||
|
||||
template <typename K, typename V>
|
||||
inline Emitter& operator<<(Emitter& emitter, const std::map<K, V>& m) {
|
||||
typedef typename std::map<K, V> map;
|
||||
emitter << BeginMap;
|
||||
for (typename map::const_iterator it = m.begin(); it != m.end(); ++it)
|
||||
emitter << Key << it->first << Value << it->second;
|
||||
for (const auto& v : m)
|
||||
emitter << Key << v.first << Value << v.second;
|
||||
emitter << EndMap;
|
||||
return emitter;
|
||||
}
|
||||
|
@@ -97,4 +97,4 @@ std::vector<unsigned char> DecodeBase64(const std::string &input) {
|
||||
ret.resize(out - &ret[0]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -10,8 +10,7 @@ void* BuildGraphOfNextDocument(Parser& parser,
|
||||
GraphBuilderAdapter eventHandler(graphBuilder);
|
||||
if (parser.HandleNextDocument(eventHandler)) {
|
||||
return eventHandler.RootNode();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -91,4 +91,4 @@ void GraphBuilderAdapter::DispositionNode(void *pNode) {
|
||||
m_builder.AppendToSequence(pContainer, pNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -16,11 +16,7 @@ std::string tolower(const std::string& str) {
|
||||
|
||||
template <typename T>
|
||||
bool IsEntirely(const std::string& str, T func) {
|
||||
for (char ch : str)
|
||||
if (!func(ch))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return std::all_of(str.begin(), str.end(), [=](char ch) { return func(ch); });
|
||||
}
|
||||
|
||||
// IsFlexibleCase
|
||||
|
9
src/depthguard.cpp
Normal file
9
src/depthguard.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "yaml-cpp/depthguard.h"
|
||||
|
||||
namespace YAML {
|
||||
|
||||
DeepRecursion::DeepRecursion(int depth, const Mark& mark_,
|
||||
const std::string& msg_)
|
||||
: ParserException(mark_, msg_), m_depth(depth) {}
|
||||
|
||||
} // namespace YAML
|
@@ -5,7 +5,7 @@ Directives::Directives() : version{true, 1, 2}, tags{} {}
|
||||
|
||||
const std::string Directives::TranslateTagHandle(
|
||||
const std::string& handle) const {
|
||||
std::map<std::string, std::string>::const_iterator it = tags.find(handle);
|
||||
auto it = tags.find(handle);
|
||||
if (it == tags.end()) {
|
||||
if (handle == "!!")
|
||||
return "tag:yaml.org,2002:";
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#include "yaml-cpp/node/emit.h"
|
||||
#include "nodeevents.h"
|
||||
#include "yaml-cpp/emitfromevents.h"
|
||||
#include "yaml-cpp/emitter.h"
|
||||
#include "nodeevents.h"
|
||||
|
||||
namespace YAML {
|
||||
Emitter& operator<<(Emitter& out, const Node& node) {
|
||||
|
@@ -59,6 +59,8 @@ void EmitFromEvents::OnSequenceStart(const Mark&, const std::string& tag,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Restore the global settings to eliminate the override from node style
|
||||
m_emitter.RestoreGlobalModifiedSettings();
|
||||
m_emitter << BeginSeq;
|
||||
m_stateStack.push(State::WaitingForSequenceEntry);
|
||||
}
|
||||
@@ -83,6 +85,8 @@ void EmitFromEvents::OnMapStart(const Mark&, const std::string& tag,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Restore the global settings to eliminate the override from node style
|
||||
m_emitter.RestoreGlobalModifiedSettings();
|
||||
m_emitter << BeginMap;
|
||||
m_stateStack.push(State::WaitingForKey);
|
||||
}
|
||||
|
@@ -49,6 +49,10 @@ bool Emitter::SetBoolFormat(EMITTER_MANIP value) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Emitter::SetNullFormat(EMITTER_MANIP value) {
|
||||
return m_pState->SetNullFormat(value, FmtScope::Global);
|
||||
}
|
||||
|
||||
bool Emitter::SetIntBase(EMITTER_MANIP value) {
|
||||
return m_pState->SetIntFormat(value, FmtScope::Global);
|
||||
}
|
||||
@@ -86,6 +90,10 @@ bool Emitter::SetDoublePrecision(std::size_t n) {
|
||||
return m_pState->SetDoublePrecision(n, FmtScope::Global);
|
||||
}
|
||||
|
||||
void Emitter::RestoreGlobalModifiedSettings() {
|
||||
m_pState->RestoreGlobalModifiedSettings();
|
||||
}
|
||||
|
||||
// SetLocalValue
|
||||
// . Either start/end a group, or set a modifier locally
|
||||
Emitter& Emitter::SetLocalValue(EMITTER_MANIP value) {
|
||||
@@ -197,6 +205,7 @@ void Emitter::EmitBeginSeq() {
|
||||
void Emitter::EmitEndSeq() {
|
||||
if (!good())
|
||||
return;
|
||||
FlowType::value originalType = m_pState->CurGroupFlowType();
|
||||
|
||||
if (m_pState->CurGroupChildCount() == 0)
|
||||
m_pState->ForceFlow();
|
||||
@@ -205,8 +214,12 @@ void Emitter::EmitEndSeq() {
|
||||
if (m_stream.comment())
|
||||
m_stream << "\n";
|
||||
m_stream << IndentTo(m_pState->CurIndent());
|
||||
if (m_pState->CurGroupChildCount() == 0)
|
||||
if (originalType == FlowType::Block) {
|
||||
m_stream << "[";
|
||||
} else {
|
||||
if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode())
|
||||
m_stream << "[";
|
||||
}
|
||||
m_stream << "]";
|
||||
}
|
||||
|
||||
@@ -227,6 +240,7 @@ void Emitter::EmitBeginMap() {
|
||||
void Emitter::EmitEndMap() {
|
||||
if (!good())
|
||||
return;
|
||||
FlowType::value originalType = m_pState->CurGroupFlowType();
|
||||
|
||||
if (m_pState->CurGroupChildCount() == 0)
|
||||
m_pState->ForceFlow();
|
||||
@@ -235,8 +249,12 @@ void Emitter::EmitEndMap() {
|
||||
if (m_stream.comment())
|
||||
m_stream << "\n";
|
||||
m_stream << IndentTo(m_pState->CurIndent());
|
||||
if (m_pState->CurGroupChildCount() == 0)
|
||||
if (originalType == FlowType::Block) {
|
||||
m_stream << "{";
|
||||
} else {
|
||||
if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode())
|
||||
m_stream << "{";
|
||||
}
|
||||
m_stream << "}";
|
||||
}
|
||||
|
||||
@@ -486,6 +504,9 @@ void Emitter::FlowMapPrepareSimpleKeyValue(EmitterNodeType::value child) {
|
||||
if (m_stream.comment())
|
||||
m_stream << "\n";
|
||||
m_stream << IndentTo(lastIndent);
|
||||
if (m_pState->HasAlias()) {
|
||||
m_stream << " ";
|
||||
}
|
||||
m_stream << ":";
|
||||
}
|
||||
|
||||
@@ -556,6 +577,8 @@ void Emitter::BlockMapPrepareLongKey(EmitterNodeType::value child) {
|
||||
break;
|
||||
case EmitterNodeType::BlockSeq:
|
||||
case EmitterNodeType::BlockMap:
|
||||
if (m_pState->HasBegunContent())
|
||||
m_stream << "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -579,8 +602,12 @@ void Emitter::BlockMapPrepareLongKeyValue(EmitterNodeType::value child) {
|
||||
case EmitterNodeType::Scalar:
|
||||
case EmitterNodeType::FlowSeq:
|
||||
case EmitterNodeType::FlowMap:
|
||||
SpaceOrIndentTo(true, curIndent + 1);
|
||||
break;
|
||||
case EmitterNodeType::BlockSeq:
|
||||
case EmitterNodeType::BlockMap:
|
||||
if (m_pState->HasBegunContent())
|
||||
m_stream << "\n";
|
||||
SpaceOrIndentTo(true, curIndent + 1);
|
||||
break;
|
||||
}
|
||||
@@ -619,6 +646,9 @@ void Emitter::BlockMapPrepareSimpleKeyValue(EmitterNodeType::value child) {
|
||||
const std::size_t nextIndent = curIndent + m_pState->CurGroupIndent();
|
||||
|
||||
if (!m_pState->HasBegunNode()) {
|
||||
if (m_pState->HasAlias()) {
|
||||
m_stream << " ";
|
||||
}
|
||||
m_stream << ":";
|
||||
}
|
||||
|
||||
@@ -672,16 +702,29 @@ void Emitter::StartedScalar() { m_pState->StartedScalar(); }
|
||||
// *******************************************************************************************
|
||||
// overloads of Write
|
||||
|
||||
StringEscaping::value GetStringEscapingStyle(const EMITTER_MANIP emitterManip) {
|
||||
switch (emitterManip) {
|
||||
case EscapeNonAscii:
|
||||
return StringEscaping::NonAscii;
|
||||
case EscapeAsJson:
|
||||
return StringEscaping::JSON;
|
||||
default:
|
||||
return StringEscaping::None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Emitter& Emitter::Write(const std::string& str) {
|
||||
if (!good())
|
||||
return *this;
|
||||
|
||||
const bool escapeNonAscii = m_pState->GetOutputCharset() == EscapeNonAscii;
|
||||
StringEscaping::value stringEscaping = GetStringEscapingStyle(m_pState->GetOutputCharset());
|
||||
|
||||
const StringFormat::value strFormat =
|
||||
Utils::ComputeStringFormat(str, m_pState->GetStringFormat(),
|
||||
m_pState->CurGroupFlowType(), escapeNonAscii);
|
||||
m_pState->CurGroupFlowType(), stringEscaping == StringEscaping::NonAscii);
|
||||
|
||||
if (strFormat == StringFormat::Literal)
|
||||
if (strFormat == StringFormat::Literal || str.size() > 1024)
|
||||
m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local);
|
||||
|
||||
PrepareNode(EmitterNodeType::Scalar);
|
||||
@@ -694,7 +737,7 @@ Emitter& Emitter::Write(const std::string& str) {
|
||||
Utils::WriteSingleQuotedString(m_stream, str);
|
||||
break;
|
||||
case StringFormat::DoubleQuoted:
|
||||
Utils::WriteDoubleQuotedString(m_stream, str, escapeNonAscii);
|
||||
Utils::WriteDoubleQuotedString(m_stream, str, stringEscaping);
|
||||
break;
|
||||
case StringFormat::Literal:
|
||||
Utils::WriteLiteralString(m_stream, str,
|
||||
@@ -764,6 +807,21 @@ const char* Emitter::ComputeFullBoolName(bool b) const {
|
||||
// these answers
|
||||
}
|
||||
|
||||
const char* Emitter::ComputeNullName() const {
|
||||
switch (m_pState->GetNullFormat()) {
|
||||
case LowerNull:
|
||||
return "null";
|
||||
case UpperNull:
|
||||
return "NULL";
|
||||
case CamelNull:
|
||||
return "Null";
|
||||
case TildeNull:
|
||||
// fallthrough
|
||||
default:
|
||||
return "~";
|
||||
}
|
||||
}
|
||||
|
||||
Emitter& Emitter::Write(bool b) {
|
||||
if (!good())
|
||||
return *this;
|
||||
@@ -785,8 +843,10 @@ Emitter& Emitter::Write(char ch) {
|
||||
if (!good())
|
||||
return *this;
|
||||
|
||||
|
||||
|
||||
PrepareNode(EmitterNodeType::Scalar);
|
||||
Utils::WriteChar(m_stream, ch);
|
||||
Utils::WriteChar(m_stream, ch, GetStringEscapingStyle(m_pState->GetOutputCharset()));
|
||||
StartedScalar();
|
||||
|
||||
return *this;
|
||||
@@ -810,6 +870,8 @@ Emitter& Emitter::Write(const _Alias& alias) {
|
||||
|
||||
StartedScalar();
|
||||
|
||||
m_pState->SetAlias();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -887,7 +949,7 @@ Emitter& Emitter::Write(const _Null& /*null*/) {
|
||||
|
||||
PrepareNode(EmitterNodeType::Scalar);
|
||||
|
||||
m_stream << "~";
|
||||
m_stream << ComputeNullName();
|
||||
|
||||
StartedScalar();
|
||||
|
||||
|
@@ -13,6 +13,7 @@ EmitterState::EmitterState()
|
||||
m_boolFmt(TrueFalseBool),
|
||||
m_boolLengthFmt(LongBool),
|
||||
m_boolCaseFmt(LowerCase),
|
||||
m_nullFmt(TildeNull),
|
||||
m_intFmt(Dec),
|
||||
m_indent(2),
|
||||
m_preCommentIndent(2),
|
||||
@@ -28,6 +29,7 @@ EmitterState::EmitterState()
|
||||
m_groups{},
|
||||
m_curIndent(0),
|
||||
m_hasAnchor(false),
|
||||
m_hasAlias(false),
|
||||
m_hasTag(false),
|
||||
m_hasNonContent(false),
|
||||
m_docCount(0) {}
|
||||
@@ -43,6 +45,7 @@ void EmitterState::SetLocalValue(EMITTER_MANIP value) {
|
||||
SetBoolFormat(value, FmtScope::Local);
|
||||
SetBoolCaseFormat(value, FmtScope::Local);
|
||||
SetBoolLengthFormat(value, FmtScope::Local);
|
||||
SetNullFormat(value, FmtScope::Local);
|
||||
SetIntFormat(value, FmtScope::Local);
|
||||
SetFlowType(GroupType::Seq, value, FmtScope::Local);
|
||||
SetFlowType(GroupType::Map, value, FmtScope::Local);
|
||||
@@ -51,6 +54,8 @@ void EmitterState::SetLocalValue(EMITTER_MANIP value) {
|
||||
|
||||
void EmitterState::SetAnchor() { m_hasAnchor = true; }
|
||||
|
||||
void EmitterState::SetAlias() { m_hasAlias = true; }
|
||||
|
||||
void EmitterState::SetTag() { m_hasTag = true; }
|
||||
|
||||
void EmitterState::SetNonContent() { m_hasNonContent = true; }
|
||||
@@ -85,6 +90,7 @@ void EmitterState::StartedNode() {
|
||||
}
|
||||
|
||||
m_hasAnchor = false;
|
||||
m_hasAlias = false;
|
||||
m_hasTag = false;
|
||||
m_hasNonContent = false;
|
||||
}
|
||||
@@ -94,15 +100,13 @@ EmitterNodeType::value EmitterState::NextGroupType(
|
||||
if (type == GroupType::Seq) {
|
||||
if (GetFlowType(type) == Block)
|
||||
return EmitterNodeType::BlockSeq;
|
||||
else
|
||||
return EmitterNodeType::FlowSeq;
|
||||
} else {
|
||||
if (GetFlowType(type) == Block)
|
||||
return EmitterNodeType::BlockMap;
|
||||
else
|
||||
return EmitterNodeType::FlowMap;
|
||||
return EmitterNodeType::FlowSeq;
|
||||
}
|
||||
|
||||
if (GetFlowType(type) == Block)
|
||||
return EmitterNodeType::BlockMap;
|
||||
return EmitterNodeType::FlowMap;
|
||||
|
||||
// can't happen
|
||||
assert(false);
|
||||
return EmitterNodeType::NoType;
|
||||
@@ -156,9 +160,15 @@ void EmitterState::EndedGroup(GroupType::value type) {
|
||||
if (m_groups.empty()) {
|
||||
if (type == GroupType::Seq) {
|
||||
return SetError(ErrorMsg::UNEXPECTED_END_SEQ);
|
||||
} else {
|
||||
return SetError(ErrorMsg::UNEXPECTED_END_MAP);
|
||||
}
|
||||
return SetError(ErrorMsg::UNEXPECTED_END_MAP);
|
||||
}
|
||||
|
||||
if (m_hasTag) {
|
||||
SetError(ErrorMsg::INVALID_TAG);
|
||||
}
|
||||
if (m_hasAnchor) {
|
||||
SetError(ErrorMsg::INVALID_ANCHOR);
|
||||
}
|
||||
|
||||
// get rid of the current group
|
||||
@@ -180,6 +190,9 @@ void EmitterState::EndedGroup(GroupType::value type) {
|
||||
m_globalModifiedSettings.restore();
|
||||
|
||||
ClearModifiedSettings();
|
||||
m_hasAnchor = false;
|
||||
m_hasTag = false;
|
||||
m_hasNonContent = false;
|
||||
}
|
||||
|
||||
EmitterNodeType::value EmitterState::CurGroupNodeType() const {
|
||||
@@ -220,11 +233,16 @@ std::size_t EmitterState::LastIndent() const {
|
||||
|
||||
void EmitterState::ClearModifiedSettings() { m_modifiedSettings.clear(); }
|
||||
|
||||
void EmitterState::RestoreGlobalModifiedSettings() {
|
||||
m_globalModifiedSettings.restore();
|
||||
}
|
||||
|
||||
bool EmitterState::SetOutputCharset(EMITTER_MANIP value,
|
||||
FmtScope::value scope) {
|
||||
switch (value) {
|
||||
case EmitNonAscii:
|
||||
case EscapeNonAscii:
|
||||
case EscapeAsJson:
|
||||
_Set(m_charset, value, scope);
|
||||
return true;
|
||||
default:
|
||||
@@ -282,6 +300,19 @@ bool EmitterState::SetBoolCaseFormat(EMITTER_MANIP value,
|
||||
}
|
||||
}
|
||||
|
||||
bool EmitterState::SetNullFormat(EMITTER_MANIP value, FmtScope::value scope) {
|
||||
switch (value) {
|
||||
case LowerNull:
|
||||
case UpperNull:
|
||||
case CamelNull:
|
||||
case TildeNull:
|
||||
_Set(m_nullFmt, value, scope);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmitterState::SetIntFormat(EMITTER_MANIP value, FmtScope::value scope) {
|
||||
switch (value) {
|
||||
case Dec:
|
||||
|
@@ -43,6 +43,7 @@ class EmitterState {
|
||||
|
||||
// node handling
|
||||
void SetAnchor();
|
||||
void SetAlias();
|
||||
void SetTag();
|
||||
void SetNonContent();
|
||||
void SetLongKey();
|
||||
@@ -65,6 +66,7 @@ class EmitterState {
|
||||
std::size_t LastIndent() const;
|
||||
std::size_t CurIndent() const { return m_curIndent; }
|
||||
bool HasAnchor() const { return m_hasAnchor; }
|
||||
bool HasAlias() const { return m_hasAlias; }
|
||||
bool HasTag() const { return m_hasTag; }
|
||||
bool HasBegunNode() const {
|
||||
return m_hasAnchor || m_hasTag || m_hasNonContent;
|
||||
@@ -72,6 +74,7 @@ class EmitterState {
|
||||
bool HasBegunContent() const { return m_hasAnchor || m_hasTag; }
|
||||
|
||||
void ClearModifiedSettings();
|
||||
void RestoreGlobalModifiedSettings();
|
||||
|
||||
// formatters
|
||||
void SetLocalValue(EMITTER_MANIP value);
|
||||
@@ -91,6 +94,9 @@ class EmitterState {
|
||||
bool SetBoolCaseFormat(EMITTER_MANIP value, FmtScope::value scope);
|
||||
EMITTER_MANIP GetBoolCaseFormat() const { return m_boolCaseFmt.get(); }
|
||||
|
||||
bool SetNullFormat(EMITTER_MANIP value, FmtScope::value scope);
|
||||
EMITTER_MANIP GetNullFormat() const { return m_nullFmt.get(); }
|
||||
|
||||
bool SetIntFormat(EMITTER_MANIP value, FmtScope::value scope);
|
||||
EMITTER_MANIP GetIntFormat() const { return m_intFmt.get(); }
|
||||
|
||||
@@ -131,6 +137,7 @@ class EmitterState {
|
||||
Setting<EMITTER_MANIP> m_boolFmt;
|
||||
Setting<EMITTER_MANIP> m_boolLengthFmt;
|
||||
Setting<EMITTER_MANIP> m_boolCaseFmt;
|
||||
Setting<EMITTER_MANIP> m_nullFmt;
|
||||
Setting<EMITTER_MANIP> m_intFmt;
|
||||
Setting<std::size_t> m_indent;
|
||||
Setting<std::size_t> m_preCommentIndent, m_postCommentIndent;
|
||||
@@ -182,6 +189,7 @@ class EmitterState {
|
||||
std::vector<std::unique_ptr<Group>> m_groups;
|
||||
std::size_t m_curIndent;
|
||||
bool m_hasAnchor;
|
||||
bool m_hasAlias;
|
||||
bool m_hasTag;
|
||||
bool m_hasNonContent;
|
||||
std::size_t m_docCount;
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
@@ -199,15 +200,10 @@ bool IsValidPlainScalar(const std::string& str, FlowType::value flowType,
|
||||
|
||||
bool IsValidSingleQuotedScalar(const std::string& str, bool escapeNonAscii) {
|
||||
// TODO: check for non-printable characters?
|
||||
for (char ch : str) {
|
||||
if (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch))) {
|
||||
return false;
|
||||
}
|
||||
if (ch == '\n') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return std::none_of(str.begin(), str.end(), [=](char ch) {
|
||||
return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch))) ||
|
||||
(ch == '\n');
|
||||
});
|
||||
}
|
||||
|
||||
bool IsValidLiteralScalar(const std::string& str, FlowType::value flowType,
|
||||
@@ -217,28 +213,39 @@ bool IsValidLiteralScalar(const std::string& str, FlowType::value flowType,
|
||||
}
|
||||
|
||||
// TODO: check for non-printable characters?
|
||||
for (char ch : str) {
|
||||
if (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return std::none_of(str.begin(), str.end(), [=](char ch) {
|
||||
return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch)));
|
||||
});
|
||||
}
|
||||
|
||||
void WriteDoubleQuoteEscapeSequence(ostream_wrapper& out, int codePoint) {
|
||||
std::pair<uint16_t, uint16_t> EncodeUTF16SurrogatePair(int codePoint) {
|
||||
const uint32_t leadOffset = 0xD800 - (0x10000 >> 10);
|
||||
|
||||
return {
|
||||
leadOffset | (codePoint >> 10),
|
||||
0xDC00 | (codePoint & 0x3FF),
|
||||
};
|
||||
}
|
||||
|
||||
void WriteDoubleQuoteEscapeSequence(ostream_wrapper& out, int codePoint, StringEscaping::value stringEscapingStyle) {
|
||||
static const char hexDigits[] = "0123456789abcdef";
|
||||
|
||||
out << "\\";
|
||||
int digits = 8;
|
||||
if (codePoint < 0xFF) {
|
||||
if (codePoint < 0xFF && stringEscapingStyle != StringEscaping::JSON) {
|
||||
out << "x";
|
||||
digits = 2;
|
||||
} else if (codePoint < 0xFFFF) {
|
||||
out << "u";
|
||||
digits = 4;
|
||||
} else {
|
||||
} else if (stringEscapingStyle != StringEscaping::JSON) {
|
||||
out << "U";
|
||||
digits = 8;
|
||||
} else {
|
||||
auto surrogatePair = EncodeUTF16SurrogatePair(codePoint);
|
||||
WriteDoubleQuoteEscapeSequence(out, surrogatePair.first, stringEscapingStyle);
|
||||
WriteDoubleQuoteEscapeSequence(out, surrogatePair.second, stringEscapingStyle);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write digits into the escape sequence
|
||||
@@ -310,7 +317,7 @@ bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str) {
|
||||
}
|
||||
|
||||
bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
|
||||
bool escapeNonAscii) {
|
||||
StringEscaping::value stringEscaping) {
|
||||
out << "\"";
|
||||
int codePoint;
|
||||
for (std::string::const_iterator i = str.begin();
|
||||
@@ -334,16 +341,19 @@ bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
|
||||
case '\b':
|
||||
out << "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
out << "\\f";
|
||||
break;
|
||||
default:
|
||||
if (codePoint < 0x20 ||
|
||||
(codePoint >= 0x80 &&
|
||||
codePoint <= 0xA0)) { // Control characters and non-breaking space
|
||||
WriteDoubleQuoteEscapeSequence(out, codePoint);
|
||||
WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping);
|
||||
} else if (codePoint == 0xFEFF) { // Byte order marks (ZWNS) should be
|
||||
// escaped (YAML 1.2, sec. 5.2)
|
||||
WriteDoubleQuoteEscapeSequence(out, codePoint);
|
||||
} else if (escapeNonAscii && codePoint > 0x7E) {
|
||||
WriteDoubleQuoteEscapeSequence(out, codePoint);
|
||||
WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping);
|
||||
} else if (stringEscaping == StringEscaping::NonAscii && codePoint > 0x7E) {
|
||||
WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping);
|
||||
} else {
|
||||
WriteCodePoint(out, codePoint);
|
||||
}
|
||||
@@ -356,37 +366,41 @@ bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
|
||||
bool WriteLiteralString(ostream_wrapper& out, const std::string& str,
|
||||
std::size_t indent) {
|
||||
out << "|\n";
|
||||
out << IndentTo(indent);
|
||||
int codePoint;
|
||||
for (std::string::const_iterator i = str.begin();
|
||||
GetNextCodePointAndAdvance(codePoint, i, str.end());) {
|
||||
if (codePoint == '\n') {
|
||||
out << "\n" << IndentTo(indent);
|
||||
out << "\n";
|
||||
} else {
|
||||
out<< IndentTo(indent);
|
||||
WriteCodePoint(out, codePoint);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteChar(ostream_wrapper& out, char ch) {
|
||||
bool WriteChar(ostream_wrapper& out, char ch, StringEscaping::value stringEscapingStyle) {
|
||||
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) {
|
||||
out << ch;
|
||||
} else if (ch == '\"') {
|
||||
out << "\"\\\"\"";
|
||||
out << R"("\"")";
|
||||
} else if (ch == '\t') {
|
||||
out << "\"\\t\"";
|
||||
out << R"("\t")";
|
||||
} else if (ch == '\n') {
|
||||
out << "\"\\n\"";
|
||||
out << R"("\n")";
|
||||
} else if (ch == '\b') {
|
||||
out << "\"\\b\"";
|
||||
out << R"("\b")";
|
||||
} else if (ch == '\r') {
|
||||
out << R"("\r")";
|
||||
} else if (ch == '\f') {
|
||||
out << R"("\f")";
|
||||
} else if (ch == '\\') {
|
||||
out << "\"\\\\\"";
|
||||
out << R"("\\")";
|
||||
} else if (0x20 <= ch && ch <= 0x7e) {
|
||||
out << "\"" << ch << "\"";
|
||||
} else {
|
||||
out << "\"";
|
||||
WriteDoubleQuoteEscapeSequence(out, ch);
|
||||
WriteDoubleQuoteEscapeSequence(out, ch, stringEscapingStyle);
|
||||
out << "\"";
|
||||
}
|
||||
return true;
|
||||
@@ -476,7 +490,7 @@ bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix,
|
||||
|
||||
bool WriteBinary(ostream_wrapper& out, const Binary& binary) {
|
||||
WriteDoubleQuotedString(out, EncodeBase64(binary.data(), binary.size()),
|
||||
false);
|
||||
StringEscaping::None);
|
||||
return true;
|
||||
}
|
||||
} // namespace Utils
|
||||
|
@@ -24,6 +24,10 @@ struct StringFormat {
|
||||
enum value { Plain, SingleQuoted, DoubleQuoted, Literal };
|
||||
};
|
||||
|
||||
struct StringEscaping {
|
||||
enum value { None, NonAscii, JSON };
|
||||
};
|
||||
|
||||
namespace Utils {
|
||||
StringFormat::value ComputeStringFormat(const std::string& str,
|
||||
EMITTER_MANIP strFormat,
|
||||
@@ -32,10 +36,11 @@ StringFormat::value ComputeStringFormat(const std::string& str,
|
||||
|
||||
bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str);
|
||||
bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
|
||||
bool escapeNonAscii);
|
||||
StringEscaping::value stringEscaping);
|
||||
bool WriteLiteralString(ostream_wrapper& out, const std::string& str,
|
||||
std::size_t indent);
|
||||
bool WriteChar(ostream_wrapper& out, char ch);
|
||||
bool WriteChar(ostream_wrapper& out, char ch,
|
||||
StringEscaping::value stringEscapingStyle);
|
||||
bool WriteComment(ostream_wrapper& out, const std::string& str,
|
||||
std::size_t postCommentIndent);
|
||||
bool WriteAlias(ostream_wrapper& out, const std::string& str);
|
||||
|
@@ -17,4 +17,4 @@ BadPushback::~BadPushback() YAML_CPP_NOEXCEPT = default;
|
||||
BadInsert::~BadInsert() YAML_CPP_NOEXCEPT = default;
|
||||
EmitterException::~EmitterException() YAML_CPP_NOEXCEPT = default;
|
||||
BadFile::~BadFile() YAML_CPP_NOEXCEPT = default;
|
||||
}
|
||||
} // namespace YAML
|
||||
|
14
src/exp.cpp
14
src/exp.cpp
@@ -54,14 +54,16 @@ std::string Escape(Stream& in, int codeLength) {
|
||||
// now break it up into chars
|
||||
if (value <= 0x7F)
|
||||
return Str(value);
|
||||
else if (value <= 0x7FF)
|
||||
|
||||
if (value <= 0x7FF)
|
||||
return Str(0xC0 + (value >> 6)) + Str(0x80 + (value & 0x3F));
|
||||
else if (value <= 0xFFFF)
|
||||
|
||||
if (value <= 0xFFFF)
|
||||
return Str(0xE0 + (value >> 12)) + Str(0x80 + ((value >> 6) & 0x3F)) +
|
||||
Str(0x80 + (value & 0x3F));
|
||||
else
|
||||
return Str(0xF0 + (value >> 18)) + Str(0x80 + ((value >> 12) & 0x3F)) +
|
||||
Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F));
|
||||
|
||||
return Str(0xF0 + (value >> 18)) + Str(0x80 + ((value >> 12) & 0x3F)) +
|
||||
Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F));
|
||||
}
|
||||
|
||||
// Escape
|
||||
@@ -103,7 +105,7 @@ std::string Escape(Stream& in) {
|
||||
case 'e':
|
||||
return "\x1B";
|
||||
case ' ':
|
||||
return "\x20";
|
||||
return R"( )";
|
||||
case '\"':
|
||||
return "\"";
|
||||
case '\'':
|
||||
|
@@ -110,7 +110,7 @@ inline const RegEx& Value() {
|
||||
return e;
|
||||
}
|
||||
inline const RegEx& ValueInFlow() {
|
||||
static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx(",}", REGEX_OR));
|
||||
static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx(",]}", REGEX_OR));
|
||||
return e;
|
||||
}
|
||||
inline const RegEx& ValueInJSONFlow() {
|
||||
@@ -155,7 +155,7 @@ inline const RegEx& PlainScalar() {
|
||||
inline const RegEx& PlainScalarInFlow() {
|
||||
static const RegEx e =
|
||||
!(BlankOrBreak() | RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) |
|
||||
(RegEx("-:", REGEX_OR) + Blank()));
|
||||
(RegEx("-:", REGEX_OR) + (Blank() | RegEx())));
|
||||
return e;
|
||||
}
|
||||
inline const RegEx& EndScalar() {
|
||||
|
@@ -22,5 +22,5 @@ node& memory::create_node() {
|
||||
void memory::merge(const memory& rhs) {
|
||||
m_nodes.insert(rhs.m_nodes.begin(), rhs.m_nodes.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace YAML
|
||||
|
@@ -9,4 +9,4 @@ Node Clone(const Node& node) {
|
||||
events.Emit(builder);
|
||||
return builder.Root();
|
||||
}
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
@@ -109,9 +110,9 @@ void node_data::compute_seq_size() const {
|
||||
}
|
||||
|
||||
void node_data::compute_map_size() const {
|
||||
kv_pairs::iterator it = m_undefinedPairs.begin();
|
||||
auto it = m_undefinedPairs.begin();
|
||||
while (it != m_undefinedPairs.end()) {
|
||||
kv_pairs::iterator jt = std::next(it);
|
||||
auto jt = std::next(it);
|
||||
if (it->first->is_defined() && it->second->is_defined())
|
||||
m_undefinedPairs.erase(it);
|
||||
it = jt;
|
||||
@@ -120,7 +121,7 @@ void node_data::compute_map_size() const {
|
||||
|
||||
const_node_iterator node_data::begin() const {
|
||||
if (!m_isDefined)
|
||||
return const_node_iterator();
|
||||
return {};
|
||||
|
||||
switch (m_type) {
|
||||
case NodeType::Sequence:
|
||||
@@ -128,13 +129,13 @@ const_node_iterator node_data::begin() const {
|
||||
case NodeType::Map:
|
||||
return const_node_iterator(m_map.begin(), m_map.end());
|
||||
default:
|
||||
return const_node_iterator();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
node_iterator node_data::begin() {
|
||||
if (!m_isDefined)
|
||||
return node_iterator();
|
||||
return {};
|
||||
|
||||
switch (m_type) {
|
||||
case NodeType::Sequence:
|
||||
@@ -142,13 +143,13 @@ node_iterator node_data::begin() {
|
||||
case NodeType::Map:
|
||||
return node_iterator(m_map.begin(), m_map.end());
|
||||
default:
|
||||
return node_iterator();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const_node_iterator node_data::end() const {
|
||||
if (!m_isDefined)
|
||||
return const_node_iterator();
|
||||
return {};
|
||||
|
||||
switch (m_type) {
|
||||
case NodeType::Sequence:
|
||||
@@ -156,13 +157,13 @@ const_node_iterator node_data::end() const {
|
||||
case NodeType::Map:
|
||||
return const_node_iterator(m_map.end(), m_map.end());
|
||||
default:
|
||||
return const_node_iterator();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
node_iterator node_data::end() {
|
||||
if (!m_isDefined)
|
||||
return node_iterator();
|
||||
return {};
|
||||
|
||||
switch (m_type) {
|
||||
case NodeType::Sequence:
|
||||
@@ -170,12 +171,13 @@ node_iterator node_data::end() {
|
||||
case NodeType::Map:
|
||||
return node_iterator(m_map.end(), m_map.end());
|
||||
default:
|
||||
return node_iterator();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// sequence
|
||||
void node_data::push_back(node& node, shared_memory_holder /* pMemory */) {
|
||||
void node_data::push_back(node& node,
|
||||
const shared_memory_holder& /* pMemory */) {
|
||||
if (m_type == NodeType::Undefined || m_type == NodeType::Null) {
|
||||
m_type = NodeType::Sequence;
|
||||
reset_sequence();
|
||||
@@ -187,7 +189,8 @@ void node_data::push_back(node& node, shared_memory_holder /* pMemory */) {
|
||||
m_sequence.push_back(&node);
|
||||
}
|
||||
|
||||
void node_data::insert(node& key, node& value, shared_memory_holder pMemory) {
|
||||
void node_data::insert(node& key, node& value,
|
||||
const shared_memory_holder& pMemory) {
|
||||
switch (m_type) {
|
||||
case NodeType::Map:
|
||||
break;
|
||||
@@ -204,20 +207,21 @@ void node_data::insert(node& key, node& value, shared_memory_holder pMemory) {
|
||||
}
|
||||
|
||||
// indexing
|
||||
node* node_data::get(node& key, shared_memory_holder /* pMemory */) const {
|
||||
node* node_data::get(node& key,
|
||||
const shared_memory_holder& /* pMemory */) const {
|
||||
if (m_type != NodeType::Map) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
if (it->first->is(key))
|
||||
return it->second;
|
||||
for (const auto& it : m_map) {
|
||||
if (it.first->is(key))
|
||||
return it.second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node& node_data::get(node& key, shared_memory_holder pMemory) {
|
||||
node& node_data::get(node& key, const shared_memory_holder& pMemory) {
|
||||
switch (m_type) {
|
||||
case NodeType::Map:
|
||||
break;
|
||||
@@ -230,9 +234,9 @@ node& node_data::get(node& key, shared_memory_holder pMemory) {
|
||||
throw BadSubscript(m_mark, key);
|
||||
}
|
||||
|
||||
for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
if (it->first->is(key))
|
||||
return *it->second;
|
||||
for (const auto& it : m_map) {
|
||||
if (it.first->is(key))
|
||||
return *it.second;
|
||||
}
|
||||
|
||||
node& value = pMemory->create_node();
|
||||
@@ -240,23 +244,26 @@ node& node_data::get(node& key, shared_memory_holder pMemory) {
|
||||
return value;
|
||||
}
|
||||
|
||||
bool node_data::remove(node& key, shared_memory_holder /* pMemory */) {
|
||||
bool node_data::remove(node& key, const shared_memory_holder& /* pMemory */) {
|
||||
if (m_type != NodeType::Map)
|
||||
return false;
|
||||
|
||||
for (kv_pairs::iterator it = m_undefinedPairs.begin();
|
||||
it != m_undefinedPairs.end();) {
|
||||
kv_pairs::iterator jt = std::next(it);
|
||||
for (auto it = m_undefinedPairs.begin(); it != m_undefinedPairs.end();) {
|
||||
auto jt = std::next(it);
|
||||
if (it->first->is(key))
|
||||
m_undefinedPairs.erase(it);
|
||||
it = jt;
|
||||
}
|
||||
|
||||
for (node_map::iterator it = m_map.begin(); it != m_map.end(); ++it) {
|
||||
if (it->first->is(key)) {
|
||||
m_map.erase(it);
|
||||
return true;
|
||||
}
|
||||
auto it =
|
||||
std::find_if(m_map.begin(), m_map.end(),
|
||||
[&](std::pair<YAML::detail::node*, YAML::detail::node*> j) {
|
||||
return (j.first->is(key));
|
||||
});
|
||||
|
||||
if (it != m_map.end()) {
|
||||
m_map.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -279,7 +286,7 @@ void node_data::insert_map_pair(node& key, node& value) {
|
||||
m_undefinedPairs.emplace_back(&key, &value);
|
||||
}
|
||||
|
||||
void node_data::convert_to_map(shared_memory_holder pMemory) {
|
||||
void node_data::convert_to_map(const shared_memory_holder& pMemory) {
|
||||
switch (m_type) {
|
||||
case NodeType::Undefined:
|
||||
case NodeType::Null:
|
||||
@@ -297,7 +304,7 @@ void node_data::convert_to_map(shared_memory_holder pMemory) {
|
||||
}
|
||||
}
|
||||
|
||||
void node_data::convert_sequence_to_map(shared_memory_holder pMemory) {
|
||||
void node_data::convert_sequence_to_map(const shared_memory_holder& pMemory) {
|
||||
assert(m_type == NodeType::Sequence);
|
||||
|
||||
reset_map();
|
||||
|
@@ -92,7 +92,7 @@ void NodeBuilder::Push(detail::node& node) {
|
||||
|
||||
m_stack.push_back(&node);
|
||||
if (needsKey)
|
||||
m_keys.push_back(PushedKey(&node, false));
|
||||
m_keys.emplace_back(&node, false);
|
||||
}
|
||||
|
||||
void NodeBuilder::Pop() {
|
||||
|
@@ -13,7 +13,7 @@ void NodeEvents::AliasManager::RegisterReference(const detail::node& node) {
|
||||
|
||||
anchor_t NodeEvents::AliasManager::LookupAnchor(
|
||||
const detail::node& node) const {
|
||||
AnchorByIdentity::const_iterator it = m_anchorByIdentity.find(node.ref());
|
||||
auto it = m_anchorByIdentity.find(node.ref());
|
||||
if (it == m_anchorByIdentity.end())
|
||||
return 0;
|
||||
return it->second;
|
||||
@@ -32,13 +32,12 @@ void NodeEvents::Setup(const detail::node& node) {
|
||||
return;
|
||||
|
||||
if (node.type() == NodeType::Sequence) {
|
||||
for (detail::const_node_iterator it = node.begin(); it != node.end(); ++it)
|
||||
Setup(**it);
|
||||
for (auto element : node)
|
||||
Setup(*element);
|
||||
} else if (node.type() == NodeType::Map) {
|
||||
for (detail::const_node_iterator it = node.begin(); it != node.end();
|
||||
++it) {
|
||||
Setup(*it->first);
|
||||
Setup(*it->second);
|
||||
for (auto element : node) {
|
||||
Setup(*element.first);
|
||||
Setup(*element.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,17 +76,15 @@ void NodeEvents::Emit(const detail::node& node, EventHandler& handler,
|
||||
break;
|
||||
case NodeType::Sequence:
|
||||
handler.OnSequenceStart(Mark(), node.tag(), anchor, node.style());
|
||||
for (detail::const_node_iterator it = node.begin(); it != node.end();
|
||||
++it)
|
||||
Emit(**it, handler, am);
|
||||
for (auto element : node)
|
||||
Emit(*element, handler, am);
|
||||
handler.OnSequenceEnd();
|
||||
break;
|
||||
case NodeType::Map:
|
||||
handler.OnMapStart(Mark(), node.tag(), anchor, node.style());
|
||||
for (detail::const_node_iterator it = node.begin(); it != node.end();
|
||||
++it) {
|
||||
Emit(*it->first, handler, am);
|
||||
Emit(*it->second, handler, am);
|
||||
for (auto element : node) {
|
||||
Emit(*element.first, handler, am);
|
||||
Emit(*element.second, handler, am);
|
||||
}
|
||||
handler.OnMapEnd();
|
||||
break;
|
||||
@@ -95,7 +92,7 @@ void NodeEvents::Emit(const detail::node& node, EventHandler& handler,
|
||||
}
|
||||
|
||||
bool NodeEvents::IsAliased(const detail::node& node) const {
|
||||
RefCount::const_iterator it = m_refCount.find(node.ref());
|
||||
auto it = m_refCount.find(node.ref());
|
||||
return it != m_refCount.end() && it->second > 1;
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -7,4 +7,4 @@ bool IsNullString(const std::string& str) {
|
||||
return str.empty() || str == "~" || str == "null" || str == "Null" ||
|
||||
str == "NULL";
|
||||
}
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -3,10 +3,10 @@
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "yaml-cpp/node/node.h"
|
||||
#include "yaml-cpp/node/impl.h"
|
||||
#include "yaml-cpp/parser.h"
|
||||
#include "nodebuilder.h"
|
||||
#include "yaml-cpp/node/impl.h"
|
||||
#include "yaml-cpp/node/node.h"
|
||||
#include "yaml-cpp/parser.h"
|
||||
|
||||
namespace YAML {
|
||||
Node Load(const std::string& input) {
|
||||
@@ -30,9 +30,9 @@ Node Load(std::istream& input) {
|
||||
}
|
||||
|
||||
Node LoadFile(const std::string& filename) {
|
||||
std::ifstream fin(filename.c_str());
|
||||
std::ifstream fin(filename);
|
||||
if (!fin) {
|
||||
throw BadFile();
|
||||
throw BadFile(filename);
|
||||
}
|
||||
return Load(fin);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ std::vector<Node> LoadAll(std::istream& input) {
|
||||
std::vector<Node> docs;
|
||||
|
||||
Parser parser(input);
|
||||
while (1) {
|
||||
while (true) {
|
||||
NodeBuilder builder;
|
||||
if (!parser.HandleNextDocument(builder)) {
|
||||
break;
|
||||
@@ -63,9 +63,9 @@ std::vector<Node> LoadAll(std::istream& input) {
|
||||
}
|
||||
|
||||
std::vector<Node> LoadAllFromFile(const std::string& filename) {
|
||||
std::ifstream fin(filename.c_str());
|
||||
std::ifstream fin(filename);
|
||||
if (!fin) {
|
||||
throw BadFile();
|
||||
throw BadFile(filename);
|
||||
}
|
||||
return LoadAll(fin);
|
||||
}
|
||||
|
@@ -17,9 +17,7 @@ Parser::Parser(std::istream& in) : Parser() { Load(in); }
|
||||
|
||||
Parser::~Parser() = default;
|
||||
|
||||
Parser::operator bool() const {
|
||||
return m_pScanner && !m_pScanner->empty();
|
||||
}
|
||||
Parser::operator bool() const { return m_pScanner && !m_pScanner->empty(); }
|
||||
|
||||
void Parser::Load(std::istream& in) {
|
||||
m_pScanner.reset(new Scanner(in));
|
||||
|
@@ -51,7 +51,7 @@ Token& Scanner::peek() {
|
||||
Mark Scanner::mark() const { return INPUT.mark(); }
|
||||
|
||||
void Scanner::EnsureTokensInQueue() {
|
||||
while (1) {
|
||||
while (true) {
|
||||
if (!m_tokens.empty()) {
|
||||
Token& token = m_tokens.front();
|
||||
|
||||
@@ -88,7 +88,7 @@ void Scanner::ScanNextToken() {
|
||||
return StartStream();
|
||||
}
|
||||
|
||||
// get rid of whitespace, etc. (in between tokens it should be irrelevent)
|
||||
// get rid of whitespace, etc. (in between tokens it should be irrelevant)
|
||||
ScanToNextToken();
|
||||
|
||||
// maybe need to end some blocks
|
||||
@@ -174,7 +174,7 @@ void Scanner::ScanNextToken() {
|
||||
}
|
||||
|
||||
void Scanner::ScanToNextToken() {
|
||||
while (1) {
|
||||
while (true) {
|
||||
// first eat whitespace
|
||||
while (INPUT && IsWhitespaceToBeEaten(INPUT.peek())) {
|
||||
if (InBlockContext() && Exp::Tab().Matches(INPUT)) {
|
||||
|
@@ -47,7 +47,8 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) {
|
||||
if (INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) {
|
||||
if (params.onDocIndicator == BREAK) {
|
||||
break;
|
||||
} else if (params.onDocIndicator == THROW) {
|
||||
}
|
||||
if (params.onDocIndicator == THROW) {
|
||||
throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR);
|
||||
}
|
||||
}
|
||||
@@ -203,7 +204,7 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) {
|
||||
|
||||
// post-processing
|
||||
if (params.trimTrailingSpaces) {
|
||||
std::size_t pos = scalar.find_last_not_of(' ');
|
||||
std::size_t pos = scalar.find_last_not_of(" \t");
|
||||
if (lastEscapedChar != std::string::npos) {
|
||||
if (pos < lastEscapedChar || pos == std::string::npos) {
|
||||
pos = lastEscapedChar;
|
||||
@@ -247,4 +248,4 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) {
|
||||
|
||||
return scalar;
|
||||
}
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -57,7 +57,7 @@ struct ScanScalarParams {
|
||||
bool leadingSpaces;
|
||||
};
|
||||
|
||||
std::string ScanScalar(Stream& INPUT, ScanScalarParams& info);
|
||||
std::string ScanScalar(Stream& INPUT, ScanScalarParams& params);
|
||||
}
|
||||
|
||||
#endif // SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
|
||||
|
@@ -78,4 +78,4 @@ const std::string ScanTagSuffix(Stream& INPUT) {
|
||||
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -37,7 +37,7 @@ void Scanner::ScanDirective() {
|
||||
token.value += INPUT.get();
|
||||
|
||||
// read parameters
|
||||
while (1) {
|
||||
while (true) {
|
||||
// first get rid of whitespace
|
||||
while (Exp::Blank().Matches(INPUT))
|
||||
INPUT.eat(1);
|
||||
@@ -171,7 +171,7 @@ void Scanner::ScanBlockEntry() {
|
||||
|
||||
// Key
|
||||
void Scanner::ScanKey() {
|
||||
// handle keys diffently in the block context (and manage indents)
|
||||
// handle keys differently in the block context (and manage indents)
|
||||
if (InBlockContext()) {
|
||||
if (!m_simpleKeyAllowed)
|
||||
throw ParserException(INPUT.mark(), ErrorMsg::MAP_KEY);
|
||||
@@ -199,7 +199,7 @@ void Scanner::ScanValue() {
|
||||
// seems fine)
|
||||
m_simpleKeyAllowed = false;
|
||||
} else {
|
||||
// handle values diffently in the block context (and manage indents)
|
||||
// handle values differently in the block context (and manage indents)
|
||||
if (InBlockContext()) {
|
||||
if (!m_simpleKeyAllowed)
|
||||
throw ParserException(INPUT.mark(), ErrorMsg::MAP_VALUE);
|
||||
|
@@ -83,9 +83,8 @@ class SettingChanges {
|
||||
}
|
||||
|
||||
void restore() YAML_CPP_NOEXCEPT {
|
||||
for (setting_changes::const_iterator it = m_settingChanges.begin();
|
||||
it != m_settingChanges.end(); ++it)
|
||||
(*it)->pop();
|
||||
for (const auto& setting : m_settingChanges)
|
||||
setting->pop();
|
||||
}
|
||||
|
||||
void push(std::unique_ptr<SettingChangeBase> pSettingChange) {
|
||||
|
@@ -5,7 +5,11 @@ namespace YAML {
|
||||
struct Mark;
|
||||
|
||||
Scanner::SimpleKey::SimpleKey(const Mark& mark_, std::size_t flowLevel_)
|
||||
: mark(mark_), flowLevel(flowLevel_), pIndent(nullptr), pMapStart(nullptr), pKey(nullptr) {}
|
||||
: mark(mark_),
|
||||
flowLevel(flowLevel_),
|
||||
pIndent(nullptr),
|
||||
pMapStart(nullptr),
|
||||
pKey(nullptr) {}
|
||||
|
||||
void Scanner::SimpleKey::Validate() {
|
||||
// Note: pIndent will *not* be garbage here;
|
||||
@@ -125,4 +129,4 @@ void Scanner::PopAllSimpleKeys() {
|
||||
while (!m_simpleKeys.empty())
|
||||
m_simpleKeys.pop();
|
||||
}
|
||||
}
|
||||
} // namespace YAML
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include "singledocparser.h"
|
||||
#include "tag.h"
|
||||
#include "token.h"
|
||||
#include "yaml-cpp/depthguard.h"
|
||||
#include "yaml-cpp/emitterstyle.h"
|
||||
#include "yaml-cpp/eventhandler.h"
|
||||
#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
|
||||
@@ -47,6 +48,8 @@ void SingleDocParser::HandleDocument(EventHandler& eventHandler) {
|
||||
}
|
||||
|
||||
void SingleDocParser::HandleNode(EventHandler& eventHandler) {
|
||||
DepthGuard<500> depthguard(depth, m_scanner.mark(), ErrorMsg::BAD_FILE);
|
||||
|
||||
// an empty node *is* a possibility
|
||||
if (m_scanner.empty()) {
|
||||
eventHandler.OnNull(m_scanner.mark(), NullAnchor);
|
||||
@@ -87,16 +90,17 @@ void SingleDocParser::HandleNode(EventHandler& eventHandler) {
|
||||
|
||||
const Token& token = m_scanner.peek();
|
||||
|
||||
if (token.type == Token::PLAIN_SCALAR && IsNullString(token.value)) {
|
||||
// add non-specific tags
|
||||
if (tag.empty())
|
||||
tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?");
|
||||
|
||||
if (token.type == Token::PLAIN_SCALAR
|
||||
&& tag.compare("?") == 0 && IsNullString(token.value)) {
|
||||
eventHandler.OnNull(mark, anchor);
|
||||
m_scanner.pop();
|
||||
return;
|
||||
}
|
||||
|
||||
// add non-specific tags
|
||||
if (tag.empty())
|
||||
tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?");
|
||||
|
||||
// now split based on what kind of node we should be
|
||||
switch (token.type) {
|
||||
case Token::PLAIN_SCALAR:
|
||||
@@ -163,7 +167,7 @@ void SingleDocParser::HandleBlockSequence(EventHandler& eventHandler) {
|
||||
m_scanner.pop();
|
||||
m_pCollectionStack->PushCollectionType(CollectionType::BlockSeq);
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
if (m_scanner.empty())
|
||||
throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ);
|
||||
|
||||
@@ -196,7 +200,7 @@ void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler) {
|
||||
m_scanner.pop();
|
||||
m_pCollectionStack->PushCollectionType(CollectionType::FlowSeq);
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
if (m_scanner.empty())
|
||||
throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW);
|
||||
|
||||
@@ -249,7 +253,7 @@ void SingleDocParser::HandleBlockMap(EventHandler& eventHandler) {
|
||||
m_scanner.pop();
|
||||
m_pCollectionStack->PushCollectionType(CollectionType::BlockMap);
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
if (m_scanner.empty())
|
||||
throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP);
|
||||
|
||||
@@ -288,7 +292,7 @@ void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) {
|
||||
m_scanner.pop();
|
||||
m_pCollectionStack->PushCollectionType(CollectionType::FlowMap);
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
if (m_scanner.empty())
|
||||
throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW);
|
||||
|
||||
@@ -373,7 +377,7 @@ void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor,
|
||||
anchor_name.clear();
|
||||
anchor = NullAnchor;
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
if (m_scanner.empty())
|
||||
return;
|
||||
|
||||
@@ -419,7 +423,7 @@ anchor_t SingleDocParser::RegisterAnchor(const std::string& name) {
|
||||
|
||||
anchor_t SingleDocParser::LookupAnchor(const Mark& mark,
|
||||
const std::string& name) const {
|
||||
Anchors::const_iterator it = m_anchors.find(name);
|
||||
auto it = m_anchors.find(name);
|
||||
if (it == m_anchors.end())
|
||||
throw ParserException(mark, ErrorMsg::UNKNOWN_ANCHOR);
|
||||
|
||||
|
@@ -15,6 +15,7 @@
|
||||
|
||||
namespace YAML {
|
||||
class CollectionStack;
|
||||
template <int> class DepthGuard; // depthguard.h
|
||||
class EventHandler;
|
||||
class Node;
|
||||
class Scanner;
|
||||
@@ -55,6 +56,7 @@ class SingleDocParser {
|
||||
anchor_t LookupAnchor(const Mark& mark, const std::string& name) const;
|
||||
|
||||
private:
|
||||
int depth = 0;
|
||||
Scanner& m_scanner;
|
||||
const Directives& m_directives;
|
||||
std::unique_ptr<CollectionStack> m_pCollectionStack;
|
||||
|
@@ -151,7 +151,8 @@ inline UtfIntroCharType IntroCharTypeOf(std::istream::int_type ch) {
|
||||
|
||||
inline char Utf8Adjust(unsigned long ch, unsigned char lead_bits,
|
||||
unsigned char rshift) {
|
||||
const unsigned char header = static_cast<unsigned char>(((1 << lead_bits) - 1) << (8 - lead_bits));
|
||||
const unsigned char header =
|
||||
static_cast<unsigned char>(((1 << lead_bits) - 1) << (8 - lead_bits));
|
||||
const unsigned char mask = (0xFF >> (lead_bits + 1));
|
||||
return static_cast<char>(
|
||||
static_cast<unsigned char>(header | ((ch >> rshift) & mask)));
|
||||
@@ -273,7 +274,7 @@ char Stream::get() {
|
||||
// . Extracts 'n' characters from the stream and updates our position
|
||||
std::string Stream::get(int n) {
|
||||
std::string ret;
|
||||
if(n > 0) {
|
||||
if (n > 0) {
|
||||
ret.reserve(static_cast<std::string::size_type>(n));
|
||||
for (int i = 0; i < n; i++)
|
||||
ret += get();
|
||||
@@ -349,7 +350,9 @@ void Stream::StreamInUtf16() const {
|
||||
// Trailing (low) surrogate...ugh, wrong order
|
||||
QueueUnicodeCodepoint(m_readahead, CP_REPLACEMENT_CHARACTER);
|
||||
return;
|
||||
} else if (ch >= 0xD800 && ch < 0xDC00) {
|
||||
}
|
||||
|
||||
if (ch >= 0xD800 && ch < 0xDC00) {
|
||||
// ch is a leading (high) surrogate
|
||||
|
||||
// Four byte UTF-8 code point
|
||||
@@ -374,11 +377,10 @@ void Stream::StreamInUtf16() const {
|
||||
// Easiest case: queue the codepoint and return
|
||||
QueueUnicodeCodepoint(m_readahead, ch);
|
||||
return;
|
||||
} else {
|
||||
// Start the loop over with the new high surrogate
|
||||
ch = chLow;
|
||||
continue;
|
||||
}
|
||||
// Start the loop over with the new high surrogate
|
||||
ch = chLow;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Select the payload bits from the high surrogate
|
||||
|
@@ -52,6 +52,63 @@ TEST_F(EmitterTest, SimpleQuotedScalar) {
|
||||
ExpectEmit("test");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, DumpAndSize) {
|
||||
Node n(Load("test"));
|
||||
EXPECT_EQ("test", Dump(n));
|
||||
out << n;
|
||||
EXPECT_EQ(4, out.size());
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, NullScalar) {
|
||||
Node n(Load("null"));
|
||||
out << n;
|
||||
ExpectEmit("~");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, AliasScalar) {
|
||||
Node n(Load("[&a str, *a]"));
|
||||
out << n;
|
||||
ExpectEmit("[&1 str, *1]");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, StringFormat) {
|
||||
out << BeginSeq;
|
||||
out.SetStringFormat(SingleQuoted);
|
||||
out << "string";
|
||||
out.SetStringFormat(DoubleQuoted);
|
||||
out << "string";
|
||||
out.SetStringFormat(Literal);
|
||||
out << "string";
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit("- 'string'\n- \"string\"\n- |\n string");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, IntBase) {
|
||||
out << BeginSeq;
|
||||
out.SetIntBase(Dec);
|
||||
out << 1024;
|
||||
out.SetIntBase(Hex);
|
||||
out << 1024;
|
||||
out.SetIntBase(Oct);
|
||||
out << 1024;
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit("- 1024\n- 0x400\n- 02000");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, NumberPrecision) {
|
||||
out.SetFloatPrecision(3);
|
||||
out.SetDoublePrecision(2);
|
||||
out << BeginSeq;
|
||||
out << 3.1425926f;
|
||||
out << 53.5893;
|
||||
out << 2384626.4338;
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit("- 3.14\n- 54\n- 2.4e+06");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, SimpleSeq) {
|
||||
out << BeginSeq;
|
||||
out << "eggs";
|
||||
@@ -81,6 +138,56 @@ TEST_F(EmitterTest, EmptyFlowSeq) {
|
||||
ExpectEmit("[]");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, EmptyBlockSeqWithBegunContent) {
|
||||
out << BeginSeq;
|
||||
out << BeginSeq << Comment("comment") << EndSeq;
|
||||
out << BeginSeq << Newline << EndSeq;
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit(R"(-
|
||||
# comment
|
||||
[]
|
||||
-
|
||||
|
||||
[])");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, EmptyBlockMapWithBegunContent) {
|
||||
out << BeginSeq;
|
||||
out << BeginMap << Comment("comment") << EndMap;
|
||||
out << BeginMap << Newline << EndMap;
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit(R"(- # comment
|
||||
{}
|
||||
-
|
||||
{})");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, EmptyFlowSeqWithBegunContent) {
|
||||
out << Flow;
|
||||
out << BeginSeq;
|
||||
out << BeginSeq << Comment("comment") << EndSeq;
|
||||
out << BeginSeq << Newline << EndSeq;
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit(R"([[ # comment
|
||||
], [
|
||||
]])");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, EmptyFlowMapWithBegunContent) {
|
||||
out << Flow;
|
||||
out << BeginSeq;
|
||||
out << BeginMap << Comment("comment") << EndMap;
|
||||
out << BeginMap << Newline << EndMap;
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit(R"([{ # comment
|
||||
}, {
|
||||
}])");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, NestedBlockSeq) {
|
||||
out << BeginSeq;
|
||||
out << "item 1";
|
||||
@@ -209,6 +316,8 @@ TEST_F(EmitterTest, SimpleLongKey) {
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, SingleLongKey) {
|
||||
const std::string shortKey(1024, 'a');
|
||||
const std::string longKey(1025, 'a');
|
||||
out << BeginMap;
|
||||
out << Key << "age";
|
||||
out << Value << "24";
|
||||
@@ -216,9 +325,14 @@ TEST_F(EmitterTest, SingleLongKey) {
|
||||
out << Value << "5'9\"";
|
||||
out << Key << "weight";
|
||||
out << Value << 145;
|
||||
out << Key << shortKey;
|
||||
out << Value << "1";
|
||||
out << Key << longKey;
|
||||
out << Value << "1";
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit("age: 24\n? height\n: 5'9\"\nweight: 145");
|
||||
ExpectEmit("age: 24\n? height\n: 5'9\"\nweight: 145\n" + shortKey +
|
||||
": 1\n? " + longKey + "\n: 1");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexLongKey) {
|
||||
@@ -268,6 +382,20 @@ TEST_F(EmitterTest, ScalarFormat) {
|
||||
"crazy\tsymbols that we like");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, LiteralWithoutTrailingSpaces) {
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "key";
|
||||
out << YAML::Value << YAML::Literal;
|
||||
out << "expect that with two newlines\n\n"
|
||||
"no spaces are emitted in the empty line";
|
||||
out << YAML::EndMap;
|
||||
|
||||
ExpectEmit(
|
||||
"key: |\n"
|
||||
" expect that with two newlines\n\n"
|
||||
" no spaces are emitted in the empty line");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, AutoLongKeyScalar) {
|
||||
out << BeginMap;
|
||||
out << Key << Literal << "multi-line\nscalar";
|
||||
@@ -316,6 +444,21 @@ TEST_F(EmitterTest, AliasAndAnchor) {
|
||||
ExpectEmit("- &fred\n name: Fred\n age: 42\n- *fred");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, AliasOnKey) {
|
||||
out << BeginSeq;
|
||||
out << Anchor("name") << "Name";
|
||||
out << BeginMap;
|
||||
out << Key << Alias("name") << Value << "Fred";
|
||||
out << EndMap;
|
||||
out << Flow << BeginMap;
|
||||
out << Key << Alias("name") << Value << "Mike";
|
||||
out << EndMap;
|
||||
out << EndSeq;
|
||||
ExpectEmit(R"(- &name Name
|
||||
- *name : Fred
|
||||
- {*name : Mike})");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, AliasAndAnchorWithNull) {
|
||||
out << BeginSeq;
|
||||
out << Anchor("fred") << Null;
|
||||
@@ -429,6 +572,12 @@ TEST_F(EmitterTest, ByKindTagWithScalar) {
|
||||
ExpectEmit("- \"12\"\n- 12\n- ! 12");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, LocalTagInNameHandle) {
|
||||
out << LocalTag("a", "foo") << "bar";
|
||||
|
||||
ExpectEmit("!a!foo bar");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, LocalTagWithScalar) {
|
||||
out << LocalTag("foo") << "bar";
|
||||
|
||||
@@ -516,6 +665,17 @@ TEST_F(EmitterTest, STLContainers) {
|
||||
ExpectEmit("- [2, 3, 5, 7, 11, 13]\n- Daniel: 26\n Jesse: 24");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, CommentStyle) {
|
||||
out.SetPreCommentIndent(1);
|
||||
out.SetPostCommentIndent(2);
|
||||
out << BeginMap;
|
||||
out << Key << "method";
|
||||
out << Value << "least squares" << Comment("should we change this method?");
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit("method: least squares # should we change this method?");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, SimpleComment) {
|
||||
out << BeginMap;
|
||||
out << Key << "method";
|
||||
@@ -612,6 +772,77 @@ TEST_F(EmitterTest, SimpleGlobalSettings) {
|
||||
ExpectEmit("- ? key 1\n : value 1\n ? key 2\n : [a, b, c]");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, GlobalLongKeyOnSeq) {
|
||||
out.SetMapFormat(LongKey);
|
||||
|
||||
out << BeginMap;
|
||||
out << Key << Anchor("key");
|
||||
out << BeginSeq << "a"
|
||||
<< "b" << EndSeq;
|
||||
out << Value << Anchor("value");
|
||||
out << BeginSeq << "c"
|
||||
<< "d" << EndSeq;
|
||||
out << Key << Alias("key") << Value << Alias("value");
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(? &key
|
||||
- a
|
||||
- b
|
||||
: &value
|
||||
- c
|
||||
- d
|
||||
? *key
|
||||
: *value)");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, GlobalLongKeyOnMap) {
|
||||
out.SetMapFormat(LongKey);
|
||||
|
||||
out << BeginMap;
|
||||
out << Key << Anchor("key");
|
||||
out << BeginMap << "a"
|
||||
<< "b" << EndMap;
|
||||
out << Value << Anchor("value");
|
||||
out << BeginMap << "c"
|
||||
<< "d" << EndMap;
|
||||
out << Key << Alias("key") << Value << Alias("value");
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(? &key
|
||||
? a
|
||||
: b
|
||||
: &value
|
||||
? c
|
||||
: d
|
||||
? *key
|
||||
: *value)");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, GlobalSettingStyleOnSeqNode) {
|
||||
Node n(Load(R"(foo:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
bar: aa)"));
|
||||
out.SetSeqFormat(YAML::Flow);
|
||||
out << n;
|
||||
ExpectEmit(R"(foo: [1, 2, 3]
|
||||
bar: aa)");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, GlobalSettingStyleOnMapNode) {
|
||||
Node n(Load(R"(-
|
||||
foo: a
|
||||
bar: b
|
||||
- 2
|
||||
- 3)"));
|
||||
out.SetMapFormat(YAML::Flow);
|
||||
out << n;
|
||||
ExpectEmit(R"(- {foo: a, bar: b}
|
||||
- 2
|
||||
- 3)");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexGlobalSettings) {
|
||||
out << BeginSeq;
|
||||
out << Block;
|
||||
@@ -644,6 +875,17 @@ TEST_F(EmitterTest, Null) {
|
||||
ExpectEmit("- ~\n- null value: ~\n ~: null key");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, OutputCharset) {
|
||||
out << BeginSeq;
|
||||
out.SetOutputCharset(EmitNonAscii);
|
||||
out << "\x24 \xC2\xA2 \xE2\x82\xAC";
|
||||
out.SetOutputCharset(EscapeNonAscii);
|
||||
out << "\x24 \xC2\xA2 \xE2\x82\xAC";
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit("- \x24 \xC2\xA2 \xE2\x82\xAC\n- \"\x24 \\xa2 \\u20ac\"");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, EscapedUnicode) {
|
||||
out << EscapeNonAscii << "\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2";
|
||||
|
||||
@@ -657,7 +899,48 @@ TEST_F(EmitterTest, Unicode) {
|
||||
|
||||
TEST_F(EmitterTest, DoubleQuotedUnicode) {
|
||||
out << DoubleQuoted << "\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2";
|
||||
ExpectEmit("\"\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2\"");
|
||||
ExpectEmit("\"\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2\"");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, EscapedJsonString) {
|
||||
out.SetStringFormat(DoubleQuoted);
|
||||
out.SetOutputCharset(EscapeAsJson);
|
||||
out << "\" \\ "
|
||||
"\x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0A \x0B \x0C \x0D \x0E \x0F "
|
||||
"\x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1F "
|
||||
"\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2";
|
||||
|
||||
ExpectEmit(R"("\" \\ \u0001 \u0002 \u0003 \u0004 \u0005 \u0006 \u0007 \b \t )"
|
||||
R"(\n \u000b \f \r \u000e \u000f \u0010 \u0011 \u0012 \u0013 )"
|
||||
R"(\u0014 \u0015 \u0016 \u0017 \u0018 \u0019 \u001a \u001b )"
|
||||
R"(\u001c \u001d \u001e \u001f )"
|
||||
"$ \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2\"");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, EscapedCharacters) {
|
||||
out << BeginSeq
|
||||
<< '\x00'
|
||||
<< '\x0C'
|
||||
<< '\x0D'
|
||||
<< EndSeq;
|
||||
|
||||
ExpectEmit("- \"\\x00\"\n- \"\\f\"\n- \"\\r\"");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, CharactersEscapedAsJson) {
|
||||
out.SetOutputCharset(EscapeAsJson);
|
||||
out << BeginSeq
|
||||
<< '\x00'
|
||||
<< '\x0C'
|
||||
<< '\x0D'
|
||||
<< EndSeq;
|
||||
|
||||
ExpectEmit("- \"\\u0000\"\n- \"\\f\"\n- \"\\r\"");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, DoubleQuotedString) {
|
||||
out << DoubleQuoted << "\" \\ \n \t \r \b \x15 \xEF\xBB\xBF \x24";
|
||||
ExpectEmit("\"\\\" \\\\ \\n \\t \\r \\b \\x15 \\ufeff $\"");
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
@@ -826,6 +1109,57 @@ TEST_F(EmitterTest, ColonAtEndOfScalarInFlow) {
|
||||
ExpectEmit("{\"C:\": \"C:\"}");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, GlobalBoolFormatting) {
|
||||
out << BeginSeq;
|
||||
out.SetBoolFormat(UpperCase);
|
||||
out.SetBoolFormat(YesNoBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(TrueFalseBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(OnOffBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(LowerCase);
|
||||
out.SetBoolFormat(YesNoBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(TrueFalseBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(OnOffBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(CamelCase);
|
||||
out.SetBoolFormat(YesNoBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(TrueFalseBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(OnOffBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(ShortBool);
|
||||
out.SetBoolFormat(UpperCase);
|
||||
out.SetBoolFormat(YesNoBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(TrueFalseBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out.SetBoolFormat(OnOffBool);
|
||||
out << true;
|
||||
out << false;
|
||||
out << EndSeq;
|
||||
ExpectEmit(
|
||||
"- YES\n- NO\n- TRUE\n- FALSE\n- ON\n- OFF\n"
|
||||
"- yes\n- no\n- true\n- false\n- on\n- off\n"
|
||||
"- Yes\n- No\n- True\n- False\n- On\n- Off\n"
|
||||
"- Y\n- N\n- Y\n- N\n- Y\n- N");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, BoolFormatting) {
|
||||
out << BeginSeq;
|
||||
out << TrueFalseBool << UpperCase << true;
|
||||
@@ -860,6 +1194,45 @@ TEST_F(EmitterTest, BoolFormatting) {
|
||||
"- Y\n- Y\n- y\n- N\n- N\n- n");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, GlobalNullFormatting) {
|
||||
out << Flow << BeginSeq;
|
||||
out.SetNullFormat(LowerNull);
|
||||
out << Null;
|
||||
out.SetNullFormat(UpperNull);
|
||||
out << Null;
|
||||
out.SetNullFormat(CamelNull);
|
||||
out << Null;
|
||||
out.SetNullFormat(TildeNull);
|
||||
out << Null;
|
||||
out << EndSeq;
|
||||
ExpectEmit("[null, NULL, Null, ~]");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, NullFormatting) {
|
||||
out << Flow << BeginSeq;
|
||||
out << LowerNull << Null;
|
||||
out << UpperNull << Null;
|
||||
out << CamelNull << Null;
|
||||
out << TildeNull << Null;
|
||||
out << EndSeq;
|
||||
ExpectEmit("[null, NULL, Null, ~]");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, NullFormattingOnNode) {
|
||||
Node n(Load("null"));
|
||||
out << Flow << BeginSeq;
|
||||
out.SetNullFormat(LowerNull);
|
||||
out << n;
|
||||
out.SetNullFormat(UpperNull);
|
||||
out << n;
|
||||
out.SetNullFormat(CamelNull);
|
||||
out << n;
|
||||
out.SetNullFormat(TildeNull);
|
||||
out << n;
|
||||
out << EndSeq;
|
||||
ExpectEmit("[null, NULL, Null, ~]");
|
||||
}
|
||||
|
||||
// TODO: Fix this test.
|
||||
// TEST_F(EmitterTest, DocStartAndEnd) {
|
||||
// out << BeginDoc;
|
||||
@@ -1025,6 +1398,224 @@ TEST_F(EmitterTest, NaN) {
|
||||
"bar: .nan");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapWithNewLine) {
|
||||
out << YAML::BeginMap;
|
||||
|
||||
out << YAML::Key << "NodeA" << YAML::Value << YAML::BeginMap;
|
||||
out << YAML::Key << "k" << YAML::Value << YAML::Flow << YAML::BeginSeq;
|
||||
out << YAML::BeginMap << YAML::Key << "i" << YAML::Value << 0 << YAML::EndMap
|
||||
<< YAML::Newline;
|
||||
out << YAML::BeginMap << YAML::Key << "i" << YAML::Value << 1 << YAML::EndMap
|
||||
<< YAML::Newline;
|
||||
out << YAML::EndSeq;
|
||||
out << YAML::EndMap;
|
||||
|
||||
out << YAML::Key << "NodeB" << YAML::Value << YAML::BeginMap;
|
||||
out << YAML::Key << "k" << YAML::Value << YAML::Flow << YAML::BeginSeq;
|
||||
out << YAML::BeginMap << YAML::Key << "i" << YAML::Value << 0 << YAML::EndMap
|
||||
<< YAML::Newline;
|
||||
out << YAML::BeginMap << YAML::Key << "i" << YAML::Value << 1 << YAML::EndMap
|
||||
<< YAML::Newline;
|
||||
out << YAML::EndSeq;
|
||||
out << YAML::EndMap;
|
||||
|
||||
out << YAML::EndMap;
|
||||
|
||||
ExpectEmit(R"(NodeA:
|
||||
k: [{i: 0},
|
||||
{i: 1},
|
||||
]
|
||||
NodeB:
|
||||
k: [{i: 0},
|
||||
{i: 1},
|
||||
])");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapWithNewLineUsingAliases) {
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "Node" << Anchor("Node") << Value << BeginMap;
|
||||
out << Key << "k" << Value << Flow << BeginSeq;
|
||||
out << BeginMap << Key << "i" << Value << 0 << EndMap;
|
||||
out << YAML::Newline;
|
||||
out << BeginMap << Key << "i" << Value << 1 << EndMap;
|
||||
out << YAML::Newline;
|
||||
out << EndSeq << EndMap;
|
||||
|
||||
out << Key << "NodeA" << Alias("Node");
|
||||
out << Key << "NodeB" << Alias("Node");
|
||||
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(Node: &Node
|
||||
k: [{i: 0},
|
||||
{i: 1},
|
||||
]
|
||||
NodeA: *Node
|
||||
NodeB: *Node)");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapUsingAliases) {
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "Node" << Anchor("Node") << Value << BeginMap;
|
||||
out << Key << "k" << Value << Flow << BeginSeq;
|
||||
out << BeginMap << Key << "i" << Value << 0 << EndMap;
|
||||
out << BeginMap << Key << "i" << Value << 1 << EndMap;
|
||||
out << EndSeq << EndMap;
|
||||
|
||||
out << Key << "NodeA" << Alias("Node");
|
||||
out << Key << "NodeB" << Alias("Node");
|
||||
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(Node: &Node
|
||||
k: [{i: 0}, {i: 1}]
|
||||
NodeA: *Node
|
||||
NodeB: *Node)");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapWithNewLineUsingAliases2) {
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "Seq" << Anchor("Seq") << Flow << BeginSeq;
|
||||
out << BeginMap << Key << "i" << Value << 0 << EndMap;
|
||||
out << YAML::Newline;
|
||||
out << BeginMap << Key << "i" << Value << 1 << EndMap;
|
||||
out << YAML::Newline;
|
||||
out << EndSeq;
|
||||
|
||||
out << Key << "NodeA" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Alias("Seq") << EndMap;
|
||||
out << Key << "NodeB" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Alias("Seq") << EndMap;
|
||||
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(Seq: &Seq [{i: 0},
|
||||
{i: 1},
|
||||
]
|
||||
NodeA:
|
||||
k: *Seq
|
||||
NodeB:
|
||||
k: *Seq)");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapUsingAliases2) {
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "Seq" << Anchor("Seq") << Value << Flow << BeginSeq;
|
||||
out << BeginMap << Key << "i" << Value << 0 << EndMap;
|
||||
out << BeginMap << Key << "i" << Value << 1 << EndMap;
|
||||
out << EndSeq;
|
||||
|
||||
out << Key << "NodeA" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Alias("Seq") << EndMap;
|
||||
out << Key << "NodeB" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Alias("Seq") << EndMap;
|
||||
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(Seq: &Seq [{i: 0}, {i: 1}]
|
||||
NodeA:
|
||||
k: *Seq
|
||||
NodeB:
|
||||
k: *Seq)");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapWithNewLineUsingAliases3) {
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "Keys" << Value << Flow << BeginSeq;
|
||||
out << Anchor("k0") << BeginMap << Key << "i" << Value << 0 << EndMap
|
||||
<< Newline;
|
||||
out << Anchor("k1") << BeginMap << Key << "i" << Value << 1 << EndMap
|
||||
<< Newline;
|
||||
out << EndSeq;
|
||||
|
||||
out << Key << "NodeA" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Flow << BeginSeq;
|
||||
out << Alias("k0") << Newline << Alias("k1") << Newline;
|
||||
out << EndSeq << EndMap;
|
||||
|
||||
out << Key << "NodeB" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Flow << BeginSeq;
|
||||
out << Alias("k0") << Newline << Alias("k1") << Newline;
|
||||
out << EndSeq << EndMap;
|
||||
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(Keys: [&k0 {i: 0},
|
||||
&k1 {i: 1},
|
||||
]
|
||||
NodeA:
|
||||
k: [*k0,
|
||||
*k1,
|
||||
]
|
||||
NodeB:
|
||||
k: [*k0,
|
||||
*k1,
|
||||
])");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapUsingAliases3a) {
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "Keys" << Value << BeginSeq;
|
||||
out << Anchor("k0") << BeginMap << Key << "i" << Value << 0 << EndMap;
|
||||
out << Anchor("k1") << BeginMap << Key << "i" << Value << 1 << EndMap;
|
||||
out << EndSeq;
|
||||
|
||||
out << Key << "NodeA" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Flow << BeginSeq;
|
||||
out << Alias("k0") << Alias("k1");
|
||||
out << EndSeq << EndMap;
|
||||
|
||||
out << Key << "NodeB" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Flow << BeginSeq;
|
||||
out << Alias("k0") << Alias("k1");
|
||||
out << EndSeq << EndMap;
|
||||
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(Keys:
|
||||
- &k0
|
||||
i: 0
|
||||
- &k1
|
||||
i: 1
|
||||
NodeA:
|
||||
k: [*k0, *k1]
|
||||
NodeB:
|
||||
k: [*k0, *k1])");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, ComplexFlowSeqEmbeddingAMapUsingAliases3b) {
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "Keys" << Value << Flow << BeginSeq;
|
||||
out << Anchor("k0") << BeginMap << Key << "i" << Value << 0 << EndMap;
|
||||
out << Anchor("k1") << BeginMap << Key << "i" << Value << 1 << EndMap;
|
||||
out << EndSeq;
|
||||
|
||||
out << Key << "NodeA" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Flow << BeginSeq;
|
||||
out << Alias("k0") << Alias("k1");
|
||||
out << EndSeq << EndMap;
|
||||
|
||||
out << Key << "NodeB" << Value << BeginMap;
|
||||
out << Key << "k" << Value << Flow << BeginSeq;
|
||||
out << Alias("k0") << Alias("k1");
|
||||
out << EndSeq << EndMap;
|
||||
|
||||
out << EndMap;
|
||||
|
||||
ExpectEmit(R"(Keys: [&k0 {i: 0}, &k1 {i: 1}]
|
||||
NodeA:
|
||||
k: [*k0, *k1]
|
||||
NodeB:
|
||||
k: [*k0, *k1])");
|
||||
}
|
||||
|
||||
class EmitterErrorTest : public ::testing::Test {
|
||||
protected:
|
||||
void ExpectEmitError(const std::string& expectedError) {
|
||||
@@ -1041,6 +1632,26 @@ TEST_F(EmitterErrorTest, BadLocalTag) {
|
||||
ExpectEmitError("invalid tag");
|
||||
}
|
||||
|
||||
TEST_F(EmitterErrorTest, BadTagAndTag) {
|
||||
out << VerbatimTag("!far") << VerbatimTag("!foo") << "bar";
|
||||
ExpectEmitError(ErrorMsg::INVALID_TAG);
|
||||
}
|
||||
|
||||
TEST_F(EmitterErrorTest, BadAnchorAndAnchor) {
|
||||
out << Anchor("far") << Anchor("foo") << "bar";
|
||||
ExpectEmitError(ErrorMsg::INVALID_ANCHOR);
|
||||
}
|
||||
|
||||
TEST_F(EmitterErrorTest, BadEmptyAnchorOnGroup) {
|
||||
out << BeginSeq << "bar" << Anchor("foo") << EndSeq;
|
||||
ExpectEmitError(ErrorMsg::INVALID_ANCHOR);
|
||||
}
|
||||
|
||||
TEST_F(EmitterErrorTest, BadEmptyTagOnGroup) {
|
||||
out << BeginSeq << "bar" << VerbatimTag("!foo") << EndSeq;
|
||||
ExpectEmitError(ErrorMsg::INVALID_TAG);
|
||||
}
|
||||
|
||||
TEST_F(EmitterErrorTest, ExtraEndSeq) {
|
||||
out << BeginSeq;
|
||||
out << "Hello";
|
||||
|
@@ -347,7 +347,72 @@ TEST_F(HandlerSpecTest, Ex2_18_MultiLineFlowScalars) {
|
||||
Parse(ex2_18);
|
||||
}
|
||||
|
||||
// TODO: 2.19 - 2.22 schema tags
|
||||
TEST_F(HandlerSpecTest, Ex2_19_Integers) {
|
||||
EXPECT_CALL(handler, OnDocumentStart(_));
|
||||
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "canonical"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "12345"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "decimal"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "+12345"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "octal"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "0o14"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "hexadecimal"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "0xC"));
|
||||
EXPECT_CALL(handler, OnMapEnd());
|
||||
EXPECT_CALL(handler, OnDocumentEnd());
|
||||
Parse(ex2_19);
|
||||
}
|
||||
|
||||
TEST_F(HandlerSpecTest, Ex2_20_FloatingPoint) {
|
||||
EXPECT_CALL(handler, OnDocumentStart(_));
|
||||
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "canonical"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "1.23015e+3"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "exponential"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "12.3015e+02"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "fixed"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "1230.15"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "negative infinity"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "-.inf"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "not a number"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, ".NaN"));
|
||||
EXPECT_CALL(handler, OnMapEnd());
|
||||
EXPECT_CALL(handler, OnDocumentEnd());
|
||||
Parse(ex2_20);
|
||||
}
|
||||
|
||||
TEST_F(HandlerSpecTest, Ex2_21_Miscellaneous) {
|
||||
EXPECT_CALL(handler, OnDocumentStart(_));
|
||||
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
|
||||
EXPECT_CALL(handler, OnNull(_, 0));
|
||||
EXPECT_CALL(handler, OnNull(_, 0));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "booleans"));
|
||||
EXPECT_CALL(handler, OnSequenceStart(_, "?", 0, EmitterStyle::Flow));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "true"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "false"));
|
||||
EXPECT_CALL(handler, OnSequenceEnd());
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "string"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "!", 0, "012345"));
|
||||
EXPECT_CALL(handler, OnMapEnd());
|
||||
EXPECT_CALL(handler, OnDocumentEnd());
|
||||
Parse(ex2_21);
|
||||
}
|
||||
|
||||
TEST_F(HandlerSpecTest, Ex2_22_Timestamps) {
|
||||
EXPECT_CALL(handler, OnDocumentStart(_));
|
||||
EXPECT_CALL(handler, OnMapStart(_, "?", 0, EmitterStyle::Block));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "canonical"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-12-15T02:59:43.1Z"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "iso8601"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-12-14t21:59:43.10-05:00"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "spaced"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "2001-12-14 21:59:43.10 -5"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "date"));
|
||||
EXPECT_CALL(handler, OnScalar(_, "?", 0, "2002-12-14"));
|
||||
EXPECT_CALL(handler, OnMapEnd());
|
||||
EXPECT_CALL(handler, OnDocumentEnd());
|
||||
Parse(ex2_22);
|
||||
}
|
||||
|
||||
TEST_F(HandlerSpecTest, Ex2_23_VariousExplicitTags) {
|
||||
EXPECT_CALL(handler, OnDocumentStart(_));
|
||||
|
@@ -21,17 +21,35 @@ TEST(LoadNodeTest, FallbackValues) {
|
||||
}
|
||||
|
||||
TEST(LoadNodeTest, NumericConversion) {
|
||||
Node node = Load("[1.5, 1, .nan, .inf, -.inf, 0x15, 015]");
|
||||
EXPECT_EQ(1.5f, node[0].as<float>());
|
||||
EXPECT_EQ(1.5, node[0].as<double>());
|
||||
EXPECT_THROW(node[0].as<int>(), TypedBadConversion<int>);
|
||||
EXPECT_EQ(1, node[1].as<int>());
|
||||
EXPECT_EQ(1.0f, node[1].as<float>());
|
||||
EXPECT_NE(node[2].as<float>(), node[2].as<float>());
|
||||
EXPECT_EQ(std::numeric_limits<float>::infinity(), node[3].as<float>());
|
||||
EXPECT_EQ(-std::numeric_limits<float>::infinity(), node[4].as<float>());
|
||||
EXPECT_EQ(21, node[5].as<int>());
|
||||
EXPECT_EQ(13, node[6].as<int>());
|
||||
EXPECT_EQ(1.5f, Load("1.5").as<float>());
|
||||
EXPECT_EQ(1.5, Load("1.5").as<double>());
|
||||
EXPECT_THROW(Load("1.5").as<int>(), TypedBadConversion<int>);
|
||||
EXPECT_EQ(1, Load("1").as<int>());
|
||||
EXPECT_EQ(1.0f, Load("1").as<float>());
|
||||
EXPECT_NE(Load(".nan").as<float>(), Load(".nan").as<float>());
|
||||
EXPECT_EQ(std::numeric_limits<float>::infinity(), Load(".inf").as<float>());
|
||||
EXPECT_EQ(-std::numeric_limits<float>::infinity(), Load("-.inf").as<float>());
|
||||
EXPECT_EQ(21, Load("0x15").as<int>());
|
||||
EXPECT_EQ(13, Load("015").as<int>());
|
||||
EXPECT_EQ(-128, +Load("-128").as<int8_t>());
|
||||
EXPECT_EQ(127, +Load("127").as<int8_t>());
|
||||
EXPECT_THROW(Load("128").as<int8_t>(), TypedBadConversion<signed char>);
|
||||
EXPECT_EQ(255, +Load("255").as<uint8_t>());
|
||||
EXPECT_THROW(Load("256").as<uint8_t>(), TypedBadConversion<unsigned char>);
|
||||
// test as<char>/as<uint8_t> with ‘a’,"ab",'1',"127"
|
||||
EXPECT_EQ('a', Load("a").as<char>());
|
||||
EXPECT_THROW(Load("ab").as<char>(), TypedBadConversion<char>);
|
||||
EXPECT_EQ('1', Load("1").as<char>());
|
||||
EXPECT_THROW(Load("127").as<char>(), TypedBadConversion<char>);
|
||||
EXPECT_THROW(Load("a").as<uint8_t>(), TypedBadConversion<unsigned char>);
|
||||
EXPECT_THROW(Load("ab").as<uint8_t>(), TypedBadConversion<unsigned char>);
|
||||
EXPECT_EQ(1, +Load("1").as<uint8_t>());
|
||||
// Throw exception: convert a negative number to an unsigned number.
|
||||
EXPECT_THROW(Load("-128").as<unsigned>(), TypedBadConversion<unsigned int>);
|
||||
EXPECT_THROW(Load("-128").as<unsigned short>(), TypedBadConversion<unsigned short>);
|
||||
EXPECT_THROW(Load("-128").as<unsigned long>(), TypedBadConversion<unsigned long>);
|
||||
EXPECT_THROW(Load("-128").as<unsigned long long>(), TypedBadConversion<unsigned long long>);
|
||||
EXPECT_THROW(Load("-128").as<uint8_t>(), TypedBadConversion<unsigned char>);
|
||||
}
|
||||
|
||||
TEST(LoadNodeTest, Binary) {
|
||||
@@ -236,6 +254,57 @@ TEST(NodeTest, IncompleteJson) {
|
||||
{"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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SingleNodeTestCase {
|
||||
std::string input;
|
||||
NodeType::value nodeType;
|
||||
int nodeSize;
|
||||
std::string expected_content;
|
||||
};
|
||||
|
||||
TEST(NodeTest, SpecialFlow) {
|
||||
std::vector<SingleNodeTestCase> tests = {
|
||||
{"[:]", NodeType::Sequence, 1, "[{~: ~}]"},
|
||||
{"[a:]", NodeType::Sequence, 1, "[{a: ~}]"},
|
||||
{"[:a]", NodeType::Sequence, 1, "[:a]"},
|
||||
{"[,]", NodeType::Sequence, 1, "[~]"},
|
||||
{"[a:,]", NodeType::Sequence, 1, "[{a: ~}]"},
|
||||
{"{:}", NodeType::Map, 1, "{~: ~}"},
|
||||
{"{a:}", NodeType::Map, 1, "{a: ~}"},
|
||||
{"{:a}", NodeType::Map, 1, "{:a: ~}"},
|
||||
{"{,}", NodeType::Map, 1, "{~: ~}"},
|
||||
{"{a:,}", NodeType::Map, 1, "{a: ~}"},
|
||||
//testcase for the trailing TAB of scalar
|
||||
{"key\t: value\t", NodeType::Map, 1, "key: value"},
|
||||
{"key\t: value\t #comment", NodeType::Map, 1, "key: value"},
|
||||
{"{key\t: value\t}", NodeType::Map, 1, "{key: value}"},
|
||||
{"{key\t: value\t #comment\n}", NodeType::Map, 1, "{key: value}"},
|
||||
};
|
||||
for (const SingleNodeTestCase& test : tests) {
|
||||
Node node = Load(test.input);
|
||||
Emitter emitter;
|
||||
emitter << node;
|
||||
EXPECT_EQ(test.nodeType, node.Type());
|
||||
EXPECT_EQ(test.nodeSize, node.size());
|
||||
EXPECT_EQ(test.expected_content, std::string(emitter.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NodeTest, IncorrectFlow) {
|
||||
std::vector<ParserExceptionTestCase> tests = {
|
||||
{"Incorrect yaml: \"{:]\"", "{:]", ErrorMsg::FLOW_END},
|
||||
{"Incorrect yaml: \"[:}\"", "[:}", ErrorMsg::FLOW_END},
|
||||
};
|
||||
for (const ParserExceptionTestCase test : tests) {
|
||||
try {
|
||||
Load(test.input);
|
||||
@@ -250,8 +319,21 @@ TEST(NodeTest, IncompleteJson) {
|
||||
TEST(NodeTest, LoadTildeAsNull) {
|
||||
Node node = Load("~");
|
||||
ASSERT_TRUE(node.IsNull());
|
||||
EXPECT_EQ(node.as<std::string>(), "null");
|
||||
EXPECT_EQ(node.as<std::string>("~"), "null");
|
||||
}
|
||||
|
||||
|
||||
TEST(NodeTest, LoadNullWithStrTag) {
|
||||
Node node = Load("!!str null");
|
||||
EXPECT_EQ(node.Tag(), "tag:yaml.org,2002:str");
|
||||
EXPECT_EQ(node.as<std::string>(), "null");
|
||||
}
|
||||
|
||||
TEST(NodeTest, LoadQuotedNull) {
|
||||
Node node = Load("\"null\"");
|
||||
EXPECT_EQ(node.as<std::string>(), "null");
|
||||
}
|
||||
|
||||
TEST(NodeTest, LoadTagWithParenthesis) {
|
||||
Node node = Load("!Complex(Tag) foo");
|
||||
EXPECT_EQ(node.Tag(), "!Complex(Tag)");
|
||||
|
@@ -1127,5 +1127,10 @@ TEST(NodeSpecTest, Ex8_22_BlockCollectionNodes) {
|
||||
EXPECT_EQ(1, doc["mapping"].size());
|
||||
EXPECT_EQ("bar", doc["mapping"]["foo"].as<std::string>());
|
||||
}
|
||||
|
||||
TEST(NodeSpecTest, FlowMapNotClosed) {
|
||||
EXPECT_THROW_PARSER_EXCEPTION(Load("{x:"), ErrorMsg::UNKNOWN_TOKEN);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,40 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
|
||||
// malloc/free based allocator just for testing custom allocators on stl containers
|
||||
template <class T>
|
||||
class CustomAllocator : public std::allocator<T> {
|
||||
public:
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef T value_type;
|
||||
template<class U> struct rebind { typedef CustomAllocator<U> other; };
|
||||
CustomAllocator() : std::allocator<T>() {}
|
||||
CustomAllocator(const CustomAllocator& other) : std::allocator<T>(other) {}
|
||||
template<class U> CustomAllocator(const CustomAllocator<U>& other) : std::allocator<T>(other) {}
|
||||
~CustomAllocator() {}
|
||||
size_type max_size() const { return (std::numeric_limits<std::ptrdiff_t>::max)()/sizeof(T); }
|
||||
pointer allocate(size_type num, const void* /*hint*/ = 0) {
|
||||
if (num > std::size_t(-1) / sizeof(T)) throw std::bad_alloc();
|
||||
return static_cast<pointer>(malloc(num * sizeof(T)));
|
||||
}
|
||||
void deallocate(pointer p, size_type /*num*/) { free(p); }
|
||||
};
|
||||
|
||||
template <class T> using CustomVector = std::vector<T,CustomAllocator<T>>;
|
||||
template <class T> using CustomList = std::list<T,CustomAllocator<T>>;
|
||||
template <class K, class V, class C=std::less<K>> using CustomMap = std::map<K,V,C,CustomAllocator<std::pair<const K,V>>>;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
using ::testing::AnyOf;
|
||||
using ::testing::Eq;
|
||||
|
||||
@@ -135,6 +169,20 @@ TEST(NodeTest, NodeAssignment) {
|
||||
EXPECT_EQ(node1[3], node2[3]);
|
||||
}
|
||||
|
||||
TEST(NodeTest, EqualRepresentationAfterMoveAssignment) {
|
||||
Node node1;
|
||||
Node node2;
|
||||
std::ostringstream ss1, ss2;
|
||||
node1["foo"] = "bar";
|
||||
ss1 << node1;
|
||||
node2["hello"] = "world";
|
||||
node2 = std::move(node1);
|
||||
ss2 << node2;
|
||||
EXPECT_FALSE(node2["hello"]);
|
||||
EXPECT_EQ("bar", node2["foo"].as<std::string>());
|
||||
EXPECT_EQ(ss1.str(), ss2.str());
|
||||
}
|
||||
|
||||
TEST(NodeTest, MapElementRemoval) {
|
||||
Node node;
|
||||
node["foo"] = "bar";
|
||||
@@ -317,6 +365,20 @@ TEST(NodeTest, StdVector) {
|
||||
EXPECT_EQ(primes, node["primes"].as<std::vector<int>>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdVectorWithCustomAllocator) {
|
||||
CustomVector<int> primes;
|
||||
primes.push_back(2);
|
||||
primes.push_back(3);
|
||||
primes.push_back(5);
|
||||
primes.push_back(7);
|
||||
primes.push_back(11);
|
||||
primes.push_back(13);
|
||||
|
||||
Node node;
|
||||
node["primes"] = primes;
|
||||
EXPECT_EQ(primes, node["primes"].as<CustomVector<int>>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdList) {
|
||||
std::list<int> primes;
|
||||
primes.push_back(2);
|
||||
@@ -331,6 +393,20 @@ TEST(NodeTest, StdList) {
|
||||
EXPECT_EQ(primes, node["primes"].as<std::list<int>>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdListWithCustomAllocator) {
|
||||
CustomList<int> primes;
|
||||
primes.push_back(2);
|
||||
primes.push_back(3);
|
||||
primes.push_back(5);
|
||||
primes.push_back(7);
|
||||
primes.push_back(11);
|
||||
primes.push_back(13);
|
||||
|
||||
Node node;
|
||||
node["primes"] = primes;
|
||||
EXPECT_EQ(primes, node["primes"].as<CustomList<int>>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdMap) {
|
||||
std::map<int, int> squares;
|
||||
squares[0] = 0;
|
||||
@@ -345,6 +421,20 @@ TEST(NodeTest, StdMap) {
|
||||
EXPECT_EQ(squares, actualSquares);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdMapWithCustomAllocator) {
|
||||
CustomMap<int,int> squares;
|
||||
squares[0] = 0;
|
||||
squares[1] = 1;
|
||||
squares[2] = 4;
|
||||
squares[3] = 9;
|
||||
squares[4] = 16;
|
||||
|
||||
Node node;
|
||||
node["squares"] = squares;
|
||||
CustomMap<int,int> actualSquares = node["squares"].as<CustomMap<int,int>>();
|
||||
EXPECT_EQ(squares, actualSquares);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdPair) {
|
||||
std::pair<int, std::string> p;
|
||||
p.first = 5;
|
||||
|
@@ -1,9 +1,12 @@
|
||||
#include <yaml-cpp/depthguard.h>
|
||||
#include "yaml-cpp/parser.h"
|
||||
#include "yaml-cpp/exceptions.h"
|
||||
#include "mock_event_handler.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using YAML::Parser;
|
||||
using YAML::MockEventHandler;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::StrictMock;
|
||||
|
||||
TEST(ParserTest, Empty) {
|
||||
@@ -14,3 +17,48 @@ TEST(ParserTest, Empty) {
|
||||
StrictMock<MockEventHandler> handler;
|
||||
EXPECT_FALSE(parser.HandleNextDocument(handler));
|
||||
}
|
||||
|
||||
TEST(ParserTest, CVE_2017_5950) {
|
||||
std::string excessive_recursion;
|
||||
for (auto i = 0; i != 16384; ++i)
|
||||
excessive_recursion.push_back('[');
|
||||
std::istringstream input{excessive_recursion};
|
||||
Parser parser{input};
|
||||
|
||||
NiceMock<MockEventHandler> handler;
|
||||
EXPECT_THROW(parser.HandleNextDocument(handler), YAML::DeepRecursion);
|
||||
}
|
||||
|
||||
TEST(ParserTest, CVE_2018_20573) {
|
||||
std::string excessive_recursion;
|
||||
for (auto i = 0; i != 20535; ++i)
|
||||
excessive_recursion.push_back('{');
|
||||
std::istringstream input{excessive_recursion};
|
||||
Parser parser{input};
|
||||
|
||||
NiceMock<MockEventHandler> handler;
|
||||
EXPECT_THROW(parser.HandleNextDocument(handler), YAML::DeepRecursion);
|
||||
}
|
||||
|
||||
TEST(ParserTest, CVE_2018_20574) {
|
||||
std::string excessive_recursion;
|
||||
for (auto i = 0; i != 21989; ++i)
|
||||
excessive_recursion.push_back('{');
|
||||
std::istringstream input{excessive_recursion};
|
||||
Parser parser{input};
|
||||
|
||||
NiceMock<MockEventHandler> handler;
|
||||
EXPECT_THROW(parser.HandleNextDocument(handler), YAML::DeepRecursion);
|
||||
}
|
||||
|
||||
TEST(ParserTest, CVE_2019_6285) {
|
||||
std::string excessive_recursion;
|
||||
for (auto i = 0; i != 23100; ++i)
|
||||
excessive_recursion.push_back('[');
|
||||
excessive_recursion.push_back('f');
|
||||
std::istringstream input{excessive_recursion};
|
||||
Parser parser{input};
|
||||
|
||||
NiceMock<MockEventHandler> handler;
|
||||
EXPECT_THROW(parser.HandleNextDocument(handler), YAML::DeepRecursion);
|
||||
}
|
||||
|
@@ -154,7 +154,29 @@ const char *ex2_18 =
|
||||
"quoted: \"So does this\n"
|
||||
" quoted scalar.\\n\"";
|
||||
|
||||
// TODO: 2.19 - 2.22 schema tags
|
||||
const char *ex2_19 =
|
||||
"canonical: 12345\n"
|
||||
"decimal: +12345\n"
|
||||
"octal: 0o14\n"
|
||||
"hexadecimal: 0xC\n";
|
||||
|
||||
const char *ex2_20 =
|
||||
"canonical: 1.23015e+3\n"
|
||||
"exponential: 12.3015e+02\n"
|
||||
"fixed: 1230.15\n"
|
||||
"negative infinity: -.inf\n"
|
||||
"not a number: .NaN\n";
|
||||
|
||||
const char *ex2_21 =
|
||||
"null:\n"
|
||||
"booleans: [ true, false ]\n"
|
||||
"string: '012345'\n";
|
||||
|
||||
const char *ex2_22 =
|
||||
"canonical: 2001-12-15T02:59:43.1Z\n"
|
||||
"iso8601: 2001-12-14t21:59:43.10-05:00\n"
|
||||
"spaced: 2001-12-14 21:59:43.10 -5\n"
|
||||
"date: 2002-12-14\n";
|
||||
|
||||
const char *ex2_23 =
|
||||
"---\n"
|
||||
|
@@ -8,23 +8,23 @@
|
||||
|
||||
class NullEventHandler : public YAML::EventHandler {
|
||||
public:
|
||||
typedef YAML::Mark Mark;
|
||||
typedef YAML::anchor_t anchor_t;
|
||||
using Mark = YAML::Mark;
|
||||
using anchor_t = YAML::anchor_t;
|
||||
|
||||
NullEventHandler() {}
|
||||
NullEventHandler() = default;
|
||||
|
||||
virtual void OnDocumentStart(const Mark&) {}
|
||||
virtual void OnDocumentEnd() {}
|
||||
virtual void OnNull(const Mark&, anchor_t) {}
|
||||
virtual void OnAlias(const Mark&, anchor_t) {}
|
||||
virtual void OnScalar(const Mark&, const std::string&, anchor_t,
|
||||
const std::string&) {}
|
||||
virtual void OnSequenceStart(const Mark&, const std::string&, anchor_t,
|
||||
YAML::EmitterStyle::value style) {}
|
||||
virtual void OnSequenceEnd() {}
|
||||
virtual void OnMapStart(const Mark&, const std::string&, anchor_t,
|
||||
YAML::EmitterStyle::value style) {}
|
||||
virtual void OnMapEnd() {}
|
||||
void OnDocumentStart(const Mark&) override {}
|
||||
void OnDocumentEnd() override {}
|
||||
void OnNull(const Mark&, anchor_t) override {}
|
||||
void OnAlias(const Mark&, anchor_t) override {}
|
||||
void OnScalar(const Mark&, const std::string&, anchor_t,
|
||||
const std::string&) override {}
|
||||
void OnSequenceStart(const Mark&, const std::string&, anchor_t,
|
||||
YAML::EmitterStyle::value style) override {}
|
||||
void OnSequenceEnd() override {}
|
||||
void OnMapStart(const Mark&, const std::string&, anchor_t,
|
||||
YAML::EmitterStyle::value style) override {}
|
||||
void OnMapEnd() override {}
|
||||
};
|
||||
|
||||
void run(std::istream& in) {
|
||||
@@ -68,14 +68,14 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (N > 1 && !cache && filename == "") {
|
||||
if (N > 1 && !cache && filename.empty()) {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
std::string input;
|
||||
if (filename != "") {
|
||||
if (!filename.empty()) {
|
||||
std::ifstream in(filename);
|
||||
input = read_stream(in);
|
||||
} else {
|
||||
@@ -87,7 +87,7 @@ int main(int argc, char** argv) {
|
||||
run(in);
|
||||
}
|
||||
} else {
|
||||
if (filename != "") {
|
||||
if (!filename.empty()) {
|
||||
std::ifstream in(filename);
|
||||
for (int i = 0; i < N; i++) {
|
||||
in.seekg(std::ios_base::beg);
|
||||
|
@@ -6,23 +6,23 @@
|
||||
|
||||
class NullEventHandler : public YAML::EventHandler {
|
||||
public:
|
||||
typedef YAML::Mark Mark;
|
||||
typedef YAML::anchor_t anchor_t;
|
||||
using Mark = YAML::Mark;
|
||||
using anchor_t = YAML::anchor_t;
|
||||
|
||||
NullEventHandler() {}
|
||||
NullEventHandler() = default;
|
||||
|
||||
virtual void OnDocumentStart(const Mark&) {}
|
||||
virtual void OnDocumentEnd() {}
|
||||
virtual void OnNull(const Mark&, anchor_t) {}
|
||||
virtual void OnAlias(const Mark&, anchor_t) {}
|
||||
virtual void OnScalar(const Mark&, const std::string&, anchor_t,
|
||||
const std::string&) {}
|
||||
virtual void OnSequenceStart(const Mark&, const std::string&, anchor_t,
|
||||
YAML::EmitterStyle::value style) {}
|
||||
virtual void OnSequenceEnd() {}
|
||||
virtual void OnMapStart(const Mark&, const std::string&, anchor_t,
|
||||
YAML::EmitterStyle::value style) {}
|
||||
virtual void OnMapEnd() {}
|
||||
void OnDocumentStart(const Mark&) override {}
|
||||
void OnDocumentEnd() override {}
|
||||
void OnNull(const Mark&, anchor_t) override {}
|
||||
void OnAlias(const Mark&, anchor_t) override {}
|
||||
void OnScalar(const Mark&, const std::string&, anchor_t,
|
||||
const std::string&) override {}
|
||||
void OnSequenceStart(const Mark&, const std::string&, anchor_t,
|
||||
YAML::EmitterStyle::value style) override {}
|
||||
void OnSequenceEnd() override {}
|
||||
void OnMapStart(const Mark&, const std::string&, anchor_t,
|
||||
YAML::EmitterStyle::value style) override {}
|
||||
void OnMapEnd() override {}
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
Reference in New Issue
Block a user