diff options
Diffstat (limited to 'src/current/include/calc-display.xsl')
-rw-r--r-- | src/current/include/calc-display.xsl | 702 |
1 files changed, 702 insertions, 0 deletions
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> & </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"> & </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> |