825 lines
26 KiB
XML
825 lines
26 KiB
XML
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
<!--
|
|
Validates a document for correctness in a manner that is beyond XSD
|
|
|
|
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/>.
|
|
|
|
Schematron is not used for this due to certain complexieis. Furthermore, we
|
|
already have the data in a package structure that is easy to use and query
|
|
against.
|
|
|
|
Validations that can be expressed in the XSD will not be included here, unless
|
|
there is significant overlap that would make the XSD representation
|
|
pointlessly incomplete.
|
|
|
|
FIXME: Needs aggresive refactoring after introduction of symbol table, for
|
|
both performance and maintinance.
|
|
-->
|
|
<xsl:stylesheet version="1.0"
|
|
xmlns="http://www.w3.org/1999/xhtml"
|
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
xmlns:lv="http://www.lovullo.com/rater"
|
|
xmlns:ext="http://www.lovullo.com/ext"
|
|
xmlns:c="http://www.lovullo.com/calc"
|
|
xmlns:lvv="http://www.lovullo.com/rater/validate"
|
|
xmlns:sym="http://www.lovullo.com/rater/symbol-map"
|
|
xmlns:preproc="http://www.lovullo.com/rater/preproc">
|
|
|
|
<xsl:import href="validate/domain.xsl" />
|
|
|
|
<xsl:include href="validate/param.xsl" />
|
|
|
|
|
|
<xsl:param name="prohibit-validation" select="'false'" />
|
|
|
|
|
|
<!-- FOR PERFORMANCE: constants may be used for large tables of data -->
|
|
<xsl:template match="lv:const" mode="lvv:validate" priority="9">
|
|
<!-- nothing to be done; constants are merely data declarations -->
|
|
</xsl:template>
|
|
|
|
|
|
<!--
|
|
Perform validations on a given rater/package
|
|
|
|
Validations will be returned as an RTF (result tree fragment), which can be
|
|
converted to a NodeSet using exsl:node-set(). All errors are returned in the
|
|
following format:
|
|
|
|
<lvv:error desc="Description of error type>Error details</lvv:error>
|
|
|
|
@return error RTF
|
|
-->
|
|
<xsl:template match="lv:package" mode="lvv:validate" priority="9">
|
|
<xsl:param name="symbol-map" />
|
|
|
|
<xsl:choose>
|
|
<xsl:when test="$prohibit-validation = 'true'">
|
|
<xsl:message>
|
|
<xsl:text>[validate] prohibited; skipping </xsl:text>
|
|
<xsl:value-of select="local-name()" />
|
|
<xsl:text> </xsl:text>
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text>...</xsl:text>
|
|
</xsl:message>
|
|
</xsl:when>
|
|
|
|
<xsl:otherwise>
|
|
<xsl:message>
|
|
<xsl:text>[validate] validating </xsl:text>
|
|
<xsl:value-of select="local-name()" />
|
|
<xsl:text> </xsl:text>
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text>...</xsl:text>
|
|
</xsl:message>
|
|
|
|
<!-- validate -->
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:otherwise>
|
|
</xsl:choose>
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="preproc:*" mode="lvv:validate" priority="9">
|
|
<!-- well that would just be silly -->
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="lv:package[ not( @program='true' ) ]/lv:yield" mode="lvv:validate" priority="5">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'lv:yield cannot appear within a non-program package'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content" select="." />
|
|
</xsl:call-template>
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="*" mode="lvv:validate" priority="1">
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="lv:template" mode="lvv:validate" priority="9">
|
|
<!-- do not validate templates; we'll only validate expansions -->
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template name="lvv:symbol-chk">
|
|
<xsl:param name="root" />
|
|
<xsl:param name="symbol-map" />
|
|
|
|
<!-- build a symbol list of all used and default symbols -->
|
|
<xsl:variable name="symlist">
|
|
<!-- @sym attributes -->
|
|
<xsl:for-each select="$root//lv:*[@sym]">
|
|
<lvv:sym value="{@sym}" />
|
|
</xsl:for-each>
|
|
|
|
<!-- defaults from mapping document -->
|
|
<xsl:for-each select="$symbol-map/sym:symbol[ not( ./* ) ]">
|
|
<lvv:sym value="{.}" />
|
|
</xsl:for-each>
|
|
</xsl:variable>
|
|
|
|
<!-- error for each restricted node -->
|
|
<xsl:for-each select="
|
|
$root//lv:*[
|
|
@sym = $symbol-map/sym:reserved/sym:reserve/@sym
|
|
]
|
|
">
|
|
|
|
<xsl:variable name="symbol" select="@sym" />
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Symbol is reserved and cannot be used'" />
|
|
<xsl:with-param name="refnode" select="$root//lv:*[@sym=$symbol]" />
|
|
<xsl:with-param name="content" select="$symbol" />
|
|
</xsl:call-template>
|
|
</xsl:for-each>
|
|
|
|
<!-- error for each duplicate node -->
|
|
<xsl:for-each select="
|
|
$symlist/*[
|
|
@value = following-sibling::*/@value
|
|
]
|
|
">
|
|
|
|
<xsl:variable name="symbol" select="@value" />
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Symbol is not unique'" />
|
|
<xsl:with-param name="refnode" select="$root//lv:*[@sym=$symbol]" />
|
|
<xsl:with-param name="content" select="$symbol" />
|
|
</xsl:call-template>
|
|
</xsl:for-each>
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="c:apply[@name]" mode="lvv:validate" priority="5">
|
|
<xsl:variable name="name" select="@name" />
|
|
<xsl:variable name="self" select="." />
|
|
<xsl:variable name="fsym" select="
|
|
root(.)/preproc:symtable/preproc:sym[
|
|
@type='func'
|
|
and @name=$name
|
|
]
|
|
" />
|
|
|
|
<!-- ensure that a function is being applied -->
|
|
<xsl:if test="not( $fsym )">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Applying non-function'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content" select="@name" />
|
|
</xsl:call-template>
|
|
</xsl:if>
|
|
|
|
<!-- check that all required arguments are provided -->
|
|
<xsl:for-each select="
|
|
$fsym/preproc:sym-ref[
|
|
concat( ':', $name, ':', @name ) = ancestor::preproc:symtable/preproc:sym[
|
|
@type='lparam'
|
|
and not( @default )
|
|
]
|
|
]
|
|
">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Missing required argument'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text> for application of </xsl:text>
|
|
<xsl:value-of select="$name" />
|
|
<xsl:text>()</xsl:text>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:for-each>
|
|
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="c:*[ @index ]/c:index" mode="lvv:validate" priority="9">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Ambiguous index specification'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content" select="../@name" />
|
|
</xsl:call-template>
|
|
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
|
|
<!--
|
|
Validate that match @on's exist
|
|
-->
|
|
<xsl:template match="lv:classify[ @as ]//lv:match" mode="lvv:validate" priority="9">
|
|
<xsl:if test="not( @on=root(.)/preproc:symtable/preproc:sym[ @type ]/@name )">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Unknown match @on'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:text>`</xsl:text>
|
|
<xsl:value-of select="@on" />
|
|
<xsl:text>' is unknown for classification </xsl:text>
|
|
|
|
<xsl:variable name="class"
|
|
select="ancestor::lv:classify" />
|
|
<xsl:value-of select="if ( $class/@preproc:generated-from ) then
|
|
$class/@preproc:generated-from
|
|
else
|
|
$class/@as" />
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:if>
|
|
|
|
<xsl:apply-templates select="." mode="lvv:validate-match" />
|
|
</xsl:template>
|
|
|
|
<!--
|
|
Validate that non-numeric value matches actually exist and are constants
|
|
-->
|
|
<xsl:template match="lv:match[@value]" mode="lvv:validate-match" priority="5">
|
|
<xsl:if test="
|
|
not( number( @value ) = @value )
|
|
and not(
|
|
@value=root(.)/preproc:symtable/preproc:sym[
|
|
@type='const'
|
|
]/@name
|
|
)
|
|
">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Unknown match value'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:text>`</xsl:text>
|
|
<xsl:value-of select="@value" />
|
|
<xsl:text>' is unknown for classification </xsl:text>
|
|
|
|
<xsl:variable name="class"
|
|
select="ancestor::lv:classify" />
|
|
<xsl:value-of select="if ( $class/@preproc:generated-from ) then
|
|
$class/@preproc:generated-from
|
|
else
|
|
$class/@as" />
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:if>
|
|
|
|
<xsl:apply-templates mode="lvv:validate-match" />
|
|
</xsl:template>
|
|
|
|
<xsl:template match="lv:match" mode="lvv:validate-match" priority="2">
|
|
<xsl:apply-templates mode="lvv:validate-match" />
|
|
</xsl:template>
|
|
|
|
<!--
|
|
Classification match assumptions must operate only on other classifiers and
|
|
must assume values that the referenced classifier actually matches on
|
|
-->
|
|
<xsl:template match="lv:match/lv:assuming" mode="lvv:validate-match" priority="5">
|
|
<xsl:variable name="on" select="../@on" />
|
|
<xsl:variable name="ref" select="root(.)//lv:classify[ @yields=$on ]" />
|
|
|
|
<!-- assumptions must only operate on variables mentioned in the referenced
|
|
classification -->
|
|
<xsl:for-each select="
|
|
.//lv:that[
|
|
not( @name=$ref//lv:match/@on )
|
|
]
|
|
">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Invalid classification assumption'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text> is not used to classify </xsl:text>
|
|
<xsl:value-of select="$on" />
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:for-each>
|
|
</xsl:template>
|
|
|
|
<xsl:template match="c:*" mode="lvv:validate-match" priority="2">
|
|
<xsl:apply-templates select="." mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
<xsl:template match="*" mode="lvv:validate-match" priority="1">
|
|
<!-- do nothing -->
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="c:value" mode="lvv:validate" priority="5">
|
|
<!-- do nothing; just prevent the below validation from occurring -->
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
<xsl:template match="c:let" mode="lvv:validate" priority="5">
|
|
<!-- do not validate this node itself -->
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
<xsl:template match="c:*[@name or @of]" mode="lvv:validate" priority="2">
|
|
<xsl:variable name="name">
|
|
<xsl:choose>
|
|
<xsl:when test="@of">
|
|
<xsl:value-of select="@of" />
|
|
</xsl:when>
|
|
|
|
<xsl:otherwise>
|
|
<xsl:value-of select="@name" />
|
|
</xsl:otherwise>
|
|
</xsl:choose>
|
|
</xsl:variable>
|
|
|
|
<!-- XXX: have to maintain this list! -->
|
|
<xsl:variable name="nodes" select="
|
|
root(.)//lv:*[
|
|
@name=$name
|
|
or @yields=$name
|
|
or @as=$name
|
|
]
|
|
, root(.)//c:*[
|
|
@generates=$name
|
|
]
|
|
, root(.)//c:values/c:value[ @name=$name ]
|
|
" />
|
|
|
|
<!-- locate function params/let vars -->
|
|
<xsl:variable name="fname" select="
|
|
ancestor::lv:function[
|
|
lv:param[
|
|
@name=$name
|
|
]
|
|
]/@name
|
|
|ancestor::c:apply[
|
|
c:arg[
|
|
@name=$name
|
|
]
|
|
]/@name
|
|
|ancestor::c:let[
|
|
c:values/c:value[
|
|
@name=$name
|
|
]
|
|
]/@name
|
|
" />
|
|
|
|
<!-- if this name references a function parameter, then it takes
|
|
precedence (note that this consequently means that it masks any other
|
|
names that may be globally defined) -->
|
|
<xsl:variable name="sym" select="
|
|
if ( $fname ) then
|
|
root(.)/preproc:symtable/preproc:sym[
|
|
@name=concat( ':', $fname, ':', $name )
|
|
]
|
|
else
|
|
root(.)/preproc:symtable/preproc:sym[
|
|
@name=$name
|
|
]
|
|
" />
|
|
|
|
<xsl:variable name="type" select="$sym/@dtype" />
|
|
|
|
<!-- all calculations must make use of numeric types -->
|
|
<xsl:if test="
|
|
not(
|
|
( $type = 'integer' )
|
|
or ( $type = 'float' )
|
|
or ( $type = 'boolean' )
|
|
or ( ancestor::c:*[ @of and @index=$name ] )
|
|
)
|
|
">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Non-numeric type in calculation'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:value-of select="$name" />
|
|
<xsl:text> is </xsl:text>
|
|
|
|
<xsl:choose>
|
|
<xsl:when test="not( $type ) or $type = ''">
|
|
<xsl:text>undefined</xsl:text>
|
|
</xsl:when>
|
|
|
|
<xsl:otherwise>
|
|
<xsl:text>of type '</xsl:text>
|
|
<xsl:value-of select="$type" />
|
|
<xsl:text>'</xsl:text>
|
|
</xsl:otherwise>
|
|
</xsl:choose>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:if>
|
|
|
|
<xsl:variable name="is-set" select="$sym/@dim" />
|
|
|
|
<xsl:choose>
|
|
<!-- furthermore, if @of is provided, then it must be a set -->
|
|
<xsl:when test="@of">
|
|
<xsl:if test="$is-set = 0">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'@of must reference a set'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content" select="$name" />
|
|
</xsl:call-template>
|
|
</xsl:if>
|
|
</xsl:when>
|
|
|
|
<!-- otherwise, an index is required to reference an item in the set unless
|
|
the value is being passed as an argument of the same set type, or is
|
|
the return value of a function (we assume it to be a return value if it
|
|
is in a tail position) -->
|
|
<!-- TODO: re-add argument param check for sets -->
|
|
<!-- TODO: c:values/c:value/@set check; should also only be allowed in tail -->
|
|
<xsl:otherwise>
|
|
<xsl:choose>
|
|
<xsl:when test="
|
|
( ( not( @index ) or ( @index = '' ) ) and not( ./c:index ) )
|
|
and not( ancestor::lv:match )
|
|
and (
|
|
$is-set != '0'
|
|
and not(
|
|
ancestor::c:arg
|
|
or (
|
|
ancestor::lv:function
|
|
and not( ./* )
|
|
)
|
|
or parent::c:length-of
|
|
or ancestor::c:value[ @set ]
|
|
)
|
|
)
|
|
and not( parent::c:length-of )
|
|
">
|
|
|
|
<xsl:choose>
|
|
<xsl:when test="$sym/@dim = '?'">
|
|
<xsl:message>
|
|
<xsl:text>internal warning: unresolved param </xsl:text>
|
|
<xsl:text>dimension: `</xsl:text>
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text>'</xsl:text>
|
|
</xsl:message>
|
|
</xsl:when>
|
|
|
|
<xsl:otherwise>
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc">
|
|
<xsl:text>Unexpected vector/matrix reference</xsl:text>
|
|
</xsl:with-param>
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text> (did you forget @index?)</xsl:text>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:otherwise>
|
|
</xsl:choose>
|
|
</xsl:when>
|
|
|
|
<!-- TODO: generalize this! -->
|
|
<!-- Matrix references require two indexes, unless referenced within
|
|
contexts -->
|
|
<xsl:when test="
|
|
( number( $is-set ) gt 1 )
|
|
and not( ./c:index[ $is-set ] )
|
|
and not( ancestor::c:arg )
|
|
and not( ancestor::c:let )
|
|
and not( ancestor::c:product[ @dot ] )
|
|
and not( ancestor::c:length-of )
|
|
and not( ancestor::c:cons )
|
|
and not( ancestor::c:cons )
|
|
and not(
|
|
ancestor::lv:function
|
|
and not( ./* )
|
|
)
|
|
">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Invalid matrix specification'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text> requires </xsl:text>
|
|
<xsl:value-of select="$is-set" />
|
|
<xsl:text> indexes (use c:index) in this context</xsl:text>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:when>
|
|
|
|
<!-- ensure that we do not have too many indexes -->
|
|
<xsl:when test="( number( $is-set ) gt 0 ) and ./c:index[ number( $is-set ) + 1 ]">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Invalid vector/matrix specification'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text> may only have </xsl:text>
|
|
<xsl:value-of select="$is-set" />
|
|
<xsl:text> index(s)</xsl:text>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:when>
|
|
|
|
<!-- if we have an index, but we're not dealing with a set, then that is
|
|
also an issue -->
|
|
<xsl:when test="@index and ( $is-set = '' )">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Using index with non-set'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content" select="@name" />
|
|
</xsl:call-template>
|
|
</xsl:when>
|
|
</xsl:choose>
|
|
</xsl:otherwise>
|
|
</xsl:choose>
|
|
|
|
<!-- index references should be defined -->
|
|
<xsl:if test="
|
|
@index and not( local-name() = 'sum' or local-name() = 'product' )
|
|
">
|
|
|
|
<xsl:variable name="index" select="@index" />
|
|
|
|
<!-- search for index definition -->
|
|
<!-- XXX: This also requires knowledge of match and require-param -->
|
|
<xsl:if test="
|
|
not(
|
|
ancestor::c:*[ @index = $index ]
|
|
or ( root(.)//lv:*[ @name = $index ]
|
|
and (
|
|
local-name() != 'match'
|
|
and local-name() != 'require-param'
|
|
)
|
|
)
|
|
)
|
|
">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Undefined index'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content" select="@index" />
|
|
</xsl:call-template>
|
|
</xsl:if>
|
|
</xsl:if>
|
|
|
|
<!-- recursively validate any nested calculations -->
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="c:apply/c:arg[@name]" mode="lvv:validate" priority="5">
|
|
<!-- merely validate its existence -->
|
|
<xsl:variable name="fname" select="parent::c:apply/@name" />
|
|
<xsl:if test="not(
|
|
concat( ':', $fname, ':', @name ) = root(.)/preproc:symtable/preproc:sym[
|
|
@type='lparam'
|
|
and @parent=$fname
|
|
]/@name
|
|
)">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Unknown argument'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:text>Argument `</xsl:text>
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text>' is unknown for function `</xsl:text>
|
|
<xsl:value-of select="$fname" />
|
|
<xsl:text>'</xsl:text>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:if>
|
|
|
|
<!-- recursively validate any nested calculations -->
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="c:product[@dot]" mode="lvv:validate" priority="5">
|
|
<!-- TODO -->
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template mode="lvv:validate" priority="2" match="
|
|
lv:union/lv:typedef[
|
|
./lv:*[1]/@type != preceding-sibling::lv:typedef[1]/lv:*[1]/@type
|
|
]
|
|
">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Union type mismatch'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:text>Expected type '</xsl:text>
|
|
<xsl:value-of select="preceding-sibling::lv:typedef[1]/lv:*[1]/@type" />
|
|
<xsl:text>' for </xsl:text>
|
|
<xsl:value-of select="@name" />
|
|
<xsl:text>, but found '</xsl:text>
|
|
<xsl:value-of select="./lv:*[1]/@type" />
|
|
<xsl:text>'</xsl:text>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
|
|
<!--
|
|
Checks for use of undefined classifications
|
|
-->
|
|
<xsl:template mode="lvv:validate" priority="2"
|
|
match="lv:rate/lv:class[
|
|
not( concat( ':class:', @ref ) = root(.)/preproc:symtable/preproc:sym[ @type='class' ]/@name )
|
|
]">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Unknown classification'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:text>unknown classification '</xsl:text>
|
|
<xsl:value-of select="@ref" />
|
|
<xsl:text>' referenced by </xsl:text>
|
|
<xsl:value-of select="ancestor::lv:rate/@yields" />
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:template>
|
|
|
|
|
|
<!--
|
|
All rate blocks must have non-empty yields
|
|
|
|
This is an awkward error, because it's not possible to identify the
|
|
rate block by name...there is none.
|
|
-->
|
|
<xsl:template mode="lvv:validate" priority="9"
|
|
match="lv:rate[ not( @yields ) or @yields = '' ]">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Unidentifiable rate block'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:text>missing or empty @yields; see document dump</xsl:text>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
</xsl:template>
|
|
|
|
|
|
<!--
|
|
lv:rate blocks have no use for @generates. Since XSDs don't work within
|
|
templates, let's validate that independently. This is particularly
|
|
important for developers unfamiliar with the distinction between lv:rate
|
|
and lv:rate-each.
|
|
-->
|
|
<xsl:template mode="lvv:validate" priority="7"
|
|
match="lv:rate[ @generates ]">
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'lv:rate/@generate'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content"
|
|
select="concat( '`', @yields, ''': lv:rate does ',
|
|
'not support @generates' )" />
|
|
</xsl:call-template>
|
|
</xsl:template>
|
|
|
|
|
|
<!--
|
|
Rate block cannot be nested.
|
|
-->
|
|
<xsl:template mode="lvv:validate" priority="8"
|
|
match="lv:rate[ ancestor::lv:rate ]">
|
|
<xsl:variable name="within" as="element( lv:rate )"
|
|
select="ancestor::lv:rate[1]" />
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Nested rate block'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content"
|
|
select="concat( '`', @yields, ''' cannot be nested ',
|
|
'within `', $within/@yields, '''' )" />
|
|
</xsl:call-template>
|
|
</xsl:template>
|
|
|
|
|
|
<!--
|
|
Throws an error if a generator is requested using unsupported data
|
|
|
|
Specifically, a generator is intended to generate a set from an expression
|
|
while looping over another set. If we're not looping, then we're not
|
|
generating a set. Furthermore, if a child expression was not provided, then
|
|
the set produced would be equivalent to @of, which is useless.
|
|
-->
|
|
<xsl:template mode="lvv:validate"
|
|
match="c:*[ @generates and not( @of and ./c:* ) ]" priority="9">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'Invalid generator'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:text>Cannot create generator '</xsl:text>
|
|
<xsl:value-of select="@generates" />
|
|
<xsl:text>'; generating expressions must contain both @of </xsl:text>
|
|
<xsl:text>and a sub-expression.</xsl:text>
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
|
|
<!--
|
|
Since @generates creates a new variable that can be referenced, it needs
|
|
documentation! Refuse to compile if documentation is not provided. Yeah, we're
|
|
assholes.
|
|
-->
|
|
<xsl:template mode="lvv:validate"
|
|
match="c:*[ @generates and not( @desc ) ]" priority="9">
|
|
|
|
<xsl:call-template name="lvv:error">
|
|
<xsl:with-param name="desc" select="'No generator description'" />
|
|
<xsl:with-param name="refnode" select="." />
|
|
<xsl:with-param name="content">
|
|
<xsl:text>@desc required when creating generator </xsl:text>
|
|
<xsl:value-of select="@generates" />
|
|
</xsl:with-param>
|
|
</xsl:call-template>
|
|
|
|
<xsl:apply-templates mode="lvv:validate" />
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template match="ext:*" mode="lvv:get-path">
|
|
<!-- omit from path output -->
|
|
</xsl:template>
|
|
|
|
<xsl:template match="*" mode="lvv:get-path">
|
|
<xsl:apply-templates select="parent::*" mode="lvv:get-path" />
|
|
<xsl:text>/</xsl:text>
|
|
<xsl:value-of select="name()" />
|
|
|
|
<!-- certain nodes may support path descriptions to aid in determining which
|
|
node is being referenced -->
|
|
<xsl:variable name="desc">
|
|
<xsl:apply-templates select="." mode="lvv:get-path-desc" />
|
|
</xsl:variable>
|
|
|
|
<xsl:if test="$desc != ''">
|
|
<xsl:text>[</xsl:text>
|
|
<xsl:value-of select="$desc" />
|
|
<xsl:text>]</xsl:text>
|
|
</xsl:if>
|
|
</xsl:template>
|
|
|
|
<xsl:template match="lv:rate[ @yields ]" mode="lvv:get-path-desc">
|
|
<xsl:text>@yields=</xsl:text>
|
|
<xsl:value-of select="@yields" />
|
|
</xsl:template>
|
|
|
|
<xsl:template match="c:*[ @name ]" mode="lvv:get-path-desc" priority="5">
|
|
<xsl:text>@name=</xsl:text>
|
|
<xsl:value-of select="@name" />
|
|
</xsl:template>
|
|
<xsl:template match="c:*[ @label ]" mode="lvv:get-path-desc" priority="1">
|
|
<xsl:text>@label=</xsl:text>
|
|
<xsl:value-of select="@label" />
|
|
</xsl:template>
|
|
|
|
<xsl:template match="*" mode="lvv:get-path-desc">
|
|
<!-- no desc by default -->
|
|
</xsl:template>
|
|
|
|
|
|
<xsl:template name="lvv:error">
|
|
<xsl:param name="desc" />
|
|
<xsl:param name="refnode" />
|
|
<xsl:param name="content" />
|
|
|
|
<xsl:variable name="path">
|
|
<xsl:if test="$refnode">
|
|
<xsl:apply-templates select="$refnode" mode="lvv:get-path" />
|
|
</xsl:if>
|
|
</xsl:variable>
|
|
|
|
<lvv:error desc="{$desc}" path="{$path}">
|
|
<xsl:value-of select="$content" />
|
|
</lvv:error>
|
|
</xsl:template>
|
|
|
|
</xsl:stylesheet>
|