tail call optimization for xslt recursive template

365 Views Asked by At

BackGround

We are using nginx with ngx_http_xslt_module enabled.This will help us in applying stylesheets to responses.
This module internally uses libxslt processor to apply xslt transformations.It has a configurable depth(maxdepth) of recursion beyond which it will signal an infinite recursion error.

Issue

At a depth of 3000 the below xslt transformation when applied to a document is failing with infinite recursion error.As per other answers that I have seen,the below xslt template is tail call optimised and some processors optimise the code.Wanted to confirm from xslt perspective if the below xslt function is really tail optimized and if libxslt does tail call optimization.

The escapeQuote xslt template when converted to code however is not tail call optimized.

"currentresult" + escapeQuote(sub-string(str))

The below is xslt template to escape double quotes.

 <xsl:template name="escapeQuote">
  <xsl:param name="pText" select="normalize-space(.)"/>
  <xsl:if test="string-length($pText) >0">
   <xsl:value-of select="substring-before(concat($pText, '&quot;'), '&quot;')"/>
   <xsl:if test="contains($pText, '&quot;')">
    <xsl:text>\"</xsl:text>
    <xsl:call-template name="escapeQuote">
      <xsl:with-param name="pText" select="substring-after($pText, '&quot;')"/>
    </xsl:call-template>
   </xsl:if>
  </xsl:if>
</xsl:template>

This is in XSLT 1.0.

1

There are 1 best solutions below

0
On

libxslt does not do tail-call optimisation. You should instead use the EXSLT functions that are included with libxslt. The str:replace function is not technically conformant to the specification, but is sufficiently correct for your needs. Specifically (assuming you are trying to change " to \"):

<xsl:template xmlns:str="http://exslt.org/strings" name="escapeQuote">
  <xsl:param name="pText" select="normalize-space(.)"/>
  <xsl:value-of select="str:replace($pText,'&quot;','\&quot;')"/>
</xsl:template>

Obviously you can ditch the template all together and just insert the relevant XPath if that works better for you.