qt4cg / qtspecs

QT4 specifications
https://qt4cg.org/
Other
28 stars 15 forks source link

xsl:pipeline #1111

Open michaelhkay opened 5 months ago

michaelhkay commented 5 months ago

In XSLT 3.0 it is not possible to write a multi-phase streaming transformation, where two are more phases each operate in streaming mode and the result of one phase is piped into the next. Such transformations can only be written as multiple stylesheets, coordinated by some calling application.

A non-streamed multiphase transformation typically uses variables for the intermediate results:

<xsl:variable name="temp1">
   <xsl:apply-templates mode="phase1"/>
</xsl:variable>
<xsl:variable name="temp2">
   <xsl:apply-templates select="$temp1" mode="phase2"/>
</xsl:variable>
<xsl:apply-templates select="$temp2"/>

This cannot be streamed because variables cannot hold streamed nodes.

The idea is to allow this to be written:

<xsl:pipeline streamable="yes">
   <xsl:apply-templates mode="phase1"/>
   <xsl:apply-templates select="." mode="phase2"/>
   <xsl:apply-templates select="." mode="phase3"/>
</xsl:pipeline>

where each instruction in the pipeline takes as its context value the result of the previous instruction.

Even when no streaming is involved, the xsl:pipeline instruction brings usability benefits: it's much clearer to the reader what is going on.

(Triggered by a support request from a user wanting to make an existing pipelined transformation streamable; but the idea was considered and "postponed to v.next" during XSLT 3.0 development. The replacement of "context item" by "context value" removes one of the obstacles.)

johnlumley commented 5 months ago

Is there any merit in (for non-streaming cases) extending to permit parameters for each apply-templates invocation? For example one of my 'pipelines' (in the grammar EBNF conversion!) starts:

<xsl:template match="/">
      <xsl:variable name="temp" as="element()">
         <xsl:apply-templates select="*" mode="selectSpec"/>
      </xsl:variable>
      <xsl:variable name="temp2" as="element()">
         <xsl:apply-templates select="$temp" mode="inLine">
            <xsl:with-param name="inLines" 
                 select="map:merge($temp/g:token[@inline eq 'true'] ! map:entry(string(@name), *))"
                 tunnel="true"/>
         </xsl:apply-templates>
      </xsl:variable>

where the parameter is computed from the result of the previous step. Would we be able to use . as the context value in such computation, or will we need a reserved variable ($previous ?) to provide such a link, viz:

<xsl:pipeline>
  <xsl:apply-templates mode="selectSpec"/>
  <xsl:apply-templates select="." mode="inLine">
      <xsl:with-param name="inLines"  select="map:merge(./g:token[@inline eq 'true'].......)" tunnel="true"/>
   </xsl:apply-templates>

I know this is unlikely to be streamable, but in non-streaming situations it could be helpful.

cedporter commented 5 months ago

Might it be clearer to the user if instead of simply using "." in subsequent phases in a pipeline to introduce an xpath function like the ones used in xsl:for-each-group and xsl:analyze-string, i.e., fn:current-group() and fn:regex-group(), respectively.