xsl-list
[Top] [All Lists]

Re: [xsl] How to convert a recursive function to a loop, using XSLT 2.0?

2019-05-11 14:55:34
Hi Roger,

Besides the obvious grouping solution, here is a short XPath 2.0 (plus some
XSLT 2.0) to do this in a non-recursive way.

Ideally I would use no XSLT and pure XPath 3 -- taking advantage of XPath
3.0 features such as the let - expression and maps, and also the ability to
generate XML as a string and then use the standard parse-xml() function to
construct a new XML document, using only XPath.

One can also produce an XSLT 1.0 solution, using keys.

This XSLT 2.0 transformation:

<xsl:stylesheet version="2.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform";
 xmlns:xs="http://www.w3.org/2001/XMLSchema"; xmlns:my="my:my"
 exclude-result-prefixes="xs my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:variable name="vHex2Dec">
    <tr from="48" to="72"/>
    <tr from="4A" to="74"/>
    <tr from="69" to="105"/>
    <tr from="6C" to="108"/>
  </xsl:variable>

  <xsl:template match="/">
    <xsl:variable name="vBytes" select="/*/Byte"/>
    <xsl:variable name="vEnds" select="index-of($vBytes, '00')"/>

    <xsl:sequence select=
    "for $vLastEnd in (0, $vEnds),
         $vThisEnd in $vEnds[. > $vLastEnd][1]
       return
          my:makeString(
            for $pos in $vLastEnd+1 to $vThisEnd -1
      return
        $vHex2Dec/*[@from = $vBytes[$pos]]/@to/xs:integer(.)
                        )

    "/>
  </xsl:template>

  <xsl:function name="my:makeString" as="element()">
    <xsl:param name="pCodepoints" as="xs:integer*"/>

    <string>
      <xsl:sequence select="codepoints-to-string($pCodepoints)"/>
    </string>
  </xsl:function>
</xsl:stylesheet>

When applied on the provided XML fragment (made a wellformed XML document):

<t>
<Byte>48</Byte>
<Byte>69</Byte>
<Byte>00</Byte>
<Byte>4A</Byte>
<Byte>69</Byte>
<Byte>6C</Byte>
<Byte>6C</Byte>
<Byte>00</Byte>
</t>

produces the wanted result:

<string>Hi</string>
<string>Jill</string>


Cheers,
Dimitre


On Fri, May 10, 2019 at 4:20 AM Costello, Roger L. 
costello(_at_)mitre(_dot_)org <
xsl-list-service(_at_)lists(_dot_)mulberrytech(_dot_)com> wrote:

Hello XSLT experts!

My input file consists of a sequence (sourceSeq) of <Byte> elements
representing a sequence of null-terminated strings. I want to create a
function that returns a sequence of <String> elements. For example, with
this sourceSeq:

<Byte>48</Byte>
<Byte>69</Byte>
<Byte>00</Byte>
<Byte>4A</Byte>
<Byte>69</Byte>
<Byte>6C</Byte>
<Byte>6C</Byte>
<Byte>00</Byte>

the function should return:

<String>Hi</String>
<String>Jill</String>

The strings are of variable length.

I do not know how many null-terminated strings are in sourceSeq. However,
I do know the total number (total-size) of <Byte> elements within sourceSeq
containing the null-terminated strings.

Below is a recursive way to implement the function. Unfortunately,
total-size can be quite large, which means the function recurses many
times, resulting in a "Too many nested function calls" error. Is there an
iterative way to implement the function?  /Roger

<xsl:function name="f:make-string-table-entries" as="element(String)*">
    <xsl:param name="total-size" as="xs:integer" />
    <xsl:param name="current-size" as="xs:integer" />
    <xsl:param name="current-position" as="xs:integer" />
    <xsl:param name="sourceSeq" as="element(Byte)*" />

    <xsl:choose>
        <xsl:when test="$current-size ge $total-size" />
        <xsl:otherwise>
            <xsl:variable name="string"
select="f:make-element-from-null-terminated-string('String',
$current-position, $sourceSeq)" as="element(String)"/>
            <xsl:sequence select="$string" />
            <xsl:variable name="length"
select="string-length($string/text()) + 1"/>  <!-- add 1 for the null byte
-->
            <xsl:sequence select="f:make-string-table-entries($total-size,
xs:integer($current-size+$length), xs:integer($current-position+$length),
$sourceSeq)" />
        </xsl:otherwise>
    </xsl:choose>

</xsl:function>




-- 
Cheers,
Dimitre Novatchev
---------------------------------------
Truly great madness cannot be achieved without significant intelligence.
---------------------------------------
To invent, you need a good imagination and a pile of junk
-------------------------------------
Never fight an inanimate object
-------------------------------------
To avoid situations in which you might make mistakes may be the
biggest mistake of all
------------------------------------
Quality means doing it right when no one is looking.
-------------------------------------
You've achieved success in your field when you don't know whether what
you're doing is work or play
-------------------------------------
To achieve the impossible dream, try going to sleep.
-------------------------------------
Facts do not cease to exist because they are ignored.
-------------------------------------
Typing monkeys will write all Shakespeare's works in 200yrs.Will they write
all patents, too? :)
-------------------------------------
Sanity is madness put to good use.
-------------------------------------
I finally figured out the only reason to be alive is to enjoy it.
--~----------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
EasyUnsubscribe: http://lists.mulberrytech.com/unsub/xsl-list/1167547
or by email: xsl-list-unsub(_at_)lists(_dot_)mulberrytech(_dot_)com
--~--
<Prev in Thread] Current Thread [Next in Thread>