x-asciimath.lua /size: 65 Kb    last modification: 2024-01-16 09: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
76local 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
176    -- brackets
177
178    ["("]         = { true, "(" },
179    [")"]         = { true, ")" },
180    ["["]         = { true, "[" },
181    ["]"]         = { true, "]" },
182    ["{"]         = { true, "{" },
183    ["}"]         = { true, "}" },
184
185    -- binary relations
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    -- arrows
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    -- logical
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    -- miscellaneous
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,  "..." }, -- ldots
252    ["ldots"]     = { true,  "..." }, -- ldots
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    -- more
264
265    ["_="]        = { true, "" },
266
267    -- bonus
268
269    ["prime"]     = { true,  "" }, -- bonus
270    ["'"]         = { true,  "" }, -- bonus
271    ["''"]        = { true,  "" }, -- bonus
272    ["'''"]       = { true,  "" }, -- bonus
273
274    -- special
275
276    ["%"]         = { false, "\\mathpercent" },
277    ["&"]         = { false, "\\mathampersand" },
278    ["#"]         = { false, "\\mathhash" },
279    ["$"]         = { false, "\\mathdollar" },
280
281    -- blackboard
282
283    ["CC"]        = { true, "" },
284    ["NN"]        = { true, "" },
285    ["QQ"]        = { true, "" },
286    ["RR"]        = { true, "" },
287    ["ZZ"]        = { true, "" },
288
289    -- greek lowercase
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    -- greek uppercase
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    -- blackboard
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    -- fraktur
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    -- script
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    -- bold
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    -- sans
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    -- monospace
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    -- some more undocumented
668
669    ["dx"] = { false, { "d", "x" } }, -- "{dx}" "\\left(dx\\right)"
670    ["dy"] = { false, { "d", "y" } }, -- "{dy}" "\\left(dy\\right)"
671    ["dz"] = { false, { "d", "z" } }, -- "{dz}" "\\left(dz\\right)"
672
673    -- fences
674
675    ["(:"] = { true, "(:" },
676    ["{:"] = { true, "{:" },
677    ["[:"] = { true, "[:" },
678    ["("]  = { true, "(" },
679    ["["]  = { true, "[" },
680    ["{"]  = { true, "{" },
681    ["<<"] = { true, "" },     -- why not <:
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, "" },   -- why not :>
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    -- a bit special:
716
717--     ["\\frac"]   = { true, "frac" },
718
719    -- now it gets real crazy, only these two:
720
721    ["&gt;"]     = { true, ">" },
722    ["&lt;"]     = { true, "<" },
723
724    -- extra:
725
726    -- also, invisible times
727
728    ["dd"]       = { false, "{\\tf d}" },
729    ["ee"]       = { false, "{\\tf e}" },
730    ["xxx"]      = { true, utfchar(0x2063) }, -- invisible times
731
732}
733
734-- This is an undocumented option for Ton (math4all):
735
736-- \startluacode
737-- if not asciimath then
738--     asciimath = {
739--         extras = {
740--             ["GTK"] = { false, "\\text{\\it GTK}" }, -- proper kerning/spacing
741--         }
742--     }
743-- end
744-- \stopluacode
745--
746-- \usemodule[asciimath]
747-- \starttext
748--    \asciimath{GTK}
749-- stoptext
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-- a..z A..Z : allemaal op italic alphabet
761-- en dan default naar upright "upr a"
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     -- reserved["\\" .. name] = char
769    end
770 -- local spec = v.mathspec
771 -- if spec then
772 --     for i=1,#spec do
773 --         local name = spec[i].name
774 --         if name and not reserved[name] then
775 --             reserved[name] = { true, utfchar(k) }
776 --         end
777 --     end
778 -- end
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 = { -- can be taken from reserved
795    ["\\sqrt"]            = true,
796    ["\\asciimathsqrt"]   = true,
797    ["\\text"]            = true, --  mathoptext
798    ["\\mathoptext"]      = true, --  mathoptext
799    ["\\asciimathoptext"] = true, --  mathoptext
800    ["\\hat"]             = true, --  widehat
801    ["\\widehat"]         = true, --  widehat
802    ["\\bar"]             = true, --
803    ["\\overbar"]         = true, --
804    ["\\overline"]        = true, --
805    ["\\underline"]       = true, --
806    ["\\vec"]             = true, --  overrightarrow
807    ["\\overrightarrow"]  = true, --  overrightarrow
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 -- ["\\atan"]      = true,
849 -- ["\\acos"]      = true,
850 -- ["\\asin"]      = true,
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-- special mess (we have a generic one now but for the moment keep this)
890-- special mess (we have a generic one now but for the moment keep this)
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-- local digitized_3   = Cs (
933--                          p_fourbefore  * p_aftercomma^0 +
934--                          p_beforecomma * p_aftercomma^0
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     -- if separator == true or not interfaces or interfaces.variables.yes then
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-- asciimath.setup { splitmethod = 3, symbol = "," }
996-- local t = {
997--     "0.00002",
998--     "1", "12", "123", "1234", "12345", "123456", "1234567", "12345678", "123456789",
999--     "1.1",
1000--     "12.12",
1001--     "123.123",
1002--     "1234.123",
1003--     "1234.1234",
1004--     "12345.1234",
1005--     "1234.12345",
1006--     "12345.12345",
1007--     "123456.123456",
1008--     "1234567.1234567",
1009--     "12345678.12345678",
1010--     "123456789.123456789",
1011--     "0.1234",
1012--     "1234.0",
1013--     "1234.00",
1014--     "0.123456789",
1015--     "100.00005",
1016--     "0.80018",
1017--     "10.80018",
1018--     "100.80018",
1019--     "1000.80018",
1020--     "10000.80018",
1021-- }
1022-- for i=1,#t do print(formatters["%-20s : [%s]"](t[i],numbermess(t[i]))) end
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----- p_number      = p_number_base
1039local p_spaces      = patterns.whitespace
1040
1041local p_utf_base    = patterns.utf8character
1042local p_utf         = C(p_utf_base)
1043-- local p_entity      = (P("&") * C((1-P(";"))^2) * P(";"))/ entities
1044
1045-- entities["gt"]    = ">"
1046-- entities["lt"]    = "<"
1047-- entities["amp"]   = "&"
1048-- entities["dquot"] = '"'
1049-- entities["quot"]  = "'"
1050
1051local p_onechar     = p_utf_base * P(-1)
1052
1053----- p_number = Cs((patterns.cpnumber or patterns.cnumber or patterns.number)/function(s) return (gsub(s,",","{,}")) end)
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-- local number  = C(float + integer)
1062-- local p_number  = C(float)
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 -- keep them ... later we remap these
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  -- dirty trick, no real unicode (still needed ?)
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--     lpeg.utfchartabletopattern(table.keys(k_unicode)) / k_unicode
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  * ( -- maybe balanced
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 -- ["<<"] = s_langle,     -- why not <:
1132 -- ["|_"] = s_lfloor,
1133 -- ["|~"] = s_lceil,
1134 -- ["〈"]  = s_langle,
1135 -- ["〈"]  = s_langle,
1136
1137 -- ["lparent"]  = s_lparent,
1138 -- ["lbracket"] = s_lbracket,
1139 -- ["lbrace"]   = s_lbrace,
1140 -- ["langle"]   = s_langle,
1141 -- ["lfloor"]   = s_lfloor,
1142 -- ["lceil"]    = s_lceil,
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 -- [">>"] = s_rangle,   -- why not :>
1157 -- ["~|"] = s_rceil,
1158 -- ["_|"] = s_rfloor,
1159 -- ["〉"]  = s_rangle,
1160 -- ["〉"]  = s_rangle,
1161
1162 -- ["rparent"]  = s_rparent,
1163 -- ["rbracket"] = s_rbracket,
1164 -- ["rbrace"]   = s_rbrace,
1165 -- ["rangle"]   = s_rangle,
1166 -- ["rfloor"]   = s_rfloor,
1167 -- ["rceil"]    = s_rceil,
1168}
1169
1170local islimits = {
1171    ["\\sum"]  = true,
1172 -- ["∑"]      = true,
1173    ["\\prod"] = true,
1174 -- ["∏"]      = true,
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-- special cases
1184
1185-- local p_special =
1186--     C("/")
1187--   + P("\\ ")  * Cc("{}") * p_spaces^0 * C(S("^_"))
1188--   + P("\\ ")  * Cc("\\space")
1189--   + P("\\\\") * Cc("\\backslash")
1190--   + P("\\")   * (R("az","AZ")^1/entities)
1191--   + P("|")    * Cc("\\|")
1192--
1193-- faster bug also uglier:
1194
1195local p_special =
1196    P("|")  * Cc("\\|") -- s_mbar -- maybe always add left / right as in mml ?
1197  + P("\\") * (
1198        (
1199            P(" ") * (
1200                  Cc("{}") * p_spaces^0 * C(S("^_"))
1201                + Cc("\\space")
1202            )
1203        )
1204      + P("\\") * Cc("\\backslash")
1205   -- + (R("az","AZ")^1/entities)
1206      + C(R("az","AZ")^1)
1207    )
1208
1209-- open | close :: {: | :}
1210
1211local u_parser = Cs ( (
1212    patterns.doublequoted +
1213    P("text") * p_spaces^0 * P("(") * (1-P(")"))^0 * P(")") + -- -- todo: balanced
1214    P("\\frac") / "frac" + -- bah
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   -- + Ct(p_open * V("tokenizer") * p_close)        -- {: (a+b,=,1),(a+b,=,7) :}
1225   -- + Ct(p_open * V("tokenizer") * p_close_right)  -- {  (a+b,=,1),(a+b,=,7) :}
1226   -- + Ct(p_open_left * V("tokenizer") * p_right)   -- {: (a+b,=,1),(a+b,=,7)  }
1227      + Ct(p_left * V("tokenizer") * p_right)        -- {  (a+b,=,1),(a+b,=,7)  }
1228      + p_special
1229      + p_reserved
1230  --  + p_utf - p_close - p_right
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                -- we have a matrix
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                            -- ok
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        -- problem: we can have a proper nesting
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 } -- space fools final checker
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 -- space fools final checker
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]   = "" -- todo: remove
1379                    tl[#tl] = nil
1380                end
1381            end
1382            if type(tr) == "table" then
1383                if tn == "^" then
1384                    -- brr 1/(1+x)^2
1385                elseif isleft[tr[1]] and isright[tr[#tr]] then
1386                    tr[1]   = "" -- todo: remove
1387                    tr[#tr] = nil
1388                end
1389            end
1390            i = i + 2
1391        elseif current == "," or current == ";" then
1392         -- t[i] = current .. "\\thinspace" -- look sbad in (a,b)
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--                 t[m] = current .. "\\begingroup" .. one .. "\\endgroup"
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 -- yes?
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--                 m = m + 1
1463                t[m] = current .. "{}" -- error
1464                return t
1465--                 break
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 -- or another sign ? or unary ?
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 -- or another sign ? or unary ?
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 -- yes?
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 .. "{}{}" -- error
1533return t
1534--                 break
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--                 break
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 -- yes?
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) -- collapse ?
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                 -- if type(one) == "table" then
1611                 --     if isleft[one[1]] and isright[one[#one]] then
1612                 --         remove(one,#one)
1613                 --         remove(one,1)
1614                 --     end
1615                 --     one = collapse(one,level)
1616                 -- end
1617                    if second and isinfix[second] then
1618                        two = t[i+4]
1619                     -- if type(two) == "table" then
1620                     --     if isleft[two[1]] and isright[two[#two]] then
1621                     --         remove(two,#two)
1622                     --         remove(two,1)
1623                     --     end
1624                     --     two = collapse(two,level)
1625                     -- end
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 -- yes?
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 -- yes?
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 -- \slash
1759            if i < n and t[i+1] == "" then
1760                -- crap for
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 -- see bar .. space needed there
1793        return concat(t," ",2,n-1)
1794    else
1795        return concat(t," ")
1796    end
1797end
1798
1799collapse = function(t,level)
1800    -- check
1801    if not t then
1802        return ""
1803    end
1804    -- tracing
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    -- steps
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    -- done
1828    return collapse_result(t)
1829end
1830
1831-- todo: cache simple ones, say #str < 10, maybe weak
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-- faster:
1859--
1860-- local p = (
1861--     (S("{[(") + P("\\left" )) * Cc(function() n = n + 1 end)
1862--   + (S("}])") + P("\\right")) * Cc(function() n = n - 1 end)
1863--   + p_utf_base
1864-- )^0
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-- bonus
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  * ( -- maybe balanced
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-- so far
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-- sin(x) = 1 : 3.3 uncached 1.2 cached , so no real gain (better optimize the converter then)
1997
1998local uncrapped = {
1999    ["%"] = "\\mathpercent",
2000    ["&"] = "\\mathampersand",
2001    ["#"] = "\\mathhash",
2002    ["$"] = "\\mathdollar",
2003    ["^"] = "\\Hat{\\enspace}", -- terrible hack ... tex really does it sbest to turn any ^ into a superscript
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--     trace_mapping = true
2039--     trace_details = true
2040
2041--     report_asciimath(cleanedup([[ac+sinx+xsqrtx+sinsqrtx+sinsqrt(x)]]))
2042--     report_asciimath(cleanedup([[a "αsinsqrtx" b]]))
2043--     convert([[a "αsinsqrtx" b]])
2044--     report_asciimath(cleanedup([[a "α" b]]))
2045--     report_asciimath(cleanedup([[//4]]))
2046
2047-- convert("leq\\leq")
2048-- convert([[\^{1/5}log]])
2049-- convert("sqrt")
2050-- convert("^")
2051
2052-- convert[[\frac{a}{b}]]
2053-- convert[[frac{a}{b}]]
2054
2055-- convert("frac{a}{b}")
2056-- convert("\\sin{a}{b}")
2057-- convert("sin{a}{b}")
2058-- convert("1: rightarrow")
2059-- convert("2: \\rightarrow")
2060
2061-- convert("((1,2,3),(4,5,6),(7,8,9))")
2062
2063-- convert("1/(t+x)^2")
2064
2065--     convert("AA a > 0 ^^ b > 0 | {:log_g:} a + {:log_g:} b")
2066--     convert("AA a &gt; 0 ^^ b > 0 | {:log_g:} a + {:log_g:} b")
2067
2068--     convert("10000,00001")
2069--     convert("4/18*100text(%)~~22,2")
2070--     convert("4/18*100text(%)≈22,2")
2071--     convert("62541/(197,6)≈316,05")
2072
2073--     convert([[sum x]])
2074--     convert([[sum^(1)_(2) x]])
2075--     convert([[lim_(1)^(2) x]])
2076--     convert([[lim_(1) x]])
2077--     convert([[lim^(2) x]])
2078
2079--     convert([[{: rangle]])
2080--     convert([[\langle\larr]])
2081--     convert([[langlelarr]])
2082--     convert([[D_f=[0 ,→〉]])
2083--     convert([[ac+sinx+xsqrtx]])
2084--     convert([[ac+\alpha x+xsqrtx-cc b*pi**psi-3alephx / bb X]])
2085--     convert([[ac+\ ^ x+xsqrtx]])
2086--     convert([[d/dx(x^2+1)]])
2087--     convert([[a "αsinsqrtx" b]])
2088--     convert([[a "α" b]])
2089--     convert([[//4]])
2090--     convert([[ {(a+b,=,1),(a+b,=,7)) ]])
2091
2092--     convert([[ 2/a // 5/b = (2 b) / ( a b) // ( 5 a ) / ( a b ) = (2 b ) / ( 5 a ) ]])
2093--     convert([[ (2+x)/a // 5/b  ]])
2094
2095--     convert([[ ( 2/a ) // ( 5/b ) = ( (2 b) / ( a b) ) // ( ( 5 a ) / ( a b ) ) = (2 b ) / ( 5 a ) ]])
2096
2097--     convert([[ (x/y)^3 = x^3/y^3 ]])
2098
2099--     convert([[ {: (1,2) :} ]])
2100--     convert([[ {: (a+b,=,1),(a+b,=,7) :} ]])
2101--     convert([[ {  (a+b,=,1),(a+b,=,7) :} ]])
2102--     convert([[ {: (a+b,=,1),(a+b,=,7)  } ]])
2103--     convert([[ {  (a+b,=,1),(a+b,=,7)  } ]])
2104
2105--     convert([[(1,5 ±sqrt(1,25 ),0 )]])
2106--     convert([[1//2]])
2107--     convert([[(p)/sqrt(p)]])
2108--     convert([[u_tot]])
2109--     convert([[u_tot=4,4 L+0,054 T]])
2110
2111--     convert([[ [←;0,2] ]])
2112--     convert([[ [←;0,2⟩ ]])
2113--     convert([[ ⟨←;0,2 ) ]])
2114--     convert([[ ⟨←;0,2 ] ]])
2115--     convert([[ ⟨←;0,2⟩ ]])
2116
2117--     convert([[ x^2(x-1/16)=0 ]])
2118--     convert([[ y = ax + 3 - 3a ]])
2119--     convert([[ y= ((1/4)) ^x ]])
2120--     convert([[ x=\ ^ (1/4) log(0 ,002 )= log(0,002) / (log(1/4) ]])
2121--     convert([[ x=\ ^glog(y) ]])
2122--     convert([[ x^ (-1 1/2) =1/x^ (1 1/2)=1/ (x^1*x^ (1/2)) =1/ (xsqrt(x)) ]])
2123--     convert([[ x^2(10 -x)&gt;2 x^2 ]])
2124--     convert([[ x^4&gt;x ]])
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