formatEquation.frink

Download or view formatEquation.frink in plain text format


/** This contains functions for formatting equations into a traditional form,
    notably putting numerators above denominators in the result.   For example,
    this turns

    a d^-2 (b + e)^-1

    to

         a    
    ──────────
    d² (b + e)

    Or the continued fraction:
    f = noEval[a0 + b1 / (a1 + b2/(a2 + b3/(a3 + b4/(a4 + x))))]
    formatExpression[f]

    to

               b1         
     ─────────────────────
                 b2       
          ────────────────
a0 +                b3    
     a1 +      ───────────
          a2 +        b4  
               a3 + ──────
                    a4 + x

    This will hopefully be put into Frink as a new formatter.

    TODO:  Try to align vertically along midlines?
*/


/** Formats an expression.  This dispatches to the appropriate formatter
    for the expression type and is generally called recursively. */

formatExpression[eq] :=
{
   /* 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]
   if denom != 1
      return formatTable[[formatExpression[num], formatExpression[denom]], "center", "center", "\u2500"]

   type = type[eq]

   if type == "Add"
      return formatAdd[eq]

   if type == "Multiply"
      return formatMultiply[eq]

   if type == "Power"
      return formatPower[eq]

   if type == "FunctionCall"
      return formatFunctionCall[eq]

   /** 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]
   {
      op = getOperatorSymbol[eq]
      if op == " === " or op == " = " or op == " -> "
         return formatOperator[eq]
   }
   
   return eq
}

// Formats an addition expression to separate fractions.
formatAdd[eq] :=
{
   size = getChildCount[eq]
   parts = new array
   for i=0 to size-1
   {
      child = getChild[eq,i]
      if type[child] == "Multiply" and isNegativeUnit[getChild[child,0]]
      {
         parts.push["-"]
         child = -child
      } else
      if i > 0
         parts.push["+"]
      
      // Format lower-precedence children in parentheses
      ep = getOperatorPrecedence[child]
      expF = formatExpression[child]
      if ep != undef and ep < getOperatorPrecedence[eq]
         expF = formatParensCompact[expF]
      
      parts.push[expF]
   }

   // Format the table into horizontally-separated sums.
   return formatTable[[parts]]
}

// Formats a multiplication expression.
formatMultiply[eq] :=
{
   size = getChildCount[eq]
   parts = new array
   for i=0 to size-1
   {
      child = getChild[eq,i]

      // Format lower-precedence children in parentheses
      ep = getOperatorPrecedence[child]
      expF = formatExpression[child]
      if ep != undef and ep < getOperatorPrecedence[eq]
         expF = formatParensCompact[expF]

      parts.push[expF]
   }

   // Format the table into horizontally-separated sums with implicit operators
   return formatTable[[parts]]
}

// Formats a power expression with raised exponent.
formatPower[eq] :=
{
   base = getChild[eq,0]
   exp  = getChild[eq,1]
   separate = false

   // Format lower-precedence children in parentheses
   baseF = formatExpression[base]
   bp = getOperatorPrecedence[base]
   if bp != undef and bp < getOperatorPrecedence[eq]
      baseF = formatParensCompact[baseF]

   /* If the exponent is an integer, format it inline as Unicode superscript
      digits. */

   if isInteger[exp]
      expF = toUnicodeSuperscript[exp]
   else
   {
      expF = formatExpression[exp]
      separate = true
   }
   
   ep = getOperatorPrecedence[exp]
   if ep != undef and ep < getOperatorPrecedence[eq]
      expF = formatParensCompact[expF]

   if separate
      return formatTable[[["",expF],[baseF,""]], "center", "top", "", ""]
   else
      return formatTable[[[baseF,expF]], "center", "top", "", ""]
}

// Formats a function call in mathematical notation.
formatFunctionCall[eq] :=
{
   args = new array
   for i = 1 to getChildCount[eq]-1
   {
      if i > 1
         args.push[", "]
      args.push[formatExpression[getChild[eq,i]]]
   }
   
   return formatTable[[[getChild[eq,0], formatBracketsCompact[[args]]]], "", "","", "" ]
}

// Format operator that has 2 children and is infix.
formatOperator[eq] :=
{
   left =  getChild[eq,0]
   right = getChild[eq,1]
   leftF = formatExpression[left]
   rightF = formatExpression[right]

   // Format lower-precedence children in parentheses
   lp = getOperatorPrecedence[left]
   if lp != undef and lp < getOperatorPrecedence[eq]
      leftF = formatParensCompact[leftF]
   
   rp = getOperatorPrecedence[right]
   if rp != undef and rp < getOperatorPrecedence[eq]
      rightF = formatParensCompact[rightF]
   
   return formatTable[[[leftF,
                       getOperatorSymbol[eq],
                       rightF]], "", "", "", ""]
}


Download or view formatEquation.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen was born 19967 days, 10 hours, 52 minutes ago.