diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex')
45 files changed, 13269 insertions, 11639 deletions
diff --git a/Master/texmf-dist/tex/luatex/luamplib/luamplib.lua b/Master/texmf-dist/tex/luatex/luamplib/luamplib.lua index e4fb642f562..972e36172fd 100644 --- a/Master/texmf-dist/tex/luatex/luamplib/luamplib.lua +++ b/Master/texmf-dist/tex/luatex/luamplib/luamplib.lua @@ -11,8 +11,8 @@ luatexbase.provides_module { name = "luamplib", - version = "2.31.1", - date = "2024/05/24", + version = "2.34.5", + date = "2024/08/03", description = "Lua package to typeset Metapost with LuaTeX's MPLib.", } @@ -45,7 +45,6 @@ local function termorlog (target, text, kind) if kind == "Error" then error() end end end - local function warn (...) -- beware '%' symbol termorlog("term and log", select("#",...) > 1 and format(...) or ...) end @@ -60,22 +59,19 @@ luamplib.showlog = luamplib.showlog or false local tableconcat = table.concat local tableinsert = table.insert +local tableunpack = table.unpack local texsprint = tex.sprint local texgettoks = tex.gettoks local texgetbox = tex.getbox local texruntoks = tex.runtoks - if not texruntoks then err("Your LuaTeX version is too old. Please upgrade it to the latest") end - local is_defined = token.is_defined local get_macro = token.get_macro - local mplib = require ('mplib') local kpse = require ('kpse') local lfs = require ('lfs') - local lfsattributes = lfs.attributes local lfsisdir = lfs.isdir local lfsmkdir = lfs.mkdir @@ -86,7 +82,6 @@ local file = file or { } local replacesuffix = file.replacesuffix or function(filename, suffix) return (filename:gsub("%.[%a%d]+$","")) .. "." .. suffix end - local is_writable = file.is_writable or function(name) if lfsisdir(name) then name = name .. "/_luam_plib_temp_file_" @@ -105,11 +100,8 @@ local mk_full_path = lfs.mkdirp or lfs.mkdirs or function(path) end end -local luamplibtime = kpse.find_file("luamplib.lua") -luamplibtime = luamplibtime and lfsattributes(luamplibtime,"modification") - +local luamplibtime = lfsattributes(kpse.find_file"luamplib.lua", "modification") local currenttime = os.time() - local outputdir, cachedir if lfstouch then for i,v in ipairs{'TEXMFVAR','TEXMF_OUTPUT_DIRECTORY','.','TEXMFOUTPUT'} do @@ -146,7 +138,6 @@ function luamplib.getcachedir(dir) end end end - local noneedtoreplace = { ["boxes.mp"] = true, -- ["format.mp"] = true, ["graph.mp"] = true, ["marith.mp"] = true, ["mfplain.mp"] = true, @@ -164,7 +155,6 @@ local noneedtoreplace = { ["mp-tool.mpiv"] = true, ["mp-cont.mpiv"] = true, } luamplib.noneedtoreplace = noneedtoreplace - local function replaceformatmp(file,newfile,ofmodify) local fh = ioopen(file,"r") if not fh then return file end @@ -182,12 +172,10 @@ local function replaceformatmp(file,newfile,ofmodify) lfstouch(newfile,currenttime,ofmodify) return newfile end - local name_b = "%f[%a_]" local name_e = "%f[^%a_]" local btex_etex = name_b.."btex"..name_e.."%s*(.-)%s*"..name_b.."etex"..name_e local verbatimtex_etex = name_b.."verbatimtex"..name_e.."%s*(.-)%s*"..name_b.."etex"..name_e - local function replaceinputmpfile (name,file) local ofmodify = lfsattributes(file,"modification") if not ofmodify then return file end @@ -200,19 +188,15 @@ local function replaceinputmpfile (name,file) return nf.size == 0 and file or newfile end end - if name == "format.mp" then return replaceformatmp(file,newfile,ofmodify) end - local fh = ioopen(file,"r") if not fh then return file end local data = fh:read("*all"); fh:close() - local count,cnt = 0,0 data, cnt = data:gsub(btex_etex, "btex %1 etex ") -- space count = count + cnt data, cnt = data:gsub(verbatimtex_etex, "verbatimtex %1 etex;") -- semicolon count = count + cnt - if count == 0 then noneedtoreplace[name] = true fh = ioopen(newfile,"w"); @@ -222,7 +206,6 @@ local function replaceinputmpfile (name,file) end return file end - fh = ioopen(newfile,"w") if not fh then return file end fh:write(data); fh:close() @@ -238,12 +221,10 @@ do end mpkpse = kpse.new(arg[exe], "mpost") end - local special_ftype = { pfb = "type1 fonts", enc = "enc files", } - function luamplib.finder (name, mode, ftype) if mode == "w" then if name and name ~= "mpout.log" then @@ -273,16 +254,14 @@ local preamble = [[ let normalfontsize = fontsize; input %s ; ]] - local currentformat = "plain" function luamplib.setformat (name) currentformat = name end - luamplib.codeinherit = false local mplibinstances = {} +luamplib.instances = mplibinstances local has_instancename = false - local function reporterror (result, prevlog) if not result then err("no result object returned") @@ -314,7 +293,7 @@ local function reporterror (result, prevlog) return log end end - +if not math.initialseed then math.randomseed(currenttime) end local function luamplibload (name) local mpx = mplib.new { ini_version = true, @@ -329,7 +308,7 @@ local function luamplibload (name) local preamble = tableconcat{ format(preamble, replacesuffix(name,"mp")), luamplib.preambles.mplibcode, - luamplib.legacy_verbatimtex and luamplib.preambles.legacyverbatimtex or "", + luamplib.legacyverbatimtex and luamplib.preambles.legacyverbatimtex or "", luamplib.textextlabel and luamplib.preambles.textextlabel or "", } local result, log @@ -341,7 +320,6 @@ local function luamplibload (name) log = reporterror(result) return mpx, result, log end - local function process (data, instancename) local currfmt if instancename and instancename ~= "" then @@ -352,7 +330,7 @@ local function process (data, instancename) currentformat, luamplib.numbersystem or "scaled", tostring(luamplib.textextlabel), - tostring(luamplib.legacy_verbatimtex), + tostring(luamplib.legacyverbatimtex), } has_instancename = false end @@ -373,8 +351,6 @@ local function process (data, instancename) if log then if result.fig then converted = luamplib.convert(result) - else - info"No figure output. Maybe no beginfig/endfig" end end else @@ -384,20 +360,17 @@ local function process (data, instancename) end local pdfmode = tex.outputmode > 0 + local catlatex = luatexbase.registernumber("catcodetable@latex") local catat11 = luatexbase.registernumber("catcodetable@atletter") - local function run_tex_code (str, cat) texruntoks(function() texsprint(cat or catlatex, str) end) end - local texboxes = { globalid = 0, localid = 4096 } local factor = 65536*(7227/7200) - local textext_fmt = 'image(addto currentpicture doublepath unitsquare \z -xscaled %f yscaled %f shifted (0,-%f) \z -withprescript "mplibtexboxid=%i:%f:%f")' - + xscaled %f yscaled %f shifted (0,-%f) \z + withprescript "mplibtexboxid=%i:%f:%f")' local function process_tex_text (str) if str then local global = (has_instancename or luamplib.globaltextext or luamplib.codeinherit) @@ -409,8 +382,7 @@ local function process_tex_text (str) else local boxid = texboxes.globalid + 1 texboxes.globalid = boxid - run_tex_code(format( - [[\expandafter\newbox\csname luamplib.box.%s\endcsname]], boxid)) + run_tex_code(format([[\expandafter\newbox\csname luamplib.box.%s\endcsname]], boxid)) tex_box_id = tex.getcount'allocationnumber' end run_tex_code(format("%s\\setbox%i\\hbox{%s}", global, tex_box_id, str)) @@ -436,7 +408,6 @@ local mplibcolorfmt = { [[\color_select:n%s\endgroup]], }, } - local colfmt = is_defined'color_select:n' and "l3color" or "xcolor" if colfmt == "l3color" then run_tex_code{ @@ -450,7 +421,6 @@ if colfmt == "l3color" then } end local ccexplat = luatexbase.registernumber"luamplibcctabexplat" - local function process_color (str) if str then if not str:find("%b{}") then @@ -501,14 +471,12 @@ end local tex_code_pre_mplib = {} luamplib.figid = 1 luamplib.in_the_fig = false - local function process_verbatimtex_prefig (str) if str then tex_code_pre_mplib[luamplib.figid] = str end return "" end - local function process_verbatimtex_infig (str) if str then return format('special "postmplibverbtex=%s";', str) @@ -530,7 +498,6 @@ local mp = mp mp.mf_path_reset = mp.mf_path_reset or function() end mp.mf_finish_saving_data = mp.mf_finish_saving_data or function() end mp.report = mp.report or info - catcodes = catcodes or {} local catcodes = catcodes catcodes.numbers = catcodes.numbers or {} @@ -559,7 +526,6 @@ local function mpprint(buffer,...) end end end - function luamplib.runscript (code) local id, str = code:match("(.-){(.*)}") if id and str then @@ -581,7 +547,7 @@ function luamplib.runscript (code) return buffer end buffer = {} - mpprint(buffer, table.unpack(res)) + mpprint(buffer, tableunpack(res)) return tableconcat(buffer) end return "" @@ -594,9 +560,7 @@ local function protecttexcontents (str) :gsub("%zPerCent%z", "\\%%") :gsub("%s+", " ") end - -luamplib.legacy_verbatimtex = true - +luamplib.legacyverbatimtex = true function luamplib.maketext (str, what) if str and str ~= "" then str = protecttexcontents(str) @@ -605,7 +569,7 @@ function luamplib.maketext (str, what) not str:find("\\begin%s*{document}") and not str:find("\\documentstyle"..name_e) and not str:find("\\usepackage"..name_e) then - if luamplib.legacy_verbatimtex then + if luamplib.legacyverbatimtex then if luamplib.in_the_fig then return process_verbatimtex_infig(str) else @@ -623,10 +587,10 @@ function luamplib.maketext (str, what) end local function colorsplit (res) - local t, tt = { }, res:gsub("[%[%]]",""):explode() + local t, tt = { }, res:gsub("[%[%]]","",2):explode() local be = tt[1]:find"^%d" and 1 or 2 for i=be, #tt do - if tt[i]:find"^%a" then break end + if not tonumber(tt[i]) then break end t[#t+1] = tt[i] end return t @@ -661,28 +625,21 @@ luamplib.shadecolor = function (str) run_tex_code({ [[\color_export:nnN{]], str, [[}{backend}\mplib_@tempa]], },ccexplat) - local name = get_macro'mplib_@tempa':match'{(.-)}{.+}' + local name, value = get_macro'mplib_@tempa':match'{(.-)}{(.-)}' local t, obj = res:explode() if pdfmode then - obj = t[1]:match"^/(.+)" - if ltx.pdf and ltx.pdf.object_id then - obj = format("%s 0 R", ltx.pdf.object_id(obj)) - else - run_tex_code({ - [[\edef\mplib_@tempa{\pdf_object_ref:n{]], obj, "}}", - },ccexplat) - obj = get_macro'mplib_@tempa' - end + obj = format("%s 0 R", ltx.pdf.object_id( t[1]:sub(2,-1) )) else obj = t[2] end - local value = t[3]:match"%[(.-)%]" or t[3] - return format('(%s) withprescript"mplib_spotcolor=%s:%s"', value,obj,name) + return format('(1) withprescript"mplib_spotcolor=%s:%s:%s"', value,obj,name) end return colorsplit(res) end -local running = -1073741824 +local decimals = "%.%d+" +local function rmzeros(str) return str:gsub("%.?0+$","") end + local emboldenfonts = { } local function getemboldenwidth (curr, fakebold) local width = emboldenfonts.width @@ -715,13 +672,14 @@ local function getrulewhatsit (line, wd, ht, dp) fmt = "pdf:content "..fmt pl = node.new("whatsit","special") end - pl.data = fmt:format(line, 0, -dp, wd, ht+dp, "B") + pl.data = fmt:format(line, 0, -dp, wd, ht+dp, "B") :gsub(decimals,rmzeros) local ss = node.new"glue" node.setglue(ss, 0, 65536, 65536, 2, 2) pl.next = ss return pl end local function getrulemetric (box, curr, bp) + local running = -1073741824 local wd,ht,dp = curr.width, curr.height, curr.depth wd = wd == running and box.width or wd ht = ht == running and box.height or ht @@ -773,16 +731,16 @@ local function embolden (box, curr, fakebold) head, curr = node.remove(head, curr) elseif curr.id == node.id"glyph" and curr.font > 0 then local f = curr.font - local i = emboldenfonts[f] + local key = format("%s:%s",f,fakebold) + local i = emboldenfonts[key] if not i then + local ft = font.getfont(f) or font.getcopy(f) if pdfmode then - local ft = font.getcopy(f) width = ft.size * fakebold / factor * 10 emboldenfonts.width = width ft.mode, ft.width = 2, width i = font.define(ft) else - local ft = font.getfont(f) or font.getcopy(f) if ft.format ~= "opentype" and ft.format ~= "truetype" then goto skip_type1 end @@ -790,7 +748,7 @@ local function embolden (box, curr, fakebold) name = format('%s;embolden=%s;',name,fakebold) _, i = fonts.constructors.readanddefine(name,ft.size) end - emboldenfonts[f] = i + emboldenfonts[key] = i end curr.font = i end @@ -802,6 +760,9 @@ end local function graphictextcolor (col, filldraw) if col:find"^[%d%.:]+$" then col = col:explode":" + for i=1,#col do + col[i] = format("%.3f", col[i]) + end if pdfmode then local op = #col == 4 and "k" or #col == 3 and "rg" or "g" col[#col+1] = filldraw == "fill" and op or op:upper() @@ -824,6 +785,7 @@ end luamplib.graphictext = function (text, fakebold, fc, dc) local fmt = process_tex_text(text):sub(1,-2) local id = tonumber(fmt:match"mplibtexboxid=(%d+):") + emboldenfonts.width = nil local box = texgetbox(id) box.head = embolden(box, box.head, fakebold) local fill = graphictextcolor(fc,"fill") @@ -975,7 +937,7 @@ function luamplib.glyph (f, c) if not gid then return mperr"cannot get GID (glyph id)" end local fac = 1000 / (shapedata.units or 1000) local t = shapedata.glyphs[gid].segments - if not t then return "image(fill fullcircle scaled 0;)" end + if not t then return "image()" end for i,v in ipairs(t) do if type(v) == "table" then for ii,vv in ipairs(v) do @@ -989,8 +951,8 @@ function luamplib.glyph (f, c) return glyphimage(t, kind) end -local rulefmt = "mplibpic[%i]:=image(addto currentpicture contour \z -unitsquare shifted - center unitsquare;) xscaled %f yscaled %f shifted (%f,%f);" +local rulefmt = "mpliboutlinepic[%i]:=image(addto currentpicture contour \z + unitsquare shifted - center unitsquare;) xscaled %f yscaled %f shifted (%f,%f);" local outline_horz, outline_vert function outline_vert (res, box, curr, xshift, yshift) local b2u = box.dir == "LTL" @@ -1108,6 +1070,18 @@ function outline_horz (res, box, curr, xshift, yshift, discwd) dx = dx - (r2l and curr.width/factor*expand or 0) local xpos = dx + xshift + (curr.xoffset or 0)/factor local ypos = yshift + (curr.yoffset or 0)/factor + local vertical = ft.shared and ft.shared.features.vertical and "rotated 90" or "" + if vertical ~= "" then -- luatexko + for _,v in ipairs(ft.characters[curr.char].commands or { }) do + if v[1] == "down" then + ypos = ypos - v[2] / factor + elseif v[1] == "right" then + xpos = xpos + v[2] / factor + else + break + end + end + end local image if ft.format == "opentype" or ft.format == "truetype" then image = luamplib.glyph(curr.font, gid) @@ -1127,8 +1101,8 @@ function outline_horz (res, box, curr, xshift, yshift, discwd) end image = format("glyph %s of %q scaled %f", gid, name, scale) end - res[#res+1] = format("mplibpic[%i]:=%s xscaled %f yscaled %f slanted %f shifted (%f,%f);", - #res+1, image, xscale, yscale, slant, xpos, ypos) + res[#res+1] = format("mpliboutlinepic[%i]:=%s xscaled %f yscaled %f slanted %f %s shifted (%f,%f);", + #res+1, image, xscale, yscale, slant, vertical, xpos, ypos) dx = dx + (r2l and 0 or curr.width/factor*expand) elseif curr.replace then local width = node.dimensions(curr.replace)/factor @@ -1213,12 +1187,8 @@ function luamplib.outlinetext (text) local id = tonumber(fmt:match"mplibtexboxid=(%d+):") local box = texgetbox(id) local res = outline_horz({ }, box, box.head, 0, 0) - if #res == 0 then res = { "mplibpic[1]:=image(fill fullcircle scaled 0;);" } end - local t = { } - for i=1, #res do - t[#t+1] = format("addto currentpicture also mplibpic[%i];", i) - end - return tableconcat(res) .. format("mplibpic[0]:=image(%s);", tableconcat(t)) + if #res == 0 then res = { "mpliboutlinepic[1]:=image();" } end + return tableconcat(res) .. format("mpliboutlinenum:=%i;", #res) end luamplib.preambles = { @@ -1322,6 +1292,9 @@ def mplib_do_outline_text_set_f (text f) text r = def mplib_do_outline_options_f = f enddef; def mplib_do_outline_options_r = r enddef; enddef; +def mplib_do_outline_text_set_u (text f) text r = + def mplib_do_outline_options_f = f enddef; +enddef; def mplib_do_outline_text_set_d (text d) text r = def mplib_do_outline_options_d = d enddef; def mplib_do_outline_options_r = r enddef; @@ -1335,27 +1308,40 @@ def mplib_do_outline_text_set_n text r = def mplib_do_outline_options_r = r enddef; enddef; def mplib_do_outline_text_set_p = enddef; -def mplib_fill_outline_text (expr p) = - i:=0; - for item within p: - i:=i+1; - addto currentpicture contour pathpart item - if i < length p: withpostscript "collect"; fi +def mplib_fill_outline_text = + for n=1 upto mpliboutlinenum: + i:=0; + for item within mpliboutlinepic[n]: + i:=i+1; + fill pathpart item mplib_do_outline_options_f withpen pencircle scaled 0 + if (n<mpliboutlinenum) or (i<length mpliboutlinepic[n]): withpostscript "collect"; fi + endfor endfor - mplib_do_outline_options_f; enddef; -def mplib_draw_outline_text (expr p) = - i:=0; - for item within p: - i:=i+1; - addto currentpicture doublepath pathpart item - if i < length p: withpostscript "collect"; fi +def mplib_draw_outline_text = + for n=1 upto mpliboutlinenum: + for item within mpliboutlinepic[n]: + draw pathpart item mplib_do_outline_options_d; + endfor + endfor +enddef; +def mplib_filldraw_outline_text = + for n=1 upto mpliboutlinenum: + i:=0; + for item within mpliboutlinepic[n]: + i:=i+1; + if (n<mpliboutlinenum) or (i<length mpliboutlinepic[n]): + fill pathpart item mplib_do_outline_options_f withpostscript "collect"; + else: + draw pathpart item mplib_do_outline_options_f withpostscript "both"; + fi + endfor endfor - mplib_do_outline_options_d; enddef; vardef mpliboutlinetext@# (expr t) text rest = save kind; string kind; kind := str @#; - save mplibpic, i; picture mplibpic[]; numeric i; + save i; numeric i; + picture mpliboutlinepic[]; numeric mpliboutlinenum; def mplib_do_outline_options_d = enddef; def mplib_do_outline_options_f = enddef; def mplib_do_outline_options_r = enddef; @@ -1363,31 +1349,120 @@ vardef mpliboutlinetext@# (expr t) text rest = image ( addto currentpicture also image ( if kind = "f": mplib_do_outline_text_set_f rest; - def mplib_do_outline_options_d = withpen pencircle scaled 0 enddef; - mplib_fill_outline_text (mplibpic0); + mplib_fill_outline_text; elseif kind = "d": mplib_do_outline_text_set_d rest; - mplib_draw_outline_text (mplibpic0); + mplib_draw_outline_text; elseif kind = "b": mplib_do_outline_text_set_b rest; - mplib_fill_outline_text (mplibpic0); - mplib_draw_outline_text (mplibpic0); + mplib_fill_outline_text; + mplib_draw_outline_text; elseif kind = "u": - mplib_do_outline_text_set_f rest; - mplib_fill_outline_text (mplibpic0); + mplib_do_outline_text_set_u rest; + mplib_filldraw_outline_text; elseif kind = "r": mplib_do_outline_text_set_r rest; - mplib_draw_outline_text (mplibpic0); - mplib_fill_outline_text (mplibpic0); + mplib_draw_outline_text; + mplib_fill_outline_text; elseif kind = "p": mplib_do_outline_text_set_p; - mplib_draw_outline_text (mplibpic0); + mplib_draw_outline_text; else: mplib_do_outline_text_set_n rest; - mplib_fill_outline_text (mplibpic0); + mplib_fill_outline_text; fi; ) mplib_do_outline_options_r; ) enddef ; +primarydef t withpattern p = + image( + if cycle t: + fill + else: + draw + fi + t withprescript "mplibpattern=" & if numeric p: decimal fi p; ) +enddef; +vardef mplibtransformmatrix (text e) = + save t; transform t; + t = identity e; + runscript("luamplib.transformmatrix = {" + & decimal xxpart t & "," + & decimal yxpart t & "," + & decimal xypart t & "," + & decimal yypart t & "," + & decimal xpart t & "," + & decimal ypart t & "," + & "}"); +enddef; +primarydef p withfademethod s = + if picture p: + image( + draw p; + draw center p withprescript "mplibfadestate=stop"; + ) + else: + p withprescript "mplibfadestate=stop" + fi + withprescript "mplibfadetype=" & s + withprescript "mplibfadebbox=" & + decimal (xpart llcorner p -1/4) & ":" & + decimal (ypart llcorner p -1/4) & ":" & + decimal (xpart urcorner p +1/4) & ":" & + decimal (ypart urcorner p +1/4) +enddef; +def withfadeopacity (expr a,b) = + withprescript "mplibfadeopacity=" & + decimal a & ":" & + decimal b +enddef; +def withfadevector (expr a,b) = + withprescript "mplibfadevector=" & + decimal xpart a & ":" & + decimal ypart a & ":" & + decimal xpart b & ":" & + decimal ypart b +enddef; +let withfadecenter = withfadevector; +def withfaderadius (expr a,b) = + withprescript "mplibfaderadius=" & + decimal a & ":" & + decimal b +enddef; +def withfadebbox (expr a,b) = + withprescript "mplibfadebbox=" & + decimal xpart a & ":" & + decimal ypart a & ":" & + decimal xpart b & ":" & + decimal ypart b +enddef; +primarydef p asgroup s = + image( + draw center p + withprescript "mplibgroupbbox=" & + decimal (xpart llcorner p -1/4) & ":" & + decimal (ypart llcorner p -1/4) & ":" & + decimal (xpart urcorner p +1/4) & ":" & + decimal (ypart urcorner p +1/4) + withprescript "gr_state=start" + withprescript "gr_type=" & s; + draw p; + draw center p withprescript "gr_state=stop"; + ) +enddef; +def withgroupbbox (expr a,b) = + withprescript "mplibgroupbbox=" & + decimal xpart a & ":" & + decimal ypart a & ":" & + decimal xpart b & ":" & + decimal ypart b +enddef; +def withgroupname expr s = + withprescript "mplibgroupname=" & s +enddef; +def usemplibgroup primary s = + draw maketext("\usemplibgroup{" & s & "}") + shifted runscript("return luamplib.trgroupshifts['" & s & "']") +enddef; ]], legacyverbatimtex = [[ def specialVerbatimTeX (text t) = runscript("luamplibprefig{"&t&"}") enddef; @@ -1413,7 +1488,6 @@ enddef; } luamplib.verbatiminput = false - local function protect_expansion (str) if str then str = str:gsub("\\","!!!Control!!!") @@ -1424,7 +1498,6 @@ local function protect_expansion (str) return format("\\unexpanded{%s}",str) end end - local function unprotect_expansion (str) if str then return str:gsub("!!!Control!!!", "\\") @@ -1434,22 +1507,17 @@ local function unprotect_expansion (str) :gsub("!!!RBrace!!!", "}") end end - luamplib.everymplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end }) luamplib.everyendmplib = setmetatable({ [""] = "" },{ __index = function(t) return t[""] end }) - function luamplib.process_mplibcode (data, instancename) texboxes.localid = 4096 - - if luamplib.legacy_verbatimtex then + if luamplib.legacyverbatimtex then luamplib.figid, tex_code_pre_mplib = 1, {} end - local everymplib = luamplib.everymplib[instancename] local everyendmplib = luamplib.everyendmplib[instancename] data = format("\n%s\n%s\n%s\n",everymplib, data, everyendmplib) :gsub("\r","\n") - if luamplib.verbatiminput then data = data:gsub("\\mpcolor%s+(.-%b{})","mplibcolor(\"%1\")") :gsub("\\mpdim%s+(%b{})", "mplibdimen(\"%1\")") @@ -1478,163 +1546,42 @@ function luamplib.process_mplibcode (data, instancename) return format("verbatimtex %s etex", unprotect_expansion(str)) end) end - process(data, instancename) end -local further_split_keys = { - mplibtexboxid = true, - sh_color_a = true, - sh_color_b = true, -} local function script2table(s) local t = {} for _,i in ipairs(s:explode("\13+")) do local k,v = i:match("(.-)=(.*)") -- v may contain = or empty. if k and v and k ~= "" and not t[k] then - if further_split_keys[k] or further_split_keys[k:sub(1,10)] then - t[k] = v:explode(":") - else - t[k] = v - end + t[k] = v end end return t end -local function getobjects(result,figure,f) - return figure:objects() -end - -function luamplib.convert (result, flusher) - luamplib.flush(result, flusher) - return true -- done -end - local figcontents = { post = { } } local function put2output(a,...) figcontents[#figcontents+1] = type(a) == "string" and format(a,...) or a end - local function pdf_startfigure(n,llx,lly,urx,ury) put2output("\\mplibstarttoPDF{%f}{%f}{%f}{%f}",llx,lly,urx,ury) end - local function pdf_stopfigure() put2output("\\mplibstoptoPDF") end - -local function pdf_literalcode (fmt,...) - put2output{-2, format(fmt,...)} -end - -local function pdf_textfigure(font,size,text,width,height,depth) - text = text:gsub(".",function(c) - return format("\\hbox{\\char%i}",string.byte(c)) -- kerning happens in metapost : false - end) - put2output("\\mplibtextext{%s}{%f}{%s}{%s}{%s}",font,size,text,0,0) -end - -local bend_tolerance = 131/65536 - -local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1 - -local function pen_characteristics(object) - local t = mplib.pen_info(object) - rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty - divider = sx*sy - rx*ry - return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), t.width -end - -local function concat(px, py) -- no tx, ty here - return (sy*px-ry*py)/divider,(sx*py-rx*px)/divider -end - -local function curved(ith,pth) - local d = pth.left_x - ith.right_x - if abs(ith.right_x - ith.x_coord - d) <= bend_tolerance and abs(pth.x_coord - pth.left_x - d) <= bend_tolerance then - d = pth.left_y - ith.right_y - if abs(ith.right_y - ith.y_coord - d) <= bend_tolerance and abs(pth.y_coord - pth.left_y - d) <= bend_tolerance then - return false - end - end - return true -end - -local function flushnormalpath(path,open) - local pth, ith - for i=1,#path do - pth = path[i] - if not ith then - pdf_literalcode("%f %f m",pth.x_coord,pth.y_coord) - elseif curved(ith,pth) then - pdf_literalcode("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord) - else - pdf_literalcode("%f %f l",pth.x_coord,pth.y_coord) - end - ith = pth - end - if not open then - local one = path[1] - if curved(pth,one) then - pdf_literalcode("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord ) - else - pdf_literalcode("%f %f l",one.x_coord,one.y_coord) - end - elseif #path == 1 then -- special case .. draw point - local one = path[1] - pdf_literalcode("%f %f l",one.x_coord,one.y_coord) - end -end - -local function flushconcatpath(path,open) - pdf_literalcode("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty) - local pth, ith - for i=1,#path do - pth = path[i] - if not ith then - pdf_literalcode("%f %f m",concat(pth.x_coord,pth.y_coord)) - elseif curved(ith,pth) then - local a, b = concat(ith.right_x,ith.right_y) - local c, d = concat(pth.left_x,pth.left_y) - pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(pth.x_coord, pth.y_coord)) - else - pdf_literalcode("%f %f l",concat(pth.x_coord, pth.y_coord)) - end - ith = pth - end - if not open then - local one = path[1] - if curved(pth,one) then - local a, b = concat(pth.right_x,pth.right_y) - local c, d = concat(one.left_x,one.left_y) - pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(one.x_coord, one.y_coord)) - else - pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord)) - end - elseif #path == 1 then -- special case .. draw point - local one = path[1] - pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord)) - end -end - -local function start_pdf_code() - if pdfmode then - pdf_literalcode("q") - else - put2output"\\special{pdf:bcontent}" - end -end -local function stop_pdf_code() - if pdfmode then - pdf_literalcode("Q") - else - put2output"\\special{pdf:econtent}" - end +local function pdf_literalcode (...) + put2output{ -2, format(...) :gsub(decimals,rmzeros) } end +local start_pdf_code = pdfmode + and function() pdf_literalcode"q" end + or function() put2output"\\special{pdf:bcontent}" end +local stop_pdf_code = pdfmode + and function() pdf_literalcode"Q" end + or function() put2output"\\special{pdf:econtent}" end local function put_tex_boxes (object,prescript) - local box = prescript.mplibtexboxid + local box = prescript.mplibtexboxid:explode":" local n,tw,th = box[1],tonumber(box[2]),tonumber(box[3]) if n and tw and th then local op = object.path @@ -1660,6 +1607,7 @@ end local prev_override_color local function do_preobj_CR(object,prescript) + if object.postscript == "collect" then return end local override = prescript and prescript.mpliboverridecolor if override then if pdfmode then @@ -1687,32 +1635,44 @@ end local pdfmanagement = is_defined'pdfmanagement_add:nnn' local pdfobjs, pdfetcs = {}, {} pdfetcs.pgfextgs = "pgf@sys@addpdfresource@extgs@plain" - -local function update_pdfobjs (os) - local on = pdfobjs[os] +pdfetcs.pgfpattern = "pgf@sys@addpdfresource@patterns@plain" +pdfetcs.pgfcolorspace = "pgf@sys@addpdfresource@colorspaces@plain" +local function update_pdfobjs (os, stream) + local key = os + if stream then key = key..stream end + local on = pdfobjs[key] if on then return on,false end if pdfmode then - on = pdf.immediateobj(os) + if stream then + on = pdf.immediateobj("stream",stream,os) + else + on = pdf.immediateobj(os) + end else on = pdfetcs.cnt or 1 - texsprint(format("\\special{pdf:obj @mplibpdfobj%s %s}",on,os)) + if stream then + texsprint(format("\\special{pdf:stream @mplibpdfobj%s (%s) <<%s>>}",on,stream,os)) + else + texsprint(format("\\special{pdf:obj @mplibpdfobj%s %s}",on,os)) + end pdfetcs.cnt = on + 1 end - pdfobjs[os] = on + pdfobjs[key] = on return on,true end - +pdfetcs.resfmt = pdfmode and "%s 0 R" or "@mplibpdfobj%s" if pdfmode then pdfetcs.getpageres = pdf.getpageresources or function() return pdf.pageresources end - pdfetcs.setpageres = pdf.setpageresources or function(s) pdf.pageresources = s end - pdfetcs.initialize_resources = function (name) + local getpageres = pdfetcs.getpageres + local setpageres = pdf.setpageresources or function(s) pdf.pageresources = s end + local initialize_resources = function (name) local tabname = format("%s_res",name) pdfetcs[tabname] = { } if luatexbase.callbacktypes.finish_pdffile then -- ltluatex local obj = pdf.reserveobj() - pdfetcs.setpageres(format("%s/%s %i 0 R", pdfetcs.getpageres() or "", name, obj)) + setpageres(format("%s/%s %i 0 R", getpageres() or "", name, obj)) luatexbase.add_to_callback("finish_pdffile", function() pdf.immediateobj(obj, format("<<%s>>", tableconcat(pdfetcs[tabname]))) end, @@ -1720,20 +1680,38 @@ if pdfmode then end end pdfetcs.fallback_update_resources = function (name, res) + local tabname = format("%s_res",name) + if not pdfetcs[tabname] then + initialize_resources(name) + end if luatexbase.callbacktypes.finish_pdffile then - local t = pdfetcs[format("%s_res",name)] + local t = pdfetcs[tabname] t[#t+1] = res else - local tpr, n = pdfetcs.getpageres() or "", 0 + local tpr, n = getpageres() or "", 0 tpr, n = tpr:gsub(format("/%s<<",name), "%1"..res) if n == 0 then tpr = format("%s/%s<<%s>>", tpr, name, res) end - pdfetcs.setpageres(tpr) + setpageres(tpr) end end else - texsprint("\\special{pdf:obj @MPlibTr<<>>}","\\special{pdf:obj @MPlibSh<<>>}") + texsprint { + "\\luamplibatfirstshipout{", + "\\special{pdf:obj @MPlibTr<<>>}", + "\\special{pdf:obj @MPlibSh<<>>}", + "\\special{pdf:obj @MPlibCS<<>>}", + "\\special{pdf:obj @MPlibPt<<>>}}", + } + pdfetcs.resadded = { } + pdfetcs.fallback_update_resources = function (name,res,obj) + texsprint{"\\special{pdf:put ", obj, " <<", res, ">>}"} + if not pdfetcs.resadded[name] then + texsprint{"\\luamplibateveryshipout{\\special{pdf:put @resources <</", name, " ", obj, ">>}}"} + pdfetcs.resadded[name] = obj + end + end end local transparancy_modes = { [0] = "Normal", @@ -1743,28 +1721,19 @@ local transparancy_modes = { [0] = "Normal", "Hue", "Saturation", "Color", "Luminosity", "Compatible", } - -local function update_tr_res(mode,opaq) - if pdfetcs.pgfloaded == nil then - pdfetcs.pgfloaded = is_defined(pdfetcs.pgfextgs) - if pdfmode and not pdfmanagement and not pdfetcs.pgfloaded and not is_defined"TRP@list" then - pdfetcs.initialize_resources"ExtGState" - end - end - local os = format("<</BM /%s/ca %.3f/CA %.3f/AIS false>>",mode,opaq,opaq) - local on, new = update_pdfobjs(os) - if not new then return on end +local function add_extgs_resources (on, new) local key = format("MPlibTr%s", on) - local val = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s", on) - if pdfmanagement then - texsprint(ccexplat, - format("\\pdfmanagement_add:nnn{Page/Resources/ExtGState}{%s}{%s}", key, val)) - else - local tr = format("/%s %s", key, val) - if pdfetcs.pgfloaded then - texsprint(format("\\csname %s\\endcsname{%s}", pdfetcs.pgfextgs,tr)) - elseif pdfmode then - if is_defined"TRP@list" then + if new then + local val = format(pdfetcs.resfmt, on) + if pdfmanagement then + texsprint { + "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/ExtGState}{", key, "}{", val, "}" + } + else + local tr = format("/%s %s", key, val) + if is_defined(pdfetcs.pgfextgs) then + texsprint { "\\csname ", pdfetcs.pgfextgs, "\\endcsname{", tr, "}" } + elseif is_defined"TRP@list" then texsprint(catat11,{ [[\if@filesw\immediate\write\@auxout{]], [[\string\g@addto@macro\string\TRP@list{]], @@ -1775,82 +1744,87 @@ local function update_tr_res(mode,opaq) texsprint(catat11,[[\global\TRP@reruntrue]]) end else - pdfetcs.fallback_update_resources("ExtGState", tr) + pdfetcs.fallback_update_resources("ExtGState",tr,"@MPlibTr") end - else - texsprint(format("\\special{pdf:put @MPlibTr<<%s>>}",tr)) - texsprint"\\special{pdf:put @resources<</ExtGState @MPlibTr>>}" end end - return on + return key end - -local function do_preobj_TR(prescript) +local function do_preobj_TR(object,prescript) + if object.postscript == "collect" then return end local opaq = prescript and prescript.tr_transparency - local tron_no if opaq then + local key, on, os, new local mode = prescript.tr_alternative or 1 - mode = transparancy_modes[tonumber(mode)] - tron_no = update_tr_res(mode, opaq) - start_pdf_code() - pdf_literalcode("/MPlibTr%i gs",tron_no) + mode = transparancy_modes[tonumber(mode)] or mode + opaq = format("%.3f", opaq) :gsub(decimals,rmzeros) + for i,v in ipairs{ {mode,opaq},{"Normal",1} } do + os = format("<</BM/%s/ca %s/CA %s/AIS false>>",v[1],v[2],v[2]) + on, new = update_pdfobjs(os) + key = add_extgs_resources(on,new) + if i == 1 then + pdf_literalcode("/%s gs",key) + else + return format("/%s gs",key) + end + end end - return tron_no end local function sh_pdfpageresources(shtype,domain,colorspace,ca,cb,coordinates,steps,fractions) - if pdfmode and not pdfmanagement and not pdfetcs.Shading_res then - pdfetcs.initialize_resources"Shading" + for _,v in ipairs{ca,cb} do + for i,vv in ipairs(v) do + for ii,vvv in ipairs(vv) do + v[i][ii] = tonumber(vvv) and format("%.3f",vvv) or vvv + end + end end - local fun2fmt,os = "<</FunctionType 2/Domain [%s]/C0 [%s]/C1 [%s]/N 1>>" + local fun2fmt,os = "<</FunctionType 2/Domain[%s]/C0[%s]/C1[%s]/N 1>>" if steps > 1 then local list,bounds,encode = { },{ },{ } for i=1,steps do if i < steps then - bounds[i] = fractions[i] or 1 + bounds[i] = format("%.3f", fractions[i] or 1) end encode[2*i-1] = 0 encode[2*i] = 1 os = fun2fmt:format(domain,tableconcat(ca[i],' '),tableconcat(cb[i],' ')) - list[i] = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s",update_pdfobjs(os)) + :gsub(decimals,rmzeros) + list[i] = format(pdfetcs.resfmt, update_pdfobjs(os)) end os = tableconcat { "<</FunctionType 3", - format("/Bounds [%s]", tableconcat(bounds,' ')), - format("/Encode [%s]", tableconcat(encode,' ')), - format("/Functions [%s]", tableconcat(list, ' ')), - format("/Domain [%s]>>", domain), - } + format("/Bounds[%s]", tableconcat(bounds,' ')), + format("/Encode[%s]", tableconcat(encode,' ')), + format("/Functions[%s]", tableconcat(list, ' ')), + format("/Domain[%s]>>", domain), + } :gsub(decimals,rmzeros) else os = fun2fmt:format(domain,tableconcat(ca[1],' '),tableconcat(cb[1],' ')) + :gsub(decimals,rmzeros) end - local objref = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s",update_pdfobjs(os)) + local objref = format(pdfetcs.resfmt, update_pdfobjs(os)) os = tableconcat { format("<</ShadingType %i", shtype), format("/ColorSpace %s", colorspace), format("/Function %s", objref), - format("/Coords [%s]", coordinates), - "/Extend [true true]/AntiAlias true>>", - } + format("/Coords[%s]", coordinates), + "/Extend[true true]/AntiAlias true>>", + } :gsub(decimals,rmzeros) local on, new = update_pdfobjs(os) - if not new then return on end - local key = format("MPlibSh%s", on) - local val = format(pdfmode and "%s 0 R" or "@mplibpdfobj%s", on) - if pdfmanagement then - texsprint(ccexplat, - format("\\pdfmanagement_add:nnn{Page/Resources/Shading}{%s}{%s}", key, val)) - else - local res = format("/%s %s", key, val) - if pdfmode then - pdfetcs.fallback_update_resources("Shading", res) + if new then + local key, val = format("MPlibSh%s", on), format(pdfetcs.resfmt, on) + if pdfmanagement then + texsprint { + "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/Shading}{", key, "}{", val, "}" + } else - texsprint(format("\\special{pdf:put @MPlibSh<<%s>>}", res)) - texsprint"\\special{pdf:put @resources<</Shading @MPlibSh>>}" + local res = format("/%s %s", key, val) + pdfetcs.fallback_update_resources("Shading",res,"@MPlibSh") end end return on end - local function color_normalize(ca,cb) if #cb == 1 then if #ca == 4 then @@ -1862,7 +1836,6 @@ local function color_normalize(ca,cb) cb[1], cb[2], cb[3], cb[4] = 1-cb[1], 1-cb[2], 1-cb[3], 0 end end - pdfetcs.clrspcs = setmetatable({ }, { __index = function(t,names) run_tex_code({ [[\color_model_new:nnn]], @@ -1874,20 +1847,21 @@ pdfetcs.clrspcs = setmetatable({ }, { __index = function(t,names) t[names] = colorspace return colorspace end }) - local function do_preobj_SH(object,prescript) local shade_no local sh_type = prescript and prescript.sh_type - if sh_type then + if not sh_type then + return + else local domain = prescript.sh_domain or "0 1" - local centera = prescript.sh_center_a or "0 0"; centera = centera:explode() - local centerb = prescript.sh_center_b or "0 0"; centerb = centerb:explode() + local centera = (prescript.sh_center_a or "0 0"):explode() + local centerb = (prescript.sh_center_b or "0 0"):explode() local transform = prescript.sh_transform == "yes" local sx,sy,sr,dx,dy = 1,1,1,0,0 if transform then - local first = prescript.sh_first or "0 0"; first = first:explode() - local setx = prescript.sh_set_x or "0 0"; setx = setx:explode() - local sety = prescript.sh_set_y or "0 0"; sety = sety:explode() + local first = (prescript.sh_first or "0 0"):explode() + local setx = (prescript.sh_set_x or "0 0"):explode() + local sety = (prescript.sh_set_y or "0 0"):explode() local x,y = tonumber(setx[1]) or 0, tonumber(sety[1]) or 0 if x ~= 0 and y ~= 0 then local path = object.path @@ -1909,15 +1883,15 @@ local function do_preobj_SH(object,prescript) end end local ca, cb, colorspace, steps, fractions - ca = { prescript.sh_color_a_1 or prescript.sh_color_a or {0} } - cb = { prescript.sh_color_b_1 or prescript.sh_color_b or {1} } + ca = { (prescript.sh_color_a_1 or prescript.sh_color_a or "0"):explode":" } + cb = { (prescript.sh_color_b_1 or prescript.sh_color_b or "1"):explode":" } steps = tonumber(prescript.sh_step) or 1 if steps > 1 then fractions = { prescript.sh_fraction_1 or 0 } for i=2,steps do fractions[i] = prescript[format("sh_fraction_%i",i)] or (i/steps) - ca[i] = prescript[format("sh_color_a_%i",i)] or {0} - cb[i] = prescript[format("sh_color_b_%i",i)] or {1} + ca[i] = (prescript[format("sh_color_a_%i",i)] or "0"):explode":" + cb[i] = (prescript[format("sh_color_b_%i",i)] or "1"):explode":" end end if prescript.mplib_spotcolor then @@ -1926,15 +1900,14 @@ local function do_preobj_SH(object,prescript) local script = object.prescript:explode"\13+" for i=#script,1,-1 do if script[i]:find"mplib_spotcolor" then - local name, value - objref, name = script[i]:match"=(.-):(.+)" - value = script[i+1]:match"=(.+)" + local t, name, value = script[i]:explode"="[2]:explode":" + value, objref, name = t[1], t[2], t[3] if not names[name] then pos = pos+1 names[name] = pos names[#names+1] = name end - local t = { } + t = { } for j=1,names[name] do t[#t+1] = 0 end t[#t+1] = value tableinsert(#ca == #cb and ca or cb, t) @@ -1985,11 +1958,489 @@ local function do_preobj_SH(object,prescript) else err"unknown shading type" end - pdf_literalcode("q /Pattern cs") end return shade_no end +pdfetcs.patterns = { } +local function gather_resources (optres) + local t, do_pattern = { }, not optres + local names = {"ExtGState","ColorSpace","Shading"} + if do_pattern then + names[#names+1] = "Pattern" + end + if pdfmode then + if pdfmanagement then + for _,v in ipairs(names) do + local pp = get_macro(format("g__pdfdict_/g__pdf_Core/Page/Resources/%s_prop",v)) + if pp and pp:find"__prop_pair" then + t[#t+1] = format("/%s %s 0 R", v, ltx.pdf.object_id("__pdf/Page/Resources/"..v)) + end + end + else + local res = pdfetcs.getpageres() or "" + run_tex_code[[\mplibtmptoks\expandafter{\the\pdfvariable pageresources}]] + res = res .. texgettoks'mplibtmptoks' + if do_pattern then return res end + res = res:explode"/+" + for _,v in ipairs(res) do + v = v:match"^%s*(.-)%s*$" + if not v:find"Pattern" and not optres:find(v) then + t[#t+1] = "/" .. v + end + end + end + else + if pdfmanagement then + for _,v in ipairs(names) do + local pp = get_macro(format("g__pdfdict_/g__pdf_Core/Page/Resources/%s_prop",v)) + if pp and pp:find"__prop_pair" then + run_tex_code { + "\\mplibtmptoks\\expanded{{", + format("/%s \\csname pdf_object_ref:n\\endcsname{__pdf/Page/Resources/%s}",v,v), + "}}", + } + t[#t+1] = texgettoks'mplibtmptoks' + end + end + elseif is_defined(pdfetcs.pgfextgs) then + run_tex_code ({ + "\\mplibtmptoks\\expanded{{", + "\\ifpgf@sys@pdf@extgs@exists /ExtGState @pgfextgs\\fi", + "\\ifpgf@sys@pdf@colorspaces@exists /ColorSpace @pgfcolorspaces\\fi", + do_pattern and "\\ifpgf@sys@pdf@patterns@exists /Pattern @pgfpatterns \\fi" or "", + "}}", + }, catat11) + t[#t+1] = texgettoks'mplibtmptoks' + else + for _,v in ipairs(names) do + local vv = pdfetcs.resadded[v] + if vv then + t[#t+1] = format("/%s %s", v, vv) + end + end + end + end + return tableconcat(t) +end +function luamplib.registerpattern ( boxid, name, opts ) + local box = texgetbox(boxid) + local wd = format("%.3f",box.width/factor) + local hd = format("%.3f",(box.height+box.depth)/factor) + info("w/h/d of pattern '%s': %s 0", name, format("%s %s",wd, hd):gsub(decimals,rmzeros)) + if opts.xstep == 0 then opts.xstep = nil end + if opts.ystep == 0 then opts.ystep = nil end + if opts.colored == nil then + opts.colored = opts.coloured + if opts.colored == nil then + opts.colored = true + end + end + if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end + if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end + if opts.matrix and opts.matrix:find"%a" then + local data = format("mplibtransformmatrix(%s);",opts.matrix) + process(data,"@mplibtransformmatrix") + local t = luamplib.transformmatrix + opts.matrix = format("%f %f %f %f", t[1], t[2], t[3], t[4]) + opts.xshift = opts.xshift or format("%f",t[5]) + opts.yshift = opts.yshift or format("%f",t[6]) + end + local attr = { + "/Type/Pattern", + "/PatternType 1", + format("/PaintType %i", opts.colored and 1 or 2), + "/TilingType 2", + format("/XStep %s", opts.xstep or wd), + format("/YStep %s", opts.ystep or hd), + format("/Matrix[%s %s %s]", opts.matrix or "1 0 0 1", opts.xshift or 0, opts.yshift or 0), + } + local optres = opts.resources or "" + optres = optres .. gather_resources(optres) + local patterns = pdfetcs.patterns + if pdfmode then + if opts.bbox then + attr[#attr+1] = format("/BBox[%s]", opts.bbox) + end + attr = tableconcat(attr) :gsub(decimals,rmzeros) + local index = tex.saveboxresource(boxid, attr, optres, true, opts.bbox and 4 or 1) + patterns[name] = { id = index, colored = opts.colored } + else + local cnt = #patterns + 1 + local objname = "@mplibpattern" .. cnt + local metric = format("bbox %s", opts.bbox or format("0 0 %s %s",wd,hd)) + texsprint { + "\\expandafter\\newbox\\csname luamplib.patternbox.", cnt, "\\endcsname", + "\\global\\setbox\\csname luamplib.patternbox.", cnt, "\\endcsname", + "\\hbox{\\unhbox ", boxid, "}\\luamplibatnextshipout{", + "\\special{pdf:bcontent}", + "\\special{pdf:bxobj ", objname, " ", metric, "}", + "\\raise\\dp\\csname luamplib.patternbox.", cnt, "\\endcsname", + "\\box\\csname luamplib.patternbox.", cnt, "\\endcsname", + "\\special{pdf:put @resources <<", optres, ">>}", + "\\special{pdf:exobj <<", tableconcat(attr), ">>}", + "\\special{pdf:econtent}}", + } + patterns[cnt] = objname + patterns[name] = { id = cnt, colored = opts.colored } + end +end +local function pattern_colorspace (cs) + local on, new = update_pdfobjs(format("[/Pattern %s]", cs)) + if new then + local key, val = format("MPlibCS%i",on), format(pdfetcs.resfmt,on) + if pdfmanagement then + texsprint { + "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/ColorSpace}{", key, "}{", val, "}" + } + else + local res = format("/%s %s", key, val) + if is_defined(pdfetcs.pgfcolorspace) then + texsprint { "\\csname ", pdfetcs.pgfcolorspace, "\\endcsname{", res, "}" } + else + pdfetcs.fallback_update_resources("ColorSpace",res,"@MPlibCS") + end + end + end + return on +end +local function do_preobj_PAT(object, prescript) + local name = prescript and prescript.mplibpattern + if not name then return end + local patterns = pdfetcs.patterns + local patt = patterns[name] + local index = patt and patt.id or err("cannot get pattern object '%s'", name) + local key = format("MPlibPt%s",index) + if patt.colored then + pdf_literalcode("/Pattern cs /%s scn", key) + else + local color = prescript.mpliboverridecolor + if not color then + local t = object.color + color = t and #t>0 and luamplib.colorconverter(t) + end + if not color then return end + local cs + if color:find" cs " or color:find"@pdf.obj" then + local t = color:explode() + if pdfmode then + cs = format("%s 0 R", ltx.pdf.object_id( t[1]:sub(2,-1) )) + color = t[3] + else + cs = t[2] + color = t[3]:match"%[(.+)%]" + end + else + local t = colorsplit(color) + cs = #t == 4 and "/DeviceCMYK" or #t == 3 and "/DeviceRGB" or "/DeviceGray" + color = tableconcat(t," ") + end + pdf_literalcode("/MPlibCS%i cs %s /%s scn", pattern_colorspace(cs), color, key) + end + if not patt.done then + local val = pdfmode and format("%s 0 R",index) or patterns[index] + if pdfmanagement then + texsprint { + "\\csname pdfmanagement_add:nnn\\endcsname{Page/Resources/Pattern}{", key, "}{", val, "}" + } + else + local res = format("/%s %s", key, val) + if is_defined(pdfetcs.pgfpattern) then + texsprint { "\\csname ", pdfetcs.pgfpattern, "\\endcsname{", res, "}" } + else + pdfetcs.fallback_update_resources("Pattern",res,"@MPlibPt") + end + end + end + patt.done = true +end + +pdfetcs.fading = { } +local function do_preobj_FADE (object, prescript) + local fd_type = prescript and prescript.mplibfadetype + local fd_stop = prescript and prescript.mplibfadestate + if not fd_type then + return fd_stop -- returns "stop" (if picture) or nil + end + local bbox = prescript.mplibfadebbox:explode":" + local dx, dy = -bbox[1], -bbox[2] + local vec = prescript.mplibfadevector; vec = vec and vec:explode":" + if not vec then + if fd_type == "linear" then + vec = {bbox[1], bbox[2], bbox[3], bbox[2]} -- left to right + else + local centerx, centery = (bbox[1]+bbox[3])/2, (bbox[2]+bbox[4])/2 + vec = {centerx, centery, centerx, centery} -- center for both circles + end + end + local coords = { vec[1]+dx, vec[2]+dy, vec[3]+dx, vec[4]+dy } + if fd_type == "linear" then + coords = format("%f %f %f %f", tableunpack(coords)) + elseif fd_type == "circular" then + local width, height = bbox[3]-bbox[1], bbox[4]-bbox[2] + local radius = (prescript.mplibfaderadius or "0:"..math.sqrt(width^2+height^2)/2):explode":" + tableinsert(coords, 3, radius[1]) + tableinsert(coords, radius[2]) + coords = format("%f %f %f %f %f %f", tableunpack(coords)) + else + err("unknown fading method '%s'", fd_type) + end + fd_type = fd_type == "linear" and 2 or 3 + local opaq = (prescript.mplibfadeopacity or "1:0"):explode":" + local on, os, new + on = sh_pdfpageresources(fd_type, "0 1", "/DeviceGray", {{opaq[1]}}, {{opaq[2]}}, coords, 1) + os = format("<</PatternType 2/Shading %s>>", format(pdfetcs.resfmt, on)) + on = update_pdfobjs(os) + bbox = format("0 0 %f %f", bbox[3]+dx, bbox[4]+dy) + local streamtext = format("q /Pattern cs/MPlibFd%s scn %s re f Q", on, bbox) + :gsub(decimals,rmzeros) + os = format("<</Pattern<</MPlibFd%s %s>>>>", on, format(pdfetcs.resfmt, on)) + on = update_pdfobjs(os) + local resources = format(pdfetcs.resfmt, on) + on = update_pdfobjs"<</S/Transparency/CS/DeviceGray>>" + local attr = tableconcat{ + "/Subtype/Form", + "/BBox[", bbox, "]", + "/Matrix[1 0 0 1 ", format("%f %f", -dx,-dy), "]", + "/Resources ", resources, + "/Group ", format(pdfetcs.resfmt, on), + } :gsub(decimals,rmzeros) + on = update_pdfobjs(attr, streamtext) + os = "<</SMask<</S/Luminosity/G " .. format(pdfetcs.resfmt, on) .. ">>>>" + on, new = update_pdfobjs(os) + local key = add_extgs_resources(on,new) + start_pdf_code() + pdf_literalcode("/%s gs", key) + if fd_stop then return "standalone" end + return "start" +end + +pdfetcs.tr_group = { shifts = { } } +luamplib.trgroupshifts = pdfetcs.tr_group.shifts +local function do_preobj_GRP (object, prescript) + local grstate = prescript and prescript.gr_state + if not grstate then return end + local trgroup = pdfetcs.tr_group + if grstate == "start" then + trgroup.name = prescript.mplibgroupname or "lastmplibgroup" + trgroup.isolated, trgroup.knockout = false, false + for _,v in ipairs(prescript.gr_type:explode",+") do + trgroup[v] = true + end + trgroup.bbox = prescript.mplibgroupbbox:explode":" + put2output[[\begingroup\setbox\mplibscratchbox\hbox\bgroup]] + elseif grstate == "stop" then + local llx,lly,urx,ury = tableunpack(trgroup.bbox) + put2output(tableconcat{ + "\\egroup", + format("\\wd\\mplibscratchbox %fbp", urx-llx), + format("\\ht\\mplibscratchbox %fbp", ury-lly), + "\\dp\\mplibscratchbox 0pt", + }) + local grattr = format("/Group<</S/Transparency/I %s/K %s>>",trgroup.isolated,trgroup.knockout) + local res = gather_resources() + local bbox = format("%f %f %f %f", llx,lly,urx,ury) :gsub(decimals,rmzeros) + if pdfmode then + put2output(tableconcat{ + "\\saveboxresource type 2 attr{/Type/XObject/Subtype/Form/FormType 1", + "/BBox[", bbox, "]", grattr, "} resources{", res, "}\\mplibscratchbox", + [[\setbox\mplibscratchbox\hbox{\useboxresource\lastsavedboxresourceindex}]], + [[\wd\mplibscratchbox 0pt\ht\mplibscratchbox 0pt\dp\mplibscratchbox 0pt]], + [[\box\mplibscratchbox\endgroup]], + "\\expandafter\\xdef\\csname luamplib.group.", trgroup.name, "\\endcsname{", + "\\noexpand\\mplibstarttoPDF{",llx,"}{",lly,"}{",urx,"}{",ury,"}", + "\\useboxresource \\the\\lastsavedboxresourceindex\\noexpand\\mplibstoptoPDF}", + }) + else + trgroup.cnt = (trgroup.cnt or 0) + 1 + local objname = format("@mplibtrgr%s", trgroup.cnt) + put2output(tableconcat{ + "\\special{pdf:bxobj ", objname, " bbox ", bbox, "}", + "\\unhbox\\mplibscratchbox", + "\\special{pdf:put @resources <<", res, ">>}", + "\\special{pdf:exobj <<", grattr, ">>}", + "\\special{pdf:uxobj ", objname, "}\\endgroup", + }) + token.set_macro("luamplib.group."..trgroup.name, tableconcat{ + "\\mplibstarttoPDF{",llx,"}{",lly,"}{",urx,"}{",ury,"}", + "\\special{pdf:uxobj ", objname, "}\\mplibstoptoPDF", + }, "global") + end + trgroup.shifts[trgroup.name] = { llx, lly } + end + return grstate +end +function luamplib.registergroup (boxid, name, opts) + local box = texgetbox(boxid) + local wd, ht, dp = node.getwhd(box) + local res = (opts.resources or "") .. gather_resources() + local attr = { "/Type/XObject/Subtype/Form/FormType 1" } + if type(opts.matrix) == "table" then opts.matrix = tableconcat(opts.matrix," ") end + if type(opts.bbox) == "table" then opts.bbox = tableconcat(opts.bbox," ") end + if opts.matrix and opts.matrix:find"%a" then + local data = format("mplibtransformmatrix(%s);",opts.matrix) + process(data,"@mplibtransformmatrix") + opts.matrix = format("%f %f %f %f %f %f",tableunpack(luamplib.transformmatrix)) + end + local grtype = 3 + if opts.bbox then + attr[#attr+1] = format("/BBox[%s]", opts.bbox) + grtype = 2 + end + if opts.matrix then + attr[#attr+1] = format("/Matrix[%s]", opts.matrix) + grtype = opts.bbox and 4 or 1 + end + if opts.asgroup then + local t = { isolated = false, knockout = false } + for _,v in ipairs(opts.asgroup:explode",+") do t[v] = true end + attr[#attr+1] = format("/Group<</S/Transparency/I %s/K %s>>", t.isolated, t.knockout) + end + local trgroup = pdfetcs.tr_group + trgroup.shifts[name] = { get_macro'MPllx', get_macro'MPlly' } + local whd + if pdfmode then + attr = tableconcat(attr) :gsub(decimals,rmzeros) + local index = tex.saveboxresource(boxid, attr, res, true, grtype) + token.set_macro("luamplib.group."..name, "\\useboxresource "..index, "global") + whd = format("%.3f %.3f 0", wd/factor, (ht+dp)/factor) :gsub(decimals,rmzeros) + else + trgroup.cnt = (trgroup.cnt or 0) + 1 + local objname = format("@mplibtrgr%s", trgroup.cnt) + texsprint { + "\\expandafter\\newbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname", + "\\global\\setbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname", + "\\hbox{\\unhbox ", boxid, "}\\luamplibatnextshipout{", + "\\special{pdf:bcontent}", + "\\special{pdf:bxobj ", objname, " width ", wd, "sp height ", ht, "sp depth ", dp, "sp}", + "\\unhbox\\csname luamplib.groupbox.", trgroup.cnt, "\\endcsname", + "\\special{pdf:put @resources <<", res, ">>}", + "\\special{pdf:exobj <<", tableconcat(attr), ">>}", + "\\special{pdf:econtent}}", + } + token.set_macro("luamplib.group."..name, tableconcat{ + "\\begingroup\\setbox\\mplibscratchbox\\hbox{\\special{pdf:uxobj ", objname, "}}", + "\\wd\\mplibscratchbox ", wd, "sp", + "\\ht\\mplibscratchbox ", ht, "sp", + "\\dp\\mplibscratchbox ", dp, "sp", + "\\box\\mplibscratchbox\\endgroup", + }, "global") + whd = format("%.3f %.3f %.3f", wd/factor, ht/factor, dp/factor) :gsub(decimals,rmzeros) + end + info("w/h/d of group '%s': %s", name, whd) +end + +local function stop_special_effects(fade,opaq,over) + if fade then -- fading + stop_pdf_code() + end + if opaq then -- opacity + pdf_literalcode(opaq) + end + if over then -- color + put2output"\\special{pdf:ec}" + end +end + +local function getobjects(result,figure,f) + return figure:objects() +end + +function luamplib.convert (result, flusher) + luamplib.flush(result, flusher) + return true -- done +end + +local function pdf_textfigure(font,size,text,width,height,depth) + text = text:gsub(".",function(c) + return format("\\hbox{\\char%i}",string.byte(c)) -- kerning happens in metapost : false + end) + put2output("\\mplibtextext{%s}{%f}{%s}{%s}{%s}",font,size,text,0,0) +end + +local bend_tolerance = 131/65536 + +local rx, sx, sy, ry, tx, ty, divider = 1, 0, 0, 1, 0, 0, 1 + +local function pen_characteristics(object) + local t = mplib.pen_info(object) + rx, ry, sx, sy, tx, ty = t.rx, t.ry, t.sx, t.sy, t.tx, t.ty + divider = sx*sy - rx*ry + return not (sx==1 and rx==0 and ry==0 and sy==1 and tx==0 and ty==0), t.width +end + +local function concat(px, py) -- no tx, ty here + return (sy*px-ry*py)/divider,(sx*py-rx*px)/divider +end + +local function curved(ith,pth) + local d = pth.left_x - ith.right_x + if abs(ith.right_x - ith.x_coord - d) <= bend_tolerance and abs(pth.x_coord - pth.left_x - d) <= bend_tolerance then + d = pth.left_y - ith.right_y + if abs(ith.right_y - ith.y_coord - d) <= bend_tolerance and abs(pth.y_coord - pth.left_y - d) <= bend_tolerance then + return false + end + end + return true +end + +local function flushnormalpath(path,open) + local pth, ith + for i=1,#path do + pth = path[i] + if not ith then + pdf_literalcode("%f %f m",pth.x_coord,pth.y_coord) + elseif curved(ith,pth) then + pdf_literalcode("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord) + else + pdf_literalcode("%f %f l",pth.x_coord,pth.y_coord) + end + ith = pth + end + if not open then + local one = path[1] + if curved(pth,one) then + pdf_literalcode("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord ) + else + pdf_literalcode("%f %f l",one.x_coord,one.y_coord) + end + elseif #path == 1 then -- special case .. draw point + local one = path[1] + pdf_literalcode("%f %f l",one.x_coord,one.y_coord) + end +end + +local function flushconcatpath(path,open) + pdf_literalcode("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty) + local pth, ith + for i=1,#path do + pth = path[i] + if not ith then + pdf_literalcode("%f %f m",concat(pth.x_coord,pth.y_coord)) + elseif curved(ith,pth) then + local a, b = concat(ith.right_x,ith.right_y) + local c, d = concat(pth.left_x,pth.left_y) + pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(pth.x_coord, pth.y_coord)) + else + pdf_literalcode("%f %f l",concat(pth.x_coord, pth.y_coord)) + end + ith = pth + end + if not open then + local one = path[1] + if curved(pth,one) then + local a, b = concat(pth.right_x,pth.right_y) + local c, d = concat(one.left_x,one.left_y) + pdf_literalcode("%f %f %f %f %f %f c",a,b,c,d,concat(one.x_coord, one.y_coord)) + else + pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord)) + end + elseif #path == 1 then -- special case .. draw point + local one = path[1] + pdf_literalcode("%f %f l",concat(one.x_coord,one.y_coord)) + end +end + function luamplib.flush (result,flusher) if result then local figures = result.fig @@ -2018,7 +2469,10 @@ function luamplib.flush (result,flusher) local prescript = object.prescript prescript = prescript and script2table(prescript) -- prescript is now a table local cr_over = do_preobj_CR(object,prescript) -- color - local tr_opaq = do_preobj_TR(prescript) -- opacity + local tr_opaq = do_preobj_TR(object,prescript) -- opacity + local fading_ = do_preobj_FADE(object,prescript) -- fading + local trgroup = do_preobj_GRP(object,prescript) -- transparency group + local pattern_ = do_preobj_PAT(object,prescript) -- pattern if prescript and prescript.mplibtexboxid then put_tex_boxes(object,prescript) elseif objecttype == "start_bounds" or objecttype == "stop_bounds" then --skip @@ -2040,7 +2494,7 @@ function luamplib.flush (result,flusher) pdf_literalcode("%f %f %f %f %f %f cm",ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) pdf_textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth) stop_pdf_code() - else + elseif not trgroup and fading_ ~= "stop" then local evenodd, collect, both = false, false, false local postscript = object.postscript if not object.istext then @@ -2064,7 +2518,6 @@ function luamplib.flush (result,flusher) savedhtap[#savedhtap+1] = object.htap or false end else - local shade_no = do_preobj_SH(object,prescript) -- shading local ml = object.miterlimit if ml and ml ~= miterlimit then miterlimit = ml @@ -2106,6 +2559,11 @@ function luamplib.flush (result,flusher) objecttype = 'fill' end end + local shade_no = do_preobj_SH(object,prescript) -- shading + if shade_no then + pdf_literalcode"q /Pattern cs" + objecttype = false + end if transformed then start_pdf_code() end @@ -2126,18 +2584,16 @@ function luamplib.flush (result,flusher) else flushnormalpath(path,open) end - if not shade_no then -- conflict with shading - if objecttype == "fill" then - pdf_literalcode(evenodd and "h f*" or "h f") - elseif objecttype == "outline" then - if both then - pdf_literalcode(evenodd and "h B*" or "h B") - else - pdf_literalcode(open and "S" or "h S") - end - elseif objecttype == "both" then + if objecttype == "fill" then + pdf_literalcode(evenodd and "h f*" or "h f") + elseif objecttype == "outline" then + if both then pdf_literalcode(evenodd and "h B*" or "h B") + else + pdf_literalcode(open and "S" or "h S") end + elseif objecttype == "both" then + pdf_literalcode(evenodd and "h B*" or "h B") end end if transformed then @@ -2177,15 +2633,25 @@ function luamplib.flush (result,flusher) end end if shade_no then -- shading - pdf_literalcode("W n /MPlibSh%s sh Q",shade_no) + pdf_literalcode("W%s n /MPlibSh%s sh Q",evenodd and "*" or "",shade_no) end end end - if tr_opaq then -- opacity - stop_pdf_code() + if fading_ == "start" then + pdfetcs.fading.specialeffects = {fading_, tr_opaq, cr_over} + elseif trgroup == "start" then + pdfetcs.tr_group.specialeffects = {fading_, tr_opaq, cr_over} + elseif fading_ == "stop" then + local se = pdfetcs.fading.specialeffects + if se then stop_special_effects(se[1], se[2], se[3]) end + elseif trgroup == "stop" then + local se = pdfetcs.tr_group.specialeffects + if se then stop_special_effects(se[1], se[2], se[3]) end + else + stop_special_effects(fading_, tr_opaq, cr_over) end - if cr_over then -- color - put2output"\\special{pdf:ec}" + if fading_ or trgroup then -- extgs resetted + miterlimit, linecap, linejoin, dashed = -1, -1, -1, false end end end diff --git a/Master/texmf-dist/tex/luatex/luamplib/luamplib.sty b/Master/texmf-dist/tex/luatex/luamplib/luamplib.sty index 6fa986d62bb..f305eacd4e0 100644 --- a/Master/texmf-dist/tex/luatex/luamplib/luamplib.sty +++ b/Master/texmf-dist/tex/luatex/luamplib/luamplib.sty @@ -8,15 +8,24 @@ %% %% See source file 'luamplib.dtx' for licencing and contact information. %% -\bgroup\expandafter\expandafter\expandafter\egroup -\expandafter\ifx\csname selectfont\endcsname\relax - \input ltluatex -\else +\ifcsname ProvidesPackage\endcsname \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{luamplib} - [2024/05/24 v2.31.1 mplib package for LuaTeX] - \ifx\newluafunction\@undefined + [2024/08/03 v2.34.5 mplib package for LuaTeX] +\fi +\ifdefined\newluafunction\else \input ltluatex +\fi +\ifnum\outputmode=0 + \ifdefined\AddToHookNext + \def\luamplibatnextshipout{\AddToHookNext{shipout/background}} + \def\luamplibatfirstshipout{\AddToHook{shipout/firstpage}} + \def\luamplibateveryshipout{\AddToHook{shipout/background}} + \else + \input atbegshi.sty + \def\luamplibatnextshipout#1{\AtBeginShipoutNext{\AtBeginShipoutAddToBox{#1}}} + \let\luamplibatfirstshipout\AtBeginShipoutFirst + \def\luamplibateveryshipout#1{\AtBeginShipout{\AtBeginShipoutAddToBox{#1}}} \fi \fi \directlua{require("luamplib")} @@ -46,6 +55,69 @@ \catcode`\&=12 \catcode`\$=12 \catcode`\%=12 \catcode`\^^M=12 } \def\mplibputtextbox#1{\vbox to 0pt{\vss\hbox to 0pt{\raise\dp#1\copy#1\hss}}} +\protected\def\usemplibgroup#1{\csname luamplib.group.#1\endcsname} +\protected\def\mplibgroup#1{% + \begingroup + \def\MPllx{0}\def\MPlly{0}% + \def\mplibgroupname{#1}% + \mplibgroupgetnexttok +} +\def\mplibgroupgetnexttok{\futurelet\nexttok\mplibgroupbranch} +\def\mplibgroupskipspace{\afterassignment\mplibgroupgetnexttok\let\nexttok= } +\def\mplibgroupbranch{% + \ifx [\nexttok + \expandafter\mplibgroupopts + \else + \ifx\mplibsptoken\nexttok + \expandafter\expandafter\expandafter\mplibgroupskipspace + \else + \let\mplibgroupoptions\empty + \expandafter\expandafter\expandafter\mplibgroupmain + \fi + \fi +} +\def\mplibgroupopts[#1]{\def\mplibgroupoptions{#1}\mplibgroupmain} +\def\mplibgroupmain{\setbox\mplibscratchbox\hbox\bgroup\ignorespaces} +\protected\def\endmplibgroup{\egroup + \directlua{ luamplib.registergroup( + \the\mplibscratchbox, '\mplibgroupname', {\mplibgroupoptions} + )}% + \endgroup +} +{\def\:{\global\let\mplibsptoken= } \: } +\protected\def\mppattern#1{% + \begingroup + \def\mplibpatternname{#1}% + \mplibpatterngetnexttok +} +\def\mplibpatterngetnexttok{\futurelet\nexttok\mplibpatternbranch} +\def\mplibpatternskipspace{\afterassignment\mplibpatterngetnexttok\let\nexttok= } +\def\mplibpatternbranch{% + \ifx [\nexttok + \expandafter\mplibpatternopts + \else + \ifx\mplibsptoken\nexttok + \expandafter\expandafter\expandafter\mplibpatternskipspace + \else + \let\mplibpatternoptions\empty + \expandafter\expandafter\expandafter\mplibpatternmain + \fi + \fi +} +\def\mplibpatternopts[#1]{% + \def\mplibpatternoptions{#1}% + \mplibpatternmain +} +\def\mplibpatternmain{% + \setbox\mplibscratchbox\hbox\bgroup\ignorespaces +} +\protected\def\endmppattern{% + \egroup + \directlua{ luamplib.registerpattern( + \the\mplibscratchbox, '\mplibpatternname', {\mplibpatternoptions} + )}% + \endgroup +} \def\mpfiginstancename{@mpfig} \protected\def\mpfig{% \begingroup @@ -66,16 +138,16 @@ \long\def\mplibdomainmpfig#1\endmpfig{% \endgroup \directlua{ - local legacy = luamplib.legacy_verbatimtex + local legacy = luamplib.legacyverbatimtex local everympfig = luamplib.everymplib["\mpfiginstancename"] or "" local everyendmpfig = luamplib.everyendmplib["\mpfiginstancename"] or "" - luamplib.legacy_verbatimtex = false + luamplib.legacyverbatimtex = false luamplib.everymplib["\mpfiginstancename"] = "" luamplib.everyendmplib["\mpfiginstancename"] = "" luamplib.process_mplibcode( "beginfig(0) "..everympfig.." "..[===[\unexpanded{#1}]===].." "..everyendmpfig.." endfig;", "\mpfiginstancename") - luamplib.legacy_verbatimtex = legacy + luamplib.legacyverbatimtex = legacy luamplib.everymplib["\mpfiginstancename"] = everympfig luamplib.everyendmplib["\mpfiginstancename"] = everyendmpfig }% @@ -89,14 +161,14 @@ \long\def\mplibdoprempfig#1\endmpfig{% \endgroup \directlua{ - local legacy = luamplib.legacy_verbatimtex + local legacy = luamplib.legacyverbatimtex local everympfig = luamplib.everymplib["\mpfiginstancename"] local everyendmpfig = luamplib.everyendmplib["\mpfiginstancename"] - luamplib.legacy_verbatimtex = false + luamplib.legacyverbatimtex = false luamplib.everymplib["\mpfiginstancename"] = "" luamplib.everyendmplib["\mpfiginstancename"] = "" luamplib.process_mplibcode([===[\unexpanded{#1}]===],"\mpfiginstancename") - luamplib.legacy_verbatimtex = legacy + luamplib.legacyverbatimtex = legacy luamplib.everymplib["\mpfiginstancename"] = everympfig luamplib.everyendmplib["\mpfiginstancename"] = everyendmpfig }% @@ -163,9 +235,9 @@ \def\mpliblegacybehavior#1{\directlua{ local s = string.lower("#1") if s == "enable" or s == "true" or s == "yes" then - luamplib.legacy_verbatimtex = true + luamplib.legacyverbatimtex = true else - luamplib.legacy_verbatimtex = false + luamplib.legacyverbatimtex = false end }} \def\mplibverbatim#1{\directlua{ diff --git a/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-27c.sty b/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-27c.sty index 76ebf8716c8..6ef3ff1751e 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-27c.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-27c.sty @@ -3,7 +3,7 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{luatexja-fontspec-27c}[2024-02-19 fontspec support of LuaTeX-ja] +\ProvidesPackage{luatexja-fontspec-27c}[2024-05-27 fontspec support of LuaTeX-ja] \RequirePackage{l3keys2e} \ExplSyntaxOn @@ -359,10 +359,24 @@ \setfontfamily #1 [#2,#4] {#3} \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn } +\DeclareDocumentCommand \providejfontfamily { m O{} m O{} } { + \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_fontspec_set_family:Nnn + \providefontfamily #1 [#2,#4] {#3} + \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn +} \DeclareDocumentCommand \newjfontface { m O{} m O{} } { \newjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} } +\DeclareDocumentCommand \renewjfontface { m O{} m O{} } { + \renewjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} +\DeclareDocumentCommand \setjfontface { m O{} m O{} } { + \setjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} +\DeclareDocumentCommand \providejfontface { m O{} m O{} } { + \providejfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} \prop_new:N \l__ltj_fontspec_tmp_prop \prop_new:N \g__ltj_fontspec_fontopts_prop diff --git a/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-29c.sty b/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-29c.sty index 1415cee1274..51af33447ce 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-29c.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-29c.sty @@ -3,7 +3,7 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{luatexja-fontspec-29c}[2024-05-05 fontspec support of LuaTeX-ja] +\ProvidesPackage{luatexja-fontspec-29c}[2024-05-27 fontspec support of LuaTeX-ja] \RequirePackage{l3keys2e} \ExplSyntaxOn @@ -359,10 +359,24 @@ \setfontfamily #1 [#2,#4] {#3} \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn } +\DeclareDocumentCommand \providejfontfamily { m O{} m O{} } { + \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_fontspec_set_family:Nnn + \providefontfamily #1 [#2,#4] {#3} + \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn +} \DeclareDocumentCommand \newjfontface { m O{} m O{} } { \newjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} } +\DeclareDocumentCommand \renewjfontface { m O{} m O{} } { + \renewjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} +\DeclareDocumentCommand \setjfontface { m O{} m O{} } { + \setjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} +\DeclareDocumentCommand \providejfontface { m O{} m O{} } { + \providejfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} \prop_new:N \l__ltj_fontspec_tmp_prop \prop_new:N \g__ltj_fontspec_fontopts_prop diff --git a/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-29e.sty b/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-29e.sty index e763d91ad7d..02a79ad6ca2 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-29e.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-fontspec-29e.sty @@ -3,7 +3,7 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{luatexja-fontspec-29e}[2024-05-14 fontspec support of LuaTeX-ja] +\ProvidesPackage{luatexja-fontspec-29e}[2024-05-27 fontspec support of LuaTeX-ja] \RequirePackage{l3keys2e} \ExplSyntaxOn @@ -359,10 +359,24 @@ \setfontfamily #1 [#2,#4] {#3} \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn } +\DeclareDocumentCommand \providejfontfamily { m O{} m O{} } { + \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_fontspec_set_family:Nnn + \providefontfamily #1 [#2,#4] {#3} + \cs_set_eq:NN \fontspec_set_family:Nnn \__ltj_orig_fontspec_set_family:Nnn +} \DeclareDocumentCommand \newjfontface { m O{} m O{} } { \newjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} } +\DeclareDocumentCommand \renewjfontface { m O{} m O{} } { + \renewjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} +\DeclareDocumentCommand \setjfontface { m O{} m O{} } { + \setjfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} +\DeclareDocumentCommand \providejfontface { m O{} m O{} } { + \providejfontfamily #1 [ BoldFont={},ItalicFont={},SmallCapsFont={},#2,#4 ] {#3} +} \prop_new:N \l__ltj_fontspec_tmp_prop \prop_new:N \g__ltj_fontspec_fontopts_prop diff --git a/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-ruby.sty b/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-ruby.sty index d0d8e5488dd..4c64b27d149 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-ruby.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/addons/luatexja-ruby.sty @@ -4,7 +4,7 @@ % LaTeX only! \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{luatexja-ruby}[2023-10-08 v0.62] +\ProvidesPackage{luatexja-ruby}[2024-09-09 v0.7] \RequirePackage{luatexja} %%------------------ @@ -18,9 +18,9 @@ %% 引数:ルビ全角を単位とした実数 %% この文字への pre-, post-intrusion の許容量 \define@key[ltj]{japaram}{rubypreintrusion}{% - \ltj@@set@stack@real{RIPRE}{-0x7FFFFFFF}{0x7FFFFFFF}#1 } + \ltj@@set@stack@real{RIPRE}{-0x7FFFFFFF}{0x7FFFFFFF}#1\relax} \define@key[ltj]{japaram}{rubypostintrusion}{% - \ltj@@set@stack@real{RIPOST}{-0x7FFFFFFF}{0x7FFFFFFF}#1 } + \ltj@@set@stack@real{RIPOST}{-0x7FFFFFFF}{0x7FFFFFFF}#1\relax} \def\ltj@@set@stack@real#1#2#3{% \directlua{luatexja.stack.set_stack_perchar(luatexja.stack_table_index.#1, #2, #3, token.scan_word)}} @@ -29,7 +29,6 @@ \def\ltj@@rkeydef#1{ \define@key[ltj]{ruby}{#1}{\expandafter\def\csname ltj@@rubyip@#1\endcsname{##1}} } -%% attr_ruby_mode %% bit 0: intrusion を有効にするか(1: 有効) %% bit 1: 前後の intrusion 許容量を小さい方に揃える (1: yes) %% bit 2--4: intrusion をどう使って親文字を配置するか @@ -42,10 +41,10 @@ %% ※ 01--100 で,intrusion で賄えきれなかった場合はいつものように伸長する. %% bit 5: 熟語ルビの際の処理方法(0: 常にグループ,1: 可能な限りブロックごとに) %% ↑bit 5 は今は無効 +%% bit 6: 突出部分について lua-ul 有効範囲から除外する処理するか \ltj@@rkeydef{mode} %% intrusion 量強制固定(bit 0, bit 1 より優先,負数で「自動」) %% attr は sp 単位だが,ユーザーはルビ全角単位で指定する -%% attr_ruby_maxprep, attr_ruby_maxpostp \ltj@@rkeydef{pre} \ltj@@rkeydef{post} %% 親文字伸長の際の比,{0}{1}{1} などと0--7 の数 3 つで指定 @@ -54,14 +53,11 @@ \ltj@@rkeydef{stretcheol} % 行末形 \ltj@@rkeydef{stretch} % 行中形 %% ルビが伸長するときの比 {1}{2}{1} などと0--7 の数 3 つで指定 -%% attr_ruby_mode 上位部分 \ltj@@rkeydef{stretchruby} %% ルビ<親のとき,ルビと親文字の端の最大値 -%% attr_ruby_maxmargin %% attr は sp 単位だが,ユーザーは親文字全角単位で指定 \ltj@@rkeydef{maxmargin} %% ルビと親文字の垂直方向の空き -%% attr_ruby_intergap %% attr は sp 単位だが,ユーザーは親文字全角単位で指定 \ltj@@rkeydef{intergap} @@ -92,6 +88,7 @@ \define@boolkey[ltj]{ruby}{intrude_xkanjiskip}[true]{} \define@boolkey[ltj]{ruby}{intrude_kanjiskip}[true]{} +\define@boolkey[ltj]{ruby}{always_highlight_protrusion}[true]{} %%%%%%%% setkeys の別名 \protected\def\ltjsetruby{\setkeys[ltj]{ruby}} @@ -141,7 +138,8 @@ [luatexja.icflag_table.XKANJI_SKIP]=true, [luatexja.icflag_table.XKANJI_SKIP_JFM]=true, \fi - \string} + \string}, + quirk_protrusion = \ifltj@ruby@always_highlight_protrusion false \else true \fi \string} } \egroup @@ -264,7 +262,7 @@ kenten=\textbullet, fontcmd=\relax, ybaseheight=0.88, tbaseheight=0.5, yrubydepth=0.12, trubydepth=0.5, intrude_jfmgk, intrude_kanjiskip, intrude_xkanjiskip, - epsilon=0.0001, + epsilon=0.0001, always_highlight_protrusion=false, } \endinput diff --git a/Master/texmf-dist/tex/luatex/luatexja/lltjext.sty b/Master/texmf-dist/tex/luatex/luatexja/lltjext.sty index a83c9305c93..aebdc3987c5 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/lltjext.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/lltjext.sty @@ -3,7 +3,7 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{lltjext}[2022-08-24 v1.2k-ltj-14 Macros for vertical writing] +\ProvidesPackage{lltjext}[2024-07-23 v1.2k-ltj-16 Macros for vertical writing] \RequirePackage{luatexja} \newcount\ltj@ext@dir \let\ltj@lltjext@orig@tabular=\tabular @@ -27,7 +27,7 @@ \def\p@stabular<#1>#2{% \setlength\dimen@{#2}% \edef\@halignto{to\the\dimen@}\p@tabular<#1>} -\def\p@tabular<#1>{\leavevmode \hbox \bgroup +\def\p@tabular<#1>{\leavevmode\null \hbox \bgroup % fixed for unwanted xkanjiskip \ltj@ext@dir=\ltjgetparameter{direction}\relax $\let\@acol\@tabacol \let\@classz\@tabclassz @@ -78,7 +78,7 @@ \ifhmode \@preamerr\z@ \@@par\fi \@preamble} \def\endarray{\crcr\egroup\egroup\@end@alignbox} -\def\endtabular{\endarray$\egroup} +\def\endtabular{\endarray$\egroup\null} \expandafter \let \csname endtabular*\endcsname = \endtabular % ↓中身\周囲→ yoko tate utod dtou % yoko A* B* B B @@ -541,17 +541,11 @@ \else \let\box@dir\utod \fi \fi -\ifltj@ltfilehook@avail % LaTeX2e >= 2020-10-01 \@defaultunitsset\@picht{#3}\unitlength \@defaultunitsset\@picwd{#2}\unitlength \setbox\@picbox\hbox to\@picwd\bgroup\box@dir \@defaultunitsset\@tempdimc{#4}\unitlength\hskip-\@tempdimc \@defaultunitsset\@tempdimc{#5}\unitlength\lower\@tempdimc\hbox\bgroup -\else % <= 2020-02-02 - \@picwd=#2\unitlength \@picht=#3\unitlength - \setbox\@picbox\hbox to\@picwd\bgroup\box@dir - \hskip-#4\unitlength\lower#5\unitlength\hbox\bgroup -\fi \ltj@@pic@reset@blshift \ignorespaces} \def\endpicture{% @@ -675,6 +669,7 @@ %% Compatibility with array package +\ExplSyntaxOn \def\ltj@lltjext@patch@array{% \patchcmd{\@tabular}{\hbox\bgroup}{% \hbox\bgroup\ltj@ext@dir=\ltjgetparameter{direction}\relax}{}{} @@ -682,10 +677,6 @@ {\ltj@ext@dir=\ltjgetparameter{direction}\relax}{}{} \expandafter\let\csname tabular*\endcsname=\ltj@lltjext@orig@tabularS \patchcmd{\p@stabular}{\p@tabular}{\@tabular}{}{} - \@ifpackagelater{array}{2018/09/13} - {\expandafter\@firstoftwo} - {\expandafter\@secondoftwo} - {% = for 2018/09/13 v2.4i or newer \def\@startpbox##1{\bgroup \color@begingroup \box@dir\adjustbaseline%%% これを追加 @@ -694,16 +685,8 @@ \vrule \@height \ltjgetht\@arstrutbox \@width \z@ \everypar{}}% }% - }{ % = for 2018/04/30 v2.4h or older - \def\@startpbox##1{\bgroup - \box@dir\adjustbaseline%%% これを追加 - \setlength\hsize{##1}\@arrayparboxrestore - \everypar{% - \vrule \@height \ltjgetht\@arstrutbox \@width \z@ - \everypar{}}% - }% - }% \def\@tabarray{\@ifnextchar<\p@tabarray{\p@tabarray<Z>}} +\ifcsname tbl_save_outer_table_cols:\endcsname %%%%%% >=2024-06-01 \def\p@array<##1>[##2]##3{% \ifnum\ltj@ext@dir=0 \ltj@ext@dir=\ltjgetparameter{direction}\relax\fi \let\box@dir\relax @@ -736,20 +719,98 @@ \@depth \arraystretch \ltjgetdp \@tempcnta \@width \z@}% \fork@array@option<##1>[##2]% + \tbl_save_outer_table_cols: \begingroup \@mkpream{##3}% - \xdef\@preamble{\noexpand\ialign \noexpand \@halignto - \bgroup \tabskip \z@skip \@arstrut \@preamble - \tabskip \z@ \cr}% + \tbl_count_table_cols: + \xdef\@preamble{ + \noexpand\ar@ialign + \@halignto + \bgroup \@arstrut + \UseTaggingSocket{tbl/row/begin} + \tbl_init_cell_data_for_row: + \@preamble + \tabskip \z@ \cr}% \endgroup \@arrayleft - \@begin@alignbox\bgroup\box@dir\adjustbaseline - \let\par\@empty - \let\@sharp####\let\protect\relax - \let\\\@arraycr\let\tabularnewline\\\let\par\@empty - \lineskip\z@skip\baselineskip\z@skip\@preamble} - \def\endarray{\crcr\egroup\egroup\@end@alignbox\@arrayright} + \@begin@alignbox + \bgroup + \box@dir\adjustbaseline + \let\@sharp####\let\protect\relax + \lineskip \z@ + \baselineskip \z@ + \mathsurround \z@ + \let\\\@arraycr \let\tabularnewline\\\let\par\@empty + \UseTaggingSocket{tbl/init} + \@preamble + } + \def\endtabular{\endarray$\egroup + \UseTaggingSocket {tbl/hmode/end}\null} + \def\endarray{ + \tbl_crcr:n{endarray} \egroup + \UseTaggingSocket{tbl/finalize} + \tbl_restore_outer_cell_data: + \egroup\@end@alignbox + \@arrayright \gdef\@preamble{}% + } +\else %%%%%% <2024-06-01 + \def\p@array<##1>[##2]##3{% + \ifnum\ltj@ext@dir=0 \ltj@ext@dir=\ltjgetparameter{direction}\relax\fi + \let\box@dir\relax + \if ##1z\relax + \ifnum\ltj@ext@dir=3\relax + \let\box@dir\utod\@tempcnta=\zstrutbox + \fi + \else + \if ##1y\relax + \let\box@dir\yoko\@tempcnta=\ystrutbox + \else\if ##1t\relax + \let\box@dir\tate\@tempcnta=\tstrutbox + \else\if ##1d\relax + \let\box@dir\dtou\@tempcnta=\dstrutbox + \else\if ##1u\relax + \let\box@dir\utod\@tempcnta=\zstrutbox + \fi\fi\fi\fi + \fi + \ifx\box@dir\relax + \ifcase\ltj@ext@dir + \or \let\box@dir\dtou\@tempcnta=\dstrutbox% dtou + \or\or\let\box@dir\tate\@tempcnta=\tstrutbox% tate + \or \let\box@dir\yoko\@tempcnta=\ystrutbox% yoko + \else \let\box@dir\utod\@tempcnta=\zstrutbox% utod + \fi + \fi + \setbox \@arstrutbox \hbox{\box@dir\vrule + \@height \arraystretch + \dimexpr \ltjgetht\@tempcnta+\extrarowheight\relax + \@depth \arraystretch \ltjgetdp \@tempcnta + \@width \z@}% + \fork@array@option<##1>[##2]% + \begingroup + \@mkpream{##3}% + \xdef\@preamble{\noexpand\ialign \@halignto + \bgroup \@arstrut\@preamble \tabskip \z@ \cr}% + \endgroup + \@arrayleft + \@begin@alignbox + \bgroup + \box@dir\adjustbaseline + \let\@sharp####\let\protect\relax + \lineskip \z@ + \baselineskip \z@ + \m@th + \let\\\@arraycr \let\tabularnewline\\\let\par\@empty + \@preamble + } + \def\endtabular{\endarray\m@th$\egroup\null} + \def\endarray{ + \crcr\egroup\egroup\@end@alignbox + \@arrayright \gdef\@preamble{}% + } +\fi + \expandafter \let \csname endtabular*\endcsname = \endtabular } +\ExplSyntaxOff \@ifpackageloaded{array}{% \let\tabular=\ltj@lltjext@orig@tabular \let\array=\ltj@lltjext@orig@array @@ -760,9 +821,4 @@ \ltj@ExecuteAfterPackage{array}{\ltj@lltjext@patch@array} } -%%%%%%%%%%%%%%%% LuaTeX-ja original - -%%\define@key[ltj]{japaram}{autouprightnum}{% %COUNT -%% \ltj@@set@stack{AURN}{0}{10000}\z@#1 } - \endinput diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltj-ivd_aj1.lua b/Master/texmf-dist/tex/luatex/luatexja/ltj-ivd_aj1.lua index de18150d793..bb265c7e548 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltj-ivd_aj1.lua +++ b/Master/texmf-dist/tex/luatex/luatexja/ltj-ivd_aj1.lua @@ -6,6 +6,9 @@ table_ivd_aj1 = { -- # -- # History: -- # +-- # 2022-09-13 Registration of additional sequences in the Adobe-Japan1 +-- # collection. +-- # -- # 2020-11-06 Registration of additional sequences in the MSARG -- # collection. -- # @@ -36,7 +39,7 @@ table_ivd_aj1 = { -- # For more details on the IVD, see UTS #37: -- # https://www.unicode.org/reports/tr37/ -- # --- # Copyright 2006-2020 Unicode, Inc. +-- # Copyright 2006-2022 Unicode, Inc. -- # For terms of use, see: https://www.unicode.org/copyright.html#8 -- # -- # EOF @@ -1952,7 +1955,7 @@ table_ivd_aj1 = { 0x209dbf,0x209db5,0x209db9,0x209dbd,0x209dc9,0x209dda,0x209de0,0x209de3,0x204d07,0x209e0a, 0x209e02,0x209e0d,0x209e1c,0x209e7b,0x222218,0x209e80,0x209e85,0x209e9b,0x22a38c,0x209ebd, 0x22a437,0x209edf,0x209eff,0x209f02,0x204d77,0x209f03,0x209f3a,0x209f3d,0x209f46,0x209f53, - 0x209f55,0x209f58,0x22a5f1,0x209f5d,0x22a602,0x209f69,0x209f6d,0x209f70,0x22a6b2,0x20793a, + 0x209f55,0x209f58,0x22a5f1,0x209f5d,0x22a602,0x209f69,0x209f6d,0x209f70,0x22a6b2,0x231350, 0x203614,0x2038ad,0x203dcc,0x203fdc,0x2045be,0x204610,0x204b7e,0x204c38,0x204ce1,0x204e0c, 0x204e23,0x204e24,0x204e79,0x204ef3,0x204f0c,0x204f19,0x204f2b,0x204f2e,0x204f31,0x204f84, 0x204f9e,0x204fb7,0x205004,0x20500c,0x20504c,0x20505f,0x205062,0x205077,0x20508e,0x20509e, diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua b/Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua index ed7ab6daa8a..6eda9f0fdeb 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua +++ b/Master/texmf-dist/tex/luatex/luatexja/ltj-jfmglue.lua @@ -3,7 +3,7 @@ -- luatexbase.provides_module({ name = 'luatexja.jfmglue', - date = '2022-08-29', + date = '2024-09-01', description = 'Insertion process of JFM glues, [x]kanjiskip and others', }) luatexja.jfmglue = luatexja.jfmglue or {} @@ -741,7 +741,7 @@ local function handle_penalty_jwp() local a = table_current_stack[luatexja.stack_table_index.JWP] if #widow_Bp == 0 then if a~=0 then - local p = node_new(id_penalty, widow_Np.nuc) + local p = node_new(id_penalty, nil, widow_Np.nuc) if a<-10000 then a = -10000 elseif a>10000 then a = 10000 end setpenalty(p, a); head = insert_before(head, widow_Np.first, p) widow_Bp[1]=p; set_attr(p, attr_icflag, KINSOKU) @@ -1059,7 +1059,7 @@ local function handle_np_jachar(mode) real_insert(g) end if mode and Np.kcat%2~=1 then - widow_Np.first, widow_Bp, Bp = Np.first, Bp, widow_Bp + widow_Np.nuc, widow_Np.first, widow_Bp, Bp = Np.nuc, Np.first, Bp, widow_Bp end end diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltj-jfont.lua b/Master/texmf-dist/tex/luatex/luatexja/ltj-jfont.lua index 26c76d88a13..3e685c0b41f 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltj-jfont.lua +++ b/Master/texmf-dist/tex/luatex/luatexja/ltj-jfont.lua @@ -3,7 +3,7 @@ -- luatexbase.provides_module({ name = 'luatexja.jfont', - date = '2023-04-06', + date = '2024-07-25', description = 'Loader for Japanese fonts', }) @@ -434,17 +434,18 @@ do end -- extract jfm_name, jfm_spec and jfm_var -- normalize position of 'jfm=' and 'jfmvar=' keys + local match, sub = string.match, string.sub local function extract_jfm_spec(name) - name = (name:match '^{(.*)}$') or (name:match '^"(.*)"$') or name + name = (match(name,'^{(.*)}$')) or (match(name,'^"(.*)"$')) or name jfm_spec, jfm_var = '', '' - local tmp, index = name:sub(1, 5), 1 + local tmp, index = sub(name,1, 5), 1 if tmp == 'file:' or tmp == 'name:' or tmp == 'psft:' then index = 6 end name = lpegmatch(jf_remainder, name) if jfm_name~='' then if luatexja.jfont.jfm_feature then - local l, t2 = name:sub(-1), {} + local l, t2 = sub(name,-1), {} for i,v in pairs(luatexja.jfont.jfm_feature) do -- print(i,type(v),v) t2[#t2+1] = (v==true) and i @@ -454,7 +455,7 @@ do else jfm_spec = jfm_name end - l = name:sub(-1) + l = sub(name,-1) name = name .. ((l==':' or l==';') and '' or ';') .. 'jfm=' .. jfm_spec if jfm_var~='' then name = name .. ';jfmvar=' .. jfm_var @@ -806,9 +807,9 @@ local list_rotate_glyphs do local ceil = math.ceil -- output of function_uax50.lua - -- UAX#50 for Unicode 14.0.0 + -- UAX#50 for Unicode 15.1.0 -- t[0] = true - local t={ 0, 167, 168, 169, 170, 174, 175, 177, 178, 188, 191, 215, 216, 247, 248, 746, 748, 888, 890, 896, 900, 907, 908, 909, 910, 930, 931, 1328, 1329, 1367, 1369, 1419, 1421, 1424, 1425, 1480, 1488, 1515, 1519, 1525, 1536, 1806, 1807, 1867, 1869, 1970, 1984, 2043, 2045, 2094, 2096, 2111, 2112, 2140, 2142, 2143, 2144, 2155, 2160, 2191, 2192, 2194, 2200, 2436, 2437, 2445, 2447, 2449, 2451, 2473, 2474, 2481, 2482, 2483, 2486, 2490, 2492, 2501, 2503, 2505, 2507, 2511, 2519, 2520, 2524, 2526, 2527, 2532, 2534, 2559, 2561, 2564, 2565, 2571, 2575, 2577, 2579, 2601, 2602, 2609, 2610, 2612, 2613, 2615, 2616, 2618, 2620, 2621, 2622, 2627, 2631, 2633, 2635, 2638, 2641, 2642, 2649, 2653, 2654, 2655, 2662, 2679, 2689, 2692, 2693, 2702, 2703, 2706, 2707, 2729, 2730, 2737, 2738, 2740, 2741, 2746, 2748, 2758, 2759, 2762, 2763, 2766, 2768, 2769, 2784, 2788, 2790, 2802, 2809, 2816, 2817, 2820, 2821, 2829, 2831, 2833, 2835, 2857, 2858, 2865, 2866, 2868, 2869, 2874, 2876, 2885, 2887, 2889, 2891, 2894, 2901, 2904, 2908, 2910, 2911, 2916, 2918, 2936, 2946, 2948, 2949, 2955, 2958, 2961, 2962, 2966, 2969, 2971, 2972, 2973, 2974, 2976, 2979, 2981, 2984, 2987, 2990, 3002, 3006, 3011, 3014, 3017, 3018, 3022, 3024, 3025, 3031, 3032, 3046, 3067, 3072, 3085, 3086, 3089, 3090, 3113, 3114, 3130, 3132, 3141, 3142, 3145, 3146, 3150, 3157, 3159, 3160, 3163, 3165, 3166, 3168, 3172, 3174, 3184, 3191, 3213, 3214, 3217, 3218, 3241, 3242, 3252, 3253, 3258, 3260, 3269, 3270, 3273, 3274, 3278, 3285, 3287, 3293, 3295, 3296, 3300, 3302, 3312, 3313, 3315, 3328, 3341, 3342, 3345, 3346, 3397, 3398, 3401, 3402, 3408, 3412, 3428, 3430, 3456, 3457, 3460, 3461, 3479, 3482, 3506, 3507, 3516, 3517, 3518, 3520, 3527, 3530, 3531, 3535, 3541, 3542, 3543, 3544, 3552, 3558, 3568, 3570, 3573, 3585, 3643, 3647, 3676, 3713, 3715, 3716, 3717, 3718, 3723, 3724, 3748, 3749, 3750, 3751, 3774, 3776, 3781, 3782, 3783, 3784, 3790, 3792, 3802, 3804, 3808, 3840, 3912, 3913, 3949, 3953, 3992, 3993, 4029, 4030, 4045, 4046, 4059, 4096, 4294, 4295, 4296, 4301, 4302, 4304, 4352, 4608, 4681, 4682, 4686, 4688, 4695, 4696, 4697, 4698, 4702, 4704, 4745, 4746, 4750, 4752, 4785, 4786, 4790, 4792, 4799, 4800, 4801, 4802, 4806, 4808, 4823, 4824, 4881, 4882, 4886, 4888, 4955, 4957, 4989, 4992, 5018, 5024, 5110, 5112, 5118, 5120, 5121, 5760, 5789, 5792, 5881, 5888, 5910, 5919, 5943, 5952, 5972, 5984, 5997, 5998, 6001, 6002, 6004, 6016, 6110, 6112, 6122, 6128, 6138, 6144, 6170, 6176, 6265, 6272, 6315, 6400, 6431, 6432, 6444, 6448, 6460, 6464, 6465, 6468, 6510, 6512, 6517, 6528, 6572, 6576, 6602, 6608, 6619, 6622, 6684, 6686, 6751, 6752, 6781, 6783, 6794, 6800, 6810, 6816, 6830, 6832, 6863, 6912, 6989, 6992, 7039, 7040, 7156, 7164, 7224, 7227, 7242, 7245, 7305, 7312, 7355, 7357, 7368, 7376, 7419, 7424, 7958, 7960, 7966, 7968, 8006, 8008, 8014, 8016, 8024, 8025, 8026, 8027, 8028, 8029, 8030, 8031, 8062, 8064, 8117, 8118, 8133, 8134, 8148, 8150, 8156, 8157, 8176, 8178, 8181, 8182, 8191, 8192, 8214, 8215, 8224, 8226, 8240, 8242, 8251, 8253, 8258, 8259, 8263, 8266, 8273, 8274, 8293, 8294, 8306, 8308, 8335, 8336, 8349, 8352, 8385, 8400, 8413, 8417, 8418, 8421, 8433, 8450, 8451, 8458, 8463, 8464, 8467, 8469, 8470, 8472, 8478, 8484, 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8501, 8512, 8517, 8523, 8524, 8526, 8527, 8586, 8588, 8592, 8734, 8735, 8756, 8758, 8960, 8968, 8972, 8992, 8996, 9001, 9003, 9004, 9085, 9115, 9150, 9166, 9167, 9168, 9169, 9180, 9186, 9251, 9252, 9472, 9632, 9754, 9760, 10088, 10102, 10132, 11026, 11056, 11088, 11098, 11124, 11126, 11158, 11160, 11192, 11218, 11219, 11244, 11248, 11264, 11508, 11513, 11558, 11559, 11560, 11565, 11566, 11568, 11624, 11631, 11633, 11647, 11671, 11680, 11687, 11688, 11695, 11696, 11703, 11704, 11711, 11712, 11719, 11720, 11727, 11728, 11735, 11736, 11743, 11744, 11856, 11858, 11870, 12296, 12306, 12308, 12320, 12336, 12337, 12448, 12449, 12540, 12541, 42192, 42540, 42560, 42744, 42752, 42955, 42960, 42962, 42963, 42964, 42965, 42970, 42994, 43053, 43056, 43066, 43072, 43128, 43136, 43206, 43214, 43226, 43232, 43348, 43359, 43360, 43392, 43470, 43471, 43482, 43486, 43519, 43520, 43575, 43584, 43598, 43600, 43610, 43612, 43715, 43739, 43767, 43777, 43783, 43785, 43791, 43793, 43799, 43808, 43815, 43816, 43823, 43824, 43884, 43888, 44014, 44016, 44026, 55296, 57344, 64256, 64263, 64275, 64280, 64285, 64311, 64312, 64317, 64318, 64319, 64320, 64322, 64323, 64325, 64326, 64451, 64467, 64912, 64914, 64968, 64975, 64976, 65008, 65040, 65056, 65072, 65097, 65104, 65112, 65119, 65123, 65127, 65136, 65141, 65142, 65277, 65279, 65280, 65288, 65290, 65293, 65294, 65306, 65311, 65339, 65340, 65341, 65342, 65343, 65344, 65371, 65471, 65474, 65480, 65482, 65488, 65490, 65496, 65498, 65501, 65507, 65508, 65512, 65519, 65529, 65532, 65536, 65548, 65549, 65575, 65576, 65595, 65596, 65598, 65599, 65614, 65616, 65630, 65664, 65787, 65792, 65795, 65799, 65844, 65847, 65935, 65936, 65949, 65952, 65953, 66000, 66046, 66176, 66205, 66208, 66257, 66272, 66300, 66304, 66340, 66349, 66379, 66384, 66427, 66432, 66462, 66463, 66500, 66504, 66518, 66560, 66718, 66720, 66730, 66736, 66772, 66776, 66812, 66816, 66856, 66864, 66916, 66927, 66939, 66940, 66955, 66956, 66963, 66964, 66966, 66967, 66978, 66979, 66994, 66995, 67002, 67003, 67005, 67072, 67383, 67392, 67414, 67424, 67432, 67456, 67462, 67463, 67505, 67506, 67515, 67584, 67590, 67592, 67593, 67594, 67638, 67639, 67641, 67644, 67645, 67647, 67670, 67671, 67743, 67751, 67760, 67808, 67827, 67828, 67830, 67835, 67868, 67871, 67898, 67903, 67904, 68000, 68024, 68028, 68048, 68050, 68100, 68101, 68103, 68108, 68116, 68117, 68120, 68121, 68150, 68152, 68155, 68159, 68169, 68176, 68185, 68192, 68256, 68288, 68327, 68331, 68343, 68352, 68406, 68409, 68438, 68440, 68467, 68472, 68498, 68505, 68509, 68521, 68528, 68608, 68681, 68736, 68787, 68800, 68851, 68858, 68904, 68912, 68922, 69216, 69247, 69248, 69290, 69291, 69294, 69296, 69298, 69376, 69416, 69424, 69466, 69488, 69514, 69552, 69580, 69600, 69623, 69632, 69710, 69714, 69750, 69759, 69827, 69837, 69838, 69840, 69865, 69872, 69882, 69888, 69941, 69942, 69960, 69968, 70007, 70016, 70112, 70113, 70133, 70144, 70162, 70163, 70207, 70272, 70279, 70280, 70281, 70282, 70286, 70287, 70302, 70303, 70314, 70320, 70379, 70384, 70394, 70400, 70404, 70405, 70413, 70415, 70417, 70419, 70441, 70442, 70449, 70450, 70452, 70453, 70458, 70459, 70469, 70471, 70473, 70475, 70478, 70480, 70481, 70487, 70488, 70493, 70500, 70502, 70509, 70512, 70517, 70656, 70748, 70749, 70754, 70784, 70856, 70864, 70874, 71168, 71237, 71248, 71258, 71264, 71277, 71296, 71354, 71360, 71370, 71424, 71451, 71453, 71468, 71472, 71495, 71680, 71740, 71840, 71923, 71935, 71943, 71945, 71946, 71948, 71956, 71957, 71959, 71960, 71990, 71991, 71993, 71995, 72007, 72016, 72026, 72096, 72104, 72106, 72152, 72154, 72165, 72384, 72441, 72704, 72713, 72714, 72759, 72760, 72774, 72784, 72813, 72816, 72848, 72850, 72872, 72873, 72887, 72960, 72967, 72968, 72970, 72971, 73015, 73018, 73019, 73020, 73022, 73023, 73032, 73040, 73050, 73056, 73062, 73063, 73065, 73066, 73103, 73104, 73106, 73107, 73113, 73120, 73130, 73440, 73465, 73648, 73649, 73664, 73714, 73727, 74650, 74752, 74863, 74864, 74869, 74880, 75076, 77712, 77811, 92160, 92729, 92736, 92767, 92768, 92778, 92782, 92863, 92864, 92874, 92880, 92910, 92912, 92918, 92928, 92998, 93008, 93018, 93019, 93026, 93027, 93048, 93053, 93072, 93760, 93851, 93952, 94027, 94031, 94088, 94095, 94112, 113664, 113771, 113776, 113789, 113792, 113801, 113808, 113818, 113820, 113828, 119296, 119366, 119808, 119893, 119894, 119965, 119966, 119968, 119970, 119971, 119973, 119975, 119977, 119981, 119982, 119994, 119995, 119996, 119997, 120004, 120005, 120070, 120071, 120075, 120077, 120085, 120086, 120093, 120094, 120122, 120123, 120127, 120128, 120133, 120134, 120135, 120138, 120145, 120146, 120486, 120488, 120780, 120782, 120832, 122624, 122655, 122880, 122887, 122888, 122905, 122907, 122914, 122915, 122917, 122918, 122923, 123136, 123181, 123184, 123198, 123200, 123210, 123214, 123216, 123536, 123567, 123584, 123642, 123647, 123648, 124896, 124903, 124904, 124908, 124909, 124911, 124912, 124927, 124928, 125125, 125127, 125143, 125184, 125260, 125264, 125274, 125278, 125280, 126065, 126133, 126209, 126270, 126464, 126468, 126469, 126496, 126497, 126499, 126500, 126501, 126503, 126504, 126505, 126515, 126516, 126520, 126521, 126522, 126523, 126524, 126530, 126531, 126535, 126536, 126537, 126538, 126539, 126540, 126541, 126544, 126545, 126547, 126548, 126549, 126551, 126552, 126553, 126554, 126555, 126556, 126557, 126558, 126559, 126560, 126561, 126563, 126564, 126565, 126567, 126571, 126572, 126579, 126580, 126584, 126585, 126589, 126590, 126591, 126592, 126602, 126603, 126620, 126625, 126628, 126629, 126634, 126635, 126652, 126704, 126706, 129024, 129036, 129040, 129096, 129104, 129114, 129120, 129160, 129168, 129198, 129200, 129202, 129792, 129939, 129940, 129995, 130032, 130042, 917505, 917506, 917536, 917632, 917760, 918000 } + local t={ 0, 167, 168, 169, 170, 174, 175, 177, 178, 188, 191, 215, 216, 247, 248, 746, 748, 888, 890, 896, 900, 907, 908, 909, 910, 930, 931, 1328, 1329, 1367, 1369, 1419, 1421, 1424, 1425, 1480, 1488, 1515, 1519, 1525, 1536, 1806, 1807, 1867, 1869, 1970, 1984, 2043, 2045, 2094, 2096, 2111, 2112, 2140, 2142, 2143, 2144, 2155, 2160, 2191, 2192, 2194, 2200, 2436, 2437, 2445, 2447, 2449, 2451, 2473, 2474, 2481, 2482, 2483, 2486, 2490, 2492, 2501, 2503, 2505, 2507, 2511, 2519, 2520, 2524, 2526, 2527, 2532, 2534, 2559, 2561, 2564, 2565, 2571, 2575, 2577, 2579, 2601, 2602, 2609, 2610, 2612, 2613, 2615, 2616, 2618, 2620, 2621, 2622, 2627, 2631, 2633, 2635, 2638, 2641, 2642, 2649, 2653, 2654, 2655, 2662, 2679, 2689, 2692, 2693, 2702, 2703, 2706, 2707, 2729, 2730, 2737, 2738, 2740, 2741, 2746, 2748, 2758, 2759, 2762, 2763, 2766, 2768, 2769, 2784, 2788, 2790, 2802, 2809, 2816, 2817, 2820, 2821, 2829, 2831, 2833, 2835, 2857, 2858, 2865, 2866, 2868, 2869, 2874, 2876, 2885, 2887, 2889, 2891, 2894, 2901, 2904, 2908, 2910, 2911, 2916, 2918, 2936, 2946, 2948, 2949, 2955, 2958, 2961, 2962, 2966, 2969, 2971, 2972, 2973, 2974, 2976, 2979, 2981, 2984, 2987, 2990, 3002, 3006, 3011, 3014, 3017, 3018, 3022, 3024, 3025, 3031, 3032, 3046, 3067, 3072, 3085, 3086, 3089, 3090, 3113, 3114, 3130, 3132, 3141, 3142, 3145, 3146, 3150, 3157, 3159, 3160, 3163, 3165, 3166, 3168, 3172, 3174, 3184, 3191, 3213, 3214, 3217, 3218, 3241, 3242, 3252, 3253, 3258, 3260, 3269, 3270, 3273, 3274, 3278, 3285, 3287, 3293, 3295, 3296, 3300, 3302, 3312, 3313, 3316, 3328, 3341, 3342, 3345, 3346, 3397, 3398, 3401, 3402, 3408, 3412, 3428, 3430, 3456, 3457, 3460, 3461, 3479, 3482, 3506, 3507, 3516, 3517, 3518, 3520, 3527, 3530, 3531, 3535, 3541, 3542, 3543, 3544, 3552, 3558, 3568, 3570, 3573, 3585, 3643, 3647, 3676, 3713, 3715, 3716, 3717, 3718, 3723, 3724, 3748, 3749, 3750, 3751, 3774, 3776, 3781, 3782, 3783, 3784, 3791, 3792, 3802, 3804, 3808, 3840, 3912, 3913, 3949, 3953, 3992, 3993, 4029, 4030, 4045, 4046, 4059, 4096, 4294, 4295, 4296, 4301, 4302, 4304, 4352, 4608, 4681, 4682, 4686, 4688, 4695, 4696, 4697, 4698, 4702, 4704, 4745, 4746, 4750, 4752, 4785, 4786, 4790, 4792, 4799, 4800, 4801, 4802, 4806, 4808, 4823, 4824, 4881, 4882, 4886, 4888, 4955, 4957, 4989, 4992, 5018, 5024, 5110, 5112, 5118, 5120, 5121, 5760, 5789, 5792, 5881, 5888, 5910, 5919, 5943, 5952, 5972, 5984, 5997, 5998, 6001, 6002, 6004, 6016, 6110, 6112, 6122, 6128, 6138, 6144, 6170, 6176, 6265, 6272, 6315, 6400, 6431, 6432, 6444, 6448, 6460, 6464, 6465, 6468, 6510, 6512, 6517, 6528, 6572, 6576, 6602, 6608, 6619, 6622, 6684, 6686, 6751, 6752, 6781, 6783, 6794, 6800, 6810, 6816, 6830, 6832, 6863, 6912, 6989, 6992, 7039, 7040, 7156, 7164, 7224, 7227, 7242, 7245, 7305, 7312, 7355, 7357, 7368, 7376, 7419, 7424, 7958, 7960, 7966, 7968, 8006, 8008, 8014, 8016, 8024, 8025, 8026, 8027, 8028, 8029, 8030, 8031, 8062, 8064, 8117, 8118, 8133, 8134, 8148, 8150, 8156, 8157, 8176, 8178, 8181, 8182, 8191, 8192, 8214, 8215, 8224, 8226, 8240, 8242, 8251, 8253, 8258, 8259, 8263, 8266, 8273, 8274, 8293, 8294, 8306, 8308, 8335, 8336, 8349, 8352, 8385, 8400, 8413, 8417, 8418, 8421, 8433, 8450, 8451, 8458, 8463, 8464, 8467, 8469, 8470, 8472, 8478, 8484, 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8501, 8512, 8517, 8523, 8524, 8526, 8527, 8586, 8588, 8592, 8734, 8735, 8756, 8758, 8960, 8968, 8972, 8992, 8996, 9001, 9003, 9004, 9085, 9115, 9150, 9166, 9167, 9168, 9169, 9180, 9186, 9251, 9252, 9472, 9632, 9754, 9760, 10088, 10102, 10132, 11026, 11056, 11088, 11098, 11124, 11126, 11158, 11160, 11192, 11218, 11219, 11244, 11248, 11264, 11508, 11513, 11558, 11559, 11560, 11565, 11566, 11568, 11624, 11631, 11633, 11647, 11671, 11680, 11687, 11688, 11695, 11696, 11703, 11704, 11711, 11712, 11719, 11720, 11727, 11728, 11735, 11736, 11743, 11744, 11856, 11858, 11870, 12296, 12306, 12308, 12320, 12336, 12337, 12448, 12449, 12540, 12541, 42192, 42540, 42560, 42744, 42752, 42955, 42960, 42962, 42963, 42964, 42965, 42970, 42994, 43053, 43056, 43066, 43072, 43128, 43136, 43206, 43214, 43226, 43232, 43348, 43359, 43360, 43392, 43470, 43471, 43482, 43486, 43519, 43520, 43575, 43584, 43598, 43600, 43610, 43612, 43715, 43739, 43767, 43777, 43783, 43785, 43791, 43793, 43799, 43808, 43815, 43816, 43823, 43824, 43884, 43888, 44014, 44016, 44026, 55296, 57344, 64256, 64263, 64275, 64280, 64285, 64311, 64312, 64317, 64318, 64319, 64320, 64322, 64323, 64325, 64326, 64451, 64467, 64912, 64914, 64968, 64975, 64976, 65008, 65040, 65056, 65072, 65097, 65104, 65112, 65119, 65123, 65127, 65136, 65141, 65142, 65277, 65279, 65280, 65288, 65290, 65293, 65294, 65306, 65311, 65339, 65340, 65341, 65342, 65343, 65344, 65371, 65471, 65474, 65480, 65482, 65488, 65490, 65496, 65498, 65501, 65507, 65508, 65512, 65519, 65529, 65532, 65536, 65548, 65549, 65575, 65576, 65595, 65596, 65598, 65599, 65614, 65616, 65630, 65664, 65787, 65792, 65795, 65799, 65844, 65847, 65935, 65936, 65949, 65952, 65953, 66000, 66046, 66176, 66205, 66208, 66257, 66272, 66300, 66304, 66340, 66349, 66379, 66384, 66427, 66432, 66462, 66463, 66500, 66504, 66518, 66560, 66718, 66720, 66730, 66736, 66772, 66776, 66812, 66816, 66856, 66864, 66916, 66927, 66939, 66940, 66955, 66956, 66963, 66964, 66966, 66967, 66978, 66979, 66994, 66995, 67002, 67003, 67005, 67072, 67383, 67392, 67414, 67424, 67432, 67456, 67462, 67463, 67505, 67506, 67515, 67584, 67590, 67592, 67593, 67594, 67638, 67639, 67641, 67644, 67645, 67647, 67670, 67671, 67743, 67751, 67760, 67808, 67827, 67828, 67830, 67835, 67868, 67871, 67898, 67903, 67904, 68000, 68024, 68028, 68048, 68050, 68100, 68101, 68103, 68108, 68116, 68117, 68120, 68121, 68150, 68152, 68155, 68159, 68169, 68176, 68185, 68192, 68256, 68288, 68327, 68331, 68343, 68352, 68406, 68409, 68438, 68440, 68467, 68472, 68498, 68505, 68509, 68521, 68528, 68608, 68681, 68736, 68787, 68800, 68851, 68858, 68904, 68912, 68922, 69216, 69247, 69248, 69290, 69291, 69294, 69296, 69298, 69373, 69416, 69424, 69466, 69488, 69514, 69552, 69580, 69600, 69623, 69632, 69710, 69714, 69750, 69759, 69827, 69837, 69838, 69840, 69865, 69872, 69882, 69888, 69941, 69942, 69960, 69968, 70007, 70016, 70112, 70113, 70133, 70144, 70162, 70163, 70210, 70272, 70279, 70280, 70281, 70282, 70286, 70287, 70302, 70303, 70314, 70320, 70379, 70384, 70394, 70400, 70404, 70405, 70413, 70415, 70417, 70419, 70441, 70442, 70449, 70450, 70452, 70453, 70458, 70459, 70469, 70471, 70473, 70475, 70478, 70480, 70481, 70487, 70488, 70493, 70500, 70502, 70509, 70512, 70517, 70656, 70748, 70749, 70754, 70784, 70856, 70864, 70874, 71168, 71237, 71248, 71258, 71264, 71277, 71296, 71354, 71360, 71370, 71424, 71451, 71453, 71468, 71472, 71495, 71680, 71740, 71840, 71923, 71935, 71943, 71945, 71946, 71948, 71956, 71957, 71959, 71960, 71990, 71991, 71993, 71995, 72007, 72016, 72026, 72096, 72104, 72106, 72152, 72154, 72165, 72384, 72441, 72448, 72458, 72704, 72713, 72714, 72759, 72760, 72774, 72784, 72813, 72816, 72848, 72850, 72872, 72873, 72887, 72960, 72967, 72968, 72970, 72971, 73015, 73018, 73019, 73020, 73022, 73023, 73032, 73040, 73050, 73056, 73062, 73063, 73065, 73066, 73103, 73104, 73106, 73107, 73113, 73120, 73130, 73440, 73465, 73472, 73489, 73490, 73531, 73534, 73562, 73648, 73649, 73664, 73714, 73727, 74650, 74752, 74863, 74864, 74869, 74880, 75076, 77712, 77811, 92160, 92729, 92736, 92767, 92768, 92778, 92782, 92863, 92864, 92874, 92880, 92910, 92912, 92918, 92928, 92998, 93008, 93018, 93019, 93026, 93027, 93048, 93053, 93072, 93760, 93851, 93952, 94027, 94031, 94088, 94095, 94112, 113664, 113771, 113776, 113789, 113792, 113801, 113808, 113818, 113820, 113828, 119296, 119366, 119488, 119508, 119808, 119893, 119894, 119965, 119966, 119968, 119970, 119971, 119973, 119975, 119977, 119981, 119982, 119994, 119995, 119996, 119997, 120004, 120005, 120070, 120071, 120075, 120077, 120085, 120086, 120093, 120094, 120122, 120123, 120127, 120128, 120133, 120134, 120135, 120138, 120145, 120146, 120486, 120488, 120780, 120782, 120832, 122624, 122655, 122661, 122667, 122880, 122887, 122888, 122905, 122907, 122914, 122915, 122917, 122918, 122923, 122928, 122990, 123023, 123024, 123136, 123181, 123184, 123198, 123200, 123210, 123214, 123216, 123536, 123567, 123584, 123642, 123647, 123648, 124112, 124154, 124896, 124903, 124904, 124908, 124909, 124911, 124912, 124927, 124928, 125125, 125127, 125143, 125184, 125260, 125264, 125274, 125278, 125280, 126065, 126133, 126209, 126270, 126464, 126468, 126469, 126496, 126497, 126499, 126500, 126501, 126503, 126504, 126505, 126515, 126516, 126520, 126521, 126522, 126523, 126524, 126530, 126531, 126535, 126536, 126537, 126538, 126539, 126540, 126541, 126544, 126545, 126547, 126548, 126549, 126551, 126552, 126553, 126554, 126555, 126556, 126557, 126558, 126559, 126560, 126561, 126563, 126564, 126565, 126567, 126571, 126572, 126579, 126580, 126584, 126585, 126589, 126590, 126591, 126592, 126602, 126603, 126620, 126625, 126628, 126629, 126634, 126635, 126652, 126704, 126706, 129024, 129036, 129040, 129096, 129104, 129114, 129120, 129160, 129168, 129198, 129200, 129202, 129792, 129939, 129940, 129995, 130032, 130042, 917505, 917506, 917536, 917632, 917760, 918000 } local function rotate_in_uax50(i) local lo, hi = 1, #t while lo < hi do @@ -902,9 +903,10 @@ do end end + local sub = string.sub local function prepare_extra_data_font(id, res, name) if type(res)=='table' and (res.psname or res.filename) then - if (res.embedding=='no') and (type(name)=='string') and (name:sub(1,5)=='psft:') then + if (res.embedding=='no') and (type(name)=='string') and (sub(name,1,5)=='psft:') then font_extra_info[id] = res.resources.ltj_extra else local bname = res.psname or nameonly(res.filename) diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltj-latex.sty b/Master/texmf-dist/tex/luatex/luatexja/ltj-latex.sty index 7e091d161d1..9f6e4d0a365 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltj-latex.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/ltj-latex.sty @@ -12,45 +12,14 @@ \def\luatexjalatexLoaded{\endinput} \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{ltj-latex}[2022-10-15 LaTeX support of LuaTeX-ja] +\ProvidesPackage{ltj-latex}[2024-05-16 LaTeX support of LuaTeX-ja] \endlinechar=13 %%% LaTeX version detection -\newif\ifltj@ltfilehook@avail -\@ifl@t@r\fmtversion{2020-10-01}{% - \@ifl@t@r\fmtversion{2020-10-02}% - {\ltj@ltfilehook@availtrue} - {% - \ifnum\patch@level<0\relax \ifnum\patch@level<-6 \ltj@ltfilehook@availtrue\fi - \else\ltj@ltfilehook@availtrue\fi - }% -}{} -\@ifl@t@r\fmtversion{2021-05-01}{\count@=1 }{\count@=0 } -\ifnum \count@<1 -%%%% \shipout - \newluafunction\ltj@@lua@shipout@inner - \directlua{ - local t = lua.get_functions_table() - local getbox, setbox = tex.getbox, tex.setbox - local shipout_lthook = luatexja.direction.shipout_lthook - t[\the\ltj@@lua@shipout@inner]=function () - local a = shipout_lthook(getbox(\the\ltj@afbox)) - if a then setbox(\the\ltj@afbox, a) end - end - } - \protected\luadef\ltj@@lua@shipout\ltj@@lua@shipout@inner - \def\ltj@shipout@{\ltj@@lua@shipout\primitive\shipout\box\ltj@afbox}% - \protected\def\ltj@shipout{\ltj@afterbox\ltj@shipout@} - \ifprimitive\shipout\let\shipout\ltj@shipout\fi - \ifcsname tex_shipout:D\endcsname - \expandafter\let\csname tex_shipout:D\endcsname\ltj@shipout - \fi -\else - \directlua{% - luatexbase.add_to_callback('pre_shipout_filter',luatexja.direction.shipout_lthook, 'ltj.direction', 1) - } -\fi +\directlua{% + luatexbase.add_to_callback('pre_shipout_filter',luatexja.direction.shipout_lthook, 'ltj.direction', 1) +} %%% patch for LaTeX \usepackage{lltjfont} @@ -59,36 +28,30 @@ \RequirePackage{lltjp-atbegshi} %%% patching commands -\ifltj@ltfilehook@avail - %% \ltj@ExecuteBeforePackage <package><code> and \ltj@ExecuteAfterPackage <package><code> - %% no-starred version: do nothing if the <package> is loaded - %% starred version: do <code> right away if the <package> is loaded - \def\ltj@ExecuteAfterPackage{\@ifstar\@@ltj@ExecuteAfterPackage@star\@@ltj@ExecuteAfterPackage} - \def\ltj@ExecuteBeforePackage{\@ifstar\@@ltj@ExecuteBeforePackage@star\@@ltj@ExecuteBeforePackage} - \@ifl@t@r\fmtversion{2021-11-15}{% 2021-11-15 - \def\@@ltj@ExecuteAfterPackage@star#1{\AddToHook{package/#1/after}} - \def\@@ltj@ExecuteBeforePackage@star#1{\AddToHook{package/#1/before}} - \long\def\@@ltj@ExecuteAfterPackage#1#2{% - \@ifpackageloaded{#1}{}{\AddToHook{package/#1/after}{#2}}% - } - \long\def\@@ltj@ExecuteBeforePackage#1#2{% - \@ifpackageloaded{#1}{}{\AddToHook{package/#1/before}{#2}}% - } - }{ - \def\@@ltj@ExecuteAfterPackage#1{\AddToHook{package/after/#1}} - \def\@@ltj@ExecuteBeforePackage#1{\AddToHook{package/before/#1}} - \long\def\@@ltj@ExecuteAfterPackage@star#1#2{% - \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteAfterPackage{#1}{#2}}% - } - \long\def\@@ltj@ExecuteBeforePackage@star#1#2{% - \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteBeforePackage{#1}{#2}}% - } +%% \ltj@ExecuteBeforePackage <package><code> and \ltj@ExecuteAfterPackage <package><code> +%% no-starred version: do nothing if the <package> is loaded +%% starred version: do <code> right away if the <package> is loaded +\def\ltj@ExecuteAfterPackage{\@ifstar\@@ltj@ExecuteAfterPackage@star\@@ltj@ExecuteAfterPackage} +\def\ltj@ExecuteBeforePackage{\@ifstar\@@ltj@ExecuteBeforePackage@star\@@ltj@ExecuteBeforePackage} +\@ifl@t@r\fmtversion{2021-11-15}{% 2021-11-15 + \def\@@ltj@ExecuteAfterPackage@star#1{\AddToHook{package/#1/after}} + \def\@@ltj@ExecuteBeforePackage@star#1{\AddToHook{package/#1/before}} + \long\def\@@ltj@ExecuteAfterPackage#1#2{% + \@ifpackageloaded{#1}{}{\AddToHook{package/#1/after}{#2}}% } -\else - \RequirePackage{filehook} - \let\ltj@ExecuteAfterPackage=\AtEndOfPackageFile - \let\ltj@ExecuteBeforePackage=\AtBeginOfPackageFile -\fi + \long\def\@@ltj@ExecuteBeforePackage#1#2{% + \@ifpackageloaded{#1}{}{\AddToHook{package/#1/before}{#2}}% + } +}{ + \def\@@ltj@ExecuteAfterPackage#1{\AddToHook{package/after/#1}} + \def\@@ltj@ExecuteBeforePackage#1{\AddToHook{package/before/#1}} + \long\def\@@ltj@ExecuteAfterPackage@star#1#2{% + \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteAfterPackage{#1}{#2}}% + } + \long\def\@@ltj@ExecuteBeforePackage@star#1#2{% + \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteBeforePackage{#1}{#2}}% + } +} \def\ltj@pkgpatch@im#1{\@ifnextchar[{\ltj@pkgpatch@im@{#1}}{\ltj@pkgpatch@im@{#1}[lltjp-#1]}}%] \def\ltj@pkgpatch@im@#1[#2]{\ltj@ExecuteAfterPackage*{#1}{\ltj@pkgpatch@im@@{#2}}} \def\ltj@pkgpatch@im@@#1{% @@ -101,11 +64,7 @@ } \def\ltj@pkgpatch#1{\@ifnextchar[{\ltj@pkgpatch@{#1}}{\ltj@pkgpatch@{#1}[lltjp-#1]}}%] -\ifltj@ltfilehook@avail - \def\ltj@pkgpatch@@{\AddToHook{begindocument/before}[luatexja.autopatch]} -\else - \def\ltj@pkgpatch@@{\gpreto\@begindocumenthook} -\fi +\def\ltj@pkgpatch@@{\AddToHook{begindocument/before}[luatexja.autopatch]} \def\ltj@pkgpatch@#1[#2]{% \ltj@pkgpatch@@{% \@ifpackageloaded{#1}{\@ifpackageloaded{#2}{}{\RequirePackage{#2}}}{}% @@ -121,18 +80,12 @@ \ltj@pkgpatch{tascmac} \ltj@pkgpatch{ascmac}[lltjp-tascmac] \ltj@pkgpatch@im{stfloats} -\ifltj@ltfilehook@avail\else - \ltj@pkgpatch{preview} %% at \begin{document} - % (preview package has delayed option) -\fi \ltj@pkgpatch{array} %% at \begin{document} \ltj@pkgpatch{collcell} %% at \begin{document} \ltj@pkgpatch@im{unicode-math} \ltj@pkgpatch@im{siunitx} \ltj@pkgpatch@im{microtype} -\ifltj@ltfilehook@avail\else\ltj@pkgpatch@im{everyshi}\fi - \RequirePackage{lltjp-geometry} \ltj@ExecuteBeforePackage{breqn}{% diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltj-pretreat.lua b/Master/texmf-dist/tex/luatex/luatexja/ltj-pretreat.lua index 43ecac806c2..8f5632be4e3 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltj-pretreat.lua +++ b/Master/texmf-dist/tex/luatex/luatexja/ltj-pretreat.lua @@ -156,16 +156,17 @@ function set_box_stack_level(head, mode) if ltjs.list_dir == dir_tate then for p in traverse_glyph(to_direct(head)) do if getlang(p)==lang_ja and has_attr(p, attr_icflag, 0) then - local pf = ltjf_replace_altfont(attr_curtfnt, getchar(p), p) + local pc = ltjs_orig_char_table[p] or getchar(p) + local pf = ltjf_replace_altfont(attr_curtfnt, pc, p) if ltjf_font_metric_table[pf].vert_activated then - local pc = ltjf_font_metric_table[pf].vform[pc]; if pc then setchar(p, pc) end + pc = ltjf_font_metric_table[pf].vform[pc]; if pc then setchar(p, pc) end end end end else for p in traverse_glyph(to_direct(head)) do if getlang(p)==lang_ja and has_attr(p, attr_icflag, 0) then - ltjf_replace_altfont(attr_curjfnt, getchar(p), p) + ltjf_replace_altfont(attr_curjfnt, ltjs_orig_char_table[p] or getchar(p), p) end end end diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltj-rmlgbm.lua b/Master/texmf-dist/tex/luatex/luatexja/ltj-rmlgbm.lua index 88352dd5b24..23d20be3f36 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltj-rmlgbm.lua +++ b/Master/texmf-dist/tex/luatex/luatexja/ltj-rmlgbm.lua @@ -15,7 +15,8 @@ do end local cidfont_data = {} local cache_chars = {} -local cache_ver = 13 +local cache_ver = 14 +local sub = string.sub local identifiers = fonts.hashes.identifiers local cid_reg, cid_order, cid_supp, cid_name @@ -147,7 +148,7 @@ do descender = 655360*0.12, }, embedding = "no", cache = "yes", factor = 0, hfactor = 0, vfactor = 0, - tounicode = 1, + tounicode = 1, filename="", properties = { language = "dflt", script = "dflt" }, } cidfont_data[cid_name] = k @@ -300,7 +301,6 @@ local function prepare_cid_font(reg, ord) return cidfont_data[cid_name] or read_cid_font() end - local definers = fonts.definers local function mk_rml(name, size, id) local specification = definers.analyze(name,size) @@ -308,8 +308,8 @@ local function mk_rml(name, size, id) specification.detail = specification.detail or '' do local n = specification.name - if n:sub(1,1)=="{" then n=n:sub(2) end - if n:sub(-1)=="}" then n=n:sub(1,-2) end + if sub(n,1,1)=="{" then n=sub(n,2) end + if sub(n,-1)=="}" then n=sub(n,1,-2) end specification.name=n end local fontdata = {} @@ -382,11 +382,11 @@ local function mk_rml(name, size, id) end local function font_callback(name, size, id, fallback) - if name:sub(1,1)=="{" and name:sub(-1)=="}" then name = name:sub(2,-2) end + if sub(name,1,1)=="{" and sub(name,-1)=="}" then name = sub(name,2,-2) end local p = name:find(":") or 0 - if name:sub(1, p-1) == 'psft' then + if sub(name,1, p-1) == 'psft' then local s = "Adobe-Japan1-7" - local basename = name:sub(p+1) + local basename = sub(name,p+1) local p = basename:find(":") local q = basename:find("/[BI][BI]?") if q and p and q<=p then @@ -394,12 +394,12 @@ local function font_callback(name, size, id, fallback) p = basename:find(":") end if p then - local xname = basename:sub(p+1) + local xname = sub(basename,p+1) p = 1 while p do local q = xname:find(";", p+1) or xname:len()+1 - if xname:sub(p, p+3)=='cid=' and q>p+4 then - s = xname:sub(p+4, q-1) + if sub(xname,p, p+3)=='cid=' and q>p+4 then + s = sub(xname,p+4, q-1) end if xname:len()+1==q then p = nil else p = q + 1 end end diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltj-ruby.lua b/Master/texmf-dist/tex/luatex/luatexja/ltj-ruby.lua index 29b6fe19988..2ff41be9474 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltj-ruby.lua +++ b/Master/texmf-dist/tex/luatex/luatexja/ltj-ruby.lua @@ -3,7 +3,7 @@ -- luatexbase.provides_module({ name = 'luatexja.ruby', - date = '2022-12-31', + date = '2024-09-06', description = 'Ruby annotation', }) luatexja.ruby = {} @@ -37,7 +37,7 @@ local setwhd = node.direct.setwhd local setlist = node.direct.setlist local setvalue = node.direct.setdata -local node_new = node.direct.new +local node_new = luatexja.dnode_new local node_remove = node.direct.remove local node_next = node.direct.getnext local node_copy, node_tail = node.direct.copy, node.direct.tail @@ -45,6 +45,7 @@ local node_free = node.direct.flush_node or node.direct.free local get_attr, set_attr = node.direct.get_attribute, node.direct.set_attribute local insert_before, insert_after = node.direct.insert_before, node.direct.insert_after local hpack = node.direct.hpack +local node_inherit_attr = luatexja.node_inherit_attr local id_hlist = node.id 'hlist' local id_vlist = node.id 'vlist' @@ -175,10 +176,10 @@ do set_attr(nh, attr_icflag, get_attr_icflag(nh) + PROCESSED_BEGIN_FLAG) setnext(node_tail(h), nh) - setlist(f, nil); node_free(f) + setlist(f, nil) setlist(b, nil); node_free(b) local g = luatexja.jfmglue.main(h,false) - return hpack(g) + g = node_inherit_attr(hpack(g),f); node_free(f); return g else return f end @@ -207,6 +208,7 @@ end -- box の中身のノードは再利用される local enlarge do + local dimensions = node.direct.dimensions local FROM_JFM = luatexja.icflag_table.FROM_JFM local PROCESSED = luatexja.icflag_table.PROCESSED local KANJI_SKIP = luatexja.icflag_table.KANJI_SKIP @@ -239,18 +241,22 @@ do hx = node_next(hx) end -- 先頭の空白を挿入 - local k = node_new(id_glue); - setglue(k, prenw, round(pre*65536), 0, 2, 0) - h = insert_before(h, h, k); + local k1 = node_new(id_glue); + setglue(k1, prenw, round(pre*65536), 0, 2, 0) + h = insert_before(h, h, k1); -- 末尾の空白を挿入 - local k = node_new(id_glue); - setglue(k, postnw, round(post*65536), 0, 2, 0) - insert_after(h, node_tail(h), k); + local k2 = node_new(id_glue); + setglue(k2, postnw, round(post*65536), 0, 2, 0) + insert_after(h, node_tail(h), k2); -- hpack - setlist(box, nil); node_free(box) - box = hpack(h, new_width, 'exactly') - setheight(box, hh); setdepth(box, hd) - return box + setlist(box, nil); + local new_box = node_inherit_attr(hpack(h, new_width, 'exactly'), box) + setheight(new_box, hh); setdepth(new_box, hd) + node_free(box); + local gset, gsign, go + = getfield(new_box,'glue_set'), getfield(new_box,'glue_sign'), getfield(new_box,'glue_order') + return new_box, (dimensions(gset, gsign, go, k1, node_next(k1))), (dimensions(gset, gsign, go, k2)) + -- return value: (enlarged box), (width of left glue), (width of right glue) end end @@ -307,15 +313,18 @@ function luatexja.ruby.texiface(rst, rtlr, rtlp) end ---------------------------------------------------------------- --- pre_line_break +-- pre_linebreak ---------------------------------------------------------------- -- r, p の中身のノードは再利用される local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end) -- r: ルビ部分の格納された box,p: 同,親文字 + -- no_begin: 行頭形ではないか + -- no_end: 行末形ではないか local rwidth = getwidth(r) local sumprot = rwidth - getwidth(p) -- >0 local pre_intrusion, post_intrusion + local pre_protrusion, post_protrusion local ppre, pmid, ppost = tmp_tbl.ppre, tmp_tbl.pmid, tmp_tbl.ppost local mapre, mapost = tmp_tbl.mapre, tmp_tbl.mapost local intmode = (tmp_tbl.mode//4)%8 @@ -325,17 +334,17 @@ local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end) mapre = min(mapre,mapost); mapost = mapre end if intmode == 0 then -- とりあえず組んでから決める - p = enlarge(p, rwidth, ppre, pmid, ppost, 0, 0) + p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, 0, 0) pre_intrusion = min(mapre, round(ppre*getfield(p, 'glue_set')*65536)) post_intrusion = min(mapost, round(ppost*getfield(p, 'glue_set')*65536)) elseif intmode == 1 then pre_intrusion = min(mapre, sumprot); post_intrusion = min(mapost, max(sumprot-pre_intrusion, 0)) - p = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion) + p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion) elseif intmode == 2 then post_intrusion = min(mapost, sumprot); pre_intrusion = min(mapre, max(sumprot-post_intrusion, 0)) - p = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion) + p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion) elseif intmode==3 then local n = min(mapre, mapost)*2 if n < sumprot then @@ -343,7 +352,7 @@ local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end) else pre_intrusion = sumprot//2; post_intrusion = sumprot - pre_intrusion end - p = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion) + p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion) pre_intrusion = min(mapre, pre_intrusion + round(ppre*getfield(p, 'glue_set')*65536)) post_intrusion = min(mapost, post_intrusion + round(ppost*getfield(p, 'glue_set')*65536)) else -- intmode == 4 @@ -354,7 +363,7 @@ local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end) else pre_intrusion, post_intrusion = mapre, min(mapost, sumprot-mapre) end - p = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion) + p, pre_protrusion, post_protrusion = enlarge(p, rwidth, ppre, pmid, ppost, pre_intrusion, post_intrusion) end setshift(r, -pre_intrusion) local rwidth = rwidth - pre_intrusion - post_intrusion @@ -371,24 +380,29 @@ local function enlarge_parent(r, p, tmp_tbl, no_begin, no_end) post_jfmgk = (post_intrusion > 0) end end - return r, p, orig_post_intrusion, post_jfmgk + return r, p, orig_post_intrusion, post_jfmgk, + pre_protrusion - pre_intrusion, post_protrusion - post_intrusion end -- ルビボックスの生成(単一グループ) -- returned value: <new box>, <ruby width>, <post_intrusion> local max_margin -local function new_ruby_box(r, p, tmp_tbl, no_begin, no_end) +local function new_ruby_box(r, p, tmp_tbl, no_begin, no_end, w) + -- no_begin: 行頭形ではないか + -- no_end: 行末形ではないか local post_intrusion, post_jfmgk = 0, false local imode local ppre, pmid, ppost = tmp_tbl.ppre, tmp_tbl.pmid, tmp_tbl.ppost local mapre, mapost = tmp_tbl.mapre, tmp_tbl.mapost local rpre, rmid, rpost, rsmash + local protrusion = {0, 0}; tmp_tbl[tmp_tbl.index] = protrusion imode = tmp_tbl.mode//0x100000; rsmash = (imode%2 ==1) imode = imode//2; rpost = imode%8; imode = (imode-rpost)/8; rmid = imode%8; imode = (imode-rmid)/8; rpre = imode%8 if getwidth(r) > getwidth(p) then -- change the width of p - r, p, post_intrusion, post_jfmgk = enlarge_parent(r, p, tmp_tbl, no_begin, no_end) + r, p, post_intrusion, post_jfmgk, protrusion[1], protrusion[2] + = enlarge_parent(r, p, tmp_tbl, no_begin, no_end) elseif getwidth(r) < getwidth(p) then -- change the width of r r = enlarge(r, getwidth(p), rpre, rmid, rpost, 0, 0) post_intrusion = 0 @@ -406,7 +420,7 @@ local function new_ruby_box(r, p, tmp_tbl, no_begin, no_end) end if need_repack then local rt = r - r = hpack(getlist(r), getwidth(r), 'exactly') + r = node_inherit_attr(hpack(getlist(r), getwidth(r), 'exactly'), r) setlist(rt, nil); node_free(rt); end end @@ -416,7 +430,8 @@ local function new_ruby_box(r, p, tmp_tbl, no_begin, no_end) insert_after(r, k, p); setnext(p, nil) if tmp_tbl.rubydepth >= 0 then setdepth(r, tmp_tbl.rubydepth) end if tmp_tbl.baseheight >= 0 then setheight(p, tmp_tbl.baseheight) end - a = node.direct.vpack(r); setshift(a, 0) + a = node_inherit_attr(node.direct.vpack(r), p) + setshift(a, 0) set_attr(a, attr_ruby, post_intrusion) set_attr(a, attr_ruby_post_jfmgk, post_jfmgk and 1 or 0) if rsmash or getheight(a)<getheight(p) then @@ -437,7 +452,10 @@ local max_allow_pre, max_allow_post local flush_list = node.direct.flush_list -- 中付き熟語ルビ,cmp containers -- 「文字の構成を考えた」やつはどうしよう -local function pre_low_cal_box(w, cmp) +local pre_low_cal_box +do + local node_prev = node.direct.getprev +pre_low_cal_box = function (w, cmp) local rb = {} local pb = {} local kf = {} @@ -450,18 +468,24 @@ local function pre_low_cal_box(w, cmp) local coef = {} -- 連立一次方程式の拡大係数行列 local rtb = expand_3bits(rst.stretch) + local kb, ka + if rst.quirk_protrusion then + kb = node_prev(node_tail(wv)); ka = node_next(kb) + node_remove(wv, kb); node_remove(wv, ka) + end -- node list 展開・行末形の計算 local nt, nta, ntb = wv, nil, nil -- nt*: node temp rst.ppre, rst.pmid, rst.ppost = rtb[6], rtb[5], rtb[4] rst.mapre, rst.mapost = max_allow_pre, 0 - for i = 1, cmp do + for i = 1, cmp do nt = node_next(nt); rb[i] = nt; nta = concat(nta, node_copy(nt)) nt = node_next(nt); pb[i] = nt; ntb = concat(ntb, node_copy(nt)) coef[i] = {} for j = 1, 2*i do coef[i][j] = 1 end for j = 2*i+1, 2*cmp+1 do coef[i][j] = 0 end + rst.index = i kf[i], coef[i][2*cmp+2] - = new_ruby_box(node_copy(nta), node_copy(ntb), rst, true, false) + = new_ruby_box(node_copy(nta), node_copy(ntb), rst, true, false, w) end node_free(nta); node_free(ntb) @@ -474,8 +498,9 @@ local function pre_low_cal_box(w, cmp) for j = 1, 2*i-1 do coef[cmp+i][j] = 0 end for j = 2*i, 2*cmp+1 do coef[cmp+i][j] = 1 end nta = concat(node_copy(rb[i]), nta); ntb = concat(node_copy(pb[i]), ntb) + rst.index = 2*cmp+1-i kf[cmp+i], coef[cmp+i][2*cmp+2] - = new_ruby_box(node_copy(nta), node_copy(ntb), rst, false, true) + = new_ruby_box(node_copy(nta), node_copy(ntb), rst, false, true, w) end -- ここで,nta, ntb には全 container を連結した box が入っているので @@ -484,13 +509,17 @@ local function pre_low_cal_box(w, cmp) for j = 1, 2*cmp+1 do coef[2*cmp+1][j] = 1 end rst.ppre, rst.pmid, rst.ppost = rtb[3], rtb[2], rtb[1] rst.mapre, rst.mapost = max_allow_pre, max_allow_post + rst.index = 2*cmp+1 kf[2*cmp+1], coef[2*cmp+1][2*cmp+2], post_intrusion_backup, post_jfmgk_backup - = new_ruby_box(nta, ntb, rst, true, true) + = new_ruby_box(nta, ntb, rst, true, true, w) -- w.value の node list 更新. local nt = wv flush_list(node_next(wv)) for i = 1, 2*cmp+1 do setnext(nt, kf[i]); nt = kf[i] end + if rst.quirk_protrusion then + insert_after(wv, nt, kb); insert_after(wv, kb, ka); + end if cmp==1 then solve_1(coef) elseif cmp==2 then solve_2(coef) @@ -499,6 +528,7 @@ local function pre_low_cal_box(w, cmp) end return coef end +end local first_whatsit do @@ -512,25 +542,40 @@ do end local next_cluster_array = {} +local pre_high -- ノード追加 -local function pre_low_app_node(head, w, cmp, coef, ht, dp) +do + local node_prev = node.direct.getprev + local KINSOKU = luatexja.icflag_table.KINSOKU + local function pre_low_app_node (head, w, cmp, coef, ht, dp) -- メインの node list 更新 - local nt = node_new(id_glue) + local nt = node_new(id_glue, nil, w) -- INHERIT ATTRIBUTES OF w setglue(nt, coef[1][2*cmp+2], 0, 0, 0, 0) set_attr(nt, attr_ruby, 1); set_attr(w, attr_ruby, 2) head = insert_before(head, w, nt) nt = w for i = 1, cmp do -- rule - local nta = node_new(id_rule, 0); + local nta = node_new(id_rule, 0, w); -- INHERIT ATTRIBUTES OF w setwhd(nta, coef[i*2][2*cmp+2], ht, dp) insert_after(head, nt, nta) set_attr(nta, attr_ruby, 2*i+1) -- glue if i~=cmp or not next_cluster_array[w] then - nt = node_new(id_glue); insert_after(head, nta, nt) + nt = node_new(id_glue, nil, w); -- INHERIT ATTRIBUTE OF w + insert_after(head, nta, nt) else - nt = next_cluster_array[w] + nt = next_cluster_array[w]; local f = node_next(nta) + while f and f~=nt do + if getid(f)==id_glue then + node_remove(head, nt); insert_before(head, f, nt); f = nil + else if getid(f)==id_penalty and get_attr_icflag(f)==KINSOKU then + node_inherit_attr(f, w) -- INHERIT ATTRIBUTE OF w; for lua-ul + set_attr(f, attr_icflag, KINSOKU) + end + f = node_next(f) + end + end end setglue(nt, coef[i*2+1][2*cmp+2], 0, 0, 0, 0) set_attr(nt, attr_ruby, 2*i+2) @@ -540,14 +585,13 @@ local function pre_low_app_node(head, w, cmp, coef, ht, dp) next_cluster_array[w]=nil return head, first_whatsit(node_next(nt)) end - -local function pre_high(ahead) +pre_high = function (ahead) if not ahead then return ahead end local head = to_direct(ahead) post_intrusion_backup, post_jfmgk_backup = 0, false local n = first_whatsit(head) while n do - if getsubtype(n) == sid_user and getfield(n, 'user_id') == RUBY_PRE then + if getsubtype(n) == sid_user and getfield(n, 'user_id') == RUBY_PRE then local nv = getvalue(n) local rst = getvalue(nv) max_allow_pre = rst.pre or 0 @@ -567,13 +611,13 @@ local function pre_high(ahead) if rst.exclude_pre_jfmgk_from_prev_ruby and atr>0 and ((old_break_info[atr] or post_intrusion_backup) > 0) then -- 「直前のルビが文字に進入→現在のルビの和文処理グルーへの進入はなし」という状況 - rst.before_jfmgk = 0 + max_allow_pre = 0;rst.before_jfmgk = 0 end post_intrusion_backup, post_jfmgk_backup = 0, false max_allow_post = rst.post or 0 max_margin = rst.maxmargin or 0 local coef = pre_low_cal_box(n, rst.count) - local s = node_tail(nv) --ルビ文字 + local s = node_tail(nv); if rst.quirk_protrusion then s = node_prev(node_prev(s)) end --ルビ文字 head, n = pre_low_app_node( head, n, rst.count, coef, getheight(s), getdepth(s) ) @@ -583,11 +627,11 @@ local function pre_high(ahead) end return to_node(head) end +end luatexbase.add_to_callback('pre_linebreak_filter', pre_high, 'ltj.ruby.pre', 100) -luatexbase.add_to_callback('hpack_filter', pre_high, 'ltj.ruby.pre', 100) ---------------------------------------------------------------- --- post_line_break +-- post_linebreak ---------------------------------------------------------------- local post_lown do @@ -595,47 +639,110 @@ do local id = get_attr(wv, attr_ruby_id) or 0 if id>0 and cache_handle then cache_handle:write( - 'lrob[' .. tostring(id) .. ']=' .. num .. '\nlrob[' .. tostring(-id) .. ']=' .. tostring(bool) .. '\n') + 'lrob[' .. tostring(id) .. ']=' .. tostring(num) .. '\nlrob[' .. tostring(-id) .. ']=' .. tostring(bool) .. '\n') end end - + local node_prev = node.direct.getprev + local function new_kern(inherit, num) + local k = node_new(id_kern, 1, inherit); setkern(k, num); set_attr(kt, attr_icflag, PROCESSED); + return k; + end post_lown = function (rs, rw, cmp, ch) -- ch: the head of `current' hlist if #rs ==0 or not rw then return ch end local hn = get_attr(rs[1], attr_ruby) local fn = get_attr(rs[#rs], attr_ruby) - local wv = getvalue(rw) + local wv = getvalue(rw); local rst = getvalue(wv) if hn==1 then if fn==2*cmp+2 then - local hn = node_tail(wv) - node_remove(wv, hn) - insert_after(ch, rs[1], hn) - set_attr(hn, attr_icflag, PROCESSED) - write_aux(wv, get_attr(hn, attr_ruby), get_attr(hn, attr_ruby_post_jfmgk))-- 行中形 + -- 行中形 + local nn = node_tail(wv); + if rst.quirk_protrusion then + local prot_b,prot_a = rst[fn-1][1], rst[fn-1][2] + local ka=nn; local kb = node_prev(nn); nn = node_prev(kb) + node_remove(wv, nn); node_remove(wv, kb); node_remove(wv, ka) + if prot_b~=0 then + setkern(kb, prot_b); insert_after(ch, rs[1], kb); + local kt = new_kern(rw, -prot_b); insert_after(ch, kb, kt); + insert_after(ch, kt, nn); + else + node_free(kb); insert_after(ch, rs[1], nn) + end + if prot_a~=0 then + local kt = new_kern(rw, -prot_a); insert_after(ch, nn, kt); + setkern(ka, prot_a); insert_after(ch, kt, ka); + else node_free(ka) + end + else + node_remove(wv, nn); insert_after(ch, rs[1], nn) + end + set_attr(nn, attr_icflag, PROCESSED) + write_aux(wv, get_attr(nn, attr_ruby), get_attr(nn, attr_ruby_post_jfmgk)) else - local deg, hn = (fn-1)/2, wv - for i = 1, deg do hn = node_next(hn) end; - node_remove(wv, hn) - setnext(hn, nil) - insert_after(ch, rs[1], hn) - set_attr(hn, attr_icflag, PROCESSED) - write_aux(wv, get_attr(hn, attr_ruby), get_attr(hn, attr_ruby_post_jfmgk)) + -- 行末形 + local deg, nn = (fn-1)/2, wv; for i = 1, deg do nn = node_next(nn) end + if rst.quirk_protrusion then + local prot_b = rst[deg][1] + local ka = node_tail(wv); local kb = node_prev(ka); + node_remove(wv, nn); setnext(nn, nil); node_remove(wv, kb); + if prot_b~=0 then + setkern(kb, prot_b); insert_after(ch, rs[1], kb); + local kt = new_kern(rw, -prot_b); insert_after(ch, kb, kt); + insert_after(ch, kt, nn); + else + node_free(kb); insert_after(ch, rs[1], nn) + end + if deg==cmp then + local prot_a = rst[deg][2]; node_remove(wv,ka) + if prot_a~=0 then + local kt = new_kern(rw, -prot_a); insert_after(ch, nn, kt); + setkern(ka, prot_a); insert_after(ch, kt, ka); + else node_free(ka) + end + end + else + node_remove(wv, nn); setnext(nn, nil); insert_after(ch, rs[1], nn) + end + + set_attr(nn, attr_icflag, PROCESSED) + write_aux(wv, get_attr(nn, attr_ruby), get_attr(nn, attr_ruby_post_jfmgk)) end else - local deg, hn = max((hn-1)/2,2), wv - for i = 1, cmp+deg-1 do hn = node_next(hn) end + -- 行頭形 + local nn = wv; for i = 1, cmp+max((hn-1)/2,2)-1 do nn = node_next(nn) end -- -1 is needed except the case hn = 3, -- because a ending-line form is removed already from the list - node_remove(wv, hn); setnext(hn, nil) - insert_after(ch, rs[1], hn) - set_attr(hn, attr_icflag, PROCESSED) + if rst.quirk_protrusion then + local deg=(hn-1)/2; local prot_b = rst[2*cmp+1-deg][1] + local ka = node_tail(wv); node_remove(wv, nn); setnext(nn, nil); + if deg==1 then + local kb = node_prev(ka); node_remove(wv, kb); + if prot_b~=0 then + setkern(kb, prot_b); insert_after(ch, rs[1], kb); + local kt = new_kern(rw, -prot_b); insert_after(ch, kb, kt); + insert_after(ch, kt, nn); + else + node_free(kb); insert_after(ch, rs[1], nn) + end + else insert_after(ch, rs[1], nn) + end + local prot_a = rst[2*cmp+1-deg][2]; node_remove(wv,ka) + if prot_a~=0 then + local kt = new_kern(rw, -prot_a); insert_after(ch, nn, kt); + setkern(ka, prot_a); insert_after(ch, kt, ka); + else node_free(ka) + end + else + node_remove(wv, nn); setnext(nn, nil); insert_after(ch, rs[1], nn) + end + + set_attr(nn, attr_icflag, PROCESSED) if fn == 2*cmp-1 then - write_aux(wv, get_attr(hn, attr_ruby), get_attr(hn, attr_ruby_post_jfmgk)) + write_aux(wv, get_attr(nn, attr_ruby), get_attr(nn, attr_ruby_post_jfmgk)) end end for i = 1,#rs do - local ri = rs[i] - ch = node_remove(ch, ri); node_free(ri); + local ri = rs[i]; ch = node_remove(ch, ri); node_free(ri) end -- cleanup if fn >= 2*cmp+1 then node_free(rw) end @@ -644,73 +751,86 @@ do end local traverse_id = node.direct.traverse_id -local function post_high_break(head) - local rs = {} -- rs: sequence of ruby_nodes, - local rw = nil -- rw: main whatsit - local cmp = -2 -- dummy - for h in traverse_id(id_hlist, to_direct(head)) do - for i = 1, #rs do rs[i] = nil end - local ha = getlist(h) +local post_high_hbox, post_high_break +do + local rs, rw = {}, nil -- rs: sequence of ruby_nodes, rw: main whatsit + local cmp + post_high_break = function (head) + for h in traverse_id(id_hlist, to_direct(head)) do + for i = 1, #rs do rs[i] = nil end + local ha = getlist(h); + while ha do + local hai = getid(ha) + local i = ((hai == id_glue and getsubtype(ha)==0) + or (hai == id_rule and getsubtype(ha)==0) + or (hai == id_whatsit and getsubtype(ha)==sid_user + and getfield(ha, 'user_id', RUBY_POST))) + and get_attr(ha, attr_ruby) or 0 + if i==0 then + ha = node_next(ha) + elseif i==1 then + setlist(h, post_lown(rs, rw, cmp, getlist(h))) + for i = 2, #rs do rs[i] = nil end -- rs[1] is set by the next statement + rs[1], rw = ha, nil; ha = node_next(ha) + elseif i==2 then + rw = ha + cmp = getvalue(getvalue(rw)).count + local hb, hc = node_remove(getlist(h), rw) + setlist(h, hb); ha = hc + else -- i>=3 + rs[#rs+1] = ha; ha = node_next(ha) + end + end + setlist(h, post_lown(rs, rw, cmp, getlist(h))) + end + return head + end + post_high_hbox = function (ahead) + for i = 1, #rs do rs[i] = nil end; rw = nil + local ha = to_direct(ahead); local head = ha while ha do local hai = getid(ha) local i = ((hai == id_glue and getsubtype(ha)==0) - or (hai == id_rule and getsubtype(ha)==0) - or (hai == id_whatsit and getsubtype(ha)==sid_user - and getfield(ha, 'user_id', RUBY_POST))) - and get_attr(ha, attr_ruby) or 0 + or (hai == id_rule and getsubtype(ha)==0) + or (hai == id_whatsit and getsubtype(ha)==sid_user + and getfield(ha, 'user_id', RUBY_POST))) + and get_attr(ha, attr_ruby) or 0 if i==0 then ha = node_next(ha) elseif i==1 then - setlist(h, post_lown(rs, rw, cmp, getlist(h))) + head = post_lown(rs, rw, cmp, head) for i = 2, #rs do rs[i] = nil end -- rs[1] is set by the next statement rs[1], rw = ha, nil; ha = node_next(ha) elseif i==2 then rw = ha cmp = getvalue(getvalue(rw)).count - local hb, hc = node_remove(getlist(h), rw) - setlist(h, hb); ha = hc - else -- i>=3 + head, ha = node_remove(head, rw) + else -- i >= 3 rs[#rs+1] = ha; ha = node_next(ha) end end - setlist(h, post_lown(rs, rw, cmp, getlist(h))) + return to_node(post_lown(rs, rw, cmp, head)) end - return head end - -local function post_high_hbox(ahead) - local ha = to_direct(ahead); local head = ha - local rs = {}; -- rs: sequence of ruby_nodes, - local rw = nil; -- rw: main whatsit - local cmp - while ha do - local hai = getid(ha) - local i = ((hai == id_glue and getsubtype(ha)==0) - or (hai == id_rule and getsubtype(ha)==0) - or (hai == id_whatsit and getsubtype(ha)==sid_user - and getfield(ha, 'user_id', RUBY_POST))) - and get_attr(ha, attr_ruby) or 0 - if i==0 then - ha = node_next(ha) - elseif i==1 then - head = post_lown(rs, rw, cmp, head) - for i = 2, #rs do rs[i] = nil end -- rs[1] is set by the next statement - rs[1], rw = ha, nil; ha = node_next(ha) - elseif i==2 then - rw = ha - cmp = getvalue(getvalue(rw)).count - head, ha = node_remove(head, rw) - else -- i >= 3 - rs[#rs+1] = ha; ha = node_next(ha) - end +do + -- pre_append_to_vlist_filter.lua: distributed with lua-ul + local found = kpse.find_file('pre_append_to_vlist_filter.lua', 'lua') + if not found then + luatexbase.add_to_callback('post_linebreak_filter', post_high_break, 'ltj.ruby.post_break') + else + require 'pre_append_to_vlist_filter' + luatexbase.add_to_callback('pre_append_to_vlist_filter', + function(head, loc) + return (loc~='post_linebreak') and head or post_high_break(head) + end, + 'ltj.ruby.post_break', + (luatexbase.priority_in_callback('pre_append_to_vlist_filter', 'add underlines to list') or 1)) end - return to_node(post_lown(rs, rw, cmp, head)) + luatexbase.add_to_callback('hpack_filter', + function(head) return post_high_hbox(pre_high(head)) end, 'ltj.ruby', + luatexbase.priority_in_callback('hpack_filter', 'add underlines to list') or nil) end -luatexbase.add_to_callback('post_linebreak_filter', post_high_break, 'ltj.ruby.post_break', 100) -luatexbase.add_to_callback('hpack_filter', post_high_hbox, 'ltj.ruby.post_hbox', 101) - - ---------------------------------------------------------------- -- for jfmglue callbacks ---------------------------------------------------------------- @@ -736,7 +856,7 @@ do p = 0 end rst.pre = -p; rst.exclude_pre_from_prev_ruby = (s<0); - rst.exclude_pre_jfmgk_from_prev_ruby = (ltjs.table_current_stack[RIPOST +Nq.char] or 0)<0; + rst.exclude_pre_jfmgk_from_prev_ruby = (ltjs.table_current_stack[RIPOST +Nq.char] or 0)<0; end if Nq.prev_ruby then set_attr(lp, attr_ruby, Nq.prev_ruby) @@ -758,6 +878,11 @@ do elseif rst.pre < 0 then -- auto rst.pre = 0 end + if rst.quirk_protrusion then + local lk = node_new(id_kern, 1, Nq.nuc, lp); set_attr(lk, attr_icflag, PROCESSED) + insert_after(lpv, node_tail(lpv), lk) + insert_after(lpv, node_tail(lpv), node_new(id_kern, 1)) + end return Np else return Np @@ -780,10 +905,12 @@ do end local RIPOST = luatexja.stack_table_index.RIPOST local abs = math.abs + local ltjj_after_hlist=luatexja.jfmglue.after_hlist + local ltjj_check_box_high=luatexja.jfmglue.check_box_high local function whatsit_after_callback(s, Nq, Np, head) if not s and getfield(Nq.nuc, 'user_id') == RUBY_PRE then if Np then - local last_glue = node_new(id_glue) + local last_glue = node_new(id_glue, nil, Nq.nuc) -- INHERIT ATTRIBUTE OF Nq.nuc set_attr(last_glue, attr_icflag, 0) insert_before(Nq.nuc, Np.first, last_glue) Np.first = last_glue @@ -798,8 +925,8 @@ do end local x = node_next(node_next(nqnv)) for i = 2, rst.count do x = node_next(node_next(x)) end - Nq.last_char = luatexja.jfmglue.check_box_high(Nq, getlist(x), nil) - luatexja.jfmglue.after_hlist(Nq) + Nq.last_char = ltjj_check_box_high(Nq, getlist(x), nil) + ltjj_after_hlist(Nq) if Np and Np.id ~=id_pbox_w and type(Np.char)=='number' then -- Np is a JAchar if rst.post < 0 then -- auto @@ -812,12 +939,15 @@ do Np.prev_ruby = get_attr(getvalue(Nq.nuc), attr_ruby_id) -- 前のクラスタがルビであったことのフラグ else -- 直前が文字以外 - local nqnv = getvalue(Nq.nuc) - local rst = getvalue(nqnv) if rst.post < 0 then -- auto rst.post = 0 end end + if rst.quirk_protrusion then + local lk = node_tail(nqnv); + node_inherit_attr(lk, Nq.nuc, Np and Np.nuc) + set_attr(lk, attr_icflag, PROCESSED) + end return head else return s diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltjsarticle.cls b/Master/texmf-dist/tex/luatex/luatexja/ltjsarticle.cls index 5de7ff1e774..813f7d1aab6 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltjsarticle.cls +++ b/Master/texmf-dist/tex/luatex/luatexja/ltjsarticle.cls @@ -38,7 +38,7 @@ %% Right brace \} Tilde \~} \NeedsTeXFormat{LaTeX2e} \ProvidesClass{ltjsarticle} - [2022/09/12 ltjsclasses ] + [2023/07/26 ltjsclasses ] \def\jsc@clsname{ltjsarticle} \def\Cjascale{0.924715} \RequirePackage{luatexja} diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltjsbook.cls b/Master/texmf-dist/tex/luatex/luatexja/ltjsbook.cls index 99480233056..3a156891c88 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltjsbook.cls +++ b/Master/texmf-dist/tex/luatex/luatexja/ltjsbook.cls @@ -38,7 +38,7 @@ %% Right brace \} Tilde \~} \NeedsTeXFormat{LaTeX2e} \ProvidesClass{ltjsbook} - [2022/09/12 ltjsclasses ] + [2023/07/26 ltjsclasses ] \def\jsc@clsname{ltjsbook} \def\Cjascale{0.924715} \RequirePackage{luatexja} diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltjskiyou.cls b/Master/texmf-dist/tex/luatex/luatexja/ltjskiyou.cls index 231be8bf2ef..5f62215f940 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltjskiyou.cls +++ b/Master/texmf-dist/tex/luatex/luatexja/ltjskiyou.cls @@ -38,7 +38,7 @@ %% Right brace \} Tilde \~} \NeedsTeXFormat{LaTeX2e} \ProvidesClass{ltjskiyou} - [2022/09/12 ltjsclasses ] + [2023/07/26 ltjsclasses ] \def\jsc@clsname{ltjskiyou} \def\Cjascale{0.924715} \RequirePackage{luatexja} diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltjspf.cls b/Master/texmf-dist/tex/luatex/luatexja/ltjspf.cls index 2723db71cac..730bc082b22 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltjspf.cls +++ b/Master/texmf-dist/tex/luatex/luatexja/ltjspf.cls @@ -38,7 +38,7 @@ %% Right brace \} Tilde \~} \NeedsTeXFormat{LaTeX2e} \ProvidesClass{ltjspf} - [2022/09/12 ltjsclasses ] + [2023/07/26 ltjsclasses ] \def\jsc@clsname{ltjspf} \def\Cjascale{0.903375} \RequirePackage{luatexja} diff --git a/Master/texmf-dist/tex/luatex/luatexja/ltjsreport.cls b/Master/texmf-dist/tex/luatex/luatexja/ltjsreport.cls index 5fb4691762f..08ed724da15 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/ltjsreport.cls +++ b/Master/texmf-dist/tex/luatex/luatexja/ltjsreport.cls @@ -38,7 +38,7 @@ %% Right brace \} Tilde \~} \NeedsTeXFormat{LaTeX2e} \ProvidesClass{ltjsreport} - [2022/09/12 ltjsclasses ] + [2023/07/26 ltjsclasses ] \def\jsc@clsname{ltjsreport} \def\Cjascale{0.924715} \RequirePackage{luatexja} diff --git a/Master/texmf-dist/tex/luatex/luatexja/luatexja.lua b/Master/texmf-dist/tex/luatex/luatexja/luatexja.lua index 1013684496a..e815ba3d682 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/luatexja.lua +++ b/Master/texmf-dist/tex/luatex/luatexja/luatexja.lua @@ -423,9 +423,10 @@ local utfchar = utf.char local function debug_show_node_X(p,print_fn, limit, inner_depth) local k = prefix local s - local pt, pic = node_type(p.id), (get_attr(p, attr_icflag) or 0) % icflag_table.PROCESSED_BEGIN_FLAG - local base = prefix .. '[' .. string.format('%7d', node.direct.todirect(p)) .. '] ' .. - string.format('%X', pic) .. ' ' .. pt .. ' ' .. tostring(p.subtype) .. ' ' + local pt, pic = node_type(p.id), (get_attr(p, attr_icflag) or 0) --% icflag_table.PROCESSED_BEGIN_FLAG + local base = prefix .. '[' .. string.format('%7d', node.direct.todirect(p)) + .. ', ' .. tostring(get_attr(p, 27)) .. '] ' + .. string.format('%X', pic) .. ' ' .. pt .. ' ' .. tostring(p.subtype) .. ' ' if pt == 'glyph' then s = base .. ' ' .. (p.char<0xF0000 and utfchar(p.char) or '') diff --git a/Master/texmf-dist/tex/luatex/luatexja/luatexja.sty b/Master/texmf-dist/tex/luatex/luatexja/luatexja.sty index a54ede6758b..90bb4fdf7e3 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/luatexja.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/luatexja.sty @@ -27,7 +27,7 @@ \newif\ifltj@disablejfam %%%% VERSION -\def\LuaTeXjaversion{20240514.0} +\def\LuaTeXjaversion{20240909.0} %% Check if LaTeX is used. \begingroup\expandafter\expandafter\expandafter\endgroup diff --git a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjcore.sty b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjcore.sty index 1c8f9cba4ef..121a45ba0b1 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjcore.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjcore.sty @@ -219,10 +219,6 @@ \@latex@error{Direction change inside float!?\MessageBreak Use \noexpand\layoutcaption provided in \string\usepackage{lltjext}}\@ehc} %%% Remove extra xkanjiskip -\renewcommand\@makefnmark{% - \unless\ifnum\ltjgetparameter{direction}=3 - \hbox{}\hbox{\@textsuperscript{\normalfont\@thefnmark}}\hbox{}% - \else\hbox{\yoko \@textsuperscript{\normalfont\@thefnmark}}\fi} \begingroup \@ifpackageloaded{hyperref} {\ifdefined\real@setref\def\ltj@setrefprefix{real}\else\def\ltj@setrefprefix{}\fi} @@ -250,6 +246,11 @@ \gdef\@stopfield{% \inhibitglue\@tempskipa\lastskip\unskip \hskip\@tempskipa\color@endgroup\egroup} +%%%%%% footnote +\renewcommand\@makefnmark{% + \unless\ifnum\ltjgetparameter{direction}=3 + \hbox{}\hbox{\@textsuperscript{\normalfont\@thefnmark}}\hbox{}% + \else\hbox{\yoko \@textsuperscript{\normalfont\@thefnmark}}\fi} \pretocmd{\footnote}{\inhibitglue}{}{} \pretocmd{\footnotemark}{\inhibitglue}{}{} \def\@ltj@process@makefnmark#1{% diff --git a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjdefs.sty b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjdefs.sty index bb63ac68774..00c858e3493 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjdefs.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjdefs.sty @@ -3,7 +3,7 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{lltjdefs}[2022-01-23 Default font settings of LuaTeX-ja] +\ProvidesPackage{lltjdefs}[2024-05-16 Default font settings of LuaTeX-ja] \ifdefined\ltj@stdmcfont\else\def\ltj@stdmcfont{file:HaranoAjiMincho-Regular.otf}\fi \ifdefined\ltj@stdgtfont\else\def\ltj@stdgtfont{file:HaranoAjiGothic-Medium.otf}\fi @@ -18,13 +18,8 @@ \DeclareKanjiEncodingDefaults{}{} \DeclareErrorKanjiFont{JY3}{mc}{m}{n}{10} \kanjifamily{mc} -\ifnum\ltj@@latex@plv<1500 % <=2020-10-01 - \kanjiseries{m} - \kanjishape{n} -\else % >=2021-06-01 - \def\k@series{m} - \def\k@shape{n} -\fi +\def\k@series{m} +\def\k@shape{n} \fontsize{10}{10} \DeclareYokoKanjiEncoding{JY3}{}{} \DeclareKanjiSubstitution{JY3}{mc}{m}{n} @@ -37,12 +32,7 @@ \newcommand\kanjiencodingdefault{JY3} \newcommand\kanjifamilydefault{\mcdefault} \newcommand\kanjiseriesdefault{\mddefault} -\ifnum 0\ifltj@ltfilehook@avail1\fi\ifdefined\@rmfamilyhook1\fi>0 - %% LaTeX 2020-02-02 - \newcommand\kanjishapedefault{\shapedefault} -\else%% LaTeX 2019-10-01 - \newcommand\kanjishapedefault{\updefault} -\fi +\newcommand\kanjishapedefault{\shapedefault} \DeclareKanjiFamily{JY3}{mc}{} \DeclareFontShape{JY3}{mc}{m}{n}{<-> s*[\ifdefined\Cjascale\Cjascale\else 0.962216\fi] @@ -90,57 +80,36 @@ \DeclareMathAlphabet{\mathgt}{JY3}{gt}{m}{n} \fi -\ifnum 0\ifltj@ltfilehook@avail1\fi\ifdefined\@rmfamilyhook1\fi>0 -%%%% LaTeX >= 2020-02-02 \edef\bfseries@mc{\bfdefault} \edef\bfseries@gt{\bfdefault} \def\bfseries@jtt{\bfseries@mc} \edef\mdseries@mc{\mddefault} \edef\mdseries@gt{\mddefault} \def\mdseries@jtt{\mdseries@mc} -\fi \newif\if@ltj@match@family \@ltj@match@familyfalse \def\verbatim@font{\normalfont\ttfamily\kanjifamily\jttdefault\selectfont} -\def\@ltj@hook@family#1#2{% #1: csname #2: contents - \ifltj@ltfilehook@avail % LaTeX 2020-10-01 - \AddToHook{#1}{#2}% - \else\ifcsname @#1hook\endcsname % LaTeX 2020-02-02 - \csappto{@#1hook}{#2}% - \else % LaTeX 2019-10-01 - \ifcsname #1 \endcsname - \expandafter\patchcmd\csname #1 \endcsname{\selectfont}{#2\selectfont}{}{}% - \else - \PackageWarningNoLine{luatexja}{% - Patching to \expandafter\string\csname #1\endcsname\space failed% - }% - \fi - \fi\fi -} -\@ltj@hook@family{rmfamily}{% +\AddToHook{rmfamily}{% \if@ltj@match@family \prepare@family@series@update@kanji{mc}\mcdefault \fi } -\@ltj@hook@family{sffamily}{% +\AddToHook{sffamily}{% \if@ltj@match@family \prepare@family@series@update@kanji{gt}\gtdefault \fi } -\@ltj@hook@family{ttfamily}{% +\AddToHook{ttfamily}{% \if@ltj@match@family \prepare@family@series@update@kanji{jtt}\jttdefault \fi } -\let\@ltj@hook@family\@undefined -\ifltj@ltfilehook@avail % 2020-10-01 - \expandafter\def\expandafter\kanjiseriesdefault - \expandafter{\kanjiseriesdefault\@empty} - \let\kanjiseriesdefault@kernel\kanjiseriesdefault -\fi +\expandafter\def\expandafter\kanjiseriesdefault + \expandafter{\kanjiseriesdefault\@empty} +\let\kanjiseriesdefault@kernel\kanjiseriesdefault %%% \eminnershape \expandafter\patchcmd\csname em \endcsname{\itshape}{\gtfamily\itshape}{}{} diff --git a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjfont.sty b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjfont.sty index 2efc9435ab8..52ac44d5ccb 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjfont.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjfont.sty @@ -3,31 +3,10 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{lltjfont}[2023-04-13 Patch to NFSS2 for LuaTeX-ja] +\ProvidesPackage{lltjfont}[2023-05-16 Patch to NFSS2 for LuaTeX-ja] %%%%%%%% LaTeX2e version detection -\newcount\ltj@@latex@plv \ltj@@latex@plv=-1 -\ifltj@ltfilehook@avail -\ExplSyntaxOn - \cs_if_exist:NTF \__shipout_execute_main_cont:Nnnn - {\ltj@@latex@plv=1500}% 2021-06-01 - {\ifnum\patch@level>2\relax \ltj@@latex@plv=1300\else \ltj@@latex@plv=1000\fi}% 2020-10-01 -\ExplSyntaxOff -\else\ifdefined\@rmfamilyhook % 2020-02-02 - \ifx\@forced@seriestrue\@undefined % 2020-02-02 pl0--2 - \@latex@error - {Please update LaTeX2e!^^J\space\space - At least LaTeX2e 2020-02-02 patch level 3 is required}% - {LaTeX2e 2020-02-02 patch level 2 (and 4) has a bug.} - \else - \ltj@@latex@plv=300 - \unless\ifx\series@maybe@drop@one@m@x\@undefined % pl5 + develop - \ltj@@latex@plv=501 - \else\unless\ifx\series@maybe@drop@one@m\@undefined % patch level 5 - \ltj@@latex@plv=500 - \fi\fi - \fi -\fi\fi +\newcount\ltj@@latex@plv \ltj@@latex@plv=1500 %% patching \DeclareTextComposite \def\ltj@chardef@text@cmd#1{% @@ -400,59 +379,7 @@ \directlua{luatexja.jfont.pickup_alt_font_a('\f@size')}% }% \fi} -\ifnum\ltj@@latex@plv<1500 % <=2020-10-01 -\protected\def\ltj@selectfont@patch{% - \ltj@@start@time@measure{selectfont}% - \let\tmp@error@fontshape\error@fontshape - \let\error@fontshape\error@kfontshape - \ltj@@is@kyenc{\k@encoding}\ifin@% - \let\cy@encoding\k@encoding - \ensure@KanjiEncodingPair{t}% - \edef\ct@encoding{\csname t@enc@\k@encoding\endcsname}% - \else - \ltj@@is@ktenc{\k@encoding}\ifin@% - \let\ct@encoding\k@encoding - \ensure@KanjiEncodingPair{y}% - \edef\cy@encoding{\csname y@enc@\k@encoding\endcsname}% - \else - \@latex@error{KANJI Encoding scheme `\k@encoding' unknown}\@eha - \fi - \fi - \global\let\ltj@afont@name=\font@name - \ltj@selectfont@tate\ltj@selectfont@yoko -% - \edef\tmp@item{{\k@encoding}}\expandafter\def\expandafter\k@encoding\tmp@item - \kenc@update - \global\let\font@name=\ltj@afont@name - \let\error@fontshape\tmp@error@fontshape - \if@knjcmd \@knjcmdfalse - \expandafter\ifx - \csname rel@\k@encoding/\k@family/\k@series/\k@shape\endcsname\relax - \expandafter\ifx - \csname rel@\k@encoding/\k@family/\k@series/all\endcsname\relax - \else - \csname rel@\k@encoding/\k@family/\k@series/all\endcsname\selectfont - % 警告: 再び \selectfont を呼んでいる.無限ループの可能性あり - \fi - \else - \csname rel@\k@encoding/\k@family/\k@series/\k@shape\endcsname\selectfont - % 警告: 再び \selectfont を呼んでいる.無限ループの可能性あり - \fi - \fi - \global\let\font@name=\ltj@afont@name - \ltj@@stop@time@measure{selectfont}% -} -\RequirePackage{everysel} -\let\ltj@@EverySelectfont@Init=\@EverySelectfont@Init -\expandafter\expandafter\expandafter\let -\expandafter\expandafter\csname ltj@@orig@selectfont\endcsname\csname selectfont\space\endcsname -\@EverySelectfont@Init% for LaTeX2e <=2020-10-01 -\def\@EverySelectfont@Init{% - \expandafter\let\csname selectfont\space \endcsname \ltj@@orig@selectfont - \ltj@@EverySelectfont@Init -} -\EverySelectfont{\ltj@selectfont@patch} -\else % LaTeX2e >=2021-06-01 + \AddToHook{selectfont}[luatexja]{% \ltj@@start@time@measure{selectfont}% \ifx\delayed@k@adjustment\@empty @@ -514,7 +441,6 @@ \global\let\font@name=\ltj@afont@name \ltj@@stop@time@measure{selectfont}% } -\fi \bgroup \def\ltj@@dir@patch#1#2#3{% @@ -721,8 +647,6 @@ } %%%%%% -\ifnum\ltj@@latex@plv>0 -%%%% LaTeX >= 2020-02-02 \newif\if@shape@roman@kanji \def\merge@kanji@series#1{% @@ -740,76 +664,11 @@ \@nil } -\ifnum\ltj@@latex@plv>500 % !!! pl5 + develop \def\set@target@series@kanji#1{% \edef\k@series{#1}% \series@maybe@drop@one@m\k@series\k@series } -\else\ifnum\ltj@@latex@plv>300 % !!! pl5 - \def\set@target@series@kanji#1{% - \edef\k@series{#1}% - \expandafter\series@maybe@drop@one@m\expandafter{\k@series}\k@series - } -\else % !!! pl 3, 4 - \def\set@target@series@kanji#1{% - \edef\k@series{#1}% - \edef\k@series{\expandafter\series@drop@one@m\k@series mm\series@drop@one@m}% - } -\fi\fi -\ifnum\ltj@@latex@plv<1500% <=2020-10-01 -\def\merge@kanji@series@#1#2#3\@nil{% - \def\reserved@a{#3}% - \ifx\reserved@a\@empty - \set@target@series@kanji{#2}% - \else - \begingroup\let\f@encoding\k@encoding\let\f@family\k@family - \maybe@load@fontshape\endgroup - \edef\reserved@a{\k@encoding /\k@family /#1/\k@shape}% - \ifcsname \reserved@a \endcsname - \set@target@series@kanji{#1}% - \else - \ifcsname \k@encoding /\k@family /#2/\k@shape \endcsname - \set@target@series@kanji{#2}% - {\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}% - \else - \set@target@series@kanji{#3}% - {\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}% - \fi - \fi - \fi -} -\def\merge@kanji@shape@#1#2#3\@nil{% - \def\reserved@a{#3}% - \ifx\reserved@a\@empty - \if@shape@roman@kanji - \set@safe@kanji@shape{#2}{}% - \else - \edef\k@shape{#2}% - \fi - \else - \begingroup\let\f@encoding\k@encoding\let\f@family\k@family - \maybe@load@fontshape\endgroup - \edef\reserved@a{\k@encoding /\k@family /\k@series/#1}% - \ifcsname \reserved@a\endcsname - \edef\k@shape{#1}% - \else - \ifcsname \k@encoding /\k@family /\k@series/#2\endcsname - \edef\k@shape{#2}% - {\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}% - \else - \if@shape@roman@kanji - \set@safe@kanji@shape{#3}% - {{\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}}% - \else - \edef\k@shape{#3}% - {\let\curr@fontshape\curr@kfontshape\@font@shape@subst@warning}% - \fi - \fi - \fi - \fi -} -\else % >=2021-06-01 \def\merge@kanji@series@#1#2#3\@nil{% \def\reserved@a{#3}% \ifx\reserved@a\@empty @@ -892,20 +751,8 @@ \fi } \let\delayed@merge@kanji@shape\merge@kanji@shape@without@substitution -\fi \newif\if@forced@series@kanji -\ifnum\ltj@@latex@plv=300 % 2020-02-02 pl3, 4 - \DeclareRobustCommand\romanseries[1]{\merge@font@series{#1}} - \DeclareRobustCommand\kanjiseries[1]{\merge@kanji@series{#1}} - \DeclareRobustCommand\romanseriesforce[1]{\edef\f@series{#1}} - \DeclareRobustCommand\kanjiseriesforce[1]{\edef\k@series{#1}} -\else\ifnum\ltj@@latex@plv<1500% >=2020-02-02 pl5 and <=2020-10-01 - \DeclareRobustCommand\romanseries[1]{\@forced@seriesfalse\merge@font@series{#1}} - \DeclareRobustCommand\kanjiseries[1]{\@forced@series@kanjifalse\merge@kanji@series{#1}} - \DeclareRobustCommand\romanseriesforce[1]{\@forced@seriestrue\edef\f@series{#1}} - \DeclareRobustCommand\kanjiseriesforce[1]{\@forced@series@kanjitrue\edef\k@series{#1}} -\else% >=2021-06-01 \DeclareRobustCommand\romanseries[1]{\@forced@seriesfalse \expandafter\def\expandafter\delayed@f@adjustment\expandafter {\delayed@f@adjustment\delayed@merge@font@series{#1}}} @@ -919,22 +766,9 @@ \expandafter\def\expandafter\delayed@k@adjustment\expandafter {\delayed@k@adjustment\edef\k@series{#1}}} \let\delayed@k@adjustment\@empty -\fi\fi \DeclareRobustCommand\fontseries[1]{\kanjiseries{#1}\romanseries{#1}}% \DeclareRobustCommand\fontseriesforce[1]{\kanjiseriesforce{#1}\romanseriesforce{#1}}% -\ifnum\ltj@@latex@plv<1500% <=2020-10-01 - \DeclareRobustCommand\romanshape[1]{\merge@font@shape{#1}} - \DeclareRobustCommand\kanjishape[1]{\merge@kanji@shape{#1}} - \DeclareRobustCommand\fontshape[1]{% - \@shape@roman@kanjitrue - \merge@font@shape{#1}\merge@kanji@shape{#1}% - \@shape@roman@kanjifalse - } - \DeclareRobustCommand\romanshapeforce[1]{\edef\f@shape{#1}} - \DeclareRobustCommand\kanjishapeforce[1]{\edef\k@shape{#1}} - \DeclareRobustCommand\fontshapeforce[1]{\set@safe@kanji@shape{#1}{}\romanshapeforce{#1}} -\else% >=2021-06-01 \DeclareRobustCommand\romanshape[1]{% \expandafter\def\expandafter\delayed@f@adjustment\expandafter {\delayed@f@adjustment\delayed@merge@font@shape{#1}}} @@ -956,9 +790,6 @@ \romanshapeforce{#1}% \expandafter\def\expandafter\delayed@k@adjustment\expandafter {\delayed@k@adjustment\set@safe@kanji@shape{#1}{}}} -\fi -\ifnum\ltj@@latex@plv>1499 -%%%% LaTeX >= 2021-06-01 \DeclareRobustCommand\usekanji[4]{% \kanjiencoding{#1}\edef\k@family{#2}% \set@target@series@kanji{#3}\edef\k@shape{#4}% @@ -969,42 +800,6 @@ \set@target@series{#3}\edef\f@shape{#4}% \let\delayed@f@adjustment\@empty \selectfont\ignorespaces} -\else\ifnum\ltj@@latex@plv>1000 -%%%% 2021-10-01 PL3 <= LaTeX < 2021-06-01 -\DeclareRobustCommand\usekanji[4]{% - \kanjiencoding{#1}\edef\k@family{#2}% - \set@target@series@kanji{#3}\edef\k@shape{#4}% - \selectfont\ignorespaces} -\DeclareRobustCommand\useroman[4]{% - \romanencoding{#1}\edef\f@family{#2}% - \set@target@series{#3}\edef\f@shape{#4}% - \selectfont\ignorespaces} -\else -%%%% 2020-10-01 <= LaTeX < 2020-10-01 PL3 -\DeclareRobustCommand\usekanji[4]{% - \kanjiencoding{#1}\edef\k@family{#2}\edef\k@series{#3}\edef\k@shape{#4}% - \selectfont\ignorespaces} -\DeclareRobustCommand\useroman[4]{% - \romanencoding{#1}\edef\f@family{#2}\edef\f@series{#3}\edef\f@shape{#4}% - \selectfont\ignorespaces} -\fi\fi -%%%% LaTeX >= 2020-02-02 END -\else -%%%% LaTeX 2019-10-01 -\DeclareRobustCommand\romanseries[1]{\edef\f@series{#1}} -\DeclareRobustCommand\kanjiseries[1]{\edef\k@series{#1}} -\DeclareRobustCommand\fontseries[1]{\kanjiseries{#1}\romanseries{#1}} -\DeclareRobustCommand\romanshape[1]{\edef\f@shape{#1}} -\DeclareRobustCommand\kanjishape[1]{\edef\k@shape{#1}} -\DeclareRobustCommand\fontshape[1]{\set@safe@kanji@shape{#1}{}\romanshape{#1}} -\DeclareRobustCommand\usekanji[4]{% - \kanjiencoding{#1}\kanjifamily{#2}\kanjiseries{#3}\kanjishape{#4}% - \selectfont\ignorespaces} -\DeclareRobustCommand\useroman[4]{% - \romanencoding{#1}\romanfamily{#2}\romanseries{#3}\romanshape{#4}% - \selectfont\ignorespaces} -%%%% LaTeX 2019-10-01 END -\fi %%%% \def\set@safe@kanji@shape#1#2{% @@ -1026,8 +821,6 @@ \else\useroman{#1}{#2}{#3}{#4}% \fi} -\ifnum 0\ifltj@ltfilehook@avail1\fi\ifdefined\@rmfamilyhook1\fi>0 -%%%% LaTeX >= 2020-02-02 \let\prepare@family@series@update@kanji=\prepare@family@series@update \patchcmd\prepare@family@series@update{\fontfamily}{\romanfamily}{}{} \patchcmd\prepare@family@series@update@kanji{\if@forced@series}{\if@forced@series@kanji}{}{} @@ -1049,34 +842,16 @@ \patchcmd\update@series@target@value@kanji{\f@series}{\k@series}{}{} \def\@meta@family@list@kanji{\@elt{mc}\@elt{gt}\@elt{jtt}} -\def\@ltj@hook@series@default#1#2#3{% - \ifltj@ltfilehook@avail % 2020-10-01 - \AddToHook{#1/defaults}{#2}% - \else\ifcsname @set#1defaultshook\endcsname %after 2020-04-07 - \expandafter\g@addto@macro\csname @set#1defaultshook\endcsname{#2}% - \else % 2020-02-02 - \expandafter\patchcmd\csname #1 \endcsname{#3}{#3#2}{}{}% - \fi\fi -} -\@ltj@hook@series@default{bfseries}{% +\AddToHook{bfseries/defaults}{% \let\bfseries@mc\bfdef@ult \let\bfseries@gt\bfdef@ult \let\bfseries@jtt\bfdef@ult -}{% - \let\bfseries@rm\bfdef@ult - \let\bfseries@sf\bfdef@ult - \let\bfseries@tt\bfdef@ult } -\@ltj@hook@series@default{mdseries}{% +\AddToHook{mdseries/defaults}{% \let\mdseries@mc\mddef@ult \let\mdseries@gt\mddef@ult \let\mdseries@jtt\mddef@ult -}{% - \let\mdseries@rm\mddef@ult - \let\mdseries@sf\mddef@ult - \let\mdseries@tt\mddef@ult } -\let\@ltj@hook@series@default\@undefined \expandafter\patchcmd\csname bfseries \endcsname{% \ifx\f@family\rmdef@ult \fontseries\bfseries@rm @@ -1105,105 +880,41 @@ \fi\fi\fi }{}{} -\def\@ltj@hook@series#1#2{% - \ifltj@ltfilehook@avail % 2020-10-01 - \AddToHook{#1}{#2}% - \else % 2020-02-02 - \expandafter\patchcmd\csname #1 \endcsname{\selectfont}{#2\selectfont}{}{}% - \fi -} -\@ltj@hook@series{bfseries}{% +\AddToHook{bfseries}{% \ifx\k@family\mcdef@ult \kanjiseries\bfseries@mc \else\ifx\k@family\gtdef@ult \kanjiseries\bfseries@gt \else\ifx\k@family\jttdef@ult\kanjiseries\bfseries@jtt \else \kanjiseries\bfdefault \fi\fi\fi } -\@ltj@hook@series{mdseries}{% +\AddToHook{mdseries}{% \ifx\k@family\mcdef@ult \kanjiseries\mdseries@mc \else\ifx\k@family\gtdef@ult \kanjiseries\mdseries@gt \else\ifx\k@family\jttdef@ult\kanjiseries\mdseries@jtt \else \kanjiseries\mddefault \fi\fi\fi } -\let\@ltj@hook@series\@undefined -\def\@ltj@hook@efd#1#2{% - \ifltj@ltfilehook@avail % 2020-10-01 - \AddToHook{expand@font@defaults}{#1}% - \else\ifdefined\@expandfontdefaultshook %after 2020-04-07 - \g@addto@macro\@expandfontdefaultshook{#1#2}% - \else % 2020-02-02 - \appto\expand@font@defaults{#1#2}% - \fi\fi -} -\@ltj@hook@efd{% +\AddToHook{expand@font@defaults}{% \edef\mcdef@ult{\mcdefault}% \edef\gtdef@ult{\gtdefault}% \edef\jttdef@ult{\jttdefault}% -}{\edef\kanjidef@ult{\kanjifamilydefault}} -\let\@ltj@hook@efd\@undefined + \edef\kanjidef@ult{\kanjifamilydefault}} -\ifltj@ltfilehook@avail % 2020-10-01 - \appto\init@series@setup{% - \ifx\kanjiseriesdefault\kanjiseriesdefault@kernel - \mdseries - \let\kanjiseriesdefault\k@series - \fi - } -\else - \appto\init@series@setup{% - \ifx\kanjidef@ult\mcdef@ult \mcfamily - \else\ifx\kanjidef@ult\gtdef@ult \gtfamily - \else\ifx\kanjidef@ult\jttdef@ult \jttfamily - \fi\fi\fi - } -\fi - -\def\@ltj@hook@dfh#1{% - \ifltj@ltfilehook@avail % 2020-10-01 - \AddToHook{normalfont}{#1}% - \else\ifdefined\@defaultfamilyhook - \g@addto@macro\@defaultfamilyhook{#1}% - \else % 2020-02-02 - \expandafter\patchcmd\csname normalfont \endcsname{\selectfont}{#1\selectfont}{}{}% - \let\reset@font\normalfont - \fi\fi +\appto\init@series@setup{% + \ifx\kanjiseriesdefault\kanjiseriesdefault@kernel + \mdseries + \let\kanjiseriesdefault\k@series + \fi } -\ifnum\ltj@@latex@plv>1499 -\@ltj@hook@dfh{% +\AddToHook{normalfont}{% \kanjiencoding{\kanjiencodingdefault}% \edef\k@family{\kanjifamilydefault}% \edef\k@series{\kanjiseriesdefault}% \edef\k@shape{\kanjishapedefault}% \let\delayed@k@adjustment\empty } -\else -\@ltj@hook@dfh{% - \kanjiencoding{\kanjiencodingdefault}% - \edef\k@family{\kanjifamilydefault}% - \edef\k@series{\kanjiseriesdefault}% - \edef\k@shape{\kanjishapedefault}% -} -\fi -\let\@ltj@hook@dfh\@undefined -%%%% LaTeX >= 2020-02-02 END -\else -%%%% LaTeX 2019-10-01 -\DeclareRobustCommand\normalfont{% - \useroman\encodingdefault\familydefault\seriesdefault\shapedefault - \usekanji\kanjiencodingdefault\kanjifamilydefault\kanjiseriesdefault\kanjishapedefault} -\let\reset@font\normalfont -%%%% LaTeX 2019-10-01 END -\fi - - - -\ifdefined\prepare@family@series@update@kanji\else -%%%% LaTeX 2019-10-01 -\def\prepare@family@series@update@kanji#1{\kanjifamily} -\fi \DeclareRobustCommand\mcfamily{% \unless\ifltj@disablejfam\not@math@alphabet\mcfamily\mathmc\fi diff --git a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-array.sty b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-array.sty index 9575dc3c049..fa5fe7b9339 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-array.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-array.sty @@ -3,7 +3,9 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{lltjp-array}[2019-07-18 Patch to array for LuaTeX-ja] +\ProvidesPackage{lltjp-array}[2024-07-21 Patch to array for LuaTeX-ja] + +\@ifpackageloaded{array}{\def\lltjp@array@array@loaded{1}}{\let\lltjp@array@array@loaded\relax} \RequirePackage{etoolbox,array} @@ -11,16 +13,23 @@ \ignorespaces\@sharp\unskip }{% \ltj@sharp -}{}{} +}{}{% >=2024-06-01 + \def\ltj@sharp{\relax\pltx@next@inhibitglue\ltjfakeboxbdd\ignorespaces\@sharp\textonly@unskip\ltjfakeboxbdd} + \patchcmd\insert@column{% + \ignorespaces\@sharp\textonly@unskip + }{% + \ltj@sharp + }{}{} +} \@ifpackageloaded{lltjext}{}{% %% remove extra \xkanjiskip (pLaTeX2e 2016/04/17) -\def\@tabular{% - \leavevmode \null - \hbox \bgroup $\col@sep\tabcolsep \let\d@llarbegin\begingroup - \let\d@llarend\endgroup - \@tabarray} -\def\endtabular{\endarray $\egroup\null} +\ifnum0<0\lltjp@array@array@loaded + \patchcmd{\@tabular}{\leavevmode}{\leavevmode\null}{}{} + \patchcmd{\@tabular}{\leavevmode\null\null}{\leavevmode\null}{}{} + \apptocmd{\endtabular}{\null}{}{} + \let\lltjp@array@array@loaded\undefined +\fi \expandafter \let \csname endtabular*\endcsname = \endtabular } \endinput diff --git a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-footmisc.sty b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-footmisc.sty index edde267d3c7..5ee7d407ce5 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-footmisc.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-footmisc.sty @@ -3,63 +3,7 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{lltjp-footmisc}[2022-02-19 Patch to footmisc for LuaTeX-ja] +\ProvidesPackage{lltjp-footmisc}[2024-07-22 Patch to footmisc for LuaTeX-ja] -\@ifpackagelater{footmisc}{2022-02-10}{%% v6.0a or later - \patchcmd{\@makenormalcolbox}{\vskip -\@outputbox@depth\@textbottom}% - {\pltx@adjust@wd@outputbox\vskip -\@outputbox@depth\@textbottom}{}{}% - \let\ltj@next\endinput -}{\let\ltj@next\relax} -\ltj@next - -% old (v5.5) -\let \if@tempswa \ifFN@bottom -\ifFN@para \@tempswatrue \fi -\if@tempswa - \ifFN@bottom - \toks@{\setbox\@outputbox \box\@cclv - \xdef\@freelist{\@freelist\@midlist}% - \global\let\@midlist\@empty - \@combinefloats - \ifvoid\footins - \else - \setbox\@outputbox \vbox\bgroup - \boxmaxdepth\@maxdepth - \unvbox\@outputbox - \vfill\relax - } - \the\FN@temptoken - \toks@\expandafter{\the\toks@\egroup\fi} - \else - \toks@{\ifvoid\footins - \setbox\@outputbox\box\@cclv - \else - \setbox\@outputbox \vbox\bgroup - \boxmaxdepth\@maxdepth - \unvbox\@cclv - } - \the\FN@temptoken - \toks@\expandafter{\the\toks@ - \egroup - \fi - \xdef\@freelist{\@freelist\@midlist}% - \global\let\@midlist\@empty - \@combinefloats - }% - \fi - \toks@\expandafter{\the\toks@ - \ifvbox\@kludgeins - \@makespecialcolbox - \else - \setbox\@outputbox \vbox to\@colht{% - \@texttop \dimen@\dp\@outputbox - \unvbox\@outputbox - \pltx@adjust@wd@outputbox - \vskip -\dimen@\@textbottom - }% - \fi - \global\maxdepth\@maxdepth - } - \edef\@makecol{\the\toks@} -\fi -\endinput +\patchcmd{\@makenormalcolbox}{\vskip -\@outputbox@depth\@textbottom}% + {\pltx@adjust@wd@outputbox\vskip -\@outputbox@depth\@textbottom}{}{}% diff --git a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-geometry.sty b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-geometry.sty index 67b0f5de7f7..641fa4a34df 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-geometry.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-geometry.sty @@ -3,7 +3,7 @@ % \NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{lltjp-geometry}[2021-10-24 Patch to geometry for LuaTeX-ja with vertical writing mode] +\ProvidesPackage{lltjp-geometry}[2024-05-16 Patch to geometry for LuaTeX-ja with vertical writing mode] \RequirePackage{expl3} \ifdefined\if@ltj@@geometry@tate\expandafter\endinput\fi \newif\if@ltj@@geometry@tate @@ -31,15 +31,6 @@ %============================================ \unless\ifdefined\ltj@ExecuteAfterPackage -\expandafter\newif\csname ifltj@ltfilehook@avail\endcsname -\@ifl@t@r\fmtversion{2020-10-01}{% - \@ifl@t@r\fmtversion{2020-10-02}{\ltj@ltfilehook@availtrue} - {% - \ifnum\patch@level<0\relax \ifnum\patch@level<-6 \ltj@ltfilehook@availtrue\fi - \else\ltj@ltfilehook@availtrue\fi - }% -}{} -\ifltj@ltfilehook@avail %% \ltj@ExecuteBeforePackage <package><code> and \ltj@ExecuteAfterPackage <package><code> %% no-starred version: do nothing if the <package> is loaded %% starred version: do <code> right away if the <package> is loaded @@ -64,11 +55,6 @@ \@ifpackageloaded{#1}{#2}{\@@ltj@ExecuteBeforePackage{#1}{#2}}% } } -\else - \RequirePackage{filehook} - \let\ltj@ExecuteAfterPackage=\AtEndOfPackageFile - \let\ltj@ExecuteBeforePackage=\AtBeginOfPackageFile -\fi \fi %============================================ @@ -89,12 +75,8 @@ \else \@gobble\iftrue\csname iftdir\endcsname \bool_set_true:N \l_tmpa_bool \fi \fi -\ifltj@ltfilehook@avail - \prop_map_inline:Nn \g__hook_begindocument_code_prop - { \tl_if_in:nnT {#2} {\tate} {\bool_set_true:N \l_tmpa_bool} } -\else - \tl_if_in:NnT \@begindocumenthook {\tate} {\bool_set_true:N \l_tmpa_bool} -\fi +\prop_map_inline:Nn \g__hook_begindocument_code_prop + { \tl_if_in:nnT {#2} {\tate} {\bool_set_true:N \l_tmpa_bool} } \bool_if:NT \l_tmpa_bool { \if@ltj@@geometry@forceoff \@ltj@@geometry@tatefalse diff --git a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-siunitx.sty b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-siunitx.sty index 9c4526c3fdb..292cbf00bfe 100644 --- a/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-siunitx.sty +++ b/Master/texmf-dist/tex/luatex/luatexja/patches/lltjp-siunitx.sty @@ -1,5 +1,5 @@ % -% lltjp-siunitx.sty 2022-12-14 +% lltjp-siunitx.sty 2024-06-10 % \NeedsTeXFormat{LaTeX2e} @@ -7,7 +7,6 @@ \RequirePackage{siunitx} \makeatletter \ExplSyntaxOn -\cs_if_exist:NTF \siunitx_print_text:n {% siunitx v3.00 \cs_set_protected:Npn \siunitx_print_text:n #1 { \text @@ -32,104 +31,26 @@ } } \cs_set_eq:NN \ltj_orig__siunitx_table_collect_begin:w \__siunitx_table_collect_begin:w -\cs_set_protected:Npn \__siunitx_table_collect_begin:w #1 \relax - { \ltj_orig__siunitx_table_collect_begin:w } -\RenewDocumentCommand \tablenum { O { } m } - { - \mode_leave_vertical: - \group_begin: - \keys_set:nn { siunitx } {#1} - \siunitx_cell_begin:w - \relax \ignorespaces #2 - \siunitx_cell_end: - \group_end: - } -}% siunitx v3.00 end -{% siunitx v2.xx -\cs_generate_variant:Nn \tl_if_in:NnT { coT } -\cs_new:Nn \__ltj_siunix_patch_inner:n { - \cs_if_exist:cT { #1_tl } { - \tl_if_in:coT { #1_tl } { \tex_char:D } { - \tl_replace_all:cnn { #1_tl } { \tex_char:D } { \ltjalchar } - } +\cs_set_protected:Npn \__siunitx_table_collect_begin:w #1 { + \token_if_eq_meaning:NNTF #1 \relax + {\ltj_orig__siunitx_table_collect_begin:w } + {\ltj_orig__siunitx_table_collect_begin:w #1 } } -} -\cs_new:Nn \__ltj_siunix_patch_inner_math:n { - \__ltj_siunix_patch_inner:n { l__siunitx_#1_math } - \__ltj_siunix_patch_inner:n { l__siunitx_#1_text } -} -\AtBeginDocument { - \__ltj_siunix_patch_inner:n { c__siunitx_minus } - \__ltj_siunix_patch_inner:n { c__siunitx_mu } - \__ltj_siunix_patch_inner_math:n { angstrom } - \__ltj_siunix_patch_inner_math:n { arcminute } - \__ltj_siunix_patch_inner_math:n { arcsecond } - \__ltj_siunix_patch_inner_math:n { ohm } -} - -%%%% new table column: S and s -\bool_new:N \l__ltj_siunitx_table_collect_relax_bool -\cs_set_protected:Npn \__siunitx_table_collect_begin_s: { - \bool_set_false:N \l__ltj_siunitx_table_collect_relax_bool - \cs_set_eq:NN \__siunitx_table_collect_token:N - \__siunitx_table_collect_token_s:N - \cs_set_eq:NN \__siunitx_table_print: \__siunitx_table_print_s: - \__siunitx_table_collect_init_s: - \__siunitx_table_collect_get: -} - -\cs_set_protected:Npn \__siunitx_table_collect_not_braced:N #1 +\cs_set_protected:Npn \__siunitx_table_collect_token:N #1 { - \token_if_eq_meaning:NNTF #1 \tex_relax:D { - \bool_if:NTF \l__ltj_siunitx_table_collect_relax_bool { - \__siunitx_table_collect_not_braced_aux_i:N #1 - } { - \bool_set_true:N \l__ltj_siunitx_table_collect_relax_bool - } - } { - \token_if_eq_meaning:NNF #1 \ltjfakeboxbdd { - \token_if_eq_meaning:NNF #1 \pltx@next@inhibitglue { - \token_if_eq_meaning:NNF #1 \tex_ignorespaces:D + \__siunitx_table_collect_search:NnF #1 { - \token_if_eq_meaning:NNF #1 \tex_unskip:D - { \__siunitx_table_collect_not_braced_aux_i:N #1 } + \ignorespaces { \__siunitx_table_collect_ignore_spaces:N #1} + \pltx@next@inhibitglue { \__siunitx_table_collect_loop:} + \ltjfakeboxbdd { \__siunitx_table_collect_loop:} + \textonly@unskip { \__siunitx_table_collect_loop:} + \unskip { \__siunitx_table_collect_loop:} + \end { \tabularnewline \end } + \relax { \__siunitx_table_collect_relax:N #1 } + \tabularnewline { \tabularnewline } + \siunitx_cell_end: { \siunitx_cell_end:} } - }}} - \__siunitx_table_collect_next: - } - -\AtBeginDocument - { - \@ifpackageloaded { mdwtab } - { - \cs_set_protected:Npn \__siunitx_table_collect_not_braced:N #1 - { - \token_if_eq_meaning:NNTF #1 \tex_relax:D { - \bool_if:NTF \l__ltj_siunitx_table_collect_relax_bool { - \__siunitx_table_collect_not_braced_aux_i:N #1 - } { - \bool_set_true:N \l__ltj_siunitx_table_collect_relax_bool - } - } { - \token_if_eq_meaning:NNF #1 \ltjfakeboxbdd { - \token_if_eq_meaning:NNF #1 \pltx@next@inhibitglue { - \token_if_eq_meaning:NNF #1 \tex_ignorespaces:D - { - \token_if_eq_meaning:NNF #1 \tex_unskip:D - { - \token_if_eq_meaning:NNF #1 \tab@setcr - { - \token_if_eq_meaning:NNF #1 \@maybe@unskip - { \__siunitx_table_collect_not_braced_aux_i:N #1 } - } - } - } - }}} - \__siunitx_table_collect_next: - } - } - { } - } + { \__siunitx_table_collect_token_aux:N #1 } } \ExplSyntaxOff \endinput diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-cssquery.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-cssquery.lua index ebb29abb15c..452656845c4 100644 --- a/Master/texmf-dist/tex/luatex/luaxml/luaxml-cssquery.lua +++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-cssquery.lua @@ -156,7 +156,7 @@ local function cssquery() -- make sure we deal with a string value = tostring(value) -- make the search string safe for pattern matching - local escaped_search = search:gsub("([%(%)%.%%%+%–%*%?%[%^%$])", "%%%1") + local escaped_search = search:gsub("([%(%)%.%%%+%-%#%*%?%[%^%$])", "%%%1") if modifier == "" then return value == search elseif modifier == "|" then @@ -434,6 +434,18 @@ local function cssquery() return querylist end + --- Remove selector from the CSS list object. + -- All actions that literaly match the given selector will be removed. + function CssQuery:remove_selector( + selector -- CSS selector to be removed + ) + for pos, obj in ipairs(self.querylist) do + if obj.source == selector then + table.remove(self.querylist, pos) + end + end + end + --- It tests list of queries agaings a DOM element and executes the --- coresponding function that is saved for the matched query. -- @return nothing diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-domobject.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-domobject.lua index 73689add5a1..dca38df0db5 100644 --- a/Master/texmf-dist/tex/luatex/luaxml/luaxml-domobject.lua +++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-domobject.lua @@ -6,16 +6,20 @@ local dom = {} local xml local handler local css_query +local html if kpse then xml = require("luaxml-mod-xml") handler = require("luaxml-mod-handler") css_query = require("luaxml-cssquery") + html = require("luaxml-mod-html") else xml = require("luaxml.mod-xml") handler = require("luaxml.mod-handler") css_query = require("luaxml.cssquery") + html = require("luaxml.mod-html") end +local HtmlParser = html.HtmlParser local void = {area = true, base = true, br = true, col = true, hr = true, img = true, input = true, link = true, meta = true, param = true} @@ -91,9 +95,14 @@ local function serialize_dom(parser, current,level, output) local format = get_action(typ, "start") insert(format, el, prepare_attributes(attr)) end - local function text(typ, text) + local function text(typ, text, parent) + local parent = parent or {} local format = get_action(typ, "text") - insert(format, escape_element(text)) + if parent.verbatim then + insert(format, text) + else + insert(format, escape_element(text)) + end end local function stop(typ, el) local format = get_action(typ, "stop") @@ -141,7 +150,7 @@ local function serialize_dom(parser, current,level, output) end start(xtype, name, attributes) - text(xtype,text_content) + text(xtype,text_content, (current or {})._parent) local children = root._children or {} for _, child in ipairs(children) do output = serialize_dom(parser,child, level + 1, output) @@ -411,8 +420,8 @@ local parse = function( return strings end - --- Get table with the inner text of an element -- leading and trailing spaces are removed and elements that contain only white space are ignored. - --- @return table + --- Get table with the inner text of an element - leading and trailing spaces are removed and elements that contain only white space are ignored. + -- @return table function DOM_Object:stripped_strings( current --- [optional] element to be selected ) @@ -612,11 +621,83 @@ local parse = function( -- include the methods to all xml nodes save_methods(parser._handler.root) -- parser: - return parser + return parser, DOM_Object +end + +-- table of elements that should be kept without XML escaping in the DOM serialization +local verbatim_elements = {script=true, style=true} + +local function html_to_dom(html_object) + -- convert parsed HTML DOM to the XML DOM + local dom, DOM_Object = parse("") -- use empty text to just initialize the DOM object + -- use root of the DOM object as the original parent + local current_parent = dom._handler.root + + local function create_node(tbl) + -- create node suitable for LuaXML DOM object + tbl._children = {} + -- this should copy methods from the DOM object to the newly created object + tbl.__index = DOM_Object + return setmetatable(tbl, DOM_Object) + end + + local function build_tree(object) + -- convert tree produced by the HTML parser to LuaXML DOM + local typ = object._type + -- process particular node types from the HTML parser + if typ == "doctype" then + current_parent:add_child_node(create_node {_name=object.name, _type="DTD"}) + elseif typ == "comment" then + current_parent:add_child_node(create_node {_text=object.text, _type="COMMENT"}) + elseif typ == "element" then + local attributes = {} + -- convert attributes to the form expected by the DOM object + for _, attr in ipairs(object.attr) do + attributes[attr.name] = attr.value + end + local element = current_parent:create_element(object.tag, attributes) + -- disable escaping of text in dom:serialize() for <script> or <style> elements + if verbatim_elements[string.lower(object.tag)] then element.verbatim = true end + current_parent:add_child_node(element) + -- set the current element as parent for the processing of children + local old_parent = current_parent + current_parent = element + -- process children + for k,v in ipairs(object.children) do + build_tree(v) + end + -- restore original parent + current_parent = old_parent + elseif typ == "text" then + local text = current_parent:create_text_node(object.text) + current_parent:add_child_node(text) + else + -- for other node types, just process the children + for k,v in ipairs(object.children) do + build_tree(v) + end + end + + end + build_tree(html_object) + return dom +end + +--- Parse HTML text as a DOM object. +-- It supports all methods as the object returned by the parse() function. +-- @param html_str string with the HTML code to be parsed +-- @return DOM_Object +local function html_parse(html_str) + local html_obj = HtmlParser:init(html_str) + local html_dom = html_obj:parse() + return html_to_dom(html_dom) end + + --- @export return { parse = parse, - serialize_dom= serialize_dom + serialize_dom= serialize_dom, + html_parse = html_parse } diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-encodings.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-encodings.lua new file mode 100644 index 00000000000..71bfc81dd1b --- /dev/null +++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-encodings.lua @@ -0,0 +1,88 @@ +--- Convert 8-bit encodings to UTF-8 +--- @module luaxml-encodings +-- this table is generated automatically by this command: +-- texlua encodings/make_encodings.lua +local encodings = { + ['ibm866'] = 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀рстуфхцчшщъыьэюяЁёЄєЇїЎў°∙·√№¤■ ', + ['iso-8859-5'] = '�������������������������������� ЁЂЃЄЅІЇЈЉЊЋЌЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя№ёђѓєѕіїјљњћќ§ўџ', + ['macintosh'] = 'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›fifl‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ', + ['windows-1251'] = 'ЂЃ‚ѓ„…†‡€‰Љ‹ЊЌЋЏђ‘’“”•–—�™љ›њќћџ ЎўЈ¤Ґ¦§Ё©Є«¬®Ї°±Ііґµ¶·ё№є»јЅѕїАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя', + ['iso-8859-13'] = '�������������������������������� ”¢£¤„¦§Ø©Ŗ«¬®Æ°±²³“µ¶·ø¹ŗ»¼½¾æĄĮĀĆÄÅĘĒČÉŹĖĢĶĪĻŠŃŅÓŌÕÖ×ŲŁŚŪÜŻŽßąįāćäåęēčéźėģķīļšńņóōõö÷ųłśūüżž’', + ['iso-8859-6'] = '�������������������������������� ���¤�������،�������������؛���؟�ءآأؤإئابةتثجحخدذرزسشصضطظعغ�����ـفقكلمنهوىيًٌٍَُِّْ', + ['iso-8859-8'] = '�������������������������������� �¢£¤¥¦§¨©×«¬®¯°±²³´µ¶·¸¹÷»¼½¾��������������������������������‗אבגדהוזחטיךכלםמןנסעףפץצקרשת��', + ['iso-8859-4'] = '�������������������������������� ĄĸŖ¤ĨĻ§¨ŠĒĢŦŽ¯°ą˛ŗ´ĩļˇ¸šēģŧŊžŋĀÁÂÃÄÅÆĮČÉĘËĖÍÎĪĐŅŌĶÔÕÖ×ØŲÚÛÜŨŪßāáâãäåæįčéęëėíîīđņōķôõö÷øųúûüũū˙', + ['koi8-r'] = '─│┌┐└┘├┤┬┴┼▀▄█▌▐░▒▓⌠■∙√≈≤≥ ⌡°²·÷═║╒ё╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡Ё╢╣╤╥╦╧╨╩╪╫╬©юабцдефгхийклмнопярстужвьызшэщчъЮАБЦДЕФГХИЙКЛМНОПЯРСТУЖВЬЫЗШЭЩЧЪ', + ['iso-8859-7'] = '�������������������������������� ‘’£€₯¦§¨©ͺ«¬�―°±²³΄΅Ά·ΈΉΊ»Ό½ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ�ΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ', + ['iso-8859-3'] = '�������������������������������� Ħ˘£¤�Ĥ§¨İŞĞĴ�Ż°ħ²³´µĥ·¸ışğĵ½�żÀÁÂ�ÄĊĈÇÈÉÊËÌÍÎÏ�ÑÒÓÔĠÖ×ĜÙÚÛÜŬŜßàáâ�äċĉçèéêëìíîï�ñòóôġö÷ĝùúûüŭŝ˙', + ['windows-1256'] = '€پ‚ƒ„…†‡ˆ‰ٹ‹Œچژڈگ‘’“”•–—ک™ڑ›œں ،¢£¤¥¦§¨©ھ«¬®¯°±²³´µ¶·¸¹؛»¼½¾؟ہءآأؤإئابةتثجحخدذرزسشصض×طظعغـفقكàلâمنهوçèéêëىيîïًٌٍَôُِ÷ّùْûüے', + ['windows-1258'] = '€�‚ƒ„…†‡ˆ‰�‹Œ����‘’“”•–—˜™�›œ��Ÿ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂĂÄÅÆÇÈÉÊË̀ÍÎÏĐÑ̉ÓÔƠÖ×ØÙÚÛÜỮßàáâăäåæçèéêë́íîïđṇ̃óôơö÷øùúûüư₫ÿ', + ['iso-8859-14'] = '�������������������������������� Ḃḃ£ĊċḊ§Ẁ©ẂḋỲ®ŸḞḟĠġṀṁ¶ṖẁṗẃṠỳẄẅṡÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏŴÑÒÓÔÕÖṪØÙÚÛÜÝŶßàáâãäåæçèéêëìíîïŵñòóôõöṫøùúûüýŷÿ', + ['windows-1252'] = '€�‚ƒ„…†‡ˆ‰Š‹Œ�Ž��‘’“”•–—˜™š›œ�žŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ', + ['iso-8859-15'] = '�������������������������������� ¡¢£€¥Š§š©ª«¬®¯°±²³Žµ¶·ž¹º»ŒœŸ¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ', + ['x-mac-cyrillic'] = 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ†°Ґ£§•¶І®©™Ђђ≠Ѓѓ∞±≤≥іµґЈЄєЇїЉљЊњјЅ¬√ƒ≈∆«»… ЋћЌќѕ–—“”‘’÷„ЎўЏџ№Ёёяабвгдежзийклмнопрстуфхцчшщъыьэю€', + ['windows-1254'] = '€�‚ƒ„…†‡ˆ‰Š‹Œ����‘’“”•–—˜™š›œ��Ÿ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏĞÑÒÓÔÕÖ×ØÙÚÛÜİŞßàáâãäåæçèéêëìíîïğñòóôõö÷øùúûüışÿ', + ['windows-1255'] = '€�‚ƒ„…†‡ˆ‰�‹�����‘’“”•–—˜™�›���� ¡¢£₪¥¦§¨©×«¬®¯°±²³´µ¶·¸¹÷»¼½¾¿ְֱֲֳִֵֶַָֹֺֻּֽ־ֿ׀ׁׂ׃װױײ׳״�������אבגדהוזחטיךכלםמןנסעףפץצקרשת��', + ['windows-1250'] = '€�‚�„…†‡�‰Š‹ŚŤŽŹ�‘’“”•–—�™š›śťžź ˇ˘Ł¤Ą¦§¨©Ş«¬®Ż°±˛ł´µ¶·¸ąş»Ľ˝ľżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙', + ['koi8-u'] = '─│┌┐└┘├┤┬┴┼▀▄█▌▐░▒▓⌠■∙√≈≤≥ ⌡°²·÷═║╒ёє╔ії╗╘╙╚╛ґў╞╟╠╡ЁЄ╣ІЇ╦╧╨╩╪ҐЎ©юабцдефгхийклмнопярстужвьызшэщчъЮАБЦДЕФГХИЙКЛМНОПЯРСТУЖВЬЫЗШЭЩЧЪ', + ['iso-8859-10'] = '�������������������������������� ĄĒĢĪĨĶ§ĻĐŠŦŽŪŊ°ąēģīĩķ·ļđšŧž―ūŋĀÁÂÃÄÅÆĮČÉĘËĖÍÎÏÐŅŌÓÔÕÖŨØŲÚÛÜÝÞßāáâãäåæįčéęëėíîïðņōóôõöũøųúûüýþĸ', + ['windows-1257'] = '€�‚�„…†‡�‰�‹�¨ˇ¸�‘’“”•–—�™�›�¯˛� �¢£¤�¦§Ø©Ŗ«¬®Æ°±²³´µ¶·ø¹ŗ»¼½¾æĄĮĀĆÄÅĘĒČÉŹĖĢĶĪĻŠŃŅÓŌÕÖ×ŲŁŚŪÜŻŽßąįāćäåęēčéźėģķīļšńņóōõö÷ųłśūüżž˙', + ['windows-1253'] = '€�‚ƒ„…†‡�‰�‹�����‘’“”•–—�™�›���� ΅Ά£¤¥¦§¨©�«¬®―°±²³΄µ¶·ΈΉΊ»Ό½ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ�ΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώ', + ['iso-8859-2'] = '�������������������������������� Ą˘Ł¤ĽŚ§¨ŠŞŤŹŽŻ°ą˛ł´ľśˇ¸šşťź˝žżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙', + ['windows-874'] = '€����…�����������‘’“”•–—�������� กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู����฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛', + ['iso-8859-16'] = '�������������������������������� ĄąŁ€„Š§š©Ș«ŹźŻ°±ČłŽ”¶·žčș»ŒœŸżÀÁÂĂÄĆÆÇÈÉÊËÌÍÎÏĐŃÒÓÔŐÖŚŰÙÚÛÜĘȚßàáâăäćæçèéêëìíîïđńòóôőöśűùúûüęțÿ', +} + + +local utfchar = utf8.char + +--- Try to find an encoding in HTML string +---@param str string HTML document +---@param len number count of characters from the start of the string where it should search for the encoding metadata +---@return string encoding identifier, or nil and message if no encoding was found +local function find_html_encoding(str, len) + -- try to find encoding in the html document + -- we limit search length, because encoding should be in the document head, ideally near the start + local len = len or 4096 + local sub = str:sub(1, len) + for meta in sub:gmatch("<meta (.-)>") do + local charset = meta:match("charset%s*=%s*[\"']?(.-)[\"']") + if charset then return string.lower(charset) end + end + return nil, "Cannot find the document encoding" +end + +local function load_mapping(enc_name) + local enc_name = enc_name or "" + local enc = encodings[enc_name] + if not enc then return nil, "Cannot load encoding " .. enc_name end + local mapping,i = {}, 128 + for pos, codepoint in utf8.codes(enc) do + mapping[i] = utfchar(codepoint) + i = i + 1 + end + return mapping +end + +--- Convert string to utf-8 +---@param text string for converting +---@param mapping table +---@return string converted string +local function recode(text, mapping) + return text:gsub("(.)", function(char) + local charpoint = string.byte(char) + if charpoint > 127 then + return mapping[charpoint] + else + return false + end + end) +end + + +return { + encodings = encodings, + find_html_encoding = find_html_encoding, + load_mapping = load_mapping, + recode = recode +} + diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-html.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-html.lua index 69e32b16e22..d8c2b7e9327 100644 --- a/Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-html.lua +++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-mod-html.lua @@ -1,3 +1,6 @@ +--- HTML parsing module for LuaXML +-- @module luaxml-mod-html +-- @author Michal Hoftich <michal.h21@gmail.com -- Copyright Michal Hoftich, 2022 -- HTML parser inspired by https://browser.engineering/html.html -- but then redone using https://html.spec.whatwg.org/multipage/parsing.html @@ -1540,9 +1543,14 @@ local function is_formatting_element(name) return formatting_element_names[name] end -local special_elements = {} +local function hash_from_array(tbl) + local t = {} + for _, v in ipairs(tbl) do t[v] = true end + return t +end + -local special_elements_list = {"address", "applet", "area", "article", "aside", +local special_elements_list = hash_from_array {"address", "applet", "area", "article", "aside", "base", "basefont", "bgsound", "blockquote", "body", "br", "button", "caption", "center", "col", "colgroup", "dd", "details", "dir", "div", "dl", "dt", "embed", "fieldset", "figcaption", "figure", "footer", "form", "frame", @@ -1555,13 +1563,9 @@ local special_elements_list = {"address", "applet", "area", "article", "aside", "mi","mo","mn","ms","mtext", "annotation-xml","foreignObject","desc", "title" } -for k,v in ipairs(special_elements_list) do - special_elements[v] = true -end - local function is_special(name) - return special_elements[name] + return special_elements_list[name] end -- these lists are used in HtmlParser:generate_implied_endtags() @@ -1649,8 +1653,12 @@ local HtmlTreeStates = {} +--- @type HtmlParser local HtmlParser = {} +--- Initialize the HTML Object +---@param body string HTML to be parsed +---@return table initialized object function HtmlParser:init(body) local o ={} setmetatable(o, self) @@ -1686,10 +1694,11 @@ for _,v in ipairs(self_closing_tags_list) do self_closing_tags[v] = true end - +--- Execute the HTML parser +--- @return table Root node of the HTML DOM function HtmlParser:parse() -- we assume utf8 input, you must convert it yourself if the source is - -- in a different encoding + -- in a different encoding. for example using luaxml-encodings library self.text = {} self.state = self.default_state -- this should enable us to pass over some characters that we want to ignore @@ -1791,6 +1800,7 @@ function HtmlParser:emit(token) elseif token_type == "start_tag" then self:add_text() -- self:start_attribute() + self:reset_insertion_mode() self:start_tag() -- print("Emit start tag", table.concat(token.name)) -- save last attribute @@ -1882,8 +1892,139 @@ function HtmlParser:set_xmlns(node, parent) end end +function HtmlParser:pop_element() + -- close the current element and add it to the DOM + local el = self:close_element() + local parent = self:get_parent() + parent:add_child(el) + return el +end + +local close_p_at_start = hash_from_array {"address", "article", "aside", "blockquote", "center", "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", "search", "section", "summary", "ul", "pre", "listing", "form", "table", "xmp", "hr"} + +local close_headers = hash_from_array {"h1", "h2", "h3", "h4", "h5", "h6"} + +local body_modes = hash_from_array {"in_body", "in_cell", "in_row", "in_select", "in_table", "in_table_body", "in_frameset"} + +local list_items = hash_from_array {"li", "dt", "dd"} + +local close_address_at_end = hash_from_array{"address", "article", "aside", "blockquote", "button", "center", "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", "search", "section", "summary", "ul", "form"} + + +function HtmlParser:close_unfinished(name) + -- close all unfinished elements until the element with the given name is found + for i = #self.unfinished, 1, -1 do + local el = self:pop_element() + if el.tag == name then + break + end + end +end + + +function HtmlParser:close_paragraph() + -- close currently open <p> elements + self:close_unfinished("p") +end + +function HtmlParser:current_element_name() + -- return name of the current element + return self:get_parent().tag +end + +local not_specials = hash_from_array { "address", "div", "p"} + +local function handle_list_item(self, name) + -- we handle li, dt and dd. dt and dd should close each other, li closes only itself + local names = {dt = true, dd = true} + if name == "li" then names = {li=true} end + for i = #self.unfinished, 1, -1 do + local current = self.unfinished[i] + local current_tag = current.tag + if names[current_tag] then + self:generate_implied_endtags(nil, {current.tag}) + for j = #self.unfinished, i, -1 do + self:pop_element() + end + break + elseif is_special(current_tag) and not not_specials[name] then + break + end + end +end + +local close_paragraph = function(self) + if is_in_button_scope(self, "p") then + self:close_paragraph() + end +end + +function HtmlParser:handle_insertion_mode(token) + -- simple handling of https://html.spec.whatwg.org/multipage/parsing.html#tree-construction + -- we don't support most rules, just the most important for avoiding mismatched tags + + if body_modes[self.insertion_mode] then + if token.type == "start_tag" then + local name = table.concat(token.name) + if close_p_at_start[name] then close_paragraph(self) end + if close_headers[name] then + close_paragraph(self) + -- close current element if it is already header + if close_headers[self:current_element_name()] then + self:pop_element() + end + elseif name == "pre" or name == "listing" then + -- we should ignore next "\n" char token + elseif name == "image" then + -- image tag is an error, change to <img> + token.name = {"img"} + elseif list_items[name] then + handle_list_item(self, name) + close_paragraph(self) + end + elseif token.type == "end_tag" then + local name = table.concat(token.name) + if close_address_at_end[name] then + if is_in_scope(self, name, {}) then + self:close_unfinished(name) + else + token.type = "ignore" + end + elseif name == "p" then + if not is_in_button_scope(self, "p") then + local parent = self:get_parent() + local node = Element:init("p", parent) + table.insert(self.unfinished, node) + end + -- use self:close_paragraph() instead of close_paragraph() because we don't need to check scope at this point + self:close_paragraph() + elseif name == "br" then + token.type = "start_tag" + elseif close_headers[name] then + local header_in_scope = false + -- detect, if there are any open h1-h6 tag and close it + for el, _ in pairs(close_headers) do + if is_in_scope(self, el, {}) then + header_in_scope = el + break + end + end + if not header_in_scope then + token.type = "ignore" + else + self:close_unfinished(header_in_scope) + end + end + end + end +end + +local rawtext_elements = hash_from_array {"style", "textarea", "xmp"} + + function HtmlParser:start_tag() local token = self.current_token + self:handle_insertion_mode(token) if token.type == "start_tag" then -- close all currently opened attributes self:start_attribute() @@ -1894,18 +2035,18 @@ function HtmlParser:start_tag() node.attr = token.attr node.self_closing = token.self_closing self:set_xmlns(node) - -- + -- in this handler we should close <p> or <li> elements without explicit closing tags if token.self_closing -- <img /> or self_closing_tags[name] -- void elements then - parent:add_child(node) + parent:add_child(node, node.tag) else -- add to the unfinished list table.insert(self.unfinished, node) end if name == "title" then self.element_state = "rcdata" - elseif name == "style" then + elseif rawtext_elements[name] then self.element_state = "rawtext" elseif name == "script" then self.element_state = "script_data" @@ -1916,11 +2057,13 @@ end function HtmlParser:end_tag() -- close current opened element local token = self.current_token + self:handle_insertion_mode(token) if token.type == "end_tag" then if #self.unfinished==0 then return nil end - local node = self:close_element() - local parent = self:get_parent() - parent:add_child(node) + -- close the current element only if the token is in the current scope + if is_in_scope(self, table.concat(token.name), {}) then + self:pop_element() + end end end @@ -1964,6 +2107,21 @@ function HtmlParser:adjusted_current_node() end +local simple_modes = { + body = "in_body", + td = "in_cell", + th = "in_cell", + tr = "in_row", + tbody = "in_table_body", + thead = "in_table_body", + tfoot = "in_table_body", + caption = "in_caption", + colgroup = "in_column_group", + table = "in_table", + template = "current_template_insertion_mode", + frameset = "in_frameset" +} + function HtmlParser:reset_insertion_mode() -- https://html.spec.whatwg.org/multipage/parsing.html#reset-the-insertion-mode-appropriately local last = false @@ -1977,16 +2135,33 @@ function HtmlParser:reset_insertion_mode() if name == "head" and last == true then self:switch_insertion("in_head") return - elseif name == "body" then - self:switch_insertion("in_body") - return elseif name == "html" then - if self.head_pointer then + if not self.head_pointer then self:switch_insertion("before_head") return else self:switch_insertion("after_head") + return + end + elseif name == "select" then + if not last then + for x = position -1, 1, -1 do + if x == 1 then break end + local ancestor = self.unfinished[x] + local ancestor_name = ancestor.tag + if ancestor_name == "template" then + break + elseif ancestor_name == "table" then + self:switch_insertion("in_select_in_table") + return + end + end end + self:switch_insertion("in_select") + return + elseif simple_modes[name] then + self:switch_insertion(simple_modes[name]) + return elseif last == true then self:switch_insertion("in_body") return @@ -2006,8 +2181,8 @@ function HtmlParser:generate_implied_endtags(included, ignored) local current = self:current_node() or {} -- keep removing elements while they are in the "included" list if included[current.tag] then - table.remove(self.unfinished) - self:generate_implied_endtags(ignored) + self:pop_element() + self:generate_implied_endtags(included, ignored) end end @@ -2023,9 +2198,7 @@ function HtmlParser:finish() self:start_tag("html") end while #self.unfinished > 0 do - local node = self:close_element() - local parent = self:get_parent() - parent:add_child(node) + self:pop_element() end -- return root element return self.Document -- self:close_element() diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-namedentities.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-namedentities.lua index 0e6399ab135..5c5ff007770 100644 --- a/Master/texmf-dist/tex/luatex/luaxml/luaxml-namedentities.lua +++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-namedentities.lua @@ -1,2234 +1,2233 @@ -local utf8 = require "utf8" return { -["Subset"]="⋐", -["Chi"]="Χ", -["tprime"]="‴", -["lneq"]="⪇", -["RightUpTeeVector"]="⥜", -["cscr"]="𝒸", -["leftrightarrows"]="⇆", -["LeftRightVector"]="⥎", -["Laplacetrf"]="ℒ", -["boxHd"]="╤", -["GreaterSlantEqual"]="⩾", -["sqsub"]="⊏", -["nvdash"]="⊬", -["boxHu"]="╧", -["vcy"]="в", -["DiacriticalDoubleAcute"]="˝", -["nvHarr"]="⤄", -["lnapprox"]="⪉", -["fscr"]="𝒻", -["OverBracket"]="⎴", -["pm"]="±", -["ovbar"]="⌽", -["larrhk"]="↩", -["atilde"]="ã", -["Igrave"]="Ì", -["profsurf"]="⌓", -["Sigma"]="Σ", -["rHar"]="⥤", -["Gcedil"]="Ģ", -["sect"]="§", -["nmid"]="∤", -["escr"]="ℯ", -["delta"]="δ", -["male"]="♂", -["Integral"]="∫", -["RightUpVectorBar"]="⥔", -["alefsym"]="ℵ", -["nsqsube"]="⋢", -["Nacute"]="Ń", -["mcomma"]="⨩", -["ApplyFunction"]=utf8.char(0x2061), -["rfisht"]="⥽", -["phmmat"]="ℳ", -["rarrw"]="↝", -["backepsilon"]="϶", -["hscr"]="𝒽", -["middot"]="·", -["ldrushar"]="⥋", -["supne"]="⊋", -["Longleftrightarrow"]="⟺", -["Oacute"]="Ó", -["af"]="", +["Implies"]="⇒", +["longrightarrow"]="⟶", +["precneqq"]="⪵", +["geqslant"]="⩾", +["Product"]="∏", +["Scirc"]="Ŝ", +["lvnE"]="≨︀", +["dotminus"]="∸", +["sscr"]="𝓈", +["capdot"]="⩀", +["dlcrop"]="⌍", +["omacr"]="ō", +["sext"]="✶", +["xutri"]="△", +["xrArr"]="⟹", +["glE"]="⪒", +["NotSupersetEqual"]="⊉", +["backsim"]="∽", ["sup"]="⊃", -["lozenge"]="◊", -["scy"]="с", -["gscr"]="ℊ", -["smallsetminus"]="∖", +["realpart"]="ℜ", ["hardcy"]="ъ", -["DoubleVerticalBar"]="∥", -["Yuml"]="Ÿ", -["plusdu"]="⨥", -["NotSucceedsSlantEqual"]="⋡", -["nwnear"]="⤧", -["lfloor"]="⌊", -["rcy"]="р", -["jscr"]="𝒿", -["AMP"]="&", -["acute"]="´", -["uacute"]="ú", -["Or"]="⩔", -["rdldhar"]="⥩", -["langd"]="⦑", -["Iacute"]="Í", -["spar"]="∥", -["ucy"]="у", -["rsh"]="↱", -["Kcedil"]="Ķ", -["iscr"]="𝒾", -["lambda"]="λ", -["lbrkslu"]="⦍", -["napE"]="⩰̸", -["shcy"]="ш", -["nsupE"]="⫆̸", -["Ycirc"]="Ŷ", -["sect"]="§", -["precnapprox"]="⪹", -["iocy"]="ё", -["dzcy"]="џ", +["sqcap"]="⊓", +["brvbar"]="¦", +["LT"]="<", +["udarr"]="⇅", +["lozf"]="⧫", +["Eogon"]="Ę", +["frac18"]="⅛", +["lesssim"]="≲", +["wedbar"]="⩟", +["EmptyVerySmallSquare"]="▫", +["aring"]="å", +["lesdoto"]="⪁", +["ltquest"]="⩻", +["LeftUpTeeVector"]="⥠", +["FilledSmallSquare"]="◼", +["mho"]="℧", +["NotGreaterTilde"]="≵", +["xharr"]="⟷", +["NotSucceedsEqual"]="⪰̸", +["sqsupset"]="⊐", +["Ccedil"]="Ç", +["aacute"]="á", +["succcurlyeq"]="≽", +["gneq"]="⪈", +["forall"]="∀", +["rightharpoonup"]="⇀", +["leftarrow"]="←", +["ang"]="∠", +["mapstoup"]="↥", +["xscr"]="𝓍", +["NotTildeEqual"]="≄", +["ograve"]="ò", +["boxdl"]="┐", +["DownLeftVectorBar"]="⥖", +["nsup"]="⊅", +["shchcy"]="щ", +["demptyv"]="⦱", +["apos"]="'", +["between"]="≬", +["leftrightsquigarrow"]="↭", +["gtcc"]="⪧", +["grave"]="`", +["bigodot"]="⨀", +["oline"]="‾", +["angmsdad"]="⦫", +["NotCongruent"]="≢", +["gtquest"]="⩼", +["fltns"]="▱", +["smeparsl"]="⧤", +["Eta"]="Η", +["ltdot"]="⋖", +["rhard"]="⇁", +["rscr"]="𝓇", ["ETH"]="Ð", -["GreaterTilde"]="≳", -["coprod"]="∐", -["rpar"]=")", -["lthree"]="⋋", -["ImaginaryI"]="ⅈ", -["DoubleUpDownArrow"]="⇕", -["intercal"]="⊺", -["spadesuit"]="♠", -["boxVH"]="╬", -["smashp"]="⨳", -["nu"]="ν", -["Agrave"]="À", -["boxtimes"]="⊠", -["sext"]="✶", -["nsub"]="⊄", -["oelig"]="œ", -["GreaterEqual"]="≥", -["TRADE"]="™", -["dtrif"]="▾", -["nless"]="≮", -["mu"]="μ", -["larrsim"]="⥳", -["GreaterEqualLess"]="⋛", -["Uuml"]="Ü", -["LeftArrowBar"]="⇤", -["ntrianglerighteq"]="⋭", -["DiacriticalDot"]="˙", -["LongLeftArrow"]="⟵", -["agrave"]="à", -["lmoustache"]="⎰", -["nvDash"]="⊭", -["para"]="¶", -["nlt"]="≮", -["Eacute"]="É", -["spades"]="♠", -["lrcorner"]="⌟", -["checkmark"]="✓", -["lessdot"]="⋖", -["opar"]="⦷", -["nVDash"]="⊯", -["pertenk"]="‱", -["range"]="⦥", -["Pr"]="⪻", -["Gammad"]="Ϝ", -["ape"]="≊", -["Longleftarrow"]="⟸", -["boxV"]="║", -["rightthreetimes"]="⋌", +["iocy"]="ё", +["semi"]=";", +["nexists"]="∄", +["approxeq"]="≊", +["compfn"]="∘", +["sstarf"]="⋆", +["angrtvbd"]="⦝", ["gnap"]="⪊", -["vzigzag"]="⦚", -["Ecirc"]="Ê", -["npar"]="∦", -["notinvb"]="⋷", -["Tau"]="Τ", -["blank"]="␣", -["Kappa"]="Κ", -["rmoust"]="⎱", -["GJcy"]="Ѓ", -["ufisht"]="⥾", -["NotLeftTriangleBar"]="⧏̸", -["bigvee"]="⋁", -["lrhar"]="⇋", -["supset"]="⊃", -["uwangle"]="⦧", -["UpTeeArrow"]="↥", -["supE"]="⫆", -["imof"]="⊷", -["leg"]="⋚", -["notniva"]="∌", -["nvgt"]=">⃒", -["rthree"]="⋌", -["yfr"]="𝔶", -["uHar"]="⥣", -["nshortmid"]="∤", -["biguplus"]="⨄", -["barvee"]="⊽", -["iff"]="⇔", -["boxvH"]="╪", -["simrarr"]="⥲", -["Egrave"]="È", -["ntilde"]="ñ", -["VerticalTilde"]="≀", -["sigmaf"]="ς", -["khcy"]="х", -["planck"]="ℏ", -["NotNestedGreaterGreater"]="⪢̸", -["NonBreakingSpace"]=" ", -["OpenCurlyQuote"]="‘", -["supsim"]="⫈", -["cudarrl"]="⤸", -["theta"]="θ", -["notindot"]="⋵̸", -["Omicron"]="Ο", -["ccedil"]="ç", -["colone"]="≔", -["bscr"]="𝒷", -["Dagger"]="‡", -["comma"]=",", +["mDDot"]="∺", +["udblac"]="ű", ["Acirc"]="Â", -["plankv"]="ℏ", -["boxdr"]="┌", -["rarrap"]="⥵", -["rmoustache"]="⎱", -["LeftAngleBracket"]="⟨", -["Cacute"]="Ć", -["blk34"]="▓", -["rfloor"]="⌋", -["awconint"]="∳", -["sfrown"]="⌢", -["middot"]="·", -["ascr"]="𝒶", -["dot"]="˙", -["caron"]="ˇ", -["image"]="ℑ", -["esdot"]="≐", -["ohbar"]="⦵", -["rarrb"]="⇥", -["submult"]="⫁", -["plusb"]="⊞", -["oopf"]="𝕠", -["breve"]="˘", -["conint"]="∮", -["angrt"]="∟", -["nbump"]="≎̸", -["raquo"]="»", -["Iuml"]="Ï", -["Rcedil"]="Ŗ", -["minus"]="−", -["nsucceq"]="⪰̸", -["telrec"]="⌕", -["CHcy"]="Ч", -["nrightarrow"]="↛", -["NotRightTriangle"]="⋫", -["zfr"]="𝔷", -["succ"]="≻", -["Aacute"]="Á", -["NestedLessLess"]="≪", -["RightTeeArrow"]="↦", -["homtht"]="∻", -["Cup"]="⋓", -["lcub"]="{", -["race"]="∽̱", -["THORN"]="Þ", -["propto"]="∝", -["odblac"]="ő", -["mumap"]="⊸", -["filig"]="fi", -["ecir"]="≖", -["DoubleRightArrow"]="⇒", -["npreceq"]="⪯̸", -["female"]="♀", -["LeftFloor"]="⌊", -["gtrarr"]="⥸", -["dotsquare"]="⊡", -["map"]="↦", -["OverBrace"]="⏞", -["zopf"]="𝕫", -["rang"]="⟩", -["laquo"]="«", -["elsdot"]="⪗", -["frac56"]="⅚", -["Iuml"]="Ï", -["ocirc"]="ô", -["clubs"]="♣", -["IOcy"]="Ё", -["scnE"]="⪶", -["deg"]="°", -["simgE"]="⪠", -["nap"]="≉", -["blacktriangle"]="▴", -["edot"]="ė", -["epsiv"]="ϵ", -["napprox"]="≉", -["NotGreaterSlantEqual"]="⩾̸", -["prap"]="⪷", -["lnap"]="⪉", -["circledS"]="Ⓢ", -["lang"]="⟨", -["qprime"]="⁗", -["aelig"]="æ", -["ncongdot"]="⩭̸", -["supdsub"]="⫘", -["nltrie"]="⋬", -["xopf"]="𝕩", +["UnderParenthesis"]="⏝", +["nisd"]="⋺", +["lneq"]="⪇", +["Dagger"]="‡", +["qscr"]="𝓆", +["gneqq"]="≩", +["doteqdot"]="≑", +["DoubleVerticalBar"]="∥", +["emptyset"]="∅", +["lEg"]="⪋", +["subseteq"]="⊆", +["dwangle"]="⦦", +["dotplus"]="∔", +["NotPrecedesSlantEqual"]="⋠", +["bnot"]="⌐", ["LeftDownVector"]="⇃", -["duarr"]="⇵", -["urcorner"]="⌝", -["nvltrie"]="⊴⃒", -["Pi"]="Π", +["leg"]="⋚", +["Therefore"]="∴", +["Omega"]="Ω", +["acE"]="∾̳", +["Auml"]="Ä", +["xvee"]="⋁", ["Eacute"]="É", -["nGtv"]="≫̸", -["icirc"]="î", -["twoheadleftarrow"]="↞", +["LeftFloor"]="⌊", +["swnwar"]="⤪", +["Jukcy"]="Є", +["nless"]="≮", +["NotSquareSupersetEqual"]="⋣", +["larrfs"]="⤝", +["gcy"]="г", +["solbar"]="⌿", +["rharul"]="⥬", +["dscy"]="ѕ", +["yen"]="¥", +["ForAll"]="∀", +["DownArrow"]="↓", +["qprime"]="⁗", +["NegativeThickSpace"]="", +["part"]="∂", +["Iukcy"]="І", +["ltcc"]="⪦", +["xlArr"]="⟸", +["fcy"]="ф", +["gl"]="≷", +["rsh"]="↱", +["lrhar"]="⇋", ["divide"]="÷", -["yopf"]="𝕪", -["ntlg"]="≸", -["Darr"]="↡", +["bemptyv"]="⦰", +["rx"]="℞", +["ecy"]="э", +["ApplyFunction"]="", +["divideontimes"]="⋇", +["prod"]="∏", +["boxHU"]="╩", +["HARDcy"]="Ъ", +["mstpos"]="∾", +["llcorner"]="⌞", +["rsquor"]="’", +["NotTildeFullEqual"]="≇", +["mlcp"]="⫛", +["cemptyv"]="⦲", +["Proportional"]="∝", +["DiacriticalDoubleAcute"]="˝", +["dcy"]="д", +["Yacute"]="Ý", +["egsdot"]="⪘", ["hslash"]="ℏ", -["Coproduct"]="∐", -["profline"]="⌒", -["imagline"]="ℐ", -["chcy"]="ч", -["ccedil"]="ç", -["PrecedesEqual"]="⪯", -["jcirc"]="ĵ", -["nang"]="∠⃒", -["Icirc"]="Î", -["nbumpe"]="≏̸", -["incare"]="℅", -["tint"]="∭", -["nhArr"]="⇎", -["emsp13"]=" ", -["blacktriangleleft"]="◂", -["Star"]="⋆", -["hkswarow"]="⤦", -["NegativeThickSpace"]="", -["eDot"]="≑", -["Euml"]="Ë", -["scaron"]="š", -["RightDownVectorBar"]="⥕", -["cent"]="¢", -["jukcy"]="є", -["backsimeq"]="⋍", -["Ncy"]="Н", -["DD"]="ⅅ", -["circledast"]="⊛", -["ccirc"]="ĉ", -["rsaquo"]="›", -["YIcy"]="Ї", -["iquest"]="¿", -["NotExists"]="∄", -["iukcy"]="і", -["Tab"]=" ", -["Equilibrium"]="⇌", -["nLt"]="≪⃒", -["yen"]="¥", -["CircleDot"]="⊙", -["CircleMinus"]="⊖", -["topf"]="𝕥", -["equivDD"]="⩸", -["DoubleLeftRightArrow"]="⇔", -["ngsim"]="≵", -["upuparrows"]="⇈", -["ll"]="≪", -["cirscir"]="⧂", -["ShortLeftArrow"]="←", -["angmsdag"]="⦮", -["shortmid"]="∣", -["ngeqslant"]="⩾̸", -["subset"]="⊂", -["curvearrowleft"]="↶", -["gdot"]="ġ", -["circledcirc"]="⊚", -["Pcy"]="П", -["asymp"]="≈", -["boxUr"]="╙", -["uopf"]="𝕦", -["frac15"]="⅕", -["Iacute"]="Í", -["Utilde"]="Ũ", -["Congruent"]="≡", -["nhpar"]="⫲", -["capbrcup"]="⩉", -["ordf"]="ª", -["SOFTcy"]="Ь", -["nlArr"]="⇍", -["not"]="¬", -["ffllig"]="ffl", -["nprec"]="⊀", -["Hat"]="^", +["infin"]="∞", ["Auml"]="Ä", -["ntriangleleft"]="⋪", -["longmapsto"]="⟼", -["nvrtrie"]="⊵⃒", -["timesb"]="⊠", -["lsh"]="↰", -["Jcy"]="Й", -["gcirc"]="ĝ", -["Oacute"]="Ó", -["vartriangleright"]="⊳", -["NotRightTriangleEqual"]="⋭", -["Rightarrow"]="⇒", -["ast"]="*", -["hamilt"]="ℋ", -["Ncedil"]="Ņ", -["apE"]="⩰", -["macr"]="¯", -["cwconint"]="∲", -["Kcy"]="К", -["Ucirc"]="Û", -["hcirc"]="ĥ", -["yacy"]="я", -["uml"]="¨", -["popf"]="𝕡", -["bumpE"]="⪮", -["varnothing"]="∅", -["RightUpVector"]="↾", -["curren"]="¤", -["Scedil"]="Ş", -["FilledVerySmallSquare"]="▪", -["dollar"]="$", -["Lcy"]="Л", -["longrightarrow"]="⟶", -["LeftTee"]="⊣", -["excl"]="!", -["acirc"]="â", -["wp"]="℘", -["lcaron"]="ľ", -["NotGreaterFullEqual"]="≧̸", -["CounterClockwiseContourIntegral"]="∳", -["sup2"]="²", -["rnmid"]="⫮", -["Lcedil"]="Ļ", -["ddotseq"]="⩷", -["tbrk"]="⎴", -["mlcp"]="⫛", -["rarrhk"]="↪", -["Tilde"]="∼", -["Mcy"]="М", -["aring"]="å", -["rarrsim"]="⥴", -["nopf"]="𝕟", -["aelig"]="æ", -["order"]="ℴ", -["diamondsuit"]="♦", -["nvsim"]="∼⃒", -["lhard"]="↽", -["commat"]="@", -["qint"]="⨌", -["lsquo"]="‘", -["sqcaps"]="⊓︀", -["circlearrowleft"]="↺", -["NotElement"]="∉", -["measuredangle"]="∡", -["Atilde"]="Ã", -["CapitalDifferentialD"]="ⅅ", -["kgreen"]="ĸ", -["Cscr"]="𝒞", +["Wcirc"]="Ŵ", +["larrtl"]="↢", +["Aacute"]="Á", +["LeftTriangleEqual"]="⊴", +["NegativeMediumSpace"]="", +["TripleDot"]="⃛", +["utdot"]="⋰", +["isinv"]="∈", +["looparrowright"]="↬", +["loang"]="⟬", +["multimap"]="⊸", ["NotLessEqual"]="≰", -["trpezium"]="⏢", -["ne"]="≠", -["NotSuperset"]="⊃⃒", -["nearrow"]="↗", -["ncong"]="≇", -["doteqdot"]="≑", -["supmult"]="⫂", -["longleftrightarrow"]="⟷", -["Rarr"]="↠", -["DoubleUpArrow"]="⇑", -["Dscr"]="𝒟", -["mp"]="∓", -["jmath"]="ȷ", -["erarr"]="⥱", -["searrow"]="↘", -["Sup"]="⋑", -["lvnE"]="≨︀", -["ffilig"]="ffi", -["NotSquareSupersetEqual"]="⋣", -["bullet"]="•", -["bigstar"]="★", -["cylcty"]="⌭", -["nvle"]="≤⃒", -["TildeFullEqual"]="≅", -["lopf"]="𝕝", -["sqsupseteq"]="⊒", -["lt"]="<", -["geqslant"]="⩾", +["midcir"]="⫰", +["nge"]="≱", ["quot"]="\"", -["le"]="≤", -["square"]="□", -["sigma"]="σ", -["prE"]="⪳", -["rtrie"]="⊵", -["triangleq"]="≜", +["REG"]="®", +["div"]="÷", +["complexes"]="ℂ", +["lhblk"]="▄", +["gvnE"]="≩︀", +["leftharpoonup"]="↼", +["it"]="", +["clubsuit"]="♣", +["iff"]="⇔", +["Hacek"]="ˇ", +["CircleTimes"]="⊗", +["Cacute"]="Ć", +["rarrtl"]="↣", +["RightDownVector"]="⇂", +["rarrfs"]="⤞", +["UpArrowBar"]="⤒", +["male"]="♂", +["RightTriangle"]="⊳", +["GreaterTilde"]="≳", +["Barwed"]="⌆", +["ltlarr"]="⥶", +["Zdot"]="Ż", +["parsim"]="⫳", +["Ugrave"]="Ù", +["Dot"]="¨", +["circleddash"]="⊝", +["rsaquo"]="›", +["hyphen"]="‐", +["angrt"]="∟", +["RightTriangleEqual"]="⊵", +["Darr"]="↡", +["ulcrop"]="⌏", +["olcir"]="⦾", +["gimel"]="ℷ", +["gel"]="⋛", +["SquareSubset"]="⊏", +["Uacute"]="Ú", +["gt"]=">", +["lnap"]="⪉", +["Union"]="⋃", +["nu"]="ν", +["and"]="∧", +["ltcir"]="⩹", ["Fscr"]="ℱ", +["ee"]="ⅇ", +["laquo"]="«", +["Hfr"]="ℌ", +["prec"]="≺", +["imagline"]="ℐ", +["precnsim"]="⋨", +["zhcy"]="ж", +["mu"]="μ", +["rlarr"]="⇄", +["pound"]="£", +["origof"]="⊶", +["rfloor"]="⌋", +["lnsim"]="⋦", +["nVdash"]="⊮", +["otimes"]="⊗", +["iuml"]="ï", +["Cscr"]="𝒞", +["lsime"]="⪍", +["ngeqq"]="≧̸", +["Del"]="∇", +["Coproduct"]="∐", +["NotSubsetEqual"]="⊈", +["HilbertSpace"]="ℋ", +["frac58"]="⅝", +["cap"]="∩", +["roang"]="⟭", +["intlarhk"]="⨗", +["DownTee"]="⊤", +["thickapprox"]="≈", +["prnap"]="⪹", +["laquo"]="«", +["lesdot"]="⩿", +["inodot"]="ı", +["Hscr"]="ℋ", +["YIcy"]="Ї", +["ctdot"]="⋯", +["thetav"]="ϑ", +["DoubleUpDownArrow"]="⇕", +["succ"]="≻", +["nbumpe"]="≏̸", +["ReverseElement"]="∋", +["Oacute"]="Ó", +["equest"]="≟", +["Lt"]="≪", +["Nacute"]="Ń", ["DiacriticalTilde"]="˜", -["SucceedsSlantEqual"]="≽", -["mopf"]="𝕞", -["Ecaron"]="Ě", -["UpArrowDownArrow"]="⇅", -["ges"]="⩾", -["nsqsupe"]="⋣", -["ubrcy"]="ў", -["Gscr"]="𝒢", -["HARDcy"]="Ъ", -["jopf"]="𝕛", -["gtquest"]="⩼", -["iinfin"]="⧜", -["twixt"]="≬", +["Escr"]="ℰ", +["boxvH"]="╪", +["Ifr"]="ℑ", +["mp"]="∓", +["ell"]="ℓ", +["Iacute"]="Í", +["NotNestedGreaterGreater"]="⪢̸", +["raquo"]="»", +["Zcy"]="З", +["micro"]="µ", +["dzigrarr"]="⟿", +["rtrif"]="▸", +["Uuml"]="Ü", +["nvHarr"]="⤄", +["leqslant"]="⩽", +["tritime"]="⨻", +["nlarr"]="↚", +["NotRightTriangle"]="⋫", +["apid"]="≋", +["succeq"]="⪰", +["NotEqualTilde"]="≂̸", +["varphi"]="ϕ", +["Vert"]="‖", +["int"]="∫", +["lurdshar"]="⥊", +["lneqq"]="≨", +["Jsercy"]="Ј", +["Superset"]="⊃", +["subsetneqq"]="⫋", +["gap"]="⪆", +["ssmile"]="⌣", +["Ugrave"]="Ù", +["IOcy"]="Ё", +["ltimes"]="⋉", +["UnderBracket"]="⎵", +["subseteqq"]="⫅", +["Dscr"]="𝒟", +["dtrif"]="▾", +["raquo"]="»", +["hbar"]="ℏ", +["ge"]="≥", +["cudarrr"]="⤵", +["icy"]="и", +["plusdo"]="∔", +["bull"]="•", +["weierp"]="℘", +["mapsto"]="↦", +["Uring"]="Ů", +["supplus"]="⫀", +["Succeeds"]="≻", +["Ascr"]="𝒜", +["cdot"]="ċ", +["nwarr"]="↖", +["target"]="⌖", +["dharr"]="⇂", +["curlyvee"]="⋎", +["supE"]="⫆", +["emsp13"]=" ", ["ltrie"]="⊴", +["olarr"]="↺", +["RightTeeVector"]="⥛", +["Vvdash"]="⊪", +["larrsim"]="⥳", +["varsubsetneq"]="⊊︀", +["Not"]="⫬", +["CenterDot"]="·", +["PlusMinus"]="±", +["aring"]="å", +["Nscr"]="𝒩", +["delta"]="δ", +["npre"]="⪯̸", +["Vcy"]="В", +["NotVerticalBar"]="∤", +["scpolint"]="⨓", +["rcedil"]="ŗ", +["Lmidot"]="Ŀ", +["DScy"]="Ѕ", +["nsubseteq"]="⊈", +["ngE"]="≧̸", +["NotSquareSubsetEqual"]="⋢", +["edot"]="ė", +["Ucy"]="У", +["lessapprox"]="⪅", +["niv"]="∋", +["Kscr"]="𝒦", +["LeftVectorBar"]="⥒", +["nhpar"]="⫲", +["scedil"]="ş", +["Uacute"]="Ú", +["ntrianglelefteq"]="⋬", +["Otilde"]="Õ", +["iexcl"]="¡", +["lbrke"]="⦋", +["simdot"]="⩪", +["sigmaf"]="ς", +["Bfr"]="𝔅", +["pitchfork"]="⋔", +["ngeqslant"]="⩾̸", +["Pscr"]="𝒫", +["gsime"]="⪎", +["Ouml"]="Ö", +["rarrsim"]="⥴", +["subE"]="⫅", +["otimesas"]="⨶", +["lfloor"]="⌊", +["prop"]="∝", +["imacr"]="ī", +["blacktriangleleft"]="◂", +["Ntilde"]="Ñ", +["llarr"]="⇇", +["nsucc"]="⊁", +["PartialD"]="∂", +["Iacute"]="Í", +["gdot"]="ġ", +["Afr"]="𝔄", +["Igrave"]="Ì", ["csub"]="⫏", -["Hscr"]="ℋ", -["leftharpoondown"]="↽", +["Mscr"]="ℳ", +["Scy"]="С", +["bsime"]="⋍", +["nequiv"]="≢", +["scnap"]="⪺", +["Tab"]=" ", +["Equal"]="⩵", +["blacklozenge"]="⧫", +["nrarrc"]="⤳̸", +["divide"]="÷", ["RightUpDownVector"]="⥏", -["LongLeftRightArrow"]="⟷", -["Im"]="ℑ", -["dcaron"]="ď", +["iinfin"]="⧜", +["precnapprox"]="⪹", +["dArr"]="⇓", +["orslope"]="⩗", +["Rcy"]="Р", +["sqsub"]="⊏", +["OverBracket"]="⎴", +["sum"]="∑", +["ncedil"]="ņ", +["ltrPar"]="⦖", +["ii"]="ⅈ", +["Gcirc"]="Ĝ", +["SucceedsTilde"]="≿", +["RightFloor"]="⌋", +["osol"]="⊘", ["barwedge"]="⌅", -["nexist"]="∄", -["blacktriangledown"]="▾", -["fork"]="⋔", -["rhard"]="⇁", -["cent"]="¢", -["RightTriangle"]="⊳", -["Eta"]="Η", -["dotminus"]="∸", -["nles"]="⩽̸", -["larrtl"]="↢", -["LeftTriangleEqual"]="⊴", -["hopf"]="𝕙", -["gammad"]="ϝ", -["gsime"]="⪎", -["llhard"]="⥫", -["precapprox"]="⪷", -["profalar"]="⌮", -["lnE"]="≨", -["ddagger"]="‡", -["leq"]="≤", -["smile"]="⌣", -["robrk"]="⟧", -["Jscr"]="𝒥", -["triangleleft"]="◃", -["acd"]="∿", -["prod"]="∏", -["atilde"]="ã", -["seArr"]="⇘", -["therefore"]="∴", -["iopf"]="𝕚", -["solbar"]="⌿", -["kcedil"]="ķ", -["NotHumpEqual"]="≏̸", -["nspar"]="∦", -["frac14"]="¼", -["nsime"]="≄", -["DoubleLongLeftArrow"]="⟸", -["sccue"]="≽", -["models"]="⊧", -["SupersetEqual"]="⊇", -["iquest"]="¿", -["oline"]="‾", -["lparlt"]="⦓", -["triplus"]="⨹", -["nequiv"]="≢", -["nlE"]="≦̸", -["nldr"]="‥", -["equals"]="=", -["Agrave"]="À", -["ccaron"]="č", -["Yacute"]="Ý", -["Oslash"]="Ø", -["udarr"]="⇅", -["ang"]="∠", -["dopf"]="𝕕", -["comp"]="∁", -["caps"]="∩︀", -["bowtie"]="⋈", -["glj"]="⪤", -["ltquest"]="⩻", -["cirmid"]="⫯", -["ordm"]="º", -["esim"]="≂", -["reals"]="ℝ", -["LeftTriangle"]="⊲", -["flat"]="♭", -["eopf"]="𝕖", -["lap"]="⪅", -["not"]="¬", -["ENG"]="Ŋ", -["DiacriticalAcute"]="´", -["sharp"]="♯", -["longleftarrow"]="⟵", -["dblac"]="˝", -["UpArrow"]="↑", -["npre"]="⪯̸", -["bopf"]="𝕓", -["DScy"]="Ѕ", +["twoheadrightarrow"]="↠", +["Gscr"]="𝒢", +["acy"]="а", +["dtdot"]="⋱", +["rbrke"]="⦌", +["imath"]="ı", +["Hcirc"]="Ĥ", +["num"]="#", +["nprec"]="⊀", +["smallsetminus"]="∖", +["lesges"]="⪓", +["LeftUpVector"]="↿", +["gtdot"]="⋗", +["theta"]="θ", +["kappav"]="ϰ", +["jmath"]="ȷ", +["Lscr"]="ℒ", +["Aogon"]="Ą", +["ltrif"]="◂", +["DownArrowUpArrow"]="⇵", +["prap"]="⪷", +["Racute"]="Ŕ", +["curlyeqprec"]="⋞", ["bepsi"]="϶", -["angrtvb"]="⊾", -["ccaps"]="⩍", -["UnderParenthesis"]="⏝", -["subne"]="⊊", -["succnsim"]="⋩", -["Bumpeq"]="≎", -["xvee"]="⋁", -["colon"]=":", -["deg"]="°", -["fnof"]="ƒ", -["Uarr"]="↟", -["Zcaron"]="Ž", -["copf"]="𝕔", -["Theta"]="Θ", -["omicron"]="ο", -["ntriangleright"]="⋫", -["DoubleLongRightArrow"]="⟹", -["UnderBrace"]="⏟", -["GT"]=">", -["lEg"]="⪋", -["supplus"]="⫀", -["acute"]="´", +["blacktriangle"]="▴", +["nvap"]="≍⃒", +["Supset"]="⋑", +["angmsd"]="∡", +["boxul"]="┘", +["LessSlantEqual"]="⩽", ["GT"]=">", -["upsilon"]="υ", -["sc"]="≻", -["xrarr"]="⟶", -["eng"]="ŋ", -["it"]="", -["Ntilde"]="Ñ", -["Mu"]="Μ", -["CircleTimes"]="⊗", -["NotTildeTilde"]="≉", -["NotLessLess"]="≪̸", -["vellip"]="⋮", -["cularrp"]="⤽", -["UnderBracket"]="⎵", -["aopf"]="𝕒", -["itilde"]="ĩ", -["latail"]="⤙", -["Nu"]="Ν", -["Ascr"]="𝒜", -["complement"]="∁", -["gjcy"]="ѓ", -["radic"]="√", -["maltese"]="✠", -["glE"]="⪒", -["plusmn"]="±", -["curvearrowright"]="↷", -["gt"]=">", -["Colone"]="⩴", -["Bscr"]="ℬ", -["ic"]="", -["clubsuit"]="♣", -["djcy"]="ђ", -["Oopf"]="𝕆", -["ltrPar"]="⦖", -["ngtr"]="≯", -["eqcirc"]="≖", -["Hacek"]="ˇ", -["vdash"]="⊢", -["NotCupCap"]="≭", -["Gdot"]="Ġ", -["jsercy"]="ј", -["marker"]="▮", +["nexist"]="∄", +["ccupssm"]="⩐", +["Eacute"]="É", +["ddarr"]="⇊", +["ntlg"]="≸", +["Iscr"]="ℐ", +["Jcirc"]="Ĵ", +["Sacute"]="Ś", +["capand"]="⩄", +["gesdoto"]="⪂", +["olcross"]="⦻", +["amalg"]="⨿", +["rarrlp"]="↬", +["NotLeftTriangle"]="⋪", +["reals"]="ℝ", +["bigcup"]="⋃", +["NJcy"]="Њ", +["Odblac"]="Ő", +["hArr"]="⇔", +["RightArrowLeftArrow"]="⇄", +["SOFTcy"]="Ь", +["Lambda"]="Λ", +["RightCeiling"]="⌉", +["Vdash"]="⊩", +["elinters"]="⏧", +["lsimg"]="⪏", ["Nopf"]="ℕ", -["Wfr"]="𝔚", -["boxHD"]="╦", -["mho"]="℧", -["ulcrop"]="⌏", -["DifferentialD"]="ⅆ", -["aacute"]="á", -["lpar"]="(", +["Esim"]="⩳", +["vzigzag"]="⦚", +["acd"]="∿", +["ccedil"]="ç", +["lbarr"]="⤌", +["nmid"]="∤", +["Backslash"]="∖", +["Laplacetrf"]="ℒ", +["npr"]="⊀", +["nap"]="≉", +["nsupseteqq"]="⫆̸", +["igrave"]="ì", +["LJcy"]="Љ", +["lessgtr"]="≶", ["angle"]="∠", -["rharu"]="⇀", -["Breve"]="˘", -["Dot"]="¨", -["rect"]="▭", -["NotNestedLessLess"]="⪡̸", -["boxplus"]="⊞", -["NotSucceeds"]="⊁", -["gnapprox"]="⪊", -["DownLeftTeeVector"]="⥞", -["Xfr"]="𝔛", -["Lang"]="⟪", -["Element"]="∈", -["sung"]="♪", -["RightVectorBar"]="⥓", -["simne"]="≆", -["LeftVector"]="↼", -["Idot"]="İ", +["lAarr"]="⇚", +["notin"]="∉", +["orv"]="⩛", +["gtreqqless"]="⪌", +["CircleMinus"]="⊖", +["Diamond"]="⋄", +["map"]="↦", +["nvsim"]="∼⃒", +["pi"]="π", +["pluscir"]="⨢", +["boxVH"]="╬", ["QUOT"]="\"", -["nsmid"]="∤", -["fjlig"]="fj", -["Ufr"]="𝔘", -["cuvee"]="⋎", +["duarr"]="⇵", +["lgE"]="⪑", +["lmoust"]="⎰", +["lsquo"]="‘", +["rightrightarrows"]="⇉", +["times"]="×", +["iuml"]="ï", +["andslope"]="⩘", +["order"]="ℴ", +["Sub"]="⋐", +["capcap"]="⩋", +["nabla"]="∇", +["ntriangleleft"]="⋪", +["blk14"]="░", +["Kopf"]="𝕂", +["lArr"]="⇐", +["Qopf"]="ℚ", +["circledS"]="Ⓢ", +["omid"]="⦶", +["ord"]="⩝", +["lvertneqq"]="≨︀", +["updownarrow"]="↕", +["yacute"]="ý", +["ni"]="∋", +["hamilt"]="ℋ", +["Jopf"]="𝕁", +["CupCap"]="≍", +["lbrack"]="[", +["preccurlyeq"]="≼", +["Ropf"]="ℝ", +["lessdot"]="⋖", +["blank"]="␣", +["SucceedsEqual"]="⪰", +["triangleleft"]="◃", +["ring"]="˚", +["omega"]="ω", +["bowtie"]="⋈", +["Sqrt"]="√", +["uuml"]="ü", +["Udblac"]="Ű", +["diams"]="♦", +["Oslash"]="Ø", ["real"]="ℜ", -["luruhar"]="⥦", -["NotRightTriangleBar"]="⧐̸", -["target"]="⌖", -["lharu"]="↼", -["CloseCurlyQuote"]="’", -["rAtail"]="⤜", -["nparallel"]="∦", -["brvbar"]="¦", -["Vfr"]="𝔙", -["wedbar"]="⩟", -["REG"]="®", -["boxur"]="└", -["Rcaron"]="Ř", -["Rang"]="⟫", -["sqsubseteq"]="⊑", -["div"]="÷", -["boxul"]="┘", -["Cdot"]="Ċ", -["vsupnE"]="⫌︀", -["zeetrf"]="ℨ", -["Sfr"]="𝔖", -["roang"]="⟭", -["LessTilde"]="≲", -["equiv"]="≡", -["frac12"]="½", -["eacute"]="é", -["rrarr"]="⇉", -["nearhk"]="⤤", -["Zopf"]="ℤ", -["SucceedsTilde"]="≿", +["egrave"]="è", +["prurel"]="⊰", +["LeftUpDownVector"]="⥑", +["TildeEqual"]="≃", +["cuwed"]="⋏", +["times"]="×", ["ap"]="≈", -["lfisht"]="⥼", -["timesd"]="⨰", -["UpDownArrow"]="↕", -["xuplus"]="⨄", -["cularr"]="↶", -["iprod"]="⨼", -["AElig"]="Æ", -["rationals"]="ℚ", -["geqq"]="≧", -["Tfr"]="𝔗", -["Yopf"]="𝕐", -["rtimes"]="⋊", -["Tcaron"]="Ť", -["srarr"]="→", -["diam"]="⋄", -["mid"]="∣", -["imath"]="ı", -["PlusMinus"]="±", -["isin"]="∈", -["vprop"]="∝", -["RightArrowLeftArrow"]="⇄", -["NotSubset"]="⊂⃒", -["bigtriangledown"]="▽", -["ldsh"]="↲", -["Qfr"]="𝔔", -["nvap"]="≍⃒", -["vert"]="|", -["Xopf"]="𝕏", -["lBarr"]="⤎", -["egsdot"]="⪘", -["Tcedil"]="Ţ", -["xi"]="ξ", -["LeftUpVectorBar"]="⥘", -["circ"]="ˆ", -["ocir"]="⊚", -["gtlPar"]="⦕", -["Rfr"]="ℜ", -["RightTriangleBar"]="⧐", -["nwarhk"]="⤣", -["zhcy"]="ж", -["Iukcy"]="І", -["plusdo"]="∔", +["lowast"]="∗", +["Lopf"]="𝕃", +["bdquo"]="„", +["blacktriangledown"]="▾", +["vBarv"]="⫩", +["Longleftarrow"]="⟸", +["lesg"]="⋚︀", +["Popf"]="ℙ", +["iogon"]="į", +["vellip"]="⋮", +["Ntilde"]="Ñ", +["cwconint"]="∲", ["boxv"]="│", -["minusb"]="⊟", -["Ncaron"]="Ň", -["angmsdaa"]="⦨", -["varsubsetneqq"]="⫋︀", -["cirfnint"]="⨐", -["LongRightArrow"]="⟶", -["ddarr"]="⇊", -["piv"]="ϖ", -["fopf"]="𝕗", -["Ofr"]="𝔒", -["fflig"]="ff", -["Jukcy"]="Є", -["xhArr"]="⟺", -["ncy"]="н", -["Scaron"]="Š", -["TildeEqual"]="≃", -["subrarr"]="⥹", -["iacute"]="í", -["ogt"]="⧁", -["nvge"]="≥⃒", -["vee"]="∨", -["UnderBar"]="_", -["andslope"]="⩘", -["gopf"]="𝕘", -["Pfr"]="𝔓", -["die"]="¨", +["boxbox"]="⧉", +["langd"]="⦑", +["diam"]="⋄", +["boxHu"]="╧", +["acute"]="´", ["Uopf"]="𝕌", +["Fcy"]="Ф", +["vee"]="∨", +["eqslantless"]="⪕", +["NotLeftTriangleEqual"]="⋬", +["OverBrace"]="⏞", +["beta"]="β", +["Otilde"]="Õ", +["fjlig"]="fj", +["uacute"]="ú", +["CirclePlus"]="⊕", +["Mellintrf"]="ℳ", +["Vopf"]="𝕍", +["Gcy"]="Г", +["searhk"]="⤥", +["boxur"]="└", +["LeftTeeVector"]="⥚", +["nsubE"]="⫅̸", +["gvertneqq"]="≩︀", +["eqcirc"]="≖", +["nrightarrow"]="↛", +["DJcy"]="Ђ", +["tdot"]="⃛", +["Dcy"]="Д", +["Sopf"]="𝕊", +["range"]="⦥", +["agrave"]="à", +["gtrsim"]="≳", ["RightArrowBar"]="⇥", -["orarr"]="↻", -["Precedes"]="≺", -["oror"]="⩖", -["straightepsilon"]="ϵ", -["cong"]="≅", -["TScy"]="Ц", -["nleftarrow"]="↚", -["DZcy"]="Џ", -["horbar"]="―", +["larrlp"]="↫", +["le"]="≤", +["xi"]="ξ", +["gla"]="⪥", +["GT"]=">", +["leftarrowtail"]="↢", +["commat"]="@", +["rightarrowtail"]="↣", +["approx"]="≈", +["sdot"]="⋅", ["Topf"]="𝕋", -["Cayleys"]="ℭ", -["leqq"]="≦", -["curlyeqprec"]="⋞", -["nLeftarrow"]="⇍", -["pcy"]="п", -["realine"]="ℛ", -["nlsim"]="≴", +["Ecy"]="Э", +["eplus"]="⩱", +["cir"]="○", ["Rarrtl"]="⤖", -["CupCap"]="≍", -["vDash"]="⊨", -["shortparallel"]="∥", -["ExponentialE"]="ⅇ", -["Sopf"]="𝕊", -["iiota"]="℩", -["Nfr"]="𝔑", -["pointint"]="⨕", -["NotGreaterLess"]="≹", -["olcross"]="⦻", -["kcy"]="к", -["complexes"]="ℂ", -["varepsilon"]="ϵ", -["Fouriertrf"]="ℱ", -["rho"]="ρ", -["Lsh"]="↰", -["exponentiale"]="ⅇ", -["hookrightarrow"]="↪", -["Ropf"]="ℝ", -["lAtail"]="⤛", -["Kfr"]="𝔎", -["bsim"]="∽", -["jcy"]="й", -["frac34"]="¾", -["DotDot"]="⃜", +["Emacr"]="Ē", +["Dcaron"]="Ď", +["Sc"]="⪼", +["cent"]="¢", +["rtri"]="▹", +["cedil"]="¸", +["spades"]="♠", +["upsilon"]="υ", +["boxminus"]="⊟", +["angmsdaa"]="⦨", +["zdot"]="ż", +["Bcy"]="Б", +["Zscr"]="𝒵", +["looparrowleft"]="↫", +["Dfr"]="𝔇", +["Bumpeq"]="≎", +["nLeftarrow"]="⇍", +["equiv"]="≡", +["Ccaron"]="Č", +["UpperLeftArrow"]="↖", +["SubsetEqual"]="⊆", +["nrArr"]="⇏", +["loarr"]="⇽", +["minusdu"]="⨪", +["sup3"]="³", +["Zopf"]="ℤ", +["elsdot"]="⪗", +["NotLessLess"]="≪̸", +["eta"]="η", +["ubrcy"]="ў", +["Yscr"]="𝒴", +["subset"]="⊂", +["Cfr"]="ℭ", +["harrcir"]="⥈", +["supsetneq"]="⊋", +["LeftUpVectorBar"]="⥘", +["LT"]="<", +["DoubleLeftArrow"]="⇐", +["circledcirc"]="⊚", +["bkarow"]="⤍", +["rangd"]="⦒", +["downharpoonright"]="⇂", +["Wopf"]="𝕎", +["ast"]="*", +["planck"]="ℏ", +["gtrdot"]="⋗", +["Ffr"]="𝔉", +["rArr"]="⇒", +["VeryThinSpace"]=" ", +["Ecaron"]="Ě", +["RightVectorBar"]="⥓", +["divonx"]="⋇", +["Xopf"]="𝕏", +["nsubset"]="⊂⃒", +["Otimes"]="⨷", +["bot"]="⊥", +["nsime"]="≄", ["thorn"]="þ", -["ThickSpace"]=" ", -["SquareUnion"]="⊔", -["frac58"]="⅝", -["dfisht"]="⥿", -["MediumSpace"]=" ", -["Lfr"]="𝔏", -["Qopf"]="ℚ", +["subsetneq"]="⊊", +["nltri"]="⋪", +["Cayleys"]="ℭ", +["Efr"]="𝔈", +["Intersection"]="⋂", +["UnderBar"]="_", +["fpartint"]="⨍", +["Iuml"]="Ï", +["rsquo"]="’", +["ecir"]="≖", +["nis"]="⋼", +["fscr"]="𝒻", +["hellip"]="…", +["ogt"]="⧁", ["ntgl"]="≹", -["LessFullEqual"]="≦", -["malt"]="✠", -["mcy"]="м", -["NotGreaterEqual"]="≱", -["bottom"]="⊥", -["NotSucceedsEqual"]="⪰̸", -["lcedil"]="ļ", -["infintie"]="⧝", -["Lcaron"]="Ľ", -["NotSquareSubset"]="⊏̸", -["brvbar"]="¦", -["fallingdotseq"]="≒", -["AElig"]="Æ", -["swarhk"]="⤦", -["Ifr"]="ℑ", -["pi"]="π", -["lcy"]="л", -["copy"]="©", -["dlcorn"]="⌞", -["oacute"]="ó", -["gtreqless"]="⋛", -["neArr"]="⇗", +["frac14"]="¼", +["squf"]="▪", +["circ"]="ˆ", +["prE"]="⪳", +["ordf"]="ª", ["ZHcy"]="Ж", -["Vdashl"]="⫦", -["darr"]="↓", -["circeq"]="≗", +["NegativeVeryThinSpace"]="", +["quot"]="\"", +["excl"]="!", +["sqcaps"]="⊓︀", +["gfr"]="𝔤", +["varsigma"]="ς", +["nwarrow"]="↖", +["reg"]="®", +["longmapsto"]="⟼", +["And"]="⩓", +["cuvee"]="⋎", +["zwj"]="", +["plankv"]="ℏ", +["varepsilon"]="ϵ", +["Aopf"]="𝔸", +["UpTee"]="⊥", +["lescc"]="⪨", +["ltri"]="◃", +["iiint"]="∭", +["ccups"]="⩌", +["alefsym"]="ℵ", +["succneqq"]="⪶", +["hscr"]="𝒽", +["Or"]="⩔", ["yuml"]="ÿ", -["Jfr"]="𝔍", -["Product"]="∏", -["boxDL"]="╗", -["el"]="⪙", -["supe"]="⊇", -["frac13"]="⅓", -["Barwed"]="⌆", -["larrpl"]="⤹", -["SHCHcy"]="Щ", -["LeftUpDownVector"]="⥑", -["Diamond"]="⋄", -["DownLeftVectorBar"]="⥖", -["Cross"]="⨯", -["ncup"]="⩂", -["supnE"]="⫌", -["lne"]="⪇", -["micro"]="µ", -["lowbar"]="_", -["perp"]="⊥", -["sqcap"]="⊓", -["Gfr"]="𝔊", -["raemptyv"]="⦳", -["gesl"]="⋛︀", -["drcrop"]="⌌", -["boxUL"]="╝", -["becaus"]="∵", -["gne"]="⪈", -["scap"]="⪸", -["swarrow"]="↙", -["nesim"]="≂̸", -["DoubleDot"]="¨", -["Mopf"]="𝕄", -["apid"]="≋", -["InvisibleComma"]="", -["Backslash"]="∖", -["mnplus"]="∓", -["niv"]="∋", -["Hfr"]="ℌ", -["rarrlp"]="↬", -["Because"]="∵", -["parsl"]="⫽", -["sup1"]="¹", -["Lt"]="≪", -["par"]="∥", -["Iota"]="Ι", -["beth"]="ℶ", -["Lopf"]="𝕃", +["rcub"]="}", +["lt"]="<", +["vArr"]="⇕", +["EqualTilde"]="≂", +["vDash"]="⊨", +["RightArrow"]="→", +["dlcorn"]="⌞", +["Barv"]="⫧", +["nsc"]="⊁", +["Proportion"]="∷", +["iota"]="ι", +["gscr"]="ℊ", +["abreve"]="ă", +["ThickSpace"]=" ", +["szlig"]="ß", +["lhard"]="↽", +["emptyv"]="∅", +["Tau"]="Τ", +["sharp"]="♯", +["bNot"]="⫭", +["LongLeftRightArrow"]="⟷", +["frac23"]="⅔", +["NotEqual"]="≠", +["scE"]="⪴", +["jfr"]="𝔧", +["bscr"]="𝒷", +["uhblk"]="▀", +["frac35"]="⅗", +["TSHcy"]="Ћ", +["ntrianglerighteq"]="⋭", +["Tcedil"]="Ţ", +["NotGreaterFullEqual"]="≧̸", +["asympeq"]="≍", +["oror"]="⩖", +["ecirc"]="ê", +["ohbar"]="⦵", +["Rho"]="Ρ", +["iacute"]="í", +["ascr"]="𝒶", +["RightUpVector"]="↾", +["Ocirc"]="Ô", +["cedil"]="¸", +["sect"]="§", +["angmsdab"]="⦩", +["FilledVerySmallSquare"]="▪", +["curlywedge"]="⋏", +["lesdotor"]="⪃", +["esdot"]="≐", +["eqvparsl"]="⧥", ["Cap"]="⋒", -["Aring"]="Å", -["rarr"]="→", -["Efr"]="𝔈", -["bigcup"]="⋃", -["DownLeftRightVector"]="⥐", -["lesges"]="⪓", -["NegativeThinSpace"]="", -["DownRightTeeVector"]="⥟", -["npolint"]="⨔", -["gEl"]="⪌", -["lltri"]="◺", -["ogon"]="˛", -["LessEqualGreater"]="⋚", -["sqsupset"]="⊐", -["NotSubsetEqual"]="⊈", -["Ubrcy"]="Ў", -["ShortDownArrow"]="↓", -["ugrave"]="ù", -["Dcaron"]="Ď", -["subE"]="⫅", -["divide"]="÷", -["bigotimes"]="⨂", -["dscy"]="ѕ", -["wreath"]="≀", -["Jopf"]="𝕁", -["pre"]="⪯", -["tdot"]="⃛", -["Cfr"]="ℭ", -["Uogon"]="Ų", -["softcy"]="ь", -["isinE"]="⋹", -["Dstrok"]="Đ", -["Jsercy"]="Ј", -["capdot"]="⩀", -["realpart"]="ℜ", -["hearts"]="♥", -["Atilde"]="Ã", -["LeftArrowRightArrow"]="⇆", -["upharpoonleft"]="↿", -["Iopf"]="𝕀", -["Intersection"]="⋂", -["midcir"]="⫰", -["ocirc"]="ô", -["sdot"]="⋅", -["Dfr"]="𝔇", -["gla"]="⪥", -["DiacriticalGrave"]="`", +["dscr"]="𝒹", +["rbrace"]="}", +["nsccue"]="⋡", +["neArr"]="⇗", +["rightthreetimes"]="⋌", +["plusmn"]="±", ["Ubreve"]="Ŭ", -["int"]="∫", -["PrecedesSlantEqual"]="≼", -["Square"]="□", -["Acy"]="А", +["ifr"]="𝔦", +["Poincareplane"]="ℌ", +["leftrightarrows"]="⇆", +["scnsim"]="⋩", +["cscr"]="𝒸", +["NotHumpEqual"]="≏̸", +["capbrcup"]="⩉", +["curarr"]="↷", +["notinvb"]="⋷", +["LeftArrow"]="←", +["Gopf"]="𝔾", +["ldrushar"]="⥋", +["nscr"]="𝓃", +["Gg"]="⋙", +["ouml"]="ö", +["nsce"]="⪰̸", +["cupbrcap"]="⩈", +["nLt"]="≪⃒", +["bullet"]="•", +["nsqsupe"]="⋣", +["rect"]="▭", +["lbrace"]="{", +["Fopf"]="𝔽", +["COPY"]="©", +["epar"]="⋕", +["mscr"]="𝓂", +["odsold"]="⦼", +["cupor"]="⩅", +["not"]="¬", +["subsim"]="⫇", +["hoarr"]="⇿", +["Scedil"]="Ş", +["eacute"]="é", +["emsp"]=" ", +["uml"]="¨", +["igrave"]="ì", +["pscr"]="𝓅", +["LeftTee"]="⊣", +["Cedilla"]="¸", +["Iopf"]="𝕀", +["HumpEqual"]="≏", +["les"]="⩽", +["Rcedil"]="Ŗ", +["lsquor"]="‚", +["raemptyv"]="⦳", +["NotSucceedsSlantEqual"]="⋡", +["SuchThat"]="∋", +["vert"]="|", +["backcong"]="≌", ["Hopf"]="ℍ", -["precsim"]="≾", -["nleq"]="≰", -["urcorn"]="⌝", -["hyphen"]="‐", -["FilledSmallSquare"]="◼", -["zdot"]="ż", -["ndash"]="–", -["hbar"]="ℏ", -["Wopf"]="𝕎", -["Esim"]="⩳", -["subsetneq"]="⊊", -["boxhD"]="╥", -["searhk"]="⤥", -["late"]="⪭", +["CircleDot"]="⊙", +["Kappa"]="Κ", +["conint"]="∮", +["DZcy"]="Џ", +["prsim"]="≾", +["Copf"]="ℂ", +["curvearrowright"]="↷", +["twixt"]="≬", +["curren"]="¤", +["rtriltri"]="⧎", +["jscr"]="𝒿", +["cupcup"]="⩊", +["Lcedil"]="Ļ", +["supseteqq"]="⫆", +["bfr"]="𝔟", +["lesseqqgtr"]="⪋", +["leftleftarrows"]="⇇", +["Bopf"]="𝔹", +["acute"]="´", ["blacktriangleright"]="▸", -["auml"]="ä", -["NotLessTilde"]="≴", -["Bfr"]="𝔅", -["grave"]="`", -["ltlarr"]="⥶", -["Aogon"]="Ą", -["sdote"]="⩦", -["gtrsim"]="≳", -["nvinfin"]="⧞", -["lgE"]="⪑", -["mdash"]="—", -["Vopf"]="𝕍", -["leftrightarrow"]="↔", -["RBarr"]="⤐", -["amalg"]="⨿", -["preccurlyeq"]="≼", -["EqualTilde"]="≂", -["scpolint"]="⨓", -["NotHumpDownHump"]="≎̸", -["uparrow"]="↑", -["downarrow"]="↓", -["boxdL"]="╕", -["Hstrok"]="Ħ", -["Ccaron"]="Č", -["succsim"]="≿", -["RightFloor"]="⌋", -["DownBreve"]="̑", -["succapprox"]="⪸", -["dsol"]="⧶", -["napid"]="≋̸", -["nprcue"]="⋠", +["frac34"]="¾", +["Gbreve"]="Ğ", +["Updownarrow"]="⇕", +["iscr"]="𝒾", +["sigmav"]="ς", +["trade"]="™", +["minusb"]="⊟", +["GJcy"]="Ѓ", +["TildeFullEqual"]="≅", +["DoubleRightTee"]="⊨", +["nang"]="∠⃒", +["ddotseq"]="⩷", +["copy"]="©", ["Eopf"]="𝔼", -["ell"]="ℓ", -["cupdot"]="⊍", -["Not"]="⫬", -["zigrarr"]="⇝", -["downharpoonright"]="⇂", -["nisd"]="⋺", -["lotimes"]="⨴", -["plusmn"]="±", -["ngE"]="≧̸", -["odash"]="⊝", -["emptyset"]="∅", +["Psi"]="Ψ", +["UpperRightArrow"]="↗", +["geqq"]="≧", +["subedot"]="⫃", +["nvlArr"]="⤂", +["Ncedil"]="Ņ", +["vsupne"]="⊋︀", +["supset"]="⊃", ["squarf"]="▪", -["NotGreaterTilde"]="≵", -["NotTildeFullEqual"]="≇", -["DownArrowUpArrow"]="⇵", -["nwarrow"]="↖", -["looparrowright"]="↬", +["LeftDoubleBracket"]="⟦", +["utrif"]="▴", ["Dopf"]="𝔻", -["oplus"]="⊕", -["UpEquilibrium"]="⥮", -["leftthreetimes"]="⋋", -["DoubleLeftTee"]="⫤", -["nabla"]="∇", -["RuleDelayed"]="⧴", -["euml"]="ë", -["varsupsetneqq"]="⫌︀", -["upsi"]="υ", -["supsup"]="⫖", -["bsol"]="\\", -["Copf"]="ℂ", -["DDotrahd"]="⤑", -["Upsilon"]="Υ", -["UpperRightArrow"]="↗", -["nedot"]="≐̸", -["NotEqual"]="≠", -["bumpeq"]="≏", -["emsp14"]=" ", -["frac38"]="⅜", -["DownTeeArrow"]="↧", -["Bopf"]="𝔹", -["capand"]="⩄", -["nleqq"]="≦̸", -["ratio"]="∶", -["eplus"]="⩱", -["nge"]="≱", -["laquo"]="«", -["urtri"]="◹", -["LeftUpVector"]="↿", -["gl"]="≷", -["Gt"]="≫", -["andd"]="⩜", +["ape"]="≊", +["gEl"]="⪌", +["COPY"]="©", +["afr"]="𝔞", +["Acy"]="А", +["ldquor"]="„", +["because"]="∵", +["Pr"]="⪻", +["OverBar"]="‾", +["lang"]="⟨", +["mopf"]="𝕞", +["crarr"]="↵", +["Epsilon"]="Ε", +["Nfr"]="𝔑", +["kjcy"]="ќ", +["TildeTilde"]="≈", +["bsim"]="∽", +["lcy"]="л", +["larrb"]="⇤", +["ncong"]="≇", +["ENG"]="Ŋ", +["brvbar"]="¦", +["Rightarrow"]="⇒", +["nopf"]="𝕟", +["Wfr"]="𝔚", +["ljcy"]="љ", +["diamondsuit"]="♦", +["latail"]="⤙", +["lambda"]="λ", +["frac14"]="¼", +["mcy"]="м", +["nparsl"]="⫽⃥", +["lBarr"]="⤎", +["subdot"]="⪽", +["frac12"]="½", +["kopf"]="𝕜", +["LeftCeiling"]="⌈", +["erarr"]="⥱", +["pre"]="⪯", +["varr"]="↕", +["Pi"]="Π", +["sub"]="⊂", +["ncy"]="н", +["marker"]="▮", +["rpargt"]="⦔", +["supe"]="⊇", +["veebar"]="⊻", +["checkmark"]="✓", +["nbsp"]=" ", +["sqsupseteq"]="⊒", +["upharpoonright"]="↾", +["lopf"]="𝕝", +["Omicron"]="Ο", +["ccaps"]="⩍", +["lrhard"]="⥭", ["Lstrok"]="Ł", -["gamma"]="γ", -["Sc"]="⪼", -["Aopf"]="𝔸", -["pluscir"]="⨢", -["Itilde"]="Ĩ", -["DoubleRightTee"]="⊨", -["pluse"]="⩲", -["iiint"]="∭", -["dotplus"]="∔", -["vrtri"]="⊳", -["Proportion"]="∷", -["bne"]="=⃥", -["cuepr"]="⋞", +["Mfr"]="𝔐", +["njcy"]="њ", +["ocy"]="о", +["Zcaron"]="Ž", +["NotSucceeds"]="⊁", +["rarrbfs"]="⤠", +["gtrapprox"]="⪆", +["oS"]="Ⓢ", +["diamond"]="⋄", +["gesdot"]="⪀", +["urcorner"]="⌝", +["lotimes"]="⨴", +["pertenk"]="‱", +["rfisht"]="⥽", +["Zfr"]="ℨ", +["Map"]="⤅", +["cross"]="✗", +["DifferentialD"]="ⅆ", +["boxtimes"]="⊠", +["horbar"]="―", +["lg"]="≶", +["orarr"]="↻", +["Vbar"]="⫫", +["ropf"]="𝕣", +["NotDoubleVerticalBar"]="∦", +["Sfr"]="𝔖", +["Iuml"]="Ï", +["leftharpoondown"]="↽", +["atilde"]="ã", +["VerticalTilde"]="≀", +["DoubleContourIntegral"]="∯", +["longleftarrow"]="⟵", +["Im"]="ℑ", +["RightUpVectorBar"]="⥔", +["nGg"]="⋙̸", +["scap"]="⪸", +["nrarr"]="↛", +["ycy"]="ы", +["ucirc"]="û", +["qopf"]="𝕢", +["oopf"]="𝕠", +["yacute"]="ý", +["Xfr"]="𝔛", +["fork"]="⋔", +["in"]="∈", +["xcap"]="⋂", +["check"]="✓", +["jcy"]="й", +["Phi"]="Φ", +["doteq"]="≐", ["vBar"]="⫨", -["yucy"]="ю", -["PrecedesTilde"]="≾", -["varr"]="↕", -["leftarrowtail"]="↢", -["rangle"]="⟩", -["subsub"]="⫕", -["rightarrowtail"]="↣", -["oslash"]="ø", -["mapstodown"]="↧", -["sqsube"]="⊑", -["NewLine"]="\n", -["Vdash"]="⊩", -["num"]="#", -["umacr"]="ū", -["InvisibleTimes"]="", -["bemptyv"]="⦰", +["frown"]="⌢", ["Iogon"]="Į", -["ssmile"]="⌣", -["timesbar"]="⨱", -["Tstrok"]="Ŧ", -["Icy"]="И", -["zacute"]="ź", -["Cedilla"]="¸", -["daleth"]="ℸ", -["semi"]=";", -["ssetmn"]="∖", -["subdot"]="⪽", -["Bernoullis"]="ℬ", -["otimesas"]="⨶", -["circleddash"]="⊝", -["circlearrowright"]="↻", -["NotLeftTriangleEqual"]="⋬", -["NotTilde"]="≁", -["npart"]="∂̸", -["Gbreve"]="Ğ", -["gtrless"]="≷", -["pound"]="£", -["curlyeqsucc"]="⋟", -["blacklozenge"]="⧫", -["ldca"]="⤶", -["nsucc"]="⊁", -["top"]="⊤", -["ufr"]="𝔲", -["sscr"]="𝓈", -["eDDot"]="⩷", -["Updownarrow"]="⇕", -["loz"]="◊", -["Gcy"]="Г", -["boxdl"]="┐", +["gnsim"]="⋧", +["nsupset"]="⊃⃒", +["equals"]="=", +["angmsdac"]="⦪", +["odash"]="⊝", +["Hstrok"]="Ħ", +["homtht"]="∻", +["maltese"]="✠", +["esim"]="≂", +["realine"]="ℛ", +["straightepsilon"]="ϵ", +["utri"]="▵", +["primes"]="ℙ", +["dbkarow"]="⤏", +["NotLessTilde"]="≴", +["nvrArr"]="⤃", +["cularrp"]="⤽", +["supsup"]="⫖", +["rang"]="⟩", +["nshortmid"]="∤", +["NotCupCap"]="≭", +["gnE"]="≩", +["otilde"]="õ", +["LeftTriangle"]="⊲", +["oelig"]="œ", +["curarrm"]="⤼", +["orderof"]="ℴ", +["DoubleLongLeftArrow"]="⟸", +["OverParenthesis"]="⏜", +["amacr"]="ā", +["copysr"]="℗", +["acirc"]="â", +["LeftArrowRightArrow"]="⇆", +["filig"]="fi", +["uopf"]="𝕦", +["rlhar"]="⇌", +["dharl"]="⇃", +["rAarr"]="⇛", +["rarrw"]="↝", +["isin"]="∈", +["simg"]="⪞", +["NoBreak"]="", +["rAtail"]="⤜", +["ndash"]="–", +["IJlig"]="IJ", +["dfr"]="𝔡", +["ohm"]="Ω", +["breve"]="˘", +["ccedil"]="ç", +["topf"]="𝕥", +["nvDash"]="⊭", +["RBarr"]="⤐", +["clubs"]="♣", ["napos"]="ʼn", -["Sqrt"]="√", -["xsqcup"]="⨆", -["Implies"]="⇒", -["uring"]="ů", -["thetav"]="ϑ", -["iiiint"]="⨌", -["nLeftrightarrow"]="⇎", -["lobrk"]="⟦", -["xfr"]="𝔵", -["Abreve"]="Ă", -["micro"]="µ", -["quatint"]="⨖", -["congdot"]="⩭", -["nrtrie"]="⋭", -["Fcy"]="Ф", -["hksearow"]="⤥", -["LowerLeftArrow"]="↙", -["rdca"]="⤷", -["OverBar"]="‾", -["frac12"]="½", -["imacr"]="ī", -["boxVL"]="╣", -["sfr"]="𝔰", -["ensp"]=" ", -["dwangle"]="⦦", -["boxh"]="─", -["Eogon"]="Ę", +["ngtr"]="≯", +["Tstrok"]="Ŧ", +["lrarr"]="⇆", +["eqslantgtr"]="⪖", +["efr"]="𝔢", +["gne"]="⪈", +["csube"]="⫑", +["Xi"]="Ξ", +["sopf"]="𝕤", +["LeftVector"]="↼", +["ggg"]="⋙", +["Ubrcy"]="Ў", +["die"]="¨", +["topbot"]="⌶", +["Rfr"]="ℜ", +["ofcir"]="⦿", ["imped"]="Ƶ", -["Ecy"]="Э", -["lates"]="⪭︀", -["phi"]="φ", -["minusd"]="∸", -["planckh"]="ℎ", -["Ocirc"]="Ô", -["lE"]="≦", -["sum"]="∑", -["lrtri"]="⊿", -["ruluhar"]="⥨", -["lAarr"]="⇚", -["DownArrow"]="↓", -["zscr"]="𝓏", -["questeq"]="≟", -["ngeqq"]="≧̸", -["And"]="⩓", -["Dcy"]="Д", -["angrtvbd"]="⦝", -["rarrpl"]="⥅", -["thkap"]="≈", -["Otimes"]="⨷", -["lesssim"]="≲", -["gE"]="≧", -["ouml"]="ö", -["nsubseteq"]="⊈", -["bigcirc"]="◯", +["plusmn"]="±", +["nles"]="⩽̸", +["pcy"]="п", +["permil"]="‰", +["notinE"]="⋹̸", +["bigoplus"]="⨁", +["boxplus"]="⊞", +["doublebarwedge"]="⌆", +["ccaron"]="č", +["asymp"]="≈", +["sup1"]="¹", +["bumpeq"]="≏", +["nrtrie"]="⋭", +["larrbfs"]="⤟", +["emacr"]="ē", +["gtrless"]="≷", +["Kfr"]="𝔎", +["plus"]="+", +["risingdotseq"]="≓", +["notindot"]="⋵̸", +["Subset"]="⋐", +["CloseCurlyDoubleQuote"]="”", +["nlsim"]="≴", +["dcaron"]="ď", ["boxVR"]="╠", -["Vert"]="‖", -["naturals"]="ℕ", -["plussim"]="⨦", -["wscr"]="𝓌", -["raquo"]="»", -["bigodot"]="⨀", -["dashv"]="⊣", -["hairsp"]=" ", -["inodot"]="ı", -["vnsup"]="⊃⃒", +["lne"]="⪇", +["Pfr"]="𝔓", +["rightsquigarrow"]="↝", +["phone"]="☎", +["DiacriticalDot"]="˙", +["Congruent"]="≡", +["SquareIntersection"]="⊓", +["ordf"]="ª", +["loz"]="◊", ["cire"]="≗", -["vartriangleleft"]="⊲", -["rbrke"]="⦌", -["boxuL"]="╛", -["sce"]="⪰", -["nrtri"]="⋫", -["loarr"]="⇽", -["ShortRightArrow"]="→", -["xscr"]="𝓍", -["Bcy"]="Б", -["HumpEqual"]="≏", -["lmidot"]="ŀ", -["DownLeftVector"]="↽", -["leftharpoonup"]="↼", +["ecaron"]="ě", +["nsub"]="⊄", +["Fouriertrf"]="ℱ", +["Qfr"]="𝔔", +["dblac"]="˝", +["sc"]="≻", +["xrarr"]="⟶", +["ImaginaryI"]="ⅈ", +["angmsdag"]="⦮", +["wopf"]="𝕨", +["middot"]="·", +["gsiml"]="⪐", +["biguplus"]="⨄", +["Prime"]="″", +["bigcirc"]="◯", +["vsubnE"]="⫋︀", +["lrtri"]="⊿", +["nparallel"]="∦", ["lbbrk"]="❲", -["rarrtl"]="↣", -["SucceedsEqual"]="⪰", -["HumpDownHump"]="≎", -["Vee"]="⋁", -["Gopf"]="𝔾", -["otilde"]="õ", -["ofr"]="𝔬", -["Sub"]="⋐", -["gnE"]="≩", -["SquareSuperset"]="⊐", -["leftrightharpoons"]="⇋", -["tosa"]="⤩", -["Ucirc"]="Û", -["mscr"]="𝓂", -["Exists"]="∃", -["langle"]="⟨", -["rfr"]="𝔯", -["THORN"]="Þ", -["gtreqqless"]="⪌", -["eqvparsl"]="⧥", -["NotCongruent"]="≢", -["phiv"]="ϕ", -["lsquor"]="‚", -["prec"]="≺", -["igrave"]="ì", -["Fopf"]="𝔽", -["nwArr"]="⇖", -["nscr"]="𝓃", -["gescc"]="⪩", -["intprod"]="⨼", -["larrlp"]="↫", -["AMP"]="&", -["psi"]="ψ", -["mDDot"]="∺", -["VDash"]="⊫", -["Lleftarrow"]="⇚", -["mfr"]="𝔪", +["dotsquare"]="⊡", +["rceil"]="⌉", ["gesles"]="⪔", -["kscr"]="𝓀", -["Omega"]="Ω", -["bumpe"]="≏", -["NotDoubleVerticalBar"]="∦", -["frac18"]="⅛", -["ecolon"]="≕", -["compfn"]="∘", -["supdot"]="⪾", -["Alpha"]="Α", -["rsquor"]="’", +["roarr"]="⇾", +["simplus"]="⨤", +["lagran"]="ℒ", +["pr"]="≺", +["mnplus"]="∓", +["Iota"]="Ι", +["NestedLessLess"]="≪", +["utilde"]="ũ", +["mldr"]="…", +["bigstar"]="★", +["micro"]="µ", ["percnt"]="%", -["pfr"]="𝔭", -["frasl"]="⁄", -["lscr"]="𝓁", -["aleph"]="ℵ", -["swArr"]="⇙", -["uuml"]="ü", -["uacute"]="ú", -["scE"]="⪴", -["hellip"]="…", +["fllig"]="fl", +["euml"]="ë", +["circledR"]="®", +["lltri"]="◺", +["IEcy"]="Е", +["notniva"]="∌", +["or"]="∨", +["curvearrowleft"]="↶", +["gnapprox"]="⪊", +["NotLessSlantEqual"]="⩽̸", +["Cconint"]="∰", +["NotSucceedsTilde"]="≿̸", +["gammad"]="ϝ", +["rightharpoondown"]="⇁", +["CounterClockwiseContourIntegral"]="∳", +["supdot"]="⪾", +["upsi"]="υ", +["nvle"]="≤⃒", +["beth"]="ℶ", +["nhArr"]="⇎", +["Gamma"]="Γ", +["complement"]="∁", +["xoplus"]="⨁", +["chi"]="χ", +["backepsilon"]="϶", +["nsube"]="⊈", +["aopf"]="𝕒", +["para"]="¶", +["drbkarow"]="⤐", +["Dstrok"]="Đ", +["isinE"]="⋹", +["Ecirc"]="Ê", +["aelig"]="æ", ["ulcorner"]="⌜", -["prnap"]="⪹", -["Gg"]="⋙", -["wedge"]="∧", -["SquareSubset"]="⊏", -["qscr"]="𝓆", -["kfr"]="𝔨", -["RightTee"]="⊢", -["eqsim"]="≂", -["rbrksld"]="⦎", -["ohm"]="Ω", -["lozf"]="⧫", -["cups"]="∪︀", -["epsilon"]="ε", -["ncap"]="⩃", -["Leftarrow"]="⇐", -["ultri"]="◸", -["egrave"]="è", -["vBarv"]="⫩", -["bNot"]="⫭", -["nfr"]="𝔫", -["rsqb"]="]", -["OpenCurlyDoubleQuote"]="“", -["rsquo"]="’", -["Del"]="∇", -["smtes"]="⪬︀", -["varphi"]="ϕ", -["CirclePlus"]="⊕", -["prcue"]="≼", -["curren"]="¤", -["nltri"]="⋪", -["oscr"]="ℴ", -["Wcirc"]="Ŵ", -["prime"]="′", -["ifr"]="𝔦", -["apos"]="'", -["UpTee"]="⊥", -["lescc"]="⪨", -["NegativeMediumSpace"]="", -["DownTee"]="⊤", -["NotSquareSubsetEqual"]="⋢", -["LowerRightArrow"]="↘", -["wr"]="≀", +["Lcaron"]="Ľ", +["bopf"]="𝕓", +["midast"]="*", +["barvee"]="⊽", +["nvgt"]=">⃒", +["bnequiv"]="≡⃥", +["caron"]="ˇ", ["andand"]="⩕", -["gvnE"]="≩︀", -["lfr"]="𝔩", -["lowast"]="∗", -["gfr"]="𝔤", -["digamma"]="ϝ", -["cemptyv"]="⦲", -["ubreve"]="ŭ", -["dstrok"]="đ", +["eparsl"]="⧣", +["xhArr"]="⟺", +["Yuml"]="Ÿ", +["fallingdotseq"]="≒", +["vltri"]="⊲", +["gtrarr"]="⥸", +["nGtv"]="≫̸", +["congdot"]="⩭", +["lfisht"]="⥼", +["nrtri"]="⋫", +["prnsim"]="⋨", +["nsupE"]="⫆̸", +["Colone"]="⩴", +["SquareSuperset"]="⊐", +["boxUL"]="╝", +["Rrightarrow"]="⇛", +["nleftrightarrow"]="↮", +["Conint"]="∯", +["ffilig"]="ffi", +["nleqq"]="≦̸", +["drcrop"]="⌌", +["boxh"]="─", +["Lang"]="⟪", +["ldrdhar"]="⥧", +["rbrack"]="]", +["nvinfin"]="⧞", +["succsim"]="≿", +["boxhu"]="┴", +["tau"]="τ", +["incare"]="℅", +["flat"]="♭", +["angst"]="Å", +["Oslash"]="Ø", +["Vfr"]="𝔙", +["tscy"]="ц", +["lates"]="⪭︀", +["eopf"]="𝕖", +["Because"]="∵", +["frac25"]="⅖", +["daleth"]="ℸ", +["ic"]="", +["ensp"]=" ", +["lrm"]="", +["DoubleLeftRightArrow"]="⇔", +["kappa"]="κ", +["fopf"]="𝕗", +["cularr"]="↶", +["shy"]="", +["yen"]="¥", +["glj"]="⪤", +["dzcy"]="џ", ["expectation"]="ℰ", -["sime"]="≃", -["ltrif"]="◂", -["iacute"]="í", -["chi"]="χ", +["dash"]="‐", +["female"]="♀", +["UnderBrace"]="⏟", +["rotimes"]="⨵", +["caps"]="∩︀", +["copf"]="𝕔", +["lparlt"]="⦓", +["NotLess"]="≮", +["simrarr"]="⥲", +["dtri"]="▿", +["supne"]="⊋", +["block"]="█", +["siml"]="⪝", +["blk12"]="▒", +["natural"]="♮", +["frac34"]="¾", +["frac38"]="⅜", +["Ufr"]="𝔘", +["gg"]="≫", +["Square"]="□", +["lt"]="<", ["erDot"]="≓", -["Vcy"]="В", +["RightAngleBracket"]="⟩", +["LeftDownTeeVector"]="⥡", +["NotGreaterGreater"]="≫̸", +["uogon"]="ų", +["ocirc"]="ô", +["sung"]="♪", +["dollar"]="$", +["Scaron"]="Š", +["top"]="⊤", +["iopf"]="𝕚", +["udhar"]="⥮", +["gjcy"]="ѓ", +["ncap"]="⩃", +["circlearrowleft"]="↺", +["bernou"]="ℬ", +["angmsdaf"]="⦭", +["ldsh"]="↲", +["isins"]="⋴", +["RoundImplies"]="⥰", +["bigtriangledown"]="▽", +["LeftAngleBracket"]="⟨", +["HorizontalLine"]="─", +["Vdashl"]="⫦", +["rHar"]="⥤", +["Tcaron"]="Ť", +["LeftDownVectorBar"]="⥙", +["jopf"]="𝕛", +["LeftRightVector"]="⥎", +["LessGreater"]="≶", +["npart"]="∂̸", +["nsmid"]="∤", +["RightTriangleBar"]="⧐", +["nesim"]="≂̸", +["deg"]="°", +["LeftTeeArrow"]="↤", +["NotHumpDownHump"]="≎̸", +["tridot"]="◬", +["lceil"]="⌈", +["ExponentialE"]="ⅇ", +["tosa"]="⤩", +["gopf"]="𝕘", +["ContourIntegral"]="∮", +["late"]="⪭", +["bbrk"]="⎵", +["half"]="½", ["boxhd"]="┬", -["DoubleDownArrow"]="⇓", -["lArr"]="⇐", -["demptyv"]="⦱", -["smt"]="⪪", -["intcal"]="⊺", -["in"]="∈", -["capcup"]="⩇", -["kjcy"]="ќ", -["Ccirc"]="Ĉ", -["bnequiv"]="≡⃥", -["Emacr"]="Ē", -["siml"]="⪝", -["jfr"]="𝔧", -["gesdoto"]="⪂", -["efr"]="𝔢", -["precneqq"]="⪵", -["because"]="∵", -["Udblac"]="Ű", -["lg"]="≶", -["boxvr"]="├", -["fpartint"]="⨍", -["rbbrk"]="❳", -["LT"]="<", -["cap"]="∩", -["circledR"]="®", -["notinvc"]="⋶", -["zwj"]="", -["nle"]="≰", -["hfr"]="𝔥", -["bigsqcup"]="⨆", -["nGg"]="⋙̸", -["doublebarwedge"]="⌆", +["hopf"]="𝕙", +["Ouml"]="Ö", +["Atilde"]="Ã", +["planckh"]="ℎ", +["MinusPlus"]="∓", +["uharl"]="↿", +["aleph"]="ℵ", +["boxHD"]="╦", +["lesseqgtr"]="⋚", +["colon"]=":", +["SquareSupersetEqual"]="⊒", +["isinsv"]="⋳", +["lnapprox"]="⪉", +["PrecedesSlantEqual"]="≼", +["boxuL"]="╛", +["csup"]="⫐", +["hstrok"]="ħ", +["leftrightharpoons"]="⇋", +["boxuR"]="╘", +["DD"]="ⅅ", +["Longrightarrow"]="⟹", +["epsilon"]="ε", +["NotTildeTilde"]="≉", +["nfr"]="𝔫", +["Vscr"]="𝒱", +["Ncy"]="Н", +["boxUl"]="╜", +["searr"]="↘", +["ddagger"]="‡", +["KHcy"]="Х", +["NotSuperset"]="⊃⃒", +["Uparrow"]="⇑", +["nsqsube"]="⋢", +["Imacr"]="Ī", +["nspar"]="∦", +["supsetneqq"]="⫌", +["Sscr"]="𝒮", +["lobrk"]="⟦", +["mfr"]="𝔪", +["acirc"]="â", +["subne"]="⊊", +["Icirc"]="Î", +["Re"]="ℜ", +["nearrow"]="↗", ["YUcy"]="Ю", -["empty"]="∅", -["nacute"]="ń", -["Acirc"]="Â", -["bigtriangleup"]="△", -["hybull"]="⁃", -["Ycy"]="Ы", -["isindot"]="⋵", -["iota"]="ι", -["numero"]="№", -["cfr"]="𝔠", -["llarr"]="⇇", -["lrhard"]="⥭", -["cross"]="✗", +["eqsim"]="≂", +["UnionPlus"]="⊎", +["topcir"]="⫱", +["boxVL"]="╣", +["Xscr"]="𝒳", +["QUOT"]="\"", +["RightDownVectorBar"]="⥕", +["epsi"]="ε", +["natur"]="♮", +["leftthreetimes"]="⋋", +["hercon"]="⊹", +["lHar"]="⥢", +["Aring"]="Å", +["Kcedil"]="Ķ", +["kfr"]="𝔨", +["Oacute"]="Ó", ["zwnj"]="", +["plusdu"]="⨥", +["Kcy"]="К", +["sqcup"]="⊔", +["zscr"]="𝓏", +["Assign"]="≔", +["zopf"]="𝕫", +["lstrok"]="ł", +["zigrarr"]="⇝", +["ShortDownArrow"]="↓", +["simlE"]="⪟", +["mid"]="∣", ["zeta"]="ζ", -["precnsim"]="⋨", -["zcy"]="з", +["gE"]="≧", +["zeetrf"]="ℨ", +["qfr"]="𝔮", ["zcaron"]="ž", -["xodot"]="⨀", -["RightDownTeeVector"]="⥝", -["ljcy"]="љ", +["Yacute"]="Ý", +["hksearow"]="⤥", +["Atilde"]="Ã", +["zacute"]="ź", +["bsemi"]="⁏", +["bigtriangleup"]="△", +["yuml"]="ÿ", +["yucy"]="ю", ["yscr"]="𝓎", +["yopf"]="𝕪", ["yicy"]="ї", -["Rcy"]="Р", -["nsubset"]="⊂⃒", -["yen"]="¥", -["ycy"]="ы", -["simeq"]="≃", -["dharr"]="⇂", -["ycirc"]="ŷ", -["hArr"]="⇔", -["yacute"]="ý", -["yacute"]="ý", +["urcorn"]="⌝", +["Aacute"]="Á", +["backsimeq"]="⋍", +["ShortUpArrow"]="↑", ["xwedge"]="⋀", -["xoplus"]="⨁", -["nparsl"]="⫽⃥", -["PartialD"]="∂", -["YAcy"]="Я", -["xrArr"]="⟹", -["xotime"]="⨂", -["sstarf"]="⋆", -["yuml"]="ÿ", -["Int"]="∬", -["bsolb"]="⧅", +["Jcy"]="Й", +["nltrie"]="⋬", +["starf"]="★", +["zfr"]="𝔷", +["comma"]=",", +["xsqcup"]="⨆", +["boxVr"]="╟", +["cirscir"]="⧂", +["ucirc"]="û", +["Cross"]="⨯", +["naturals"]="ℕ", +["quaternions"]="ℍ", +["xopf"]="𝕩", +["xodot"]="⨀", ["xnis"]="⋻", -["xmap"]="⟼", -["hercon"]="⊹", -["xlArr"]="⟸", -["xharr"]="⟷", -["rbrack"]="]", +["lsaquo"]="‹", +["xlarr"]="⟵", +["Zacute"]="Ź", +["vdash"]="⊢", +["submult"]="⫁", +["Oscr"]="𝒪", ["xdtri"]="▽", -["diamond"]="⋄", +["LowerRightArrow"]="↘", ["xcup"]="⋃", -["subsup"]="⫓", -["lneqq"]="≨", -["dharl"]="⇃", -["Scy"]="С", -["wopf"]="𝕨", -["rightleftharpoons"]="⇌", -["wfr"]="𝔴", -["nsupseteqq"]="⫆̸", -["boxuR"]="╘", -["permil"]="‰", +["Ycy"]="Ы", +["timesb"]="⊠", +["Ucirc"]="Û", +["eth"]="ð", +["wscr"]="𝓌", +["ratail"]="⤚", +["wr"]="≀", +["Ograve"]="Ò", +["wp"]="℘", +["euml"]="ë", +["yfr"]="𝔶", +["rangle"]="⟩", +["hearts"]="♥", ["wedgeq"]="≙", -["eparsl"]="⧣", -["RoundImplies"]="⥰", -["afr"]="𝔞", -["vsubne"]="⊊︀", -["sup2"]="²", +["wedge"]="∧", +["gtcir"]="⩺", +["par"]="∥", +["swarr"]="↙", +["phiv"]="ϕ", +["NotElement"]="∉", +["alpha"]="α", +["SquareUnion"]="⊔", +["parsl"]="⫽", ["vscr"]="𝓋", +["rharu"]="⇀", +["vrtri"]="⊳", +["comp"]="∁", +["Agrave"]="À", +["rdca"]="⤷", +["vprop"]="∝", +["Tscr"]="𝒯", ["vopf"]="𝕧", -["lbrace"]="{", +["ldca"]="⤶", ["vnsub"]="⊂⃒", -["vltri"]="⊲", -["Uacute"]="Ú", -["vfr"]="𝔳", -["sacute"]="ś", -["njcy"]="њ", -["frac35"]="⅗", -["squf"]="▪", +["gbreve"]="ğ", +["NotSquareSubset"]="⊏̸", +["verbar"]="|", +["YAcy"]="Я", ["veeeq"]="≚", -["lsime"]="⪍", -["mapstoleft"]="↤", +["downharpoonleft"]="⇃", +["Star"]="⋆", +["xfr"]="𝔵", +["nges"]="⩾̸", +["GreaterGreater"]="⪢", +["vartriangleright"]="⊳", +["rcy"]="р", +["vartheta"]="ϑ", +["varsupsetneqq"]="⫌︀", +["dashv"]="⊣", ["varsupsetneq"]="⊋︀", -["varsubsetneq"]="⊊︀", -["Tcy"]="Т", -["cwint"]="∱", -["varsigma"]="ς", -["LessGreater"]="≶", +["Ncaron"]="Ň", +["varsubsetneqq"]="⫋︀", ["varrho"]="ϱ", -["varpropto"]="∝", -["pr"]="≺", -["backprime"]="‵", +["triangledown"]="▿", +["varpi"]="ϖ", +["sol"]="/", +["ssetmn"]="∖", +["varnothing"]="∅", ["varkappa"]="ϰ", -["Poincareplane"]="ℌ", -["roplus"]="⨮", -["Edot"]="Ė", -["copysr"]="℗", +["vangrt"]="⦜", +["Leftarrow"]="⇐", +["Qscr"]="𝒬", +["uwangle"]="⦧", +["rarrap"]="⥵", ["uuml"]="ü", -["dfr"]="𝔡", ["uuarr"]="⇈", -["uogon"]="ų", -["Uscr"]="𝒰", -["gel"]="⋛", -["olarr"]="↺", -["lsaquo"]="‹", -["omacr"]="ō", -["utdot"]="⋰", -["or"]="∨", -["Ocirc"]="Ô", -["ClockwiseContourIntegral"]="∲", -["SmallCircle"]="∘", -["gtcc"]="⪧", ["uscr"]="𝓊", +["urtri"]="◹", +["uring"]="ů", +["sect"]="§", +["quatint"]="⨖", ["urcrop"]="⌎", -["ContourIntegral"]="∮", -["szlig"]="ß", -["egs"]="⪖", -["Ucy"]="У", -["ange"]="⦤", -["upharpoonright"]="↾", -["xcap"]="⋂", +["wfr"]="𝔴", +["duhar"]="⥯", +["upuparrows"]="⇈", +["macr"]="¯", +["SmallCircle"]="∘", +["upsih"]="ϒ", +["uplus"]="⊎", +["shy"]="", +["uparrow"]="↑", ["uml"]="¨", -["nsubseteqq"]="⫅̸", +["eDot"]="≑", +["blk34"]="▓", +["umacr"]="ū", +["ultri"]="◸", +["rbbrk"]="❳", ["ulcorn"]="⌜", -["uhblk"]="▀", -["scirc"]="ŝ", -["shy"]="", -["uharl"]="↿", +["uharr"]="↾", ["ugrave"]="ù", -["lstrok"]="ł", -["Gamma"]="Γ", -["udhar"]="⥮", -["udblac"]="ű", -["ucirc"]="û", -["loang"]="⟬", -["uarr"]="↑", +["Ograve"]="Ò", +["nsupseteq"]="⊉", +["ufisht"]="⥾", +["Breve"]="˘", +["ucy"]="у", +["cylcty"]="⌭", +["ubreve"]="ŭ", +["SHcy"]="Ш", +["boxdR"]="╒", +["awint"]="⨑", +["uHar"]="⥣", ["uArr"]="⇑", -["twoheadrightarrow"]="↠", +["REG"]="®", +["twoheadleftarrow"]="↞", ["tstrok"]="ŧ", +["iprod"]="⨼", ["tshcy"]="ћ", -["tscy"]="ц", -["angsph"]="∢", -["kappa"]="κ", ["tscr"]="𝓉", -["tritime"]="⨻", -["trisb"]="⧍", +["trpezium"]="⏢", +["ycirc"]="ŷ", +["larrhk"]="↩", +["triplus"]="⨹", ["triminus"]="⨺", -["trie"]="≜", -["tridot"]="◬", -["aacute"]="á", +["Rsh"]="↱", +["becaus"]="∵", +["Acirc"]="Â", ["trianglerighteq"]="⊵", -["RightTeeVector"]="⥛", ["triangleright"]="▹", +["hkswarow"]="⤦", +["triangleq"]="≜", +["AElig"]="Æ", ["trianglelefteq"]="⊴", +["varpropto"]="∝", ["triangle"]="▵", -["NegativeVeryThinSpace"]="", -["triangledown"]="▿", -["dash"]="‐", -["trade"]="™", +["tprime"]="‴", ["topfork"]="⫚", -["Upsi"]="ϒ", -["topcir"]="⫱", -["loplus"]="⨭", -["Zdot"]="Ż", -["lbrke"]="⦋", -["angmsd"]="∡", -["dArr"]="⇓", -["topbot"]="⌶", ["toea"]="⤨", -["iexcl"]="¡", -["acirc"]="â", -["easter"]="⩮", -["CloseCurlyDoubleQuote"]="”", -["cupcup"]="⩊", -["rlhar"]="⇌", -["nsimeq"]="≄", -["Uparrow"]="⇑", -["tau"]="τ", -["npr"]="⊀", -["phone"]="☎", -["emacr"]="ē", -["SHcy"]="Ш", -["gtdot"]="⋗", -["ReverseUpEquilibrium"]="⥯", +["ac"]="∾", +["timesd"]="⨰", +["UpArrowDownArrow"]="⇅", +["timesbar"]="⨱", +["llhard"]="⥫", +["CHcy"]="Ч", +["odblac"]="ő", +["THORN"]="Þ", +["notni"]="∌", +["nbump"]="≎̸", +["eqcolon"]="≕", ["tilde"]="˜", -["angmsdac"]="⦪", -["duhar"]="⥯", -["approxeq"]="≊", -["NotSupersetEqual"]="⊉", -["thorn"]="þ", -["thksim"]="∼", -["subsim"]="⫇", -["bnot"]="⌐", +["simgE"]="⪠", +["intercal"]="⊺", +["thetasym"]="ϑ", +["xcirc"]="◯", ["thinsp"]=" ", ["thicksim"]="∼", -["gt"]=">", -["UpperLeftArrow"]="↖", -["thetasym"]="ϑ", -["simlE"]="⪟", -["bbrktbrk"]="⎶", -["Jcirc"]="Ĵ", -["cedil"]="¸", -["NotLessSlantEqual"]="⩽̸", -["rx"]="℞", -["infin"]="∞", -["curlyvee"]="⋎", -["LeftArrow"]="←", -["tfr"]="𝔱", -["blacksquare"]="▪", -["emptyv"]="∅", -["Mellintrf"]="ℳ", -["aogon"]="ą", -["succnapprox"]="⪺", -["tcy"]="т", -["ecaron"]="ě", +["LeftArrowBar"]="⇤", +["Uuml"]="Ü", +["DownTeeArrow"]="↧", +["cent"]="¢", +["hookleftarrow"]="↩", +["odiv"]="⨸", +["therefore"]="∴", +["ClockwiseContourIntegral"]="∲", +["Ofr"]="𝔒", +["auml"]="ä", +["frac12"]="½", +["nRightarrow"]="⇏", +["kcedil"]="ķ", +["DownRightVectorBar"]="⥗", +["boxDL"]="╗", ["tcedil"]="ţ", -["boxhU"]="╨", -["boxVl"]="╢", -["gacute"]="ǵ", -["tcaron"]="ť", -["boxDr"]="╓", -["bump"]="≎", -["blk12"]="▒", -["RightArrow"]="→", -["els"]="⪕", -["apacir"]="⩯", -["acy"]="а", -["omid"]="⦶", -["boxbox"]="⧉", -["iecy"]="е", -["szlig"]="ß", -["upsih"]="ϒ", -["swnwar"]="⤪", -["Icirc"]="Î", -["swarr"]="↙", -["times"]="×", -["Ograve"]="Ò", -["hoarr"]="⇿", -["NotTildeEqual"]="≄", -["IEcy"]="Е", -["iexcl"]="¡", -["bdquo"]="„", -["TildeTilde"]="≈", +["boxUr"]="╙", +["icirc"]="î", +["lmoustache"]="⎰", +["prime"]="′", +["ngeq"]="≱", ["nshortparallel"]="∦", -["subedot"]="⫃", -["supsetneq"]="⊋", -["CenterDot"]="·", -["lessapprox"]="⪅", -["LeftVectorBar"]="⥒", -["bigoplus"]="⨁", -["supseteqq"]="⫆", -["drcorn"]="⌟", -["hstrok"]="ħ", -["HorizontalLine"]="─", -["curarrm"]="⤼", -["lopar"]="⦅", +["szlig"]="ß", +["Egrave"]="È", +["sqsubset"]="⊏", +["NotRightTriangleEqual"]="⋭", +["swarrow"]="↙", +["Ucirc"]="Û", +["DownArrowBar"]="⤓", +["Lfr"]="𝔏", ["wcirc"]="ŵ", +["LessLess"]="⪡", +["Tilde"]="∼", +["empty"]="∅", +["swarhk"]="⤦", +["DownLeftVector"]="↽", +["Uscr"]="𝒰", +["khcy"]="х", +["gesl"]="⋛︀", +["supsub"]="⫔", +["malt"]="✠", +["cirmid"]="⫯", +["PrecedesTilde"]="≾", +["lharul"]="⥪", +["rightleftharpoons"]="⇌", +["Ccedil"]="Ç", +["plusb"]="⊞", +["nsupe"]="⊉", +["geq"]="≥", +["dHar"]="⥥", +["lcedil"]="ļ", +["ange"]="⦤", +["supseteq"]="⊇", +["Upsi"]="ϒ", +["mapstodown"]="↧", +["profalar"]="⌮", +["cupdot"]="⊍", +["mapstoleft"]="↤", +["xotime"]="⨂", +["tbrk"]="⎴", +["supmult"]="⫂", +["disin"]="⋲", ["suphsub"]="⫗", -["Otilde"]="Õ", -["EmptySmallSquare"]="◻", +["eg"]="⪚", ["supedot"]="⫄", -["Kopf"]="𝕂", -["boxminus"]="⊟", -["csupe"]="⫒", -["Ffr"]="𝔉", -["origof"]="⊶", -["sup3"]="³", -["boxH"]="═", -["vsubnE"]="⫋︀", -["Hcirc"]="Ĥ", -["block"]="█", +["NonBreakingSpace"]=" ", +["Euml"]="Ë", ["bsolhsub"]="⟈", -["Supset"]="⋑", -["GreaterFullEqual"]="≧", +["supdsub"]="⫘", +["sup3"]="³", +["DoubleLeftTee"]="⫤", +["angrtvb"]="⊾", +["supsim"]="⫈", +["sup2"]="²", +["sup2"]="²", +["circledast"]="⊛", +["rfr"]="𝔯", +["darr"]="↓", +["rarrhk"]="↪", +["macr"]="¯", ["sup1"]="¹", -["llcorner"]="⌞", -["sopf"]="𝕤", -["larrbfs"]="⤟", -["succneqq"]="⪶", -["part"]="∂", -["eacute"]="é", -["COPY"]="©", -["frac23"]="⅔", -["dbkarow"]="⤏", -["succeq"]="⪰", -["notni"]="∌", -["Euml"]="Ë", -["harrcir"]="⥈", -["leftrightsquigarrow"]="↭", -["xcirc"]="◯", -["caret"]="⁁", -["les"]="⩽", -["lharul"]="⥪", -["bsime"]="⋍", -["DoubleLeftArrow"]="⇐", -["ecirc"]="ê", -["subsetneqq"]="⫋", -["crarr"]="↵", -["cuwed"]="⋏", -["Ugrave"]="Ù", -["Gcirc"]="Ĝ", -["LeftTriangleBar"]="⧏", +["aacute"]="á", +["oscr"]="ℴ", +["lharu"]="↼", +["telrec"]="⌕", +["Dashv"]="⫤", +["succnsim"]="⋩", +["AElig"]="Æ", +["Vee"]="⋁", +["succnapprox"]="⪺", +["CapitalDifferentialD"]="ⅅ", ["gsim"]="≳", -["RightTriangleEqual"]="⊵", -["lacute"]="ĺ", -["Ccedil"]="Ç", -["ee"]="ⅇ", -["heartsuit"]="♥", -["coloneq"]="≔", -["subseteq"]="⊆", -["NotSucceedsTilde"]="≿̸", +["succapprox"]="⪸", +["rmoustache"]="⎱", +["DotEqual"]="≐", +["subsup"]="⫓", +["Gcedil"]="Ģ", +["questeq"]="≟", +["lthree"]="⋋", +["cong"]="≅", +["lcub"]="{", +["radic"]="√", +["subrarr"]="⥹", +["nvlt"]="<⃒", ["subplus"]="⪿", -["cupcap"]="⩆", -["Amacr"]="Ā", -["angst"]="Å", -["supsetneqq"]="⫌", -["sube"]="⊆", -["imagpart"]="ℑ", -["Lambda"]="Λ", -["Afr"]="𝔄", -["sub"]="⊂", +["bigsqcup"]="⨆", +["downarrow"]="↓", +["auml"]="ä", +["boxdL"]="╕", +["boxvr"]="├", +["lbrkslu"]="⦍", +["bigwedge"]="⋀", +["apacir"]="⩯", +["Wscr"]="𝒲", +["iecy"]="е", +["VerticalSeparator"]="❘", +["Yfr"]="𝔜", ["strns"]="¯", ["straightphi"]="ϕ", -["boxvL"]="╡", -["dagger"]="†", -["eta"]="η", -["KHcy"]="Х", -["Yfr"]="𝔜", -["lbrack"]="[", -["VerticalBar"]="∣", -["rightleftarrows"]="⇄", -["fltns"]="▱", -["verbar"]="|", -["RightAngleBracket"]="⟩", -["DownRightVector"]="⇁", -["Lacute"]="Ĺ", -["sol"]="/", -["lrm"]="", -["lesdot"]="⩿", -["nleqslant"]="⩽̸", -["capcap"]="⩋", +["kcy"]="к", +["aogon"]="ą", +["star"]="☆", +["caret"]="⁁", +["cups"]="∪︀", +["iquest"]="¿", +["spar"]="∥", +["NotExists"]="∄", +["Gt"]="≫", ["squ"]="□", -["xutri"]="△", -["TSHcy"]="Ћ", -["gap"]="⪆", -["gtrdot"]="⋗", -["scnap"]="⪺", -["bkarow"]="⤍", -["NotPrecedes"]="⊀", -["suphsol"]="⟉", +["sqsupe"]="⊒", +["subnE"]="⫋", ["sqsup"]="⊐", -["epsi"]="ε", -["sqsubset"]="⊏", -["check"]="✓", -["UpArrowBar"]="⤒", -["NotPrecedesEqual"]="⪯̸", -["csube"]="⫑", -["nsupset"]="⊃⃒", -["lesdoto"]="⪁", +["sqsubseteq"]="⊑", +["ffr"]="𝔣", +["UpTeeArrow"]="↥", +["middot"]="·", +["DoubleLongRightArrow"]="⟹", +["sqsube"]="⊑", +["sce"]="⪰", +["Sup"]="⋑", +["cacute"]="ć", +["GreaterFullEqual"]="≧", +["pfr"]="𝔭", +["Lleftarrow"]="⇚", +["heartsuit"]="♥", +["precsim"]="≾", +["nsucceq"]="⪰̸", ["sqcups"]="⊔︀", -["suplarr"]="⥻", -["nsim"]="≁", -["NotEqualTilde"]="≂̸", -["nearr"]="↗", -["SquareIntersection"]="⊓", -["intlarhk"]="⨗", -["Ecirc"]="Ê", -["vangrt"]="⦜", -["fllig"]="fl", -["Leftrightarrow"]="⇔", -["primes"]="ℙ", -["nvlArr"]="⤂", +["Igrave"]="Ì", +["iquest"]="¿", +["Pcy"]="П", +["thksim"]="∼", +["djcy"]="ђ", +["hfr"]="𝔥", +["spadesuit"]="♠", +["rbrkslu"]="⦐", +["shcy"]="ш", +["barwed"]="⌅", +["rpar"]=")", +["iiiint"]="⨌", +["solb"]="⧄", +["Integral"]="∫", +["napE"]="⩰̸", +["softcy"]="ь", +["NotRightTriangleBar"]="⧐̸", +["Mu"]="Μ", +["frasl"]="⁄", +["Icy"]="И", +["dfisht"]="⥿", ["smte"]="⪬", -["ltri"]="◃", -["Tscr"]="𝒯", +["smt"]="⪪", +["bottom"]="⊥", +["oacute"]="ó", +["ofr"]="𝔬", ["smid"]="∣", -["smeparsl"]="⧤", -["rarrc"]="⤳", -["divideontimes"]="⋇", -["eth"]="ð", -["boxvR"]="╞", -["copy"]="©", -["nges"]="⩾̸", -["fcy"]="ф", -["ShortUpArrow"]="↑", -["simplus"]="⨤", -["frac45"]="⅘", -["nexists"]="∄", -["LeftTeeArrow"]="↤", -["leftleftarrows"]="⇇", -["simg"]="⪞", -["euro"]="€", -["simdot"]="⩪", -["sim"]="∼", -["sigmav"]="ς", -["iuml"]="ï", -["updownarrow"]="↕", -["shy"]="", -["solb"]="⧄", +["ReverseUpEquilibrium"]="⥯", +["smashp"]="⨳", +["slarr"]="←", +["simne"]="≆", +["LeftRightArrow"]="↔", +["thkap"]="≈", +["nlArr"]="⇍", +["chcy"]="ч", +["not"]="¬", +["kgreen"]="ĸ", +["simeq"]="≃", +["LongRightArrow"]="⟶", +["sime"]="≃", +["sigma"]="σ", +["odot"]="⊙", +["amp"]="&", +["Longleftrightarrow"]="⟺", +["Upsilon"]="Υ", +["upharpoonleft"]="↿", +["bigotimes"]="⨂", +["parallel"]="∥", +["ffllig"]="ffl", +["shortparallel"]="∥", +["boxdr"]="┌", +["shortmid"]="∣", +["Euml"]="Ë", +["UpDownArrow"]="↕", +["lcaron"]="ľ", +["sfrown"]="⌢", +["harr"]="↔", +["dsol"]="⧶", +["Beta"]="Β", +["srarr"]="→", +["boxHd"]="╤", ["nesear"]="⤨", -["Umacr"]="Ū", -["uharr"]="↾", -["Therefore"]="∴", -["Barv"]="⫧", -["amacr"]="ā", ["setmn"]="∖", +["DoubleDot"]="¨", ["setminus"]="∖", -["Sscr"]="𝒮", +["andd"]="⩜", +["leftrightarrow"]="↔", ["seswar"]="⤩", -["ccupssm"]="⩐", -["searr"]="↘", +["eogon"]="ę", +["bne"]="=⃥", +["DownBreve"]="̑", +["nLtv"]="≪̸", +["nprcue"]="⋠", +["SucceedsSlantEqual"]="≽", +["downdownarrows"]="⇊", +["seArr"]="⇘", +["Utilde"]="Ũ", +["Gammad"]="Ϝ", +["sdote"]="⩦", +["iexcl"]="¡", +["nvdash"]="⊬", +["csupe"]="⫒", ["sdotb"]="⊡", -["icirc"]="î", -["amp"]="&", -["gcy"]="г", -["Aacute"]="Á", -["boxvh"]="┼", -["bfr"]="𝔟", -["OverParenthesis"]="⏜", -["sqsupe"]="⊒", -["amp"]="&", -["frac14"]="¼", -["ZeroWidthSpace"]="", -["Colon"]="∷", -["Proportional"]="∝", -["thickapprox"]="≈", -["subseteqq"]="⫅", -["IJlig"]="IJ", -["scedil"]="ş", -["lrarr"]="⇆", -["angmsdaf"]="⦭", -["downharpoonleft"]="⇃", +["scsim"]="≿", +["bcy"]="б", +["NotTilde"]="≁", +["minus"]="−", +["scnE"]="⪶", +["scirc"]="ŝ", +["oplus"]="⊕", +["para"]="¶", +["Ycirc"]="Ŷ", +["notinva"]="∉", +["nLl"]="⋘̸", +["Aring"]="Å", +["THORN"]="Þ", +["numero"]="№", +["scaron"]="š", +["bsol"]="\\", +["harrw"]="↭", +["exponentiale"]="ⅇ", +["eDDot"]="⩷", +["AMP"]="&", +["gamma"]="γ", +["opar"]="⦷", +["cwint"]="∱", +["GreaterEqual"]="≥", +["bigcap"]="⋂", +["rtimes"]="⋊", +["integers"]="ℤ", +["easter"]="⩮", ["RightDoubleBracket"]="⟧", -["half"]="½", -["larr"]="←", ["sbquo"]="‚", -["Vscr"]="𝒱", -["nVdash"]="⊮", -["DownRightVectorBar"]="⥗", -["RightDownVector"]="⇂", -["disin"]="⋲", -["nsce"]="⪰̸", -["rbrace"]="}", -["MinusPlus"]="∓", -["rtrif"]="▸", -["parsim"]="⫳", -["supseteq"]="⊇", -["Superset"]="⊃", -["Verbar"]="‖", +["cfr"]="𝔠", +["ruluhar"]="⥨", +["image"]="ℑ", +["nleqslant"]="⩽̸", +["Yopf"]="𝕐", +["apE"]="⩰", +["NotPrecedesEqual"]="⪯̸", +["rsqb"]="]", +["rtrie"]="⊵", +["rthree"]="⋌", +["amp"]="&", +["eng"]="ŋ", +["ll"]="≪", +["rrarr"]="⇉", +["RightTee"]="⊢", +["ETH"]="Ð", +["ReverseEquilibrium"]="⇋", +["langle"]="⟨", ["Zeta"]="Ζ", -["nsccue"]="⋡", -["bsemi"]="⁏", -["ldquor"]="„", -["curarr"]="↷", -["rscr"]="𝓇", -["rppolint"]="⨒", -["rpargt"]="⦔", -["rotimes"]="⨵", -["Equal"]="⩵", -["ropf"]="𝕣", -["ropar"]="⦆", -["roarr"]="⇾", -["odot"]="⊙", -["natural"]="♮", -["slarr"]="←", -["angmsdae"]="⦬", -["acE"]="∾̳", +["roplus"]="⨮", +["there4"]="∴", +["DownRightTeeVector"]="⥟", +["npar"]="∦", +["robrk"]="⟧", +["exist"]="∃", +["lfr"]="𝔩", +["VDash"]="⊫", +["rnmid"]="⫮", +["rmoust"]="⎱", ["rlm"]="", -["COPY"]="©", -["iogon"]="į", -["nleftrightarrow"]="↮", -["reg"]="®", -["risingdotseq"]="≓", -["rightsquigarrow"]="↝", -["dtri"]="▿", -["Larr"]="↞", -["icy"]="и", -["gimel"]="ℷ", +["Ccirc"]="Ĉ", +["imagpart"]="ℑ", +["Equilibrium"]="⇌", +["dd"]="ⅆ", +["cuepr"]="⋞", +["Agrave"]="À", +["cuesc"]="⋟", +["square"]="□", +["notnivc"]="⋽", +["rbrksld"]="⦎", ["cup"]="∪", -["DJcy"]="Ђ", -["gtrapprox"]="⪆", -["rightharpoonup"]="⇀", -["prsim"]="≾", -["rightharpoondown"]="⇁", -["frac16"]="⅙", -["nbsp"]=" ", -["cedil"]="¸", -["numsp"]=" ", +["OpenCurlyDoubleQuote"]="“", +["Lacute"]="Ĺ", +["NewLine"]="\n", +["Sigma"]="Σ", +["Abreve"]="Ă", +["rightleftarrows"]="⇄", +["tint"]="∭", +["angmsdae"]="⦬", +["Leftrightarrow"]="⇔", +["rdquor"]="”", +["Verbar"]="‖", +["Gfr"]="𝔊", ["rightarrow"]="→", ["rhov"]="ϱ", -["rdquo"]="”", -["rharul"]="⥬", -["frac34"]="¾", -["ldrdhar"]="⥧", -["gnsim"]="⋧", -["rtriltri"]="⧎", -["emsp"]=" ", -["rlarr"]="⇄", -["Oslash"]="Ø", -["LeftDownTeeVector"]="⥡", +["Alpha"]="Α", +["rho"]="ρ", +["DownLeftTeeVector"]="⥞", +["ntriangleright"]="⋫", ["reg"]="®", -["LeftTeeVector"]="⥚", -["gsiml"]="⪐", -["QUOT"]="\"", -["blk14"]="░", -["rightrightarrows"]="⇉", -["NotGreaterGreater"]="≫̸", -["Xscr"]="𝒳", -["Re"]="ℜ", -["Ouml"]="Ö", +["Int"]="∬", +["agrave"]="à", +["Ecirc"]="Ê", +["NotReverseElement"]="∌", +["backprime"]="‵", +["Jfr"]="𝔍", ["rdsh"]="↳", -["and"]="∧", -["larrb"]="⇤", -["TripleDot"]="⃛", -["bcy"]="б", -["diams"]="♦", -["veebar"]="⊻", -["rcub"]="}", -["Scirc"]="Ŝ", -["ac"]="∾", -["rceil"]="⌉", -["rcedil"]="ŗ", -["cupbrcap"]="⩈", -["rcaron"]="ř", -["cuesc"]="⋟", -["HilbertSpace"]="ℋ", -["plus"]="+", -["rbrkslu"]="⦐", -["LessLess"]="⪡", -["macr"]="¯", -["notinE"]="⋹̸", +["jsercy"]="ј", +["Bscr"]="ℬ", +["boxH"]="═", +["leq"]="≤", +["NotPrecedes"]="⊀", +["coloneq"]="≔", +["boxvh"]="┼", +["larr"]="←", +["ncup"]="⩂", +["EmptySmallSquare"]="◻", +["vartriangleleft"]="⊲", +["iacute"]="í", +["nVDash"]="⊯", +["lap"]="⪅", +["precapprox"]="⪷", +["ecolon"]="≕", +["lsqb"]="[", ["rbarr"]="⤍", -["abreve"]="ă", -["sqcup"]="⊔", -["ratail"]="⤚", -["VeryThinSpace"]=" ", -["Uarrocir"]="⥉", -["rarrfs"]="⤞", -["Odblac"]="Ő", -["Imacr"]="Ī", -["ordm"]="º", -["odsold"]="⦼", -["NJcy"]="Њ", -["Yacute"]="Ý", -["rarrbfs"]="⤠", -["prop"]="∝", -["notin"]="∉", -["vsupne"]="⊋︀", -["bigcap"]="⋂", -["Phi"]="Φ", -["Ograve"]="Ò", -["Yscr"]="𝒴", -["approx"]="≈", -["racute"]="ŕ", -["ggg"]="⋙", -["lmoust"]="⎰", +["yacy"]="я", +["efDot"]="≒", +["ratio"]="∶", +["wreath"]="≀", +["nearhk"]="⤤", +["rarrpl"]="⥅", +["nvge"]="≥⃒", +["searrow"]="↘", +["thorn"]="þ", +["rdquo"]="”", +["LessFullEqual"]="≦", +["nvrtrie"]="⊵⃒", +["nsubseteqq"]="⫅̸", +["nedot"]="≐̸", +["VerticalLine"]="|", +["lsh"]="↰", +["DownRightVector"]="⇁", +["ecirc"]="ê", +["andv"]="⩚", +["trie"]="≜", +["race"]="∽̱", ["rBarr"]="⤏", -["gneqq"]="≩", -["isinv"]="∈", -["harr"]="↔", -["rArr"]="⇒", -["rAarr"]="⇛", -["quot"]="\"", +["lbrksld"]="⦏", +["emsp14"]=" ", +["RuleDelayed"]="⧴", +["vsubne"]="⊊︀", +["ShortLeftArrow"]="←", +["gtlPar"]="⦕", ["quest"]="?", -["iuml"]="ï", -["LeftRightArrow"]="↔", -["LeftDoubleBracket"]="⟦", -["gesdot"]="⪀", -["Ntilde"]="Ñ", -["boxUR"]="╚", -["exist"]="∃", -["subnE"]="⫋", -["ograve"]="ò", -["qopf"]="𝕢", -["Zacute"]="Ź", -["qfr"]="𝔮", -["dcy"]="д", -["NotReverseElement"]="∌", -["DotEqual"]="≐", -["VerticalLine"]="|", -["pscr"]="𝓅", -["prurel"]="⊰", -["cdot"]="ċ", -["bprime"]="‵", -["ltdot"]="⋖", -["prnsim"]="⋨", -["curlywedge"]="⋏", -["prnE"]="⪵", -["REG"]="®", -["sup3"]="³", +["dot"]="˙", +["RightVector"]="⇀", +["rppolint"]="⨒", +["Hat"]="^", +["zcy"]="з", +["suplarr"]="⥻", +["boxhU"]="╨", +["puncsp"]=" ", +["GreaterLess"]="≷", +["psi"]="ψ", +["ncaron"]="ň", +["SupersetEqual"]="⊇", +["propto"]="∝", ["preceq"]="⪯", -["LeftDownVectorBar"]="⥙", -["ForAll"]="∀", +["frac56"]="⅚", +["NotLessGreater"]="≸", +["jukcy"]="є", +["swArr"]="⇙", +["profline"]="⌒", +["prnE"]="⪵", +["larrpl"]="⤹", +["af"]="", +["profsurf"]="⌓", +["Larr"]="↞", ["forkv"]="⫙", -["varpi"]="ϖ", -["nrArr"]="⇏", +["prcue"]="≼", +["RightUpTeeVector"]="⥜", ["pound"]="£", +["ugrave"]="ù", +["popf"]="𝕡", +["frac78"]="⅞", +["suphsol"]="⟉", +["loplus"]="⨭", +["pointint"]="⨕", +["cupcap"]="⩆", +["LessTilde"]="≲", +["pm"]="±", +["blacksquare"]="▪", ["plustwo"]="⨧", -["shchcy"]="щ", -["cacute"]="ć", -["Dashv"]="⫤", -["lHar"]="⥢", -["NotLeftTriangle"]="⋪", -["gbreve"]="ğ", -["boxDl"]="╖", -["Beta"]="Β", -["ReverseEquilibrium"]="⇋", +["copy"]="©", +["plussim"]="⨦", +["Lcy"]="Л", +["pluse"]="⩲", +["ropar"]="⦆", +["OpenCurlyQuote"]="‘", ["plusacir"]="⨣", -["eqslantgtr"]="⪖", -["ecy"]="э", -["pitchfork"]="⋔", -["ctdot"]="⋯", -["odiv"]="⨸", -["lesseqqgtr"]="⪋", -["GreaterLess"]="≷", -["Ocy"]="О", -["weierp"]="℘", -["Uring"]="Ů", -["period"]=".", -["nis"]="⋼", -["cudarrr"]="⤵", -["boxHU"]="╩", -["Ouml"]="Ö", -["Assign"]="≔", -["Lscr"]="ℒ", -["parallel"]="∥", -["there4"]="∴", -["cupor"]="⩅", -["nwarr"]="↖", -["para"]="¶", -["ouml"]="ö", -["otimes"]="⊗", -["drbkarow"]="⤐", -["puncsp"]=" ", -["otilde"]="õ", -["Vvdash"]="⊪", -["osol"]="⊘", +["Exists"]="∃", +["piv"]="ϖ", +["phmmat"]="ℳ", +["angzarr"]="⍼", +["vsupnE"]="⫌︀", +["phi"]="φ", +["perp"]="⊥", +["rdldhar"]="⥩", +["ccirc"]="ĉ", +["Tfr"]="𝔗", +["Tcy"]="Т", +["circeq"]="≗", +["InvisibleTimes"]="", +["OElig"]="Œ", +["trisb"]="⧍", +["Rcaron"]="Ř", +["olt"]="⧀", +["UpArrow"]="↑", +["DDotrahd"]="⤑", +["Uarrocir"]="⥉", +["lacute"]="ĺ", ["oslash"]="ø", -["dd"]="ⅆ", -["NotPrecedesSlantEqual"]="⋠", -["orv"]="⩛", -["orslope"]="⩗", -["KJcy"]="Ќ", -["larrfs"]="⤝", -["orderof"]="ℴ", -["asympeq"]="≍", -["ord"]="⩝", -["NotLess"]="≮", -["NotGreater"]="≯", -["ThinSpace"]=" ", -["ominus"]="⊖", +["Uarr"]="↟", +["bsolb"]="⧅", +["gt"]=">", +["ordm"]="º", +["Itilde"]="Ĩ", +["ordm"]="º", +["Jscr"]="𝒥", +["Rarr"]="↠", +["measuredangle"]="∡", ["DoubleLongLeftRightArrow"]="⟺", -["lesseqgtr"]="⋚", +["lmidot"]="ŀ", +["period"]=".", ["operp"]="⦹", -["quaternions"]="ℍ", -["boxdR"]="╒", -["omega"]="ω", -["Kscr"]="𝒦", -["lessgtr"]="≶", -["utilde"]="ũ", -["olt"]="⧀", -["Rho"]="Ρ", -["olcir"]="⦾", -["oint"]="∮", +["bprime"]="‵", +["numsp"]=" ", +["ominus"]="⊖", +["nldr"]="‥", ["ograve"]="ò", -["Succeeds"]="≻", -["Igrave"]="Ì", -["eogon"]="ę", -["Uacute"]="Ú", -["bbrk"]="⎵", -["ocy"]="о", -["bcong"]="≌", +["omicron"]="ο", +["nearr"]="↗", +["jcirc"]="ĵ", +["nlE"]="≦̸", +["deg"]="°", +["frac13"]="⅓", +["tcy"]="т", +["lowbar"]="_", +["vfr"]="𝔳", +["ogon"]="˛", +["sim"]="∼", +["ocirc"]="ô", +["Icirc"]="Î", +["ocir"]="⊚", +["ZeroWidthSpace"]="", ["oast"]="⊛", +["smile"]="⌣", +["bigvee"]="⋁", ["oacute"]="ó", -["cir"]="○", -["angmsdab"]="⦩", -["Iscr"]="ℐ", -["ldquo"]="“", -["RightVector"]="⇀", -["euml"]="ë", -["between"]="≬", -["oS"]="Ⓢ", -["downdownarrows"]="⇊", -["nvlt"]="<⃒", -["lurdshar"]="⥊", -["starf"]="★", -["xlarr"]="⟵", -["Aring"]="Å", -["Xi"]="Ξ", -["gtcir"]="⩺", -["Nscr"]="𝒩", -["elinters"]="⏧", -["ntrianglelefteq"]="⋬", -["Vbar"]="⫫", +["Egrave"]="È", +["nwnear"]="⤧", +["nwarhk"]="⤣", +["rationals"]="ℚ", +["DiacriticalGrave"]="`", +["npreceq"]="⪯̸", +["rarrc"]="⤳", +["Uogon"]="Ų", +["InvisibleComma"]="", +["nvltrie"]="⊴⃒", +["digamma"]="ϝ", +["cirE"]="⧃", +["AMP"]="&", +["Theta"]="Θ", +["frac15"]="⅕", +["fnof"]="ƒ", +["icirc"]="î", +["Cdot"]="Ċ", +["ngsim"]="≵", +["sube"]="⊆", +["tcaron"]="ť", ["ntilde"]="ñ", -["andv"]="⩚", -["Rsh"]="↱", -["angmsdad"]="⦫", -["nsup"]="⊅", -["nbsp"]=" ", -["SquareSupersetEqual"]="⊒", -["ETH"]="Ð", -["nsube"]="⊈", -["DoubleContourIntegral"]="∯", -["nsubE"]="⫅̸", -["angmsdah"]="⦯", -["NotLessGreater"]="≸", -["awint"]="⨑", -["rtri"]="▹", -["nsc"]="⊁", -["boxvl"]="┤", -["beta"]="β", -["LessSlantEqual"]="⩽", -["supsub"]="⫔", -["agrave"]="à", -["LeftUpTeeVector"]="⥠", -["nrarrw"]="↝̸", -["ltcc"]="⪦", -["nrarrc"]="⤳̸", -["frac78"]="⅞", -["nvrArr"]="⤃", -["igrave"]="ì", -["barwed"]="⌅", -["Mscr"]="ℳ", -["nrarr"]="↛", -["SubsetEqual"]="⊆", -["notnivc"]="⋽", -["notnivb"]="⋾", -["succcurlyeq"]="≽", -["eth"]="ð", -["notinva"]="∉", -["Otilde"]="Õ", -["star"]="☆", -["rangd"]="⦒", -["Psi"]="Ψ", -["nsupe"]="⊉", -["mapstoup"]="↥", -["LJcy"]="Љ", -["looparrowleft"]="↫", -["nlarr"]="↚", -["Ll"]="⋘", -["ordf"]="ª", -["ni"]="∋", +["ufr"]="𝔲", +["Mcy"]="М", ["Delta"]="Δ", -["NotVerticalBar"]="∤", -["backcong"]="≌", -["NoBreak"]="", +["vnsup"]="⊃⃒", +["lAtail"]="⤛", +["DoubleUpArrow"]="⇑", +["aelig"]="æ", +["lscr"]="𝓁", +["Rscr"]="ℛ", +["capcup"]="⩇", +["npolint"]="⨔", +["gesdotol"]="⪄", +["uacute"]="ú", +["bumpE"]="⪮", +["drcorn"]="⌟", +["notnivb"]="⋾", +["notinvc"]="⋶", +["ovbar"]="⌽", +["Mopf"]="𝕄", +["NotGreater"]="≯", +["boxDr"]="╓", +["lrcorner"]="⌟", +["nleq"]="≰", +["nleftarrow"]="↚", +["nle"]="≰", +["boxvl"]="┤", +["Downarrow"]="⇓", +["hcirc"]="ĥ", ["nharr"]="↮", +["tfr"]="𝔱", +["nlt"]="≮", +["gacute"]="ǵ", +["colone"]="≔", +["Rang"]="⟫", ["ngt"]="≯", -["Pscr"]="𝒫", -["laemptyv"]="⦴", -["utrif"]="▴", -["ngeq"]="≱", -["ncedil"]="ņ", -["ncaron"]="ň", -["boxhu"]="┴", -["NotSquareSuperset"]="⊐̸", -["bull"]="•", +["ntilde"]="ñ", +["vcy"]="в", +["boxvL"]="╡", +["NotLeftTriangleBar"]="⧏̸", +["NestedGreaterGreater"]="≫", +["xmap"]="⟼", +["napid"]="≋̸", +["LeftTriangleBar"]="⧏", ["SquareSubsetEqual"]="⊑", -["VerticalSeparator"]="❘", -["natur"]="♮", -["ffr"]="𝔣", -["rdquor"]="”", -["lagran"]="ℒ", -["nRightarrow"]="⇏", -["nLtv"]="≪̸", -["nLl"]="⋘̸", -["Epsilon"]="Ε", -["csup"]="⫐", -["leftarrow"]="←", -["nGt"]="≫⃒", -["multimap"]="⊸", -["mstpos"]="∾", -["SuchThat"]="∋", -["mldr"]="…", -["midast"]="*", -["Sum"]="∑", -["vartheta"]="ϑ", -["mapsto"]="↦", -["nsupseteq"]="⊉", -["Oscr"]="𝒪", -["forall"]="∀", -["aring"]="å", -["ltimes"]="⋉", -["ltcir"]="⩹", -["lt"]="<", -["ii"]="ⅈ", -["equest"]="≟", -["scsim"]="≿", -["lsqb"]="[", -["lsimg"]="⪏", -["Egrave"]="È", -["Downarrow"]="⇓", -["lsim"]="≲", -["eqslantless"]="⪕", -["ucirc"]="û", -["Auml"]="Ä", -["lnsim"]="⋦", -["uplus"]="⊎", -["lhblk"]="▄", -["Ugrave"]="Ù", -["dlcrop"]="⌍", -["OElig"]="Œ", -["lesg"]="⋚︀", -["lesdotor"]="⪃", -["eg"]="⪚", -["lvertneqq"]="≨︀", -["leqslant"]="⩽", -["ccups"]="⩌", -["lceil"]="⌈", -["auml"]="ä", -["RightCeiling"]="⌉", -["lbrksld"]="⦏", -["lbarr"]="⤌", +["eacute"]="é", +["ne"]="≠", +["ncongdot"]="⩭̸", +["boxvR"]="╞", +["PrecedesEqual"]="⪯", +["Omacr"]="Ō", +["egs"]="⪖", +["ouml"]="ö", +["nbsp"]=" ", +["xuplus"]="⨄", +["napprox"]="≉", +["CloseCurlyQuote"]="’", +["nacute"]="ń", +["Chi"]="Χ", +["subsub"]="⫕", +["gescc"]="⪩", +["gcirc"]="ĝ", +["Lsh"]="↰", +["sfr"]="𝔰", +["circlearrowright"]="↻", +["rcaron"]="ř", +["ThinSpace"]=" ", +["otilde"]="õ", +["TRADE"]="™", +["cudarrl"]="⤸", ["lat"]="⪫", -["Rscr"]="ℛ", -["Zcy"]="З", -["bigwedge"]="⋀", -["Prime"]="″", +["nLeftrightarrow"]="⇎", +["leqq"]="≦", +["dagger"]="†", +["dstrok"]="đ", +["sacute"]="ś", +["Amacr"]="Ā", +["mumap"]="⊸", +["models"]="⊧", +["DiacriticalAcute"]="´", +["minusd"]="∸", +["hybull"]="⁃", +["ijlig"]="ij", +["fflig"]="ff", +["NotGreaterEqual"]="≱", +["Colon"]="∷", +["boxV"]="║", +["DotDot"]="⃜", +["mdash"]="—", +["hairsp"]=" ", +["bcong"]="≌", +["mcomma"]="⨩", +["cirfnint"]="⨐", +["TScy"]="Ц", +["luruhar"]="⥦", +["awconint"]="∳", +["nsim"]="≁", +["els"]="⪕", +["imof"]="⊷", +["lpar"]="(", +["lozenge"]="◊", +["oint"]="∮", +["Umacr"]="Ū", +["Sum"]="∑", +["lopar"]="⦅", +["racute"]="ŕ", +["LessEqualGreater"]="⋚", +["Wedge"]="⋀", +["bbrktbrk"]="⎶", +["RightDownTeeVector"]="⥝", +["HumpDownHump"]="≎", +["rarrb"]="⇥", +["longleftrightarrow"]="⟷", +["lnE"]="≨", +["NotGreaterSlantEqual"]="⩾̸", +["Nu"]="Ν", +["NotNestedLessLess"]="⪡̸", +["UpEquilibrium"]="⥮", +["boxhD"]="╥", +["el"]="⪙", ["boxDR"]="╔", -["scnsim"]="⋩", -["efDot"]="≒", -["kopf"]="𝕜", -["minusdu"]="⨪", -["kappav"]="ϰ", +["Gdot"]="Ġ", +["itilde"]="ĩ", +["isindot"]="⋵", +["GreaterEqualLess"]="⋛", +["Ll"]="⋘", +["nrarrw"]="↝̸", +["supnE"]="⫌", +["epsiv"]="ϵ", +["Element"]="∈", +["gtreqless"]="⋛", +["laemptyv"]="⦴", +["nGt"]="≫⃒", +["angsph"]="∢", +["lE"]="≦", +["kscr"]="𝓀", +["Ocy"]="О", +["LowerLeftArrow"]="↙", +["dopf"]="𝕕", +["iukcy"]="і", +["atilde"]="ã", +["DoubleRightArrow"]="⇒", +["oslash"]="ø", +["rarr"]="→", +["intprod"]="⨼", +["hookrightarrow"]="↪", +["intcal"]="⊺", +["infintie"]="⧝", +["escr"]="ℯ", +["iiota"]="℩", +["curren"]="¤", +["Bernoullis"]="ℬ", +["angmsdah"]="⦯", +["ShortRightArrow"]="→", +["boxUR"]="╚", +["ges"]="⩾", +["DoubleDownArrow"]="⇓", +["NotSquareSuperset"]="⊐̸", +["lsim"]="≲", +["bump"]="≎", +["curlyeqsucc"]="⋟", +["SHCHcy"]="Щ", +["frac45"]="⅘", +["qint"]="⨌", +["Precedes"]="≺", +["RightTeeArrow"]="↦", +["frac16"]="⅙", +["euro"]="€", +["smtes"]="⪬︀", +["eth"]="ð", +["ldquo"]="“", +["equivDD"]="⩸", +["Oopf"]="𝕆", ["boxVh"]="╫", -["Uuml"]="Ü", -["centerdot"]="·", -["isinsv"]="⋳", -["isins"]="⋴", -["bot"]="⊥", -["integers"]="ℤ", -["Racute"]="Ŕ", -["ijlig"]="ij", -["bernou"]="ℬ", -["Cconint"]="∰", -["doteq"]="≐", -["hookleftarrow"]="↩", -["epar"]="⋕", -["harrw"]="↭", -["dzigrarr"]="⟿", -["Zscr"]="𝒵", -["alpha"]="α", -["times"]="×", +["GreaterSlantEqual"]="⩾", +["KJcy"]="Ќ", ["egrave"]="è", -["UnionPlus"]="⊎", -["Qscr"]="𝒬", -["Omacr"]="Ō", -["Zfr"]="ℨ", -["gneq"]="⪈", -["divonx"]="⋇", -["gesdotol"]="⪄", -["gvertneqq"]="≩︀", -["geq"]="≥", -["utri"]="▵", -["ge"]="≥", -["vArr"]="⇕", -["Rrightarrow"]="⇛", -["frac25"]="⅖", -["LeftCeiling"]="⌈", -["GreaterGreater"]="⪢", -["LT"]="<", -["Longrightarrow"]="⟹", -["eqcolon"]="≕", -["ofcir"]="⦿", -["boxUl"]="╜", -["Wscr"]="𝒲", -["Sacute"]="Ś", -["Union"]="⋃", -["gg"]="≫", -["DownArrowBar"]="⤓", -["Escr"]="ℰ", -["ecirc"]="ê", -["Lmidot"]="Ŀ", -["Wedge"]="⋀", -["frown"]="⌢", -["ring"]="˚", -["dtdot"]="⋱", -["Ccedil"]="Ç", -["Conint"]="∯", -["dscr"]="𝒹", -["Map"]="⤅", -["EmptyVerySmallSquare"]="▫", -["backsim"]="∽", -["angzarr"]="⍼", -["dHar"]="⥥", -["Mfr"]="𝔐", -["cirE"]="⧃", -["Popf"]="ℙ", -["NestedGreaterGreater"]="≫", -["ReverseElement"]="∋", -["boxVr"]="╟", +["NotGreaterLess"]="≹", +["boxVl"]="╢", +["MediumSpace"]=" ", +["LongLeftArrow"]="⟵", +["uarr"]="↑", +["Ocirc"]="Ô", +["Idot"]="İ", +["bumpe"]="≏", +["nwArr"]="⇖", +["NegativeThinSpace"]="", +["coprod"]="∐", +["VerticalBar"]="∣", +["sccue"]="≽", +["boxDl"]="╖", +["NotSubset"]="⊂⃒", +["Edot"]="Ė", +["scy"]="с", +["Cup"]="⋓", +["centerdot"]="·", +["nsimeq"]="≄", +["DownLeftRightVector"]="⥐", } diff --git a/Master/texmf-dist/tex/luatex/luaxml/luaxml-transform.lua b/Master/texmf-dist/tex/luatex/luaxml/luaxml-transform.lua index b6841fea5fb..4b51eed1170 100644 --- a/Master/texmf-dist/tex/luatex/luaxml/luaxml-transform.lua +++ b/Master/texmf-dist/tex/luatex/luaxml/luaxml-transform.lua @@ -198,6 +198,10 @@ local function add_action(selector, template, parameters, csspar) end +local function reset_actions(selector, csspar) + local css = csspar or css + css:remove_selector(selector) +end --- Transform XML string @@ -243,7 +247,7 @@ end local Transformer --- Make new Transformer object --- @return Tranformer object +-- @return Transformer object local function new() local self = setmetatable({}, Transformer) self.css = cssquery() @@ -289,6 +293,12 @@ function Transformer:add_custom_action(selector, fn ) add_custom_action(selector, fn, self.css) end +--- Remove all actions that match the given selector +---@param selector string +function Transformer:reset_actions(selector) + reset_actions(selector, self.css) +end + -- all methods that use transformation functions must -- correctly handle the cssquery object that this library uses @@ -338,6 +348,7 @@ local M = { print_tex = print_tex, add_action = add_action, add_custom_action = add_custom_action, + reset_actions = reset_actions, simple_content = simple_content, load_file = load_file, process_dom = process_dom, diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown-parser.lua b/Master/texmf-dist/tex/luatex/markdown/markdown-parser.lua new file mode 100644 index 00000000000..2cdcf3ff759 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/markdown/markdown-parser.lua @@ -0,0 +1,8990 @@ +-- +-- Copyright (C) 2009-2016 John MacFarlane, Hans Hagen +-- +-- Permission is hereby granted, free of charge, to any person obtaining +-- a copy of this software and associated documentation files (the +-- "Software"), to deal in the Software without restriction, including +-- without limitation the rights to use, copy, modify, merge, publish, +-- distribute, sublicense, and/or sell copies of the Software, and to +-- permit persons to whom the Software is furnished to do so, subject to +-- the following conditions: +-- +-- The above copyright notice and this permission notice shall be +-- included in all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-- MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT. +-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-- +-- Copyright (C) 2016-2024 Vít Starý Novotný, Andrej Genčur +-- +-- This work may be distributed and/or modified under the +-- conditions of the LaTeX Project Public License, either version 1.3 +-- of this license or (at your option) any later version. +-- The latest version of this license is in +-- +-- http://www.latex-project.org/lppl.txt +-- +-- and version 1.3 or later is part of all distributions of LaTeX +-- version 2005/12/01 or later. +-- +-- This work has the LPPL maintenance status `maintained'. +-- The Current Maintainer of this work is Vít Starý Novotný. +-- +-- Send bug reports, requests for additions and questions +-- either to the GitHub issue tracker at +-- +-- https://github.com/witiko/markdown/issues +-- +-- or to the e-mail address <witiko@mail.muni.cz>. +-- +-- MODIFICATION ADVICE: +-- +-- If you want to customize this file, it is best to make a copy of +-- the source file(s) from which it was produced. Use a different +-- name for your copy(ies) and modify the copy(ies); this will ensure +-- that your modifications do not get overwritten when you install a +-- new release of the standard system. You should also ensure that +-- your modified source file does not generate any modified file with +-- the same name as a standard file. +-- +-- You will also need to produce your own, suitably named, .ins file to +-- control the generation of files from your source file; this file +-- should contain your own preambles for the files it generates, not +-- those in the standard .ins files. +-- +local metadata = { + version = "3.7.0-0-g98dece19", + comment = "A module for the conversion from markdown " + .. "to plain TeX", + author = "John MacFarlane, Hans Hagen, Vít Starý Novotný, " + .. "Andrej Genčur", + copyright = {"2009-2016 John MacFarlane, Hans Hagen", + "2016-2024 Vít Starý Novotný, Andrej Genčur"}, + license = "LPPL 1.3c" +} + +if not modules then modules = { } end +modules['markdown'] = metadata +local lpeg = require("lpeg") +local unicode = require("unicode") +local md5 = require("md5"); +(function() + local should_initialize = package.loaded.kpse == nil + or tex.initialize ~= nil + kpse = require("kpse") + if should_initialize then + kpse.set_program_name("luatex") + end +end)() +local uni_algos = require("lua-uni-algos") +local M = {metadata = metadata} +local walkable_syntax = { + Block = { + "Blockquote", + "Verbatim", + "ThematicBreak", + "BulletList", + "OrderedList", + "DisplayHtml", + "Heading", + }, + BlockOrParagraph = { + "Block", + "Paragraph", + "Plain", + }, + Inline = { + "Str", + "Space", + "Endline", + "EndlineBreak", + "LinkAndEmph", + "Code", + "AutoLinkUrl", + "AutoLinkEmail", + "AutoLinkRelativeReference", + "InlineHtml", + "HtmlEntity", + "EscapedChar", + "Smart", + "Symbol", + }, +} +local defaultOptions = {} +defaultOptions.eagerCache = true +defaultOptions.singletonCache = true +local singletonCache = { + convert = nil, + options = nil, +} +defaultOptions.unicodeNormalization = true +defaultOptions.unicodeNormalizationForm = "nfc" +defaultOptions.cacheDir = "." +defaultOptions.contentBlocksLanguageMap = "markdown-languages.json" +defaultOptions.debugExtensionsFileName = "debug-extensions.json" +defaultOptions.frozenCacheFileName = "frozenCache.tex" +defaultOptions.autoIdentifiers = false +defaultOptions.blankBeforeBlockquote = false +defaultOptions.blankBeforeCodeFence = false +defaultOptions.blankBeforeDivFence = false +defaultOptions.blankBeforeHeading = false +defaultOptions.blankBeforeList = false +defaultOptions.bracketedSpans = false +defaultOptions.breakableBlockquotes = true +defaultOptions.citationNbsps = true +defaultOptions.citations = false +defaultOptions.codeSpans = true +defaultOptions.contentBlocks = false +defaultOptions.contentLevel = "block" +defaultOptions.debugExtensions = false +defaultOptions.definitionLists = false +defaultOptions.ensureJekyllData = false +defaultOptions.expectJekyllData = false +metadata.user_extension_api_version = 2 +metadata.grammar_version = 4 +defaultOptions.extensions = {} +defaultOptions.fancyLists = false +defaultOptions.fencedCode = true +defaultOptions.fencedCodeAttributes = false +defaultOptions.fencedDivs = false +defaultOptions.finalizeCache = false +defaultOptions.frozenCacheCounter = 0 +defaultOptions.gfmAutoIdentifiers = false +defaultOptions.hashEnumerators = false +defaultOptions.headerAttributes = false +defaultOptions.html = true +defaultOptions.hybrid = false +defaultOptions.inlineCodeAttributes = false +defaultOptions.inlineNotes = false +defaultOptions.jekyllData = false +defaultOptions.linkAttributes = false +defaultOptions.lineBlocks = false +defaultOptions.mark = false +defaultOptions.notes = false +defaultOptions.pipeTables = false +defaultOptions.preserveTabs = true +defaultOptions.rawAttribute = false +defaultOptions.relativeReferences = false +defaultOptions.shiftHeadings = 0 +defaultOptions.slice = "^ $" +defaultOptions.smartEllipses = false +defaultOptions.startNumber = true +defaultOptions.strikeThrough = false +defaultOptions.stripIndent = false +defaultOptions.subscripts = false +defaultOptions.superscripts = false +defaultOptions.tableAttributes = false +defaultOptions.tableCaptions = false +defaultOptions.taskLists = false +defaultOptions.texComments = false +defaultOptions.texMathDollars = false +defaultOptions.texMathDoubleBackslash = false +defaultOptions.texMathSingleBackslash = false +defaultOptions.tightLists = true +defaultOptions.underscores = true +local upper, format, length = + string.upper, string.format, string.len +local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, Cp, any = + lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cg, lpeg.Cb, + lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs, lpeg.Cp, lpeg.P(1) +local util = {} +function util.err(msg, exit_code) + io.stderr:write("markdown.lua: " .. msg .. "\n") + os.exit(exit_code or 1) +end +function util.cache(dir, string, salt, transform, suffix) + local digest = md5.sumhexa(string .. (salt or "")) + local name = util.pathname(dir, digest .. suffix) + local file = io.open(name, "r") + if file == nil then -- If no cache entry exists, create a new one. + file = assert(io.open(name, "w"), + [[Could not open file "]] .. name .. [[" for writing]]) + local result = string + if transform ~= nil then + result = transform(result) + end + assert(file:write(result)) + assert(file:close()) + end + return name +end +function util.cache_verbatim(dir, string) + local name = util.cache(dir, string, nil, nil, ".verbatim") + return name +end +function util.table_copy(t) + local u = { } + for k, v in pairs(t) do u[k] = v end + return setmetatable(u, getmetatable(t)) +end +function util.encode_json_string(s) + s = s:gsub([[\]], [[\\]]) + s = s:gsub([["]], [[\"]]) + return [["]] .. s .. [["]] +end +function util.expand_tabs_in_line(s, tabstop) + local tab = tabstop or 4 + local corr = 0 + return (s:gsub("()\t", function(p) + local sp = tab - (p - 1 + corr) % tab + corr = corr - 1 + sp + return string.rep(" ", sp) + end)) +end +function util.walk(t, f) + local typ = type(t) + if typ == "string" then + f(t) + elseif typ == "table" then + local i = 1 + local n + n = t[i] + while n do + util.walk(n, f) + i = i + 1 + n = t[i] + end + elseif typ == "function" then + local ok, val = pcall(t) + if ok then + util.walk(val,f) + end + else + f(tostring(t)) + end +end +function util.flatten(ary) + local new = {} + for _,v in ipairs(ary) do + if type(v) == "table" then + for _,w in ipairs(util.flatten(v)) do + new[#new + 1] = w + end + else + new[#new + 1] = v + end + end + return new +end +function util.rope_to_string(rope) + local buffer = {} + util.walk(rope, function(x) buffer[#buffer + 1] = x end) + return table.concat(buffer) +end +function util.rope_last(rope) + if #rope == 0 then + return nil + else + local l = rope[#rope] + if type(l) == "table" then + return util.rope_last(l) + else + return l + end + end +end +function util.intersperse(ary, x) + local new = {} + local l = #ary + for i,v in ipairs(ary) do + local n = #new + new[n + 1] = v + if i ~= l then + new[n + 2] = x + end + end + return new +end +function util.map(ary, f) + local new = {} + for i,v in ipairs(ary) do + new[i] = f(v) + end + return new +end +function util.escaper(char_escapes, string_escapes) + local char_escapes_list = "" + for i,_ in pairs(char_escapes) do + char_escapes_list = char_escapes_list .. i + end + local escapable = S(char_escapes_list) / char_escapes + if string_escapes then + for k,v in pairs(string_escapes) do + escapable = P(k) / v + escapable + end + end + local escape_string = Cs((escapable + any)^0) + return function(s) + return lpeg.match(escape_string, s) + end +end +function util.pathname(dir, file) + if #dir == 0 then + return file + else + return dir .. "/" .. file + end +end +function util.salt(options) + local opt_string = {} + for k, _ in pairs(defaultOptions) do + local v = options[k] + if type(v) == "table" then + for _, i in ipairs(v) do + opt_string[#opt_string+1] = k .. "=" .. tostring(i) + end + elseif k ~= "cacheDir" then + opt_string[#opt_string+1] = k .. "=" .. tostring(v) + end + end + table.sort(opt_string) + local salt = table.concat(opt_string, ",") + .. "," .. metadata.version + return salt +end +local entities = {} + +local character_entities = { + ["Tab"] = 9, + ["NewLine"] = 10, + ["excl"] = 33, + ["QUOT"] = 34, + ["quot"] = 34, + ["num"] = 35, + ["dollar"] = 36, + ["percnt"] = 37, + ["AMP"] = 38, + ["amp"] = 38, + ["apos"] = 39, + ["lpar"] = 40, + ["rpar"] = 41, + ["ast"] = 42, + ["midast"] = 42, + ["plus"] = 43, + ["comma"] = 44, + ["period"] = 46, + ["sol"] = 47, + ["colon"] = 58, + ["semi"] = 59, + ["LT"] = 60, + ["lt"] = 60, + ["nvlt"] = {60, 8402}, + ["bne"] = {61, 8421}, + ["equals"] = 61, + ["GT"] = 62, + ["gt"] = 62, + ["nvgt"] = {62, 8402}, + ["quest"] = 63, + ["commat"] = 64, + ["lbrack"] = 91, + ["lsqb"] = 91, + ["bsol"] = 92, + ["rbrack"] = 93, + ["rsqb"] = 93, + ["Hat"] = 94, + ["UnderBar"] = 95, + ["lowbar"] = 95, + ["DiacriticalGrave"] = 96, + ["grave"] = 96, + ["fjlig"] = {102, 106}, + ["lbrace"] = 123, + ["lcub"] = 123, + ["VerticalLine"] = 124, + ["verbar"] = 124, + ["vert"] = 124, + ["rbrace"] = 125, + ["rcub"] = 125, + ["NonBreakingSpace"] = 160, + ["nbsp"] = 160, + ["iexcl"] = 161, + ["cent"] = 162, + ["pound"] = 163, + ["curren"] = 164, + ["yen"] = 165, + ["brvbar"] = 166, + ["sect"] = 167, + ["Dot"] = 168, + ["DoubleDot"] = 168, + ["die"] = 168, + ["uml"] = 168, + ["COPY"] = 169, + ["copy"] = 169, + ["ordf"] = 170, + ["laquo"] = 171, + ["not"] = 172, + ["shy"] = 173, + ["REG"] = 174, + ["circledR"] = 174, + ["reg"] = 174, + ["macr"] = 175, + ["strns"] = 175, + ["deg"] = 176, + ["PlusMinus"] = 177, + ["plusmn"] = 177, + ["pm"] = 177, + ["sup2"] = 178, + ["sup3"] = 179, + ["DiacriticalAcute"] = 180, + ["acute"] = 180, + ["micro"] = 181, + ["para"] = 182, + ["CenterDot"] = 183, + ["centerdot"] = 183, + ["middot"] = 183, + ["Cedilla"] = 184, + ["cedil"] = 184, + ["sup1"] = 185, + ["ordm"] = 186, + ["raquo"] = 187, + ["frac14"] = 188, + ["frac12"] = 189, + ["half"] = 189, + ["frac34"] = 190, + ["iquest"] = 191, + ["Agrave"] = 192, + ["Aacute"] = 193, + ["Acirc"] = 194, + ["Atilde"] = 195, + ["Auml"] = 196, + ["Aring"] = 197, + ["angst"] = 197, + ["AElig"] = 198, + ["Ccedil"] = 199, + ["Egrave"] = 200, + ["Eacute"] = 201, + ["Ecirc"] = 202, + ["Euml"] = 203, + ["Igrave"] = 204, + ["Iacute"] = 205, + ["Icirc"] = 206, + ["Iuml"] = 207, + ["ETH"] = 208, + ["Ntilde"] = 209, + ["Ograve"] = 210, + ["Oacute"] = 211, + ["Ocirc"] = 212, + ["Otilde"] = 213, + ["Ouml"] = 214, + ["times"] = 215, + ["Oslash"] = 216, + ["Ugrave"] = 217, + ["Uacute"] = 218, + ["Ucirc"] = 219, + ["Uuml"] = 220, + ["Yacute"] = 221, + ["THORN"] = 222, + ["szlig"] = 223, + ["agrave"] = 224, + ["aacute"] = 225, + ["acirc"] = 226, + ["atilde"] = 227, + ["auml"] = 228, + ["aring"] = 229, + ["aelig"] = 230, + ["ccedil"] = 231, + ["egrave"] = 232, + ["eacute"] = 233, + ["ecirc"] = 234, + ["euml"] = 235, + ["igrave"] = 236, + ["iacute"] = 237, + ["icirc"] = 238, + ["iuml"] = 239, + ["eth"] = 240, + ["ntilde"] = 241, + ["ograve"] = 242, + ["oacute"] = 243, + ["ocirc"] = 244, + ["otilde"] = 245, + ["ouml"] = 246, + ["div"] = 247, + ["divide"] = 247, + ["oslash"] = 248, + ["ugrave"] = 249, + ["uacute"] = 250, + ["ucirc"] = 251, + ["uuml"] = 252, + ["yacute"] = 253, + ["thorn"] = 254, + ["yuml"] = 255, + ["Amacr"] = 256, + ["amacr"] = 257, + ["Abreve"] = 258, + ["abreve"] = 259, + ["Aogon"] = 260, + ["aogon"] = 261, + ["Cacute"] = 262, + ["cacute"] = 263, + ["Ccirc"] = 264, + ["ccirc"] = 265, + ["Cdot"] = 266, + ["cdot"] = 267, + ["Ccaron"] = 268, + ["ccaron"] = 269, + ["Dcaron"] = 270, + ["dcaron"] = 271, + ["Dstrok"] = 272, + ["dstrok"] = 273, + ["Emacr"] = 274, + ["emacr"] = 275, + ["Edot"] = 278, + ["edot"] = 279, + ["Eogon"] = 280, + ["eogon"] = 281, + ["Ecaron"] = 282, + ["ecaron"] = 283, + ["Gcirc"] = 284, + ["gcirc"] = 285, + ["Gbreve"] = 286, + ["gbreve"] = 287, + ["Gdot"] = 288, + ["gdot"] = 289, + ["Gcedil"] = 290, + ["Hcirc"] = 292, + ["hcirc"] = 293, + ["Hstrok"] = 294, + ["hstrok"] = 295, + ["Itilde"] = 296, + ["itilde"] = 297, + ["Imacr"] = 298, + ["imacr"] = 299, + ["Iogon"] = 302, + ["iogon"] = 303, + ["Idot"] = 304, + ["imath"] = 305, + ["inodot"] = 305, + ["IJlig"] = 306, + ["ijlig"] = 307, + ["Jcirc"] = 308, + ["jcirc"] = 309, + ["Kcedil"] = 310, + ["kcedil"] = 311, + ["kgreen"] = 312, + ["Lacute"] = 313, + ["lacute"] = 314, + ["Lcedil"] = 315, + ["lcedil"] = 316, + ["Lcaron"] = 317, + ["lcaron"] = 318, + ["Lmidot"] = 319, + ["lmidot"] = 320, + ["Lstrok"] = 321, + ["lstrok"] = 322, + ["Nacute"] = 323, + ["nacute"] = 324, + ["Ncedil"] = 325, + ["ncedil"] = 326, + ["Ncaron"] = 327, + ["ncaron"] = 328, + ["napos"] = 329, + ["ENG"] = 330, + ["eng"] = 331, + ["Omacr"] = 332, + ["omacr"] = 333, + ["Odblac"] = 336, + ["odblac"] = 337, + ["OElig"] = 338, + ["oelig"] = 339, + ["Racute"] = 340, + ["racute"] = 341, + ["Rcedil"] = 342, + ["rcedil"] = 343, + ["Rcaron"] = 344, + ["rcaron"] = 345, + ["Sacute"] = 346, + ["sacute"] = 347, + ["Scirc"] = 348, + ["scirc"] = 349, + ["Scedil"] = 350, + ["scedil"] = 351, + ["Scaron"] = 352, + ["scaron"] = 353, + ["Tcedil"] = 354, + ["tcedil"] = 355, + ["Tcaron"] = 356, + ["tcaron"] = 357, + ["Tstrok"] = 358, + ["tstrok"] = 359, + ["Utilde"] = 360, + ["utilde"] = 361, + ["Umacr"] = 362, + ["umacr"] = 363, + ["Ubreve"] = 364, + ["ubreve"] = 365, + ["Uring"] = 366, + ["uring"] = 367, + ["Udblac"] = 368, + ["udblac"] = 369, + ["Uogon"] = 370, + ["uogon"] = 371, + ["Wcirc"] = 372, + ["wcirc"] = 373, + ["Ycirc"] = 374, + ["ycirc"] = 375, + ["Yuml"] = 376, + ["Zacute"] = 377, + ["zacute"] = 378, + ["Zdot"] = 379, + ["zdot"] = 380, + ["Zcaron"] = 381, + ["zcaron"] = 382, + ["fnof"] = 402, + ["imped"] = 437, + ["gacute"] = 501, + ["jmath"] = 567, + ["circ"] = 710, + ["Hacek"] = 711, + ["caron"] = 711, + ["Breve"] = 728, + ["breve"] = 728, + ["DiacriticalDot"] = 729, + ["dot"] = 729, + ["ring"] = 730, + ["ogon"] = 731, + ["DiacriticalTilde"] = 732, + ["tilde"] = 732, + ["DiacriticalDoubleAcute"] = 733, + ["dblac"] = 733, + ["DownBreve"] = 785, + ["Alpha"] = 913, + ["Beta"] = 914, + ["Gamma"] = 915, + ["Delta"] = 916, + ["Epsilon"] = 917, + ["Zeta"] = 918, + ["Eta"] = 919, + ["Theta"] = 920, + ["Iota"] = 921, + ["Kappa"] = 922, + ["Lambda"] = 923, + ["Mu"] = 924, + ["Nu"] = 925, + ["Xi"] = 926, + ["Omicron"] = 927, + ["Pi"] = 928, + ["Rho"] = 929, + ["Sigma"] = 931, + ["Tau"] = 932, + ["Upsilon"] = 933, + ["Phi"] = 934, + ["Chi"] = 935, + ["Psi"] = 936, + ["Omega"] = 937, + ["ohm"] = 937, + ["alpha"] = 945, + ["beta"] = 946, + ["gamma"] = 947, + ["delta"] = 948, + ["epsi"] = 949, + ["epsilon"] = 949, + ["zeta"] = 950, + ["eta"] = 951, + ["theta"] = 952, + ["iota"] = 953, + ["kappa"] = 954, + ["lambda"] = 955, + ["mu"] = 956, + ["nu"] = 957, + ["xi"] = 958, + ["omicron"] = 959, + ["pi"] = 960, + ["rho"] = 961, + ["sigmaf"] = 962, + ["sigmav"] = 962, + ["varsigma"] = 962, + ["sigma"] = 963, + ["tau"] = 964, + ["upsi"] = 965, + ["upsilon"] = 965, + ["phi"] = 966, + ["chi"] = 967, + ["psi"] = 968, + ["omega"] = 969, + ["thetasym"] = 977, + ["thetav"] = 977, + ["vartheta"] = 977, + ["Upsi"] = 978, + ["upsih"] = 978, + ["phiv"] = 981, + ["straightphi"] = 981, + ["varphi"] = 981, + ["piv"] = 982, + ["varpi"] = 982, + ["Gammad"] = 988, + ["digamma"] = 989, + ["gammad"] = 989, + ["kappav"] = 1008, + ["varkappa"] = 1008, + ["rhov"] = 1009, + ["varrho"] = 1009, + ["epsiv"] = 1013, + ["straightepsilon"] = 1013, + ["varepsilon"] = 1013, + ["backepsilon"] = 1014, + ["bepsi"] = 1014, + ["IOcy"] = 1025, + ["DJcy"] = 1026, + ["GJcy"] = 1027, + ["Jukcy"] = 1028, + ["DScy"] = 1029, + ["Iukcy"] = 1030, + ["YIcy"] = 1031, + ["Jsercy"] = 1032, + ["LJcy"] = 1033, + ["NJcy"] = 1034, + ["TSHcy"] = 1035, + ["KJcy"] = 1036, + ["Ubrcy"] = 1038, + ["DZcy"] = 1039, + ["Acy"] = 1040, + ["Bcy"] = 1041, + ["Vcy"] = 1042, + ["Gcy"] = 1043, + ["Dcy"] = 1044, + ["IEcy"] = 1045, + ["ZHcy"] = 1046, + ["Zcy"] = 1047, + ["Icy"] = 1048, + ["Jcy"] = 1049, + ["Kcy"] = 1050, + ["Lcy"] = 1051, + ["Mcy"] = 1052, + ["Ncy"] = 1053, + ["Ocy"] = 1054, + ["Pcy"] = 1055, + ["Rcy"] = 1056, + ["Scy"] = 1057, + ["Tcy"] = 1058, + ["Ucy"] = 1059, + ["Fcy"] = 1060, + ["KHcy"] = 1061, + ["TScy"] = 1062, + ["CHcy"] = 1063, + ["SHcy"] = 1064, + ["SHCHcy"] = 1065, + ["HARDcy"] = 1066, + ["Ycy"] = 1067, + ["SOFTcy"] = 1068, + ["Ecy"] = 1069, + ["YUcy"] = 1070, + ["YAcy"] = 1071, + ["acy"] = 1072, + ["bcy"] = 1073, + ["vcy"] = 1074, + ["gcy"] = 1075, + ["dcy"] = 1076, + ["iecy"] = 1077, + ["zhcy"] = 1078, + ["zcy"] = 1079, + ["icy"] = 1080, + ["jcy"] = 1081, + ["kcy"] = 1082, + ["lcy"] = 1083, + ["mcy"] = 1084, + ["ncy"] = 1085, + ["ocy"] = 1086, + ["pcy"] = 1087, + ["rcy"] = 1088, + ["scy"] = 1089, + ["tcy"] = 1090, + ["ucy"] = 1091, + ["fcy"] = 1092, + ["khcy"] = 1093, + ["tscy"] = 1094, + ["chcy"] = 1095, + ["shcy"] = 1096, + ["shchcy"] = 1097, + ["hardcy"] = 1098, + ["ycy"] = 1099, + ["softcy"] = 1100, + ["ecy"] = 1101, + ["yucy"] = 1102, + ["yacy"] = 1103, + ["iocy"] = 1105, + ["djcy"] = 1106, + ["gjcy"] = 1107, + ["jukcy"] = 1108, + ["dscy"] = 1109, + ["iukcy"] = 1110, + ["yicy"] = 1111, + ["jsercy"] = 1112, + ["ljcy"] = 1113, + ["njcy"] = 1114, + ["tshcy"] = 1115, + ["kjcy"] = 1116, + ["ubrcy"] = 1118, + ["dzcy"] = 1119, + ["ensp"] = 8194, + ["emsp"] = 8195, + ["emsp13"] = 8196, + ["emsp14"] = 8197, + ["numsp"] = 8199, + ["puncsp"] = 8200, + ["ThinSpace"] = 8201, + ["thinsp"] = 8201, + ["VeryThinSpace"] = 8202, + ["hairsp"] = 8202, + ["NegativeMediumSpace"] = 8203, + ["NegativeThickSpace"] = 8203, + ["NegativeThinSpace"] = 8203, + ["NegativeVeryThinSpace"] = 8203, + ["ZeroWidthSpace"] = 8203, + ["zwnj"] = 8204, + ["zwj"] = 8205, + ["lrm"] = 8206, + ["rlm"] = 8207, + ["dash"] = 8208, + ["hyphen"] = 8208, + ["ndash"] = 8211, + ["mdash"] = 8212, + ["horbar"] = 8213, + ["Verbar"] = 8214, + ["Vert"] = 8214, + ["OpenCurlyQuote"] = 8216, + ["lsquo"] = 8216, + ["CloseCurlyQuote"] = 8217, + ["rsquo"] = 8217, + ["rsquor"] = 8217, + ["lsquor"] = 8218, + ["sbquo"] = 8218, + ["OpenCurlyDoubleQuote"] = 8220, + ["ldquo"] = 8220, + ["CloseCurlyDoubleQuote"] = 8221, + ["rdquo"] = 8221, + ["rdquor"] = 8221, + ["bdquo"] = 8222, + ["ldquor"] = 8222, + ["dagger"] = 8224, + ["Dagger"] = 8225, + ["ddagger"] = 8225, + ["bull"] = 8226, + ["bullet"] = 8226, + ["nldr"] = 8229, + ["hellip"] = 8230, + ["mldr"] = 8230, + ["permil"] = 8240, + ["pertenk"] = 8241, + ["prime"] = 8242, + ["Prime"] = 8243, + ["tprime"] = 8244, + ["backprime"] = 8245, + ["bprime"] = 8245, + ["lsaquo"] = 8249, + ["rsaquo"] = 8250, + ["OverBar"] = 8254, + ["oline"] = 8254, + ["caret"] = 8257, + ["hybull"] = 8259, + ["frasl"] = 8260, + ["bsemi"] = 8271, + ["qprime"] = 8279, + ["MediumSpace"] = 8287, + ["ThickSpace"] = {8287, 8202}, + ["NoBreak"] = 8288, + ["ApplyFunction"] = 8289, + ["af"] = 8289, + ["InvisibleTimes"] = 8290, + ["it"] = 8290, + ["InvisibleComma"] = 8291, + ["ic"] = 8291, + ["euro"] = 8364, + ["TripleDot"] = 8411, + ["tdot"] = 8411, + ["DotDot"] = 8412, + ["Copf"] = 8450, + ["complexes"] = 8450, + ["incare"] = 8453, + ["gscr"] = 8458, + ["HilbertSpace"] = 8459, + ["Hscr"] = 8459, + ["hamilt"] = 8459, + ["Hfr"] = 8460, + ["Poincareplane"] = 8460, + ["Hopf"] = 8461, + ["quaternions"] = 8461, + ["planckh"] = 8462, + ["hbar"] = 8463, + ["hslash"] = 8463, + ["planck"] = 8463, + ["plankv"] = 8463, + ["Iscr"] = 8464, + ["imagline"] = 8464, + ["Ifr"] = 8465, + ["Im"] = 8465, + ["image"] = 8465, + ["imagpart"] = 8465, + ["Laplacetrf"] = 8466, + ["Lscr"] = 8466, + ["lagran"] = 8466, + ["ell"] = 8467, + ["Nopf"] = 8469, + ["naturals"] = 8469, + ["numero"] = 8470, + ["copysr"] = 8471, + ["weierp"] = 8472, + ["wp"] = 8472, + ["Popf"] = 8473, + ["primes"] = 8473, + ["Qopf"] = 8474, + ["rationals"] = 8474, + ["Rscr"] = 8475, + ["realine"] = 8475, + ["Re"] = 8476, + ["Rfr"] = 8476, + ["real"] = 8476, + ["realpart"] = 8476, + ["Ropf"] = 8477, + ["reals"] = 8477, + ["rx"] = 8478, + ["TRADE"] = 8482, + ["trade"] = 8482, + ["Zopf"] = 8484, + ["integers"] = 8484, + ["mho"] = 8487, + ["Zfr"] = 8488, + ["zeetrf"] = 8488, + ["iiota"] = 8489, + ["Bernoullis"] = 8492, + ["Bscr"] = 8492, + ["bernou"] = 8492, + ["Cayleys"] = 8493, + ["Cfr"] = 8493, + ["escr"] = 8495, + ["Escr"] = 8496, + ["expectation"] = 8496, + ["Fouriertrf"] = 8497, + ["Fscr"] = 8497, + ["Mellintrf"] = 8499, + ["Mscr"] = 8499, + ["phmmat"] = 8499, + ["order"] = 8500, + ["orderof"] = 8500, + ["oscr"] = 8500, + ["alefsym"] = 8501, + ["aleph"] = 8501, + ["beth"] = 8502, + ["gimel"] = 8503, + ["daleth"] = 8504, + ["CapitalDifferentialD"] = 8517, + ["DD"] = 8517, + ["DifferentialD"] = 8518, + ["dd"] = 8518, + ["ExponentialE"] = 8519, + ["ee"] = 8519, + ["exponentiale"] = 8519, + ["ImaginaryI"] = 8520, + ["ii"] = 8520, + ["frac13"] = 8531, + ["frac23"] = 8532, + ["frac15"] = 8533, + ["frac25"] = 8534, + ["frac35"] = 8535, + ["frac45"] = 8536, + ["frac16"] = 8537, + ["frac56"] = 8538, + ["frac18"] = 8539, + ["frac38"] = 8540, + ["frac58"] = 8541, + ["frac78"] = 8542, + ["LeftArrow"] = 8592, + ["ShortLeftArrow"] = 8592, + ["larr"] = 8592, + ["leftarrow"] = 8592, + ["slarr"] = 8592, + ["ShortUpArrow"] = 8593, + ["UpArrow"] = 8593, + ["uarr"] = 8593, + ["uparrow"] = 8593, + ["RightArrow"] = 8594, + ["ShortRightArrow"] = 8594, + ["rarr"] = 8594, + ["rightarrow"] = 8594, + ["srarr"] = 8594, + ["DownArrow"] = 8595, + ["ShortDownArrow"] = 8595, + ["darr"] = 8595, + ["downarrow"] = 8595, + ["LeftRightArrow"] = 8596, + ["harr"] = 8596, + ["leftrightarrow"] = 8596, + ["UpDownArrow"] = 8597, + ["updownarrow"] = 8597, + ["varr"] = 8597, + ["UpperLeftArrow"] = 8598, + ["nwarr"] = 8598, + ["nwarrow"] = 8598, + ["UpperRightArrow"] = 8599, + ["nearr"] = 8599, + ["nearrow"] = 8599, + ["LowerRightArrow"] = 8600, + ["searr"] = 8600, + ["searrow"] = 8600, + ["LowerLeftArrow"] = 8601, + ["swarr"] = 8601, + ["swarrow"] = 8601, + ["nlarr"] = 8602, + ["nleftarrow"] = 8602, + ["nrarr"] = 8603, + ["nrightarrow"] = 8603, + ["nrarrw"] = {8605, 824}, + ["rarrw"] = 8605, + ["rightsquigarrow"] = 8605, + ["Larr"] = 8606, + ["twoheadleftarrow"] = 8606, + ["Uarr"] = 8607, + ["Rarr"] = 8608, + ["twoheadrightarrow"] = 8608, + ["Darr"] = 8609, + ["larrtl"] = 8610, + ["leftarrowtail"] = 8610, + ["rarrtl"] = 8611, + ["rightarrowtail"] = 8611, + ["LeftTeeArrow"] = 8612, + ["mapstoleft"] = 8612, + ["UpTeeArrow"] = 8613, + ["mapstoup"] = 8613, + ["RightTeeArrow"] = 8614, + ["map"] = 8614, + ["mapsto"] = 8614, + ["DownTeeArrow"] = 8615, + ["mapstodown"] = 8615, + ["hookleftarrow"] = 8617, + ["larrhk"] = 8617, + ["hookrightarrow"] = 8618, + ["rarrhk"] = 8618, + ["larrlp"] = 8619, + ["looparrowleft"] = 8619, + ["looparrowright"] = 8620, + ["rarrlp"] = 8620, + ["harrw"] = 8621, + ["leftrightsquigarrow"] = 8621, + ["nharr"] = 8622, + ["nleftrightarrow"] = 8622, + ["Lsh"] = 8624, + ["lsh"] = 8624, + ["Rsh"] = 8625, + ["rsh"] = 8625, + ["ldsh"] = 8626, + ["rdsh"] = 8627, + ["crarr"] = 8629, + ["cularr"] = 8630, + ["curvearrowleft"] = 8630, + ["curarr"] = 8631, + ["curvearrowright"] = 8631, + ["circlearrowleft"] = 8634, + ["olarr"] = 8634, + ["circlearrowright"] = 8635, + ["orarr"] = 8635, + ["LeftVector"] = 8636, + ["leftharpoonup"] = 8636, + ["lharu"] = 8636, + ["DownLeftVector"] = 8637, + ["leftharpoondown"] = 8637, + ["lhard"] = 8637, + ["RightUpVector"] = 8638, + ["uharr"] = 8638, + ["upharpoonright"] = 8638, + ["LeftUpVector"] = 8639, + ["uharl"] = 8639, + ["upharpoonleft"] = 8639, + ["RightVector"] = 8640, + ["rharu"] = 8640, + ["rightharpoonup"] = 8640, + ["DownRightVector"] = 8641, + ["rhard"] = 8641, + ["rightharpoondown"] = 8641, + ["RightDownVector"] = 8642, + ["dharr"] = 8642, + ["downharpoonright"] = 8642, + ["LeftDownVector"] = 8643, + ["dharl"] = 8643, + ["downharpoonleft"] = 8643, + ["RightArrowLeftArrow"] = 8644, + ["rightleftarrows"] = 8644, + ["rlarr"] = 8644, + ["UpArrowDownArrow"] = 8645, + ["udarr"] = 8645, + ["LeftArrowRightArrow"] = 8646, + ["leftrightarrows"] = 8646, + ["lrarr"] = 8646, + ["leftleftarrows"] = 8647, + ["llarr"] = 8647, + ["upuparrows"] = 8648, + ["uuarr"] = 8648, + ["rightrightarrows"] = 8649, + ["rrarr"] = 8649, + ["ddarr"] = 8650, + ["downdownarrows"] = 8650, + ["ReverseEquilibrium"] = 8651, + ["leftrightharpoons"] = 8651, + ["lrhar"] = 8651, + ["Equilibrium"] = 8652, + ["rightleftharpoons"] = 8652, + ["rlhar"] = 8652, + ["nLeftarrow"] = 8653, + ["nlArr"] = 8653, + ["nLeftrightarrow"] = 8654, + ["nhArr"] = 8654, + ["nRightarrow"] = 8655, + ["nrArr"] = 8655, + ["DoubleLeftArrow"] = 8656, + ["Leftarrow"] = 8656, + ["lArr"] = 8656, + ["DoubleUpArrow"] = 8657, + ["Uparrow"] = 8657, + ["uArr"] = 8657, + ["DoubleRightArrow"] = 8658, + ["Implies"] = 8658, + ["Rightarrow"] = 8658, + ["rArr"] = 8658, + ["DoubleDownArrow"] = 8659, + ["Downarrow"] = 8659, + ["dArr"] = 8659, + ["DoubleLeftRightArrow"] = 8660, + ["Leftrightarrow"] = 8660, + ["hArr"] = 8660, + ["iff"] = 8660, + ["DoubleUpDownArrow"] = 8661, + ["Updownarrow"] = 8661, + ["vArr"] = 8661, + ["nwArr"] = 8662, + ["neArr"] = 8663, + ["seArr"] = 8664, + ["swArr"] = 8665, + ["Lleftarrow"] = 8666, + ["lAarr"] = 8666, + ["Rrightarrow"] = 8667, + ["rAarr"] = 8667, + ["zigrarr"] = 8669, + ["LeftArrowBar"] = 8676, + ["larrb"] = 8676, + ["RightArrowBar"] = 8677, + ["rarrb"] = 8677, + ["DownArrowUpArrow"] = 8693, + ["duarr"] = 8693, + ["loarr"] = 8701, + ["roarr"] = 8702, + ["hoarr"] = 8703, + ["ForAll"] = 8704, + ["forall"] = 8704, + ["comp"] = 8705, + ["complement"] = 8705, + ["PartialD"] = 8706, + ["npart"] = {8706, 824}, + ["part"] = 8706, + ["Exists"] = 8707, + ["exist"] = 8707, + ["NotExists"] = 8708, + ["nexist"] = 8708, + ["nexists"] = 8708, + ["empty"] = 8709, + ["emptyset"] = 8709, + ["emptyv"] = 8709, + ["varnothing"] = 8709, + ["Del"] = 8711, + ["nabla"] = 8711, + ["Element"] = 8712, + ["in"] = 8712, + ["isin"] = 8712, + ["isinv"] = 8712, + ["NotElement"] = 8713, + ["notin"] = 8713, + ["notinva"] = 8713, + ["ReverseElement"] = 8715, + ["SuchThat"] = 8715, + ["ni"] = 8715, + ["niv"] = 8715, + ["NotReverseElement"] = 8716, + ["notni"] = 8716, + ["notniva"] = 8716, + ["Product"] = 8719, + ["prod"] = 8719, + ["Coproduct"] = 8720, + ["coprod"] = 8720, + ["Sum"] = 8721, + ["sum"] = 8721, + ["minus"] = 8722, + ["MinusPlus"] = 8723, + ["mnplus"] = 8723, + ["mp"] = 8723, + ["dotplus"] = 8724, + ["plusdo"] = 8724, + ["Backslash"] = 8726, + ["setminus"] = 8726, + ["setmn"] = 8726, + ["smallsetminus"] = 8726, + ["ssetmn"] = 8726, + ["lowast"] = 8727, + ["SmallCircle"] = 8728, + ["compfn"] = 8728, + ["Sqrt"] = 8730, + ["radic"] = 8730, + ["Proportional"] = 8733, + ["prop"] = 8733, + ["propto"] = 8733, + ["varpropto"] = 8733, + ["vprop"] = 8733, + ["infin"] = 8734, + ["angrt"] = 8735, + ["ang"] = 8736, + ["angle"] = 8736, + ["nang"] = {8736, 8402}, + ["angmsd"] = 8737, + ["measuredangle"] = 8737, + ["angsph"] = 8738, + ["VerticalBar"] = 8739, + ["mid"] = 8739, + ["shortmid"] = 8739, + ["smid"] = 8739, + ["NotVerticalBar"] = 8740, + ["nmid"] = 8740, + ["nshortmid"] = 8740, + ["nsmid"] = 8740, + ["DoubleVerticalBar"] = 8741, + ["par"] = 8741, + ["parallel"] = 8741, + ["shortparallel"] = 8741, + ["spar"] = 8741, + ["NotDoubleVerticalBar"] = 8742, + ["npar"] = 8742, + ["nparallel"] = 8742, + ["nshortparallel"] = 8742, + ["nspar"] = 8742, + ["and"] = 8743, + ["wedge"] = 8743, + ["or"] = 8744, + ["vee"] = 8744, + ["cap"] = 8745, + ["caps"] = {8745, 65024}, + ["cup"] = 8746, + ["cups"] = {8746, 65024}, + ["Integral"] = 8747, + ["int"] = 8747, + ["Int"] = 8748, + ["iiint"] = 8749, + ["tint"] = 8749, + ["ContourIntegral"] = 8750, + ["conint"] = 8750, + ["oint"] = 8750, + ["Conint"] = 8751, + ["DoubleContourIntegral"] = 8751, + ["Cconint"] = 8752, + ["cwint"] = 8753, + ["ClockwiseContourIntegral"] = 8754, + ["cwconint"] = 8754, + ["CounterClockwiseContourIntegral"] = 8755, + ["awconint"] = 8755, + ["Therefore"] = 8756, + ["there4"] = 8756, + ["therefore"] = 8756, + ["Because"] = 8757, + ["becaus"] = 8757, + ["because"] = 8757, + ["ratio"] = 8758, + ["Colon"] = 8759, + ["Proportion"] = 8759, + ["dotminus"] = 8760, + ["minusd"] = 8760, + ["mDDot"] = 8762, + ["homtht"] = 8763, + ["Tilde"] = 8764, + ["nvsim"] = {8764, 8402}, + ["sim"] = 8764, + ["thicksim"] = 8764, + ["thksim"] = 8764, + ["backsim"] = 8765, + ["bsim"] = 8765, + ["race"] = {8765, 817}, + ["ac"] = 8766, + ["acE"] = {8766, 819}, + ["mstpos"] = 8766, + ["acd"] = 8767, + ["VerticalTilde"] = 8768, + ["wr"] = 8768, + ["wreath"] = 8768, + ["NotTilde"] = 8769, + ["nsim"] = 8769, + ["EqualTilde"] = 8770, + ["NotEqualTilde"] = {8770, 824}, + ["eqsim"] = 8770, + ["esim"] = 8770, + ["nesim"] = {8770, 824}, + ["TildeEqual"] = 8771, + ["sime"] = 8771, + ["simeq"] = 8771, + ["NotTildeEqual"] = 8772, + ["nsime"] = 8772, + ["nsimeq"] = 8772, + ["TildeFullEqual"] = 8773, + ["cong"] = 8773, + ["simne"] = 8774, + ["NotTildeFullEqual"] = 8775, + ["ncong"] = 8775, + ["TildeTilde"] = 8776, + ["ap"] = 8776, + ["approx"] = 8776, + ["asymp"] = 8776, + ["thickapprox"] = 8776, + ["thkap"] = 8776, + ["NotTildeTilde"] = 8777, + ["nap"] = 8777, + ["napprox"] = 8777, + ["ape"] = 8778, + ["approxeq"] = 8778, + ["apid"] = 8779, + ["napid"] = {8779, 824}, + ["backcong"] = 8780, + ["bcong"] = 8780, + ["CupCap"] = 8781, + ["asympeq"] = 8781, + ["nvap"] = {8781, 8402}, + ["Bumpeq"] = 8782, + ["HumpDownHump"] = 8782, + ["NotHumpDownHump"] = {8782, 824}, + ["bump"] = 8782, + ["nbump"] = {8782, 824}, + ["HumpEqual"] = 8783, + ["NotHumpEqual"] = {8783, 824}, + ["bumpe"] = 8783, + ["bumpeq"] = 8783, + ["nbumpe"] = {8783, 824}, + ["DotEqual"] = 8784, + ["doteq"] = 8784, + ["esdot"] = 8784, + ["nedot"] = {8784, 824}, + ["doteqdot"] = 8785, + ["eDot"] = 8785, + ["efDot"] = 8786, + ["fallingdotseq"] = 8786, + ["erDot"] = 8787, + ["risingdotseq"] = 8787, + ["Assign"] = 8788, + ["colone"] = 8788, + ["coloneq"] = 8788, + ["ecolon"] = 8789, + ["eqcolon"] = 8789, + ["ecir"] = 8790, + ["eqcirc"] = 8790, + ["circeq"] = 8791, + ["cire"] = 8791, + ["wedgeq"] = 8793, + ["veeeq"] = 8794, + ["triangleq"] = 8796, + ["trie"] = 8796, + ["equest"] = 8799, + ["questeq"] = 8799, + ["NotEqual"] = 8800, + ["ne"] = 8800, + ["Congruent"] = 8801, + ["bnequiv"] = {8801, 8421}, + ["equiv"] = 8801, + ["NotCongruent"] = 8802, + ["nequiv"] = 8802, + ["le"] = 8804, + ["leq"] = 8804, + ["nvle"] = {8804, 8402}, + ["GreaterEqual"] = 8805, + ["ge"] = 8805, + ["geq"] = 8805, + ["nvge"] = {8805, 8402}, + ["LessFullEqual"] = 8806, + ["lE"] = 8806, + ["leqq"] = 8806, + ["nlE"] = {8806, 824}, + ["nleqq"] = {8806, 824}, + ["GreaterFullEqual"] = 8807, + ["NotGreaterFullEqual"] = {8807, 824}, + ["gE"] = 8807, + ["geqq"] = 8807, + ["ngE"] = {8807, 824}, + ["ngeqq"] = {8807, 824}, + ["lnE"] = 8808, + ["lneqq"] = 8808, + ["lvertneqq"] = {8808, 65024}, + ["lvnE"] = {8808, 65024}, + ["gnE"] = 8809, + ["gneqq"] = 8809, + ["gvertneqq"] = {8809, 65024}, + ["gvnE"] = {8809, 65024}, + ["Lt"] = 8810, + ["NestedLessLess"] = 8810, + ["NotLessLess"] = {8810, 824}, + ["ll"] = 8810, + ["nLt"] = {8810, 8402}, + ["nLtv"] = {8810, 824}, + ["Gt"] = 8811, + ["NestedGreaterGreater"] = 8811, + ["NotGreaterGreater"] = {8811, 824}, + ["gg"] = 8811, + ["nGt"] = {8811, 8402}, + ["nGtv"] = {8811, 824}, + ["between"] = 8812, + ["twixt"] = 8812, + ["NotCupCap"] = 8813, + ["NotLess"] = 8814, + ["nless"] = 8814, + ["nlt"] = 8814, + ["NotGreater"] = 8815, + ["ngt"] = 8815, + ["ngtr"] = 8815, + ["NotLessEqual"] = 8816, + ["nle"] = 8816, + ["nleq"] = 8816, + ["NotGreaterEqual"] = 8817, + ["nge"] = 8817, + ["ngeq"] = 8817, + ["LessTilde"] = 8818, + ["lesssim"] = 8818, + ["lsim"] = 8818, + ["GreaterTilde"] = 8819, + ["gsim"] = 8819, + ["gtrsim"] = 8819, + ["NotLessTilde"] = 8820, + ["nlsim"] = 8820, + ["NotGreaterTilde"] = 8821, + ["ngsim"] = 8821, + ["LessGreater"] = 8822, + ["lessgtr"] = 8822, + ["lg"] = 8822, + ["GreaterLess"] = 8823, + ["gl"] = 8823, + ["gtrless"] = 8823, + ["NotLessGreater"] = 8824, + ["ntlg"] = 8824, + ["NotGreaterLess"] = 8825, + ["ntgl"] = 8825, + ["Precedes"] = 8826, + ["pr"] = 8826, + ["prec"] = 8826, + ["Succeeds"] = 8827, + ["sc"] = 8827, + ["succ"] = 8827, + ["PrecedesSlantEqual"] = 8828, + ["prcue"] = 8828, + ["preccurlyeq"] = 8828, + ["SucceedsSlantEqual"] = 8829, + ["sccue"] = 8829, + ["succcurlyeq"] = 8829, + ["PrecedesTilde"] = 8830, + ["precsim"] = 8830, + ["prsim"] = 8830, + ["NotSucceedsTilde"] = {8831, 824}, + ["SucceedsTilde"] = 8831, + ["scsim"] = 8831, + ["succsim"] = 8831, + ["NotPrecedes"] = 8832, + ["npr"] = 8832, + ["nprec"] = 8832, + ["NotSucceeds"] = 8833, + ["nsc"] = 8833, + ["nsucc"] = 8833, + ["NotSubset"] = {8834, 8402}, + ["nsubset"] = {8834, 8402}, + ["sub"] = 8834, + ["subset"] = 8834, + ["vnsub"] = {8834, 8402}, + ["NotSuperset"] = {8835, 8402}, + ["Superset"] = 8835, + ["nsupset"] = {8835, 8402}, + ["sup"] = 8835, + ["supset"] = 8835, + ["vnsup"] = {8835, 8402}, + ["nsub"] = 8836, + ["nsup"] = 8837, + ["SubsetEqual"] = 8838, + ["sube"] = 8838, + ["subseteq"] = 8838, + ["SupersetEqual"] = 8839, + ["supe"] = 8839, + ["supseteq"] = 8839, + ["NotSubsetEqual"] = 8840, + ["nsube"] = 8840, + ["nsubseteq"] = 8840, + ["NotSupersetEqual"] = 8841, + ["nsupe"] = 8841, + ["nsupseteq"] = 8841, + ["subne"] = 8842, + ["subsetneq"] = 8842, + ["varsubsetneq"] = {8842, 65024}, + ["vsubne"] = {8842, 65024}, + ["supne"] = 8843, + ["supsetneq"] = 8843, + ["varsupsetneq"] = {8843, 65024}, + ["vsupne"] = {8843, 65024}, + ["cupdot"] = 8845, + ["UnionPlus"] = 8846, + ["uplus"] = 8846, + ["NotSquareSubset"] = {8847, 824}, + ["SquareSubset"] = 8847, + ["sqsub"] = 8847, + ["sqsubset"] = 8847, + ["NotSquareSuperset"] = {8848, 824}, + ["SquareSuperset"] = 8848, + ["sqsup"] = 8848, + ["sqsupset"] = 8848, + ["SquareSubsetEqual"] = 8849, + ["sqsube"] = 8849, + ["sqsubseteq"] = 8849, + ["SquareSupersetEqual"] = 8850, + ["sqsupe"] = 8850, + ["sqsupseteq"] = 8850, + ["SquareIntersection"] = 8851, + ["sqcap"] = 8851, + ["sqcaps"] = {8851, 65024}, + ["SquareUnion"] = 8852, + ["sqcup"] = 8852, + ["sqcups"] = {8852, 65024}, + ["CirclePlus"] = 8853, + ["oplus"] = 8853, + ["CircleMinus"] = 8854, + ["ominus"] = 8854, + ["CircleTimes"] = 8855, + ["otimes"] = 8855, + ["osol"] = 8856, + ["CircleDot"] = 8857, + ["odot"] = 8857, + ["circledcirc"] = 8858, + ["ocir"] = 8858, + ["circledast"] = 8859, + ["oast"] = 8859, + ["circleddash"] = 8861, + ["odash"] = 8861, + ["boxplus"] = 8862, + ["plusb"] = 8862, + ["boxminus"] = 8863, + ["minusb"] = 8863, + ["boxtimes"] = 8864, + ["timesb"] = 8864, + ["dotsquare"] = 8865, + ["sdotb"] = 8865, + ["RightTee"] = 8866, + ["vdash"] = 8866, + ["LeftTee"] = 8867, + ["dashv"] = 8867, + ["DownTee"] = 8868, + ["top"] = 8868, + ["UpTee"] = 8869, + ["bot"] = 8869, + ["bottom"] = 8869, + ["perp"] = 8869, + ["models"] = 8871, + ["DoubleRightTee"] = 8872, + ["vDash"] = 8872, + ["Vdash"] = 8873, + ["Vvdash"] = 8874, + ["VDash"] = 8875, + ["nvdash"] = 8876, + ["nvDash"] = 8877, + ["nVdash"] = 8878, + ["nVDash"] = 8879, + ["prurel"] = 8880, + ["LeftTriangle"] = 8882, + ["vartriangleleft"] = 8882, + ["vltri"] = 8882, + ["RightTriangle"] = 8883, + ["vartriangleright"] = 8883, + ["vrtri"] = 8883, + ["LeftTriangleEqual"] = 8884, + ["ltrie"] = 8884, + ["nvltrie"] = {8884, 8402}, + ["trianglelefteq"] = 8884, + ["RightTriangleEqual"] = 8885, + ["nvrtrie"] = {8885, 8402}, + ["rtrie"] = 8885, + ["trianglerighteq"] = 8885, + ["origof"] = 8886, + ["imof"] = 8887, + ["multimap"] = 8888, + ["mumap"] = 8888, + ["hercon"] = 8889, + ["intcal"] = 8890, + ["intercal"] = 8890, + ["veebar"] = 8891, + ["barvee"] = 8893, + ["angrtvb"] = 8894, + ["lrtri"] = 8895, + ["Wedge"] = 8896, + ["bigwedge"] = 8896, + ["xwedge"] = 8896, + ["Vee"] = 8897, + ["bigvee"] = 8897, + ["xvee"] = 8897, + ["Intersection"] = 8898, + ["bigcap"] = 8898, + ["xcap"] = 8898, + ["Union"] = 8899, + ["bigcup"] = 8899, + ["xcup"] = 8899, + ["Diamond"] = 8900, + ["diam"] = 8900, + ["diamond"] = 8900, + ["sdot"] = 8901, + ["Star"] = 8902, + ["sstarf"] = 8902, + ["divideontimes"] = 8903, + ["divonx"] = 8903, + ["bowtie"] = 8904, + ["ltimes"] = 8905, + ["rtimes"] = 8906, + ["leftthreetimes"] = 8907, + ["lthree"] = 8907, + ["rightthreetimes"] = 8908, + ["rthree"] = 8908, + ["backsimeq"] = 8909, + ["bsime"] = 8909, + ["curlyvee"] = 8910, + ["cuvee"] = 8910, + ["curlywedge"] = 8911, + ["cuwed"] = 8911, + ["Sub"] = 8912, + ["Subset"] = 8912, + ["Sup"] = 8913, + ["Supset"] = 8913, + ["Cap"] = 8914, + ["Cup"] = 8915, + ["fork"] = 8916, + ["pitchfork"] = 8916, + ["epar"] = 8917, + ["lessdot"] = 8918, + ["ltdot"] = 8918, + ["gtdot"] = 8919, + ["gtrdot"] = 8919, + ["Ll"] = 8920, + ["nLl"] = {8920, 824}, + ["Gg"] = 8921, + ["ggg"] = 8921, + ["nGg"] = {8921, 824}, + ["LessEqualGreater"] = 8922, + ["leg"] = 8922, + ["lesg"] = {8922, 65024}, + ["lesseqgtr"] = 8922, + ["GreaterEqualLess"] = 8923, + ["gel"] = 8923, + ["gesl"] = {8923, 65024}, + ["gtreqless"] = 8923, + ["cuepr"] = 8926, + ["curlyeqprec"] = 8926, + ["cuesc"] = 8927, + ["curlyeqsucc"] = 8927, + ["NotPrecedesSlantEqual"] = 8928, + ["nprcue"] = 8928, + ["NotSucceedsSlantEqual"] = 8929, + ["nsccue"] = 8929, + ["NotSquareSubsetEqual"] = 8930, + ["nsqsube"] = 8930, + ["NotSquareSupersetEqual"] = 8931, + ["nsqsupe"] = 8931, + ["lnsim"] = 8934, + ["gnsim"] = 8935, + ["precnsim"] = 8936, + ["prnsim"] = 8936, + ["scnsim"] = 8937, + ["succnsim"] = 8937, + ["NotLeftTriangle"] = 8938, + ["nltri"] = 8938, + ["ntriangleleft"] = 8938, + ["NotRightTriangle"] = 8939, + ["nrtri"] = 8939, + ["ntriangleright"] = 8939, + ["NotLeftTriangleEqual"] = 8940, + ["nltrie"] = 8940, + ["ntrianglelefteq"] = 8940, + ["NotRightTriangleEqual"] = 8941, + ["nrtrie"] = 8941, + ["ntrianglerighteq"] = 8941, + ["vellip"] = 8942, + ["ctdot"] = 8943, + ["utdot"] = 8944, + ["dtdot"] = 8945, + ["disin"] = 8946, + ["isinsv"] = 8947, + ["isins"] = 8948, + ["isindot"] = 8949, + ["notindot"] = {8949, 824}, + ["notinvc"] = 8950, + ["notinvb"] = 8951, + ["isinE"] = 8953, + ["notinE"] = {8953, 824}, + ["nisd"] = 8954, + ["xnis"] = 8955, + ["nis"] = 8956, + ["notnivc"] = 8957, + ["notnivb"] = 8958, + ["barwed"] = 8965, + ["barwedge"] = 8965, + ["Barwed"] = 8966, + ["doublebarwedge"] = 8966, + ["LeftCeiling"] = 8968, + ["lceil"] = 8968, + ["RightCeiling"] = 8969, + ["rceil"] = 8969, + ["LeftFloor"] = 8970, + ["lfloor"] = 8970, + ["RightFloor"] = 8971, + ["rfloor"] = 8971, + ["drcrop"] = 8972, + ["dlcrop"] = 8973, + ["urcrop"] = 8974, + ["ulcrop"] = 8975, + ["bnot"] = 8976, + ["profline"] = 8978, + ["profsurf"] = 8979, + ["telrec"] = 8981, + ["target"] = 8982, + ["ulcorn"] = 8988, + ["ulcorner"] = 8988, + ["urcorn"] = 8989, + ["urcorner"] = 8989, + ["dlcorn"] = 8990, + ["llcorner"] = 8990, + ["drcorn"] = 8991, + ["lrcorner"] = 8991, + ["frown"] = 8994, + ["sfrown"] = 8994, + ["smile"] = 8995, + ["ssmile"] = 8995, + ["cylcty"] = 9005, + ["profalar"] = 9006, + ["topbot"] = 9014, + ["ovbar"] = 9021, + ["solbar"] = 9023, + ["angzarr"] = 9084, + ["lmoust"] = 9136, + ["lmoustache"] = 9136, + ["rmoust"] = 9137, + ["rmoustache"] = 9137, + ["OverBracket"] = 9140, + ["tbrk"] = 9140, + ["UnderBracket"] = 9141, + ["bbrk"] = 9141, + ["bbrktbrk"] = 9142, + ["OverParenthesis"] = 9180, + ["UnderParenthesis"] = 9181, + ["OverBrace"] = 9182, + ["UnderBrace"] = 9183, + ["trpezium"] = 9186, + ["elinters"] = 9191, + ["blank"] = 9251, + ["circledS"] = 9416, + ["oS"] = 9416, + ["HorizontalLine"] = 9472, + ["boxh"] = 9472, + ["boxv"] = 9474, + ["boxdr"] = 9484, + ["boxdl"] = 9488, + ["boxur"] = 9492, + ["boxul"] = 9496, + ["boxvr"] = 9500, + ["boxvl"] = 9508, + ["boxhd"] = 9516, + ["boxhu"] = 9524, + ["boxvh"] = 9532, + ["boxH"] = 9552, + ["boxV"] = 9553, + ["boxdR"] = 9554, + ["boxDr"] = 9555, + ["boxDR"] = 9556, + ["boxdL"] = 9557, + ["boxDl"] = 9558, + ["boxDL"] = 9559, + ["boxuR"] = 9560, + ["boxUr"] = 9561, + ["boxUR"] = 9562, + ["boxuL"] = 9563, + ["boxUl"] = 9564, + ["boxUL"] = 9565, + ["boxvR"] = 9566, + ["boxVr"] = 9567, + ["boxVR"] = 9568, + ["boxvL"] = 9569, + ["boxVl"] = 9570, + ["boxVL"] = 9571, + ["boxHd"] = 9572, + ["boxhD"] = 9573, + ["boxHD"] = 9574, + ["boxHu"] = 9575, + ["boxhU"] = 9576, + ["boxHU"] = 9577, + ["boxvH"] = 9578, + ["boxVh"] = 9579, + ["boxVH"] = 9580, + ["uhblk"] = 9600, + ["lhblk"] = 9604, + ["block"] = 9608, + ["blk14"] = 9617, + ["blk12"] = 9618, + ["blk34"] = 9619, + ["Square"] = 9633, + ["squ"] = 9633, + ["square"] = 9633, + ["FilledVerySmallSquare"] = 9642, + ["blacksquare"] = 9642, + ["squarf"] = 9642, + ["squf"] = 9642, + ["EmptyVerySmallSquare"] = 9643, + ["rect"] = 9645, + ["marker"] = 9646, + ["fltns"] = 9649, + ["bigtriangleup"] = 9651, + ["xutri"] = 9651, + ["blacktriangle"] = 9652, + ["utrif"] = 9652, + ["triangle"] = 9653, + ["utri"] = 9653, + ["blacktriangleright"] = 9656, + ["rtrif"] = 9656, + ["rtri"] = 9657, + ["triangleright"] = 9657, + ["bigtriangledown"] = 9661, + ["xdtri"] = 9661, + ["blacktriangledown"] = 9662, + ["dtrif"] = 9662, + ["dtri"] = 9663, + ["triangledown"] = 9663, + ["blacktriangleleft"] = 9666, + ["ltrif"] = 9666, + ["ltri"] = 9667, + ["triangleleft"] = 9667, + ["loz"] = 9674, + ["lozenge"] = 9674, + ["cir"] = 9675, + ["tridot"] = 9708, + ["bigcirc"] = 9711, + ["xcirc"] = 9711, + ["ultri"] = 9720, + ["urtri"] = 9721, + ["lltri"] = 9722, + ["EmptySmallSquare"] = 9723, + ["FilledSmallSquare"] = 9724, + ["bigstar"] = 9733, + ["starf"] = 9733, + ["star"] = 9734, + ["phone"] = 9742, + ["female"] = 9792, + ["male"] = 9794, + ["spades"] = 9824, + ["spadesuit"] = 9824, + ["clubs"] = 9827, + ["clubsuit"] = 9827, + ["hearts"] = 9829, + ["heartsuit"] = 9829, + ["diamondsuit"] = 9830, + ["diams"] = 9830, + ["sung"] = 9834, + ["flat"] = 9837, + ["natur"] = 9838, + ["natural"] = 9838, + ["sharp"] = 9839, + ["check"] = 10003, + ["checkmark"] = 10003, + ["cross"] = 10007, + ["malt"] = 10016, + ["maltese"] = 10016, + ["sext"] = 10038, + ["VerticalSeparator"] = 10072, + ["lbbrk"] = 10098, + ["rbbrk"] = 10099, + ["bsolhsub"] = 10184, + ["suphsol"] = 10185, + ["LeftDoubleBracket"] = 10214, + ["lobrk"] = 10214, + ["RightDoubleBracket"] = 10215, + ["robrk"] = 10215, + ["LeftAngleBracket"] = 10216, + ["lang"] = 10216, + ["langle"] = 10216, + ["RightAngleBracket"] = 10217, + ["rang"] = 10217, + ["rangle"] = 10217, + ["Lang"] = 10218, + ["Rang"] = 10219, + ["loang"] = 10220, + ["roang"] = 10221, + ["LongLeftArrow"] = 10229, + ["longleftarrow"] = 10229, + ["xlarr"] = 10229, + ["LongRightArrow"] = 10230, + ["longrightarrow"] = 10230, + ["xrarr"] = 10230, + ["LongLeftRightArrow"] = 10231, + ["longleftrightarrow"] = 10231, + ["xharr"] = 10231, + ["DoubleLongLeftArrow"] = 10232, + ["Longleftarrow"] = 10232, + ["xlArr"] = 10232, + ["DoubleLongRightArrow"] = 10233, + ["Longrightarrow"] = 10233, + ["xrArr"] = 10233, + ["DoubleLongLeftRightArrow"] = 10234, + ["Longleftrightarrow"] = 10234, + ["xhArr"] = 10234, + ["longmapsto"] = 10236, + ["xmap"] = 10236, + ["dzigrarr"] = 10239, + ["nvlArr"] = 10498, + ["nvrArr"] = 10499, + ["nvHarr"] = 10500, + ["Map"] = 10501, + ["lbarr"] = 10508, + ["bkarow"] = 10509, + ["rbarr"] = 10509, + ["lBarr"] = 10510, + ["dbkarow"] = 10511, + ["rBarr"] = 10511, + ["RBarr"] = 10512, + ["drbkarow"] = 10512, + ["DDotrahd"] = 10513, + ["UpArrowBar"] = 10514, + ["DownArrowBar"] = 10515, + ["Rarrtl"] = 10518, + ["latail"] = 10521, + ["ratail"] = 10522, + ["lAtail"] = 10523, + ["rAtail"] = 10524, + ["larrfs"] = 10525, + ["rarrfs"] = 10526, + ["larrbfs"] = 10527, + ["rarrbfs"] = 10528, + ["nwarhk"] = 10531, + ["nearhk"] = 10532, + ["hksearow"] = 10533, + ["searhk"] = 10533, + ["hkswarow"] = 10534, + ["swarhk"] = 10534, + ["nwnear"] = 10535, + ["nesear"] = 10536, + ["toea"] = 10536, + ["seswar"] = 10537, + ["tosa"] = 10537, + ["swnwar"] = 10538, + ["nrarrc"] = {10547, 824}, + ["rarrc"] = 10547, + ["cudarrr"] = 10549, + ["ldca"] = 10550, + ["rdca"] = 10551, + ["cudarrl"] = 10552, + ["larrpl"] = 10553, + ["curarrm"] = 10556, + ["cularrp"] = 10557, + ["rarrpl"] = 10565, + ["harrcir"] = 10568, + ["Uarrocir"] = 10569, + ["lurdshar"] = 10570, + ["ldrushar"] = 10571, + ["LeftRightVector"] = 10574, + ["RightUpDownVector"] = 10575, + ["DownLeftRightVector"] = 10576, + ["LeftUpDownVector"] = 10577, + ["LeftVectorBar"] = 10578, + ["RightVectorBar"] = 10579, + ["RightUpVectorBar"] = 10580, + ["RightDownVectorBar"] = 10581, + ["DownLeftVectorBar"] = 10582, + ["DownRightVectorBar"] = 10583, + ["LeftUpVectorBar"] = 10584, + ["LeftDownVectorBar"] = 10585, + ["LeftTeeVector"] = 10586, + ["RightTeeVector"] = 10587, + ["RightUpTeeVector"] = 10588, + ["RightDownTeeVector"] = 10589, + ["DownLeftTeeVector"] = 10590, + ["DownRightTeeVector"] = 10591, + ["LeftUpTeeVector"] = 10592, + ["LeftDownTeeVector"] = 10593, + ["lHar"] = 10594, + ["uHar"] = 10595, + ["rHar"] = 10596, + ["dHar"] = 10597, + ["luruhar"] = 10598, + ["ldrdhar"] = 10599, + ["ruluhar"] = 10600, + ["rdldhar"] = 10601, + ["lharul"] = 10602, + ["llhard"] = 10603, + ["rharul"] = 10604, + ["lrhard"] = 10605, + ["UpEquilibrium"] = 10606, + ["udhar"] = 10606, + ["ReverseUpEquilibrium"] = 10607, + ["duhar"] = 10607, + ["RoundImplies"] = 10608, + ["erarr"] = 10609, + ["simrarr"] = 10610, + ["larrsim"] = 10611, + ["rarrsim"] = 10612, + ["rarrap"] = 10613, + ["ltlarr"] = 10614, + ["gtrarr"] = 10616, + ["subrarr"] = 10617, + ["suplarr"] = 10619, + ["lfisht"] = 10620, + ["rfisht"] = 10621, + ["ufisht"] = 10622, + ["dfisht"] = 10623, + ["lopar"] = 10629, + ["ropar"] = 10630, + ["lbrke"] = 10635, + ["rbrke"] = 10636, + ["lbrkslu"] = 10637, + ["rbrksld"] = 10638, + ["lbrksld"] = 10639, + ["rbrkslu"] = 10640, + ["langd"] = 10641, + ["rangd"] = 10642, + ["lparlt"] = 10643, + ["rpargt"] = 10644, + ["gtlPar"] = 10645, + ["ltrPar"] = 10646, + ["vzigzag"] = 10650, + ["vangrt"] = 10652, + ["angrtvbd"] = 10653, + ["ange"] = 10660, + ["range"] = 10661, + ["dwangle"] = 10662, + ["uwangle"] = 10663, + ["angmsdaa"] = 10664, + ["angmsdab"] = 10665, + ["angmsdac"] = 10666, + ["angmsdad"] = 10667, + ["angmsdae"] = 10668, + ["angmsdaf"] = 10669, + ["angmsdag"] = 10670, + ["angmsdah"] = 10671, + ["bemptyv"] = 10672, + ["demptyv"] = 10673, + ["cemptyv"] = 10674, + ["raemptyv"] = 10675, + ["laemptyv"] = 10676, + ["ohbar"] = 10677, + ["omid"] = 10678, + ["opar"] = 10679, + ["operp"] = 10681, + ["olcross"] = 10683, + ["odsold"] = 10684, + ["olcir"] = 10686, + ["ofcir"] = 10687, + ["olt"] = 10688, + ["ogt"] = 10689, + ["cirscir"] = 10690, + ["cirE"] = 10691, + ["solb"] = 10692, + ["bsolb"] = 10693, + ["boxbox"] = 10697, + ["trisb"] = 10701, + ["rtriltri"] = 10702, + ["LeftTriangleBar"] = 10703, + ["NotLeftTriangleBar"] = {10703, 824}, + ["NotRightTriangleBar"] = {10704, 824}, + ["RightTriangleBar"] = 10704, + ["iinfin"] = 10716, + ["infintie"] = 10717, + ["nvinfin"] = 10718, + ["eparsl"] = 10723, + ["smeparsl"] = 10724, + ["eqvparsl"] = 10725, + ["blacklozenge"] = 10731, + ["lozf"] = 10731, + ["RuleDelayed"] = 10740, + ["dsol"] = 10742, + ["bigodot"] = 10752, + ["xodot"] = 10752, + ["bigoplus"] = 10753, + ["xoplus"] = 10753, + ["bigotimes"] = 10754, + ["xotime"] = 10754, + ["biguplus"] = 10756, + ["xuplus"] = 10756, + ["bigsqcup"] = 10758, + ["xsqcup"] = 10758, + ["iiiint"] = 10764, + ["qint"] = 10764, + ["fpartint"] = 10765, + ["cirfnint"] = 10768, + ["awint"] = 10769, + ["rppolint"] = 10770, + ["scpolint"] = 10771, + ["npolint"] = 10772, + ["pointint"] = 10773, + ["quatint"] = 10774, + ["intlarhk"] = 10775, + ["pluscir"] = 10786, + ["plusacir"] = 10787, + ["simplus"] = 10788, + ["plusdu"] = 10789, + ["plussim"] = 10790, + ["plustwo"] = 10791, + ["mcomma"] = 10793, + ["minusdu"] = 10794, + ["loplus"] = 10797, + ["roplus"] = 10798, + ["Cross"] = 10799, + ["timesd"] = 10800, + ["timesbar"] = 10801, + ["smashp"] = 10803, + ["lotimes"] = 10804, + ["rotimes"] = 10805, + ["otimesas"] = 10806, + ["Otimes"] = 10807, + ["odiv"] = 10808, + ["triplus"] = 10809, + ["triminus"] = 10810, + ["tritime"] = 10811, + ["intprod"] = 10812, + ["iprod"] = 10812, + ["amalg"] = 10815, + ["capdot"] = 10816, + ["ncup"] = 10818, + ["ncap"] = 10819, + ["capand"] = 10820, + ["cupor"] = 10821, + ["cupcap"] = 10822, + ["capcup"] = 10823, + ["cupbrcap"] = 10824, + ["capbrcup"] = 10825, + ["cupcup"] = 10826, + ["capcap"] = 10827, + ["ccups"] = 10828, + ["ccaps"] = 10829, + ["ccupssm"] = 10832, + ["And"] = 10835, + ["Or"] = 10836, + ["andand"] = 10837, + ["oror"] = 10838, + ["orslope"] = 10839, + ["andslope"] = 10840, + ["andv"] = 10842, + ["orv"] = 10843, + ["andd"] = 10844, + ["ord"] = 10845, + ["wedbar"] = 10847, + ["sdote"] = 10854, + ["simdot"] = 10858, + ["congdot"] = 10861, + ["ncongdot"] = {10861, 824}, + ["easter"] = 10862, + ["apacir"] = 10863, + ["apE"] = 10864, + ["napE"] = {10864, 824}, + ["eplus"] = 10865, + ["pluse"] = 10866, + ["Esim"] = 10867, + ["Colone"] = 10868, + ["Equal"] = 10869, + ["ddotseq"] = 10871, + ["eDDot"] = 10871, + ["equivDD"] = 10872, + ["ltcir"] = 10873, + ["gtcir"] = 10874, + ["ltquest"] = 10875, + ["gtquest"] = 10876, + ["LessSlantEqual"] = 10877, + ["NotLessSlantEqual"] = {10877, 824}, + ["leqslant"] = 10877, + ["les"] = 10877, + ["nleqslant"] = {10877, 824}, + ["nles"] = {10877, 824}, + ["GreaterSlantEqual"] = 10878, + ["NotGreaterSlantEqual"] = {10878, 824}, + ["geqslant"] = 10878, + ["ges"] = 10878, + ["ngeqslant"] = {10878, 824}, + ["nges"] = {10878, 824}, + ["lesdot"] = 10879, + ["gesdot"] = 10880, + ["lesdoto"] = 10881, + ["gesdoto"] = 10882, + ["lesdotor"] = 10883, + ["gesdotol"] = 10884, + ["lap"] = 10885, + ["lessapprox"] = 10885, + ["gap"] = 10886, + ["gtrapprox"] = 10886, + ["lne"] = 10887, + ["lneq"] = 10887, + ["gne"] = 10888, + ["gneq"] = 10888, + ["lnap"] = 10889, + ["lnapprox"] = 10889, + ["gnap"] = 10890, + ["gnapprox"] = 10890, + ["lEg"] = 10891, + ["lesseqqgtr"] = 10891, + ["gEl"] = 10892, + ["gtreqqless"] = 10892, + ["lsime"] = 10893, + ["gsime"] = 10894, + ["lsimg"] = 10895, + ["gsiml"] = 10896, + ["lgE"] = 10897, + ["glE"] = 10898, + ["lesges"] = 10899, + ["gesles"] = 10900, + ["els"] = 10901, + ["eqslantless"] = 10901, + ["egs"] = 10902, + ["eqslantgtr"] = 10902, + ["elsdot"] = 10903, + ["egsdot"] = 10904, + ["el"] = 10905, + ["eg"] = 10906, + ["siml"] = 10909, + ["simg"] = 10910, + ["simlE"] = 10911, + ["simgE"] = 10912, + ["LessLess"] = 10913, + ["NotNestedLessLess"] = {10913, 824}, + ["GreaterGreater"] = 10914, + ["NotNestedGreaterGreater"] = {10914, 824}, + ["glj"] = 10916, + ["gla"] = 10917, + ["ltcc"] = 10918, + ["gtcc"] = 10919, + ["lescc"] = 10920, + ["gescc"] = 10921, + ["smt"] = 10922, + ["lat"] = 10923, + ["smte"] = 10924, + ["smtes"] = {10924, 65024}, + ["late"] = 10925, + ["lates"] = {10925, 65024}, + ["bumpE"] = 10926, + ["NotPrecedesEqual"] = {10927, 824}, + ["PrecedesEqual"] = 10927, + ["npre"] = {10927, 824}, + ["npreceq"] = {10927, 824}, + ["pre"] = 10927, + ["preceq"] = 10927, + ["NotSucceedsEqual"] = {10928, 824}, + ["SucceedsEqual"] = 10928, + ["nsce"] = {10928, 824}, + ["nsucceq"] = {10928, 824}, + ["sce"] = 10928, + ["succeq"] = 10928, + ["prE"] = 10931, + ["scE"] = 10932, + ["precneqq"] = 10933, + ["prnE"] = 10933, + ["scnE"] = 10934, + ["succneqq"] = 10934, + ["prap"] = 10935, + ["precapprox"] = 10935, + ["scap"] = 10936, + ["succapprox"] = 10936, + ["precnapprox"] = 10937, + ["prnap"] = 10937, + ["scnap"] = 10938, + ["succnapprox"] = 10938, + ["Pr"] = 10939, + ["Sc"] = 10940, + ["subdot"] = 10941, + ["supdot"] = 10942, + ["subplus"] = 10943, + ["supplus"] = 10944, + ["submult"] = 10945, + ["supmult"] = 10946, + ["subedot"] = 10947, + ["supedot"] = 10948, + ["nsubE"] = {10949, 824}, + ["nsubseteqq"] = {10949, 824}, + ["subE"] = 10949, + ["subseteqq"] = 10949, + ["nsupE"] = {10950, 824}, + ["nsupseteqq"] = {10950, 824}, + ["supE"] = 10950, + ["supseteqq"] = 10950, + ["subsim"] = 10951, + ["supsim"] = 10952, + ["subnE"] = 10955, + ["subsetneqq"] = 10955, + ["varsubsetneqq"] = {10955, 65024}, + ["vsubnE"] = {10955, 65024}, + ["supnE"] = 10956, + ["supsetneqq"] = 10956, + ["varsupsetneqq"] = {10956, 65024}, + ["vsupnE"] = {10956, 65024}, + ["csub"] = 10959, + ["csup"] = 10960, + ["csube"] = 10961, + ["csupe"] = 10962, + ["subsup"] = 10963, + ["supsub"] = 10964, + ["subsub"] = 10965, + ["supsup"] = 10966, + ["suphsub"] = 10967, + ["supdsub"] = 10968, + ["forkv"] = 10969, + ["topfork"] = 10970, + ["mlcp"] = 10971, + ["Dashv"] = 10980, + ["DoubleLeftTee"] = 10980, + ["Vdashl"] = 10982, + ["Barv"] = 10983, + ["vBar"] = 10984, + ["vBarv"] = 10985, + ["Vbar"] = 10987, + ["Not"] = 10988, + ["bNot"] = 10989, + ["rnmid"] = 10990, + ["cirmid"] = 10991, + ["midcir"] = 10992, + ["topcir"] = 10993, + ["nhpar"] = 10994, + ["parsim"] = 10995, + ["nparsl"] = {11005, 8421}, + ["parsl"] = 11005, + ["fflig"] = 64256, + ["filig"] = 64257, + ["fllig"] = 64258, + ["ffilig"] = 64259, + ["ffllig"] = 64260, + ["Ascr"] = 119964, + ["Cscr"] = 119966, + ["Dscr"] = 119967, + ["Gscr"] = 119970, + ["Jscr"] = 119973, + ["Kscr"] = 119974, + ["Nscr"] = 119977, + ["Oscr"] = 119978, + ["Pscr"] = 119979, + ["Qscr"] = 119980, + ["Sscr"] = 119982, + ["Tscr"] = 119983, + ["Uscr"] = 119984, + ["Vscr"] = 119985, + ["Wscr"] = 119986, + ["Xscr"] = 119987, + ["Yscr"] = 119988, + ["Zscr"] = 119989, + ["ascr"] = 119990, + ["bscr"] = 119991, + ["cscr"] = 119992, + ["dscr"] = 119993, + ["fscr"] = 119995, + ["hscr"] = 119997, + ["iscr"] = 119998, + ["jscr"] = 119999, + ["kscr"] = 120000, + ["lscr"] = 120001, + ["mscr"] = 120002, + ["nscr"] = 120003, + ["pscr"] = 120005, + ["qscr"] = 120006, + ["rscr"] = 120007, + ["sscr"] = 120008, + ["tscr"] = 120009, + ["uscr"] = 120010, + ["vscr"] = 120011, + ["wscr"] = 120012, + ["xscr"] = 120013, + ["yscr"] = 120014, + ["zscr"] = 120015, + ["Afr"] = 120068, + ["Bfr"] = 120069, + ["Dfr"] = 120071, + ["Efr"] = 120072, + ["Ffr"] = 120073, + ["Gfr"] = 120074, + ["Jfr"] = 120077, + ["Kfr"] = 120078, + ["Lfr"] = 120079, + ["Mfr"] = 120080, + ["Nfr"] = 120081, + ["Ofr"] = 120082, + ["Pfr"] = 120083, + ["Qfr"] = 120084, + ["Sfr"] = 120086, + ["Tfr"] = 120087, + ["Ufr"] = 120088, + ["Vfr"] = 120089, + ["Wfr"] = 120090, + ["Xfr"] = 120091, + ["Yfr"] = 120092, + ["afr"] = 120094, + ["bfr"] = 120095, + ["cfr"] = 120096, + ["dfr"] = 120097, + ["efr"] = 120098, + ["ffr"] = 120099, + ["gfr"] = 120100, + ["hfr"] = 120101, + ["ifr"] = 120102, + ["jfr"] = 120103, + ["kfr"] = 120104, + ["lfr"] = 120105, + ["mfr"] = 120106, + ["nfr"] = 120107, + ["ofr"] = 120108, + ["pfr"] = 120109, + ["qfr"] = 120110, + ["rfr"] = 120111, + ["sfr"] = 120112, + ["tfr"] = 120113, + ["ufr"] = 120114, + ["vfr"] = 120115, + ["wfr"] = 120116, + ["xfr"] = 120117, + ["yfr"] = 120118, + ["zfr"] = 120119, + ["Aopf"] = 120120, + ["Bopf"] = 120121, + ["Dopf"] = 120123, + ["Eopf"] = 120124, + ["Fopf"] = 120125, + ["Gopf"] = 120126, + ["Iopf"] = 120128, + ["Jopf"] = 120129, + ["Kopf"] = 120130, + ["Lopf"] = 120131, + ["Mopf"] = 120132, + ["Oopf"] = 120134, + ["Sopf"] = 120138, + ["Topf"] = 120139, + ["Uopf"] = 120140, + ["Vopf"] = 120141, + ["Wopf"] = 120142, + ["Xopf"] = 120143, + ["Yopf"] = 120144, + ["aopf"] = 120146, + ["bopf"] = 120147, + ["copf"] = 120148, + ["dopf"] = 120149, + ["eopf"] = 120150, + ["fopf"] = 120151, + ["gopf"] = 120152, + ["hopf"] = 120153, + ["iopf"] = 120154, + ["jopf"] = 120155, + ["kopf"] = 120156, + ["lopf"] = 120157, + ["mopf"] = 120158, + ["nopf"] = 120159, + ["oopf"] = 120160, + ["popf"] = 120161, + ["qopf"] = 120162, + ["ropf"] = 120163, + ["sopf"] = 120164, + ["topf"] = 120165, + ["uopf"] = 120166, + ["vopf"] = 120167, + ["wopf"] = 120168, + ["xopf"] = 120169, + ["yopf"] = 120170, + ["zopf"] = 120171, +} +function entities.dec_entity(s) + local n = tonumber(s) + if n == nil then + return "&#" .. s .. ";" -- fallback for unknown entities + end + return unicode.utf8.char(n) +end +function entities.hex_entity(s) + local n = tonumber("0x"..s) + if n == nil then + return "&#x" .. s .. ";" -- fallback for unknown entities + end + return unicode.utf8.char(n) +end +function entities.hex_entity_with_x_char(x, s) + local n = tonumber("0x"..s) + if n == nil then + return "&#" .. x .. s .. ";" -- fallback for unknown entities + end + return unicode.utf8.char(n) +end +function entities.char_entity(s) + local code_points = character_entities[s] + if code_points == nil then + return "&" .. s .. ";" + end + if type(code_points) ~= 'table' then + code_points = {code_points} + end + local char_table = {} + for _, code_point in ipairs(code_points) do + table.insert(char_table, unicode.utf8.char(code_point)) + end + return table.concat(char_table) +end +M.writer = {} +function M.writer.new(options) + local self = {} + self.options = options + self.flatten_inlines = false + local slice_specifiers = {} + for specifier in options.slice:gmatch("[^%s]+") do + table.insert(slice_specifiers, specifier) + end + + if #slice_specifiers == 2 then + self.slice_begin, self.slice_end = table.unpack(slice_specifiers) + local slice_begin_type = self.slice_begin:sub(1, 1) + if slice_begin_type ~= "^" and slice_begin_type ~= "$" then + self.slice_begin = "^" .. self.slice_begin + end + local slice_end_type = self.slice_end:sub(1, 1) + if slice_end_type ~= "^" and slice_end_type ~= "$" then + self.slice_end = "$" .. self.slice_end + end + elseif #slice_specifiers == 1 then + self.slice_begin = "^" .. slice_specifiers[1] + self.slice_end = "$" .. slice_specifiers[1] + end + + self.slice_begin_type = self.slice_begin:sub(1, 1) + self.slice_begin_identifier = self.slice_begin:sub(2) or "" + self.slice_end_type = self.slice_end:sub(1, 1) + self.slice_end_identifier = self.slice_end:sub(2) or "" + + if self.slice_begin == "^" and self.slice_end ~= "^" then + self.is_writing = true + else + self.is_writing = false + end + self.space = " " + self.nbsp = "\\markdownRendererNbsp{}" + function self.plain(s) + return s + end + function self.paragraph(s) + if not self.is_writing then return "" end + return s + end + self.interblocksep_text = "\\markdownRendererInterblockSeparator\n{}" + function self.interblocksep() + if not self.is_writing then return "" end + return self.interblocksep_text + end + self.paragraphsep_text = "\\markdownRendererParagraphSeparator\n{}" + function self.paragraphsep() + if not self.is_writing then return "" end + return self.paragraphsep_text + end + self.undosep_text = "\\markdownRendererUndoSeparator\n{}" + function self.undosep() + if not self.is_writing then return "" end + return self.undosep_text + end + self.soft_line_break = function() + if self.flatten_inlines then return "\n" end + return "\\markdownRendererSoftLineBreak\n{}" + end + self.hard_line_break = function() + if self.flatten_inlines then return "\n" end + return "\\markdownRendererHardLineBreak\n{}" + end + self.ellipsis = "\\markdownRendererEllipsis{}" + function self.thematic_break() + if not self.is_writing then return "" end + return "\\markdownRendererThematicBreak{}" + end + self.escaped_uri_chars = { + ["{"] = "\\markdownRendererLeftBrace{}", + ["}"] = "\\markdownRendererRightBrace{}", + ["\\"] = "\\markdownRendererBackslash{}", + ["\r"] = " ", + ["\n"] = " ", + } + self.escaped_minimal_strings = { + ["^^"] = "\\markdownRendererCircumflex" + .. "\\markdownRendererCircumflex ", + ["☒"] = "\\markdownRendererTickedBox{}", + ["⌛"] = "\\markdownRendererHalfTickedBox{}", + ["☐"] = "\\markdownRendererUntickedBox{}", + [entities.hex_entity('FFFD')] + = "\\markdownRendererReplacementCharacter{}", + } + self.escaped_strings = util.table_copy(self.escaped_minimal_strings) + self.escaped_strings[entities.hex_entity('00A0')] = self.nbsp + self.escaped_chars = { + ["{"] = "\\markdownRendererLeftBrace{}", + ["}"] = "\\markdownRendererRightBrace{}", + ["%"] = "\\markdownRendererPercentSign{}", + ["\\"] = "\\markdownRendererBackslash{}", + ["#"] = "\\markdownRendererHash{}", + ["$"] = "\\markdownRendererDollarSign{}", + ["&"] = "\\markdownRendererAmpersand{}", + ["_"] = "\\markdownRendererUnderscore{}", + ["^"] = "\\markdownRendererCircumflex{}", + ["~"] = "\\markdownRendererTilde{}", + ["|"] = "\\markdownRendererPipe{}", + [entities.hex_entity('0000')] + = "\\markdownRendererReplacementCharacter{}", + } + local function create_escaper(char_escapes, string_escapes) + local escape = util.escaper(char_escapes, string_escapes) + return function(s) + if self.flatten_inlines then return s end + return escape(s) + end + end + local escape_typographic_text = create_escaper( + self.escaped_chars, self.escaped_strings) + local escape_programmatic_text = create_escaper( + self.escaped_uri_chars, self.escaped_minimal_strings) + local escape_minimal = create_escaper( + {}, self.escaped_minimal_strings) + self.escape = escape_typographic_text + self.math = escape_minimal + if options.hybrid then + self.identifier = escape_minimal + self.string = escape_minimal + self.uri = escape_minimal + self.infostring = escape_minimal + else + self.identifier = escape_programmatic_text + self.string = escape_typographic_text + self.uri = escape_programmatic_text + self.infostring = escape_programmatic_text + end + function self.warning(t) + return {"\\markdownRendererWarning{", self.identifier(t), "}"} + end + function self.error(t) + return {"\\markdownRendererError{", self.identifier(t), "}"} + end + function self.code(s, attributes) + if self.flatten_inlines then return s end + local buf = {} + if attributes ~= nil then + table.insert(buf, + "\\markdownRendererCodeSpanAttributeContextBegin\n") + table.insert(buf, self.attributes(attributes)) + end + table.insert(buf, + {"\\markdownRendererCodeSpan{", self.escape(s), "}"}) + if attributes ~= nil then + table.insert(buf, + "\\markdownRendererCodeSpanAttributeContextEnd{}") + end + return buf + end + function self.link(lab, src, tit, attributes) + if self.flatten_inlines then return lab end + local buf = {} + if attributes ~= nil then + table.insert(buf, + "\\markdownRendererLinkAttributeContextBegin\n") + table.insert(buf, self.attributes(attributes)) + end + table.insert(buf, {"\\markdownRendererLink{",lab,"}", + "{",self.escape(src),"}", + "{",self.uri(src),"}", + "{",self.string(tit or ""),"}"}) + if attributes ~= nil then + table.insert(buf, + "\\markdownRendererLinkAttributeContextEnd{}") + end + return buf + end + function self.image(lab, src, tit, attributes) + if self.flatten_inlines then return lab end + local buf = {} + if attributes ~= nil then + table.insert(buf, + "\\markdownRendererImageAttributeContextBegin\n") + table.insert(buf, self.attributes(attributes)) + end + table.insert(buf, {"\\markdownRendererImage{",lab,"}", + "{",self.string(src),"}", + "{",self.uri(src),"}", + "{",self.string(tit or ""),"}"}) + if attributes ~= nil then + table.insert(buf, + "\\markdownRendererImageAttributeContextEnd{}") + end + return buf + end + function self.bulletlist(items,tight) + if not self.is_writing then return "" end + local buffer = {} + for _,item in ipairs(items) do + if item ~= "" then + buffer[#buffer + 1] = self.bulletitem(item) + end + end + local contents = util.intersperse(buffer,"\n") + if tight and options.tightLists then + return {"\\markdownRendererUlBeginTight\n",contents, + "\n\\markdownRendererUlEndTight "} + else + return {"\\markdownRendererUlBegin\n",contents, + "\n\\markdownRendererUlEnd "} + end + end + function self.bulletitem(s) + return {"\\markdownRendererUlItem ",s, + "\\markdownRendererUlItemEnd "} + end + function self.orderedlist(items,tight,startnum) + if not self.is_writing then return "" end + local buffer = {} + local num = startnum + for _,item in ipairs(items) do + if item ~= "" then + buffer[#buffer + 1] = self.ordereditem(item,num) + end + if num ~= nil and item ~= "" then + num = num + 1 + end + end + local contents = util.intersperse(buffer,"\n") + if tight and options.tightLists then + return {"\\markdownRendererOlBeginTight\n",contents, + "\n\\markdownRendererOlEndTight "} + else + return {"\\markdownRendererOlBegin\n",contents, + "\n\\markdownRendererOlEnd "} + end + end + function self.ordereditem(s,num) + if num ~= nil then + return {"\\markdownRendererOlItemWithNumber{",num,"}",s, + "\\markdownRendererOlItemEnd "} + else + return {"\\markdownRendererOlItem ",s, + "\\markdownRendererOlItemEnd "} + end + end + function self.inline_html_comment(contents) + if self.flatten_inlines then return contents end + return {"\\markdownRendererInlineHtmlComment{",contents,"}"} + end + function self.inline_html_tag(contents) + if self.flatten_inlines then return contents end + return {"\\markdownRendererInlineHtmlTag{", + self.string(contents),"}"} + end + function self.block_html_element(s) + if not self.is_writing then return "" end + local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") + return {"\\markdownRendererInputBlockHtmlElement{",name,"}"} + end + function self.emphasis(s) + if self.flatten_inlines then return s end + return {"\\markdownRendererEmphasis{",s,"}"} + end + function self.tickbox(f) + if f == 1.0 then + return "☒ " + elseif f == 0.0 then + return "☐ " + else + return "⌛ " + end + end + function self.strong(s) + if self.flatten_inlines then return s end + return {"\\markdownRendererStrongEmphasis{",s,"}"} + end + function self.blockquote(s) + if not self.is_writing then return "" end + return {"\\markdownRendererBlockQuoteBegin\n",s, + "\\markdownRendererBlockQuoteEnd "} + end + function self.verbatim(s) + if not self.is_writing then return "" end + s = s:gsub("\n$", "") + local name = util.cache_verbatim(options.cacheDir, s) + return {"\\markdownRendererInputVerbatim{",name,"}"} + end + function self.document(d) + local buf = {"\\markdownRendererDocumentBegin\n", d} + + -- pop all attributes + table.insert(buf, self.pop_attributes()) + + table.insert(buf, "\\markdownRendererDocumentEnd") + + return buf + end + local seen_identifiers = {} + local key_value_regex = "([^= ]+)%s*=%s*(.*)" + local function normalize_attributes(attributes, auto_identifiers) + -- normalize attributes + local normalized_attributes = {} + local has_explicit_identifiers = false + local key, value + for _, attribute in ipairs(attributes or {}) do + if attribute:sub(1, 1) == "#" then + table.insert(normalized_attributes, attribute) + has_explicit_identifiers = true + seen_identifiers[attribute:sub(2)] = true + elseif attribute:sub(1, 1) == "." then + table.insert(normalized_attributes, attribute) + else + key, value = attribute:match(key_value_regex) + if key:lower() == "id" then + table.insert(normalized_attributes, "#" .. value) + elseif key:lower() == "class" then + local classes = {} + for class in value:gmatch("%S+") do + table.insert(classes, class) + end + table.sort(classes) + for _, class in ipairs(classes) do + table.insert(normalized_attributes, "." .. class) + end + else + table.insert(normalized_attributes, attribute) + end + end + end + + -- if no explicit identifiers exist, add auto identifiers + if not has_explicit_identifiers and auto_identifiers ~= nil then + local seen_auto_identifiers = {} + for _, auto_identifier in ipairs(auto_identifiers) do + if seen_auto_identifiers[auto_identifier] == nil then + seen_auto_identifiers[auto_identifier] = true + if seen_identifiers[auto_identifier] == nil then + seen_identifiers[auto_identifier] = true + table.insert(normalized_attributes, + "#" .. auto_identifier) + else + local auto_identifier_number = 1 + while true do + local numbered_auto_identifier = auto_identifier .. "-" + .. auto_identifier_number + if seen_identifiers[numbered_auto_identifier] == nil then + seen_identifiers[numbered_auto_identifier] = true + table.insert(normalized_attributes, + "#" .. numbered_auto_identifier) + break + end + auto_identifier_number = auto_identifier_number + 1 + end + end + end + end + end + + -- sort and deduplicate normalized attributes + table.sort(normalized_attributes) + local seen_normalized_attributes = {} + local deduplicated_normalized_attributes = {} + for _, attribute in ipairs(normalized_attributes) do + if seen_normalized_attributes[attribute] == nil then + seen_normalized_attributes[attribute] = true + table.insert(deduplicated_normalized_attributes, attribute) + end + end + + return deduplicated_normalized_attributes + end + + function self.attributes(attributes, should_normalize_attributes) + local normalized_attributes + if should_normalize_attributes == false then + normalized_attributes = attributes + else + normalized_attributes = normalize_attributes(attributes) + end + + local buf = {} + local key, value + for _, attribute in ipairs(normalized_attributes) do + if attribute:sub(1, 1) == "#" then + table.insert(buf, {"\\markdownRendererAttributeIdentifier{", + attribute:sub(2), "}"}) + elseif attribute:sub(1, 1) == "." then + table.insert(buf, {"\\markdownRendererAttributeClassName{", + attribute:sub(2), "}"}) + else + key, value = attribute:match(key_value_regex) + table.insert(buf, {"\\markdownRendererAttributeKeyValue{", + key, "}{", value, "}"}) + end + end + + return buf + end + self.active_attributes = {} + self.attribute_type_levels = {} + setmetatable(self.attribute_type_levels, + { __index = function() return 0 end }) + local function apply_attributes() + local buf = {} + for i = 1, #self.active_attributes do + local start_output = self.active_attributes[i][3] + if start_output ~= nil then + table.insert(buf, start_output) + end + end + return buf + end + + local function tear_down_attributes() + local buf = {} + for i = #self.active_attributes, 1, -1 do + local end_output = self.active_attributes[i][4] + if end_output ~= nil then + table.insert(buf, end_output) + end + end + return buf + end + function self.push_attributes(attribute_type, attributes, + start_output, end_output) + local attribute_type_level + = self.attribute_type_levels[attribute_type] + self.attribute_type_levels[attribute_type] + = attribute_type_level + 1 + + -- index attributes in a hash table for easy lookup + attributes = attributes or {} + for i = 1, #attributes do + attributes[attributes[i]] = true + end + + local buf = {} + -- handle slicing + if attributes["#" .. self.slice_end_identifier] ~= nil and + self.slice_end_type == "^" then + if self.is_writing then + table.insert(buf, self.undosep()) + table.insert(buf, tear_down_attributes()) + end + self.is_writing = false + end + if attributes["#" .. self.slice_begin_identifier] ~= nil and + self.slice_begin_type == "^" then + table.insert(buf, apply_attributes()) + self.is_writing = true + end + if self.is_writing and start_output ~= nil then + table.insert(buf, start_output) + end + table.insert(self.active_attributes, + {attribute_type, attributes, + start_output, end_output}) + return buf + end + + function self.pop_attributes(attribute_type) + local buf = {} + -- pop attributes until we find attributes of correct type + -- or until no attributes remain + local current_attribute_type = false + while current_attribute_type ~= attribute_type and + #self.active_attributes > 0 do + local attributes, _, end_output + current_attribute_type, attributes, _, end_output = table.unpack( + self.active_attributes[#self.active_attributes]) + local attribute_type_level + = self.attribute_type_levels[current_attribute_type] + self.attribute_type_levels[current_attribute_type] + = attribute_type_level - 1 + if self.is_writing and end_output ~= nil then + table.insert(buf, end_output) + end + table.remove(self.active_attributes, #self.active_attributes) + -- handle slicing + if attributes["#" .. self.slice_end_identifier] ~= nil + and self.slice_end_type == "$" then + if self.is_writing then + table.insert(buf, self.undosep()) + table.insert(buf, tear_down_attributes()) + end + self.is_writing = false + end + if attributes["#" .. self.slice_begin_identifier] ~= nil and + self.slice_begin_type == "$" then + self.is_writing = true + table.insert(buf, apply_attributes()) + end + end + return buf + end + local function create_auto_identifier(s) + local buffer = {} + local prev_space = false + local letter_found = false + local normalized_s = s + if not options.unicodeNormalization + or options.unicodeNormalizationForm ~= "nfc" then + normalized_s = uni_algos.normalize.NFC(normalized_s) + end + + for _, code in utf8.codes(normalized_s) do + local char = utf8.char(code) + + -- Remove everything up to the first letter. + if not letter_found then + local is_letter = unicode.utf8.match(char, "%a") + if is_letter then + letter_found = true + else + goto continue + end + end + + -- Remove all non-alphanumeric characters, except underscores, + -- hyphens, and periods. + if not unicode.utf8.match(char, "[%w_%-%.%s]") then + goto continue + end + + -- Replace all spaces and newlines with hyphens. + if unicode.utf8.match(char, "[%s\n]") then + char = "-" + if prev_space then + goto continue + else + prev_space = true + end + else + -- Convert all alphabetic characters to lowercase. + char = unicode.utf8.lower(char) + prev_space = false + end + + table.insert(buffer, char) + + ::continue:: + end + + if prev_space then + table.remove(buffer) + end + + local identifier = #buffer == 0 and "section" + or table.concat(buffer, "") + return identifier + end + local function create_gfm_auto_identifier(s) + local buffer = {} + local prev_space = false + local letter_found = false + local normalized_s = s + if not options.unicodeNormalization + or options.unicodeNormalizationForm ~= "nfc" then + normalized_s = uni_algos.normalize.NFC(normalized_s) + end + + for _, code in utf8.codes(normalized_s) do + local char = utf8.char(code) + + -- Remove everything up to the first non-space. + if not letter_found then + local is_letter = unicode.utf8.match(char, "%S") + if is_letter then + letter_found = true + else + goto continue + end + end + + -- Remove all non-alphanumeric characters, except underscores + -- and hyphens. + if not unicode.utf8.match(char, "[%w_%-%s]") then + prev_space = false + goto continue + end + + -- Replace all spaces and newlines with hyphens. + if unicode.utf8.match(char, "[%s\n]") then + char = "-" + if prev_space then + goto continue + else + prev_space = true + end + else + -- Convert all alphabetic characters to lowercase. + char = unicode.utf8.lower(char) + prev_space = false + end + + table.insert(buffer, char) + + ::continue:: + end + + if prev_space then + table.remove(buffer) + end + + local identifier = #buffer == 0 and "section" + or table.concat(buffer, "") + return identifier + end + self.secbegin_text = "\\markdownRendererSectionBegin\n" + self.secend_text = "\n\\markdownRendererSectionEnd " + function self.heading(s, level, attributes) + local buf = {} + local flat_text, inlines = table.unpack(s) + + -- push empty attributes for implied sections + while self.attribute_type_levels["heading"] < level - 1 do + table.insert(buf, + self.push_attributes("heading", + nil, + self.secbegin_text, + self.secend_text)) + end + + -- pop attributes for sections that have ended + while self.attribute_type_levels["heading"] >= level do + table.insert(buf, self.pop_attributes("heading")) + end + + -- construct attributes for the new section + local auto_identifiers = {} + if self.options.autoIdentifiers then + table.insert(auto_identifiers, create_auto_identifier(flat_text)) + end + if self.options.gfmAutoIdentifiers then + table.insert(auto_identifiers, + create_gfm_auto_identifier(flat_text)) + end + local normalized_attributes = normalize_attributes(attributes, + auto_identifiers) + + -- push attributes for the new section + local start_output = {} + local end_output = {} + table.insert(start_output, self.secbegin_text) + table.insert(end_output, self.secend_text) + + table.insert(buf, self.push_attributes("heading", + normalized_attributes, + start_output, + end_output)) + assert(self.attribute_type_levels["heading"] == level) + + -- render the heading and its attributes + if self.is_writing and #normalized_attributes > 0 then + table.insert(buf, + "\\markdownRendererHeaderAttributeContextBegin\n") + table.insert(buf, self.attributes(normalized_attributes, false)) + end + + local cmd + level = level + options.shiftHeadings + if level <= 1 then + cmd = "\\markdownRendererHeadingOne" + elseif level == 2 then + cmd = "\\markdownRendererHeadingTwo" + elseif level == 3 then + cmd = "\\markdownRendererHeadingThree" + elseif level == 4 then + cmd = "\\markdownRendererHeadingFour" + elseif level == 5 then + cmd = "\\markdownRendererHeadingFive" + elseif level >= 6 then + cmd = "\\markdownRendererHeadingSix" + else + cmd = "" + end + if self.is_writing then + table.insert(buf, {cmd, "{", inlines, "}"}) + end + + if self.is_writing and #normalized_attributes > 0 then + table.insert(buf, "\\markdownRendererHeaderAttributeContextEnd{}") + end + + return buf + end + function self.get_state() + return { + is_writing=self.is_writing, + flatten_inlines=self.flatten_inlines, + active_attributes={table.unpack(self.active_attributes)}, + } + end + function self.set_state(s) + local previous_state = self.get_state() + for key, value in pairs(s) do + self[key] = value + end + return previous_state + end + function self.defer_call(f) + local previous_state = self.get_state() + return function(...) + local state = self.set_state(previous_state) + local return_value = f(...) + self.set_state(state) + return return_value + end + end + + return self +end +local parsers = {} +parsers.percent = P("%") +parsers.at = P("@") +parsers.comma = P(",") +parsers.asterisk = P("*") +parsers.dash = P("-") +parsers.plus = P("+") +parsers.underscore = P("_") +parsers.period = P(".") +parsers.hash = P("#") +parsers.dollar = P("$") +parsers.ampersand = P("&") +parsers.backtick = P("`") +parsers.less = P("<") +parsers.more = P(">") +parsers.space = P(" ") +parsers.squote = P("'") +parsers.dquote = P('"') +parsers.lparent = P("(") +parsers.rparent = P(")") +parsers.lbracket = P("[") +parsers.rbracket = P("]") +parsers.lbrace = P("{") +parsers.rbrace = P("}") +parsers.circumflex = P("^") +parsers.slash = P("/") +parsers.equal = P("=") +parsers.colon = P(":") +parsers.semicolon = P(";") +parsers.exclamation = P("!") +parsers.pipe = P("|") +parsers.tilde = P("~") +parsers.backslash = P("\\") +parsers.tab = P("\t") +parsers.newline = P("\n") + +parsers.digit = R("09") +parsers.hexdigit = R("09","af","AF") +parsers.letter = R("AZ","az") +parsers.alphanumeric = R("AZ","az","09") +parsers.keyword = parsers.letter + * (parsers.alphanumeric + parsers.dash)^0 + +parsers.doubleasterisks = P("**") +parsers.doubleunderscores = P("__") +parsers.doubletildes = P("~~") +parsers.fourspaces = P(" ") + +parsers.any = P(1) +parsers.succeed = P(true) +parsers.fail = P(false) + +parsers.internal_punctuation = S(":;,.?") +parsers.ascii_punctuation = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") +;(function() + local pathname = assert(kpse.find_file("UnicodeData.txt"), + [[Could not locate file "UnicodeData.txt"]]) + local file = assert(io.open(pathname, "r"), + [[Could not open file "UnicodeData.txt"]]) + local prefix_trees = {} + for line in file:lines() do + local codepoint, major_category = line:match("^(%x+);[^;]*;(%a)") + if major_category == "P" or major_category == "S" then + local code = unicode.utf8.char(tonumber(codepoint, 16)) + if prefix_trees[#code] == nil then + prefix_trees[#code] = {} + end + local node = prefix_trees[#code] + for i = 1, #code do + local byte = code:sub(i, i) + if i < #code then + if node[byte] == nil then + node[byte] = {} + end + node = node[byte] + else + table.insert(node, byte) + end + end + end + end + assert(file:close()) + local function depth_first_search(node, path, visit, leave) + visit(node, path) + for label, child in pairs(node) do + if type(child) == "table" then + depth_first_search(child, path .. label, visit, leave) + else + visit(child, path) + end + end + leave(node, path) + end + + parsers.punctuation = {} + for length, prefix_tree in pairs(prefix_trees) do + local subparsers = {} + depth_first_search(prefix_tree, "", function(node, path) + if type(node) == "table" then + subparsers[path] = parsers.fail + else + assert(type(node) == "string") + subparsers[path] = subparsers[path] + S(node) + end + end, function(_, path) + if #path > 0 then + local byte = path:sub(#path, #path) + local parent_path = path:sub(1, #path-1) + subparsers[parent_path] = subparsers[parent_path] + + S(byte) * subparsers[path] + else + parsers.punctuation[length] = subparsers[path] + end + end) + assert(parsers.punctuation[length] ~= nil) + end +end)() + +parsers.escapable = parsers.ascii_punctuation +parsers.anyescaped = parsers.backslash / "" + * parsers.escapable + + parsers.any + +parsers.spacechar = S("\t ") +parsers.spacing = S(" \n\r\t") +parsers.nonspacechar = parsers.any - parsers.spacing +parsers.optionalspace = parsers.spacechar^0 + +parsers.normalchar = parsers.any - (V("SpecialChar") + + parsers.spacing) +parsers.eof = -parsers.any +parsers.nonindentspace = parsers.space^-3 * - parsers.spacechar +parsers.indent = parsers.space^-3 * parsers.tab + + parsers.fourspaces / "" +parsers.linechar = P(1 - parsers.newline) + +parsers.blankline = parsers.optionalspace + * parsers.newline / "\n" +parsers.blanklines = parsers.blankline^0 +parsers.skipblanklines = ( parsers.optionalspace + * parsers.newline)^0 +parsers.indentedline = parsers.indent /"" + * C( parsers.linechar^1 + * parsers.newline^-1) +parsers.optionallyindentedline = parsers.indent^-1 /"" + * C( parsers.linechar^1 + * parsers.newline^-1) +parsers.sp = parsers.spacing^0 +parsers.spnl = parsers.optionalspace + * ( parsers.newline + * parsers.optionalspace)^-1 +parsers.line = parsers.linechar^0 * parsers.newline +parsers.nonemptyline = parsers.line - parsers.blankline + +parsers.leader = parsers.space^-3 + +local function has_trail(indent_table) + return indent_table ~= nil and + indent_table.trail ~= nil and + next(indent_table.trail) ~= nil +end + +local function has_indents(indent_table) + return indent_table ~= nil and + indent_table.indents ~= nil and + next(indent_table.indents) ~= nil +end + +local function add_trail(indent_table, trail_info) + indent_table.trail = trail_info + return indent_table +end + +local function remove_trail(indent_table) + indent_table.trail = nil + return indent_table +end + +local function update_indent_table(indent_table, new_indent, add) + indent_table = remove_trail(indent_table) + + if not has_indents(indent_table) then + indent_table.indents = {} + end + + if add then + indent_table.indents[#indent_table.indents + 1] = new_indent + else + if indent_table.indents[#indent_table.indents].name + == new_indent.name then + indent_table.indents[#indent_table.indents] = nil + end + end + + return indent_table +end + +local function remove_indent(name) + local remove_indent_level = + function(s, i, indent_table) -- luacheck: ignore s i + indent_table = update_indent_table(indent_table, {name=name}, + false) + return true, indent_table + end + + return Cg(Cmt(Cb("indent_info"), remove_indent_level), "indent_info") +end + +local function process_starter_spacing(indent, spacing, + minimum, left_strip_length) + left_strip_length = left_strip_length or 0 + + local count = 0 + local tab_value = 4 - (indent) % 4 + + local code_started, minimum_found = false, false + local code_start, minimum_remainder = "", "" + + local left_total_stripped = 0 + local full_remainder = "" + + if spacing ~= nil then + for i = 1, #spacing do + local character = spacing:sub(i, i) + + if character == "\t" then + count = count + tab_value + tab_value = 4 + elseif character == " " then + count = count + 1 + tab_value = 4 - (1 - tab_value) % 4 + end + + if (left_strip_length ~= 0) then + local possible_to_strip = math.min(count, left_strip_length) + count = count - possible_to_strip + left_strip_length = left_strip_length - possible_to_strip + left_total_stripped = left_total_stripped + possible_to_strip + else + full_remainder = full_remainder .. character + end + + if (minimum_found) then + minimum_remainder = minimum_remainder .. character + elseif (count >= minimum) then + minimum_found = true + minimum_remainder = minimum_remainder + .. string.rep(" ", count - minimum) + end + + if (code_started) then + code_start = code_start .. character + elseif (count >= minimum + 4) then + code_started = true + code_start = code_start + .. string.rep(" ", count - (minimum + 4)) + end + end + end + + local remainder + if (code_started) then + remainder = code_start + else + remainder = string.rep(" ", count - minimum) + end + + local is_minimum = count >= minimum + return { + is_code = code_started, + remainder = remainder, + left_total_stripped = left_total_stripped, + is_minimum = is_minimum, + minimum_remainder = minimum_remainder, + total_length = count, + full_remainder = full_remainder + } +end + +local function count_indent_tab_level(indent_table) + local count = 0 + if not has_indents(indent_table) then + return count + end + + for i=1, #indent_table.indents do + count = count + indent_table.indents[i].length + end + return count +end + +local function total_delimiter_length(delimiter) + local count = 0 + if type(delimiter) == "string" then return #delimiter end + for _, value in pairs(delimiter) do + count = count + total_delimiter_length(value) + end + return count +end + +local function process_starter_indent(_, _, indent_table, starter, + is_blank, indent_type, breakable) + local last_trail = starter[1] + local delimiter = starter[2] + local raw_new_trail = starter[3] + + if indent_type == "bq" and not breakable then + indent_table.ignore_blockquote_blank = true + end + + if has_trail(indent_table) then + local trail = indent_table.trail + if trail.is_code then + return false + end + last_trail = trail.remainder + else + local sp = process_starter_spacing(0, last_trail, 0, 0) + + if sp.is_code then + return false + end + last_trail = sp.remainder + end + + local preceding_indentation = count_indent_tab_level(indent_table) % 4 + local last_trail_length = #last_trail + local delimiter_length = total_delimiter_length(delimiter) + + local total_indent_level = preceding_indentation + last_trail_length + + delimiter_length + + local sp = {} + if not is_blank then + sp = process_starter_spacing(total_indent_level, raw_new_trail, + 0, 1) + end + + local del_trail_length = sp.left_total_stripped + if is_blank then + del_trail_length = 1 + elseif not sp.is_code then + del_trail_length = del_trail_length + #sp.remainder + end + + local indent_length = last_trail_length + delimiter_length + + del_trail_length + local new_indent_info = {name=indent_type, length=indent_length} + + indent_table = update_indent_table(indent_table, new_indent_info, + true) + indent_table = add_trail(indent_table, + {is_code=sp.is_code, + remainder=sp.remainder, + total_length=sp.total_length, + full_remainder=sp.full_remainder}) + + return true, indent_table +end + +local function decode_pattern(name) + local delimeter = parsers.succeed + if name == "bq" then + delimeter = parsers.more + end + + return C(parsers.optionalspace) * C(delimeter) + * C(parsers.optionalspace) * Cp() +end + +local function left_blank_starter(indent_table) + local blank_starter_index + + if not has_indents(indent_table) then + return + end + + for i = #indent_table.indents,1,-1 do + local value = indent_table.indents[i] + if value.name == "li" then + blank_starter_index = i + else + break + end + end + + return blank_starter_index +end + +local function traverse_indent(s, i, indent_table, is_optional, + is_blank, current_line_indents) + local new_index = i + + local preceding_indentation = 0 + local current_trail = {} + + local blank_starter = left_blank_starter(indent_table) + + if current_line_indents == nil then + current_line_indents = {} + end + + for index = 1,#indent_table.indents do + local value = indent_table.indents[index] + local pattern = decode_pattern(value.name) + + -- match decoded pattern + local new_indent_info = lpeg.match(Ct(pattern), s, new_index) + if new_indent_info == nil then + local blankline_end = lpeg.match( + Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index) + if is_optional or not indent_table.ignore_blockquote_blank + or not blankline_end then + return is_optional, new_index, current_trail, + current_line_indents + end + + return traverse_indent(s, tonumber(blankline_end.pos), + indent_table, is_optional, is_blank, + current_line_indents) + end + + local raw_last_trail = new_indent_info[1] + local delimiter = new_indent_info[2] + local raw_new_trail = new_indent_info[3] + local next_index = new_indent_info[4] + + local space_only = delimiter == "" + + -- check previous trail + if not space_only and next(current_trail) == nil then + local sp = process_starter_spacing(0, raw_last_trail, 0, 0) + current_trail = {is_code=sp.is_code, remainder=sp.remainder, + total_length=sp.total_length, + full_remainder=sp.full_remainder} + end + + if next(current_trail) ~= nil then + if not space_only and current_trail.is_code then + return is_optional, new_index, current_trail, + current_line_indents + end + if current_trail.internal_remainder ~= nil then + raw_last_trail = current_trail.internal_remainder + end + end + + local raw_last_trail_length = 0 + local delimiter_length = 0 + + if not space_only then + delimiter_length = #delimiter + raw_last_trail_length = #raw_last_trail + end + + local total_indent_level = preceding_indentation + + raw_last_trail_length + delimiter_length + + local spacing_to_process + local minimum = 0 + local left_strip_length = 0 + + if not space_only then + spacing_to_process = raw_new_trail + left_strip_length = 1 + else + spacing_to_process = raw_last_trail + minimum = value.length + end + + local sp = process_starter_spacing(total_indent_level, + spacing_to_process, minimum, + left_strip_length) + + if space_only and not sp.is_minimum then + return is_optional or (is_blank and blank_starter <= index), + new_index, current_trail, current_line_indents + end + + local indent_length = raw_last_trail_length + delimiter_length + + sp.left_total_stripped + + -- update info for the next pattern + if not space_only then + preceding_indentation = preceding_indentation + indent_length + else + preceding_indentation = preceding_indentation + value.length + end + + current_trail = {is_code=sp.is_code, remainder=sp.remainder, + internal_remainder=sp.minimum_remainder, + total_length=sp.total_length, + full_remainder=sp.full_remainder} + + current_line_indents[#current_line_indents + 1] = new_indent_info + new_index = next_index + end + + return true, new_index, current_trail, current_line_indents +end + +local function check_trail(expect_code, is_code) + return (expect_code and is_code) or (not expect_code and not is_code) +end + +local check_trail_joined = + function(s, i, indent_table, -- luacheck: ignore s i + spacing, expect_code, omit_remainder) + local is_code + local remainder + + if has_trail(indent_table) then + local trail = indent_table.trail + is_code = trail.is_code + if is_code then + remainder = trail.remainder + else + remainder = trail.full_remainder + end + else + local sp = process_starter_spacing(0, spacing, 0, 0) + is_code = sp.is_code + if is_code then + remainder = sp.remainder + else + remainder = sp.full_remainder + end + end + + local result = check_trail(expect_code, is_code) + if omit_remainder then + return result + end + return result, remainder + end + +local check_trail_length = + function(s, i, indent_table, -- luacheck: ignore s i + spacing, min, max) + local trail + + if has_trail(indent_table) then + trail = indent_table.trail + else + trail = process_starter_spacing(0, spacing, 0, 0) + end + + local total_length = trail.total_length + if total_length == nil then + return false + end + + return min <= total_length and total_length <= max + end + +local function check_continuation_indentation(s, i, indent_table, + is_optional, is_blank) + if not has_indents(indent_table) then + return true + end + + local passes, new_index, current_trail, current_line_indents = + traverse_indent(s, i, indent_table, is_optional, is_blank) + + if passes then + indent_table.current_line_indents = current_line_indents + indent_table = add_trail(indent_table, current_trail) + return new_index, indent_table + end + return false +end + +local function get_last_indent_name(indent_table) + if has_indents(indent_table) then + return indent_table.indents[#indent_table.indents].name + end +end + +local function remove_remainder_if_blank(indent_table, remainder) + if get_last_indent_name(indent_table) == "li" then + return "" + end + return remainder +end + +local check_trail_type = + function(s, i, -- luacheck: ignore s i + trail, spacing, trail_type) + if trail == nil then + trail = process_starter_spacing(0, spacing, 0, 0) + end + + if trail_type == "non-code" then + return check_trail(false, trail.is_code) + end + if trail_type == "code" then + return check_trail(true, trail.is_code) + end + if trail_type == "full-code" then + if (trail.is_code) then + return i, trail.remainder + end + return i, "" + end + if trail_type == "full-any" then + return i, trail.internal_remainder + end + end + +local trail_freezing = + function(s, i, -- luacheck: ignore s i + indent_table, is_freezing) + if is_freezing then + if indent_table.is_trail_frozen then + indent_table.trail = indent_table.frozen_trail + else + indent_table.frozen_trail = indent_table.trail + indent_table.is_trail_frozen = true + end + else + indent_table.frozen_trail = nil + indent_table.is_trail_frozen = false + end + return true, indent_table + end + +local check_continuation_indentation_and_trail = + function (s, i, indent_table, is_optional, is_blank, trail_type, + reset_rem, omit_remainder) + if not has_indents(indent_table) then + local spacing, new_index = lpeg.match( C(parsers.spacechar^0) + * Cp(), s, i) + local result, remainder = check_trail_type(s, i, + indent_table.trail, spacing, trail_type) + if remainder == nil then + if result then + return new_index + end + return false + end + if result then + return new_index, remainder + end + return false + end + + local passes, new_index, current_trail = traverse_indent(s, i, + indent_table, is_optional, is_blank) + + if passes then + local spacing + if current_trail == nil then + local newer_spacing, newer_index = lpeg.match( + C(parsers.spacechar^0) * Cp(), s, i) + current_trail = process_starter_spacing(0, newer_spacing, 0, 0) + new_index = newer_index + spacing = newer_spacing + else + spacing = current_trail.remainder + end + local result, remainder = check_trail_type(s, new_index, + current_trail, spacing, trail_type) + if remainder == nil or omit_remainder then + if result then + return new_index + end + return false + end + + if is_blank and reset_rem then + remainder = remove_remainder_if_blank(indent_table, remainder) + end + if result then + return new_index, remainder + end + return false + end + return false + end + +parsers.check_trail = Cmt( Cb("indent_info") * C(parsers.spacechar^0) + * Cc(false), check_trail_joined) + +parsers.check_trail_no_rem = Cmt( Cb("indent_info") + * C(parsers.spacechar^0) * Cc(false) + * Cc(true), check_trail_joined) + +parsers.check_code_trail = Cmt( Cb("indent_info") + * C(parsers.spacechar^0) + * Cc(true), check_trail_joined) + +parsers.check_trail_length_range = function(min, max) + return Cmt( Cb("indent_info") * C(parsers.spacechar^0) * Cc(min) + * Cc(max), check_trail_length) +end + +parsers.check_trail_length = function(n) + return parsers.check_trail_length_range(n, n) +end + +parsers.freeze_trail = Cg( Cmt(Cb("indent_info") + * Cc(true), trail_freezing), "indent_info") + +parsers.unfreeze_trail = Cg(Cmt(Cb("indent_info") * Cc(false), + trail_freezing), "indent_info") + +parsers.check_minimal_indent = Cmt(Cb("indent_info") * Cc(false), + check_continuation_indentation) + +parsers.check_optional_indent = Cmt(Cb("indent_info") * Cc(true), + check_continuation_indentation) + +parsers.check_minimal_blank_indent + = Cmt( Cb("indent_info") * Cc(false) + * Cc(true) + , check_continuation_indentation) + + +parsers.check_minimal_indent_and_trail = + Cmt( Cb("indent_info") + * Cc(false) * Cc(false) * Cc("non-code") * Cc(true) + , check_continuation_indentation_and_trail) + +parsers.check_minimal_indent_and_code_trail = + Cmt( Cb("indent_info") + * Cc(false) * Cc(false) * Cc("code") * Cc(false) + , check_continuation_indentation_and_trail) + +parsers.check_minimal_blank_indent_and_full_code_trail = + Cmt( Cb("indent_info") + * Cc(false) * Cc(true) * Cc("full-code") * Cc(true) + , check_continuation_indentation_and_trail) + +parsers.check_minimal_indent_and_any_trail = + Cmt( Cb("indent_info") + * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false) + , check_continuation_indentation_and_trail) + +parsers.check_minimal_blank_indent_and_any_trail = + Cmt( Cb("indent_info") + * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false) + , check_continuation_indentation_and_trail) + +parsers.check_minimal_blank_indent_and_any_trail_no_rem = + Cmt( Cb("indent_info") + * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(true) + , check_continuation_indentation_and_trail) + +parsers.check_optional_indent_and_any_trail = + Cmt( Cb("indent_info") + * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false) + , check_continuation_indentation_and_trail) + +parsers.check_optional_blank_indent_and_any_trail = + Cmt( Cb("indent_info") + * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false) + , check_continuation_indentation_and_trail) + + +parsers.spnlc_noexc = parsers.optionalspace + * ( parsers.newline + * parsers.check_minimal_indent_and_any_trail)^-1 + +parsers.spnlc = parsers.optionalspace + * (V("EndlineNoSub"))^-1 + +parsers.spnlc_sep = parsers.optionalspace * V("EndlineNoSub") + + parsers.spacechar^1 + +parsers.only_blank = parsers.spacechar^0 + * (parsers.newline + parsers.eof) + +parsers.commented_line_letter = parsers.linechar + + parsers.newline + - parsers.backslash + - parsers.percent +parsers.commented_line = Cg(Cc(""), "backslashes") + * ((#(parsers.commented_line_letter + - parsers.newline) + * Cb("backslashes") + * Cs(parsers.commented_line_letter + - parsers.newline)^1 -- initial + * Cg(Cc(""), "backslashes")) + + #(parsers.backslash * parsers.backslash) + * Cg((parsers.backslash -- even backslash + * parsers.backslash)^1, "backslashes") + + (parsers.backslash + * (#parsers.percent + * Cb("backslashes") + / function(backslashes) + return string.rep("\\", #backslashes / 2) + end + * C(parsers.percent) + + #parsers.commented_line_letter + * Cb("backslashes") + * Cc("\\") + * C(parsers.commented_line_letter)) + * Cg(Cc(""), "backslashes")))^0 + * (#parsers.percent + * Cb("backslashes") + / function(backslashes) + return string.rep("\\", #backslashes / 2) + end + * ((parsers.percent -- comment + * parsers.line + * #parsers.blankline) -- blank line + / "\n" + + parsers.percent -- comment + * parsers.line + * parsers.optionalspace) -- leading spaces + + #(parsers.newline) + * Cb("backslashes") + * C(parsers.newline)) + +parsers.chunk = parsers.line * (parsers.optionallyindentedline + - parsers.blankline)^0 + +parsers.attribute_key_char = parsers.alphanumeric + S("-_:.") +parsers.attribute_raw_char = parsers.alphanumeric + S("-_") +parsers.attribute_key = (parsers.attribute_key_char + - parsers.dash - parsers.digit) + * parsers.attribute_key_char^0 +parsers.attribute_value = ( (parsers.dquote / "") + * (parsers.anyescaped - parsers.dquote)^0 + * (parsers.dquote / "")) + + ( (parsers.squote / "") + * (parsers.anyescaped - parsers.squote)^0 + * (parsers.squote / "")) + + ( parsers.anyescaped + - parsers.dquote + - parsers.rbrace + - parsers.space)^0 +parsers.attribute_identifier = parsers.attribute_key_char^1 +parsers.attribute_classname = parsers.letter + * parsers.attribute_key_char^0 +parsers.attribute_raw = parsers.attribute_raw_char^1 + +parsers.attribute = (parsers.dash * Cc(".unnumbered")) + + C( parsers.hash + * parsers.attribute_identifier) + + C( parsers.period + * parsers.attribute_classname) + + Cs( parsers.attribute_key + * parsers.optionalspace + * parsers.equal + * parsers.optionalspace + * parsers.attribute_value) +parsers.attributes = parsers.lbrace + * parsers.optionalspace + * parsers.attribute + * (parsers.spacechar^1 + * parsers.attribute)^0 + * parsers.optionalspace + * parsers.rbrace + +parsers.raw_attribute = parsers.lbrace + * parsers.optionalspace + * parsers.equal + * C(parsers.attribute_raw) + * parsers.optionalspace + * parsers.rbrace + +-- block followed by 0 or more optionally +-- indented blocks with first line indented. +parsers.indented_blocks = function(bl) + return Cs( bl + * ( parsers.blankline^1 + * parsers.indent + * -parsers.blankline + * bl)^0 + * (parsers.blankline^1 + parsers.eof) ) +end +local function repeat_between(pattern, min, max) + return -pattern^(max + 1) * pattern^min +end + +parsers.hexentity = parsers.ampersand * parsers.hash * C(S("Xx")) + * C(repeat_between(parsers.hexdigit, 1, 6)) + * parsers.semicolon +parsers.decentity = parsers.ampersand * parsers.hash + * C(repeat_between(parsers.digit, 1, 7)) + * parsers.semicolon +parsers.tagentity = parsers.ampersand * C(parsers.alphanumeric^1) + * parsers.semicolon + +parsers.html_entities + = parsers.hexentity / entities.hex_entity_with_x_char + + parsers.decentity / entities.dec_entity + + parsers.tagentity / entities.char_entity +parsers.bullet = function(bullet_char, interrupting) + local allowed_end + if interrupting then + allowed_end = C(parsers.spacechar^1) * #parsers.linechar + else + allowed_end = C(parsers.spacechar^1) + + #(parsers.newline + parsers.eof) + end + return parsers.check_trail + * Ct(C(bullet_char) * Cc("")) + * allowed_end +end + +local function tickbox(interior) + return parsers.optionalspace * parsers.lbracket + * interior * parsers.rbracket * parsers.spacechar^1 +end + +parsers.ticked_box = tickbox(S("xX")) * Cc(1.0) +parsers.halfticked_box = tickbox(S("./")) * Cc(0.5) +parsers.unticked_box = tickbox(parsers.spacechar^1) * Cc(0.0) + +parsers.openticks = Cg(parsers.backtick^1, "ticks") + +local function captures_equal_length(_,i,a,b) + return #a == #b and i +end + +parsers.closeticks = Cmt(C(parsers.backtick^1) + * Cb("ticks"), captures_equal_length) + +parsers.intickschar = (parsers.any - S("\n\r`")) + + V("NoSoftLineBreakEndline") + + (parsers.backtick^1 - parsers.closeticks) + +local function process_inticks(s) + s = s:gsub("\n", " ") + s = s:gsub("^ (.*) $", "%1") + return s +end + +parsers.inticks = parsers.openticks + * C(parsers.space^0) + * parsers.closeticks + + parsers.openticks + * Cs(Cs(parsers.intickschar^0) / process_inticks) + * parsers.closeticks + +-- case-insensitive match (we assume s is lowercase) +-- must be single byte encoding +parsers.keyword_exact = function(s) + local parser = P(0) + for i=1,#s do + local c = s:sub(i,i) + local m = c .. upper(c) + parser = parser * S(m) + end + return parser +end + +parsers.special_block_keyword = + parsers.keyword_exact("pre") + + parsers.keyword_exact("script") + + parsers.keyword_exact("style") + + parsers.keyword_exact("textarea") + +parsers.block_keyword = + parsers.keyword_exact("address") + + parsers.keyword_exact("article") + + parsers.keyword_exact("aside") + + parsers.keyword_exact("base") + + parsers.keyword_exact("basefont") + + parsers.keyword_exact("blockquote") + + parsers.keyword_exact("body") + + parsers.keyword_exact("caption") + + parsers.keyword_exact("center") + + parsers.keyword_exact("col") + + parsers.keyword_exact("colgroup") + + parsers.keyword_exact("dd") + + parsers.keyword_exact("details") + + parsers.keyword_exact("dialog") + + parsers.keyword_exact("dir") + + parsers.keyword_exact("div") + + parsers.keyword_exact("dl") + + parsers.keyword_exact("dt") + + parsers.keyword_exact("fieldset") + + parsers.keyword_exact("figcaption") + + parsers.keyword_exact("figure") + + parsers.keyword_exact("footer") + + parsers.keyword_exact("form") + + parsers.keyword_exact("frame") + + parsers.keyword_exact("frameset") + + parsers.keyword_exact("h1") + + parsers.keyword_exact("h2") + + parsers.keyword_exact("h3") + + parsers.keyword_exact("h4") + + parsers.keyword_exact("h5") + + parsers.keyword_exact("h6") + + parsers.keyword_exact("head") + + parsers.keyword_exact("header") + + parsers.keyword_exact("hr") + + parsers.keyword_exact("html") + + parsers.keyword_exact("iframe") + + parsers.keyword_exact("legend") + + parsers.keyword_exact("li") + + parsers.keyword_exact("link") + + parsers.keyword_exact("main") + + parsers.keyword_exact("menu") + + parsers.keyword_exact("menuitem") + + parsers.keyword_exact("nav") + + parsers.keyword_exact("noframes") + + parsers.keyword_exact("ol") + + parsers.keyword_exact("optgroup") + + parsers.keyword_exact("option") + + parsers.keyword_exact("p") + + parsers.keyword_exact("param") + + parsers.keyword_exact("section") + + parsers.keyword_exact("source") + + parsers.keyword_exact("summary") + + parsers.keyword_exact("table") + + parsers.keyword_exact("tbody") + + parsers.keyword_exact("td") + + parsers.keyword_exact("tfoot") + + parsers.keyword_exact("th") + + parsers.keyword_exact("thead") + + parsers.keyword_exact("title") + + parsers.keyword_exact("tr") + + parsers.keyword_exact("track") + + parsers.keyword_exact("ul") + +-- end conditions +parsers.html_blankline_end_condition + = parsers.linechar^0 + * ( parsers.newline + * (parsers.check_minimal_blank_indent_and_any_trail + * #parsers.blankline + + parsers.check_minimal_indent_and_any_trail) + * parsers.linechar^1)^0 + * (parsers.newline^-1 / "") + +local function remove_trailing_blank_lines(s) + return s:gsub("[\n\r]+%s*$", "") +end + +parsers.html_until_end = function(end_marker) + return Cs(Cs((parsers.newline + * (parsers.check_minimal_blank_indent_and_any_trail + * #parsers.blankline + + parsers.check_minimal_indent_and_any_trail) + + parsers.linechar - end_marker)^0 + * parsers.linechar^0 * parsers.newline^-1) + / remove_trailing_blank_lines) +end + +-- attributes +parsers.html_attribute_spacing = parsers.optionalspace + * V("NoSoftLineBreakEndline") + * parsers.optionalspace + + parsers.spacechar^1 + +parsers.html_attribute_name = ( parsers.letter + + parsers.colon + + parsers.underscore) + * ( parsers.alphanumeric + + parsers.colon + + parsers.underscore + + parsers.period + + parsers.dash)^0 + +parsers.html_attribute_value = parsers.squote + * (parsers.linechar - parsers.squote)^0 + * parsers.squote + + parsers.dquote + * (parsers.linechar - parsers.dquote)^0 + * parsers.dquote + + ( parsers.any + - parsers.spacechar + - parsers.newline + - parsers.dquote + - parsers.squote + - parsers.backtick + - parsers.equal + - parsers.less + - parsers.more)^1 + +parsers.html_inline_attribute_value = parsers.squote + * (V("NoSoftLineBreakEndline") + + parsers.any + - parsers.blankline^2 + - parsers.squote)^0 + * parsers.squote + + parsers.dquote + * (V("NoSoftLineBreakEndline") + + parsers.any + - parsers.blankline^2 + - parsers.dquote)^0 + * parsers.dquote + + (parsers.any + - parsers.spacechar + - parsers.newline + - parsers.dquote + - parsers.squote + - parsers.backtick + - parsers.equal + - parsers.less + - parsers.more)^1 + +parsers.html_attribute_value_specification + = parsers.optionalspace + * parsers.equal + * parsers.optionalspace + * parsers.html_attribute_value + +parsers.html_spnl = parsers.optionalspace + * (V("NoSoftLineBreakEndline") + * parsers.optionalspace)^-1 + +parsers.html_inline_attribute_value_specification + = parsers.html_spnl + * parsers.equal + * parsers.html_spnl + * parsers.html_inline_attribute_value + +parsers.html_attribute + = parsers.html_attribute_spacing + * parsers.html_attribute_name + * parsers.html_inline_attribute_value_specification^-1 + +parsers.html_non_newline_attribute + = parsers.spacechar^1 + * parsers.html_attribute_name + * parsers.html_attribute_value_specification^-1 + +parsers.nested_breaking_blank = parsers.newline + * parsers.check_minimal_blank_indent + * parsers.blankline + +parsers.html_comment_start = P("<!--") + +parsers.html_comment_end = P("-->") + +parsers.html_comment + = Cs( parsers.html_comment_start + * parsers.html_until_end(parsers.html_comment_end)) + +parsers.html_inline_comment = (parsers.html_comment_start / "") + * -P(">") * -P("->") + * Cs(( V("NoSoftLineBreakEndline") + + parsers.any + - parsers.nested_breaking_blank + - parsers.html_comment_end)^0) + * (parsers.html_comment_end / "") + +parsers.html_cdatasection_start = P("<![CDATA[") + +parsers.html_cdatasection_end = P("]]>") + +parsers.html_cdatasection + = Cs( parsers.html_cdatasection_start + * parsers.html_until_end(parsers.html_cdatasection_end)) + +parsers.html_inline_cdatasection + = parsers.html_cdatasection_start + * Cs(V("NoSoftLineBreakEndline") + parsers.any + - parsers.nested_breaking_blank - parsers.html_cdatasection_end)^0 + * parsers.html_cdatasection_end + +parsers.html_declaration_start = P("<!") * parsers.letter + +parsers.html_declaration_end = P(">") + +parsers.html_declaration + = Cs( parsers.html_declaration_start + * parsers.html_until_end(parsers.html_declaration_end)) + +parsers.html_inline_declaration + = parsers.html_declaration_start + * Cs(V("NoSoftLineBreakEndline") + parsers.any + - parsers.nested_breaking_blank - parsers.html_declaration_end)^0 + * parsers.html_declaration_end + +parsers.html_instruction_start = P("<?") + +parsers.html_instruction_end = P("?>") + +parsers.html_instruction + = Cs( parsers.html_instruction_start + * parsers.html_until_end(parsers.html_instruction_end)) + +parsers.html_inline_instruction = parsers.html_instruction_start + * Cs( V("NoSoftLineBreakEndline") + + parsers.any + - parsers.nested_breaking_blank + - parsers.html_instruction_end)^0 + * parsers.html_instruction_end + +parsers.html_blankline = parsers.newline + * parsers.optionalspace + * parsers.newline + +parsers.html_tag_start = parsers.less + +parsers.html_tag_closing_start = parsers.less + * parsers.slash + +parsers.html_tag_end = parsers.html_spnl + * parsers.more + +parsers.html_empty_tag_end = parsers.html_spnl + * parsers.slash + * parsers.more + +-- opening tags +parsers.html_any_open_inline_tag = parsers.html_tag_start + * parsers.keyword + * parsers.html_attribute^0 + * parsers.html_tag_end + +parsers.html_any_open_tag = parsers.html_tag_start + * parsers.keyword + * parsers.html_non_newline_attribute^0 + * parsers.html_tag_end + +parsers.html_open_tag = parsers.html_tag_start + * parsers.block_keyword + * parsers.html_attribute^0 + * parsers.html_tag_end + +parsers.html_open_special_tag = parsers.html_tag_start + * parsers.special_block_keyword + * parsers.html_attribute^0 + * parsers.html_tag_end + +-- incomplete tags +parsers.incomplete_tag_following = parsers.spacechar + + parsers.more + + parsers.slash * parsers.more + + #(parsers.newline + parsers.eof) + +parsers.incomplete_special_tag_following = parsers.spacechar + + parsers.more + + #( parsers.newline + + parsers.eof) + +parsers.html_incomplete_open_tag = parsers.html_tag_start + * parsers.block_keyword + * parsers.incomplete_tag_following + +parsers.html_incomplete_open_special_tag + = parsers.html_tag_start + * parsers.special_block_keyword + * parsers.incomplete_special_tag_following + +parsers.html_incomplete_close_tag = parsers.html_tag_closing_start + * parsers.block_keyword + * parsers.incomplete_tag_following + +parsers.html_incomplete_close_special_tag + = parsers.html_tag_closing_start + * parsers.special_block_keyword + * parsers.incomplete_tag_following + +-- closing tags +parsers.html_close_tag = parsers.html_tag_closing_start + * parsers.block_keyword + * parsers.html_tag_end + +parsers.html_any_close_tag = parsers.html_tag_closing_start + * parsers.keyword + * parsers.html_tag_end + +parsers.html_close_special_tag = parsers.html_tag_closing_start + * parsers.special_block_keyword + * parsers.html_tag_end + +-- empty tags +parsers.html_any_empty_inline_tag = parsers.html_tag_start + * parsers.keyword + * parsers.html_attribute^0 + * parsers.html_empty_tag_end + +parsers.html_any_empty_tag = parsers.html_tag_start + * parsers.keyword + * parsers.html_non_newline_attribute^0 + * parsers.optionalspace + * parsers.slash + * parsers.more + +parsers.html_empty_tag = parsers.html_tag_start + * parsers.block_keyword + * parsers.html_attribute^0 + * parsers.html_empty_tag_end + +parsers.html_empty_special_tag = parsers.html_tag_start + * parsers.special_block_keyword + * parsers.html_attribute^0 + * parsers.html_empty_tag_end + +parsers.html_incomplete_blocks + = parsers.html_incomplete_open_tag + + parsers.html_incomplete_open_special_tag + + parsers.html_incomplete_close_tag + +-- parse special html blocks +parsers.html_blankline_ending_special_block_opening + = ( parsers.html_close_special_tag + + parsers.html_empty_special_tag) + * #( parsers.optionalspace + * (parsers.newline + parsers.eof)) + +parsers.html_blankline_ending_special_block + = parsers.html_blankline_ending_special_block_opening + * parsers.html_blankline_end_condition + +parsers.html_special_block_opening + = parsers.html_incomplete_open_special_tag + - parsers.html_empty_special_tag + +parsers.html_closing_special_block + = parsers.html_special_block_opening + * parsers.html_until_end(parsers.html_close_special_tag) + +parsers.html_special_block + = parsers.html_blankline_ending_special_block + + parsers.html_closing_special_block + +-- parse html blocks +parsers.html_block_opening = parsers.html_incomplete_open_tag + + parsers.html_incomplete_close_tag + +parsers.html_block = parsers.html_block_opening + * parsers.html_blankline_end_condition + +-- parse any html blocks +parsers.html_any_block_opening + = ( parsers.html_any_open_tag + + parsers.html_any_close_tag + + parsers.html_any_empty_tag) + * #(parsers.optionalspace * (parsers.newline + parsers.eof)) + +parsers.html_any_block = parsers.html_any_block_opening + * parsers.html_blankline_end_condition + +parsers.html_inline_comment_full = parsers.html_comment_start + * -P(">") * -P("->") + * Cs(( V("NoSoftLineBreakEndline") + + parsers.any - P("--") + - parsers.nested_breaking_blank + - parsers.html_comment_end)^0) + * parsers.html_comment_end + +parsers.html_inline_tags = parsers.html_inline_comment_full + + parsers.html_any_empty_inline_tag + + parsers.html_inline_instruction + + parsers.html_inline_cdatasection + + parsers.html_inline_declaration + + parsers.html_any_open_inline_tag + + parsers.html_any_close_tag + +parsers.urlchar = parsers.anyescaped + - parsers.newline + - parsers.more + +parsers.auto_link_scheme_part = parsers.alphanumeric + + parsers.plus + + parsers.period + + parsers.dash + +parsers.auto_link_scheme = parsers.letter + * parsers.auto_link_scheme_part + * parsers.auto_link_scheme_part^-30 + +parsers.absolute_uri = parsers.auto_link_scheme * parsers.colon + * ( parsers.any - parsers.spacing + - parsers.less - parsers.more)^0 + +parsers.printable_characters = S(".!#$%&'*+/=?^_`{|}~-") + +parsers.email_address_local_part_char = parsers.alphanumeric + + parsers.printable_characters + +parsers.email_address_local_part + = parsers.email_address_local_part_char^1 + +parsers.email_address_dns_label = parsers.alphanumeric + * ( parsers.alphanumeric + + parsers.dash)^-62 + * B(parsers.alphanumeric) + +parsers.email_address_domain = parsers.email_address_dns_label + * ( parsers.period + * parsers.email_address_dns_label)^0 + +parsers.email_address = parsers.email_address_local_part + * parsers.at + * parsers.email_address_domain + +parsers.auto_link_url = parsers.less + * C(parsers.absolute_uri) + * parsers.more + +parsers.auto_link_email = parsers.less + * C(parsers.email_address) + * parsers.more + +parsers.auto_link_relative_reference = parsers.less + * C(parsers.urlchar^1) + * parsers.more + +parsers.autolink = parsers.auto_link_url + + parsers.auto_link_email + +-- content in balanced brackets, parentheses, or quotes: +parsers.bracketed = P{ parsers.lbracket + * (( parsers.backslash / "" * parsers.rbracket + + parsers.any - (parsers.lbracket + + parsers.rbracket + + parsers.blankline^2) + ) + V(1))^0 + * parsers.rbracket } + +parsers.inparens = P{ parsers.lparent + * ((parsers.anyescaped - (parsers.lparent + + parsers.rparent + + parsers.blankline^2) + ) + V(1))^0 + * parsers.rparent } + +parsers.squoted = P{ parsers.squote * parsers.alphanumeric + * ((parsers.anyescaped - (parsers.squote + + parsers.blankline^2) + ) + V(1))^0 + * parsers.squote } + +parsers.dquoted = P{ parsers.dquote * parsers.alphanumeric + * ((parsers.anyescaped - (parsers.dquote + + parsers.blankline^2) + ) + V(1))^0 + * parsers.dquote } + +parsers.link_text = parsers.lbracket + * Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + parsers.autolink + + V("InlineHtml") + + ( parsers.backslash * parsers.backslash) + + ( parsers.backslash + * ( parsers.lbracket + + parsers.rbracket) + + V("NoSoftLineBreakSpace") + + V("NoSoftLineBreakEndline") + + (parsers.any + - ( parsers.newline + + parsers.lbracket + + parsers.rbracket + + parsers.blankline^2))))^0) + * parsers.rbracket + +parsers.link_label_body = -#(parsers.sp * parsers.rbracket) + * #( ( parsers.any + - parsers.rbracket)^-999 + * parsers.rbracket) + * Cs((parsers.alphanumeric^1 + + parsers.inticks + + parsers.autolink + + V("InlineHtml") + + ( parsers.backslash * parsers.backslash) + + ( parsers.backslash + * ( parsers.lbracket + + parsers.rbracket) + + V("NoSoftLineBreakSpace") + + V("NoSoftLineBreakEndline") + + (parsers.any + - ( parsers.newline + + parsers.lbracket + + parsers.rbracket + + parsers.blankline^2))))^1) + +parsers.link_label = parsers.lbracket + * parsers.link_label_body + * parsers.rbracket + +parsers.inparens_url = P{ parsers.lparent + * ((parsers.anyescaped - (parsers.lparent + + parsers.rparent + + parsers.spacing) + ) + V(1))^0 + * parsers.rparent } + +-- url for markdown links, allowing nested brackets: +parsers.url = parsers.less * Cs((parsers.anyescaped + - parsers.newline + - parsers.less + - parsers.more)^0) + * parsers.more + + -parsers.less + * Cs((parsers.inparens_url + (parsers.anyescaped + - parsers.spacing + - parsers.lparent + - parsers.rparent))^1) + +-- quoted text: +parsers.title_s = parsers.squote + * Cs((parsers.html_entities + + V("NoSoftLineBreakSpace") + + V("NoSoftLineBreakEndline") + + ( parsers.anyescaped + - parsers.newline + - parsers.squote + - parsers.blankline^2))^0) + * parsers.squote + +parsers.title_d = parsers.dquote + * Cs((parsers.html_entities + + V("NoSoftLineBreakSpace") + + V("NoSoftLineBreakEndline") + + ( parsers.anyescaped + - parsers.newline + - parsers.dquote + - parsers.blankline^2))^0) + * parsers.dquote + +parsers.title_p = parsers.lparent + * Cs((parsers.html_entities + + V("NoSoftLineBreakSpace") + + V("NoSoftLineBreakEndline") + + ( parsers.anyescaped + - parsers.newline + - parsers.lparent + - parsers.rparent + - parsers.blankline^2))^0) + * parsers.rparent + +parsers.title + = parsers.title_d + parsers.title_s + parsers.title_p + +parsers.optionaltitle + = parsers.spnlc * parsers.title * parsers.spacechar^0 + Cc("") + +-- parse a reference definition: [foo]: /bar "title" +parsers.define_reference_parser = (parsers.check_trail / "") + * parsers.link_label * parsers.colon + * parsers.spnlc * parsers.url + * ( parsers.spnlc_sep * parsers.title + * parsers.only_blank + + Cc("") * parsers.only_blank) +parsers.Inline = V("Inline") + +-- parse many p between starter and ender +parsers.between = function(p, starter, ender) + local ender2 = B(parsers.nonspacechar) * ender + return ( starter + * #parsers.nonspacechar + * Ct(p * (p - ender2)^0) + * ender2) +end + +parsers.lineof = function(c) + return ( parsers.check_trail_no_rem + * (P(c) * parsers.optionalspace)^3 + * (parsers.newline + parsers.eof)) +end + +parsers.thematic_break_lines = parsers.lineof(parsers.asterisk) + + parsers.lineof(parsers.dash) + + parsers.lineof(parsers.underscore) +-- parse Atx heading start and return level +parsers.heading_start = #parsers.hash * C(parsers.hash^-6) + * -parsers.hash / length + +-- parse setext header ending and return level +parsers.heading_level + = parsers.nonindentspace * parsers.equal^1 + * parsers.optionalspace * #parsers.newline * Cc(1) + + parsers.nonindentspace * parsers.dash^1 + * parsers.optionalspace * #parsers.newline * Cc(2) + +local function strip_atx_end(s) + return s:gsub("%s+#*%s*\n$","") +end + +parsers.atx_heading = parsers.check_trail_no_rem + * Cg(parsers.heading_start, "level") + * (C( parsers.optionalspace + * parsers.hash^0 + * parsers.optionalspace + * parsers.newline) + + parsers.spacechar^1 + * C(parsers.line)) +M.reader = {} +function M.reader.new(writer, options) + local self = {} + self.writer = writer + self.options = options + self.parsers = {} + (function(parsers) + setmetatable(self.parsers, { + __index = function (_, key) + return parsers[key] + end + }) + end)(parsers) + local parsers = self.parsers + function self.normalize_tag(tag) + tag = util.rope_to_string(tag) + tag = tag:gsub("[ \n\r\t]+", " ") + tag = tag:gsub("^ ", ""):gsub(" $", "") + tag = uni_algos.case.casefold(tag, true, false) + return tag + end + local function iterlines(s, f) + local rope = lpeg.match(Ct((parsers.line / f)^1), s) + return util.rope_to_string(rope) + end + if options.preserveTabs then + self.expandtabs = function(s) return s end + else + self.expandtabs = function(s) + if s:find("\t") then + return iterlines(s, util.expand_tabs_in_line) + else + return s + end + end + end + self.parser_functions = {} + self.create_parser = function(name, grammar, toplevel) + self.parser_functions[name] = function(str) + if toplevel and options.stripIndent then + local min_prefix_length, min_prefix = nil, '' + str = iterlines(str, function(line) + if lpeg.match(parsers.nonemptyline, line) == nil then + return line + end + line = util.expand_tabs_in_line(line) + local prefix = lpeg.match(C(parsers.optionalspace), line) + local prefix_length = #prefix + local is_shorter = min_prefix_length == nil + if not is_shorter then + is_shorter = prefix_length < min_prefix_length + end + if is_shorter then + min_prefix_length, min_prefix = prefix_length, prefix + end + return line + end) + str = str:gsub('^' .. min_prefix, '') + end + if toplevel and (options.texComments or options.hybrid) then + str = lpeg.match(Ct(parsers.commented_line^1), str) + str = util.rope_to_string(str) + end + local res = lpeg.match(grammar(), str) + if res == nil then + return writer.error(format("%s failed on:\n%s", + name, str:sub(1,20))) + else + return res + end + end + end + + self.create_parser("parse_blocks", + function() + return parsers.blocks + end, true) + + self.create_parser("parse_blocks_nested", + function() + return parsers.blocks_nested + end, false) + + self.create_parser("parse_inlines", + function() + return parsers.inlines + end, false) + + self.create_parser("parse_inlines_no_inline_note", + function() + return parsers.inlines_no_inline_note + end, false) + + self.create_parser("parse_inlines_no_html", + function() + return parsers.inlines_no_html + end, false) + + self.create_parser("parse_inlines_nbsp", + function() + return parsers.inlines_nbsp + end, false) + self.create_parser("parse_inlines_no_link_or_emphasis", + function() + return parsers.inlines_no_link_or_emphasis + end, false) + parsers.minimally_indented_blankline + = parsers.check_minimal_indent * (parsers.blankline / "") + + parsers.minimally_indented_block + = parsers.check_minimal_indent * V("Block") + + parsers.minimally_indented_block_or_paragraph + = parsers.check_minimal_indent * V("BlockOrParagraph") + + parsers.minimally_indented_paragraph + = parsers.check_minimal_indent * V("Paragraph") + + parsers.minimally_indented_plain + = parsers.check_minimal_indent * V("Plain") + + parsers.minimally_indented_par_or_plain + = parsers.minimally_indented_paragraph + + parsers.minimally_indented_plain + + parsers.minimally_indented_par_or_plain_no_blank + = parsers.minimally_indented_par_or_plain + - parsers.minimally_indented_blankline + + parsers.minimally_indented_ref + = parsers.check_minimal_indent * V("Reference") + + parsers.minimally_indented_blank + = parsers.check_minimal_indent * V("Blank") + + parsers.conditionally_indented_blankline + = parsers.check_minimal_blank_indent * (parsers.blankline / "") + + parsers.minimally_indented_ref_or_block + = parsers.minimally_indented_ref + + parsers.minimally_indented_block + - parsers.minimally_indented_blankline + + parsers.minimally_indented_ref_or_block_or_par + = parsers.minimally_indented_ref + + parsers.minimally_indented_block_or_paragraph + - parsers.minimally_indented_blankline + + + function parsers.separator_loop(separated_block, paragraph, + block_separator, paragraph_separator) + return separated_block + + block_separator + * paragraph + * separated_block + + paragraph_separator + * paragraph + end + + function parsers.create_loop_body_pair(separated_block, paragraph, + block_separator, + paragraph_separator) + return { + block = parsers.separator_loop(separated_block, paragraph, + block_separator, block_separator), + par = parsers.separator_loop(separated_block, paragraph, + block_separator, paragraph_separator) + } + end + + parsers.block_sep_group = function(blank) + return blank^0 * parsers.eof + + ( blank^2 / writer.paragraphsep + + blank^0 / writer.interblocksep + ) + end + + parsers.par_sep_group = function(blank) + return blank^0 * parsers.eof + + blank^0 / writer.paragraphsep + end + + parsers.sep_group_no_output = function(blank) + return blank^0 * parsers.eof + + blank^0 + end + + parsers.content_blank = parsers.minimally_indented_blankline + + parsers.ref_or_block_separated + = parsers.sep_group_no_output(parsers.content_blank) + * ( parsers.minimally_indented_ref + - parsers.content_blank) + + parsers.block_sep_group(parsers.content_blank) + * ( parsers.minimally_indented_block + - parsers.content_blank) + + parsers.loop_body_pair = + parsers.create_loop_body_pair( + parsers.ref_or_block_separated, + parsers.minimally_indented_par_or_plain_no_blank, + parsers.block_sep_group(parsers.content_blank), + parsers.par_sep_group(parsers.content_blank)) + + parsers.content_loop = ( V("Block") + * parsers.loop_body_pair.block^0 + + (V("Paragraph") + V("Plain")) + * parsers.ref_or_block_separated + * parsers.loop_body_pair.block^0 + + (V("Paragraph") + V("Plain")) + * parsers.loop_body_pair.par^0) + * parsers.content_blank^0 + + parsers.indented_content = function() + return Ct( (V("Reference") + (parsers.blankline / "")) + * parsers.content_blank^0 + * parsers.check_minimal_indent + * parsers.content_loop + + (V("Reference") + (parsers.blankline / "")) + * parsers.content_blank^0 + + parsers.content_loop) + end + + parsers.add_indent = function(pattern, name, breakable) + return Cg(Cmt( Cb("indent_info") + * Ct(pattern) + * ( #parsers.linechar -- check if starter is blank + * Cc(false) + Cc(true)) + * Cc(name) + * Cc(breakable), + process_starter_indent), "indent_info") + end + + if options.hashEnumerators then + parsers.dig = parsers.digit + parsers.hash + else + parsers.dig = parsers.digit + end + + parsers.enumerator = function(delimiter_type, interrupting) + local delimiter_range + local allowed_end + if interrupting then + delimiter_range = P("1") + allowed_end = C(parsers.spacechar^1) * #parsers.linechar + else + delimiter_range = parsers.dig * parsers.dig^-8 + allowed_end = C(parsers.spacechar^1) + + #(parsers.newline + parsers.eof) + end + + return parsers.check_trail + * Ct(C(delimiter_range) * C(delimiter_type)) + * allowed_end + end + + parsers.starter = parsers.bullet(parsers.dash) + + parsers.bullet(parsers.asterisk) + + parsers.bullet(parsers.plus) + + parsers.enumerator(parsers.period) + + parsers.enumerator(parsers.rparent) + + parsers.blockquote_start + = parsers.check_trail + * C(parsers.more) + * C(parsers.spacechar^0) + + parsers.blockquote_body + = parsers.add_indent(parsers.blockquote_start, "bq", true) + * parsers.indented_content() + * remove_indent("bq") + + if not options.breakableBlockquotes then + parsers.blockquote_body + = parsers.add_indent(parsers.blockquote_start, "bq", false) + * parsers.indented_content() + * remove_indent("bq") + end + local function parse_content_part(content_part) + local rope = util.rope_to_string(content_part) + local parsed + = self.parser_functions.parse_inlines_no_link_or_emphasis(rope) + parsed.indent_info = nil + return parsed + end + + local collect_emphasis_content = + function(t, opening_index, closing_index) + local content = {} + + local content_part = {} + for i = opening_index, closing_index do + local value = t[i] + + if value.rendered ~= nil then + content[#content + 1] = parse_content_part(content_part) + content_part = {} + content[#content + 1] = value.rendered + value.rendered = nil + else + if value.type == "delimiter" + and value.element == "emphasis" then + if value.is_active then + content_part[#content_part + 1] + = string.rep(value.character, value.current_count) + end + else + content_part[#content_part + 1] = value.content + end + value.content = '' + value.is_active = false + end + end + + if next(content_part) ~= nil then + content[#content + 1] = parse_content_part(content_part) + end + + return content + end + + local function fill_emph(t, opening_index, closing_index) + local content + = collect_emphasis_content(t, opening_index + 1, + closing_index - 1) + t[opening_index + 1].is_active = true + t[opening_index + 1].rendered = writer.emphasis(content) + end + + local function fill_strong(t, opening_index, closing_index) + local content + = collect_emphasis_content(t, opening_index + 1, + closing_index - 1) + t[opening_index + 1].is_active = true + t[opening_index + 1].rendered = writer.strong(content) + end + + local function breaks_three_rule(opening_delimiter, closing_delimiter) + return ( opening_delimiter.is_closing + or closing_delimiter.is_opening) + and (( opening_delimiter.original_count + + closing_delimiter.original_count) % 3 == 0) + and ( opening_delimiter.original_count % 3 ~= 0 + or closing_delimiter.original_count % 3 ~= 0) + end + + local find_emphasis_opener = function(t, bottom_index, latest_index, + character, closing_delimiter) + for i = latest_index, bottom_index, -1 do + local value = t[i] + if value.is_active and + value.is_opening and + value.type == "delimiter" and + value.element == "emphasis" and + (value.character == character) and + (value.current_count > 0) then + if not breaks_three_rule(value, closing_delimiter) then + return i + end + end + end + end + + local function process_emphasis(t, opening_index, closing_index) + for i = opening_index, closing_index do + local value = t[i] + if value.type == "delimiter" and value.element == "emphasis" then + local delimiter_length = string.len(value.content) + value.character = string.sub(value.content, 1, 1) + value.current_count = delimiter_length + value.original_count = delimiter_length + end + end + + local openers_bottom = { + ['*'] = { + [true] = {opening_index, opening_index, opening_index}, + [false] = {opening_index, opening_index, opening_index} + }, + ['_'] = { + [true] = {opening_index, opening_index, opening_index}, + [false] = {opening_index, opening_index, opening_index} + } + } + + local current_position = opening_index + local max_position = closing_index + + while current_position <= max_position do + local value = t[current_position] + + if value.type ~= "delimiter" or + value.element ~= "emphasis" or + not value.is_active or + not value.is_closing or + (value.current_count <= 0) then + current_position = current_position + 1 + goto continue + end + + local character = value.character + local is_opening = value.is_opening + local closing_length_modulo_three = value.original_count % 3 + + local current_openers_bottom + = openers_bottom[character][is_opening] + [closing_length_modulo_three + 1] + + local opener_position + = find_emphasis_opener(t, current_openers_bottom, + current_position - 1, character, value) + + if (opener_position == nil) then + openers_bottom[character][is_opening] + [closing_length_modulo_three + 1] + = current_position + current_position = current_position + 1 + goto continue + end + + local opening_delimiter = t[opener_position] + + local current_opening_count = opening_delimiter.current_count + local current_closing_count = t[current_position].current_count + + if (current_opening_count >= 2) + and (current_closing_count >= 2) then + opening_delimiter.current_count = current_opening_count - 2 + t[current_position].current_count = current_closing_count - 2 + fill_strong(t, opener_position, current_position) + else + opening_delimiter.current_count = current_opening_count - 1 + t[current_position].current_count = current_closing_count - 1 + fill_emph(t, opener_position, current_position) + end + + ::continue:: + end + end + + local cont = lpeg.R("\128\191") -- continuation byte + + local function utf8_by_byte_count(n) + if (n == 1) then + return lpeg.R("\0\127") + end + if (n == 2) then + return lpeg.R("\194\223") * cont + end + if (n == 3) then + return lpeg.R("\224\239") * cont * cont + end + if (n == 4) then + return lpeg.R("\240\244") * cont * cont * cont + end + end + local function check_unicode_type(s, i, start_pos, end_pos, chartype) + local c + local char_length + for pos = start_pos, end_pos, 1 do + if (start_pos < 0) then + char_length = -pos + else + char_length = pos + 1 + end + + if (chartype == "punctuation") then + if lpeg.match(parsers.punctuation[char_length], s, i+pos) then + return i + end + else + c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos) + if (c ~= nil) and (unicode.utf8.match(c, chartype)) then + return i + end + end + end + end + + local function check_preceding_unicode_punctuation(s, i) + return check_unicode_type(s, i, -4, -1, "punctuation") + end + + local function check_preceding_unicode_whitespace(s, i) + return check_unicode_type(s, i, -4, -1, "%s") + end + + local function check_following_unicode_punctuation(s, i) + return check_unicode_type(s, i, 0, 3, "punctuation") + end + + local function check_following_unicode_whitespace(s, i) + return check_unicode_type(s, i, 0, 3, "%s") + end + + parsers.unicode_preceding_punctuation + = B(parsers.escapable) + + Cmt(parsers.succeed, check_preceding_unicode_punctuation) + + parsers.unicode_preceding_whitespace + = Cmt(parsers.succeed, check_preceding_unicode_whitespace) + + parsers.unicode_following_punctuation + = #parsers.escapable + + Cmt(parsers.succeed, check_following_unicode_punctuation) + + parsers.unicode_following_whitespace + = Cmt(parsers.succeed, check_following_unicode_whitespace) + + parsers.delimiter_run = function(character) + return (B(parsers.backslash * character) + -B(character)) + * character^1 + * -#character + end + + parsers.left_flanking_delimiter_run = function(character) + return (B( parsers.any) + * ( parsers.unicode_preceding_punctuation + + parsers.unicode_preceding_whitespace) + + -B(parsers.any)) + * parsers.delimiter_run(character) + * parsers.unicode_following_punctuation + + parsers.delimiter_run(character) + * -#( parsers.unicode_following_punctuation + + parsers.unicode_following_whitespace + + parsers.eof) + end + + parsers.right_flanking_delimiter_run = function(character) + return parsers.unicode_preceding_punctuation + * parsers.delimiter_run(character) + * ( parsers.unicode_following_punctuation + + parsers.unicode_following_whitespace + + parsers.eof) + + (B(parsers.any) + * -( parsers.unicode_preceding_punctuation + + parsers.unicode_preceding_whitespace)) + * parsers.delimiter_run(character) + end + + if options.underscores then + parsers.emph_start + = parsers.left_flanking_delimiter_run(parsers.asterisk) + + ( -#parsers.right_flanking_delimiter_run(parsers.underscore) + + ( parsers.unicode_preceding_punctuation + * #parsers.right_flanking_delimiter_run(parsers.underscore))) + * parsers.left_flanking_delimiter_run(parsers.underscore) + + parsers.emph_end + = parsers.right_flanking_delimiter_run(parsers.asterisk) + + ( -#parsers.left_flanking_delimiter_run(parsers.underscore) + + #( parsers.left_flanking_delimiter_run(parsers.underscore) + * parsers.unicode_following_punctuation)) + * parsers.right_flanking_delimiter_run(parsers.underscore) + else + parsers.emph_start + = parsers.left_flanking_delimiter_run(parsers.asterisk) + + parsers.emph_end + = parsers.right_flanking_delimiter_run(parsers.asterisk) + end + + parsers.emph_capturing_open_and_close + = #parsers.emph_start * #parsers.emph_end + * Ct( Cg(Cc("delimiter"), "type") + * Cg(Cc("emphasis"), "element") + * Cg(C(parsers.emph_start), "content") + * Cg(Cc(true), "is_opening") + * Cg(Cc(true), "is_closing")) + + parsers.emph_capturing_open = Ct( Cg(Cc("delimiter"), "type") + * Cg(Cc("emphasis"), "element") + * Cg(C(parsers.emph_start), "content") + * Cg(Cc(true), "is_opening") + * Cg(Cc(false), "is_closing")) + + parsers.emph_capturing_close = Ct( Cg(Cc("delimiter"), "type") + * Cg(Cc("emphasis"), "element") + * Cg(C(parsers.emph_end), "content") + * Cg(Cc(false), "is_opening") + * Cg(Cc(true), "is_closing")) + + parsers.emph_open_or_close = parsers.emph_capturing_open_and_close + + parsers.emph_capturing_open + + parsers.emph_capturing_close + + parsers.emph_open = parsers.emph_capturing_open_and_close + + parsers.emph_capturing_open + + parsers.emph_close = parsers.emph_capturing_open_and_close + + parsers.emph_capturing_close + + -- List of references defined in the document + local references + + -- List of note references defined in the document + parsers.rawnotes = {} + + function self.register_link(_, tag, url, title, + attributes) + local normalized_tag = self.normalize_tag(tag) + if references[normalized_tag] == nil then + references[normalized_tag] = { + url = url, + title = title, + attributes = attributes + } + end + return "" + end + + function self.lookup_reference(tag) + return references[self.normalize_tag(tag)] + end + + function self.lookup_note_reference(tag) + return parsers.rawnotes[self.normalize_tag(tag)] + end + + parsers.title_s_direct_ref = parsers.squote + * Cs((parsers.html_entities + + ( parsers.anyescaped + - parsers.squote + - parsers.blankline^2))^0) + * parsers.squote + + parsers.title_d_direct_ref = parsers.dquote + * Cs((parsers.html_entities + + ( parsers.anyescaped + - parsers.dquote + - parsers.blankline^2))^0) + * parsers.dquote + + parsers.title_p_direct_ref = parsers.lparent + * Cs((parsers.html_entities + + ( parsers.anyescaped + - parsers.lparent + - parsers.rparent + - parsers.blankline^2))^0) + * parsers.rparent + + parsers.title_direct_ref = parsers.title_s_direct_ref + + parsers.title_d_direct_ref + + parsers.title_p_direct_ref + + parsers.inline_direct_ref_inside = parsers.lparent * parsers.spnl + * Cg(parsers.url + Cc(""), "url") + * parsers.spnl + * Cg( parsers.title_direct_ref + + Cc(""), "title") + * parsers.spnl * parsers.rparent + + parsers.inline_direct_ref = parsers.lparent * parsers.spnlc + * Cg(parsers.url + Cc(""), "url") + * parsers.spnlc + * Cg(parsers.title + Cc(""), "title") + * parsers.spnlc * parsers.rparent + + parsers.empty_link = parsers.lbracket + * parsers.rbracket + + parsers.inline_link = parsers.link_text + * parsers.inline_direct_ref + + parsers.full_link = parsers.link_text + * parsers.link_label + + parsers.shortcut_link = parsers.link_label + * -(parsers.empty_link + parsers.link_label) + + parsers.collapsed_link = parsers.link_label + * parsers.empty_link + + parsers.image_opening = #(parsers.exclamation * parsers.inline_link) + * Cg(Cc("inline"), "link_type") + + #(parsers.exclamation * parsers.full_link) + * Cg(Cc("full"), "link_type") + + #( parsers.exclamation + * parsers.collapsed_link) + * Cg(Cc("collapsed"), "link_type") + + #(parsers.exclamation * parsers.shortcut_link) + * Cg(Cc("shortcut"), "link_type") + + #(parsers.exclamation * parsers.empty_link) + * Cg(Cc("empty"), "link_type") + + parsers.link_opening = #parsers.inline_link + * Cg(Cc("inline"), "link_type") + + #parsers.full_link + * Cg(Cc("full"), "link_type") + + #parsers.collapsed_link + * Cg(Cc("collapsed"), "link_type") + + #parsers.shortcut_link + * Cg(Cc("shortcut"), "link_type") + + #parsers.empty_link + * Cg(Cc("empty_link"), "link_type") + + #parsers.link_text + * Cg(Cc("link_text"), "link_type") + + parsers.note_opening = #(parsers.circumflex * parsers.link_text) + * Cg(Cc("note_inline"), "link_type") + + parsers.raw_note_opening = #( parsers.lbracket + * parsers.circumflex + * parsers.link_label_body + * parsers.rbracket) + * Cg(Cc("raw_note"), "link_type") + + local inline_note_element = Cg(Cc("note"), "element") + * parsers.note_opening + * Cg( parsers.circumflex + * parsers.lbracket, "content") + + local image_element = Cg(Cc("image"), "element") + * parsers.image_opening + * Cg( parsers.exclamation + * parsers.lbracket, "content") + + local note_element = Cg(Cc("note"), "element") + * parsers.raw_note_opening + * Cg( parsers.lbracket + * parsers.circumflex, "content") + + local link_element = Cg(Cc("link"), "element") + * parsers.link_opening + * Cg(parsers.lbracket, "content") + + local opening_elements = parsers.fail + + if options.inlineNotes then + opening_elements = opening_elements + inline_note_element + end + + opening_elements = opening_elements + image_element + + if options.notes then + opening_elements = opening_elements + note_element + end + + opening_elements = opening_elements + link_element + + parsers.link_image_opening = Ct( Cg(Cc("delimiter"), "type") + * Cg(Cc(true), "is_opening") + * Cg(Cc(false), "is_closing") + * opening_elements) + + parsers.link_image_closing = Ct( Cg(Cc("delimiter"), "type") + * Cg(Cc("link"), "element") + * Cg(Cc(false), "is_opening") + * Cg(Cc(true), "is_closing") + * ( Cg(Cc(true), "is_direct") + * Cg( parsers.rbracket + * #parsers.inline_direct_ref, + "content") + + Cg(Cc(false), "is_direct") + * Cg(parsers.rbracket, "content"))) + + parsers.link_image_open_or_close = parsers.link_image_opening + + parsers.link_image_closing + + if options.html then + parsers.link_emph_precedence = parsers.inticks + + parsers.autolink + + parsers.html_inline_tags + else + parsers.link_emph_precedence = parsers.inticks + + parsers.autolink + end + + parsers.link_and_emph_endline = parsers.newline + * ((parsers.check_minimal_indent + * -V("EndlineExceptions") + + parsers.check_optional_indent + * -V("EndlineExceptions") + * -parsers.starter) / "") + * parsers.spacechar^0 / "\n" + + parsers.link_and_emph_content + = Ct( Cg(Cc("content"), "type") + * Cg(Cs(( parsers.link_emph_precedence + + parsers.backslash * parsers.any + + parsers.link_and_emph_endline + + (parsers.linechar + - parsers.blankline^2 + - parsers.link_image_open_or_close + - parsers.emph_open_or_close))^0), "content")) + + parsers.link_and_emph_table + = (parsers.link_image_opening + parsers.emph_open) + * parsers.link_and_emph_content + * ((parsers.link_image_open_or_close + parsers.emph_open_or_close) + * parsers.link_and_emph_content)^1 + + local function collect_link_content(t, opening_index, closing_index) + local content = {} + for i = opening_index, closing_index do + content[#content + 1] = t[i].content + end + return util.rope_to_string(content) + end + + local function find_link_opener(t, bottom_index, latest_index) + for i = latest_index, bottom_index, -1 do + local value = t[i] + if value.type == "delimiter" and + value.is_opening and + ( value.element == "link" + or value.element == "image" + or value.element == "note") + and not value.removed then + if value.is_active then + return i + end + value.removed = true + return nil + end + end + end + + local function find_next_link_closing_index(t, latest_index) + for i = latest_index, #t do + local value = t[i] + if value.is_closing and + value.element == "link" and + not value.removed then + return i + end + end + end + + local function disable_previous_link_openers(t, opening_index) + if t[opening_index].element == "image" then + return + end + + for i = opening_index, 1, -1 do + local value = t[i] + if value.is_active and + value.type == "delimiter" and + value.is_opening and + value.element == "link" then + value.is_active = false + end + end + end + + local function disable_range(t, opening_index, closing_index) + for i = opening_index, closing_index do + local value = t[i] + if value.is_active then + value.is_active = false + if value.type == "delimiter" then + value.removed = true + end + end + end + end + + local delete_parsed_content_in_range = + function(t, opening_index, closing_index) + for i = opening_index, closing_index do + t[i].rendered = nil + end + end + + local function empty_content_in_range(t, opening_index, closing_index) + for i = opening_index, closing_index do + t[i].content = '' + end + end + + local function join_attributes(reference_attributes, own_attributes) + local merged_attributes = {} + for _, attribute in ipairs(reference_attributes or {}) do + table.insert(merged_attributes, attribute) + end + for _, attribute in ipairs(own_attributes or {}) do + table.insert(merged_attributes, attribute) + end + if next(merged_attributes) == nil then + merged_attributes = nil + end + return merged_attributes + end + + local render_link_or_image = + function(t, opening_index, closing_index, content_end_index, + reference) + process_emphasis(t, opening_index, content_end_index) + local mapped = collect_emphasis_content(t, opening_index + 1, + content_end_index - 1) + + local rendered = {} + if (t[opening_index].element == "link") then + rendered = writer.link(mapped, reference.url, + reference.title, reference.attributes) + end + + if (t[opening_index].element == "image") then + rendered = writer.image(mapped, reference.url, reference.title, + reference.attributes) + end + + if (t[opening_index].element == "note") then + if (t[opening_index].link_type == "note_inline") then + rendered = writer.note(mapped) + end + if (t[opening_index].link_type == "raw_note") then + rendered = writer.note(reference) + end + end + + t[opening_index].rendered = rendered + delete_parsed_content_in_range(t, opening_index + 1, + closing_index) + empty_content_in_range(t, opening_index, closing_index) + disable_previous_link_openers(t, opening_index) + disable_range(t, opening_index, closing_index) + end + + local resolve_inline_following_content = + function(t, closing_index, match_reference, match_link_attributes) + local content = "" + for i = closing_index + 1, #t do + content = content .. t[i].content + end + + local matching_content = parsers.succeed + + if match_reference then + matching_content = matching_content + * parsers.inline_direct_ref_inside + end + + if match_link_attributes then + matching_content = matching_content + * Cg(Ct(parsers.attributes^-1), "attributes") + end + + local matched = lpeg.match(Ct( matching_content + * Cg(Cp(), "end_position")), content) + + local matched_count = matched.end_position - 1 + for i = closing_index + 1, #t do + local value = t[i] + + local chars_left = matched_count + matched_count = matched_count - #value.content + + if matched_count <= 0 then + value.content = value.content:sub(chars_left + 1) + break + end + + value.content = '' + value.is_active = false + end + + local attributes = matched.attributes + if attributes == nil or next(attributes) == nil then + attributes = nil + end + + return { + url = matched.url or "", + title = matched.title or "", + attributes = attributes + } + end + + local function resolve_inline_link(t, opening_index, closing_index) + local inline_content + = resolve_inline_following_content(t, closing_index, true, + t.match_link_attributes) + render_link_or_image(t, opening_index, closing_index, + closing_index, inline_content) + end + + local resolve_note_inline_link = + function(t, opening_index, closing_index) + local inline_content + = resolve_inline_following_content(t, closing_index, + false, false) + render_link_or_image(t, opening_index, closing_index, + closing_index, inline_content) + end + + local function resolve_shortcut_link(t, opening_index, closing_index) + local content + = collect_link_content(t, opening_index + 1, closing_index - 1) + local r = self.lookup_reference(content) + + if r then + local inline_content + = resolve_inline_following_content(t, closing_index, false, + t.match_link_attributes) + r.attributes + = join_attributes(r.attributes, inline_content.attributes) + render_link_or_image(t, opening_index, closing_index, + closing_index, r) + end + end + + local function resolve_raw_note_link(t, opening_index, closing_index) + local content + = collect_link_content(t, opening_index + 1, closing_index - 1) + local r = self.lookup_note_reference(content) + + if r then + local parsed_ref = self.parser_functions.parse_blocks_nested(r) + render_link_or_image(t, opening_index, closing_index, + closing_index, parsed_ref) + end + end + + local function resolve_full_link(t, opening_index, closing_index) + local next_link_closing_index + = find_next_link_closing_index(t, closing_index + 4) + local next_link_content + = collect_link_content(t, closing_index + 3, + next_link_closing_index - 1) + local r = self.lookup_reference(next_link_content) + + if r then + local inline_content + = resolve_inline_following_content(t, next_link_closing_index, + false, + t.match_link_attributes) + r.attributes + = join_attributes(r.attributes, inline_content.attributes) + render_link_or_image(t, opening_index, next_link_closing_index, + closing_index, r) + end + end + + local function resolve_collapsed_link(t, opening_index, closing_index) + local next_link_closing_index + = find_next_link_closing_index(t, closing_index + 4) + local content + = collect_link_content(t, opening_index + 1, closing_index - 1) + local r = self.lookup_reference(content) + + if r then + local inline_content + = resolve_inline_following_content(t, closing_index, false, + t.match_link_attributes) + r.attributes + = join_attributes(r.attributes, inline_content.attributes) + render_link_or_image(t, opening_index, next_link_closing_index, + closing_index, r) + end + end + + local function process_links_and_emphasis(t) + for _,value in ipairs(t) do + value.is_active = true + end + + for i,value in ipairs(t) do + if not value.is_closing + or value.type ~= "delimiter" + or not ( value.element == "link" + or value.element == "image" + or value.element == "note") + or value.removed then + goto continue + end + + local opener_position = find_link_opener(t, 1, i - 1) + if (opener_position == nil) then + goto continue + end + + local opening_delimiter = t[opener_position] + opening_delimiter.removed = true + + local link_type = opening_delimiter.link_type + + if (link_type == "inline") then + resolve_inline_link(t, opener_position, i) + end + if (link_type == "shortcut") then + resolve_shortcut_link(t, opener_position, i) + end + if (link_type == "full") then + resolve_full_link(t, opener_position, i) + end + if (link_type == "collapsed") then + resolve_collapsed_link(t, opener_position, i) + end + if (link_type == "note_inline") then + resolve_note_inline_link(t, opener_position, i) + end + if (link_type == "raw_note") then + resolve_raw_note_link(t, opener_position, i) + end + + ::continue:: + end + + t[#t].content = t[#t].content:gsub("%s*$","") + + process_emphasis(t, 1, #t) + local final_result = collect_emphasis_content(t, 1, #t) + return final_result + end + + function self.defer_link_and_emphasis_processing(delimiter_table) + return writer.defer_call(function() + return process_links_and_emphasis(delimiter_table) + end) + end + + parsers.Str = ( parsers.normalchar + * (parsers.normalchar + parsers.at)^0) + / writer.string + + parsers.Symbol = (parsers.backtick^1 + V("SpecialChar")) + / writer.string + + parsers.Ellipsis = P("...") / writer.ellipsis + + parsers.Smart = parsers.Ellipsis + + parsers.Code = parsers.inticks / writer.code + + if options.blankBeforeBlockquote then + parsers.bqstart = parsers.fail + else + parsers.bqstart = parsers.blockquote_start + end + + if options.blankBeforeHeading then + parsers.headerstart = parsers.fail + else + parsers.headerstart = parsers.atx_heading + end + + if options.blankBeforeList then + parsers.interrupting_bullets = parsers.fail + parsers.interrupting_enumerators = parsers.fail + else + parsers.interrupting_bullets + = parsers.bullet(parsers.dash, true) + + parsers.bullet(parsers.asterisk, true) + + parsers.bullet(parsers.plus, true) + + parsers.interrupting_enumerators + = parsers.enumerator(parsers.period, true) + + parsers.enumerator(parsers.rparent, true) + end + + if options.html then + parsers.html_interrupting + = parsers.check_trail + * ( parsers.html_incomplete_open_tag + + parsers.html_incomplete_close_tag + + parsers.html_incomplete_open_special_tag + + parsers.html_comment_start + + parsers.html_cdatasection_start + + parsers.html_declaration_start + + parsers.html_instruction_start + - parsers.html_close_special_tag + - parsers.html_empty_special_tag) + else + parsers.html_interrupting = parsers.fail + end + + parsers.EndlineExceptions + = parsers.blankline -- paragraph break + + parsers.eof -- end of document + + parsers.bqstart + + parsers.thematic_break_lines + + parsers.interrupting_bullets + + parsers.interrupting_enumerators + + parsers.headerstart + + parsers.html_interrupting + + parsers.NoSoftLineBreakEndlineExceptions = parsers.EndlineExceptions + + parsers.endline = parsers.newline + * (parsers.check_minimal_indent + * -V("EndlineExceptions") + + parsers.check_optional_indent + * -V("EndlineExceptions") + * -parsers.starter) / function(_) return end + * parsers.spacechar^0 + + parsers.Endline = parsers.endline + / writer.soft_line_break + + parsers.EndlineNoSub = parsers.endline + + parsers.NoSoftLineBreakEndline + = parsers.newline + * (parsers.check_minimal_indent + * -V("NoSoftLineBreakEndlineExceptions") + + parsers.check_optional_indent + * -V("NoSoftLineBreakEndlineExceptions") + * -parsers.starter) + * parsers.spacechar^0 + / writer.space + + parsers.EndlineBreak = parsers.backslash * parsers.Endline + / writer.hard_line_break + + parsers.OptionalIndent + = parsers.spacechar^1 / writer.space + + parsers.Space = parsers.spacechar^2 * parsers.Endline + / writer.hard_line_break + + parsers.spacechar^1 + * parsers.Endline^-1 + * parsers.eof / self.expandtabs + + parsers.spacechar^1 * parsers.Endline + / writer.soft_line_break + + parsers.spacechar^1 + * -parsers.newline / self.expandtabs + + parsers.NoSoftLineBreakSpace + = parsers.spacechar^2 * parsers.Endline + / writer.hard_line_break + + parsers.spacechar^1 + * parsers.Endline^-1 + * parsers.eof / self.expandtabs + + parsers.spacechar^1 * parsers.Endline + / writer.soft_line_break + + parsers.spacechar^1 + * -parsers.newline / self.expandtabs + + parsers.NonbreakingEndline + = parsers.endline + / writer.nbsp + + parsers.NonbreakingSpace + = parsers.spacechar^2 * parsers.endline + / writer.nbsp + + parsers.spacechar^1 + * parsers.endline^-1 * parsers.eof / "" + + parsers.spacechar^1 * parsers.endline + * parsers.optionalspace + / writer.nbsp + + parsers.spacechar^1 * parsers.optionalspace + / writer.nbsp + +function self.auto_link_url(url, attributes) + return writer.link(writer.escape(url), + url, nil, attributes) +end +function self.auto_link_email(email, attributes) + return writer.link(writer.escape(email), + "mailto:"..email, + nil, attributes) +end + + parsers.AutoLinkUrl = parsers.auto_link_url + / self.auto_link_url + + parsers.AutoLinkEmail + = parsers.auto_link_email + / self.auto_link_email + + parsers.AutoLinkRelativeReference + = parsers.auto_link_relative_reference + / self.auto_link_url + + parsers.LinkAndEmph = Ct(parsers.link_and_emph_table) + / self.defer_link_and_emphasis_processing + + parsers.EscapedChar = parsers.backslash + * C(parsers.escapable) / writer.string + + parsers.InlineHtml = Cs(parsers.html_inline_comment) + / writer.inline_html_comment + + Cs(parsers.html_any_empty_inline_tag + + parsers.html_inline_instruction + + parsers.html_inline_cdatasection + + parsers.html_inline_declaration + + parsers.html_any_open_inline_tag + + parsers.html_any_close_tag) + / writer.inline_html_tag + + parsers.HtmlEntity = parsers.html_entities / writer.string + parsers.DisplayHtml = Cs(parsers.check_trail + * ( parsers.html_comment + + parsers.html_special_block + + parsers.html_block + + parsers.html_any_block + + parsers.html_instruction + + parsers.html_cdatasection + + parsers.html_declaration)) + / writer.block_html_element + + parsers.indented_non_blank_line = parsers.indentedline + - parsers.blankline + + parsers.Verbatim + = Cs( parsers.check_code_trail + * (parsers.line - parsers.blankline) + * (( parsers.check_minimal_blank_indent_and_full_code_trail + * parsers.blankline)^0 + * ( (parsers.check_minimal_indent / "") + * parsers.check_code_trail + * (parsers.line - parsers.blankline))^1)^0) + / self.expandtabs / writer.verbatim + + parsers.Blockquote = parsers.blockquote_body + / writer.blockquote + + parsers.ThematicBreak = parsers.thematic_break_lines + / writer.thematic_break + + parsers.Reference = parsers.define_reference_parser + / self.register_link + + parsers.Paragraph = parsers.freeze_trail + * (Ct((parsers.Inline)^1) + * (parsers.newline + parsers.eof) + * parsers.unfreeze_trail + / writer.paragraph) + + parsers.Plain = parsers.nonindentspace * Ct(parsers.Inline^1) + / writer.plain + + if options.taskLists then + parsers.tickbox = ( parsers.ticked_box + + parsers.halfticked_box + + parsers.unticked_box + ) / writer.tickbox + else + parsers.tickbox = parsers.fail + end + + parsers.list_blank = parsers.conditionally_indented_blankline + + parsers.ref_or_block_list_separated + = parsers.sep_group_no_output(parsers.list_blank) + * parsers.minimally_indented_ref + + parsers.block_sep_group(parsers.list_blank) + * parsers.minimally_indented_block + + parsers.ref_or_block_non_separated + = parsers.minimally_indented_ref + + (parsers.succeed / writer.interblocksep) + * parsers.minimally_indented_block + - parsers.minimally_indented_blankline + + parsers.tight_list_loop_body_pair = + parsers.create_loop_body_pair( + parsers.ref_or_block_non_separated, + parsers.minimally_indented_par_or_plain_no_blank, + (parsers.succeed / writer.interblocksep), + (parsers.succeed / writer.paragraphsep)) + + parsers.loose_list_loop_body_pair = + parsers.create_loop_body_pair( + parsers.ref_or_block_list_separated, + parsers.minimally_indented_par_or_plain, + parsers.block_sep_group(parsers.list_blank), + parsers.par_sep_group(parsers.list_blank)) + + parsers.tight_list_content_loop + = V("Block") + * parsers.tight_list_loop_body_pair.block^0 + + (V("Paragraph") + V("Plain")) + * parsers.ref_or_block_non_separated + * parsers.tight_list_loop_body_pair.block^0 + + (V("Paragraph") + V("Plain")) + * parsers.tight_list_loop_body_pair.par^0 + + parsers.loose_list_content_loop + = V("Block") + * parsers.loose_list_loop_body_pair.block^0 + + (V("Paragraph") + V("Plain")) + * parsers.ref_or_block_list_separated + * parsers.loose_list_loop_body_pair.block^0 + + (V("Paragraph") + V("Plain")) + * parsers.loose_list_loop_body_pair.par^0 + + parsers.list_item_tightness_condition + = -#( parsers.list_blank^0 + * parsers.minimally_indented_ref_or_block_or_par) + * remove_indent("li") + + remove_indent("li") + * parsers.fail + + parsers.indented_content_tight + = Ct( (parsers.blankline / "") + * #parsers.list_blank + * remove_indent("li") + + ( (V("Reference") + (parsers.blankline / "")) + * parsers.check_minimal_indent + * parsers.tight_list_content_loop + + (V("Reference") + (parsers.blankline / "")) + + (parsers.tickbox^-1 / writer.escape) + * parsers.tight_list_content_loop + ) + * parsers.list_item_tightness_condition) + + parsers.indented_content_loose + = Ct( (parsers.blankline / "") + * #parsers.list_blank + + ( (V("Reference") + (parsers.blankline / "")) + * parsers.check_minimal_indent + * parsers.loose_list_content_loop + + (V("Reference") + (parsers.blankline / "")) + + (parsers.tickbox^-1 / writer.escape) + * parsers.loose_list_content_loop)) + + parsers.TightListItem = function(starter) + return -parsers.ThematicBreak + * parsers.add_indent(starter, "li") + * parsers.indented_content_tight + end + + parsers.LooseListItem = function(starter) + return -parsers.ThematicBreak + * parsers.add_indent(starter, "li") + * parsers.indented_content_loose + * remove_indent("li") + end + + parsers.BulletListOfType = function(bullet_type) + local bullet = parsers.bullet(bullet_type) + return ( Ct( parsers.TightListItem(bullet) + * ( (parsers.check_minimal_indent / "") + * parsers.TightListItem(bullet) + )^0 + ) + * Cc(true) + * -#( (parsers.list_blank^0 / "") + * parsers.check_minimal_indent + * (bullet - parsers.ThematicBreak) + ) + + Ct( parsers.LooseListItem(bullet) + * ( (parsers.list_blank^0 / "") + * (parsers.check_minimal_indent / "") + * parsers.LooseListItem(bullet) + )^0 + ) + * Cc(false) + ) / writer.bulletlist + end + + parsers.BulletList = parsers.BulletListOfType(parsers.dash) + + parsers.BulletListOfType(parsers.asterisk) + + parsers.BulletListOfType(parsers.plus) + + local function ordered_list(items,tight,starter) + local startnum = starter[2][1] + if options.startNumber then + startnum = tonumber(startnum) or 1 -- fallback for '#' + if startnum ~= nil then + startnum = math.floor(startnum) + end + else + startnum = nil + end + return writer.orderedlist(items,tight,startnum) + end + + parsers.OrderedListOfType = function(delimiter_type) + local enumerator = parsers.enumerator(delimiter_type) + return Cg(enumerator, "listtype") + * (Ct( parsers.TightListItem(Cb("listtype")) + * ( (parsers.check_minimal_indent / "") + * parsers.TightListItem(enumerator))^0) + * Cc(true) + * -#((parsers.list_blank^0 / "") + * parsers.check_minimal_indent * enumerator) + + Ct( parsers.LooseListItem(Cb("listtype")) + * ((parsers.list_blank^0 / "") + * (parsers.check_minimal_indent / "") + * parsers.LooseListItem(enumerator))^0) + * Cc(false) + ) * Ct(Cb("listtype")) / ordered_list + end + + parsers.OrderedList = parsers.OrderedListOfType(parsers.period) + + parsers.OrderedListOfType(parsers.rparent) + parsers.Blank = parsers.blankline / "" + + V("Reference") + function parsers.parse_heading_text(s) + local inlines = self.parser_functions.parse_inlines(s) + local flatten_inlines = self.writer.flatten_inlines + self.writer.flatten_inlines = true + local flat_text = self.parser_functions.parse_inlines(s) + flat_text = util.rope_to_string(flat_text) + self.writer.flatten_inlines = flatten_inlines + return {flat_text, inlines} + end + + -- parse atx header + parsers.AtxHeading = parsers.check_trail_no_rem + * Cg(parsers.heading_start, "level") + * ((C( parsers.optionalspace + * parsers.hash^0 + * parsers.optionalspace + * parsers.newline) + + parsers.spacechar^1 + * C(parsers.line)) + / strip_atx_end + / parsers.parse_heading_text) + * Cb("level") + / writer.heading + + parsers.heading_line = parsers.linechar^1 + - parsers.thematic_break_lines + + parsers.heading_text = parsers.heading_line + * ( (V("Endline") / "\n") + * ( parsers.heading_line + - parsers.heading_level))^0 + * parsers.newline^-1 + + parsers.SetextHeading = parsers.freeze_trail + * parsers.check_trail_no_rem + * #( parsers.heading_text + * parsers.check_minimal_indent + * parsers.check_trail + * parsers.heading_level) + * Cs(parsers.heading_text) + / parsers.parse_heading_text + * parsers.check_minimal_indent_and_trail + * parsers.heading_level + * parsers.newline + * parsers.unfreeze_trail + / writer.heading + + parsers.Heading = parsers.AtxHeading + parsers.SetextHeading + function self.finalize_grammar(extensions) + local walkable_syntax = (function(global_walkable_syntax) + local local_walkable_syntax = {} + for lhs, rule in pairs(global_walkable_syntax) do + local_walkable_syntax[lhs] = util.table_copy(rule) + end + return local_walkable_syntax + end)(walkable_syntax) + local current_extension_name = nil + self.insert_pattern = function(selector, pattern, pattern_name) + assert(pattern_name == nil or type(pattern_name) == "string") + local _, _, lhs, pos, rhs + = selector:find("^(%a+)%s+([%a%s]+%a+)%s+(%a+)$") + assert(lhs ~= nil, + [[Expected selector in form ]] + .. [["LHS (before|after|instead of) RHS", not "]] + .. selector .. [["]]) + assert(walkable_syntax[lhs] ~= nil, + [[Rule ]] .. lhs + .. [[ -> ... does not exist in markdown grammar]]) + assert(pos == "before" or pos == "after" or pos == "instead of", + [[Expected positional specifier "before", "after", ]] + .. [[or "instead of", not "]] + .. pos .. [["]]) + local rule = walkable_syntax[lhs] + local index = nil + for current_index, current_rhs in ipairs(rule) do + if type(current_rhs) == "string" and current_rhs == rhs then + index = current_index + if pos == "after" then + index = index + 1 + end + break + end + end + assert(index ~= nil, + [[Rule ]] .. lhs .. [[ -> ]] .. rhs + .. [[ does not exist in markdown grammar]]) + local accountable_pattern + if current_extension_name then + accountable_pattern + = {pattern, current_extension_name, pattern_name} + else + assert(type(pattern) == "string", + [[reader->insert_pattern() was called outside ]] + .. [[an extension with ]] + .. [[a PEG pattern instead of a rule name]]) + accountable_pattern = pattern + end + if pos == "instead of" then + rule[index] = accountable_pattern + else + table.insert(rule, index, accountable_pattern) + end + end + local syntax = + { "Blocks", + + Blocks = V("InitializeState") + * V("ExpectedJekyllData") + * V("Blank")^0 + * ( V("Block") + * ( V("Blank")^0 * parsers.eof + + ( V("Blank")^2 / writer.paragraphsep + + V("Blank")^0 / writer.interblocksep + ) + ) + + ( V("Paragraph") + V("Plain") ) + * ( V("Blank")^0 * parsers.eof + + ( V("Blank")^2 / writer.paragraphsep + + V("Blank")^0 / writer.interblocksep + ) + ) + * V("Block") + * ( V("Blank")^0 * parsers.eof + + ( V("Blank")^2 / writer.paragraphsep + + V("Blank")^0 / writer.interblocksep + ) + ) + + ( V("Paragraph") + V("Plain") ) + * ( V("Blank")^0 * parsers.eof + + V("Blank")^0 / writer.paragraphsep + ) + )^0, + + ExpectedJekyllData = parsers.succeed, + + Blank = parsers.Blank, + Reference = parsers.Reference, + + Blockquote = parsers.Blockquote, + Verbatim = parsers.Verbatim, + ThematicBreak = parsers.ThematicBreak, + BulletList = parsers.BulletList, + OrderedList = parsers.OrderedList, + DisplayHtml = parsers.DisplayHtml, + Heading = parsers.Heading, + Paragraph = parsers.Paragraph, + Plain = parsers.Plain, + + EndlineExceptions = parsers.EndlineExceptions, + NoSoftLineBreakEndlineExceptions + = parsers.NoSoftLineBreakEndlineExceptions, + + Str = parsers.Str, + Space = parsers.Space, + NoSoftLineBreakSpace + = parsers.NoSoftLineBreakSpace, + OptionalIndent = parsers.OptionalIndent, + Endline = parsers.Endline, + EndlineNoSub = parsers.EndlineNoSub, + NoSoftLineBreakEndline + = parsers.NoSoftLineBreakEndline, + EndlineBreak = parsers.EndlineBreak, + LinkAndEmph = parsers.LinkAndEmph, + Code = parsers.Code, + AutoLinkUrl = parsers.AutoLinkUrl, + AutoLinkEmail = parsers.AutoLinkEmail, + AutoLinkRelativeReference + = parsers.AutoLinkRelativeReference, + InlineHtml = parsers.InlineHtml, + HtmlEntity = parsers.HtmlEntity, + EscapedChar = parsers.EscapedChar, + Smart = parsers.Smart, + Symbol = parsers.Symbol, + SpecialChar = parsers.fail, + InitializeState = parsers.succeed, + } + self.update_rule = function(rule_name, get_pattern) + assert(current_extension_name ~= nil) + assert(syntax[rule_name] ~= nil, + [[Rule ]] .. rule_name + .. [[ -> ... does not exist in markdown grammar]]) + local previous_pattern + local extension_name + if walkable_syntax[rule_name] then + local previous_accountable_pattern + = walkable_syntax[rule_name][1] + previous_pattern = previous_accountable_pattern[1] + extension_name + = previous_accountable_pattern[2] + .. ", " .. current_extension_name + else + previous_pattern = nil + extension_name = current_extension_name + end + local pattern + if type(get_pattern) == "function" then + pattern = get_pattern(previous_pattern) + else + assert(previous_pattern == nil, + [[Rule ]] .. rule_name .. + [[ has already been updated by ]] .. extension_name) + pattern = get_pattern + end + local accountable_pattern = { pattern, extension_name, rule_name } + walkable_syntax[rule_name] = { accountable_pattern } + end + local special_characters = {} + self.add_special_character = function(c) + table.insert(special_characters, c) + syntax.SpecialChar = S(table.concat(special_characters, "")) + end + + self.add_special_character("*") + self.add_special_character("[") + self.add_special_character("]") + self.add_special_character("<") + self.add_special_character("!") + self.add_special_character("\\") + self.initialize_named_group = function(name, value) + local pattern = Ct("") + if value ~= nil then + pattern = pattern / value + end + syntax.InitializeState = syntax.InitializeState + * Cg(pattern, name) + end + self.initialize_named_group("indent_info") + for _, extension in ipairs(extensions) do + current_extension_name = extension.name + extension.extend_writer(writer) + extension.extend_reader(self) + end + current_extension_name = nil + if options.debugExtensions then + local sorted_lhs = {} + for lhs, _ in pairs(walkable_syntax) do + table.insert(sorted_lhs, lhs) + end + table.sort(sorted_lhs) + + local output_lines = {"{"} + for lhs_index, lhs in ipairs(sorted_lhs) do + local encoded_lhs = util.encode_json_string(lhs) + table.insert(output_lines, [[ ]] ..encoded_lhs .. [[: []]) + local rule = walkable_syntax[lhs] + for rhs_index, rhs in ipairs(rule) do + local human_readable_rhs + if type(rhs) == "string" then + human_readable_rhs = rhs + else + local pattern_name + if rhs[3] then + pattern_name = rhs[3] + else + pattern_name = "Anonymous Pattern" + end + local extension_name = rhs[2] + human_readable_rhs = pattern_name .. [[ (]] + .. extension_name .. [[)]] + end + local encoded_rhs + = util.encode_json_string(human_readable_rhs) + local output_line = [[ ]] .. encoded_rhs + if rhs_index < #rule then + output_line = output_line .. "," + end + table.insert(output_lines, output_line) + end + local output_line = " ]" + if lhs_index < #sorted_lhs then + output_line = output_line .. "," + end + table.insert(output_lines, output_line) + end + table.insert(output_lines, "}") + + local output = table.concat(output_lines, "\n") + local output_filename = options.debugExtensionsFileName + local output_file = assert(io.open(output_filename, "w"), + [[Could not open file "]] .. output_filename + .. [[" for writing]]) + assert(output_file:write(output)) + assert(output_file:close()) + end + for lhs, rule in pairs(walkable_syntax) do + syntax[lhs] = parsers.fail + for _, rhs in ipairs(rule) do + local pattern + if type(rhs) == "string" then + pattern = V(rhs) + else + pattern = rhs[1] + if type(pattern) == "string" then + pattern = V(pattern) + end + end + syntax[lhs] = syntax[lhs] + pattern + end + end + if options.underscores then + self.add_special_character("_") + end + + if not options.codeSpans then + syntax.Code = parsers.fail + else + self.add_special_character("`") + end + + if not options.html then + syntax.DisplayHtml = parsers.fail + syntax.InlineHtml = parsers.fail + syntax.HtmlEntity = parsers.fail + else + self.add_special_character("&") + end + + if options.preserveTabs then + options.stripIndent = false + end + + if not options.smartEllipses then + syntax.Smart = parsers.fail + else + self.add_special_character(".") + end + + if not options.relativeReferences then + syntax.AutoLinkRelativeReference = parsers.fail + end + + if options.contentLevel == "inline" then + syntax[1] = "Inlines" + syntax.Inlines = V("InitializeState") + * parsers.Inline^0 + * ( parsers.spacing^0 + * parsers.eof / "") + syntax.Space = parsers.Space + parsers.blankline / writer.space + end + + local blocks_nested_t = util.table_copy(syntax) + blocks_nested_t.ExpectedJekyllData = parsers.succeed + parsers.blocks_nested = Ct(blocks_nested_t) + + parsers.blocks = Ct(syntax) + + local inlines_t = util.table_copy(syntax) + inlines_t[1] = "Inlines" + inlines_t.Inlines = V("InitializeState") + * parsers.Inline^0 + * ( parsers.spacing^0 + * parsers.eof / "") + parsers.inlines = Ct(inlines_t) + + local inlines_no_inline_note_t = util.table_copy(inlines_t) + inlines_no_inline_note_t.InlineNote = parsers.fail + parsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t) + + local inlines_no_html_t = util.table_copy(inlines_t) + inlines_no_html_t.DisplayHtml = parsers.fail + inlines_no_html_t.InlineHtml = parsers.fail + inlines_no_html_t.HtmlEntity = parsers.fail + parsers.inlines_no_html = Ct(inlines_no_html_t) + + local inlines_nbsp_t = util.table_copy(inlines_t) + inlines_nbsp_t.Endline = parsers.NonbreakingEndline + inlines_nbsp_t.Space = parsers.NonbreakingSpace + parsers.inlines_nbsp = Ct(inlines_nbsp_t) + + local inlines_no_link_or_emphasis_t = util.table_copy(inlines_t) + inlines_no_link_or_emphasis_t.LinkAndEmph = parsers.fail + inlines_no_link_or_emphasis_t.EndlineExceptions + = parsers.EndlineExceptions - parsers.eof + parsers.inlines_no_link_or_emphasis + = Ct(inlines_no_link_or_emphasis_t) + return function(input) + if options.unicodeNormalization then + local form = options.unicodeNormalizationForm + if form == "nfc" then + input = uni_algos.normalize.NFC(input) + elseif form == "nfd" then + input = uni_algos.normalize.NFD(input) + elseif form == "nfkc" then + input = uni_algos.normalize.NFKC(input) + elseif form == "nfkd" then + input = uni_algos.normalize.NFKD(input) + else + return writer.error( + format("Unknown normalization form %s", form)) + end + end + input = input:gsub("\r\n?", "\n") + if input:sub(-1) ~= "\n" then + input = input .. "\n" + end + references = {} + local document = self.parser_functions.parse_blocks(input) + local output = util.rope_to_string(writer.document(document)) + local undosep_start, undosep_end + local potential_secend_start, secend_start + local potential_sep_start, sep_start + while true do + -- find a `writer->undosep` + undosep_start, undosep_end + = output:find(writer.undosep_text, 1, true) + if undosep_start == nil then break end + -- skip any preceding section ends + secend_start = undosep_start + while true do + potential_secend_start = secend_start - #writer.secend_text + if potential_secend_start < 1 + or output:sub(potential_secend_start, + secend_start - 1) ~= writer.secend_text + then + break + end + secend_start = potential_secend_start + end + -- find an immediately preceding + -- block element / paragraph separator + sep_start = secend_start + potential_sep_start = sep_start - #writer.interblocksep_text + if potential_sep_start >= 1 + and output:sub(potential_sep_start, + sep_start - 1) == writer.interblocksep_text + then + sep_start = potential_sep_start + else + potential_sep_start = sep_start - #writer.paragraphsep_text + if potential_sep_start >= 1 + and output:sub(potential_sep_start, + sep_start - 1) == writer.paragraphsep_text + then + sep_start = potential_sep_start + end + end + -- remove `writer->undosep` and immediately preceding + -- block element / paragraph separator + output = output:sub(1, sep_start - 1) + .. output:sub(secend_start, undosep_start - 1) + .. output:sub(undosep_end + 1) + end + return output + end + end + return self +end +M.extensions = {} +M.extensions.bracketed_spans = function() + return { + name = "built-in bracketed_spans syntax extension", + extend_writer = function(self) + function self.span(s, attr) + if self.flatten_inlines then return s end + return {"\\markdownRendererBracketedSpanAttributeContextBegin", + self.attributes(attr), + s, + "\\markdownRendererBracketedSpanAttributeContextEnd{}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local span_label = parsers.lbracket + * (Cs((parsers.alphanumeric^1 + + parsers.inticks + + parsers.autolink + + V("InlineHtml") + + ( parsers.backslash * parsers.backslash) + + ( parsers.backslash + * (parsers.lbracket + parsers.rbracket) + + V("Space") + V("Endline") + + (parsers.any + - ( parsers.newline + + parsers.lbracket + + parsers.rbracket + + parsers.blankline^2))))^1) + / self.parser_functions.parse_inlines) + * parsers.rbracket + + local Span = span_label + * Ct(parsers.attributes) + / writer.span + + self.insert_pattern("Inline before LinkAndEmph", + Span, "Span") + end + } +end +M.extensions.citations = function(citation_nbsps) + return { + name = "built-in citations syntax extension", + extend_writer = function(self) + function self.citations(text_cites, cites) + local buffer = {} + if self.flatten_inlines then + for _,cite in ipairs(cites) do + if cite.prenote then + table.insert(buffer, {cite.prenote, " "}) + end + table.insert(buffer, cite.name) + if cite.postnote then + table.insert(buffer, {" ", cite.postnote}) + end + end + else + table.insert(buffer, + {"\\markdownRenderer", + text_cites and "TextCite" or "Cite", + "{", #cites, "}"}) + for _,cite in ipairs(cites) do + table.insert(buffer, + {cite.suppress_author and "-" or "+", "{", + cite.prenote or "", "}{", + cite.postnote or "", "}{", cite.name, "}"}) + end + end + return buffer + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local citation_chars + = parsers.alphanumeric + + S("#$%&-+<>~/_") + + local citation_name + = Cs(parsers.dash^-1) * parsers.at + * Cs(citation_chars + * ((( citation_chars + + parsers.internal_punctuation + - parsers.comma - parsers.semicolon) + * -#(( parsers.internal_punctuation + - parsers.comma + - parsers.semicolon)^0 + * -( citation_chars + + parsers.internal_punctuation + - parsers.comma + - parsers.semicolon)))^0 + * citation_chars)^-1) + + local citation_body_prenote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + parsers.autolink + + V("InlineHtml") + + V("Space") + V("EndlineNoSub") + + (parsers.anyescaped + - ( parsers.newline + + parsers.rbracket + + parsers.blankline^2)) + - ( parsers.spnl + * parsers.dash^-1 + * parsers.at))^1) + + local citation_body_postnote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + parsers.autolink + + V("InlineHtml") + + V("Space") + V("EndlineNoSub") + + (parsers.anyescaped + - ( parsers.newline + + parsers.rbracket + + parsers.semicolon + + parsers.blankline^2)) + - (parsers.spnl * parsers.rbracket))^1) + + local citation_body_chunk + = ( citation_body_prenote + * parsers.spnlc_sep + + Cc("") + * parsers.spnlc + ) + * citation_name + * ( parsers.internal_punctuation + - parsers.semicolon)^-1 + * ( parsers.spnlc / function(_) return end + * citation_body_postnote + + Cc("") + * parsers.spnlc + ) + + local citation_body + = citation_body_chunk + * ( parsers.semicolon + * parsers.spnlc + * citation_body_chunk + )^0 + + local citation_headless_body_postnote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + parsers.autolink + + V("InlineHtml") + + V("Space") + V("Endline") + + (parsers.anyescaped + - ( parsers.newline + + parsers.rbracket + + parsers.at + + parsers.semicolon + parsers.blankline^2)) + - (parsers.spnl * parsers.rbracket))^0) + + local citation_headless_body + = citation_headless_body_postnote + * ( parsers.semicolon + * parsers.spnlc + * citation_body_chunk + )^0 + + local citations + = function(text_cites, raw_cites) + local function normalize(str) + if str == "" then + str = nil + else + str = (citation_nbsps and + self.parser_functions.parse_inlines_nbsp or + self.parser_functions.parse_inlines)(str) + end + return str + end + + local cites = {} + for i = 1,#raw_cites,4 do + cites[#cites+1] = { + prenote = normalize(raw_cites[i]), + suppress_author = raw_cites[i+1] == "-", + name = writer.identifier(raw_cites[i+2]), + postnote = normalize(raw_cites[i+3]), + } + end + return writer.citations(text_cites, cites) + end + + local TextCitations + = Ct((parsers.spnlc + * Cc("") + * citation_name + * ((parsers.spnlc + * parsers.lbracket + * citation_headless_body + * parsers.rbracket) + Cc("")))^1) + / function(raw_cites) + return citations(true, raw_cites) + end + + local ParenthesizedCitations + = Ct((parsers.spnlc + * parsers.lbracket + * citation_body + * parsers.rbracket)^1) + / function(raw_cites) + return citations(false, raw_cites) + end + + local Citations = TextCitations + ParenthesizedCitations + + self.insert_pattern("Inline before LinkAndEmph", + Citations, "Citations") + + self.add_special_character("@") + self.add_special_character("-") + end + } +end +M.extensions.content_blocks = function(language_map) + local languages_json = (function() + local base, prev, curr + for _, pathname in ipairs{kpse.lookup(language_map, + {all=true})} do + local file = io.open(pathname, "r") + if not file then goto continue end + local input = assert(file:read("*a")) + assert(file:close()) + local json = input:gsub('("[^\n]-"):','[%1]=') + curr = load("_ENV = {}; return "..json)() + if type(curr) == "table" then + if base == nil then + base = curr + else + setmetatable(prev, { __index = curr }) + end + prev = curr + end + ::continue:: + end + return base or {} + end)() + + return { + name = "built-in content_blocks syntax extension", + extend_writer = function(self) + function self.contentblock(src,suf,type,tit) + if not self.is_writing then return "" end + src = src.."."..suf + suf = suf:lower() + if type == "onlineimage" then + return {"\\markdownRendererContentBlockOnlineImage{",suf,"}", + "{",self.string(src),"}", + "{",self.uri(src),"}", + "{",self.string(tit or ""),"}"} + elseif languages_json[suf] then + return {"\\markdownRendererContentBlockCode{",suf,"}", + "{",self.string(languages_json[suf]),"}", + "{",self.string(src),"}", + "{",self.uri(src),"}", + "{",self.string(tit or ""),"}"} + else + return {"\\markdownRendererContentBlock{",suf,"}", + "{",self.string(src),"}", + "{",self.uri(src),"}", + "{",self.string(tit or ""),"}"} + end + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local contentblock_tail + = parsers.optionaltitle + * (parsers.newline + parsers.eof) + + -- case insensitive online image suffix: + local onlineimagesuffix + = (function(...) + local parser = nil + for _, suffix in ipairs({...}) do + local pattern=nil + for i=1,#suffix do + local char=suffix:sub(i,i) + char = S(char:lower()..char:upper()) + if pattern == nil then + pattern = char + else + pattern = pattern * char + end + end + if parser == nil then + parser = pattern + else + parser = parser + pattern + end + end + return parser + end)("png", "jpg", "jpeg", "gif", "tif", "tiff") + + -- online image url for iA Writer content blocks with + -- mandatory suffix, allowing nested brackets: + local onlineimageurl + = (parsers.less + * Cs((parsers.anyescaped + - parsers.more + - parsers.spacing + - #(parsers.period + * onlineimagesuffix + * parsers.more + * contentblock_tail))^0) + * parsers.period + * Cs(onlineimagesuffix) + * parsers.more + + (Cs((parsers.inparens + + (parsers.anyescaped + - parsers.spacing + - parsers.rparent + - #(parsers.period + * onlineimagesuffix + * contentblock_tail)))^0) + * parsers.period + * Cs(onlineimagesuffix)) + ) * Cc("onlineimage") + + -- filename for iA Writer content blocks with mandatory suffix: + local localfilepath + = parsers.slash + * Cs((parsers.anyescaped + - parsers.tab + - parsers.newline + - #(parsers.period + * parsers.alphanumeric^1 + * contentblock_tail))^1) + * parsers.period + * Cs(parsers.alphanumeric^1) + * Cc("localfile") + + local ContentBlock + = parsers.check_trail_no_rem + * (localfilepath + onlineimageurl) + * contentblock_tail + / writer.contentblock + + self.insert_pattern("Block before Blockquote", + ContentBlock, "ContentBlock") + end + } +end +M.extensions.definition_lists = function(tight_lists) + return { + name = "built-in definition_lists syntax extension", + extend_writer = function(self) + local function dlitem(term, defs) + local retVal = {"\\markdownRendererDlItem{",term,"}"} + for _, def in ipairs(defs) do + retVal[#retVal+1] + = {"\\markdownRendererDlDefinitionBegin ",def, + "\\markdownRendererDlDefinitionEnd "} + end + retVal[#retVal+1] = "\\markdownRendererDlItemEnd " + return retVal + end + + function self.definitionlist(items,tight) + if not self.is_writing then return "" end + local buffer = {} + for _,item in ipairs(items) do + buffer[#buffer + 1] = dlitem(item.term, item.definitions) + end + if tight and tight_lists then + return {"\\markdownRendererDlBeginTight\n", buffer, + "\n\\markdownRendererDlEndTight"} + else + return {"\\markdownRendererDlBegin\n", buffer, + "\n\\markdownRendererDlEnd"} + end + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local defstartchar = S("~:") + + local defstart + = parsers.check_trail_length(0) * defstartchar + * #parsers.spacing + * (parsers.tab + parsers.space^-3) + + parsers.check_trail_length(1) + * defstartchar * #parsers.spacing + * (parsers.tab + parsers.space^-2) + + parsers.check_trail_length(2) + * defstartchar * #parsers.spacing + * (parsers.tab + parsers.space^-1) + + parsers.check_trail_length(3) + * defstartchar * #parsers.spacing + + local indented_line + = (parsers.check_minimal_indent / "") + * parsers.check_code_trail * parsers.line + + local blank + = parsers.check_minimal_blank_indent_and_any_trail + * parsers.optionalspace * parsers.newline + + local dlchunk = Cs(parsers.line * (indented_line - blank)^0) + + local indented_blocks = function(bl) + return Cs( bl + * (blank^1 * (parsers.check_minimal_indent / "") + * parsers.check_code_trail * -parsers.blankline * bl)^0 + * (blank^1 + parsers.eof)) + end + + local function definition_list_item(term, defs, _) + return { term = self.parser_functions.parse_inlines(term), + definitions = defs } + end + + local DefinitionListItemLoose + = C(parsers.line) * blank^0 + * Ct((parsers.check_minimal_indent * (defstart + * indented_blocks(dlchunk) + / self.parser_functions.parse_blocks_nested))^1) + * Cc(false) / definition_list_item + + local DefinitionListItemTight + = C(parsers.line) + * Ct((parsers.check_minimal_indent * (defstart * dlchunk + / self.parser_functions.parse_blocks_nested))^1) + * Cc(true) / definition_list_item + + local DefinitionList + = ( Ct(DefinitionListItemLoose^1) * Cc(false) + + Ct(DefinitionListItemTight^1) + * (blank^0 + * -DefinitionListItemLoose * Cc(true)) + ) / writer.definitionlist + + self.insert_pattern("Block after Heading", + DefinitionList, "DefinitionList") + end + } +end +M.extensions.fancy_lists = function() + return { + name = "built-in fancy_lists syntax extension", + extend_writer = function(self) + local options = self.options + + function self.fancylist(items,tight,startnum,numstyle,numdelim) + if not self.is_writing then return "" end + local buffer = {} + local num = startnum + for _,item in ipairs(items) do + if item ~= "" then + buffer[#buffer + 1] = self.fancyitem(item,num) + end + if num ~= nil and item ~= "" then + num = num + 1 + end + end + local contents = util.intersperse(buffer,"\n") + if tight and options.tightLists then + return {"\\markdownRendererFancyOlBeginTight{", + numstyle,"}{",numdelim,"}",contents, + "\n\\markdownRendererFancyOlEndTight "} + else + return {"\\markdownRendererFancyOlBegin{", + numstyle,"}{",numdelim,"}",contents, + "\n\\markdownRendererFancyOlEnd "} + end + end + function self.fancyitem(s,num) + if num ~= nil then + return {"\\markdownRendererFancyOlItemWithNumber{",num,"}",s, + "\\markdownRendererFancyOlItemEnd "} + else + return {"\\markdownRendererFancyOlItem ",s, + "\\markdownRendererFancyOlItemEnd "} + end + end + end, extend_reader = function(self) + local parsers = self.parsers + local options = self.options + local writer = self.writer + + local function combine_markers_and_delims(markers, delims) + local markers_table = {} + for _,marker in ipairs(markers) do + local start_marker + local continuation_marker + if type(marker) == "table" then + start_marker = marker[1] + continuation_marker = marker[2] + else + start_marker = marker + continuation_marker = marker + end + for _,delim in ipairs(delims) do + table.insert(markers_table, + {start_marker, continuation_marker, delim}) + end + end + return markers_table + end + + local function join_table_with_func(func, markers_table) + local pattern = func(table.unpack(markers_table[1])) + for i = 2, #markers_table do + pattern = pattern + func(table.unpack(markers_table[i])) + end + return pattern + end + + local lowercase_letter_marker = R("az") + local uppercase_letter_marker = R("AZ") + + local roman_marker = function(chars) + local m, d, c = P(chars[1]), P(chars[2]), P(chars[3]) + local l, x, v, i + = P(chars[4]), P(chars[5]), P(chars[6]), P(chars[7]) + return m^-3 + * (c*m + c*d + d^-1 * c^-3) + * (x*c + x*l + l^-1 * x^-3) + * (i*x + i*v + v^-1 * i^-3) + end + + local lowercase_roman_marker + = roman_marker({"m", "d", "c", "l", "x", "v", "i"}) + local uppercase_roman_marker + = roman_marker({"M", "D", "C", "L", "X", "V", "I"}) + + local lowercase_opening_roman_marker = P("i") + local uppercase_opening_roman_marker = P("I") + + local digit_marker = parsers.dig * parsers.dig^-8 + + local markers = { + {lowercase_opening_roman_marker, lowercase_roman_marker}, + {uppercase_opening_roman_marker, uppercase_roman_marker}, + lowercase_letter_marker, + uppercase_letter_marker, + lowercase_roman_marker, + uppercase_roman_marker, + digit_marker + } + + local delims = { + parsers.period, + parsers.rparent + } + + local markers_table = combine_markers_and_delims(markers, delims) + + local function enumerator(start_marker, _, + delimiter_type, interrupting) + local delimiter_range + local allowed_end + if interrupting then + delimiter_range = P("1") + allowed_end = C(parsers.spacechar^1) * #parsers.linechar + else + delimiter_range = start_marker + allowed_end = C(parsers.spacechar^1) + + #(parsers.newline + parsers.eof) + end + + return parsers.check_trail + * Ct(C(delimiter_range) * C(delimiter_type)) + * allowed_end + end + + local starter = join_table_with_func(enumerator, markers_table) + + local TightListItem = function(starter) + return parsers.add_indent(starter, "li") + * parsers.indented_content_tight + end + + local LooseListItem = function(starter) + return parsers.add_indent(starter, "li") + * parsers.indented_content_loose + * remove_indent("li") + end + + local function roman2number(roman) + local romans = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, + ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 } + local numeral = 0 + + local i = 1 + local len = string.len(roman) + while i < len do + local z1, z2 = romans[ string.sub(roman, i, i) ], + romans[ string.sub(roman, i+1, i+1) ] + if z1 < z2 then + numeral = numeral + (z2 - z1) + i = i + 2 + else + numeral = numeral + z1 + i = i + 1 + end + end + if i <= len then + numeral = numeral + romans[ string.sub(roman,i,i) ] + end + return numeral + end + + local function sniffstyle(numstr, delimend) + local numdelim + if delimend == ")" then + numdelim = "OneParen" + elseif delimend == "." then + numdelim = "Period" + else + numdelim = "Default" + end + + local num + num = numstr:match("^([I])$") + if num then + return roman2number(num), "UpperRoman", numdelim + end + num = numstr:match("^([i])$") + if num then + return roman2number(string.upper(num)), "LowerRoman", numdelim + end + num = numstr:match("^([A-Z])$") + if num then + return string.byte(num) - string.byte("A") + 1, + "UpperAlpha", numdelim + end + num = numstr:match("^([a-z])$") + if num then + return string.byte(num) - string.byte("a") + 1, + "LowerAlpha", numdelim + end + num = numstr:match("^([IVXLCDM]+)") + if num then + return roman2number(num), "UpperRoman", numdelim + end + num = numstr:match("^([ivxlcdm]+)") + if num then + return roman2number(string.upper(num)), "LowerRoman", numdelim + end + return math.floor(tonumber(numstr) or 1), "Decimal", numdelim + end + + local function fancylist(items,tight,start) + local startnum, numstyle, numdelim + = sniffstyle(start[2][1], start[2][2]) + return writer.fancylist(items,tight, + options.startNumber and startnum or 1, + numstyle or "Decimal", + numdelim or "Default") + end + + local FancyListOfType + = function(start_marker, continuation_marker, delimiter_type) + local enumerator_start + = enumerator(start_marker, continuation_marker, + delimiter_type) + local enumerator_cont + = enumerator(continuation_marker, continuation_marker, + delimiter_type) + return Cg(enumerator_start, "listtype") + * (Ct( TightListItem(Cb("listtype")) + * ((parsers.check_minimal_indent / "") + * TightListItem(enumerator_cont))^0) + * Cc(true) + * -#((parsers.conditionally_indented_blankline^0 / "") + * parsers.check_minimal_indent * enumerator_cont) + + Ct( LooseListItem(Cb("listtype")) + * ((parsers.conditionally_indented_blankline^0 / "") + * (parsers.check_minimal_indent / "") + * LooseListItem(enumerator_cont))^0) + * Cc(false) + ) * Ct(Cb("listtype")) / fancylist + end + + local FancyList + = join_table_with_func(FancyListOfType, markers_table) + + local Endline = parsers.newline + * (parsers.check_minimal_indent + * -parsers.EndlineExceptions + + parsers.check_optional_indent + * -parsers.EndlineExceptions + * -starter) + * parsers.spacechar^0 + / writer.soft_line_break + + self.update_rule("OrderedList", FancyList) + self.update_rule("Endline", Endline) + end + } +end +M.extensions.fenced_code = function(blank_before_code_fence, + allow_attributes, + allow_raw_blocks) + return { + name = "built-in fenced_code syntax extension", + extend_writer = function(self) + local options = self.options + + function self.fencedCode(s, i, attr) + if not self.is_writing then return "" end + s = s:gsub("\n$", "") + local buf = {} + if attr ~= nil then + table.insert(buf, + {"\\markdownRendererFencedCodeAttributeContextBegin", + self.attributes(attr)}) + end + local name = util.cache_verbatim(options.cacheDir, s) + table.insert(buf, + {"\\markdownRendererInputFencedCode{", + name,"}{",self.string(i),"}{",self.infostring(i),"}"}) + if attr ~= nil then + table.insert(buf, + "\\markdownRendererFencedCodeAttributeContextEnd{}") + end + return buf + end + + if allow_raw_blocks then + function self.rawBlock(s, attr) + if not self.is_writing then return "" end + s = s:gsub("\n$", "") + local name = util.cache_verbatim(options.cacheDir, s) + return {"\\markdownRendererInputRawBlock{", + name,"}{", self.string(attr),"}"} + end + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local function captures_geq_length(_,i,a,b) + return #a >= #b and i + end + + local function strip_enclosing_whitespaces(str) + return str:gsub("^%s*(.-)%s*$", "%1") + end + + local tilde_infostring = Cs(Cs((V("HtmlEntity") + + parsers.anyescaped + - parsers.newline)^0) + / strip_enclosing_whitespaces) + + local backtick_infostring + = Cs( Cs((V("HtmlEntity") + + ( -#(parsers.backslash * parsers.backtick) + * parsers.anyescaped) + - parsers.newline + - parsers.backtick)^0) + / strip_enclosing_whitespaces) + + local fenceindent + + local function has_trail(indent_table) + return indent_table ~= nil and + indent_table.trail ~= nil and + next(indent_table.trail) ~= nil + end + + local function has_indents(indent_table) + return indent_table ~= nil and + indent_table.indents ~= nil and + next(indent_table.indents) ~= nil + end + + local function get_last_indent_name(indent_table) + if has_indents(indent_table) then + return indent_table.indents[#indent_table.indents].name + end + end + + local count_fenced_start_indent = + function(_, _, indent_table, trail) + local last_indent_name = get_last_indent_name(indent_table) + fenceindent = 0 + if last_indent_name ~= "li" then + fenceindent = #trail + end + return true + end + + local fencehead = function(char, infostring) + return Cmt( Cb("indent_info") + * parsers.check_trail, count_fenced_start_indent) + * Cg(char^3, "fencelength") + * parsers.optionalspace + * infostring + * (parsers.newline + parsers.eof) + end + + local fencetail = function(char) + return parsers.check_trail_no_rem + * Cmt(C(char^3) * Cb("fencelength"), captures_geq_length) + * parsers.optionalspace * (parsers.newline + parsers.eof) + + parsers.eof + end + + local process_fenced_line = + function(s, i, -- luacheck: ignore s i + indent_table, line_content, is_blank) + local remainder = "" + if has_trail(indent_table) then + remainder = indent_table.trail.internal_remainder + end + + if is_blank + and get_last_indent_name(indent_table) == "li" then + remainder = "" + end + + local str = remainder .. line_content + local index = 1 + local remaining = fenceindent + + while true do + local c = str:sub(index, index) + if c == " " and remaining > 0 then + remaining = remaining - 1 + index = index + 1 + elseif c == "\t" and remaining > 3 then + remaining = remaining - 4 + index = index + 1 + else + break + end + end + + return true, str:sub(index) + end + + local fencedline = function(char) + return Cmt( Cb("indent_info") + * C(parsers.line - fencetail(char)) + * Cc(false), process_fenced_line) + end + + local blankfencedline + = Cmt( Cb("indent_info") + * C(parsers.blankline) + * Cc(true), process_fenced_line) + + local TildeFencedCode + = fencehead(parsers.tilde, tilde_infostring) + * Cs(( (parsers.check_minimal_blank_indent / "") + * blankfencedline + + ( parsers.check_minimal_indent / "") + * fencedline(parsers.tilde))^0) + * ( (parsers.check_minimal_indent / "") + * fencetail(parsers.tilde) + parsers.succeed) + + local BacktickFencedCode + = fencehead(parsers.backtick, backtick_infostring) + * Cs(( (parsers.check_minimal_blank_indent / "") + * blankfencedline + + (parsers.check_minimal_indent / "") + * fencedline(parsers.backtick))^0) + * ( (parsers.check_minimal_indent / "") + * fencetail(parsers.backtick) + parsers.succeed) + + local infostring_with_attributes + = Ct(C((parsers.linechar + - ( parsers.optionalspace + * parsers.attributes))^0) + * parsers.optionalspace + * Ct(parsers.attributes)) + + local FencedCode + = ((TildeFencedCode + BacktickFencedCode) + / function(infostring, code) + local expanded_code = self.expandtabs(code) + + if allow_raw_blocks then + local raw_attr = lpeg.match(parsers.raw_attribute, + infostring) + if raw_attr then + return writer.rawBlock(expanded_code, raw_attr) + end + end + + local attr = nil + if allow_attributes then + local match = lpeg.match(infostring_with_attributes, + infostring) + if match then + infostring, attr = table.unpack(match) + end + end + return writer.fencedCode(expanded_code, infostring, attr) + end) + + self.insert_pattern("Block after Verbatim", + FencedCode, "FencedCode") + + local fencestart + if blank_before_code_fence then + fencestart = parsers.fail + else + fencestart = fencehead(parsers.backtick, backtick_infostring) + + fencehead(parsers.tilde, tilde_infostring) + end + + self.update_rule("EndlineExceptions", function(previous_pattern) + if previous_pattern == nil then + previous_pattern = parsers.EndlineExceptions + end + return previous_pattern + fencestart + end) + + self.add_special_character("`") + self.add_special_character("~") + end + } +end +M.extensions.fenced_divs = function(blank_before_div_fence) + return { + name = "built-in fenced_divs syntax extension", + extend_writer = function(self) + function self.div_begin(attributes) + local start_output + = {"\\markdownRendererFencedDivAttributeContextBegin\n", + self.attributes(attributes)} + local end_output + = {"\\markdownRendererFencedDivAttributeContextEnd{}"} + return self.push_attributes( + "div", attributes, start_output, end_output) + end + function self.div_end() + return self.pop_attributes("div") + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + local fenced_div_infostring + = C((parsers.linechar + - ( parsers.spacechar^1 + * parsers.colon^1))^1) + + local fenced_div_begin = parsers.nonindentspace + * parsers.colon^3 + * parsers.optionalspace + * fenced_div_infostring + * ( parsers.spacechar^1 + * parsers.colon^1)^0 + * parsers.optionalspace + * (parsers.newline + parsers.eof) + + local fenced_div_end = parsers.nonindentspace + * parsers.colon^3 + * parsers.optionalspace + * (parsers.newline + parsers.eof) + self.initialize_named_group("fenced_div_level", "0") + self.initialize_named_group("fenced_div_num_opening_indents") + + local function increment_div_level() + local push_indent_table = + function(s, i, indent_table, -- luacheck: ignore s i + fenced_div_num_opening_indents, fenced_div_level) + fenced_div_level = tonumber(fenced_div_level) + 1 + local num_opening_indents = 0 + if indent_table.indents ~= nil then + num_opening_indents = #indent_table.indents + end + fenced_div_num_opening_indents[fenced_div_level] + = num_opening_indents + return true, fenced_div_num_opening_indents + end + + local increment_level = + function(s, i, fenced_div_level) -- luacheck: ignore s i + fenced_div_level = tonumber(fenced_div_level) + 1 + return true, tostring(fenced_div_level) + end + + return Cg( Cmt( Cb("indent_info") + * Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), push_indent_table) + , "fenced_div_num_opening_indents") + * Cg( Cmt( Cb("fenced_div_level"), increment_level) + , "fenced_div_level") + end + + local function decrement_div_level() + local pop_indent_table = + function(s, i, -- luacheck: ignore s i + fenced_div_indent_table, fenced_div_level) + fenced_div_level = tonumber(fenced_div_level) + fenced_div_indent_table[fenced_div_level] = nil + return true, tostring(fenced_div_level - 1) + end + + return Cg( Cmt( Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), pop_indent_table) + , "fenced_div_level") + end + + local non_fenced_div_block + = parsers.check_minimal_indent * V("Block") + - parsers.check_minimal_indent_and_trail * fenced_div_end + + local non_fenced_div_paragraph + = parsers.check_minimal_indent * V("Paragraph") + - parsers.check_minimal_indent_and_trail * fenced_div_end + + local blank = parsers.minimally_indented_blank + + local block_separated = parsers.block_sep_group(blank) + * non_fenced_div_block + + local loop_body_pair + = parsers.create_loop_body_pair(block_separated, + non_fenced_div_paragraph, + parsers.block_sep_group(blank), + parsers.par_sep_group(blank)) + + local content_loop = ( non_fenced_div_block + * loop_body_pair.block^0 + + non_fenced_div_paragraph + * block_separated + * loop_body_pair.block^0 + + non_fenced_div_paragraph + * loop_body_pair.par^0) + * blank^0 + + local FencedDiv = fenced_div_begin + / function (infostring) + local attr + = lpeg.match(Ct(parsers.attributes), + infostring) + if attr == nil then + attr = {"." .. infostring} + end + return attr + end + / writer.div_begin + * increment_div_level() + * parsers.skipblanklines + * Ct(content_loop) + * parsers.minimally_indented_blank^0 + * parsers.check_minimal_indent_and_trail + * fenced_div_end + * decrement_div_level() + * (Cc("") / writer.div_end) + + self.insert_pattern("Block after Verbatim", + FencedDiv, "FencedDiv") + + self.add_special_character(":") + + local function is_inside_div() + local check_div_level = + function(s, i, fenced_div_level) -- luacheck: ignore s i + fenced_div_level = tonumber(fenced_div_level) + return fenced_div_level > 0 + end + + return Cmt(Cb("fenced_div_level"), check_div_level) + end + + local function check_indent() + local compare_indent = + function(s, i, indent_table, -- luacheck: ignore s i + fenced_div_num_opening_indents, fenced_div_level) + fenced_div_level = tonumber(fenced_div_level) + local num_current_indents + = ( indent_table.current_line_indents ~= nil and + #indent_table.current_line_indents) or 0 + local num_opening_indents + = fenced_div_num_opening_indents[fenced_div_level] + return num_current_indents == num_opening_indents + end + + return Cmt( Cb("indent_info") + * Cb("fenced_div_num_opening_indents") + * Cb("fenced_div_level"), compare_indent) + end + + local fencestart = is_inside_div() + * fenced_div_end + * check_indent() + + if not blank_before_div_fence then + self.update_rule("EndlineExceptions", function(previous_pattern) + if previous_pattern == nil then + previous_pattern = parsers.EndlineExceptions + end + return previous_pattern + fencestart + end) + end + end + } +end +M.extensions.header_attributes = function() + return { + name = "built-in header_attributes syntax extension", + extend_writer = function() + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local function strip_atx_end(s) + return s:gsub("%s+#*%s*$","") + end + + local AtxHeading = Cg(parsers.heading_start, "level") + * parsers.optionalspace + * (C(((parsers.linechar + - (parsers.attributes + * parsers.optionalspace + * parsers.newline)) + * (parsers.linechar + - parsers.lbrace)^0)^1) + / strip_atx_end + / parsers.parse_heading_text) + * Cg(Ct(parsers.newline + + (parsers.attributes + * parsers.optionalspace + * parsers.newline)), "attributes") + * Cb("level") + * Cb("attributes") + / writer.heading + + local function strip_trailing_spaces(s) + return s:gsub("%s*$","") + end + + local heading_line = (parsers.linechar + - (parsers.attributes + * parsers.optionalspace + * parsers.newline))^1 + - parsers.thematic_break_lines + + local heading_text + = heading_line + * ( (V("Endline") / "\n") + * (heading_line - parsers.heading_level))^0 + * parsers.newline^-1 + + local SetextHeading + = parsers.freeze_trail * parsers.check_trail_no_rem + * #(heading_text + * (parsers.attributes + * parsers.optionalspace + * parsers.newline)^-1 + * parsers.check_minimal_indent + * parsers.check_trail + * parsers.heading_level) + * Cs(heading_text) / strip_trailing_spaces + / parsers.parse_heading_text + * Cg(Ct((parsers.attributes + * parsers.optionalspace + * parsers.newline)^-1), "attributes") + * parsers.check_minimal_indent_and_trail * parsers.heading_level + * Cb("attributes") + * parsers.newline + * parsers.unfreeze_trail + / writer.heading + + local Heading = AtxHeading + SetextHeading + self.update_rule("Heading", Heading) + end + } +end +M.extensions.inline_code_attributes = function() + return { + name = "built-in inline_code_attributes syntax extension", + extend_writer = function() + end, extend_reader = function(self) + local writer = self.writer + + local CodeWithAttributes = parsers.inticks + * Ct(parsers.attributes) + / writer.code + + self.insert_pattern("Inline before Code", + CodeWithAttributes, + "CodeWithAttributes") + end + } +end +M.extensions.line_blocks = function() + return { + name = "built-in line_blocks syntax extension", + extend_writer = function(self) + function self.lineblock(lines) + if not self.is_writing then return "" end + local buffer = {} + for i = 1, #lines - 1 do + buffer[#buffer + 1] = { lines[i], self.hard_line_break } + end + buffer[#buffer + 1] = lines[#lines] + + return {"\\markdownRendererLineBlockBegin\n" + ,buffer, + "\n\\markdownRendererLineBlockEnd "} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local LineBlock + = Ct((Cs(( (parsers.pipe * parsers.space) / "" + * ((parsers.space)/entities.char_entity("nbsp"))^0 + * parsers.linechar^0 * (parsers.newline/"")) + * (-parsers.pipe + * (parsers.space^1/" ") + * parsers.linechar^1 + * (parsers.newline/"") + )^0 + * (parsers.blankline/"")^0) + / self.parser_functions.parse_inlines)^1) + / writer.lineblock + + self.insert_pattern("Block after Blockquote", + LineBlock, "LineBlock") + end + } +end +M.extensions.mark = function() + return { + name = "built-in mark syntax extension", + extend_writer = function(self) + function self.mark(s) + if self.flatten_inlines then return s end + return {"\\markdownRendererMark{", s, "}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local doubleequals = P("==") + + local Mark + = parsers.between(V("Inline"), doubleequals, doubleequals) + / function (inlines) return writer.mark(inlines) end + + self.add_special_character("=") + self.insert_pattern("Inline before LinkAndEmph", + Mark, "Mark") + end + } +end +M.extensions.link_attributes = function() + return { + name = "built-in link_attributes syntax extension", + extend_writer = function() + end, extend_reader = function(self) + local parsers = self.parsers + local options = self.options + + local define_reference_parser + = (parsers.check_trail / "") + * parsers.link_label + * parsers.colon + * parsers.spnlc * parsers.url + * ( parsers.spnlc_sep * parsers.title + * (parsers.spnlc * Ct(parsers.attributes)) + * parsers.only_blank + + parsers.spnlc_sep * parsers.title * parsers.only_blank + + Cc("") * (parsers.spnlc * Ct(parsers.attributes)) + * parsers.only_blank + + Cc("") * parsers.only_blank) + + local ReferenceWithAttributes = define_reference_parser + / self.register_link + + self.update_rule("Reference", ReferenceWithAttributes) + + + local LinkWithAttributesAndEmph + = Ct(parsers.link_and_emph_table * Cg(Cc(true), + "match_link_attributes")) + / self.defer_link_and_emphasis_processing + + self.update_rule("LinkAndEmph", LinkWithAttributesAndEmph) + + local AutoLinkUrlWithAttributes + = parsers.auto_link_url + * Ct(parsers.attributes) + / self.auto_link_url + + self.insert_pattern("Inline before AutoLinkUrl", + AutoLinkUrlWithAttributes, + "AutoLinkUrlWithAttributes") + + local AutoLinkEmailWithAttributes + = parsers.auto_link_email + * Ct(parsers.attributes) + / self.auto_link_email + + self.insert_pattern("Inline before AutoLinkEmail", + AutoLinkEmailWithAttributes, + "AutoLinkEmailWithAttributes") + + if options.relativeReferences then + + local AutoLinkRelativeReferenceWithAttributes + = parsers.auto_link_relative_reference + * Ct(parsers.attributes) + / self.auto_link_url + + self.insert_pattern( + "Inline before AutoLinkRelativeReference", + AutoLinkRelativeReferenceWithAttributes, + "AutoLinkRelativeReferenceWithAttributes") + + end + + end + } +end +M.extensions.notes = function(notes, inline_notes) + assert(notes or inline_notes) + return { + name = "built-in notes syntax extension", + extend_writer = function(self) + function self.note(s) + if self.flatten_inlines then return "" end + return {"\\markdownRendererNote{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local rawnotes = parsers.rawnotes + + if inline_notes then + local InlineNote + = parsers.circumflex + * ( parsers.link_label + / self.parser_functions.parse_inlines_no_inline_note) + / writer.note + + self.insert_pattern("Inline after LinkAndEmph", + InlineNote, "InlineNote") + end + if notes then + local function strip_first_char(s) + return s:sub(2) + end + + local RawNoteRef + = #(parsers.lbracket * parsers.circumflex) + * parsers.link_label / strip_first_char + + -- like indirect_link + local function lookup_note(ref) + return writer.defer_call(function() + local found = rawnotes[self.normalize_tag(ref)] + if found then + return writer.note( + self.parser_functions.parse_blocks_nested(found)) + else + return {"[", + self.parser_functions.parse_inlines("^" .. ref), "]"} + end + end) + end + + local function register_note(ref,rawnote) + local normalized_tag = self.normalize_tag(ref) + if rawnotes[normalized_tag] == nil then + rawnotes[normalized_tag] = rawnote + end + return "" + end + + local NoteRef = RawNoteRef / lookup_note + + local optionally_indented_line + = parsers.check_optional_indent_and_any_trail * parsers.line + + local blank + = parsers.check_optional_blank_indent_and_any_trail + * parsers.optionalspace * parsers.newline + + local chunk + = Cs(parsers.line + * (optionally_indented_line - blank)^0) + + local indented_blocks = function(bl) + return Cs( bl + * ( blank^1 * (parsers.check_optional_indent / "") + * parsers.check_code_trail + * -parsers.blankline * bl)^0) + end + + local NoteBlock + = parsers.check_trail_no_rem + * RawNoteRef * parsers.colon + * parsers.spnlc * indented_blocks(chunk) + / register_note + + local Reference = NoteBlock + parsers.Reference + + self.update_rule("Reference", Reference) + self.insert_pattern("Inline before LinkAndEmph", + NoteRef, "NoteRef") + end + + self.add_special_character("^") + end + } +end +M.extensions.pipe_tables = function(table_captions, table_attributes) + + local function make_pipe_table_rectangular(rows) + local num_columns = #rows[2] + local rectangular_rows = {} + for i = 1, #rows do + local row = rows[i] + local rectangular_row = {} + for j = 1, num_columns do + rectangular_row[j] = row[j] or "" + end + table.insert(rectangular_rows, rectangular_row) + end + return rectangular_rows + end + + local function pipe_table_row(allow_empty_first_column + , nonempty_column + , column_separator + , column) + local row_beginning + if allow_empty_first_column then + row_beginning = -- empty first column + #(parsers.spacechar^4 + * column_separator) + * parsers.optionalspace + * column + * parsers.optionalspace + -- non-empty first column + + parsers.nonindentspace + * nonempty_column^-1 + * parsers.optionalspace + else + row_beginning = parsers.nonindentspace + * nonempty_column^-1 + * parsers.optionalspace + end + + return Ct(row_beginning + * (-- single column with no leading pipes + #(column_separator + * parsers.optionalspace + * parsers.newline) + * column_separator + * parsers.optionalspace + -- single column with leading pipes or + -- more than a single column + + (column_separator + * parsers.optionalspace + * column + * parsers.optionalspace)^1 + * (column_separator + * parsers.optionalspace)^-1)) + end + + return { + name = "built-in pipe_tables syntax extension", + extend_writer = function(self) + function self.table(rows, caption, attributes) + if not self.is_writing then return "" end + local buffer = {} + if attributes ~= nil then + table.insert(buffer, + "\\markdownRendererTableAttributeContextBegin\n") + table.insert(buffer, self.attributes(attributes)) + end + table.insert(buffer, + {"\\markdownRendererTable{", + caption or "", "}{", #rows - 1, "}{", + #rows[1], "}"}) + local temp = rows[2] -- put alignments on the first row + rows[2] = rows[1] + rows[1] = temp + for i, row in ipairs(rows) do + table.insert(buffer, "{") + for _, column in ipairs(row) do + if i > 1 then -- do not use braces for alignments + table.insert(buffer, "{") + end + table.insert(buffer, column) + if i > 1 then + table.insert(buffer, "}") + end + end + table.insert(buffer, "}") + end + if attributes ~= nil then + table.insert(buffer, + "\\markdownRendererTableAttributeContextEnd{}") + end + return buffer + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local table_hline_separator = parsers.pipe + parsers.plus + + local table_hline_column = (parsers.dash + - #(parsers.dash + * (parsers.spacechar + + table_hline_separator + + parsers.newline)))^1 + * (parsers.colon * Cc("r") + + parsers.dash * Cc("d")) + + parsers.colon + * (parsers.dash + - #(parsers.dash + * (parsers.spacechar + + table_hline_separator + + parsers.newline)))^1 + * (parsers.colon * Cc("c") + + parsers.dash * Cc("l")) + + local table_hline = pipe_table_row(false + , table_hline_column + , table_hline_separator + , table_hline_column) + + local table_caption_beginning + = ( parsers.check_minimal_blank_indent_and_any_trail_no_rem + * parsers.optionalspace * parsers.newline)^0 + * parsers.check_minimal_indent_and_trail + * (P("Table")^-1 * parsers.colon) + * parsers.optionalspace + + local function strip_trailing_spaces(s) + return s:gsub("%s*$","") + end + + local table_row + = pipe_table_row(true + , (C((parsers.linechar - parsers.pipe)^1) + / strip_trailing_spaces + / self.parser_functions.parse_inlines) + , parsers.pipe + , (C((parsers.linechar - parsers.pipe)^0) + / strip_trailing_spaces + / self.parser_functions.parse_inlines)) + + local table_caption + if table_captions then + table_caption = #table_caption_beginning + * table_caption_beginning + if table_attributes then + table_caption = table_caption + * (C(((( parsers.linechar + - (parsers.attributes + * parsers.optionalspace + * parsers.newline + * -#( parsers.optionalspace + * parsers.linechar))) + + ( parsers.newline + * #( parsers.optionalspace + * parsers.linechar) + * C(parsers.optionalspace) + / writer.space)) + * (parsers.linechar + - parsers.lbrace)^0)^1) + / self.parser_functions.parse_inlines) + * (parsers.newline + + ( Ct(parsers.attributes) + * parsers.optionalspace + * parsers.newline)) + else + table_caption = table_caption + * C(( parsers.linechar + + ( parsers.newline + * #( parsers.optionalspace + * parsers.linechar) + * C(parsers.optionalspace) + / writer.space))^1) + / self.parser_functions.parse_inlines + * parsers.newline + end + else + table_caption = parsers.fail + end + + local PipeTable + = Ct( table_row * parsers.newline + * (parsers.check_minimal_indent_and_trail / {}) + * table_hline * parsers.newline + * ( (parsers.check_minimal_indent / {}) + * table_row * parsers.newline)^0) + / make_pipe_table_rectangular + * table_caption^-1 + / writer.table + + self.insert_pattern("Block after Blockquote", + PipeTable, "PipeTable") + end + } +end +M.extensions.raw_inline = function() + return { + name = "built-in raw_inline syntax extension", + extend_writer = function(self) + local options = self.options + + function self.rawInline(s, attr) + if not self.is_writing then return "" end + if self.flatten_inlines then return s end + local name = util.cache_verbatim(options.cacheDir, s) + return {"\\markdownRendererInputRawInline{", + name,"}{", self.string(attr),"}"} + end + end, extend_reader = function(self) + local writer = self.writer + + local RawInline = parsers.inticks + * parsers.raw_attribute + / writer.rawInline + + self.insert_pattern("Inline before Code", + RawInline, "RawInline") + end + } +end +M.extensions.strike_through = function() + return { + name = "built-in strike_through syntax extension", + extend_writer = function(self) + function self.strike_through(s) + if self.flatten_inlines then return s end + return {"\\markdownRendererStrikeThrough{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local StrikeThrough = ( + parsers.between(parsers.Inline, parsers.doubletildes, + parsers.doubletildes) + ) / writer.strike_through + + self.insert_pattern("Inline after LinkAndEmph", + StrikeThrough, "StrikeThrough") + + self.add_special_character("~") + end + } +end +M.extensions.subscripts = function() + return { + name = "built-in subscripts syntax extension", + extend_writer = function(self) + function self.subscript(s) + if self.flatten_inlines then return s end + return {"\\markdownRendererSubscript{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local Subscript = ( + parsers.between(parsers.Str, parsers.tilde, parsers.tilde) + ) / writer.subscript + + self.insert_pattern("Inline after LinkAndEmph", + Subscript, "Subscript") + + self.add_special_character("~") + end + } +end +M.extensions.superscripts = function() + return { + name = "built-in superscripts syntax extension", + extend_writer = function(self) + function self.superscript(s) + if self.flatten_inlines then return s end + return {"\\markdownRendererSuperscript{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local Superscript = ( + parsers.between(parsers.Str, parsers.circumflex, + parsers.circumflex) + ) / writer.superscript + + self.insert_pattern("Inline after LinkAndEmph", + Superscript, "Superscript") + + self.add_special_character("^") + end + } +end +M.extensions.tex_math = function(tex_math_dollars, + tex_math_single_backslash, + tex_math_double_backslash) + return { + name = "built-in tex_math syntax extension", + extend_writer = function(self) + function self.display_math(s) + if self.flatten_inlines then return s end + return {"\\markdownRendererDisplayMath{",self.math(s),"}"} + end + function self.inline_math(s) + if self.flatten_inlines then return s end + return {"\\markdownRendererInlineMath{",self.math(s),"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local function between(p, starter, ender) + return (starter * Cs(p * (p - ender)^0) * ender) + end + + local function strip_preceding_whitespaces(str) + return str:gsub("^%s*(.-)$", "%1") + end + + local allowed_before_closing + = B( parsers.backslash * parsers.any + + parsers.any * (parsers.any - parsers.backslash)) + + local allowed_before_closing_no_space + = B( parsers.backslash * parsers.any + + parsers.any * (parsers.nonspacechar - parsers.backslash)) + + local dollar_math_content + = (parsers.newline * (parsers.check_optional_indent / "") + + parsers.backslash^-1 + * parsers.linechar) + - parsers.blankline^2 + - parsers.dollar + + local inline_math_opening_dollars = parsers.dollar + * #(parsers.nonspacechar) + + local inline_math_closing_dollars + = allowed_before_closing_no_space + * parsers.dollar + * -#(parsers.digit) + + local inline_math_dollars = between(Cs( dollar_math_content), + inline_math_opening_dollars, + inline_math_closing_dollars) + + local display_math_opening_dollars = parsers.dollar + * parsers.dollar + + local display_math_closing_dollars = parsers.dollar + * parsers.dollar + + local display_math_dollars = between(Cs( dollar_math_content), + display_math_opening_dollars, + display_math_closing_dollars) + local backslash_math_content + = (parsers.newline * (parsers.check_optional_indent / "") + + parsers.linechar) + - parsers.blankline^2 + local inline_math_opening_double = parsers.backslash + * parsers.backslash + * parsers.lparent + + local inline_math_closing_double = allowed_before_closing + * parsers.spacechar^0 + * parsers.backslash + * parsers.backslash + * parsers.rparent + + local inline_math_double = between(Cs( backslash_math_content), + inline_math_opening_double, + inline_math_closing_double) + / strip_preceding_whitespaces + + local display_math_opening_double = parsers.backslash + * parsers.backslash + * parsers.lbracket + + local display_math_closing_double = allowed_before_closing + * parsers.spacechar^0 + * parsers.backslash + * parsers.backslash + * parsers.rbracket + + local display_math_double = between(Cs( backslash_math_content), + display_math_opening_double, + display_math_closing_double) + / strip_preceding_whitespaces + local inline_math_opening_single = parsers.backslash + * parsers.lparent + + local inline_math_closing_single = allowed_before_closing + * parsers.spacechar^0 + * parsers.backslash + * parsers.rparent + + local inline_math_single = between(Cs( backslash_math_content), + inline_math_opening_single, + inline_math_closing_single) + / strip_preceding_whitespaces + + local display_math_opening_single = parsers.backslash + * parsers.lbracket + + local display_math_closing_single = allowed_before_closing + * parsers.spacechar^0 + * parsers.backslash + * parsers.rbracket + + local display_math_single = between(Cs( backslash_math_content), + display_math_opening_single, + display_math_closing_single) + / strip_preceding_whitespaces + + local display_math = parsers.fail + + local inline_math = parsers.fail + + if tex_math_dollars then + display_math = display_math + display_math_dollars + inline_math = inline_math + inline_math_dollars + end + + if tex_math_double_backslash then + display_math = display_math + display_math_double + inline_math = inline_math + inline_math_double + end + + if tex_math_single_backslash then + display_math = display_math + display_math_single + inline_math = inline_math + inline_math_single + end + + local TexMath = display_math / writer.display_math + + inline_math / writer.inline_math + + self.insert_pattern("Inline after LinkAndEmph", + TexMath, "TexMath") + + if tex_math_dollars then + self.add_special_character("$") + end + + if tex_math_single_backslash or tex_math_double_backslash then + self.add_special_character("\\") + self.add_special_character("[") + self.add_special_character("]") + self.add_special_character(")") + self.add_special_character("(") + end + end + } +end +M.extensions.jekyll_data = function(expect_jekyll_data, + ensure_jekyll_data) + return { + name = "built-in jekyll_data syntax extension", + extend_writer = function(self) + function self.jekyllData(d, t, p) + if not self.is_writing then return "" end + + local buf = {} + + local keys = {} + for k, _ in pairs(d) do + table.insert(keys, k) + end + table.sort(keys, function(first, second) + if type(first) ~= type(second) then + return type(first) < type(second) + else + return first < second + end + end) + + if not p then + table.insert(buf, "\\markdownRendererJekyllDataBegin") + end + + local is_sequence = false + if #d > 0 and #d == #keys then + for i=1, #d do + if d[i] == nil then + goto not_a_sequence + end + end + is_sequence = true + end + ::not_a_sequence:: + + if is_sequence then + table.insert(buf, + "\\markdownRendererJekyllDataSequenceBegin{") + table.insert(buf, self.identifier(p or "null")) + table.insert(buf, "}{") + table.insert(buf, #keys) + table.insert(buf, "}") + else + table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{") + table.insert(buf, self.identifier(p or "null")) + table.insert(buf, "}{") + table.insert(buf, #keys) + table.insert(buf, "}") + end + + for _, k in ipairs(keys) do + local v = d[k] + local typ = type(v) + k = tostring(k or "null") + if typ == "table" and next(v) ~= nil then + table.insert( + buf, + self.jekyllData(v, t, k) + ) + else + k = self.identifier(k) + v = tostring(v) + if typ == "boolean" then + table.insert(buf, "\\markdownRendererJekyllDataBoolean{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, v) + table.insert(buf, "}") + elseif typ == "number" then + table.insert(buf, "\\markdownRendererJekyllDataNumber{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, v) + table.insert(buf, "}") + elseif typ == "string" then + table.insert(buf, + "\\markdownRendererJekyllDataProgrammaticString{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, self.identifier(v)) + table.insert(buf, "}") + table.insert(buf, + "\\markdownRendererJekyllDataTypographicString{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, t(v)) + table.insert(buf, "}") + elseif typ == "table" then + table.insert(buf, "\\markdownRendererJekyllDataEmpty{") + table.insert(buf, k) + table.insert(buf, "}") + else + local error = self.error(format( + "Unexpected type %s for value of " + .. "YAML key %s", typ, k)) + table.insert(buf, error) + end + end + end + + if is_sequence then + table.insert(buf, "\\markdownRendererJekyllDataSequenceEnd") + else + table.insert(buf, "\\markdownRendererJekyllDataMappingEnd") + end + + if not p then + table.insert(buf, "\\markdownRendererJekyllDataEnd") + end + + return buf + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local JekyllData + = Cmt( C((parsers.line - P("---") - P("..."))^0) + , function(s, i, text) -- luacheck: ignore s i + local data + local ran_ok, _ = pcall(function() + -- TODO: Use `require("tinyyaml")` in TeX Live 2023 + local tinyyaml = require("markdown-tinyyaml") + data = tinyyaml.parse(text, {timestamps=false}) + end) + if ran_ok and data ~= nil then + return true, writer.jekyllData(data, function(s) + return self.parser_functions.parse_blocks_nested(s) + end, nil) + else + return false + end + end + ) + + local UnexpectedJekyllData + = P("---") + * parsers.blankline / 0 + -- if followed by blank, it's thematic break + * #(-parsers.blankline) + * JekyllData + * (P("---") + P("...")) + + local ExpectedJekyllData + = ( P("---") + * parsers.blankline / 0 + -- if followed by blank, it's thematic break + * #(-parsers.blankline) + )^-1 + * JekyllData + * (P("---") + P("..."))^-1 + + if ensure_jekyll_data then + ExpectedJekyllData = ExpectedJekyllData + * parsers.eof + else + ExpectedJekyllData = ( ExpectedJekyllData + * (V("Blank")^0 / writer.interblocksep) + )^-1 + end + + self.insert_pattern("Block before Blockquote", + UnexpectedJekyllData, "UnexpectedJekyllData") + if expect_jekyll_data then + self.update_rule("ExpectedJekyllData", ExpectedJekyllData) + end + end + } +end +function M.new(options) + options = options or {} + setmetatable(options, { __index = function (_, key) + return defaultOptions[key] end }) + if options.singletonCache and singletonCache.convert then + for k, v in pairs(defaultOptions) do + if type(v) == "table" then + for i = 1, math.max(#singletonCache.options[k], #options[k]) do + if singletonCache.options[k][i] ~= options[k][i] then + goto miss + end + end + elseif k ~= "cacheDir" + and singletonCache.options[k] ~= options[k] then + goto miss + end + end + return singletonCache.convert + end + ::miss:: + local extensions = {} + + if options.bracketedSpans then + local bracketed_spans_extension = M.extensions.bracketed_spans() + table.insert(extensions, bracketed_spans_extension) + end + + if options.contentBlocks then + local content_blocks_extension = M.extensions.content_blocks( + options.contentBlocksLanguageMap) + table.insert(extensions, content_blocks_extension) + end + + if options.definitionLists then + local definition_lists_extension = M.extensions.definition_lists( + options.tightLists) + table.insert(extensions, definition_lists_extension) + end + + if options.fencedCode then + local fenced_code_extension = M.extensions.fenced_code( + options.blankBeforeCodeFence, + options.fencedCodeAttributes, + options.rawAttribute) + table.insert(extensions, fenced_code_extension) + end + + if options.fencedDivs then + local fenced_div_extension = M.extensions.fenced_divs( + options.blankBeforeDivFence) + table.insert(extensions, fenced_div_extension) + end + + if options.headerAttributes then + local header_attributes_extension = M.extensions.header_attributes() + table.insert(extensions, header_attributes_extension) + end + + if options.inlineCodeAttributes then + local inline_code_attributes_extension = + M.extensions.inline_code_attributes() + table.insert(extensions, inline_code_attributes_extension) + end + + if options.jekyllData then + local jekyll_data_extension = M.extensions.jekyll_data( + options.expectJekyllData, options.ensureJekyllData) + table.insert(extensions, jekyll_data_extension) + end + + if options.linkAttributes then + local link_attributes_extension = + M.extensions.link_attributes() + table.insert(extensions, link_attributes_extension) + end + + if options.lineBlocks then + local line_block_extension = M.extensions.line_blocks() + table.insert(extensions, line_block_extension) + end + + if options.mark then + local mark_extension = M.extensions.mark() + table.insert(extensions, mark_extension) + end + + if options.pipeTables then + local pipe_tables_extension = M.extensions.pipe_tables( + options.tableCaptions, options.tableAttributes) + table.insert(extensions, pipe_tables_extension) + end + + if options.rawAttribute then + local raw_inline_extension = M.extensions.raw_inline() + table.insert(extensions, raw_inline_extension) + end + + if options.strikeThrough then + local strike_through_extension = M.extensions.strike_through() + table.insert(extensions, strike_through_extension) + end + + if options.subscripts then + local subscript_extension = M.extensions.subscripts() + table.insert(extensions, subscript_extension) + end + + if options.superscripts then + local superscript_extension = M.extensions.superscripts() + table.insert(extensions, superscript_extension) + end + + if options.texMathDollars or + options.texMathSingleBackslash or + options.texMathDoubleBackslash then + local tex_math_extension = M.extensions.tex_math( + options.texMathDollars, + options.texMathSingleBackslash, + options.texMathDoubleBackslash) + table.insert(extensions, tex_math_extension) + end + + if options.notes or options.inlineNotes then + local notes_extension = M.extensions.notes( + options.notes, options.inlineNotes) + table.insert(extensions, notes_extension) + end + + if options.citations then + local citations_extension + = M.extensions.citations(options.citationNbsps) + table.insert(extensions, citations_extension) + end + + if options.fancyLists then + local fancy_lists_extension = M.extensions.fancy_lists() + table.insert(extensions, fancy_lists_extension) + end + for _, user_extension_filename in ipairs(options.extensions) do + local user_extension = (function(filename) + local pathname = assert(kpse.find_file(filename), + [[Could not locate user-defined syntax extension "]] + .. filename) + local input_file = assert(io.open(pathname, "r"), + [[Could not open user-defined syntax extension "]] + .. pathname .. [[" for reading]]) + local input = assert(input_file:read("*a")) + assert(input_file:close()) + local user_extension, err = load([[ + local sandbox = {} + setmetatable(sandbox, {__index = _G}) + _ENV = sandbox + ]] .. input)() + assert(user_extension, + [[Failed to compile user-defined syntax extension "]] + .. pathname .. [[": ]] .. (err or [[]])) + assert(user_extension.api_version ~= nil, + [[User-defined syntax extension "]] .. pathname + .. [[" does not specify mandatory field "api_version"]]) + assert(type(user_extension.api_version) == "number", + [[User-defined syntax extension "]] .. pathname + .. [[" specifies field "api_version" of type "]] + .. type(user_extension.api_version) + .. [[" but "number" was expected]]) + assert(user_extension.api_version > 0 + and user_extension.api_version + <= metadata.user_extension_api_version, + [[User-defined syntax extension "]] .. pathname + .. [[" uses syntax extension API version "]] + .. user_extension.api_version .. [[ but markdown.lua ]] + .. metadata.version .. [[ uses API version ]] + .. metadata.user_extension_api_version + .. [[, which is incompatible]]) + + assert(user_extension.grammar_version ~= nil, + [[User-defined syntax extension "]] .. pathname + .. [[" does not specify mandatory field "grammar_version"]]) + assert(type(user_extension.grammar_version) == "number", + [[User-defined syntax extension "]] .. pathname + .. [[" specifies field "grammar_version" of type "]] + .. type(user_extension.grammar_version) + .. [[" but "number" was expected]]) + assert(user_extension.grammar_version == metadata.grammar_version, + [[User-defined syntax extension "]] .. pathname + .. [[" uses grammar version "]] + .. user_extension.grammar_version + .. [[ but markdown.lua ]] .. metadata.version + .. [[ uses grammar version ]] .. metadata.grammar_version + .. [[, which is incompatible]]) + + assert(user_extension.finalize_grammar ~= nil, + [[User-defined syntax extension "]] .. pathname + .. [[" does not specify mandatory "finalize_grammar" field]]) + assert(type(user_extension.finalize_grammar) == "function", + [[User-defined syntax extension "]] .. pathname + .. [[" specifies field "finalize_grammar" of type "]] + .. type(user_extension.finalize_grammar) + .. [[" but "function" was expected]]) + local extension = { + name = [[user-defined "]] .. pathname .. [[" syntax extension]], + extend_reader = user_extension.finalize_grammar, + extend_writer = function() end, + } + return extension + end)(user_extension_filename) + table.insert(extensions, user_extension) + end + local writer = M.writer.new(options) + local reader = M.reader.new(writer, options) + local convert = reader.finalize_grammar(extensions) + collectgarbage("collect") + if options.singletonCache then + local singletonCacheOptions = {} + for k, v in pairs(options) do + singletonCacheOptions[k] = v + end + setmetatable(singletonCacheOptions, + { __index = function (_, key) + return defaultOptions[key] end }) + singletonCache.options = singletonCacheOptions + singletonCache.convert = convert + end + return convert +end +return M diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua b/Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua index 36cef293ed9..25b351f7e40 100644 --- a/Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua +++ b/Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua @@ -253,7 +253,9 @@ function Parser:parsestring(line, stopper) end line = ssub(line, 2) end - return rtrim(buf), line + buf = rtrim(buf) + local val = tonumber(buf) or buf + return val, line end local function isemptyline(line) diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua index e65712cdcb6..7d1d4d6cde8 100644 --- a/Master/texmf-dist/tex/luatex/markdown/markdown.lua +++ b/Master/texmf-dist/tex/luatex/markdown/markdown.lua @@ -9,18 +9,18 @@ -- permit persons to whom the Software is furnished to do so, subject to -- the following conditions: -- --- The above copyright notice and this permission notice shall be included --- in all copies or substantial portions of the Software. +-- The above copyright notice and this permission notice shall be +-- included in all copies or substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF --- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +-- MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT. -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -- --- Copyright (C) 2016-2024 Vít Starý Novotný +-- Copyright (C) 2016-2024 Vít Starý Novotný, Andrej Genčur -- -- This work may be distributed and/or modified under the -- conditions of the LaTeX Project Public License, either version 1.3 @@ -58,68 +58,23 @@ -- those in the standard .ins files. -- local metadata = { - version = "3.5.0-0-gfd01a252", - comment = "A module for the conversion from markdown to plain TeX", - author = "John MacFarlane, Hans Hagen, Vít Starý Novotný", + version = "3.7.0-0-g98dece19", + comment = "A module for the conversion from markdown " + .. "to plain TeX", + author = "John MacFarlane, Hans Hagen, Vít Starý Novotný, " + .. "Andrej Genčur", copyright = {"2009-2016 John MacFarlane, Hans Hagen", - "2016-2023 Vít Starý Novotný"}, + "2016-2024 Vít Starý Novotný, Andrej Genčur"}, license = "LPPL 1.3c" } -if not modules then modules = { } end -modules['markdown'] = metadata -local lpeg = require("lpeg") -local unicode = require("unicode") local md5 = require("md5"); -(function() - local should_initialize = package.loaded.kpse == nil - or tex.initialize ~= nil - kpse = require("kpse") - if should_initialize then - kpse.set_program_name("luatex") - end -end)() -local uni_algos = require("lua-uni-algos") local M = {metadata = metadata} -local walkable_syntax = { - Block = { - "Blockquote", - "Verbatim", - "ThematicBreak", - "BulletList", - "OrderedList", - "DisplayHtml", - "Heading", - }, - BlockOrParagraph = { - "Block", - "Paragraph", - "Plain", - }, - Inline = { - "Str", - "Space", - "Endline", - "EndlineBreak", - "LinkAndEmph", - "Code", - "AutoLinkUrl", - "AutoLinkEmail", - "AutoLinkRelativeReference", - "InlineHtml", - "HtmlEntity", - "EscapedChar", - "Smart", - "Symbol", - }, -} local defaultOptions = {} -defaultOptions.eagerCache = false +defaultOptions.eagerCache = true defaultOptions.singletonCache = true -local singletonCache = { - convert = nil, - options = nil, -} +defaultOptions.unicodeNormalization = true +defaultOptions.unicodeNormalizationForm = "nfc" defaultOptions.cacheDir = "." defaultOptions.contentBlocksLanguageMap = "markdown-languages.json" defaultOptions.debugExtensionsFileName = "debug-extensions.json" @@ -139,9 +94,8 @@ defaultOptions.contentBlocks = false defaultOptions.contentLevel = "block" defaultOptions.debugExtensions = false defaultOptions.definitionLists = false +defaultOptions.ensureJekyllData = false defaultOptions.expectJekyllData = false -metadata.user_extension_api_version = 2 -metadata.grammar_version = 4 defaultOptions.extensions = {} defaultOptions.fancyLists = false defaultOptions.fencedCode = true @@ -182,21 +136,12 @@ defaultOptions.texMathDoubleBackslash = false defaultOptions.texMathSingleBackslash = false defaultOptions.tightLists = true defaultOptions.underscores = true -local upper, format, length = - string.upper, string.format, string.len -local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, Cp, any = - lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cg, lpeg.Cb, - lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs, lpeg.Cp, lpeg.P(1) local util = {} -function util.err(msg, exit_code) - io.stderr:write("markdown.lua: " .. msg .. "\n") - os.exit(exit_code or 1) -end function util.cache(dir, string, salt, transform, suffix) local digest = md5.sumhexa(string .. (salt or "")) local name = util.pathname(dir, digest .. suffix) local file = io.open(name, "r") - if file == nil then -- If no cache entry exists, then create a new one. + if file == nil then -- If no cache entry exists, create a new one. file = assert(io.open(name, "w"), [[Could not open file "]] .. name .. [[" for writing]]) local result = string @@ -208,116 +153,6 @@ function util.cache(dir, string, salt, transform, suffix) end return name end -function util.cache_verbatim(dir, string) - local name = util.cache(dir, string, nil, nil, ".verbatim") - return name -end -function util.table_copy(t) - local u = { } - for k, v in pairs(t) do u[k] = v end - return setmetatable(u, getmetatable(t)) -end -function util.encode_json_string(s) - s = s:gsub([[\]], [[\\]]) - s = s:gsub([["]], [[\"]]) - return [["]] .. s .. [["]] -end -function util.expand_tabs_in_line(s, tabstop) - local tab = tabstop or 4 - local corr = 0 - return (s:gsub("()\t", function(p) - local sp = tab - (p - 1 + corr) % tab - corr = corr - 1 + sp - return string.rep(" ", sp) - end)) -end -function util.walk(t, f) - local typ = type(t) - if typ == "string" then - f(t) - elseif typ == "table" then - local i = 1 - local n - n = t[i] - while n do - util.walk(n, f) - i = i + 1 - n = t[i] - end - elseif typ == "function" then - local ok, val = pcall(t) - if ok then - util.walk(val,f) - end - else - f(tostring(t)) - end -end -function util.flatten(ary) - local new = {} - for _,v in ipairs(ary) do - if type(v) == "table" then - for _,w in ipairs(util.flatten(v)) do - new[#new + 1] = w - end - else - new[#new + 1] = v - end - end - return new -end -function util.rope_to_string(rope) - local buffer = {} - util.walk(rope, function(x) buffer[#buffer + 1] = x end) - return table.concat(buffer) -end -function util.rope_last(rope) - if #rope == 0 then - return nil - else - local l = rope[#rope] - if type(l) == "table" then - return util.rope_last(l) - else - return l - end - end -end -function util.intersperse(ary, x) - local new = {} - local l = #ary - for i,v in ipairs(ary) do - local n = #new - new[n + 1] = v - if i ~= l then - new[n + 2] = x - end - end - return new -end -function util.map(ary, f) - local new = {} - for i,v in ipairs(ary) do - new[i] = f(v) - end - return new -end -function util.escaper(char_escapes, string_escapes) - local char_escapes_list = "" - for i,_ in pairs(char_escapes) do - char_escapes_list = char_escapes_list .. i - end - local escapable = S(char_escapes_list) / char_escapes - if string_escapes then - for k,v in pairs(string_escapes) do - escapable = P(k) / v + escapable - end - end - local escape_string = Cs((escapable + any)^0) - return function(s) - return lpeg.match(escape_string, s) - end -end function util.pathname(dir, file) if #dir == 0 then return file @@ -325,8021 +160,70 @@ function util.pathname(dir, file) return dir .. "/" .. file end end -local entities = {} - -local character_entities = { - ["Tab"] = 9, - ["NewLine"] = 10, - ["excl"] = 33, - ["QUOT"] = 34, - ["quot"] = 34, - ["num"] = 35, - ["dollar"] = 36, - ["percnt"] = 37, - ["AMP"] = 38, - ["amp"] = 38, - ["apos"] = 39, - ["lpar"] = 40, - ["rpar"] = 41, - ["ast"] = 42, - ["midast"] = 42, - ["plus"] = 43, - ["comma"] = 44, - ["period"] = 46, - ["sol"] = 47, - ["colon"] = 58, - ["semi"] = 59, - ["LT"] = 60, - ["lt"] = 60, - ["nvlt"] = {60, 8402}, - ["bne"] = {61, 8421}, - ["equals"] = 61, - ["GT"] = 62, - ["gt"] = 62, - ["nvgt"] = {62, 8402}, - ["quest"] = 63, - ["commat"] = 64, - ["lbrack"] = 91, - ["lsqb"] = 91, - ["bsol"] = 92, - ["rbrack"] = 93, - ["rsqb"] = 93, - ["Hat"] = 94, - ["UnderBar"] = 95, - ["lowbar"] = 95, - ["DiacriticalGrave"] = 96, - ["grave"] = 96, - ["fjlig"] = {102, 106}, - ["lbrace"] = 123, - ["lcub"] = 123, - ["VerticalLine"] = 124, - ["verbar"] = 124, - ["vert"] = 124, - ["rbrace"] = 125, - ["rcub"] = 125, - ["NonBreakingSpace"] = 160, - ["nbsp"] = 160, - ["iexcl"] = 161, - ["cent"] = 162, - ["pound"] = 163, - ["curren"] = 164, - ["yen"] = 165, - ["brvbar"] = 166, - ["sect"] = 167, - ["Dot"] = 168, - ["DoubleDot"] = 168, - ["die"] = 168, - ["uml"] = 168, - ["COPY"] = 169, - ["copy"] = 169, - ["ordf"] = 170, - ["laquo"] = 171, - ["not"] = 172, - ["shy"] = 173, - ["REG"] = 174, - ["circledR"] = 174, - ["reg"] = 174, - ["macr"] = 175, - ["strns"] = 175, - ["deg"] = 176, - ["PlusMinus"] = 177, - ["plusmn"] = 177, - ["pm"] = 177, - ["sup2"] = 178, - ["sup3"] = 179, - ["DiacriticalAcute"] = 180, - ["acute"] = 180, - ["micro"] = 181, - ["para"] = 182, - ["CenterDot"] = 183, - ["centerdot"] = 183, - ["middot"] = 183, - ["Cedilla"] = 184, - ["cedil"] = 184, - ["sup1"] = 185, - ["ordm"] = 186, - ["raquo"] = 187, - ["frac14"] = 188, - ["frac12"] = 189, - ["half"] = 189, - ["frac34"] = 190, - ["iquest"] = 191, - ["Agrave"] = 192, - ["Aacute"] = 193, - ["Acirc"] = 194, - ["Atilde"] = 195, - ["Auml"] = 196, - ["Aring"] = 197, - ["angst"] = 197, - ["AElig"] = 198, - ["Ccedil"] = 199, - ["Egrave"] = 200, - ["Eacute"] = 201, - ["Ecirc"] = 202, - ["Euml"] = 203, - ["Igrave"] = 204, - ["Iacute"] = 205, - ["Icirc"] = 206, - ["Iuml"] = 207, - ["ETH"] = 208, - ["Ntilde"] = 209, - ["Ograve"] = 210, - ["Oacute"] = 211, - ["Ocirc"] = 212, - ["Otilde"] = 213, - ["Ouml"] = 214, - ["times"] = 215, - ["Oslash"] = 216, - ["Ugrave"] = 217, - ["Uacute"] = 218, - ["Ucirc"] = 219, - ["Uuml"] = 220, - ["Yacute"] = 221, - ["THORN"] = 222, - ["szlig"] = 223, - ["agrave"] = 224, - ["aacute"] = 225, - ["acirc"] = 226, - ["atilde"] = 227, - ["auml"] = 228, - ["aring"] = 229, - ["aelig"] = 230, - ["ccedil"] = 231, - ["egrave"] = 232, - ["eacute"] = 233, - ["ecirc"] = 234, - ["euml"] = 235, - ["igrave"] = 236, - ["iacute"] = 237, - ["icirc"] = 238, - ["iuml"] = 239, - ["eth"] = 240, - ["ntilde"] = 241, - ["ograve"] = 242, - ["oacute"] = 243, - ["ocirc"] = 244, - ["otilde"] = 245, - ["ouml"] = 246, - ["div"] = 247, - ["divide"] = 247, - ["oslash"] = 248, - ["ugrave"] = 249, - ["uacute"] = 250, - ["ucirc"] = 251, - ["uuml"] = 252, - ["yacute"] = 253, - ["thorn"] = 254, - ["yuml"] = 255, - ["Amacr"] = 256, - ["amacr"] = 257, - ["Abreve"] = 258, - ["abreve"] = 259, - ["Aogon"] = 260, - ["aogon"] = 261, - ["Cacute"] = 262, - ["cacute"] = 263, - ["Ccirc"] = 264, - ["ccirc"] = 265, - ["Cdot"] = 266, - ["cdot"] = 267, - ["Ccaron"] = 268, - ["ccaron"] = 269, - ["Dcaron"] = 270, - ["dcaron"] = 271, - ["Dstrok"] = 272, - ["dstrok"] = 273, - ["Emacr"] = 274, - ["emacr"] = 275, - ["Edot"] = 278, - ["edot"] = 279, - ["Eogon"] = 280, - ["eogon"] = 281, - ["Ecaron"] = 282, - ["ecaron"] = 283, - ["Gcirc"] = 284, - ["gcirc"] = 285, - ["Gbreve"] = 286, - ["gbreve"] = 287, - ["Gdot"] = 288, - ["gdot"] = 289, - ["Gcedil"] = 290, - ["Hcirc"] = 292, - ["hcirc"] = 293, - ["Hstrok"] = 294, - ["hstrok"] = 295, - ["Itilde"] = 296, - ["itilde"] = 297, - ["Imacr"] = 298, - ["imacr"] = 299, - ["Iogon"] = 302, - ["iogon"] = 303, - ["Idot"] = 304, - ["imath"] = 305, - ["inodot"] = 305, - ["IJlig"] = 306, - ["ijlig"] = 307, - ["Jcirc"] = 308, - ["jcirc"] = 309, - ["Kcedil"] = 310, - ["kcedil"] = 311, - ["kgreen"] = 312, - ["Lacute"] = 313, - ["lacute"] = 314, - ["Lcedil"] = 315, - ["lcedil"] = 316, - ["Lcaron"] = 317, - ["lcaron"] = 318, - ["Lmidot"] = 319, - ["lmidot"] = 320, - ["Lstrok"] = 321, - ["lstrok"] = 322, - ["Nacute"] = 323, - ["nacute"] = 324, - ["Ncedil"] = 325, - ["ncedil"] = 326, - ["Ncaron"] = 327, - ["ncaron"] = 328, - ["napos"] = 329, - ["ENG"] = 330, - ["eng"] = 331, - ["Omacr"] = 332, - ["omacr"] = 333, - ["Odblac"] = 336, - ["odblac"] = 337, - ["OElig"] = 338, - ["oelig"] = 339, - ["Racute"] = 340, - ["racute"] = 341, - ["Rcedil"] = 342, - ["rcedil"] = 343, - ["Rcaron"] = 344, - ["rcaron"] = 345, - ["Sacute"] = 346, - ["sacute"] = 347, - ["Scirc"] = 348, - ["scirc"] = 349, - ["Scedil"] = 350, - ["scedil"] = 351, - ["Scaron"] = 352, - ["scaron"] = 353, - ["Tcedil"] = 354, - ["tcedil"] = 355, - ["Tcaron"] = 356, - ["tcaron"] = 357, - ["Tstrok"] = 358, - ["tstrok"] = 359, - ["Utilde"] = 360, - ["utilde"] = 361, - ["Umacr"] = 362, - ["umacr"] = 363, - ["Ubreve"] = 364, - ["ubreve"] = 365, - ["Uring"] = 366, - ["uring"] = 367, - ["Udblac"] = 368, - ["udblac"] = 369, - ["Uogon"] = 370, - ["uogon"] = 371, - ["Wcirc"] = 372, - ["wcirc"] = 373, - ["Ycirc"] = 374, - ["ycirc"] = 375, - ["Yuml"] = 376, - ["Zacute"] = 377, - ["zacute"] = 378, - ["Zdot"] = 379, - ["zdot"] = 380, - ["Zcaron"] = 381, - ["zcaron"] = 382, - ["fnof"] = 402, - ["imped"] = 437, - ["gacute"] = 501, - ["jmath"] = 567, - ["circ"] = 710, - ["Hacek"] = 711, - ["caron"] = 711, - ["Breve"] = 728, - ["breve"] = 728, - ["DiacriticalDot"] = 729, - ["dot"] = 729, - ["ring"] = 730, - ["ogon"] = 731, - ["DiacriticalTilde"] = 732, - ["tilde"] = 732, - ["DiacriticalDoubleAcute"] = 733, - ["dblac"] = 733, - ["DownBreve"] = 785, - ["Alpha"] = 913, - ["Beta"] = 914, - ["Gamma"] = 915, - ["Delta"] = 916, - ["Epsilon"] = 917, - ["Zeta"] = 918, - ["Eta"] = 919, - ["Theta"] = 920, - ["Iota"] = 921, - ["Kappa"] = 922, - ["Lambda"] = 923, - ["Mu"] = 924, - ["Nu"] = 925, - ["Xi"] = 926, - ["Omicron"] = 927, - ["Pi"] = 928, - ["Rho"] = 929, - ["Sigma"] = 931, - ["Tau"] = 932, - ["Upsilon"] = 933, - ["Phi"] = 934, - ["Chi"] = 935, - ["Psi"] = 936, - ["Omega"] = 937, - ["ohm"] = 937, - ["alpha"] = 945, - ["beta"] = 946, - ["gamma"] = 947, - ["delta"] = 948, - ["epsi"] = 949, - ["epsilon"] = 949, - ["zeta"] = 950, - ["eta"] = 951, - ["theta"] = 952, - ["iota"] = 953, - ["kappa"] = 954, - ["lambda"] = 955, - ["mu"] = 956, - ["nu"] = 957, - ["xi"] = 958, - ["omicron"] = 959, - ["pi"] = 960, - ["rho"] = 961, - ["sigmaf"] = 962, - ["sigmav"] = 962, - ["varsigma"] = 962, - ["sigma"] = 963, - ["tau"] = 964, - ["upsi"] = 965, - ["upsilon"] = 965, - ["phi"] = 966, - ["chi"] = 967, - ["psi"] = 968, - ["omega"] = 969, - ["thetasym"] = 977, - ["thetav"] = 977, - ["vartheta"] = 977, - ["Upsi"] = 978, - ["upsih"] = 978, - ["phiv"] = 981, - ["straightphi"] = 981, - ["varphi"] = 981, - ["piv"] = 982, - ["varpi"] = 982, - ["Gammad"] = 988, - ["digamma"] = 989, - ["gammad"] = 989, - ["kappav"] = 1008, - ["varkappa"] = 1008, - ["rhov"] = 1009, - ["varrho"] = 1009, - ["epsiv"] = 1013, - ["straightepsilon"] = 1013, - ["varepsilon"] = 1013, - ["backepsilon"] = 1014, - ["bepsi"] = 1014, - ["IOcy"] = 1025, - ["DJcy"] = 1026, - ["GJcy"] = 1027, - ["Jukcy"] = 1028, - ["DScy"] = 1029, - ["Iukcy"] = 1030, - ["YIcy"] = 1031, - ["Jsercy"] = 1032, - ["LJcy"] = 1033, - ["NJcy"] = 1034, - ["TSHcy"] = 1035, - ["KJcy"] = 1036, - ["Ubrcy"] = 1038, - ["DZcy"] = 1039, - ["Acy"] = 1040, - ["Bcy"] = 1041, - ["Vcy"] = 1042, - ["Gcy"] = 1043, - ["Dcy"] = 1044, - ["IEcy"] = 1045, - ["ZHcy"] = 1046, - ["Zcy"] = 1047, - ["Icy"] = 1048, - ["Jcy"] = 1049, - ["Kcy"] = 1050, - ["Lcy"] = 1051, - ["Mcy"] = 1052, - ["Ncy"] = 1053, - ["Ocy"] = 1054, - ["Pcy"] = 1055, - ["Rcy"] = 1056, - ["Scy"] = 1057, - ["Tcy"] = 1058, - ["Ucy"] = 1059, - ["Fcy"] = 1060, - ["KHcy"] = 1061, - ["TScy"] = 1062, - ["CHcy"] = 1063, - ["SHcy"] = 1064, - ["SHCHcy"] = 1065, - ["HARDcy"] = 1066, - ["Ycy"] = 1067, - ["SOFTcy"] = 1068, - ["Ecy"] = 1069, - ["YUcy"] = 1070, - ["YAcy"] = 1071, - ["acy"] = 1072, - ["bcy"] = 1073, - ["vcy"] = 1074, - ["gcy"] = 1075, - ["dcy"] = 1076, - ["iecy"] = 1077, - ["zhcy"] = 1078, - ["zcy"] = 1079, - ["icy"] = 1080, - ["jcy"] = 1081, - ["kcy"] = 1082, - ["lcy"] = 1083, - ["mcy"] = 1084, - ["ncy"] = 1085, - ["ocy"] = 1086, - ["pcy"] = 1087, - ["rcy"] = 1088, - ["scy"] = 1089, - ["tcy"] = 1090, - ["ucy"] = 1091, - ["fcy"] = 1092, - ["khcy"] = 1093, - ["tscy"] = 1094, - ["chcy"] = 1095, - ["shcy"] = 1096, - ["shchcy"] = 1097, - ["hardcy"] = 1098, - ["ycy"] = 1099, - ["softcy"] = 1100, - ["ecy"] = 1101, - ["yucy"] = 1102, - ["yacy"] = 1103, - ["iocy"] = 1105, - ["djcy"] = 1106, - ["gjcy"] = 1107, - ["jukcy"] = 1108, - ["dscy"] = 1109, - ["iukcy"] = 1110, - ["yicy"] = 1111, - ["jsercy"] = 1112, - ["ljcy"] = 1113, - ["njcy"] = 1114, - ["tshcy"] = 1115, - ["kjcy"] = 1116, - ["ubrcy"] = 1118, - ["dzcy"] = 1119, - ["ensp"] = 8194, - ["emsp"] = 8195, - ["emsp13"] = 8196, - ["emsp14"] = 8197, - ["numsp"] = 8199, - ["puncsp"] = 8200, - ["ThinSpace"] = 8201, - ["thinsp"] = 8201, - ["VeryThinSpace"] = 8202, - ["hairsp"] = 8202, - ["NegativeMediumSpace"] = 8203, - ["NegativeThickSpace"] = 8203, - ["NegativeThinSpace"] = 8203, - ["NegativeVeryThinSpace"] = 8203, - ["ZeroWidthSpace"] = 8203, - ["zwnj"] = 8204, - ["zwj"] = 8205, - ["lrm"] = 8206, - ["rlm"] = 8207, - ["dash"] = 8208, - ["hyphen"] = 8208, - ["ndash"] = 8211, - ["mdash"] = 8212, - ["horbar"] = 8213, - ["Verbar"] = 8214, - ["Vert"] = 8214, - ["OpenCurlyQuote"] = 8216, - ["lsquo"] = 8216, - ["CloseCurlyQuote"] = 8217, - ["rsquo"] = 8217, - ["rsquor"] = 8217, - ["lsquor"] = 8218, - ["sbquo"] = 8218, - ["OpenCurlyDoubleQuote"] = 8220, - ["ldquo"] = 8220, - ["CloseCurlyDoubleQuote"] = 8221, - ["rdquo"] = 8221, - ["rdquor"] = 8221, - ["bdquo"] = 8222, - ["ldquor"] = 8222, - ["dagger"] = 8224, - ["Dagger"] = 8225, - ["ddagger"] = 8225, - ["bull"] = 8226, - ["bullet"] = 8226, - ["nldr"] = 8229, - ["hellip"] = 8230, - ["mldr"] = 8230, - ["permil"] = 8240, - ["pertenk"] = 8241, - ["prime"] = 8242, - ["Prime"] = 8243, - ["tprime"] = 8244, - ["backprime"] = 8245, - ["bprime"] = 8245, - ["lsaquo"] = 8249, - ["rsaquo"] = 8250, - ["OverBar"] = 8254, - ["oline"] = 8254, - ["caret"] = 8257, - ["hybull"] = 8259, - ["frasl"] = 8260, - ["bsemi"] = 8271, - ["qprime"] = 8279, - ["MediumSpace"] = 8287, - ["ThickSpace"] = {8287, 8202}, - ["NoBreak"] = 8288, - ["ApplyFunction"] = 8289, - ["af"] = 8289, - ["InvisibleTimes"] = 8290, - ["it"] = 8290, - ["InvisibleComma"] = 8291, - ["ic"] = 8291, - ["euro"] = 8364, - ["TripleDot"] = 8411, - ["tdot"] = 8411, - ["DotDot"] = 8412, - ["Copf"] = 8450, - ["complexes"] = 8450, - ["incare"] = 8453, - ["gscr"] = 8458, - ["HilbertSpace"] = 8459, - ["Hscr"] = 8459, - ["hamilt"] = 8459, - ["Hfr"] = 8460, - ["Poincareplane"] = 8460, - ["Hopf"] = 8461, - ["quaternions"] = 8461, - ["planckh"] = 8462, - ["hbar"] = 8463, - ["hslash"] = 8463, - ["planck"] = 8463, - ["plankv"] = 8463, - ["Iscr"] = 8464, - ["imagline"] = 8464, - ["Ifr"] = 8465, - ["Im"] = 8465, - ["image"] = 8465, - ["imagpart"] = 8465, - ["Laplacetrf"] = 8466, - ["Lscr"] = 8466, - ["lagran"] = 8466, - ["ell"] = 8467, - ["Nopf"] = 8469, - ["naturals"] = 8469, - ["numero"] = 8470, - ["copysr"] = 8471, - ["weierp"] = 8472, - ["wp"] = 8472, - ["Popf"] = 8473, - ["primes"] = 8473, - ["Qopf"] = 8474, - ["rationals"] = 8474, - ["Rscr"] = 8475, - ["realine"] = 8475, - ["Re"] = 8476, - ["Rfr"] = 8476, - ["real"] = 8476, - ["realpart"] = 8476, - ["Ropf"] = 8477, - ["reals"] = 8477, - ["rx"] = 8478, - ["TRADE"] = 8482, - ["trade"] = 8482, - ["Zopf"] = 8484, - ["integers"] = 8484, - ["mho"] = 8487, - ["Zfr"] = 8488, - ["zeetrf"] = 8488, - ["iiota"] = 8489, - ["Bernoullis"] = 8492, - ["Bscr"] = 8492, - ["bernou"] = 8492, - ["Cayleys"] = 8493, - ["Cfr"] = 8493, - ["escr"] = 8495, - ["Escr"] = 8496, - ["expectation"] = 8496, - ["Fouriertrf"] = 8497, - ["Fscr"] = 8497, - ["Mellintrf"] = 8499, - ["Mscr"] = 8499, - ["phmmat"] = 8499, - ["order"] = 8500, - ["orderof"] = 8500, - ["oscr"] = 8500, - ["alefsym"] = 8501, - ["aleph"] = 8501, - ["beth"] = 8502, - ["gimel"] = 8503, - ["daleth"] = 8504, - ["CapitalDifferentialD"] = 8517, - ["DD"] = 8517, - ["DifferentialD"] = 8518, - ["dd"] = 8518, - ["ExponentialE"] = 8519, - ["ee"] = 8519, - ["exponentiale"] = 8519, - ["ImaginaryI"] = 8520, - ["ii"] = 8520, - ["frac13"] = 8531, - ["frac23"] = 8532, - ["frac15"] = 8533, - ["frac25"] = 8534, - ["frac35"] = 8535, - ["frac45"] = 8536, - ["frac16"] = 8537, - ["frac56"] = 8538, - ["frac18"] = 8539, - ["frac38"] = 8540, - ["frac58"] = 8541, - ["frac78"] = 8542, - ["LeftArrow"] = 8592, - ["ShortLeftArrow"] = 8592, - ["larr"] = 8592, - ["leftarrow"] = 8592, - ["slarr"] = 8592, - ["ShortUpArrow"] = 8593, - ["UpArrow"] = 8593, - ["uarr"] = 8593, - ["uparrow"] = 8593, - ["RightArrow"] = 8594, - ["ShortRightArrow"] = 8594, - ["rarr"] = 8594, - ["rightarrow"] = 8594, - ["srarr"] = 8594, - ["DownArrow"] = 8595, - ["ShortDownArrow"] = 8595, - ["darr"] = 8595, - ["downarrow"] = 8595, - ["LeftRightArrow"] = 8596, - ["harr"] = 8596, - ["leftrightarrow"] = 8596, - ["UpDownArrow"] = 8597, - ["updownarrow"] = 8597, - ["varr"] = 8597, - ["UpperLeftArrow"] = 8598, - ["nwarr"] = 8598, - ["nwarrow"] = 8598, - ["UpperRightArrow"] = 8599, - ["nearr"] = 8599, - ["nearrow"] = 8599, - ["LowerRightArrow"] = 8600, - ["searr"] = 8600, - ["searrow"] = 8600, - ["LowerLeftArrow"] = 8601, - ["swarr"] = 8601, - ["swarrow"] = 8601, - ["nlarr"] = 8602, - ["nleftarrow"] = 8602, - ["nrarr"] = 8603, - ["nrightarrow"] = 8603, - ["nrarrw"] = {8605, 824}, - ["rarrw"] = 8605, - ["rightsquigarrow"] = 8605, - ["Larr"] = 8606, - ["twoheadleftarrow"] = 8606, - ["Uarr"] = 8607, - ["Rarr"] = 8608, - ["twoheadrightarrow"] = 8608, - ["Darr"] = 8609, - ["larrtl"] = 8610, - ["leftarrowtail"] = 8610, - ["rarrtl"] = 8611, - ["rightarrowtail"] = 8611, - ["LeftTeeArrow"] = 8612, - ["mapstoleft"] = 8612, - ["UpTeeArrow"] = 8613, - ["mapstoup"] = 8613, - ["RightTeeArrow"] = 8614, - ["map"] = 8614, - ["mapsto"] = 8614, - ["DownTeeArrow"] = 8615, - ["mapstodown"] = 8615, - ["hookleftarrow"] = 8617, - ["larrhk"] = 8617, - ["hookrightarrow"] = 8618, - ["rarrhk"] = 8618, - ["larrlp"] = 8619, - ["looparrowleft"] = 8619, - ["looparrowright"] = 8620, - ["rarrlp"] = 8620, - ["harrw"] = 8621, - ["leftrightsquigarrow"] = 8621, - ["nharr"] = 8622, - ["nleftrightarrow"] = 8622, - ["Lsh"] = 8624, - ["lsh"] = 8624, - ["Rsh"] = 8625, - ["rsh"] = 8625, - ["ldsh"] = 8626, - ["rdsh"] = 8627, - ["crarr"] = 8629, - ["cularr"] = 8630, - ["curvearrowleft"] = 8630, - ["curarr"] = 8631, - ["curvearrowright"] = 8631, - ["circlearrowleft"] = 8634, - ["olarr"] = 8634, - ["circlearrowright"] = 8635, - ["orarr"] = 8635, - ["LeftVector"] = 8636, - ["leftharpoonup"] = 8636, - ["lharu"] = 8636, - ["DownLeftVector"] = 8637, - ["leftharpoondown"] = 8637, - ["lhard"] = 8637, - ["RightUpVector"] = 8638, - ["uharr"] = 8638, - ["upharpoonright"] = 8638, - ["LeftUpVector"] = 8639, - ["uharl"] = 8639, - ["upharpoonleft"] = 8639, - ["RightVector"] = 8640, - ["rharu"] = 8640, - ["rightharpoonup"] = 8640, - ["DownRightVector"] = 8641, - ["rhard"] = 8641, - ["rightharpoondown"] = 8641, - ["RightDownVector"] = 8642, - ["dharr"] = 8642, - ["downharpoonright"] = 8642, - ["LeftDownVector"] = 8643, - ["dharl"] = 8643, - ["downharpoonleft"] = 8643, - ["RightArrowLeftArrow"] = 8644, - ["rightleftarrows"] = 8644, - ["rlarr"] = 8644, - ["UpArrowDownArrow"] = 8645, - ["udarr"] = 8645, - ["LeftArrowRightArrow"] = 8646, - ["leftrightarrows"] = 8646, - ["lrarr"] = 8646, - ["leftleftarrows"] = 8647, - ["llarr"] = 8647, - ["upuparrows"] = 8648, - ["uuarr"] = 8648, - ["rightrightarrows"] = 8649, - ["rrarr"] = 8649, - ["ddarr"] = 8650, - ["downdownarrows"] = 8650, - ["ReverseEquilibrium"] = 8651, - ["leftrightharpoons"] = 8651, - ["lrhar"] = 8651, - ["Equilibrium"] = 8652, - ["rightleftharpoons"] = 8652, - ["rlhar"] = 8652, - ["nLeftarrow"] = 8653, - ["nlArr"] = 8653, - ["nLeftrightarrow"] = 8654, - ["nhArr"] = 8654, - ["nRightarrow"] = 8655, - ["nrArr"] = 8655, - ["DoubleLeftArrow"] = 8656, - ["Leftarrow"] = 8656, - ["lArr"] = 8656, - ["DoubleUpArrow"] = 8657, - ["Uparrow"] = 8657, - ["uArr"] = 8657, - ["DoubleRightArrow"] = 8658, - ["Implies"] = 8658, - ["Rightarrow"] = 8658, - ["rArr"] = 8658, - ["DoubleDownArrow"] = 8659, - ["Downarrow"] = 8659, - ["dArr"] = 8659, - ["DoubleLeftRightArrow"] = 8660, - ["Leftrightarrow"] = 8660, - ["hArr"] = 8660, - ["iff"] = 8660, - ["DoubleUpDownArrow"] = 8661, - ["Updownarrow"] = 8661, - ["vArr"] = 8661, - ["nwArr"] = 8662, - ["neArr"] = 8663, - ["seArr"] = 8664, - ["swArr"] = 8665, - ["Lleftarrow"] = 8666, - ["lAarr"] = 8666, - ["Rrightarrow"] = 8667, - ["rAarr"] = 8667, - ["zigrarr"] = 8669, - ["LeftArrowBar"] = 8676, - ["larrb"] = 8676, - ["RightArrowBar"] = 8677, - ["rarrb"] = 8677, - ["DownArrowUpArrow"] = 8693, - ["duarr"] = 8693, - ["loarr"] = 8701, - ["roarr"] = 8702, - ["hoarr"] = 8703, - ["ForAll"] = 8704, - ["forall"] = 8704, - ["comp"] = 8705, - ["complement"] = 8705, - ["PartialD"] = 8706, - ["npart"] = {8706, 824}, - ["part"] = 8706, - ["Exists"] = 8707, - ["exist"] = 8707, - ["NotExists"] = 8708, - ["nexist"] = 8708, - ["nexists"] = 8708, - ["empty"] = 8709, - ["emptyset"] = 8709, - ["emptyv"] = 8709, - ["varnothing"] = 8709, - ["Del"] = 8711, - ["nabla"] = 8711, - ["Element"] = 8712, - ["in"] = 8712, - ["isin"] = 8712, - ["isinv"] = 8712, - ["NotElement"] = 8713, - ["notin"] = 8713, - ["notinva"] = 8713, - ["ReverseElement"] = 8715, - ["SuchThat"] = 8715, - ["ni"] = 8715, - ["niv"] = 8715, - ["NotReverseElement"] = 8716, - ["notni"] = 8716, - ["notniva"] = 8716, - ["Product"] = 8719, - ["prod"] = 8719, - ["Coproduct"] = 8720, - ["coprod"] = 8720, - ["Sum"] = 8721, - ["sum"] = 8721, - ["minus"] = 8722, - ["MinusPlus"] = 8723, - ["mnplus"] = 8723, - ["mp"] = 8723, - ["dotplus"] = 8724, - ["plusdo"] = 8724, - ["Backslash"] = 8726, - ["setminus"] = 8726, - ["setmn"] = 8726, - ["smallsetminus"] = 8726, - ["ssetmn"] = 8726, - ["lowast"] = 8727, - ["SmallCircle"] = 8728, - ["compfn"] = 8728, - ["Sqrt"] = 8730, - ["radic"] = 8730, - ["Proportional"] = 8733, - ["prop"] = 8733, - ["propto"] = 8733, - ["varpropto"] = 8733, - ["vprop"] = 8733, - ["infin"] = 8734, - ["angrt"] = 8735, - ["ang"] = 8736, - ["angle"] = 8736, - ["nang"] = {8736, 8402}, - ["angmsd"] = 8737, - ["measuredangle"] = 8737, - ["angsph"] = 8738, - ["VerticalBar"] = 8739, - ["mid"] = 8739, - ["shortmid"] = 8739, - ["smid"] = 8739, - ["NotVerticalBar"] = 8740, - ["nmid"] = 8740, - ["nshortmid"] = 8740, - ["nsmid"] = 8740, - ["DoubleVerticalBar"] = 8741, - ["par"] = 8741, - ["parallel"] = 8741, - ["shortparallel"] = 8741, - ["spar"] = 8741, - ["NotDoubleVerticalBar"] = 8742, - ["npar"] = 8742, - ["nparallel"] = 8742, - ["nshortparallel"] = 8742, - ["nspar"] = 8742, - ["and"] = 8743, - ["wedge"] = 8743, - ["or"] = 8744, - ["vee"] = 8744, - ["cap"] = 8745, - ["caps"] = {8745, 65024}, - ["cup"] = 8746, - ["cups"] = {8746, 65024}, - ["Integral"] = 8747, - ["int"] = 8747, - ["Int"] = 8748, - ["iiint"] = 8749, - ["tint"] = 8749, - ["ContourIntegral"] = 8750, - ["conint"] = 8750, - ["oint"] = 8750, - ["Conint"] = 8751, - ["DoubleContourIntegral"] = 8751, - ["Cconint"] = 8752, - ["cwint"] = 8753, - ["ClockwiseContourIntegral"] = 8754, - ["cwconint"] = 8754, - ["CounterClockwiseContourIntegral"] = 8755, - ["awconint"] = 8755, - ["Therefore"] = 8756, - ["there4"] = 8756, - ["therefore"] = 8756, - ["Because"] = 8757, - ["becaus"] = 8757, - ["because"] = 8757, - ["ratio"] = 8758, - ["Colon"] = 8759, - ["Proportion"] = 8759, - ["dotminus"] = 8760, - ["minusd"] = 8760, - ["mDDot"] = 8762, - ["homtht"] = 8763, - ["Tilde"] = 8764, - ["nvsim"] = {8764, 8402}, - ["sim"] = 8764, - ["thicksim"] = 8764, - ["thksim"] = 8764, - ["backsim"] = 8765, - ["bsim"] = 8765, - ["race"] = {8765, 817}, - ["ac"] = 8766, - ["acE"] = {8766, 819}, - ["mstpos"] = 8766, - ["acd"] = 8767, - ["VerticalTilde"] = 8768, - ["wr"] = 8768, - ["wreath"] = 8768, - ["NotTilde"] = 8769, - ["nsim"] = 8769, - ["EqualTilde"] = 8770, - ["NotEqualTilde"] = {8770, 824}, - ["eqsim"] = 8770, - ["esim"] = 8770, - ["nesim"] = {8770, 824}, - ["TildeEqual"] = 8771, - ["sime"] = 8771, - ["simeq"] = 8771, - ["NotTildeEqual"] = 8772, - ["nsime"] = 8772, - ["nsimeq"] = 8772, - ["TildeFullEqual"] = 8773, - ["cong"] = 8773, - ["simne"] = 8774, - ["NotTildeFullEqual"] = 8775, - ["ncong"] = 8775, - ["TildeTilde"] = 8776, - ["ap"] = 8776, - ["approx"] = 8776, - ["asymp"] = 8776, - ["thickapprox"] = 8776, - ["thkap"] = 8776, - ["NotTildeTilde"] = 8777, - ["nap"] = 8777, - ["napprox"] = 8777, - ["ape"] = 8778, - ["approxeq"] = 8778, - ["apid"] = 8779, - ["napid"] = {8779, 824}, - ["backcong"] = 8780, - ["bcong"] = 8780, - ["CupCap"] = 8781, - ["asympeq"] = 8781, - ["nvap"] = {8781, 8402}, - ["Bumpeq"] = 8782, - ["HumpDownHump"] = 8782, - ["NotHumpDownHump"] = {8782, 824}, - ["bump"] = 8782, - ["nbump"] = {8782, 824}, - ["HumpEqual"] = 8783, - ["NotHumpEqual"] = {8783, 824}, - ["bumpe"] = 8783, - ["bumpeq"] = 8783, - ["nbumpe"] = {8783, 824}, - ["DotEqual"] = 8784, - ["doteq"] = 8784, - ["esdot"] = 8784, - ["nedot"] = {8784, 824}, - ["doteqdot"] = 8785, - ["eDot"] = 8785, - ["efDot"] = 8786, - ["fallingdotseq"] = 8786, - ["erDot"] = 8787, - ["risingdotseq"] = 8787, - ["Assign"] = 8788, - ["colone"] = 8788, - ["coloneq"] = 8788, - ["ecolon"] = 8789, - ["eqcolon"] = 8789, - ["ecir"] = 8790, - ["eqcirc"] = 8790, - ["circeq"] = 8791, - ["cire"] = 8791, - ["wedgeq"] = 8793, - ["veeeq"] = 8794, - ["triangleq"] = 8796, - ["trie"] = 8796, - ["equest"] = 8799, - ["questeq"] = 8799, - ["NotEqual"] = 8800, - ["ne"] = 8800, - ["Congruent"] = 8801, - ["bnequiv"] = {8801, 8421}, - ["equiv"] = 8801, - ["NotCongruent"] = 8802, - ["nequiv"] = 8802, - ["le"] = 8804, - ["leq"] = 8804, - ["nvle"] = {8804, 8402}, - ["GreaterEqual"] = 8805, - ["ge"] = 8805, - ["geq"] = 8805, - ["nvge"] = {8805, 8402}, - ["LessFullEqual"] = 8806, - ["lE"] = 8806, - ["leqq"] = 8806, - ["nlE"] = {8806, 824}, - ["nleqq"] = {8806, 824}, - ["GreaterFullEqual"] = 8807, - ["NotGreaterFullEqual"] = {8807, 824}, - ["gE"] = 8807, - ["geqq"] = 8807, - ["ngE"] = {8807, 824}, - ["ngeqq"] = {8807, 824}, - ["lnE"] = 8808, - ["lneqq"] = 8808, - ["lvertneqq"] = {8808, 65024}, - ["lvnE"] = {8808, 65024}, - ["gnE"] = 8809, - ["gneqq"] = 8809, - ["gvertneqq"] = {8809, 65024}, - ["gvnE"] = {8809, 65024}, - ["Lt"] = 8810, - ["NestedLessLess"] = 8810, - ["NotLessLess"] = {8810, 824}, - ["ll"] = 8810, - ["nLt"] = {8810, 8402}, - ["nLtv"] = {8810, 824}, - ["Gt"] = 8811, - ["NestedGreaterGreater"] = 8811, - ["NotGreaterGreater"] = {8811, 824}, - ["gg"] = 8811, - ["nGt"] = {8811, 8402}, - ["nGtv"] = {8811, 824}, - ["between"] = 8812, - ["twixt"] = 8812, - ["NotCupCap"] = 8813, - ["NotLess"] = 8814, - ["nless"] = 8814, - ["nlt"] = 8814, - ["NotGreater"] = 8815, - ["ngt"] = 8815, - ["ngtr"] = 8815, - ["NotLessEqual"] = 8816, - ["nle"] = 8816, - ["nleq"] = 8816, - ["NotGreaterEqual"] = 8817, - ["nge"] = 8817, - ["ngeq"] = 8817, - ["LessTilde"] = 8818, - ["lesssim"] = 8818, - ["lsim"] = 8818, - ["GreaterTilde"] = 8819, - ["gsim"] = 8819, - ["gtrsim"] = 8819, - ["NotLessTilde"] = 8820, - ["nlsim"] = 8820, - ["NotGreaterTilde"] = 8821, - ["ngsim"] = 8821, - ["LessGreater"] = 8822, - ["lessgtr"] = 8822, - ["lg"] = 8822, - ["GreaterLess"] = 8823, - ["gl"] = 8823, - ["gtrless"] = 8823, - ["NotLessGreater"] = 8824, - ["ntlg"] = 8824, - ["NotGreaterLess"] = 8825, - ["ntgl"] = 8825, - ["Precedes"] = 8826, - ["pr"] = 8826, - ["prec"] = 8826, - ["Succeeds"] = 8827, - ["sc"] = 8827, - ["succ"] = 8827, - ["PrecedesSlantEqual"] = 8828, - ["prcue"] = 8828, - ["preccurlyeq"] = 8828, - ["SucceedsSlantEqual"] = 8829, - ["sccue"] = 8829, - ["succcurlyeq"] = 8829, - ["PrecedesTilde"] = 8830, - ["precsim"] = 8830, - ["prsim"] = 8830, - ["NotSucceedsTilde"] = {8831, 824}, - ["SucceedsTilde"] = 8831, - ["scsim"] = 8831, - ["succsim"] = 8831, - ["NotPrecedes"] = 8832, - ["npr"] = 8832, - ["nprec"] = 8832, - ["NotSucceeds"] = 8833, - ["nsc"] = 8833, - ["nsucc"] = 8833, - ["NotSubset"] = {8834, 8402}, - ["nsubset"] = {8834, 8402}, - ["sub"] = 8834, - ["subset"] = 8834, - ["vnsub"] = {8834, 8402}, - ["NotSuperset"] = {8835, 8402}, - ["Superset"] = 8835, - ["nsupset"] = {8835, 8402}, - ["sup"] = 8835, - ["supset"] = 8835, - ["vnsup"] = {8835, 8402}, - ["nsub"] = 8836, - ["nsup"] = 8837, - ["SubsetEqual"] = 8838, - ["sube"] = 8838, - ["subseteq"] = 8838, - ["SupersetEqual"] = 8839, - ["supe"] = 8839, - ["supseteq"] = 8839, - ["NotSubsetEqual"] = 8840, - ["nsube"] = 8840, - ["nsubseteq"] = 8840, - ["NotSupersetEqual"] = 8841, - ["nsupe"] = 8841, - ["nsupseteq"] = 8841, - ["subne"] = 8842, - ["subsetneq"] = 8842, - ["varsubsetneq"] = {8842, 65024}, - ["vsubne"] = {8842, 65024}, - ["supne"] = 8843, - ["supsetneq"] = 8843, - ["varsupsetneq"] = {8843, 65024}, - ["vsupne"] = {8843, 65024}, - ["cupdot"] = 8845, - ["UnionPlus"] = 8846, - ["uplus"] = 8846, - ["NotSquareSubset"] = {8847, 824}, - ["SquareSubset"] = 8847, - ["sqsub"] = 8847, - ["sqsubset"] = 8847, - ["NotSquareSuperset"] = {8848, 824}, - ["SquareSuperset"] = 8848, - ["sqsup"] = 8848, - ["sqsupset"] = 8848, - ["SquareSubsetEqual"] = 8849, - ["sqsube"] = 8849, - ["sqsubseteq"] = 8849, - ["SquareSupersetEqual"] = 8850, - ["sqsupe"] = 8850, - ["sqsupseteq"] = 8850, - ["SquareIntersection"] = 8851, - ["sqcap"] = 8851, - ["sqcaps"] = {8851, 65024}, - ["SquareUnion"] = 8852, - ["sqcup"] = 8852, - ["sqcups"] = {8852, 65024}, - ["CirclePlus"] = 8853, - ["oplus"] = 8853, - ["CircleMinus"] = 8854, - ["ominus"] = 8854, - ["CircleTimes"] = 8855, - ["otimes"] = 8855, - ["osol"] = 8856, - ["CircleDot"] = 8857, - ["odot"] = 8857, - ["circledcirc"] = 8858, - ["ocir"] = 8858, - ["circledast"] = 8859, - ["oast"] = 8859, - ["circleddash"] = 8861, - ["odash"] = 8861, - ["boxplus"] = 8862, - ["plusb"] = 8862, - ["boxminus"] = 8863, - ["minusb"] = 8863, - ["boxtimes"] = 8864, - ["timesb"] = 8864, - ["dotsquare"] = 8865, - ["sdotb"] = 8865, - ["RightTee"] = 8866, - ["vdash"] = 8866, - ["LeftTee"] = 8867, - ["dashv"] = 8867, - ["DownTee"] = 8868, - ["top"] = 8868, - ["UpTee"] = 8869, - ["bot"] = 8869, - ["bottom"] = 8869, - ["perp"] = 8869, - ["models"] = 8871, - ["DoubleRightTee"] = 8872, - ["vDash"] = 8872, - ["Vdash"] = 8873, - ["Vvdash"] = 8874, - ["VDash"] = 8875, - ["nvdash"] = 8876, - ["nvDash"] = 8877, - ["nVdash"] = 8878, - ["nVDash"] = 8879, - ["prurel"] = 8880, - ["LeftTriangle"] = 8882, - ["vartriangleleft"] = 8882, - ["vltri"] = 8882, - ["RightTriangle"] = 8883, - ["vartriangleright"] = 8883, - ["vrtri"] = 8883, - ["LeftTriangleEqual"] = 8884, - ["ltrie"] = 8884, - ["nvltrie"] = {8884, 8402}, - ["trianglelefteq"] = 8884, - ["RightTriangleEqual"] = 8885, - ["nvrtrie"] = {8885, 8402}, - ["rtrie"] = 8885, - ["trianglerighteq"] = 8885, - ["origof"] = 8886, - ["imof"] = 8887, - ["multimap"] = 8888, - ["mumap"] = 8888, - ["hercon"] = 8889, - ["intcal"] = 8890, - ["intercal"] = 8890, - ["veebar"] = 8891, - ["barvee"] = 8893, - ["angrtvb"] = 8894, - ["lrtri"] = 8895, - ["Wedge"] = 8896, - ["bigwedge"] = 8896, - ["xwedge"] = 8896, - ["Vee"] = 8897, - ["bigvee"] = 8897, - ["xvee"] = 8897, - ["Intersection"] = 8898, - ["bigcap"] = 8898, - ["xcap"] = 8898, - ["Union"] = 8899, - ["bigcup"] = 8899, - ["xcup"] = 8899, - ["Diamond"] = 8900, - ["diam"] = 8900, - ["diamond"] = 8900, - ["sdot"] = 8901, - ["Star"] = 8902, - ["sstarf"] = 8902, - ["divideontimes"] = 8903, - ["divonx"] = 8903, - ["bowtie"] = 8904, - ["ltimes"] = 8905, - ["rtimes"] = 8906, - ["leftthreetimes"] = 8907, - ["lthree"] = 8907, - ["rightthreetimes"] = 8908, - ["rthree"] = 8908, - ["backsimeq"] = 8909, - ["bsime"] = 8909, - ["curlyvee"] = 8910, - ["cuvee"] = 8910, - ["curlywedge"] = 8911, - ["cuwed"] = 8911, - ["Sub"] = 8912, - ["Subset"] = 8912, - ["Sup"] = 8913, - ["Supset"] = 8913, - ["Cap"] = 8914, - ["Cup"] = 8915, - ["fork"] = 8916, - ["pitchfork"] = 8916, - ["epar"] = 8917, - ["lessdot"] = 8918, - ["ltdot"] = 8918, - ["gtdot"] = 8919, - ["gtrdot"] = 8919, - ["Ll"] = 8920, - ["nLl"] = {8920, 824}, - ["Gg"] = 8921, - ["ggg"] = 8921, - ["nGg"] = {8921, 824}, - ["LessEqualGreater"] = 8922, - ["leg"] = 8922, - ["lesg"] = {8922, 65024}, - ["lesseqgtr"] = 8922, - ["GreaterEqualLess"] = 8923, - ["gel"] = 8923, - ["gesl"] = {8923, 65024}, - ["gtreqless"] = 8923, - ["cuepr"] = 8926, - ["curlyeqprec"] = 8926, - ["cuesc"] = 8927, - ["curlyeqsucc"] = 8927, - ["NotPrecedesSlantEqual"] = 8928, - ["nprcue"] = 8928, - ["NotSucceedsSlantEqual"] = 8929, - ["nsccue"] = 8929, - ["NotSquareSubsetEqual"] = 8930, - ["nsqsube"] = 8930, - ["NotSquareSupersetEqual"] = 8931, - ["nsqsupe"] = 8931, - ["lnsim"] = 8934, - ["gnsim"] = 8935, - ["precnsim"] = 8936, - ["prnsim"] = 8936, - ["scnsim"] = 8937, - ["succnsim"] = 8937, - ["NotLeftTriangle"] = 8938, - ["nltri"] = 8938, - ["ntriangleleft"] = 8938, - ["NotRightTriangle"] = 8939, - ["nrtri"] = 8939, - ["ntriangleright"] = 8939, - ["NotLeftTriangleEqual"] = 8940, - ["nltrie"] = 8940, - ["ntrianglelefteq"] = 8940, - ["NotRightTriangleEqual"] = 8941, - ["nrtrie"] = 8941, - ["ntrianglerighteq"] = 8941, - ["vellip"] = 8942, - ["ctdot"] = 8943, - ["utdot"] = 8944, - ["dtdot"] = 8945, - ["disin"] = 8946, - ["isinsv"] = 8947, - ["isins"] = 8948, - ["isindot"] = 8949, - ["notindot"] = {8949, 824}, - ["notinvc"] = 8950, - ["notinvb"] = 8951, - ["isinE"] = 8953, - ["notinE"] = {8953, 824}, - ["nisd"] = 8954, - ["xnis"] = 8955, - ["nis"] = 8956, - ["notnivc"] = 8957, - ["notnivb"] = 8958, - ["barwed"] = 8965, - ["barwedge"] = 8965, - ["Barwed"] = 8966, - ["doublebarwedge"] = 8966, - ["LeftCeiling"] = 8968, - ["lceil"] = 8968, - ["RightCeiling"] = 8969, - ["rceil"] = 8969, - ["LeftFloor"] = 8970, - ["lfloor"] = 8970, - ["RightFloor"] = 8971, - ["rfloor"] = 8971, - ["drcrop"] = 8972, - ["dlcrop"] = 8973, - ["urcrop"] = 8974, - ["ulcrop"] = 8975, - ["bnot"] = 8976, - ["profline"] = 8978, - ["profsurf"] = 8979, - ["telrec"] = 8981, - ["target"] = 8982, - ["ulcorn"] = 8988, - ["ulcorner"] = 8988, - ["urcorn"] = 8989, - ["urcorner"] = 8989, - ["dlcorn"] = 8990, - ["llcorner"] = 8990, - ["drcorn"] = 8991, - ["lrcorner"] = 8991, - ["frown"] = 8994, - ["sfrown"] = 8994, - ["smile"] = 8995, - ["ssmile"] = 8995, - ["cylcty"] = 9005, - ["profalar"] = 9006, - ["topbot"] = 9014, - ["ovbar"] = 9021, - ["solbar"] = 9023, - ["angzarr"] = 9084, - ["lmoust"] = 9136, - ["lmoustache"] = 9136, - ["rmoust"] = 9137, - ["rmoustache"] = 9137, - ["OverBracket"] = 9140, - ["tbrk"] = 9140, - ["UnderBracket"] = 9141, - ["bbrk"] = 9141, - ["bbrktbrk"] = 9142, - ["OverParenthesis"] = 9180, - ["UnderParenthesis"] = 9181, - ["OverBrace"] = 9182, - ["UnderBrace"] = 9183, - ["trpezium"] = 9186, - ["elinters"] = 9191, - ["blank"] = 9251, - ["circledS"] = 9416, - ["oS"] = 9416, - ["HorizontalLine"] = 9472, - ["boxh"] = 9472, - ["boxv"] = 9474, - ["boxdr"] = 9484, - ["boxdl"] = 9488, - ["boxur"] = 9492, - ["boxul"] = 9496, - ["boxvr"] = 9500, - ["boxvl"] = 9508, - ["boxhd"] = 9516, - ["boxhu"] = 9524, - ["boxvh"] = 9532, - ["boxH"] = 9552, - ["boxV"] = 9553, - ["boxdR"] = 9554, - ["boxDr"] = 9555, - ["boxDR"] = 9556, - ["boxdL"] = 9557, - ["boxDl"] = 9558, - ["boxDL"] = 9559, - ["boxuR"] = 9560, - ["boxUr"] = 9561, - ["boxUR"] = 9562, - ["boxuL"] = 9563, - ["boxUl"] = 9564, - ["boxUL"] = 9565, - ["boxvR"] = 9566, - ["boxVr"] = 9567, - ["boxVR"] = 9568, - ["boxvL"] = 9569, - ["boxVl"] = 9570, - ["boxVL"] = 9571, - ["boxHd"] = 9572, - ["boxhD"] = 9573, - ["boxHD"] = 9574, - ["boxHu"] = 9575, - ["boxhU"] = 9576, - ["boxHU"] = 9577, - ["boxvH"] = 9578, - ["boxVh"] = 9579, - ["boxVH"] = 9580, - ["uhblk"] = 9600, - ["lhblk"] = 9604, - ["block"] = 9608, - ["blk14"] = 9617, - ["blk12"] = 9618, - ["blk34"] = 9619, - ["Square"] = 9633, - ["squ"] = 9633, - ["square"] = 9633, - ["FilledVerySmallSquare"] = 9642, - ["blacksquare"] = 9642, - ["squarf"] = 9642, - ["squf"] = 9642, - ["EmptyVerySmallSquare"] = 9643, - ["rect"] = 9645, - ["marker"] = 9646, - ["fltns"] = 9649, - ["bigtriangleup"] = 9651, - ["xutri"] = 9651, - ["blacktriangle"] = 9652, - ["utrif"] = 9652, - ["triangle"] = 9653, - ["utri"] = 9653, - ["blacktriangleright"] = 9656, - ["rtrif"] = 9656, - ["rtri"] = 9657, - ["triangleright"] = 9657, - ["bigtriangledown"] = 9661, - ["xdtri"] = 9661, - ["blacktriangledown"] = 9662, - ["dtrif"] = 9662, - ["dtri"] = 9663, - ["triangledown"] = 9663, - ["blacktriangleleft"] = 9666, - ["ltrif"] = 9666, - ["ltri"] = 9667, - ["triangleleft"] = 9667, - ["loz"] = 9674, - ["lozenge"] = 9674, - ["cir"] = 9675, - ["tridot"] = 9708, - ["bigcirc"] = 9711, - ["xcirc"] = 9711, - ["ultri"] = 9720, - ["urtri"] = 9721, - ["lltri"] = 9722, - ["EmptySmallSquare"] = 9723, - ["FilledSmallSquare"] = 9724, - ["bigstar"] = 9733, - ["starf"] = 9733, - ["star"] = 9734, - ["phone"] = 9742, - ["female"] = 9792, - ["male"] = 9794, - ["spades"] = 9824, - ["spadesuit"] = 9824, - ["clubs"] = 9827, - ["clubsuit"] = 9827, - ["hearts"] = 9829, - ["heartsuit"] = 9829, - ["diamondsuit"] = 9830, - ["diams"] = 9830, - ["sung"] = 9834, - ["flat"] = 9837, - ["natur"] = 9838, - ["natural"] = 9838, - ["sharp"] = 9839, - ["check"] = 10003, - ["checkmark"] = 10003, - ["cross"] = 10007, - ["malt"] = 10016, - ["maltese"] = 10016, - ["sext"] = 10038, - ["VerticalSeparator"] = 10072, - ["lbbrk"] = 10098, - ["rbbrk"] = 10099, - ["bsolhsub"] = 10184, - ["suphsol"] = 10185, - ["LeftDoubleBracket"] = 10214, - ["lobrk"] = 10214, - ["RightDoubleBracket"] = 10215, - ["robrk"] = 10215, - ["LeftAngleBracket"] = 10216, - ["lang"] = 10216, - ["langle"] = 10216, - ["RightAngleBracket"] = 10217, - ["rang"] = 10217, - ["rangle"] = 10217, - ["Lang"] = 10218, - ["Rang"] = 10219, - ["loang"] = 10220, - ["roang"] = 10221, - ["LongLeftArrow"] = 10229, - ["longleftarrow"] = 10229, - ["xlarr"] = 10229, - ["LongRightArrow"] = 10230, - ["longrightarrow"] = 10230, - ["xrarr"] = 10230, - ["LongLeftRightArrow"] = 10231, - ["longleftrightarrow"] = 10231, - ["xharr"] = 10231, - ["DoubleLongLeftArrow"] = 10232, - ["Longleftarrow"] = 10232, - ["xlArr"] = 10232, - ["DoubleLongRightArrow"] = 10233, - ["Longrightarrow"] = 10233, - ["xrArr"] = 10233, - ["DoubleLongLeftRightArrow"] = 10234, - ["Longleftrightarrow"] = 10234, - ["xhArr"] = 10234, - ["longmapsto"] = 10236, - ["xmap"] = 10236, - ["dzigrarr"] = 10239, - ["nvlArr"] = 10498, - ["nvrArr"] = 10499, - ["nvHarr"] = 10500, - ["Map"] = 10501, - ["lbarr"] = 10508, - ["bkarow"] = 10509, - ["rbarr"] = 10509, - ["lBarr"] = 10510, - ["dbkarow"] = 10511, - ["rBarr"] = 10511, - ["RBarr"] = 10512, - ["drbkarow"] = 10512, - ["DDotrahd"] = 10513, - ["UpArrowBar"] = 10514, - ["DownArrowBar"] = 10515, - ["Rarrtl"] = 10518, - ["latail"] = 10521, - ["ratail"] = 10522, - ["lAtail"] = 10523, - ["rAtail"] = 10524, - ["larrfs"] = 10525, - ["rarrfs"] = 10526, - ["larrbfs"] = 10527, - ["rarrbfs"] = 10528, - ["nwarhk"] = 10531, - ["nearhk"] = 10532, - ["hksearow"] = 10533, - ["searhk"] = 10533, - ["hkswarow"] = 10534, - ["swarhk"] = 10534, - ["nwnear"] = 10535, - ["nesear"] = 10536, - ["toea"] = 10536, - ["seswar"] = 10537, - ["tosa"] = 10537, - ["swnwar"] = 10538, - ["nrarrc"] = {10547, 824}, - ["rarrc"] = 10547, - ["cudarrr"] = 10549, - ["ldca"] = 10550, - ["rdca"] = 10551, - ["cudarrl"] = 10552, - ["larrpl"] = 10553, - ["curarrm"] = 10556, - ["cularrp"] = 10557, - ["rarrpl"] = 10565, - ["harrcir"] = 10568, - ["Uarrocir"] = 10569, - ["lurdshar"] = 10570, - ["ldrushar"] = 10571, - ["LeftRightVector"] = 10574, - ["RightUpDownVector"] = 10575, - ["DownLeftRightVector"] = 10576, - ["LeftUpDownVector"] = 10577, - ["LeftVectorBar"] = 10578, - ["RightVectorBar"] = 10579, - ["RightUpVectorBar"] = 10580, - ["RightDownVectorBar"] = 10581, - ["DownLeftVectorBar"] = 10582, - ["DownRightVectorBar"] = 10583, - ["LeftUpVectorBar"] = 10584, - ["LeftDownVectorBar"] = 10585, - ["LeftTeeVector"] = 10586, - ["RightTeeVector"] = 10587, - ["RightUpTeeVector"] = 10588, - ["RightDownTeeVector"] = 10589, - ["DownLeftTeeVector"] = 10590, - ["DownRightTeeVector"] = 10591, - ["LeftUpTeeVector"] = 10592, - ["LeftDownTeeVector"] = 10593, - ["lHar"] = 10594, - ["uHar"] = 10595, - ["rHar"] = 10596, - ["dHar"] = 10597, - ["luruhar"] = 10598, - ["ldrdhar"] = 10599, - ["ruluhar"] = 10600, - ["rdldhar"] = 10601, - ["lharul"] = 10602, - ["llhard"] = 10603, - ["rharul"] = 10604, - ["lrhard"] = 10605, - ["UpEquilibrium"] = 10606, - ["udhar"] = 10606, - ["ReverseUpEquilibrium"] = 10607, - ["duhar"] = 10607, - ["RoundImplies"] = 10608, - ["erarr"] = 10609, - ["simrarr"] = 10610, - ["larrsim"] = 10611, - ["rarrsim"] = 10612, - ["rarrap"] = 10613, - ["ltlarr"] = 10614, - ["gtrarr"] = 10616, - ["subrarr"] = 10617, - ["suplarr"] = 10619, - ["lfisht"] = 10620, - ["rfisht"] = 10621, - ["ufisht"] = 10622, - ["dfisht"] = 10623, - ["lopar"] = 10629, - ["ropar"] = 10630, - ["lbrke"] = 10635, - ["rbrke"] = 10636, - ["lbrkslu"] = 10637, - ["rbrksld"] = 10638, - ["lbrksld"] = 10639, - ["rbrkslu"] = 10640, - ["langd"] = 10641, - ["rangd"] = 10642, - ["lparlt"] = 10643, - ["rpargt"] = 10644, - ["gtlPar"] = 10645, - ["ltrPar"] = 10646, - ["vzigzag"] = 10650, - ["vangrt"] = 10652, - ["angrtvbd"] = 10653, - ["ange"] = 10660, - ["range"] = 10661, - ["dwangle"] = 10662, - ["uwangle"] = 10663, - ["angmsdaa"] = 10664, - ["angmsdab"] = 10665, - ["angmsdac"] = 10666, - ["angmsdad"] = 10667, - ["angmsdae"] = 10668, - ["angmsdaf"] = 10669, - ["angmsdag"] = 10670, - ["angmsdah"] = 10671, - ["bemptyv"] = 10672, - ["demptyv"] = 10673, - ["cemptyv"] = 10674, - ["raemptyv"] = 10675, - ["laemptyv"] = 10676, - ["ohbar"] = 10677, - ["omid"] = 10678, - ["opar"] = 10679, - ["operp"] = 10681, - ["olcross"] = 10683, - ["odsold"] = 10684, - ["olcir"] = 10686, - ["ofcir"] = 10687, - ["olt"] = 10688, - ["ogt"] = 10689, - ["cirscir"] = 10690, - ["cirE"] = 10691, - ["solb"] = 10692, - ["bsolb"] = 10693, - ["boxbox"] = 10697, - ["trisb"] = 10701, - ["rtriltri"] = 10702, - ["LeftTriangleBar"] = 10703, - ["NotLeftTriangleBar"] = {10703, 824}, - ["NotRightTriangleBar"] = {10704, 824}, - ["RightTriangleBar"] = 10704, - ["iinfin"] = 10716, - ["infintie"] = 10717, - ["nvinfin"] = 10718, - ["eparsl"] = 10723, - ["smeparsl"] = 10724, - ["eqvparsl"] = 10725, - ["blacklozenge"] = 10731, - ["lozf"] = 10731, - ["RuleDelayed"] = 10740, - ["dsol"] = 10742, - ["bigodot"] = 10752, - ["xodot"] = 10752, - ["bigoplus"] = 10753, - ["xoplus"] = 10753, - ["bigotimes"] = 10754, - ["xotime"] = 10754, - ["biguplus"] = 10756, - ["xuplus"] = 10756, - ["bigsqcup"] = 10758, - ["xsqcup"] = 10758, - ["iiiint"] = 10764, - ["qint"] = 10764, - ["fpartint"] = 10765, - ["cirfnint"] = 10768, - ["awint"] = 10769, - ["rppolint"] = 10770, - ["scpolint"] = 10771, - ["npolint"] = 10772, - ["pointint"] = 10773, - ["quatint"] = 10774, - ["intlarhk"] = 10775, - ["pluscir"] = 10786, - ["plusacir"] = 10787, - ["simplus"] = 10788, - ["plusdu"] = 10789, - ["plussim"] = 10790, - ["plustwo"] = 10791, - ["mcomma"] = 10793, - ["minusdu"] = 10794, - ["loplus"] = 10797, - ["roplus"] = 10798, - ["Cross"] = 10799, - ["timesd"] = 10800, - ["timesbar"] = 10801, - ["smashp"] = 10803, - ["lotimes"] = 10804, - ["rotimes"] = 10805, - ["otimesas"] = 10806, - ["Otimes"] = 10807, - ["odiv"] = 10808, - ["triplus"] = 10809, - ["triminus"] = 10810, - ["tritime"] = 10811, - ["intprod"] = 10812, - ["iprod"] = 10812, - ["amalg"] = 10815, - ["capdot"] = 10816, - ["ncup"] = 10818, - ["ncap"] = 10819, - ["capand"] = 10820, - ["cupor"] = 10821, - ["cupcap"] = 10822, - ["capcup"] = 10823, - ["cupbrcap"] = 10824, - ["capbrcup"] = 10825, - ["cupcup"] = 10826, - ["capcap"] = 10827, - ["ccups"] = 10828, - ["ccaps"] = 10829, - ["ccupssm"] = 10832, - ["And"] = 10835, - ["Or"] = 10836, - ["andand"] = 10837, - ["oror"] = 10838, - ["orslope"] = 10839, - ["andslope"] = 10840, - ["andv"] = 10842, - ["orv"] = 10843, - ["andd"] = 10844, - ["ord"] = 10845, - ["wedbar"] = 10847, - ["sdote"] = 10854, - ["simdot"] = 10858, - ["congdot"] = 10861, - ["ncongdot"] = {10861, 824}, - ["easter"] = 10862, - ["apacir"] = 10863, - ["apE"] = 10864, - ["napE"] = {10864, 824}, - ["eplus"] = 10865, - ["pluse"] = 10866, - ["Esim"] = 10867, - ["Colone"] = 10868, - ["Equal"] = 10869, - ["ddotseq"] = 10871, - ["eDDot"] = 10871, - ["equivDD"] = 10872, - ["ltcir"] = 10873, - ["gtcir"] = 10874, - ["ltquest"] = 10875, - ["gtquest"] = 10876, - ["LessSlantEqual"] = 10877, - ["NotLessSlantEqual"] = {10877, 824}, - ["leqslant"] = 10877, - ["les"] = 10877, - ["nleqslant"] = {10877, 824}, - ["nles"] = {10877, 824}, - ["GreaterSlantEqual"] = 10878, - ["NotGreaterSlantEqual"] = {10878, 824}, - ["geqslant"] = 10878, - ["ges"] = 10878, - ["ngeqslant"] = {10878, 824}, - ["nges"] = {10878, 824}, - ["lesdot"] = 10879, - ["gesdot"] = 10880, - ["lesdoto"] = 10881, - ["gesdoto"] = 10882, - ["lesdotor"] = 10883, - ["gesdotol"] = 10884, - ["lap"] = 10885, - ["lessapprox"] = 10885, - ["gap"] = 10886, - ["gtrapprox"] = 10886, - ["lne"] = 10887, - ["lneq"] = 10887, - ["gne"] = 10888, - ["gneq"] = 10888, - ["lnap"] = 10889, - ["lnapprox"] = 10889, - ["gnap"] = 10890, - ["gnapprox"] = 10890, - ["lEg"] = 10891, - ["lesseqqgtr"] = 10891, - ["gEl"] = 10892, - ["gtreqqless"] = 10892, - ["lsime"] = 10893, - ["gsime"] = 10894, - ["lsimg"] = 10895, - ["gsiml"] = 10896, - ["lgE"] = 10897, - ["glE"] = 10898, - ["lesges"] = 10899, - ["gesles"] = 10900, - ["els"] = 10901, - ["eqslantless"] = 10901, - ["egs"] = 10902, - ["eqslantgtr"] = 10902, - ["elsdot"] = 10903, - ["egsdot"] = 10904, - ["el"] = 10905, - ["eg"] = 10906, - ["siml"] = 10909, - ["simg"] = 10910, - ["simlE"] = 10911, - ["simgE"] = 10912, - ["LessLess"] = 10913, - ["NotNestedLessLess"] = {10913, 824}, - ["GreaterGreater"] = 10914, - ["NotNestedGreaterGreater"] = {10914, 824}, - ["glj"] = 10916, - ["gla"] = 10917, - ["ltcc"] = 10918, - ["gtcc"] = 10919, - ["lescc"] = 10920, - ["gescc"] = 10921, - ["smt"] = 10922, - ["lat"] = 10923, - ["smte"] = 10924, - ["smtes"] = {10924, 65024}, - ["late"] = 10925, - ["lates"] = {10925, 65024}, - ["bumpE"] = 10926, - ["NotPrecedesEqual"] = {10927, 824}, - ["PrecedesEqual"] = 10927, - ["npre"] = {10927, 824}, - ["npreceq"] = {10927, 824}, - ["pre"] = 10927, - ["preceq"] = 10927, - ["NotSucceedsEqual"] = {10928, 824}, - ["SucceedsEqual"] = 10928, - ["nsce"] = {10928, 824}, - ["nsucceq"] = {10928, 824}, - ["sce"] = 10928, - ["succeq"] = 10928, - ["prE"] = 10931, - ["scE"] = 10932, - ["precneqq"] = 10933, - ["prnE"] = 10933, - ["scnE"] = 10934, - ["succneqq"] = 10934, - ["prap"] = 10935, - ["precapprox"] = 10935, - ["scap"] = 10936, - ["succapprox"] = 10936, - ["precnapprox"] = 10937, - ["prnap"] = 10937, - ["scnap"] = 10938, - ["succnapprox"] = 10938, - ["Pr"] = 10939, - ["Sc"] = 10940, - ["subdot"] = 10941, - ["supdot"] = 10942, - ["subplus"] = 10943, - ["supplus"] = 10944, - ["submult"] = 10945, - ["supmult"] = 10946, - ["subedot"] = 10947, - ["supedot"] = 10948, - ["nsubE"] = {10949, 824}, - ["nsubseteqq"] = {10949, 824}, - ["subE"] = 10949, - ["subseteqq"] = 10949, - ["nsupE"] = {10950, 824}, - ["nsupseteqq"] = {10950, 824}, - ["supE"] = 10950, - ["supseteqq"] = 10950, - ["subsim"] = 10951, - ["supsim"] = 10952, - ["subnE"] = 10955, - ["subsetneqq"] = 10955, - ["varsubsetneqq"] = {10955, 65024}, - ["vsubnE"] = {10955, 65024}, - ["supnE"] = 10956, - ["supsetneqq"] = 10956, - ["varsupsetneqq"] = {10956, 65024}, - ["vsupnE"] = {10956, 65024}, - ["csub"] = 10959, - ["csup"] = 10960, - ["csube"] = 10961, - ["csupe"] = 10962, - ["subsup"] = 10963, - ["supsub"] = 10964, - ["subsub"] = 10965, - ["supsup"] = 10966, - ["suphsub"] = 10967, - ["supdsub"] = 10968, - ["forkv"] = 10969, - ["topfork"] = 10970, - ["mlcp"] = 10971, - ["Dashv"] = 10980, - ["DoubleLeftTee"] = 10980, - ["Vdashl"] = 10982, - ["Barv"] = 10983, - ["vBar"] = 10984, - ["vBarv"] = 10985, - ["Vbar"] = 10987, - ["Not"] = 10988, - ["bNot"] = 10989, - ["rnmid"] = 10990, - ["cirmid"] = 10991, - ["midcir"] = 10992, - ["topcir"] = 10993, - ["nhpar"] = 10994, - ["parsim"] = 10995, - ["nparsl"] = {11005, 8421}, - ["parsl"] = 11005, - ["fflig"] = 64256, - ["filig"] = 64257, - ["fllig"] = 64258, - ["ffilig"] = 64259, - ["ffllig"] = 64260, - ["Ascr"] = 119964, - ["Cscr"] = 119966, - ["Dscr"] = 119967, - ["Gscr"] = 119970, - ["Jscr"] = 119973, - ["Kscr"] = 119974, - ["Nscr"] = 119977, - ["Oscr"] = 119978, - ["Pscr"] = 119979, - ["Qscr"] = 119980, - ["Sscr"] = 119982, - ["Tscr"] = 119983, - ["Uscr"] = 119984, - ["Vscr"] = 119985, - ["Wscr"] = 119986, - ["Xscr"] = 119987, - ["Yscr"] = 119988, - ["Zscr"] = 119989, - ["ascr"] = 119990, - ["bscr"] = 119991, - ["cscr"] = 119992, - ["dscr"] = 119993, - ["fscr"] = 119995, - ["hscr"] = 119997, - ["iscr"] = 119998, - ["jscr"] = 119999, - ["kscr"] = 120000, - ["lscr"] = 120001, - ["mscr"] = 120002, - ["nscr"] = 120003, - ["pscr"] = 120005, - ["qscr"] = 120006, - ["rscr"] = 120007, - ["sscr"] = 120008, - ["tscr"] = 120009, - ["uscr"] = 120010, - ["vscr"] = 120011, - ["wscr"] = 120012, - ["xscr"] = 120013, - ["yscr"] = 120014, - ["zscr"] = 120015, - ["Afr"] = 120068, - ["Bfr"] = 120069, - ["Dfr"] = 120071, - ["Efr"] = 120072, - ["Ffr"] = 120073, - ["Gfr"] = 120074, - ["Jfr"] = 120077, - ["Kfr"] = 120078, - ["Lfr"] = 120079, - ["Mfr"] = 120080, - ["Nfr"] = 120081, - ["Ofr"] = 120082, - ["Pfr"] = 120083, - ["Qfr"] = 120084, - ["Sfr"] = 120086, - ["Tfr"] = 120087, - ["Ufr"] = 120088, - ["Vfr"] = 120089, - ["Wfr"] = 120090, - ["Xfr"] = 120091, - ["Yfr"] = 120092, - ["afr"] = 120094, - ["bfr"] = 120095, - ["cfr"] = 120096, - ["dfr"] = 120097, - ["efr"] = 120098, - ["ffr"] = 120099, - ["gfr"] = 120100, - ["hfr"] = 120101, - ["ifr"] = 120102, - ["jfr"] = 120103, - ["kfr"] = 120104, - ["lfr"] = 120105, - ["mfr"] = 120106, - ["nfr"] = 120107, - ["ofr"] = 120108, - ["pfr"] = 120109, - ["qfr"] = 120110, - ["rfr"] = 120111, - ["sfr"] = 120112, - ["tfr"] = 120113, - ["ufr"] = 120114, - ["vfr"] = 120115, - ["wfr"] = 120116, - ["xfr"] = 120117, - ["yfr"] = 120118, - ["zfr"] = 120119, - ["Aopf"] = 120120, - ["Bopf"] = 120121, - ["Dopf"] = 120123, - ["Eopf"] = 120124, - ["Fopf"] = 120125, - ["Gopf"] = 120126, - ["Iopf"] = 120128, - ["Jopf"] = 120129, - ["Kopf"] = 120130, - ["Lopf"] = 120131, - ["Mopf"] = 120132, - ["Oopf"] = 120134, - ["Sopf"] = 120138, - ["Topf"] = 120139, - ["Uopf"] = 120140, - ["Vopf"] = 120141, - ["Wopf"] = 120142, - ["Xopf"] = 120143, - ["Yopf"] = 120144, - ["aopf"] = 120146, - ["bopf"] = 120147, - ["copf"] = 120148, - ["dopf"] = 120149, - ["eopf"] = 120150, - ["fopf"] = 120151, - ["gopf"] = 120152, - ["hopf"] = 120153, - ["iopf"] = 120154, - ["jopf"] = 120155, - ["kopf"] = 120156, - ["lopf"] = 120157, - ["mopf"] = 120158, - ["nopf"] = 120159, - ["oopf"] = 120160, - ["popf"] = 120161, - ["qopf"] = 120162, - ["ropf"] = 120163, - ["sopf"] = 120164, - ["topf"] = 120165, - ["uopf"] = 120166, - ["vopf"] = 120167, - ["wopf"] = 120168, - ["xopf"] = 120169, - ["yopf"] = 120170, - ["zopf"] = 120171, -} -function entities.dec_entity(s) - local n = tonumber(s) - if n == nil then - return "&#" .. s .. ";" -- fallback for unknown entities - end - return unicode.utf8.char(n) -end -function entities.hex_entity(s) - local n = tonumber("0x"..s) - if n == nil then - return "&#x" .. s .. ";" -- fallback for unknown entities - end - return unicode.utf8.char(n) -end -function entities.hex_entity_with_x_char(x, s) - local n = tonumber("0x"..s) - if n == nil then - return "&#" .. x .. s .. ";" -- fallback for unknown entities - end - return unicode.utf8.char(n) -end -function entities.char_entity(s) - local code_points = character_entities[s] - if code_points == nil then - return "&" .. s .. ";" - end - if type(code_points) ~= 'table' then - code_points = {code_points} - end - local char_table = {} - for _, code_point in ipairs(code_points) do - table.insert(char_table, unicode.utf8.char(code_point)) - end - return table.concat(char_table) -end -M.writer = {} -function M.writer.new(options) - local self = {} - self.options = options - self.flatten_inlines = false - local slice_specifiers = {} - for specifier in options.slice:gmatch("[^%s]+") do - table.insert(slice_specifiers, specifier) - end - - if #slice_specifiers == 2 then - self.slice_begin, self.slice_end = table.unpack(slice_specifiers) - local slice_begin_type = self.slice_begin:sub(1, 1) - if slice_begin_type ~= "^" and slice_begin_type ~= "$" then - self.slice_begin = "^" .. self.slice_begin - end - local slice_end_type = self.slice_end:sub(1, 1) - if slice_end_type ~= "^" and slice_end_type ~= "$" then - self.slice_end = "$" .. self.slice_end - end - elseif #slice_specifiers == 1 then - self.slice_begin = "^" .. slice_specifiers[1] - self.slice_end = "$" .. slice_specifiers[1] - end - - self.slice_begin_type = self.slice_begin:sub(1, 1) - self.slice_begin_identifier = self.slice_begin:sub(2) or "" - self.slice_end_type = self.slice_end:sub(1, 1) - self.slice_end_identifier = self.slice_end:sub(2) or "" - - if self.slice_begin == "^" and self.slice_end ~= "^" then - self.is_writing = true - else - self.is_writing = false - end - self.suffix = ".tex" - self.space = " " - self.nbsp = "\\markdownRendererNbsp{}" - function self.plain(s) - return s - end - function self.paragraph(s) - if not self.is_writing then return "" end - return s - end - function self.pack(name) - return [[\input{]] .. name .. [[}\relax]] - end - self.interblocksep_text = "\\markdownRendererInterblockSeparator\n{}" - function self.interblocksep() - if not self.is_writing then return "" end - return self.interblocksep_text - end - self.paragraphsep_text = "\\markdownRendererParagraphSeparator\n{}" - function self.paragraphsep() - if not self.is_writing then return "" end - return self.paragraphsep_text - end - self.undosep_text = "\\markdownRendererUndoSeparator\n{}" - function self.undosep() - if not self.is_writing then return "" end - return self.undosep_text - end - self.soft_line_break = function() - if self.flatten_inlines then return "\n" end - return "\\markdownRendererSoftLineBreak\n{}" - end - self.hard_line_break = function() - if self.flatten_inlines then return "\n" end - return "\\markdownRendererHardLineBreak\n{}" - end - self.ellipsis = "\\markdownRendererEllipsis{}" - function self.thematic_break() - if not self.is_writing then return "" end - return "\\markdownRendererThematicBreak{}" - end - self.escaped_uri_chars = { - ["{"] = "\\markdownRendererLeftBrace{}", - ["}"] = "\\markdownRendererRightBrace{}", - ["\\"] = "\\markdownRendererBackslash{}", - } - self.escaped_minimal_strings = { - ["^^"] = "\\markdownRendererCircumflex\\markdownRendererCircumflex ", - ["☒"] = "\\markdownRendererTickedBox{}", - ["⌛"] = "\\markdownRendererHalfTickedBox{}", - ["☐"] = "\\markdownRendererUntickedBox{}", - [entities.hex_entity('FFFD')] = "\\markdownRendererReplacementCharacter{}", - } - self.escaped_strings = util.table_copy(self.escaped_minimal_strings) - self.escaped_strings[entities.hex_entity('00A0')] = self.nbsp - self.escaped_chars = { - ["{"] = "\\markdownRendererLeftBrace{}", - ["}"] = "\\markdownRendererRightBrace{}", - ["%"] = "\\markdownRendererPercentSign{}", - ["\\"] = "\\markdownRendererBackslash{}", - ["#"] = "\\markdownRendererHash{}", - ["$"] = "\\markdownRendererDollarSign{}", - ["&"] = "\\markdownRendererAmpersand{}", - ["_"] = "\\markdownRendererUnderscore{}", - ["^"] = "\\markdownRendererCircumflex{}", - ["~"] = "\\markdownRendererTilde{}", - ["|"] = "\\markdownRendererPipe{}", - [entities.hex_entity('0000')] = "\\markdownRendererReplacementCharacter{}", - } - local function create_escaper(char_escapes, string_escapes) - local escape = util.escaper(char_escapes, string_escapes) - return function(s) - if self.flatten_inlines then return s end - return escape(s) - end - end - local escape_typographic_text = create_escaper( - self.escaped_chars, self.escaped_strings) - local escape_programmatic_text = create_escaper( - self.escaped_uri_chars, self.escaped_minimal_strings) - local escape_minimal = create_escaper( - {}, self.escaped_minimal_strings) - self.escape = escape_typographic_text - self.math = escape_minimal - if options.hybrid then - self.identifier = escape_minimal - self.string = escape_minimal - self.uri = escape_minimal - self.infostring = escape_minimal - else - self.identifier = escape_programmatic_text - self.string = escape_typographic_text - self.uri = escape_programmatic_text - self.infostring = escape_programmatic_text - end - function self.code(s, attributes) - if self.flatten_inlines then return s end - local buf = {} - if attributes ~= nil then - table.insert(buf, - "\\markdownRendererCodeSpanAttributeContextBegin\n") - table.insert(buf, self.attributes(attributes)) - end - table.insert(buf, - {"\\markdownRendererCodeSpan{", self.escape(s), "}"}) - if attributes ~= nil then - table.insert(buf, - "\\markdownRendererCodeSpanAttributeContextEnd{}") - end - return buf - end - function self.link(lab, src, tit, attributes) - if self.flatten_inlines then return lab end - local buf = {} - if attributes ~= nil then - table.insert(buf, - "\\markdownRendererLinkAttributeContextBegin\n") - table.insert(buf, self.attributes(attributes)) - end - table.insert(buf, {"\\markdownRendererLink{",lab,"}", - "{",self.escape(src),"}", - "{",self.uri(src),"}", - "{",self.string(tit or ""),"}"}) - if attributes ~= nil then - table.insert(buf, - "\\markdownRendererLinkAttributeContextEnd{}") - end - return buf - end - function self.image(lab, src, tit, attributes) - if self.flatten_inlines then return lab end - local buf = {} - if attributes ~= nil then - table.insert(buf, - "\\markdownRendererImageAttributeContextBegin\n") - table.insert(buf, self.attributes(attributes)) - end - table.insert(buf, {"\\markdownRendererImage{",lab,"}", - "{",self.string(src),"}", - "{",self.uri(src),"}", - "{",self.string(tit or ""),"}"}) - if attributes ~= nil then - table.insert(buf, - "\\markdownRendererImageAttributeContextEnd{}") - end - return buf - end - function self.bulletlist(items,tight) - if not self.is_writing then return "" end - local buffer = {} - for _,item in ipairs(items) do - if item ~= "" then - buffer[#buffer + 1] = self.bulletitem(item) - end - end - local contents = util.intersperse(buffer,"\n") - if tight and options.tightLists then - return {"\\markdownRendererUlBeginTight\n",contents, - "\n\\markdownRendererUlEndTight "} - else - return {"\\markdownRendererUlBegin\n",contents, - "\n\\markdownRendererUlEnd "} - end - end - function self.bulletitem(s) - return {"\\markdownRendererUlItem ",s, - "\\markdownRendererUlItemEnd "} - end - function self.orderedlist(items,tight,startnum) - if not self.is_writing then return "" end - local buffer = {} - local num = startnum - for _,item in ipairs(items) do - if item ~= "" then - buffer[#buffer + 1] = self.ordereditem(item,num) - end - if num ~= nil and item ~= "" then - num = num + 1 - end - end - local contents = util.intersperse(buffer,"\n") - if tight and options.tightLists then - return {"\\markdownRendererOlBeginTight\n",contents, - "\n\\markdownRendererOlEndTight "} - else - return {"\\markdownRendererOlBegin\n",contents, - "\n\\markdownRendererOlEnd "} - end - end - function self.ordereditem(s,num) - if num ~= nil then - return {"\\markdownRendererOlItemWithNumber{",num,"}",s, - "\\markdownRendererOlItemEnd "} - else - return {"\\markdownRendererOlItem ",s, - "\\markdownRendererOlItemEnd "} - end - end - function self.inline_html_comment(contents) - if self.flatten_inlines then return contents end - return {"\\markdownRendererInlineHtmlComment{",contents,"}"} - end - function self.inline_html_tag(contents) - if self.flatten_inlines then return contents end - return {"\\markdownRendererInlineHtmlTag{",self.string(contents),"}"} - end - function self.block_html_element(s) - if not self.is_writing then return "" end - local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") - return {"\\markdownRendererInputBlockHtmlElement{",name,"}"} - end - function self.emphasis(s) - if self.flatten_inlines then return s end - return {"\\markdownRendererEmphasis{",s,"}"} - end - function self.tickbox(f) - if f == 1.0 then - return "☒ " - elseif f == 0.0 then - return "☐ " - else - return "⌛ " - end - end - function self.strong(s) - if self.flatten_inlines then return s end - return {"\\markdownRendererStrongEmphasis{",s,"}"} - end - function self.blockquote(s) - if not self.is_writing then return "" end - return {"\\markdownRendererBlockQuoteBegin\n",s, - "\\markdownRendererBlockQuoteEnd "} - end - function self.verbatim(s) - if not self.is_writing then return "" end - s = s:gsub("\n$", "") - local name = util.cache_verbatim(options.cacheDir, s) - return {"\\markdownRendererInputVerbatim{",name,"}"} - end - function self.document(d) - local buf = {"\\markdownRendererDocumentBegin\n", d} - - -- pop all attributes - table.insert(buf, self.pop_attributes()) - - table.insert(buf, "\\markdownRendererDocumentEnd") - - return buf - end - local seen_identifiers = {} - local key_value_regex = "([^= ]+)%s*=%s*(.*)" - local function normalize_attributes(attributes, auto_identifiers) - -- normalize attributes - local normalized_attributes = {} - local has_explicit_identifiers = false - local key, value - for _, attribute in ipairs(attributes or {}) do - if attribute:sub(1, 1) == "#" then - table.insert(normalized_attributes, attribute) - has_explicit_identifiers = true - seen_identifiers[attribute:sub(2)] = true - elseif attribute:sub(1, 1) == "." then - table.insert(normalized_attributes, attribute) - else - key, value = attribute:match(key_value_regex) - if key:lower() == "id" then - table.insert(normalized_attributes, "#" .. value) - elseif key:lower() == "class" then - local classes = {} - for class in value:gmatch("%S+") do - table.insert(classes, class) - end - table.sort(classes) - for _, class in ipairs(classes) do - table.insert(normalized_attributes, "." .. class) - end - else - table.insert(normalized_attributes, attribute) - end - end - end - - -- if no explicit identifiers exist, add auto identifiers - if not has_explicit_identifiers and auto_identifiers ~= nil then - local seen_auto_identifiers = {} - for _, auto_identifier in ipairs(auto_identifiers) do - if seen_auto_identifiers[auto_identifier] == nil then - seen_auto_identifiers[auto_identifier] = true - if seen_identifiers[auto_identifier] == nil then - seen_identifiers[auto_identifier] = true - table.insert(normalized_attributes, - "#" .. auto_identifier) - else - local auto_identifier_number = 1 - while true do - local numbered_auto_identifier = auto_identifier .. "-" - .. auto_identifier_number - if seen_identifiers[numbered_auto_identifier] == nil then - seen_identifiers[numbered_auto_identifier] = true - table.insert(normalized_attributes, - "#" .. numbered_auto_identifier) - break - end - auto_identifier_number = auto_identifier_number + 1 - end - end - end - end - end - - -- sort and deduplicate normalized attributes - table.sort(normalized_attributes) - local seen_normalized_attributes = {} - local deduplicated_normalized_attributes = {} - for _, attribute in ipairs(normalized_attributes) do - if seen_normalized_attributes[attribute] == nil then - seen_normalized_attributes[attribute] = true - table.insert(deduplicated_normalized_attributes, attribute) - end - end - - return deduplicated_normalized_attributes - end - - function self.attributes(attributes, should_normalize_attributes) - local normalized_attributes - if should_normalize_attributes == false then - normalized_attributes = attributes - else - normalized_attributes = normalize_attributes(attributes) - end - - local buf = {} - local key, value - for _, attribute in ipairs(normalized_attributes) do - if attribute:sub(1, 1) == "#" then - table.insert(buf, {"\\markdownRendererAttributeIdentifier{", - attribute:sub(2), "}"}) - elseif attribute:sub(1, 1) == "." then - table.insert(buf, {"\\markdownRendererAttributeClassName{", - attribute:sub(2), "}"}) - else - key, value = attribute:match(key_value_regex) - table.insert(buf, {"\\markdownRendererAttributeKeyValue{", - key, "}{", value, "}"}) - end - end - - return buf - end - self.active_attributes = {} - self.attribute_type_levels = {} - setmetatable(self.attribute_type_levels, - { __index = function() return 0 end }) - local function apply_attributes() - local buf = {} - for i = 1, #self.active_attributes do - local start_output = self.active_attributes[i][3] - if start_output ~= nil then - table.insert(buf, start_output) - end - end - return buf - end - - local function tear_down_attributes() - local buf = {} - for i = #self.active_attributes, 1, -1 do - local end_output = self.active_attributes[i][4] - if end_output ~= nil then - table.insert(buf, end_output) - end - end - return buf - end - function self.push_attributes(attribute_type, attributes, - start_output, end_output) - local attribute_type_level = self.attribute_type_levels[attribute_type] - self.attribute_type_levels[attribute_type] = attribute_type_level + 1 - - -- index attributes in a hash table for easy lookup - attributes = attributes or {} - for i = 1, #attributes do - attributes[attributes[i]] = true - end - - local buf = {} - -- handle slicing - if attributes["#" .. self.slice_end_identifier] ~= nil and - self.slice_end_type == "^" then - if self.is_writing then - table.insert(buf, self.undosep()) - table.insert(buf, tear_down_attributes()) - end - self.is_writing = false - end - if attributes["#" .. self.slice_begin_identifier] ~= nil and - self.slice_begin_type == "^" then - table.insert(buf, apply_attributes()) - self.is_writing = true - end - if self.is_writing and start_output ~= nil then - table.insert(buf, start_output) - end - table.insert(self.active_attributes, - {attribute_type, attributes, - start_output, end_output}) - return buf - end - - function self.pop_attributes(attribute_type) - local buf = {} - -- pop attributes until we find attributes of correct type - -- or until no attributes remain - local current_attribute_type = false - while current_attribute_type ~= attribute_type and - #self.active_attributes > 0 do - local attributes, _, end_output - current_attribute_type, attributes, _, end_output = table.unpack( - self.active_attributes[#self.active_attributes]) - local attribute_type_level = self.attribute_type_levels[current_attribute_type] - self.attribute_type_levels[current_attribute_type] = attribute_type_level - 1 - if self.is_writing and end_output ~= nil then - table.insert(buf, end_output) - end - table.remove(self.active_attributes, #self.active_attributes) - -- handle slicing - if attributes["#" .. self.slice_end_identifier] ~= nil - and self.slice_end_type == "$" then - if self.is_writing then - table.insert(buf, self.undosep()) - table.insert(buf, tear_down_attributes()) - end - self.is_writing = false - end - if attributes["#" .. self.slice_begin_identifier] ~= nil and - self.slice_begin_type == "$" then - self.is_writing = true - table.insert(buf, apply_attributes()) - end - end - return buf - end - local function create_auto_identifier(s) - local buffer = {} - local prev_space = false - local letter_found = false - - for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do - local char = utf8.char(code) - - -- Remove everything up to the first letter. - if not letter_found then - local is_letter = unicode.utf8.match(char, "%a") - if is_letter then - letter_found = true - else - goto continue - end - end - - -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods. - if not unicode.utf8.match(char, "[%w_%-%.%s]") then - goto continue - end - - -- Replace all spaces and newlines with hyphens. - if unicode.utf8.match(char, "[%s\n]") then - char = "-" - if prev_space then - goto continue - else - prev_space = true - end - else - -- Convert all alphabetic characters to lowercase. - char = unicode.utf8.lower(char) - prev_space = false - end - - table.insert(buffer, char) - - ::continue:: - end - - if prev_space then - table.remove(buffer) - end - - local identifier = #buffer == 0 and "section" or table.concat(buffer, "") - return identifier - end - local function create_gfm_auto_identifier(s) - local buffer = {} - local prev_space = false - local letter_found = false - - for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do - local char = utf8.char(code) - - -- Remove everything up to the first non-space. - if not letter_found then - local is_letter = unicode.utf8.match(char, "%S") - if is_letter then - letter_found = true - else - goto continue - end - end - - -- Remove all non-alphanumeric characters, except underscores and hyphens. - if not unicode.utf8.match(char, "[%w_%-%s]") then - prev_space = false - goto continue - end - - -- Replace all spaces and newlines with hyphens. - if unicode.utf8.match(char, "[%s\n]") then - char = "-" - if prev_space then - goto continue - else - prev_space = true - end - else - -- Convert all alphabetic characters to lowercase. - char = unicode.utf8.lower(char) - prev_space = false - end - - table.insert(buffer, char) - - ::continue:: - end - - if prev_space then - table.remove(buffer) - end - - local identifier = #buffer == 0 and "section" or table.concat(buffer, "") - return identifier - end - self.secbegin_text = "\\markdownRendererSectionBegin\n" - self.secend_text = "\n\\markdownRendererSectionEnd " - function self.heading(s, level, attributes) - local buf = {} - local flat_text, inlines = table.unpack(s) - - -- push empty attributes for implied sections - while self.attribute_type_levels["heading"] < level - 1 do - table.insert(buf, - self.push_attributes("heading", - nil, - self.secbegin_text, - self.secend_text)) - end - - -- pop attributes for sections that have ended - while self.attribute_type_levels["heading"] >= level do - table.insert(buf, self.pop_attributes("heading")) - end - - -- construct attributes for the new section - local auto_identifiers = {} - if self.options.autoIdentifiers then - table.insert(auto_identifiers, create_auto_identifier(flat_text)) - end - if self.options.gfmAutoIdentifiers then - table.insert(auto_identifiers, create_gfm_auto_identifier(flat_text)) - end - local normalized_attributes = normalize_attributes(attributes, auto_identifiers) - - -- push attributes for the new section - local start_output = {} - local end_output = {} - table.insert(start_output, self.secbegin_text) - table.insert(end_output, self.secend_text) - - table.insert(buf, self.push_attributes("heading", - normalized_attributes, - start_output, - end_output)) - assert(self.attribute_type_levels["heading"] == level) - - -- render the heading and its attributes - if self.is_writing and #normalized_attributes > 0 then - table.insert(buf, "\\markdownRendererHeaderAttributeContextBegin\n") - table.insert(buf, self.attributes(normalized_attributes, false)) - end - - local cmd - level = level + options.shiftHeadings - if level <= 1 then - cmd = "\\markdownRendererHeadingOne" - elseif level == 2 then - cmd = "\\markdownRendererHeadingTwo" - elseif level == 3 then - cmd = "\\markdownRendererHeadingThree" - elseif level == 4 then - cmd = "\\markdownRendererHeadingFour" - elseif level == 5 then - cmd = "\\markdownRendererHeadingFive" - elseif level >= 6 then - cmd = "\\markdownRendererHeadingSix" - else - cmd = "" - end - if self.is_writing then - table.insert(buf, {cmd, "{", inlines, "}"}) - end - - if self.is_writing and #normalized_attributes > 0 then - table.insert(buf, "\\markdownRendererHeaderAttributeContextEnd{}") - end - - return buf - end - function self.get_state() - return { - is_writing=self.is_writing, - flatten_inlines=self.flatten_inlines, - active_attributes={table.unpack(self.active_attributes)}, - } - end - function self.set_state(s) - local previous_state = self.get_state() - for key, value in pairs(s) do - self[key] = value - end - return previous_state - end - function self.defer_call(f) - local previous_state = self.get_state() - return function(...) - local state = self.set_state(previous_state) - local return_value = f(...) - self.set_state(state) - return return_value - end - end - - return self -end -local parsers = {} -parsers.percent = P("%") -parsers.at = P("@") -parsers.comma = P(",") -parsers.asterisk = P("*") -parsers.dash = P("-") -parsers.plus = P("+") -parsers.underscore = P("_") -parsers.period = P(".") -parsers.hash = P("#") -parsers.dollar = P("$") -parsers.ampersand = P("&") -parsers.backtick = P("`") -parsers.less = P("<") -parsers.more = P(">") -parsers.space = P(" ") -parsers.squote = P("'") -parsers.dquote = P('"') -parsers.lparent = P("(") -parsers.rparent = P(")") -parsers.lbracket = P("[") -parsers.rbracket = P("]") -parsers.lbrace = P("{") -parsers.rbrace = P("}") -parsers.circumflex = P("^") -parsers.slash = P("/") -parsers.equal = P("=") -parsers.colon = P(":") -parsers.semicolon = P(";") -parsers.exclamation = P("!") -parsers.pipe = P("|") -parsers.tilde = P("~") -parsers.backslash = P("\\") -parsers.tab = P("\t") -parsers.newline = P("\n") - -parsers.digit = R("09") -parsers.hexdigit = R("09","af","AF") -parsers.letter = R("AZ","az") -parsers.alphanumeric = R("AZ","az","09") -parsers.keyword = parsers.letter - * (parsers.alphanumeric + parsers.dash)^0 - -parsers.doubleasterisks = P("**") -parsers.doubleunderscores = P("__") -parsers.doubletildes = P("~~") -parsers.fourspaces = P(" ") - -parsers.any = P(1) -parsers.succeed = P(true) -parsers.fail = P(false) - -parsers.internal_punctuation = S(":;,.?") -parsers.ascii_punctuation = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") -parsers.punctuation = {} -(function() - local pathname = kpse.lookup("UnicodeData.txt") - local file = assert(io.open(pathname, "r"), - [[Could not open file "UnicodeData.txt"]]) - for line in file:lines() do - local codepoint, major_category = line:match("^(%x+);[^;]*;(%a)") - if major_category == "P" or major_category == "S" then - local code = unicode.utf8.char(tonumber(codepoint, 16)) - if parsers.punctuation[#code] == nil then - parsers.punctuation[#code] = parsers.fail - end - local code_parser = parsers.succeed - for i = 1, #code do - local byte = code:sub(i, i) - local byte_parser = S(byte) - code_parser = code_parser - * byte_parser - end - parsers.punctuation[#code] = parsers.punctuation[#code] - + code_parser - end - end - assert(file:close()) -end)() - -parsers.escapable = parsers.ascii_punctuation -parsers.anyescaped = parsers.backslash / "" * parsers.escapable - + parsers.any - -parsers.spacechar = S("\t ") -parsers.spacing = S(" \n\r\t") -parsers.nonspacechar = parsers.any - parsers.spacing -parsers.optionalspace = parsers.spacechar^0 - -parsers.normalchar = parsers.any - (V("SpecialChar") - + parsers.spacing) -parsers.eof = -parsers.any -parsers.nonindentspace = parsers.space^-3 * - parsers.spacechar -parsers.indent = parsers.space^-3 * parsers.tab - + parsers.fourspaces / "" -parsers.linechar = P(1 - parsers.newline) - -parsers.blankline = parsers.optionalspace - * parsers.newline / "\n" -parsers.blanklines = parsers.blankline^0 -parsers.skipblanklines = (parsers.optionalspace * parsers.newline)^0 -parsers.indentedline = parsers.indent /"" - * C(parsers.linechar^1 * parsers.newline^-1) -parsers.optionallyindentedline = parsers.indent^-1 /"" - * C(parsers.linechar^1 * parsers.newline^-1) -parsers.sp = parsers.spacing^0 -parsers.spnl = parsers.optionalspace - * (parsers.newline * parsers.optionalspace)^-1 -parsers.line = parsers.linechar^0 * parsers.newline -parsers.nonemptyline = parsers.line - parsers.blankline - -parsers.leader = parsers.space^-3 - -local function has_trail(indent_table) - return indent_table ~= nil and - indent_table.trail ~= nil and - next(indent_table.trail) ~= nil -end - -local function has_indents(indent_table) - return indent_table ~= nil and - indent_table.indents ~= nil and - next(indent_table.indents) ~= nil -end - -local function add_trail(indent_table, trail_info) - indent_table.trail = trail_info - return indent_table -end - -local function remove_trail(indent_table) - indent_table.trail = nil - return indent_table -end - -local function update_indent_table(indent_table, new_indent, add) - indent_table = remove_trail(indent_table) - - if not has_indents(indent_table) then - indent_table.indents = {} - end - - if add then - indent_table.indents[#indent_table.indents + 1] = new_indent - else - if indent_table.indents[#indent_table.indents].name == new_indent.name then - indent_table.indents[#indent_table.indents] = nil - end - end - - return indent_table -end - -local function remove_indent(name) - local function remove_indent_level(s, i, indent_table) -- luacheck: ignore s i - indent_table = update_indent_table(indent_table, {name=name}, false) - return true, indent_table - end - - return Cg(Cmt(Cb("indent_info"), remove_indent_level), "indent_info") -end - -local function process_starter_spacing(indent, spacing, minimum, left_strip_length) - left_strip_length = left_strip_length or 0 - - local count = 0 - local tab_value = 4 - (indent) % 4 - - local code_started, minimum_found = false, false - local code_start, minimum_remainder = "", "" - - local left_total_stripped = 0 - local full_remainder = "" - - if spacing ~= nil then - for i = 1, #spacing do - local character = spacing:sub(i, i) - - if character == "\t" then - count = count + tab_value - tab_value = 4 - elseif character == " " then - count = count + 1 - tab_value = 4 - (1 - tab_value) % 4 - end - - if (left_strip_length ~= 0) then - local possible_to_strip = math.min(count, left_strip_length) - count = count - possible_to_strip - left_strip_length = left_strip_length - possible_to_strip - left_total_stripped = left_total_stripped + possible_to_strip - else - full_remainder = full_remainder .. character - end - - if (minimum_found) then - minimum_remainder = minimum_remainder .. character - elseif (count >= minimum) then - minimum_found = true - minimum_remainder = minimum_remainder .. string.rep(" ", count - minimum) - end - - if (code_started) then - code_start = code_start .. character - elseif (count >= minimum + 4) then - code_started = true - code_start = code_start .. string.rep(" ", count - (minimum + 4)) - end - end - end - - local remainder - if (code_started) then - remainder = code_start - else - remainder = string.rep(" ", count - minimum) - end - - local is_minimum = count >= minimum - return { - is_code = code_started, - remainder = remainder, - left_total_stripped = left_total_stripped, - is_minimum = is_minimum, - minimum_remainder = minimum_remainder, - total_length = count, - full_remainder = full_remainder - } -end - -local function count_indent_tab_level(indent_table) - local count = 0 - if not has_indents(indent_table) then - return count - end - - for i=1, #indent_table.indents do - count = count + indent_table.indents[i].length - end - return count -end - -local function total_delimiter_length(delimiter) - local count = 0 - if type(delimiter) == "string" then return #delimiter end - for _, value in pairs(delimiter) do - count = count + total_delimiter_length(value) - end - return count -end - -local function process_starter_indent(_, _, indent_table, starter, is_blank, indent_type, breakable) - local last_trail = starter[1] - local delimiter = starter[2] - local raw_new_trail = starter[3] - - if indent_type == "bq" and not breakable then - indent_table.ignore_blockquote_blank = true - end - - if has_trail(indent_table) then - local trail = indent_table.trail - if trail.is_code then - return false - end - last_trail = trail.remainder - else - local sp = process_starter_spacing(0, last_trail, 0, 0) - - if sp.is_code then - return false - end - last_trail = sp.remainder - end - - local preceding_indentation = count_indent_tab_level(indent_table) % 4 - local last_trail_length = #last_trail - local delimiter_length = total_delimiter_length(delimiter) - - local total_indent_level = preceding_indentation + last_trail_length + delimiter_length - - local sp = {} - if not is_blank then - sp = process_starter_spacing(total_indent_level, raw_new_trail, 0, 1) - end - - local del_trail_length = sp.left_total_stripped - if is_blank then - del_trail_length = 1 - elseif not sp.is_code then - del_trail_length = del_trail_length + #sp.remainder - end - - local indent_length = last_trail_length + delimiter_length + del_trail_length - local new_indent_info = {name=indent_type, length=indent_length} - - indent_table = update_indent_table(indent_table, new_indent_info, true) - indent_table = add_trail(indent_table, {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length, - full_remainder=sp.full_remainder}) - - return true, indent_table -end - -local function decode_pattern(name) - local delimeter = parsers.succeed - if name == "bq" then - delimeter = parsers.more - end - - return C(parsers.optionalspace) * C(delimeter) * C(parsers.optionalspace) * Cp() -end - -local function left_blank_starter(indent_table) - local blank_starter_index - - if not has_indents(indent_table) then - return - end - - for i = #indent_table.indents,1,-1 do - local value = indent_table.indents[i] - if value.name == "li" then - blank_starter_index = i - else - break - end - end - - return blank_starter_index -end - -local function traverse_indent(s, i, indent_table, is_optional, is_blank, current_line_indents) - local new_index = i - - local preceding_indentation = 0 - local current_trail = {} - - local blank_starter = left_blank_starter(indent_table) - - if current_line_indents == nil then - current_line_indents = {} - end - - for index = 1,#indent_table.indents do - local value = indent_table.indents[index] - local pattern = decode_pattern(value.name) - - -- match decoded pattern - local new_indent_info = lpeg.match(Ct(pattern), s, new_index) - if new_indent_info == nil then - local blankline_end = lpeg.match(Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index) - if is_optional or not indent_table.ignore_blockquote_blank or not blankline_end then - return is_optional, new_index, current_trail, current_line_indents - end - - return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank, current_line_indents) - end - - local raw_last_trail = new_indent_info[1] - local delimiter = new_indent_info[2] - local raw_new_trail = new_indent_info[3] - local next_index = new_indent_info[4] - - local space_only = delimiter == "" - - -- check previous trail - if not space_only and next(current_trail) == nil then - local sp = process_starter_spacing(0, raw_last_trail, 0, 0) - current_trail = {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length, - full_remainder=sp.full_remainder} - end - - if next(current_trail) ~= nil then - if not space_only and current_trail.is_code then - return is_optional, new_index, current_trail, current_line_indents - end - if current_trail.internal_remainder ~= nil then - raw_last_trail = current_trail.internal_remainder - end - end - - local raw_last_trail_length = 0 - local delimiter_length = 0 - - if not space_only then - delimiter_length = #delimiter - raw_last_trail_length = #raw_last_trail - end - - local total_indent_level = preceding_indentation + raw_last_trail_length + delimiter_length - - local spacing_to_process - local minimum = 0 - local left_strip_length = 0 - - if not space_only then - spacing_to_process = raw_new_trail - left_strip_length = 1 - else - spacing_to_process = raw_last_trail - minimum = value.length - end - - local sp = process_starter_spacing(total_indent_level, spacing_to_process, minimum, left_strip_length) - - if space_only and not sp.is_minimum then - return is_optional or (is_blank and blank_starter <= index), new_index, current_trail, current_line_indents - end - - local indent_length = raw_last_trail_length + delimiter_length + sp.left_total_stripped - - -- update info for the next pattern - if not space_only then - preceding_indentation = preceding_indentation + indent_length - else - preceding_indentation = preceding_indentation + value.length - end - - current_trail = {is_code=sp.is_code, remainder=sp.remainder, internal_remainder=sp.minimum_remainder, - total_length=sp.total_length, full_remainder=sp.full_remainder} - - current_line_indents[#current_line_indents + 1] = new_indent_info - new_index = next_index - end - - return true, new_index, current_trail, current_line_indents -end - -local function check_trail(expect_code, is_code) - return (expect_code and is_code) or (not expect_code and not is_code) -end - -local function check_trail_joined(s, i, indent_table, spacing, expect_code, omit_remainder) -- luacheck: ignore s i - local is_code - local remainder - - if has_trail(indent_table) then - local trail = indent_table.trail - is_code = trail.is_code - if is_code then - remainder = trail.remainder - else - remainder = trail.full_remainder - end - else - local sp = process_starter_spacing(0, spacing, 0, 0) - is_code = sp.is_code - if is_code then - remainder = sp.remainder - else - remainder = sp.full_remainder - end - end - - local result = check_trail(expect_code, is_code) - if omit_remainder then - return result - end - return result, remainder -end - -local function check_trail_length(s, i, indent_table, spacing, min, max) -- luacheck: ignore s i - local trail - - if has_trail(indent_table) then - trail = indent_table.trail - else - trail = process_starter_spacing(0, spacing, 0, 0) - end - - local total_length = trail.total_length - if total_length == nil then - return false - end - - return min <= total_length and total_length <= max -end - -local function check_continuation_indentation(s, i, indent_table, is_optional, is_blank) - if not has_indents(indent_table) then - return true - end - - local passes, new_index, current_trail, current_line_indents = - traverse_indent(s, i, indent_table, is_optional, is_blank) - - if passes then - indent_table.current_line_indents = current_line_indents - indent_table = add_trail(indent_table, current_trail) - return new_index, indent_table - end - return false -end - -local function get_last_indent_name(indent_table) - if has_indents(indent_table) then - return indent_table.indents[#indent_table.indents].name - end -end - -local function remove_remainder_if_blank(indent_table, remainder) - if get_last_indent_name(indent_table) == "li" then - return "" - end - return remainder -end - -local function check_trail_type(s, i, trail, spacing, trail_type) -- luacheck: ignore s i - if trail == nil then - trail = process_starter_spacing(0, spacing, 0, 0) - end - - if trail_type == "non-code" then - return check_trail(false, trail.is_code) - end - if trail_type == "code" then - return check_trail(true, trail.is_code) - end - if trail_type == "full-code" then - if (trail.is_code) then - return i, trail.remainder - end - return i, "" - end - if trail_type == "full-any" then - return i, trail.internal_remainder - end -end - -local function trail_freezing(s, i, indent_table, is_freezing) -- luacheck: ignore s i - if is_freezing then - if indent_table.is_trail_frozen then - indent_table.trail = indent_table.frozen_trail - else - indent_table.frozen_trail = indent_table.trail - indent_table.is_trail_frozen = true - end - else - indent_table.frozen_trail = nil - indent_table.is_trail_frozen = false - end - return true, indent_table -end - -local function check_continuation_indentation_and_trail(s, i, indent_table, is_optional, is_blank, trail_type, - reset_rem, omit_remainder) - if not has_indents(indent_table) then - local spacing, new_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i) - local result, remainder = check_trail_type(s, i, indent_table.trail, spacing, trail_type) - if remainder == nil then - if result then - return new_index - end - return false - end - if result then - return new_index, remainder - end - return false - end - - local passes, new_index, current_trail = traverse_indent(s, i, indent_table, is_optional, is_blank) - - if passes then - local spacing - if current_trail == nil then - local newer_spacing, newer_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i) - current_trail = process_starter_spacing(0, newer_spacing, 0, 0) - new_index = newer_index - spacing = newer_spacing - else - spacing = current_trail.remainder - end - local result, remainder = check_trail_type(s, new_index, current_trail, spacing, trail_type) - if remainder == nil or omit_remainder then - if result then - return new_index - end - return false - end - - if is_blank and reset_rem then - remainder = remove_remainder_if_blank(indent_table, remainder) - end - if result then - return new_index, remainder - end - return false - end - return false -end - -parsers.check_trail = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(false), check_trail_joined) - -parsers.check_trail_no_rem = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(false) * Cc(true), check_trail_joined) - -parsers.check_code_trail = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(true), check_trail_joined) - -parsers.check_trail_length_range = function(min, max) - return Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(min) * Cc(max), check_trail_length) -end - -parsers.check_trail_length = function(n) - return parsers.check_trail_length_range(n, n) -end - -parsers.freeze_trail = Cg(Cmt(Cb("indent_info") * Cc(true), trail_freezing), "indent_info") - -parsers.unfreeze_trail = Cg(Cmt(Cb("indent_info") * Cc(false), trail_freezing), "indent_info") - -parsers.check_minimal_indent = Cmt(Cb("indent_info") * Cc(false), check_continuation_indentation) - -parsers.check_optional_indent = Cmt(Cb("indent_info") * Cc(true), check_continuation_indentation) - -parsers.check_minimal_blank_indent = Cmt(Cb("indent_info") * Cc(false) * Cc(true), check_continuation_indentation) - - -parsers.check_minimal_indent_and_trail = Cmt( Cb("indent_info") - * Cc(false) * Cc(false) * Cc("non-code") * Cc(true), - check_continuation_indentation_and_trail) - -parsers.check_minimal_indent_and_code_trail = Cmt( Cb("indent_info") - * Cc(false) * Cc(false) * Cc("code") * Cc(false), - check_continuation_indentation_and_trail) - -parsers.check_minimal_blank_indent_and_full_code_trail = Cmt( Cb("indent_info") - * Cc(false) * Cc(true) * Cc("full-code") * Cc(true), - check_continuation_indentation_and_trail) - -parsers.check_minimal_indent_and_any_trail = Cmt( Cb("indent_info") - * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false), - check_continuation_indentation_and_trail) - -parsers.check_minimal_blank_indent_and_any_trail = Cmt( Cb("indent_info") - * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false), - check_continuation_indentation_and_trail) - -parsers.check_minimal_blank_indent_and_any_trail_no_rem = Cmt( Cb("indent_info") - * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(true), - check_continuation_indentation_and_trail) - -parsers.check_optional_indent_and_any_trail = Cmt( Cb("indent_info") - * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false), - check_continuation_indentation_and_trail) - -parsers.check_optional_blank_indent_and_any_trail = Cmt( Cb("indent_info") - * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false), - check_continuation_indentation_and_trail) - - -parsers.spnlc_noexc = parsers.optionalspace - * (parsers.newline * parsers.check_minimal_indent_and_any_trail)^-1 - -parsers.spnlc = parsers.optionalspace - * (V("EndlineNoSub"))^-1 - -parsers.spnlc_sep = parsers.optionalspace * V("EndlineNoSub") - + parsers.spacechar^1 - -parsers.only_blank = parsers.spacechar^0 * (parsers.newline + parsers.eof) - -parsers.commented_line_letter = parsers.linechar - + parsers.newline - - parsers.backslash - - parsers.percent -parsers.commented_line = Cg(Cc(""), "backslashes") - * ((#(parsers.commented_line_letter - - parsers.newline) - * Cb("backslashes") - * Cs(parsers.commented_line_letter - - parsers.newline)^1 -- initial - * Cg(Cc(""), "backslashes")) - + #(parsers.backslash * parsers.backslash) - * Cg((parsers.backslash -- even backslash - * parsers.backslash)^1, "backslashes") - + (parsers.backslash - * (#parsers.percent - * Cb("backslashes") - / function(backslashes) - return string.rep("\\", #backslashes / 2) - end - * C(parsers.percent) - + #parsers.commented_line_letter - * Cb("backslashes") - * Cc("\\") - * C(parsers.commented_line_letter)) - * Cg(Cc(""), "backslashes")))^0 - * (#parsers.percent - * Cb("backslashes") - / function(backslashes) - return string.rep("\\", #backslashes / 2) - end - * ((parsers.percent -- comment - * parsers.line - * #parsers.blankline) -- blank line - / "\n" - + parsers.percent -- comment - * parsers.line - * parsers.optionalspace) -- leading tabs and spaces - + #(parsers.newline) - * Cb("backslashes") - * C(parsers.newline)) - -parsers.chunk = parsers.line * (parsers.optionallyindentedline - - parsers.blankline)^0 - -parsers.attribute_key_char = parsers.alphanumeric + S("-_:.") -parsers.attribute_raw_char = parsers.alphanumeric + S("-_") -parsers.attribute_key = (parsers.attribute_key_char - - parsers.dash - parsers.digit) - * parsers.attribute_key_char^0 -parsers.attribute_value = ( (parsers.dquote / "") - * (parsers.anyescaped - parsers.dquote)^0 - * (parsers.dquote / "")) - + ( (parsers.squote / "") - * (parsers.anyescaped - parsers.squote)^0 - * (parsers.squote / "")) - + ( parsers.anyescaped - parsers.dquote - parsers.rbrace - - parsers.space)^0 -parsers.attribute_identifier = parsers.attribute_key_char^1 -parsers.attribute_classname = parsers.letter - * parsers.attribute_key_char^0 -parsers.attribute_raw = parsers.attribute_raw_char^1 - -parsers.attribute = (parsers.dash * Cc(".unnumbered")) - + C( parsers.hash - * parsers.attribute_identifier) - + C( parsers.period - * parsers.attribute_classname) - + Cs( parsers.attribute_key - * parsers.optionalspace * parsers.equal * parsers.optionalspace - * parsers.attribute_value) -parsers.attributes = parsers.lbrace - * parsers.optionalspace - * parsers.attribute - * (parsers.spacechar^1 - * parsers.attribute)^0 - * parsers.optionalspace - * parsers.rbrace - -parsers.raw_attribute = parsers.lbrace - * parsers.optionalspace - * parsers.equal - * C(parsers.attribute_raw) - * parsers.optionalspace - * parsers.rbrace - --- block followed by 0 or more optionally --- indented blocks with first line indented. -parsers.indented_blocks = function(bl) - return Cs( bl - * (parsers.blankline^1 * parsers.indent * -parsers.blankline * bl)^0 - * (parsers.blankline^1 + parsers.eof) ) -end -local function repeat_between(pattern, min, max) - return -pattern^(max + 1) * pattern^min -end - -parsers.hexentity = parsers.ampersand * parsers.hash * C(S("Xx")) - * C(repeat_between(parsers.hexdigit, 1, 6)) * parsers.semicolon -parsers.decentity = parsers.ampersand * parsers.hash - * C(repeat_between(parsers.digit, 1, 7)) * parsers.semicolon -parsers.tagentity = parsers.ampersand * C(parsers.alphanumeric^1) - * parsers.semicolon - -parsers.html_entities = parsers.hexentity / entities.hex_entity_with_x_char - + parsers.decentity / entities.dec_entity - + parsers.tagentity / entities.char_entity -parsers.bullet = function(bullet_char, interrupting) - local allowed_end - if interrupting then - allowed_end = C(parsers.spacechar^1) * #parsers.linechar - else - allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof) - end - return parsers.check_trail - * Ct(C(bullet_char) * Cc("")) - * allowed_end -end - -local function tickbox(interior) - return parsers.optionalspace * parsers.lbracket - * interior * parsers.rbracket * parsers.spacechar^1 -end - -parsers.ticked_box = tickbox(S("xX")) * Cc(1.0) -parsers.halfticked_box = tickbox(S("./")) * Cc(0.5) -parsers.unticked_box = tickbox(parsers.spacechar^1) * Cc(0.0) - -parsers.openticks = Cg(parsers.backtick^1, "ticks") - -local function captures_equal_length(_,i,a,b) - return #a == #b and i -end - -parsers.closeticks = Cmt(C(parsers.backtick^1) - * Cb("ticks"), captures_equal_length) - -parsers.intickschar = (parsers.any - S("\n\r`")) - + V("NoSoftLineBreakEndline") - + (parsers.backtick^1 - parsers.closeticks) - -local function process_inticks(s) - s = s:gsub("\n", " ") - s = s:gsub("^ (.*) $", "%1") - return s -end - -parsers.inticks = parsers.openticks - * C(parsers.space^0) - * parsers.closeticks - + parsers.openticks - * Cs(Cs(parsers.intickschar^0) / process_inticks) - * parsers.closeticks - --- case-insensitive match (we assume s is lowercase). must be single byte encoding -parsers.keyword_exact = function(s) - local parser = P(0) - for i=1,#s do - local c = s:sub(i,i) - local m = c .. upper(c) - parser = parser * S(m) - end - return parser -end - -parsers.special_block_keyword = - parsers.keyword_exact("pre") + - parsers.keyword_exact("script") + - parsers.keyword_exact("style") + - parsers.keyword_exact("textarea") - -parsers.block_keyword = - parsers.keyword_exact("address") + - parsers.keyword_exact("article") + - parsers.keyword_exact("aside") + - parsers.keyword_exact("base") + - parsers.keyword_exact("basefont") + - parsers.keyword_exact("blockquote") + - parsers.keyword_exact("body") + - parsers.keyword_exact("caption") + - parsers.keyword_exact("center") + - parsers.keyword_exact("col") + - parsers.keyword_exact("colgroup") + - parsers.keyword_exact("dd") + - parsers.keyword_exact("details") + - parsers.keyword_exact("dialog") + - parsers.keyword_exact("dir") + - parsers.keyword_exact("div") + - parsers.keyword_exact("dl") + - parsers.keyword_exact("dt") + - parsers.keyword_exact("fieldset") + - parsers.keyword_exact("figcaption") + - parsers.keyword_exact("figure") + - parsers.keyword_exact("footer") + - parsers.keyword_exact("form") + - parsers.keyword_exact("frame") + - parsers.keyword_exact("frameset") + - parsers.keyword_exact("h1") + - parsers.keyword_exact("h2") + - parsers.keyword_exact("h3") + - parsers.keyword_exact("h4") + - parsers.keyword_exact("h5") + - parsers.keyword_exact("h6") + - parsers.keyword_exact("head") + - parsers.keyword_exact("header") + - parsers.keyword_exact("hr") + - parsers.keyword_exact("html") + - parsers.keyword_exact("iframe") + - parsers.keyword_exact("legend") + - parsers.keyword_exact("li") + - parsers.keyword_exact("link") + - parsers.keyword_exact("main") + - parsers.keyword_exact("menu") + - parsers.keyword_exact("menuitem") + - parsers.keyword_exact("nav") + - parsers.keyword_exact("noframes") + - parsers.keyword_exact("ol") + - parsers.keyword_exact("optgroup") + - parsers.keyword_exact("option") + - parsers.keyword_exact("p") + - parsers.keyword_exact("param") + - parsers.keyword_exact("section") + - parsers.keyword_exact("source") + - parsers.keyword_exact("summary") + - parsers.keyword_exact("table") + - parsers.keyword_exact("tbody") + - parsers.keyword_exact("td") + - parsers.keyword_exact("tfoot") + - parsers.keyword_exact("th") + - parsers.keyword_exact("thead") + - parsers.keyword_exact("title") + - parsers.keyword_exact("tr") + - parsers.keyword_exact("track") + - parsers.keyword_exact("ul") - --- end conditions -parsers.html_blankline_end_condition = parsers.linechar^0 - * ( parsers.newline - * (parsers.check_minimal_blank_indent_and_any_trail - * #parsers.blankline - + parsers.check_minimal_indent_and_any_trail) - * parsers.linechar^1)^0 - * (parsers.newline^-1 / "") - -local function remove_trailing_blank_lines(s) - return s:gsub("[\n\r]+%s*$", "") -end - -parsers.html_until_end = function(end_marker) - return Cs(Cs((parsers.newline - * (parsers.check_minimal_blank_indent_and_any_trail - * #parsers.blankline - + parsers.check_minimal_indent_and_any_trail) - + parsers.linechar - end_marker)^0 - * parsers.linechar^0 * parsers.newline^-1) - / remove_trailing_blank_lines) -end - --- attributes -parsers.html_attribute_spacing = parsers.optionalspace - * V("NoSoftLineBreakEndline") - * parsers.optionalspace - + parsers.spacechar^1 - -parsers.html_attribute_name = (parsers.letter + parsers.colon + parsers.underscore) - * (parsers.alphanumeric + parsers.colon + parsers.underscore - + parsers.period + parsers.dash)^0 - -parsers.html_attribute_value = parsers.squote - * (parsers.linechar - parsers.squote)^0 - * parsers.squote - + parsers.dquote - * (parsers.linechar - parsers.dquote)^0 - * parsers.dquote - + ( parsers.any - parsers.spacechar - parsers.newline - - parsers.dquote - parsers.squote - parsers.backtick - - parsers.equal - parsers.less - parsers.more)^1 - -parsers.html_inline_attribute_value = parsers.squote - * (V("NoSoftLineBreakEndline") - + parsers.any - - parsers.blankline^2 - - parsers.squote)^0 - * parsers.squote - + parsers.dquote - * (V("NoSoftLineBreakEndline") - + parsers.any - - parsers.blankline^2 - - parsers.dquote)^0 - * parsers.dquote - + (parsers.any - parsers.spacechar - parsers.newline - - parsers.dquote - parsers.squote - parsers.backtick - - parsers.equal - parsers.less - parsers.more)^1 - -parsers.html_attribute_value_specification = parsers.optionalspace - * parsers.equal - * parsers.optionalspace - * parsers.html_attribute_value - -parsers.html_spnl = parsers.optionalspace - * (V("NoSoftLineBreakEndline") * parsers.optionalspace)^-1 - -parsers.html_inline_attribute_value_specification = parsers.html_spnl - * parsers.equal - * parsers.html_spnl - * parsers.html_inline_attribute_value - -parsers.html_attribute = parsers.html_attribute_spacing - * parsers.html_attribute_name - * parsers.html_inline_attribute_value_specification^-1 - -parsers.html_non_newline_attribute = parsers.spacechar^1 - * parsers.html_attribute_name - * parsers.html_attribute_value_specification^-1 - -parsers.nested_breaking_blank = parsers.newline - * parsers.check_minimal_blank_indent - * parsers.blankline - -parsers.html_comment_start = P("<!--") - -parsers.html_comment_end = P("-->") - -parsers.html_comment = Cs( parsers.html_comment_start - * parsers.html_until_end(parsers.html_comment_end)) - -parsers.html_inline_comment = (parsers.html_comment_start / "") - * -P(">") * -P("->") - * Cs((V("NoSoftLineBreakEndline") + parsers.any - - parsers.nested_breaking_blank - parsers.html_comment_end)^0) - * (parsers.html_comment_end / "") - -parsers.html_cdatasection_start = P("<![CDATA[") - -parsers.html_cdatasection_end = P("]]>") - -parsers.html_cdatasection = Cs( parsers.html_cdatasection_start - * parsers.html_until_end(parsers.html_cdatasection_end)) - -parsers.html_inline_cdatasection = parsers.html_cdatasection_start - * Cs(V("NoSoftLineBreakEndline") + parsers.any - - parsers.nested_breaking_blank - parsers.html_cdatasection_end)^0 - * parsers.html_cdatasection_end - -parsers.html_declaration_start = P("<!") * parsers.letter - -parsers.html_declaration_end = P(">") - -parsers.html_declaration = Cs( parsers.html_declaration_start - * parsers.html_until_end(parsers.html_declaration_end)) - -parsers.html_inline_declaration = parsers.html_declaration_start - * Cs(V("NoSoftLineBreakEndline") + parsers.any - - parsers.nested_breaking_blank - parsers.html_declaration_end)^0 - * parsers.html_declaration_end - -parsers.html_instruction_start = P("<?") - -parsers.html_instruction_end = P("?>") - -parsers.html_instruction = Cs( parsers.html_instruction_start - * parsers.html_until_end(parsers.html_instruction_end)) - -parsers.html_inline_instruction = parsers.html_instruction_start - * Cs(V("NoSoftLineBreakEndline") + parsers.any - - parsers.nested_breaking_blank - parsers.html_instruction_end)^0 - * parsers.html_instruction_end - -parsers.html_blankline = parsers.newline - * parsers.optionalspace - * parsers.newline - -parsers.html_tag_start = parsers.less - -parsers.html_tag_closing_start = parsers.less - * parsers.slash - -parsers.html_tag_end = parsers.html_spnl - * parsers.more - -parsers.html_empty_tag_end = parsers.html_spnl - * parsers.slash - * parsers.more - --- opening tags -parsers.html_any_open_inline_tag = parsers.html_tag_start - * parsers.keyword - * parsers.html_attribute^0 - * parsers.html_tag_end - -parsers.html_any_open_tag = parsers.html_tag_start - * parsers.keyword - * parsers.html_non_newline_attribute^0 - * parsers.html_tag_end - -parsers.html_open_tag = parsers.html_tag_start - * parsers.block_keyword - * parsers.html_attribute^0 - * parsers.html_tag_end - -parsers.html_open_special_tag = parsers.html_tag_start - * parsers.special_block_keyword - * parsers.html_attribute^0 - * parsers.html_tag_end - --- incomplete tags -parsers.incomplete_tag_following = parsers.spacechar - + parsers.more - + parsers.slash * parsers.more - + #(parsers.newline + parsers.eof) - -parsers.incomplete_special_tag_following = parsers.spacechar - + parsers.more - + #(parsers.newline + parsers.eof) - -parsers.html_incomplete_open_tag = parsers.html_tag_start - * parsers.block_keyword - * parsers.incomplete_tag_following - -parsers.html_incomplete_open_special_tag = parsers.html_tag_start - * parsers.special_block_keyword - * parsers.incomplete_special_tag_following - -parsers.html_incomplete_close_tag = parsers.html_tag_closing_start - * parsers.block_keyword - * parsers.incomplete_tag_following - -parsers.html_incomplete_close_special_tag = parsers.html_tag_closing_start - * parsers.special_block_keyword - * parsers.incomplete_tag_following - --- closing tags -parsers.html_close_tag = parsers.html_tag_closing_start - * parsers.block_keyword - * parsers.html_tag_end - -parsers.html_any_close_tag = parsers.html_tag_closing_start - * parsers.keyword - * parsers.html_tag_end - -parsers.html_close_special_tag = parsers.html_tag_closing_start - * parsers.special_block_keyword - * parsers.html_tag_end - --- empty tags -parsers.html_any_empty_inline_tag = parsers.html_tag_start - * parsers.keyword - * parsers.html_attribute^0 - * parsers.html_empty_tag_end - -parsers.html_any_empty_tag = parsers.html_tag_start - * parsers.keyword - * parsers.html_non_newline_attribute^0 - * parsers.optionalspace - * parsers.slash - * parsers.more - -parsers.html_empty_tag = parsers.html_tag_start - * parsers.block_keyword - * parsers.html_attribute^0 - * parsers.html_empty_tag_end - -parsers.html_empty_special_tag = parsers.html_tag_start - * parsers.special_block_keyword - * parsers.html_attribute^0 - * parsers.html_empty_tag_end - -parsers.html_incomplete_blocks = parsers.html_incomplete_open_tag - + parsers.html_incomplete_open_special_tag - + parsers.html_incomplete_close_tag - --- parse special html blocks -parsers.html_blankline_ending_special_block_opening = (parsers.html_close_special_tag - + parsers.html_empty_special_tag) - * #(parsers.optionalspace - * (parsers.newline + parsers.eof)) - -parsers.html_blankline_ending_special_block = parsers.html_blankline_ending_special_block_opening - * parsers.html_blankline_end_condition - -parsers.html_special_block_opening = parsers.html_incomplete_open_special_tag - - parsers.html_empty_special_tag - -parsers.html_closing_special_block = parsers.html_special_block_opening - * parsers.html_until_end(parsers.html_close_special_tag) - -parsers.html_special_block = parsers.html_blankline_ending_special_block - + parsers.html_closing_special_block - --- parse html blocks -parsers.html_block_opening = parsers.html_incomplete_open_tag - + parsers.html_incomplete_close_tag - -parsers.html_block = parsers.html_block_opening - * parsers.html_blankline_end_condition - --- parse any html blocks -parsers.html_any_block_opening = (parsers.html_any_open_tag - + parsers.html_any_close_tag - + parsers.html_any_empty_tag) - * #(parsers.optionalspace * (parsers.newline + parsers.eof)) - -parsers.html_any_block = parsers.html_any_block_opening - * parsers.html_blankline_end_condition - -parsers.html_inline_comment_full = parsers.html_comment_start - * -P(">") * -P("->") - * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--") - - parsers.nested_breaking_blank - parsers.html_comment_end)^0) - * parsers.html_comment_end - -parsers.html_inline_tags = parsers.html_inline_comment_full - + parsers.html_any_empty_inline_tag - + parsers.html_inline_instruction - + parsers.html_inline_cdatasection - + parsers.html_inline_declaration - + parsers.html_any_open_inline_tag - + parsers.html_any_close_tag - -parsers.urlchar = parsers.anyescaped - - parsers.newline - - parsers.more - -parsers.auto_link_scheme_part = parsers.alphanumeric - + parsers.plus - + parsers.period - + parsers.dash - -parsers.auto_link_scheme = parsers.letter - * parsers.auto_link_scheme_part - * parsers.auto_link_scheme_part^-30 - -parsers.absolute_uri = parsers.auto_link_scheme * parsers.colon - * (parsers.any - parsers.spacing - parsers.less - parsers.more)^0 - -parsers.printable_characters = S(".!#$%&'*+/=?^_`{|}~-") - -parsers.email_address_local_part_char = parsers.alphanumeric - + parsers.printable_characters - -parsers.email_address_local_part = parsers.email_address_local_part_char^1 - -parsers.email_address_dns_label = parsers.alphanumeric - * (parsers.alphanumeric + parsers.dash)^-62 - * B(parsers.alphanumeric) - -parsers.email_address_domain = parsers.email_address_dns_label - * (parsers.period * parsers.email_address_dns_label)^0 - -parsers.email_address = parsers.email_address_local_part - * parsers.at - * parsers.email_address_domain - -parsers.auto_link_url = parsers.less - * C(parsers.absolute_uri) - * parsers.more - -parsers.auto_link_email = parsers.less - * C(parsers.email_address) - * parsers.more - -parsers.auto_link_relative_reference = parsers.less - * C(parsers.urlchar^1) - * parsers.more - -parsers.autolink = parsers.auto_link_url - + parsers.auto_link_email - --- content in balanced brackets, parentheses, or quotes: -parsers.bracketed = P{ parsers.lbracket - * (( parsers.backslash / "" * parsers.rbracket - + parsers.any - (parsers.lbracket - + parsers.rbracket - + parsers.blankline^2) - ) + V(1))^0 - * parsers.rbracket } - -parsers.inparens = P{ parsers.lparent - * ((parsers.anyescaped - (parsers.lparent - + parsers.rparent - + parsers.blankline^2) - ) + V(1))^0 - * parsers.rparent } - -parsers.squoted = P{ parsers.squote * parsers.alphanumeric - * ((parsers.anyescaped - (parsers.squote - + parsers.blankline^2) - ) + V(1))^0 - * parsers.squote } - -parsers.dquoted = P{ parsers.dquote * parsers.alphanumeric - * ((parsers.anyescaped - (parsers.dquote - + parsers.blankline^2) - ) + V(1))^0 - * parsers.dquote } - -parsers.link_text = parsers.lbracket - * Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + parsers.autolink - + V("InlineHtml") - + ( parsers.backslash * parsers.backslash) - + ( parsers.backslash * (parsers.lbracket + parsers.rbracket) - + V("NoSoftLineBreakSpace") - + V("NoSoftLineBreakEndline") - + (parsers.any - - (parsers.newline + parsers.lbracket + parsers.rbracket + parsers.blankline^2))))^0) - * parsers.rbracket - -parsers.link_label = parsers.lbracket - * -#(parsers.sp * parsers.rbracket) - * #((parsers.any - parsers.rbracket)^-999 * parsers.rbracket) - * Cs((parsers.alphanumeric^1 - + parsers.inticks - + parsers.autolink - + V("InlineHtml") - + ( parsers.backslash * parsers.backslash) - + ( parsers.backslash * (parsers.lbracket + parsers.rbracket) - + V("NoSoftLineBreakSpace") - + V("NoSoftLineBreakEndline") - + (parsers.any - - (parsers.newline + parsers.lbracket + parsers.rbracket + parsers.blankline^2))))^1) - * parsers.rbracket - -parsers.inparens_url = P{ parsers.lparent - * ((parsers.anyescaped - (parsers.lparent - + parsers.rparent - + parsers.spacing) - ) + V(1))^0 - * parsers.rparent } - --- url for markdown links, allowing nested brackets: -parsers.url = parsers.less * Cs((parsers.anyescaped - - parsers.newline - - parsers.less - - parsers.more)^0) - * parsers.more - + -parsers.less - * Cs((parsers.inparens_url + (parsers.anyescaped - - parsers.spacing - - parsers.lparent - - parsers.rparent))^1) - --- quoted text: -parsers.title_s = parsers.squote - * Cs((parsers.html_entities - + V("NoSoftLineBreakSpace") - + V("NoSoftLineBreakEndline") - + (parsers.anyescaped - parsers.newline - parsers.squote - parsers.blankline^2))^0) - * parsers.squote - -parsers.title_d = parsers.dquote - * Cs((parsers.html_entities - + V("NoSoftLineBreakSpace") - + V("NoSoftLineBreakEndline") - + (parsers.anyescaped - parsers.newline - parsers.dquote - parsers.blankline^2))^0) - * parsers.dquote - -parsers.title_p = parsers.lparent - * Cs((parsers.html_entities - + V("NoSoftLineBreakSpace") - + V("NoSoftLineBreakEndline") - + (parsers.anyescaped - parsers.newline - parsers.lparent - parsers.rparent - - parsers.blankline^2))^0) - * parsers.rparent - -parsers.title = parsers.title_d + parsers.title_s + parsers.title_p - -parsers.optionaltitle - = parsers.spnlc * parsers.title * parsers.spacechar^0 - + Cc("") - --- parse a reference definition: [foo]: /bar "title" -parsers.define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon - * parsers.spnlc * parsers.url - * ( parsers.spnlc_sep * parsers.title * parsers.only_blank - + Cc("") * parsers.only_blank) -parsers.Inline = V("Inline") - --- parse many p between starter and ender -parsers.between = function(p, starter, ender) - local ender2 = B(parsers.nonspacechar) * ender - return (starter * #parsers.nonspacechar * Ct(p * (p - ender2)^0) * ender2) -end - -parsers.lineof = function(c) - return (parsers.check_trail_no_rem * (P(c) * parsers.optionalspace)^3 - * (parsers.newline + parsers.eof)) -end - -parsers.thematic_break_lines = parsers.lineof(parsers.asterisk) - + parsers.lineof(parsers.dash) - + parsers.lineof(parsers.underscore) --- parse Atx heading start and return level -parsers.heading_start = #parsers.hash * C(parsers.hash^-6) - * -parsers.hash / length - --- parse setext header ending and return level -parsers.heading_level = parsers.nonindentspace * parsers.equal^1 * parsers.optionalspace * #parsers.newline * Cc(1) - + parsers.nonindentspace * parsers.dash^1 * parsers.optionalspace * #parsers.newline * Cc(2) - -local function strip_atx_end(s) - return s:gsub("%s+#*%s*\n$","") -end - -parsers.atx_heading = parsers.check_trail_no_rem - * Cg(parsers.heading_start, "level") - * (C( parsers.optionalspace - * parsers.hash^0 - * parsers.optionalspace - * parsers.newline) - + parsers.spacechar^1 - * C(parsers.line)) -M.reader = {} -function M.reader.new(writer, options) - local self = {} - self.writer = writer - self.options = options - self.parsers = {} - (function(parsers) - setmetatable(self.parsers, { - __index = function (_, key) - return parsers[key] - end - }) - end)(parsers) - local parsers = self.parsers - function self.normalize_tag(tag) - tag = util.rope_to_string(tag) - tag = tag:gsub("[ \n\r\t]+", " ") - tag = tag:gsub("^ ", ""):gsub(" $", "") - tag = uni_algos.case.casefold(tag, true, false) - return tag - end - local function iterlines(s, f) - local rope = lpeg.match(Ct((parsers.line / f)^1), s) - return util.rope_to_string(rope) - end - if options.preserveTabs then - self.expandtabs = function(s) return s end - else - self.expandtabs = function(s) - if s:find("\t") then - return iterlines(s, util.expand_tabs_in_line) - else - return s - end - end - end - self.parser_functions = {} - self.create_parser = function(name, grammar, toplevel) - self.parser_functions[name] = function(str) - if toplevel and options.stripIndent then - local min_prefix_length, min_prefix = nil, '' - str = iterlines(str, function(line) - if lpeg.match(parsers.nonemptyline, line) == nil then - return line - end - line = util.expand_tabs_in_line(line) - local prefix = lpeg.match(C(parsers.optionalspace), line) - local prefix_length = #prefix - local is_shorter = min_prefix_length == nil - is_shorter = is_shorter or prefix_length < min_prefix_length - if is_shorter then - min_prefix_length, min_prefix = prefix_length, prefix - end - return line - end) - str = str:gsub('^' .. min_prefix, '') - end - if toplevel and (options.texComments or options.hybrid) then - str = lpeg.match(Ct(parsers.commented_line^1), str) - str = util.rope_to_string(str) - end - local res = lpeg.match(grammar(), str) - if res == nil then - error(format("%s failed on:\n%s", name, str:sub(1,20))) - else - return res - end - end - end - - self.create_parser("parse_blocks", - function() - return parsers.blocks - end, true) - - self.create_parser("parse_blocks_nested", - function() - return parsers.blocks_nested - end, false) - - self.create_parser("parse_inlines", - function() - return parsers.inlines - end, false) - - self.create_parser("parse_inlines_no_inline_note", - function() - return parsers.inlines_no_inline_note - end, false) - - self.create_parser("parse_inlines_no_html", - function() - return parsers.inlines_no_html - end, false) - - self.create_parser("parse_inlines_nbsp", - function() - return parsers.inlines_nbsp - end, false) - self.create_parser("parse_inlines_no_link_or_emphasis", - function() - return parsers.inlines_no_link_or_emphasis - end, false) - parsers.minimally_indented_blankline = parsers.check_minimal_indent * (parsers.blankline / "") - - parsers.minimally_indented_block = parsers.check_minimal_indent * V("Block") - - parsers.minimally_indented_block_or_paragraph = parsers.check_minimal_indent * V("BlockOrParagraph") - - parsers.minimally_indented_paragraph = parsers.check_minimal_indent * V("Paragraph") - - parsers.minimally_indented_plain = parsers.check_minimal_indent * V("Plain") - - parsers.minimally_indented_par_or_plain = parsers.minimally_indented_paragraph - + parsers.minimally_indented_plain - - parsers.minimally_indented_par_or_plain_no_blank = parsers.minimally_indented_par_or_plain - - parsers.minimally_indented_blankline - - parsers.minimally_indented_ref = parsers.check_minimal_indent * V("Reference") - - parsers.minimally_indented_blank = parsers.check_minimal_indent * V("Blank") - - parsers.conditionally_indented_blankline = parsers.check_minimal_blank_indent * (parsers.blankline / "") - - parsers.minimally_indented_ref_or_block = parsers.minimally_indented_ref - + parsers.minimally_indented_block - - parsers.minimally_indented_blankline - - parsers.minimally_indented_ref_or_block_or_par = parsers.minimally_indented_ref - + parsers.minimally_indented_block_or_paragraph - - parsers.minimally_indented_blankline - - - parsers.separator_loop = function(separated_block, paragraph, block_separator, paragraph_separator) - return separated_block - + block_separator - * paragraph - * separated_block - + paragraph_separator - * paragraph - end - - parsers.create_loop_body_pair = function(separated_block, paragraph, block_separator, paragraph_separator) - return { - block = parsers.separator_loop(separated_block, paragraph, block_separator, block_separator), - par = parsers.separator_loop(separated_block, paragraph, block_separator, paragraph_separator) - } - end - - parsers.block_sep_group = function(blank) - return blank^0 * parsers.eof - + ( blank^2 / writer.paragraphsep - + blank^0 / writer.interblocksep - ) - end - - parsers.par_sep_group = function(blank) - return blank^0 * parsers.eof - + blank^0 / writer.paragraphsep - end - - parsers.sep_group_no_output = function(blank) - return blank^0 * parsers.eof - + blank^0 - end - - parsers.content_blank = parsers.minimally_indented_blankline - - parsers.ref_or_block_separated = parsers.sep_group_no_output(parsers.content_blank) - * ( parsers.minimally_indented_ref - - parsers.content_blank) - + parsers.block_sep_group(parsers.content_blank) - * ( parsers.minimally_indented_block - - parsers.content_blank) - - parsers.loop_body_pair = - parsers.create_loop_body_pair(parsers.ref_or_block_separated, - parsers.minimally_indented_par_or_plain_no_blank, - parsers.block_sep_group(parsers.content_blank), - parsers.par_sep_group(parsers.content_blank)) - - parsers.content_loop = ( V("Block") - * parsers.loop_body_pair.block^0 - + (V("Paragraph") + V("Plain")) - * parsers.ref_or_block_separated - * parsers.loop_body_pair.block^0 - + (V("Paragraph") + V("Plain")) - * parsers.loop_body_pair.par^0) - * parsers.content_blank^0 - - parsers.indented_content = function() - return Ct( (V("Reference") + (parsers.blankline / "")) - * parsers.content_blank^0 - * parsers.check_minimal_indent - * parsers.content_loop - + (V("Reference") + (parsers.blankline / "")) - * parsers.content_blank^0 - + parsers.content_loop) - end - - parsers.add_indent = function(pattern, name, breakable) - return Cg(Cmt( Cb("indent_info") - * Ct(pattern) - * (#parsers.linechar * Cc(false) + Cc(true)) -- check if starter is blank - * Cc(name) - * Cc(breakable), - process_starter_indent), "indent_info") - end - - if options.hashEnumerators then - parsers.dig = parsers.digit + parsers.hash - else - parsers.dig = parsers.digit - end - - parsers.enumerator = function(delimiter_type, interrupting) - local delimiter_range - local allowed_end - if interrupting then - delimiter_range = P("1") - allowed_end = C(parsers.spacechar^1) * #parsers.linechar - else - delimiter_range = parsers.dig * parsers.dig^-8 - allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof) - end - - return parsers.check_trail - * Ct(C(delimiter_range) * C(delimiter_type)) - * allowed_end - end - - parsers.starter = parsers.bullet(parsers.dash) - + parsers.bullet(parsers.asterisk) - + parsers.bullet(parsers.plus) - + parsers.enumerator(parsers.period) - + parsers.enumerator(parsers.rparent) - - parsers.blockquote_start = parsers.check_trail * C(parsers.more) * C(parsers.spacechar^0) - - parsers.blockquote_body = parsers.add_indent(parsers.blockquote_start, "bq", true) - * parsers.indented_content() - * remove_indent("bq") - - if not options.breakableBlockquotes then - parsers.blockquote_body = parsers.add_indent(parsers.blockquote_start, "bq", false) - * parsers.indented_content() - * remove_indent("bq") - end - local function parse_content_part(content_part) - local rope = util.rope_to_string(content_part) - local parsed = self.parser_functions.parse_inlines_no_link_or_emphasis(rope) - parsed.indent_info = nil - return parsed - end - - local function collect_emphasis_content(t, opening_index, closing_index) - local content = {} - - local content_part = {} - for i = opening_index, closing_index do - local value = t[i] - - if value.rendered ~= nil then - content[#content + 1] = parse_content_part(content_part) - content_part = {} - content[#content + 1] = value.rendered - value.rendered = nil - else - if value.type == "delimiter" and value.element == "emphasis" then - if value.is_active then - content_part[#content_part + 1] = string.rep(value.character, value.current_count) - end - else - content_part[#content_part + 1] = value.content - end - value.content = '' - value.is_active = false - end - end - - if next(content_part) ~= nil then - content[#content + 1] = parse_content_part(content_part) - end - - return content - end - - local function fill_emph(t, opening_index, closing_index) - local content = collect_emphasis_content(t, opening_index + 1, closing_index - 1) - t[opening_index + 1].is_active = true - t[opening_index + 1].rendered = writer.emphasis(content) - end - - local function fill_strong(t, opening_index, closing_index) - local content = collect_emphasis_content(t, opening_index + 1, closing_index - 1) - t[opening_index + 1].is_active = true - t[opening_index + 1].rendered = writer.strong(content) - end - - local function breaks_three_rule(opening_delimiter, closing_delimiter) - return (opening_delimiter.is_closing or closing_delimiter.is_opening) and - ((opening_delimiter.original_count + closing_delimiter.original_count) % 3 == 0) and - (opening_delimiter.original_count % 3 ~= 0 or closing_delimiter.original_count % 3 ~= 0) - end - - local function find_emphasis_opener(t, bottom_index, latest_index, character, closing_delimiter) - for i = latest_index, bottom_index, -1 do - local value = t[i] - if value.is_active and - value.is_opening and - value.type == "delimiter" and - value.element == "emphasis" and - (value.character == character) and - (value.current_count > 0) then - if not breaks_three_rule(value, closing_delimiter) then - return i - end - end - end - end - - local function process_emphasis(t, opening_index, closing_index) - for i = opening_index, closing_index do - local value = t[i] - if value.type == "delimiter" and value.element == "emphasis" then - local delimiter_length = string.len(value.content) - value.character = string.sub(value.content, 1, 1) - value.current_count = delimiter_length - value.original_count = delimiter_length - end - end - - local openers_bottom = { - ['*'] = { - [true] = {opening_index, opening_index, opening_index}, - [false] = {opening_index, opening_index, opening_index} - }, - ['_'] = { - [true] = {opening_index, opening_index, opening_index}, - [false] = {opening_index, opening_index, opening_index} - } - } - - local current_position = opening_index - local max_position = closing_index - - while current_position <= max_position do - local value = t[current_position] - - if value.type ~= "delimiter" or - value.element ~= "emphasis" or - not value.is_active or - not value.is_closing or - (value.current_count <= 0) then - current_position = current_position + 1 - goto continue - end - - local character = value.character - local is_opening = value.is_opening - local closing_length_modulo_three = value.original_count % 3 - - local current_openers_bottom = openers_bottom[character][is_opening][closing_length_modulo_three + 1] - - local opener_position = find_emphasis_opener(t, current_openers_bottom, current_position - 1, character, value) - - if (opener_position == nil) then - openers_bottom[character][is_opening][closing_length_modulo_three + 1] = current_position - current_position = current_position + 1 - goto continue - end - - local opening_delimiter = t[opener_position] - - local current_opening_count = opening_delimiter.current_count - local current_closing_count = t[current_position].current_count - - if (current_opening_count >= 2) and (current_closing_count >= 2) then - opening_delimiter.current_count = current_opening_count - 2 - t[current_position].current_count = current_closing_count - 2 - fill_strong(t, opener_position, current_position) - else - opening_delimiter.current_count = current_opening_count - 1 - t[current_position].current_count = current_closing_count - 1 - fill_emph(t, opener_position, current_position) - end - - ::continue:: - end - end - - local cont = lpeg.R("\128\191") -- continuation byte - - local function utf8_by_byte_count(n) - if (n == 1) then - return lpeg.R("\0\127") - end - if (n == 2) then - return lpeg.R("\194\223") * cont - end - if (n == 3) then - return lpeg.R("\224\239") * cont * cont - end - if (n == 4) then - return lpeg.R("\240\244") * cont * cont * cont - end - end - local function check_unicode_type(s, i, start_pos, end_pos, chartype) - local c - local char_length - for pos = start_pos, end_pos, 1 do - if (start_pos < 0) then - char_length = -pos - else - char_length = pos + 1 - end - - if (chartype == "punctuation") then - if lpeg.match(parsers.punctuation[char_length], s, i+pos) then - return i - end - else - c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos) - if (c ~= nil) and (unicode.utf8.match(c, chartype)) then - return i - end - end - end - end - - local function check_preceding_unicode_punctuation(s, i) - return check_unicode_type(s, i, -4, -1, "punctuation") - end - - local function check_preceding_unicode_whitespace(s, i) - return check_unicode_type(s, i, -4, -1, "%s") - end - - local function check_following_unicode_punctuation(s, i) - return check_unicode_type(s, i, 0, 3, "punctuation") - end - - local function check_following_unicode_whitespace(s, i) - return check_unicode_type(s, i, 0, 3, "%s") - end - - parsers.unicode_preceding_punctuation = B(parsers.escapable) - + Cmt(parsers.succeed, check_preceding_unicode_punctuation) - - parsers.unicode_preceding_whitespace = Cmt(parsers.succeed, check_preceding_unicode_whitespace) - - parsers.unicode_following_punctuation = #parsers.escapable - + Cmt(parsers.succeed, check_following_unicode_punctuation) - - parsers.unicode_following_whitespace = Cmt(parsers.succeed, check_following_unicode_whitespace) - - parsers.delimiter_run = function(character) - return (B(parsers.backslash * character) + -B(character)) - * character^1 - * -#character - end - - parsers.left_flanking_delimiter_run = function(character) - return (B( parsers.any) - * (parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace) - + -B(parsers.any)) - * parsers.delimiter_run(character) - * parsers.unicode_following_punctuation - + parsers.delimiter_run(character) - * -#(parsers.unicode_following_punctuation + parsers.unicode_following_whitespace - + parsers.eof) - end - - parsers.right_flanking_delimiter_run = function(character) - return parsers.unicode_preceding_punctuation - * parsers.delimiter_run(character) - * (parsers.unicode_following_punctuation + parsers.unicode_following_whitespace - + parsers.eof) - + (B(parsers.any) - * -(parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace)) - * parsers.delimiter_run(character) - end - - if options.underscores then - parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk) - + (-#parsers.right_flanking_delimiter_run(parsers.underscore) - + (parsers.unicode_preceding_punctuation - * #parsers.right_flanking_delimiter_run(parsers.underscore))) - * parsers.left_flanking_delimiter_run(parsers.underscore) - - parsers.emph_end = parsers.right_flanking_delimiter_run(parsers.asterisk) - + (-#parsers.left_flanking_delimiter_run(parsers.underscore) - + #(parsers.left_flanking_delimiter_run(parsers.underscore) - * parsers.unicode_following_punctuation)) - * parsers.right_flanking_delimiter_run(parsers.underscore) - else - parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk) - - parsers.emph_end = parsers.right_flanking_delimiter_run(parsers.asterisk) - end - - parsers.emph_capturing_open_and_close = #parsers.emph_start * #parsers.emph_end - * Ct( Cg(Cc("delimiter"), "type") - * Cg(Cc("emphasis"), "element") - * Cg(C(parsers.emph_start), "content") - * Cg(Cc(true), "is_opening") - * Cg(Cc(true), "is_closing")) - - parsers.emph_capturing_open = Ct( Cg(Cc("delimiter"), "type") - * Cg(Cc("emphasis"), "element") - * Cg(C(parsers.emph_start), "content") - * Cg(Cc(true), "is_opening") - * Cg(Cc(false), "is_closing")) - - parsers.emph_capturing_close = Ct( Cg(Cc("delimiter"), "type") - * Cg(Cc("emphasis"), "element") - * Cg(C(parsers.emph_end), "content") - * Cg(Cc(false), "is_opening") - * Cg(Cc(true), "is_closing")) - - parsers.emph_open_or_close = parsers.emph_capturing_open_and_close - + parsers.emph_capturing_open - + parsers.emph_capturing_close - - parsers.emph_open = parsers.emph_capturing_open_and_close - + parsers.emph_capturing_open - - parsers.emph_close = parsers.emph_capturing_open_and_close - + parsers.emph_capturing_close - - -- List of references defined in the document - local references - - function self.register_link(_, tag, url, title, - attributes) - local normalized_tag = self.normalize_tag(tag) - if references[normalized_tag] == nil then - references[normalized_tag] = { - url = url, - title = title, - attributes = attributes - } - end - return "" - end - - function self.lookup_reference(tag) - return references[self.normalize_tag(tag)] - end - - parsers.title_s_direct_ref = parsers.squote - * Cs((parsers.html_entities - + (parsers.anyescaped - parsers.squote - parsers.blankline^2))^0) - * parsers.squote - - parsers.title_d_direct_ref = parsers.dquote - * Cs((parsers.html_entities - + (parsers.anyescaped - parsers.dquote - parsers.blankline^2))^0) - * parsers.dquote - - parsers.title_p_direct_ref = parsers.lparent - * Cs((parsers.html_entities - + (parsers.anyescaped - parsers.lparent - parsers.rparent - parsers.blankline^2))^0) - * parsers.rparent - - parsers.title_direct_ref = parsers.title_s_direct_ref - + parsers.title_d_direct_ref - + parsers.title_p_direct_ref - - parsers.inline_direct_ref_inside = parsers.lparent * parsers.spnl - * Cg(parsers.url + Cc(""), "url") - * parsers.spnl - * Cg(parsers.title_direct_ref + Cc(""), "title") - * parsers.spnl * parsers.rparent - - parsers.inline_direct_ref = parsers.lparent * parsers.spnlc - * Cg(parsers.url + Cc(""), "url") - * parsers.spnlc - * Cg(parsers.title + Cc(""), "title") - * parsers.spnlc * parsers.rparent - - parsers.empty_link = parsers.lbracket - * parsers.rbracket - - parsers.inline_link = parsers.link_text - * parsers.inline_direct_ref - - parsers.full_link = parsers.link_text - * parsers.link_label - - parsers.shortcut_link = parsers.link_label - * -(parsers.empty_link + parsers.link_label) - - parsers.collapsed_link = parsers.link_label - * parsers.empty_link - - parsers.image_opening = #(parsers.exclamation * parsers.inline_link) - * Cg(Cc("inline"), "link_type") - + #(parsers.exclamation * parsers.full_link) - * Cg(Cc("full"), "link_type") - + #(parsers.exclamation * parsers.collapsed_link) - * Cg(Cc("collapsed"), "link_type") - + #(parsers.exclamation * parsers.shortcut_link) - * Cg(Cc("shortcut"), "link_type") - + #(parsers.exclamation * parsers.empty_link) - * Cg(Cc("empty"), "link_type") - - parsers.link_opening = #parsers.inline_link - * Cg(Cc("inline"), "link_type") - + #parsers.full_link - * Cg(Cc("full"), "link_type") - + #parsers.collapsed_link - * Cg(Cc("collapsed"), "link_type") - + #parsers.shortcut_link - * Cg(Cc("shortcut"), "link_type") - + #parsers.empty_link - * Cg(Cc("empty_link"), "link_type") - + #parsers.link_text - * Cg(Cc("link_text"), "link_type") - - parsers.link_image_opening = Ct( Cg(Cc("delimiter"), "type") - * Cg(Cc(true), "is_opening") - * Cg(Cc(false), "is_closing") - * ( Cg(Cc("image"), "element") - * parsers.image_opening - * Cg(parsers.exclamation * parsers.lbracket, "content") - + Cg(Cc("link"), "element") - * parsers.link_opening - * Cg(parsers.lbracket, "content"))) - - parsers.link_image_closing = Ct( Cg(Cc("delimiter"), "type") - * Cg(Cc("link"), "element") - * Cg(Cc(false), "is_opening") - * Cg(Cc(true), "is_closing") - * ( Cg(Cc(true), "is_direct") - * Cg(parsers.rbracket * #parsers.inline_direct_ref, "content") - + Cg(Cc(false), "is_direct") - * Cg(parsers.rbracket, "content"))) - - parsers.link_image_open_or_close = parsers.link_image_opening - + parsers.link_image_closing - - if options.html then - parsers.link_emph_precedence = parsers.inticks - + parsers.autolink - + parsers.html_inline_tags - else - parsers.link_emph_precedence = parsers.inticks - + parsers.autolink - end - - parsers.link_and_emph_endline = parsers.newline - * ((parsers.check_minimal_indent - * -V("EndlineExceptions") - + parsers.check_optional_indent - * -V("EndlineExceptions") - * -parsers.starter) / "") - * parsers.spacechar^0 / "\n" - - parsers.link_and_emph_content = Ct( Cg(Cc("content"), "type") - * Cg(Cs(( parsers.link_emph_precedence - + parsers.backslash * parsers.any - + parsers.link_and_emph_endline - + (parsers.linechar - - parsers.blankline^2 - - parsers.link_image_open_or_close - - parsers.emph_open_or_close))^0), "content")) - - parsers.link_and_emph_table = (parsers.link_image_opening + parsers.emph_open) - * parsers.link_and_emph_content - * ((parsers.link_image_open_or_close + parsers.emph_open_or_close) - * parsers.link_and_emph_content)^1 - - local function collect_link_content(t, opening_index, closing_index) - local content = {} - for i = opening_index, closing_index do - content[#content + 1] = t[i].content - end - return util.rope_to_string(content) - end - - local function find_link_opener(t, bottom_index, latest_index) - for i = latest_index, bottom_index, -1 do - local value = t[i] - if value.type == "delimiter" and - value.is_opening and - (value.element == "link" or value.element == "image") - and not value.removed then - if value.is_active then - return i - end - value.removed = true - return nil - end - end - end - - local function find_next_link_closing_index(t, latest_index) - for i = latest_index, #t do - local value = t[i] - if value.is_closing and - value.element == "link" and - not value.removed then - return i - end - end - end - - local function disable_previous_link_openers(t, opening_index) - if t[opening_index].element == "image" then - return - end - - for i = opening_index, 1, -1 do - local value = t[i] - if value.is_active and - value.type == "delimiter" and - value.is_opening and - value.element == "link" then - value.is_active = false - end - end - end - - local function disable_range(t, opening_index, closing_index) - for i = opening_index, closing_index do - local value = t[i] - if value.is_active then - value.is_active = false - if value.type == "delimiter" then - value.removed = true - end - end - end - end - - local function delete_parsed_content_in_range(t, opening_index, closing_index) - for i = opening_index, closing_index do - t[i].rendered = nil - end - end - - local function empty_content_in_range(t, opening_index, closing_index) - for i = opening_index, closing_index do - t[i].content = '' - end - end - - local function join_attributes(reference_attributes, own_attributes) - local merged_attributes = {} - for _, attribute in ipairs(reference_attributes or {}) do - table.insert(merged_attributes, attribute) - end - for _, attribute in ipairs(own_attributes or {}) do - table.insert(merged_attributes, attribute) - end - if next(merged_attributes) == nil then - merged_attributes = nil - end - return merged_attributes - end - - local function render_link_or_image(t, opening_index, closing_index, content_end_index, reference) - process_emphasis(t, opening_index, content_end_index) - local mapped = collect_emphasis_content(t, opening_index + 1, content_end_index - 1) - - local rendered = {} - if (t[opening_index].element == "link") then - rendered = writer.link(mapped, reference.url, reference.title, reference.attributes) - end - - if (t[opening_index].element == "image") then - rendered = writer.image(mapped, reference.url, reference.title, reference.attributes) - end - - t[opening_index].rendered = rendered - delete_parsed_content_in_range(t, opening_index + 1, closing_index) - empty_content_in_range(t, opening_index, closing_index) - disable_previous_link_openers(t, opening_index) - disable_range(t, opening_index, closing_index) - end - - local function resolve_inline_following_content(t, closing_index, match_reference, match_link_attributes) - local content = "" - for i = closing_index + 1, #t do - content = content .. t[i].content - end - - local matching_content = parsers.succeed - - if match_reference then - matching_content = matching_content * parsers.inline_direct_ref_inside - end - - if match_link_attributes then - matching_content = matching_content * Cg(Ct(parsers.attributes^-1), "attributes") - end - - local matched = lpeg.match(Ct(matching_content * Cg(Cp(), "end_position")), content) - - local matched_count = matched.end_position - 1 - for i = closing_index + 1, #t do - local value = t[i] - - local chars_left = matched_count - matched_count = matched_count - #value.content - - if matched_count <= 0 then - value.content = value.content:sub(chars_left + 1) - break - end - - value.content = '' - value.is_active = false - end - - local attributes = matched.attributes - if attributes == nil or next(attributes) == nil then - attributes = nil - end - - return { - url = matched.url or "", - title = matched.title or "", - attributes = attributes - } - end - - local function resolve_inline_link(t, opening_index, closing_index) - local inline_content = resolve_inline_following_content(t, closing_index, true, t.match_link_attributes) - render_link_or_image(t, opening_index, closing_index, closing_index, inline_content) - end - - local function resolve_shortcut_link(t, opening_index, closing_index) - local content = collect_link_content(t, opening_index + 1, closing_index - 1) - local r = self.lookup_reference(content) - - if r then - local inline_content = resolve_inline_following_content(t, closing_index, false, t.match_link_attributes) - r.attributes = join_attributes(r.attributes, inline_content.attributes) - render_link_or_image(t, opening_index, closing_index, closing_index, r) - end - end - - local function resolve_full_link(t, opening_index, closing_index) - local next_link_closing_index = find_next_link_closing_index(t, closing_index + 4) - local next_link_content = collect_link_content(t, closing_index + 3, next_link_closing_index - 1) - local r = self.lookup_reference(next_link_content) - - if r then - local inline_content = resolve_inline_following_content(t, next_link_closing_index, false, - t.match_link_attributes) - r.attributes = join_attributes(r.attributes, inline_content.attributes) - render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r) - end - end - - local function resolve_collapsed_link(t, opening_index, closing_index) - local next_link_closing_index = find_next_link_closing_index(t, closing_index + 4) - local content = collect_link_content(t, opening_index + 1, closing_index - 1) - local r = self.lookup_reference(content) - - if r then - local inline_content = resolve_inline_following_content(t, closing_index, false, t.match_link_attributes) - r.attributes = join_attributes(r.attributes, inline_content.attributes) - render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r) - end - end - - local function process_links_and_emphasis(t) - for _,value in ipairs(t) do - value.is_active = true - end - - for i,value in ipairs(t) do - if not value.is_closing or - value.type ~= "delimiter" or - not (value.element == "link" or value.element == "image") then - goto continue - end - - local opener_position = find_link_opener(t, 1, i - 1) - if (opener_position == nil) then - goto continue - end - - local opening_delimiter = t[opener_position] - opening_delimiter.removed = true - - local link_type = opening_delimiter.link_type - - if (link_type == "inline") then - resolve_inline_link(t, opener_position, i) - end - if (link_type == "shortcut") then - resolve_shortcut_link(t, opener_position, i) - end - if (link_type == "full") then - resolve_full_link(t, opener_position, i) - end - if (link_type == "collapsed") then - resolve_collapsed_link(t, opener_position, i) - end - - ::continue:: - end - - t[#t].content = t[#t].content:gsub("%s*$","") - - process_emphasis(t, 1, #t) - local final_result = collect_emphasis_content(t, 1, #t) - return final_result - end - - function self.defer_link_and_emphasis_processing(delimiter_table) - return writer.defer_call(function() - return process_links_and_emphasis(delimiter_table) - end) - end - - parsers.Str = (parsers.normalchar * (parsers.normalchar + parsers.at)^0) - / writer.string - - parsers.Symbol = (parsers.backtick^1 + V("SpecialChar")) - / writer.string - - parsers.Ellipsis = P("...") / writer.ellipsis - - parsers.Smart = parsers.Ellipsis - - parsers.Code = parsers.inticks / writer.code - - if options.blankBeforeBlockquote then - parsers.bqstart = parsers.fail - else - parsers.bqstart = parsers.blockquote_start - end - - if options.blankBeforeHeading then - parsers.headerstart = parsers.fail - else - parsers.headerstart = parsers.atx_heading - end - - if options.blankBeforeList then - parsers.interrupting_bullets = parsers.fail - parsers.interrupting_enumerators = parsers.fail - else - parsers.interrupting_bullets = parsers.bullet(parsers.dash, true) - + parsers.bullet(parsers.asterisk, true) - + parsers.bullet(parsers.plus, true) - - parsers.interrupting_enumerators = parsers.enumerator(parsers.period, true) - + parsers.enumerator(parsers.rparent, true) - end - - if options.html then - parsers.html_interrupting = parsers.check_trail - * ( parsers.html_incomplete_open_tag - + parsers.html_incomplete_close_tag - + parsers.html_incomplete_open_special_tag - + parsers.html_comment_start - + parsers.html_cdatasection_start - + parsers.html_declaration_start - + parsers.html_instruction_start - - parsers.html_close_special_tag - - parsers.html_empty_special_tag) - else - parsers.html_interrupting = parsers.fail - end - - parsers.EndlineExceptions - = parsers.blankline -- paragraph break - + parsers.eof -- end of document - + parsers.bqstart - + parsers.thematic_break_lines - + parsers.interrupting_bullets - + parsers.interrupting_enumerators - + parsers.headerstart - + parsers.html_interrupting - - parsers.NoSoftLineBreakEndlineExceptions = parsers.EndlineExceptions - - parsers.endline = parsers.newline - * (parsers.check_minimal_indent - * -V("EndlineExceptions") - + parsers.check_optional_indent - * -V("EndlineExceptions") - * -parsers.starter) - * parsers.spacechar^0 - - parsers.Endline = parsers.endline - / writer.soft_line_break - - parsers.EndlineNoSub = parsers.endline - - parsers.NoSoftLineBreakEndline - = parsers.newline - * (parsers.check_minimal_indent - * -V("NoSoftLineBreakEndlineExceptions") - + parsers.check_optional_indent - * -V("NoSoftLineBreakEndlineExceptions") - * -parsers.starter) - * parsers.spacechar^0 - / writer.space - - parsers.EndlineBreak = parsers.backslash * parsers.Endline - / writer.hard_line_break - - parsers.OptionalIndent - = parsers.spacechar^1 / writer.space - - parsers.Space = parsers.spacechar^2 * parsers.Endline - / writer.hard_line_break - + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / self.expandtabs - + parsers.spacechar^1 * parsers.Endline - / writer.soft_line_break - + parsers.spacechar^1 * -parsers.newline / self.expandtabs - - parsers.NoSoftLineBreakSpace - = parsers.spacechar^2 * parsers.Endline - / writer.hard_line_break - + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / self.expandtabs - + parsers.spacechar^1 * parsers.Endline - / writer.soft_line_break - + parsers.spacechar^1 * -parsers.newline / self.expandtabs - - parsers.NonbreakingEndline - = parsers.endline - / writer.soft_line_break - - parsers.NonbreakingSpace - = parsers.spacechar^2 * parsers.Endline - / writer.hard_line_break - + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / "" - + parsers.spacechar^1 * parsers.Endline - * parsers.optionalspace - / writer.soft_line_break - + parsers.spacechar^1 * parsers.optionalspace - / writer.nbsp - -function self.auto_link_url(url, attributes) - return writer.link(writer.escape(url), - url, nil, attributes) -end -function self.auto_link_email(email, attributes) - return writer.link(writer.escape(email), - "mailto:"..email, - nil, attributes) -end - - parsers.AutoLinkUrl = parsers.auto_link_url - / self.auto_link_url - - parsers.AutoLinkEmail - = parsers.auto_link_email - / self.auto_link_email - - parsers.AutoLinkRelativeReference - = parsers.auto_link_relative_reference - / self.auto_link_url - - parsers.LinkAndEmph = Ct(parsers.link_and_emph_table) - / self.defer_link_and_emphasis_processing - - parsers.EscapedChar = parsers.backslash * C(parsers.escapable) / writer.string - - parsers.InlineHtml = Cs(parsers.html_inline_comment) / writer.inline_html_comment - + Cs(parsers.html_any_empty_inline_tag - + parsers.html_inline_instruction - + parsers.html_inline_cdatasection - + parsers.html_inline_declaration - + parsers.html_any_open_inline_tag - + parsers.html_any_close_tag) - / writer.inline_html_tag - - parsers.HtmlEntity = parsers.html_entities / writer.string - parsers.DisplayHtml = Cs(parsers.check_trail - * ( parsers.html_comment - + parsers.html_special_block - + parsers.html_block - + parsers.html_any_block - + parsers.html_instruction - + parsers.html_cdatasection - + parsers.html_declaration)) - / writer.block_html_element - - parsers.indented_non_blank_line = parsers.indentedline - parsers.blankline - - parsers.Verbatim = Cs( - parsers.check_code_trail - * (parsers.line - parsers.blankline) - * ((parsers.check_minimal_blank_indent_and_full_code_trail * parsers.blankline)^0 - * ((parsers.check_minimal_indent / "") * parsers.check_code_trail - * (parsers.line - parsers.blankline))^1)^0 - ) / self.expandtabs / writer.verbatim - - parsers.Blockquote = parsers.blockquote_body - / writer.blockquote - - parsers.ThematicBreak = parsers.thematic_break_lines - / writer.thematic_break - - parsers.Reference = parsers.define_reference_parser - / self.register_link - - parsers.Paragraph = parsers.freeze_trail - * (Ct((parsers.Inline)^1) - * (parsers.newline + parsers.eof) - * parsers.unfreeze_trail - / writer.paragraph) - - parsers.Plain = parsers.nonindentspace * Ct(parsers.Inline^1) - / writer.plain - - if options.taskLists then - parsers.tickbox = ( parsers.ticked_box - + parsers.halfticked_box - + parsers.unticked_box - ) / writer.tickbox - else - parsers.tickbox = parsers.fail - end - - parsers.list_blank = parsers.conditionally_indented_blankline - - parsers.ref_or_block_list_separated = parsers.sep_group_no_output(parsers.list_blank) - * parsers.minimally_indented_ref - + parsers.block_sep_group(parsers.list_blank) - * parsers.minimally_indented_block - - parsers.ref_or_block_non_separated = parsers.minimally_indented_ref - + (parsers.succeed / writer.interblocksep) - * parsers.minimally_indented_block - - parsers.minimally_indented_blankline - - parsers.tight_list_loop_body_pair = - parsers.create_loop_body_pair(parsers.ref_or_block_non_separated, - parsers.minimally_indented_par_or_plain_no_blank, - (parsers.succeed / writer.interblocksep), - (parsers.succeed / writer.paragraphsep)) - - parsers.loose_list_loop_body_pair = - parsers.create_loop_body_pair(parsers.ref_or_block_list_separated, - parsers.minimally_indented_par_or_plain, - parsers.block_sep_group(parsers.list_blank), - parsers.par_sep_group(parsers.list_blank)) - - parsers.tight_list_content_loop = V("Block") - * parsers.tight_list_loop_body_pair.block^0 - + (V("Paragraph") + V("Plain")) - * parsers.ref_or_block_non_separated - * parsers.tight_list_loop_body_pair.block^0 - + (V("Paragraph") + V("Plain")) - * parsers.tight_list_loop_body_pair.par^0 - - parsers.loose_list_content_loop = V("Block") - * parsers.loose_list_loop_body_pair.block^0 - + (V("Paragraph") + V("Plain")) - * parsers.ref_or_block_list_separated - * parsers.loose_list_loop_body_pair.block^0 - + (V("Paragraph") + V("Plain")) - * parsers.loose_list_loop_body_pair.par^0 - - parsers.list_item_tightness_condition = -#( parsers.list_blank^0 - * parsers.minimally_indented_ref_or_block_or_par) - * remove_indent("li") - + remove_indent("li") - * parsers.fail - - parsers.indented_content_tight = Ct( (parsers.blankline / "") - * #parsers.list_blank - * remove_indent("li") - + ( (V("Reference") + (parsers.blankline / "")) - * parsers.check_minimal_indent - * parsers.tight_list_content_loop - + (V("Reference") + (parsers.blankline / "")) - + (parsers.tickbox^-1 / writer.escape) - * parsers.tight_list_content_loop - ) - * parsers.list_item_tightness_condition - ) - - parsers.indented_content_loose = Ct( (parsers.blankline / "") - * #parsers.list_blank - + ( (V("Reference") + (parsers.blankline / "")) - * parsers.check_minimal_indent - * parsers.loose_list_content_loop - + (V("Reference") + (parsers.blankline / "")) - + (parsers.tickbox^-1 / writer.escape) - * parsers.loose_list_content_loop - ) - ) - - parsers.TightListItem = function(starter) - return -parsers.ThematicBreak - * parsers.add_indent(starter, "li") - * parsers.indented_content_tight - end - - parsers.LooseListItem = function(starter) - return -parsers.ThematicBreak - * parsers.add_indent(starter, "li") - * parsers.indented_content_loose - * remove_indent("li") - end - - parsers.BulletListOfType = function(bullet_type) - local bullet = parsers.bullet(bullet_type) - return ( Ct( parsers.TightListItem(bullet) - * ( (parsers.check_minimal_indent / "") - * parsers.TightListItem(bullet) - )^0 - ) - * Cc(true) - * -#( (parsers.list_blank^0 / "") - * parsers.check_minimal_indent - * (bullet - parsers.ThematicBreak) - ) - + Ct( parsers.LooseListItem(bullet) - * ( (parsers.list_blank^0 / "") - * (parsers.check_minimal_indent / "") - * parsers.LooseListItem(bullet) - )^0 - ) - * Cc(false) - ) / writer.bulletlist - end - - parsers.BulletList = parsers.BulletListOfType(parsers.dash) - + parsers.BulletListOfType(parsers.asterisk) - + parsers.BulletListOfType(parsers.plus) - - local function ordered_list(items,tight,starter) - local startnum = starter[2][1] - if options.startNumber then - startnum = tonumber(startnum) or 1 -- fallback for '#' - if startnum ~= nil then - startnum = math.floor(startnum) - end - else - startnum = nil - end - return writer.orderedlist(items,tight,startnum) - end - - parsers.OrderedListOfType = function(delimiter_type) - local enumerator = parsers.enumerator(delimiter_type) - return Cg(enumerator, "listtype") - * (Ct( parsers.TightListItem(Cb("listtype")) - * ((parsers.check_minimal_indent / "") * parsers.TightListItem(enumerator))^0) - * Cc(true) - * -#((parsers.list_blank^0 / "") - * parsers.check_minimal_indent * enumerator) - + Ct( parsers.LooseListItem(Cb("listtype")) - * ((parsers.list_blank^0 / "") - * (parsers.check_minimal_indent / "") * parsers.LooseListItem(enumerator))^0) - * Cc(false) - ) * Ct(Cb("listtype")) / ordered_list - end - - parsers.OrderedList = parsers.OrderedListOfType(parsers.period) - + parsers.OrderedListOfType(parsers.rparent) - parsers.Blank = parsers.blankline / "" - + V("Reference") - function parsers.parse_heading_text(s) - local inlines = self.parser_functions.parse_inlines(s) - local flatten_inlines = self.writer.flatten_inlines - self.writer.flatten_inlines = true - local flat_text = self.parser_functions.parse_inlines(s) - flat_text = util.rope_to_string(flat_text) - self.writer.flatten_inlines = flatten_inlines - return {flat_text, inlines} - end - - -- parse atx header - parsers.AtxHeading = parsers.check_trail_no_rem - * Cg(parsers.heading_start, "level") - * ((C( parsers.optionalspace - * parsers.hash^0 - * parsers.optionalspace - * parsers.newline) - + parsers.spacechar^1 - * C(parsers.line)) - / strip_atx_end - / parsers.parse_heading_text) - * Cb("level") - / writer.heading - - parsers.heading_line = parsers.linechar^1 - - parsers.thematic_break_lines - - parsers.heading_text = parsers.heading_line - * ((V("Endline") / "\n") * (parsers.heading_line - parsers.heading_level))^0 - * parsers.newline^-1 - - parsers.SetextHeading = parsers.freeze_trail * parsers.check_trail_no_rem - * #(parsers.heading_text - * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level) - * Cs(parsers.heading_text) - / parsers.parse_heading_text - * parsers.check_minimal_indent_and_trail * parsers.heading_level - * parsers.newline - * parsers.unfreeze_trail - / writer.heading - - parsers.Heading = parsers.AtxHeading + parsers.SetextHeading - function self.finalize_grammar(extensions) - local walkable_syntax = (function(global_walkable_syntax) - local local_walkable_syntax = {} - for lhs, rule in pairs(global_walkable_syntax) do - local_walkable_syntax[lhs] = util.table_copy(rule) - end - return local_walkable_syntax - end)(walkable_syntax) - local current_extension_name = nil - self.insert_pattern = function(selector, pattern, pattern_name) - assert(pattern_name == nil or type(pattern_name) == "string") - local _, _, lhs, pos, rhs = selector:find("^(%a+)%s+([%a%s]+%a+)%s+(%a+)$") - assert(lhs ~= nil, - [[Expected selector in form "LHS (before|after|instead of) RHS", not "]] - .. selector .. [["]]) - assert(walkable_syntax[lhs] ~= nil, - [[Rule ]] .. lhs .. [[ -> ... does not exist in markdown grammar]]) - assert(pos == "before" or pos == "after" or pos == "instead of", - [[Expected positional specifier "before", "after", or "instead of", not "]] - .. pos .. [["]]) - local rule = walkable_syntax[lhs] - local index = nil - for current_index, current_rhs in ipairs(rule) do - if type(current_rhs) == "string" and current_rhs == rhs then - index = current_index - if pos == "after" then - index = index + 1 - end - break - end - end - assert(index ~= nil, - [[Rule ]] .. lhs .. [[ -> ]] .. rhs - .. [[ does not exist in markdown grammar]]) - local accountable_pattern - if current_extension_name then - accountable_pattern = { pattern, current_extension_name, pattern_name } - else - assert(type(pattern) == "string", - [[reader->insert_pattern() was called outside an extension with ]] - .. [[a PEG pattern instead of a rule name]]) - accountable_pattern = pattern - end - if pos == "instead of" then - rule[index] = accountable_pattern - else - table.insert(rule, index, accountable_pattern) - end - end - local syntax = - { "Blocks", - - Blocks = V("InitializeState") - * ( V("ExpectedJekyllData") - * (V("Blank")^0 / writer.interblocksep) - )^-1 - * V("Blank")^0 - * ( V("Block") - * ( V("Blank")^0 * parsers.eof - + ( V("Blank")^2 / writer.paragraphsep - + V("Blank")^0 / writer.interblocksep - ) - ) - + ( V("Paragraph") + V("Plain") ) - * ( V("Blank")^0 * parsers.eof - + ( V("Blank")^2 / writer.paragraphsep - + V("Blank")^0 / writer.interblocksep - ) - ) - * V("Block") - * ( V("Blank")^0 * parsers.eof - + ( V("Blank")^2 / writer.paragraphsep - + V("Blank")^0 / writer.interblocksep - ) - ) - + ( V("Paragraph") + V("Plain") ) - * ( V("Blank")^0 * parsers.eof - + V("Blank")^0 / writer.paragraphsep - ) - )^0, - - ExpectedJekyllData = parsers.fail, - - Blank = parsers.Blank, - Reference = parsers.Reference, - - Blockquote = parsers.Blockquote, - Verbatim = parsers.Verbatim, - ThematicBreak = parsers.ThematicBreak, - BulletList = parsers.BulletList, - OrderedList = parsers.OrderedList, - DisplayHtml = parsers.DisplayHtml, - Heading = parsers.Heading, - Paragraph = parsers.Paragraph, - Plain = parsers.Plain, - - EndlineExceptions = parsers.EndlineExceptions, - NoSoftLineBreakEndlineExceptions - = parsers.NoSoftLineBreakEndlineExceptions, - - Str = parsers.Str, - Space = parsers.Space, - NoSoftLineBreakSpace = parsers.NoSoftLineBreakSpace, - OptionalIndent = parsers.OptionalIndent, - Endline = parsers.Endline, - EndlineNoSub = parsers.EndlineNoSub, - NoSoftLineBreakEndline - = parsers.NoSoftLineBreakEndline, - EndlineBreak = parsers.EndlineBreak, - LinkAndEmph = parsers.LinkAndEmph, - Code = parsers.Code, - AutoLinkUrl = parsers.AutoLinkUrl, - AutoLinkEmail = parsers.AutoLinkEmail, - AutoLinkRelativeReference - = parsers.AutoLinkRelativeReference, - InlineHtml = parsers.InlineHtml, - HtmlEntity = parsers.HtmlEntity, - EscapedChar = parsers.EscapedChar, - Smart = parsers.Smart, - Symbol = parsers.Symbol, - SpecialChar = parsers.fail, - InitializeState = parsers.succeed, - } - self.update_rule = function(rule_name, get_pattern) - assert(current_extension_name ~= nil) - assert(syntax[rule_name] ~= nil, - [[Rule ]] .. rule_name .. [[ -> ... does not exist in markdown grammar]]) - local previous_pattern - local extension_name - if walkable_syntax[rule_name] then - local previous_accountable_pattern = walkable_syntax[rule_name][1] - previous_pattern = previous_accountable_pattern[1] - extension_name = previous_accountable_pattern[2] .. ", " .. current_extension_name - else - previous_pattern = nil - extension_name = current_extension_name - end - local pattern - if type(get_pattern) == "function" then - pattern = get_pattern(previous_pattern) - else - assert(previous_pattern == nil, - [[Rule ]] .. rule_name .. - [[ has already been updated by ]] .. extension_name) - pattern = get_pattern - end - local accountable_pattern = { pattern, extension_name, rule_name } - walkable_syntax[rule_name] = { accountable_pattern } - end - local special_characters = {} - self.add_special_character = function(c) - table.insert(special_characters, c) - syntax.SpecialChar = S(table.concat(special_characters, "")) - end - - self.add_special_character("*") - self.add_special_character("[") - self.add_special_character("]") - self.add_special_character("<") - self.add_special_character("!") - self.add_special_character("\\") - self.initialize_named_group = function(name, value) - local pattern = Ct("") - if value ~= nil then - pattern = pattern / value - end - syntax.InitializeState = syntax.InitializeState - * Cg(pattern, name) - end - self.initialize_named_group("indent_info") - for _, extension in ipairs(extensions) do - current_extension_name = extension.name - extension.extend_writer(writer) - extension.extend_reader(self) - end - current_extension_name = nil - if options.debugExtensions then - local sorted_lhs = {} - for lhs, _ in pairs(walkable_syntax) do - table.insert(sorted_lhs, lhs) - end - table.sort(sorted_lhs) - - local output_lines = {"{"} - for lhs_index, lhs in ipairs(sorted_lhs) do - local encoded_lhs = util.encode_json_string(lhs) - table.insert(output_lines, [[ ]] ..encoded_lhs .. [[: []]) - local rule = walkable_syntax[lhs] - for rhs_index, rhs in ipairs(rule) do - local human_readable_rhs - if type(rhs) == "string" then - human_readable_rhs = rhs - else - local pattern_name - if rhs[3] then - pattern_name = rhs[3] - else - pattern_name = "Anonymous Pattern" - end - local extension_name = rhs[2] - human_readable_rhs = pattern_name .. [[ (]] .. extension_name .. [[)]] - end - local encoded_rhs = util.encode_json_string(human_readable_rhs) - local output_line = [[ ]] .. encoded_rhs - if rhs_index < #rule then - output_line = output_line .. "," - end - table.insert(output_lines, output_line) - end - local output_line = " ]" - if lhs_index < #sorted_lhs then - output_line = output_line .. "," - end - table.insert(output_lines, output_line) - end - table.insert(output_lines, "}") - - local output = table.concat(output_lines, "\n") - local output_filename = options.debugExtensionsFileName - local output_file = assert(io.open(output_filename, "w"), - [[Could not open file "]] .. output_filename .. [[" for writing]]) - assert(output_file:write(output)) - assert(output_file:close()) - end - for lhs, rule in pairs(walkable_syntax) do - syntax[lhs] = parsers.fail - for _, rhs in ipairs(rule) do - local pattern - if type(rhs) == "string" then - pattern = V(rhs) - else - pattern = rhs[1] - if type(pattern) == "string" then - pattern = V(pattern) - end - end - syntax[lhs] = syntax[lhs] + pattern - end - end - if options.underscores then - self.add_special_character("_") - end - - if not options.codeSpans then - syntax.Code = parsers.fail - else - self.add_special_character("`") - end - - if not options.html then - syntax.DisplayHtml = parsers.fail - syntax.InlineHtml = parsers.fail - syntax.HtmlEntity = parsers.fail - else - self.add_special_character("&") - end - - if options.preserveTabs then - options.stripIndent = false - end - - if not options.smartEllipses then - syntax.Smart = parsers.fail - else - self.add_special_character(".") - end - - if not options.relativeReferences then - syntax.AutoLinkRelativeReference = parsers.fail - end - - if options.contentLevel == "inline" then - syntax[1] = "Inlines" - syntax.Inlines = V("InitializeState") - * parsers.Inline^0 - * ( parsers.spacing^0 - * parsers.eof / "") - syntax.Space = parsers.Space + parsers.blankline / writer.space - end - - local blocks_nested_t = util.table_copy(syntax) - blocks_nested_t.ExpectedJekyllData = parsers.fail - parsers.blocks_nested = Ct(blocks_nested_t) - - parsers.blocks = Ct(syntax) - - local inlines_t = util.table_copy(syntax) - inlines_t[1] = "Inlines" - inlines_t.Inlines = V("InitializeState") - * parsers.Inline^0 - * ( parsers.spacing^0 - * parsers.eof / "") - parsers.inlines = Ct(inlines_t) - - local inlines_no_inline_note_t = util.table_copy(inlines_t) - inlines_no_inline_note_t.InlineNote = parsers.fail - parsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t) - - local inlines_no_html_t = util.table_copy(inlines_t) - inlines_no_html_t.DisplayHtml = parsers.fail - inlines_no_html_t.InlineHtml = parsers.fail - inlines_no_html_t.HtmlEntity = parsers.fail - parsers.inlines_no_html = Ct(inlines_no_html_t) - - local inlines_nbsp_t = util.table_copy(inlines_t) - inlines_nbsp_t.Endline = parsers.NonbreakingEndline - inlines_nbsp_t.Space = parsers.NonbreakingSpace - parsers.inlines_nbsp = Ct(inlines_nbsp_t) - - local inlines_no_link_or_emphasis_t = util.table_copy(inlines_t) - inlines_no_link_or_emphasis_t.LinkAndEmph = parsers.fail - inlines_no_link_or_emphasis_t.EndlineExceptions = parsers.EndlineExceptions - parsers.eof - parsers.inlines_no_link_or_emphasis = Ct(inlines_no_link_or_emphasis_t) - return function(input) - input = input:gsub("\r\n?", "\n") - if input:sub(-1) ~= "\n" then - input = input .. "\n" - end - references = {} - local opt_string = {} - for k, _ in pairs(defaultOptions) do - local v = options[k] - if type(v) == "table" then - for _, i in ipairs(v) do - opt_string[#opt_string+1] = k .. "=" .. tostring(i) - end - elseif k ~= "cacheDir" then - opt_string[#opt_string+1] = k .. "=" .. tostring(v) - end - end - table.sort(opt_string) - local salt = table.concat(opt_string, ",") .. "," .. metadata.version - local output - local function convert(input) - local document = self.parser_functions.parse_blocks(input) - local output = util.rope_to_string(writer.document(document)) - local undosep_start, undosep_end - local potential_secend_start, secend_start - local potential_sep_start, sep_start - while true do - -- find a `writer->undosep` - undosep_start, undosep_end = output:find(writer.undosep_text, 1, true) - if undosep_start == nil then break end - -- skip any preceding section ends - secend_start = undosep_start - while true do - potential_secend_start = secend_start - #writer.secend_text - if potential_secend_start < 1 - or output:sub(potential_secend_start, secend_start - 1) ~= writer.secend_text then - break - end - secend_start = potential_secend_start - end - -- find an immediately preceding block element / paragraph separator - sep_start = secend_start - potential_sep_start = sep_start - #writer.interblocksep_text - if potential_sep_start >= 1 - and output:sub(potential_sep_start, sep_start - 1) == writer.interblocksep_text then - sep_start = potential_sep_start - else - potential_sep_start = sep_start - #writer.paragraphsep_text - if potential_sep_start >= 1 - and output:sub(potential_sep_start, sep_start - 1) == writer.paragraphsep_text then - sep_start = potential_sep_start - end - end - -- remove `writer->undosep` and immediately preceding block element / paragraph separator - output = output:sub(1, sep_start - 1) - .. output:sub(secend_start, undosep_start - 1) - .. output:sub(undosep_end + 1) - end - return output - end - if options.eagerCache or options.finalizeCache then - local name = util.cache(options.cacheDir, input, salt, convert, - ".md" .. writer.suffix) - output = writer.pack(name) - else - output = convert(input) - end - if options.finalizeCache then - local file, mode - if options.frozenCacheCounter > 0 then - mode = "a" - else - mode = "w" - end - file = assert(io.open(options.frozenCacheFileName, mode), - [[Could not open file "]] .. options.frozenCacheFileName - .. [[" for writing]]) - assert(file:write([[\expandafter\global\expandafter\def\csname ]] - .. [[markdownFrozenCache]] .. options.frozenCacheCounter - .. [[\endcsname{]] .. output .. [[}]] .. "\n")) - assert(file:close()) - end - return output - end - end - return self -end -M.extensions = {} -M.extensions.bracketed_spans = function() - return { - name = "built-in bracketed_spans syntax extension", - extend_writer = function(self) - function self.span(s, attr) - if self.flatten_inlines then return s end - return {"\\markdownRendererBracketedSpanAttributeContextBegin", - self.attributes(attr), - s, - "\\markdownRendererBracketedSpanAttributeContextEnd{}"} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local span_label = parsers.lbracket - * (Cs((parsers.alphanumeric^1 - + parsers.inticks - + parsers.autolink - + V("InlineHtml") - + ( parsers.backslash * parsers.backslash) - + ( parsers.backslash * (parsers.lbracket + parsers.rbracket) - + V("Space") + V("Endline") - + (parsers.any - - (parsers.newline + parsers.lbracket + parsers.rbracket - + parsers.blankline^2))))^1) - / self.parser_functions.parse_inlines) - * parsers.rbracket - - local Span = span_label - * Ct(parsers.attributes) - / writer.span - - self.insert_pattern("Inline before LinkAndEmph", - Span, "Span") - end - } -end -M.extensions.citations = function(citation_nbsps) - return { - name = "built-in citations syntax extension", - extend_writer = function(self) - function self.citations(text_cites, cites) - local buffer = {} - if self.flatten_inlines then - for _,cite in ipairs(cites) do - if cite.prenote then - table.insert(buffer, {cite.prenote, " "}) - end - table.insert(buffer, cite.name) - if cite.postnote then - table.insert(buffer, {" ", cite.postnote}) - end - end - else - table.insert(buffer, {"\\markdownRenderer", text_cites and "TextCite" or "Cite", - "{", #cites, "}"}) - for _,cite in ipairs(cites) do - table.insert(buffer, {cite.suppress_author and "-" or "+", "{", - cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"}) - end - end - return buffer - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local citation_chars - = parsers.alphanumeric - + S("#$%&-+<>~/_") - - local citation_name - = Cs(parsers.dash^-1) * parsers.at - * Cs(citation_chars - * (((citation_chars + parsers.internal_punctuation - - parsers.comma - parsers.semicolon) - * -#((parsers.internal_punctuation - parsers.comma - - parsers.semicolon)^0 - * -(citation_chars + parsers.internal_punctuation - - parsers.comma - parsers.semicolon)))^0 - * citation_chars)^-1) - - local citation_body_prenote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + parsers.autolink - + V("InlineHtml") - + V("Space") + V("Endline") - + (parsers.anyescaped - - (parsers.newline + parsers.rbracket + parsers.blankline^2)) - - (parsers.spnl * parsers.dash^-1 * parsers.at))^1) - - local citation_body_postnote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + parsers.autolink - + V("InlineHtml") - + V("Space") + V("Endline") - + (parsers.anyescaped - - (parsers.newline + parsers.rbracket + parsers.semicolon - + parsers.blankline^2)) - - (parsers.spnl * parsers.rbracket))^1) - - local citation_body_chunk - = ( citation_body_prenote - * parsers.spnlc_sep - + Cc("") - * parsers.spnlc - ) - * citation_name - * (parsers.internal_punctuation - parsers.semicolon)^-1 - * ( parsers.spnlc - * citation_body_postnote - + Cc("") - * parsers.spnlc - ) - - local citation_body - = citation_body_chunk - * ( parsers.semicolon - * parsers.spnlc - * citation_body_chunk - )^0 - - local citation_headless_body_postnote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + parsers.autolink - + V("InlineHtml") - + V("Space") + V("Endline") - + (parsers.anyescaped - - (parsers.newline + parsers.rbracket + parsers.at - + parsers.semicolon + parsers.blankline^2)) - - (parsers.spnl * parsers.rbracket))^0) - - local citation_headless_body - = citation_headless_body_postnote - * ( parsers.semicolon - * parsers.spnlc - * citation_body_chunk - )^0 - - local citations - = function(text_cites, raw_cites) - local function normalize(str) - if str == "" then - str = nil - else - str = (citation_nbsps and - self.parser_functions.parse_inlines_nbsp or - self.parser_functions.parse_inlines)(str) - end - return str - end - - local cites = {} - for i = 1,#raw_cites,4 do - cites[#cites+1] = { - prenote = normalize(raw_cites[i]), - suppress_author = raw_cites[i+1] == "-", - name = writer.identifier(raw_cites[i+2]), - postnote = normalize(raw_cites[i+3]), - } - end - return writer.citations(text_cites, cites) - end - - local TextCitations - = Ct((parsers.spnlc - * Cc("") - * citation_name - * ((parsers.spnlc - * parsers.lbracket - * citation_headless_body - * parsers.rbracket) + Cc("")))^1) - / function(raw_cites) - return citations(true, raw_cites) - end - - local ParenthesizedCitations - = Ct((parsers.spnlc - * parsers.lbracket - * citation_body - * parsers.rbracket)^1) - / function(raw_cites) - return citations(false, raw_cites) - end - - local Citations = TextCitations + ParenthesizedCitations - - self.insert_pattern("Inline before LinkAndEmph", - Citations, "Citations") - - self.add_special_character("@") - self.add_special_character("-") - end - } -end -M.extensions.content_blocks = function(language_map) - local languages_json = (function() - local base, prev, curr - for _, pathname in ipairs{kpse.lookup(language_map, { all=true })} do - local file = io.open(pathname, "r") - if not file then goto continue end - local input = assert(file:read("*a")) - assert(file:close()) - local json = input:gsub('("[^\n]-"):','[%1]=') - curr = load("_ENV = {}; return "..json)() - if type(curr) == "table" then - if base == nil then - base = curr - else - setmetatable(prev, { __index = curr }) - end - prev = curr - end - ::continue:: - end - return base or {} - end)() - - return { - name = "built-in content_blocks syntax extension", - extend_writer = function(self) - function self.contentblock(src,suf,type,tit) - if not self.is_writing then return "" end - src = src.."."..suf - suf = suf:lower() - if type == "onlineimage" then - return {"\\markdownRendererContentBlockOnlineImage{",suf,"}", - "{",self.string(src),"}", - "{",self.uri(src),"}", - "{",self.string(tit or ""),"}"} - elseif languages_json[suf] then - return {"\\markdownRendererContentBlockCode{",suf,"}", - "{",self.string(languages_json[suf]),"}", - "{",self.string(src),"}", - "{",self.uri(src),"}", - "{",self.string(tit or ""),"}"} - else - return {"\\markdownRendererContentBlock{",suf,"}", - "{",self.string(src),"}", - "{",self.uri(src),"}", - "{",self.string(tit or ""),"}"} - end - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local contentblock_tail - = parsers.optionaltitle - * (parsers.newline + parsers.eof) - - -- case insensitive online image suffix: - local onlineimagesuffix - = (function(...) - local parser = nil - for _, suffix in ipairs({...}) do - local pattern=nil - for i=1,#suffix do - local char=suffix:sub(i,i) - char = S(char:lower()..char:upper()) - if pattern == nil then - pattern = char - else - pattern = pattern * char - end - end - if parser == nil then - parser = pattern - else - parser = parser + pattern - end - end - return parser - end)("png", "jpg", "jpeg", "gif", "tif", "tiff") - - -- online image url for iA Writer content blocks with mandatory suffix, - -- allowing nested brackets: - local onlineimageurl - = (parsers.less - * Cs((parsers.anyescaped - - parsers.more - - parsers.spacing - - #(parsers.period - * onlineimagesuffix - * parsers.more - * contentblock_tail))^0) - * parsers.period - * Cs(onlineimagesuffix) - * parsers.more - + (Cs((parsers.inparens - + (parsers.anyescaped - - parsers.spacing - - parsers.rparent - - #(parsers.period - * onlineimagesuffix - * contentblock_tail)))^0) - * parsers.period - * Cs(onlineimagesuffix)) - ) * Cc("onlineimage") - - -- filename for iA Writer content blocks with mandatory suffix: - local localfilepath - = parsers.slash - * Cs((parsers.anyescaped - - parsers.tab - - parsers.newline - - #(parsers.period - * parsers.alphanumeric^1 - * contentblock_tail))^1) - * parsers.period - * Cs(parsers.alphanumeric^1) - * Cc("localfile") - - local ContentBlock - = parsers.check_trail_no_rem - * (localfilepath + onlineimageurl) - * contentblock_tail - / writer.contentblock - - self.insert_pattern("Block before Blockquote", - ContentBlock, "ContentBlock") - end - } -end -M.extensions.definition_lists = function(tight_lists) - return { - name = "built-in definition_lists syntax extension", - extend_writer = function(self) - local function dlitem(term, defs) - local retVal = {"\\markdownRendererDlItem{",term,"}"} - for _, def in ipairs(defs) do - retVal[#retVal+1] = {"\\markdownRendererDlDefinitionBegin ",def, - "\\markdownRendererDlDefinitionEnd "} - end - retVal[#retVal+1] = "\\markdownRendererDlItemEnd " - return retVal - end - - function self.definitionlist(items,tight) - if not self.is_writing then return "" end - local buffer = {} - for _,item in ipairs(items) do - buffer[#buffer + 1] = dlitem(item.term, item.definitions) - end - if tight and tight_lists then - return {"\\markdownRendererDlBeginTight\n", buffer, - "\n\\markdownRendererDlEndTight"} - else - return {"\\markdownRendererDlBegin\n", buffer, - "\n\\markdownRendererDlEnd"} - end - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local defstartchar = S("~:") - - local defstart = parsers.check_trail_length(0) * defstartchar * #parsers.spacing - * (parsers.tab + parsers.space^-3) - + parsers.check_trail_length(1) * defstartchar * #parsers.spacing - * (parsers.tab + parsers.space^-2) - + parsers.check_trail_length(2) * defstartchar * #parsers.spacing - * (parsers.tab + parsers.space^-1) - + parsers.check_trail_length(3) * defstartchar * #parsers.spacing - - local indented_line = (parsers.check_minimal_indent / "") * parsers.check_code_trail * parsers.line - - local blank = parsers.check_minimal_blank_indent_and_any_trail * parsers.optionalspace * parsers.newline - - local dlchunk = Cs(parsers.line * (indented_line - blank)^0) - - local indented_blocks = function(bl) - return Cs( bl - * (blank^1 * (parsers.check_minimal_indent / "") - * parsers.check_code_trail * -parsers.blankline * bl)^0 - * (blank^1 + parsers.eof)) - end - - local function definition_list_item(term, defs, _) - return { term = self.parser_functions.parse_inlines(term), - definitions = defs } - end - - local DefinitionListItemLoose - = C(parsers.line) * blank^0 - * Ct((parsers.check_minimal_indent * (defstart - * indented_blocks(dlchunk) - / self.parser_functions.parse_blocks_nested))^1) - * Cc(false) / definition_list_item - - local DefinitionListItemTight - = C(parsers.line) - * Ct((parsers.check_minimal_indent * (defstart * dlchunk - / self.parser_functions.parse_blocks_nested))^1) - * Cc(true) / definition_list_item - - local DefinitionList - = ( Ct(DefinitionListItemLoose^1) * Cc(false) - + Ct(DefinitionListItemTight^1) - * (blank^0 - * -DefinitionListItemLoose * Cc(true)) - ) / writer.definitionlist - - self.insert_pattern("Block after Heading", - DefinitionList, "DefinitionList") - end - } -end -M.extensions.fancy_lists = function() - return { - name = "built-in fancy_lists syntax extension", - extend_writer = function(self) - local options = self.options - - function self.fancylist(items,tight,startnum,numstyle,numdelim) - if not self.is_writing then return "" end - local buffer = {} - local num = startnum - for _,item in ipairs(items) do - if item ~= "" then - buffer[#buffer + 1] = self.fancyitem(item,num) - end - if num ~= nil and item ~= "" then - num = num + 1 - end - end - local contents = util.intersperse(buffer,"\n") - if tight and options.tightLists then - return {"\\markdownRendererFancyOlBeginTight{", - numstyle,"}{",numdelim,"}",contents, - "\n\\markdownRendererFancyOlEndTight "} - else - return {"\\markdownRendererFancyOlBegin{", - numstyle,"}{",numdelim,"}",contents, - "\n\\markdownRendererFancyOlEnd "} - end - end - function self.fancyitem(s,num) - if num ~= nil then - return {"\\markdownRendererFancyOlItemWithNumber{",num,"}",s, - "\\markdownRendererFancyOlItemEnd "} - else - return {"\\markdownRendererFancyOlItem ",s,"\\markdownRendererFancyOlItemEnd "} - end - end - end, extend_reader = function(self) - local parsers = self.parsers - local options = self.options - local writer = self.writer - - local function combine_markers_and_delims(markers, delims) - local markers_table = {} - for _,marker in ipairs(markers) do - local start_marker - local continuation_marker - if type(marker) == "table" then - start_marker = marker[1] - continuation_marker = marker[2] - else - start_marker = marker - continuation_marker = marker - end - for _,delim in ipairs(delims) do - table.insert(markers_table, {start_marker, continuation_marker, delim}) - end - end - return markers_table - end - - local function join_table_with_func(func, markers_table) - local pattern = func(table.unpack(markers_table[1])) - for i = 2, #markers_table do - pattern = pattern + func(table.unpack(markers_table[i])) - end - return pattern - end - - local lowercase_letter_marker = R("az") - local uppercase_letter_marker = R("AZ") - - local roman_marker = function(chars) - local m, d, c = P(chars[1]), P(chars[2]), P(chars[3]) - local l, x, v, i = P(chars[4]), P(chars[5]), P(chars[6]), P(chars[7]) - return m^-3 - * (c*m + c*d + d^-1 * c^-3) - * (x*c + x*l + l^-1 * x^-3) - * (i*x + i*v + v^-1 * i^-3) - end - - local lowercase_roman_marker = roman_marker({"m", "d", "c", "l", "x", "v", "i"}) - local uppercase_roman_marker = roman_marker({"M", "D", "C", "L", "X", "V", "I"}) - - local lowercase_opening_roman_marker = P("i") - local uppercase_opening_roman_marker = P("I") - - local digit_marker = parsers.dig * parsers.dig^-8 - - local markers = { - {lowercase_opening_roman_marker, lowercase_roman_marker}, - {uppercase_opening_roman_marker, uppercase_roman_marker}, - lowercase_letter_marker, - uppercase_letter_marker, - lowercase_roman_marker, - uppercase_roman_marker, - digit_marker - } - - local delims = { - parsers.period, - parsers.rparent - } - - local markers_table = combine_markers_and_delims(markers, delims) - - local function enumerator(start_marker, _, delimiter_type, interrupting) - local delimiter_range - local allowed_end - if interrupting then - delimiter_range = P("1") - allowed_end = C(parsers.spacechar^1) * #parsers.linechar - else - delimiter_range = start_marker - allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof) - end - - return parsers.check_trail - * Ct(C(delimiter_range) * C(delimiter_type)) - * allowed_end - end - - local starter = join_table_with_func(enumerator, markers_table) - - local TightListItem = function(starter) - return parsers.add_indent(starter, "li") - * parsers.indented_content_tight - end - - local LooseListItem = function(starter) - return parsers.add_indent(starter, "li") - * parsers.indented_content_loose - * remove_indent("li") - end - - local function roman2number(roman) - local romans = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 } - local numeral = 0 - - local i = 1 - local len = string.len(roman) - while i < len do - local z1, z2 = romans[ string.sub(roman, i, i) ], romans[ string.sub(roman, i+1, i+1) ] - if z1 < z2 then - numeral = numeral + (z2 - z1) - i = i + 2 - else - numeral = numeral + z1 - i = i + 1 - end - end - if i <= len then numeral = numeral + romans[ string.sub(roman,i,i) ] end - return numeral - end - - local function sniffstyle(numstr, delimend) - local numdelim - if delimend == ")" then - numdelim = "OneParen" - elseif delimend == "." then - numdelim = "Period" - else - numdelim = "Default" - end - - local num - num = numstr:match("^([I])$") - if num then - return roman2number(num), "UpperRoman", numdelim - end - num = numstr:match("^([i])$") - if num then - return roman2number(string.upper(num)), "LowerRoman", numdelim - end - num = numstr:match("^([A-Z])$") - if num then - return string.byte(num) - string.byte("A") + 1, "UpperAlpha", numdelim - end - num = numstr:match("^([a-z])$") - if num then - return string.byte(num) - string.byte("a") + 1, "LowerAlpha", numdelim - end - num = numstr:match("^([IVXLCDM]+)") - if num then - return roman2number(num), "UpperRoman", numdelim - end - num = numstr:match("^([ivxlcdm]+)") - if num then - return roman2number(string.upper(num)), "LowerRoman", numdelim - end - return math.floor(tonumber(numstr) or 1), "Decimal", numdelim - end - - local function fancylist(items,tight,start) - local startnum, numstyle, numdelim = sniffstyle(start[2][1], start[2][2]) - return writer.fancylist(items,tight, - options.startNumber and startnum or 1, - numstyle or "Decimal", - numdelim or "Default") - end - - local FancyListOfType = function(start_marker, continuation_marker, delimiter_type) - local enumerator_start = enumerator(start_marker, continuation_marker, delimiter_type) - local enumerator_cont = enumerator(continuation_marker, continuation_marker, delimiter_type) - return Cg(enumerator_start, "listtype") - * (Ct( TightListItem(Cb("listtype")) - * ((parsers.check_minimal_indent / "") * TightListItem(enumerator_cont))^0) - * Cc(true) - * -#((parsers.conditionally_indented_blankline^0 / "") - * parsers.check_minimal_indent * enumerator_cont) - + Ct( LooseListItem(Cb("listtype")) - * ((parsers.conditionally_indented_blankline^0 / "") - * (parsers.check_minimal_indent / "") * LooseListItem(enumerator_cont))^0) - * Cc(false) - ) * Ct(Cb("listtype")) / fancylist - end - - local FancyList = join_table_with_func(FancyListOfType, markers_table) - - local Endline = parsers.newline - * (parsers.check_minimal_indent - * -parsers.EndlineExceptions - + parsers.check_optional_indent - * -parsers.EndlineExceptions - * -starter) - * parsers.spacechar^0 - / writer.soft_line_break - - self.update_rule("OrderedList", FancyList) - self.update_rule("Endline", Endline) - end - } -end -M.extensions.fenced_code = function(blank_before_code_fence, - allow_attributes, - allow_raw_blocks) - return { - name = "built-in fenced_code syntax extension", - extend_writer = function(self) - local options = self.options - - function self.fencedCode(s, i, attr) - if not self.is_writing then return "" end - s = s:gsub("\n$", "") - local buf = {} - if attr ~= nil then - table.insert(buf, {"\\markdownRendererFencedCodeAttributeContextBegin", - self.attributes(attr)}) - end - local name = util.cache_verbatim(options.cacheDir, s) - table.insert(buf, {"\\markdownRendererInputFencedCode{", - name,"}{",self.string(i),"}{",self.infostring(i),"}"}) - if attr ~= nil then - table.insert(buf, "\\markdownRendererFencedCodeAttributeContextEnd{}") - end - return buf - end - - if allow_raw_blocks then - function self.rawBlock(s, attr) - if not self.is_writing then return "" end - s = s:gsub("\n$", "") - local name = util.cache_verbatim(options.cacheDir, s) - return {"\\markdownRendererInputRawBlock{", - name,"}{", self.string(attr),"}"} - end - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local function captures_geq_length(_,i,a,b) - return #a >= #b and i - end - - local function strip_enclosing_whitespaces(str) - return str:gsub("^%s*(.-)%s*$", "%1") - end - - local tilde_infostring = Cs(Cs((V("HtmlEntity") - + parsers.anyescaped - - parsers.newline)^0) - / strip_enclosing_whitespaces) - - local backtick_infostring = Cs(Cs((V("HtmlEntity") - + (-#(parsers.backslash * parsers.backtick) * parsers.anyescaped) - - parsers.newline - - parsers.backtick)^0) - / strip_enclosing_whitespaces) - - local fenceindent - - local function has_trail(indent_table) - return indent_table ~= nil and - indent_table.trail ~= nil and - next(indent_table.trail) ~= nil - end - - local function has_indents(indent_table) - return indent_table ~= nil and - indent_table.indents ~= nil and - next(indent_table.indents) ~= nil - end - - local function get_last_indent_name(indent_table) - if has_indents(indent_table) then - return indent_table.indents[#indent_table.indents].name - end - end - - local function count_fenced_start_indent(_, _, indent_table, trail) - local last_indent_name = get_last_indent_name(indent_table) - fenceindent = 0 - if last_indent_name ~= "li" then - fenceindent = #trail - end - return true - end - - local fencehead = function(char, infostring) - return Cmt(Cb("indent_info") * parsers.check_trail, count_fenced_start_indent) - * Cg(char^3, "fencelength") - * parsers.optionalspace - * infostring - * (parsers.newline + parsers.eof) - end - - local fencetail = function(char) - return parsers.check_trail_no_rem - * Cmt(C(char^3) * Cb("fencelength"), captures_geq_length) - * parsers.optionalspace * (parsers.newline + parsers.eof) - + parsers.eof - end - - local function process_fenced_line(s, i, indent_table, line_content, is_blank) -- luacheck: ignore s i - local remainder = "" - if has_trail(indent_table) then - remainder = indent_table.trail.internal_remainder - end - - if is_blank and get_last_indent_name(indent_table) == "li" then - remainder = "" - end - - local str = remainder .. line_content - local index = 1 - local remaining = fenceindent - - while true do - local c = str:sub(index, index) - if c == " " and remaining > 0 then - remaining = remaining - 1 - index = index + 1 - elseif c == "\t" and remaining > 3 then - remaining = remaining - 4 - index = index + 1 - else - break - end - end - - return true, str:sub(index) - end - - local fencedline = function(char) - return Cmt(Cb("indent_info") * C(parsers.line - fencetail(char)) * Cc(false), process_fenced_line) - end - - local blankfencedline = Cmt(Cb("indent_info") * C(parsers.blankline) * Cc(true), process_fenced_line) - - local TildeFencedCode - = fencehead(parsers.tilde, tilde_infostring) - * Cs(((parsers.check_minimal_blank_indent / "") * blankfencedline - + (parsers.check_minimal_indent / "") * fencedline(parsers.tilde))^0) - * ((parsers.check_minimal_indent / "") * fencetail(parsers.tilde) + parsers.succeed) - - local BacktickFencedCode - = fencehead(parsers.backtick, backtick_infostring) - * Cs(((parsers.check_minimal_blank_indent / "") * blankfencedline - + (parsers.check_minimal_indent / "") * fencedline(parsers.backtick))^0) - * ((parsers.check_minimal_indent / "") * fencetail(parsers.backtick) + parsers.succeed) - - local infostring_with_attributes - = Ct(C((parsers.linechar - - ( parsers.optionalspace - * parsers.attributes))^0) - * parsers.optionalspace - * Ct(parsers.attributes)) - - local FencedCode - = ((TildeFencedCode + BacktickFencedCode) - / function(infostring, code) - local expanded_code = self.expandtabs(code) - - if allow_raw_blocks then - local raw_attr = lpeg.match(parsers.raw_attribute, - infostring) - if raw_attr then - return writer.rawBlock(expanded_code, raw_attr) - end - end - - local attr = nil - if allow_attributes then - local match = lpeg.match(infostring_with_attributes, - infostring) - if match then - infostring, attr = table.unpack(match) - end - end - return writer.fencedCode(expanded_code, infostring, attr) - end) - - self.insert_pattern("Block after Verbatim", - FencedCode, "FencedCode") - - local fencestart - if blank_before_code_fence then - fencestart = parsers.fail - else - fencestart = fencehead(parsers.backtick, backtick_infostring) - + fencehead(parsers.tilde, tilde_infostring) - end - - self.update_rule("EndlineExceptions", function(previous_pattern) - if previous_pattern == nil then - previous_pattern = parsers.EndlineExceptions - end - return previous_pattern + fencestart - end) - - self.add_special_character("`") - self.add_special_character("~") - end - } -end -M.extensions.fenced_divs = function(blank_before_div_fence) - return { - name = "built-in fenced_divs syntax extension", - extend_writer = function(self) - function self.div_begin(attributes) - local start_output = {"\\markdownRendererFencedDivAttributeContextBegin\n", - self.attributes(attributes)} - local end_output = {"\\markdownRendererFencedDivAttributeContextEnd{}"} - return self.push_attributes("div", attributes, start_output, end_output) - end - function self.div_end() - return self.pop_attributes("div") - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - local fenced_div_infostring - = C((parsers.linechar - - ( parsers.spacechar^1 - * parsers.colon^1))^1) - - local fenced_div_begin = parsers.nonindentspace - * parsers.colon^3 - * parsers.optionalspace - * fenced_div_infostring - * ( parsers.spacechar^1 - * parsers.colon^1)^0 - * parsers.optionalspace - * (parsers.newline + parsers.eof) - - local fenced_div_end = parsers.nonindentspace - * parsers.colon^3 - * parsers.optionalspace - * (parsers.newline + parsers.eof) - self.initialize_named_group("fenced_div_level", "0") - self.initialize_named_group("fenced_div_num_opening_indents") - - local function increment_div_level() - local function push_indent_table(s, i, indent_table, -- luacheck: ignore s i - fenced_div_num_opening_indents, fenced_div_level) - fenced_div_level = tonumber(fenced_div_level) + 1 - local num_opening_indents = 0 - if indent_table.indents ~= nil then - num_opening_indents = #indent_table.indents - end - fenced_div_num_opening_indents[fenced_div_level] = num_opening_indents - return true, fenced_div_num_opening_indents - end - - local function increment_level(s, i, fenced_div_level) -- luacheck: ignore s i - fenced_div_level = tonumber(fenced_div_level) + 1 - return true, tostring(fenced_div_level) - end - - return Cg( Cmt( Cb("indent_info") - * Cb("fenced_div_num_opening_indents") - * Cb("fenced_div_level"), push_indent_table) - , "fenced_div_num_opening_indents") - * Cg( Cmt( Cb("fenced_div_level"), increment_level) - , "fenced_div_level") - end - - local function decrement_div_level() - local function pop_indent_table(s, i, fenced_div_indent_table, fenced_div_level) -- luacheck: ignore s i - fenced_div_level = tonumber(fenced_div_level) - fenced_div_indent_table[fenced_div_level] = nil - return true, tostring(fenced_div_level - 1) - end - - return Cg( Cmt( Cb("fenced_div_num_opening_indents") - * Cb("fenced_div_level"), pop_indent_table) - , "fenced_div_level") - end - - local non_fenced_div_block = parsers.check_minimal_indent * V("Block") - - parsers.check_minimal_indent_and_trail * fenced_div_end - - local non_fenced_div_paragraph = parsers.check_minimal_indent * V("Paragraph") - - parsers.check_minimal_indent_and_trail * fenced_div_end - - local blank = parsers.minimally_indented_blank - - local block_separated = parsers.block_sep_group(blank) - * non_fenced_div_block - - local loop_body_pair = parsers.create_loop_body_pair(block_separated, - non_fenced_div_paragraph, - parsers.block_sep_group(blank), - parsers.par_sep_group(blank)) - - local content_loop = ( non_fenced_div_block - * loop_body_pair.block^0 - + non_fenced_div_paragraph - * block_separated - * loop_body_pair.block^0 - + non_fenced_div_paragraph - * loop_body_pair.par^0) - * blank^0 - - local FencedDiv = fenced_div_begin - / function (infostring) - local attr = lpeg.match(Ct(parsers.attributes), infostring) - if attr == nil then - attr = {"." .. infostring} - end - return attr - end - / writer.div_begin - * increment_div_level() - * parsers.skipblanklines - * Ct(content_loop) - * parsers.minimally_indented_blank^0 - * parsers.check_minimal_indent_and_trail * fenced_div_end - * decrement_div_level() - * (Cc("") / writer.div_end) - - self.insert_pattern("Block after Verbatim", - FencedDiv, "FencedDiv") - - self.add_special_character(":") - - local function is_inside_div() - local function check_div_level(s, i, fenced_div_level) -- luacheck: ignore s i - fenced_div_level = tonumber(fenced_div_level) - return fenced_div_level > 0 - end - - return Cmt(Cb("fenced_div_level"), check_div_level) - end - - local function check_indent() - local function compare_indent(s, i, indent_table, -- luacheck: ignore s i - fenced_div_num_opening_indents, fenced_div_level) - fenced_div_level = tonumber(fenced_div_level) - local num_current_indents = (indent_table.current_line_indents ~= nil and - #indent_table.current_line_indents) or 0 - local num_opening_indents = fenced_div_num_opening_indents[fenced_div_level] - return num_current_indents == num_opening_indents - end - - return Cmt( Cb("indent_info") - * Cb("fenced_div_num_opening_indents") - * Cb("fenced_div_level"), compare_indent) - end - - local fencestart = is_inside_div() - * fenced_div_end - * check_indent() - - if not blank_before_div_fence then - self.update_rule("EndlineExceptions", function(previous_pattern) - if previous_pattern == nil then - previous_pattern = parsers.EndlineExceptions - end - return previous_pattern + fencestart - end) - end - end - } -end -M.extensions.header_attributes = function() - return { - name = "built-in header_attributes syntax extension", - extend_writer = function() - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local function strip_atx_end(s) - return s:gsub("%s+#*%s*$","") - end - - local AtxHeading = Cg(parsers.heading_start, "level") - * parsers.optionalspace - * (C(((parsers.linechar - - (parsers.attributes - * parsers.optionalspace - * parsers.newline)) - * (parsers.linechar - - parsers.lbrace)^0)^1) - / strip_atx_end - / parsers.parse_heading_text) - * Cg(Ct(parsers.newline - + (parsers.attributes - * parsers.optionalspace - * parsers.newline)), "attributes") - * Cb("level") - * Cb("attributes") - / writer.heading - - local function strip_trailing_spaces(s) - return s:gsub("%s*$","") - end - - local heading_line = (parsers.linechar - - (parsers.attributes - * parsers.optionalspace - * parsers.newline))^1 - - parsers.thematic_break_lines - - local heading_text = heading_line - * ((V("Endline") / "\n") * (heading_line - parsers.heading_level))^0 - * parsers.newline^-1 - - local SetextHeading = parsers.freeze_trail * parsers.check_trail_no_rem - * #(heading_text - * (parsers.attributes - * parsers.optionalspace - * parsers.newline)^-1 - * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level) - * Cs(heading_text) / strip_trailing_spaces - / parsers.parse_heading_text - * Cg(Ct((parsers.attributes - * parsers.optionalspace - * parsers.newline)^-1), "attributes") - * parsers.check_minimal_indent_and_trail * parsers.heading_level - * Cb("attributes") - * parsers.newline - * parsers.unfreeze_trail - / writer.heading - - local Heading = AtxHeading + SetextHeading - self.update_rule("Heading", Heading) - end - } -end -M.extensions.inline_code_attributes = function() - return { - name = "built-in inline_code_attributes syntax extension", - extend_writer = function() - end, extend_reader = function(self) - local writer = self.writer - - local CodeWithAttributes = parsers.inticks - * Ct(parsers.attributes) - / writer.code - - self.insert_pattern("Inline before Code", - CodeWithAttributes, - "CodeWithAttributes") - end - } -end -M.extensions.line_blocks = function() - return { - name = "built-in line_blocks syntax extension", - extend_writer = function(self) - function self.lineblock(lines) - if not self.is_writing then return "" end - local buffer = {} - for i = 1, #lines - 1 do - buffer[#buffer + 1] = { lines[i], self.hard_line_break } - end - buffer[#buffer + 1] = lines[#lines] - - return {"\\markdownRendererLineBlockBegin\n" - ,buffer, - "\n\\markdownRendererLineBlockEnd "} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local LineBlock = Ct( - (Cs( - ( (parsers.pipe * parsers.space)/"" - * ((parsers.space)/entities.char_entity("nbsp"))^0 - * parsers.linechar^0 * (parsers.newline/"")) - * (-parsers.pipe - * (parsers.space^1/" ") - * parsers.linechar^1 - * (parsers.newline/"") - )^0 - * (parsers.blankline/"")^0 - ) / self.parser_functions.parse_inlines)^1) / writer.lineblock - - self.insert_pattern("Block after Blockquote", - LineBlock, "LineBlock") - end - } -end -M.extensions.mark = function() - return { - name = "built-in mark syntax extension", - extend_writer = function(self) - function self.mark(s) - if self.flatten_inlines then return s end - return {"\\markdownRendererMark{", s, "}"} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local doubleequals = P("==") - - local Mark = parsers.between(V("Inline"), doubleequals, doubleequals) - / function (inlines) return writer.mark(inlines) end - - self.add_special_character("=") - self.insert_pattern("Inline before LinkAndEmph", - Mark, "Mark") - end - } -end -M.extensions.link_attributes = function() - return { - name = "built-in link_attributes syntax extension", - extend_writer = function() - end, extend_reader = function(self) - local parsers = self.parsers - local options = self.options - - local define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon - * parsers.spnlc * parsers.url - * ( parsers.spnlc_sep * parsers.title * (parsers.spnlc * Ct(parsers.attributes)) - * parsers.only_blank - + parsers.spnlc_sep * parsers.title * parsers.only_blank - + Cc("") * (parsers.spnlc * Ct(parsers.attributes)) * parsers.only_blank - + Cc("") * parsers.only_blank) - - local ReferenceWithAttributes = define_reference_parser - / self.register_link - - self.update_rule("Reference", ReferenceWithAttributes) - - - local LinkWithAttributesAndEmph = Ct(parsers.link_and_emph_table * Cg(Cc(true), "match_link_attributes")) - / self.defer_link_and_emphasis_processing - - self.update_rule("LinkAndEmph", LinkWithAttributesAndEmph) - - local AutoLinkUrlWithAttributes - = parsers.auto_link_url - * Ct(parsers.attributes) - / self.auto_link_url - - self.insert_pattern("Inline before AutoLinkUrl", - AutoLinkUrlWithAttributes, - "AutoLinkUrlWithAttributes") - - local AutoLinkEmailWithAttributes - = parsers.auto_link_email - * Ct(parsers.attributes) - / self.auto_link_email - - self.insert_pattern("Inline before AutoLinkEmail", - AutoLinkEmailWithAttributes, - "AutoLinkEmailWithAttributes") - - if options.relativeReferences then - - local AutoLinkRelativeReferenceWithAttributes - = parsers.auto_link_relative_reference - * Ct(parsers.attributes) - / self.auto_link_url - - self.insert_pattern( - "Inline before AutoLinkRelativeReference", - AutoLinkRelativeReferenceWithAttributes, - "AutoLinkRelativeReferenceWithAttributes") - - end - - end - } -end -M.extensions.notes = function(notes, inline_notes) - assert(notes or inline_notes) - return { - name = "built-in notes syntax extension", - extend_writer = function(self) - function self.note(s) - if self.flatten_inlines then return "" end - return {"\\markdownRendererNote{",s,"}"} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - if inline_notes then - local InlineNote - = parsers.circumflex - * (parsers.link_label / self.parser_functions.parse_inlines_no_inline_note) - / writer.note - - self.insert_pattern("Inline after LinkAndEmph", - InlineNote, "InlineNote") - end - if notes then - local function strip_first_char(s) - return s:sub(2) - end - - local RawNoteRef - = #(parsers.lbracket * parsers.circumflex) - * parsers.link_label / strip_first_char - - local rawnotes = {} - - -- like indirect_link - local function lookup_note(ref) - return writer.defer_call(function() - local found = rawnotes[self.normalize_tag(ref)] - if found then - return writer.note( - self.parser_functions.parse_blocks_nested(found)) - else - return {"[", - self.parser_functions.parse_inlines("^" .. ref), "]"} - end - end) - end - - local function register_note(ref,rawnote) - local normalized_tag = self.normalize_tag(ref) - if rawnotes[normalized_tag] == nil then - rawnotes[normalized_tag] = rawnote - end - return "" - end - - local NoteRef = RawNoteRef / lookup_note - - local optionally_indented_line = parsers.check_optional_indent_and_any_trail * parsers.line - - local blank = parsers.check_optional_blank_indent_and_any_trail * parsers.optionalspace * parsers.newline - - local chunk = Cs(parsers.line * (optionally_indented_line - blank)^0) - - local indented_blocks = function(bl) - return Cs( bl - * (blank^1 * (parsers.check_optional_indent / "") - * parsers.check_code_trail * -parsers.blankline * bl)^0) - end - - local NoteBlock - = parsers.check_trail_no_rem * RawNoteRef * parsers.colon - * parsers.spnlc * indented_blocks(chunk) - / register_note - - local Reference = NoteBlock + parsers.Reference - - self.update_rule("Reference", Reference) - self.insert_pattern("Inline before LinkAndEmph", - NoteRef, "NoteRef") - end - - self.add_special_character("^") - end - } -end -M.extensions.pipe_tables = function(table_captions, table_attributes) - - local function make_pipe_table_rectangular(rows) - local num_columns = #rows[2] - local rectangular_rows = {} - for i = 1, #rows do - local row = rows[i] - local rectangular_row = {} - for j = 1, num_columns do - rectangular_row[j] = row[j] or "" +function util.salt(options) + local opt_string = {} + for k, _ in pairs(defaultOptions) do + local v = options[k] + if type(v) == "table" then + for _, i in ipairs(v) do + opt_string[#opt_string+1] = k .. "=" .. tostring(i) end - table.insert(rectangular_rows, rectangular_row) + elseif k ~= "cacheDir" then + opt_string[#opt_string+1] = k .. "=" .. tostring(v) end - return rectangular_rows end - - local function pipe_table_row(allow_empty_first_column - , nonempty_column - , column_separator - , column) - local row_beginning - if allow_empty_first_column then - row_beginning = -- empty first column - #(parsers.spacechar^4 - * column_separator) - * parsers.optionalspace - * column - * parsers.optionalspace - -- non-empty first column - + parsers.nonindentspace - * nonempty_column^-1 - * parsers.optionalspace - else - row_beginning = parsers.nonindentspace - * nonempty_column^-1 - * parsers.optionalspace - end - - return Ct(row_beginning - * (-- single column with no leading pipes - #(column_separator - * parsers.optionalspace - * parsers.newline) - * column_separator - * parsers.optionalspace - -- single column with leading pipes or - -- more than a single column - + (column_separator - * parsers.optionalspace - * column - * parsers.optionalspace)^1 - * (column_separator - * parsers.optionalspace)^-1)) - end - - return { - name = "built-in pipe_tables syntax extension", - extend_writer = function(self) - function self.table(rows, caption, attributes) - if not self.is_writing then return "" end - local buffer = {} - if attributes ~= nil then - table.insert(buffer, - "\\markdownRendererTableAttributeContextBegin\n") - table.insert(buffer, self.attributes(attributes)) - end - table.insert(buffer, - {"\\markdownRendererTable{", - caption or "", "}{", #rows - 1, "}{", - #rows[1], "}"}) - local temp = rows[2] -- put alignments on the first row - rows[2] = rows[1] - rows[1] = temp - for i, row in ipairs(rows) do - table.insert(buffer, "{") - for _, column in ipairs(row) do - if i > 1 then -- do not use braces for alignments - table.insert(buffer, "{") - end - table.insert(buffer, column) - if i > 1 then - table.insert(buffer, "}") - end - end - table.insert(buffer, "}") - end - if attributes ~= nil then - table.insert(buffer, - "\\markdownRendererTableAttributeContextEnd{}") - end - return buffer - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local table_hline_separator = parsers.pipe + parsers.plus - - local table_hline_column = (parsers.dash - - #(parsers.dash - * (parsers.spacechar - + table_hline_separator - + parsers.newline)))^1 - * (parsers.colon * Cc("r") - + parsers.dash * Cc("d")) - + parsers.colon - * (parsers.dash - - #(parsers.dash - * (parsers.spacechar - + table_hline_separator - + parsers.newline)))^1 - * (parsers.colon * Cc("c") - + parsers.dash * Cc("l")) - - local table_hline = pipe_table_row(false - , table_hline_column - , table_hline_separator - , table_hline_column) - - local table_caption_beginning = (parsers.check_minimal_blank_indent_and_any_trail_no_rem - * parsers.optionalspace * parsers.newline)^0 - * parsers.check_minimal_indent_and_trail - * (P("Table")^-1 * parsers.colon) - * parsers.optionalspace - - local function strip_trailing_spaces(s) - return s:gsub("%s*$","") - end - - local table_row = pipe_table_row(true - , (C((parsers.linechar - parsers.pipe)^1) - / strip_trailing_spaces - / self.parser_functions.parse_inlines) - , parsers.pipe - , (C((parsers.linechar - parsers.pipe)^0) - / strip_trailing_spaces - / self.parser_functions.parse_inlines)) - - local table_caption - if table_captions then - table_caption = #table_caption_beginning - * table_caption_beginning - if table_attributes then - table_caption = table_caption - * (C(((( parsers.linechar - - (parsers.attributes - * parsers.optionalspace - * parsers.newline - * -#( parsers.optionalspace - * parsers.linechar))) - + ( parsers.newline - * #( parsers.optionalspace - * parsers.linechar) - * C(parsers.optionalspace) / writer.space)) - * (parsers.linechar - - parsers.lbrace)^0)^1) - / self.parser_functions.parse_inlines) - * (parsers.newline - + ( Ct(parsers.attributes) - * parsers.optionalspace - * parsers.newline)) - else - table_caption = table_caption - * C(( parsers.linechar - + ( parsers.newline - * #( parsers.optionalspace - * parsers.linechar) - * C(parsers.optionalspace) / writer.space))^1) - / self.parser_functions.parse_inlines - * parsers.newline - end - else - table_caption = parsers.fail - end - - local PipeTable = Ct(table_row * parsers.newline * (parsers.check_minimal_indent_and_trail / {}) - * table_hline * parsers.newline - * ((parsers.check_minimal_indent / {}) * table_row * parsers.newline)^0) - / make_pipe_table_rectangular - * table_caption^-1 - / writer.table - - self.insert_pattern("Block after Blockquote", - PipeTable, "PipeTable") - end - } + table.sort(opt_string) + local salt = table.concat(opt_string, ",") + .. "," .. metadata.version + return salt end -M.extensions.raw_inline = function() - return { - name = "built-in raw_inline syntax extension", - extend_writer = function(self) - local options = self.options - - function self.rawInline(s, attr) - if not self.is_writing then return "" end - if self.flatten_inlines then return s end - local name = util.cache_verbatim(options.cacheDir, s) - return {"\\markdownRendererInputRawInline{", - name,"}{", self.string(attr),"}"} - end - end, extend_reader = function(self) - local writer = self.writer - - local RawInline = parsers.inticks - * parsers.raw_attribute - / writer.rawInline - - self.insert_pattern("Inline before Code", - RawInline, "RawInline") - end - } -end -M.extensions.strike_through = function() - return { - name = "built-in strike_through syntax extension", - extend_writer = function(self) - function self.strike_through(s) - if self.flatten_inlines then return s end - return {"\\markdownRendererStrikeThrough{",s,"}"} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local StrikeThrough = ( - parsers.between(parsers.Inline, parsers.doubletildes, - parsers.doubletildes) - ) / writer.strike_through - - self.insert_pattern("Inline after LinkAndEmph", - StrikeThrough, "StrikeThrough") - - self.add_special_character("~") - end - } -end -M.extensions.subscripts = function() - return { - name = "built-in subscripts syntax extension", - extend_writer = function(self) - function self.subscript(s) - if self.flatten_inlines then return s end - return {"\\markdownRendererSubscript{",s,"}"} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local Subscript = ( - parsers.between(parsers.Str, parsers.tilde, parsers.tilde) - ) / writer.subscript - - self.insert_pattern("Inline after LinkAndEmph", - Subscript, "Subscript") - - self.add_special_character("~") - end - } +local function warn(s) + io.stderr:write("Warning: " .. s .. "\n") end -M.extensions.superscripts = function() - return { - name = "built-in superscripts syntax extension", - extend_writer = function(self) - function self.superscript(s) - if self.flatten_inlines then return s end - return {"\\markdownRendererSuperscript{",s,"}"} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local Superscript = ( - parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex) - ) / writer.superscript - - self.insert_pattern("Inline after LinkAndEmph", - Superscript, "Superscript") - - self.add_special_character("^") - end - } -end -M.extensions.tex_math = function(tex_math_dollars, - tex_math_single_backslash, - tex_math_double_backslash) - return { - name = "built-in tex_math syntax extension", - extend_writer = function(self) - function self.display_math(s) - if self.flatten_inlines then return s end - return {"\\markdownRendererDisplayMath{",self.math(s),"}"} - end - function self.inline_math(s) - if self.flatten_inlines then return s end - return {"\\markdownRendererInlineMath{",self.math(s),"}"} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local function between(p, starter, ender) - return (starter * Cs(p * (p - ender)^0) * ender) - end - - local function strip_preceding_whitespaces(str) - return str:gsub("^%s*(.-)$", "%1") - end - - local allowed_before_closing = B( parsers.backslash * parsers.any - + parsers.any * (parsers.any - parsers.backslash)) - - local allowed_before_closing_no_space = B( parsers.backslash * parsers.any - + parsers.any * (parsers.nonspacechar - parsers.backslash)) - - local dollar_math_content = (parsers.newline * (parsers.check_optional_indent / "") - + parsers.backslash^-1 - * parsers.linechar) - - parsers.blankline^2 - - parsers.dollar - - local inline_math_opening_dollars = parsers.dollar - * #(parsers.nonspacechar) - - local inline_math_closing_dollars = allowed_before_closing_no_space - * parsers.dollar - * -#(parsers.digit) - - local inline_math_dollars = between(Cs( dollar_math_content), - inline_math_opening_dollars, - inline_math_closing_dollars) - - local display_math_opening_dollars = parsers.dollar - * parsers.dollar - - local display_math_closing_dollars = parsers.dollar - * parsers.dollar - - local display_math_dollars = between(Cs( dollar_math_content), - display_math_opening_dollars, - display_math_closing_dollars) - local backslash_math_content = (parsers.newline * (parsers.check_optional_indent / "") - + parsers.linechar) - - parsers.blankline^2 - local inline_math_opening_double = parsers.backslash - * parsers.backslash - * parsers.lparent - - local inline_math_closing_double = allowed_before_closing - * parsers.spacechar^0 - * parsers.backslash - * parsers.backslash - * parsers.rparent - - local inline_math_double = between(Cs( backslash_math_content), - inline_math_opening_double, - inline_math_closing_double) - / strip_preceding_whitespaces - - local display_math_opening_double = parsers.backslash - * parsers.backslash - * parsers.lbracket - - local display_math_closing_double = allowed_before_closing - * parsers.spacechar^0 - * parsers.backslash - * parsers.backslash - * parsers.rbracket - - local display_math_double = between(Cs( backslash_math_content), - display_math_opening_double, - display_math_closing_double) - / strip_preceding_whitespaces - local inline_math_opening_single = parsers.backslash - * parsers.lparent - - local inline_math_closing_single = allowed_before_closing - * parsers.spacechar^0 - * parsers.backslash - * parsers.rparent - - local inline_math_single = between(Cs( backslash_math_content), - inline_math_opening_single, - inline_math_closing_single) - / strip_preceding_whitespaces - - local display_math_opening_single = parsers.backslash - * parsers.lbracket - local display_math_closing_single = allowed_before_closing - * parsers.spacechar^0 - * parsers.backslash - * parsers.rbracket - - local display_math_single = between(Cs( backslash_math_content), - display_math_opening_single, - display_math_closing_single) - / strip_preceding_whitespaces - - local display_math = parsers.fail - - local inline_math = parsers.fail - - if tex_math_dollars then - display_math = display_math + display_math_dollars - inline_math = inline_math + inline_math_dollars - end - - if tex_math_double_backslash then - display_math = display_math + display_math_double - inline_math = inline_math + inline_math_double - end - - if tex_math_single_backslash then - display_math = display_math + display_math_single - inline_math = inline_math + inline_math_single - end - - local TexMath = display_math / writer.display_math - + inline_math / writer.inline_math - - self.insert_pattern("Inline after LinkAndEmph", - TexMath, "TexMath") - - if tex_math_dollars then - self.add_special_character("$") - end - - if tex_math_single_backslash or tex_math_double_backslash then - self.add_special_character("\\") - self.add_special_character("[") - self.add_special_character("]") - self.add_special_character(")") - self.add_special_character("(") - end - end - } -end -M.extensions.jekyll_data = function(expect_jekyll_data) - return { - name = "built-in jekyll_data syntax extension", - extend_writer = function(self) - function self.jekyllData(d, t, p) - if not self.is_writing then return "" end - - local buf = {} - - local keys = {} - for k, _ in pairs(d) do - table.insert(keys, k) - end - table.sort(keys) - - if not p then - table.insert(buf, "\\markdownRendererJekyllDataBegin") - end - - if #d > 0 then - table.insert(buf, "\\markdownRendererJekyllDataSequenceBegin{") - table.insert(buf, self.identifier(p or "null")) - table.insert(buf, "}{") - table.insert(buf, #keys) - table.insert(buf, "}") - else - table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{") - table.insert(buf, self.identifier(p or "null")) - table.insert(buf, "}{") - table.insert(buf, #keys) - table.insert(buf, "}") - end - - for _, k in ipairs(keys) do - local v = d[k] - local typ = type(v) - k = tostring(k or "null") - if typ == "table" and next(v) ~= nil then - table.insert( - buf, - self.jekyllData(v, t, k) - ) - else - k = self.identifier(k) - v = tostring(v) - if typ == "boolean" then - table.insert(buf, "\\markdownRendererJekyllDataBoolean{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, v) - table.insert(buf, "}") - elseif typ == "number" then - table.insert(buf, "\\markdownRendererJekyllDataNumber{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, v) - table.insert(buf, "}") - elseif typ == "string" then - table.insert(buf, "\\markdownRendererJekyllDataString{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, t(v)) - table.insert(buf, "}") - elseif typ == "table" then - table.insert(buf, "\\markdownRendererJekyllDataEmpty{") - table.insert(buf, k) - table.insert(buf, "}") - else - error(format("Unexpected type %s for value of " .. - "YAML key %s", typ, k)) - end - end - end - - if #d > 0 then - table.insert(buf, "\\markdownRendererJekyllDataSequenceEnd") - else - table.insert(buf, "\\markdownRendererJekyllDataMappingEnd") - end - - if not p then - table.insert(buf, "\\markdownRendererJekyllDataEnd") - end - - return buf - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local JekyllData - = Cmt( C((parsers.line - P("---") - P("..."))^0) - , function(s, i, text) -- luacheck: ignore s i - local data - local ran_ok, _ = pcall(function() - -- TODO: Replace with `require("tinyyaml")` in TeX Live 2023 - local tinyyaml = require("markdown-tinyyaml") - data = tinyyaml.parse(text, {timestamps=false}) - end) - if ran_ok and data ~= nil then - return true, writer.jekyllData(data, function(s) - return self.parser_functions.parse_blocks_nested(s) - end, nil) - else - return false - end - end - ) - - local UnexpectedJekyllData - = P("---") - * parsers.blankline / 0 - * #(-parsers.blankline) -- if followed by blank, it's thematic break - * JekyllData - * (P("---") + P("...")) - - local ExpectedJekyllData - = ( P("---") - * parsers.blankline / 0 - * #(-parsers.blankline) -- if followed by blank, it's thematic break - )^-1 - * JekyllData - * (P("---") + P("..."))^-1 - - self.insert_pattern("Block before Blockquote", - UnexpectedJekyllData, "UnexpectedJekyllData") - if expect_jekyll_data then - self.update_rule("ExpectedJekyllData", ExpectedJekyllData) - end - end - } -end function M.new(options) options = options or {} setmetatable(options, { __index = function (_, key) return defaultOptions[key] end }) - if options.singletonCache and singletonCache.convert then - for k, v in pairs(defaultOptions) do - if type(v) == "table" then - for i = 1, math.max(#singletonCache.options[k], #options[k]) do - if singletonCache.options[k][i] ~= options[k][i] then - goto miss - end - end - elseif singletonCache.options[k] ~= options[k] then - goto miss - end - end - return singletonCache.convert - end - ::miss:: - local extensions = {} - - if options.bracketedSpans then - local bracketed_spans_extension = M.extensions.bracketed_spans() - table.insert(extensions, bracketed_spans_extension) - end - - if options.contentBlocks then - local content_blocks_extension = M.extensions.content_blocks( - options.contentBlocksLanguageMap) - table.insert(extensions, content_blocks_extension) - end - - if options.definitionLists then - local definition_lists_extension = M.extensions.definition_lists( - options.tightLists) - table.insert(extensions, definition_lists_extension) - end - - if options.fencedCode then - local fenced_code_extension = M.extensions.fenced_code( - options.blankBeforeCodeFence, - options.fencedCodeAttributes, - options.rawAttribute) - table.insert(extensions, fenced_code_extension) - end - - if options.fencedDivs then - local fenced_div_extension = M.extensions.fenced_divs( - options.blankBeforeDivFence) - table.insert(extensions, fenced_div_extension) - end - - if options.headerAttributes then - local header_attributes_extension = M.extensions.header_attributes() - table.insert(extensions, header_attributes_extension) - end - - if options.inlineCodeAttributes then - local inline_code_attributes_extension = - M.extensions.inline_code_attributes() - table.insert(extensions, inline_code_attributes_extension) - end - - if options.jekyllData then - local jekyll_data_extension = M.extensions.jekyll_data( - options.expectJekyllData) - table.insert(extensions, jekyll_data_extension) - end - - if options.linkAttributes then - local link_attributes_extension = - M.extensions.link_attributes() - table.insert(extensions, link_attributes_extension) - end - - if options.lineBlocks then - local line_block_extension = M.extensions.line_blocks() - table.insert(extensions, line_block_extension) - end - - if options.mark then - local mark_extension = M.extensions.mark() - table.insert(extensions, mark_extension) - end - - if options.pipeTables then - local pipe_tables_extension = M.extensions.pipe_tables( - options.tableCaptions, options.tableAttributes) - table.insert(extensions, pipe_tables_extension) - end - - if options.rawAttribute then - local raw_inline_extension = M.extensions.raw_inline() - table.insert(extensions, raw_inline_extension) - end - - if options.strikeThrough then - local strike_through_extension = M.extensions.strike_through() - table.insert(extensions, strike_through_extension) - end - - if options.subscripts then - local subscript_extension = M.extensions.subscripts() - table.insert(extensions, subscript_extension) - end - - if options.superscripts then - local superscript_extension = M.extensions.superscripts() - table.insert(extensions, superscript_extension) - end - - if options.texMathDollars or - options.texMathSingleBackslash or - options.texMathDoubleBackslash then - local tex_math_extension = M.extensions.tex_math( - options.texMathDollars, - options.texMathSingleBackslash, - options.texMathDoubleBackslash) - table.insert(extensions, tex_math_extension) - end - - if options.notes or options.inlineNotes then - local notes_extension = M.extensions.notes( - options.notes, options.inlineNotes) - table.insert(extensions, notes_extension) - end - - if options.citations then - local citations_extension = M.extensions.citations(options.citationNbsps) - table.insert(extensions, citations_extension) - end - - if options.fancyLists then - local fancy_lists_extension = M.extensions.fancy_lists() - table.insert(extensions, fancy_lists_extension) - end - for _, user_extension_filename in ipairs(options.extensions) do - local user_extension = (function(filename) - local pathname = kpse.lookup(filename) - local input_file = assert(io.open(pathname, "r"), - [[Could not open user-defined syntax extension "]] - .. pathname .. [[" for reading]]) - local input = assert(input_file:read("*a")) - assert(input_file:close()) - local user_extension, err = load([[ - local sandbox = {} - setmetatable(sandbox, {__index = _G}) - _ENV = sandbox - ]] .. input)() - assert(user_extension, - [[Failed to compile user-defined syntax extension "]] - .. pathname .. [[": ]] .. (err or [[]])) - assert(user_extension.api_version ~= nil, - [[User-defined syntax extension "]] .. pathname - .. [[" does not specify mandatory field "api_version"]]) - assert(type(user_extension.api_version) == "number", - [[User-defined syntax extension "]] .. pathname - .. [[" specifies field "api_version" of type "]] - .. type(user_extension.api_version) - .. [[" but "number" was expected]]) - assert(user_extension.api_version > 0 - and user_extension.api_version <= metadata.user_extension_api_version, - [[User-defined syntax extension "]] .. pathname - .. [[" uses syntax extension API version "]] - .. user_extension.api_version .. [[ but markdown.lua ]] - .. metadata.version .. [[ uses API version ]] - .. metadata.user_extension_api_version - .. [[, which is incompatible]]) - - assert(user_extension.grammar_version ~= nil, - [[User-defined syntax extension "]] .. pathname - .. [[" does not specify mandatory field "grammar_version"]]) - assert(type(user_extension.grammar_version) == "number", - [[User-defined syntax extension "]] .. pathname - .. [[" specifies field "grammar_version" of type "]] - .. type(user_extension.grammar_version) - .. [[" but "number" was expected]]) - assert(user_extension.grammar_version == metadata.grammar_version, - [[User-defined syntax extension "]] .. pathname - .. [[" uses grammar version "]] .. user_extension.grammar_version - .. [[ but markdown.lua ]] .. metadata.version - .. [[ uses grammar version ]] .. metadata.grammar_version - .. [[, which is incompatible]]) - - assert(user_extension.finalize_grammar ~= nil, - [[User-defined syntax extension "]] .. pathname - .. [[" does not specify mandatory "finalize_grammar" field]]) - assert(type(user_extension.finalize_grammar) == "function", - [[User-defined syntax extension "]] .. pathname - .. [[" specifies field "finalize_grammar" of type "]] - .. type(user_extension.finalize_grammar) - .. [[" but "function" was expected]]) - local extension = { - name = [[user-defined "]] .. pathname .. [[" syntax extension]], - extend_reader = user_extension.finalize_grammar, - extend_writer = function() end, - } - return extension - end)(user_extension_filename) - table.insert(extensions, user_extension) - end - local writer = M.writer.new(options) - local reader = M.reader.new(writer, options) - local convert = reader.finalize_grammar(extensions) - collectgarbage("collect") - if options.singletonCache then - local singletonCacheOptions = {} - for k, v in pairs(options) do - singletonCacheOptions[k] = v + local parser_convert = nil + return function(input) + local function convert(input) + if parser_convert == nil then + local parser = require("markdown-parser") + if metadata.version ~= parser.metadata.version then + warn("markdown.lua " .. metadata.version .. " used with " .. + "markdown-parser.lua " .. parser.metadata.version .. ".") + end + parser_convert = parser.new(options) + end + return parser_convert(input) + end + local output + if options.eagerCache or options.finalizeCache then + local salt = util.salt(options) + local name = util.cache(options.cacheDir, input, salt, convert, + ".md.tex") + output = [[\input{]] .. name .. [[}\relax]] + else + output = convert(input) + end + if options.finalizeCache then + local file, mode + if options.frozenCacheCounter > 0 then + mode = "a" + else + mode = "w" + end + file = assert(io.open(options.frozenCacheFileName, mode), + [[Could not open file "]] .. options.frozenCacheFileName + .. [[" for writing]]) + assert(file:write( + [[\expandafter\global\expandafter\def\csname ]] + .. [[markdownFrozenCache]] .. options.frozenCacheCounter + .. [[\endcsname{]] .. output .. [[}]] .. "\n")) + assert(file:close()) end - setmetatable(singletonCacheOptions, - { __index = function (_, key) - return defaultOptions[key] end }) - singletonCache.options = singletonCacheOptions - singletonCache.convert = convert + return output end - return convert end - return M diff --git a/Master/texmf-dist/tex/luatex/nodetree/nodetree-embed.sty b/Master/texmf-dist/tex/luatex/nodetree/nodetree-embed.sty index ddff12f436d..ec7ed8bd4bb 100644 --- a/Master/texmf-dist/tex/luatex/nodetree/nodetree-embed.sty +++ b/Master/texmf-dist/tex/luatex/nodetree/nodetree-embed.sty @@ -8,7 +8,7 @@ %% %% This is a generated file. %% -%% Copyright (C) 2016-2023 by Josef Friedrich <josef@friedrich.rocks> +%% Copyright (C) 2016-2024 by Josef Friedrich <josef@friedrich.rocks> %% ---------------------------------------------------------------------- %% This work may be distributed and/or modified under the conditions of %% the LaTeX Project Public License, either version 1.3c of this license @@ -22,7 +22,7 @@ %% \NeedsTeXFormat{LaTeX2e}[1994/06/01] \ProvidesPackage{nodetree-embed} - [2023/09/10 v2.3.0 Embed node trees into a LaTeX document] + [2024/09/17 v2.4.0 Embed node trees into a LaTeX document] \RequirePackage{xcolor,mdframed,expl3,xparse,fontspec} \input{nodetree} \RequirePackage{kvoptions} diff --git a/Master/texmf-dist/tex/luatex/nodetree/nodetree.lua b/Master/texmf-dist/tex/luatex/nodetree/nodetree.lua index 18a1f204c34..82a3e294d33 100644 --- a/Master/texmf-dist/tex/luatex/nodetree/nodetree.lua +++ b/Master/texmf-dist/tex/luatex/nodetree/nodetree.lua @@ -30,7 +30,7 @@ --- be either `continue` or `stop`. if not modules then modules = {} end modules ['nodetree'] = { - version = '2.3.0', + version = '2.4.0', comment = 'nodetree', author = 'Josef Friedrich', copyright = 'Josef Friedrich', @@ -649,30 +649,37 @@ end --- Convert a Lua table into a format string. --- ----@param table table # A table to generate an inline view of. +---@param tbl table # A table to generate an inline view of. --- ---@return string -function template.table_inline(table) +function template.table_inline(tbl) local tex_escape = '' if options.channel == 'tex' then tex_escape = '\\' end - if type(table) == 'table' then - table = get_all_table_data(table) + if type(tbl) == 'table' then + tbl = get_all_table_data(tbl) local output = tex_escape .. '{' local kv_list = '' - for key, value in pairs(table) do - if type(key) ~= 'numbers' then - key = '\'' .. - template.colored_string(key, 'cyan', 'dim') .. '\'' - end - kv_list = kv_list .. '[' .. key .. '] = ' .. - template.table_inline(value) .. ', ' + local keys = {} + for key in pairs(tbl) do + keys[#keys + 1] = key + end + table.sort(keys) + for i = 1, #keys do + local key = keys[i] + local value = tbl[key] + if type(key) ~= 'numbers' then + key = '\'' .. + template.colored_string(key, 'cyan', 'dim') .. '\'' + end + kv_list = kv_list .. '[' .. key .. '] = ' .. + template.table_inline(value) .. ', ' end output = output .. kv_list:gsub(', $', '') return output .. tex_escape .. '}' else - return tostring(table) + return tostring(tbl) end end diff --git a/Master/texmf-dist/tex/luatex/nodetree/nodetree.sty b/Master/texmf-dist/tex/luatex/nodetree/nodetree.sty index a450ccdf022..f27e9c856c7 100644 --- a/Master/texmf-dist/tex/luatex/nodetree/nodetree.sty +++ b/Master/texmf-dist/tex/luatex/nodetree/nodetree.sty @@ -8,7 +8,7 @@ %% %% This is a generated file. %% -%% Copyright (C) 2016-2023 by Josef Friedrich <josef@friedrich.rocks> +%% Copyright (C) 2016-2024 by Josef Friedrich <josef@friedrich.rocks> %% ---------------------------------------------------------------------- %% This work may be distributed and/or modified under the conditions of %% the LaTeX Project Public License, either version 1.3c of this license @@ -22,7 +22,7 @@ %% \NeedsTeXFormat{LaTeX2e}[1999/12/01] \ProvidesPackage{nodetree} - [2023/09/10 v2.3.0 Visualize node lists in a tree view] + [2024/09/17 v2.4.0 Visualize node lists in a tree view] \input{nodetree} \RequirePackage{kvoptions} \SetupKeyvalOptions{ diff --git a/Master/texmf-dist/tex/luatex/nodetree/nodetree.tex b/Master/texmf-dist/tex/luatex/nodetree/nodetree.tex index 919ea275f7a..180bb1fb7c4 100644 --- a/Master/texmf-dist/tex/luatex/nodetree/nodetree.tex +++ b/Master/texmf-dist/tex/luatex/nodetree/nodetree.tex @@ -8,7 +8,7 @@ %% %% This is a generated file. %% -%% Copyright (C) 2016-2023 by Josef Friedrich <josef@friedrich.rocks> +%% Copyright (C) 2016-2024 by Josef Friedrich <josef@friedrich.rocks> %% ---------------------------------------------------------------------- %% This work may be distributed and/or modified under the conditions of %% the LaTeX Project Public License, either version 1.3c of this license diff --git a/Master/texmf-dist/tex/luatex/penlight/penlight.lua b/Master/texmf-dist/tex/luatex/penlight/penlight.lua index 6ffcaf49b70..e104886a83f 100644 --- a/Master/texmf-dist/tex/luatex/penlight/penlight.lua +++ b/Master/texmf-dist/tex/luatex/penlight/penlight.lua @@ -275,7 +275,7 @@ function Doc:reset() end ---- Append a child to the currrent Node (ignoring current position). +--- Append a child to the current Node (ignoring current position). -- @param child a child node (either text or a document) -- @return the current node (`self`) -- @usage @@ -311,7 +311,7 @@ end --- Set attributes of a document node. --- Will add/overwite values, but will not remove existing ones. +-- Will add/overwrite values, but will not remove existing ones. -- Operates on the Node itself, will not take position into account. -- @tparam table t a table containing attribute/value pairs -- @return the current node (`self`) @@ -447,7 +447,7 @@ do -- @param self document node to traverse -- @param tag tag-name to look for -- @param list array table to add the matching ones to - -- @param recurse if truthy, recursivly search the node + -- @param recurse if truthy, recursively search the node local function _children_with_name(self, tag, list, recurse) -- TODO: protect against recursion for _, child in ipairs(self) do @@ -942,7 +942,7 @@ local html_empty_elements = { --lists all HTML empty (void) elements } --- Parse a well-formed HTML file as a string. --- Tags are case-insenstive, DOCTYPE is ignored, and empty elements can be .. empty. +-- Tags are case-insensitive, DOCTYPE is ignored, and empty elements can be .. empty. -- @param s the HTML function _M.parsehtml(s) return _M.basic_parse(s,false,true) @@ -1335,7 +1335,7 @@ end -- Multiple short args can be combined like so: ( `-abcd`). -- -- When specifying the `flags_valid` parameter, its contents can also contain --- aliasses, to convert short/long flags to the same output name. See the +-- aliases, to convert short/long flags to the same output name. See the -- example below. -- -- Note: if a flag is repeated, the last value wins. @@ -1354,7 +1354,7 @@ end -- { "hello", "world" }, -- list of flags taking values -- { "l", "a", "b"}) -- list of allowed flags (value ones will be added) -- --- -- More complex example using aliasses: +-- -- More complex example using aliases: -- local valid = { -- long = "l", -- if 'l' is specified, it is reported as 'long' -- new = { "n", "old" }, -- here both 'n' and 'old' will go into 'new' @@ -1397,16 +1397,16 @@ function app.parse_args (args,flags_with_values, flags_valid) valid = setmetatable({},{ __index = function(_, key) return key end }) else valid = {} - for k,aliasses in pairs(flags_valid) do + for k,aliases in pairs(flags_valid) do if type(k) == "number" then -- array/list entry - k = aliasses + k = aliases end - if type(aliasses) == "string" then -- single alias - aliasses = { aliasses } + if type(aliases) == "string" then -- single alias + aliases = { aliases } end - if type(aliasses) == "table" then -- list of aliasses + if type(aliases) == "table" then -- list of aliases -- it's the alternate name, so add the proper mappings - for i, alias in ipairs(aliasses) do + for i, alias in ipairs(aliases) do valid[alias] = k end end @@ -1427,7 +1427,7 @@ function app.parse_args (args,flags_with_values, flags_valid) end -- now check that all flags with values are reported as such under all - -- of their aliasses + -- of their aliases for k, main_alias in pairs(valid) do if with_values[main_alias] then with_values[k] = true @@ -1696,7 +1696,7 @@ function array2d.flatten (t) return makelist(res) end ---- reshape a 2D array. Reshape the aray by specifying a new nr of rows. +--- reshape a 2D array. Reshape the array by specifying a new nr of rows. -- @array2d t 2d array -- @int nrows new number of rows -- @bool co use column-order (Fortran-style) (default false) @@ -1773,7 +1773,7 @@ end --- extract the specified columns. -- @array2d t 2d array -- @tparam {int} cidx a table of column indices --- @return a new 2d array with the extracted colums +-- @return a new 2d array with the extracted columns function array2d.extract_cols (t,cidx) assert_arg(1,t,'table') local res = {} @@ -2097,7 +2097,7 @@ end, -------------------- -- Module: 'pl.class' -------------------- ---- Provides a reuseable and convenient framework for creating classes in Lua. +--- Provides a reusable and convenient framework for creating classes in Lua. -- Two possible notations: -- -- B = class(A) @@ -2124,7 +2124,7 @@ local function call_ctor (c,obj,...) init = rawget(parent_with_init, '_init') parent_with_init = rawget(parent_with_init, '_parent_with_init') end - if parent_with_init then -- super() points to one above whereever _init came from + if parent_with_init then -- super() points to one above wherever _init came from rawset(obj,'super',function(obj,...) call_ctor(parent_with_init,obj,...) end) @@ -2970,9 +2970,9 @@ end local config = {} ---- like `io.lines`, but allows for lines to be continued with '`\`'. +--- like `io.lines`, but allows for lines to be continued with '`\\`'. -- @param file a file-like object (anything where read() returns the next line) or a filename. --- Defaults to stardard input. +-- Defaults to standard input. -- @return an iterator over the lines, or nil -- @return error 'not a file-like object' or 'file is nil' function config.lines(file) @@ -3353,7 +3353,7 @@ function data.read(file,cnfg) -- note that using dot as the thousands separator (@thousands_dot) -- requires a special conversion function! For CSV, _empty fields_ are - -- considered to default to numerial zeroes. + -- considered to default to numerical zeroes. local tonumber = tonumber local function try_number(x) if thousands_dot then x = x:gsub('%.(...)','%1') end @@ -5327,7 +5327,7 @@ function repr (e,lastpred) end func.repr = repr --- collect all the non-PE values in this PE into vlist, and replace each occurence +-- collect all the non-PE values in this PE into vlist, and replace each occurrence -- with a constant PH (_C1, etc). Return the maximum placeholder index found. local collect_values function collect_values (e,vlist) @@ -5411,13 +5411,20 @@ utils.add_function_factory(_PEMT,func.I) func.bind1 = utils.bind1 func.curry = func.bind1 ---- create a function which chains two functions. +--- create a function which chains multiple functions. -- @func f a function of at least one argument -- @func g a function of at least one argument +-- @param ... additional functions to compose -- @return a function --- @usage printf = compose(io.write,string.format) -function func.compose (f,g) - return function(...) return f(g(...)) end +-- @usage printf = compose(io.write, string.format) +-- @usage printf = compose(io.write, string.lower, string.format) +function func.compose (...) + local args = pack(...) + return tablex.reduce(function(f, g) + return function(...) + return f(g(...)) + end + end, args) end --- bind the arguments of a function to given values. @@ -5475,7 +5482,7 @@ end, -------------------- -------------- -- PL loader, for loading all PL libraries, only on demand. --- Whenever a module is implicitly accesssed, the table will have the module automatically injected. +-- Whenever a module is implicitly accessed, the table will have the module automatically injected. -- (e.g. `_ENV.tablex`) -- then that module is dynamically loaded. The submodules are all brought into -- the table that is provided as the argument, or returned in a new table. @@ -5573,7 +5580,7 @@ end, -------------------- -------------- -- Entry point for loading all PL libraries only on demand, into the global space. --- Requiring 'pl' means that whenever a module is implicitly accesssed +-- Requiring 'pl' means that whenever a module is implicitly accessed -- (e.g. `utils.split`) -- then that module is dynamically loaded. The submodules are all brought into -- the global space. @@ -6872,7 +6879,7 @@ function List:insert(i, x) return self end ---- Insert an item at the begining of the list. +--- Insert an item at the beginning of the list. -- @param x a data item -- @return the list function List:put (x) @@ -7282,7 +7289,7 @@ function List:iter () return iter(self) end ---- Create an iterator over a seqence. +--- Create an iterator over a sequence. -- This captures the Python concept of 'sequence'. -- For tables, iterates over all values with integer indices. -- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function @@ -7637,10 +7644,14 @@ local function makelist(t) return setmetatable(t, require('pl.List')) end ---- list of keys. +--- return a List of all keys. +-- @class function +-- @name Map:keys Map.keys = tablex.keys ---- list of values. +--- return a List of all values. +-- @class function +-- @name Map:values Map.values = tablex.values --- return an iterator over all key-value pairs. @@ -7648,7 +7659,7 @@ function Map:iter () return pairs(self) end ---- return a List of all key-value pairs, sorted by the keys. +--- return a List of all key-value pairs, sorted by the keys in ascending order. function Map:items() local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self)) ls:sort(function(t1,t2) return t1[1] < t2[1] end) @@ -8223,12 +8234,12 @@ local function err_func(name, param, err, code) end --- Lua iterator over the entries of a given directory. --- Implicit link to [`luafilesystem.dir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- Implicit link to [`luafilesystem.dir`](https://lunarmodules.github.io/luafilesystem/manual.html#dir) -- @function dir path.dir = lfs.dir --- Creates a directory. --- Implicit link to [`luafilesystem.mkdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- Implicit link to [`luafilesystem.mkdir`](https://lunarmodules.github.io/luafilesystem/manual.html#mkdir) -- @function mkdir path.mkdir = function(d) local ok, err, code = lfs.mkdir(d) @@ -8239,7 +8250,7 @@ path.mkdir = function(d) end --- Removes a directory. --- Implicit link to [`luafilesystem.rmdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- Implicit link to [`luafilesystem.rmdir`](https://lunarmodules.github.io/luafilesystem/manual.html#rmdir) -- @function rmdir path.rmdir = function(d) local ok, err, code = lfs.rmdir(d) @@ -8250,7 +8261,7 @@ path.rmdir = function(d) end --- Gets attributes. --- Implicit link to [`luafilesystem.attributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- Implicit link to [`luafilesystem.attributes`](https://lunarmodules.github.io/luafilesystem/manual.html#attributes) -- @function attrib path.attrib = function(d, r) local ok, err, code = attrib(d, r) @@ -8261,7 +8272,7 @@ path.attrib = function(d, r) end --- Get the working directory. --- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- Implicit link to [`luafilesystem.currentdir`](https://lunarmodules.github.io/luafilesystem/manual.html#currentdir) -- @function currentdir path.currentdir = function() local ok, err, code = currentdir() @@ -8272,7 +8283,7 @@ path.currentdir = function() end --- Gets symlink attributes. --- Implicit link to [`luafilesystem.symlinkattributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- Implicit link to [`luafilesystem.symlinkattributes`](https://lunarmodules.github.io/luafilesystem/manual.html#symlinkattributes) -- @function link_attrib path.link_attrib = function(d, r) local ok, err, code = link_attrib(d, r) @@ -8285,7 +8296,7 @@ end --- Changes the working directory. -- On Windows, if a drive is specified, it also changes the current drive. If -- only specifying the drive, it will only switch drive, but not modify the path. --- Implicit link to [`luafilesystem.chdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) +-- Implicit link to [`luafilesystem.chdir`](https://lunarmodules.github.io/luafilesystem/manual.html#chdir) -- @function chdir path.chdir = function(d) local ok, err, code = lfs.chdir(d) @@ -8329,7 +8340,7 @@ end --- does a path exist? -- @string P A file path --- @return the file path if it exists (either as file, directory, socket, etc), nil otherwise +-- @return the file path if it exists (either as file, directory, socket, etc), false otherwise function path.exists(P) assert_string(1,P) return attrib(P,'mode') ~= nil and P @@ -8426,8 +8437,8 @@ end -- @string[opt] pwd optional start path to use (default is current dir) function path.abspath(P,pwd) assert_string(1,P) - if pwd then assert_string(2,pwd) end local use_pwd = pwd ~= nil + if use_pwd then assert_string(2,pwd) end if not use_pwd and not currentdir() then return P end P = P:gsub('[\\/]$','') pwd = pwd or currentdir() @@ -8671,17 +8682,35 @@ end -- In windows, if HOME isn't set, then USERPROFILE is used in preference to -- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows. -- @string P A file path +-- @treturn[1] string The file path with the `~` prefix substituted, or the input path if it had no prefix. +-- @treturn[2] nil +-- @treturn[2] string Error message if the environment variables were unavailable. function path.expanduser(P) assert_string(1,P) - if at(P,1) == '~' then - local home = getenv('HOME') - if not home then -- has to be Windows - home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH') - end - return home..sub(P,2) - else + if P:sub(1,1) ~= '~' then return P end + + local home = getenv('HOME') + if (not home) and (not path.is_windows) then + -- no more options to try on Nix + return nil, "failed to expand '~' (HOME not set)" + end + + if (not home) then + -- try alternatives on Windows + home = getenv 'USERPROFILE' + if not home then + local hd = getenv 'HOMEDRIVE' + local hp = getenv 'HOMEPATH' + if not (hd and hp) then + return nil, "failed to expand '~' (HOME, USERPROFILE, and HOMEDRIVE and/or HOMEPATH not set)" + end + home = hd..hp + end + end + + return home..sub(P,2) end @@ -8839,7 +8868,7 @@ end --- an iterator over all permutations of the elements of the given lists. -- @param ... list-like tables, they are nil-safe if a length-field `n` is provided (see `utils.pack`) --- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceeded by an index +-- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceded by an index -- @usage -- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety -- local bools = utils.pack(true, false) @@ -8977,6 +9006,42 @@ local assert_arg = utils.assert_arg local original_tostring = tostring +-- Calculate min and max integer supported by lua_Number +-- Assumptions: +-- 1. max_int = 2 ^ n - 1 +-- 2. min_int = -max_int +-- 3. if n > max_int versions with integer support will have +-- integer overflow and versions without integers will lose least significant bit +-- Note: if lua_Integer is smaller than lua_Number mantissa string.format('%d') +-- can throw runtime error +local max_int, min_int +local next_cand = 1 +while next_cand > 0 and next_cand % 2 == 1 do + max_int = next_cand + min_int = -next_cand + next_cand = next_cand * 2 + 1 +end + +local function is_integer(value) + if _VERSION == "Lua 5.3" or _VERSION == "Lua 5.4" then + return mtype(value) == "integer" + end + if value < min_int or value > max_int then + return false + end + return math.floor(value) == value +end + +local function is_float(value) + if _VERSION == "Lua 5.3" or _VERSION == "Lua 5.4" then + return mtype(value) == "float" + end + if value < min_int or value > max_int then + return true + end + return mfloor(value) == value +end + -- Patch tostring to format numbers with better precision -- and to produce cross-platform results for -- infinite values and NaN. @@ -8989,11 +9054,11 @@ local function tostring(value) return "Inf" elseif value == -mhuge then return "-Inf" - elseif (_VERSION ~= "Lua 5.3" or mtype(value) == "integer") and mfloor(value) == value then + elseif is_integer(value) then return ("%d"):format(value) else local res = ("%.14g"):format(value) - if _VERSION == "Lua 5.3" and mtype(value) == "float" and not res:find("%.") then + if is_float(value) and not res:find("%.") then -- Number is internally a float but looks like an integer. -- Insert ".0" after first run of digits. res = res:gsub("%d+", "%0.0", 1) @@ -11289,7 +11354,7 @@ local function _partition(p,delim,fn) end end ---- partition the string using first occurance of a delimiter +--- partition the string using first occurrence of a delimiter -- @string s the string -- @string ch delimiter (match as plain string, no patterns) -- @return part before ch @@ -11303,7 +11368,7 @@ function stringx.partition(s,ch) return _partition(s,ch,stringx.lfind) end ---- partition the string p using last occurance of a delimiter +--- partition the string p using last occurrence of a delimiter -- @string s the string -- @string ch delimiter (match as plain string, no patterns) -- @return part before ch @@ -11575,7 +11640,7 @@ end ---- Miscelaneous +--- Miscellaneous -- @section misc --- return an iterator over all lines in a string @@ -11593,7 +11658,7 @@ function stringx.lines(s) return s:gmatch('([^\n]*)\n') end ---- inital word letters uppercase ('title case'). +--- initial word letters uppercase ('title case'). -- Here 'words' mean chunks of non-space characters. -- @string s the string -- @return a string with each word's first letter uppercase @@ -11614,7 +11679,7 @@ do --- Return a shortened version of a string. -- Fits string within w characters. Removed characters are marked with ellipsis. -- @string s the string - -- @int w the maxinum size allowed + -- @int w the maximum size allowed -- @bool tail true if we want to show the end of the string (head otherwise) -- @usage ('1234567890'):shorten(8) == '12345...' -- @usage ('1234567890'):shorten(8, true) == '...67890' @@ -11688,7 +11753,7 @@ end --- Python-style formatting operator. -- Calling `text.format_operator()` overloads the % operator for strings to give --- Python/Ruby style formated output. +-- Python/Ruby style formatted output. -- This is extended to also do template-like substitution for map-like data. -- -- Note this goes further than the original, and will allow these cases: @@ -11698,7 +11763,7 @@ end -- 3. a map of var=value pairs -- 4. a function, as in gsub -- --- For the second two cases, it uses $-variable substituion. +-- For the second two cases, it uses $-variable substitution. -- -- When called, this function will monkey-patch the global `string` metatable by -- adding a `__mod` method. @@ -12799,59 +12864,72 @@ end, local utils = require 'pl.utils' -local append,format,strsub,strfind,strgsub = table.insert,string.format,string.sub,string.find,string.gsub +local append, concat = table.insert, table.concat +local format, strsub, strfind, strgsub, strrep = string.format, string.sub, string.find, string.gsub, string.rep -local APPENDER = "\n__R_size = __R_size + 1; __R_table[__R_size] = " +local APPENDER = " __R_size = __R_size + 1; __R_table[__R_size] = " +-- When this function returns, `pieces` is guaranteed to hold a complete Lua +-- statement, meaning that new statements can be appended without creating +-- invalid Lua code. local function parseDollarParen(pieces, chunk, exec_pat, newline) local s = 1 for term, executed, e in chunk:gmatch(exec_pat) do - executed = '('..strsub(executed,2,-2)..')' - append(pieces, APPENDER..format("%q", strsub(chunk,s, term - 1))) - append(pieces, APPENDER..format("__tostring(%s or '')", executed)) + executed = '(' .. strsub(executed, 2, -2) .. ')' + append(pieces, APPENDER .. format("%q;", strsub(chunk, s, term - 1))) + append(pieces, APPENDER .. format("__tostring(%s or '');", executed)) s = e end - local r + local remainder, newlines_removed if newline then - r = format("%q", strgsub(strsub(chunk,s),"\n","")) + remainder, newlines_removed = strgsub(strsub(chunk, s), "\n", "") else - r = format("%q", strsub(chunk,s)) + remainder, newlines_removed = strsub(chunk, s), 0 end - if r ~= '""' then - append(pieces, APPENDER..r) + if remainder ~= "" then + append(pieces, APPENDER .. format("%q;", remainder)) + end + if newlines_removed > 0 then + append(pieces, strrep("\n", newlines_removed)) end end -local function parseHashLines(chunk,inline_escape,brackets,esc,newline) - local exec_pat = "()"..inline_escape.."(%b"..brackets..")()" +local function parseHashLines(chunk, inline_escape, brackets, esc, newline) + -- Escape special characters to avoid invalid expressions + inline_escape = utils.escape(inline_escape) + esc = utils.escape(esc) + + local exec_pat = "()" .. inline_escape .. "(%b" .. brackets .. ")()" - local esc_pat = esc.."+([^\n]*\n?)" - local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat - local pieces, s = {"return function()\nlocal __R_size, __R_table, __tostring = 0, {}, __tostring", n = 1}, 1 + local esc_pat = esc .. "+([^\n]*\n?)" + local esc_pat1, esc_pat2 = "^" .. esc_pat, "\n" .. esc_pat + local pieces, s = {"return function() local __R_size, __R_table, __tostring = 0, {}, __tostring; "}, 1 while true do - local _, e, lua = strfind(chunk,esc_pat1, s) + local _, e, lua = strfind(chunk, esc_pat1, s) if not e then local ss - ss, e, lua = strfind(chunk,esc_pat2, s) - parseDollarParen(pieces, strsub(chunk,s, ss), exec_pat, newline) + ss, e, lua = strfind(chunk, esc_pat2, s) + parseDollarParen(pieces, strsub(chunk, s, ss), exec_pat, newline) if not e then break end end - if strsub(lua, -1, -1) == "\n" then lua = strsub(lua, 1, -2) end - append(pieces, "\n"..lua) + if strsub(lua, -1, -1) ~= "\n" then lua = lua .. "\n" end -- Ensure trailing newline + append(pieces, lua) + -- since `lua` ends with a newline, there is no danger of subsequent + -- statements being gobbled up by comments or being altered s = e + 1 end - append(pieces, "\nreturn __R_table\nend") + append(pieces, "return __R_table; end") -- let's check for a special case where there is nothing to template, but it's -- just a single static string local short = false - if (#pieces == 3) and (pieces[2]:find(APPENDER, 1, true) == 1) then - pieces = { "return " .. pieces[2]:sub(#APPENDER+1,-1) } + if (#pieces == 3) and (strfind(pieces[2], APPENDER, 1, true) == 1) then + pieces = { "return " .. strsub(pieces[2], #APPENDER + 1, -1) } short = true end -- if short == true, the generated function will not return a table of strings, -- but a single string - return table.concat(pieces), short + return concat(pieces), short end local template = {} @@ -12859,33 +12937,34 @@ local template = {} --- expand the template using the specified environment. -- This function will compile and render the template. For more performant -- recurring usage use the two step approach by using `compile` and `ct:render`. --- There are six special fields in the environment table `env` --- --- * `_parent`: continue looking up in this table (e.g. `_parent=_G`). --- * `_brackets`: bracket pair that wraps inline Lua expressions, default is '()'. --- * `_escape`: character marking Lua lines, default is '#' --- * `_inline_escape`: character marking inline Lua expression, default is '$'. --- * `_chunk_name`: chunk name for loaded templates, used if there --- is an error in Lua code. Default is 'TMP'. --- * `_debug`: if truthy, the generated code will be printed upon a render error --- -- @string str the template string --- @tab[opt] env the environment --- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last --- return value (`source_code`) is only returned if the debug option is used. -function template.substitute(str,env) +-- @tparam[opt] table env the environment. This table has the following special fields: +-- @tparam[opt=nil] table env._parent continue looking up in this table (e.g. `_parent=_G`). +-- @tparam[opt="()"] string env._brackets bracket pair that wraps inline Lua expressions. +-- @tparam[opt="#"] string env._escape character marking Lua lines. +-- @tparam[opt="$"] string env._inline_escape character marking inline Lua expression. +-- @tparam[opt="TMP"] string env._chunk_name chunk name for loaded templates, used if there +-- is an error in Lua code. +-- @tparam[opt=false] boolean env._debug if truthy, the generated code will be printed upon a render error. +-- @treturn[1] string render result +-- @treturn[1] nil +-- @treturn[1] string source_code (only if '`env._debug`' was truthy). +-- @treturn[2] nil +-- @treturn[2] string error message +-- @treturn[2] string source_code (only if '`env._debug`' was truthy). +function template.substitute(str, env) env = env or {} local t, err = template.compile(str, { - chunk_name = rawget(env,"_chunk_name"), - escape = rawget(env,"_escape"), - inline_escape = rawget(env,"_inline_escape"), - inline_brackets = rawget(env,"_brackets"), - newline = nil, - debug = rawget(env,"_debug") + chunk_name = rawget(env, "_chunk_name"), + escape = rawget(env, "_escape"), + inline_escape = rawget(env, "_inline_escape"), + inline_brackets = rawget(env, "_brackets"), + newline = false, + debug = rawget(env, "_debug") }) if not t then return t, err end - return t:render(env, rawget(env,"_parent"), rawget(env,"_debug")) + return t:render(env, rawget(env, "_parent"), rawget(env, "_debug")) end --- executes the previously compiled template and renders it. @@ -12894,12 +12973,16 @@ end -- @tab[opt] parent continue looking up in this table (e.g. `parent=_G`). -- @bool[opt] db if thruthy, it will print the code upon a render error -- (provided the template was compiled with the debug option). --- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last return value --- (`source_code`) is only returned if the template was compiled with the debug option. +-- @treturn[1] string render result +-- @treturn[1] nil +-- @treturn[1] string source_code (only if '`env._debug`' was truthy). +-- @treturn[2] nil +-- @treturn[2] string error message +-- @treturn[2] string source_code (only if '`env._debug`' was truthy). -- @usage -- local ct, err = template.compile(my_template) -- local rendered , err = ct:render(my_env, parent) -local render = function(self, env, parent, db) +local function render(self, env, parent, db) env = env or {} if parent then -- parent is a bit silly, but for backward compatibility retained setmetatable(env, {__index = parent}) @@ -12911,25 +12994,27 @@ local render = function(self, env, parent, db) if self.code and db then print(self.code) end return nil, out, self.code end - return table.concat(out), nil, self.code + return concat(out), nil, self.code end --- compiles the template. -- Returns an object that can repeatedly be rendered without parsing/compiling --- the template again. --- The options passed in the `opts` table support the following options: --- --- * `chunk_name`: chunk name for loaded templates, used if there --- is an error in Lua code. Default is 'TMP'. --- * `escape`: character marking Lua lines, default is '#' --- * `inline_escape`: character marking inline Lua expression, default is '$'. --- * `inline_brackets`: bracket pair that wraps inline Lua expressions, default is '()'. --- * `newline`: string to replace newline characters, default is `nil` (not replacing newlines). --- * `debug`: if truthy, the generated source code will be retained within the compiled template object, default is `nil`. --- --- @string str the template string --- @tab[opt] opts the compilation options to use --- @return template object, or `nil + error + source_code` +-- the template again. Preserves the line layout of the template so that line +-- numbers in error messages should point to the correct lines in the source +-- string. +-- @tparam string str the template string +-- @tparam[opt] table opts the compilation options to use. This table supports the following options: +-- @tparam[opt="TMP"] string opts.chunk_name chunk name for loaded templates, used if there +-- is an error in Lua code. +-- @tparam[opt="#"] string opts.escape character marking Lua lines. +-- @tparam[opt="$"] string opts.inline_escape character marking inline Lua expression. +-- @tparam[opt="()"] string opts.inline_brackets bracket pair that wraps inline Lua expressions. +-- @tparam[opt=false] boolean opts.newline if truthy, newlines will be stripped from text in the template. +-- @tparam[opt=false] boolean opts.debug if truthy, the generated code will be printed upon a render error. +-- @treturn[1] ct compiled template object +-- @treturn[2] nil +-- @treturn[2] string error message +-- @treturn[2] string source_code -- @usage -- local ct, err = template.compile(my_template) -- local rendered , err = ct:render(my_env, parent) @@ -12940,9 +13025,9 @@ function template.compile(str, opts) local inline_escape = opts.inline_escape or '$' local inline_brackets = opts.inline_brackets or '()' - local code, short = parseHashLines(str,inline_escape,inline_brackets,escape,opts.newline) + local code, short = parseHashLines(str, inline_escape, inline_brackets, escape, opts.newline) local env = { __tostring = tostring } - local fn, err = utils.load(code, chunk_name,'t',env) + local fn, err = utils.load(code, chunk_name, 't', env) if not fn then return nil, err, code end if short then @@ -13453,7 +13538,8 @@ local operators local _function_factories = {} -local utils = { _VERSION = "1.13.1" } +local utils = { _VERSION = "1.14.0" } + for k, v in pairs(compat) do utils[k] = v end --- Some standard patterns @@ -13571,7 +13657,11 @@ end -- @param value1 Value returned if cond is truthy -- @param value2 Value returned if cond is falsy function utils.choose(cond, value1, value2) - return cond and value1 or value2 + if cond then + return value1 + else + return value2 + end end --- convert an array of values to strings. @@ -13727,7 +13817,7 @@ end -- non-existing values, and/or converting strings/identifiers to other values. -- -- Calling on the object does the same, but returns a soft error; `nil + err`, if --- the call is succesful (the key exists), it will return the value. +-- the call is successful (the key exists), it will return the value. -- -- When calling with varargs or an array the values will be equal to the keys. -- The enum object is read-only. @@ -13755,7 +13845,7 @@ end -- }, -- -- some_method = function(self) --- return self.ERR.OUT_OF_BOUNDS +-- return nil, self.ERR.OUT_OF_BOUNDS -- end, -- } -- @@ -14011,6 +14101,11 @@ end --- OS functions -- @section OS-functions +--- Execute a shell command. +-- This function is a copy of `compat.execute`. +-- @class function +-- @name utils.execute + --- execute a shell command and return the output. -- This function redirects the output to tempfiles and returns the content of those files. -- @param cmd a shell command @@ -14121,7 +14216,7 @@ end -- @param s The input string -- @param re optional A Lua string pattern; defaults to '%s+' -- @param plain optional If truthy don't use Lua patterns --- @param n optional maximum number of elements (if there are more, the last will remian un-split) +-- @param n optional maximum number of elements (if there are more, the last will remain un-split) -- @return a list-like table -- @raise error if s is not a string -- @see splitv @@ -14175,8 +14270,8 @@ end -- This is useful if you have a function which is relatively expensive, -- but you don't know in advance what values will be required, so -- building a table upfront is wasteful/impossible. --- @param func a function of at least one argument --- @return a function with at least one argument, which is used as the key. +-- @param func a function that takes exactly one argument (which later serves as the cache key) and returns a single value +-- @return a function taking one argument and returning a single value either from the cache or by running the original input function function utils.memoize(func) local cache = {} return function(k) diff --git a/Master/texmf-dist/tex/luatex/penlight/penlight.sty b/Master/texmf-dist/tex/luatex/penlight/penlight.sty index 7a7c94b6a77..8a0bb2c8f00 100644 --- a/Master/texmf-dist/tex/luatex/penlight/penlight.sty +++ b/Master/texmf-dist/tex/luatex/penlight/penlight.sty @@ -1,6 +1,6 @@ % Kale Ewasiuk (kalekje@gmail.com) -% 2023-07-22 -% Copyright (C) 2021-2023 Kale Ewasiuk +% 2024-09-15 +% Copyright (C) 2021-2024 Kale Ewasiuk % % Permission is hereby granted, free of charge, to any person obtaining a copy % of this software and associated documentation files (the "Software"), to deal @@ -22,7 +22,7 @@ % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE % OR OTHER DEALINGS IN THE SOFTWARE. -\ProvidesPackage{penlight}[2023-07-22] +\ProvidesPackage{penlight}[2024-09-15] \directlua{penlight = require'penlight'} diff --git a/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.lua b/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.lua index e51b01c14bd..0e948c3fe18 100644 --- a/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.lua +++ b/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.lua @@ -1,5 +1,5 @@ --% Kale Ewasiuk (kalekje@gmail.com) ---% 2024-03-14 +--% 2024-08-17 --% Copyright (C) 2021-2024 Kale Ewasiuk --% --% Permission is hereby granted, free of charge, to any person obtaining a copy @@ -857,6 +857,21 @@ end +function penlight.caseswitch(s, c, kv) + local kvtbl = penlight.luakeys.parse(kv) + local sw = kvtbl[c] -- the returned switch + if sw == nil then -- if switch not found + if s == penlight.tex.xTrue then -- if star, throw error + pl.tex.pkgerror('penlight', 'case: "'..c..'" not found in key-vals: "'..kv..'"') + sw = '' + else + sw = kvtbl['__'] or '' -- use __ as not found case + end + end + tex.sprint(sw) +end + + penlight.tbls = {} diff --git a/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.sty b/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.sty index 3c411d227ac..58580036b5b 100644 --- a/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.sty +++ b/Master/texmf-dist/tex/luatex/penlightplus/penlightplus.sty @@ -1,5 +1,5 @@ % Kale Ewasiuk (kalekje@gmail.com) -% 2024-03-14 +% 2024-08-17 % Copyright (C) 2021-2024 Kale Ewasiuk % % Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,7 +22,7 @@ % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE % OR OTHER DEALINGS IN THE SOFTWARE. -\ProvidesPackage{penlightplus}[2024-03-14] +\ProvidesPackage{penlightplus}[2024-08-17] \RequirePackage{luacode} \RequirePackage{luakeys} @@ -97,6 +97,18 @@ + + + +\NewDocumentCommand{\caseswitch}{s m +m}{\ignorespaces\luadirect{penlight.caseswitch(\luastring{#1},\luastring{#2},\luastringN{#3})}\unskip} +% argument 1 is star option, which throws an error if case is not found +% argument 2 is the case, fully expanded +% argument 3 is a luakeys table of options, not expanded + + + + + %%% tbls below \NewDocumentCommand{\tblnew}{m}{\luadirect{% initialize a tbl and set blank |