1if not modules then modules = { } end modules ['back-exp-imp-mth'] = {
2 version = 1.001,
3 comment = "companion to back-exp.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local sub = string.sub
10local utfchar, utfvalues = utf.char, utf.values
11local setmetatableindex, concat = table.setmetatableindex, table.concat
12
13local structurestags = structures.tags
14local specifications = structurestags.specifications
15local locatedtag = structurestags.locatedtag
16
17local backend = structurestags.backend
18
19local setattribute = backend.setattribute
20local extras = backend.extras
21local checks = backend.checks
22local finalizers = backend.finalizers
23
24
25
26local f_em = string.formatters["%.6Nem"]
27
28local implement = interfaces.implement
29
30do
31
32 local automathrows = true directives.register("export.math.autorows", function(v) automathrows = v end)
33 local automathapply = true directives.register("export.math.autoapply", function(v) automathapply = v end)
34 local automathnumber = true directives.register("export.math.autonumber", function(v) automathnumber = v end)
35 local automathstrip = true directives.register("export.math.autostrip", function(v) automathstrip = v end)
36
37 local functions = mathematics.categories.functions
38
39 local function collapse(di,i,data,ndata,detail,element)
40 local collapsing = di.data
41 if data then
42 di.element = element
43 di.detail = nil
44 i = i + 1
45 while i <= ndata do
46 local dn = data[i]
47 if dn.detail == detail then
48 collapsing[#collapsing+1] = dn.data[1]
49 dn.skip = "ignore"
50 i = i + 1
51 else
52 break
53 end
54 end
55 end
56 return i
57 end
58
59 local function collapse_mn(di,i,data,ndata)
60
61
62 local collapsing = di.data
63 if data then
64 i = i + 1
65 while i <= ndata do
66 local dn = data[i]
67 local tg = dn.tg
68 if tg == "mn" then
69 collapsing[#collapsing+1] = dn.data[1]
70 dn.skip = "ignore"
71 i = i + 1
72 elseif tg == "mo" then
73 local d = dn.data[1]
74 if d == "." then
75 collapsing[#collapsing+1] = d
76 dn.skip = "ignore"
77 i = i + 1
78 else
79 break
80 end
81 else
82 break
83 end
84 end
85 end
86 return i
87 end
88
89
90
91 local apply_function = {
92 {
93 element = "mo",
94
95
96 data = { "⁡" },
97 nature = "mixed",
98 }
99 }
100
101 local functioncontent = { }
102
103 setmetatableindex(functioncontent,function(t,k)
104 local v = { { content = k } }
105 t[k] = v
106 return v
107 end)
108
109 local dummy_nucleus = {
110 element = "mtext",
111 data = { content = "" },
112 nature = "inline",
113 comment = "dummy nucleus",
114 fulltag = "mtext>0"
115 }
116
117 local function accentchar(d)
118 local detail = tonumber(d.detail)
119 if detail then
120 local d1 = d.data[1]
121 if d1 and d1.tg == "mrow" then
122
123 d.element = "mrow"
124 d.detail = nil
125 local s = specifications[d.fulltag]
126 s.detail = nil
127
128 d1.element = "mo"
129
130 d1.nature = "mixed"
131 d1.data = { { content = utfchar(detail) } }
132 return d
133 end
134 end
135 end
136
137 local no_mrow = {
138 mrow = true,
139 mfenced = true,
140 mfrac = true,
141 mroot = true,
142 msqrt = true,
143 mtable = true,
144 mi = true,
145 mo = true,
146 mn = true,
147 mspace = true,
148
149
150
151 }
152
153 local function checkmath(root)
154 local data = root.data
155 if data then
156 local ndata = #data
157 local roottg = root.tg
158 if roottg == "mo" then
159 local s = specifications[root.fulltag]
160 local c = s.class
161 if c == "open" or c == "close" or c == "middle" then
162 root.attributes = {
163 maxsize = 1
164 }
165 end
166 elseif roottg == "msubsup" then
167
168 local nucleus, superscript, subscript
169 if ndata > 3 then
170
171 else
172 for i=1,ndata do
173 local di = data[i]
174 if not di then
175
176 elseif di.content then
177
178 else
179 local s = specifications[di.fulltag]
180 if s.subscript then
181 subscript = i
182 elseif s.superscript then
183 superscript = i
184 else
185 nucleus = i
186 end
187 end
188 end
189 if superscript or subscript then
190
191 local nuc = nucleus and data[nucleus]
192 local sub = subscript and data[subscript]
193 local sup = superscript and data[superscript]
194 local n = 0
195 if nuc then n = n + 1 ; data[n] = nuc end
196 if sub then n = n + 1 ; data[n] = sub end
197 if sup then n = n + 1 ; data[n] = sup end
198 end
199 end
200
201
202
203
204
205
206
207
208 elseif roottg == "mfenced" then
209 local s = specifications[root.fulltag]
210 local o = s.operator
211 if o then
212 root.skip = "comment"
213
214 else
215 local l = s.left
216 local m = s.middle
217 local r = s.right
218 if l then
219 l = utfchar(l)
220 end
221 if m then
222 local t = { }
223 for i=1,#m do
224 t[i] = utfchar(m[i])
225 end
226 m = concat(t)
227 end
228 if r then
229 r = utfchar(r)
230 end
231 root.attributes = {
232 open = l,
233 separators = m,
234 close = r,
235 }
236 end
237 elseif roottg == "mstacker" then
238 end
239 if ndata == 0 then
240 root.skip = "comment"
241 root.nota = "weird"
242 return
243 elseif ndata == 1 then
244 local d = data[1]
245 if not d or d == "" then
246 root.skip = "comment"
247 return
248 elseif d.content then
249 return
250 else
251 local tg = d.tg
252 if automathrows and (roottg == "mrow" or roottg == "mtext") then
253
254
255 if no_mrow[tg] then
256 root.skip = "comment"
257 end
258 elseif roottg == "mo" then
259 if tg == "mo" then
260 root.skip = "comment"
261 end
262 end
263 end
264 end
265 local i = 1
266 while i <= ndata do
267 local di = data[i]
268 if di and not di.content then
269 local tg = di.tg
270 if tg == "math" then
271
272 di.skip = "comment"
273 checkmath(di)
274 i = i + 1
275 elseif tg == "mover" then
276 local s = specifications[di.fulltag]
277 if s.accent then
278 local t = s.top
279 local d = di.data
280
281 di.attributes = {
282 accent = "true",
283 }
284
285 if t then
286
287 if true then
288 local dd = d[1].data
289 if dd then
290 dd[1].content = utfchar(t)
291 end
292 end
293
294 di.data = { d[2], d[1] }
295 end
296 else
297
298 end
299 checkmath(di)
300 i = i + 1
301 elseif tg == "munder" then
302 local s = specifications[di.fulltag]
303 if s.accent then
304 local b = s.bottom
305 local d = di.data
306
307 di.attributes = {
308 accent = "true",
309 }
310
311 if b then
312
313 if true then
314 local dd = d[2].data
315 if dd then
316 dd[1].content = utfchar(b)
317 end
318 end
319 end
320 else
321
322 end
323 checkmath(di)
324 i = i + 1
325 elseif tg == "munderover" then
326 local s = specifications[di.fulltag]
327 if s.accent then
328 local t = s.top
329 local b = s.bottom
330 local d = di.data
331
332
333 di.attributes = {
334 accent = "true",
335 accentunder = "true",
336 }
337
338
339 if t and b then
340
341 if true then
342 local dd = d[1].data
343 if dd then
344 dd[1].content = utfchar(t)
345 end
346 local dd = d[3].data
347 if dd then
348 dd[1].content = utfchar(b)
349 end
350 end
351 di.data = { d[2], d[3], d[1] }
352 else
353
354 end
355 else
356
357 end
358 checkmath(di)
359 i = i + 1
360 elseif tg == "mstacker" then
361
362 local d = di.data
363 local d1 = d[1]
364 local d2 = d[2]
365 local d3 = d[3]
366 local t1 = d1 and d1.tg
367 local t2 = d2 and d2.tg
368 local t3 = d3 and d3.tg
369 local m = nil
370 local t = nil
371 local b = nil
372
373
374 if t1 == "mstackermid" then
375 m = accentchar(d1)
376 if t2 == "mstackertop" then
377 if t3 == "mstackerbot" then
378 t = accentchar(d2)
379 b = accentchar(d3)
380 di.element = "munderover"
381 di.data = { m or d1.data[1], b or d3.data[1], t or d2.data[1] }
382 else
383 t = accentchar(d2)
384 di.element = "mover"
385 di.data = { m or d1.data[1], t or d2.data[1] }
386 end
387 elseif t2 == "mstackerbot" then
388 if t3 == "mstackertop" then
389 b = accentchar(d2)
390 t = accentchar(d3)
391 di.element = "munderover"
392 di.data = { m or d1.data[1], t or d3.data[1], m, b or d2.data[1] }
393 else
394 b = accentchar(d2)
395 di.element = "munder"
396 di.data = { m or d1.data[1], b or d2.data[1] }
397 end
398 else
399
400
401 end
402 else
403
404 end
405 if t or b then
406 di.attributes = {
407 accent = t and "true" or nil,
408 accentunder = b and "true" or nil,
409 }
410 di.detail = nil
411 end
412
413 checkmath(di)
414 i = i + 1
415 elseif tg == "mroot" then
416 local data = di.data
417 local size = #data
418 if size == 1 then
419
420 di.element = "msqrt"
421 elseif size == 2 then
422 data[1], data[2] = data[2], data[1]
423 end
424 checkmath(di)
425 i = i + 1
426 elseif tg == "break" then
427 di.skip = "comment"
428 i = i + 1
429 elseif tg == "mspace" then
430
431 local s = specifications[di.fulltag]
432 local e = s and s.emfactor
433 if e and e ~= 0 then
434 di.element = "mspace"
435 di.attributes = {
436 width = f_em(e),
437 }
438 end
439 i = i + 1
440 elseif tg == "mtext" then
441
442
443 local data = di.data
444 if #data > 1 then
445 for i=1,#data do
446 local di = data[i]
447 local content = di.content
448 if content then
449 data[i] = {
450 element = "mtext",
451 nature = "inline",
452 data = { di },
453 n = 0,
454 }
455 elseif di.tg == "math" then
456 local di = di.data[1]
457 if di then
458 data[i] = di
459 checkmath(di)
460 end
461 end
462 end
463 di.element = "mrow"
464
465
466 end
467 checkmath(di)
468 i = i + 1
469 elseif tg == "mrow" and detail then
470 di.detail = nil
471 checkmath(di)
472 di = {
473 element = "maction",
474 nature = "display",
475 attributes = { actiontype = detail },
476 data = { di },
477 n = 0,
478 }
479 data[i] = di
480 i = i + 1
481 else
482 local category = di.mathcategory
483 if category then
484
485 if category == 1 then
486 i = collapse(di,i,data,ndata,detail,"mo")
487 elseif category == 2 then
488 i = collapse(di,i,data,ndata,detail,"mi")
489 elseif category == 3 then
490 i = collapse(di,i,data,ndata,detail,"mn")
491 elseif category == 4 then
492 i = collapse(di,i,data,ndata,detail,"ms")
493 elseif category >= 1000 then
494 local apply = category >= 2000
495 if apply then
496 category = category - 1000
497 end
498 if tg == "mi" then
499 if roottg == "mrow" then
500 root.skip = "comment"
501 root.element = "function"
502 end
503 i = collapse(di,i,data,ndata,detail,"mi")
504 local tag = functions[category]
505 if tag then
506 di.data = functioncontent[tag]
507 end
508 if apply then
509 di.after = apply_function
510 elseif automathapply then
511 local following
512 if i <= ndata then
513
514 following = data[i]
515 else
516 local parent = di.__p__
517 if parent.tg == "mrow" then
518 parent = parent.__p__
519 end
520 local index = parent.__i__
521 following = parent.data[index+1]
522 end
523 if following then
524 local tg = following.tg
525 if tg == "mrow" or tg == "mfenced" then
526 di.after = apply_function
527 end
528 end
529 end
530 else
531 checkmath(di)
532 i = i + 1
533 end
534 else
535 checkmath(di)
536 i = i + 1
537 end
538 elseif automathnumber and tg == "mn" then
539 checkmath(di)
540 i = collapse_mn(di,i,data,ndata)
541 else
542 checkmath(di)
543 i = i + 1
544 end
545 end
546 else
547 if parenttg ~= "mtext" and di == " " then
548 data[i] = false
549 end
550 i = i + 1
551 end
552 end
553 end
554 end
555
556 local function stripmath(di)
557 if not di then
558
559 elseif di.content then
560 return di
561 else
562 local tg = di.tg
563 if tg == "mtext" or tg == "ms" then
564 return di
565 elseif tg == "mspace" then
566 return di
567 else
568 local data = di.data
569 local ndata = #data
570 local n = 0
571 for i=1,ndata do
572 local d = data[i]
573 if d and not d.content then
574 d = stripmath(d)
575 end
576 if d then
577 local content = d.content
578 if d.tg == "mspace" then
579 n = n + 1
580 data[n] = d
581 d.data = { }
582 elseif not content then
583
584 n = n + 1
585 d.__i__ = n
586 data[n] = d
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615 else
616 n = n + 1
617 data[n] = d
618 end
619 end
620 end
621 for i=ndata,n+1,-1 do
622 data[i] = nil
623 end
624 if #data > 0 then
625 return di
626 end
627 end
628 end
629 end
630
631 function checks.math(di)
632 if di.skip == "comment" then
633
634
635 else
636 local specification = specifications[di.fulltag]
637 local mode = specification and specification.mode == "display" and "block" or "inline"
638 di.attributes = {
639 ["display"] = mode,
640 ["xmlns:m"] = mathmlns,
641 ["xmlns:math"] = mathmlns,
642 }
643
644 if mode == "inline" then
645
646 di.nature = "inline"
647 else
648 di.nature = "display"
649 end
650 if automathstrip then
651 stripmath(di)
652 end
653 checkmath(di)
654 end
655 end
656
657
658
659
660
661 local function checked(d)
662 local n = #d
663 if n == 1 then
664 local di = d[1]
665 local tg = di.tg
666 if tg == "ignore" then
667
668 return 1
669 elseif di.content then
670 return 1
671 else
672 local dd = di.data
673 if #dd > 0 and checked(dd) > 0 then
674 return 1
675 else
676 return 0
677 end
678 end
679 else
680 local m = 0
681 for i=1,n do
682 local di = d[i]
683 local tg = di.tg
684 if tg == "ignore" then
685
686 elseif di.content then
687 m = m + 1
688 d[m] = di
689 else
690 local dd = di.data
691 if #dd > 0 and checked(dd) > 0 then
692 m = m + 1
693 d[m] = di
694 end
695 end
696 end
697 if m < n then
698 for i=n,m+1,-1 do
699 d[i] = nil
700 end
701 end
702 return m
703 end
704 end
705
706 function checks.mrow(di)
707
708
709
710
711 end
712
713
714
715 local function flatten(di)
716 local r = di.__p__
717 while r do
718 local d = r.data
719 local n = #d
720 if d and n > 1 then
721 n = checked(d)
722 end
723 local tg = r.tg
724 if n == 1 and (tg == "mtext" or tg == "mrow") then
725 r.skip = "comment"
726 r = r.__p__
727 else
728 break
729 end
730 end
731 end
732
733 function checks.mtable(di)
734 flatten(di)
735 local d = di.data
736 for i=1,#d do
737 local d = d[i]
738 if d.tg == "mtr" then
739 local d = d.data
740 for i=1,#d do
741 local d = d[i]
742 if d.tg == "mtd" then
743
744 elseif d.content then
745 d.content = ""
746 else
747 d.skip = "comment"
748 end
749 end
750 elseif d.content then
751 d.content = ""
752 else
753 d.skip = "comment"
754 end
755 end
756 end
757
758 do
759
760 local a, z, A, Z = 0x61, 0x7A, 0x41, 0x5A
761
762 function extras.mi(di,element,n,fulltag)
763 local str = di.data[1].content
764 if str and sub(str,1,1) ~= "&" then
765 for v in utfvalues(str) do
766 if (v >= a and v <= z) or (v >= A and v <= Z) then
767 local a = di.attributes
768 if a then
769 a.mathvariant = "normal"
770 else
771 di.attributes = { mathvariant = "normal" }
772 end
773 end
774 end
775 end
776 end
777
778 end
779
780 function extras.msub(di,element,n,fulltag)
781
782 local data = di.data
783 if #data == 1 then
784 local d = data[1]
785 data[2] = d
786 d.__i__ = 2
787 data[1] = dummy_nucleus
788 end
789 end
790
791 extras.msup = extras.msub
792
793end
794 |