/** This is a formatter for MathML (presentation format only at this time.) It formats expressions into MathML form. */ class MathML { /** Formats an expression. This creates the top-level tag and then dispatches to formatExpr[eq] to format the body of the document. indent is the indent level (an integer) to begin the indentation. */ class format[eq, inline=true, indent=0] := { spc = indent[indent] return spc + "\n" + formatExpr[eq, indent+1] + spc + "\n" } /** Formats an expression. This dispatches to the appropriate formatter for the expression type. This is called recursively. */ class formatExpr[eq, indent] := { spc = indent[indent] /* First, determine if an expression can be broken into a numerator and denominator. If the denominator is not 1, this means that it can be broken up. This takes care of division, exponentiation by negative exponents, fractions, and more. This formats into a vertically-separated fraction divided by a horizontal line. */ [num, denom] = frac = numeratorDenominator[eq, true, false] if ! isInteger[denom] or denom != 1 { return spc + "\n" + formatExpr[num, indent+1] + formatExpr[denom, indent+1] + spc + "\n" } type = type[eq] if type == "Add" return formatAdd[eq, indent] if type == "Multiply" return formatMultiply[eq, indent] if type == "Power" return formatPower[eq, indent] if type == "FunctionCall" return formatFunctionCall[eq, indent] /** Handle other operators. This should probably be extended to all operator types like <=, >=, >, etc. However, some operators have other than 2 children and formatOperator currently only handles two-argument infix operators. */ if isOperator[eq] and getChildCount[eq] == 2 { // op = getOperatorSymbol[eq] // if op == " === " or op == " = " or op == " -> " return formatOperator[eq, indent] } if type == "Symbol" return formatSymbol[eq, indent] if isUnit[eq] return formatUnit[eq, indent] return "Unidentified: type=" + type + ": $eq" } /** Formats an addition expression. It separates fractions into individual terms. */ class formatAdd[eq, indent] := { spc = indent[indent] spc1 = indent[indent+1] size = getChildCount[eq] ret = spc + "\n" for i=0 to size-1 { child = getChild[eq,i] if type[child] == "Multiply" and isNegativeUnit[getChild[child,0]] { ret = ret + spc1 + "-\n" child = -child } else if i > 0 ret = ret + spc1 + "+\n" // Format lower-precedence children in parentheses ep = getOperatorPrecedence[child] if ep != undef and ep < getOperatorPrecedence[eq] sub = parens[child, indent+1] else sub = formatExpr[child, indent+1] ret = ret + sub } return ret + spc + "\n" } /** Formats a multiplication expression. */ class formatMultiply[eq, indent] := { spc = indent[indent] spc1 = indent[indent+1] size = getChildCount[eq] ret = spc + "\n" for i=0 to size-1 { child = getChild[eq,i] if i > 0 ret = ret + spc1 + " \n" // Format lower-precedence children in parentheses ep = getOperatorPrecedence[child] if ep != undef and ep < getOperatorPrecedence[eq] expF = parens[child, indent+1] else expF = formatExpr[child, indent+1] ret = ret + expF } return ret + spc + "\n" } /** Formats a power expression with raised exponent. */ class formatPower[eq, indent] := { base = getChild[eq,0] exp = getChild[eq,1] [num, denom] = numeratorDenominator[exp] if num == 1 if denom == 2 return formatSqrt[base, indent] else if isInteger[denom] return formatRoot[base, exp, indent] separate = false // Format lower-precedence children in parentheses bp = getOperatorPrecedence[base] if bp != undef and bp < getOperatorPrecedence[eq] baseF = parens[base, indent+1] else baseF = formatExpr[base, indent+1] // Format lower-precedence children in parentheses ep = getOperatorPrecedence[exp] if ep != undef and ep < getOperatorPrecedence[eq] expF = parens[exp, indent+1] else expF = formatExpr[exp, indent+1] spc = indent[indent] spc1 = indent[indent+1] return spc + "\n" + baseF + expF + spc + "\n" } /** Formats a square root. */ class formatSqrt[eq, indent] := { spc = indent[indent] return spc + "\n" + formatExpr[eq, indent+1] + spc + "\n" } /** Formats a root. Exp should be a rational number with 1 as the numerator. */ class formatRoot[base, exp, indent] := { spc = indent[indent] return spc + "\n" + formatExpr[base, indent+1] + formatUnit[denominator[exp], indent+1] + spc + "\n" } /** Formats a function call in mathematical notation. */ class formatFunctionCall[eq, indent] := { spc = indent[indent] spc1 = indent[indent+1] spc2 = indent[indent+2] spc3 = indent[indent+3] res = spc + "\n" + spc1 + "" + HTMLEncode[getChild[eq, 0]] + "\n" + spc1 + "\n" + spc1 + "\n" + spc2 + "[\n" + spc2 + "\n" for i = 1 to getChildCount[eq]-1 { if i > 1 res = res + spc3 + ",\n" res = res + formatExpr[getChild[eq,i], indent+3] } return res + spc2 + "\n" + spc2 + "]\n" + spc1 + "\n" + spc + "\n" } /** Format operator that has 2 children and is infix. */ class formatOperator[eq, indent] := { left = getChild[eq,0] right = getChild[eq,1] // Format lower-precedence children in parentheses lp = getOperatorPrecedence[left] if lp != undef and lp < getOperatorPrecedence[eq] leftF = parens[left, indent+1] else leftF = formatExpr[left, indent+1] rp = getOperatorPrecedence[right] if rp != undef and rp < getOperatorPrecedence[eq] rightF = parens[right, indent+1] else rightF = formatExpr[right, indent+1] spc = indent[indent] spc1 = indent[indent+1] return spc + "\n" + leftF + spc1 + "" + HTMLEncode[trim[getOperatorSymbol[eq]]] + "\n" + rightF + spc + "\n" } /** Put parentheses around the specified expression. */ class parens[eq, indent] := { spc = indent[indent] spc1 = indent[indent+1] return spc + "\n" + spc1 +"(\n" + formatExpr[eq, indent+1] + spc1 +")\n" + spc + "\n" } /** Formats a Unit. */ class formatUnit[eq, indent] := { spc = indent[indent] spc1 = indent[indent+1] // Is a dimensionless real number? if eq conforms dimensionless { if isReal[eq] { if eq > 0 // Positive number return spc + "" + HTMLEncode[eq] + "\n" else { // Negative number, format as operator plus mn. Ugh. return spc + "\n" + spc1 + "-\n" + formatExpr[-eq, indent+1] + spc + "\n" } } if isComplex[eq] { im = Im[eq] return spc + "\n" + spc1 +"(\n" + formatExpr[Re[eq], indent+1] + spc1 + (im > 0 ? ("+\n") : ("-")) + (im >= 0 ? formatExpr[im, indent+1] : formatExpr[-im, indent+1]) + spc1 + " \n" + spc1 + "\n" + //TODO: getImaginarySymbol[]? spc1 +")\n" + spc + "\n" } } // TODO: Format dimensions and complex numbers and rational numbers // (Rational numbers should get handled by numeratorDenominator case // in formatExpr) dimensions = dimensionsToArray[eq] size = length[dimensions] ret = spc + "\n" + formatExpr[getScale[eq], indent+1] + spc1 + " \n" spc2 = indent[indent+2] for i=0 to size-1 { [symbol, exp] = dimensions@i if i > 0 ret = ret + spc1 + "· \n" if (exp != 1) ret = ret + spc1 + "\n" + spc2 + "" + HTMLEncode[symbol] + "\n"+ formatUnit[exp, indent+2] + spc1 + "\n" else ret = ret + spc1 + "" + HTMLEncode[symbol] + "\n" } ret = ret + spc + "\n" return ret } class formatSymbol[eq, indent] := { spc = indent[indent] return spc + "" + HTMLEncode[inputFormUnicode[eq]] + "\n" } /** Creates an indent with levels spaces. */ class indent[levels] := { repeat[" ", levels] } /* This encodes strings safely for HTML. */ class HTMLEncode[line] := { line = toString[line] line =~ %s/&/&/g; line =~ %s//>/g; return line } /** Test formatting of an expression by rendering it to a file and displays it in a browser. */ class test[eq, inline=true, indent=0] := { f = "MathMLTest.html" w = new Writer[f] raw = HTMLEncode[inputFormUnicode[eq]] w.println[""" MathML Test

Raw expression: $raw

"""] w.print[format[eq, inline, 3]] w.println["""

\n \n"""] w.close[] browse[f] } } "MathML.frink included correctly!"