tame/src/current/include/depgen.xsl

546 lines
17 KiB
XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Dependency generation
Copyright (C) 2014-2019 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/>.
TODO: we can combine this dependency discovery with the symbol table
generation, eliminating extra passes
TODO: dependency symbols should not duplicate metadata
-->
<stylesheet version="1.0"
xmlns="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:preproc="http://www.lovullo.com/rater/preproc"
xmlns:lv="http://www.lovullo.com/rater"
xmlns:lvm="http://www.lovullo.com/rater/map"
xmlns:t="http://www.lovullo.com/rater/apply-template"
xmlns:c="http://www.lovullo.com/calc"
xmlns:ext="http://www.lovullo.com/ext"
xmlns:util="http://www.lovullo.com/util"
exclude-result-prefixes="ext util xs">
<variable name="tex-defaults">
<preproc:syms>
<preproc:sym value="\alpha" vec="A" />
<preproc:sym value="\beta" vec="B" />
<preproc:sym value="\gamma" vec="\Gamma" />
<preproc:sym value="x" vec="X" />
<preproc:sym value="y" vec="Y" />
<preproc:sym value="z" vec="Z" />
</preproc:syms>
</variable>
<!-- simply allows invoking the template with dynamic input -->
<template name="preproc:gen-deps">
<param name="pkg" as="element( lv:package )" />
<apply-templates select="$pkg" mode="preproc:gen-deps" />
</template>
<template match="*" mode="preproc:gen-deps">
<copy>
<sequence select="@*" />
<message>
<text>[depgen] *determining symbol dependencies...</text>
</message>
<apply-templates select="preproc:symtable" mode="preproc:depgen" />
<sequence select="*" />
</copy>
</template>
<template match="preproc:symtable" mode="preproc:depgen" priority="9">
<variable name="symtable" select="." />
<variable name="symtable-map" as="map( xs:string, element( preproc:sym ) )"
select="map:merge( for $sym in $symtable/preproc:sym
return map{ string( $sym/@name ) : $sym } )" />
<preproc:sym-deps>
<variable name="deps" as="element( preproc:sym-dep )*">
<apply-templates mode="preproc:depgen"
select="root(.)/lv:*,
root(.)/lvm:*">
<with-param name="symtable-map" select="$symtable-map"
tunnel="yes" />
</apply-templates>
</variable>
<!-- preproc:sym-deps may be nested -->
<for-each select="$deps, $deps//preproc:sym-dep">
<variable name="sym-name" as="xs:string"
select="@name" />
<variable name="cursym" as="element( preproc:sym )?"
select="$symtable-map( $sym-name )" />
<if test="not( $cursym )">
<message select="." />
<message terminate="yes"
select="concat( 'internal error: ',
'cannot find symbol in symbol table: ',
'`', $sym-name, '''' )" />
</if>
<!-- do not output duplicates (we used to not output references
to ourselves, but we are now retaining them, since those
data are useful) -->
<variable name="uniq" select="
preproc:sym-ref[
not( @name=preceding-sibling::preproc:sym-ref/@name )
]
" />
<!-- symbols must not have themselves as their own dependency -->
<if test="$uniq[ not( $cursym/@allow-circular = 'true' )
and ( @name = $cursym/@name
or @parent = $cursym/@name ) ]">
<message terminate="yes"
select="concat( '[preproc] !!! fatal: symbol ',
$cursym/@name,
' references itself ',
'(circular dependency)' )" />
</if>
<!-- grab the original source symbol for these references and augment them
with any additional dependency metadata -->
<variable name="syms-rtf">
<for-each select="$uniq">
<variable name="name" select="@name" />
<variable name="sym" as="element( preproc:sym )?"
select="$symtable-map( $name )" />
<!-- we should never have this problem. -->
<if test="not( $sym ) and not( @lax='true' )">
<message terminate="yes">
<text>[depgen] internal error: </text>
<text>could not locate dependency symbol `</text>
<value-of select="@name" />
<text>' in local symbol table; needed by </text>
<value-of select="$cursym/@name" />
</message>
</if>
<!-- copy and augment (we set @name because $sym/@name may not exist
if @lax) -->
<preproc:sym name="{@name}">
<if test="$sym">
<sequence select="$sym/@*" />
</if>
<preproc:meta>
<!-- retain type -->
<sequence select="$sym/@type" />
<sequence select="$sym/@dim" />
<!-- copy any additional metadata -->
<sequence select="@*[ not( local-name() = 'name' ) ]" />
</preproc:meta>
</preproc:sym>
</for-each>
</variable>
<variable name="syms" select="$syms-rtf/preproc:sym" />
<!-- only applicable if the symbol is @lax and the symbol was not
found in the local symbol table -->
<variable name="lax" select="
$uniq[
@lax='true'
and not( @name=$syms/@name )
]
" />
<preproc:sym-dep name="{@name}">
<!-- process symbols that have not been found in the local symbol
table (only applicable when cursym is @lax) -->
<for-each select="$lax">
<!-- the @lax flag here is simply to denote that this symbol may not
actually exist and that ignoring the check was explicitly
requested (and not a bug in the depgen process) -->
<preproc:sym-ref name="{@name}" lax="true">
<sequence select="preproc:meta/@*" />
</preproc:sym-ref>
</for-each>
<!-- @tex provided an non-empty, or function -->
<for-each select="
$syms[
( @tex and not( @tex='' ) )
or @type='func'
]">
<choose>
<!-- even if function, @tex overrides symbol -->
<when test="@tex and not( @tex='' )">
<preproc:sym-ref tex="{@tex}">
<sequence select="@*" />
<sequence select="preproc:meta/@*" />
</preproc:sym-ref>
</when>
<!-- must be a function; use its name -->
<otherwise>
<preproc:sym-ref>
<sequence select="@*" />
<sequence select="preproc:meta/@*" />
<attribute name="tex">
<text>\textrm{</text>
<value-of select="@name" />
<text>}</text>
</attribute>
</preproc:sym-ref>
</otherwise>
</choose>
</for-each>
<!-- no @tex, @tex empty, no function -->
<for-each select="
$syms[
( not( @tex ) or @tex='' )
and not( @type='func' )
]">
<variable name="name" select="@name" />
<variable name="sym" select="." />
<preproc:sym-ref>
<!-- minimal attribute copy (avoid data duplication as much as
possible to reduce modification headaches later on) -->
<sequence select="@name, @parent" />
<sequence select="preproc:meta/@*" />
<!-- assign a symbol -->
<variable name="pos" select="position()" />
<attribute name="tex">
<variable name="texsym" select="
$tex-defaults/preproc:syms/preproc:sym[
position() = $pos
]
" />
<choose>
<when test="$sym/@tex and not( $sym/@tex='' )">
<value-of select="$sym/@tex" />
</when>
<!-- scalar/vector default -->
<when test="$texsym and number( $sym/@dim ) lt 2">
<value-of select="$texsym/@value" />
</when>
<!-- matrix default -->
<when test="$texsym">
<value-of select="$texsym/@vec" />
</when>
<!-- no default available; generate one -->
<otherwise>
<value-of select="
if ( number( $sym/@dim ) lt 2 ) then '\theta'
else '\Theta'
" />
<text>_{</text>
<value-of select="$pos" />
<text>}</text>
</otherwise>
</choose>
</attribute>
</preproc:sym-ref>
</for-each>
</preproc:sym-dep>
</for-each>
</preproc:sym-deps>
</template>
<template mode="preproc:depgen" priority="7"
match="lv:rate">
<preproc:sym-dep name="{@yields}">
<apply-templates mode="preproc:depgen" />
</preproc:sym-dep>
</template>
<template mode="preproc:depgen" priority="7"
match="c:sum[@generates]
|c:product[@generates]">
<preproc:sym-dep name="{@generates}">
<preproc:sym-ref name="{ancestor::lv:rate[1]/@yields}" />
</preproc:sym-dep>
<!-- this may have other rules; see below -->
<next-match />
</template>
<template mode="preproc:depgen" priority="7"
match="lv:classify">
<preproc:sym-dep name=":class:{@as}">
<apply-templates mode="preproc:depgen" />
</preproc:sym-dep>
<if test="@yields">
<preproc:sym-dep name="{@yields}">
<preproc:sym-ref name=":class:{@as}" />
</preproc:sym-dep>
</if>
</template>
<template mode="preproc:depgen" priority="8"
match="lv:function/lv:param">
<variable name="fname" as="xs:string"
select="parent::lv:function/@name" />
<preproc:sym-dep name=":{$fname}:{@name}">
<preproc:sym-ref name="{$fname}" />
</preproc:sym-dep>
</template>
<template mode="preproc:depgen" priority="7"
match="lv:param">
<preproc:sym-dep name="{@name}">
<preproc:sym-ref name="{@type}" />
<apply-templates mode="preproc:depgen" />
</preproc:sym-dep>
</template>
<template mode="preproc:depgen" priority="7"
match="lv:function">
<preproc:sym-dep name="{@name}">
<apply-templates mode="preproc:depgen" />
</preproc:sym-dep>
</template>
<template mode="preproc:depgen" priority="7"
match="lv:typedef">
<preproc:sym-dep name="{@name}">
<apply-templates mode="preproc:depgen" />
</preproc:sym-dep>
</template>
<template match="lv:template"
mode="preproc:depgen" priority="8">
<!-- ignore symbols within templates -->
</template>
<template name="preproc:depgen-c-normal" match="c:value-of|c:when" mode="preproc:depgen" priority="5">
<param name="name" select="@name" />
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="pkg" as="element( lv:package )"
select="root(.)" />
<variable name="sym" as="element( preproc:sym )?"
select="$symtable-map( $name )" />
<!-- see if there is a c:let associated with this name -->
<variable name="let" select="
ancestor::c:let[ c:values/c:value/@name=$name ]
" />
<choose>
<!-- c:let reference -->
<when test="$let">
<preproc:sym-ref name=":{$let/@name}:{$name}" />
</when>
<!-- scalar constant -->
<when test="( $sym/@type='const' ) and ( $sym/@dim='0' )">
<!-- while these are optimized away, they are still useful for evaluating
dependency trees and generating code -->
<preproc:sym-ref name="{$sym/@name}" />
</when>
<!-- function param reference -->
<when test="$name=ancestor::lv:function/lv:param/@name">
<variable name="fname" as="xs:string"
select="ancestor::lv:function/@name" />
<preproc:sym-ref name=":{$fname}:{$name}"
varname="{$name}"/>
</when>
<!-- index reference -->
<when test="$name=ancestor::c:*[ @of ]/@index" />
<!-- unknown symbol (it is important to do this after the above checks) -->
<when test="not( $sym )">
<!-- do not terminate; validator can provide additional information -->
<message>
<text>[depgen] warning: unknown symbol `</text>
<value-of select="$name" />
<text>'</text>
</message>
</when>
<when test="$sym/@parent">
<preproc:sym-ref name="{$sym/@name}" parent="{$sym/@parent}" />
</when>
<!-- just an average 'ol symbol -->
<otherwise>
<preproc:sym-ref name="{$name}" />
</otherwise>
</choose>
<apply-templates mode="preproc:depgen" />
</template>
<template match="c:sum[@of]|c:product[@of]" mode="preproc:depgen" priority="5">
<!-- process using @of -->
<call-template name="preproc:depgen-c-normal">
<with-param name="name" select="@of" />
</call-template>
</template>
<template match="c:apply" mode="preproc:depgen" priority="5">
<!-- no special treatment yet -->
<call-template name="preproc:depgen-c-normal" />
</template>
<template match="c:apply/c:arg" mode="preproc:depgen" priority="5">
<!-- arguments may have calculations, so we must recurse -->
<apply-templates mode="preproc:depgen" />
</template>
<template name="preproc:depgen-match">
<param name="on" select="@on" />
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="class" select="ancestor::lv:classify" />
<variable name="sym" as="element( preproc:sym )"
select="$symtable-map( $on )" />
<!-- are we depending on another classification? -->
<if test="$sym/@type='cgen'">
<variable name="cname" select="substring-after( $sym/@parent, ':class:' )" />
<!-- check if one of our dependencies wants to be external to the classifier,
but we're trying to pull them in...tug-of-war -->
<if test="$sym/@extclass='true' and not( $class/@external='true' )">
<message terminate="yes">
<text>[preproc] !!! fatal: internal classification `</text>
<value-of select="$class/@as" />
<text>' cannot pull in external classification `</text>
<value-of select="$cname" />
<text>'</text>
</message>
</if>
</if>
<!-- process the @on -->
<call-template name="preproc:depgen-c-normal">
<with-param name="name" select="$on" />
</call-template>
</template>
<template match="lv:match[ @value ]" mode="preproc:depgen" priority="5">
<!-- process the @value -->
<call-template name="preproc:depgen-c-normal">
<with-param name="name" select="@value" />
</call-template>
<call-template name="preproc:depgen-match" />
</template>
<template match="lv:match[ @anyOf ]" mode="preproc:depgen" priority="6">
<!-- process the "normal" match -->
<call-template name="preproc:depgen-match" />
<!-- we depend on the type -->
<preproc:sym-ref name="{@anyOf}" />
<call-template name="preproc:depgen-match" />
</template>
<template match="lv:match[ @pattern ]" mode="preproc:depgen" priority="5">
<!-- there are no pattern dependencies; process @on -->
<call-template name="preproc:depgen-match" />
</template>
<!-- match on calculated value -->
<template match="lv:match[ c:* ]" mode="preproc:depgen" priority="6">
<!-- process the "normal" match -->
<call-template name="preproc:depgen-match" />
<!-- process the calculation dependencies -->
<apply-templates select="c:*" mode="preproc:depgen" />
</template>
<template match="lv:template" mode="preproc:depgen" priority="9">
<!-- don't generate dependencies for templates (which may have been
generated inline in unexpected places) -->
</template>
<template match="lv:union" mode="preproc:depgen" priority="5">
<for-each select="lv:typedef">
<preproc:sym-ref name="{@name}" />
</for-each>
<!-- we still need to process the typedefs independently -->
<apply-templates mode="preproc:depgen" />
</template>
<template match="lv:enum/lv:item" mode="preproc:depgen" priority="5">
<preproc:sym-ref name="{@name}" />
</template>
<!-- @class deps -->
<template match="lv:class" mode="preproc:depgen" priority="5">
<preproc:sym-ref name=":class:{@ref}" class-no="{@no}" />
</template>
<template match="c:*|lv:*" mode="preproc:depgen" priority="3">
<!-- ignore -->
<apply-templates mode="preproc:depgen" />
</template>
<template match="node()" mode="preproc:depgen" priority="1">
<!-- skip -->
</template>
</stylesheet>