diff options
Diffstat (limited to 'src/current/compiler/worksheet.xsl')
-rw-r--r-- | src/current/compiler/worksheet.xsl | 543 |
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> |