\documentclass{article}
\usepackage{open-axiom}
\begin{document}
\title{\$SPAD/src/algebra mathml.spad}
\author{Arthur C. Ralfs}
\maketitle
\begin{abstract}
MathMLFormat is a package to produce presentation mathematical
markup language from OutputForm.
\end{abstract}
\eject
\tableofcontents
\eject
\section{Preface}

Both this code and documentation are still under development and
I don't pretend they are anywhere close to perfect or even finished.
However the code does work and I hope it might be useful to somebody
both for it's ability to output MathML from Axiom and as an example
of how to write a new output form.

\section{Introduction to Mathematical Markup Language}

MathML exists in two forms: presentation and content.
At this time (2007-02-11) the package only has a presentation
package.  A content package is in the
works however it is more difficult.  Unfortunately Axiom does
not make its semantics easily available.  The \spadtype{OutputForm}
domain mediates between the individual Axiom domains and the
user visible output but \spadtype{OutputForm} does not provide full
semantic information.  From my currently incomplete understanding
of Axiom it appears that remedying this would entail going back
to the individual domains and rewriting a lot of code.
However some semantics are conveyed directly by \spadtype{OutputForm} and other
things can be deduced from \spadtype{OutputForm} or from the original
user command.

\section{Displaying MathML}

The MathML string produced by ")set output mathml on" can be pasted
directly into an appropriate xhtml page and then viewed in Firefox
or some other MathML aware browser. The boiler plate code needed for
a test page, testmathml.xml, is:

\begin{verbatim}
<?xml version="1.0" ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"
                      "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd" [
<!ENTITY mathml "http://www.w3.org/1998/Math/MathML">
]>

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:xlink="http://www.w3.org/1999/xlink" >


  <head>
    <title>MathML Test </title>
  </head>

  <body>

  </body>
</html>
\end{verbatim}


Paste the MathML string into the body element and it should display
nicely in Firefox.

\section{Test Cases}

Here's a list of test cases that currently format correctly:

1. (x+y)**2

2. integrate(x**x,x)

3. integral(x**x,x)

4. (5 + sqrt 63 + sqrt 847)**(1/3)

5. set $[$1,2,3$]$

6. multiset $[$x rem 5 for x in primes(2,1000)$]$

7. series(sin(a*x),x=0)

8. matrix $[$ $[$x**i + y**j for i in 1..10$]$ for j in 1..10$]$

9. y := operator 'y
   a. D(y(x,z),$[$x,x,z,x$]$)
   b. D(y x,x,2)

10. x := series 'x
    a. sin(1+x)

11. series(1/log(y),y=1)

12. y:UTS(FLOAT,'z,0) := exp(z)

13. a. c := continuedFraction(314159/100000)
    b. c := continuedFraction(14159/100000)
    c. c := continuedFraction(3,repeating [1], repeating [3,6])

14. F := operator F
    x := operator x
    y := operator y
    a := F(x z,y z,z**2) + x y(z+1)
    D(a,z)

The \spadtype{TexFormat} domain has the capability to format an object with
subscripts, superscripts, presubscripts and presuperscripts however
I don't know of any Axiom command that produces such an object. In
fact at present I see the case of "SUPERSUB" being used for putting
primes in the superscript position to denote ordinary differentiation.
I also only see the "SUB" case being used to denote partial
derivatives.

\section{)set output mathml on}


Making mathml appear as output during a normal Axiom session
by invoking ")set output mathml on" proved to be a bit tedious
and seems to be undocumented.  I document my experience here
in case it proves useful to somebody else trying to get a new
output format from Axiom.

In \spadtype{MathMLFormat} the functions 
\spadfun{coerce(expr : OutputForm) : String} and
\spadfun{display(s : String) : Void} provide the desired mathml output.
Note that this package was constructed by close examination of
Robert Sutor's \spadtype{TexFormat} domain and much remains from that source.
To have mathml displayed as output we need to get Axiom to 
call display(coerce(expr)) at the appropriate place.  Here's what
I did to get that to happen. Note that my starting point here was
an attempt by Andrey Grozin to do the same.  To figure things out
I searched through files for "tex" to see what was done for the
\spadtype{TexFormat} domain, and used grep to find which files had mention of
\spadtype{TexFormat}.

\subsection{File src/interp/setvars.boot.pamphlet}


  Create an output mathml section by analogy to the tex section.
Remember to add the code chunk "outputmathmlCode" at the end.

setvars.boot is a bootstrap file which means that it has to be
precompiled into lisp code and then that code has to be inserted
back into setvars.boot. To do this extract the boot code by running
"notangle" on it.  I did this from the "tmp" directory.
From inside axiom run ")lisp (bbottran::boottocl "tmp/setvars.boot")
which put "setvars.clisp" into "int/interp/setvars.clisp".  Then
replace the lisp in "setvars.boot.pamphlet" with that in the newly
generated "setvars.clisp".

The relevant code chunks appearing in "setvars.boot.pamphlet" are:
\begin{verbatim}
    outputmathmlCode
    setOutputMathml
    describeSetOutputMathml
\end{verbatim}
and the relevant variables are:
\begin{verbatim}
    setOutputMathml
    $mathmlOutputStream
    $mathmlOutputFile
    $mathmlFormat
    describeSetOutputMathml
\end{verbatim}

\subsection{File setvart.boot.pamphlet}


Create an output mathml section in "setvart.boot.pamphlet" again
patterned after the tex section.  I changed the default file 
extension from ".stex" to ".smml".

To the "section{output}" table I added the line
\begin{verbatim}
   mathml                  created output in MathML style       Off:CONSOLE
\end{verbatim}
Added the code chunk "outputmathml" to the code chunk "output"
in "section{output}".

Relevant code chunks:
\begin{verbatim}
         outputmathml
\end{verbatim}
Relevant variables:
\begin{verbatim}
         setOutputMathml
         $mathmlFormat
         $mathmlOutputFile
\end{verbatim}

Note when copying the tex stuff I changed occurrences of "tex"
to "mathml", "Tex" to "Mathml" and "TeX" to "MathML".

\subsection{File src/algebra/Makefile.pamphlet}


The file "src/algebra/tex.spad.pamphlet" contains
the domain \spadtype{TexFormat} (TEX) and the package 
\spadtype{TexFormat1} (TEX1).
However the sole function of \spadtype{TexFormat1} is to \spadfun{coerce}
objects from a domain into \spadtype{OutputForm} and then apply 
\spadtype{TexFormat}
to them.  It is to save programmers the trouble of doing
the coercion themselves from inside spad code.  It does
not appear to be used for the main purpose of delivering
Axiom output in TeX format.  In order to keep the mathml
package as simple as possible, and because I didn't see much
use for this, I didn't copy the \spadtype{TexFormat1} package.  So
no analog of the TEX1 entries in "Makefile.pamphlet" were
needed.  One curiosity I don't understand is why TEX1
appears in layer 4 when it seems to depend on TEX which
appears in layer 14.

Initially I added "\${OUT}/MMLFORM.o" to layer 14 and
"mathml.spad.pamphlet" to completed spad files in layer 14.
When trying to compile the build failed at MMLFORM.  It left
"MMLFORM.erlib" in "int/algebra" instead of "MMLFORM.NRLIB"
which confused me at first because mathml.spad compiled
under a running axiom.  By examining the file "obj/tmp/trace"
I saw that a new dependency had been introduced, compared
to TexFormat, with the function eltName depending on the
domain FSAGG in layer 16.  So the lines had to be moved 
from layer 14 to layer 17.

Added appropriate lines to "SPADFILES" and "DOCFILES".

\subsection{File src/algebra/exposed.lsp.pamphlet}

Add the line "(|MathMLFormat| . MMLFORM)"

\subsection{File src/algebra/Lattice.pamphlet}

I don't see that this file is used anywhere but I made
the appropriate changes anyway by searching for "TEX" and
mimicing everything for MMLFORM.

\subsection{File src/doc/axiom.bib.pamphlet}

Added mathml.spad subsection to "src/doc/axiom.bib.pamphlet".

\subsection{File interp/i-output.boot.pamphlet}


This is where the \spadfun{coerce} and \spadfun{display} functions 
from MathMLFormat
actually get called.  The following was added:

\begin{verbatim}
mathmlFormat expr ==
  mml := '(MathMLFormat)
  mmlrep := '(String)
  formatFn := getFunctionFromDomain("coerce",mml,[$OutputForm])
  displayFn := getFunctionFromDomain("display",mml,[mmlrep])
  SPADCALL(SPADCALL(expr,formatFn),displayFn)
  TERPRI $mathmlOutputStream
  FORCE_-OUTPUT $mathmlOutputStream
  NIL
\end{verbatim}

Note that compared to the texFormat function there are a couple
of differences.  Since \spadtype{MathMLFormat} is currently a package rather
than a domain there is the "mmlrep" variable whereas in texFormat
the argument of the "display" function is an instance of the 
domain.  Also the \spadfun{coerce} function here only has one argument,
namely "\$OutputForm".

Also for the function "output(expr,domain)" add lines for mathml,
e.g. "if \$mathmlFormat then mathmlFormat expr".

After these changes Axiom compiled with mathml enabled under
)set output.

\section{package MMLFORM MathMLFormat}

\subsection{Public Declarations}

The declarations
\begin{verbatim}
  E      ==> OutputForm
  I      ==> Integer
  L      ==> List
  S      ==> String
  US     ==> UniversalSegment(Integer)
\end{verbatim}
provide abbreviations for domains used heavily in the code.
The publicly exposed functions are:

    \spadfun{coerce: E -$>$ S}  This function is the main one for converting
an expression in domain OutputForm into a MathML string.

    \spadfun{coerceS: E -$>$ S}  This function is for use from the command line.
It converts an OutputForm expression into a MathML string and does
some formatting so that the output is not one long line.  If you take
the output from this function, stick it in an emacs buffer in
nxml-mode and then indent according to mode, you'll get something that's
nicer to look at than what comes from coerce. Note that coerceS returns
the same value as coerce but invokes a display function as well so that
the result will be printed twice in different formats.  The need for this
is that the output from coerce is automatically formatted with line breaks
by Axiom's output routine that are not in the right place.

    \spadfun{coerceL: E -$>$ S}  Similar to coerceS except that the displayed result
is the MathML string in one long line.  These functions can be used,
for instance, to get the MathML for the previous result by typing
coerceL(%)\$MMLFORM.

    \spadfun{exprex: E -$>$ S}  Converts \spadtype{OutputForm} to 
\spadtype{String} with
the structure preserved with braces.  This is useful in developing this
package. Actually this is not quite accurate.  The function
\spadfun{precondition} is first applied to the \spadtype{OutputForm}
expression before \spadfun{exprex}.   Raw \spadtype{OutputForm} and the nature
of the \spadfun{precondition} function is still obscure to me at the time of
this writing (2007-02-14), however I probably need to understand it to make
sure I'm not missing any semantics.  The spad function \spadfun{precondition}
is just a wrapper for the lisp function outputTran\$Lisp, which I guess is
compiled from boot.

    \spadfun{display: S -$>$ Void}  This one prints the string returned by coerce as one
long line, adding "math" tags: <math ...$>$ ... </math$>$.  Thus the output
from this can be stuck directly into an appropriate html/xhtml page and will
be displayed nicely by a MathML aware browser.

    \spadfun{displayF: S -$>$ Void}  This function doesn't exist 
yet but it would be nice
to have a humanly readable formatted output as well.  The basics do exist in
the coerceS function however the formatting still needs some work to be 
really good.

<<public declarations>>=
)abbrev domain MMLFORM MathMLFormat
++ Author: Arthur C. Ralfs
++ Date: January 2007
++ This package is based on the TeXFormat domain by Robert S. Sutor
++ without which I wouldn't have known where to start.
++ Basic Operations: coerce, coerceS, coerceL, exprex, display
++ Description:
++    \spadtype{MathMLFormat} provides a coercion from \spadtype{OutputForm}
++    to MathML format.

MathMLFormat(): public == private where
  E      ==> OutputForm
  I      ==> Integer
  L      ==> List
  S      ==> String
  US     ==> UniversalSegment(Integer)

  public == SetCategory with
    coerce:    E -> S
      ++ coerceS(o) changes o in the standard output format to MathML
      ++ format.
    coerceS:   E -> S
      ++ coerceS(o) changes o in the standard output format to MathML
      ++ format and displays formatted result.
    coerceL:   E -> S
      ++ coerceS(o) changes o in the standard output format to MathML
      ++ format and displays result as one long string.
    exprex:    E -> S
      ++ coverts \spadtype{OutputForm} to \spadtype{String} with the
      ++ structure preserved with braces.  Actually this is not quite
      ++ accurate.  The function \spadfun{precondition} is first 
      ++ applied to the
      ++ \spadtype{OutputForm} expression before \spadfun{exprex}.  
      ++ The raw \spadtype{OutputForm} and
      ++ the nature of the \spadfun{precondition} function is 
      ++ still obscure to me
      ++ at the time of this writing (2007-02-14).
    display:   S -> Void
      ++ prints the string returned by coerce, adding <math ...> tags.

@
\subsection{Private Constant Declarations}
<<private constant declarations>>=
  private == add
    import OutputForm
    import Character
    import Integer
    import List OutputForm
    import List String

    -- local variable declarations and definitions

    expr: E
    prec,opPrec: I
    str:  S
    blank         : S := " \  "

    maxPrec       : I   := 1000000
    minPrec       : I   := 0

    unaryOps      : L S := ["-","^"]$(L S)
    unaryPrecs    : L I := [700,260]$(L I)

    -- the precedence of / in the following is relatively low because
    -- the bar obviates the need for parentheses.
    binaryOps     : L S := ["+->","|","**","/","<",">","=","OVER"]$(L S)
    binaryPrecs   : L I := [0,0,900, 700,400,400,400,   700]$(L I)

    naryOps       : L S := ["-","+","*",blank,",",";"," ","ROW","",
       " \cr ","&","</mtd></mtr><mtr><mtd>"]$(L S)
    naryPrecs     : L I := [700,700,800,  800,110,110,  0,    0, 0,
             0,  0,   0]$(L I)
    naryNGOps     : L S := ["ROW","&"]$(L S)

    plexOps       : L S := ["SIGMA","SIGMA2","PI","PI2","INTSIGN","INDEFINTEGRAL"]$(L S)
    plexPrecs     : L I := [    700, 800,     700, 800 , 700,      700]$(L I)

    specialOps    : L S := ["MATRIX","BRACKET","BRACE","CONCATB","VCONCAT",  _
                            "AGGLST","CONCAT","OVERBAR","ROOT","SUB","TAG", _
                            "SUPERSUB","ZAG","AGGSET","SC","PAREN", _
                            "SEGMENT","QUOTE","theMap" ]

    -- the next two lists provide translations for some strings for
    -- which MML provides special macros.

    specialStrings : L S :=
      ["cos", "cot", "csc", "log", "sec", "sin", "tan",
        "cosh", "coth", "csch", "sech", "sinh", "tanh",
          "acos","asin","atan","erf","...","$","infinity","Gamma"]
    specialStringsInMML : L S :=
      ["<mo>cos</mo>","<mo>cot</mo>","<mo>csc</mo>","<mo>log</mo>","<mo>sec</mo>","<mo>sin</mo>","<mo>tan</mo>",
        "<mo>cosh</mo>","<mo>coth</mo>","<mo>csch</mo>","<mo>sech</mo>","<mo>sinh</mo>","<mo>tanh</mo>",
          "<mo>arccos</mo>","<mo>arcsin</mo>","<mo>arctan</mo>","<mo>erf</mo>","<mo>&#x2026;</mo>","<mo>$</mo>","<mo>&#x221E;</mo>","<mo>&#x0413;</mo>"]

@
\subsection{Private Function Declarations}

These are the local functions:

    addBraces:S -$>$ S

    addBrackets:S -$>$ S

    atomize:E -$>$ L E

    displayElt:S -$>$ Void
      function for recursively displaying mathml nicely formatted

    eltLimit:(S,I,S) -$>$ I
      demarcates end postion of mathml element with name:S starting at
      position i:I in mathml string s:S and returns end of end tag as
      i:I position in mathml string, i.e. find start and end of
      substring:  <name ...$>$...</name$>$

    eltName:(I,S) -$>$ S
      find name of mathml element starting at position i:I in string s:S

    group:S -$>$ S

    formatBinary:(S,L E, I) -$>$ S

    formatFunction:(S,L E, I) -$>$ S

    formatMatrix:L E -$>$ S

    formatNary:(S,L E, I) -$>$ S

    formatNaryNoGroup:(S,L E, I) -$>$ S

    formatNullary:S -$>$ S

    formatPlex:(S,L E, I) -$>$ S

    formatSpecial:(S,L E, I) -$>$ S

    formatUnary:(S,  E, I) -$>$ S

    formatMml:(E,I) -$>$ S

    newWithNum:I -$>$ \$
      this is a relic from tex.spad and is not used here so far.  I'll
      probably remove it.

    parenthesize:S -$>$ S

    precondition:E -$>$ E
      this function is applied to the OutputForm expression before
      doing anything else.

    postcondition:S -$>$ S
      this function is applied after all other OutputForm -$>$ MathML
      transformations.  In the TexFormat domain the ungroup function
      first peels off the outermost set of braces however I have 
      replaced braces with <mrow$>$s here and sometimes the outermost set
      of <mrow$>$s is necessary to get proper display in Firefox.  For instance
      with getting the correct size of brackets on a matrix the whole
      expression needs to be enclosed in a mrow element.  It also checks
      for +- and removes the +.

    stringify:E -$>$ S

    tagEnd:(S,I,S) -$>$ I
      finds closing "$>$" of start or end tag for mathML element for formatting
      MathML string for human readability.  No analog in TexFormat.

    ungroup:S -$>$ S

<<private function declarations>>=
    -- local function signatures

    addBraces:      S -> S
    addBrackets:    S -> S
    atomize:        E -> L E
    displayElt:     S -> Void
      ++ function for recursively displaying mathml nicely formatted
    eltLimit:       (S,I,S) -> I
      ++ demarcates end postion of mathml element with name:S starting at
      ++ position i:I in mathml string s:S and returns end of end tag as
      ++  i:I position in mathml string, i.e. find start and end of
      ++  substring:  <name ...>...</name>
    eltName:        (I,S) -> S
      ++ find name of mathml element starting at position i:I in string s:S
    group:          S -> S
    formatBinary:   (S,L E, I) -> S
    formatFunction: (S,L E, I) -> S
    formatIntSign:  (L E, I) -> S
    formatMatrix:   L E -> S
    formatNary:     (S,L E, I) -> S
    formatNaryNoGroup: (S,L E, I) -> S
    formatNullary:  S -> S
    formatPlex:     (S,L E, I) -> S
    formatSpecial:  (S,L E, I) -> S
    formatSub:      (E, L E, I) -> S
    formatSuperSub: (E, L E, I) -> S
    formatSuperSub1: (E, L E, I) -> S
    formatUnary:    (S,  E, I) -> S
    formatMml:      (E,I) -> S
    formatZag:      L E -> S
    formatZag1:     L E -> S
    newWithNum:     I -> $
    parenthesize:   S -> S
    precondition:   E -> E
    postcondition:  S -> S
    stringify:      E -> S
    tagEnd:         (S,I,S) -> I
      ++  finds closing ">" of start or end tag for mathML element
    ungroup:        S -> S

@
\subsection{Public Function Definitions}

Note that I use the function sayTeX$Lisp much as I would printf in a
C program.  I've noticed in grepping the code that there are other "say"
functions, sayBrightly and sayMessage for instance, but I have no idea
what the difference is between them at this point.  sayTeX$Lisp does the
job so for the time being I'll use that until I learn more.

The functions coerceS and coerceL should probably be changed to display
functions, {\it i.e.}\/ \spadfun{displayS} and \spadfun{display L}, 
returning Void.  I really only need the one coerce function.

<<public function definitions>>=
    -- public function definitions

    coerce(expr : E): S ==
      s : S := postcondition formatMml(precondition expr, minPrec)
      s

    coerceS(expr : E): S ==
      s : S := postcondition formatMml(precondition expr, minPrec)
      sayTeX$Lisp "<math xmlns=_"http://www.w3.org/1998/Math/MathML_" mathsize=_"big_" display=_"block_">"
      displayElt(s)
      sayTeX$Lisp "</math>"
      s

    coerceL(expr : E): S ==
      s : S := postcondition formatMml(precondition expr, minPrec)
      sayTeX$Lisp "<math xmlns=_"http://www.w3.org/1998/Math/MathML_" mathsize=_"big_" display=_"block_">"
      sayTeX$Lisp s
      sayTeX$Lisp "</math>"
      s

    display(mathml : S): Void ==
      sayTeX$Lisp "<math xmlns=_"http://www.w3.org/1998/Math/MathML_" mathsize=_"big_" display=_"block_">"
      sayTeX$Lisp mathml
      sayTeX$Lisp "</math>"



    exprex(expr : E): S ==
      -- This breaks down an expression into atoms and returns it as
      -- a string.  It's for developmental purposes to help understand
      -- the expressions.
      a : E
      expr := precondition expr
--      sayTeX$Lisp "0: "stringify expr
      (ATOM(expr)$Lisp@Boolean) or (stringify expr = "NOTHING") => 
        concat ["{",stringify expr,"}"]      
      le : L E := (expr pretend L E)
      op := first le
      sop : S := exprex op
      args : L E := rest le
      nargs : I := #args
--      sayTeX$Lisp concat ["1: ",stringify first le," : ",string(nargs)$S]
      s : S := concat ["{",sop]
      if nargs > 0  then
        for a in args repeat
--        sayTeX$Lisp concat ["2: ",stringify a]
          s1 : S := exprex a
          s := concat [s,s1]
      s := concat [s,"}"]

@
\subsection{Private Function Definitions}

\subsubsection{Display Functions}

    displayElt(mathml:S):Void

    eltName(pos:I,mathml:S):S

    eltLimit(name:S,pos:I,mathml:S):I

    tagEnd(name:S,pos:I,mathml:S):I

<<display functions>>=

    displayElt(mathML:S): Void ==
      -- Takes a string of syntactically complete mathML
      -- and formats it for display.
--      sayTeX$Lisp "****displayElt1****"
--      sayTeX$Lisp mathML
      enT:I -- marks end of tag, e.g. "<name>"
      enE:I -- marks end of element, e.g. "<name> ... </name>"
      end:I -- marks end of mathML string
      u:US
      end := #mathML
      length:I := 60
--      sayTeX$Lisp "****displayElt1.1****"
      name:S := eltName(1,mathML)
--      sayTeX$Lisp name
--      sayTeX$Lisp concat("****displayElt1.2****",name)
      enE := eltLimit(name,2+#name,mathML)
--      sayTeX$Lisp "****displayElt2****"
      if enE < length then
--        sayTeX$Lisp "****displayElt3****"
        u := segment(1,enE)$US
        sayTeX$Lisp mathML.u
      else
--        sayTeX$Lisp "****displayElt4****"
        enT := tagEnd(name,1,mathML)
        u := segment(1,enT)$US
        sayTeX$Lisp mathML.u
        u := segment(enT+1,enE-#name-3)$US
        displayElt(mathML.u)
        u := segment(enE-#name-2,enE)$US
        sayTeX$Lisp mathML.u
      if end > enE then
--        sayTeX$Lisp "****displayElt5****"
        u := segment(enE+1,end)$US
        displayElt(mathML.u)


    eltName(pos:I,mathML:S): S ==
      -- Assuming pos is the position of "<" for a start tag of a mathML
      -- element finds and returns the element's name.
      i:I := pos+1
      --sayTeX$Lisp "eltName:mathmML string: "mathML
      while member?(mathML.i,lowerCase()$CharacterClass)$CharacterClass repeat
         i := i+1
      u:US := segment(pos+1,i-1)
      name:S := mathML.u

    eltLimit(name:S,pos:I,mathML:S): I ==
      -- Finds the end of a mathML element like "<name ...> ... </name>"
      -- where pos is the position of the space after name in the start tag
      -- although it could point to the closing ">".  Returns the position
      -- of the ">" in the end tag.
      pI:I := pos
      startI:I
      endI:I
      startS:S := concat ["<",name]
      endS:S := concat ["</",name,">"]
      level:I := 1
      --sayTeX$Lisp "eltLimit: element name: "name
      while (level > 0) repeat
        startI := position(startS,mathML,pI)$String

        endI := position(endS,mathML,pI)$String

        if (startI = 0) then
          level := level-1
          --sayTeX$Lisp "****eltLimit 1******"
          pI := tagEnd(name,endI,mathML)
        else
          if (startI < endI) then
            level := level+1
            pI := tagEnd(name,startI,mathML)
          else
            level := level-1
            pI := tagEnd(name,endI,mathML)
      pI


    tagEnd(name:S,pos:I,mathML:S):I ==
      -- Finds the closing ">" for either a start or end tag of a mathML
      -- element, so the return value is the position of ">" in mathML.
      pI:I := pos
      while  (mathML.pI ~= char ">") repeat
        pI := pI+1
      u:US := segment(pos,pI)$US
      --sayTeX$Lisp "tagEnd: "mathML.u
      pI

@
\subsubsection{Formatting Functions}

Still need to format \\zag in formatSpecial!

In formatPlex the case op = "INTSIGN" is now passed off to
formatIntSign which is a change from the TexFormat domain.
This is done here for presentation mark up to replace the
ugly bound variable that Axiom delivers.  For content mark up
this has to be done anyway.  

The formatPlex function also allows for op = "INDEFINTEGRAL".
However I don't know what Axiom command gives rise to this case.
The INTSIGN case already allows for both definite and indefinite
integrals.

In the function formatSpecial various cases are handled including
SUB and SUPERSUB.  These cases are now caught in formatMml and so
the code in formatSpecial doesn't get executed.  The only cases
I know of using these are partial derivatives for SUB and ordinary
derivatives or SUPERSUB however in TexFormat the capability is there
to handle multiscripts, i.e. an object with subscripts, superscripts,
pre-subscripts and pre-superscripts but I am so far unaware of any
Axiom command that produces such a multiscripted object.

Another question is how to represent derivatives.  At present I have
differential notation for partials and prime notation for ordinary 
derivatives, 
but it would be nice to allow for different derivative notations in 
different circumstances, maybe some options to )set output mathml on.

Ordinary derivatives are formatted in formatSuperSub and there are
2 versions, formatSuperSub and formatSuperSub1, which at this point
have to be switched by swapping names.

<<formatting functions>>=

    atomize(expr : E): L E ==
      -- This breaks down an expression into a flat list of atomic expressions.
      -- expr should be preconditioned.
      le : L E := nil()
      a : E
      letmp : L E
      (ATOM(expr)$Lisp@Boolean) or (stringify expr = "NOTHING") => 
        le := append(le,list(expr))
      letmp := expr pretend L E
      for a in letmp repeat
        le := append(le,atomize a)
      le


    ungroup(str: S): S ==
      len : I := #str
      len < 14 => str
      lrow : S :=  "<mrow>"
      rrow : S :=  "</mrow>"
      -- drop leading and trailing mrows
      u1 : US := segment(1,6)$US
      u2 : US := segment(len-6,len)$US
      if (str.u1 =$S lrow) and (str.u2 =$S rrow) then
        u : US := segment(7,len-7)$US
        str := str.u
      str

    postcondition(str: S): S ==
--      str := ungroup str
      len : I := #str
      plusminus : S := "<mo>+</mo><mo>-</mo>"
      pos : I := position(plusminus,str,1)
      if pos > 0 then
        ustart:US := segment(1,pos-1)$US
        uend:US := segment(pos+20,len)$US
        str := concat [str.ustart,"<mo>-</mo>",str.uend]
        if pos < len-18 then
          str := postcondition(str)
      str



    stringify expr == (mathObject2String$Lisp expr)@S



    group str ==
      concat ["<mrow>",str,"</mrow>"]

    addBraces str ==
      concat ["<mo>{</mo>",str,"<mo>}</mo>"]

    addBrackets str ==
      concat ["<mo>[</mo>",str,"<mo>]</mo>"]

    parenthesize str ==
      concat ["<mo>(</mo>",str,"<mo>)</mo>"]

    precondition expr ==
      outputTran$Lisp expr

    formatSpecial(op : S, args : L E, prec : I) : S ==
      arg : E
      prescript : Boolean := false
      op = "theMap" => "<mtext>theMap(...)</mtext>"
      op = "AGGLST" =>
        formatNary(",",args,prec)
      op = "AGGSET" =>
        formatNary(";",args,prec)
      op = "TAG" =>
        group concat [formatMml(first args,prec),
                      "<mo>&#x02192;</mo>",
                        formatMml(second args,prec)]
                         --RightArrow
      op = "VCONCAT" =>
        group concat("<mtable><mtr>",
                     concat(concat([concat("<mtd>",concat(formatMml(u, minPrec),"</mtd>"))
                                    for u in args]::L S),
                            "</mtr></mtable>"))
      op = "CONCATB" =>
        formatNary(" ",args,prec)
      op = "CONCAT" =>
        formatNary("",args,minPrec)
      op = "QUOTE" =>
        group concat("<mo>'</mo>",formatMml(first args, minPrec))
      op = "BRACKET" =>
        group addBrackets ungroup formatMml(first args, minPrec)
      op = "BRACE" =>
        group addBraces ungroup formatMml(first args, minPrec)
      op = "PAREN" =>
        group parenthesize ungroup formatMml(first args, minPrec)
      op = "OVERBAR" =>
        null args => ""
        group concat ["<mover accent='true'><mrow>",formatMml(first args,minPrec),"</mrow><mo stretchy='true'>&#x000AF;</mo></mover>"]
         --OverBar
      op = "ROOT" =>
        null args => ""
        tmp : S := group formatMml(first args, minPrec)
        null rest args => concat ["<msqrt>",tmp,"</msqrt>"]
        group concat
          ["<mroot><mrow>",tmp,"</mrow>",formatMml(first rest args, minPrec),"</mroot>"]
      op = "SEGMENT" =>
        tmp : S := concat [formatMml(first args, minPrec),"<mo>..</mo>"]
        group
          null rest args =>  tmp
          concat [tmp,formatMml(first rest args, minPrec)]
      -- SUB should now be diverted in formatMml although I'll leave
      -- the code here for now.
      op = "SUB" =>
        group concat ["<msub>",formatMml(first args, minPrec),
          formatSpecial("AGGLST",rest args,minPrec),"</msub>"]
      -- SUPERSUB should now be diverted in formatMml although I'll leave
      -- the code here for now.
      op = "SUPERSUB" =>
        base:S := formatMml(first args, minPrec)
        args := rest args
        if #args = 1 then
          "<msub><mrow>"base"</mrow><mrow>"formatMml(first args, minPrec)"</mrow></msub>"
        else if #args = 2 then
        -- it would be nice to substitue &#x2032; for , in the case of
        -- an ordinary derivative, it looks a lot better.
          "<msubsup><mrow>"base"</mrow><mrow>"formatMml(first args,minPrec)"</mrow><mrow>"formatMml(first rest args, minPrec)"</mrow></msubsup>"
        else if #args = 3 then
          "<mmultiscripts><mrow>"base"</mrow><mrow>"formatMml(first args,minPrec)"</mrow><mrow>"formatMml(first rest args,minPrec)"</mrow><mprescripts/><mrow>"formatMml(first rest rest args,minPrec)"</mrow><none/></mmultiscripts>"
        else if #args = 4 then
          "<mmultiscripts><mrow>"base"</mrow><mrow>"formatMml(first args,minPrec)"</mrow><mrow>"formatMml(first rest args,minPrec)"</mrow><mprescripts/><mrow>"formatMml(first rest rest args,minPrec)"</mrow><mrow>"formatMml(first rest rest rest args,minPrec)"</mrow></mmultiscripts>"
        else
          "<mtext>Problem with multiscript object</mtext>"
      op = "SC" =>
        -- need to handle indentation someday
        null args => ""
        tmp := formatNaryNoGroup("</mtd></mtr><mtr><mtd>", args, minPrec)
        group concat ["<mtable><mtr><mtd>",tmp,"</mtd></mtr></mtable>"]
      op = "MATRIX" => formatMatrix rest args
      op = "ZAG" =>
-- {{+}{3}{{ZAG}{1}{7}}{{ZAG}{1}{15}}{{ZAG}{1}{1}}{{ZAG}{1}{25}}{{ZAG}{1}{1}}{{ZAG}{1}{7}}{{ZAG}{1}{4}}}
-- to format continued fraction traditionally need to intercept it at the
-- formatNary of the "+"
        concat [" \zag{",formatMml(first args, minPrec),"}{",
          formatMml(first rest args,minPrec),"}"]
      concat ["<mtext>not done yet for: ",op,"</mtext>"]

    formatSub(expr : E, args : L E, opPrec : I) : S ==
      -- This one produces differential notation partial derivatives.
      -- It doesn't work in all cases and may not be workable, use
      -- formatSub1 below for now.
      -- At this time this is only to handle partial derivatives.
      -- If the SUB case handles anything else I'm not aware of it.
      -- This an example of the 4th partial of y(x,z) w.r.t. x,x,z,x
      -- {{{SUB}{y}{{CONCAT}{{CONCAT}{{CONCAT}{{CONCAT}{,}{1}}{{CONCAT}{,}{1}}}{{CONCAT}{,}{2}}}{{CONCAT}{,}{1}}}}{x}{z}}
      atomE : L E := atomize(expr)      
      op : S := stringify first atomE
      op ~= "SUB" => "<mtext>Mistake in formatSub: no SUB</mtext>"
      stringify first rest rest atomE ~= "CONCAT" => "<mtext>Mistake in formatSub: no CONCAT</mtext>"
      -- expecting form for atomE like
      --[{SUB}{func}{CONCAT}...{CONCAT}{,}{n}{CONCAT}{,}{n}...{CONCAT}{,}{n}],
      --counting the first CONCATs before the comma gives the number of
      --derivatives
      ndiffs : I := 0
      tmpLE : L E := rest rest atomE
      while stringify first tmpLE = "CONCAT" repeat
        ndiffs := ndiffs+1
        tmpLE := rest tmpLE
      numLS : L S := nil
      i : I := 1
      while i < ndiffs repeat
        numLS := append(numLS,list(stringify first rest tmpLE))
        tmpLE := rest rest rest tmpLE
        i := i+1
      numLS := append(numLS,list(stringify first rest tmpLE))
      -- numLS contains the numbers of the bound variables as strings
      -- for the differentiations, thus for the differentiation [x,x,z,x]
      -- for y(x,z) numLS = ["1","1","2","1"]
      posLS : L S := nil
      i := 0
 --     sayTeX$Lisp "formatSub: nargs = "string(#args)
      while i < #args repeat
        posLS := append(posLS,list(string(i+1)))
        i := i+1
      -- posLS contains the positions of the bound variables in args
      -- as a list of strings, e.g. for the above example ["1","2"]
      tmpS: S := stringify atomE.2
      if ndiffs = 1 then
        s : S := "<mfrac><mo>&#x02202;</mo><mi>"tmpS"</mi><mrow>"
      else        
        s : S := "<mfrac><mrow><msup><mo>&#x02202;</mo><mn>"string(ndiffs)"</mn></msup><mi>"tmpS"</mi></mrow><mrow>"
      -- need to find the order of the differentiation w.r.t. the i-th
      -- variable
      i := 1
      j : I
      k : I
      tmpS: S
      while i < #posLS+1 repeat
        j := 0
        k := 1
        while k < #numLS + 1 repeat
          if numLS.k = string i then j := j + 1
          k := k+1
        if j > 0 then
          tmpS := stringify args.i
          if j = 1 then
            s := s"<mo>&#x02202;</mo><mi>"tmpS"</mi>"
          else
            s := s"<mo>&#x02202;</mo><msup><mi>"tmpS"</mi><mn>"string(j)"</mn></msup>"
        i := i + 1
      s := s"</mrow></mfrac><mo>(</mo>"
      i := 1
      while i < #posLS+1 repeat
        tmpS := stringify args.i
        s := s"<mi>"tmpS"</mi>"
        if i < #posLS then s := s"<mo>,</mo>"
        i := i+1
      s := s"<mo>)</mo>"

    formatSub1(expr : E, args : L E, opPrec : I) : S ==
      -- This one produces partial derivatives notated by ",n" as
      -- subscripts.
      -- At this time this is only to handle partial derivatives.
      -- If the SUB case handles anything else I'm not aware of it.
      -- This an example of the 4th partial of y(x,z) w.r.t. x,x,z,x
      -- {{{SUB}{y}{{CONCAT}{{CONCAT}{{CONCAT}{{CONCAT}{,}{1}}
      -- {{CONCAT}{,}{1}}}{{CONCAT}{,}{2}}}{{CONCAT}{,}{1}}}}{x}{z}},
      -- here expr is everything in the first set of braces and 
      -- args is {{x}{z}}
      atomE : L E := atomize(expr)      
      op : S := stringify first atomE
      op ~= "SUB" => "<mtext>Mistake in formatSub: no SUB</mtext>"
      stringify first rest rest atomE ~= "CONCAT" => "<mtext>Mistake in formatSub: no CONCAT</mtext>"
      -- expecting form for atomE like
      --[{SUB}{func}{CONCAT}...{CONCAT}{,}{n}{CONCAT}{,}{n}...{CONCAT}{,}{n}],
      --counting the first CONCATs before the comma gives the number of
      --derivatives
      ndiffs : I := 0
      tmpLE : L E := rest rest atomE
      while stringify first tmpLE = "CONCAT" repeat
        ndiffs := ndiffs+1
        tmpLE := rest tmpLE
      numLS : L S := nil
      i : I := 1
      while i < ndiffs repeat
        numLS := append(numLS,list(stringify first rest tmpLE))
        tmpLE := rest rest rest tmpLE
        i := i+1
      numLS := append(numLS,list(stringify first rest tmpLE))
      -- numLS contains the numbers of the bound variables as strings
      -- for the differentiations, thus for the differentiation [x,x,z,x]
      -- for y(x,z) numLS = ["1","1","2","1"]
      posLS : L S := nil
      i := 0
 --     sayTeX$Lisp "formatSub: nargs = "string(#args)
      while i < #args repeat
        posLS := append(posLS,list(string(i+1)))
        i := i+1
      -- posLS contains the positions of the bound variables in args
      -- as a list of strings, e.g. for the above example ["1","2"]
      funcS: S := stringify atomE.2
      s : S := "<msub><mi>"funcS"</mi><mrow>"
      i := 1
      while i < #numLS+1 repeat
        s := s"<mo>,</mo><mn>"numLS.i"</mn>"
        i := i + 1
      s := s"</mrow></msub><mo>(</mo>"
      i := 1
      while i < #posLS+1 repeat
--        tmpS := stringify args.i
        tmpS := formatMml(first args,minPrec)
        args := rest args
        s := s"<mi>"tmpS"</mi>"
        if i < #posLS then s := s"<mo>,</mo>"
        i := i+1
      s := s"<mo>)</mo>"

    formatSuperSub(expr : E, args : L E, opPrec : I) : S ==
      -- this produces prime notation ordinary derivatives.
      -- first have to divine the semantics, add cases as needed
      atomE : L E := atomize(expr)      
      op : S := stringify first atomE
      op ~= "SUPERSUB" => "<mtext>Mistake in formatSuperSub: no SUPERSUB1</mtext>"
      #args ~= 1 => "<mtext>Mistake in SuperSub1: #args <> 1</mtext>"
      var : E := first args
      -- should be looking at something like {{SUPERSUB}{var}{ }{,,...,}} for
      -- example here's the second derivative of y w.r.t. x
      -- {{{SUPERSUB}{y}{ }{,,}}{x}}, expr is the first {} and args is the
      -- {x}
      funcS : S := stringify first rest atomE
      bvarS : S := stringify first args
      -- count the number of commas
      commaS : S := stringify first rest rest rest atomE
      commaTest : S := ","
      i : I := 0
      while position(commaTest,commaS,1) > 0 repeat
        i := i+1
        commaTest := commaTest","
      s : S := "<msup><mi>"funcS"</mi><mrow>"
      j : I := 0
      while j < i repeat
        s := s"<mo>&#x02032;</mo>"
        j := j + 1
      s := s"</mrow></msup><mo>&#x02061;</mo><mo>(</mo>"formatMml(first args,minPrec)"<mo>)</mo>"

    formatSuperSub1(expr : E, args : L E, opPrec : I) : S ==
      -- This one produces ordinary derivatives with differential notation,
      -- it needs a little more work yet.
      -- first have to divine the semantics, add cases as needed
      atomE : L E := atomize(expr)      
      op : S := stringify first atomE
      op ~= "SUPERSUB" => "<mtext>Mistake in formatSuperSub: no SUPERSUB</mtext>"
      #args ~= 1 => "<mtext>Mistake in SuperSub: #args <> 1</mtext>"
      var : E := first args
      -- should be looking at something like {{SUPERSUB}{var}{ }{,,...,}} for
      -- example here's the second derivative of y w.r.t. x
      -- {{{SUPERSUB}{y}{ }{,,}}{x}}, expr is the first {} and args is the
      -- {x}
      funcS : S := stringify first rest atomE
      bvarS : S := stringify first args
      -- count the number of commas
      commaS : S := stringify first rest rest rest atomE
      commaTest : S := ","
      ndiffs : I := 0
      while position(commaTest,commaS,1) > 0 repeat
        ndiffs := ndiffs+1
        commaTest := commaTest","
      s : S := "<mfrac><mrow><msup><mo>&#x02146;</mo><mn>"string(ndiffs)"</mn></msup><mi>"funcS"</mi></mrow><mrow><mo>&#x02146;</mo><msup><mi>"formatMml(first args,minPrec)"</mi><mn>"string(ndiffs)"</mn></msup></mrow></mfrac><mo>&#x02061;</mo><mo>(</mo><mi>"formatMml(first args,minPrec)"</mi><mo>)</mo>"

    formatPlex(op : S, args : L E, prec : I) : S ==
      checkarg:Boolean := false
      hold : S
      p : I := position(op,plexOps)
      p < 1 => error "unknown plex op"
      op = "INTSIGN" => formatIntSign(args,minPrec)
      opPrec := plexPrecs.p
      n : I := #args
      (n ~= 2) and (n ~= 3) => error "wrong number of arguments for plex"
      s : S :=
        op = "SIGMA"   => 
          checkarg := true
          "<mo>&#x02211;</mo>"
        -- Sum
        op = "SIGMA2"   => 
          checkarg := true
          "<mo>&#x02211;</mo>"
        -- Sum
        op = "PI"      => 
          checkarg := true
          "<mo>&#x0220F;</mo>"
        -- Product
        op = "PI2"     => 
          checkarg := true
          "<mo>&#x0220F;</mo>"
        -- Product
--        op = "INTSIGN" => "<mo>&#x0222B;</mo>"
        -- Integral, int
        op = "INDEFINTEGRAL" => "<mo>&#x0222B;</mo>"
        -- Integral, int
        "????"
      hold := formatMml(first args,minPrec)
      args := rest args
      if op ~= "INDEFINTEGRAL" then
        if hold ~= "" then
          s := concat ["<munderover>",s,group hold]
        else
          s := concat ["<munderover>",s,group " "]
        if not null rest args then
          hold := formatMml(first args,minPrec)
          if hold ~= "" then
            s := concat [s,group hold,"</munderover>"]
          else
            s := concat [s,group " ","</munderover>"]
          args := rest args
        -- if checkarg true need to test op arg for "+" at least
        -- and wrap parentheses if so
        if checkarg then
          la : L E := (first args pretend L E)
          opa : S := stringify first la
          if opa = "+" then
            s := concat [s,"<mo>(</mo>",formatMml(first args,minPrec),"<mo>)</mo>"]
          else s := concat [s,formatMml(first args,minPrec)]
        else s := concat [s,formatMml(first args,minPrec)]
      else
        hold := group concat [hold,formatMml(first args,minPrec)]
        s := concat [s,hold]
--      if opPrec < prec then s := parenthesize s
-- getting ugly parentheses on fractions
      group s

    formatIntSign(args : L E, opPrec : I) : S ==
      -- the original OutputForm expression looks something like this:
      -- {{INTSIGN}{NOTHING or lower limit?}
      -- {bvar or upper limit?}{{*}{integrand}{{CONCAT}{d}{axiom var}}}}
      -- the args list passed here consists of the rest of this list, i.e.
      -- starting at the NOTHING or ...
      (stringify first args) = "NOTHING" =>
        -- the bound variable is the second one in the argument list
        bvar : E := first rest args
        bvarS : S := stringify bvar
        tmpS : S
        i : I := 0
        u1 : US
        u2 : US
        -- this next one atomizes the integrand plus differential
        atomE : L E := atomize(first rest rest args)
        -- pick out the bound variable used by axiom
        varRS : S := stringify last(atomE)
        tmpLE : L E := ((first rest rest args) pretend L E)
        integrand : S := formatMml(first rest tmpLE,minPrec)
        -- replace the bound variable, i.e. axiom uses someting of the form
        -- %A for the bound variable and puts the original variable used
        -- in the input command as a superscript on the integral sign.
        -- I'm assuming that the axiom variable is 2 characters.
        while (i := position(varRS,integrand,i+1)) > 0 repeat
          u1 := segment(1,i-1)$US
          u2 := segment(i+2,#integrand)$US
          integrand := concat [integrand.u1,bvarS,integrand.u2]
        concat ["<mrow><mo>&#x0222B;</mo>" integrand "<mo>&#x02146;</mo><mi>" bvarS "</mi></mrow>"]

      lowlim : S := stringify first args
      highlim : S := stringify first rest args
      bvar : E := last atomize(first rest rest args)
      bvarS : S := stringify bvar
      tmpLE : L E := ((first rest rest args) pretend L E)
      integrand : S := formatMml(first rest tmpLE,minPrec)
      concat ["<mrow><munderover><mo>&#x0222B;</mo><mi>" lowlim "</mi><mi>" highlim "</mi></munderover>" integrand "<mo>&#x02146;</mo><mi>" bvarS "</mi></mrow>"] 


    formatMatrix(args : L E) : S ==
      -- format for args is [[ROW ...],[ROW ...],[ROW ...]]
      -- generate string for formatting columns (centered)
      group addBrackets concat
        ["<mtable><mtr><mtd>",formatNaryNoGroup("</mtd></mtr><mtr><mtd>",args,minPrec),
          "</mtd></mtr></mtable>"]

    formatFunction(op : S, args : L E, prec : I) : S ==
      group concat ["<mo>",op,"</mo>",parenthesize formatNary(",",args,minPrec)]

    formatNullary(op : S) ==
      op = "NOTHING" => ""
      group concat ["<mo>",op,"</mo><mo>(</mo><mo>)</mo>"]

    formatUnary(op : S, arg : E, prec : I) ==
      p : I := position(op,unaryOps)
      p < 1 => error "unknown unary op"
      opPrec := unaryPrecs.p
      s : S := concat ["<mo>",op,"</mo>",formatMml(arg,opPrec)]
      opPrec < prec => group parenthesize s
      op = "-" => s
      group s

    formatBinary(op : S, args : L E, prec : I) : S ==
      p : I := position(op,binaryOps)
      p < 1 => error "unknown binary op"
      opPrec := binaryPrecs.p
      -- if base op is product or sum need to add parentheses
      if ATOM(first args)$Lisp@Boolean then
        opa:S := stringify first args
      else
        la : L E := (first args pretend L E)
        opa : S := stringify first la
      if (opa = "SIGMA" or opa = "SIGMA2" or opa = "PI" or opa = "PI2") and op = "**" then
        s1 : S := concat ["<mo>(</mo>",formatMml(first args, opPrec),"<mo>)</mo>"]
      else 
        s1 : S := formatMml(first args, opPrec)
      s2 : S := formatMml(first rest args, opPrec)
      op :=
        op = "|"     =>  s := concat ["<mrow>",s1,"</mrow><mo>",op,"</mo><mrow>",s2,"</mrow>"]
        op = "**"    =>  s := concat ["<msup><mrow>",s1,"</mrow><mrow>",s2,"</mrow></msup>"]
        op = "/"     =>  s := concat ["<mfrac><mrow>",s1,"</mrow><mrow>",s2,"</mrow></mfrac>"]
        op = "OVER"  =>  s := concat ["<mfrac><mrow>",s1,"</mrow><mrow>",s2,"</mrow></mfrac>"]
        op = "+->"   =>  s := concat ["<mrow>",s1,"</mrow><mo>",op,"</mo><mrow>",s2,"</mrow>"]
        s := concat ["<mrow>",s1,"</mrow><mo>",op,"</mo><mrow>",s2,"</mrow>"]
      group
        op = "OVER" => s
--        opPrec < prec => parenthesize s
-- ugly parentheses?
        s

    formatNary(op : S, args : L E, prec : I) : S ==
      group formatNaryNoGroup(op, args, prec)

    formatNaryNoGroup(op : S, args : L E, prec : I) : S ==
      checkargs:Boolean := false
      null args => ""
      p : I := position(op,naryOps)
      p < 1 => error "unknown nary op"
      -- need to test for "ZAG" case and divert it here
      -- ex 1. continuedFraction(314159/100000)
      -- {{+}{3}{{ZAG}{1}{7}}{{ZAG}{1}{15}}{{ZAG}{1}{1}}{{ZAG}{1}{25}}
      -- {{ZAG}{1}{1}}{{ZAG}{1}{7}}{{ZAG}{1}{4}}}
      -- this is the preconditioned output form
      -- including "op", the args list would be the rest of this
      -- i.e op = '+' and args = {{3}{{ZAG}{1}{7}}{{ZAG}{1}{15}}
      -- {{ZAG}{1}{1}}{{ZAG}{1}{25}}{{ZAG}{1}{1}}{{ZAG}{1}{7}}{{ZAG}{1}{4}}}
      -- ex 2. continuedFraction(14159/100000)
      -- this one doesn't have the leading integer
      -- {{+}{{ZAG}{1}{7}}{{ZAG}{1}{15}}{{ZAG}{1}{1}}{{ZAG}{1}{25}}
      -- {{ZAG}{1}{1}}{{ZAG}{1}{7}}{{ZAG}{1}{4}}}
      --
      -- ex 3. continuedFraction(3,repeating [1], repeating [3,6])
      -- {{+}{3}{{ZAG}{1}{3}}{{ZAG}{1}{6}}{{ZAG}{1}{3}}{{ZAG}{1}{6}}
      -- {{ZAG}{1}{3}}{{ZAG}{1}{6}}{{ZAG}{1}{3}}{{ZAG}{1}{6}}
      -- {{ZAG}{1}{3}}{{ZAG}{1}{6}}{...}}
      -- In each of these examples the args list consists of the terms
      -- following the '+' op
      -- so the first arg could be a "ZAG" or something
      -- else, but the second arg looks like it has to be "ZAG", so maybe
      -- test for #args > 1 and args.2 contains "ZAG".
      -- Note that since the resulting MathML <mfrac>s are nested we need
      -- to handle the whole continued fraction at once, i.e. we can't
      -- just look for, e.g., {{ZAG}{1}{6}}
      (#args > 1) and (position("ZAG",stringify first rest args,1) > 0) =>
           tmpS : S := stringify first args
           position("ZAG",tmpS,1) > 0 => formatZag(args)
--         position("ZAG",tmpS,1) > 0 => formatZag1(args)
           concat [formatMml(first args,minPrec) "<mo>+</mo>" formatZag(rest args)]
      -- At least for the ops "*","+","-" we need to test to see if a sigma or pi
      -- is one of their arguments because we might need parentheses as indicated
      -- by the problem with summation(operator(f)(i),i=1..n)+1 versus 
      -- summation(operator(f)(i)+1,i=1..n) having identical displays as of
      -- 2007-12-21
      op :=
        op = ","     => "<mo>,</mo>" --originally , \:
        op = ";"     => "<mo>;</mo>" --originally ; \: should figure these out
        op = "*"     =>
            checkargs := true
            "<mo>&#x02062;</mo>"
        -- InvisibleTimes
        op = " "     => "<mspace width='0.5em'/>"
        op = "ROW"   => "</mtd><mtd>"
        op = "+"     => 
            checkargs := true
            "<mo>+</mo>"
        op = "-"     => 
            checkargs := true
            "<mo>-</mo>"
        op
      l : L S := nil
      opPrec := naryPrecs.p
      -- if checkargs is true check each arg except last one to see if it's
      -- a sigma or pi and if so add parentheses. Other op's may have to be
      -- checked for in future
      count:I := 1
      for a in args repeat
        if checkargs then
          if count < #args then
            -- check here for sum or product
            if ATOM(a)$Lisp@Boolean then
              opa:S := stringify a
            else
              la : L E := (a pretend L E)
              opa : S := stringify first la
            if opa = "SIGMA" or opa = "SIGMA2" or opa = "PI" or opa = "PI2" then
              l := concat(op,concat(concat ["<mo>(</mo>",formatMml(a,opPrec),"<mo>)</mo>"],l)$L(S))$L(S)
            else l := concat(op,concat(formatMml(a,opPrec),l)$L(S))$L(S)
          else l := concat(op,concat(formatMml(a,opPrec),l)$L(S))$L(S)
        else l := concat(op,concat(formatMml(a,opPrec),l)$L(S))$L(S)
        count := count + 1
      s : S := concat reverse rest l
      opPrec < prec => parenthesize s
      s

    formatZag(args : L E) : S ==
      -- args will be a list of things like this {{ZAG}{1}{7}}, the ZAG
      -- must be there, the '1' and '7' could conceivably be more complex
      -- expressions
      tmpZag : L E := first args pretend L E
      -- may want to test that tmpZag contains 'ZAG'
      #args > 1 => "<mfrac>"formatMml(first rest tmpZag,minPrec)"<mrow><mn>"formatMml(first rest rest tmpZag,minPrec)"</mn><mo>+</mo>"formatZag(rest args)"</mrow></mfrac>"
      (first args = "...":: E)@Boolean => "<mo>&#x2026;</mo>"
      op:S := stringify first args
      position("ZAG",op,1) > 0 =>
        "<mfrac>"formatMml(first rest tmpZag,minPrec)formatMml(first rest rest tmpZag,minPrec)"</mfrac>"
      error "formatZag: Last argument in ZAG construct has unknown operator: "op

    formatZag1(args : L E) : S ==
    -- make alternative ZAG format without diminishing fonts, maybe
    -- use a table
    -- {{ZAG}{1}{7}}
      tmpZag : L E := first args pretend L E
      #args > 1 => "<mfrac>"formatMml(first rest tmpZag,minPrec)"<mrow><mn>"formatMml(first rest rest tmpZag,minPrec)"</mn><mo>+</mo>"formatZag(rest args)"</mrow></mfrac>"
      (first args = "...":: E)@Boolean => "<mo>&#x2026;</mo>"
      error "formatZag1: Unexpected kind of ZAG"

    formatMml(expr : E,prec : I) ==
      i,len : Integer
      intSplitLen : Integer := 20
      ATOM(expr)$Lisp@Boolean =>
        str := stringify expr
        len := #str
        -- this bit seems to deal with integers
        FIXP$Lisp expr =>
          i := expr pretend Integer
          if (i < 0) or (i > 9)
            then
              group
                 nstr : String := ""
                 -- insert some blanks into the string, if too long
                 while ((len := #str) > intSplitLen) repeat
                   nstr := concat [nstr," ",
                     elt(str,segment(1,intSplitLen)$US)]
                   str := elt(str,segment(intSplitLen+1)$US)
                 empty? nstr => concat ["<mn>",str,"</mn>"]
                 nstr :=
                   empty? str => nstr
                   concat [nstr," ",str]
                 concat ["<mn>",elt(nstr,segment(2)$US),"</mn>"]
            else str := concat ["<mn>",str,"</mn>"]
        str = "%pi" => "<mi>&#x003C0;</mi>"
        -- pi
        str = "%e"  => "<mi>&#x02147;</mi>"
        -- ExponentialE
        str = "%i"  => "<mi>&#x02148;</mi>"
        -- ImaginaryI
        len > 0 and str.1 = char "%" => concat(concat("<mi>",str),"</mi>")
        len > 1 and digit? str.1 => concat ["<mn>",str,"</mn>"] -- should handle floats
        -- presumably this is a literal string
        len > 0 and str.1 = char "_"" =>
          concat(concat("<mtext>",str),"</mtext>")
        len = 1 and str.1 = char " " => " "
        (i := position(str,specialStrings)) > 0 =>
          specialStringsInMML.i
        (i := position(char " ",str)) > 0 =>
          -- We want to preserve spacing, so use a roman font.
          -- What's this for?  Leave the \rm in for now so I can see
          -- where it arises.  Removed 2007-02-14
          concat(concat("<mtext>",str),"</mtext>")
        -- if we get to here does that mean it's a variable?
        concat ["<mi>",str,"</mi>"]
      l : L E := (expr pretend L E)
      null l => blank
      op : S := stringify first l
      args : L E := rest l
      nargs : I := #args
      -- need to test here in case first l is SUPERSUB case and then
      -- pass first l and args to formatSuperSub.
      position("SUPERSUB",op,1) > 0 =>
        formatSuperSub(first l,args,minPrec)
      -- now test for SUB
      position("SUB",op,1) > 0 =>
        formatSub1(first l,args,minPrec)

      -- special cases
      member?(op, specialOps) => formatSpecial(op,args,prec)
      member?(op, plexOps)    => formatPlex(op,args,prec)

      -- nullary case
      0 = nargs => formatNullary op

      -- unary case
      (1 = nargs) and member?(op, unaryOps) =>
        formatUnary(op, first args, prec)

      -- binary case
      (2 = nargs) and member?(op, binaryOps) =>
        formatBinary(op, args, prec)

      -- nary case
      member?(op,naryNGOps) => formatNaryNoGroup(op,args, prec)
      member?(op,naryOps) => formatNary(op,args, prec)

      op := formatMml(first l,minPrec)
      formatFunction(op,args,prec)

@
\section{Mathematical Markup Language Form}
<<package MMLFORM MathMLForm>>=
<<public declarations>>
<<private constant declarations>>
<<private function declarations>>
<<public function definitions>>
<<display functions>>
<<formatting functions>>

@
\section{License}
<<license>>=
--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@
<<*>>=
<<license>>
<<package MMLFORM MathMLForm>>
@
\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}