Commit b43f8271 authored by jbeder's avatar jbeder
Browse files

Set the eol style to native for all files.

parent 859ac5e5
#pragma once #pragma once
#include <vector> #include <vector>
#include <string> #include <string>
#include <ios> #include <ios>
namespace YAML namespace YAML
{ {
struct Stream; struct Stream;
enum REGEX_OP { REGEX_EMPTY, REGEX_MATCH, REGEX_RANGE, REGEX_OR, REGEX_AND, REGEX_NOT, REGEX_SEQ }; enum REGEX_OP { REGEX_EMPTY, REGEX_MATCH, REGEX_RANGE, REGEX_OR, REGEX_AND, REGEX_NOT, REGEX_SEQ };
// simplified regular expressions // simplified regular expressions
// . Only straightforward matches (no repeated characters) // . Only straightforward matches (no repeated characters)
// . Only matches from start of string // . Only matches from start of string
class RegEx class RegEx
{ {
private: private:
struct Operator { struct Operator {
virtual ~Operator() {} virtual ~Operator() {}
virtual int Match(const std::string& str, const RegEx& regex) const = 0; virtual int Match(const std::string& str, const RegEx& regex) const = 0;
virtual int Match(std::istream& in, const RegEx& regex) const = 0; virtual int Match(std::istream& in, const RegEx& regex) const = 0;
}; };
struct MatchOperator: public Operator { struct MatchOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const; virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const; virtual int Match(std::istream& in, const RegEx& regex) const;
}; };
struct RangeOperator: public Operator { struct RangeOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const; virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const; virtual int Match(std::istream& in, const RegEx& regex) const;
}; };
struct OrOperator: public Operator { struct OrOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const; virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const; virtual int Match(std::istream& in, const RegEx& regex) const;
}; };
struct AndOperator: public Operator { struct AndOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const; virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const; virtual int Match(std::istream& in, const RegEx& regex) const;
}; };
struct NotOperator: public Operator { struct NotOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const; virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const; virtual int Match(std::istream& in, const RegEx& regex) const;
}; };
struct SeqOperator: public Operator { struct SeqOperator: public Operator {
virtual int Match(const std::string& str, const RegEx& regex) const; virtual int Match(const std::string& str, const RegEx& regex) const;
virtual int Match(std::istream& in, const RegEx& regex) const; virtual int Match(std::istream& in, const RegEx& regex) const;
}; };
public: public:
friend struct Operator; friend struct Operator;
RegEx(); RegEx();
RegEx(char ch); RegEx(char ch);
RegEx(char a, char z); RegEx(char a, char z);
RegEx(const std::string& str, REGEX_OP op = REGEX_SEQ); RegEx(const std::string& str, REGEX_OP op = REGEX_SEQ);
RegEx(const RegEx& rhs); RegEx(const RegEx& rhs);
~RegEx(); ~RegEx();
RegEx& operator = (const RegEx& rhs); RegEx& operator = (const RegEx& rhs);
bool Matches(char ch) const; bool Matches(char ch) const;
bool Matches(const std::string& str) const; bool Matches(const std::string& str) const;
bool Matches(std::istream& in) const; bool Matches(std::istream& in) const;
bool Matches(Stream& in) const; bool Matches(Stream& in) const;
int Match(const std::string& str) const; int Match(const std::string& str) const;
int Match(std::istream& in) const; int Match(std::istream& in) const;
int Match(Stream& in) const; int Match(Stream& in) const;
friend RegEx operator ! (const RegEx& ex); 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); 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: private:
RegEx(REGEX_OP op); RegEx(REGEX_OP op);
void SetOp(); void SetOp();
private: private:
REGEX_OP m_op; REGEX_OP m_op;
Operator *m_pOp; Operator *m_pOp;
char m_a, m_z; char m_a, m_z;
std::vector <RegEx> m_params; std::vector <RegEx> m_params;
}; };
} }
#include "crt.h" #include "crt.h"
#include "scalar.h" #include "scalar.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
#include "node.h" #include "node.h"
#include <sstream> #include <sstream>
namespace YAML namespace YAML
{ {
Scalar::Scalar() Scalar::Scalar()
{ {
} }
Scalar::~Scalar() Scalar::~Scalar()
{ {
} }
void Scalar::Parse(Scanner *pScanner, const ParserState& state) void Scalar::Parse(Scanner *pScanner, const ParserState& state)
{ {
Token& token = pScanner->peek(); Token& token = pScanner->peek();
m_data = token.value; m_data = token.value;
pScanner->pop(); pScanner->pop();
} }
void Scalar::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) void Scalar::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine)
{ {
out << "\""; out << "\"";
for(unsigned i=0;i<m_data.size();i++) { for(unsigned i=0;i<m_data.size();i++) {
switch(m_data[i]) { switch(m_data[i]) {
case '\\': out << "\\\\"; break; case '\\': out << "\\\\"; break;
case '\t': out << "\\t"; break; case '\t': out << "\\t"; break;
case '\n': out << "\\n"; break; case '\n': out << "\\n"; break;
case '\r': out << "\\r"; break; case '\r': out << "\\r"; break;
default: out << m_data[i]; break; default: out << m_data[i]; break;
} }
} }
out << "\"\n"; out << "\"\n";
} }
void Scalar::Read(std::string& s) void Scalar::Read(std::string& s)
{ {
s = m_data; s = m_data;
} }
void Scalar::Read(int& i) void Scalar::Read(int& i)
{ {
std::stringstream data(m_data); std::stringstream data(m_data);
data >> i; data >> i;
if(!data) if(!data)
throw InvalidScalar(); throw InvalidScalar();
} }
void Scalar::Read(unsigned& u) void Scalar::Read(unsigned& u)
{ {
std::stringstream data(m_data); std::stringstream data(m_data);
data >> u; data >> u;
if(!data) if(!data)
throw InvalidScalar(); throw InvalidScalar();
} }
void Scalar::Read(long& l) void Scalar::Read(long& l)
{ {
std::stringstream data(m_data); std::stringstream data(m_data);
data >> l; data >> l;
if(!data) if(!data)
throw InvalidScalar(); throw InvalidScalar();
} }
void Scalar::Read(float& f) void Scalar::Read(float& f)
{ {
std::stringstream data(m_data); std::stringstream data(m_data);
data >> f; data >> f;
if(!data) if(!data)
throw InvalidScalar(); throw InvalidScalar();
} }
void Scalar::Read(double& d) void Scalar::Read(double& d)
{ {
std::stringstream data(m_data); std::stringstream data(m_data);
data >> d; data >> d;
if(!data) if(!data)
throw InvalidScalar(); throw InvalidScalar();
} }
void Scalar::Read(char& c) void Scalar::Read(char& c)
{ {
std::stringstream data(m_data); std::stringstream data(m_data);
data >> c; data >> c;
if(!data) if(!data)
throw InvalidScalar(); throw InvalidScalar();
} }
int Scalar::Compare(Content *pContent) int Scalar::Compare(Content *pContent)
{ {
return -pContent->Compare(this); return -pContent->Compare(this);
} }
int Scalar::Compare(Scalar *pScalar) int Scalar::Compare(Scalar *pScalar)
{ {
if(m_data < pScalar->m_data) if(m_data < pScalar->m_data)
return -1; return -1;
else if(m_data > pScalar->m_data) else if(m_data > pScalar->m_data)
return 1; return 1;
else else
return 0; return 0;
} }
} }
#pragma once #pragma once
#include "content.h" #include "content.h"
#include <string> #include <string>
namespace YAML namespace YAML
{ {
class Scalar: public Content class Scalar: public Content
{ {
public: public:
Scalar(); Scalar();
virtual ~Scalar(); virtual ~Scalar();
virtual void Parse(Scanner *pScanner, const ParserState& state); virtual void Parse(Scanner *pScanner, const ParserState& state);
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine); virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine);
virtual bool IsScalar() const { return true; } virtual bool IsScalar() const { return true; }
// extraction // extraction
virtual void Read(std::string& s); virtual void Read(std::string& s);
virtual void Read(int& i); virtual void Read(int& i);
virtual void Read(unsigned& u); virtual void Read(unsigned& u);
virtual void Read(long& l); virtual void Read(long& l);
virtual void Read(float& f); virtual void Read(float& f);
virtual void Read(double& d); virtual void Read(double& d);
virtual void Read(char& c); virtual void Read(char& c);
// ordering // ordering
virtual int Compare(Content *pContent); virtual int Compare(Content *pContent);
virtual int Compare(Scalar *pScalar); virtual int Compare(Scalar *pScalar);
virtual int Compare(Sequence *pSeq) { return -1; } virtual int Compare(Sequence *pSeq) { return -1; }
virtual int Compare(Map *pMap) { return -1; } virtual int Compare(Map *pMap) { return -1; }
protected: protected:
std::string m_data; std::string m_data;
}; };
} }
#include "crt.h" #include "crt.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
#include "exp.h" #include "exp.h"
#include <cassert> #include <cassert>
namespace YAML namespace YAML
{ {
Scanner::Scanner(std::istream& in) Scanner::Scanner(std::istream& in)
: INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), m_flowLevel(0) : INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), m_flowLevel(0)
{ {
} }
Scanner::~Scanner() Scanner::~Scanner()
{ {
} }
// empty // empty
// . Returns true if there are no more tokens to be read // . Returns true if there are no more tokens to be read
bool Scanner::empty() bool Scanner::empty()
{ {
EnsureTokensInQueue(); EnsureTokensInQueue();
return m_tokens.empty(); return m_tokens.empty();
} }
// pop // pop
// . Simply removes the next token on the queue. // . Simply removes the next token on the queue.
void Scanner::pop() void Scanner::pop()
{ {
EnsureTokensInQueue(); EnsureTokensInQueue();
if(!m_tokens.empty()) if(!m_tokens.empty())
m_tokens.pop(); m_tokens.pop();
} }
// peek // peek
// . Returns (but does not remove) the next token on the queue. // . Returns (but does not remove) the next token on the queue.
Token& Scanner::peek() Token& Scanner::peek()
{ {
EnsureTokensInQueue(); EnsureTokensInQueue();
assert(!m_tokens.empty()); // should we be asserting here? I mean, we really just be checking assert(!m_tokens.empty()); // should we be asserting here? I mean, we really just be checking
// if it's empty before peeking. // if it's empty before peeking.
return m_tokens.front(); return m_tokens.front();
} }
// EnsureTokensInQueue // EnsureTokensInQueue
// . Scan until there's a valid token at the front of the queue, // . Scan until there's a valid token at the front of the queue,
// or we're sure the queue is empty. // or we're sure the queue is empty.
void Scanner::EnsureTokensInQueue() void Scanner::EnsureTokensInQueue()
{ {
while(1) { while(1) {
if(!m_tokens.empty()) { if(!m_tokens.empty()) {
Token& token = m_tokens.front(); Token& token = m_tokens.front();
// if this guy's valid, then we're done // if this guy's valid, then we're done
if(token.status == TS_VALID) if(token.status == TS_VALID)
return; return;
// here's where we clean up the impossible tokens // here's where we clean up the impossible tokens
if(token.status == TS_INVALID) { if(token.status == TS_INVALID) {
m_tokens.pop(); m_tokens.pop();
continue; continue;
} }
// note: what's left are the unverified tokens // note: what's left are the unverified tokens
} }
// no token? maybe we've actually finished // no token? maybe we've actually finished
if(m_endedStream) if(m_endedStream)
return; return;
// no? then scan... // no? then scan...
ScanNextToken(); ScanNextToken();
} }
} }
// ScanNextToken // ScanNextToken
// . The main scanning function; here we branch out and // . The main scanning function; here we branch out and
// scan whatever the next token should be. // scan whatever the next token should be.
void Scanner::ScanNextToken() void Scanner::ScanNextToken()
{ {
if(m_endedStream) if(m_endedStream)
return; return;
if(!m_startedStream) if(!m_startedStream)
return StartStream(); return StartStream();
// get rid of whitespace, etc. (in between tokens it should be irrelevent) // get rid of whitespace, etc. (in between tokens it should be irrelevent)
ScanToNextToken(); ScanToNextToken();
// check the latest simple key // check the latest simple key
VerifySimpleKey(); VerifySimpleKey();
// maybe need to end some blocks // maybe need to end some blocks
PopIndentTo(INPUT.column); PopIndentTo(INPUT.column);
// ***** // *****
// And now branch based on the next few characters! // And now branch based on the next few characters!
// ***** // *****
// end of stream // end of stream
if(INPUT.peek() == EOF) if(INPUT.peek() == EOF)
return EndStream(); return EndStream();
if(INPUT.column == 0 && INPUT.peek() == Keys::Directive) if(INPUT.column == 0 && INPUT.peek() == Keys::Directive)
return ScanDirective(); return ScanDirective();
// document token // document token
if(INPUT.column == 0 && Exp::DocStart.Matches(INPUT)) if(INPUT.column == 0 && Exp::DocStart.Matches(INPUT))
return ScanDocStart(); return ScanDocStart();
if(INPUT.column == 0 && Exp::DocEnd.Matches(INPUT)) if(INPUT.column == 0 && Exp::DocEnd.Matches(INPUT))
return ScanDocEnd(); return ScanDocEnd();
// flow start/end/entry // flow start/end/entry
if(INPUT.peek() == Keys::FlowSeqStart || INPUT.peek() == Keys::FlowMapStart) if(INPUT.peek() == Keys::FlowSeqStart || INPUT.peek() == Keys::FlowMapStart)
return ScanFlowStart(); return ScanFlowStart();
if(INPUT.peek() == Keys::FlowSeqEnd || INPUT.peek() == Keys::FlowMapEnd) if(INPUT.peek() == Keys::FlowSeqEnd || INPUT.peek() == Keys::FlowMapEnd)
return ScanFlowEnd(); return ScanFlowEnd();
if(INPUT.peek() == Keys::FlowEntry) if(INPUT.peek() == Keys::FlowEntry)
return ScanFlowEntry(); return ScanFlowEntry();
// block/map stuff // block/map stuff
if(Exp::BlockEntry.Matches(INPUT)) if(Exp::BlockEntry.Matches(INPUT))
return ScanBlockEntry(); return ScanBlockEntry();
if((m_flowLevel == 0 ? Exp::Key : Exp::KeyInFlow).Matches(INPUT)) if((m_flowLevel == 0 ? Exp::Key : Exp::KeyInFlow).Matches(INPUT))
return ScanKey(); return ScanKey();
if((m_flowLevel == 0 ? Exp::Value : Exp::ValueInFlow).Matches(INPUT)) if((m_flowLevel == 0 ? Exp::Value : Exp::ValueInFlow).Matches(INPUT))
return ScanValue(); return ScanValue();
// alias/anchor // alias/anchor
if(INPUT.peek() == Keys::Alias || INPUT.peek() == Keys::Anchor) if(INPUT.peek() == Keys::Alias || INPUT.peek() == Keys::Anchor)
return ScanAnchorOrAlias(); return ScanAnchorOrAlias();
// tag // tag
if(INPUT.peek() == Keys::Tag) if(INPUT.peek() == Keys::Tag)
return ScanTag(); return ScanTag();
// special scalars // special scalars
if(m_flowLevel == 0 && (INPUT.peek() == Keys::LiteralScalar || INPUT.peek() == Keys::FoldedScalar)) if(m_flowLevel == 0 && (INPUT.peek() == Keys::LiteralScalar || INPUT.peek() == Keys::FoldedScalar))
return ScanBlockScalar(); return ScanBlockScalar();
if(INPUT.peek() == '\'' || INPUT.peek() == '\"') if(INPUT.peek() == '\'' || INPUT.peek() == '\"')
return ScanQuotedScalar(); return ScanQuotedScalar();
// plain scalars // plain scalars
if((m_flowLevel == 0 ? Exp::PlainScalar : Exp::PlainScalarInFlow).Matches(INPUT)) if((m_flowLevel == 0 ? Exp::PlainScalar : Exp::PlainScalarInFlow).Matches(INPUT))
return ScanPlainScalar(); return ScanPlainScalar();
// don't know what it is! // don't know what it is!
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::UNKNOWN_TOKEN); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::UNKNOWN_TOKEN);
} }
// ScanToNextToken // ScanToNextToken
// . Eats input until we reach the next token-like thing. // . Eats input until we reach the next token-like thing.
void Scanner::ScanToNextToken() void Scanner::ScanToNextToken()
{ {
while(1) { while(1) {
// first eat whitespace // first eat whitespace
while(IsWhitespaceToBeEaten(INPUT.peek())) while(IsWhitespaceToBeEaten(INPUT.peek()))
INPUT.eat(1); INPUT.eat(1);
// then eat a comment // then eat a comment
if(Exp::Comment.Matches(INPUT)) { if(Exp::Comment.Matches(INPUT)) {
// eat until line break // eat until line break
while(INPUT && !Exp::Break.Matches(INPUT)) while(INPUT && !Exp::Break.Matches(INPUT))
INPUT.eat(1); INPUT.eat(1);
} }
// if it's NOT a line break, then we're done! // if it's NOT a line break, then we're done!
if(!Exp::Break.Matches(INPUT)) if(!Exp::Break.Matches(INPUT))
break; break;
// otherwise, let's eat the line break and keep going // otherwise, let's eat the line break and keep going
int n = Exp::Break.Match(INPUT); int n = Exp::Break.Match(INPUT);
INPUT.eat(n); INPUT.eat(n);
// oh yeah, and let's get rid of that simple key // oh yeah, and let's get rid of that simple key
VerifySimpleKey(); VerifySimpleKey();
// new line - we may be able to accept a simple key now // new line - we may be able to accept a simple key now
if(m_flowLevel == 0) if(m_flowLevel == 0)
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
} }
} }
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// Misc. helpers // Misc. helpers
// IsWhitespaceToBeEaten // IsWhitespaceToBeEaten
// . We can eat whitespace if: // . We can eat whitespace if:
// 1. It's a space // 1. It's a space
// 2. It's a tab, and we're either: // 2. It's a tab, and we're either:
// a. In the flow context // a. In the flow context
// b. In the block context but not where a simple key could be allowed // 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 ':') // (i.e., not at the beginning of a line, or following '-', '?', or ':')
bool Scanner::IsWhitespaceToBeEaten(char ch) bool Scanner::IsWhitespaceToBeEaten(char ch)
{ {
if(ch == ' ') if(ch == ' ')
return true; return true;
if(ch == '\t' && (m_flowLevel >= 0 || !m_simpleKeyAllowed)) if(ch == '\t' && (m_flowLevel >= 0 || !m_simpleKeyAllowed))
return true; return true;
return false; return false;
} }
// StartStream // StartStream
// . Set the initial conditions for starting a stream. // . Set the initial conditions for starting a stream.
void Scanner::StartStream() void Scanner::StartStream()
{ {
m_startedStream = true; m_startedStream = true;
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_indents.push(-1); m_indents.push(-1);
} }
// EndStream // EndStream
// . Close out the stream, finish up, etc. // . Close out the stream, finish up, etc.
void Scanner::EndStream() void Scanner::EndStream()
{ {
// force newline // force newline
if(INPUT.column > 0) if(INPUT.column > 0)
INPUT.column = 0; INPUT.column = 0;
PopIndentTo(-1); PopIndentTo(-1);
VerifyAllSimpleKeys(); VerifyAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
m_endedStream = true; m_endedStream = true;
} }
// PushIndentTo // PushIndentTo
// . Pushes an indentation onto the stack, and enqueues the // . Pushes an indentation onto the stack, and enqueues the
// proper token (sequence start or mapping start). // proper token (sequence start or mapping start).
// . Returns the token it generates (if any). // . Returns the token it generates (if any).
Token *Scanner::PushIndentTo(int column, bool sequence) Token *Scanner::PushIndentTo(int column, bool sequence)
{ {
// are we in flow? // are we in flow?
if(m_flowLevel > 0) if(m_flowLevel > 0)
return 0; return 0;
// is this actually an indentation? // is this actually an indentation?
if(column <= m_indents.top()) if(column <= m_indents.top())
return 0; return 0;
// now push // now push
m_indents.push(column); m_indents.push(column);
if(sequence) if(sequence)
m_tokens.push(Token(TT_BLOCK_SEQ_START, INPUT.line, INPUT.column)); m_tokens.push(Token(TT_BLOCK_SEQ_START, INPUT.line, INPUT.column));
else else
m_tokens.push(Token(TT_BLOCK_MAP_START, INPUT.line, INPUT.column)); m_tokens.push(Token(TT_BLOCK_MAP_START, INPUT.line, INPUT.column));
return &m_tokens.back(); return &m_tokens.back();
} }
// PopIndentTo // PopIndentTo
// . Pops indentations off the stack until we reach 'column' indentation, // . Pops indentations off the stack until we reach 'column' indentation,
// and enqueues the proper token each time. // and enqueues the proper token each time.
void Scanner::PopIndentTo(int column) void Scanner::PopIndentTo(int column)
{ {
// are we in flow? // are we in flow?
if(m_flowLevel > 0) if(m_flowLevel > 0)
return; return;
// now pop away // now pop away
while(!m_indents.empty() && m_indents.top() > column) { while(!m_indents.empty() && m_indents.top() > column) {
m_indents.pop(); m_indents.pop();
m_tokens.push(Token(TT_BLOCK_END, INPUT.line, INPUT.column)); m_tokens.push(Token(TT_BLOCK_END, INPUT.line, INPUT.column));
} }
} }
} }
#pragma once #pragma once
#include <ios> #include <ios>
#include <string> #include <string>
#include <queue> #include <queue>
#include <stack> #include <stack>
#include <set> #include <set>
#include "stream.h" #include "stream.h"
#include "token.h" #include "token.h"
namespace YAML namespace YAML
{ {
class Scanner class Scanner
{ {
public: public:
Scanner(std::istream& in); Scanner(std::istream& in);
~Scanner(); ~Scanner();
// token queue management (hopefully this looks kinda stl-ish) // token queue management (hopefully this looks kinda stl-ish)
bool empty(); bool empty();
void pop(); void pop();
Token& peek(); Token& peek();
private: private:
// scanning // scanning
void EnsureTokensInQueue(); void EnsureTokensInQueue();
void ScanNextToken(); void ScanNextToken();
void ScanToNextToken(); void ScanToNextToken();
void StartStream(); void StartStream();
void EndStream(); void EndStream();
Token *PushIndentTo(int column, bool sequence); Token *PushIndentTo(int column, bool sequence);
void PopIndentTo(int column); void PopIndentTo(int column);
// checking input // checking input
void InsertSimpleKey(); void InsertSimpleKey();
bool VerifySimpleKey(); bool VerifySimpleKey();
void VerifyAllSimpleKeys(); void VerifyAllSimpleKeys();
bool IsWhitespaceToBeEaten(char ch); bool IsWhitespaceToBeEaten(char ch);
struct SimpleKey { struct SimpleKey {
SimpleKey(int pos_, int line_, int column_, int flowLevel_); SimpleKey(int pos_, int line_, int column_, int flowLevel_);
void Validate(); void Validate();
void Invalidate(); void Invalidate();
int pos, line, column, flowLevel; int pos, line, column, flowLevel;
Token *pMapStart, *pKey; Token *pMapStart, *pKey;
}; };
// and the tokens // and the tokens
void ScanDirective(); void ScanDirective();
void ScanDocStart(); void ScanDocStart();
void ScanDocEnd(); void ScanDocEnd();
void ScanBlockSeqStart(); void ScanBlockSeqStart();
void ScanBlockMapSTart(); void ScanBlockMapSTart();
void ScanBlockEnd(); void ScanBlockEnd();
void ScanBlockEntry(); void ScanBlockEntry();
void ScanFlowStart(); void ScanFlowStart();
void ScanFlowEnd(); void ScanFlowEnd();
void ScanFlowEntry(); void ScanFlowEntry();
void ScanKey(); void ScanKey();
void ScanValue(); void ScanValue();
void ScanAnchorOrAlias(); void ScanAnchorOrAlias();
void ScanTag(); void ScanTag();
void ScanPlainScalar(); void ScanPlainScalar();
void ScanQuotedScalar(); void ScanQuotedScalar();
void ScanBlockScalar(); void ScanBlockScalar();
private: private:
// the stream // the stream
Stream INPUT; Stream INPUT;
// the output (tokens) // the output (tokens)
std::queue <Token> m_tokens; std::queue <Token> m_tokens;
// state info // state info
bool m_startedStream, m_endedStream; bool m_startedStream, m_endedStream;
bool m_simpleKeyAllowed; bool m_simpleKeyAllowed;
int m_flowLevel; // number of unclosed '[' and '{' indicators int m_flowLevel; // number of unclosed '[' and '{' indicators
bool m_isLastKeyValid; bool m_isLastKeyValid;
std::stack <SimpleKey> m_simpleKeys; std::stack <SimpleKey> m_simpleKeys;
std::stack <int> m_indents; std::stack <int> m_indents;
}; };
} }
#include "crt.h" #include "crt.h"
#include "scanscalar.h" #include "scanscalar.h"
#include "scanner.h" #include "scanner.h"
#include "exp.h" #include "exp.h"
#include "exceptions.h" #include "exceptions.h"
#include "token.h" #include "token.h"
namespace YAML namespace YAML
{ {
// ScanScalar // ScanScalar
// . This is where the scalar magic happens. // . This is where the scalar magic happens.
// //
// . We do the scanning in three phases: // . We do the scanning in three phases:
// 1. Scan until newline // 1. Scan until newline
// 2. Eat newline // 2. Eat newline
// 3. Scan leading blanks. // 3. Scan leading blanks.
// //
// . Depending on the parameters given, we store or stop // . Depending on the parameters given, we store or stop
// and different places in the above flow. // and different places in the above flow.
std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) std::string ScanScalar(Stream& INPUT, ScanScalarParams& params)
{ {
bool foundNonEmptyLine = false, pastOpeningBreak = false; bool foundNonEmptyLine = false, pastOpeningBreak = false;
bool emptyLine = false, moreIndented = false; bool emptyLine = false, moreIndented = false;
std::string scalar; std::string scalar;
params.leadingSpaces = false; params.leadingSpaces = false;
while(INPUT) { while(INPUT) {
// ******************************** // ********************************
// Phase #1: scan until line ending // Phase #1: scan until line ending
while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) { while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) {
if(INPUT.peek() == EOF) if(INPUT.peek() == EOF)
break; break;
// document indicator? // document indicator?
if(INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT)) { if(INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT)) {
if(params.onDocIndicator == BREAK) if(params.onDocIndicator == BREAK)
break; break;
else if(params.onDocIndicator == THROW) else if(params.onDocIndicator == THROW)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::DOC_IN_SCALAR); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::DOC_IN_SCALAR);
} }
foundNonEmptyLine = true; foundNonEmptyLine = true;
pastOpeningBreak = true; pastOpeningBreak = true;
// escaped newline? (only if we're escaping on slash) // escaped newline? (only if we're escaping on slash)
if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) { if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) {
int n = Exp::EscBreak.Match(INPUT); int n = Exp::EscBreak.Match(INPUT);
INPUT.eat(n); INPUT.eat(n);
continue; continue;
} }
// escape this? // escape this?
if(INPUT.peek() == params.escape) { if(INPUT.peek() == params.escape) {
scalar += Exp::Escape(INPUT); scalar += Exp::Escape(INPUT);
continue; continue;
} }
// otherwise, just add the damn character // otherwise, just add the damn character
scalar += INPUT.get(); scalar += INPUT.get();
} }
// eof? if we're looking to eat something, then we throw // eof? if we're looking to eat something, then we throw
if(INPUT.peek() == EOF) { if(INPUT.peek() == EOF) {
if(params.eatEnd) if(params.eatEnd)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::EOF_IN_SCALAR); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::EOF_IN_SCALAR);
break; break;
} }
// doc indicator? // doc indicator?
if(params.onDocIndicator == BREAK && INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT)) if(params.onDocIndicator == BREAK && INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT))
break; break;
// are we done via character match? // are we done via character match?
int n = params.end.Match(INPUT); int n = params.end.Match(INPUT);
if(n >= 0) { if(n >= 0) {
if(params.eatEnd) if(params.eatEnd)
INPUT.eat(n); INPUT.eat(n);
break; break;
} }
// ******************************** // ********************************
// Phase #2: eat line ending // Phase #2: eat line ending
n = Exp::Break.Match(INPUT); n = Exp::Break.Match(INPUT);
INPUT.eat(n); INPUT.eat(n);
// ******************************** // ********************************
// Phase #3: scan initial spaces // Phase #3: scan initial spaces
// first the required indentation // first the required indentation
while(INPUT.peek() == ' ' && (INPUT.column < params.indent || (params.detectIndent && !foundNonEmptyLine))) while(INPUT.peek() == ' ' && (INPUT.column < params.indent || (params.detectIndent && !foundNonEmptyLine)))
INPUT.eat(1); INPUT.eat(1);
// update indent if we're auto-detecting // update indent if we're auto-detecting
if(params.detectIndent && !foundNonEmptyLine) if(params.detectIndent && !foundNonEmptyLine)
params.indent = std::max(params.indent, INPUT.column); params.indent = std::max(params.indent, INPUT.column);
// and then the rest of the whitespace // and then the rest of the whitespace
while(Exp::Blank.Matches(INPUT)) { while(Exp::Blank.Matches(INPUT)) {
// we check for tabs that masquerade as indentation // we check for tabs that masquerade as indentation
if(INPUT.peek() == '\t'&& INPUT.column < params.indent && params.onTabInIndentation == THROW) if(INPUT.peek() == '\t'&& INPUT.column < params.indent && params.onTabInIndentation == THROW)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::TAB_IN_INDENTATION); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::TAB_IN_INDENTATION);
if(!params.eatLeadingWhitespace) if(!params.eatLeadingWhitespace)
break; break;
INPUT.eat(1); INPUT.eat(1);
} }
// was this an empty line? // was this an empty line?
bool nextEmptyLine = Exp::Break.Matches(INPUT); bool nextEmptyLine = Exp::Break.Matches(INPUT);
bool nextMoreIndented = (INPUT.peek() == ' '); bool nextMoreIndented = (INPUT.peek() == ' ');
// for block scalars, we always start with a newline, so we should ignore it (not fold or keep) // for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
bool useNewLine = pastOpeningBreak; bool useNewLine = pastOpeningBreak;
// and for folded scalars, we don't fold the very last newline to a space // and for folded scalars, we don't fold the very last newline to a space
if(params.fold && !emptyLine && INPUT.column < params.indent) if(params.fold && !emptyLine && INPUT.column < params.indent)
useNewLine = false; useNewLine = false;
if(useNewLine) { if(useNewLine) {
if(params.fold && !emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented) if(params.fold && !emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented)
scalar += " "; scalar += " ";
else else
scalar += "\n"; scalar += "\n";
} }
emptyLine = nextEmptyLine; emptyLine = nextEmptyLine;
moreIndented = nextMoreIndented; moreIndented = nextMoreIndented;
pastOpeningBreak = true; pastOpeningBreak = true;
// are we done via indentation? // are we done via indentation?
if(!emptyLine && INPUT.column < params.indent) { if(!emptyLine && INPUT.column < params.indent) {
params.leadingSpaces = true; params.leadingSpaces = true;
break; break;
} }
} }
// post-processing // post-processing
if(params.trimTrailingSpaces) { if(params.trimTrailingSpaces) {
unsigned pos = scalar.find_last_not_of(' '); unsigned pos = scalar.find_last_not_of(' ');
if(pos < scalar.size()) if(pos < scalar.size())
scalar.erase(pos + 1); scalar.erase(pos + 1);
} }
if(params.chomp <= 0) { if(params.chomp <= 0) {
unsigned pos = scalar.find_last_not_of('\n'); unsigned pos = scalar.find_last_not_of('\n');
if(params.chomp == 0 && pos + 1 < scalar.size()) if(params.chomp == 0 && pos + 1 < scalar.size())
scalar.erase(pos + 2); scalar.erase(pos + 2);
else if(params.chomp == -1 && pos < scalar.size()) else if(params.chomp == -1 && pos < scalar.size())
scalar.erase(pos + 1); scalar.erase(pos + 1);
} }
return scalar; return scalar;
} }
} }
#pragma once #pragma once
#include <string> #include <string>
#include "regex.h" #include "regex.h"
#include "stream.h" #include "stream.h"
namespace YAML namespace YAML
{ {
enum CHOMP { STRIP = -1, CLIP, KEEP }; enum CHOMP { STRIP = -1, CLIP, KEEP };
enum ACTION { NONE, BREAK, THROW }; enum ACTION { NONE, BREAK, THROW };
struct ScanScalarParams { struct ScanScalarParams {
ScanScalarParams(): eatEnd(false), indent(0), detectIndent(false), eatLeadingWhitespace(0), escape(0), fold(false), ScanScalarParams(): eatEnd(false), indent(0), detectIndent(false), eatLeadingWhitespace(0), escape(0), fold(false),
trimTrailingSpaces(0), chomp(CLIP), onDocIndicator(NONE), onTabInIndentation(NONE), leadingSpaces(false) {} trimTrailingSpaces(0), chomp(CLIP), onDocIndicator(NONE), onTabInIndentation(NONE), leadingSpaces(false) {}
// input: // input:
RegEx end; // what condition ends this scalar? RegEx end; // what condition ends this scalar?
bool eatEnd; // should we eat that condition when we see it? bool eatEnd; // should we eat that condition when we see it?
int indent; // what level of indentation should be eaten and ignored? int indent; // what level of indentation should be eaten and ignored?
bool detectIndent; // should we try to autodetect the indent? bool detectIndent; // should we try to autodetect the indent?
bool eatLeadingWhitespace; // should we continue eating this delicious indentation after 'indent' spaces? bool eatLeadingWhitespace; // should we continue eating this delicious indentation after 'indent' spaces?
char escape; // what character do we escape on (i.e., slash or single quote) (0 for none) char escape; // what character do we escape on (i.e., slash or single quote) (0 for none)
bool fold; // do we fold line ends? bool fold; // do we fold line ends?
bool trimTrailingSpaces; // do we remove all trailing spaces (at the very end) bool trimTrailingSpaces; // do we remove all trailing spaces (at the very end)
CHOMP chomp; // do we strip, clip, or keep trailing newlines (at the very end) CHOMP chomp; // do we strip, clip, or keep trailing newlines (at the very end)
// Note: strip means kill all, clip means keep at most one, keep means keep all // Note: strip means kill all, clip means keep at most one, keep means keep all
ACTION onDocIndicator; // what do we do if we see a document indicator? 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 ACTION onTabInIndentation; // what do we do if we see a tab where we should be seeing indentation spaces
// output: // output:
bool leadingSpaces; bool leadingSpaces;
}; };
std::string ScanScalar(Stream& INPUT, ScanScalarParams& info); std::string ScanScalar(Stream& INPUT, ScanScalarParams& info);
} }
#include "crt.h" #include "crt.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
#include "exp.h" #include "exp.h"
#include "scanscalar.h" #include "scanscalar.h"
#include <sstream> #include <sstream>
namespace YAML namespace YAML
{ {
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// Specialization for scanning specific tokens // Specialization for scanning specific tokens
// Directive // Directive
// . Note: no semantic checking is done here (that's for the parser to do) // . Note: no semantic checking is done here (that's for the parser to do)
void Scanner::ScanDirective() void Scanner::ScanDirective()
{ {
std::string name; std::string name;
std::vector <std::string> params; std::vector <std::string> params;
// pop indents and simple keys // pop indents and simple keys
PopIndentTo(-1); PopIndentTo(-1);
VerifyAllSimpleKeys(); VerifyAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
// store pos and eat indicator // store pos and eat indicator
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
INPUT.eat(1); INPUT.eat(1);
// read name // read name
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT)) while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
name += INPUT.get(); name += INPUT.get();
// read parameters // read parameters
while(1) { while(1) {
// first get rid of whitespace // first get rid of whitespace
while(Exp::Blank.Matches(INPUT)) while(Exp::Blank.Matches(INPUT))
INPUT.eat(1); INPUT.eat(1);
// break on newline or comment // break on newline or comment
if(INPUT.peek() == EOF || Exp::Break.Matches(INPUT) || Exp::Comment.Matches(INPUT)) if(INPUT.peek() == EOF || Exp::Break.Matches(INPUT) || Exp::Comment.Matches(INPUT))
break; break;
// now read parameter // now read parameter
std::string param; std::string param;
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT)) while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
param += INPUT.get(); param += INPUT.get();
params.push_back(param); params.push_back(param);
} }
Token token(TT_DIRECTIVE, line, column); Token token(TT_DIRECTIVE, line, column);
token.value = name; token.value = name;
token.params = params; token.params = params;
m_tokens.push(token); m_tokens.push(token);
} }
// DocStart // DocStart
void Scanner::ScanDocStart() void Scanner::ScanDocStart()
{ {
PopIndentTo(INPUT.column); PopIndentTo(INPUT.column);
VerifyAllSimpleKeys(); VerifyAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
// eat // eat
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
INPUT.eat(3); INPUT.eat(3);
m_tokens.push(Token(TT_DOC_START, line, column)); m_tokens.push(Token(TT_DOC_START, line, column));
} }
// DocEnd // DocEnd
void Scanner::ScanDocEnd() void Scanner::ScanDocEnd()
{ {
PopIndentTo(-1); PopIndentTo(-1);
VerifyAllSimpleKeys(); VerifyAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
// eat // eat
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
INPUT.eat(3); INPUT.eat(3);
m_tokens.push(Token(TT_DOC_END, line, column)); m_tokens.push(Token(TT_DOC_END, line, column));
} }
// FlowStart // FlowStart
void Scanner::ScanFlowStart() void Scanner::ScanFlowStart()
{ {
// flows can be simple keys // flows can be simple keys
InsertSimpleKey(); InsertSimpleKey();
m_flowLevel++; m_flowLevel++;
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
// eat // eat
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
char ch = INPUT.get(); char ch = INPUT.get();
TOKEN_TYPE type = (ch == Keys::FlowSeqStart ? TT_FLOW_SEQ_START : TT_FLOW_MAP_START); TOKEN_TYPE type = (ch == Keys::FlowSeqStart ? TT_FLOW_SEQ_START : TT_FLOW_MAP_START);
m_tokens.push(Token(type, line, column)); m_tokens.push(Token(type, line, column));
} }
// FlowEnd // FlowEnd
void Scanner::ScanFlowEnd() void Scanner::ScanFlowEnd()
{ {
if(m_flowLevel == 0) if(m_flowLevel == 0)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::FLOW_END); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::FLOW_END);
m_flowLevel--; m_flowLevel--;
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
// eat // eat
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
char ch = INPUT.get(); char ch = INPUT.get();
TOKEN_TYPE type = (ch == Keys::FlowSeqEnd ? TT_FLOW_SEQ_END : TT_FLOW_MAP_END); TOKEN_TYPE type = (ch == Keys::FlowSeqEnd ? TT_FLOW_SEQ_END : TT_FLOW_MAP_END);
m_tokens.push(Token(type, line, column)); m_tokens.push(Token(type, line, column));
} }
// FlowEntry // FlowEntry
void Scanner::ScanFlowEntry() void Scanner::ScanFlowEntry()
{ {
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
// eat // eat
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
INPUT.eat(1); INPUT.eat(1);
m_tokens.push(Token(TT_FLOW_ENTRY, line, column)); m_tokens.push(Token(TT_FLOW_ENTRY, line, column));
} }
// BlockEntry // BlockEntry
void Scanner::ScanBlockEntry() void Scanner::ScanBlockEntry()
{ {
// we better be in the block context! // we better be in the block context!
if(m_flowLevel > 0) if(m_flowLevel > 0)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::BLOCK_ENTRY); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::BLOCK_ENTRY);
// can we put it here? // can we put it here?
if(!m_simpleKeyAllowed) if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::BLOCK_ENTRY); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::BLOCK_ENTRY);
PushIndentTo(INPUT.column, true); PushIndentTo(INPUT.column, true);
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
// eat // eat
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
INPUT.eat(1); INPUT.eat(1);
m_tokens.push(Token(TT_BLOCK_ENTRY, line, column)); m_tokens.push(Token(TT_BLOCK_ENTRY, line, column));
} }
// Key // Key
void Scanner::ScanKey() void Scanner::ScanKey()
{ {
// handle keys diffently in the block context (and manage indents) // handle keys diffently in the block context (and manage indents)
if(m_flowLevel == 0) { if(m_flowLevel == 0) {
if(!m_simpleKeyAllowed) if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::MAP_KEY); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::MAP_KEY);
PushIndentTo(INPUT.column, false); PushIndentTo(INPUT.column, false);
} }
// can only put a simple key here if we're in block context // can only put a simple key here if we're in block context
if(m_flowLevel == 0) if(m_flowLevel == 0)
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
else else
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
// eat // eat
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
INPUT.eat(1); INPUT.eat(1);
m_tokens.push(Token(TT_KEY, line, column)); m_tokens.push(Token(TT_KEY, line, column));
} }
// Value // Value
void Scanner::ScanValue() void Scanner::ScanValue()
{ {
// does this follow a simple key? // does this follow a simple key?
if(m_isLastKeyValid) { if(m_isLastKeyValid) {
// can't follow a simple key with another simple key (dunno why, though - it seems fine) // can't follow a simple key with another simple key (dunno why, though - it seems fine)
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
} else { } else {
// handle values diffently in the block context (and manage indents) // handle values diffently in the block context (and manage indents)
if(m_flowLevel == 0) { if(m_flowLevel == 0) {
if(!m_simpleKeyAllowed) if(!m_simpleKeyAllowed)
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::MAP_VALUE); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::MAP_VALUE);
PushIndentTo(INPUT.column, false); PushIndentTo(INPUT.column, false);
} }
// can only put a simple key here if we're in block context // can only put a simple key here if we're in block context
if(m_flowLevel == 0) if(m_flowLevel == 0)
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
else else
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
} }
// eat // eat
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
INPUT.eat(1); INPUT.eat(1);
m_tokens.push(Token(TT_VALUE, line, column)); m_tokens.push(Token(TT_VALUE, line, column));
} }
// AnchorOrAlias // AnchorOrAlias
void Scanner::ScanAnchorOrAlias() void Scanner::ScanAnchorOrAlias()
{ {
bool alias; bool alias;
std::string name; std::string name;
// insert a potential simple key // insert a potential simple key
if(m_simpleKeyAllowed) if(m_simpleKeyAllowed)
InsertSimpleKey(); InsertSimpleKey();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
// eat the indicator // eat the indicator
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
char indicator = INPUT.get(); char indicator = INPUT.get();
alias = (indicator == Keys::Alias); alias = (indicator == Keys::Alias);
// now eat the content // now eat the content
while(Exp::AlphaNumeric.Matches(INPUT)) while(Exp::AlphaNumeric.Matches(INPUT))
name += INPUT.get(); name += INPUT.get();
// we need to have read SOMETHING! // we need to have read SOMETHING!
if(name.empty()) if(name.empty())
throw ParserException(INPUT.line, INPUT.column, alias ? ErrorMsg::ALIAS_NOT_FOUND : ErrorMsg::ANCHOR_NOT_FOUND); throw ParserException(INPUT.line, INPUT.column, alias ? ErrorMsg::ALIAS_NOT_FOUND : ErrorMsg::ANCHOR_NOT_FOUND);
// and needs to end correctly // and needs to end correctly
if(INPUT.peek() != EOF && !Exp::AnchorEnd.Matches(INPUT)) if(INPUT.peek() != EOF && !Exp::AnchorEnd.Matches(INPUT))
throw ParserException(INPUT.line, INPUT.column, alias ? ErrorMsg::CHAR_IN_ALIAS : ErrorMsg::CHAR_IN_ANCHOR); throw ParserException(INPUT.line, INPUT.column, alias ? ErrorMsg::CHAR_IN_ALIAS : ErrorMsg::CHAR_IN_ANCHOR);
// and we're done // and we're done
Token token(alias ? TT_ALIAS : TT_ANCHOR, line, column); Token token(alias ? TT_ALIAS : TT_ANCHOR, line, column);
token.value = name; token.value = name;
m_tokens.push(token); m_tokens.push(token);
} }
// Tag // Tag
void Scanner::ScanTag() void Scanner::ScanTag()
{ {
std::string handle, suffix; std::string handle, suffix;
// insert a potential simple key // insert a potential simple key
if(m_simpleKeyAllowed) if(m_simpleKeyAllowed)
InsertSimpleKey(); InsertSimpleKey();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
// eat the indicator // eat the indicator
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
handle += INPUT.get(); handle += INPUT.get();
// read the handle // read the handle
while(INPUT.peek() != EOF && INPUT.peek() != Keys::Tag && !Exp::BlankOrBreak.Matches(INPUT)) while(INPUT.peek() != EOF && INPUT.peek() != Keys::Tag && !Exp::BlankOrBreak.Matches(INPUT))
handle += INPUT.get(); handle += INPUT.get();
// is there a suffix? // is there a suffix?
if(INPUT.peek() == Keys::Tag) { if(INPUT.peek() == Keys::Tag) {
// eat the indicator // eat the indicator
handle += INPUT.get(); handle += INPUT.get();
// then read it // then read it
while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT)) while(INPUT.peek() != EOF && !Exp::BlankOrBreak.Matches(INPUT))
suffix += INPUT.get(); suffix += INPUT.get();
} else { } else {
// this is a bit weird: we keep just the '!' as the handle and move the rest to the suffix // this is a bit weird: we keep just the '!' as the handle and move the rest to the suffix
suffix = handle.substr(1); suffix = handle.substr(1);
handle = "!"; handle = "!";
} }
Token token(TT_TAG, line, column); Token token(TT_TAG, line, column);
token.value = handle; token.value = handle;
token.params.push_back(suffix); token.params.push_back(suffix);
m_tokens.push(token); m_tokens.push(token);
} }
// PlainScalar // PlainScalar
void Scanner::ScanPlainScalar() void Scanner::ScanPlainScalar()
{ {
std::string scalar; std::string scalar;
// set up the scanning parameters // set up the scanning parameters
ScanScalarParams params; ScanScalarParams params;
params.end = (m_flowLevel > 0 ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment); params.end = (m_flowLevel > 0 ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment);
params.eatEnd = false; params.eatEnd = false;
params.indent = (m_flowLevel > 0 ? 0 : m_indents.top() + 1); params.indent = (m_flowLevel > 0 ? 0 : m_indents.top() + 1);
params.fold = true; params.fold = true;
params.eatLeadingWhitespace = true; params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = true; params.trimTrailingSpaces = true;
params.chomp = CLIP; params.chomp = CLIP;
params.onDocIndicator = BREAK; params.onDocIndicator = BREAK;
params.onTabInIndentation = THROW; params.onTabInIndentation = THROW;
// insert a potential simple key // insert a potential simple key
if(m_simpleKeyAllowed) if(m_simpleKeyAllowed)
InsertSimpleKey(); InsertSimpleKey();
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
scalar = ScanScalar(INPUT, params); scalar = ScanScalar(INPUT, params);
// can have a simple key only if we ended the scalar by starting a new line // can have a simple key only if we ended the scalar by starting a new line
m_simpleKeyAllowed = params.leadingSpaces; m_simpleKeyAllowed = params.leadingSpaces;
// finally, check and see if we ended on an illegal character // finally, check and see if we ended on an illegal character
//if(Exp::IllegalCharInScalar.Matches(INPUT)) //if(Exp::IllegalCharInScalar.Matches(INPUT))
// throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_SCALAR); // throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_SCALAR);
Token token(TT_SCALAR, line, column); Token token(TT_SCALAR, line, column);
token.value = scalar; token.value = scalar;
m_tokens.push(token); m_tokens.push(token);
} }
// QuotedScalar // QuotedScalar
void Scanner::ScanQuotedScalar() void Scanner::ScanQuotedScalar()
{ {
std::string scalar; std::string scalar;
// eat single or double quote // eat single or double quote
char quote = INPUT.get(); char quote = INPUT.get();
bool single = (quote == '\''); bool single = (quote == '\'');
// setup the scanning parameters // setup the scanning parameters
ScanScalarParams params; ScanScalarParams params;
params.end = (single ? RegEx(quote) && !Exp::EscSingleQuote : RegEx(quote)); params.end = (single ? RegEx(quote) && !Exp::EscSingleQuote : RegEx(quote));
params.eatEnd = true; params.eatEnd = true;
params.escape = (single ? '\'' : '\\'); params.escape = (single ? '\'' : '\\');
params.indent = 0; params.indent = 0;
params.fold = true; params.fold = true;
params.eatLeadingWhitespace = true; params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = false; params.trimTrailingSpaces = false;
params.chomp = CLIP; params.chomp = CLIP;
params.onDocIndicator = THROW; params.onDocIndicator = THROW;
// insert a potential simple key // insert a potential simple key
if(m_simpleKeyAllowed) if(m_simpleKeyAllowed)
InsertSimpleKey(); InsertSimpleKey();
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
scalar = ScanScalar(INPUT, params); scalar = ScanScalar(INPUT, params);
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
Token token(TT_SCALAR, line, column); Token token(TT_SCALAR, line, column);
token.value = scalar; token.value = scalar;
m_tokens.push(token); m_tokens.push(token);
} }
// BlockScalarToken // BlockScalarToken
// . These need a little extra processing beforehand. // . 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), // . 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. // and then we need to figure out what level of indentation we'll be using.
void Scanner::ScanBlockScalar() void Scanner::ScanBlockScalar()
{ {
std::string scalar; std::string scalar;
ScanScalarParams params; ScanScalarParams params;
params.indent = 1; params.indent = 1;
params.detectIndent = true; params.detectIndent = true;
// eat block indicator ('|' or '>') // eat block indicator ('|' or '>')
int line = INPUT.line, column = INPUT.column; int line = INPUT.line, column = INPUT.column;
char indicator = INPUT.get(); char indicator = INPUT.get();
params.fold = (indicator == Keys::FoldedScalar); params.fold = (indicator == Keys::FoldedScalar);
// eat chomping/indentation indicators // eat chomping/indentation indicators
int n = Exp::Chomp.Match(INPUT); int n = Exp::Chomp.Match(INPUT);
for(int i=0;i<n;i++) { for(int i=0;i<n;i++) {
char ch = INPUT.get(); char ch = INPUT.get();
if(ch == '+') if(ch == '+')
params.chomp = KEEP; params.chomp = KEEP;
else if(ch == '-') else if(ch == '-')
params.chomp = STRIP; params.chomp = STRIP;
else if(Exp::Digit.Matches(ch)) { else if(Exp::Digit.Matches(ch)) {
if(ch == '0') if(ch == '0')
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::ZERO_INDENT_IN_BLOCK); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::ZERO_INDENT_IN_BLOCK);
params.indent = ch - '0'; params.indent = ch - '0';
params.detectIndent = false; params.detectIndent = false;
} }
} }
// now eat whitespace // now eat whitespace
while(Exp::Blank.Matches(INPUT)) while(Exp::Blank.Matches(INPUT))
INPUT.eat(1); INPUT.eat(1);
// and comments to the end of the line // and comments to the end of the line
if(Exp::Comment.Matches(INPUT)) if(Exp::Comment.Matches(INPUT))
while(INPUT && !Exp::Break.Matches(INPUT)) while(INPUT && !Exp::Break.Matches(INPUT))
INPUT.eat(1); INPUT.eat(1);
// if it's not a line break, then we ran into a bad character inline // if it's not a line break, then we ran into a bad character inline
if(INPUT && !Exp::Break.Matches(INPUT)) if(INPUT && !Exp::Break.Matches(INPUT))
throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_BLOCK); throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_BLOCK);
// set the initial indentation // set the initial indentation
if(m_indents.top() >= 0) if(m_indents.top() >= 0)
params.indent += m_indents.top(); params.indent += m_indents.top();
params.eatLeadingWhitespace = false; params.eatLeadingWhitespace = false;
params.trimTrailingSpaces = false; params.trimTrailingSpaces = false;
params.onTabInIndentation = THROW; params.onTabInIndentation = THROW;
scalar = ScanScalar(INPUT, params); scalar = ScanScalar(INPUT, params);
// simple keys always ok after block scalars (since we're gonna start a new line anyways) // simple keys always ok after block scalars (since we're gonna start a new line anyways)
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
Token token(TT_SCALAR, line, column); Token token(TT_SCALAR, line, column);
token.value = scalar; token.value = scalar;
m_tokens.push(token); m_tokens.push(token);
} }
} }
#include "crt.h" #include "crt.h"
#include "sequence.h" #include "sequence.h"
#include "node.h" #include "node.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include <iostream> #include <iostream>
namespace YAML namespace YAML
{ {
Sequence::Sequence() Sequence::Sequence()
{ {
} }
Sequence::~Sequence() Sequence::~Sequence()
{ {
Clear(); Clear();
} }
void Sequence::Clear() void Sequence::Clear()
{ {
for(unsigned i=0;i<m_data.size();i++) for(unsigned i=0;i<m_data.size();i++)
delete m_data[i]; delete m_data[i];
m_data.clear(); m_data.clear();
} }
bool Sequence::GetBegin(std::vector <Node *>::const_iterator& it) const bool Sequence::GetBegin(std::vector <Node *>::const_iterator& it) const
{ {
it = m_data.begin(); it = m_data.begin();
return true; return true;
} }
bool Sequence::GetEnd(std::vector <Node *>::const_iterator& it) const bool Sequence::GetEnd(std::vector <Node *>::const_iterator& it) const
{ {
it = m_data.end(); it = m_data.end();
return true; return true;
} }
Node *Sequence::GetNode(unsigned i) const Node *Sequence::GetNode(unsigned i) const
{ {
if(i < m_data.size()) if(i < m_data.size())
return m_data[i]; return m_data[i];
return 0; return 0;
} }
unsigned Sequence::GetSize() const unsigned Sequence::GetSize() const
{ {
return m_data.size(); return m_data.size();
} }
void Sequence::Parse(Scanner *pScanner, const ParserState& state) void Sequence::Parse(Scanner *pScanner, const ParserState& state)
{ {
Clear(); Clear();
// split based on start token // split based on start token
switch(pScanner->peek().type) { switch(pScanner->peek().type) {
case TT_BLOCK_SEQ_START: ParseBlock(pScanner, state); break; case TT_BLOCK_SEQ_START: ParseBlock(pScanner, state); break;
case TT_BLOCK_ENTRY: ParseImplicit(pScanner, state); break; case TT_BLOCK_ENTRY: ParseImplicit(pScanner, state); break;
case TT_FLOW_SEQ_START: ParseFlow(pScanner, state); break; case TT_FLOW_SEQ_START: ParseFlow(pScanner, state); break;
} }
} }
void Sequence::ParseBlock(Scanner *pScanner, const ParserState& state) void Sequence::ParseBlock(Scanner *pScanner, const ParserState& state)
{ {
// eat start token // eat start token
pScanner->pop(); pScanner->pop();
while(1) { while(1) {
if(pScanner->empty()) if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_SEQ); throw ParserException(-1, -1, ErrorMsg::END_OF_SEQ);
Token token = pScanner->peek(); Token token = pScanner->peek();
if(token.type != TT_BLOCK_ENTRY && token.type != TT_BLOCK_END) if(token.type != TT_BLOCK_ENTRY && token.type != TT_BLOCK_END)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_SEQ); throw ParserException(token.line, token.column, ErrorMsg::END_OF_SEQ);
pScanner->pop(); pScanner->pop();
if(token.type == TT_BLOCK_END) if(token.type == TT_BLOCK_END)
break; break;
Node *pNode = new Node; Node *pNode = new Node;
m_data.push_back(pNode); m_data.push_back(pNode);
pNode->Parse(pScanner, state); pNode->Parse(pScanner, state);
} }
} }
void Sequence::ParseImplicit(Scanner *pScanner, const ParserState& state) void Sequence::ParseImplicit(Scanner *pScanner, const ParserState& state)
{ {
while(1) { while(1) {
// we're actually *allowed* to have no tokens at some point // we're actually *allowed* to have no tokens at some point
if(pScanner->empty()) if(pScanner->empty())
break; break;
// and we end at anything other than a block entry // and we end at anything other than a block entry
Token& token = pScanner->peek(); Token& token = pScanner->peek();
if(token.type != TT_BLOCK_ENTRY) if(token.type != TT_BLOCK_ENTRY)
break; break;
pScanner->pop(); pScanner->pop();
Node *pNode = new Node; Node *pNode = new Node;
m_data.push_back(pNode); m_data.push_back(pNode);
pNode->Parse(pScanner, state); pNode->Parse(pScanner, state);
} }
} }
void Sequence::ParseFlow(Scanner *pScanner, const ParserState& state) void Sequence::ParseFlow(Scanner *pScanner, const ParserState& state)
{ {
// eat start token // eat start token
pScanner->pop(); pScanner->pop();
while(1) { while(1) {
if(pScanner->empty()) if(pScanner->empty())
throw ParserException(-1, -1, ErrorMsg::END_OF_SEQ_FLOW); throw ParserException(-1, -1, ErrorMsg::END_OF_SEQ_FLOW);
// first check for end // first check for end
if(pScanner->peek().type == TT_FLOW_SEQ_END) { if(pScanner->peek().type == TT_FLOW_SEQ_END) {
pScanner->pop(); pScanner->pop();
break; break;
} }
// then read the node // then read the node
Node *pNode = new Node; Node *pNode = new Node;
m_data.push_back(pNode); m_data.push_back(pNode);
pNode->Parse(pScanner, state); 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) // 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(); Token& token = pScanner->peek();
if(token.type == TT_FLOW_ENTRY) if(token.type == TT_FLOW_ENTRY)
pScanner->pop(); pScanner->pop();
else if(token.type != TT_FLOW_SEQ_END) else if(token.type != TT_FLOW_SEQ_END)
throw ParserException(token.line, token.column, ErrorMsg::END_OF_SEQ_FLOW); throw ParserException(token.line, token.column, ErrorMsg::END_OF_SEQ_FLOW);
} }
} }
void Sequence::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine) void Sequence::Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine)
{ {
if(startedLine && !onlyOneCharOnLine) if(startedLine && !onlyOneCharOnLine)
out << "\n"; out << "\n";
for(unsigned i=0;i<m_data.size();i++) { for(unsigned i=0;i<m_data.size();i++) {
if((startedLine && !onlyOneCharOnLine) || i > 0) { if((startedLine && !onlyOneCharOnLine) || i > 0) {
for(int j=0;j<indent;j++) for(int j=0;j<indent;j++)
out << " "; out << " ";
} }
out << "- "; out << "- ";
m_data[i]->Write(out, indent + 1, true, i > 0 || !startedLine || onlyOneCharOnLine); m_data[i]->Write(out, indent + 1, true, i > 0 || !startedLine || onlyOneCharOnLine);
} }
if(m_data.empty()) if(m_data.empty())
out << "\n"; out << "\n";
} }
int Sequence::Compare(Content *pContent) int Sequence::Compare(Content *pContent)
{ {
return -pContent->Compare(this); return -pContent->Compare(this);
} }
int Sequence::Compare(Sequence *pSeq) int Sequence::Compare(Sequence *pSeq)
{ {
unsigned n = m_data.size(), m = pSeq->m_data.size(); unsigned n = m_data.size(), m = pSeq->m_data.size();
if(n < m) if(n < m)
return -1; return -1;
else if(n > m) else if(n > m)
return 1; return 1;
for(unsigned i=0;i<n;i++) { for(unsigned i=0;i<n;i++) {
int cmp = m_data[i]->Compare(*pSeq->m_data[i]); int cmp = m_data[i]->Compare(*pSeq->m_data[i]);
if(cmp != 0) if(cmp != 0)
return cmp; return cmp;
} }
return 0; return 0;
} }
} }
#pragma once #pragma once
#include "content.h" #include "content.h"
#include <vector> #include <vector>
namespace YAML namespace YAML
{ {
class Node; class Node;
class Sequence: public Content class Sequence: public Content
{ {
public: public:
Sequence(); Sequence();
virtual ~Sequence(); virtual ~Sequence();
void Clear(); void Clear();
virtual bool GetBegin(std::vector <Node *>::const_iterator& it) const; virtual bool GetBegin(std::vector <Node *>::const_iterator& it) const;
virtual bool GetEnd(std::vector <Node *>::const_iterator& it) const; virtual bool GetEnd(std::vector <Node *>::const_iterator& it) const;
virtual Node *GetNode(unsigned i) const; virtual Node *GetNode(unsigned i) const;
virtual unsigned GetSize() const; virtual unsigned GetSize() const;
virtual void Parse(Scanner *pScanner, const ParserState& state); virtual void Parse(Scanner *pScanner, const ParserState& state);
virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine); virtual void Write(std::ostream& out, int indent, bool startedLine, bool onlyOneCharOnLine);
virtual bool IsSequence() const { return true; } virtual bool IsSequence() const { return true; }
// ordering // ordering
virtual int Compare(Content *pContent); virtual int Compare(Content *pContent);
virtual int Compare(Scalar *pScalar) { return 1; } virtual int Compare(Scalar *pScalar) { return 1; }
virtual int Compare(Sequence *pSeq); virtual int Compare(Sequence *pSeq);
virtual int Compare(Map *pMap) { return -1; } virtual int Compare(Map *pMap) { return -1; }
private: private:
void ParseBlock(Scanner *pScanner, const ParserState& state); void ParseBlock(Scanner *pScanner, const ParserState& state);
void ParseImplicit(Scanner *pScanner, const ParserState& state); void ParseImplicit(Scanner *pScanner, const ParserState& state);
void ParseFlow(Scanner *pScanner, const ParserState& state); void ParseFlow(Scanner *pScanner, const ParserState& state);
protected: protected:
std::vector <Node *> m_data; std::vector <Node *> m_data;
}; };
} }
#include "crt.h" #include "crt.h"
#include "scanner.h" #include "scanner.h"
#include "token.h" #include "token.h"
#include "exceptions.h" #include "exceptions.h"
#include "exp.h" #include "exp.h"
namespace YAML namespace YAML
{ {
Scanner::SimpleKey::SimpleKey(int pos_, int line_, int column_, int flowLevel_) Scanner::SimpleKey::SimpleKey(int pos_, int line_, int column_, int flowLevel_)
: pos(pos_), line(line_), column(column_), flowLevel(flowLevel_), pMapStart(0), pKey(0) : pos(pos_), line(line_), column(column_), flowLevel(flowLevel_), pMapStart(0), pKey(0)
{ {
} }
void Scanner::SimpleKey::Validate() void Scanner::SimpleKey::Validate()
{ {
if(pMapStart) if(pMapStart)
pMapStart->status = TS_VALID; pMapStart->status = TS_VALID;
if(pKey) if(pKey)
pKey->status = TS_VALID; pKey->status = TS_VALID;
} }
void Scanner::SimpleKey::Invalidate() void Scanner::SimpleKey::Invalidate()
{ {
if(pMapStart) if(pMapStart)
pMapStart->status = TS_INVALID; pMapStart->status = TS_INVALID;
if(pKey) if(pKey)
pKey->status = TS_INVALID; pKey->status = TS_INVALID;
} }
// InsertSimpleKey // InsertSimpleKey
// . Adds a potential simple key to the queue, // . Adds a potential simple key to the queue,
// and saves it on a stack. // and saves it on a stack.
void Scanner::InsertSimpleKey() void Scanner::InsertSimpleKey()
{ {
SimpleKey key(INPUT.pos(), INPUT.line, INPUT.column, m_flowLevel); SimpleKey key(INPUT.pos(), INPUT.line, INPUT.column, m_flowLevel);
// first add a map start, if necessary // first add a map start, if necessary
key.pMapStart = PushIndentTo(INPUT.column, false); key.pMapStart = PushIndentTo(INPUT.column, false);
if(key.pMapStart) if(key.pMapStart)
key.pMapStart->status = TS_UNVERIFIED; key.pMapStart->status = TS_UNVERIFIED;
// then add the (now unverified) key // then add the (now unverified) key
m_tokens.push(Token(TT_KEY, INPUT.line, INPUT.column)); m_tokens.push(Token(TT_KEY, INPUT.line, INPUT.column));
key.pKey = &m_tokens.back(); key.pKey = &m_tokens.back();
key.pKey->status = TS_UNVERIFIED; key.pKey->status = TS_UNVERIFIED;
m_simpleKeys.push(key); m_simpleKeys.push(key);
} }
// VerifySimpleKey // VerifySimpleKey
// . Determines whether the latest simple key to be added is valid, // . Determines whether the latest simple key to be added is valid,
// and if so, makes it valid. // and if so, makes it valid.
bool Scanner::VerifySimpleKey() bool Scanner::VerifySimpleKey()
{ {
m_isLastKeyValid = false; m_isLastKeyValid = false;
if(m_simpleKeys.empty()) if(m_simpleKeys.empty())
return m_isLastKeyValid; return m_isLastKeyValid;
// grab top key // grab top key
SimpleKey key = m_simpleKeys.top(); SimpleKey key = m_simpleKeys.top();
// only validate if we're in the correct flow level // only validate if we're in the correct flow level
if(key.flowLevel != m_flowLevel) if(key.flowLevel != m_flowLevel)
return false; return false;
m_simpleKeys.pop(); m_simpleKeys.pop();
bool isValid = true; bool isValid = true;
// needs to be followed immediately by a value // needs to be followed immediately by a value
if(m_flowLevel > 0 && !Exp::ValueInFlow.Matches(INPUT)) if(m_flowLevel > 0 && !Exp::ValueInFlow.Matches(INPUT))
isValid = false; isValid = false;
if(m_flowLevel == 0 && !Exp::Value.Matches(INPUT)) if(m_flowLevel == 0 && !Exp::Value.Matches(INPUT))
isValid = false; isValid = false;
// also needs to be less than 1024 characters and inline // also needs to be less than 1024 characters and inline
if(INPUT.line != key.line || INPUT.pos() - key.pos > 1024) if(INPUT.line != key.line || INPUT.pos() - key.pos > 1024)
isValid = false; isValid = false;
// invalidate key // invalidate key
if(isValid) if(isValid)
key.Validate(); key.Validate();
else else
key.Invalidate(); key.Invalidate();
// In block style, remember that we've pushed an indent for this potential simple key (if it was starting). // 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. // 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 // 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. // between) since keys have to be inline, and will be invalidated immediately on a newline.
if(!isValid && m_flowLevel == 0) if(!isValid && m_flowLevel == 0)
m_indents.pop(); m_indents.pop();
m_isLastKeyValid = isValid; m_isLastKeyValid = isValid;
return isValid; return isValid;
} }
void Scanner::VerifyAllSimpleKeys() void Scanner::VerifyAllSimpleKeys()
{ {
while(!m_simpleKeys.empty()) while(!m_simpleKeys.empty())
VerifySimpleKey(); VerifySimpleKey();
} }
} }
#include "crt.h" #include "crt.h"
#include "stream.h" #include "stream.h"
#include <iostream> #include <iostream>
namespace YAML namespace YAML
{ {
int Stream::pos() const int Stream::pos() const
{ {
return input.tellg(); return input.tellg();
} }
char Stream::peek() char Stream::peek()
{ {
return input.peek(); return input.peek();
} }
Stream::operator bool() Stream::operator bool()
{ {
return input.good(); return input.good();
} }
// get // get
// . Extracts a character from the stream and updates our position // . Extracts a character from the stream and updates our position
char Stream::get() char Stream::get()
{ {
char ch = input.get(); char ch = input.get();
column++; column++;
if(ch == '\n') { if(ch == '\n') {
column = 0; column = 0;
line++; line++;
} }
return ch; return ch;
} }
// get // get
// . Extracts 'n' characters from the stream and updates our position // . Extracts 'n' characters from the stream and updates our position
std::string Stream::get(int n) std::string Stream::get(int n)
{ {
std::string ret; std::string ret;
for(int i=0;i<n;i++) for(int i=0;i<n;i++)
ret += get(); ret += get();
return ret; return ret;
} }
// eat // eat
// . Eats 'n' characters and updates our position. // . Eats 'n' characters and updates our position.
void Stream::eat(int n) void Stream::eat(int n)
{ {
for(int i=0;i<n;i++) for(int i=0;i<n;i++)
get(); get();
} }
} }
#pragma once #pragma once
#include <ios> #include <ios>
#include <string> #include <string>
namespace YAML namespace YAML
{ {
struct Stream struct Stream
{ {
Stream(std::istream& input_): input(input_), line(0), column(0) {} Stream(std::istream& input_): input(input_), line(0), column(0) {}
int pos() const; int pos() const;
operator bool(); operator bool();
bool operator !() { return !(*this); } bool operator !() { return !(*this); }
std::istream& stream() const { return input; } std::istream& stream() const { return input; }
char peek(); char peek();
char get(); char get();
std::string get(int n); std::string get(int n);
void eat(int n = 1); void eat(int n = 1);
std::istream& input; std::istream& input;
int line, column; int line, column;
}; };
} }
#pragma once #pragma once
#include <ios> #include <ios>
#include <string> #include <string>
#include <vector> #include <vector>
namespace YAML namespace YAML
{ {
enum TOKEN_STATUS { TS_VALID, TS_INVALID, TS_UNVERIFIED }; enum TOKEN_STATUS { TS_VALID, TS_INVALID, TS_UNVERIFIED };
enum TOKEN_TYPE { enum TOKEN_TYPE {
TT_DIRECTIVE, TT_DIRECTIVE,
TT_DOC_START, TT_DOC_START,
TT_DOC_END, TT_DOC_END,
TT_BLOCK_SEQ_START, TT_BLOCK_SEQ_START,
TT_BLOCK_MAP_START, TT_BLOCK_MAP_START,
TT_BLOCK_END, TT_BLOCK_END,
TT_BLOCK_ENTRY, TT_BLOCK_ENTRY,
TT_FLOW_SEQ_START, TT_FLOW_SEQ_START,
TT_FLOW_MAP_START, TT_FLOW_MAP_START,
TT_FLOW_SEQ_END, TT_FLOW_SEQ_END,
TT_FLOW_MAP_END, TT_FLOW_MAP_END,
TT_FLOW_ENTRY, TT_FLOW_ENTRY,
TT_KEY, TT_KEY,
TT_VALUE, TT_VALUE,
TT_ANCHOR, TT_ANCHOR,
TT_ALIAS, TT_ALIAS,
TT_TAG, TT_TAG,
TT_SCALAR, TT_SCALAR,
}; };
const std::string TokenNames[] = { const std::string TokenNames[] = {
"DIRECTIVE", "DIRECTIVE",
"DOC_START", "DOC_START",
"DOC_END", "DOC_END",
"BLOCK_SEQ_START", "BLOCK_SEQ_START",
"BLOCK_MAP_START", "BLOCK_MAP_START",
"BLOCK_END", "BLOCK_END",
"BLOCK_ENTRY", "BLOCK_ENTRY",
"FLOW_SEQ_START", "FLOW_SEQ_START",
"FLOW_MAP_START", "FLOW_MAP_START",
"FLOW_SEQ_END", "FLOW_SEQ_END",
"FLOW_MAP_END", "FLOW_MAP_END",
"FLOW_ENTRY", "FLOW_ENTRY",
"KEY", "KEY",
"VALUE", "VALUE",
"ANCHOR", "ANCHOR",
"ALIAS", "ALIAS",
"TAG", "TAG",
"SCALAR", "SCALAR",
}; };
struct Token { struct Token {
Token(TOKEN_TYPE type_, int line_, int column_): status(TS_VALID), type(type_), line(line_), column(column_) {} 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) { friend std::ostream& operator << (std::ostream& out, const Token& token) {
out << TokenNames[token.type] << std::string(": ") << token.value; out << TokenNames[token.type] << std::string(": ") << token.value;
for(unsigned i=0;i<token.params.size();i++) for(unsigned i=0;i<token.params.size();i++)
out << std::string(" ") << token.params[i]; out << std::string(" ") << token.params[i];
return out; return out;
} }
TOKEN_STATUS status; TOKEN_STATUS status;
TOKEN_TYPE type; TOKEN_TYPE type;
int line, column; int line, column;
std::string value; std::string value;
std::vector <std::string> params; std::vector <std::string> params;
}; };
} }
#include "yaml.h" #include "yaml.h"
#include "tests.h" #include "tests.h"
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#ifdef _MSC_VER #ifdef _MSC_VER
#ifdef _DEBUG #ifdef _DEBUG
#pragma comment(lib, "yamlcppd.lib") #pragma comment(lib, "yamlcppd.lib")
#else #else
#pragma comment(lib, "yamlcpp.lib") #pragma comment(lib, "yamlcpp.lib")
#endif // _DEBUG #endif // _DEBUG
#endif // _MSC_VER #endif // _MSC_VER
void run() void run()
{ {
std::ifstream fin("tests/test.yaml"); std::ifstream fin("tests/test.yaml");
try { try {
YAML::Parser parser(fin); YAML::Parser parser(fin);
parser.PrintTokens(std::cout); parser.PrintTokens(std::cout);
} catch(YAML::Exception&) { } catch(YAML::Exception&) {
std::cout << "Error parsing the yaml!\n"; std::cout << "Error parsing the yaml!\n";
} }
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
bool verbose = false; bool verbose = false;
for(int i=1;i<argc;i++) { for(int i=1;i<argc;i++) {
if(strcmp(argv[i], "-v") == 0) if(strcmp(argv[i], "-v") == 0)
verbose = true; verbose = true;
} }
#ifdef WINDOWS #ifdef WINDOWS
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF); _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF);
#endif // WINDOWS #endif // WINDOWS
Test::RunAll(verbose); Test::RunAll(verbose);
run(); run();
return 0; return 0;
} }
#include "yaml.h" #include "yaml.h"
#include "tests.h" #include "tests.h"
#include "parser.h" #include "parser.h"
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include <iostream> #include <iostream>
namespace Test namespace Test
{ {
// runs all the tests on all data we have // runs all the tests on all data we have
void RunAll(bool verbose) void RunAll(bool verbose)
{ {
std::vector <std::string> files; std::vector <std::string> files;
files.push_back("tests/simple.yaml"); files.push_back("tests/simple.yaml");
files.push_back("tests/mixed.yaml"); files.push_back("tests/mixed.yaml");
files.push_back("tests/scalars.yaml"); files.push_back("tests/scalars.yaml");
files.push_back("tests/directives.yaml"); files.push_back("tests/directives.yaml");
bool passed = true; bool passed = true;
for(unsigned i=0;i<files.size();i++) { for(unsigned i=0;i<files.size();i++) {
if(!Inout(files[i], verbose)) { if(!Inout(files[i], verbose)) {
std::cout << "Inout test failed on " << files[i] << "\n"; std::cout << "Inout test failed on " << files[i] << "\n";
passed = false; passed = false;
} else } else
std::cout << "Inout test passed: " << files[i] << "\n"; std::cout << "Inout test passed: " << files[i] << "\n";
} }
if(passed) if(passed)
std::cout << "All tests passed!\n"; std::cout << "All tests passed!\n";
} }
// loads the given YAML file, outputs it, and then loads the outputted file, // loads the given YAML file, outputs it, and then loads the outputted file,
// outputs again, and makes sure that the two outputs are the same // outputs again, and makes sure that the two outputs are the same
bool Inout(const std::string& file, bool verbose) bool Inout(const std::string& file, bool verbose)
{ {
std::ifstream fin(file.c_str()); std::ifstream fin(file.c_str());
try { try {
// read and output // read and output
YAML::Parser parser(fin); YAML::Parser parser(fin);
if(!parser) if(!parser)
return false; return false;
YAML::Node doc; YAML::Node doc;
parser.GetNextDocument(doc); parser.GetNextDocument(doc);
std::stringstream out; std::stringstream out;
out << doc; out << doc;
// and save // and save
std::string firstTry = out.str(); std::string firstTry = out.str();
// and now again // and now again
parser.Load(out); parser.Load(out);
if(!parser) if(!parser)
return false; return false;
parser.GetNextDocument(doc); parser.GetNextDocument(doc);
std::stringstream out2; std::stringstream out2;
out2 << doc; out2 << doc;
// and save // and save
std::string secondTry = out2.str(); std::string secondTry = out2.str();
// now compare // now compare
if(firstTry == secondTry) if(firstTry == secondTry)
return true; return true;
std::ofstream fout("tests/out.yaml"); std::ofstream fout("tests/out.yaml");
fout << "---\n"; fout << "---\n";
fout << firstTry << std::endl; fout << firstTry << std::endl;
fout << "---\n"; fout << "---\n";
fout << secondTry << std::endl; fout << secondTry << std::endl;
} catch(YAML::ParserException& e) { } catch(YAML::ParserException& e) {
std::cout << file << " (line " << e.line + 1 << ", col " << e.column + 1 << "): " << e.msg << std::endl; std::cout << file << " (line " << e.line + 1 << ", col " << e.column + 1 << "): " << e.msg << std::endl;
if(verbose) { if(verbose) {
std::cout << "Token queue:\n"; std::cout << "Token queue:\n";
std::ifstream f(file.c_str()); std::ifstream f(file.c_str());
YAML::Parser p(f); YAML::Parser p(f);
p.PrintTokens(std::cout); p.PrintTokens(std::cout);
} }
return false; return false;
} }
return true; return true;
} }
} }
#include <string> #include <string>
namespace Test { namespace Test {
void RunAll(bool verbose); void RunAll(bool verbose);
bool Inout(const std::string& file, bool verbose); bool Inout(const std::string& file, bool verbose);
} }
%YAML 1.2 %YAML 1.2
%TAG ! !howdy %TAG ! !howdy
--- ---
- basic node - basic node
- ! yeah baby - ! yeah baby
\ No newline at end of file
- the main thing is a sequence - the main thing is a sequence
- here's a key: value - here's a key: value
and another: value and another: value
- let's inline: [1, 2, 3] - let's inline: [1, 2, 3]
and an inline map: {key: value, 243: 101} and an inline map: {key: value, 243: 101}
- and multiple indents: - and multiple indents:
- here's - here's
- a - a
- list - list
and another: and another:
- list - list
- of - of
- things - things
- maybe now: - maybe now:
let's: get let's: get
pretty: pretty:
deep: here deep: here
in: in:
the: nesting the: nesting
just: to just: to
confuse: confuse:
the: heck the: heck
out: out:
- of - of
- the: parser - the: parser
if: if:
- we - we
- can - can
- do: that - do: that
what: do what: do
you: think? you: think?
\ No newline at end of file
- normal scalar, but - normal scalar, but
over several lines over several lines
- | - |
literal scalar - so we can draw ASCII: literal scalar - so we can draw ASCII:
- - - -
| - | | - |
------ ------
- > - >
and a folded scalar... so we and a folded scalar... so we
can just keep writing various can just keep writing various
things. And if we want to keep indentation: things. And if we want to keep indentation:
we just indent a little we just indent a little
see, this stays indented see, this stays indented
- >- - >-
Here's a folded scalar Here's a folded scalar
that gets chomped. that gets chomped.
- |- - |-
And here's a literal scalar And here's a literal scalar
that gets chomped. that gets chomped.
- >2 - >2
Here's a folded scalar Here's a folded scalar
that starts with some indentation. that starts with some indentation.
- ::vector - ::vector
- ": - ()" - ": - ()"
- Up, up, and away! - Up, up, and away!
- -123 - -123
- http://example.com/foo#bar - http://example.com/foo#bar
# Inside flow collection: # Inside flow collection:
- [ ::vector, - [ ::vector,
": - ()", ": - ()",
"Up, up and away!", "Up, up and away!",
-123, -123,
http://example.com/foo#bar ] http://example.com/foo#bar ]
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment