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
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.)
-rw-r--r--Makefile.am4
-rw-r--r--README.md7
-rw-r--r--doc/Makefile.am4
-rw-r--r--src/current/.gitignore1
-rw-r--r--src/current/Makefile8
-rw-r--r--src/current/c1map.xsl383
-rw-r--r--src/current/c1map/c1nodes.xsl95
-rw-r--r--src/current/c1map/render.xsl339
-rw-r--r--src/current/c1map/transform.xsl53
-rw-r--r--src/current/c1map/valparse.xsl130
-rw-r--r--src/current/calc.xsd883
-rw-r--r--src/current/compile.xsl101
-rw-r--r--src/current/compiler/fragments.xsl137
-rw-r--r--src/current/compiler/js-calc.xsl1141
-rw-r--r--src/current/compiler/js.xsl1908
-rw-r--r--src/current/compiler/linker.xsl1442
-rw-r--r--src/current/compiler/linker/log.xsl95
-rw-r--r--src/current/compiler/map.xsl997
-rw-r--r--src/current/compiler/validate.xsl771
-rw-r--r--src/current/compiler/validate/domain.xsl193
-rw-r--r--src/current/compiler/validate/param.xsl103
-rw-r--r--src/current/compiler/worksheet.xsl543
-rw-r--r--src/current/doc/.gitignore4
-rw-r--r--src/current/doc/chapters/class.tex372
-rw-r--r--src/current/doc/manual.sty30
-rw-r--r--src/current/doc/manual.tex19
-rw-r--r--src/current/dot.xsl65
-rw-r--r--src/current/dot/attr-color.xsl72
-rw-r--r--src/current/dot/attr-extern.xsl33
-rw-r--r--src/current/dot/attr-keep.xsl33
-rw-r--r--src/current/dot/attr-shape.xsl62
-rw-r--r--src/current/dot/defnode-attr.xsl70
-rw-r--r--src/current/dot/defnode.xsl107
-rw-r--r--src/current/dot/depout.xsl212
-rw-r--r--src/current/dot/pkg-exec.xsl99
-rw-r--r--src/current/dot/pkg-obj.xsl49
-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
-rw-r--r--src/current/link.xsl8
-rw-r--r--src/current/map.xsd412
-rw-r--r--src/current/pkg-dep.xsl56
-rw-r--r--src/current/scripts/entry-form.js2164
-rw-r--r--src/current/src/.gitignore6
-rw-r--r--src/current/src/Makefile18
-rw-r--r--src/current/src/com/lovullo/dslc/DslCompiler.java303
-rw-r--r--src/current/standalone.xsl226
-rw-r--r--src/current/summary.css835
-rw-r--r--src/current/summary.xsl2107
-rwxr-xr-xsrc/current/tools/csv2xml110
-rwxr-xr-xsrc/current/tools/csvi124
-rwxr-xr-xsrc/current/tools/csvm2csv112
-rwxr-xr-xsrc/current/tools/gen-make96
-rw-r--r--src/current/tools/lib/zipre.php124
-rwxr-xr-xsrc/current/tools/tdat2xml274
-rwxr-xr-xsrc/current/tools/zipre23
-rw-r--r--src/current/worksheet.xsd34
72 files changed, 25223 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 90090f1..e632d54 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -22,7 +22,9 @@ path_src = src
path_test = test
# all source files will be run through hoxsl; see `applies' target
-apply_src := $(shell find "$(path_src)" "$(path_test)" -name '*.xsl')
+apply_src := $(shell find "$(path_src)" "$(path_test)" \
+ -name '*.xsl' \
+ -a \! -path "$(path_src)"/current/\* )
apply_dest := $(apply_src:%.xsl=%.xsl.apply)
# needed by test runner
diff --git a/README.md b/README.md
index 3a5dbdb..6b56241 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,13 @@ TAME's core library, and [hoxsl](https://github.com/lovullo/hoxsl) was
developed as a supporting library.
+## "Current"
+The current state of the project as used in production is found in
+`src/current/`. The environment surrounding the development of this
+project resulted in a bit of a mess, which is being refactored into
+`src/` as it is touched. Documentation is virtually non-existent.
+
+
## License
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 6bd1f0c..889824b 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -21,7 +21,9 @@
path_src := ../src
path_tools := ../tools
-stylesheets := $(shell find "$(path_src)" -name '*.xsl')
+stylesheets := $(shell find "$(path_src)" \
+ -name '*.xsl' \
+ -a \! -path "$(path_src)"/current/\* )
stexi := $(stylesheets:.xsl=.texi)
info_TEXINFOS = tame.texi
diff --git a/src/current/.gitignore b/src/current/.gitignore
new file mode 100644
index 0000000..2460008
--- /dev/null
+++ b/src/current/.gitignore
@@ -0,0 +1 @@
+!Makefile
diff --git a/src/current/Makefile b/src/current/Makefile
new file mode 100644
index 0000000..dc6eb54
--- /dev/null
+++ b/src/current/Makefile
@@ -0,0 +1,8 @@
+
+.PHONY: dslc clean
+
+dslc:
+ $(MAKE) -C src/ dslc
+
+clean:
+ $(MAKE) -C src/ clean
diff --git a/src/current/c1map.xsl b/src/current/c1map.xsl
new file mode 100644
index 0000000..660fdf7
--- /dev/null
+++ b/src/current/c1map.xsl
@@ -0,0 +1,383 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Generates PHP code that works with the LoVullo ConceptOne import system
+
+ This map expects that the data are available in the bucket provided by the
+ quote and therefore validates against a provided Program UI source file. Data
+ external to the bucket may be provided if it is indicated as such.
+
+ Each map source file is independent; variables and values do not bleed into
+ one-another, unless explicitly passed.
+-->
+<xsl:stylesheet version="2.0"
+ xmlns:c1="http://www.epic-premier.com/XMLSchema"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lvm="http://www.lovullo.com/rater/map/c1"
+ xmlns:lvmp="http://www.lovullo.com/rater/map/c1/pp">
+
+<xsl:output
+ indent="yes"
+ omit-xml-declaration="yes"
+ />
+
+<!-- newline -->
+<xsl:variable name="lvmp:nl" select="'&#10;'" />
+
+<xsl:include href="c1map/c1nodes.xsl" />
+<xsl:include href="c1map/valparse.xsl" />
+<xsl:include href="c1map/render.xsl" />
+
+<!--
+ Represents the root of the source document that processing was initiated upon
+-->
+<xsl:variable name="orig-root" select="/" />
+
+
+<!--
+ The root node
+-->
+<xsl:template match="lvm:c1-map" priority="5">
+ <!-- populated by includes, if any -->
+ <xsl:param name="args" />
+
+ <xsl:message select="$args" />
+
+ <!-- get the name from the first C1 node -->
+ <xsl:variable name="name" select="
+ if ( @id ) then
+ @id
+ else
+ concat( translate( c1:*[1]/name(), '_', '' ), 'Composer' )
+ " />
+
+ <!-- preprocessed result -->
+ <xsl:variable name="pp-result">
+ <lvmp:root program="{@program}" name="{$name}">
+ <!-- introduce outer scope for variables -->
+ <lvmp:scope id="">
+ <xsl:apply-templates />
+ </lvmp:scope>
+ </lvmp:root>
+ </xsl:variable>
+
+ <!-- final processing -->
+ <xsl:variable name="result">
+ <xsl:apply-templates select="$pp-result/lvmp:root" mode="lvmp:render" />
+ </xsl:variable>
+
+ <!-- remove escapes -->
+ <xsl:value-of disable-output-escaping="yes" select="$result" />
+</xsl:template>
+
+
+<!--
+ Include another source map file relative to the path of the original source
+ file
+
+ The attributes @name and @for-each are special and cannot be used as
+ arguments to the template. The special @for-each attribute will be copied
+ into each of the children of the root node in the template as @lvm:for-each.
+ For example, if the template consists of
+
+ <c1-map>
+ <Product>
+ </Product>
+ </c1-map>
+
+ then <lvm:include for-each="foo" /> would produce
+
+ <c1-map>
+ <Product lvm:for-each="foo">
+ </Product>
+ </c1-map>
+-->
+<xsl:template match="lvm:include" priority="5">
+ <xsl:message>[c1map] +<xsl:value-of select="@name" /></xsl:message>
+
+ <xsl:variable name="src" select="
+ document( concat( @name, '.xml' ), $orig-root )/lvm:c1-map
+ " />
+
+ <xsl:if test="not( $src )">
+ <xsl:message terminate="yes">fatal: c1-map node not found</xsl:message>
+ </xsl:if>
+
+ <!-- process the body of the c1-map; we don't want to process the root node,
+ as that would start processing from scratch, prematurely rendering the result -->
+ <lvmp:scope id="/{@name}">
+ <!-- arguments are included as attributes -->
+ <xsl:variable name="args" select="
+ @*[ not( local-name()='name' or local-name()='for-each' ) ]
+ " />
+ <xsl:variable name="for-each" select="@for-each" />
+
+ <!-- augment the XML with our own mappings -->
+ <xsl:variable name="augmented">
+ <lvm:c1-map>
+ <xsl:copy-of select="$src/@*" />
+
+ <xsl:for-each select="$src/lvm:param">
+ <xsl:call-template name="lvmp:param-to-map">
+ <xsl:with-param name="args" select="$args" />
+ <xsl:with-param name="param" select="." />
+ <xsl:with-param name="context" select="/lvm:c1-map" />
+ </xsl:call-template>
+ </xsl:for-each>
+
+
+ <xsl:choose>
+ <!-- if @for-each was provided, then apply to all first-level
+ children of the included template -->
+ <xsl:when test="$for-each">
+ <!-- we will need to expose the mapping -->
+ <!-- TODO: need to validate that it actually exists -->
+ <lvm:external name="{$for-each}" />
+
+ <xsl:for-each select="$src/*">
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+ <xsl:attribute name="lvm:for-each" select="$for-each" />
+ <xsl:copy-of select="*|text()" />
+ </xsl:copy>
+ </xsl:for-each>
+ </xsl:when>
+
+ <!-- no @for-each; just do a quick copy of all the nodes -->
+ <xsl:otherwise>
+ <xsl:copy-of select="$src/*" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </lvm:c1-map>
+ </xsl:variable>
+
+ <xsl:apply-templates select="$augmented/lvm:c1-map/*" />
+ </lvmp:scope>
+
+ <xsl:message>[c1map] -<xsl:value-of select="@name" /></xsl:message>
+</xsl:template>
+
+
+<!--
+ Processes a template param into mappings
+
+ This will generate the mappings necessary to process the template as though
+ it was hard-coded with the imported mappings.
+
+ The {} brace syntax denotes a variable, but mixing values and inline
+ variables are not supported.
+-->
+<xsl:template name="lvmp:param-to-map">
+ <xsl:param name="args" />
+ <xsl:param name="param" />
+ <xsl:param name="context" />
+
+ <xsl:variable name="name" select="$param/@name" />
+ <xsl:variable name="arg" select="$args[ local-name()=$name ]" />
+ <xsl:variable name="argvar" select="substring-after( $arg, '{' )" />
+
+ <xsl:if test="$argvar and not( $argvar='' )">
+ <xsl:variable name="varname" select="substring-before( $argvar, '}' )" />
+
+ <lvmp:translate name="{$name}" to="{$varname}" />
+
+ <xsl:variable name="predot" select="substring-before( $varname, '.' )" />
+
+ <xsl:choose>
+ <!-- no dot; output the entire thing -->
+ <xsl:when test="$predot = ''">
+ <lvm:external name="{$varname}" />
+ </xsl:when>
+
+ <!-- multi-level var -->
+ <xsl:otherwise>
+ <lvm:external name="{$predot}" dict="true" lvmp:no-validate="true" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+ <!-- TODO: no need to do this if the above conditional matches -->
+ <lvm:map to="{$name}" lvmp:allow-default="true">
+ <xsl:copy-of select="$param/@dict" />
+ <xsl:copy-of select="$param/@default" />
+
+ <xsl:choose>
+ <xsl:when test="$arg">
+ <!-- determines if we have a variable -->
+
+ <xsl:choose>
+ <xsl:when test="$argvar and not( $argvar='' )">
+ <xsl:attribute name="from"
+ select="substring-before( $argvar, '}' )" />
+ </xsl:when>
+
+ <!-- static value -->
+ <xsl:otherwise>
+ <xsl:attribute name="value" select="$arg" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <!-- required param -->
+ <xsl:when test="$param/@required">
+ <xsl:message terminate="yes">
+ <xsl:text>error: missing required template argument `</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:when>
+
+ <!-- otherwise, we have no value -->
+ <xsl:otherwise>
+ <xsl:attribute name="value" select="''" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </lvm:map>
+</xsl:template>
+
+
+<!--
+ Common actions performed by nearly every mapping
+-->
+<xsl:template name="lvmp:map-common">
+ <!-- may or may not be set -->
+ <xsl:copy-of select="@dict" />
+ <xsl:copy-of select="@link" />
+ <xsl:copy-of select="@transform" />
+
+ <xsl:apply-templates select="@default" />
+</xsl:template>
+
+
+<xsl:template match="lvm:param" priority="4">
+ <!-- processed above on import; no longer needed -->
+</xsl:template>
+
+<xsl:template match="lvm:map[@from]" priority="4">
+ <lvmp:var name="{@to}" from="{@from}" src="map">
+ <xsl:call-template name="lvmp:map-common" />
+ </lvmp:var>
+</xsl:template>
+
+<xsl:template match="lvm:map[lvm:from]" priority="4">
+ <lvmp:var name="{@to}" from="{lvm:from/@name}">
+ <xsl:call-template name="lvmp:map-common" />
+ </lvmp:var>
+</xsl:template>
+
+<xsl:template match="lvm:map[@value]" priority="4">
+ <!-- it does not make sense to have a string value be a dictionary -->
+ <xsl:if test="@dict">
+ <xsl:message terminate="yes">
+ <xsl:text>error: cannot have @dict on static mapping `</xsl:text>
+ <xsl:value-of select="@to" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <!-- nor does a default make sense -->
+ <xsl:if test="@default and not( @lvmp:allow-default='true' )">
+ <xsl:message terminate="yes">
+ <xsl:text>error: cannot have @default on static mapping `</xsl:text>
+ <xsl:value-of select="@to" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <lvmp:var name="{@to}" value="{@value}">
+ <!-- we may use defaults internally -->
+ <xsl:call-template name="lvmp:map-common" />
+ </lvmp:var>
+</xsl:template>
+
+<xsl:template match="lvm:pass" priority="4">
+ <lvmp:var name="{@name}" from="{@name}" src="map">
+ <xsl:call-template name="lvmp:map-common" />
+ </lvmp:var>
+</xsl:template>
+
+<xsl:template match="lvm:external" priority="4">
+ <lvmp:var name="{@name}" from="{@name}" src="external">
+ <xsl:call-template name="lvmp:map-common" />
+ </lvmp:var>
+</xsl:template>
+
+<xsl:template match="lvm:*/@default">
+ <lvmp:default>
+ <xsl:apply-templates select="." mode="lvm:valparse" />
+ </lvmp:default>
+</xsl:template>
+
+
+<xsl:template match="lvmp:translate" priority="4">
+ <!-- added by pre-processor during include; ignore -->
+</xsl:template>
+
+
+<!--
+ Override default behavior of c1 nodes when iteration is requested
+-->
+<xsl:template match="c1:*[ @lvm:for-each ]"
+ mode="lvmp:c1-node-result" priority="5">
+
+ <lvmp:for-each name="{@lvm:for-each}">
+ <!-- proceed with processing as normal -->
+ <xsl:apply-templates select="@*|*" />
+ </lvmp:for-each>
+</xsl:template>
+
+
+<xsl:template match="lvm:if" priority="4">
+ <lvmp:condition>
+ <lvmp:when>
+ <xsl:call-template name="lvmp:gen-val">
+ <xsl:with-param name="name" select="@name" />
+ </xsl:call-template>
+ </lvmp:when>
+
+ <xsl:apply-templates />
+ </lvmp:condition>
+</xsl:template>
+
+
+<!--
+ Unhandled node character data
+
+ Note that, if a node contains newlines, then there will be text preceding and
+ following its children. For example:
+
+ <foo>
+ <bar>
+ </foo>
+
+ In the above, the `foo' node has the text "\n ", followed by the node `bar',
+ followed by the text "\n" (assuming that `foo' starts in column 1).
+-->
+<xsl:template match="text()" priority="1">
+ <!-- do not output whitespace from source files -->
+</xsl:template>
+
+
+<!--
+ Bail out on unhandled nodes.
+-->
+<xsl:template match="*" priority="1">
+ <xsl:message>
+ <xsl:text>[c1map] fatal: unexpected node </xsl:text>
+ <xsl:apply-templates select="." mode="lvmp:node-out" />
+ <xsl:text>:</xsl:text>
+ </xsl:message>
+
+ <xsl:message terminate="yes" select="." />
+</xsl:template>
+
+<xsl:template match="*" mode="lvmp:node-out">
+ <xsl:variable name="parent" select="parent::*" />
+ <xsl:if test="$parent">
+ <xsl:apply-templates select="$parent" mode="lvmp:node-out" />
+ </xsl:if>
+
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="name()" />
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/c1map/c1nodes.xsl b/src/current/c1map/c1nodes.xsl
new file mode 100644
index 0000000..da4470f
--- /dev/null
+++ b/src/current/c1map/c1nodes.xsl
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Describes how ConceptOne nodes are handled in the output.
+
+ Only nodes in the C1 XML namespace will be included in the output; all other
+ nodes will be in error, except for nodes as part of the c1 map namespace,
+ which are processed and will not be included in the output.
+
+ The output is an array format used to generate the final XML at runtime; this
+ format was not developed in conjunction with this project and is separate, so
+ be sure that this compiler is updated if the format changes.
+-->
+<xsl:stylesheet version="2.0"
+ xmlns:c1="http://www.epic-premier.com/XMLSchema"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lvm="http://www.lovullo.com/rater/map/c1"
+ xmlns:lvmp="http://www.lovullo.com/rater/map/c1/pp">
+
+<!--
+ Nodes with attributes or children are recursively processed and have the
+ form:
+ '>Name' => array( <recurse> )
+-->
+<xsl:template match="c1:*[*|@*]" priority="5">
+ <!-- make the output a little bit sane -->
+ <xsl:value-of select="$lvmp:nl" />
+
+ <!-- defer node rendering; allows us to easily determine if there are
+ siblings of the same name within a node boundary -->
+ <lvmp:node name="{name()}" />
+ <xsl:text> => </xsl:text>
+
+ <lvmp:node-boundary>
+ <xsl:apply-templates select="." mode="lvmp:c1-node-result" />
+ </lvmp:node-boundary>
+</xsl:template>
+
+
+<!--
+ The default behavior of c1 nodes is to simply output the nodes as-is, with
+ variable substitutions.
+-->
+<xsl:template match="c1:*" mode="lvmp:c1-node-result" priority="1">
+ <xsl:text>array( </xsl:text>
+ <xsl:apply-templates select="@*|*" />
+ <xsl:text>) </xsl:text>
+</xsl:template>
+
+
+<!--
+ Text-only nodes are of the form:
+ '>Name' => 'value'
+-->
+<xsl:template match="c1:*[text()]" priority="4">
+ <!-- defer node rendering; allows us to easily determine if there are
+ siblings of the same name within a node boundary -->
+ <lvmp:node name="{name()}" />
+ <xsl:text> => </xsl:text>
+
+ <xsl:text></xsl:text>
+ <!-- TODO: escape single quotes -->
+ <xsl:apply-templates select="text()" mode="lvm:valparse" />
+ <xsl:text>, </xsl:text>
+</xsl:template>
+
+
+<!--
+ Attributes are of the format:
+ '[Name]' => 'value'
+-->
+<xsl:template match="c1:*/@*" priority="5">
+ <xsl:text>'[</xsl:text>
+ <xsl:value-of select="name()" />
+ <xsl:text>]' => </xsl:text>
+ <xsl:apply-templates select="." mode="lvm:valparse" />
+ <xsl:text>, </xsl:text>
+</xsl:template>
+
+
+<!-- alternative attribute format for special situations -->
+<xsl:template match="lvm:attribute" priority="5">
+ <xsl:text>'[</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>]' => </xsl:text>
+ <xsl:apply-templates select="@value" mode="lvm:valparse" />
+ <xsl:text>, </xsl:text>
+</xsl:template>
+
+
+<xsl:template match="c1:*/@lvm:*" priority="6">
+ <!-- discard all system attributes -->
+ <!-- TODO: error once everything is properly implemented -->
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/c1map/render.xsl b/src/current/c1map/render.xsl
new file mode 100644
index 0000000..c782a35
--- /dev/null
+++ b/src/current/c1map/render.xsl
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Renders the final PHP code
+-->
+<xsl:stylesheet version="2.0"
+ xmlns:c1="http://www.epic-premier.com/XMLSchema"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lvm="http://www.lovullo.com/rater/map/c1"
+ xmlns:lvmp="http://www.lovullo.com/rater/map/c1/pp">
+
+
+<xsl:import href="transform.xsl" />
+
+
+<xsl:template match="lvmp:root" mode="lvmp:render" priority="5">
+ <xsl:text>&lt;?php </xsl:text>
+ <xsl:value-of select="$lvmp:nl" />
+
+ <!-- TODO: add program id to namespace -->
+ <xsl:text>namespace lovullo\c1\interfaces\c1\contract\</xsl:text>
+ <xsl:value-of select="@program" />
+ <xsl:text>;</xsl:text>
+ <xsl:value-of select="$lvmp:nl" />
+
+ <xsl:text>class </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text> {</xsl:text>
+ <xsl:value-of select="$lvmp:nl" />
+
+ <xsl:text>public function compose( $contract ) {</xsl:text>
+ <xsl:value-of select="$lvmp:nl" />
+ <xsl:text> return array(</xsl:text>
+ <xsl:value-of select="$lvmp:nl" />
+ <!-- render the preprocessed content -->
+ <xsl:apply-templates mode="lvmp:render" />
+ <xsl:value-of select="$lvmp:nl" />
+ <xsl:text> );</xsl:text>
+ <xsl:value-of select="$lvmp:nl" />
+ <xsl:text> }</xsl:text>
+ <xsl:value-of select="$lvmp:nl" />
+
+ <xsl:text>}</xsl:text>
+ <xsl:value-of select="$lvmp:nl" />
+ <xsl:text>?&gt;</xsl:text>
+</xsl:template>
+
+
+<xsl:template match="text()" mode="lvmp:render" priority="5">
+ <xsl:value-of select="." />
+</xsl:template>
+
+
+<xsl:template name="lvmp:value" match="lvmp:value" mode="lvmp:render" priority="5">
+ <xsl:param name="name" select="@ref" />
+ <xsl:param name="scope" select="ancestor::lvmp:scope[1]" />
+ <xsl:param name="var" select="$scope/lvmp:var[ @name=$name ][1]" />
+ <xsl:param name="from" select="$var/@from" />
+ <xsl:param name="value" select="$var/@value" />
+ <xsl:param name="default" select="$var/lvmp:default" />
+
+ <!-- provide error if the variable could not be found in the current scope -->
+ <xsl:call-template name="lvmp:check-var">
+ <xsl:with-param name="name" select="$name" />
+ <xsl:with-param name="scope" select="$scope" />
+ </xsl:call-template>
+
+ <xsl:choose>
+ <!-- mapping was provided -->
+ <xsl:when test="$from and not( $from='' )">
+ <xsl:apply-templates select="$var" mode="lvmp:transform">
+ <xsl:with-param name="value">
+ <xsl:apply-templates select="." mode="lvmp:render-value">
+ <xsl:with-param name="var" select="$var" />
+ <xsl:with-param name="from" select="$from" />
+ <xsl:with-param name="scope" select="$scope" />
+ <xsl:with-param name="default" select="$default" />
+ </xsl:apply-templates>
+ </xsl:with-param>
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <!-- static string mapping -->
+ <xsl:when test="$value and not( $value='' )">
+ <!-- TODO: escape single quote -->
+ <xsl:text>'</xsl:text>
+ <xsl:value-of select="$value" />
+ <xsl:text>'</xsl:text>
+ </xsl:when>
+
+ <!-- if no @from was provided, then it forces the use of the default
+ value -->
+ <xsl:when test="$default">
+ <xsl:text>$contract-&gt;checkDefaultValue( '</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>', </xsl:text>
+ <xsl:apply-templates select="$default" mode="lvmp:render" />
+ <xsl:text> )</xsl:text>
+ </xsl:when>
+
+ <!-- if no default is available, then this node shall never be set -->
+ <xsl:otherwise>
+ <xsl:text>null</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template mode="lvmp:render" priority="7"
+ match="lvmp:value[ lvmp:value ]">
+
+ <xsl:param name="name" select="@ref" />
+ <xsl:param name="scope" select="ancestor::lvmp:scope[1]" />
+ <xsl:param name="var" select="$scope/lvmp:var[ @name=$name ][1]" />
+ <xsl:param name="from" select="$var/@from" />
+ <xsl:param name="value" select="$var/@value" />
+ <xsl:param name="default" select="$var/lvmp:default" />
+
+ <xsl:text>$contract-&gt;getValueByContext( </xsl:text>
+ <!-- recursive process contexts -->
+ <xsl:apply-templates select="lvmp:value" mode="lvmp:render">
+ <xsl:with-param name="scope" select="$scope" />
+ <xsl:with-param name="default" select="$default" />
+ </xsl:apply-templates>
+ <xsl:text>, '</xsl:text>
+ <!-- TODO: validate existence of key in dict -->
+ <xsl:value-of select="$name" />
+ <xsl:text>' </xsl:text>
+
+ <xsl:text> )</xsl:text>
+</xsl:template>
+
+
+<xsl:template match="lvmp:value" mode="lvmp:render-value" priority="1">
+ <xsl:param name="var" />
+ <xsl:param name="scope" />
+ <xsl:param name="default" />
+ <xsl:param name="from" />
+ <!-- optional -->
+ <xsl:param name="value-node" select="." />
+ <xsl:param name="context" />
+
+ <!-- the name of the index we reference for our value defaults to our own
+ name, but can be overridden in the mapping -->
+ <xsl:param name="index-name">
+ <xsl:choose>
+ <!-- link provided; override -->
+ <xsl:when test="$var/@link">
+ <!-- parse the link and replace it with the var it maps to -->
+ <xsl:call-template name="lvmp:var-from">
+ <xsl:with-param name="name" select="$var/@link" />
+ <xsl:with-param name="scope" select="$scope" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- default to our own index -->
+ <xsl:otherwise>
+ <xsl:value-of select="$from" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:param>
+
+ <xsl:text>$contract-&gt;getValue( '</xsl:text>
+ <xsl:value-of select="$from" />
+ <xsl:text>', $contract->getValueIndex( '</xsl:text>
+ <xsl:value-of select="$index-name" />
+ <xsl:text>' )</xsl:text>
+
+ <xsl:if test="$default">
+ <xsl:text>, </xsl:text>
+ <xsl:apply-templates select="$default" mode="lvmp:render" />
+ </xsl:if>
+
+ <xsl:text> )</xsl:text>
+</xsl:template>
+
+
+<!--
+ Generates an error in the event that the given var name cannot be found in
+ the given scope.
+-->
+<xsl:template name="lvmp:check-var">
+ <xsl:param name="name" />
+ <xsl:param name="scope" select="ancestor::lvmp:scope[1]" />
+
+ <!-- look up variable in parent scope -->
+ <xsl:variable name="var" select="$scope/lvmp:var[ @name=$name ][1]" />
+
+ <!-- provide error if the variable could not be found in the current scope -->
+ <xsl:if test="not( $var )">
+ <xsl:message terminate="yes">
+ <xsl:text>error: variable not within scope: </xsl:text>
+ <xsl:value-of select="$scope/@id" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="$name" />
+ </xsl:message>
+ </xsl:if>
+</xsl:template>
+
+
+<!--
+ Validates and then returns the source mapping of the given variable within
+ the given (or current) scope
+-->
+<xsl:template name="lvmp:var-from">
+ <xsl:param name="name" />
+ <xsl:param name="scope" select="ancestor::lvmp:scope[1]" />
+
+ <!-- undefined/scoping check -->
+ <xsl:call-template name="lvmp:check-var">
+ <xsl:with-param name="name" select="$name" />
+ <xsl:with-param name="scope" select="$scope" />
+ </xsl:call-template>
+
+ <xsl:value-of select="$scope/lvmp:var[ @name=$name ][1]/@from" />
+</xsl:template>
+
+
+<xsl:template match="lvmp:translate" mode="lvmp:render" priority="5">
+ <!-- no longer needed -->
+</xsl:template>
+
+<xsl:template match="lvmp:var" mode="lvmp:render" priority="5">
+ <!-- no longer needed -->
+</xsl:template>
+
+<xsl:template match="lvmp:var/lvmp:default" mode="lvmp:render" priority="5">
+ <!-- render contents -->
+ <xsl:apply-templates mode="lvmp:render" />
+</xsl:template>
+
+
+<!--
+ Scope boundary; no longer needed in output
+-->
+<xsl:template match="lvmp:scope" mode="lvmp:render" priority="2">
+ <xsl:apply-templates mode="lvmp:render" />
+</xsl:template>
+
+
+<!--
+ Conditional boundary; not needed in output
+-->
+<xsl:template match="lvmp:condition" mode="lvmp:render" priority="2">
+ <xsl:apply-templates mode="lvmp:render" />
+</xsl:template>
+
+<xsl:template match="lvmp:condition/lvmp:when" mode="lvmp:render" priority="2">
+ <!-- will be processed as part of sibling output -->
+</xsl:template>
+
+
+<!--
+ Iteration boundary
+-->
+<xsl:template match="lvmp:for-each" mode="lvmp:render" priority="5">
+ <xsl:variable name="from">
+ <xsl:call-template name="lvmp:var-from">
+ <xsl:with-param name="name" select="@name" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:text>$contract->iterateValues( '</xsl:text>
+ <xsl:value-of select="$from" />
+ <xsl:text>', </xsl:text>
+ <xsl:text>function( $contract ) {</xsl:text>
+ <xsl:text> return array(</xsl:text>
+ <xsl:apply-templates mode="lvmp:render" />
+ <xsl:text>);</xsl:text>
+ <xsl:text>}</xsl:text>
+ <xsl:text>)</xsl:text>
+</xsl:template>
+
+
+<!--
+ The C1 import system recognizes the following formats:
+
+ '>Node'
+ 'N>Node'
+
+ where N is any number; the latter is used for creating unique indexes for
+ multiple siblings of the same name.
+-->
+<xsl:template match="lvmp:node" mode="lvmp:render" priority="5">
+ <xsl:variable name="name" select="@name" />
+
+ <xsl:text>'</xsl:text>
+
+ <!-- generate a unique key to avoid conflicts -->
+ <xsl:value-of select="generate-id(.)" />
+
+ <xsl:text>&gt;</xsl:text>
+ <xsl:value-of select="@name" />
+
+ <xsl:text>'</xsl:text>
+</xsl:template>
+
+
+<!--
+ Node boundary; not needed in output
+
+ The boundary dictates the context of a conditional. This template has a lower
+ priority and will be the default if there is no parent conditional.
+-->
+<xsl:template match="lvmp:node-boundary" mode="lvmp:render" priority="2">
+ <xsl:apply-templates mode="lvmp:render" />
+
+ <!-- unconditional delimiter; nothing funky going on -->
+ <xsl:text>, </xsl:text>
+</xsl:template>
+
+<xsl:template mode="lvmp:render" priority="5" match="
+ lvmp:node-boundary[
+ parent::lvmp:condition
+ or parent::lvmp:scope/parent::lvmp:condition
+ ]
+">
+ <!-- select the nearest condition -->
+ <xsl:variable name="cond" select="ancestor::lvmp:condition[1]" />
+
+ <xsl:text>( ( </xsl:text>
+ <xsl:text>$contract->isTruthy( </xsl:text>
+ <xsl:apply-templates select="$cond/lvmp:when/lvmp:*" mode="lvmp:render" />
+ <xsl:text>)</xsl:text>
+ <xsl:text> ) ? </xsl:text>
+ <xsl:apply-templates mode="lvmp:render">
+ <xsl:with-param name="no-trailing-sep" select="true()" />
+ </xsl:apply-templates>
+ <xsl:text> : null ), </xsl:text>
+</xsl:template>
+
+
+<xsl:template match="*" mode="lvmp:render" priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>[c1map] fatal: unexpected node during render: </xsl:text>
+ <xsl:apply-templates select="." mode="lvmp:node-out" />
+ </xsl:message>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/c1map/transform.xsl b/src/current/c1map/transform.xsl
new file mode 100644
index 0000000..8793648
--- /dev/null
+++ b/src/current/c1map/transform.xsl
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Transforms values
+-->
+<xsl:stylesheet version="2.0"
+ xmlns:c1="http://www.epic-premier.com/XMLSchema"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lvm="http://www.lovullo.com/rater/map/c1"
+ xmlns:lvmp="http://www.lovullo.com/rater/map/c1/pp">
+
+
+<xsl:template match="lvmp:var[ @transform='proper-case' ]" priority="5"
+ mode="lvmp:transform">
+
+ <xsl:param name="value" />
+
+ <xsl:text>ucwords(strtolower(</xsl:text>
+ <xsl:copy-of select="$value" />
+ <xsl:text>))</xsl:text>
+</xsl:template>
+
+
+
+<xsl:template match="lvmp:var[ not( @transform ) or @transform='' ]"
+ mode="lvmp:transform" priority="3">
+
+ <xsl:param name="value" />
+
+ <!-- no transformation; do nothing -->
+ <xsl:copy-of select="$value" />
+</xsl:template>
+
+
+<xsl:template match="lvmp:var" mode="lvmp:transform" priority="2">
+ <xsl:message terminate="yes">
+ <xsl:text>error: unknown transformation `</xsl:text>
+ <xsl:value-of select="@transform" />
+ <xsl:text>' for `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+</xsl:template>
+
+
+<xsl:template match="lvmp:*" mode="lvmp:transform" priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>internal error: unexpected node for transformation: </xsl:text>
+ <xsl:copy-of select="." />
+ </xsl:message>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/c1map/valparse.xsl b/src/current/c1map/valparse.xsl
new file mode 100644
index 0000000..8c4ab1f
--- /dev/null
+++ b/src/current/c1map/valparse.xsl
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Inline value parser
+
+ Will parse all attributes and text of the form "a{b}c", where `b' is some
+ variable.
+-->
+<xsl:stylesheet version="2.0"
+ xmlns:c1="http://www.epic-premier.com/XMLSchema"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:lvm="http://www.lovullo.com/rater/map/c1"
+ xmlns:lvmp="http://www.lovullo.com/rater/map/c1/pp">
+
+
+<xsl:template match="@*|text()" mode="lvm:valparse">
+ <xsl:call-template name="lvm:valparse">
+ <xsl:with-param name="str" select="." />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template name="lvm:valparse">
+ <xsl:param name="str" />
+
+ <xsl:variable name="pre" select="substring-before( $str, '{' )" />
+ <xsl:variable name="post" select="substring-after( $str, '}' )" />
+
+ <!-- get stuff between the two -->
+ <xsl:variable name="postpre" select="substring-after( $str, '{' )" />
+ <xsl:variable name="val" select="substring-before( $postpre, '}' )" />
+
+
+ <xsl:if test="$pre">
+ <xsl:text>'</xsl:text>
+ <!-- TODO: escape -->
+ <xsl:value-of select="$pre" />
+ <xsl:text>' . </xsl:text>
+ </xsl:if>
+
+ <xsl:choose>
+ <!-- variable reference -->
+ <xsl:when test="$val">
+ <xsl:call-template name="lvmp:gen-val">
+ <xsl:with-param name="name" select="$val" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- static value; no variable -->
+ <xsl:otherwise>
+ <xsl:text>'</xsl:text>
+ <xsl:value-of select="$str" />
+ <xsl:text>'</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:if test="$post">
+ <xsl:text> . '</xsl:text>
+ <!-- TODO: escape -->
+ <xsl:value-of select="$post" />
+ <xsl:text>'</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+
+<!--
+ Generate a variable reference for later rendering
+
+ If a variable of the form x.y is found, it is recursively processed with `y'
+ as the variable and `x' as the context: a dictionary lookup.
+-->
+<xsl:template name="lvmp:gen-val">
+ <xsl:param name="name" />
+
+ <xsl:variable name="trans" select="
+ ancestor::lvm:c1-map/lvmp:translate[ @name=$name ]
+ " />
+
+ <xsl:choose>
+ <!-- name translation requested -->
+ <xsl:when test="$trans">
+ <xsl:call-template name="lvmp:do-gen-val">
+ <xsl:with-param name="name" select="$trans/@to" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- no translation; use name as-is -->
+ <xsl:otherwise>
+ <xsl:call-template name="lvmp:do-gen-val">
+ <xsl:with-param name="name" select="$name" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="lvmp:do-gen-val">
+ <xsl:param name="name" />
+
+ <!-- we may have a variable of the form `x.y', in which case we should
+ process `y' withint he context of `x' -->
+ <xsl:variable name="rightmost" select="
+ substring-after( $name, '.' )
+ " />
+
+ <xsl:choose>
+ <!-- no more key references -->
+ <xsl:when test="$rightmost = ''">
+ <lvmp:value ref="{$name}" />
+ </xsl:when>
+
+ <!-- recursively process key reference -->
+ <xsl:otherwise>
+ <!-- determine the key context, which is the entire base sans the
+ rightmost variable name -->
+ <xsl:variable name="context" select="
+ substring( $name, 1, (
+ string-length( $name ) - string-length( $rightmost ) - 1
+ ) )
+ " />
+
+ <lvmp:value ref="{$rightmost}" index-key="{$context}">
+ <xsl:call-template name="lvmp:gen-val">
+ <xsl:with-param name="name" select="$context" />
+ </xsl:call-template>
+ </lvmp:value>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/calc.xsd b/src/current/calc.xsd
new file mode 100644
index 0000000..0030aa7
--- /dev/null
+++ b/src/current/calc.xsd
@@ -0,0 +1,883 @@
+<?xml version="1.0"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://www.lovullo.com/calc"
+ xmlns="http://www.lovullo.com/calc"
+ elementFormDefault="qualified">
+
+
+<!--basicTypes-->
+ <xs:simpleType name="nameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Generic name reference restriction, intended to be generic enough to
+ work with most systems (supporting both C-style and Lisp-style
+ identifiers).
+
+ The systems that implement this schema should perform their own, more
+ strict, type checks.
+ </xs:documentation>
+ </xs:annotation>
+
+ <!-- we need to allow '@' since that's used in macros -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="[a-zA-Z_@{-][a-zA-Z0-9_@{}-]*" />
+ <xs:maxLength value="50" />
+ </xs:restriction>
+ </xs:simpleType>
+
+
+ <xs:simpleType name="constValueType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Valid value for constants.
+ </xs:documentation>
+ </xs:annotation>
+
+ <!-- we need to allow '@' since that's used in macros -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="-?[0-9]+(.[0-9]+)?[mek]?|\{?@[a-z][a-zA-Z0-9_]*@\}?" />
+ <xs:maxLength value="50" />
+ </xs:restriction>
+ </xs:simpleType>
+
+
+ <xs:simpleType name="descType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Documentation for a specific element.
+
+ The documentation must not be sparse; please provide something
+ descriptive that will be useful to someone completely new to the code.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:restriction base="xs:string">
+ <xs:minLength value="2" />
+ </xs:restriction>
+ </xs:simpleType>
+
+
+ <xs:simpleType name="indexNameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Single-character index variable
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:restriction base="xs:NCName">
+ <xs:pattern value="[a-z]" />
+ </xs:restriction>
+ </xs:simpleType>
+
+
+ <xs:simpleType name="symbolType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Symbol used to represent an entity when rendered.
+
+ The string should consist of TeX/LaTeX commands and should produce a
+ single symbol.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:restriction base="xs:string" />
+ </xs:simpleType>
+<!--/basicTypes-->
+
+
+<!--calculations-->
+ <!--operators-->
+ <xs:element name="operator" type="operatorBaseType" abstract="true">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Abstract operator type used to classify elements as operators.
+
+ Operators are portions of the calculation that perform a portion of
+ the calculation. For example, a summation would classify as an
+ operation (operator), but value-of would not (as it is only
+ representing a value, but not performing a calculation).
+ </xs:documentation>
+ </xs:annotation>
+ </xs:element>
+
+
+ <xs:complexType name="operatorBaseType" abstract="true">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Elements/attributes common to all operator types (see the operator
+ element for a description on what qualifies as an operator).
+
+ All operators may include child calculations. Furthermore, they may
+ be labeled and may have an identifier associated with their result.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:element name="when" type="whenType" minOccurs="0" maxOccurs="unbounded" />
+ <xs:group ref="calculation" minOccurs="0" maxOccurs="unbounded" />
+ <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </xs:sequence>
+
+ <xs:attribute name="yields" type="nameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional identifier with which the result of the calculation may be
+ referenced.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="label" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional description of a calculation. @label is used in place of
+ @desc to ensure that it is not confused with the otherwise required
+ @desc attribute.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="desc" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ @desc should be used to describe a value that is generated by a
+ calculation and should not otherwise be used (e.g. with
+ lv:sum/@generate)
+
+ This is different from @label, which provides a high-level
+ description of what the equation is doing. @desc describes what the
+ generated value is.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="index" type="indexNameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Allows for the definition of an index variable which will be
+ defined for all children of the operation.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+
+ <xs:element name="sum" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Represents a summation (addition) of a set of values.
+
+ If @of is used, the equation defined by child elements will be
+ iterated using an index on the set provided by @of. If no equation is
+ given, all elements in the set identified by @of will be summed.
+
+ If @of is omitted, the result of each child element will be summed.
+
+ This operator should also be used for subtraction by summing negative
+ values.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:attribute name="of" type="nameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Iterate over all values of this set. Must be a set.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="generates" type="nameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional name of a set to generate from this expressions.
+
+ Generators are compatible only with @of and a sub-expression.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="sym" type="symbolType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Symbol to use when typesetting the generator
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="precision" type="constValueType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Yield precision
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <xs:element name="product" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Represents the product (multiplication) of a set of values.
+
+ If @of is used, the equation defined by child elements will be
+ iterated using an index on the set provided by @of. If no equation is
+ given, all elements in the set identified by @of will be multiplied.
+
+ If @of is omitted, the result of each child element will be
+ multiplied.
+
+ While this operator may also be used for division by multiplying by
+ the inverse of a number (n^-1), a limited quotient operator is
+ provided for clarity and convenience.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:attribute name="of" type="nameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Iterate over all values of this set. Must be a set.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="generates" type="nameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional name of a set to generate from this expressions.
+
+ Generators are compatible only with @of and a sub-expression.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="sym" type="symbolType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Symbol to use when typesetting the generator
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="dot" type="xs:boolean">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Dot product of any number of vectors
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="precision" type="constValueType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Yield precision
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <xs:element name="quotient" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Represents the quotient (division) of two values.
+
+ This operator is to be thought of as a fraction: the numerator is
+ identified by the first child element and the denominator the second.
+
+ While the summation operator may also be used for division by
+ multiplying by the inverse of a number (n^-1), this limited operator
+ is provided for clarity and convenience.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType" />
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <!-- named after Scheme's expt; same arg order -->
+ <xs:element name="expt" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Exponent; the first child node is the base, the second is the order.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType" />
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <xs:element name="cons" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Construct a list using the given element and an existing list
+
+ This is analogous to lisp's cons; the first argument is the car and
+ the second is the cdr.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:sequence>
+ <!-- both car and cdr required -->
+ <xs:group ref="calculation" minOccurs="2" maxOccurs="2" />
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <xs:element name="car" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Retrieve the first element of a list
+
+ Analogous to lisp's car.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:sequence>
+ <!-- we accept only a set -->
+ <xs:group ref="calculation" minOccurs="1" maxOccurs="1" />
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <xs:element name="cdr" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Return the list without its first element
+
+ If the list contains only one element, then an empty list will be
+ returned. Analogous to lisp's cdr.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:sequence>
+ <!-- we accept only a set -->
+ <xs:group ref="calculation" minOccurs="1" maxOccurs="1" />
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <xs:element name="length-of" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Retrieves the length of a vector
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:sequence>
+ <!-- we accept only a set -->
+ <xs:group ref="calculation" minOccurs="1" maxOccurs="1" />
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <!--apply-->
+ <xs:element name="apply" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Denotes a function application.
+
+ The result of the function identified by @name will be used in
+ place of the call. The application may optionally accept an
+ argument list (if the function accepts arguments).
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:sequence>
+ <xs:element name="arg" type="applyArgumentType" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+
+ <xs:attribute name="name" type="nameType" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Name of the function to apply
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <!-- argument shorthand -->
+ <xs:anyAttribute namespace="##any" processContents="lax" />
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <xs:complexType name="applyArgumentType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Represents an argument to be applied to a function.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:group ref="calculation" minOccurs="1" maxOccurs="1" />
+ </xs:sequence>
+
+ <xs:attribute name="name" type="nameType" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Name of the parameter to apply to the function
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+
+ <xs:element name="recurse" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Recursively applies the current function.
+
+ All arguments are copied to the argument list of the function application unless overridden.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:sequence>
+ <xs:element name="arg" type="applyArgumentType" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+
+ <!-- argument shorthand -->
+ <xs:anyAttribute namespace="##any" processContents="lax" />
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <!--/apply-->
+
+
+ <xs:element name="ceil" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Converts the given value to an integer, rounding up if necessary (e.g. 0.1 => 1)
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:attribute name="precision" type="constValueType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Value precision to consider for rounding.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <xs:element name="floor" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Converts the given value to an integer, rounding down if necessary (e.g. 0.9 => 0)
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType">
+ <xs:attribute name="precision" type="constValueType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Value precision to consider for rounding.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="set" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Creates a set out of its siblings
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType" />
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+
+
+ <!--
+ Debug
+ -->
+ <xs:element name="debug-to-console" substitutionGroup="operator">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Outputs the result of the contained expression to the console and
+ returns its value.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="operatorBaseType" />
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ <!--/operators-->
+
+
+ <!--when-->
+ <xs:complexType name="whenType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Defines a condition under which the expression will yield one if
+ true, otherwise will be <em>strongly</em> zero (zero even if
+ undefined)
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:group ref="conditions" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+
+ <xs:attribute name="name" type="nameType" use="required" />
+ <xs:attribute name="index" type="nameType" />
+ <xs:attribute name="label" type="xs:string" />
+ </xs:complexType>
+
+
+ <xs:group name="conditions">
+ <xs:choice>
+ <xs:element name="eq" type="conditionType" />
+ <xs:element name="ne" type="conditionType" />
+ <xs:element name="lt" type="conditionType" />
+ <xs:element name="gt" type="conditionType" />
+ <xs:element name="lte" type="conditionType" />
+ <xs:element name="gte" type="conditionType" />
+
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+ </xs:choice>
+ </xs:group>
+
+
+ <xs:complexType name="conditionType">
+ <xs:sequence>
+ <xs:group ref="calculation" minOccurs="0" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+ <!--/when-->
+
+
+ <xs:complexType name="indexType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ More flexible alternative to the @index attribute on elements; supports
+ calculations for determining the index
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:group ref="calculation" minOccurs="1" maxOccurs="1" />
+ </xs:sequence>
+ </xs:complexType>
+
+
+ <xs:complexType name="valueType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Returns the value associated with the given identifier.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:element name="when" type="whenType" minOccurs="0" maxOccurs="unbounded" />
+ <xs:element name="index" type="indexType" minOccurs="0" maxOccurs="2" />
+ </xs:sequence>
+
+ <xs:attribute name="name" type="nameType" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ An identifier for which the value should be retrieved
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="index" type="nameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Allows for the definition of an index variable which will be
+ defined for all children of the operation.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="label" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional description of a calculation. @label is used in place of
+ @desc to ensure that it is not confused with the otherwise required
+ @desc attribute.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+
+ <xs:complexType name="constType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Returns @value. This element simply enforces documentation and prevents
+ the use of magic values.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:element name="when" type="whenType" minOccurs="0" maxOccurs="unbounded" />
+ <xs:element name="index" type="indexType" minOccurs="0" maxOccurs="2" />
+ </xs:sequence>
+
+ <xs:attribute name="value" type="constValueType" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Value of constant (must be within the domain of its @type)
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" type="nameType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Type (domain) of the constant (inferred when not provided)
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="desc" type="descType" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Description of the value explaining its intent
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="label" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional description of a calculation. @label is used in place of
+ @desc to ensure that it is not confused with the otherwise required
+ @desc attribute.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+
+ <xs:complexType name="casesType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Represents a list of cases for a calculation.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:element name="case" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+
+ <xs:element name="when" type="whenType" minOccurs="0" maxOccurs="unbounded" />
+ <xs:group ref="calculation" minOccurs="1" maxOccurs="1" />
+ </xs:sequence>
+
+ <xs:attribute name="label" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional description of the case
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
+
+ <xs:element name="otherwise" minOccurs="0" maxOccurs="1">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:group ref="calculation" />
+ </xs:sequence>
+
+ <xs:attribute name="label" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional description of the case
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+
+ <xs:attribute name="label" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Optional description of the case set
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+
+
+ <xs:complexType name="letType">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Declares variables visible within the let block.
+
+ This exists as a means of assignment for re-using values.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:element name="values" minOccurs="1" maxOccurs="1">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Declares a list of values to define for this block
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ <xs:element name="value" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Declares a value that will remain in scope for the let block
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:sequence>
+ <xs:group ref="calculation" minOccurs="1" maxOccurs="1" />
+ </xs:sequence>
+
+ <xs:attribute name="name" type="nameType" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Variable name
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="type" type="nameType" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Variable datatype
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <!-- TODO: enum -->
+ <xs:attribute name="set" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Vector/matrix; if omitted, implicitly scalar.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+
+ <xs:attribute name="desc" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Describe the purpose of the value
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:group ref="calculation" minOccurs="1" maxOccurs="1" />
+ </xs:sequence>
+ </xs:complexType>
+
+
+ <xs:group name="calculation">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Represents every element that can be used to perform a calculation.
+ Implementors may use this group to accept any type of calculation.
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:choice>
+ <xs:element ref="operator" />
+ <xs:element name="value-of" type="valueType" />
+ <xs:element name="const" type="constType" />
+ <xs:element name="cases" type="casesType" />
+ <xs:element name="let" type="letType" />
+ <xs:any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+ </xs:choice>
+ </xs:group>
+
+
+ <xs:group name="calculationRoot">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">
+ Root calculation node; this should be the entry point for any type of
+ calculation. This helps to ensure that certain nodes are only used at
+ the beginning of a calculation (such as cases).
+ </xs:documentation>
+ </xs:annotation>
+
+ <xs:choice>
+ <xs:group ref="calculation" />
+ </xs:choice>
+ </xs:group>
+<!--/calculations-->
+
+</xs:schema>
diff --git a/src/current/compile.xsl b/src/current/compile.xsl
new file mode 100644
index 0000000..2e8d561
--- /dev/null
+++ b/src/current/compile.xsl
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Writes preprocessor output to disk to eliminate the overhead of reprocessing
+ for each task that requires such output.
+
+ Also performs validation.
+
+ N.B.: Requires XSLT >=2.0
+-->
+
+<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:lvm="http://www.lovullo.com/rater/map"
+ xmlns:lvmc="http://www.lovullo.com/rater/map/compiler"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:w="http://www.lovullo.com/rater/worksheet"
+ xmlns:util="http://www.lovullo.com/util"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+ xmlns:compiler="http://www.lovullo.com/rater/compiler"
+ xmlns:calc-compiler="http://www.lovullo.com/calc/compiler"
+ xmlns:ext="http://www.lovullo.com/ext">
+
+<xsl:output
+ indent="yes"
+ omit-xml-declaration="yes"
+ />
+
+<!-- processing utilities -->
+<xsl:include href="include/util.xsl" />
+
+<!-- compiler -> JS -->
+<xsl:include href="include/preprocess.xsl" />
+<xsl:include href="compiler/validate.xsl" />
+<xsl:include href="compiler/js.xsl" />
+<xsl:include href="compiler/map.xsl" />
+
+<!-- contains get-symbol-map -->
+<xsl:include href="include/display.xsl" />
+
+<!-- allows disabling of time-consuming validation -->
+<xsl:param name="preproc-cache-validate" select="'true'" />
+
+
+<!--
+ Simply copy the preprocessor output; the caller is responsible for outputting
+ this to a document.
+-->
+<xsl:template match="/lv:rater|/lv:package" priority="5">
+ <!-- XXX: Duplicate code; see summary -->
+ <xsl:variable name="processed">
+ <xsl:apply-templates select="." mode="preproc:compile" />
+ </xsl:variable>
+
+ <!-- fail on preprocessor errors -->
+ <xsl:call-template name="preproc:err-chk">
+ <xsl:with-param name="processed" select="$processed" />
+ </xsl:call-template>
+
+ <!-- validation must have passed; output the nodes -->
+ <xsl:copy-of select="$processed" />
+</xsl:template>
+
+
+<xsl:template name="preproc:err-chk">
+ <xsl:param name="processed" />
+
+ <xsl:for-each select="$processed//preproc:error">
+ <xsl:message terminate="yes">
+ <xsl:text>!!! preprocessor error: </xsl:text>
+ <xsl:value-of select="." />
+ </xsl:message>
+ </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template match="*" mode="preproc:handle-errors" priority="1">
+ <!-- do nothing -->
+</xsl:template>
+
+
+<xsl:template match="lvm:program-map|lvm:return-map" priority="5">
+ <xsl:apply-templates select="." mode="lvmc:compile" />
+</xsl:template>
+
+
+<xsl:template match="w:worksheet" priority="5">
+ <xsl:apply-templates select="." mode="w:compile" />
+</xsl:template>
+
+
+<!-- any unhandled nodes should be an error -->
+<xsl:template match="*" priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>fatal: source file is invalid: unexpected node </xsl:text>
+ <xsl:value-of select="name()" />
+ </xsl:message>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/compiler/fragments.xsl b/src/current/compiler/fragments.xsl
new file mode 100644
index 0000000..8ea6024
--- /dev/null
+++ b/src/current/compiler/fragments.xsl
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Compiles rater XML into JavaScript
+
+ This stylesheet should be included by whatever is doing the processing and is
+ responsible for outputting the generated code in whatever manner is
+ appropriate (inline JS, a file, etc).
+-->
+
+<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:c="http://www.lovullo.com/calc"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<xsl:template match="lv:package" mode="preproc:compile-fragments">
+ <xsl:copy>
+ <xsl:sequence select="@*, *" />
+
+ <!-- compile each fragment in the symbol table -->
+ <preproc:fragments>
+ <xsl:for-each select="preproc:symtable/preproc:sym">
+ <xsl:variable name="result">
+ <xsl:apply-templates select="." mode="preproc:compile-fragments" />
+ </xsl:variable>
+
+ <xsl:if test="$result != ''">
+ <preproc:fragment id="{@name}">
+ <xsl:value-of select="$result" />
+ </preproc:fragment>
+ </xsl:if>
+ </xsl:for-each>
+ </preproc:fragments>
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @src ]" mode="preproc:compile-fragments" priority="9">
+ <!-- do not compile external symbols -->
+</xsl:template>
+
+<xsl:template match="preproc:sym" mode="preproc:compile-fragments" priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>[jsc] fatal: unknown symbol type for `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>': </xsl:text>
+ <xsl:value-of select="@type" />
+ </xsl:message>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='rate' ]" mode="preproc:compile-fragments" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <!-- could be one of two places -->
+ <xsl:apply-templates mode="compile" select="
+ $pkg/lv:rate[ @yields=$name ]
+ , $pkg/lv:rate-group/lv:rate[ @yields=$name ]
+ " />
+</xsl:template>
+<xsl:template match="preproc:sym[ @type='gen' ]" mode="preproc:compile-fragments" priority="5">
+ <!-- compiled by above -->
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='class' ]" mode="preproc:compile-fragments" priority="5">
+ <!-- name is 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="compile" />
+</xsl:template>
+<xsl:template match="preproc:sym[ @type='cgen' ]" mode="preproc:compile-fragments" priority="5">
+ <!-- compiled by above -->
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='func' ]" mode="preproc:compile-fragments" 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="compile" />
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='param' ]" mode="preproc:compile-fragments" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <xsl:apply-templates select="$pkg/lv:param[ @name=$name ]" mode="compile" />
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='type' ]" mode="preproc:compile-fragments" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <!-- a typedef can stand on its own or exist within another typedef -->
+ <xsl:apply-templates mode="compile" select="
+ $pkg/lv:typedef[ @name=$name ]
+ , $pkg//lv:typedef//lv:typedef[ @name=$name ]
+ " />
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='const' ]" mode="preproc:compile-fragments" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <xsl:apply-templates select="$pkg/lv:const[ @name=$name ]" mode="compile" />
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='tpl' ]" mode="preproc:compile-fragments" priority="5">
+ <!-- templates are for the preprocessor only -->
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='lparam' ]" mode="preproc:compile-fragments" priority="5">
+ <!-- they're local and therefore compiled as part of the containing block -->
+</xsl:template>
+
+<xsl:template match="preproc:sym[ @type='meta' ]"
+ mode="preproc:compile-fragments" priority="5">
+ <xsl:variable name="name" select="substring-after( @name, ':meta:' )" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <xsl:variable name="node" as="element( lv:prop )"
+ select="$pkg/lv:meta/lv:prop[ @name=$name ]" />
+ <xsl:apply-templates mode="compile"
+ select="$node" />
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/compiler/js-calc.xsl b/src/current/compiler/js-calc.xsl
new file mode 100644
index 0000000..8594d4f
--- /dev/null
+++ b/src/current/compiler/js-calc.xsl
@@ -0,0 +1,1141 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Compiles calculation XML into JavaScript
+
+ This stylesheet should be included by whatever is doing the processing and is
+ responsible for outputting the generated code in whatever manner is
+ appropriate (inline JS, a file, etc).
+
+ It is assumed that validations are performed prior to compiling; otherwise, it
+ is possible to inject JavaScript into the output; only pass correct input to
+ this compiler.
+
+ All undefined values in a set are considered to be zero, resulting in an
+ infinite set; this simplifies sums and compilation.
+
+ The generated code may not be optimal, but it may be processed by another
+ system (e.g. Closure Compiler) to perform additional optimizations.
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:w="http://www.lovullo.com/rater/worksheet"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+ xmlns:calc-compiler="http://www.lovullo.com/calc/compiler">
+
+
+<!-- enable debugging by default -->
+<xsl:param name="calcc-debug" select="'yes'" />
+
+<!-- name to output when reporting problems -->
+<xsl:variable name="calc-compiler:name" select="'js-calc-compiler'" />
+
+<xsl:template name="calc-compiler:error">
+ <xsl:param name="message" select="'unspecified error'" />
+
+ <xsl:message terminate="yes">
+ <xsl:value-of select="$calc-compiler:name" />
+ <xsl:text>: </xsl:text>
+ <xsl:value-of select="$message" />
+ </xsl:message>
+</xsl:template>
+
+
+<xsl:template match="c:*" mode="compile" priority="1">
+ <xsl:variable name="debugval">
+ <xsl:if test="
+ ( $calcc-debug = 'yes' )
+ or ( //w:worksheet//w:display/@name = @generates )
+ or (
+ (
+ local-name() = 'case'
+ or ./c:index
+ )
+ and //w:worksheet//w:display/@name = ancestor::c:*/@generates
+ )
+ ">
+
+ <xsl:text>yes</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- should we force debugging? TODO: decouple from lv namespace -->
+ <xsl:variable name="debug-force" select="
+ ancestor::lv:*/@yields =
+ root(.)//calc-compiler:force-debug/calc-compiler:ref/@name
+ " />
+
+ <xsl:if test="$debugval = 'yes' or $debug-force">
+ <xsl:text>( function() { var result = </xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="compile-pre" />
+
+ <xsl:if test="$debugval = 'yes' or $debug-force">
+ <xsl:text>; </xsl:text>
+ <xsl:text>( debug['</xsl:text>
+ <xsl:value-of select="@_id" />
+ <xsl:text>'] || ( debug['</xsl:text>
+ <xsl:value-of select="@_id" />
+ <xsl:text>'] = [] ) ).push( result ); </xsl:text>
+
+ <xsl:text>return result; </xsl:text>
+ <xsl:text>} )() </xsl:text>
+ </xsl:if>
+</xsl:template>
+
+
+<!--
+ Begins compilation of a calculation
+
+ This is responsible for wrapping the value to enforce precedence rules and
+ convert it to a number (to avoid string concatenation when adding), then
+ delegating the compilation to the appropriate template(s).
+
+ @return generated JS for the given calculation
+-->
+<xsl:template match="c:*" mode="compile-pre" priority="1">
+ <!-- ensure everything is grouped (for precedence) and converted to a
+ number -->
+ <xsl:text>( </xsl:text>
+ <xsl:apply-templates select="." mode="compile-calc" />
+ <xsl:text> )</xsl:text>
+</xsl:template>
+
+
+<xsl:template match="c:const[ ./c:when ]|c:value-of[ ./c:when ]" mode="compile-pre" priority="5">
+ <xsl:text>( </xsl:text>
+ <!-- first, do what we normally would do (compile the value) -->
+ <xsl:text>( </xsl:text>
+ <xsl:apply-templates select="." mode="compile-calc" />
+ <xsl:text> )</xsl:text>
+
+ <!-- then multiply by the c:when result -->
+ <xsl:text> * ( </xsl:text>
+ <xsl:for-each select="./c:when">
+ <xsl:if test="position() > 1">
+ <xsl:text> * </xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="compile" />
+ </xsl:for-each>
+ <xsl:text> )</xsl:text>
+ <xsl:text> )</xsl:text>
+</xsl:template>
+
+
+
+<!--
+ Generates code for the sum or product of a set
+
+ This applies only with sets provided via the @of attribute, which will be used
+ to denote what set to some over. If no @index is provided, a dummy index will
+ be used. Otherwise, the index provided will be used and may be referenced by
+ children.
+
+ If child nodes exist, the summation/product will be performed over their
+ resulting equation. Otherwise, the summation/product will be performed over
+ the set of values only.
+
+ The generated self-executing function may be used inline with the rest of the
+ expression; the computed value will be returned.
+
+ @return generated summation/product anonymous self-executing function
+-->
+<xsl:template match="c:product[@of]|c:sum[@of]" mode="compile-calc" priority="1">
+ <!-- see c:ceil/c:floor precision comments -->
+ <xsl:variable name="precision">
+ <xsl:choose>
+ <xsl:when test="@precision">
+ <xsl:value-of select="@precision" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>8</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="operator">
+ <xsl:apply-templates select="." mode="compile-getop" />
+ </xsl:variable>
+
+ <xsl:variable name="index">
+ <xsl:choose>
+ <xsl:when test="@index">
+ <xsl:value-of select="@index" />
+ </xsl:when>
+
+ <!-- if they don't provide us with an index, then we don't want them using
+ it -->
+ <xsl:otherwise>
+ <xsl:text>_$i$_</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- introduce scope both to encapsulate values and so we can insert this as
+ part of a larger expression (will return a value) -->
+ <xsl:text>( function() {</xsl:text>
+
+ <!-- will store result of the summation/product -->
+ <xsl:text>var sum = 0;</xsl:text>
+
+ <xsl:variable name="of" select="@of" />
+
+ <xsl:variable name="func" select="ancestor::lv:function" />
+
+ <xsl:variable name="value">
+ <xsl:choose>
+ <!-- is @of a function param? -->
+ <xsl:when test="
+ $func
+ and root(.)/preproc:symtable/preproc:sym[
+ @type='lparam'
+ and @name=concat( ':', $func/@name, ':', $of )
+ ]
+ ">
+
+ <xsl:value-of select="@of" />
+ </xsl:when>
+
+ <!-- maybe a constant? -->
+ <xsl:when test="
+ root(.)/preproc:symtable/preproc:sym[
+ @type='const'
+ and @name=$of
+ ]
+ ">
+
+ <xsl:text>consts['</xsl:text>
+ <xsl:value-of select="@of" />
+ <xsl:text>']</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>args['</xsl:text>
+ <xsl:value-of select="@of" />
+ <xsl:text>']</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- if we're looking to generate a set, initialize it -->
+ <xsl:if test="@generates">
+ <xsl:text>var G = []; </xsl:text>
+ </xsl:if>
+
+ <!-- loop through each value -->
+ <xsl:text>for ( var </xsl:text>
+ <xsl:value-of select="$index" />
+ <xsl:text> in </xsl:text>
+ <xsl:value-of select="$value" />
+ <xsl:text> ) {</xsl:text>
+
+ <xsl:text>var result = +(+( </xsl:text>
+ <xsl:choose>
+ <!-- if there are child nodes, use that as the summand/expression -->
+ <xsl:when test="./c:*">
+ <xsl:apply-templates select="." mode="compile-calc-sumprod" />
+ </xsl:when>
+
+ <!-- otherwise, sum/multiply ever value in the set identified by @of -->
+ <xsl:otherwise>
+ <xsl:value-of select="$value" />
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$index" />
+ <xsl:text>]</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text> )).toFixed(</xsl:text>
+ <xsl:value-of select="$precision" />
+ <xsl:text>);</xsl:text>
+
+ <!-- if generating a set, store this result -->
+ <xsl:if test="@generates">
+ <xsl:text>G.push( result ); </xsl:text>
+ </xsl:if>
+
+ <!-- generate summand -->
+ <xsl:text>sum </xsl:text>
+ <xsl:value-of select="$operator" />
+ <xsl:text>= +result;</xsl:text>
+
+ <!-- end of loop -->
+ <xsl:text>}</xsl:text>
+
+ <!-- if a set has been generated, store it -->
+ <xsl:if test="@generates">
+ <xsl:text>args['</xsl:text>
+ <xsl:value-of select="@generates" />
+ <xsl:text>'] = G; </xsl:text>
+ </xsl:if>
+
+ <xsl:text>return sum;</xsl:text>
+ <xsl:text>} )()</xsl:text>
+</xsl:template>
+
+
+<!--
+ Compile dot products
+
+ N.B. This match has a higher priority than other products.
+
+ Dot products should operate only on vectors; if other input passes the
+ validator, then the result is undefined.
+-->
+<xsl:template match="c:product[@dot]" mode="compile-calc" priority="5">
+ <xsl:text>( function() { </xsl:text>
+
+ <!-- we need to determine which vector is the longest to ensure that we
+ properly compute every value (remember, undefined will be equivalent to
+ 0, so the vectors needn't be of equal length *gasp* blasphemy!) -->
+ <xsl:text>var _$dlen$ = longerOf( </xsl:text>
+ <xsl:for-each select=".//c:value-of">
+ <xsl:if test="position() > 1">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+
+ <!-- skip any wrapping; we want the direct reference (so compile-calc, not
+ compile)-->
+ <xsl:apply-templates select="." mode="compile-calc" />
+ </xsl:for-each>
+ <xsl:text> ); </xsl:text>
+
+ <!-- will store the total sum -->
+ <xsl:text>var _$dsum$ = 0;</xsl:text>
+
+ <!-- sum the product of each -->
+ <xsl:text disable-output-escaping="yes">for ( var _$d$ = 0; _$d$ &lt; _$dlen$; _$d$++ ) {</xsl:text>
+ <xsl:text>_$dsum$ += </xsl:text>
+ <!-- product of each -->
+ <xsl:for-each select=".//c:value-of">
+ <xsl:if test="position() > 1">
+ <xsl:text> * </xsl:text>
+ </xsl:if>
+
+ <xsl:text>( ( </xsl:text>
+ <xsl:apply-templates select="." mode="compile" />
+ <xsl:text> || [] )[ _$d$ ] || 0 )</xsl:text>
+ </xsl:for-each>
+ <xsl:text>; </xsl:text>
+ <xsl:text>}</xsl:text>
+
+ <xsl:text>return _$dsum$;</xsl:text>
+
+ <xsl:text> } )()</xsl:text>
+</xsl:template>
+
+
+<!--
+ Generates a sum/product over the expression denoted by its child nodes
+
+ When no @of set is provided, this will add or multiple each child expression.
+
+ @return generated expression
+-->
+<xsl:template match="c:sum|c:product" mode="compile-calc">
+ <xsl:apply-templates select="." mode="compile-calc-sumprod" />
+</xsl:template>
+
+
+<xsl:template match="c:sum|c:product" mode="compile-calc-sumprod">
+ <xsl:variable name="operator">
+ <xsl:apply-templates select="." mode="compile-getop" />
+ </xsl:variable>
+
+ <xsl:for-each select="./c:*">
+ <!-- add operator between each expression -->
+ <xsl:if test="position() > 1">
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="$operator" />
+ <xsl:text> </xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="compile" />
+ </xsl:for-each>
+</xsl:template>
+
+
+<!--
+ @return addition operator
+-->
+<xsl:template match="c:sum" mode="compile-getop">
+ <xsl:text>+</xsl:text>
+</xsl:template>
+
+<!--
+ @return multiplication operator
+-->
+<xsl:template match="c:product" mode="compile-getop">
+ <xsl:text>*</xsl:text>
+</xsl:template>
+
+
+<!--
+ Generate code for ceiling/floor functions
+
+ @return ceil/floor compiled JS
+-->
+<xsl:template match="c:ceil|c:floor" mode="compile-calc">
+ <!-- determine precision for ceil/floor (floating point precision errors can
+ drastically affect the outcome) -->
+ <xsl:variable name="precision">
+ <xsl:choose>
+ <!-- if a precision was explicitly provided, then use that -->
+ <xsl:when test="@precision">
+ <xsl:value-of select="@precision" />
+ </xsl:when>
+
+ <!-- ECMAScript uses a default precision of 24; by reducing the
+ precision to 8 decimal places, we can drastically reduce the affect
+ of precision errors on the calculations -->
+ <xsl:otherwise>
+ <xsl:text>8</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:text>Math.</xsl:text>
+ <xsl:value-of select="local-name()" />
+ <xsl:text>( +(</xsl:text>
+ <xsl:apply-templates select="./c:*" mode="compile" />
+ <xsl:text> ).toFixed( </xsl:text>
+ <xsl:value-of select="$precision" />
+ <xsl:text> ) )</xsl:text>
+</xsl:template>
+
+
+<!--
+ Replaces a constant with its value in the compiled expression
+
+ Constants are syntatic sugar in the language to prevent magic values; they are
+ not required at runtime. As such, the value of the constant will be placed
+ directly into the compiled code.
+
+ @return quoted constant value
+-->
+<xsl:template match="c:const" mode="compile-calc">
+ <!-- assumed to be numeric -->
+ <xsl:value-of select="@value" />
+</xsl:template>
+
+
+<!--
+ Generate JS representing the value associated with the given name
+
+ @return generated JS representing value or 0
+-->
+<xsl:template match="c:value-of" mode="compile-calc" priority="1">
+ <xsl:apply-templates select="." mode="compile-calc-value" />
+</xsl:template>
+
+<!-- TODO: this should really be decoupled -->
+<!-- TODO: does not properly support matrices -->
+<xsl:template match="c:value-of[ ancestor::lv:match ]" mode="compile-calc" priority="5">
+ <xsl:variable name="name" select="@name" />
+
+ <xsl:choose>
+ <!-- scalar -->
+ <xsl:when test="
+ root(.)/preproc:symtable/preproc:sym[ @name=$name ]
+ /@dim = '0'
+ ">
+ <xsl:apply-templates select="." mode="compile-calc-value" />
+ </xsl:when>
+
+ <!-- non-scalar -->
+ <xsl:otherwise>
+ <xsl:text>(</xsl:text>
+ <xsl:apply-templates select="." mode="compile-calc-value" />
+ <xsl:text>)[__$$i]</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Generate JS representing the value of a function argument
+
+ This will match whenever value-of is used on a name that matches any function
+ parameter.
+
+ XXX: We want to remain decoupled from lv if possible.
+
+ @return generated JS representing argument value or 0
+-->
+<xsl:template mode="compile-calc-value"
+ match="c:*[@name=ancestor::lv:function/lv:param/@name]">
+
+ <!-- use the argument passed to the function -->
+ <xsl:apply-templates select="." mode="compile-calc-index">
+ <xsl:with-param name="value" select="@name" />
+ </xsl:apply-templates>
+
+ <xsl:text> || 0</xsl:text>
+</xsl:template>
+
+
+<!--
+ Using value from let expressions
+-->
+<xsl:template mode="compile-calc-value"
+ match="c:*[ @name=ancestor::c:let/c:values/c:value/@name ]">
+
+ <!-- compile the value with the index (if any) -->
+ <xsl:apply-templates select="." mode="compile-calc-index">
+ <xsl:with-param name="value" select="@name" />
+ </xsl:apply-templates>
+
+ <xsl:text> || 0</xsl:text>
+</xsl:template>
+
+
+<!--
+ Generate JS representing the value of a global constant
+
+ Since constants are intended only to prevent magic values during development
+ (and are not required at runtime), the value of the constant will be placed
+ directly into the compiled code. However, we will *not* do this if the
+ constant is a set, since its index may be determined at runtime.
+
+ Note that "magic" constants' values are not inlined.
+
+ @return quoted constant value
+-->
+<xsl:template mode="compile-calc-value"
+ match="
+ c:*[
+ @name=root(.)/preproc:symtable/preproc:sym[
+ @type='const'
+ and @dim='0'
+ ]/@name
+ ]
+ ">
+
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="sym"
+ select="root(.)/preproc:symtable/preproc:sym[ @name=$name ]" />
+
+ <!-- it is expected that validations prior to compiling will prevent JS
+ injection here -->
+ <xsl:choose>
+ <!-- "magic" constants should not have their values inlined -->
+ <xsl:when test="$sym/@magic='true'">
+ <xsl:text>consts['</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>']</xsl:text>
+ </xsl:when>
+
+ <!-- @value should be defined when @dim=0 -->
+ <xsl:otherwise>
+ <xsl:value-of select="$sym/@value" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Generates JS representing the value of a constant as part of a set
+
+ Since the index of constant sets can be determined at runtime, we need to
+ store all possible values. As such, we shouldn't repeat ourselves by inlining
+ all possible values; instead, we'll reference a pre-generated set of values
+ for the particular constant.
+
+ @return generated code representing value of a variable, or 0 if undefined
+-->
+<xsl:template mode="compile-calc-value"
+ match="
+ c:*[
+ @name=root(.)/preproc:symtable/preproc:sym[
+ @type='const'
+ and not( @dim='0' )
+ ]/@name
+ ]
+ ">
+
+ <xsl:variable name="value">
+ <xsl:text>consts['</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>']</xsl:text>
+ </xsl:variable>
+
+ <xsl:apply-templates select="." mode="compile-calc-index">
+ <xsl:with-param name="value" select="$value" />
+ </xsl:apply-templates>
+
+ <!-- undefined values in sets are considered to be 0 -->
+ <xsl:text> || 0</xsl:text>
+</xsl:template>
+
+
+<!--
+ Generate JS representing the value of a generated index
+
+ @return generated code associated with the value of the generated index
+-->
+<xsl:template mode="compile-calc-value"
+ match="c:*[ @name = ancestor::c:*[ @of ]/@index ]">
+
+ <!-- depending on how the index is generated, it could be a string, so we must
+ cast it -->
+ <xsl:text>+</xsl:text>
+ <xsl:value-of select="@name" />
+</xsl:template>
+
+
+<!--
+ Generates JS representing the value of a variable
+
+ If the variable is undefined, the value will be considered to be 0 (this is
+ especially important for the summation of sets within this implementation).
+ That is: a value will never be considered undefined.
+
+ @return generated code representing value of a variable, or 0 if undefined
+-->
+<xsl:template match="c:*" mode="compile-calc-value">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <xsl:variable name="dim"
+ select="$pkg/preproc:symtable/preproc:sym[ @name=$name ]/@dim" />
+
+ <!-- retrieve the value, casting to a number (to avoid potentially nasty
+ string concatenation bugs instead of integer/floating point arithmetic)
+ as long as we're either not a set, or provide an index for the set -->
+ <xsl:variable name="value">
+ <xsl:text>args['</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>']</xsl:text>
+ </xsl:variable>
+
+ <!-- the awkward double-negatives are intentional, since @index may not
+ exist; here's what we're doing:
+
+ - If it's not a set, then indexes are irrelevant; always cast scalars
+ - Otherwise
+ - If an index was provided and it is not a matrix, cast
+ - Otherwise
+ - If two indexes were provided and it is a matrix, cast
+ -->
+ <!-- N.B. it is important to do this outside the value variable, otherwise the
+ cast may be done at the incorrect time -->
+ <xsl:if test="
+ (
+ $dim='0'
+ or (
+ (
+ ( @index and not( @index = '' ) )
+ or ( ./c:index )
+ )
+ and not( $dim='2' )
+ )
+ or (
+ ( $dim='2' )
+ and ./c:index[2]
+ )
+ )
+ and not(
+ parent::c:arg
+ and not( @index )
+ )
+ ">
+
+ <xsl:text>+</xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="compile-calc-index">
+ <xsl:with-param name="value" select="$value" />
+ </xsl:apply-templates>
+
+ <!-- default to 0 if nothing is set (see notes on bottom of summary page; we
+ assume all undefined values in a set to be implicitly 0, which greatly
+ simplifies things) -->
+ <xsl:text> || 0</xsl:text>
+</xsl:template>
+
+
+<!--
+ Include the index if one was provided
+
+ The index is represented as a var in the compiled JS, so treat it as an
+ identifier not a string.
+
+ An @index attribute or node may be provided. In the former case, the
+ identifier simply represents a variable. In the latter, a more complicate
+ expression may be used to generate the index.
+
+ Note that the compiled variable to which the index will be applied is
+ included, as a string, to this template. This allows it to be wrapped if
+ necessary (using multiple indexes) so that the default value can be properly
+ applied, avoiding undefined[N].
+
+ @param string value compiled variable to which index will be applied
+
+ @return index (including brackets), if one was provided
+-->
+<xsl:template match="c:*" mode="compile-calc-index">
+ <xsl:param name="value" />
+ <xsl:variable name="index" select="@index" />
+
+ <xsl:choose>
+ <xsl:when test="@index">
+ <!-- output the value, falling back on an empty array to prevent errors
+ when attempting to access undefined values -->
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="$value" />
+ <xsl:text>||[])</xsl:text>
+
+ <xsl:text>[</xsl:text>
+
+ <xsl:choose>
+ <!-- if this index was explicitly defined as such, just output the name
+ (since it will have been defined as a variable in the compiled code)
+ -->
+ <xsl:when test="ancestor::c:*[ @of and @index=$index ]">
+ <xsl:value-of select="@index" />
+ </xsl:when>
+
+ <!-- if the index references a parameter, simply output the identifier -->
+ <xsl:when test="@index=ancestor::lv:function/lv:param/@name">
+ <xsl:value-of select="@index" />
+ </xsl:when>
+
+ <!-- scalar constant -->
+ <xsl:when test="@index = root(.)/preproc:symtable/preproc:sym
+ [ @type='const'
+ and @dim='0' ]
+ /@name">
+ <xsl:value-of select="root(.)/preproc:symtable
+ /preproc:sym[ @name=$index ]
+ /@value" />
+ </xsl:when>
+
+ <!-- otherwise, it's a variable -->
+ <xsl:otherwise>
+ <xsl:text>args['</xsl:text>
+ <xsl:value-of select="@index" />
+ <xsl:text>']</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:text>]</xsl:text>
+ </xsl:when>
+
+ <!-- if index node(s) were provided, then recursively generate -->
+ <xsl:when test="./c:index">
+ <xsl:apply-templates select="./c:index[1]" mode="compile-calc-index">
+ <xsl:with-param name="wrap" select="$value" />
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <!-- otherwise, we have no index, so just output the compiled variable -->
+ <xsl:otherwise>
+ <xsl:value-of select="$value" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="c:index" mode="compile-calc-index">
+ <xsl:param name="wrap" />
+
+ <!-- get the next index node, if available -->
+ <xsl:variable name="next" select="following-sibling::c:index" />
+
+ <xsl:variable name="value">
+ <xsl:value-of select="$wrap" />
+
+ <!-- generate index -->
+ <xsl:text>[</xsl:text>
+ <xsl:apply-templates select="./c:*[1]" mode="compile" />
+ <xsl:text>]</xsl:text>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$next">
+ <!-- recurse on any sibling indexes, wrapping with default value -->
+ <xsl:apply-templates select="$next" mode="compile-calc-index">
+ <xsl:with-param name="wrap">
+ <!-- wrap the value in parenthesis so that we can provide a default value if
+ the index lookup fails -->
+ <xsl:text>(</xsl:text>
+
+ <xsl:value-of disable-output-escaping="yes" select="$value" />
+
+ <!-- using 0 as the default is fine, even if we have $next; accessing an index
+ of 0 is perfectly fine, since it will be cast to an object; we just must
+ make sure that it is not undefined -->
+ <xsl:text> || 0 )</xsl:text>
+ </xsl:with-param>
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <!-- if there is no sibling, just output our value with the index, unwrapped
+ (since the caller will handle its default value for us -->
+ <xsl:otherwise>
+ <xsl:value-of disable-output-escaping="yes" select="$value" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ Generates a quotient between two calculations
+
+ The result is simply division between the two nodes, the first being the
+ numerator and the second being the denominator.
+
+ @return generate quotient
+-->
+<xsl:template match="c:quotient" mode="compile-calc">
+ <!-- we only accept a numerator and a denominator -->
+ <xsl:apply-templates select="./c:*[1]" mode="compile" />
+ <xsl:text> / </xsl:text>
+ <xsl:apply-templates select="./c:*[2]" mode="compile" />
+</xsl:template>
+
+
+<xsl:template match="c:expt" mode="compile-calc">
+ <!-- we only accept a numerator and a denominator -->
+ <xsl:text>Math.pow(</xsl:text>
+ <xsl:apply-templates select="./c:*[1]" mode="compile" />
+ <xsl:text>, </xsl:text>
+ <xsl:apply-templates select="./c:*[2]" mode="compile" />
+ <xsl:text>)</xsl:text>
+</xsl:template>
+
+
+<!--
+ Generates a function application (call)
+
+ Arguments are set by name in the XML, but the generate code uses actual
+ function arguments. Therefore, the generated argument list will be generated
+ in the order that the function is expecting, filling in unset parameters with
+ the constant 0.
+
+ @return generated function application
+-->
+<xsl:template match="c:apply" mode="compile-calc">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="self" select="." />
+
+ <xsl:call-template name="calc-compiler:gen-func-name">
+ <xsl:with-param name="name" select="@name" />
+ </xsl:call-template>
+
+ <xsl:text>( args</xsl:text>
+
+ <xsl:variable name="arg-prefix" select="concat( ':', $name, ':' )" />
+
+ <!-- generate argument list in the order that the arguments are expected (they
+ can be entered in the XML in any order) -->
+ <xsl:for-each select="
+ root(.)/preproc:symtable/preproc:sym[
+ @type='func'
+ and @name=$name
+ ]/preproc:sym-ref
+ ">
+
+ <xsl:text>, </xsl:text>
+
+ <xsl:variable name="pname" select="substring-after( @name, $arg-prefix )" />
+ <xsl:variable name="arg" select="$self/c:arg[@name=$pname]" />
+
+ <xsl:choose>
+ <!-- if the call specified this argument, then use it -->
+ <xsl:when test="$arg">
+ <xsl:apply-templates select="$arg/c:*[1]" mode="compile" />
+ </xsl:when>
+
+ <!-- otherwise, there's no value; pass in 0 -->
+ <xsl:otherwise>
+ <xsl:value-of select="'0'" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <xsl:text> )</xsl:text>
+
+ <!-- if c:when was provided, compile it in such a way that we retain the
+ function call (we want the result for providing debug information) -->
+ <xsl:if test="./c:when">
+ <xsl:text> * </xsl:text>
+ <xsl:apply-templates select="./c:when" mode="compile" />
+ </xsl:if>
+</xsl:template>
+
+
+<xsl:template match="c:when" mode="compile-calc">
+ <!-- note that if we have multiple c:whens, they'll be multiplied together by
+ whatever calls this, so we're probably fine -->
+
+ <xsl:text>( function() {</xsl:text>
+ <!-- return a 1 or a 0 depending on the result of the expression -->
+ <xsl:text>return ( </xsl:text>
+ <xsl:text>( </xsl:text>
+ <!-- get the value associated with this node -->
+ <xsl:apply-templates select="." mode="compile-calc-value" />
+ <xsl:text> ) </xsl:text>
+
+ <!-- generate remainder of expression -->
+ <xsl:apply-templates select="./c:*[1]" mode="compile-calc-when" />
+ <xsl:text>) ? 1 : 0; </xsl:text>
+ <xsl:text>} )()</xsl:text>
+</xsl:template>
+
+
+<xsl:template match="c:eq|c:ne|c:lt|c:gt|c:lte|c:gte" mode="compile-calc-when">
+ <xsl:variable name="name" select="local-name()" />
+
+ <!-- map to LaTeX equivalent -->
+ <xsl:variable name="map">
+ <c id="eq">==</c>
+ <c id="ne">!=</c>
+ <c id="gt">&gt;</c>
+ <c id="lt">&lt;</c>
+ <c id="gte">&gt;=</c>
+ <c id="lte">&lt;=</c>
+ </xsl:variable>
+
+ <xsl:value-of disable-output-escaping="yes" select="$map/*[ @id=$name ]" />
+ <xsl:text> </xsl:text>
+
+ <xsl:apply-templates select="./c:*[1]" mode="compile" />
+</xsl:template>
+
+
+<xsl:template match="c:*" mode="compile-calc-when">
+ <xsl:apply-templates select="." mode="compile-pre" />
+</xsl:template>
+
+
+<xsl:template match="c:cases" mode="compile-calc">
+ <xsl:text>( function() {</xsl:text>
+
+ <xsl:for-each select="./c:case">
+ <!-- turn "if" into an "else if" if needed -->
+ <xsl:if test="position() > 1">
+ <xsl:text>else </xsl:text>
+ </xsl:if>
+
+ <xsl:text>if (</xsl:text>
+ <xsl:for-each select="./c:when">
+ <xsl:if test="position() > 1">
+ <!-- they all yield integer values, so this should work just fine -->
+ <xsl:text> * </xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="compile" />
+ </xsl:for-each>
+ <xsl:text> ) { return </xsl:text>
+ <!-- process on its own so that we can debug its final value -->
+ <xsl:apply-templates select="." mode="compile" />
+ <xsl:text>; } </xsl:text>
+ </xsl:for-each>
+
+ <!-- check for the existence of an "otherwise" clause, which should be
+ trivial enough to generate -->
+ <xsl:if test="c:otherwise">
+ <!-- this situation may occur in templates since it's a convenient and
+ easy-to-use template notation (conditional cases, none of which may
+ actually match and be output) -->
+ <xsl:choose>
+ <xsl:when test="c:case">
+ <xsl:text>else</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>if ( true )</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <xsl:text> { return </xsl:text>
+ <xsl:apply-templates select="c:otherwise" mode="compile" />
+ <xsl:text>; } </xsl:text>
+ </xsl:if>
+
+ <xsl:text> } )() || 0</xsl:text>
+</xsl:template>
+
+<xsl:template match="c:case" mode="compile-calc">
+ <xsl:apply-templates
+ select="./c:*[ not( local-name() = 'when' ) ][1]"
+ mode="compile-calc-when" />
+</xsl:template>
+
+<xsl:template match="c:otherwise" mode="compile-calc">
+ <xsl:apply-templates
+ select="c:*[1]"
+ mode="compile-calc-when" />
+</xsl:template>
+
+
+<xsl:template match="c:set" mode="compile-calc">
+ <xsl:text>[</xsl:text>
+ <xsl:for-each select="./c:*">
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="compile" />
+ </xsl:for-each>
+ <xsl:text>]</xsl:text>
+</xsl:template>
+
+
+<!--
+ Just list the Lisp cons (constructs a list)
+-->
+<xsl:template match="c:cons" mode="compile-calc">
+ <xsl:variable name="car" select="./c:*[1]" />
+ <xsl:variable name="cdr" select="./c:*[2]" />
+
+ <xsl:text>(function(){</xsl:text>
+ <!-- duplicate the array just in case...if we notice a performance impact,
+ then we can determine if such a duplication is necessary -->
+ <xsl:text>var cdr = Array.prototype.slice.call(</xsl:text>
+ <xsl:apply-templates select="$cdr" mode="compile" />
+ <xsl:text>, 0);</xsl:text>
+ <xsl:text>cdr.unshift( </xsl:text>
+ <xsl:apply-templates select="$car" mode="compile" />
+ <xsl:text> ); </xsl:text>
+ <!-- no longer the cdr -->
+ <xsl:text>return cdr; </xsl:text>
+ <xsl:text>})()</xsl:text>
+</xsl:template>
+
+
+<xsl:template match="c:car" mode="compile-calc">
+ <xsl:text>(</xsl:text>
+ <xsl:apply-templates select="c:*[1]" mode="compile" />
+ <xsl:text>[0]||0)</xsl:text>
+</xsl:template>
+
+<xsl:template match="c:cdr" mode="compile-calc">
+ <xsl:apply-templates select="c:*[1]" mode="compile" />
+ <xsl:text>.slice(1)</xsl:text>
+</xsl:template>
+
+
+<!--
+ Returns the length of any type of set (not just a vector)
+-->
+<xsl:template match="c:length-of" mode="compile-calc">
+ <xsl:text>( </xsl:text>
+ <xsl:apply-templates select="./c:*[1]" mode="compile" />
+ <xsl:text>.length || 0 )</xsl:text>
+</xsl:template>
+
+
+<xsl:template match="c:let" mode="compile-calc">
+ <!-- the first node contains all the values -->
+ <xsl:variable name="values" select="./c:values/c:value" />
+
+ <!-- if a @name was provided, then treat it like a named Scheme let
+ expression in that it can be invoked with different arguments -->
+ <xsl:variable name="fname">
+ <xsl:if test="@name">
+ <xsl:call-template name="calc-compiler:gen-func-name">
+ <xsl:with-param name="name" select="@name" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:text>( </xsl:text>
+ <xsl:if test="@name">
+ <xsl:value-of select="$fname" />
+ <xsl:text> = </xsl:text>
+ </xsl:if>
+ <xsl:text>function( </xsl:text>
+ <!-- generate arguments -->
+ <xsl:for-each select="$values">
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="@name" />
+ </xsl:for-each>
+ <xsl:text> ) { </xsl:text>
+
+ <!-- the second node is the body -->
+ <xsl:text>return </xsl:text>
+ <xsl:apply-templates select="./c:*[2]" mode="compile" />
+ <xsl:text>;</xsl:text>
+ <xsl:text>} )</xsl:text>
+
+ <!-- assign the arguments according to the calculations -->
+ <xsl:text>( </xsl:text>
+ <xsl:for-each select="$values">
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <!-- compile the argument value (the parenthesis are just to make it
+ easier to read the compiled code) -->
+ <xsl:text>(</xsl:text>
+ <xsl:apply-templates select="./c:*[1]" mode="compile" />
+ <xsl:text>)</xsl:text>
+ </xsl:for-each>
+ <xsl:text> ) </xsl:text>
+</xsl:template>
+
+
+<!--
+ Generate a function name for use as the name of a function within the compiled
+ code
+
+ @return generated function name
+-->
+<xsl:template name="calc-compiler:gen-func-name">
+ <xsl:param name="name" />
+
+ <xsl:text>func_</xsl:text>
+ <xsl:value-of select="$name" />
+</xsl:template>
+
+
+<xsl:template match="c:debug-to-console" mode="compile-calc">
+ <xsl:text>(function(){</xsl:text>
+ <xsl:text>var result = </xsl:text>
+ <xsl:apply-templates select="./c:*[1]" mode="compile" />
+ <xsl:text>;</xsl:text>
+
+ <!-- log the result and return it so that we do not inhibit the calculation
+ (allowing it to be inlined anywhere) -->
+ <xsl:text>console.log( </xsl:text>
+ <xsl:if test="@label">
+ <xsl:text>'</xsl:text>
+ <xsl:value-of select="@label" />
+ <xsl:text>', </xsl:text>
+ </xsl:if>
+
+ <xsl:text>result ); </xsl:text>
+ <xsl:text>return result; </xsl:text>
+ <xsl:text>})()</xsl:text>
+</xsl:template>
+
+
+<!--
+ Catch-all for calculations
+
+ If an unmatched calculation element is used, then the generated code will
+ output an error to the console (if a console is available) and return a value
+ that may be used within context of an equation; this allows it to be placed
+ inline.
+
+ @return self-executing anonymous error function
+-->
+<xsl:template match="c:*" mode="compile-calc">
+ <xsl:text>( function () {</xsl:text>
+ <xsl:text>throw Error( "Unknown calculation: </xsl:text>
+ <xsl:value-of select="name()" />
+ <xsl:text>" ); </xsl:text>
+ <xsl:text>} )() </xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/compiler/js.xsl b/src/current/compiler/js.xsl
new file mode 100644
index 0000000..55ea029
--- /dev/null
+++ b/src/current/compiler/js.xsl
@@ -0,0 +1,1908 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Compiles rater XML into JavaScript
+
+ This stylesheet should be included by whatever is doing the processing and is
+ responsible for outputting the generated code in whatever manner is
+ appropriate (inline JS, a file, etc).
+-->
+
+<stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:lvp="http://www.lovullo.com"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:w="http://www.lovullo.com/rater/worksheet"
+ xmlns:wc="http://www.lovullo.com/rater/worksheet/compiler"
+ xmlns:compiler="http://www.lovullo.com/rater/compiler"
+ xmlns:calc-compiler="http://www.lovullo.com/calc/compiler"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+ xmlns:util="http://www.lovullo.com/util"
+ xmlns:ext="http://www.lovullo.com/ext">
+
+
+<!-- calculation compiler -->
+<include href="js-calc.xsl" />
+
+<!-- rating worksheet definition compiler -->
+<include href="worksheet.xsl" />
+
+<!-- newline -->
+<variable name="compiler:nl" select="'&#10;'" />
+
+
+<!--
+ Generates rater function
+
+ The rater is a single function that may be invoked with a key-value argument
+ list and will return a variety of data, including the final premium.
+
+ @param NodeSet pkgs all packages, including rater
+
+ @return compiled JS
+-->
+<template match="lv:package" mode="compiler:entry">
+ <!-- enclose everything in a self-executing function to sandbox our data -->
+ <text>( function() { </text>
+ <!-- to store debug information for equations (we have to put this out here
+ so that functions also have access to it...yes, it's stateful, yes it's
+ bullshit, but oh well) -->
+ <text>var debug = {};</text>
+ <text>var consts = {};</text>
+ <text>var params = {};</text>
+ <text>var types = {};</text>
+ <text>var meta = {};</text>
+ <!--
+ <value-of select="$compiler:nl" />
+ <apply-templates
+ select="root(.)//lv:const[ .//lv:item or preproc:iou ]"
+ mode="compile" />
+ -->
+
+</template>
+
+
+<template match="lv:package" mode="compiler:entry-rater">
+ <!-- the rater itself -->
+ <value-of select="$compiler:nl" />
+ <text>function rater( arglist, _canterm ) {</text>
+ <text>_canterm = ( _canterm == undefined ) ? true : !!_canterm;</text>
+
+ <!-- XXX: fix; clear debug from any previous runs -->
+ <text>debug = {};</text>
+
+ <!-- magic constants (N.B. these ones must be re-calculated with each
+ call, otherwise the data may be incorrect!) -->
+ <value-of select="$compiler:nl" />
+
+ <!-- XXX: Remove this; shouldn't be magic -->
+ <text>consts['__DATE_YEAR__'] = ( new Date() ).getFullYear(); </text>
+
+ <!-- clone the object so as not to modify the one that was passed
+ (ES5 feature); also adds constants -->
+ <text>var args = Object.create( arglist );</text>
+
+ <!-- will store the global params that we ended up requiring -->
+ <text>var req_params = {};</text>
+
+ <!-- handle defaults -->
+ <text>init_defaults( args, params );</text>
+
+ <!-- perform classifications -->
+ <value-of select="$compiler:nl" />
+ <text>var classes = rater.classify( args, _canterm );</text>
+ <!-- for @external generated clases -->
+ <text>var genclasses = {};</text>
+</template>
+
+<template match="lv:package" mode="compiler:entry-classifier">
+ <!-- allow classification of any arbitrary dataset -->
+ <value-of select="$compiler:nl" />
+ <text>rater.classify = function( args, _canterm ) {</text>
+ <text>_canterm = ( _canterm == undefined ) ? true : !!_canterm;</text>
+
+ <!-- XXX: Remove this; shouldn't be magic -->
+ <text>consts['__DATE_YEAR__'] = ( new Date() ).getFullYear(); </text>
+
+ <!-- object into which all classifications will be stored -->
+ <text>var classes = {}, genclasses = {}; </text>
+
+ <!-- TODO: We need to do something with this... -->
+ <text>var req_params = {}; </text>
+</template>
+
+<template match="lv:package" mode="compiler:exit-classifier">
+ <text>return classes;</text>
+ <text> };</text>
+
+ <!-- TODO: make sure fromMap has actually been compiled -->
+ <text>rater.classify.fromMap = function( args_base, _canterm ) { </text>
+ <text>var ret = {}; </text>
+ <text>rater.fromMap( args_base, function( args ) {</text>
+ <text>var classes = rater.classify( args, _canterm ); </text>
+
+ <text>
+ for ( var c in classes )
+ {
+ ret[ c ] = {
+ is: !!classes[ c ],
+ indexes: args[ rater.classify.classmap[ c ] ]
+ };
+ }
+ </text>
+ <text>} );</text>
+ <text>return ret;</text>
+ <text> }; </text>
+</template>
+
+<template match="lv:package" mode="compiler:exit-rater">
+ <param name="symbols" as="element( preproc:sym )*" />
+
+ <value-of select="$compiler:nl" />
+ <text>return { </text>
+ <!-- round the premium (special symbol ___yield) to max of 2 decimal places -->
+ <text>premium: ( Math.round( args.___yield * 100 ) / 100 ), </text>
+ <text>classes: classes, </text>
+ <text>vars: args, </text>
+ <text>consts: consts, </text>
+ <text>reqParams: req_params, </text>
+ <text>debug: debug </text>
+ <text>}; </text>
+ <text>}</text>
+
+ <!-- make the name of the supplier available -->
+ <text>rater.supplier = '</text>
+ <value-of select="substring-after( @name, '/' )" />
+ <text>'; </text>
+
+ <text>rater.meta = meta;</text>
+
+ <!-- provide classification -> yields mapping -->
+ <value-of select="$compiler:nl" />
+ <text>rater.classify.classmap = { </text>
+ <apply-templates select="." mode="compiler:classifier-yields-map">
+ <with-param name="symbols" select="$symbols" />
+ </apply-templates>
+ <text> }; </text>
+
+ <!-- provide classification descriptions -->
+ <value-of select="$compiler:nl" />
+ <text>rater.classify.desc = { </text>
+ <sequence select="
+ compiler:class-desc(
+ $symbols[ @type='class' ] )" />
+ <text> }; </text>
+
+ <variable name="mapfrom" select="
+ preproc:symtable/preproc:sym[
+ @type='map'
+ ]/preproc:from[
+ not(
+ @name = parent::preproc:sym
+ /preceding-sibling::preproc:sym[
+ @type='map'
+ ]/preproc:from/@name
+ )
+ ]
+ " />
+
+ <!-- mapped fields (external names) -->
+ <text>rater.knownFields = {</text>
+ <for-each select="$mapfrom">
+ <if test="position() > 1">
+ <text>, </text>
+ </if>
+
+ <text>'</text>
+ <value-of select="@name" />
+ <text>': true</text>
+ </for-each>
+ <text>}</text>
+
+ <!-- common stuff -->
+ <call-template name="compiler:static" />
+
+ <!-- the rater has been generated; return it -->
+ <text>return rater;</text>
+ <text>} )()</text>
+</template>
+
+
+<template match="lv:package" mode="compiler:classifier-yields-map">
+ <param name="symbols" />
+
+ <!-- for each cgen symbol (which is the classification @yields), map the
+ classification name (the @parent) to the cgen symbol name -->
+ <for-each select="$symbols[ @type='cgen' ]">
+ <if test="position() > 1">
+ <text>,</text>
+ </if>
+
+ <text>'</text>
+ <value-of select="substring-after( @parent, ':class:' )" />
+ <text>':'</text>
+ <value-of select="@name" />
+ <text>'</text>
+ </for-each>
+</template>
+
+
+<function name="compiler:class-desc">
+ <param name="syms" as="element( preproc:sym )*" />
+
+ <for-each select="$syms">
+ <if test="position() > 1">
+ <text>,</text>
+ </if>
+
+ <text>'</text>
+ <value-of select="substring-after( @name, ':class:' )" />
+ <text>':'</text>
+ <!-- todo: escape -->
+ <value-of select="translate( @desc, &quot;'&quot;, '' )" />
+ <text>'</text>
+ </for-each>
+</function>
+
+
+<!--
+ Compile global parameter list into an object literal
+
+ @return key and value for the given parameter
+-->
+<template match="lv:param" mode="compile">
+ <!-- generate key using param name -->
+ <text>params['</text>
+ <value-of select="@name" />
+ <text>'] = {</text>
+
+ <!-- param properties -->
+ <text>type: '</text>
+ <value-of select="@type" />
+ <text>',</text>
+
+ <text>'default': '</text>
+ <value-of select="@default" />
+ <text>',</text>
+
+ <text>required: </text>
+ <!-- param is required if the attribute is present, not non-empty -->
+ <value-of select="string( not( boolean( @default ) ) )" />
+ <text>};</text>
+</template>
+
+
+<!--
+ Generate value table for unions
+
+ The type of the table will be considered the type of the first enum and each
+ enum value table will be combined.
+
+ @return object literal properties containing union data
+-->
+<template match="lv:typedef/lv:union" mode="compile" priority="5">
+ <!-- generate key using type name -->
+ <text>types['</text>
+ <value-of select="../@name" />
+ <text>'] = {</text>
+
+ <!-- its type will be the type of its first enum (all must share the same
+ domain) -->
+ <text>type: '</text>
+ <value-of select=".//lv:enum[1]/@type" />
+ <text>',</text>
+
+ <!-- finally, its table of values should consist of every enum it contains -->
+ <text>values: {</text>
+ <for-each select="./lv:typedef/lv:enum/lv:item">
+ <if test="position() > 1">
+ <text>,</text>
+ </if>
+
+ <apply-templates select="." mode="compile" />
+ </for-each>
+ <text>}</text>
+
+ <text>};</text>
+</template>
+
+
+<!--
+ Generate value table for enums
+
+ @return object literal properties containing enum data
+-->
+<template match="lv:typedef/lv:enum" mode="compile" priority="5">
+ <!-- generate key using type name -->
+ <text>types['</text>
+ <value-of select="../@name" />
+ <text>'] = {</text>
+
+ <!-- domain of all values -->
+ <text>type: '</text>
+ <value-of select="@type" />
+ <text>',</text>
+
+ <!-- table of acceptable values -->
+ <text>values: {</text>
+ <for-each select="./lv:item">
+ <if test="position() > 1">
+ <text>,</text>
+ </if>
+
+ <apply-templates select="." mode="compile" />
+ </for-each>
+ <text>}</text>
+
+ <text>};</text>
+</template>
+
+
+<template match="lv:typedef/lv:base-type" mode="compile" priority="5">
+ <text>types['</text>
+ <value-of select="../@name" />
+ <text>'] = {</text>
+
+ <!-- base types are their own type -->
+ <text>type: '</text>
+ <value-of select="../@name" />
+ <text>',</text>
+
+ <text>values:{}</text>
+
+ <text>};</text>
+</template>
+
+
+<template match="lv:typedef/*" mode="compile" priority="1">
+ <message terminate="yes">
+ <text>[lvc] Unhandled typedef: </text>
+ <value-of select="../@name" />
+ </message>
+</template>
+
+
+<!--
+ Generate enum item value
+
+ @return property representing a specific value
+-->
+<template match="lv:enum/lv:item" mode="compile">
+ <!-- determine enumerated value -->
+ <variable name="value">
+ <choose>
+ <when test="@value">
+ <value-of select="@value" />
+ </when>
+
+ <!-- default to string value equivalent to name -->
+ <otherwise>
+ <value-of select="@name" />
+ </otherwise>
+ </choose>
+ </variable>
+
+ <!-- we are only interest in its value; its constant is an internal value -->
+ <text>'</text>
+ <value-of select="$value" />
+ <text>': true</text>
+</template>
+
+
+<!--
+ Generate an object containing values of constant sets
+
+ This is done instead of inlining constant values as we do with non-sets since
+ the specific index can be determined at runtime.
+
+ @return JS object assignment for constant set values
+-->
+<template mode="compile" priority="1"
+ match="lv:const[ element() or @values ]">
+ <text>consts['</text>
+ <value-of select="@name" />
+ <text>'] = [ </text>
+
+ <!-- matrices -->
+ <for-each select="compiler:const-sets( . )[ not( . = '' ) ]">
+ <if test="position() > 1">
+ <text>, </text>
+ </if>
+
+ <text>[ </text>
+ <for-each select="compiler:set-items( ., true() )">
+ <if test="position() > 1">
+ <text>, </text>
+ </if>
+
+ <value-of select="." />
+ </for-each>
+ <text> ]</text>
+ </for-each>
+
+ <!-- vectors -->
+ <for-each select="compiler:set-items( ., false() )">
+ <if test="position() > 1">
+ <text>, </text>
+ </if>
+
+ <value-of select="." />
+ </for-each>
+
+ <text> ]; </text>
+</template>
+
+
+<!--
+ Produce sequence of sets
+
+ Sets are used to group together items in a matrix. A set can be
+ defined explicitly (using nodes), or via a GNU Octave or
+ MATLAB-style matrix definition.
+-->
+<function name="compiler:const-sets" as="item()*">
+ <param name="const" as="element( lv:const )" />
+
+ <variable name="values-def" as="xs:string?"
+ select="$const/@values" />
+
+ <choose>
+ <when test="$values-def and contains( $values-def, ';' )">
+ <sequence select="tokenize(
+ normalize-space( $values-def ), ';' )" />
+ </when>
+
+ <otherwise>
+ <sequence select="$const/lv:set" />
+ </otherwise>
+ </choose>
+</function>
+
+
+<!--
+ Produce a sequence of items
+
+ Items represent elements of a vector. They may be specified
+ explicitly using nodes, or via a comma-delimited string.
+-->
+<function name="compiler:set-items" as="xs:string*">
+ <param name="set" as="item()*" />
+ <param name="allow-values" as="xs:boolean" />
+
+ <choose>
+ <when test="$set instance of xs:string">
+ <sequence select="tokenize(
+ normalize-space( $set ), ',' )" />
+ </when>
+
+ <when test="$set/@values and $allow-values">
+ <sequence select="tokenize(
+ normalize-space( $set/@values ), ',' )" />
+ </when>
+
+ <otherwise>
+ <sequence select="$set/lv:item/@value" />
+ </otherwise>
+ </choose>
+</function>
+
+
+<!--
+ Generate code to perform a classification
+
+ Based on the criteria provided by the classification, generate and store the
+ result of a boolean expression performing the classification using global
+ arguments.
+
+ TODO: Refactor; both @yields and $result-set checks are unneeded; they can be
+ combined (@yields as the default, which may or may not exist)
+
+ @return generated classification expression
+-->
+<template match="lv:classify" mode="compile">
+ <param name="noclass" />
+ <param name="result-set" />
+ <param name="ignores" />
+
+ <variable name="self" select="." />
+
+ <value-of select="$compiler:nl" />
+
+ <if test="not( $noclass )">
+ <if test="@preproc:generated='true'">
+ <text>gen</text>
+ </if>
+
+ <text>classes['</text>
+ <value-of select="@as" />
+ <text>'] = (function(){var result,tmp; </text>
+ </if>
+
+ <!-- locate classification criteria -->
+ <variable name="criteria" as="element( lv:match )*"
+ select="lv:match[
+ not( $ignores )
+ or not( @on=$ignores/@ref ) ]" />
+
+ <variable name="criteria-syms" as="element( preproc:sym )*"
+ select="root(.)/preproc:symtable/preproc:sym[
+ @name = $criteria/@on ]" />
+
+ <variable name="dest">
+ <choose>
+ <when test="$result-set">
+ <value-of select="$result-set" />
+ </when>
+
+ <otherwise>
+ <text>args['</text>
+ <value-of select="@yields" />
+ <text>']</text>
+ </otherwise>
+ </choose>
+ </variable>
+
+ <!-- generate boolean value from match expressions -->
+ <choose>
+ <!-- if classification criteria were provided, then use them -->
+ <when test="$criteria">
+ <variable name="criteria-scalar" as="element( lv:match )*"
+ select="$criteria[
+ @on = $criteria-syms[
+ @dim = '0' ]/@name ]" />
+
+ <variable name="op" as="xs:string"
+ select="compiler:match-group-op( $self )" />
+
+ <text></text>
+ <!-- first, non-scalar criteria -->
+ <apply-templates mode="compile"
+ select="$criteria[
+ not( @on = $criteria-scalar/@on ) ]">
+ <with-param name="result-set" select="$result-set" />
+ <with-param name="ignores" select="$ignores" />
+ <with-param name="operator" select="$op" />
+ </apply-templates>
+
+ <!-- scalars must be matched last -->
+ <apply-templates mode="compile"
+ select="$criteria-scalar">
+ <with-param name="result-set" select="$result-set" />
+ <with-param name="ignores" select="$ignores" />
+ <with-param name="operator" select="$op" />
+ </apply-templates>
+ </when>
+
+ <!-- if no classification criteria, then always true/false -->
+ <otherwise>
+ <!-- this is only useful if $noclass is *not* set -->
+ <if test="not( $noclass )">
+ <choose>
+ <!-- universal -->
+ <when test="not( @any='true' )">
+ <text>tmp = true; </text>
+ </when>
+
+ <!-- existential -->
+ <otherwise>
+ <text>tmp = false; </text>
+ </otherwise>
+ </choose>
+ </if>
+
+ <!-- if @yields was provided, then store the value in a variable of their
+ choice as well (since cmatch will not be done) -->
+ <if test="@yields or $result-set">
+ <value-of select="$dest" />
+ <choose>
+ <!-- universal -->
+ <when test="not( @any='true' )">
+ <text> = 1;</text>
+ </when>
+
+ <!-- existential -->
+ <otherwise>
+ <text> = 0;</text>
+ </otherwise>
+ </choose>
+ </if>
+ </otherwise>
+ </choose>
+
+ <text> return tmp;})();</text>
+
+ <!-- support termination on certain classifications (useful for eligibility
+ and error conditions) -->
+ <if test="@terminate = 'true'">
+ <text>if ( _canterm &amp;&amp; </text>
+
+ <if test="@preproc:generated='true'">
+ <text>gen</text>
+ </if>
+ <text>classes['</text>
+ <value-of select="@as" />
+ <text>'] ) throw Error( '</text>
+ <value-of select="@desc" />
+ <text>' );</text>
+
+ <value-of select="$compiler:nl" />
+ </if>
+
+ <variable name="sym"
+ select="root(.)/preproc:symtable/preproc:sym[ @name=$self/@yields ]" />
+
+ <!-- if we are not any type of set, then yield the value of the first
+ index (note the $criteria check; see above); note that we do not do
+ not( @set ) here, since that may have ill effects as it implies that
+ the node is not preprocessed -->
+ <!-- TODO: this can be simplified, since @yields is always provided -->
+ <if test="$criteria and ( @yields or $result-set ) and ( $sym/@dim='0' )">
+ <value-of select="$dest" />
+ <text> = </text>
+ <value-of select="$dest" />
+ <text>[0];</text>
+
+ <value-of select="$compiler:nl" />
+ </if>
+</template>
+
+
+<template match="preproc:rate" mode="compile">
+ <variable name="yields" select="@ref" />
+
+ <!-- compile the lv:rate block associated with this name -->
+ <apply-templates
+ select="root(.)//lv:rate[ @yields=$yields ]"
+ mode="compile" />
+</template>
+
+<template match="preproc:class" mode="compile">
+ <variable name="as" select="@ref" />
+
+ <apply-templates
+ select="root(.)//lv:classify[ @as=$as ]"
+ mode="compile" />
+</template>
+
+
+<!--
+ Generate domain checks for require-param nodes
+
+ The resulting code will cause an exception to be thrown if the domain check
+ fails.
+
+ FIXME: remove
+
+ @return generated domain check code
+-->
+<template match="lv:required-param" mode="compile">
+ <!--
+ <variable name="name" select="@ref" />
+
+ <text>vocalDomainCheck( '</text>
+ <value-of select="$name" />
+ <text>', '</text>
+ <value-of select="root(.)//lv:param[ @name=$name ]/@type" />
+ <text>', args['</text>
+ <value-of select="$name" />
+ <text>'] ); </text>
+ -->
+
+ <!-- record that this param was required -->
+ <!--
+ <text>req_params['</text>
+ <value-of select="$name" />
+ <text>'] = true; </text>
+ -->
+</template>
+
+
+<!--
+ Generate code asserting a match
+
+ Siblings are joined by default with ampersands to denote an AND relationship,
+ unless overridden.
+
+ @return generated match code
+-->
+<template match="lv:match" mode="compile" priority="1">
+ <!-- default to all matches being required -->
+ <param name="operator" select="'&amp;&amp;'" />
+ <param name="yields" select="../@yields" />
+ <param name="result-set" />
+
+ <variable name="name" select="@on" />
+
+ <text> tmp = </text>
+
+ <variable name="input-raw">
+ <choose>
+ <!-- if we have assumptions, then we'll be recalculating (rather than
+ referencing) an existing classification -->
+ <when test="lv:assuming">
+ <text>_cassume</text>
+ </when>
+
+ <otherwise>
+ <text>args['</text>
+ <value-of select="translate( @on, &quot;'&quot;, '' )" />
+ <text>']</text>
+ </otherwise>
+ </choose>
+ </variable>
+
+ <!-- yields (if not set, generate one so that cmatches still works properly)
+ -->
+ <variable name="yieldto">
+ <choose>
+ <!-- if we were given a result set to use, then use it -->
+ <when test="$result-set">
+ <value-of select="$result-set" />
+ </when>
+
+ <!-- store directly into the destination result set -->
+ <otherwise>
+ <call-template name="compiler:gen-match-yieldto">
+ <with-param name="yields" select="$yields" />
+ </call-template>
+ </otherwise>
+ </choose>
+ </variable>
+
+ <!-- the input value -->
+ <variable name="input">
+ <choose>
+ <when test="@scalar = 'true'">
+ <text>stov( </text>
+ <value-of select="$input-raw" />
+ <text>, ( ( </text>
+ <value-of select="$yieldto" />
+ <!-- note that we default to 1 so that there is at least a single
+ element (which will be the case of the scalar is the first match)
+ in a given classification; the awkward inner [] is to protect
+ against potentially undefined values and will hopefully never
+ happen, and the length is checked on the inner grouping rather than
+ on the outside of the entire expression to ensure that it will
+ yield the intended result if yieldto.length === 0 -->
+ <text> || [] ).length || 1 ) )</text>
+ </when>
+
+ <otherwise>
+ <value-of select="$input-raw" />
+ </otherwise>
+ </choose>
+ </variable>
+
+ <if test="lv:assuming">
+ <text>(function(){</text>
+ <!-- initialize variable (ensuring that the closure we're about to generate
+ will properly assign the value rather than encapsulate it) -->
+ <text>var </text>
+ <value-of select="$input-raw" />
+ <text>; </text>
+
+ <!-- generate assumptions and recursively generate the referenced
+ classification -->
+ <apply-templates select="." mode="compile-match-assumptions">
+ <with-param name="result-var" select="$input-raw" />
+ </apply-templates>
+ <text>; return </text>
+ </if>
+
+ <!-- invoke the classification matcher on this input -->
+ <text>anyValue( </text>
+ <value-of select="$input" />
+ <text>, </text>
+
+ <!-- TODO: error if multiple; also, refactor -->
+ <choose>
+ <when test="@value">
+ <variable name="value" select="@value" />
+ <variable name="sym"
+ select="root(.)/preproc:symtable/preproc:sym[ @name=$value ]" />
+
+ <choose>
+ <!-- simple constant -->
+ <when test="$sym and @value">
+ <value-of select="$sym/@value" />
+ </when>
+
+ <!-- value unavailable (XXX: this probably should never happen...) -->
+ <when test="$sym and @value">
+ <message>
+ <text>[jsc] !!! TODO: bad classification match: '</text>
+ <value-of select="$value" />
+ </message>
+ </when>
+
+ <otherwise>
+ <text>'</text>
+ <!-- TODO: Should we disallow entirely? -->
+ <message>
+ <text>[jsc] warning: static classification match '</text>
+ <value-of select="$value" />
+ <text>' in </text>
+ <value-of select="ancestor::lv:classify[1]/@as" />
+ <text>; use calculation predicate or constant instead</text>
+ </message>
+
+ <value-of select="$value" />
+ <text>'</text>
+ </otherwise>
+ </choose>
+ </when>
+
+ <when test="@pattern">
+ <text>function( val ) { </text>
+ <text>return /</text>
+ <value-of select="@pattern" />
+ <text>/.test( val );</text>
+ <text> }</text>
+ </when>
+
+ <when test="./c:*">
+ <text>function( val, __$$i ) { </text>
+ <text>return ( </text>
+ <for-each select="./c:*">
+ <if test="position() > 1">
+ <text disable-output-escaping="yes"> &amp;&amp; </text>
+ </if>
+
+ <text>( val </text>
+ <apply-templates select="." mode="compile-calc-when" />
+ <text> ) </text>
+ </for-each>
+ <text>);</text>
+ <text>}</text>
+ </when>
+
+ <otherwise>
+ <apply-templates select="." mode="compiler:match-anyof" />
+ </otherwise>
+ </choose>
+
+ <text>, ( </text>
+ <value-of select="$yieldto" />
+ <text> || ( </text>
+ <value-of select="$yieldto" />
+ <text> = [] ) ), </text>
+
+ <!-- if this match is part of a classification that should yield a matrix,
+ then force a matrix set -->
+ <choose>
+ <when test="ancestor::lv:classify/@set = 'matrix'">
+ <text>true</text>
+ </when>
+ <otherwise>
+ <text>false</text>
+ </otherwise>
+ </choose>
+
+ <text>, </text>
+ <choose>
+ <when test="parent::lv:classify/@any='true'">
+ <text>false</text>
+ </when>
+ <otherwise>
+ <text>true</text>
+ </otherwise>
+ </choose>
+
+ <!-- for debugging -->
+ <text>,"</text>
+ <value-of select="$input" />
+ <text>"</text>
+
+ <!-- end of anyValue() call -->
+ <text> ) </text>
+
+ <!-- end of assuming function call -->
+ <if test="lv:assuming">
+ <text>})()</text>
+ </if>
+
+ <text>;</text>
+
+ <text>( debug['</text>
+ <value-of select="@_id" />
+ <text>'] || ( debug['</text>
+ <value-of select="@_id" />
+ <text>'] = [] ) ).push( tmp ); </text>
+
+
+ <text>result = </text>
+ <choose>
+ <!-- join with operator if not first in set -->
+ <when test="position() > 1">
+ <text>result </text>
+ <value-of select="$operator" />
+ <text> tmp;</text>
+ </when>
+
+ <otherwise>
+ <text>tmp;</text>
+ </otherwise>
+ </choose>
+</template>
+
+<template name="compiler:gen-match-yieldto">
+ <param name="yields" />
+
+ <text>args['</text>
+ <choose>
+ <when test="$yields">
+ <value-of select="$yields" />
+ </when>
+
+ <otherwise>
+ <call-template name="compiler:gen-default-yield">
+ <with-param name="name" select="ancestor::lv:classify/@as" />
+ </call-template>
+ </otherwise>
+ </choose>
+ <text>']</text>
+</template>
+
+<!--
+ Handles the special "float" domain
+
+ Rather than the impossible task of calculating all possible floats and
+ asserting that the given values is within that set, the obvious task is to
+ assert whether or not the value is logically capable of existing in such a
+ set based on a definition of such a set.
+
+ See also "integer"
+-->
+<template match="lv:match[ @anyOf='float' ]" mode="compiler:match-anyof" priority="5">
+ <!-- ceil(x) - floor(x) = [ x is not an integer ] -->
+ <text>function( val ) {</text>
+ <text>return ( typeof +val === 'number' ) </text>
+ <text disable-output-escaping="yes">&amp;&amp; </text>
+ <!-- note: greater than or equal to, since we want to permit integers as
+ well -->
+ <text disable-output-escaping="yes">( Math.ceil( val ) >= Math.floor( val ) )</text>
+ <text>;</text>
+ <text>}</text>
+</template>
+
+<!--
+ Handles the special "float" domain
+
+ Rather than the impossible task of calculating all possible integers and
+ asserting that the given values is within that set, the obvious task is to
+ assert whether or not the value is logically capable of existing in such a
+ set based on a definition of such a set.
+
+ See also "float"
+-->
+<template match="lv:match[ @anyOf='integer' ]" mode="compiler:match-anyof" priority="5">
+ <!-- ceil(x) - floor(x) = [ x is not an integer ] -->
+ <text>function( val ) {</text>
+ <text>return ( typeof +val === 'number' ) </text>
+ <text disable-output-escaping="yes">&amp;&amp; </text>
+ <text>( Math.floor( val ) === Math.ceil( val ) )</text>
+ <text>;</text>
+ <text>}</text>
+</template>
+
+<!--
+ Handles matching on an empty set
+
+ This is useful for asserting against fields that may have default values,
+ since in such a case an empty value would be permitted.
+-->
+<template match="lv:match[ @anyOf='empty' ]" mode="compiler:match-anyof" priority="5">
+ <!-- ceil(x) - floor(x) = [ x is not an integer ] -->
+ <text>function( val ) {</text>
+ <text>return ( val === '' ) </text>
+ <text>|| ( val === undefined ) || ( val === null )</text>
+ <text>;</text>
+ <text>}</text>
+</template>
+
+<!--
+ Uh oh. Hopefully this never happens; will throw an exception if a type is
+ defined as a base type (using typedef), but is not handled by the compiler.
+-->
+<template match="lv:match[ @anyOf=root(.)//lv:typedef[ ./lv:base-type ]/@name ]"
+ mode="compiler:match-anyof" priority="3">
+
+ <text>function( val ) {</text>
+ <text>throw Error( 'CRITICAL: Unhandled base type: </text>
+ <value-of select="@anyOf" />
+ <text>' );</text>
+ <text>}</text>
+</template>
+
+<!--
+ Used for user-defined domains
+-->
+<template match="lv:match[ @anyOf ]" mode="compiler:match-anyof" priority="1">
+ <text>types['</text>
+ <value-of select="@anyOf" />
+ <text>'].values</text>
+</template>
+
+
+<function name="compiler:match-group-op" as="xs:string">
+ <param name="class" as="element( lv:classify )" />
+
+ <sequence select="if ( $class/@any = 'true' ) then
+ '||'
+ else
+ '&amp;&amp;'" />
+</function>
+
+
+<!--
+ Compiles a function
+
+ Parameters will be converted into actual function parameters. The function
+ will return the result of its expression (represented by a calculation in the
+ XML).
+
+ @return generated function
+-->
+<template match="lv:function" mode="compile">
+ <value-of select="$compiler:nl" />
+
+ <text>function </text>
+ <call-template name="calc-compiler:gen-func-name">
+ <with-param name="name" select="@name" />
+ </call-template>
+ <text>( args </text>
+
+ <!-- add parameters -->
+ <for-each select="./lv:param">
+ <text>, </text>
+ <value-of select="@name" />
+ </for-each>
+
+ <text>) {</text>
+
+ <text>return ( </text>
+ <!-- begin calculation generation (there should be only one calculation node
+ as a child, so only it will be considered) -->
+ <apply-templates select="./c:*[1]" mode="compile" />
+ <text> );</text>
+
+ <text>} </text>
+</template>
+
+
+<!--
+ Compile lv:rate's in such an order that dependencies will be compiled first
+
+ This is important to ensure that premium calculations based on other premiums
+ are actually calculated after the premium that they depend on. Having an
+ order-dependent document doesn't make sense with the declarative style and is
+ especially confusing when including packages.
+-->
+<template match="lv:package" mode="compile-rates">
+ <!-- generate the rate blocks, dependencies first; does not compile classifier
+ dependencies, as those will be compiled with the appropriate classifier
+ -->
+ <apply-templates mode="compile" select="
+ ./preproc:rate-deps/preproc:flat/preproc:rate
+ |
+ ./preproc:rate-deps/preproc:flat/preproc:class[ @external='true' ]
+ " />
+
+</template>
+
+
+
+<!--
+ Generates a premium calculation
+
+ The result of the generated expression, as denoted by a calculation in the
+ XML, will be stored in the variable identified by @yields.
+
+ TODO: If another calculation uses the yielded premium in the document before
+ this lv:rate block, then this block needs to be compiled *before* the block
+ that references is. We don't want to depend on order, as that would not be
+ declarative (in this particular scenario, at least).
+
+ @return generated self-executing premium calculation function
+-->
+<template match="lv:rate" mode="compile">
+ <value-of select="$compiler:nl" />
+
+ <!-- see c:ceil/c:floor precision comments in js-calc -->
+ <variable name="precision">
+ <choose>
+ <when test="@precision">
+ <value-of select="@precision" />
+ </when>
+
+ <otherwise>
+ <text>8</text>
+ </otherwise>
+ </choose>
+ </variable>
+
+ <variable name="store">
+ <!-- TODO: escape single quotes (even though there should never be any) -->
+ <text>args['</text>
+ <value-of select="@yields" />
+ <text>']</text>
+ </variable>
+
+ <!-- store the premium -->
+ <value-of select="$store" />
+ <text> = </text>
+
+ <text>( function rate_</text>
+ <!-- dashes, which may end up in generated code from templates, must be
+ removed -->
+ <value-of select="translate( @yields, '-', '_' )" />
+ <text>() {</text>
+
+ <text>var predmatch = ( </text>
+ <apply-templates select="." mode="compile-class-condition" />
+ <text> ); </text>
+
+ <!-- set the magic _CMATCH_ var to represent a list of indexes that meet all
+ the classifications -->
+ <text>consts._CMATCH_ = </text>
+ <apply-templates select="." mode="compile-cmatch" />
+ <text>;</text>
+
+ <!-- return the result of the calculation for this rate block -->
+ <text>return (+( </text>
+ <!-- begin calculation generation (there should be only one calculation
+ node as a child, so only it will be considered) -->
+ <apply-templates select="./c:*[1]" mode="compile" />
+ <text> )).toFixed(</text>
+ <value-of select="$precision" />
+ <text>) * predmatch; } )() </text>
+
+ <text>; </text>
+</template>
+
+<template match="lv:rate" mode="compile-class-condition">
+ <!-- generate expression for class list (leave the @no check to the cmatch
+ algorithm, since we want per-index @no's) -->
+ <text>( </text>
+ <variable name="class-set" select="./lv:class" />
+
+ <choose>
+ <when test="$class-set">
+ <for-each select="$class-set">
+ <!-- join class expressions with AND operator -->
+ <if test="position() > 1">
+ <text disable-output-escaping="yes"> &amp;&amp; </text>
+ </if>
+
+ <!-- negate if @no -->
+ <if test="@no='true'">
+ <text>!</text>
+ </if>
+
+ <variable name="ref" select="@ref" />
+
+ <if test="
+ root(.)/preproc:symtable/preproc:sym[
+ @name=concat( ':class:', $ref )
+ ]/@preproc:generated='true'
+ ">
+ <text>gen</text>
+ </if>
+
+ <text>classes['</text>
+ <value-of select="@ref" />
+ <text>']</text>
+ </for-each>
+ </when>
+
+ <!-- well, we need to output something -->
+ <otherwise>
+ <text>true</text>
+ </otherwise>
+ </choose>
+ <text> )</text>
+</template>
+
+
+<template match="lv:rate" mode="compile-cmatch">
+ <variable name="root" select="root(.)" />
+
+ <!-- generate cmatch call that will generate the cmatch set -->
+ <text>cmatch( [</text>
+ <for-each select="lv:class[ not( @no='true' ) ]">
+ <if test="position() > 1">
+ <text>, </text>
+ </if>
+
+ <text>args['</text>
+ <call-template name="compiler:get-class-yield">
+ <with-param name="name" select="@ref" />
+ <with-param name="search" select="$root" />
+ </call-template>
+ <text>']</text>
+ </for-each>
+ <text>], [</text>
+ <for-each select="lv:class[ @no='true' ]">
+ <if test="position() > 1">
+ <text>, </text>
+ </if>
+
+ <text>args['</text>
+ <call-template name="compiler:get-class-yield">
+ <with-param name="name" select="@ref" />
+ <with-param name="search" select="$root" />
+ </call-template>
+ <text>']</text>
+ </for-each>
+ <text>] )</text>
+</template>
+
+
+<template name="compiler:get-class-yield">
+ <param name="name" />
+ <param name="search" />
+
+ <variable name="yields">
+ <value-of select="
+ root(.)/preproc:symtable/preproc:sym[
+ @name=concat( ':class:', $name )
+ ]/@yields
+ " />
+ </variable>
+
+ <choose>
+ <when test="$yields != ''">
+ <value-of select="$yields" />
+ </when>
+
+ <otherwise>
+ <call-template name="compiler:gen-default-yield">
+ <with-param name="name" select="$name" />
+ </call-template>
+ </otherwise>
+ </choose>
+</template>
+
+
+<template name="compiler:gen-default-yield">
+ <param name="name" />
+
+ <!-- a random name that would be invalid to reference from the XML -->
+ <text>___$$</text>
+ <value-of select="$name" />
+ <text>$$</text>
+</template>
+
+
+<!--
+ Generates calculation used to yield a final premium
+
+ @return generated expression
+-->
+<template match="lv:yield" mode="compile">
+ <!-- compile yield calculation -->
+ <apply-templates select="./c:*[1]" mode="compile" />
+</template>
+
+
+
+<template match="lv:meta/lv:prop" mode="compile">
+ <text>meta['</text>
+ <value-of select="@name" />
+ <text>'] = </text>
+
+ <call-template name="util:json">
+ <with-param name="array">
+ <!-- values -->
+ <for-each select="lv:value">
+ <variable name="name" select="@name" />
+
+ <util:value>
+ <!-- TODO: refactor -->
+ <value-of select="
+ root(.)//lv:const[ @name=$name ]/@value
+ , root(.)//lv:item[ @name=$name ]/@value" />
+ </util:value>
+ </for-each>
+
+ <!-- constants -->
+ <for-each select="lv:const">
+ <util:value>
+ <value-of select="@value" />
+ </util:value>
+ </for-each>
+ </with-param>
+ </call-template>
+
+ <text>;</text>
+</template>
+
+
+
+<template match="lvp:*" mode="compile" priority="1">
+ <!-- do nothing with UI nodes -->
+</template>
+<template match="lvp:*" priority="1">
+ <!-- do nothing with UI nodes -->
+</template>
+
+<!--
+ Static code common to each rater
+
+ This is included here because XSLT cannot, without extension, read plain-text
+ files (the included file must be XML). If we separated it into another file,
+ we would be doing the same thing as we are doing here.
+
+ @return JavaScript code
+-->
+<template name="compiler:static">
+<text>
+<![CDATA[
+ var domains = {
+ 'integer': function( value )
+ {
+ return ( value == +value );
+ },
+
+ 'float': function( value )
+ {
+ return ( value == +value );
+ },
+
+ 'boolean': function( value )
+ {
+ return ( ( +value === 1 ) || ( +value === 0 ) );
+ },
+
+ 'string': function( value )
+ {
+ // well, everything is a string
+ return true;
+ }
+ };
+
+
+ function argCheck( args, params )
+ {
+ var req = {};
+
+ for ( var name in params )
+ {
+ // if the argument is not required, then we do not yet need to deal
+ // with it
+ if ( !( params[ name ].required ) )
+ {
+ continue;
+ }
+
+ // first, ensure that required arguments have been provided (note
+ // the strict check for .length; this is important, since it won't
+ // have a length property if it's not an array, in which case we do
+ // not want to trigger an error)
+ if ( !( args[ name ] ) || ( args[ name ].length === 0 ) )
+ {
+ throw Error( "argument required: " + name );
+ }
+
+ var value = args[ name ];
+
+ // next, ensure that the argument is within the domain of its type
+ vocalDomainCheck( name, params[ name ].type, deepClone( value ) );
+
+ // record that we required this param
+ req[ name ] = true;
+ }
+
+ return req;
+ }
+
+
+ function vocalDomainCheck( name, domain, value )
+ {
+ var default_val = ( rater.params[ name ] || {} )['default'];
+
+ if ( !( domainCheck( domain, value, default_val ) ) )
+ {
+ throw Error(
+ "argument '" + name + "' value outside domain of '" +
+ domain + "': " + JSON.stringify( value )
+ );
+ }
+
+ return true;
+ }
+
+
+ function domainCheck( domain, value, default_val )
+ {
+ var type;
+
+ // if it's an object, then the value is assumed to be an array
+ if ( typeof value === 'object' )
+ {
+ if ( value.length < 1 )
+ {
+ return true;
+ }
+
+ // clone before popping so that we don't wipe out any values that
+ // will be used
+ value = Array.prototype.slice.call( value );
+
+ // check each value recursively
+ return domainCheck( domain, value.pop(), default_val )
+ && domainCheck( domain, value, default_val );
+ }
+
+ if ( ( ( value === undefined ) || ( value === '' ) )
+ && ( default_val !== undefined )
+ )
+ {
+ value = +default_val;
+ }
+
+ if ( domains[ domain ] )
+ {
+ return domains[ domain ]( value );
+ }
+ else if ( type = types[ domain ] ) /** XXX: types global **/
+ {
+ // custom type checks are two-fold: ensure that the value is within
+ // the domain of its base type and that it is within its list of
+ // acceptable values
+ return !!( domainCheck( type.type, value )
+ && type.values[ value ]
+ );
+ }
+
+ // no domain found
+ return false;
+ }
+
+
+ /**
+ * Checks for matches against values for any param value
+ *
+ * A single successful match will result in a successful classification.
+ *
+ * For an explanation and formal definition of this algorithm, please see
+ * the section entitled "Classification Match (cmatch) Algorithm" in the
+ * manual.
+ *
+ * @param {Array|string} param value or set of values to check
+ * @param {Array|string} values or set of values to match against
+ * @param {Object} yield_to object to yield into
+ * @param {boolean} clear when true, AND results; otherwise, OR
+ *
+ * @return {boolean} true if any match is found, otherwise false
+ */
+ function anyValue( param, values, yield_to, ismatrix, clear, _id )
+ {
+ // convert everything to an array if needed (we'll assume all objects to
+ // be arrays; Array.isArray() is ES5-only) to make them easier to work
+ // with
+ if ( ( param === undefined ) || ( param === null ) )
+ {
+ // according to the specification, an undefined input vector should
+ // yield an empty result set, which in turn will be interpreted as
+ // false (yield_to is the result vector)
+ param = [];
+ }
+ else if ( typeof param !== 'object' )
+ {
+ param = [ param ];
+ }
+
+ var values_orig = values;
+ if ( typeof values !== 'object' )
+ {
+ // the || 0 here ensures that non-values are treated as 0, as
+ // mentioned in the specification
+ values = [ values || 0 ];
+ }
+ else
+ {
+ var tmp = [];
+ for ( var v in values )
+ {
+ tmp.push( v );
+ }
+
+ values = tmp;
+ }
+
+ // if no yield var name was provided, we'll just be storing in a
+ // temporary array which will be discarded when it goes out of scope
+ // (this is the result vector in the specification)
+ var store = yield_to || [];
+
+ var i = param.length,
+ found = false,
+ scalar = ( i === 0 ),
+ u = ( store.length === 0 ) ? clear : false;
+
+ while ( i-- )
+ {
+ // these var names are as they appear in the algorithm---temporary,
+ // and value
+ var t,
+ v = returnOrReduceOr( store[ i ], u );
+
+ // recurse on vectors
+ if ( typeof param[ i ] === 'object' )
+ {
+ var r = deepClone( store[ i ] || [] );
+ if ( typeof r !== 'object' )
+ {
+ r = [ r ];
+ }
+
+ var rfound = !!anyValue( param[ i ], values_orig, r, false, clear, _id );
+ found = ( found || rfound );
+
+ if ( ( typeof store[ i ] === 'object' )
+ || ( store[ i ] === undefined )
+ )
+ {
+ // we do not want to reduce; this is the match that we are
+ // interested in
+ store[ i ] = r;
+ continue;
+ }
+ else
+ {
+ t = returnOrReduceOr( r, clear );
+ }
+ }
+ else
+ {
+ // we have a scalar, folks!
+ scalar = true;
+ t = anyPredicate( values, ( param[ i ] || 0 ), i );
+ }
+
+ store[ i ] = +( ( clear )
+ ? ( v && t )
+ : ( v || t )
+ );
+
+ // equivalent of "Boolean Classification Match" section of manual
+ found = ( found || !!store[ i ] );
+ }
+
+ if ( store.length > param.length )
+ {
+ var sval = ( scalar ) ? anyPredicate( values, param[0] ) : null;
+ if ( typeof sval === 'function' )
+ {
+ // pass the scalar value to the function
+ sval = values[0]( param[0] );
+ }
+
+ // XXX: review the algorithm; this is a mess
+ for ( var k = param.length, l = store.length; k < l; k++ )
+ {
+ // note that this has the same effect as initializing (in the
+ // case of a scalar) the scalar to the length of the store
+ var v = +(
+ ( returnOrReduceOr( store[ k ], clear )
+ || ( !clear && ( scalar && sval ) )
+ )
+ && ( !clear || ( scalar && sval ) )
+ );
+
+ store[ k ] = ( scalar )
+ ? v
+ : [ v ];
+
+ found = ( found || !!v );
+ }
+ }
+
+ return found;
+ }
+
+
+ function anyPredicate( preds, value, index )
+ {
+ for ( var i in preds )
+ {
+ var p = preds[ i ];
+
+ if ( ( typeof p === 'function' )
+ && p( value, index )
+ )
+ {
+ return true;
+ }
+ // lazy equality intentional
+ else if ( p == value )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ function returnOrReduceOr( arr, c )
+ {
+ if ( arr === undefined )
+ {
+ return !!c;
+ }
+ else if ( !( arr.length ) )
+ {
+ return arr;
+ }
+
+ return reduce( arr, function( a, b )
+ {
+ return returnOrReduceOr( a, c ) || returnOrReduceOr( b, c );
+ } );
+ }
+
+
+ function returnOrReduceAnd( arr, c )
+ {
+ if ( arr === undefined )
+ {
+ return !!c;
+ }
+ else if ( !( arr.length ) )
+ {
+ return arr;
+ }
+
+ return reduce( arr, function( a, b )
+ {
+ return returnOrReduceAnd( a, c ) && returnOrReduceAnd( b, c );
+ } );
+ }
+
+
+ function deepClone( obj )
+ {
+ var objnew = [];
+
+ // if we were not given an object, then do nothing
+ if ( typeof obj !== 'object' )
+ {
+ return obj;
+ }
+
+ for ( var i in obj )
+ {
+ // deep-clone for matrices
+ objnew[ i ] = ( typeof obj[ i ] === 'object' )
+ ? deepClone( obj[ i ] )
+ : obj[ i ];
+ }
+
+ return objnew;
+ }
+
+
+ /**
+ * Converts a match set to an integer
+ *
+ * If the given set is an array, then return a sum of each of its boolean
+ * values (if any one is set, then it's 1); otherwise, cast the given
+ * value to a number, just in case it's not.
+ *
+ * This function does not check to ensure that the given set contains valid
+ * data.
+ *
+ * @param {*} match set to convert
+ *
+ * @return {number} 1 or 0
+ */
+ function matchSetToInt( match )
+ {
+ if ( Array.isArray( match ) )
+ {
+ return reduce( match, function( a, b )
+ {
+ return a + b;
+ } );
+ }
+
+ return +match;
+ }
+
+
+ function cmatch( match, nomatch )
+ {
+ var len = 0,
+ cmatch = [];
+
+ // the length of our set should be the length of the largest set (we
+ // will not know this until runtime)
+ for ( var i in match )
+ {
+ // note that this has the consequence of not matching on scalar-only
+ // classifications...is this what we want? If not, we need to
+ // document a proper solution.
+ if ( ( match[ i ] || [] ).length > len )
+ {
+ len = match[ i ].length;
+ }
+ }
+
+ for ( var i in nomatch )
+ {
+ if ( ( nomatch[ i ] || [] ).length > len )
+ {
+ len = nomatch[ i ].length;
+ }
+ }
+
+ while ( len-- )
+ {
+ var fail = false;
+
+ for ( var i in match )
+ {
+ // if we're dealing with a scalar, then it should be used for
+ // every index
+ var mdata = ( ( typeof match[ i ] !== 'object' )
+ ? match[ i ]
+ : ( match[ i ] || [] )[ len ]
+ );
+
+ if ( !( matchSetToInt( mdata ) ) )
+ {
+ fail = true;
+ }
+ }
+
+ // XXX duplicate code
+ for ( var i in nomatch )
+ {
+ // if we're dealing with a scalar, then it should be used for
+ // every index
+ var mdata = ( ( typeof nomatch[ i ] !== 'object' )
+ ? nomatch[ i ]
+ : ( nomatch[ i ] || [] )[ len ]
+ );
+
+ if ( matchSetToInt( mdata ) !== 0 )
+ {
+ fail = true;
+ }
+ }
+
+ cmatch[ len ] = ( fail ) ? 0 : 1;
+ }
+
+ return cmatch;
+ }
+
+
+ /**
+ * Return the length of the longest set
+ *
+ * Provide each set as its own argument.
+ *
+ * @return number length of longest set
+ */
+ function longerOf()
+ {
+ var i = arguments.length,
+ len = 0;
+ while ( i-- )
+ {
+ var thislen = arguments[ i ].length;
+
+ if ( thislen > len )
+ {
+ len = thislen;
+ }
+ }
+
+ return len;
+ }
+
+
+ /**
+ * Some browsers don't support Array.reduce(), and adding to the prototype
+ * causes problems since we cannot make it non-enumerable in those browsers
+ * due to broken Object.defineProperty implementations (IE8).
+ */
+ function reduce( arr, c )
+ {
+ var ret = arr[ 0 ],
+ i = 0, // skip first
+ l = arr.length;
+
+ while ( ++i < l )
+ {
+ ret = c( ret, arr[ i ] );
+ }
+
+ // note that this will have the effet of returning the first element if
+ // there are none/no more than 1
+ return ret;
+ }
+
+
+ /* scalar to vector */
+ function stov( s, n )
+ {
+ // already a vector
+ if ( typeof s === 'object' )
+ {
+ // if the length is only one, then we can pretend that it is a
+ // scalar (unless the requested length is one, in which case it is
+ // utterly pointless to continue)
+ if ( ( n === 1 ) || ( s.length !== 1 ) )
+ {
+ return s;
+ }
+
+ s = s[ 0 ];
+ }
+
+ var v = [];
+ for ( var i = 0; i < n; i++ )
+ {
+ v.push( s );
+ }
+
+ return v;
+ }
+
+
+ function argreplace( orig, value )
+ {
+ if ( !( typeof orig === 'object' ) )
+ {
+ return value;
+ }
+
+ // we have an object; recurse
+ for ( var i in orig )
+ {
+ return argreplace( orig[ i ], value );
+ }
+ }
+
+
+ function init_defaults( args, params )
+ {
+ for ( var param in params )
+ {
+ var val = params[ param ]['default'];
+ if ( !val )
+ {
+ continue;
+ }
+
+ args[ param ] = set_defaults( args[ param ], val );
+ }
+ }
+
+
+ function set_defaults( input, value )
+ {
+ // scalar
+ if ( !( typeof input === 'object' ) )
+ {
+ return ( input === '' || input === undefined ) ? value : input;
+ }
+
+ // otherwise, assume array
+ var i = input.length;
+ var ret = [];
+ while ( i-- ) {
+ ret[i] = ( input[i] === '' ) ? value : input[i];
+ }
+ return ret;
+ }
+]]>
+</text>
+</template>
+
+</stylesheet>
diff --git a/src/current/compiler/linker.xsl b/src/current/compiler/linker.xsl
new file mode 100644
index 0000000..f6a0478
--- /dev/null
+++ b/src/current/compiler/linker.xsl
@@ -0,0 +1,1442 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Compiles rater XML into JavaScript
+
+ This stylesheet should be included by whatever is doing the processing and is
+ responsible for outputting the generated code in whatever manner is
+ appropriate (inline JS, a file, etc).
+-->
+
+<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:lv="http://www.lovullo.com/rater"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:l="http://www.lovullo.com/rater/linker"
+ xmlns:log="http://www.lovullo.com/logger"
+ xmlns:compiler="http://www.lovullo.com/rater/compiler"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+<xsl:include href="../include/preproc/symtable.xsl" />
+<xsl:include href="../include/util.xsl" />
+<xsl:include href="js.xsl" />
+
+<xsl:include href="linker/log.xsl" />
+
+<!-- indentation makes dep lists easier to mentally process -->
+<xsl:output indent="yes" />
+
+<!-- optional fragments to include in exit block, comma-delimited
+ (e.g. path/to/object/file/_fragment_) -->
+<xsl:param name="rater-exit-fragments" />
+
+<!--
+ Used to output a great deal of linker information for debugging
+
+ THIS WILL HAVE A PERFORMANCE HIT!
+-->
+<xsl:param name="l:aggressive-debug" select="false()" />
+
+<xsl:variable name="l:orig-root" as="document-node( element( lv:package ) )"
+ select="/" />
+
+<xsl:variable name="l:process-empty" as="element( l:pstack )">
+ <l:pstack />
+</xsl:variable>
+
+<xsl:variable name="l:stack-empty" as="element( l:sym-stack )">
+ <l:sym-stack />
+</xsl:variable>
+
+
+<xsl:template match="*" mode="l:link" priority="1">
+ <xsl:call-template name="log:error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>cannot link </xsl:text>
+ <xsl:value-of select="name()" />
+ <xsl:text>; must link program</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+
+<!-- entry point (if run directly) -->
+<xsl:template match="/" priority="1">
+ <xsl:apply-templates select="/lv:*" mode="l:link" />
+</xsl:template>
+
+
+<!--
+ We will only link program package
+
+ Raters are similar to shared object files (that is, packages), but explicitly
+ recognize the fact that linking should be done. They also contain definitions
+ for exit points (lv:yields); think of it like defining a main() function.
+-->
+<xsl:template match="lv:package[ @program='true' ]" mode="l:link" priority="5">
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>linking </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <!-- start by recursively discovering imported shared object files -->
+ <xsl:variable name="pre-deps" as="element( l:dep )">
+ <xsl:call-template name="log:debug">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>building dependency tree...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <l:dep>
+ <!-- empty stack -->
+ <xsl:apply-templates select="preproc:symtable" mode="l:depgen">
+ <xsl:with-param name="stack" select="$l:stack-empty" as="element( l:sym-stack )" />
+ </xsl:apply-templates>
+ </l:dep>
+ </xsl:variable>
+
+
+ <!-- a single-pass post-processing of the deps to resolve any final issues -->
+ <xsl:variable name="deps" as="element( l:dep )">
+ <xsl:call-template name="log:debug">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>resolving dependency tree...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:apply-templates select="$pre-deps" mode="l:resolv-deps" />
+ </xsl:variable>
+
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+
+ <!-- copy the dependency tree -->
+ <xsl:copy-of select="$deps" />
+
+ <!-- if map data was provided, generate the map -->
+ <xsl:variable name="maplink">
+ <xsl:apply-templates select="." mode="l:map">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+ </xsl:variable>
+
+ <xsl:if test="$maplink//l:map-error">
+ <xsl:call-template name="log:error">
+ <xsl:with-param name="name" select="'link'" />
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- all good. -->
+ <xsl:copy-of select="$maplink" />
+
+ <!-- link. -->
+ <l:exec>
+ <xsl:apply-templates select="." mode="l:link-deps">
+ <!-- TODO: remove this exception -->
+ <xsl:with-param name="deps" select="
+ $deps/preproc:sym[
+ not(
+ starts-with( @type, 'map' )
+ or starts-with( @type, 'retmap' )
+ )
+ ]
+ " />
+ </xsl:apply-templates>
+ </l:exec>
+ </xsl:copy>
+</xsl:template>
+
+
+<xsl:template mode="l:depgen" as="element( preproc:sym )*"
+ match="preproc:symtable">
+ <xsl:param name="stack" as="element( l:sym-stack )"
+ select="$l:stack-empty" />
+
+ <!-- we care only of the symbols used by lv:yields, from which all
+ dependencies may be derived (if it's not derivable from the yield
+ symbol, then it must not be used); note that lv:yield actually compiles
+ into a special symbol ___yield -->
+ <xsl:variable name="yields" as="element( preproc:sym )+">
+ <xsl:copy-of select="preproc:sym[ @name='___yield' ]" />
+
+ <!-- also include anything derivable from any @keep symbol, either local
+ or imported -->
+ <xsl:copy-of select="preproc:sym[ @keep='true' ]" />
+
+ <!-- TODO: these should be included as a consequence of the linking
+ process, not as an exception -->
+ <xsl:copy-of select="
+ preproc:sym[
+ @type='map' or @type='map:head' or @type='map:tail'
+ or @type='retmap' or @type='retmap:head' or @type='retmap:tail'
+ ]
+ " />
+
+ <!-- TODO: same as above -->
+ <xsl:copy-of select="preproc:sym[ @name='___worksheet' ]" />
+ </xsl:variable>
+
+ <!-- start at the top of the table and begin processing each symbol
+ individually, generating a dependency tree as we go -->
+ <xsl:variable name="result" as="element()+">
+ <xsl:call-template name="l:depgen-sym">
+ <xsl:with-param name="pending" select="$yields" />
+ <xsl:with-param name="stack" select="$stack" as="element( l:sym-stack )" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- stack will contain additional metadata -->
+ <xsl:sequence select="$result[ . instance of
+ element( preproc:sym ) ]" />
+</xsl:template>
+
+
+<xsl:template mode="l:resolv-deps" as="element( l:dep )"
+ priority="9"
+ match="l:dep">
+ <xsl:copy>
+ <xsl:apply-templates mode="l:resolv-deps" />
+ </xsl:copy>
+</xsl:template>
+
+
+<!-- replace marks with the symbols that they reference (note that the linker
+ will not add duplicates, so we needn't worry about them) -->
+<xsl:template mode="l:resolv-deps" as="element( preproc:sym )"
+ priority="8"
+ match="preproc:sym[ @l:mark-inclass ]">
+ <!-- FIXME: I sometimes return more than one symbol! -->
+ <xsl:variable name="sym" as="element( preproc:sym )*"
+ select=" root(.)/preproc:sym[
+ @name = current()/@name ]" />
+
+ <!-- sanity check; hopefully never necessary -->
+ <xsl:if test="not( $sym )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>l:mark-class found for non-existing symbol </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>; there is a bug in l:depgen-process-sym</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- copy the element and mark as inclass (no need to check @src, since at
+ this point, such conflicts would have already resulted in an error -->
+ <preproc:sym>
+ <xsl:sequence select="$sym/@*" />
+
+ <!-- override attribute -->
+ <xsl:attribute name="inclass" select="'true'" />
+ </preproc:sym>
+</xsl:template>
+
+
+<!-- any now-inclass symbols should be stripped of their original position -->
+<xsl:template mode="l:resolv-deps" priority="7"
+ match="preproc:sym[
+ @name = root(.)/preproc:sym[ @l:mark-inclass ]
+ /@name ]">
+ <!-- bye-bye -->
+</xsl:template>
+
+
+<xsl:template match="*" mode="l:resolv-deps" priority="1">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<!-- FIXME: I want element( l:preproc:sym ), but we also have
+ l:mark-inclass -->
+<xsl:template name="l:depgen-sym" as="element()*">
+ <xsl:param name="pending" as="element( preproc:sym )*" />
+ <xsl:param name="stack" as="element( l:sym-stack )" />
+ <xsl:param name="path" as="xs:string"
+ select="''" />
+ <xsl:param name="processing" as="element( l:pstack )"
+ select="$l:process-empty" />
+
+ <xsl:variable name="pend-count" as="xs:integer"
+ select="count( $pending )" />
+ <xsl:variable name="stack-count" as="xs:integer"
+ select="count( $stack/preproc:sym )" />
+ <xsl:variable name="process-count" as="xs:integer"
+ select="count( $processing/* )" />
+
+ <xsl:choose>
+ <!-- if there are no pending symbols left, then we are done; return the
+ stack -->
+ <xsl:when test="$pend-count = 0">
+ <xsl:sequence select="$stack/*" />
+ </xsl:when>
+
+
+ <xsl:otherwise>
+ <!-- take the first item from the pending list -->
+ <xsl:variable name="cur" as="element( preproc:sym )"
+ select="$pending[1]" />
+
+ <!-- aggressive debugging data -->
+ <xsl:if test="$l:aggressive-debug">
+ <xsl:call-template name="log:debug">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="$path" />
+ <xsl:text>) </xsl:text>
+ <xsl:value-of select="$pend-count" />
+ <xsl:text>p - </xsl:text>
+ <xsl:value-of select="$stack-count" />
+ <xsl:text>s - </xsl:text>
+ <xsl:value-of select="$process-count" />
+ <xsl:text>r - </xsl:text>
+ <xsl:value-of select="$cur/@name" />
+ <xsl:text> [s:: </xsl:text>
+ <xsl:value-of select="$stack/preproc:sym/@name" />
+ <xsl:text> ::s] [r:: </xsl:text>
+ <xsl:value-of select="$processing/preproc:sym/@name" />
+ <xsl:text>::r]</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="pkg-seen" as="xs:boolean"
+ select="(
+ ( $cur/@src = '' or not( $cur/@src ) )
+ and $stack/preproc:pkg-seen/@src = ''
+ )
+ or $cur/@src = $stack/preproc:pkg-seen/@src" />
+
+ <xsl:variable name="newpending" as="element( l:pending )">
+ <l:pending>
+ <xsl:sequence select="$pending" />
+
+ <!-- if this is the first time seeing this package, then pend its
+ @keep's for processing -->
+ <xsl:if test="not( $pkg-seen )">
+ <xsl:message select="'[link] found package ', $cur/@src" />
+
+ <xsl:variable name="document" as="element( lv:package )"
+ select="if ( not( $cur/@src or $cur/@src = '' ) ) then
+ $l:orig-root/lv:package
+ else
+ document( concat( $cur/@src, '.xmlo' ),
+ $l:orig-root )
+ /lv:package" />
+
+ <xsl:variable name="keeps" as="element( preproc:sym )*" select="
+ $document/preproc:symtable/preproc:sym[
+ (
+ @keep='true'
+ or ( $l:orig-root/lv:package/@auto-keep-imports='true'
+ and ( @type = 'class'
+ or @type = 'cgen' ) )
+ )
+ and not(
+ $l:orig-root/lv:package/@no-extclass-keeps='true'
+ and @extclass='true'
+ )
+ and not( @name=$pending/@name )
+ and not( @name=$stack/preproc:sym/@name )
+ ]
+ " />
+
+ <xsl:variable name="keepdeps" as="element( preproc:sym )*">
+ <xsl:call-template name="l:dep-aug">
+ <xsl:with-param name="cur" select="$cur" />
+ <xsl:with-param name="deps" select="$keeps" />
+ <xsl:with-param name="proc-barrier" select="true()" />
+ <xsl:with-param name="parent-name"
+ select="concat( 'package ', $cur/@src )" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:sequence select="$keepdeps" />
+ </xsl:if>
+ </l:pending>
+ </xsl:variable>
+
+
+ <xsl:variable name="stack-seen" as="element( l:sym-stack )">
+ <l:sym-stack>
+ <xsl:if test="not( $pkg-seen )">
+ <xsl:sequence select="$stack/*" />
+ <preproc:pkg-seen src="{$cur/@src}" />
+ </xsl:if>
+ </l:sym-stack>
+ </xsl:variable>
+
+ <xsl:variable name="newstack" as="element( l:sym-stack )"
+ select="if ( $pkg-seen ) then
+ $stack
+ else
+ $stack-seen" />
+
+ <xsl:apply-templates select="$cur" mode="l:depgen-process-sym">
+ <xsl:with-param name="pending" select="$newpending/*" />
+ <xsl:with-param name="stack" select="$newstack" />
+ <xsl:with-param name="path" select="$path" />
+ <xsl:with-param name="processing" select="
+ if ( $cur/@l:proc-barrier = 'true' )
+ then $l:process-empty
+ else
+ $processing
+ " />
+ </xsl:apply-templates>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:function name="l:resolv-extern" as="element( preproc:sym )">
+ <xsl:param name="sym" as="element( preproc:sym )" />
+
+ <xsl:variable name="name" as="xs:string"
+ select="$sym/@name" />
+
+ <!-- there is no reason (in the current implementation) that this
+ should _not_ have already been resolved in the package being
+ linked -->
+ <xsl:variable name="pkg" as="element( lv:package )" select="
+ $l:orig-root/lv:package" />
+
+ <xsl:variable name="resolv" as="element( preproc:sym )?"
+ select="$pkg/preproc:symtable/preproc:sym[
+ @name=$name ]" />
+
+ <xsl:choose>
+ <!-- if this symbol is not external, then we have found it -->
+ <xsl:when test="$resolv and not( $resolv/@extern )">
+ <xsl:sequence select="$resolv" />
+ </xsl:when>
+
+ <!-- if there is no more stack to check and we have not found the symbol,
+ then this is a problem (this should never happen) -->
+ <xsl:otherwise>
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>unresolved extern </xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text> (declared by `</xsl:text>
+ <xsl:value-of select="$sym/@src" />
+ <xsl:text>')</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:function>
+
+
+<!--
+ Resolves externs before processing them
+
+ External symbols need special treatment; unlike other symbols, which are both
+ declared and defined within the same package, externs are declared but their
+ definitions are deferred to a later package. This allows using a value from
+ another package in a C-style reverse-inheritence kind of way that is
+ generally not a good idea, but sometimes necessary.
+
+ When a package imports another package that declares an extern, the importing
+ package must either declare that symbol as an extern or provide a definition.
+ This is important, as it provides us with an easy means of resolution:
+ traverse up the stack of symbols that are being processed, check their
+ containing packages and see if there is a definition for the symbol. At some
+ point, assuming that the shared object files are properly built, we must
+ arrive at the definition. Since the symbol is defined within that package,
+ the object file will also contain its dependency list.
+-->
+<xsl:template match="preproc:sym[ @extern='true' ]" mode="l:depgen-process-sym" priority="5">
+ <xsl:param name="pending" as="element( preproc:sym )*" />
+ <xsl:param name="stack" as="element( l:sym-stack )" />
+ <xsl:param name="path" as="xs:string" />
+ <xsl:param name="processing" as="element( l:pstack )" />
+
+ <xsl:variable name="cur" select="." />
+
+ <xsl:variable name="eresolv" as="element( preproc:sym )*"
+ select="l:resolv-extern( $cur )" />
+
+ <!-- were we able to resolve the symbol? -->
+ <xsl:if test="empty( $eresolv )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>could not resolve external symbol `</xsl:text>
+ <xsl:value-of select="$cur/@name" />
+ <xsl:text>'</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- in the event that we are importing symbols from program packages (which
+ hopefully is a rare case), we may have external symbols that resolve to
+ the same package; filter out duplicates -->
+ <xsl:variable name="eresolv-uniq" as="element( preproc:sym )"
+ select="$eresolv[
+ not( @src = $eresolv[ not( current() ) ]/@src ) ]" />
+
+ <!-- did we find more than one? (that would be very bad and likely represents
+ a symbol table generation bug) -->
+ <xsl:if test="count( $eresolv-uniq ) gt 1">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>ambiguous external symbol `</xsl:text>
+ <xsl:value-of select="$cur/@name" />
+ <xsl:text>'; resolution failed (found </xsl:text>
+ <xsl:for-each select="$eresolv-uniq">
+ <xsl:if test="position() > 1">
+ <xsl:text>; </xsl:text>
+ </xsl:if>
+ <xsl:value-of select="@src" />
+ </xsl:for-each>
+ <xsl:text>); pulled in by: </xsl:text>
+
+ <!-- help the user figure out how this happened -->
+ <xsl:for-each select="$processing/preproc:sym">
+ <xsl:if test="position() gt 1">
+ <xsl:text> - </xsl:text>
+ </xsl:if>
+ <xsl:value-of select="concat( @src, '/', @name )" />
+ </xsl:for-each>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:call-template name="log:debug">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg"
+ select="concat(
+ 'external symbol ', $cur/@name,
+ ' resolved to ',
+ ( if ( $eresolv-uniq/@src ) then
+ $eresolv-uniq/@src
+ else '' ),
+ '/',
+ $eresolv-uniq/@name )" />
+ </xsl:call-template>
+
+ <!-- use the resolved symbol in place of the original extern -->
+ <xsl:apply-templates select="$eresolv-uniq" mode="l:depgen-process-sym">
+ <xsl:with-param name="pending" select="$pending" />
+ <xsl:with-param name="stack" select="$stack" as="element( l:sym-stack )" />
+ <xsl:with-param name="path" select="$path" />
+ <xsl:with-param name="processing" select="$processing" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<xsl:template mode="l:depgen-process-sym" priority="1"
+ match="preproc:sym">
+ <xsl:param name="pending" as="element( preproc:sym )*" />
+ <xsl:param name="stack" as="element( l:sym-stack )" />
+ <xsl:param name="path" as="xs:string" />
+ <xsl:param name="processing" as="element( l:pstack )" />
+
+ <xsl:variable name="cur" as="element( preproc:sym )"
+ select="." />
+
+ <!-- determines if the compile destination for these dependencies will be
+ within the classifier; this is the case for all class dependencies
+ *unless* the class is external -->
+ <xsl:variable name="inclass" as="xs:boolean"
+ select="( ( $cur/@type='class' )
+ and not( $cur/@extclass='true' ) )
+ or $processing/preproc:sym[
+ @type='class'
+ and not( @extclass='true' ) ]" />
+
+ <!-- perform circular dependency check and blow up if found (we cannot choose
+ a proper linking order without a correct dependency tree); the only
+ exception is if the circular dependency is a function, since that simply
+ implies recursion, which we can handle just fine -->
+ <xsl:variable name="circ" as="element( preproc:sym )*"
+ select="$processing/preproc:sym[
+ @name=$cur/@name
+ and @src=$cur/@src ]" />
+
+ <xsl:choose>
+ <!-- non-function; fatal -->
+ <xsl:when test="$circ
+ and $circ[ not( @type='func' ) ]
+ ">
+ <xsl:call-template name="l:err-circular">
+ <xsl:with-param name="stack" select="$processing" />
+ <xsl:with-param name="cur" select="$cur" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- function; we've done all we need to, so do not re-link
+ (recursive call) -->
+ <xsl:when test="$circ">
+ <!-- continue processing; leave stack unchanged -->
+ <xsl:call-template name="l:depgen-sym">
+ <xsl:with-param name="pending" select="remove( $pending, 1 )" />
+ <xsl:with-param name="processing" select="$processing" />
+ <xsl:with-param name="stack" select="$stack" as="element( l:sym-stack )" />
+ </xsl:call-template>
+ </xsl:when>
+
+
+ <!-- process; TODO: good refactoring point; large template -->
+ <xsl:otherwise>
+ <xsl:variable name="existing" as="element( preproc:sym )*"
+ select="$stack/preproc:sym[
+ @name=$cur/@name ]" />
+
+ <xsl:variable name="src-conflict" as="element( preproc:sym )*"
+ select="if ( not( $cur/@src ) or $cur/@src = '' ) then
+ ()
+ else
+ $existing[ not( @src = $cur/@src ) ]" />
+
+ <xsl:if test="$src-conflict">
+ <xsl:call-template name="log:error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>symbol name is not unique: `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' found in</xsl:text>
+ <xsl:value-of select="$cur/@src" />
+
+ <xsl:for-each select="$src-conflict">
+ <xsl:text> and </xsl:text>
+ <xsl:value-of select="@src" />
+ </xsl:for-each>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- determine if class already exists, but needs to be marked for
+ inclusion within the classifier -->
+ <xsl:variable name="needs-class-mark" as="xs:boolean"
+ select="$existing
+ and $inclass
+ and not( $existing/@inclass='true' )
+ and not( $stack/preproc:sym[
+ @l:mark-inclass
+ and @name=$cur/@name
+ and @src=$cur/@src ] )" />
+
+ <!-- continue with the remainder of the symbol list -->
+ <xsl:call-template name="l:depgen-sym">
+ <xsl:with-param name="pending" select="remove( $pending, 1 )" />
+ <xsl:with-param name="processing" select="$processing" />
+
+ <xsl:with-param name="stack" as="element( l:sym-stack )">
+ <!-- if this symbol already exists on the stack, then there is no use
+ re-adding it (note that we check both the symbol name and its source
+ since symbols could very well share a name due to exporting rules) -->
+ <xsl:choose>
+ <xsl:when test="not( $existing ) or $needs-class-mark">
+ <!-- does this symbol have any dependencies? -->
+ <xsl:variable name="deps" as="element( preproc:sym )*">
+ <xsl:apply-templates select="$cur" mode="l:depgen-sym" />
+ </xsl:variable>
+
+ <!-- determine our path -->
+ <xsl:variable name="mypath">
+ <xsl:call-template name="preproc:get-path">
+ <xsl:with-param name="path" select="$cur/@src" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- augment each of the dep paths with our own (this ultimately
+ creates symbol paths relative to the rater) -->
+ <xsl:variable name="deps-aug" as="element( preproc:sym )*">
+ <xsl:call-template name="l:dep-aug">
+ <xsl:with-param name="cur" select="$cur" />
+ <xsl:with-param name="deps" select="$deps" />
+ <xsl:with-param name="inclass" select="$inclass" />
+ <xsl:with-param name="mypath" select="$mypath" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <l:sym-stack>
+ <!-- process the dependencies (note that this has the effect of
+ outputting the existing stack as well, which is why we have
+ not yet done so) -->
+ <xsl:call-template name="l:depgen-sym">
+ <xsl:with-param name="pending" select="$deps-aug" />
+ <xsl:with-param name="stack" select="$stack" as="element( l:sym-stack )" />
+ <xsl:with-param name="path" select="$mypath" />
+ <xsl:with-param name="processing" as="element( l:pstack )">
+ <l:pstack>
+ <xsl:sequence select="$processing/*" />
+ <xsl:sequence select="$cur" />
+ </l:pstack>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <!-- finally, we can output ourself -->
+ <xsl:choose>
+ <!-- existing symbol needs to be marked -->
+ <xsl:when test="$needs-class-mark">
+ <preproc:sym l:mark-inclass="true" name="{$cur/@name}" src="{$cur/@src}" />
+ </xsl:when>
+
+ <!-- new symbol -->
+ <xsl:otherwise>
+ <preproc:sym>
+ <xsl:sequence select="$cur/@*" />
+ <xsl:attribute name="inclass" select="$inclass" />
+ </preproc:sym>
+ </xsl:otherwise>
+ </xsl:choose>
+ </l:sym-stack>
+ </xsl:when>
+
+
+ <!-- already exists; leave stack unchanged -->
+ <xsl:otherwise>
+ <xsl:sequence select="$stack" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template name="l:dep-aug" as="element( preproc:sym )*">
+ <xsl:param name="cur" as="element( preproc:sym )" />
+ <xsl:param name="deps" as="element( preproc:sym )*" />
+ <xsl:param name="inclass" as="xs:boolean"
+ select="false()" />
+ <xsl:param name="proc-barrier" as="xs:boolean"
+ select="false()" />
+ <xsl:param name="parent-name" as="xs:string"
+ select="$cur/@name" />
+ <xsl:param name="mypath">
+ <!-- default -->
+ <xsl:call-template name="preproc:get-path">
+ <xsl:with-param name="path" select="$cur/@src" />
+ </xsl:call-template>
+ </xsl:param>
+
+ <xsl:for-each select="$deps">
+ <xsl:copy>
+ <xsl:copy-of select="@*" />
+
+ <xsl:variable name="newsrc">
+ <xsl:choose>
+ <!-- if a source path is provided, then we must take it
+ relative to the current symbol's package's directory
+ -->
+ <xsl:when test="@src">
+ <xsl:call-template name="preproc:resolv-path">
+ <xsl:with-param name="path">
+ <xsl:value-of select="$mypath" />
+
+ <xsl:if test="$mypath and not( $mypath='' ) and @src and not( @src='' )">
+ <xsl:text>/</xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="@src" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- if no source path is set, then it exists in the same
+ package as we do -->
+ <xsl:otherwise>
+ <xsl:value-of select="$cur/@src" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- set new src path -->
+ <xsl:attribute name="src" select="$newsrc" />
+
+ <!-- flag for inclusion into classifier, if necessary -->
+ <xsl:if test="$inclass">
+ <xsl:attribute name="inclass" select="$inclass" />
+ </xsl:if>
+
+ <xsl:if test="$proc-barrier">
+ <xsl:attribute name="l:proc-barrier" select="'true'" />
+ </xsl:if>
+
+
+ <xsl:if test="$l:aggressive-debug">
+ <xsl:call-template name="log:debug">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:value-of select="$parent-name" />
+ <xsl:text> depends upon </xsl:text>
+ <xsl:if test="@extern='true'">
+ <xsl:text>external </xsl:text>
+ </xsl:if>
+ <xsl:value-of select="concat( @type, ' ', $newsrc, '/', @name )" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:copy>
+ </xsl:for-each>
+</xsl:template>
+
+
+
+<!-- TODO: some better way. -->
+<xsl:template match="preproc:sym[ starts-with( @type, 'map' ) or starts-with( @type, 'retmap' ) ]"
+ mode="l:depgen-sym" priority="7">
+
+ <!-- do not process deps -->
+</xsl:template>
+
+
+<xsl:template mode="l:depgen-sym" as="element()*"
+ match="preproc:pkg-seen"
+ priority="5">
+ <xsl:sequence select="." />
+</xsl:template>
+
+
+<xsl:template mode="l:depgen-sym" as="element( preproc:sym )*"
+ match="preproc:sym[
+ @type='const' ]"
+ priority="7">
+ <!-- no need to link this; it has no code associated with it -->
+</xsl:template>
+
+<xsl:template mode="l:depgen-sym" as="element( preproc:sym )*"
+ match="preproc:sym"
+ priority="5">
+ <!-- get the source package -->
+ <xsl:variable name="pkg" as="element( lv:package )?" select="
+ if ( @src and not( @src='' ) ) then
+ document( concat( @src, '.xmlo' ), $l:orig-root )/lv:*
+ else
+ $l:orig-root/lv:package
+ " />
+
+ <xsl:variable name="name" as="xs:string"
+ select="@name" />
+ <xsl:variable name="deps" as="element( preproc:sym-dep )?"
+ select="$pkg/preproc:sym-deps
+ /preproc:sym-dep[ @name=$name ]" />
+
+ <!-- if we could not locate the dependencies, then consider this to be an
+ error (even if there are no deps, there should still be a list dfn) -->
+ <xsl:if test="not( $deps )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>could not locate dependency list for </xsl:text>
+ <xsl:value-of select="@src" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="@name" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="symtable" as="element( preproc:symtable )"
+ select="$pkg/preproc:symtable" />
+
+ <xsl:for-each select="
+ $deps/preproc:sym-ref[
+ not( @parent=$deps/preproc:sym-ref/@name )
+ ]
+ ">
+ <xsl:variable name="sym-ref" as="xs:string"
+ select="@name" />
+
+ <xsl:variable name="sym" as="element( preproc:sym )?"
+ select="$symtable/preproc:sym[ @name=$sym-ref ]" />
+
+ <!-- if we cannot locate the referenced symbol, then that too is an error
+ -->
+ <xsl:if test="not( $sym )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>failed locating dependency symbol `</xsl:text>
+ <xsl:value-of select="$sym-ref" />
+ <xsl:text>'</xsl:text>
+ <xsl:text> from package </xsl:text>
+ <xsl:value-of select="$pkg/@name" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- output the symbol, sans children -->
+ <preproc:sym>
+ <xsl:sequence select="$sym/@*" />
+ </preproc:sym>
+ </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template mode="l:depgen-sym"
+ match="*"
+ priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>internal error: unknown symbol for l:depgen-sym: </xsl:text>
+ <xsl:sequence select="." />
+ </xsl:message>
+</xsl:template>
+
+
+<xsl:template name="l:err-circular">
+ <xsl:param name="stack" />
+ <xsl:param name="cur" />
+
+ <xsl:call-template name="log:error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>circular dependency </xsl:text>
+
+ <xsl:text>(</xsl:text>
+ <xsl:value-of select="concat( $cur/@src, '/', $cur/@name )" />
+ <xsl:text>): </xsl:text>
+
+ <xsl:for-each select="$stack//preproc:sym">
+ <xsl:if test="position() > 1">
+ <xsl:text> - </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="concat( @src, '/', @name )" />
+ </xsl:for-each>
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+
+
+<!--
+ Links the object code for each dependency
+
+ And here is where the magic happens. This will take the generated dependency
+ list and, in order of the list, combine the object code from its source
+ package's object file. The result is a fully linked executable.
+
+ Not only is this one of the most interesting parts, this is also one of the
+ fastest; all the hard work has already been done.
+
+ Note: though the linked code is suitable for execution, it is up to a
+ particular implementation to decide how it should be wrapped and invoked.
+-->
+<xsl:template match="lv:package" mode="l:link-deps">
+ <xsl:param name="deps" />
+
+ <!-- to make this executable, we must compile an entry point -->
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>compiling entry point...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ <xsl:apply-templates select="." mode="compiler:entry" />
+
+
+ <xsl:apply-templates select="." mode="l:link-meta">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="." mode="l:link-worksheet">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="." mode="l:link-classifier">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="." mode="l:link-params">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="." mode="l:link-types">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="." mode="l:link-funcs">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="." mode="l:link-rater">
+ <xsl:with-param name="deps" select="$deps" />
+ </xsl:apply-templates>
+
+
+ <!-- finally, finish up -->
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>compiling exit...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:value-of select="@name" />
+ <xsl:text> compilation complete.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:function name="l:link-exit-fragments" as="xs:string*">
+ <xsl:param name="paths" as="xs:string" />
+ <xsl:param name="context" as="node()" />
+
+ <xsl:variable name="split" as="xs:string*"
+ select="tokenize( $paths, ',' )" />
+
+ <xsl:variable name="base-uri" as="xs:anyURI"
+ select="base-uri( $context )" />
+
+ <xsl:for-each select="$split">
+ <xsl:variable name="fragment" as="xs:string?"
+ select="l:get-fragment-by-path( ., $base-uri )" />
+
+ <xsl:if test="empty( $fragment )">
+ <xsl:call-template name="log:error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>fatal: missing exit fragment: </xsl:text>
+ <xsl:value-of select="." />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:sequence select="$fragment" />
+ </xsl:for-each>
+</xsl:function>
+
+
+<xsl:template match="lv:package" mode="l:link-meta">
+ <xsl:param name="deps" as="element( preproc:sym )*" />
+
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>** linking metadata...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:apply-templates select="." mode="l:do-link">
+ <xsl:with-param name="symbols" select="
+ $deps[ @type='meta' ]" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="l:link-worksheet">
+ <xsl:param name="deps" as="element( preproc:sym )*" />
+
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>** linking worksheet...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:apply-templates select="." mode="l:do-link">
+ <xsl:with-param name="symbols" select="
+ $deps[ @type='worksheet' ]" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="l:link-classifier">
+ <xsl:param name="deps" />
+
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>** linking classifier...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <!-- link everything that shall be a part of the classifier -->
+ <xsl:apply-templates select="." mode="compiler:entry-classifier" />
+ <xsl:apply-templates select="." mode="l:do-link">
+ <xsl:with-param name="symbols" select="
+ $deps[
+ @inclass='true'
+ and not( @type='param' )
+ and not( @type='type' )
+ and not( @type='meta' )
+ and not( @type='worksheet' )
+ ]
+ " />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="." mode="compiler:exit-classifier" />
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="l:link-params">
+ <xsl:param name="deps" />
+
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>** linking global params...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <!-- link all params -->
+ <xsl:apply-templates select="." mode="l:do-link">
+ <xsl:with-param name="symbols" select="
+ $deps[ @type='param' ]
+ " />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="l:link-types">
+ <xsl:param name="deps" />
+
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>** linking types...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <!-- link all params -->
+ <xsl:apply-templates select="." mode="l:do-link">
+ <xsl:with-param name="symbols" select="
+ $deps[ @type='type' ]
+ " />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="l:link-funcs">
+ <xsl:param name="deps" />
+
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>** linking functions...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <!-- link all params -->
+ <xsl:apply-templates select="." mode="l:do-link">
+ <xsl:with-param name="symbols" select="
+ $deps[ @type='func' ]
+ " />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="l:link-rater">
+ <xsl:param name="deps" />
+
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>** linking rater...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:apply-templates select="." mode="compiler:entry-rater" />
+
+ <!-- TODO: this list of exclusions is a mess -->
+ <xsl:apply-templates select="." mode="l:do-link">
+ <xsl:with-param name="symbols" select="
+ $deps[
+ not( @inclass='true' )
+ and not( @type='param' )
+ and not( @type='type' )
+ and not( @type='func' )
+ and not( @type='meta' )
+ and not( @type='worksheet' )
+ ]
+ " />
+ </xsl:apply-templates>
+
+ <xsl:sequence select="l:link-exit-fragments(
+ $rater-exit-fragments,
+ . )" />
+
+ <xsl:apply-templates select="." mode="compiler:exit-rater">
+ <xsl:with-param name="symbols" select="$deps" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<xsl:template match="lv:package" mode="l:do-link">
+ <xsl:param name="symbols" />
+
+ <!-- link each of the dependencies -->
+ <xsl:for-each select="$symbols">
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>linking </xsl:text>
+ <xsl:value-of select="concat( @type, ' ', @src, '/', @name )" />
+ <xsl:text>...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:apply-templates select="." mode="l:link-deps" />
+ </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym[ @type='lparam' ]" mode="l:link-deps" priority="9">
+ <!-- no object code for local params -->
+</xsl:template>
+
+<!-- priority of 7 because there may otherwise be some ambiguities
+ (e.g. with lparam) -->
+<xsl:template match="preproc:sym[ @parent ]" mode="l:link-deps" priority="7">
+ <!-- if a parent is defined, then its symbol will have been sufficient -->
+</xsl:template>
+
+<xsl:template match="preproc:sym" mode="l:link-deps" priority="5">
+ <!-- consult the source package for the last time... -->
+ <xsl:variable name="pkg" select="
+ if ( @src and not( @src='' ) ) then
+ document( concat( @src, '.xmlo' ), $l:orig-root )/lv:*
+ else
+ $l:orig-root/lv:package
+ " />
+
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="objcode" as="xs:string?"
+ select="l:get-fragment( $pkg, $name )" />
+
+ <xsl:if test="empty( $objcode )">
+ <xsl:if test="not( @type='param'
+ or ( @type='const' and @dim='0' )
+ or @type='tpl'
+ or @type='meta'
+ or not( @type='worksheet' ) )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>missing object code for symbol </xsl:text>
+ <xsl:value-of select="concat( @src, '/', @name )" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:if>
+
+ <xsl:copy-of select="$objcode" />
+</xsl:template>
+
+
+<xsl:function name="l:get-fragment-by-path" as="xs:string?">
+ <xsl:param name="path" as="xs:string" />
+ <xsl:param name="base-uri" as="xs:anyURI" />
+
+ <xsl:variable name="pkg-path" as="xs:string">
+ <xsl:call-template name="preproc:get-path">
+ <xsl:with-param name="path" select="$path" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="fragment-name" as="xs:string">
+ <xsl:call-template name="preproc:get-basename">
+ <xsl:with-param name="path" select="$path" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="package-uri" as="xs:anyURI"
+ select="resolve-uri(
+ concat( $pkg-path, '.xmlo' ),
+ $base-uri )" />
+
+ <xsl:variable name="doc" as="document-node()"
+ select="doc( $package-uri )" />
+
+ <xsl:if test="empty( $doc )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>could not locate package for exit-fragment: </xsl:text>
+ <xsl:value-of select="$package-uri" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="package" as="element()"
+ select="$doc/*" />
+
+ <xsl:sequence select="l:get-fragment(
+ $package,
+ $fragment-name )" />
+</xsl:function>
+
+
+<xsl:function name="l:get-fragment" as="xs:string?">
+ <xsl:param name="package" as="element()" />
+ <xsl:param name="name" as="xs:string" />
+
+ <xsl:variable name="fragment" as="element( preproc:fragment )?"
+ select="$package/preproc:fragments/preproc:fragment[
+ @id = $name ]" />
+
+ <xsl:sequence select="$fragment/text()" />
+</xsl:function>
+
+
+<xsl:template match="lv:package" mode="l:map" priority="5">
+ <!-- it is important that we check against the dependencies actually compiled
+ rather than the list of available symbols -->
+ <xsl:param name="deps" as="element( l:dep )" />
+
+ <xsl:variable name="syms" as="element( preproc:sym )*"
+ select="preproc:symtable/preproc:sym" />
+
+ <xsl:variable name="mapsyms" as="element( preproc:sym )*"
+ select="$syms[ @type='map' ]" />
+ <xsl:variable name="retmapsyms" as="element( preproc:sym )*"
+ select="$syms[ @type='retmap' ]" />
+
+ <!-- get head and tail -->
+ <xsl:variable name="head" select="$syms[ @type='map:head' ]" />
+ <xsl:variable name="tail" select="$syms[ @type='map:tail' ]" />
+ <xsl:variable name="ret-head" select="$syms[ @type='retmap:head' ]" />
+ <xsl:variable name="ret-tail" select="$syms[ @type='retmap:tail' ]" />
+
+ <xsl:if test="count( $mapsyms ) gt 0">
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>generating input map...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:if test="not( $head ) or not( $tail )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>missing object code for input map head or tail</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- input map -->
+ <l:map-exec>
+ <xsl:apply-templates select="$head" mode="l:link-deps" />
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates select="$mapsyms" mode="l:map">
+ <xsl:with-param name="symtable" select="$deps" />
+ <!-- TODO -->
+ <xsl:with-param name="ignore-error" select="true()" />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="$tail" mode="l:link-deps" />
+ </l:map-exec>
+ </xsl:if>
+
+
+ <!-- TODO: very similar to above; refactor -->
+ <xsl:if test="count( $retmapsyms ) gt 0">
+ <xsl:call-template name="log:info">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>generating return map...</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:if test="not( $ret-head ) or not( $ret-tail )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>missing object code for return map head or tail</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- return map -->
+ <l:retmap-exec>
+ <xsl:apply-templates select="$ret-head" mode="l:link-deps" />
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates select="$retmapsyms" mode="l:map">
+ <xsl:with-param name="type" select="'return'" />
+ <xsl:with-param name="from" select="'input'" />
+ <xsl:with-param name="symtable" select="$deps" />
+ </xsl:apply-templates>
+ <xsl:apply-templates select="$ret-tail" mode="l:link-deps" />
+ </l:retmap-exec>
+ </xsl:if>
+</xsl:template>
+
+
+<xsl:template match="preproc:sym" mode="l:map" priority="5">
+ <xsl:param name="symtable" as="element( l:dep )" />
+ <xsl:param name="type" as="xs:string"
+ select="'input'" />
+ <xsl:param name="from" as="xs:string"
+ select="'destination'" />
+ <xsl:param name="ignore-error" as="xs:boolean"
+ select="false()" />
+
+ <xsl:variable name="name" as="xs:string"
+ select="@name" />
+ <xsl:variable name="src" as="xs:string"
+ select="@src" />
+
+ <!-- map symbols must always be remote -->
+ <xsl:variable name="pkg" as="element( lv:package )"
+ select="document( concat( @src, '.xmlo' ), . )
+ /lv:package" />
+
+ <!-- get map symbol dependencies -->
+ <xsl:variable name="deps" as="element( preproc:sym-dep )*"
+ select="$pkg/preproc:sym-deps/
+ preproc:sym-dep[ @name=$name ]" />
+
+ <xsl:if test="not( $deps )">
+ <xsl:call-template name="log:internal-error">
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:text>could not locate symbol dependencies: </xsl:text>
+ <xsl:value-of select="concat( @src, '/', @name )" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- FIXME: we should not have to check for @yields here; we may
+ have to require imports in the map to satisfy normalization
+ before-hand -->
+ <xsl:variable name="unknown" as="element( preproc:sym-ref )*"
+ select="$deps/preproc:sym-ref[
+ not( @name=$symtable/preproc:sym/@name
+ or @name=$symtable/preproc:sym/@yields ) ]" />
+
+ <xsl:choose>
+ <!-- ensure that every dependency is known (we only care that the symbol
+ actually exists and is an input) -->
+ <xsl:when test="$unknown and not( $ignore-error )">
+ <xsl:for-each select="$unknown">
+ <xsl:call-template name="log:error">
+ <xsl:with-param name="terminate" select="'no'" />
+ <xsl:with-param name="name" select="'link'" />
+ <xsl:with-param name="msg">
+ <xsl:value-of select="$type" />
+ <xsl:text> map </xsl:text>
+ <xsl:value-of select="$from" />
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text> is not a known </xsl:text>
+ <xsl:value-of select="$type" />
+ <xsl:text> field for </xsl:text>
+ <xsl:value-of select="concat( $src, '/', $name )" />
+ <xsl:text>; ensure that it exists and is either used or has @keep set</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <l:map-error />
+ </xsl:when>
+
+ <!-- good to go; link symbol -->
+ <xsl:otherwise>
+ <xsl:apply-templates select="." mode="l:link-deps" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/compiler/linker/log.xsl b/src/current/compiler/linker/log.xsl
new file mode 100644
index 0000000..9c8db60
--- /dev/null
+++ b/src/current/compiler/linker/log.xsl
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Compiles rater XML into JavaScript
+
+ This stylesheet should be included by whatever is doing the processing and is
+ responsible for outputting the generated code in whatever manner is
+ appropriate (inline JS, a file, etc).
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:log="http://www.lovullo.com/logger">
+
+<xsl:template name="log:info">
+ <xsl:param name="name" />
+ <xsl:param name="msg" />
+
+ <xsl:message>
+ <xsl:if test="$name">
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>] </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$msg" />
+ </xsl:message>
+</xsl:template>
+
+<xsl:template name="log:debug">
+ <xsl:param name="name" />
+ <xsl:param name="msg" />
+
+ <xsl:message>
+ <xsl:if test="$name">
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>] </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$msg" />
+ </xsl:message>
+</xsl:template>
+
+<xsl:template name="log:warn">
+ <xsl:param name="name" />
+ <xsl:param name="msg" />
+
+ <xsl:message>
+ <xsl:if test="$name">
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>] warning: </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$msg" />
+ </xsl:message>
+</xsl:template>
+
+<xsl:template name="log:error">
+ <xsl:param name="name" />
+ <xsl:param name="msg" />
+ <xsl:param name="terminate" select="'yes'" />
+
+ <xsl:message terminate="{$terminate}">
+ <xsl:if test="$msg">
+ <xsl:if test="$name">
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>] error: </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$msg" />
+ </xsl:if>
+ </xsl:message>
+</xsl:template>
+
+<xsl:template name="log:internal-error">
+ <xsl:param name="name" />
+ <xsl:param name="msg" />
+ <xsl:param name="terminate" select="'yes'" />
+
+ <xsl:message terminate="{$terminate}">
+ <xsl:if test="$name">
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>] internal error: </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="$msg" />
+ </xsl:message>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/compiler/map.xsl b/src/current/compiler/map.xsl
new file mode 100644
index 0000000..10d8c42
--- /dev/null
+++ b/src/current/compiler/map.xsl
@@ -0,0 +1,997 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Compiles map fragments to produce a map from source data to a destination.
+
+ The source fields will be validated at compile-time to ensure that they exist;
+ destination fields should be checked by the compiler and/or linker. The linker
+ is responsible for assembling the fragments into a working map function.
+
+ When linking, the special head and tail fragments of the topmost map should be
+ used (that is, if A includes B and C, use A).
+
+ XXX: This is tightly coupled with the Program UI; refactor to support any type
+ of source.
+-->
+
+<stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:lvm="http://www.lovullo.com/rater/map"
+ xmlns:lvmc="http://www.lovullo.com/rater/map/compiler"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:lvp="http://www.lovullo.com">
+
+
+<param name="map-noterminate" select="'no'" />
+
+<!--
+ Turn on/off unused param checks
+
+ This is useful for, say, the global classifier, where a param may end up not
+ being used if it's used in external classifications.
+-->
+<param name="unused-param-check" select="'true'" />
+
+<!--
+ Generate a function that maps a set of inputs to a set of outputs
+-->
+<template match="lvm:program-map" mode="lvmc:compile" priority="8">
+ <param name="rater" />
+
+ <variable name="program-ui" select="
+ document( concat( @src, '.xml' ), . )/lvp:program
+ " />
+
+ <variable name="map" select="." />
+
+ <variable name="vresult">
+ <choose>
+ <when test="$program-ui">
+ <apply-templates select="." mode="lvmc:validate-ui">
+ <with-param name="ui" select="$program-ui" />
+ </apply-templates>
+ </when>
+
+ <otherwise>
+ <message terminate="yes">
+ <text>fatal: program UI source XML not found</text>
+ </message>
+ </otherwise>
+ </choose>
+ </variable>
+
+ <if test="
+ $vresult/lvmc:terminate
+ and $map-noterminate = 'no'
+ ">
+ <message terminate="yes">!!! Terminating due to errors.</message>
+ </if>
+
+ <!-- we need to use an lv-namespaced node so that we are recognized
+ consistently with the rest of the system -->
+ <variable name="pkg">
+ <lv:package name="{$__srcpkg}" lvmc:type="map">
+ <!-- initial symbol table; full table will be generated below -->
+ <call-template name="lvmc:stub-symtable">
+ <with-param name="type-prefix" select="'map'" />
+ </call-template>
+
+ <!-- copy all source nodes -->
+ <copy-of select="*" />
+
+ <preproc:fragments>
+ <!-- special fragment to be output as the head -->
+ <preproc:fragment id=":map:___head">
+ <!-- use a callback just in case we need to make portions of this async in the
+ future -->
+ <text>function( input, callback ) {</text>
+ <text>var output = {};</text>
+ </preproc:fragment>
+
+ <!-- compile mapped -->
+ <apply-templates select="./lvm:*" mode="lvmc:compile">
+ <with-param name="rater" select="$rater" />
+ <with-param name="type" select="'map'" />
+ </apply-templates>
+
+ <!-- special fragment to be output as the foot -->
+ <preproc:fragment id=":map:___tail">
+ <text>callback(output);</text>
+ <text>};</text>
+ </preproc:fragment>
+ </preproc:fragments>
+ </lv:package>
+ </variable>
+
+ <!-- output the result after symbol processing -->
+ <call-template name="preproc:gen-deps">
+ <with-param name="pkg" as="element( lv:package )">
+ <apply-templates select="$pkg" mode="preproc:sym-discover">
+ <with-param name="orig-root" select="." />
+ </apply-templates>
+ </with-param>
+ </call-template>
+</template>
+
+
+<!--
+ Generate a function that maps a set of rater outputs
+-->
+<template match="lvm:return-map" mode="lvmc:compile" priority="8">
+ <param name="rater" />
+
+ <variable name="pkg">
+ <lv:package name="{$__srcpkg}" lvmc:type="retmap">
+ <!-- initial symbol table; full table will be generated below -->
+ <call-template name="lvmc:stub-symtable">
+ <with-param name="type-prefix" select="'retmap'" />
+ </call-template>
+
+ <!-- copy source data -->
+ <copy-of select="*" />
+
+ <preproc:fragments>
+ <!-- special fragment to be output as the head -->
+ <preproc:fragment id=":retmap:___head">
+ <!-- use a callback just in case we need to make portions of this async in the
+ future -->
+ <text>function ( input, callback ) {</text>
+ <text>var output = {};</text>
+ </preproc:fragment>
+
+ <apply-templates select="./lvm:*" mode="lvmc:compile">
+ <with-param name="rater" select="$rater" />
+ <with-param name="type" select="'retmap'" />
+ </apply-templates>
+
+ <!-- special fragment to be output as the foot -->
+ <preproc:fragment id=":retmap:___tail">
+ <text>callback(output);</text>
+ <text>}</text>
+ </preproc:fragment>
+ </preproc:fragments>
+ </lv:package>
+ </variable>
+
+ <!-- output the result after symbol processing -->
+ <call-template name="preproc:gen-deps">
+ <with-param name="pkg" as="element( lv:package )">
+ <apply-templates select="$pkg" mode="preproc:sym-discover">
+ <with-param name="orig-root" select="." />
+ </apply-templates>
+ </with-param>
+ </call-template>
+</template>
+
+
+<template name="lvmc:stub-symtable">
+ <param name="type-prefix" select="'map'" />
+
+ <preproc:symtable>
+ <!-- purposely non-polluting. @ignore-dup is intended to be
+ temporary until static generation of these names is resolved;
+ this will not cause problems, since the code is always the
+ same (future bug pending!) -->
+ <preproc:sym name=":{$type-prefix}:___head"
+ type="{$type-prefix}:head"
+ ignore-dup="true" />
+ <preproc:sym name=":{$type-prefix}:___tail"
+ type="{$type-prefix}:tail"
+ ignore-dup="true" />
+ </preproc:symtable>
+</template>
+
+
+<template name="lvmc:mapsym">
+ <param name="name" />
+ <param name="from" />
+ <param name="type-prefix" select="/lv:package/@lvmc:type" />
+
+ <!-- allow mappings to be overridden after import, which allows defaults
+ to be set and then overridden -->
+ <preproc:sym name=":{$type-prefix}:{$name}" virtual="true"
+ type="{$type-prefix}" pollute="true">
+
+ <!-- for consistency and cleanliness, only copy over if set -->
+ <if test="@override='true'">
+ <copy-of select="@override" />
+ </if>
+
+ <copy-of select="@affects-eligibility" />
+
+ <!-- only copy from data if present -->
+ <if test="$from">
+ <copy-of select="$from" />
+ </if>
+ </preproc:sym>
+</template>
+
+
+<!--
+ Directly map an input to the output
+-->
+<template match="lvm:pass" mode="lvmc:compile" priority="5">
+ <param name="rater" />
+ <param name="type" />
+
+ <preproc:fragment id=":{$type}:{@name}">
+ <text>output['</text>
+ <value-of select="@name" />
+ <text>']=</text>
+ <call-template name="lvmc:gen-input-default">
+ <with-param name="rater" select="$rater" />
+ <with-param name="to" select="@name" />
+ <with-param name="from" select="@name" />
+ </call-template>
+ <text>;</text>
+
+ <!-- newline to make output reading and debugging easier -->
+ <text>&#10;</text>
+ </preproc:fragment>
+</template>
+
+<template match="lvm:pass" mode="preproc:symtable" priority="5">
+ <call-template name="lvmc:mapsym">
+ <with-param name="name" select="@name" />
+ <with-param name="from">
+ <preproc:from name="{@name}" />
+ </with-param>
+ </call-template>
+</template>
+
+<template match="lvm:pass" mode="preproc:depgen" priority="5">
+ <preproc:sym-ref name="{@name}" lax="true" />
+</template>
+
+
+
+<!--
+ Maps an input to an output of a different name
+-->
+<template match="lvm:map[ @from ]" mode="lvmc:compile" priority="5">
+ <param name="rater" />
+ <param name="type" />
+
+ <!-- if src and dest are identical, then it may as well be lvm:pass -->
+ <if test="@to = @from">
+ <message>
+ <text>[map] notice: `</text>
+ <value-of select="@to" />
+ <!-- TODO: get namespace prefix from name() -->
+ <text>' has a destination of the same name; use lvm:pass instead</text>
+ </message>
+ </if>
+
+ <preproc:fragment id=":{$type}:{@to}">
+ <text>output['</text>
+ <value-of select="@to" />
+ <text>']=</text>
+ <call-template name="lvmc:gen-input-default">
+ <with-param name="rater" select="$rater" />
+ <with-param name="to" select="@to" />
+ <with-param name="from" select="@from" />
+ </call-template>
+ <text>;</text>
+
+ <!-- newline to make output reading and debugging easier -->
+ <text>&#10;</text>
+ </preproc:fragment>
+</template>
+
+<template name="lvmc:sym-from" match="lvm:map[ @from ]" mode="preproc:symtable" priority="5">
+ <call-template name="lvmc:mapsym">
+ <with-param name="name" select="@to" />
+ <with-param name="from">
+ <preproc:from name="{@from}" />
+ </with-param>
+ </call-template>
+</template>
+
+<template match="lvm:map[ @from
+ and root(.)/@lvmc:type = 'map' ]"
+ mode="preproc:depgen" priority="5">
+ <!-- to the DSL -->
+ <preproc:sym-ref name="{@to}" lax="true" />
+</template>
+
+<template match="lvm:map[ @from
+ and root(.)/@lvmc:type = 'retmap' ]"
+ mode="preproc:depgen" priority="5">
+ <!-- from the DSL -->
+ <preproc:sym-ref name="{@from}" lax="true" />
+</template>
+
+<template match="lvm:map[ @from ]" mode="preproc:depgen" priority="4">
+ <message terminate="yes"
+ select="'internal error: unhandled lvm:map: ', ." />
+</template>
+
+<template match="/*[ @lvmc:type='retmap' ]//lvm:map[ @from ]" mode="preproc:depgen" priority="6">
+ <preproc:sym-ref name="{@from}" lax="true" />
+</template>
+
+
+<!--
+ Triggers dependency generation on the source document, which contains far more
+ information than our symbol table
+-->
+<template match="preproc:sym[ @type='map' ]" mode="preproc:depgen" priority="6">
+ <variable name="name" select="substring-after( @name, ':map:' )" />
+ <variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <apply-templates mode="preproc:depgen"
+ select="$pkg/lvm:*[ @name=$name or @to=$name ]" />
+</template>
+
+
+<!-- FIXME: this is a cluster -->
+<template match="preproc:sym[ @type='retmap' ]" mode="preproc:depgen" priority="6">
+ <variable name="from" as="element( preproc:from )*"
+ select="preproc:from" />
+
+ <if test="$from">
+ <variable name="src-name" as="xs:string"
+ select="substring-after( @name, ':retmap:' )" />
+
+ <variable name="name" as="xs:string+"
+ select="$from/@name" />
+
+ <variable name="pkg" as="element( lv:package )"
+ select="root(.)" />
+
+ <variable name="src-node" as="element()+"
+ select="$pkg/lvm:*[ @name = $src-name
+ or @to = $src-name ]" />
+
+ <if test="count( $src-node ) gt count( $from )">
+ <message terminate="yes"
+ select="'error: duplicate source identifier: ',
+ $src-name" />
+ </if>
+
+ <apply-templates mode="preproc:depgen"
+ select="$src-node" />
+ </if>
+</template>
+
+
+<!--
+ These guys have no dependencies; handle them to prevent depgen errors
+-->
+<template match="preproc:sym[ @type='map:head' or @type='map:tail' ]" mode="preproc:depgen" priority="2">
+ <!-- do nothing -->
+</template>
+<template match="preproc:sym[ @type='retmap:head' or @type='retmap:tail' ]" mode="preproc:depgen" priority="2">
+ <!-- do nothing -->
+</template>
+
+
+<!--
+ Generate a direct input mapping or, if a default exists for the field, use the
+ default if the input is an empty string
+
+ XXX: This is broken; $rater is not provided at the entry point, and
+ if it were, this needs to reference its symbol table.
+-->
+<template name="lvmc:gen-input-default">
+ <param name="rater" />
+ <param name="to" />
+ <!-- use one or the other; latter takes precedence -->
+ <param name="from" />
+ <param name="from-str" />
+
+ <variable name="default">
+ <if test="$rater">
+ <value-of select="concat( '''',
+ lvmc:escape-string(
+ $rater/lv:param[ @name=$to ]/@default ),
+ '''' )" />
+ </if>
+ </variable>
+
+ <variable name="from-var">
+ <choose>
+ <when test="$from-str">
+ <value-of select="$from-str" />
+ </when>
+
+ <otherwise>
+ <text>input['</text>
+ <value-of select="$from" />
+ <text>']</text>
+ </otherwise>
+ </choose>
+ </variable>
+
+ <choose>
+ <when test="$default and not( $default = '' )">
+ <text>set_defaults(</text>
+ <value-of select="$from-var" />
+ <text>,'</text>
+ <value-of select="$default" />
+ <text>')</text>
+ </when>
+
+ <otherwise>
+ <value-of select="$from-var" />
+ </otherwise>
+ </choose>
+</template>
+
+
+<!--
+ Maps a static value to the output
+-->
+<template match="lvm:map[ @value ]" mode="lvmc:compile" priority="5">
+ <param name="type" />
+
+ <preproc:fragment id=":{$type}:{@to}">
+ <text>output['</text>
+ <value-of select="@to" />
+ <text>']='</text>
+ <value-of select="normalize-space( @value )" />
+ <text>';</text>
+
+ <!-- newline to make output reading and debugging easier -->
+ <text>&#10;</text>
+ </preproc:fragment>
+</template>
+
+<template match="lvm:map[ @value ]" mode="preproc:symtable" priority="5">
+ <call-template name="lvmc:mapsym">
+ <with-param name="name" select="@to" />
+ </call-template>
+</template>
+
+
+<template match="lvm:map[*]" mode="lvmc:compile" priority="5">
+ <param name="rater" />
+ <param name="type" />
+
+ <preproc:fragment id=":{$type}:{@to}">
+ <text>output['</text>
+ <value-of select="@to" />
+ <text>']=</text>
+
+ <apply-templates select="./lvm:*" mode="lvmc:compile">
+ <with-param name="rater" select="$rater" />
+ </apply-templates>
+
+ <text>;</text>
+
+ <!-- newline to make output reading and debugging easier -->
+ <text>&#10;</text>
+ </preproc:fragment>
+</template>
+
+<template match="lvm:map[ * ]" mode="preproc:symtable" priority="5">
+ <param name="to" select="@to" />
+
+ <call-template name="lvmc:mapsym">
+ <with-param name="name" select="$to" />
+ <with-param name="from">
+ <for-each select=".//lvm:from">
+ <preproc:from name="{@name}" />
+ </for-each>
+ </with-param>
+ </call-template>
+</template>
+
+<template match="/*[ @lvmc:type='retmap' ]/lvm:map[ * ]" mode="preproc:symtable" priority="6">
+ <variable name="to" select="@to" />
+
+ <call-template name="lvmc:mapsym">
+ <with-param name="name" select="$to" />
+ <with-param name="from">
+ <for-each select=".//lvm:from">
+ <preproc:from name="{@name}" />
+ </for-each>
+ </with-param>
+ </call-template>
+</template>
+
+<template match="lvm:map[ * ]" mode="preproc:depgen" priority="5">
+ <preproc:sym-ref name="{@to}" lax="true" />
+</template>
+
+<template match="lvm:map[ *
+ and root(.)/@lvmc:type = 'retmap' ]"
+ mode="preproc:depgen" priority="6">
+ <for-each select=".//lvm:from">
+ <preproc:sym-ref name="{@name}" lax="true" />
+ </for-each>
+</template>
+
+
+
+<template match="lvm:const" mode="lvmc:compile" priority="5">
+ <text>'</text>
+ <value-of select="@value" />
+ <text>'</text>
+</template>
+
+<template match="lvm:map//lvm:set[@each]" mode="lvmc:compile" priority="5">
+ <text>(function(){</text>
+ <text>var ret=[];</text>
+ <text>var len=input['</text>
+ <value-of select="@each" />
+ <text>'].length;</text>
+
+ <text>for(var _i=0;_i&lt;len;_i++){</text>
+ <text>var </text>
+ <value-of select="@index" />
+ <text>=_i;</text>
+
+ <text>ret[_i]=</text>
+ <apply-templates select="./lvm:*" mode="lvmc:compile" />
+ <text>;</text>
+ <text>}</text>
+
+ <text>return ret;</text>
+ <text>})()</text>
+</template>
+
+<template match="lvm:map//lvm:set[@ignore-empty='true']" mode="lvmc:compile" priority="3">
+ <text>(function(){</text>
+ <text>var ret=[]; var tmp;</text>
+
+ <for-each select="./lvm:*">
+ <text>tmp=</text>
+ <apply-templates select="." mode="lvmc:compile" />
+ <text>;</text>
+
+ <text>if(tmp&amp;&amp;tmp!=='0')ret.push(tmp);</text>
+ </for-each>
+
+ <text>return ret;</text>
+ <text>})()</text>
+</template>
+
+<template match="lvm:map//lvm:set" mode="lvmc:compile" priority="2">
+ <text>[</text>
+ <for-each select="./lvm:*">
+ <if test="position() > 1">
+ <text>,</text>
+ </if>
+
+ <apply-templates select="." mode="lvmc:compile" />
+ </for-each>
+ <text>]</text>
+</template>
+
+<template match="lvm:map//lvm:static" mode="lvmc:compile" priority="5">
+ <text>'</text>
+ <value-of select="@value" />
+ <text>'</text>
+</template>
+
+
+<template match="lvm:map//lvm:from[*]" mode="lvmc:compile" priority="5">
+ <param name="rater" />
+
+ <variable name="to" select="ancestor::lvm:map/@to" />
+
+ <variable name="nested" as="xs:boolean"
+ select="exists( ancestor::lvm:from )" />
+
+ <!-- oval = orig val -->
+ <text>(function(oval){</text>
+ <text>var val = ( (oval||'').length ) ? oval : [oval]; </text>
+ <text>var ret = []; </text>
+
+ <if test="not( $nested )">
+ <text>var curindex;</text>
+ </if>
+
+ <text>for ( var i = 0, l = val.length; i&lt;l; i++ ){</text>
+ <if test="not( $nested )">
+ <text>curindex = i;</text>
+ </if>
+
+ <!-- note that we're casting the value to a string; this is important,
+ since case comparisons are strict (===) -->
+ <text>switch(''+val[i]){</text>
+ <apply-templates mode="lvmc:compile" />
+
+ <if test="not( lvm:default )">
+ <text>default: ret.push(</text>
+ <choose>
+ <!-- give precedence to explicit default -->
+ <when test="@default">
+ <sequence select="concat( '''',
+ lvmc:escape-string( @default ),
+ '''' )" />
+ </when>
+
+ <!-- otherwise, generate one -->
+ <otherwise>
+ <call-template name="lvmc:gen-input-default">
+ <with-param name="rater" select="$rater" />
+ <with-param name="to" select="$to" />
+ <with-param name="from-str">
+ <text>''+val[i]</text>
+ </with-param>
+ </call-template>
+ </otherwise>
+ </choose>
+ <text>);</text>
+ </if>
+ <text>}</text>
+ <text>}</text>
+
+ <choose>
+ <when test="@scalar='true'">
+ <text>return ret[0]; </text>
+ </when>
+
+ <otherwise>
+ <text>return ret; </text>
+ </otherwise>
+ </choose>
+
+ <text>})(input['</text>
+ <value-of select="@name" />
+ <text>']</text>
+
+ <if test="$nested">
+ <text>[curindex]</text>
+ </if>
+
+ <text>)</text>
+</template>
+
+
+<template match="lvm:map//lvm:from" mode="lvmc:compile" priority="2">
+ <variable name="nested" as="xs:boolean"
+ select="exists( ancestor::lvm:from )" />
+
+ <text>input['</text>
+ <value-of select="@name" />
+ <text>']</text>
+
+ <choose>
+ <when test="@index">
+ <text>[</text>
+ <value-of select="@index" />
+ <text>]</text>
+ </when>
+
+ <when test="$nested">
+ <text>[curindex]</text>
+ </when>
+ </choose>
+</template>
+
+
+<template match="lvm:from/lvm:default"
+ mode="lvmc:compile" priority="5">
+ <sequence select="concat(
+ 'default:ret.push(',
+ string-join(
+ lvmc:concat-compile( element(), () ),
+ '' ),
+ ');' )" />
+</template>
+
+
+<template match="lvm:map//lvm:value"
+ mode="lvmc:compile" priority="5">
+ <sequence select="concat( '''', text(), '''' )" />
+</template>
+
+
+<template match="lvm:map//lvm:from/lvm:translate" mode="lvmc:compile" priority="5">
+ <text>case '</text>
+ <value-of select="@key" />
+ <text>':</text>
+ <apply-templates select="." mode="lvmc:compile-translate" />
+ <text> break;</text>
+</template>
+
+
+<template match="lvm:translate[ element() ]"
+ mode="lvmc:compile-translate" priority="5">
+ <sequence select="concat(
+ 'ret.push(',
+ string-join(
+ lvmc:concat-compile( element(), @empty ),
+ '' ),
+ ');' )" />
+</template>
+
+
+<function name="lvmc:concat-compile" as="xs:string+">
+ <param name="children" as="element()+" />
+ <param name="default" as="xs:string?" />
+
+ <text>(function(){</text>
+ <!-- end result should compile into a (dynamic) string -->
+ <text>var result=</text>
+ <for-each select="$children">
+ <if test="position() > 1">
+ <text> + </text>
+ </if>
+
+ <apply-templates mode="lvmc:compile"
+ select="." />
+ </for-each>
+ <text>;</text>
+
+ <text>return (result === "") ? '</text>
+ <sequence select="lvmc:escape-string( $default )" />
+ <text>' : result;</text>
+ <text>})()</text>
+</function>
+
+
+<function name="lvmc:escape-string" as="xs:string">
+ <param name="str" as="xs:string?" />
+
+ <sequence select="replace( $str, '''', '\\''' )" />
+</function>
+
+
+<template match="lvm:translate"
+ mode="lvmc:compile-translate" priority="1">
+ <text>ret.push('</text>
+ <value-of select="normalize-space( @value )" />
+ <text>');</text>
+</template>
+
+
+<template match="text()|comment()" mode="lvmc:compile" priority="1">
+ <!-- strip all text and comments -->
+</template>
+
+
+<template match="*" mode="lvmc:compile" priority="1">
+ <message terminate="yes">
+ <text>fatal: invalid map: unexpected node </text>
+ <apply-templates select="." mode="lvmc:pathout" />
+ </message>
+</template>
+
+
+<template match="lvm:import|lvm:class" mode="lvmc:compile" priority="2">
+ <!-- ignore -->
+</template>
+
+
+<!-- import symbols -->
+<template match="lvm:import" mode="preproc:symtable" priority="5">
+ <!-- original root passed to sym-discover -->
+ <param name="orig-root" />
+
+ <!-- perform symbol import -->
+ <call-template name="preproc:symimport">
+ <with-param name="orig-root" select="$orig-root" />
+ <with-param name="package" select="@path" />
+ <with-param name="export" select="'true'" />
+ </call-template>
+</template>
+
+
+<template match="*" mode="lvmc:pathout">
+ <if test="parent::*">
+ <apply-templates select="parent::*" mode="lvmc:pathout" />
+ </if>
+
+ <text>/</text>
+ <value-of select="name()" />
+</template>
+
+
+<!--
+ Outputs a simple pass-through map that may be used if no map is present
+
+ This simply calls the callback with the given input after creating a new
+ object with it as the prototype, ensuring that altered data does not impact
+ the original data.
+-->
+<template name="lvmc:dummy-map">
+ <param name="name" select="'map'" />
+
+ <text>function </text>
+ <value-of select="$name" />
+ <text>( input, callback ) { </text>
+ <!-- protect input against potential mutilation from classifier -->
+ <text>var prot = function() {}; </text>
+ <text>prot.prototype = input; </text>
+ <text>callback( new prot() );</text>
+ <text> }</text>
+</template>
+
+
+
+<!--
+ Validates map between program and the rater, checking for errors that would
+ cause significant problems.
+-->
+<template match="lvm:program-map" mode="lvmc:validate-rater">
+ <param name="rater" />
+
+ <variable name="map" select="." />
+
+ <!--
+ Get a list of all fields that have not been mapped
+ -->
+ <variable name="nomap" select="
+ $rater/lv:param[
+ not(
+ @name=$map//lvm:pass/@name
+ or @name=$map//lvm:map/@to
+ )
+ ]
+ " />
+
+ <!-- required and unmapped -->
+ <variable name="req-nomap" select="
+ $nomap[ not( @default ) or @default='' ]
+ " />
+
+ <!-- warning on non-mapped, but not required -->
+ <for-each select="$nomap[ @default ]">
+ <message>
+ <text>! [map warning] unmapped optional field: </text>
+ <value-of select="@name" />
+ </message>
+ </for-each>
+
+ <!-- error on required non-mapped -->
+ <for-each select="$req-nomap">
+ <message>
+ <text>!!! [map error] unmapped required field: </text>
+ <value-of select="@name" />
+ </message>
+ </for-each>
+
+
+ <if test="$unused-param-check = 'true'">
+ <variable name="unknown" select="
+ //lvm:pass[
+ not( @name=$rater/lv:param/@name )
+ ]
+ |
+ //lvm:map[
+ not( @to=$rater/lv:param/@name )
+ ]
+ |
+ //lvm:class[
+ not( @name=$rater/lv:classify/@as )
+ ]
+ " />
+
+ <!-- error on unknown -->
+ <for-each select="$unknown">
+ <message>
+ <text>!!! [map error] unknown/unused destination identifier: </text>
+ <value-of select="@name|@to" />
+ </message>
+ </for-each>
+
+ <if test="count( $unknown )">
+ <lvmc:terminate />
+ </if>
+ </if>
+
+
+ <!-- fail. -->
+ <if test="count( $req-nomap )">
+ <lvmc:terminate />
+ </if>
+</template>
+
+
+<template match="lvm:program-map" mode="lvmc:validate-ui">
+ <param name="ui" />
+
+
+ <!-- get a list of unknown source mappings -->
+ <!-- TODO: this is a mess -->
+ <variable name="unknown-pre" select="
+ .//lvm:pass[
+ not( @name=($ui//lvp:question/@id|$ui//lvp:external/@id) )
+ and not( @name=$ui//lvp:calc/@id )
+ ]
+ |
+ .//lvm:map[
+ @from
+ and not( @from=($ui//lvp:question/@id|$ui//lvp:external/@id) )
+ and not( @from=$ui//lvp:calc/@id )
+ ]
+ |
+ .//lvm:from[
+ not( @name=($ui//lvp:question/@id|$ui//lvp:external/@id) )
+ and not( @name=$ui//lvp:calc/@id )
+ ]
+ |
+ .//lvm:set[
+ @each
+ and not( @each=($ui//lvp:question/@id|$ui//lvp:external/@id) )
+ and not( @each=$ui//lvp:calc/@id )
+ ]
+ " />
+
+ <variable name="unknown"
+ select="$unknown-pre[ not( @novalidate='true' ) ]" />
+
+
+ <!-- error on unknown -->
+ <for-each select="$unknown">
+ <message>
+ <text>!!! [map error] unknown source field: </text>
+ <value-of select="@name|@from" />
+ </message>
+ </for-each>
+
+ <if test="count( $unknown )">
+ <lvmc:terminate />
+ </if>
+</template>
+
+
+<!--
+ Outputs source and dest mappings in a common, easily-referenced format useful
+ for parsing
+-->
+<template match="lvm:program-map" mode="lvmc:source-dest-map" priority="5">
+ <lvmc:map>
+ <apply-templates select="./lvm:*" mode="lvmc:source-dest-map" />
+ </lvmc:map>
+</template>
+
+<template match="lvm:pass" mode="lvmc:source-dest-map" priority="5">
+ <lvmc:map from="{@name}" to="{@name}" elig="{@affects-eligibility}" />
+</template>
+
+<template match="lvm:map[ @from ]" mode="lvmc:source-dest-map" priority="5">
+ <lvmc:map from="{@from}" to="{@to}" elig="{@affects-eligibility}" />
+</template>
+<template match="lvm:map/lvm:from" mode="lvmc:source-dest-map" priority="5">
+ <lvmc:map from="{@name}" to="{ancestor::lvm:map/@to}"
+ elig="{@affects-eligibility}" />
+</template>
+<template match="lvm:map//lvm:set/lvm:from" mode="lvmc:source-dest-map" priority="4">
+ <!-- not included; not a one-to-one mapping -->
+</template>
+
+<template match="lvm:map[*]" mode="lvmc:source-dest-map" priority="5">
+ <apply-templates select=".//lvm:*" mode="lvmc:source-dest-map" />
+</template>
+
+<template match="lvm:map//lvm:set" mode="lvmc:source-dest-map" priority="2">
+ <!-- do nothing -->
+</template>
+<template match="lvm:map//lvm:static" mode="lvmc:source-dest-map" priority="2">
+ <!-- do nothing -->
+</template>
+<template match="lvm:map//lvm:value" mode="lvmc:source-dest-map" priority="2">
+ <!-- do nothing -->
+</template>
+<template match="lvm:map//lvm:translate" mode="lvmc:source-dest-map" priority="2">
+ <!-- do nothing -->
+</template>
+<template match="lvm:map[ @value ]" mode="lvmc:source-dest-map" priority="2">
+ <!-- no source -->
+</template>
+<template match="lvm:const" mode="lvmc:source-dest-map" priority="2">
+ <!-- no source -->
+</template>
+
+<template match="lvm:class" mode="lvmc:source-dest-map" priority="2">
+ <!-- not applicable -->
+</template>
+
+
+<template match="*" mode="lvmc:source-dest-map" priority="1">
+ <message terminate="yes">
+ <text>Unknown node: </text>
+ <value-of select="name()" />
+ </message>
+</template>
+
+</stylesheet>
diff --git a/src/current/compiler/validate.xsl b/src/current/compiler/validate.xsl
new file mode 100644
index 0000000..91cfd03
--- /dev/null
+++ b/src/current/compiler/validate.xsl
@@ -0,0 +1,771 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Validates a document for correctness in a manner that is beyond XSD.
+
+ Schematron is not used for this due to certain complexieis. Furthermore, we
+ already have the data in a package structure that is easy to use and query
+ against.
+
+ Validations that can be expressed in the XSD will not be included here, unless
+ there is significant overlap that would make the XSD representation
+ pointlessly incomplete.
+
+ FIXME: Needs aggresive refactoring after introduction of symbol table, for
+ both performance and maintinance.
+-->
+
+<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:ext="http://www.lovullo.com/ext"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:lvv="http://www.lovullo.com/rater/validate"
+ xmlns:sym="http://www.lovullo.com/rater/symbol-map"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+<xsl:import href="validate/domain.xsl" />
+
+<xsl:include href="validate/param.xsl" />
+
+
+<xsl:param name="prohibit-validation" select="'false'" />
+
+
+<!-- FOR PERFORMANCE: constants may be used for large tables of data -->
+<xsl:template match="lv:const" mode="lvv:validate" priority="9">
+ <!-- nothing to be done; constants are merely data declarations -->
+</xsl:template>
+
+
+<!--
+ Perform validations on a given rater/package
+
+ Validations will be returned as an RTF (result tree fragment), which can be
+ converted to a NodeSet using exsl:node-set(). All errors are returned in the
+ following format:
+
+ <lvv:error desc="Description of error type>Error details</lvv:error>
+
+ @return error RTF
+-->
+<xsl:template match="lv:package" mode="lvv:validate" priority="9">
+ <xsl:param name="symbol-map" />
+
+ <xsl:choose>
+ <xsl:when test="$prohibit-validation = 'true'">
+ <xsl:message>
+ <xsl:text>[validate] prohibited; skipping </xsl:text>
+ <xsl:value-of select="local-name()" />
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>...</xsl:text>
+ </xsl:message>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:message>
+ <xsl:text>[validate] validating </xsl:text>
+ <xsl:value-of select="local-name()" />
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>...</xsl:text>
+ </xsl:message>
+
+ <!-- validate -->
+ <xsl:apply-templates mode="lvv:validate" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<xsl:template match="preproc:*" mode="lvv:validate" priority="9">
+ <!-- well that would just be silly -->
+</xsl:template>
+
+
+<xsl:template match="lv:package[ not( @program='true' ) ]/lv:yield" mode="lvv:validate" priority="5">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'lv:yield cannot appear within a non-program package'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content" select="." />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template match="*" mode="lvv:validate" priority="1">
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+
+<xsl:template match="lv:template" mode="lvv:validate" priority="9">
+ <!-- do not validate templates; we'll only validate expansions -->
+</xsl:template>
+
+
+<xsl:template name="lvv:symbol-chk">
+ <xsl:param name="root" />
+ <xsl:param name="symbol-map" />
+
+ <!-- build a symbol list of all used and default symbols -->
+ <xsl:variable name="symlist">
+ <!-- @sym attributes -->
+ <xsl:for-each select="$root//lv:*[@sym]">
+ <lvv:sym value="{@sym}" />
+ </xsl:for-each>
+
+ <!-- defaults from mapping document -->
+ <xsl:for-each select="$symbol-map/sym:symbol[ not( ./* ) ]">
+ <lvv:sym value="{.}" />
+ </xsl:for-each>
+ </xsl:variable>
+
+ <!-- error for each restricted node -->
+ <xsl:for-each select="
+ $root//lv:*[
+ @sym = $symbol-map/sym:reserved/sym:reserve/@sym
+ ]
+ ">
+
+ <xsl:variable name="symbol" select="@sym" />
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Symbol is reserved and cannot be used'" />
+ <xsl:with-param name="refnode" select="$root//lv:*[@sym=$symbol]" />
+ <xsl:with-param name="content" select="$symbol" />
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <!-- error for each duplicate node -->
+ <xsl:for-each select="
+ $symlist/*[
+ @value = following-sibling::*/@value
+ ]
+ ">
+
+ <xsl:variable name="symbol" select="@value" />
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Symbol is not unique'" />
+ <xsl:with-param name="refnode" select="$root//lv:*[@sym=$symbol]" />
+ <xsl:with-param name="content" select="$symbol" />
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template match="c:apply[@name]" mode="lvv:validate" priority="5">
+ <xsl:variable name="name" select="@name" />
+ <xsl:variable name="self" select="." />
+ <xsl:variable name="fsym" select="
+ root(.)/preproc:symtable/preproc:sym[
+ @type='func'
+ and @name=$name
+ ]
+ " />
+
+ <!-- ensure that a function is being applied -->
+ <xsl:if test="not( $fsym )">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Applying non-function'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content" select="@name" />
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- check that all required arguments are provided -->
+ <xsl:for-each select="
+ $fsym/preproc:sym-ref[
+ concat( ':', $name, ':', @name ) = ancestor::preproc:symtable/preproc:sym[
+ @type='lparam'
+ and not( @default )
+ ]
+ ]
+ ">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Missing required argument'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:value-of select="@name" />
+ <xsl:text> for application of </xsl:text>
+ <xsl:value-of select="$name" />
+ <xsl:text>()</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+
+<xsl:template match="c:*[ @index ]/c:index" mode="lvv:validate" priority="9">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Ambiguous index specification'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content" select="../@name" />
+ </xsl:call-template>
+
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+
+<!--
+ Validate that match @on's exist
+-->
+<xsl:template match="lv:classify[ @as ]//lv:match" mode="lvv:validate" priority="9">
+ <xsl:if test="not( @on=root(.)/preproc:symtable/preproc:sym[ @type ]/@name )">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Unknown match @on'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>`</xsl:text>
+ <xsl:value-of select="@on" />
+ <xsl:text>' is unknown for classification </xsl:text>
+
+ <xsl:variable name="class"
+ select="ancestor::lv:classify" />
+ <xsl:value-of select="if ( $class/@preproc:generated-from ) then
+ $class/@preproc:generated-from
+ else
+ $class/@as" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:apply-templates select="." mode="lvv:validate-match" />
+</xsl:template>
+
+<!--
+ Validate that non-numeric value matches actually exist and are constants
+-->
+<xsl:template match="lv:match[@value]" mode="lvv:validate-match" priority="5">
+ <xsl:if test="
+ not( number( @value ) = @value )
+ and not(
+ @value=root(.)/preproc:symtable/preproc:sym[
+ @type='const'
+ ]/@name
+ )
+ ">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Unknown match value'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>`</xsl:text>
+ <xsl:value-of select="@value" />
+ <xsl:text>' is unknown for classification </xsl:text>
+
+ <xsl:variable name="class"
+ select="ancestor::lv:classify" />
+ <xsl:value-of select="if ( $class/@preproc:generated-from ) then
+ $class/@preproc:generated-from
+ else
+ $class/@as" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:apply-templates mode="lvv:validate-match" />
+</xsl:template>
+
+<xsl:template match="lv:match" mode="lvv:validate-match" priority="2">
+ <xsl:apply-templates mode="lvv:validate-match" />
+</xsl:template>
+
+<!--
+ Classification match assumptions must operate only on other classifiers and
+ must assume values that the referenced classifier actually matches on
+-->
+<xsl:template match="lv:match/lv:assuming" mode="lvv:validate-match" priority="5">
+ <xsl:variable name="on" select="../@on" />
+ <xsl:variable name="ref" select="root(.)//lv:classify[ @yields=$on ]" />
+
+ <!-- assumptions must only operate on variables mentioned in the referenced
+ classification -->
+ <xsl:for-each select="
+ .//lv:that[
+ not( @name=$ref//lv:match/@on )
+ ]
+ ">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Invalid classification assumption'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:value-of select="@name" />
+ <xsl:text> is not used to classify </xsl:text>
+ <xsl:value-of select="$on" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template match="c:*" mode="lvv:validate-match" priority="2">
+ <xsl:apply-templates select="." mode="lvv:validate" />
+</xsl:template>
+
+<xsl:template match="*" mode="lvv:validate-match" priority="1">
+ <!-- do nothing -->
+</xsl:template>
+
+
+<xsl:template match="c:value" mode="lvv:validate" priority="5">
+ <!-- do nothing; just prevent the below validation from occurring -->
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+<xsl:template match="c:let" mode="lvv:validate" priority="5">
+ <!-- do not validate this node itself -->
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+<xsl:template match="c:*[@name or @of]" mode="lvv:validate" priority="2">
+ <xsl:variable name="name">
+ <xsl:choose>
+ <xsl:when test="@of">
+ <xsl:value-of select="@of" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="@name" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- XXX: have to maintain this list! -->
+ <xsl:variable name="nodes" select="
+ root(.)//lv:*[
+ @name=$name
+ or @yields=$name
+ or @as=$name
+ ]
+ , root(.)//c:*[
+ @generates=$name
+ ]
+ , root(.)//c:values/c:value[ @name=$name ]
+ " />
+
+ <!-- locate function params/let vars -->
+ <xsl:variable name="fname" select="
+ ancestor::lv:function[
+ lv:param[
+ @name=$name
+ ]
+ ]/@name
+ |ancestor::c:apply[
+ c:arg[
+ @name=$name
+ ]
+ ]/@name
+ |ancestor::c:let[
+ c:values/c:value[
+ @name=$name
+ ]
+ ]/@name
+ " />
+
+ <!-- if this name references a function parameter, then it takes
+ precedence (note that this consequently means that it masks any other
+ names that may be globally defined) -->
+ <xsl:variable name="sym" select="
+ if ( $fname ) then
+ root(.)/preproc:symtable/preproc:sym[
+ @name=concat( ':', $fname, ':', $name )
+ ]
+ else
+ root(.)/preproc:symtable/preproc:sym[
+ @name=$name
+ ]
+ " />
+
+ <xsl:variable name="type" select="$sym/@dtype" />
+
+ <!-- all calculations must make use of numeric types -->
+ <xsl:if test="
+ not(
+ ( $type = 'integer' )
+ or ( $type = 'float' )
+ or ( $type = 'boolean' )
+ or ( ancestor::c:*[ @of and @index=$name ] )
+ )
+ ">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Non-numeric type in calculation'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:value-of select="$name" />
+ <xsl:text> is </xsl:text>
+
+ <xsl:choose>
+ <xsl:when test="not( $type ) or $type = ''">
+ <xsl:text>undefined</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:text>of type '</xsl:text>
+ <xsl:value-of select="$type" />
+ <xsl:text>'</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="is-set" select="$sym/@dim" />
+
+ <xsl:choose>
+ <!-- furthermore, if @of is provided, then it must be a set -->
+ <xsl:when test="@of">
+ <xsl:if test="$is-set = 0">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'@of must reference a set'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content" select="$name" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:when>
+
+ <!-- otherwise, an index is required to reference an item in the set unless
+ the value is being passed as an argument of the same set type, or is
+ the return value of a function (we assume it to be a return value if it
+ is in a tail position) -->
+ <!-- TODO: re-add argument param check for sets -->
+ <!-- TODO: c:values/c:value/@set check; should also only be allowed in tail -->
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="
+ ( ( not( @index ) or ( @index = '' ) ) and not( ./c:index ) )
+ and not( ancestor::lv:match )
+ and (
+ $is-set != '0'
+ and not(
+ ancestor::c:arg
+ or (
+ ancestor::lv:function
+ and not( ./* )
+ )
+ or parent::c:length-of
+ or ancestor::c:value[ @set ]
+ )
+ )
+ and not( parent::c:length-of )
+ ">
+
+ <xsl:choose>
+ <xsl:when test="$sym/@dim = '?'">
+ <xsl:message>
+ <xsl:text>internal warning: unresolved param </xsl:text>
+ <xsl:text>dimension: `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc">
+ <xsl:text>Unexpected vector/matrix reference</xsl:text>
+ </xsl:with-param>
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:value-of select="@name" />
+ <xsl:text> (did you forget @index?)</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <!-- matrices require two indexes, unless being used as an argument to a
+ function or as part of a dot product (it is also acceptable as the
+ return value of a function, which must be in a tail position) -->
+ <xsl:when test="
+ ( number( $is-set ) gt 1 )
+ and not( ./c:index[ $is-set ] )
+ and not( ancestor::c:arg )
+ and not( ancestor::c:let )
+ and not( ancestor::c:product[ @dot ] )
+ and not( ancestor::c:cons )
+ and not( ancestor::c:cons )
+ and not(
+ ancestor::lv:function
+ and not( ./* )
+ )
+ ">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Invalid matrix specification'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:value-of select="@name" />
+ <xsl:text> requires </xsl:text>
+ <xsl:value-of select="$is-set" />
+ <xsl:text> indexes (use c:index) unless being</xsl:text>
+ <xsl:text> passed as a function argument</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- ensure that we do not have too many indexes -->
+ <xsl:when test="( number( $is-set ) gt 0 ) and ./c:index[ number( $is-set ) + 1 ]">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Invalid vector/matrix specification'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:value-of select="@name" />
+ <xsl:text> may only have </xsl:text>
+ <xsl:value-of select="$is-set" />
+ <xsl:text> index(s)</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- if we have an index, but we're not dealing with a set, then that is
+ also an issue -->
+ <xsl:when test="@index and ( $is-set = '' )">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Using index with non-set'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content" select="@name" />
+ </xsl:call-template>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- index references should be defined -->
+ <xsl:if test="
+ @index and not( local-name() = 'sum' or local-name() = 'product' )
+ ">
+
+ <xsl:variable name="index" select="@index" />
+
+ <!-- search for index definition -->
+ <!-- XXX: This also requires knowledge of match and require-param -->
+ <xsl:if test="
+ not(
+ ancestor::c:*[ @index = $index ]
+ or ( root(.)//lv:*[ @name = $index ]
+ and (
+ local-name() != 'match'
+ and local-name() != 'require-param'
+ )
+ )
+ )
+ ">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Undefined index'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content" select="@index" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:if>
+
+ <!-- recursively validate any nested calculations -->
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+
+<xsl:template match="c:apply/c:arg[@name]" mode="lvv:validate" priority="5">
+ <!-- merely validate its existence -->
+ <xsl:variable name="fname" select="parent::c:apply/@name" />
+ <xsl:if test="not(
+ concat( ':', $fname, ':', @name ) = root(.)/preproc:symtable/preproc:sym[
+ @type='lparam'
+ and @parent=$fname
+ ]/@name
+ )">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Unknown argument'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>Argument `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' is unknown for function `</xsl:text>
+ <xsl:value-of select="$fname" />
+ <xsl:text>'</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- recursively validate any nested calculations -->
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+
+<xsl:template match="c:product[@dot]" mode="lvv:validate" priority="5">
+ <!-- TODO -->
+</xsl:template>
+
+
+<xsl:template mode="lvv:validate" priority="2" match="
+ lv:union/lv:typedef[
+ ./lv:*[1]/@type != preceding-sibling::lv:typedef[1]/lv:*[1]/@type
+ ]
+ ">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Union type mismatch'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>Expected type '</xsl:text>
+ <xsl:value-of select="preceding-sibling::lv:typedef[1]/lv:*[1]/@type" />
+ <xsl:text>' for </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>, but found '</xsl:text>
+ <xsl:value-of select="./lv:*[1]/@type" />
+ <xsl:text>'</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+
+<!--
+ Checks for use of undefined classifications
+-->
+<xsl:template mode="lvv:validate" priority="2"
+ match="lv:rate/lv:class[
+ not( concat( ':class:', @ref ) = root(.)/preproc:symtable/preproc:sym[ @type='class' ]/@name )
+ ]">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Unknown classification'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>unknown classification '</xsl:text>
+ <xsl:value-of select="@ref" />
+ <xsl:text>' referenced by </xsl:text>
+ <xsl:value-of select="ancestor::lv:rate/@yields" />
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+
+<!--
+ All rate blocks must have non-empty yields
+
+ This is an awkward error, because it's not possible to identify the
+ rate block by name...there is none.
+-->
+<xsl:template mode="lvv:validate" priority="9"
+ match="lv:rate[ not( @yields ) or @yields = '' ]">
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Unidentifiable rate block'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>missing or empty @yields; see document dump</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+
+<!--
+ Throws an error if a generator is requested using unsupported data
+
+ Specifically, a generator is intended to generate a set from an expression
+ while looping over another set. If we're not looping, then we're not
+ generating a set. Furthermore, if a child expression was not provided, then
+ the set produced would be equivalent to @of, which is useless.
+-->
+<xsl:template mode="lvv:validate"
+ match="c:*[ @generates and not( @of and ./c:* ) ]" priority="9">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Invalid generator'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>Cannot create generator '</xsl:text>
+ <xsl:value-of select="@generates" />
+ <xsl:text>'; generating expressions must contain both @of </xsl:text>
+ <xsl:text>and a sub-expression.</xsl:text>
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+
+<!--
+ Since @generates creates a new variable that can be referenced, it needs
+ documentation! Refuse to compile if documentation is not provided. Yeah, we're
+ assholes.
+-->
+<xsl:template mode="lvv:validate"
+ match="c:*[ @generates and not( @desc ) ]" priority="9">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'No generator description'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>@desc required when creating generator </xsl:text>
+ <xsl:value-of select="@generates" />
+ </xsl:with-param>
+ </xsl:call-template>
+
+ <xsl:apply-templates mode="lvv:validate" />
+</xsl:template>
+
+
+<xsl:template match="ext:*" mode="lvv:get-path">
+ <!-- omit from path output -->
+</xsl:template>
+
+<xsl:template match="*" mode="lvv:get-path">
+ <xsl:apply-templates select="parent::*" mode="lvv:get-path" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="name()" />
+
+ <!-- certain nodes may support path descriptions to aid in determining which
+ node is being referenced -->
+ <xsl:variable name="desc">
+ <xsl:apply-templates select="." mode="lvv:get-path-desc" />
+ </xsl:variable>
+
+ <xsl:if test="$desc != ''">
+ <xsl:text>[</xsl:text>
+ <xsl:value-of select="$desc" />
+ <xsl:text>]</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template match="lv:rate[ @yields ]" mode="lvv:get-path-desc">
+ <xsl:text>@yields=</xsl:text>
+ <xsl:value-of select="@yields" />
+</xsl:template>
+
+<xsl:template match="c:*[ @name ]" mode="lvv:get-path-desc" priority="5">
+ <xsl:text>@name=</xsl:text>
+ <xsl:value-of select="@name" />
+</xsl:template>
+<xsl:template match="c:*[ @label ]" mode="lvv:get-path-desc" priority="1">
+ <xsl:text>@label=</xsl:text>
+ <xsl:value-of select="@label" />
+</xsl:template>
+
+<xsl:template match="*" mode="lvv:get-path-desc">
+ <!-- no desc by default -->
+</xsl:template>
+
+
+<xsl:template name="lvv:error">
+ <xsl:param name="desc" />
+ <xsl:param name="refnode" />
+ <xsl:param name="content" />
+
+ <xsl:variable name="path">
+ <xsl:if test="$refnode">
+ <xsl:apply-templates select="$refnode" mode="lvv:get-path" />
+ </xsl:if>
+ </xsl:variable>
+
+ <lvv:error desc="{$desc}" path="{$path}">
+ <xsl:value-of select="$content" />
+ </lvv:error>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/compiler/validate/domain.xsl b/src/current/compiler/validate/domain.xsl
new file mode 100644
index 0000000..0d84a8c
--- /dev/null
+++ b/src/current/compiler/validate/domain.xsl
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Domain validations
+
+ TODO: For core domains, validate src package path as well. (Right now,
+ param types are polluting, and so this is not a problem.)
+-->
+
+<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:lvv="http://www.lovullo.com/rater/validate"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ Assert that VALUE falls within the provided domain
+
+ SYM-DOMAIN should be a symbol resolving to the domain definition.
+-->
+<xsl:template name="lvv:domain-check">
+ <xsl:param name="value" />
+ <xsl:param name="sym-domain" />
+
+ <xsl:if test="not( $sym-domain )">
+ <xsl:message terminate="yes">
+ <xsl:text>internal error: no domain symbol provided; </xsl:text>
+ <xsl:text>caller: </xsl:text>
+ <xsl:copy-of select="." />
+ </xsl:message>
+ </xsl:if>
+
+ <!-- generate node to simplify xpath expressions -->
+ <xsl:variable name="sym-validate">
+ <lvv:chk value="{$value}">
+ <xsl:copy-of select="$sym-domain" />
+ </lvv:chk>
+ </xsl:variable>
+
+ <xsl:apply-templates mode="lvv:domain-check"
+ select="$sym-validate/lvv:chk">
+
+ <xsl:with-param name="self-pkg" select="
+ $sym-domain/ancestor::lv:package" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<!--
+ Core type checks
+
+ - Integers must simply match when rounded;
+ - Floats must be any type of number; and
+ - Booleans may be only 1 or 0.
+-->
+<xsl:template match="
+ lvv:chk[
+ preproc:sym/@type = 'type'
+ and (
+ (
+ preproc:sym/@name = 'integer'
+ and not(
+ @value = floor( @value )
+ )
+ )
+ or (
+ preproc:sym/@name = 'float'
+ and not(
+ @value = number( @value )
+ )
+ )
+ or (
+ preproc:sym/@name = 'boolean'
+ and not(
+ number( @value ) = 0
+ or number( @value ) = 1
+ )
+ )
+ )
+ ]"
+ mode="lvv:domain-check" priority="5">
+
+ <xsl:call-template name="lvv:domain-fail" />
+</xsl:template>
+
+
+<!--
+ Domain assertions on user-defined types
+-->
+<xsl:template match="
+ lvv:chk[
+ preproc:sym/@type='type'
+ and not (
+ preproc:sym/@name = 'integer'
+ or preproc:sym/@name = 'float'
+ or preproc:sym/@name = 'boolean'
+ )
+ ]
+ "
+ mode="lvv:domain-check" priority="5">
+
+ <xsl:param name="self-pkg" />
+
+ <xsl:variable name="chkval" select="@value" />
+
+ <xsl:variable name="domain">
+ <xsl:call-template name="lvv:get-domain-by-sym">
+ <xsl:with-param name="sym" select="preproc:sym" />
+ <xsl:with-param name="self-pkg" select="$self-pkg" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$domain/lv:domain/lv:element[ @value = $chkval ]">
+ <lvv:ok type="domain-check" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="lvv:domain-fail" />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
+<!--
+ No validation failure
+-->
+<xsl:template match="lvv:chk"
+ mode="lvv:domain-check" priority="2">
+
+ <lvv:ok type="domain-check" />
+</xsl:template>
+
+
+<!--
+ We passed ourselves something unexpected
+-->
+<xsl:template match="*"
+ mode="lvv:domain-chk" priority="1">
+
+ <xsl:message terminate="yes">
+ <xsl:text>internal error: unexpected node for lvv:domain-chk: </xsl:text>
+ <xsl:copy-of select="." />
+ </xsl:message>
+</xsl:template>
+
+
+<!--
+ Mark validation as a failure, outputting the assertion
+
+ TODO: Once domains are used as the primary source instead of typedefs,
+ check to ensure that the symbol is an actual domain symbol.
+-->
+<xsl:template name="lvv:domain-fail">
+ <lvv:fail type="domain-check">
+ <xsl:copy-of select="." />
+ </lvv:fail>
+</xsl:template>
+
+
+<xsl:template name="lvv:get-domain-by-sym">
+ <xsl:param name="sym" />
+ <xsl:param name="self-pkg" select="ancestor::lv:package" />
+
+ <!-- package containing symbol -->
+ <xsl:variable name="pkg" select="
+ if ( $sym/@src and not( $sym/@src='' ) ) then
+ document( concat( $sym/@src, '.xmlo' ), $__entry-root )
+ /lv:package
+ else
+ $self-pkg
+ " />
+
+ <!-- attempt to locate domain of the given name -->
+ <xsl:variable name="domain" select="
+ $pkg/lv:domain[ @name = $sym/@name ]" />
+
+ <xsl:if test="not( $domain )">
+ <xsl:message terminate="yes">
+ <xsl:text>error: no domain found for </xsl:text>
+ <xsl:value-of select="$sym/@src" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="$sym/@name" />
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:copy-of select="$domain" />
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/compiler/validate/param.xsl b/src/current/compiler/validate/param.xsl
new file mode 100644
index 0000000..1d18d7a
--- /dev/null
+++ b/src/current/compiler/validate/param.xsl
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Parameter validations
+-->
+
+<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:lvv="http://www.lovullo.com/rater/validate"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ Param type must be known
+
+ TODO: Doesn't the symbol table lookup process handle this?
+-->
+<xsl:template match="
+ lv:param[
+ not(
+ @type=root(.)/preproc:symtable/preproc:sym[
+ @type
+ ]/@name
+ )
+ ]"
+ mode="lvv:validate" priority="5">
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'Unknown param type'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>'</xsl:text>
+ <xsl:value-of select="@type" />
+ <xsl:text>' is undefined for param </xsl:text>
+ <xsl:value-of select="@name" />
+ </xsl:with-param>
+ </xsl:call-template>
+</xsl:template>
+
+
+<!--
+ Default must be within the domain of the param
+
+ Note that this template priority is less than the template that checks to
+ ensure that the param type exists in the first place.
+-->
+<xsl:template match="lv:param[ @default ]"
+ mode="lvv:validate" priority="4">
+
+ <xsl:variable name="type" select="@type" />
+
+ <!-- default must be within its domain -->
+ <xsl:variable name="result">
+ <xsl:call-template name="lvv:domain-check">
+ <xsl:with-param name="value" select="@default" />
+ <xsl:with-param name="sym-domain" select="
+ root(.)/preproc:symtable/preproc:sym[
+ @name = $type
+ ]" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:if test="not( $result/lvv:ok )">
+ <xsl:variable name="fail" select="$result/lvv:fail/lvv:chk" />
+
+ <!-- if we didn't succeed, but we didn't fail, then we did something we
+ weren't supposed to -->
+ <xsl:if test="not( $fail )">
+ <xsl:message terminate="yes">
+ <xsl:text>internal error: in limbo processing param `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' @default</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:call-template name="lvv:error">
+ <xsl:with-param name="desc" select="'param @default domain violation'" />
+ <xsl:with-param name="refnode" select="." />
+ <xsl:with-param name="content">
+ <xsl:text>param `</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>' @default of `</xsl:text>
+ <xsl:value-of select="$fail/@value" />
+ <xsl:text>' is not within its domain of </xsl:text>
+ <xsl:value-of select="$fail/preproc:sym/@src" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="$fail/preproc:sym/@name" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+</xsl:template>
+
+
+<!--
+ Fallback for no validation issues
+-->
+<xsl:template match="lv:param" mode="lvv:validate" priority="2">
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/compiler/worksheet.xsl b/src/current/compiler/worksheet.xsl
new file mode 100644
index 0000000..9149ddd
--- /dev/null
+++ b/src/current/compiler/worksheet.xsl
@@ -0,0 +1,543 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Compiles rater XML into JavaScript
+
+ This stylesheet should be included by whatever is doing the processing and is
+ responsible for outputting the generated code in whatever manner is
+ appropriate (inline JS, a file, etc).
+-->
+
+<stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:lv="http://www.lovullo.com/rater"
+ xmlns:w="http://www.lovullo.com/rater/worksheet"
+ xmlns:_w="http://www.lovullo.com/rater/worksheet/_priv"
+ xmlns:c="http://www.lovullo.com/calc"
+ xmlns:lvmc="http://www.lovullo.com/rater/map/compiler"
+ xmlns:wc="http://www.lovullo.com/rater/worksheet/compiler"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc"
+ xmlns:util="http://www.lovullo.com/util">
+
+<variable name="wc:lc" select="'abcdefghijklmnopqrstuvwxyz'" />
+<variable name="wc:uc" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
+
+<!-- lexemes to be converted to a human-readable format -->
+<!-- TODO: move this into an external file so it may be easily configurable -->
+<variable name="wc:hlex">
+ <!-- prefix to suffix -->
+ <wc:lex prefix="prem" to="Premium" />
+ <wc:lex prefix="rate" to="Rate" />
+ <wc:lex prefix="credit" to="Credit" />
+ <wc:lex prefix="surcharge" to="Surcharge" />
+
+ <wc:lex str="gl" to="GL" />
+ <wc:lex str="prop" to="Property" />
+ <wc:lex str="equip" to="Equipment" />
+ <wc:lex str="adjust" to="Adjustment" />
+ <wc:lex str="adj" to="Adjustment" />
+ <wc:lex str="ded" to="Deductible" />
+ <wc:lex str="dw" to="Dwelling" />
+ <wc:lex str="fam" to="Family" />
+ <wc:lex str="tiv" to="TIV" />
+
+ <!-- *Each is used for generators -->
+ <wc:lex str="each" to="" />
+</variable>
+
+
+<!-- we expect that the worksheet will have been preprocessed into the rater
+ document -->
+<template match="w:worksheet" mode="w:compile" priority="10">
+ <param name="corder" />
+
+ <variable name="displays" as="element( w:display )*"
+ select="w:display" />
+
+ <variable name="package" as="element( lv:package )"
+ select="_w:load-package( @package, . )" />
+
+ <variable name="syms" as="element( preproc:sym )*"
+ select="_w:filter-needed-symbols(
+ _w:load-symbols( $package ),
+ $displays )" />
+
+ <lv:package name="{@name}"
+ lvmc:type="worksheet">
+ <!-- we provide one special symbol -->
+ <preproc:symtable>
+ <preproc:sym name="___worksheet"
+ type="worksheet" />
+ </preproc:symtable>
+
+ <!-- TODO -->
+ <preproc:sym-deps>
+ <preproc:sym-dep name="___worksheet" />
+ </preproc:sym-deps>
+
+ <copy-of select="node()" />
+
+ <preproc:fragments>
+ <preproc:fragment id="___worksheet">
+ <text>rater.worksheet = </text>
+
+ <call-template name="util:json">
+ <with-param name="obj">
+ <for-each select="$displays">
+ <sequence select="_w:compile-display( ., $syms )" />
+ </for-each>
+
+ <variable name="yield" as="element( lv:rate )?"
+ select="$package/lv:rate[ @yields = '___yield' ]" />
+
+ <!-- always include yield -->
+ <if test="$yield">
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="id" select="'yield'" />
+ <with-param name="array">
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="value" select="'Yields'" />
+ </call-template>
+ </util:value>
+
+ <util:value>
+ <apply-templates mode="wc:compile"
+ select="$yield/c:*" />
+ </util:value>
+ </with-param>
+ </call-template>
+ </util:value>
+ </if>
+ </with-param>
+ </call-template>
+
+ <text>;</text>
+ </preproc:fragment>
+ </preproc:fragments>
+ </lv:package>
+</template>
+
+
+<function name="_w:compile-display" as="element( util:value )">
+ <param name="display" as="element( w:display )" />
+ <param name="syms" as="element( preproc:sym )*" />
+
+ <variable name="name" as="xs:string"
+ select="$display/@name" />
+
+ <variable name="sym" as="element( preproc:sym )?"
+ select="$syms[ @name = $name ]" />
+
+ <!-- terminate on unknown references -->
+ <if test="empty( $sym )">
+ <message terminate="yes"
+ select="'Reference to unknown symbol:', $name" />
+ </if>
+
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="id" select="$name" />
+
+ <with-param name="array">
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="value">
+ <call-template name="wc:var-to-hstr">
+ <with-param name="var" select="$name" />
+ </call-template>
+ </with-param>
+ </call-template>
+ </util:value>
+
+ <util:value>
+ <choose>
+ <when test="$display/@collapse = 'true'">
+ <sequence select="''" />
+ </when>
+
+ <otherwise>
+ <variable name="rate-block" as="element( lv:rate )"
+ select="_w:get-src-node( $sym )" />
+
+ <apply-templates mode="wc:compile"
+ select="$rate-block/c:*">
+ <with-param name="display" select="$display"
+ tunnel="yes" />
+ </apply-templates>
+ </otherwise>
+ </choose>
+ </util:value>
+
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="value">
+ <value-of select="$display/@always" />
+ </with-param>
+ </call-template>
+ </util:value>
+ </with-param>
+ </call-template>
+ </util:value>
+</function>
+
+
+<function name="_w:load-package" as="element( lv:package )">
+ <param name="path" as="xs:string" />
+ <param name="context" as="node()" />
+
+ <!-- TODO: function to provide xmlo extension -->
+ <variable name="package-uri" as="xs:anyURI"
+ select="resolve-uri(
+ concat( $path, '.xmlo' ),
+ base-uri( $context ) )" />
+
+ <if test="not( doc-available( $package-uri ) )">
+ <message terminate="yes"
+ select="concat( 'fatal: package ',
+ $path,
+ ' not found' )" />
+ </if>
+
+ <sequence select="doc( $package-uri )/lv:package" />
+</function>
+
+
+<!-- TODO: some of this logic can be factored out into a common
+ library -->
+<function name="_w:load-symbols" as="element( preproc:sym )+">
+ <param name="package" as="element( lv:package )" />
+
+ <sequence select="$package/preproc:symtable/preproc:sym" />
+</function>
+
+
+
+<function name="_w:filter-needed-symbols" as="element( preproc:sym )*">
+ <param name="syms" as="element( preproc:sym )*" />
+ <param name="displays" as="element( w:display )*" />
+
+ <sequence select="$syms[ @name = $displays/@name ]" />
+</function>
+
+
+<function name="_w:get-src-package" as="element( lv:package )">
+ <param name="sym" as="element( preproc:sym )" />
+
+ <variable name="sym-path" as="xs:string?"
+ select="$sym/@src" />
+
+ <variable name="context-uri" as="xs:anyURI"
+ select="base-uri( $sym )" />
+
+ <!-- TODO: function to provide xmlo extension -->
+ <variable name="src-uri" as="xs:anyURI"
+ select="if ( $sym-path and not( $sym-path = '' ) ) then
+ resolve-uri(
+ concat( $sym-path, '.xmlo' ),
+ $context-uri )
+ else
+ $context-uri" />
+
+ <if test="not( doc-available( $src-uri ) )">
+ <message terminate="yes"
+ select="concat( 'fatal: package ',
+ $sym-path,
+ ' not found; required by symbol ',
+ $sym/@name )" />
+ </if>
+
+ <sequence select="doc( $src-uri )/lv:package" />
+</function>
+
+
+
+<function name="_w:get-src-node" as="element( lv:rate )">
+ <param name="sym" as="element( preproc:sym )" />
+
+ <variable name="package" as="element( lv:package )"
+ select="_w:get-src-package( $sym )" />
+
+ <variable name="rate-name" as="xs:string"
+ select="if ( $sym/@parent ) then
+ $sym/@parent
+ else
+ $sym/@name" />
+
+ <sequence select="$package/lv:rate[ @yields = $rate-name ]" />
+</function>
+
+
+<template match="c:sum[@of='_CMATCH_']" mode="wc:compile" priority="9">
+ <apply-templates select="./c:*" mode="wc:compile" />
+</template>
+
+<template match="c:sum[@of='_CMATCH_']/c:product[c:value-of[@name='_CMATCH_']]"
+ mode="wc:compile" priority="9">
+
+ <!-- ignore the product and continue with the 2nd node -->
+ <apply-templates select="./c:*[2]" mode="wc:compile" />
+</template>
+
+
+<template match="c:apply" priority="7" mode="wc:compile">
+ <param name="display" as="element( w:display )"
+ tunnel="yes" />
+
+ <choose>
+ <!-- do not expand -->
+ <when test="
+ not(
+ @name = $display/ancestor::w:worksheet
+ /w:expand-function/@name
+ or (
+ @name = $display/w:expand-function/@name
+ )
+ )
+ ">
+
+ <call-template name="wc:compile-calc">
+ <with-param name="nochildren" select="true()" />
+ <with-param name="runtime" select="true()" />
+ </call-template>
+ </when>
+
+ <!-- expand -->
+ <otherwise>
+ <call-template name="wc:compile-calc" />
+ </otherwise>
+ </choose>
+</template>
+
+
+<template match="c:value-of[c:index]" mode="wc:compile" priority="5">
+ <call-template name="wc:compile-calc">
+ <with-param name="nochildren" select="true()" />
+ <with-param name="runtime" select="true()" />
+ </call-template>
+</template>
+
+<!-- we need to take into account constants that are compiled in place (so we
+ cannot determine their value by name at runtime) -->
+<template match="c:value-of[ @name=//lv:const[ not( * ) ]/@name ]" mode="wc:compile" priority="5">
+ <variable name="name" select="@name" />
+
+ <call-template name="wc:compile-calc">
+ <with-param name="include-value">
+ <value-of select="//lv:const[ @name=$name ]/@value" />
+ </with-param>
+ </call-template>
+</template>
+
+
+<!--
+ Will output JSON of the following structure:
+
+ [ "type", {desc}, [subnodes] ]
+
+ The subnodes are recursively generated in the same format as above.
+-->
+<template name="wc:compile-calc" match="c:*" mode="wc:compile" priority="4">
+ <param name="nochildren" as="xs:boolean" select="false()" />
+ <param name="runtime" select="false()" />
+ <param name="include-value" />
+
+ <call-template name="util:json">
+ <with-param name="array">
+ <!-- output node type -->
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="value" select="local-name()" />
+ </call-template>
+ </util:value>
+
+ <!-- description -->
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="obj">
+
+ <!-- build each attribute into the description -->
+ <for-each select="@*">
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="id" select="local-name()" />
+ <with-param name="value" select="." />
+ </call-template>
+ </util:value>
+ </for-each>
+
+ <!-- certain values should be calculated at runtime -->
+ <if test="$runtime = true()">
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="id" select="'runtime'" />
+ <with-param name="value" select="'true'" />
+ </call-template>
+ </util:value>
+ </if>
+
+ </with-param>
+ </call-template>
+ </util:value>
+
+ <!-- children -->
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="array">
+
+ <if test="not( $nochildren = true() )">
+ <!-- sub-nodes (recursive) -->
+ <for-each select="c:*">
+ <util:value>
+ <apply-templates select="." mode="wc:compile" />
+ </util:value>
+ </for-each>
+ </if>
+
+ </with-param>
+ </call-template>
+ </util:value>
+
+ <!-- optional value (if we can determine compile-time) -->
+ <if test="$include-value">
+ <util:value>
+ <call-template name="util:json">
+ <with-param name="value" select="$include-value" />
+ </call-template>
+ </util:value>
+ </if>
+ </with-param>
+ </call-template>
+</template>
+
+
+<template name="wc:var-to-hstr">
+ <param name="var" />
+
+ <!-- string separators (TODO: make configurable) -->
+ <variable name="pre" select="substring-before( $var, '4' )" />
+
+ <choose>
+ <when test="not( $pre = '' )">
+ <!-- before separator -->
+ <call-template name="wc:var-to-hstr">
+ <with-param name="var" select="$pre" />
+ </call-template>
+
+ <text> for </text>
+
+ <!-- after -->
+ <call-template name="wc:var-to-hstr">
+ <with-param name="var" select="substring-after( $var, '4' )" />
+ </call-template>
+ </when>
+
+ <!-- no separator; continue -->
+ <otherwise>
+ <call-template name="wc:_var-to-hstr">
+ <with-param name="var" select="$var" />
+ </call-template>
+ </otherwise>
+ </choose>
+</template>
+
+<!-- var to human-readable string -->
+<template name="wc:_var-to-hstr">
+ <param name="var" />
+
+ <!-- start by grabbing the prefix -->
+ <variable name="prefix">
+ <call-template name="wc:str-until-uc">
+ <with-param name="str" select="$var" />
+ </call-template>
+ </variable>
+
+ <!-- and grab the rest of the string after the prefix -->
+ <variable name="remain" select="substring-after( $var, $prefix )" />
+
+ <!-- convert the first char to lowercase so that we do not screw up the uc
+ prefix substr on the next call -->
+ <variable name="remain-recurse" select="
+ concat(
+ translate( substring( $remain, 1, 1 ), $wc:uc, $wc:lc ),
+ substring( $remain, 2 )
+ )
+ " />
+
+ <variable name="prelex" select="$wc:hlex//wc:lex[ @prefix=$prefix ]" />
+
+ <choose>
+ <when test="$prelex">
+ <if test="not( $remain-recurse = '' )">
+ <call-template name="wc:var-to-hstr">
+ <with-param name="var" select="$remain-recurse" />
+ </call-template>
+ </if>
+
+ <text> </text>
+ <value-of select="$prelex/@to" />
+ </when>
+
+ <!-- no knowledge of prefix; output it and then recurse -->
+ <otherwise>
+ <variable name="strlex" select="$wc:hlex//wc:lex[ @str=$prefix ]" />
+
+ <choose>
+ <!-- if we recognize this text as a lexeme to be replaced, then do so
+ -->
+ <when test="$strlex">
+ <value-of select="$strlex/@to" />
+ </when>
+
+ <!-- just output the text as-is -->
+ <otherwise>
+ <!-- ucfirst -->
+ <value-of select="
+ concat(
+ translate( substring( $prefix, 1, 1 ), $wc:lc, $wc:uc ),
+ substring( $prefix, 2 )
+ )
+ " />
+ </otherwise>
+ </choose>
+
+ <if test="not( $remain-recurse = '' )">
+ <text> </text>
+
+ <call-template name="wc:var-to-hstr">
+ <with-param name="var" select="$remain-recurse" />
+ </call-template>
+ </if>
+ </otherwise>
+ </choose>
+</template>
+
+<!-- get string prefix until reaching a upper-case char -->
+<template name="wc:str-until-uc">
+ <param name="str" />
+
+ <variable name="char" select="substring( $str, 1, 1 )" />
+
+ <choose>
+ <when test="$str = ''">
+ <!-- done; nothing else to do -->
+ </when>
+
+ <!-- did we find an upper-case char? -->
+ <when test="translate( $char, $wc:uc, '' ) = ''">
+ <!-- we're done; do nothing and do not output -->
+ </when>
+
+ <otherwise>
+ <!-- output the char and recurse -->
+ <value-of select="$char" />
+
+ <call-template name="wc:str-until-uc">
+ <with-param name="str" select="substring( $str, 2 )" />
+ </call-template>
+ </otherwise>
+ </choose>
+</template>
+
+</stylesheet>
diff --git a/src/current/doc/.gitignore b/src/current/doc/.gitignore
new file mode 100644
index 0000000..2fa45d0
--- /dev/null
+++ b/src/current/doc/.gitignore
@@ -0,0 +1,4 @@
+*.aux
+*.pdf
+*.log
+*.toc
diff --git a/src/current/doc/chapters/class.tex b/src/current/doc/chapters/class.tex
new file mode 100644
index 0000000..82a2f62
--- /dev/null
+++ b/src/current/doc/chapters/class.tex
@@ -0,0 +1,372 @@
+\chapter{Classification System}
+The classification system is one of the most powerful features of \lang,
+allowing precise control over the classification and conditional processing of
+large sets of data, whether it be external input or values generated from within
+\lang\ itself. Virtually every conditional calculation is best represented
+through use of the classification system.
+
+
+\section{Classification Matcher}
+Data classification is performed by the classification matcher (sometimes
+referred to simply as the ``matcher''). Put simply, it is a function (defined by
+\aref{cmatch}) that, given a vector of inputs, produces a boolean vector (which
+may itself contain boolean vectors) determining if the given input conforms to a
+set of stated rules. A set of rules operating on a set input vectors is
+collectively known as a \term{classification}. The system that performs matching
+based on classifications is referred to as a \term{classifier}.
+
+A single classification can be separated into a set of rules, often referred to
+as \term{matches} within the context of \lang. A single rule attempts to match
+on a vector of inputs.\footnote{Scalar inputs are a special condition defined in
+\sref{cmatch-scalar}.} A simple example of such a match is shown in
+\fref{cmatch-ex-single}.
+
+\begin{figure}[h]
+ $$
+ I = \left[
+ \begin{array}{c}
+ 1 \\ 3 \\ 4 \\ 1
+ \end{array}
+ \right]
+ \qquad
+ M = \left[
+ \begin{array}{c}
+ 1 \\ 4
+ \end{array}
+ \right]
+ \quad
+ \to
+ \quad
+ R = \left[
+ \begin{array}{c}
+ \top \\ \bot \\ \top \\ \top
+ \end{array}
+ \right].
+ $$
+
+ \caption{A simple classification match $M$ on input $I$ and its result vector
+ $R$.}
+ \label{f:cmatch-ex-single}
+\end{figure}
+
+In \fref{cmatch-ex-single}, the input vector $I$ is \term{matched} against the
+rule $M$. The output is a boolean result vector $R$ which can be summarized with
+the following rule:
+
+$$
+ R_n = \exists m\in M(m = I_n).
+$$
+\noindent
+In other words, $R_n$ is $\top$ if $I_n\in M$ and is $\bot$ if $I_n\notin M$.
+Under this definition, $M$ can be considered to be the \term{domain} under which
+a given input $I_n$ is considered to be valid (a \term{match}).
+
+We say that a classification rule \term{matches} if \emph{any} input matches.
+That is:
+
+$$
+ \left[\textrm{The rule $M$ matches input $I$}\right]
+ \iff
+ \top\in R
+$$
+\noindent
+Another way to think of this concept is the reduction of the result vector $R$
+using a logical OR. Alternatively, one could assert that:
+
+$$
+ \left[\textrm{The rule $M$ matches input $I$}\right]
+ \iff
+ \sum\limits_n R_n \geq 1, \qquad R \in \set{0,1},
+$$
+\noindent
+if an implementation were willing to use the sets \boolset and \set{1,0}
+interchangeably.\footnote{See \sref{cmatch-int}.}
+
+The following sections, however, serve to demonstrate that such a simple view of
+the classification system, while useful for an introductory demonstration, is
+not sufficient when considering the level of flexibility that is necessary to
+handle more complicated data (in particular, when $I$ is a
+matrix).\footnote{See $\Omega$-reductions, introduced in
+\asref{cmatch}{omega-reduce}.}
+
+%TODO: More example sections
+
+
+\subsection{Classification Match (cmatch) Algorithm}
+\label{a:cmatch}
+
+The classification match (``cmatch'') algorithm is used to determine if a given
+set of data matches a given set of classification criteria.
+
+Let $I$ be the vector of input values.\footnote{$I$ may be a matrix (a vector
+of vectors).} Let $M$ be the vector of predicates to match against $I$ such
+that a match will be considered successful if \emph{any} predicate is true.
+Since $I$ shall always be a vector of values---even if the vector contains only
+one element (see algorithm below for comments on scalar values)---$M$ should be
+a vector of one element if the desire is to match against a scalar value (rather
+than a vector of values). Let $c$ (clear) be a boolean value\footnote{$1$ or $0$
+when used within an integer context within the algorithm.} representing whether
+the results of this operation should be logically AND'd together with the
+prior cmatch result ($R'$ in the algorithm below); otherwise, the results will
+be OR'd (see step \ref{a:cmatch-c} below).
+
+Let $A\!\left(M,I,c,R'\right)$ (the ``algorithm'') be defined as:
+
+\begin{enumerate}
+ \item
+ Let $R$ be the result vector.
+
+ \item\label{a:cmatch-scalar}
+ If the given input vector $I$ is a scalar, it should be converted to a vector
+ of length 1 with the value of the single element being the original scalar
+ value of $I$---that is, let $s$ be the original scalar value of $I$; then: $I
+ = \left[ s \right]$. If $s$ is undefined, then an empty result vector should
+ be returned.
+
+ \item\label{a:cmatch:input-vectorize}
+ Step \ref{a:cmatch-scalar} should also be done to the match vector $M$,
+ yielding $M = \left[ s \right]$ where $s$ is the original scalar $M$. If $s$
+ is undefined, then it should be treated as if it were the integer
+ $0$.\footnote{Consistent with the behavior of the remainder of the DSL.}
+
+ \item
+ Step \ref{a:cmatch-scalar} should also be done to the prior result vector
+ $R'$, yielding $R = \left[ s \right]$ where $s$ is the original scalar $R'$.
+ This situation may result from recursing at step \ref{a:cmatch-mrecurse} when
+ $R'_k$ is a scalar. If $s$ is undefined, then $R'$ should be initialized to an
+ empty vector, implying a fresh match (no prior results).
+ \goodbreak
+
+ \item\label{a:cmatch-iter}
+ The length of the result vector $R$~($\#R$) shall be the larger of the length
+ of the input vector $I$~($\#I$) or the prior result vector $R'$~($\#R'$).
+ For each $I_k \in I$:
+
+ \begin{enumerate}
+ \item\label{a:cmatch-mrecurse}
+ If $I_k$ is a vector, recurse, beginning at step 1. Let $r =
+ A(M_k,I_k,c,R'_k)$.
+
+ \begin{align*}
+ u &= \left\{
+ \begin{array}{ll}
+ \bot & \textrm{if }\#R' > 0, \\
+ c & \textrm{otherwise.}
+ \end{array}
+ \right. \\
+ %
+ R_k &= \left\{
+ \begin{array}{ll}
+ r & \textrm{if $R'_k$ is a vector or undefined}, \\
+ \Omega(r,u) & \textrm{otherwise}.\footnotemark
+ \end{array}
+ \right.
+ \end{align*}
+
+ \footnotetext{\label{a:cmatch-order} If $R'_k$ is a scalar, we must ensure
+ consistency with step \ref{a:cmatch-c} to ensure that the algorithm is not
+ dependent on input or execution order. Note the use of $u$ in place of
+ $c$---this ensures that, if there are any $R'$, we are consistent with the
+ effects of step \ref{a:cmatch:fill} (but in reverse).}
+
+ Continue with the next $I$ at step \ref{a:cmatch-iter}.
+
+ \item
+ \label{a:cmatch:omega-reduce}
+ Otherwise, $I_k$ is a scalar. Let $t$ be a temporary (intermediate) scalar
+ such that $t = \exists m \in M m(I_k)$.
+
+ \item\label{a:cmatch-c}
+ Let $v = \Omega\left(R'_k,c\right)$ and let
+ $$
+ R_k = \left\{
+ \begin{array}{ll}
+ v \wedge t & c = \top, \\
+ v \vee t & c = \bot.
+ \end{array}
+ \right.,
+ $$
+
+ where\footnote{$\Omega$ is simply the recursive reduction of a vector using
+ a logical OR. This function exists to resolve the situation where $R'_k$ is
+ a vector of values when $I_k$ is a scalar, which will occur when $M_k$ is
+ scalar for any $k$ during one application of the cmatch algorithm and $M_k$
+ is a vector for another iteration, where $R'$ is the previous match using
+ scalars. Note also that $X$, according to the recursion rule, may only be
+ undefined on the first iteration (in effect initializing the value).}
+
+ $$
+ \Omega\left(X,u\right) = \left\{
+ \begin{array}{ll}
+ u & \textrm{if X is undefined,} \\
+ X & \textrm{if X is a scalar,} \\
+ \exists x\in X \Omega(x,u) & \textrm{otherwise.}
+ \end{array}
+ \right. \>
+ \mbox{
+ $X \in \left\{\textrm{undefined},\top,\bot\right\}$
+ or a vector.
+ }
+ $$
+ \end{enumerate}
+
+ \item\label{a:cmatch:fill}
+ Let $v = \Omega\left(R'_k,c\right) \wedge \neg c$. If $\#R' > \#I$,
+ $$
+ R_k = \left\{
+ \begin{array}{ll}
+ v & \exists n\in I(n\textrm{ is a scalar}), \\
+ \left[v\right] & \textrm{otherwise.}\footnotemark
+ \end{array}
+ \right.
+ k \in \left\{j : \#I \leq j < \#R' \right\}.
+ $$
+
+ \footnotetext{Note that step \ref{a:cmatch:fill} will produce results
+ inconsistent with the recursive step \ref{a:cmatch-mrecurse} if there exists
+ an $I_n$ that is a matrix; this algorithm is not designed to handle such
+ scenarios.}
+\end{enumerate}
+
+Given a set of classification criteria $C$ such that $C_k = M$ for some integer
+$k$ and some application of $A$, and a vectorized clear flag $c$ such that $c_k$
+is associated with $C_k$, the final result $F(\#C-1)$ shall be defined as
+
+$$
+ F(k) = \left\{
+ \begin{array}{ll}
+ A\left(C_k,I_k,c_k\right) & \textrm{k = 0,} \\
+ A\bigl(C_k,I_k,c_k,F\!\left(k-1\right)\bigr) & \textrm{otherwise.}
+ \end{array}
+ \right.
+$$
+
+The order of recursion on $F$ need not be right-to-left; $A$ is defined such
+that it will produce the same result when applied in any order. This is
+necessary since the input may be provided in any order.\footnote{Ibid,
+\ref{a:cmatch-order}.}
+
+\subsubsection{Boolean Classification Match}
+\label{s:cmatch-boolean}
+A scalar boolean classification match $b$ may be obtained simply as $b =
+\Omega\left(F,\bot\right)$, where $F$ and $\Omega$ are defined in the algorithm
+above. Consequently, note that an empty result set $F$ will be treated as
+$\bot$, since index $0$ will be undefined.
+
+\subsubsection{Match Vector}
+$M$ is defined to be a vector of predicates which serve to {\sl match} against a
+vector of input values. Most frequently, predicates will likely be against scalar
+values. In such a case, an implementation may choose to forego function
+application for performance reasons and instead match directly against the
+scalar value. However, this document will consider scalar matches in the context
+of predicates as functions. As such, if $M$ is a matrix, then the results are
+implementation-defined (since the value does not make sense within the algorithm
+as defined).
+
+\subsubsection{Integer Results}
+\label{s:cmatch-int}
+$A$ defines $R$ to be a vector/matrix of boolean values. However, it may be
+useful to use the cmatch results in calculations; as such, implementations that
+make use of or produce cmatch results are required to do one or both of the
+following where $b$ is a boolean scalar:
+
+\begin{enumerate}
+ \item
+ Implicitly consider $b$ to be $\textrm{int}\!\left(b\right)$ when used in
+ calculations, and/or
+
+ \item
+ Perform the implicit conversion before $R$ is returned from $A$,
+\end{enumerate}
+
+where the function {\sl int} is defined as
+
+$$
+ \textrm{int}(b) = \left\{
+ \begin{array}{ll}
+ 1 & \textrm{if }b = \top, \\
+ 0 & \textrm{if }b = \bot.
+ \end{array}
+ \right.\qquad
+ b \in \left\{\top,\bot\right\}.
+$$
+
+
+\subsection{Scalar Classification Matches}
+\label{s:cmatch-scalar}
+Implementations may find it convenient to support scalar inputs and scalar
+classification matches to represent matching ``all'' indexes of a vector.
+\aref{cmatch} defines both a classification match ($R$, and consequently $F$)
+and an input ($I$) to be a vector, which is generally sufficient. However, in
+the case where the number of indexes of the inputs and results of other matches
+may be arbitrary, it may be useful to apply a certain classification across all
+indexes, which cannot be done when $c = \top$ using \aref{cmatch}.
+
+The onus of such a feature is on the implementation---it should flag such input
+($I$) as a scalar, which is necessary since $I$ is unconditionally converted to
+a vector by step \asref{cmatch}{input-vectorize}. If an implementation decides
+to support scalar classification matches, \emph{it must conform to this
+section}. Let such a scalar flag be denoted $s_k \inbool$ respective to input
+$I_k$. Handling of both $F$ and $I$ is discussed in the sections that follow.
+
+\subsubsection{Mixing Scalar And Vectorized Inputs}
+\label{s:cmatch-scalar-mixed}
+Under the condition that $\exists v\in s(v=\top)$, the compiler must:
+
+\begingroup
+ % this definition is local to this group
+ \def\siset{k \in\set{j : s_j = \top}}
+
+ \begin{enumerate}
+ \item
+ Reorder inputs $I$ such that each scalar input $I_k, \siset$ be applied
+ after all non-scalar inputs have been matched using \aref{cmatch}.
+ \begin{enumerate}
+ \item
+ Consequently (and contrary to what was mentioned in \aref{cmatch}),
+ application order of $A$ with respect to inputs $I$ \emph{does} in fact
+ matter and implementations should ensure that this restriction holds
+ during runtime.
+ \end{enumerate}
+
+ \item
+ Before application of a scalar input, the scalar $I_k$ should be vectorized
+ according to the following rule:
+
+ $$
+ I'_{k,l} = I_k,
+ \qquad \siset,
+ \; 0 \leq l < \#R',
+ $$
+
+ where $R'$ is the value immediately before the application of $I_k$ as
+ defined in \aref{cmatch}.
+
+ \item
+ Application of \aref{cmatch} should then proceed as normal, using $I'$ in
+ place of $I$.
+ \end{enumerate}
+\endgroup
+
+\subsubsection{Converting Vectorized Match To Scalar}
+As defined by \aref{cmatch}, the result $R$ will always be a vector. An
+implementation may \emph{only} convert a vectorized match to a scalar using the
+method defined in this section under the condition that $\forall v\in
+s(v=\top)$; otherwise, there will be a loss of data (due to the expansion rules
+defined in \sref{cmatch-scalar-mixed}). The implementation also \emph{must not}
+reduce the vectorized match to a scalar using $\Omega$. An implementation
+\emph{may}, however, $\Omega$-reduce the match result $R$ into an
+\emph{separate} value as mentioned in \sref{cmatch-boolean}.
+
+Under the condition that $\forall v\in s(v=\top)$, the system may post-process
+$F$ (as defined in \aref{cmatch}) such that
+
+$$
+ F' = F_0,
+$$
+
+and return $F'$ in place of $F$.
+
+Note also that $F'$ may be fed back into \aref{cmatch} as an input and that the
+results will be consistent and well-defined according to
+\sref{cmatch-scalar-mixed} (and, consequently, this section).
diff --git a/src/current/doc/manual.sty b/src/current/doc/manual.sty
new file mode 100644
index 0000000..b3524f4
--- /dev/null
+++ b/src/current/doc/manual.sty
@@ -0,0 +1,30 @@
+% manual style package
+
+% these margins ensure that the PDF can be easily scrolled vertically without
+% worrying about alternating margins (good for viewing on screen, but not on
+% paper)
+\usepackage[margin=1.25in]{geometry}
+\usepackage{amsmath}
+
+\setcounter{secnumdepth}{3}
+\setcounter{tocdepth}{3}
+
+% no name yet
+\def\lang{the DSL}
+
+\def\sref#1{Section \ref{s:#1}}
+\def\fref#1{Figure \ref{f:#1}}
+\def\aref#1{Algorithm \ref{a:#1}}
+\def\asref#1#2{A\ref{a:#1}(\ref{a:#1:#2})}
+
+\def\set#1{%
+ \ifmmode%
+ \left\{#1\right\}%
+ \else
+ $\left\{#1\right\}$%
+ \fi%
+}
+\def\boolset{\set{\top,\bot}}
+\def\inbool{\in\boolset}
+
+\def\term#1{{\sl #1}}
diff --git a/src/current/doc/manual.tex b/src/current/doc/manual.tex
new file mode 100644
index 0000000..9d1d80e
--- /dev/null
+++ b/src/current/doc/manual.tex
@@ -0,0 +1,19 @@
+\documentclass[10pt]{book}
+
+%%begin preamble
+ \usepackage{manual}
+
+ \author{Mike Gerwitz\\LoVullo Associates}
+ \date{\today}
+%%end preamble
+
+\begin{document}
+
+\title{Calc DSL: Design Specification and Programmer's Manual}
+\maketitle
+
+\tableofcontents
+
+\include{chapters/class}
+
+\end{document}
diff --git a/src/current/dot.xsl b/src/current/dot.xsl
new file mode 100644
index 0000000..ed0bb39
--- /dev/null
+++ b/src/current/dot.xsl
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Outputs graph visualization of dependencies in DOT format
+-->
+
+<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:dot="http://www.lovullo.com/calc/dot">
+
+<xsl:import href="dot/depout.xsl" />
+<xsl:import href="dot/defnode.xsl" />
+
+<!-- supported sources (entry points) -->
+<xsl:include href="dot/pkg-obj.xsl" />
+<xsl:include href="dot/pkg-exec.xsl" />
+
+<xsl:output method="text" />
+
+
+<!--
+ Newline character
+-->
+<xsl:variable name="dot:nl" select="'&#10;'" />
+
+
+<!--
+ Immediately fails on unrecognized source type
+-->
+<xsl:template match="lv:package" priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>[dot] fatal: this is not an object/executable file: </xsl:text>
+ <xsl:text>no symbol dependencies found</xsl:text>
+ </xsl:message>
+</xsl:template>
+
+
+<!--
+ Beginning of a DOT document
+-->
+<xsl:template match="lv:package" mode="dot:head">
+ <xsl:text>/* dependency graph of </xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text> */</xsl:text>
+
+ <xsl:text>digraph "</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>" { </xsl:text>
+
+ <xsl:text>graph [rankdir="LR", ranksep="2"]; </xsl:text>
+</xsl:template>
+
+
+<!--
+ End of a DOT document
+-->
+<xsl:template match="lv:package" mode="dot:tail">
+ <xsl:text>}</xsl:text>
+</xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/src/current/dot/attr-color.xsl b/src/current/dot/attr-color.xsl
new file mode 100644
index 0000000..4ef7c01
--- /dev/null
+++ b/src/current/dot/attr-color.xsl
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Styles node color based on symbol type
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ Parameter
+-->
+<xsl:template mode="dot:attr-color" priority="5"
+ match="*[ @type='param' ]">
+
+ <dot:attr name="color">#204a87</dot:attr>
+</xsl:template>
+
+
+<!--
+ Param type
+-->
+<xsl:template mode="dot:attr-color" priority="5"
+ match="*[ @type='type' ]">
+
+ <dot:attr name="color">#729fcf</dot:attr>
+</xsl:template>
+
+
+<!--
+ Classification
+-->
+<xsl:template mode="dot:attr-color" priority="5"
+ match="*[ @type='class' or @type='cgen' ]">
+
+ <dot:attr name="color">#4e9a06</dot:attr>
+</xsl:template>
+
+
+<!--
+ Function
+-->
+<xsl:template mode="dot:attr-color" priority="5"
+ match="*[ @type='func' ]">
+
+ <dot:attr name="color">#c4a000</dot:attr>
+</xsl:template>
+
+
+<!--
+ Map
+-->
+<xsl:template mode="dot:attr-color" priority="5"
+ match="*[ @type='map' or @type='retmap' ]">
+
+ <dot:attr name="color">#888a85</dot:attr>
+</xsl:template>
+
+
+<!--
+ Default
+-->
+<xsl:template match="*" mode="dot:attr-color" priority="1">
+ <!-- no color -->
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/dot/attr-extern.xsl b/src/current/dot/attr-extern.xsl
new file mode 100644
index 0000000..318da4a
--- /dev/null
+++ b/src/current/dot/attr-extern.xsl
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Styles node based on locality
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ External nodes should be styled as such
+-->
+<xsl:template mode="dot:attr-extern" priority="5" match="
+ *[ @src and not( @src='' ) ]
+ ">
+
+ <dot:attr name="style">dashed</dot:attr>
+</xsl:template>
+
+
+
+<!--
+ Default node attributes
+-->
+<xsl:template match="preproc:sym" mode="dot:defnode-attr" priority="1">
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/dot/attr-keep.xsl b/src/current/dot/attr-keep.xsl
new file mode 100644
index 0000000..3a72af4
--- /dev/null
+++ b/src/current/dot/attr-keep.xsl
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Styles node based on keep flag
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ External nodes should be styled as such
+-->
+<xsl:template mode="dot:attr-keep" priority="5" match="
+ *[ @keep='true' ]
+ ">
+
+ <dot:attr name="fontcolor">red</dot:attr>
+</xsl:template>
+
+
+
+<!--
+ Default node attributes
+-->
+<xsl:template match="preproc:sym" mode="dot:defnode-keep" priority="1">
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/dot/attr-shape.xsl b/src/current/dot/attr-shape.xsl
new file mode 100644
index 0000000..f4bc289
--- /dev/null
+++ b/src/current/dot/attr-shape.xsl
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Styles node shape based on symbol type
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ Parameter
+-->
+<xsl:template mode="dot:attr-shape" priority="5"
+ match="*[ @type='param' ]">
+
+ <dot:attr name="shape">box</dot:attr>
+</xsl:template>
+
+
+<!--
+ Classification
+-->
+<xsl:template mode="dot:attr-shape" priority="5"
+ match="*[ @type='class' or @type='cgen' ]">
+
+ <dot:attr name="shape">octagon</dot:attr>
+</xsl:template>
+
+
+<!--
+ Function
+-->
+<xsl:template mode="dot:attr-shape" priority="5"
+ match="*[ @type='func' ]">
+
+ <dot:attr name="shape">component</dot:attr>
+</xsl:template>
+
+
+<!--
+ Map
+-->
+<xsl:template mode="dot:attr-shape" priority="5"
+ match="*[ @type='map' or @type='retmap' ]">
+
+ <dot:attr name="shape">note</dot:attr>
+</xsl:template>
+
+
+<!--
+ Default
+-->
+<xsl:template match="*" mode="dot:attr-shape" priority="1">
+ <!-- default shape -->
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/src/current/dot/defnode-attr.xsl b/src/current/dot/defnode-attr.xsl
new file mode 100644
index 0000000..778835a
--- /dev/null
+++ b/src/current/dot/defnode-attr.xsl
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Outputs graph visualization of dependencies in DOT format
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+<xsl:import href="./attr-color.xsl" />
+<xsl:import href="./attr-shape.xsl" />
+<xsl:import href="./attr-extern.xsl" />
+<xsl:import href="./attr-keep.xsl" />
+
+
+<!--
+ External nodes should be styled as such
+-->
+<xsl:template mode="dot:defnode-attr" priority="5" match="
+ preproc:sym[ @src and not( @src='' ) ]
+ ">
+
+ <dot:attr name="label">
+ <xsl:value-of select="@src" />
+ <xsl:text>/\n</xsl:text>
+ <xsl:value-of select="@name" />
+ </dot:attr>
+
+ <dot:attr name="tooltip">
+ <xsl:value-of select="@src" />
+ <xsl:text>/</xsl:text>
+ <xsl:value-of select="@name" />
+ </dot:attr>
+</xsl:template>
+
+
+
+<!--
+ Default node attributes
+-->
+<xsl:template match="preproc:sym" mode="dot:defnode-attr" priority="1">
+</xsl:template>
+
+
+<!--
+ Render an attribute list as a comma-delimited string
+
+ Expects a tree of dot:attr nodes where @name is the name of the attribute, and
+ its normalized text is the value. The value will be quoted; double quotes must
+ be manually escaped prior to calling this template.
+-->
+<xsl:template name="dot:render-attr-list">
+ <xsl:param name="attr-list" />
+
+ <xsl:for-each select="$attr-list/dot:attr">
+ <xsl:if test="position() > 1">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+
+ <xsl:value-of select="@name" />
+ <xsl:text>="</xsl:text>
+ <xsl:value-of select="normalize-space( text() )" />
+ <xsl:text>"</xsl:text>
+ </xsl:for-each>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/current/dot/defnode.xsl b/src/current/dot/defnode.xsl
new file mode 100644
index 0000000..c34c591
--- /dev/null
+++ b/src/current/dot/defnode.xsl
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Graph node definitions
+
+ Nodes do not need to be defined (DOT will generate them upon first reference);
+ this defines nodes that require additional data associated with them.
+-->
+
+<xsl:stylesheet version="2.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
+ xmlns:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+<xsl:import href="./defnode-attr.xsl" />
+
+
+<!--
+ Do not declare constants or generated symbols
+
+ XXX: Duplicated logic from smy-ref!
+-->
+<xsl:template mode="dot:defnode" priority="9" match="
+ preproc:sym[
+ @type='const'
+ or @type='map' or @type='retmap'
+ or @type='map:head' or @type='map:tail'
+ or @type='retmap:head' or @type='retmap:tail'
+ or (
+ @type='type'
+ and (
+ @name='integer'
+ or @name='float'
+ or @name='boolean'
+ )
+ )
+ or @parent
+ or @preproc:generated='true'
+ ]
+ ">
+</xsl:template>
+
+
+<!--
+ Process parent symbol in place of current symbol
+
+ Symbols with defined parents are generated as part of that parent and will
+ therefore be treated as a single unit.
+-->
+<xsl:template match="preproc:sym[ @parent ]" mode="dot:defnode" priority="7">
+ <xsl:variable name="parent" select="@parent" />
+
+ <xsl:apply-templates select="
+ parent::preproc:symtable/preproc:sym[ @name=$parent ]
+ " />
+</xsl:template>
+
+
+<!--
+ Default node definition
+
+ If no attributes are generated, then the node will be entirely omitted (it'll
+ be created automatically by DOT when referenced).
+-->
+<xsl:template match="preproc:sym" mode="dot:defnode" priority="1">
+ <xsl:variable name="attr">
+ <xsl:call-template name="dot:render-attr-list">
+ <xsl:with-param name="attr-list">
+ <!-- this kluge exists because of XSLT limitiations and the confusion
+ that would result (in this particular situation) from
+ xsl:apply-imports
+ -->
+ <xsl:apply-templates select="." mode="dot:defnode-attr" />
+ <xsl:apply-templates select="." mode="dot:attr-extern" />
+ <xsl:apply-templates select="." mode="dot:attr-color" />
+ <xsl:apply-templates select="." mode="dot:attr-shape" />
+ <xsl:apply-templates select="." mode="dot:attr-keep" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:if test="not( $attr = '' )">
+ <xsl:text>"</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>" [</xsl:text>
+ <xsl:value-of select="$attr" />
+
+ <xsl:if test="@src and not( @src='' )">
+ <xsl:if test="$attr">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+
+ <!-- link to other packages in the graph for navigation -->
+ <xsl:text>href="</xsl:text>
+ <xsl:value-of select="concat( @src, '.svg' )" />
+ <xsl:text>"</xsl:text>
+ </xsl:if>
+ <xsl:text>];</xsl:text>
+
+ <xsl:value-of select="$dot:nl" />
+ </xsl:if>
+</xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/src/current/dot/depout.xsl b/src/current/dot/depout.xsl
new file mode 100644
index 0000000..5b3dbda
--- /dev/null
+++ b/src/current/dot/depout.xsl
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Outputs dependency relationship to a directed graph
+-->
+
+<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:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+
+<!--
+ Container for all the dependencies
+-->
+<xsl:template match="preproc:sym-deps" mode="dot:depout" priority="5">
+ <xsl:apply-templates mode="dot:depout" />
+</xsl:template>
+
+
+<!--
+ Treat the entry point (lv:yield) as the root node
+-->
+<xsl:template match="preproc:sym-dep[ @name='___yield' ]" mode="dot:depout" priority="6">
+ <xsl:value-of select="@name" />
+ <xsl:text> [root=ctr,fontsize=24,style=bold,label="Yield"]; </xsl:text>
+
+ <xsl:apply-templates mode="dot:depout" />
+</xsl:template>
+
+
+<!--
+ Constant and generated symbols will not be rendered
+-->
+<xsl:template mode="dot:depout" priority="5" match="
+ preproc:sym-dep[
+ @name=ancestor::lv:package/preproc:symtable/preproc:sym[
+ @type='const'
+ or @preproc:generated='true'
+ ]/@name
+ ]
+ ">
+</xsl:template>
+
+
+<!--
+ Container for symbol dependencies
+
+ That is: this node represents a symbol, and its children are its dependencies.
+-->
+<xsl:template match="preproc:sym-dep" mode="dot:depout" priority="3">
+ <xsl:apply-templates mode="dot:depout" />
+</xsl:template>
+
+
+<!--
+ Do not output relationships to primitives or constants; or generated
+-->
+<xsl:template mode="dot:depout" priority="5" match="
+ preproc:sym-ref[
+ @type='type'
+ and (
+ @name='integer'
+ or @name='float'
+ or @name='boolean'
+ )
+ or @type='const'
+ or @type='lparam'
+ or @type='map' or @type='retmap'
+ or @type='map:head' or @type='map:tail'
+ or @type='retmap:head' or @type='retmap:tail'
+ ]
+ ">
+
+ <!-- skip -->
+</xsl:template>
+
+
+<!--
+ Process generated symbol deps as our own
+
+ Generated symbols are not known by the user, so they should be treated as part
+ of the unit from which they are generated.
+-->
+<xsl:template mode="dot:depout" priority="4" match="
+ preproc:sym-ref[
+ @name=ancestor::lv:package/preproc:symtable/preproc:sym[
+ @preproc:generated='true'
+ ]/@name
+ ]
+ ">
+
+ <xsl:param name="usedby" select="parent::preproc:sym-dep/@name" />
+
+ <xsl:variable name="name" select="@name" />
+
+ <!-- process the generated symbol's deps as our own -->
+ <xsl:apply-templates mode="dot:depout" select="
+ ancestor::preproc:sym-deps/preproc:sym-dep[
+ @name=$name
+ ]/preproc:sym-ref
+ ">
+ <xsl:with-param name="usedby" select="$usedby" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<!--
+ Process references with parents as if they were their parent symbol
+
+ Symbols with defined parents are generated as part of that parent and will
+ therefore be treated as a single unit.
+-->
+<xsl:template match="preproc:sym-ref[ @parent ]" mode="dot:depout" priority="3">
+ <xsl:param name="usedby" select="parent::preproc:sym-dep/@name" />
+
+ <xsl:variable name="parent" select="@parent" />
+
+ <xsl:apply-templates mode="dot:depout" select="
+ ancestor::lv:package/preproc:symtable/preproc:sym[ @name=$parent ]
+ ">
+ <xsl:with-param name="usedby" select="$usedby" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<!--
+ Trigger processing of symbol associated with the ref
+-->
+<xsl:template match="preproc:sym-ref" mode="dot:depout" priority="2">
+ <xsl:param name="usedby" select="parent::preproc:sym-dep/@name" />
+
+ <xsl:variable name="name" select="@name" />
+
+ <xsl:apply-templates mode="dot:depout" select="
+ ancestor::lv:package/preproc:symtable/preproc:sym[ @name=$name ]
+ ">
+ <xsl:with-param name="usedby" select="$usedby" />
+ </xsl:apply-templates>
+</xsl:template>
+
+
+<!--
+ Output symbol reference to directed graph
+
+ The symbol (dependency) is referenced as a node adjacent to the node of the
+ symbol that uses it. The edge is directed toward the dependency and shall be
+ read as "uses".
+
+ For example: "foo uses bar":
+ (foo) -> (bar)
+-->
+<xsl:template match="preproc:sym" mode="dot:depout" priority="5">
+ <xsl:param name="usedby" />
+
+ <xsl:variable name="attr">
+ <xsl:call-template name="dot:render-attr-list">
+ <xsl:with-param name="attr-list">
+ <xsl:apply-templates select="." mode="dot:attr-extern" />
+ <xsl:apply-templates select="." mode="dot:attr-color" />
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:text>"</xsl:text>
+ <xsl:value-of select="$usedby" />
+ <xsl:text disable-output-escaping="yes">" -&gt; "</xsl:text>
+ <xsl:value-of select="@name" />
+ <xsl:text>"</xsl:text>
+
+ <xsl:if test="not( $attr='' )">
+ <xsl:text> [</xsl:text>
+ <xsl:value-of select="$attr" />
+ <xsl:text>]</xsl:text>
+ </xsl:if>
+
+ <xsl:text>;</xsl:text>
+
+ <xsl:value-of select="$dot:nl" />
+</xsl:template>
+
+
+<!--
+ Bail out if asked to render something unexpected
+-->
+<xsl:template match="*" mode="dot:depout" priority="1">
+ <xsl:message terminate="yes">
+ <xsl:text>error: what do I do!?: unexpected </xsl:text>
+ <xsl:value-of select="name()" />
+ </xsl:message>
+</xsl:template>
+
+
+<!--
+ Extra comments and attributes are ignored
+
+ text() is ignored, otherwise, extra whitespace corresponding to the
+ indentation of nodes appears in the output.
+
+ Ignoring attributes is just in case of an xpath whoopsie, but probably isn't
+ necessary, and is probably dangerous (because it may veil bugs).
+-->
+<xsl:template match="@*|text()" mode="dot:depout" priority="1">
+ <!-- ignore -->
+</xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/src/current/dot/pkg-exec.xsl b/src/current/dot/pkg-exec.xsl
new file mode 100644
index 0000000..1c934bf
--- /dev/null
+++ b/src/current/dot/pkg-exec.xsl
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Processes executable file dependency graph
+-->
+
+<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:l="http://www.lovullo.com/rater/linker"
+ xmlns:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ Entry point for linked executable (.xmle) DOT generation
+
+ We wish to generate a dependency graph for an entire program. This approach is
+ a little bit different than the approach to processing object files, because
+ we know that the linker's symbol table contains *only* those symbols that are
+ used (or kept). We further know that each symbol (unless there's a bug in the
+ linker) is referenced only a single time in the symbol table.
+
+ This makes our job easy: simply walk the symbol table, look up the
+ preproc:sym-dep in the source package, and render as we normally would for an
+ object file.
+
+ Lord Jesus it's a fire.
+-->
+<xsl:template match="lv:package[ l:dep ]" priority="9">
+ <xsl:apply-templates select="." mode="dot:head" />
+
+ <!-- we know that all symbols in the linker symbol table are used, so we can
+ immediately generate the node definitions -->
+ <xsl:apply-templates mode="dot:defnode"
+ select="l:dep/preproc:sym" />
+
+ <!-- outputting the dependencies of those symbols is more involved and
+ requires processing data from each object file -->
+ <xsl:apply-templates select="l:dep/preproc:sym" mode="dot:ldep-sym-deps">
+ <xsl:with-param name="exec-name" select="concat( @__rootpath, @name )" />
+ </xsl:apply-templates>
+
+ <xsl:apply-templates select="." mode="dot:tail" />
+</xsl:template>
+
+
+<!--
+ Omit symbols with parent references
+
+ Symbols with parents are generated from that parent and will be considered to
+ be a single unit. Since the parent will also be in the symbol table (it is,
+ after all, a dependency), we don't have to worry about these at all.
+-->
+<xsl:template match="preproc:sym[ @parent ]" mode="dot:ldep-sym-deps" priority="5">
+ <!-- ignore -->
+</xsl:template>
+
+
+<!--
+ Process dependencies for each symbol
+
+ The linker symbol table only stores a flattened symbol list; to get the
+ symbol's dependencies, we must consult the source object file.
+-->
+<xsl:template match="preproc:sym" mode="dot:ldep-sym-deps" priority="1">
+ <xsl:param name="exec-name" />
+
+ <xsl:variable name="name" select="@name" />
+
+ <!-- empty @src implies program package -->
+ <xsl:variable name="pkg" select="
+ if ( @src and not( @src='' ) ) then
+ document( concat( @src, '.xmlo' ), / )/lv:package
+ else
+ document( concat( $exec-name, '.xmlo' ), / )/lv:package
+ " />
+
+ <xsl:variable name="sym-dep" select="
+ $pkg/preproc:sym-deps/preproc:sym-dep[
+ @name=$name
+ ]
+ " />
+
+ <xsl:if test="not( $sym-dep )">
+ <xsl:message terminate="yes">
+ <xsl:text>error: cannot locate symbol dependencies for `</xsl:text>
+ <xsl:value-of select="concat( @src, '/', @name )" />
+ <xsl:text>'</xsl:text>
+ </xsl:message>
+ </xsl:if>
+
+ <xsl:apply-templates select="$sym-dep" mode="dot:depout" />
+</xsl:template>
+
+
+</xsl:stylesheet>
+
diff --git a/src/current/dot/pkg-obj.xsl b/src/current/dot/pkg-obj.xsl
new file mode 100644
index 0000000..d281cff
--- /dev/null
+++ b/src/current/dot/pkg-obj.xsl
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Processes object file dependency graph
+-->
+
+<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:dot="http://www.lovullo.com/calc/dot"
+ xmlns:preproc="http://www.lovullo.com/rater/preproc">
+
+
+<!--
+ Entry point for object file DOT generation
+
+ To render the graph, we first declare all nodes associated with all referenced
+ symbols in the symbol tree (it's important to check against preproc:sym-deps,
+ since it's likely that not all imported symbols are used); this allows setting
+ attributes for the symbol nodes on the graph without having to worry about
+ duplicate code later on.
+
+ After that, we simply recurse through the dependency list and neighbor the
+ nodes.
+-->
+<xsl:template match="lv:package[ preproc:sym-deps ]" priority="5">
+ <xsl:apply-templates select="." mode="dot:head" />
+
+ <xsl:variable name="sym-deps" select="preproc:sym-deps" />
+
+ <!-- pre-style all referenced nodes (the symbol table is likely to contain
+ references to symbols that were imported but not used) -->
+ <xsl:apply-templates mode="dot:defnode" select="
+ preproc:symtable/preproc:sym[
+ @name=$sym-deps/preproc:sym-dep/preproc:sym-ref/@name
+ or @name=$sym-deps/preproc:sym-dep/preproc:sym-ref/@parent
+ ]
+ " />
+
+ <!-- output graph description -->
+ <xsl:apply-templates select="preproc:sym-deps" mode="dot:depout" />
+
+ <xsl:apply-templates select="." mode="dot:tail" />
+</xsl:template>
+
+
+</xsl:stylesheet>
+
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>
+