summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2015-01-23 15:47:59 +0100
committerBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2015-01-23 15:47:59 +0100
commit18d3637ca02ab69f1ee744fa94c43c243de0f571 (patch)
tree42c859f014ab7dbb7d31a747e0ef3839c77c60fa /src
parent85d72823ef18711fe7a29f5b23cc37b318766332 (diff)
parentaa817d3bfd90aa39b6fd8a915bc78a8bb210cd3d (diff)
Merge branch 'master' of somweyr.de:ousia
Diffstat (limited to 'src')
-rw-r--r--src/core/CodeTokenizer.cpp32
-rw-r--r--src/core/Registry.cpp90
-rw-r--r--src/core/Registry.hpp129
-rw-r--r--src/core/Tokenizer.cpp19
-rw-r--r--src/core/Tokenizer.hpp13
-rw-r--r--src/core/common/CharReader.cpp220
-rw-r--r--src/core/common/CharReader.hpp173
-rw-r--r--src/core/common/Exceptions.cpp16
-rw-r--r--src/core/common/Exceptions.hpp26
-rw-r--r--src/core/common/Location.cpp33
-rw-r--r--src/core/common/Location.hpp426
-rw-r--r--src/core/common/Logger.cpp222
-rw-r--r--src/core/common/Logger.hpp448
-rw-r--r--src/core/common/Rtti.cpp39
-rw-r--r--src/core/common/Rtti.hpp21
-rw-r--r--src/core/common/Utils.cpp23
-rw-r--r--src/core/common/Utils.hpp20
-rw-r--r--src/core/managed/Managed.hpp32
-rw-r--r--src/core/managed/Manager.hpp3
-rw-r--r--src/core/model/Node.cpp4
-rw-r--r--src/core/model/Node.hpp22
-rw-r--r--src/core/model/Project.cpp42
-rw-r--r--src/core/model/Project.hpp51
-rw-r--r--src/core/parser/Parser.cpp13
-rw-r--r--src/core/parser/Parser.hpp101
-rw-r--r--src/core/parser/ParserContext.cpp36
-rw-r--r--src/core/parser/ParserContext.hpp92
-rw-r--r--src/core/parser/ParserScope.cpp (renamed from src/core/parser/Scope.cpp)58
-rw-r--r--src/core/parser/ParserScope.hpp (renamed from src/core/parser/Scope.hpp)102
-rw-r--r--src/core/parser/ParserStack.cpp2
-rw-r--r--src/core/parser/ParserStack.hpp22
-rw-r--r--src/core/resource/ResourceManager.cpp275
-rw-r--r--src/core/resource/ResourceManager.hpp236
-rw-r--r--src/core/resource/ResourceUtils.cpp138
-rw-r--r--src/core/resource/ResourceUtils.hpp128
-rw-r--r--src/plugins/css/CSSParser.cpp8
-rw-r--r--src/plugins/css/CSSParser.hpp20
-rw-r--r--src/plugins/filesystem/FileLocator.cpp9
-rw-r--r--src/plugins/xml/XmlParser.cpp52
-rw-r--r--src/plugins/xml/XmlParser.hpp18
40 files changed, 2313 insertions, 1101 deletions
diff --git a/src/core/CodeTokenizer.cpp b/src/core/CodeTokenizer.cpp
index e9c1bbf..fbc1150 100644
--- a/src/core/CodeTokenizer.cpp
+++ b/src/core/CodeTokenizer.cpp
@@ -26,8 +26,10 @@ Token CodeTokenizer::constructToken(const Token &t)
{
std::string content = buf.str();
buf.str(std::string());
- return Token{returnTokenId, content, startToken.startColumn,
- startToken.startLine, t.endColumn, t.endLine};
+ return Token{
+ returnTokenId, content,
+ SourceLocation{t.location.getSourceId(), startToken.location.getStart(),
+ t.location.getEnd()}};
}
void CodeTokenizer::buffer(const Token &t) { buf << t.content; }
@@ -40,12 +42,6 @@ bool CodeTokenizer::doPrepare(const Token &t, std::deque<Token> &peeked)
mode = it->second.mode;
}
- if (t.startLine != t.endLine && mode != CodeTokenMode::LINEBREAK) {
- throw TokenizerException(
- "We did not expect a multiline token (except linebreaks). Most "
- "likely you did not add a linebreak token to your tokenizer!");
- }
-
switch (state) {
case CodeTokenizerState::NORMAL:
switch (mode) {
@@ -60,9 +56,8 @@ bool CodeTokenizer::doPrepare(const Token &t, std::deque<Token> &peeked)
break;
case CodeTokenMode::LINEBREAK:
if (!ignoreLinebreaks) {
- peeked.push_back({it->second.id, t.content,
- t.startColumn, t.startLine,
- t.endColumn, t.endLine});
+ peeked.push_back(
+ {it->second.id, t.content, t.location});
}
return !ignoreLinebreaks;
default:
@@ -87,18 +82,21 @@ bool CodeTokenizer::doPrepare(const Token &t, std::deque<Token> &peeked)
peeked.push_back(Token{
TOKEN_TEXT,
t.content.substr(begin, (int)c - begin),
- t.startColumn + begin, t.startLine,
- t.startColumn + (int)c, t.endLine});
+ SourceLocation{
+ t.location.getSourceId(),
+ t.location.getStart() + begin,
+ t.location.getStart() + c}});
begin = -1;
empty = false;
}
}
}
if (begin >= 0) {
- peeked.push_back(
- Token{TOKEN_TEXT, t.content.substr(begin),
- t.startColumn + begin, t.startLine,
- t.endColumn, t.endLine});
+ peeked.push_back(Token{
+ TOKEN_TEXT, t.content.substr(begin),
+ SourceLocation{t.location.getSourceId(),
+ t.location.getStart() + begin,
+ t.location.getEnd()}});
empty = false;
}
} else {
diff --git a/src/core/Registry.cpp b/src/core/Registry.cpp
index 86665a2..4aad7db 100644
--- a/src/core/Registry.cpp
+++ b/src/core/Registry.cpp
@@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <core/common/Exceptions.hpp>
+#include <core/common/Utils.hpp>
#include <core/parser/Parser.hpp>
#include <core/resource/Resource.hpp>
#include <core/resource/ResourceLocator.hpp>
@@ -24,32 +26,92 @@
namespace ousia {
-using namespace parser;
-
/* Class Registry */
-void Registry::registerParser(parser::Parser &parser)
+void Registry::registerParser(const std::set<std::string> &mimetypes,
+ const RttiSet &types, Parser *parser)
+{
+ for (const std::string &mimetype : mimetypes) {
+ // Make sure no other parser was given for this mimetype
+ auto it = parsers.find(mimetype);
+ if (it != parsers.end()) {
+ throw OusiaException{std::string{"Parser for mimetype "} +
+ mimetype +
+ std::string{" already registered."}};
+ }
+
+ // Store a reference at the parser and a copy of the given RttiSet
+ parsers[mimetype] = std::pair<Parser *, RttiSet>{parser, types};
+ }
+}
+
+static const std::pair<Parser *, RttiSet> NullParser{nullptr, RttiSet{}};
+
+const std::pair<Parser *, RttiSet> &Registry::getParserForMimetype(
+ const std::string &mimetype) const
+{
+ const auto it = parsers.find(mimetype);
+ if (it != parsers.end()) {
+ return it->second;
+ }
+ return NullParser;
+}
+
+void Registry::registerExtension(const std::string &extension,
+ const std::string &mimetype)
{
- parsers.push_back(&parser);
- for (const auto &mime : parser.mimetypes()) {
- //TODO: This does not allow for multiple parsers with the same mimetype.
- // Is that how its supposed to be?
- parserMimetypes.insert(std::make_pair(mime, &parser));
+ // Always use extensions in lower case
+ std::string ext = Utils::toLower(extension);
+
+ // Make sure the extension is unique
+ auto it = extensions.find(ext);
+ if (it != extensions.end()) {
+ throw OusiaException{std::string{"Extension "} + extension +
+ std::string{" already registered."}};
}
+
+ // Register the mimetype
+ extensions[ext] = mimetype;
+}
+
+void Registry::registerDefaultExtensions()
+{
+ registerExtension("oxd", "text/vnd.ousia.oxd");
+ registerExtension("oxm", "text/vnd.ousia.oxm");
+ registerExtension("opd", "text/vnd.ousia.opd");
+ registerExtension("oss", "text/vnd.ousia.oss");
+ registerExtension("js", "application/javascript");
}
-Parser *Registry::getParserForMimetype(const std::string &mimetype) const
+std::string Registry::getMimetypeForExtension(
+ const std::string &extension) const
{
- const auto it = parserMimetypes.find(mimetype);
- if (it != parserMimetypes.end()) {
+ // Always use extensions in lower case
+ std::string ext = Utils::toLower(extension);
+
+ // Try to find the extension
+ auto it = extensions.find(ext);
+ if (it != extensions.end()) {
return it->second;
}
- return nullptr;
+ return std::string{};
+}
+
+std::string Registry::getMimetypeForFilename(const std::string &filename) const
+{
+ // Fetch the file extension
+ std::string ext = Utils::extractFileExtension(filename);
+ if (ext.empty()) {
+ return std::string{};
+ }
+
+ // Fetch the mimetype for the extension
+ return getMimetypeForExtension(ext);
}
-void Registry::registerResourceLocator(ResourceLocator &locator)
+void Registry::registerResourceLocator(ResourceLocator *locator)
{
- locators.push_back(&locator);
+ locators.push_back(locator);
}
bool Registry::locateResource(Resource &resource, const std::string &path,
diff --git a/src/core/Registry.hpp b/src/core/Registry.hpp
index 40eede1..4b4cb65 100644
--- a/src/core/Registry.hpp
+++ b/src/core/Registry.hpp
@@ -16,37 +16,140 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/**
+ * @file Registry.hpp
+ *
+ * Class used for registering plugin classes. The Registry is one of the central
+ * classes needed for parsing and transforming an Ousía document.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
#ifndef _OUSIA_REGISTRY_HPP_
#define _OUSIA_REGISTRY_HPP_
#include <map>
+#include <set>
#include <vector>
+#include <core/common/Rtti.hpp>
#include <core/resource/Resource.hpp>
namespace ousia {
-// TODO: Add support for ScriptEngine type
-
-namespace parser {
+// Forward declarations
class Parser;
-}
class ResourceLocator;
+/**
+ * The Registry class is the central class which is used to store references to
+ * all available plugins.
+ */
class Registry {
private:
- std::vector<parser::Parser *> parsers;
- std::map<std::string, parser::Parser *> parserMimetypes;
-
+ /**
+ * Mapping between parser mimetypes and pairs of parsers and their supported
+ * Rtti types.
+ */
+ std::map<std::string, std::pair<Parser *, RttiSet>> parsers;
+
+ /**
+ * Map from file extensions to registered mimetypes.
+ */
+ std::map<std::string, std::string> extensions;
+
+ /**
+ * List containing all registered ResourceLocator instances.
+ */
std::vector<ResourceLocator *> locators;
public:
- void registerParser(parser::Parser &parser);
-
- parser::Parser *getParserForMimetype(const std::string &mimetype) const;
-
- void registerResourceLocator(ResourceLocator &locator);
-
+ /**
+ * Registers a new parser instance for the given set of mimetypes. Throws
+ * an exception if a parser is already registered for one of the mimetypes.
+ *
+ * @param mimetypes is a set of mimetypes for which the Parser should be
+ * registered.
+ * @param types is a set of node the parser is known to return. This
+ * information is needed in order to prevent inclusion of the wrong Node
+ * types
+ * @param parser is the parser instance that is registered for the given
+ * mimetypes.
+ */
+ void registerParser(const std::set<std::string> &mimetypes,
+ const RttiSet &types, Parser *parser);
+
+ /**
+ * Returns a pointer pointing at a Parser that was registered for handling
+ * the given mimetype.
+ *
+ * @param mimetype is the mimetype for which a Parser instance should be
+ * looked up.
+ * @return a pair containing a pointer at the parser and the RttiTypes
+ * supported by the parser. The pointer is set to a nullptr if no such
+ * parser could be found.
+ */
+ const std::pair<Parser *, RttiSet> &getParserForMimetype(
+ const std::string &mimetype) const;
+
+ /**
+ * Registers a file extension with a certain mimetype. Throws an exception
+ * if a mimetype is already registered for this extension.
+ *
+ * @param extension is the file extension for which the mimetype should be
+ * registered. The extension has to be provided without a leading dot. The
+ * extensions are handled case insensitive.
+ * @param mimetype is the mimetype that should be registered for the
+ * extension.
+ */
+ void registerExtension(const std::string &extension,
+ const std::string &mimetype);
+
+ /**
+ * Registers mimetypes for some default extensions.
+ */
+ void registerDefaultExtensions();
+
+ /**
+ * Returns the mimetype for the given extension.
+ *
+ * @param extension is the file extension for which the mimetype should be
+ * looked up. The extension has to be provided without a leading dot. The
+ * extensions are handled case insensitive.
+ * @return the registered mimetype or an empty string of none was found.
+ */
+ std::string getMimetypeForExtension(const std::string &extension) const;
+
+ /**
+ * Tries to deduce the mimetype from the given filename.
+ *
+ * @param filename is the filename from which the mimetype should be
+ * deduced.
+ * @return the mimetype or an empty string if no filename could be deduced.
+ */
+ std::string getMimetypeForFilename(const std::string &filename) const;
+
+ /**
+ * Registers a ResourceLocator instance that should be used for locating
+ * resources. Two registered ResourceLocator should not be capable of
+ * accessing Resources at the same location. If this happens, the resource
+ * locator that was registered first has precedence.
+ *
+ * @param locator is the ResourceLocator instance that should be registered.
+ */
+ void registerResourceLocator(ResourceLocator *locator);
+
+ /**
+ * Locates a resource using the registered ResourceLocator instances.
+ *
+ * @param resource is the resource descriptor to which the result will be
+ * written.
+ * @param path is the path under which the resource should be looked up.
+ * @param type is the ResourceType. Specifying a resource type may help to
+ * locate the resource.
+ * @param relativeTo is another resource relative to which the resource may
+ * be looked up.
+ */
bool locateResource(Resource &resource, const std::string &path,
ResourceType type = ResourceType::UNKNOWN,
const Resource &relativeTo = NullResource) const;
diff --git a/src/core/Tokenizer.cpp b/src/core/Tokenizer.cpp
index 9d25608..ab4735a 100644
--- a/src/core/Tokenizer.cpp
+++ b/src/core/Tokenizer.cpp
@@ -81,8 +81,7 @@ bool Tokenizer::prepare()
{
std::stringstream buffer;
char c;
- int startColumn = input.getColumn();
- int startLine = input.getLine();
+ SourcePosition start = input.getOffset();
bool bufEmpty = true;
while (input.peek(c)) {
if (root.children.find(c) != root.children.end()) {
@@ -124,20 +123,16 @@ bool Tokenizer::prepare()
if (bufEmpty) {
// if we did not have text before, construct that token.
if (doPrepare(
- Token{match, tBuf.str(), startColumn, startLine,
- input.getColumn(), input.getLine()},
+ Token{match, tBuf.str(), input.getLocation(start)},
peeked)) {
return true;
} else {
- startColumn = input.getColumn();
- startLine = input.getLine();
+ start = input.getOffset();
continue;
}
} else {
// otherwise we return the text before the token.
- if (doPrepare(Token{TOKEN_TEXT, buffer.str(), startColumn,
- startLine, input.getColumn(),
- input.getLine()},
+ if (doPrepare(Token{TOKEN_TEXT, buffer.str(), input.getLocation(start)},
peeked)) {
return true;
} else{
@@ -146,8 +141,7 @@ bool Tokenizer::prepare()
//constructed.
buffer.str(std::string());
bufEmpty = true;
- startColumn = input.getColumn();
- startLine = input.getLine();
+ start = input.getOffset();
continue;
}
}
@@ -161,8 +155,7 @@ bool Tokenizer::prepare()
input.consumePeek();
}
if (!bufEmpty) {
- return doPrepare(Token{TOKEN_TEXT, buffer.str(), startColumn, startLine,
- input.getColumn(), input.getLine()},
+ return doPrepare(Token{TOKEN_TEXT, buffer.str(), input.getLocation(start)},
peeked);
}
return false;
diff --git a/src/core/Tokenizer.hpp b/src/core/Tokenizer.hpp
index 2b03e17..50e458c 100644
--- a/src/core/Tokenizer.hpp
+++ b/src/core/Tokenizer.hpp
@@ -121,19 +121,12 @@ static const int TOKEN_TEXT = -2;
struct Token {
int tokenId;
std::string content;
- int startColumn;
- int startLine;
- int endColumn;
- int endLine;
+ SourceLocation location;
- Token(int tokenId, std::string content, int startColumn, int startLine,
- int endColumn, int endLine)
+ Token(int tokenId, std::string content, SourceLocation location)
: tokenId(tokenId),
content(content),
- startColumn(startColumn),
- startLine(startLine),
- endColumn(endColumn),
- endLine(endLine)
+ location(location)
{
}
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 &currentFile() const;
-
- /**
- * Returns the current filename or an empty string if no surch file is
- * available.
- */
- const std::string &currentFilename() 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);
};
}
diff --git a/src/core/managed/Managed.hpp b/src/core/managed/Managed.hpp
index 08158b2..8b594ea 100644
--- a/src/core/managed/Managed.hpp
+++ b/src/core/managed/Managed.hpp
@@ -143,14 +143,46 @@ public:
/* Data store methods */
+ /**
+ * Stores arbitrary data under the given key. Data will be overriden. This
+ * function can be used to attach data to the Managed object.
+ *
+ * @param key is an arbitrary string key that under which the data should
+ * be stored.
+ * @param h is the data that should be stored.
+ */
void storeData(const std::string &key, Handle<Managed> h);
+ /**
+ * Returns true if data was stored under the given key.
+ *
+ * @return true if data was stored under the given key, false otherwise.
+ */
bool hasDataKey(const std::string &key);
+ /**
+ * Returns data previously stored under the given key.
+ *
+ * @param key is the key specifying the slot from which the data should be
+ * read.
+ * @return previously stored data or nullptr if no data was stored for this
+ * key.
+ */
Rooted<Managed> readData(const std::string &key);
+ /**
+ * Returns a copy of all data that was attached to the node.
+ *
+ * @return a map between keys and stored data.
+ */
std::map<std::string, Rooted<Managed>> readData();
+ /**
+ * Deletes all data entries with the given key from the node.
+ *
+ * @param key is the key specifying the slot that should be deleted.
+ * @return true if the operation was successful, false otherwise.
+ */
bool deleteData(const std::string &key);
/* Event handling methods */
diff --git a/src/core/managed/Manager.hpp b/src/core/managed/Manager.hpp
index fec4bd1..7a93736 100644
--- a/src/core/managed/Manager.hpp
+++ b/src/core/managed/Manager.hpp
@@ -320,7 +320,8 @@ public:
* references.
*
* @param uid is the unique id for which the object should be returned.
- * @return a pointer to the object with the given uid.
+ * @return a pointer to the object with the given uid or nullptr if the
+ * object no longer exists.
*/
Managed * getManaged(ManagedUid uid);
diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp
index eb0e4a7..dbc85e2 100644
--- a/src/core/model/Node.cpp
+++ b/src/core/model/Node.cpp
@@ -363,7 +363,7 @@ bool Node::checkDuplicate(Handle<Node> elem,
logger.error(std::string("Element with name \"") + name +
std::string("\" defined multiple times in parent ") +
type().name + std::string(" \"") +
- Utils::join(path(), ".") + std::string("\""));
+ Utils::join(path(), ".") + std::string("\""), *elem);
return false;
}
return true;
@@ -375,7 +375,7 @@ bool Node::validateName(Logger &logger) const
{
if (!Utils::isIdentifier(name)) {
logger.error(type().name + std::string(" name \"") + name +
- std::string("\" is not a valid identifier"));
+ std::string("\" is not a valid identifier"), this);
return false;
}
return true;
diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp
index c5761a8..6fc7dba 100644
--- a/src/core/model/Node.hpp
+++ b/src/core/model/Node.hpp
@@ -35,6 +35,7 @@
#include <string>
#include <vector>
+#include <core/common/Location.hpp>
#include <core/managed/Managed.hpp>
#include <core/managed/ManagedContainer.hpp>
@@ -137,6 +138,12 @@ private:
Owned<Node> parent;
/**
+ * Location from which the node was read (specifies the source file and the
+ * range in that source file).
+ */
+ SourceLocation location;
+
+ /**
* A "dirty" flag that signifies if this Node has been already validated
* or not.
*/
@@ -519,6 +526,21 @@ public:
* @return the current ValidationState of this Node.
*/
ValidationState getValidationState() const { return validationState; }
+
+ /**
+ * Returns the location in the source file.
+ *
+ * @return a source location descriptor.
+ */
+ SourceLocation getLocation() const { return location; }
+
+ /**
+ * Sets the location of the node to the given value.
+ *
+ * @param location describes the exact position of the Node in a source
+ * file.
+ */
+ void setLocation(const SourceLocation &location) {this->location = location;}
};
/**
diff --git a/src/core/model/Project.cpp b/src/core/model/Project.cpp
index fc08660..a0f1f08 100644
--- a/src/core/model/Project.cpp
+++ b/src/core/model/Project.cpp
@@ -30,17 +30,13 @@ namespace model {
Project::Project(Manager &mgr)
: Node(mgr),
systemTypesystem(acquire(new SystemTypesystem(mgr))),
- documents(this),
- domains(this),
- typesystems(this)
+ documents(this)
{
}
bool Project::doValidate(Logger &logger) const
{
- return continueValidation(documents, logger) &
- continueValidation(domains, logger) &
- continueValidation(typesystems, logger);
+ return continueValidation(documents, logger);
}
Rooted<SystemTypesystem> Project::getSystemTypesystem()
@@ -50,16 +46,8 @@ Rooted<SystemTypesystem> Project::getSystemTypesystem()
Rooted<Typesystem> Project::createTypesystem(const std::string &name)
{
- Rooted<Typesystem> typesystem{
+ return Rooted<Typesystem>{
new Typesystem{getManager(), systemTypesystem, name}};
- addTypesystem(typesystem);
- return typesystem;
-}
-
-void Project::addTypesystem(Handle<Typesystem> typesystem)
-{
- invalidate();
- typesystems.push_back(typesystem);
}
Rooted<Document> Project::createDocument(const std::string &name)
@@ -69,39 +57,25 @@ Rooted<Document> Project::createDocument(const std::string &name)
return document;
}
-void Project::addDocument(Handle<Document> document)
-{
- invalidate();
- documents.push_back(document);
-}
-
Rooted<Domain> Project::createDomain(const std::string &name)
{
- Rooted<Domain> domain{new Domain(getManager(), systemTypesystem, name)};
- addDomain(domain);
- return domain;
+ return Rooted<Domain>{new Domain(getManager(), systemTypesystem, name)};
}
-void Project::addDomain(Handle<Domain> domain)
+void Project::addDocument(Handle<Document> document)
{
invalidate();
- domains.push_back(domain);
+ documents.push_back(document);
}
-const NodeVector<Document> &Project::getDocuments() { return documents; }
-
-const NodeVector<Domain> &Project::getDomains() { return domains; }
-
-const NodeVector<Typesystem> &Project::getTypesystems() { return typesystems; }
+const NodeVector<Document> &Project::getDocuments() const { return documents; }
}
namespace RttiTypes {
const Rtti Project = RttiBuilder<model::Project>("Project")
.parent(&Node)
.composedOf(&Document)
- .composedOf(&Typesystem)
- .composedOf(&SystemTypesystem)
- .composedOf(&Domain);
+ .composedOf(&SystemTypesystem);
}
}
diff --git a/src/core/model/Project.hpp b/src/core/model/Project.hpp
index 576bd60..4e2a43b 100644
--- a/src/core/model/Project.hpp
+++ b/src/core/model/Project.hpp
@@ -44,8 +44,9 @@ class Document;
class Domain;
/**
- * The Project class constitutes the top-level node in which documents, domains,
- * typesystems and other resources are embedded.
+ * The Project class constitutes the top-level node in which a collection of
+ * documents are stored. It also contains an instance of the SystemTypesystem
+ * and allows for simple creation of new Typesystem and Domain instances.
*/
class Project : public Node {
private:
@@ -60,16 +61,6 @@ private:
*/
NodeVector<Document> documents;
- /**
- * List containing all loaded domains.
- */
- NodeVector<Domain> domains;
-
- /**
- * List containing all loaded typesystems.
- */
- NodeVector<Typesystem> typesystems;
-
protected:
/**
* Validates the project and all parts it consists of.
@@ -103,13 +94,6 @@ public:
Rooted<Typesystem> createTypesystem(const std::string &name);
/**
- * Adds a single new typesystem to the project.
- *
- * @param typesystem is the typesystem that should be added to the project.
- */
- void addTypesystem(Handle<Typesystem> typesystem);
-
- /**
* Returns a new document with the given name and adds it to the list of
* documents.
*
@@ -118,13 +102,6 @@ public:
Rooted<Document> createDocument(const std::string &name);
/**
- * Adds the given document to the list of documents in the project.
- *
- * @param document is the document that should be added to the project.
- */
- void addDocument(Handle<Document> document);
-
- /**
* Returns a new domain with the given name and adds it to the list of
* domains. Provides a reference of the system typesystem to the domain.
*
@@ -133,32 +110,18 @@ public:
Rooted<Domain> createDomain(const std::string &name);
/**
- * Adds the given domain to the list of domains in the project.
+ * Adds the given document to the list of documents in the project.
*
- * @param domain is the document that should be added to the project.
+ * @param document is the document that should be added to the project.
*/
- void addDomain(Handle<Domain> domain);
+ void addDocument(Handle<Document> document);
/**
* Returns all documents of this project.
*
* @return a reference pointing at the document list.
*/
- const NodeVector<Document> &getDocuments();
-
- /**
- * Returns all domains of this project.
- *
- * @return a reference pointing at the domain list.
- */
- const NodeVector<Domain> &getDomains();
-
- /**
- * Returns all typesystems of this project.
- *
- * @return a reference pointing at the typesystem list.
- */
- const NodeVector<Typesystem> &getTypesystems();
+ const NodeVector<Document> &getDocuments() const;
};
}
diff --git a/src/core/parser/Parser.cpp b/src/core/parser/Parser.cpp
index b5d9656..2978669 100644
--- a/src/core/parser/Parser.cpp
+++ b/src/core/parser/Parser.cpp
@@ -16,16 +16,23 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <core/common/CharReader.hpp>
+
#include "Parser.hpp"
namespace ousia {
-namespace parser {
+
+/* Class Parser */
+
+Rooted<Node> Parser::parse(CharReader &reader, ParserContext &ctx)
+{
+ return doParse(reader, ctx);
+}
Rooted<Node> Parser::parse(const std::string &str, ParserContext &ctx)
{
CharReader reader{str};
- return parse(reader, ctx);
-}
+ return doParse(reader, ctx);
}
}
diff --git a/src/core/parser/Parser.hpp b/src/core/parser/Parser.hpp
index 049ee4e..e4419f5 100644
--- a/src/core/parser/Parser.hpp
+++ b/src/core/parser/Parser.hpp
@@ -32,94 +32,52 @@
#include <set>
#include <string>
-#include <core/Registry.hpp>
-#include <core/common/CharReader.hpp>
-#include <core/common/Exceptions.hpp>
-#include <core/common/Logger.hpp>
+#include <core/managed/Managed.hpp>
#include <core/model/Node.hpp>
-#include <core/model/Project.hpp>
-
-#include "Scope.hpp"
namespace ousia {
-namespace parser {
-// TODO: Implement a proper Mimetype class
+// Forward declarations
+class CharReader;
+class ParserContext;
/**
- * Struct containing the objects that are passed to a parser instance.
+ * Abstract parser class. This class builds the basic interface that should be
+ * used by any parser which reads data from an input stream and transforms it
+ * into an Ousía node graph.
*/
-struct ParserContext {
- /**
- * Reference to the Scope instance that should be used within the parser.
- */
- Scope &scope;
-
- /**
- * Reference to the Registry instance that should be used within the parser.
- */
- Registry &registry;
-
- /**
- * Reference to the Logger the parser should log any messages to.
- */
- Logger &logger;
-
+class Parser {
+protected:
/**
- * Reference to the Manager the parser should append nodes to.
+ * Parses the given input stream and returns a corresponding node for
+ * inclusion in the document graph. This method should be overridden by
+ * derived classes.
+ *
+ * @param reader is a reference to the CharReader that should be used.
+ * @param ctx is a reference to the context that should be used while
+ * parsing the document.
+ * @return a reference to the node representing the subgraph that has been
+ * created. The resulting node may point at not yet resolved entities, the
+ * calling code will try to resolve these. If no valid node can be produced,
+ * a corresponding LoggableException must be thrown by the parser.
*/
- Manager &manager;
+ virtual Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) = 0;
+public:
/**
- * Project instance into which the new content should be parsed.
+ * Default constructor.
*/
- Rooted<model::Project> project;
+ Parser() {}
/**
- * Constructor of the ParserContext class.
- *
- * @param scope is a reference to the Scope instance that should be used to
- * lookup names.
- * @param registry is a reference at the Registry class, which allows to
- * obtain references at parsers for other formats or script engine
- * implementations.
- * @param logger is a reference to the Logger instance that should be used
- * to log error messages and warnings that occur while parsing the document.
- * @param manager is a Reference to the Manager the parser should append
- * nodes to.
- * @param project is the project into which the content should be parsed.
+ * No copy construction.
*/
- ParserContext(Scope &scope, Registry &registry, Logger &logger,
- Manager &manager, Handle<model::Project> project)
- : scope(scope),
- registry(registry),
- logger(logger),
- manager(manager),
- project(project){};
-};
-
-/**
- * Abstract parser class. This class builds the basic interface that should be
- * used by any parser which reads data from an input stream and transforms it
- * into an Ousía node graph.
- */
-class Parser {
-public:
- Parser(){};
Parser(const Parser &) = delete;
/**
- * Returns a set containing all mime types supported by the parser. The mime
- * types are used to describe the type of the document that is read by the
- * parser. The default implementation returns an empty set. This method
- * should be overridden by derived classes.
- *
- * @return a set containing the string value of the supported mime types.
+ * Virtual destructor.
*/
- virtual std::set<std::string> mimetypes()
- {
- return std::set<std::string>{};
- };
+ virtual ~Parser(){};
/**
* Parses the given input stream and returns a corresponding node for
@@ -132,9 +90,9 @@ public:
* @return a reference to the node representing the subgraph that has been
* created. The resulting node may point at not yet resolved entities, the
* calling code will try to resolve these. If no valid node can be produced,
- * a corresponding LoggableException must be thrown by the parser.
+ * a corresponding ParserException must be thrown by the parser.
*/
- virtual Rooted<Node> parse(CharReader &reader, ParserContext &ctx) = 0;
+ Rooted<Node> parse(CharReader &reader, ParserContext &ctx);
/**
* Parses the given string and returns a corresponding node for
@@ -152,7 +110,6 @@ public:
Rooted<Node> parse(const std::string &str, ParserContext &ctx);
};
}
-}
#endif /* _OUSIA_PARSER_HPP_ */
diff --git a/src/core/parser/ParserContext.cpp b/src/core/parser/ParserContext.cpp
new file mode 100644
index 0000000..fa26c59
--- /dev/null
+++ b/src/core/parser/ParserContext.cpp
@@ -0,0 +1,36 @@
+/*
+ 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 "ParserContext.hpp"
+
+namespace ousia {
+
+/* Class ParserContext */
+
+ParserContext::ParserContext(ParserScope &scope, Registry &registry,
+ Logger &logger, Manager &manager,
+ Handle<model::Project> project)
+ : scope(scope),
+ registry(registry),
+ logger(logger),
+ manager(manager),
+ project(project)
+{
+}
+}
+
diff --git a/src/core/parser/ParserContext.hpp b/src/core/parser/ParserContext.hpp
new file mode 100644
index 0000000..bb64600
--- /dev/null
+++ b/src/core/parser/ParserContext.hpp
@@ -0,0 +1,92 @@
+/*
+ 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/>.
+*/
+
+/**
+ * @file ParserContext.hpp
+ *
+ * Contains the ParserContext, a struct containing references to all the
+ * important structures a Parser needs to access while parsing an input stream.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_PARSER_CONTEXT_HPP_
+#define _OUSIA_PARSER_CONTEXT_HPP_
+
+#include <core/managed/Managed.hpp>
+#include <core/model/Node.hpp>
+#include <core/model/Project.hpp>
+
+namespace ousia {
+
+// Forward declaration
+class Logger;
+class ParserScope;
+class Registry;
+
+/**
+ * Struct containing the objects that are passed to a parser instance.
+ */
+struct ParserContext {
+ /**
+ * Reference to the ParserScope instance that should be used within the parser.
+ */
+ ParserScope &scope;
+
+ /**
+ * Reference to the Registry instance that should be used within the parser.
+ */
+ Registry &registry;
+
+ /**
+ * Reference to the Logger the parser should log any messages to.
+ */
+ Logger &logger;
+
+ /**
+ * Reference to the Manager the parser should append nodes to.
+ */
+ Manager &manager;
+
+ /**
+ * Project instance into which the new content should be parsed.
+ */
+ Rooted<model::Project> project;
+
+ /**
+ * Constructor of the ParserContext class.
+ *
+ * @param scope is a reference to the ParserScope instance that should be
+ * used to lookup names.
+ * @param registry is a reference at the Registry class, which allows to
+ * obtain references at parsers for other formats or script engine
+ * implementations.
+ * @param logger is a reference to the Logger instance that should be used
+ * to log error messages and warnings that occur while parsing the document.
+ * @param manager is a Reference to the Manager the parser should append
+ * nodes to.
+ * @param project is the project into which the content should be parsed.
+ */
+ ParserContext(ParserScope &scope, Registry &registry, Logger &logger,
+ Manager &manager, Handle<model::Project> project);
+};
+
+}
+
+#endif /* _OUSIA_PARSER_CONTEXT_HPP_ */
+
diff --git a/src/core/parser/Scope.cpp b/src/core/parser/ParserScope.cpp
index 01292df..b236a1f 100644
--- a/src/core/parser/Scope.cpp
+++ b/src/core/parser/ParserScope.cpp
@@ -18,35 +18,14 @@
#include <core/common/Utils.hpp>
-#include "Scope.hpp"
+#include "ParserScope.hpp"
namespace ousia {
-namespace parser {
-/* Class GuardedScope */
+/* Class ParserScopeBase */
-GuardedScope::GuardedScope(Scope *scope, Handle<Node> node) : scope(scope)
-{
- scope->push(node);
-}
-
-GuardedScope::~GuardedScope()
-{
- if (scope) {
- scope->pop();
- }
-}
-
-GuardedScope::GuardedScope(GuardedScope &&s)
-{
- scope = s.scope;
- s.scope = nullptr;
-}
-
-/* Class ScopeBase */
-
-Rooted<Node> ScopeBase::resolve(const std::vector<std::string> &path,
- const Rtti &type, Logger &logger)
+Rooted<Node> ParserScopeBase::resolve(const std::vector<std::string> &path,
+ const Rtti &type, Logger &logger)
{
// Go up the stack and try to resolve the
for (auto it = nodes.rbegin(); it != nodes.rend(); it++) {
@@ -101,22 +80,17 @@ bool DeferredResolution::resolve(Logger &logger)
return false;
}
-/* Class Scope */
-
-void Scope::push(Handle<Node> node) { nodes.push_back(node); }
+/* Class ParserScope */
-void Scope::pop() { nodes.pop_back(); }
+void ParserScope::push(Handle<Node> node) { nodes.push_back(node); }
-GuardedScope Scope::descend(Handle<Node> node)
-{
- return GuardedScope{this, node};
-}
+void ParserScope::pop() { nodes.pop_back(); }
-Rooted<Node> Scope::getRoot() const { return nodes.front(); }
+Rooted<Node> ParserScope::getRoot() const { return nodes.front(); }
-Rooted<Node> Scope::getLeaf() { return nodes.back(); }
+Rooted<Node> ParserScope::getLeaf() { return nodes.back(); }
-bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
+bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type,
Logger &logger, ResolutionImposterCallback imposterCallback,
ResolutionResultCallback resultCallback,
const SourceLocation &location)
@@ -128,11 +102,11 @@ bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
return true;
}
-bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
+bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type,
Logger &logger, ResolutionResultCallback resultCallback,
const SourceLocation &location)
{
- Rooted<Node> res = ScopeBase::resolve(path, type, logger);
+ Rooted<Node> res = ParserScopeBase::resolve(path, type, logger);
if (res != nullptr) {
try {
resultCallback(res, logger);
@@ -146,7 +120,7 @@ bool Scope::resolve(const std::vector<std::string> &path, const Rtti &type,
return false;
}
-bool Scope::performDeferredResolution(Logger &logger)
+bool ParserScope::performDeferredResolution(Logger &logger)
{
// Repeat the resolution process as long as something has changed in the
// last iteration (resolving a node may cause other nodes to be resolvable).
@@ -176,8 +150,8 @@ bool Scope::performDeferredResolution(Logger &logger)
// Output error messages for all elements for which resolution did not
// succeed.
for (const auto &failed : deferred) {
- logger.error(std::string("Could not resolve ") + failed.type.name + std::string(" \"") +
- Utils::join(failed.path, ".") +
+ logger.error(std::string("Could not resolve ") + failed.type.name +
+ std::string(" \"") + Utils::join(failed.path, ".") +
std::string("\""),
failed.location);
}
@@ -185,4 +159,4 @@ bool Scope::performDeferredResolution(Logger &logger)
return false;
}
}
-}
+
diff --git a/src/core/parser/Scope.hpp b/src/core/parser/ParserScope.hpp
index b9b7f80..a759738 100644
--- a/src/core/parser/Scope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -16,8 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef _OUSIA_PARSER_SCOPE_H_
-#define _OUSIA_PARSER_SCOPE_H_
+#ifndef _OUSIA_PARSER_SCOPE_HPP_
+#define _OUSIA_PARSER_SCOPE_HPP_
#include <functional>
#include <list>
@@ -29,19 +29,21 @@
#include <core/model/Node.hpp>
/**
- * @file Scope.hpp
+ * @file ParserScope.hpp
*
- * Contains the Scope class used for resolving references based on the current
+ * Contains the ParserScope class used for resolving references based on the current
* parser state.
*
* @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
*/
namespace ousia {
-namespace parser {
// Forward declaration
-class Scope;
+class CharReader;
+class Registry;
+class Logger;
+class ParserScope;
/**
* Callback function type used for creating a dummy object while no correct
@@ -57,57 +59,9 @@ using ResolutionResultCallback =
std::function<void(Handle<Node>, Logger &logger)>;
/**
- * The GuardedScope class takes care of pushing a Node instance into the
- * name resolution stack of a Scope instance and poping this node once the
- * ScopedScope instance is deletes. This way you cannot forget to pop a Node
- * from a Scope instance as this operation is performed automatically.
- */
-class GuardedScope {
-private:
- /**
- * Reference at the backing scope instance.
- */
- Scope *scope;
-
-public:
- /**
- * Creates a new ScopedScope instance.
- *
- * @param scope is the backing Scope instance.
- * @param node is the Node instance that should be pushed onto the stack of
- * the Scope instance.
- */
- GuardedScope(Scope *scope, Handle<Node> node);
-
- /**
- * Pops the Node given in the constructor form the stack of the Scope
- * instance.
- */
- ~GuardedScope();
-
- /**
- * Move constructor of the ScopedScope class.
- */
- GuardedScope(GuardedScope &&);
-
- // No copy construction
- GuardedScope(const GuardedScope &) = delete;
-
- /**
- * Provides access at the underlying Scope instance.
- */
- Scope *operator->() { return scope; }
-
- /**
- * Provides access at the underlying Scope instance.
- */
- Scope &operator*() { return *scope; }
-};
-
-/**
* Base class for the
*/
-class ScopeBase {
+class ParserScopeBase {
protected:
/**
* List containing all nodes currently on the scope, with the newest nodes
@@ -117,18 +71,18 @@ protected:
public:
/**
- * Default constructor, creates an empty Scope instance.
+ * Default constructor, creates an empty ParserScope instance.
*/
- ScopeBase() {}
+ ParserScopeBase() {}
/**
- * Creates a new instance of the ScopeBase class, copying the the given
+ * Creates a new instance of the ParserScopeBase class, copying the the given
* nodes as initial start value of the node stack. This could for example
* be initialized with the path of a node.
*
* @param nodes is a node vector containing the current node stack.
*/
- ScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) {}
+ ParserScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) {}
/**
* Tries to resolve a node for the given type and path for all nodes that
@@ -157,7 +111,7 @@ private:
/**
* Copy of the scope at the time when the resolution was first triggered.
*/
- ScopeBase scope;
+ ParserScopeBase scope;
/**
* Callback function to be called when an element is successfully resolved.
@@ -185,7 +139,7 @@ public:
* arguments.
*
* @param nodes is a reference at the current internal node stack of the
- * Scope class.
+ * ParserScope class.
* @param path is the path that was queried when the resolution failed the
* first time.
* @param type is the Rtti of the element that should be queried.
@@ -213,12 +167,11 @@ public:
/**
* Provides an interface for document parsers to resolve references based on the
- * current position in the created document tree. The Scope class itself is
- * represented as a chain of Scope objects where each element has a reference to
- * a Node object attached to it. The descend method can be used to add a new
- * scope element to the chain.
+ * current position in the created document tree. The ParserScope class itself
+ * is represented as a chain of ParserScope objects where each element has a
+ * reference to a Node object attached to it.
*/
-class Scope : public ScopeBase {
+class ParserScope : public ParserScopeBase {
private:
/**
* List containing all deferred resolution descriptors.
@@ -227,10 +180,10 @@ private:
public:
/**
- * Default constructor of the Scope class, creates an empty Scope with no
+ * Default constructor of the ParserScope class, creates an empty ParserScope with no
* element on the internal stack.
*/
- Scope() {}
+ ParserScope() {}
/**
* Pushes a new node onto the scope.
@@ -245,20 +198,14 @@ public:
void pop();
/**
- * Returns a ScopedScope instance, which automatically pushes the given node
- * into the Scope stack and pops it once the ScopedScope is destroyed.
- */
- GuardedScope descend(Handle<Node> node);
-
- /**
- * Returns the top-most Node instance in the Scope hirarchy.
+ * Returns the top-most Node instance in the ParserScope hirarchy.
*
* @return a reference at the root node.
*/
Rooted<Node> getRoot() const;
/**
- * Returns the bottom-most Node instance in the Scope hirarchy, e.g. the
+ * Returns the bottom-most Node instance in the ParserScope hirarchy, e.g. the
* node that was pushed last onto the stack.
*
* @return a reference at the leaf node.
@@ -475,7 +422,6 @@ public:
bool performDeferredResolution(Logger &logger);
};
}
-}
-#endif /* _OUSIA_PARSER_SCOPE_H_ */
+#endif /* _OUSIA_PARSER_SCOPE_HPP_ */
diff --git a/src/core/parser/ParserStack.cpp b/src/core/parser/ParserStack.cpp
index 9cf782f..3792ee8 100644
--- a/src/core/parser/ParserStack.cpp
+++ b/src/core/parser/ParserStack.cpp
@@ -24,7 +24,6 @@
#include <core/common/Exceptions.hpp>
namespace ousia {
-namespace parser {
/* A default handler */
@@ -186,5 +185,4 @@ void ParserStack::data(const std::string &data, int field)
stack.top().handler->data(data, field);
}
}
-}
diff --git a/src/core/parser/ParserStack.hpp b/src/core/parser/ParserStack.hpp
index 492ab9c..6296dff 100644
--- a/src/core/parser/ParserStack.hpp
+++ b/src/core/parser/ParserStack.hpp
@@ -42,9 +42,9 @@
#include <core/common/Argument.hpp>
#include "Parser.hpp"
+#include "ParserContext.hpp"
namespace ousia {
-namespace parser {
/**
* The State type alias is used to
@@ -139,7 +139,7 @@ public:
const std::string &name() { return handlerData.name; }
- Scope &scope() { return handlerData.ctx.scope; }
+ ParserScope &scope() { return handlerData.ctx.scope; }
Registry &registry() { return handlerData.ctx.registry; }
@@ -322,6 +322,11 @@ private:
std::stack<HandlerInstance> stack;
/**
+ * Reference at some user defined data.
+ */
+ void *userData;
+
+ /**
* Used internally to get all expected command names for the given state
* (does not work if the current Handler instance allows arbitrary
* children). This function is used to build error messages.
@@ -340,8 +345,9 @@ public:
* corresponding HandlerDescriptor instances.
*/
ParserStack(ParserContext &ctx,
- const std::multimap<std::string, HandlerDescriptor> &handlers)
- : ctx(ctx), handlers(handlers){};
+ const std::multimap<std::string, HandlerDescriptor> &handlers,
+ void *userData = nullptr)
+ : ctx(ctx), handlers(handlers), userData(userData){};
/**
* Returns the state the ParserStack instance currently is in.
@@ -419,9 +425,15 @@ public:
* @return a reference to the parser context.
*/
ParserContext &getContext() { return ctx; }
+
+ /**
+ * Returns the user defined data.
+ *
+ * @return the userData pointer that was given in the constructor.
+ */
+ void *getUserData() { return userData; }
};
}
-}
#endif /* _OUSIA_PARSER_STACK_HPP_ */
diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp
new file mode 100644
index 0000000..f154c9c
--- /dev/null
+++ b/src/core/resource/ResourceManager.cpp
@@ -0,0 +1,275 @@
+/*
+ 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 <vector>
+
+#include <core/common/CharReader.hpp>
+#include <core/common/Exceptions.hpp>
+#include <core/common/Logger.hpp>
+#include <core/common/Rtti.hpp>
+#include <core/common/Utils.hpp>
+#include <core/model/Node.hpp>
+#include <core/parser/ParserContext.hpp>
+#include <core/parser/Parser.hpp>
+#include <core/Registry.hpp>
+
+#include "ResourceManager.hpp"
+#include "ResourceUtils.hpp"
+
+namespace ousia {
+
+/* Static helper functions */
+
+static void logUnsopportedType(Logger &logger, Resource &resource, const RttiSet &supportedTypes)
+{
+ // Build a list containing the expected type names
+ std::vector<std::string> expected;
+ for (const Rtti *supportedType : supportedTypes) {
+ expected.push_back(supportedType->name);
+ }
+
+ // Log the actual error message
+ logger.error(
+ std::string("Expected the file \"") + resource.getLocation() +
+ std::string("\" to define one of the following internal types ") +
+ Utils::join(expected, ", ", "{", "}"));
+}
+
+/* Class ResourceManager */
+
+SourceId ResourceManager::allocateSourceId(const Resource &resource)
+{
+ // Increment the source id and make sure the values don't overflow
+ SourceId sourceId = nextSourceId++;
+ if (sourceId == InvalidSourceId) {
+ nextSourceId = InvalidSourceId;
+ throw OusiaException{"Internal resource handles depleted!"};
+ }
+
+ // Register the node and the resource with this id
+ locations[resource.getLocation()] = sourceId;
+ resources[sourceId] = resource;
+
+ return sourceId;
+}
+
+void ResourceManager::storeNode(SourceId sourceId, Handle<Node> node)
+{
+ nodes[sourceId] = node->getUid();
+}
+
+void ResourceManager::purgeResource(SourceId sourceId)
+{
+ Resource res = getResource(sourceId);
+ if (res.isValid()) {
+ locations.erase(res.getLocation());
+ }
+ resources.erase(sourceId);
+ nodes.erase(sourceId);
+ lineNumberCache.erase(sourceId);
+}
+
+Rooted<Node> ResourceManager::parse(ParserContext &ctx, Resource &resource,
+ const std::string &mimetype,
+ const RttiSet &supportedTypes)
+{
+ // Try to deduce the mimetype of no mimetype was given
+ std::string mime = mimetype;
+ if (mime.empty()) {
+ mime = ctx.registry.getMimetypeForFilename(resource.getLocation());
+ if (mime.empty()) {
+ ctx.logger.error(std::string("Filename \"") + resource.getLocation() +
+ std::string(
+ "\" has an unknown file extension. Explicitly "
+ "specify a mimetype."));
+ return nullptr;
+ }
+ }
+
+ // Fetch a parser for the mimetype
+ const std::pair<Parser *, RttiSet> &parserDescr =
+ ctx.registry.getParserForMimetype(mime);
+ Parser *parser = parserDescr.first;
+
+ // Make sure a parser was found
+ if (!parser) {
+ ctx.logger.error(std::string("Cannot parse files of type \"") + mime +
+ std::string("\""));
+ return nullptr;
+ }
+
+ // Make sure the parser returns at least one of the supported types
+ if (!Rtti::setIsOneOf(parserDescr.second, supportedTypes)) {
+ logUnsopportedType(ctx.logger, resource, supportedTypes);
+ return nullptr;
+ }
+
+ // Allocate a new SourceId handle for this Resource
+ SourceId sourceId = allocateSourceId(resource);
+
+ // We can now try to parse the given file
+ Rooted<Node> node;
+ try {
+ // Set the current source id in the logger instance
+ ScopedLogger logger(ctx.logger, SourceLocation{sourceId});
+
+ // Fetch the input stream and create a char reader
+ std::unique_ptr<std::istream> is = resource.stream();
+ CharReader reader(*is, sourceId);
+
+ // Actually parse the input stream
+ node = parser->parse(reader, ctx);
+ if (node == nullptr) {
+ throw LoggableException{"Internal error: Parser returned null."};
+ }
+ } catch (LoggableException ex) {
+ // Remove all data associated with the allocated source id
+ purgeResource(sourceId);
+
+ // Log the exception and return nullptr
+ ctx.logger.log(ex);
+ return nullptr;
+ }
+
+ // Store the parsed node along with the sourceId
+ storeNode(sourceId, node);
+
+ // Return the parsed node
+ return node;
+}
+
+SourceId ResourceManager::getSourceId(const std::string &location)
+{
+ auto it = locations.find(location);
+ if (it != locations.end()) {
+ return it->second;
+ }
+ return InvalidSourceId;
+}
+
+SourceId ResourceManager::getSourceId(const Resource &resource)
+{
+ if (resource.isValid()) {
+ return getSourceId(resource.getLocation());
+ }
+ return InvalidSourceId;
+}
+
+const Resource &ResourceManager::getResource(SourceId sourceId) const
+{
+ auto it = resources.find(sourceId);
+ if (it != resources.end()) {
+ return it->second;
+ }
+ return NullResource;
+}
+
+Rooted<Node> ResourceManager::getNode(Manager &mgr, SourceId sourceId)
+{
+ auto it = nodes.find(sourceId);
+ if (it != nodes.end()) {
+ Managed *managed = mgr.getManaged(sourceId);
+ if (managed != nullptr) {
+ return dynamic_cast<Node *>(managed);
+ } else {
+ purgeResource(sourceId);
+ }
+ }
+ return nullptr;
+}
+
+Rooted<Node> ResourceManager::getNode(Manager &mgr, const std::string &location)
+{
+ return getNode(mgr, getSourceId(location));
+}
+
+Rooted<Node> ResourceManager::getNode(Manager &mgr, const Resource &resource)
+{
+ return getNode(mgr, getSourceId(resource));
+}
+
+Rooted<Node> ResourceManager::link(ParserContext &ctx, const std::string &path,
+ const std::string &mimetype,
+ const std::string &rel,
+ const RttiSet &supportedTypes,
+ const Resource &relativeTo)
+{
+ // Try to deduce the ResourceType
+ ResourceType resourceType =
+ ResourceUtils::deduceResourceType(rel, supportedTypes, ctx.logger);
+
+ // Lookup the resource for given path and resource type
+ Resource resource;
+ if (!ctx.registry.locateResource(resource, path, resourceType,
+ relativeTo)) {
+ ctx.logger.error("File \"" + path + "\" not found.");
+ return nullptr;
+ }
+
+ // Try to shrink the set of supportedTypes
+ RttiSet types = ResourceUtils::limitRttiTypes(supportedTypes, rel);
+
+ // Check whether the resource has already been parsed
+ Rooted<Node> node = getNode(ctx.manager, resource);
+ if (node == nullptr) {
+ // Node has not already been parsed, parse it now
+ node = parse(ctx, resource, mimetype, supportedTypes);
+
+ // Abort if parsing failed
+ if (node == nullptr) {
+ return nullptr;
+ }
+ }
+
+ // Make sure the node has one of the supported types
+ if (!node->type().isOneOf(supportedTypes)) {
+ logUnsopportedType(ctx.logger, resource, supportedTypes);
+ return nullptr;
+ }
+
+ return node;
+}
+
+Rooted<Node> ResourceManager::link(ParserContext &ctx, const std::string &path,
+ const std::string &mimetype,
+ const std::string &rel,
+ const RttiSet &supportedTypes,
+ SourceId relativeTo)
+{
+ // Fetch the resource corresponding to the source id, make sure it is valid
+ const Resource &relativeResource = getResource(relativeTo);
+ if (!relativeResource.isValid()) {
+ ctx.logger.fatalError("Internal error: Invalid SourceId supplied.");
+ return nullptr;
+ }
+
+ // Continue with the usual include routine
+ return link(ctx, path, mimetype, rel, supportedTypes, relativeResource);
+}
+
+SourceContext ResourceManager::buildContext(const SourceLocation &location)
+{
+ SourceContext res;
+
+ // TODO
+
+ return res;
+}
+
+}
+
diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp
new file mode 100644
index 0000000..51c00e3
--- /dev/null
+++ b/src/core/resource/ResourceManager.hpp
@@ -0,0 +1,236 @@
+/*
+ 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/>.
+*/
+
+/**
+ * @file ResourceManager.hpp
+ *
+ * Defines the ResourceManager class which is responsible for keeping track of
+ * already included resources and to retrieve CharReader instance for not-yet
+ * parsed resources.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_RESOURCE_MANAGER_HPP_
+#define _OUSIA_RESOURCE_MANAGER_HPP_
+
+#include <string>
+#include <unordered_map>
+
+#include <core/common/Location.hpp>
+#include <core/common/Rtti.hpp>
+#include <core/managed/Managed.hpp>
+
+#include "Resource.hpp"
+
+namespace ousia {
+
+// Forward declarations
+class Node;
+class ParserContext;
+extern const Resource NullResource;
+
+/**
+ * The ResourceManager class is responsible for keepking track of all included
+ * resources. It retrieves CharReader instances for not-yet parsed resources
+ * and returns references for those resources that already have been parsed.
+ */
+class ResourceManager {
+private:
+ /**
+ * Next SourceId to be used.
+ */
+ SourceId nextSourceId = 0;
+
+ /**
+ * Map between Resource locations and the corresponding SourceId.
+ */
+ std::unordered_map<std::string, SourceId> locations;
+
+ /**
+ * Map used for mapping SourceId instances to the underlying resource.
+ */
+ std::unordered_map<SourceId, Resource> resources;
+
+ /**
+ * Map between a SourceId and the corresponding (if available) parsed node
+ * uid (this resembles weak references to the Node instance).
+ */
+ std::unordered_map<SourceId, ManagedUid> nodes;
+
+ /**
+ * Cache used for translating byte offsets to line numbers. Maps from a
+ * SourceId onto a list of (sorted) SourceOffsets. The index in the list
+ * corresponds to the line number.
+ */
+ std::unordered_map<SourceId, std::vector<SourceOffset>> lineNumberCache;
+
+ /**
+ * Allocates a new SourceId for the given resource.
+ *
+ * @param resource is the Resource that should be associated with the newly
+ * allocated SourceId.
+ * @return a new SourceId describing the given resource.
+ */
+ SourceId allocateSourceId(const Resource &resource);
+
+ /**
+ * Registers the parsed node for this node id.
+ *
+ * @param sourceId is SourceId instance of the resource.
+ * @param node is the node that was parsed from that resource.
+ */
+ void storeNode(SourceId sourceId, Handle<Node> node);
+
+ /**
+ * Removes a resource from the internal stores.
+ *
+ * @param sourceId is the id of the file that should be removed.
+ */
+ void purgeResource(SourceId sourceId);
+
+ /**
+ * Used internally to parse the given resource.
+ *
+ * @param ctx is the context from the Registry and the Logger instance will
+ * be looked up.
+ * @param resource is the resource from which the input stream should be
+ * obtained.
+ * @param mimetype is the mimetype of the resource that should be parsed
+ * (may be empty, in which case the mimetype is deduced from the file
+ * extension)
+ * @param supportedTypes contains the types of the returned Node the caller
+ * can deal with. Note that only the types the parser claims to return are
+ * checked, not the actual result.
+ * @return the parsed node or nullptr if something goes wrong.
+ */
+ Rooted<Node> parse(ParserContext &ctx, Resource &resource,
+ const std::string &mimetype,
+ const RttiSet &supportedTypes);
+
+public:
+ /**
+ * Returns the sourceId for the given location string.
+ *
+ * @param location is the location string for which the resource id should
+ * be returned.
+ * @return the SourceId that can be used to identify the Resource, or
+ * InvalidSourceId if the specified location is not loaded.
+ */
+ SourceId getSourceId(const std::string &location);
+
+ /**
+ * Returns the sourceId for the given Resource.
+ *
+ * @param resource is the Resource for which the sourceId should be
+ * returned.
+ * @return the SourceId that can be used to identify the Resource, or
+ * InvalidSourceId if the specified resource is not loaded or invalid.
+ */
+ SourceId getSourceId(const Resource &resource);
+
+ /**
+ * Returns a Resource instance for the given SourceId.
+ *
+ * @param sourceId is the id of the Resource instance that should be
+ * returned.
+ * @return the Resource instance corresponding to the given sourceId. If the
+ * sourceId is invalid, the returned Resource will be invalid (a reference
+ * at NullResource).
+ */
+ const Resource &getResource(SourceId sourceId) const;
+
+ /**
+ * Returns the node that is associated with the given SourceId or nullptr if
+ * the Node no longer exists or the supplied SourceId is invalid.
+ *
+ * @param mgr is the Manager instance that should be used to resolve the
+ * internal weak reference to the Node instance.
+ * @param sourceId is the id of the resource for which the parsed Node
+ * instance should be returned.
+ * @return the Node instance corresponding to the given sourceId.
+ */
+ Rooted<Node> getNode(Manager &mgr, SourceId sourceId);
+
+ /**
+ * Returns the node that is associated with the given location or nullptr if
+ * the Node no longer exists or the supplied location was never parsed.
+ *
+ * @param mgr is the Manager instance that should be used to resolve the
+ * internal weak reference to the Node instance.
+ * @param location is the location from which the node was parsed.
+ * @return the Node instance corresponding to the given location.
+ */
+ Rooted<Node> getNode(Manager &mgr, const std::string &location);
+
+ /**
+ * Returns the node that is associated with the given resource or nullptr if
+ * the Node no longer exists or the supplied resource was never parsed.
+ *
+ * @param mgr is the Manager instance that should be used to resolve the
+ * internal weak reference to the Node instance.
+ * @param resource is the resource from which the node was parsed.
+ * @return the Node instance corresponding to the given resource.
+ */
+ Rooted<Node> getNode(Manager &mgr, const Resource &resource);
+
+ /**
+ * Resolves the reference to the file specified by the given path and -- if
+ * this has not already happened -- parses the file. Logs any problem in
+ * the logger instance of the given ParserContext.
+ *
+ * @param ctx is the context from the Registry and the Logger instance will
+ * be looked up.
+ * @param path is the path to the file that should be included.
+ * @param mimetype is the mimetype the file was included with. If no
+ * mimetype is given, the path must have an extension that is known by
+ */
+ Rooted<Node> link(ParserContext &ctx, const std::string &path,
+ const std::string &mimetype = "",
+ const std::string &rel = "",
+ const RttiSet &supportedTypes = RttiSet{},
+ const Resource &relativeTo = NullResource);
+
+ /**
+ * Resolves the reference to the file specified by the given path and -- if
+ * this has not already happened -- parses the file. Logs any problem in
+ * the logger instance of the given ParserContext.
+ */
+ Rooted<Node> link(ParserContext &ctx, const std::string &path,
+ const std::string &mimetype, const std::string &rel,
+ const RttiSet &supportedTypes, SourceId relativeTo);
+
+ /**
+ * Creates and returns a SourceContext structure containing information
+ * about the given SourceLocation (such as line and column number). Throws
+ * a LoggableException if an irrecoverable error occurs while looking up the
+ * context (such as a no longer existing resource).
+ *
+ * @param location is the SourceLocation for which context information
+ * should be retrieved. This method is used by the Logger class to print
+ * pretty messages.
+ * @return a valid SourceContext if a valid SourceLocation was given or an
+ * invalid SourceContext if the location is invalid.
+ */
+ SourceContext buildContext(const SourceLocation &location);
+
+};
+}
+
+#endif /* _OUSIA_RESOURCE_MANAGER_HPP_ */
+
diff --git a/src/core/resource/ResourceUtils.cpp b/src/core/resource/ResourceUtils.cpp
new file mode 100644
index 0000000..7c42716
--- /dev/null
+++ b/src/core/resource/ResourceUtils.cpp
@@ -0,0 +1,138 @@
+/*
+ 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 <core/common/Logger.hpp>
+#include <core/common/Rtti.hpp>
+#include <core/common/Utils.hpp>
+
+#include "ResourceUtils.hpp"
+
+namespace ousia {
+
+namespace RttiTypes {
+extern const Rtti Document;
+extern const Rtti Domain;
+extern const Rtti Node;
+extern const Rtti Typesystem;
+}
+
+/**
+ * Map mapping from relations (the "rel" attribute in includes) to the
+ * corresponding ResourceType.
+ */
+static const std::unordered_map<std::string, ResourceType> RelResourceTypeMap{
+ {"document", ResourceType::DOCUMENT},
+ {"domain", ResourceType::DOMAIN_DESC},
+ {"typesystem", ResourceType::TYPESYSTEM}};
+
+/**
+ * Map mapping from relations to the corresponding Rtti descriptor.
+ */
+static const std::unordered_map<std::string, const Rtti *> RelRttiTypeMap{
+ {"document", &RttiTypes::Document},
+ {"domain", &RttiTypes::Domain},
+ {"typesystem", &RttiTypes::Typesystem}};
+
+/**
+ * Map mapping from Rtti pointers to the corresponding ResourceType.
+ */
+static const std::unordered_map<const Rtti *, ResourceType> RttiResourceTypeMap{
+ {&RttiTypes::Document, ResourceType::DOCUMENT},
+ {&RttiTypes::Domain, ResourceType::DOMAIN_DESC},
+ {&RttiTypes::Typesystem, ResourceType::TYPESYSTEM}};
+
+ResourceType ResourceUtils::deduceResourceType(const std::string &rel,
+ const RttiSet &supportedTypes,
+ Logger &logger)
+{
+ ResourceType res;
+
+ // Try to deduce the ResourceType from the "rel" attribute
+ res = deduceResourceType(rel, logger);
+
+ // If this did not work, try to deduce the ResourceType from the
+ // supportedTypes supplied by the Parser instance.
+ if (res == ResourceType::UNKNOWN) {
+ res = deduceResourceType(supportedTypes, logger);
+ }
+ if (res == ResourceType::UNKNOWN) {
+ logger.note(
+ "Ambigous resource type, consider specifying the \"rel\" "
+ "attribute");
+ }
+ return res;
+}
+
+ResourceType ResourceUtils::deduceResourceType(const std::string &rel,
+ Logger &logger)
+{
+ std::string s = Utils::toLower(rel);
+ if (!s.empty()) {
+ auto it = RelResourceTypeMap.find(s);
+ if (it != RelResourceTypeMap.end()) {
+ return it->second;
+ } else {
+ logger.error(std::string("Unknown relation \"") + rel +
+ std::string("\""));
+ }
+ }
+ return ResourceType::UNKNOWN;
+}
+
+ResourceType ResourceUtils::deduceResourceType(const RttiSet &supportedTypes,
+ Logger &logger)
+{
+ if (supportedTypes.size() == 1U) {
+ auto it = RttiResourceTypeMap.find(*supportedTypes.begin());
+ if (it != RttiResourceTypeMap.end()) {
+ return it->second;
+ }
+ }
+ return ResourceType::UNKNOWN;
+}
+
+const Rtti *ResourceUtils::deduceRttiType(const std::string &rel)
+{
+ std::string s = Utils::toLower(rel);
+ if (!s.empty()) {
+ auto it = RelRttiTypeMap.find(s);
+ if (it != RelRttiTypeMap.end()) {
+ return it->second;
+ }
+ }
+ return &RttiTypes::Node;
+}
+
+RttiSet ResourceUtils::limitRttiTypes(const RttiSet &supportedTypes,
+ const std::string &rel)
+{
+ return limitRttiTypes(supportedTypes, deduceRttiType(rel));
+}
+
+RttiSet ResourceUtils::limitRttiTypes(const RttiSet &supportedTypes,
+ const Rtti *type)
+{
+ RttiSet res;
+ for (const Rtti *supportedType : supportedTypes) {
+ if (supportedType->isa(*type)) {
+ res.insert(supportedType);
+ }
+ }
+ return res;
+}
+}
diff --git a/src/core/resource/ResourceUtils.hpp b/src/core/resource/ResourceUtils.hpp
new file mode 100644
index 0000000..13f9251
--- /dev/null
+++ b/src/core/resource/ResourceUtils.hpp
@@ -0,0 +1,128 @@
+/*
+ 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/>.
+*/
+
+/**
+ * @file ResourceUtils.hpp
+ *
+ * Contains the ResourceUtils class which defines a set of static utility
+ * functions for dealing with Resources and ResourceTypes.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_RESOURCE_UTILS_HPP_
+#define _OUSIA_RESOURCE_UTILS_HPP_
+
+#include <string>
+
+#include <core/common/Rtti.hpp>
+
+#include "Resource.hpp"
+
+namespace ousia {
+
+/**
+ * Class containing static utility functions for dealing with Resources and
+ * ResourceTypes.
+ */
+class ResourceUtils {
+public:
+ /**
+ * Function used to deduce the resource type from a given "relation" string
+ * and a set of RTTI types into which the resource should be converted by a
+ * parser.
+ *
+ * @param rel is a relation string which specifies the type of the resource.
+ * May be empty.
+ * @param supportedTypes is a set of RTTI types into which the resource
+ * should be converted by a parser. Set may be empty.
+ * @param logger is the Logger instance to which errors should be logged.
+ * @return a ResourceType specifier.
+ */
+ static ResourceType deduceResourceType(const std::string &rel,
+ const RttiSet &supportedTypes,
+ Logger &logger);
+
+ /**
+ * Function used to deduce the resource type from a given "relation" string.
+ *
+ * @param rel is a relation string which specifies the type of the resource.
+ * May be empty.
+ * @param logger is the Logger instance to which errors should be logged
+ * (e.g. if the relation string is invalid).
+ * @return a ResourceType specifier.
+ */
+ static ResourceType deduceResourceType(const std::string &rel,
+ Logger &logger);
+
+ /**
+ * Function used to deduce the resource type from a set of RTTI types into
+ * which the resource should be converted by a parser.
+ *
+ * @param supportedTypes is a set of RTTI types into which the resource
+ * should be converted by a parser. Set may be empty.
+ * @param logger is the Logger instance to which errors should be logged.
+ * @return a ResourceType specifier.
+ */
+ static ResourceType deduceResourceType(const RttiSet &supportedTypes,
+ Logger &logger);
+
+ /**
+ * Transforms the given relation string to the corresponding RttiType.
+ *
+ * @param rel is a relation string which specifies the type of the resource.
+ * May be empty.
+ * @return a pointer at the corresponding Rtti instance or a pointer at the
+ * Rtti descriptor of the Node class (the most general Node type) if the
+ * given relation type is unknown.
+ */
+ static const Rtti *deduceRttiType(const std::string &rel);
+
+ /**
+ * Reduces the number of types supported by a parser as the type of a
+ * resource to the intersection of the given supported types and the RTTI
+ * type associated with the given relation string.
+ *
+ * @param supportedTypes is a set of RTTI types into which the resource
+ * should be converted by a parser. Set may be empty.
+ * @param rel is a relation string which specifies the type of the resource.
+ * @return the supported type set limited to those types that can actually
+ * be returned according to the given relation string.
+ */
+ static RttiSet limitRttiTypes(const RttiSet &supportedTypes,
+ const std::string &rel);
+
+ /**
+ * Reduces the number of types supported by a parser as the type of a
+ * resource to the intersection of the given supported types and the RTTI
+ * type associated with the given relation string.
+ *
+ * @param supportedTypes is a set of RTTI types into which the resource
+ * should be converted by a parser. Set may be empty.
+ * @param type is the type that is to be expected from the parser.
+ * @return the supported type set limited to those types that can actually
+ * be returned according to the given relation string (form an isa
+ * relationship with the given type).
+ */
+ static RttiSet limitRttiTypes(const RttiSet &supportedTypes,
+ const Rtti *type);
+};
+}
+
+#endif /* _OUSIA_RESOURCE_UTILS_HPP_ */
+
diff --git a/src/plugins/css/CSSParser.cpp b/src/plugins/css/CSSParser.cpp
index 40486cc..8cb41ea 100644
--- a/src/plugins/css/CSSParser.cpp
+++ b/src/plugins/css/CSSParser.cpp
@@ -19,10 +19,9 @@
#include "CSSParser.hpp"
#include <core/common/VariantReader.hpp>
+#include <core/parser/ParserContext.hpp>
namespace ousia {
-namespace parser {
-namespace css {
// CSS code tokens
static const int CURLY_OPEN = 1;
@@ -75,7 +74,7 @@ static const std::map<int, CodeTokenDescriptor> CSS_DESCRIPTORS = {
{ESCAPE, {CodeTokenMode::ESCAPE, ESCAPE}},
{LINEBREAK, {CodeTokenMode::LINEBREAK, LINEBREAK}}};
-Rooted<Node> CSSParser::parse(CharReader &reader, ParserContext &ctx)
+Rooted<Node> CSSParser::doParse(CharReader &reader, ParserContext &ctx)
{
CodeTokenizer tokenizer{reader, CSS_ROOT, CSS_DESCRIPTORS};
tokenizer.ignoreComments = true;
@@ -362,5 +361,4 @@ bool CSSParser::expect(int expectedType, CodeTokenizer &tokenizer, Token &t,
return true;
}
}
-}
-}
+
diff --git a/src/plugins/css/CSSParser.hpp b/src/plugins/css/CSSParser.hpp
index 1ec54f5..c6594f6 100644
--- a/src/plugins/css/CSSParser.hpp
+++ b/src/plugins/css/CSSParser.hpp
@@ -24,6 +24,7 @@
*
* @author Benjamin Paassen - bpaassen@techfak.uni-bielefeld.de
*/
+
#ifndef _OUSIA_CSS_PARSER_HPP_
#define _OUSIA_CSS_PARSER_HPP_
@@ -36,8 +37,6 @@
#include <core/parser/Parser.hpp>
namespace ousia {
-namespace parser {
-namespace css {
/**
* This is a context free, recursive parser for a subset of the CSS3 language
@@ -139,7 +138,7 @@ private:
bool expect(int expectedType, CodeTokenizer &tokenizer, Token &t,
bool force, ParserContext &ctx);
-public:
+protected:
/**
* This parses the given input as CSS content as specified by the grammar
* seen above. The return value is a Rooted reference to the root of the
@@ -157,21 +156,8 @@ public:
* @return returns the root node of the resulting SelectorTree. For more
* information on the return conventions consult the Parser.hpp.
*/
- Rooted<Node> parse(CharReader &reader, ParserContext &ctx) override;
-
- using Parser::parse;
-
- /**
- * As befits a class called CSSParser, this Parser parses CSS.
- */
- std::set<std::string> mimetypes()
- {
- std::set<std::string> out{"text/css"};
- return out;
- }
+ Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) override;
};
}
-}
-}
#endif
diff --git a/src/plugins/filesystem/FileLocator.cpp b/src/plugins/filesystem/FileLocator.cpp
index 467363b..356394e 100644
--- a/src/plugins/filesystem/FileLocator.cpp
+++ b/src/plugins/filesystem/FileLocator.cpp
@@ -131,7 +131,7 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path,
base /= path;
// If we already found a fitting resource there, use that.
- if (fs::exists(base)) {
+ if (fs::exists(base) && fs::is_regular_file(base)) {
std::string location = fs::canonical(base).generic_string();
#ifdef FILELOCATOR_DEBUG_PRINT
std::cout << "FileLocator: Found \"" << path << "\" at "
@@ -143,6 +143,11 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path,
}
}
+ // If the path starts with "./" or "../" only perform relative lookups!
+ if (path.substr(0, 2) == "./" || path.substr(0, 3) == "../") {
+ return false;
+ }
+
// Otherwise look in the search paths, search backwards, last defined search
// paths have a higher precedence
auto it = searchPaths.find(type);
@@ -154,7 +159,7 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path,
#endif
fs::path p{*it};
p /= path;
- if (fs::exists(p)) {
+ if (fs::exists(p) && fs::is_regular_file(p)) {
std::string location = fs::canonical(p).generic_string();
#ifdef FILELOCATOR_DEBUG_PRINT
std::cout << "FileLocator: Found \"" << path << "\" in "
diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp
index 434a72c..78d9df8 100644
--- a/src/plugins/xml/XmlParser.cpp
+++ b/src/plugins/xml/XmlParser.cpp
@@ -25,13 +25,12 @@
#include <core/common/Utils.hpp>
#include <core/common/VariantReader.hpp>
#include <core/parser/ParserStack.hpp>
+#include <core/parser/ParserScope.hpp>
#include <core/model/Typesystem.hpp>
#include "XmlParser.hpp"
namespace ousia {
-namespace parser {
-namespace xml {
using namespace ousia::model;
@@ -132,11 +131,12 @@ public:
!(defaultValue.isObject() && defaultValue.asObject() == nullptr);
Rooted<StructType> structType = scope().getLeaf().cast<StructType>();
- Rooted<Attribute> attribute = structType->createAttribute(
- name, defaultValue, optional, logger());
+ Rooted<Attribute> attribute =
+ structType->createAttribute(name, defaultValue, optional, logger());
// Try to resolve the type
- scope().resolve<Type>(type, logger(),
+ scope().resolve<Type>(
+ type, logger(),
[attribute](Handle<Type> type, Logger &logger) mutable {
attribute->setType(type, logger);
},
@@ -235,16 +235,23 @@ public:
/* Adapter Expat -> ParserStack */
+struct XMLParserUserData {
+ SourceId sourceId;
+};
+
static SourceLocation syncLoggerPosition(XML_Parser p)
{
+ // Fetch the parser stack and the associated user data
+ ParserStack *stack = static_cast<ParserStack *>(XML_GetUserData(p));
+ XMLParserUserData *ud =
+ static_cast<XMLParserUserData *>(stack->getUserData());
+
// Fetch the current location in the XML file
- int line = XML_GetCurrentLineNumber(p);
- int column = XML_GetCurrentColumnNumber(p);
size_t offs = XML_GetCurrentByteIndex(p);
- SourceLocation loc{line, column, offs};
- // Update the default location of the current logger instance
- ParserStack *stack = static_cast<ParserStack *>(XML_GetUserData(p));
+ // Build the source location and update the default location of the current
+ // logger instance
+ SourceLocation loc{ud->sourceId, offs};
stack->getContext().logger.setDefaultLocation(loc);
return loc;
}
@@ -271,9 +278,9 @@ static void xmlStartElementHandler(void *p, const XML_Char *name,
static void xmlEndElementHandler(void *p, const XML_Char *name)
{
XML_Parser parser = static_cast<XML_Parser>(p);
- syncLoggerPosition(parser);
-
ParserStack *stack = static_cast<ParserStack *>(XML_GetUserData(parser));
+
+ syncLoggerPosition(parser);
stack->end();
}
@@ -291,19 +298,17 @@ static void xmlCharacterDataHandler(void *p, const XML_Char *s, int len)
/* Class XmlParser */
-std::set<std::string> XmlParser::mimetypes()
-{
- return std::set<std::string>{{"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"}};
-}
-
-Rooted<Node> XmlParser::parse(CharReader &reader, ParserContext &ctx)
+Rooted<Node> XmlParser::doParse(CharReader &reader, ParserContext &ctx)
{
// Create the parser object
ScopedExpatXmlParser p{"UTF-8"};
// Create the parser stack instance and pass the reference to the state
// machine descriptor
- ParserStack stack{ctx, XML_HANDLERS};
+ XMLParserUserData data;
+ data.sourceId = reader.getSourceId();
+
+ ParserStack stack{ctx, XML_HANDLERS, &data};
XML_SetUserData(&p, &stack);
XML_UseParserAsHandlerArg(&p);
@@ -327,15 +332,14 @@ Rooted<Node> XmlParser::parse(CharReader &reader, ParserContext &ctx)
// Parse the data and handle any XML error
if (!XML_ParseBuffer(&p, bytesRead, bytesRead == 0)) {
- // Fetch the current line number and column
- int line = XML_GetCurrentLineNumber(&p);
- int column = XML_GetCurrentColumnNumber(&p);
+ // Fetch the xml parser byte offset
size_t offs = XML_GetCurrentByteIndex(&p);
// Throw a corresponding exception
XML_Error code = XML_GetErrorCode(&p);
std::string msg = std::string{XML_ErrorString(code)};
- throw LoggableException{"XML: " + msg, line, column, offs};
+ throw LoggableException{"XML: " + msg,
+ SourceLocation{reader.getSourceId(), offs}};
}
// Abort once there are no more bytes in the stream
@@ -346,6 +350,4 @@ Rooted<Node> XmlParser::parse(CharReader &reader, ParserContext &ctx)
return nullptr;
}
}
-}
-}
diff --git a/src/plugins/xml/XmlParser.hpp b/src/plugins/xml/XmlParser.hpp
index 62f0128..3c0ffb7 100644
--- a/src/plugins/xml/XmlParser.hpp
+++ b/src/plugins/xml/XmlParser.hpp
@@ -31,23 +31,13 @@
#include <core/parser/Parser.hpp>
namespace ousia {
-namespace parser {
-namespace xml {
/**
* The XmlParser class implements parsing the various types of Ousía XML
* documents using the expat stream XML parser.
*/
class XmlParser : public Parser {
-public:
- /**
- * Returns the mimetype supported by the XmlParser which is
- * "text/vnd.ousia.oxm" and "text/vnd.ousia.oxd".
- *
- * @return a list containing the mimetype supported by Ousía.
- */
- std::set<std::string> mimetypes() override;
-
+protected:
/**
* Parses the given input stream as XML file and returns the parsed
* top-level node.
@@ -56,14 +46,10 @@ public:
* @param ctx is a reference to the ParserContext instance that should be
* used.
*/
- Rooted<Node> parse(CharReader &reader, ParserContext &ctx) override;
-
- using Parser::parse;
+ Rooted<Node> doParse(CharReader &reader, ParserContext &ctx) override;
};
}
-}
-}
#endif /* _OUSIA_XML_PARSER_HPP_ */