Mike Gerwitz

Activist for User Freedom

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