Compare commits

..

68 Commits

Author SHA1 Message Date
Jesse Beder
326899815f Added overloads for parsing stl maps and vectors 2010-03-15 04:25:17 +00:00
Jesse Beder
083a97b171 Set alias nodes to return the tag of their anchor 2010-03-15 04:10:36 +00:00
Jesse Beder
2226987442 Disabled those warnings in the release version of the .svn too. 2010-03-03 05:33:07 +00:00
Jesse Beder
bca7737463 Updated to remove most of the warnings in Visual Studio. (There's still the one about all control paths returning a value left.) Fixed one warning (when an istream converts to void * to then convert to bool), and disabled three. 2010-03-03 05:30:06 +00:00
Jesse Beder
6f40b09525 Added newline to install and license files 2009-12-21 20:35:27 +00:00
Jesse Beder
3a755de572 Added missing include 2009-12-02 05:59:18 +00:00
Jesse Beder
9718e58120 Added test for duplicate key 2009-12-02 01:29:16 +00:00
Jesse Beder
8723b8f358 Fixed leak when adding duplicate keys (and actually changed the behavior - now we take the first instance, not the last) 2009-12-02 01:01:45 +00:00
Jesse Beder
03df73a7b0 Refactored emitter operator << overloads to not template them, so it's easier to overload for pointer types 2009-11-17 20:21:22 +00:00
Jesse Beder
3307f0941c Refactored the traits a bit, and added displaying the key to string and numeric key not found errors 2009-11-12 17:00:12 +00:00
Jesse Beder
54b68230ae Small bug from switching static initialized regexes to lazy ones 2009-11-12 05:45:47 +00:00
Jesse Beder
32491166ac Replaced conversion macros with SFINAE 2009-11-10 21:23:52 +00:00
Jesse Beder
6f94f954bb Overloaded more integral types for emitting 2009-11-06 03:24:12 +00:00
Jesse Beder
90fd24d149 Fixed the return value of the integral conversion functions, and also unset the dec flag so it reads other bases (just a temporary fix, since we're officially supposed to read binary too) 2009-11-06 03:13:54 +00:00
Jesse Beder
9a21a3ec8d Switched the Exp:: regexes to functions that lazily evaluate their regexes 2009-11-04 22:56:59 +00:00
Jesse Beder
3779e4255d Fixed silly bug in node cloning 2009-10-30 20:29:14 +00:00
Jesse Beder
ec62dc547e Added some block scalar tests (with errors) 2009-10-30 18:16:26 +00:00
Jesse Beder
a9b9e1ccec Updated the Visual Studio solution for the new files/renaming. 2009-10-30 04:52:13 +00:00
Jesse Beder
e04be7890a Fixed bug with block maps with null value (the next key was being read as the value) 2009-10-30 01:06:19 +00:00
Jesse Beder
ecb30132e9 Fixed the whitespace tracking when we escape a newline in a double-quoted string 2009-10-29 22:55:50 +00:00
Jesse Beder
52be1ccfb9 Fixed mistake in test 2009-10-29 22:39:53 +00:00
Jesse Beder
3405a6fe01 Refactored the compact map notation, which made it easy to implement explicit keys for compact maps 2009-10-29 22:09:50 +00:00
Jesse Beder
d372729b92 Added case for parsing a compact key: value pair in a flow sequence with a null key 2009-10-29 22:01:01 +00:00
Jesse Beder
fadc2ad39f Implemented adjacent key:value pairs when the key is JSON-like 2009-10-29 21:05:48 +00:00
Jesse Beder
a5607f82a3 Added test 2009-10-29 20:45:20 +00:00
Jesse Beder
f4c683ac22 Added flow collection tests 2009-10-29 20:35:07 +00:00
Jesse Beder
8c9c9d90da Added ability to read compact maps in a flow sequence 2009-10-29 19:41:46 +00:00
Jesse Beder
a372bfdc60 Merged r295:305 from the tags branch to the trunk 2009-10-29 15:48:06 +00:00
Jesse Beder
fe57829aca Removed crt stuff (we can do memory leak checking in Linux easier) 2009-10-27 14:55:01 +00:00
Jesse Beder
b5c53d9e3a Removed unused test yaml file 2009-10-27 14:48:01 +00:00
Jesse Beder
f2a2d25ec0 Now actually removed yaml-reader 2009-10-27 14:47:08 +00:00
Jesse Beder
a706ffaf62 Reverted yaml-reader name change 2009-10-27 14:45:14 +00:00
Jesse Beder
8f48e693fe Renamed yaml-reader test (try 2) 2009-10-27 14:39:48 +00:00
Jesse Beder
a0bf12e7a1 Renamed yaml-reader test 2009-10-27 14:38:53 +00:00
Jesse Beder
2314c04d5d Tagged version 0.2.4 2009-10-25 20:27:31 +00:00
Jesse Beder
22410f46f5 Updated the CMake globbing so it only compiles sources starting with a lowercase letter (apparently Mac OS auto-generates files looking like ._whatever and it was trying to compile those too) 2009-10-25 18:01:48 +00:00
Jesse Beder
9559a661aa Tagged version 0.2.3 2009-10-22 21:55:44 +00:00
Jesse Beder
beb524489c Small refactoring 2009-10-22 21:51:32 +00:00
Jesse Beder
4ffb93c12b Switch to flow map when emitting an empty block map 2009-10-22 14:21:12 +00:00
Jesse Beder
ae06a40fe6 Switch to flow sequence when emitting an empty sequence 2009-10-22 14:17:12 +00:00
Jesse Beder
315b00065b Fixed bug in plain scalar folding 2009-10-20 14:47:16 +00:00
Jesse Beder
6f02f7556e Added a bunch of tests, simplified the testing code 2009-10-20 14:43:24 +00:00
Jesse Beder
fa0af88dfe Merged r270:HEAD of the emitting-unicode branch 2009-10-19 23:31:11 +00:00
Jesse Beder
bce845bb1f Fixed little bug in parser commit 2009-10-19 22:42:30 +00:00
Jesse Beder
ed570b9f7c Added default constructor to Parser, and cleaned it up a bit 2009-10-19 22:40:46 +00:00
Jesse Beder
59b0e986bf Update CMakeLists.txt to append, not overwrite CMAKE_CXX_FLAGS 2009-10-19 22:32:26 +00:00
Jesse Beder
cffb98d15b Patched for optional building of tests and tools 2009-10-12 05:21:00 +00:00
Jesse Beder
3e1ba0f3b4 Refactored the UTF-8 emitting 2009-10-08 21:05:56 +00:00
Jesse Beder
d0b5bf4b7b Fixed the emitter unicode output 2009-10-07 06:46:05 +00:00
Jesse Beder
7db39e66b8 Updated signature of Parser::GetNextDocument (issue 45) 2009-09-29 18:25:11 +00:00
Jesse Beder
94eb7f1dbd Modified old gcc version patch so it still uses the new Node::Read in Visual Studio. Also broke up the \uNNNN characters in the spec tests into \xNN-type strings. 2009-09-16 05:31:28 +00:00
Jesse Beder
5733b77b84 Patched for gcc version <= 3.3 (just fall back to original version of Node::Read) 2009-09-16 04:01:40 +00:00
Jesse Beder
98bebfb628 Tagged version 0.2.2 2009-09-09 01:37:23 +00:00
Jesse Beder
7c4cc9bf5f Cleaned up the read template overloads (per litb's update); it seems the old version didn't compile in VS2008. Also updated the VS project files. 2009-09-08 20:57:18 +00:00
Jesse Beder
fa885d1813 More tests, found bug in implicit keys in flow sequence 2009-09-08 05:35:39 +00:00
Jesse Beder
da4614eb8b Fixed flow folding, and made the separation slightly cleaner (but the whole scanscalar thing could use a major refactoring) 2009-09-08 05:24:06 +00:00
Jesse Beder
4dcd222d1f Tests through 6.29, skipping directives and tags 2009-09-08 04:16:45 +00:00
Jesse Beder
7bdd31b34b (Actually) fixed the folding newline bug, but it's a bit messy, and we don't accurately make the distinction between block folding and flow folding 2009-09-07 23:29:04 +00:00
Jesse Beder
a4b8521efe Fixed newlines in folded scalars bug 2009-09-07 22:48:32 +00:00
Jesse Beder
7037562998 Simplified testing output 2009-09-07 22:17:02 +00:00
Jesse Beder
f3ff6ffc55 Fixed bugs with tab as non-content whitespace 2009-09-07 17:12:45 +00:00
Jesse Beder
e3ff87ecde Fixed bugs in escape characters (both parsing and emitting) 2009-09-07 16:31:23 +00:00
Jesse Beder
45ac700fff Fixed error in test 2009-09-07 06:56:05 +00:00
Jesse Beder
2aab5acab4 Fixed last newline of folded scalar bug 2009-09-07 06:54:38 +00:00
Jesse Beder
e9d760eea9 Fixed bug in trailing newlines of plain scalars 2009-09-07 06:42:03 +00:00
Jesse Beder
d485d0a834 Added spec tests (minus tags, directives, and BOM) up through example 5.12 - this exposed an error in line folding 2009-09-07 06:35:37 +00:00
Jesse Beder
973ac4b3bd Added spec tests through example 2.13 2009-09-06 22:17:53 +00:00
Jesse Beder
e91a152e06 Tagged release 0.2.1 for patch with complex keys 2009-09-06 22:02:59 +00:00
74 changed files with 4608 additions and 1449 deletions

View File

@@ -10,16 +10,23 @@ if(IPHONE)
endif(IPHONE) endif(IPHONE)
if(CMAKE_COMPILER_IS_GNUCC) if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_CXX_FLAGS "-O2 -Wall -pedantic -Wextra") set(CMAKE_CXX_FLAGS "-O2 -Wall -Wextra -pedantic -Wno-long-long ${CMAKE_CXX_FLAGS}")
endif(CMAKE_COMPILER_IS_GNUCC) endif(CMAKE_COMPILER_IS_GNUCC)
if(MSVC)
set(LIB_TYPE) # I can't figure out how CMake handles Windows shared libraries
set(CMAKE_CXX_FLAGS "/W3 /wd4127 /wd4355 /D_SCL_SECURE_NO_WARNINGS ${CMAKE_CXX_FLAGS}")
endif(MSVC)
set(YAML_CPP_VERSION_MAJOR "0") set(YAML_CPP_VERSION_MAJOR "0")
set(YAML_CPP_VERSION_MINOR "2") set(YAML_CPP_VERSION_MINOR "2")
set(YAML_CPP_VERSION_PATCH "0") set(YAML_CPP_VERSION_PATCH "4")
set(YAML_CPP_VERSION "${YAML_CPP_VERSION_MAJOR}.${YAML_CPP_VERSION_MINOR}.${YAML_CPP_VERSION_PATCH}") set(YAML_CPP_VERSION "${YAML_CPP_VERSION_MAJOR}.${YAML_CPP_VERSION_MINOR}.${YAML_CPP_VERSION_PATCH}")
enable_testing() enable_testing()
option(YAML_CPP_BUILD_TOOLS "Enables or disables testing and parse tools" true)
if(WIN32) if(WIN32)
set(_library_dir bin) # .dll are in PATH, like executables set(_library_dir bin) # .dll are in PATH, like executables
else(WIN32) else(WIN32)
@@ -36,9 +43,9 @@ set(_INSTALL_DESTINATIONS
ARCHIVE DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX}
) )
# #
file(GLOB public_headers include/*.h) file(GLOB public_headers include/[a-z]*.h)
file(GLOB private_headers src/*.h) file(GLOB private_headers src/[a-z]*.h)
file(GLOB sources src/*.cpp) file(GLOB sources src/[a-z]*.cpp)
include_directories(${YAML_CPP_SOURCE_DIR}/include) include_directories(${YAML_CPP_SOURCE_DIR}/include)
add_library(yaml-cpp add_library(yaml-cpp
@@ -64,5 +71,7 @@ if(UNIX)
install(FILES ${PC_FILE} DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) install(FILES ${PC_FILE} DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
endif(UNIX) endif(UNIX)
add_subdirectory (yaml-reader) if(YAML_CPP_BUILD_TOOLS)
add_subdirectory (util) add_subdirectory (test)
add_subdirectory (util)
endif(YAML_CPP_BUILD_TOOLS)

View File

@@ -5,6 +5,7 @@
#include "null.h" #include "null.h"
#include "traits.h"
#include <string> #include <string>
#include <sstream> #include <sstream>
@@ -18,26 +19,13 @@ namespace YAML
bool Convert(const std::string& input, bool& output); bool Convert(const std::string& input, bool& output);
bool Convert(const std::string& input, _Null& output); bool Convert(const std::string& input, _Null& output);
#define YAML_MAKE_STREAM_CONVERT(type) \ template <typename T>
inline bool Convert(const std::string& input, type& output) { \ inline bool Convert(const std::string& input, T& output, typename enable_if<is_numeric<T> >::type * = 0) {
std::stringstream stream(input); \ std::stringstream stream(input);
stream >> output; \ stream.unsetf(std::ios::dec);
return !stream.fail(); \ stream >> output;
return !!stream;
} }
YAML_MAKE_STREAM_CONVERT(char)
YAML_MAKE_STREAM_CONVERT(unsigned char)
YAML_MAKE_STREAM_CONVERT(int)
YAML_MAKE_STREAM_CONVERT(unsigned int)
YAML_MAKE_STREAM_CONVERT(short)
YAML_MAKE_STREAM_CONVERT(unsigned short)
YAML_MAKE_STREAM_CONVERT(long)
YAML_MAKE_STREAM_CONVERT(unsigned long)
YAML_MAKE_STREAM_CONVERT(float)
YAML_MAKE_STREAM_CONVERT(double)
YAML_MAKE_STREAM_CONVERT(long double)
#undef YAML_MAKE_STREAM_CONVERT
} }
#endif // CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #endif // CONVERSION_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -1,17 +0,0 @@
#pragma once
#ifndef CRT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define CRT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
// for detecting memory leaks
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif // _DEBUG
#endif // CRT_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -9,6 +9,7 @@
#include "null.h" #include "null.h"
#include <memory> #include <memory>
#include <string> #include <string>
#include <sstream>
namespace YAML namespace YAML
{ {
@@ -29,6 +30,7 @@ namespace YAML
const std::string GetLastError() const; const std::string GetLastError() const;
// global setters // global setters
bool SetOutputCharset(EMITTER_MANIP value);
bool SetStringFormat(EMITTER_MANIP value); bool SetStringFormat(EMITTER_MANIP value);
bool SetBoolFormat(EMITTER_MANIP value); bool SetBoolFormat(EMITTER_MANIP value);
bool SetIntBase(EMITTER_MANIP value); bool SetIntBase(EMITTER_MANIP value);
@@ -44,16 +46,23 @@ namespace YAML
// overloads of write // overloads of write
Emitter& Write(const std::string& str); Emitter& Write(const std::string& str);
Emitter& Write(const char *str);
Emitter& Write(int i);
Emitter& Write(bool b); Emitter& Write(bool b);
Emitter& Write(float f);
Emitter& Write(double d);
Emitter& Write(const _Alias& alias); Emitter& Write(const _Alias& alias);
Emitter& Write(const _Anchor& anchor); Emitter& Write(const _Anchor& anchor);
Emitter& Write(const _Tag& tag);
Emitter& Write(const _Comment& comment); Emitter& Write(const _Comment& comment);
Emitter& Write(const _Null& null); Emitter& Write(const _Null& null);
template <typename T>
Emitter& WriteIntegralType(T value);
template <typename T>
Emitter& WriteStreamable(T value);
private:
void PreWriteIntegralType(std::stringstream& str);
void PostWriteIntegralType(const std::stringstream& str);
private: private:
enum ATOMIC_TYPE { AT_SCALAR, AT_SEQ, AT_BLOCK_SEQ, AT_FLOW_SEQ, AT_MAP, AT_BLOCK_MAP, AT_FLOW_MAP }; enum ATOMIC_TYPE { AT_SCALAR, AT_SEQ, AT_BLOCK_SEQ, AT_FLOW_SEQ, AT_MAP, AT_BLOCK_MAP, AT_FLOW_MAP };
@@ -74,18 +83,61 @@ namespace YAML
std::auto_ptr <EmitterState> m_pState; std::auto_ptr <EmitterState> m_pState;
}; };
// overloads of insertion
template <typename T> template <typename T>
inline Emitter& operator << (Emitter& emitter, T v) { inline Emitter& Emitter::WriteIntegralType(T value)
return emitter.Write(v); {
if(!good())
return *this;
std::stringstream str;
PreWriteIntegralType(str);
str << value;
PostWriteIntegralType(str);
return *this;
} }
template <> template <typename T>
inline Emitter& Emitter::WriteStreamable(T value)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
std::stringstream str;
str << value;
m_stream << str.str();
PostAtomicWrite();
return *this;
}
// overloads of insertion
inline Emitter& operator << (Emitter& emitter, const std::string& v) { return emitter.Write(v); }
inline Emitter& operator << (Emitter& emitter, bool v) { return emitter.Write(v); }
inline Emitter& operator << (Emitter& emitter, const _Alias& v) { return emitter.Write(v); }
inline Emitter& operator << (Emitter& emitter, const _Anchor& v) { return emitter.Write(v); }
inline Emitter& operator << (Emitter& emitter, const _Tag& v) { return emitter.Write(v); }
inline Emitter& operator << (Emitter& emitter, const _Comment& v) { return emitter.Write(v); }
inline Emitter& operator << (Emitter& emitter, const _Null& v) { return emitter.Write(v); }
inline Emitter& operator << (Emitter& emitter, const char *v) { return emitter.Write(std::string(v)); }
inline Emitter& operator << (Emitter& emitter, int v) { return emitter.WriteIntegralType(v); }
inline Emitter& operator << (Emitter& emitter, unsigned int v) { return emitter.WriteIntegralType(v); }
inline Emitter& operator << (Emitter& emitter, short v) { return emitter.WriteIntegralType(v); }
inline Emitter& operator << (Emitter& emitter, unsigned short v) { return emitter.WriteIntegralType(v); }
inline Emitter& operator << (Emitter& emitter, long v) { return emitter.WriteIntegralType(v); }
inline Emitter& operator << (Emitter& emitter, unsigned long v) { return emitter.WriteIntegralType(v); }
inline Emitter& operator << (Emitter& emitter, float v) { return emitter.WriteStreamable(v); }
inline Emitter& operator << (Emitter& emitter, double v) { return emitter.WriteStreamable(v); }
inline Emitter& operator << (Emitter& emitter, EMITTER_MANIP value) { inline Emitter& operator << (Emitter& emitter, EMITTER_MANIP value) {
return emitter.SetLocalValue(value); return emitter.SetLocalValue(value);
} }
template <>
inline Emitter& operator << (Emitter& emitter, _Indent indent) { inline Emitter& operator << (Emitter& emitter, _Indent indent) {
return emitter.SetLocalIndent(indent); return emitter.SetLocalIndent(indent);
} }

View File

@@ -11,6 +11,10 @@ namespace YAML
enum EMITTER_MANIP { enum EMITTER_MANIP {
// general manipulators // general manipulators
Auto, Auto,
// output character set
EmitNonAscii,
EscapeNonAscii,
// string manipulators // string manipulators
// Auto, // duplicate // Auto, // duplicate
@@ -76,6 +80,16 @@ namespace YAML
inline _Anchor Anchor(const std::string content) { inline _Anchor Anchor(const std::string content) {
return _Anchor(content); return _Anchor(content);
} }
struct _Tag {
_Tag(const std::string& content_): content(content_), verbatim(true) {}
std::string content;
bool verbatim;
};
inline _Tag VerbatimTag(const std::string& content) {
return _Tag(content);
}
struct _Comment { struct _Comment {
_Comment(const std::string& content_): content(content_) {} _Comment(const std::string& content_): content(content_) {}

View File

@@ -5,6 +5,7 @@
#include "mark.h" #include "mark.h"
#include "traits.h"
#include <exception> #include <exception>
#include <string> #include <string>
#include <sstream> #include <sstream>
@@ -17,7 +18,12 @@ namespace YAML
const std::string YAML_DIRECTIVE_ARGS = "YAML directives must have exactly one argument"; const std::string YAML_DIRECTIVE_ARGS = "YAML directives must have exactly one argument";
const std::string YAML_VERSION = "bad YAML version: "; const std::string YAML_VERSION = "bad YAML version: ";
const std::string YAML_MAJOR_VERSION = "YAML major version too large"; const std::string YAML_MAJOR_VERSION = "YAML major version too large";
const std::string REPEATED_YAML_DIRECTIVE= "repeated YAML directive";
const std::string TAG_DIRECTIVE_ARGS = "TAG directives must have exactly two arguments"; const std::string TAG_DIRECTIVE_ARGS = "TAG directives must have exactly two arguments";
const std::string REPEATED_TAG_DIRECTIVE = "repeated TAG directive";
const std::string CHAR_IN_TAG_HANDLE = "illegal character found while scanning tag handle";
const std::string TAG_WITH_NO_SUFFIX = "tag handle with no suffix";
const std::string END_OF_VERBATIM_TAG = "end of verbatim tag not found";
const std::string END_OF_MAP = "end of map not found"; const std::string END_OF_MAP = "end of map not found";
const std::string END_OF_MAP_FLOW = "end of map flow not found"; const std::string END_OF_MAP_FLOW = "end of map flow not found";
const std::string END_OF_SEQ = "end of sequence not found"; const std::string END_OF_SEQ = "end of sequence not found";
@@ -57,10 +63,27 @@ namespace YAML
const std::string SINGLE_QUOTED_CHAR = "invalid character in single-quoted string"; const std::string SINGLE_QUOTED_CHAR = "invalid character in single-quoted string";
const std::string INVALID_ANCHOR = "invalid anchor"; const std::string INVALID_ANCHOR = "invalid anchor";
const std::string INVALID_ALIAS = "invalid alias"; const std::string INVALID_ALIAS = "invalid alias";
const std::string INVALID_TAG = "invalid tag";
const std::string EXPECTED_KEY_TOKEN = "expected key token"; const std::string EXPECTED_KEY_TOKEN = "expected key token";
const std::string EXPECTED_VALUE_TOKEN = "expected value token"; const std::string EXPECTED_VALUE_TOKEN = "expected value token";
const std::string UNEXPECTED_KEY_TOKEN = "unexpected key token"; const std::string UNEXPECTED_KEY_TOKEN = "unexpected key token";
const std::string UNEXPECTED_VALUE_TOKEN = "unexpected value token"; const std::string UNEXPECTED_VALUE_TOKEN = "unexpected value token";
template <typename T>
inline const std::string KEY_NOT_FOUND_WITH_KEY(const T&, typename disable_if<is_numeric<T> >::type * = 0) {
return KEY_NOT_FOUND;
}
inline const std::string KEY_NOT_FOUND_WITH_KEY(const std::string& key) {
return KEY_NOT_FOUND + ": " + key;
}
template <typename T>
inline const std::string KEY_NOT_FOUND_WITH_KEY(const T& key, typename enable_if<is_numeric<T> >::type * = 0) {
std::stringstream stream;
stream << KEY_NOT_FOUND << ": " << key;
return stream.str();
}
} }
class Exception: public std::exception { class Exception: public std::exception {
@@ -102,22 +125,23 @@ namespace YAML
class KeyNotFound: public RepresentationException { class KeyNotFound: public RepresentationException {
public: public:
KeyNotFound(const Mark& mark_) template <typename T>
: RepresentationException(mark_, ErrorMsg::KEY_NOT_FOUND) {} KeyNotFound(const Mark& mark_, const T& key_)
: RepresentationException(mark_, ErrorMsg::KEY_NOT_FOUND_WITH_KEY(key_)) {}
}; };
template <typename T> template <typename T>
class TypedKeyNotFound: public KeyNotFound { class TypedKeyNotFound: public KeyNotFound {
public: public:
TypedKeyNotFound(const Mark& mark_, const T& key_) TypedKeyNotFound(const Mark& mark_, const T& key_)
: KeyNotFound(mark_), key(key_) {} : KeyNotFound(mark_, key_), key(key_) {}
~TypedKeyNotFound() throw() {} virtual ~TypedKeyNotFound() throw() {}
T key; T key;
}; };
template <typename T> template <typename T>
TypedKeyNotFound <T> MakeTypedKeyNotFound(const Mark& mark, const T& key) { inline TypedKeyNotFound <T> MakeTypedKeyNotFound(const Mark& mark, const T& key) {
return TypedKeyNotFound <T> (mark, key); return TypedKeyNotFound <T> (mark, key);
} }

View File

@@ -9,7 +9,6 @@
#include "iterator.h" #include "iterator.h"
#include "mark.h" #include "mark.h"
#include "noncopyable.h" #include "noncopyable.h"
#include "parserstate.h"
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -21,6 +20,7 @@ namespace YAML
class Content; class Content;
class Scanner; class Scanner;
class Emitter; class Emitter;
struct ParserState;
enum CONTENT_TYPE { CT_NONE, CT_SCALAR, CT_SEQUENCE, CT_MAP }; enum CONTENT_TYPE { CT_NONE, CT_SCALAR, CT_SEQUENCE, CT_MAP };
@@ -32,7 +32,7 @@ namespace YAML
void Clear(); void Clear();
std::auto_ptr<Node> Clone() const; std::auto_ptr<Node> Clone() const;
void Parse(Scanner *pScanner, const ParserState& state); void Parse(Scanner *pScanner, ParserState& state);
CONTENT_TYPE GetType() const; CONTENT_TYPE GetType() const;
@@ -75,6 +75,9 @@ namespace YAML
const Node *Identity() const { return m_pIdentity; } const Node *Identity() const { return m_pIdentity; }
bool IsAlias() const { return m_alias; } bool IsAlias() const { return m_alias; }
bool IsReferenced() const { return m_referenced; } bool IsReferenced() const { return m_referenced; }
// for tags
const std::string GetTag() const { return IsAlias() ? m_pIdentity->GetTag() : m_tag; }
// emitting // emitting
friend Emitter& operator << (Emitter& out, const Node& node); friend Emitter& operator << (Emitter& out, const Node& node);
@@ -99,10 +102,10 @@ namespace YAML
Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent); Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent);
// helpers for parsing // helpers for parsing
void ParseHeader(Scanner *pScanner, const ParserState& state); void ParseHeader(Scanner *pScanner, ParserState& state);
void ParseTag(Scanner *pScanner, const ParserState& state); void ParseTag(Scanner *pScanner, ParserState& state);
void ParseAnchor(Scanner *pScanner, const ParserState& state); void ParseAnchor(Scanner *pScanner, ParserState& state);
void ParseAlias(Scanner *pScanner, const ParserState& state); void ParseAlias(Scanner *pScanner, ParserState& state);
private: private:
Mark m_mark; Mark m_mark;

View File

@@ -79,22 +79,22 @@ namespace YAML
template <typename T> template <typename T>
inline bool operator == (const T& value, const Node& node) { inline bool operator == (const T& value, const Node& node) {
return value == node.Read<T>(); return value == node.operator T();
} }
template <typename T> template <typename T>
inline bool operator == (const Node& node, const T& value) { inline bool operator == (const Node& node, const T& value) {
return value == node.Read<T>(); return value == node.operator T();
} }
template <typename T> template <typename T>
inline bool operator != (const T& value, const Node& node) { inline bool operator != (const T& value, const Node& node) {
return value != node.Read<T>(); return value != node.operator T();
} }
template <typename T> template <typename T>
inline bool operator != (const Node& node, const T& value) { inline bool operator != (const Node& node, const T& value) {
return value != node.Read<T>(); return value != node.operator T();
} }
inline bool operator == (const char *value, const Node& node) { inline bool operator == (const char *value, const Node& node) {

View File

@@ -6,7 +6,18 @@ namespace YAML
// (the goal is to call ConvertScalar if we can, and fall back to operator >> if not) // (the goal is to call ConvertScalar if we can, and fall back to operator >> if not)
// thanks to litb from stackoverflow.com // thanks to litb from stackoverflow.com
// http://stackoverflow.com/questions/1386183/how-to-call-a-templated-function-if-it-exists-and-something-else-otherwise/1386390#1386390 // http://stackoverflow.com/questions/1386183/how-to-call-a-templated-function-if-it-exists-and-something-else-otherwise/1386390#1386390
// Note: this doesn't work on gcc 3.2, but does on gcc 3.4 and above. I'm not sure about 3.3.
#if __GNUC__ && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ <= 3))
// trick doesn't work? Just fall back to ConvertScalar.
// This means that we can't use any user-defined types as keys in a map
template <typename T>
inline bool Node::Read(T& value) const {
return ConvertScalar(*this, value);
}
#else
// usual case: the trick!
template<bool> template<bool>
struct read_impl; struct read_impl;
@@ -38,12 +49,13 @@ namespace YAML
struct flag { char c[2]; }; struct flag { char c[2]; };
flag Convert(...); flag Convert(...);
char (& operator,(flag, flag) )[1]; int operator,(flag, flag);
template<typename T> template<typename T>
void operator,(flag, T const&); char operator,(flag, T const&);
char (& operator,(char(&)[1], flag) )[1]; char operator,(int, flag);
int operator,(char, flag);
} }
template <typename T> template <typename T>
@@ -52,6 +64,7 @@ namespace YAML
return read_impl<sizeof (fallback::flag(), Convert(std::string(), value), fallback::flag()) != 1>::read(*this, value); return read_impl<sizeof (fallback::flag(), Convert(std::string(), value), fallback::flag()) != 1>::read(*this, value);
} }
#endif // done with trick
// the main conversion function // the main conversion function
template <typename T> template <typename T>

View File

@@ -4,44 +4,42 @@
#define PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #define PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include "node.h"
#include "noncopyable.h"
#include <ios> #include <ios>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include "node.h" #include <memory>
#include "parserstate.h"
namespace YAML namespace YAML
{ {
class Scanner; class Scanner;
struct ParserState;
struct Token; struct Token;
class Parser class Parser: private noncopyable
{ {
public: public:
Parser();
Parser(std::istream& in); Parser(std::istream& in);
~Parser(); ~Parser();
operator bool() const; operator bool() const;
void Load(std::istream& in); void Load(std::istream& in);
void GetNextDocument(Node& document); bool GetNextDocument(Node& document);
void PrintTokens(std::ostream& out); void PrintTokens(std::ostream& out);
private: private:
void ParseDirectives(); void ParseDirectives();
void HandleDirective(Token *pToken); void HandleDirective(const Token& token);
void HandleYamlDirective(Token *pToken); void HandleYamlDirective(const Token& token);
void HandleTagDirective(Token *pToken); void HandleTagDirective(const Token& token);
private: private:
// can't copy this std::auto_ptr<Scanner> m_pScanner;
Parser(const Parser&) {} std::auto_ptr<ParserState> m_pState;
Parser& operator = (const Parser&) { return *this; }
private:
Scanner *m_pScanner;
ParserState m_state;
}; };
} }

View File

@@ -1,26 +0,0 @@
#pragma once
#ifndef PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include <string>
#include <map>
namespace YAML
{
struct Version {
int major, minor;
};
struct ParserState
{
Version version;
std::map <std::string, std::string> tags;
void Reset();
std::string TranslateTag(const std::string& handle) const;
};
}
#endif // PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66

36
include/stlnode.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#ifndef STLNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define STLNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include <vector>
#include <map>
namespace YAML
{
template <typename T>
void operator >> (const Node& node, std::vector<T>& v)
{
v.clear();
v.resize(node.size());
for(unsigned i=0;i<node.size();++i)
node[i] >> v[i];
}
template <typename K, typename V>
void operator >> (const Node& node, std::map<K, V>& m)
{
m.clear();
for(Iterator it=node.begin();it!=node.end();++it) {
K k;
V v;
it.first() >> k;
it.second() >> v;
m[k] = v;
}
}
}
#endif // STLNODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66

50
include/traits.h Normal file
View File

@@ -0,0 +1,50 @@
#pragma once
#ifndef TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
namespace YAML
{
template <typename>
struct is_numeric { enum { value = false }; };
template <> struct is_numeric <char> { enum { value = true }; };
template <> struct is_numeric <unsigned char> { enum { value = true }; };
template <> struct is_numeric <int> { enum { value = true }; };
template <> struct is_numeric <unsigned int> { enum { value = true }; };
template <> struct is_numeric <long int> { enum { value = true }; };
template <> struct is_numeric <unsigned long int> { enum { value = true }; };
template <> struct is_numeric <short int> { enum { value = true }; };
template <> struct is_numeric <unsigned short int> { enum { value = true }; };
template <> struct is_numeric <long long> { enum { value = true }; };
template <> struct is_numeric <unsigned long long> { enum { value = true }; };
template <> struct is_numeric <float> { enum { value = true }; };
template <> struct is_numeric <double> { enum { value = true }; };
template <> struct is_numeric <long double> { enum { value = true }; };
template <bool, class T = void>
struct enable_if_c {
typedef T type;
};
template <class T>
struct enable_if_c<false, T> {};
template <class Cond, class T = void>
struct enable_if : public enable_if_c<Cond::value, T> {};
template <bool, class T = void>
struct disable_if_c {
typedef T type;
};
template <class T>
struct disable_if_c<true, T> {};
template <class Cond, class T = void>
struct disable_if : public disable_if_c<Cond::value, T> {};
}
#endif // TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -4,9 +4,9 @@
#define YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #define YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include "crt.h"
#include "parser.h" #include "parser.h"
#include "node.h" #include "node.h"
#include "stlnode.h"
#include "iterator.h" #include "iterator.h"
#include "emitter.h" #include "emitter.h"
#include "stlemitter.h" #include "stlemitter.h"

View File

@@ -21,4 +21,4 @@ make install
If you don't want to use CMake, just add all .cpp files to a makefile. yaml-cpp does not need any special build settings, so no 'configure' file is necessary. If you don't want to use CMake, just add all .cpp files to a makefile. yaml-cpp does not need any special build settings, so no 'configure' file is necessary.
(Note: this is pretty tedious. It's sooo much easier to use CMake.) (Note: this is pretty tedious. It's sooo much easier to use CMake.)

View File

@@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.

View File

@@ -2,9 +2,9 @@
<VisualStudioProject <VisualStudioProject
ProjectType="Visual C++" ProjectType="Visual C++"
Version="9.00" Version="9.00"
Name="yaml-reader" Name="parse"
ProjectGUID="{E8CC0D8A-D784-4A6B-B78B-ACEA13F9FB0B}" ProjectGUID="{CD007B57-7812-4930-A5E2-6E5E56338814}"
RootNamespace="yamlreader" RootNamespace="parse"
TargetFrameworkVersion="196613" TargetFrameworkVersion="196613"
> >
<Platforms> <Platforms>
@@ -39,13 +39,15 @@
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalOptions="/D_SCL_SECURE_NO_WARNINGS"
Optimization="0" Optimization="0"
AdditionalIncludeDirectories="include" AdditionalIncludeDirectories="include"
MinimalRebuild="true" MinimalRebuild="true"
BasicRuntimeChecks="3" BasicRuntimeChecks="3"
RuntimeLibrary="3" RuntimeLibrary="3"
WarningLevel="3" WarningLevel="3"
DebugInformationFormat="3" DebugInformationFormat="4"
DisableSpecificWarnings="4127;4355"
/> />
<Tool <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@@ -110,13 +112,15 @@
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalOptions="/D_SCL_SECURE_NO_WARNINGS"
Optimization="2" Optimization="2"
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="include" AdditionalIncludeDirectories="include"
RuntimeLibrary="2" RuntimeLibrary="2"
EnableFunctionLevelLinking="true" EnableFunctionLevelLinking="true"
WarningLevel="4" WarningLevel="3"
DebugInformationFormat="3" DebugInformationFormat="3"
DisableSpecificWarnings="4127;4355"
/> />
<Tool <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@@ -168,19 +172,7 @@
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
> >
<File <File
RelativePath=".\yaml-reader\emittertests.cpp" RelativePath=".\util\parse.cpp"
>
</File>
<File
RelativePath=".\yaml-reader\main.cpp"
>
</File>
<File
RelativePath=".\yaml-reader\parsertests.cpp"
>
</File>
<File
RelativePath=".\yaml-reader\tests.cpp"
> >
</File> </File>
</Filter> </Filter>
@@ -189,10 +181,12 @@
Filter="h;hpp;hxx;hm;inl;inc;xsd" Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
> >
<File </Filter>
RelativePath=".\yaml-reader\tests.h" <Filter
> Name="Resource Files"
</File> Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter> </Filter>
</Files> </Files>
<Globals> <Globals>

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "aliascontent.h" #include "aliascontent.h"
namespace YAML namespace YAML
@@ -13,7 +12,7 @@ namespace YAML
return 0; // TODO: how to clone an alias? return 0; // TODO: how to clone an alias?
} }
void AliasContent::Parse(Scanner * /*pScanner*/, const ParserState& /*state*/) void AliasContent::Parse(Scanner * /*pScanner*/, ParserState& /*state*/)
{ {
} }

View File

@@ -15,7 +15,7 @@ namespace YAML
virtual Content *Clone() const; virtual Content *Clone() const;
virtual void Parse(Scanner* pScanner, const ParserState& state); virtual void Parse(Scanner* pScanner, ParserState& state);
virtual void Write(Emitter&) const; virtual void Write(Emitter&) const;
virtual bool GetBegin(std::vector <Node *>::const_iterator&) const; virtual bool GetBegin(std::vector <Node *>::const_iterator&) const;

View File

@@ -1,13 +0,0 @@
#include "crt.h"
#include "content.h"
namespace YAML
{
Content::Content()
{
}
Content::~Content()
{
}
}

View File

@@ -23,12 +23,12 @@ namespace YAML
class Content class Content
{ {
public: public:
Content(); Content() {}
virtual ~Content(); virtual ~Content() {}
virtual Content *Clone() const = 0; virtual Content *Clone() const = 0;
virtual void Parse(Scanner *pScanner, const ParserState& state) = 0; virtual void Parse(Scanner *pScanner, ParserState& state) = 0;
virtual void Write(Emitter& out) const = 0; virtual void Write(Emitter& out) const = 0;
virtual bool GetBegin(std::vector <Node *>::const_iterator&) const { return false; } virtual bool GetBegin(std::vector <Node *>::const_iterator&) const { return false; }

View File

@@ -37,6 +37,11 @@ namespace YAML
} }
// global setters // global setters
bool Emitter::SetOutputCharset(EMITTER_MANIP value)
{
return m_pState->SetOutputCharset(value, GLOBAL);
}
bool Emitter::SetStringFormat(EMITTER_MANIP value) bool Emitter::SetStringFormat(EMITTER_MANIP value)
{ {
return m_pState->SetStringFormat(value, GLOBAL); return m_pState->SetStringFormat(value, GLOBAL);
@@ -141,6 +146,8 @@ namespace YAML
switch(curState) { switch(curState) {
// document-level // document-level
case ES_WAITING_FOR_DOC: case ES_WAITING_FOR_DOC:
m_stream << "---";
m_pState->RequireSeparation();
m_pState->SwitchState(ES_WRITING_DOC); m_pState->SwitchState(ES_WRITING_DOC);
return true; return true;
case ES_WRITING_DOC: case ES_WRITING_DOC:
@@ -318,7 +325,10 @@ namespace YAML
EMITTER_STATE curState = m_pState->GetCurState(); EMITTER_STATE curState = m_pState->GetCurState();
EMITTER_MANIP flowType = m_pState->GetFlowType(GT_SEQ); EMITTER_MANIP flowType = m_pState->GetFlowType(GT_SEQ);
if(flowType == Block) { if(flowType == Block) {
if(curState == ES_WRITING_BLOCK_SEQ_ENTRY || curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE) { if(curState == ES_WRITING_BLOCK_SEQ_ENTRY ||
curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE ||
curState == ES_WRITING_DOC
) {
m_stream << "\n"; m_stream << "\n";
m_pState->UnsetSeparation(); m_pState->UnsetSeparation();
} }
@@ -344,12 +354,22 @@ namespace YAML
EMITTER_STATE curState = m_pState->GetCurState(); EMITTER_STATE curState = m_pState->GetCurState();
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
if(flowType == FT_BLOCK) if(flowType == FT_BLOCK) {
assert(curState == ES_DONE_WITH_BLOCK_SEQ_ENTRY); // Note: block sequences are *not* allowed to be empty, but we convert it
else if(flowType == FT_FLOW) { // to a flow sequence if it is
m_stream << "]"; assert(curState == ES_DONE_WITH_BLOCK_SEQ_ENTRY || curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY);
if(curState == ES_WAITING_FOR_BLOCK_SEQ_ENTRY) {
// Note: only one of these will actually output anything for a given situation
EmitSeparationIfNecessary();
unsigned curIndent = m_pState->GetCurIndent();
m_stream << IndentTo(curIndent);
m_stream << "[]";
}
} else if(flowType == FT_FLOW) {
// Note: flow sequences are allowed to be empty // Note: flow sequences are allowed to be empty
assert(curState == ES_DONE_WITH_FLOW_SEQ_ENTRY || curState == ES_WAITING_FOR_FLOW_SEQ_ENTRY); assert(curState == ES_DONE_WITH_FLOW_SEQ_ENTRY || curState == ES_WAITING_FOR_FLOW_SEQ_ENTRY);
m_stream << "]";
} else } else
assert(false); assert(false);
@@ -373,7 +393,10 @@ namespace YAML
EMITTER_STATE curState = m_pState->GetCurState(); EMITTER_STATE curState = m_pState->GetCurState();
EMITTER_MANIP flowType = m_pState->GetFlowType(GT_MAP); EMITTER_MANIP flowType = m_pState->GetFlowType(GT_MAP);
if(flowType == Block) { if(flowType == Block) {
if(curState == ES_WRITING_BLOCK_SEQ_ENTRY || curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE) { if(curState == ES_WRITING_BLOCK_SEQ_ENTRY ||
curState == ES_WRITING_BLOCK_MAP_KEY || curState == ES_WRITING_BLOCK_MAP_VALUE ||
curState == ES_WRITING_DOC
) {
m_stream << "\n"; m_stream << "\n";
m_pState->UnsetSeparation(); m_pState->UnsetSeparation();
} }
@@ -399,12 +422,21 @@ namespace YAML
EMITTER_STATE curState = m_pState->GetCurState(); EMITTER_STATE curState = m_pState->GetCurState();
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
if(flowType == FT_BLOCK) if(flowType == FT_BLOCK) {
assert(curState == ES_DONE_WITH_BLOCK_MAP_VALUE); // Note: block sequences are *not* allowed to be empty, but we convert it
else if(flowType == FT_FLOW) { // to a flow sequence if it is
m_stream << "}"; assert(curState == ES_DONE_WITH_BLOCK_MAP_VALUE || curState == ES_WAITING_FOR_BLOCK_MAP_ENTRY);
if(curState == ES_WAITING_FOR_BLOCK_MAP_ENTRY) {
// Note: only one of these will actually output anything for a given situation
EmitSeparationIfNecessary();
unsigned curIndent = m_pState->GetCurIndent();
m_stream << IndentTo(curIndent);
m_stream << "{}";
}
} else if(flowType == FT_FLOW) {
// Note: flow maps are allowed to be empty // Note: flow maps are allowed to be empty
assert(curState == ES_DONE_WITH_FLOW_MAP_VALUE || curState == ES_WAITING_FOR_FLOW_MAP_ENTRY); assert(curState == ES_DONE_WITH_FLOW_MAP_VALUE || curState == ES_WAITING_FOR_FLOW_MAP_ENTRY);
m_stream << "}";
} else } else
assert(false); assert(false);
@@ -485,13 +517,14 @@ namespace YAML
PreAtomicWrite(); PreAtomicWrite();
EmitSeparationIfNecessary(); EmitSeparationIfNecessary();
bool escapeNonAscii = m_pState->GetOutputCharset() == EscapeNonAscii;
EMITTER_MANIP strFmt = m_pState->GetStringFormat(); EMITTER_MANIP strFmt = m_pState->GetStringFormat();
FLOW_TYPE flowType = m_pState->GetCurGroupFlowType(); FLOW_TYPE flowType = m_pState->GetCurGroupFlowType();
unsigned curIndent = m_pState->GetCurIndent(); unsigned curIndent = m_pState->GetCurIndent();
switch(strFmt) { switch(strFmt) {
case Auto: case Auto:
Utils::WriteString(m_stream, str, flowType == FT_FLOW); Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii);
break; break;
case SingleQuoted: case SingleQuoted:
if(!Utils::WriteSingleQuotedString(m_stream, str)) { if(!Utils::WriteSingleQuotedString(m_stream, str)) {
@@ -500,11 +533,11 @@ namespace YAML
} }
break; break;
case DoubleQuoted: case DoubleQuoted:
Utils::WriteDoubleQuotedString(m_stream, str); Utils::WriteDoubleQuotedString(m_stream, str, escapeNonAscii);
break; break;
case Literal: case Literal:
if(flowType == FT_FLOW) if(flowType == FT_FLOW)
Utils::WriteString(m_stream, str, flowType == FT_FLOW); Utils::WriteString(m_stream, str, flowType == FT_FLOW, escapeNonAscii);
else else
Utils::WriteLiteralString(m_stream, str, curIndent + m_pState->GetIndent()); Utils::WriteLiteralString(m_stream, str, curIndent + m_pState->GetIndent());
break; break;
@@ -516,24 +549,12 @@ namespace YAML
return *this; return *this;
} }
Emitter& Emitter::Write(const char *str) void Emitter::PreWriteIntegralType(std::stringstream& str)
{ {
if(!good())
return *this;
return Write(std::string(str));
}
Emitter& Emitter::Write(int i)
{
if(!good())
return *this;
PreAtomicWrite(); PreAtomicWrite();
EmitSeparationIfNecessary(); EmitSeparationIfNecessary();
EMITTER_MANIP intFmt = m_pState->GetIntFormat(); EMITTER_MANIP intFmt = m_pState->GetIntFormat();
std::stringstream str;
switch(intFmt) { switch(intFmt) {
case Dec: case Dec:
str << std::dec; str << std::dec;
@@ -541,18 +562,18 @@ namespace YAML
case Hex: case Hex:
str << std::hex; str << std::hex;
break; break;
case Oct: case Oct:
str << std::oct; str << std::oct;
break; break;
default: default:
assert(false); assert(false);
} }
}
str << i;
void Emitter::PostWriteIntegralType(const std::stringstream& str)
{
m_stream << str.str(); m_stream << str.str();
PostAtomicWrite(); PostAtomicWrite();
return *this;
} }
Emitter& Emitter::Write(bool b) Emitter& Emitter::Write(bool b)
@@ -594,38 +615,6 @@ namespace YAML
return *this; return *this;
} }
Emitter& Emitter::Write(float f)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
std::stringstream str;
str << f;
m_stream << str.str();
PostAtomicWrite();
return *this;
}
Emitter& Emitter::Write(double d)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
std::stringstream str;
str << d;
m_stream << str.str();
PostAtomicWrite();
return *this;
}
Emitter& Emitter::Write(const _Alias& alias) Emitter& Emitter::Write(const _Alias& alias)
{ {
if(!good()) if(!good())
@@ -657,6 +646,22 @@ namespace YAML
return *this; return *this;
} }
Emitter& Emitter::Write(const _Tag& tag)
{
if(!good())
return *this;
PreAtomicWrite();
EmitSeparationIfNecessary();
if(!Utils::WriteTag(m_stream, tag.content)) {
m_pState->SetError(ErrorMsg::INVALID_TAG);
return *this;
}
m_pState->RequireSeparation();
// Note: no PostAtomicWrite() because we need another value for this node
return *this;
}
Emitter& Emitter::Write(const _Comment& comment) Emitter& Emitter::Write(const _Comment& comment)
{ {
if(!good()) if(!good())
@@ -679,3 +684,4 @@ namespace YAML
return *this; return *this;
} }
} }

View File

@@ -9,6 +9,7 @@ namespace YAML
m_stateStack.push(ES_WAITING_FOR_DOC); m_stateStack.push(ES_WAITING_FOR_DOC);
// set default global manipulators // set default global manipulators
m_charset.set(EmitNonAscii);
m_strFmt.set(Auto); m_strFmt.set(Auto);
m_boolFmt.set(TrueFalseBool); m_boolFmt.set(TrueFalseBool);
m_boolLengthFmt.set(LongBool); m_boolLengthFmt.set(LongBool);
@@ -43,6 +44,7 @@ namespace YAML
// . Only the ones that make sense will be accepted // . Only the ones that make sense will be accepted
void EmitterState::SetLocalValue(EMITTER_MANIP value) void EmitterState::SetLocalValue(EMITTER_MANIP value)
{ {
SetOutputCharset(value, LOCAL);
SetStringFormat(value, LOCAL); SetStringFormat(value, LOCAL);
SetBoolFormat(value, LOCAL); SetBoolFormat(value, LOCAL);
SetBoolCaseFormat(value, LOCAL); SetBoolCaseFormat(value, LOCAL);
@@ -132,6 +134,18 @@ namespace YAML
{ {
m_modifiedSettings.clear(); m_modifiedSettings.clear();
} }
bool EmitterState::SetOutputCharset(EMITTER_MANIP value, FMT_SCOPE scope)
{
switch(value) {
case EmitNonAscii:
case EscapeNonAscii:
_Set(m_charset, value, scope);
return true;
default:
return false;
}
}
bool EmitterState::SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope) bool EmitterState::SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope)
{ {

View File

@@ -108,6 +108,9 @@ namespace YAML
void ClearModifiedSettings(); void ClearModifiedSettings();
// formatters // formatters
bool SetOutputCharset(EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetOutputCharset() const { return m_charset.get(); }
bool SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope); bool SetStringFormat(EMITTER_MANIP value, FMT_SCOPE scope);
EMITTER_MANIP GetStringFormat() const { return m_strFmt.get(); } EMITTER_MANIP GetStringFormat() const { return m_strFmt.get(); }
@@ -149,6 +152,7 @@ namespace YAML
// other state // other state
std::stack <EMITTER_STATE> m_stateStack; std::stack <EMITTER_STATE> m_stateStack;
Setting <EMITTER_MANIP> m_charset;
Setting <EMITTER_MANIP> m_strFmt; Setting <EMITTER_MANIP> m_strFmt;
Setting <EMITTER_MANIP> m_boolFmt; Setting <EMITTER_MANIP> m_boolFmt;
Setting <EMITTER_MANIP> m_boolLengthFmt; Setting <EMITTER_MANIP> m_boolLengthFmt;

View File

@@ -11,13 +11,125 @@ namespace YAML
namespace Utils namespace Utils
{ {
namespace { namespace {
bool IsPrintable(char ch) { enum {REPLACEMENT_CHARACTER = 0xFFFD};
return (0x20 <= ch && ch <= 0x7E);
bool IsAnchorChar(int ch) { // test for ns-anchor-char
switch (ch) {
case ',': case '[': case ']': case '{': case '}': // c-flow-indicator
case ' ': case '\t': // s-white
case 0xFEFF: // c-byte-order-mark
case 0xA: case 0xD: // b-char
return false;
case 0x85:
return true;
}
if (ch < 0x20)
return false;
if (ch < 0x7E)
return true;
if (ch < 0xA0)
return false;
if (ch >= 0xD800 && ch <= 0xDFFF)
return false;
if ((ch & 0xFFFE) == 0xFFFE)
return false;
if ((ch >= 0xFDD0) && (ch <= 0xFDEF))
return false;
if (ch > 0x10FFFF)
return false;
return true;
} }
bool IsValidPlainScalar(const std::string& str, bool inFlow) { int Utf8BytesIndicated(char ch) {
int byteVal = static_cast<unsigned char>(ch);
switch (byteVal >> 4) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
return 1;
case 12: case 13:
return 2;
case 14:
return 3;
case 15:
return 4;
default:
return -1;
}
}
bool IsTrailingByte(char ch) {
return (ch & 0xC0) == 0x80;
}
bool GetNextCodePointAndAdvance(int& codePoint, std::string::const_iterator& first, std::string::const_iterator last) {
if (first == last)
return false;
int nBytes = Utf8BytesIndicated(*first);
if (nBytes < 1) {
// Bad lead byte
++first;
codePoint = REPLACEMENT_CHARACTER;
return true;
}
if (nBytes == 1) {
codePoint = *first++;
return true;
}
// Gather bits from trailing bytes
codePoint = static_cast<unsigned char>(*first) & ~(0xFF << (7 - nBytes));
++first;
--nBytes;
for (; nBytes > 0; ++first, --nBytes) {
if ((first == last) || !IsTrailingByte(*first)) {
codePoint = REPLACEMENT_CHARACTER;
break;
}
codePoint <<= 6;
codePoint |= *first & 0x3F;
}
// Check for illegal code points
if (codePoint > 0x10FFFF)
codePoint = REPLACEMENT_CHARACTER;
else if (codePoint >= 0xD800 && codePoint <= 0xDFFF)
codePoint = REPLACEMENT_CHARACTER;
else if ((codePoint & 0xFFFE) == 0xFFFE)
codePoint = REPLACEMENT_CHARACTER;
else if (codePoint >= 0xFDD0 && codePoint <= 0xFDEF)
codePoint = REPLACEMENT_CHARACTER;
return true;
}
void WriteCodePoint(ostream& out, int codePoint) {
if (codePoint < 0 || codePoint > 0x10FFFF) {
codePoint = REPLACEMENT_CHARACTER;
}
if (codePoint < 0x7F) {
out << static_cast<char>(codePoint);
} else if (codePoint < 0x7FF) {
out << static_cast<char>(0xC0 | (codePoint >> 6))
<< static_cast<char>(0x80 | (codePoint & 0x3F));
} else if (codePoint < 0xFFFF) {
out << static_cast<char>(0xE0 | (codePoint >> 12))
<< static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F))
<< static_cast<char>(0x80 | (codePoint & 0x3F));
} else {
out << static_cast<char>(0xF0 | (codePoint >> 18))
<< static_cast<char>(0x80 | ((codePoint >> 12) & 0x3F))
<< static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F))
<< static_cast<char>(0x80 | (codePoint & 0x3F));
}
}
bool IsValidPlainScalar(const std::string& str, bool inFlow, bool allowOnlyAscii) {
// first check the start // first check the start
const RegEx& start = (inFlow ? Exp::PlainScalarInFlow : Exp::PlainScalar); const RegEx& start = (inFlow ? Exp::PlainScalarInFlow() : Exp::PlainScalar());
if(!start.Matches(str)) if(!start.Matches(str))
return false; return false;
@@ -26,66 +138,111 @@ namespace YAML
return false; return false;
// then check until something is disallowed // then check until something is disallowed
const RegEx& disallowed = (inFlow ? Exp::EndScalarInFlow : Exp::EndScalar) const RegEx& disallowed = (inFlow ? Exp::EndScalarInFlow() : Exp::EndScalar())
|| (Exp::BlankOrBreak + Exp::Comment) || (Exp::BlankOrBreak() + Exp::Comment())
|| (!Exp::Printable) || Exp::NotPrintable()
|| Exp::Break || Exp::Utf8_ByteOrderMark()
|| Exp::Tab; || Exp::Break()
|| Exp::Tab();
StringCharSource buffer(str.c_str(), str.size()); StringCharSource buffer(str.c_str(), str.size());
while(buffer) { while(buffer) {
if(disallowed.Matches(buffer)) if(disallowed.Matches(buffer))
return false; return false;
if(allowOnlyAscii && (0x7F < static_cast<unsigned char>(buffer[0])))
return false;
++buffer; ++buffer;
} }
return true; return true;
} }
void WriteDoubleQuoteEscapeSequence(ostream& out, int codePoint) {
static const char hexDigits[] = "0123456789abcdef";
char escSeq[] = "\\U00000000";
int digits = 8;
if (codePoint < 0xFF) {
escSeq[1] = 'x';
digits = 2;
} else if (codePoint < 0xFFFF) {
escSeq[1] = 'u';
digits = 4;
}
// Write digits into the escape sequence
int i = 2;
for (; digits > 0; --digits, ++i) {
escSeq[i] = hexDigits[(codePoint >> (4 * (digits - 1))) & 0xF];
}
escSeq[i] = 0; // terminate with NUL character
out << escSeq;
}
bool WriteAliasName(ostream& out, const std::string& str) {
int codePoint;
for(std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end());
)
{
if (!IsAnchorChar(codePoint))
return false;
WriteCodePoint(out, codePoint);
}
return true;
}
} }
bool WriteString(ostream& out, const std::string& str, bool inFlow) bool WriteString(ostream& out, const std::string& str, bool inFlow, bool escapeNonAscii)
{ {
if(IsValidPlainScalar(str, inFlow)) { if(IsValidPlainScalar(str, inFlow, escapeNonAscii)) {
out << str; out << str;
return true; return true;
} else } else
return WriteDoubleQuotedString(out, str); return WriteDoubleQuotedString(out, str, escapeNonAscii);
} }
bool WriteSingleQuotedString(ostream& out, const std::string& str) bool WriteSingleQuotedString(ostream& out, const std::string& str)
{ {
out << "'"; out << "'";
for(std::size_t i=0;i<str.size();i++) { int codePoint;
char ch = str[i]; for(std::string::const_iterator i = str.begin();
if(!IsPrintable(ch)) GetNextCodePointAndAdvance(codePoint, i, str.end());
return false; )
{
if(ch == '\'') if (codePoint == '\n')
return false; // We can't handle a new line and the attendant indentation yet
if (codePoint == '\'')
out << "''"; out << "''";
else else
out << ch; WriteCodePoint(out, codePoint);
} }
out << "'"; out << "'";
return true; return true;
} }
bool WriteDoubleQuotedString(ostream& out, const std::string& str) bool WriteDoubleQuotedString(ostream& out, const std::string& str, bool escapeNonAscii)
{ {
out << "\""; out << "\"";
for(std::size_t i=0;i<str.size();i++) { int codePoint;
char ch = str[i]; for(std::string::const_iterator i = str.begin();
if(IsPrintable(ch)) { GetNextCodePointAndAdvance(codePoint, i, str.end());
if(ch == '\"') )
out << "\\\""; {
else if(ch == '\\') if (codePoint == '\"')
out << "\\\\"; out << "\\\"";
else else if (codePoint == '\\')
out << ch; out << "\\\\";
} else { else if (codePoint < 0x20 || (codePoint >= 0x80 && codePoint <= 0xA0)) // Control characters and non-breaking space
// TODO: for the common escaped characters, give their usual symbol WriteDoubleQuoteEscapeSequence(out, codePoint);
std::stringstream str; else if (codePoint == 0xFEFF) // Byte order marks (ZWNS) should be escaped (YAML 1.2, sec. 5.2)
str << "\\x" << std::hex << std::setfill('0') << std::setw(2) << static_cast <int>(ch); WriteDoubleQuoteEscapeSequence(out, codePoint);
out << str.str(); else if (escapeNonAscii && codePoint > 0x7E)
} WriteDoubleQuoteEscapeSequence(out, codePoint);
else
WriteCodePoint(out, codePoint);
} }
out << "\""; out << "\"";
return true; return true;
@@ -95,11 +252,15 @@ namespace YAML
{ {
out << "|\n"; out << "|\n";
out << IndentTo(indent); out << IndentTo(indent);
for(std::size_t i=0;i<str.size();i++) { int codePoint;
if(str[i] == '\n') for(std::string::const_iterator i = str.begin();
out << "\n" << IndentTo(indent); GetNextCodePointAndAdvance(codePoint, i, str.end());
)
{
if (codePoint == '\n')
out << "\n" << IndentTo(indent);
else else
out << str[i]; WriteCodePoint(out, codePoint);
} }
return true; return true;
} }
@@ -108,11 +269,15 @@ namespace YAML
{ {
unsigned curIndent = out.col(); unsigned curIndent = out.col();
out << "#" << Indentation(postCommentIndent); out << "#" << Indentation(postCommentIndent);
for(std::size_t i=0;i<str.size();i++) { int codePoint;
if(str[i] == '\n') for(std::string::const_iterator i = str.begin();
GetNextCodePointAndAdvance(codePoint, i, str.end());
)
{
if(codePoint == '\n')
out << "\n" << IndentTo(curIndent) << "#" << Indentation(postCommentIndent); out << "\n" << IndentTo(curIndent) << "#" << Indentation(postCommentIndent);
else else
out << str[i]; WriteCodePoint(out, codePoint);
} }
return true; return true;
} }
@@ -120,24 +285,30 @@ namespace YAML
bool WriteAlias(ostream& out, const std::string& str) bool WriteAlias(ostream& out, const std::string& str)
{ {
out << "*"; out << "*";
for(std::size_t i=0;i<str.size();i++) { return WriteAliasName(out, str);
if(!IsPrintable(str[i]) || str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\r')
return false;
out << str[i];
}
return true;
} }
bool WriteAnchor(ostream& out, const std::string& str) bool WriteAnchor(ostream& out, const std::string& str)
{ {
out << "&"; out << "&";
for(std::size_t i=0;i<str.size();i++) { return WriteAliasName(out, str);
if(!IsPrintable(str[i]) || str[i] == ' ' || str[i] == '\t' || str[i] == '\n' || str[i] == '\r') }
bool WriteTag(ostream& out, const std::string& str)
{
out << "!<";
StringCharSource buffer(str.c_str(), str.size());
while(buffer) {
int n = Exp::URI().Match(buffer);
if(n <= 0)
return false; return false;
out << str[i]; while(--n >= 0) {
out << buffer[0];
++buffer;
}
} }
out << ">";
return true; return true;
} }
} }

View File

@@ -11,13 +11,14 @@ namespace YAML
{ {
namespace Utils namespace Utils
{ {
bool WriteString(ostream& out, const std::string& str, bool inFlow); bool WriteString(ostream& out, const std::string& str, bool inFlow, bool escapeNonAscii);
bool WriteSingleQuotedString(ostream& out, const std::string& str); bool WriteSingleQuotedString(ostream& out, const std::string& str);
bool WriteDoubleQuotedString(ostream& out, const std::string& str); bool WriteDoubleQuotedString(ostream& out, const std::string& str, bool escapeNonAscii);
bool WriteLiteralString(ostream& out, const std::string& str, int indent); bool WriteLiteralString(ostream& out, const std::string& str, int indent);
bool WriteComment(ostream& out, const std::string& str, int postCommentIndent); bool WriteComment(ostream& out, const std::string& str, int postCommentIndent);
bool WriteAlias(ostream& out, const std::string& str); bool WriteAlias(ostream& out, const std::string& str);
bool WriteAnchor(ostream& out, const std::string& str); bool WriteAnchor(ostream& out, const std::string& str);
bool WriteTag(ostream& out, const std::string& str);
} }
} }

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "exp.h" #include "exp.h"
#include "exceptions.h" #include "exceptions.h"
#include <sstream> #include <sstream>
@@ -28,9 +27,9 @@ namespace YAML
return value; return value;
} }
std::string Str(char ch) std::string Str(unsigned ch)
{ {
return std::string("") + ch; return std::string(1, static_cast<char>(ch));
} }
// Escape // Escape
@@ -83,7 +82,7 @@ namespace YAML
// now do the slash (we're not gonna check if it's a slash - you better pass one!) // now do the slash (we're not gonna check if it's a slash - you better pass one!)
switch(ch) { switch(ch) {
case '0': return "\0"; case '0': return std::string(1, '\x00');
case 'a': return "\x07"; case 'a': return "\x07";
case 'b': return "\x08"; case 'b': return "\x08";
case 't': case 't':
@@ -97,8 +96,9 @@ namespace YAML
case '\"': return "\""; case '\"': return "\"";
case '\'': return "\'"; case '\'': return "\'";
case '\\': return "\\"; case '\\': return "\\";
case 'N': return "\xC2\x85"; // NEL (#x85) case '/': return "/";
case '_': return "\xC2\xA0"; // #xA0 case 'N': return "\x85";
case '_': return "\xA0";
case 'L': return "\xE2\x80\xA8"; // LS (#x2028) case 'L': return "\xE2\x80\xA8"; // LS (#x2028)
case 'P': return "\xE2\x80\xA9"; // PS (#x2029) case 'P': return "\xE2\x80\xA9"; // PS (#x2029)
case 'x': return Escape(in, 2); case 'x': return Escape(in, 2);

166
src/exp.h
View File

@@ -17,45 +17,153 @@ namespace YAML
namespace Exp namespace Exp
{ {
// misc // misc
const RegEx Space = RegEx(' '); inline const RegEx& Space() {
const RegEx Tab = RegEx('\t'); static const RegEx e = RegEx(' ');
const RegEx Blank = Space || Tab; return e;
const RegEx Break = RegEx('\n') || RegEx("\r\n"); }
const RegEx BlankOrBreak = Blank || Break; inline const RegEx& Tab() {
const RegEx Digit = RegEx('0', '9'); static const RegEx e = RegEx('\t');
const RegEx Alpha = RegEx('a', 'z') || RegEx('A', 'Z'); return e;
const RegEx AlphaNumeric = Alpha || Digit; }
const RegEx Hex = Digit || RegEx('A', 'F') || RegEx('a', 'f'); inline const RegEx& Blank() {
const RegEx Printable = RegEx(0x20, 0x7E); static const RegEx e = Space() || Tab();
return e;
}
inline const RegEx& Break() {
static const RegEx e = RegEx('\n') || RegEx("\r\n");
return e;
}
inline const RegEx& BlankOrBreak() {
static const RegEx e = Blank() || Break();
return e;
}
inline const RegEx& Digit() {
static const RegEx e = RegEx('0', '9');
return e;
}
inline const RegEx& Alpha() {
static const RegEx e = RegEx('a', 'z') || RegEx('A', 'Z');
return e;
}
inline const RegEx& AlphaNumeric() {
static const RegEx e = Alpha() || Digit();
return e;
}
inline const RegEx& Word() {
static const RegEx e = AlphaNumeric() || RegEx('-');
return e;
}
inline const RegEx& Hex() {
static const RegEx e = Digit() || RegEx('A', 'F') || RegEx('a', 'f');
return e;
}
// Valid Unicode code points that are not part of c-printable (YAML 1.2, sec. 5.1)
inline const RegEx& NotPrintable() {
static const RegEx e = RegEx(0) ||
RegEx("\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x7F", REGEX_OR) ||
RegEx(0x0E, 0x1F) ||
(RegEx('\xC2') + (RegEx('\x80', '\x84') || RegEx('\x86', '\x9F')));
return e;
}
inline const RegEx& Utf8_ByteOrderMark() {
static const RegEx e = RegEx("\xEF\xBB\xBF");
return e;
}
// actual tags // actual tags
const RegEx DocStart = RegEx("---") + (BlankOrBreak || RegEx()); inline const RegEx& DocStart() {
const RegEx DocEnd = RegEx("...") + (BlankOrBreak || RegEx()); static const RegEx e = RegEx("---") + (BlankOrBreak() || RegEx());
const RegEx DocIndicator = DocStart || DocEnd; return e;
const RegEx BlockEntry = RegEx('-') + (BlankOrBreak || RegEx()); }
const RegEx Key = RegEx('?'), inline const RegEx& DocEnd() {
KeyInFlow = RegEx('?') + BlankOrBreak; static const RegEx e = RegEx("...") + (BlankOrBreak() || RegEx());
const RegEx Value = RegEx(':') + (BlankOrBreak || RegEx()), return e;
ValueInFlow = RegEx(':') + (BlankOrBreak || RegEx(",}", REGEX_OR)); }
const RegEx Comment = RegEx('#'); inline const RegEx& DocIndicator() {
const RegEx AnchorEnd = RegEx("?:,]}%@`", REGEX_OR) || BlankOrBreak; static const RegEx e = DocStart() || DocEnd();
return e;
}
inline const RegEx& BlockEntry() {
static const RegEx e = RegEx('-') + (BlankOrBreak() || RegEx());
return e;
}
inline const RegEx& Key() {
static const RegEx e = RegEx('?');
return e;
}
inline const RegEx& KeyInFlow() {
static const RegEx e = RegEx('?') + BlankOrBreak();
return e;
}
inline const RegEx& Value() {
static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx());
return e;
}
inline const RegEx& ValueInFlow() {
static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx(",}", REGEX_OR));
return e;
}
inline const RegEx& ValueInJSONFlow() {
static const RegEx e = RegEx(':');
return e;
}
inline const RegEx Comment() {
static const RegEx e = RegEx('#');
return e;
}
inline const RegEx& AnchorEnd() {
static const RegEx e = RegEx("?:,]}%@`", REGEX_OR) || BlankOrBreak();
return e;
}
inline const RegEx& URI() {
static const RegEx e = Word() || RegEx("#;/?:@&=+$,_.!~*'()[]", REGEX_OR) || (RegEx('%') + Hex() + Hex());
return e;
}
inline const RegEx& Tag() {
static const RegEx e = Word() || RegEx("#;/?:@&=+$_.~*'", REGEX_OR) || (RegEx('%') + Hex() + Hex());
return e;
}
// Plain scalar rules: // Plain scalar rules:
// . Cannot start with a blank. // . Cannot start with a blank.
// . Can never start with any of , [ ] { } # & * ! | > \' \" % @ ` // . Can never start with any of , [ ] { } # & * ! | > \' \" % @ `
// . In the block context - ? : must be not be followed with a space. // . In the block context - ? : must be not be followed with a space.
// . In the flow context ? is illegal and : and - must not be followed with a space. // . In the flow context ? is illegal and : and - must not be followed with a space.
const RegEx PlainScalar = !(BlankOrBreak || RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-?:", REGEX_OR) + Blank)), inline const RegEx& PlainScalar() {
PlainScalarInFlow = !(BlankOrBreak || RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-:", REGEX_OR) + Blank)); static const RegEx e = !(BlankOrBreak() || RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-?:", REGEX_OR) + Blank()));
const RegEx EndScalar = RegEx(':') + (BlankOrBreak || RegEx()), return e;
EndScalarInFlow = (RegEx(':') + (BlankOrBreak || RegEx(",]}", REGEX_OR))) || RegEx(",?[]{}", REGEX_OR); }
inline const RegEx& PlainScalarInFlow() {
static const RegEx e = !(BlankOrBreak() || RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-:", REGEX_OR) + Blank()));
return e;
}
inline const RegEx& EndScalar() {
static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx());
return e;
}
inline const RegEx& EndScalarInFlow() {
static const RegEx e = (RegEx(':') + (BlankOrBreak() || RegEx(",]}", REGEX_OR))) || RegEx(",?[]{}", REGEX_OR);
return e;
}
const RegEx EscSingleQuote = RegEx("\'\'"); inline const RegEx& EscSingleQuote() {
const RegEx EscBreak = RegEx('\\') + Break; static const RegEx e = RegEx("\'\'");
return e;
}
inline const RegEx& EscBreak() {
static const RegEx e = RegEx('\\') + Break();
return e;
}
const RegEx ChompIndicator = RegEx("+-", REGEX_OR); inline const RegEx& ChompIndicator() {
const RegEx Chomp = (ChompIndicator + Digit) || (Digit + ChompIndicator) || ChompIndicator || Digit; static const RegEx e = RegEx("+-", REGEX_OR);
return e;
}
inline const RegEx& Chomp() {
static const RegEx e = (ChompIndicator() + Digit()) || (Digit() + ChompIndicator()) || ChompIndicator() || Digit();
return e;
}
// and some functions // and some functions
std::string Escape(Stream& in); std::string Escape(Stream& in);
@@ -74,6 +182,8 @@ namespace YAML
const char Tag = '!'; const char Tag = '!';
const char LiteralScalar = '|'; const char LiteralScalar = '|';
const char FoldedScalar = '>'; const char FoldedScalar = '>';
const char VerbatimTagStart = '<';
const char VerbatimTagEnd = '>';
} }
} }

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "node.h" #include "node.h"
#include "exceptions.h" #include "exceptions.h"
#include "iterpriv.h" #include "iterpriv.h"

View File

@@ -1,11 +1,9 @@
#include "crt.h"
#include "map.h" #include "map.h"
#include "node.h" #include "node.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
#include "emitter.h" #include "emitter.h"
#include <memory>
namespace YAML namespace YAML
{ {
@@ -18,7 +16,7 @@ namespace YAML
for(node_map::const_iterator it=data.begin();it!=data.end();++it) { for(node_map::const_iterator it=data.begin();it!=data.end();++it) {
std::auto_ptr<Node> pKey = it->first->Clone(); std::auto_ptr<Node> pKey = it->first->Clone();
std::auto_ptr<Node> pValue = it->second->Clone(); std::auto_ptr<Node> pValue = it->second->Clone();
m_data[pKey.release()] = pValue.release(); AddEntry(pKey, pValue);
} }
} }
@@ -58,7 +56,7 @@ namespace YAML
return m_data.size(); return m_data.size();
} }
void Map::Parse(Scanner *pScanner, const ParserState& state) void Map::Parse(Scanner *pScanner, ParserState& state)
{ {
Clear(); Clear();
@@ -66,14 +64,17 @@ namespace YAML
switch(pScanner->peek().type) { switch(pScanner->peek().type) {
case Token::BLOCK_MAP_START: ParseBlock(pScanner, state); break; case Token::BLOCK_MAP_START: ParseBlock(pScanner, state); break;
case Token::FLOW_MAP_START: ParseFlow(pScanner, state); break; case Token::FLOW_MAP_START: ParseFlow(pScanner, state); break;
case Token::KEY: ParseCompact(pScanner, state); break;
case Token::VALUE: ParseCompactWithNoKey(pScanner, state); break;
default: break; default: break;
} }
} }
void Map::ParseBlock(Scanner *pScanner, const ParserState& state) void Map::ParseBlock(Scanner *pScanner, ParserState& state)
{ {
// eat start token // eat start token
pScanner->pop(); pScanner->pop();
state.PushCollectionType(ParserState::BLOCK_MAP);
while(1) { while(1) {
if(pScanner->empty()) if(pScanner->empty())
@@ -102,15 +103,17 @@ namespace YAML
pValue->Parse(pScanner, state); pValue->Parse(pScanner, state);
} }
// assign the map with the actual pointers AddEntry(pKey, pValue);
m_data[pKey.release()] = pValue.release();
} }
state.PopCollectionType(ParserState::BLOCK_MAP);
} }
void Map::ParseFlow(Scanner *pScanner, const ParserState& state) void Map::ParseFlow(Scanner *pScanner, ParserState& state)
{ {
// eat start token // eat start token
pScanner->pop(); pScanner->pop();
state.PushCollectionType(ParserState::FLOW_MAP);
while(1) { while(1) {
if(pScanner->empty()) if(pScanner->empty())
@@ -144,9 +147,55 @@ namespace YAML
else if(nextToken.type != Token::FLOW_MAP_END) else if(nextToken.type != Token::FLOW_MAP_END)
throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW); throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW);
// assign the map with the actual pointers AddEntry(pKey, pValue);
m_data[pKey.release()] = pValue.release();
} }
state.PopCollectionType(ParserState::FLOW_MAP);
}
// ParseCompact
// . Single "key: value" pair in a flow sequence
void Map::ParseCompact(Scanner *pScanner, ParserState& state)
{
state.PushCollectionType(ParserState::COMPACT_MAP);
std::auto_ptr <Node> pKey(new Node), pValue(new Node);
// grab key
pScanner->pop();
pKey->Parse(pScanner, state);
// now grab value (optional)
if(!pScanner->empty() && pScanner->peek().type == Token::VALUE) {
pScanner->pop();
pValue->Parse(pScanner, state);
}
AddEntry(pKey, pValue);
state.PopCollectionType(ParserState::COMPACT_MAP);
}
// ParseCompactWithNoKey
// . Single ": value" pair in a flow sequence
void Map::ParseCompactWithNoKey(Scanner *pScanner, ParserState& state)
{
state.PushCollectionType(ParserState::COMPACT_MAP);
std::auto_ptr <Node> pKey(new Node), pValue(new Node);
// grab value
pScanner->pop();
pValue->Parse(pScanner, state);
AddEntry(pKey, pValue);
state.PopCollectionType(ParserState::COMPACT_MAP);
}
void Map::AddEntry(std::auto_ptr<Node> pKey, std::auto_ptr<Node> pValue)
{
node_map::const_iterator it = m_data.find(pKey.get());
if(it != m_data.end())
return;
m_data[pKey.release()] = pValue.release();
} }
void Map::Write(Emitter& out) const void Map::Write(Emitter& out) const

View File

@@ -6,6 +6,7 @@
#include "content.h" #include "content.h"
#include <map> #include <map>
#include <memory>
namespace YAML namespace YAML
{ {
@@ -27,7 +28,7 @@ namespace YAML
virtual bool GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& it) const; virtual bool GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& it) const;
virtual bool GetEnd(std::map <Node *, Node *, ltnode>::const_iterator& it) const; virtual bool GetEnd(std::map <Node *, Node *, ltnode>::const_iterator& it) const;
virtual std::size_t GetSize() const; virtual std::size_t GetSize() const;
virtual void Parse(Scanner *pScanner, const ParserState& state); virtual void Parse(Scanner *pScanner, ParserState& state);
virtual void Write(Emitter& out) const; virtual void Write(Emitter& out) const;
virtual bool IsMap() const { return true; } virtual bool IsMap() const { return true; }
@@ -39,8 +40,12 @@ namespace YAML
virtual int Compare(Map *pMap); virtual int Compare(Map *pMap);
private: private:
void ParseBlock(Scanner *pScanner, const ParserState& state); void ParseBlock(Scanner *pScanner, ParserState& state);
void ParseFlow(Scanner *pScanner, const ParserState& state); void ParseFlow(Scanner *pScanner, ParserState& state);
void ParseCompact(Scanner *pScanner, ParserState& state);
void ParseCompactWithNoKey(Scanner *pScanner, ParserState& state);
void AddEntry(std::auto_ptr<Node> pKey, std::auto_ptr<Node> pValue);
private: private:
node_map m_data; node_map m_data;

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "node.h" #include "node.h"
#include "token.h" #include "token.h"
#include "scanner.h" #include "scanner.h"
@@ -10,6 +9,7 @@
#include "aliascontent.h" #include "aliascontent.h"
#include "iterpriv.h" #include "iterpriv.h"
#include "emitter.h" #include "emitter.h"
#include "tag.h"
#include <stdexcept> #include <stdexcept>
namespace YAML namespace YAML
@@ -27,7 +27,7 @@ namespace YAML
Node::Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent) Node::Node(const Mark& mark, const std::string& anchor, const std::string& tag, const Content *pContent)
: m_mark(mark), m_anchor(anchor), m_tag(tag), m_pContent(0), m_alias(false), m_pIdentity(this), m_referenced(false) : m_mark(mark), m_anchor(anchor), m_tag(tag), m_pContent(0), m_alias(false), m_pIdentity(this), m_referenced(false)
{ {
if(m_pContent) if(pContent)
m_pContent = pContent->Clone(); m_pContent = pContent->Clone();
} }
@@ -54,7 +54,7 @@ namespace YAML
return std::auto_ptr<Node> (new Node(m_mark, m_anchor, m_tag, m_pContent)); return std::auto_ptr<Node> (new Node(m_mark, m_anchor, m_tag, m_pContent));
} }
void Node::Parse(Scanner *pScanner, const ParserState& state) void Node::Parse(Scanner *pScanner, ParserState& state)
{ {
Clear(); Clear();
@@ -64,6 +64,13 @@ namespace YAML
// save location // save location
m_mark = pScanner->peek().mark; m_mark = pScanner->peek().mark;
// special case: a value node by itself must be a map, with no header
if(pScanner->peek().type == Token::VALUE) {
m_pContent = new Map;
m_pContent->Parse(pScanner, state);
return;
}
ParseHeader(pScanner, state); ParseHeader(pScanner, state);
@@ -99,10 +106,12 @@ namespace YAML
case Token::BLOCK_MAP_START: case Token::BLOCK_MAP_START:
m_pContent = new Map; m_pContent = new Map;
break; break;
case Token::KEY:
// compact maps can only go in a flow sequence
if(state.GetCurCollectionType() == ParserState::FLOW_SEQ)
m_pContent = new Map;
break;
default: default:
// std::stringstream str;
// str << TokenNames[pScanner->peek().type];
// throw std::runtime_error(str.str());
break; break;
} }
@@ -117,7 +126,7 @@ namespace YAML
// ParseHeader // ParseHeader
// . Grabs any tag, alias, or anchor tokens and deals with them. // . Grabs any tag, alias, or anchor tokens and deals with them.
void Node::ParseHeader(Scanner *pScanner, const ParserState& state) void Node::ParseHeader(Scanner *pScanner, ParserState& state)
{ {
while(1) { while(1) {
if(pScanner->empty()) if(pScanner->empty())
@@ -132,20 +141,18 @@ namespace YAML
} }
} }
void Node::ParseTag(Scanner *pScanner, const ParserState& state) void Node::ParseTag(Scanner *pScanner, ParserState& state)
{ {
Token& token = pScanner->peek(); Token& token = pScanner->peek();
if(m_tag != "") if(m_tag != "")
throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS); throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS);
m_tag = state.TranslateTag(token.value); Tag tag(token);
m_tag = tag.Translate(state);
for(std::size_t i=0;i<token.params.size();i++)
m_tag += token.params[i];
pScanner->pop(); pScanner->pop();
} }
void Node::ParseAnchor(Scanner *pScanner, const ParserState& /*state*/) void Node::ParseAnchor(Scanner *pScanner, ParserState& /*state*/)
{ {
Token& token = pScanner->peek(); Token& token = pScanner->peek();
if(m_anchor != "") if(m_anchor != "")
@@ -156,7 +163,7 @@ namespace YAML
pScanner->pop(); pScanner->pop();
} }
void Node::ParseAlias(Scanner *pScanner, const ParserState& /*state*/) void Node::ParseAlias(Scanner *pScanner, ParserState& /*state*/)
{ {
Token& token = pScanner->peek(); Token& token = pScanner->peek();
if(m_anchor != "") if(m_anchor != "")
@@ -242,7 +249,10 @@ namespace YAML
bool Node::GetScalar(std::string& s) const bool Node::GetScalar(std::string& s) const
{ {
if(!m_pContent) { if(!m_pContent) {
s = "~"; if(m_tag.empty())
s = "~";
else
s = "";
return true; return true;
} }
@@ -259,7 +269,8 @@ namespace YAML
out << Anchor(node.m_anchor); out << Anchor(node.m_anchor);
} }
// TODO: write tag if(node.m_tag != "")
out << VerbatimTag(node.m_tag);
// write content // write content
if(node.m_pContent) if(node.m_pContent)

View File

@@ -1,40 +1,45 @@
#include "crt.h"
#include "parser.h" #include "parser.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
#include "parserstate.h"
#include <sstream> #include <sstream>
#include <cstdio> #include <cstdio>
namespace YAML namespace YAML
{ {
Parser::Parser(std::istream& in): m_pScanner(0) Parser::Parser()
{
}
Parser::Parser(std::istream& in)
{ {
Load(in); Load(in);
} }
Parser::~Parser() Parser::~Parser()
{ {
delete m_pScanner;
} }
Parser::operator bool() const Parser::operator bool() const
{ {
return !m_pScanner->empty(); return m_pScanner.get() && !m_pScanner->empty();
} }
void Parser::Load(std::istream& in) void Parser::Load(std::istream& in)
{ {
delete m_pScanner; m_pScanner.reset(new Scanner(in));
m_pScanner = new Scanner(in); m_pState.reset(new ParserState);
m_state.Reset();
} }
// GetNextDocument // GetNextDocument
// . Reads the next document in the queue (of tokens). // . Reads the next document in the queue (of tokens).
// . Throws a ParserException on error. // . Throws a ParserException on error.
void Parser::GetNextDocument(Node& document) bool Parser::GetNextDocument(Node& document)
{ {
if(!m_pScanner.get())
return false;
// clear node // clear node
document.Clear(); document.Clear();
@@ -43,14 +48,14 @@ namespace YAML
// we better have some tokens in the queue // we better have some tokens in the queue
if(m_pScanner->empty()) if(m_pScanner->empty())
return; return false;
// first eat doc start (optional) // first eat doc start (optional)
if(m_pScanner->peek().type == Token::DOC_START) if(m_pScanner->peek().type == Token::DOC_START)
m_pScanner->pop(); m_pScanner->pop();
// now parse our root node // now parse our root node
document.Parse(m_pScanner, m_state); document.Parse(m_pScanner.get(), *m_pState);
// and finally eat any doc ends we see // and finally eat any doc ends we see
while(!m_pScanner->empty() && m_pScanner->peek().type == Token::DOC_END) while(!m_pScanner->empty() && m_pScanner->peek().type == Token::DOC_END)
@@ -58,6 +63,8 @@ namespace YAML
// clear anchors from the scanner, which are no longer relevant // clear anchors from the scanner, which are no longer relevant
m_pScanner->ClearAnchors(); m_pScanner->ClearAnchors();
return true;
} }
// ParseDirectives // ParseDirectives
@@ -77,55 +84,66 @@ namespace YAML
// we keep the directives from the last document if none are specified; // we keep the directives from the last document if none are specified;
// but if any directives are specific, then we reset them // but if any directives are specific, then we reset them
if(!readDirective) if(!readDirective)
m_state.Reset(); m_pState.reset(new ParserState);
readDirective = true; readDirective = true;
HandleDirective(&token); HandleDirective(token);
m_pScanner->pop(); m_pScanner->pop();
} }
} }
void Parser::HandleDirective(Token *pToken) void Parser::HandleDirective(const Token& token)
{ {
if(pToken->value == "YAML") if(token.value == "YAML")
HandleYamlDirective(pToken); HandleYamlDirective(token);
else if(pToken->value == "TAG") else if(token.value == "TAG")
HandleTagDirective(pToken); HandleTagDirective(token);
} }
// HandleYamlDirective // HandleYamlDirective
// . Should be of the form 'major.minor' (like a version number) // . Should be of the form 'major.minor' (like a version number)
void Parser::HandleYamlDirective(Token *pToken) void Parser::HandleYamlDirective(const Token& token)
{ {
if(pToken->params.size() != 1) if(token.params.size() != 1)
throw ParserException(pToken->mark, ErrorMsg::YAML_DIRECTIVE_ARGS); throw ParserException(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS);
if(!m_pState->version.isDefault)
throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE);
std::stringstream str(pToken->params[0]); std::stringstream str(token.params[0]);
str >> m_state.version.major; str >> m_pState->version.major;
str.get(); str.get();
str >> m_state.version.minor; str >> m_pState->version.minor;
if(!str || str.peek() != EOF) if(!str || str.peek() != EOF)
throw ParserException(pToken->mark, ErrorMsg::YAML_VERSION + pToken->params[0]); throw ParserException(token.mark, ErrorMsg::YAML_VERSION + token.params[0]);
if(m_state.version.major > 1) if(m_pState->version.major > 1)
throw ParserException(pToken->mark, ErrorMsg::YAML_MAJOR_VERSION); throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION);
m_pState->version.isDefault = false;
// TODO: warning on major == 1, minor > 2? // TODO: warning on major == 1, minor > 2?
} }
// HandleTagDirective // HandleTagDirective
// . Should be of the form 'handle prefix', where 'handle' is converted to 'prefix' in the file. // . Should be of the form 'handle prefix', where 'handle' is converted to 'prefix' in the file.
void Parser::HandleTagDirective(Token *pToken) void Parser::HandleTagDirective(const Token& token)
{ {
if(pToken->params.size() != 2) if(token.params.size() != 2)
throw ParserException(pToken->mark, ErrorMsg::TAG_DIRECTIVE_ARGS); throw ParserException(token.mark, ErrorMsg::TAG_DIRECTIVE_ARGS);
std::string handle = pToken->params[0], prefix = pToken->params[1]; const std::string& handle = token.params[0];
m_state.tags[handle] = prefix; const std::string& prefix = token.params[1];
if(m_pState->tags.find(handle) != m_pState->tags.end())
throw ParserException(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE);
m_pState->tags[handle] = prefix;
} }
void Parser::PrintTokens(std::ostream& out) void Parser::PrintTokens(std::ostream& out)
{ {
if(!m_pScanner.get())
return;
while(1) { while(1) {
if(m_pScanner->empty()) if(m_pScanner->empty())
break; break;

View File

@@ -1,25 +1,23 @@
#include "crt.h"
#include "parserstate.h" #include "parserstate.h"
namespace YAML namespace YAML
{ {
void ParserState::Reset() ParserState::ParserState()
{ {
// version // version
version.isDefault = true;
version.major = 1; version.major = 1;
version.minor = 2; version.minor = 2;
// and tags
tags.clear();
tags["!"] = "!";
tags["!!"] = "tag:yaml.org,2002:";
} }
std::string ParserState::TranslateTag(const std::string& handle) const const std::string ParserState::TranslateTagHandle(const std::string& handle) const
{ {
std::map <std::string, std::string>::const_iterator it = tags.find(handle); std::map <std::string, std::string>::const_iterator it = tags.find(handle);
if(it == tags.end()) if(it == tags.end()) {
if(handle == "!!")
return "tag:yaml.org,2002:";
return handle; return handle;
}
return it->second; return it->second;
} }

37
src/parserstate.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#ifndef PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include <string>
#include <map>
#include <stack>
#include <cassert>
namespace YAML
{
struct Version {
bool isDefault;
int major, minor;
};
struct ParserState
{
enum COLLECTION_TYPE { NONE, BLOCK_MAP, BLOCK_SEQ, FLOW_MAP, FLOW_SEQ, COMPACT_MAP };
ParserState();
const std::string TranslateTagHandle(const std::string& handle) const;
COLLECTION_TYPE GetCurCollectionType() const { if(collectionStack.empty()) return NONE; return collectionStack.top(); }
void PushCollectionType(COLLECTION_TYPE type) { collectionStack.push(type); }
void PopCollectionType(COLLECTION_TYPE type) { assert(type == GetCurCollectionType()); collectionStack.pop(); }
Version version;
std::map <std::string, std::string> tags;
std::stack <COLLECTION_TYPE> collectionStack;
};
}
#endif // PARSERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "regex.h" #include "regex.h"
namespace YAML namespace YAML

View File

@@ -37,12 +37,12 @@ namespace YAML
int Match(const std::string& str) const; int Match(const std::string& str) const;
int Match(const Stream& in) const; int Match(const Stream& in) const;
template <typename Source> int Match(const Source& source) const;
private: private:
RegEx(REGEX_OP op); RegEx(REGEX_OP op);
template <typename Source> bool IsValidSource(const Source& source) const; template <typename Source> bool IsValidSource(const Source& source) const;
template <typename Source> int Match(const Source& source) const;
template <typename Source> int MatchUnchecked(const Source& source) const; template <typename Source> int MatchUnchecked(const Source& source) const;
template <typename Source> int MatchOpEmpty(const Source& source) const; template <typename Source> int MatchOpEmpty(const Source& source) const;

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "scalar.h" #include "scalar.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
@@ -25,7 +24,7 @@ namespace YAML
return new Scalar(m_data); return new Scalar(m_data);
} }
void Scalar::Parse(Scanner *pScanner, const ParserState& /*state*/) void Scalar::Parse(Scanner *pScanner, ParserState& /*state*/)
{ {
Token& token = pScanner->peek(); Token& token = pScanner->peek();
m_data = token.value; m_data = token.value;

View File

@@ -18,7 +18,7 @@ namespace YAML
virtual Content *Clone() const; virtual Content *Clone() const;
virtual void Parse(Scanner *pScanner, const ParserState& state); virtual void Parse(Scanner *pScanner, ParserState& state);
virtual void Write(Emitter& out) const; virtual void Write(Emitter& out) const;
virtual bool IsScalar() const { return true; } virtual bool IsScalar() const { return true; }

View File

@@ -1,19 +1,22 @@
#include "crt.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
#include "exp.h" #include "exp.h"
#include <cassert> #include <cassert>
#include <memory>
namespace YAML namespace YAML
{ {
Scanner::Scanner(std::istream& in) Scanner::Scanner(std::istream& in)
: INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false) : INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), m_canBeJSONFlow(false)
{ {
} }
Scanner::~Scanner() Scanner::~Scanner()
{ {
for(unsigned i=0;i<m_indentRefs.size();i++)
delete m_indentRefs[i];
m_indentRefs.clear();
} }
// empty // empty
@@ -116,10 +119,10 @@ namespace YAML
return ScanDirective(); return ScanDirective();
// document token // document token
if(INPUT.column() == 0 && Exp::DocStart.Matches(INPUT)) if(INPUT.column() == 0 && Exp::DocStart().Matches(INPUT))
return ScanDocStart(); return ScanDocStart();
if(INPUT.column() == 0 && Exp::DocEnd.Matches(INPUT)) if(INPUT.column() == 0 && Exp::DocEnd().Matches(INPUT))
return ScanDocEnd(); return ScanDocEnd();
// flow start/end/entry // flow start/end/entry
@@ -133,13 +136,13 @@ namespace YAML
return ScanFlowEntry(); return ScanFlowEntry();
// block/map stuff // block/map stuff
if(Exp::BlockEntry.Matches(INPUT)) if(Exp::BlockEntry().Matches(INPUT))
return ScanBlockEntry(); return ScanBlockEntry();
if((InBlockContext() ? Exp::Key : Exp::KeyInFlow).Matches(INPUT)) if((InBlockContext() ? Exp::Key() : Exp::KeyInFlow()).Matches(INPUT))
return ScanKey(); return ScanKey();
if((InBlockContext() ? Exp::Value : Exp::ValueInFlow).Matches(INPUT)) if(GetValueRegex().Matches(INPUT))
return ScanValue(); return ScanValue();
// alias/anchor // alias/anchor
@@ -158,7 +161,7 @@ namespace YAML
return ScanQuotedScalar(); return ScanQuotedScalar();
// plain scalars // plain scalars
if((InBlockContext() ? Exp::PlainScalar : Exp::PlainScalarInFlow).Matches(INPUT)) if((InBlockContext() ? Exp::PlainScalar() : Exp::PlainScalarInFlow()).Matches(INPUT))
return ScanPlainScalar(); return ScanPlainScalar();
// don't know what it is! // don't know what it is!
@@ -171,22 +174,25 @@ namespace YAML
{ {
while(1) { while(1) {
// first eat whitespace // first eat whitespace
while(INPUT && IsWhitespaceToBeEaten(INPUT.peek())) while(INPUT && IsWhitespaceToBeEaten(INPUT.peek())) {
if(InBlockContext() && Exp::Tab().Matches(INPUT))
m_simpleKeyAllowed = false;
INPUT.eat(1); INPUT.eat(1);
}
// then eat a comment // then eat a comment
if(Exp::Comment.Matches(INPUT)) { if(Exp::Comment().Matches(INPUT)) {
// eat until line break // eat until line break
while(INPUT && !Exp::Break.Matches(INPUT)) while(INPUT && !Exp::Break().Matches(INPUT))
INPUT.eat(1); INPUT.eat(1);
} }
// if it's NOT a line break, then we're done! // if it's NOT a line break, then we're done!
if(!Exp::Break.Matches(INPUT)) if(!Exp::Break().Matches(INPUT))
break; break;
// otherwise, let's eat the line break and keep going // otherwise, let's eat the line break and keep going
int n = Exp::Break.Match(INPUT); int n = Exp::Break().Match(INPUT);
INPUT.eat(n); INPUT.eat(n);
// oh yeah, and let's get rid of that simple key // oh yeah, and let's get rid of that simple key
@@ -202,30 +208,43 @@ namespace YAML
// Misc. helpers // Misc. helpers
// IsWhitespaceToBeEaten // IsWhitespaceToBeEaten
// . We can eat whitespace if: // . We can eat whitespace if it's a space or tab
// 1. It's a space // . Note: originally tabs in block context couldn't be eaten
// 2. It's a tab, and we're either: // "where a simple key could be allowed
// a. In the flow context // (i.e., not at the beginning of a line, or following '-', '?', or ':')"
// b. In the block context but not where a simple key could be allowed // I think this is wrong, since tabs can be non-content whitespace; it's just
// (i.e., not at the beginning of a line, or following '-', '?', or ':') // that they can't contribute to indentation, so once you've seen a tab in a
// line, you can't start a simple key
bool Scanner::IsWhitespaceToBeEaten(char ch) bool Scanner::IsWhitespaceToBeEaten(char ch)
{ {
if(ch == ' ') if(ch == ' ')
return true; return true;
if(ch == '\t' && (InFlowContext() || !m_simpleKeyAllowed)) if(ch == '\t')
return true; return true;
return false; return false;
} }
// GetValueRegex
// . Get the appropriate regex to check if it's a value token
const RegEx& Scanner::GetValueRegex() const
{
if(InBlockContext())
return Exp::Value();
return m_canBeJSONFlow ? Exp::ValueInJSONFlow() : Exp::ValueInFlow();
}
// StartStream // StartStream
// . Set the initial conditions for starting a stream. // . Set the initial conditions for starting a stream.
void Scanner::StartStream() void Scanner::StartStream()
{ {
m_startedStream = true; m_startedStream = true;
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_indents.push(IndentMarker(-1, IndentMarker::NONE)); IndentMarker *pIndent = new IndentMarker(-1, IndentMarker::NONE);
m_indentRefs.push_back(pIndent);
m_indents.push(pIndent);
m_anchors.clear(); m_anchors.clear();
} }
@@ -244,6 +263,22 @@ namespace YAML
m_endedStream = true; m_endedStream = true;
} }
Token *Scanner::PushToken(Token::TYPE type)
{
m_tokens.push(Token(type, INPUT.mark()));
return &m_tokens.back();
}
Token::TYPE Scanner::GetStartTokenFor(IndentMarker::INDENT_TYPE type) const
{
switch(type) {
case IndentMarker::SEQ: return Token::BLOCK_SEQ_START;
case IndentMarker::MAP: return Token::BLOCK_MAP_START;
case IndentMarker::NONE: assert(false); break;
}
assert(false);
}
// PushIndentTo // PushIndentTo
// . Pushes an indentation onto the stack, and enqueues the // . Pushes an indentation onto the stack, and enqueues the
// proper token (sequence start or mapping start). // proper token (sequence start or mapping start).
@@ -254,8 +289,9 @@ namespace YAML
if(InFlowContext()) if(InFlowContext())
return 0; return 0;
IndentMarker indent(column, type); std::auto_ptr<IndentMarker> pIndent(new IndentMarker(column, type));
const IndentMarker& lastIndent = m_indents.top(); IndentMarker& indent = *pIndent;
const IndentMarker& lastIndent = *m_indents.top();
// is this actually an indentation? // is this actually an indentation?
if(indent.column < lastIndent.column) if(indent.column < lastIndent.column)
@@ -264,22 +300,18 @@ namespace YAML
return 0; return 0;
// push a start token // push a start token
if(type == IndentMarker::SEQ) indent.pStartToken = PushToken(GetStartTokenFor(type));
m_tokens.push(Token(Token::BLOCK_SEQ_START, INPUT.mark()));
else if(type == IndentMarker::MAP)
m_tokens.push(Token(Token::BLOCK_MAP_START, INPUT.mark()));
else
assert(false);
indent.pStartToken = &m_tokens.back();
// and then the indent // and then the indent
m_indents.push(indent); m_indents.push(&indent);
return &m_indents.top(); m_indentRefs.push_back(pIndent.release());
return m_indentRefs.back();
} }
// PopIndentToHere // PopIndentToHere
// . Pops indentations off the stack until we reach the current indentation level, // . Pops indentations off the stack until we reach the current indentation level,
// and enqueues the proper token each time. // and enqueues the proper token each time.
// . Then pops all invalid indentations off.
void Scanner::PopIndentToHere() void Scanner::PopIndentToHere()
{ {
// are we in flow? // are we in flow?
@@ -288,14 +320,17 @@ namespace YAML
// now pop away // now pop away
while(!m_indents.empty()) { while(!m_indents.empty()) {
const IndentMarker& indent = m_indents.top(); const IndentMarker& indent = *m_indents.top();
if(indent.column < INPUT.column()) if(indent.column < INPUT.column())
break; break;
if(indent.column == INPUT.column() && !(indent.type == IndentMarker::SEQ && !Exp::BlockEntry.Matches(INPUT))) if(indent.column == INPUT.column() && !(indent.type == IndentMarker::SEQ && !Exp::BlockEntry().Matches(INPUT)))
break; break;
PopIndent(); PopIndent();
} }
while(!m_indents.empty() && m_indents.top()->status == IndentMarker::INVALID)
PopIndent();
} }
// PopAllIndents // PopAllIndents
@@ -309,7 +344,7 @@ namespace YAML
// now pop away // now pop away
while(!m_indents.empty()) { while(!m_indents.empty()) {
const IndentMarker& indent = m_indents.top(); const IndentMarker& indent = *m_indents.top();
if(indent.type == IndentMarker::NONE) if(indent.type == IndentMarker::NONE)
break; break;
@@ -321,17 +356,17 @@ namespace YAML
// . Pops a single indent, pushing the proper token // . Pops a single indent, pushing the proper token
void Scanner::PopIndent() void Scanner::PopIndent()
{ {
IndentMarker indent = m_indents.top(); const IndentMarker& indent = *m_indents.top();
IndentMarker::INDENT_TYPE type = indent.type;
m_indents.pop(); m_indents.pop();
if(!indent.isValid) {
if(indent.status != IndentMarker::VALID) {
InvalidateSimpleKey(); InvalidateSimpleKey();
return; return;
} }
if(type == IndentMarker::SEQ) if(indent.type == IndentMarker::SEQ)
m_tokens.push(Token(Token::BLOCK_SEQ_END, INPUT.mark())); m_tokens.push(Token(Token::BLOCK_SEQ_END, INPUT.mark()));
else if(type == IndentMarker::MAP) else if(indent.type == IndentMarker::MAP)
m_tokens.push(Token(Token::BLOCK_MAP_END, INPUT.mark())); m_tokens.push(Token(Token::BLOCK_MAP_END, INPUT.mark()));
} }
@@ -340,7 +375,7 @@ namespace YAML
{ {
if(m_indents.empty()) if(m_indents.empty())
return 0; return 0;
return m_indents.top().column; return m_indents.top()->column;
} }
// Save // Save
@@ -385,3 +420,4 @@ namespace YAML
m_anchors.clear(); m_anchors.clear();
} }
} }

View File

@@ -16,6 +16,7 @@
namespace YAML namespace YAML
{ {
class Node; class Node;
class RegEx;
class Scanner class Scanner
{ {
@@ -36,11 +37,12 @@ namespace YAML
private: private:
struct IndentMarker { struct IndentMarker {
enum INDENT_TYPE { MAP, SEQ, NONE }; enum INDENT_TYPE { MAP, SEQ, NONE };
IndentMarker(int column_, INDENT_TYPE type_): column(column_), type(type_), isValid(true), pStartToken(0) {} enum STATUS { VALID, INVALID, UNKNOWN };
IndentMarker(int column_, INDENT_TYPE type_): column(column_), type(type_), status(VALID), pStartToken(0) {}
int column; int column;
INDENT_TYPE type; INDENT_TYPE type;
bool isValid; STATUS status;
Token *pStartToken; Token *pStartToken;
}; };
@@ -53,11 +55,13 @@ namespace YAML
void ScanToNextToken(); void ScanToNextToken();
void StartStream(); void StartStream();
void EndStream(); void EndStream();
Token *PushToken(Token::TYPE type);
bool InFlowContext() const { return !m_flows.empty(); } bool InFlowContext() const { return !m_flows.empty(); }
bool InBlockContext() const { return m_flows.empty(); } bool InBlockContext() const { return m_flows.empty(); }
int GetFlowLevel() const { return m_flows.size(); } int GetFlowLevel() const { return m_flows.size(); }
Token::TYPE GetStartTokenFor(IndentMarker::INDENT_TYPE type) const;
IndentMarker *PushIndentTo(int column, IndentMarker::INDENT_TYPE type); IndentMarker *PushIndentTo(int column, IndentMarker::INDENT_TYPE type);
void PopIndentToHere(); void PopIndentToHere();
void PopAllIndents(); void PopAllIndents();
@@ -75,6 +79,7 @@ namespace YAML
void ThrowParserException(const std::string& msg) const; void ThrowParserException(const std::string& msg) const;
bool IsWhitespaceToBeEaten(char ch); bool IsWhitespaceToBeEaten(char ch);
const RegEx& GetValueRegex() const;
struct SimpleKey { struct SimpleKey {
SimpleKey(const Mark& mark_, int flowLevel_); SimpleKey(const Mark& mark_, int flowLevel_);
@@ -117,11 +122,14 @@ namespace YAML
// state info // state info
bool m_startedStream, m_endedStream; bool m_startedStream, m_endedStream;
bool m_simpleKeyAllowed; bool m_simpleKeyAllowed;
bool m_canBeJSONFlow;
std::stack <SimpleKey> m_simpleKeys; std::stack <SimpleKey> m_simpleKeys;
std::stack <IndentMarker> m_indents; std::stack <IndentMarker *> m_indents;
std::vector <IndentMarker *> m_indentRefs; // for "garbage collection"
std::stack <FLOW_MARKER> m_flows; std::stack <FLOW_MARKER> m_flows;
std::map <std::string, const Node *> m_anchors; std::map <std::string, const Node *> m_anchors;
}; };
} }
#endif // SCANNER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #endif // SCANNER_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "scanscalar.h" #include "scanscalar.h"
#include "scanner.h" #include "scanner.h"
#include "exp.h" #include "exp.h"
@@ -19,20 +18,26 @@ namespace YAML
// and different places in the above flow. // and different places in the above flow.
std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) std::string ScanScalar(Stream& INPUT, ScanScalarParams& params)
{ {
bool foundNonEmptyLine = false, pastOpeningBreak = false; bool foundNonEmptyLine = false;
bool pastOpeningBreak = (params.fold == FOLD_FLOW);
bool emptyLine = false, moreIndented = false; bool emptyLine = false, moreIndented = false;
int foldedNewlineCount = 0;
bool foldedNewlineStartedMoreIndented = false;
std::string scalar; std::string scalar;
params.leadingSpaces = false; params.leadingSpaces = false;
while(INPUT) { while(INPUT) {
// ******************************** // ********************************
// Phase #1: scan until line ending // Phase #1: scan until line ending
while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) {
std::size_t lastNonWhitespaceChar = scalar.size();
bool escapedNewline = false;
while(!params.end.Matches(INPUT) && !Exp::Break().Matches(INPUT)) {
if(!INPUT) if(!INPUT)
break; break;
// document indicator? // document indicator?
if(INPUT.column() == 0 && Exp::DocIndicator.Matches(INPUT)) { if(INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) {
if(params.onDocIndicator == BREAK) if(params.onDocIndicator == BREAK)
break; break;
else if(params.onDocIndicator == THROW) else if(params.onDocIndicator == THROW)
@@ -43,20 +48,26 @@ namespace YAML
pastOpeningBreak = true; pastOpeningBreak = true;
// escaped newline? (only if we're escaping on slash) // escaped newline? (only if we're escaping on slash)
if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) { if(params.escape == '\\' && Exp::EscBreak().Matches(INPUT)) {
int n = Exp::EscBreak.Match(INPUT); // eat escape character and get out (but preserve trailing whitespace!)
INPUT.eat(n); INPUT.get();
continue; lastNonWhitespaceChar = scalar.size();
escapedNewline = true;
break;
} }
// escape this? // escape this?
if(INPUT.peek() == params.escape) { if(INPUT.peek() == params.escape) {
scalar += Exp::Escape(INPUT); scalar += Exp::Escape(INPUT);
lastNonWhitespaceChar = scalar.size();
continue; continue;
} }
// otherwise, just add the damn character // otherwise, just add the damn character
scalar += INPUT.get(); char ch = INPUT.get();
scalar += ch;
if(ch != ' ' && ch != '\t')
lastNonWhitespaceChar = scalar.size();
} }
// eof? if we're looking to eat something, then we throw // eof? if we're looking to eat something, then we throw
@@ -67,7 +78,7 @@ namespace YAML
} }
// doc indicator? // doc indicator?
if(params.onDocIndicator == BREAK && INPUT.column() == 0 && Exp::DocIndicator.Matches(INPUT)) if(params.onDocIndicator == BREAK && INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT))
break; break;
// are we done via character match? // are we done via character match?
@@ -77,10 +88,14 @@ namespace YAML
INPUT.eat(n); INPUT.eat(n);
break; break;
} }
// do we remove trailing whitespace?
if(params.fold == FOLD_FLOW)
scalar.erase(lastNonWhitespaceChar);
// ******************************** // ********************************
// Phase #2: eat line ending // Phase #2: eat line ending
n = Exp::Break.Match(INPUT); n = Exp::Break().Match(INPUT);
INPUT.eat(n); INPUT.eat(n);
// ******************************** // ********************************
@@ -95,7 +110,7 @@ namespace YAML
params.indent = std::max(params.indent, INPUT.column()); params.indent = std::max(params.indent, INPUT.column());
// and then the rest of the whitespace // and then the rest of the whitespace
while(Exp::Blank.Matches(INPUT)) { while(Exp::Blank().Matches(INPUT)) {
// we check for tabs that masquerade as indentation // we check for tabs that masquerade as indentation
if(INPUT.peek() == '\t'&& INPUT.column() < params.indent && params.onTabInIndentation == THROW) if(INPUT.peek() == '\t'&& INPUT.column() < params.indent && params.onTabInIndentation == THROW)
throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION); throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION);
@@ -107,20 +122,39 @@ namespace YAML
} }
// was this an empty line? // was this an empty line?
bool nextEmptyLine = Exp::Break.Matches(INPUT); bool nextEmptyLine = Exp::Break().Matches(INPUT);
bool nextMoreIndented = (INPUT.peek() == ' '); bool nextMoreIndented = Exp::Blank().Matches(INPUT);
if(params.fold == FOLD_BLOCK && foldedNewlineCount == 0 && nextEmptyLine)
foldedNewlineStartedMoreIndented = moreIndented;
// for block scalars, we always start with a newline, so we should ignore it (not fold or keep) // for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
bool useNewLine = pastOpeningBreak; if(pastOpeningBreak) {
// and for folded scalars, we don't fold the very last newline to a space switch(params.fold) {
if(params.fold && !emptyLine && INPUT.column() < params.indent) case DONT_FOLD:
useNewLine = false; scalar += "\n";
break;
if(useNewLine) { case FOLD_BLOCK:
if(params.fold && !emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented) if(!emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented && INPUT.column() >= params.indent)
scalar += " "; scalar += " ";
else else if(nextEmptyLine)
scalar += "\n"; foldedNewlineCount++;
else
scalar += "\n";
if(!nextEmptyLine && foldedNewlineCount > 0) {
scalar += std::string(foldedNewlineCount - 1, '\n');
if(foldedNewlineStartedMoreIndented || nextMoreIndented)
scalar += "\n";
foldedNewlineCount = 0;
}
break;
case FOLD_FLOW:
if(nextEmptyLine)
scalar += "\n";
else if(!emptyLine && !nextEmptyLine && !escapedNewline)
scalar += " ";
break;
}
} }
emptyLine = nextEmptyLine; emptyLine = nextEmptyLine;
@@ -141,11 +175,11 @@ namespace YAML
scalar.erase(pos + 1); scalar.erase(pos + 1);
} }
if(params.chomp <= 0) { if(params.chomp == STRIP || params.chomp == CLIP) {
std::size_t pos = scalar.find_last_not_of('\n'); std::size_t pos = scalar.find_last_not_of('\n');
if(params.chomp == 0 && pos + 1 < scalar.size()) if(params.chomp == CLIP && pos + 1 < scalar.size())
scalar.erase(pos + 2); scalar.erase(pos + 2);
else if(params.chomp == -1 && pos < scalar.size()) else if(params.chomp == STRIP && pos < scalar.size())
scalar.erase(pos + 1); scalar.erase(pos + 1);
} }

View File

@@ -12,9 +12,10 @@ namespace YAML
{ {
enum CHOMP { STRIP = -1, CLIP, KEEP }; enum CHOMP { STRIP = -1, CLIP, KEEP };
enum ACTION { NONE, BREAK, THROW }; enum ACTION { NONE, BREAK, THROW };
enum FOLD { DONT_FOLD, FOLD_BLOCK, FOLD_FLOW };
struct ScanScalarParams { struct ScanScalarParams {
ScanScalarParams(): eatEnd(false), indent(0), detectIndent(false), eatLeadingWhitespace(0), escape(0), fold(false), ScanScalarParams(): eatEnd(false), indent(0), detectIndent(false), eatLeadingWhitespace(0), escape(0), fold(DONT_FOLD),
trimTrailingSpaces(0), chomp(CLIP), onDocIndicator(NONE), onTabInIndentation(NONE), leadingSpaces(false) {} trimTrailingSpaces(0), chomp(CLIP), onDocIndicator(NONE), onTabInIndentation(NONE), leadingSpaces(false) {}
// input: // input:
@@ -24,7 +25,7 @@ namespace YAML
bool detectIndent; // should we try to autodetect the indent? bool detectIndent; // should we try to autodetect the indent?
bool eatLeadingWhitespace; // should we continue eating this delicious indentation after 'indent' spaces? bool eatLeadingWhitespace; // should we continue eating this delicious indentation after 'indent' spaces?
char escape; // what character do we escape on (i.e., slash or single quote) (0 for none) char escape; // what character do we escape on (i.e., slash or single quote) (0 for none)
bool fold; // do we fold line ends? FOLD fold; // how do we fold line ends?
bool trimTrailingSpaces; // do we remove all trailing spaces (at the very end) bool trimTrailingSpaces; // do we remove all trailing spaces (at the very end)
CHOMP chomp; // do we strip, clip, or keep trailing newlines (at the very end) CHOMP chomp; // do we strip, clip, or keep trailing newlines (at the very end)
// Note: strip means kill all, clip means keep at most one, keep means keep all // Note: strip means kill all, clip means keep at most one, keep means keep all
@@ -39,3 +40,4 @@ namespace YAML
} }
#endif // SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #endif // SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66

84
src/scantag.cpp Normal file
View File

@@ -0,0 +1,84 @@
#include "scanner.h"
#include "regex.h"
#include "exp.h"
#include "exceptions.h"
namespace YAML
{
const std::string ScanVerbatimTag(Stream& INPUT)
{
std::string tag;
// eat the start character
INPUT.get();
while(INPUT) {
if(INPUT.peek() == Keys::VerbatimTagEnd) {
// eat the end character
INPUT.get();
return tag;
}
int n = Exp::URI().Match(INPUT);
if(n <= 0)
break;
tag += INPUT.get(n);
}
throw ParserException(INPUT.mark(), ErrorMsg::END_OF_VERBATIM_TAG);
}
const std::string ScanTagHandle(Stream& INPUT, bool& canBeHandle)
{
std::string tag;
canBeHandle = true;
Mark firstNonWordChar;
while(INPUT) {
if(INPUT.peek() == Keys::Tag) {
if(!canBeHandle)
throw ParserException(firstNonWordChar, ErrorMsg::CHAR_IN_TAG_HANDLE);
break;
}
int n = 0;
if(canBeHandle) {
n = Exp::Word().Match(INPUT);
if(n <= 0) {
canBeHandle = false;
firstNonWordChar = INPUT.mark();
}
}
if(!canBeHandle)
n = Exp::Tag().Match(INPUT);
if(n <= 0)
break;
tag += INPUT.get(n);
}
return tag;
}
const std::string ScanTagSuffix(Stream& INPUT)
{
std::string tag;
while(INPUT) {
int n = Exp::Tag().Match(INPUT);
if(n <= 0)
break;
tag += INPUT.get(n);
}
if(tag.empty())
throw ParserException(INPUT.mark(), ErrorMsg::TAG_WITH_NO_SUFFIX);
return tag;
}
}

18
src/scantag.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#ifndef SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include <string>
#include "stream.h"
namespace YAML
{
const std::string ScanVerbatimTag(Stream& INPUT);
const std::string ScanTagHandle(Stream& INPUT, bool& canBeHandle);
const std::string ScanTagSuffix(Stream& INPUT);
}
#endif // SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -1,9 +1,10 @@
#include "crt.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
#include "exp.h" #include "exp.h"
#include "scanscalar.h" #include "scanscalar.h"
#include "scantag.h"
#include "tag.h"
#include <sstream> #include <sstream>
namespace YAML namespace YAML
@@ -23,36 +24,34 @@ namespace YAML
PopAllSimpleKeys(); PopAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_canBeJSONFlow = false;
// store pos and eat indicator // store pos and eat indicator
Mark mark = INPUT.mark(); Token token(Token::DIRECTIVE, INPUT.mark());
INPUT.eat(1); INPUT.eat(1);
// read name // read name
while(INPUT && !Exp::BlankOrBreak.Matches(INPUT)) while(INPUT && !Exp::BlankOrBreak().Matches(INPUT))
name += INPUT.get(); token.value += INPUT.get();
// read parameters // read parameters
while(1) { while(1) {
// first get rid of whitespace // first get rid of whitespace
while(Exp::Blank.Matches(INPUT)) while(Exp::Blank().Matches(INPUT))
INPUT.eat(1); INPUT.eat(1);
// break on newline or comment // break on newline or comment
if(!INPUT || Exp::Break.Matches(INPUT) || Exp::Comment.Matches(INPUT)) if(!INPUT || Exp::Break().Matches(INPUT) || Exp::Comment().Matches(INPUT))
break; break;
// now read parameter // now read parameter
std::string param; std::string param;
while(INPUT && !Exp::BlankOrBreak.Matches(INPUT)) while(INPUT && !Exp::BlankOrBreak().Matches(INPUT))
param += INPUT.get(); param += INPUT.get();
params.push_back(param); token.params.push_back(param);
} }
Token token(Token::DIRECTIVE, mark);
token.value = name;
token.params = params;
m_tokens.push(token); m_tokens.push(token);
} }
@@ -62,6 +61,7 @@ namespace YAML
PopAllIndents(); PopAllIndents();
PopAllSimpleKeys(); PopAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_canBeJSONFlow = false;
// eat // eat
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
@@ -75,6 +75,7 @@ namespace YAML
PopAllIndents(); PopAllIndents();
PopAllSimpleKeys(); PopAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_canBeJSONFlow = false;
// eat // eat
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
@@ -88,6 +89,7 @@ namespace YAML
// flows can be simple keys // flows can be simple keys
InsertPotentialSimpleKey(); InsertPotentialSimpleKey();
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_canBeJSONFlow = false;
// eat // eat
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
@@ -105,10 +107,15 @@ namespace YAML
throw ParserException(INPUT.mark(), ErrorMsg::FLOW_END); throw ParserException(INPUT.mark(), ErrorMsg::FLOW_END);
// we might have a solo entry in the flow context // we might have a solo entry in the flow context
if(VerifySimpleKey()) if(InFlowContext()) {
m_tokens.push(Token(Token::VALUE, INPUT.mark())); if(m_flows.top() == FLOW_MAP && VerifySimpleKey())
m_tokens.push(Token(Token::VALUE, INPUT.mark()));
else if(m_flows.top() == FLOW_SEQ)
InvalidateSimpleKey();
}
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_canBeJSONFlow = true;
// eat // eat
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
@@ -127,11 +134,16 @@ namespace YAML
// FlowEntry // FlowEntry
void Scanner::ScanFlowEntry() void Scanner::ScanFlowEntry()
{ {
// we might have a solo entry in the flow context // we might have a solo entry in the flow context
if(VerifySimpleKey()) if(InFlowContext()) {
m_tokens.push(Token(Token::VALUE, INPUT.mark())); if(m_flows.top() == FLOW_MAP && VerifySimpleKey())
m_tokens.push(Token(Token::VALUE, INPUT.mark()));
else if(m_flows.top() == FLOW_SEQ)
InvalidateSimpleKey();
}
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_canBeJSONFlow = false;
// eat // eat
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
@@ -152,6 +164,7 @@ namespace YAML
PushIndentTo(INPUT.column(), IndentMarker::SEQ); PushIndentTo(INPUT.column(), IndentMarker::SEQ);
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_canBeJSONFlow = false;
// eat // eat
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
@@ -184,6 +197,7 @@ namespace YAML
{ {
// and check that simple key // and check that simple key
bool isSimpleKey = VerifySimpleKey(); bool isSimpleKey = VerifySimpleKey();
m_canBeJSONFlow = false;
if(isSimpleKey) { if(isSimpleKey) {
// can't follow a simple key with another simple key (dunno why, though - it seems fine) // can't follow a simple key with another simple key (dunno why, though - it seems fine)
@@ -216,6 +230,7 @@ namespace YAML
// insert a potential simple key // insert a potential simple key
InsertPotentialSimpleKey(); InsertPotentialSimpleKey();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_canBeJSONFlow = false;
// eat the indicator // eat the indicator
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
@@ -223,7 +238,7 @@ namespace YAML
alias = (indicator == Keys::Alias); alias = (indicator == Keys::Alias);
// now eat the content // now eat the content
while(Exp::AlphaNumeric.Matches(INPUT)) while(Exp::AlphaNumeric().Matches(INPUT))
name += INPUT.get(); name += INPUT.get();
// we need to have read SOMETHING! // we need to have read SOMETHING!
@@ -231,7 +246,7 @@ namespace YAML
throw ParserException(INPUT.mark(), alias ? ErrorMsg::ALIAS_NOT_FOUND : ErrorMsg::ANCHOR_NOT_FOUND); throw ParserException(INPUT.mark(), alias ? ErrorMsg::ALIAS_NOT_FOUND : ErrorMsg::ANCHOR_NOT_FOUND);
// and needs to end correctly // and needs to end correctly
if(INPUT && !Exp::AnchorEnd.Matches(INPUT)) if(INPUT && !Exp::AnchorEnd().Matches(INPUT))
throw ParserException(INPUT.mark(), alias ? ErrorMsg::CHAR_IN_ALIAS : ErrorMsg::CHAR_IN_ANCHOR); throw ParserException(INPUT.mark(), alias ? ErrorMsg::CHAR_IN_ALIAS : ErrorMsg::CHAR_IN_ANCHOR);
// and we're done // and we're done
@@ -243,37 +258,35 @@ namespace YAML
// Tag // Tag
void Scanner::ScanTag() void Scanner::ScanTag()
{ {
std::string handle, suffix;
// insert a potential simple key // insert a potential simple key
InsertPotentialSimpleKey(); InsertPotentialSimpleKey();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_canBeJSONFlow = false;
Token token(Token::TAG, INPUT.mark());
// eat the indicator // eat the indicator
Mark mark = INPUT.mark(); INPUT.get();
handle += INPUT.get();
if(INPUT && INPUT.peek() == Keys::VerbatimTagStart){
std::string tag = ScanVerbatimTag(INPUT);
// read the handle token.value = tag;
while(INPUT && INPUT.peek() != Keys::Tag && !Exp::BlankOrBreak.Matches(INPUT)) token.data = Tag::VERBATIM;
handle += INPUT.get();
// is there a suffix?
if(INPUT.peek() == Keys::Tag) {
// eat the indicator
handle += INPUT.get();
// then read it
while(INPUT && !Exp::BlankOrBreak.Matches(INPUT))
suffix += INPUT.get();
} else { } else {
// this is a bit weird: we keep just the '!' as the handle and move the rest to the suffix bool canBeHandle;
suffix = handle.substr(1); token.value = ScanTagHandle(INPUT, canBeHandle);
handle = "!"; token.data = (token.value.empty() ? Tag::SECONDARY_HANDLE : Tag::PRIMARY_HANDLE);
// is there a suffix?
if(canBeHandle && INPUT.peek() == Keys::Tag) {
// eat the indicator
INPUT.get();
token.params.push_back(ScanTagSuffix(INPUT));
token.data = Tag::NAMED_HANDLE;
}
} }
Token token(Token::TAG, mark);
token.value = handle;
token.params.push_back(suffix);
m_tokens.push(token); m_tokens.push(token);
} }
@@ -284,13 +297,13 @@ namespace YAML
// set up the scanning parameters // set up the scanning parameters
ScanScalarParams params; ScanScalarParams params;
params.end = (InFlowContext() ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment); params.end = (InFlowContext() ? Exp::EndScalarInFlow() : Exp::EndScalar()) || (Exp::BlankOrBreak() + Exp::Comment());
params.eatEnd = false; params.eatEnd = false;
params.indent = (InFlowContext() ? 0 : GetTopIndent() + 1); params.indent = (InFlowContext() ? 0 : GetTopIndent() + 1);
params.fold = true; params.fold = FOLD_FLOW;
params.eatLeadingWhitespace = true; params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = true; params.trimTrailingSpaces = true;
params.chomp = CLIP; params.chomp = STRIP;
params.onDocIndicator = BREAK; params.onDocIndicator = BREAK;
params.onTabInIndentation = THROW; params.onTabInIndentation = THROW;
@@ -302,6 +315,7 @@ namespace YAML
// can have a simple key only if we ended the scalar by starting a new line // can have a simple key only if we ended the scalar by starting a new line
m_simpleKeyAllowed = params.leadingSpaces; m_simpleKeyAllowed = params.leadingSpaces;
m_canBeJSONFlow = false;
// finally, check and see if we ended on an illegal character // finally, check and see if we ended on an illegal character
//if(Exp::IllegalCharInScalar.Matches(INPUT)) //if(Exp::IllegalCharInScalar.Matches(INPUT))
@@ -323,11 +337,11 @@ namespace YAML
// setup the scanning parameters // setup the scanning parameters
ScanScalarParams params; ScanScalarParams params;
params.end = (single ? RegEx(quote) && !Exp::EscSingleQuote : RegEx(quote)); params.end = (single ? RegEx(quote) && !Exp::EscSingleQuote() : RegEx(quote));
params.eatEnd = true; params.eatEnd = true;
params.escape = (single ? '\'' : '\\'); params.escape = (single ? '\'' : '\\');
params.indent = 0; params.indent = 0;
params.fold = true; params.fold = FOLD_FLOW;
params.eatLeadingWhitespace = true; params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = false; params.trimTrailingSpaces = false;
params.chomp = CLIP; params.chomp = CLIP;
@@ -344,6 +358,7 @@ namespace YAML
// and scan // and scan
scalar = ScanScalar(INPUT, params); scalar = ScanScalar(INPUT, params);
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_canBeJSONFlow = true;
Token token(Token::SCALAR, mark); Token token(Token::SCALAR, mark);
token.value = scalar; token.value = scalar;
@@ -365,17 +380,18 @@ namespace YAML
// eat block indicator ('|' or '>') // eat block indicator ('|' or '>')
Mark mark = INPUT.mark(); Mark mark = INPUT.mark();
char indicator = INPUT.get(); char indicator = INPUT.get();
params.fold = (indicator == Keys::FoldedScalar); params.fold = (indicator == Keys::FoldedScalar ? FOLD_BLOCK : DONT_FOLD);
// eat chomping/indentation indicators // eat chomping/indentation indicators
int n = Exp::Chomp.Match(INPUT); params.chomp = CLIP;
int n = Exp::Chomp().Match(INPUT);
for(int i=0;i<n;i++) { for(int i=0;i<n;i++) {
char ch = INPUT.get(); char ch = INPUT.get();
if(ch == '+') if(ch == '+')
params.chomp = KEEP; params.chomp = KEEP;
else if(ch == '-') else if(ch == '-')
params.chomp = STRIP; params.chomp = STRIP;
else if(Exp::Digit.Matches(ch)) { else if(Exp::Digit().Matches(ch)) {
if(ch == '0') if(ch == '0')
throw ParserException(INPUT.mark(), ErrorMsg::ZERO_INDENT_IN_BLOCK); throw ParserException(INPUT.mark(), ErrorMsg::ZERO_INDENT_IN_BLOCK);
@@ -385,16 +401,16 @@ namespace YAML
} }
// now eat whitespace // now eat whitespace
while(Exp::Blank.Matches(INPUT)) while(Exp::Blank().Matches(INPUT))
INPUT.eat(1); INPUT.eat(1);
// and comments to the end of the line // and comments to the end of the line
if(Exp::Comment.Matches(INPUT)) if(Exp::Comment().Matches(INPUT))
while(INPUT && !Exp::Break.Matches(INPUT)) while(INPUT && !Exp::Break().Matches(INPUT))
INPUT.eat(1); INPUT.eat(1);
// if it's not a line break, then we ran into a bad character inline // if it's not a line break, then we ran into a bad character inline
if(INPUT && !Exp::Break.Matches(INPUT)) if(INPUT && !Exp::Break().Matches(INPUT))
throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_BLOCK); throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_BLOCK);
// set the initial indentation // set the initial indentation
@@ -409,6 +425,7 @@ namespace YAML
// simple keys always ok after block scalars (since we're gonna start a new line anyways) // simple keys always ok after block scalars (since we're gonna start a new line anyways)
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_canBeJSONFlow = false;
Token token(Token::SCALAR, mark); Token token(Token::SCALAR, mark);
token.value = scalar; token.value = scalar;

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "sequence.h" #include "sequence.h"
#include "node.h" #include "node.h"
#include "scanner.h" #include "scanner.h"
@@ -60,7 +59,7 @@ namespace YAML
return m_data.size(); return m_data.size();
} }
void Sequence::Parse(Scanner *pScanner, const ParserState& state) void Sequence::Parse(Scanner *pScanner, ParserState& state)
{ {
Clear(); Clear();
@@ -72,10 +71,11 @@ namespace YAML
} }
} }
void Sequence::ParseBlock(Scanner *pScanner, const ParserState& state) void Sequence::ParseBlock(Scanner *pScanner, ParserState& state)
{ {
// eat start token // eat start token
pScanner->pop(); pScanner->pop();
state.PushCollectionType(ParserState::BLOCK_SEQ);
while(1) { while(1) {
if(pScanner->empty()) if(pScanner->empty())
@@ -101,12 +101,15 @@ namespace YAML
pNode->Parse(pScanner, state); pNode->Parse(pScanner, state);
} }
state.PopCollectionType(ParserState::BLOCK_SEQ);
} }
void Sequence::ParseFlow(Scanner *pScanner, const ParserState& state) void Sequence::ParseFlow(Scanner *pScanner, ParserState& state)
{ {
// eat start token // eat start token
pScanner->pop(); pScanner->pop();
state.PushCollectionType(ParserState::FLOW_SEQ);
while(1) { while(1) {
if(pScanner->empty()) if(pScanner->empty())
@@ -130,6 +133,8 @@ namespace YAML
else if(token.type != Token::FLOW_SEQ_END) else if(token.type != Token::FLOW_SEQ_END)
throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW); throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW);
} }
state.PopCollectionType(ParserState::FLOW_SEQ);
} }
void Sequence::Write(Emitter& out) const void Sequence::Write(Emitter& out) const

View File

@@ -26,7 +26,7 @@ namespace YAML
virtual Node *GetNode(std::size_t i) const; virtual Node *GetNode(std::size_t i) const;
virtual std::size_t GetSize() const; virtual std::size_t GetSize() const;
virtual void Parse(Scanner *pScanner, const ParserState& state); virtual void Parse(Scanner *pScanner, ParserState& state);
virtual void Write(Emitter& out) const; virtual void Write(Emitter& out) const;
virtual bool IsSequence() const { return true; } virtual bool IsSequence() const { return true; }
@@ -38,8 +38,8 @@ namespace YAML
virtual int Compare(Map *) { return -1; } virtual int Compare(Map *) { return -1; }
private: private:
void ParseBlock(Scanner *pScanner, const ParserState& state); void ParseBlock(Scanner *pScanner, ParserState& state);
void ParseFlow(Scanner *pScanner, const ParserState& state); void ParseFlow(Scanner *pScanner, ParserState& state);
protected: protected:
std::vector <Node *> m_data; std::vector <Node *> m_data;

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
@@ -13,9 +12,11 @@ namespace YAML
void Scanner::SimpleKey::Validate() void Scanner::SimpleKey::Validate()
{ {
// Note: pIndent will *not* be garbage here; see below // Note: pIndent will *not* be garbage here;
// we "garbage collect" them so we can
// always refer to them
if(pIndent) if(pIndent)
pIndent->isValid = true; pIndent->status = IndentMarker::VALID;
if(pMapStart) if(pMapStart)
pMapStart->status = Token::VALID; pMapStart->status = Token::VALID;
if(pKey) if(pKey)
@@ -24,8 +25,8 @@ namespace YAML
void Scanner::SimpleKey::Invalidate() void Scanner::SimpleKey::Invalidate()
{ {
// Note: pIndent might be a garbage pointer here, but that's ok if(pIndent)
// An indent will only be popped if the simple key is invalid pIndent->status = IndentMarker::INVALID;
if(pMapStart) if(pMapStart)
pMapStart->status = Token::INVALID; pMapStart->status = Token::INVALID;
if(pKey) if(pKey)
@@ -37,9 +38,6 @@ namespace YAML
{ {
if(!m_simpleKeyAllowed) if(!m_simpleKeyAllowed)
return false; return false;
if(InFlowContext() && m_flows.top() != FLOW_MAP)
return false;
return !ExistsActiveSimpleKey(); return !ExistsActiveSimpleKey();
} }
@@ -67,11 +65,13 @@ namespace YAML
SimpleKey key(INPUT.mark(), GetFlowLevel()); SimpleKey key(INPUT.mark(), GetFlowLevel());
// first add a map start, if necessary // first add a map start, if necessary
key.pIndent = PushIndentTo(INPUT.column(), IndentMarker::MAP); if(InBlockContext()) {
if(key.pIndent) { key.pIndent = PushIndentTo(INPUT.column(), IndentMarker::MAP);
key.pIndent->isValid = false; if(key.pIndent) {
key.pMapStart = key.pIndent->pStartToken; key.pIndent->status = IndentMarker::UNKNOWN;
key.pMapStart->status = Token::UNVERIFIED; key.pMapStart = key.pIndent->pStartToken;
key.pMapStart->status = Token::UNVERIFIED;
}
} }
// then add the (now unverified) key // then add the (now unverified) key
@@ -136,3 +136,4 @@ namespace YAML
m_simpleKeys.pop(); m_simpleKeys.pop();
} }
} }

View File

@@ -1,4 +1,3 @@
#include "crt.h"
#include "stream.h" #include "stream.h"
#include <iostream> #include <iostream>
#include "exp.h" #include "exp.h"

View File

@@ -30,6 +30,11 @@ namespace YAML
++m_offset; ++m_offset;
return *this; return *this;
} }
StringCharSource& operator += (std::size_t offset) {
m_offset += offset;
return *this;
}
private: private:
const char *m_str; const char *m_str;
std::size_t m_size; std::size_t m_size;

50
src/tag.cpp Normal file
View File

@@ -0,0 +1,50 @@
#include "tag.h"
#include "token.h"
#include "parserstate.h"
#include <cassert>
namespace YAML
{
Tag::Tag(const Token& token): type(static_cast<TYPE>(token.data))
{
switch(type) {
case VERBATIM:
value = token.value;
break;
case PRIMARY_HANDLE:
value = token.value;
break;
case SECONDARY_HANDLE:
value = token.value;
break;
case NAMED_HANDLE:
handle = token.value;
value = token.params[0];
break;
case NON_SPECIFIC:
break;
default:
assert(false);
}
}
const std::string Tag::Translate(const ParserState& state)
{
switch(type) {
case VERBATIM:
return value;
case PRIMARY_HANDLE:
return state.TranslateTagHandle("!") + value;
case SECONDARY_HANDLE:
return state.TranslateTagHandle("!!") + value;
case NAMED_HANDLE:
return state.TranslateTagHandle("!" + handle + "!") + value;
case NON_SPECIFIC:
// TODO:
return "!";
default:
assert(false);
}
}
}

26
src/tag.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#ifndef TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include <string>
namespace YAML
{
struct Token;
struct ParserState;
struct Tag {
enum TYPE {
VERBATIM, PRIMARY_HANDLE, SECONDARY_HANDLE, NAMED_HANDLE, NON_SPECIFIC
};
Tag(const Token& token);
const std::string Translate(const ParserState& state);
TYPE type;
std::string handle, value;
};
}
#endif // TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -24,6 +24,7 @@ namespace YAML
"FLOW_MAP_START", "FLOW_MAP_START",
"FLOW_SEQ_END", "FLOW_SEQ_END",
"FLOW_MAP_END", "FLOW_MAP_END",
"FLOW_MAP_COMPACT",
"FLOW_ENTRY", "FLOW_ENTRY",
"KEY", "KEY",
"VALUE", "VALUE",
@@ -49,6 +50,7 @@ namespace YAML
FLOW_MAP_START, FLOW_MAP_START,
FLOW_SEQ_END, FLOW_SEQ_END,
FLOW_MAP_END, FLOW_MAP_END,
FLOW_MAP_COMPACT,
FLOW_ENTRY, FLOW_ENTRY,
KEY, KEY,
VALUE, VALUE,
@@ -59,7 +61,7 @@ namespace YAML
}; };
// data // data
Token(TYPE type_, const Mark& mark_): status(VALID), type(type_), mark(mark_) {} Token(TYPE type_, const Mark& mark_): status(VALID), type(type_), mark(mark_), data(0) {}
friend std::ostream& operator << (std::ostream& out, const Token& token) { friend std::ostream& operator << (std::ostream& out, const Token& token) {
out << TokenNames[token.type] << std::string(": ") << token.value; out << TokenNames[token.type] << std::string(": ") << token.value;
@@ -73,6 +75,7 @@ namespace YAML
Mark mark; Mark mark;
std::string value; std::string value;
std::vector <std::string> params; std::vector <std::string> params;
int data;
}; };
} }

226
test.vcproj Normal file
View File

@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="test"
ProjectGUID="{D1108F40-6ADF-467E-A95A-236C39A515C5}"
RootNamespace="test"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalOptions="/D_SCL_SECURE_NO_WARNINGS"
Optimization="0"
AdditionalIncludeDirectories="include"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
WarningLevel="3"
DebugInformationFormat="4"
DisableSpecificWarnings="4127;4355"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="yamlcppd.lib"
AdditionalLibraryDirectories="lib"
GenerateDebugInformation="true"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalOptions="/D_SCL_SECURE_NO_WARNINGS"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="include"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
WarningLevel="3"
DebugInformationFormat="3"
DisableSpecificWarnings="4127;4355"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="yamlcpp.lib"
AdditionalLibraryDirectories="lib"
GenerateDebugInformation="true"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\test\emittertests.cpp"
>
</File>
<File
RelativePath=".\test\main.cpp"
>
</File>
<File
RelativePath=".\test\parsertests.cpp"
>
</File>
<File
RelativePath=".\test\spectests.cpp"
>
</File>
<File
RelativePath=".\test\tests.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\test\emittertests.h"
>
</File>
<File
RelativePath=".\test\parsertests.h"
>
</File>
<File
RelativePath=".\test\spectests.h"
>
</File>
<File
RelativePath=".\test\tests.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

10
test/CMakeLists.txt Normal file
View File

@@ -0,0 +1,10 @@
file(GLOB test_headers [a-z]*.h)
file(GLOB test_sources [a-z]*.cpp)
add_executable(run-tests
${test_sources}
${test_headers}
)
target_link_libraries(run-tests yaml-cpp)
add_test(yaml-reader-test run-tests)

View File

@@ -9,7 +9,7 @@ namespace Test
void SimpleScalar(YAML::Emitter& out, std::string& desiredOutput) { void SimpleScalar(YAML::Emitter& out, std::string& desiredOutput) {
out << "Hello, World!"; out << "Hello, World!";
desiredOutput = "Hello, World!"; desiredOutput = "--- Hello, World!";
} }
void SimpleSeq(YAML::Emitter& out, std::string& desiredOutput) { void SimpleSeq(YAML::Emitter& out, std::string& desiredOutput) {
@@ -19,7 +19,7 @@ namespace Test
out << "milk"; out << "milk";
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- eggs\n- bread\n- milk"; desiredOutput = "---\n- eggs\n- bread\n- milk";
} }
void SimpleFlowSeq(YAML::Emitter& out, std::string& desiredOutput) { void SimpleFlowSeq(YAML::Emitter& out, std::string& desiredOutput) {
@@ -30,7 +30,7 @@ namespace Test
out << "Moe"; out << "Moe";
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "[Larry, Curly, Moe]"; desiredOutput = "--- [Larry, Curly, Moe]";
} }
void EmptyFlowSeq(YAML::Emitter& out, std::string& desiredOutput) { void EmptyFlowSeq(YAML::Emitter& out, std::string& desiredOutput) {
@@ -38,7 +38,7 @@ namespace Test
out << YAML::BeginSeq; out << YAML::BeginSeq;
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "[]"; desiredOutput = "--- []";
} }
void NestedBlockSeq(YAML::Emitter& out, std::string& desiredOutput) { void NestedBlockSeq(YAML::Emitter& out, std::string& desiredOutput) {
@@ -47,7 +47,7 @@ namespace Test
out << YAML::BeginSeq << "subitem 1" << "subitem 2" << YAML::EndSeq; out << YAML::BeginSeq << "subitem 1" << "subitem 2" << YAML::EndSeq;
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- item 1\n-\n - subitem 1\n - subitem 2"; desiredOutput = "---\n- item 1\n-\n - subitem 1\n - subitem 2";
} }
void NestedFlowSeq(YAML::Emitter& out, std::string& desiredOutput) { void NestedFlowSeq(YAML::Emitter& out, std::string& desiredOutput) {
@@ -56,7 +56,7 @@ namespace Test
out << YAML::Flow << YAML::BeginSeq << "two" << "three" << YAML::EndSeq; out << YAML::Flow << YAML::BeginSeq << "two" << "three" << YAML::EndSeq;
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- one\n- [two, three]"; desiredOutput = "---\n- one\n- [two, three]";
} }
void SimpleMap(YAML::Emitter& out, std::string& desiredOutput) { void SimpleMap(YAML::Emitter& out, std::string& desiredOutput) {
@@ -67,7 +67,7 @@ namespace Test
out << YAML::Value << "3B"; out << YAML::Value << "3B";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "name: Ryan Braun\nposition: 3B"; desiredOutput = "---\nname: Ryan Braun\nposition: 3B";
} }
void SimpleFlowMap(YAML::Emitter& out, std::string& desiredOutput) { void SimpleFlowMap(YAML::Emitter& out, std::string& desiredOutput) {
@@ -79,7 +79,7 @@ namespace Test
out << YAML::Value << "blue"; out << YAML::Value << "blue";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "{shape: square, color: blue}"; desiredOutput = "--- {shape: square, color: blue}";
} }
void MapAndList(YAML::Emitter& out, std::string& desiredOutput) { void MapAndList(YAML::Emitter& out, std::string& desiredOutput) {
@@ -90,7 +90,7 @@ namespace Test
out << YAML::Value << YAML::BeginSeq << "Sasha" << "Malia" << YAML::EndSeq; out << YAML::Value << YAML::BeginSeq << "Sasha" << "Malia" << YAML::EndSeq;
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "name: Barack Obama\nchildren:\n - Sasha\n - Malia"; desiredOutput = "---\nname: Barack Obama\nchildren:\n - Sasha\n - Malia";
} }
void ListAndMap(YAML::Emitter& out, std::string& desiredOutput) { void ListAndMap(YAML::Emitter& out, std::string& desiredOutput) {
@@ -103,7 +103,7 @@ namespace Test
out << "item 2"; out << "item 2";
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- item 1\n-\n pens: 8\n pencils: 14\n- item 2"; desiredOutput = "---\n- item 1\n-\n pens: 8\n pencils: 14\n- item 2";
} }
void NestedBlockMap(YAML::Emitter& out, std::string& desiredOutput) { void NestedBlockMap(YAML::Emitter& out, std::string& desiredOutput) {
@@ -119,7 +119,7 @@ namespace Test
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "name: Fred\ngrades:\n algebra: A\n physics: C+\n literature: B"; desiredOutput = "---\nname: Fred\ngrades:\n algebra: A\n physics: C+\n literature: B";
} }
void NestedFlowMap(YAML::Emitter& out, std::string& desiredOutput) { void NestedFlowMap(YAML::Emitter& out, std::string& desiredOutput) {
@@ -136,7 +136,7 @@ namespace Test
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "{name: Fred, grades: {algebra: A, physics: C+, literature: B}}"; desiredOutput = "--- {name: Fred, grades: {algebra: A, physics: C+, literature: B}}";
} }
void MapListMix(YAML::Emitter& out, std::string& desiredOutput) { void MapListMix(YAML::Emitter& out, std::string& desiredOutput) {
@@ -149,7 +149,7 @@ namespace Test
out << YAML::Key << "invincible" << YAML::Value << YAML::OnOffBool << false; out << YAML::Key << "invincible" << YAML::Value << YAML::OnOffBool << false;
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "name: Bob\nposition: [2, 4]\ninvincible: off"; desiredOutput = "---\nname: Bob\nposition: [2, 4]\ninvincible: off";
} }
void SimpleLongKey(YAML::Emitter& out, std::string& desiredOutput) void SimpleLongKey(YAML::Emitter& out, std::string& desiredOutput)
@@ -162,7 +162,7 @@ namespace Test
out << YAML::Value << 145; out << YAML::Value << 145;
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "? height\n: 5'9\"\n? weight\n: 145"; desiredOutput = "---\n? height\n: 5'9\"\n? weight\n: 145";
} }
void SingleLongKey(YAML::Emitter& out, std::string& desiredOutput) void SingleLongKey(YAML::Emitter& out, std::string& desiredOutput)
@@ -176,7 +176,7 @@ namespace Test
out << YAML::Value << 145; out << YAML::Value << 145;
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "age: 24\n? height\n: 5'9\"\nweight: 145"; desiredOutput = "---\nage: 24\n? height\n: 5'9\"\nweight: 145";
} }
void ComplexLongKey(YAML::Emitter& out, std::string& desiredOutput) void ComplexLongKey(YAML::Emitter& out, std::string& desiredOutput)
@@ -189,7 +189,7 @@ namespace Test
out << YAML::Value << "demon"; out << YAML::Value << "demon";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "?\n - 1\n - 3\n: monster\n? [2, 0]\n: demon"; desiredOutput = "---\n?\n - 1\n - 3\n: monster\n? [2, 0]\n: demon";
} }
void AutoLongKey(YAML::Emitter& out, std::string& desiredOutput) void AutoLongKey(YAML::Emitter& out, std::string& desiredOutput)
@@ -203,7 +203,7 @@ namespace Test
out << YAML::Value << "angel"; out << YAML::Value << "angel";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "?\n - 1\n - 3\n: monster\n? [2, 0]\n: demon\nthe origin: angel"; desiredOutput = "---\n?\n - 1\n - 3\n: monster\n? [2, 0]\n: demon\nthe origin: angel";
} }
void ScalarFormat(YAML::Emitter& out, std::string& desiredOutput) void ScalarFormat(YAML::Emitter& out, std::string& desiredOutput)
@@ -217,7 +217,7 @@ namespace Test
out << YAML::Literal << "literal scalar\nthat may span\nmany, many\nlines and have \"whatever\" crazy\tsymbols that we like"; out << YAML::Literal << "literal scalar\nthat may span\nmany, many\nlines and have \"whatever\" crazy\tsymbols that we like";
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- simple scalar\n- 'explicit single-quoted scalar'\n- \"explicit double-quoted scalar\"\n- \"auto-detected\\x0adouble-quoted scalar\"\n- a non-\"auto-detected\" double-quoted scalar\n- |\n literal scalar\n that may span\n many, many\n lines and have \"whatever\" crazy\tsymbols that we like"; desiredOutput = "---\n- simple scalar\n- 'explicit single-quoted scalar'\n- \"explicit double-quoted scalar\"\n- \"auto-detected\\x0adouble-quoted scalar\"\n- a non-\"auto-detected\" double-quoted scalar\n- |\n literal scalar\n that may span\n many, many\n lines and have \"whatever\" crazy\tsymbols that we like";
} }
void AutoLongKeyScalar(YAML::Emitter& out, std::string& desiredOutput) void AutoLongKeyScalar(YAML::Emitter& out, std::string& desiredOutput)
@@ -227,7 +227,7 @@ namespace Test
out << YAML::Value << "and its value"; out << YAML::Value << "and its value";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "? |\n multi-line\n scalar\n: and its value"; desiredOutput = "---\n? |\n multi-line\n scalar\n: and its value";
} }
void LongKeyFlowMap(YAML::Emitter& out, std::string& desiredOutput) void LongKeyFlowMap(YAML::Emitter& out, std::string& desiredOutput)
@@ -240,7 +240,7 @@ namespace Test
out << YAML::Value << "and its value"; out << YAML::Value << "and its value";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "{simple key: and value, ? long key: and its value}"; desiredOutput = "--- {simple key: and value, ? long key: and its value}";
} }
void BlockMapAsKey(YAML::Emitter& out, std::string& desiredOutput) void BlockMapAsKey(YAML::Emitter& out, std::string& desiredOutput)
@@ -255,7 +255,7 @@ namespace Test
out << "total value"; out << "total value";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "?\n key: value\n next key: next value\n: total value"; desiredOutput = "---\n?\n key: value\n next key: next value\n: total value";
} }
void AliasAndAnchor(YAML::Emitter& out, std::string& desiredOutput) void AliasAndAnchor(YAML::Emitter& out, std::string& desiredOutput)
@@ -269,7 +269,7 @@ namespace Test
out << YAML::Alias("fred"); out << YAML::Alias("fred");
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- &fred\n name: Fred\n age: 42\n- *fred"; desiredOutput = "---\n- &fred\n name: Fred\n age: 42\n- *fred";
} }
void AliasAndAnchorWithNull(YAML::Emitter& out, std::string& desiredOutput) void AliasAndAnchorWithNull(YAML::Emitter& out, std::string& desiredOutput)
@@ -279,7 +279,98 @@ namespace Test
out << YAML::Alias("fred"); out << YAML::Alias("fred");
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- &fred ~\n- *fred"; desiredOutput = "---\n- &fred ~\n- *fred";
}
void SimpleVerbatimTag(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::VerbatimTag("!foo") << "bar";
desiredOutput = "--- !<!foo> bar";
}
void VerbatimTagInBlockSeq(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginSeq;
out << YAML::VerbatimTag("!foo") << "bar";
out << "baz";
out << YAML::EndSeq;
desiredOutput = "---\n- !<!foo> bar\n- baz";
}
void VerbatimTagInFlowSeq(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::Flow << YAML::BeginSeq;
out << YAML::VerbatimTag("!foo") << "bar";
out << "baz";
out << YAML::EndSeq;
desiredOutput = "--- [!<!foo> bar, baz]";
}
void VerbatimTagInFlowSeqWithNull(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::Flow << YAML::BeginSeq;
out << YAML::VerbatimTag("!foo") << YAML::Null;
out << "baz";
out << YAML::EndSeq;
desiredOutput = "--- [!<!foo> ~, baz]";
}
void VerbatimTagInBlockMap(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginMap;
out << YAML::Key << YAML::VerbatimTag("!foo") << "bar";
out << YAML::Value << YAML::VerbatimTag("!waz") << "baz";
out << YAML::EndMap;
desiredOutput = "---\n!<!foo> bar: !<!waz> baz";
}
void VerbatimTagInFlowMap(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::Flow << YAML::BeginMap;
out << YAML::Key << YAML::VerbatimTag("!foo") << "bar";
out << YAML::Value << "baz";
out << YAML::EndMap;
desiredOutput = "--- {!<!foo> bar: baz}";
}
void VerbatimTagInFlowMapWithNull(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::Flow << YAML::BeginMap;
out << YAML::Key << YAML::VerbatimTag("!foo") << YAML::Null;
out << YAML::Value << "baz";
out << YAML::EndMap;
desiredOutput = "--- {!<!foo> ~: baz}";
}
void VerbatimTagWithEmptySeq(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::VerbatimTag("!foo") << YAML::BeginSeq << YAML::EndSeq;
desiredOutput = "--- !<!foo>\n[]";
}
void VerbatimTagWithEmptyMap(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::VerbatimTag("!bar") << YAML::BeginMap << YAML::EndMap;
desiredOutput = "--- !<!bar>\n{}";
}
void VerbatimTagWithEmptySeqAndMap(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginSeq;
out << YAML::VerbatimTag("!foo") << YAML::BeginSeq << YAML::EndSeq;
out << YAML::VerbatimTag("!bar") << YAML::BeginMap << YAML::EndMap;
out << YAML::EndSeq;
desiredOutput = "---\n- !<!foo>\n []\n- !<!bar>\n {}";
} }
void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput) void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput)
@@ -335,7 +426,7 @@ namespace Test
out << YAML::Value << YAML::Alias("id001"); out << YAML::Value << YAML::Alias("id001");
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "receipt: Oz-Ware Purchase Invoice\ndate: 2007-08-06\ncustomer:\n given: Dorothy\n family: Gale\nitems:\n -\n part_no: A4786\n descrip: Water Bucket (Filled)\n price: 1.47\n quantity: 4\n -\n part_no: E1628\n descrip: High Heeled \"Ruby\" Slippers\n price: 100.27\n quantity: 1\nbill-to: &id001\n street: |\n 123 Tornado Alley\n Suite 16\n city: East Westville\n state: KS\nship-to: *id001"; desiredOutput = "---\nreceipt: Oz-Ware Purchase Invoice\ndate: 2007-08-06\ncustomer:\n given: Dorothy\n family: Gale\nitems:\n -\n part_no: A4786\n descrip: Water Bucket (Filled)\n price: 1.47\n quantity: 4\n -\n part_no: E1628\n descrip: High Heeled \"Ruby\" Slippers\n price: 100.27\n quantity: 1\nbill-to: &id001\n street: |\n 123 Tornado Alley\n Suite 16\n city: East Westville\n state: KS\nship-to: *id001";
} }
void STLContainers(YAML::Emitter& out, std::string& desiredOutput) void STLContainers(YAML::Emitter& out, std::string& desiredOutput)
@@ -355,7 +446,7 @@ namespace Test
out << ages; out << ages;
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- [2, 3, 5, 7, 11, 13]\n-\n Daniel: 26\n Jesse: 24"; desiredOutput = "---\n- [2, 3, 5, 7, 11, 13]\n-\n Daniel: 26\n Jesse: 24";
} }
void SimpleComment(YAML::Emitter& out, std::string& desiredOutput) void SimpleComment(YAML::Emitter& out, std::string& desiredOutput)
@@ -365,7 +456,7 @@ namespace Test
out << YAML::Value << "least squares" << YAML::Comment("should we change this method?"); out << YAML::Value << "least squares" << YAML::Comment("should we change this method?");
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "method: least squares # should we change this method?"; desiredOutput = "---\nmethod: least squares # should we change this method?";
} }
void MultiLineComment(YAML::Emitter& out, std::string& desiredOutput) void MultiLineComment(YAML::Emitter& out, std::string& desiredOutput)
@@ -375,7 +466,7 @@ namespace Test
out << "item 2"; out << "item 2";
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- item 1 # really really long\n # comment that couldn't possibly\n # fit on one line\n- item 2"; desiredOutput = "---\n- item 1 # really really long\n # comment that couldn't possibly\n # fit on one line\n- item 2";
} }
void ComplexComments(YAML::Emitter& out, std::string& desiredOutput) void ComplexComments(YAML::Emitter& out, std::string& desiredOutput)
@@ -385,7 +476,7 @@ namespace Test
out << YAML::Value << "value"; out << YAML::Value << "value";
out << YAML::EndMap; out << YAML::EndMap;
desiredOutput = "? long key # long key\n: value"; desiredOutput = "---\n? long key # long key\n: value";
} }
void Indentation(YAML::Emitter& out, std::string& desiredOutput) void Indentation(YAML::Emitter& out, std::string& desiredOutput)
@@ -398,7 +489,7 @@ namespace Test
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "-\n key 1: value 1\n key 2:\n - a\n - b\n - c"; desiredOutput = "---\n-\n key 1: value 1\n key 2:\n - a\n - b\n - c";
} }
void SimpleGlobalSettings(YAML::Emitter& out, std::string& desiredOutput) void SimpleGlobalSettings(YAML::Emitter& out, std::string& desiredOutput)
@@ -413,7 +504,7 @@ namespace Test
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "-\n ? key 1\n : value 1\n ? key 2\n : [a, b, c]"; desiredOutput = "---\n-\n ? key 1\n : value 1\n ? key 2\n : [a, b, c]";
} }
void ComplexGlobalSettings(YAML::Emitter& out, std::string& desiredOutput) void ComplexGlobalSettings(YAML::Emitter& out, std::string& desiredOutput)
@@ -432,7 +523,7 @@ namespace Test
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "-\n key 1: value 1\n key 2: [a, b, c]\n-\n ? [1, 2]\n :\n a: b"; desiredOutput = "---\n-\n key 1: value 1\n key 2: [a, b, c]\n-\n ? [1, 2]\n :\n a: b";
} }
void Null(YAML::Emitter& out, std::string& desiredOutput) void Null(YAML::Emitter& out, std::string& desiredOutput)
@@ -445,7 +536,95 @@ namespace Test
out << YAML::EndMap; out << YAML::EndMap;
out << YAML::EndSeq; out << YAML::EndSeq;
desiredOutput = "- ~\n-\n null value: ~\n ~: null key"; desiredOutput = "---\n- ~\n-\n null value: ~\n ~: null key";
}
void EscapedUnicode(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::EscapeNonAscii << "\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2";
desiredOutput = "--- \"$ \\xa2 \\u20ac \\U00024b62\"";
}
void Unicode(YAML::Emitter& out, std::string& desiredOutput)
{
out << "\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2";
desiredOutput = "--- \x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2";
}
void DoubleQuotedUnicode(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::DoubleQuoted << "\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2";
desiredOutput = "--- \"\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2\"";
}
struct Foo {
Foo(): x(0) {}
Foo(int x_, const std::string& bar_): x(x_), bar(bar_) {}
int x;
std::string bar;
};
YAML::Emitter& operator << (YAML::Emitter& out, const Foo& foo) {
out << YAML::BeginMap;
out << YAML::Key << "x" << YAML::Value << foo.x;
out << YAML::Key << "bar" << YAML::Value << foo.bar;
out << YAML::EndMap;
return out;
}
void UserType(YAML::Emitter& out, std::string& desiredOutput)
{
out << YAML::BeginSeq;
out << Foo(5, "hello");
out << Foo(3, "goodbye");
out << YAML::EndSeq;
desiredOutput = "---\n-\n x: 5\n bar: hello\n-\n x: 3\n bar: goodbye";
}
void UserTypeInContainer(YAML::Emitter& out, std::string& desiredOutput)
{
std::vector<Foo> fv;
fv.push_back(Foo(5, "hello"));
fv.push_back(Foo(3, "goodbye"));
out << fv;
desiredOutput = "---\n-\n x: 5\n bar: hello\n-\n x: 3\n bar: goodbye";
}
template <typename T>
YAML::Emitter& operator << (YAML::Emitter& out, const T *v) {
if(v)
out << *v;
else
out << YAML::Null;
return out;
}
void PointerToInt(YAML::Emitter& out, std::string& desiredOutput)
{
int foo = 5;
int *bar = &foo;
int *baz = 0;
out << YAML::BeginSeq;
out << bar << baz;
out << YAML::EndSeq;
desiredOutput = "---\n- 5\n- ~";
}
void PointerToUserType(YAML::Emitter& out, std::string& desiredOutput)
{
Foo foo(5, "hello");
Foo *bar = &foo;
Foo *baz = 0;
out << YAML::BeginSeq;
out << bar << baz;
out << YAML::EndSeq;
desiredOutput = "---\n-\n x: 5\n bar: hello\n- ~";
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -534,4 +713,111 @@ namespace Test
out << YAML::EndSeq; out << YAML::EndSeq;
} }
} }
namespace {
void RunEmitterTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, int& passed, int& total) {
YAML::Emitter out;
std::string desiredOutput;
test(out, desiredOutput);
std::string output = out.c_str();
if(output == desiredOutput) {
passed++;
} else {
std::cout << "Emitter test failed: " << name << "\n";
std::cout << "Output:\n";
std::cout << output << "<<<\n";
std::cout << "Desired output:\n";
std::cout << desiredOutput << "<<<\n";
}
total++;
}
void RunEmitterErrorTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, int& passed, int& total) {
YAML::Emitter out;
std::string desiredError;
test(out, desiredError);
std::string lastError = out.GetLastError();
if(!out.good() && lastError == desiredError) {
passed++;
} else {
std::cout << "Emitter test failed: " << name << "\n";
if(out.good())
std::cout << "No error detected\n";
else
std::cout << "Detected error: " << lastError << "\n";
std::cout << "Expected error: " << desiredError << "\n";
}
total++;
}
}
bool RunEmitterTests()
{
int passed = 0;
int total = 0;
RunEmitterTest(&Emitter::SimpleScalar, "simple scalar", passed, total);
RunEmitterTest(&Emitter::SimpleSeq, "simple seq", passed, total);
RunEmitterTest(&Emitter::SimpleFlowSeq, "simple flow seq", passed, total);
RunEmitterTest(&Emitter::EmptyFlowSeq, "empty flow seq", passed, total);
RunEmitterTest(&Emitter::NestedBlockSeq, "nested block seq", passed, total);
RunEmitterTest(&Emitter::NestedFlowSeq, "nested flow seq", passed, total);
RunEmitterTest(&Emitter::SimpleMap, "simple map", passed, total);
RunEmitterTest(&Emitter::SimpleFlowMap, "simple flow map", passed, total);
RunEmitterTest(&Emitter::MapAndList, "map and list", passed, total);
RunEmitterTest(&Emitter::ListAndMap, "list and map", passed, total);
RunEmitterTest(&Emitter::NestedBlockMap, "nested block map", passed, total);
RunEmitterTest(&Emitter::NestedFlowMap, "nested flow map", passed, total);
RunEmitterTest(&Emitter::MapListMix, "map list mix", passed, total);
RunEmitterTest(&Emitter::SimpleLongKey, "simple long key", passed, total);
RunEmitterTest(&Emitter::SingleLongKey, "single long key", passed, total);
RunEmitterTest(&Emitter::ComplexLongKey, "complex long key", passed, total);
RunEmitterTest(&Emitter::AutoLongKey, "auto long key", passed, total);
RunEmitterTest(&Emitter::ScalarFormat, "scalar format", passed, total);
RunEmitterTest(&Emitter::AutoLongKeyScalar, "auto long key scalar", passed, total);
RunEmitterTest(&Emitter::LongKeyFlowMap, "long key flow map", passed, total);
RunEmitterTest(&Emitter::BlockMapAsKey, "block map as key", passed, total);
RunEmitterTest(&Emitter::AliasAndAnchor, "alias and anchor", passed, total);
RunEmitterTest(&Emitter::AliasAndAnchorWithNull, "alias and anchor with null", passed, total);
RunEmitterTest(&Emitter::SimpleVerbatimTag, "simple verbatim tag", passed, total);
RunEmitterTest(&Emitter::VerbatimTagInBlockSeq, "verbatim tag in block seq", passed, total);
RunEmitterTest(&Emitter::VerbatimTagInFlowSeq, "verbatim tag in flow seq", passed, total);
RunEmitterTest(&Emitter::VerbatimTagInFlowSeqWithNull, "verbatim tag in flow seq with null", passed, total);
RunEmitterTest(&Emitter::VerbatimTagInBlockMap, "verbatim tag in block map", passed, total);
RunEmitterTest(&Emitter::VerbatimTagInFlowMap, "verbatim tag in flow map", passed, total);
RunEmitterTest(&Emitter::VerbatimTagInFlowMapWithNull, "verbatim tag in flow map with null", passed, total);
RunEmitterTest(&Emitter::VerbatimTagWithEmptySeq, "verbatim tag with empty seq", passed, total);
RunEmitterTest(&Emitter::VerbatimTagWithEmptyMap, "verbatim tag with empty map", passed, total);
RunEmitterTest(&Emitter::VerbatimTagWithEmptySeqAndMap, "verbatim tag with empty seq and map", passed, total);
RunEmitterTest(&Emitter::ComplexDoc, "complex doc", passed, total);
RunEmitterTest(&Emitter::STLContainers, "STL containers", passed, total);
RunEmitterTest(&Emitter::SimpleComment, "simple comment", passed, total);
RunEmitterTest(&Emitter::MultiLineComment, "multi-line comment", passed, total);
RunEmitterTest(&Emitter::ComplexComments, "complex comments", passed, total);
RunEmitterTest(&Emitter::Indentation, "indentation", passed, total);
RunEmitterTest(&Emitter::SimpleGlobalSettings, "simple global settings", passed, total);
RunEmitterTest(&Emitter::ComplexGlobalSettings, "complex global settings", passed, total);
RunEmitterTest(&Emitter::Null, "null", passed, total);
RunEmitterTest(&Emitter::EscapedUnicode, "escaped unicode", passed, total);
RunEmitterTest(&Emitter::Unicode, "unicode", passed, total);
RunEmitterTest(&Emitter::DoubleQuotedUnicode, "double quoted unicode", passed, total);
RunEmitterTest(&Emitter::UserType, "user type", passed, total);
RunEmitterTest(&Emitter::UserTypeInContainer, "user type in container", passed, total);
RunEmitterTest(&Emitter::PointerToInt, "pointer to int", passed, total);
RunEmitterTest(&Emitter::PointerToUserType, "pointer to user type", passed, total);
RunEmitterErrorTest(&Emitter::ExtraEndSeq, "extra EndSeq", passed, total);
RunEmitterErrorTest(&Emitter::ExtraEndMap, "extra EndMap", passed, total);
RunEmitterErrorTest(&Emitter::BadSingleQuoted, "bad single quoted string", passed, total);
RunEmitterErrorTest(&Emitter::InvalidAnchor, "invalid anchor", passed, total);
RunEmitterErrorTest(&Emitter::InvalidAlias, "invalid alias", passed, total);
RunEmitterErrorTest(&Emitter::MissingKey, "missing key", passed, total);
RunEmitterErrorTest(&Emitter::MissingValue, "missing value", passed, total);
RunEmitterErrorTest(&Emitter::UnexpectedKey, "unexpected key", passed, total);
RunEmitterErrorTest(&Emitter::UnexpectedValue, "unexpected value", passed, total);
std::cout << "Emitter tests: " << passed << "/" << total << " passed\n";
return passed == total;
}
} }

11
test/emittertests.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#ifndef EMITTERTESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define EMITTERTESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
namespace Test {
bool RunEmitterTests();
}
#endif // EMITTERTESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66

7
test/main.cpp Normal file
View File

@@ -0,0 +1,7 @@
#include "tests.h"
int main()
{
Test::RunAll();
return 0;
}

View File

@@ -118,7 +118,7 @@ namespace Test
inputScalar = "http://example.com/foo#bar"; inputScalar = "http://example.com/foo#bar";
desiredOutput = "http://example.com/foo#bar"; desiredOutput = "http://example.com/foo#bar";
} }
bool SimpleSeq() bool SimpleSeq()
{ {
std::string input = std::string input =
@@ -610,5 +610,379 @@ namespace Test
return true; return true;
} }
bool BlockKeyWithNullValue()
{
std::string input =
"key:\n"
"just a key: value";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
if(doc.size() != 2)
return false;
if(!IsNull(doc["key"]))
return false;
if(doc["just a key"] != "value")
return false;
return true;
}
bool Bases()
{
std::string input =
"- 15\n"
"- 0x10\n"
"- 030\n"
"- 0xffffffff\n";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
if(doc.size() != 4)
return false;
if(doc[0] != 15)
return false;
if(doc[1] != 0x10)
return false;
if(doc[2] != 030)
return false;
if(doc[3] != 0xffffffff)
return false;
return true;
}
bool KeyNotFound()
{
std::string input = "key: value";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
try {
doc["bad key"];
} catch(const YAML::Exception& e) {
if(e.msg != YAML::ErrorMsg::KEY_NOT_FOUND + ": bad key")
throw;
}
try {
doc[5];
} catch(const YAML::Exception& e) {
if(e.msg != YAML::ErrorMsg::KEY_NOT_FOUND + ": 5")
throw;
}
try {
doc[2.5];
} catch(const YAML::Exception& e) {
if(e.msg != YAML::ErrorMsg::KEY_NOT_FOUND + ": 2.5")
throw;
}
return true;
}
bool DuplicateKey()
{
std::string input = "{a: 1, b: 2, c: 3, a: 4}";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
if(doc["a"] != 1)
return false;
if(doc["b"] != 2)
return false;
if(doc["c"] != 3)
return false;
return true;
}
}
namespace {
void RunScalarParserTest(void (*test)(std::string&, std::string&), const std::string& name, int& passed, int& total) {
std::string error;
std::string inputScalar, desiredOutput;
std::string output;
bool ok = true;
try {
test(inputScalar, desiredOutput);
std::stringstream stream(inputScalar);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
doc >> output;
} catch(const YAML::Exception& e) {
ok = false;
error = e.what();
}
if(ok && output == desiredOutput) {
passed++;
} else {
std::cout << "Parser test failed: " << name << "\n";
if(error != "")
std::cout << "Caught exception: " << error << "\n";
else {
std::cout << "Output:\n" << output << "<<<\n";
std::cout << "Desired output:\n" << desiredOutput << "<<<\n";
}
}
total++;
}
void RunParserTest(bool (*test)(), const std::string& name, int& passed, int& total) {
std::string error;
bool ok = true;
try {
ok = test();
} catch(const YAML::Exception& e) {
ok = false;
error = e.msg;
}
if(ok) {
passed++;
} else {
std::cout << "Parser test failed: " << name << "\n";
if(error != "")
std::cout << "Caught exception: " << error << "\n";
}
total++;
}
typedef void (*EncodingFn)(std::ostream&, int);
inline char Byte(int ch)
{
return static_cast<char>(static_cast<unsigned char>(static_cast<unsigned int>(ch)));
}
void EncodeToUtf8(std::ostream& stream, int ch)
{
if (ch <= 0x7F)
{
stream << Byte(ch);
}
else if (ch <= 0x7FF)
{
stream << Byte(0xC0 | (ch >> 6));
stream << Byte(0x80 | (ch & 0x3F));
}
else if (ch <= 0xFFFF)
{
stream << Byte(0xE0 | (ch >> 12));
stream << Byte(0x80 | ((ch >> 6) & 0x3F));
stream << Byte(0x80 | (ch & 0x3F));
}
else if (ch <= 0x1FFFFF)
{
stream << Byte(0xF0 | (ch >> 18));
stream << Byte(0x80 | ((ch >> 12) & 0x3F));
stream << Byte(0x80 | ((ch >> 6) & 0x3F));
stream << Byte(0x80 | (ch & 0x3F));
}
}
bool SplitUtf16HighChar(std::ostream& stream, EncodingFn encoding, int ch)
{
int biasedValue = ch - 0x10000;
if (biasedValue < 0)
{
return false;
}
int high = 0xD800 | (biasedValue >> 10);
int low = 0xDC00 | (biasedValue & 0x3FF);
encoding(stream, high);
encoding(stream, low);
return true;
}
void EncodeToUtf16LE(std::ostream& stream, int ch)
{
if (!SplitUtf16HighChar(stream, &EncodeToUtf16LE, ch))
{
stream << Byte(ch & 0xFF) << Byte(ch >> 8);
}
}
void EncodeToUtf16BE(std::ostream& stream, int ch)
{
if (!SplitUtf16HighChar(stream, &EncodeToUtf16BE, ch))
{
stream << Byte(ch >> 8) << Byte(ch & 0xFF);
}
}
void EncodeToUtf32LE(std::ostream& stream, int ch)
{
stream << Byte(ch & 0xFF) << Byte((ch >> 8) & 0xFF)
<< Byte((ch >> 16) & 0xFF) << Byte((ch >> 24) & 0xFF);
}
void EncodeToUtf32BE(std::ostream& stream, int ch)
{
stream << Byte((ch >> 24) & 0xFF) << Byte((ch >> 16) & 0xFF)
<< Byte((ch >> 8) & 0xFF) << Byte(ch & 0xFF);
}
class EncodingTester
{
public:
EncodingTester(EncodingFn encoding, bool declareEncoding)
{
if (declareEncoding)
{
encoding(m_yaml, 0xFEFF);
}
AddEntry(encoding, 0x0021, 0x007E); // Basic Latin
AddEntry(encoding, 0x00A1, 0x00FF); // Latin-1 Supplement
AddEntry(encoding, 0x0660, 0x06FF); // Arabic (largest contiguous block)
// CJK unified ideographs (multiple lines)
AddEntry(encoding, 0x4E00, 0x4EFF);
AddEntry(encoding, 0x4F00, 0x4FFF);
AddEntry(encoding, 0x5000, 0x51FF); // 512 character line
AddEntry(encoding, 0x5200, 0x54FF); // 768 character line
AddEntry(encoding, 0x5500, 0x58FF); // 1024 character line
AddEntry(encoding, 0x103A0, 0x103C3); // Old Persian
m_yaml.seekg(0, std::ios::beg);
}
std::istream& stream() {return m_yaml;}
const std::vector<std::string>& entries() {return m_entries;}
private:
std::stringstream m_yaml;
std::vector<std::string> m_entries;
void AddEntry(EncodingFn encoding, int startCh, int endCh)
{
encoding(m_yaml, '-');
encoding(m_yaml, ' ');
encoding(m_yaml, '|');
encoding(m_yaml, '\n');
encoding(m_yaml, ' ');
encoding(m_yaml, ' ');
std::stringstream entry;
for (int ch = startCh; ch <= endCh; ++ch)
{
encoding(m_yaml, ch);
EncodeToUtf8(entry, ch);
}
encoding(m_yaml, '\n');
m_entries.push_back(entry.str());
}
};
void RunEncodingTest(EncodingFn encoding, bool declareEncoding, const std::string& name, int& passed, int& total)
{
EncodingTester tester(encoding, declareEncoding);
std::string error;
bool ok = true;
try {
YAML::Parser parser(tester.stream());
YAML::Node doc;
parser.GetNextDocument(doc);
YAML::Iterator itNode = doc.begin();
std::vector<std::string>::const_iterator itEntry = tester.entries().begin();
for (; (itNode != doc.end()) && (itEntry != tester.entries().end()); ++itNode, ++itEntry)
{
std::string stScalarValue;
if (!itNode->GetScalar(stScalarValue) && (stScalarValue == *itEntry))
{
break;
}
}
if ((itNode != doc.end()) || (itEntry != tester.entries().end()))
{
ok = false;
}
} catch(const YAML::Exception& e) {
ok = false;
error = e.msg;
}
if(ok) {
passed++;
} else {
std::cout << "Parser test failed: " << name << "\n";
if(error != "")
std::cout << "Caught exception: " << error << "\n";
}
total++;
}
}
bool RunParserTests()
{
int passed = 0;
int total = 0;
RunScalarParserTest(&Parser::SimpleScalar, "simple scalar", passed, total);
RunScalarParserTest(&Parser::MultiLineScalar, "multi-line scalar", passed, total);
RunScalarParserTest(&Parser::LiteralScalar, "literal scalar", passed, total);
RunScalarParserTest(&Parser::FoldedScalar, "folded scalar", passed, total);
RunScalarParserTest(&Parser::ChompedFoldedScalar, "chomped folded scalar", passed, total);
RunScalarParserTest(&Parser::ChompedLiteralScalar, "chomped literal scalar", passed, total);
RunScalarParserTest(&Parser::FoldedScalarWithIndent, "folded scalar with indent", passed, total);
RunScalarParserTest(&Parser::ColonScalar, "colon scalar", passed, total);
RunScalarParserTest(&Parser::QuotedScalar, "quoted scalar", passed, total);
RunScalarParserTest(&Parser::CommaScalar, "comma scalar", passed, total);
RunScalarParserTest(&Parser::DashScalar, "dash scalar", passed, total);
RunScalarParserTest(&Parser::URLScalar, "url scalar", passed, total);
RunParserTest(&Parser::SimpleSeq, "simple seq", passed, total);
RunParserTest(&Parser::SimpleMap, "simple map", passed, total);
RunParserTest(&Parser::FlowSeq, "flow seq", passed, total);
RunParserTest(&Parser::FlowMap, "flow map", passed, total);
RunParserTest(&Parser::FlowMapWithOmittedKey, "flow map with omitted key", passed, total);
RunParserTest(&Parser::FlowMapWithOmittedValue, "flow map with omitted value", passed, total);
RunParserTest(&Parser::FlowMapWithSoloEntry, "flow map with solo entry", passed, total);
RunParserTest(&Parser::FlowMapEndingWithSoloEntry, "flow map ending with solo entry", passed, total);
RunParserTest(&Parser::QuotedSimpleKeys, "quoted simple keys", passed, total);
RunParserTest(&Parser::CompressedMapAndSeq, "compressed map and seq", passed, total);
RunParserTest(&Parser::NullBlockSeqEntry, "null block seq entry", passed, total);
RunParserTest(&Parser::NullBlockMapKey, "null block map key", passed, total);
RunParserTest(&Parser::NullBlockMapValue, "null block map value", passed, total);
RunParserTest(&Parser::SimpleAlias, "simple alias", passed, total);
RunParserTest(&Parser::AliasWithNull, "alias with null", passed, total);
RunParserTest(&Parser::AnchorInSimpleKey, "anchor in simple key", passed, total);
RunParserTest(&Parser::AliasAsSimpleKey, "alias as simple key", passed, total);
RunParserTest(&Parser::ExplicitDoc, "explicit doc", passed, total);
RunParserTest(&Parser::MultipleDocs, "multiple docs", passed, total);
RunParserTest(&Parser::ExplicitEndDoc, "explicit end doc", passed, total);
RunParserTest(&Parser::MultipleDocsWithSomeExplicitIndicators, "multiple docs with some explicit indicators", passed, total);
RunParserTest(&Parser::BlockKeyWithNullValue, "block key with null value", passed, total);
RunParserTest(&Parser::Bases, "bases", passed, total);
RunParserTest(&Parser::KeyNotFound, "key not found", passed, total);
RunParserTest(&Parser::DuplicateKey, "duplicate key", passed, total);
RunEncodingTest(&EncodeToUtf8, false, "UTF-8, no BOM", passed, total);
RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with BOM", passed, total);
RunEncodingTest(&EncodeToUtf16LE, false, "UTF-16LE, no BOM", passed, total);
RunEncodingTest(&EncodeToUtf16LE, true, "UTF-16LE with BOM", passed, total);
RunEncodingTest(&EncodeToUtf16BE, false, "UTF-16BE, no BOM", passed, total);
RunEncodingTest(&EncodeToUtf16BE, true, "UTF-16BE with BOM", passed, total);
RunEncodingTest(&EncodeToUtf32LE, false, "UTF-32LE, no BOM", passed, total);
RunEncodingTest(&EncodeToUtf32LE, true, "UTF-32LE with BOM", passed, total);
RunEncodingTest(&EncodeToUtf32BE, false, "UTF-32BE, no BOM", passed, total);
RunEncodingTest(&EncodeToUtf32BE, true, "UTF-32BE with BOM", passed, total);
std::cout << "Parser tests: " << passed << "/" << total << " passed\n";
return passed == total;
} }
} }

11
test/parsertests.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#ifndef PARSERTESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define PARSERTESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
namespace Test {
bool RunParserTests();
}
#endif // PARSERTESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66

2166
test/spectests.cpp Normal file

File diff suppressed because it is too large Load Diff

29
test/tests.cpp Normal file
View File

@@ -0,0 +1,29 @@
#include "tests.h"
#include "emittertests.h"
#include "parsertests.h"
#include "spectests.h"
#include "yaml.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
namespace Test
{
void RunAll()
{
bool passed = true;
if(!RunParserTests())
passed = false;
if(!RunEmitterTests())
passed = false;
if(!RunSpecTests())
passed = false;
if(passed)
std::cout << "All tests passed!\n";
}
}

51
test/tests.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#ifndef TESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define TESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include <string>
namespace Test {
void RunAll();
namespace Parser {
// scalar tests
void SimpleScalar(std::string& inputScalar, std::string& desiredOutput);
void MultiLineScalar(std::string& inputScalar, std::string& desiredOutput);
void LiteralScalar(std::string& inputScalar, std::string& desiredOutput);
void FoldedScalar(std::string& inputScalar, std::string& desiredOutput);
void ChompedFoldedScalar(std::string& inputScalar, std::string& desiredOutput);
void ChompedLiteralScalar(std::string& inputScalar, std::string& desiredOutput);
void FoldedScalarWithIndent(std::string& inputScalar, std::string& desiredOutput);
void ColonScalar(std::string& inputScalar, std::string& desiredOutput);
void QuotedScalar(std::string& inputScalar, std::string& desiredOutput);
void CommaScalar(std::string& inputScalar, std::string& desiredOutput);
void DashScalar(std::string& inputScalar, std::string& desiredOutput);
void URLScalar(std::string& inputScalar, std::string& desiredOutput);
// misc tests
bool SimpleSeq();
bool SimpleMap();
bool FlowSeq();
bool FlowMap();
bool FlowMapWithOmittedKey();
bool FlowMapWithOmittedValue();
bool FlowMapWithSoloEntry();
bool FlowMapEndingWithSoloEntry();
bool QuotedSimpleKeys();
bool CompressedMapAndSeq();
bool NullBlockSeqEntry();
bool NullBlockMapKey();
bool NullBlockMapValue();
bool SimpleAlias();
bool AliasWithNull();
bool AnchorInSimpleKey();
bool AliasAsSimpleKey();
bool ExplicitDoc();
bool MultipleDocs();
bool ExplicitEndDoc();
bool MultipleDocsWithSomeExplicitIndicators();
}
}
#endif // TESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -11,9 +11,8 @@ int main(int argc, char **argv)
std::istream& input = (argc > 1 ? fin : std::cin); std::istream& input = (argc > 1 ? fin : std::cin);
try { try {
YAML::Parser parser(input); YAML::Parser parser(input);
while(parser) { YAML::Node doc;
YAML::Node doc; while(parser.GetNextDocument(doc)) {
parser.GetNextDocument(doc);
YAML::Emitter emitter; YAML::Emitter emitter;
emitter << doc; emitter << doc;
std::cout << emitter.c_str() << "\n"; std::cout << emitter.c_str() << "\n";

View File

@@ -1,10 +0,0 @@
file(GLOB yaml-reader_headers *.h)
file(GLOB yaml-reader_sources *.cpp)
add_executable(yaml-reader
${yaml-reader_sources}
${yaml-reader_headers}
)
target_link_libraries(yaml-reader yaml-cpp)
add_test(yaml-reader-test yaml-reader)

View File

@@ -1,11 +0,0 @@
#include "tests.h"
int main()
{
#ifdef WINDOWS
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF);
#endif // WINDOWS
Test::RunAll();
return 0;
}

View File

@@ -1,356 +0,0 @@
#include "spectests.h"
#include "yaml.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
namespace {
struct TEST {
TEST(): ok(false) {}
TEST(bool ok_): ok(ok_) {}
TEST(const char *error_): ok(false), error(error_) {}
bool ok;
std::string error;
};
}
#define YAML_ASSERT(cond) do { if(!(cond)) return "Assert failed: " #cond; } while(false)
namespace Test {
namespace {
void RunSpecTest(TEST (*test)(), const std::string& index, const std::string& name, bool& passed) {
TEST ret;
try {
ret = test();
} catch(const YAML::Exception& e) {
ret.ok = false;
ret.error = e.msg;
}
if(ret.ok) {
std::cout << "Spec test " << index << " passed: " << name << "\n";
} else {
passed = false;
std::cout << "Spec test " << index << " failed: " << name << "\n";
std::cout << ret.error << "\n";
}
}
}
namespace Spec {
TEST SeqScalars() {
std::string input =
"- Mark McGwire\n"
"- Sammy Sosa\n"
"- Ken Griffey";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 3);
YAML_ASSERT(doc[0] == "Mark McGwire");
YAML_ASSERT(doc[1] == "Sammy Sosa");
YAML_ASSERT(doc[2] == "Ken Griffey");
return true;
}
TEST MappingScalarsToScalars() {
std::string input =
"hr: 65 # Home runs\n"
"avg: 0.278 # Batting average\n"
"rbi: 147 # Runs Batted In";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 3);
YAML_ASSERT(doc["hr"] == "65");
YAML_ASSERT(doc["avg"] == "0.278");
YAML_ASSERT(doc["rbi"] == "147");
return true;
}
TEST MappingScalarsToSequences() {
std::string input =
"american:\n"
"- Boston Red Sox\n"
"- Detroit Tigers\n"
"- New York Yankees\n"
"national:\n"
"- New York Mets\n"
"- Chicago Cubs\n"
"- Atlanta Braves";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 2);
YAML_ASSERT(doc["american"].size() == 3);
YAML_ASSERT(doc["american"][0] == "Boston Red Sox");
YAML_ASSERT(doc["american"][1] == "Detroit Tigers");
YAML_ASSERT(doc["american"][2] == "New York Yankees");
YAML_ASSERT(doc["national"].size() == 3);
YAML_ASSERT(doc["national"][0] == "New York Mets");
YAML_ASSERT(doc["national"][1] == "Chicago Cubs");
YAML_ASSERT(doc["national"][2] == "Atlanta Braves");
return true;
}
TEST SequenceOfMappings()
{
std::string input =
"-\n"
" name: Mark McGwire\n"
" hr: 65\n"
" avg: 0.278\n"
"-\n"
" name: Sammy Sosa\n"
" hr: 63\n"
" avg: 0.288";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 2);
YAML_ASSERT(doc[0].size() == 3);
YAML_ASSERT(doc[0]["name"] == "Mark McGwire");
YAML_ASSERT(doc[0]["hr"] == "65");
YAML_ASSERT(doc[0]["avg"] == "0.278");
YAML_ASSERT(doc[1].size() == 3);
YAML_ASSERT(doc[1]["name"] == "Sammy Sosa");
YAML_ASSERT(doc[1]["hr"] == "63");
YAML_ASSERT(doc[1]["avg"] == "0.288");
return true;
}
TEST SequenceOfSequences()
{
std::string input =
"- [name , hr, avg ]\n"
"- [Mark McGwire, 65, 0.278]\n"
"- [Sammy Sosa , 63, 0.288]";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 3);
YAML_ASSERT(doc[0].size() == 3);
YAML_ASSERT(doc[0][0] == "name");
YAML_ASSERT(doc[0][1] == "hr");
YAML_ASSERT(doc[0][2] == "avg");
YAML_ASSERT(doc[1].size() == 3);
YAML_ASSERT(doc[1][0] == "Mark McGwire");
YAML_ASSERT(doc[1][1] == "65");
YAML_ASSERT(doc[1][2] == "0.278");
YAML_ASSERT(doc[2].size() == 3);
YAML_ASSERT(doc[2][0] == "Sammy Sosa");
YAML_ASSERT(doc[2][1] == "63");
YAML_ASSERT(doc[2][2] == "0.288");
return true;
}
TEST MappingOfMappings()
{
std::string input =
"Mark McGwire: {hr: 65, avg: 0.278}\n"
"Sammy Sosa: {\n"
" hr: 63,\n"
" avg: 0.288\n"
" }";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 2);
YAML_ASSERT(doc["Mark McGwire"].size() == 2);
YAML_ASSERT(doc["Mark McGwire"]["hr"] == "65");
YAML_ASSERT(doc["Mark McGwire"]["avg"] == "0.278");
YAML_ASSERT(doc["Sammy Sosa"].size() == 2);
YAML_ASSERT(doc["Sammy Sosa"]["hr"] == "63");
YAML_ASSERT(doc["Sammy Sosa"]["avg"] == "0.288");
return true;
}
TEST TwoDocumentsInAStream()
{
std::string input =
"# Ranking of 1998 home runs\n"
"---\n"
"- Mark McGwire\n"
"- Sammy Sosa\n"
"- Ken Griffey\n"
"\n"
"# Team ranking\n"
"---\n"
"- Chicago Cubs\n"
"- St Louis Cardinals";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 3);
YAML_ASSERT(doc[0] == "Mark McGwire");
YAML_ASSERT(doc[1] == "Sammy Sosa");
YAML_ASSERT(doc[2] == "Ken Griffey");
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 2);
YAML_ASSERT(doc[0] == "Chicago Cubs");
YAML_ASSERT(doc[1] == "St Louis Cardinals");
return true;
}
TEST PlayByPlayFeed()
{
std::string input =
"---\n"
"time: 20:03:20\n"
"player: Sammy Sosa\n"
"action: strike (miss)\n"
"...\n"
"---\n"
"time: 20:03:47\n"
"player: Sammy Sosa\n"
"action: grand slam\n"
"...";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 3);
YAML_ASSERT(doc["time"] == "20:03:20");
YAML_ASSERT(doc["player"] == "Sammy Sosa");
YAML_ASSERT(doc["action"] == "strike (miss)");
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 3);
YAML_ASSERT(doc["time"] == "20:03:47");
YAML_ASSERT(doc["player"] == "Sammy Sosa");
YAML_ASSERT(doc["action"] == "grand slam");
return true;
}
TEST SingleDocumentWithTwoComments()
{
std::string input =
"---\n"
"hr: # 1998 hr ranking\n"
" - Mark McGwire\n"
" - Sammy Sosa\n"
"rbi:\n"
" # 1998 rbi ranking\n"
" - Sammy Sosa\n"
" - Ken Griffey";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 2);
YAML_ASSERT(doc["hr"].size() == 2);
YAML_ASSERT(doc["hr"][0] == "Mark McGwire");
YAML_ASSERT(doc["hr"][1] == "Sammy Sosa");
YAML_ASSERT(doc["rbi"].size() == 2);
YAML_ASSERT(doc["rbi"][0] == "Sammy Sosa");
YAML_ASSERT(doc["rbi"][1] == "Ken Griffey");
return true;
}
TEST SimpleAnchor()
{
std::string input =
"---\n"
"hr:\n"
" - Mark McGwire\n"
" # Following node labeled SS\n"
" - &SS Sammy Sosa\n"
"rbi:\n"
" - *SS # Subsequent occurrence\n"
" - Ken Griffey";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 2);
YAML_ASSERT(doc["hr"].size() == 2);
YAML_ASSERT(doc["hr"][0] == "Mark McGwire");
YAML_ASSERT(doc["hr"][1] == "Sammy Sosa");
YAML_ASSERT(doc["rbi"].size() == 2);
YAML_ASSERT(doc["rbi"][0] == "Sammy Sosa");
YAML_ASSERT(doc["rbi"][1] == "Ken Griffey");
return true;
}
struct Pair {
Pair() {}
Pair(const std::string& f, const std::string& s): first(f), second(s) {}
std::string first, second;
};
bool operator == (const Pair& p, const Pair& q) {
return p.first == q.first && p.second == q.second;
}
void operator >> (const YAML::Node& node, Pair& p) {
node[0] >> p.first;
node[1] >> p.second;
}
TEST MappingBetweenSequences()
{
std::string input =
"? - Detroit Tigers\n"
" - Chicago cubs\n"
":\n"
" - 2001-07-23\n"
"\n"
"? [ New York Yankees,\n"
" Atlanta Braves ]\n"
": [ 2001-07-02, 2001-08-12,\n"
" 2001-08-14 ]";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
YAML_ASSERT(doc.size() == 2);
YAML_ASSERT(doc[Pair("Detroit Tigers", "Chicago cubs")].size() == 1);
YAML_ASSERT(doc[Pair("Detroit Tigers", "Chicago cubs")][0] == "2001-07-23");
YAML_ASSERT(doc[Pair("New York Yankees", "Atlanta Braves")].size() == 3);
YAML_ASSERT(doc[Pair("New York Yankees", "Atlanta Braves")][0] == "2001-07-02");
YAML_ASSERT(doc[Pair("New York Yankees", "Atlanta Braves")][1] == "2001-08-12");
YAML_ASSERT(doc[Pair("New York Yankees", "Atlanta Braves")][2] == "2001-08-14");
return true;
}
}
bool RunSpecTests()
{
bool passed = true;
RunSpecTest(&Spec::SeqScalars, "2.1", "Sequence of Scalars", passed);
RunSpecTest(&Spec::MappingScalarsToScalars, "2.2", "Mapping Scalars to Scalars", passed);
RunSpecTest(&Spec::MappingScalarsToSequences, "2.3", "Mapping Scalars to Sequences", passed);
RunSpecTest(&Spec::SequenceOfMappings, "2.4", "Sequence of Mappings", passed);
RunSpecTest(&Spec::SequenceOfSequences, "2.5", "Sequence of Sequences", passed);
RunSpecTest(&Spec::MappingOfMappings, "2.6", "Mapping of Mappings", passed);
RunSpecTest(&Spec::TwoDocumentsInAStream, "2.7", "Two Documents in a Stream", passed);
RunSpecTest(&Spec::PlayByPlayFeed, "2.8", "Play by Play Feed from a Game", passed);
RunSpecTest(&Spec::SingleDocumentWithTwoComments, "2.9", "Single Document with Two Comments", passed);
RunSpecTest(&Spec::SimpleAnchor, "2.10", "Node for \"Sammy Sosa\" appears twice in this document", passed);
RunSpecTest(&Spec::MappingBetweenSequences, "2.11", "Mapping between Sequences", passed);
return passed;
}
}

View File

@@ -1,389 +0,0 @@
#include "tests.h"
#include "spectests.h"
#include "yaml.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
namespace Test
{
void RunAll()
{
bool passed = true;
if(!RunParserTests())
passed = false;
if(!RunEmitterTests())
passed = false;
if(!RunSpecTests())
passed = false;
if(passed)
std::cout << "All tests passed!\n";
}
////////////////////////////////////////////////////////////////////////////////////////
// Parser tests
namespace {
void RunScalarParserTest(void (*test)(std::string&, std::string&), const std::string& name, bool& passed) {
std::string error;
std::string inputScalar, desiredOutput;
std::string output;
bool ok = true;
try {
test(inputScalar, desiredOutput);
std::stringstream stream(inputScalar);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
doc >> output;
} catch(const YAML::Exception& e) {
ok = false;
error = e.msg;
}
if(ok && output == desiredOutput) {
std::cout << "Parser test passed: " << name << "\n";
} else {
passed = false;
std::cout << "Parser test failed: " << name << "\n";
if(error != "")
std::cout << "Caught exception: " << error << "\n";
else {
std::cout << "Output:\n" << output << "<<<\n";
std::cout << "Desired output:\n" << desiredOutput << "<<<\n";
}
}
}
void RunParserTest(bool (*test)(), const std::string& name, bool& passed) {
std::string error;
bool ok = true;
try {
ok = test();
} catch(const YAML::Exception& e) {
ok = false;
error = e.msg;
}
if(ok) {
std::cout << "Parser test passed: " << name << "\n";
} else {
passed = false;
std::cout << "Parser test failed: " << name << "\n";
if(error != "")
std::cout << "Caught exception: " << error << "\n";
}
}
typedef void (*EncodingFn)(std::ostream&, int);
inline char Byte(int ch)
{
return static_cast<char>(static_cast<unsigned char>(static_cast<unsigned int>(ch)));
}
void EncodeToUtf8(std::ostream& stream, int ch)
{
if (ch <= 0x7F)
{
stream << Byte(ch);
}
else if (ch <= 0x7FF)
{
stream << Byte(0xC0 | (ch >> 6));
stream << Byte(0x80 | (ch & 0x3F));
}
else if (ch <= 0xFFFF)
{
stream << Byte(0xE0 | (ch >> 12));
stream << Byte(0x80 | ((ch >> 6) & 0x3F));
stream << Byte(0x80 | (ch & 0x3F));
}
else if (ch <= 0x1FFFFF)
{
stream << Byte(0xF0 | (ch >> 18));
stream << Byte(0x80 | ((ch >> 12) & 0x3F));
stream << Byte(0x80 | ((ch >> 6) & 0x3F));
stream << Byte(0x80 | (ch & 0x3F));
}
}
bool SplitUtf16HighChar(std::ostream& stream, EncodingFn encoding, int ch)
{
int biasedValue = ch - 0x10000;
if (biasedValue < 0)
{
return false;
}
int high = 0xD800 | (biasedValue >> 10);
int low = 0xDC00 | (biasedValue & 0x3FF);
encoding(stream, high);
encoding(stream, low);
return true;
}
void EncodeToUtf16LE(std::ostream& stream, int ch)
{
if (!SplitUtf16HighChar(stream, &EncodeToUtf16LE, ch))
{
stream << Byte(ch & 0xFF) << Byte(ch >> 8);
}
}
void EncodeToUtf16BE(std::ostream& stream, int ch)
{
if (!SplitUtf16HighChar(stream, &EncodeToUtf16BE, ch))
{
stream << Byte(ch >> 8) << Byte(ch & 0xFF);
}
}
void EncodeToUtf32LE(std::ostream& stream, int ch)
{
stream << Byte(ch & 0xFF) << Byte((ch >> 8) & 0xFF)
<< Byte((ch >> 16) & 0xFF) << Byte((ch >> 24) & 0xFF);
}
void EncodeToUtf32BE(std::ostream& stream, int ch)
{
stream << Byte((ch >> 24) & 0xFF) << Byte((ch >> 16) & 0xFF)
<< Byte((ch >> 8) & 0xFF) << Byte(ch & 0xFF);
}
class EncodingTester
{
public:
EncodingTester(EncodingFn encoding, bool declareEncoding)
{
if (declareEncoding)
{
encoding(m_yaml, 0xFEFF);
}
AddEntry(encoding, 0x0021, 0x007E); // Basic Latin
AddEntry(encoding, 0x00A1, 0x00FF); // Latin-1 Supplement
AddEntry(encoding, 0x0660, 0x06FF); // Arabic (largest contiguous block)
// CJK unified ideographs (multiple lines)
AddEntry(encoding, 0x4E00, 0x4EFF);
AddEntry(encoding, 0x4F00, 0x4FFF);
AddEntry(encoding, 0x5000, 0x51FF); // 512 character line
AddEntry(encoding, 0x5200, 0x54FF); // 768 character line
AddEntry(encoding, 0x5500, 0x58FF); // 1024 character line
AddEntry(encoding, 0x103A0, 0x103C3); // Old Persian
m_yaml.seekg(0, std::ios::beg);
}
std::istream& stream() {return m_yaml;}
const std::vector<std::string>& entries() {return m_entries;}
private:
std::stringstream m_yaml;
std::vector<std::string> m_entries;
void AddEntry(EncodingFn encoding, int startCh, int endCh)
{
encoding(m_yaml, '-');
encoding(m_yaml, ' ');
encoding(m_yaml, '|');
encoding(m_yaml, '\n');
encoding(m_yaml, ' ');
encoding(m_yaml, ' ');
std::stringstream entry;
for (int ch = startCh; ch <= endCh; ++ch)
{
encoding(m_yaml, ch);
EncodeToUtf8(entry, ch);
}
encoding(m_yaml, '\n');
m_entries.push_back(entry.str());
}
};
void RunEncodingTest(EncodingFn encoding, bool declareEncoding, const std::string& name, bool& passed)
{
EncodingTester tester(encoding, declareEncoding);
std::string error;
bool ok = true;
try {
YAML::Parser parser(tester.stream());
YAML::Node doc;
parser.GetNextDocument(doc);
YAML::Iterator itNode = doc.begin();
std::vector<std::string>::const_iterator itEntry = tester.entries().begin();
for (; (itNode != doc.end()) && (itEntry != tester.entries().end()); ++itNode, ++itEntry)
{
std::string stScalarValue;
if (!itNode->GetScalar(stScalarValue) && (stScalarValue == *itEntry))
{
break;
}
}
if ((itNode != doc.end()) || (itEntry != tester.entries().end()))
{
ok = false;
}
} catch(const YAML::Exception& e) {
ok = false;
error = e.msg;
}
if(ok) {
std::cout << "Parser test passed: " << name << "\n";
} else {
passed = false;
std::cout << "Parser test failed: " << name << "\n";
if(error != "")
std::cout << "Caught exception: " << error << "\n";
}
}
}
bool RunParserTests()
{
bool passed = true;
RunScalarParserTest(&Parser::SimpleScalar, "simple scalar", passed);
RunScalarParserTest(&Parser::MultiLineScalar, "multi-line scalar", passed);
RunScalarParserTest(&Parser::LiteralScalar, "literal scalar", passed);
RunScalarParserTest(&Parser::FoldedScalar, "folded scalar", passed);
RunScalarParserTest(&Parser::ChompedFoldedScalar, "chomped folded scalar", passed);
RunScalarParserTest(&Parser::ChompedLiteralScalar, "chomped literal scalar", passed);
RunScalarParserTest(&Parser::FoldedScalarWithIndent, "folded scalar with indent", passed);
RunScalarParserTest(&Parser::ColonScalar, "colon scalar", passed);
RunScalarParserTest(&Parser::QuotedScalar, "quoted scalar", passed);
RunScalarParserTest(&Parser::CommaScalar, "comma scalar", passed);
RunScalarParserTest(&Parser::DashScalar, "dash scalar", passed);
RunScalarParserTest(&Parser::URLScalar, "url scalar", passed);
RunParserTest(&Parser::SimpleSeq, "simple seq", passed);
RunParserTest(&Parser::SimpleMap, "simple map", passed);
RunParserTest(&Parser::FlowSeq, "flow seq", passed);
RunParserTest(&Parser::FlowMap, "flow map", passed);
RunParserTest(&Parser::FlowMapWithOmittedKey, "flow map with omitted key", passed);
RunParserTest(&Parser::FlowMapWithOmittedValue, "flow map with omitted value", passed);
RunParserTest(&Parser::FlowMapWithSoloEntry, "flow map with solo entry", passed);
RunParserTest(&Parser::FlowMapEndingWithSoloEntry, "flow map ending with solo entry", passed);
RunParserTest(&Parser::QuotedSimpleKeys, "quoted simple keys", passed);
RunParserTest(&Parser::CompressedMapAndSeq, "compressed map and seq", passed);
RunParserTest(&Parser::NullBlockSeqEntry, "null block seq entry", passed);
RunParserTest(&Parser::NullBlockMapKey, "null block map key", passed);
RunParserTest(&Parser::NullBlockMapValue, "null block map value", passed);
RunParserTest(&Parser::SimpleAlias, "simple alias", passed);
RunParserTest(&Parser::AliasWithNull, "alias with null", passed);
RunParserTest(&Parser::AnchorInSimpleKey, "anchor in simple key", passed);
RunParserTest(&Parser::AliasAsSimpleKey, "alias as simple key", passed);
RunParserTest(&Parser::ExplicitDoc, "explicit doc", passed);
RunParserTest(&Parser::MultipleDocs, "multiple docs", passed);
RunParserTest(&Parser::ExplicitEndDoc, "explicit end doc", passed);
RunParserTest(&Parser::MultipleDocsWithSomeExplicitIndicators, "multiple docs with some explicit indicators", passed);
RunEncodingTest(&EncodeToUtf8, false, "UTF-8, no BOM", passed);
RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with BOM", passed);
RunEncodingTest(&EncodeToUtf16LE, false, "UTF-16LE, no BOM", passed);
RunEncodingTest(&EncodeToUtf16LE, true, "UTF-16LE with BOM", passed);
RunEncodingTest(&EncodeToUtf16BE, false, "UTF-16BE, no BOM", passed);
RunEncodingTest(&EncodeToUtf16BE, true, "UTF-16BE with BOM", passed);
RunEncodingTest(&EncodeToUtf32LE, false, "UTF-32LE, no BOM", passed);
RunEncodingTest(&EncodeToUtf32LE, true, "UTF-32LE with BOM", passed);
RunEncodingTest(&EncodeToUtf32BE, false, "UTF-32BE, no BOM", passed);
RunEncodingTest(&EncodeToUtf32BE, true, "UTF-32BE with BOM", passed);
return passed;
}
////////////////////////////////////////////////////////////////////////////////////////
// Emitter tests
namespace {
void RunEmitterTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, bool& passed) {
YAML::Emitter out;
std::string desiredOutput;
test(out, desiredOutput);
std::string output = out.c_str();
if(output == desiredOutput) {
std::cout << "Emitter test passed: " << name << "\n";
} else {
passed = false;
std::cout << "Emitter test failed: " << name << "\n";
std::cout << "Output:\n";
std::cout << output << "<<<\n";
std::cout << "Desired output:\n";
std::cout << desiredOutput << "<<<\n";
}
}
void RunEmitterErrorTest(void (*test)(YAML::Emitter&, std::string&), const std::string& name, bool& passed) {
YAML::Emitter out;
std::string desiredError;
test(out, desiredError);
std::string lastError = out.GetLastError();
if(!out.good() && lastError == desiredError) {
std::cout << "Emitter test passed: " << name << "\n";
} else {
passed = false;
std::cout << "Emitter test failed: " << name << "\n";
if(out.good())
std::cout << "No error detected\n";
else
std::cout << "Detected error: " << lastError << "\n";
std::cout << "Expected error: " << desiredError << "\n";
}
}
}
bool RunEmitterTests()
{
bool passed = true;
RunEmitterTest(&Emitter::SimpleScalar, "simple scalar", passed);
RunEmitterTest(&Emitter::SimpleSeq, "simple seq", passed);
RunEmitterTest(&Emitter::SimpleFlowSeq, "simple flow seq", passed);
RunEmitterTest(&Emitter::EmptyFlowSeq, "empty flow seq", passed);
RunEmitterTest(&Emitter::NestedBlockSeq, "nested block seq", passed);
RunEmitterTest(&Emitter::NestedFlowSeq, "nested flow seq", passed);
RunEmitterTest(&Emitter::SimpleMap, "simple map", passed);
RunEmitterTest(&Emitter::SimpleFlowMap, "simple flow map", passed);
RunEmitterTest(&Emitter::MapAndList, "map and list", passed);
RunEmitterTest(&Emitter::ListAndMap, "list and map", passed);
RunEmitterTest(&Emitter::NestedBlockMap, "nested block map", passed);
RunEmitterTest(&Emitter::NestedFlowMap, "nested flow map", passed);
RunEmitterTest(&Emitter::MapListMix, "map list mix", passed);
RunEmitterTest(&Emitter::SimpleLongKey, "simple long key", passed);
RunEmitterTest(&Emitter::SingleLongKey, "single long key", passed);
RunEmitterTest(&Emitter::ComplexLongKey, "complex long key", passed);
RunEmitterTest(&Emitter::AutoLongKey, "auto long key", passed);
RunEmitterTest(&Emitter::ScalarFormat, "scalar format", passed);
RunEmitterTest(&Emitter::AutoLongKeyScalar, "auto long key scalar", passed);
RunEmitterTest(&Emitter::LongKeyFlowMap, "long key flow map", passed);
RunEmitterTest(&Emitter::BlockMapAsKey, "block map as key", passed);
RunEmitterTest(&Emitter::AliasAndAnchor, "alias and anchor", passed);
RunEmitterTest(&Emitter::AliasAndAnchorWithNull, "alias and anchor with null", passed);
RunEmitterTest(&Emitter::ComplexDoc, "complex doc", passed);
RunEmitterTest(&Emitter::STLContainers, "STL containers", passed);
RunEmitterTest(&Emitter::SimpleComment, "simple comment", passed);
RunEmitterTest(&Emitter::MultiLineComment, "multi-line comment", passed);
RunEmitterTest(&Emitter::ComplexComments, "complex comments", passed);
RunEmitterTest(&Emitter::Indentation, "indentation", passed);
RunEmitterTest(&Emitter::SimpleGlobalSettings, "simple global settings", passed);
RunEmitterTest(&Emitter::ComplexGlobalSettings, "complex global settings", passed);
RunEmitterTest(&Emitter::Null, "null", passed);
RunEmitterErrorTest(&Emitter::ExtraEndSeq, "extra EndSeq", passed);
RunEmitterErrorTest(&Emitter::ExtraEndMap, "extra EndMap", passed);
RunEmitterErrorTest(&Emitter::BadSingleQuoted, "bad single quoted string", passed);
RunEmitterErrorTest(&Emitter::InvalidAnchor, "invalid anchor", passed);
RunEmitterErrorTest(&Emitter::InvalidAlias, "invalid alias", passed);
RunEmitterErrorTest(&Emitter::MissingKey, "missing key", passed);
RunEmitterErrorTest(&Emitter::MissingValue, "missing value", passed);
RunEmitterErrorTest(&Emitter::UnexpectedKey, "unexpected key", passed);
RunEmitterErrorTest(&Emitter::UnexpectedValue, "unexpected value", passed);
return passed;
}
}

View File

@@ -1,103 +0,0 @@
#pragma once
#ifndef TESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#define TESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
#include <string>
namespace YAML { class Emitter; }
namespace Test {
void RunAll();
bool RunParserTests();
bool RunEmitterTests();
namespace Parser {
// scalar tests
void SimpleScalar(std::string& inputScalar, std::string& desiredOutput);
void MultiLineScalar(std::string& inputScalar, std::string& desiredOutput);
void LiteralScalar(std::string& inputScalar, std::string& desiredOutput);
void FoldedScalar(std::string& inputScalar, std::string& desiredOutput);
void ChompedFoldedScalar(std::string& inputScalar, std::string& desiredOutput);
void ChompedLiteralScalar(std::string& inputScalar, std::string& desiredOutput);
void FoldedScalarWithIndent(std::string& inputScalar, std::string& desiredOutput);
void ColonScalar(std::string& inputScalar, std::string& desiredOutput);
void QuotedScalar(std::string& inputScalar, std::string& desiredOutput);
void CommaScalar(std::string& inputScalar, std::string& desiredOutput);
void DashScalar(std::string& inputScalar, std::string& desiredOutput);
void URLScalar(std::string& inputScalar, std::string& desiredOutput);
// misc tests
bool SimpleSeq();
bool SimpleMap();
bool FlowSeq();
bool FlowMap();
bool FlowMapWithOmittedKey();
bool FlowMapWithOmittedValue();
bool FlowMapWithSoloEntry();
bool FlowMapEndingWithSoloEntry();
bool QuotedSimpleKeys();
bool CompressedMapAndSeq();
bool NullBlockSeqEntry();
bool NullBlockMapKey();
bool NullBlockMapValue();
bool SimpleAlias();
bool AliasWithNull();
bool AnchorInSimpleKey();
bool AliasAsSimpleKey();
bool ExplicitDoc();
bool MultipleDocs();
bool ExplicitEndDoc();
bool MultipleDocsWithSomeExplicitIndicators();
}
namespace Emitter {
// correct emitting
void SimpleScalar(YAML::Emitter& out, std::string& desiredOutput);
void SimpleSeq(YAML::Emitter& out, std::string& desiredOutput);
void SimpleFlowSeq(YAML::Emitter& ouptut, std::string& desiredOutput);
void EmptyFlowSeq(YAML::Emitter& out, std::string& desiredOutput);
void NestedBlockSeq(YAML::Emitter& out, std::string& desiredOutput);
void NestedFlowSeq(YAML::Emitter& out, std::string& desiredOutput);
void SimpleMap(YAML::Emitter& out, std::string& desiredOutput);
void SimpleFlowMap(YAML::Emitter& out, std::string& desiredOutput);
void MapAndList(YAML::Emitter& out, std::string& desiredOutput);
void ListAndMap(YAML::Emitter& out, std::string& desiredOutput);
void NestedBlockMap(YAML::Emitter& out, std::string& desiredOutput);
void NestedFlowMap(YAML::Emitter& out, std::string& desiredOutput);
void MapListMix(YAML::Emitter& out, std::string& desiredOutput);
void SimpleLongKey(YAML::Emitter& out, std::string& desiredOutput);
void SingleLongKey(YAML::Emitter& out, std::string& desiredOutput);
void ComplexLongKey(YAML::Emitter& out, std::string& desiredOutput);
void AutoLongKey(YAML::Emitter& out, std::string& desiredOutput);
void ScalarFormat(YAML::Emitter& out, std::string& desiredOutput);
void AutoLongKeyScalar(YAML::Emitter& out, std::string& desiredOutput);
void LongKeyFlowMap(YAML::Emitter& out, std::string& desiredOutput);
void BlockMapAsKey(YAML::Emitter& out, std::string& desiredOutput);
void AliasAndAnchor(YAML::Emitter& out, std::string& desiredOutput);
void AliasAndAnchorWithNull(YAML::Emitter& out, std::string& desiredOutput);
void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput);
void STLContainers(YAML::Emitter& out, std::string& desiredOutput);
void SimpleComment(YAML::Emitter& out, std::string& desiredOutput);
void MultiLineComment(YAML::Emitter& out, std::string& desiredOutput);
void ComplexComments(YAML::Emitter& out, std::string& desiredOutput);
void Indentation(YAML::Emitter& out, std::string& desiredOutput);
void SimpleGlobalSettings(YAML::Emitter& out, std::string& desiredOutput);
void ComplexGlobalSettings(YAML::Emitter& out, std::string& desiredOutput);
void Null(YAML::Emitter& out, std::string& desiredOutput);
// incorrect emitting
void ExtraEndSeq(YAML::Emitter& out, std::string& desiredError);
void ExtraEndMap(YAML::Emitter& out, std::string& desiredError);
void BadSingleQuoted(YAML::Emitter& out, std::string& desiredError);
void InvalidAnchor(YAML::Emitter& out, std::string& desiredError);
void InvalidAlias(YAML::Emitter& out, std::string& desiredError);
void MissingKey(YAML::Emitter& out, std::string& desiredError);
void MissingValue(YAML::Emitter& out, std::string& desiredError);
void UnexpectedKey(YAML::Emitter& out, std::string& desiredError);
void UnexpectedValue(YAML::Emitter& out, std::string& desiredError);
}
}
#endif // TESTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@@ -1,4 +0,0 @@
--- &list
- This document contains a recursive list.
- *list
...

View File

@@ -1,12 +1,17 @@
 
Microsoft Visual Studio Solution File, Format Version 10.00 Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008 # Visual Studio 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yaml-reader", "yaml-reader.vcproj", "{E8CC0D8A-D784-4A6B-B78B-ACEA13F9FB0B}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yamlcpp", "yamlcpp.vcproj", "{3104AB4E-CD31-4F47-95E9-0E8D9374E15D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcproj", "{D1108F40-6ADF-467E-A95A-236C39A515C5}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
{3104AB4E-CD31-4F47-95E9-0E8D9374E15D} = {3104AB4E-CD31-4F47-95E9-0E8D9374E15D} {3104AB4E-CD31-4F47-95E9-0E8D9374E15D} = {3104AB4E-CD31-4F47-95E9-0E8D9374E15D}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yamlcpp", "yamlcpp.vcproj", "{3104AB4E-CD31-4F47-95E9-0E8D9374E15D}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "parse", "parse.vcproj", "{CD007B57-7812-4930-A5E2-6E5E56338814}"
ProjectSection(ProjectDependencies) = postProject
{3104AB4E-CD31-4F47-95E9-0E8D9374E15D} = {3104AB4E-CD31-4F47-95E9-0E8D9374E15D}
EndProjectSection
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -14,14 +19,18 @@ Global
Release|Win32 = Release|Win32 Release|Win32 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E8CC0D8A-D784-4A6B-B78B-ACEA13F9FB0B}.Debug|Win32.ActiveCfg = Debug|Win32
{E8CC0D8A-D784-4A6B-B78B-ACEA13F9FB0B}.Debug|Win32.Build.0 = Debug|Win32
{E8CC0D8A-D784-4A6B-B78B-ACEA13F9FB0B}.Release|Win32.ActiveCfg = Release|Win32
{E8CC0D8A-D784-4A6B-B78B-ACEA13F9FB0B}.Release|Win32.Build.0 = Release|Win32
{3104AB4E-CD31-4F47-95E9-0E8D9374E15D}.Debug|Win32.ActiveCfg = Debug|Win32 {3104AB4E-CD31-4F47-95E9-0E8D9374E15D}.Debug|Win32.ActiveCfg = Debug|Win32
{3104AB4E-CD31-4F47-95E9-0E8D9374E15D}.Debug|Win32.Build.0 = Debug|Win32 {3104AB4E-CD31-4F47-95E9-0E8D9374E15D}.Debug|Win32.Build.0 = Debug|Win32
{3104AB4E-CD31-4F47-95E9-0E8D9374E15D}.Release|Win32.ActiveCfg = Release|Win32 {3104AB4E-CD31-4F47-95E9-0E8D9374E15D}.Release|Win32.ActiveCfg = Release|Win32
{3104AB4E-CD31-4F47-95E9-0E8D9374E15D}.Release|Win32.Build.0 = Release|Win32 {3104AB4E-CD31-4F47-95E9-0E8D9374E15D}.Release|Win32.Build.0 = Release|Win32
{D1108F40-6ADF-467E-A95A-236C39A515C5}.Debug|Win32.ActiveCfg = Debug|Win32
{D1108F40-6ADF-467E-A95A-236C39A515C5}.Debug|Win32.Build.0 = Debug|Win32
{D1108F40-6ADF-467E-A95A-236C39A515C5}.Release|Win32.ActiveCfg = Release|Win32
{D1108F40-6ADF-467E-A95A-236C39A515C5}.Release|Win32.Build.0 = Release|Win32
{CD007B57-7812-4930-A5E2-6E5E56338814}.Debug|Win32.ActiveCfg = Debug|Win32
{CD007B57-7812-4930-A5E2-6E5E56338814}.Debug|Win32.Build.0 = Debug|Win32
{CD007B57-7812-4930-A5E2-6E5E56338814}.Release|Win32.ActiveCfg = Release|Win32
{CD007B57-7812-4930-A5E2-6E5E56338814}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -40,6 +40,7 @@
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalOptions="/D_SCL_SECURE_NO_WARNINGS"
Optimization="0" Optimization="0"
AdditionalIncludeDirectories="include" AdditionalIncludeDirectories="include"
PreprocessorDefinitions="WIN32;_DEBUG;_LIB" PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
@@ -49,6 +50,7 @@
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="4" WarningLevel="4"
DebugInformationFormat="3" DebugInformationFormat="3"
DisableSpecificWarnings="4127;4355"
/> />
<Tool <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@@ -104,6 +106,7 @@
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalOptions="/D_SCL_SECURE_NO_WARNINGS"
Optimization="2" Optimization="2"
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="include" AdditionalIncludeDirectories="include"
@@ -113,6 +116,7 @@
UsePrecompiledHeader="0" UsePrecompiledHeader="0"
WarningLevel="4" WarningLevel="4"
DebugInformationFormat="3" DebugInformationFormat="3"
DisableSpecificWarnings="4127;4355"
/> />
<Tool <Tool
Name="VCManagedResourceCompilerTool" Name="VCManagedResourceCompilerTool"
@@ -171,10 +175,6 @@
RelativePath=".\src\aliascontent.cpp" RelativePath=".\src\aliascontent.cpp"
> >
</File> </File>
<File
RelativePath=".\src\content.cpp"
>
</File>
<File <File
RelativePath=".\src\conversion.cpp" RelativePath=".\src\conversion.cpp"
> >
@@ -191,6 +191,10 @@
RelativePath=".\src\node.cpp" RelativePath=".\src\node.cpp"
> >
</File> </File>
<File
RelativePath=".\src\null.cpp"
>
</File>
<File <File
RelativePath=".\src\scalar.cpp" RelativePath=".\src\scalar.cpp"
> >
@@ -199,6 +203,10 @@
RelativePath=".\src\sequence.cpp" RelativePath=".\src\sequence.cpp"
> >
</File> </File>
<File
RelativePath=".\src\tag.cpp"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="Scanner" Name="Scanner"
@@ -219,6 +227,10 @@
RelativePath=".\src\scanscalar.cpp" RelativePath=".\src\scanscalar.cpp"
> >
</File> </File>
<File
RelativePath=".\src\scantag.cpp"
>
</File>
<File <File
RelativePath=".\src\scantoken.cpp" RelativePath=".\src\scantoken.cpp"
> >
@@ -333,6 +345,10 @@
RelativePath=".\src\sequence.h" RelativePath=".\src\sequence.h"
> >
</File> </File>
<File
RelativePath=".\src\tag.h"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="Scanner" Name="Scanner"
@@ -357,6 +373,10 @@
RelativePath=".\src\scanscalar.h" RelativePath=".\src\scanscalar.h"
> >
</File> </File>
<File
RelativePath=".\src\scantag.h"
>
</File>
<File <File
RelativePath=".\src\stream.h" RelativePath=".\src\stream.h"
> >