Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Gerwitz <gerwitm@lovullo.com>2016-08-24 09:43:05 -0400
committerMike Gerwitz <gerwitm@lovullo.com>2016-08-24 12:38:00 -0400
commitff01f39c1e8c9b9549d884a0db1f9a74799cf37e (patch)
tree35978db88a8d385250b1b47ad05966e19516373d /src/current/include
parent6c0aa54bd1b7b49d736f0db3a8f48b7aa90b3b65 (diff)
downloadtame-ff01f39c1e8c9b9549d884a0db1f9a74799cf37e.tar.gz
tame-ff01f39c1e8c9b9549d884a0db1f9a74799cf37e.tar.bz2
tame-ff01f39c1e8c9b9549d884a0db1f9a74799cf37e.zip
Liberate current implementation of "Calc DSL"
(Copyright headers will be added in the next commit; these are the original files, unaltered in any way.) The internal project name at LoVullo is simply "Calc DSL". This liberates the entire thing. If anything was missed, I'll be added later. To continue building at LoVullo with this move, symlinks are used for the transition; this is the exact code that is used in production. There is a lot here---over 25,000 lines. Much of it is in disarray from the environment surrounding its development, but it does work well for what it was intended to do. (LoVullo folks: fork point is 65723a0 in calcdsl.git.)
Diffstat (limited to 'src/current/include')
-rw-r--r--src/current/include/calc-display.xsl702
-rw-r--r--src/current/include/depgen.xsl526
-rw-r--r--src/current/include/display.xsl551
-rw-r--r--src/current/include/dslc-base.xsl90
-rw-r--r--src/current/include/entry-form.xsl323
-rw-r--r--src/current/include/exslt/str.tokenize.template.xsl65
-rw-r--r--src/current/include/orderizer.xsl268
-rw-r--r--src/current/include/preproc/domain.xsl226
-rw-r--r--src/current/include/preproc/eligclass.xsl261
-rw-r--r--src/current/include/preproc/expand.xsl691
-rw-r--r--src/current/include/preproc/macros.xsl456
-rw-r--r--src/current/include/preproc/package.xsl817
-rw-r--r--src/current/include/preproc/path.xsl230
-rw-r--r--src/current/include/preproc/symtable.xsl959
-rw-r--r--src/current/include/preproc/template.xsl1149
-rw-r--r--src/current/include/preprocess.xsl48
-rw-r--r--src/current/include/symbol-map.xml80
-rw-r--r--src/current/include/util.xsl186
18 files changed, 7628 insertions, 0 deletions
diff --git a/src/current/include/calc-display.xsl b/src/current/include/calc-display.xsl
new file mode 100644
index 0000000..4f9f0fa
--- /dev/null
+++ b/src/current/include/calc-display.xsl
@@ -0,0 +1,702 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Handles calculation output in LaTeX format for styling by Mathjax
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ Recursively apply any child equations and then do the same in calc-after mode
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:*" mode="calc-recurse">
+ <xsl:apply-templates select="./c:*" />
+
+ <!-- invoke `after' templates, which allows inserting data for display after
+ the initial equation -->
+ <xsl:apply-templates select="./c:*" mode="calc-after" />
+</xsl:template>
+
+
+<!--
+ Style sum of values as a LaTeX equation
+
+ Note that this does not deal with the summation of a series; that's left to
+ the handling of the @of attribute.
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:sum">
+ <xsl:apply-templates select="." mode="sum-body" />
+</xsl:template>
+
+<xsl:template match="c:sum" mode="sum-body">
+ <xsl:for-each select="./c:*">
+ <!-- get value to display -->
+ <xsl:variable name="display">
+ <xsl:apply-templates select="." />
+ </xsl:variable>
+
+ <!-- delimit with +s if not first; if we're adding a negative, omit the
+ addition symbol as well (unless we're displaying a product, since
+ multiplying by a negative would otherwise appear to be subtraction) -->
+ <xsl:if test="
+ ( position() > 1 )
+ and (
+ not( substring( $display, 1, 1 ) = '-' )
+ or ( local-name() = 'product' )
+ )
+ ">
+
+ <xsl:text> + </xsl:text>
+ </xsl:if>
+
+ <!-- the only reason we would have a sum within a sum (that doesn't use
+ sigma-notation) is for grouping -->
+ <xsl:if test="( local-name() = 'sum' ) and not( @of )">
+ <xsl:text>\left(</xsl:text>
+ </xsl:if>
+
+ <xsl:copy-of select="$display" />
+
+ <xsl:if test="( local-name() = 'sum' ) and not( @of )">
+ <xsl:text>\right)</xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+
+ <!-- since we looped manually, we must also invoke `after' templates
+ manually -->
+ <xsl:apply-templates select="./c:*" mode="calc-after" />
+</xsl:template>
+
+
+<!--
+ Style summation of a set as a LaTeX equation
+
+ Note that @of deals witht summation of sets only (rather, using the index of a
+ set to sum over the provided calculation). See the other template(s) for
+ summing values without a set.
+
+ An index may optionally be provided via an @index attribute; otherwise, one
+ will be chosen for you. The index is used for the lower limit; the upper limit
+ is omitted. The child nodes are then used to generate the equation to be
+ applied by the summation.
+
+ If no child nodes are provided, then the summation is meant to imply that each
+ value in the set should be summed. Adding child nodes overrides this behavior.
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:sum[@of]">
+ <xsl:variable name="of" select="@of" />
+
+ <!-- if no index is provided, simply use its symbol to indicate all values
+ within its domain -->
+ <xsl:variable name="index">
+ <xsl:choose>
+ <xsl:when test="@index">
+ <xsl:value-of select="@index" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- TODO: Determine an index that is not in use -->
+ <xsl:text>k</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="ref">
+ <xsl:value-of select="@of" />
+ </xsl:variable>
+
+ <!-- retrieve the symbol associated with this value (no index) -->
+ <xsl:variable name="symbol">
+ <xsl:call-template name="get-symbol">
+ <xsl:with-param name="name" select="$ref" />
+ <xsl:with-param name="search" select="/" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- also retrieve the symbol without its index -->
+
+ <!-- if an index was provided, set the lower limit to 0 (we do this in a
+ separate variable so that we can display the symbol on its own elsewhere)
+ -->
+ <xsl:variable name="index-limit">
+ <xsl:value-of select="$index" />
+
+ <!-- we only need the explicit notation if we are summing more than the
+ set -->
+ <xsl:if test="./c:*">
+ <xsl:text>=0</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:text>\sum \limits_{</xsl:text>
+ <xsl:value-of select="$index-limit" />
+ <xsl:text>}</xsl:text>
+
+ <!-- upper limit is only necessary for clarification if they have provided a
+ more complex expression; if we're only summing over a single set, then
+ the extra notation is unnecessary and will just clutter -->
+ <xsl:if test="./c:*">
+ <!-- the upper limit of the summation will be denoted by #S, where S is the
+ symbol for a given set -->
+ <xsl:text>^{\grave\#</xsl:text>
+ <xsl:copy-of select="$symbol" />
+ <xsl:text>}</xsl:text>
+ </xsl:if>
+
+ <!-- if no children are provided, just sum @of -->
+ <xsl:if test="not(./c:*)">
+ <!-- output the symbol followed by its index, only if an index was provided
+ (and is therefore necessary) -->
+ <xsl:call-template name="get-symbol">
+ <xsl:with-param name="name" select="$ref" />
+ <xsl:with-param name="index-symbol" select="$index" />
+ <xsl:with-param name="search" select="/" />
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- output any additional expressions, if any -->
+ <xsl:apply-templates select="." mode="sum-body" />
+</xsl:template>
+
+
+<!--
+ Style product of values as a LaTeX equation
+
+ Note that this does not deal with the product of a series; that's left to
+ the handling of the @of attribute (TODO: @of not yet implemented for
+ products).
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:product">
+ <xsl:variable name="enclose" select="
+ @dot='true'
+ and (
+ preceding-sibling::c:*
+ or following-sibling::c:*
+ )
+ " />
+
+ <xsl:if test="$enclose">
+ <xsl:text>(</xsl:text>
+ </xsl:if>
+
+ <xsl:for-each select="./c:*">
+ <!-- Function symbols can have multiple chars, so we'll need to add the
+ multiplication symbol. Adjacent constants should also be separated by a
+ dot, otherwise it'll look like one giant number. -->
+ <xsl:if test="
+ (
+ local-name() = 'apply'
+ or ../@dot = 'true'
+ or (
+ ( local-name() = 'const' )
+ and ( local-name( preceding-sibling::*[1] ) = 'const' )
+ )
+ )
+ and ( position() > 1 )
+ ">
+ <xsl:text> \,\cdot\, </xsl:text>
+ </xsl:if>
+
+ <!-- if precedence of this operation is lower, we will need to include
+ parenthesis -->
+ <!-- XXX: Relies on hard-coded precedence rules in multiple locations;
+ refactor! -->
+ <xsl:if test="
+ ( local-name() = 'sum' )
+ or ( ( local-name() = 'product' ) and not( @of ) )
+ ">
+ <xsl:text>\left(</xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="." />
+
+ <!-- close parenthesis -->
+ <xsl:if test="
+ ( local-name() = 'sum' )
+ or ( ( local-name() = 'product' ) and not( @of ) )
+ ">
+ <xsl:text>\right)</xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+
+ <xsl:if test="$enclose">
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+
+ <!-- since we looped manually, we must also invoke `after' templates
+ manually -->
+ <xsl:apply-templates select="./c:*" mode="calc-after" />
+</xsl:template>
+
+
+<!--
+ Style quotient of two values as a LaTeX equation
+
+ This is used to divide two values and will be styled as a fraction. The
+ numerator should be the first calculation node and the denominator the second;
+ there should be no additional nodes.
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:quotient">
+ <!-- numerator (first child) -->
+ <xsl:text>\frac{</xsl:text>
+ <xsl:apply-templates select="./c:*[1]" />
+
+ <!-- denominator (second child) -->
+ <xsl:text>}{</xsl:text>
+ <xsl:apply-templates select="./c:*[2]" />
+ <xsl:text>}</xsl:text>
+
+ <!-- since we processed manually, we must also invoke `after' templates
+ manually -->
+ <xsl:apply-templates select="./c:*" mode="calc-after" />
+</xsl:template>
+
+
+<xsl:template match="c:let">
+ <!-- process the body of the let expression (the variables should have been
+ processed separately) -->
+ <xsl:apply-templates select="c:*" />
+</xsl:template>
+
+
+<!--
+ Style a value for display within a LaTeX equation
+
+ Forwards to calc-get-value template.
+-->
+<xsl:template match="c:value-of">
+ <xsl:apply-templates select="." mode="calc-get-value" />
+</xsl:template>
+
+
+<!--
+ Values from a c:let must have their names altered before looking up the symbol
+-->
+<xsl:template match="c:*[ @name=ancestor::c:let/c:values/c:value/@name ]" mode="calc-get-value">
+ <xsl:call-template name="calc-get-value">
+ <!-- :<let-name>:<our-name> -->
+ <xsl:with-param name="name" select="
+ concat( ':', ancestor::c:let[1]/@name, ':', @name )
+ " />
+ </xsl:call-template>
+</xsl:template>
+
+
+<!--
+ Style a value for display within a LaTeX equation
+
+ By default, the symbol for the given value (variable) will be rendered in
+ place of this node.
+
+ This element is not expected to have any children.
+
+ XXX: Refactor me; there are more clear and less convoluted ways to accomplish
+ this.
+
+ @return LaTeX equation
+-->
+<xsl:template name="calc-get-value" match="c:*" mode="calc-get-value">
+ <xsl:param name="name" select="@name" />
+
+ <xsl:variable name="index-symbol">
+ <xsl:if test="./c:index">
+ <xsl:for-each select="./c:index">
+ <!-- separate multiple indexes with commas -->
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="./c:*[1]" />
+ </xsl:for-each>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:variable name="sym" select="
+ /lv:*/preproc:symtable/preproc:sym[
+ @name=$name
+ ]
+ " />
+
+
+ <xsl:variable name="value">
+ <xsl:choose>
+ <!-- for scalar constants that do not have a symbol, simply inline their
+ value (if they have a symbol, then it is assumed that their symbolic
+ meaning is more meaningful than its value) -->
+ <xsl:when test="
+ $sym[
+ @type='const'
+ and @dim='0'
+ and ( not( @text ) or @tex='' )
+ ]
+ ">
+ <xsl:value-of select="$sym/@value" />
+ </xsl:when>
+
+ <!-- local index (generated with @of) -->
+ <xsl:when test="ancestor::c:*[ @of and @index=$name ]">
+ <xsl:value-of select="$name" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="get-symbol">
+ <xsl:with-param name="name" select="$name" />
+ <xsl:with-param name="index" select="@index" />
+ <xsl:with-param name="index-symbol" select="$index-symbol" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:copy-of select="$value" />
+
+ <!-- yes, a value still may have things to appear after it -->
+ <xsl:apply-templates select="./c:*" mode="calc-after" />
+</xsl:template>
+
+
+<!--
+ Style a constant value for use in a LaTeX equation
+
+ Constant values are not treated as variables; instead, their value (rather
+ than their symbol) is immediately rendered.
+
+ Use this only if the value itself makes more sense (and is more clear) than a
+ variable.
+
+ This element is not expected to have any children.
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:const">
+ <!-- a constant value of 1 with Iverson's brackets is redundant -->
+ <xsl:if test="not( ( @value = '1' ) and ./c:when )">
+ <!-- display constant value -->
+ <xsl:value-of select="@value" />
+ </xsl:if>
+
+ <!-- a constant may still have things to appear after it -->
+ <xsl:apply-templates select="./c:*" mode="calc-after" />
+</xsl:template>
+
+
+<xsl:template match="c:ceil|c:floor">
+ <xsl:text>\left\l</xsl:text>
+ <xsl:value-of select="local-name()" />
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates select="." mode="calc-recurse" />
+ <xsl:text>\right\r</xsl:text>
+ <xsl:value-of select="local-name()" />
+</xsl:template>
+
+
+<!--
+ Styles a function application for display in a LaTeX equation
+
+ Indicates a function application. A call to the function, with each of its
+ arguments in parenthesis, will be rendered.
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:apply">
+ <xsl:variable name="self" select="." />
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="fsym" select="//lv:function[@name=$name]/@sym" />
+
+ <xsl:call-template name="get-symbol">
+ <xsl:with-param name="name" select="@name" />
+ <xsl:with-param name="search" select="/" />
+ </xsl:call-template>
+
+ <!-- if a symbol is provided, then omit the parenthesis -->
+ <xsl:if test="not( $fsym )">
+ <xsl:text>\left(</xsl:text>
+ </xsl:if>
+
+ <!-- output the symbol associated with the value of each argument -->
+ <xsl:for-each select="./c:arg">
+ <!-- delimit params with a comma -->
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <xsl:variable name="valname" select="./c:*[1]/@name" />
+
+ <xsl:choose>
+ <!-- if this variable has been defined as an index, then simply output
+ it -->
+ <xsl:when test="ancestor::c:*[ @of and @index=$valname ]">
+ <xsl:value-of select="$valname" />
+ </xsl:when>
+
+ <!-- display the value of constants -->
+ <xsl:when test="local-name( ./c:*[1] ) = 'const'">
+ <xsl:value-of select="./c:*[1]/@value" />
+ </xsl:when>
+
+ <!-- otherwise, it's some other variable and we must look up its
+ symbol -->
+ <xsl:otherwise>
+ <xsl:apply-templates select="./c:*[1]" />
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- we may have c:when, etc (args are their own sub-equations) -->
+ <xsl:apply-templates select="./c:*[1]/c:*" mode="calc-after" />
+ </xsl:for-each>
+
+ <xsl:if test="not( $fsym )">
+ <xsl:text>\right)</xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="./c:*" mode="calc-after" />
+</xsl:template>
+
+
+<!--
+ Outputs Iverson's brackets only if forced; see calc-after mode
+
+ This is hidden by default until calc-after to ensure that this is display
+ *after* the equation is output.
+
+ @param boolean force-show optionally force the display of the notation
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:when">
+ <xsl:param name="force-show" select="false()" />
+ <xsl:param name="standalone" select="false()" />
+
+ <!-- by default, we want to defer showing this until after the equation has
+ been output; however, the caller can force it to be displayed if needed -->
+ <xsl:if test="$force-show = true()">
+ <xsl:apply-templates select="." mode="calc-after">
+ <xsl:with-param name="standalone" select="$standalone" />
+ </xsl:apply-templates>
+ </xsl:if>
+</xsl:template>
+
+
+<!--
+ Outputs Iverson's brackets
+
+ This is called *after* the equation is output
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:when" mode="calc-after" priority="5">
+ <xsl:param name="brackets" select="true()" />
+ <xsl:param name="standalone" select="false()" />
+
+ <xsl:variable name="preceding" select="preceding-sibling::c:when" />
+
+ <!-- output bracket only if (a) requested and (b) first in set -->
+ <xsl:if test="$brackets and ( $standalone or not( $preceding ) )">
+ <xsl:text>\left[</xsl:text>
+ </xsl:if>
+
+ <!-- if we do have a preceding sibling, prefix with "and" -->
+ <xsl:if test="not( $standalone ) and $preceding">
+ <xsl:text>\text{ and }</xsl:text>
+ </xsl:if>
+
+ <!-- output the symbol for the variable we are comparing against -->
+ <xsl:apply-templates select="." mode="calc-get-value" />
+
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates select="./c:*" mode="calc-iversons" />
+
+ <!-- output bracket only if (a) requested and (b) last in set -->
+ <xsl:if test="
+ $brackets and ( $standalone or not( following-sibling::c:when ) )
+ ">
+
+ <xsl:text>\right]</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+
+<xsl:template match="c:cases">
+ <xsl:text>\begin{cases}</xsl:text>
+ <xsl:apply-templates select="./c:case|./c:otherwise" />
+ <xsl:text>\end{cases}</xsl:text>
+
+ <!-- if any equations immediately follow, add some extra space so as not to
+ confuse the reader -->
+ <xsl:if test="following-sibling::c:*">
+ <xsl:text>\;\;\;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+
+<!--
+ Generate a case
+
+ When $force-show is provided, as it is when displaying only small portions of
+ an equation, it will display the line using Iverson's brackets.
+-->
+<xsl:template match="c:cases/c:case|c:cases/c:otherwise">
+ <xsl:param name="force-show" select="false()" />
+
+ <!-- generate value -->
+ <xsl:apply-templates select="./c:*[ not( local-name() = 'when' ) ][1]" />
+
+ <xsl:choose>
+ <xsl:when test="not( $force-show )">
+ <xsl:text> &amp; </xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text> [</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:choose>
+ <xsl:when test="local-name() != 'otherwise'">
+ <xsl:if test="not( $force-show )">
+ <xsl:text>\text{if } </xsl:text>
+ </xsl:if>
+
+ <!-- generate condition under which this value will apply -->
+ <xsl:apply-templates select="./c:when" mode="calc-after">
+ <xsl:with-param name="brackets" select="false()" />
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>\text{otherwise}</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- determine how we end the line (more cases or end?) -->
+ <xsl:choose>
+ <xsl:when test="$force-show">
+ <xsl:text>]</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="following-sibling::c:case|following-sibling::c:otherwise">
+ <xsl:text>; \\</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>.</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Do nothing with calc-after for any unmatched calculations
+-->
+<xsl:template match="c:*" mode="calc-after" priority="1">
+ <!-- make sure nothing is done for all other nodes with calc-after -->
+</xsl:template>
+
+
+<!--
+ Display a notation intended for use within Iverson's brackets
+
+ @return LaTeX equation
+-->
+<xsl:template match="c:eq|c:ne|c:gt|c:lt|c:gte|c:lte" mode="calc-iversons">
+ <xsl:variable name="name" select="local-name()" />
+
+ <!-- map to LaTeX equivalent -->
+ <xsl:variable name="map">
+ <c id="eq">=</c>
+ <c id="ne">\not=\;</c>
+ <c id="gt">\gt</c>
+ <c id="lt">\lt</c>
+ <c id="gte">\geq</c>
+ <c id="lte">\leq</c>
+ </xsl:variable>
+
+ <xsl:value-of select="$map/*[ @id=$name ]" />
+ <xsl:text> </xsl:text>
+
+ <xsl:apply-templates select="." mode="calc-recurse" />
+</xsl:template>
+
+
+<!--
+ TODO: Technically this is incorrect; sets cannot have duplicate values. This
+ would be best styled as a vector/matrix/etc.
+-->
+<xsl:template match="c:set" priority="1">
+ <xsl:text>\left[</xsl:text>
+ <xsl:for-each select="./c:*">
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="." />
+ </xsl:for-each>
+ <xsl:text>\right]^T</xsl:text>
+</xsl:template>
+
+<!-- style subsets as matrices (easier to read) -->
+<xsl:template match="c:set[ ./c:set ]" priority="5">
+ <xsl:text>\left[\begin{array}\\</xsl:text>
+
+ <xsl:for-each select="./c:set">
+ <xsl:if test="position() > 1">
+ <xsl:text>\\</xsl:text>
+ </xsl:if>
+
+ <xsl:for-each select="./c:*">
+ <xsl:if test="position() > 1">
+ <xsl:text disable-output-escaping="yes"> &amp; </xsl:text>
+ </xsl:if>
+
+ <xsl:text>{</xsl:text>
+ <xsl:apply-templates select="." />
+ <xsl:text>}</xsl:text>
+ </xsl:for-each>
+ </xsl:for-each>
+
+ <xsl:text>\end{array}\right]</xsl:text>
+</xsl:template>
+
+
+<xsl:template match="c:length-of">
+ <xsl:text>\#\left(</xsl:text>
+ <xsl:apply-templates select="./c:*[1]" />
+ <xsl:text>\right)</xsl:text>
+</xsl:template>
+
+<xsl:template match="c:cons">
+ <xsl:text>\textrm{cons}\left(</xsl:text>
+ <xsl:apply-templates select="./c:*[1]" />
+ <xsl:text>,</xsl:text>
+ <xsl:apply-templates select="./c:*[2]" />
+ <xsl:text>\right)</xsl:text>
+</xsl:template>
+
+<xsl:template match="c:car">
+ <xsl:text>\left(</xsl:text>
+ <xsl:apply-templates select="./c:*[1]" />
+ <xsl:text>\right)_0</xsl:text>
+</xsl:template>
+
+<xsl:template match="c:cdr">
+ <xsl:text>\textrm{cdr}\left(</xsl:text>
+ <xsl:apply-templates select="./c:*[1]" />
+ <xsl:text>\right)</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/depgen.xsl b/src/current/include/depgen.xsl
new file mode 100644
index 0000000..d897c27
--- /dev/null
+++ b/src/current/include/depgen.xsl
@@ -0,0 +1,526 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ TODO: we can combine this dependency discovery with the symbol table
+ generation, eliminating extra passes
+
+ TODO: dependency symbols should not duplicate metadata
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="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"
+ xmlns:util="http://www.lovullo.com/util">
+
+
+<xsl: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>
+</xsl:variable>
+
+
+<!-- simply allows invoking the template with dynamic input -->
+<xsl:template name="preproc:gen-deps">
+ <xsl:param name="pkg" as="element( lv:package )" />
+ <xsl:apply-templates select="$pkg" mode="preproc:gen-deps" />
+</xsl:template>
+
+
+<xsl:template match="*" mode="preproc:gen-deps">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:message>
+ <xsl:text>[depgen] *determining symbol dependencies...</xsl:text>
+ </xsl:message>
+
+ <xsl:apply-templates select="preproc:symtable" mode="preproc:depgen" />
+
+ <xsl:sequence select="*" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="preproc:symtable" mode="preproc:depgen" priority="9">
+ <xsl:variable name="symtable" select="." />
+
+ <preproc:sym-deps>
+ <!-- process dependencies for all non-imported symbols -->
+ <xsl:for-each select="preproc:sym[ not( @src ) ]">
+ <xsl:variable name="cursym" select="." />
+
+ <xsl:variable name="deps">
+ <preproc:deps>
+ <xsl:apply-templates select="." mode="preproc:depgen" />
+ </preproc:deps>
+ </xsl:variable>
+
+ <!-- do not output duplicates (we used to not output references
+ to ourselves, but we are now retaining them, since those
+ data are useful) -->
+ <xsl:variable name="uniq" select="
+ $deps/preproc:deps/preproc:sym-ref[
+ not( @name=preceding-sibling::preproc:sym-ref/@name )
+ ]
+ " />
+
+ <!-- symbols must not have themselves as their own dependency -->
+ <xsl:if test="$uniq[ not( $cursym/@allow-circular = 'true' )
+ and ( @name = $cursym/@name
+ or @parent = $cursym/@name ) ]">
+ <xsl:message terminate="yes"
+ select="concat( '[preproc] !!! fatal: symbol ',
+ $cursym/@name,
+ ' references itself ',
+ '(circular dependency)' )" />
+ </xsl:if>
+
+ <!-- grab the original source symbol for these references and augment them
+ with any additional dependency metadata -->
+ <xsl:variable name="syms-rtf">
+ <xsl:for-each select="$uniq">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="sym" select="
+ $symtable/preproc:sym[ @name=$name ]
+ " />
+
+ <!-- we should never have this problem. -->
+ <xsl:if test="not( $sym ) and not( @lax='true' )">
+ <xsl:message terminate="yes">
+ <xsl:text>[depgen] internal error: </xsl:text>
+ <xsl:text>could not locate dependency symbol `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' in local symbol table; needed by </xsl:text>
+ <xsl:value-of select="$cursym/@name" />
+ </xsl:message>
+ </xsl:if>
+
+ <!-- copy and augment (we set @name because $sym/@name may not exist
+ if @lax) -->
+ <preproc:sym name="{@name}">
+ <xsl:if test="$sym">
+ <xsl:sequence select="$sym/@*" />
+ </xsl:if>
+
+ <preproc:meta>
+ <!-- retain type -->
+ <xsl:sequence select="$sym/@type" />
+ <xsl:sequence select="$sym/@dim" />
+
+ <!-- copy any additional metadata -->
+ <xsl:sequence select="@*[ not( local-name() = 'name' ) ]" />
+ </preproc:meta>
+ </preproc:sym>
+ </xsl:for-each>
+ </xsl:variable>
+ <xsl: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 -->
+ <xsl: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) -->
+ <xsl: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">
+ <xsl:sequence select="preproc:meta/@*" />
+ </preproc:sym-ref>
+ </xsl:for-each>
+
+ <!-- @tex provided an non-empty, or function -->
+ <xsl:for-each select="
+ $syms[
+ ( @tex and not( @tex='' ) )
+ or @type='func'
+ ]">
+
+ <xsl:choose>
+ <!-- even if function, @tex overrides symbol -->
+ <xsl:when test="@tex and not( @tex='' )">
+ <preproc:sym-ref tex="{@tex}">
+ <xsl:sequence select="@*" />
+ <xsl:sequence select="preproc:meta/@*" />
+ </preproc:sym-ref>
+ </xsl:when>
+
+ <!-- must be a function; use its name -->
+ <xsl:otherwise>
+ <preproc:sym-ref>
+ <xsl:sequence select="@*" />
+ <xsl:sequence select="preproc:meta/@*" />
+
+ <xsl:attribute name="tex">
+ <xsl:text>\textrm{</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>}</xsl:text>
+ </xsl:attribute>
+ </preproc:sym-ref>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <!-- no @tex, @tex empty, no function -->
+ <xsl:for-each select="
+ $syms[
+ ( not( @tex ) or @tex='' )
+ and not( @type='func' )
+ ]">
+
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="sym" select="." />
+
+ <preproc:sym-ref>
+ <!-- minimal attribute copy (avoid data duplication as much as
+ possible to reduce modification headaches later on) -->
+ <xsl:sequence select="@name, @parent" />
+ <xsl:sequence select="preproc:meta/@*" />
+
+ <!-- assign a symbol -->
+ <xsl:variable name="pos" select="position()" />
+ <xsl:attribute name="tex">
+ <xsl:variable name="texsym" select="
+ $tex-defaults/preproc:syms/preproc:sym[
+ position() = $pos
+ ]
+ " />
+
+ <xsl:choose>
+ <xsl:when test="$sym/@tex and not( $sym/@tex='' )">
+ <xsl:value-of select="$sym/@tex" />
+ </xsl:when>
+
+ <!-- scalar/vector default -->
+ <xsl:when test="$texsym and number( $sym/@dim ) lt 2">
+ <xsl:value-of select="$texsym/@value" />
+ </xsl:when>
+
+ <!-- matrix default -->
+ <xsl:when test="$texsym">
+ <xsl:value-of select="$texsym/@vec" />
+ </xsl:when>
+
+ <!-- no default available; generate one -->
+ <xsl:otherwise>
+ <xsl:value-of select="
+ if ( number( $sym/@dim ) lt 2 ) then '\theta'
+ else '\Theta'
+ " />
+ <xsl:text>_{</xsl:text>
+ <xsl:value-of select="$pos" />
+ <xsl:text>}</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </preproc:sym-ref>
+ </xsl:for-each>
+ </preproc:sym-dep>
+ </xsl:for-each>
+ </preproc:sym-deps>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @extern='true' ]" mode="preproc:depgen" priority="9">
+ <!-- externs will be processed once they are resolved in another package -->
+</xsl:template>
+
+
+<!-- all symbols with a @parent (e.g. generators) should depend on the parent
+ itself (which of course introduces the parent's dependencies into the tree) -->
+<xsl:template match="preproc:sym[ @parent ]" mode="preproc:depgen" priority="7">
+ <preproc:sym-ref name="{@parent}" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='rate' ]" mode="preproc:depgen" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <xsl:variable name="rate" as="element( lv:rate )"
+ select="$pkg/lv:rate[ @yields=$name ]" />
+
+ <xsl:apply-templates mode="preproc:depgen"
+ select="$rate" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='class' ]" mode="preproc:depgen" priority="5">
+ <!-- all class symbol names are prefixed with ":class:" -->
+ <xsl:variable name="as" select="substring-after( @name, ':class:' )" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <xsl:apply-templates
+ select="$pkg/lv:classify[ @as=$as ]"
+ mode="preproc:depgen" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='param' ]" mode="preproc:depgen" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:apply-templates
+ select="root(.)/lv:param[ @name=$name ]"
+ mode="preproc:depgen" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='func' ]" mode="preproc:depgen" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <xsl:apply-templates
+ select="$pkg/lv:function[ @name=$name ]"
+ mode="preproc:depgen" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='type' ]" mode="preproc:depgen" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <!-- a typedef could optionally be contained within another typedef -->
+ <xsl:apply-templates mode="preproc:depgen" select="
+ $pkg/lv:typedef[ @name=$name ]
+ , $pkg/lv:typedef//lv:typedef[ @name=$name ]
+ " />
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='lparam' ]" mode="preproc:depgen" priority="5">
+ <!-- do nothing -->
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='const' ]" mode="preproc:depgen" priority="5">
+ <!-- do nothing -->
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='tpl' ]" mode="preproc:depgen" priority="5">
+ <!-- do nothing -->
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='meta' ]" mode="preproc:depgen" priority="5">
+ <!-- do nothing -->
+</xsl:template>
+
+
+<xsl:template match="preproc:sym" mode="preproc:depgen" priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>[depgen] error: unexpected symbol </xsl:text>
+ <xsl:sequence select="." />
+ </xsl:message>
+</xsl:template>
+
+
+<xsl:template name="preproc:depgen-c-normal" match="c:value-of|c:when" mode="preproc:depgen" priority="5">
+ <xsl:param name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+
+ <xsl:variable name="sym"
+ select="$pkg/preproc:symtable/preproc:sym[ @name=$name ]" />
+
+ <!-- see if there is a c:let associated with this name -->
+ <xsl:variable name="let" select="
+ ancestor::c:let[ c:values/c:value/@name=$name ]
+ " />
+
+ <xsl:choose>
+ <!-- c:let reference -->
+ <xsl:when test="$let">
+ <preproc:sym-ref name=":{$let/@name}:{$name}" />
+ </xsl:when>
+
+ <!-- scalar constant -->
+ <xsl: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}" />
+ </xsl:when>
+
+ <!-- function param reference -->
+ <xsl:when test="$name=ancestor::lv:function/lv:param/@name">
+ <xsl:variable name="fname" as="xs:string"
+ select="ancestor::lv:function/@name" />
+
+ <preproc:sym-ref name=":{$fname}:{$name}"
+ varname="{$name}"/>
+ </xsl:when>
+
+ <!-- index reference -->
+ <xsl:when test="$name=ancestor::c:*[ @of ]/@index" />
+
+ <!-- unknown symbol (it is important to do this after the above checks) -->
+ <xsl:when test="not( $sym )">
+ <!-- do not terminate; validator can provide additional information -->
+ <xsl:message>
+ <xsl:text>[depgen] warning: unknown symbol `</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:when>
+
+ <xsl:when test="$sym/@parent">
+ <preproc:sym-ref name="{$sym/@name}" parent="{$sym/@parent}" />
+ </xsl:when>
+
+ <!-- just an average 'ol symbol -->
+ <xsl:otherwise>
+ <preproc:sym-ref name="{$name}" />
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:apply-templates mode="preproc:depgen" />
+</xsl:template>
+
+
+<xsl:template match="c:sum[@of]|c:product[@of]" mode="preproc:depgen" priority="5">
+ <!-- process using @of -->
+ <xsl:call-template name="preproc:depgen-c-normal">
+ <xsl:with-param name="name" select="@of" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template match="c:apply" mode="preproc:depgen" priority="5">
+ <!-- no special treatment yet -->
+ <xsl:call-template name="preproc:depgen-c-normal" />
+</xsl:template>
+
+<xsl:template match="c:apply/c:arg" mode="preproc:depgen" priority="5">
+ <!-- arguments may have calculations, so we must recurse -->
+ <xsl:apply-templates mode="preproc:depgen" />
+</xsl:template>
+
+
+<xsl:template match="c:let/c:values/c:value" mode="preproc:depgen" priority="5">
+ <!-- do not consider the c:value name -->
+ <xsl:apply-templates mode="preproc:depgen" />
+</xsl:template>
+
+
+<xsl:template name="preproc:depgen-match">
+ <xsl:param name="on" select="@on" />
+
+ <xsl:variable name="class" select="ancestor::lv:classify" />
+ <xsl:variable name="sym"
+ select="root(.)/preproc:symtable/preproc:sym[ @name=$on ]" />
+
+ <!-- are we depending on another classification? -->
+ <xsl:if test="$sym/@type='cgen'">
+ <xsl: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 -->
+ <xsl:if test="$sym/@extclass='true' and not( $class/@external='true' )">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc] !!! fatal: internal classification `</xsl:text>
+ <xsl:value-of select="$class/@as" />
+ <xsl:text>' cannot pull in external classification `</xsl:text>
+ <xsl:value-of select="$cname" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ </xsl:if>
+
+ <!-- process the @on -->
+ <xsl:call-template name="preproc:depgen-c-normal">
+ <xsl:with-param name="name" select="$on" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template match="lv:match[ @value ]" mode="preproc:depgen" priority="5">
+ <!-- process the @value -->
+ <xsl:call-template name="preproc:depgen-c-normal">
+ <xsl:with-param name="name" select="@value" />
+ </xsl:call-template>
+
+ <xsl:call-template name="preproc:depgen-match" />
+</xsl:template>
+
+
+<xsl:template match="lv:match[ @anyOf ]" mode="preproc:depgen" priority="6">
+ <!-- process the "normal" match -->
+ <xsl:call-template name="preproc:depgen-match" />
+
+ <!-- we depend on the type -->
+ <preproc:sym-ref name="{@anyOf}" />
+ <xsl:call-template name="preproc:depgen-match" />
+</xsl:template>
+
+
+<xsl:template match="lv:match[ @pattern ]" mode="preproc:depgen" priority="5">
+ <!-- there are no pattern dependencies; process @on -->
+ <xsl:call-template name="preproc:depgen-match" />
+</xsl:template>
+
+
+<!-- match on calculated value -->
+<xsl:template match="lv:match[ c:* ]" mode="preproc:depgen" priority="6">
+ <!-- process the "normal" match -->
+ <xsl:call-template name="preproc:depgen-match" />
+
+ <!-- process the calculation dependencies -->
+ <xsl:apply-templates select="c:*" mode="preproc:depgen" />
+</xsl:template>
+
+
+<xsl:template match="lv:template/lv:param" mode="preproc:depgen" priority="9">
+ <!-- ignore -->
+</xsl:template>
+
+
+<xsl:template match="lv:param" mode="preproc:depgen" priority="5">
+ <!-- while the type is reduced to a primitive, let's still include the
+ dependency symbol -->
+ <preproc:sym-ref name="{@type}" />
+</xsl:template>
+
+
+<xsl:template match="lv:typedef" mode="preproc:depgen" priority="5">
+ <!-- we depend on any types that we create a union of -->
+ <xsl:for-each select="lv:union/lv:typedef">
+ <preproc:sym-ref name="{@name}" />
+ </xsl:for-each>
+</xsl:template>
+
+
+<!-- @class deps -->
+<xsl:template match="lv:class" mode="preproc:depgen" priority="5">
+ <preproc:sym-ref name=":class:{@ref}" class-no="{@no}" />
+</xsl:template>
+
+
+<xsl:template match="c:*|lv:*" mode="preproc:depgen" priority="3">
+ <!-- ignore -->
+ <xsl:apply-templates mode="preproc:depgen" />
+</xsl:template>
+
+
+<xsl:template match="text()" mode="preproc:depgen" priority="2">
+ <!-- not interested. nope. -->
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/display.xsl b/src/current/include/display.xsl
new file mode 100644
index 0000000..7296b56
--- /dev/null
+++ b/src/current/include/display.xsl
@@ -0,0 +1,551 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Display-related tasks
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:sym="http://www.lovullo.com/rater/symbol-map"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+ xmlns:summary="http://www.lovullo.com/rater/summary"
+
+ xmlns:exsl="http://exslt.org/common"
+ extension-element-prefixes="exsl">
+
+
+<!-- maps certain elements to their default symbols -->
+<xsl:variable name="symbol-map" select="document( 'symbol-map.xml' )/sym:symbol-map/*" />
+
+<!-- easy-to-reference linked dependency list -->
+<xsl:variable name="edeps" select="/lv:*/preproc:deps/preproc:sym" />
+
+<xsl:template name="get-symbol-map">
+ <xsl:copy-of select="$symbol-map" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='rate' ]" mode="summary:desc" priority="5">
+ <span class="letlist-{@name}">
+ <a href="#{@name}">
+ <xsl:value-of select="@name" />
+ </a>
+ <xsl:text> scalar</xsl:text>
+ </span>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='gen' ]" mode="summary:desc" priority="5">
+ <span class="letlist-{@parent}">
+ <a href="#{@parent}">
+ <xsl:value-of select="@name" />
+ </a>
+ <xsl:text> generator; vector</xsl:text>
+
+ <span class="param">
+ <xsl:text> (</xsl:text>
+ <a href="#{@parent}">
+ <xsl:value-of select="@parent" />
+ </a>
+ <xsl:text>)</xsl:text>
+ </span>
+ </span>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='cgen' ]" mode="summary:desc" priority="5">
+ <xsl:variable name="parent" select="@parent" />
+ <xsl:variable name="sym" select="
+ ancestor::preproc:symtable/preproc:sym[ @name=$parent ]
+ " />
+
+ <xsl:apply-templates select="$sym" mode="summary:desc" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='class' ]" mode="summary:desc" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="document" select="
+ if ( @src ) then
+ document( concat( @src, '.xmlo' ), . )/lv:*
+ else
+ /lv:*
+ " />
+ <xsl:variable name="class" select="
+ $document/lv:classify[
+ @as=substring-after( $name, ':class:' )
+ ]
+ " />
+
+ <span class="letlist-{$class/@as}">
+ <xsl:text>"</xsl:text>
+ <xsl:value-of select="$class/@desc" />
+ <xsl:text>"</xsl:text>
+ <xsl:text> classification </xsl:text>
+
+ <xsl:choose>
+ <xsl:when test="@dim = '0'">
+ <xsl:text>scalar</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="@dim = '1'">
+ <xsl:text>vector</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="@dim = '2'">
+ <xsl:text>matrix</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text> [dim </xsl:text>
+ <xsl:value-of select="@dim" />
+ <xsl:text>]</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- TODO: use generator in letlist-* -->
+ <span class="param">
+ <xsl:text> (</xsl:text>
+ <a href="#:class:{$class/@as}">
+ <xsl:value-of select="$class/@as" />
+ </a>
+ <xsl:text>)</xsl:text>
+ </span>
+ </span>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='const' ]" mode="summary:desc" priority="5">
+ <xsl:value-of select="@name" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='param' ]" mode="summary:desc" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="document" select="
+ if ( @src ) then
+ document( concat( @src, '.xmlo' ), . )/lv:*
+ else
+ /lv:*
+ " />
+ <xsl:variable name="param" select="
+ $document/lv:param[
+ @name=$name
+ ]
+ " />
+
+ <xsl:value-of select="$param/@desc" />
+
+ <span class="param letlist-{$param/@name}">
+ <xsl:text> (</xsl:text>
+ <a href="#{$param/@name}">
+ <xsl:value-of select="$param/@name" />
+ </a>
+ <xsl:text>)</xsl:text>
+ </span>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym" mode="summary:desc" priority="1">
+ <xsl:value-of select="@name" />
+ <xsl:text> (!)</xsl:text>
+</xsl:template>
+
+
+<xsl:template name="get-symbol">
+ <xsl:param name="name" select="@name" />
+ <xsl:param name="index" />
+ <xsl:param name="index-symbol" />
+ <xsl:param name="default" />
+
+ <preproc:sym-ref name="{$name}">
+ <!-- might be an empty string (if provided) -->
+ <xsl:if test="$default">
+ <xsl:attribute name="default" select="$default" />
+ </xsl:if>
+ </preproc:sym-ref>
+
+ <xsl:choose>
+ <xsl:when test="$index-symbol != ''">
+ <xsl:text>_{</xsl:text>
+ <xsl:value-of select="$index-symbol" />
+ <xsl:text>}</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$index">
+ <xsl:text>_{</xsl:text>
+ <preproc:sym-ref name="{$index}" default="{$index}" />
+ <xsl:text>}</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="_get-index-symbol">
+ <xsl:param name="element" />
+ <xsl:param name="index" />
+ <xsl:param name="search" />
+
+ <xsl:call-template name="get-symbol">
+ <xsl:with-param name="name" select="$index" />
+ <xsl:with-param name="search" select="$search" />
+ <xsl:with-param name="default" select="$index" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<!--
+ Retrieve the default symbol for the given type (in LaTeX)
+
+ If the type is "function", the given name will be used for its default symbol.
+
+ @param Node element node to retrieve symbol for
+ @param NodeSet search all document nodes
+
+ @return default symbol (LaTeX)
+-->
+<xsl:template name="_get-default-symbol">
+ <xsl:param name="element" />
+ <xsl:param name="name" />
+ <xsl:param name="index" />
+ <xsl:param name="search" />
+
+ <xsl:variable name="type">
+ <xsl:choose>
+ <xsl:when test="
+ ( local-name( $element ) = 'param' )
+ and ( local-name( $element/.. ) = 'function' )">
+
+ <!-- this is a function parameter; make a distinction between a global
+ parameter -->
+ <xsl:text>fparam</xsl:text>
+ </xsl:when>
+
+ <!-- if matching lv:classify/@as, then it represents an accumulator -->
+ <xsl:when test="
+ ( local-name( $element ) = 'classify' )
+ and ( $element/@as = $name )
+ ">
+
+ <xsl:text>class</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$element/@generates = $name">
+ <xsl:text>generator</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="local-name( $element )" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="symbol" select="$symbol-map[@type=$type]" />
+
+ <!-- output the symbol default -->
+ <xsl:choose>
+ <!-- certain types use their own name for a default (e.g. functions) -->
+ <xsl:when test="$symbol/sym:name">
+ <xsl:text>\textrm{</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>}</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$symbol/sym:nothing">
+ <!-- do nothing; no symbol is desired -->
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:if test="$index and ( $index != '' )">
+ <xsl:text>(</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$symbol" />
+
+ <!-- determine if our default index should be subscript or superscript -->
+ <xsl:variable name="subsup">
+ <xsl:choose>
+ <xsl:when test="$symbol/@index-pos">
+ <xsl:value-of select="$symbol/@index-pos" />
+ </xsl:when>
+
+ <!-- default to subscript -->
+ <xsl:otherwise>
+ <xsl:text>_</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- in addition to the symbol itself, which alone is not likely to be
+ unique, we will add a subscript to uniquely identify it by number -->
+ <xsl:if test="$search">
+ <xsl:value-of select="$subsup" />
+ <xsl:text>{</xsl:text>
+
+ <xsl:call-template name="_get-name-index">
+ <xsl:with-param name="element" select="$element" />
+ <xsl:with-param name="name" select="$name" />
+ <xsl:with-param name="search" select="$search" />
+ </xsl:call-template>
+
+ <xsl:text>}</xsl:text>
+ </xsl:if>
+
+ <xsl:if test="$index and ( $index != '' )">
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+
+ <!-- if an index was given, and our default index was *not* a subscript,
+ then we can dedicate the subscript to the index -->
+ <xsl:if test="$index and ( $index != '' )">
+ <xsl:text>_{</xsl:text>
+ <xsl:value-of select="$index" />
+ <xsl:text>}</xsl:text>
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Retrieve index of the element associated with the given name across all named
+ elements of the same type and parent type in all of $search
+
+ TODO: surely there's a more performant manner...not that speed is an issue
+ right now
+
+ @param Node element node to retrieve symbol for
+ @param NodeSet search all document nodes
+
+ @return index
+-->
+<xsl:template name="_get-name-index">
+ <xsl:param name="element" />
+ <xsl:param name="name" />
+ <xsl:param name="search" />
+
+ <xsl:choose>
+ <!-- functions are handled slightly differently, as they introduce scope -->
+ <xsl:when test="local-name( $element/.. ) = 'function'">
+ <xsl:for-each select="$element/../lv:param">
+ <xsl:if test="@name = $name">
+ <xsl:value-of select="position()" />
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:when>
+
+ <!-- non-function -->
+ <xsl:otherwise>
+ <xsl:value-of select="
+ $search//summary:default-indexes/summary:index[ @name=$name ]/@value"
+ />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Retrieve description for the given element by name
+
+ $vardesc, for those who support it, is useful if the description describes the
+ node, not a variable generated from it. For example, lv:classify's description
+ is a short description of the classification, but if documenting @yields, we
+ want to describe what it is yielding, which would not be immediately clear
+ from the description.
+
+ @param string name name of element
+ @param NodeSet search all documents to search
+
+ @return element description
+-->
+<xsl:template name="get-desc">
+ <xsl:param name="name" />
+ <xsl:param name="search" />
+
+ <!-- XXX: Have to maintain this list! -->
+ <xsl:variable name="desc"
+ select="$search//summary:descs/summary:desc[ @name=$name ]/@desc" />
+
+ <xsl:choose>
+ <xsl:when test="$desc">
+ <xsl:copy-of select="$desc" />
+ </xsl:when>
+
+ <!-- if we cannot find the element, then display an error -->
+ <xsl:otherwise>
+ <span class="error">
+ <xsl:text>Unknown @name reference: </xsl:text>
+ <xsl:value-of select="$name" />
+ </span>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+
+
+
+<!--
+ Retrieve processed name for the given element by name
+
+ @param string name name of element
+ @param NodeSet search all documents to search
+
+ @return element description
+-->
+<xsl:template name="get-name">
+ <xsl:param name="name" />
+ <xsl:param name="search" />
+
+ <xsl:value-of select="
+ $search//summary:descs/summary:desc[ @name=$name ]/@display
+ " />
+</xsl:template>
+
+
+<xsl:template match="lv:rate" mode="gen-let-list" priority="5">
+ <xsl:param name="deps" />
+ <xsl:param name="context" />
+
+ <xsl:call-template name="do-gen-let-list">
+ <xsl:with-param name="symname" select="@yields" />
+ <xsl:with-param name="context" select="$context" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template match="lv:function" mode="gen-let-list" priority="5">
+ <xsl:param name="deps" />
+
+ <xsl:call-template name="do-gen-let-list">
+ <xsl:with-param name="symname" select="@name" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template match="*" mode="gen-let-list" priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>[summary] !!! unknown let-list type </xsl:text>
+ <xsl:value-of select="name()" />
+ </xsl:message>
+</xsl:template>
+
+
+<!--
+ Generate list of let statements describing each variable in the given node set
+
+ Variables come from various sources depending on the operation being
+ performed.
+-->
+<xsl:template name="do-gen-let-list">
+ <xsl:param name="context" />
+ <xsl:param name="symname" />
+
+ <xsl:variable name="deps" select="
+ /lv:*/preproc:sym-deps/preproc:sym-dep[
+ @name=$symname
+ ]
+ " />
+
+ <ul class="let">
+ <!-- output a description for each dependency -->
+ <xsl:variable name="result">
+ <xsl:for-each select="
+ /lv:*/preproc:symtable/preproc:sym[
+ not( @type='lparam' )
+ and @name=$deps/preproc:sym-ref/@name
+ ]
+ ">
+
+ <xsl:call-template name="_gen-let-list-item">
+ <xsl:with-param name="context" select="$context" />
+ </xsl:call-template>
+ </xsl:for-each>
+
+
+ <!-- handle c:let formatting separately -->
+ <xsl:for-each select="
+ /lv:*/preproc:symtable/preproc:sym[
+ @type='lparam'
+ and @name=$deps/preproc:sym-ref/@name
+ ]
+ ">
+
+ <xsl:call-template name="_gen-let-list-item">
+ <xsl:with-param name="context" select="$context" />
+ <xsl:with-param name="class" select="'letequ'" />
+ </xsl:call-template>
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:apply-templates select="$result" mode="typeset-final">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+ </ul>
+</xsl:template>
+
+
+<xsl:template name="_gen-let-list-item">
+ <xsl:param name="context" />
+ <xsl:param name="class" />
+
+ <li>
+ <xsl:if test="$class">
+ <xsl:attribute name="class" select="$class" />
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="@type='lparam' and $context">
+ <xsl:text>\(</xsl:text>
+ <preproc:sym-ref name="{@name}" />
+ <xsl:text> = </xsl:text>
+
+ <xsl:variable name="varname" select="@varname" />
+
+ <xsl:apply-templates select="
+ $context//c:let/c:values/c:value[
+ @name=$varname
+ ]/c:*
+ " />
+ <xsl:text>\) </xsl:text>
+
+ <span class="letdesc">
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="@desc" />
+ <xsl:text>)</xsl:text>
+ </span>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>let \(</xsl:text>
+ <preproc:sym-ref name="{@name}" />
+ <xsl:text>\) = </xsl:text>
+
+ <xsl:apply-templates select="." mode="summary:desc" />
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!--
+ <xsl:variable name="param-name">
+ <xsl:call-template name="get-name">
+ <xsl:with-param name="name" select="$param" />
+ <xsl:with-param name="search" select="/" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:if test="$param-name != ''">
+ <span class="param letlist-{$param-name}">
+ <xsl:text> (</xsl:text>
+ <a href="#{$param-name}">
+ <xsl:value-of select="$param-name" />
+ </a>
+ <xsl:text>)</xsl:text>
+ </span>
+ </xsl:if>
+ -->
+ </li>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/dslc-base.xsl b/src/current/include/dslc-base.xsl
new file mode 100644
index 0000000..53a3195
--- /dev/null
+++ b/src/current/include/dslc-base.xsl
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Additional functionality provided by dslc
+
+ XSL does not provide every feature suitable for compilation (which is no
+ suprise, since this was not its intended use case). As such, dslc provides
+ additional features that are defined/abstracted within this file; every
+ template that is intended for use with dslc should include this.
+-->
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+
+<!--
+ Package source path, stripped of its extension
+
+ XSL does not provide a means of exposing the file path (nor should it,
+ really). This param will hold the source path of the package, sans its
+ extension, relative to the project root that was used during compilation.
+
+ I.e., given this source path:
+ suppliers/common/foo.xml
+ we would expect this value for __srcpkg:
+ suppliers/common/foo
+
+ By stripping the extension, we have the benefit of being void of any semantics
+ that may be associated with it (e.g. xml vs xmlo vs xmle); rather, that
+ information should be derived from the structe of the document itself and the
+ path can be used as an identifier to describe the document as a whole,
+ regardless of what form it is in.
+
+ Consequently, no two files are able to have the same __srcpkg string; this
+ value may therefore be used for disambiguation.
+-->
+<xsl:param name="__srcpkg" />
+
+
+<!--
+ Relative path to project root
+
+ The project root is determined entirely by __srcpath by repeating the string
+ "../" for the number of occurrances of "/".
+-->
+<xsl:param name="__relroot" />
+
+
+<!--
+ Random value that may be used to seed random values
+
+ XSLT is deterministic and does not offer support for generating random values;
+ its generate-id() function is not sufficient for cross-package generation.
+-->
+<xsl:param name="__rseed" />
+
+
+<!--
+ Root node of template on which stylesheet was invoked
+
+ This points to the original, unprocessed document. This is especially
+ important for `document' function calls, which use nodes as a reference
+ point for resolving relative paths.
+-->
+<xsl:variable name="__entry-root" select="/" />
+
+
+
+<!--
+ Apply relative root to PATH
+
+ If PATH is an absolute path, it will be prefixed with the relative root
+ with the leading path delimiter stripped; otherwise, it will be echoed
+ as-is.
+-->
+<xsl:template name="__apply-relroot">
+ <xsl:param name="path" />
+
+ <xsl:choose>
+ <xsl:when test="starts-with( $path, '/' )">
+ <xsl:value-of select="$__relroot" />
+ <xsl:value-of select="substring-after( $path, '/' )" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="$path" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/entry-form.xsl b/src/current/include/entry-form.xsl
new file mode 100644
index 0000000..3d6b3c8
--- /dev/null
+++ b/src/current/include/entry-form.xsl
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Outputs HTML form that can be used to feed values to the rater for testing
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:l="http://www.lovullo.com/rater/linker"
+ xmlns:util="http://www.lovullo.com/util"
+ xmlns:ext="http://www.lovullo.com/ext"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+
+ xmlns:exsl="http://exslt.org/common"
+ xmlns:str="http://exslt.org/strings"
+ extension-element-prefixes="exsl str">
+
+
+<!--
+ Generate HTML entry form for testing
+
+ Allows for collection of data to feed to the rater.
+
+ The entry form will only be generated for raters, not other packages (since
+ actual rating will need to be performed).
+
+ @return form HTML
+-->
+<xsl:template match="lv:package" mode="entry-form">
+ <xsl:param name="root-pkg" />
+
+ <form class="entry-form">
+ <h1>Rating Test Case</h1>
+
+ <div class="foot">
+ <p id="prior-message"></p>
+
+ <div>
+ <input type="submit" value="Calculate Premium" />
+ <input type="reset" value="Reset" />
+ </div>
+
+ <div class="final-premium"></div>
+
+ <div class="final-accept">
+ <button id="final-accept-good">Looks Good!</button>
+ <button id="final-accept-bad">Incorrect</button>
+ </div>
+
+ <div class="final-comments">
+ <h1>Submit Test Case</h1>
+
+ <p>Submission comments (please describe what you were testing, the
+ desired result and, if the premium was incorrect, what went wrong):</p>
+
+ <textarea id="final-comments"></textarea>
+
+ <div id="final-expect-container">
+ <p>Expected premium (if known; must be exact); this will allow us to
+ automatically re-run this test when we believe that the problem has been
+ fixed. <strong>Otherwise, you must re-test manually:</strong></p>
+ <input type="text" id="final-expected" value="" />
+ <em>(Only fill out if it does not hit the minimum premium.)</em>
+ </div>
+
+ <br />
+ <label><input type="checkbox" id="final-waiting"> Requires Testing</input></label>
+
+ <br />
+ <button id="final-submit">Submit</button>
+ <button id="final-submit-new">Submit As New Test Case</button>
+ <button id="final-cancel">Nevermind. Cancel.</button>
+ </div>
+ </div>
+
+ <dl>
+ <!-- generate HTML elements for each *global* parameter, *but only if it
+ is used in the rater* -->
+ <xsl:apply-templates
+ select="/lv:package/l:dep/preproc:sym[ @type='param' ]"
+ mode="entry-form">
+
+ <xsl:with-param name="root-pkg" select="$root-pkg" />
+ </xsl:apply-templates>
+ </dl>
+ </form>
+
+ <script type="text/javascript" src="{$fw-path}/rater/scripts/entry-form.js"></script>
+</xsl:template>
+
+
+<!--
+ Generate text and input for a global parameter
+
+ @return parameter HTML
+-->
+<xsl:template match="preproc:sym" mode="entry-form">
+ <xsl:param name="root-pkg" />
+
+ <xsl:variable name="self" select="." />
+ <xsl:variable name="package" select="
+ if ( @src and not( @src='' ) ) then
+ document( concat( @src, '.xmlo' ), . )/lv:*
+ else
+ $root-pkg
+ " />
+
+ <xsl:variable name="name">
+ <xsl:value-of select="@name" />
+
+ <!-- if this is a set, then we will need to generate an array of
+ elements -->
+ <xsl:if test="number(@dim) gt 0">
+ <xsl:text>[]</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:variable name="param"
+ select="$package/lv:param[ @name=$self/@name ]" />
+
+ <dt id="param-{@name}">
+ <xsl:value-of select="@desc" />
+ </dt>
+
+ <xsl:variable name="matrix">
+ <xsl:if test="number(@dim) gt 1">
+ <xsl:text> matrix</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- generate field itself -->
+ <dd id="param-input-{@name}">
+ <div class="entry-row{$matrix}">
+ <div class="entry-field">
+ <xsl:apply-templates select="$param" mode="entry-form-field">
+ <xsl:with-param name="name" select="$name" />
+ <xsl:with-param name="sym" select="$self" />
+ <xsl:with-param name="pkg" select="$package" />
+ </xsl:apply-templates>
+
+ <!-- if this is a set, add the ability to remove values -->
+ <xsl:if test="number(@dim) gt 0">
+ <button class="entry-rm">-</button>
+ </xsl:if>
+ </div>
+
+ <xsl:if test="number(@dim) gt 1">
+ <button class="entry-add-matrix">+</button>
+ </xsl:if>
+ </div>
+
+ <!-- if this is a set, add ability to add values -->
+ <xsl:if test="number(@dim) gt 0">
+ <button class="entry-add">+</button>
+ </xsl:if>
+ </dd>
+</xsl:template>
+
+
+<!--
+ Generate input field for integer parameters
+
+ @return parameter HTML
+-->
+<xsl:template match="lv:param[@type='integer']" mode="entry-form-field">
+ <xsl:param name="name" select="@name" />
+ <input type="text" name="{$name}" value="{@default}" />
+</xsl:template>
+
+
+<!--
+ Generate input field for float parameters
+
+ @return parameter HTML
+-->
+<xsl:template match="lv:param[@type='float']" mode="entry-form-field">
+ <xsl:param name="name" select="@name" />
+ <input type="text" name="{$name}" value="{@default}" />
+</xsl:template>
+
+
+<!--
+ Generate radio fields for boolean parameters
+
+ @return parameter HTML
+-->
+<xsl:template match="lv:param[@type='boolean']" mode="entry-form-field">
+ <xsl:param name="name" select="@name" />
+
+ <xsl:variable name="default-y">
+ <xsl:if test="@default = '1'">
+ <xsl:text>selected</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:variable name="default-n">
+ <xsl:if test="@default = '0'">
+ <xsl:text>selected</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+
+ <select name="{$name}">
+ <option selected="{$default-y}" value="1">Yes</option>
+ <option selected="{$default-n}" value="0">No</option>
+ </select>
+</xsl:template>
+
+
+<!--
+ Handle parameters that are either of unknown or user-defined types
+
+ @return parameter HTML
+-->
+<xsl:template match="lv:param" mode="entry-form-field">
+ <xsl:param name="name" select="@name" />
+ <xsl:param name="sym" />
+ <xsl:param name="pkg" />
+
+ <xsl:variable name="type" select="@type" />
+
+ <!-- the typedef may or may not be in the same package as the param -->
+ <xsl:variable name="typesym" select="
+ $pkg/preproc:symtable/preproc:sym[
+ @type='type'
+ and @name=$type
+ ]
+ " />
+
+ <!-- if the @src attribute is empty, then it resides within the same package
+ -->
+ <xsl:variable name="typesrc">
+ <xsl:choose>
+ <xsl:when test="@src">
+ <xsl:value-of select="@src" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="$sym/@src" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- load the typedef from the appropriate package -->
+ <xsl:variable name="typepkg" select="
+ if ( @src and not( @src='' ) ) then
+ document( concat( $typesrc, '.xmlo' ), $sym )/lv:*
+ else
+ $pkg
+ " />
+ <!-- this makes maintinance more difficult, but speeds up searching large
+ trees -->
+ <xsl:variable name="typedef" select="
+ $typepkg/lv:typedef[ @name=$type ]
+ |$typepkg/lv:typedef/lv:union/lv:typedef[ @name=$type ]
+ " />
+
+ <xsl:choose>
+ <xsl:when test="$typedef/lv:enum|$typedef/lv:union">
+ <xsl:apply-templates select="." mode="entry-form-field-enum">
+ <xsl:with-param name="name" select="$name" />
+ <xsl:with-param name="typedef" select="$typedef" />
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:message>
+ <xsl:text>[summary] warning: unknown param type `</xsl:text>
+ <xsl:value-of select="$typesym/@src" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="@type" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+
+ <span class="error">
+ <xsl:text>Unknown type: </xsl:text>
+ <xsl:value-of select="@type" />
+ </span>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Generate HTML for enumerated lists
+
+ @return parameter HTML
+-->
+<xsl:template match="lv:param" mode="entry-form-field-enum">
+ <xsl:param name="name" select="@name" />
+ <xsl:param name="typedef" />
+
+ <xsl:variable name="type" select="@type" />
+
+ <!-- get all fields, even if they're within a union -->
+ <xsl:variable name="fields" select="$typedef//lv:enum/lv:item" />
+
+ <select name="{$name}" value="{@default}">
+ <option value=""></option>
+
+ <xsl:for-each select="$fields">
+ <xsl:variable name="value">
+ <xsl:choose>
+ <xsl:when test="@value">
+ <xsl:value-of select="@value" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@name" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <option value="{$value}">
+ <xsl:value-of select="@name" />
+ <xsl:text>: </xsl:text>
+ <xsl:value-of select="@desc" />
+ </option>
+ </xsl:for-each>
+ </select>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/exslt/str.tokenize.template.xsl b/src/current/include/exslt/str.tokenize.template.xsl
new file mode 100644
index 0000000..6da270b
--- /dev/null
+++ b/src/current/include/exslt/str.tokenize.template.xsl
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:str="http://exslt.org/strings"
+ extension-element-prefixes="str">
+
+<xsl:template name="str:tokenize">
+ <xsl:param name="string" select="''" />
+ <xsl:param name="delimiters" select="' &#x9;&#xA;'" />
+ <xsl:choose>
+ <xsl:when test="not($string)" />
+ <xsl:when test="not($delimiters)">
+ <xsl:call-template name="str:_tokenize-characters">
+ <xsl:with-param name="string" select="$string" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="str:_tokenize-delimiters">
+ <xsl:with-param name="string" select="$string" />
+ <xsl:with-param name="delimiters" select="$delimiters" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="str:_tokenize-characters">
+ <xsl:param name="string" />
+ <xsl:if test="$string">
+ <token><xsl:value-of select="substring($string, 1, 1)" /></token>
+ <xsl:call-template name="str:_tokenize-characters">
+ <xsl:with-param name="string" select="substring($string, 2)" />
+ </xsl:call-template>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template name="str:_tokenize-delimiters">
+ <xsl:param name="string" />
+ <xsl:param name="delimiters" />
+ <xsl:variable name="delimiter" select="substring($delimiters, 1, 1)" />
+ <xsl:choose>
+ <xsl:when test="not($delimiter)">
+ <token><xsl:value-of select="$string" /></token>
+ </xsl:when>
+ <xsl:when test="contains($string, $delimiter)">
+ <xsl:if test="not(starts-with($string, $delimiter))">
+ <xsl:call-template name="str:_tokenize-delimiters">
+ <xsl:with-param name="string" select="substring-before($string, $delimiter)" />
+ <xsl:with-param name="delimiters" select="substring($delimiters, 2)" />
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:call-template name="str:_tokenize-delimiters">
+ <xsl:with-param name="string" select="substring-after($string, $delimiter)" />
+ <xsl:with-param name="delimiters" select="$delimiters" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="str:_tokenize-delimiters">
+ <xsl:with-param name="string" select="$string" />
+ <xsl:with-param name="delimiters" select="substring($delimiters, 2)" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/orderizer.xsl b/src/current/include/orderizer.xsl
new file mode 100644
index 0000000..170bd2b
--- /dev/null
+++ b/src/current/include/orderizer.xsl
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ No, not "odorizer".
+
+ This is a powerful system that takes an immensely complex (and insurmountable)
+ task out of the programmer's hands. In particular, the system:
+
+ - Uses the map to recognize the order in which params appear in a UI
+ - Using that order, determines which fields in the UI could potentially
+ cause fields that appear previous to them to become invalid (e.g. hide)
+ due to classification criteria.
+ - Based on those conflicts, constructs a custom classification that ignores
+ only the conflicting portions, allowing a great deal of precision in
+ controlling field validity up to that particular point in the UI.
+
+ The result is highly-refined, custom-generated classifications per-question
+ for only the criteria that are needed to ensure that fields cannot hide fields
+ previous to them: A task that would otherwise be prohibitively complicated (or
+ would otherwise have to be far too coarse) if done manually in systems with
+ highly sophisticated classification schemes. Furthermore, the result is wholly
+ deterministic.
+
+ It should be noted that futher benefit can be realized when the map is altered
+ or questions in the UI are reordered; the system will simply re-calculate new
+ classifications that yield desirable results.
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:lvp="http://www.lovullo.com"
+ xmlns:lvm="http://www.lovullo.com/rater/map"
+ xmlns:lvmc="http://www.lovullo.com/rater/map/compiler"
+ xmlns:gc="http://www.lovullo.com/calc/global-classifier"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:o="http://www.lovullo.com/rater/compiler/orderizer"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+
+<!--
+ Determines param conflicts based on UI ordering
+-->
+<xsl:template name="o:analyze">
+ <xsl:param name="class-deps" />
+ <xsl:param name="ui" />
+ <xsl:param name="sdmap" />
+ <xsl:param name="param-classes" />
+
+ <xsl:variable name="rater" select="." />
+
+ <!-- put fields in the order that they appear in the UI -->
+ <!-- TODO: duplicate check (error) -->
+ <xsl:variable name="ordered">
+ <o:ordered>
+ <xsl:for-each select="$ui//lvp:question">
+ <xsl:variable name="id" select="@id" />
+ <xsl:copy-of select="$sdmap/lvmc:map[ @from=$id ]" />
+ </xsl:for-each>
+ </o:ordered>
+ </xsl:variable>
+
+ <!-- the param-class list gives us the classes that are directly used; let's
+ get a list of all classes that are used by those classes as well, which
+ will simplify the queries to come -->
+ <xsl:message>[orderizer] recursively discovering param classes...</xsl:message>
+ <xsl:variable name="param-classes-expanded">
+ <xsl:apply-templates select="$param-classes" mode="o:gen-param-reflist">
+ <xsl:with-param name="class-deps" select="$class-deps" />
+ </xsl:apply-templates>
+ </xsl:variable>
+
+ <xsl:variable name="initial">
+ <o:analysis>
+ <xsl:for-each select="$ordered//lvmc:*">
+ <xsl:variable name="cur" select="." />
+ <xsl:variable name="pref" select="@to" />
+
+ <!-- progress indicator -->
+ <xsl:message>
+ <xsl:text>[orderizer] checking previous UI siblings: </xsl:text>
+ <xsl:value-of select="@to" />
+ </xsl:message>
+
+ <!-- preceding -->
+ <xsl:for-each select="
+ $param-classes/gc:param[
+ @ref=$cur/preceding-sibling::lvmc:map/@to
+ ]">
+
+ <!-- immediate conflicts (we check these separately rather than in the
+ xpath above, which would be cleaner, so that they can be
+ processed -->
+ <xsl:variable name="conflicts" select="
+ .//gc:class[
+ @ref=$param-classes-expanded/o:param-refs/o:param[
+ @ref=$pref
+ ]/o:class/@ref
+ ]
+ " />
+
+ <xsl:if test="$conflicts">
+ <o:conflict ref="{@ref}" relative-to="{$pref}">
+ <xsl:for-each select="$conflicts">
+ <!-- record the immediate problem -->
+ <o:class ref="{@ref}" yields="{@yields}" />
+ </xsl:for-each>
+ </o:conflict>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:for-each>
+ </o:analysis>
+ </xsl:variable>
+
+ <xsl:apply-templates select="$initial/o:analysis" mode="o:reduce-analysis" />
+</xsl:template>
+
+
+<xsl:template match="gc:param-classes" mode="o:gen-param-reflist" priority="5">
+ <xsl:param name="class-deps" />
+
+ <o:param-refs>
+ <xsl:apply-templates select="gc:param" mode="o:gen-param-reflist">
+ <xsl:with-param name="class-deps" select="$class-deps" />
+ </xsl:apply-templates>
+ </o:param-refs>
+</xsl:template>
+
+<xsl:template match="gc:param" mode="o:gen-param-reflist" priority="5">
+ <xsl:param name="class-deps" />
+
+ <o:param ref="{@ref}">
+ <xsl:apply-templates select="gc:class" mode="o:gen-param-reflist">
+ <xsl:with-param name="class-deps" select="$class-deps" />
+ </xsl:apply-templates>
+ </o:param>
+</xsl:template>
+
+<xsl:template match="gc:class" mode="o:gen-param-reflist" priority="5">
+ <xsl:param name="class-deps" />
+
+ <!-- well, this class is certainly used -->
+ <o:class ref="{@ref}" />
+
+ <xsl:variable name="ref" select="@ref" />
+
+ <!-- but how about things that use this class? -->
+ <xsl:apply-templates select="$class-deps/preproc:class[ @ref=$ref ]"
+ mode="o:gen-param-reflist">
+ <xsl:with-param name="class-deps" select="$class-deps" />
+ </xsl:apply-templates>
+</xsl:template>
+
+<xsl:template match="preproc:class" mode="o:gen-param-reflist" priority="5">
+ <xsl:param name="class-deps" />
+
+ <o:class ref="{@ref}" />
+
+ <xsl:apply-templates mode="o:gen-param-reflist">
+ <xsl:with-param name="class-deps" select="$class-deps" />
+ </xsl:apply-templates>
+</xsl:template>
+
+<xsl:template match="*" mode="o:gen-param-reflist" priority="1">
+ <!-- do nothing -->
+</xsl:template>
+
+
+<xsl:template match="preproc:class" mode="o:get-indirect-params" priority="5">
+ <xsl:apply-templates mode="o:get-indirect-params" />
+</xsl:template>
+
+<xsl:template match="preproc:class-dep" mode="o:get-indirect-params" priority="5">
+ <xsl:variable name="ref" select="@ref" />
+
+ <xsl:apply-templates
+ select="ancestor::preproc:class-deps/preproc:class[ @ref=$ref ]"
+ mode="o:get-indirect-params" />
+</xsl:template>
+
+<xsl:template match="preproc:rate-dep" mode="o:get-indirect-params" priority="5">
+ <xsl:variable name="ref" select="@ref" />
+
+ <xsl:apply-templates
+ select="//preproc:rate-deps/preproc:rate[ @ref=$ref ]"
+ mode="o:get-indirect-params" />
+</xsl:template>
+
+<xsl:template match="preproc:param" mode="o:get-indirect-params" priority="5">
+ <xsl:copy-of select="." />
+</xsl:template>
+
+<xsl:template match="*" mode="o:get-indirect-params" priority="1">
+ <!-- do nothing -->
+</xsl:template>
+
+
+<!--
+ Combines initial analysis results, merging common refs and de-duplicating
+ conflicts.
+-->
+<xsl:template match="o:analysis" mode="o:reduce-analysis">
+ <!-- start by combining the contents of all unique refs -->
+ <xsl:variable name="combined">
+ <xsl:for-each select="
+ o:conflict[
+ not( @ref=preceding-sibling::o:conflict/@ref )
+ ]
+ ">
+
+ <xsl:variable name="ref" select="@ref" />
+ <xsl:variable name="dups" select="
+ following-sibling::o:conflict[ @ref=$ref ]
+ " />
+
+ <o:conflict ref="{@ref}">
+ <!-- add relativity nodes -->
+ <xsl:for-each select="$dups|.">
+ <o:relative-to ref="{@relative-to}" />
+ </xsl:for-each>
+
+ <!-- add class deps -->
+ <o:reasons>
+ <xsl:copy-of select="($dups|.)/o:class" />
+ </o:reasons>
+ </o:conflict>
+ </xsl:for-each>
+ </xsl:variable>
+
+ <!-- now, remove duplicates resulting from the above operation -->
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+
+ <xsl:apply-templates select="$combined/o:conflict"
+ mode="o:reduce-analysis" />
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="o:conflict" mode="o:reduce-analysis">
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+
+ <!-- unique relative-to nodes -->
+ <xsl:copy-of select="
+ o:relative-to[
+ not( @ref=preceding-sibling::o:relative-to/@ref )
+ ]
+ " />
+
+ <xsl:apply-templates select="o:reasons" mode="o:reduce-analysis" />
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="o:reasons" mode="o:reduce-analysis">
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+
+ <!-- unique reasons -->
+ <xsl:copy-of select="
+ o:class[
+ not( @ref=preceding-sibling::o:class/@ref )
+ ]
+ " />
+ </xsl:copy>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/preproc/domain.xsl b/src/current/include/preproc/domain.xsl
new file mode 100644
index 0000000..6dc436d
--- /dev/null
+++ b/src/current/include/preproc/domain.xsl
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Compiles domains from typedefs
+
+ The ultime goal is to implement typedefs as macros and move to a generic
+ domain system that is much more powerful.
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:c="http://www.lovullo.com/calc">
+
+
+<!--
+ Base typedefs (internal) are declarations only
+
+ TODO: We still want a domain generated.
+-->
+<xsl:template match="lv:typedef[ lv:base-type ]"
+ mode="preproc:mkdomain" priority="9">
+</xsl:template>
+
+
+<!--
+ Union typedefs
+
+ This generates not only the contained typedefs, but also a denormalized
+ domain containing the union of all the subdomain elements. This is
+ intended to improve lookup performance and reduce algorithmic complexity.
+-->
+<xsl:template match="
+ lv:typedef[
+ lv:union
+ ]
+ "
+ mode="preproc:mkdomain" priority="5">
+
+ <!-- generate all contained domains -->
+ <xsl:variable name="subdomains">
+ <xsl:variable name="union-types" select="
+ lv:union/lv:typedef" />
+
+ <!-- if a union is empty, then somebody probably made an oopsie or wrote
+ a defective/deficient template -->
+ <xsl:if test="count( $union-types ) = 0">
+ <xsl:message>
+ <xsl:text>warning: union `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' has no subdomains; something is probably wrong</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:apply-templates mode="preproc:mkdomain"
+ select="$union-types" />
+ </xsl:variable>
+
+ <!-- provide a denormalized domain for performance and to reduce
+ algorithmic complexity-->
+ <xsl:call-template name="preproc:mkdomain-union">
+ <xsl:with-param name="name" select="@name" />
+ <xsl:with-param name="subdomains" select="$subdomains" />
+ </xsl:call-template>
+
+ <xsl:copy-of select="$subdomains" />
+</xsl:template>
+
+
+<!--
+ Enumerated typedefs
+-->
+<xsl:template match="
+ lv:typedef[
+ lv:enum
+ ]
+ "
+ mode="preproc:mkdomain" priority="5">
+
+ <lv:domain name="{@name}">
+ <xsl:variable name="items" select="lv:enum/lv:item" />
+
+ <!-- if a typedef is empty, then somebody probably made an oopsie or
+ wrote a defective/deficient template -->
+ <xsl:if test="count( $items ) = 0">
+ <xsl:message>
+ <xsl:text>warning: typedef `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' is empty; something is probably wrong</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:apply-templates mode="preproc:mkdomain"
+ select="$items" />
+ </lv:domain>
+</xsl:template>
+
+
+<!--
+ Prohibit mixing explicit and auto-generated values
+
+ For the time being at least.
+-->
+<xsl:template match="
+ lv:typedef[
+ lv:enum/lv:item[ @value ]
+ and lv:enum/lv:item[ not( @value ) ]
+ ]"
+ mode="preproc:mkdomain" priority="2">
+
+ <xsl:message terminate="yes">
+ <xsl:text>error: typedef `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' must not contain both @value and non-@value items</xsl:text>
+ </xsl:message>
+</xsl:template>
+
+
+<!--
+ Unsupported typedef
+
+ Well, we know that it is a typedef, but its format is unknown. This
+ wouldn't be surprising, since it is presently very limited.
+-->
+<xsl:template match="lv:typedef"
+ mode="preproc:mkdomain" priority="2">
+
+ <xsl:message terminate="yes">
+ <xsl:text>error: malformed typedef `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+</xsl:template>
+
+
+
+<!--
+ Generate a denormalized domain consisting of the union of its subdomains'
+ elements
+
+ As this is a union, duplicate elements will be removed; the user will not
+ be notified of this fact, as this allows domains to overlap in order to
+ interpret the same data in different manners.
+-->
+<xsl:template name="preproc:mkdomain-union">
+ <xsl:param name="name" />
+ <xsl:param name="subdomains" />
+
+ <xsl:variable name="union">
+ <preproc:elements>
+ <xsl:copy-of select="$subdomains/lv:domain/lv:element" />
+ </preproc:elements>
+ </xsl:variable>
+
+ <!-- remove duplicate values (yes, this will take the first description if
+ there are duplicates; whatever, for now) -->
+ <lv:domain name="{@name}">
+ <xsl:copy-of select="
+ $union/preproc:elements/lv:element[
+ not( @value = preceding-sibling::lv:element/@value )
+ ]" />
+ </lv:domain>
+</xsl:template>
+
+
+<!--
+ Enumerated items without values require calculation
+
+ Note the above validation that ensures that our value generation is
+ sufficient.
+-->
+<xsl:template match="lv:enum/lv:item[ not( @value ) ]"
+ mode="preproc:mkdomain" priority="5">
+
+ <xsl:variable name="augmented">
+ <xsl:copy>
+ <xsl:attribute name="value" select="position()" />
+ </xsl:copy>
+ </xsl:variable>
+
+ <!-- re-process using an augmented item with the value calculated -->
+ <xsl:apply-templates mode="preproc:mkdomain"
+ select="$augmented/lv:item" />
+</xsl:template>
+
+
+<!--
+ Convert typedef item into a domain element
+
+ This is a straightforward rename with sanity checking. Note that the
+ element may have an empty description.
+
+ We do not care about the name, since we use that to generate constants
+ elsewhere.
+-->
+<xsl:template match="lv:item"
+ mode="preproc:mkdomain" priority="4">
+
+ <!-- previous templates should have prevented this, but just in case -->
+ <xsl:if test="not( @value )">
+ <xsl:message terminate="yes">
+ <xsl:text>internal error: preproc:mkdomain on non-value item: </xsl:text>
+ <xsl:copy-of select="." />
+ </xsl:message>
+ </xsl:if>
+
+ <lv:element value="{@value}" desc="{@desc}" />
+</xsl:template>
+
+
+<!--
+ Unexpected node; terminate
+-->
+<xsl:template match="*"
+ mode="preproc:mkdomain" priority="1">
+
+ <xsl:message terminate="yes">
+ <xsl:text>internal error: unknown domain source: </xsl:text>
+ <xsl:copy-of select="." />
+ </xsl:message>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/include/preproc/eligclass.xsl b/src/current/include/preproc/eligclass.xsl
new file mode 100644
index 0000000..8e78c09
--- /dev/null
+++ b/src/current/include/preproc/eligclass.xsl
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Package eligibility class generation
+
+ Here, the term "eligibility" means whether the package is eligible to be used
+ in a result set basead on the values of its params within their respective
+ domains and other factors such as the results of terminating classifications
+ and the eligibility of imported packages.
+
+ The goal of the eligibility classification is to create a cascading failure in
+ the event of bad data.
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="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:c="http://www.lovullo.com/calc">
+
+
+
+<!--
+ Trigger eligibility class generation
+-->
+<xsl:template match="lv:package[ not( @preproc:elig-class-yields ) ]"
+ as="element( lv:package )"
+ priority="5"
+ mode="preproc:expand-elig-class">
+ <xsl:param name="orig-root" as="element( lv:package )" />
+
+ <xsl:variable name="elig-class" as="element( lv:classify )">
+ <xsl:apply-templates select="." mode="preproc:gen-elig-class">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:apply-templates>
+ </xsl:variable>
+
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:attribute name="preproc:elig-class"
+ select="$elig-class/@as" />
+
+ <xsl:attribute name="preproc:elig-class-yields"
+ select="$elig-class/@yields" />
+
+ <xsl:sequence select="$elig-class" />
+ <xsl:apply-templates mode="preproc:macros" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="lv:package" as="element( lv:package )"
+ priority="1"
+ mode="preproc:expand-elig-class">
+
+ <!-- already processed -->
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+
+<!--
+ Generate eligibility classification asserting all data integrity aspects of a
+ package
+
+ The eligibility classification will yield a scalar.
+-->
+<xsl:template match="lv:package" as="element( lv:classify )"
+ mode="preproc:gen-elig-class">
+ <xsl:param name="orig-root" as="element( lv:package )" />
+
+ <xsl:message>[preproc/eligclass] generating eligibility class</xsl:message>
+
+
+ <!-- class-ify name -->
+ <xsl:variable name="as" as="xs:string"
+ select="preproc:gen-elig-class-name( @name )" />
+ <xsl:variable name="yields" as="xs:string"
+ select="preproc:gen-elig-class-yields( @name )" />
+
+
+ <lv:classify as="{$as}" yields="{$yields}"
+ desc="{@name} package is eligible">
+
+ <!-- TODO: this should really be a compile-time value -->
+ <xsl:if test="@keep-elig-class = 'true'">
+ <xsl:attribute name="keep" select="'true'" />
+ </xsl:if>
+
+ <!-- each of our imported packages' elig classes must be truthful -->
+ <xsl:apply-templates mode="preproc:gen-elig-class-matches"
+ select="lv:import">
+
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:apply-templates>
+
+ <!-- param values must be within their domain -->
+ <!-- XXX: does not work when param is undefined due to no mapping
+ <xsl:apply-templates mode="preproc:gen-elig-param-class"
+ select="lv:param" />
+ -->
+
+ <!-- terminating classifications must not have matched -->
+ <xsl:apply-templates mode="preproc:gen-elig-term-class"
+ select="preproc:symtable/preproc:sym[ @type='class' ]" />
+ </lv:classify>
+</xsl:template>
+
+
+<!--
+ Generate eligibility classification name for package
+-->
+<xsl:function name="preproc:gen-elig-class-name"
+ as="xs:string">
+ <xsl:param name="name" />
+
+ <xsl:sequence select="
+ concat( '--elig-',
+ translate(
+ translate( $name, '.', '' ),
+ '/', '-'
+ )
+ )
+ " />
+</xsl:function>
+
+
+<!--
+ Generate eligibility result scalar name for package
+-->
+<xsl:function name="preproc:gen-elig-class-yields"
+ as="xs:string">
+ <xsl:param name="name" />
+
+ <xsl:sequence select="
+ concat(
+ 'isElig',
+ translate(
+ translate( $name, '.', '' ),
+ '/-', '' ) )" />
+</xsl:function>
+
+
+<!--
+ Generate matches on eligibility of imported packages
+
+ For each imported package, its eligibility classification must be true.
+-->
+<xsl:template match="lv:import[ @package ]"
+ as="element( lv:match )?"
+ priority="5"
+ mode="preproc:gen-elig-class-matches">
+ <xsl:param name="orig-root" as="element( lv:package )" />
+
+ <!-- FIXME: path may not yet be resolved due to preprocessing order -->
+ <xsl:variable name="pkg-path" as="xs:string">
+ <xsl:call-template name="__apply-relroot">
+ <xsl:with-param name="path" select="@package" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="document( concat( $pkg-path, '.xmlo' ),
+ $__entry-root )
+ /lv:package" />
+
+ <xsl:if test="not( $pkg )">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc/eligclass] error: could not load `</xsl:text>
+ <xsl:value-of select="$pkg-path" />
+ <xsl:text>' object file</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:variable name="chk" as="xs:string?"
+ select="$pkg/@preproc:elig-class-yields" />
+
+ <xsl:choose>
+ <xsl:when test="not( $chk ) or ( $chk = '' )">
+ <!-- TODO: make this an error once we make maps part of the
+ conventional build process -->
+ <xsl:message>
+ <xsl:text>[preproc/eligclass] internal: empty eligibility </xsl:text>
+ <xsl:text>class for `</xsl:text>
+ <xsl:value-of select="$pkg/@name" />
+ <xsl:text>'; skipping</xsl:text>
+ </xsl:message>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- use eligibility class as stated by the package -->
+ <lv:match on="{$chk}" value="TRUE" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="lv:import" priority="1"
+ mode="preproc:gen-elig-class-matches">
+
+ <!-- do nothing -->
+</xsl:template>
+
+
+<!--
+ Param values must be within their domain
+
+ This is a trivial operation.
+-->
+<xsl:template match="lv:param"
+ as="element( lv:any )"
+ mode="preproc:gen-elig-param-class" priority="5">
+
+ <lv:any>
+ <lv:match on="{@name}" anyOf="{@type}" />
+
+ <!-- TODO: defaults should always be within the domain! -->
+ <xsl:if test="@default">
+ <lv:match on="{@name}" anyOf="empty" />
+ </xsl:if>
+ </lv:any>
+</xsl:template>
+
+
+
+<!--
+ Terminating classification dependencies
+
+ All terminiating classifications defined in the package must yield false
+ for the package to be eligible.
+
+ N.B. This checks to ensure @extclass is not set; this prevents errors when
+ the eligibility classification attempts to pull in a terminating
+ classification marked as external to the classifier. There may or may not
+ be something we want to do about this in the future.
+-->
+<xsl:template match="preproc:sym[
+ not( @src )
+ and not( @pollute='true' )
+ and @type='class'
+ and @terminate='true'
+ and not( @extclass='true' )
+ ]"
+ as="element( lv:match )"
+ priority="5"
+ mode="preproc:gen-elig-term-class">
+
+ <lv:match on="{@yields}" value="FALSE" />
+</xsl:template>
+
+
+<xsl:template match="preproc:sym" priority="1"
+ mode="preproc:gen-elig-term-class">
+
+ <!-- do nothing -->
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/include/preproc/expand.xsl b/src/current/include/preproc/expand.xsl
new file mode 100644
index 0000000..57c6856
--- /dev/null
+++ b/src/current/include/preproc/expand.xsl
@@ -0,0 +1,691 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Handles node expansion
+
+ This process is responsible for expanding shorthand and various other data
+ into a consistent format for the compiler and other processes.
+-->
+
+<xsl:stylesheet
+ version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ 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:w="http://www.lovullo.com/rater/worksheet">
+
+
+<xsl:include href="domain.xsl" />
+
+
+<xsl:template match="lv:package[ not( @preproc:name ) ]"
+ mode="preproc:expand" priority="5"
+ as="element( lv:package )">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <!-- generate name from source package identifier -->
+ <xsl:attribute name="name" select="$__srcpkg" />
+
+ <!-- relative path to root src directory (for resolving absolute include
+ paths) -->
+ <xsl:attribute name="__rootpath" select="$__relroot" />
+
+ <!-- TODO: temporary; remove -->
+ <xsl:attribute name="preproc:name" select="$__srcpkg" />
+
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+
+<xsl:template match="*" mode="preproc:expand" priority="1">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!-- imports relative to project root -->
+<xsl:template match="lv:import[ starts-with( @package, '/' ) ]" mode="preproc:expand" priority="5">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <!-- resolve path into path relative to project root -->
+ <xsl:attribute name="package">
+ <xsl:call-template name="__apply-relroot">
+ <xsl:with-param name="path" select="@package" />
+ </xsl:call-template>
+ </xsl:attribute>
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ Domain data are extracted from typedefs
+
+ Eventually, the typedefs will be converted into templates and removed entirely.
+-->
+<xsl:template match="lv:typedef"
+ mode="preproc:expand" priority="5">
+
+ <xsl:apply-templates select="." mode="preproc:mkdomain" />
+
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ Infer primitive type if not provided.
+-->
+<xsl:template mode="preproc:expand"
+ match="c:const[ not( @type ) ]
+ |lv:const[ not( @type ) ]"
+ priority="5">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:attribute name="type"
+ select="if ( substring-before( @value, '.' ) ) then
+ 'float'
+ else
+ 'integer'" />
+
+ <xsl:sequence select="*" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ Give let's a name so that they may be easily referenced uniquely
+-->
+<xsl:template match="c:let[ not( @name ) ]" mode="preproc:expand" priority="5">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:attribute name="name" select="generate-id(.)" />
+
+ <xsl:apply-templates select="*" mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ Default label of c:let value expressions to description if no label is provided
+
+ This is useful for breakdown display.
+
+ TODO: play well with others; if we change the priority back to 5, we introduce
+ match ambiguities
+-->
+<xsl:template match="c:let/c:values/c:value/c:*[1][ not( @label ) ]" mode="preproc:expand" priority="4">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <!-- default the label to the description of the parent c:value -->
+ <xsl:attribute name="label" select="../@desc" />
+
+ <xsl:apply-templates select="*" mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ c:when with no children is shorthand for > 0
+
+ Note: we check for any children because we may have things like
+ template applications that we do not want wiped out.
+-->
+<xsl:template match="c:when[ not( * ) ]" mode="preproc:expand" priority="5">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <c:gt>
+ <c:value-of name="FALSE" />
+ </c:gt>
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ c:when with multiple children
+-->
+<xsl:template match="c:when[ count( c:* ) gt 1 ]" mode="preproc:expand" priority="5">
+ <xsl:variable name="when" select="." />
+
+ <!-- expand into adjacent c:when's -->
+ <xsl:for-each select="c:*">
+ <c:when>
+ <xsl:sequence select="$when/@*" />
+ <xsl:apply-templates select="." mode="preproc:expand" />
+ </c:when>
+ </xsl:for-each>
+</xsl:template>
+
+
+<!--
+ Recursion shorthand
+
+ This exists simply because function application is so verbose and, when
+ recursing, generally only a small fraction of the arguments actually change.
+-->
+<xsl:template match="c:recurse" mode="preproc:expand" priority="5">
+ <xsl:variable name="self" select="." />
+ <xsl:variable name="fname" select="ancestor::lv:function/@name" />
+ <xsl:variable name="overrides" select="./c:arg" />
+
+ <c:apply name="{$fname}">
+ <!-- every non-@name attribute should be converted into an argument -->
+ <xsl:call-template name="preproc:arg-short-expand" />
+
+ <!-- include all non-overridden args -->
+ <xsl:for-each select="
+ ancestor::lv:function/lv:param[
+ not(
+ @name=$overrides/@name
+ or @name=$self/@*/local-name()
+ )
+ ]
+ ">
+
+ <!-- copy the arg value -->
+ <c:arg name="{@name}">
+ <c:value-of name="{@name}" />
+ </c:arg>
+ </xsl:for-each>
+
+ <!-- copy in the overrides -->
+ <xsl:apply-templates select="$overrides" mode="preproc:expand" />
+ </c:apply>
+</xsl:template>
+
+
+<!-- metadata constants have different semantics -->
+<!-- TODO: maybe ignore single-quoted? -->
+<xsl:template mode="preproc:expand" priority="6"
+ match="lv:meta/lv:prop/lv:const">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<!-- constants that contain 'e' (scientific notation) should be expanded; allows
+ for avoiding constants with many zeroes, which is hard to read -->
+<xsl:template mode="preproc:expand" priority="5"
+ match="c:const[ substring-before( @value, 'e' ) ]
+ |lv:const[ substring-before( @value, 'e' ) ]">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:attribute name="value">
+ <xsl:call-template name="preproc:expand-e">
+ <xsl:with-param name="number" select="@value" />
+ </xsl:call-template>
+ </xsl:attribute>
+
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template mode="preproc:expand" priority="5"
+ match="c:const[ substring-before( @value, 'm' ) ]
+ |lv:const[ substring-before( @value, 'm' ) ]">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:attribute name="value">
+ <xsl:call-template name="preproc:expand-e">
+ <xsl:with-param name="number"
+ select="concat( substring-before( @value, 'm' ), 'e6' )" />
+ </xsl:call-template>
+ </xsl:attribute>
+
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template mode="preproc:expand" priority="5"
+ match="c:const[ substring-before( @value, 'k' ) ]
+ |lv:const[ substring-before( @value, 'k' ) ]">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:attribute name="value">
+ <xsl:call-template name="preproc:expand-e">
+ <xsl:with-param name="number"
+ select="concat( substring-before( @value, 'k' ), 'e3' )" />
+ </xsl:call-template>
+ </xsl:attribute>
+
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!-- expand scientific notation -->
+<!-- XXX: negatives not currently supported -->
+<xsl:template name="preproc:expand-e">
+ <xsl:param name="number" />
+ <xsl:param name="whole" select="substring-before( $number, '.' )" />
+ <xsl:param name="dec" select="substring-before( substring-after( $number, '.' ), 'e' )" />
+ <xsl:param name="count" as="xs:double"
+ select="number( substring-after( $number, 'e' ) )" />
+
+ <!-- output the whole number portion -->
+ <xsl:choose>
+ <xsl:when test="$whole and not( $whole = '' )">
+ <xsl:value-of select="$whole" />
+ </xsl:when>
+
+ <!-- if no decimal was provided, then use the entire number before 'e' -->
+ <xsl:when test="$number and not( $number = '' ) and ( $whole = '' )">
+ <xsl:value-of select="substring-before( $number, 'e' )" />
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:choose>
+ <xsl:when test="$count > 0">
+ <xsl:choose>
+ <!-- if we have a decimal, then use the first digit (as if we moved one
+ place to the right) -->
+ <xsl:when test="$dec and not( $dec = '' )">
+ <xsl:value-of select="substring( $dec, 1, 1 )" />
+ </xsl:when>
+
+ <!-- no decimal portion remaining; fill with 0 -->
+ <xsl:otherwise>
+ <xsl:text>0</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- recursively expand -->
+ <xsl:call-template name="preproc:expand-e">
+ <!-- already processed the whole -->
+ <xsl:with-param name="whole" select="''" />
+
+ <xsl:with-param name="dec">
+ <!-- move to the right one decimal place; otherwise, no decimal -->
+ <xsl:if test="$dec">
+ <xsl:value-of select="substring( $dec, 2 )" />
+ </xsl:if>
+ </xsl:with-param>
+
+ <xsl:with-param name="count" select="$count - 1" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- output the remaining decimal, if any -->
+ <xsl:otherwise>
+ <xsl:if test="$dec and not( $dec = '' )">
+ <xsl:text>.</xsl:text>
+ <xsl:value-of select="$dec" />
+ </xsl:if>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Optimize away c:cases if they contain only c:otherwise
+
+ This is useful primarily for templates that may create a case statement for
+ conditional operations (using lv:if/lv:unless) and ensures that there is no
+ penalty for doing so if none of the template conditions result in a c:case.
+
+ Note that we should *not* perform these optimizations if there are templates
+ awaiting application or any other lv:* nodes that have not been expanded.
+-->
+<xsl:template mode="preproc:expand" priority="5" match="
+ c:cases[
+ not( lv:* or t:* )
+ and c:otherwise[
+ not( preceding-sibling::c:* or following-sibling::c:* )
+ ]
+ ]
+ ">
+
+ <!-- just replace with the content of the otherwise block (do not explicitly
+ process c:*, since there may be templates) -->
+ <xsl:apply-templates select="c:otherwise/*" mode="preproc:expand" />
+</xsl:template>
+
+
+<!--
+ Optimize away c:sum/c:product blocks that contain one or zero elements, so
+ long as they do not contain a generator (since that would remove a ref) or @of
+ (since that will actually loop through multiple).
+
+ Note that we should *not* perform these optimizations if there are templates
+ awaiting application or any other lv:* nodes that have not been expanded.
+-->
+<xsl:template match="c:sum[ lv:* or t:* ]|c:product[ lv:* or t:* ]" mode="preproc:expand" priority="7">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+<xsl:template match="c:sum[ not( @of or @generates ) and count( c:* ) &lt; 2 ]" mode="preproc:expand" priority="5">
+ <xsl:apply-templates select="c:*" mode="preproc:expand" />
+</xsl:template>
+<xsl:template match="c:product[ not( @of or @generates ) and count( c:* ) &lt; 2 ]" mode="preproc:expand" priority="5">
+ <xsl:apply-templates select="c:*" mode="preproc:expand" />
+</xsl:template>
+
+
+<!-- TODO: We could add shorthand for indexes too, e.g. name[i] or name[0] -->
+<xsl:template match="
+ c:apply[
+ @*[
+ not(
+ local-name() = 'name'
+ or local-name() = 'label'
+ )
+ ]
+ ]
+ "
+ mode="preproc:expand" priority="5">
+
+ <xsl:copy>
+ <!-- keep the name attribute, which specifies what function to apply -->
+ <xsl:sequence select="@name, @label" />
+
+ <!-- every other attribute should be converted into an argument -->
+ <xsl:call-template name="preproc:arg-short-expand" />
+
+ <xsl:apply-templates select="c:arg" mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template name="preproc:arg-short-expand">
+ <xsl:for-each select="@*[
+ not(
+ local-name() = 'name'
+ or local-name() = 'label'
+ )
+ ]">
+
+ <c:arg name="{local-name()}">
+ <c:value-of name="{.}" />
+ </c:arg>
+ </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template match="lv:rate[ lv:class ]|lv:function[ lv:class ]|lv:yield[ lv:class ]"
+ mode="preproc:expand" priority="9">
+ <!-- already processed -->
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+<!--
+ Add lv:class nodes containing the values of each individual class
+
+ This eliminates the need to tokenize later and drastically simplifies xpath
+ queries.
+-->
+<xsl:template match="lv:rate|lv:function|lv:yield" mode="preproc:expand" priority="5">
+ <xsl:variable name="self" select="." />
+
+ <xsl:variable name="classes" select="tokenize( @class, ' ' )" />
+ <xsl:variable name="no-classes" select="tokenize( @no, ' ' )" />
+
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <!-- convert classes into nodes to make life easier down the road (if any) -->
+ <xsl:for-each select="$classes">
+ <xsl:if test=".">
+ <lv:class ref="{.}" no="false" />
+ </xsl:if>
+ </xsl:for-each>
+
+ <xsl:for-each select="$no-classes">
+ <xsl:if test=".">
+ <lv:class ref="{.}" no="true" />
+ </xsl:if>
+ </xsl:for-each>
+
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+
+<!--
+ To make life a bit easier, calculate the set type of a classification @yields
+ and add it to the node as a @set attribute
+-->
+<xsl:template match="lv:classify" mode="preproc:expand" priority="5">
+ <xsl:variable name="self" select="." />
+
+ <xsl:copy>
+ <!-- we just want to add an attribute that allows easy referencing of this
+ @yields set type, which will be a matrix if any matches match on a
+ matrix of values, otherwise it will be a vector -->
+ <xsl:attribute name="set">
+ <xsl:variable name="params"
+ select="root(.)//lv:param[ @name=$self//lv:match/@on ]" />
+
+ <xsl:choose>
+ <!-- XXX: This does not work properly with classes depending on other
+ classes -->
+ <xsl:when test="$params/@set = 'matrix'">
+ <xsl:text>matrix</xsl:text>
+ </xsl:when>
+
+ <!-- XXX: remove this when the above is fixed...note also that we have
+ to check for lv:join since it hasn't necessarily been preprocessed
+ yet...what a mess. Also note that, since templates and other things
+ may not have been expanded, we also fail this test if the
+ classification does not match on either a param or another
+ classification (since then things will get more complicated)-->
+ <xsl:when test="
+ not(
+ $self//lv:match
+ or $self//lv:join
+ )
+ or (
+ not( $params/@set )
+ and not(
+ .//lv:match[ @on=root(.)/lv:classify/@yields ]
+ )
+ and not( .//lv:join )
+ and (
+ $params
+ or .//lv:match[ @on=root(.)/lv:classify ]
+ )
+ )
+ ">
+ <!-- output nothing; it's just a scalar -->
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>vector</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+
+ <!-- if there is no @yields attribute, then generate one -->
+ <xsl:if test="not( @yields )">
+ <xsl:attribute name="yields">
+ <xsl:text>__is</xsl:text>
+ <!-- certain characters are not valid for @yields -->
+ <xsl:value-of select="translate( @as, '-', '' )" />
+ </xsl:attribute>
+ </xsl:if>
+
+ <xsl:sequence select="@*" />
+
+ <!-- force @keep on @terminate -->
+ <xsl:if test="@terminate='true'">
+ <xsl:attribute name="keep" select="'true'" />
+ </xsl:if>
+
+ <!-- copy everything else -->
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!-- default lv:match/@on short-hand to assert on a value of TRUE -->
+<xsl:template match="lv:match[ not( @value
+ or @anyOf
+ or @pattern
+ or * ) ]"
+ mode="preproc:expand" priority="7">
+
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+ <xsl:attribute name="value"
+ select="'TRUE'" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template mode="preproc:expand"
+ match="lv:join[ @all='true' ]"
+ priority="8">
+ <xsl:call-template name="preproc:mk-class-join-contents" />
+</xsl:template>
+
+
+<xsl:template mode="preproc:expand"
+ match="lv:join"
+ priority="7">
+ <lv:any>
+ <xsl:call-template name="preproc:mk-class-join-contents" />
+ </lv:any>
+</xsl:template>
+
+
+<xsl:template name="preproc:mk-class-join-contents">
+ <xsl:variable name="prefix" select="@prefix" />
+
+ <!-- TODO: remove lv:template nodes in a pass before this so that this
+ check is not necessary -->
+ <xsl:for-each select="root(.)/lv:classify[
+ starts-with( @as, $prefix )
+ and not( ancestor::lv:template )
+ ]">
+ <lv:match value="TRUE">
+ <xsl:attribute name="on">
+ <xsl:choose>
+ <xsl:when test="@yields">
+ <xsl:value-of select="@yields" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>__is</xsl:text>
+ <xsl:value-of select="@as" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ </lv:match>
+ </xsl:for-each>
+</xsl:template>
+
+
+<!-- enums have implicit values (as they are, well, enumerated; @value overrides) -->
+<!-- TODO: should @value set the next implicit index? -->
+<xsl:template match="lv:item[ not( @value ) ]" mode="preproc:expand" priority="5">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:attribute name="value" select="count( preceding-sibling::* )" />
+ <xsl:apply-templates mode="preproc:expand" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="w:display[ @prefix ]" mode="preproc:expand" priority="5">
+ <xsl:variable name="prefix" select="@prefix" />
+ <xsl:variable name="children" select="w:*" />
+
+ <xsl:for-each select="root(.)//lv:rate[ starts-with( @yields, $prefix ) ]">
+ <w:display name="{@yields}">
+ <xsl:sequence select="$children" />
+ </w:display>
+ </xsl:for-each>
+</xsl:template>
+
+
+<!-- remove templates that have been copied from an external source for
+ processing -->
+<xsl:template match="lv:template[
+ @name=root()
+ /preproc:symtable/preproc:sym[ @src ]/@name ]"
+ mode="preproc:expand" priority="5">
+</xsl:template>
+
+<!-- IMPORTANT: do not process unexpanded templates -->
+<xsl:template match="lv:template" mode="preproc:expand" priority="4">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<xsl:template match="preproc:symtable" mode="preproc:expand" priority="5">
+ <!-- ignore -->
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<xsl:template match="lv:__external-data" mode="preproc:expand" priority="5">
+ <!-- intended for use by code generators; data is not retained in object file
+ unless some other process overrides this template -->
+</xsl:template>
+
+</xsl:stylesheet>
+
+<!--
+Footnotes
+
+What!? We need footnotes for this project!?
+
+[0] This was a complicated issue to begin dealing with due to the information we
+ need and the information that is available. In particular, at this point, we
+ would like to exclude any non-external objects from appearing in the
+ rate-only output, but we cannot do that, since the preprocessor has not yet
+ reached that block! We cannot reorder, because the class order block depends
+ on the rate block!
+
+ (See the commit that introduced this footnote: In the past, the template at
+ this position passed an `external-only' flag to the template.)
+
+ Originally, the method was to ``guess'' based on the how the system was
+ currently being used (dangerous, but was meant to be temporary...though we
+ know how that goes...): implicit externals were ignored, meaning it may not
+ function as we would like. Now, this could be resolved in the short-term by
+ actually addinging explicit external attributes to everything that needed it
+ so that this code would run smoothly. Worked great! Well, so I had thought.
+
+ Then it came down to a certain classification that used the `frame'
+ classification. This classification was only used by an external
+ classification in scottsdale, but other companies used it for rating, so the
+ @external classifier was inappropriate. Of course, the system could easily
+ figure out if it was to be marked as external or not (it already does), but
+ we do not yet have access to that information. Therefore, what ended up
+ happening, was the frame classification was excluded from the classifier,
+ excluded from one of the iterations that this footnote references (because
+ it was not explicitly external) and included elsewhere where we didn't care
+ if it was external or not. When the dependency tree was flattened to
+ determine compilation order, the classification that uses `frame' was
+ compiled *before* the `frame' classification itself, due to that exclusion!
+ Since `frame' was excluded from the classifier, therefore, it was always
+ false! That is the situation I was trying to avoid with the explicit
+ @external attributes, but here, that solution would not work.
+
+ Therefore, checking for external-only here will not work; we must output
+ everything and work on post-processing the data once everything is said and
+ done, to remove the duplicates that are also present in the classifier.
+-->
diff --git a/src/current/include/preproc/macros.xsl b/src/current/include/preproc/macros.xsl
new file mode 100644
index 0000000..4c4f8e6
--- /dev/null
+++ b/src/current/include/preproc/macros.xsl
@@ -0,0 +1,456 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Handles macro preprocessing
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="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">
+
+
+<xsl:include href="template.xsl" />
+<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.
+-->
+<xsl:template match="*" mode="preproc:macropass" priority="1"
+ as="node()+">
+ <xsl:variable name="result" as="node()+">
+ <xsl:apply-templates select="." mode="preproc:macros" />
+ </xsl:variable>
+
+ <xsl:variable name="nodeset" select="$result" />
+
+ <xsl:variable name="repass"
+ select="$nodeset//preproc:repass" />
+
+ <!-- halt if we are in error -->
+ <xsl:for-each select="$nodeset//preproc:error">
+ <xsl:message terminate="yes">
+ <xsl:text>!!! [preproc] error: </xsl:text>
+ <xsl:value-of select="." />
+ </xsl:message>
+ </xsl:for-each>
+
+ <xsl:choose>
+ <!-- if it was indicated that we must do so, recurse -->
+ <xsl:when test="$repass and not( $repass[ @need-sym ] )">
+
+ <!-- record the repass to keep a count -->
+ <!-- TODO: reintroduce
+ <preproc:repass-record />
+ -->
+
+ <xsl:message>[preproc] *REPASS*</xsl:message>
+
+ <!-- perform the repass -->
+ <xsl:apply-templates select="$nodeset" mode="preproc:macropass">
+ <xsl:with-param name="clear-tpl-step"
+ tunnel="yes"
+ select="false()" />
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <!-- no more passes needed; macro expansion complete -->
+ <xsl:otherwise>
+ <xsl:sequence select="$nodeset" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="*" mode="preproc:macros" priority="1">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:apply-templates mode="preproc:macros" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ Remove repass nodes left over from the previous pass
+
+ Otherwise, we would recurse indefinately.
+-->
+<xsl:template match="preproc:repass" mode="preproc:macros" priority="5">
+ <!-- remove; no longer needed -->
+</xsl:template>
+
+
+<xsl:template match="preproc:tpl-step" mode="preproc:macros" priority="5">
+ <xsl:param name="clear-tpl-step"
+ tunnel="yes"
+ select="true()" />
+
+ <xsl:choose>
+ <xsl:when test="$clear-tpl-step">
+ <!-- strip -->
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:copy-of select="." />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ lv:rater is just a special type of package
+-->
+<xsl:template match="lv:rater" mode="preproc:macros" priority="9">
+ <lv:package program="true">
+ <xsl:sequence select="@*, *" />
+ </lv:package>
+
+ <preproc:repass src="lv:rater" />
+</xsl: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.
+-->
+<xsl:template match="lv:typedef"
+ mode="preproc:macros" priority="2">
+
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<!--
+ Expand values beginning with `#' into constants
+
+ It is a nuisance to have separate params (e.g. templates) for constants
+ and values.
+-->
+<xsl:template mode="preproc:macros"
+ match="c:value-of[ starts-with( @name, '#' ) ]"
+ priority="7">
+ <c:const value="{substring-after( @name, '#' )}"
+ type="float"
+ desc="Generated short-hand constant" />
+</xsl:template>
+
+
+<!--
+ Expand index values beginning with `#' into constants
+
+ It is a nuisance to have separate params (e.g. templates) for constants
+ and values.
+-->
+<xsl:template mode="preproc:macros"
+ match="c:value-of[ starts-with( @index, '#' ) ]"
+ priority="7">
+ <xsl:copy>
+ <xsl:copy-of select="@*[ not( name() = 'index' ) ]" />
+
+ <c:index>
+ <c:const value="{substring-after( @index, '#' )}"
+ type="float"
+ desc="Generated short-hand constant" />
+ </c:index>
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ It does not make sense to try to take an index of a scalar
+-->
+<xsl:template mode="preproc:macros"
+ match="c:value-of[
+ @index
+ and starts-with( @name, '#' ) ]"
+ priority="9">
+ <preproc:error>
+ <xsl:text>Cannot take index of scalar value: </xsl:text>
+ <xsl:value-of select="@name" />
+ </preproc:error>
+</xsl:template>
+
+
+<!--
+ Classifications containing only an lv:any child node can be converted into
+ existential classifications
+-->
+<xsl:template match="lv:classify[ lv:any and count(*) = 1 ]" mode="preproc:macros" priority="8">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:attribute name="any" select="'true'" />
+
+ <xsl:sequence select="lv:any/*" />
+ </xsl:copy>
+
+ <preproc:repass src="lv:classify any" />
+</xsl:template>
+
+
+<xsl:template match="lv:classify[ .//lv:any|.//lv:all ]" mode="preproc:macros" priority="6">
+ <xsl:variable name="result">
+ <xsl:apply-templates select="." mode="preproc:class-groupgen" />
+ </xsl:variable>
+
+ <xsl:apply-templates select="$result/lv:classify" mode="preproc:class-extract" />
+
+ <preproc:repass src="lv:classify any|all" />
+</xsl:template>
+
+
+<xsl:template match="lv:classify" mode="preproc:class-groupgen" priority="5">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:apply-templates mode="preproc:class-groupgen" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template mode="preproc:class-groupgen" priority="9"
+ match="lv:any[ not( element() ) ]
+ |lv:all[ not( element() ) ]">
+ <!-- useless; remove -->
+</xsl:template>
+
+
+<xsl: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 -->
+ <xsl:variable name="id" select="concat( $__rseed, generate-id(.) )" />
+
+ <xsl:variable name="parent-name" select="ancestor::lv:classify/@as" />
+ <xsl:variable name="yields" select="concat( 'is', $id )" />
+
+ <xsl:variable name="external" as="xs:string?"
+ select="ancestor::lv:classify/@external" />
+
+ <!-- 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}"
+ external="{$external}"
+ desc="(generated from predicate group of {$parent-name}">
+ <xsl:if test="local-name() = 'any'">
+ <xsl:attribute name="any" select="'true'" />
+ </xsl:if>
+
+ <xsl:apply-templates mode="preproc:class-groupgen" />
+ </lv:classify>
+
+ <!-- this will remain in its place -->
+ <lv:match on="{$yields}" value="TRUE" preproc:generated="true" />
+</xsl:template>
+
+
+<!-- retain everything else -->
+<xsl:template match="*" mode="preproc:class-groupgen" priority="1">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<xsl:template match="lv:classify" mode="preproc:class-extract" priority="5">
+ <xsl:apply-templates select="lv:classify" mode="preproc:class-extract" />
+
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:apply-templates mode="preproc:class-filter" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="*" mode="preproc:class-extract" priority="1">
+ <!-- ignore non-class -->
+</xsl:template>
+
+
+<xsl:template match="lv:classify" mode="preproc:class-filter" priority="5">
+ <!-- remove -->
+</xsl:template>
+
+
+<xsl:template match="*" mode="preproc:class-filter" priority="1">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<!--
+ Sections exist purely for organization and documentation. Move all
+ nodes out of it, so that we do not complicate parsing.
+-->
+<xsl:template mode="preproc:macros" priority="2"
+ match="lv:section">
+ <xsl:apply-templates select="*" mode="preproc:macros" />
+</xsl:template>
+
+
+<!--
+ lv:yield is simply another rate block with a special name that is recognized
+ by the linker
+-->
+<xsl:template match="lv:yield" mode="preproc:macros" priority="5">
+ <lv:rate yields="___yield" local="true">
+ <xsl:apply-templates mode="preproc:macros" />
+ </lv:rate>
+</xsl:template>
+
+
+<!-- this situation may occur both manually and from lv:rate-each-template -->
+<xsl:template match="lv:rate-each[ lv:apply-template ]" mode="preproc:macros" priority="9">
+ <xsl:variable name="apply">
+ <preproc:apply>
+ <xsl:apply-templates select="lv:apply-template" mode="preproc:macros" />
+ </preproc:apply>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- did the template apply? (note that we only check for a single one,
+ since that's all that we should have) -->
+ <xsl:when test="$apply/preproc:apply/lv:apply-template">
+ <xsl:sequence select="." />
+
+ <xsl:message>
+ <xsl:text>[preproc] waiting to expand rate-each </xsl:text>
+ <xsl:value-of select="@yields" />
+ <xsl:text> (immediate template(s) need expansion)...</xsl:text>
+ </xsl:message>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- it applied! -->
+ <xsl:copy>
+ <xsl:sequence select="@*, *[ not( local-name()='apply-template' ) ]" />
+ <xsl:sequence select="$apply/preproc:apply/*" />
+ </xsl:copy>
+
+ <!-- we'll process this block next time around -->
+ <preproc:repass src="lv:rate-each lv:apply-template" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Convenience macro that expands to a lv:rate block summing over the magic
+ _CMATCH_ set with the product of its value
+
+ The intent here is to reduce highly repetitive code.
+-->
+<xsl:template match="lv:rate-each" mode="preproc:macros" priority="5">
+ <!-- TODO: debug flag
+ <xsl:message>
+ <xsl:text>[preproc] expanding rate-each </xsl:text>
+ <xsl:value-of select="@yields" />
+ <xsl:text>...</xsl:text>
+ </xsl:message>
+ -->
+
+ <lv:rate>
+ <xsl:sequence select="@*[
+ not( local-name() = 'index' )
+ and not( local-name() = 'generates' )
+ ]" />
+
+ <xsl:if test="not( @yields )">
+ <!-- if @generates is not supplied either, then we cannot continue -->
+ <xsl:choose>
+ <xsl:when test="not( @generates )">
+ <!-- TODO: some means of identifying this...the error isn't terribly
+ helpful... :x -->
+ <preproc:error>
+ <xsl:text>rate-each must provide either @yields or @generates</xsl:text>
+ </preproc:error>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:attribute name="yields"
+ select="concat( '_', @generates )" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+ <xsl:sequence select="./lv:class" />
+
+ <c:sum of="_CMATCH_" index="{@index}" sym="{@gensym}">
+ <!-- copy @generates, if it exists (has the benefit of copying nothing
+ if it does not exist) -->
+ <xsl:sequence select="@generates" />
+
+ <xsl:attribute name="desc">
+ <xsl:text>Set of individual </xsl:text>
+ <xsl:value-of select="@yields" />
+ <xsl:text> premiums</xsl:text>
+ </xsl:attribute>
+
+ <c:product>
+ <c:value-of name="_CMATCH_" index="{@index}">
+ <xsl:attribute name="label">
+ <xsl:text>Zero if not </xsl:text>
+ <xsl:value-of select="@class" />
+ <xsl:text>, otherwise one</xsl:text>
+ </xsl:attribute>
+ </c:value-of>
+
+ <xsl:apply-templates
+ select="*[
+ not(
+ local-name() = 'class'
+ )
+ ]"
+ mode="preproc:macros" />
+ </c:product>
+ </c:sum>
+ </lv:rate>
+</xsl:template>
+
+
+<!--
+ Generates a classifier for each boolean param
+
+ This is for convenience; a boolean can esssentially be considered its own
+ classifier, so let's generate one to cut down on the amount of code.
+
+ Technically not a macro, but needs to be done before preproc:expand.
+
+ XXX: Get rid of me! Now unused!
+-->
+<xsl:template match="lv:params[ not( @preproc:processed ) ]"
+ mode="preproc:macros" priority="5">
+
+ <xsl:copy>
+ <xsl:attribute name="preproc:processed" select="'true'" />
+ <xsl:sequence select="@*" />
+
+ <xsl:apply-templates mode="preproc:macros" />
+ </xsl:copy>
+
+ <xsl:for-each select="lv:param[ @type='boolean' ]">
+ <xsl:variable name="as" select="translate( @name, '_', '-' )" />
+ <xsl:variable name="genas" select="concat( 'is-', $as )" />
+
+ <!-- ensure that this name does not already exist -->
+ <xsl:if test="not( /lv:*/lv:classify[ @as=$genas ] )">
+ <!-- TODO: We're flagging as @keep for now due to gclass needs, but this
+ should be removed later -->
+ <lv:classify as="{$genas}" desc="{@desc}" keep="true">
+ <lv:match on="{@name}" value="TRUE" />
+ </lv:classify>
+ </xsl:if>
+ </xsl:for-each>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/preproc/package.xsl b/src/current/include/preproc/package.xsl
new file mode 100644
index 0000000..48536af
--- /dev/null
+++ b/src/current/include/preproc/package.xsl
@@ -0,0 +1,817 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Preprocesses package XML
+
+ This will preprocess a package XML suitable for compilation into an object
+ file.
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ 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:w="http://www.lovullo.com/rater/worksheet"
+ xmlns:lvv="http://www.lovullo.com/rater/validate"
+ xmlns:ext="http://www.lovullo.com/ext"
+ xmlns:util="http://www.lovullo.com/util">
+
+
+<xsl:include href="../dslc-base.xsl" />
+
+<!-- phases -->
+<xsl:include href="macros.xsl" />
+<xsl:include href="expand.xsl" />
+<xsl:include href="symtable.xsl" />
+
+<xsl:include href="../../compiler/fragments.xsl" />
+
+
+<!-- begin preprocessing from an arbitrary node -->
+<xsl:template name="preproc:pkg-compile" as="element( lv:package )"
+ match="*" mode="preproc:compile" priority="1">
+ <xsl:param name="orig-root" as="element()"
+ select="root(.)/lv:package" />
+
+ <xsl:param name="stopshort" />
+
+ <!-- should be provided externally -->
+ <xsl:if test="not( $__rseed ) or ( $__rseed = '' )">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc] error: missing random seed `__rseed'</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:message>
+ <xsl:text>[preproc] *beginning macro expansion...</xsl:text>
+ </xsl:message>
+
+ <!-- these can contain repass nodes, thus the element()+ -->
+
+ <!-- macro expansion -->
+ <xsl:variable name="stage1" as="element()+">
+ <xsl:apply-templates select="." mode="preproc:macropass" />
+
+ <xsl:message>
+ <xsl:text>[preproc] *macro pass complete; expanding...</xsl:text>
+ </xsl:message>
+ </xsl:variable>
+
+ <!-- expand shorthands, etc -->
+ <xsl:variable name="stage2" as="element()+">
+ <xsl:apply-templates select="$stage1"
+ mode="preproc:expand" />
+
+ <xsl:message>
+ <xsl:text>[preproc] *expansion complete; generating symbol table...</xsl:text>
+ </xsl:message>
+ </xsl:variable>
+
+ <xsl:variable name="stage3" as="element()+">
+ <xsl:apply-templates select="$stage2"
+ mode="preproc:sym-discover">
+
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:apply-templates>
+
+ <xsl:message>
+ <xsl:text>[preproc] *symbol table generated; checking for </xsl:text>
+ <xsl:text>unprocessed templates...</xsl:text>
+ </xsl:message>
+ </xsl:variable>
+
+
+ <!-- TODO: resolve this mess -->
+ <xsl:variable name="stage3-pkg" as="element( lv:package )"
+ select="$stage3/preproc:symtable/parent::*" />
+
+ <!-- TODO: move me somewhere more appropriate and create an error
+ system that is _guaranteed_ to catch everything -->
+ <xsl:for-each select="$stage3-pkg/preproc:symtable/preproc:error">
+ <xsl:message terminate="yes">
+ <xsl:text>!!! [preproc] error: </xsl:text>
+ <xsl:value-of select="." />
+ </xsl:message>
+ </xsl:for-each>
+
+
+ <!-- determine if we should finish or simply return for further processing -->
+ <xsl:choose>
+ <xsl:when test="not( $stopshort )">
+ <!-- template expansions may have been deferred until their symbols were made
+ available -->
+ <xsl:variable name="final" as="element( lv:package )">
+ <xsl:call-template name="preproc:tpl-sym-recurse">
+ <xsl:with-param name="package" select="$stage3-pkg" />
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="extern-chk" as="element( preproc:error )*"
+ select="preproc:final-extern-check(
+ $final/preproc:symtable )" />
+
+
+ <xsl:if test="$extern-chk">
+ <xsl:for-each select="$extern-chk">
+ <xsl:message>
+ <xsl:text>!!! [preproc] error: </xsl:text>
+ <xsl:value-of select="." />
+ </xsl:message>
+ </xsl:for-each>
+
+ <xsl:message>~~~~[begin document dump]~~~~</xsl:message>
+ <xsl:message select="$final" />
+ <xsl:message>~~~~[end document dump]~~~~</xsl:message>
+
+ <xsl:message select="'Aborting due to unresolved externs'" />
+
+ <xsl:message terminate="yes"
+ select="'Document dumped.'" />
+ </xsl:if>
+
+ <!-- ensure that all template parameters have been expanded -->
+ <xsl:apply-templates select="$final" mode="preproc:tpl-check" />
+
+ <!-- perform validation before dependency generation to ensure that all
+ dependencies are available -->
+ <xsl:apply-templates select="$final" mode="preproc:pkg-validate" />
+
+ <!-- determine how many passes have been made -->
+ <!-- TODO: reintroduce
+ <xsl:variable name="repass-count"
+ select="count( $final//preproc:repass-record )" />
+ -->
+
+ <!-- assign unique ids to each node -->
+ <xsl:variable name="idized" as="element( lv:package )">
+ <xsl:apply-templates select="$final" mode="preproc:idize" />
+ </xsl:variable>
+
+ <!-- generate deps -->
+ <xsl:variable name="depd" as="element( lv:package )">
+ <xsl:apply-templates select="$idized" mode="preproc:gen-deps" />
+ </xsl:variable>
+
+ <!-- post-process symbol table to resolve any unknowns that require a
+ dependency tree -->
+ <xsl:variable name="resolvd" as="element( lv:package )">
+ <xsl:apply-templates select="$depd" mode="preproc:resolv-syms">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:apply-templates>
+ </xsl:variable>
+
+
+ <!-- compile fragments -->
+ <xsl:message>
+ <xsl:text>[preproc] compiling fragments...</xsl:text>
+ </xsl:message>
+
+
+ <xsl:apply-templates select="$resolvd"
+ mode="preproc:compile-fragments" />
+
+
+ <!-- output a repass count, which could be a strong indicator of performance
+ issues -->
+ <!-- TODO: reintroduce
+ <xsl:message>
+ <xsl:text>[Preprocessor repass count: </xsl:text>
+ <xsl:value-of select="$repass-count" />
+ <xsl:text>] [Node count: </xsl:text>
+ <xsl:value-of select="count( $final//* )" />
+ <xsl:text>]</xsl:text>
+ </xsl:message>
+ -->
+ </xsl:when>
+
+ <!-- return for further processing -->
+ <xsl:otherwise>
+ <xsl:sequence select="$stage3" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ A very primitive guard against unexpanded template parameters.
+-->
+<xsl:template match="*[ starts-with( @*, '@' ) ]" mode="preproc:tpl-check" priority="5">
+ <xsl:message>
+ <xsl:text>[preproc] fatal: unexpanded template parameter: </xsl:text>
+ <xsl:sequence select="@*[ starts-with( ., '@' ) ]" />
+ </xsl:message>
+
+ <xsl:message>
+ <xsl:text>[preproc] fatal: reference node: </xsl:text>
+ <xsl:sequence select="." />
+ </xsl:message>
+
+ <xsl:message>~~~~[begin document dump]~~~~</xsl:message>
+ <xsl:message select="root(.)" />
+ <xsl:message>~~~~[end document dump]~~~~</xsl:message>
+
+ <xsl:message terminate="yes">[preproc] notice: Document dumped.</xsl:message>
+</xsl:template>
+
+
+<!-- this should never happen; but is has, so here's a failsafe -->
+<xsl:template match="lv:apply-template" mode="preproc:tpl-check" priority="5">
+ <xsl:message>
+ <xsl:text>[preproc] fatal: unexpanded template: </xsl:text>
+ <xsl:sequence select="." />
+ </xsl:message>
+
+ <xsl:message>
+ <xsl:text>[preproc] fatal: reference node: </xsl:text>
+ <xsl:sequence select="." />
+ </xsl:message>
+
+ <xsl:message select="'[preproc] internal error: there is a bug in the',
+ 'preprocessor; this should never happen!'" />
+
+ <xsl:message>~~~~[begin document dump]~~~~</xsl:message>
+ <xsl:message select="root(.)" />
+ <xsl:message>~~~~[end document dump]~~~~</xsl:message>
+
+ <xsl:message terminate="yes">[preproc] notice: Document dumped.</xsl:message>
+</xsl:template>
+
+
+<!-- skip things that cannot contain template applications -->
+<xsl:template match="lv:template|lv:const|lv:typedef"
+ mode="preproc:tpl-check" priority="9">
+</xsl:template>
+
+<xsl:template match="*" mode="preproc:tpl-check" priority="1">
+ <xsl:apply-templates select="*" mode="preproc:tpl-check" />
+</xsl:template>
+
+
+
+<!--
+ TODO: This needs to go away in the form of a more performant system
+ that marks a repass as needed *without* scanning the entire tree.
+-->
+<xsl:template name="preproc:tpl-sym-recurse" as="element( lv:package )">
+ <xsl:param name="package" as="element( lv:package )" />
+ <xsl:param name="orig-root" as="element()" />
+
+ <!-- number of iterations where no template applications have
+ happened -->
+ <xsl:param name="tpl-stall-count"
+ tunnel="yes"
+ select="0" />
+
+ <!-- get a list of needed template applications (ignoring applications that
+ are within templates and nested applications) -->
+ <xsl:variable name="apply" as="element( lv:apply-template )*"
+ select="$package//lv:apply-template[
+ not(
+ @name=$package/lv:template/@name
+ or ancestor::lv:template
+ or ancestor::lv:apply-template
+ )
+ ]" />
+ <xsl:variable name="napply" select="count( $apply )" />
+
+ <xsl:choose>
+ <xsl:when test="$apply">
+ <!-- get a list of required templates -->
+ <xsl:variable name="req">
+ <tpl>
+ <xsl:for-each select="$apply">
+ <xsl:copy>
+ <xsl:sequence select="@name" />
+ </xsl:copy>
+ </xsl:for-each>
+ </tpl>
+ </xsl:variable>
+
+ <xsl:variable name="requniq" as="element( lv:apply-template )*" select="
+ $req//lv:apply-template[
+ not( @name=preceding-sibling::lv:apply-template/@name
+ or @name=$package//lv:template/@name ) ]
+ " />
+
+ <xsl:message>
+ <xsl:text>[preproc] </xsl:text>
+ <xsl:value-of select="count( $apply )" />
+ <xsl:text> template(s) still need application: </xsl:text>
+
+ <xsl:for-each select="$apply">
+ <xsl:if test="position() gt 1">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="@name" />
+ </xsl:for-each>
+ </xsl:message>
+
+ <!-- load each of the requested templates (but only once) -->
+ <xsl:variable name="tpls">
+ <!-- TODO: we no longer need to load the templates; the
+ template replacement looks up the template on-demand from
+ the symbol table; we're no longer injecting them -->
+ <xsl:apply-templates mode="preproc:tpl-from-sym" select="$requniq">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ <xsl:with-param name="symtable" select="$package/preproc:symtable" />
+ </xsl:apply-templates>
+ </xsl:variable>
+
+ <!-- if we have recursed and have not decreased the application count at all,
+ then we have a problem -->
+ <xsl:if test="$requniq
+ and ( $package//preproc:sym-available )
+ and not( $package//preproc:repass[ @tpl-applied ] )">
+ <xsl:message terminate="yes">
+ <xsl:text>!!! [preproc] fatal: unable to locate symbols for </xsl:text>
+ <xsl:text>remaining templates: </xsl:text>
+
+ <xsl:for-each select="$requniq">
+ <xsl:if test="position() > 1">
+ <xsl:text>; </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="@name" />
+ </xsl:for-each>
+ </xsl:message>
+ </xsl:if>
+
+ <!-- if there was an error during this part of the process, halt -->
+ <xsl:if test="$tpls//preproc:error">
+ <xsl:message terminate="yes">
+ <xsl:text>!!! [preproc] fatal: terminating due to errors</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <!-- perform expansion on the new package with the needed templates -->
+ <xsl:variable name="result" as="element( lv:package )">
+ <xsl:apply-templates select="$package" mode="preproc:compile">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ <xsl:with-param name="stopshort" select="true()" />
+ </xsl:apply-templates>
+
+ <xsl:message>
+ <xsl:text>[preproc] *expansion complete (recursive)</xsl:text>
+ </xsl:message>
+ </xsl:variable>
+
+ <!-- failsafe to prevent infinite recursion (5 (0-indexed)
+ should be plenty, since everything in the system results in
+ a template application in fewer steps -->
+ <xsl:if test="$requniq and $tpl-stall-count eq 4">
+ <xsl:message select="'!!! [preproc] internal: expansion deadlock!'" />
+ <xsl:message
+ select="'!!! [preproc] internal: stalled for 5 iterations'" />
+
+ <xsl:sequence select="preproc:dump-document( $result )" />
+
+ <xsl:message terminate="yes">
+ <xsl:text></xsl:text>
+ <xsl:text>!!! [preproc] fatal: expansion of remaining </xsl:text>
+ <xsl:text>templates aborted (have all been imported?): </xsl:text>
+
+ <xsl:for-each select="$requniq">
+ <xsl:if test="position() > 1">
+ <xsl:text>; </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="@name" />
+ </xsl:for-each>
+ </xsl:message>
+ </xsl:if>
+
+ <!-- recurse to continue expanding if need be -->
+ <xsl:call-template name="preproc:tpl-sym-recurse">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ <xsl:with-param name="package" select="$result" />
+ <xsl:with-param name="tpl-stall-count"
+ tunnel="yes"
+ select="if ( $package//preproc:tpl-step ) then
+ 0
+ else
+ $tpl-stall-count + 1" />
+ </xsl:call-template>
+ </xsl:when>
+
+
+ <!-- expansion sequences and template short-hand expansions that
+ have not yet taken place, due to one reason or another (too
+ few passes: bug as far as I'm concerned) -->
+ <xsl:when test="$package//lv:expand-sequence[
+ not( ancestor::lv:template
+ or ancestor::lv:apply-template ) ]
+ |$package//t:*[
+ not( ancestor::lv:template
+ or ancestor::lv:apply-template ) ]">
+ <xsl:message select="'[preproc] pending expansions still present'" />
+
+ <xsl:variable name="result" as="element( lv:package )">
+ <xsl:apply-templates select="$package" mode="preproc:compile">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ <xsl:with-param name="stopshort" select="true()" />
+ </xsl:apply-templates>
+ </xsl:variable>
+
+ <xsl:call-template name="preproc:tpl-sym-recurse">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ <xsl:with-param name="package" select="$result" />
+ </xsl:call-template>
+ </xsl:when>
+
+
+ <!-- no further applications are necessary -->
+ <xsl:otherwise>
+ <!-- apply one final pass for the eligibility class generation -->
+ <xsl:call-template name="preproc:elig-class-pass">
+ <xsl:with-param name="package" select="$package" />
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="preproc:elig-class-pass" as="element( lv:package )">
+ <xsl:param name="package" as="element( lv:package )" />
+ <xsl:param name="orig-root" as="element()" />
+
+ <xsl:variable name="final-pkg" as="element( lv:package )">
+ <xsl:apply-templates select="$package" mode="preproc:expand-elig-class">
+ <xsl:with-param name="orig-root" select="$package" />
+ </xsl:apply-templates>
+ </xsl:variable>
+
+ <!-- yield the final pass against the elig-class-augmented package -->
+ <xsl:apply-templates select="$final-pkg" mode="preproc:compile">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ <xsl:with-param name="stopshort" select="true()" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<!--
+ This is but a shell of its former self.
+
+ We are no longer injecting templates, so this can be removed or
+ adapted to do just enough to verify that the template symbols exist.
+-->
+<xsl:template match="lv:apply-template" mode="preproc:tpl-from-sym">
+ <xsl:param name="orig-root" as="element()" />
+
+ <xsl:param name="symtable" as="element( preproc:symtable )"
+ select="root(.)/preproc:symtable" />
+
+ <xsl:variable name="tplname" select="@name" />
+
+ <xsl:variable name="sym" as="element( preproc:sym )?" select="
+ $symtable/preproc:sym[
+ @name=$tplname
+ and @type='tpl'
+ ]
+ " />
+
+ <!-- if we have a symbol table, then attempt to locate it -->
+ <xsl:choose>
+ <!-- if we have located the template, then we know its source (@src must be
+ set, otherwise the template is defined in this package and should have
+ been available to begin with) -->
+ <xsl:when test="$sym">
+ <preproc:sym-available for="{$tplname}" />
+ </xsl:when>
+
+ <!-- nothing we can do yet -->
+ <xsl:otherwise>
+ <xsl:message>
+ <xsl:text>[preproc] template symbol not yet available: </xsl:text>
+ <xsl:value-of select="$tplname" />
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!-- TODO: we should use an attr besides _id; one more definitive -->
+<xsl:template match="*[ @_id ]" mode="preproc:macropass" priority="9">
+ <!-- already preprocessed -->
+ <xsl:sequence select="." />
+</xsl:template>
+<xsl:template match="*[ @_id ]" mode="preproc:idize" priority="9">
+ <!-- already preprocessed -->
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+
+<xsl:template match="preproc:repass-record" mode="preproc:idize" priority="9">
+ <!-- no longer needed; remove -->
+</xsl:template>
+
+
+<!-- do not idize preproc nodes (unneeded waste of cycles) -->
+<xsl:template match="preproc:*" mode="preproc:idize" priority="5">
+ <xsl:sequence select="." />
+</xsl:template>
+
+<!-- do not idize templates (will cause processing problems) -->
+<xsl:template match="lv:template" mode="preproc:idize" priority="5">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<!--
+ Generates a unique id for each element and stores it as @_id
+
+ This allows the node set to be copied and maintain its identity.
+-->
+<xsl:template match="*" mode="preproc:idize" priority="1">
+ <xsl:variable name="id">
+ <xsl:value-of select="generate-id(.)" />
+ </xsl:variable>
+
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:attribute name="_id">
+ <xsl:value-of select="$id" />
+ </xsl:attribute>
+
+ <xsl:apply-templates mode="preproc:idize" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="preproc:resolv-syms" as="element( lv:package )"
+ priority="9">
+ <xsl:param name="orig-root" as="element()" />
+ <xsl:param name="rpcount" select="0" />
+
+ <!-- arbitrary; intended to prevent infinite recursion -->
+ <!-- TODO: same method as for templates; ensure changes, but do not create
+ arbitrary limit -->
+ <xsl:if test="$rpcount = 100">
+ <xsl:sequence select="preproc:dump-document( root() )" />
+
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc] !!! recursion limit reached in resolving `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' symbols</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:variable name="result" as="element( lv:package )">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:message>
+ <xsl:text>[preproc] *resolving symbol attributes...</xsl:text>
+ </xsl:message>
+
+ <xsl:apply-templates mode="preproc:resolv-syms">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:apply-templates>
+ </xsl:copy>
+ </xsl:variable>
+
+ <xsl:variable name="repass"
+ select="$result//preproc:symtable/preproc:repass" />
+
+ <xsl:choose>
+ <!-- repass scheduled; go for it -->
+ <xsl:when test="$repass">
+ <xsl:message>[preproc] *SYM REPASS*</xsl:message>
+ <xsl:message>
+ <xsl:text>[preproc] The following </xsl:text>
+ <xsl:value-of select="count( $repass )" />
+ <xsl:text> symbol(s) are still unresolved:</xsl:text>
+ </xsl:message>
+
+ <xsl:for-each select="$repass">
+ <xsl:message>
+ <xsl:text>[preproc] - </xsl:text>
+ <xsl:value-of select="@ref" />
+ </xsl:message>
+ </xsl:for-each>
+
+ <xsl:apply-templates select="$result" mode="preproc:resolv-syms">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ <xsl:with-param name="rpcount" select="$rpcount + 1" />
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <!-- no repass needed; done -->
+ <xsl:otherwise>
+ <xsl:sequence select="$result" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="preproc:symtable" mode="preproc:resolv-syms" priority="5">
+ <xsl:param name="orig-root" as="element()" />
+
+ <xsl:copy>
+ <xsl:apply-templates mode="preproc:resolv-syms">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:apply-templates>
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ not( @src ) and @dim='?' ]" mode="preproc:resolv-syms" priority="5">
+ <xsl:param name="orig-root" as="element()" />
+
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <xsl:variable name="deps" as="element( preproc:sym-dep )*" select="
+ $pkg/preproc:sym-deps/preproc:sym-dep[ @name=$name ]
+ " />
+
+ <xsl:variable name="depsyms-unresolv" as="element( preproc:sym )*" select="
+ $pkg/preproc:symtable/preproc:sym[
+ @name=$deps/preproc:sym-ref/@name
+ ]
+ " />
+
+ <xsl:variable name="depsyms-resolv">
+ <xsl:for-each select="$depsyms-unresolv">
+ <xsl:choose>
+ <xsl:when test="not( @src )">
+ <xsl:sequence select="." />
+ </xsl:when>
+
+ <!-- look up complete symbol -->
+ <xsl:otherwise>
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="sym" select="
+ document( concat( @src, '.xmlo' ), $orig-root )
+ /lv:package/preproc:symtable/preproc:sym[
+ @name=$name
+ ]
+ " />
+
+ <xsl:if test="not( $sym )">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc] !!! failed to look up symbol `</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:sequence select="$sym" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:variable name="depsyms" select="$depsyms-resolv/preproc:sym" />
+
+ <xsl:choose>
+ <!-- unresolved dependency dimensions; defer until next pass -->
+ <xsl:when test="
+ $depsyms/@dim = '?'
+ ">
+ <xsl:message>
+ <xsl:text>[preproc] deferring `</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>' dimensions with unresolved dependencies</xsl:text>
+ </xsl:message>
+
+ <!-- schedule repass :x -->
+ <xsl:sequence select="." />
+ <preproc:repass src="preproc:sym resolv-syms"
+ ref="{$name}" />
+ </xsl:when>
+
+ <!-- all dependencies are resolved; calculate dimensions -->
+ <xsl:otherwise>
+ <!-- sort dependencies so that the largest dimension is at the top -->
+ <xsl:variable name="maxset">
+ <xsl:for-each select="$depsyms">
+ <xsl:sort select="@dim" data-type="number" order="descending" />
+ <xsl:sequence select="." />
+ </xsl:for-each>
+ </xsl:variable>
+
+ <xsl:variable name="max">
+ <xsl:choose>
+ <xsl:when test="count( $deps/preproc:sym-ref ) = 0">
+ <!-- no dependencies, unknown size, so it's a scalar -->
+ <xsl:text>0</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- largest value -->
+ <xsl:value-of select="$maxset/preproc:sym[1]/@dim" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- failure? -->
+ <xsl:if test="not( $max ) or $max = ''">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc] !!! failed to determine dimensions of `</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <!-- logging -->
+ <xsl:message>
+ <xsl:text>[preproc] resolved `</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>' dimensions as `</xsl:text>
+ <xsl:value-of select="$max" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+
+ <!-- copy, substituting calculated dimensions -->
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:attribute name="dim" select="$max" />
+ <xsl:sequence select="*" />
+ </xsl:copy>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="preproc:repass" mode="preproc:resolv-syms" priority="9">
+ <!-- strip -->
+</xsl:template>
+
+
+<xsl:template match="*" mode="preproc:resolv-syms" priority="1">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="preproc:pkg-validate">
+ <xsl:variable name="symbol-map">
+ <xsl:call-template name="get-symbol-map" />
+ </xsl:variable>
+
+ <xsl:variable name="err">
+ <xsl:apply-templates select="." mode="lvv:validate">
+ <xsl:with-param name="symbol-map" select="$symbol-map" />
+ </xsl:apply-templates>
+ </xsl:variable>
+
+ <xsl:apply-templates select="$err//lvv:error" mode="preproc:handle-lvv-errors">
+ <xsl:with-param name="document" select="." />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<xsl:template match="*|text()" mode="preproc:pkg-validate">
+</xsl:template>
+
+
+<!-- errors should cause a failure -->
+<xsl:template match="lvv:error" mode="preproc:handle-lvv-errors" priority="5">
+ <xsl:param name="document" />
+
+ <!-- output error -->
+ <xsl:message>
+ <xsl:text>!!! </xsl:text>
+ <xsl:value-of select="@desc" />
+
+ <xsl:if test="@path != ''">
+ <xsl:text> (</xsl:text>
+ <xsl:value-of select="@path" />
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+
+ <xsl:text>: </xsl:text>
+ <xsl:value-of select="." />
+ </xsl:message>
+
+ <!-- terminate after we've output each error -->
+ <xsl:if test="not( following-sibling::lvv:error )">
+ <!-- dump document for debugging -->
+ <xsl:sequence select="preproc:dump-document( $document )" />
+ <xsl:message terminate="yes">Compilation failed due to validation errors.</xsl:message>
+ </xsl:if>
+</xsl:template>
+
+
+<xsl:function name="preproc:dump-document">
+ <xsl:param name="document" />
+
+ <xsl:message>~~~~[begin document dump]~~~~</xsl:message>
+ <xsl:message select="$document" />
+ <xsl:message>~~~~[end document dump]~~~~</xsl:message>
+ <xsl:message>internal: document dumped.</xsl:message>
+</xsl:function>
+
+
+<xsl:template match="node()|text()" mode="preproc:handle-lvv-errors" priority="1">
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/preproc/path.xsl b/src/current/include/preproc/path.xsl
new file mode 100644
index 0000000..469603d
--- /dev/null
+++ b/src/current/include/preproc/path.xsl
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Operations on paths
+-->
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<xsl:template name="preproc:get-path">
+ <xsl:param name="path" />
+ <xsl:param name="prev" select="''" />
+
+ <xsl:variable name="first" select="substring-before( $path, '/' )" />
+ <xsl:variable name="rest" select="substring-after( $path, '/' )" />
+
+ <xsl:choose>
+ <!-- if there's no $first, then there is no path separator, in which case
+ we're done; if there's no rest, then there is a path separator, but it
+ resulted in an empty string, meanaing that it ends in a path
+ separator, in which case we are also done -->
+ <xsl:when test="not( $first ) or not( $rest )">
+ <!-- no more path separators; we're done -->
+ <xsl:value-of select="$prev" />
+ </xsl:when>
+
+ <!-- keep recursing -->
+ <xsl:otherwise>
+ <xsl:call-template name="preproc:get-path">
+ <xsl:with-param name="path" select="$rest" />
+ <xsl:with-param name="prev">
+ <xsl:if test="not( $prev = '' )">
+ <xsl:value-of select="concat( $prev, '/' )" />
+ </xsl:if>
+
+ <xsl:value-of select="$first" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!-- FIXME: duplicate code with above -->
+<xsl:template name="preproc:get-basename">
+ <xsl:param name="path" />
+ <xsl:param name="prev" select="''" />
+
+ <xsl:variable name="first" select="substring-before( $path, '/' )" />
+ <xsl:variable name="rest" select="substring-after( $path, '/' )" />
+
+ <xsl:choose>
+ <!-- if there's no $first, then there is no path separator, in which case
+ we're done; if there's no rest, then there is a path separator, but it
+ resulted in an empty string, meanaing that it ends in a path
+ separator, in which case we are also done -->
+ <xsl:when test="not( $first ) or not( $rest )">
+ <!-- no more path separators; we're done -->
+ <xsl:value-of select="$path" />
+ </xsl:when>
+
+ <!-- keep recursing -->
+ <xsl:otherwise>
+ <xsl:call-template name="preproc:get-basename">
+ <xsl:with-param name="path" select="$rest" />
+ <xsl:with-param name="prev">
+ <xsl:if test="not( $prev = '' )">
+ <xsl:value-of select="concat( $prev, '/' )" />
+ </xsl:if>
+
+ <xsl:value-of select="$first" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="preproc:resolv-path">
+ <xsl:param name="path" />
+
+ <!-- in order: strip //, process ../, strip ./ -->
+ <xsl:call-template name="preproc:strip-sdot-path">
+ <xsl:with-param name="path">
+ <xsl:call-template name="preproc:resolv-rel-path">
+ <xsl:with-param name="path">
+ <xsl:call-template name="preproc:strip-extra-path">
+ <xsl:with-param name="path" select="$path" />
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+
+<!-- XXX: warning, this won't like 'foo../' -->
+<xsl:template name="preproc:resolv-rel-path">
+ <xsl:param name="path" />
+
+ <!-- relative paths -->
+ <xsl:variable name="before" select="substring-before( $path, '../' )" />
+ <xsl:variable name="after" select="substring-after( $path, '../' )" />
+
+ <xsl:choose>
+ <xsl:when test="$before">
+ <xsl:call-template name="preproc:resolv-rel-path">
+ <xsl:with-param name="path">
+ <!-- remove the last directory before the ../ -->
+ <xsl:variable name="before-path">
+ <xsl:call-template name="preproc:get-path">
+ <xsl:with-param name="path" select="$before" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:value-of select="$before-path" />
+
+ <!-- the above get-path call will strip the trailing slash -->
+ <xsl:if test="not( $before-path = '' ) and not( $after = '' )">
+ <xsl:text>/</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$after" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+
+
+ <!-- if there's no $before but there is an $after, then we must begin with
+ '../', which we can do nothing with; output it and continue processing
+ the remainder of the path -->
+ <xsl:when test="$after">
+ <xsl:text>../</xsl:text>
+
+ <xsl:call-template name="preproc:resolv-rel-path">
+ <xsl:with-param name="path" select="$after" />
+ </xsl:call-template>
+ </xsl:when>
+
+
+ <!-- no relative paths remaining -->
+ <xsl:otherwise>
+ <xsl:value-of select="$path" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="preproc:strip-sdot-path">
+ <xsl:param name="path" />
+
+ <xsl:choose>
+ <!-- the only time this should be called with an unresolved relative path
+ is if it begins with one, in which case we'll simply output it and
+ continue processing without it -->
+ <xsl:when test="starts-with( $path, '../' )">
+ <xsl:text>../</xsl:text>
+
+ <!-- continue processing without it -->
+ <xsl:call-template name="preproc:strip-sdot-path">
+ <xsl:with-param name="path" select="substring-after( $path, '../' )" />
+ </xsl:call-template>
+ </xsl:when>
+
+
+ <!-- path is safe for processing -->
+ <xsl:otherwise>
+ <xsl:variable name="a" select="substring-before( $path, './' )" />
+ <xsl:variable name="b" select="substring-after( $path, './' )" />
+
+
+ <xsl:choose>
+ <!-- if we found one, recurse -->
+ <xsl:when test="$a or $b">
+ <xsl:call-template name="preproc:strip-sdot-path">
+ <xsl:with-param name="path">
+ <xsl:value-of select="$a" />
+
+ <xsl:if test="$a and $b">
+ <xsl:text>/</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$b" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+
+
+ <!-- done -->
+ <xsl:otherwise>
+ <xsl:value-of select="$path" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="preproc:strip-extra-path">
+ <xsl:param name="path" />
+
+ <xsl:variable name="a" select="substring-before( $path, '//' )" />
+ <xsl:variable name="b" select="substring-after( $path, '//' )" />
+
+
+ <xsl:choose>
+ <!-- if we found one, recurse -->
+ <xsl:when test="$a or $b">
+ <xsl:call-template name="preproc:strip-extra-path">
+ <xsl:with-param name="path">
+ <xsl:value-of select="$a" />
+
+ <xsl:if test="$a and $b">
+ <xsl:text>/</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$b" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- we're done! -->
+ <xsl:otherwise>
+ <xsl:value-of select="$path" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/preproc/symtable.xsl b/src/current/include/preproc/symtable.xsl
new file mode 100644
index 0000000..9210c76
--- /dev/null
+++ b/src/current/include/preproc/symtable.xsl
@@ -0,0 +1,959 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Generates a symbol table from fully a expanded (preprocessed) package
+
+ It is important that this table be generated after fully expanding all
+ templates, macros, etc; otherwise, the table may be incomplete.
+
+ The preproc:sym/@tex attribute is a TeX symbol used for typsetting. This
+ process is not responsible for generating defaults; that should be done by the
+ linker to ensure that there are no conflicts after all symbols are known.
+
+ Here are the recognized types:
+ rate - lv:rate block
+ gen - generator (c:*/@generates)
+ cgen - class generator (lv:classify/@yields)
+ class - classification (lv:classify/@as)
+ param - global param (lv:param)
+ lparam - local param (lv:function/lv:param)
+ const - global constant (lv:const; lv:enum/lv:item)
+ tpl - template (lv:template)
+ type - datatype (lv:typedef)
+ func - function
+
+ Dimensions (think 0=point, 1=line, 2=plane, etc):
+ 0 - scalar
+ 1 - vector
+ 2 - matrix
+ ...
+
+ Symbols from imported packages will be consumed and added to the output,
+ unless local; this has a similiar effect to including a C header file.
+ External symbols are denoted by preproc:sym/@src, which specifies the name of
+ the package from which it was imported. If an imported symbol conflicts with
+ another symbol (imported or otherwise), an error will be produced. Symbols
+ that are imported are implicitly marked as local.
+
+ Certain symbols will "polute" the symbol table of every package that imports
+ it, every package that imports that one, etc; this is for compatibility with
+ the old system and will hopefully be phased out eventually. Pollution will
+ reserve the symbol, but will not provide enough information about that symbol
+ to be useful, which will ensure that a package has to import the symbol
+ explicitly in order to actually make use of it.
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:symtable="http://www.lovullo.com/tame/symtable"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:c="http://www.lovullo.com/calc">
+
+
+<xsl:include href="path.xsl" />
+<xsl:include href="../../tame/src/symtable.xsl" />
+
+
+<!-- we will recurse through the entire tree rather than performing a series of
+ xpaths that are likely to hit the same nodes many times -->
+<xsl:template match="*" mode="preproc:symtable" priority="1">
+ <xsl:apply-templates mode="preproc:symtable" />
+</xsl:template>
+
+<xsl:template match="text()" mode="preproc:symtable" priority="9">
+ <!-- output nothing -->
+</xsl:template>
+
+
+<!-- an alternative way to invoke the preproc:sym-discover template; useful for
+ use on variables -->
+<xsl:template match="*" mode="preproc:sym-discover" as="element()">
+ <xsl:param name="orig-root" />
+
+ <xsl:call-template name="preproc:sym-discover">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template match="preproc:*" mode="preproc:sym-discover"
+ as="element()" priority="9">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<!--
+ Other systems may contribute to a symbol table by invoking this template and
+ supplying the necessary preproc:symtable templates
+
+ TODO: This guy needs some refactoring
+-->
+<xsl:template name="preproc:sym-discover" as="element()">
+ <xsl:param name="orig-root" />
+
+ <xsl:variable name="this-pkg" as="element( lv:package )"
+ select="." />
+
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:variable name="new">
+ <xsl:message>
+ <xsl:text>[preproc/symtable] discovering symbols...</xsl:text>
+ </xsl:message>
+
+ <preproc:syms>
+ <xsl:apply-templates mode="preproc:symtable">
+ <!-- we only need this param for the root children, so this is the only
+ template application that passes this -->
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ </xsl:apply-templates>
+ </preproc:syms>
+ </xsl:variable>
+
+ <!-- gather a list of overrides -->
+ <xsl:variable name="overrides" select="
+ $new/preproc:syms/preproc:sym[ @override='true' ]
+ " />
+
+ <!-- check for duplicates -->
+ <xsl:variable name="symdup" as="element( preproc:sym )*"
+ select="symtable:find-duplicates( $new/preproc:syms )" />
+
+ <!-- overrides that override nothing may be the sign of a bug (expectation
+ of something that isn't there) -->
+ <xsl:for-each select="$overrides[ not( @name=$symdup/@name ) ]">
+ <xsl:message>
+ <xsl:text>[preproc/symtable] warning: symbol /</xsl:text>
+ <xsl:value-of select="$this-pkg/@name" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text> has @override set, but does not override anything</xsl:text>
+ </xsl:message>
+ </xsl:for-each>
+
+ <!-- perform non-override duplicate checks (TODO: @ignore-dup is
+ intended to be temporary while map __head and __tail
+ generation conflicts are resolved) -->
+ <xsl:variable name="symdup-problems" as="element( preproc:sym )*" select="
+ $symdup[
+ not( @name = $overrides/@name
+ and @virtual = 'true' )
+ and not( @ignore-dup = 'true' ) ]
+ " />
+
+ <xsl:for-each select="$symdup-problems">
+ <xsl:variable name="dupname" select="@name" />
+
+ <xsl:choose>
+ <!-- attempt to override a non-virtual symbol -->
+ <xsl:when test="@name=$overrides/@name and not( @virtual='true' )">
+ <xsl:message>
+ <xsl:text>[preproc/symtable] error: cannot override non-virtual symbol /</xsl:text>
+ <xsl:value-of select="$this-pkg/@name" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="@name" />
+ </xsl:message>
+ </xsl:when>
+
+ <!-- just a plain duplicate -->
+ <xsl:otherwise>
+ <xsl:message>
+ <xsl:text>[preproc/symtable] error: duplicate symbol /</xsl:text>
+ <xsl:value-of select="$this-pkg/@name" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text> (defined in ./</xsl:text>
+
+ <!-- output sources -->
+ <xsl:for-each select="
+ $new/preproc:syms/preproc:sym[
+ @name=$dupname
+ and @src
+ and not( @extern='true' )
+ and not( @name=$overrides/@name and @virtual='true' )
+ ]
+ ">
+ <xsl:if test="position() gt 1">
+ <xsl:text> and ./</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="@src" />
+ </xsl:for-each>
+ <xsl:text>)</xsl:text>
+
+ <!-- if virtual, suggest override as an alternative solution -->
+ <xsl:if test="@virtual='true'">
+ <xsl:text>; did you forget @override?</xsl:text>
+ </xsl:if>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <!-- terminate if any duplicates are found, dumping documents for debugging -->
+ <xsl:if test="count( $symdup-problems ) gt 0">
+ <xsl:message>~~~~[begin document dump]~~~~</xsl:message>
+ <xsl:message select="$this-pkg" />
+ <xsl:message>~~~~[end document dump]~~~~</xsl:message>
+
+ <xsl:message>~~~~[begin symbol dump]~~~~</xsl:message>
+ <xsl:message select="$new" />
+ <xsl:message>~~~~[end symbol dump]~~~~</xsl:message>
+
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc/symtable] fatal: aborting due to symbol errors</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+
+ <xsl:variable name="result" as="element( preproc:symtable )">
+ <preproc:symtable>
+ <!-- copy any existing symbols table -->
+ <preproc:syms>
+ <xsl:sequence select="preproc:symtable/preproc:sym" />
+ <xsl:sequence select="$new/preproc:syms/preproc:sym" />
+ </preproc:syms>
+ </preproc:symtable>
+ </xsl:variable>
+
+ <!-- output the symbols, checking for duplicates -->
+ <preproc:symtable>
+ <!-- validate imported externs -->
+ <xsl:variable name="extresults" as="element( preproc:syms )">
+ <xsl:call-template name="preproc:symtable-process-extern">
+ <xsl:with-param name="result" select="$result" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- remove duplicates (if any) -->
+ <xsl:sequence select="
+ $extresults/preproc:sym[
+ not( @name=preceding-sibling::preproc:sym/@name )
+ ]
+ , $extresults//preproc:error
+ " />
+
+ <!-- process symbols (except imported externs) -->
+ <xsl:variable name="newresult" as="element( preproc:syms )">
+ <xsl:call-template name="preproc:symtable-process-symbols">
+ <xsl:with-param name="extresults" select="$extresults" />
+ <xsl:with-param name="new" select="$new/preproc:syms" />
+ <xsl:with-param name="this-pkg" select="$this-pkg" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="dedup" as="element( preproc:sym )*"
+ select="$newresult/preproc:sym[
+ not(
+ (
+ @pollute='true'
+ and not( @type )
+ and (
+ @name=preceding-sibling::preproc:sym/@name
+ or @name=$newresult/preproc:sym[ @type ]/@name
+ )
+ )
+ )
+ ]" />
+
+
+ <xsl:apply-templates mode="preproc:symtable-complete"
+ select="$dedup">
+ <xsl:with-param name="syms" select="$dedup" />
+ </xsl:apply-templates>
+ </preproc:symtable>
+
+ <xsl:message>
+ <xsl:text>[preproc/symtable] done.</xsl:text>
+ </xsl:message>
+
+ <!-- copy all of the original elements after the symbol table; we're not
+ outputting them as we go, so we need to make sure that we don't get
+ rid of them; discard any existing symbol table -->
+ <xsl:apply-templates mode="preproc:symtable-inject" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="preproc:symtable" mode="preproc:symtable-inject"
+ priority="5">
+ <!-- strip old symbol table -->
+</xsl:template>
+
+<xsl:template match="*" mode="preproc:symtable-inject">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<xsl:template name="preproc:symtable-process-extern"
+ as="element( preproc:syms )">
+ <xsl:param name="result" as="element( preproc:symtable )" />
+
+ <xsl:variable name="syms" as="element( preproc:syms )"
+ select="$result/preproc:syms" />
+
+ <preproc:syms>
+ <xsl:for-each select="$syms/preproc:sym[
+ @extern='true'
+ and @src
+ and not( @held ) ]">
+ <xsl:variable name="name" select="@name" />
+
+ <!-- our value may be concrete or an extern itself; we may also import
+ a package with a concrete definition -->
+ <xsl:variable name="ours" select="
+ $syms/preproc:sym[
+ @name=$name
+ and (
+ not( @src )
+ or ( @dtype and @src and not( @extern ) )
+ )
+ ]
+ " />
+
+ <xsl:choose>
+ <!-- we have our own symbol; ensure the important values match the
+ expected (unless @dim has not yet been resolved) -->
+ <!-- XXX: the @dim='?' check leaves room for a dimension mismatch to
+ slip through; re-order checks (see package.xsl) as necessary to
+ ensure that this doesn't happen -->
+ <xsl:when test="$ours">
+ <xsl:if test="
+ not(
+ @type=$ours/@type
+ and @dtype=$ours/@dtype
+ and (
+ @dim=$ours/@dim
+ or $ours/@dim='?'
+ )
+ )
+ ">
+
+ <preproc:error>
+ <xsl:text>extern mismatch: '</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' (imported from </xsl:text>
+ <xsl:value-of select="@src" />
+ <xsl:text>)</xsl:text>
+
+ <xsl:for-each select="@type, @dtype, @dim">
+ <xsl:variable name="aname" select="local-name()" />
+ <xsl:text>; </xsl:text>
+
+ <xsl:value-of select="local-name()" />
+ <xsl:text>=</xsl:text>
+ <xsl:value-of select="$ours/@*[ local-name() = $aname ]" />
+ <xsl:text>, </xsl:text>
+ <xsl:value-of select="." />
+ <xsl:text> expected</xsl:text>
+ </xsl:for-each>
+ </preproc:error>
+ </xsl:if>
+
+ <!-- N.B.: there could potentially be multiple matches -->
+ <!-- TODO: pollution should be removed and l:resolv-extern
+ in the linker should look up the package that should
+ include the resolved extern from the processing stack -->
+ <preproc:sym pollute="true">
+ <xsl:sequence select="$ours[1]/@*|*" />
+ </preproc:sym>
+ </xsl:when>
+
+ <!-- we do not have our own symbol matching this extern;
+ ignore this for now, as it may be handled by a future
+ template expansion -->
+ <xsl:otherwise>
+ <preproc:sym held="true">
+ <xsl:sequence select="@*" />
+ </preproc:sym>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+ </preproc:syms>
+</xsl:template>
+
+
+<xsl:function name="preproc:final-extern-check" as="element(preproc:error )*">
+ <xsl:param name="symtable" as="element( preproc:symtable )" />
+
+ <!-- any remaining unresolved externs at this point are bad -->
+ <xsl:for-each select="$symtable/preproc:sym[
+ @extern = 'true'
+ and @src ]">
+
+ <!-- since @missing may be provided, let's include the actual
+ symbol in the runlog for debugging -->
+ <xsl:message select="'[preproc] missing extern: ', @name" />
+
+ <preproc:error>
+ <xsl:choose>
+ <xsl:when test="@missing and not( @missing = '' )">
+ <xsl:value-of select="normalize-space( @missing )" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>unresolved extern '</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>'</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:text> (required by </xsl:text>
+ <xsl:value-of select="@src" />
+ <xsl:text>)</xsl:text>
+ </preproc:error>
+ </xsl:for-each>
+</xsl:function>
+
+
+<xsl:template name="preproc:symtable-process-symbols">
+ <xsl:param name="extresults" as="element( preproc:syms )" />
+ <xsl:param name="new" as="element( preproc:syms )" />
+ <xsl:param name="this-pkg" as="element( lv:package )" />
+
+ <preproc:syms>
+ <xsl:variable name="cursym" as="element( preproc:sym )*"
+ select="preproc:symtable/preproc:sym[
+ not( @held = 'true' ) ]" />
+
+ <xsl:sequence select="$cursym" />
+
+ <xsl:message>
+ <xsl:text>[preproc/symtable] processing symbol table...</xsl:text>
+ </xsl:message>
+
+ <xsl:for-each select="$new/preproc:sym[ not( @extern='true' and @src ) ]">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="src" select="@src" />
+ <xsl:variable name="dupall" select="
+ (
+ preceding-sibling::preproc:sym,
+ $cursym,
+ $extresults/preproc:sym
+ )[
+ @name=$name
+ ]
+ " />
+ <xsl:variable name="dup" select="
+ $dupall[
+ not(
+ @src=$src
+ or ( not( @src ) and not( $src ) )
+ )
+ ]
+ " />
+
+ <xsl:choose>
+ <xsl:when test="@pollute='true' and not( @type )">
+ <!-- we'll strip these out later -->
+ <xsl:sequence select="." />
+ </xsl:when>
+
+ <!-- note that dupall uses preceding-sibling, which will catch
+ duplicates in that case even if @override is not set -->
+ <xsl:when test="following-sibling::preproc:sym[ @name=$name and @override='true' ]">
+ <!-- overridden; we're obsolete :( -->
+ </xsl:when>
+
+ <!-- if we've gotten this far, then the override is good; clear it -->
+ <xsl:when test="@override='true'">
+ <xsl:copy>
+ <xsl:sequence select="@*[ not( name()='override' ) ], *" />
+ </xsl:copy>
+ </xsl:when>
+
+ <xsl:when test="$dupall[ @type ]">
+ <!-- there is already a symbol of this name from the same package;
+ let's not add duplicates -->
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- this symbol is good; use it -->
+ <xsl:sequence select="." />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+ </preproc:syms>
+</xsl:template>
+
+
+<xsl:template match="preproc:symtable" mode="preproc:symtable" priority="9">
+ <!-- ignore existing symbol tables (for now at least) -->
+</xsl:template>
+
+
+<!-- do not re-import symbol tables that have already been imported -->
+<xsl:template match="lv:import[
+ @package=root(.)/preproc:symtable/preproc:sym[
+ @dtype
+ ]/@src
+ ]"
+ mode="preproc:symtable" priority="9">
+</xsl:template>
+
+
+<xsl:template name="preproc:symimport" match="lv:import[ @package ]" mode="preproc:symtable" priority="8">
+ <xsl:param name="orig-root" />
+ <xsl:param name="package" select="@package" />
+ <xsl:param name="export" select="@export" />
+ <xsl:param name="ignore-keep" select="@ignore-keep" />
+ <xsl:param name="no-extclass" select="@no-extclass" />
+ <xsl:param name="keep-classes" select="@keep-classes" />
+
+ <xsl:variable name="path" select="concat( $package, '.xmlo' )" />
+ <xsl:variable name="syms"
+ select="document( $path, $orig-root )/lv:*/preproc:symtable" />
+
+ <xsl:variable name="import-path" select="$package" />
+
+ <!-- if they're including a program package, do they realize what they're
+ doing!? -->
+ <!-- FIXME: @allow-nonpkg is no longer accurate terminology; change to
+ @allow-nonprg -->
+ <xsl:if test="
+ not( @allow-nonpkg = 'true' )
+ and $syms/parent::lv:package[ @program='true' ]
+ ">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc/symtable] error: refusing to import non-package </xsl:text>
+ <xsl:value-of select="$import-path" />
+ <xsl:text>; use @allow-nonpkg to force (if you know what you are doing)</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <!-- determine our path from our name -->
+ <xsl:variable name="our-path">
+ <xsl:call-template name="preproc:get-path">
+ <xsl:with-param name="path"
+ select="ancestor::lv:package/@name" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- to keep everything consistent and to simplify package equality
+ assertions, resolve relative paths -->
+ <xsl:variable name="import-default-path" select="$import-path" />
+
+ <xsl:message>
+ <xsl:text>[preproc/symtable] importing symbol table of </xsl:text>
+ <xsl:value-of select="$import-path" />
+ <xsl:text>...</xsl:text>
+ </xsl:message>
+
+ <!-- attempt to import symbols from the processed package -->
+ <xsl:if test="not( $syms )">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc/symtable] internal error: </xsl:text>
+ <xsl:text>failed to locate symbol table: </xsl:text>
+ <xsl:value-of select="$path" />
+ </xsl:message>
+ </xsl:if>
+
+ <!-- copy directly into symbol table, setting external source; local symbols
+ will not be imported -->
+ <xsl:for-each select="
+ $syms/preproc:sym[
+ (
+ not( @local='true' )
+ or @pollute='true'
+ or (
+ ( @type='class' or @type='cgen' )
+ and $keep-classes='true'
+ )
+ )
+ and not( $no-extclass='true' and @extclass='true' )
+ ]
+ ">
+ <xsl:copy>
+ <xsl:choose>
+ <!-- pollution should have only the name and pollution status copied
+ over, which has the effect of reserving the symbol but not
+ providing enough information to actually make use of it; only
+ strip the data if this is a second-hand import -->
+ <!-- TODO: this list has gotten too large, but reducing it will require
+ refactoring other compilers and may reduce performance -->
+ <xsl:when test="@pollute='true' and @local='true'">
+ <xsl:sequence select="@name, @src, @pollute, @keep, @parent, @extclass" />
+ </xsl:when>
+
+ <!-- copy all the symbol information -->
+ <xsl:otherwise>
+ <xsl:sequence select="@*" />
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- all imported symbols are implicitly local (so including one package
+ will not include symbols down the entire hierarchy), unless the
+ symbol is explicitly marked global or @export was provided on the
+ import node -->
+ <xsl:if test="not( $export='true' )">
+ <xsl:attribute name="local" select="'true'" />
+ </xsl:if>
+
+ <!-- determine the relative path to the import -->
+ <xsl:attribute name="src">
+ <xsl:choose>
+ <!-- if no @src is set, then the answer is simple: the relative path is
+ the import path -->
+ <xsl:when test="not( @src )">
+ <xsl:value-of select="$import-default-path" />
+ </xsl:when>
+
+ <!-- otherwise, we need to merge the import path into the existing
+ relative path by prepending the import path (sans the package name
+ itself) onto the existing relative path and resolving relative
+ paths -->
+ <xsl:otherwise>
+ <xsl:call-template name="preproc:resolv-path">
+ <xsl:with-param name="path">
+ <!-- get the path of the import, sans package name -->
+ <xsl:variable name="path">
+ <xsl:call-template name="preproc:get-path">
+ <xsl:with-param name="path" select="$import-path" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- concatenate it with the existing relative path -->
+ <xsl:if test="not( $path = '' )">
+ <xsl:value-of select="concat( $path, '/' )" />
+ </xsl:if>
+
+ <xsl:value-of select="@src" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+
+ <!-- keep manipulation: *always* keep classes if requested, even if
+ @ignore-keep is provided; in the case of the latter, unsets @keep -->
+ <xsl:choose>
+ <!-- keep classes when requested -->
+ <xsl:when test="
+ ( @type = 'class' or @type = 'cgen' )
+ and $keep-classes = 'true'">
+
+ <xsl:attribute name="keep" select="'false'" />
+ </xsl:when>
+
+ <!-- demolish @keep if requested -->
+ <xsl:when test="$ignore-keep = 'true'">
+ <xsl:attribute name="keep" select="'false'" />
+ </xsl:when>
+ </xsl:choose>
+
+ <!-- children should always be copied, unless poluting -->
+ <xsl:if test="not( @pollute='true' and @local='true' )">
+ <xsl:sequence select="preproc:*" />
+ </xsl:if>
+ </xsl:copy>
+ </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template match="lv:template" mode="preproc:symtable" priority="9">
+ <!-- do not process template bodies; we're only interested in the symbols
+ they produce after expansion -->
+ <preproc:sym name="{@name}"
+ type="tpl" dim="0" desc="{@desc}" />
+</xsl:template>
+
+
+<xsl:template match="lv:rate" mode="preproc:symtable" priority="5">
+ <xsl:variable name="external" select="boolean( @external='true' )" />
+
+ <preproc:sym name="{@yields}" type="rate"
+ extclass="{$external}" keep="{boolean( @keep )}"
+ local="{@local}" dtype="float" dim="0" tex="{@sym}" />
+
+ <xsl:apply-templates mode="preproc:symtable" />
+</xsl:template>
+
+
+<xsl:template match="lv:const" mode="preproc:symtable" priority="5">
+ <xsl:variable name="dim">
+ <xsl:choose>
+ <!-- TODO: matrix/vector predicate to support either type via
+ @values -->
+ <xsl:when test="./lv:set or @values">
+ <xsl:text>2</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="./lv:item">
+ <xsl:text>1</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>0</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- TODO: remove magic support -->
+ <preproc:sym name="{@name}"
+ magic="{boolean( @magic='true' )}"
+ type="const" dtype="{@type}" dim="{$dim}" desc="{@desc}" tex="{@sym}">
+
+ <!-- may or may not exist -->
+ <xsl:sequence select="@value" />
+ </preproc:sym>
+
+ <!-- for performance, we will not recurse any further; the rest are simply
+ data declarations -->
+</xsl:template>
+
+
+<xsl:template match="c:*[ @generates ]" mode="preproc:symtable" priority="5">
+ <xsl:variable name="parent" select="ancestor::lv:rate" />
+
+ <preproc:sym name="{@generates}" keep="{boolean( $parent/@keep )}"
+ parent="{$parent/@yields}"
+ type="gen" dtype="float" dim="1" desc="{@desc}" tex="{@sym}" />
+
+ <xsl:apply-templates mode="preproc:symtable" />
+</xsl:template>
+
+
+<!-- note the @dim value; this is determined later from its dependencies -->
+<xsl:template match="lv:classify" mode="preproc:symtable" priority="5">
+ <xsl:variable name="external" select="boolean( @external='true' )" />
+ <xsl:variable name="terminate" select="boolean( @terminate='true' )" />
+ <xsl:variable name="keep" select="boolean( @keep='true' )" />
+
+ <preproc:sym name=":class:{@as}"
+ extclass="{$external}" terminate="{$terminate}" keep="{$keep}"
+ type="class" dim="?" desc="{@desc}" yields="{@yields}"
+ orig-name="{@as}">
+
+ <!-- copy preprocessor metadata to symbol for easy reference -->
+ <xsl:sequence select="@preproc:*" />
+ </preproc:sym>
+
+ <!-- generator if @yields is provided (note that we also have a @yields above
+ to avoid scanning separate object files for such common information)
+ -->
+ <xsl:if test="@yields">
+ <preproc:sym name="{@yields}" keep="{$keep}"
+ parent=":class:{@as}"
+ extclass="{$external}" terminate="{$terminate}"
+ type="cgen" dtype="boolean" dim="?" desc="{@desc}">
+
+ <xsl:sequence select="@preproc:*" />
+ </preproc:sym>
+ </xsl:if>
+
+ <xsl:apply-templates mode="preproc:symtable" />
+</xsl:template>
+
+
+<!-- Will be completed during post-processing so that typedefs can be
+ properly resolved -->
+<xsl:template match="lv:param" mode="preproc:symtable" priority="5">
+ <xsl:variable name="dim">
+ <xsl:call-template name="preproc:param-dim" />
+ </xsl:variable>
+
+ <!-- we use the primitive data type derived from the typedef to ensure that
+ the system can still make use of the type even when the typedef is not
+ exported; indeed, typedefs are simply restrictions that need only be
+ known for compiling (at least at present). Also note the keep="true" to
+ ensure that all param symbols are retained after linking -->
+ <preproc:sym name="{@name}" keep="true"
+ type="param" dtype="{@type}" dim="{$dim}" desc="{@desc}" tex="{@sym}" />
+</xsl:template>
+
+
+<xsl:template match="lv:typedef" mode="preproc:symtable" priority="5">
+ <!-- FIXME: this is a kluge -->
+ <xsl:variable name="dtype" as="xs:string?"
+ select="if ( lv:base-type ) then
+ @name
+ else
+ lv:enum/@type
+ , lv:union/lv:typedef[1]/lv:enum/@type" />
+
+ <xsl:if test="not( $dtype )">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc/symtable] internal error: </xsl:text>
+ <xsl:text>failed to resolve type primitve of `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <preproc:sym name="{@name}" dtype="{$dtype}"
+ type="type" dim="0" desc="{@desc}" />
+
+ <xsl:apply-templates mode="preproc:symtable" />
+</xsl:template>
+
+
+<xsl:template match="lv:typedef/lv:enum/lv:item" mode="preproc:symtable" priority="5">
+ <xsl:variable name="dtype" select="parent::lv:enum/@type" />
+
+ <preproc:sym name="{@name}" value="{@value}"
+ type="const" dtype="{$dtype}" dim="0" desc="{@desc}" />
+</xsl:template>
+
+
+<xsl:template match="lv:function" mode="preproc:symtable" priority="5">
+ <!-- default TeX symbol to the function name -->
+ <xsl:variable name="tex">
+ <xsl:choose>
+ <xsl:when test="@sym">
+ <xsl:value-of select="@sym" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>\textrm{</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>}</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- TODO: determine return data type from tail -->
+ <!-- TODO: same for dim -->
+ <!-- functions can have circular dependencies (recursion) -->
+ <preproc:sym name="{@name}" type="func" dtype="float" dim="0" desc="{@desc}"
+ tex="{$tex}" allow-circular="true">
+
+ <!-- we need to include the argument order and symbol refs so that the
+ compiler knows how to call the function -->
+ <xsl:variable name="fname" select="@name" />
+ <xsl:for-each select="lv:param">
+ <preproc:sym-ref name=":{$fname}:{@name}" />
+ </xsl:for-each>
+ </preproc:sym>
+
+ <xsl:apply-templates mode="preproc:symtable" />
+</xsl:template>
+
+
+<!--
+ Function parameters are local to the function and are represented differently
+ in the symbol table than most other symbols. In particular:
+ - They have type lparam, not param
+ - Their name begins with a colon, which is normally invalid (ensuring that
+ there will be no naming conflicts)
+ - The name following the colon is the concatenation of the function name,
+ an underscore and the param name
+-->
+<xsl:template match="lv:function/lv:param" mode="preproc:symtable" priority="6">
+ <!-- determine number of dimensions -->
+ <xsl:variable name="dim">
+ <xsl:call-template name="preproc:param-dim" />
+ </xsl:variable>
+
+ <xsl:variable name="fname" select="parent::lv:function/@name" />
+
+ <preproc:sym name=":{$fname}:{@name}" parent="{$fname}" varname="{@name}"
+ type="lparam" dtype="{@type}" dim="{$dim}" desc="{@desc}" tex="{@sym}">
+
+ <!-- may or may not be defined -->
+ <xsl:sequence select="@default" />
+ </preproc:sym>
+</xsl:template>
+
+
+<!--
+ Same concept as function params
+-->
+<xsl:template match="c:let/c:values/c:value" mode="preproc:symtable" priority="5">
+ <xsl:variable name="name" select="@name" />
+
+ <!-- determine number of dimensions -->
+ <xsl:variable name="dim">
+ <xsl:call-template name="preproc:param-dim" />
+ </xsl:variable>
+
+ <!-- the name is generated automatically by the preprocessor; the user cannot
+ set it -->
+ <xsl:variable name="lname" select="
+ ancestor::c:let[
+ c:values/c:value[ @name=$name ]
+ ]/@name
+ " />
+
+ <preproc:sym name=":{$lname}:{@name}" local="true" varname="{@name}"
+ type="lparam" dtype="{@type}" dim="{$dim}" desc="{@desc}" tex="{@sym}" />
+
+ <xsl:apply-templates mode="preproc:symtable" />
+</xsl:template>
+
+
+<xsl:template match="lv:extern" mode="preproc:symtable" priority="5">
+ <preproc:sym desc="{@name} extern" extern="true" missing="{@missing}">
+ <!-- copy all the user-supplied params -->
+ <xsl:sequence select="@*" />
+ </preproc:sym>
+</xsl:template>
+
+
+<xsl:template match="lv:meta" mode="preproc:symtable" priority="5">
+ <xsl:for-each select="lv:prop">
+ <preproc:sym name=":meta:{@name}" type="meta"
+ keep="true" />
+ </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='param' ]" mode="preproc:symtable-complete" priority="5">
+ <xsl:param name="syms" as="element( preproc:sym )*" />
+
+ <!-- attempt to derive type information from a typedef -->
+ <!-- TODO: also check symbol table after import (post-process) -->
+ <xsl:variable name="type" select="@dtype" />
+ <xsl:variable name="typedef" as="element( preproc:sym )?"
+ select="$syms[ @type = 'type'
+ and @name = $type ]" />
+
+ <xsl:if test="not( $typedef and $typedef/@dtype )">
+ <xsl:message terminate="yes">
+ <xsl:text>[preproc/symtable] internal error: </xsl:text>
+ <xsl:text>failed to resolve type: </xsl:text>
+ <xsl:value-of select="$type" />
+ </xsl:message>
+ </xsl:if>
+
+ <!-- complete datatype with primitive -->
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+ <xsl:attribute name="dtype" select="$typedef/@dtype" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="*" mode="preproc:symtable-complete" priority="1">
+ <!-- symbol does not need completion -->
+ <xsl:sequence select="." />
+</xsl:template>
+
+<!--
+ Determines param dimension from its string definition:
+ vector = 1-dimensional;
+ matrix = 2-dimensional;
+ otherwise, scalar = 0-dimensional
+
+ Other dimensions are certainly supported, but @set's syntax does not support
+ their specification.
+-->
+<xsl:template name="preproc:param-dim">
+ <xsl:choose>
+ <xsl:when test="@set = 'vector'">
+ <xsl:text>1</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="@set = 'matrix'">
+ <xsl:text>2</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>0</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/preproc/template.xsl b/src/current/include/preproc/template.xsl
new file mode 100644
index 0000000..e1e8edb
--- /dev/null
+++ b/src/current/include/preproc/template.xsl
@@ -0,0 +1,1149 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Performs template processing and expansion
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="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"
+ xmlns:util="http://www.lovullo.com/util"
+ xmlns:eseq="http://www.lovullo.com/tame/preproc/expand/eseq"
+ exclude-result-prefixes="xs ext eseq lv util">
+
+<xsl:import href="../../tame/src/preproc/expand/expand-sequence.xsl" />
+
+
+<!--
+ Macro that abstracts away the boilerplate template application code when
+ dealing with a single root template
+
+ This macro will cause another macro expansion pass.
+-->
+<xsl:template match="lv:rate-each-template" mode="preproc:macros" priority="5">
+ <!-- TODO: debug flag
+ <xsl:message>
+ <xsl:text>[preproc] expanding rate-each-template </xsl:text>
+ <xsl:value-of select="@yields" />
+ <xsl:text>...</xsl:text>
+ </xsl:message>
+ -->
+
+ <lv:rate-each>
+ <!-- copy all attributes except for those we will explicitly handle below -->
+ <xsl:sequence select="@*[
+ not( local-name() = 'name' )
+ ]" />
+
+ <!-- since the entire body is composed from a template, we do not need to
+ accept an index from the user; traditionally, we use 'k' to leave
+ indexes like 'i' and 'j' open -->
+ <xsl:variable name="index">
+ <xsl:text>k</xsl:text>
+ </xsl:variable>
+
+ <xsl:attribute name="index">
+ <xsl:value-of select="$index" />
+ </xsl:attribute>
+
+ <!-- any lv:* nodes should be placed at the top -->
+ <xsl:sequence select="./lv:class" />
+
+ <!-- the template is expected to have an @index@ parameter -->
+ <lv:apply-template name="{@name}">
+ <lv:with-param name="@index@" value="{$index}" />
+
+ <!-- copy any user-provided params into the template application -->
+ <xsl:sequence select="./lv:with-param" />
+ </lv:apply-template>
+ </lv:rate-each>
+
+ <!-- since we generated another preprocessor macro (lv:rate-each), notify the
+ system that we will need to make another pass -->
+ <preproc:repass tpl="{@name}" />
+</xsl:template>
+
+
+<!--
+ If the symbol table is not yet available, defer short-hand
+ applications for now
+
+ It is far less costly to leave the node alone than it is to process
+ and copy potentially massive trees only to have to repass anyway.
+-->
+<xsl:template mode="preproc:macros" priority="9"
+ match="t:*[ not( root(.)/preproc:symtable ) ]">
+ <xsl:sequence select="." />
+ <preproc:repass need-sym="true" />
+</xsl:template>
+
+
+<!--
+ Converts shorthand template applications into full template expansions
+
+ TODO: This causes an extra pass; we'd like to avoid having to do that.
+-->
+<xsl:template match="t:*" mode="preproc:macros" priority="5">
+ <!-- TODO: debug flag
+ <xsl:message>
+ <xsl:text>[preproc] expanding template shorthand for </xsl:text>
+ <xsl:value-of select="local-name()" />
+ <xsl:text>...</xsl:text>
+ </xsl:message>
+ -->
+
+ <xsl:variable name="name" as="xs:string"
+ select="concat( '_', local-name(), '_' )" />
+
+ <xsl:variable name="params" as="element( lv:with-param )*">
+ <xsl:for-each select="@*">
+ <lv:with-param name="@{local-name()}@" value="{.}" />
+ </xsl:for-each>
+
+ <!-- there may be existing lv:with-param nodes -->
+ <xsl:sequence select="./lv:with-param" />
+
+ <xsl:variable name="paramnodes"
+ select="*[ not( local-name() = 'with-param' ) ]" />
+
+ <!-- if sub-nodes were provided, pass it as the "@values@" param -->
+ <xsl:if test="$paramnodes">
+ <lv:with-param name="@values@">
+ <xsl:sequence select="$paramnodes" />
+ </lv:with-param>
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- XXX: a large chunk of this is duplicate code; factor out -->
+ <xsl:variable name="tpl" as="element( lv:template )?"
+ select="preproc:locate-template( $name, root( . ) )" />
+
+ <xsl:variable name="src-root" as="element( lv:package )"
+ select="if ( root(.)/lv:package ) then
+ root(.)/lv:package
+ else
+ root(.)" />
+
+ <xsl:choose>
+ <xsl:when test="$tpl">
+ <!-- avoid a costly repass; apply immediately -->
+ <xsl:sequence select="preproc:expand-template(
+ $tpl, $src-root, $params, . )" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <preproc:error>Undefined template <xsl:value-of select="$name" /></preproc:error>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:function name="preproc:locate-template"
+ as="element( lv:template )?">
+ <xsl:param name="name" as="xs:string" />
+ <xsl:param name="root" as="element( lv:package )" />
+
+ <xsl:variable name="sym" as="element( preproc:sym )?"
+ select="$root/preproc:symtable/preproc:sym[
+ @name = $name ]" />
+
+ <xsl:variable name="package" as="element( lv:package )?"
+ select="if ( $sym/@src ) then
+ document( concat( $sym/@src, '.xmlo' ), $__entry-root )
+ /lv:package
+ else
+ $root/lv:package" />
+
+ <!-- legacy lookup for inline-templates; needs to regenerate symbol
+ list, then this can be removed -->
+ <xsl:variable name="inline-match" as="element( lv:template )?"
+ select="$root//lv:template[ @name = $name ]" />
+
+ <xsl:sequence select="if ( $inline-match ) then
+ $inline-match
+ else
+ $package//lv:template[ @name=$name ]" />
+</xsl:function>
+
+
+
+<!--
+ Applies a template in place as if it were pasted directly into the XML in that
+ location
+
+ This works much like a C macro and is intended to be a very simple means of
+ code re-use that permits free construction of lv:rate blocks. See XSD
+ documentation.
+
+ It is *very important* that these macros be expanded before the templates
+ themselves are removed from the XML by further processing.
+
+ Note that if the attribute shorthand is used for params, one extra expansion
+ will have to occur, which is an additional performance hit.
+-->
+<xsl:template match="lv:apply-template[
+ @name=root(.)/preproc:symtable/preproc:sym/@name ]
+ |lv:apply-template[ @name=root(.)//lv:template/@name ]"
+ mode="preproc:macros" priority="6">
+
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="attrparams" select="@*[ not( local-name() = 'name' ) ]" />
+
+ <!-- used for type checking -->
+ <xsl:variable name="root" as="element( lv:package )"
+ select="if ( root( . ) instance of document-node() ) then
+ root( . )/lv:package
+ else
+ root( . )" />
+
+ <xsl:variable name="src-root" as="element( lv:package )"
+ select="if ( $root/lv:package ) then
+ $root/lv:package
+ else
+ $root" />
+
+ <xsl:variable name="tpl" as="element( lv:template )?"
+ select="preproc:locate-template( $name, $root )" />
+
+ <xsl:choose>
+ <xsl:when test="exists( $tpl ) and $attrparams">
+ <xsl:message>
+ <xsl:text>[preproc] expanding template parameters for </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>...</xsl:text>
+ </xsl:message>
+
+ <xsl:variable name="params" as="element( lv:with-param )*">
+ <xsl:for-each select="$attrparams">
+ <lv:with-param name="@{local-name()}@" value="{.}" />
+ </xsl:for-each>
+
+ <!-- there may also be non-shorthand params -->
+ <xsl:sequence select="lv:with-param" />
+ </xsl:variable>
+
+ <!-- immediately apply without a wasteful repass -->
+ <xsl:sequence select="preproc:expand-template(
+ $tpl, $src-root, $params, . )" />
+ </xsl:when>
+
+ <xsl:when test="$tpl">
+ <xsl:sequence select="preproc:expand-template(
+ $tpl, $src-root, lv:with-param, . )" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <preproc:error>Undefined template <xsl:value-of select="$name" /></preproc:error>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:function name="preproc:expand-template">
+ <xsl:param name="tpl" as="element( lv:template )" />
+ <xsl:param name="src-root" />
+ <xsl:param name="params" as="element( lv:with-param )*" />
+ <xsl:param name="context" as="node()" />
+
+ <xsl:variable name="name" as="xs:string"
+ select="$tpl/@name" />
+
+ <!-- TODO: debug flag
+ <xsl:message>
+ <xsl:text>[preproc] applying template </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>...</xsl:text>
+ </xsl:message>
+ -->
+
+ <!-- check to ensure that each given argument is defined, unless @strict
+ is false -->
+ <xsl:for-each select="
+ $params[
+ not( @name=$tpl/lv:param/@name )
+ and not( @strict='false' )
+ ]
+ ">
+ <preproc:error>
+ <xsl:text>undefined template param </xsl:text>
+ <xsl:value-of select="$name" />/<xsl:value-of select="@name" />
+ <xsl:text>; available params are </xsl:text>
+
+ <xsl:for-each select="$tpl/lv:param/@name">
+ <xsl:if test="position() > 1">
+ <xsl:text>; </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="." />
+ </xsl:for-each>
+ </preproc:error>
+ </xsl:for-each>
+
+ <!-- replace this node with a copy of all the child nodes of the given
+ template; this inlines it as if it were copied and pasted directly
+ into the XML, much like a C macro -->
+ <xsl:apply-templates
+ select="$tpl[ 1 ]/*"
+ mode="preproc:apply-template">
+
+ <xsl:with-param name="apply" select="$context"
+ tunnel="yes" />
+ <xsl:with-param name="apply-tpl-name" select="$name"
+ tunnel="yes" />
+ <xsl:with-param name="params" select="$params"
+ tunnel="yes" />
+
+ <xsl:with-param name="first-child" select="true()" />
+ <xsl:with-param name="src-root" select="$src-root"
+ tunnel="yes" />
+ </xsl:apply-templates>
+
+ <!-- since templates can include anything, we should perform another pass
+ to be sure that all macros that this template may contain/generate
+ are expanded -->
+ <preproc:repass tpl="{$name}" />
+ <preproc:tpl-step name="{$name}" type="apply-template" />
+</xsl:function>
+
+
+
+<!--
+ An inline template implicitly defines and then immediately applies a template
+ with an optional looping construct
+-->
+<xsl:template match="lv:inline-template" mode="preproc:macros" priority="5">
+ <xsl:variable name="name" select="concat( '___i', generate-id(.), '___' )" />
+ <xsl:variable name="inline" select="." />
+
+ <xsl:message>
+ <xsl:text>[preproc] preparing inline template </xsl:text>
+ <xsl:value-of select="$name" />
+ </xsl:message>
+
+ <!-- generate template -->
+ <lv:template name="{$name}" desc="Inline template" preproc:from-inline="{$name}">
+ <!-- generate params (from both our own attrs and any for-each sets) -->
+ <xsl:variable name="params">
+ <params>
+ <xsl:for-each select="@*, lv:for-each/lv:set/@*">
+ <!-- manual lv:param's will override default param generation -->
+ <xsl:if test="not( local-name() = $inline/lv:param/@name )">
+ <lv:param name="@{local-name()}@" desc="Generated param" />
+ </xsl:if>
+ </xsl:for-each>
+
+ <xsl:if test="lv:for-each/lv:sym-set">
+ <lv:param name="@sym_name@" desc="Symbol name" />
+ <lv:param name="@sym_type@" desc="Symbol type" />
+ <lv:param name="@sym_dim@" desc="Symbol degree (dimensions)" />
+ <lv:param name="@sym_desc@" desc="Symbol description (if any)" />
+ <lv:param name="@sym_yields@" desc="Symbol yield name (if any)" />
+ <lv:param name="@sym_parent@" desc="Symbol parent (if any)" />
+ <lv:param name="@sym_external@" desc="Symbol external to classifier" />
+ </xsl:if>
+ </params>
+ </xsl:variable>
+
+ <!-- use only unique params from each attribute source -->
+ <xsl:sequence select="
+ $params//lv:param[
+ not( @name=preceding-sibling::lv:param/@name )
+ ]
+ " />
+
+ <!-- include any params priovided by the user (they may be used to
+ generate content) -->
+ <xsl:sequence select="lv:param" />
+
+ <!-- copy template body -->
+ <!-- FIXME: do not use local-name here; use instance of -->
+ <xsl:sequence select="*[ not( local-name() = 'for-each' ) ]" />
+ </lv:template>
+
+ <xsl:choose>
+ <xsl:when test="lv:for-each">
+ <xsl:apply-templates select="lv:for-each/lv:*" mode="preproc:inline-apply">
+ <xsl:with-param name="name" select="$name" />
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:apply-templates select="." mode="preproc:inline-apply">
+ <xsl:with-param name="name" select="$name" />
+ </xsl:apply-templates>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- the next pass will perform the template application -->
+ <preproc:repass tpl="{$name}" />
+</xsl:template>
+
+
+<xsl:template match="lv:inline-template|lv:for-each/lv:set" mode="preproc:inline-apply">
+ <xsl:param name="name" />
+
+ <!-- immediately apply the template -->
+ <lv:apply-template name="{$name}">
+ <!-- each attribute will be considered to be a template param (and any
+ parent lv:inline-template attributes will be added to each and every
+ set) -->
+ <xsl:for-each select="@*|ancestor::lv:inline-template/@*">
+ <lv:with-param name="@{local-name()}@" value="{.}" />
+ </xsl:for-each>
+ </lv:apply-template>
+</xsl:template>
+
+
+<xsl:template mode="preproc:inline-apply"
+ match="lv:for-each/lv:sym-set">
+ <xsl:param name="name" />
+
+ <xsl:param name="src-root" as="element( lv:package )"
+ select="root(.)"
+ tunnel="yes" />
+
+ <!-- TODO: moar predicates! -->
+ <xsl:variable name="syms" as="element( preproc:sym )*"
+ select="$src-root/preproc:symtable/preproc:sym[
+ ( not( current()/@type )
+ or @type = current()/@type )
+ and ( not( current()/@yields )
+ or @yields = current()/@yields )
+ and ( not( current()/@parent )
+ or @parent = current()/@parent )
+ and ( not( current()/@external )
+ or @external = current()/@external )
+ and ( not( current()/@imports = 'true' )
+ or not( @src ) )
+ and ( not( current()/@name-prefix )
+ or substring( @name,
+ 1,
+ string-length(current()/@name-prefix ) )
+ = current()/@name-prefix
+ or substring( @orig-name,
+ 1,
+ string-length(current()/@name-prefix ) )
+ = current()/@name-prefix ) ]" />
+
+ <xsl:for-each select="$syms">
+ <!-- some symbols are modified to include a prefix; these internal
+ details should not be exposed to the user, since they are
+ useless within the DSL itself -->
+ <xsl:variable name="name-strip"
+ select="substring-after(
+ substring-after( @name, ':' ),
+ ':' )" />
+
+ <xsl:variable name="sym-name"
+ select="if ( $name-strip ) then
+ $name-strip
+ else
+ @name" />
+
+ <lv:apply-template name="{$name}">
+ <lv:with-param name="@sym_name@" value="{$sym-name}" />
+ <lv:with-param name="@sym_type@" value="{@type}" />
+ <lv:with-param name="@sym_dim@" value="{@dim}" />
+ <lv:with-param name="@sym_desc@" value="{@desc}" />
+ <lv:with-param name="@sym_yields@" value="{@yields}" />
+ <lv:with-param name="@sym_parent@" value="{@parent}" />
+ <lv:with-param name="@sym_external@" value="{@external}" />
+ </lv:apply-template>
+ </xsl:for-each>
+</xsl:template>
+
+
+<!--
+ This block is used when we attempt to apply a template that has not been
+ defined
+-->
+<xsl:template match="lv:apply-template" mode="preproc:macros" priority="5">
+ <!-- keep this application around for later -->
+ <xsl:sequence select="." />
+
+ <!-- nothing we can do yet -->
+ <xsl:message>
+ <xsl:text>[preproc] deferring application of unknown template </xsl:text>
+ <xsl:value-of select="@name" />
+ </xsl:message>
+
+ <preproc:repass need-sym="{@name}" />
+</xsl:template>
+
+
+<xsl:template match="lv:template/lv:param" mode="preproc:apply-template" priority="5">
+ <xsl:param name="first-child" select="false()" />
+
+ <!-- do not copy the template params for the template being applied; that
+ could result in some potentially inteesting bugs -->
+ <xsl:if test="not( $first-child )">
+ <xsl:sequence select="." />
+ </xsl:if>
+</xsl:template>
+
+
+<!--
+ Preprocesses template nodes
+
+ Currently, only attributes will be processed. Otherwise, the template ends up
+ recursing on all children to process all attributes.
+-->
+<xsl:template match="*" mode="preproc:apply-template" priority="1">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"
+ mode="preproc:apply-template" />
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="lv:param-meta" mode="preproc:apply-template" priority="5">
+ <xsl:param name="apply" as="node()"
+ tunnel="yes" />
+
+ <!-- determine the value -->
+ <xsl:variable name="value">
+ <xsl:call-template name="preproc:template-param-value">
+ <xsl:with-param name="name" select="@value" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <preproc:tpl-meta name="{@name}" value="{$value}" />
+</xsl:template>
+
+
+<!--
+ Copies child nodes associated with the param during application into the
+ replacement
+
+ This effectively allows the definition of custom node types.
+-->
+<xsl:template match="lv:param-copy" mode="preproc:apply-template" priority="5">
+ <xsl:param name="params" as="element( lv:with-param )*"
+ tunnel="yes" />
+
+ <!-- if any metadata has been defined, retain it so that it may be queried by
+ future templates -->
+ <xsl:apply-templates mode="preproc:apply-template"
+ select="lv:param-meta" />
+
+ <xsl:variable name="varname" select="@name" />
+
+ <!-- determine the nodes to copy -->
+ <xsl:variable name="copy"
+ select="$params[ @name=$varname ]/*" />
+
+ <xsl:choose>
+ <!-- if the @expand flag is set, then immediately begin expanding any
+ template params that it may have (that we know of), in effect making
+ the body of the template application *part* of the template itself -->
+ <xsl:when test="@expand='true'">
+ <xsl:message>[preproc] expanding param-copy...</xsl:message>
+ <xsl:apply-templates select="$copy" mode="preproc:apply-template">
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <!-- by default, expansion is not requested, meaning that no template param
+ expansion will take place (on this pass) for the copied nodes; if any
+ template params exist, they will be handled on the next pass -->
+ <xsl:otherwise>
+ <xsl:sequence select="$copy" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="lv:dyn-node" mode="preproc:apply-template" priority="5">
+ <xsl:variable name="dyn-name">
+ <xsl:call-template name="preproc:template-param-value">
+ <xsl:with-param name="name" select="@name" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:element name="{$dyn-name}">
+ <xsl:apply-templates mode="preproc:apply-template" />
+ </xsl:element>
+</xsl:template>
+
+
+<!--
+ Display an arbitrary error to the user during compilation
+
+ This produces a compile-time error and immediately halts
+ compilation.
+-->
+<xsl:template mode="preproc:apply-template" priority="5"
+ match="lv:error">
+ <xsl:param name="apply-tpl-name" as="xs:string"
+ tunnel="yes" />
+
+ <xsl:variable name="message">
+ <xsl:apply-templates mode="preproc:gen-param-value" />
+ </xsl:variable>
+
+ <preproc:error>
+ <xsl:value-of select="$apply-tpl-name" />
+ <xsl:text>: </xsl:text>
+ <xsl:value-of select="normalize-space( $message )" />
+ </preproc:error>
+</xsl:template>
+
+
+<!--
+ Display an arbitrary warning to the user during compilation
+
+ This produces a compile-time warning that is purely informational
+ and does not halt compilation. It is intended to point out
+ undesirable conditions.
+-->
+<xsl:template mode="preproc:apply-template" priority="5"
+ match="lv:warning">
+ <xsl:param name="apply-tpl-name" as="xs:string"
+ tunnel="yes" />
+
+ <xsl:variable name="message">
+ <xsl:apply-templates mode="preproc:gen-param-value" />
+ </xsl:variable>
+
+ <xsl:message>
+ <xsl:text>warning: </xsl:text>
+ <xsl:value-of select="$apply-tpl-name" />
+ <xsl:text>: </xsl:text>
+ <xsl:value-of select="normalize-space( $message )" />
+ </xsl:message>
+</xsl:template>
+
+
+<xsl:template mode="preproc:apply-template" priority="5"
+ match="text()">
+ <xsl:value-of select="." />
+</xsl:template>
+
+
+<!--
+ Basic conditional support within templates
+
+ Simple concept, powerful consequences.
+
+ ***No recursion limit check...TODO
+-->
+<xsl:template match="lv:if|lv:unless" mode="preproc:apply-template" priority="5">
+ <xsl:param name="apply-tpl-name" as="xs:string"
+ tunnel="yes" />
+
+ <xsl:choose>
+ <!-- if the param is unknown, retain; it may be intended for a generated
+ template -->
+ <xsl:when test="
+ not(
+ @name=ancestor::lv:template[
+ @name=$apply-tpl-name
+ ]/lv:param/@name
+ )
+ ">
+ <xsl:copy>
+ <xsl:sequence select="@*" />
+
+ <xsl:apply-templates select="*" mode="preproc:apply-template" />
+ </xsl:copy>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- calculate the param value -->
+ <xsl:variable name="param-value">
+ <xsl:call-template name="preproc:template-param-value">
+ <xsl:with-param name="name" select="@name" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- @value value -->
+ <xsl:variable name="cmp-value">
+ <xsl:call-template name="preproc:template-param-value">
+ <xsl:with-param name="name" select="@*[ not( local-name() = 'name' ) ]" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:apply-templates select="." mode="preproc:apply-template-cmp">
+ <xsl:with-param name="param-value" select="$param-value" />
+ <xsl:with-param name="cmp-value" select="$cmp-value" />
+
+ <xsl:with-param name="negate">
+ <xsl:choose>
+ <xsl:when test="local-name() = 'if'">
+ <xsl:value-of select="false()" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="true()" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:apply-templates>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="lv:*[@eq]" mode="preproc:apply-template-cmp" priority="5">
+ <xsl:param name="negate" select="false()" />
+ <xsl:param name="param-value" />
+ <xsl:param name="cmp-value" />
+
+ <xsl:if test="
+ ( ( $negate = 'true' ) and not( $param-value = $cmp-value ) )
+ or ( ( $negate = 'false' ) and ( $param-value = $cmp-value ) )
+ ">
+
+ <xsl:apply-templates select="*" mode="preproc:apply-template" />
+ </xsl:if>
+</xsl:template>
+
+
+<xsl:template match="lv:*[not(@*[not(local-name()='name')])]" mode="preproc:apply-template-cmp" priority="2">
+ <xsl:param name="negate" select="'false'" />
+ <xsl:param name="param-value" />
+ <xsl:param name="cmp-value" />
+
+ <!-- TODO: This is convoluted...we "know" it's empty because it'll return its
+ own name as a value by default... -->
+ <xsl:if test="
+ ( ( $negate = 'true' ) and ( $param-value = @name ) )
+ or ( ( $negate = 'false' ) and not( $param-value = @name ) )
+ ">
+
+ <xsl:apply-templates select="*" mode="preproc:apply-template" />
+ </xsl:if>
+</xsl:template>
+
+<xsl:template match="lv:*" mode="preproc:apply-template-cmp" priority="1">
+ <preproc:error>
+ <xsl:text>Unknown template comparison attribute for </xsl:text>
+ <xsl:value-of select="name()" />
+ <xsl:text> in </xsl:text>
+ <xsl:value-of select="ancestor::lv:template/@name" />
+ <xsl:text>:</xsl:text>
+
+ <xsl:for-each select="@*">
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="local-name()" />
+ <xsl:text>="</xsl:text>
+ <xsl:value-of select="." />
+ <xsl:text>"</xsl:text>
+ </xsl:for-each>
+ </preproc:error>
+</xsl:template>
+
+
+<xsl:template mode="preproc:gen-param-value" priority="5"
+ match="lv:param-add">
+ <!-- calculate the param value -->
+ <xsl:variable name="param-value">
+ <xsl:call-template name="preproc:template-param-value">
+ <xsl:with-param name="name" select="@name" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:value-of select="number($param-value) + number(@value)" />
+</xsl:template>
+
+
+<!--
+ Process template attributes
+
+ Provides primitive parameter support. If a parameter is found (convention is
+ that it must begin and end with '@', but that convention is not enforced
+ here), it is replaced by the value given by the calling lv:apply-template.
+
+ Note that we currently do not perform any string replacements. As such, we
+ only support full replacement of an attribute (that is, the attribute must
+ contain only a parameter, not a parameter as part of a larger string); it is
+ for this reason that the convention of beginning and ending with '@' arose: it
+ allows us to perform substring replacements later on with clear delimiters.
+
+ TODO: substring replacement for added flexibility
+-->
+<xsl:template match="@*" mode="preproc:apply-template" priority="5">
+ <xsl:variable name="name" select="local-name()" />
+ <xsl:variable name="varname" select="." />
+
+ <!-- compile param value -->
+ <xsl:variable name="value">
+ <xsl:call-template name="preproc:template-param-value">
+ <xsl:with-param name="name" select="$varname" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- if the result is an empty string, then do not output the attribute (this
+ allows for conditional attributes -->
+ <xsl:if test="not( $value = '' )">
+ <xsl:attribute name="{$name}" select="$value" />
+ </xsl:if>
+</xsl:template>
+
+
+<xsl:template match="lv:with-param/@name" mode="preproc:apply-template" priority="9">
+ <!-- do not do any sort of expansion of recursive template param names -->
+ <xsl:copy />
+</xsl:template>
+
+
+<xsl:template name="preproc:template-param-value">
+ <xsl:param name="name" />
+ <xsl:param name="params" as="element( lv:with-param )*"
+ tunnel="yes" />
+
+ <xsl:variable name="after" select="substring-after( $name, '@' )" />
+
+ <!-- TODO: support multiple @vars@ per attribute -->
+ <xsl:choose>
+ <xsl:when test="$name = ''">
+ <!-- we're done; nothing more to do -->
+ </xsl:when>
+
+ <!-- no vars; just output and abort -->
+ <xsl:when test="not( $after )">
+ <xsl:value-of select="$name" />
+ </xsl:when>
+
+ <!-- if the variable is inline, output the value up until the inline
+ delimiter (curly brace); this supports inline vars at any point in the
+ string -->
+ <xsl:when test="not( starts-with( $name, '@' ) )">
+ <!-- this might be nothing (if we start with a curly brace) -->
+ <xsl:value-of select="substring-before( $name, '{' )" />
+
+ <!-- get the param between {} -->
+ <xsl:variable name="param"
+ select="substring-before( substring-after( $name, '{' ), '}' )" />
+
+ <!-- if a param was found, process it -->
+ <xsl:if test="$param">
+ <xsl:variable name="result">
+ <xsl:call-template name="preproc:template-param-value">
+ <xsl:with-param name="name" select="$param" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- if the result is the same as the param name, then it was not found;
+ re-enclose in braces so that it can be processed later (where we
+ may find a value) -->
+ <xsl:choose>
+ <xsl:when test="$result = $param">
+ <xsl:text>{</xsl:text>
+ <xsl:value-of select="$result" />
+ <xsl:text>}</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="$result" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+ <!-- recurse to generate the remainder of the string -->
+ <xsl:call-template name="preproc:template-param-value">
+ <xsl:with-param name="name" select="substring-after( $name, '}' )" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- starts with a macro param -->
+ <xsl:otherwise>
+ <!-- get the param, since we may have additional text following it -->
+
+ <!-- get the value of this parameter, if it exists, from the caller (note
+ below that we will consider a value of the same name as the param to
+ be undefined, allowing defaults to be generated; this is useful when
+ proxying params between templates)-->
+ <xsl:variable name="val"
+ select="$params[ @name=$name ]/@value" />
+
+ <!-- the param node itself -->
+ <xsl:variable name="param"
+ select="ancestor::lv:template/lv:param[ @name=$name ]" />
+
+
+ <xsl:choose>
+ <!-- this test is structured to fail if the param has not been defined
+ on the templates parameter list; this is a restriction imposed
+ purely for documentation; the preprocessor can do just fine without
+ -->
+ <xsl:when test="$val and not( $val = $name ) and $param">
+ <xsl:value-of select="$val" />
+ </xsl:when>
+
+ <!-- otherwise, if we just have the param and it has child nodes,
+ generate a value (in essence: generate a default value if one is
+ not provided) -->
+ <xsl:when test="$param and $param/lv:*">
+ <xsl:apply-templates select="$param/lv:*"
+ mode="preproc:gen-param-value" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="$name" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template mode="preproc:gen-param-value" priority="6"
+ match="lv:param/lv:text[ @unique='true' ]">
+ <xsl:param name="apply" as="node()"
+ tunnel="yes" />
+
+ <xsl:value-of select="." />
+ <xsl:value-of select="generate-id( $apply )" />
+</xsl:template>
+
+
+<xsl:template match="lv:param/lv:text" mode="preproc:gen-param-value"
+ priority="4">
+ <xsl:value-of select="." />
+</xsl:template>
+
+
+<xsl:template mode="preproc:gen-param-value" priority="5"
+ match="lv:param/lv:param-inherit[@meta]">
+ <xsl:param name="apply" as="node()"
+ tunnel="yes" />
+
+ <xsl:variable name="name" select="@meta" />
+
+ <!-- find the metadata -->
+ <xsl:variable name="values"
+ select="$apply/ancestor::*/preceding-sibling::preproc:tpl-meta[ @name=$name ]/@value
+ , $apply/preceding-sibling::preproc:tpl-meta[ @name=$name ]/@value" />
+
+ <!-- take the last one (precedence) -->
+ <xsl:value-of select="$values[ count( $values ) ]" />
+</xsl:template>
+
+
+<xsl:template mode="preproc:gen-param-value" priority="4"
+ match="lv:param-value">
+ <xsl:param name="params" as="element( lv:with-param )*"
+ tunnel="yes" />
+
+ <!-- name of the parameter we're referencing -->
+ <xsl:variable name="pname" as="xs:string"
+ select="@name" />
+
+ <!-- the original string -->
+ <xsl:variable name="str" as="xs:string?"
+ select="$params[ @name=$pname ]/@value" />
+
+ <!-- apply @snake if need be -->
+ <xsl:variable name="processed">
+ <xsl:choose>
+ <!-- snakeify -->
+ <xsl:when test="@snake">
+ <xsl:value-of select="translate( $str, '-', '_' )" />
+ </xsl:when>
+
+ <!-- convert spaces and underscores to dashes -->
+ <xsl:when test="@dash">
+ <xsl:value-of select="translate( $str, '_ ', '--' )" />
+ </xsl:when>
+
+ <xsl:when test="@rmdash">
+ <xsl:value-of select="translate( $str, '-', '' )" />
+ </xsl:when>
+
+ <!-- do nothing -->
+ <xsl:otherwise>
+ <xsl:value-of select="$str" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- perform any additional processing -->
+ <xsl:apply-templates select="." mode="preproc:gen-param-value-style">
+ <xsl:with-param name="str" select="$processed" />
+ </xsl:apply-templates>
+</xsl:template>
+
+<xsl:template match="lv:param-value[@ucfirst]" mode="preproc:gen-param-value-style" priority="5">
+ <xsl:param name="str" />
+
+ <xsl:call-template name="util:ucfirst">
+ <xsl:with-param name="str" select="$str" />
+ </xsl:call-template>
+</xsl:template>
+
+<!-- slightly higher priority than @ucfirst, since this obviously should
+ override -->
+<xsl:template match="lv:param-value[@upper]" mode="preproc:gen-param-value-style" priority="6">
+ <xsl:param name="str" />
+
+ <xsl:call-template name="util:uppercase">
+ <xsl:with-param name="str" select="$str" />
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="lv:param-value[@lower]" mode="preproc:gen-param-value-style" priority="6">
+ <xsl:param name="str" />
+
+ <xsl:call-template name="util:lowercase">
+ <xsl:with-param name="str" select="$str" />
+ </xsl:call-template>
+</xsl:template>
+
+<!-- convert into valid identifier name -->
+<xsl:template match="lv:param-value[@identifier]"
+ mode="preproc:gen-param-value-style" priority="6">
+ <xsl:param name="str" />
+
+ <xsl:sequence select="replace(
+ normalize-unicode( $str, 'NFC' ),
+ '[^a-zA-Z0-9_-]',
+ '' )" />
+</xsl:template>
+
+<!-- no other styling -->
+<xsl:template match="lv:param-value" mode="preproc:gen-param-value-style" priority="1">
+ <xsl:param name="str" />
+ <xsl:value-of select="$str" />
+</xsl:template>
+
+
+<!--
+ Converts class name to its @yields variable name
+-->
+<xsl:template mode="preproc:gen-param-value" priority="5"
+ match="lv:param-class-to-yields">
+ <xsl:param name="params" as="element( lv:with-param )*"
+ tunnel="yes" />
+
+ <xsl:param name="src-root" as="element( lv:package )"
+ select="root(.)"
+ tunnel="yes" />
+
+ <!-- get the class name from the param -->
+ <xsl:variable name="pname" select="@name" />
+ <xsl:variable name="as" select="$params[ @name=$pname ]/@value" />
+
+ <!-- get @yields from class -->
+ <xsl:variable name="yields" select="
+ $src-root/preproc:symtable/preproc:sym[
+ @name=concat( ':class:', $as )
+ ]/@yields
+ " />
+
+ <xsl:choose>
+ <xsl:when test="not( $yields ) or $yields=''">
+ <xsl:message>
+ <xsl:text>error: unable to determine @yields for class `</xsl:text>
+ <xsl:value-of select="$as" />
+ <xsl:text>' (has the class been imported?)</xsl:text>
+ </xsl:message>
+
+ <!-- just retain the name; it'll be used for an error message,
+ since it won't be foudn -->
+ <!-- TODO: this is dangerous; find a way to propagate the error -->
+ <xsl:value-of select="$as" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="$yields" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template mode="preproc:gen-param-value" priority="5"
+ match="text()">
+ <xsl:value-of select="." />
+</xsl:template>
+
+
+<xsl:template mode="preproc:gen-param-value" priority="1"
+ match="node()">
+ <xsl:message terminate="yes">
+ <xsl:text>error: unknown param node content: </xsl:text>
+ <xsl:copy-of select="." />
+ </xsl:message>
+</xsl:template>
+
+
+<!-- Templates should never be expanded until they are actually replaced by
+ macro expansion using apply-templates; just keep ignoring this until it's
+ eventually removed by the expand phase -->
+<xsl:template match="lv:template" mode="preproc:macros" priority="5">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<!--
+ expand-sequence
+
+ TODO: move to a better place
+-->
+<xsl:template mode="preproc:macros" priority="5"
+ match="lv:expand-sequence">
+ <xsl:sequence select="eseq:expand-step( . )" />
+</xsl:template>
+
+
+<!--
+ expand-group is our means of grouping together expressions to be
+ expanded as a group; this is far more efficient than expanding each
+ one individually, when that is unneeded.
+-->
+<xsl:template mode="preproc:macros" priority="5"
+ match="lv:expand-group">
+ <!-- strip expand-group -->
+ <xsl:apply-templates mode="preproc:macros" />
+</xsl:template>
+
+
+
+<xsl:function name="eseq:is-expandable" as="xs:boolean"
+ override="yes">
+ <xsl:param name="node" as="node()" />
+
+ <!-- TODO: what a mess; clean me up by changing the point at which
+ this is processed, which will expand all of these into
+ lv:apply-template -->
+ <xsl:sequence select="$node instance of element()
+ and not( $node instance of
+ element( lv:template ) )
+ and (
+ $node instance of
+ element( lv:apply-template )
+ or $node instance of
+ element( lv:inline-template )
+ or namespace-uri( $node )
+ = namespace-uri-for-prefix( 't', $node )
+ or (
+ $node//lv:inline-template,
+ $node//lv:apply-template,
+ $node//t:* )
+ [ not(
+ ancestor::lv:template
+ or ancestor::lv:apply-template ) ] )" />
+</xsl:function>
+
+
+
+<xsl:function name="eseq:expand-node" as="node()*"
+ override="yes">
+ <xsl:param name="node" as="node()" />
+
+ <xsl:apply-templates mode="preproc:macros"
+ select="$node" />
+</xsl:function>
+
+
+
+<xsl:template mode="preproc:macros" priority="9"
+ match="node()[ not( . instance of element() ) ]">
+ <xsl:sequence select="." />
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/preprocess.xsl b/src/current/include/preprocess.xsl
new file mode 100644
index 0000000..b0d8cd5
--- /dev/null
+++ b/src/current/include/preprocess.xsl
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Preprocesses the XML
+
+ This progress is aggressive; the resulting tree will follow the structure of
+ the original XML, but will be heavily augmented and some parts rewritten.
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ 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:w="http://www.lovullo.com/rater/worksheet"
+ xmlns:ext="http://www.lovullo.com/ext"
+ xmlns:util="http://www.lovullo.com/util">
+
+
+<xsl:include href="depgen.xsl" />
+<xsl:include href="preproc/package.xsl" />
+
+
+<!--
+ Raters themselves get special treatment
+-->
+<xsl:template match="lv:rater" mode="preproc:compile" priority="9">
+ <xsl:param name="orig-root" select="." />
+ <xsl:param name="stopshort" />
+
+ <xsl:message>
+ <xsl:text>[preproc] [rater]</xsl:text>
+ </xsl:message>
+
+ <!-- handle package preprocessing -->
+ <xsl:variable name="pkg-result">
+ <xsl:call-template name="preproc:pkg-compile">
+ <xsl:with-param name="orig-root" select="$orig-root" />
+ <xsl:with-param name="stopshort" select="$stopshort" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:copy-of select="$pkg-result" />
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/include/symbol-map.xml b/src/current/include/symbol-map.xml
new file mode 100644
index 0000000..9cf6421
--- /dev/null
+++ b/src/current/include/symbol-map.xml
@@ -0,0 +1,80 @@
+<symbol-map
+ xmlns="http://www.lovullo.com/rater/symbol-map"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.lovullo.com symbol-map.xsd">
+
+ <!-- global parameter -->
+ <symbol type="param">\Theta</symbol>
+
+ <!-- function parameter -->
+ <symbol type="fparam">\theta</symbol>
+
+ <!-- constant -->
+ <symbol type="const">C</symbol>
+
+ <!-- enum item -->
+ <symbol type="item">E</symbol>
+
+ <symbol type="classify">\kappa</symbol>
+
+ <!-- the class accumulator will accumulate the values returned by any lv:rate
+ that matches a given classification -->
+ <symbol type="class-accumulator">K</symbol>
+
+ <!-- map functions to their own name -->
+ <symbol type="function">
+ <name />
+ </symbol>
+
+ <!-- lv:rate yield (omega to denote "final result") -->
+ <symbol type="rate">\omega</symbol>
+
+ <!-- generating expressions -->
+ <symbol type="generator">G</symbol>
+
+ <symbol type="accumulator">\sigma</symbol>
+
+ <!-- typedefs should have no symbol by default; their plain-text domain can be
+ listed -->
+ <symbol type="typedef">
+ <nothing />
+ </symbol>
+
+
+ <!--
+ Explicit symbol reservations
+
+ Any symbol mentioned within this file will be disallowed from being used in
+ any @sym attribute within raters/packages.
+
+ Note that this list need not contain symbols defined elsewhere in the
+ framework. For example, symbols mentioned in symbol-map need not appear here.
+
+ Do not be overly picky with this list; include only those that are established
+ in the field of mathematics as a whole (not a specific category, unless it is
+ CS), where the symbol is almost always understood to have a particular
+ meaning. For example:
+
+ - We wouldn't want to allow \pi, since that is already established as the
+ ratio of a circle's circumference to its diameter
+ - Nor should we allow \delta, since that is commonly used in CS to represent
+ Kronecker's delta
+ - We should *not*, however, disallow \mu just because it is the Möbius
+ function in number theory
+ -->
+ <reserved>
+ <!-- commonly used as an alterative to Iverson's convention under certain
+ circumstances, so it's best to avoid the notation entirely even if it is
+ not used within the framework -->
+ <reserve sym="\delta" reason="Kronecker delta" />
+
+ <reserve sym="\pi" reason="Ratio of a circle's circumference to its diameter" />
+ <reserve sym="\phi" reason="The golden ratio (another established mathematical constant)" />
+ <reserve sym="\Sigma" reason="Summation symbol; sigma-notation" />
+ <reserve sym="\Pi" reasons="Product symbol; pi-notation" />
+
+ <!-- as common as this is in programming as an index, this is another
+ established mathematical constant (sqrt(-1)) -->
+ <reserve sym="i" reason="Imaginary number" />
+ </reserved>
+</symbol-map>
diff --git a/src/current/include/util.xsl b/src/current/include/util.xsl
new file mode 100644
index 0000000..0e0030f
--- /dev/null
+++ b/src/current/include/util.xsl
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Utility templates
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:util="http://www.lovullo.com/util"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:lvm="http://www.lovullo.com/rater/map"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:ext="http://www.lovullo.com/ext">
+
+
+<xsl:variable name="_chrlower" select="'abcdefghijklmnopqrstuvwxyz'" />
+<xsl:variable name="_chrupper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
+
+<!--
+ Path to map directory
+-->
+<xsl:param name="map-root" select="'../map/'" />
+<xsl:param name="retmap-root" select="concat( $map-root, 'return/' )" />
+
+
+<xsl:template name="util:load-package">
+ <xsl:param name="package" />
+ <xsl:param name="self" />
+
+ <xsl:variable name="path" select="concat($package, '.xml')" />
+ <xsl:variable name="pkg" select="document( $path, $self )/lv:package" />
+
+ <ext:package name="{$pkg/@name}">
+ <!-- path, including extension -->
+ <xsl:attribute name="path">
+ <xsl:value-of select="$path" />
+ </xsl:attribute>
+
+ <!-- path, excluding extension (as it appears in @package) -->
+ <xsl:attribute name="import-path">
+ <xsl:value-of select="@package" />
+ </xsl:attribute>
+
+ <xsl:copy-of select="$pkg" />
+ </ext:package>
+</xsl:template>
+
+
+<xsl:template match="lvm:*" mode="util:map-expand" priority="1">
+ <xsl:param name="path" select="'.'" />
+
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+ <xsl:apply-templates mode="util:map-expand">
+ <xsl:with-param name="path" select="$path" />
+ </xsl:apply-templates>
+ </xsl:copy>
+</xsl:template>
+
+<!-- recursively inline imports -->
+<xsl:template match="lvm:import" mode="util:map-expand" priority="5">
+ <xsl:param name="path" select="'.'" />
+
+ <xsl:apply-templates
+ select="document( concat( @path, '.xml' ), . )/lvm:*/*"
+ mode="util:map-expand">
+
+ <xsl:with-param name="path" select="concat( $path, '/', @path )" />
+ </xsl:apply-templates>
+</xsl:template>
+
+<!-- must be lower priority than lv:import -->
+<xsl:template match="/lvm:*/lvm:*" mode="util:map-expand" priority="3">
+ <xsl:param name="path" select="'.'" />
+
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+ <xsl:attribute name="__src" select="$path" />
+
+ <xsl:apply-templates mode="util:map-expand">
+ <xsl:with-param name="path" select="$path" />
+ </xsl:apply-templates>
+ </xsl:copy>
+</xsl:template>
+
+
+<!--
+ Converts first character to uppercase
+
+ Functions like ucfirst in PHP
+
+ @param string str string to ucfirst
+
+ @return string provided string with first character converted to uppercase
+-->
+<xsl:template name="util:ucfirst">
+ <xsl:param name="str" />
+
+ <!-- convert first char to uppercase -->
+ <xsl:value-of
+ select="translate( substring( $str, 1, 1 ), $_chrlower, $_chrupper )" />
+
+ <!-- remainder of string as it was provided -->
+ <xsl:value-of select="substring( $str, 2 )" />
+</xsl:template>
+
+
+<!--
+ Converts a string to uppercase
+-->
+<xsl:template name="util:uppercase">
+ <xsl:param name="str" />
+ <xsl:value-of select="translate( $str, $_chrlower, $_chrupper )" />
+</xsl:template>
+
+<!--
+ Converts a string to lowercase
+-->
+<xsl:template name="util:lowercase">
+ <xsl:param name="str" />
+ <xsl:value-of select="translate( $str, $_chrupper, $_chrlower )" />
+</xsl:template>
+
+
+<xsl:template name="util:json">
+ <xsl:param name="id" />
+ <xsl:param name="value" />
+ <xsl:param name="obj" />
+ <xsl:param name="array" />
+
+ <xsl:if test="$id">
+ <xsl:text>"</xsl:text>
+ <xsl:call-template name="util:json-escape">
+ <xsl:with-param name="string" select="$id" />
+ </xsl:call-template>
+ <xsl:text>":</xsl:text>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="$array">
+ <xsl:text>[</xsl:text>
+ <xsl:for-each select="$array/*">
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="." />
+ </xsl:for-each>
+ <xsl:text>]</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$obj">
+ <xsl:text>{</xsl:text>
+ <xsl:for-each select="$obj/*">
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="." />
+ </xsl:for-each>
+ <xsl:text>}</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$value">
+ <xsl:text>"</xsl:text>
+ <xsl:call-template name="util:json-escape">
+ <xsl:with-param name="string" select="$value" />
+ </xsl:call-template>
+ <xsl:text>"</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:message terminate="yes">[util] !!! invalid util:json</xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="util:json-escape">
+ <xsl:param name="string" />
+
+ <xsl:value-of select="$string" />
+</xsl:template>
+
+</xsl:stylesheet>