2007-07-28

Macros for XSLT

Gary King is confused because [XSLT] seems so ridiculously verbose. Others have already suggested mad higher-order function tricks using XSLT 2.0.

My solution: Macros. If XSLT lacks an element to do what you want (creating a text node with a newline, in Gary's case), just invent the feature you need and send your XSLT stylesheet through another XSLT stylesheet to implement it.

BR

Say you have demo.xsl which wants to use
  <x:br/>

to emit a newline. (In this example, `x' is simply the namespace for our extensions.) Write an additional stylesheet macros.xsl and send the original demo.xsl through the macro stylesheet to generate the actual XSLT source code. A macro template for <x:br> would be as simple as:
  <xsl:template match="x:br">
    <_xsl:text><xsl:text>&#10;</xsl:text></_xsl:text>
  </xsl:template>
In the macro stylesheet, xsl is the namespace of the "macro definition" and _xsl is the namespace of the "macro expansion". (If you care about details, the trick is to use xsl:namespace-alias to make the XSLT processor believe they are different namespaces.)

DOTIMES

For a more interesting example of macro use, suppose we want to repeat our code count times. Doing this kind of iteration involves a recursive template call, which we want to hide. We will define a macro <x:dotimes> that can be used like this:
  <x:dotimes var="i" count="3">
    <xsl:value-of select="$i"/>
  </x:dotimes>
Our macro stylesheet replaces each use of <x:dotimes> with a template call, and adds a recursive template as a top-level element:
  <xsl:template match="xsl:stylesheet">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <xsl:for-each select="//x:dotimes">
        <_xsl:template name="x:dotimes_{generate-id()}">
          ... recursive template definition here ...
        </_xsl:template>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="x:dotimes">
    <_xsl:call-template name="x:dotimes_{generate-id()}">
      ... parameters elided for brevity ...
    </_xsl:call-template>
  </xsl:template>
Download the full macros.xsl and demo.xsl to try the example. To run it with xsltproc, use the Makefile in the same directory.

2 comments:

Anonymous said...

cool

Holger Schauer said...

That's quite cool. However, I see a workflow problem creeping behind the corner: In Lisp (hey, even in C), macro processing in built-in. It's not in XSLT. This implies that in CL you can simply throw your macro definitions into the code where you need them, the CL system will see it and do the right thing. And in C, cpp will be called anyway during the build process and you're fine. But in XSLT, you have different files and have to do the macro processing by hand. Which in turn has consequences whenever more than one programmer is involved. Heck, it's likely that even with one programmer and three months later that somebody will work *only* with the result of the macro expansion. This is extremely likely to happen if you're using a built-in XSLT library and none of the stand-alone XSLT processors.

In CL, there is a saying that one should use macros only when necessary. Given the above, I would conclude that for XSLT, the advice should probably read to avoid macros at all cost.

Of course, none of this is your fault and I'm very grateful you pointed out the idea and provided the code. Now, if only the W3C would recommend this idea ... :-)

PS: Thanks also for the link to Novatchevs FXSL library. It doesn't suffer from the problem as one simply imports XSLT templates (which is standard in XSLT in contrast to preprocessing of stylesheets), I think.