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