xmlxsltconstantfolding

Recursively apply templates to result tree in XSLT (constant folding)


I am trying to apply constant folding to XML representations of simple numeric expressions. Constant folding is the process of replacing constant (sub)expressions with their literal value. Part of constant folding is replacing every binary operation that is applied to two literal values by the computed literal result. So say we have an expression:

1+x+2*(3+1)

and its XML representation:

<addition>
   <addition>
      <value>1</value>
      <variable>x</variable>
   </addition>
   <multiplication>
      <value>2</value>
      <addition>
         <value>3</value>
         <value>1</value>
      </addition>
   </multiplication>
</addition>

we can replace

<addition>
   <value>3</value>
   <value>1</value>
</addition>

with

<value>4</value>

And subsequently we can replace

<multiplication>
   <value>2</value>
   <value>4</value>
</multiplication>

with

<value>8</value>

The final result should look like this (it can be simplified even further, but this is sufficient for now):

<addition>
   <addition>
      <value>1</value>
      <variable>x</variable>
   </addition>
   <value>8</value>
</addition>

Part of my XSLT stylesheet looks like this:

<xsl:template match="addition[count(value) = 2]">
   <value>
      <xsl:value-of select="value[1] + value[2]" />
   </value>
</xsl:template>

<xsl:template match="multiplication[count(value) = 2]">
   <value>
      <xsl:value-of select="value[1] * value[2]" />
   </value>
</xsl:template>

...

<xsl:template match="@*|node()">
   <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
   </xsl:copy>
</xsl:template>

The idea of this template is to recursively replace all operation elements (addition, multiplication, ...), for which both operands (children) are value elements, with a single value element containing the computed value. The rest of tree (operations containing variables) is to be kept the same.

However, this stylesheet will only apply constant folding once, only replacing the deepest binary operation elements with their literal value. The only way I can get this stylesheet to work is to keep transforming the output of the last transformation until input and output document are the same. I'm new to XSLT, so I'm pretty sure there should be a more natural way to do this.


Solution

  • This transformation:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="addition[not(descendant::variable)]">
         <xsl:variable name="vArg1">
           <xsl:apply-templates select="*[1]"/>
         </xsl:variable>
         <xsl:variable name="vArg2">
           <xsl:apply-templates select="*[2]"/>
         </xsl:variable>
    
         <value>
           <xsl:value-of select="$vArg1 + $vArg2"/>
      </value>
     </xsl:template>
    
     <xsl:template match="multiplication[not(descendant::variable)]">
         <xsl:variable name="vArg1">
           <xsl:apply-templates select="*[1]"/>
         </xsl:variable>
         <xsl:variable name="vArg2">
           <xsl:apply-templates select="*[2]"/>
         </xsl:variable>
    
         <value>
           <xsl:value-of select="$vArg1 * $vArg2"/>
      </value>
     </xsl:template>
    </xsl:stylesheet>
    

    when applied on the provided XML document:

    <addition>
       <addition>
          <value>1</value>
          <variable>x</variable>
       </addition>
       <multiplication>
          <value>2</value>
          <addition>
             <value>3</value>
             <value>1</value>
          </addition>
       </multiplication>
    </addition>
    

    produces the wanted, correct result:

    <addition>
       <addition>
          <value>1</value>
          <variable>x</variable>
       </addition>
       <value>8</value>
    </addition>
    

    Explanation: Proper use of templates, xsl:apply-templates and xsl:variable .