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