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/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 -------------------------------------- 10 files changed, 758 insertions(+), 749 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/core/parser') 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_ */ - -- 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/core/parser') 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 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/core/parser') 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