Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/current/compiler/worksheet.xsl')
-rw-r--r--src/current/compiler/worksheet.xsl543
1 files changed, 543 insertions, 0 deletions
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>