`f:set-args' setter and `f:args' accessor implemented

As operations on dynamic function references become more complex, and since
evolution of the reference structure is planned to evolve, maintaining an
abstraction is vital for both grokking and maintenance.
master
Mike Gerwitz 2014-11-30 04:34:44 -05:00
parent cd278c31d4
commit 18c1614777
2 changed files with 220 additions and 6 deletions

View File

@ -104,19 +104,24 @@
<!--
Retrieve the QName of the target dynamic function
Usually, this would match precisely the QName of the target
Usually, this will match precisely the QName of the target
function.
@var{fnreF} must be a valid dynamic function reference.
@i{Implementation details:} This actually represents the QName of
the @emph{application template}, which could differ from the target
function name. One reason this may be the case is to provide a
function alias.
-->
<function name="f:QName" as="xs:QName?">
<param name="fnref" as="element( f:ref )" />
<param name="fnref" as="item()+" />
<variable name="desc" as="element( f:ref )"
select="$fnref[ 1 ]" />
<variable name="target" as="element()?"
select="$fnref/element()[ 1 ]" />
select="$desc/element()[ 1 ]" />
<sequence select="node-name( $target )" />
</function>
@ -143,4 +148,58 @@
<sequence select="$arity" />
</function>
<!--
Retrieve a sequence of the partially applied arguments of
@var{fnref}
The resulting sequence may be empty. @var{fnref} is assumed to be a
valid dynamic function reference; you should verify that assumption
beforehand.
-->
<function name="f:args" as="item()*">
<param name="fnref" as="item()+" />
<sequence select="remove( $fnref, 1 )" />
</function>
<!--
Set partially applied arguments of @var{fnref} to @var{args},
replacing any existing arguments
The arity of @var{fnref} will be adjusted relative to the number of
items in @var{args} such that the resulting dynamic function
reference has the illusion of being its own, new function:
increasing the argument count of @var{fnref} will @emph{decrease}
the arity of the resulting reference and vice versa.
@var{fnref} must be a valid dynamic function reference.
-->
<function name="f:set-args" as="item()+">
<param name="fnref" as="item()+" />
<param name="args" as="item()*" />
<variable name="desc" as="element( f:ref )"
select="$fnref[ 1 ]" />
<!-- this reference may be partially applied; see below arity
adjustment -->
<variable name="target-arity" as="xs:double"
select="$desc/@arity + count( f:args( $fnref ) )" />
<f:ref>
<sequence select="$desc/@*" />
<!-- treat partial applications as their own functions (with their
own arities) -->
<attribute name="arity"
select="$target-arity - count( $args )" />
<sequence select="$desc/*" />
</f:ref>
<sequence select="$args" />
</function>
</stylesheet>

View File

@ -38,6 +38,14 @@
select="f:make-ref( QName( $foo-uri, 'foo:bar' ),
$test-arity )" />
<variable name="args">
<foo:args>
<foo:a />
<foo:b />
<foo:c />
</foo:args>
</variable>
<scenario label="f:make-ref constructor">
<scenario label="when called with a function QName and arity">
@ -169,10 +177,11 @@
<scenario label="f:QName accessor">
<scenario label="given a valid dynamic function reference">
<variable name="foo-qname"
select="QName( $foo-uri, 'foo' )" />
<variable name="foo-qname"
select="QName( $foo-uri, 'foo' )" />
<scenario label="given a valid dynamic function reference with no
argments">
<call function="f:QName">
<param name="fnref"
select="f:make-ref( $foo-qname, 0 )" />
@ -186,6 +195,22 @@
</scenario>
<scenario label="given a valid dynamic function reference with
argments">
<call function="f:QName">
<param name="fnref"
select="f:set-args( f:make-ref( $foo-qname, 0 ),
(1, 2) )" />
</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 label="given a dynamic function reference with no target
node">
<call function="f:QName">
@ -241,4 +266,134 @@
select="$test-arity - 3" />
</scenario>
</scenario>
<scenario label="f:set-args setter">
<scenario label="setting empty args on reference with no args">
<call function="f:set-args">
<param name="fnref" select="$test-fn" />
<param name="args" select="()" />
</call>
<expect label="retains target QName"
test="f:QName( $x:result ) = f:QName( $test-fn )" />
<expect label="retains target arity"
test="f:arity( $x:result ) = f:arity( $test-fn )" />
</scenario>
<scenario label="setting args on reference with no args">
<call function="f:set-args">
<param name="fnref" select="$test-fn" />
<param name="args" select="(1, 2)" />
</call>
<expect label="retains target QName"
test="f:QName( $x:result ) = f:QName( $test-fn )" />
<!-- the function is now partially applied -->
<expect label="reduces arity by number of arguments"
test="f:arity( $x:result ) = f:arity( $test-fn ) - 2" />
</scenario>
<scenario label="clearing args on reference with args">
<call function="f:set-args">
<param name="fnref"
select="f:set-args( $test-fn, (1, 2) )" />
<param name="args"
select="()" />
</call>
<expect label="retains target QName"
test="f:QName( $x:result ) = f:QName( $test-fn )" />
<!-- the function is no longer partially applied -->
<expect label="restores target function arity"
test="f:arity( $x:result ) = f:arity( $test-fn )" />
</scenario>
<scenario label="increasing reference argument count">
<call function="f:set-args">
<param name="fnref"
select="f:set-args( $test-fn, (1, 2) )" />
<param name="args"
select="(1, 2, 3)" />
</call>
<expect label="retains target QName"
test="f:QName( $x:result ) = f:QName( $test-fn )" />
<!-- 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" />
</scenario>
<scenario label="decrasing reference argument count">
<call function="f:set-args">
<param name="fnref"
select="f:set-args( $test-fn, (1, 2) )" />
<param name="args"
select="1" />
</call>
<expect label="retains target QName"
test="f:QName( $x:result ) = f:QName( $test-fn )" />
<!-- 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" />
</scenario>
</scenario>
<scenario label="f:args accessor">
<scenario label="given a new dynamic function reference">
<call function="f:args">
<param name="fnref" select="$test-fn" />
</call>
<expect label="produces empty argument list"
select="()" />
</scenario>
<scenario label="after an initial f:set-args">
<call function="f:args">
<param name="fnref"
select="f:set-args( $test-fn, ($args/foo:a,
$args/foo:b) )" />
</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 other items"
test="count( $x:result ) = 2" />
</scenario>
<scenario label="after a f:set-args on an existing argument list">
<call function="f:args">
<param name="fnref"
select="f:set-args(
f:set-args( $test-fn, ($args/foo:a,
$args/foo:b) ),
($args/foo:b,
$args/foo:c) )" />
</call>
<expect label="returns sequence of arguments by reference,
discarding previously set arguments"
test="$x:result[ 1 ] is $args/foo:b
and $x:result[ 2 ] is $args/foo:c" />
<expect label="returns no other items"
test="count( $x:result ) = 2" />
</scenario>
</scenario>
</description>