From 45322566c7050c737ddfd69d08791ac6d65b6919 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Wed, 3 Sep 2008 22:20:39 +0000 Subject: [PATCH] Set the eol style to native for all files. --- include/crt.h | 22 +- include/exceptions.h | 118 ++--- include/iterator.h | 60 +-- include/node.h | 184 +++---- include/parser.h | 84 ++-- include/parserstate.h | 40 +- include/yaml.h | 16 +- src/content.cpp | 26 +- src/content.h | 110 ++-- src/exp.cpp | 226 ++++----- src/exp.h | 142 +++--- src/iterator.cpp | 216 ++++---- src/iterpriv.h | 50 +- src/ltnode.h | 20 +- src/map.cpp | 392 +++++++-------- src/map.h | 76 +-- src/node.cpp | 634 +++++++++++------------ src/parser.cpp | 266 +++++----- src/parserstate.cpp | 52 +- src/regex.cpp | 612 +++++++++++----------- src/regex.h | 180 +++---- src/scalar.cpp | 216 ++++---- src/scalar.h | 74 +-- src/scanner.cpp | 552 ++++++++++---------- src/scanner.h | 170 +++---- src/scanscalar.cpp | 308 ++++++------ src/scanscalar.h | 70 +-- src/scantoken.cpp | 810 +++++++++++++++--------------- src/sequence.cpp | 350 ++++++------- src/sequence.h | 82 +-- src/simplekey.cpp | 204 ++++---- src/stream.cpp | 106 ++-- src/stream.h | 50 +- src/token.h | 136 ++--- yaml-reader/main.cpp | 82 +-- yaml-reader/tests.cpp | 176 +++---- yaml-reader/tests.h | 12 +- yaml-reader/tests/directives.yaml | 8 +- yaml-reader/tests/mixed.yaml | 62 +-- yaml-reader/tests/scalars.yaml | 68 +-- yaml-reader/tests/simple.yaml | 24 +- yaml-reader/tests/test.yaml | 6 +- 42 files changed, 3546 insertions(+), 3546 deletions(-) diff --git a/include/crt.h b/include/crt.h index ed2b68a..5c6f96a 100644 --- a/include/crt.h +++ b/include/crt.h @@ -1,11 +1,11 @@ -#pragma once - -// for detecting memory leaks -#ifdef _DEBUG - -#define _CRTDBG_MAP_ALLOC -#include -#include - -#endif // _DEBUG - +#pragma once + +// for detecting memory leaks +#ifdef _DEBUG + +#define _CRTDBG_MAP_ALLOC +#include +#include + +#endif // _DEBUG + diff --git a/include/exceptions.h b/include/exceptions.h index 29c555a..427088a 100644 --- a/include/exceptions.h +++ b/include/exceptions.h @@ -1,59 +1,59 @@ -#pragma once - -#include -#include - -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 +#include + +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"; + } +} diff --git a/include/iterator.h b/include/iterator.h index afaddb6..3dbd9ee 100644 --- a/include/iterator.h +++ b/include/iterator.h @@ -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; + }; +} diff --git a/include/node.h b/include/node.h index 6d604c9..8f1a739 100644 --- a/include/node.h +++ b/include/node.h @@ -1,92 +1,92 @@ -#pragma once - -#include -#include -#include -#include -#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 - 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 - 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 +#include +#include +#include +#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 + 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 + 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; + }; +} diff --git a/include/parser.h b/include/parser.h index ef4d656..83f611b 100644 --- a/include/parser.h +++ b/include/parser.h @@ -1,42 +1,42 @@ -#pragma once - -#include -#include -#include -#include -#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 +#include +#include +#include +#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; + }; +} diff --git a/include/parserstate.h b/include/parserstate.h index e98667d..f8eb97f 100644 --- a/include/parserstate.h +++ b/include/parserstate.h @@ -1,20 +1,20 @@ -#pragma once - -#include -#include - -namespace YAML -{ - struct Version { - int major, minor; - }; - - struct ParserState - { - Version version; - std::map tags; - - void Reset(); - std::string TranslateTag(const std::string& handle) const; - }; -} +#pragma once + +#include +#include + +namespace YAML +{ + struct Version { + int major, minor; + }; + + struct ParserState + { + Version version; + std::map tags; + + void Reset(); + std::string TranslateTag(const std::string& handle) const; + }; +} diff --git a/include/yaml.h b/include/yaml.h index 8286be4..f7cd1e7 100644 --- a/include/yaml.h +++ b/include/yaml.h @@ -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" + diff --git a/src/content.cpp b/src/content.cpp index 4995729..850dcab 100644 --- a/src/content.cpp +++ b/src/content.cpp @@ -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() + { + } +} diff --git a/src/content.h b/src/content.h index 166932e..b7a5da9 100644 --- a/src/content.h +++ b/src/content.h @@ -1,55 +1,55 @@ -#pragma once - -#include -#include -#include -#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 ::const_iterator& it) const { return false; } - virtual bool GetBegin(std::map ::const_iterator& it) const { return false; } - virtual bool GetEnd(std::vector ::const_iterator& it) const { return false; } - virtual bool GetEnd(std::map ::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 +#include +#include +#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 ::const_iterator& it) const { return false; } + virtual bool GetBegin(std::map ::const_iterator& it) const { return false; } + virtual bool GetEnd(std::vector ::const_iterator& it) const { return false; } + virtual bool GetEnd(std::map ::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: + }; +} diff --git a/src/exp.cpp b/src/exp.cpp index 8ce2af7..7148acc 100644 --- a/src/exp.cpp +++ b/src/exp.cpp @@ -1,113 +1,113 @@ -#include "crt.h" -#include "exp.h" -#include "exceptions.h" -#include - -namespace YAML -{ - namespace Exp - { - unsigned ParseHex(const std::string& str, int line, int column) - { - unsigned value = 0; - for(unsigned i=0;i= 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 + +namespace YAML +{ + namespace Exp + { + unsigned ParseHex(const std::string& str, int line, int column) + { + unsigned value = 0; + for(unsigned i=0;i= 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); + } + } +} diff --git a/src/exp.h b/src/exp.h index d2bd469..7811bd2 100644 --- a/src/exp.h +++ b/src/exp.h @@ -1,71 +1,71 @@ -#pragma once - -#include "regex.h" -#include -#include -#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 +#include +#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 = '>'; + } +} diff --git a/src/iterator.cpp b/src/iterator.cpp index 1ce3dc0..51e52a3 100644 --- a/src/iterator.cpp +++ b/src/iterator.cpp @@ -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); + } +} diff --git a/src/iterpriv.h b/src/iterpriv.h index 5c29366..ec38617 100644 --- a/src/iterpriv.h +++ b/src/iterpriv.h @@ -1,25 +1,25 @@ -#pragma once - -#include "ltnode.h" -#include -#include - -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 ::const_iterator it): type(IT_SEQ), seqIter(it) {} - IterPriv(std::map ::const_iterator it): type(IT_MAP), mapIter(it) {} - - enum ITER_TYPE { IT_NONE, IT_SEQ, IT_MAP }; - ITER_TYPE type; - - std::vector ::const_iterator seqIter; - std::map ::const_iterator mapIter; - }; -} +#pragma once + +#include "ltnode.h" +#include +#include + +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 ::const_iterator it): type(IT_SEQ), seqIter(it) {} + IterPriv(std::map ::const_iterator it): type(IT_MAP), mapIter(it) {} + + enum ITER_TYPE { IT_NONE, IT_SEQ, IT_MAP }; + ITER_TYPE type; + + std::vector ::const_iterator seqIter; + std::map ::const_iterator mapIter; + }; +} diff --git a/src/ltnode.h b/src/ltnode.h index af8c1bb..958d0ac 100644 --- a/src/ltnode.h +++ b/src/ltnode.h @@ -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; + }; +} diff --git a/src/map.cpp b/src/map.cpp index 727726b..facb537 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,196 +1,196 @@ -#include "crt.h" -#include "map.h" -#include "node.h" -#include "scanner.h" -#include "token.h" -#include "exceptions.h" -#include - -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 ::const_iterator& it) const - { - it = m_data.begin(); - return true; - } - - bool Map::GetEnd(std::map ::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;ifirst->Write(out, indent + 1, true, it!= m_data.begin() || !startedLine || onlyOneCharOnLine); - - for(int i=0;isecond->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 + +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 ::const_iterator& it) const + { + it = m_data.begin(); + return true; + } + + bool Map::GetEnd(std::map ::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;ifirst->Write(out, indent + 1, true, it!= m_data.begin() || !startedLine || onlyOneCharOnLine); + + for(int i=0;isecond->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; + } +} diff --git a/src/map.h b/src/map.h index 8e58887..0f7c3bf 100644 --- a/src/map.h +++ b/src/map.h @@ -1,38 +1,38 @@ -#pragma once - -#include "content.h" -#include - -namespace YAML -{ - class Node; - - class Map: public Content - { - public: - Map(); - virtual ~Map(); - - void Clear(); - virtual bool GetBegin(std::map ::const_iterator& it) const; - virtual bool GetEnd(std::map ::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_map; - node_map m_data; - }; -} +#pragma once + +#include "content.h" +#include + +namespace YAML +{ + class Node; + + class Map: public Content + { + public: + Map(); + virtual ~Map(); + + void Clear(); + virtual bool GetBegin(std::map ::const_iterator& it) const; + virtual bool GetEnd(std::map ::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_map; + node_map m_data; + }; +} diff --git a/src/node.cpp b/src/node.cpp index 64ec8b3..f009903 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -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;ipop(); - } - - 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 ::const_iterator seqIter; - if(m_pContent->GetBegin(seqIter)) - return Iterator(new IterPriv(seqIter)); - - std::map ::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 ::const_iterator seqIter; - if(m_pContent->GetEnd(seqIter)) - return Iterator(new IterPriv(seqIter)); - - std::map ::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;ipop(); + } + + 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 ::const_iterator seqIter; + if(m_pContent->GetBegin(seqIter)) + return Iterator(new IterPriv(seqIter)); + + std::map ::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 ::const_iterator seqIter; + if(m_pContent->GetEnd(seqIter)) + return Iterator(new IterPriv(seqIter)); + + std::map ::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; + } +} diff --git a/src/parser.cpp b/src/parser.cpp index 18c7c8d..d0ba2b2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,133 +1,133 @@ -#include "crt.h" -#include "parser.h" -#include "scanner.h" -#include "token.h" -#include "exceptions.h" -#include - -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 + +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(); + } + } +} diff --git a/src/parserstate.cpp b/src/parserstate.cpp index 4a14b3a..40adb03 100644 --- a/src/parserstate.cpp +++ b/src/parserstate.cpp @@ -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 ::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 ::const_iterator it = tags.find(handle); + if(it == tags.end()) + return handle; + + return it->second; + } +} diff --git a/src/regex.cpp b/src/regex.cpp index c40a087..9c0e217 100644 --- a/src/regex.cpp +++ b/src/regex.cpp @@ -1,306 +1,306 @@ -#include "crt.h" -#include "regex.h" -#include "stream.h" -#include - -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= 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= 0) - return n; - } - return -1; - } - - int RegEx::OrOperator::Match(std::istream& in, const RegEx& regex) const - { - for(unsigned i=0;i= 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= 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 + +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= 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= 0) + return n; + } + return -1; + } + + int RegEx::OrOperator::Match(std::istream& in, const RegEx& regex) const + { + for(unsigned i=0;i= 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= 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 -#include -#include - -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 m_params; - }; -} +#pragma once + +#include +#include +#include + +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 m_params; + }; +} diff --git a/src/scalar.cpp b/src/scalar.cpp index 56edb4a..ed8f032 100644 --- a/src/scalar.cpp +++ b/src/scalar.cpp @@ -1,108 +1,108 @@ -#include "crt.h" -#include "scalar.h" -#include "scanner.h" -#include "token.h" -#include "exceptions.h" -#include "node.h" -#include - -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> 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 + +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> 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; + } +} diff --git a/src/scalar.h b/src/scalar.h index f33f8a5..ea6e055 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -1,37 +1,37 @@ -#pragma once - -#include "content.h" -#include - -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 + +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; + }; +} diff --git a/src/scanner.cpp b/src/scanner.cpp index b800840..5aed8d5 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -1,276 +1,276 @@ -#include "crt.h" -#include "scanner.h" -#include "token.h" -#include "exceptions.h" -#include "exp.h" -#include - -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 + +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)); + } + } +} diff --git a/src/scanner.h b/src/scanner.h index 805b82d..8764719 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -1,85 +1,85 @@ -#pragma once - -#include -#include -#include -#include -#include -#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 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 m_simpleKeys; - std::stack m_indents; - }; -} +#pragma once + +#include +#include +#include +#include +#include +#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 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 m_simpleKeys; + std::stack m_indents; + }; +} diff --git a/src/scanscalar.cpp b/src/scanscalar.cpp index 70abef4..b318f00 100644 --- a/src/scanscalar.cpp +++ b/src/scanscalar.cpp @@ -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; + } +} diff --git a/src/scanscalar.h b/src/scanscalar.h index b2c4327..ee66213 100644 --- a/src/scanscalar.h +++ b/src/scanscalar.h @@ -1,35 +1,35 @@ -#pragma once - -#include -#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 +#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); +} diff --git a/src/scantoken.cpp b/src/scantoken.cpp index 31489ae..39f6720 100644 --- a/src/scantoken.cpp +++ b/src/scantoken.cpp @@ -1,405 +1,405 @@ -#include "crt.h" -#include "scanner.h" -#include "token.h" -#include "exceptions.h" -#include "exp.h" -#include "scanscalar.h" -#include - -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 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= 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 + +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 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= 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); + } +} diff --git a/src/sequence.cpp b/src/sequence.cpp index f997a79..df4a201 100644 --- a/src/sequence.cpp +++ b/src/sequence.cpp @@ -1,175 +1,175 @@ -#include "crt.h" -#include "sequence.h" -#include "node.h" -#include "scanner.h" -#include "token.h" -#include - -namespace YAML -{ - Sequence::Sequence() - { - - } - - Sequence::~Sequence() - { - Clear(); - } - - void Sequence::Clear() - { - for(unsigned i=0;i::const_iterator& it) const - { - it = m_data.begin(); - return true; - } - - bool Sequence::GetEnd(std::vector ::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 0) { - for(int j=0;jWrite(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;iCompare(*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 + +namespace YAML +{ + Sequence::Sequence() + { + + } + + Sequence::~Sequence() + { + Clear(); + } + + void Sequence::Clear() + { + for(unsigned i=0;i::const_iterator& it) const + { + it = m_data.begin(); + return true; + } + + bool Sequence::GetEnd(std::vector ::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 0) { + for(int j=0;jWrite(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;iCompare(*pSeq->m_data[i]); + if(cmp != 0) + return cmp; + } + + return 0; + } +} diff --git a/src/sequence.h b/src/sequence.h index 89dba85..6d5b01c 100644 --- a/src/sequence.h +++ b/src/sequence.h @@ -1,41 +1,41 @@ -#pragma once - -#include "content.h" -#include - -namespace YAML -{ - class Node; - - class Sequence: public Content - { - public: - Sequence(); - virtual ~Sequence(); - - void Clear(); - virtual bool GetBegin(std::vector ::const_iterator& it) const; - virtual bool GetEnd(std::vector ::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 m_data; - }; -} +#pragma once + +#include "content.h" +#include + +namespace YAML +{ + class Node; + + class Sequence: public Content + { + public: + Sequence(); + virtual ~Sequence(); + + void Clear(); + virtual bool GetBegin(std::vector ::const_iterator& it) const; + virtual bool GetEnd(std::vector ::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 m_data; + }; +} diff --git a/src/simplekey.cpp b/src/simplekey.cpp index e9d25ee..32a1761 100644 --- a/src/simplekey.cpp +++ b/src/simplekey.cpp @@ -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(); + } +} diff --git a/src/stream.cpp b/src/stream.cpp index 73d4129..b388899 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -1,53 +1,53 @@ -#include "crt.h" -#include "stream.h" -#include - -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 + +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 -#include - -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 +#include + +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; + }; +} diff --git a/src/token.h b/src/token.h index 8a98c1a..5b000d1 100644 --- a/src/token.h +++ b/src/token.h @@ -1,68 +1,68 @@ -#pragma once - -#include -#include -#include - -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 params; - }; -} +#pragma once + +#include +#include +#include + +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 params; + }; +} diff --git a/yaml-reader/main.cpp b/yaml-reader/main.cpp index 1d32cba..ab122c0 100644 --- a/yaml-reader/main.cpp +++ b/yaml-reader/main.cpp @@ -1,41 +1,41 @@ -#include "yaml.h" -#include "tests.h" -#include -#include - -#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 +#include + +#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 -#include -#include -#include - -namespace Test -{ - // runs all the tests on all data we have - void RunAll(bool verbose) - { - std::vector 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 +#include +#include +#include + +namespace Test +{ + // runs all the tests on all data we have + void RunAll(bool verbose) + { + std::vector 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 - -namespace Test { - void RunAll(bool verbose); - bool Inout(const std::string& file, bool verbose); -} +#include + +namespace Test { + void RunAll(bool verbose); + bool Inout(const std::string& file, bool verbose); +} diff --git a/yaml-reader/tests/directives.yaml b/yaml-reader/tests/directives.yaml index ff09846..adc9130 100644 --- a/yaml-reader/tests/directives.yaml +++ b/yaml-reader/tests/directives.yaml @@ -1,5 +1,5 @@ -%YAML 1.2 -%TAG ! !howdy ---- -- basic node +%YAML 1.2 +%TAG ! !howdy +--- +- basic node - ! yeah baby \ No newline at end of file diff --git a/yaml-reader/tests/mixed.yaml b/yaml-reader/tests/mixed.yaml index 88da1e7..68cbf4a 100644 --- a/yaml-reader/tests/mixed.yaml +++ b/yaml-reader/tests/mixed.yaml @@ -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? \ No newline at end of file diff --git a/yaml-reader/tests/scalars.yaml b/yaml-reader/tests/scalars.yaml index ae0059a..65f9a06 100644 --- a/yaml-reader/tests/scalars.yaml +++ b/yaml-reader/tests/scalars.yaml @@ -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 ] \ No newline at end of file diff --git a/yaml-reader/tests/simple.yaml b/yaml-reader/tests/simple.yaml index 55b2d21..69d4239 100644 --- a/yaml-reader/tests/simple.yaml +++ b/yaml-reader/tests/simple.yaml @@ -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 \ No newline at end of file diff --git a/yaml-reader/tests/test.yaml b/yaml-reader/tests/test.yaml index 1392289..662ec0c 100644 --- a/yaml-reader/tests/test.yaml +++ b/yaml-reader/tests/test.yaml @@ -1,3 +1,3 @@ -- it's just -- one thing -- after another +- it's just +- one thing +- after another