1098 lines
32 KiB
XML
1098 lines
32 KiB
XML
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
<!--
|
|
Compiles map fragments to produce a map from source data to a destination
|
|
|
|
Copyright (C) 2016 R-T Specialty, LLC.
|
|
|
|
This file is part of TAME.
|
|
|
|
TAME 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/>.
|
|
|
|
The source fields will be validated at compile-time to ensure that they exist;
|
|
destination fields should be checked by the compiler and/or linker. The linker
|
|
is responsible for assembling the fragments into a working map function.
|
|
|
|
When linking, the special head and tail fragments of the topmost map should be
|
|
used (that is, if A includes B and C, use A).
|
|
|
|
TODO: Just generate a normal package and use the package system;
|
|
this duplicates a lot of logic, and does so piecemeal and poorly.
|
|
|
|
XXX: This is tightly coupled with the Program UI; refactor to support any type
|
|
of source.
|
|
-->
|
|
<stylesheet version="2.0"
|
|
xmlns="http://www.w3.org/1999/XSL/Transform"
|
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
xmlns:lvm="http://www.lovullo.com/rater/map"
|
|
xmlns:lvmc="http://www.lovullo.com/rater/map/compiler"
|
|
xmlns:preproc="http://www.lovullo.com/rater/preproc"
|
|
|
|
xmlns:lv="http://www.lovullo.com/rater"
|
|
xmlns:c="http://www.lovullo.com/calc"
|
|
xmlns:lvp="http://www.lovullo.com">
|
|
|
|
|
|
<param name="map-noterminate" select="'no'" />
|
|
|
|
<!--
|
|
Turn on/off unused param checks
|
|
|
|
This is useful for, say, the global classifier, where a param may end up not
|
|
being used if it's used in external classifications.
|
|
-->
|
|
<param name="unused-param-check" select="'true'" />
|
|
|
|
<!--
|
|
Generate a function that maps a set of inputs to a set of outputs
|
|
-->
|
|
<template match="lvm:program-map" mode="lvmc:compile" priority="8">
|
|
<param name="rater" />
|
|
|
|
<variable name="program-ui" select="
|
|
document( concat( @src, '.xml' ), . )/lvp:program
|
|
" />
|
|
|
|
<variable name="map" select="." />
|
|
|
|
<variable name="vresult">
|
|
<choose>
|
|
<when test="$program-ui">
|
|
<apply-templates select="." mode="lvmc:validate-ui">
|
|
<with-param name="ui" select="$program-ui" />
|
|
</apply-templates>
|
|
</when>
|
|
|
|
<otherwise>
|
|
<message terminate="yes">
|
|
<text>fatal: program UI source XML not found</text>
|
|
</message>
|
|
</otherwise>
|
|
</choose>
|
|
</variable>
|
|
|
|
<if test="
|
|
$vresult/lvmc:terminate
|
|
and $map-noterminate = 'no'
|
|
">
|
|
<message terminate="yes">!!! Terminating due to errors.</message>
|
|
</if>
|
|
|
|
<!-- we need to use an lv-namespaced node so that we are recognized
|
|
consistently with the rest of the system -->
|
|
<variable name="pkg">
|
|
<lv:package name="{$__srcpkg}" lvmc:type="map">
|
|
<!-- XXX: copied from expand.xsl! -->
|
|
<attribute name="name" select="$__srcpkg" />
|
|
<attribute name="__rootpath" select="$__relroot" />
|
|
<attribute name="preproc:name" select="$__srcpkg" />
|
|
|
|
<!-- initial symbol table; full table will be generated below -->
|
|
<call-template name="lvmc:stub-symtable">
|
|
<with-param name="type-prefix" select="'map'" />
|
|
</call-template>
|
|
|
|
<!-- copy source nodes -->
|
|
<apply-templates mode="preproc:expand" select="node()" />
|
|
</lv:package>
|
|
</variable>
|
|
|
|
<!-- process symbol table -->
|
|
<variable name="pkg-with-symtable" as="element( lv:package )">
|
|
<call-template name="preproc:gen-deps">
|
|
<with-param name="pkg" as="element( lv:package )">
|
|
<apply-templates select="$pkg" mode="preproc:sym-discover">
|
|
<with-param name="orig-root" select="." />
|
|
</apply-templates>
|
|
</with-param>
|
|
</call-template>
|
|
</variable>
|
|
|
|
<!-- final result with compiled fragments -->
|
|
<lv:package>
|
|
<sequence select="$pkg-with-symtable/@*,
|
|
$pkg-with-symtable/node()" />
|
|
|
|
<preproc:fragments>
|
|
<!-- special fragment to be output as the head -->
|
|
<preproc:fragment id=":map:___head">
|
|
<!-- use a callback just in case we need to make portions of this async in the
|
|
future -->
|
|
<text>function( input, callback ) {</text>
|
|
<text>var output = {};</text>
|
|
</preproc:fragment>
|
|
|
|
<!-- compile mapped -->
|
|
<apply-templates select="./lvm:*" mode="lvmc:compile">
|
|
<with-param name="symtable" select="$pkg-with-symtable/preproc:symtable" />
|
|
<with-param name="rater" select="$rater" />
|
|
<with-param name="type" select="'map'" />
|
|
</apply-templates>
|
|
|
|
<!-- special fragment to be output as the foot -->
|
|
<preproc:fragment id=":map:___tail">
|
|
<text>callback(output);</text>
|
|
<text>};</text>
|
|
</preproc:fragment>
|
|
</preproc:fragments>
|
|
</lv:package>
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Generate a function that maps a set of rater outputs
|
|
|
|
TODO: This is essentailly the same as the input map; refactor.
|
|
-->
|
|
<template match="lvm:return-map" mode="lvmc:compile" priority="8">
|
|
<param name="rater" />
|
|
|
|
<!-- we don't have use for this right now, but it's required
|
|
by other parts of this system -->
|
|
<variable name="dummy-symtable" as="element( preproc:symtable )">
|
|
<preproc:symtable lvmc:sym-ignore="true" />
|
|
</variable>
|
|
|
|
<variable name="pkg">
|
|
<lv:package name="{$__srcpkg}" lvmc:type="retmap">
|
|
<!-- XXX: copied from expand.xsl! -->
|
|
<attribute name="name" select="$__srcpkg" />
|
|
<attribute name="__rootpath" select="$__relroot" />
|
|
<attribute name="preproc:name" select="$__srcpkg" />
|
|
|
|
<!-- initial symbol table; full table will be generated below -->
|
|
<call-template name="lvmc:stub-symtable">
|
|
<with-param name="type-prefix" select="'retmap'" />
|
|
</call-template>
|
|
|
|
<!-- copy source nodes -->
|
|
<apply-templates mode="preproc:expand" select="node()" />
|
|
</lv:package>
|
|
</variable>
|
|
|
|
<!-- process symbol table -->
|
|
<variable name="pkg-with-symtable" as="element( lv:package )">
|
|
<call-template name="preproc:gen-deps">
|
|
<with-param name="pkg" as="element( lv:package )">
|
|
<apply-templates select="$pkg" mode="preproc:sym-discover">
|
|
<with-param name="orig-root" select="." />
|
|
</apply-templates>
|
|
</with-param>
|
|
</call-template>
|
|
</variable>
|
|
|
|
<!-- final result with compiled fragments -->
|
|
<lv:package>
|
|
<sequence select="$pkg-with-symtable/@*,
|
|
$pkg-with-symtable/node()" />
|
|
|
|
<preproc:fragments>
|
|
<!-- special fragment to be output as the head -->
|
|
<preproc:fragment id=":retmap:___head">
|
|
<!-- use a callback just in case we need to make portions of this async in the
|
|
future -->
|
|
<text>function( input, callback ) {</text>
|
|
<text>var output = {};</text>
|
|
</preproc:fragment>
|
|
|
|
<!-- compile mapped -->
|
|
<apply-templates select="./lvm:*" mode="lvmc:compile">
|
|
<with-param name="symtable" select="$pkg-with-symtable/preproc:symtable" />
|
|
<with-param name="rater" select="$rater" />
|
|
<with-param name="type" select="'retmap'" />
|
|
</apply-templates>
|
|
|
|
<!-- special fragment to be output as the foot -->
|
|
<preproc:fragment id=":retmap:___tail">
|
|
<text>callback(output);</text>
|
|
<text>};</text>
|
|
</preproc:fragment>
|
|
</preproc:fragments>
|
|
</lv:package>
|
|
</template>
|
|
|
|
|
|
<template name="lvmc:stub-symtable">
|
|
<param name="type-prefix" select="'map'" />
|
|
|
|
<preproc:symtable>
|
|
<!-- purposely non-polluting. @ignore-dup is intended to be
|
|
temporary until static generation of these names is resolved;
|
|
this will not cause problems, since the code is always the
|
|
same (future bug pending!) -->
|
|
<preproc:sym name=":{$type-prefix}:___head"
|
|
type="{$type-prefix}:head"
|
|
pollute="true"
|
|
ignore-dup="true" />
|
|
<preproc:sym name=":{$type-prefix}:___tail"
|
|
type="{$type-prefix}:tail"
|
|
ignore-dup="true" />
|
|
</preproc:symtable>
|
|
</template>
|
|
|
|
|
|
<template name="lvmc:mapsym">
|
|
<param name="name" />
|
|
<param name="from" />
|
|
<param name="type-prefix" select="/lv:package/@lvmc:type" />
|
|
|
|
<!-- allow mappings to be overridden after import, which allows defaults
|
|
to be set and then overridden -->
|
|
<preproc:sym name=":{$type-prefix}:{$name}" virtual="true"
|
|
keep="true" type="{$type-prefix}" pollute="true">
|
|
|
|
<!-- for consistency and cleanliness, only copy over if set -->
|
|
<if test="@override='true'">
|
|
<copy-of select="@override" />
|
|
</if>
|
|
|
|
<copy-of select="@affects-eligibility" />
|
|
|
|
<!-- only copy from data if present -->
|
|
<if test="$from">
|
|
<copy-of select="$from" />
|
|
</if>
|
|
</preproc:sym>
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Directly map an input to the output
|
|
-->
|
|
<template match="lvm:pass" mode="lvmc:compile" priority="5">
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="type" as="xs:string" />
|
|
|
|
<preproc:fragment id=":{$type}:{@name}">
|
|
<text>output['</text>
|
|
<value-of select="@name" />
|
|
<text>']=</text>
|
|
<call-template name="lvmc:gen-input-default">
|
|
<with-param name="sym"
|
|
select="lvmc:get-symbol( $symtable, $type, @name, @name )" />
|
|
<with-param name="from" select="@name" />
|
|
</call-template>
|
|
<text>;</text>
|
|
|
|
<!-- newline to make output reading and debugging easier -->
|
|
<text> </text>
|
|
</preproc:fragment>
|
|
</template>
|
|
|
|
<template match="lvm:pass" mode="preproc:symtable" priority="5">
|
|
<call-template name="lvmc:mapsym">
|
|
<with-param name="name" select="@name" />
|
|
<with-param name="from">
|
|
<preproc:from name="{@name}" />
|
|
</with-param>
|
|
</call-template>
|
|
</template>
|
|
|
|
<template match="lvm:pass" mode="preproc:depgen" priority="5">
|
|
<preproc:sym-ref name="{@name}" lax="true" />
|
|
</template>
|
|
|
|
|
|
|
|
<!--
|
|
Maps an input to an output of a different name
|
|
-->
|
|
<template match="lvm:map[ @from ]" mode="lvmc:compile" priority="5">
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="type" />
|
|
|
|
<!-- if src and dest are identical, then it may as well be lvm:pass -->
|
|
<if test="@to = @from">
|
|
<message>
|
|
<text>[map] notice: `</text>
|
|
<value-of select="@to" />
|
|
<!-- TODO: get namespace prefix from name() -->
|
|
<text>' has a destination of the same name; use lvm:pass instead</text>
|
|
</message>
|
|
</if>
|
|
|
|
<preproc:fragment id=":{$type}:{@to}">
|
|
<text>output['</text>
|
|
<value-of select="@to" />
|
|
<text>']=</text>
|
|
<call-template name="lvmc:gen-input-default">
|
|
<with-param name="sym"
|
|
select="lvmc:get-symbol( $symtable, $type, @to, @from )" />
|
|
<with-param name="from" select="@from" />
|
|
</call-template>
|
|
<text>;</text>
|
|
|
|
<!-- newline to make output reading and debugging easier -->
|
|
<text> </text>
|
|
</preproc:fragment>
|
|
</template>
|
|
|
|
<template name="lvmc:sym-from" match="lvm:map[ @from ]" mode="preproc:symtable" priority="5">
|
|
<call-template name="lvmc:mapsym">
|
|
<with-param name="name" select="@to" />
|
|
<with-param name="from">
|
|
<preproc:from name="{@from}" />
|
|
</with-param>
|
|
</call-template>
|
|
</template>
|
|
|
|
<template match="lvm:map[ @from
|
|
and root(.)/@lvmc:type = 'map' ]"
|
|
mode="preproc:depgen" priority="5">
|
|
<!-- to the DSL -->
|
|
<preproc:sym-ref name="{@to}" lax="true" />
|
|
</template>
|
|
|
|
<template match="lvm:map[ @from
|
|
and root(.)/@lvmc:type = 'retmap' ]"
|
|
mode="preproc:depgen" priority="5">
|
|
<!-- from the DSL -->
|
|
<preproc:sym-ref name="{@from}" lax="true" />
|
|
</template>
|
|
|
|
<template match="lvm:map[ @from ]" mode="preproc:depgen" priority="4">
|
|
<message terminate="yes"
|
|
select="'internal error: unhandled lvm:map: ', ." />
|
|
</template>
|
|
|
|
<template match="/*[ @lvmc:type='retmap' ]//lvm:map[ @from ]" mode="preproc:depgen" priority="6">
|
|
<preproc:sym-ref name="{@from}" lax="true" />
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Triggers dependency generation on the source document, which contains far more
|
|
information than our symbol table
|
|
-->
|
|
<template match="preproc:sym[ @type='map' ]" mode="preproc:depgen" priority="6">
|
|
<variable name="name" select="substring-after( @name, ':map:' )" />
|
|
<variable name="pkg" as="element( lv:package )"
|
|
select="root(.)" />
|
|
|
|
<apply-templates mode="preproc:depgen"
|
|
select="$pkg/lvm:*[ @name=$name or @to=$name ]" />
|
|
</template>
|
|
|
|
|
|
<!-- FIXME: this is a cluster -->
|
|
<template match="preproc:sym[ @type='retmap' ]" mode="preproc:depgen" priority="6">
|
|
<variable name="from" as="element( preproc:from )*"
|
|
select="preproc:from" />
|
|
|
|
<if test="$from">
|
|
<variable name="src-name" as="xs:string"
|
|
select="substring-after( @name, ':retmap:' )" />
|
|
|
|
<variable name="name" as="xs:string+"
|
|
select="$from/@name" />
|
|
|
|
<variable name="pkg" as="element( lv:package )"
|
|
select="root(.)" />
|
|
|
|
<variable name="src-node" as="element()+"
|
|
select="$pkg/lvm:*[ @name = $src-name
|
|
or @to = $src-name ]" />
|
|
|
|
<if test="count( $src-node ) gt count( $from )">
|
|
<message terminate="yes"
|
|
select="'error: duplicate source identifier: ',
|
|
$src-name" />
|
|
</if>
|
|
|
|
<apply-templates mode="preproc:depgen"
|
|
select="$src-node" />
|
|
</if>
|
|
</template>
|
|
|
|
|
|
<!--
|
|
These guys have no dependencies; handle them to prevent depgen errors
|
|
-->
|
|
<template match="preproc:sym[ @type='map:head' or @type='map:tail' ]" mode="preproc:depgen" priority="2">
|
|
<!-- do nothing -->
|
|
</template>
|
|
<template match="preproc:sym[ @type='retmap:head' or @type='retmap:tail' ]" mode="preproc:depgen" priority="2">
|
|
<!-- do nothing -->
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Attempt to locate the expected symbol, and blow up otherwise.
|
|
|
|
TODO: The retmap distinction muddies this; refactor to be agnostic
|
|
(onus on caller perhaps).
|
|
-->
|
|
<function name="lvmc:get-symbol" as="element( preproc:sym )?">
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="type" as="xs:string" />
|
|
<param name="to" as="xs:string" />
|
|
<param name="from" as="xs:string?" />
|
|
|
|
<variable name="symname" as="xs:string?"
|
|
select="if ( $type = 'retmap' ) then $from else $to" />
|
|
|
|
<variable name="sym" as="element( preproc:sym )?"
|
|
select="$symtable/preproc:sym[ @name=$symname and @src ]" />
|
|
|
|
<!-- for error message display -->
|
|
<variable name="srcdest" as="xs:string"
|
|
select="if ( $type = 'retmap' ) then 'source' else 'destination'" />
|
|
|
|
<if test="$symname and not( $sym ) and not( $symtable/@lvmc:sym-ignore )">
|
|
<message terminate="yes"
|
|
select="concat(
|
|
'error: unknown ', $srcdest, ' identifier `',
|
|
string( $symname ),
|
|
''' (did you import the package?)' )" />
|
|
</if>
|
|
</function>
|
|
|
|
|
|
<!--
|
|
Generate a direct input mapping or, if a default exists for the field, use the
|
|
default if the input is an empty string.
|
|
-->
|
|
<template name="lvmc:gen-input-default">
|
|
<param name="sym" as="element( preproc:sym )?" />
|
|
<!-- use one or the other; latter takes precedence -->
|
|
<param name="from" />
|
|
<param name="from-str" />
|
|
|
|
<variable name="from-var">
|
|
<choose>
|
|
<when test="$from-str">
|
|
<value-of select="$from-str" />
|
|
</when>
|
|
|
|
<otherwise>
|
|
<text>input['</text>
|
|
<value-of select="$from" />
|
|
<text>']</text>
|
|
</otherwise>
|
|
</choose>
|
|
</variable>
|
|
|
|
<choose>
|
|
<when test="$sym and $sym/@default and not( $sym/@default = '' )">
|
|
<text>set_defaults(</text>
|
|
<value-of select="$from-var" />
|
|
<text>,'</text>
|
|
<value-of select="lvmc:escape-string( $sym/@default )" />
|
|
<text>')</text>
|
|
</when>
|
|
|
|
<otherwise>
|
|
<value-of select="$from-var" />
|
|
</otherwise>
|
|
</choose>
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Maps a static value to the output
|
|
-->
|
|
<template match="lvm:map[ @value ]" mode="lvmc:compile" priority="5">
|
|
<param name="type" as="xs:string" />
|
|
|
|
<preproc:fragment id=":{$type}:{@to}">
|
|
<text>output['</text>
|
|
<value-of select="@to" />
|
|
<text>']='</text>
|
|
<value-of select="normalize-space( @value )" />
|
|
<text>';</text>
|
|
|
|
<!-- newline to make output reading and debugging easier -->
|
|
<text> </text>
|
|
</preproc:fragment>
|
|
</template>
|
|
|
|
<template match="lvm:map[ @value ]" mode="preproc:symtable" priority="5">
|
|
<call-template name="lvmc:mapsym">
|
|
<with-param name="name" select="@to" />
|
|
</call-template>
|
|
</template>
|
|
|
|
|
|
<template match="lvm:map[*]" mode="lvmc:compile" priority="5">
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="rater" />
|
|
<param name="type" as="xs:string"/>
|
|
|
|
<preproc:fragment id=":{$type}:{@to}">
|
|
<text>output['</text>
|
|
<value-of select="@to" />
|
|
<text>']=</text>
|
|
|
|
<apply-templates select="./lvm:*" mode="lvmc:compile">
|
|
<with-param name="symtable" select="$symtable" />
|
|
<with-param name="rater" select="$rater" />
|
|
<with-param name="type" select="$type" />
|
|
</apply-templates>
|
|
|
|
<text>;</text>
|
|
|
|
<!-- newline to make output reading and debugging easier -->
|
|
<text> </text>
|
|
</preproc:fragment>
|
|
</template>
|
|
|
|
<template match="lvm:map[ * ]" mode="preproc:symtable" priority="5">
|
|
<param name="to" select="@to" />
|
|
|
|
<call-template name="lvmc:mapsym">
|
|
<with-param name="name" select="$to" />
|
|
<with-param name="from">
|
|
<for-each select=".//lvm:from">
|
|
<preproc:from name="{@name}" />
|
|
</for-each>
|
|
</with-param>
|
|
</call-template>
|
|
</template>
|
|
|
|
<template match="/*[ @lvmc:type='retmap' ]/lvm:map[ * ]" mode="preproc:symtable" priority="6">
|
|
<variable name="to" select="@to" />
|
|
|
|
<call-template name="lvmc:mapsym">
|
|
<with-param name="name" select="$to" />
|
|
<with-param name="from">
|
|
<for-each select=".//lvm:from">
|
|
<preproc:from name="{@name}" />
|
|
</for-each>
|
|
</with-param>
|
|
</call-template>
|
|
</template>
|
|
|
|
<template match="lvm:map[ * ]" mode="preproc:depgen" priority="5">
|
|
<preproc:sym-ref name="{@to}" lax="true" />
|
|
</template>
|
|
|
|
<template match="lvm:map[ *
|
|
and root(.)/@lvmc:type = 'retmap' ]"
|
|
mode="preproc:depgen" priority="6">
|
|
<for-each select=".//lvm:from">
|
|
<preproc:sym-ref name="{@name}" lax="true" />
|
|
</for-each>
|
|
</template>
|
|
|
|
|
|
|
|
<template match="lvm:const" mode="lvmc:compile" priority="5">
|
|
<text>'</text>
|
|
<value-of select="@value" />
|
|
<text>'</text>
|
|
</template>
|
|
|
|
<template match="lvm:map//lvm:set[@each]" mode="lvmc:compile" priority="5">
|
|
<text>(function(){</text>
|
|
<text>var ret=[];</text>
|
|
<text>var len=input['</text>
|
|
<value-of select="@each" />
|
|
<text>'].length;</text>
|
|
|
|
<text>for(var _i=0;_i<len;_i++){</text>
|
|
<text>var </text>
|
|
<value-of select="@index" />
|
|
<text>=_i;</text>
|
|
|
|
<text>ret[_i]=</text>
|
|
<apply-templates select="./lvm:*" mode="lvmc:compile" />
|
|
<text>;</text>
|
|
<text>}</text>
|
|
|
|
<text>return ret;</text>
|
|
<text>})()</text>
|
|
</template>
|
|
|
|
<template match="lvm:map//lvm:set[@ignore-empty='true']" mode="lvmc:compile" priority="3">
|
|
<text>(function(){</text>
|
|
<text>var ret=[]; var tmp;</text>
|
|
|
|
<for-each select="./lvm:*">
|
|
<text>tmp=</text>
|
|
<apply-templates select="." mode="lvmc:compile" />
|
|
<text>;</text>
|
|
|
|
<text>if(tmp&&tmp!=='0')ret.push(tmp);</text>
|
|
</for-each>
|
|
|
|
<text>return ret;</text>
|
|
<text>})()</text>
|
|
</template>
|
|
|
|
<template match="lvm:map//lvm:set" mode="lvmc:compile" priority="2">
|
|
<text>[</text>
|
|
<for-each select="./lvm:*">
|
|
<if test="position() > 1">
|
|
<text>,</text>
|
|
</if>
|
|
|
|
<apply-templates select="." mode="lvmc:compile" />
|
|
</for-each>
|
|
<text>]</text>
|
|
</template>
|
|
|
|
<template match="lvm:map//lvm:static" mode="lvmc:compile" priority="5">
|
|
<text>'</text>
|
|
<value-of select="@value" />
|
|
<text>'</text>
|
|
</template>
|
|
|
|
|
|
<template match="lvm:map//lvm:from[*]" mode="lvmc:compile" priority="5">
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="type" as="xs:string" />
|
|
|
|
<variable name="to" select="ancestor::lvm:map/@to" />
|
|
|
|
<variable name="nested" as="xs:boolean"
|
|
select="exists( ancestor::lvm:from )" />
|
|
|
|
<!-- XXX: we rely on the side-effect of this blowing up if the
|
|
symbol does not exist -->
|
|
<variable name="sym" as="element( preproc:sym )?"
|
|
select="lvmc:get-symbol( $symtable, $type, $to, @name )" />
|
|
|
|
<!-- kluge to force function call (it's lazy) -->
|
|
<if test="not( $sym )" />
|
|
|
|
<!-- oval = orig val -->
|
|
<text>(function(oval){</text>
|
|
<text>var val = ( (oval||'').length ) ? oval : [oval]; </text>
|
|
<text>var ret = []; </text>
|
|
|
|
<if test="not( $nested )">
|
|
<text>var curindex;</text>
|
|
</if>
|
|
|
|
<text>for ( var i = 0, l = val.length; i<l; i++ ){</text>
|
|
<if test="not( $nested )">
|
|
<text>curindex = i;</text>
|
|
</if>
|
|
|
|
<!-- note that we're casting the value to a string; this is important,
|
|
since case comparisons are strict (===) -->
|
|
<text>switch(''+val[i]){</text>
|
|
<apply-templates mode="lvmc:compile">
|
|
<with-param name="symtable" select="$symtable" />
|
|
<with-param name="type" select="$type" />
|
|
</apply-templates>
|
|
|
|
<if test="not( lvm:default )">
|
|
<text>default: ret.push(</text>
|
|
<choose>
|
|
<!-- give precedence to explicit default -->
|
|
<when test="@default">
|
|
<sequence select="concat( '''',
|
|
lvmc:escape-string( @default ),
|
|
'''' )" />
|
|
</when>
|
|
|
|
<!-- otherwise, generate one -->
|
|
<otherwise>
|
|
<call-template name="lvmc:gen-input-default">
|
|
<with-param name="sym" select="$sym" />
|
|
<with-param name="from-str">
|
|
<text>''+val[i]</text>
|
|
</with-param>
|
|
</call-template>
|
|
</otherwise>
|
|
</choose>
|
|
<text>);</text>
|
|
</if>
|
|
<text>}</text>
|
|
<text>}</text>
|
|
|
|
<choose>
|
|
<when test="@scalar='true'">
|
|
<text>return ret[0]; </text>
|
|
</when>
|
|
|
|
<otherwise>
|
|
<text>return ret; </text>
|
|
</otherwise>
|
|
</choose>
|
|
|
|
<text>})(input['</text>
|
|
<value-of select="@name" />
|
|
<text>']</text>
|
|
|
|
<if test="$nested">
|
|
<text>[curindex]</text>
|
|
</if>
|
|
|
|
<text>)</text>
|
|
</template>
|
|
|
|
|
|
<template match="lvm:map//lvm:from" mode="lvmc:compile" priority="2">
|
|
<variable name="nested" as="xs:boolean"
|
|
select="exists( ancestor::lvm:from )" />
|
|
|
|
<text>input['</text>
|
|
<value-of select="@name" />
|
|
<text>']</text>
|
|
|
|
<choose>
|
|
<when test="@index">
|
|
<text>[</text>
|
|
<value-of select="@index" />
|
|
<text>]</text>
|
|
</when>
|
|
|
|
<when test="$nested">
|
|
<text>[curindex]</text>
|
|
</when>
|
|
</choose>
|
|
</template>
|
|
|
|
|
|
<template match="lvm:from/lvm:default"
|
|
mode="lvmc:compile" priority="5">
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="type" as="xs:string" />
|
|
|
|
<sequence select="concat(
|
|
'default:ret.push(',
|
|
string-join(
|
|
lvmc:concat-compile( element(), (), $symtable, $type),
|
|
'' ),
|
|
');' )" />
|
|
</template>
|
|
|
|
|
|
<template match="lvm:map//lvm:value"
|
|
mode="lvmc:compile" priority="5">
|
|
<sequence select="concat( '''', text(), '''' )" />
|
|
</template>
|
|
|
|
|
|
<template match="lvm:map//lvm:from/lvm:translate" mode="lvmc:compile" priority="5">
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="type" as="xs:string" />
|
|
|
|
<text>case '</text>
|
|
<value-of select="@key" />
|
|
<text>':</text>
|
|
<apply-templates select="." mode="lvmc:compile-translate">
|
|
<with-param name="symtable" select="$symtable" />
|
|
<with-param name="type" select="$type" />
|
|
</apply-templates>
|
|
<text> break;</text>
|
|
</template>
|
|
|
|
|
|
<template match="lvm:translate[ element() ]"
|
|
mode="lvmc:compile-translate" priority="5">
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="type" as="xs:string" />
|
|
|
|
<sequence select="concat(
|
|
'ret.push(',
|
|
string-join(
|
|
lvmc:concat-compile( element(), @empty, $symtable, $type ),
|
|
'' ),
|
|
');' )" />
|
|
</template>
|
|
|
|
|
|
<function name="lvmc:concat-compile" as="xs:string+">
|
|
<param name="children" as="element()+" />
|
|
<param name="default" as="xs:string?" />
|
|
<param name="symtable" as="element( preproc:symtable )" />
|
|
<param name="type" as="xs:string" />
|
|
|
|
<text>(function(){</text>
|
|
<!-- end result should compile into a (dynamic) string -->
|
|
<text>var result=</text>
|
|
<for-each select="$children">
|
|
<if test="position() > 1">
|
|
<text> + </text>
|
|
</if>
|
|
|
|
<apply-templates mode="lvmc:compile" select=".">
|
|
<with-param name="symtable" select="$symtable" />
|
|
<with-param name="type" select="$type" />
|
|
</apply-templates>
|
|
</for-each>
|
|
<text>;</text>
|
|
|
|
<text>return (result === "") ? '</text>
|
|
<sequence select="lvmc:escape-string( $default )" />
|
|
<text>' : result;</text>
|
|
<text>})()</text>
|
|
</function>
|
|
|
|
|
|
<function name="lvmc:escape-string" as="xs:string">
|
|
<param name="str" as="xs:string?" />
|
|
|
|
<sequence select="replace( $str, '''', '\\''' )" />
|
|
</function>
|
|
|
|
|
|
<template match="lvm:translate"
|
|
mode="lvmc:compile-translate" priority="1">
|
|
<text>ret.push('</text>
|
|
<value-of select="normalize-space( @value )" />
|
|
<text>');</text>
|
|
</template>
|
|
|
|
|
|
<template match="text()|comment()" mode="lvmc:compile" priority="1">
|
|
<!-- strip all text and comments -->
|
|
</template>
|
|
|
|
|
|
<template match="*" mode="lvmc:compile" priority="1">
|
|
<message terminate="yes">
|
|
<text>fatal: invalid map: unexpected node </text>
|
|
<apply-templates select="." mode="lvmc:pathout" />
|
|
</message>
|
|
</template>
|
|
|
|
|
|
<template match="lvm:import|lvm:class" mode="lvmc:compile" priority="2">
|
|
<!-- ignore -->
|
|
</template>
|
|
|
|
|
|
<!-- import symbols -->
|
|
<template match="lvm:import[ @path ]" mode="preproc:symtable" priority="5">
|
|
<!-- original root passed to sym-discover -->
|
|
<param name="orig-root" />
|
|
|
|
<variable name="src" as="xs:string"
|
|
select="@path" />
|
|
|
|
<!-- perform symbol import -->
|
|
<call-template name="preproc:symimport">
|
|
<with-param name="orig-root" select="$orig-root" />
|
|
<with-param name="package" select="$src" />
|
|
<with-param name="export" select="'true'" />
|
|
</call-template>
|
|
</template>
|
|
|
|
|
|
<template match="*" mode="lvmc:pathout">
|
|
<if test="parent::*">
|
|
<apply-templates select="parent::*" mode="lvmc:pathout" />
|
|
</if>
|
|
|
|
<text>/</text>
|
|
<value-of select="name()" />
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Outputs a simple pass-through map that may be used if no map is present
|
|
|
|
This simply calls the callback with the given input after creating a new
|
|
object with it as the prototype, ensuring that altered data does not impact
|
|
the original data.
|
|
-->
|
|
<template name="lvmc:dummy-map">
|
|
<param name="name" select="'map'" />
|
|
|
|
<text>function </text>
|
|
<value-of select="$name" />
|
|
<text>( input, callback ) { </text>
|
|
<!-- protect input against potential mutilation from classifier -->
|
|
<text>var prot = function() {}; </text>
|
|
<text>prot.prototype = input; </text>
|
|
<text>callback( new prot() );</text>
|
|
<text> }</text>
|
|
</template>
|
|
|
|
|
|
|
|
<!--
|
|
Validates map between program and the rater, checking for errors that would
|
|
cause significant problems.
|
|
-->
|
|
<template match="lvm:program-map" mode="lvmc:validate-rater">
|
|
<param name="rater" />
|
|
|
|
<variable name="map" select="." />
|
|
|
|
<!--
|
|
Get a list of all fields that have not been mapped
|
|
-->
|
|
<variable name="nomap" select="
|
|
$rater/lv:param[
|
|
not(
|
|
@name=$map//lvm:pass/@name
|
|
or @name=$map//lvm:map/@to
|
|
)
|
|
]
|
|
" />
|
|
|
|
<!-- required and unmapped -->
|
|
<variable name="req-nomap" select="
|
|
$nomap[ not( @default ) or @default='' ]
|
|
" />
|
|
|
|
<!-- warning on non-mapped, but not required -->
|
|
<for-each select="$nomap[ @default ]">
|
|
<message>
|
|
<text>! [map warning] unmapped optional field: </text>
|
|
<value-of select="@name" />
|
|
</message>
|
|
</for-each>
|
|
|
|
<!-- error on required non-mapped -->
|
|
<for-each select="$req-nomap">
|
|
<message>
|
|
<text>!!! [map error] unmapped required field: </text>
|
|
<value-of select="@name" />
|
|
</message>
|
|
</for-each>
|
|
|
|
|
|
<if test="$unused-param-check = 'true'">
|
|
<variable name="unknown" select="
|
|
//lvm:pass[
|
|
not( @name=$rater/lv:param/@name )
|
|
]
|
|
|
|
|
//lvm:map[
|
|
not( @to=$rater/lv:param/@name )
|
|
]
|
|
|
|
|
//lvm:class[
|
|
not( @name=$rater/lv:classify/@as )
|
|
]
|
|
" />
|
|
|
|
<!-- error on unknown -->
|
|
<for-each select="$unknown">
|
|
<message>
|
|
<text>!!! [map error] unknown/unused destination identifier: </text>
|
|
<value-of select="@name|@to" />
|
|
</message>
|
|
</for-each>
|
|
|
|
<if test="count( $unknown )">
|
|
<lvmc:terminate />
|
|
</if>
|
|
</if>
|
|
|
|
|
|
<!-- fail. -->
|
|
<if test="count( $req-nomap )">
|
|
<lvmc:terminate />
|
|
</if>
|
|
</template>
|
|
|
|
|
|
<template match="lvm:program-map" mode="lvmc:validate-ui">
|
|
<param name="ui" />
|
|
|
|
<variable name="knowns" as="xs:string*"
|
|
select="$ui//lvp:question/@id,
|
|
$ui//lvp:external/@id,
|
|
$ui//lvp:calc/@id,
|
|
for $id in $ui//lvp:meta/lvp:field/@id
|
|
return concat( 'meta:', $id )" />
|
|
|
|
|
|
<!-- get a list of unknown source mappings -->
|
|
<!-- TODO: this is a mess -->
|
|
<variable name="unknown-pre" select="
|
|
.//lvm:pass[ not( @name = $knowns ) ],
|
|
.//lvm:map[ @from and not( @from = $knowns ) ],
|
|
.//lvm:from[ not( @name = $knowns ) ],
|
|
.//lvm:set[ @each and not( @each = $knowns ) ]
|
|
" />
|
|
|
|
<variable name="unknown"
|
|
select="$unknown-pre[ not( @novalidate='true' ) ]" />
|
|
|
|
|
|
<!-- error on unknown -->
|
|
<for-each select="$unknown">
|
|
<message>
|
|
<text>!!! [map error] unknown source field: </text>
|
|
<value-of select="@name|@from" />
|
|
</message>
|
|
</for-each>
|
|
|
|
<if test="count( $unknown )">
|
|
<lvmc:terminate />
|
|
</if>
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Outputs source and dest mappings in a common, easily-referenced format useful
|
|
for parsing
|
|
-->
|
|
<template match="lvm:program-map" mode="lvmc:source-dest-map" priority="5">
|
|
<lvmc:map>
|
|
<apply-templates select="./lvm:*" mode="lvmc:source-dest-map" />
|
|
</lvmc:map>
|
|
</template>
|
|
|
|
<template match="lvm:pass" mode="lvmc:source-dest-map" priority="5">
|
|
<lvmc:map from="{@name}" to="{@name}" elig="{@affects-eligibility}" />
|
|
</template>
|
|
|
|
<template match="lvm:map[ @from ]" mode="lvmc:source-dest-map" priority="5">
|
|
<lvmc:map from="{@from}" to="{@to}" elig="{@affects-eligibility}" />
|
|
</template>
|
|
<template match="lvm:map/lvm:from" mode="lvmc:source-dest-map" priority="5">
|
|
<lvmc:map from="{@name}" to="{ancestor::lvm:map/@to}"
|
|
elig="{@affects-eligibility}" />
|
|
</template>
|
|
<template match="lvm:map//lvm:set/lvm:from" mode="lvmc:source-dest-map" priority="4">
|
|
<!-- not included; not a one-to-one mapping -->
|
|
</template>
|
|
|
|
<template match="lvm:map[*]" mode="lvmc:source-dest-map" priority="5">
|
|
<apply-templates select=".//lvm:*" mode="lvmc:source-dest-map" />
|
|
</template>
|
|
|
|
<template match="lvm:map//lvm:set" mode="lvmc:source-dest-map" priority="2">
|
|
<!-- do nothing -->
|
|
</template>
|
|
<template match="lvm:map//lvm:static" mode="lvmc:source-dest-map" priority="2">
|
|
<!-- do nothing -->
|
|
</template>
|
|
<template match="lvm:map//lvm:value" mode="lvmc:source-dest-map" priority="2">
|
|
<!-- do nothing -->
|
|
</template>
|
|
<template match="lvm:map//lvm:translate" mode="lvmc:source-dest-map" priority="2">
|
|
<!-- do nothing -->
|
|
</template>
|
|
<template match="lvm:map[ @value ]" mode="lvmc:source-dest-map" priority="2">
|
|
<!-- no source -->
|
|
</template>
|
|
<template match="lvm:const" mode="lvmc:source-dest-map" priority="2">
|
|
<!-- no source -->
|
|
</template>
|
|
|
|
<template match="lvm:class" mode="lvmc:source-dest-map" priority="2">
|
|
<!-- not applicable -->
|
|
</template>
|
|
|
|
|
|
<template match="*" mode="lvmc:source-dest-map" priority="1">
|
|
<message terminate="yes">
|
|
<text>Unknown node: </text>
|
|
<value-of select="name()" />
|
|
</message>
|
|
</template>
|
|
|
|
</stylesheet>
|