vector/table: Add comparison operators

master
Mike Gerwitz 2019-02-04 12:21:05 -05:00
commit 8e7a946127
9 changed files with 652 additions and 166 deletions

View File

@ -33,7 +33,7 @@
<import package="vector/fold" />
<import package="vector/interpolate" />
<import package="vector/length" />
<import package="vector/stub" />
<import package="vector/table" />
<import package="aggregate" />
<import package="insurance" />

View File

@ -130,9 +130,9 @@
<rate yields="interpTableMaxFieldValue">
<t:query-first-field table="interp-query-field-test"
field="value">
<t:when field="key">
<t:where-eq field="key">
<c:value-of name="interpTableMaxKeyValue" />
</t:when>
</t:where-eq>
</t:query-first-field>
</rate>
</section>
@ -170,10 +170,10 @@
key="key"
step="INTERP_TABLE_STEP"
actual="#300">
<t:when field="pred">
<t:where-eq field="pred">
<c:const value="31" type="float"
desc="Test predicate value" />
</t:when>
</t:where-eq>
</t:interpolate-query-field>
</t:given>
@ -220,10 +220,10 @@
key="key"
step="INTERP_TABLE_STEP"
actual="#350">
<t:when field="pred">
<t:where-eq field="pred">
<c:const value="31" type="float"
desc="Test predicate value" />
</t:when>
</t:where-eq>
</t:interpolate-query-field>
</t:given>

View File

@ -0,0 +1,382 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, LLC.
This file is part of tame-core.
tame-core 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/>.
-->
<package xmlns="http://www.lovullo.com/rater"
xmlns:c="http://www.lovullo.com/calc"
xmlns:t="http://www.lovullo.com/rater/apply-template"
desc="Table Querying Specs">
<import package="../../spec" />
<import package="../../../base" />
<import package="../../../vector/table" />
<t:create-table name="test-table"
desc="Dummy table for query testing">
<t:table-column name="a"
index="0"
desc="Column A" />
<t:table-column name="b"
index="1"
desc="Column B" />
<t:table-column name="c"
index="2"
desc="Column C" />
<t:table-rows>
<t:table-row>
<t:table-value const="1" />
<t:table-value const="11" />
<t:table-value const="111" />
</t:table-row>
<t:table-row>
<t:table-value const="1" />
<t:table-value const="12" />
<t:table-value const="121" />
</t:table-row>
<t:table-row>
<t:table-value const="2" />
<t:table-value const="21" />
<t:table-value const="111" />
</t:table-row>
</t:table-rows>
</t:create-table>
<t:create-table name="test-table-seq"
desc="Dummy sequential table for query testing">
<t:table-column name="a"
index="0"
desc="Column A" />
<t:table-column name="b"
index="1"
desc="Column B" />
<t:table-rows>
<t:table-row>
<t:table-value const="1" />
<t:table-value const="1" />
</t:table-row>
<t:table-row>
<t:table-value const="2" />
<t:table-value const="1" />
</t:table-row>
<t:table-row>
<t:table-value const="2" />
<t:table-value const="2" />
</t:table-row>
<t:table-row>
<t:table-value const="5" />
<t:table-value const="1" />
</t:table-row>
<t:table-row>
<t:table-value const="5" />
<t:table-value const="2" />
</t:table-row>
<t:table-row>
<t:table-value const="5" />
<t:table-value const="3" />
</t:table-row>
<t:table-row>
<t:table-value const="7" />
<t:table-value const="1" />
</t:table-row>
<t:table-row>
<t:table-value const="7" />
<t:table-value const="2" />
</t:table-row>
<t:table-row>
<t:table-value const="7" />
<t:table-value const="3" />
</t:table-row>
<t:table-row>
<t:table-value const="7" />
<t:table-value const="4" />
</t:table-row>
</t:table-rows>
</t:create-table>
<t:describe name="_query-first-field_">
<t:it desc="returns first row of multi-row result">
<t:given>
<t:query-first-field table="test-table" field="c">
<t:where-eq field="a">
<c:value-of name="#1" />
</t:where-eq>
</t:query-first-field>
</t:given>
<t:expect>
<t:match-result eq="111" />
</t:expect>
</t:it>
<t:it desc="returns first row of single-row result">
<t:given>
<t:query-first-field table="test-table" field="c">
<t:where-eq field="a">
<c:value-of name="#1" />
</t:where-eq>
<t:where-eq field="b">
<c:value-of name="#12" />
</t:where-eq>
</t:query-first-field>
</t:given>
<t:expect>
<t:match-result eq="121" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="_query-field_">
<t:describe name="with predicates">
<t:it desc="returns vector of field values">
<t:given>
<c:length-of>
<t:query-field table="test-table" field="c">
<t:where-eq field="a">
<c:value-of name="#1" />
</t:where-eq>
</t:query-field>
</c:length-of>
</t:given>
<t:expect>
<t:match-result eq="2" />
</t:expect>
</t:it>
<t:it desc="returns vector of field values even for single result">
<t:given>
<c:car>
<t:query-field table="test-table" field="c">
<t:where-eq field="a">
<c:value-of name="#1" />
</t:where-eq>
<t:where-eq field="b">
<c:value-of name="#11" />
</t:where-eq>
</t:query-field>
</c:car>
</t:given>
<t:expect>
<t:match-result eq="111" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with no predicates">
<t:it desc="returns vector of all field values">
<t:given>
<c:length-of>
<t:query-field table="test-table" field="c" />
</c:length-of>
</t:given>
<t:expect>
<t:match-result eq="3" />
</t:expect>
</t:it>
</t:describe>
<!-- TODO: tried using inline-template here but id generation was not
working as expected -->
<t:describe name="with CMP_OP_LT">
<t:it desc="matches less than a given value">
<t:given>
<c:let>
<c:values>
<c:value name="results" type="integer" set="vector">
<t:query-field table="test-table-seq" field="a">
<t:where-lt field="a">
<c:value-of name="#5" />
</t:where-lt>
</t:query-field>
</c:value>
</c:values>
<c:sum of="results" />
</c:let>
</t:given>
<t:expect>
<!-- 1 + 2 + 2 -->
<t:match-result eq="5" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with CMP_OP_LTE">
<t:it desc="matches less than or equal to a given value">
<t:given>
<c:let>
<c:values>
<c:value name="results" type="integer" set="vector">
<t:query-field table="test-table-seq" field="a">
<t:where-lte field="a">
<c:value-of name="#5" />
</t:where-lte>
</t:query-field>
</c:value>
</c:values>
<c:sum of="results" />
</c:let>
</t:given>
<t:expect>
<!-- 1 + 2 + 2 + 5 + 5 + 5 -->
<t:match-result eq="20" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with CMP_OP_GT">
<t:it desc="matches greater than a given value">
<t:given>
<c:let>
<c:values>
<c:value name="results" type="integer" set="vector">
<t:query-field table="test-table-seq" field="a">
<t:where-gt field="a">
<c:value-of name="#5" />
</t:where-gt>
</t:query-field>
</c:value>
</c:values>
<c:sum of="results" />
</c:let>
</t:given>
<t:expect>
<!-- 7 + 7 + 7 + 7 -->
<t:match-result eq="28" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with CMP_OP_GTE">
<t:it desc="matches greater than or equal to a given value">
<t:given>
<c:let>
<c:values>
<c:value name="results" type="integer" set="vector">
<t:query-field table="test-table-seq" field="a">
<t:where-gte field="a">
<c:value-of name="#5" />
</t:where-gte>
</t:query-field>
</c:value>
</c:values>
<c:sum of="results" />
</c:let>
</t:given>
<t:expect>
<!-- 5 + 5 + 5 + 7 + 7 + 7 + 7 -->
<t:match-result eq="43" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
<t:describe name="_query-row_">
<t:describe name="with predicates">
<t:it desc="returns vector of rows">
<t:given>
<c:length-of>
<t:query-row table="test-table">
<t:where-eq field="a">
<c:value-of name="#1" />
</t:where-eq>
</t:query-row>
</c:length-of>
</t:given>
<t:expect>
<t:match-result eq="2" />
</t:expect>
</t:it>
<t:it desc="returns vector of rows even for single result">
<t:given>
<c:let>
<c:values>
<c:value name="first_row" type="integer" set="vector">
<c:car>
<t:query-row table="test-table">
<t:where-eq field="a">
<c:value-of name="#1" />
</t:where-eq>
<t:where-eq field="b">
<c:value-of name="#11" />
</t:where-eq>
</t:query-row>
</c:car>
</c:value>
</c:values>
<c:sum of="first_row" />
</c:let>
</t:given>
<t:expect>
<t:match-result eq="123" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with no predicates">
<t:it desc="returns vector of all rows">
<t:given>
<c:length-of>
<t:query-row table="test-table" />
</c:length-of>
</t:given>
<t:expect>
<t:match-result eq="3" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
</package>

View File

@ -24,9 +24,23 @@
desc="Filtering Vectors and Matrices">
<import package="../base" />
<import package="../when" />
<import package="list" />
<typedef name="CmpOp"
desc="Comparison operator">
<enum type="integer">
<!-- DO NOT REORDER; see mrange 'over' check -->
<item name="CMP_OP_EQ" value="1" desc="Equal (=)" />
<item name="CMP_OP_LT" value="2" desc="Less than (&lt;)" />
<item name="CMP_OP_LTE" value="3" desc="Less than or equal to (&lt;=)" />
<item name="CMP_OP_GT" value="4" desc="Greater than (&gt;)" />
<item name="CMP_OP_GTE" value="5" desc="Greater than or equal to (&gt;=)" />
</enum>
</typedef>
<section title="Vector Filtering">
<function name="vfilter_lookup"
desc="Filter predicate by value and use corresponding index in
@ -45,10 +59,11 @@
<section title="Matrix Filtering">
\ref{mfilter} handles complex filtering of matrices.
If the requested column~\tt{@col@} is marked as sequential with~\tt{@seq@},
a~$O(lg n)$ bisect algorithm will be used;
otherwise,
it will undergo a~$O(n)$ linear scan.
If the requested column~\tt{@col@} is marked as sequential with~\tt{@seq@}
\emph{and} the comparison operator is~\ref{CMP_OP_EQ},
then an~$O(lg n)$ bisect algorithm will be used;
otherwise,
it will undergo a~$O(n)$ linear scan.
<function name="mfilter"
desc="Filter matrix rows by column value">
@ -56,6 +71,7 @@
<param name="col" type="integer" desc="Column index to filter on" />
<param name="vals" type="float" desc="Column value to filter on" />
<param name="seq" type="boolean" desc="Is data sequential?" />
<param name="op" type="integer" desc="Comparison operator" />
<!-- merge the result of each condition in vals into a single set, which
has the effect of supporting multiple conditions on a single column of
@ -63,18 +79,16 @@
the lookups separately for each, we preserve the bisect-ability of the
condition. -->
<t:merge-until-empty set="vals" car="val" glance="TABLE_WHEN_MASK_VALUE">
<c:apply name="mrange" matrix="matrix" col="col" val="val" seq="seq">
<c:apply name="mrange" matrix="matrix" col="col" val="val" seq="seq" op="op">
<c:arg name="start">
<c:cases>
<!-- if we know that the data is sequential, then we may not need to
perform a linear search (if the dataset is large enough and the
column value is relatively distinct) -->
<!-- TODO: bisect is currently only performed for CMP_OP_EQ -->
<c:case>
<c:when name="seq">
<c:eq>
<c:value-of name="TRUE" />
</c:eq>
</c:when>
<t:when-eq name="op" value="CMP_OP_EQ" />
<t:when-eq name="seq" value="TRUE" />
<c:apply name="bisect" matrix="matrix" col="col" val="val">
<c:arg name="start">
@ -120,6 +134,7 @@
<param name="start" type="integer" desc="Starting index (inclusive)" />
<param name="end" type="integer" desc="Ending index (inclusive)" />
<param name="seq" type="boolean" desc="Is data sequential?" />
<param name="op" type="integer" desc="Comparison operator" />
<c:let>
<c:values>
@ -145,17 +160,8 @@
<c:value name="over" type="boolean"
desc="Did we pass the potential value in a sorted list?">
<c:value-of name="TRUE">
<c:when name="seq">
<c:eq>
<c:value-of name="TRUE" />
</c:eq>
</c:when>
<c:when name="curval">
<c:gt>
<c:value-of name="val" />
</c:gt>
</c:when>
<t:when-eq name="seq" value="TRUE" />
<t:when-gt name="curval" value="val" />
</c:value-of>
</c:value>
</c:values>
@ -163,33 +169,25 @@
<c:cases>
<!-- if we're done filtering, then return an empty set -->
<c:case>
<c:when name="start">
<c:gt>
<c:value-of name="end" />
</c:gt>
</c:when>
<t:when-gt name="start" value="end" />
<!-- empty set -->
<c:vector />
</c:case>
<!-- if the data is sequential and the next element is over the
requested value, then we're done -->
requested value, then we're done (can only be used for
equality and LT{,E}; need a GT{,E} version -->
<c:case>
<c:when name="over">
<c:eq>
<c:value-of name="TRUE" />
</c:eq>
</c:when>
<t:when-lte name="op" value="CMP_OP_LTE" />
<t:when-eq name="over" value="TRUE" />
<!-- empty set -->
<c:vector />
</c:case>
<c:otherwise>
<c:apply name="_mfilter" matrix="matrix" col="col" val="val"
start="start" end="end" seq="seq">
<c:apply name="_mrange_cmp" matrix="matrix" col="col" val="val"
start="start" end="end" seq="seq" op="op">
<c:arg name="cur">
<c:value-of name="matrix">
<!-- current row -->
@ -211,33 +209,95 @@
</function>
<function name="_mfilter" desc="mfilter helper">
<!-- mutually recursive with _mrange -->
<function name="_mrange_cmp" desc="mrange helper for value comparison">
<param name="matrix" type="float" set="matrix" desc="Matrix to filter" />
<param name="col" type="integer" desc="Column index to filter on" />
<param name="val" type="float" desc="Column value to filter on" />
<param name="start" type="integer" desc="Starting index (aka current index)" />
<param name="end" type="integer" desc="Ending index" />
<param name="seq" type="integer" desc="Is data sequential?" />
<param name="op" type="integer" desc="Comparison operator" />
<param name="cur" type="float" desc="Current value" />
<c:cases>
<c:case>
<c:when name="cur">
<c:eq>
<c:value-of name="val" />
</c:eq>
</c:when>
<c:cons>
<c:value-of name="matrix">
<c:index>
<c:value-of name="start" />
</c:index>
</c:value-of>
<c:let>
<c:values>
<c:value name="found" type="boolean"
desc="Whether comparison matches">
<c:cases>
<c:case label="Equal">
<t:when-eq name="op" value="CMP_OP_EQ" />
<c:value-of name="TRUE">
<t:when-eq name="cur" value="val" />
</c:value-of>
</c:case>
<c:case label="Less than">
<t:when-eq name="op" value="CMP_OP_LT" />
<c:value-of name="TRUE">
<t:when-lt name="cur" value="val" />
</c:value-of>
</c:case>
<c:case label="Less than or equal to">
<t:when-eq name="op" value="CMP_OP_LTE" />
<c:value-of name="TRUE">
<t:when-lte name="cur" value="val" />
</c:value-of>
</c:case>
<c:case label="Greater than">
<t:when-eq name="op" value="CMP_OP_GT" />
<c:value-of name="TRUE">
<t:when-gt name="cur" value="val" />
</c:value-of>
</c:case>
<c:case label="Greater than or equal to">
<t:when-eq name="op" value="CMP_OP_GTE" />
<c:value-of name="TRUE">
<t:when-gte name="cur" value="val" />
</c:value-of>
</c:case>
</c:cases>
</c:value>
</c:values>
<c:cases>
<!-- if values matches, cons it -->
<c:case>
<t:when-eq name="found" value="TRUE" />
<c:cons>
<c:value-of name="matrix">
<c:index>
<c:value-of name="start" />
</c:index>
</c:value-of>
<c:apply name="mrange" matrix="matrix" col="col" val="val"
end="end" seq="seq" op="op">
<c:arg name="start">
<c:sum>
<c:value-of name="start" />
<c:const value="1" desc="Check next element" />
</c:sum>
</c:arg>
</c:apply>
</c:cons>
</c:case>
<!-- no match, continue (mutual) recursion -->
<c:otherwise>
<c:apply name="mrange" matrix="matrix" col="col" val="val"
end="end" seq="seq">
end="end" seq="seq" op="op">
<c:arg name="start">
<c:sum>
<c:value-of name="start" />
@ -245,21 +305,9 @@
</c:sum>
</c:arg>
</c:apply>
</c:cons>
</c:case>
<c:otherwise>
<c:apply name="mrange" matrix="matrix" col="col" val="val"
end="end" seq="seq">
<c:arg name="start">
<c:sum>
<c:value-of name="start" />
<c:const value="1" desc="Check next element" />
</c:sum>
</c:arg>
</c:apply>
</c:otherwise>
</c:cases>
</c:otherwise>
</c:cases>
</c:let>
</function>
@ -329,16 +377,13 @@
<c:cases>
<!-- give up if we've reached our gap limit -->
<c:case>
<c:when name="gap">
<c:lte>
<c:value-of name="MFILTER_BISECT_GAP_MAX" />
</c:lte>
</c:when>
<t:when-lte name="gap" value="MFILTER_BISECT_GAP_MAX" />
<!-- we tried our best; return our current position -->
<c:value-of name="start" />
</c:case>
<!-- we have not yet reached our gap limit; keep going -->
<c:otherwise>
<c:let>
@ -378,22 +423,14 @@
<c:cases>
<!-- if the middle value is lower than our value, then take the upper half -->
<c:case>
<c:when name="mid">
<c:lt>
<c:value-of name="val" />
</c:lt>
</c:when>
<t:when-lt name="mid" value="val" />
<c:recurse start="mid_index" />
</c:case>
<!-- similarily, take the lower half if we over-shot -->
<c:case>
<c:when name="mid">
<c:gt>
<c:value-of name="val" />
</c:gt>
</c:when>
<t:when-gt name="mid" value="val" />
<c:recurse end="mid_index" />
</c:case>
@ -460,24 +497,14 @@
<c:cases>
<!-- if we have no more indexes to check, then we're done -->
<c:case>
<c:when name="i">
<c:eq>
<c:const value="0"
desc="Did we check the final (first) index?" />
</c:eq>
</c:when>
<t:when-eq name="i" value="#0" />
<!-- well, then, we're done -->
<c:value-of name="i" />
</c:case>
<!-- if the previous column value is the same value, then continue checking -->
<c:case>
<c:when name="prev">
<c:eq>
<c:value-of name="val" />
</c:eq>
</c:when>
<t:when-eq name="prev" value="val" />
<c:recurse>
<c:arg name="i">
@ -507,23 +534,7 @@
<c:cases>
<!-- if masked -->
<c:case>
<!-- no index provided -->
<unless name="@index@">
<c:when name="@name@">
<c:eq>
<c:value-of name="FALSE" />
</c:eq>
</c:when>
</unless>
<!-- index provided -->
<if name="@index@">
<c:when name="@name@" index="@index@">
<c:eq>
<c:value-of name="FALSE" />
</c:eq>
</c:when>
</if>
<t:when-eq name="@name@" index="@index@" value="FALSE" />
<!-- TODO: configurable mask via meta and/or param -->
<c:value-of name="TABLE_WHEN_MASK_VALUE" />

View File

@ -202,10 +202,10 @@
<t:query-field table="@table@"
field="@field@">
<!-- query for upper and lower values for interpolation -->
<t:when field="@key@">
<t:where-eq field="@key@">
<c:value-of name="low" />
<c:value-of name="high" />
</t:when>
</t:where-eq>
<param-copy name="@values@" />
</t:query-field>

View File

@ -28,6 +28,7 @@
<!-- since templates are inlined, we need to make these symbols available to
avoid terrible confusion -->
<import package="../numeric/common" export="true"/>
<import package="../when" export="true"/>
<import package="common" export="true" />
<import package="filter" export="true" />
<import package="matrix" export="true" />
@ -314,37 +315,78 @@
<text>_RATE_TABLE</text>
</param>
<c:apply name="_mquery">
<c:arg name="matrix">
<c:value-of name="@matrix@" />
</c:arg>
<c:arg name="criteria">
<c:vector>
<param-copy name="@values@">
<param-meta name="table_basename" value="@matrix@" />
</param-copy>
</c:vector>
</c:arg>
<c:let>
<c:values>
<c:value name="_qparams" type="integer" set="matrix"
desc="Query parameters">
<c:vector>
<param-copy name="@values@">
<param-meta name="table_basename" value="@matrix@" />
</param-copy>
</c:vector>
</c:value>
</c:values>
<c:arg name="i">
<!-- begin with the last predicate (due to the way we'll recurse, it
will be applied *last* -->
<t:dec>
<c:length-of>
<c:vector>
<param-copy name="@values@">
<param-meta name="table_basename" value="@matrix@" />
</param-copy>
</c:vector>
</c:length-of>
</t:dec>
</c:arg>
</c:apply>
<c:apply name="_mquery" matrix="@matrix@">
<c:arg name="criteria">
<c:value-of name="_qparams" />
</c:arg>
<c:arg name="i">
<!-- begin with the last predicate (due to the way we'll recurse, it
will be applied *last* -->
<t:dec>
<c:length-of>
<c:value-of name="_qparams" />
</c:length-of>
</t:dec>
</c:arg>
</c:apply>
</c:let>
</template>
<template name="_when_" desc="Create field predicate for query definition">
There are a series of \tt{_where-*_} templates for query predicates that
are analogous to the \tt{_match-*_} and \tt{_when-*_} templates used in
other contexts.
<inline-template>
<for-each>
<set tplname="_where-eq_" op="CMP_OP_EQ" desc="equal" />
<set tplname="_where-lt_" op="CMP_OP_LT" desc="less than" />
<set tplname="_where-lte_" op="CMP_OP_LTE" desc="less than or equal to" />
<set tplname="_where-gt_" op="CMP_OP_GT" desc="greater than" />
<set tplname="_where-gte_" op="CMP_OP_GTE" desc="greater than or equal to" />
</for-each>
<template name="@tplname@" desc="Field predicate for table query ({@desc@})">
<param name="@values@" desc="Field value (provide only one node)" />
<param name="@id@" desc="Field index" />
<param name="@field@" desc="Field name (to be used with base)" />
<param name="@name@" desc="Field name (as a variable/constant)">
<text></text>
</param>
<param name="@seqvar@" desc="Var/constant containing whether field is sequential">
<text></text>
</param>
<t:where id="@id@" seqvar="@seqvar@"
field="@field@" name="@name@" op="@op@">
<expand-barrier>
<param-copy name="@values@" />
</expand-barrier>
</t:where>
</template>
</inline-template>
<template name="_where_" desc="Create field predicate for query definition">
<param name="@id@" desc="Field index" />
<param name="@values@" desc="Field value (provide only one node)" />
<param name="@sequential@" desc="Is data sequential?" />
@ -367,6 +409,12 @@
<text>_IS_SEQ</text>
</param>
<param name="@op@"
desc="Comparison operator (default CMP_OP_EQ; see CmpOp typedef)">
<text>CMP_OP_EQ</text>
</param>
<c:vector label="Conditional for {@field@}">
<!-- the first element will represent the column (field) index -->
<unless name="@name@">
@ -381,7 +429,7 @@
<param-copy name="@values@" />
</c:vector>
<!-- the final element will represent whether or not this field is sequential -->
<!-- the third element will represent whether or not this field is sequential -->
<if name="@sequential@">
<c:const value="@sequential@" type="boolean" desc="Whether data is sequential" />
</if>
@ -396,10 +444,53 @@
<c:value-of name="FALSE" />
</unless>
</unless>
<!-- the fourth and final element is the comparison operator -->
<c:value-of name="@op@" />
</c:vector>
</template>
<!--
_when_ is deprecated in favor of _where-eq_.
This old template aimed to be consistent with the use of `when'
elsewhere (for cases and value predicates), but it was awkward in a
query abstraction.
-->
<template name="_when_"
desc="Create field predicate for query definition (deprecated;
use _where-*_)">
<param name="@values@" desc="Field value (provide only one node)" />
<param name="@id@" desc="Field index" />
<param name="@sequential@" desc="Is data sequential?" />
<param name="@field@" desc="Field name (to be used with base)" />
<param name="@name@" desc="Field name (as a variable/constant)">
<text></text>
</param>
<param name="@seqvar@" desc="Var/constant containing whether field is sequential">
<text></text>
</param>
<param name="@op@"
desc="Comparison operator (default CMP_OP_EQ; see CmpOp typedef)">
<text></text>
</param>
<warning>
_when_ is deprecated; use _where-eq_ instead
</warning>
<t:where id="@id@" sequential="@sequential@" seqvar="@seqvar@"
field="@field@" name="@name@" op="CMP_OP_EQ">
<param-copy name="@values@" />
</t:where>
</template>
<!--
These functions make the magic happen
@ -417,38 +508,27 @@
<c:cases>
<c:case>
<c:when name="i">
<c:eq>
<!-- it's important that we allow index 0, since that is a valid
predicate -->
<c:const value="-1" type="integer" desc="We're done." />
</c:eq>
</c:when>
<!-- it's important that we allow index 0, since that is a valid
predicate -->
<t:when-eq name="i" value="#-1" />
<!-- we're done; stick with the result -->
<c:value-of name="matrix" />
</c:case>
<c:otherwise>
<c:apply name="mfilter">
<!-- matrix to search -->
<c:arg name="matrix">
<!-- >> recursion happens here << -->
<c:apply name="_mquery">
<c:arg name="matrix">
<c:value-of name="matrix" />
</c:arg>
<c:arg name="criteria">
<c:value-of name="criteria" />
</c:arg>
<c:recurse>
<c:arg name="i">
<t:dec>
<c:value-of name="i" />
</t:dec>
</c:arg>
</c:apply>
</c:recurse>
</c:arg>
<!-- field (column) -->
@ -486,6 +566,18 @@
</c:index>
</c:value-of>
</c:arg>
<!-- comparison operator -->
<c:arg name="op">
<c:value-of name="criteria">
<c:index>
<c:value-of name="i" />
</c:index>
<c:index>
<c:const value="3" type="integer" desc="Comparison operator" />
</c:index>
</c:value-of>
</c:arg>
</c:apply>
</c:otherwise>
</c:cases>

View File

@ -150,7 +150,7 @@
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="_[a-zA-Z0-9@\{\}-]+_" />
<xs:pattern value="_[a-zA-Z0-9@\{\}-]+_|@[a-z][a-zA-Z0-9]*@" />
</xs:restriction>
</xs:simpleType>

View File

@ -693,6 +693,7 @@
<xs:sequence>
<xs:element name="when" type="whenType" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="index" type="indexType" minOccurs="0" maxOccurs="2" />
<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="name" type="nameType" use="required">

View File

@ -145,7 +145,7 @@
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="_[a-zA-Z0-9@\{\}-]+_" />
<xs:pattern value="_[a-zA-Z0-9@\{\}-]+_|@[a-z][a-zA-Z0-9]*@" />
</xs:restriction>
</xs:simpleType>