Next: , Previous: , Up: Expansion Sequence   [Contents][Index]


A.1.1.2 Expansion Operations

The concept of “expansion” is treated as an abstract operation (see Predicating Expansion). The system implementing expansion sequences should provide concrete definitions of what it means to expand a node, and what it means to check whether a node is expanded.

Recall that expansion takes place only the on the head node. Expanding the head therefore involves reproducing the expansion sequence with head expanded and siblings untouched.

function: element() _eseq:expand-head (eseq as element())

xmlns:_eseq="http://www.lovullo.com/tame/preproc/expand/eseq/_priv"

Expanding the head produces a new expansion sequence with the head expanded and all its sibling nodes left untouched. This produces the fundamental effect of the expansion sequence.

A head node must be available to satisfy the domain of the function.

Actual expansion is left to eseq:expand-node#1.

Definition:

<function name="_eseq:expand-head"  as="element()">
  <param name="eseq"  as="element()" />

  <variable name="head"  as="node()"  select="$eseq/node()[1]" />

  <!-- This @code{for-each} is purely to set the context for
       @code{copy}, since we do not know the sequence element
       name. -->
  <for-each select="$eseq">
    <copy>
      <sequence select="$eseq/@*, eseq:expand-node( $head ), $head/following-sibling::node()" />
    </copy>
  </for-each>
</function>

Just as we defined an overridable expansion predicate, we will provide an overridable function that performs actual node expansion.

Its default behavior is an important consideration: what if eseq:is-expandable#1 is overridden but the implementation forgets to override eseq:expand-node#1? If the default behavior were to simply echo back the node, it seems likely that we would never finish processing, since the very node that matched the predicate to begin with would remain unchanged.

Ideally, we remain side-effect free (meaning no triggering of errors). Instead, we return a single node in our own namespace that represents the error; this will likely trigger an error in the system, since the node is unrecognized.6

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

xmlns:eseq="http://www.lovullo.com/tame/preproc/expand/eseq"

An implementation must override this function.

This function should perform whatever is necessary to expand the provided node. Note that this call represents a single step in an expansion, so it need not result in a complete expansion; further processing will take place according to the result of the eseq:is-expandable#1 predicate.

If eseq:is-expandable#1 is provided, but an override for this function is not, then the default behavior is to return a node in our namespace providing a description of the problem; this is to prevent infinite recursion/iteration.

Definition:

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

  <eseq:expand-error>
    <text>A node expansion was requested via `eseq:expand-node'</text>
    <text>, but no implementation was provided.  Please</text>
    <text>override the function.</text>
  </eseq:expand-error>
</function>

The return type of eseq:expand-node#1 produces an interesting concept. Consider what may happen after an expansion:

  1. Node expanded into a single node;
  2. Node expanded into nothing (producing no node); or
  3. Node expanded into multiple nodes.

The first is obviously not an issue, since it keeps us consistent with what we have been doing. In the second case, a node is removed rather than being hoisted, but we are otherwise in a state that we expect: less a node. So will the case of expanding into multiple nodes cause any problems?

It shouldn’t, but it is worth a discussion to rationalize. Expansion sequences exist to provide expansion guarantees; the system otherwise expands nodes as it can in an undefined manner. Since that manner is undefined, providing it with stronger restrictions is acceptable: the newly expanded nodes will be processed in order as a consequence of becoming part of the expansion sequence, but they will otherwise be processed as normal.7

After expansion, with the current preprocessor design, we have no choice but the yield control back to the caller to allow it to continue processing; the expansion may have yielded additional symbols that must be added to the symbol table, for example. The process will be continued on the next call to eseq:expand-step#1.


Footnotes

(6)

If an error is not triggered, then the system is not very sound.

(7)

In fact, this is a useful property, since it can be exploited by templates to create abstractions with ordering guarantees. So consider it a feature!


Next: , Previous: , Up: Expansion Sequence   [Contents][Index]