Reintroduce legacy classification system, place new behind flag
This largely reintroduces the legacy classification system, but there are a number of things that are not affected by the flag. For example: 1. Alias classifications are still optimized when the flag is off; 2. Classifications without predicates emit slightly different code than before, though their functionality has not changed; 3. There's been a lot of refactoring and minor optimizations that are unaffected by the flag; 4. lv:match/@pattern will now emit a warning; and 5. Cleaning and casting of input data is not gated. This allows us to incrementally migrate to the new system where behavior may be different, but this is admittedly a bit dangerous in that the new system was aggressively tested and reasoned about, so reintroducing the legacy system may combine in unexpected ways.master
parent
5f6cb4cf51
commit
934824b2ee
|
@ -31,179 +31,228 @@
|
|||
may need further adjustment to thwart future optimizations (or a way to
|
||||
explicitly inhibit them).
|
||||
|
||||
<t:describe name="classify">
|
||||
<t:describe name="without predicates">
|
||||
<t:it desc="yields TRUE for conjunction">
|
||||
<classify as="conj-no-pred" yields="conjNoPred"
|
||||
desc="No predicate, conjunction" />
|
||||
|
||||
<t:given name="conjNoPred" />
|
||||
<template name="_class-tests_" desc="Classification system tests">
|
||||
<param name="@system@" desc="SUT (lowercase)" />
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<param name="@systemuc@" desc="SUT (title case)">
|
||||
<param-value name="@system@" ucfirst="true" />
|
||||
</param>
|
||||
|
||||
|
||||
<t:it desc="yields FALSE for disjunction">
|
||||
<classify as="disj-no-pred" yields="disjNoPred"
|
||||
any="true"
|
||||
desc="No predicate, disjunction" />
|
||||
<t:describe name="{@system@} classify">
|
||||
<t:describe name="without predicates">
|
||||
<t:it desc="yields TRUE for conjunction">
|
||||
<classify as="conj-no-pred-{@system@}"
|
||||
yields="conjNoPred{@systemuc@}"
|
||||
desc="No predicate, conjunction" />
|
||||
|
||||
<t:given name="disjNoPred" />
|
||||
<t:given name="conjNoPred{@systemuc@}" />
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
</t:describe>
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
<t:describe name="with scalar predicates">
|
||||
<t:it desc="yields TRUE when scalar value is TRUE">
|
||||
<t:given-classify>
|
||||
<match on="alwaysTrue" />
|
||||
</t:given-classify>
|
||||
<t:it desc="yields FALSE for disjunction">
|
||||
<classify as="disj-no-pred-{@system@}"
|
||||
yields="disjNoPred{@systemuc@}"
|
||||
any="true"
|
||||
desc="No predicate, disjunction" />
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<t:given name="disjNoPred{@systemuc@}" />
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
</t:describe>
|
||||
|
||||
|
||||
<t:it desc="yields FALSE when scalar value is FALSE">
|
||||
<t:given-classify>
|
||||
<match on="neverTrue" />
|
||||
</t:given-classify>
|
||||
<t:describe name="with scalar predicates">
|
||||
<t:it desc="yields TRUE when scalar value is TRUE">
|
||||
<t:given-classify>
|
||||
<match on="alwaysTrue" />
|
||||
</t:given-classify>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
<t:it desc="yields TRUE for all-true scalar conjunction">
|
||||
<t:given-classify>
|
||||
<match on="alwaysTrue" />
|
||||
<match on="neverTrue" value="FALSE" />
|
||||
</t:given-classify>
|
||||
<t:it desc="yields FALSE when scalar value is FALSE">
|
||||
<t:given-classify>
|
||||
<match on="neverTrue" />
|
||||
</t:given-classify>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
<t:it desc="yields TRUE for all-true scalar disjunction">
|
||||
<t:given-classify>
|
||||
<any>
|
||||
<t:it desc="yields TRUE for all-true scalar conjunction">
|
||||
<t:given-classify>
|
||||
<match on="alwaysTrue" />
|
||||
<match on="neverTrue" value="FALSE" />
|
||||
</any>
|
||||
</t:given-classify>
|
||||
</t:given-classify>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
<t:it desc="yields TRUE for single-true scalar disjunction">
|
||||
<t:given-classify>
|
||||
<any>
|
||||
<match on="alwaysTrue" />
|
||||
<match on="neverTrue" />
|
||||
</any>
|
||||
</t:given-classify>
|
||||
<t:it desc="yields TRUE for all-true scalar disjunction">
|
||||
<t:given-classify>
|
||||
<any>
|
||||
<match on="alwaysTrue" />
|
||||
<match on="neverTrue" value="FALSE" />
|
||||
</any>
|
||||
</t:given-classify>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
</t:describe>
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
<t:describe name="with vector predicates">
|
||||
<t:it desc="yields TRUE for all-true element-wise conjunction">
|
||||
<t:given-classify-scalar>
|
||||
<match on="NVEC3" value="ZERO" />
|
||||
<match on="nClass3" value="TRUE" />
|
||||
</t:given-classify-scalar>
|
||||
<t:it desc="yields TRUE for single-true scalar disjunction">
|
||||
<t:given-classify>
|
||||
<any>
|
||||
<match on="alwaysTrue" />
|
||||
<match on="neverTrue" />
|
||||
</any>
|
||||
</t:given-classify>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
</t:describe>
|
||||
|
||||
|
||||
<t:it desc="yields FALSE for some-true element-wise conjunction">
|
||||
<t:given-classify-scalar>
|
||||
<match on="NVEC3" value="ZERO" />
|
||||
<match on="nClass3" value="FALSE" />
|
||||
</t:given-classify-scalar>
|
||||
<t:describe name="with vector predicates">
|
||||
<t:it desc="yields TRUE for all-true element-wise conjunction">
|
||||
<t:given-classify-scalar>
|
||||
<match on="NVEC3" value="ZERO" />
|
||||
<match on="nClass3" value="TRUE" />
|
||||
</t:given-classify-scalar>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
<t:it desc="yields TRUE for some-true element-wise disjunction">
|
||||
<t:given-classify-scalar>
|
||||
<any>
|
||||
<t:it desc="yields FALSE for some-true element-wise conjunction">
|
||||
<t:given-classify-scalar>
|
||||
<match on="NVEC3" value="ZERO" />
|
||||
<match on="nClass3" value="FALSE" />
|
||||
</any>
|
||||
</t:given-classify-scalar>
|
||||
</t:given-classify-scalar>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
<t:it desc="yields FALSE for all-false element-wise disjunction">
|
||||
<t:given-classify-scalar>
|
||||
<any>
|
||||
<match on="NVEC3" value="TRUE" />
|
||||
<match on="nClass3" value="FALSE" />
|
||||
</any>
|
||||
</t:given-classify-scalar>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
The old classification system would interpret missing values as $0$,
|
||||
which could potentially trigger a match.
|
||||
The new classification system will always yield \tparam{FALSE}
|
||||
regardless of predicate when values are undefined.
|
||||
|
||||
<t:describe name="of different lengths">
|
||||
<t:describe name="with legacy classification system">
|
||||
<t:it desc="interprets undefined values as zero during match">
|
||||
<classify as="vec-len-mismatch-conj-legacy"
|
||||
yields="vecLenMismatchConjLegacy"
|
||||
desc="Multi vector length mismatch (legacy)">
|
||||
<!-- actually ZERO for all indexes -->
|
||||
<t:it desc="yields TRUE for some-true element-wise disjunction">
|
||||
<t:given-classify-scalar>
|
||||
<any>
|
||||
<match on="NVEC3" value="ZERO" />
|
||||
<match on="nClass3" value="FALSE" />
|
||||
</any>
|
||||
</t:given-classify-scalar>
|
||||
|
||||
<!-- legacy system, implicitly zero for match -->
|
||||
<match on="NVEC2" value="ZERO" />
|
||||
</classify>
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
<t:given>
|
||||
<c:value-of name="vecLenMismatchConjLegacy" index="#2" />
|
||||
</t:given>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
<t:it desc="yields FALSE for all-false element-wise disjunction">
|
||||
<t:given-classify-scalar>
|
||||
<any>
|
||||
<match on="NVEC3" value="TRUE" />
|
||||
<match on="nClass3" value="FALSE" />
|
||||
</any>
|
||||
</t:given-classify-scalar>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
|
||||
|
||||
The old classification system would interpret missing values as $0$,
|
||||
which could potentially trigger a match.
|
||||
The new classification system will always yield \tparam{FALSE}
|
||||
regardless of predicate when values are undefined.
|
||||
|
||||
<t:describe name="of different lengths">
|
||||
<if name="@system@" eq="legacy">
|
||||
<t:describe name="with legacy classification system">
|
||||
<t:it desc="interprets undefined values as zero during match">
|
||||
<classify as="vec-len-mismatch-conj-{@system@}"
|
||||
yields="vecLenMismatchConj{@systemuc@}"
|
||||
desc="Multi vector length mismatch (legacy)">
|
||||
<!-- actually ZERO for all indexes -->
|
||||
<match on="NVEC3" value="ZERO" />
|
||||
|
||||
<!-- legacy system, implicitly zero for match -->
|
||||
<match on="NVEC2" value="ZERO" />
|
||||
</classify>
|
||||
|
||||
<t:given>
|
||||
<c:value-of name="vecLenMismatchConj{@systemuc@}" index="#2" />
|
||||
</t:given>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="TRUE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
</t:describe>
|
||||
</if>
|
||||
|
||||
|
||||
<if name="@system@" eq="new">
|
||||
<t:describe name="with new classification system">
|
||||
<t:it desc="yields false for conjunction rather than implicit zero">
|
||||
<classify as="vec-len-mismatch-conj-{@system@}"
|
||||
yields="vecLenMismatchConj{@systemuc@}"
|
||||
desc="Multi vector length mismatch (new system)">
|
||||
<!-- actually ZERO for all indexes -->
|
||||
<match on="NVEC3" value="ZERO" />
|
||||
|
||||
<!-- must not be implicitly ZERO for third index -->
|
||||
<match on="NVEC2" value="ZERO" />
|
||||
</classify>
|
||||
|
||||
<t:given>
|
||||
<c:value-of name="vecLenMismatchConj{@systemuc@}" index="#2" />
|
||||
</t:given>
|
||||
|
||||
<t:expect>
|
||||
<t:match-result value="FALSE" />
|
||||
</t:expect>
|
||||
</t:it>
|
||||
</t:describe>
|
||||
</if>
|
||||
</t:describe>
|
||||
</t:describe>
|
||||
</t:describe>
|
||||
</t:describe>
|
||||
</template>
|
||||
|
||||
|
||||
<section title="Legacy System Tests">
|
||||
<t:class-tests system="legacy" />
|
||||
</section>
|
||||
|
||||
<section title="New System Tests">
|
||||
<t:use-new-classification-system />
|
||||
<t:class-tests system="new" />
|
||||
</section>
|
||||
</package>
|
||||
|
|
|
@ -0,0 +1,508 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<!--
|
||||
Compiles rater XML into JavaScript (legacy classification system)
|
||||
|
||||
Copyright (C) 2014-2021 Ryan Specialty Group, 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/>.
|
||||
-->
|
||||
<stylesheet version="2.0"
|
||||
xmlns="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
|
||||
xmlns:lv="http://www.lovullo.com/rater"
|
||||
xmlns:lvp="http://www.lovullo.com"
|
||||
xmlns:c="http://www.lovullo.com/calc"
|
||||
xmlns:w="http://www.lovullo.com/rater/worksheet"
|
||||
xmlns:wc="http://www.lovullo.com/rater/worksheet/compiler"
|
||||
xmlns:compiler="http://www.lovullo.com/rater/compiler"
|
||||
xmlns:calc-compiler="http://www.lovullo.com/calc/compiler"
|
||||
xmlns:preproc="http://www.lovullo.com/rater/preproc"
|
||||
xmlns:util="http://www.lovullo.com/util"
|
||||
xmlns:ext="http://www.lovullo.com/ext">
|
||||
|
||||
|
||||
<function name="compiler:compile-classify-legacy" as="xs:string+">
|
||||
<param name="symtable-map" as="map(*)" />
|
||||
<param name="classify" as="element( lv:classify )" />
|
||||
|
||||
<apply-templates mode="compile-legacy" select="$classify">
|
||||
<with-param name="symtable-map" select="$symtable-map"
|
||||
tunnel="yes" />
|
||||
</apply-templates>
|
||||
</function>
|
||||
|
||||
|
||||
<!--
|
||||
Generate code to perform a classification
|
||||
|
||||
Based on the criteria provided by the classification, generate and store the
|
||||
result of a boolean expression performing the classification using global
|
||||
arguments.
|
||||
|
||||
@return generated classification expression
|
||||
-->
|
||||
<template match="lv:classify" mode="compile-legacy">
|
||||
<param name="symtable-map" as="map(*)" tunnel="yes" />
|
||||
<param name="noclass" />
|
||||
<param name="ignores" />
|
||||
|
||||
<variable name="self" select="." />
|
||||
|
||||
<value-of select="$compiler:nl" />
|
||||
|
||||
<variable name="dest">
|
||||
<text>A['</text>
|
||||
<value-of select="@yields" />
|
||||
<text>']</text>
|
||||
</variable>
|
||||
|
||||
<if test="not( $noclass )">
|
||||
<sequence select="concat( $dest, '=[];', $compiler:nl )" />
|
||||
|
||||
<if test="@preproc:generated='true'">
|
||||
<text>g</text>
|
||||
</if>
|
||||
|
||||
<text>c['</text>
|
||||
<value-of select="@as" />
|
||||
<text>'] = (function(){var result,tmp; </text>
|
||||
</if>
|
||||
|
||||
<!-- locate classification predicates (since lv:any and lv:all are split
|
||||
into their own classifications, matching on any depth ensures we get
|
||||
into any preproc:* nodes as well) -->
|
||||
<variable name="criteria" as="element( lv:match )*"
|
||||
select="./lv:match[
|
||||
not( $ignores )
|
||||
or not( @on=$ignores/@ref ) ]" />
|
||||
|
||||
<variable name="criteria-syms" as="element( preproc:sym )*"
|
||||
select="for $match in $criteria
|
||||
return $symtable-map( $match/@on )" />
|
||||
|
||||
<!-- generate boolean value from match expressions -->
|
||||
<choose>
|
||||
<!-- if classification criteria were provided, then use them -->
|
||||
<when test="$criteria">
|
||||
<variable name="op" as="xs:string"
|
||||
select="compiler:match-group-op( $self )" />
|
||||
|
||||
<text></text>
|
||||
<!-- order matches from highest to lowest dimensions (required for
|
||||
the cmatch algorithm)-->
|
||||
<for-each select="reverse( xs:integer( min( $criteria-syms/@dim ) )
|
||||
to xs:integer( max( $criteria-syms/@dim ) ) )">
|
||||
<apply-templates mode="compile-legacy"
|
||||
select="$criteria[
|
||||
@on = $criteria-syms[
|
||||
@dim = current() ]/@name ]">
|
||||
<with-param name="ignores" select="$ignores" />
|
||||
<with-param name="operator" select="$op" />
|
||||
</apply-templates>
|
||||
</for-each>
|
||||
</when>
|
||||
|
||||
<!-- if no classification criteria, then always true/false -->
|
||||
<otherwise>
|
||||
<!-- this is only useful if $noclass is *not* set -->
|
||||
<if test="not( $noclass )">
|
||||
<choose>
|
||||
<!-- universal -->
|
||||
<when test="not( @any='true' )">
|
||||
<text>tmp = true; </text>
|
||||
</when>
|
||||
|
||||
<!-- existential -->
|
||||
<otherwise>
|
||||
<text>tmp = false; </text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
|
||||
<!-- if @yields was provided, then store the value in a variable of their
|
||||
choice as well (since cmatch will not be done) -->
|
||||
<if test="@yields">
|
||||
<value-of select="$dest" />
|
||||
<choose>
|
||||
<!-- universal -->
|
||||
<when test="not( @any='true' )">
|
||||
<text> = 1;</text>
|
||||
</when>
|
||||
|
||||
<!-- existential -->
|
||||
<otherwise>
|
||||
<text> = 0;</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
<text> return tmp;})();</text>
|
||||
|
||||
<!-- support termination on certain classifications (useful for eligibility
|
||||
and error conditions) -->
|
||||
<if test="@terminate = 'true'">
|
||||
<text>if ( _canterm && </text>
|
||||
|
||||
<if test="@preproc:generated='true'">
|
||||
<text>g</text>
|
||||
</if>
|
||||
<text>c['</text>
|
||||
<value-of select="@as" />
|
||||
<text>'] ) throw Error( '</text>
|
||||
<value-of select="replace( @desc, '''', '\\''' )" />
|
||||
<text>' );</text>
|
||||
|
||||
<value-of select="$compiler:nl" />
|
||||
</if>
|
||||
|
||||
<variable name="sym"
|
||||
select="$symtable-map( $self/@yields )" />
|
||||
|
||||
<!-- if we are not any type of set, then yield the value of the first
|
||||
index (note the $criteria check; see above); note that we do not do
|
||||
not( @set ) here, since that may have ill effects as it implies that
|
||||
the node is not preprocessed -->
|
||||
<!-- TODO: this can be simplified, since @yields is always provided -->
|
||||
<if test="$criteria and @yields and ( $sym/@dim='0' )">
|
||||
<value-of select="$dest" />
|
||||
<text> = </text>
|
||||
<value-of select="$dest" />
|
||||
<text>[0];</text>
|
||||
|
||||
<value-of select="$compiler:nl" />
|
||||
</if>
|
||||
</template>
|
||||
|
||||
|
||||
<!--
|
||||
Generate code asserting a match
|
||||
|
||||
Siblings are joined by default with ampersands to denote an AND relationship,
|
||||
unless overridden.
|
||||
|
||||
@return generated match code
|
||||
-->
|
||||
<template match="lv:match" mode="compile-legacy" priority="1">
|
||||
<param name="symtable-map" as="map(*)" tunnel="yes" />
|
||||
|
||||
<!-- default to all matches being required -->
|
||||
<param name="operator" select="'&&'" />
|
||||
<param name="yields" select="../@yields" />
|
||||
|
||||
<variable name="name" select="@on" />
|
||||
|
||||
<variable name="sym-on" as="element( preproc:sym )"
|
||||
select="$symtable-map( $name )" />
|
||||
|
||||
<text> tmp = </text>
|
||||
|
||||
<variable name="input-raw">
|
||||
<choose>
|
||||
<!-- if we have assumptions, then we'll be recalculating (rather than
|
||||
referencing) an existing classification -->
|
||||
<when test="lv:assuming">
|
||||
<text>_cassume</text>
|
||||
</when>
|
||||
|
||||
<otherwise>
|
||||
<choose>
|
||||
<when test="$sym-on/@type = 'const'">
|
||||
<text>consts</text>
|
||||
</when>
|
||||
<otherwise>
|
||||
<text>A</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
<text>['</text>
|
||||
<value-of select="translate( @on, "'", '' )" />
|
||||
<text>']</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</variable>
|
||||
|
||||
<!-- yields (if not set, generate one so that cmatches still works properly)
|
||||
-->
|
||||
<variable name="yieldto">
|
||||
<call-template name="compiler:gen-match-yieldto">
|
||||
<with-param name="yields" select="$yields" />
|
||||
</call-template>
|
||||
</variable>
|
||||
|
||||
<!-- the input value -->
|
||||
<variable name="input">
|
||||
<choose>
|
||||
<when test="@scalar = 'true'">
|
||||
<text>stov( </text>
|
||||
<value-of select="$input-raw" />
|
||||
<text>, ( ( </text>
|
||||
<value-of select="$yieldto" />
|
||||
<!-- note that we default to 1 so that there is at least a single
|
||||
element (which will be the case of the scalar is the first match)
|
||||
in a given classification; the awkward inner [] is to protect
|
||||
against potentially undefined values and will hopefully never
|
||||
happen, and the length is checked on the inner grouping rather than
|
||||
on the outside of the entire expression to ensure that it will
|
||||
yield the intended result if yieldto.length === 0 -->
|
||||
<text> || [] ).length || 1 ) )</text>
|
||||
</when>
|
||||
|
||||
<otherwise>
|
||||
<value-of select="$input-raw" />
|
||||
</otherwise>
|
||||
</choose>
|
||||
</variable>
|
||||
|
||||
<if test="lv:assuming">
|
||||
<text>(function(){</text>
|
||||
<!-- initialize variable (ensuring that the closure we're about to generate
|
||||
will properly assign the value rather than encapsulate it) -->
|
||||
<text>var </text>
|
||||
<value-of select="$input-raw" />
|
||||
<text>; </text>
|
||||
|
||||
<!-- generate assumptions and recursively generate the referenced
|
||||
classification -->
|
||||
<apply-templates select="." mode="compile-match-assumptions">
|
||||
<with-param name="result-var" select="$input-raw" />
|
||||
</apply-templates>
|
||||
<text>; return </text>
|
||||
</if>
|
||||
|
||||
<!-- invoke the classification matcher on this input -->
|
||||
<text>anyValue( </text>
|
||||
<value-of select="$input" />
|
||||
<text>, </text>
|
||||
|
||||
<!-- TODO: error if multiple; also, refactor -->
|
||||
<choose>
|
||||
<when test="@value">
|
||||
<variable name="value" select="@value" />
|
||||
<variable name="sym" as="element( preproc:sym )?"
|
||||
select="$symtable-map( $value )" />
|
||||
|
||||
<choose>
|
||||
<!-- value unavailable (TODO: vector/matrix support) -->
|
||||
<when test="$sym and not( $sym/@value )">
|
||||
<message>
|
||||
<text>[jsc] !!! bad classification match: `</text>
|
||||
<value-of select="$value" />
|
||||
<text>' is not a scalar constant</text>
|
||||
</message>
|
||||
</when>
|
||||
|
||||
<!-- simple constant -->
|
||||
<when test="$sym and @value">
|
||||
<value-of select="$sym/@value" />
|
||||
</when>
|
||||
|
||||
<otherwise>
|
||||
<text>'</text>
|
||||
<!-- TODO: Should we disallow entirely? -->
|
||||
<message>
|
||||
<text>[jsc] warning: static classification match '</text>
|
||||
<value-of select="$value" />
|
||||
<text>' in </text>
|
||||
<value-of select="ancestor::lv:classify[1]/@as" />
|
||||
<text>; use calculation predicate or constant instead</text>
|
||||
</message>
|
||||
|
||||
<value-of select="$value" />
|
||||
<text>'</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</when>
|
||||
|
||||
<when test="@pattern">
|
||||
<text>function( val ) { </text>
|
||||
<text>return /</text>
|
||||
<value-of select="@pattern" />
|
||||
<text>/.test( val );</text>
|
||||
<text> }</text>
|
||||
</when>
|
||||
|
||||
<when test="./c:*">
|
||||
<text>function( val, __$$i ) { </text>
|
||||
<text>return ( </text>
|
||||
<for-each select="./c:*">
|
||||
<if test="position() > 1">
|
||||
<text disable-output-escaping="yes"> && </text>
|
||||
</if>
|
||||
|
||||
<text>( val </text>
|
||||
<apply-templates select="." mode="compile-calc-when" />
|
||||
<text> ) </text>
|
||||
</for-each>
|
||||
<text>);</text>
|
||||
<text>}</text>
|
||||
</when>
|
||||
|
||||
<otherwise>
|
||||
<apply-templates select="." mode="compiler:match-anyof-legacy" />
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
<text>, </text>
|
||||
<value-of select="$yieldto" />
|
||||
<text>, </text>
|
||||
|
||||
<!-- if this match is part of a classification that should yield a matrix,
|
||||
then force a matrix set -->
|
||||
<choose>
|
||||
<when test="ancestor::lv:classify/@set = 'matrix'">
|
||||
<text>true</text>
|
||||
</when>
|
||||
<otherwise>
|
||||
<text>false</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
<text>, </text>
|
||||
<choose>
|
||||
<when test="parent::lv:classify/@any='true'">
|
||||
<text>false</text>
|
||||
</when>
|
||||
<otherwise>
|
||||
<text>true</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
<!-- for debugging -->
|
||||
<if test="$debug-id-on-stack">
|
||||
<text>/*!+*/,"</text>
|
||||
<value-of select="$input" />
|
||||
<text>"/*!-*/</text>
|
||||
</if>
|
||||
|
||||
<!-- end of anyValue() call -->
|
||||
<text> ) </text>
|
||||
|
||||
<!-- end of assuming function call -->
|
||||
<if test="lv:assuming">
|
||||
<text>})()</text>
|
||||
</if>
|
||||
|
||||
<text>;</text>
|
||||
|
||||
<text>/*!+*/( D['</text>
|
||||
<value-of select="@_id" />
|
||||
<text>'] || ( D['</text>
|
||||
<value-of select="@_id" />
|
||||
<text>'] = [] ) ).push( tmp );/*!-*/ </text>
|
||||
|
||||
|
||||
<text>result = </text>
|
||||
<choose>
|
||||
<!-- join with operator if not first in set -->
|
||||
<when test="position() > 1">
|
||||
<text>result </text>
|
||||
<value-of select="$operator" />
|
||||
<text> tmp;</text>
|
||||
</when>
|
||||
|
||||
<otherwise>
|
||||
<text>tmp;</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</template>
|
||||
|
||||
|
||||
<!--
|
||||
Handles the special "float" domain
|
||||
|
||||
Rather than the impossible task of calculating all possible floats and
|
||||
asserting that the given values is within that set, the obvious task is to
|
||||
assert whether or not the value is logically capable of existing in such a
|
||||
set based on a definition of such a set.
|
||||
|
||||
See also "integer"
|
||||
-->
|
||||
<template match="lv:match[ @anyOf='float' ]" mode="compiler:match-anyof-legacy" priority="5">
|
||||
<!-- ceil(x) - floor(x) = [ x is not an integer ] -->
|
||||
<text>function( val ) {</text>
|
||||
<text>return ( typeof +val === 'number' ) </text>
|
||||
<text disable-output-escaping="yes">&& </text>
|
||||
<!-- note: greater than or equal to, since we want to permit integers as
|
||||
well -->
|
||||
<text disable-output-escaping="yes">( Math.ceil( val ) >= Math.floor( val ) )</text>
|
||||
<text>;</text>
|
||||
<text>}</text>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
Handles the special "float" domain
|
||||
|
||||
Rather than the impossible task of calculating all possible integers and
|
||||
asserting that the given values is within that set, the obvious task is to
|
||||
assert whether or not the value is logically capable of existing in such a
|
||||
set based on a definition of such a set.
|
||||
|
||||
See also "float"
|
||||
-->
|
||||
<template match="lv:match[ @anyOf='integer' ]" mode="compiler:match-anyof-legacy" priority="5">
|
||||
<!-- ceil(x) - floor(x) = [ x is not an integer ] -->
|
||||
<text>function( val ) {</text>
|
||||
<text>return ( typeof +val === 'number' ) </text>
|
||||
<text disable-output-escaping="yes">&& </text>
|
||||
<text>( Math.floor( val ) === Math.ceil( val ) )</text>
|
||||
<text>;</text>
|
||||
<text>}</text>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
Handles matching on an empty set
|
||||
|
||||
This is useful for asserting against fields that may have default values,
|
||||
since in such a case an empty value would be permitted.
|
||||
-->
|
||||
<template match="lv:match[ @anyOf='empty' ]" mode="compiler:match-anyof-legacy" priority="5">
|
||||
<!-- ceil(x) - floor(x) = [ x is not an integer ] -->
|
||||
<text>function( val ) {</text>
|
||||
<text>return ( val === '' ) </text>
|
||||
<text>|| ( val === undefined ) || ( val === null )</text>
|
||||
<text>;</text>
|
||||
<text>}</text>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
Uh oh. Hopefully this never happens; will throw an exception if a type is
|
||||
defined as a base type (using typedef), but is not handled by the compiler.
|
||||
-->
|
||||
<template match="lv:match[ @anyOf=root(.)//lv:typedef[ ./lv:base-type ]/@name ]"
|
||||
mode="compiler:match-anyof-legacy" priority="3">
|
||||
|
||||
<text>function( val ) {</text>
|
||||
<text>throw Error( 'CRITICAL: Unhandled base type: </text>
|
||||
<value-of select="@anyOf" />
|
||||
<text>' );</text>
|
||||
<text>}</text>
|
||||
</template>
|
||||
|
||||
<!--
|
||||
Used for user-defined domains
|
||||
-->
|
||||
<template match="lv:match[ @anyOf ]" mode="compiler:match-anyof-legacy" priority="1">
|
||||
<text>types['</text>
|
||||
<value-of select="@anyOf" />
|
||||
<text>'].values</text>
|
||||
</template>
|
||||
|
||||
|
||||
</stylesheet>
|
|
@ -39,6 +39,8 @@
|
|||
xmlns:util="http://www.lovullo.com/util"
|
||||
xmlns:ext="http://www.lovullo.com/ext">
|
||||
|
||||
<!-- legacy classification system -->
|
||||
<include href="js-legacy.xsl" />
|
||||
|
||||
<!-- calculation compiler -->
|
||||
<include href="js-calc.xsl" />
|
||||
|
@ -629,15 +631,33 @@
|
|||
</function>
|
||||
|
||||
|
||||
<template mode="compile" priority="6"
|
||||
match="lv:classify[ compiler:use-legacy-classify(.) ]">
|
||||
<param name="symtable-map" as="map(*)" tunnel="yes" />
|
||||
|
||||
<sequence select="concat(
|
||||
$compiler:nl,
|
||||
'/*!lc*/',
|
||||
string-join(
|
||||
compiler:compile-classify-legacy( $symtable-map, . ),
|
||||
'' ),
|
||||
'/*lc!*/' )" />
|
||||
</template>
|
||||
|
||||
|
||||
<template match="lv:classify" mode="compile" priority="5">
|
||||
<param name="symtable-map" as="map(*)" tunnel="yes" />
|
||||
|
||||
|
||||
<message select="concat( 'internal: new: ', @as )" />
|
||||
<sequence select="compiler:compile-classify-assign( $symtable-map, . )" />
|
||||
</template>
|
||||
|
||||
|
||||
<template mode="compile" priority="8"
|
||||
match="lv:classify[ @preproc:inline='true' ]">
|
||||
match="lv:classify[
|
||||
@preproc:inline='true'
|
||||
and not( compiler:use-legacy-classify(.) ) ]">
|
||||
<!-- emit nothing; it'll be inlined at the match site -->
|
||||
</template>
|
||||
|
||||
|
@ -680,6 +700,19 @@
|
|||
</function>
|
||||
|
||||
|
||||
<function name="compiler:use-legacy-classify" as="xs:boolean">
|
||||
<param name="classify" as="element( lv:classify )" />
|
||||
|
||||
<variable name="flagname" as="xs:string"
|
||||
select="'___feature-newclassify'" />
|
||||
|
||||
<sequence select="empty(
|
||||
( $classify | $classify/ancestor::* )
|
||||
/preceding-sibling::preproc:tpl-meta[
|
||||
@name=$flagname and @value = '1' ] )" />
|
||||
</function>
|
||||
|
||||
|
||||
<function name="compiler:compile-classify-assign" as="xs:string">
|
||||
<param name="symtable-map" as="map(*)" />
|
||||
<param name="classify" as="element( lv:classify )" />
|
||||
|
@ -1230,6 +1263,142 @@
|
|||
</function>
|
||||
|
||||
|
||||
<!--
|
||||
Generate code asserting a match
|
||||
|
||||
Siblings are joined by default with ampersands to denote an AND relationship,
|
||||
unless overridden.
|
||||
|
||||
@return generated match code
|
||||
-->
|
||||
<template match="lv:match" mode="compile" priority="1">
|
||||
<param name="symtable-map" as="map(*)" tunnel="yes" />
|
||||
|
||||
<!-- default to all matches being required -->
|
||||
<param name="operator" select="'&&'" />
|
||||
<param name="yields" select="../@yields" />
|
||||
|
||||
<variable name="name" select="@on" />
|
||||
|
||||
<text> tmp=</text>
|
||||
|
||||
<variable name="input-raw" as="xs:string"
|
||||
select="compiler:match-name-on( $symtable-map, . )" />
|
||||
|
||||
<!-- yields (if not set, generate one so that cmatches still works properly)
|
||||
-->
|
||||
<variable name="yieldto">
|
||||
<call-template name="compiler:gen-match-yieldto">
|
||||
<with-param name="yields" select="$yields" />
|
||||
</call-template>
|
||||
</variable>
|
||||
|
||||
<!-- the input value -->
|
||||
<variable name="input">
|
||||
<choose>
|
||||
<when test="@scalar = 'true'">
|
||||
<text>stov( </text>
|
||||
<value-of select="$input-raw" />
|
||||
<text>, ((</text>
|
||||
<value-of select="$yieldto" />
|
||||
<!-- note that we default to 1 so that there is at least a single
|
||||
element (which will be the case of the scalar is the first match)
|
||||
in a given classification; the awkward inner [] is to protect
|
||||
against potentially undefined values and will hopefully never
|
||||
happen, and the length is checked on the inner grouping rather than
|
||||
on the outside of the entire expression to ensure that it will
|
||||
yield the intended result if yieldto.length === 0 -->
|
||||
<text>||[]).length||1))</text>
|
||||
</when>
|
||||
|
||||
<otherwise>
|
||||
<value-of select="$input-raw" />
|
||||
</otherwise>
|
||||
</choose>
|
||||
</variable>
|
||||
|
||||
<!-- invoke the classification matcher on this input -->
|
||||
<text>anyValue( </text>
|
||||
<value-of select="$input" />
|
||||
<text>, </text>
|
||||
|
||||
<!-- TODO: error if multiple; also, refactor -->
|
||||
<choose>
|
||||
<when test="@value">
|
||||
<value-of select="compiler:match-value( $symtable-map, . )" />
|
||||
</when>
|
||||
|
||||
<when test="@pattern">
|
||||
<text>function(val) {</text>
|
||||
<text>return /</text>
|
||||
<value-of select="@pattern" />
|
||||
<text>/.test(val);</text>
|
||||
<text>}</text>
|
||||
</when>
|
||||
|
||||
<when test="./c:*">
|
||||
<text>function(val, __$$i) { </text>
|
||||
<text>return (</text>
|
||||
<for-each select="./c:*">
|
||||
<if test="position() > 1">
|
||||
<text disable-output-escaping="yes"> && </text>
|
||||
</if>
|
||||
|
||||
<text>(val </text>
|
||||
<apply-templates select="." mode="compile-calc-when" />
|
||||
<text>)</text>
|
||||
</for-each>
|
||||
<text>);</text>
|
||||
<text>}</text>
|
||||
</when>
|
||||
|
||||
<otherwise>
|
||||
<apply-templates select="." mode="compiler:match-anyof" />
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
<text>, </text>
|
||||
<value-of select="$yieldto" />
|
||||
<text>, </text>
|
||||
|
||||
<!-- if this match is part of a classification that should yield a matrix,
|
||||
then force a matrix set -->
|
||||
<choose>
|
||||
<when test="ancestor::lv:classify/@set = 'matrix'">
|
||||
<text>true</text>
|
||||
</when>
|
||||
<otherwise>
|
||||
<text>false</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
<text>, </text>
|
||||
<choose>
|
||||
<when test="parent::lv:classify/@any='true'">
|
||||
<text>false</text>
|
||||
</when>
|
||||
<otherwise>
|
||||
<text>true</text>
|
||||
</otherwise>
|
||||
</choose>
|
||||
|
||||
<!-- for debugging -->
|
||||
<if test="$debug-id-on-stack">
|
||||
<text>/*!+*/,"</text>
|
||||
<value-of select="$input" />
|
||||
<text>"/*!-*/</text>
|
||||
</if>
|
||||
|
||||
<!-- end of anyValue() call -->
|
||||
<text>);</text>
|
||||
|
||||
<text>/*!+*/(D['</text>
|
||||
<value-of select="@_id" />
|
||||
<text>']||(D['</text>
|
||||
<value-of select="@_id" />
|
||||
<text>']=[])).push(tmp);/*!-*/ </text>
|
||||
</template>
|
||||
|
||||
<template name="compiler:gen-match-yieldto">
|
||||
<param name="yields" />
|
||||
|
||||
|
@ -1822,29 +1991,190 @@
|
|||
function cgtei(y) { return function (x, i) { return +(x >= (y[i]||0)); }; }
|
||||
function cltei(y) { return function (x, i) { return +(x <= (y[i]||0)); }; }
|
||||
|
||||
|
||||
/**
|
||||
* Return the length of the longest set
|
||||
* Checks for matches against values for any param value
|
||||
*
|
||||
* Provide each set as its own argument.
|
||||
* A single successful match will result in a successful classification.
|
||||
*
|
||||
* @return number length of longest set
|
||||
* For an explanation and formal definition of this algorithm, please see
|
||||
* the section entitled "Classification Match (cmatch) Algorithm" in the
|
||||
* manual.
|
||||
*
|
||||
* @param {Array|string} param value or set of values to check
|
||||
* @param {Array|string} values or set of values to match against
|
||||
* @param {Object} yield_to object to yield into
|
||||
* @param {boolean} clear when true, AND results; otherwise, OR
|
||||
*
|
||||
* @return {boolean} true if any match is found, otherwise false
|
||||
*/
|
||||
function longerOf()
|
||||
function anyValue( param, values, yield_to, ismatrix, clear, _id )
|
||||
{
|
||||
var i = arguments.length,
|
||||
len = 0;
|
||||
// convert everything to an array if needed (we'll assume all objects to
|
||||
// be arrays; Array.isArray() is ES5-only) to make them easier to work
|
||||
// with
|
||||
if ( !Array.isArray( param ) )
|
||||
{
|
||||
param = [ param ];
|
||||
}
|
||||
|
||||
var values_orig = values;
|
||||
if ( typeof values !== 'object' )
|
||||
{
|
||||
// the || 0 here ensures that non-values are treated as 0, as
|
||||
// mentioned in the specification
|
||||
values = [ values || 0 ];
|
||||
}
|
||||
else
|
||||
{
|
||||
var tmp = [];
|
||||
for ( var v in values )
|
||||
{
|
||||
tmp.push( v );
|
||||
}
|
||||
|
||||
values = tmp;
|
||||
}
|
||||
|
||||
// if no yield var name was provided, we'll just be storing in a
|
||||
// temporary array which will be discarded when it goes out of scope
|
||||
// (this is the result vector in the specification)
|
||||
var store = yield_to || [];
|
||||
|
||||
var i = param.length,
|
||||
found = false,
|
||||
scalar = ( i === 0 ),
|
||||
u = ( store.length === 0 ) ? clear : false;
|
||||
|
||||
while ( i-- )
|
||||
{
|
||||
var thislen = arguments[ i ].length;
|
||||
// these var names are as they appear in the algorithm---temporary,
|
||||
// and value
|
||||
var t,
|
||||
v = returnOrReduceOr( store[ i ], u );
|
||||
|
||||
if ( thislen > len )
|
||||
// recurse on vectors
|
||||
if ( Array.isArray( param[ i ] ) || Array.isArray( store[ i ] ) )
|
||||
{
|
||||
len = thislen;
|
||||
var r = deepClone( store[ i ] || [] );
|
||||
if ( !Array.isArray( r ) )
|
||||
{
|
||||
r = [ r ];
|
||||
}
|
||||
|
||||
var rfound = !!anyValue( param[ i ], values_orig, r, false, clear, _id );
|
||||
found = ( found || rfound );
|
||||
|
||||
if ( Array.isArray( store[ i ] )
|
||||
|| ( store[ i ] === undefined )
|
||||
)
|
||||
{
|
||||
// we do not want to reduce; this is the match that we are
|
||||
// interested in
|
||||
store[ i ] = r;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = returnOrReduceOr( r, clear );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have a scalar, folks!
|
||||
scalar = true;
|
||||
t = anyPredicate( values, ( param[ i ] || 0 ), i );
|
||||
}
|
||||
|
||||
store[ i ] = +( ( clear )
|
||||
? ( v && t )
|
||||
: ( v || t )
|
||||
);
|
||||
|
||||
// equivalent of "Boolean Classification Match" section of manual
|
||||
found = ( found || !!store[ i ] );
|
||||
}
|
||||
|
||||
if ( store.length > param.length )
|
||||
{
|
||||
var sval = ( scalar ) ? anyPredicate( values, param[0] ) : null;
|
||||
if ( typeof sval === 'function' )
|
||||
{
|
||||
// pass the scalar value to the function
|
||||
sval = values[0]( param[0] );
|
||||
}
|
||||
|
||||
// XXX: review the algorithm; this is a mess
|
||||
for ( var k = param.length, l = store.length; k < l; k++ )
|
||||
{
|
||||
// note that this has the same effect as initializing (in the
|
||||
// case of a scalar) the scalar to the length of the store
|
||||
var v = +(
|
||||
( returnOrReduceOr( store[ k ], clear )
|
||||
|| ( !clear && ( scalar && sval ) )
|
||||
)
|
||||
&& ( !clear || ( scalar && sval ) )
|
||||
);
|
||||
|
||||
store[ k ] = ( scalar )
|
||||
? v
|
||||
: [ v ];
|
||||
|
||||
found = ( found || !!v );
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
function anyPredicate( preds, value, index )
|
||||
{
|
||||
return preds.some( function( p ) {
|
||||
return (typeof p === 'function')
|
||||
? p(value, index)
|
||||
: p == value;
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
function returnOrReduceOr( arr, c )
|
||||
{
|
||||
if ( arr === undefined )
|
||||
{
|
||||
return !!c;
|
||||
}
|
||||
else if ( !( arr.length ) )
|
||||
{
|
||||
return arr;
|
||||
}
|
||||
|
||||
return arr.reduce( function( a, b ) {
|
||||
return a || returnOrReduceOr( b, c );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
function returnOrReduceAnd( arr, c )
|
||||
{
|
||||
if ( arr === undefined )
|
||||
{
|
||||
return !!c;
|
||||
}
|
||||
else if ( !( arr.length ) )
|
||||
{
|
||||
return arr;
|
||||
}
|
||||
|
||||
return arr.reduce( function( a, b ) {
|
||||
return a && returnOrReduceAnd( b, c );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
function deepClone( arr )
|
||||
{
|
||||
if ( !Array.isArray( arr ) ) return arr;
|
||||
return arr.map( deepClone );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1943,6 +2273,31 @@
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the length of the longest set
|
||||
*
|
||||
* Provide each set as its own argument.
|
||||
*
|
||||
* @return number length of longest set
|
||||
*/
|
||||
function longerOf()
|
||||
{
|
||||
var i = arguments.length,
|
||||
len = 0;
|
||||
while ( i-- )
|
||||
{
|
||||
var thislen = arguments[ i ].length;
|
||||
|
||||
if ( thislen > len )
|
||||
{
|
||||
len = thislen;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* scalar to vector */
|
||||
function stov( s, n )
|
||||
{
|
||||
|
@ -1964,6 +2319,21 @@
|
|||
}
|
||||
|
||||
|
||||
function argreplace( orig, value )
|
||||
{
|
||||
if ( !( typeof orig === 'object' ) )
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
// we have an object; recurse
|
||||
for ( var i in orig )
|
||||
{
|
||||
return argreplace( orig[ i ], value );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function init_defaults( args, params )
|
||||
{
|
||||
for ( var param in params )
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
xmlns:lv="http://www.lovullo.com/rater"
|
||||
xmlns:ext="http://www.lovullo.com/ext"
|
||||
xmlns:c="http://www.lovullo.com/calc"
|
||||
xmlns:compiler="http://www.lovullo.com/rater/compiler"
|
||||
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">
|
||||
|
@ -268,17 +269,30 @@
|
|||
</template>
|
||||
|
||||
|
||||
<!-- @pattern support removed -->
|
||||
<template match="lv:match[@pattern]" mode="lvv:validate-match" priority="9">
|
||||
<call-template name="lvv:error">
|
||||
<with-param name="desc" select="'lv:match[@pattern] support removed'" />
|
||||
<with-param name="refnode" select="." />
|
||||
<with-param name="content">
|
||||
<text>use lookup tables in place of @pattern in `</text>
|
||||
<value-of select="parent::lv:classify/@as" />
|
||||
<text>'</text>
|
||||
</with-param>
|
||||
</call-template>
|
||||
<choose>
|
||||
<!-- warn of upcoming removal -->
|
||||
<when test="compiler:use-legacy-classify( ancestor::lv:classify )">
|
||||
<message select="concat( 'warning: ',
|
||||
ancestor::lv:classify/@as,
|
||||
': lv:match[@pattern] support is deprecated ',
|
||||
'and is removed with the new classification ',
|
||||
'system; use lookup tables instead' )" />
|
||||
</when>
|
||||
|
||||
<!-- @pattern support removed in the new classification system -->
|
||||
<otherwise>
|
||||
<call-template name="lvv:error">
|
||||
<with-param name="desc" select="'lv:match[@pattern] support removed'" />
|
||||
<with-param name="refnode" select="." />
|
||||
<with-param name="content">
|
||||
<text>use lookup tables in place of @pattern in `</text>
|
||||
<value-of select="parent::lv:classify/@as" />
|
||||
<text>'</text>
|
||||
</with-param>
|
||||
</call-template>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</template>
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
xmlns:lv="http://www.lovullo.com/rater"
|
||||
xmlns:t="http://www.lovullo.com/rater/apply-template"
|
||||
xmlns:c="http://www.lovullo.com/calc"
|
||||
xmlns:compiler="http://www.lovullo.com/rater/compiler"
|
||||
xmlns:eseq="http://www.lovullo.com/tame/preproc/expand/eseq"
|
||||
xmlns:ext="http://www.lovullo.com/ext">
|
||||
|
||||
|
@ -287,7 +288,11 @@
|
|||
( lv:any | lv:all )
|
||||
and not( eseq:is-expandable(.) ) ]">
|
||||
<variable name="result">
|
||||
<apply-templates select="." mode="preproc:class-groupgen" />
|
||||
<apply-templates select="." mode="preproc:class-groupgen">
|
||||
<with-param name="legacy-classify"
|
||||
select="compiler:use-legacy-classify( . )"
|
||||
tunnel="yes" />
|
||||
</apply-templates>
|
||||
</variable>
|
||||
|
||||
<apply-templates select="$result/lv:classify" mode="preproc:class-extract" />
|
||||
|
@ -408,6 +413,8 @@
|
|||
|
||||
|
||||
<template match="lv:any|lv:all" mode="preproc:class-groupgen" priority="5">
|
||||
<param name="legacy-classify" as="xs:boolean" tunnel="yes" />
|
||||
|
||||
<!-- this needs to be unique enough that there is unlikely to be a conflict
|
||||
between generated ids in various packages; generate-id is not enough for
|
||||
cross-package guarantees (indeed, I did witness conflicts), so there is
|
||||
|
@ -422,19 +429,25 @@
|
|||
<lv:classify as="{$id}" yields="{$yields}"
|
||||
preproc:generated="true"
|
||||
preproc:generated-from="{$parent-name}"
|
||||
preproc:inline="true"
|
||||
desc="(generated from predicate group of {$parent-name}">
|
||||
<if test="local-name() = 'any'">
|
||||
<attribute name="any" select="'true'" />
|
||||
</if>
|
||||
|
||||
<if test="not( $legacy-classify )">
|
||||
<attribute name="preproc:inline" select="'true'" />
|
||||
</if>
|
||||
|
||||
<apply-templates mode="preproc:class-groupgen" />
|
||||
</lv:classify>
|
||||
|
||||
<!-- this will remain in its place -->
|
||||
<lv:match on="{$yields}" value="TRUE"
|
||||
preproc:generated="true"
|
||||
preproc:inline="true" />
|
||||
preproc:generated="true">
|
||||
<if test="not( $legacy-classify )">
|
||||
<attribute name="preproc:inline" select="'true'" />
|
||||
</if>
|
||||
</lv:match>
|
||||
</template>
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue