/* 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 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_ */