diff options
Diffstat (limited to 'Master/texmf-dist/tex/luatex/markdown/markdown.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/markdown/markdown.lua | 8289 |
1 files changed, 66 insertions, 8223 deletions
diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua index d3711c4f243..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,21 @@ -- those in the standard .ins files. -- local metadata = { - version = "3.6.0-0-g83c781b4", - 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 = "." @@ -141,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 @@ -184,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 @@ -210,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 @@ -327,8060 +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 - 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("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") -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) - 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 - 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 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 - } -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 - } +local function warn(s) + io.stderr:write("Warning: " .. s .. "\n") 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, 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, "\\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 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: 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 |