Oliver Becker, System Architecture, Department of Computer Science, Humboldt University Berlin .

 

The XSLT Loop Compiler

XSLT is a declarative language that doesn't allow updating variables. Hence there are no constructs to create iterative loops, which are well-known from imperative programming languages like C(++) or Java. (Even <xsl:for-each> isn't really a loop construct.) To solve iterative problems you have to transform them into recursive problems - no problem if you're familiar with that.

In XSLT this means defining a named template and calling it with appropriate parameters. The resulting code could become slightly unreadable.

Don't bother - here comes the loop compiler!

The XSLT Loop Compiler allows you thinking iterative. It performs the necessary transformation into recursive template calls. The loop language introduces five new elements. They could be considered as XSLT extension elements. However, the resulting code is fully XSLT conformant - it doesn't use any proprietary extension elements or functions. The code should run with every compliant XSLT processor.

Usage

Before we're diving into the details: how to use this compiler?

  1. Write your stylesheet which may contain the additional loop elements described below.
  2. Transform this extended stylesheet into a pure XSLT stylesheet with the compiler stylesheet loop-compiler.xslt
  3. The resulting stylesheet should work with every XSLT compliant processor.

An example is on my main XSLT page at the end of the loop compiler section.

Namespace

All loop elements belong to the namespace "http://informatik.hu-berlin.de/loop".
The prefix I chose was loop.

There's no "www" contained in the namespace - it isn't an URL for retrieving something.

Elements

<loop:for>

performs a traditional for-loop, i.e. start and end values are known, the loop repeats by increasing (or decreasing) a counter by a fixed step.

   <loop:for name="qname" from="expression" to="expression" step="expression">
      
<!-- template body -->
   
</loop:for>

The step attribute is optional. If it is omitted, step defaults to 1. If step < 1 then the loop runs until $qname < to, otherwise until $qname > to.

Example:

   <loop:for name="i" from="10" to="1" step="-1">
      
<loop:for name="j" from="1" to="10">
         
<xsl:value-of select="$i * $j" />
         
<xsl:text>&#x9;</xsl:text>
      
</loop:for>
      
<xsl:text>&#xA;</xsl:text>
   
</loop:for>

gives an upside down multiplication table up to ten.

 

<loop:while>

performs a traditional while-loop, repeating while a test expression evaluates to true.

   <loop:while test="expression">
      
<!-- optional some <xsl:variable> elements -->
      
<loop:do>
         
<!-- template body -->
      
</loop:do>
      
<loop:last> 
         
<!-- template body -->
      
</loop:last>
      
<!-- at least one <loop:update> element -->
   
</loop:while>

A <loop:while> may have in this order zero or more <xsl:variable> children, one optional <loop:do> child element, one optional <loop:last> child element, and one or more <loop:update> children (see below).

Actions to be performed every turn of the loop must be placed inside of <loop:do>, actions to be performed when the loop terminates must be placed inside of <loop:last>. The element <loop:last> was introduced because all variables will have their old values after returning from the loop. Therefore one would have no means to access the final values of these variables.

Sometimes one needs the same computed value inside of <loop:do>, <loop:last>, <loop:update>, or even as part of the test expression of <loop:while>. This can be simplified by storing the computed value as a <xsl:variable>. These variables must appear first as children of <loop:while>. Hence the execution of a while loop is: first compute all variables, next evaluate the test expression, if true then perform all actions inside of <loop:do>, update the variables and repeat, otherwise perform all actions inside of <loop:last> and terminate (i.e. return from the loop). This kind of loop is a "tail recursion".

Note: Though a variable is declared only inside of <loop:while> it may be referenced already within the test expression of <loop:while>'s start tag.

An example showing <loop:while> follows below, after introducing <loop:update>.

 

<loop:update>

allows assigning a variable a new value for the next turn.

A <loop:update> element is translated into an XSLT <xsl:with-param> element, therefore it has to follow the same rules.

   <loop:update name="qname" select="expression" />

respectively

   <loop:update name="qname">
      
<!-- template body -->
   
</loop:update>

Every variable to be upated must be declared beforehand, i.e. qname must refer to a variable or parameter that is visible at the parent loop element. If a <loop:while> contains no <loop:update> at all, the loop will repeat infinitely (which is most probably not what you want).

While <loop:update> usually will be used within a <loop:while> loop, it can of course also be placed inside of <loop:for> to alter additional variables. However, the counter of <loop:for> (its name attribute) can't be changed this way.

Example:

   <xsl:param name="input" select="10" />
   
<xsl:variable name="result" select="1" />
   
<loop:while test="$diff * $diff &gt; 0.000001">
      
<xsl:variable name="diff" select="$result * $result - $input" />
      
<loop:do>
         
<xsl:text>Intermediate result: </xsl:text>
         
<xsl:value-of select="$result" />
         
<xsl:text>&#xA;</xsl:text>
      
</loop:do>
      
<loop:last>
         
<xsl:text>Square root of </xsl:text>
         
<xsl:value-of select="$input" />
         
<xsl:text> is </xsl:text>
         
<xsl:value-of select="format-number($result,'#.00000')" />
         
<xsl:text>&#xA;</xsl:text>
      
</loop:last>
      
<loop:update name="result" select="($result + $input div $result) div 2" />
   
</loop:while>

This loop computes the square root of a given number (input). A complete stylesheet containing both examples of this page can be downloaded here.

Well, these are not the kind of problems XSLT was designed for. More reasonable examples, demonstrating <loop:while> and <loop:update>, can be found on my XSLT page, particularly tokenizer.lxsl, number-total.lxsl and total-sales.lxsl.

 

© ob Freitag, 06-Apr-2001 12:34:38 CEST .