From 6c2946bf58e148463ee559a464038729efb12b62 Mon Sep 17 00:00:00 2001 From: Jesse Beder Date: Tue, 8 Jul 2008 05:48:38 +0000 Subject: [PATCH] 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. --- exceptions.h | 48 ++++++------------------------- exp.cpp | 18 ++++++++---- map.cpp | 10 +++---- parser.cpp | 33 ++++++++++----------- parser.h | 8 +++--- scanner.cpp | 8 +++--- scanscalar.cpp | 6 ++-- scantoken.cpp | 78 +++++++++++++++++++++++++++++++++----------------- sequence.cpp | 8 +++--- simplekey.cpp | 4 +-- tests.cpp | 3 +- token.h | 3 +- 12 files changed, 114 insertions(+), 113 deletions(-) diff --git a/exceptions.h b/exceptions.h index 585963b..017b0b1 100644 --- a/exceptions.h +++ b/exceptions.h @@ -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 {}; diff --git a/exp.cpp b/exp.cpp index 493654c..f766de6 100644 --- a/exp.cpp +++ b/exp.cpp @@ -1,11 +1,12 @@ #include "exp.h" #include "exceptions.h" +#include 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= 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()); } } } diff --git a/map.cpp b/map.cpp index e23c2af..0687f34 100644 --- a/map.cpp +++ b/map.cpp @@ -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) { diff --git a/parser.cpp b/parser.cpp index 46c92b8..82935d1 100644 --- a/parser.cpp +++ b/parser.cpp @@ -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 & 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 & 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 & 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; } diff --git a/parser.h b/parser.h index 33c24c1..2b9b8ea 100644 --- a/parser.h +++ b/parser.h @@ -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 & params); - void HandleYamlDirective(const std::vector & params); - void HandleTagDirective(const std::vector & params); + void HandleDirective(Token *pToken); + void HandleYamlDirective(Token *pToken); + void HandleTagDirective(Token *pToken); private: Scanner *m_pScanner; diff --git a/scanner.cpp b/scanner.cpp index a231b9d..cfa330d 100644 --- a/scanner.cpp +++ b/scanner.cpp @@ -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)); } } } diff --git a/scanscalar.cpp b/scanscalar.cpp index 76b0238..c83c091 100644 --- a/scanscalar.cpp +++ b/scanscalar.cpp @@ -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; diff --git a/scantoken.cpp b/scantoken.cpp index 05bae3d..04dbfa8 100644 --- a/scantoken.cpp +++ b/scantoken.cpp @@ -3,6 +3,7 @@ #include "exceptions.h" #include "exp.h" #include "scanscalar.h" +#include 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); } diff --git a/sequence.cpp b/sequence.cpp index 1ec82de..b4cd495 100644 --- a/sequence.cpp +++ b/sequence.cpp @@ -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"); } } diff --git a/simplekey.cpp b/simplekey.cpp index 3f17265..040c398 100644 --- a/simplekey.cpp +++ b/simplekey.cpp @@ -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); diff --git a/tests.cpp b/tests.cpp index 01df50b..98b5a3b 100644 --- a/tests.cpp +++ b/tests.cpp @@ -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; } diff --git a/token.h b/token.h index 17bd20d..3606fd0 100644 --- a/token.h +++ b/token.h @@ -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 params; };