tame/src/current/include/preproc/macros.xsl

373 lines
10 KiB
XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Handles macro preprocessing
Copyright (C) 2014-2020 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:preproc="http://www.lovullo.com/rater/preproc"
xmlns:lv="http://www.lovullo.com/rater"
xmlns:t="http://www.lovullo.com/rater/apply-template"
xmlns:c="http://www.lovullo.com/calc"
xmlns:ext="http://www.lovullo.com/ext">
<include href="template.xsl" />
<include href="eligclass.xsl" />
<!--
Perform a macro expansion pass
This will continue to recurse until no preproc:repass nodes are found; this
allos macros to expand into macros for further processing.
-->
<template match="*" mode="preproc:macropass" priority="1"
as="node()+">
<variable name="result" as="node()+">
<apply-templates select="." mode="preproc:macros" />
</variable>
<variable name="nodeset" select="$result" />
<variable name="repass"
select="$nodeset//preproc:repass" />
<!-- halt if we are in error -->
<for-each select="$nodeset//preproc:error">
<message terminate="yes">
<text>!!! [preproc] error: </text>
<value-of select="." />
</message>
</for-each>
<choose>
<!-- if it was indicated that we must do so, recurse -->
<when test="$repass and not( $repass[ @need-sym ] )">
<!-- record the repass to keep a count -->
<!-- TODO: reintroduce
<preproc:repass-record />
-->
<message>[preproc] *REPASS*</message>
<!-- perform the repass -->
<apply-templates select="$nodeset" mode="preproc:macropass">
<with-param name="clear-tpl-step"
tunnel="yes"
select="false()" />
</apply-templates>
</when>
<!-- no more passes needed; strip any cruft and we're done -->
<otherwise>
<apply-templates mode="preproc:strip-tpl-cruft"
select="$nodeset" />
</otherwise>
</choose>
</template>
<template match="*" mode="preproc:macros" priority="1">
<copy>
<sequence select="@*" />
<apply-templates mode="preproc:macros" />
</copy>
</template>
<!--
Remove repass nodes left over from the previous pass
Otherwise, we would recurse indefinately.
-->
<template match="preproc:repass" mode="preproc:macros" priority="5">
<!-- remove; no longer needed -->
</template>
<template match="preproc:tpl-step" mode="preproc:macros" priority="5">
<param name="clear-tpl-step"
tunnel="yes"
select="true()" />
<choose>
<when test="$clear-tpl-step">
<!-- strip -->
</when>
<otherwise>
<copy-of select="." />
</otherwise>
</choose>
</template>
<!--
lv:rater is just a special type of package
-->
<template match="lv:rater" mode="preproc:macros" priority="9">
<lv:package program="true">
<sequence select="@*, *" />
</lv:package>
<preproc:repass src="lv:rater" />
</template>
<!--
FOR PERFORMANCE ONLY:
These nodes (usually) contain nothing that can be processed on the macro pass,
so recursion is unnecessary; note the low priority.
-->
<template match="lv:typedef"
mode="preproc:macros" priority="2">
<sequence select="." />
</template>
<!--
Expand values beginning with `#' into constants
It is a nuisance to have separate params (e.g. templates) for constants
and values.
-->
<template mode="preproc:macros"
match="c:value-of[ starts-with( @name, '#' ) ]"
priority="7">
<variable name="desc" as="xs:string"
select="if ( @label ) then
@label
else
'Generated short-hand constant'" />
<c:const value="{substring-after( @name, '#' )}"
type="float"
desc="{$desc}" />
</template>
<!--
Expand index values beginning with `#' into constants
It is a nuisance to have separate params (e.g. templates) for constants
and values.
-->
<template mode="preproc:macros"
match="c:value-of[ starts-with( @index, '#' ) ]"
priority="7">
<copy>
<copy-of select="@*[ not( name() = 'index' ) ]" />
<c:index>
<c:const value="{substring-after( @index, '#' )}"
type="float"
desc="Generated short-hand constant" />
</c:index>
</copy>
</template>
<!--
It does not make sense to try to take an index of a scalar
-->
<template mode="preproc:macros"
match="c:value-of[
@index
and starts-with( @name, '#' ) ]"
priority="9">
<preproc:error>
<text>Cannot take index of scalar value: </text>
<value-of select="@name" />
</preproc:error>
</template>
<!--
We can't accurately determine how to rewrite classifications if tempaltes
have not yet been expanded.
-->
<template mode="preproc:macros" priority="9"
match="lv:classify[ t:* ]">
<copy>
<sequence select="@*" />
<apply-templates mode="preproc:macros"
select="*" />
</copy>
<preproc:repass src="lv:classify wait on template expansion" />
</template>
<!--
Classifications containing only an lv:any child node can be converted into
existential classifications
-->
<template mode="preproc:macros" priority="8"
match="lv:classify[
lv:any
and count( lv:* ) = 1
and not( preproc:tpl-barrier/lv:* ) ]">
<copy>
<sequence select="@*" />
<attribute name="any" select="'true'" />
<sequence select="lv:any/*" />
</copy>
<preproc:repass src="lv:classify any" />
</template>
<template match="lv:classify[ .//lv:any|.//lv:all ]" mode="preproc:macros" priority="6">
<variable name="result">
<apply-templates select="." mode="preproc:class-groupgen" />
</variable>
<apply-templates select="$result/lv:classify" mode="preproc:class-extract" />
<preproc:repass src="lv:classify any|all" />
</template>
<template match="lv:classify" mode="preproc:class-groupgen" priority="5">
<copy>
<sequence select="@*" />
<apply-templates mode="preproc:class-groupgen" />
</copy>
</template>
<template mode="preproc:class-groupgen" priority="9"
match="lv:any[ not( element() ) ]
|lv:all[ not( element() ) ]">
<!-- useless; remove -->
</template>
<template match="lv:any|lv:all" mode="preproc:class-groupgen" priority="5">
<!-- 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
a random seed passed into the stylesheet externally -->
<variable name="id" select="concat( $__rseed, generate-id(.) )" />
<variable name="parent-name" select="ancestor::lv:classify/@as" />
<variable name="yields" select="concat( 'is', $id )" />
<!-- this will be raised outside of the parent classification during
post-processing -->
<lv:classify as="{$id}" yields="{$yields}"
preproc:generated="true"
preproc:generated-from="{$parent-name}"
desc="(generated from predicate group of {$parent-name}">
<if test="local-name() = 'any'">
<attribute name="any" 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" />
</template>
<!-- traverse through preproc nodes (e.g. preproc:tpl-barrier) -->
<template match="preproc:*" mode="preproc:class-groupgen" priority="2">
<copy>
<sequence select="@*" />
<apply-templates select="node()" mode="preproc:class-groupgen" />
</copy>
</template>
<!-- retain everything else -->
<template match="*" mode="preproc:class-groupgen" priority="1">
<sequence select="." />
</template>
<template match="lv:classify" mode="preproc:class-extract" priority="5">
<apply-templates select="lv:classify" mode="preproc:class-extract" />
<copy>
<sequence select="@*" />
<apply-templates mode="preproc:class-filter" />
</copy>
</template>
<template match="*" mode="preproc:class-extract" priority="1">
<!-- ignore non-class -->
</template>
<template match="lv:classify" mode="preproc:class-filter" priority="5">
<!-- remove -->
</template>
<template match="*" mode="preproc:class-filter" priority="1">
<sequence select="." />
</template>
<!--
Sections exist purely for organization and documentation. Move all
nodes out of it, so that we do not complicate parsing.
-->
<template mode="preproc:macros" priority="2"
match="lv:section">
<apply-templates select="*" mode="preproc:macros" />
</template>
<!--
wrapper around `<t:yield />`
-->
<template match="lv:yield" mode="preproc:macros" priority="5">
<t:yield>
<copy-of select="@*" />
<apply-templates mode="preproc:expand" />
</t:yield>
</template>
<!--
wrapper around `<t:rate-each />`
-->
<template match="lv:rate-each" mode="preproc:macros" priority="5">
<t:rate-each>
<copy-of select="@*" />
<apply-templates mode="preproc:expand"
select="*[ not( local-name() = 'class' ) ]" />
</t:rate-each>
</template>
</stylesheet>