Rename specvar integer-info to *integer-info*
[maxima.git] / share / contrib / lurkmathml / mathml.lisp
blob263078a0ef8f73482b39d1638f3d908fffe19409
1 (in-package :maxima)
2 ;; MathML-printing
3 ;; Created by David Drysdale (DMD), December 2002/January 2003
4 ;;
5 ;; closely based on the original TeX conversion code in mactex.lisp,
6 ;; for which the following credits apply:
7 ;; (c) copyright 1987, Richard J. Fateman
8 ;; small corrections and additions: Andrey Grozin, 2001
9 ;; additional additions: Judah Milgram (JM), September 2001
10 ;; additional corrections: Barton Willis (BLW), October 2001
12 ;; Usage: mathml(d8,"/tmp/foo.xml"); mathml(d10,"/tmp/foo.xml"); ..
13 ;; to append lines d8 and d10 to the mathml file. If given only
14 ;; one argument the result goes to standard output.
16 ;; Method:
18 ;; Producing MathML from a macsyma internal expression is done by
19 ;; a reversal of the parsing process. Fundamentally, a
20 ;; traversal of the expression tree is produced by the program,
21 ;; with appropriate substitutions and recognition of the
22 ;; infix / prefix / postfix / matchfix relations on symbols. Various
23 ;; changes are made to this so that MathML will like the results.
25 ;; Instructions:
26 ;; in macsyma, type mathml(<expression>); or mathml(<label>); or
27 ;; mathml(<expr-or-label>, <file-name>); In the case of a label,
28 ;; an equation-number will also be produced.
29 ;; in case a file-name is supplied, the output will be sent
30 ;; (perhaps appended) to that file.
32 (macsyma-module mathml)
34 (declare-top (special lop rop ccol $gcprint texport $labels $inchar vaxima-main-dir))
36 ;; top level command the result of converting the expression x.
38 (defmspec $mathml(l) ;; mexplabel, and optional filename
39 ;;if filename supplied but 'nil' then return a string
40 (let ((args (cdr l)))
41 (cond ((and (cdr args) (null (cadr args)))
42 (let ((*standard-output* (make-string-output-stream)))
43 (apply 'mathml1 args)
44 (get-output-stream-string *standard-output*)
47 (t (apply 'mathml1 args)))))
49 (defun mathml1 (mexplabel &optional filename ) ;; mexplabel, and optional filename
50 (prog (mexp texport $gcprint ccol x y itsalabel tmpport)
51 ;; $gcprint = nil turns gc messages off
52 (setq ccol 1)
53 (cond ((null mexplabel)
54 (displa " No eqn given to MathML")
55 (return nil)))
56 ;; collect the file-name, if any, and open a port if needed
57 (setq texport (cond((null filename) *standard-output* ); t= output to terminal
59 (open (string (stripdollar filename))
60 :direction :output
61 :if-exists :append
62 :if-does-not-exist :create))))
63 ;; go back and analyze the first arg more thoroughly now.
64 ;; do a normal evaluation of the expression in macsyma
65 (setq mexp (meval mexplabel))
66 (cond ((member mexplabel $labels :test #'eq); leave it if it is a label
67 (setq mexplabel (intern (format nil "(~a)" (stripdollar mexplabel))))
68 (setq itsalabel t))
69 (t (setq mexplabel nil)));flush it otherwise
71 ;; maybe it is a function?
72 (cond((symbolp (setq x mexp)) ;;exclude strings, numbers
73 (setq x ($verbify x))
74 (cond ((setq y (mget x 'mexpr))
75 (setq mexp (list '(mdefine) (cons (list x) (cdadr y)) (caddr y))))
76 ((setq y (mget x 'mmacro))
77 (setq mexp (list '(mdefmacro) (cons (list x) (cdadr y)) (caddr y))))
78 ((setq y (mget x 'aexpr))
79 (setq mexp (list '(mdefine) (cons (list x 'array) (cdadr y)) (caddr y)))))))
80 (cond ((and (null (atom mexp))
81 (member (caar mexp) '(mdefine mdefmacro) :test #'eq))
82 (format texport "<pre>~%" )
83 (cond (mexplabel (format texport "~a " mexplabel)))
84 ;; need to get rid of "<" signs
85 (setq tmpport (make-string-output-stream))
86 (mgrind mexp tmpport)
87 (close tmpport)
88 (format texport "~a"
89 (string-substitute "&lt;" #\< (get-output-stream-string tmpport)))
90 (format texport ";~%</pre>"))
92 ((and itsalabel ;; but is it a user-command-label?
93 (char= (char (string $inchar) 1) (char (string mexplabel) 1)))
94 ;; aha, this is a C-line: do the grinding:
95 (format texport "<pre>~%~a " mexplabel)
96 ;; need to get rid of "<" signs
97 (setq tmpport (make-string-output-stream))
98 (mgrind mexp tmpport)
99 (close tmpport)
100 (format texport "~a"
101 (string-substitute "&lt;" #\< (get-output-stream-string tmpport)))
102 (format texport ";~%</pre>"))
104 (t ; display the expression for MathML now:
105 (myprinc "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"> " texport)
106 (mapc #'(lambda (x) (myprinc x texport))
107 ;;initially the left and right contexts are
108 ;; empty lists, and there are implicit parens
109 ;; around the whole expression
110 (mathml mexp nil nil 'mparen 'mparen))
111 (cond (mexplabel
112 (format texport "<mspace width=\"verythickmathspace\"/> <mtext>~a</mtext> " mexplabel)))
113 (format texport "</math>")))
114 (cond(filename(terpri texport); and drain port if not terminal
115 (close texport)))
116 (return mexplabel)))
118 (defun mathml (x l r lop rop)
119 ;; x is the expression of interest; l is the list of strings to its
120 ;; left, r to its right. lop and rop are the operators on the left
121 ;; and right of x in the tree, and will determine if parens must
122 ;; be inserted
123 (setq x (nformat x))
124 (cond ((atom x) (mathml-atom x l r))
125 ((or (<= (mathml-lbp (caar x)) (mathml-rbp lop))
126 (> (mathml-lbp rop) (mathml-rbp (caar x))))
127 (mathml-paren x l r))
128 ;; special check needed because macsyma notates arrays peculiarly
129 ((member 'array (cdar x) :test #'eq) (mathml-array x l r))
130 ;; dispatch for object-oriented mathml-ifiying
131 ((get (caar x) 'mathml) (funcall (get (caar x) 'mathml) x l r))
132 (t (mathml-function x l r nil))))
134 (defun string-substitute (newstring oldchar x &aux matchpos)
135 (setq matchpos (position oldchar x))
136 (if (null matchpos) x
137 (concatenate 'string
138 (subseq x 0 matchpos)
139 newstring
140 (string-substitute newstring oldchar (subseq x (1+ matchpos))))))
142 ;;; NOTE that we try to include spaces after closing tags (e.g. "</mwhatever> ")
143 ;;; so that the line breaking algorithm in myprinc has some spaces where it
144 ;;; can choose to line break.
146 ;;; First we have the functions which are called directly by mathml and its
147 ;;; descendents
149 (defun mathml-atom (x l r)
150 (append l
151 (list (cond ((numberp x) (mathmlnumformat x))
152 ((stringp x) (format nil "<mtext>~a</mtext>" x))
153 ((and (symbolp x) (get x 'mathmlword)))
154 (t (mathml-stripdollar x))))
157 (defun mathmlnumformat(atom)
158 (let (r firstpart exponent)
159 (cond ((integerp atom)
160 (strcat "<mn>" (format nil "~d" atom) "</mn> "))
162 (setq r (explode atom))
163 (setq exponent (member 'e r :test #'string-equal));; is it ddd.ddde+EE
164 (cond ((null exponent)
165 (strcat "<mn>" (format nil "~a" (implode (exploden atom))) "</mn> "))
167 (setq firstpart
168 (nreverse (cdr (member 'e (reverse r) :test #'string-equal))))
169 (strcat
170 "<mrow><mn>"
171 (apply #'strcat firstpart)
172 "</mn><mo>&times;</mo> <msup><mn>10</mn><mn>"
173 (apply #'strcat (cdr exponent))
174 "</mn></msup> </mrow> ")
175 ))))))
177 (defun mathml-stripdollar(sym)
178 (or (symbolp sym)
179 (return-from mathml-stripdollar sym))
180 (let* ((pname (maybe-invert-string-case (string-left-trim '(#\$) (symbol-name sym))))
181 (l (length pname))
182 (begin-sub
183 (loop for i downfrom (1- l)
184 when (not (digit-char-p (aref pname i)))
185 do (return (1+ i)))))
186 (cond ((< begin-sub l) ;; need to do subscripting
187 (strcat "<msub><mi>"
188 (subseq pname 0 begin-sub)
189 "</mi> <mn>"
190 (subseq pname begin-sub l)
191 "</mn></msub> "))
192 (t ;; no subscripting needed
193 (strcat "<mi>" pname "</mi> ")))))
195 (defun mathml-paren (x l r)
196 (mathml x (append l '("<mfenced separators=\"\">")) (cons "</mfenced> " r) 'mparen 'mparen))
198 (defun mathml-array (x l r)
199 (let ((f))
200 (if (eq 'mqapply (caar x))
201 (setq f (cadr x)
202 x (cdr x)
203 l (mathml f (append l (list "<mfenced separators=\",\">"))
204 (list "</mfenced> ") 'mparen 'mparen))
205 (setq f (caar x)
206 l (mathml (mathmlword f) (append l '("<msub><mrow>")) nil lop 'mfunction)))
207 (setq
208 r (nconc (mathml-list (cdr x) nil (list "</mrow></msub> ") "<mo>,</mo>") r))
209 (nconc l (list "</mrow><mrow>") r )))
211 ;; set up a list , separated by symbols (, * ...) and then tack on the
212 ;; ending item (e.g. "]" or perhaps ")"
213 (defun mathml-list (x l r sym)
214 (if (null x) r
215 (do ((nl))
216 ((null (cdr x))
217 (setq nl (nconc nl (mathml (car x) l r 'mparen 'mparen)))
219 (setq nl (nconc nl (mathml (car x) l (list sym) 'mparen 'mparen))
220 x (cdr x)
221 l nil))))
223 ;; we could patch this so sin x rather than sin(x), but instead we made sin a prefix
224 ;; operator
225 (defun mathml-function (x l r op) op
226 (setq l (mathml (mathmlword (caar x)) l nil 'mparen 'mparen)
227 r (mathml (cons '(mprogn) (cdr x)) nil r 'mparen 'mparen))
228 (nconc l r))
230 ;;; Now we have functions which are called via property lists
232 (defun mathml-prefix (x l r)
233 (mathml (cadr x) (append l (mathmlsym (caar x))) r (caar x) rop))
235 (defun mathml-infix (x l r)
236 ;; check for 2 args
237 (if (or (null (cddr x)) (cdddr x)) (wna-err (caar x)))
238 (setq l (mathml (cadr x) l nil lop (caar x)))
239 (mathml (caddr x) (append l (mathmlsym (caar x))) r (caar x) rop))
241 (defun mathml-postfix (x l r)
242 (mathml (cadr x) l (append (mathmlsym (caar x)) r) lop (caar x)))
244 (defun mathml-nary (x l r)
245 (let* ((op (caar x)) (sym (mathmlsym op)) (y (cdr x)) (ext-lop lop) (ext-rop rop))
246 (cond ((null y) (mathml-function x l r t)) ; this should not happen
247 ((null (cdr y)) (mathml-function x l r t)) ; this should not happen, too
248 (t (do ((nl) (lop ext-lop op) (rop op (if (null (cdr y)) ext-rop op)))
249 ((null (cdr y)) (setq nl (nconc nl (mathml (car y) l r lop rop))) nl)
250 (setq nl (nconc nl (mathml (car y) l (list sym) lop rop))
251 y (cdr y)
252 l nil))))))
254 (defun mathml-nofix (x l r) (mathml (caar x) l r (caar x) rop))
256 (defun mathml-matchfix (x l r)
257 (setq l (append l (car (mathmlsym (caar x))))
258 ;; car of mathmlsym of a matchfix operator is the lead op
259 r (append (cdr (mathmlsym (caar x))) r)
260 ;; cdr is the trailing op
261 x (mathml-list (cdr x) nil r "<mo>,</mo>"))
262 (append l x))
264 (defun mathmlsym (x) (or (get x 'mathmlsym) (get x 'strsym)(get x 'dissym)
265 (stripdollar x)))
267 (defun mathmlword (x)(or (get x 'mathmlword) (stripdollar x)))
269 (defprop bigfloat mathml-bigfloat mathml)
271 (defun mathml-bigfloat (x l r)
272 (let ((formatted (fpformat x)))
273 (if (or (find '|b| formatted) (find '|B| formatted))
274 (let*
275 ((spell-out-expt
276 (append
277 '("<mrow><msub><mn>")
278 (apply #'append
279 (mapcar
280 #'(lambda (e) (if (or (eq e '|b|) (eq e '|B|))
281 '("</mn><mi>B</mi></msub>"
282 "<mo>&times;</mo>"
283 "<msup><mn>10</mn><mn>")
284 (list e)))
285 formatted))
286 '("</mn></msup></mrow>"))))
287 (append l spell-out-expt r))
288 (append l formatted r))))
290 (defprop mprog "<mi>block</mi><mspace width=\"mediummathspace\"/> " mathmlword)
291 (defprop %erf "<mi>erf</mi> " mathmlword)
292 (defprop $erf "<mi>erf</mi> " mathmlword) ;; etc for multicharacter names
293 (defprop $true "<mi>true</mi> " mathmlword)
294 (defprop $false "<mi>false</mi> " mathmlword)
296 (defprop mprogn mathml-matchfix mathml) ;; mprogn is (<progstmnt>, ...)
297 (defprop mprogn (("<mfenced separators=\"\">") "</mfenced> ") mathmlsym)
299 (defprop mlist mathml-matchfix mathml)
300 (defprop mlist (("<mfenced separators=\"\" open=\"[\" close=\"]\">")"</mfenced> ") mathmlsym)
302 ;;absolute value
303 (defprop mabs mathml-matchfix mathml)
304 (defprop mabs (("<mfenced separators=\"\" open=\"|\" close=\"|\">")"</mfenced> ") mathmlsym)
306 (defprop mqapply mathml-mqapply mathml)
308 (defun mathml-mqapply (x l r)
309 (setq l (mathml (cadr x) l (list "(" ) lop 'mfunction)
310 r (mathml-list (cddr x) nil (cons ")" r) "<mo>,</mo>"))
311 (append l r));; fixed 9/24/87 RJF
313 (defprop $%i "<mi>&ImaginaryI;</mi> " mathmlword)
314 (defprop $%pi "<mi>&pi;</mi> " mathmlword)
315 (defprop $%e "<mi>&ExponentialE;</mi> " mathmlword)
316 (defprop $inf "<mi>&infin;</mi> " mathmlword)
317 (defprop $minf "<mi>-&infin;</mi> " mathmlword)
318 (defprop %laplace "<mo>&Laplacetrf;</mo>" mathmlword)
319 (defprop $alpha "<mi>&alpha;</mi> " mathmlword)
320 (defprop $beta "<mi>&beta;</mi> " mathmlword)
321 (defprop $gamma "<mi>&gamma;</mi> " mathmlword)
322 (defprop %gamma "<mi>&Gamma;</mi> " mathmlword)
323 (defprop $delta "<mi>&delta;</mi> " mathmlword)
324 (defprop $epsilon "<mi>&epsilon;</mi> " mathmlword)
325 (defprop $zeta "<mi>&zeta;</mi> " mathmlword)
326 (defprop $eta "<mi>&eta;</mi> " mathmlword)
327 (defprop $theta "<mi>&theta;</mi> " mathmlword)
328 (defprop $iota "<mi>&iota;</mi> " mathmlword)
329 (defprop $kappa "<mi>&kappa;</mi> " mathmlword)
330 (defprop lambda "<mi>&lambda;</mi> " mathmlword)
331 (defprop $mu "<mi>&mu;</mi> " mathmlword)
332 (defprop $nu "<mi>&nu;</mi> " mathmlword)
333 (defprop $xi "<mi>&xi;</mi> " mathmlword)
334 (defprop $pi "<mi>&pi;</mi> " mathmlword)
335 (defprop $rho "<mi>&rho;</mi> " mathmlword)
336 (defprop $sigma "<mi>&sigma;</mi> " mathmlword)
337 (defprop $tau "<mi>&tau;</mi> " mathmlword)
338 (defprop $upsilon "<mi>&upsilon;</mi> " mathmlword)
339 (defprop $phi "<mi>&phi;</mi> " mathmlword)
340 (defprop $chi "<mi>&chi;</mi> " mathmlword)
341 (defprop $psi "<mi>&psi;</mi> " mathmlword)
342 (defprop $omega "<mi>&omega;</mi> " mathmlword)
344 (defprop mquote mathml-prefix mathml)
345 (defprop mquote ("<mo>'</mo>") mathmlsym)
346 (defprop mquote 201. mathml-rbp)
348 (defprop msetq mathml-infix mathml)
349 (defprop msetq ("<mo>:</mo>") mathmlsym)
350 (defprop msetq 180. mathml-rbp)
351 (defprop msetq 20. mathml-rbp)
353 (defprop mset mathml-infix mathml)
354 (defprop mset ("<mo>::</mo>") mathmlsym)
355 (defprop mset 180. mathml-lbp)
356 (defprop mset 20. mathml-rbp)
358 (defprop mdefine mathml-infix mathml)
359 (defprop mdefine ("<mo>:=</mo>") mathmlsym)
360 (defprop mdefine 180. mathml-lbp)
361 (defprop mdefine 20. mathml-rbp)
363 (defprop mdefmacro mathml-infix mathml)
364 (defprop mdefmacro ("<mo>::=</mo>") mathmlsym)
365 (defprop mdefmacro 180. mathml-lbp)
366 (defprop mdefmacro 20. mathml-rbp)
368 (defprop marrow mathml-infix mathml)
369 (defprop marrow ("<mo>&rightarrow;</mo>") mathmlsym)
370 (defprop marrow 25 mathml-lbp)
371 (defprop marrow 25 mathml-rbp)
373 (defprop mfactorial mathml-postfix mathml)
374 (defprop mfactorial ("<mo>!</mo>") mathmlsym)
375 (defprop mfactorial 160. mathml-lbp)
377 (defprop mexpt mathml-mexpt mathml)
378 (defprop mexpt 140. mathml-lbp)
379 (defprop mexpt 139. mathml-rbp)
381 (defprop %sum 110. mathml-rbp) ;; added by BLW, 1 Oct 2001
382 (defprop %product 115. mathml-rbp) ;; added by BLW, 1 Oct 2001
384 ;; insert left-angle-brackets for mncexpt. a^<n> is how a^^n looks.
385 (defun mathml-mexpt (x l r)
386 (let((nc (eq (caar x) 'mncexpt))); true if a^^b rather than a^b
387 ;; here is where we have to check for f(x)^b to be displayed
388 ;; as f^b(x), as is the case for sin(x)^2 .
389 ;; which should be sin^2 x rather than (sin x)^2 or (sin(x))^2.
390 ;; yet we must not display (a+b)^2 as +^2(a,b)...
391 ;; or (sin(x))^(-1) as sin^(-1)x, which would be arcsine x
392 (cond ;; this whole clause
393 ;; should be deleted if this hack is unwanted and/or the
394 ;; time it takes is of concern.
395 ;; it shouldn't be too expensive.
396 ((and (eq (caar x) 'mexpt) ; don't do this hack for mncexpt
397 (let*
398 ((fx (cadr x)); this is f(x)
399 (f (and (not (atom fx)) (atom (caar fx)) (caar fx))) ; this is f [or nil]
400 (bascdr (and f (cdr fx))) ; this is (x) [maybe (x,y..), or nil]
401 (expon (caddr x)) ;; this is the exponent
402 (doit (and
403 f ; there is such a function
404 (member (get-first-char f) '(#\% #\$) :test #'char=) ;; insist it is a % or $ function
405 (not (member f '(%sum %product %derivative %integrate %at
406 %lsum %limit) :test #'eq)) ;; what else? what a hack...
407 (or (and (atom expon) (not (numberp expon))) ; f(x)^y is ok
408 (and (atom expon) (numberp expon) (> expon 0))))))
409 ; f(x)^3 is ok, but not f(x)^-1, which could
410 ; inverse of f, if written f^-1 x
411 ; what else? f(x)^(1/2) is sqrt(f(x)), ??
412 (cond (doit
413 (setq l (mathml `((mexpt) ,f ,expon) l nil 'mparen 'mparen))
414 (if (and (null (cdr bascdr))
415 (eq (get f 'mathml) 'mathml-prefix))
416 (setq r (mathml (car bascdr) nil r f 'mparen))
417 (setq r (mathml (cons '(mprogn) bascdr) nil r 'mparen 'mparen))))
418 (t nil))))) ; won't doit. fall through
419 (t (setq l (mathml (cadr x) (append l '("<msup><mrow>")) nil lop (caar x))
420 r (if (mmminusp (setq x (nformat (caddr x))))
421 ;; the change in base-line makes parens unnecessary
422 (if nc
423 (mathml (cadr x) '("</mrow> <mfenced separators=\"\" open=\"<\" close=\">\"> -")(cons "</mfenced></msup> " r) 'mparen 'mparen)
424 (mathml (cadr x) '("</mrow> <mfenced separators=\"\"> -")(cons "</mfenced></msup> " r) 'mparen 'mparen))
425 (if nc
426 (mathml x (list "</mrow> <mfenced separators=\"\" open=\"<\" close=\">\">")(cons "</mfenced></msup>" r) 'mparen 'mparen)
427 (if (and (numberp x) (< x 10))
428 (mathml x (list "</mrow> ")(cons "</msup> " r) 'mparen 'mparen)
429 (mathml x (list "</mrow> <mrow>")(cons "</mrow></msup> " r) 'mparen 'mparen))
430 )))))
431 (append l r)))
433 (defprop mncexpt mathml-mexpt mathml)
435 (defprop mncexpt 135. mathml-lbp)
436 (defprop mncexpt 134. mathml-rbp)
438 (defprop mnctimes mathml-nary mathml)
439 (defprop mnctimes "<mi>&ctdot;</mi> " mathmlsym)
440 (defprop mnctimes 110. mathml-lbp)
441 (defprop mnctimes 109. mathml-rbp)
443 (defprop mtimes mathml-nary mathml)
444 (defprop mtimes "<mspace width=\"thinmathspace\"/>" mathmlsym)
445 (defprop mtimes 120. mathml-lbp)
446 (defprop mtimes 120. mathml-rbp)
448 (defprop %sqrt mathml-sqrt mathml)
450 (defun mathml-sqrt(x l r)
451 ;; format as \\sqrt { } assuming implicit parens for sqr grouping
452 (mathml (cadr x) (append l '("<msqrt>")) (append '("</msqrt>") r) 'mparen 'mparen))
454 ;; macsyma doesn't know about cube (or nth) roots,
455 ;; but if it did, this is what it would look like.
456 (defprop $cubrt mathml-cubrt mathml)
458 (defun mathml-cubrt (x l r)
459 (mathml (cadr x) (append l '("<mroot><mrow>")) (append '("</mrow>3</mroot>") r) 'mparen 'mparen))
461 (defprop mquotient mathml-mquotient mathml)
462 (defprop mquotient ("<mo>/</mo>") mathmlsym)
463 (defprop mquotient 122. mathml-lbp) ;;dunno about this
464 (defprop mquotient 123. mathml-rbp)
466 (defun mathml-mquotient (x l r)
467 (if (or (null (cddr x)) (cdddr x)) (wna-err (caar x)))
468 (setq l (mathml (cadr x) (append l '("<mfrac><mrow>")) nil 'mparen 'mparen)
469 r (mathml (caddr x) (list "</mrow> <mrow>") (append '("</mrow></mfrac> ")r) 'mparen 'mparen))
470 (append l r))
472 (defprop $matrix mathml-matrix mathml)
474 (defun mathml-matrix(x l r) ;;matrix looks like ((mmatrix)((mlist) a b) ...)
475 (append l `("<mfenced separators=\"\" open=\"(\" close=\")\"><mtable>")
476 (mapcan #'(lambda(y)
477 (mathml-list (cdr y) (list "<mtr><mtd>") (list "</mtd></mtr> ") "</mtd><mtd>"))
478 (cdr x))
479 '("</mtable></mfenced> ") r))
481 ;; macsyma sum or prod is over integer range, not low <= index <= high
482 ;; Mathml is lots more flexible .. but
484 (defprop %sum mathml-sum mathml)
485 (defprop %lsum mathml-lsum mathml)
486 (defprop %product mathml-sum mathml)
488 ;; easily extended to union, intersect, otherops
490 (defun mathml-lsum(x l r)
491 (let ((op (cond ((eq (caar x) '%lsum) "<mrow><munder><mo>&sum;</mo> <mrow>")
492 ;; extend here
494 ;; gotta be one of those above
495 (s1 (mathml (cadr x) nil nil 'mparen rop));; summand
496 (index ;; "index = lowerlimit"
497 (mathml `((min simp) , (caddr x), (cadddr x)) nil nil 'mparen 'mparen)))
498 (append l `( ,op ,@index "</mrow></munder> <mrow>" ,@s1 "</mrow></mrow> ") r)))
500 (defun mathml-sum(x l r)
501 (let ((op (cond ((eq (caar x) '%sum) "<mrow><munderover><mo>&sum;</mo><mrow>")
502 ((eq (caar x) '%product) "<mrow><munderover><mo>&prod;</mo><mrow>")
503 ;; extend here
505 ;; gotta be one of those above
506 (s1 (mathml (cadr x) nil nil 'mparen rop));; summand
507 (index ;; "index = lowerlimit"
508 (mathml `((mequal simp) ,(caddr x),(cadddr x)) nil nil 'mparen 'mparen))
509 (toplim (mathml (car(cddddr x)) nil nil 'mparen 'mparen)))
510 (append l `( ,op ,@index "</mrow> <mrow>" ,@toplim "</mrow></munderover> <mrow>" ,@s1 "</mrow></mrow> ") r)))
512 (defprop %integrate mathml-int mathml)
514 (defun mathml-int (x l r)
515 (let ((s1 (mathml (cadr x) nil nil 'mparen 'mparen));;integrand delims / & d
516 (var (mathml (caddr x) nil nil 'mparen rop))) ;; variable
517 (cond((= (length x) 3)
518 (append l `("<mrow><mo>&int;</mo><mrow>" ,@s1 "</mrow> <mspace width=\"mediummathspace\"/> <mrow><mo>&DifferentialD;</mo><mi>" ,@var "</mi></mrow></mrow> ") r))
519 (t ;; presumably length 5
520 (let ((low (mathml (nth 3 x) nil nil 'mparen 'mparen))
521 ;; 1st item is 0
522 (hi (mathml (nth 4 x) nil nil 'mparen 'mparen)))
523 (append l `("<mrow><munderover><mo>&int;</mo> <mrow>" ,@low "</mrow> <mrow>" ,@hi "</mrow> </munderover> <mrow>" ,@s1 "</mrow> <mspace width=\"mediummathspace\"/> <mrow><mo>&DifferentialD;</mo><mi>" ,@var "</mi> </mrow></mrow> ") r))))))
525 (defprop %limit mathml-limit mathml)
527 (defprop mrarr mathml-infix mathml)
528 (defprop mrarr ("<mo>&rarr;</mo> ") mathmlsym)
529 (defprop mrarr 80. mathml-lbp)
530 (defprop mrarr 80. mathml-rbp)
532 (defun mathml-limit(x l r) ;; ignoring direction, last optional arg to limit
533 (let ((s1 (mathml (second x) nil nil 'mparen rop));; limitfunction
534 (subfun ;; the thing underneath "limit"
535 (mathml `((mrarr simp) ,(third x) ,(fourth x)) nil nil 'mparen 'mparen)))
536 (append l `("<munder><mo>lim</mo><mrow>" ,@subfun "</mrow> </munder> <mrow>" ,@s1 "</mrow>") r)))
538 (defprop %at mathml-at mathml)
540 ;; e.g. at(diff(f(x)),x=a)
541 (defun mathml-at (x l r)
542 (let ((s1 (mathml (cadr x) nil nil lop rop))
543 (sub (mathml (caddr x) nil nil 'mparen 'mparen)))
544 (append l '("<msub><mfenced separators=\"\" open=\"\" close=\"|\">") s1 '("</mfenced> <mrow>") sub '("</mrow> </msub> ") r)))
546 ;;binomial coefficients
548 (defprop %binomial mathml-choose mathml)
550 (defun mathml-choose (x l r)
551 `(,@l
552 "<mfenced separators=\"\" open=\"(\" close=\")\"><mtable><mtr><mtd>"
553 ,@(mathml (cadr x) nil nil 'mparen 'mparen)
554 "</mtd></mtr> <mtr><mtd>"
555 ,@(mathml (caddr x) nil nil 'mparen 'mparen)
556 "</mtd></mtr> </mtable></mfenced> "
557 ,@r))
560 (defprop rat mathml-rat mathml)
561 (defprop rat 120. mathml-lbp)
562 (defprop rat 121. mathml-rbp)
563 (defun mathml-rat(x l r) (mathml-mquotient x l r))
565 (defprop mplus mathml-mplus mathml)
566 (defprop mplus 100. mathml-lbp)
567 (defprop mplus 100. mathml-rbp)
569 (defun mathml-mplus (x l r)
570 ;(declare (fixnum w))
571 (cond ((member 'trunc (car x) :test #'eq)
572 (setq r (cons "<mo>+</mo><mtext>&ctdot;</mtext> " r))))
573 (cond ((null (cddr x))
574 (if (null (cdr x))
575 (mathml-function x l r t)
576 (mathml (cadr x) (cons "<mo>+</mo>" l) r 'mplus rop)))
577 (t (setq l (mathml (cadr x) l nil lop 'mplus)
578 x (cddr x))
579 (do ((nl l) (dissym))
580 ((null (cdr x))
581 (if (mmminusp (car x)) (setq l (cadar x) dissym (list "<mo>-</mo> "))
582 (setq l (car x) dissym (list "<mo>+</mo> ")))
583 (setq r (mathml l dissym r 'mplus rop))
584 (append nl r))
585 (if (mmminusp (car x)) (setq l (cadar x) dissym (list "<mo>-</mo> "))
586 (setq l (car x) dissym (list "<mo>+</mo> ")))
587 (setq nl (append nl (mathml l dissym nil 'mplus 'mplus))
588 x (cdr x))))))
590 (defprop mminus mathml-prefix mathml)
591 (defprop mminus ("<mo>-</mo>") mathmlsym)
592 (defprop mminus 100. mathml-rbp)
593 (defprop mminus 100. mathml-lbp)
595 (defprop min mathml-infix mathml)
596 (defprop min ("<mo>&isin;</mo> ") mathmlsym)
597 (defprop min 80. mathml-lbp)
598 (defprop min 80. mathml-rbp)
600 (defprop mequal mathml-infix mathml)
601 (defprop mequal ("<mo>=</mo> ") mathmlsym)
602 (defprop mequal 80. mathml-lbp)
603 (defprop mequal 80. mathml-rbp)
605 (defprop mnotequal mathml-infix mathml)
606 (defprop mnotequal 80. mathml-lbp)
607 (defprop mnotequal 80. mathml-rbp)
609 (defprop mgreaterp mathml-infix mathml)
610 (defprop mgreaterp ("<mo>&gt;</mo> ") mathmlsym)
611 (defprop mgreaterp 80. mathml-lbp)
612 (defprop mgreaterp 80. mathml-rbp)
614 (defprop mgeqp mathml-infix mathml)
615 (defprop mgeqp ("<mo>&ge;</mo> ") mathmlsym)
616 (defprop mgeqp 80. mathml-lbp)
617 (defprop mgeqp 80. mathml-rbp)
619 (defprop mlessp mathml-infix mathml)
620 (defprop mlessp ("<mo>&lt;</mo> ") mathmlsym)
621 (defprop mlessp 80. mathml-lbp)
622 (defprop mlessp 80. mathml-rbp)
624 (defprop mleqp mathml-infix mathml)
625 (defprop mleqp ("<mo>&le;</mo> ") mathmlsym)
626 (defprop mleqp 80. mathml-lbp)
627 (defprop mleqp 80. mathml-rbp)
629 (defprop mnot mathml-prefix mathml)
630 (defprop mnot ("<mo>&not;</mo> ") mathmlsym)
631 (defprop mnot 70. mathml-rbp)
633 (defprop mand mathml-nary mathml)
634 (defprop mand ("<mo>&and;</mo> ") mathmlsym)
635 (defprop mand 60. mathml-lbp)
636 (defprop mand 60. mathml-rbp)
638 (defprop mor mathml-nary mathml)
639 (defprop mor ("<mo>&or;</mo> ") mathmlsym)
641 ;; make sin(x) display as sin x , but sin(x+y) as sin(x+y)
642 ;; etc
644 (defun mathml-setup (x)
645 (let((a (car x))
646 (b (cadr x)))
647 (setf (get a 'mathml) 'mathml-prefix)
648 (setf (get a 'mathmlword) b) ;This means "sin" will always be roman
649 (setf (get a 'mathmlsym) (list b))
650 (setf (get a 'mathml-rbp) 130)))
652 (mapc #'mathml-setup
654 (%acos "<mi>arccos</mi> ")
655 (%asin "<mi>arcsin</mi> ")
656 (%atan "<mi>arctan</mi> ")
657 (%arg "<mi>arg</mi> ")
658 (%cos "<mi>cos</mi> ")
659 (%cosh "<mi>cosh</mi> ")
660 (%cot "<mi>cot</mi> ")
661 (%coth "<mi>coth</mi> ")
662 (%csc "<mi>cosec</mi> ")
663 (%deg "<mi>deg</mi> ")
664 (%determinant "<mi>det</mi> ")
665 (%dim "<mi>dim</mi> ")
666 (%exp "<mi>exp</mi> ")
667 (%gcd "<mi>gcd</mi> ")
668 (%hom "<mi>hom</mi> ")
669 (%inf "<mi>&infin;</mi> ")
670 (%ker "<mi>ker</mi> ")
671 (%lg "<mi>lg</mi> ")
672 ;;(%limit "<mi>lim</mi> ")
673 (%liminf "<mi>lim inf</mi> ")
674 (%limsup "<mi>lim sup</mi> ")
675 (%ln "<mi>ln</mi> ")
676 (%log "<mi>log</mi> ")
677 (%max "<mi>max</mi> ")
678 (%min "<mi>min</mi> ")
679 ; Latex's "Pr" ... ?
680 (%sec "<mi>sec</mi> ")
681 (%sech "<mi>sech</mi> ")
682 (%sin "<mi>sin</mi> ")
683 (%sinh "<mi>sinh</mi> ")
684 (%sup "<mi>sup</mi> ")
685 (%tan "<mi>tan</mi> ")
686 (%tanh "<mi>tanh</mi> ")
687 ;; (%erf "{\\rm erf}") this would tend to set erf(x) as erf x. Unusual
688 ;(%laplace "{\\cal L}")
689 )) ;; etc
691 (defprop mor mathml-nary mathml)
692 (defprop mor 50. mathml-lbp)
693 (defprop mor 50. mathml-rbp)
695 (defprop mcond mathml-mcond mathml)
696 (defprop mcond 25. mathml-lbp)
697 (defprop mcond 25. mathml-rbp)
699 (defprop %derivative mathml-derivative mathml)
701 (defun mathml-derivative (x l r)
702 (mathml (mathml-d x "&DifferentialD;") l r lop rop ))
704 (defun mathml-d(x dsym) ;dsym should be "&DifferentialD;" or "&PartialD;"
705 ;; format the macsyma derivative form so it looks
706 ;; sort of like a quotient times the deriva-dand.
707 (let*
708 ((arg (cadr x)) ;; the function being differentiated
709 (difflist (cddr x)) ;; list of derivs e.g. (x 1 y 2)
710 (ords (odds difflist 0)) ;; e.g. (1 2)
711 (vars (odds difflist 1)) ;; e.g. (x y)
712 (numer `((mexpt) ,dsym ((mplus) ,@ords))) ; d^n numerator
713 (denom (cons '(mtimes)
714 (mapcan #'(lambda(b e)
715 `(,dsym ,(simplifya `((mexpt) ,b ,e) nil)))
716 vars ords))))
717 `((mtimes)
718 ((mquotient) ,(simplifya numer nil) ,denom)
719 ,arg)))
721 (defun mathml-mcond (x l r)
722 (append l
723 (mathml (cadr x) '("<mi>if</mi> <mspace width=\"mediummathspace\"/>")
724 '("<mspace width=\"mediummathspace\"/> <mi>then</mi><mspace width=\"mediummathspace\"/> ") 'mparen 'mparen)
725 (if (eql (fifth x) '$false)
726 (mathml (caddr x) nil r 'mcond rop)
727 (append (mathml (caddr x) nil nil 'mparen 'mparen)
728 (mathml (fifth x) '("<mspace width=\"mediummathspace\"/> <mi>else</mi><mspace width=\"mediummathspace\"/> ") r 'mcond rop)))))
730 (defprop mdo mathml-mdo mathml)
731 (defprop mdo 30. mathml-lbp)
732 (defprop mdo 30. mathml-rbp)
733 (defprop mdoin mathml-mdoin mathml)
734 (defprop mdoin 30. mathml-rbp)
736 (defun mathml-lbp(x)(cond((get x 'mathml-lbp))(t(lbp x))))
737 (defun mathml-rbp(x)(cond((get x 'mathml-rbp))(t(lbp x))))
739 ;; these aren't quite right
741 (defun mathml-mdo (x l r)
742 (mathml-list (mathmlmdo x) l r "<mspace width=\"mediummathspace\"/> "))
744 (defun mathml-mdoin (x l r)
745 (mathml-list (mathmlmdoin x) l r "<mspace width=\"mediummathspace\"/> "))
747 (defun mathmlmdo (x)
748 (nconc (cond ((second x) `("<mi>for</mi> " ,(second x))))
749 (cond ((equal 1 (third x)) nil)
750 ((third x) `("<mi>from</mi> " ,(third x))))
751 (cond ((equal 1 (fourth x)) nil)
752 ((fourth x) `("<mi>step</mi> " ,(fourth x)))
753 ((fifth x) `("<mi>next</mi> " ,(fifth x))))
754 (cond ((sixth x) `("<mi>thru</mi> " ,(sixth x))))
755 (cond ((null (seventh x)) nil)
756 ((eq 'mnot (caar (seventh x)))
757 `("<mi>while</mi> " ,(cadr (seventh x))))
758 (t `("<mi>unless</mi> " ,(seventh x))))
759 `("<mi>do</mi> " ,(eighth x))))
761 (defun mathmlmdoin (x)
762 (nconc `("<mi>for</mi>" ,(second x) "<mi>in</mi> " ,(third x))
763 (cond ((sixth x) `("<mi>thru</mi> " ,(sixth x))))
764 (cond ((null (seventh x)) nil)
765 ((eq 'mnot (caar (seventh x)))
766 `("<mi>while</mi> " ,(cadr (seventh x))))
767 (t `("<mi>unless</mi> " ,(seventh x))))
768 `("<mi>do</mi> " ,(eighth x))))
770 ;; Undone and trickier:
771 ;; Maybe do some special hacking for standard notations for
772 ;; hypergeometric fns, alternative summation notations 0<=n<=inf, etc.