Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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>
+
+ <xsl:choose>
+ <xsl:when test="lv:for-each">
+ <xsl:apply-templates select="lv:for-each/lv:*" mode="preproc:inline-apply">
+ <xsl:with-param name="name" select="$name" />
+ </xsl:apply-templates>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:apply-templates select="." mode="preproc:inline-apply">
+ <xsl:with-param name="name" select="$name" />
+ </xsl:apply-templates>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- the next pass will perform the template application -->
+ <preproc:repass tpl="{$name}" />
+</xsl:template>
+
+
+<xsl:template match="lv:inline-template|lv:for-each/lv:set" mode="preproc:inline-apply">
+ <xsl:param name="name" />
+
+ <!-- immediately apply the template -->
+ <lv:apply-template name="{$name}">
+ <!-- each attribute will be considered to be a template param (and any
+ parent lv:inline-template attributes will be added to each and every
+ set) -->
+ <xsl:for-each select="@*|ancestor::lv:inline-template/@*">
+ <lv:with-param name="@{local-name()}@" value="{.}" />
+ </xsl:for-each>
+ </lv:apply-template>
+</xsl:template>
+
+
+<xsl:template mode="preproc:inline-apply"
+ match="lv:for-each/lv:sym-set">
+ <xsl:param name="name" />
+
+ <xsl:param name="src-root" as="element( lv:package )"