All ref functions now have data adjacency guarantees
Accessors will ignore adjacent data and mutators will echo them, by reference, unchanged.master
commit
bb049984d5
|
@ -31,15 +31,25 @@
|
|||
The descriptor @var{desc} has the following format:
|
||||
|
||||
@example
|
||||
<f:ref arity="N" [...]>
|
||||
<f:ref arity="N" length="M" [...]>
|
||||
<target />
|
||||
</f:ref>
|
||||
@end example
|
||||
|
||||
where the @var{target} node shares the same QName as the function to
|
||||
be applied, and @var{@arity} is its arity. The @var{f:ref} node may
|
||||
be decorated with additional attributes depending on its context or
|
||||
be applied, and @var{@arity} is its arity. @var{@length} caches the
|
||||
number of items (in a sequence) that make up the reference; it can
|
||||
be retrieved with @code{f:length}. The @var{f:ref} node may be
|
||||
decorated with additional attributes depending on its context or
|
||||
constructor.
|
||||
|
||||
Each of these functions respects data adjacent to the given
|
||||
reference (which is at the head of the sequence). For example,
|
||||
@code{f:set-args} will set the arguments of the dynamic function
|
||||
reference represented by the head of the provided sequence, but will
|
||||
leave all data following the reference untouched; this allows
|
||||
references to be processed in a streaming manner without cutting the
|
||||
sequence up before operating on the reference.
|
||||
-->
|
||||
|
||||
<stylesheet version="2.0"
|
||||
|
@ -66,7 +76,7 @@
|
|||
<variable name="ns"
|
||||
select="namespace-uri-from-QName( $name )" />
|
||||
|
||||
<f:ref arity="{$arity}">
|
||||
<f:ref arity="{$arity}" length="1">
|
||||
<element name="{$name}"
|
||||
namespace="{$ns}" />
|
||||
</f:ref>
|
||||
|
@ -160,7 +170,12 @@
|
|||
<function name="f:args" as="item()*">
|
||||
<param name="fnref" as="item()+" />
|
||||
|
||||
<sequence select="remove( $fnref, 1 )" />
|
||||
<variable name="desc" as="element( f:ref )"
|
||||
select="$fnref[ 1 ]" />
|
||||
<variable name="length" as="xs:double"
|
||||
select="$desc/@length" />
|
||||
|
||||
<sequence select="subsequence( $fnref, 2, ( $length - 1 ) )" />
|
||||
</function>
|
||||
|
||||
|
||||
|
@ -200,10 +215,17 @@
|
|||
<attribute name="arity"
|
||||
select="$target-arity - count( $args )" />
|
||||
|
||||
<attribute name="length"
|
||||
select="count( $args ) + 1" />
|
||||
|
||||
<sequence select="$desc/*" />
|
||||
</f:ref>
|
||||
|
||||
<sequence select="$args" />
|
||||
<!-- be sure to retain the adjacent data (which is offset by the
|
||||
_original_ reference length) -->
|
||||
<sequence select="$args,
|
||||
subsequence( $fnref,
|
||||
$desc/@length + 1 )" />
|
||||
</function>
|
||||
|
||||
|
||||
|
@ -234,4 +256,28 @@
|
|||
($args, f:args( $fnref )) )" />
|
||||
</function>
|
||||
|
||||
|
||||
<!--
|
||||
Retrieve length of @var{fnref} as a number of sequence items
|
||||
|
||||
While the reference is intended to be opaque, it is no secret that
|
||||
the data are stored as a sequence. The length is therefore
|
||||
important for stepping through that sequence of data, similar to how
|
||||
data/struct length are required for pointer arithmetic when dealing
|
||||
with memory in C.
|
||||
|
||||
So, this doesn't break encapsulation: it just tells us how big we
|
||||
are, which is an external quality that can be easily discovered
|
||||
without our help; we just cache it for both performance and
|
||||
convenience. Aren't we nice?
|
||||
-->
|
||||
<function name="f:length" as="xs:double">
|
||||
<param name="fnref" as="item()+" />
|
||||
|
||||
<variable name="desc" as="element( f:ref )"
|
||||
select="$fnref[ 1 ]" />
|
||||
|
||||
<sequence select="number( $desc/@length )" />
|
||||
</function>
|
||||
|
||||
</stylesheet>
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
xmlns:foo="http://www.lovullo.com/_junk"
|
||||
stylesheet="partial-test.xsl">
|
||||
|
||||
<!-- TODO: this is common between a number of files; move me -->
|
||||
<variable name="foo-uri"
|
||||
select="namespace-uri-for-prefix(
|
||||
'foo', root(.)/element() )" />
|
||||
|
||||
<variable name="fnref"
|
||||
select="foo:ternary()" />
|
||||
|
||||
|
@ -89,11 +94,10 @@
|
|||
test="$x:result[ 1 ]
|
||||
and $x:result[ 1 ] = foo:ternary-applied" />
|
||||
|
||||
<variable name="rargs" select="f:args( $x:result )" />
|
||||
<expect label="applies target function with arguments, by reference"
|
||||
test="$rargs[ 1 ] is $args/foo:a
|
||||
and $rargs[ 2 ] is $args/foo:b
|
||||
and $rargs[ 3 ] is $args/foo:c" />
|
||||
test="$x:result[ 2 ] is $args/foo:a
|
||||
and $x:result[ 3 ] is $args/foo:b
|
||||
and $x:result[ 4 ] is $args/foo:c" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -144,9 +148,9 @@
|
|||
arguments to a target">
|
||||
<scenario label="unary">
|
||||
<call function="f:partial">
|
||||
<param name="fnref">
|
||||
<f:ref arity="1"><foo:fn1 /></f:ref>
|
||||
</param>
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo:fn1' ),
|
||||
1 )" />
|
||||
<param name="args" select="$args/foo:a" />
|
||||
</call>
|
||||
|
||||
|
@ -160,9 +164,9 @@
|
|||
|
||||
<scenario label="binary">
|
||||
<call function="f:partial">
|
||||
<param name="fnref">
|
||||
<f:ref arity="2"><foo:fn2 /></f:ref>
|
||||
</param>
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo:fn2' ),
|
||||
2 )" />
|
||||
<param name="args" select="$args/foo:a,
|
||||
$args/foo:b" />
|
||||
</call>
|
||||
|
@ -178,9 +182,9 @@
|
|||
|
||||
<scenario label="ternary">
|
||||
<call function="f:partial">
|
||||
<param name="fnref">
|
||||
<f:ref arity="3"><foo:fn3 /></f:ref>
|
||||
</param>
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo:fn3' ),
|
||||
3 )" />
|
||||
<param name="args" select="$args/foo:a,
|
||||
$args/foo:b,
|
||||
$args/foo:c" />
|
||||
|
@ -198,9 +202,9 @@
|
|||
|
||||
<scenario label="4-ary">
|
||||
<call function="f:partial">
|
||||
<param name="fnref">
|
||||
<f:ref arity="4"><foo:fn4 /></f:ref>
|
||||
</param>
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo:fn4' ),
|
||||
4 )" />
|
||||
<param name="args" select="$args/foo:a,
|
||||
$args/foo:b,
|
||||
$args/foo:c,
|
||||
|
@ -220,9 +224,9 @@
|
|||
|
||||
<scenario label="5-ary">
|
||||
<call function="f:partial">
|
||||
<param name="fnref">
|
||||
<f:ref arity="5"><foo:fn5 /></f:ref>
|
||||
</param>
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo:fn5' ),
|
||||
5 )" />
|
||||
<param name="args" select="$args/foo:a,
|
||||
$args/foo:b,
|
||||
$args/foo:c,
|
||||
|
@ -244,9 +248,9 @@
|
|||
|
||||
<scenario label="6-ary">
|
||||
<call function="f:partial">
|
||||
<param name="fnref">
|
||||
<f:ref arity="6"><foo:fn6 /></f:ref>
|
||||
</param>
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo:fn6' ),
|
||||
6 )" />
|
||||
<param name="args" select="$args/foo:a,
|
||||
$args/foo:b,
|
||||
$args/foo:c,
|
||||
|
@ -270,9 +274,9 @@
|
|||
|
||||
<scenario label="7-ary">
|
||||
<call function="f:partial">
|
||||
<param name="fnref">
|
||||
<f:ref arity="7"><foo:fn7 /></f:ref>
|
||||
</param>
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo:fn7' ),
|
||||
7 )" />
|
||||
<param name="args" select="$args/foo:a,
|
||||
$args/foo:b,
|
||||
$args/foo:c,
|
||||
|
@ -298,9 +302,9 @@
|
|||
|
||||
<scenario label="8-ary">
|
||||
<call function="f:partial">
|
||||
<param name="fnref">
|
||||
<f:ref arity="8"><foo:fn8 /></f:ref>
|
||||
</param>
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo:fn8' ),
|
||||
8 )" />
|
||||
<param name="args" select="$args/foo:a,
|
||||
$args/foo:b,
|
||||
$args/foo:c,
|
||||
|
|
|
@ -68,6 +68,13 @@
|
|||
|
||||
<expect label="produces reference to expected QName"
|
||||
test="f:QName( $x:result ) = $qname" />
|
||||
|
||||
<!-- this isn't breaking encapsulation: we know that a sequence
|
||||
is returned and we must know how to delimit the references
|
||||
within it -->
|
||||
<expect label="produces length equal to length of returned
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -102,6 +109,19 @@
|
|||
</scenario>
|
||||
|
||||
|
||||
<scenario label="given a valid dynamic function reference adjacent
|
||||
to other data">
|
||||
<call function="f:is-ref">
|
||||
<param name="fnref"
|
||||
select="f:make-ref( QName( $foo-uri, 'foo' ), 0 ),
|
||||
'adjacent data'" />
|
||||
</call>
|
||||
|
||||
<expect label="returns true()"
|
||||
select="true()" />
|
||||
</scenario>
|
||||
|
||||
|
||||
<scenario label="given an invalid dynamic function reference">
|
||||
<call function="f:is-ref">
|
||||
<param name="fnref">
|
||||
|
@ -223,14 +243,30 @@
|
|||
<expect label="returns empty sequence"
|
||||
select="()" />
|
||||
</scenario>
|
||||
|
||||
|
||||
<scenario label="given a valid dynamic function reference with
|
||||
adjacent data">
|
||||
<call function="f:QName">
|
||||
<param name="fnref"
|
||||
select="f:make-ref( $foo-qname, 0 ),
|
||||
'adjacent data'" />
|
||||
</call>
|
||||
|
||||
<expect label="returns QName"
|
||||
test="$x:result instance of xs:QName" />
|
||||
|
||||
<expect label="returns QName of target dynamic function"
|
||||
select="$foo-qname" />
|
||||
</scenario>
|
||||
</scenario>
|
||||
|
||||
|
||||
<scenario label="f:arity">
|
||||
<scenario label="given a proper function reference">
|
||||
<variable name="test-arity"
|
||||
select="5" />
|
||||
<variable name="test-arity"
|
||||
select="5" />
|
||||
|
||||
<scenario label="given a proper function reference">
|
||||
<!-- test our format directly; do not rely on the
|
||||
apply-gen code -->
|
||||
<call function="f:arity">
|
||||
|
@ -242,6 +278,20 @@
|
|||
</scenario>
|
||||
|
||||
|
||||
<scenario label="given a proper function reference with adjacent
|
||||
data">
|
||||
<!-- test our format directly; do not rely on the
|
||||
apply-gen code -->
|
||||
<call function="f:arity">
|
||||
<param name="fnref" select="$test-fn,
|
||||
'adjacent data'" />
|
||||
</call>
|
||||
|
||||
<expect label="provides function arity"
|
||||
select="$test-arity" />
|
||||
</scenario>
|
||||
|
||||
|
||||
<scenario label="given a partially applied function reference">
|
||||
<call function="f:arity">
|
||||
<param name="fnref"
|
||||
|
@ -281,6 +331,10 @@
|
|||
|
||||
<expect label="retains target arity"
|
||||
test="f:arity( $x:result ) = f:arity( $test-fn )" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -296,6 +350,10 @@
|
|||
<!-- the function is now partially applied -->
|
||||
<expect label="reduces arity by number of arguments"
|
||||
test="f:arity( $x:result ) = f:arity( $test-fn ) - 2" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -313,6 +371,10 @@
|
|||
<!-- the function is no longer partially applied -->
|
||||
<expect label="restores target function arity"
|
||||
test="f:arity( $x:result ) = f:arity( $test-fn )" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -330,6 +392,10 @@
|
|||
<!-- the function is no longer partially applied -->
|
||||
<expect label="reduces arity by number of argments"
|
||||
test="f:arity( $x:result ) = f:arity( $test-fn ) - 3" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -347,6 +413,36 @@
|
|||
<!-- the function is no longer partially applied -->
|
||||
<expect label="increases arity by difference in argument count"
|
||||
test="f:arity( $x:result ) = f:arity( $test-fn ) - 1" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
<scenario label="operating adjacent to other data">
|
||||
<call function="f:set-args">
|
||||
<param name="fnref" select="$test-fn,
|
||||
$args/foo:a,
|
||||
$args/foo:b" />
|
||||
<param name="args" select="(1, 2)" />
|
||||
</call>
|
||||
|
||||
<expect label="retains target QName"
|
||||
test="f:QName( $x:result ) = f:QName( $test-fn )" />
|
||||
|
||||
<expect label="reduces arity by number of arguments"
|
||||
test="f:arity( $x:result ) = f:arity( $test-fn ) - 2" />
|
||||
|
||||
<!-- notice that the length _does not_ reflect adjacent data -->
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence, sans adjacent data"
|
||||
test="f:length( $x:result ) = count( $x:result ) - 2" />
|
||||
|
||||
<variable name="rlength" select="count( $x:result )" />
|
||||
<expect label="retains adjacent data by reference"
|
||||
test="$x:result[ $rlength - 1 ] is $args/foo:a
|
||||
and $x:result[ $rlength ] is $args/foo:b" />
|
||||
</scenario>
|
||||
</scenario>
|
||||
|
||||
|
@ -396,6 +492,23 @@
|
|||
<expect label="returns no other items"
|
||||
test="count( $x:result ) = 2" />
|
||||
</scenario>
|
||||
|
||||
|
||||
<scenario label="operating on a reference preceding adjacent data">
|
||||
<call function="f:args">
|
||||
<param name="fnref"
|
||||
select="f:set-args( $test-fn, ($args/foo:a,
|
||||
$args/foo:b) ),
|
||||
'adjacent data'" />
|
||||
</call>
|
||||
|
||||
<expect label="returns sequence of arguments by reference"
|
||||
test="$x:result[ 1 ] is $args/foo:a
|
||||
and $x:result[ 2 ] is $args/foo:b" />
|
||||
|
||||
<expect label="returns no adjacent data"
|
||||
test="count( $x:result ) = 2" />
|
||||
</scenario>
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -419,6 +532,10 @@
|
|||
<expect label="sets arguments by reference"
|
||||
test="f:args( $x:result )[ 1 ] is $args/foo:a
|
||||
and f:args( $x:result )[ 2 ] is $args/foo:b" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -448,6 +565,10 @@
|
|||
<expect label="pushes new arguments by reference"
|
||||
test="f:args( $x:result )[ 3 ] is $args/foo:c
|
||||
and f:args( $x:result )[ 4 ] is $args/foo:d" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -470,6 +591,43 @@
|
|||
<expect label="retains previous arguments by reference"
|
||||
test="f:args( $x:result )[ 1 ] is $args/foo:a
|
||||
and f:args( $x:result )[ 2 ] is $args/foo:b" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
<scenario label="pushing arguments onto reference adjacent to
|
||||
other data">
|
||||
<call function="f:push-args">
|
||||
<param name="fnref"
|
||||
select="$test-fn,
|
||||
$args/foo:a,
|
||||
$args/foo:b" />
|
||||
<param name="args"
|
||||
select="$args/foo:c" />
|
||||
</call>
|
||||
|
||||
<expect label="retains target QName"
|
||||
test="f:QName( $x:result ) = f:QName( $test-fn )" />
|
||||
|
||||
<expect label="reduces arity by number of arguments, relative to
|
||||
target function"
|
||||
test="f:arity( $x:result ) = f:arity( $test-fn ) - 1" />
|
||||
|
||||
<expect label="sets arguments by reference"
|
||||
test="f:args( $x:result )[ 1 ] is $args/foo:c" />
|
||||
|
||||
<variable name="rlength" select="count( $x:result )" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence, sans adjacent data"
|
||||
test="f:length( $x:result ) = $rlength - 2" />
|
||||
|
||||
<expect label="retains adjacent data by reference"
|
||||
test="$x:result[ $rlength - 1 ] is $args/foo:a
|
||||
and $x:result[ $rlength ] is $args/foo:b" />
|
||||
</scenario>
|
||||
</scenario>
|
||||
|
||||
|
@ -494,6 +652,10 @@
|
|||
<expect label="sets arguments by reference"
|
||||
test="f:args( $x:result )[ 1 ] is $args/foo:a
|
||||
and f:args( $x:result )[ 2 ] is $args/foo:b" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -523,6 +685,10 @@
|
|||
<expect label="unshifts new arguments by reference"
|
||||
test="f:args( $x:result )[ 1 ] is $args/foo:c
|
||||
and f:args( $x:result )[ 2 ] is $args/foo:d" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
|
@ -545,6 +711,43 @@
|
|||
<expect label="retains previous arguments by reference"
|
||||
test="f:args( $x:result )[ 1 ] is $args/foo:a
|
||||
and f:args( $x:result )[ 2 ] is $args/foo:b" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence"
|
||||
test="f:length( $x:result ) = count( $x:result )" />
|
||||
</scenario>
|
||||
|
||||
|
||||
<scenario label="unshifting arguments onto reference adjacent to
|
||||
other data">
|
||||
<call function="f:unshift-args">
|
||||
<param name="fnref"
|
||||
select="$test-fn,
|
||||
$args/foo:a,
|
||||
$args/foo:b" />
|
||||
<param name="args"
|
||||
select="$args/foo:c" />
|
||||
</call>
|
||||
|
||||
<expect label="retains target QName"
|
||||
test="f:QName( $x:result ) = f:QName( $test-fn )" />
|
||||
|
||||
<expect label="reduces arity by number of arguments, relative to
|
||||
target function"
|
||||
test="f:arity( $x:result ) = f:arity( $test-fn ) - 1" />
|
||||
|
||||
<expect label="sets arguments by reference"
|
||||
test="f:args( $x:result )[ 1 ] is $args/foo:c" />
|
||||
|
||||
<variable name="rlength" select="count( $x:result )" />
|
||||
|
||||
<expect label="produces length equal to length of constructed
|
||||
sequence, sans adjacent data"
|
||||
test="f:length( $x:result ) = $rlength - 2" />
|
||||
|
||||
<expect label="retains adjacent data by reference"
|
||||
test="$x:result[ $rlength - 1 ] is $args/foo:a
|
||||
and $x:result[ $rlength ] is $args/foo:b" />
|
||||
</scenario>
|
||||
</scenario>
|
||||
</description>
|
||||
|
|
Loading…
Reference in New Issue