Combined the myriad ScannerExceptions and ParserExceptions to a single ParserException class that has a message and a line/column position in the file where the error occurred.

This commit is contained in:
Jesse Beder
2008-07-08 05:48:38 +00:00
parent 115cf601e9
commit 6c2946bf58
12 changed files with 114 additions and 113 deletions

View File

@@ -5,48 +5,16 @@
namespace YAML
{
class Exception: public std::exception {};
class ScannerException: public Exception {};
class ParserException: public Exception {};
class ParserException: public Exception {
public:
ParserException(int line_, int column_, const std::string& msg_)
: line(line_), column(column_), msg(msg_) {}
int line, column;
std::string msg;
};
class RepresentationException: public Exception {};
// scanner exceptions
class UnknownToken: public ScannerException {};
class IllegalBlockEntry: public ScannerException {};
class IllegalMapKey: public ScannerException {};
class IllegalMapValue: public ScannerException {};
class IllegalScalar: public ScannerException {};
class IllegalTabInIndentation: public ScannerException {};
class IllegalFlowEnd: public ScannerException {};
class IllegalDocIndicator: public ScannerException {};
class IllegalEOF: public ScannerException {};
class RequiredSimpleKeyNotFound: public ScannerException {};
class ZeroIndentationInBlockScalar: public ScannerException {};
class UnexpectedCharacterInBlockScalar: public ScannerException {};
class AnchorNotFound: public ScannerException {};
class IllegalCharacterInAnchor: public ScannerException {};
class UnknownEscapeSequence: public ScannerException {
public:
UnknownEscapeSequence(char ch_): ch(ch_) {}
char ch;
};
class NonHexNumber: public ScannerException {
public:
NonHexNumber(char ch_): ch(ch_) {}
char ch;
};
class InvalidUnicode: public ScannerException {
public:
InvalidUnicode(unsigned value_): value(value_) {}
unsigned value;
};
// parser exceptions
class MapEndNotFound: public ParserException {};
class SeqEndNotFound: public ParserException {};
class BadYAMLDirective: public ParserException {};
class BadTAGDirective: public ParserException {};
// representation exceptions
class InvalidScalar: public RepresentationException {};
class BadDereference: public RepresentationException {};

18
exp.cpp
View File

@@ -1,11 +1,12 @@
#include "exp.h"
#include "exceptions.h"
#include <sstream>
namespace YAML
{
namespace Exp
{
unsigned ParseHex(std::string str)
unsigned ParseHex(const std::string& str, int line, int column)
{
unsigned value = 0;
for(unsigned i=0;i<str.size();i++) {
@@ -18,7 +19,7 @@ namespace YAML
else if('0' <= ch && ch <= '9')
digit = ch - '0';
else
throw NonHexNumber(ch);
throw ParserException(line, column, "bad character found while scanning hex number");
value = (value << 4) + digit;
}
@@ -42,11 +43,14 @@ namespace YAML
str += in.get();
// get the value
unsigned value = ParseHex(str);
unsigned value = ParseHex(str, in.line, in.column);
// legal unicode?
if((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF)
throw InvalidUnicode(value);
if((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) {
std::stringstream msg;
msg << "invalid unicode: " << value;
throw ParserException(in.line, in.column, msg.str());
}
// now break it up into chars
if(value <= 0x7F)
@@ -101,7 +105,9 @@ namespace YAML
case 'U': return Escape(in, 8);
}
throw UnknownEscapeSequence(ch);
std::stringstream msg;
msg << "unknown escape character: " << ch;
throw ParserException(in.line, in.column, msg.str());
}
}
}

10
map.cpp
View File

@@ -57,10 +57,10 @@ namespace YAML
while(1) {
Token *pToken = pScanner->PeekNextToken();
if(!pToken)
throw MapEndNotFound();
throw ParserException(-1, -1, "end of map not found");
if(pToken->type != TT_KEY && pToken->type != TT_BLOCK_END)
throw MapEndNotFound();
throw ParserException(pToken->line, pToken->column, "end of map not found");
pScanner->PopNextToken();
if(pToken->type == TT_BLOCK_END)
@@ -96,7 +96,7 @@ namespace YAML
while(1) {
Token *pToken = pScanner->PeekNextToken();
if(!pToken)
throw MapEndNotFound();
throw ParserException(-1, -1, "end of map flow not found");
// first check for end
if(pToken->type == TT_FLOW_MAP_END) {
@@ -106,7 +106,7 @@ namespace YAML
// now it better be a key
if(pToken->type != TT_KEY)
throw MapEndNotFound();
throw ParserException(pToken->line, pToken->column, "end of map flow not found");
pScanner->PopNextToken();
@@ -128,7 +128,7 @@ namespace YAML
if(pToken->type == TT_FLOW_ENTRY)
pScanner->EatNextToken();
else if(pToken->type != TT_FLOW_MAP_END)
throw MapEndNotFound();
throw ParserException(pToken->line, pToken->column, "end of map flow not found");
m_data[pKey] = pValue;
} catch(Exception& e) {

View File

@@ -30,7 +30,7 @@ namespace YAML
// GetNextDocument
// . Reads the next document in the queue (of tokens).
// . Throws (ScannerException|ParserException)s on errors.
// . Throws (ParserException|ParserException)s on errors.
void Parser::GetNextDocument(Node& document)
{
// clear node
@@ -72,42 +72,43 @@ namespace YAML
m_state.Reset();
readDirective = true;
HandleDirective(pToken->value, pToken->params);
HandleDirective(pToken);
m_pScanner->PopNextToken();
}
}
void Parser::HandleDirective(const std::string& name, const std::vector <std::string>& params)
void Parser::HandleDirective(Token *pToken)
{
if(name == "YAML")
HandleYamlDirective(params);
else if(name == "TAG")
HandleTagDirective(params);
if(pToken->value == "YAML")
HandleYamlDirective(pToken);
else if(pToken->value == "TAG")
HandleTagDirective(pToken);
}
// HandleYamlDirective
// . Should be of the form 'major.minor' (like a version number)
void Parser::HandleYamlDirective(const std::vector <std::string>& params)
void Parser::HandleYamlDirective(Token *pToken)
{
if(params.size() != 1)
throw BadYAMLDirective();
if(pToken->params.size() != 1)
throw ParserException(pToken->line, pToken->column, "YAML directives must have exactly one argument");
std::stringstream str(params[0]);
std::stringstream str(pToken->params[0]);
str >> m_state.version.major;
str.get();
str >> m_state.version.minor;
if(!str)
throw BadYAMLDirective(); // TODO: or throw if there are any more characters in the stream?
throw ParserException(pToken->line, pToken->column, "bad YAML directive");
// TODO: or throw if there are any more characters in the stream?
// TODO: throw on major > 1? warning on major == 1, minor > 2?
}
void Parser::HandleTagDirective(const std::vector <std::string>& params)
void Parser::HandleTagDirective(Token *pToken)
{
if(params.size() != 2)
throw BadTAGDirective();
if(pToken->params.size() != 2)
throw ParserException(pToken->line, pToken->column, "TAG directives must have exactly two arguments");
std::string handle = params[0], prefix = params[1];
std::string handle = pToken->params[0], prefix = pToken->params[1];
m_state.tags[handle] = prefix;
}

View File

@@ -9,8 +9,8 @@
namespace YAML
{
class Node;
class Scanner;
struct Token;
class Parser
{
@@ -26,9 +26,9 @@ namespace YAML
private:
void ParseDirectives();
void HandleDirective(const std::string& name, const std::vector <std::string>& params);
void HandleYamlDirective(const std::vector <std::string>& params);
void HandleTagDirective(const std::vector <std::string>& params);
void HandleDirective(Token *pToken);
void HandleYamlDirective(Token *pToken);
void HandleTagDirective(Token *pToken);
private:
Scanner *m_pScanner;

View File

@@ -157,7 +157,7 @@ namespace YAML
return ScanPlainScalar();
// don't know what it is!
throw UnknownToken();
throw ParserException(INPUT.line, INPUT.column, "unknown token");
}
// ScanToNextToken
@@ -256,9 +256,9 @@ namespace YAML
m_indents.push(column);
Token *pToken = 0;
if(sequence)
pToken = new Token(TT_BLOCK_SEQ_START);
pToken = new Token(TT_BLOCK_SEQ_START, INPUT.line, INPUT.column);
else
pToken = new Token(TT_BLOCK_MAP_START);
pToken = new Token(TT_BLOCK_MAP_START, INPUT.line, INPUT.column);
m_tokens.push(pToken);
return pToken;
@@ -276,7 +276,7 @@ namespace YAML
// now pop away
while(!m_indents.empty() && m_indents.top() > column) {
m_indents.pop();
m_tokens.push(new Token(TT_BLOCK_END));
m_tokens.push(new Token(TT_BLOCK_END, INPUT.line, INPUT.column));
}
}
}

View File

@@ -35,7 +35,7 @@ namespace YAML
if(params.onDocIndicator == BREAK)
break;
else if(params.onDocIndicator == THROW)
throw IllegalDocIndicator();
throw ParserException(INPUT.line, INPUT.column, "illegal document indicator in scalar");
}
foundNonEmptyLine = true;
@@ -61,7 +61,7 @@ namespace YAML
// eof? if we're looking to eat something, then we throw
if(INPUT.peek() == EOF) {
if(params.eatEnd)
throw IllegalEOF();
throw ParserException(INPUT.line, INPUT.column, "illegal EOF in scalar");
break;
}
@@ -97,7 +97,7 @@ namespace YAML
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 IllegalTabInIndentation();
throw ParserException(INPUT.line, INPUT.column, "illegal tab when looking for indentation");
if(!params.eatLeadingWhitespace)
break;

View File

@@ -3,6 +3,7 @@
#include "exceptions.h"
#include "exp.h"
#include "scanscalar.h"
#include <sstream>
namespace YAML
{
@@ -22,7 +23,8 @@ namespace YAML
m_simpleKeyAllowed = false;
// eat indicator
// store pos and eat indicator
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
// read name
@@ -47,7 +49,7 @@ namespace YAML
params.push_back(param);
}
Token *pToken = new Token(TT_DIRECTIVE);
Token *pToken = new Token(TT_DIRECTIVE, line, column);
pToken->value = name;
pToken->params = params;
m_tokens.push(pToken);
@@ -61,8 +63,9 @@ namespace YAML
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(3);
m_tokens.push(new Token(TT_DOC_START));
m_tokens.push(new Token(TT_DOC_START, line, column));
}
// DocEnd
@@ -73,8 +76,9 @@ namespace YAML
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(3);
m_tokens.push(new Token(TT_DOC_END));
m_tokens.push(new Token(TT_DOC_END, line, column));
}
// FlowStart
@@ -86,24 +90,26 @@ namespace YAML
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(new Token(type));
m_tokens.push(new Token(type, line, column));
}
// FlowEnd
void Scanner::ScanFlowEnd()
{
if(m_flowLevel == 0)
throw IllegalFlowEnd();
throw ParserException(INPUT.line, INPUT.column, "illegal 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(new Token(type));
m_tokens.push(new Token(type, line, column));
}
// FlowEntry
@@ -112,8 +118,9 @@ namespace YAML
m_simpleKeyAllowed = true;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(new Token(TT_FLOW_ENTRY));
m_tokens.push(new Token(TT_FLOW_ENTRY, line, column));
}
// BlockEntry
@@ -121,18 +128,19 @@ namespace YAML
{
// we better be in the block context!
if(m_flowLevel > 0)
throw IllegalBlockEntry();
throw ParserException(INPUT.line, INPUT.column, "illegal block entry");
// can we put it here?
if(!m_simpleKeyAllowed)
throw IllegalBlockEntry();
throw ParserException(INPUT.line, INPUT.column, "illegal block entry");
PushIndentTo(INPUT.column, true);
m_simpleKeyAllowed = true;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(new Token(TT_BLOCK_ENTRY));
m_tokens.push(new Token(TT_BLOCK_ENTRY, line, column));
}
// Key
@@ -141,7 +149,7 @@ namespace YAML
// handle keys diffently in the block context (and manage indents)
if(m_flowLevel == 0) {
if(!m_simpleKeyAllowed)
throw IllegalMapKey();
throw ParserException(INPUT.line, INPUT.column, "illegal map key");
PushIndentTo(INPUT.column, false);
}
@@ -153,8 +161,9 @@ namespace YAML
m_simpleKeyAllowed = false;
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(new Token(TT_KEY));
m_tokens.push(new Token(TT_KEY, line, column));
}
// Value
@@ -168,7 +177,7 @@ namespace YAML
// handle values diffently in the block context (and manage indents)
if(m_flowLevel == 0) {
if(!m_simpleKeyAllowed)
throw IllegalMapValue();
throw ParserException(INPUT.line, INPUT.column, "illegal map value");
PushIndentTo(INPUT.column, false);
}
@@ -181,8 +190,9 @@ namespace YAML
}
// eat
int line = INPUT.line, column = INPUT.column;
INPUT.eat(1);
m_tokens.push(new Token(TT_VALUE));
m_tokens.push(new Token(TT_VALUE, line, column));
}
// AnchorOrAlias
@@ -197,6 +207,7 @@ namespace YAML
m_simpleKeyAllowed = false;
// eat the indicator
int line = INPUT.line, column = INPUT.column;
char indicator = INPUT.get();
alias = (indicator == Keys::Alias);
@@ -205,15 +216,24 @@ namespace YAML
name += INPUT.get();
// we need to have read SOMETHING!
if(name.empty())
throw AnchorNotFound();
if(name.empty()) {
std::stringstream msg;
msg << (alias ? "alias" : "anchor");
msg << " not found after ";
msg << (alias ? "*" : "&");
throw ParserException(INPUT.line, INPUT.column, msg.str());
}
// and needs to end correctly
if(INPUT.peek() != EOF && !Exp::AnchorEnd.Matches(INPUT))
throw IllegalCharacterInAnchor();
if(INPUT.peek() != EOF && !Exp::AnchorEnd.Matches(INPUT)) {
std::stringstream msg;
msg << "illegal character found while scanning ";
msg << (alias ? "alias" : "anchor");
throw ParserException(INPUT.line, INPUT.column, msg.str());
}
// and we're done
Token *pToken = new Token(alias ? TT_ALIAS : TT_ANCHOR);
Token *pToken = new Token(alias ? TT_ALIAS : TT_ANCHOR, line, column);
pToken->value = name;
m_tokens.push(pToken);
}
@@ -229,6 +249,7 @@ namespace YAML
m_simpleKeyAllowed = false;
// eat the indicator
int line = INPUT.line, column = INPUT.column;
handle += INPUT.get();
// read the handle
@@ -249,7 +270,7 @@ namespace YAML
handle = "!";
}
Token *pToken = new Token(TT_TAG);
Token *pToken = new Token(TT_TAG, line, column);
pToken->value = handle;
pToken->params.push_back(suffix);
m_tokens.push(pToken);
@@ -276,6 +297,7 @@ namespace YAML
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
@@ -284,9 +306,9 @@ namespace YAML
// finally, we can't have any colons in a scalar, so if we ended on a colon, there
// had better be a break after it
if(Exp::IllegalColonInScalar.Matches(INPUT))
throw IllegalScalar();
throw ParserException(INPUT.line, INPUT.column, "illegal character in scalar");
Token *pToken = new Token(TT_SCALAR);
Token *pToken = new Token(TT_SCALAR, line, column);
pToken->value = scalar;
m_tokens.push(pToken);
}
@@ -316,10 +338,11 @@ namespace YAML
if(m_simpleKeyAllowed)
InsertSimpleKey();
int line = INPUT.line, column = INPUT.column;
scalar = ScanScalar(INPUT, params);
m_simpleKeyAllowed = false;
Token *pToken = new Token(TT_SCALAR);
Token *pToken = new Token(TT_SCALAR, line, column);
pToken->value = scalar;
m_tokens.push(pToken);
}
@@ -337,6 +360,7 @@ namespace YAML
params.detectIndent = true;
// eat block indicator ('|' or '>')
int line = INPUT.line, column = INPUT.column;
char indicator = INPUT.get();
params.fold = (indicator == Keys::FoldedScalar);
@@ -350,7 +374,7 @@ namespace YAML
params.chomp = STRIP;
else if(Exp::Digit.Matches(ch)) {
if(ch == '0')
throw ZeroIndentationInBlockScalar();
throw ParserException(INPUT.line, INPUT.column, "cannot set zero indentation for a block scalar");
params.indent = ch - '0';
params.detectIndent = false;
@@ -368,7 +392,7 @@ namespace YAML
// if it's not a line break, then we ran into a bad character inline
if(INPUT && !Exp::Break.Matches(INPUT))
throw UnexpectedCharacterInBlockScalar();
throw ParserException(INPUT.line, INPUT.column, "unexpected character in block scalar");
// set the initial indentation
if(m_indents.top() >= 0)
@@ -383,7 +407,7 @@ namespace YAML
// simple keys always ok after block scalars (since we're gonna start a new line anyways)
m_simpleKeyAllowed = true;
Token *pToken = new Token(TT_SCALAR);
Token *pToken = new Token(TT_SCALAR, line, column);
pToken->value = scalar;
m_tokens.push(pToken);
}

View File

@@ -68,10 +68,10 @@ namespace YAML
while(1) {
Token *pToken = pScanner->PeekNextToken();
if(!pToken)
throw SeqEndNotFound();
throw ParserException(-1, -1, "end of sequence not found");
if(pToken->type != TT_BLOCK_ENTRY && pToken->type != TT_BLOCK_END)
throw SeqEndNotFound();
throw ParserException(pToken->line, pToken->column, "end of sequence not found");
pScanner->PopNextToken();
if(pToken->type == TT_BLOCK_END)
@@ -111,7 +111,7 @@ namespace YAML
while(1) {
Token *pToken = pScanner->PeekNextToken();
if(!pToken)
throw SeqEndNotFound();
throw ParserException(-1, -1, "end of sequence flow not found");
// first check for end
if(pToken->type == TT_FLOW_SEQ_END) {
@@ -129,7 +129,7 @@ namespace YAML
if(pToken->type == TT_FLOW_ENTRY)
pScanner->EatNextToken();
else if(pToken->type != TT_FLOW_SEQ_END)
throw SeqEndNotFound();
throw ParserException(pToken->line, pToken->column, "end of sequence flow not found");
}
}

View File

@@ -21,7 +21,7 @@ namespace YAML
void Scanner::SimpleKey::Invalidate()
{
if(required)
throw RequiredSimpleKeyNotFound();
throw ParserException(line, column, "required simple key not found");
if(pMapStart)
pMapStart->status = TS_INVALID;
@@ -44,7 +44,7 @@ namespace YAML
// key.required = true; // TODO: is this correct?
// then add the (now unverified) key
key.pKey = new Token(TT_KEY);
key.pKey = new Token(TT_KEY, INPUT.line, INPUT.column);
key.pKey->status = TS_UNVERIFIED;
m_tokens.push(key.pKey);

View File

@@ -71,7 +71,8 @@ namespace YAML
fout << secondTry << std::endl;
return false;
} catch(Exception&) {
} catch(ParserException& e) {
std::cout << file << " (line " << e.line + 1 << ", col " << e.column + 1 << "): " << e.msg << std::endl;
return false;
}

View File

@@ -50,7 +50,7 @@ namespace YAML
};
struct Token {
Token(TOKEN_TYPE type_): status(TS_VALID), type(type_) {}
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] << ": " << token.value;
@@ -61,6 +61,7 @@ namespace YAML
TOKEN_STATUS status;
TOKEN_TYPE type;
int line, column;
std::string value;
std::vector <std::string> params;
};