Up: Macro Expansion   [Contents][Index]

A.1.1 Expansion Sequence

An expansion sequence \(E\) is an ordered list of nodes \(N_1,N_2,\ldots,N_m\) satisfying the property that, given some node \(N_x\in E\) such that \(m\geq x>1\), the node \(N_{x-1}\) must have already been fully expanded before expansion of \(N_x\) begins. Such an ordering guarantee is generally unnecessary.

Expansion sequences are initiated by invoking eseq:expand-step#1 on any arbitrary node containing any number of children to be expanded in order. Each call will proceed one step (detailed herein), eventually resulting in each node expanded and the expansion sequence node eliminated.

function: node()* eseq:expand-step (eseq as node()*)


This fuction performs only a single pass, expected to be applied once for each pass as necessary. This may change in the future once the preprocessor supports subtree expansion.

The final result of the expansion is the result of expanding each child serially, requiring that expansion of the previous sibling be wholly completed before expansion of a node. The parent expansion sequence node will be stripped from the result once empty.

Note that this function accepts an empty XML sequence, and will return an empty sequence in such a case.


<function name="eseq:expand-step"  as="node()*">
  <param name="eseq"  as="node()*" />

  <variable name="count"  as="xs:integer"  select="count( $eseq )" />

  <variable name="target"  as="element()?"  select="$eseq[ $count ]" />

  <!-- We must be careful in how we retain other nodes; since we
       accept a sequence of nodes, the @code{preceding-sibling}
       pseudo-selector cannot be used, since they are not part of the
       same tree. -->
  <sequence select="$eseq[ position() lt $count ]" />

  <apply-templates mode="_eseq:expand"  select="$target" />

Throughout the process, we will consider only the head of the sequence for processing; we call the the head node. Once the head is done expanding, it is hoisted out out of the sequence—order maintained—and processing continues with the next head. If no such head exists, then we are done.

This implementation sounds simple on the face of it, but is complicated by the fact that we are not performing this operation in one sweep—due to the implementation of the preprocessor at the time that this was written, we must pass control back with each pass that might trigger an expansion, allowing the symbol table to be updated and other processing to take place. At least that’s the rationale; we wouldn’t want to make such assumptions in this implementation. But we do need to keep that in mind.

With that, we will also want to keep the number of re-passes to a minimum; that means yielding back to the caller only when necessary. Until a redesign of the preprocessor such that it will reprocess only as necessary, running a sequential expansion will always be slower, and processing time will grow linearly with the number of expansion nodes, relative to the size of the entire package.

Up: Macro Expansion   [Contents][Index]