diff options
author | Benjamin Paassen <bpaassen@techfak.uni-bielefeld.de> | 2015-01-23 15:47:59 +0100 |
---|---|---|
committer | Benjamin Paassen <bpaassen@techfak.uni-bielefeld.de> | 2015-01-23 15:47:59 +0100 |
commit | 18d3637ca02ab69f1ee744fa94c43c243de0f571 (patch) | |
tree | 42c859f014ab7dbb7d31a747e0ef3839c77c60fa /src/core/common | |
parent | 85d72823ef18711fe7a29f5b23cc37b318766332 (diff) | |
parent | aa817d3bfd90aa39b6fd8a915bc78a8bb210cd3d (diff) |
Merge branch 'master' of somweyr.de:ousia
Diffstat (limited to 'src/core/common')
-rw-r--r-- | src/core/common/CharReader.cpp | 220 | ||||
-rw-r--r-- | src/core/common/CharReader.hpp | 173 | ||||
-rw-r--r-- | src/core/common/Exceptions.cpp | 16 | ||||
-rw-r--r-- | src/core/common/Exceptions.hpp | 26 | ||||
-rw-r--r-- | src/core/common/Location.cpp | 33 | ||||
-rw-r--r-- | src/core/common/Location.hpp | 426 | ||||
-rw-r--r-- | src/core/common/Logger.cpp | 222 | ||||
-rw-r--r-- | src/core/common/Logger.hpp | 448 | ||||
-rw-r--r-- | src/core/common/Rtti.cpp | 39 | ||||
-rw-r--r-- | src/core/common/Rtti.hpp | 21 | ||||
-rw-r--r-- | src/core/common/Utils.cpp | 23 | ||||
-rw-r--r-- | src/core/common/Utils.hpp | 20 |
12 files changed, 981 insertions, 686 deletions
diff --git a/src/core/common/CharReader.cpp b/src/core/common/CharReader.cpp index 6966b97..6fd3d45 100644 --- a/src/core/common/CharReader.cpp +++ b/src/core/common/CharReader.cpp @@ -376,50 +376,39 @@ bool Buffer::fetch(CursorId cursor, char &c) return fetchCharacter(cursor, c, false); } -/* CharReader::Cursor class */ - -void CharReader::Cursor::assign(std::shared_ptr<Buffer> buffer, - CharReader::Cursor &cursor) -{ - // Copy the cursor position - buffer->copyCursor(cursor.cursor, this->cursor); - - // Copy the state - line = cursor.line; - column = cursor.column; -} - /* CharReader class */ -CharReader::CharReader(std::shared_ptr<Buffer> buffer, size_t line, - size_t column) +CharReader::CharReader(std::shared_ptr<Buffer> buffer, SourceId sourceId, + size_t offs) : buffer(buffer), - readCursor(buffer->createCursor(), line, column), - peekCursor(buffer->createCursor(), line, column), - coherent(true) + readCursor(buffer->createCursor()), + peekCursor(buffer->createCursor()), + coherent(true), + sourceId(sourceId), + offs(offs) { } -CharReader::CharReader(const std::string &str, size_t line, size_t column) - : CharReader(std::shared_ptr<Buffer>{new Buffer{str}}, line, column) +CharReader::CharReader(const std::string &str, SourceId sourceId, size_t offs) + : CharReader(std::shared_ptr<Buffer>{new Buffer{str}}, sourceId, offs) { } -CharReader::CharReader(std::istream &istream, size_t line, size_t column) - : CharReader(std::shared_ptr<Buffer>{new Buffer{istream}}, line, column) +CharReader::CharReader(std::istream &istream, SourceId sourceId, size_t offs) + : CharReader(std::shared_ptr<Buffer>{new Buffer{istream}}, sourceId, offs) { } CharReader::~CharReader() { - buffer->deleteCursor(readCursor.cursor); - buffer->deleteCursor(peekCursor.cursor); + buffer->deleteCursor(readCursor); + buffer->deleteCursor(peekCursor); } -bool CharReader::readAtCursor(Cursor &cursor, char &c) +bool CharReader::readAtCursor(Buffer::CursorId &cursor, char &c) { // Return false if we're at the end of the stream - if (!buffer->read(cursor.cursor, c)) { + if (!buffer->read(cursor, c)) { return false; } @@ -431,24 +420,12 @@ bool CharReader::readAtCursor(Cursor &cursor, char &c) // Check whether the next character is a continuation of the // current character char c2; - if (buffer->read(cursor.cursor, c2)) { + if (buffer->read(cursor, c2)) { if ((c2 != '\n' && c2 != '\r') || c2 == c) { - buffer->moveCursor(cursor.cursor, -1); + buffer->moveCursor(cursor, -1); } } } - - // Count lines and columns - if (c == '\n') { - // A linebreak was reached, go to the next line - cursor.line++; - cursor.column = 1; - } else { - // Ignore UTF-8 continuation bytes - if (!((c & 0x80) && !(c & 0x40))) { - cursor.column++; - } - } return true; } @@ -456,7 +433,7 @@ bool CharReader::peek(char &c) { // If the reader was coherent, update the peek cursor state if (coherent) { - peekCursor.assign(buffer, readCursor); + buffer->copyCursor(readCursor, peekCursor); coherent = false; } @@ -471,12 +448,8 @@ bool CharReader::read(char &c) // Set the peek position to the current read position, if reading was not // coherent - if (!coherent) { - peekCursor.assign(buffer, readCursor); - coherent = true; - } else { - buffer->copyCursor(readCursor.cursor, peekCursor.cursor); - } + buffer->copyCursor(readCursor, peekCursor); + coherent = true; // Return the result of the read function return res; @@ -485,7 +458,7 @@ bool CharReader::read(char &c) void CharReader::resetPeek() { if (!coherent) { - peekCursor.assign(buffer, readCursor); + buffer->copyCursor(readCursor, peekCursor); coherent = true; } } @@ -493,7 +466,7 @@ void CharReader::resetPeek() void CharReader::consumePeek() { if (!coherent) { - readCursor.assign(buffer, peekCursor); + buffer->copyCursor(peekCursor, readCursor); coherent = true; } } @@ -513,7 +486,8 @@ bool CharReader::consumeWhitespace() CharReaderFork CharReader::fork() { - return CharReaderFork(buffer, readCursor, peekCursor, coherent); + return CharReaderFork{buffer, readCursor, peekCursor, + sourceId, offs, coherent}; } size_t CharReader::readRaw(char *buf, size_t size) @@ -528,155 +502,49 @@ size_t CharReader::readRaw(char *buf, size_t size) return res; } -SourceContext CharReader::getContextAt(ssize_t maxSize, - Buffer::CursorId referenceCursor) -{ - // Clone the given read cursor - Buffer::CursorId cur = buffer->createCursor(referenceCursor); - - // Fetch the start position of the search - ssize_t offs = buffer->offset(cur); - ssize_t start = offs; - ssize_t end = offs; - char c; - - // Search the beginning of the line with the last non-whitespace character - bool hadNonWhitespace = false; - bool foundBegin = false; - for (ssize_t i = 0; i < maxSize; i++) { - // Fetch the character at the current position - if (buffer->fetch(cur, c)) { - // Abort, at linebreaks if we found a non-linebreak character - hadNonWhitespace = hadNonWhitespace || !Utils::isWhitespace(c); - if (hadNonWhitespace && (c == '\n' || c == '\r')) { - buffer->moveCursor(cur, 1); - start++; - foundBegin = true; - break; - } - } - if (buffer->moveCursor(cur, -1) == 0) { - foundBegin = true; - break; - } else { - // Update the start position and the hadNonWhitespace flag - start--; - } - } +bool CharReader::atEnd() const { return buffer->atEnd(readCursor); } - // Search the end of the line - buffer->moveCursor(cur, offs - start); - bool foundEnd = false; - for (ssize_t i = 0; i < maxSize; i++) { - // Increment the end counter if a character was read, abort if the end - // of the stream has been reached - if (buffer->read(cur, c)) { - end++; - } else { - foundEnd = true; - break; - } - - // Abort on linebreak characters - if (c == '\n' || c == '\r') { - foundEnd = true; - break; - } - } - - // Calculate the truncated start and end position and limit the number of - // characters to the maximum number of characters - ssize_t tStart = start; - ssize_t tEnd = end; - if (tEnd - tStart > maxSize) { - tStart = std::max(offs - maxSize / 2, tStart); - tEnd = tStart + maxSize; - } - - // Try to go to the calculated start position and fetch the actual start - // position - ssize_t aStart = end + buffer->moveCursor(cur, tStart - end); - if (aStart > tStart) { - tEnd = tEnd + (aStart - tStart); - tStart = aStart; - } - - // Read one line - std::stringstream ss; - size_t relPos = 0; - for (ssize_t i = tStart; i < tEnd; i++) { - if (buffer->read(cur, c)) { - // Break once a linebreak is reached - if (c == '\n' || c == '\r') { - break; - } - - // Add the current character to the output - ss << c; - - // Increment the string-relative offset as long as the original - // offset is not reached in the for loop - if (i < offs) { - relPos++; - } - } - } - - // Delete the newly created cursor - buffer->deleteCursor(cur); - - return SourceContext{ss.str(), relPos, !foundBegin || tStart != start, - !foundEnd || tEnd != end}; +SourceOffset CharReader::getOffset() const +{ + return buffer->offset(readCursor) + offs; } -SourceContext CharReader::getContextAtOffs(ssize_t maxSize, size_t offs) +SourcePosition CharReader::getPosition() const { - // Create a new cursor and calculate how far it has to be moved to reach - // the position specified in the location instance - Buffer::CursorId cur = buffer->createCursor(); - ssize_t moveOffs = offs - buffer->offset(cur); - - // Try to move the cursor to the specified position and read the context - SourceContext res; - if (buffer->moveCursor(cur, moveOffs) == moveOffs) { - res = getContextAt(60, cur); - } - - // Delete the read cursor - buffer->deleteCursor(cur); - return res; + return getOffset(); } -SourceContext CharReader::getContext(ssize_t maxSize) +SourceLocation CharReader::getLocation() const { - return getContextAt(maxSize, readCursor.cursor); + return SourceLocation{sourceId, getOffset()}; } -SourceContext CharReader::contextCallback(const SourceLocation &location, - void *data) +SourceLocation CharReader::getLocation(SourcePosition start) const { - return static_cast<CharReader *>(data)->getContextAtOffs(60, location.offs); + return SourceLocation{sourceId, start, getOffset()}; } +SourceId CharReader::getSourceId() const { return sourceId; } + /* Class CharReaderFork */ CharReaderFork::CharReaderFork(std::shared_ptr<Buffer> buffer, - CharReader::Cursor &parentReadCursor, - CharReader::Cursor &parentPeekCursor, - bool coherent) - : CharReader(buffer, 1, 1), + Buffer::CursorId parentReadCursor, + Buffer::CursorId parentPeekCursor, + SourceId sourceId, size_t offs, bool coherent) + : CharReader(buffer, sourceId, offs), parentReadCursor(parentReadCursor), parentPeekCursor(parentPeekCursor) { - readCursor.assign(buffer, parentReadCursor); - peekCursor.assign(buffer, parentPeekCursor); + buffer->copyCursor(parentReadCursor, readCursor); + buffer->copyCursor(parentPeekCursor, peekCursor); this->coherent = coherent; } void CharReaderFork::commit() { - parentReadCursor.assign(buffer, readCursor); - parentPeekCursor.assign(buffer, peekCursor); + buffer->copyCursor(readCursor, parentReadCursor); + buffer->copyCursor(peekCursor, parentPeekCursor); } } diff --git a/src/core/common/CharReader.hpp b/src/core/common/CharReader.hpp index 134d9d9..5a4d906 100644 --- a/src/core/common/CharReader.hpp +++ b/src/core/common/CharReader.hpp @@ -355,54 +355,10 @@ class CharReaderFork; /** * Used within parsers for convenient access to single characters in an input * stream or buffer. It allows reading and peeking single characters from a - * buffer. Additionally it counts the current column/row (with correct handling - * for UTF-8) and contains an internal state machine that handles the detection - * of linebreaks and converts these to a single '\n'. + * buffer. Additionally it contains an internal state machine that handles the + * detection of linebreaks and converts these to a single '\n'. */ class CharReader { -protected: - /** - * Internally used cursor structure for managing the read and the peek - * cursor. - */ - struct Cursor { - /** - * Corresponding cursor in the underlying buffer instance. - */ - const Buffer::CursorId cursor; - - /** - * Current line the cursor is in. - */ - int line; - - /** - * Current column the cursor is in. - */ - int column; - - /** - * Constructor of the Cursor class. - * - * @param cursor is the underlying cursor in the Buffer instance. - * @param line is the line at which the cursor is positioned. - * @param column is the column at which the cursor is positioned. - */ - Cursor(Buffer::CursorId cursor, int line, int column) - : cursor(cursor), line(line), column(column) - { - } - - /** - * Assigns one cursor to another. - * - * @param buffer is the underlying buffer instance the internal cursor - * belongs to. - * @param cursor is the cursor from which the state should be copied. - */ - void assign(std::shared_ptr<Buffer> buffer, Cursor &cursor); - }; - private: /** * Substitutes "\r", "\n\r", "\r\n" with a single "\n". @@ -411,7 +367,7 @@ private: * @param c a reference to the character that should be written. * @return true if another character needs to be read. */ - bool substituteLinebreaks(Cursor &cursor, char &c); + bool substituteLinebreaks(Buffer::CursorId &cursor, char &c); /** * Reads a single character from the given cursor. @@ -421,29 +377,7 @@ private: * @return true if a character was read, false if the end of the stream has * been reached. */ - bool readAtCursor(Cursor &cursor, char &c); - - /** - * Returns the line the given cursor currently is in, but at most the - * given number of characters in the form of a Context structure. - * - * @param maxSize is the maximum length of the extracted context - * @param referenceCursor is a cursor in the internal buffer pointing at the - * location at which the context should be read. - */ - SourceContext getContextAt(ssize_t maxSize, - Buffer::CursorId referenceCursor); - - /** - * Returns the line the at the given byte offset, but at most the - * given number of characters in the form of a Context structure. - * - * @param maxSize is the maximum length of the extracted context - * @param offs is the byte offset for which the context should be read. - * @return the context at the specified position or an empty (invalid) - * context if the context could not be read. - */ - SourceContext getContextAtOffs(ssize_t maxSize, size_t offs); + bool readAtCursor(Buffer::CursorId &cursor, char &c); protected: /** @@ -454,12 +388,12 @@ protected: /** * Cursor used for reading. */ - Cursor readCursor; + Buffer::CursorId readCursor; /** * Cursor used for peeking. */ - Cursor peekCursor; + Buffer::CursorId peekCursor; /** * Set to true as long the underlying Buffer cursor is at the same position @@ -469,33 +403,50 @@ protected: bool coherent; /** + * Id of the underlying source file. + */ + SourceId sourceId; + + /** + * Offset to be added to the underlying buffer byte positions. + */ + size_t offs; + + /** * Protected constructor of the CharReader base class. Creates new read * and peek cursors for the given buffer. * * @param buffer is a reference to the underlying Buffer class responsible * for allowing to read from a single input stream from multiple locations. + * @param sourceId is the ID of the underlying source file. + * @param offs is the byte offset at which the char reader should start + * counting. */ - CharReader(std::shared_ptr<Buffer> buffer, size_t line, size_t column); + CharReader(std::shared_ptr<Buffer> buffer, SourceId sourceId, size_t offs); public: /** * Creates a new CharReader instance from a string. * * @param str is a string containing the input data. - * @param line is the start line. - * @param column is the start column. + * @param sourceId is the ID of the underlying source file. + * @param offs is the byte offset at which the char reader should start + * counting. */ - CharReader(const std::string &str, size_t line = 1, size_t column = 1); + CharReader(const std::string &str, SourceId sourceId = InvalidSourceId, + size_t offs = 0); /** * Creates a new CharReader instance for an input stream. * * @param istream is the input stream from which incomming data should be * read. - * @param line is the start line. - * @param column is the start column. + * @param sourceId is the ID of the underlying source file. + * @param offs is the byte offset at which the char reader should start + * counting. */ - CharReader(std::istream &istream, size_t line = 1, size_t column = 1); + CharReader(std::istream &istream, SourceId sourceId = InvalidSourceId, + size_t offs = 0); /** * Deletes the used cursors from the underlying buffer instance. @@ -572,56 +523,52 @@ public: size_t readRaw(char *buf, size_t size); /** - * Returns true if there are no more characters as the stream was - * closed. + * Returns true if there are no more characters as the stream was closed. * * @return true if there is no more data. */ - bool atEnd() const { return buffer->atEnd(readCursor.cursor); } + bool atEnd() const; /** * Returns the offset of the read cursor in bytes. + * + * @return the offset of the read cursor in bytes. */ - size_t getOffset() const { return buffer->offset(readCursor.cursor); } - - /** - * Returns the line number the read cursor currently is at. - */ - int getLine() const { return readCursor.line; } + SourceOffset getOffset() const; /** - * Returns the column the read cursor currently is at. + * Returns the offset of the read cursor in bytes. + * + * @return the offset of the read cursor in bytes. */ - int getColumn() const { return readCursor.column; } + SourcePosition getPosition() const; /** - * Returns the current position of the read cursor (line and column). + * Returns a SourceLocation object describing the exact position (including + * the source file) of the read cursor. + * + * @return a SourceLocation object at the position of the current read + * cursor. */ - SourceLocation getLocation() const - { - return SourceLocation(getLine(), getColumn(), getOffset()); - } + SourceLocation getLocation() const; /** - * Returns the line the read cursor currently is in, but at most the - * given number of characters in the form of a Context structure. + * Returns a SourceLocation object starting at the given start position and + * ending at the exact position (including the source file) of the read + * cursor. * - * @param maxSize is the maximum length of the extracted context + * @return a SourceLocation object at the position of the current read + * cursor. */ - SourceContext getContext(ssize_t maxSize = 60); + SourceLocation getLocation(SourcePosition start) const; /** - * Function that can be used to provide the context for a certain source - * location. A pointer to this function can be supplied to a Logger instance - * in the pushFile() method. The data should be set to a pointer to the - * CharReader instance. + * Returns the current SourceId which describes the Resource on which the + * CharReader is currently working. * - * @param location is the location for which the context should be returned. - * Only the "offs" field within the location is used. - * @param data is a pointer pointing at a CharReader instance. + * @return the current SourceId. */ - static SourceContext contextCallback(const SourceLocation &location, - void *data); + SourceId getSourceId() const; }; /** @@ -637,12 +584,12 @@ private: /** * The reader cursor of the underlying CharReader instance. */ - CharReader::Cursor &parentReadCursor; + Buffer::CursorId parentReadCursor; /** * The peek cursor of the underlying CharReader instance. */ - CharReader::Cursor &parentPeekCursor; + Buffer::CursorId parentPeekCursor; /** * Constructor of the CharReaderFork class. @@ -650,12 +597,14 @@ private: * @param buffer is a reference at the parent Buffer instance. * @param parentPeekCursor is a reference at the parent read cursor. * @param parentPeekCursor is a reference at the parent peek cursor. + * @param location is the current location. * @param coherent specifies whether the char reader cursors are initialized * coherently. */ CharReaderFork(std::shared_ptr<Buffer> buffer, - CharReader::Cursor &parentReadCursor, - CharReader::Cursor &parentPeekCursor, bool coherent); + Buffer::CursorId parentReadCursor, + Buffer::CursorId parentPeekCursor, SourceId sourceId, + size_t offs, bool coherent); public: /** diff --git a/src/core/common/Exceptions.cpp b/src/core/common/Exceptions.cpp index e368b5a..caba7cc 100644 --- a/src/core/common/Exceptions.cpp +++ b/src/core/common/Exceptions.cpp @@ -22,21 +22,5 @@ namespace ousia { -/* Class LoggableException */ - -std::string LoggableException::formatMessage(const std::string &msg, - const SourceLocation &loc) -{ - std::stringstream ss; - ss << "error "; - if (loc.hasLine()) { - ss << "at line " << loc.line << ", "; - if (loc.hasColumn()) { - ss << "column " << loc.column << " "; - } - } - ss << "with message: " << msg; - return ss.str(); -} } diff --git a/src/core/common/Exceptions.hpp b/src/core/common/Exceptions.hpp index 2a88427..0be33b3 100644 --- a/src/core/common/Exceptions.hpp +++ b/src/core/common/Exceptions.hpp @@ -77,14 +77,6 @@ public: * makes it simple to handle non-recoverable errors in the code. */ class LoggableException : public OusiaException { -private: - /** - * Function used internally to build the formated message that should be - * reported to the runtime environment. - */ - static std::string formatMessage(const std::string &msg, - const SourceLocation &loc); - public: /** * Reported error message. @@ -104,7 +96,7 @@ public: */ LoggableException(std::string msg, SourceLocation loc = SourceLocation{}) - : OusiaException(formatMessage(msg, loc)), + : OusiaException(msg), msg(std::move(msg)), loc(std::move(loc)) { @@ -128,15 +120,27 @@ public: * Constructor of LoggableException for arbitrary position objects. * * @param msg is the actual log message. - * @param loc is a reference to a variable with position and context data. + * @param loc is a reference to a variable with location data. */ template <class LocationType> - LoggableException(std::string msg, LocationType &loc) + LoggableException(std::string msg, const LocationType &loc) : LoggableException(std::move(msg), loc.getLocation()) { } /** + * Constructor of LoggableException for arbitrary position objects. + * + * @param msg is the actual log message. + * @param loc is a pointe to a variable with location data. + */ + template <class LocationType> + LoggableException(std::string msg, const LocationType *loc) + : LoggableException(std::move(msg), loc->getLocation()) + { + } + + /** * Returns the position at which the exception occured in the text. * * @return the position descriptor. diff --git a/src/core/common/Location.cpp b/src/core/common/Location.cpp new file mode 100644 index 0000000..7fe5ea5 --- /dev/null +++ b/src/core/common/Location.cpp @@ -0,0 +1,33 @@ +/* + Ousía + Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "Location.hpp" + +namespace ousia { + +/* Global Functions */ + +const SourceLocation NullSourceLocation; + +SourceContext NullSourceContextCallback(const SourceLocation &location) +{ + return SourceContext{}; +} + +} + diff --git a/src/core/common/Location.hpp b/src/core/common/Location.hpp index 39e1011..808abbd 100644 --- a/src/core/common/Location.hpp +++ b/src/core/common/Location.hpp @@ -16,99 +16,379 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +/** + * @file Location.hpp + * + * Types used for describing positions, ranges and excerpts of source files used + * for describing log messages. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + #ifndef _OUSIA_LOCATION_HPP_ #define _OUSIA_LOCATION_HPP_ +#include <cstdint> +#include <functional> +#include <limits> #include <string> namespace ousia { /** - * Struct representing a location within a source file. A position is defined by - * a byte offset (which is always reproducable), a line number and a column - * number (which may differ depending on the encoding used). + * Type used for referencing a source file currently opened in a Project. + */ +using SourceId = uint32_t; + +/** + * Maximum value for a SourceId. Indicates invalid entries. + */ +constexpr SourceId InvalidSourceId = std::numeric_limits<SourceId>::max(); + +/** + * Type used for specifying an offset within a source file. + */ +using SourceOffset = uint32_t; + +/** + * Maximum value for a SourceOffset. As SourceOffset is a 32 Bit unsigned + * integer, the maximum value is 2^32-1, which means that 4 GiB are addressable + * by SourceOffset. + */ +constexpr SourceOffset InvalidSourceOffset = + std::numeric_limits<SourceOffset>::max(); + +/** + * Function for clamping a size_t to a valid SourceOffset value. + * + * @param pos is the size_t value that should be converted to a SourceOffset + * value. If pos is larger than the maximum value that can be represented by + * SourceOffset, the result is set to this maximum value, which is interpreted + * as "invalid" by functions dealing with the SourceOffset type. + * @return the clamped position value. + */ +inline SourceOffset clampToSourcePosition(size_t pos) +{ + return pos > InvalidSourceOffset ? InvalidSourceOffset : pos; +} + +/** + * Class specifying a position within an (unspecified) source file. */ -struct SourceLocation { +class SourcePosition { +private: /** - * Current line, starting with one. + * Offset position in bytes relative to the start of the document. */ - int line; + SourceOffset pos; +public: /** - * Current column, starting with one. + * Default constructor of the SourcePosition class. Sets the position to + * InvalidSourceOffset and thus marks the SourcePosition as invalid. */ - int column; + SourcePosition() : pos(InvalidSourceOffset) {} /** - * Current byte offset. + * Creates a new SourcePosition instance with the given byte offset. */ - size_t offs; + SourcePosition(size_t pos) : pos(clampToSourcePosition(pos)) {} /** - * Default constructor of the SourceLocation struct, initializes all - * memebers with zero. + * Sets the position of the SourcePosition value to the given value. Clamps + * the given size_t to the valid range. + * + * @param pos is the position value that should be set. */ - SourceLocation() : line(0), column(0), offs(0) {} + void setPosition(size_t pos) { this->pos = clampToSourcePosition(pos); } /** - * Creates a new SourceLocation struct with only a line and no column. + * Returns the position value. Only use the value if "valid" returns true. * - * @param line is the line number. - * @param column is the column number. + * @return the current position. */ - SourceLocation(int line) : line(line), column(0), offs(0) {} + SourceOffset getPosition() const { return pos; } /** - * Creates a new SourceLocation struct with a line and column. + * Returns true if the source position is valid, false otherwise. Invalid + * positions are set to the maximum representable number. * - * @param line is the line number. - * @param column is the column number. + * @return true if the SourcePosition instance is value, false otherwise. + */ + bool isValid() const { return pos != InvalidSourceOffset; } +}; + +/** + * The SourceRange class represents a range within an (unspecified) source file. + */ +class SourceRange { +private: + /** + * Start byte offset. + */ + SourcePosition start; + + /** + * End byte offset. */ - SourceLocation(int line, int column) : line(line), column(column), offs(0) + SourcePosition end; + +public: + /** + * Default constructor. Creates an invalid range. + */ + SourceRange(){}; + + /** + * Constructor for a zero-length range. + * + * @param pos is the byte offset at which the SourceRange instance should be + * located. + */ + SourceRange(SourcePosition pos) : start(pos), end(pos) {} + + /** + * Constructor of a SourceRange instance. + * + * @param start is the byte offset of the first character in the range + * (start is inclusive). + * @param end points at the end of the range (end is non-inclusive). + */ + SourceRange(SourcePosition start, SourcePosition end) + : start(start), end(end) { } /** - * Creates a new SourceLocation struct with a line, column and byte offset. + * Sets the start of the SourceRange value to the given value. This + * operation might render the SourceRange invalid (if the given position is + * larger than the end position). + * + * @param pos is the start position value that should be set. + */ + void setStart(SourcePosition pos) { this->start = pos; } + + /** + * Sets the end of the SourceRange value to the given value. This operation + * might render the SourceRange invalid (if the given position is smaller + * than the start position). * - * @param line is the line number. - * @param column is the column number. - * @param offs is the byte offset. + * @param pos is the end position that should be set. */ - SourceLocation(int line, int column, size_t offs) - : line(line), column(column), offs(offs) + void setEnd(SourcePosition pos) { this->end = pos; } + + /** + * Sets the start and end of the SourceRange value to the given values. + * This operation might render the SourceRange invalid (if the given end + * position is smaller than the start position). + * + * @param start is the start position that should be set. + * @param end is the end position that should be set. + */ + void setRange(SourcePosition start, SourcePosition end) { + this->start = start; + this->end = end; } /** - * Returns true, if the line number is valid, false otherwise. + * Makes the Range represent a zero-length range that is located at the + * given position. The given position should be interpreted as being located + * "between the character just before the start offset and the start + * offset". * - * @return true for valid line numbers. + * @param pos is the position to which start and end should be set. */ - bool hasLine() const { return line > 0; } + void setPosition(SourcePosition pos) + { + this->start = pos; + this->end = pos; + } /** - * Returns true, if the column number is valid, false otherwise. + * Returns the start position of the SourceRange instance. * - * @return true for valid column numbers. + * @return the start offset in bytes. + */ + SourceOffset getStart() const { return start.getPosition(); } + + /** + * Returns the end position of the SourceRange instance. + * + * @return the end offset in bytes (non-inclusive). + */ + SourceOffset getEnd() const { return end.getPosition(); } + + /** + * Returns a copy of the underlying SourcePosition instance representing the + * start position. + * + * @return a copy of the start SourcePosition instance. */ - bool hasColumn() const { return column > 0; } + SourcePosition getStartPosition() const { return start; } /** - * Returns true, if the position is valid, false otherwise. This function is - * equivalent to the hasLine() function. + * Returns a copy of the underlying SourcePosition instance representing the + * end position. * - * @return true if the Position struct is valid. + * @return a copy of the end SourcePosition instance. */ - bool valid() const { return hasLine(); } + SourcePosition getEndPosition() const { return end; } + + /** + * Returns the length of the range. A range may have a zero value length, in + * which case it should be interpreted as "between the character before + * the start offset and the start offset". The returned value is only valid + * if the isValid() method returns true! + * + * @return the length of the range in bytes. + */ + size_t getLength() const { return end.getPosition() - start.getPosition(); } + + /** + * Returns true if this range is actually valid. This is the case if the + * start position is smaller or equal to the end position and start and end + * position themself are valid. + * + * @return true if the Range is valid. + */ + bool isValid() const + { + return start.isValid() && end.isValid() && + start.getPosition() <= end.getPosition(); + } }; /** + * The SourceLocation class describes a range within a specific source file. + */ +class SourceLocation : public SourceRange { +private: + /** + * Id of the source file. + */ + SourceId sourceId; + +public: + /** + * Default constructor. + */ + SourceLocation() : sourceId(InvalidSourceId){}; + + /** + * Constructor, binds the SourceLocation to the given source file. + * + * @param sourceId specifies the file this location refers to. + */ + SourceLocation(SourceId sourceId) : sourceId(sourceId){}; + + /** + * Constructor for a zero-length range. + * + * @param sourceId specifies the file this location refers to. + * @param pos is the byte offset at which the SourceRange instance should be + * located. + */ + SourceLocation(SourceId sourceId, SourcePosition pos) + : SourceRange(pos), sourceId(sourceId) + { + } + + /** + * Constructor of a SourceRange instance. + * + * @param sourceId specifies the file this location refers to. + * @param start is the byte offset of the first character in the range + * (start is inclusive). + * @param end points at the end of the range (end is non-inclusive). + */ + SourceLocation(SourceId sourceId, SourcePosition start, SourcePosition end) + : SourceRange(start, end), sourceId(sourceId) + { + } + + /** + * Constructor of a SourceRange instance. + * + * @param sourceId specifies the file this location refers to. + * @param start is the byte offset of the first character in the range + * (start is inclusive). + * @param end points at the end of the range (end is non-inclusive). + */ + SourceLocation(SourceId sourceId, const SourceRange &range) + : SourceRange(range), sourceId(sourceId) + { + } + + /** + * Sets the source id to the given value. + * + * @param sourceId specifies the file this location refers to. + */ + void setSourceId(SourceId sourceId) { this->sourceId = sourceId; } + + /** + * Returns the id of the source file this SourceLocation instance is bound + * to. + * + * @return the id of the source file this instance is bound to. + */ + SourceId getSourceId() const { return sourceId; } + + /** + * Returns true if this location is actually valid. This is the case if + * the underlying range is valid and the source id is valid. + * + * @return true if the Range is valid. + */ + bool isValid() const + { + return SourceRange::isValid() && sourceId != InvalidSourceId; + } +}; + +/** + * NullSourceLocation is an empty SourceLocation instance. + */ +extern const SourceLocation NullSourceLocation; + +/** * Represents the context of a SourceLocation instance. Used to build error * messages. */ struct SourceContext { /** + * Underlying source range (contains the byte start and end offsets in + * bytes). + */ + SourceRange range; + + /** + * Name of the underlying resource. + */ + std::string filename; + + /** + * Start line, starting with one. + */ + int startLine; + + /** + * Start column, starting with one. + */ + int startColumn; + + /** + * End line, starting with one. + */ + int endLine; + + /** + * End column, starting with one. + */ + int endColumn; + + /** * Set to the content of the current line. */ std::string text; @@ -120,6 +400,12 @@ struct SourceContext { int relPos; /** + * Relative length (in characters) within that line. May end beyond the + * text given in the context. + */ + int relLen; + + /** * Set to true if the beginning of the line has been truncated (because * the reader position is too far away from the actual position of the * line). @@ -134,39 +420,45 @@ struct SourceContext { bool truncatedEnd; /** - * Default constructor, initializes all members with zero values. + * Default constructor, initializes primitive members with zero values. */ SourceContext() - : text(), relPos(0), truncatedStart(false), truncatedEnd(false) + : startLine(0), + startColumn(0), + endLine(0), + endColumn(0), + relPos(0), + relLen(0), + truncatedStart(false), + truncatedEnd(false) { } /** - * Constructor of the SourceContext class. + * Returns true the context text is not empty. * - * @param text is the current line the text cursor is at. - * @param relPos is the relative position of the text cursor within that - * line. - * @param truncatedStart specifies whether the text was truncated at the - * beginning. - * @param truncatedEnd specifies whether the text was truncated at the - * end. - */ - SourceContext(std::string text, size_t relPos, bool truncatedStart, - bool truncatedEnd) - : text(std::move(text)), - relPos(relPos), - truncatedStart(truncatedStart), - truncatedEnd(truncatedEnd) - { - } + * @return true if the context is valid and e.g. should be printed. + */ + bool isValid() const { return range.isValid() && hasLine() && hasColumn(); } /** - * Returns true the context text is not empty. + * Returns true if a valid (non-empty) filename is set. + */ + bool hasFile() const { return !filename.empty(); } + + /** + * Returns true, if the start line number is valid, false otherwise. * - * @return true if the context is valid and e.g. should be printed. + * @return true for valid line numbers. + */ + bool hasLine() const { return startLine > 0; } + + /** + * Returns true, if the start column number is valid, false otherwise. + * + * @return true for valid column numbers. */ - bool valid() const { return !text.empty(); } + bool hasColumn() const { return startColumn > 0; } }; /** @@ -174,10 +466,20 @@ struct SourceContext { * location. * * @param location is the location for which the context should be looked up. - * @param data is used defined data associated with the callback. + * @return the corresponding SourceContext. + */ +using SourceContextCallback = + std::function<SourceContext(const SourceLocation &)>; + +/** + * Function to be used as default value for the SourceContextCallback. Returns + * an invalid SourceContext. + * + * @param location is the location for which the context should be looked up. + * @return an empty, invalid SourceContext. */ -using SourceContextCallback = SourceContext (*)(const SourceLocation &location, - void *data); +SourceContext NullSourceContextCallback(const SourceLocation &location); + } #endif /* _OUSIA_LOCATION_HPP_ */ diff --git a/src/core/common/Logger.cpp b/src/core/common/Logger.cpp index fa4b5c8..034953d 100644 --- a/src/core/common/Logger.cpp +++ b/src/core/common/Logger.cpp @@ -27,10 +27,10 @@ namespace ousia { /* Class Logger */ void Logger::log(Severity severity, const std::string &msg, - const SourceLocation &loc) + const SourceLocation &loc, MessageMode mode) { // Assemble the message and pass it through the filter, then process it - Message message { severity, std::move(msg), loc }; + Message message{severity, mode, std::move(msg), loc}; if (filterMessage(message)) { processMessage(message); } @@ -42,30 +42,43 @@ LoggerFork Logger::fork() { return LoggerFork(this); } void LoggerFork::processMessage(const Message &msg) { - calls.push_back(Call(CallType::MESSAGE, messages.size())); + calls.emplace_back(CallType::MESSAGE, messages.size()); messages.push_back(msg); } -void LoggerFork::processPushFile(const File &file) +void LoggerFork::processPushDefaultLocation(const SourceLocation &loc) { - calls.push_back(Call(CallType::PUSH_FILE, files.size())); - files.push_back(file); + calls.emplace_back(CallType::PUSH_LOCATION, locations.size()); + locations.push_back(loc); } -void LoggerFork::processPopFile() +void LoggerFork::processPopDefaultLocation() { - calls.push_back(Call(CallType::POP_FILE, 0)); + calls.emplace_back(CallType::POP_LOCATION, 0); } void LoggerFork::processSetDefaultLocation(const SourceLocation &loc) { // Check whether setDefaultLocation was called immediately before, if yes, // simply override the data - if (!calls.empty() && calls.back().type == CallType::SET_DEFAULT_LOCATION) { + if (!calls.empty() && calls.back().type == CallType::SET_LOCATION) { locations.back() = loc; } else { - calls.push_back(Call(CallType::SET_DEFAULT_LOCATION, locations.size())); - locations.push_back(loc); + calls.emplace_back(CallType::SET_LOCATION, locations.size()); + locations.emplace_back(loc); + } +} + +void LoggerFork::processSetSourceContextCallback( + SourceContextCallback sourceContextCallback) +{ + // Check whether setSourceContextCallback was called immediately before, + // if yes, simply override the data + if (!calls.empty() && calls.back().type == CallType::SET_CONTEXT_CALLBACK) { + callbacks.back() = sourceContextCallback; + } else { + calls.emplace_back(CallType::SET_CONTEXT_CALLBACK, callbacks.size()); + callbacks.emplace_back(sourceContextCallback); } } @@ -73,45 +86,91 @@ void LoggerFork::purge() { calls.clear(); messages.clear(); - files.clear(); locations.clear(); + callbacks.clear(); } void LoggerFork::commit() { for (const Call &call : calls) { switch (call.type) { - case CallType::MESSAGE: { + case CallType::MESSAGE: if (parent->filterMessage(messages[call.dataIdx])) { parent->processMessage(messages[call.dataIdx]); } break; - } - case CallType::PUSH_FILE: { - parent->processPushFile(files[call.dataIdx]); + case CallType::PUSH_LOCATION: + parent->processPushDefaultLocation(locations[call.dataIdx]); break; - } - case CallType::POP_FILE: - parent->processPopFile(); + case CallType::POP_LOCATION: + parent->processPopDefaultLocation(); break; - case CallType::SET_DEFAULT_LOCATION: + case CallType::SET_LOCATION: parent->processSetDefaultLocation(locations[call.dataIdx]); break; + case CallType::SET_CONTEXT_CALLBACK: + parent->processSetSourceContextCallback( + callbacks[call.dataIdx]); + break; } } purge(); } -/* Class ConcreteLogger */ +/* Class ScopedLogger */ + +ScopedLogger::ScopedLogger(Logger &parent, SourceLocation loc) + : parent(parent), depth(0) +{ + pushDefaultLocation(loc); +} + +ScopedLogger::~ScopedLogger() +{ + while (depth > 0) { + popDefaultLocation(); + } +} + +void ScopedLogger::processMessage(const Message &msg) +{ + parent.processMessage(msg); +} + +bool ScopedLogger::filterMessage(const Message &msg) +{ + return parent.filterMessage(msg); +} + +void ScopedLogger::processPushDefaultLocation(const SourceLocation &loc) +{ + parent.processPushDefaultLocation(loc); + depth++; +} + +void ScopedLogger::processPopDefaultLocation() +{ + depth--; + parent.processPopDefaultLocation(); +} -static const Logger::File EMPTY_FILE{"", SourceLocation{}, nullptr, nullptr}; +void ScopedLogger::processSetDefaultLocation(const SourceLocation &loc) +{ + parent.processSetDefaultLocation(loc); +} -void ConcreteLogger::processPushFile(const File &file) +void ScopedLogger::processSetSourceContextCallback( + SourceContextCallback sourceContextCallback) { - files.push_back(file); + parent.processSetSourceContextCallback(sourceContextCallback); } -void ConcreteLogger::processPopFile() { files.pop_back(); } +/* Class ConcreteLogger */ + +ConcreteLogger::ConcreteLogger(Severity minSeverity) + : minSeverity(minSeverity), sourceContextCallback(NullSourceContextCallback) +{ +} bool ConcreteLogger::filterMessage(const Message &msg) { @@ -126,40 +185,46 @@ bool ConcreteLogger::filterMessage(const Message &msg) return sev >= static_cast<uint8_t>(minSeverity); } -void ConcreteLogger::processSetDefaultLocation(const SourceLocation &loc) +void ConcreteLogger::processPushDefaultLocation(const SourceLocation &loc) { - defaultLocation = loc; + locations.emplace_back(loc); } -const Logger::File &ConcreteLogger::currentFile() const +void ConcreteLogger::processPopDefaultLocation() { - if (!files.empty()) { - return files.back(); + if (!locations.empty()) { + locations.pop_back(); + } +} + +void ConcreteLogger::processSetDefaultLocation(const SourceLocation &loc) +{ + if (!locations.empty()) { + locations.back() = loc; + } else { + locations.emplace_back(loc); } - return EMPTY_FILE; } -const std::string &ConcreteLogger::currentFilename() const +void ConcreteLogger::processSetSourceContextCallback( + SourceContextCallback sourceContextCallback) { - return currentFile().file; + this->sourceContextCallback = sourceContextCallback; } const SourceLocation &ConcreteLogger::messageLocation(const Message &msg) const { - if (msg.loc.valid()) { + if (msg.loc.isValid()) { return msg.loc; + } else if (!locations.empty()) { + return locations.back(); } - return defaultLocation; + return NullSourceLocation; } SourceContext ConcreteLogger::messageContext(const Message &msg) const { - const Logger::File &file = currentFile(); - const SourceLocation &loc = messageLocation(msg); - if (file.ctxCallback && loc.valid()) { - return file.ctxCallback(loc, file.ctxCallbackData); - } - return SourceContext{}; + return sourceContextCallback(messageLocation(msg)); } Severity ConcreteLogger::getMaxEncounteredSeverity() @@ -183,8 +248,9 @@ size_t ConcreteLogger::getSeverityCount(Severity severity) void ConcreteLogger::reset() { - files.clear(); + locations.clear(); messageCounts.clear(); + sourceContextCallback = NullSourceContextCallback; } bool ConcreteLogger::hasError() @@ -193,6 +259,11 @@ bool ConcreteLogger::hasError() getSeverityCount(Severity::FATAL_ERROR) > 0; } +bool ConcreteLogger::hasFatalError() +{ + return getSeverityCount(Severity::FATAL_ERROR) > 0; +} + /* Class TerminalLogger */ void TerminalLogger::processMessage(const Message &msg) @@ -200,29 +271,26 @@ void TerminalLogger::processMessage(const Message &msg) Terminal t(useColor); // Fetch filename, position and context - const std::string filename = currentFilename(); - const SourceLocation pos = messageLocation(msg); const SourceContext ctx = messageContext(msg); // Print the file name - bool hasFile = !filename.empty(); - if (hasFile) { - os << t.bright() << filename << t.reset(); + if (ctx.hasFile()) { + os << t.bright() << ctx.filename << t.reset(); } // Print line and column number - if (pos.hasLine()) { - if (hasFile) { + if (ctx.hasLine()) { + if (ctx.hasFile()) { os << ':'; } - os << t.bright() << pos.line << t.reset(); - if (pos.hasColumn()) { - os << ':' << pos.column; + os << t.bright() << ctx.startLine << t.reset(); + if (ctx.hasColumn()) { + os << ':' << ctx.startColumn; } } // Print the optional seperator - if (hasFile || pos.hasLine()) { + if (ctx.hasFile() || ctx.hasLine()) { os << ": "; } @@ -249,30 +317,30 @@ void TerminalLogger::processMessage(const Message &msg) os << msg.msg << std::endl; // Print the error message context if available - if (ctx.valid()) { - size_t relPos = ctx.relPos; - if (ctx.truncatedStart) { - os << "[...] "; - } - os << ctx.text; - if (ctx.truncatedEnd) { - os << " [...]"; - } - os << std::endl; - - if (ctx.truncatedStart) { - os << " "; - } - - for (size_t i = 0; i < relPos; i++) { - if (i < ctx.text.size() && ctx.text[i] == '\t') { - os << '\t'; - } else { - os << ' '; - } - } - os << t.color(Terminal::GREEN) << '^' << t.reset() << std::endl; - } + /* if (ctx.valid()) { + size_t relPos = ctx.relPos; + if (ctx.truncatedStart) { + os << "[...] "; + } + os << ctx.text; + if (ctx.truncatedEnd) { + os << " [...]"; + } + os << std::endl; + + if (ctx.truncatedStart) { + os << " "; + } + + for (size_t i = 0; i < relPos; i++) { + if (i < ctx.text.size() && ctx.text[i] == '\t') { + os << '\t'; + } else { + os << ' '; + } + } + os << t.color(Terminal::GREEN) << '^' << t.reset() << std::endl; + }*/ } } diff --git a/src/core/common/Logger.hpp b/src/core/common/Logger.hpp index 767d8ab..85b1bb1 100644 --- a/src/core/common/Logger.hpp +++ b/src/core/common/Logger.hpp @@ -73,6 +73,40 @@ enum class Severity : uint8_t { FATAL_ERROR = 4 }; +/** + * Enum signifying how the message should be displayed. MessageMode constants + * can be combined using the bitwise or (|) operator. + */ +enum class MessageMode : uint8_t { + /** + * Default display mode. + */ + DEFAULT = 0, + + /** + * Do not display a context. + */ + NO_CONTEXT = 1, + + /** + * Do not display a file backtrace. + */ + NO_TRACE = 2 +}; + +/** + * Bitwise or for the MessageMode class. + * + * @param a is the first MessageMode. + * @param b is the second MessageMode. + * @return the two message modes combined using bitwise or. + */ +inline MessageMode operator|(MessageMode a, MessageMode b) +{ + return static_cast<MessageMode>(static_cast<uint8_t>(a) | + static_cast<uint8_t>(b)); +} + // Forward declaration class LoggerFork; class ScopedLogger; @@ -92,51 +126,6 @@ public: friend ScopedLogger; /** - * Describes a file inclusion. - */ - struct File { - /** - * Current filename. - */ - std::string file; - - /** - * Location at which the file was included. - */ - SourceLocation loc; - - /** - * Callback used to retrieve the context for a certain location - */ - SourceContextCallback ctxCallback; - - /** - * Data to be passed to the callback. - */ - void *ctxCallbackData; - - /** - * Constructor of the Scope struct. - * - * @param type is the type of - * @param file is the name of the current file. - * @param loc is the location at which the file was included. - * @param ctxCallback is the callback function that should be called - * for looking up the context belonging to a SourceLocation instance. - * @param ctxCallbackData is additional data that should be passed to - * the callback function. - */ - File(std::string file, SourceLocation loc, - SourceContextCallback ctxCallback, void *ctxCallbackData) - : file(std::move(file)), - loc(loc), - ctxCallback(ctxCallback), - ctxCallbackData(ctxCallbackData) - { - } - }; - - /** * The message struct represents a single log message and all information * attached to it. */ @@ -147,6 +136,11 @@ public: Severity severity; /** + * Message mode. + */ + MessageMode mode; + + /** * Actual log message. */ std::string msg; @@ -157,15 +151,51 @@ public: SourceLocation loc; /** + * Default constructor of the Message struct. + */ + Message() : severity(Severity::DEBUG), mode(MessageMode::DEFAULT) {} + + /** * Constructor of the Message struct. * * @param severity describes the message severity. + * @param mode is the mode in which the message should be displayed. * @param msg contains the actual message. + * @param loc is the location at which the message should be displayed. */ - Message(Severity severity, std::string msg, const SourceLocation &loc) - : severity(severity), msg(std::move(msg)), loc(loc){}; + Message(Severity severity, MessageMode mode, std::string msg, + const SourceLocation &loc) + : severity(severity), mode(mode), msg(std::move(msg)), loc(loc) + { + } }; + /** + * Calls the getLocation function on the given reference. + * + * @param obj is the object on which the getLocation function should be + * called. + * @return the SourceLocation returned by the getLocation function. + */ + template <typename T> + static SourceLocation location(const T &obj) + { + return obj.getLocation(); + } + + /** + * Calls the getLocation function on the given pointer. + * + * @param obj is the object on which the getLocation function should be + * called. + * @return the SourceLocation returned by the getLocation function. + */ + template <typename T> + static SourceLocation location(const T *obj) + { + return obj->getLocation(); + } + protected: /** * Function to be overriden by child classes to actually display or store @@ -188,24 +218,37 @@ protected: virtual bool filterMessage(const Message &msg) { return true; } /** - * Called whenever a new file is pushed onto the stack. + * Called whenever the pushDefaultLocation function is called. * - * @param file is the file structure that should be stored on the stack. + * @param loc is the default location that should be pushed onto the stack. */ - virtual void processPushFile(const File &file) {} + virtual void processPushDefaultLocation(const SourceLocation &loc) {} /** - * Called whenever a scope is popped from the stack. + * Called whenever the popDefaultLocation function is called. + * + * @param loc is the default location that should be popped from the stack. */ - virtual void processPopFile() {} + virtual void processPopDefaultLocation() {} /** * Called whenever the setDefaultLocation function is called. * - * @param loc is the default location that should be set. + * @param loc is the default location that shuold replace the current one on + * the stack. */ virtual void processSetDefaultLocation(const SourceLocation &loc) {} + /** + * Called whenever the setSourceContextCallback function is called. + * + * @param sourceContextCallback is the callback function that should be set. + */ + virtual void processSetSourceContextCallback( + SourceContextCallback sourceContextCallback) + { + } + public: /** * Virtual destructor. @@ -228,28 +271,25 @@ public: * @param severity is the severity of the log message. * @param msg is the actual log message. * @param loc is the location in the source file the message refers to. + * @param mode specifies how the message should be displayed. */ void log(Severity severity, const std::string &msg, - const SourceLocation &loc = SourceLocation{}); + const SourceLocation &loc = SourceLocation{}, + MessageMode mode = MessageMode::DEFAULT); /** * Logs the given loggable exception. * * @param ex is the exception that should be logged. + * @param loc is a location which (if valid overrides the location given in + * the exception. + * @param mode specifies how the message should be displayed. */ - void log(const LoggableException &ex) + void log(const LoggableException &ex, + const SourceLocation &loc = SourceLocation{}, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::ERROR, ex.msg, ex.getLocation()); - } - - /** - * Logs the given loggable exception at the given location. - * - * @param ex is the exception that should be logged. - */ - void log(const LoggableException &ex, const SourceLocation &loc) - { - log(Severity::ERROR, ex.msg, loc.valid() ? loc : ex.getLocation()); + log(Severity::ERROR, ex.msg, loc.isValid() ? loc : ex.getLocation()); } /** @@ -260,11 +300,13 @@ public: * @param msg is the actual log message. * @param loc is a reference to a variable which provides location * information. + * @param mode specifies how the message should be displayed. */ template <class LocationType> - void log(Severity severity, const std::string &msg, LocationType &loc) + void log(Severity severity, const std::string &msg, LocationType loc, + MessageMode mode = MessageMode::DEFAULT) { - log(severity, msg, loc.getLocation()); + log(severity, msg, location(loc), mode); } /** @@ -275,10 +317,11 @@ public: * @param loc is the location in the source file the message refers to. */ void debug(const std::string &msg, - const SourceLocation &loc = SourceLocation{}) + const SourceLocation &loc = SourceLocation{}, + MessageMode mode = MessageMode::DEFAULT) { #ifndef NDEBUG - log(Severity::DEBUG, msg, loc); + log(Severity::DEBUG, msg, loc, mode); #endif } @@ -291,10 +334,11 @@ public: * information. */ template <class LocationType> - void debug(const std::string &msg, LocationType &loc) + void debug(const std::string &msg, LocationType loc, + MessageMode mode = MessageMode::DEFAULT) { #ifndef NDEBUG - log(Severity::DEBUG, msg, loc); + log(Severity::DEBUG, msg, loc, mode); #endif } @@ -305,9 +349,10 @@ public: * @param loc is the location in the source file the message refers to. */ void note(const std::string &msg, - const SourceLocation &loc = SourceLocation{}) + const SourceLocation &loc = SourceLocation{}, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::NOTE, msg, loc); + log(Severity::NOTE, msg, loc, mode); } /** @@ -318,9 +363,10 @@ public: * information. */ template <class LocationType> - void note(const std::string &msg, LocationType &loc) + void note(const std::string &msg, LocationType loc, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::NOTE, msg, loc); + log(Severity::NOTE, msg, loc, mode); } /** @@ -330,9 +376,10 @@ public: * @param loc is a reference to a variable which provides position */ void warning(const std::string &msg, - const SourceLocation &loc = SourceLocation{}) + const SourceLocation &loc = SourceLocation{}, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::WARNING, msg, loc); + log(Severity::WARNING, msg, loc, mode); } /** @@ -343,9 +390,10 @@ public: * information. */ template <class LocationType> - void warning(const std::string &msg, LocationType &loc) + void warning(const std::string &msg, LocationType loc, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::WARNING, msg, loc); + log(Severity::WARNING, msg, location(loc), mode); } /** @@ -355,9 +403,10 @@ public: * @param loc is a reference to a variable which provides position */ void error(const std::string &msg, - const SourceLocation &loc = SourceLocation{}) + const SourceLocation &loc = SourceLocation{}, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::ERROR, msg, std::move(loc)); + log(Severity::ERROR, msg, loc, mode); } /** @@ -368,9 +417,10 @@ public: * information. */ template <class LocationType> - void error(const std::string &msg, LocationType &loc) + void error(const std::string &msg, LocationType loc, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::ERROR, msg, loc); + log(Severity::ERROR, msg, location(loc), mode); } /** @@ -380,9 +430,10 @@ public: * @param loc is a reference to a variable which provides position */ void fatalError(const std::string &msg, - const SourceLocation &loc = SourceLocation{}) + const SourceLocation &loc = SourceLocation{}, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::FATAL_ERROR, msg, loc); + log(Severity::FATAL_ERROR, msg, loc, mode); } /** @@ -393,41 +444,44 @@ public: * information. */ template <class LocationType> - void fatalError(const std::string &msg, LocationType &loc) + void fatalError(const std::string &msg, LocationType loc, + MessageMode mode = MessageMode::DEFAULT) { - log(Severity::FATAL_ERROR, msg, loc); + log(Severity::FATAL_ERROR, msg, location(loc), mode); } /** - * Pushes a new file name onto the internal filename stack. + * Sets the source context callback to be used to resolve SourceLocation + * instances to SourceContext instances. The sourceContextCallback should be + * set as early as possible when using the logger. * - * @param name is the name of the file to be added to the stack. - * @param loc is the position from which the new file is included. - * @param ctxCallback is the callback function that should be called if a - * SourceLocation needs to be resolved to a SourceContext. - * @param ctxCallbackData is the data that should be passed to the callback. + * @param sourceContextCallback is the new sourceContextCallback to be used. */ - void pushFile(std::string name, SourceLocation loc = SourceLocation{}, - SourceContextCallback ctxCallback = nullptr, - void *ctxCallbackData = nullptr) + void setSourceContextCallback(SourceContextCallback sourceContextCallback) { - processPushFile( - File(std::move(name), loc, ctxCallback, ctxCallbackData)); + processSetSourceContextCallback(sourceContextCallback); } /** - * Pops the filename from the internal filename stack. Resets any location - * set by the setDefaultLocation() method. + * Pushes a new default location onto the default location stack. + * + * @param loc is the location that should be used if no (valid) location is + * specified in the Logger. */ - void popFile() + void pushDefaultLocation(const SourceLocation &loc) { - processPopFile(); - resetDefaultLocation(); + processPushDefaultLocation(loc); } /** - * Sets the default location. The default location is automatically reset - * once the popFile() method is called. + * Pops the last default location from the default location stack. + */ + void popDefaultLocation() { processPopDefaultLocation(); } + + /** + * Replaces the topmost default location on the location stack with the + * given location. Creates a new entry in the location stack if the stack + * was empty. * * @param loc is the location that should be used if no (valid) location is * specified in the Logger. @@ -438,12 +492,6 @@ public: } /** - * Resets the default location, a previously set default location will be - * no longer used. - */ - void resetDefaultLocation() { processSetDefaultLocation(SourceLocation{}); } - - /** * Returns a forked logger instance which can be used to collect log * messages for which it is not sure whether they will be used. * @@ -469,7 +517,13 @@ private: /** * Intanally used to store the incomming function calls. */ - enum class CallType { MESSAGE, PUSH_FILE, POP_FILE, SET_DEFAULT_LOCATION }; + enum class CallType { + MESSAGE, + PUSH_LOCATION, + POP_LOCATION, + SET_LOCATION, + SET_CONTEXT_CALLBACK + }; /** * Datastructure used to represent a logger function call. @@ -506,14 +560,14 @@ private: std::vector<Message> messages; /** - * Vector storing all incomming pushed Scope instances. + * Vector storing all incomming location instances. */ - std::vector<File> files; + std::vector<SourceLocation> locations; /** - * Vector storing all incomming location instances. + * Vector storing all incomming source context callbacks. */ - std::vector<SourceLocation> locations; + std::vector<SourceContextCallback> callbacks; /** * Parent logger instance. @@ -529,17 +583,19 @@ private: protected: void processMessage(const Message &msg) override; - void processPushFile(const File &file) override; - void processPopFile() override; + void processPushDefaultLocation(const SourceLocation &loc) override; + void processPopDefaultLocation() override; void processSetDefaultLocation(const SourceLocation &loc) override; + void processSetSourceContextCallback( + SourceContextCallback sourceContextCallback) override; public: // Default move constructor LoggerFork(LoggerFork &&l) : calls(std::move(l.calls)), messages(std::move(l.messages)), - files(std::move(l.files)), locations(std::move(l.locations)), + callbacks(std::move(l.callbacks)), parent(std::move(l.parent)){}; /** @@ -578,93 +634,54 @@ protected: * * @param msg is the message to be relayed to the parent logger. */ - void processMessage(const Message &msg) override - { - parent.processMessage(msg); - } + void processMessage(const Message &msg) override; /** * Relays the filterMessage call to the parent logger. * * @param msg is the message to be relayed to the parent logger. */ - bool filterMessage(const Message &msg) override - { - return parent.filterMessage(msg); - } + bool filterMessage(const Message &msg) override; /** - * Relays the processPushFile call to the parent logger and increments the - * stack depth counter. - * - * @param file is the File instance to be relayed to the parent logger. + * Relays the processPushDefaultLocation call to the parent logger and + * increments the stack depth counter. */ - void processPushFile(const File &file) - { - parent.processPushFile(file); - depth++; - } + void processPushDefaultLocation(const SourceLocation &loc) override; /** - * Relays the processPopFile call to the parent logger and decrements the - * stack depth counter. + * Relays the processPopDefaultLocation call to the parent logger and + * decrements the stack depth counter. */ - void processPopFile() - { - depth--; - parent.processPopFile(); - } + void processPopDefaultLocation() override; /** * Relays the processSetDefaultLocation call to the parent logger. - * - * @param loc is the location to be passed to the parent logger. */ - void processSetDefaultLocation(const SourceLocation &loc) - { - parent.processSetDefaultLocation(loc); - } + void processSetDefaultLocation(const SourceLocation &loc) override; -public: /** - * Constructor of the ScopedLogger class. - * - * @param parent is the parent logger instance to which all calls should - * be relayed. + * Relays the processSetSourceContextCallback call to the parent logger. */ - ScopedLogger(Logger &parent) : Logger(), parent(parent) {} + void processSetSourceContextCallback( + SourceContextCallback sourceContextCallback) override; +public: /** * Constructor of the ScopedLogger class, pushes a first file instance onto * the file stack. * * @param parent is the parent logger instance to which all calls should * be relayed. - * @param name is the name of the file to be added to the stack. - * @param loc is the position from which the new file is included. - * @param ctxCallback is the callback function that should be called if a - * SourceLocation needs to be resolved to a SourceContext. - * @param ctxCallbackData is the data that should be passed to the callback. - */ - ScopedLogger(Logger &parent, std::string name, - SourceLocation loc = SourceLocation{}, - SourceContextCallback ctxCallback = nullptr, - void *ctxCallbackData = nullptr) - : Logger(), parent(parent), depth(0) - { - pushFile(name, loc, ctxCallback, ctxCallbackData); - } + * @param loc specifies the first source location. + */ + ScopedLogger(Logger &parent, SourceLocation loc = SourceLocation{}); /** * Destructor of the ScopedLogger class, automatically unwinds the file * stack. */ - ~ScopedLogger() - { - while (depth > 0) { - processPopFile(); - } - } + ~ScopedLogger(); }; /** @@ -681,7 +698,7 @@ protected: { if (msg.severity == Severity::ERROR || msg.severity == Severity::FATAL_ERROR) { - throw LoggableException(msg.msg); + throw LoggableException(msg.msg, msg.loc); } } }; @@ -701,14 +718,14 @@ constexpr Severity DEFAULT_MIN_SEVERITY = Severity::DEBUG; class ConcreteLogger : public Logger { private: /** - * Stack containing the current file instance. + * Vector used to store the counts of each message type. */ - std::vector<File> files; + std::vector<size_t> messageCounts; /** - * Vector used to store the counts of each message type. + * Vector used to store the current default locations. */ - std::vector<size_t> messageCounts; + std::vector<SourceLocation> locations; /** * Minimum severity to be used for filtering messages. @@ -716,9 +733,9 @@ private: Severity minSeverity; /** - * Current default location. + * Current source context callback. */ - SourceLocation defaultLocation; + SourceContextCallback sourceContextCallback; protected: /** @@ -730,25 +747,11 @@ protected: */ bool filterMessage(const Message &msg) override; - /** - * Pushes the given file descriptor onto the internal file stack. - * - * @param file is the File descriptor to be pushed onto the internal file - * stack. - */ - void processPushFile(const File &file) override; - - /** - * Pops the given file descriptor from the internal file stack. - */ - void processPopFile() override; - - /** - * Sets the default location. - * - * @param loc is the new default location. - */ + void processPushDefaultLocation(const SourceLocation &loc) override; + void processPopDefaultLocation() override; void processSetDefaultLocation(const SourceLocation &loc) override; + void processSetSourceContextCallback( + SourceContextCallback sourceContextCallback) override; public: /** @@ -757,24 +760,7 @@ public: * @param minSeverity is the severity below which message should be * discarded. */ - ConcreteLogger(Severity minSeverity = DEFAULT_MIN_SEVERITY) - : minSeverity(minSeverity) - { - } - - /** - * Returns the name of the current file or an empty instance of the File - * instance if no current file is available. - * - * @return the name of the current file. - */ - const File ¤tFile() const; - - /** - * Returns the current filename or an empty string if no surch file is - * available. - */ - const std::string ¤tFilename() const; + ConcreteLogger(Severity minSeverity = DEFAULT_MIN_SEVERITY); /** * Returns the current cursor location. @@ -800,7 +786,8 @@ public: /** * Returns the number of messages for the given severity. * - * @param severity is the log severity for which the message count should + * @param severity is the log severity for which the message count + *should * be returned. * @return the number of messages for this severity. Returns zero for * invalid arguments. @@ -808,22 +795,32 @@ public: size_t getSeverityCount(Severity severity); /** - * Resets the statistics gathered by the ConcreteLogger instance (the number + * Resets the statistics gathered by the ConcreteLogger instance (the + * number * of messages per log severity) and the internal file stack. */ void reset(); /** - * Returns true if at least one message with either a fatal error or error - * severity was logged. + * Returns true if at least one message with either a fatal error or + * error severity was logged. * * @return true if an error or fatal error was logged. */ bool hasError(); + + /** + * Returns true if at least one message with either a fatal error was + * logged. + * + * @return true if a fatal error was logged. + */ + bool hasFatalError(); }; /** - * Class extending the Logger class and printing the log messages to the given + * Class extending the Logger class and printing the log messages to the + * given * stream. */ class TerminalLogger : public ConcreteLogger { @@ -850,7 +847,8 @@ public: * Should be set to std::cerr in most cases. * @param useColor if true, the TerminalLogger class will do its best to * use ANSI/VT100 control sequences for colored log messages. - * @param minSeverity is the minimum severity below which log messages are + * @param minSeverity is the minimum severity below which log messages + *are * discarded. */ TerminalLogger(std::ostream &os, bool useColor = false, diff --git a/src/core/common/Rtti.cpp b/src/core/common/Rtti.cpp index 17b8880..6849a0e 100644 --- a/src/core/common/Rtti.cpp +++ b/src/core/common/Rtti.cpp @@ -47,8 +47,8 @@ const Rtti &RttiStore::lookup(const std::type_info &native) /* Class RttiBuilderBase */ -RttiBuilderBase &RttiBuilderBase::genericMethod(const std::string &name, - std::shared_ptr<Function> function) +RttiBuilderBase &RttiBuilderBase::genericMethod( + const std::string &name, std::shared_ptr<Function> function) { if (!methods.emplace(name, function).second) { throw OusiaException(std::string("Method with name \"") + name + @@ -80,10 +80,11 @@ void Rtti::initialize() const // Register the parent properties and methods { - for (const Rtti *parent: parents) { + for (const Rtti *parent : parents) { parent->initialize(); methods.insert(parent->methods.begin(), parent->methods.end()); - properties.insert(parent->properties.begin(), parent->properties.end()); + properties.insert(parent->properties.begin(), + parent->properties.end()); } } @@ -125,18 +126,41 @@ bool Rtti::isa(const Rtti &other) const return parents.count(&other) > 0; } +bool Rtti::isOneOf(const RttiSet &others) const +{ + initialize(); + for (const Rtti *other : others) { + if (parents.count(other) > 0) { + return true; + } + } + return false; +} + +bool Rtti::setIsOneOf(const RttiSet &s1, const RttiSet &s2) +{ + for (const Rtti *t1 : s1) { + if (t1->isOneOf(s2)) { + return true; + } + } + return false; +} + bool Rtti::composedOf(const Rtti &other) const { initialize(); return compositeTypes.count(&other) > 0; } -const RttiMethodMap &Rtti::getMethods() const { +const RttiMethodMap &Rtti::getMethods() const +{ initialize(); return methods; } -const RttiPropertyMap &Rtti::getProperties() const { +const RttiPropertyMap &Rtti::getProperties() const +{ initialize(); return properties; } @@ -151,7 +175,8 @@ std::shared_ptr<Function> Rtti::getMethod(const std::string &name) const return it->second; } -std::shared_ptr<PropertyDescriptor> Rtti::getProperty(const std::string &name) const +std::shared_ptr<PropertyDescriptor> Rtti::getProperty( + const std::string &name) const { initialize(); auto it = properties.find(name); diff --git a/src/core/common/Rtti.hpp b/src/core/common/Rtti.hpp index 8350cb5..05cc728 100644 --- a/src/core/common/Rtti.hpp +++ b/src/core/common/Rtti.hpp @@ -378,10 +378,31 @@ public: * * @param other is the other type for which the relation to this type * should be checked. + * @return true if this type (directly or indirectly) has the given other + * type as parent or equals the other type. */ bool isa(const Rtti &other) const; /** + * Returns true if this Rtti instance is one of the given types. + * + * @param others is a set of other types to be checked. + * @return true if this type (directly or indirectly) has once of the given + * other types as parent or equals one of the other types. + */ + bool isOneOf(const RttiSet &others) const; + + /** + * Checks whether any type in the first set is one type in the second set. + * + * @param s1 is the first set. For each type in this set we check whether + * it is one of the types in s2. + * @param s2 is the second set. + * @return true if the above condition is fulfilled, false otherwise. + */ + static bool setIsOneOf(const RttiSet &s1, const RttiSet &s2); + + /** * Returns true if an instance of this type may have references to the other * given type. This mechanism is used to prune impossible paths when * resolving objects of a certain type by name in an object graph. diff --git a/src/core/common/Utils.cpp b/src/core/common/Utils.cpp index 5fde29c..c8fcdc6 100644 --- a/src/core/common/Utils.cpp +++ b/src/core/common/Utils.cpp @@ -17,7 +17,9 @@ */ #include <algorithm> +#include <cctype> #include <limits> +#include <string> #include "Utils.hpp" @@ -74,5 +76,26 @@ std::vector<std::string> Utils::split(const std::string &s, char delim) return res; } +std::string Utils::toLower(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), tolower); + return s; +} + +std::string Utils::extractFileExtension(const std::string &filename) +{ + size_t n = 0; + for (ssize_t i = filename.size() - 1; i >= 0; i--) { + if (filename[i] == '/' || filename[i] == '\\') { + return std::string{}; + } + if (filename[i] == '.') { + return toLower(filename.substr(i + 1, n)); + } + n++; + } + return std::string{}; +} + } diff --git a/src/core/common/Utils.hpp b/src/core/common/Utils.hpp index 1f7f142..22e0fd3 100644 --- a/src/core/common/Utils.hpp +++ b/src/core/common/Utils.hpp @@ -114,6 +114,26 @@ public: * @return a vector of strings containing the splitted sub-strings. */ static std::vector<std::string> split(const std::string &s, char delim); + + /** + * Converts the given string to lowercase (only works for ANSI characters). + * + * @param s is the string that should be converted to lowercase. + * @return s in lowercase. + */ + static std::string toLower(std::string s); + + /** + * Reads the file extension of the given filename. + * + * @param filename is the filename from which the extension should be + * extracted. + * @return the extension, excluding any leading dot. The extension is + * defined as the substring after the last dot in the given string, if the + * dot is after a slash or backslash. The extension is converted to + * lowercase. + */ + static std::string extractFileExtension(const std::string &filename); }; } |