diff --git a/include/myxml/error.hpp b/include/myxml/error.hpp index 61d0356..f46fb19 100644 --- a/include/myxml/error.hpp +++ b/include/myxml/error.hpp @@ -12,9 +12,11 @@ namespace myxml std::string message; // store message after being concated with prefix mutable std::string fullMessage; + std::size_t line; + std::size_t column; public: - ParseError(std::string message); + ParseError(std::string message, std::size_t line, std::size_t column); virtual const char *what() const noexcept override; }; @@ -31,7 +33,7 @@ namespace myxml virtual const char *prefix() const; public: - SyntaxError(std::string); + SyntaxError(std::string, std::size_t line, std::size_t column); }; /** @@ -43,7 +45,7 @@ namespace myxml virtual const char *prefix() const; public: - SemanticError(std::string); + SemanticError(std::string, std::size_t line, std::size_t column); }; /** @@ -55,7 +57,7 @@ namespace myxml virtual const char *prefix() const; public: - UnexpectedEndOfInput(); + UnexpectedEndOfInput(std::size_t line, std::size_t column); }; class IOError : public std::exception diff --git a/include/myxml/parser.hpp b/include/myxml/parser.hpp index 7478e41..0c8ad1c 100644 --- a/include/myxml/parser.hpp +++ b/include/myxml/parser.hpp @@ -33,6 +33,7 @@ namespace myxml std::optional take(); std::optional takeN(int); void skipWhiteSpaces(); + std::tuple currentLoc(); /** * For all parsing method, diff --git a/src/error.cpp b/src/error.cpp index e97241c..8dc02ee 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -1,15 +1,17 @@ +#include #include "myxml/error.hpp" namespace myxml { - ParseError::ParseError(std::string message) - : message(message) + ParseError::ParseError(std::string message, std::size_t line, std::size_t column) + : message(message), line(line), column(column) { } const char *ParseError::what() const noexcept { - this->fullMessage = this->prefix() + this->message; + this->fullMessage = fmt::format("{}{}\nin line: {} column: {}", + this->prefix(), this->message, this->line, this->column); return this->fullMessage.c_str(); } @@ -18,8 +20,8 @@ namespace myxml return "Syntax Error: "; } - SyntaxError::SyntaxError(std::string message) - : ParseError(message) + SyntaxError::SyntaxError(std::string, std::size_t line, std::size_t column) + : ParseError(message, line, column) { } @@ -28,8 +30,8 @@ namespace myxml return "Sematic Error: "; } - SemanticError::SemanticError(std::string message) - : ParseError(message) + SemanticError::SemanticError(std::string, std::size_t line, std::size_t column) + : ParseError(message, line, column) { } @@ -38,8 +40,8 @@ namespace myxml return "Unexpected End of Input: "; } - UnexpectedEndOfInput::UnexpectedEndOfInput() - : ParseError("End of input") + UnexpectedEndOfInput::UnexpectedEndOfInput(std::size_t line, std::size_t column) + : ParseError("End of input", line, column) { } diff --git a/src/parser.cpp b/src/parser.cpp index 743ef3a..cce6fef 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -13,6 +13,11 @@ namespace myxml } } + std::tuple Parser::currentLoc() + { + return this->buffer->CurrentLocation(); + } + std::optional Parser::peek() { return this->buffer->Peek(); @@ -46,11 +51,15 @@ namespace myxml std::string Parser::parseIdent() { if (this->peek() == std::nullopt) - throw UnexpectedEndOfInput(); + { + auto [line, col] = this->currentLoc(); + throw UnexpectedEndOfInput(line, col); + } // validate heading character if (auto head = this->peek(); !head || (!std::isalpha(*head) && head != '_')) { - throw SyntaxError(fmt::format("element name which starts with {} is invalid.", *head)); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("element name which starts with {} is invalid.", *head), line, col); } std::size_t len = 0; while (this->afterN(len) && util::isValidXmlChar(*this->afterN(len))) @@ -64,11 +73,13 @@ namespace myxml { if (!this->peek()) { - throw UnexpectedEndOfInput(); + auto [line, col] = this->currentLoc(); + throw UnexpectedEndOfInput(line, col); } if (this->peek() != '"') { - throw SyntaxError(fmt::format("expected '\"' at the beginning of string literal, find {}", *this->peek())); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("expected '\"' at the beginning of string literal, find {}", *this->peek()), line, col); } this->take(); std::size_t len = 0; @@ -78,7 +89,8 @@ namespace myxml } if (!this->afterN(len)) { // if jump out due to length limit - throw SyntaxError(fmt::format("missing closing double quote for string literal")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("missing closing double quote for string literal"), line, col); } auto it = this->takeN(len); this->take(); // skip " @@ -104,7 +116,8 @@ namespace myxml } if (this->take() != '=') { - throw SyntaxError(fmt::format("expected '=' after attribute name")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("expected '=' after attribute name"), line, col); } attr.first = key; auto value = this->parseStringLiteral(); @@ -116,7 +129,8 @@ namespace myxml { if (!this->peek()) { - throw UnexpectedEndOfInput(); + auto [line, col] = this->currentLoc(); + throw UnexpectedEndOfInput(line, col); } std::size_t len = 0; while (this->afterN(len) != '<') @@ -125,7 +139,8 @@ namespace myxml } if (!this->afterN(len)) { // if jump out of while loop due to length limit - throw SyntaxError(fmt::format("expected '<' after text")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("expected '<' after text"), line, col); } return std::shared_ptr(new Text(*this->takeN(len))); } @@ -144,7 +159,8 @@ namespace myxml } if (!this->afterN(len + 2)) { - throw SyntaxError(fmt::format("expected \"]]>\" after CDATA")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("expected \"]]>\" after CDATA"), line, col); } auto it = std::string(*this->takeN(len)); this->takeN(2); @@ -176,14 +192,16 @@ namespace myxml { if (tag.type != ElementTag::ClosingType::Open) { - throw SyntaxError(fmt::format("unexpected ending '/' found in closing tag")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("unexpected ending '/' found in closing tag"), line, col); } tag.type = ElementTag::ClosingType::Closed; this->take(); } if (this->take() != '>') { - throw SyntaxError(fmt::format("expected '>' at the end of the tag")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("expected '>' at the end of the tag"), line, col); } return tag; } @@ -227,7 +245,8 @@ namespace myxml case ElementTag::ClosingType::Closing: if (tag->name != elem->GetName()) { - throw SyntaxError(fmt::format("elem name in closing tag is mismatched with the header")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("elem name in closing tag is mismatched with the header"), line, col); } if (!header.attris.empty()) { @@ -245,7 +264,8 @@ namespace myxml break; } } - throw UnexpectedEndOfInput(); + auto [line, col] = this->currentLoc(); + throw UnexpectedEndOfInput(line, col); } std::shared_ptr Parser::ParseElement() @@ -269,7 +289,8 @@ namespace myxml } else // Closing { - throw SyntaxError(fmt::format("unexpected closing tag")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("unexpected closing tag"), line, col); } } else @@ -293,7 +314,8 @@ namespace myxml this->skipWhiteSpaces(); if (this->takeN(2) != "?>") { - throw SyntaxError(fmt::format("expected \"?>\" at end of xml declaration")); + auto [line, col] = this->currentLoc(); + throw SyntaxError(fmt::format("expected \"?>\" at end of xml declaration"), line, col); } if (auto decl = Declaration::BuildFromAttrs(attrs); decl) { @@ -301,7 +323,8 @@ namespace myxml } else { - throw SemanticError(fmt::format("declaration has incorrect attributes")); + auto [line, col] = this->currentLoc(); + throw SemanticError(fmt::format("declaration has incorrect attributes"), line, col); } } @@ -318,7 +341,8 @@ namespace myxml } else { - throw SemanticError(fmt::format("missing root element in xml document")); + auto [line, col] = this->currentLoc(); + throw SemanticError(fmt::format("missing root element in xml document"), line, col); } return document; }