1if not modules then modules = { } end modules ['x-asciimath'] = {
2 version = 1.001,
3 comment = "companion to x-asciimath.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
18
19
20
21local trace_mapping = false if trackers then trackers.register("modules.asciimath.mapping", function(v) trace_mapping = v end) end
22local trace_details = false if trackers then trackers.register("modules.asciimath.details", function(v) trace_details = v end) end
23local trace_digits = false if trackers then trackers.register("modules.asciimath.digits", function(v) trace_digits = v end) end
24
25local report_asciimath = logs.reporter("mathematics","asciimath")
26
27local asciimath = asciimath or { }
28local moduledata = moduledata or { }
29moduledata.asciimath = asciimath
30
31if not characters then
32 require("char-def")
33 require("char-ini")
34 require("char-ent")
35end
36
37local next, type = next, type
38local concat, insert, remove = table.concat, table.insert, table.remove
39local rep, gmatch, gsub, find = string.rep, string.gmatch, string.gsub, string.find
40local utfchar, utfbyte = utf.char, utf.byte
41
42local lpegmatch, patterns = lpeg.match, lpeg.patterns
43local S, P, R, C, V, Cc, Ct, Cs, Carg = lpeg.S, lpeg.P, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Ct, lpeg.Cs, lpeg.Carg
44
45local sortedhash = table.sortedhash
46local sortedkeys = table.sortedkeys
47local formatters = string.formatters
48
49local entities = characters.entities or { }
50
51local xmltext = xml.text
52local xmlpure = xml.pure
53local xmlinclusion = xml.inclusion
54local xmlcollected = xml.collected
55
56local lxmlgetid = lxml.getid
57
58
59
60local s_lparent = "\\left\\lparent"
61local s_lbrace = "\\left\\lbrace"
62local s_lbracket = "\\left\\lbracket"
63local s_langle = "\\left\\langle"
64local s_lfloor = "\\left\\lfloor"
65local s_lceil = "\\left\\lceil"
66local s_left = "\\left."
67
68local s_rparent = "\\right\\rparent"
69local s_rbrace = "\\right\\rbrace"
70local s_rbracket = "\\right\\rbracket"
71local s_rangle = "\\right\\rangle"
72local s_rfloor = "\\right\\rfloor"
73local s_rceil = "\\right\\rceil"
74local s_right = "\\right."
75
76local s_mslash = "\\middle/"
77
78local s_lbar = "\\left\\|"
79local s_mbar = "\\middle\\|"
80local s_rbar = "\\right\\|"
81
82local s_lnothing = "\\left ."
83local s_rnothing = "\\right ."
84
85local reserved = {
86
87 ["prod"] = { false, "\\prod" },
88 ["sinh"] = { false, "\\sinh" },
89 ["cosh"] = { false, "\\cosh" },
90 ["tanh"] = { false, "\\tanh" },
91 ["sum"] = { false, "\\sum" },
92 ["int"] = { false, "\\int" },
93 ["sin"] = { false, "\\sin" },
94 ["cos"] = { false, "\\cos" },
95 ["tan"] = { false, "\\tan" },
96 ["csc"] = { false, "\\csc" },
97 ["sec"] = { false, "\\sec" },
98 ["cot"] = { false, "\\cot" },
99 ["log"] = { false, "\\log" },
100 ["det"] = { false, "\\det" },
101 ["lim"] = { false, "\\lim" },
102 ["mod"] = { false, "\\mod" },
103 ["gcd"] = { false, "\\gcd" },
104 ["min"] = { false, "\\min" },
105 ["max"] = { false, "\\max" },
106 ["ln"] = { false, "\\ln" },
107
108
109
110
111
112 ["arctan"] = { false, "\\arctan" },
113 ["arccos"] = { false, "\\arccos" },
114 ["arcsin"] = { false, "\\arcsin" },
115
116 ["arctanh"] = { false, "\\arctanh" },
117 ["arccosh"] = { false, "\\arccosh" },
118 ["arcsinh"] = { false, "\\arcsinh" },
119
120 ["and"] = { false, "\\text{and}" },
121 ["or"] = { false, "\\text{or}" },
122 ["if"] = { false, "\\text{if}" },
123
124 ["sqrt"] = { false, "\\asciimathsqrt", "unary" },
125 ["root"] = { false, "\\asciimathroot", "binary" },
126
127 ["frac"] = { false, "\\frac", "binary" },
128 ["stackrel"] = { false, "\\asciimathstackrel", "binary" },
129 ["hat"] = { false, "\\widehat", "unary" },
130 ["bar"] = { false, "\\overline", "unary" },
131 ["overbar"] = { false, "\\overline", "unary" },
132 ["overline"] = { false, "\\overline", "unary" },
133 ["underline"] = { false, "\\underline", "unary" },
134 ["overbrace"] = { false, "\\overbrace", "unary" },
135 ["underbrace"]= { false, "\\underbrace", "unary" },
136 ["overset"] = { false, "\\overset", "unary" },
137 ["underset"] = { false, "\\underset", "unary" },
138 ["obrace"] = { false, "\\overbrace", "unary" },
139 ["ubrace"] = { false, "\\underbrace", "unary" },
140 ["ul"] = { false, "\\underline", "unary" },
141 ["vec"] = { false, "\\overrightarrow", "unary" },
142 ["dot"] = { false, "\\dot", "unary" },
143 ["ddot"] = { false, "\\ddot", "unary" },
144
145
146
147 ["+"] = { true, "+" },
148 ["-"] = { true, "-" },
149 ["*"] = { true, "⋅" },
150 ["**"] = { true, "⋆" },
151 ["////"] = { true, "⁄⁄" },
152 ["//"] = { true, "⁄" },
153 ["\\"] = { true, "\\" },
154 ["xx"] = { true, "×" },
155 ["times"] = { true, "×" },
156 ["-:"] = { true, "÷" },
157 ["@"] = { true, "∘" },
158 ["circ"] = { true, "∘" },
159 ["o+"] = { true, "⊕" },
160 ["ox"] = { true, "⊗" },
161 ["o."] = { true, "⊙" },
162 ["^^"] = { true, "∧" },
163 ["vv"] = { true, "∨" },
164 ["nn"] = { true, "∩" },
165 ["uu"] = { true, "∪" },
166
167
168
169 ["^^^"] = { true, "⋀" },
170 ["vvv"] = { true, "⋁" },
171 ["nnn"] = { true, "⋂" },
172 ["uuu"] = { true, "⋃" },
173 ["int"] = { true, "∫" },
174 ["oint"] = { true, "∮" },
175
176
177
178 ["("] = { true, "(" },
179 [")"] = { true, ")" },
180 ["["] = { true, "[" },
181 ["]"] = { true, "]" },
182 ["{"] = { true, "{" },
183 ["}"] = { true, "}" },
184
185
186
187 ["="] = { true, "=" },
188 ["eq"] = { true, "=" },
189 ["!="] = { true, "≠" },
190 ["ne"] = { true, "≠" },
191 ["neq"] = { true, "≠" },
192 ["<"] = { true, "<" },
193 ["lt"] = { true, "<" },
194 [">"] = { true, ">" },
195 ["gt"] = { true, ">" },
196 ["<="] = { true, "≤" },
197 ["le"] = { true, "≤" },
198 ["leq"] = { true, "≤" },
199 [">="] = { true, "≥" },
200 ["ge"] = { true, "≥" },
201 ["geq"] = { true, "≥" },
202 ["-<"] = { true, "≺" },
203 [">-"] = { true, "≻" },
204 ["in"] = { true, "∈" },
205 ["!in"] = { true, "∉" },
206 ["sub"] = { true, "⊂" },
207 ["sup"] = { true, "⊃" },
208 ["sube"] = { true, "⊆" },
209 ["supe"] = { true, "⊇" },
210 ["-="] = { true, "≡" },
211 ["~="] = { true, "≅" },
212 ["~~"] = { true, "≈" },
213 ["prop"] = { true, "∝" },
214
215
216
217 ["rarr"] = { true, "→" },
218 ["->"] = { true, "→" },
219 ["larr"] = { true, "←" },
220 ["harr"] = { true, "↔" },
221 ["uarr"] = { true, "↑" },
222 ["darr"] = { true, "↓" },
223 ["rArr"] = { true, "⇒" },
224 ["lArr"] = { true, "⇐" },
225 ["hArr"] = { true, "⇔" },
226 ["|->"] = { true, "↦" },
227
228
229
230 ["not"] = { true, "¬" },
231 ["=>"] = { true, "⇒" },
232 ["iff"] = { true, "⇔" },
233 ["AA"] = { true, "∀" },
234 ["EE"] = { true, "∃" },
235 ["_|_"] = { true, "⊥" },
236 ["TT"] = { true, "⊤" },
237 ["|--"] = { true, "⊢" },
238 ["|=="] = { true, "⊨" },
239
240
241
242 ["del"] = { true, "∂" },
243 ["grad"] = { true, "∇" },
244 ["+-"] = { true, "±" },
245 ["O/"] = { true, "∅" },
246 ["oo"] = { true, "∞" },
247 ["aleph"] = { true, "ℵ" },
248 ["angle"] = { true, "∠" },
249 ["/_"] = { true, "∠" },
250 [":."] = { true, "∴" },
251 ["..."] = { true, "..." },
252 ["ldots"] = { true, "..." },
253 ["cdots"] = { true, "⋯" },
254 ["vdots"] = { true, "⋮" },
255 ["ddots"] = { true, "⋱" },
256 ["diamond"] = { true, "⋄" },
257 ["square"] = { true, "□" },
258 ["|__"] = { true, "⌊" },
259 ["__|"] = { true, "⌋" },
260 ["|~"] = { true, "⌈" },
261 ["~|"] = { true, "⌉" },
262
263
264
265 ["_="] = { true, "≡" },
266
267
268
269 ["prime"] = { true, "′" },
270 ["'"] = { true, "′" },
271 ["''"] = { true, "″" },
272 ["'''"] = { true, "‴" },
273
274
275
276 ["%"] = { false, "\\mathpercent" },
277 ["&"] = { false, "\\mathampersand" },
278 ["#"] = { false, "\\mathhash" },
279 ["$"] = { false, "\\mathdollar" },
280
281
282
283 ["CC"] = { true, "ℂ" },
284 ["NN"] = { true, "ℕ" },
285 ["QQ"] = { true, "ℚ" },
286 ["RR"] = { true, "ℝ" },
287 ["ZZ"] = { true, "ℤ" },
288
289
290
291 ["alpha"] = { true, "α" },
292 ["beta"] = { true, "β" },
293 ["gamma"] = { true, "γ" },
294 ["delta"] = { true, "δ" },
295 ["epsilon"] = { true, "ε" },
296 ["varepsilon"] = { true, "ɛ" },
297 ["zeta"] = { true, "ζ" },
298 ["eta"] = { true, "η" },
299 ["theta"] = { true, "θ" },
300 ["vartheta"] = { true, "ϑ" },
301 ["iota"] = { true, "ι" },
302 ["kappa"] = { true, "κ" },
303 ["lambda"] = { true, "λ" },
304 ["mu"] = { true, "μ" },
305 ["nu"] = { true, "ν" },
306 ["xi"] = { true, "ξ" },
307 ["pi"] = { true, "π" },
308 ["rho"] = { true, "ρ" },
309 ["sigma"] = { true, "σ" },
310 ["tau"] = { true, "τ" },
311 ["upsilon"] = { true, "υ" },
312 ["phi"] = { true, "ϕ" },
313 ["varphi"] = { true, "φ" },
314 ["chi"] = { true, "χ" },
315 ["psi"] = { true, "ψ" },
316 ["omega"] = { true, "ω" },
317
318
319
320 ["Gamma"] = { true, "Γ" },
321 ["Delta"] = { true, "Δ" },
322 ["Theta"] = { true, "Θ" },
323 ["Lambda"] = { true, "Λ" },
324 ["Xi"] = { true, "Ξ" },
325 ["Pi"] = { true, "Π" },
326 ["Sigma"] = { true, "Σ" },
327 ["Phi"] = { true, "Φ" },
328 ["Psi"] = { true, "Ψ" },
329 ["Omega"] = { true, "Ω" },
330
331
332
333 ["bbb a"] = { true, "𝕒" },
334 ["bbb b"] = { true, "𝕓" },
335 ["bbb c"] = { true, "𝕔" },
336 ["bbb d"] = { true, "𝕕" },
337 ["bbb e"] = { true, "𝕖" },
338 ["bbb f"] = { true, "𝕗" },
339 ["bbb g"] = { true, "𝕘" },
340 ["bbb h"] = { true, "𝕙" },
341 ["bbb i"] = { true, "𝕚" },
342 ["bbb j"] = { true, "𝕛" },
343 ["bbb k"] = { true, "𝕜" },
344 ["bbb l"] = { true, "𝕝" },
345 ["bbb m"] = { true, "𝕞" },
346 ["bbb n"] = { true, "𝕟" },
347 ["bbb o"] = { true, "𝕠" },
348 ["bbb p"] = { true, "𝕡" },
349 ["bbb q"] = { true, "𝕢" },
350 ["bbb r"] = { true, "𝕣" },
351 ["bbb s"] = { true, "𝕤" },
352 ["bbb t"] = { true, "𝕥" },
353 ["bbb u"] = { true, "𝕦" },
354 ["bbb v"] = { true, "𝕧" },
355 ["bbb w"] = { true, "𝕨" },
356 ["bbb x"] = { true, "𝕩" },
357 ["bbb y"] = { true, "𝕪" },
358 ["bbb z"] = { true, "𝕫" },
359
360 ["bbb A"] = { true, "𝔸" },
361 ["bbb B"] = { true, "𝔹" },
362 ["bbb C"] = { true, "ℂ" },
363 ["bbb D"] = { true, "𝔻" },
364 ["bbb E"] = { true, "𝔼" },
365 ["bbb F"] = { true, "𝔽" },
366 ["bbb G"] = { true, "𝔾" },
367 ["bbb H"] = { true, "ℍ" },
368 ["bbb I"] = { true, "𝕀" },
369 ["bbb J"] = { true, "𝕁" },
370 ["bbb K"] = { true, "𝕂" },
371 ["bbb L"] = { true, "𝕃" },
372 ["bbb M"] = { true, "𝕄" },
373 ["bbb N"] = { true, "ℕ" },
374 ["bbb O"] = { true, "𝕆" },
375 ["bbb P"] = { true, "ℙ" },
376 ["bbb Q"] = { true, "ℚ" },
377 ["bbb R"] = { true, "ℝ" },
378 ["bbb S"] = { true, "𝕊" },
379 ["bbb T"] = { true, "𝕋" },
380 ["bbb U"] = { true, "𝕌" },
381 ["bbb V"] = { true, "𝕍" },
382 ["bbb W"] = { true, "𝕎" },
383 ["bbb X"] = { true, "𝕏" },
384 ["bbb Y"] = { true, "𝕐" },
385 ["bbb Z"] = { true, "ℤ" },
386
387
388
389 ["fr a"] = { true, "𝔞" },
390 ["fr b"] = { true, "𝔟" },
391 ["fr c"] = { true, "𝔠" },
392 ["fr d"] = { true, "𝔡" },
393 ["fr e"] = { true, "𝔢" },
394 ["fr f"] = { true, "𝔣" },
395 ["fr g"] = { true, "𝔤" },
396 ["fr h"] = { true, "𝔥" },
397 ["fr i"] = { true, "𝔦" },
398 ["fr j"] = { true, "𝔧" },
399 ["fr k"] = { true, "𝔨" },
400 ["fr l"] = { true, "𝔩" },
401 ["fr m"] = { true, "𝔪" },
402 ["fr n"] = { true, "𝔫" },
403 ["fr o"] = { true, "𝔬" },
404 ["fr p"] = { true, "𝔭" },
405 ["fr q"] = { true, "𝔮" },
406 ["fr r"] = { true, "𝔯" },
407 ["fr s"] = { true, "𝔰" },
408 ["fr t"] = { true, "𝔱" },
409 ["fr u"] = { true, "𝔲" },
410 ["fr v"] = { true, "𝔳" },
411 ["fr w"] = { true, "𝔴" },
412 ["fr x"] = { true, "𝔵" },
413 ["fr y"] = { true, "𝔶" },
414 ["fr z"] = { true, "𝔷" },
415
416 ["fr A"] = { true, "𝔄" },
417 ["fr B"] = { true, "𝔅" },
418 ["fr C"] = { true, "ℭ" },
419 ["fr D"] = { true, "𝔇" },
420 ["fr E"] = { true, "𝔈" },
421 ["fr F"] = { true, "𝔉" },
422 ["fr G"] = { true, "𝔊" },
423 ["fr H"] = { true, "ℌ" },
424 ["fr I"] = { true, "ℑ" },
425 ["fr J"] = { true, "𝔍" },
426 ["fr K"] = { true, "𝔎" },
427 ["fr L"] = { true, "𝔏" },
428 ["fr M"] = { true, "𝔐" },
429 ["fr N"] = { true, "𝔑" },
430 ["fr O"] = { true, "𝔒" },
431 ["fr P"] = { true, "𝔓" },
432 ["fr Q"] = { true, "𝔔" },
433 ["fr R"] = { true, "ℜ" },
434 ["fr S"] = { true, "𝔖" },
435 ["fr T"] = { true, "𝔗" },
436 ["fr U"] = { true, "𝔘" },
437 ["fr V"] = { true, "𝔙" },
438 ["fr W"] = { true, "𝔚" },
439 ["fr X"] = { true, "𝔛" },
440 ["fr Y"] = { true, "𝔜" },
441 ["fr Z"] = { true, "ℨ" },
442
443
444
445 ["cc a"] = { true, "𝒶" },
446 ["cc b"] = { true, "𝒷" },
447 ["cc c"] = { true, "𝒸" },
448 ["cc d"] = { true, "𝒹" },
449 ["cc e"] = { true, "ℯ" },
450 ["cc f"] = { true, "𝒻" },
451 ["cc g"] = { true, "ℊ" },
452 ["cc h"] = { true, "𝒽" },
453 ["cc i"] = { true, "𝒾" },
454 ["cc j"] = { true, "𝒿" },
455 ["cc k"] = { true, "𝓀" },
456 ["cc l"] = { true, "𝓁" },
457 ["cc m"] = { true, "𝓂" },
458 ["cc n"] = { true, "𝓃" },
459 ["cc o"] = { true, "ℴ" },
460 ["cc p"] = { true, "𝓅" },
461 ["cc q"] = { true, "𝓆" },
462 ["cc r"] = { true, "𝓇" },
463 ["cc s"] = { true, "𝓈" },
464 ["cc t"] = { true, "𝓉" },
465 ["cc u"] = { true, "𝓊" },
466 ["cc v"] = { true, "𝓋" },
467 ["cc w"] = { true, "𝓌" },
468 ["cc x"] = { true, "𝓍" },
469 ["cc y"] = { true, "𝓎" },
470 ["cc z"] = { true, "𝓏" },
471
472 ["cc A"] = { true, "𝒜" },
473 ["cc B"] = { true, "ℬ" },
474 ["cc C"] = { true, "𝒞" },
475 ["cc D"] = { true, "𝒟" },
476 ["cc E"] = { true, "ℰ" },
477 ["cc F"] = { true, "ℱ" },
478 ["cc G"] = { true, "𝒢" },
479 ["cc H"] = { true, "ℋ" },
480 ["cc I"] = { true, "ℐ" },
481 ["cc J"] = { true, "𝒥" },
482 ["cc K"] = { true, "𝒦" },
483 ["cc L"] = { true, "ℒ" },
484 ["cc M"] = { true, "ℳ" },
485 ["cc N"] = { true, "𝒩" },
486 ["cc O"] = { true, "𝒪" },
487 ["cc P"] = { true, "𝒫" },
488 ["cc Q"] = { true, "𝒬" },
489 ["cc R"] = { true, "ℛ" },
490 ["cc S"] = { true, "𝒮" },
491 ["cc T"] = { true, "𝒯" },
492 ["cc U"] = { true, "𝒰" },
493 ["cc V"] = { true, "𝒱" },
494 ["cc W"] = { true, "𝒲" },
495 ["cc X"] = { true, "𝒳" },
496 ["cc Y"] = { true, "𝒴" },
497 ["cc Z"] = { true, "𝒵" },
498
499
500
501 ["bb a"] = { true, "𝒂" },
502 ["bb b"] = { true, "𝒃" },
503 ["bb c"] = { true, "𝒄" },
504 ["bb d"] = { true, "𝒅" },
505 ["bb e"] = { true, "𝒆" },
506 ["bb f"] = { true, "𝒇" },
507 ["bb g"] = { true, "𝒈" },
508 ["bb h"] = { true, "𝒉" },
509 ["bb i"] = { true, "𝒊" },
510 ["bb j"] = { true, "𝒋" },
511 ["bb k"] = { true, "𝒌" },
512 ["bb l"] = { true, "𝒍" },
513 ["bb m"] = { true, "𝒎" },
514 ["bb n"] = { true, "𝒏" },
515 ["bb o"] = { true, "𝒐" },
516 ["bb p"] = { true, "𝒑" },
517 ["bb q"] = { true, "𝒒" },
518 ["bb r"] = { true, "𝒓" },
519 ["bb s"] = { true, "𝒔" },
520 ["bb t"] = { true, "𝒕" },
521 ["bb u"] = { true, "𝒖" },
522 ["bb v"] = { true, "𝒗" },
523 ["bb w"] = { true, "𝒘" },
524 ["bb x"] = { true, "𝒙" },
525 ["bb y"] = { true, "𝒚" },
526 ["bb z"] = { true, "𝒛" },
527
528 ["bb A"] = { true, "𝑨" },
529 ["bb B"] = { true, "𝑩" },
530 ["bb C"] = { true, "𝑪" },
531 ["bb D"] = { true, "𝑫" },
532 ["bb E"] = { true, "𝑬" },
533 ["bb F"] = { true, "𝑭" },
534 ["bb G"] = { true, "𝑮" },
535 ["bb H"] = { true, "𝑯" },
536 ["bb I"] = { true, "𝑰" },
537 ["bb J"] = { true, "𝑱" },
538 ["bb K"] = { true, "𝑲" },
539 ["bb L"] = { true, "𝑳" },
540 ["bb M"] = { true, "𝑴" },
541 ["bb N"] = { true, "𝑵" },
542 ["bb O"] = { true, "𝑶" },
543 ["bb P"] = { true, "𝑷" },
544 ["bb Q"] = { true, "𝑸" },
545 ["bb R"] = { true, "𝑹" },
546 ["bb S"] = { true, "𝑺" },
547 ["bb T"] = { true, "𝑻" },
548 ["bb U"] = { true, "𝑼" },
549 ["bb V"] = { true, "𝑽" },
550 ["bb W"] = { true, "𝑾" },
551 ["bb X"] = { true, "𝑿" },
552 ["bb Y"] = { true, "𝒀" },
553 ["bb Z"] = { true, "𝒁" },
554
555
556
557 ["sf a"] = { true, "𝖺" },
558 ["sf b"] = { true, "𝖻" },
559 ["sf c"] = { true, "𝖼" },
560 ["sf d"] = { true, "𝖽" },
561 ["sf e"] = { true, "𝖾" },
562 ["sf f"] = { true, "𝖿" },
563 ["sf g"] = { true, "𝗀" },
564 ["sf h"] = { true, "𝗁" },
565 ["sf i"] = { true, "𝗂" },
566 ["sf j"] = { true, "𝗃" },
567 ["sf k"] = { true, "𝗄" },
568 ["sf l"] = { true, "𝗅" },
569 ["sf m"] = { true, "𝗆" },
570 ["sf n"] = { true, "𝗇" },
571 ["sf o"] = { true, "𝗈" },
572 ["sf p"] = { true, "𝗉" },
573 ["sf q"] = { true, "𝗊" },
574 ["sf r"] = { true, "𝗋" },
575 ["sf s"] = { true, "𝗌" },
576 ["sf t"] = { true, "𝗍" },
577 ["sf u"] = { true, "𝗎" },
578 ["sf v"] = { true, "𝗏" },
579 ["sf w"] = { true, "𝗐" },
580 ["sf x"] = { true, "𝗑" },
581 ["sf y"] = { true, "𝗒" },
582 ["sf z"] = { true, "𝗓" },
583
584 ["sf A"] = { true, "𝖠" },
585 ["sf B"] = { true, "𝖡" },
586 ["sf C"] = { true, "𝖢" },
587 ["sf D"] = { true, "𝖣" },
588 ["sf E"] = { true, "𝖤" },
589 ["sf F"] = { true, "𝖥" },
590 ["sf G"] = { true, "𝖦" },
591 ["sf H"] = { true, "𝖧" },
592 ["sf I"] = { true, "𝖨" },
593 ["sf J"] = { true, "𝖩" },
594 ["sf K"] = { true, "𝖪" },
595 ["sf L"] = { true, "𝖫" },
596 ["sf M"] = { true, "𝖬" },
597 ["sf N"] = { true, "𝖭" },
598 ["sf O"] = { true, "𝖮" },
599 ["sf P"] = { true, "𝖯" },
600 ["sf Q"] = { true, "𝖰" },
601 ["sf R"] = { true, "𝖱" },
602 ["sf S"] = { true, "𝖲" },
603 ["sf T"] = { true, "𝖳" },
604 ["sf U"] = { true, "𝖴" },
605 ["sf V"] = { true, "𝖵" },
606 ["sf W"] = { true, "𝖶" },
607 ["sf X"] = { true, "𝖷" },
608 ["sf Y"] = { true, "𝖸" },
609 ["sf Z"] = { true, "𝖹" },
610
611
612
613 ["tt a"] = { true, "𝚊" },
614 ["tt b"] = { true, "𝚋" },
615 ["tt c"] = { true, "𝚌" },
616 ["tt d"] = { true, "𝚍" },
617 ["tt e"] = { true, "𝚎" },
618 ["tt f"] = { true, "𝚏" },
619 ["tt g"] = { true, "𝚐" },
620 ["tt h"] = { true, "𝚑" },
621 ["tt i"] = { true, "𝚒" },
622 ["tt j"] = { true, "𝚓" },
623 ["tt k"] = { true, "𝚔" },
624 ["tt l"] = { true, "𝚕" },
625 ["tt m"] = { true, "𝚖" },
626 ["tt n"] = { true, "𝚗" },
627 ["tt o"] = { true, "𝚘" },
628 ["tt p"] = { true, "𝚙" },
629 ["tt q"] = { true, "𝚚" },
630 ["tt r"] = { true, "𝚛" },
631 ["tt s"] = { true, "𝚜" },
632 ["tt t"] = { true, "𝚝" },
633 ["tt u"] = { true, "𝚞" },
634 ["tt v"] = { true, "𝚟" },
635 ["tt w"] = { true, "𝚠" },
636 ["tt x"] = { true, "𝚡" },
637 ["tt y"] = { true, "𝚢" },
638 ["tt z"] = { true, "𝚣" },
639
640 ["tt A"] = { true, "𝙰" },
641 ["tt B"] = { true, "𝙱" },
642 ["tt C"] = { true, "𝙲" },
643 ["tt D"] = { true, "𝙳" },
644 ["tt E"] = { true, "𝙴" },
645 ["tt F"] = { true, "𝙵" },
646 ["tt G"] = { true, "𝙶" },
647 ["tt H"] = { true, "𝙷" },
648 ["tt I"] = { true, "𝙸" },
649 ["tt J"] = { true, "𝙹" },
650 ["tt K"] = { true, "𝙺" },
651 ["tt L"] = { true, "𝙻" },
652 ["tt M"] = { true, "𝙼" },
653 ["tt N"] = { true, "𝙽" },
654 ["tt O"] = { true, "𝙾" },
655 ["tt P"] = { true, "𝙿" },
656 ["tt Q"] = { true, "𝚀" },
657 ["tt R"] = { true, "𝚁" },
658 ["tt S"] = { true, "𝚂" },
659 ["tt T"] = { true, "𝚃" },
660 ["tt U"] = { true, "𝚄" },
661 ["tt V"] = { true, "𝚅" },
662 ["tt W"] = { true, "𝚆" },
663 ["tt X"] = { true, "𝚇" },
664 ["tt Y"] = { true, "𝚈" },
665 ["tt Z"] = { true, "𝚉" },
666
667
668
669 ["dx"] = { false, { "d", "x" } },
670 ["dy"] = { false, { "d", "y" } },
671 ["dz"] = { false, { "d", "z" } },
672
673
674
675 ["(:"] = { true, "(:" },
676 ["{:"] = { true, "{:" },
677 ["[:"] = { true, "[:" },
678 ["("] = { true, "(" },
679 ["["] = { true, "[" },
680 ["{"] = { true, "{" },
681 ["<<"] = { true, "⟨" },
682 ["|_"] = { true, "⌊" },
683 ["|~"] = { true, "⌈" },
684 ["⟨"] = { true, "⟨" },
685 ["〈"] = { true, "⟨" },
686 ["〈"] = { true, "⟨" },
687
688 [":)"] = { true, ":)" },
689 [":}"] = { true, ":}" },
690 [":]"] = { true, ":]" },
691 [")"] = { true, ")" },
692 ["]"] = { true, "]" },
693 ["}"] = { true, "}" },
694 [">>"] = { true, "⟩" },
695 ["~|"] = { true, "⌉" },
696 ["_|"] = { true, "⌋" },
697 ["⟩"] = { true, "⟩" },
698 ["〉"] = { true, "⟩" },
699 ["〉"] = { true, "⟩" },
700
701 ["lparent"] = { true, "(" },
702 ["lbracket"] = { true, "[" },
703 ["lbrace"] = { true, "{" },
704 ["langle"] = { true, "⟨" },
705 ["lfloor"] = { true, "⌊" },
706 ["lceil"] = { true, "⌈" },
707
708 ["rparent"] = { true, ")" },
709 ["rbracket"] = { true, "]" },
710 ["rbrace"] = { true, "}" },
711 ["rangle"] = { true, "⟩" },
712 ["rfloor"] = { true, "⌋" },
713 ["rceil"] = { true, "⌉" },
714
715
716
717
718
719
720
721 [">"] = { true, ">" },
722 ["<"] = { true, "<" },
723
724
725
726
727
728 ["dd"] = { false, "{\\tf d}" },
729 ["ee"] = { false, "{\\tf e}" },
730 ["xxx"] = { true, utfchar(0x2063) },
731
732}
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751local extras = asciimath.extras
752if extras then
753 for k, v in next, extras do
754 if not reserved[k] then
755 reserved[k] = v
756 end
757 end
758end
759
760
761
762
763for k, v in next, characters.data do
764 local name = v.mathname
765 if name and not reserved[name] then
766 local char = { true, utfchar(k) }
767 reserved[ name] = char
768
769 end
770
771
772
773
774
775
776
777
778
779end
780
781reserved.P = nil
782reserved.S = nil
783
784
785local isbinary = {
786 ["\\frac"] = true,
787 ["\\root"] = true,
788 ["\\asciimathroot"] = true,
789 ["\\asciimathstackrel"] = true,
790 ["\\overset"] = true,
791 ["\\underset"] = true,
792}
793
794local isunary = {
795 ["\\sqrt"] = true,
796 ["\\asciimathsqrt"] = true,
797 ["\\text"] = true,
798 ["\\mathoptext"] = true,
799 ["\\asciimathoptext"] = true,
800 ["\\hat"] = true,
801 ["\\widehat"] = true,
802 ["\\bar"] = true,
803 ["\\overbar"] = true,
804 ["\\overline"] = true,
805 ["\\underline"] = true,
806 ["\\vec"] = true,
807 ["\\overrightarrow"] = true,
808 ["\\dot"] = true,
809 ["\\ddot"] = true,
810
811 ["\\overbrace"] = true,
812 ["\\underbrace"] = true,
813 ["\\obrace"] = true,
814 ["\\ubrace"] = true,
815}
816
817local isfunny = {
818 ["\\sin"] = true,
819}
820
821local isinfix = {
822 ["^"] = true,
823 ["_"] = true,
824}
825
826local isstupid = {
827 ["\\prod"] = true,
828 ["\\sinh"] = true,
829 ["\\cosh"] = true,
830 ["\\tanh"] = true,
831 ["\\sum"] = true,
832 ["\\int"] = true,
833 ["\\sin"] = true,
834 ["\\cos"] = true,
835 ["\\tan"] = true,
836 ["\\csc"] = true,
837 ["\\sec"] = true,
838 ["\\cot"] = true,
839 ["\\log"] = true,
840 ["\\det"] = true,
841 ["\\lim"] = true,
842 ["\\mod"] = true,
843 ["\\gcd"] = true,
844 ["\\min"] = true,
845 ["\\max"] = true,
846 ["\\ln"] = true,
847
848
849
850
851
852 ["\\arctan"] = true,
853 ["\\arccos"] = true,
854 ["\\arcsin"] = true,
855
856 ["\\arctanh"] = true,
857 ["\\arccosh"] = true,
858 ["\\arcsinh"] = true,
859
860 ["f"] = true,
861 ["g"] = true,
862}
863
864local isleft = {
865 [s_lparent] = true,
866 [s_lbrace] = true,
867 [s_lbracket] = true,
868 [s_langle] = true,
869 [s_lfloor] = true,
870 [s_lceil] = true,
871 [s_left] = true,
872}
873
874local isright = {
875 [s_rparent] = true,
876 [s_rbrace] = true,
877 [s_rbracket] = true,
878 [s_rangle] = true,
879 [s_rfloor] = true,
880 [s_rceil] = true,
881 [s_right] = true,
882}
883
884local issimplified = {
885}
886
887
888
889
890
891
892local d_one = R("09")
893local d_two = d_one * d_one
894local d_three = d_two * d_one
895local d_four = d_three * d_one
896local d_split = P(-1) + Carg(2) * (S(".") /"")
897
898local d_spaced = (Carg(1) * d_three)^1
899
900local digitized_1 = Cs ( (
901 d_three * d_spaced * d_split +
902 d_two * d_spaced * d_split +
903 d_one * d_spaced * d_split +
904 P(1)
905 )^1 )
906
907local p_fourbefore = d_four * d_split
908local p_fourafter = d_four * P(-1)
909
910local p_beforecomma = d_three * d_spaced^0 * d_split
911 + d_two * d_spaced^0 * d_split
912 + d_one * d_spaced^0 * d_split
913 + d_one * d_split
914
915local p_aftercomma = p_fourafter
916 + d_three * d_spaced
917 + d_two * d_spaced
918 + d_one * d_spaced
919
920local digitized_2 = Cs (
921 p_fourbefore * (p_aftercomma^0) +
922 p_beforecomma * ((p_aftercomma + d_one^1)^0)
923 )
924
925local p_fourbefore = d_four * d_split
926local p_fourafter = d_four
927local d_spaced = (Carg(1) * (d_three + d_two + d_one))^1
928local p_aftercomma = p_fourafter * P(-1)
929 + d_three * d_spaced * P(1)^0
930 + d_one^1
931
932
933
934
935
936
937local digitized_3 = Cs((p_fourbefore + p_beforecomma) * p_aftercomma^0)
938
939local splitmethods = {
940 digitized_1,
941 digitized_2,
942 digitized_3,
943}
944
945local splitmethod = nil
946local symbolmethod = nil
947local digitseparator = utfchar(0x2008)
948local digitsymbol = "."
949
950local v_yes_digits = interfaces and interfaces.variables.yes or true
951
952function asciimath.setup(settings)
953 splitmethod = splitmethods[tonumber(settings.splitmethod) or 0]
954 if splitmethod then
955 digitsymbol = settings.symbol
956 if not digitsymbol or digitsymbol == "" then
957 digitsymbol = "."
958 end
959 local separator = settings.separator
960
961 if separator == true or separator == nil or separator == v_yes_digits then
962 digitseparator = utfchar(0x2008)
963 elseif type(separator) == "string" and separator ~= "" then
964 digitseparator = separator
965 else
966 splitmethod = nil
967 end
968 if digitsymbol ~= "." then
969 symbolmethod = lpeg.replacer(".",digitsymbol)
970 else
971 symbolmethod = nil
972 end
973 end
974end
975
976local collected_digits = { }
977local collected_filename = "asciimath-digits.lua"
978
979function numbermess(s)
980 if splitmethod then
981 local d = lpegmatch(splitmethod,s,1,digitseparator,digitsymbol)
982 if not d and symbolmethod then
983 d = lpegmatch(symbolmethod,s)
984 end
985 if d then
986 if trace_digits and s ~= d then
987 collected_digits[s] = d
988 end
989 return d
990 end
991 end
992 return s
993end
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024statistics.register("asciimath",function()
1025 if trace_digits then
1026 local n = table.count(collected_digits)
1027 if n > 0 then
1028 table.save(collected_filename,collected_digits)
1029 return string.format("%s digit conversions saved in %s",n,collected_filename)
1030 else
1031 os.remove(collected_filename)
1032 end
1033 end
1034end)
1035
1036local p_number_base = patterns.cpnumber or patterns.cnumber or patterns.number
1037local p_number = C(p_number_base)
1038
1039local p_spaces = patterns.whitespace
1040
1041local p_utf_base = patterns.utf8character
1042local p_utf = C(p_utf_base)
1043
1044
1045
1046
1047
1048
1049
1050
1051local p_onechar = p_utf_base * P(-1)
1052
1053
1054
1055local sign = P("-")^-1
1056local digits = R("09")^1
1057local integer = sign * digits
1058local real = digits * (S(".") * digits)^-1
1059local float = real * (P("E") * integer)^-1
1060
1061
1062
1063local p_number = float / numbermess
1064
1065local k_reserved = sortedkeys(reserved)
1066local k_commands = { }
1067local k_unicode = { }
1068
1069asciimath.keys = {
1070 reserved = k_reserved
1071}
1072
1073local k_reserved_different = { }
1074local k_reserved_words = { }
1075
1076for k, v in sortedhash(reserved) do
1077 local replacement = v[2]
1078 if v[1] then
1079 k_unicode[k] = replacement
1080 else
1081 k_unicode[k] = k
1082 if k ~= replacement then
1083 k_reserved_different[#k_reserved_different+1] = k
1084 end
1085 end
1086 if find(k,"^[a-zA-Z]+$") then
1087 k_unicode["\\"..k] = replacement
1088 else
1089 k_unicode["\\"..k] = k
1090 end
1091 if not find(k,"[^a-zA-Z]") then
1092 k_reserved_words[#k_reserved_words+1] = k
1093 end
1094 k_commands[k] = replacement
1095end
1096
1097local p_reserved =
1098 lpeg.utfchartabletopattern(k_reserved_different) / k_commands
1099
1100local p_unicode =
1101
1102 lpeg.utfchartabletopattern(k_unicode) / k_unicode
1103
1104local p_texescape = patterns.texescape
1105
1106local function texescaped(s)
1107 return lpegmatch(p_texescape,s) or s
1108end
1109
1110local p_text =
1111 P("text")
1112 * p_spaces^0
1113 * Cc("\\asciimathoptext")
1114 * (
1115 Cs( P("{") * ((1-P("}"))^0/texescaped) * P("}") )
1116 + Cs((P("(")/"{") * ((1-P(")"))^0/texescaped) * (P(")")/"}"))
1117 )
1118 + Cc("\\asciimathoptext") * Cs(Cc("{") * (C(patterns.undouble)/texescaped) * Cc("}"))
1119
1120local m_left = {
1121 ["(:"] = s_langle,
1122 ["{:"] = s_left,
1123 ["[:"] = s_left,
1124 ["("] = s_lparent,
1125 ["["] = s_lbracket,
1126 ["{"] = s_lbrace,
1127 ["⟨"] = s_langle,
1128 ["⌈"] = s_lceil,
1129 ["⌊"] = s_lfloor,
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143}
1144
1145local m_right = {
1146 [":)"] = s_rangle,
1147 [":}"] = s_right,
1148 [":]"] = s_right,
1149 [")"] = s_rparent,
1150 ["]"] = s_rbracket,
1151 ["}"] = s_rbrace,
1152 ["⟩"] = s_rangle,
1153 ["⌉"] = s_rceil,
1154 ["⌋"] = s_rfloor,
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168}
1169
1170local islimits = {
1171 ["\\sum"] = true,
1172
1173 ["\\prod"] = true,
1174
1175 ["\\lim"] = true,
1176}
1177
1178local p_left =
1179 lpeg.utfchartabletopattern(m_left) / m_left
1180local p_right =
1181 lpeg.utfchartabletopattern(m_right) / m_right
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195local p_special =
1196 P("|") * Cc("\\|")
1197 + P("\\") * (
1198 (
1199 P(" ") * (
1200 Cc("{}") * p_spaces^0 * C(S("^_"))
1201 + Cc("\\space")
1202 )
1203 )
1204 + P("\\") * Cc("\\backslash")
1205
1206 + C(R("az","AZ")^1)
1207 )
1208
1209
1210
1211local u_parser = Cs ( (
1212 patterns.doublequoted +
1213 P("text") * p_spaces^0 * P("(") * (1-P(")"))^0 * P(")") +
1214 P("\\frac") / "frac" +
1215 p_unicode +
1216 p_utf_base
1217)^0 )
1218
1219local a_parser = Ct { "tokenizer",
1220 tokenizer = (
1221 p_spaces
1222 + p_number
1223 + p_text
1224
1225
1226
1227 + Ct(p_left * V("tokenizer") * p_right)
1228 + p_special
1229 + p_reserved
1230
1231 + (p_utf - p_right)
1232 )^1,
1233}
1234
1235local collapse = nil
1236local serialize = table.serialize
1237local f_state = formatters["level %s : %s : intermediate"]
1238
1239local function show_state(t,level,state)
1240 report_asciimath(serialize(t,f_state(level,state)))
1241end
1242
1243local function show_result(original,unicoded,texcoded)
1244 report_asciimath("original > %s",original)
1245 report_asciimath("unicoded > %s",unicoded)
1246 report_asciimath("texcoded > %s",texcoded)
1247end
1248
1249local function collapse_matrices(t)
1250 local n = #t
1251 if n > 4 and t[3] == "," then
1252 local l1 = t[1]
1253 local r1 = t[n]
1254 if isleft[l1] and isright[r1] then
1255 local l2 = t[2]
1256 local r2 = t[n-1]
1257 if type(l2) == "table" and type(r2) == "table" then
1258
1259 local valid = true
1260 for i=3,n-2,2 do
1261 if t[i] ~= "," then
1262 valid = false
1263 break
1264 end
1265 end
1266 if valid then
1267 for i=2,n-1,2 do
1268 local ti = t[i]
1269 local tl = ti[1]
1270 local tr = ti[#ti]
1271 if isleft[tl] and isright[tr] then
1272
1273 else
1274 valid = false
1275 break
1276 end
1277 end
1278 if valid then
1279 local omit = l1 == s_left and r1 == s_right
1280 if omit then
1281 t[1] = "\\startmatrix"
1282 else
1283 t[1] = l1 .. "\\startmatrix"
1284 end
1285 for i=2,n-1 do
1286 if t[i] == "," then
1287 t[i] = "\\NR"
1288 else
1289 local ti = t[i]
1290 ti[1] = "\\NC"
1291 for i=2,#ti-1 do
1292 if ti[i] == "," then
1293 ti[i] = "\\NC"
1294 end
1295 end
1296 ti[#ti] = nil
1297 end
1298 end
1299 if omit then
1300 t[n] = "\\NR\\stopmatrix"
1301 else
1302 t[n] = "\\NR\\stopmatrix" .. r1
1303 end
1304 end
1305 end
1306 end
1307 end
1308 end
1309 return t
1310end
1311
1312local function collapse_bars(t)
1313 local n, i, l, m = #t, 1, false, 0
1314 while i <= n do
1315 local current = t[i]
1316 if current == "\\|" then
1317 if l then
1318 m = m + 1
1319 t[l] = s_lbar
1320 t[i] = s_rbar
1321 t[m] = { unpack(t,l,i) }
1322 l = false
1323 else
1324 l = i
1325 end
1326 elseif not l then
1327 m = m + 1
1328 t[m] = current
1329 end
1330 i = i + 1
1331 end
1332 if l then
1333
1334local d = false
1335for i=1,m do
1336 local ti = t[i]
1337 if type(ti) == "string" and find(ti,"\\left",1,true) then
1338 d = true
1339 break
1340 end
1341end
1342if not d then
1343 local tt = { s_lnothing }
1344 local tm = 1
1345 for i=1,m do
1346 tm = tm + 1
1347 tt[tm] = t[i]
1348 end
1349 tm = tm + 1
1350 tt[tm] = s_mbar
1351 for i=l+1,n do
1352 tm = tm + 1
1353 tt[tm] = t[i]
1354 end
1355 tm = tm + 1
1356 tt[tm] = s_rnothing
1357 m = tm
1358 t = tt
1359end
1360 elseif m < n then
1361 for i=n,m+1,-1 do
1362 t[i] = nil
1363 end
1364 end
1365 return t
1366end
1367
1368local function collapse_pairs(t)
1369 local n, i = #t, 1
1370 while i < n do
1371 local current = t[i]
1372 if current == "/" and i > 1 then
1373 local tl = t[i-1]
1374 local tr = t[i+1]
1375 local tn = t[i+2]
1376 if type(tl) == "table" then
1377 if isleft[tl[1]] and isright[tl[#tl]] then
1378 tl[1] = ""
1379 tl[#tl] = nil
1380 end
1381 end
1382 if type(tr) == "table" then
1383 if tn == "^" then
1384
1385 elseif isleft[tr[1]] and isright[tr[#tr]] then
1386 tr[1] = ""
1387 tr[#tr] = nil
1388 end
1389 end
1390 i = i + 2
1391 elseif current == "," or current == ";" then
1392
1393 i = i + 1
1394 else
1395 i = i + 1
1396 end
1397 end
1398 return t
1399end
1400
1401local function collapse_parentheses(t)
1402 local n, i = #t, 1
1403 if n > 2 then
1404 while i < n do
1405 local current = t[i]
1406 if type(current) == "table" and isleft[t[i-1]] and isright[t[i+1]] then
1407 local c = #current
1408 if c > 2 and isleft[current[1]] and isright[current[c]] then
1409 remove(current,c)
1410 remove(current,1)
1411 end
1412 i = i + 3
1413 else
1414 i = i + 1
1415 end
1416 end
1417 end
1418 return t
1419end
1420
1421local function collapse_stupids(t)
1422 local n, m, i = #t, 0, 1
1423 while i <= n do
1424 m = m + 1
1425 local current = t[i]
1426 if isstupid[current] then
1427 local one = t[i+1]
1428 if type(one) == "table" then
1429 one = collapse(one,level)
1430 t[m] = current .. "{" .. one .. "}"
1431
1432 i = i + 2
1433 else
1434 t[m] = current
1435 i = i + 1
1436 end
1437 else
1438 t[m] = current
1439 i = i + 1
1440 end
1441 end
1442 if i == n then
1443 m = m + 1
1444 t[m] = t[n]
1445 end
1446 if m < n then
1447 for i=n,m+1,-1 do
1448 t[i] = nil
1449 end
1450 end
1451 return t
1452end
1453
1454local function collapse_signs(t)
1455 local n, m, i = #t, 0, 1
1456 while i <= n do
1457 m = m + 1
1458 local current = t[i]
1459 if isunary[current] then
1460 local one = t[i+1]
1461 if not one then
1462
1463 t[m] = current .. "{}"
1464 return t
1465
1466 end
1467 if type(one) == "table" then
1468 if isleft[one[1]] and isright[one[#one]] then
1469 remove(one,#one)
1470 remove(one,1)
1471 end
1472 one = collapse(one,level)
1473 elseif one == "-" and i + 2 <= n then
1474 local t2 = t[i+2]
1475 if type(t2) == "string" then
1476 one = one .. t2
1477 i = i + 1
1478 end
1479 end
1480 t[m] = current .. "{" .. one .. "}"
1481 i = i + 2
1482 elseif i + 2 <= n and isfunny[current] then
1483 local one = t[i+1]
1484 if isinfix[one] then
1485 local two = t[i+2]
1486 if two == "-" then
1487 local three = t[i+3]
1488 if three then
1489 if type(three) == "table" then
1490 three = collapse(three,level)
1491 end
1492 t[m] = current .. one .. "{" .. two .. three .. "}"
1493 i = i + 4
1494 else
1495 t[m] = current
1496 i = i + 1
1497 end
1498 else
1499 t[m] = current
1500 i = i + 1
1501 end
1502 else
1503 t[m] = current
1504 i = i + 1
1505 end
1506 else
1507 t[m] = current
1508 i = i + 1
1509 end
1510 end
1511 if i == n then
1512 m = m + 1
1513 t[m] = t[n]
1514 end
1515 if m < n then
1516 for i=n,m+1,-1 do
1517 t[i] = nil
1518 end
1519 end
1520 return t
1521end
1522
1523local function collapse_binaries(t)
1524 local n, m, i = #t, 0, 1
1525 while i <= n do
1526 m = m + 1
1527 local current = t[i]
1528 if isbinary[current] then
1529 local one = t[i+1]
1530 local two = t[i+2]
1531 if not one then
1532 t[m] = current .. "{}{}"
1533return t
1534
1535 end
1536 if type(one) == "table" then
1537 if isleft[one[1]] and isright[one[#one]] then
1538 remove(one,#one)
1539 remove(one,1)
1540 end
1541 one = collapse(one,level)
1542 end
1543 if not two then
1544 t[m] = current .. "{" .. one .. "}{}"
1545return t
1546
1547 end
1548 if type(two) == "table" then
1549 if isleft[two[1]] and isright[two[#two]] then
1550 remove(two,#two)
1551 remove(two,1)
1552 end
1553 two = collapse(two,level)
1554 end
1555 t[m] = current .. "{" .. one .. "}{" .. two .. "}"
1556 i = i + 3
1557 else
1558 t[m] = current
1559 i = i + 1
1560 end
1561 end
1562 if i == n then
1563 m = m + 1
1564 t[m] = t[n]
1565 end
1566 if m < n then
1567 for i=n,m+1,-1 do
1568 t[i] = nil
1569 end
1570 end
1571 return t
1572end
1573
1574local function collapse_infixes_1(t)
1575 local n, i = #t, 1
1576 while i <= n do
1577 local current = t[i]
1578 if isinfix[current] then
1579 local what = t[i+1]
1580 if what then
1581 if type(what) == "table" then
1582 local f, l = what[1], what[#what]
1583 if isleft[f] and isright[l] then
1584 remove(what,#what)
1585 remove(what,1)
1586 end
1587 t[i+1] = collapse(what,level)
1588 end
1589 i = i + 2
1590 else
1591 break
1592 end
1593 else
1594 i = i + 1
1595 end
1596 end
1597 return t
1598end
1599
1600function collapse_limits(t)
1601 local n, m, i = #t, 0, 1
1602 while i <= n do
1603 m = m + 1
1604 local current = t[i]
1605 if islimits[current] then
1606 local one, two, first, second = nil, nil, t[i+1], t[i+3]
1607 if first and isinfix[first] then
1608 one = t[i+2]
1609 if one then
1610
1611
1612
1613
1614
1615
1616
1617 if second and isinfix[second] then
1618 two = t[i+4]
1619
1620
1621
1622
1623
1624
1625
1626 end
1627 if two then
1628 t[m] = current .. "\\limits" .. first .. "{" .. one .. "}" .. second .. "{" .. two .. "}"
1629 i = i + 5
1630 else
1631 t[m] = current .. "\\limits" .. first .. "{" .. one .. "}"
1632 i = i + 3
1633 end
1634 else
1635 t[m] = current
1636 i = i + 1
1637 end
1638 else
1639 t[m] = current
1640 i = i + 1
1641 end
1642 else
1643 t[m] = current
1644 i = i + 1
1645 end
1646 end
1647 if i == n then
1648 m = m + 1
1649 t[m] = t[n]
1650 end
1651 if m < n then
1652 for i=n,m+1,-1 do
1653 t[i] = nil
1654 end
1655 end
1656 return t
1657end
1658
1659local function collapse_tables(t)
1660 local n, m, i = #t, 0, 1
1661 while i <= n do
1662 m = m + 1
1663 local current = t[i]
1664 if type(current) == "table" then
1665 if current[1] == "\\NC" then
1666 t[m] = collapse(current,level)
1667 else
1668 t[m] = "{" .. collapse(current,level) .. "}"
1669 end
1670 i = i + 1
1671 else
1672 t[m] = current
1673 i = i + 1
1674 end
1675 end
1676 if i == n then
1677 m = m + 1
1678 t[m] = t[n]
1679 end
1680 if m < n then
1681 for i=n,m+1,-1 do
1682 t[i] = nil
1683 end
1684 end
1685 return t
1686end
1687
1688local function collapse_infixes_2(t)
1689 local n, m, i = #t, 0, 1
1690 while i < n do
1691 local current = t[i]
1692 if isinfix[current] and i > 1 then
1693 local tl = t[i-1]
1694 local tr = t[i+1]
1695 local ti = t[i+2]
1696 local tn = t[i+3]
1697 if ti and tn and isinfix[ti] then
1698 t[m] = tl .. current .. "{" .. tr .. "}" .. ti .. "{" .. tn .. "}"
1699 i = i + 4
1700 else
1701 t[m] = tl .. current .. "{" .. tr .. "}"
1702 i = i + 2
1703 end
1704 else
1705 m = m + 1
1706 t[m] = current
1707 i = i + 1
1708 end
1709 end
1710 if i == n then
1711 m = m + 1
1712 t[m] = t[n]
1713 end
1714 if m < n then
1715 for i=n,m+1,-1 do
1716 t[i] = nil
1717 end
1718 end
1719 return t
1720end
1721
1722local function collapse_fractions_1(t)
1723 local n, m, i = #t, 0, 1
1724 while i < n do
1725 local current = t[i]
1726 if current == "/" and i > 1 then
1727 local tl = t[i-1]
1728 local tr = t[i+1]
1729 t[m] = "\\frac{" .. tl .. "}{" .. tr .. "}"
1730 i = i + 2
1731 if i < n then
1732 m = m + 1
1733 t[m] = t[i]
1734 i = i + 1
1735 end
1736 else
1737 m = m + 1
1738 t[m] = current
1739 i = i + 1
1740 end
1741 end
1742 if i == n then
1743 m = m + 1
1744 t[m] = t[n]
1745 end
1746 if m < n then
1747 for i=n,m+1,-1 do
1748 t[i] = nil
1749 end
1750 end
1751 return t
1752end
1753
1754local function collapse_fractions_2(t)
1755 local n, m, i = #t, 0, 1
1756 while i < n do
1757 local current = t[i]
1758 if current == "⁄" and i > 1 then
1759 if i < n and t[i+1] == "⁄" then
1760
1761 t[m] = "{" .. s_left .. t[i-1] .. s_mslash .. s_mslash .. t[i+2] .. s_right .. "}"
1762 i = i + 3
1763 else
1764 t[m] = "{" .. s_left .. t[i-1] .. s_mslash .. t[i+1] .. s_right .. "}"
1765 i = i + 2
1766 end
1767 if i < n then
1768 m = m + 1
1769 t[m] = t[i]
1770 i = i + 1
1771 end
1772 else
1773 m = m + 1
1774 t[m] = current
1775 i = i + 1
1776 end
1777 end
1778 if i == n then
1779 m = m + 1
1780 t[m] = t[n]
1781 end
1782 if m < n then
1783 for i=n,m+1,-1 do
1784 t[i] = nil
1785 end
1786 end
1787 return t
1788end
1789
1790local function collapse_result(t)
1791 local n = #t
1792 if t[1] == s_left and t[n] == s_right then
1793 return concat(t," ",2,n-1)
1794 else
1795 return concat(t," ")
1796 end
1797end
1798
1799collapse = function(t,level)
1800
1801 if not t then
1802 return ""
1803 end
1804
1805 if trace_details then
1806 if level then
1807 level = level + 1
1808 else
1809 level = 1
1810 end
1811 show_state(t,level,"parsed")
1812 end
1813
1814 t = collapse_matrices (t) if trace_details then show_state(t,level,"matrices") end
1815 t = collapse_bars (t) if trace_details then show_state(t,level,"bars") end
1816 t = collapse_stupids (t) if trace_details then show_state(t,level,"stupids") end
1817 t = collapse_pairs (t) if trace_details then show_state(t,level,"pairs") end
1818 t = collapse_parentheses(t) if trace_details then show_state(t,level,"parentheses") end
1819 t = collapse_signs (t) if trace_details then show_state(t,level,"signs") end
1820 t = collapse_binaries (t) if trace_details then show_state(t,level,"binaries") end
1821 t = collapse_infixes_1 (t) if trace_details then show_state(t,level,"infixes (1)") end
1822 t = collapse_limits (t) if trace_details then show_state(t,level,"limits") end
1823 t = collapse_tables (t) if trace_details then show_state(t,level,"tables") end
1824 t = collapse_infixes_2 (t) if trace_details then show_state(t,level,"infixes (2)") end
1825 t = collapse_fractions_1(t) if trace_details then show_state(t,level,"fractions (1)") end
1826 t = collapse_fractions_2(t) if trace_details then show_state(t,level,"fractions (2)") end
1827
1828 return collapse_result(t)
1829end
1830
1831
1832
1833local context = context
1834local ctx_mathematics = context and context.mathematics or report_asciimath
1835local ctx_type = context and context.type or function() end
1836local ctx_inleft = context and context.inleft or function() end
1837
1838local function convert(str,totex)
1839 local unicoded = lpegmatch(u_parser,str) or str
1840 local texcoded = collapse(lpegmatch(a_parser,unicoded))
1841 if trace_mapping then
1842 show_result(str,unicoded,texcoded)
1843 end
1844 if totex then
1845 ctx_mathematics(texcoded)
1846 else
1847 return texcoded
1848 end
1849end
1850
1851local n = 0
1852local p = (
1853 (S("{[(") + P("\\left" )) / function() n = n + 1 end
1854 + (S("}])") + P("\\right")) / function() n = n - 1 end
1855 + p_utf_base
1856)^0
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866local function invalidtex(str)
1867 n = 0
1868 lpegmatch(p,str)
1869 if n == 0 then
1870 return false
1871 elseif n < 0 then
1872 return formatters["too many left fences: %s"](-n)
1873 elseif n > 0 then
1874 return formatters["not enough right fences: %s"](n)
1875 end
1876end
1877
1878local collected = { }
1879local indexed = { }
1880
1881
1882
1883local p_reserved_spaced =
1884 C(lpeg.utfchartabletopattern(k_reserved_words)) / " %1 "
1885
1886local p_text =
1887 C(P("text")) / " %1 "
1888 * p_spaces^0
1889 * (
1890 (P("{") * (1-P("}"))^0 * P("}"))
1891 + (P("(") * (1-P(")"))^0 * P(")"))
1892 )
1893 + patterns.doublequoted
1894
1895local p_expand = Cs((p_text + p_reserved_spaced + p_utf_base)^0)
1896local p_compress = patterns.collapser
1897
1898local function cleanedup(str)
1899 return lpegmatch(p_compress,lpegmatch(p_expand,str)) or str
1900end
1901
1902
1903
1904local function register(s,cleanedup,collected,shortname)
1905 local c = cleanedup(s)
1906 local f = collected[c]
1907 if f then
1908 f.count = f.count + 1
1909 f.files[shortname] = (f.files[shortname] or 0) + 1
1910 if s ~= c then
1911 f.cleanedup = f.cleanedup + 1
1912 end
1913 f.dirty[s] = (f.dirty[s] or 0) + 1
1914 else
1915 local texcoded = convert(s)
1916 local message = invalidtex(texcoded)
1917 if message then
1918 report_asciimath("%s: %s : %s",message,s,texcoded)
1919 end
1920 collected[c] = {
1921 count = 1,
1922 files = { [shortname] = 1 },
1923 texcoded = texcoded,
1924 message = message,
1925 cleanedup = s ~= c and 1 or 0,
1926 dirty = { [s] = 1 }
1927 }
1928 end
1929end
1930
1931local function wrapup(collected,indexed)
1932 local n = 0
1933 for k, v in sortedhash(collected) do
1934 n = n + 1
1935 v.n= n
1936 indexed[n] = k
1937 end
1938end
1939
1940function collect(fpattern,element,collected,indexed)
1941 local element = element or "am"
1942 local mpattern = formatters["<%s>(.-)</%s>"](element,element)
1943 local filenames = resolvers.findtexfile(fpattern)
1944 if filenames and filenames ~= "" then
1945 filenames = { filenames }
1946 else
1947 filenames = dir.glob(fpattern)
1948 end
1949 local cfpattern = gsub(fpattern,"^%./",lfs.currentdir())
1950 local cfpattern = gsub(cfpattern,"\\","/")
1951 local wildcard = string.split(cfpattern,"*")[1]
1952 if not collected then
1953 collected = { }
1954 indexed = { }
1955 end
1956 for i=1,#filenames do
1957 filename = gsub(filenames[i],"\\","/")
1958 local splitname = (wildcard and wildcard ~= "" and string.split(filename,wildcard)[2]) or filename
1959 local shortname = gsub(splitname or file.basename(filename),"^%./","")
1960 if shortname == "" then
1961 shortname = filename
1962 end
1963 local fullname = resolvers.findtexfile(filename) or filename
1964 if fullname ~= "" then
1965 for s in gmatch(io.loaddata(fullname),mpattern) do
1966 register(s,cleanedup,collected,shortname)
1967 end
1968 end
1969 end
1970 wrapup(collected,indexed)
1971 return collected, indexed
1972end
1973
1974function filter(root,pattern,collected,indexed)
1975 if not pattern or pattern == "" then
1976 pattern = "am"
1977 end
1978 if not collected then
1979 collected = { }
1980 indexed = { }
1981 end
1982 for c in xmlcollected(root,pattern) do
1983 register(xmltext(c),cleanedup,collected,xmlinclusion(c) or "" )
1984 end
1985 wrapup(collected,indexed)
1986 return collected, indexed
1987end
1988
1989asciimath.convert = convert
1990asciimath.reserved = reserved
1991asciimath.collect = collect
1992asciimath.filter = filter
1993asciimath.invalidtex = invalidtex
1994asciimath.cleanedup = cleanedup
1995
1996
1997
1998local uncrapped = {
1999 ["%"] = "\\mathpercent",
2000 ["&"] = "\\mathampersand",
2001 ["#"] = "\\mathhash",
2002 ["$"] = "\\mathdollar",
2003 ["^"] = "\\Hat{\\enspace}",
2004 ["_"] = "\\underline{\\enspace}",
2005}
2006
2007local function convert(str,nowrap)
2008 if str ~= "" then
2009 local unicoded = lpegmatch(u_parser,str) or str
2010 if lpegmatch(p_onechar,unicoded) then
2011 ctx_mathematics(uncrapped[unicoded] or unicoded)
2012 else
2013 local texcoded = collapse(lpegmatch(a_parser,unicoded))
2014 if trace_mapping then
2015 show_result(str,unicoded,texcoded)
2016 end
2017 if #texcoded == 0 then
2018 report_asciimath("error in asciimath: %s",str)
2019 else
2020 local message = invalidtex(texcoded)
2021 if message then
2022 report_asciimath("%s: %s : %s",message,str,texcoded)
2023 ctx_type(formatters["<%s>"](message))
2024 elseif nowrap then
2025 context(texcoded)
2026 else
2027 ctx_mathematics(texcoded)
2028 end
2029 end
2030 end
2031 end
2032end
2033
2034local context = context
2035
2036if not context then
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126 return
2127
2128end
2129
2130interfaces.implement {
2131 name = "asciimath",
2132 actions = convert,
2133 arguments = "string"
2134}
2135
2136interfaces.implement {
2137 name = "justasciimath",
2138 actions = convert,
2139 arguments = { "string", true },
2140}
2141
2142interfaces.implement {
2143 name = "xmlasciimath",
2144 actions = function(id)
2145 convert(xmlpure(lxmlgetid(id)))
2146 end,
2147 arguments = "string"
2148}
2149
2150local ctx_typebuffer = context.typebuffer
2151local ctx_mathematics = context.mathematics
2152local ctx_color = context.color
2153
2154local sequenced = table.sequenced
2155local assign_buffer = buffers.assign
2156
2157local show = { }
2158asciimath.show = show
2159
2160local collected, indexed, ignored = { }, { }, { }
2161
2162local color = { "darkred" }
2163
2164function show.ignore(n)
2165 if type(n) == "string" then
2166 local c = collected[n]
2167 n = c and c.n
2168 end
2169 if n then
2170 ignored[n] = true
2171 end
2172end
2173
2174function show.count(n,showcleanedup)
2175 local v = collected[indexed[n]]
2176 local count = v.count
2177 local cleanedup = v.cleanedup
2178 if not showcleanedup or cleanedup == 0 then
2179 context(count)
2180 elseif count == cleanedup then
2181 ctx_color(color,count)
2182 else
2183 context("%s+",count-cleanedup)
2184 ctx_color(color,cleanedup)
2185 end
2186end
2187
2188local h = { }
2189local am = { "am" }
2190
2191function show.nofdirty(n)
2192 local k = indexed[n]
2193 local v = collected[k]
2194 local n = v.cleanedup
2195 h = { }
2196 if n > 0 then
2197 for d, n in sortedhash(v.dirty) do
2198 if d ~= k then
2199 h[#h+1] = { d, n }
2200 end
2201 end
2202 end
2203 context(#h)
2204end
2205
2206function show.dirty(m,wrapped)
2207 local d = h[m]
2208 if d then
2209 ctx_inleft(d[2])
2210 if wrapped then
2211 assign_buffer("am",'"' .. d[1] .. '"')
2212 else
2213 assign_buffer("am",d[1])
2214 end
2215 ctx_typebuffer(am)
2216 end
2217end
2218
2219function show.files(n)
2220 context(sequenced(collected[indexed[n]].files," "))
2221end
2222
2223function show.input(n,wrapped)
2224 if wrapped then
2225 assign_buffer("am",'"' .. indexed[n] .. '"')
2226 else
2227 assign_buffer("am",indexed[n])
2228 end
2229 ctx_typebuffer(am)
2230end
2231
2232function show.result(n)
2233 local v = collected[indexed[n]]
2234 if ignored[n] then
2235 context("ignored")
2236 elseif v.message then
2237 ctx_color(color, v.message)
2238 else
2239 ctx_mathematics(v.texcoded)
2240 end
2241end
2242
2243function show.load(str,element)
2244 collected, indexed, ignored = { }, { }, { }
2245 local t = utilities.parsers.settings_to_array(str)
2246 for i=1,#t do
2247 asciimath.collect(t[i],element or "am",collected,indexed)
2248 end
2249end
2250
2251function show.filter(id,element)
2252 collected, indexed, ignored = { }, { }, { }
2253 asciimath.filter(lxmlgetid(id),element or "am",collected,indexed)
2254end
2255
2256function show.max()
2257 context(#indexed)
2258end
2259
2260function show.statistics()
2261 local usedfiles = { }
2262 local noffiles = 0
2263 local nofokay = 0
2264 local nofbad = 0
2265 local nofcleanedup = 0
2266 for k, v in next, collected do
2267 if ignored[v.n] then
2268 nofbad = nofbad + v.count
2269 elseif v.message then
2270 nofbad = nofbad + v.count
2271 else
2272 nofokay = nofokay + v.count
2273 end
2274 nofcleanedup = nofcleanedup + v.cleanedup
2275 for k, v in next, v.files do
2276 local u = usedfiles[k]
2277 if u then
2278 usedfiles[k] = u + 1
2279 else
2280 noffiles = noffiles + 1
2281 usedfiles[k] = 1
2282 end
2283 end
2284 end
2285 local NC = context.NC
2286 local NR = context.NR
2287 local EQ = context.EQ
2288 context.starttabulate { "|B||" }
2289 NC() context("files") EQ() context(noffiles) NC() NR()
2290 NC() context("formulas") EQ() context(nofokay+nofbad) NC() NR()
2291 NC() context("uniques") EQ() context(#indexed) NC() NR()
2292 NC() context("cleanedup") EQ() context(nofcleanedup) NC() NR()
2293 NC() context("errors") EQ() context(nofbad) NC() NR()
2294 context.stoptabulate()
2295end
2296
2297function show.save(name)
2298 table.save(name ~= "" and name or "dummy.lua",collected)
2299end
2300 |