From 6decad0b8e7e369bd8215f31a45dd3eae982d2a9 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Wed, 21 Jan 2015 01:17:49 +0100 Subject: Some further refactoring -- renamed Scope to ParserScope, got rid of parser namespace, added new functionality to RegistryClass, wrote documentation, added function for extracting file extensions to Utils --- src/core/Registry.cpp | 69 ++++-- src/core/Registry.hpp | 115 +++++++-- src/core/common/Utils.cpp | 23 ++ src/core/common/Utils.hpp | 20 ++ src/core/parser/Parser.cpp | 13 +- src/core/parser/Parser.hpp | 101 +++----- src/core/parser/ParserContext.cpp | 36 +++ src/core/parser/ParserContext.hpp | 92 ++++++++ src/core/parser/ParserScope.cpp | 162 +++++++++++++ src/core/parser/ParserScope.hpp | 427 +++++++++++++++++++++++++++++++++ src/core/parser/ParserStack.cpp | 2 - src/core/parser/ParserStack.hpp | 5 +- src/core/parser/Scope.cpp | 188 --------------- src/core/parser/Scope.hpp | 481 -------------------------------------- src/plugins/css/CSSParser.cpp | 8 +- src/plugins/css/CSSParser.hpp | 20 +- src/plugins/xml/XmlParser.cpp | 12 +- src/plugins/xml/XmlParser.hpp | 18 +- 18 files changed, 968 insertions(+), 824 deletions(-) create mode 100644 src/core/parser/ParserContext.cpp create mode 100644 src/core/parser/ParserContext.hpp create mode 100644 src/core/parser/ParserScope.cpp create mode 100644 src/core/parser/ParserScope.hpp delete mode 100644 src/core/parser/Scope.cpp delete mode 100644 src/core/parser/Scope.hpp (limited to 'src') diff --git a/src/core/Registry.cpp b/src/core/Registry.cpp index 86665a2..c42a97a 100644 --- a/src/core/Registry.cpp +++ b/src/core/Registry.cpp @@ -16,6 +16,8 @@ along with this program. If not, see . */ +#include +#include #include #include #include @@ -24,32 +26,71 @@ namespace ousia { -using namespace parser; - /* Class Registry */ -void Registry::registerParser(parser::Parser &parser) +void Registry::registerParser(const std::set &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, types}; + } +} + +static const std::pair NullParser{nullptr, RttiSet{}}; + +const std::pair &Registry::getParserForMimetype( + const std::string &mimetype) const { - 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)); + 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) +{ + // 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; } -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{}; } -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..965f336 100644 --- a/src/core/Registry.hpp +++ b/src/core/Registry.hpp @@ -16,37 +16,126 @@ along with this program. If not, see . */ +/** + * @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 +#include #include +#include #include 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 parsers; - std::map parserMimetypes; - + /** + * Mapping between parser mimetypes and pairs of parsers and their supported + * Rtti types. + */ + std::map> parsers; + + /** + * Map from file extensions to registered mimetypes. + */ + std::map extensions; + + /** + * List containing all registered ResourceLocator instances. + */ std::vector 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 &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 &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); + + /** + * 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; + + /** + * 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/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 +#include #include +#include #include "Utils.hpp" @@ -74,5 +76,26 @@ std::vector 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 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/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 . */ +#include + #include "Parser.hpp" namespace ousia { -namespace parser { + +/* Class Parser */ + +Rooted Parser::parse(CharReader &reader, ParserContext &ctx) +{ + return doParse(reader, ctx); +} Rooted 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 #include -#include -#include -#include -#include +#include #include -#include - -#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 ®istry; - - /** - * 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 doParse(CharReader &reader, ParserContext &ctx) = 0; +public: /** - * Project instance into which the new content should be parsed. + * Default constructor. */ - Rooted 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 ®istry, Logger &logger, - Manager &manager, Handle 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 mimetypes() - { - return std::set{}; - }; + 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 parse(CharReader &reader, ParserContext &ctx) = 0; + Rooted parse(CharReader &reader, ParserContext &ctx); /** * Parses the given string and returns a corresponding node for @@ -152,7 +110,6 @@ public: Rooted 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 . +*/ + +#include "ParserContext.hpp" + +namespace ousia { + +/* Class ParserContext */ + +ParserContext::ParserContext(ParserScope &scope, Registry ®istry, + Logger &logger, Manager &manager, + Handle 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..88d1f52 --- /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 . +*/ + +/** + * @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 +#include +#include + +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 ®istry; + + /** + * 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 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 ®istry, Logger &logger, + Manager &manager, Handle project); +}; + +} + +#endif /* _OUSIA_PARSER_CONTEXT_HPP_ */ + diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp new file mode 100644 index 0000000..b236a1f --- /dev/null +++ b/src/core/parser/ParserScope.cpp @@ -0,0 +1,162 @@ +/* + 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 . +*/ + +#include + +#include "ParserScope.hpp" + +namespace ousia { + +/* Class ParserScopeBase */ + +Rooted ParserScopeBase::resolve(const std::vector &path, + const Rtti &type, Logger &logger) +{ + // Go up the stack and try to resolve the + for (auto it = nodes.rbegin(); it != nodes.rend(); it++) { + std::vector res = (*it)->resolve(path, type); + + // Abort if the object could not be resolved + if (res.empty()) { + continue; + } + + // Log an error if the object is not unique + if (res.size() > 1) { + logger.error(std::string("The reference \"") + + Utils::join(path, ".") + ("\" is ambigous!")); + logger.note("Referenced objects are:"); + for (const ResolutionResult &r : res) { + logger.note(Utils::join(r.path(), ".")); + } + } + return res[0].node; + } + return nullptr; +} + +/* Class DeferredResolution */ + +DeferredResolution::DeferredResolution(const NodeVector &nodes, + const std::vector &path, + const Rtti &type, + ResolutionResultCallback resultCallback, + const SourceLocation &location) + : scope(nodes), + resultCallback(resultCallback), + path(path), + type(type), + location(location) +{ +} + +bool DeferredResolution::resolve(Logger &logger) +{ + Rooted res = scope.resolve(path, type, logger); + if (res != nullptr) { + try { + resultCallback(res, logger); + } + catch (LoggableException ex) { + logger.log(ex); + } + return true; + } + return false; +} + +/* Class ParserScope */ + +void ParserScope::push(Handle node) { nodes.push_back(node); } + +void ParserScope::pop() { nodes.pop_back(); } + +Rooted ParserScope::getRoot() const { return nodes.front(); } + +Rooted ParserScope::getLeaf() { return nodes.back(); } + +bool ParserScope::resolve(const std::vector &path, const Rtti &type, + Logger &logger, ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback, + const SourceLocation &location) +{ + if (!resolve(path, type, logger, resultCallback, location)) { + resultCallback(imposterCallback(), logger); + return false; + } + return true; +} + +bool ParserScope::resolve(const std::vector &path, const Rtti &type, + Logger &logger, ResolutionResultCallback resultCallback, + const SourceLocation &location) +{ + Rooted res = ParserScopeBase::resolve(path, type, logger); + if (res != nullptr) { + try { + resultCallback(res, logger); + } + catch (LoggableException ex) { + logger.log(ex, location); + } + return true; + } + deferred.emplace_back(nodes, path, type, resultCallback, location); + return false; +} + +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). + while (true) { + // Iterate over all deferred resolution processes, + bool hasChange = false; + for (auto it = deferred.begin(); it != deferred.end();) { + if (it->resolve(logger)) { + it = deferred.erase(it); + hasChange = true; + } else { + it++; + } + } + + // Abort if nothing has changed in the last iteration + if (!hasChange) { + break; + } + } + + // We were successful if there are no more deferred resolutions + if (deferred.empty()) { + return true; + } + + // 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, ".") + + std::string("\""), + failed.location); + } + deferred.clear(); + return false; +} +} + diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp new file mode 100644 index 0000000..a759738 --- /dev/null +++ b/src/core/parser/ParserScope.hpp @@ -0,0 +1,427 @@ +/* + 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 . +*/ + +#ifndef _OUSIA_PARSER_SCOPE_HPP_ +#define _OUSIA_PARSER_SCOPE_HPP_ + +#include +#include +#include + +#include +#include +#include +#include + +/** + * @file ParserScope.hpp + * + * 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 { + +// Forward declaration +class CharReader; +class Registry; +class Logger; +class ParserScope; + +/** + * Callback function type used for creating a dummy object while no correct + * object is available for resolution. + */ +using ResolutionImposterCallback = std::function()>; + +/** + * Callback function type called whenever the result of a resolution is + * available. + */ +using ResolutionResultCallback = + std::function, Logger &logger)>; + +/** + * Base class for the + */ +class ParserScopeBase { +protected: + /** + * List containing all nodes currently on the scope, with the newest nodes + * being pushed to the back of the list. + */ + NodeVector nodes; + +public: + /** + * Default constructor, creates an empty ParserScope instance. + */ + ParserScopeBase() {} + + /** + * 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. + */ + ParserScopeBase(const NodeVector &nodes) : nodes(nodes) {} + + /** + * Tries to resolve a node for the given type and path for all nodes that + * are currently in the stack, starting with the topmost node on the stack. + * + * @param path is the path for which a node should be resolved. + * @param type is the type of the node that should be resolved. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @return a reference at a resolved node or nullptr if no node could be + * found. + */ + Rooted resolve(const std::vector &path, + const Rtti &type, Logger &logger); +}; + +/** + * Class used for representing a deferred resolution. A deferred resolution is + * triggered whenever an object cannot be resolved, but there may be a chance + * that it can be resolved in the future. This happens e.g. if a document is + * just being parsed and the object that is being refered to has not been + * reached yet. + */ +class DeferredResolution { +private: + /** + * Copy of the scope at the time when the resolution was first triggered. + */ + ParserScopeBase scope; + + /** + * Callback function to be called when an element is successfully resolved. + */ + ResolutionResultCallback resultCallback; + +public: + /** + * Path queried for the resolution. + */ + std::vector path; + + /** + * Reference at the type of the object that should be resolved. + */ + const Rtti &type; + + /** + * Position at which the resolution was triggered. + */ + const SourceLocation location; + + /** + * Constructor of the DeferredResolutionScope class. Copies the given + * arguments. + * + * @param nodes is a reference at the current internal node stack of the + * 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. + * @param resultCallback is the callback function that should be called if + * the desired element has indeed been found. + * @param location is the location at which the resolution was triggered. + */ + DeferredResolution(const NodeVector &nodes, + const std::vector &path, + const Rtti &type, + ResolutionResultCallback resultCallback, + const SourceLocation &location = SourceLocation{}); + + /** + * Performs the actual deferred resolution and calls the resultCallback + * callback function in case the resolution is sucessful. In this case + * returns true, false otherwise. + * + * @param logger is the logger instance to which error messages should be + * logged. + * @return true if the resolution was successful, false otherwise. + */ + bool resolve(Logger &logger); +}; + +/** + * Provides an interface for document parsers to resolve references based on the + * 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 ParserScope : public ParserScopeBase { +private: + /** + * List containing all deferred resolution descriptors. + */ + std::list deferred; + +public: + /** + * Default constructor of the ParserScope class, creates an empty ParserScope with no + * element on the internal stack. + */ + ParserScope() {} + + /** + * Pushes a new node onto the scope. + * + * @param node is the node that should be used for local lookup. + */ + void push(Handle node); + + /** + * Removes the last pushed node from the scope. + */ + void pop(); + + /** + * Returns the top-most Node instance in the ParserScope hirarchy. + * + * @return a reference at the root node. + */ + Rooted getRoot() const; + + /** + * 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. + */ + Rooted getLeaf(); + + /** + * Tries to resolve a node for the given type and path for all nodes + * currently on the stack, starting with the topmost node on the stack. + * Calls the "imposterCallback" function for obtaining a temporary result if + * a node cannot be resolved right now. The "resultCallback" is at most + * called twice: Once when this method is called (probably with the + * temporary) and another time if the resolution turned out to be successful + * at a later point in time. + * + * @param path is the path for which a node should be resolved. + * @param type is the type of the node that should be resolved. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @param imposterCallback is the callback function that is called if + * the node cannot be resolved at this moment. It gives the caller the + * possibility to create an imposter (a temporary object) that may be used + * later in the resolution process. + * @param resultCallback is the callback function to which the result of + * the resolution process is passed. This function is called at least once + * either with the imposter (if the resolution was not successful) or the + * resolved object directly when this function is called. If the resolution + * was not successful the first time, it may be called another time later + * in the context of the "performDeferredResolution" function. + * @param location is the location in the current source file in which the + * resolution was triggered. + * @return true if the resolution was immediately successful. This does not + * mean, that the resolved object does not exist, as it may be resolved + * later. + */ + bool resolve(const std::vector &path, const Rtti &type, + Logger &logger, ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback, + const SourceLocation &location = SourceLocation{}); + + /** + * Tries to resolve a node for the given type and path for all nodes + * currently on the stack, starting with the topmost node on the stack. + * The "resultCallback" is called when the resolution was successful, which + * may be at a later point in time. + * + * @param path is the path for which a node should be resolved. + * @param type is the type of the node that should be resolved. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @param resultCallback is the callback function to which the result of + * the resolution process is passed. This function is called once the + * resolution was successful. + * @param location is the location in the current source file in which the + * resolution was triggered. + * @return true if the resolution was immediately successful. This does not + * mean, that the resolved object does not exist, as it may be resolved + * later. + */ + bool resolve(const std::vector &path, const Rtti &type, + Logger &logger, ResolutionResultCallback resultCallback, + const SourceLocation &location = SourceLocation{}); + + /** + * Tries to resolve a node for the given type and path for all nodes + * currently on the stack, starting with the topmost node on the stack. + * Calls the "imposterCallback" function for obtaining a temporary result if + * a node cannot be resolved right now. The "resultCallback" is at most + * called twice: Once when this method is called (probably with the + * temporary) and another time if the resolution turned out to because + * successful at a later point in time. + * + * @tparam T is the type of the node that should be resolved. + * @param path is the path for which a node should be resolved. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @param imposterCallback is the callback function that is called if + * the node cannot be resolved at this moment. It gives the caller the + * possibility to create an imposter (a temporary object) that may be used + * later in the resolution process. + * @param resultCallback is the callback function to which the result of + * the resolution process is passed. This function is called at least once + * either with the imposter (if the resolution was not successful) or the + * resolved object directly when this function is called. If the resolution + * was not successful the first time, it may be called another time later + * in the context of the "performDeferredResolution" function. + * @param location is the location in the current source file in which the + * resolution was triggered. + * @return true if the resolution was immediately successful. This does not + * mean, that the resolved object does not exist, as it may be resolved + * later. + */ + template + bool resolve(const std::vector &path, Logger &logger, + std::function()> imposterCallback, + std::function, Logger &)> resultCallback, + const SourceLocation &location = SourceLocation{}) + { + return resolve( + path, typeOf(), logger, + [imposterCallback]() -> Rooted { return imposterCallback(); }, + [resultCallback](Handle node, Logger &logger) { + resultCallback(node.cast(), logger); + }, + location); + } + + /** + * Tries to resolve a node for the given type and path for all nodes + * currently on the stack, starting with the topmost node on the stack. + * The "resultCallback" is called when the resolution was successful, which + * may be at a later point in time. + * + * @tparam T is the type of the node that should be resolved. + * @param path is the path for which a node should be resolved. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @param resultCallback is the callback function to which the result of + * the resolution process is passed. This function is called once the + * resolution was successful. + * @param location is the location in the current source file in which the + * resolution was triggered. + * @return true if the resolution was immediately successful. This does not + * mean, that the resolved object does not exist, as it may be resolved + * later. + */ + template + bool resolve(const std::vector &path, Logger &logger, + std::function, Logger &)> resultCallback, + const SourceLocation &location = SourceLocation{}) + { + return resolve(path, typeOf(), logger, + [resultCallback](Handle node, Logger &logger) { + resultCallback(node.cast(), logger); + }, + location); + } + + /** + * Tries to resolve a node for the given type and path for all nodes + * currently on the stack, starting with the topmost node on the stack. + * Calls the "imposterCallback" function for obtaining a temporary result if + * a node cannot be resolved right now. The "resultCallback" is at most + * called twice: Once when this method is called (probably with the + * temporary) and another time if the resolution turned out to because + * successful at a later point in time. + * + * @tparam T is the type of the node that should be resolved. + * @param name is the path for which a node should be resolved. The name is + * split at '.' to form a path. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @param imposterCallback is the callback function that is called if + * the node cannot be resolved at this moment. It gives the caller the + * possibility to create an imposter (a temporary object) that may be used + * later in the resolution process. + * @param resultCallback is the callback function to which the result of + * the resolution process is passed. This function is called at least once + * either with the imposter (if the resolution was not successful) or the + * resolved object directly when this function is called. If the resolution + * was not successful the first time, it may be called another time later + * in the context of the "performDeferredResolution" function. + * @param location is the location in the current source file in which the + * resolution was triggered. + * @return true if the resolution was immediately successful. This does not + * mean, that the resolved object does not exist, as it may be resolved + * later. + */ + template + bool resolve(const std::string &name, Logger &logger, + std::function()> imposterCallback, + std::function, Logger &)> resultCallback, + const SourceLocation &location = SourceLocation{}) + { + return resolve(Utils::split(name, '.'), logger, imposterCallback, + resultCallback, location); + } + + /** + * Tries to resolve a node for the given type and path for all nodes + * currently on the stack, starting with the topmost node on the stack. + * The "resultCallback" is called when the resolution was successful, which + * may be at a later point in time. + * + * @tparam T is the type of the node that should be resolved. + * @param name is the path for which a node should be resolved. The name is + * split at '.' to form a path. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @param resultCallback is the callback function to which the result of + * the resolution process is passed. This function is called once the + * resolution was successful. + * @param location is the location in the current source file in which the + * resolution was triggered. + * @return true if the resolution was immediately successful. This does not + * mean, that the resolved object does not exist, as it may be resolved + * later. + */ + template + bool resolve(const std::string &name, Logger &logger, + std::function, Logger &)> resultCallback, + const SourceLocation &location = SourceLocation{}) + { + return resolve(Utils::split(name, '.'), logger, resultCallback, + location); + } + + /** + * Tries to resolve all currently deferred resolution steps. The list of + * pending deferred resolutions is cleared after this function has run. + * + * @param logger is the logger instance into which errors should be logged. + */ + bool performDeferredResolution(Logger &logger); +}; +} + +#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 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..4af88f9 100644 --- a/src/core/parser/ParserStack.hpp +++ b/src/core/parser/ParserStack.hpp @@ -42,9 +42,9 @@ #include #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 ®istry() { return handlerData.ctx.registry; } @@ -421,7 +421,6 @@ public: ParserContext &getContext() { return ctx; } }; } -} #endif /* _OUSIA_PARSER_STACK_HPP_ */ diff --git a/src/core/parser/Scope.cpp b/src/core/parser/Scope.cpp deleted file mode 100644 index 01292df..0000000 --- a/src/core/parser/Scope.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* - 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 . -*/ - -#include - -#include "Scope.hpp" - -namespace ousia { -namespace parser { - -/* Class GuardedScope */ - -GuardedScope::GuardedScope(Scope *scope, Handle 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 ScopeBase::resolve(const std::vector &path, - const Rtti &type, Logger &logger) -{ - // Go up the stack and try to resolve the - for (auto it = nodes.rbegin(); it != nodes.rend(); it++) { - std::vector res = (*it)->resolve(path, type); - - // Abort if the object could not be resolved - if (res.empty()) { - continue; - } - - // Log an error if the object is not unique - if (res.size() > 1) { - logger.error(std::string("The reference \"") + - Utils::join(path, ".") + ("\" is ambigous!")); - logger.note("Referenced objects are:"); - for (const ResolutionResult &r : res) { - logger.note(Utils::join(r.path(), ".")); - } - } - return res[0].node; - } - return nullptr; -} - -/* Class DeferredResolution */ - -DeferredResolution::DeferredResolution(const NodeVector &nodes, - const std::vector &path, - const Rtti &type, - ResolutionResultCallback resultCallback, - const SourceLocation &location) - : scope(nodes), - resultCallback(resultCallback), - path(path), - type(type), - location(location) -{ -} - -bool DeferredResolution::resolve(Logger &logger) -{ - Rooted res = scope.resolve(path, type, logger); - if (res != nullptr) { - try { - resultCallback(res, logger); - } - catch (LoggableException ex) { - logger.log(ex); - } - return true; - } - return false; -} - -/* Class Scope */ - -void Scope::push(Handle node) { nodes.push_back(node); } - -void Scope::pop() { nodes.pop_back(); } - -GuardedScope Scope::descend(Handle node) -{ - return GuardedScope{this, node}; -} - -Rooted Scope::getRoot() const { return nodes.front(); } - -Rooted Scope::getLeaf() { return nodes.back(); } - -bool Scope::resolve(const std::vector &path, const Rtti &type, - Logger &logger, ResolutionImposterCallback imposterCallback, - ResolutionResultCallback resultCallback, - const SourceLocation &location) -{ - if (!resolve(path, type, logger, resultCallback, location)) { - resultCallback(imposterCallback(), logger); - return false; - } - return true; -} - -bool Scope::resolve(const std::vector &path, const Rtti &type, - Logger &logger, ResolutionResultCallback resultCallback, - const SourceLocation &location) -{ - Rooted res = ScopeBase::resolve(path, type, logger); - if (res != nullptr) { - try { - resultCallback(res, logger); - } - catch (LoggableException ex) { - logger.log(ex, location); - } - return true; - } - deferred.emplace_back(nodes, path, type, resultCallback, location); - return false; -} - -bool Scope::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). - while (true) { - // Iterate over all deferred resolution processes, - bool hasChange = false; - for (auto it = deferred.begin(); it != deferred.end();) { - if (it->resolve(logger)) { - it = deferred.erase(it); - hasChange = true; - } else { - it++; - } - } - - // Abort if nothing has changed in the last iteration - if (!hasChange) { - break; - } - } - - // We were successful if there are no more deferred resolutions - if (deferred.empty()) { - return true; - } - - // 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, ".") + - std::string("\""), - failed.location); - } - deferred.clear(); - return false; -} -} -} diff --git a/src/core/parser/Scope.hpp b/src/core/parser/Scope.hpp deleted file mode 100644 index b9b7f80..0000000 --- a/src/core/parser/Scope.hpp +++ /dev/null @@ -1,481 +0,0 @@ -/* - 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 . -*/ - -#ifndef _OUSIA_PARSER_SCOPE_H_ -#define _OUSIA_PARSER_SCOPE_H_ - -#include -#include -#include - -#include -#include -#include -#include - -/** - * @file Scope.hpp - * - * Contains the Scope 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; - -/** - * Callback function type used for creating a dummy object while no correct - * object is available for resolution. - */ -using ResolutionImposterCallback = std::function()>; - -/** - * Callback function type called whenever the result of a resolution is - * available. - */ -using ResolutionResultCallback = - std::function, 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); - - /** - * 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 { -protected: - /** - * List containing all nodes currently on the scope, with the newest nodes - * being pushed to the back of the list. - */ - NodeVector nodes; - -public: - /** - * Default constructor, creates an empty Scope instance. - */ - ScopeBase() {} - - /** - * Creates a new instance of the ScopeBase 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 &nodes) : nodes(nodes) {} - - /** - * Tries to resolve a node for the given type and path for all nodes that - * are currently in the stack, starting with the topmost node on the stack. - * - * @param path is the path for which a node should be resolved. - * @param type is the type of the node that should be resolved. - * @param logger is the logger instance into which resolution problems - * should be logged. - * @return a reference at a resolved node or nullptr if no node could be - * found. - */ - Rooted resolve(const std::vector &path, - const Rtti &type, Logger &logger); -}; - -/** - * Class used for representing a deferred resolution. A deferred resolution is - * triggered whenever an object cannot be resolved, but there may be a chance - * that it can be resolved in the future. This happens e.g. if a document is - * just being parsed and the object that is being refered to has not been - * reached yet. - */ -class DeferredResolution { -private: - /** - * Copy of the scope at the time when the resolution was first triggered. - */ - ScopeBase scope; - - /** - * Callback function to be called when an element is successfully resolved. - */ - ResolutionResultCallback resultCallback; - -public: - /** - * Path queried for the resolution. - */ - std::vector path; - - /** - * Reference at the type of the object that should be resolved. - */ - const Rtti &type; - - /** - * Position at which the resolution was triggered. - */ - const SourceLocation location; - - /** - * Constructor of the DeferredResolutionScope class. Copies the given - * arguments. - * - * @param nodes is a reference at the current internal node stack of the - * Scope 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. - * @param resultCallback is the callback function that should be called if - * the desired element has indeed been found. - * @param location is the location at which the resolution was triggered. - */ - DeferredResolution(const NodeVector &nodes, - const std::vector &path, - const Rtti &type, - ResolutionResultCallback resultCallback, - const SourceLocation &location = SourceLocation{}); - - /** - * Performs the actual deferred resolution and calls the resultCallback - * callback function in case the resolution is sucessful. In this case - * returns true, false otherwise. - * - * @param logger is the logger instance to which error messages should be - * logged. - * @return true if the resolution was successful, false otherwise. - */ - bool resolve(Logger &logger); -}; - -/** - * 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. - */ -class Scope : public ScopeBase { -private: - /** - * List containing all deferred resolution descriptors. - */ - std::list deferred; - -public: - /** - * Default constructor of the Scope class, creates an empty Scope with no - * element on the internal stack. - */ - Scope() {} - - /** - * Pushes a new node onto the scope. - * - * @param node is the node that should be used for local lookup. - */ - void push(Handle node); - - /** - * Removes the last pushed node from the scope. - */ - 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); - - /** - * Returns the top-most Node instance in the Scope hirarchy. - * - * @return a reference at the root node. - */ - Rooted getRoot() const; - - /** - * Returns the bottom-most Node instance in the Scope hirarchy, e.g. the - * node that was pushed last onto the stack. - * - * @return a reference at the leaf node. - */ - Rooted getLeaf(); - - /** - * Tries to resolve a node for the given type and path for all nodes - * currently on the stack, starting with the topmost node on the stack. - * Calls the "imposterCallback" function for obtaining a temporary result if - * a node cannot be resolved right now. The "resultCallback" is at most - * called twice: Once when this method is called (probably with the - * temporary) and another time if the resolution turned out to be successful - * at a later point in time. - * - * @param path is the path for which a node should be resolved. - * @param type is the type of the node that should be resolved. - * @param logger is the logger instance into which resolution problems - * should be logged. - * @param imposterCallback is the callback function that is called if - * the node cannot be resolved at this moment. It gives the caller the - * possibility to create an imposter (a temporary object) that may be used - * later in the resolution process. - * @param resultCallback is the callback function to which the result of - * the resolution process is passed. This function is called at least once - * either with the imposter (if the resolution was not successful) or the - * resolved object directly when this function is called. If the resolution - * was not successful the first time, it may be called another time later - * in the context of the "performDeferredResolution" function. - * @param location is the location in the current source file in which the - * resolution was triggered. - * @return true if the resolution was immediately successful. This does not - * mean, that the resolved object does not exist, as it may be resolved - * later. - */ - bool resolve(const std::vector &path, const Rtti &type, - Logger &logger, ResolutionImposterCallback imposterCallback, - ResolutionResultCallback resultCallback, - const SourceLocation &location = SourceLocation{}); - - /** - * Tries to resolve a node for the given type and path for all nodes - * currently on the stack, starting with the topmost node on the stack. - * The "resultCallback" is called when the resolution was successful, which - * may be at a later point in time. - * - * @param path is the path for which a node should be resolved. - * @param type is the type of the node that should be resolved. - * @param logger is the logger instance into which resolution problems - * should be logged. - * @param resultCallback is the callback function to which the result of - * the resolution process is passed. This function is called once the - * resolution was successful. - * @param location is the location in the current source file in which the - * resolution was triggered. - * @return true if the resolution was immediately successful. This does not - * mean, that the resolved object does not exist, as it may be resolved - * later. - */ - bool resolve(const std::vector &path, const Rtti &type, - Logger &logger, ResolutionResultCallback resultCallback, - const SourceLocation &location = SourceLocation{}); - - /** - * Tries to resolve a node for the given type and path for all nodes - * currently on the stack, starting with the topmost node on the stack. - * Calls the "imposterCallback" function for obtaining a temporary result if - * a node cannot be resolved right now. The "resultCallback" is at most - * called twice: Once when this method is called (probably with the - * temporary) and another time if the resolution turned out to because - * successful at a later point in time. - * - * @tparam T is the type of the node that should be resolved. - * @param path is the path for which a node should be resolved. - * @param logger is the logger instance into which resolution problems - * should be logged. - * @param imposterCallback is the callback function that is called if - * the node cannot be resolved at this moment. It gives the caller the - * possibility to create an imposter (a temporary object) that may be used - * later in the resolution process. - * @param resultCallback is the callback function to which the result of - * the resolution process is passed. This function is called at least once - * either with the imposter (if the resolution was not successful) or the - * resolved object directly when this function is called. If the resolution - * was not successful the first time, it may be called another time later - * in the context of the "performDeferredResolution" function. - * @param location is the location in the current source file in which the - * resolution was triggered. - * @return true if the resolution was immediately successful. This does not - * mean, that the resolved object does not exist, as it may be resolved - * later. - */ - template - bool resolve(const std::vector &path, Logger &logger, - std::function()> imposterCallback, - std::function, Logger &)> resultCallback, - const SourceLocation &location = SourceLocation{}) - { - return resolve( - path, typeOf(), logger, - [imposterCallback]() -> Rooted { return imposterCallback(); }, - [resultCallback](Handle node, Logger &logger) { - resultCallback(node.cast(), logger); - }, - location); - } - - /** - * Tries to resolve a node for the given type and path for all nodes - * currently on the stack, starting with the topmost node on the stack. - * The "resultCallback" is called when the resolution was successful, which - * may be at a later point in time. - * - * @tparam T is the type of the node that should be resolved. - * @param path is the path for which a node should be resolved. - * @param logger is the logger instance into which resolution problems - * should be logged. - * @param resultCallback is the callback function to which the result of - * the resolution process is passed. This function is called once the - * resolution was successful. - * @param location is the location in the current source file in which the - * resolution was triggered. - * @return true if the resolution was immediately successful. This does not - * mean, that the resolved object does not exist, as it may be resolved - * later. - */ - template - bool resolve(const std::vector &path, Logger &logger, - std::function, Logger &)> resultCallback, - const SourceLocation &location = SourceLocation{}) - { - return resolve(path, typeOf(), logger, - [resultCallback](Handle node, Logger &logger) { - resultCallback(node.cast(), logger); - }, - location); - } - - /** - * Tries to resolve a node for the given type and path for all nodes - * currently on the stack, starting with the topmost node on the stack. - * Calls the "imposterCallback" function for obtaining a temporary result if - * a node cannot be resolved right now. The "resultCallback" is at most - * called twice: Once when this method is called (probably with the - * temporary) and another time if the resolution turned out to because - * successful at a later point in time. - * - * @tparam T is the type of the node that should be resolved. - * @param name is the path for which a node should be resolved. The name is - * split at '.' to form a path. - * @param logger is the logger instance into which resolution problems - * should be logged. - * @param imposterCallback is the callback function that is called if - * the node cannot be resolved at this moment. It gives the caller the - * possibility to create an imposter (a temporary object) that may be used - * later in the resolution process. - * @param resultCallback is the callback function to which the result of - * the resolution process is passed. This function is called at least once - * either with the imposter (if the resolution was not successful) or the - * resolved object directly when this function is called. If the resolution - * was not successful the first time, it may be called another time later - * in the context of the "performDeferredResolution" function. - * @param location is the location in the current source file in which the - * resolution was triggered. - * @return true if the resolution was immediately successful. This does not - * mean, that the resolved object does not exist, as it may be resolved - * later. - */ - template - bool resolve(const std::string &name, Logger &logger, - std::function()> imposterCallback, - std::function, Logger &)> resultCallback, - const SourceLocation &location = SourceLocation{}) - { - return resolve(Utils::split(name, '.'), logger, imposterCallback, - resultCallback, location); - } - - /** - * Tries to resolve a node for the given type and path for all nodes - * currently on the stack, starting with the topmost node on the stack. - * The "resultCallback" is called when the resolution was successful, which - * may be at a later point in time. - * - * @tparam T is the type of the node that should be resolved. - * @param name is the path for which a node should be resolved. The name is - * split at '.' to form a path. - * @param logger is the logger instance into which resolution problems - * should be logged. - * @param resultCallback is the callback function to which the result of - * the resolution process is passed. This function is called once the - * resolution was successful. - * @param location is the location in the current source file in which the - * resolution was triggered. - * @return true if the resolution was immediately successful. This does not - * mean, that the resolved object does not exist, as it may be resolved - * later. - */ - template - bool resolve(const std::string &name, Logger &logger, - std::function, Logger &)> resultCallback, - const SourceLocation &location = SourceLocation{}) - { - return resolve(Utils::split(name, '.'), logger, resultCallback, - location); - } - - /** - * Tries to resolve all currently deferred resolution steps. The list of - * pending deferred resolutions is cleared after this function has run. - * - * @param logger is the logger instance into which errors should be logged. - */ - bool performDeferredResolution(Logger &logger); -}; -} -} - -#endif /* _OUSIA_PARSER_SCOPE_H_ */ - 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 +#include namespace ousia { -namespace parser { -namespace css { // CSS code tokens static const int CURLY_OPEN = 1; @@ -75,7 +74,7 @@ static const std::map CSS_DESCRIPTORS = { {ESCAPE, {CodeTokenMode::ESCAPE, ESCAPE}}, {LINEBREAK, {CodeTokenMode::LINEBREAK, LINEBREAK}}}; -Rooted CSSParser::parse(CharReader &reader, ParserContext &ctx) +Rooted 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 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 parse(CharReader &reader, ParserContext &ctx) override; - - using Parser::parse; - - /** - * As befits a class called CSSParser, this Parser parses CSS. - */ - std::set mimetypes() - { - std::set out{"text/css"}; - return out; - } + Rooted doParse(CharReader &reader, ParserContext &ctx) override; }; } -} -} #endif diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index 434a72c..ef738d8 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -25,13 +25,12 @@ #include #include #include +#include #include #include "XmlParser.hpp" namespace ousia { -namespace parser { -namespace xml { using namespace ousia::model; @@ -291,12 +290,7 @@ static void xmlCharacterDataHandler(void *p, const XML_Char *s, int len) /* Class XmlParser */ -std::set XmlParser::mimetypes() -{ - return std::set{{"text/vnd.ousia.oxm", "text/vnd.ousia.oxd"}}; -} - -Rooted XmlParser::parse(CharReader &reader, ParserContext &ctx) +Rooted XmlParser::doParse(CharReader &reader, ParserContext &ctx) { // Create the parser object ScopedExpatXmlParser p{"UTF-8"}; @@ -346,6 +340,4 @@ Rooted 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 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 mimetypes() override; - +protected: /** * Parses the given input stream as XML file and returns the parsed * top-level node. @@ -56,13 +46,9 @@ public: * @param ctx is a reference to the ParserContext instance that should be * used. */ - Rooted parse(CharReader &reader, ParserContext &ctx) override; - - using Parser::parse; + Rooted doParse(CharReader &reader, ParserContext &ctx) override; }; -} -} } #endif /* _OUSIA_XML_PARSER_HPP_ */ -- cgit v1.2.3 From 3c659c2737b26d8ee28c727b277325852df8dd09 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Wed, 21 Jan 2015 01:24:37 +0100 Subject: Do only perform relative file lookups if a relative path is given (to allow users to include files without accidently including a global resource) --- src/plugins/filesystem/FileLocator.cpp | 5 +++++ test/plugins/filesystem/FileLocatorTest.cpp | 20 ++++++++++++++++++++ testdata/filesystem/b/d.txt | 1 + 3 files changed, 26 insertions(+) create mode 100644 testdata/filesystem/b/d.txt (limited to 'src') diff --git a/src/plugins/filesystem/FileLocator.cpp b/src/plugins/filesystem/FileLocator.cpp index 467363b..af5244c 100644 --- a/src/plugins/filesystem/FileLocator.cpp +++ b/src/plugins/filesystem/FileLocator.cpp @@ -143,6 +143,11 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path, } } + // If the path starts with "./" only perform relative lookups! + if (path.substr(0, 2) == "./") { + return false; + } + // Otherwise look in the search paths, search backwards, last defined search // paths have a higher precedence auto it = searchPaths.find(type); diff --git a/test/plugins/filesystem/FileLocatorTest.cpp b/test/plugins/filesystem/FileLocatorTest.cpp index 17d43dd..beb091d 100644 --- a/test/plugins/filesystem/FileLocatorTest.cpp +++ b/test/plugins/filesystem/FileLocatorTest.cpp @@ -142,6 +142,26 @@ TEST(FileLocator, testLocate) assert_not_located(locator, "c.txt", "", ResourceType::SCRIPT); } +TEST(FileLocator, testLocateRelative) +{ + FileLocator locator; + locator.addUnittestSearchPath("filesystem"); + + // Add the respective search path + locator.addUnittestSearchPath("filesystem/b"); + + Resource resA, resC; + ASSERT_TRUE(locator.locate(resA, "a.txt")); + ASSERT_TRUE(locator.locate(resC, "c.txt")); + + Resource resD; + ASSERT_TRUE(locator.locate(resD, "d.txt")); + ASSERT_TRUE(locator.locate(resD, "d.txt", ResourceType::UNKNOWN, resA)); + ASSERT_TRUE(locator.locate(resD, "d.txt", ResourceType::UNKNOWN, resC)); + ASSERT_FALSE(locator.locate(resD, "./d.txt", ResourceType::UNKNOWN, resA)); + ASSERT_TRUE(locator.locate(resD, "./d.txt", ResourceType::UNKNOWN, resC)); +} + TEST(FileLocator, testStream) { FileLocator locator; diff --git a/testdata/filesystem/b/d.txt b/testdata/filesystem/b/d.txt new file mode 100644 index 0000000..3e676fe --- /dev/null +++ b/testdata/filesystem/b/d.txt @@ -0,0 +1 @@ +file d -- cgit v1.2.3 From 8e7be30a309ccd84684033ea854c574b32282495 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Thu, 22 Jan 2015 02:42:29 +0100 Subject: Added Location.cpp (empty, but verifies that the header has no errors) --- CMakeLists.txt | 1 + src/core/common/Location.cpp | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/core/common/Location.cpp (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b8efc8..bcba476 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,7 @@ ADD_LIBRARY(ousia_core src/core/common/Exceptions src/core/common/Function src/core/common/Logger + src/core/common/Location src/core/common/Number src/core/common/Property src/core/common/Rtti diff --git a/src/core/common/Location.cpp b/src/core/common/Location.cpp new file mode 100644 index 0000000..6f9250a --- /dev/null +++ b/src/core/common/Location.cpp @@ -0,0 +1,23 @@ +/* + 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 . +*/ + +#include "Location.hpp" + +namespace ousia { +} + -- cgit v1.2.3 From d41716e4cc2be229fee82d99dc84e253d7949d47 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Thu, 22 Jan 2015 02:43:00 +0100 Subject: Added function which registers mimetypes for some default extensions (to be extended) --- src/core/Registry.cpp | 9 +++++++++ src/core/Registry.hpp | 5 +++++ 2 files changed, 14 insertions(+) (limited to 'src') diff --git a/src/core/Registry.cpp b/src/core/Registry.cpp index c42a97a..88babb7 100644 --- a/src/core/Registry.cpp +++ b/src/core/Registry.cpp @@ -74,6 +74,15 @@ void Registry::registerExtension(const std::string &extension, 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"); +} + std::string Registry::getMimetypeForExtension( const std::string &extension) const { diff --git a/src/core/Registry.hpp b/src/core/Registry.hpp index 965f336..f932480 100644 --- a/src/core/Registry.hpp +++ b/src/core/Registry.hpp @@ -105,6 +105,11 @@ public: 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. * -- cgit v1.2.3 From b3ebc84c8dfa7379a3977ed7305fd7cf7fdd8ee7 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Thu, 22 Jan 2015 02:43:13 +0100 Subject: Improved FileLocator --- src/plugins/filesystem/FileLocator.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/plugins/filesystem/FileLocator.cpp b/src/plugins/filesystem/FileLocator.cpp index af5244c..6804c6d 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_file(base)) { std::string location = fs::canonical(base).generic_string(); #ifdef FILELOCATOR_DEBUG_PRINT std::cout << "FileLocator: Found \"" << path << "\" at " @@ -143,8 +143,8 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path, } } - // If the path starts with "./" only perform relative lookups! - if (path.substr(0, 2) == "./") { + // If the path starts with "./" or "../" only perform relative lookups! + if (path.substr(0, 2) == "./" || path.substr(0, 3) == "../") { return false; } @@ -159,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_file(p)) { std::string location = fs::canonical(p).generic_string(); #ifdef FILELOCATOR_DEBUG_PRINT std::cout << "FileLocator: Found \"" << path << "\" in " -- cgit v1.2.3 From f90a9bf51f300dd277071b1461d00411d7c21b89 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Thu, 22 Jan 2015 02:45:57 +0100 Subject: Rethought task of the Project class: Only keeps track of the included documents -- does not have references to typesystems, domains etc. (this allows instances of these classes to be freed as soon as no document refers to them). Project should be a parent of Document. Project should resolve documents as composita (this allows to have between-document references). --- src/core/model/Project.cpp | 38 +++++++--------------------------- src/core/model/Project.hpp | 51 +++++++--------------------------------------- 2 files changed, 14 insertions(+), 75 deletions(-) (limited to 'src') diff --git a/src/core/model/Project.cpp b/src/core/model/Project.cpp index fc08660..56a3af2 100644 --- a/src/core/model/Project.cpp +++ b/src/core/model/Project.cpp @@ -38,9 +38,7 @@ Project::Project(Manager &mgr) bool Project::doValidate(Logger &logger) const { - return continueValidation(documents, logger) & - continueValidation(domains, logger) & - continueValidation(typesystems, logger); + return continueValidation(documents, logger); } Rooted Project::getSystemTypesystem() @@ -50,58 +48,36 @@ Rooted Project::getSystemTypesystem() Rooted Project::createTypesystem(const std::string &name) { - Rooted typesystem{ + return Rooted{ new Typesystem{getManager(), systemTypesystem, name}}; - addTypesystem(typesystem); - return typesystem; -} - -void Project::addTypesystem(Handle typesystem) -{ - invalidate(); - typesystems.push_back(typesystem); } Rooted Project::createDocument(const std::string &name) { - Rooted document{new Document(getManager(), name)}; + return Rooted document{new Document(getManager(), name)}; addDocument(document); return document; } -void Project::addDocument(Handle document) -{ - invalidate(); - documents.push_back(document); -} - Rooted Project::createDomain(const std::string &name) { - Rooted domain{new Domain(getManager(), systemTypesystem, name)}; - addDomain(domain); - return domain; + return Rooted{new Domain(getManager(), systemTypesystem, name)}; } -void Project::addDomain(Handle domain) +void Project::addDocument(Handle document) { invalidate(); - domains.push_back(domain); + documents.push_back(document); } const NodeVector &Project::getDocuments() { return documents; } - -const NodeVector &Project::getDomains() { return domains; } - -const NodeVector &Project::getTypesystems() { return typesystems; } } namespace RttiTypes { const Rtti Project = RttiBuilder("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 documents; - /** - * List containing all loaded domains. - */ - NodeVector domains; - - /** - * List containing all loaded typesystems. - */ - NodeVector typesystems; - protected: /** * Validates the project and all parts it consists of. @@ -102,13 +93,6 @@ public: */ Rooted 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); - /** * Returns a new document with the given name and adds it to the list of * documents. @@ -117,13 +101,6 @@ public: */ Rooted 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); - /** * 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 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); + void addDocument(Handle document); /** * Returns all documents of this project. * * @return a reference pointing at the document list. */ - const NodeVector &getDocuments(); - - /** - * Returns all domains of this project. - * - * @return a reference pointing at the domain list. - */ - const NodeVector &getDomains(); - - /** - * Returns all typesystems of this project. - * - * @return a reference pointing at the typesystem list. - */ - const NodeVector &getTypesystems(); + const NodeVector &getDocuments() const; }; } -- cgit v1.2.3 From 9b9c7439b6cb1b711901ad49cd4855e1b0964652 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Thu, 22 Jan 2015 02:49:09 +0100 Subject: Backup --- src/core/common/Location.hpp | 375 ++++++++++++++++++++++++++++------ src/core/parser/ParserContext.hpp | 4 +- src/core/resource/ResourceManager.cpp | 252 +++++++++++++++++++++++ src/core/resource/ResourceManager.hpp | 152 ++++++++++++++ 4 files changed, 722 insertions(+), 61 deletions(-) create mode 100644 src/core/resource/ResourceManager.cpp create mode 100644 src/core/resource/ResourceManager.hpp (limited to 'src') diff --git a/src/core/common/Location.hpp b/src/core/common/Location.hpp index 39e1011..f3a30b2 100644 --- a/src/core/common/Location.hpp +++ b/src/core/common/Location.hpp @@ -16,91 +16,310 @@ along with this program. If not, see . */ +/** + * @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 +#include #include 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; + +/** + * 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. */ -struct SourceLocation { +constexpr SourceOffset SourceOffsetMax = + std::numeric_limits::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 > SourceOffsetMax ? SourceOffsetMax : pos; +} + +/** + * Class specifying a position within an (unspecified) source file. + */ +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 + * SourceOffsetMax and thus marks the SourcePosition as invalid. */ - int column; + SourcePosition() : pos(SourceOffsetMax) {} /** - * 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. */ - SourceLocation(int line, int column) : line(line), column(column), offs(0) + bool isValid() { return pos != SourceOffsetMax; } +}; + +/** + * The SourceRange class represents a range within an (unspecified) source file. + */ +class SourceRange { +private: + /** + * Start byte offset. + */ + SourcePosition start; + + /** + * End byte offset. + */ + 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. */ - bool hasColumn() const { return column > 0; } + SourceOffset getStart() const { return start.getPosition(); } /** - * Returns true, if the position is valid, false otherwise. This function is - * equivalent to the hasLine() function. + * Returns the end position of the SourceRange instance. * - * @return true if the Position struct is valid. + * @return the end offset in bytes (non-inclusive). */ - bool valid() const { return hasLine(); } + 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. + */ + SourcePosition getStartPosition() const { return start; } + + /** + * Returns a copy of the underlying SourcePosition instance representing the + * end position. + * + * @return a copy of the end SourcePosition instance. + */ + 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() + { + 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: + /** + * Constructor, binds the SourceLocation to the given source file. + * + * @param sourceId is the sourceId to which the SourceLocation instance + * should be bound. The sourceId cannot be overriden after construction. + */ + SourceLocation(SourceId sourceId) : sourceId(sourceId){}; + + /** + * Constructor for a zero-length range. + * + * @param sourceId is the sourceId to which the SourceLocation instance + * should be bound. The sourceId cannot be overriden after construction. + * @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 is the sourceId to which the SourceLocation instance + * should be bound. The sourceId cannot be overriden after construction. + * @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 is the sourceId to which the SourceLocation instance + * should be bound. The sourceId cannot be overriden after construction. + * @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) + { + } + + /** + * 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() { return sourceId; } }; /** @@ -108,6 +327,37 @@ struct SourceLocation { * 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. */ @@ -119,6 +369,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 @@ -134,39 +390,40 @@ 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 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; } }; /** diff --git a/src/core/parser/ParserContext.hpp b/src/core/parser/ParserContext.hpp index 88d1f52..bb64600 100644 --- a/src/core/parser/ParserContext.hpp +++ b/src/core/parser/ParserContext.hpp @@ -71,8 +71,8 @@ struct ParserContext { /** * Constructor of the ParserContext class. * - * @param scope is a reference to the ParserScope instance that should be used to - * lookup names. + * @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. diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp new file mode 100644 index 0000000..563fc12 --- /dev/null +++ b/src/core/resource/ResourceManager.cpp @@ -0,0 +1,252 @@ +/* + 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 . +*/ + +#include +#include +#include +#include +#include +#include + +#include "Resource.hpp" +#include "ResourceManager.hpp" + +namespace ousia { + +/* Deduction of the ResourceType */ + +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 RelResourceTypeMap{ + {"document", ResourceType::DOCUMENT}, + {"domain", ResourceType::DOMAIN_DESC}, + {"typesystem", ResourceType::TYPESYSTEM}}; + +/** + * Map mapping from Rtti pointers to the corresponding ResourceType. + */ +static const std::unordered_map RttiResourceTypeMap{ + {&RttiTypes::Document, ResourceType::DOCUMENT}, + {&RttiTypes::Domain, ResourceType::DOMAIN_DESC}, + {&RttiTypes::Typesystem, ResourceType::TYPESYSTEM}}; + +static ResourceType relToResourceType(const std::string &rel, Logger &logger) +{ + std::string s = Utils::toLowercase(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; +} + +static ResourceType supportedTypesToResourceType(const RttiSet &supportedTypes) +{ + if (supportedTypes.size() == 1U) { + auto it = RttiResourceTypeMap.find(supportedTypes[0]); + if (it != RelResourceTypeMap.end()) { + return it->second; + } + } + return ResourceType::UNKNOWN; +} + +static ResourceType deduceResourceType(const std::string &rel, + const RttiSet &supportedTypes, + Logger &logger) +{ + ResourceType res; + + // Try to deduce the ResourceType from the "rel" attribute + res = relToResourceType(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 = supportedTypesToResourceType(supportedTypes); + } + if (res == ResourceType::UNKNOWN) { + logger.note( + "Ambigous resource type, consider specifying the \"rel\" " + "attribute"); + } + return res; +} + +/* Functions for reducing the set of supported types */ + +/** + * Map mapping from relations (the "rel" attribute in includes) to the + * corresponding RttiType + */ +static const std::unordered_map RelRttiTypeMap{ + {"document", &RttiTypes::DOCUMENT}, + {"domain", &RttiTypes::DOMAIN}, + {"typesystem", &RttiTypes::TYPESYSTEM}}; + +static Rtti *relToRttiType(const std::string &rel) +{ + std::string s = Utils::toLowercase(rel); + if (!s.empty()) { + auto it = RelRttiTypeMap.find(s); + if (it != RelRttiTypeMap.end()) { + return it->second; + } + } + return &ResourceType::Node; +} + +static RttiType shrinkSupportedTypes(const RttiSet &supportedTypes, + const std::string &rel) +{ + RttiSet types; + RttiType *type = relToRttiType(rel); + for (RttiType *supportedType : supportedTypes) { + if (supportedType->isa(type)) { + types.insert(supportedType); + } + } + return types; +} + +/* Class ResourceManager */ + +Rooted ResourceManager::link(ParserContext &ctx, Resource &resource, + const std::string &mimetype, + const RttiSet &supportedTypes) +{ + +} + +Rooted 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 = + 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 = shrinkSupportedTypes(supportedTypes, rel); + + // Check whether the resource has already been parsed + Rooted node = nullptr; + auto it = locations.find(res.getLocation()); + if (it != locations.end()) { + node = + } + = link(ctx, resource, mimetype, types); + + // Try to deduce the mimetype + std::string mime = mimetype; + if (mime.empty()) { + // Fetch the file extension + std::string ext = Utils::extractFileExtension(path); + if (ext.empty()) { + ctx.logger.error( + std::string("Specified filename \"") + path + + std::string( + "\" has no extension and no mimetype (\"type\" " + "attribute) was given instead.")); + return nullptr; + } + + // Fetch the mimetype for the extension + mime = ctx.registry.getMimetypeForExtension(ext); + if (mime.empty()) { + ctx.logger.error(std::string("Unknown file extension \"") + ext + + std::string("\"")); + return nullptr; + } + } + + // Fetch a parser for the mimetype + const std::pair parser = + ctx.registry.getParserForMimetype(mime); + + // Make sure a parser was found + if (!parser->first) { + ctx.logger.error(std::string("Cannot parse files of type \"") + mime + + std::string("\"")); + } + + // Make sure the parser returns one of the supported types +} + +Rooted 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 include(ctx, path, mimetype, rel, supportedTypes, relativeResource); +} + +const Resource &getResource(SourceId sourceId) const +{ + if (sourceId < resources.size()) { + return resources[sourceId]; + } + return NullResource; +} + +SourceContext ResourceManager::buildContext(const SourceLocation &location) +{ + SourceContext res; + + // TODO + + return res; +} +}; +} + +#endif /* _OUSIA_RESOURCE_MANAGER_HPP_ */ + diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp new file mode 100644 index 0000000..809fd55 --- /dev/null +++ b/src/core/resource/ResourceManager.hpp @@ -0,0 +1,152 @@ +/* + 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 . +*/ + +/** + * @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 +#include + +#include +#include + +namespace ousia { + +// Forward declarations +class Node; +class ParserContext; +class Resource; +class RttiSet; +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: + /** + * Vector used for mapping SourceId instances to the underlying resource. + */ + std::vector resources; + + /** + * Index pointing at the next free entry in the resources list. + */ + SourceId nextFreeSourceId = 0; + + /** + * Map between Resource locations and the corresponding SourceId. + */ + std::unordered_map locations; + + /** + * Map between a SourceId and the corresponding (if available) parsed node + * uid (this resembles weak references to the Node instance). + */ + std::unordered_map 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> lineNumberCache; + + + Rooted getCachedNode(SourceId id); + + Rooted getCachedNode(const std::string &location); + + SourceId getSourceId(const std::string &location); + + /** + * Used internally to either parse a resource or retrieve it from the + * internal cache of already parsed resources. + */ + Rooted link(ParserContext &ctx, Resource &resource, const std::string &mimetype, + const RttiSet &supportedTypes); + + +public: + /** + * 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 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 link(ParserContext &ctx, const std::string &path, + const std::string &mimetype, const std::string &rel, + const RttiSet &supportedTypes, SourceId relativeTo); + + /** + * 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; + + /** + * 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_ */ + -- cgit v1.2.3 From 887244db14f00063ebd429a8535fb204a96bee4e Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 00:30:20 +0100 Subject: Improved documentation --- src/core/managed/Managed.hpp | 32 ++++++++++++++++++++++++++++++++ src/core/managed/Manager.hpp | 3 ++- 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'src') 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 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 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> 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); -- cgit v1.2.3 From ef59091033b3f69dd01df009f21927b8ad81f5a9 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 00:30:44 +0100 Subject: Improved documentation and added "isOneOf" function to Rtti class. --- src/core/common/Rtti.cpp | 11 +++++++++++ src/core/common/Rtti.hpp | 11 +++++++++++ 2 files changed, 22 insertions(+) (limited to 'src') diff --git a/src/core/common/Rtti.cpp b/src/core/common/Rtti.cpp index 1213669..668e418 100644 --- a/src/core/common/Rtti.cpp +++ b/src/core/common/Rtti.cpp @@ -125,6 +125,17 @@ bool Rtti::isa(const Rtti &other) const return parents.count(&other) > 0; } +bool Rtii::isOneOf(const RttiSet &others) const +{ + initialize(); + for (const Rtti *other: others) { + if (parents.count(other) > 0) { + return true; + } + } + return false; +} + bool Rtti::composedOf(const Rtti &other) const { initialize(); diff --git a/src/core/common/Rtti.hpp b/src/core/common/Rtti.hpp index fa2692f..53043e2 100644 --- a/src/core/common/Rtti.hpp +++ b/src/core/common/Rtti.hpp @@ -378,9 +378,20 @@ 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; + /** * 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 -- cgit v1.2.3 From ba52f3f4823faf9c73e9445770a44887f3c2b389 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 00:31:07 +0100 Subject: Added mimetype by filename lookup to registry --- src/core/Registry.cpp | 12 ++++++++++++ src/core/Registry.hpp | 9 +++++++++ 2 files changed, 21 insertions(+) (limited to 'src') diff --git a/src/core/Registry.cpp b/src/core/Registry.cpp index 88babb7..044668c 100644 --- a/src/core/Registry.cpp +++ b/src/core/Registry.cpp @@ -97,6 +97,18 @@ std::string Registry::getMimetypeForExtension( return std::string{}; } +std::string Registry::getMimetypeForFilename(const std::string &filename) const +{ + // Fetch the file extension + std::string ext = Utils::extractFileExtension(path); + if (ext.empty()) { + return std::string{}; + } + + // Fetch the mimetype for the extension + return ctx.registry.getMimetypeForExtension(ext); +} + void Registry::registerResourceLocator(ResourceLocator *locator) { locators.push_back(locator); diff --git a/src/core/Registry.hpp b/src/core/Registry.hpp index f932480..4b4cb65 100644 --- a/src/core/Registry.hpp +++ b/src/core/Registry.hpp @@ -120,6 +120,15 @@ public: */ 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 -- cgit v1.2.3 From 1179bad0732e09b1bcae5fd77b62c4e7116d6d73 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 00:32:17 +0100 Subject: Renamed SourceOffset max to InvalidSourceOffset, introduced InvalidSourceId, made sourceId in SourceLocation mutable --- src/core/common/Location.hpp | 52 ++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/core/common/Location.hpp b/src/core/common/Location.hpp index f3a30b2..ae50e91 100644 --- a/src/core/common/Location.hpp +++ b/src/core/common/Location.hpp @@ -39,6 +39,11 @@ namespace ousia { */ using SourceId = uint32_t; +/** + * Maximum value for a SourceId. Indicates invalid entries. + */ +constexpr SourceId InvalidSourceId = std::numeric_limits::max(); + /** * Type used for specifying an offset within a source file. */ @@ -49,7 +54,7 @@ using SourceOffset = uint32_t; * integer, the maximum value is 2^32-1, which means that 4 GiB are addressable * by SourceOffset. */ -constexpr SourceOffset SourceOffsetMax = +constexpr SourceOffset InvalidSourceOffset = std::numeric_limits::max(); /** @@ -63,7 +68,7 @@ constexpr SourceOffset SourceOffsetMax = */ inline SourceOffset clampToSourcePosition(size_t pos) { - return pos > SourceOffsetMax ? SourceOffsetMax : pos; + return pos > InvalidSourceOffset ? InvalidSourceOffset : pos; } /** @@ -79,9 +84,9 @@ private: public: /** * Default constructor of the SourcePosition class. Sets the position to - * SourceOffsetMax and thus marks the SourcePosition as invalid. + * InvalidSourceOffset and thus marks the SourcePosition as invalid. */ - SourcePosition() : pos(SourceOffsetMax) {} + SourcePosition() : pos(InvalidSourceOffset) {} /** * Creates a new SourcePosition instance with the given byte offset. @@ -109,7 +114,7 @@ public: * * @return true if the SourcePosition instance is value, false otherwise. */ - bool isValid() { return pos != SourceOffsetMax; } + bool isValid() { return pos != InvalidSourceOffset; } }; /** @@ -261,22 +266,25 @@ private: /** * Id of the source file. */ - SourceId sourceId; + SourceId sourceId = InvalidSourceId; public: + /** + * Default constructor. + */ + SourceLocation(){}; + /** * Constructor, binds the SourceLocation to the given source file. * - * @param sourceId is the sourceId to which the SourceLocation instance - * should be bound. The sourceId cannot be overriden after construction. + * @param sourceId specifies the file this location refers to. */ SourceLocation(SourceId sourceId) : sourceId(sourceId){}; /** * Constructor for a zero-length range. * - * @param sourceId is the sourceId to which the SourceLocation instance - * should be bound. The sourceId cannot be overriden after construction. + * @param sourceId specifies the file this location refers to. * @param pos is the byte offset at which the SourceRange instance should be * located. */ @@ -288,8 +296,7 @@ public: /** * Constructor of a SourceRange instance. * - * @param sourceId is the sourceId to which the SourceLocation instance - * should be bound. The sourceId cannot be overriden after construction. + * @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). @@ -302,8 +309,7 @@ public: /** * Constructor of a SourceRange instance. * - * @param sourceId is the sourceId to which the SourceLocation instance - * should be bound. The sourceId cannot be overriden after construction. + * @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). @@ -313,6 +319,13 @@ public: { } + /** + * 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. @@ -320,6 +333,17 @@ public: * @return the id of the source file this instance is bound to. */ SourceId getSourceId() { 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() + { + return SourceRange::isValid() && sourceId != InvalidSourceId; + } }; /** -- cgit v1.2.3 From 17f4fb572a0493679342077c36ff8eb3116cb222 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 00:32:39 +0100 Subject: Atatching Location to Node --- src/core/model/Node.hpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src') diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp index c5761a8..af92a50 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -136,6 +137,12 @@ private: */ Owned 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() { 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;} }; /** -- cgit v1.2.3 From edc7d6cd129e4e9dcbe3e2a33a7a31dcd4eaf998 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 00:33:47 +0100 Subject: Added ResourceUtils class containing helper functions for deducing resource types. --- CMakeLists.txt | 1 + src/core/resource/ResourceUtils.cpp | 137 ++++++++++++++++++++++++++++++++++++ src/core/resource/ResourceUtils.hpp | 130 ++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 src/core/resource/ResourceUtils.cpp create mode 100644 src/core/resource/ResourceUtils.hpp (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index bcba476..a56eb7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ ADD_LIBRARY(ousia_core src/core/parser/ParserStack src/core/resource/Resource src/core/resource/ResourceLocator + src/core/resource/ResourceUtils # src/core/script/ScriptEngine ) diff --git a/src/core/resource/ResourceUtils.cpp b/src/core/resource/ResourceUtils.cpp new file mode 100644 index 0000000..26faaad --- /dev/null +++ b/src/core/resource/ResourceUtils.cpp @@ -0,0 +1,137 @@ +/* + 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 . +*/ + +#include +#include + +#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 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 RelRttiTypeMap{ + {"document", &RttiTypes::DOCUMENT}, + {"domain", &RttiTypes::DOMAIN}, + {"typesystem", &RttiTypes::TYPESYSTEM}}; + +/** + * Map mapping from Rtti pointers to the corresponding ResourceType. + */ +static const std::unordered_map 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::toLowercase(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[0]); + if (it != RelResourceTypeMap.end()) { + return it->second; + } + } + return ResourceType::UNKNOWN; +} + +const Rtti *ResourceUtils::deduceRttiType(const std::string &rel) +{ + std::string s = Utils::toLowercase(rel); + if (!s.empty()) { + auto it = RelRttiTypeMap.find(s); + if (it != RelRttiTypeMap.end()) { + return it->second; + } + } + return &ResourceType::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..8796e06 --- /dev/null +++ b/src/core/resource/ResourceUtils.hpp @@ -0,0 +1,130 @@ +/* + 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 . +*/ + +/** + * @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 + +#include "Resource.hpp" + +namespace ousia { + +// Forward declarations +class Rtti; +class RttiSet; + +/** + * 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_ */ + -- cgit v1.2.3 From c7aab61b7cf2b824f168507d9a693b375df0515b Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 00:34:01 +0100 Subject: Improved SourceLocation --- src/core/common/Location.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/core/common/Location.hpp b/src/core/common/Location.hpp index ae50e91..4ce01a8 100644 --- a/src/core/common/Location.hpp +++ b/src/core/common/Location.hpp @@ -266,13 +266,13 @@ private: /** * Id of the source file. */ - SourceId sourceId = InvalidSourceId; + SourceId sourceId; public: /** * Default constructor. */ - SourceLocation(){}; + SourceLocation() : sourceId(InvalidSourceId) {}; /** * Constructor, binds the SourceLocation to the given source file. -- cgit v1.2.3 From 748a44fba54acb936249d13f69231c85f07a1069 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 00:34:18 +0100 Subject: Implemented most functions in ResourceManager --- CMakeLists.txt | 1 + src/core/resource/ResourceManager.cpp | 294 ++++++++++++++++++---------------- src/core/resource/ResourceManager.hpp | 146 +++++++++++++---- 3 files changed, 276 insertions(+), 165 deletions(-) (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index a56eb7f..ad9c0ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ ADD_LIBRARY(ousia_core src/core/parser/ParserStack src/core/resource/Resource src/core/resource/ResourceLocator + src/core/resource/ResourceManager src/core/resource/ResourceUtils # src/core/script/ScriptEngine ) diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp index 563fc12..4a42609 100644 --- a/src/core/resource/ResourceManager.cpp +++ b/src/core/resource/ResourceManager.cpp @@ -16,6 +16,9 @@ along with this program. If not, see . */ +#include + +#include #include #include #include @@ -23,128 +26,182 @@ #include #include -#include "Resource.hpp" #include "ResourceManager.hpp" +#include "ResourceUtils.hpp" namespace ousia { -/* Deduction of the ResourceType */ - -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 RelResourceTypeMap{ - {"document", ResourceType::DOCUMENT}, - {"domain", ResourceType::DOMAIN_DESC}, - {"typesystem", ResourceType::TYPESYSTEM}}; - -/** - * Map mapping from Rtti pointers to the corresponding ResourceType. - */ -static const std::unordered_map RttiResourceTypeMap{ - {&RttiTypes::Document, ResourceType::DOCUMENT}, - {&RttiTypes::Domain, ResourceType::DOMAIN_DESC}, - {&RttiTypes::Typesystem, ResourceType::TYPESYSTEM}}; - -static ResourceType relToResourceType(const std::string &rel, Logger &logger) +/* Static helper functions */ + +static bool typeSetsIntersect(const RttiSet &s1, const RttiSet &s2) { - std::string s = Utils::toLowercase(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("\"")); + for (const Rtti *t1 : s1) { + if (t1->isOneOf(s2)) { + return true; } } - return ResourceType::UNKNOWN; + return false; } -static ResourceType supportedTypesToResourceType(const RttiSet &supportedTypes) +static void logUnsopportedType(Logger &logger, const RttiSet &supportedTypes) { - if (supportedTypes.size() == 1U) { - auto it = RttiResourceTypeMap.find(supportedTypes[0]); - if (it != RelResourceTypeMap.end()) { - return it->second; - } + // Build a list containing the expected type names + std::vector expected; + for (const Rtti *supportedType : supportedTypes) { + expected.push_back(supportedType->name); } - return ResourceType::UNKNOWN; + + // Log the actual error message + ctx.logger.error( + std::string("Expected the file \"") + resource.location + + std::string("\" to define one of the following internal types ") + + Utils::join(expected, ", ", "{", "}")); } -static ResourceType deduceResourceType(const std::string &rel, - const RttiSet &supportedTypes, - Logger &logger) +/* Class ResourceManager */ + +SourceId ResourceManager::allocateSourceId(const Resource &resource) { - ResourceType res; + // 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; +} - // Try to deduce the ResourceType from the "rel" attribute - res = relToResourceType(rel, logger); +void ResourceManager::storeNode(SourceId sourceId, Handle node) +{ + nodes[sourceId] = node->getUid(); +} - // If this did not work, try to deduce the ResourceType from the - // supportedTypes supplied by the Parser instance. - if (res == ResourceType::UNKNOWN) { - res = supportedTypesToResourceType(supportedTypes); +void ResourceManager::purgeResource(SourceId sourceId) +{ + Resource res = getResource(sourceId); + if (res.isValid()) { + locations.erase(res.getLocation()); } - if (res == ResourceType::UNKNOWN) { - logger.note( - "Ambigous resource type, consider specifying the \"rel\" " - "attribute"); + resources.erase(sourceId); + nodes.erase(sourceId); + lineNumberCache.erase(sourceId); +} + +Rooted 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 \"") + path + + std::string( + "\" has an unknown file extension. Explicitly " + "specify a mimetype.")); + return nullptr; + } } - return res; + + // Fetch a parser for the mimetype + const std::pair &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 (!typeSetsIntersect(parserDescr.second, supportedTypes)) { + logUnsopportedType(ctx.logger, 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; + try { + CharReader reader(resource.stream(), sourceId); + 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; } -/* Functions for reducing the set of supported types */ +SourceId ResourceManager::getSourceId(const std::string &location) +{ + auto it = locations.find(location); + if (it != locations.end()) { + return it->second; + } + return InvalidSourceId; +} -/** - * Map mapping from relations (the "rel" attribute in includes) to the - * corresponding RttiType - */ -static const std::unordered_map RelRttiTypeMap{ - {"document", &RttiTypes::DOCUMENT}, - {"domain", &RttiTypes::DOMAIN}, - {"typesystem", &RttiTypes::TYPESYSTEM}}; +SourceId ResourceManager::getSourceId(const Resource &resource) +{ + if (resource.isValid()) { + return getSourceId(resource.getLocation()); + } + return InvalidSourceId; +} -static Rtti *relToRttiType(const std::string &rel) +const Resource &ResourceManager::getResource(SourceId sourceId) const { - std::string s = Utils::toLowercase(rel); - if (!s.empty()) { - auto it = RelRttiTypeMap.find(s); - if (it != RelRttiTypeMap.end()) { - return it->second; - } + auto it = resources.find(sourceId); + if (it != resourced.end()) { + return it->second; } - return &ResourceType::Node; + return NullResource; } -static RttiType shrinkSupportedTypes(const RttiSet &supportedTypes, - const std::string &rel) +Rooted ResourceManager::getNode(Manager &mgr, SourceId sourceId) { - RttiSet types; - RttiType *type = relToRttiType(rel); - for (RttiType *supportedType : supportedTypes) { - if (supportedType->isa(type)) { - types.insert(supportedType); + auto it = nodes.find(sourceId); + if (it != nodes.end()) { + Managed *managed = mgr.getManaged(sourceId); + if (managed != nullptr) { + return dynamic_cast(managed); + } else { + purgeResource(sourceId); } } - return types; + return nullptr; } -/* Class ResourceManager */ +Rooted ResourceManager::getNode(Manager &mgr, const std::string &location) +{ + return getNode(mgr, getSourceId(location)); +} -Rooted ResourceManager::link(ParserContext &ctx, Resource &resource, - const std::string &mimetype, - const RttiSet &supportedTypes) +Rooted ResourceManager::getNode(Manager &mgr, const Resource &resource) { - + return getNode(mgr, getSourceId(resource)); } Rooted ResourceManager::link(ParserContext &ctx, const std::string &path, @@ -155,7 +212,7 @@ Rooted ResourceManager::link(ParserContext &ctx, const std::string &path, { // Try to deduce the ResourceType ResourceType resourceType = - deduceResourceType(rel, supportedTypes, ctx.logger); + ResourceUtils::deduceResourceType(rel, supportedTypes, ctx.logger); // Lookup the resource for given path and resource type Resource resource; @@ -166,50 +223,27 @@ Rooted ResourceManager::link(ParserContext &ctx, const std::string &path, } // Try to shrink the set of supportedTypes - RttiSet types = shrinkSupportedTypes(supportedTypes, rel); + RttiSet types = ResourceUtils::limitRttiTypes(supportedTypes, rel); // Check whether the resource has already been parsed - Rooted node = nullptr; - auto it = locations.find(res.getLocation()); - if (it != locations.end()) { - node = - } - = link(ctx, resource, mimetype, types); + Rooted node = getNode(ctx.manager, resource); + if (node == nullptr) { + // Node has not already been parsed, parse it now + node = parse(ctx, resource, mimetype, supportedTypes); - // Try to deduce the mimetype - std::string mime = mimetype; - if (mime.empty()) { - // Fetch the file extension - std::string ext = Utils::extractFileExtension(path); - if (ext.empty()) { - ctx.logger.error( - std::string("Specified filename \"") + path + - std::string( - "\" has no extension and no mimetype (\"type\" " - "attribute) was given instead.")); - return nullptr; - } - - // Fetch the mimetype for the extension - mime = ctx.registry.getMimetypeForExtension(ext); - if (mime.empty()) { - ctx.logger.error(std::string("Unknown file extension \"") + ext + - std::string("\"")); + // Abort if parsing failed + if (node == nullptr) { return nullptr; } } - // Fetch a parser for the mimetype - const std::pair parser = - ctx.registry.getParserForMimetype(mime); - - // Make sure a parser was found - if (!parser->first) { - ctx.logger.error(std::string("Cannot parse files of type \"") + mime + - std::string("\"")); + // Make sure the node has one of the supported types + if (!node->type().isOneOf(supportedTypes)) { + logUnsopportedType(ctx.logger, supportedTypes); + return nullptr; } - // Make sure the parser returns one of the supported types + return node; } Rooted ResourceManager::link(ParserContext &ctx, const std::string &path, @@ -229,14 +263,6 @@ Rooted ResourceManager::link(ParserContext &ctx, const std::string &path, return include(ctx, path, mimetype, rel, supportedTypes, relativeResource); } -const Resource &getResource(SourceId sourceId) const -{ - if (sourceId < resources.size()) { - return resources[sourceId]; - } - return NullResource; -} - SourceContext ResourceManager::buildContext(const SourceLocation &location) { SourceContext res; @@ -245,7 +271,7 @@ SourceContext ResourceManager::buildContext(const SourceLocation &location) return res; } -}; + } #endif /* _OUSIA_RESOURCE_MANAGER_HPP_ */ diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp index 809fd55..05dcc8e 100644 --- a/src/core/resource/ResourceManager.hpp +++ b/src/core/resource/ResourceManager.hpp @@ -35,12 +35,13 @@ #include #include +#include "Resource.hpp" + namespace ousia { // Forward declarations class Node; class ParserContext; -class Resource; class RttiSet; extern const Resource NullResource; @@ -52,19 +53,19 @@ extern const Resource NullResource; class ResourceManager { private: /** - * Vector used for mapping SourceId instances to the underlying resource. + * Next SourceId to be used. */ - std::vector resources; + SourceId nextSourceId = 0; /** - * Index pointing at the next free entry in the resources list. + * Map between Resource locations and the corresponding SourceId. */ - SourceId nextFreeSourceId = 0; + std::unordered_map locations; /** - * Map between Resource locations and the corresponding SourceId. + * Map used for mapping SourceId instances to the underlying resource. */ - std::unordered_map locations; + std::unordered_map resources; /** * Map between a SourceId and the corresponding (if available) parsed node @@ -79,22 +80,115 @@ private: */ std::unordered_map> 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); - Rooted getCachedNode(SourceId id); + /** + * 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); - Rooted getCachedNode(const std::string &location); + /** + * 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 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); /** - * Used internally to either parse a resource or retrieve it from the - * internal cache of already parsed resources. + * 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. */ - Rooted link(ParserContext &ctx, Resource &resource, const std::string &mimetype, - const RttiSet &supportedTypes); + 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 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 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 getNode(Manager &mgr, const Resource &resource); -public: /** * 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 @@ -107,10 +201,10 @@ public: * mimetype is given, the path must have an extension that is known by */ Rooted link(ParserContext &ctx, const std::string &path, - const std::string &mimetype = "", - const std::string &rel = "", - const RttiSet &supportedTypes = RttiSet{}, - const Resource &relativeTo = NullResource); + 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 @@ -118,19 +212,8 @@ public: * the logger instance of the given ParserContext. */ Rooted link(ParserContext &ctx, const std::string &path, - const std::string &mimetype, const std::string &rel, - const RttiSet &supportedTypes, SourceId relativeTo); - - /** - * 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; + const std::string &mimetype, const std::string &rel, + const RttiSet &supportedTypes, SourceId relativeTo); /** * Creates and returns a SourceContext structure containing information @@ -145,6 +228,7 @@ public: * invalid SourceContext if the location is invalid. */ SourceContext buildContext(const SourceLocation &location); + }; } -- cgit v1.2.3 From 5eaeae92ccf209194bced39d888c5a0e527a9c1a Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 01:25:55 +0100 Subject: Started to adapt CharReader --- src/core/common/CharReader.cpp | 209 +++++++---------------------------------- src/core/common/CharReader.hpp | 164 +++++++++----------------------- 2 files changed, 77 insertions(+), 296 deletions(-) (limited to 'src') diff --git a/src/core/common/CharReader.cpp b/src/core/common/CharReader.cpp index 6966b97..b0bbade 100644 --- a/src/core/common/CharReader.cpp +++ b/src/core/common/CharReader.cpp @@ -376,44 +376,33 @@ bool Buffer::fetch(CursorId cursor, char &c) return fetchCharacter(cursor, c, false); } -/* CharReader::Cursor class */ - -void CharReader::Cursor::assign(std::shared_ptr 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, size_t line, - size_t column) +CharReader::CharReader(std::shared_ptr 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{new Buffer{str}}, line, column) +CharReader::CharReader(const std::string &str, SourceId sourceId, size_t offs) + : CharReader(std::shared_ptr{new Buffer{str}}, sourceId, offs) { } -CharReader::CharReader(std::istream &istream, size_t line, size_t column) - : CharReader(std::shared_ptr{new Buffer{istream}}, line, column) +CharReader::CharReader(std::istream &istream, SourceId sourceId, size_t offs) + : CharReader(std::shared_ptr{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) @@ -437,18 +426,6 @@ bool CharReader::readAtCursor(Cursor &cursor, char &c) } } } - - // 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,38 @@ 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--; - } - } - - // 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}; -} - -SourceContext CharReader::getContextAtOffs(ssize_t maxSize, size_t offs) -{ - // 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; -} +bool CharReader::atEnd() const { return buffer->atEnd(readCursor.cursor); } -SourceContext CharReader::getContext(ssize_t maxSize) +size_t CharReader::getOffset() const { - return getContextAt(maxSize, readCursor.cursor); + return buffer->offset(readCursor.cursor) + offs; } -SourceContext CharReader::contextCallback(const SourceLocation &location, - void *data) +SourceLocation CharReader::getLocation() const { - return static_cast(data)->getContextAtOffs(60, location.offs); + return SourceLocation{sourceId, getOffset()}; } /* Class CharReaderFork */ CharReaderFork::CharReaderFork(std::shared_ptr buffer, - CharReader::Cursor &parentReadCursor, - CharReader::Cursor &parentPeekCursor, + Buffer::CursorId parentReadCursor, + Buffer::CursorId parentPeekCursor, + SourceContextCallback sourceId, size_t offs, bool coherent) - : CharReader(buffer, 1, 1), + : 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..0957e97 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, Cursor &cursor); - }; - private: /** * Substitutes "\r", "\n\r", "\r\n" with a single "\n". @@ -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 @@ -468,34 +402,51 @@ 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, size_t line, size_t column); + CharReader(std::shared_ptr 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,27 @@ 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. - */ - 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; } - - /** - * Returns the column the read cursor currently is at. - */ - int getColumn() const { return readCursor.column; } - - /** - * Returns the current position of the read cursor (line and column). - */ - SourceLocation getLocation() const - { - return SourceLocation(getLine(), getColumn(), getOffset()); - } - - /** - * Returns the line the read 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 + * @return the offset of the read cursor in bytes. */ - SourceContext getContext(ssize_t maxSize = 60); + size_t getOffset() 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 a SourceLocation object describing the exact position (including + * the source file) of the read cursor. * - * @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 a SourceLocation object at the position of the current read + * cursor. */ - static SourceContext contextCallback(const SourceLocation &location, - void *data); + SourceLocation getLocation() const; }; /** @@ -637,12 +559,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 +572,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, - CharReader::Cursor &parentReadCursor, - CharReader::Cursor &parentPeekCursor, bool coherent); + Buffer::CursorId &parentReadCursor, + Buffer::CursorId &parentPeekCursor, + SourceContextCallback sourceId, size_t offs, bool coherent); public: /** -- cgit v1.2.3 From 45a3d085b86e4d761a30718d05be40d4640ba63f Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 12:08:15 +0100 Subject: Adapted LoggableException according to new SourceLocation class. --- src/core/common/Exceptions.cpp | 16 ---------------- src/core/common/Exceptions.hpp | 26 +++++++++++++++----------- 2 files changed, 15 insertions(+), 27 deletions(-) (limited to 'src') 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,14 +120,26 @@ 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 - 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 + LoggableException(std::string msg, const LocationType *loc) + : LoggableException(std::move(msg), loc->getLocation()) + { + } + /** * Returns the position at which the exception occured in the text. * -- cgit v1.2.3 From 6dbb4d19a860937ec1c78df01b1371272e1de8de Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 12:08:42 +0100 Subject: Improved SourceLocation class. --- src/core/common/Location.cpp | 9 +++++++++ src/core/common/Location.hpp | 29 +++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/core/common/Location.cpp b/src/core/common/Location.cpp index 6f9250a..7a75347 100644 --- a/src/core/common/Location.cpp +++ b/src/core/common/Location.cpp @@ -19,5 +19,14 @@ #include "Location.hpp" namespace ousia { + +/* Global Functions */ + +const SourceLocation NullSourceLocation; + +void SourceContext NullSourceContextCallback(const SourceLocation &location) +{ + return SourceContext{}; +} } diff --git a/src/core/common/Location.hpp b/src/core/common/Location.hpp index 4ce01a8..57892cc 100644 --- a/src/core/common/Location.hpp +++ b/src/core/common/Location.hpp @@ -29,6 +29,7 @@ #define _OUSIA_LOCATION_HPP_ #include +#include #include #include @@ -272,7 +273,7 @@ public: /** * Default constructor. */ - SourceLocation() : sourceId(InvalidSourceId) {}; + SourceLocation() : sourceId(InvalidSourceId){}; /** * Constructor, binds the SourceLocation to the given source file. @@ -346,6 +347,11 @@ public: } }; +/** + * NullSourceLocation is an empty SourceLocation instance. + */ +extern const SourceLocation NullSourceLocation; + /** * Represents the context of a SourceLocation instance. Used to build error * messages. @@ -435,6 +441,11 @@ struct SourceContext { */ bool isValid() const { return range.isValid() && hasLine() && hasColumn(); } + /** + * 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. * @@ -455,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 = SourceContext (*)(const SourceLocation &location, - void *data); +using SourceContextCallback = + std::function; + +/** + * 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. + */ +void SourceContext NullSourceContextCallback(const SourceLocation &location); + } #endif /* _OUSIA_LOCATION_HPP_ */ -- cgit v1.2.3 From 7610f2d1bc6c7b263ef41e765d571ac460709a7e Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 12:09:05 +0100 Subject: Made getLocation const --- src/core/model/Node.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp index af92a50..6fc7dba 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -532,7 +532,7 @@ public: * * @return a source location descriptor. */ - SourceLocation getLocation() { return location; } + SourceLocation getLocation() const { return location; } /** * Sets the location of the node to the given value. -- cgit v1.2.3 From a7e40c1f1572b05e9608e0ed43ac54c1f7e7d84b Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 12:09:14 +0100 Subject: Adapted Logger class --- src/core/common/Logger.cpp | 168 +++++++++++------ src/core/common/Logger.hpp | 447 +++++++++++++++++++++++---------------------- 2 files changed, 336 insertions(+), 279 deletions(-) (limited to 'src') diff --git a/src/core/common/Logger.cpp b/src/core/common/Logger.cpp index fa4b5c8..9f070f9 100644 --- a/src/core/common/Logger.cpp +++ b/src/core/common/Logger.cpp @@ -27,10 +27,11 @@ namespace ousia { /* Class Logger */ void Logger::log(Severity severity, const std::string &msg, - const SourceLocation &loc) + const SourceLocation &loc, + MessageMode mode = MessageMode::DEFAULT) { // Assemble the message and pass it through the filter, then process it - Message message { severity, std::move(msg), loc }; + Message message{severity, std::move(msg), loc, mode}; if (filterMessage(message)) { processMessage(message); } @@ -42,30 +43,37 @@ 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) { - locations.back() = loc; + calls.emplace_back(CallType : SET_LOCATION, locations.size()); + locations.push_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() = loc; } else { - calls.push_back(Call(CallType::SET_DEFAULT_LOCATION, locations.size())); - locations.push_back(loc); + calls.emplace_back(CallType::SET_CONTEXT_CALLBACK, callbacks.size()); + callbacks.emplace_back(sourceContextCallback); } } @@ -73,45 +81,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 = SourceLocation{}) + : Logger(), 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.processSetContextCallback(sourceContextCallback); } -void ConcreteLogger::processPopFile() { files.pop_back(); } +/* Class ConcreteLogger */ + +ConcreteLogger(Severity minSeverity = Severity::DEFAULT_MIN_SEVERITY) + : minSeverity(minSeverity), sourceContextCallback(NullSourceContextCallback) +{ +} bool ConcreteLogger::filterMessage(const Message &msg) { @@ -126,40 +180,38 @@ bool ConcreteLogger::filterMessage(const Message &msg) return sev >= static_cast(minSeverity); } -void ConcreteLogger::processSetDefaultLocation(const SourceLocation &loc) -{ - defaultLocation = loc; +void ConcreteLogger::processPushDefaultLocation(const SourceLocation &loc) { + locations.emplace_back(loc); } -const Logger::File &ConcreteLogger::currentFile() const -{ - if (!files.empty()) { - return files.back(); +void ConcreteLogger::processPopDefaultLocation() { + if (!locations.empty()) { + locations.pop_back(); } - return EMPTY_FILE; } -const std::string &ConcreteLogger::currentFilename() const +void ConcreteLogger::processSetDefaultLocation(const SourceLocation &loc) { - return currentFile().file; + if (!locations.empty()) { + locations.back() = loc; + } else { + locations.emplace_back(loc); + } } const SourceLocation &ConcreteLogger::messageLocation(const Message &msg) const { if (msg.loc.valid()) { return msg.loc; + } else if (!locatios.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() @@ -193,6 +245,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 +257,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 (ctx.hasLine()) { if (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,7 +303,7 @@ void TerminalLogger::processMessage(const Message &msg) os << msg.msg << std::endl; // Print the error message context if available - if (ctx.valid()) { +/* if (ctx.valid()) { size_t relPos = ctx.relPos; if (ctx.truncatedStart) { os << "[...] "; @@ -272,7 +326,7 @@ void TerminalLogger::processMessage(const Message &msg) } } 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..092bd3a 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(static_cast(a) | + static_cast(b)); +} + // Forward declaration class LoggerFork; class ScopedLogger; @@ -91,51 +125,6 @@ public: friend LoggerFork; 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. @@ -146,6 +135,11 @@ public: */ Severity severity; + /** + * Message mode. + */ + MessageMode mode; + /** * Actual log message. */ @@ -156,16 +150,52 @@ 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 + 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 + 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) - { - 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) + void log(const LoggableException &ex, + const SourceLocation &loc = SourceLocation{}, + MessageMode mode = MessageMode::DEFAULT) { - 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 - 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 - 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 - 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 - 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 - 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 - 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. @@ -437,12 +491,6 @@ public: processSetDefaultLocation(loc); } - /** - * 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 messages; /** - * Vector storing all incomming pushed Scope instances. + * Vector storing all incomming location instances. */ - std::vector files; + std::vector locations; /** - * Vector storing all incomming location instances. + * Vector storing all incomming source context callbacks. */ - std::vector locations; + std::vector callbacks; /** * Parent logger instance. @@ -529,9 +583,11 @@ 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 @@ -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,24 +718,29 @@ constexpr Severity DEFAULT_MIN_SEVERITY = Severity::DEBUG; class ConcreteLogger : public Logger { private: /** - * Stack containing the current file instance. + * Current source context callback. */ - std::vector files; + SourceContextCallback sourceContextCallback; /** * Vector used to store the counts of each message type. */ std::vector messageCounts; + /** + * Vector used to store the current default locations. + */ + std::vector locations; + /** * Minimum severity to be used for filtering messages. */ Severity minSeverity; /** - * Current default location. + * Current source context callback. */ - SourceLocation defaultLocation; + SourceContextCallback callback; protected: /** @@ -730,25 +752,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 +765,7 @@ public: * @param minSeverity is the severity below which message should be * discarded. */ - ConcreteLogger(Severity minSeverity = DEFAULT_MIN_SEVERITY) - : minSeverity(minSeverity) - { - } - - /** - * Returns the name of the current file or an empty instance of the File - * instance if no current file is available. - * - * @return the name of the current file. - */ - const File ¤tFile() const; - - /** - * Returns the current filename or an empty string if no surch file is - * available. - */ - const std::string ¤tFilename() const; + ConcreteLogger(Severity minSeverity = Severity::DEFAULT_MIN_SEVERITY); /** * Returns the current cursor location. @@ -800,7 +791,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 +800,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 +852,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, -- cgit v1.2.3 From 6c1007fc979da229ba2b9794fcb20a29e2edebc3 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:25:46 +0100 Subject: Addapted Tokenizer to new SourceLocation --- src/core/CodeTokenizer.cpp | 32 +++++++++++++++----------------- src/core/Tokenizer.cpp | 19 ++++++------------- src/core/Tokenizer.hpp | 13 +++---------- 3 files changed, 24 insertions(+), 40 deletions(-) (limited to 'src') 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 &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 &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 &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/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) { } -- cgit v1.2.3 From b0ab6e4cb077af892046e1fa1504d08e5b66deaf Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:25:58 +0100 Subject: Fixed typos --- src/core/Registry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/core/Registry.cpp b/src/core/Registry.cpp index 044668c..4aad7db 100644 --- a/src/core/Registry.cpp +++ b/src/core/Registry.cpp @@ -100,13 +100,13 @@ std::string Registry::getMimetypeForExtension( std::string Registry::getMimetypeForFilename(const std::string &filename) const { // Fetch the file extension - std::string ext = Utils::extractFileExtension(path); + std::string ext = Utils::extractFileExtension(filename); if (ext.empty()) { return std::string{}; } // Fetch the mimetype for the extension - return ctx.registry.getMimetypeForExtension(ext); + return getMimetypeForExtension(ext); } void Registry::registerResourceLocator(ResourceLocator *locator) -- cgit v1.2.3 From d4457c98def55d694abc51e008d3fe5663768aab Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:26:40 +0100 Subject: Improved and fixed CharReader --- src/core/common/CharReader.cpp | 33 ++++++++++++++++++++++----------- src/core/common/CharReader.hpp | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/core/common/CharReader.cpp b/src/core/common/CharReader.cpp index b0bbade..6fd3d45 100644 --- a/src/core/common/CharReader.cpp +++ b/src/core/common/CharReader.cpp @@ -405,10 +405,10 @@ CharReader::~CharReader() 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; } @@ -420,9 +420,9 @@ 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); } } } @@ -486,8 +486,8 @@ bool CharReader::consumeWhitespace() CharReaderFork CharReader::fork() { - return CharReaderFork(buffer, readCursor, peekCursor, sourceId, offs, - coherent); + return CharReaderFork{buffer, readCursor, peekCursor, + sourceId, offs, coherent}; } size_t CharReader::readRaw(char *buf, size_t size) @@ -502,11 +502,16 @@ size_t CharReader::readRaw(char *buf, size_t size) return res; } -bool CharReader::atEnd() const { return buffer->atEnd(readCursor.cursor); } +bool CharReader::atEnd() const { return buffer->atEnd(readCursor); } -size_t CharReader::getOffset() const +SourceOffset CharReader::getOffset() const { - return buffer->offset(readCursor.cursor) + offs; + return buffer->offset(readCursor) + offs; +} + +SourcePosition CharReader::getPosition() const +{ + return getOffset(); } SourceLocation CharReader::getLocation() const @@ -514,13 +519,19 @@ SourceLocation CharReader::getLocation() const return SourceLocation{sourceId, getOffset()}; } +SourceLocation CharReader::getLocation(SourcePosition start) const +{ + return SourceLocation{sourceId, start, getOffset()}; +} + +SourceId CharReader::getSourceId() const { return sourceId; } + /* Class CharReaderFork */ CharReaderFork::CharReaderFork(std::shared_ptr buffer, Buffer::CursorId parentReadCursor, Buffer::CursorId parentPeekCursor, - SourceContextCallback sourceId, size_t offs, - bool coherent) + SourceId sourceId, size_t offs, bool coherent) : CharReader(buffer, sourceId, offs), parentReadCursor(parentReadCursor), parentPeekCursor(parentPeekCursor) diff --git a/src/core/common/CharReader.hpp b/src/core/common/CharReader.hpp index 0957e97..5a4d906 100644 --- a/src/core/common/CharReader.hpp +++ b/src/core/common/CharReader.hpp @@ -367,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. @@ -534,7 +534,14 @@ public: * * @return the offset of the read cursor in bytes. */ - size_t getOffset() const; + SourceOffset getOffset() const; + + /** + * Returns the offset of the read cursor in bytes. + * + * @return the offset of the read cursor in bytes. + */ + SourcePosition getPosition() const; /** * Returns a SourceLocation object describing the exact position (including @@ -544,6 +551,24 @@ public: * cursor. */ SourceLocation getLocation() const; + + /** + * Returns a SourceLocation object starting at the given start position and + * ending at 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(SourcePosition start) const; + + /** + * Returns the current SourceId which describes the Resource on which the + * CharReader is currently working. + * + * @return the current SourceId. + */ + SourceId getSourceId() const; }; /** @@ -577,9 +602,9 @@ private: * coherently. */ CharReaderFork(std::shared_ptr buffer, - Buffer::CursorId &parentReadCursor, - Buffer::CursorId &parentPeekCursor, - SourceContextCallback sourceId, size_t offs, bool coherent); + Buffer::CursorId parentReadCursor, + Buffer::CursorId parentPeekCursor, SourceId sourceId, + size_t offs, bool coherent); public: /** -- cgit v1.2.3 From 9e084e8e7f657112436c3e07f12a2319373b1909 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:26:47 +0100 Subject: typo --- src/core/common/Location.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/common/Location.cpp b/src/core/common/Location.cpp index 7a75347..7fe5ea5 100644 --- a/src/core/common/Location.cpp +++ b/src/core/common/Location.cpp @@ -24,9 +24,10 @@ namespace ousia { const SourceLocation NullSourceLocation; -void SourceContext NullSourceContextCallback(const SourceLocation &location) +SourceContext NullSourceContextCallback(const SourceLocation &location) { return SourceContext{}; } + } -- cgit v1.2.3 From 1131b68bdf2949f56422a2c53617a5f7d2d9be47 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:28:22 +0100 Subject: Fixed typos, autoformat and setIsOneOf method --- src/core/common/Rtti.cpp | 32 +++++++++++++++++++++++--------- src/core/common/Rtti.hpp | 10 ++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/core/common/Rtti.cpp b/src/core/common/Rtti.cpp index 668e418..a913d76 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) +RttiBuilderBase &RttiBuilderBase::genericMethod( + const std::string &name, std::shared_ptr 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,10 +126,10 @@ bool Rtti::isa(const Rtti &other) const return parents.count(&other) > 0; } -bool Rtii::isOneOf(const RttiSet &others) const +bool Rtti::isOneOf(const RttiSet &others) const { initialize(); - for (const Rtti *other: others) { + for (const Rtti *other : others) { if (parents.count(other) > 0) { return true; } @@ -136,18 +137,30 @@ bool Rtii::isOneOf(const RttiSet &others) const 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; } @@ -162,7 +175,8 @@ std::shared_ptr Rtti::getMethod(const std::string &name) const return it->second; } -std::shared_ptr Rtti::getProperty(const std::string &name) const +std::shared_ptr 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 53043e2..e2f1fa2 100644 --- a/src/core/common/Rtti.hpp +++ b/src/core/common/Rtti.hpp @@ -392,6 +392,16 @@ public: */ 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 -- cgit v1.2.3 From 2fbb15a2d80b878af1919fea7331c945d6ab43a0 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:28:39 +0100 Subject: replaced nonexistant is_file with is_regular_file --- src/plugins/filesystem/FileLocator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/plugins/filesystem/FileLocator.cpp b/src/plugins/filesystem/FileLocator.cpp index 6804c6d..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) && fs::is_file(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 " @@ -159,7 +159,7 @@ bool FileLocator::doLocate(Resource &resource, const std::string &path, #endif fs::path p{*it}; p /= path; - if (fs::exists(p) && fs::is_file(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 " -- cgit v1.2.3 From 6dad2a341e5d0c55680ecec817b9b13893511031 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:29:08 +0100 Subject: Forward declaration of RttiSet does not work --- src/core/resource/ResourceManager.hpp | 2 +- src/core/resource/ResourceUtils.hpp | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp index 05dcc8e..51c00e3 100644 --- a/src/core/resource/ResourceManager.hpp +++ b/src/core/resource/ResourceManager.hpp @@ -33,6 +33,7 @@ #include #include +#include #include #include "Resource.hpp" @@ -42,7 +43,6 @@ namespace ousia { // Forward declarations class Node; class ParserContext; -class RttiSet; extern const Resource NullResource; /** diff --git a/src/core/resource/ResourceUtils.hpp b/src/core/resource/ResourceUtils.hpp index 8796e06..13f9251 100644 --- a/src/core/resource/ResourceUtils.hpp +++ b/src/core/resource/ResourceUtils.hpp @@ -30,14 +30,12 @@ #include +#include + #include "Resource.hpp" namespace ousia { -// Forward declarations -class Rtti; -class RttiSet; - /** * Class containing static utility functions for dealing with Resources and * ResourceTypes. -- cgit v1.2.3 From 01edb481fe5919ffde18b9081a0de1a3f94b317c Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:29:53 +0100 Subject: Compiles now --- src/core/model/Project.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/core/model/Project.cpp b/src/core/model/Project.cpp index 56a3af2..a0f1f08 100644 --- a/src/core/model/Project.cpp +++ b/src/core/model/Project.cpp @@ -30,9 +30,7 @@ namespace model { Project::Project(Manager &mgr) : Node(mgr), systemTypesystem(acquire(new SystemTypesystem(mgr))), - documents(this), - domains(this), - typesystems(this) + documents(this) { } @@ -54,7 +52,7 @@ Rooted Project::createTypesystem(const std::string &name) Rooted Project::createDocument(const std::string &name) { - return Rooted document{new Document(getManager(), name)}; + Rooted document{new Document(getManager(), name)}; addDocument(document); return document; } @@ -70,7 +68,7 @@ void Project::addDocument(Handle document) documents.push_back(document); } -const NodeVector &Project::getDocuments() { return documents; } +const NodeVector &Project::getDocuments() const { return documents; } } namespace RttiTypes { -- cgit v1.2.3 From 31873a51b21ca8d549c172f9e773818356021a55 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:30:07 +0100 Subject: Adapted XMLParser to new SourceLocation --- src/plugins/xml/XmlParser.cpp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index ef738d8..78d9df8 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -131,11 +131,12 @@ public: !(defaultValue.isObject() && defaultValue.asObject() == nullptr); Rooted structType = scope().getLeaf().cast(); - Rooted attribute = structType->createAttribute( - name, defaultValue, optional, logger()); + Rooted attribute = + structType->createAttribute(name, defaultValue, optional, logger()); // Try to resolve the type - scope().resolve(type, logger(), + scope().resolve( + type, logger(), [attribute](Handle type, Logger &logger) mutable { attribute->setType(type, logger); }, @@ -234,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(XML_GetUserData(p)); + XMLParserUserData *ud = + static_cast(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(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; } @@ -270,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(p); - syncLoggerPosition(parser); - ParserStack *stack = static_cast(XML_GetUserData(parser)); + + syncLoggerPosition(parser); stack->end(); } @@ -297,7 +305,10 @@ Rooted XmlParser::doParse(CharReader &reader, ParserContext &ctx) // 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); @@ -321,15 +332,14 @@ Rooted XmlParser::doParse(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 -- cgit v1.2.3 From 4aee189703a9305206094858165c67043c2e1953 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:30:37 +0100 Subject: Logger compiles now --- src/core/common/Logger.cpp | 94 ++++++++++++++++++++++++++-------------------- src/core/common/Logger.hpp | 11 ++---- 2 files changed, 57 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/core/common/Logger.cpp b/src/core/common/Logger.cpp index 9f070f9..034953d 100644 --- a/src/core/common/Logger.cpp +++ b/src/core/common/Logger.cpp @@ -27,11 +27,10 @@ namespace ousia { /* Class Logger */ void Logger::log(Severity severity, const std::string &msg, - const SourceLocation &loc, - MessageMode mode = MessageMode::DEFAULT) + const SourceLocation &loc, MessageMode mode) { // Assemble the message and pass it through the filter, then process it - Message message{severity, std::move(msg), loc, mode}; + Message message{severity, mode, std::move(msg), loc}; if (filterMessage(message)) { processMessage(message); } @@ -60,8 +59,14 @@ void LoggerFork::processPopDefaultLocation() void LoggerFork::processSetDefaultLocation(const SourceLocation &loc) { - calls.emplace_back(CallType : SET_LOCATION, locations.size()); - locations.push_back(loc); + // Check whether setDefaultLocation was called immediately before, if yes, + // simply override the data + if (!calls.empty() && calls.back().type == CallType::SET_LOCATION) { + locations.back() = loc; + } else { + calls.emplace_back(CallType::SET_LOCATION, locations.size()); + locations.emplace_back(loc); + } } void LoggerFork::processSetSourceContextCallback( @@ -70,7 +75,7 @@ void LoggerFork::processSetSourceContextCallback( // 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() = loc; + callbacks.back() = sourceContextCallback; } else { calls.emplace_back(CallType::SET_CONTEXT_CALLBACK, callbacks.size()); callbacks.emplace_back(sourceContextCallback); @@ -114,8 +119,8 @@ void LoggerFork::commit() /* Class ScopedLogger */ -ScopedLogger::ScopedLogger(Logger &parent, SourceLocation loc = SourceLocation{}) - : Logger(), parent(parent), depth(0) +ScopedLogger::ScopedLogger(Logger &parent, SourceLocation loc) + : parent(parent), depth(0) { pushDefaultLocation(loc); } @@ -157,12 +162,12 @@ void ScopedLogger::processSetDefaultLocation(const SourceLocation &loc) void ScopedLogger::processSetSourceContextCallback( SourceContextCallback sourceContextCallback) { - parent.processSetContextCallback(sourceContextCallback); + parent.processSetSourceContextCallback(sourceContextCallback); } /* Class ConcreteLogger */ -ConcreteLogger(Severity minSeverity = Severity::DEFAULT_MIN_SEVERITY) +ConcreteLogger::ConcreteLogger(Severity minSeverity) : minSeverity(minSeverity), sourceContextCallback(NullSourceContextCallback) { } @@ -180,11 +185,13 @@ bool ConcreteLogger::filterMessage(const Message &msg) return sev >= static_cast(minSeverity); } -void ConcreteLogger::processPushDefaultLocation(const SourceLocation &loc) { +void ConcreteLogger::processPushDefaultLocation(const SourceLocation &loc) +{ locations.emplace_back(loc); } -void ConcreteLogger::processPopDefaultLocation() { +void ConcreteLogger::processPopDefaultLocation() +{ if (!locations.empty()) { locations.pop_back(); } @@ -199,11 +206,17 @@ void ConcreteLogger::processSetDefaultLocation(const SourceLocation &loc) } } +void ConcreteLogger::processSetSourceContextCallback( + SourceContextCallback sourceContextCallback) +{ + this->sourceContextCallback = sourceContextCallback; +} + const SourceLocation &ConcreteLogger::messageLocation(const Message &msg) const { - if (msg.loc.valid()) { + if (msg.loc.isValid()) { return msg.loc; - } else if (!locatios.empty()) { + } else if (!locations.empty()) { return locations.back(); } return NullSourceLocation; @@ -235,8 +248,9 @@ size_t ConcreteLogger::getSeverityCount(Severity severity) void ConcreteLogger::reset() { - files.clear(); + locations.clear(); messageCounts.clear(); + sourceContextCallback = NullSourceContextCallback; } bool ConcreteLogger::hasError() @@ -266,7 +280,7 @@ void TerminalLogger::processMessage(const Message &msg) // Print line and column number if (ctx.hasLine()) { - if (hasFile) { + if (ctx.hasFile()) { os << ':'; } os << t.bright() << ctx.startLine << t.reset(); @@ -303,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 092bd3a..85b1bb1 100644 --- a/src/core/common/Logger.hpp +++ b/src/core/common/Logger.hpp @@ -594,8 +594,8 @@ public: 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)){}; /** @@ -717,11 +717,6 @@ constexpr Severity DEFAULT_MIN_SEVERITY = Severity::DEBUG; */ class ConcreteLogger : public Logger { private: - /** - * Current source context callback. - */ - SourceContextCallback sourceContextCallback; - /** * Vector used to store the counts of each message type. */ @@ -740,7 +735,7 @@ private: /** * Current source context callback. */ - SourceContextCallback callback; + SourceContextCallback sourceContextCallback; protected: /** @@ -765,7 +760,7 @@ public: * @param minSeverity is the severity below which message should be * discarded. */ - ConcreteLogger(Severity minSeverity = Severity::DEFAULT_MIN_SEVERITY); + ConcreteLogger(Severity minSeverity = DEFAULT_MIN_SEVERITY); /** * Returns the current cursor location. -- cgit v1.2.3 From a5e14d8304716289f7ede9834d063bb212f76e83 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:31:06 +0100 Subject: Ability to add user data to ParserStack (this is just a hack, there should be a better solution, e.g. to derive from ParserStack) --- src/core/parser/ParserStack.hpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/core/parser/ParserStack.hpp b/src/core/parser/ParserStack.hpp index 4af88f9..6296dff 100644 --- a/src/core/parser/ParserStack.hpp +++ b/src/core/parser/ParserStack.hpp @@ -321,6 +321,11 @@ private: */ std::stack 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 @@ -340,8 +345,9 @@ public: * corresponding HandlerDescriptor instances. */ ParserStack(ParserContext &ctx, - const std::multimap &handlers) - : ctx(ctx), handlers(handlers){}; + const std::multimap &handlers, + void *userData = nullptr) + : ctx(ctx), handlers(handlers), userData(userData){}; /** * Returns the state the ParserStack instance currently is in. @@ -419,6 +425,13 @@ 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; } }; } -- cgit v1.2.3 From ea55274d4d5a6066f73380352274d568caa2d079 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:31:34 +0100 Subject: Fixed ResourceManager and ResourceUtils --- src/core/resource/ResourceManager.cpp | 41 ++++++++++++++++------------------- src/core/resource/ResourceUtils.cpp | 25 +++++++++++---------- 2 files changed, 32 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp index 4a42609..f154c9c 100644 --- a/src/core/resource/ResourceManager.cpp +++ b/src/core/resource/ResourceManager.cpp @@ -18,12 +18,14 @@ #include +#include #include #include #include #include #include #include +#include #include #include "ResourceManager.hpp" @@ -33,17 +35,7 @@ namespace ousia { /* Static helper functions */ -static bool typeSetsIntersect(const RttiSet &s1, const RttiSet &s2) -{ - for (const Rtti *t1 : s1) { - if (t1->isOneOf(s2)) { - return true; - } - } - return false; -} - -static void logUnsopportedType(Logger &logger, const RttiSet &supportedTypes) +static void logUnsopportedType(Logger &logger, Resource &resource, const RttiSet &supportedTypes) { // Build a list containing the expected type names std::vector expected; @@ -52,8 +44,8 @@ static void logUnsopportedType(Logger &logger, const RttiSet &supportedTypes) } // Log the actual error message - ctx.logger.error( - std::string("Expected the file \"") + resource.location + + logger.error( + std::string("Expected the file \"") + resource.getLocation() + std::string("\" to define one of the following internal types ") + Utils::join(expected, ", ", "{", "}")); } @@ -101,7 +93,7 @@ Rooted ResourceManager::parse(ParserContext &ctx, Resource &resource, if (mime.empty()) { mime = ctx.registry.getMimetypeForFilename(resource.getLocation()); if (mime.empty()) { - ctx.logger.error(std::string("Filename \"") + path + + ctx.logger.error(std::string("Filename \"") + resource.getLocation() + std::string( "\" has an unknown file extension. Explicitly " "specify a mimetype.")); @@ -122,8 +114,8 @@ Rooted ResourceManager::parse(ParserContext &ctx, Resource &resource, } // Make sure the parser returns at least one of the supported types - if (!typeSetsIntersect(parserDescr.second, supportedTypes)) { - logUnsopportedType(ctx.logger, supportedTypes); + if (!Rtti::setIsOneOf(parserDescr.second, supportedTypes)) { + logUnsopportedType(ctx.logger, resource, supportedTypes); return nullptr; } @@ -133,7 +125,14 @@ Rooted ResourceManager::parse(ParserContext &ctx, Resource &resource, // We can now try to parse the given file Rooted node; try { - CharReader reader(resource.stream(), sourceId); + // 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 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."}; @@ -174,7 +173,7 @@ SourceId ResourceManager::getSourceId(const Resource &resource) const Resource &ResourceManager::getResource(SourceId sourceId) const { auto it = resources.find(sourceId); - if (it != resourced.end()) { + if (it != resources.end()) { return it->second; } return NullResource; @@ -239,7 +238,7 @@ Rooted ResourceManager::link(ParserContext &ctx, const std::string &path, // Make sure the node has one of the supported types if (!node->type().isOneOf(supportedTypes)) { - logUnsopportedType(ctx.logger, supportedTypes); + logUnsopportedType(ctx.logger, resource, supportedTypes); return nullptr; } @@ -260,7 +259,7 @@ Rooted ResourceManager::link(ParserContext &ctx, const std::string &path, } // Continue with the usual include routine - return include(ctx, path, mimetype, rel, supportedTypes, relativeResource); + return link(ctx, path, mimetype, rel, supportedTypes, relativeResource); } SourceContext ResourceManager::buildContext(const SourceLocation &location) @@ -274,5 +273,3 @@ SourceContext ResourceManager::buildContext(const SourceLocation &location) } -#endif /* _OUSIA_RESOURCE_MANAGER_HPP_ */ - diff --git a/src/core/resource/ResourceUtils.cpp b/src/core/resource/ResourceUtils.cpp index 26faaad..7c42716 100644 --- a/src/core/resource/ResourceUtils.cpp +++ b/src/core/resource/ResourceUtils.cpp @@ -16,8 +16,9 @@ along with this program. If not, see . */ -#include #include +#include +#include #include "ResourceUtils.hpp" @@ -42,15 +43,15 @@ static const std::unordered_map RelResourceTypeMap{ /** * Map mapping from relations to the corresponding Rtti descriptor. */ -static const std::unordered_map RelRttiTypeMap{ - {"document", &RttiTypes::DOCUMENT}, - {"domain", &RttiTypes::DOMAIN}, - {"typesystem", &RttiTypes::TYPESYSTEM}}; +static const std::unordered_map RelRttiTypeMap{ + {"document", &RttiTypes::Document}, + {"domain", &RttiTypes::Domain}, + {"typesystem", &RttiTypes::Typesystem}}; /** * Map mapping from Rtti pointers to the corresponding ResourceType. */ -static const std::unordered_map RttiResourceTypeMap{ +static const std::unordered_map RttiResourceTypeMap{ {&RttiTypes::Document, ResourceType::DOCUMENT}, {&RttiTypes::Domain, ResourceType::DOMAIN_DESC}, {&RttiTypes::Typesystem, ResourceType::TYPESYSTEM}}; @@ -80,7 +81,7 @@ ResourceType ResourceUtils::deduceResourceType(const std::string &rel, ResourceType ResourceUtils::deduceResourceType(const std::string &rel, Logger &logger) { - std::string s = Utils::toLowercase(rel); + std::string s = Utils::toLower(rel); if (!s.empty()) { auto it = RelResourceTypeMap.find(s); if (it != RelResourceTypeMap.end()) { @@ -97,8 +98,8 @@ ResourceType ResourceUtils::deduceResourceType(const RttiSet &supportedTypes, Logger &logger) { if (supportedTypes.size() == 1U) { - auto it = RttiResourceTypeMap.find(supportedTypes[0]); - if (it != RelResourceTypeMap.end()) { + auto it = RttiResourceTypeMap.find(*supportedTypes.begin()); + if (it != RttiResourceTypeMap.end()) { return it->second; } } @@ -107,14 +108,14 @@ ResourceType ResourceUtils::deduceResourceType(const RttiSet &supportedTypes, const Rtti *ResourceUtils::deduceRttiType(const std::string &rel) { - std::string s = Utils::toLowercase(rel); + std::string s = Utils::toLower(rel); if (!s.empty()) { auto it = RelRttiTypeMap.find(s); if (it != RelRttiTypeMap.end()) { return it->second; } } - return &ResourceType::Node; + return &RttiTypes::Node; } RttiSet ResourceUtils::limitRttiTypes(const RttiSet &supportedTypes, @@ -128,7 +129,7 @@ RttiSet ResourceUtils::limitRttiTypes(const RttiSet &supportedTypes, { RttiSet res; for (const Rtti *supportedType : supportedTypes) { - if (supportedType.isa(type)) { + if (supportedType->isa(*type)) { res.insert(supportedType); } } -- cgit v1.2.3 From d242f74de618e92bb7baaca59aa224685783c5a8 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:31:43 +0100 Subject: Made more stuff const --- src/core/common/Location.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/core/common/Location.hpp b/src/core/common/Location.hpp index 57892cc..808abbd 100644 --- a/src/core/common/Location.hpp +++ b/src/core/common/Location.hpp @@ -115,7 +115,7 @@ public: * * @return true if the SourcePosition instance is value, false otherwise. */ - bool isValid() { return pos != InvalidSourceOffset; } + bool isValid() const { return pos != InvalidSourceOffset; } }; /** @@ -252,7 +252,7 @@ public: * * @return true if the Range is valid. */ - bool isValid() + bool isValid() const { return start.isValid() && end.isValid() && start.getPosition() <= end.getPosition(); @@ -333,7 +333,7 @@ public: * * @return the id of the source file this instance is bound to. */ - SourceId getSourceId() { return sourceId; } + SourceId getSourceId() const { return sourceId; } /** * Returns true if this location is actually valid. This is the case if @@ -341,7 +341,7 @@ public: * * @return true if the Range is valid. */ - bool isValid() + bool isValid() const { return SourceRange::isValid() && sourceId != InvalidSourceId; } @@ -469,7 +469,7 @@ struct SourceContext { * @return the corresponding SourceContext. */ using SourceContextCallback = - std::function; + std::function; /** * Function to be used as default value for the SourceContextCallback. Returns @@ -478,7 +478,7 @@ using SourceContextCallback = * @param location is the location for which the context should be looked up. * @return an empty, invalid SourceContext. */ -void SourceContext NullSourceContextCallback(const SourceLocation &location); +SourceContext NullSourceContextCallback(const SourceLocation &location); } -- cgit v1.2.3 From aa817d3bfd90aa39b6fd8a915bc78a8bb210cd3d Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 23 Jan 2015 15:43:36 +0100 Subject: Referencing the nodes in which the validation fails --- src/core/model/Node.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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 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; -- cgit v1.2.3