xslttcltdom

How to apply an XSLT transformation that includes spaces to an XML doc using tDOM?


I have some XML of the form:

<definitions devices="myDevice">
    <reg offset="0x0000" mnem="someRegister">
        <field mnem="someField" msb="31" lsb="24 />
        ...
    </reg>
    ...
</definitions>

I want the XML to be the definitive reference and use XSLT to transform it to HTML for documentation, .h for building (and maybe other forms too).

The HTML version is working fine and produces a table per register, with a row per field:

... (header boilerplate removed)
<xsl:for-each select="definitions/reg">
  <table>
    <tr>
      <th><xsl:value-of select="@offset"/></th>
      <th><xsl:value-of select="@mnem"/></th>
    </tr>
    <xsl:for-each select="field">
        <tr>
            <td><xsl:value-of select="@msb"/>..<xsl:value-of select="@lsb"/></td>
            <td><xsl:value-of select="@mnem"/></td>
        </tr>
    </xsl:for-each>
  </table>
</xsl:for-each>

Converting to a .h isn't going so well. I'm completely failing to generate the required spaces in the output:

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="definitions/reg">
#define <xsl:value-of select="translate(@mnem,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="@offset"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

I'd hope for that to produce the output:

#define SOMEREGISTER 0x0000

But I actually get:

#define SOMEREGISTER0x0000

I don't understand why I get the space after the '#define', but not the one after the transformed mnemonic. I've tried a simpler solution with just an inline space, with the same results.

I'm too new to this (XSLT) to know whether I'm a) doing it wrong or b) finding a limitation in tDOM.


Solution

  • Testing with this:

    # I could have read these from a file I suppose...
    set in {<definitions devices="myDevice">
    <reg offset="0x0000" mnem="someRegister">
    <field mnem="someField" msb="31" lsb="24" />
    </reg>
    </definitions>}
    set ss {<?xml version="1.0"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
    <xsl:for-each select="definitions/reg">
    <xsl:text>#define </xsl:text>
    <xsl:value-of select="translate(@mnem,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
    <xsl:text xml:space="preserve"> </xsl:text>
    <xsl:value-of select="@offset"/>
    </xsl:for-each>
    </xsl:template>
    </xsl:stylesheet>}
    
    # Interesting code starts here
    package require tdom
    set indoc [dom parse $in]
    set xslt [dom parse -keepEmpties $ss]
    set outdoc [$indoc xslt $xslt]
    puts [$outdoc asText]
    

    I find that this works. The issue is that the tDOM parser doesn't handle the xml:space attribute correctly; without the magical -keepEmpties option, all the empty strings are stripped from the stylesheet and that leads to a wrong XSLT stylesheet being applied. But with the option, it appears to do the right thing.

    Note that the XSLT engine itself is doing the right thing. It's the XML parser/DOM builder. (I think it's a bug; I'll look up where to report it.)