diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/resource/ResourceManager.cpp | 182 | ||||
| -rw-r--r-- | src/core/resource/ResourceManager.hpp | 18 | 
2 files changed, 125 insertions, 75 deletions
diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp index 1d32a4d..48d12b3 100644 --- a/src/core/resource/ResourceManager.cpp +++ b/src/core/resource/ResourceManager.cpp @@ -70,6 +70,25 @@ void ResourceManager::purgeResource(SourceId sourceId)  	contextReaders.erase(sourceId);  } +template <class T> +class GuardedSetInsertion { +private: +	std::unordered_set<T> &set; +	T value; +	bool success; + +public: +	GuardedSetInsertion(std::unordered_set<T> &set, T value) +	    : set(set), value(value) +	{ +		success = set.insert(value).second; +	} + +	~GuardedSetInsertion() { set.erase(value); } + +	bool isSuccess() { return success; } +}; +  NodeVector<Node> ResourceManager::parse(      ParserContext &ctx, const std::string &path, const std::string &mimetype,      const std::string &rel, const RttiSet &supportedTypes, ParseMode mode) @@ -89,86 +108,111 @@ NodeVector<Node> ResourceManager::parse(  		return NodeVector<Node>{};  	} -	// Allocate a new SourceId handle for this Resource -	SourceId sourceId = allocateSourceId(resource); - -	// We can now try to parse the given file +	// initialize the output vector.  	NodeVector<Node> parsedNodes; -	// Set the current source id in the logger instance. Note that this -	// modifies the logger instance -- the GuardedLogger is just used to -	// make sure the default location is popped from the stack again. -	GuardedLogger guardedLogger(logger, SourceLocation{sourceId}); +	// Allocate a new SourceId handle for this Resource +	bool newResource = false; +	SourceId sourceId = getSourceId(resource); +	if (sourceId == InvalidSourceId) { +		newResource = true; +		sourceId = allocateSourceId(resource); +	} -	try { -		// Fetch the input stream and create a char reader -		std::unique_ptr<std::istream> is = resource.stream(); -		CharReader reader(*is, sourceId); - -		// Actually parse the input stream, distinguish the IMPORT and the -		// INCLUDE mode -		switch (mode) { -			case ParseMode::IMPORT: { -				// Create a new, empty parser scope instance and a new parser -				// context with this instance in place -				ParserScope innerScope; -				ParserContext childCtx = ctx.clone(innerScope, sourceId); - -				// Run the parser -				req.getParser()->parse(reader, childCtx); - -				// Make sure the scope has been unwound and perform all -				// deferred resolutions -				innerScope.checkUnwound(logger); -				innerScope.performDeferredResolution(logger); - -				// Fetch the nodes that were parsed by this parser instance and -				// validate them -				parsedNodes = innerScope.getTopLevelNodes(); -				for (auto parsedNode : parsedNodes) { -					parsedNode->validate(logger); -				} +	if (!newResource && mode == ParseMode::IMPORT) { +		// if a already imported resource should be imported we just use the +		// cached node. +		parsedNodes.push_back(getNode(ctx.getManager(), sourceId)); +	} else { +		// check for cycles. +		GuardedSetInsertion<SourceId> cycleDetection{currentlyParsing, +		                                             sourceId}; +		if (!cycleDetection.isSuccess()) { +			throw LoggableException{ +			    std::string("Detected cyclic inclusion of ") + +			    resource.getLocation()}; +		} -				// Make sure the number of elements is exactly one -- we can -				// only store one element per top-level node. -				if (parsedNodes.empty()) { -					throw LoggableException{"Module is empty."}; -				} -				if (parsedNodes.size() > 1) { -					throw LoggableException{ -					    std::string( -					        "Expected exactly one top-level node but got ") + -					    std::to_string(parsedNodes.size())}; +		// We can now try to parse the given file + +		// Set the current source id in the logger instance. Note that this +		// modifies the logger instance -- the GuardedLogger is just used to +		// make sure the default location is popped from the stack again. +		GuardedLogger guardedLogger(logger, SourceLocation{sourceId}); + +		try { +			// Fetch the input stream and create a char reader +			std::unique_ptr<std::istream> is = resource.stream(); +			CharReader reader(*is, sourceId); + +			// Actually parse the input stream, distinguish the IMPORT and the +			// INCLUDE mode +			switch (mode) { +				case ParseMode::IMPORT: { +					// Create a new, empty parser scope instance and a new +					// parser +					// context with this instance in place +					ParserScope innerScope; +					ParserContext childCtx = ctx.clone(innerScope, sourceId); + +					// Run the parser +					req.getParser()->parse(reader, childCtx); + +					// Make sure the scope has been unwound and perform all +					// deferred resolutions +					innerScope.checkUnwound(logger); +					innerScope.performDeferredResolution(logger); + +					// Fetch the nodes that were parsed by this parser instance +					// and +					// validate them +					parsedNodes = innerScope.getTopLevelNodes(); +					for (auto parsedNode : parsedNodes) { +						parsedNode->validate(logger); +					} + +					// Make sure the number of elements is exactly one -- we can +					// only store one element per top-level node. +					if (parsedNodes.empty()) { +						throw LoggableException{"Module is empty."}; +					} +					if (parsedNodes.size() > 1) { +						throw LoggableException{ +						    std::string( +						        "Expected exactly one top-level node but " +						        "got ") + +						    std::to_string(parsedNodes.size())}; +					} + +					// Store the parsed node along with the sourceId +					storeNode(sourceId, parsedNodes[0]); + +					break;  				} +				case ParseMode::INCLUDE: { +					// Fork the scope instance and create a new parser context +					// with this instance in place +					ParserScope forkedScope = scope.fork(); +					ParserContext childCtx = ctx.clone(forkedScope, sourceId); -				// Store the parsed node along with the sourceId -				storeNode(sourceId, parsedNodes[0]); - -				break; -			} -			case ParseMode::INCLUDE: { -				// Fork the scope instance and create a new parser context with -				// this instanc ein place -				ParserScope forkedScope = scope.fork(); -				ParserContext childCtx = ctx.clone(forkedScope, sourceId); - -				// Run the parser -				req.getParser()->parse(reader, childCtx); +					// Run the parser +					req.getParser()->parse(reader, childCtx); -				// Join the forked scope with the outer scope -				scope.join(forkedScope, logger); +					// Join the forked scope with the outer scope +					scope.join(forkedScope, logger); -				// Fetch the nodes that were parsed by this parser instance -				parsedNodes = forkedScope.getTopLevelNodes(); +					// Fetch the nodes that were parsed by this parser instance +					parsedNodes = forkedScope.getTopLevelNodes(); -				break; +					break; +				}  			}  		} -	} -	catch (LoggableException ex) { -		// Log the exception and return nullptr -		logger.log(ex); -		return NodeVector<Node>{}; +		catch (LoggableException ex) { +			// Log the exception and return nullptr +			logger.log(ex); +			return NodeVector<Node>{}; +		}  	}  	// Make sure the parsed nodes fulfill the "supportedTypes" constraint, diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp index 1279bee..186ce42 100644 --- a/src/core/resource/ResourceManager.hpp +++ b/src/core/resource/ResourceManager.hpp @@ -82,6 +82,11 @@ private:  	std::unordered_map<SourceId, ManagedUid> nodes;  	/** +	 * The set of SourceIds for which resources are currently being parsed. +	 */ +	std::unordered_set<SourceId> currentlyParsing; + +	/**  	 * Map containing SourceContextReader instances which are -- as their name  	 * suggests -- used to produce SourceContext structures describing the  	 * source code at a given SourceLocation. @@ -132,8 +137,8 @@ private:  	 * @return the parsed nodes or an empty list if something went wrong.  	 */  	NodeVector<Node> parse(ParserContext &ctx, const std::string &path, -	                   const std::string &mimetype, const std::string &rel, -	                   const RttiSet &supportedTypes, ParseMode mode); +	                       const std::string &mimetype, const std::string &rel, +	                       const RttiSet &supportedTypes, ParseMode mode);  public:  	/** @@ -161,8 +166,8 @@ public:  	 * @return the parsed node or nullptr if something went wrong.  	 */  	Rooted<Node> import(ParserContext &ctx, const std::string &path, -	                  const std::string &mimetype, const std::string &rel, -	                  const RttiSet &supportedTypes); +	                    const std::string &mimetype, const std::string &rel, +	                    const RttiSet &supportedTypes);  	/**  	 * Resolves the reference to the file specified by the given path and parses @@ -192,8 +197,9 @@ public:  	 * @return the parsed nodes or an empty list if something went wrong.  	 */  	NodeVector<Node> include(ParserContext &ctx, const std::string &path, -	                     const std::string &mimetype, const std::string &rel, -	                     const RttiSet &supportedTypes); +	                         const std::string &mimetype, +	                         const std::string &rel, +	                         const RttiSet &supportedTypes);  	/**  	 * Creates and returns a SourceContext structure containing information  | 
