Commit b43f8271 authored by jbeder's avatar jbeder
Browse files

Set the eol style to native for all files.

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