Set the eol style to native for all files.

This commit is contained in:
Jesse Beder 2008-09-03 22:20:39 +00:00
parent a45f083e00
commit 45322566c7
42 changed files with 3546 additions and 3546 deletions

View file

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

View file

@ -1,59 +1,59 @@
#pragma once
#include <exception>
#include <string>
namespace YAML
{
class Exception: public std::exception {};
class ParserException: public Exception {
public:
ParserException(int line_, int column_, const std::string& msg_)
: line(line_), column(column_), msg(msg_) {}
virtual ~ParserException() throw () {}
int line, column;
std::string msg;
};
class RepresentationException: public Exception {};
// representation exceptions
class InvalidScalar: public RepresentationException {};
class BadDereference: public RepresentationException {};
// error messages
namespace ErrorMsg
{
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_MAJOR_VERSION = "YAML major version too large";
const std::string TAG_DIRECTIVE_ARGS = "TAG directives must have exactly two arguments";
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_SEQ = "end of sequence not found";
const std::string END_OF_SEQ_FLOW = "end of sequence flow not found";
const std::string MULTIPLE_TAGS = "cannot assign multiple tags to the same node";
const std::string MULTIPLE_ANCHORS = "cannot assign multiple anchors to the same node";
const std::string MULTIPLE_ALIASES = "cannot assign multiple aliases to the same node";
const std::string ALIAS_CONTENT = "aliases can't have any content, *including* tags";
const std::string INVALID_HEX = "bad character found while scanning hex number";
const std::string INVALID_UNICODE = "invalid unicode: ";
const std::string INVALID_ESCAPE = "unknown escape character: ";
const std::string UNKNOWN_TOKEN = "unknown token";
const std::string DOC_IN_SCALAR = "illegal document indicator in scalar";
const std::string EOF_IN_SCALAR = "illegal EOF in scalar";
const std::string CHAR_IN_SCALAR = "illegal character in scalar";
const std::string TAB_IN_INDENTATION = "illegal tab when looking for indentation";
const std::string FLOW_END = "illegal flow end";
const std::string BLOCK_ENTRY = "illegal block entry";
const std::string MAP_KEY = "illegal map key";
const std::string MAP_VALUE = "illegal map value";
const std::string ALIAS_NOT_FOUND = "alias not found after *";
const std::string ANCHOR_NOT_FOUND = "anchor not found after &";
const std::string CHAR_IN_ALIAS = "illegal character found while scanning alias";
const std::string CHAR_IN_ANCHOR = "illegal character found while scanning anchor";
const std::string ZERO_INDENT_IN_BLOCK = "cannot set zero indentation for a block scalar";
const std::string CHAR_IN_BLOCK = "unexpected character in block scalar";
}
}
#pragma once
#include <exception>
#include <string>
namespace YAML
{
class Exception: public std::exception {};
class ParserException: public Exception {
public:
ParserException(int line_, int column_, const std::string& msg_)
: line(line_), column(column_), msg(msg_) {}
virtual ~ParserException() throw () {}
int line, column;
std::string msg;
};
class RepresentationException: public Exception {};
// representation exceptions
class InvalidScalar: public RepresentationException {};
class BadDereference: public RepresentationException {};
// error messages
namespace ErrorMsg
{
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_MAJOR_VERSION = "YAML major version too large";
const std::string TAG_DIRECTIVE_ARGS = "TAG directives must have exactly two arguments";
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_SEQ = "end of sequence not found";
const std::string END_OF_SEQ_FLOW = "end of sequence flow not found";
const std::string MULTIPLE_TAGS = "cannot assign multiple tags to the same node";
const std::string MULTIPLE_ANCHORS = "cannot assign multiple anchors to the same node";
const std::string MULTIPLE_ALIASES = "cannot assign multiple aliases to the same node";
const std::string ALIAS_CONTENT = "aliases can't have any content, *including* tags";
const std::string INVALID_HEX = "bad character found while scanning hex number";
const std::string INVALID_UNICODE = "invalid unicode: ";
const std::string INVALID_ESCAPE = "unknown escape character: ";
const std::string UNKNOWN_TOKEN = "unknown token";
const std::string DOC_IN_SCALAR = "illegal document indicator in scalar";
const std::string EOF_IN_SCALAR = "illegal EOF in scalar";
const std::string CHAR_IN_SCALAR = "illegal character in scalar";
const std::string TAB_IN_INDENTATION = "illegal tab when looking for indentation";
const std::string FLOW_END = "illegal flow end";
const std::string BLOCK_ENTRY = "illegal block entry";
const std::string MAP_KEY = "illegal map key";
const std::string MAP_VALUE = "illegal map value";
const std::string ALIAS_NOT_FOUND = "alias not found after *";
const std::string ANCHOR_NOT_FOUND = "anchor not found after &";
const std::string CHAR_IN_ALIAS = "illegal character found while scanning alias";
const std::string CHAR_IN_ANCHOR = "illegal character found while scanning anchor";
const std::string ZERO_INDENT_IN_BLOCK = "cannot set zero indentation for a block scalar";
const std::string CHAR_IN_BLOCK = "unexpected character in block scalar";
}
}

View file

@ -1,30 +1,30 @@
#pragma once
namespace YAML
{
class Node;
struct IterPriv;
class Iterator
{
public:
Iterator();
Iterator(IterPriv *pData);
Iterator(const Iterator& rhs);
~Iterator();
Iterator& operator = (const Iterator& rhs);
Iterator& operator ++ ();
Iterator operator ++ (int);
const Node& operator * () const;
const Node *operator -> () const;
const Node& first() const;
const Node& second() const;
friend bool operator == (const Iterator& it, const Iterator& jt);
friend bool operator != (const Iterator& it, const Iterator& jt);
private:
IterPriv *m_pData;
};
}
#pragma once
namespace YAML
{
class Node;
struct IterPriv;
class Iterator
{
public:
Iterator();
Iterator(IterPriv *pData);
Iterator(const Iterator& rhs);
~Iterator();
Iterator& operator = (const Iterator& rhs);
Iterator& operator ++ ();
Iterator operator ++ (int);
const Node& operator * () const;
const Node *operator -> () const;
const Node& first() const;
const Node& second() const;
friend bool operator == (const Iterator& it, const Iterator& jt);
friend bool operator != (const Iterator& it, const Iterator& jt);
private:
IterPriv *m_pData;
};
}

View file

@ -1,92 +1,92 @@
#pragma once
#include <string>
#include <ios>
#include <vector>
#include <map>
#include "parserstate.h"
#include "exceptions.h"
#include "iterator.h"
namespace YAML
{
class Content;
class Scanner;
enum CONTENT_TYPE { CT_NONE, CT_SCALAR, CT_SEQUENCE, CT_MAP };
class Node
{
public:
Node();
~Node();
void Clear();
void Parse(Scanner *pScanner, const ParserState& state);
void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) const;
CONTENT_TYPE GetType() const;
// accessors
Iterator begin() const;
Iterator end() const;
unsigned size() const;
template <typename T>
const Node& GetValue(const T& key) const {
if(!m_pContent)
throw BadDereference();
for(Iterator it=begin();it!=end();++it) {
T t;
try {
it.first() >> t;
if(key == t)
return it.second();
} catch(RepresentationException&) {
}
}
throw BadDereference();
}
template <typename T>
const Node& operator [] (const T& key) const {
return GetValue(key);
}
const Node& operator [] (const char *key) const {
return GetValue(std::string(key));
}
const Node& operator [] (unsigned u) const;
const Node& operator [] (int i) const;
// extraction
friend void operator >> (const Node& node, std::string& s);
friend void operator >> (const Node& node, int& i);
friend void operator >> (const Node& node, unsigned& u);
friend void operator >> (const Node& node, long& l);
friend void operator >> (const Node& node, float& f);
friend void operator >> (const Node& node, double& d);
friend void operator >> (const Node& node, char& c);
// insertion
friend std::ostream& operator << (std::ostream& out, const Node& node);
// ordering
int Compare(const Node& rhs) const;
friend bool operator < (const Node& n1, const Node& n2);
private:
void ParseHeader(Scanner *pScanner, const ParserState& state);
void ParseTag(Scanner *pScanner, const ParserState& state);
void ParseAnchor(Scanner *pScanner, const ParserState& state);
void ParseAlias(Scanner *pScanner, const ParserState& state);
private:
std::string m_anchor, m_tag;
Content *m_pContent;
bool m_alias;
};
}
#pragma once
#include <string>
#include <ios>
#include <vector>
#include <map>
#include "parserstate.h"
#include "exceptions.h"
#include "iterator.h"
namespace YAML
{
class Content;
class Scanner;
enum CONTENT_TYPE { CT_NONE, CT_SCALAR, CT_SEQUENCE, CT_MAP };
class Node
{
public:
Node();
~Node();
void Clear();
void Parse(Scanner *pScanner, const ParserState& state);
void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) const;
CONTENT_TYPE GetType() const;
// accessors
Iterator begin() const;
Iterator end() const;
unsigned size() const;
template <typename T>
const Node& GetValue(const T& key) const {
if(!m_pContent)
throw BadDereference();
for(Iterator it=begin();it!=end();++it) {
T t;
try {
it.first() >> t;
if(key == t)
return it.second();
} catch(RepresentationException&) {
}
}
throw BadDereference();
}
template <typename T>
const Node& operator [] (const T& key) const {
return GetValue(key);
}
const Node& operator [] (const char *key) const {
return GetValue(std::string(key));
}
const Node& operator [] (unsigned u) const;
const Node& operator [] (int i) const;
// extraction
friend void operator >> (const Node& node, std::string& s);
friend void operator >> (const Node& node, int& i);
friend void operator >> (const Node& node, unsigned& u);
friend void operator >> (const Node& node, long& l);
friend void operator >> (const Node& node, float& f);
friend void operator >> (const Node& node, double& d);
friend void operator >> (const Node& node, char& c);
// insertion
friend std::ostream& operator << (std::ostream& out, const Node& node);
// ordering
int Compare(const Node& rhs) const;
friend bool operator < (const Node& n1, const Node& n2);
private:
void ParseHeader(Scanner *pScanner, const ParserState& state);
void ParseTag(Scanner *pScanner, const ParserState& state);
void ParseAnchor(Scanner *pScanner, const ParserState& state);
void ParseAlias(Scanner *pScanner, const ParserState& state);
private:
std::string m_anchor, m_tag;
Content *m_pContent;
bool m_alias;
};
}

View file

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

View file

@ -1,20 +1,20 @@
#pragma once
#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;
};
}
#pragma once
#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;
};
}

View file

@ -1,8 +1,8 @@
#pragma once
#include "crt.h"
#include "parser.h"
#include "node.h"
#include "iterator.h"
#include "exceptions.h"
#pragma once
#include "crt.h"
#include "parser.h"
#include "node.h"
#include "iterator.h"
#include "exceptions.h"

View file

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

View file

@ -1,55 +1,55 @@
#pragma once
#include <ios>
#include <vector>
#include <map>
#include "parserstate.h"
#include "exceptions.h"
#include "ltnode.h"
namespace YAML
{
class Scanner;
class Parser;
class Node;
class Scalar;
class Sequence;
class Map;
class Content
{
public:
Content();
virtual ~Content();
virtual void Parse(Scanner *pScanner, const ParserState& state) = 0;
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) = 0;
virtual bool GetBegin(std::vector <Node *>::const_iterator& it) const { return false; }
virtual bool GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& it) const { return false; }
virtual bool GetEnd(std::vector <Node *>::const_iterator& it) const { return false; }
virtual bool GetEnd(std::map <Node *, Node *, ltnode>::const_iterator& it) const { return false; }
virtual Node *GetNode(unsigned i) const { return 0; }
virtual unsigned GetSize() const { return 0; }
virtual bool IsScalar() const { return false; }
virtual bool IsMap() const { return false; }
virtual bool IsSequence() const { return false; }
// extraction
virtual void Read(std::string& s) { throw InvalidScalar(); }
virtual void Read(int& i) { throw InvalidScalar(); }
virtual void Read(unsigned& u) { throw InvalidScalar(); }
virtual void Read(long& l) { throw InvalidScalar(); }
virtual void Read(float& f) { throw InvalidScalar(); }
virtual void Read(double& d) { throw InvalidScalar(); }
virtual void Read(char& c) { throw InvalidScalar(); }
// ordering
virtual int Compare(Content *pContent) { return 0; }
virtual int Compare(Scalar *pScalar) { return 0; }
virtual int Compare(Sequence *pSeq) { return 0; }
virtual int Compare(Map *pMap) { return 0; }
protected:
};
}
#pragma once
#include <ios>
#include <vector>
#include <map>
#include "parserstate.h"
#include "exceptions.h"
#include "ltnode.h"
namespace YAML
{
class Scanner;
class Parser;
class Node;
class Scalar;
class Sequence;
class Map;
class Content
{
public:
Content();
virtual ~Content();
virtual void Parse(Scanner *pScanner, const ParserState& state) = 0;
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) = 0;
virtual bool GetBegin(std::vector <Node *>::const_iterator& it) const { return false; }
virtual bool GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& it) const { return false; }
virtual bool GetEnd(std::vector <Node *>::const_iterator& it) const { return false; }
virtual bool GetEnd(std::map <Node *, Node *, ltnode>::const_iterator& it) const { return false; }
virtual Node *GetNode(unsigned i) const { return 0; }
virtual unsigned GetSize() const { return 0; }
virtual bool IsScalar() const { return false; }
virtual bool IsMap() const { return false; }
virtual bool IsSequence() const { return false; }
// extraction
virtual void Read(std::string& s) { throw InvalidScalar(); }
virtual void Read(int& i) { throw InvalidScalar(); }
virtual void Read(unsigned& u) { throw InvalidScalar(); }
virtual void Read(long& l) { throw InvalidScalar(); }
virtual void Read(float& f) { throw InvalidScalar(); }
virtual void Read(double& d) { throw InvalidScalar(); }
virtual void Read(char& c) { throw InvalidScalar(); }
// ordering
virtual int Compare(Content *pContent) { return 0; }
virtual int Compare(Scalar *pScalar) { return 0; }
virtual int Compare(Sequence *pSeq) { return 0; }
virtual int Compare(Map *pMap) { return 0; }
protected:
};
}

View file

@ -1,113 +1,113 @@
#include "crt.h"
#include "exp.h"
#include "exceptions.h"
#include <sstream>
namespace YAML
{
namespace Exp
{
unsigned ParseHex(const std::string& str, int line, int column)
{
unsigned value = 0;
for(unsigned i=0;i<str.size();i++) {
char ch = str[i];
int digit = 0;
if('a' <= ch && ch <= 'f')
digit = ch - 'a' + 10;
else if('A' <= ch && ch <= 'F')
digit = ch - 'A' + 10;
else if('0' <= ch && ch <= '9')
digit = ch - '0';
else
throw ParserException(line, column, ErrorMsg::INVALID_HEX);
value = (value << 4) + digit;
}
return value;
}
std::string Str(char ch)
{
return std::string("") + ch;
}
// Escape
// . Translates the next 'codeLength' characters into a hex number and returns the result.
// . Throws if it's not actually hex.
std::string Escape(Stream& in, int codeLength)
{
// grab string
std::string str;
for(int i=0;i<codeLength;i++)
str += in.get();
// get the value
unsigned value = ParseHex(str, in.line, in.column);
// legal unicode?
if((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) {
std::stringstream msg;
msg << ErrorMsg::INVALID_UNICODE << value;
throw ParserException(in.line, in.column, msg.str());
}
// now break it up into chars
if(value <= 0x7F)
return Str(value);
else if(value <= 0x7FF)
return Str(0xC0 + (value >> 6)) + Str(0x80 + (value & 0x3F));
else if(value <= 0xFFFF)
return Str(0xE0 + (value >> 12)) + Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F));
else
return Str(0xF0 + (value >> 18)) + Str(0x80 + ((value >> 12) & 0x3F)) +
Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F));
}
// Escape
// . Escapes the sequence starting 'in' (it must begin with a '\' or single quote)
// and returns the result.
// . Throws if it's an unknown escape character.
std::string Escape(Stream& in)
{
// eat slash
char escape = in.get();
// switch on escape character
char ch = in.get();
// first do single quote, since it's easier
if(escape == '\'' && ch == '\'')
return "\'";
// now do the slash (we're not gonna check if it's a slash - you better pass one!)
switch(ch) {
case '0': return "\0";
case 'a': return "\x07";
case 'b': return "\x08";
case 't':
case '\t': return "\x09";
case 'n': return "\x0A";
case 'v': return "\x0B";
case 'f': return "\x0C";
case 'r': return "\x0D";
case 'e': return "\x1B";
case ' ': return "\x20";
case '\"': return "\"";
case '\'': return "\'";
case '\\': return "\\";
case 'N': return "\xC2\x85"; // NEL (#x85)
case '_': return "\xC2\xA0"; // #xA0
case 'L': return "\xE2\x80\xA8"; // LS (#x2028)
case 'P': return "\xE2\x80\xA9"; // PS (#x2029)
case 'x': return Escape(in, 2);
case 'u': return Escape(in, 4);
case 'U': return Escape(in, 8);
}
std::stringstream msg;
throw ParserException(in.line, in.column, ErrorMsg::INVALID_ESCAPE + ch);
}
}
}
#include "crt.h"
#include "exp.h"
#include "exceptions.h"
#include <sstream>
namespace YAML
{
namespace Exp
{
unsigned ParseHex(const std::string& str, int line, int column)
{
unsigned value = 0;
for(unsigned i=0;i<str.size();i++) {
char ch = str[i];
int digit = 0;
if('a' <= ch && ch <= 'f')
digit = ch - 'a' + 10;
else if('A' <= ch && ch <= 'F')
digit = ch - 'A' + 10;
else if('0' <= ch && ch <= '9')
digit = ch - '0';
else
throw ParserException(line, column, ErrorMsg::INVALID_HEX);
value = (value << 4) + digit;
}
return value;
}
std::string Str(char ch)
{
return std::string("") + ch;
}
// Escape
// . Translates the next 'codeLength' characters into a hex number and returns the result.
// . Throws if it's not actually hex.
std::string Escape(Stream& in, int codeLength)
{
// grab string
std::string str;
for(int i=0;i<codeLength;i++)
str += in.get();
// get the value
unsigned value = ParseHex(str, in.line, in.column);
// legal unicode?
if((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) {
std::stringstream msg;
msg << ErrorMsg::INVALID_UNICODE << value;
throw ParserException(in.line, in.column, msg.str());
}
// now break it up into chars
if(value <= 0x7F)
return Str(value);
else if(value <= 0x7FF)
return Str(0xC0 + (value >> 6)) + Str(0x80 + (value & 0x3F));
else if(value <= 0xFFFF)
return Str(0xE0 + (value >> 12)) + Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F));
else
return Str(0xF0 + (value >> 18)) + Str(0x80 + ((value >> 12) & 0x3F)) +
Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F));
}
// Escape
// . Escapes the sequence starting 'in' (it must begin with a '\' or single quote)
// and returns the result.
// . Throws if it's an unknown escape character.
std::string Escape(Stream& in)
{
// eat slash
char escape = in.get();
// switch on escape character
char ch = in.get();
// first do single quote, since it's easier
if(escape == '\'' && ch == '\'')
return "\'";
// now do the slash (we're not gonna check if it's a slash - you better pass one!)
switch(ch) {
case '0': return "\0";
case 'a': return "\x07";
case 'b': return "\x08";
case 't':
case '\t': return "\x09";
case 'n': return "\x0A";
case 'v': return "\x0B";
case 'f': return "\x0C";
case 'r': return "\x0D";
case 'e': return "\x1B";
case ' ': return "\x20";
case '\"': return "\"";
case '\'': return "\'";
case '\\': return "\\";
case 'N': return "\xC2\x85"; // NEL (#x85)
case '_': return "\xC2\xA0"; // #xA0
case 'L': return "\xE2\x80\xA8"; // LS (#x2028)
case 'P': return "\xE2\x80\xA9"; // PS (#x2029)
case 'x': return Escape(in, 2);
case 'u': return Escape(in, 4);
case 'U': return Escape(in, 8);
}
std::stringstream msg;
throw ParserException(in.line, in.column, ErrorMsg::INVALID_ESCAPE + ch);
}
}
}

142
src/exp.h
View file

@ -1,71 +1,71 @@
#pragma once
#include "regex.h"
#include <string>
#include <ios>
#include "stream.h"
namespace YAML
{
////////////////////////////////////////////////////////////////////////////////
// Here we store a bunch of expressions for matching different parts of the file.
namespace Exp
{
// misc
const RegEx Blank = RegEx(' ') || RegEx('\t');
const RegEx Break = RegEx('\n') || RegEx("\r\n");
const RegEx BlankOrBreak = Blank || Break;
const RegEx Digit = RegEx('0', '9');
const RegEx Alpha = RegEx('a', 'z') || RegEx('A', 'Z');
const RegEx AlphaNumeric = Alpha || Digit;
const RegEx Hex = Digit || RegEx('A', 'F') || RegEx('a', 'f');
// actual tags
const RegEx DocStart = RegEx("---") + (BlankOrBreak || RegEx(EOF) || RegEx());
const RegEx DocEnd = RegEx("...") + (BlankOrBreak || RegEx(EOF) || RegEx());
const RegEx DocIndicator = DocStart || DocEnd;
const RegEx BlockEntry = RegEx('-') + (BlankOrBreak || RegEx(EOF));
const RegEx Key = RegEx('?'),
KeyInFlow = RegEx('?') + BlankOrBreak;
const RegEx Value = RegEx(':') + BlankOrBreak,
ValueInFlow = RegEx(':') + BlankOrBreak;
const RegEx Comment = RegEx('#');
const RegEx AnchorEnd = RegEx("?:,]}%@`", REGEX_OR) || BlankOrBreak;
// Plain scalar rules:
// . Cannot start with a blank.
// . Can never start with any of , [ ] { } # & * ! | > \' \" % @ `
// . 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.
const RegEx PlainScalar = !(BlankOrBreak || RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-?:", REGEX_OR) + Blank)),
PlainScalarInFlow = !(BlankOrBreak || RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-:", REGEX_OR) + Blank));
const RegEx EndScalar = RegEx(':') + BlankOrBreak,
EndScalarInFlow = (RegEx(':') + BlankOrBreak) || RegEx(",?[]{}", REGEX_OR);
const RegEx EscSingleQuote = RegEx("\'\'");
const RegEx EscBreak = RegEx('\\') + Break;
const RegEx ChompIndicator = RegEx("+-", REGEX_OR);
const RegEx Chomp = (ChompIndicator + Digit) || (Digit + ChompIndicator) || ChompIndicator || Digit;
// and some functions
std::string Escape(Stream& in);
}
namespace Keys
{
const char Directive = '%';
const char FlowSeqStart = '[';
const char FlowSeqEnd = ']';
const char FlowMapStart = '{';
const char FlowMapEnd = '}';
const char FlowEntry = ',';
const char Alias = '*';
const char Anchor = '&';
const char Tag = '!';
const char LiteralScalar = '|';
const char FoldedScalar = '>';
}
}
#pragma once
#include "regex.h"
#include <string>
#include <ios>
#include "stream.h"
namespace YAML
{
////////////////////////////////////////////////////////////////////////////////
// Here we store a bunch of expressions for matching different parts of the file.
namespace Exp
{
// misc
const RegEx Blank = RegEx(' ') || RegEx('\t');
const RegEx Break = RegEx('\n') || RegEx("\r\n");
const RegEx BlankOrBreak = Blank || Break;
const RegEx Digit = RegEx('0', '9');
const RegEx Alpha = RegEx('a', 'z') || RegEx('A', 'Z');
const RegEx AlphaNumeric = Alpha || Digit;
const RegEx Hex = Digit || RegEx('A', 'F') || RegEx('a', 'f');
// actual tags
const RegEx DocStart = RegEx("---") + (BlankOrBreak || RegEx(EOF) || RegEx());
const RegEx DocEnd = RegEx("...") + (BlankOrBreak || RegEx(EOF) || RegEx());
const RegEx DocIndicator = DocStart || DocEnd;
const RegEx BlockEntry = RegEx('-') + (BlankOrBreak || RegEx(EOF));
const RegEx Key = RegEx('?'),
KeyInFlow = RegEx('?') + BlankOrBreak;
const RegEx Value = RegEx(':') + BlankOrBreak,
ValueInFlow = RegEx(':') + BlankOrBreak;
const RegEx Comment = RegEx('#');
const RegEx AnchorEnd = RegEx("?:,]}%@`", REGEX_OR) || BlankOrBreak;
// Plain scalar rules:
// . Cannot start with a blank.
// . Can never start with any of , [ ] { } # & * ! | > \' \" % @ `
// . 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.
const RegEx PlainScalar = !(BlankOrBreak || RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-?:", REGEX_OR) + Blank)),
PlainScalarInFlow = !(BlankOrBreak || RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) || (RegEx("-:", REGEX_OR) + Blank));
const RegEx EndScalar = RegEx(':') + BlankOrBreak,
EndScalarInFlow = (RegEx(':') + BlankOrBreak) || RegEx(",?[]{}", REGEX_OR);
const RegEx EscSingleQuote = RegEx("\'\'");
const RegEx EscBreak = RegEx('\\') + Break;
const RegEx ChompIndicator = RegEx("+-", REGEX_OR);
const RegEx Chomp = (ChompIndicator + Digit) || (Digit + ChompIndicator) || ChompIndicator || Digit;
// and some functions
std::string Escape(Stream& in);
}
namespace Keys
{
const char Directive = '%';
const char FlowSeqStart = '[';
const char FlowSeqEnd = ']';
const char FlowMapStart = '{';
const char FlowMapEnd = '}';
const char FlowEntry = ',';
const char Alias = '*';
const char Anchor = '&';
const char Tag = '!';
const char LiteralScalar = '|';
const char FoldedScalar = '>';
}
}

View file

@ -1,108 +1,108 @@
#include "crt.h"
#include "node.h"
#include "exceptions.h"
#include "iterpriv.h"
namespace YAML
{
Iterator::Iterator(): m_pData(0)
{
m_pData = new IterPriv;
}
Iterator::Iterator(IterPriv *pData): m_pData(pData)
{
}
Iterator::Iterator(const Iterator& rhs): m_pData(0)
{
m_pData = new IterPriv(*rhs.m_pData);
}
Iterator& Iterator::operator = (const Iterator& rhs)
{
if(this == &rhs)
return *this;
delete m_pData;
m_pData = new IterPriv(*rhs.m_pData);
return *this;
}
Iterator::~Iterator()
{
delete m_pData;
}
Iterator& Iterator::operator ++ ()
{
if(m_pData->type == IterPriv::IT_SEQ)
++m_pData->seqIter;
else if(m_pData->type == IterPriv::IT_MAP)
++m_pData->mapIter;
return *this;
}
Iterator Iterator::operator ++ (int)
{
Iterator temp = *this;
if(m_pData->type == IterPriv::IT_SEQ)
++m_pData->seqIter;
else if(m_pData->type == IterPriv::IT_MAP)
++m_pData->mapIter;
return temp;
}
const Node& Iterator::operator * () const
{
if(m_pData->type == IterPriv::IT_SEQ)
return **m_pData->seqIter;
throw BadDereference();
}
const Node *Iterator::operator -> () const
{
if(m_pData->type == IterPriv::IT_SEQ)
return *m_pData->seqIter;
throw BadDereference();
}
const Node& Iterator::first() const
{
if(m_pData->type == IterPriv::IT_MAP)
return *m_pData->mapIter->first;
throw BadDereference();
}
const Node& Iterator::second() const
{
if(m_pData->type == IterPriv::IT_MAP)
return *m_pData->mapIter->second;
throw BadDereference();
}
bool operator == (const Iterator& it, const Iterator& jt)
{
if(it.m_pData->type != jt.m_pData->type)
return false;
if(it.m_pData->type == IterPriv::IT_SEQ)
return it.m_pData->seqIter == jt.m_pData->seqIter;
else if(it.m_pData->type == IterPriv::IT_MAP)
return it.m_pData->mapIter == jt.m_pData->mapIter;
return true;
}
bool operator != (const Iterator& it, const Iterator& jt)
{
return !(it == jt);
}
}
#include "crt.h"
#include "node.h"
#include "exceptions.h"
#include "iterpriv.h"
namespace YAML
{
Iterator::Iterator(): m_pData(0)
{
m_pData = new IterPriv;
}
Iterator::Iterator(IterPriv *pData): m_pData(pData)
{
}
Iterator::Iterator(const Iterator& rhs): m_pData(0)
{
m_pData = new IterPriv(*rhs.m_pData);
}
Iterator& Iterator::operator = (const Iterator& rhs)
{
if(this == &rhs)
return *this;
delete m_pData;
m_pData = new IterPriv(*rhs.m_pData);
return *this;
}
Iterator::~Iterator()
{
delete m_pData;
}
Iterator& Iterator::operator ++ ()
{
if(m_pData->type == IterPriv::IT_SEQ)
++m_pData->seqIter;
else if(m_pData->type == IterPriv::IT_MAP)
++m_pData->mapIter;
return *this;
}
Iterator Iterator::operator ++ (int)
{
Iterator temp = *this;
if(m_pData->type == IterPriv::IT_SEQ)
++m_pData->seqIter;
else if(m_pData->type == IterPriv::IT_MAP)
++m_pData->mapIter;
return temp;
}
const Node& Iterator::operator * () const
{
if(m_pData->type == IterPriv::IT_SEQ)
return **m_pData->seqIter;
throw BadDereference();
}
const Node *Iterator::operator -> () const
{
if(m_pData->type == IterPriv::IT_SEQ)
return *m_pData->seqIter;
throw BadDereference();
}
const Node& Iterator::first() const
{
if(m_pData->type == IterPriv::IT_MAP)
return *m_pData->mapIter->first;
throw BadDereference();
}
const Node& Iterator::second() const
{
if(m_pData->type == IterPriv::IT_MAP)
return *m_pData->mapIter->second;
throw BadDereference();
}
bool operator == (const Iterator& it, const Iterator& jt)
{
if(it.m_pData->type != jt.m_pData->type)
return false;
if(it.m_pData->type == IterPriv::IT_SEQ)
return it.m_pData->seqIter == jt.m_pData->seqIter;
else if(it.m_pData->type == IterPriv::IT_MAP)
return it.m_pData->mapIter == jt.m_pData->mapIter;
return true;
}
bool operator != (const Iterator& it, const Iterator& jt)
{
return !(it == jt);
}
}

View file

@ -1,25 +1,25 @@
#pragma once
#include "ltnode.h"
#include <vector>
#include <map>
namespace YAML
{
class Node;
// IterPriv
// . The implementation for iterators - essentially a union of sequence and map iterators.
struct IterPriv
{
IterPriv(): type(IT_NONE) {}
IterPriv(std::vector <Node *>::const_iterator it): type(IT_SEQ), seqIter(it) {}
IterPriv(std::map <Node *, Node *, ltnode>::const_iterator it): type(IT_MAP), mapIter(it) {}
enum ITER_TYPE { IT_NONE, IT_SEQ, IT_MAP };
ITER_TYPE type;
std::vector <Node *>::const_iterator seqIter;
std::map <Node *, Node *, ltnode>::const_iterator mapIter;
};
}
#pragma once
#include "ltnode.h"
#include <vector>
#include <map>
namespace YAML
{
class Node;
// IterPriv
// . The implementation for iterators - essentially a union of sequence and map iterators.
struct IterPriv
{
IterPriv(): type(IT_NONE) {}
IterPriv(std::vector <Node *>::const_iterator it): type(IT_SEQ), seqIter(it) {}
IterPriv(std::map <Node *, Node *, ltnode>::const_iterator it): type(IT_MAP), mapIter(it) {}
enum ITER_TYPE { IT_NONE, IT_SEQ, IT_MAP };
ITER_TYPE type;
std::vector <Node *>::const_iterator seqIter;
std::map <Node *, Node *, ltnode>::const_iterator mapIter;
};
}

View file

@ -1,10 +1,10 @@
#pragma once
namespace YAML
{
class Node;
struct ltnode {
bool operator()(const Node *pNode1, const Node *pNode2) const;
};
}
#pragma once
namespace YAML
{
class Node;
struct ltnode {
bool operator()(const Node *pNode1, const Node *pNode2) const;
};
}

View file

@ -1,196 +1,196 @@
#include "crt.h"
#include "map.h"
#include "node.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include <iostream>
namespace YAML
{
Map::Map()
{
}
Map::~Map()
{
Clear();
}
void Map::Clear()
{
for(node_map::const_iterator it=m_data.begin();it!=m_data.end();++it) {
delete it->first;
delete it->second;
}
m_data.clear();
}
bool Map::GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& it) const
{
it = m_data.begin();
return true;
}
bool Map::GetEnd(std::map <Node *, Node *, ltnode>::const_iterator& it) const
{
it = m_data.end();
return true;
}
void Map::Parse(Scanner *pScanner, const ParserState& state)
{
Clear();
// split based on start token
switch(pScanner->peek().type) {
case TT_BLOCK_MAP_START: ParseBlock(pScanner, state); break;
case TT_FLOW_MAP_START: ParseFlow(pScanner, state); break;
}
}
void Map::ParseBlock(Scanner *pScanner, const ParserState& state)
{
// eat start token
pScanner->pop();
while(1) {
if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_MAP);
Token token = pScanner->peek();
if(token.type != TT_KEY && token.type != TT_BLOCK_END)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_MAP);
pScanner->pop();
if(token.type == TT_BLOCK_END)
break;
Node *pKey = new Node;
Node *pValue = new Node;
try {
// grab key
pKey->Parse(pScanner, state);
// now grab value (optional)
if(!pScanner->empty() && pScanner->peek().type == TT_VALUE) {
pScanner->pop();
pValue->Parse(pScanner, state);
}
m_data[pKey] = pValue;
} catch(Exception& e) {
delete pKey;
delete pValue;
throw e;
}
}
}
void Map::ParseFlow(Scanner *pScanner, const ParserState& state)
{
// eat start token
pScanner->pop();
while(1) {
if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_MAP_FLOW);
Token& token = pScanner->peek();
// first check for end
if(token.type == TT_FLOW_MAP_END) {
pScanner->pop();
break;
}
// now it better be a key
if(token.type != TT_KEY)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_MAP_FLOW);
pScanner->pop();
Node *pKey = new Node;
Node *pValue = new Node;
try {
// grab key
pKey->Parse(pScanner, state);
// now grab value (optional)
if(!pScanner->empty() && pScanner->peek().type == TT_VALUE) {
pScanner->pop();
pValue->Parse(pScanner, state);
}
// now eat the separator (or could be a map end, which we ignore - but if it's neither, then it's a bad node)
Token& nextToken = pScanner->peek();
if(nextToken.type == TT_FLOW_ENTRY)
pScanner->pop();
else if(nextToken.type != TT_FLOW_MAP_END)
throw ParserException(nextToken.line, nextToken.column, ErrorMsg::END_OF_MAP_FLOW);
m_data[pKey] = pValue;
} catch(Exception& e) {
// clean up and rethrow
delete pKey;
delete pValue;
throw e;
}
}
}
void Map::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine)
{
if(startedLine && !onlyOneCharOnLine)
out << "\n";
for(node_map::const_iterator it=m_data.begin();it!=m_data.end();++it) {
if((startedLine && !onlyOneCharOnLine) || it != m_data.begin()) {
for(int i=0;i<indent;i++)
out << " ";
}
out << "? ";
it->first->Write(out, indent + 1, true, it!= m_data.begin() || !startedLine || onlyOneCharOnLine);
for(int i=0;i<indent;i++)
out << " ";
out << ": ";
it->second->Write(out, indent + 1, true, true);
}
if(m_data.empty())
out << "\n";
}
int Map::Compare(Content *pContent)
{
return -pContent->Compare(this);
}
int Map::Compare(Map *pMap)
{
node_map::const_iterator it = m_data.begin(), jt = pMap->m_data.begin();
while(1) {
if(it == m_data.end()) {
if(jt == pMap->m_data.end())
return 0;
else
return -1;
}
if(jt == pMap->m_data.end())
return 1;
int cmp = it->first->Compare(*jt->first);
if(cmp != 0)
return cmp;
cmp = it->second->Compare(*jt->second);
if(cmp != 0)
return cmp;
}
return 0;
}
}
#include "crt.h"
#include "map.h"
#include "node.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include <iostream>
namespace YAML
{
Map::Map()
{
}
Map::~Map()
{
Clear();
}
void Map::Clear()
{
for(node_map::const_iterator it=m_data.begin();it!=m_data.end();++it) {
delete it->first;
delete it->second;
}
m_data.clear();
}
bool Map::GetBegin(std::map <Node *, Node *, ltnode>::const_iterator& it) const
{
it = m_data.begin();
return true;
}
bool Map::GetEnd(std::map <Node *, Node *, ltnode>::const_iterator& it) const
{
it = m_data.end();
return true;
}
void Map::Parse(Scanner *pScanner, const ParserState& state)
{
Clear();
// split based on start token
switch(pScanner->peek().type) {
case TT_BLOCK_MAP_START: ParseBlock(pScanner, state); break;
case TT_FLOW_MAP_START: ParseFlow(pScanner, state); break;
}
}
void Map::ParseBlock(Scanner *pScanner, const ParserState& state)
{
// eat start token
pScanner->pop();
while(1) {
if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_MAP);
Token token = pScanner->peek();
if(token.type != TT_KEY && token.type != TT_BLOCK_END)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_MAP);
pScanner->pop();
if(token.type == TT_BLOCK_END)
break;
Node *pKey = new Node;
Node *pValue = new Node;
try {
// grab key
pKey->Parse(pScanner, state);
// now grab value (optional)
if(!pScanner->empty() && pScanner->peek().type == TT_VALUE) {
pScanner->pop();
pValue->Parse(pScanner, state);
}
m_data[pKey] = pValue;
} catch(Exception& e) {
delete pKey;
delete pValue;
throw e;
}
}
}
void Map::ParseFlow(Scanner *pScanner, const ParserState& state)
{
// eat start token
pScanner->pop();
while(1) {
if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_MAP_FLOW);
Token& token = pScanner->peek();
// first check for end
if(token.type == TT_FLOW_MAP_END) {
pScanner->pop();
break;
}
// now it better be a key
if(token.type != TT_KEY)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_MAP_FLOW);
pScanner->pop();
Node *pKey = new Node;
Node *pValue = new Node;
try {
// grab key
pKey->Parse(pScanner, state);
// now grab value (optional)
if(!pScanner->empty() && pScanner->peek().type == TT_VALUE) {
pScanner->pop();
pValue->Parse(pScanner, state);
}
// now eat the separator (or could be a map end, which we ignore - but if it's neither, then it's a bad node)
Token& nextToken = pScanner->peek();
if(nextToken.type == TT_FLOW_ENTRY)
pScanner->pop();
else if(nextToken.type != TT_FLOW_MAP_END)
throw ParserException(nextToken.line, nextToken.column, ErrorMsg::END_OF_MAP_FLOW);
m_data[pKey] = pValue;
} catch(Exception& e) {
// clean up and rethrow
delete pKey;
delete pValue;
throw e;
}
}
}
void Map::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine)
{
if(startedLine && !onlyOneCharOnLine)
out << "\n";
for(node_map::const_iterator it=m_data.begin();it!=m_data.end();++it) {
if((startedLine && !onlyOneCharOnLine) || it != m_data.begin()) {
for(int i=0;i<indent;i++)
out << " ";
}
out << "? ";
it->first->Write(out, indent + 1, true, it!= m_data.begin() || !startedLine || onlyOneCharOnLine);
for(int i=0;i<indent;i++)
out << " ";
out << ": ";
it->second->Write(out, indent + 1, true, true);
}
if(m_data.empty())
out << "\n";
}
int Map::Compare(Content *pContent)
{
return -pContent->Compare(this);
}
int Map::Compare(Map *pMap)
{
node_map::const_iterator it = m_data.begin(), jt = pMap->m_data.begin();
while(1) {
if(it == m_data.end()) {
if(jt == pMap->m_data.end())
return 0;
else
return -1;
}
if(jt == pMap->m_data.end())
return 1;
int cmp = it->first->Compare(*jt->first);
if(cmp != 0)
return cmp;
cmp = it->second->Compare(*jt->second);
if(cmp != 0)
return cmp;
}
return 0;
}
}

View file

@ -1,38 +1,38 @@
#pragma once
#include "content.h"
#include <map>
namespace YAML
{
class Node;
class Map: public Content
{
public:
Map();
virtual ~Map();
void Clear();
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 void Parse(Scanner *pScanner, const ParserState& state);
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine);
virtual bool IsMap() const { return true; }
// ordering
virtual int Compare(Content *pContent);
virtual int Compare(Scalar *pScalar) { return 1; }
virtual int Compare(Sequence *pSeq) { return 1; }
virtual int Compare(Map *pMap);
private:
void ParseBlock(Scanner *pScanner, const ParserState& state);
void ParseFlow(Scanner *pScanner, const ParserState& state);
protected:
typedef std::map <Node *, Node *, ltnode> node_map;
node_map m_data;
};
}
#pragma once
#include "content.h"
#include <map>
namespace YAML
{
class Node;
class Map: public Content
{
public:
Map();
virtual ~Map();
void Clear();
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 void Parse(Scanner *pScanner, const ParserState& state);
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine);
virtual bool IsMap() const { return true; }
// ordering
virtual int Compare(Content *pContent);
virtual int Compare(Scalar *pScalar) { return 1; }
virtual int Compare(Sequence *pSeq) { return 1; }
virtual int Compare(Map *pMap);
private:
void ParseBlock(Scanner *pScanner, const ParserState& state);
void ParseFlow(Scanner *pScanner, const ParserState& state);
protected:
typedef std::map <Node *, Node *, ltnode> node_map;
node_map m_data;
};
}

View file

@ -1,317 +1,317 @@
#include "crt.h"
#include "node.h"
#include "token.h"
#include "scanner.h"
#include "content.h"
#include "parser.h"
#include "scalar.h"
#include "sequence.h"
#include "map.h"
#include "iterpriv.h"
namespace YAML
{
// the ordering!
bool ltnode::operator ()(const Node *pNode1, const Node *pNode2) const
{
return *pNode1 < *pNode2;
}
Node::Node(): m_pContent(0), m_alias(false)
{
}
Node::~Node()
{
Clear();
}
void Node::Clear()
{
delete m_pContent;
m_pContent = 0;
m_alias = false;
}
void Node::Parse(Scanner *pScanner, const ParserState& state)
{
Clear();
ParseHeader(pScanner, state);
// is this an alias? if so, it can have no content
if(m_alias)
return;
// now split based on what kind of node we should be
switch(pScanner->peek().type) {
case TT_SCALAR:
m_pContent = new Scalar;
m_pContent->Parse(pScanner, state);
break;
case TT_FLOW_SEQ_START:
case TT_BLOCK_SEQ_START:
case TT_BLOCK_ENTRY:
m_pContent = new Sequence;
m_pContent->Parse(pScanner, state);
break;
case TT_FLOW_MAP_START:
case TT_BLOCK_MAP_START:
m_pContent = new Map;
m_pContent->Parse(pScanner, state);
break;
}
}
// ParseHeader
// . Grabs any tag, alias, or anchor tokens and deals with them.
void Node::ParseHeader(Scanner *pScanner, const ParserState& state)
{
while(1) {
if(pScanner->empty())
return;
switch(pScanner->peek().type) {
case TT_TAG: ParseTag(pScanner, state); break;
case TT_ANCHOR: ParseAnchor(pScanner, state); break;
case TT_ALIAS: ParseAlias(pScanner, state); break;
default: return;
}
}
}
void Node::ParseTag(Scanner *pScanner, const ParserState& state)
{
Token& token = pScanner->peek();
if(m_tag != "")
throw ParserException(token.line, token.column, ErrorMsg::MULTIPLE_TAGS);
m_tag = state.TranslateTag(token.value);
for(unsigned i=0;i<token.params.size();i++)
m_tag += token.params[i];
pScanner->pop();
}
void Node::ParseAnchor(Scanner *pScanner, const ParserState& state)
{
Token& token = pScanner->peek();
if(m_anchor != "")
throw ParserException(token.line, token.column, ErrorMsg::MULTIPLE_ANCHORS);
m_anchor = token.value;
m_alias = false;
pScanner->pop();
}
void Node::ParseAlias(Scanner *pScanner, const ParserState& state)
{
Token& token = pScanner->peek();
if(m_anchor != "")
throw ParserException(token.line, token.column, ErrorMsg::MULTIPLE_ALIASES);
if(m_tag != "")
throw ParserException(token.line, token.column, ErrorMsg::ALIAS_CONTENT);
m_anchor = token.value;
m_alias = true;
pScanner->pop();
}
void Node::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) const
{
// write anchor/alias
if(m_anchor != "") {
if(m_alias)
out << std::string("*");
else
out << std::string("&");
out << m_anchor << std::string(" ");
startedLine = true;
onlyOneCharOnLine = false;
}
// write tag
if(m_tag != "") {
out << std::string("!<") << m_tag << std::string("> ");
startedLine = true;
onlyOneCharOnLine = false;
}
if(!m_pContent) {
out << std::string("\n");
} else {
m_pContent->Write(out, indent, startedLine, onlyOneCharOnLine);
}
}
CONTENT_TYPE Node::GetType() const
{
if(!m_pContent)
return CT_NONE;
if(m_pContent->IsScalar())
return CT_SCALAR;
else if(m_pContent->IsSequence())
return CT_SEQUENCE;
else if(m_pContent->IsMap())
return CT_MAP;
return CT_NONE;
}
// begin
// Returns an iterator to the beginning of this (sequence or map).
Iterator Node::begin() const
{
if(!m_pContent)
return Iterator();
std::vector <Node *>::const_iterator seqIter;
if(m_pContent->GetBegin(seqIter))
return Iterator(new IterPriv(seqIter));
std::map <Node *, Node *, ltnode>::const_iterator mapIter;
if(m_pContent->GetBegin(mapIter))
return Iterator(new IterPriv(mapIter));
return Iterator();
}
// end
// . Returns an iterator to the end of this (sequence or map).
Iterator Node::end() const
{
if(!m_pContent)
return Iterator();
std::vector <Node *>::const_iterator seqIter;
if(m_pContent->GetEnd(seqIter))
return Iterator(new IterPriv(seqIter));
std::map <Node *, Node *, ltnode>::const_iterator mapIter;
if(m_pContent->GetEnd(mapIter))
return Iterator(new IterPriv(mapIter));
return Iterator();
}
// size
// . Returns the size of this node, if it's a sequence node.
// . Otherwise, returns zero.
unsigned Node::size() const
{
if(!m_pContent)
return 0;
return m_pContent->GetSize();
}
const Node& Node::operator [] (unsigned u) const
{
if(!m_pContent)
throw BadDereference();
Node *pNode = m_pContent->GetNode(u);
if(pNode)
return *pNode;
return GetValue(u);
}
const Node& Node::operator [] (int i) const
{
if(!m_pContent)
throw BadDereference();
Node *pNode = m_pContent->GetNode(i);
if(pNode)
return *pNode;
return GetValue(i);
}
///////////////////////////////////////////////////////
// Extraction
void operator >> (const Node& node, std::string& s)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(s);
}
void operator >> (const Node& node, int& i)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(i);
}
void operator >> (const Node& node, unsigned& u)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(u);
}
void operator >> (const Node& node, long& l)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(l);
}
void operator >> (const Node& node, float& f)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(f);
}
void operator >> (const Node& node, double& d)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(d);
}
void operator >> (const Node& node, char& c)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(c);
}
std::ostream& operator << (std::ostream& out, const Node& node)
{
node.Write(out, 0, false, false);
return out;
}
int Node::Compare(const Node& rhs) const
{
// Step 1: no content is the smallest
if(!m_pContent) {
if(rhs.m_pContent)
return -1;
else
return 0;
}
if(!rhs.m_pContent)
return 1;
return m_pContent->Compare(rhs.m_pContent);
}
bool operator < (const Node& n1, const Node& n2)
{
return n1.Compare(n2) < 0;
}
}
#include "crt.h"
#include "node.h"
#include "token.h"
#include "scanner.h"
#include "content.h"
#include "parser.h"
#include "scalar.h"
#include "sequence.h"
#include "map.h"
#include "iterpriv.h"
namespace YAML
{
// the ordering!
bool ltnode::operator ()(const Node *pNode1, const Node *pNode2) const
{
return *pNode1 < *pNode2;
}
Node::Node(): m_pContent(0), m_alias(false)
{
}
Node::~Node()
{
Clear();
}
void Node::Clear()
{
delete m_pContent;
m_pContent = 0;
m_alias = false;
}
void Node::Parse(Scanner *pScanner, const ParserState& state)
{
Clear();
ParseHeader(pScanner, state);
// is this an alias? if so, it can have no content
if(m_alias)
return;
// now split based on what kind of node we should be
switch(pScanner->peek().type) {
case TT_SCALAR:
m_pContent = new Scalar;
m_pContent->Parse(pScanner, state);
break;
case TT_FLOW_SEQ_START:
case TT_BLOCK_SEQ_START:
case TT_BLOCK_ENTRY:
m_pContent = new Sequence;
m_pContent->Parse(pScanner, state);
break;
case TT_FLOW_MAP_START:
case TT_BLOCK_MAP_START:
m_pContent = new Map;
m_pContent->Parse(pScanner, state);
break;
}
}
// ParseHeader
// . Grabs any tag, alias, or anchor tokens and deals with them.
void Node::ParseHeader(Scanner *pScanner, const ParserState& state)
{
while(1) {
if(pScanner->empty())
return;
switch(pScanner->peek().type) {
case TT_TAG: ParseTag(pScanner, state); break;
case TT_ANCHOR: ParseAnchor(pScanner, state); break;
case TT_ALIAS: ParseAlias(pScanner, state); break;
default: return;
}
}
}
void Node::ParseTag(Scanner *pScanner, const ParserState& state)
{
Token& token = pScanner->peek();
if(m_tag != "")
throw ParserException(token.line, token.column, ErrorMsg::MULTIPLE_TAGS);
m_tag = state.TranslateTag(token.value);
for(unsigned i=0;i<token.params.size();i++)
m_tag += token.params[i];
pScanner->pop();
}
void Node::ParseAnchor(Scanner *pScanner, const ParserState& state)
{
Token& token = pScanner->peek();
if(m_anchor != "")
throw ParserException(token.line, token.column, ErrorMsg::MULTIPLE_ANCHORS);
m_anchor = token.value;
m_alias = false;
pScanner->pop();
}
void Node::ParseAlias(Scanner *pScanner, const ParserState& state)
{
Token& token = pScanner->peek();
if(m_anchor != "")
throw ParserException(token.line, token.column, ErrorMsg::MULTIPLE_ALIASES);
if(m_tag != "")
throw ParserException(token.line, token.column, ErrorMsg::ALIAS_CONTENT);
m_anchor = token.value;
m_alias = true;
pScanner->pop();
}
void Node::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) const
{
// write anchor/alias
if(m_anchor != "") {
if(m_alias)
out << std::string("*");
else
out << std::string("&");
out << m_anchor << std::string(" ");
startedLine = true;
onlyOneCharOnLine = false;
}
// write tag
if(m_tag != "") {
out << std::string("!<") << m_tag << std::string("> ");
startedLine = true;
onlyOneCharOnLine = false;
}
if(!m_pContent) {
out << std::string("\n");
} else {
m_pContent->Write(out, indent, startedLine, onlyOneCharOnLine);
}
}
CONTENT_TYPE Node::GetType() const
{
if(!m_pContent)
return CT_NONE;
if(m_pContent->IsScalar())
return CT_SCALAR;
else if(m_pContent->IsSequence())
return CT_SEQUENCE;
else if(m_pContent->IsMap())
return CT_MAP;
return CT_NONE;
}
// begin
// Returns an iterator to the beginning of this (sequence or map).
Iterator Node::begin() const
{
if(!m_pContent)
return Iterator();
std::vector <Node *>::const_iterator seqIter;
if(m_pContent->GetBegin(seqIter))
return Iterator(new IterPriv(seqIter));
std::map <Node *, Node *, ltnode>::const_iterator mapIter;
if(m_pContent->GetBegin(mapIter))
return Iterator(new IterPriv(mapIter));
return Iterator();
}
// end
// . Returns an iterator to the end of this (sequence or map).
Iterator Node::end() const
{
if(!m_pContent)
return Iterator();
std::vector <Node *>::const_iterator seqIter;
if(m_pContent->GetEnd(seqIter))
return Iterator(new IterPriv(seqIter));
std::map <Node *, Node *, ltnode>::const_iterator mapIter;
if(m_pContent->GetEnd(mapIter))
return Iterator(new IterPriv(mapIter));
return Iterator();
}
// size
// . Returns the size of this node, if it's a sequence node.
// . Otherwise, returns zero.
unsigned Node::size() const
{
if(!m_pContent)
return 0;
return m_pContent->GetSize();
}
const Node& Node::operator [] (unsigned u) const
{
if(!m_pContent)
throw BadDereference();
Node *pNode = m_pContent->GetNode(u);
if(pNode)
return *pNode;
return GetValue(u);
}
const Node& Node::operator [] (int i) const
{
if(!m_pContent)
throw BadDereference();
Node *pNode = m_pContent->GetNode(i);
if(pNode)
return *pNode;
return GetValue(i);
}
///////////////////////////////////////////////////////
// Extraction
void operator >> (const Node& node, std::string& s)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(s);
}
void operator >> (const Node& node, int& i)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(i);
}
void operator >> (const Node& node, unsigned& u)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(u);
}
void operator >> (const Node& node, long& l)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(l);
}
void operator >> (const Node& node, float& f)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(f);
}
void operator >> (const Node& node, double& d)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(d);
}
void operator >> (const Node& node, char& c)
{
if(!node.m_pContent)
throw;
node.m_pContent->Read(c);
}
std::ostream& operator << (std::ostream& out, const Node& node)
{
node.Write(out, 0, false, false);
return out;
}
int Node::Compare(const Node& rhs) const
{
// Step 1: no content is the smallest
if(!m_pContent) {
if(rhs.m_pContent)
return -1;
else
return 0;
}
if(!rhs.m_pContent)
return 1;
return m_pContent->Compare(rhs.m_pContent);
}
bool operator < (const Node& n1, const Node& n2)
{
return n1.Compare(n2) < 0;
}
}

View file

@ -1,133 +1,133 @@
#include "crt.h"
#include "parser.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include <sstream>
namespace YAML
{
Parser::Parser(std::istream& in): m_pScanner(0)
{
Load(in);
}
Parser::~Parser()
{
delete m_pScanner;
}
Parser::operator bool() const
{
return !m_pScanner->empty();
}
void Parser::Load(std::istream& in)
{
delete m_pScanner;
m_pScanner = new Scanner(in);
m_state.Reset();
}
// GetNextDocument
// . Reads the next document in the queue (of tokens).
// . Throws a ParserException on error.
void Parser::GetNextDocument(Node& document)
{
// clear node
document.Clear();
// first read directives
ParseDirectives();
// we better have some tokens in the queue
if(m_pScanner->empty())
return;
// first eat doc start (optional)
if(m_pScanner->peek().type == TT_DOC_START)
m_pScanner->pop();
// now parse our root node
document.Parse(m_pScanner, m_state);
// and finally eat any doc ends we see
while(!m_pScanner->empty() && m_pScanner->peek().type == TT_DOC_END)
m_pScanner->pop();
}
// ParseDirectives
// . Reads any directives that are next in the queue.
void Parser::ParseDirectives()
{
bool readDirective = false;
while(1) {
if(m_pScanner->empty())
break;
Token& token = m_pScanner->peek();
if(token.type != TT_DIRECTIVE)
break;
// we keep the directives from the last document if none are specified;
// but if any directives are specific, then we reset them
if(!readDirective)
m_state.Reset();
readDirective = true;
HandleDirective(&token);
m_pScanner->pop();
}
}
void Parser::HandleDirective(Token *pToken)
{
if(pToken->value == "YAML")
HandleYamlDirective(pToken);
else if(pToken->value == "TAG")
HandleTagDirective(pToken);
}
// HandleYamlDirective
// . Should be of the form 'major.minor' (like a version number)
void Parser::HandleYamlDirective(Token *pToken)
{
if(pToken->params.size() != 1)
throw ParserException(pToken->line, pToken->column, ErrorMsg::YAML_DIRECTIVE_ARGS);
std::stringstream str(pToken->params[0]);
str >> m_state.version.major;
str.get();
str >> m_state.version.minor;
if(!str || str.peek() != EOF)
throw ParserException(pToken->line, pToken->column, ErrorMsg::YAML_VERSION + pToken->params[0]);
if(m_state.version.major > 1)
throw ParserException(pToken->line, pToken->column, ErrorMsg::YAML_MAJOR_VERSION);
// TODO: warning on major == 1, minor > 2?
}
// HandleTagDirective
// . Should be of the form 'handle prefix', where 'handle' is converted to 'prefix' in the file.
void Parser::HandleTagDirective(Token *pToken)
{
if(pToken->params.size() != 2)
throw ParserException(pToken->line, pToken->column, ErrorMsg::TAG_DIRECTIVE_ARGS);
std::string handle = pToken->params[0], prefix = pToken->params[1];
m_state.tags[handle] = prefix;
}
void Parser::PrintTokens(std::ostream& out)
{
while(1) {
if(m_pScanner->empty())
break;
out << m_pScanner->peek() << "\n";
m_pScanner->pop();
}
}
}
#include "crt.h"
#include "parser.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include <sstream>
namespace YAML
{
Parser::Parser(std::istream& in): m_pScanner(0)
{
Load(in);
}
Parser::~Parser()
{
delete m_pScanner;
}
Parser::operator bool() const
{
return !m_pScanner->empty();
}
void Parser::Load(std::istream& in)
{
delete m_pScanner;
m_pScanner = new Scanner(in);
m_state.Reset();
}
// GetNextDocument
// . Reads the next document in the queue (of tokens).
// . Throws a ParserException on error.
void Parser::GetNextDocument(Node& document)
{
// clear node
document.Clear();
// first read directives
ParseDirectives();
// we better have some tokens in the queue
if(m_pScanner->empty())
return;
// first eat doc start (optional)
if(m_pScanner->peek().type == TT_DOC_START)
m_pScanner->pop();
// now parse our root node
document.Parse(m_pScanner, m_state);
// and finally eat any doc ends we see
while(!m_pScanner->empty() && m_pScanner->peek().type == TT_DOC_END)
m_pScanner->pop();
}
// ParseDirectives
// . Reads any directives that are next in the queue.
void Parser::ParseDirectives()
{
bool readDirective = false;
while(1) {
if(m_pScanner->empty())
break;
Token& token = m_pScanner->peek();
if(token.type != TT_DIRECTIVE)
break;
// we keep the directives from the last document if none are specified;
// but if any directives are specific, then we reset them
if(!readDirective)
m_state.Reset();
readDirective = true;
HandleDirective(&token);
m_pScanner->pop();
}
}
void Parser::HandleDirective(Token *pToken)
{
if(pToken->value == "YAML")
HandleYamlDirective(pToken);
else if(pToken->value == "TAG")
HandleTagDirective(pToken);
}
// HandleYamlDirective
// . Should be of the form 'major.minor' (like a version number)
void Parser::HandleYamlDirective(Token *pToken)
{
if(pToken->params.size() != 1)
throw ParserException(pToken->line, pToken->column, ErrorMsg::YAML_DIRECTIVE_ARGS);
std::stringstream str(pToken->params[0]);
str >> m_state.version.major;
str.get();
str >> m_state.version.minor;
if(!str || str.peek() != EOF)
throw ParserException(pToken->line, pToken->column, ErrorMsg::YAML_VERSION + pToken->params[0]);
if(m_state.version.major > 1)
throw ParserException(pToken->line, pToken->column, ErrorMsg::YAML_MAJOR_VERSION);
// TODO: warning on major == 1, minor > 2?
}
// HandleTagDirective
// . Should be of the form 'handle prefix', where 'handle' is converted to 'prefix' in the file.
void Parser::HandleTagDirective(Token *pToken)
{
if(pToken->params.size() != 2)
throw ParserException(pToken->line, pToken->column, ErrorMsg::TAG_DIRECTIVE_ARGS);
std::string handle = pToken->params[0], prefix = pToken->params[1];
m_state.tags[handle] = prefix;
}
void Parser::PrintTokens(std::ostream& out)
{
while(1) {
if(m_pScanner->empty())
break;
out << m_pScanner->peek() << "\n";
m_pScanner->pop();
}
}
}

View file

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

View file

@ -1,306 +1,306 @@
#include "crt.h"
#include "regex.h"
#include "stream.h"
#include <iostream>
namespace YAML
{
RegEx::RegEx(REGEX_OP op): m_op(op), m_pOp(0)
{
SetOp();
}
RegEx::RegEx(const RegEx& rhs): m_pOp(0)
{
m_op = rhs.m_op;
m_a = rhs.m_a;
m_z = rhs.m_z;
m_params = rhs.m_params;
SetOp();
}
RegEx::RegEx(): m_op(REGEX_EMPTY), m_pOp(0)
{
SetOp();
}
RegEx::RegEx(char ch): m_op(REGEX_MATCH), m_pOp(0), m_a(ch)
{
SetOp();
}
RegEx::RegEx(char a, char z): m_op(REGEX_RANGE), m_pOp(0), m_a(a), m_z(z)
{
SetOp();
}
RegEx::RegEx(const std::string& str, REGEX_OP op): m_op(op), m_pOp(0)
{
for(unsigned i=0;i<str.size();i++)
m_params.push_back(RegEx(str[i]));
SetOp();
}
RegEx::~RegEx()
{
delete m_pOp;
}
RegEx& RegEx::operator = (const RegEx& rhs)
{
delete m_pOp;
m_pOp = 0;
m_op = rhs.m_op;
m_a = rhs.m_a;
m_z = rhs.m_z;
m_params = rhs.m_params;
SetOp();
return *this;
}
void RegEx::SetOp()
{
delete m_pOp;
m_pOp = 0;
switch(m_op) {
case REGEX_MATCH: m_pOp = new MatchOperator; break;
case REGEX_RANGE: m_pOp = new RangeOperator; break;
case REGEX_OR: m_pOp = new OrOperator; break;
case REGEX_AND: m_pOp = new AndOperator; break;
case REGEX_NOT: m_pOp = new NotOperator; break;
case REGEX_SEQ: m_pOp = new SeqOperator; break;
}
}
bool RegEx::Matches(char ch) const
{
std::string str;
str += ch;
return Matches(str);
}
bool RegEx::Matches(const std::string& str) const
{
return Match(str) >= 0;
}
bool RegEx::Matches(std::istream& in) const
{
return Match(in) >= 0;
}
bool RegEx::Matches(Stream& in) const
{
return Match(in) >= 0;
}
// Match
// . Matches the given string against this regular expression.
// . Returns the number of characters matched.
// . Returns -1 if no characters were matched (the reason for
// not returning zero is that we may have an empty regex
// which is ALWAYS successful at matching zero characters).
int RegEx::Match(const std::string& str) const
{
if(!m_pOp)
return 0;
return m_pOp->Match(str, *this);
}
// Match
int RegEx::Match(Stream& in) const
{
return Match(in.stream());
}
// Match
// . The stream version does the same thing as the string version;
// REMEMBER that we only match from the start of the stream!
// . Note: the istream is not a const reference, but we guarantee
// that the pointer will be in the same spot, and we'll clear its
// flags before we end.
int RegEx::Match(std::istream& in) const
{
if(!m_pOp)
return -1;
int pos = in.tellg();
int ret = m_pOp->Match(in, *this);
// reset input stream!
in.clear();
in.seekg(pos);
return ret;
}
RegEx operator ! (const RegEx& ex)
{
RegEx ret(REGEX_NOT);
ret.m_params.push_back(ex);
return ret;
}
RegEx operator || (const RegEx& ex1, const RegEx& ex2)
{
RegEx ret(REGEX_OR);
ret.m_params.push_back(ex1);
ret.m_params.push_back(ex2);
return ret;
}
RegEx operator && (const RegEx& ex1, const RegEx& ex2)
{
RegEx ret(REGEX_AND);
ret.m_params.push_back(ex1);
ret.m_params.push_back(ex2);
return ret;
}
RegEx operator + (const RegEx& ex1, const RegEx& ex2)
{
RegEx ret(REGEX_SEQ);
ret.m_params.push_back(ex1);
ret.m_params.push_back(ex2);
return ret;
}
//////////////////////////////////////////////////////////////////////////////
// Operators
// MatchOperator
int RegEx::MatchOperator::Match(const std::string& str, const RegEx& regex) const
{
if(str.empty() || str[0] != regex.m_a)
return -1;
return 1;
}
int RegEx::MatchOperator::Match(std::istream& in, const RegEx& regex) const
{
if(!in || in.peek() != regex.m_a)
return -1;
return 1;
}
// RangeOperator
int RegEx::RangeOperator::Match(const std::string& str, const RegEx& regex) const
{
if(str.empty() || regex.m_a > str[0] || regex.m_z < str[0])
return -1;
return 1;
}
int RegEx::RangeOperator::Match(std::istream& in, const RegEx& regex) const
{
if(!in || regex.m_a > in.peek() || regex.m_z < in.peek())
return -1;
return 1;
}
// OrOperator
int RegEx::OrOperator::Match(const std::string& str, const RegEx& regex) const
{
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(str);
if(n >= 0)
return n;
}
return -1;
}
int RegEx::OrOperator::Match(std::istream& in, const RegEx& regex) const
{
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(in);
if(n >= 0)
return n;
}
return -1;
}
// AndOperator
// Note: 'AND' is a little funny, since we may be required to match things
// of different lengths. If we find a match, we return the length of
// the FIRST entry on the list.
int RegEx::AndOperator::Match(const std::string& str, const RegEx& regex) const
{
int first = -1;
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(str);
if(n == -1)
return -1;
if(i == 0)
first = n;
}
return first;
}
int RegEx::AndOperator::Match(std::istream& in, const RegEx& regex) const
{
int first = -1;
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(in);
if(n == -1)
return -1;
if(i == 0)
first = n;
}
return first;
}
// NotOperator
int RegEx::NotOperator::Match(const std::string& str, const RegEx& regex) const
{
if(regex.m_params.empty())
return -1;
if(regex.m_params[0].Match(str) >= 0)
return -1;
return 1;
}
int RegEx::NotOperator::Match(std::istream& in, const RegEx& regex) const
{
if(regex.m_params.empty())
return -1;
if(regex.m_params[0].Match(in) >= 0)
return -1;
return 1;
}
// SeqOperator
int RegEx::SeqOperator::Match(const std::string& str, const RegEx& regex) const
{
int offset = 0;
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(str.substr(offset));
if(n == -1)
return -1;
offset += n;
}
return offset;
}
int RegEx::SeqOperator::Match(std::istream& in, const RegEx& regex) const
{
int offset = 0;
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(in);
if(n == -1)
return -1;
offset += n;
in.ignore(n);
}
return offset;
}
}
#include "crt.h"
#include "regex.h"
#include "stream.h"
#include <iostream>
namespace YAML
{
RegEx::RegEx(REGEX_OP op): m_op(op), m_pOp(0)
{
SetOp();
}
RegEx::RegEx(const RegEx& rhs): m_pOp(0)
{
m_op = rhs.m_op;
m_a = rhs.m_a;
m_z = rhs.m_z;
m_params = rhs.m_params;
SetOp();
}
RegEx::RegEx(): m_op(REGEX_EMPTY), m_pOp(0)
{
SetOp();
}
RegEx::RegEx(char ch): m_op(REGEX_MATCH), m_pOp(0), m_a(ch)
{
SetOp();
}
RegEx::RegEx(char a, char z): m_op(REGEX_RANGE), m_pOp(0), m_a(a), m_z(z)
{
SetOp();
}
RegEx::RegEx(const std::string& str, REGEX_OP op): m_op(op), m_pOp(0)
{
for(unsigned i=0;i<str.size();i++)
m_params.push_back(RegEx(str[i]));
SetOp();
}
RegEx::~RegEx()
{
delete m_pOp;
}
RegEx& RegEx::operator = (const RegEx& rhs)
{
delete m_pOp;
m_pOp = 0;
m_op = rhs.m_op;
m_a = rhs.m_a;
m_z = rhs.m_z;
m_params = rhs.m_params;
SetOp();
return *this;
}
void RegEx::SetOp()
{
delete m_pOp;
m_pOp = 0;
switch(m_op) {
case REGEX_MATCH: m_pOp = new MatchOperator; break;
case REGEX_RANGE: m_pOp = new RangeOperator; break;
case REGEX_OR: m_pOp = new OrOperator; break;
case REGEX_AND: m_pOp = new AndOperator; break;
case REGEX_NOT: m_pOp = new NotOperator; break;
case REGEX_SEQ: m_pOp = new SeqOperator; break;
}
}
bool RegEx::Matches(char ch) const
{
std::string str;
str += ch;
return Matches(str);
}
bool RegEx::Matches(const std::string& str) const
{
return Match(str) >= 0;
}
bool RegEx::Matches(std::istream& in) const
{
return Match(in) >= 0;
}
bool RegEx::Matches(Stream& in) const
{
return Match(in) >= 0;
}
// Match
// . Matches the given string against this regular expression.
// . Returns the number of characters matched.
// . Returns -1 if no characters were matched (the reason for
// not returning zero is that we may have an empty regex
// which is ALWAYS successful at matching zero characters).
int RegEx::Match(const std::string& str) const
{
if(!m_pOp)
return 0;
return m_pOp->Match(str, *this);
}
// Match
int RegEx::Match(Stream& in) const
{
return Match(in.stream());
}
// Match
// . The stream version does the same thing as the string version;
// REMEMBER that we only match from the start of the stream!
// . Note: the istream is not a const reference, but we guarantee
// that the pointer will be in the same spot, and we'll clear its
// flags before we end.
int RegEx::Match(std::istream& in) const
{
if(!m_pOp)
return -1;
int pos = in.tellg();
int ret = m_pOp->Match(in, *this);
// reset input stream!
in.clear();
in.seekg(pos);
return ret;
}
RegEx operator ! (const RegEx& ex)
{
RegEx ret(REGEX_NOT);
ret.m_params.push_back(ex);
return ret;
}
RegEx operator || (const RegEx& ex1, const RegEx& ex2)
{
RegEx ret(REGEX_OR);
ret.m_params.push_back(ex1);
ret.m_params.push_back(ex2);
return ret;
}
RegEx operator && (const RegEx& ex1, const RegEx& ex2)
{
RegEx ret(REGEX_AND);
ret.m_params.push_back(ex1);
ret.m_params.push_back(ex2);
return ret;
}
RegEx operator + (const RegEx& ex1, const RegEx& ex2)
{
RegEx ret(REGEX_SEQ);
ret.m_params.push_back(ex1);
ret.m_params.push_back(ex2);
return ret;
}
//////////////////////////////////////////////////////////////////////////////
// Operators
// MatchOperator
int RegEx::MatchOperator::Match(const std::string& str, const RegEx& regex) const
{
if(str.empty() || str[0] != regex.m_a)
return -1;
return 1;
}
int RegEx::MatchOperator::Match(std::istream& in, const RegEx& regex) const
{
if(!in || in.peek() != regex.m_a)
return -1;
return 1;
}
// RangeOperator
int RegEx::RangeOperator::Match(const std::string& str, const RegEx& regex) const
{
if(str.empty() || regex.m_a > str[0] || regex.m_z < str[0])
return -1;
return 1;
}
int RegEx::RangeOperator::Match(std::istream& in, const RegEx& regex) const
{
if(!in || regex.m_a > in.peek() || regex.m_z < in.peek())
return -1;
return 1;
}
// OrOperator
int RegEx::OrOperator::Match(const std::string& str, const RegEx& regex) const
{
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(str);
if(n >= 0)
return n;
}
return -1;
}
int RegEx::OrOperator::Match(std::istream& in, const RegEx& regex) const
{
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(in);
if(n >= 0)
return n;
}
return -1;
}
// AndOperator
// Note: 'AND' is a little funny, since we may be required to match things
// of different lengths. If we find a match, we return the length of
// the FIRST entry on the list.
int RegEx::AndOperator::Match(const std::string& str, const RegEx& regex) const
{
int first = -1;
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(str);
if(n == -1)
return -1;
if(i == 0)
first = n;
}
return first;
}
int RegEx::AndOperator::Match(std::istream& in, const RegEx& regex) const
{
int first = -1;
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(in);
if(n == -1)
return -1;
if(i == 0)
first = n;
}
return first;
}
// NotOperator
int RegEx::NotOperator::Match(const std::string& str, const RegEx& regex) const
{
if(regex.m_params.empty())
return -1;
if(regex.m_params[0].Match(str) >= 0)
return -1;
return 1;
}
int RegEx::NotOperator::Match(std::istream& in, const RegEx& regex) const
{
if(regex.m_params.empty())
return -1;
if(regex.m_params[0].Match(in) >= 0)
return -1;
return 1;
}
// SeqOperator
int RegEx::SeqOperator::Match(const std::string& str, const RegEx& regex) const
{
int offset = 0;
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(str.substr(offset));
if(n == -1)
return -1;
offset += n;
}
return offset;
}
int RegEx::SeqOperator::Match(std::istream& in, const RegEx& regex) const
{
int offset = 0;
for(unsigned i=0;i<regex.m_params.size();i++) {
int n = regex.m_params[i].Match(in);
if(n == -1)
return -1;
offset += n;
in.ignore(n);
}
return offset;
}
}

View file

@ -1,90 +1,90 @@
#pragma once
#include <vector>
#include <string>
#include <ios>
namespace YAML
{
struct Stream;
enum REGEX_OP { REGEX_EMPTY, REGEX_MATCH, REGEX_RANGE, REGEX_OR, REGEX_AND, REGEX_NOT, REGEX_SEQ };
// simplified regular expressions
// . Only straightforward matches (no repeated characters)
// . Only matches from start of string
class RegEx
{
private:
struct Operator {
virtual ~Operator() {}
virtual int Match(const std::string& str, const RegEx& regex) const = 0;
virtual int Match(std::istream& in, const RegEx& regex) const = 0;
};
struct MatchOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct RangeOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct OrOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct AndOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct NotOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct SeqOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
public:
friend struct Operator;
RegEx();
RegEx(char ch);
RegEx(char a, char z);
RegEx(const std::string& str, REGEX_OP op = REGEX_SEQ);
RegEx(const RegEx& rhs);
~RegEx();
RegEx& operator = (const RegEx& rhs);
bool Matches(char ch) const;
bool Matches(const std::string& str) const;
bool Matches(std::istream& in) const;
bool Matches(Stream& in) const;
int Match(const std::string& str) const;
int Match(std::istream& in) const;
int Match(Stream& in) const;
friend RegEx operator ! (const RegEx& ex);
friend RegEx operator || (const RegEx& ex1, const RegEx& ex2);
friend RegEx operator && (const RegEx& ex1, const RegEx& ex2);
friend RegEx operator + (const RegEx& ex1, const RegEx& ex2);
private:
RegEx(REGEX_OP op);
void SetOp();
private:
REGEX_OP m_op;
Operator *m_pOp;
char m_a, m_z;
std::vector <RegEx> m_params;
};
}
#pragma once
#include <vector>
#include <string>
#include <ios>
namespace YAML
{
struct Stream;
enum REGEX_OP { REGEX_EMPTY, REGEX_MATCH, REGEX_RANGE, REGEX_OR, REGEX_AND, REGEX_NOT, REGEX_SEQ };
// simplified regular expressions
// . Only straightforward matches (no repeated characters)
// . Only matches from start of string
class RegEx
{
private:
struct Operator {
virtual ~Operator() {}
virtual int Match(const std::string& str, const RegEx& regex) const = 0;
virtual int Match(std::istream& in, const RegEx& regex) const = 0;
};
struct MatchOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct RangeOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct OrOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct AndOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct NotOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
struct SeqOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const;
};
public:
friend struct Operator;
RegEx();
RegEx(char ch);
RegEx(char a, char z);
RegEx(const std::string& str, REGEX_OP op = REGEX_SEQ);
RegEx(const RegEx& rhs);
~RegEx();
RegEx& operator = (const RegEx& rhs);
bool Matches(char ch) const;
bool Matches(const std::string& str) const;
bool Matches(std::istream& in) const;
bool Matches(Stream& in) const;
int Match(const std::string& str) const;
int Match(std::istream& in) const;
int Match(Stream& in) const;
friend RegEx operator ! (const RegEx& ex);
friend RegEx operator || (const RegEx& ex1, const RegEx& ex2);
friend RegEx operator && (const RegEx& ex1, const RegEx& ex2);
friend RegEx operator + (const RegEx& ex1, const RegEx& ex2);
private:
RegEx(REGEX_OP op);
void SetOp();
private:
REGEX_OP m_op;
Operator *m_pOp;
char m_a, m_z;
std::vector <RegEx> m_params;
};
}

View file

@ -1,108 +1,108 @@
#include "crt.h"
#include "scalar.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "node.h"
#include <sstream>
namespace YAML
{
Scalar::Scalar()
{
}
Scalar::~Scalar()
{
}
void Scalar::Parse(Scanner *pScanner, const ParserState& state)
{
Token& token = pScanner->peek();
m_data = token.value;
pScanner->pop();
}
void Scalar::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine)
{
out << "\"";
for(unsigned i=0;i<m_data.size();i++) {
switch(m_data[i]) {
case '\\': out << "\\\\"; break;
case '\t': out << "\\t"; break;
case '\n': out << "\\n"; break;
case '\r': out << "\\r"; break;
default: out << m_data[i]; break;
}
}
out << "\"\n";
}
void Scalar::Read(std::string& s)
{
s = m_data;
}
void Scalar::Read(int& i)
{
std::stringstream data(m_data);
data >> i;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(unsigned& u)
{
std::stringstream data(m_data);
data >> u;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(long& l)
{
std::stringstream data(m_data);
data >> l;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(float& f)
{
std::stringstream data(m_data);
data >> f;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(double& d)
{
std::stringstream data(m_data);
data >> d;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(char& c)
{
std::stringstream data(m_data);
data >> c;
if(!data)
throw InvalidScalar();
}
int Scalar::Compare(Content *pContent)
{
return -pContent->Compare(this);
}
int Scalar::Compare(Scalar *pScalar)
{
if(m_data < pScalar->m_data)
return -1;
else if(m_data > pScalar->m_data)
return 1;
else
return 0;
}
}
#include "crt.h"
#include "scalar.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "node.h"
#include <sstream>
namespace YAML
{
Scalar::Scalar()
{
}
Scalar::~Scalar()
{
}
void Scalar::Parse(Scanner *pScanner, const ParserState& state)
{
Token& token = pScanner->peek();
m_data = token.value;
pScanner->pop();
}
void Scalar::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine)
{
out << "\"";
for(unsigned i=0;i<m_data.size();i++) {
switch(m_data[i]) {
case '\\': out << "\\\\"; break;
case '\t': out << "\\t"; break;
case '\n': out << "\\n"; break;
case '\r': out << "\\r"; break;
default: out << m_data[i]; break;
}
}
out << "\"\n";
}
void Scalar::Read(std::string& s)
{
s = m_data;
}
void Scalar::Read(int& i)
{
std::stringstream data(m_data);
data >> i;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(unsigned& u)
{
std::stringstream data(m_data);
data >> u;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(long& l)
{
std::stringstream data(m_data);
data >> l;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(float& f)
{
std::stringstream data(m_data);
data >> f;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(double& d)
{
std::stringstream data(m_data);
data >> d;
if(!data)
throw InvalidScalar();
}
void Scalar::Read(char& c)
{
std::stringstream data(m_data);
data >> c;
if(!data)
throw InvalidScalar();
}
int Scalar::Compare(Content *pContent)
{
return -pContent->Compare(this);
}
int Scalar::Compare(Scalar *pScalar)
{
if(m_data < pScalar->m_data)
return -1;
else if(m_data > pScalar->m_data)
return 1;
else
return 0;
}
}

View file

@ -1,37 +1,37 @@
#pragma once
#include "content.h"
#include <string>
namespace YAML
{
class Scalar: public Content
{
public:
Scalar();
virtual ~Scalar();
virtual void Parse(Scanner *pScanner, const ParserState& state);
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine);
virtual bool IsScalar() const { return true; }
// extraction
virtual void Read(std::string& s);
virtual void Read(int& i);
virtual void Read(unsigned& u);
virtual void Read(long& l);
virtual void Read(float& f);
virtual void Read(double& d);
virtual void Read(char& c);
// ordering
virtual int Compare(Content *pContent);
virtual int Compare(Scalar *pScalar);
virtual int Compare(Sequence *pSeq) { return -1; }
virtual int Compare(Map *pMap) { return -1; }
protected:
std::string m_data;
};
}
#pragma once
#include "content.h"
#include <string>
namespace YAML
{
class Scalar: public Content
{
public:
Scalar();
virtual ~Scalar();
virtual void Parse(Scanner *pScanner, const ParserState& state);
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine);
virtual bool IsScalar() const { return true; }
// extraction
virtual void Read(std::string& s);
virtual void Read(int& i);
virtual void Read(unsigned& u);
virtual void Read(long& l);
virtual void Read(float& f);
virtual void Read(double& d);
virtual void Read(char& c);
// ordering
virtual int Compare(Content *pContent);
virtual int Compare(Scalar *pScalar);
virtual int Compare(Sequence *pSeq) { return -1; }
virtual int Compare(Map *pMap) { return -1; }
protected:
std::string m_data;
};
}

View file

@ -1,276 +1,276 @@
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
#include <cassert>
namespace YAML
{
Scanner::Scanner(std::istream& in)
: INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), m_flowLevel(0)
{
}
Scanner::~Scanner()
{
}
// empty
// . Returns true if there are no more tokens to be read
bool Scanner::empty()
{
EnsureTokensInQueue();
return m_tokens.empty();
}
// pop
// . Simply removes the next token on the queue.
void Scanner::pop()
{
EnsureTokensInQueue();
if(!m_tokens.empty())
m_tokens.pop();
}
// peek
// . Returns (but does not remove) the next token on the queue.
Token& Scanner::peek()
{
EnsureTokensInQueue();
assert(!m_tokens.empty()); // should we be asserting here? I mean, we really just be checking
// if it's empty before peeking.
return m_tokens.front();
}
// EnsureTokensInQueue
// . Scan until there's a valid token at the front of the queue,
// or we're sure the queue is empty.
void Scanner::EnsureTokensInQueue()
{
while(1) {
if(!m_tokens.empty()) {
Token& token = m_tokens.front();
// if this guy's valid, then we're done
if(token.status == TS_VALID)
return;
// here's where we clean up the impossible tokens
if(token.status == TS_INVALID) {
m_tokens.pop();
continue;
}
// note: what's left are the unverified tokens
}
// no token? maybe we've actually finished
if(m_endedStream)
return;
// no? then scan...
ScanNextToken();
}
}
// ScanNextToken
// . The main scanning function; here we branch out and
// scan whatever the next token should be.
void Scanner::ScanNextToken()
{
if(m_endedStream)
return;
if(!m_startedStream)
return StartStream();
// get rid of whitespace, etc. (in between tokens it should be irrelevent)
ScanToNextToken();
// check the latest simple key
VerifySimpleKey();
// maybe need to end some blocks
PopIndentTo(INPUT.column);
// *****
// And now branch based on the next few characters!
// *****
// end of stream
if(INPUT.peek() == EOF)
return EndStream();
if(INPUT.column == 0 && INPUT.peek() == Keys::Directive)
return ScanDirective();
// document token
if(INPUT.column == 0 && Exp::DocStart.Matches(INPUT))
return ScanDocStart();
if(INPUT.column == 0 && Exp::DocEnd.Matches(INPUT))
return ScanDocEnd();
// flow start/end/entry
if(INPUT.peek() == Keys::FlowSeqStart || INPUT.peek() == Keys::FlowMapStart)
return ScanFlowStart();
if(INPUT.peek() == Keys::FlowSeqEnd || INPUT.peek() == Keys::FlowMapEnd)
return ScanFlowEnd();
if(INPUT.peek() == Keys::FlowEntry)
return ScanFlowEntry();
// block/map stuff
if(Exp::BlockEntry.Matches(INPUT))
return ScanBlockEntry();
if((m_flowLevel == 0 ? Exp::Key : Exp::KeyInFlow).Matches(INPUT))
return ScanKey();
if((m_flowLevel == 0 ? Exp::Value : Exp::ValueInFlow).Matches(INPUT))
return ScanValue();
// alias/anchor
if(INPUT.peek() == Keys::Alias || INPUT.peek() == Keys::Anchor)
return ScanAnchorOrAlias();
// tag
if(INPUT.peek() == Keys::Tag)
return ScanTag();
// special scalars
if(m_flowLevel == 0 && (INPUT.peek() == Keys::LiteralScalar || INPUT.peek() == Keys::FoldedScalar))
return ScanBlockScalar();
if(INPUT.peek() == '\'' || INPUT.peek() == '\"')
return ScanQuotedScalar();
// plain scalars
if((m_flowLevel == 0 ? Exp::PlainScalar : Exp::PlainScalarInFlow).Matches(INPUT))
return ScanPlainScalar();
// don't know what it is!
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::UNKNOWN_TOKEN);
}
// ScanToNextToken
// . Eats input until we reach the next token-like thing.
void Scanner::ScanToNextToken()
{
while(1) {
// first eat whitespace
while(IsWhitespaceToBeEaten(INPUT.peek()))
INPUT.eat(1);
// then eat a comment
if(Exp::Comment.Matches(INPUT)) {
// eat until line break
while(INPUT && !Exp::Break.Matches(INPUT))
INPUT.eat(1);
}
// if it's NOT a line break, then we're done!
if(!Exp::Break.Matches(INPUT))
break;
// otherwise, let's eat the line break and keep going
int n = Exp::Break.Match(INPUT);
INPUT.eat(n);
// oh yeah, and let's get rid of that simple key
VerifySimpleKey();
// new line - we may be able to accept a simple key now
if(m_flowLevel == 0)
m_simpleKeyAllowed = true;
}
}
///////////////////////////////////////////////////////////////////////
// Misc. helpers
// IsWhitespaceToBeEaten
// . We can eat whitespace if:
// 1. It's a space
// 2. It's a tab, and we're either:
// a. In the flow context
// b. In the block context but not where a simple key could be allowed
// (i.e., not at the beginning of a line, or following '-', '?', or ':')
bool Scanner::IsWhitespaceToBeEaten(char ch)
{
if(ch == ' ')
return true;
if(ch == '\t' && (m_flowLevel >= 0 || !m_simpleKeyAllowed))
return true;
return false;
}
// StartStream
// . Set the initial conditions for starting a stream.
void Scanner::StartStream()
{
m_startedStream = true;
m_simpleKeyAllowed = true;
m_indents.push(-1);
}
// EndStream
// . Close out the stream, finish up, etc.
void Scanner::EndStream()
{
// force newline
if(INPUT.column > 0)
INPUT.column = 0;
PopIndentTo(-1);
VerifyAllSimpleKeys();
m_simpleKeyAllowed = false;
m_endedStream = true;
}
// PushIndentTo
// . Pushes an indentation onto the stack, and enqueues the
// proper token (sequence start or mapping start).
// . Returns the token it generates (if any).
Token *Scanner::PushIndentTo(int column, bool sequence)
{
// are we in flow?
if(m_flowLevel > 0)
return 0;
// is this actually an indentation?
if(column <= m_indents.top())
return 0;
// now push
m_indents.push(column);
if(sequence)
m_tokens.push(Token(TT_BLOCK_SEQ_START, INPUT.line, INPUT.column));
else
m_tokens.push(Token(TT_BLOCK_MAP_START, INPUT.line, INPUT.column));
return &m_tokens.back();
}
// PopIndentTo
// . Pops indentations off the stack until we reach 'column' indentation,
// and enqueues the proper token each time.
void Scanner::PopIndentTo(int column)
{
// are we in flow?
if(m_flowLevel > 0)
return;
// now pop away
while(!m_indents.empty() && m_indents.top() > column) {
m_indents.pop();
m_tokens.push(Token(TT_BLOCK_END, INPUT.line, INPUT.column));
}
}
}
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
#include <cassert>
namespace YAML
{
Scanner::Scanner(std::istream& in)
: INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), m_flowLevel(0)
{
}
Scanner::~Scanner()
{
}
// empty
// . Returns true if there are no more tokens to be read
bool Scanner::empty()
{
EnsureTokensInQueue();
return m_tokens.empty();
}
// pop
// . Simply removes the next token on the queue.
void Scanner::pop()
{
EnsureTokensInQueue();
if(!m_tokens.empty())
m_tokens.pop();
}
// peek
// . Returns (but does not remove) the next token on the queue.
Token& Scanner::peek()
{
EnsureTokensInQueue();
assert(!m_tokens.empty()); // should we be asserting here? I mean, we really just be checking
// if it's empty before peeking.
return m_tokens.front();
}
// EnsureTokensInQueue
// . Scan until there's a valid token at the front of the queue,
// or we're sure the queue is empty.
void Scanner::EnsureTokensInQueue()
{
while(1) {
if(!m_tokens.empty()) {
Token& token = m_tokens.front();
// if this guy's valid, then we're done
if(token.status == TS_VALID)
return;
// here's where we clean up the impossible tokens
if(token.status == TS_INVALID) {
m_tokens.pop();
continue;
}
// note: what's left are the unverified tokens
}
// no token? maybe we've actually finished
if(m_endedStream)
return;
// no? then scan...
ScanNextToken();
}
}
// ScanNextToken
// . The main scanning function; here we branch out and
// scan whatever the next token should be.
void Scanner::ScanNextToken()
{
if(m_endedStream)
return;
if(!m_startedStream)
return StartStream();
// get rid of whitespace, etc. (in between tokens it should be irrelevent)
ScanToNextToken();
// check the latest simple key
VerifySimpleKey();
// maybe need to end some blocks
PopIndentTo(INPUT.column);
// *****
// And now branch based on the next few characters!
// *****
// end of stream
if(INPUT.peek() == EOF)
return EndStream();
if(INPUT.column == 0 && INPUT.peek() == Keys::Directive)
return ScanDirective();
// document token
if(INPUT.column == 0 && Exp::DocStart.Matches(INPUT))
return ScanDocStart();
if(INPUT.column == 0 && Exp::DocEnd.Matches(INPUT))
return ScanDocEnd();
// flow start/end/entry
if(INPUT.peek() == Keys::FlowSeqStart || INPUT.peek() == Keys::FlowMapStart)
return ScanFlowStart();
if(INPUT.peek() == Keys::FlowSeqEnd || INPUT.peek() == Keys::FlowMapEnd)
return ScanFlowEnd();
if(INPUT.peek() == Keys::FlowEntry)
return ScanFlowEntry();
// block/map stuff
if(Exp::BlockEntry.Matches(INPUT))
return ScanBlockEntry();
if((m_flowLevel == 0 ? Exp::Key : Exp::KeyInFlow).Matches(INPUT))
return ScanKey();
if((m_flowLevel == 0 ? Exp::Value : Exp::ValueInFlow).Matches(INPUT))
return ScanValue();
// alias/anchor
if(INPUT.peek() == Keys::Alias || INPUT.peek() == Keys::Anchor)
return ScanAnchorOrAlias();
// tag
if(INPUT.peek() == Keys::Tag)
return ScanTag();
// special scalars
if(m_flowLevel == 0 && (INPUT.peek() == Keys::LiteralScalar || INPUT.peek() == Keys::FoldedScalar))
return ScanBlockScalar();
if(INPUT.peek() == '\'' || INPUT.peek() == '\"')
return ScanQuotedScalar();
// plain scalars
if((m_flowLevel == 0 ? Exp::PlainScalar : Exp::PlainScalarInFlow).Matches(INPUT))
return ScanPlainScalar();
// don't know what it is!
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::UNKNOWN_TOKEN);
}
// ScanToNextToken
// . Eats input until we reach the next token-like thing.
void Scanner::ScanToNextToken()
{
while(1) {
// first eat whitespace
while(IsWhitespaceToBeEaten(INPUT.peek()))
INPUT.eat(1);
// then eat a comment
if(Exp::Comment.Matches(INPUT)) {
// eat until line break
while(INPUT && !Exp::Break.Matches(INPUT))
INPUT.eat(1);
}
// if it's NOT a line break, then we're done!
if(!Exp::Break.Matches(INPUT))
break;
// otherwise, let's eat the line break and keep going
int n = Exp::Break.Match(INPUT);
INPUT.eat(n);
// oh yeah, and let's get rid of that simple key
VerifySimpleKey();
// new line - we may be able to accept a simple key now
if(m_flowLevel == 0)
m_simpleKeyAllowed = true;
}
}
///////////////////////////////////////////////////////////////////////
// Misc. helpers
// IsWhitespaceToBeEaten
// . We can eat whitespace if:
// 1. It's a space
// 2. It's a tab, and we're either:
// a. In the flow context
// b. In the block context but not where a simple key could be allowed
// (i.e., not at the beginning of a line, or following '-', '?', or ':')
bool Scanner::IsWhitespaceToBeEaten(char ch)
{
if(ch == ' ')
return true;
if(ch == '\t' && (m_flowLevel >= 0 || !m_simpleKeyAllowed))
return true;
return false;
}
// StartStream
// . Set the initial conditions for starting a stream.
void Scanner::StartStream()
{
m_startedStream = true;
m_simpleKeyAllowed = true;
m_indents.push(-1);
}
// EndStream
// . Close out the stream, finish up, etc.
void Scanner::EndStream()
{
// force newline
if(INPUT.column > 0)
INPUT.column = 0;
PopIndentTo(-1);
VerifyAllSimpleKeys();
m_simpleKeyAllowed = false;
m_endedStream = true;
}
// PushIndentTo
// . Pushes an indentation onto the stack, and enqueues the
// proper token (sequence start or mapping start).
// . Returns the token it generates (if any).
Token *Scanner::PushIndentTo(int column, bool sequence)
{
// are we in flow?
if(m_flowLevel > 0)
return 0;
// is this actually an indentation?
if(column <= m_indents.top())
return 0;
// now push
m_indents.push(column);
if(sequence)
m_tokens.push(Token(TT_BLOCK_SEQ_START, INPUT.line, INPUT.column));
else
m_tokens.push(Token(TT_BLOCK_MAP_START, INPUT.line, INPUT.column));
return &m_tokens.back();
}
// PopIndentTo
// . Pops indentations off the stack until we reach 'column' indentation,
// and enqueues the proper token each time.
void Scanner::PopIndentTo(int column)
{
// are we in flow?
if(m_flowLevel > 0)
return;
// now pop away
while(!m_indents.empty() && m_indents.top() > column) {
m_indents.pop();
m_tokens.push(Token(TT_BLOCK_END, INPUT.line, INPUT.column));
}
}
}

View file

@ -1,85 +1,85 @@
#pragma once
#include <ios>
#include <string>
#include <queue>
#include <stack>
#include <set>
#include "stream.h"
#include "token.h"
namespace YAML
{
class Scanner
{
public:
Scanner(std::istream& in);
~Scanner();
// token queue management (hopefully this looks kinda stl-ish)
bool empty();
void pop();
Token& peek();
private:
// scanning
void EnsureTokensInQueue();
void ScanNextToken();
void ScanToNextToken();
void StartStream();
void EndStream();
Token *PushIndentTo(int column, bool sequence);
void PopIndentTo(int column);
// checking input
void InsertSimpleKey();
bool VerifySimpleKey();
void VerifyAllSimpleKeys();
bool IsWhitespaceToBeEaten(char ch);
struct SimpleKey {
SimpleKey(int pos_, int line_, int column_, int flowLevel_);
void Validate();
void Invalidate();
int pos, line, column, flowLevel;
Token *pMapStart, *pKey;
};
// and the tokens
void ScanDirective();
void ScanDocStart();
void ScanDocEnd();
void ScanBlockSeqStart();
void ScanBlockMapSTart();
void ScanBlockEnd();
void ScanBlockEntry();
void ScanFlowStart();
void ScanFlowEnd();
void ScanFlowEntry();
void ScanKey();
void ScanValue();
void ScanAnchorOrAlias();
void ScanTag();
void ScanPlainScalar();
void ScanQuotedScalar();
void ScanBlockScalar();
private:
// the stream
Stream INPUT;
// the output (tokens)
std::queue <Token> m_tokens;
// state info
bool m_startedStream, m_endedStream;
bool m_simpleKeyAllowed;
int m_flowLevel; // number of unclosed '[' and '{' indicators
bool m_isLastKeyValid;
std::stack <SimpleKey> m_simpleKeys;
std::stack <int> m_indents;
};
}
#pragma once
#include <ios>
#include <string>
#include <queue>
#include <stack>
#include <set>
#include "stream.h"
#include "token.h"
namespace YAML
{
class Scanner
{
public:
Scanner(std::istream& in);
~Scanner();
// token queue management (hopefully this looks kinda stl-ish)
bool empty();
void pop();
Token& peek();
private:
// scanning
void EnsureTokensInQueue();
void ScanNextToken();
void ScanToNextToken();
void StartStream();
void EndStream();
Token *PushIndentTo(int column, bool sequence);
void PopIndentTo(int column);
// checking input
void InsertSimpleKey();
bool VerifySimpleKey();
void VerifyAllSimpleKeys();
bool IsWhitespaceToBeEaten(char ch);
struct SimpleKey {
SimpleKey(int pos_, int line_, int column_, int flowLevel_);
void Validate();
void Invalidate();
int pos, line, column, flowLevel;
Token *pMapStart, *pKey;
};
// and the tokens
void ScanDirective();
void ScanDocStart();
void ScanDocEnd();
void ScanBlockSeqStart();
void ScanBlockMapSTart();
void ScanBlockEnd();
void ScanBlockEntry();
void ScanFlowStart();
void ScanFlowEnd();
void ScanFlowEntry();
void ScanKey();
void ScanValue();
void ScanAnchorOrAlias();
void ScanTag();
void ScanPlainScalar();
void ScanQuotedScalar();
void ScanBlockScalar();
private:
// the stream
Stream INPUT;
// the output (tokens)
std::queue <Token> m_tokens;
// state info
bool m_startedStream, m_endedStream;
bool m_simpleKeyAllowed;
int m_flowLevel; // number of unclosed '[' and '{' indicators
bool m_isLastKeyValid;
std::stack <SimpleKey> m_simpleKeys;
std::stack <int> m_indents;
};
}

View file

@ -1,154 +1,154 @@
#include "crt.h"
#include "scanscalar.h"
#include "scanner.h"
#include "exp.h"
#include "exceptions.h"
#include "token.h"
namespace YAML
{
// ScanScalar
// . This is where the scalar magic happens.
//
// . We do the scanning in three phases:
// 1. Scan until newline
// 2. Eat newline
// 3. Scan leading blanks.
//
// . Depending on the parameters given, we store or stop
// and different places in the above flow.
std::string ScanScalar(Stream& INPUT, ScanScalarParams& params)
{
bool foundNonEmptyLine = false, pastOpeningBreak = false;
bool emptyLine = false, moreIndented = false;
std::string scalar;
params.leadingSpaces = false;
while(INPUT) {
// ********************************
// Phase #1: scan until line ending
while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) {
if(INPUT.peek() == EOF)
break;
// document indicator?
if(INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT)) {
if(params.onDocIndicator == BREAK)
break;
else if(params.onDocIndicator == THROW)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::DOC_IN_SCALAR);
}
foundNonEmptyLine = true;
pastOpeningBreak = true;
// escaped newline? (only if we're escaping on slash)
if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) {
int n = Exp::EscBreak.Match(INPUT);
INPUT.eat(n);
continue;
}
// escape this?
if(INPUT.peek() == params.escape) {
scalar += Exp::Escape(INPUT);
continue;
}
// otherwise, just add the damn character
scalar += INPUT.get();
}
// eof? if we're looking to eat something, then we throw
if(INPUT.peek() == EOF) {
if(params.eatEnd)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::EOF_IN_SCALAR);
break;
}
// doc indicator?
if(params.onDocIndicator == BREAK && INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT))
break;
// are we done via character match?
int n = params.end.Match(INPUT);
if(n >= 0) {
if(params.eatEnd)
INPUT.eat(n);
break;
}
// ********************************
// Phase #2: eat line ending
n = Exp::Break.Match(INPUT);
INPUT.eat(n);
// ********************************
// Phase #3: scan initial spaces
// first the required indentation
while(INPUT.peek() == ' ' && (INPUT.column < params.indent || (params.detectIndent && !foundNonEmptyLine)))
INPUT.eat(1);
// update indent if we're auto-detecting
if(params.detectIndent && !foundNonEmptyLine)
params.indent = std::max(params.indent, INPUT.column);
// and then the rest of the whitespace
while(Exp::Blank.Matches(INPUT)) {
// we check for tabs that masquerade as indentation
if(INPUT.peek() == '\t'&& INPUT.column < params.indent && params.onTabInIndentation == THROW)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::TAB_IN_INDENTATION);
if(!params.eatLeadingWhitespace)
break;
INPUT.eat(1);
}
// was this an empty line?
bool nextEmptyLine = Exp::Break.Matches(INPUT);
bool nextMoreIndented = (INPUT.peek() == ' ');
// for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
bool useNewLine = pastOpeningBreak;
// and for folded scalars, we don't fold the very last newline to a space
if(params.fold && !emptyLine && INPUT.column < params.indent)
useNewLine = false;
if(useNewLine) {
if(params.fold && !emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented)
scalar += " ";
else
scalar += "\n";
}
emptyLine = nextEmptyLine;
moreIndented = nextMoreIndented;
pastOpeningBreak = true;
// are we done via indentation?
if(!emptyLine && INPUT.column < params.indent) {
params.leadingSpaces = true;
break;
}
}
// post-processing
if(params.trimTrailingSpaces) {
unsigned pos = scalar.find_last_not_of(' ');
if(pos < scalar.size())
scalar.erase(pos + 1);
}
if(params.chomp <= 0) {
unsigned pos = scalar.find_last_not_of('\n');
if(params.chomp == 0 && pos + 1 < scalar.size())
scalar.erase(pos + 2);
else if(params.chomp == -1 && pos < scalar.size())
scalar.erase(pos + 1);
}
return scalar;
}
}
#include "crt.h"
#include "scanscalar.h"
#include "scanner.h"
#include "exp.h"
#include "exceptions.h"
#include "token.h"
namespace YAML
{
// ScanScalar
// . This is where the scalar magic happens.
//
// . We do the scanning in three phases:
// 1. Scan until newline
// 2. Eat newline
// 3. Scan leading blanks.
//
// . Depending on the parameters given, we store or stop
// and different places in the above flow.
std::string ScanScalar(Stream& INPUT, ScanScalarParams& params)
{
bool foundNonEmptyLine = false, pastOpeningBreak = false;
bool emptyLine = false, moreIndented = false;
std::string scalar;
params.leadingSpaces = false;
while(INPUT) {
// ********************************
// Phase #1: scan until line ending
while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) {
if(INPUT.peek() == EOF)
break;
// document indicator?
if(INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT)) {
if(params.onDocIndicator == BREAK)
break;
else if(params.onDocIndicator == THROW)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::DOC_IN_SCALAR);
}
foundNonEmptyLine = true;
pastOpeningBreak = true;
// escaped newline? (only if we're escaping on slash)
if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) {
int n = Exp::EscBreak.Match(INPUT);
INPUT.eat(n);
continue;
}
// escape this?
if(INPUT.peek() == params.escape) {
scalar += Exp::Escape(INPUT);
continue;
}
// otherwise, just add the damn character
scalar += INPUT.get();
}
// eof? if we're looking to eat something, then we throw
if(INPUT.peek() == EOF) {
if(params.eatEnd)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::EOF_IN_SCALAR);
break;
}
// doc indicator?
if(params.onDocIndicator == BREAK && INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT))
break;
// are we done via character match?
int n = params.end.Match(INPUT);
if(n >= 0) {
if(params.eatEnd)
INPUT.eat(n);
break;
}
// ********************************
// Phase #2: eat line ending
n = Exp::Break.Match(INPUT);
INPUT.eat(n);
// ********************************
// Phase #3: scan initial spaces
// first the required indentation
while(INPUT.peek() == ' ' && (INPUT.column < params.indent || (params.detectIndent && !foundNonEmptyLine)))
INPUT.eat(1);
// update indent if we're auto-detecting
if(params.detectIndent && !foundNonEmptyLine)
params.indent = std::max(params.indent, INPUT.column);
// and then the rest of the whitespace
while(Exp::Blank.Matches(INPUT)) {
// we check for tabs that masquerade as indentation
if(INPUT.peek() == '\t'&& INPUT.column < params.indent && params.onTabInIndentation == THROW)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::TAB_IN_INDENTATION);
if(!params.eatLeadingWhitespace)
break;
INPUT.eat(1);
}
// was this an empty line?
bool nextEmptyLine = Exp::Break.Matches(INPUT);
bool nextMoreIndented = (INPUT.peek() == ' ');
// for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
bool useNewLine = pastOpeningBreak;
// and for folded scalars, we don't fold the very last newline to a space
if(params.fold && !emptyLine && INPUT.column < params.indent)
useNewLine = false;
if(useNewLine) {
if(params.fold && !emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented)
scalar += " ";
else
scalar += "\n";
}
emptyLine = nextEmptyLine;
moreIndented = nextMoreIndented;
pastOpeningBreak = true;
// are we done via indentation?
if(!emptyLine && INPUT.column < params.indent) {
params.leadingSpaces = true;
break;
}
}
// post-processing
if(params.trimTrailingSpaces) {
unsigned pos = scalar.find_last_not_of(' ');
if(pos < scalar.size())
scalar.erase(pos + 1);
}
if(params.chomp <= 0) {
unsigned pos = scalar.find_last_not_of('\n');
if(params.chomp == 0 && pos + 1 < scalar.size())
scalar.erase(pos + 2);
else if(params.chomp == -1 && pos < scalar.size())
scalar.erase(pos + 1);
}
return scalar;
}
}

View file

@ -1,35 +1,35 @@
#pragma once
#include <string>
#include "regex.h"
#include "stream.h"
namespace YAML
{
enum CHOMP { STRIP = -1, CLIP, KEEP };
enum ACTION { NONE, BREAK, THROW };
struct ScanScalarParams {
ScanScalarParams(): eatEnd(false), indent(0), detectIndent(false), eatLeadingWhitespace(0), escape(0), fold(false),
trimTrailingSpaces(0), chomp(CLIP), onDocIndicator(NONE), onTabInIndentation(NONE), leadingSpaces(false) {}
// input:
RegEx end; // what condition ends this scalar?
bool eatEnd; // should we eat that condition when we see it?
int indent; // what level of indentation should be eaten and ignored?
bool detectIndent; // should we try to autodetect the indent?
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)
bool fold; // do we fold line ends?
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)
// Note: strip means kill all, clip means keep at most one, keep means keep all
ACTION onDocIndicator; // what do we do if we see a document indicator?
ACTION onTabInIndentation; // what do we do if we see a tab where we should be seeing indentation spaces
// output:
bool leadingSpaces;
};
std::string ScanScalar(Stream& INPUT, ScanScalarParams& info);
}
#pragma once
#include <string>
#include "regex.h"
#include "stream.h"
namespace YAML
{
enum CHOMP { STRIP = -1, CLIP, KEEP };
enum ACTION { NONE, BREAK, THROW };
struct ScanScalarParams {
ScanScalarParams(): eatEnd(false), indent(0), detectIndent(false), eatLeadingWhitespace(0), escape(0), fold(false),
trimTrailingSpaces(0), chomp(CLIP), onDocIndicator(NONE), onTabInIndentation(NONE), leadingSpaces(false) {}
// input:
RegEx end; // what condition ends this scalar?
bool eatEnd; // should we eat that condition when we see it?
int indent; // what level of indentation should be eaten and ignored?
bool detectIndent; // should we try to autodetect the indent?
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)
bool fold; // do we fold line ends?
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)
// Note: strip means kill all, clip means keep at most one, keep means keep all
ACTION onDocIndicator; // what do we do if we see a document indicator?
ACTION onTabInIndentation; // what do we do if we see a tab where we should be seeing indentation spaces
// output:
bool leadingSpaces;
};
std::string ScanScalar(Stream& INPUT, ScanScalarParams& info);
}

View file

@ -1,405 +1,405 @@
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
#include "scanscalar.h"
#include <sstream>
namespace YAML
{
///////////////////////////////////////////////////////////////////////
// Specialization for scanning specific tokens
// Directive
// . Note: no semantic checking is done here (that's for the parser to do)
void Scanner::ScanDirective()
{
std::string name;
std::vector <std::string> params;
// pop indents and simple keys
PopIndentTo(-1);
VerifyAllSimpleKeys();
m_simpleKeyAllowed = false;
// store pos and eat indicator
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
// read name
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
name += INPUT.get();
// read parameters
while(1) {
// first get rid of whitespace
while(Exp::Blank.Matches(INPUT))
INPUT.eat(1);
// break on newline or comment
if(INPUT.peek() == EOF || Exp::Break.Matches(INPUT) || Exp::Comment.Matches(INPUT))
break;
// now read parameter
std::string param;
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
param += INPUT.get();
params.push_back(param);
}
Token token(TT_DIRECTIVE, line, column);
token.value = name;
token.params = params;
m_tokens.push(token);
}
// DocStart
void Scanner::ScanDocStart()
{
PopIndentTo(INPUT.column);
VerifyAllSimpleKeys();
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(3);
m_tokens.push(Token(TT_DOC_START, line, column));
}
// DocEnd
void Scanner::ScanDocEnd()
{
PopIndentTo(-1);
VerifyAllSimpleKeys();
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(3);
m_tokens.push(Token(TT_DOC_END, line, column));
}
// FlowStart
void Scanner::ScanFlowStart()
{
// flows can be simple keys
InsertSimpleKey();
m_flowLevel++;
m_simpleKeyAllowed = true;
// eat
int line = INPUT.line, column = INPUT.column;
char ch = INPUT.get();
TOKEN_TYPE type = (ch == Keys::FlowSeqStart ? TT_FLOW_SEQ_START : TT_FLOW_MAP_START);
m_tokens.push(Token(type, line, column));
}
// FlowEnd
void Scanner::ScanFlowEnd()
{
if(m_flowLevel == 0)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::FLOW_END);
m_flowLevel--;
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
char ch = INPUT.get();
TOKEN_TYPE type = (ch == Keys::FlowSeqEnd ? TT_FLOW_SEQ_END : TT_FLOW_MAP_END);
m_tokens.push(Token(type, line, column));
}
// FlowEntry
void Scanner::ScanFlowEntry()
{
m_simpleKeyAllowed = true;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(Token(TT_FLOW_ENTRY, line, column));
}
// BlockEntry
void Scanner::ScanBlockEntry()
{
// we better be in the block context!
if(m_flowLevel > 0)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::BLOCK_ENTRY);
// can we put it here?
if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::BLOCK_ENTRY);
PushIndentTo(INPUT.column, true);
m_simpleKeyAllowed = true;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(Token(TT_BLOCK_ENTRY, line, column));
}
// Key
void Scanner::ScanKey()
{
// handle keys diffently in the block context (and manage indents)
if(m_flowLevel == 0) {
if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::MAP_KEY);
PushIndentTo(INPUT.column, false);
}
// can only put a simple key here if we're in block context
if(m_flowLevel == 0)
m_simpleKeyAllowed = true;
else
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(Token(TT_KEY, line, column));
}
// Value
void Scanner::ScanValue()
{
// does this follow a simple key?
if(m_isLastKeyValid) {
// can't follow a simple key with another simple key (dunno why, though - it seems fine)
m_simpleKeyAllowed = false;
} else {
// handle values diffently in the block context (and manage indents)
if(m_flowLevel == 0) {
if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::MAP_VALUE);
PushIndentTo(INPUT.column, false);
}
// can only put a simple key here if we're in block context
if(m_flowLevel == 0)
m_simpleKeyAllowed = true;
else
m_simpleKeyAllowed = false;
}
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(Token(TT_VALUE, line, column));
}
// AnchorOrAlias
void Scanner::ScanAnchorOrAlias()
{
bool alias;
std::string name;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertSimpleKey();
m_simpleKeyAllowed = false;
// eat the indicator
int line = INPUT.line, column = INPUT.column;
char indicator = INPUT.get();
alias = (indicator == Keys::Alias);
// now eat the content
while(Exp::AlphaNumeric.Matches(INPUT))
name += INPUT.get();
// we need to have read SOMETHING!
if(name.empty())
throw ParserException(INPUT.line, INPUT.column, alias ? ErrorMsg::ALIAS_NOT_FOUND : ErrorMsg::ANCHOR_NOT_FOUND);
// and needs to end correctly
if(INPUT.peek() != EOF && !Exp::AnchorEnd.Matches(INPUT))
throw ParserException(INPUT.line, INPUT.column, alias ? ErrorMsg::CHAR_IN_ALIAS : ErrorMsg::CHAR_IN_ANCHOR);
// and we're done
Token token(alias ? TT_ALIAS : TT_ANCHOR, line, column);
token.value = name;
m_tokens.push(token);
}
// Tag
void Scanner::ScanTag()
{
std::string handle, suffix;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertSimpleKey();
m_simpleKeyAllowed = false;
// eat the indicator
int line = INPUT.line, column = INPUT.column;
handle += INPUT.get();
// read the handle
while(INPUT.peek() != EOF && INPUT.peek() != Keys::Tag && !Exp::BlankOrBreak.Matches(INPUT))
handle += INPUT.get();
// is there a suffix?
if(INPUT.peek() == Keys::Tag) {
// eat the indicator
handle += INPUT.get();
// then read it
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
suffix += INPUT.get();
} else {
// this is a bit weird: we keep just the '!' as the handle and move the rest to the suffix
suffix = handle.substr(1);
handle = "!";
}
Token token(TT_TAG, line, column);
token.value = handle;
token.params.push_back(suffix);
m_tokens.push(token);
}
// PlainScalar
void Scanner::ScanPlainScalar()
{
std::string scalar;
// set up the scanning parameters
ScanScalarParams params;
params.end = (m_flowLevel > 0 ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment);
params.eatEnd = false;
params.indent = (m_flowLevel > 0 ? 0 : m_indents.top() + 1);
params.fold = true;
params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = true;
params.chomp = CLIP;
params.onDocIndicator = BREAK;
params.onTabInIndentation = THROW;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertSimpleKey();
int line = INPUT.line, column = INPUT.column;
scalar = ScanScalar(INPUT, params);
// can have a simple key only if we ended the scalar by starting a new line
m_simpleKeyAllowed = params.leadingSpaces;
// finally, check and see if we ended on an illegal character
//if(Exp::IllegalCharInScalar.Matches(INPUT))
// throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_SCALAR);
Token token(TT_SCALAR, line, column);
token.value = scalar;
m_tokens.push(token);
}
// QuotedScalar
void Scanner::ScanQuotedScalar()
{
std::string scalar;
// eat single or double quote
char quote = INPUT.get();
bool single = (quote == '\'');
// setup the scanning parameters
ScanScalarParams params;
params.end = (single ? RegEx(quote) && !Exp::EscSingleQuote : RegEx(quote));
params.eatEnd = true;
params.escape = (single ? '\'' : '\\');
params.indent = 0;
params.fold = true;
params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = false;
params.chomp = CLIP;
params.onDocIndicator = THROW;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertSimpleKey();
int line = INPUT.line, column = INPUT.column;
scalar = ScanScalar(INPUT, params);
m_simpleKeyAllowed = false;
Token token(TT_SCALAR, line, column);
token.value = scalar;
m_tokens.push(token);
}
// BlockScalarToken
// . These need a little extra processing beforehand.
// . We need to scan the line where the indicator is (this doesn't count as part of the scalar),
// and then we need to figure out what level of indentation we'll be using.
void Scanner::ScanBlockScalar()
{
std::string scalar;
ScanScalarParams params;
params.indent = 1;
params.detectIndent = true;
// eat block indicator ('|' or '>')
int line = INPUT.line, column = INPUT.column;
char indicator = INPUT.get();
params.fold = (indicator == Keys::FoldedScalar);
// eat chomping/indentation indicators
int n = Exp::Chomp.Match(INPUT);
for(int i=0;i<n;i++) {
char ch = INPUT.get();
if(ch == '+')
params.chomp = KEEP;
else if(ch == '-')
params.chomp = STRIP;
else if(Exp::Digit.Matches(ch)) {
if(ch == '0')
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::ZERO_INDENT_IN_BLOCK);
params.indent = ch - '0';
params.detectIndent = false;
}
}
// now eat whitespace
while(Exp::Blank.Matches(INPUT))
INPUT.eat(1);
// and comments to the end of the line
if(Exp::Comment.Matches(INPUT))
while(INPUT && !Exp::Break.Matches(INPUT))
INPUT.eat(1);
// if it's not a line break, then we ran into a bad character inline
if(INPUT && !Exp::Break.Matches(INPUT))
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_BLOCK);
// set the initial indentation
if(m_indents.top() >= 0)
params.indent += m_indents.top();
params.eatLeadingWhitespace = false;
params.trimTrailingSpaces = false;
params.onTabInIndentation = THROW;
scalar = ScanScalar(INPUT, params);
// simple keys always ok after block scalars (since we're gonna start a new line anyways)
m_simpleKeyAllowed = true;
Token token(TT_SCALAR, line, column);
token.value = scalar;
m_tokens.push(token);
}
}
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
#include "scanscalar.h"
#include <sstream>
namespace YAML
{
///////////////////////////////////////////////////////////////////////
// Specialization for scanning specific tokens
// Directive
// . Note: no semantic checking is done here (that's for the parser to do)
void Scanner::ScanDirective()
{
std::string name;
std::vector <std::string> params;
// pop indents and simple keys
PopIndentTo(-1);
VerifyAllSimpleKeys();
m_simpleKeyAllowed = false;
// store pos and eat indicator
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
// read name
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
name += INPUT.get();
// read parameters
while(1) {
// first get rid of whitespace
while(Exp::Blank.Matches(INPUT))
INPUT.eat(1);
// break on newline or comment
if(INPUT.peek() == EOF || Exp::Break.Matches(INPUT) || Exp::Comment.Matches(INPUT))
break;
// now read parameter
std::string param;
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
param += INPUT.get();
params.push_back(param);
}
Token token(TT_DIRECTIVE, line, column);
token.value = name;
token.params = params;
m_tokens.push(token);
}
// DocStart
void Scanner::ScanDocStart()
{
PopIndentTo(INPUT.column);
VerifyAllSimpleKeys();
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(3);
m_tokens.push(Token(TT_DOC_START, line, column));
}
// DocEnd
void Scanner::ScanDocEnd()
{
PopIndentTo(-1);
VerifyAllSimpleKeys();
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(3);
m_tokens.push(Token(TT_DOC_END, line, column));
}
// FlowStart
void Scanner::ScanFlowStart()
{
// flows can be simple keys
InsertSimpleKey();
m_flowLevel++;
m_simpleKeyAllowed = true;
// eat
int line = INPUT.line, column = INPUT.column;
char ch = INPUT.get();
TOKEN_TYPE type = (ch == Keys::FlowSeqStart ? TT_FLOW_SEQ_START : TT_FLOW_MAP_START);
m_tokens.push(Token(type, line, column));
}
// FlowEnd
void Scanner::ScanFlowEnd()
{
if(m_flowLevel == 0)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::FLOW_END);
m_flowLevel--;
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
char ch = INPUT.get();
TOKEN_TYPE type = (ch == Keys::FlowSeqEnd ? TT_FLOW_SEQ_END : TT_FLOW_MAP_END);
m_tokens.push(Token(type, line, column));
}
// FlowEntry
void Scanner::ScanFlowEntry()
{
m_simpleKeyAllowed = true;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(Token(TT_FLOW_ENTRY, line, column));
}
// BlockEntry
void Scanner::ScanBlockEntry()
{
// we better be in the block context!
if(m_flowLevel > 0)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::BLOCK_ENTRY);
// can we put it here?
if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::BLOCK_ENTRY);
PushIndentTo(INPUT.column, true);
m_simpleKeyAllowed = true;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(Token(TT_BLOCK_ENTRY, line, column));
}
// Key
void Scanner::ScanKey()
{
// handle keys diffently in the block context (and manage indents)
if(m_flowLevel == 0) {
if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::MAP_KEY);
PushIndentTo(INPUT.column, false);
}
// can only put a simple key here if we're in block context
if(m_flowLevel == 0)
m_simpleKeyAllowed = true;
else
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(Token(TT_KEY, line, column));
}
// Value
void Scanner::ScanValue()
{
// does this follow a simple key?
if(m_isLastKeyValid) {
// can't follow a simple key with another simple key (dunno why, though - it seems fine)
m_simpleKeyAllowed = false;
} else {
// handle values diffently in the block context (and manage indents)
if(m_flowLevel == 0) {
if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::MAP_VALUE);
PushIndentTo(INPUT.column, false);
}
// can only put a simple key here if we're in block context
if(m_flowLevel == 0)
m_simpleKeyAllowed = true;
else
m_simpleKeyAllowed = false;
}
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(Token(TT_VALUE, line, column));
}
// AnchorOrAlias
void Scanner::ScanAnchorOrAlias()
{
bool alias;
std::string name;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertSimpleKey();
m_simpleKeyAllowed = false;
// eat the indicator
int line = INPUT.line, column = INPUT.column;
char indicator = INPUT.get();
alias = (indicator == Keys::Alias);
// now eat the content
while(Exp::AlphaNumeric.Matches(INPUT))
name += INPUT.get();
// we need to have read SOMETHING!
if(name.empty())
throw ParserException(INPUT.line, INPUT.column, alias ? ErrorMsg::ALIAS_NOT_FOUND : ErrorMsg::ANCHOR_NOT_FOUND);
// and needs to end correctly
if(INPUT.peek() != EOF && !Exp::AnchorEnd.Matches(INPUT))
throw ParserException(INPUT.line, INPUT.column, alias ? ErrorMsg::CHAR_IN_ALIAS : ErrorMsg::CHAR_IN_ANCHOR);
// and we're done
Token token(alias ? TT_ALIAS : TT_ANCHOR, line, column);
token.value = name;
m_tokens.push(token);
}
// Tag
void Scanner::ScanTag()
{
std::string handle, suffix;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertSimpleKey();
m_simpleKeyAllowed = false;
// eat the indicator
int line = INPUT.line, column = INPUT.column;
handle += INPUT.get();
// read the handle
while(INPUT.peek() != EOF && INPUT.peek() != Keys::Tag && !Exp::BlankOrBreak.Matches(INPUT))
handle += INPUT.get();
// is there a suffix?
if(INPUT.peek() == Keys::Tag) {
// eat the indicator
handle += INPUT.get();
// then read it
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
suffix += INPUT.get();
} else {
// this is a bit weird: we keep just the '!' as the handle and move the rest to the suffix
suffix = handle.substr(1);
handle = "!";
}
Token token(TT_TAG, line, column);
token.value = handle;
token.params.push_back(suffix);
m_tokens.push(token);
}
// PlainScalar
void Scanner::ScanPlainScalar()
{
std::string scalar;
// set up the scanning parameters
ScanScalarParams params;
params.end = (m_flowLevel > 0 ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment);
params.eatEnd = false;
params.indent = (m_flowLevel > 0 ? 0 : m_indents.top() + 1);
params.fold = true;
params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = true;
params.chomp = CLIP;
params.onDocIndicator = BREAK;
params.onTabInIndentation = THROW;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertSimpleKey();
int line = INPUT.line, column = INPUT.column;
scalar = ScanScalar(INPUT, params);
// can have a simple key only if we ended the scalar by starting a new line
m_simpleKeyAllowed = params.leadingSpaces;
// finally, check and see if we ended on an illegal character
//if(Exp::IllegalCharInScalar.Matches(INPUT))
// throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_SCALAR);
Token token(TT_SCALAR, line, column);
token.value = scalar;
m_tokens.push(token);
}
// QuotedScalar
void Scanner::ScanQuotedScalar()
{
std::string scalar;
// eat single or double quote
char quote = INPUT.get();
bool single = (quote == '\'');
// setup the scanning parameters
ScanScalarParams params;
params.end = (single ? RegEx(quote) && !Exp::EscSingleQuote : RegEx(quote));
params.eatEnd = true;
params.escape = (single ? '\'' : '\\');
params.indent = 0;
params.fold = true;
params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = false;
params.chomp = CLIP;
params.onDocIndicator = THROW;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertSimpleKey();
int line = INPUT.line, column = INPUT.column;
scalar = ScanScalar(INPUT, params);
m_simpleKeyAllowed = false;
Token token(TT_SCALAR, line, column);
token.value = scalar;
m_tokens.push(token);
}
// BlockScalarToken
// . These need a little extra processing beforehand.
// . We need to scan the line where the indicator is (this doesn't count as part of the scalar),
// and then we need to figure out what level of indentation we'll be using.
void Scanner::ScanBlockScalar()
{
std::string scalar;
ScanScalarParams params;
params.indent = 1;
params.detectIndent = true;
// eat block indicator ('|' or '>')
int line = INPUT.line, column = INPUT.column;
char indicator = INPUT.get();
params.fold = (indicator == Keys::FoldedScalar);
// eat chomping/indentation indicators
int n = Exp::Chomp.Match(INPUT);
for(int i=0;i<n;i++) {
char ch = INPUT.get();
if(ch == '+')
params.chomp = KEEP;
else if(ch == '-')
params.chomp = STRIP;
else if(Exp::Digit.Matches(ch)) {
if(ch == '0')
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::ZERO_INDENT_IN_BLOCK);
params.indent = ch - '0';
params.detectIndent = false;
}
}
// now eat whitespace
while(Exp::Blank.Matches(INPUT))
INPUT.eat(1);
// and comments to the end of the line
if(Exp::Comment.Matches(INPUT))
while(INPUT && !Exp::Break.Matches(INPUT))
INPUT.eat(1);
// if it's not a line break, then we ran into a bad character inline
if(INPUT && !Exp::Break.Matches(INPUT))
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_BLOCK);
// set the initial indentation
if(m_indents.top() >= 0)
params.indent += m_indents.top();
params.eatLeadingWhitespace = false;
params.trimTrailingSpaces = false;
params.onTabInIndentation = THROW;
scalar = ScanScalar(INPUT, params);
// simple keys always ok after block scalars (since we're gonna start a new line anyways)
m_simpleKeyAllowed = true;
Token token(TT_SCALAR, line, column);
token.value = scalar;
m_tokens.push(token);
}
}

View file

@ -1,175 +1,175 @@
#include "crt.h"
#include "sequence.h"
#include "node.h"
#include "scanner.h"
#include "token.h"
#include <iostream>
namespace YAML
{
Sequence::Sequence()
{
}
Sequence::~Sequence()
{
Clear();
}
void Sequence::Clear()
{
for(unsigned i=0;i<m_data.size();i++)
delete m_data[i];
m_data.clear();
}
bool Sequence::GetBegin(std::vector <Node *>::const_iterator& it) const
{
it = m_data.begin();
return true;
}
bool Sequence::GetEnd(std::vector <Node *>::const_iterator& it) const
{
it = m_data.end();
return true;
}
Node *Sequence::GetNode(unsigned i) const
{
if(i < m_data.size())
return m_data[i];
return 0;
}
unsigned Sequence::GetSize() const
{
return m_data.size();
}
void Sequence::Parse(Scanner *pScanner, const ParserState& state)
{
Clear();
// split based on start token
switch(pScanner->peek().type) {
case TT_BLOCK_SEQ_START: ParseBlock(pScanner, state); break;
case TT_BLOCK_ENTRY: ParseImplicit(pScanner, state); break;
case TT_FLOW_SEQ_START: ParseFlow(pScanner, state); break;
}
}
void Sequence::ParseBlock(Scanner *pScanner, const ParserState& state)
{
// eat start token
pScanner->pop();
while(1) {
if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_SEQ);
Token token = pScanner->peek();
if(token.type != TT_BLOCK_ENTRY && token.type != TT_BLOCK_END)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_SEQ);
pScanner->pop();
if(token.type == TT_BLOCK_END)
break;
Node *pNode = new Node;
m_data.push_back(pNode);
pNode->Parse(pScanner, state);
}
}
void Sequence::ParseImplicit(Scanner *pScanner, const ParserState& state)
{
while(1) {
// we're actually *allowed* to have no tokens at some point
if(pScanner->empty())
break;
// and we end at anything other than a block entry
Token& token = pScanner->peek();
if(token.type != TT_BLOCK_ENTRY)
break;
pScanner->pop();
Node *pNode = new Node;
m_data.push_back(pNode);
pNode->Parse(pScanner, state);
}
}
void Sequence::ParseFlow(Scanner *pScanner, const ParserState& state)
{
// eat start token
pScanner->pop();
while(1) {
if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_SEQ_FLOW);
// first check for end
if(pScanner->peek().type == TT_FLOW_SEQ_END) {
pScanner->pop();
break;
}
// then read the node
Node *pNode = new Node;
m_data.push_back(pNode);
pNode->Parse(pScanner, state);
// now eat the separator (or could be a sequence end, which we ignore - but if it's neither, then it's a bad node)
Token& token = pScanner->peek();
if(token.type == TT_FLOW_ENTRY)
pScanner->pop();
else if(token.type != TT_FLOW_SEQ_END)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_SEQ_FLOW);
}
}
void Sequence::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine)
{
if(startedLine && !onlyOneCharOnLine)
out << "\n";
for(unsigned i=0;i<m_data.size();i++) {
if((startedLine && !onlyOneCharOnLine) || i > 0) {
for(int j=0;j<indent;j++)
out << " ";
}
out << "- ";
m_data[i]->Write(out, indent + 1, true, i > 0 || !startedLine || onlyOneCharOnLine);
}
if(m_data.empty())
out << "\n";
}
int Sequence::Compare(Content *pContent)
{
return -pContent->Compare(this);
}
int Sequence::Compare(Sequence *pSeq)
{
unsigned n = m_data.size(), m = pSeq->m_data.size();
if(n < m)
return -1;
else if(n > m)
return 1;
for(unsigned i=0;i<n;i++) {
int cmp = m_data[i]->Compare(*pSeq->m_data[i]);
if(cmp != 0)
return cmp;
}
return 0;
}
}
#include "crt.h"
#include "sequence.h"
#include "node.h"
#include "scanner.h"
#include "token.h"
#include <iostream>
namespace YAML
{
Sequence::Sequence()
{
}
Sequence::~Sequence()
{
Clear();
}
void Sequence::Clear()
{
for(unsigned i=0;i<m_data.size();i++)
delete m_data[i];
m_data.clear();
}
bool Sequence::GetBegin(std::vector <Node *>::const_iterator& it) const
{
it = m_data.begin();
return true;
}
bool Sequence::GetEnd(std::vector <Node *>::const_iterator& it) const
{
it = m_data.end();
return true;
}
Node *Sequence::GetNode(unsigned i) const
{
if(i < m_data.size())
return m_data[i];
return 0;
}
unsigned Sequence::GetSize() const
{
return m_data.size();
}
void Sequence::Parse(Scanner *pScanner, const ParserState& state)
{
Clear();
// split based on start token
switch(pScanner->peek().type) {
case TT_BLOCK_SEQ_START: ParseBlock(pScanner, state); break;
case TT_BLOCK_ENTRY: ParseImplicit(pScanner, state); break;
case TT_FLOW_SEQ_START: ParseFlow(pScanner, state); break;
}
}
void Sequence::ParseBlock(Scanner *pScanner, const ParserState& state)
{
// eat start token
pScanner->pop();
while(1) {
if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_SEQ);
Token token = pScanner->peek();
if(token.type != TT_BLOCK_ENTRY && token.type != TT_BLOCK_END)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_SEQ);
pScanner->pop();
if(token.type == TT_BLOCK_END)
break;
Node *pNode = new Node;
m_data.push_back(pNode);
pNode->Parse(pScanner, state);
}
}
void Sequence::ParseImplicit(Scanner *pScanner, const ParserState& state)
{
while(1) {
// we're actually *allowed* to have no tokens at some point
if(pScanner->empty())
break;
// and we end at anything other than a block entry
Token& token = pScanner->peek();
if(token.type != TT_BLOCK_ENTRY)
break;
pScanner->pop();
Node *pNode = new Node;
m_data.push_back(pNode);
pNode->Parse(pScanner, state);
}
}
void Sequence::ParseFlow(Scanner *pScanner, const ParserState& state)
{
// eat start token
pScanner->pop();
while(1) {
if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_SEQ_FLOW);
// first check for end
if(pScanner->peek().type == TT_FLOW_SEQ_END) {
pScanner->pop();
break;
}
// then read the node
Node *pNode = new Node;
m_data.push_back(pNode);
pNode->Parse(pScanner, state);
// now eat the separator (or could be a sequence end, which we ignore - but if it's neither, then it's a bad node)
Token& token = pScanner->peek();
if(token.type == TT_FLOW_ENTRY)
pScanner->pop();
else if(token.type != TT_FLOW_SEQ_END)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_SEQ_FLOW);
}
}
void Sequence::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine)
{
if(startedLine && !onlyOneCharOnLine)
out << "\n";
for(unsigned i=0;i<m_data.size();i++) {
if((startedLine && !onlyOneCharOnLine) || i > 0) {
for(int j=0;j<indent;j++)
out << " ";
}
out << "- ";
m_data[i]->Write(out, indent + 1, true, i > 0 || !startedLine || onlyOneCharOnLine);
}
if(m_data.empty())
out << "\n";
}
int Sequence::Compare(Content *pContent)
{
return -pContent->Compare(this);
}
int Sequence::Compare(Sequence *pSeq)
{
unsigned n = m_data.size(), m = pSeq->m_data.size();
if(n < m)
return -1;
else if(n > m)
return 1;
for(unsigned i=0;i<n;i++) {
int cmp = m_data[i]->Compare(*pSeq->m_data[i]);
if(cmp != 0)
return cmp;
}
return 0;
}
}

View file

@ -1,41 +1,41 @@
#pragma once
#include "content.h"
#include <vector>
namespace YAML
{
class Node;
class Sequence: public Content
{
public:
Sequence();
virtual ~Sequence();
void Clear();
virtual bool GetBegin(std::vector <Node *>::const_iterator& it) const;
virtual bool GetEnd(std::vector <Node *>::const_iterator& it) const;
virtual Node *GetNode(unsigned i) const;
virtual unsigned GetSize() const;
virtual void Parse(Scanner *pScanner, const ParserState& state);
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine);
virtual bool IsSequence() const { return true; }
// ordering
virtual int Compare(Content *pContent);
virtual int Compare(Scalar *pScalar) { return 1; }
virtual int Compare(Sequence *pSeq);
virtual int Compare(Map *pMap) { return -1; }
private:
void ParseBlock(Scanner *pScanner, const ParserState& state);
void ParseImplicit(Scanner *pScanner, const ParserState& state);
void ParseFlow(Scanner *pScanner, const ParserState& state);
protected:
std::vector <Node *> m_data;
};
}
#pragma once
#include "content.h"
#include <vector>
namespace YAML
{
class Node;
class Sequence: public Content
{
public:
Sequence();
virtual ~Sequence();
void Clear();
virtual bool GetBegin(std::vector <Node *>::const_iterator& it) const;
virtual bool GetEnd(std::vector <Node *>::const_iterator& it) const;
virtual Node *GetNode(unsigned i) const;
virtual unsigned GetSize() const;
virtual void Parse(Scanner *pScanner, const ParserState& state);
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine);
virtual bool IsSequence() const { return true; }
// ordering
virtual int Compare(Content *pContent);
virtual int Compare(Scalar *pScalar) { return 1; }
virtual int Compare(Sequence *pSeq);
virtual int Compare(Map *pMap) { return -1; }
private:
void ParseBlock(Scanner *pScanner, const ParserState& state);
void ParseImplicit(Scanner *pScanner, const ParserState& state);
void ParseFlow(Scanner *pScanner, const ParserState& state);
protected:
std::vector <Node *> m_data;
};
}

View file

@ -1,102 +1,102 @@
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
namespace YAML
{
Scanner::SimpleKey::SimpleKey(int pos_, int line_, int column_, int flowLevel_)
: pos(pos_), line(line_), column(column_), flowLevel(flowLevel_), pMapStart(0), pKey(0)
{
}
void Scanner::SimpleKey::Validate()
{
if(pMapStart)
pMapStart->status = TS_VALID;
if(pKey)
pKey->status = TS_VALID;
}
void Scanner::SimpleKey::Invalidate()
{
if(pMapStart)
pMapStart->status = TS_INVALID;
if(pKey)
pKey->status = TS_INVALID;
}
// InsertSimpleKey
// . Adds a potential simple key to the queue,
// and saves it on a stack.
void Scanner::InsertSimpleKey()
{
SimpleKey key(INPUT.pos(), INPUT.line, INPUT.column, m_flowLevel);
// first add a map start, if necessary
key.pMapStart = PushIndentTo(INPUT.column, false);
if(key.pMapStart)
key.pMapStart->status = TS_UNVERIFIED;
// then add the (now unverified) key
m_tokens.push(Token(TT_KEY, INPUT.line, INPUT.column));
key.pKey = &m_tokens.back();
key.pKey->status = TS_UNVERIFIED;
m_simpleKeys.push(key);
}
// VerifySimpleKey
// . Determines whether the latest simple key to be added is valid,
// and if so, makes it valid.
bool Scanner::VerifySimpleKey()
{
m_isLastKeyValid = false;
if(m_simpleKeys.empty())
return m_isLastKeyValid;
// grab top key
SimpleKey key = m_simpleKeys.top();
// only validate if we're in the correct flow level
if(key.flowLevel != m_flowLevel)
return false;
m_simpleKeys.pop();
bool isValid = true;
// needs to be followed immediately by a value
if(m_flowLevel > 0 && !Exp::ValueInFlow.Matches(INPUT))
isValid = false;
if(m_flowLevel == 0 && !Exp::Value.Matches(INPUT))
isValid = false;
// also needs to be less than 1024 characters and inline
if(INPUT.line != key.line || INPUT.pos() - key.pos > 1024)
isValid = false;
// invalidate key
if(isValid)
key.Validate();
else
key.Invalidate();
// In block style, remember that we've pushed an indent for this potential simple key (if it was starting).
// If it was invalid, then we need to pop it off.
// Note: we're guaranteed to be popping the right one (i.e., there couldn't have been anything in
// between) since keys have to be inline, and will be invalidated immediately on a newline.
if(!isValid && m_flowLevel == 0)
m_indents.pop();
m_isLastKeyValid = isValid;
return isValid;
}
void Scanner::VerifyAllSimpleKeys()
{
while(!m_simpleKeys.empty())
VerifySimpleKey();
}
}
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
namespace YAML
{
Scanner::SimpleKey::SimpleKey(int pos_, int line_, int column_, int flowLevel_)
: pos(pos_), line(line_), column(column_), flowLevel(flowLevel_), pMapStart(0), pKey(0)
{
}
void Scanner::SimpleKey::Validate()
{
if(pMapStart)
pMapStart->status = TS_VALID;
if(pKey)
pKey->status = TS_VALID;
}
void Scanner::SimpleKey::Invalidate()
{
if(pMapStart)
pMapStart->status = TS_INVALID;
if(pKey)
pKey->status = TS_INVALID;
}
// InsertSimpleKey
// . Adds a potential simple key to the queue,
// and saves it on a stack.
void Scanner::InsertSimpleKey()
{
SimpleKey key(INPUT.pos(), INPUT.line, INPUT.column, m_flowLevel);
// first add a map start, if necessary
key.pMapStart = PushIndentTo(INPUT.column, false);
if(key.pMapStart)
key.pMapStart->status = TS_UNVERIFIED;
// then add the (now unverified) key
m_tokens.push(Token(TT_KEY, INPUT.line, INPUT.column));
key.pKey = &m_tokens.back();
key.pKey->status = TS_UNVERIFIED;
m_simpleKeys.push(key);
}
// VerifySimpleKey
// . Determines whether the latest simple key to be added is valid,
// and if so, makes it valid.
bool Scanner::VerifySimpleKey()
{
m_isLastKeyValid = false;
if(m_simpleKeys.empty())
return m_isLastKeyValid;
// grab top key
SimpleKey key = m_simpleKeys.top();
// only validate if we're in the correct flow level
if(key.flowLevel != m_flowLevel)
return false;
m_simpleKeys.pop();
bool isValid = true;
// needs to be followed immediately by a value
if(m_flowLevel > 0 && !Exp::ValueInFlow.Matches(INPUT))
isValid = false;
if(m_flowLevel == 0 && !Exp::Value.Matches(INPUT))
isValid = false;
// also needs to be less than 1024 characters and inline
if(INPUT.line != key.line || INPUT.pos() - key.pos > 1024)
isValid = false;
// invalidate key
if(isValid)
key.Validate();
else
key.Invalidate();
// In block style, remember that we've pushed an indent for this potential simple key (if it was starting).
// If it was invalid, then we need to pop it off.
// Note: we're guaranteed to be popping the right one (i.e., there couldn't have been anything in
// between) since keys have to be inline, and will be invalidated immediately on a newline.
if(!isValid && m_flowLevel == 0)
m_indents.pop();
m_isLastKeyValid = isValid;
return isValid;
}
void Scanner::VerifyAllSimpleKeys()
{
while(!m_simpleKeys.empty())
VerifySimpleKey();
}
}

View file

@ -1,53 +1,53 @@
#include "crt.h"
#include "stream.h"
#include <iostream>
namespace YAML
{
int Stream::pos() const
{
return input.tellg();
}
char Stream::peek()
{
return input.peek();
}
Stream::operator bool()
{
return input.good();
}
// get
// . Extracts a character from the stream and updates our position
char Stream::get()
{
char ch = input.get();
column++;
if(ch == '\n') {
column = 0;
line++;
}
return ch;
}
// get
// . Extracts 'n' characters from the stream and updates our position
std::string Stream::get(int n)
{
std::string ret;
for(int i=0;i<n;i++)
ret += get();
return ret;
}
// eat
// . Eats 'n' characters and updates our position.
void Stream::eat(int n)
{
for(int i=0;i<n;i++)
get();
}
}
#include "crt.h"
#include "stream.h"
#include <iostream>
namespace YAML
{
int Stream::pos() const
{
return input.tellg();
}
char Stream::peek()
{
return input.peek();
}
Stream::operator bool()
{
return input.good();
}
// get
// . Extracts a character from the stream and updates our position
char Stream::get()
{
char ch = input.get();
column++;
if(ch == '\n') {
column = 0;
line++;
}
return ch;
}
// get
// . Extracts 'n' characters from the stream and updates our position
std::string Stream::get(int n)
{
std::string ret;
for(int i=0;i<n;i++)
ret += get();
return ret;
}
// eat
// . Eats 'n' characters and updates our position.
void Stream::eat(int n)
{
for(int i=0;i<n;i++)
get();
}
}

View file

@ -1,25 +1,25 @@
#pragma once
#include <ios>
#include <string>
namespace YAML
{
struct Stream
{
Stream(std::istream& input_): input(input_), line(0), column(0) {}
int pos() const;
operator bool();
bool operator !() { return !(*this); }
std::istream& stream() const { return input; }
char peek();
char get();
std::string get(int n);
void eat(int n = 1);
std::istream& input;
int line, column;
};
}
#pragma once
#include <ios>
#include <string>
namespace YAML
{
struct Stream
{
Stream(std::istream& input_): input(input_), line(0), column(0) {}
int pos() const;
operator bool();
bool operator !() { return !(*this); }
std::istream& stream() const { return input; }
char peek();
char get();
std::string get(int n);
void eat(int n = 1);
std::istream& input;
int line, column;
};
}

View file

@ -1,68 +1,68 @@
#pragma once
#include <ios>
#include <string>
#include <vector>
namespace YAML
{
enum TOKEN_STATUS { TS_VALID, TS_INVALID, TS_UNVERIFIED };
enum TOKEN_TYPE {
TT_DIRECTIVE,
TT_DOC_START,
TT_DOC_END,
TT_BLOCK_SEQ_START,
TT_BLOCK_MAP_START,
TT_BLOCK_END,
TT_BLOCK_ENTRY,
TT_FLOW_SEQ_START,
TT_FLOW_MAP_START,
TT_FLOW_SEQ_END,
TT_FLOW_MAP_END,
TT_FLOW_ENTRY,
TT_KEY,
TT_VALUE,
TT_ANCHOR,
TT_ALIAS,
TT_TAG,
TT_SCALAR,
};
const std::string TokenNames[] = {
"DIRECTIVE",
"DOC_START",
"DOC_END",
"BLOCK_SEQ_START",
"BLOCK_MAP_START",
"BLOCK_END",
"BLOCK_ENTRY",
"FLOW_SEQ_START",
"FLOW_MAP_START",
"FLOW_SEQ_END",
"FLOW_MAP_END",
"FLOW_ENTRY",
"KEY",
"VALUE",
"ANCHOR",
"ALIAS",
"TAG",
"SCALAR",
};
struct Token {
Token(TOKEN_TYPE type_, int line_, int column_): status(TS_VALID), type(type_), line(line_), column(column_) {}
friend std::ostream& operator << (std::ostream& out, const Token& token) {
out << TokenNames[token.type] << std::string(": ") << token.value;
for(unsigned i=0;i<token.params.size();i++)
out << std::string(" ") << token.params[i];
return out;
}
TOKEN_STATUS status;
TOKEN_TYPE type;
int line, column;
std::string value;
std::vector <std::string> params;
};
}
#pragma once
#include <ios>
#include <string>
#include <vector>
namespace YAML
{
enum TOKEN_STATUS { TS_VALID, TS_INVALID, TS_UNVERIFIED };
enum TOKEN_TYPE {
TT_DIRECTIVE,
TT_DOC_START,
TT_DOC_END,
TT_BLOCK_SEQ_START,
TT_BLOCK_MAP_START,
TT_BLOCK_END,
TT_BLOCK_ENTRY,
TT_FLOW_SEQ_START,
TT_FLOW_MAP_START,
TT_FLOW_SEQ_END,
TT_FLOW_MAP_END,
TT_FLOW_ENTRY,
TT_KEY,
TT_VALUE,
TT_ANCHOR,
TT_ALIAS,
TT_TAG,
TT_SCALAR,
};
const std::string TokenNames[] = {
"DIRECTIVE",
"DOC_START",
"DOC_END",
"BLOCK_SEQ_START",
"BLOCK_MAP_START",
"BLOCK_END",
"BLOCK_ENTRY",
"FLOW_SEQ_START",
"FLOW_MAP_START",
"FLOW_SEQ_END",
"FLOW_MAP_END",
"FLOW_ENTRY",
"KEY",
"VALUE",
"ANCHOR",
"ALIAS",
"TAG",
"SCALAR",
};
struct Token {
Token(TOKEN_TYPE type_, int line_, int column_): status(TS_VALID), type(type_), line(line_), column(column_) {}
friend std::ostream& operator << (std::ostream& out, const Token& token) {
out << TokenNames[token.type] << std::string(": ") << token.value;
for(unsigned i=0;i<token.params.size();i++)
out << std::string(" ") << token.params[i];
return out;
}
TOKEN_STATUS status;
TOKEN_TYPE type;
int line, column;
std::string value;
std::vector <std::string> params;
};
}

View file

@ -1,41 +1,41 @@
#include "yaml.h"
#include "tests.h"
#include <fstream>
#include <iostream>
#ifdef _MSC_VER
#ifdef _DEBUG
#pragma comment(lib, "yamlcppd.lib")
#else
#pragma comment(lib, "yamlcpp.lib")
#endif // _DEBUG
#endif // _MSC_VER
void run()
{
std::ifstream fin("tests/test.yaml");
try {
YAML::Parser parser(fin);
parser.PrintTokens(std::cout);
} catch(YAML::Exception&) {
std::cout << "Error parsing the yaml!\n";
}
}
int main(int argc, char **argv)
{
bool verbose = false;
for(int i=1;i<argc;i++) {
if(strcmp(argv[i], "-v") == 0)
verbose = true;
}
#ifdef WINDOWS
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF);
#endif // WINDOWS
Test::RunAll(verbose);
run();
return 0;
}
#include "yaml.h"
#include "tests.h"
#include <fstream>
#include <iostream>
#ifdef _MSC_VER
#ifdef _DEBUG
#pragma comment(lib, "yamlcppd.lib")
#else
#pragma comment(lib, "yamlcpp.lib")
#endif // _DEBUG
#endif // _MSC_VER
void run()
{
std::ifstream fin("tests/test.yaml");
try {
YAML::Parser parser(fin);
parser.PrintTokens(std::cout);
} catch(YAML::Exception&) {
std::cout << "Error parsing the yaml!\n";
}
}
int main(int argc, char **argv)
{
bool verbose = false;
for(int i=1;i<argc;i++) {
if(strcmp(argv[i], "-v") == 0)
verbose = true;
}
#ifdef WINDOWS
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF);
#endif // WINDOWS
Test::RunAll(verbose);
run();
return 0;
}

View file

@ -1,88 +1,88 @@
#include "yaml.h"
#include "tests.h"
#include "parser.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
namespace Test
{
// runs all the tests on all data we have
void RunAll(bool verbose)
{
std::vector <std::string> files;
files.push_back("tests/simple.yaml");
files.push_back("tests/mixed.yaml");
files.push_back("tests/scalars.yaml");
files.push_back("tests/directives.yaml");
bool passed = true;
for(unsigned i=0;i<files.size();i++) {
if(!Inout(files[i], verbose)) {
std::cout << "Inout test failed on " << files[i] << "\n";
passed = false;
} else
std::cout << "Inout test passed: " << files[i] << "\n";
}
if(passed)
std::cout << "All tests passed!\n";
}
// loads the given YAML file, outputs it, and then loads the outputted file,
// outputs again, and makes sure that the two outputs are the same
bool Inout(const std::string& file, bool verbose)
{
std::ifstream fin(file.c_str());
try {
// read and output
YAML::Parser parser(fin);
if(!parser)
return false;
YAML::Node doc;
parser.GetNextDocument(doc);
std::stringstream out;
out << doc;
// and save
std::string firstTry = out.str();
// and now again
parser.Load(out);
if(!parser)
return false;
parser.GetNextDocument(doc);
std::stringstream out2;
out2 << doc;
// and save
std::string secondTry = out2.str();
// now compare
if(firstTry == secondTry)
return true;
std::ofstream fout("tests/out.yaml");
fout << "---\n";
fout << firstTry << std::endl;
fout << "---\n";
fout << secondTry << std::endl;
} catch(YAML::ParserException& e) {
std::cout << file << " (line " << e.line + 1 << ", col " << e.column + 1 << "): " << e.msg << std::endl;
if(verbose) {
std::cout << "Token queue:\n";
std::ifstream f(file.c_str());
YAML::Parser p(f);
p.PrintTokens(std::cout);
}
return false;
}
return true;
}
}
#include "yaml.h"
#include "tests.h"
#include "parser.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
namespace Test
{
// runs all the tests on all data we have
void RunAll(bool verbose)
{
std::vector <std::string> files;
files.push_back("tests/simple.yaml");
files.push_back("tests/mixed.yaml");
files.push_back("tests/scalars.yaml");
files.push_back("tests/directives.yaml");
bool passed = true;
for(unsigned i=0;i<files.size();i++) {
if(!Inout(files[i], verbose)) {
std::cout << "Inout test failed on " << files[i] << "\n";
passed = false;
} else
std::cout << "Inout test passed: " << files[i] << "\n";
}
if(passed)
std::cout << "All tests passed!\n";
}
// loads the given YAML file, outputs it, and then loads the outputted file,
// outputs again, and makes sure that the two outputs are the same
bool Inout(const std::string& file, bool verbose)
{
std::ifstream fin(file.c_str());
try {
// read and output
YAML::Parser parser(fin);
if(!parser)
return false;
YAML::Node doc;
parser.GetNextDocument(doc);
std::stringstream out;
out << doc;
// and save
std::string firstTry = out.str();
// and now again
parser.Load(out);
if(!parser)
return false;
parser.GetNextDocument(doc);
std::stringstream out2;
out2 << doc;
// and save
std::string secondTry = out2.str();
// now compare
if(firstTry == secondTry)
return true;
std::ofstream fout("tests/out.yaml");
fout << "---\n";
fout << firstTry << std::endl;
fout << "---\n";
fout << secondTry << std::endl;
} catch(YAML::ParserException& e) {
std::cout << file << " (line " << e.line + 1 << ", col " << e.column + 1 << "): " << e.msg << std::endl;
if(verbose) {
std::cout << "Token queue:\n";
std::ifstream f(file.c_str());
YAML::Parser p(f);
p.PrintTokens(std::cout);
}
return false;
}
return true;
}
}

View file

@ -1,6 +1,6 @@
#include <string>
namespace Test {
void RunAll(bool verbose);
bool Inout(const std::string& file, bool verbose);
}
#include <string>
namespace Test {
void RunAll(bool verbose);
bool Inout(const std::string& file, bool verbose);
}

View file

@ -1,5 +1,5 @@
%YAML 1.2
%TAG ! !howdy
---
- basic node
%YAML 1.2
%TAG ! !howdy
---
- basic node
- ! yeah baby

View file

@ -1,32 +1,32 @@
- the main thing is a sequence
- here's a key: value
and another: value
- let's inline: [1, 2, 3]
and an inline map: {key: value, 243: 101}
- and multiple indents:
- here's
- a
- list
and another:
- list
- of
- things
- maybe now:
let's: get
pretty:
deep: here
in:
the: nesting
just: to
confuse:
the: heck
out:
- of
- the: parser
if:
- we
- can
- do: that
what: do
you: think?
- the main thing is a sequence
- here's a key: value
and another: value
- let's inline: [1, 2, 3]
and an inline map: {key: value, 243: 101}
- and multiple indents:
- here's
- a
- list
and another:
- list
- of
- things
- maybe now:
let's: get
pretty:
deep: here
in:
the: nesting
just: to
confuse:
the: heck
out:
- of
- the: parser
if:
- we
- can
- do: that
what: do
you: think?

View file

@ -1,35 +1,35 @@
- normal scalar, but
over several lines
- |
literal scalar - so we can draw ASCII:
- -
| - |
------
- >
and a folded scalar... so we
can just keep writing various
things. And if we want to keep indentation:
we just indent a little
see, this stays indented
- >-
Here's a folded scalar
that gets chomped.
- |-
And here's a literal scalar
that gets chomped.
- >2
Here's a folded scalar
that starts with some indentation.
- ::vector
- ": - ()"
- Up, up, and away!
- -123
- http://example.com/foo#bar
# Inside flow collection:
- [ ::vector,
": - ()",
"Up, up and away!",
-123,
- normal scalar, but
over several lines
- |
literal scalar - so we can draw ASCII:
- -
| - |
------
- >
and a folded scalar... so we
can just keep writing various
things. And if we want to keep indentation:
we just indent a little
see, this stays indented
- >-
Here's a folded scalar
that gets chomped.
- |-
And here's a literal scalar
that gets chomped.
- >2
Here's a folded scalar
that starts with some indentation.
- ::vector
- ": - ()"
- Up, up, and away!
- -123
- http://example.com/foo#bar
# Inside flow collection:
- [ ::vector,
": - ()",
"Up, up and away!",
-123,
http://example.com/foo#bar ]

View file

@ -1,13 +1,13 @@
---
just a scalar
---
and another scalar
---
now an end document
...
---
and now two
...
...
---
---
just a scalar
---
and another scalar
---
now an end document
...
---
and now two
...
...
---
and that's it

View file

@ -1,3 +1,3 @@
- it's just
- one thing
- after another
- it's just
- one thing
- after another