Static and Dynamic Analysis

Static and Dynamic Analysis in XSLT 3.0

Static analysis rejects errors before data is read: unknown functions (XPST0017), impossible types, code excluded by use-when. At run time, xsl:analyze-string splits text with a regular expression into matching and non-matching parts, xsl:assert checks an invariant, and xsl:evaluate compiles an XPath expression supplied as a string, here from a parameter:

analysis.xsl: assert, evaluate and analyze-stringXML
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" expand-text="yes">
  <xsl:output method="text"/>
  <xsl:param name="filter" as="xs:string" select="'true()'"/>
  <xsl:template match="/catalog">
    <xsl:assert test="header/records = count(book)">record count differs</xsl:assert>
    <xsl:variable name="hits" as="element(book)*">
      <xsl:evaluate xpath="'book[' || $filter || ']'" context-item="."/>
    </xsl:variable>
    <xsl:text>{count($hits)} match {$filter}: {$hits/@id}&#10;</xsl:text>
    <xsl:analyze-string select="book[3]/summary" regex="from (\w+) to (\w+)">
      <xsl:matching-substring>{regex-group(1)} to {regex-group(2)}</xsl:matching-substring>
    </xsl:analyze-string>
  </xsl:template>
</xsl:stylesheet>
A filter from the command line, then a wrong record count
xslt3 -s:booknest-catalog.xml -xsl:analysis.xsl 'filter=pages > 300'; echo
sed 's#<records>6#<records>7#' booknest-catalog.xml > seven.xml
xslt3 -ea:on -s:seven.xml -xsl:analysis.xsl 2>&1 | head -n 1
Output
3 match pages > 300: b1 b2 b5
Kerala to Zanzibar
record count differs

Saxon 726,956 skips xsl:assert unless you pass -ea:on. A malformed filter (filter=pages >) fails with XTDE3160 at run time, when the string is compiled. Treat xsl:evaluate like SQL built from strings: no untrusted input.


Analysis

<xsl:analyze-string>, <xsl:matching-substring>, <xsl:non-matching-substring>, <xsl:fallback>

<xsl:analyze-string> matches a string against a regular expression, processing the matched and non-matched portions separately with <xsl:matching-substring> and <xsl:non-matching-substring>; <xsl:fallback> supplies fallback content for processors that do not support the instruction. The example below matches the regex ^(a.)(..) against the string 'abcdef': the match itself is "abcd", with capturing group 1 = "ab" and group 2 = "cd"; the matched portion outputs group 2 via regex-group(2), while the unmatched remainder ("ef") is wrapped in parentheses.
ch06-analyze-string.xslXML
<xsl:stylesheet version="3.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="/">
      <xsl:analyze-string select="'abcdef'" regex="^(a.)(..)"
                                                            flags="msix">
         <xsl:matching-substring>
            <xsl:value-of select="regex-group(2)"/>
         </xsl:matching-substring>
         <xsl:non-matching-substring>
            (<xsl:value-of select="."/>)
         </xsl:non-matching-substring>
         <xsl:fallback>
            Your processor does not support this feature.
         </xsl:fallback>
      </xsl:analyze-string>
   </xsl:template>
</xsl:stylesheet>
Output
<?xml version="1.0" encoding="UTF-8"?>cd
            (ef)

<xsl:assert>, <xsl:evaluate>

<xsl:assert> tests a condition and outputs its content as a diagnostic message only when the condition is false, without affecting the result if it is true. <xsl:evaluate> dynamically evaluates an XPath expression supplied as a string. The example below asserts that 101 ge 100 (true, so nothing is emitted), reports a message via <xsl:message select="10 ge 10"/>, and dynamically evaluates the string expression "(9*9)||''".
ch06-assert-evaluate.xslXML
<xsl:stylesheet version="3.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="/">
     <xsl:assert test="101 ge 100">
        Is smaller than 100.
     </xsl:assert>
     <xsl:message select="10 ge 10">
        Is greater than or equals 10
     </xsl:message>
     <xsl:evaluate xpath="(9*9)||''"/>
   </xsl:template>
</xsl:stylesheet>
Output
(The processor reports the message 'true. Is greater than or equals 10'.)

<?xml version="1.0" encoding="UTF-8"?>81
<xsl:assert> can have the following attributes: select (expression), error-code ({eqname}).
<xsl:message> can have the following attributes: terminate ({"yes" | "no"}), error-code ({eqname}).
<xsl:evaluate> can have the following attributes: as (sequence-type), base-uri ({uri}), with-params (expression), context-item (expression), namespace-context (expression), schema-aware ({"yes" | "no"}). The required item type of its xpath attribute is xs:string.