This post from 7 years ago suggests a Xalan specific non-XSLT solution might be available, but implementation independence and Michael's response pushed me in the direction of looking for a working XSLT1-based solution.
Here it is; I'm not sure whether it'll be useful to anyone else. I wonder whether there is a simpler way?
You can rely on XSLT, provided you set everything up correctly. From the spec:
In XSLT, an outermost expression (i.e. an expression that is not part
of another expression) gets its context as follows:
the context node comes from the current node
the context position comes from the position of the current node in
the current node list; the first position is 1
Starting at the end, I get the result of applying expression $expression into a variable $result:
<xsl:variable name="result" >
<xsl:apply-templates select="$vNodeSet" mode="myeval">
<xsl:with-param name="expression" ><xsl:value-of select="$expression"/></xsl:with-param>
<xsl:with-param name="pos" ><xsl:value-of select="$pos"/></xsl:with-param>
</xsl:apply-templates>
</xsl:variable>
That apply-templates pushes a suitable "current node list" to the following template:
<xsl:template match="*" mode="myeval">
<xsl:param name="expression">1. </xsl:param>
<xsl:param name="pos">3</xsl:param>
<xsl:choose>
<xsl:when test="position()=$pos">
<xsl:value-of select="dyn:evaluate($expression)" /></xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
This template evaluates the expression for the node I want to be the "current node". Notice:
- the use of dy:evaluate (thanks Martin!)
- $pos which identifies what I want as the current node.
I was able to calculate $pos using Dimitre's answer to an earlier question; I've also used his variable name vNodeSet
Thanks Dimitre, Martin, and Michael!