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