Expanding on Eamon's answer...
In the case of either XSLT 1.0 or 2.0, you'd start by putting the intermediate (pre-processed) result in an <xsl:variable> element, declared either globally (top-level) or locally (inside a template).
<xsl:variable name="intermediate-result">
<!-- code to create pre-processed result, e.g.: -->
<xsl:apply-templates mode="pre-process"/>
</xsl:variable>
In XSLT 2.0, the value of the $intermediate-result variable is a node sequence consisting of one document node (was called "root node" in XSLT/XPath 1.0). You can access and use it just as you would any other variable, e.g., select="$intermediate-result/doc"
But in XSLT 1.0, the value of the $intermediate-result variable is not a first-class node-set. Instead, it's something called a "result tree fragment". It behaves like a node-set containing one root node, but you're restricted in how you can use it. You can copy it and get its string-value, but you can't drill down using XPath, as in select="$intermediate-result/doc". To do that, you must first convert it to a first-class node-set using your processor's node-set() extension function. In Saxon 6.5, libxslt, and 4xslt, you can use exsl:node-set() (as in Eamon's answer). In MSXML, you'd need to use msxsl:node-set(), where xmlns:msxsl="urn:schemas-microsoft-com:xslt", and in Xalan, I believe it's called xalan:nodeset() (without the hyphen, but you'll have to Google for the namespace URI). For example: select="exsl:node-set($intermediate-result)/doc"
XSLT 2.0 simply abolished the result tree fragment, making node-set() unnecessary.