1if not modules then modules = { } end modules ['lxml-mms'] = {
2 version = 1.001,
3 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
4 comment = "written together with Mikael Sundqvist",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37local tonumber, tostring, type, next = tonumber, tostring, type, next
38local concat = table.concat
39local formatters = string.formatters
40
41local xmlall = xml.all
42local xmlfirst = xml.first
43local xmltext = xml.text
44local xmlconvert = xml.convert
45local xmlcollected = xml.collected
46
47local report = logs.reporter("mms")
48
49local getname = mathematics.dictionaries.name
50local classes = mathematics.classes
51local getverboselabel = mathematics.getverboselabel
52local getoptionallabel = mathematics.getoptionallabel
53local functions = mathematics.categories.functions
54local functiontype = mathematics.functiontype
55
56local function xmlevery(x)
57 local dt = x.dt
58 local tt
59 local nn = 0
60 for i=1,#dt do
61 local di = dt[i]
62 if di.tg and not di.special then
63 if tt then
64 nn = nn + 1
65 tt[nn] = di
66 else
67 nn = 1
68 tt = { di }
69 end
70 end
71 end
72 return tt, nn
73end
74
75local function xmlfixattributes(x)
76 for c in xmlcollected(x,"*") do
77 local at = c.at
78 for k, v in next, at do
79 at[k] = tostring(v)
80 end
81 end
82end
83
84local function xmlwipeattributes(x)
85 local at = { }
86 for c in xmlcollected(x,"*") do
87 local at = c.at
88 local colspan = at.colspan
89 local stretchy = at.stretchy
90 local scriptlevel = at.scriptlevel
91 local rulethickness = at.rulethickness
92 if colspan or stretchy or scriptlevel or rulethickness then
93 c.at = {
94 columnspan = colspan,
95 stretchy = stretchy,
96 scriptlevel = scriptlevel,
97 linethickness = rulethickness,
98 }
99 else
100 c.at = {
101
102 }
103 end
104 end
105end
106
107local prepare do
108
109 local expandtimes, expandtimesone
110
111 expandtimesone = function(c,all,i,start,stop)
112 if c then
113 local tg = c.tg
114 if tg == "mrow" then
115 local at = c.at
116 if at.mathunit then
117
118 else
119 local a, na = xmlevery(c)
120 if na > 0 then
121
122
123
124
125
126
127 local times = false
128 local block = true
129 local closed = false
130 for i=1,na do
131 local ai = a[i]
132 local tg = ai.tg
133 local at = ai.at
134 local class = at.mathclass
135 if at.mathsymbolic
136 or at.mathgroup == "postfix operator"
137 then
138 at.checkfunction = "true"
139 if closed then
140 at.mathtimes = 9.1
141 closed = false
142 elseif block then
143 block = false
144 else
145 at.mathtimes = 9.2
146 end
147 block = false
148 times = true
149 expandtimesone(ai,a,i)
150 elseif at.mathfunction
151
152 or at.mathfunctionstack
153 or class == "integral"
154 or at.mathgroup == "unary set"
155 or at.mathgroup == "number set"
156
157 then
158 at.checkfunction = "true"
159 if closed then
160 at.mathtimes = 1.1
161 closed = false
162 elseif block then
163 block = false
164
165
166 elseif at.mathgroup ~= "unary set" then
167 at.mathtimes = 1.2
168 end
169
170
171
172 block = true
173
174 times = false
175 expandtimesone(ai,a,i)
176 elseif tg == "mo" then
177
178 if class == "open" then
179 if closed then
180 at.mathtimes = 2.1
181 closed = false
182 elseif times and not block then
183 at.mathtimes = 2.2
184 end
185 elseif i > 1 and class == "close" then
186 closed = true
187 else
188 closed = false
189 end
190 expandtimes(ai)
191 times = false
192 block = true
193 elseif tg == "mn" then
194 if closed then
195 at.mathtimes = 3.1
196 closed = false
197 elseif times and not block then
198 at.mathtimes = 3.2
199 end
200 expandtimesone(ai)
201 times = false
202 block = false
203 elseif tg == "msub" or tg == "msup" or tg == "msubsup" then
204 local first = xmlfirst(ai,"/*")
205 local fat = first and first.at
206 if closed then
207 at.mathtimes = 4.1
208 closed = false
209 elseif first and at.mathclass == "close" then
210 closed = true
211 elseif times and not block then
212 at.mathtimes = 4.2
213 end
214 if first then
215 if fat.mathfunction
216 or fat.mathfunctionstack
217 or fat.mathclass == "integral"
218 or fat.mathclass == "operator"
219 or fat.mathclass == "differential"
220 then
221 if not fat.mathfunction then
222 fat.mathfunction = "true"
223 end
224 at.checkfunction = "true"
225 expandtimesone(ai,a,i)
226 block = true
227 times = false
228 else
229 expandtimesone(ai,a,i)
230 block = false
231 times = true
232 end
233 else
234 expandtimesone(ai,a,i)
235 block = false
236 times = true
237 end
238 elseif at.mathfractionstack
239 or tg == "mroot"
240 or tg == "mfrac"
241 or tg == "msqrt"
242 or at.mathsymbolic
243 then
244 if closed then
245 at.mathtimes = 5.1
246 closed = false
247 elseif block then
248 block = false
249 else
250 at.mathtimes = 5.2
251 end
252 times = true
253 expandtimesone(ai,a,i)
254 elseif class == "differential" then
255 closed = false
256 block = true
257 times = true
258 expandtimesone(ai,a,i)
259 elseif class == "variable" then
260
261
262
263
264
265 if closed then
266 at.mathtimes = 6.1
267 closed = false
268 elseif times and not block then
269 at.mathtimes = 6.2
270 end
271 block = false
272 times = true
273
274 expandtimesone(ai,a,i)
275 elseif tg == "mrow" then
276 if closed or times then
277 at.mathtimes = 7.1
278 closed = false
279 end
280 times = true
281 expandtimesone(ai,a,i)
282 elseif tg == "mtext" or tg == "ms" then
283 if closed then
284 at.mathtimes = 8.1
285 closed = false
286 end
287 expandtimesone(ai)
288 times = false
289 block = false
290 else
291 if closed then
292 at.mathtimes = 9.1
293 closed = false
294 end
295 times = false
296 expandtimesone(ai,a,i)
297 end
298 end
299 end
300 end
301 ::DONE::
302 elseif tg == "mtable" then
303 for cell in xmlcollected(c,"mtd") do
304 local a, na = xmlevery(cell)
305 if a then
306 expandtimes(a)
307 end
308 end
309 elseif tg == "mtext" or tg == "mspace" or tg == "ms" then
310
311 elseif tg == "mfrac" then
312 local a, na = xmlevery(c)
313 if i > 1 and na == 2 and all[i-1].tg == "mn" and a[1].tg == "mn" and a[2].tg == "mn" and not c.__p__.at.mathfractionstack then
314
315 c.at.mathplus = 1
316 end
317
318 else
319 local a, na = xmlevery(c)
320 if a then
321 expandtimes(a)
322 end
323 end
324 end
325 end
326
327 expandtimes = function(all)
328 if all then
329 for i=1,#all do
330 expandtimesone(all[i],all,i)
331 end
332 end
333 end
334
335 prepare = function(x)
336 local all = xmlall(x,"mi[not @mathfunction]")
337 if all then
338 for i=1,#all do
339 local a = all[i]
340 local t = a.dt[1]
341 local f = functions[t]
342 if f then
343 local at = a.at
344 at.mathfunction = "true"
345 at.mathsymbolic = "true"
346 end
347 end
348 end
349
350 for c in xmlcollected(x,"mrow[@mathfunctionstack]") do
351
352 local cat = c.at
353 local s = cat.mathstack
354 for cc in xmlcollected(c,"(mi|mo)[@mathstack]") do
355 local ccat = cc.at
356 if ccat.mathstack == s then
357 cat.mathfunction = ccat.mathfunction or cat.mathfunction
358 cat.mathcharacter = ccat.mathcharacter or cat.mathcharacter
359 cat.mathgroup = ccat.mathgroup or cat.mathgroup
360 if cc.tg == "mo" then
361 ccat.mathignore = "true"
362
363 end
364 end
365 end
366 end
367
368 local all = xmlall(x,"(msub|msup|msubsup)/mo[@mathclass='open' or @mathclass='close' or @mathclass='middle']")
369 if all then
370 for i=1,#all do
371 local ai = all[i]
372 local at = ai.at
373 at.mathignore = "true"
374 ai.__p__.at.mathclass = at.mathclass
375 end
376 end
377
378 expandtimesone(x)
379 end
380
381end
382
383local tomeaning, getlast do
384
385
386
387 local t, n, expand, expandone, okay, language, domain
388
389
390
391 local function getlabel(tag)
392 return getverboselabel(tag,language,domain)
393 end
394
395 local function getoptional(tag)
396 return getoptionallabel(tag,language,domain)
397 end
398
399 local utfsplit = utf.split
400 local utfbyte = utf.byte
401 local isprivate = fonts.helpers.isprivate
402
403 local unknown <const> = " ? "
404
405 local function sanitize(s)
406 local t = utfsplit(s)
407 local n = #t
408 if n == 1 then
409 if isprivate(utfbyte(s)) then
410 return unknown
411 end
412 else
413 local done = false
414 for i=1,n do
415 if isprivate(utfbyte(t[i])) then
416 done = true
417 t[i] = unknown
418 end
419 end
420 if done then
421 return concat(t)
422 end
423 end
424 return s
425 end
426
427 local function expandsymbol(c)
428 local at = c.at
429 if not at.mathignore then
430 local result = at.mathidentity
431 if not result then
432
433
434
435
436 local group = at.mathgroup
437 if group then
438 local character = tonumber(at.mathcharacter)
439 local index = tonumber(at.mathindex)
440 local s = getname(group,character) or getname(group,index)
441 if type(s) == "string" then
442 result = getlabel(s)
443 else
444 result = sanitize(xmltext(c))
445 end
446 else
447 result = sanitize(xmltext(c))
448 end
449
450 end
451 if result ~= "" then
452 n = n + 1 ; t[n] = result
453 end
454 end
455 end
456
457
458
459 local function hasmiddle(a)
460 if a then
461 for i=1,#a do
462 local class = a[i].at.mathclass
463 if class == "middle" then
464 return true
465 end
466 end
467 end
468 return false
469 end
470
471 local function haslimits(at)
472 local f = at.mathfunction
473 if f then
474 f = functions[f]
475 if f then
476 return f.method == "limits"
477 end
478 end
479 return false
480 end
481
482 local function isintegral(at)
483 return at.mathclass == "integral"
484 end
485
486 local function isoperator(at)
487 return at.mathclass == "operator"
488 end
489
490
491
492 local function expandsupindex(c,a,all,i)
493 if c.at.mathsupindex then
494 n = n + 1 ; t[n] = getlabel("supindex")
495 expandone(a,all,i)
496 else
497 n = n + 1 ; t[n] = getlabel("to the power of")
498 expandone(a,all,i)
499 local last = t[n]
500 if last == "2" then
501 n = n - 1 ; t[n] = getlabel("squared")
502 elseif last == "3" then
503 n = n - 1 ; t[n] = getlabel("cubed")
504 end
505 end
506 end
507
508 local function expandsubindex(c,a,all,i)
509 if c.at.mathsubindex then
510 n = n + 1 ; t[n] = getlabel("subindex")
511 else
512 n = n + 1 ; t[n] = getlabel("sub")
513 end
514 expandone(a,all,i)
515 end
516
517 local function checkfunction(a,i,na)
518 if i + 3 <= na then
519 local a1 = a[i+1]
520 if a1.tg == "mo" then
521 local a3 = a[i+3]
522 if a3.tg == "mo" then
523 local a2 = a[i+2]
524 local t2 = a2.tg
525 if t2 == "mn" or t2 == "mi" then
526 local at1 = a1.at
527 local at3 = a3.at
528 local class1 = at1.mathclass
529 local class2 = at3.mathclass
530 if class1 and class2 and class1 == "open" and class2 == "close" then
531 at1.mathnogroup = "true"
532 at3.mathnogroup = "true"
533 end
534 end
535 end
536 end
537 end
538 end
539
540 local partials = {
541 ["∂"] = "d",
542 }
543
544 local function isdifferential(a)
545 local a1 = a[1]
546 local a2 = a[2]
547 if a1 and a2 then
548 local tg2 = a2.tg
549 local ps = false
550 if tg2 == "mrow" or tg2 == "msub" or tg2 == "msup" or tg2 == "msubsup" then
551 local aa = xmlfirst(a2,"/*")
552 if aa then
553 if aa.tg == "mi" and aa.at.mathclass == "differential" then
554 ps = aa
555 else
556 local aa = xmlfirst(aa,"/*")
557 if aa and aa.tg == "mi" and aa.at.mathclass == "differential" then
558 ps = aa
559 else
560 return false
561 end
562 end
563 else
564 return false
565 end
566 end
567
568 local tg1 = a1.tg
569 if tg1 == "mrow" or tg1 == "msub" or tg1 == "msup" or tg1 == "msubsup" then
570 local aa = xmlfirst(a1,"/*")
571 if aa then
572 if aa.tg == "mi" and aa.at.mathclass == "differential" then
573 local p = partials[aa.dt[1]]
574 if p then
575
576
577 return true, true
578 else
579 return true, false
580 end
581 else
582 local aa = xmlfirst(aa,"/*")
583 if aa and aa.tg == "mi" and aa.at.mathclass == "differential" then
584 local p = partials[aa.dt[1]]
585 if p then
586
587
588 return true, true
589 else
590 return true, false
591 end
592 else
593 return false
594 end
595 end
596 else
597 return false
598 end
599 elseif tg1 == "mi" and a1.at.mathclass == "differential" then
600 local p = partials[a1.dt[1]]
601 if partials[a1.dt[1]] then
602
603
604 return true, true
605 else
606 return true, false
607 end
608 end
609 end
610 return false
611 end
612
613 local function applyof(all,i,label)
614 local ai1 = all[i + 1]
615 if ai1 and ai1.tg == "mo" then
616 local c = ai1.at.mathclass
617 if c == "open" then
618 n = n + 1 ; t[n] = getlabel(label)
619 ai1.at.mathtimes = nil
620 end
621 end
622 end
623
624 local function expandfenced(ai,fenced,all,i)
625 local at = ai.at
626 local class = at.mathclass
627 if not class then
628 if at.mathnogroup then
629
630 else
631 expandone(ai,all,i)
632 end
633 elseif class == "open" then
634 if fenced or at.mathnogroup then
635
636 else
637 n = n + 1 ; t[n] = getlabel("begin group")
638 end
639 at.mathignore = "true"
640 expandone(ai)
641 elseif class == "close" then
642 if fenced or at.mathnogroup then
643
644 else
645 n = n + 1 ; t[n] = getlabel("end group")
646 end
647 at.mathignore = "true"
648 expandone(ai)
649 elseif class == "middle" then
650 if fenced then
651 n = n + 1 ; t[n] = getlabel(fenced.tag .. ":fence")
652 at.mathignore = "true"
653 expandone(ai)
654 local aa = all and all[i+1]
655 if aa then
656 aa.at.mathnogroup = "true"
657 end
658 else
659 expandone(ai)
660 end
661 else
662 expandone(ai,all,i)
663 end
664 end
665
666 local trace_times = false
667
668 trackers.register("structures.tags.math.times", function(v) trace_times = v end)
669
670 expandone = function(c,all,i,start,stop)
671 if c then
672 local tg = c.tg
673 if tg == "mrow" then
674 local at = c.at
675 if at.mathunit then
676 n = n + 1 ; t[n] = at.mathunit
677 else
678 local category = tonumber(at.mathcategory)
679 local kind = category and functiontype(category)
680 local fenced = kind == "fence" and functions[category]
681 local a, na = xmlevery(c)
682 if na > 0 then
683 local subfence = hasmiddle(a)
684 if at.mathfunctionstack then
685 if at.mathfunctionstack then
686 n = n + 1 ; t[n] = getlabel(at.mathfunctionstack)
687 else
688 expandsymbol(c)
689 end
690 at.mathnogroup = "true"
691 else
692 local mfs = at.mathfractionstack
693 if mfs then
694 at.mathnogroup = "true"
695
696 local s = getlabel(mfs)
697 if na > 1 and s and s ~= "" then
698 at.nofraction = mfs
699 n = n + 1 ; t[n] = s
700 end
701 local a = xmlall(c,"/mfrac") or { }
702 expandone(a[1],a,1)
703 goto DONE
704 end
705 end
706
707 local isgroup = false
708 if fenced then
709
710 elseif at.mathnogroup then
711
712 else
713 isgroup = n > 1
714 end
715 if fenced then
716 local s = getlabel(fenced.tag)
717 n = n + 1 ; t[n] = getlabel("optional begin")
718 if s == "" then
719 n = n + 1 ; t[n] = getlabel("begin fenced")
720 else
721 n = n + 1 ; t[n] = s
722 end
723 elseif isgroup then
724 n = n + 1 ; t[n] = getlabel(start or "begin group")
725 end
726 if fenced then
727 if na == 3 then
728 a[2].at.mathnogroup = "true"
729 elseif na == 5 and hasmiddle then
730 a[2].at.mathnogroup = "true"
731 a[3].at.mathnogroup = "true"
732 end
733 end
734
735 for i=1,na do
736 local ai = a[i]
737 local tg = ai.tg
738 local at = ai.at
739 if at.mathtimes then
740 local s
741
742
743
744 s = getlabel("times")
745
746 if s and s ~= "" then
747 if trace_times then
748 n = n + 1 ; t[n] = s .. at.mathtimes
749 else
750 n = n + 1 ; t[n] = s
751 end
752 end
753 end
754 if at.checkfunction then
755 checkfunction(a,i,na)
756 expandone(ai,a,i)
757 elseif tg == "mo" then
758 expandfenced(ai,fenced,a,i)
759
760
761 elseif tg == "msub" or tg == "msup" or tg == "msubsup" then
762 local first = xmlfirst(ai,"/*")
763 local fat = first and first.at
764 if first then
765 if fat.checkfunction then
766 checkfunction(a,i,na)
767 expandone(ai,a,i)
768 else
769 expandfenced(ai,fenced,a,i)
770 end
771 else
772 expandfenced(ai,fenced,a,i)
773 end
774 else
775 expandone(ai,a,i)
776 end
777 end
778
779 if fenced then
780 local s = getlabel(fenced.tag)
781 if s == "" then
782 n = n + 1 ; t[n] = getlabel("end fenced")
783 else
784 n = n + 1 ; t[n] = getlabel("end")
785 n = n + 1 ; t[n] = s
786 end
787 elseif isgroup then
788 n = n + 1 ; t[n] = getlabel(stop or "end group")
789 end
790 end
791 end
792 ::DONE::
793 elseif tg == "mo" then
794 expandsymbol(c)
795 elseif tg == "mn" then
796 n = n + 1 ; t[n] = xmltext(c)
797 elseif tg == "mi" then
798 local at = c.at
799 if at.mathunit then
800 n = n + 1 ; t[n] = at.mathunit
801 elseif at.mathsymbolic then
802 n = n + 1 ; t[n] = getlabel("function")
803 expandsymbol(c)
804 local tg = c.__p__.tg
805 if tg == "msub" or tg == "msup" or tg == "msubsup" then
806 else
807 if all then
808 applyof(all,i,"functionof")
809 end
810 end
811 elseif at.mathfunction then
812 n = n + 1 ; t[n] = getlabel(c.dt[1])
813 local tg = c.__p__.tg
814 if tg == "msub" or tg == "msup" or tg == "msubsup" then
815 else
816 if all then
817 applyof(all,i,"functionof")
818 end
819 end
820 else
821 local s = getoptional(c.dt[1])
822 if s and s ~= "" then
823 n = n + 1 ; t[n] = s
824 end
825 expandsymbol(c)
826 end
827 elseif tg == "msub" then
828 local a, na = xmlevery(c)
829 if a then
830 local a1 = a[1]
831 local at = a1.at
832 expandone(a1,all,i)
833 if haslimits(at) or isintegral(at) or isoperator(at) then
834 if isintegral(at) or isoperator(at) then
835 n = n + 1 ; t[n] = getlabel("integralsub")
836 else
837 n = n + 1 ; t[n] = getlabel("limitsub")
838 end
839 expandone(a[2],all,i)
840 n = n + 1 ; t[n] = getlabel("pause")
841 if all and #all > i then
842 n = n + 1 ; t[n] = getlabel("operatorof")
843 end
844 else
845 expandsubindex(c,a[2],all,i)
846 if at.mathfunction or at.mathsymbolic then
847 applyof(all,i,"functionof")
848 end
849 end
850 end
851 elseif tg == "msup" then
852 local a, na = xmlevery(c)
853 if a then
854 local a1 = a[1]
855 local a2 = a[2]
856 local at = a1.at
857 local group = a2.at.mathgroup
858 if group == "postfix operator" then
859
860 expandone(a2)
861 n = n + 1 ; t[n] = getlabel("operatorof")
862 expandone(a1)
863 elseif group == "prime" then
864 expandone(a1)
865 expandone(a2)
866 if all then
867 applyof(all,i,"primeof")
868 end
869 elseif haslimits(at) or isintegral(at) or isoperator(at) then
870 expandone(a1,all,i)
871 n = n + 1 ; t[n] = getlabel("operatorsup")
872 expandone(a2,all,i)
873 n = n + 1 ; t[n] = getlabel("pause")
874 if all and #all > i then
875 n = n + 1 ; t[n] = getlabel("operatorof")
876 end
877 else
878 expandone(a1,all,i)
879 expandsupindex(c,a2,all,i)
880 if at.mathfunction or at.mathsymbolic then
881 applyof(all,i,"functionof")
882 end
883 end
884 end
885 elseif tg == "msubsup" then
886 local a, na = xmlevery(c)
887 if a then
888 local a1 = a[1]
889 local a2 = a[2]
890 local a3 = a[3]
891 local at = a1.at
892 if a2.at.mathclass == "prime" then
893 expandone(a2)
894 expandone(a1)
895 if haslimits(at) or isintegral(at) or isoperator(at) then
896 n = n + 1 ; t[n] = getlabel("operatorsubsupfrom")
897 expandone(a3,all,i)
898 n = n + 1 ; t[n] = getlabel("pause")
899 end
900 if all and #all > i then
901 n = n + 1 ; t[n] = getlabel("operatorof")
902 end
903 elseif haslimits(at) or isintegral(at) or isoperator(at) then
904 expandone(a1,all,i)
905 n = n + 1 ; t[n] = getlabel("operatorsubsupfrom")
906 expandone(a2,all,i)
907 n = n + 1 ; t[n] = getlabel("operatorsubsupto")
908 expandone(a3,all,i)
909 n = n + 1 ; t[n] = getlabel("pause")
910 if all and #all > i then
911 n = n + 1 ; t[n] = getlabel("operatorof")
912 end
913 else
914 expandone(a1,all,i)
915 expandsubindex(c,a2,all,i)
916 expandsupindex(c,a3,all,i)
917 if at.mathfunction or at.mathsymbolic then
918 applyof(all,i,"functionof")
919 end
920 end
921 end
922 elseif tg == "mfrac" then
923 local a, na = xmlevery(c)
924 if a then
925 local ok, partial = isdifferential(a)
926 if ok then
927 if partial then
928 n = n + 1 ; t[n] = getlabel("the partial derivative")
929 else
930 n = n + 1 ; t[n] = getlabel("the derivative")
931 end
932 expandone(a[1],all,i,"","")
933 n = n + 1 ; t[n] = getlabel("over")
934 expandone(a[2],all,i,"","end derivative")
935 elseif c.__p__.at.nofraction then
936 expandone(a[1],all,i,"","")
937 n = n + 1 ; t[n] = getlabel("over")
938 expandone(a[2],all,i,"","")
939 n = n + 1 ; t[n] = getlabel("end " .. c.__p__.at.nofraction)
940
941 else
942 n = n + 1 ; t[n] = getlabel("the fraction of")
943 expandone(a[1],all,i,"begin numerator","end numerator")
944 n = n + 1 ; t[n] = getlabel("and")
945 expandone(a[2],all,i,"begin denominator","end denominator")
946 end
947 end
948 elseif tg == "msqrt" then
949 n = n + 1 ; t[n] = getlabel("the square root")
950 local a, na = xmlevery(c)
951 if a then
952 n = n + 1 ; t[n] = getlabel("rootof")
953 expand(a,all,i)
954 end
955 elseif tg == "mroot" then
956 local a, na = xmlevery(c)
957 n = n + 1 ; t[n] = getlabel("the root with degree")
958 if a then
959 expandone(a[2],all,i)
960 n = n + 1 ; t[n] = getlabel("rootof")
961 expandone(a[1],all,i)
962 end
963 elseif tg == "munder" then
964 local a, na = xmlevery(c)
965 if a then
966 local category = tonumber(c.at.mathcategory)
967 if functiontype(category) == "accent" then
968 local fnc = functions[category]
969 n = n + 1 ; t[n] = getlabel(fnc.tag)
970 expandone(a[1],all,i)
971 else
972 expandone(a[1],all,i)
973 n = n + 1 ; t[n] = getlabel("under")
974 expandone(a[2],all,i)
975 end
976 end
977 elseif tg == "mover" then
978 local a, na = xmlevery(c)
979 if a then
980 local category = tonumber(c.at.mathcategory)
981 if functiontype(category) == "accent" then
982 local fnc = functions[category]
983 n = n + 1 ; t[n] = getlabel(fnc.tag)
984 expandone(a[1],all,i)
985 else
986 expandone(a[1],all,i)
987 n = n + 1 ; t[n] = getlabel("over")
988 expandone(a[2],all,i)
989 end
990 end
991 elseif tg == "munderover" then
992 local a, na = xmlevery(c)
993 if a then
994 expandone(a[1],all,i)
995 n = n + 1 ; t[n] = getlabel("under")
996 expandone(a[2],all,i)
997 n = n + 1 ; t[n] = getlabel("and over")
998 expandone(a[3],all,i)
999 end
1000 elseif tg == "mtext" then
1001 n = n + 1 ; t[n] = xmltext(c)
1002 elseif tg == "mspace" then
1003 n = n + 1 ; t[n] = ""
1004 elseif tg == "ms" then
1005 n = n + 1 ; t[n] = xmltext(c)
1006 elseif tg == "mmultiscripts" then
1007 local a, na = xmlevery(c)
1008 if a then
1009 local p = false
1010 local s = true
1011 expandone(a[1],all,i)
1012 for i=2,#a do
1013 local ai = a[i]
1014 if ai.tg == "mprescripts" then
1015 p = true
1016 n = n + 1 ; t[n] = getlabel("prescripts")
1017 elseif p then
1018 if ai.tg ~= "mtext" then
1019 n = n + 1 ; t[n] = getlabel(s and "presub" or "presuper")
1020 expandone(ai,all,i)
1021 end
1022 s = not s
1023 end
1024 end
1025 s = true
1026 for i=2,#a do
1027 local ai = a[i]
1028 if ai.tg == "mprescripts" then
1029 break
1030 elseif i == 2 then
1031 n = n + 1 ; t[n] = getlabel("postscripts")
1032 end
1033 if ai.tg ~= "mtext" then
1034 n = n + 1 ; t[n] = getlabel(s and "postsub" or "postsuper")
1035 expandone(ai,all,i)
1036 end
1037 s = not s
1038 end
1039 n = n + 1 ; t[n] = getlabel("end scripts")
1040 end
1041 elseif tg == "math" then
1042 local a, na = xmlevery(c)
1043 if a then
1044 expand(a,all,i)
1045 end
1046 elseif tg == "mtable" then
1047 local detail = c.at.detail
1048 if detail == "cases" then
1049 n = n + 1 ; t[n] = getlabel("begin cases")
1050 local nr = 0
1051 for row in xmlcollected(c,"/mtr") do
1052 nr = nr + 1
1053 n = n + 1 ; t[n] = getlabel("case")
1054 n = n + 1 ; t[n] = tostring(nr)
1055 for cell in xmlcollected(row,"/mtd") do
1056 local a, na = xmlevery(cell)
1057 if a then
1058 expand(a)
1059 end
1060 end
1061
1062 end
1063 n = n + 1 ; t[n] = getlabel("end cases")
1064 else
1065 n = n + 1 ; t[n] = getlabel("begin table")
1066 local nr = 0
1067 for row in xmlcollected(c,"/mtr") do
1068 local nc = 0
1069 nr = nr + 1
1070
1071
1072 for cell in xmlcollected(row,"/mtd") do
1073 nc = nc + 1
1074
1075
1076 n = n + 1 ; t[n] = getlabel("cell")
1077 n = n + 1 ; t[n] = tostring(nr)
1078 n = n + 1 ; t[n] = tostring(nc)
1079 local a, na = xmlevery(cell)
1080 if a then
1081 expand(a)
1082 end
1083 end
1084 end
1085 n = n + 1 ; t[n] = getlabel("end table")
1086 end
1087 elseif not c.special then
1088 okay = false
1089 report("todo: %s",tostring(c))
1090 end
1091 end
1092 end
1093
1094 expand = function(all)
1095 if all then
1096 for i=1,#all do
1097 expandone(all[i],all,i)
1098 end
1099 end
1100 end
1101
1102 local keep_last = false
1103
1104 trackers.register("structures.tags.math.keeplast", function(v) keep_last = v end)
1105
1106 tomeaning = function(x,l)
1107 t = { }
1108 n = 0
1109 okay = true
1110 language = l or "en"
1111 domain = x.at["data-lmtx-domain"] or x.at["domain"] or "default"
1112
1113 expandone(x)
1114 if okay then
1115
1116 local m = false
1117 for i=1,n do
1118 local ti = t[i]
1119 if ti == "" then
1120 if not m then
1121 m = i - 1
1122 end
1123 elseif m then
1124 m = m + 1
1125 t[m] = ti
1126 end
1127 end
1128 if m then
1129 n = m
1130 end
1131 if t[n] == "." then
1132 n = n - 1
1133 end
1134 if keep_last then
1135 xmlfixattributes(x)
1136 buffers.assign(type(keep_last) == "string" and keep_last or "lastmms",tostring(x))
1137 else
1138 lastxml = false
1139 end
1140 return concat(t," ",1,n)
1141 end
1142 end
1143
1144 getlast = function()
1145 return lastxml or ""
1146 end
1147
1148end
1149
1150local stripped do
1151
1152 local strip = true
1153
1154 directives.register("structures.tags.math.strip", function(v) strip = v end)
1155
1156 stripped = function(s)
1157 if strip and #s > 0 then
1158
1159 local x = xmlconvert(s)
1160 xmlwipeattributes(x)
1161 return tostring(x)
1162 else
1163 return s
1164 end
1165 end
1166
1167end
1168
1169local verbose do
1170
1171
1172
1173
1174 local warned = false
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186 verbose = function(s,l)
1187 if not warned then
1188 report("this feature is experimental and under construction")
1189 warned = true
1190 end
1191 local root = xmlconvert(s)
1192 if not root.error then
1193 local x = xmlfirst(root,"/math")
1194 if x then
1195 prepare(x)
1196
1197 local meaning = tomeaning(x,l)
1198
1199
1200
1201 return meaning
1202 end
1203 end
1204 end
1205
1206end
1207
1208xml.mml = {
1209 verbose = verbose,
1210 stripped = stripped,
1211}
1212 |