1if not modules then modules = { } end modules ['math-ini'] = {
2 version = 1.001,
3 comment = "companion to math-ini.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
9
10
11
12
13
14
15
16
17
18local next, type = next, type
19local formatters, find = string.formatters, string.find
20local utfchar, utfbyte, utflength = utf.char, utf.byte, utf.length
21
22local sortedhash = table.sortedhash
23local toboolean = toboolean
24
25local context = context
26local commands = commands
27local implement = interfaces.implement
28
29local ctx_sprint = context.sprint
30local ctx_doifelsesomething = commands.doifelsesomething
31
32local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
33
34local report_math = logs.reporter("mathematics","initializing")
35
36mathematics = mathematics or { }
37local mathematics = mathematics
38
39mathematics.extrabase = fonts.privateoffsets.mathextrabase
40mathematics.privatebase = fonts.privateoffsets.mathbase
41
42local unsetvalue = attributes.unsetvalue
43local allocate = utilities.storage.allocate
44local chardata = characters.data
45
46local texsetattribute = tex.setattribute
47local setmathcode = tex.setmathcode
48local setdelcode = tex.setdelcode
49
50local families = allocate {
51 mr = 0,
52 mb = 1,
53}
54
55
56
57local classes = allocate {
58 ord = 0,
59 op = 1,
60 bin = 2,
61 rel = 3,
62 open = 4,
63 middle = 4,
64 close = 5,
65 punct = 6,
66 alpha = 7,
67 accent = 8,
68 radical = 9,
69 xaccent = 10,
70 topaccent = 11,
71 botaccent = 12,
72 under = 13,
73 over = 14,
74 delimiter = 15,
75 inner = 0,
76 nothing = 0,
77 choice = 0,
78 box = 0,
79 limop = 1,
80 nolop = 1,
81
82 ordinary = 0,
83 alphabetic = 7,
84 unknown = 0,
85 default = 0,
86 punctuation = 6,
87 normal = 0,
88 opening = 4,
89 closing = 5,
90 binary = 2,
91 relation = 3,
92 fence = 0,
93 diacritic = 8,
94 large = 1,
95 variable = 7,
96 number = 7,
97 root = 16,
98}
99
100local open_class = 4
101local middle_class = 4
102local close_class = 5
103local accent_class = 8
104local radical_class = 9
105local topaccent_class = 11
106local botaccent_class = 12
107local under_class = 13
108local over_class = 14
109local delimiter_class = 15
110local root_class = 16
111
112local accents = allocate {
113 accent = true,
114 topaccent = true, [11] = true,
115 botaccent = true, [12] = true,
116 under = true, [13] = true,
117 over = true, [14] = true,
118 unknown = false,
119}
120
121local codes = allocate {
122 ordinary = 0, [0] = "ordinary",
123 largeoperator = 1, "largeoperator",
124 binaryoperator = 2, "binaryoperator",
125 relation = 3, "relation",
126 openingsymbol = 4, "openingsymbol",
127 closingsymbol = 5, "closingsymbol",
128 punctuation = 6, "punctuation",
129 variable = 7, "variable",
130}
131
132local extensibles = allocate {
133 unknown = 0,
134 l = 1, left = 1,
135 r = 2, right = 2,
136 h = 3, horizontal = 3,
137 u = 5, up = 4,
138 d = 5, down = 5,
139 v = 6, vertical = 6,
140 m = 7, mixed = 7,
141}
142
143table.setmetatableindex(extensibles,function(t,k) t[k] = 0 return 0 end)
144
145local virtualized = allocate {
146}
147
148function mathematics.virtualize(unicode,virtual)
149
150 local function virtualize(k,v)
151 local c = virtualized[k]
152 if c == v then
153 report_math("character %C is already virtualized to %C",k,v)
154 elseif c then
155 report_math("character %C is already virtualized to %C, ignoring mapping to %C",k,c,v)
156 else
157 virtualized[k] = v
158 end
159 end
160
161 if type(unicode) == "table" then
162 for k, v in next, unicode do
163 virtualize(k,v)
164 end
165 elseif type(unicode) == "number" and type(virtual) == "number" then
166 virtualize(unicode,virtual)
167
168
169 end
170end
171
172mathematics.extensibles = extensibles
173mathematics.classes = classes
174mathematics.codes = codes
175
176mathematics.families = families
177mathematics.virtualized = virtualized
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226local escapes = characters.filters.utf.private.escapes
227
228
229
230local setmathcharacter = function(class,family,slot,unicode,mset,dset)
231 if mset and codes[class] then
232 setmathcode("global",slot,class,family,unicode)
233 mset = false
234 end
235 if dset and class == open_class or class == close_class or class == middle_class then
236 setdelcode("global",slot,family,unicode,0,0)
237 dset = false
238 end
239 return mset, dset
240end
241
242
243
244local f_accent = formatters[ [[\defUmathtopaccent \%s{%X}{%X}{%X}]] ]
245local f_topaccent = formatters[ [[\defUmathtopaccent \%s{%X}{%X}{%X}]] ]
246local f_botaccent = formatters[ [[\defUmathbotaccent \%s{%X}{%X}{%X}]] ]
247local f_over = formatters[ [[\defUdelimiterover \%s{%X}{%X}{%X}]] ]
248local f_under = formatters[ [[\defUdelimiterunder\%s{%X}{%X}{%X}]] ]
249local f_fence = formatters[ [[\defUdelimiter \%s{%X}{%X}{%X}]] ]
250local f_delimiter = formatters[ [[\defUdelimiter \%s{%X}{%X}{%X}]] ]
251local f_radical = formatters[ [[\defUradical \%s{%X}{%X}]] ]
252local f_root = formatters[ [[\defUroot \%s{%X}{%X}]] ]
253local f_char = formatters[ [[\defUmathchar \%s{%X}{%X}{%X}]] ]
254
255local texmathchardef = tex.mathchardef
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286local setmathsymbol = function(name,class,family,slot)
287 if class == accent_class then
288 ctx_sprint(f_topaccent(name,0,family,slot))
289 elseif class == topaccent_class then
290 ctx_sprint(f_topaccent(name,0,family,slot))
291 elseif class == botaccent_class then
292 ctx_sprint(f_botaccent(name,0,family,slot))
293 elseif class == over_class then
294 ctx_sprint(f_over(name,0,family,slot))
295 elseif class == under_class then
296 ctx_sprint(f_under(name,0,family,slot))
297 elseif class == open_class or class == close_class or class == middle_class then
298 setdelcode("global",slot,{family,slot,0,0})
299 ctx_sprint(f_fence(name,class,family,slot))
300 elseif class == delimiter_class then
301 setdelcode("global",slot,{family,slot,0,0})
302 ctx_sprint(f_delimiter(name,0,family,slot))
303 elseif class == radical_class then
304 ctx_sprint(f_radical(name,family,slot))
305 elseif class == root_class then
306 ctx_sprint(f_root(name,family,slot))
307 elseif texmathchardef then
308 texmathchardef(name,class,family,slot,"permanent")
309 else
310
311 ctx_sprint(f_char(name,class,family,slot))
312 end
313end
314
315local function report(class,family,unicode,name)
316 local nametype = type(name)
317 if nametype == "string" then
318 report_math("class name %a, class %a, family %a, char %C, name %a",classname,class,family,unicode,name)
319 elseif nametype == "number" then
320 report_math("class name %a, class %a, family %a, char %C, number %U",classname,class,family,unicode,name)
321 else
322 report_math("class name %a, class %a, family %a, char %C", classname,class,family,unicode)
323 end
324end
325
326
327
328function mathematics.define(family)
329 family = family or 0
330 family = families[family] or family
331 local data = characters.data
332 for unicode, character in sortedhash(data) do
333 local symbol = character.mathsymbol
334 local mset = true
335 local dset = true
336 if symbol then
337 local other = data[symbol]
338 local class = other.mathclass
339 if class then
340 class = classes[class] or class
341 if trace_defining then
342 report(class,family,unicode,symbol)
343 end
344 mset, dset = setmathcharacter(class,family,unicode,symbol,mset,dset)
345 end
346 local spec = other.mathspec
347 if spec then
348 for i=1,#spec do
349 local m = spec[i]
350 local class = m.class
351 if class then
352 class = classes[class] or class
353 mset, dset = setmathcharacter(class,family,unicode,symbol,mset,dset)
354 end
355 end
356 end
357 end
358 local mathclass = character.mathclass
359 local mathspec = character.mathspec
360 if mathspec then
361 if mathclass then
362 local name = character.mathname
363 if name then
364 report_math("fatal error, conflicting mathclass and mathspec for %C",unicode)
365 os.exit()
366 else
367 local class = classes[mathclass] or mathclass
368 if not class then
369 if trace_defining then
370 report("unknown",family,unicode)
371 end
372 else
373 if trace_defining then
374 report(class,family,unicode)
375 end
376 mset, dset = setmathcharacter(class,family,unicode,unicode,mset,dset)
377 end
378 end
379 end
380 for i=1,#mathspec do
381 local m = mathspec[i]
382 local name = m.name
383 local class = m.class
384 if class then
385 class = classes[class] or class
386 if name then
387 if trace_defining then
388 report(class,family,unicode,name)
389 end
390 setmathsymbol(name,class,family,unicode)
391 else
392 name = (class == classes.variable or class == classes.number) and character.adobename
393 if name and trace_defining then
394 report(class,family,unicode,name)
395 end
396 end
397 mset, dset = setmathcharacter(class,family,unicode,m.unicode or unicode,mset,dset)
398 end
399 end
400 elseif mathclass then
401 local name = character.mathname
402 local class = classes[mathclass] or mathclass
403 if not class then
404 if trace_defining then
405 report("unknown",family,unicode,name)
406 end
407 elseif name == false then
408 if trace_defining then
409 report(class,family,unicode,name)
410 end
411 mset, dset = setmathcharacter(class,family,unicode,unicode,mset,dset)
412 else
413
414
415
416 if name then
417 if trace_defining then
418 report(class,family,unicode,name)
419 end
420 setmathsymbol(name,class,family,unicode)
421 else
422 if trace_defining then
423 report(class,family,unicode,character.adobename)
424 end
425 end
426 mset, dset = setmathcharacter(class,family,unicode,unicode,mset,dset)
427 end
428 end
429 end
430end
431
432
433
434
435
436local lpegmatch = lpeg.match
437
438local utf8byte = lpeg.patterns.utf8byte * lpeg.P(-1)
439
440
441
442
443
444
445
446local somechar = { }
447
448table.setmetatableindex(somechar,function(t,k)
449 if k then
450 local b = lpegmatch(utf8byte,k)
451 local v = b and chardata[b] or false
452 t[k] = v
453 return v
454 end
455end)
456
457local function utfmathclass(chr, default)
458 local cd = somechar[chr]
459 return cd and cd.mathclass or default or "unknown"
460end
461
462local function utfmathlimop(chr)
463 local cd = somechar[chr]
464 return cd and cd.mathclass == "limop" or false
465end
466
467local function utfmathaccent(chr,default,asked1,asked2)
468 local cd = somechar[chr]
469 if not cd then
470 return default or false
471 end
472 if asked1 and asked1 ~= "" then
473 local mc = cd.mathclass
474 if mc and (mc == asked1 or mc == asked2) then
475 return true
476 end
477 local ms = cd.mathspec
478 if not ms then
479 local mp = cd.mathparent
480 if mp then
481 ms = chardata[mp].mathspec
482 end
483 end
484 if ms then
485 for i=1,#ms do
486 local msi = ms[i]
487 local mc = msi.class
488 if mc and (mc == asked1 or mc == asked2) then
489 return true
490 end
491 end
492 end
493 else
494 local mc = cd.mathclass
495 if mc then
496 return accents[mc] or default or false
497 end
498 local ms = cd.mathspec
499 if ms then
500 for i=1,#ms do
501 local msi = ms[i]
502 local mc = msi.class
503 if mc then
504 return accents[mc] or default or false
505 end
506 end
507 end
508 end
509 return default or false
510end
511
512local function utfmathstretch(chr,default)
513 local cd = somechar[chr]
514 return cd and cd.mathstretch or default or ""
515end
516
517local function utfmathcommand(chr,default,asked1,asked2)
518 local cd = somechar[chr]
519 if not cd then
520 return default or ""
521 end
522 if asked1 then
523 local mn = cd.mathname
524 local mc = cd.mathclass
525 if mn and mc and (mc == asked1 or mc == asked2) then
526 return mn
527 end
528 local ms = cd.mathspec
529 if not ms then
530 local mp = cd.mathparent
531 if mp then
532 ms = chardata[mp].mathspec
533 end
534 end
535 if ms then
536 for i=1,#ms do
537 local msi = ms[i]
538 local mn = msi.name
539 if mn then
540 local mc = msi.class
541 if mc == asked1 or mc == asked2 then
542 return mn
543 end
544 end
545 end
546 end
547 else
548 local mn = cd.mathname
549 if mn then
550 return mn
551 end
552 local ms = cd.mathspec
553 if ms then
554 for i=1,#ms do
555 local msi = ms[i]
556 local mn = msi.name
557 if mn then
558 return mn
559 end
560 end
561 end
562 end
563 return default or ""
564end
565
566local function utfmathfiller(chr, default)
567 local cd = somechar[chr]
568 local cmd = cd and cd.mathfiller
569 return cmd or default or ""
570end
571
572mathematics.utfmathclass = utfmathclass
573mathematics.utfmathstretch = utfmathstretch
574mathematics.utfmathcommand = utfmathcommand
575mathematics.utfmathfiller = utfmathfiller
576mathematics.utfmathaccent = utfmathaccent
577
578
579
580implement {
581 name = "utfmathclass",
582 actions = { utfmathclass, context },
583 arguments = "string"
584}
585
586implement {
587 name = "utfmathstretch",
588 actions = { utfmathstretch, context },
589 arguments = "string"
590}
591
592implement {
593 name = "utfmathcommand",
594 actions = { utfmathcommand, context },
595 arguments = "string"
596}
597
598implement {
599 name = "utfmathfiller",
600 actions = { utfmathfiller, context },
601 arguments = "string"
602}
603
604implement {
605 name = "utfmathcommandabove",
606 actions = { utfmathcommand, context },
607 arguments = { "string", false, "'topaccent'","'over'" }
608}
609
610implement {
611 name = "utfmathcommandbelow",
612 actions = { utfmathcommand, context },
613 arguments = { "string", false, "'botaccent'","'under'" }
614}
615
616implement {
617 name = "utfmathcommandfiller",
618 actions = { utfmathfiller, context },
619 arguments = "string"
620}
621
622
623
624implement {
625 name = "doifelseutfmathabove",
626 actions = { utfmathaccent, ctx_doifelsesomething },
627 arguments = { "string", false, "'topaccent'", "'over'" }
628}
629
630implement {
631 name = "doifelseutfmathbelow",
632 actions = { utfmathaccent, ctx_doifelsesomething },
633 arguments = { "string", false, "'botaccent'", "'under'" }
634}
635
636implement {
637 name = "doifelseutfmathaccent",
638 actions = { utfmathaccent, ctx_doifelsesomething },
639 arguments = "string",
640}
641
642implement {
643 name = "doifelseutfmathfiller",
644 actions = { utfmathfiller, ctx_doifelsesomething },
645 arguments = "string",
646}
647
648implement {
649 name = "doifelseutfmathlimop",
650 actions = { utfmathlimop, ctx_doifelsesomething },
651 arguments = "string",
652}
653
654
655
656
657
658
659
660
661function mathematics.big(tfmdata,unicode,n,method)
662 local t = tfmdata.characters
663 local c = t[unicode]
664 if c and n > 0 then
665 local vv = c.vert_variants or c.next and t[c.next].vert_variants
666 if vv then
667 local vvn = vv[n]
668 return vvn and vvn.glyph or vv[#vv].glyph or unicode
669 elseif method == 1 or method == 2 then
670 if method == 2 then
671 n = n * 2
672 end
673 local next = c.next
674 while next do
675 if n <= 1 then
676 return next
677 else
678 n = n - 1
679 local tn = t[next].next
680 if tn then
681 next = tn
682 else
683 return next
684 end
685 end
686 end
687 elseif method >= 3 then
688 local size = 1.33^n
689 if method == 4 then
690 size = tfmdata.parameters.size * size
691 else
692 size = (c.height + c.depth) * size
693 end
694 local next = c.next
695 while next do
696 local cn = t[next]
697 if (cn.height + cn.depth) >= size then
698 return next
699 else
700 local tn = cn.next
701 if tn then
702 next = tn
703 else
704 return next
705 end
706 end
707 end
708 end
709 end
710 return unicode
711end
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752local categories = { }
753mathematics.categories = categories
754
755local a_mathcategory = attributes.private("mathcategory")
756
757local functions = storage.allocate()
758local noffunctions = 1000
759
760categories.functions = functions
761
762implement {
763 name = "tagmfunctiontxt",
764 arguments = { "string", "conditional" },
765 actions = function(tag,apply)
766 local delta = apply and 1000 or 0
767 texsetattribute(a_mathcategory,1000 + delta)
768 end
769}
770
771implement {
772 name = "tagmfunctionlab",
773 arguments = { "string", "conditional" },
774 actions = function(tag,apply)
775 local delta = apply and 1000 or 0
776 local n = functions[tag]
777 if not n then
778 noffunctions = noffunctions + 1
779 functions[noffunctions] = tag
780 functions[tag] = noffunctions
781 texsetattribute(a_mathcategory,noffunctions + delta)
782 else
783 texsetattribute(a_mathcategory,n + delta)
784 end
785 end
786}
787
788
789
790local list
791
792function mathematics.resetattributes()
793 if not list then
794 list = { }
795 for k, v in next, attributes.numbers do
796 if find(k,"^math") then
797 list[#list+1] = v
798 end
799 end
800 end
801 for i=1,#list do
802 texsetattribute(list[i],unsetvalue)
803 end
804end
805
806implement {
807 name = "resetmathattributes",
808 actions = mathematics.resetattributes
809}
810
811
812
813interfaces.implement {
814 name = "enableasciimode",
815 onlyonce = true,
816 actions = resolvers.macros.enablecomment,
817}
818 |