f:apply implemented

Although the general concept was independently discovered, the finer details
of this implementation are motivated by the excellent, comprehensive work of
Dimitre Novatchev[0].

For those of us who either can't, or don't wish to, abaondon XSLT.

[0]: http://conferences.idealliance.org/extreme/html/2006/Novatchev01/EML2006Novatchev01.html
master
Mike Gerwitz 2014-11-21 09:15:24 -05:00
parent 061d4b7e09
commit f50c2299fa
3 changed files with 494 additions and 0 deletions

196
src/apply.xsl 100644
View File

@ -0,0 +1,196 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Dynamic function application
Copyright (C) 2014 LoVullo Associates, Inc.
This file is part of hoxsl.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
This can be used to create higher-order functions.
If you need more arguments than this, then you may want to consider
another approach.
-->
<stylesheet version="2.0"
xmlns="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://www.lovullo.com/xslink/apply">
<function name="f:apply">
<param name="fnref" as="element()" />
<apply-templates select="$fnref" mode="f:apply" />
</function>
<function name="f:apply">
<param name="fnref" as="element()" />
<param name="arg1" />
<apply-templates select="$fnref" mode="f:apply">
<with-param name="arg1" select="$arg1" />
</apply-templates>
</function>
<function name="f:apply">
<param name="fnref" as="element()" />
<param name="arg1" />
<param name="arg2" />
<apply-templates select="$fnref" mode="f:apply">
<with-param name="arg1" select="$arg1" />
<with-param name="arg2" select="$arg2" />
</apply-templates>
</function>
<function name="f:apply">
<param name="fnref" as="element()" />
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<apply-templates select="$fnref" mode="f:apply">
<with-param name="arg1" select="$arg1" />
<with-param name="arg2" select="$arg2" />
<with-param name="arg3" select="$arg3" />
</apply-templates>
</function>
<function name="f:apply">
<param name="fnref" as="element()" />
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<apply-templates select="$fnref" mode="f:apply">
<with-param name="arg1" select="$arg1" />
<with-param name="arg2" select="$arg2" />
<with-param name="arg3" select="$arg3" />
<with-param name="arg4" select="$arg4" />
</apply-templates>
</function>
<function name="f:apply">
<param name="fnref" as="element()" />
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<param name="arg5" />
<apply-templates select="$fnref" mode="f:apply">
<with-param name="arg1" select="$arg1" />
<with-param name="arg2" select="$arg2" />
<with-param name="arg3" select="$arg3" />
<with-param name="arg4" select="$arg4" />
<with-param name="arg5" select="$arg5" />
</apply-templates>
</function>
<function name="f:apply">
<param name="fnref" as="element()" />
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<param name="arg5" />
<param name="arg6" />
<apply-templates select="$fnref" mode="f:apply">
<with-param name="arg1" select="$arg1" />
<with-param name="arg2" select="$arg2" />
<with-param name="arg3" select="$arg3" />
<with-param name="arg4" select="$arg4" />
<with-param name="arg5" select="$arg5" />
<with-param name="arg6" select="$arg6" />
</apply-templates>
</function>
<function name="f:apply">
<param name="fnref" as="element()" />
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<param name="arg5" />
<param name="arg6" />
<param name="arg7" />
<apply-templates select="$fnref" mode="f:apply">
<with-param name="arg1" select="$arg1" />
<with-param name="arg2" select="$arg2" />
<with-param name="arg3" select="$arg3" />
<with-param name="arg4" select="$arg4" />
<with-param name="arg5" select="$arg5" />
<with-param name="arg6" select="$arg6" />
<with-param name="arg7" select="$arg7" />
</apply-templates>
</function>
<function name="f:apply">
<param name="fnref" as="element()" />
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<param name="arg5" />
<param name="arg6" />
<param name="arg7" />
<param name="arg8" />
<apply-templates select="$fnref" mode="f:apply">
<with-param name="arg1" select="$arg1" />
<with-param name="arg2" select="$arg2" />
<with-param name="arg3" select="$arg3" />
<with-param name="arg4" select="$arg4" />
<with-param name="arg5" select="$arg5" />
<with-param name="arg6" select="$arg6" />
<with-param name="arg7" select="$arg7" />
<with-param name="arg8" select="$arg8" />
</apply-templates>
</function>
<!--
Abort processing on unknown function reference
This will occur if no application template is defined for the
given function reference. Override it if you wish to perform
autoloading or what have you.
If you generate the nullary and application template using
apply-gen, then you won't have to worry about this.
-->
<template mode="f:apply"
match="*"
priority="1">
<message terminate="yes">
<text>error: cannot apply unknown function reference: </text>
<copy-of select="." />
</message>
</template>
</stylesheet>

123
test/apply-test.xsl 100644
View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Tests dynamic function application
Copyright (C) 2014 LoVullo Associates, Inc.
This file is part of hoxsl.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<stylesheet version="2.0"
xmlns="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://www.lovullo.com/xslink/apply"
xmlns:foo="http://www.lovullo.com/_junk">
<import href="../src/apply.xsl" />
<template mode="f:apply" match="foo:fn0">
<!-- return something to show that we were called properly -->
<sequence select="0" />
</template>
<template mode="f:apply" match="foo:fn1">
<param name="arg1" />
<sequence select="string($arg1)" />
</template>
<template mode="f:apply" match="foo:fn2">
<param name="arg1" />
<param name="arg2" />
<sequence select="concat($arg1, $arg2)" />
</template>
<template mode="f:apply" match="foo:fn3">
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<sequence select="concat($arg1, $arg2, $arg3)" />
</template>
<template mode="f:apply" match="foo:fn4">
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<sequence select="concat($arg1, $arg2, $arg3, $arg4)" />
</template>
<template mode="f:apply" match="foo:fn5">
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<param name="arg5" />
<sequence select="concat($arg1, $arg2, $arg3, $arg4,
$arg5)" />
</template>
<template mode="f:apply" match="foo:fn6">
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<param name="arg5" />
<param name="arg6" />
<sequence select="concat($arg1, $arg2, $arg3, $arg4,
$arg5, $arg6)" />
</template>
<template mode="f:apply" match="foo:fn7">
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<param name="arg5" />
<param name="arg6" />
<param name="arg7" />
<sequence select="concat($arg1, $arg2, $arg3, $arg4,
$arg5, $arg6, $arg7)" />
</template>
<template mode="f:apply" match="foo:fn8">
<param name="arg1" />
<param name="arg2" />
<param name="arg3" />
<param name="arg4" />
<param name="arg5" />
<param name="arg6" />
<param name="arg7" />
<param name="arg8" />
<sequence select="concat($arg1, $arg2, $arg3, $arg4,
$arg5, $arg6, $arg7, $arg8)" />
</template>
</stylesheet>

175
test/apply.xspec 100644
View File

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Tests dynamic function application
Copyright (C) 2014 LoVullo Associates, Inc.
This file is part of hoxsl.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<description xmlns="http://www.jenitennison.com/xslt/xspec"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://www.lovullo.com/xslink/apply"
xmlns:foo="http://www.lovullo.com/_junk"
stylesheet="apply-test.xsl">
<scenario label="f:apply">
<scenario label="applied to a function reference element">
<variable name="arg"
select="'foo'" />
<call function="f:apply">
<param name="fnref">
<foo:fn1 />
</param>
<param name="arg1" select="$arg" />
</call>
<!-- our test appliaction template simply echoes back its only
argument -->
<expect label="applies application template with mode f:apply"
test="$x:result = ($arg)" />
</scenario>
<scenario label="applying">
<scenario label="nullary function">
<call function="f:apply">
<param name="fnref"><foo:fn0 /></param>
</call>
<expect label="passes each argument"
test="$x:result = 0" />
</scenario>
<scenario label="unary function">
<call function="f:apply">
<param name="fnref"><foo:fn1 /></param>
<param name="arg1" select="1" />
</call>
<expect label="passes each argument"
test="$x:result = '1'" />
</scenario>
<scenario label="binary function">
<call function="f:apply">
<param name="fnref"><foo:fn2 /></param>
<param name="arg1" select="1" />
<param name="arg2" select="2" />
</call>
<expect label="passes each argument"
test="$x:result = '12'" />
</scenario>
<scenario label="ternary function">
<call function="f:apply">
<param name="fnref"><foo:fn3 /></param>
<param name="arg1" select="1" />
<param name="arg2" select="2" />
<param name="arg3" select="3" />
</call>
<expect label="passes each argument"
test="$x:result = '123'" />
</scenario>
<scenario label="4-ary function">
<call function="f:apply">
<param name="fnref"><foo:fn4 /></param>
<param name="arg1" select="1" />
<param name="arg2" select="2" />
<param name="arg3" select="3" />
<param name="arg4" select="4" />
</call>
<expect label="passes each argument"
test="$x:result = '1234'" />
</scenario>
<scenario label="5-ary function">
<call function="f:apply">
<param name="fnref"><foo:fn5 /></param>
<param name="arg1" select="1" />
<param name="arg2" select="2" />
<param name="arg3" select="3" />
<param name="arg4" select="4" />
<param name="arg5" select="5" />
</call>
<expect label="passes each argument"
test="$x:result = '12345'" />
</scenario>
<scenario label="6-ary function">
<call function="f:apply">
<param name="fnref"><foo:fn6 /></param>
<param name="arg1" select="1" />
<param name="arg2" select="2" />
<param name="arg3" select="3" />
<param name="arg4" select="4" />
<param name="arg5" select="5" />
<param name="arg6" select="6" />
</call>
<expect label="passes each argument"
test="$x:result = '123456'" />
</scenario>
<scenario label="7-ary function">
<call function="f:apply">
<param name="fnref"><foo:fn7 /></param>
<param name="arg1" select="1" />
<param name="arg2" select="2" />
<param name="arg3" select="3" />
<param name="arg4" select="4" />
<param name="arg5" select="5" />
<param name="arg6" select="6" />
<param name="arg7" select="7" />
</call>
<expect label="passes each argument"
test="$x:result = '1234567'" />
</scenario>
<scenario label="8-ary function">
<call function="f:apply">
<param name="fnref"><foo:fn8 /></param>
<param name="arg1" select="1" />
<param name="arg2" select="2" />
<param name="arg3" select="3" />
<param name="arg4" select="4" />
<param name="arg5" select="5" />
<param name="arg6" select="6" />
<param name="arg7" select="7" />
<param name="arg8" select="8" />
</call>
<expect label="passes each argument"
test="$x:result = '12345678'" />
</scenario>
</scenario>
</scenario>
</description>