summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2024-02-18 21:05:58 +0000
committerKarl Berry <karl@freefriends.org>2024-02-18 21:05:58 +0000
commit45d62c355885e905db2a729761eb384ec447e931 (patch)
treea80c1c9bd45c98a38855f2ac39f1ac5233862747 /Master/texmf-dist/tex/luatex
parentc503d4c5c13af6a3749ec30bfee14cffda84240c (diff)
markdown (18feb24)
git-svn-id: svn://tug.org/texlive/trunk@69962 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex')
-rw-r--r--Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua4
-rw-r--r--Master/texmf-dist/tex/luatex/markdown/markdown.lua4728
2 files changed, 3513 insertions, 1219 deletions
diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua b/Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua
index 074ad79446e..36cef293ed9 100644
--- a/Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua
+++ b/Master/texmf-dist/tex/luatex/markdown/markdown-tinyyaml.lua
@@ -633,8 +633,10 @@ function Parser:parseseq(line, lines, indent)
error("did not find expected alphabetic or numeric character")
elseif rest then
-- Array entry with a value
+ local nextline = lines[1]
+ local indent2 = countindent(nextline)
tremove(lines, 1)
- tinsert(seq, self:parsescalar(rest, lines))
+ tinsert(seq, self:parsescalar(rest, lines, indent2))
end
end
return seq
diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua
index 225428abe03..fce3c1fe827 100644
--- a/Master/texmf-dist/tex/luatex/markdown/markdown.lua
+++ b/Master/texmf-dist/tex/luatex/markdown/markdown.lua
@@ -20,7 +20,7 @@
-- 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-2023 Vít Novotný
+-- Copyright (C) 2016-2024 Vít Starý Novotný
--
-- This work may be distributed and/or modified under the
-- conditions of the LaTeX Project Public License, either version 1.3
@@ -33,7 +33,7 @@
-- version 2005/12/01 or later.
--
-- This work has the LPPL maintenance status `maintained'.
--- The Current Maintainer of this work is Vít Novotný.
+-- The Current Maintainer of this work is Vít Starý Novotný.
--
-- Send bug reports, requests for additions and questions
-- either to the GitHub issue tracker at
@@ -58,50 +58,28 @@
-- those in the standard .ins files.
--
local metadata = {
- version = "2.23.0-0-g0b22f91",
+ version = "3.4.1-0-g457226ae",
comment = "A module for the conversion from markdown to plain TeX",
- author = "John MacFarlane, Hans Hagen, Vít Novotný",
+ author = "John MacFarlane, Hans Hagen, Vít Starý Novotný",
copyright = {"2009-2016 John MacFarlane, Hans Hagen",
- "2016-2023 Vít Novotný"},
+ "2016-2023 Vít Starý Novotný"},
license = "LPPL 1.3c"
}
if not modules then modules = { } end
modules['markdown'] = metadata
local lpeg = require("lpeg")
-local unicode
-(function()
- local ran_ok
- ran_ok, unicode = pcall(require, "unicode")
- if not ran_ok then
- unicode = {utf8 = {char=utf8.char}}
- end
-end)()
+local unicode = require("unicode")
local md5 = require("md5");
(function()
local should_initialize = package.loaded.kpse == nil
or tex.initialize ~= nil
- local ran_ok
- ran_ok, kpse = pcall(require, "kpse")
- if ran_ok and should_initialize then
+ kpse = require("kpse")
+ if should_initialize then
kpse.set_program_name("luatex")
end
- if not ran_ok then
- kpse = {lookup = function(f, _) return f end}
- end
-end)()
-local uni_case
-(function()
- local ran_ok
- ran_ok, uni_case = pcall(require, "lua-uni-case")
- if not ran_ok then
- if unicode.utf8.lower then
- uni_case = {casefold = unicode.utf8.lower}
- else
- uni_case = {casefold = string.lower}
- end
- end
end)()
+local uni_algos = require("lua-uni-algos")
local M = {metadata = metadata}
local walkable_syntax = {
Block = {
@@ -110,8 +88,11 @@ local walkable_syntax = {
"ThematicBreak",
"BulletList",
"OrderedList",
- "Heading",
"DisplayHtml",
+ "Heading",
+ },
+ BlockOrParagraph = {
+ "Block",
"Paragraph",
"Plain",
},
@@ -119,11 +100,8 @@ local walkable_syntax = {
"Str",
"Space",
"Endline",
- "UlOrStarLine",
- "Strong",
- "Emph",
- "Link",
- "Image",
+ "EndlineBreak",
+ "LinkAndEmph",
"Code",
"AutoLinkUrl",
"AutoLinkEmail",
@@ -136,49 +114,56 @@ local walkable_syntax = {
},
}
local defaultOptions = {}
+defaultOptions.eagerCache = false
+defaultOptions.singletonCache = true
+local singletonCache = {
+ convert = nil,
+ options = nil,
+}
defaultOptions.cacheDir = "."
defaultOptions.contentBlocksLanguageMap = "markdown-languages.json"
defaultOptions.debugExtensionsFileName = "debug-extensions.json"
defaultOptions.frozenCacheFileName = "frozenCache.tex"
+defaultOptions.autoIdentifiers = false
defaultOptions.blankBeforeBlockquote = false
defaultOptions.blankBeforeCodeFence = false
defaultOptions.blankBeforeDivFence = false
defaultOptions.blankBeforeHeading = false
+defaultOptions.blankBeforeList = false
defaultOptions.bracketedSpans = false
-defaultOptions.breakableBlockquotes = false
+defaultOptions.breakableBlockquotes = true
defaultOptions.citationNbsps = true
defaultOptions.citations = false
defaultOptions.codeSpans = true
defaultOptions.contentBlocks = false
+defaultOptions.contentLevel = "block"
defaultOptions.debugExtensions = false
defaultOptions.definitionLists = false
-defaultOptions.eagerCache = true
defaultOptions.expectJekyllData = false
metadata.user_extension_api_version = 2
-metadata.grammar_version = 2
+metadata.grammar_version = 4
defaultOptions.extensions = {}
defaultOptions.fancyLists = false
-defaultOptions.fencedCode = false
+defaultOptions.fencedCode = true
defaultOptions.fencedCodeAttributes = false
defaultOptions.fencedDivs = false
defaultOptions.finalizeCache = false
defaultOptions.frozenCacheCounter = 0
-defaultOptions.hardLineBreaks = false
+defaultOptions.gfmAutoIdentifiers = false
defaultOptions.hashEnumerators = false
defaultOptions.headerAttributes = false
-defaultOptions.html = false
+defaultOptions.html = true
defaultOptions.hybrid = false
defaultOptions.inlineCodeAttributes = false
-defaultOptions.inlineFootnotes = false
defaultOptions.inlineNotes = false
defaultOptions.jekyllData = false
defaultOptions.linkAttributes = false
defaultOptions.lineBlocks = false
-defaultOptions.footnotes = false
+defaultOptions.mark = false
defaultOptions.notes = false
defaultOptions.pipeTables = false
-defaultOptions.preserveTabs = false
-defaultOptions.rawAttribute = true
+defaultOptions.preserveTabs = true
+defaultOptions.rawAttribute = false
defaultOptions.relativeReferences = false
defaultOptions.shiftHeadings = 0
defaultOptions.slice = "^ $"
@@ -188,6 +173,7 @@ defaultOptions.strikeThrough = false
defaultOptions.stripIndent = false
defaultOptions.subscripts = false
defaultOptions.superscripts = false
+defaultOptions.tableAttributes = false
defaultOptions.tableCaptions = false
defaultOptions.taskLists = false
defaultOptions.texComments = false
@@ -198,9 +184,9 @@ 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, any =
+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.P(1)
+ 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")
@@ -348,13 +334,13 @@ local character_entities = {
["Tab"] = 9,
["NewLine"] = 10,
["excl"] = 33,
- ["quot"] = 34,
["QUOT"] = 34,
+ ["quot"] = 34,
["num"] = 35,
["dollar"] = 36,
["percnt"] = 37,
- ["amp"] = 38,
["AMP"] = 38,
+ ["amp"] = 38,
["apos"] = 39,
["lpar"] = 40,
["rpar"] = 41,
@@ -366,31 +352,36 @@ local character_entities = {
["sol"] = 47,
["colon"] = 58,
["semi"] = 59,
- ["lt"] = 60,
["LT"] = 60,
+ ["lt"] = 60,
+ ["nvlt"] = {60, 8402},
+ ["bne"] = {61, 8421},
["equals"] = 61,
- ["gt"] = 62,
["GT"] = 62,
+ ["gt"] = 62,
+ ["nvgt"] = {62, 8402},
["quest"] = 63,
["commat"] = 64,
- ["lsqb"] = 91,
["lbrack"] = 91,
+ ["lsqb"] = 91,
["bsol"] = 92,
- ["rsqb"] = 93,
["rbrack"] = 93,
+ ["rsqb"] = 93,
["Hat"] = 94,
+ ["UnderBar"] = 95,
["lowbar"] = 95,
- ["grave"] = 96,
["DiacriticalGrave"] = 96,
- ["lcub"] = 123,
+ ["grave"] = 96,
+ ["fjlig"] = {102, 106},
["lbrace"] = 123,
+ ["lcub"] = 123,
+ ["VerticalLine"] = 124,
["verbar"] = 124,
["vert"] = 124,
- ["VerticalLine"] = 124,
- ["rcub"] = 125,
["rbrace"] = 125,
- ["nbsp"] = 160,
+ ["rcub"] = 125,
["NonBreakingSpace"] = 160,
+ ["nbsp"] = 160,
["iexcl"] = 161,
["cent"] = 162,
["pound"] = 163,
@@ -399,36 +390,35 @@ local character_entities = {
["brvbar"] = 166,
["sect"] = 167,
["Dot"] = 168,
- ["die"] = 168,
["DoubleDot"] = 168,
+ ["die"] = 168,
["uml"] = 168,
- ["copy"] = 169,
["COPY"] = 169,
+ ["copy"] = 169,
["ordf"] = 170,
["laquo"] = 171,
["not"] = 172,
["shy"] = 173,
- ["reg"] = 174,
- ["circledR"] = 174,
["REG"] = 174,
+ ["circledR"] = 174,
+ ["reg"] = 174,
["macr"] = 175,
- ["OverBar"] = 175,
["strns"] = 175,
["deg"] = 176,
+ ["PlusMinus"] = 177,
["plusmn"] = 177,
["pm"] = 177,
- ["PlusMinus"] = 177,
["sup2"] = 178,
["sup3"] = 179,
- ["acute"] = 180,
["DiacriticalAcute"] = 180,
+ ["acute"] = 180,
["micro"] = 181,
["para"] = 182,
- ["middot"] = 183,
- ["centerdot"] = 183,
["CenterDot"] = 183,
- ["cedil"] = 184,
+ ["centerdot"] = 183,
+ ["middot"] = 183,
["Cedilla"] = 184,
+ ["cedil"] = 184,
["sup1"] = 185,
["ordm"] = 186,
["raquo"] = 187,
@@ -443,6 +433,7 @@ local character_entities = {
["Atilde"] = 195,
["Auml"] = 196,
["Aring"] = 197,
+ ["angst"] = 197,
["AElig"] = 198,
["Ccedil"] = 199,
["Egrave"] = 200,
@@ -492,8 +483,8 @@ local character_entities = {
["ocirc"] = 244,
["otilde"] = 245,
["ouml"] = 246,
- ["divide"] = 247,
["div"] = 247,
+ ["divide"] = 247,
["oslash"] = 248,
["ugrave"] = 249,
["uacute"] = 250,
@@ -628,20 +619,19 @@ local character_entities = {
["gacute"] = 501,
["jmath"] = 567,
["circ"] = 710,
- ["caron"] = 711,
["Hacek"] = 711,
- ["breve"] = 728,
+ ["caron"] = 711,
["Breve"] = 728,
- ["dot"] = 729,
+ ["breve"] = 728,
["DiacriticalDot"] = 729,
+ ["dot"] = 729,
["ring"] = 730,
["ogon"] = 731,
- ["tilde"] = 732,
["DiacriticalTilde"] = 732,
- ["dblac"] = 733,
+ ["tilde"] = 732,
["DiacriticalDoubleAcute"] = 733,
+ ["dblac"] = 733,
["DownBreve"] = 785,
- ["UnderBar"] = 818,
["Alpha"] = 913,
["Beta"] = 914,
["Gamma"] = 915,
@@ -666,12 +656,12 @@ local character_entities = {
["Chi"] = 935,
["Psi"] = 936,
["Omega"] = 937,
+ ["ohm"] = 937,
["alpha"] = 945,
["beta"] = 946,
["gamma"] = 947,
["delta"] = 948,
- ["epsiv"] = 949,
- ["varepsilon"] = 949,
+ ["epsi"] = 949,
["epsilon"] = 949,
["zeta"] = 950,
["eta"] = 951,
@@ -685,38 +675,39 @@ local character_entities = {
["omicron"] = 959,
["pi"] = 960,
["rho"] = 961,
+ ["sigmaf"] = 962,
["sigmav"] = 962,
["varsigma"] = 962,
- ["sigmaf"] = 962,
["sigma"] = 963,
["tau"] = 964,
["upsi"] = 965,
["upsilon"] = 965,
["phi"] = 966,
- ["phiv"] = 966,
- ["varphi"] = 966,
["chi"] = 967,
["psi"] = 968,
["omega"] = 969,
+ ["thetasym"] = 977,
["thetav"] = 977,
["vartheta"] = 977,
- ["thetasym"] = 977,
["Upsi"] = 978,
["upsih"] = 978,
+ ["phiv"] = 981,
["straightphi"] = 981,
+ ["varphi"] = 981,
["piv"] = 982,
["varpi"] = 982,
["Gammad"] = 988,
- ["gammad"] = 989,
["digamma"] = 989,
+ ["gammad"] = 989,
["kappav"] = 1008,
["varkappa"] = 1008,
["rhov"] = 1009,
["varrho"] = 1009,
- ["epsi"] = 1013,
+ ["epsiv"] = 1013,
["straightepsilon"] = 1013,
- ["bepsi"] = 1014,
+ ["varepsilon"] = 1013,
["backepsilon"] = 1014,
+ ["bepsi"] = 1014,
["IOcy"] = 1025,
["DJcy"] = 1026,
["GJcy"] = 1027,
@@ -815,40 +806,40 @@ local character_entities = {
["emsp14"] = 8197,
["numsp"] = 8199,
["puncsp"] = 8200,
- ["thinsp"] = 8201,
["ThinSpace"] = 8201,
- ["hairsp"] = 8202,
+ ["thinsp"] = 8201,
["VeryThinSpace"] = 8202,
- ["ZeroWidthSpace"] = 8203,
- ["NegativeVeryThinSpace"] = 8203,
- ["NegativeThinSpace"] = 8203,
+ ["hairsp"] = 8202,
["NegativeMediumSpace"] = 8203,
["NegativeThickSpace"] = 8203,
+ ["NegativeThinSpace"] = 8203,
+ ["NegativeVeryThinSpace"] = 8203,
+ ["ZeroWidthSpace"] = 8203,
["zwnj"] = 8204,
["zwj"] = 8205,
["lrm"] = 8206,
["rlm"] = 8207,
- ["hyphen"] = 8208,
["dash"] = 8208,
+ ["hyphen"] = 8208,
["ndash"] = 8211,
["mdash"] = 8212,
["horbar"] = 8213,
["Verbar"] = 8214,
["Vert"] = 8214,
- ["lsquo"] = 8216,
["OpenCurlyQuote"] = 8216,
+ ["lsquo"] = 8216,
+ ["CloseCurlyQuote"] = 8217,
["rsquo"] = 8217,
["rsquor"] = 8217,
- ["CloseCurlyQuote"] = 8217,
["lsquor"] = 8218,
["sbquo"] = 8218,
- ["ldquo"] = 8220,
["OpenCurlyDoubleQuote"] = 8220,
+ ["ldquo"] = 8220,
+ ["CloseCurlyDoubleQuote"] = 8221,
["rdquo"] = 8221,
["rdquor"] = 8221,
- ["CloseCurlyDoubleQuote"] = 8221,
- ["ldquor"] = 8222,
["bdquo"] = 8222,
+ ["ldquor"] = 8222,
["dagger"] = 8224,
["Dagger"] = 8225,
["ddagger"] = 8225,
@@ -862,10 +853,11 @@ local character_entities = {
["prime"] = 8242,
["Prime"] = 8243,
["tprime"] = 8244,
- ["bprime"] = 8245,
["backprime"] = 8245,
+ ["bprime"] = 8245,
["lsaquo"] = 8249,
["rsaquo"] = 8250,
+ ["OverBar"] = 8254,
["oline"] = 8254,
["caret"] = 8257,
["hybull"] = 8259,
@@ -873,6 +865,7 @@ local character_entities = {
["bsemi"] = 8271,
["qprime"] = 8279,
["MediumSpace"] = 8287,
+ ["ThickSpace"] = {8287, 8202},
["NoBreak"] = 8288,
["ApplyFunction"] = 8289,
["af"] = 8289,
@@ -881,34 +874,34 @@ local character_entities = {
["InvisibleComma"] = 8291,
["ic"] = 8291,
["euro"] = 8364,
- ["tdot"] = 8411,
["TripleDot"] = 8411,
+ ["tdot"] = 8411,
["DotDot"] = 8412,
["Copf"] = 8450,
["complexes"] = 8450,
["incare"] = 8453,
["gscr"] = 8458,
- ["hamilt"] = 8459,
["HilbertSpace"] = 8459,
["Hscr"] = 8459,
+ ["hamilt"] = 8459,
["Hfr"] = 8460,
["Poincareplane"] = 8460,
- ["quaternions"] = 8461,
["Hopf"] = 8461,
+ ["quaternions"] = 8461,
["planckh"] = 8462,
- ["planck"] = 8463,
["hbar"] = 8463,
- ["plankv"] = 8463,
["hslash"] = 8463,
+ ["planck"] = 8463,
+ ["plankv"] = 8463,
["Iscr"] = 8464,
["imagline"] = 8464,
- ["image"] = 8465,
+ ["Ifr"] = 8465,
["Im"] = 8465,
+ ["image"] = 8465,
["imagpart"] = 8465,
- ["Ifr"] = 8465,
+ ["Laplacetrf"] = 8466,
["Lscr"] = 8466,
["lagran"] = 8466,
- ["Laplacetrf"] = 8466,
["ell"] = 8467,
["Nopf"] = 8469,
["naturals"] = 8469,
@@ -918,40 +911,38 @@ local character_entities = {
["wp"] = 8472,
["Popf"] = 8473,
["primes"] = 8473,
- ["rationals"] = 8474,
["Qopf"] = 8474,
+ ["rationals"] = 8474,
["Rscr"] = 8475,
["realine"] = 8475,
- ["real"] = 8476,
["Re"] = 8476,
- ["realpart"] = 8476,
["Rfr"] = 8476,
- ["reals"] = 8477,
+ ["real"] = 8476,
+ ["realpart"] = 8476,
["Ropf"] = 8477,
+ ["reals"] = 8477,
["rx"] = 8478,
- ["trade"] = 8482,
["TRADE"] = 8482,
- ["integers"] = 8484,
+ ["trade"] = 8482,
["Zopf"] = 8484,
- ["ohm"] = 8486,
+ ["integers"] = 8484,
["mho"] = 8487,
["Zfr"] = 8488,
["zeetrf"] = 8488,
["iiota"] = 8489,
- ["angst"] = 8491,
- ["bernou"] = 8492,
["Bernoullis"] = 8492,
["Bscr"] = 8492,
- ["Cfr"] = 8493,
+ ["bernou"] = 8492,
["Cayleys"] = 8493,
+ ["Cfr"] = 8493,
["escr"] = 8495,
["Escr"] = 8496,
["expectation"] = 8496,
- ["Fscr"] = 8497,
["Fouriertrf"] = 8497,
- ["phmmat"] = 8499,
+ ["Fscr"] = 8497,
["Mellintrf"] = 8499,
["Mscr"] = 8499,
+ ["phmmat"] = 8499,
["order"] = 8500,
["orderof"] = 8500,
["oscr"] = 8500,
@@ -965,8 +956,8 @@ local character_entities = {
["DifferentialD"] = 8518,
["dd"] = 8518,
["ExponentialE"] = 8519,
- ["exponentiale"] = 8519,
["ee"] = 8519,
+ ["exponentiale"] = 8519,
["ImaginaryI"] = 8520,
["ii"] = 8520,
["frac13"] = 8531,
@@ -981,46 +972,47 @@ local character_entities = {
["frac38"] = 8540,
["frac58"] = 8541,
["frac78"] = 8542,
+ ["LeftArrow"] = 8592,
+ ["ShortLeftArrow"] = 8592,
["larr"] = 8592,
["leftarrow"] = 8592,
- ["LeftArrow"] = 8592,
["slarr"] = 8592,
- ["ShortLeftArrow"] = 8592,
+ ["ShortUpArrow"] = 8593,
+ ["UpArrow"] = 8593,
["uarr"] = 8593,
["uparrow"] = 8593,
- ["UpArrow"] = 8593,
- ["ShortUpArrow"] = 8593,
+ ["RightArrow"] = 8594,
+ ["ShortRightArrow"] = 8594,
["rarr"] = 8594,
["rightarrow"] = 8594,
- ["RightArrow"] = 8594,
["srarr"] = 8594,
- ["ShortRightArrow"] = 8594,
- ["darr"] = 8595,
- ["downarrow"] = 8595,
["DownArrow"] = 8595,
["ShortDownArrow"] = 8595,
+ ["darr"] = 8595,
+ ["downarrow"] = 8595,
+ ["LeftRightArrow"] = 8596,
["harr"] = 8596,
["leftrightarrow"] = 8596,
- ["LeftRightArrow"] = 8596,
- ["varr"] = 8597,
- ["updownarrow"] = 8597,
["UpDownArrow"] = 8597,
- ["nwarr"] = 8598,
+ ["updownarrow"] = 8597,
+ ["varr"] = 8597,
["UpperLeftArrow"] = 8598,
+ ["nwarr"] = 8598,
["nwarrow"] = 8598,
- ["nearr"] = 8599,
["UpperRightArrow"] = 8599,
+ ["nearr"] = 8599,
["nearrow"] = 8599,
+ ["LowerRightArrow"] = 8600,
["searr"] = 8600,
["searrow"] = 8600,
- ["LowerRightArrow"] = 8600,
+ ["LowerLeftArrow"] = 8601,
["swarr"] = 8601,
["swarrow"] = 8601,
- ["LowerLeftArrow"] = 8601,
["nlarr"] = 8602,
["nleftarrow"] = 8602,
["nrarr"] = 8603,
["nrightarrow"] = 8603,
+ ["nrarrw"] = {8605, 824},
["rarrw"] = 8605,
["rightsquigarrow"] = 8605,
["Larr"] = 8606,
@@ -1037,27 +1029,27 @@ local character_entities = {
["mapstoleft"] = 8612,
["UpTeeArrow"] = 8613,
["mapstoup"] = 8613,
- ["map"] = 8614,
["RightTeeArrow"] = 8614,
+ ["map"] = 8614,
["mapsto"] = 8614,
["DownTeeArrow"] = 8615,
["mapstodown"] = 8615,
- ["larrhk"] = 8617,
["hookleftarrow"] = 8617,
- ["rarrhk"] = 8618,
+ ["larrhk"] = 8617,
["hookrightarrow"] = 8618,
+ ["rarrhk"] = 8618,
["larrlp"] = 8619,
["looparrowleft"] = 8619,
- ["rarrlp"] = 8620,
["looparrowright"] = 8620,
+ ["rarrlp"] = 8620,
["harrw"] = 8621,
["leftrightsquigarrow"] = 8621,
["nharr"] = 8622,
["nleftrightarrow"] = 8622,
- ["lsh"] = 8624,
["Lsh"] = 8624,
- ["rsh"] = 8625,
+ ["lsh"] = 8624,
["Rsh"] = 8625,
+ ["rsh"] = 8625,
["ldsh"] = 8626,
["rdsh"] = 8627,
["crarr"] = 8629,
@@ -1065,456 +1057,505 @@ local character_entities = {
["curvearrowleft"] = 8630,
["curarr"] = 8631,
["curvearrowright"] = 8631,
- ["olarr"] = 8634,
["circlearrowleft"] = 8634,
- ["orarr"] = 8635,
+ ["olarr"] = 8634,
["circlearrowright"] = 8635,
- ["lharu"] = 8636,
+ ["orarr"] = 8635,
["LeftVector"] = 8636,
["leftharpoonup"] = 8636,
- ["lhard"] = 8637,
- ["leftharpoondown"] = 8637,
+ ["lharu"] = 8636,
["DownLeftVector"] = 8637,
+ ["leftharpoondown"] = 8637,
+ ["lhard"] = 8637,
+ ["RightUpVector"] = 8638,
["uharr"] = 8638,
["upharpoonright"] = 8638,
- ["RightUpVector"] = 8638,
+ ["LeftUpVector"] = 8639,
["uharl"] = 8639,
["upharpoonleft"] = 8639,
- ["LeftUpVector"] = 8639,
- ["rharu"] = 8640,
["RightVector"] = 8640,
+ ["rharu"] = 8640,
["rightharpoonup"] = 8640,
+ ["DownRightVector"] = 8641,
["rhard"] = 8641,
["rightharpoondown"] = 8641,
- ["DownRightVector"] = 8641,
- ["dharr"] = 8642,
["RightDownVector"] = 8642,
+ ["dharr"] = 8642,
["downharpoonright"] = 8642,
- ["dharl"] = 8643,
["LeftDownVector"] = 8643,
+ ["dharl"] = 8643,
["downharpoonleft"] = 8643,
- ["rlarr"] = 8644,
- ["rightleftarrows"] = 8644,
["RightArrowLeftArrow"] = 8644,
- ["udarr"] = 8645,
+ ["rightleftarrows"] = 8644,
+ ["rlarr"] = 8644,
["UpArrowDownArrow"] = 8645,
- ["lrarr"] = 8646,
- ["leftrightarrows"] = 8646,
+ ["udarr"] = 8645,
["LeftArrowRightArrow"] = 8646,
- ["llarr"] = 8647,
+ ["leftrightarrows"] = 8646,
+ ["lrarr"] = 8646,
["leftleftarrows"] = 8647,
- ["uuarr"] = 8648,
+ ["llarr"] = 8647,
["upuparrows"] = 8648,
- ["rrarr"] = 8649,
+ ["uuarr"] = 8648,
["rightrightarrows"] = 8649,
+ ["rrarr"] = 8649,
["ddarr"] = 8650,
["downdownarrows"] = 8650,
- ["lrhar"] = 8651,
["ReverseEquilibrium"] = 8651,
["leftrightharpoons"] = 8651,
- ["rlhar"] = 8652,
- ["rightleftharpoons"] = 8652,
+ ["lrhar"] = 8651,
["Equilibrium"] = 8652,
- ["nlArr"] = 8653,
+ ["rightleftharpoons"] = 8652,
+ ["rlhar"] = 8652,
["nLeftarrow"] = 8653,
- ["nhArr"] = 8654,
+ ["nlArr"] = 8653,
["nLeftrightarrow"] = 8654,
- ["nrArr"] = 8655,
+ ["nhArr"] = 8654,
["nRightarrow"] = 8655,
- ["lArr"] = 8656,
- ["Leftarrow"] = 8656,
+ ["nrArr"] = 8655,
["DoubleLeftArrow"] = 8656,
- ["uArr"] = 8657,
- ["Uparrow"] = 8657,
+ ["Leftarrow"] = 8656,
+ ["lArr"] = 8656,
["DoubleUpArrow"] = 8657,
- ["rArr"] = 8658,
- ["Rightarrow"] = 8658,
- ["Implies"] = 8658,
+ ["Uparrow"] = 8657,
+ ["uArr"] = 8657,
["DoubleRightArrow"] = 8658,
- ["dArr"] = 8659,
- ["Downarrow"] = 8659,
+ ["Implies"] = 8658,
+ ["Rightarrow"] = 8658,
+ ["rArr"] = 8658,
["DoubleDownArrow"] = 8659,
- ["hArr"] = 8660,
- ["Leftrightarrow"] = 8660,
+ ["Downarrow"] = 8659,
+ ["dArr"] = 8659,
["DoubleLeftRightArrow"] = 8660,
+ ["Leftrightarrow"] = 8660,
+ ["hArr"] = 8660,
["iff"] = 8660,
- ["vArr"] = 8661,
- ["Updownarrow"] = 8661,
["DoubleUpDownArrow"] = 8661,
+ ["Updownarrow"] = 8661,
+ ["vArr"] = 8661,
["nwArr"] = 8662,
["neArr"] = 8663,
["seArr"] = 8664,
["swArr"] = 8665,
- ["lAarr"] = 8666,
["Lleftarrow"] = 8666,
- ["rAarr"] = 8667,
+ ["lAarr"] = 8666,
["Rrightarrow"] = 8667,
+ ["rAarr"] = 8667,
["zigrarr"] = 8669,
- ["larrb"] = 8676,
["LeftArrowBar"] = 8676,
- ["rarrb"] = 8677,
+ ["larrb"] = 8676,
["RightArrowBar"] = 8677,
- ["duarr"] = 8693,
+ ["rarrb"] = 8677,
["DownArrowUpArrow"] = 8693,
+ ["duarr"] = 8693,
["loarr"] = 8701,
["roarr"] = 8702,
["hoarr"] = 8703,
- ["forall"] = 8704,
["ForAll"] = 8704,
+ ["forall"] = 8704,
["comp"] = 8705,
["complement"] = 8705,
- ["part"] = 8706,
["PartialD"] = 8706,
- ["exist"] = 8707,
+ ["npart"] = {8706, 824},
+ ["part"] = 8706,
["Exists"] = 8707,
- ["nexist"] = 8708,
+ ["exist"] = 8707,
["NotExists"] = 8708,
+ ["nexist"] = 8708,
["nexists"] = 8708,
["empty"] = 8709,
["emptyset"] = 8709,
["emptyv"] = 8709,
["varnothing"] = 8709,
- ["nabla"] = 8711,
["Del"] = 8711,
- ["isin"] = 8712,
- ["isinv"] = 8712,
+ ["nabla"] = 8711,
["Element"] = 8712,
["in"] = 8712,
- ["notin"] = 8713,
+ ["isin"] = 8712,
+ ["isinv"] = 8712,
["NotElement"] = 8713,
+ ["notin"] = 8713,
["notinva"] = 8713,
- ["niv"] = 8715,
["ReverseElement"] = 8715,
- ["ni"] = 8715,
["SuchThat"] = 8715,
+ ["ni"] = 8715,
+ ["niv"] = 8715,
+ ["NotReverseElement"] = 8716,
["notni"] = 8716,
["notniva"] = 8716,
- ["NotReverseElement"] = 8716,
- ["prod"] = 8719,
["Product"] = 8719,
- ["coprod"] = 8720,
+ ["prod"] = 8719,
["Coproduct"] = 8720,
- ["sum"] = 8721,
+ ["coprod"] = 8720,
["Sum"] = 8721,
+ ["sum"] = 8721,
["minus"] = 8722,
+ ["MinusPlus"] = 8723,
["mnplus"] = 8723,
["mp"] = 8723,
- ["MinusPlus"] = 8723,
- ["plusdo"] = 8724,
["dotplus"] = 8724,
- ["setmn"] = 8726,
- ["setminus"] = 8726,
+ ["plusdo"] = 8724,
["Backslash"] = 8726,
- ["ssetmn"] = 8726,
+ ["setminus"] = 8726,
+ ["setmn"] = 8726,
["smallsetminus"] = 8726,
+ ["ssetmn"] = 8726,
["lowast"] = 8727,
- ["compfn"] = 8728,
["SmallCircle"] = 8728,
- ["radic"] = 8730,
+ ["compfn"] = 8728,
["Sqrt"] = 8730,
+ ["radic"] = 8730,
+ ["Proportional"] = 8733,
["prop"] = 8733,
["propto"] = 8733,
- ["Proportional"] = 8733,
- ["vprop"] = 8733,
["varpropto"] = 8733,
+ ["vprop"] = 8733,
["infin"] = 8734,
["angrt"] = 8735,
["ang"] = 8736,
["angle"] = 8736,
+ ["nang"] = {8736, 8402},
["angmsd"] = 8737,
["measuredangle"] = 8737,
["angsph"] = 8738,
- ["mid"] = 8739,
["VerticalBar"] = 8739,
- ["smid"] = 8739,
+ ["mid"] = 8739,
["shortmid"] = 8739,
- ["nmid"] = 8740,
+ ["smid"] = 8739,
["NotVerticalBar"] = 8740,
- ["nsmid"] = 8740,
+ ["nmid"] = 8740,
["nshortmid"] = 8740,
+ ["nsmid"] = 8740,
+ ["DoubleVerticalBar"] = 8741,
["par"] = 8741,
["parallel"] = 8741,
- ["DoubleVerticalBar"] = 8741,
- ["spar"] = 8741,
["shortparallel"] = 8741,
+ ["spar"] = 8741,
+ ["NotDoubleVerticalBar"] = 8742,
["npar"] = 8742,
["nparallel"] = 8742,
- ["NotDoubleVerticalBar"] = 8742,
- ["nspar"] = 8742,
["nshortparallel"] = 8742,
+ ["nspar"] = 8742,
["and"] = 8743,
["wedge"] = 8743,
["or"] = 8744,
["vee"] = 8744,
["cap"] = 8745,
+ ["caps"] = {8745, 65024},
["cup"] = 8746,
- ["int"] = 8747,
+ ["cups"] = {8746, 65024},
["Integral"] = 8747,
+ ["int"] = 8747,
["Int"] = 8748,
- ["tint"] = 8749,
["iiint"] = 8749,
+ ["tint"] = 8749,
+ ["ContourIntegral"] = 8750,
["conint"] = 8750,
["oint"] = 8750,
- ["ContourIntegral"] = 8750,
["Conint"] = 8751,
["DoubleContourIntegral"] = 8751,
["Cconint"] = 8752,
["cwint"] = 8753,
- ["cwconint"] = 8754,
["ClockwiseContourIntegral"] = 8754,
- ["awconint"] = 8755,
+ ["cwconint"] = 8754,
["CounterClockwiseContourIntegral"] = 8755,
+ ["awconint"] = 8755,
+ ["Therefore"] = 8756,
["there4"] = 8756,
["therefore"] = 8756,
- ["Therefore"] = 8756,
+ ["Because"] = 8757,
["becaus"] = 8757,
["because"] = 8757,
- ["Because"] = 8757,
["ratio"] = 8758,
["Colon"] = 8759,
["Proportion"] = 8759,
- ["minusd"] = 8760,
["dotminus"] = 8760,
+ ["minusd"] = 8760,
["mDDot"] = 8762,
["homtht"] = 8763,
- ["sim"] = 8764,
["Tilde"] = 8764,
- ["thksim"] = 8764,
+ ["nvsim"] = {8764, 8402},
+ ["sim"] = 8764,
["thicksim"] = 8764,
- ["bsim"] = 8765,
+ ["thksim"] = 8764,
["backsim"] = 8765,
+ ["bsim"] = 8765,
+ ["race"] = {8765, 817},
["ac"] = 8766,
+ ["acE"] = {8766, 819},
["mstpos"] = 8766,
["acd"] = 8767,
- ["wreath"] = 8768,
["VerticalTilde"] = 8768,
["wr"] = 8768,
- ["nsim"] = 8769,
+ ["wreath"] = 8768,
["NotTilde"] = 8769,
- ["esim"] = 8770,
+ ["nsim"] = 8769,
["EqualTilde"] = 8770,
+ ["NotEqualTilde"] = {8770, 824},
["eqsim"] = 8770,
- ["sime"] = 8771,
+ ["esim"] = 8770,
+ ["nesim"] = {8770, 824},
["TildeEqual"] = 8771,
+ ["sime"] = 8771,
["simeq"] = 8771,
+ ["NotTildeEqual"] = 8772,
["nsime"] = 8772,
["nsimeq"] = 8772,
- ["NotTildeEqual"] = 8772,
- ["cong"] = 8773,
["TildeFullEqual"] = 8773,
+ ["cong"] = 8773,
["simne"] = 8774,
- ["ncong"] = 8775,
["NotTildeFullEqual"] = 8775,
- ["asymp"] = 8776,
- ["ap"] = 8776,
+ ["ncong"] = 8775,
["TildeTilde"] = 8776,
+ ["ap"] = 8776,
["approx"] = 8776,
- ["thkap"] = 8776,
+ ["asymp"] = 8776,
["thickapprox"] = 8776,
- ["nap"] = 8777,
+ ["thkap"] = 8776,
["NotTildeTilde"] = 8777,
+ ["nap"] = 8777,
["napprox"] = 8777,
["ape"] = 8778,
["approxeq"] = 8778,
["apid"] = 8779,
- ["bcong"] = 8780,
+ ["napid"] = {8779, 824},
["backcong"] = 8780,
- ["asympeq"] = 8781,
+ ["bcong"] = 8780,
["CupCap"] = 8781,
- ["bump"] = 8782,
- ["HumpDownHump"] = 8782,
+ ["asympeq"] = 8781,
+ ["nvap"] = {8781, 8402},
["Bumpeq"] = 8782,
- ["bumpe"] = 8783,
+ ["HumpDownHump"] = 8782,
+ ["NotHumpDownHump"] = {8782, 824},
+ ["bump"] = 8782,
+ ["nbump"] = {8782, 824},
["HumpEqual"] = 8783,
+ ["NotHumpEqual"] = {8783, 824},
+ ["bumpe"] = 8783,
["bumpeq"] = 8783,
- ["esdot"] = 8784,
+ ["nbumpe"] = {8783, 824},
["DotEqual"] = 8784,
["doteq"] = 8784,
- ["eDot"] = 8785,
+ ["esdot"] = 8784,
+ ["nedot"] = {8784, 824},
["doteqdot"] = 8785,
+ ["eDot"] = 8785,
["efDot"] = 8786,
["fallingdotseq"] = 8786,
["erDot"] = 8787,
["risingdotseq"] = 8787,
+ ["Assign"] = 8788,
["colone"] = 8788,
["coloneq"] = 8788,
- ["Assign"] = 8788,
["ecolon"] = 8789,
["eqcolon"] = 8789,
["ecir"] = 8790,
["eqcirc"] = 8790,
- ["cire"] = 8791,
["circeq"] = 8791,
+ ["cire"] = 8791,
["wedgeq"] = 8793,
["veeeq"] = 8794,
- ["trie"] = 8796,
["triangleq"] = 8796,
+ ["trie"] = 8796,
["equest"] = 8799,
["questeq"] = 8799,
- ["ne"] = 8800,
["NotEqual"] = 8800,
- ["equiv"] = 8801,
+ ["ne"] = 8800,
["Congruent"] = 8801,
- ["nequiv"] = 8802,
+ ["bnequiv"] = {8801, 8421},
+ ["equiv"] = 8801,
["NotCongruent"] = 8802,
+ ["nequiv"] = 8802,
["le"] = 8804,
["leq"] = 8804,
- ["ge"] = 8805,
+ ["nvle"] = {8804, 8402},
["GreaterEqual"] = 8805,
+ ["ge"] = 8805,
["geq"] = 8805,
- ["lE"] = 8806,
+ ["nvge"] = {8805, 8402},
["LessFullEqual"] = 8806,
+ ["lE"] = 8806,
["leqq"] = 8806,
- ["gE"] = 8807,
+ ["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,
- ["twixt"] = 8812,
+ ["nGt"] = {8811, 8402},
+ ["nGtv"] = {8811, 824},
["between"] = 8812,
+ ["twixt"] = 8812,
["NotCupCap"] = 8813,
- ["nlt"] = 8814,
["NotLess"] = 8814,
["nless"] = 8814,
- ["ngt"] = 8815,
+ ["nlt"] = 8814,
["NotGreater"] = 8815,
+ ["ngt"] = 8815,
["ngtr"] = 8815,
- ["nle"] = 8816,
["NotLessEqual"] = 8816,
+ ["nle"] = 8816,
["nleq"] = 8816,
- ["nge"] = 8817,
["NotGreaterEqual"] = 8817,
+ ["nge"] = 8817,
["ngeq"] = 8817,
- ["lsim"] = 8818,
["LessTilde"] = 8818,
["lesssim"] = 8818,
+ ["lsim"] = 8818,
+ ["GreaterTilde"] = 8819,
["gsim"] = 8819,
["gtrsim"] = 8819,
- ["GreaterTilde"] = 8819,
- ["nlsim"] = 8820,
["NotLessTilde"] = 8820,
- ["ngsim"] = 8821,
+ ["nlsim"] = 8820,
["NotGreaterTilde"] = 8821,
- ["lg"] = 8822,
- ["lessgtr"] = 8822,
+ ["ngsim"] = 8821,
["LessGreater"] = 8822,
+ ["lessgtr"] = 8822,
+ ["lg"] = 8822,
+ ["GreaterLess"] = 8823,
["gl"] = 8823,
["gtrless"] = 8823,
- ["GreaterLess"] = 8823,
- ["ntlg"] = 8824,
["NotLessGreater"] = 8824,
- ["ntgl"] = 8825,
+ ["ntlg"] = 8824,
["NotGreaterLess"] = 8825,
- ["pr"] = 8826,
+ ["ntgl"] = 8825,
["Precedes"] = 8826,
+ ["pr"] = 8826,
["prec"] = 8826,
- ["sc"] = 8827,
["Succeeds"] = 8827,
+ ["sc"] = 8827,
["succ"] = 8827,
- ["prcue"] = 8828,
["PrecedesSlantEqual"] = 8828,
+ ["prcue"] = 8828,
["preccurlyeq"] = 8828,
- ["sccue"] = 8829,
["SucceedsSlantEqual"] = 8829,
+ ["sccue"] = 8829,
["succcurlyeq"] = 8829,
- ["prsim"] = 8830,
- ["precsim"] = 8830,
["PrecedesTilde"] = 8830,
+ ["precsim"] = 8830,
+ ["prsim"] = 8830,
+ ["NotSucceedsTilde"] = {8831, 824},
+ ["SucceedsTilde"] = 8831,
["scsim"] = 8831,
["succsim"] = 8831,
- ["SucceedsTilde"] = 8831,
+ ["NotPrecedes"] = 8832,
["npr"] = 8832,
["nprec"] = 8832,
- ["NotPrecedes"] = 8832,
+ ["NotSucceeds"] = 8833,
["nsc"] = 8833,
["nsucc"] = 8833,
- ["NotSucceeds"] = 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,
- ["Superset"] = 8835,
+ ["vnsup"] = {8835, 8402},
["nsub"] = 8836,
["nsup"] = 8837,
- ["sube"] = 8838,
["SubsetEqual"] = 8838,
+ ["sube"] = 8838,
["subseteq"] = 8838,
+ ["SupersetEqual"] = 8839,
["supe"] = 8839,
["supseteq"] = 8839,
- ["SupersetEqual"] = 8839,
+ ["NotSubsetEqual"] = 8840,
["nsube"] = 8840,
["nsubseteq"] = 8840,
- ["NotSubsetEqual"] = 8840,
+ ["NotSupersetEqual"] = 8841,
["nsupe"] = 8841,
["nsupseteq"] = 8841,
- ["NotSupersetEqual"] = 8841,
["subne"] = 8842,
["subsetneq"] = 8842,
+ ["varsubsetneq"] = {8842, 65024},
+ ["vsubne"] = {8842, 65024},
["supne"] = 8843,
["supsetneq"] = 8843,
+ ["varsupsetneq"] = {8843, 65024},
+ ["vsupne"] = {8843, 65024},
["cupdot"] = 8845,
- ["uplus"] = 8846,
["UnionPlus"] = 8846,
- ["sqsub"] = 8847,
+ ["uplus"] = 8846,
+ ["NotSquareSubset"] = {8847, 824},
["SquareSubset"] = 8847,
+ ["sqsub"] = 8847,
["sqsubset"] = 8847,
- ["sqsup"] = 8848,
+ ["NotSquareSuperset"] = {8848, 824},
["SquareSuperset"] = 8848,
+ ["sqsup"] = 8848,
["sqsupset"] = 8848,
- ["sqsube"] = 8849,
["SquareSubsetEqual"] = 8849,
+ ["sqsube"] = 8849,
["sqsubseteq"] = 8849,
- ["sqsupe"] = 8850,
["SquareSupersetEqual"] = 8850,
+ ["sqsupe"] = 8850,
["sqsupseteq"] = 8850,
- ["sqcap"] = 8851,
["SquareIntersection"] = 8851,
- ["sqcup"] = 8852,
+ ["sqcap"] = 8851,
+ ["sqcaps"] = {8851, 65024},
["SquareUnion"] = 8852,
- ["oplus"] = 8853,
+ ["sqcup"] = 8852,
+ ["sqcups"] = {8852, 65024},
["CirclePlus"] = 8853,
- ["ominus"] = 8854,
+ ["oplus"] = 8853,
["CircleMinus"] = 8854,
- ["otimes"] = 8855,
+ ["ominus"] = 8854,
["CircleTimes"] = 8855,
+ ["otimes"] = 8855,
["osol"] = 8856,
- ["odot"] = 8857,
["CircleDot"] = 8857,
- ["ocir"] = 8858,
+ ["odot"] = 8857,
["circledcirc"] = 8858,
- ["oast"] = 8859,
+ ["ocir"] = 8858,
["circledast"] = 8859,
- ["odash"] = 8861,
+ ["oast"] = 8859,
["circleddash"] = 8861,
- ["plusb"] = 8862,
+ ["odash"] = 8861,
["boxplus"] = 8862,
- ["minusb"] = 8863,
+ ["plusb"] = 8862,
["boxminus"] = 8863,
- ["timesb"] = 8864,
+ ["minusb"] = 8863,
["boxtimes"] = 8864,
- ["sdotb"] = 8865,
+ ["timesb"] = 8864,
["dotsquare"] = 8865,
- ["vdash"] = 8866,
+ ["sdotb"] = 8865,
["RightTee"] = 8866,
- ["dashv"] = 8867,
+ ["vdash"] = 8866,
["LeftTee"] = 8867,
- ["top"] = 8868,
+ ["dashv"] = 8867,
["DownTee"] = 8868,
- ["bottom"] = 8869,
+ ["top"] = 8868,
+ ["UpTee"] = 8869,
["bot"] = 8869,
+ ["bottom"] = 8869,
["perp"] = 8869,
- ["UpTee"] = 8869,
["models"] = 8871,
- ["vDash"] = 8872,
["DoubleRightTee"] = 8872,
+ ["vDash"] = 8872,
["Vdash"] = 8873,
["Vvdash"] = 8874,
["VDash"] = 8875,
@@ -1523,22 +1564,24 @@ local character_entities = {
["nVdash"] = 8878,
["nVDash"] = 8879,
["prurel"] = 8880,
- ["vltri"] = 8882,
- ["vartriangleleft"] = 8882,
["LeftTriangle"] = 8882,
- ["vrtri"] = 8883,
- ["vartriangleright"] = 8883,
+ ["vartriangleleft"] = 8882,
+ ["vltri"] = 8882,
["RightTriangle"] = 8883,
+ ["vartriangleright"] = 8883,
+ ["vrtri"] = 8883,
+ ["LeftTriangleEqual"] = 8884,
["ltrie"] = 8884,
+ ["nvltrie"] = {8884, 8402},
["trianglelefteq"] = 8884,
- ["LeftTriangleEqual"] = 8884,
+ ["RightTriangleEqual"] = 8885,
+ ["nvrtrie"] = {8885, 8402},
["rtrie"] = 8885,
["trianglerighteq"] = 8885,
- ["RightTriangleEqual"] = 8885,
["origof"] = 8886,
["imof"] = 8887,
- ["mumap"] = 8888,
["multimap"] = 8888,
+ ["mumap"] = 8888,
["hercon"] = 8889,
["intcal"] = 8890,
["intercal"] = 8890,
@@ -1546,39 +1589,39 @@ local character_entities = {
["barvee"] = 8893,
["angrtvb"] = 8894,
["lrtri"] = 8895,
- ["xwedge"] = 8896,
["Wedge"] = 8896,
["bigwedge"] = 8896,
- ["xvee"] = 8897,
+ ["xwedge"] = 8896,
["Vee"] = 8897,
["bigvee"] = 8897,
- ["xcap"] = 8898,
+ ["xvee"] = 8897,
["Intersection"] = 8898,
["bigcap"] = 8898,
- ["xcup"] = 8899,
+ ["xcap"] = 8898,
["Union"] = 8899,
["bigcup"] = 8899,
+ ["xcup"] = 8899,
+ ["Diamond"] = 8900,
["diam"] = 8900,
["diamond"] = 8900,
- ["Diamond"] = 8900,
["sdot"] = 8901,
- ["sstarf"] = 8902,
["Star"] = 8902,
- ["divonx"] = 8903,
+ ["sstarf"] = 8902,
["divideontimes"] = 8903,
+ ["divonx"] = 8903,
["bowtie"] = 8904,
["ltimes"] = 8905,
["rtimes"] = 8906,
- ["lthree"] = 8907,
["leftthreetimes"] = 8907,
- ["rthree"] = 8908,
+ ["lthree"] = 8907,
["rightthreetimes"] = 8908,
- ["bsime"] = 8909,
+ ["rthree"] = 8908,
["backsimeq"] = 8909,
- ["cuvee"] = 8910,
+ ["bsime"] = 8909,
["curlyvee"] = 8910,
- ["cuwed"] = 8911,
+ ["cuvee"] = 8910,
["curlywedge"] = 8911,
+ ["cuwed"] = 8911,
["Sub"] = 8912,
["Subset"] = 8912,
["Sup"] = 8913,
@@ -1588,49 +1631,53 @@ local character_entities = {
["fork"] = 8916,
["pitchfork"] = 8916,
["epar"] = 8917,
- ["ltdot"] = 8918,
["lessdot"] = 8918,
+ ["ltdot"] = 8918,
["gtdot"] = 8919,
["gtrdot"] = 8919,
["Ll"] = 8920,
+ ["nLl"] = {8920, 824},
["Gg"] = 8921,
["ggg"] = 8921,
- ["leg"] = 8922,
+ ["nGg"] = {8921, 824},
["LessEqualGreater"] = 8922,
+ ["leg"] = 8922,
+ ["lesg"] = {8922, 65024},
["lesseqgtr"] = 8922,
+ ["GreaterEqualLess"] = 8923,
["gel"] = 8923,
+ ["gesl"] = {8923, 65024},
["gtreqless"] = 8923,
- ["GreaterEqualLess"] = 8923,
["cuepr"] = 8926,
["curlyeqprec"] = 8926,
["cuesc"] = 8927,
["curlyeqsucc"] = 8927,
- ["nprcue"] = 8928,
["NotPrecedesSlantEqual"] = 8928,
- ["nsccue"] = 8929,
+ ["nprcue"] = 8928,
["NotSucceedsSlantEqual"] = 8929,
- ["nsqsube"] = 8930,
+ ["nsccue"] = 8929,
["NotSquareSubsetEqual"] = 8930,
- ["nsqsupe"] = 8931,
+ ["nsqsube"] = 8930,
["NotSquareSupersetEqual"] = 8931,
+ ["nsqsupe"] = 8931,
["lnsim"] = 8934,
["gnsim"] = 8935,
- ["prnsim"] = 8936,
["precnsim"] = 8936,
+ ["prnsim"] = 8936,
["scnsim"] = 8937,
["succnsim"] = 8937,
+ ["NotLeftTriangle"] = 8938,
["nltri"] = 8938,
["ntriangleleft"] = 8938,
- ["NotLeftTriangle"] = 8938,
+ ["NotRightTriangle"] = 8939,
["nrtri"] = 8939,
["ntriangleright"] = 8939,
- ["NotRightTriangle"] = 8939,
+ ["NotLeftTriangleEqual"] = 8940,
["nltrie"] = 8940,
["ntrianglelefteq"] = 8940,
- ["NotLeftTriangleEqual"] = 8940,
+ ["NotRightTriangleEqual"] = 8941,
["nrtrie"] = 8941,
["ntrianglerighteq"] = 8941,
- ["NotRightTriangleEqual"] = 8941,
["vellip"] = 8942,
["ctdot"] = 8943,
["utdot"] = 8944,
@@ -1639,9 +1686,11 @@ local character_entities = {
["isinsv"] = 8947,
["isins"] = 8948,
["isindot"] = 8949,
+ ["notindot"] = {8949, 824},
["notinvc"] = 8950,
["notinvb"] = 8951,
["isinE"] = 8953,
+ ["notinE"] = {8953, 824},
["nisd"] = 8954,
["xnis"] = 8955,
["nis"] = 8956,
@@ -1651,14 +1700,14 @@ local character_entities = {
["barwedge"] = 8965,
["Barwed"] = 8966,
["doublebarwedge"] = 8966,
- ["lceil"] = 8968,
["LeftCeiling"] = 8968,
- ["rceil"] = 8969,
+ ["lceil"] = 8968,
["RightCeiling"] = 8969,
- ["lfloor"] = 8970,
+ ["rceil"] = 8969,
["LeftFloor"] = 8970,
- ["rfloor"] = 8971,
+ ["lfloor"] = 8970,
["RightFloor"] = 8971,
+ ["rfloor"] = 8971,
["drcrop"] = 8972,
["dlcrop"] = 8973,
["urcrop"] = 8974,
@@ -1690,10 +1739,10 @@ local character_entities = {
["lmoustache"] = 9136,
["rmoust"] = 9137,
["rmoustache"] = 9137,
- ["tbrk"] = 9140,
["OverBracket"] = 9140,
- ["bbrk"] = 9141,
+ ["tbrk"] = 9140,
["UnderBracket"] = 9141,
+ ["bbrk"] = 9141,
["bbrktbrk"] = 9142,
["OverParenthesis"] = 9180,
["UnderParenthesis"] = 9181,
@@ -1702,10 +1751,10 @@ local character_entities = {
["trpezium"] = 9186,
["elinters"] = 9191,
["blank"] = 9251,
- ["oS"] = 9416,
["circledS"] = 9416,
- ["boxh"] = 9472,
+ ["oS"] = 9416,
["HorizontalLine"] = 9472,
+ ["boxh"] = 9472,
["boxv"] = 9474,
["boxdr"] = 9484,
["boxdl"] = 9488,
@@ -1751,50 +1800,50 @@ local character_entities = {
["blk14"] = 9617,
["blk12"] = 9618,
["blk34"] = 9619,
+ ["Square"] = 9633,
["squ"] = 9633,
["square"] = 9633,
- ["Square"] = 9633,
- ["squf"] = 9642,
- ["squarf"] = 9642,
- ["blacksquare"] = 9642,
["FilledVerySmallSquare"] = 9642,
+ ["blacksquare"] = 9642,
+ ["squarf"] = 9642,
+ ["squf"] = 9642,
["EmptyVerySmallSquare"] = 9643,
["rect"] = 9645,
["marker"] = 9646,
["fltns"] = 9649,
- ["xutri"] = 9651,
["bigtriangleup"] = 9651,
- ["utrif"] = 9652,
+ ["xutri"] = 9651,
["blacktriangle"] = 9652,
- ["utri"] = 9653,
+ ["utrif"] = 9652,
["triangle"] = 9653,
- ["rtrif"] = 9656,
+ ["utri"] = 9653,
["blacktriangleright"] = 9656,
+ ["rtrif"] = 9656,
["rtri"] = 9657,
["triangleright"] = 9657,
- ["xdtri"] = 9661,
["bigtriangledown"] = 9661,
- ["dtrif"] = 9662,
+ ["xdtri"] = 9661,
["blacktriangledown"] = 9662,
+ ["dtrif"] = 9662,
["dtri"] = 9663,
["triangledown"] = 9663,
- ["ltrif"] = 9666,
["blacktriangleleft"] = 9666,
+ ["ltrif"] = 9666,
["ltri"] = 9667,
["triangleleft"] = 9667,
["loz"] = 9674,
["lozenge"] = 9674,
["cir"] = 9675,
["tridot"] = 9708,
- ["xcirc"] = 9711,
["bigcirc"] = 9711,
+ ["xcirc"] = 9711,
["ultri"] = 9720,
["urtri"] = 9721,
["lltri"] = 9722,
["EmptySmallSquare"] = 9723,
["FilledSmallSquare"] = 9724,
- ["starf"] = 9733,
["bigstar"] = 9733,
+ ["starf"] = 9733,
["star"] = 9734,
["phone"] = 9742,
["female"] = 9792,
@@ -1805,8 +1854,8 @@ local character_entities = {
["clubsuit"] = 9827,
["hearts"] = 9829,
["heartsuit"] = 9829,
- ["diams"] = 9830,
["diamondsuit"] = 9830,
+ ["diams"] = 9830,
["sung"] = 9834,
["flat"] = 9837,
["natur"] = 9838,
@@ -1821,51 +1870,53 @@ local character_entities = {
["VerticalSeparator"] = 10072,
["lbbrk"] = 10098,
["rbbrk"] = 10099,
- ["lobrk"] = 10214,
+ ["bsolhsub"] = 10184,
+ ["suphsol"] = 10185,
["LeftDoubleBracket"] = 10214,
- ["robrk"] = 10215,
+ ["lobrk"] = 10214,
["RightDoubleBracket"] = 10215,
- ["lang"] = 10216,
+ ["robrk"] = 10215,
["LeftAngleBracket"] = 10216,
+ ["lang"] = 10216,
["langle"] = 10216,
- ["rang"] = 10217,
["RightAngleBracket"] = 10217,
+ ["rang"] = 10217,
["rangle"] = 10217,
["Lang"] = 10218,
["Rang"] = 10219,
["loang"] = 10220,
["roang"] = 10221,
- ["xlarr"] = 10229,
- ["longleftarrow"] = 10229,
["LongLeftArrow"] = 10229,
- ["xrarr"] = 10230,
- ["longrightarrow"] = 10230,
+ ["longleftarrow"] = 10229,
+ ["xlarr"] = 10229,
["LongRightArrow"] = 10230,
- ["xharr"] = 10231,
- ["longleftrightarrow"] = 10231,
+ ["longrightarrow"] = 10230,
+ ["xrarr"] = 10230,
["LongLeftRightArrow"] = 10231,
- ["xlArr"] = 10232,
- ["Longleftarrow"] = 10232,
+ ["longleftrightarrow"] = 10231,
+ ["xharr"] = 10231,
["DoubleLongLeftArrow"] = 10232,
- ["xrArr"] = 10233,
- ["Longrightarrow"] = 10233,
+ ["Longleftarrow"] = 10232,
+ ["xlArr"] = 10232,
["DoubleLongRightArrow"] = 10233,
- ["xhArr"] = 10234,
- ["Longleftrightarrow"] = 10234,
+ ["Longrightarrow"] = 10233,
+ ["xrArr"] = 10233,
["DoubleLongLeftRightArrow"] = 10234,
- ["xmap"] = 10236,
+ ["Longleftrightarrow"] = 10234,
+ ["xhArr"] = 10234,
["longmapsto"] = 10236,
+ ["xmap"] = 10236,
["dzigrarr"] = 10239,
["nvlArr"] = 10498,
["nvrArr"] = 10499,
["nvHarr"] = 10500,
["Map"] = 10501,
["lbarr"] = 10508,
- ["rbarr"] = 10509,
["bkarow"] = 10509,
+ ["rbarr"] = 10509,
["lBarr"] = 10510,
- ["rBarr"] = 10511,
["dbkarow"] = 10511,
+ ["rBarr"] = 10511,
["RBarr"] = 10512,
["drbkarow"] = 10512,
["DDotrahd"] = 10513,
@@ -1882,16 +1933,17 @@ local character_entities = {
["rarrbfs"] = 10528,
["nwarhk"] = 10531,
["nearhk"] = 10532,
- ["searhk"] = 10533,
["hksearow"] = 10533,
- ["swarhk"] = 10534,
+ ["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,
@@ -1937,10 +1989,10 @@ local character_entities = {
["llhard"] = 10603,
["rharul"] = 10604,
["lrhard"] = 10605,
- ["udhar"] = 10606,
["UpEquilibrium"] = 10606,
- ["duhar"] = 10607,
+ ["udhar"] = 10606,
["ReverseUpEquilibrium"] = 10607,
+ ["duhar"] = 10607,
["RoundImplies"] = 10608,
["erarr"] = 10609,
["simrarr"] = 10610,
@@ -2007,30 +2059,31 @@ local character_entities = {
["trisb"] = 10701,
["rtriltri"] = 10702,
["LeftTriangleBar"] = 10703,
+ ["NotLeftTriangleBar"] = {10703, 824},
+ ["NotRightTriangleBar"] = {10704, 824},
["RightTriangleBar"] = 10704,
- ["race"] = 10714,
["iinfin"] = 10716,
["infintie"] = 10717,
["nvinfin"] = 10718,
["eparsl"] = 10723,
["smeparsl"] = 10724,
["eqvparsl"] = 10725,
- ["lozf"] = 10731,
["blacklozenge"] = 10731,
+ ["lozf"] = 10731,
["RuleDelayed"] = 10740,
["dsol"] = 10742,
- ["xodot"] = 10752,
["bigodot"] = 10752,
- ["xoplus"] = 10753,
+ ["xodot"] = 10752,
["bigoplus"] = 10753,
- ["xotime"] = 10754,
+ ["xoplus"] = 10753,
["bigotimes"] = 10754,
- ["xuplus"] = 10756,
+ ["xotime"] = 10754,
["biguplus"] = 10756,
- ["xsqcup"] = 10758,
+ ["xuplus"] = 10756,
["bigsqcup"] = 10758,
- ["qint"] = 10764,
+ ["xsqcup"] = 10758,
["iiiint"] = 10764,
+ ["qint"] = 10764,
["fpartint"] = 10765,
["cirfnint"] = 10768,
["awint"] = 10769,
@@ -2062,8 +2115,8 @@ local character_entities = {
["triplus"] = 10809,
["triminus"] = 10810,
["tritime"] = 10811,
- ["iprod"] = 10812,
["intprod"] = 10812,
+ ["iprod"] = 10812,
["amalg"] = 10815,
["capdot"] = 10816,
["ncup"] = 10818,
@@ -2093,27 +2146,35 @@ local character_entities = {
["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,
- ["eDDot"] = 10871,
["ddotseq"] = 10871,
+ ["eDDot"] = 10871,
["equivDD"] = 10872,
["ltcir"] = 10873,
["gtcir"] = 10874,
["ltquest"] = 10875,
["gtquest"] = 10876,
- ["les"] = 10877,
["LessSlantEqual"] = 10877,
+ ["NotLessSlantEqual"] = {10877, 824},
["leqslant"] = 10877,
- ["ges"] = 10878,
+ ["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,
@@ -2157,7 +2218,9 @@ local character_entities = {
["simlE"] = 10911,
["simgE"] = 10912,
["LessLess"] = 10913,
+ ["NotNestedLessLess"] = {10913, 824},
["GreaterGreater"] = 10914,
+ ["NotNestedGreaterGreater"] = {10914, 824},
["glj"] = 10916,
["gla"] = 10917,
["ltcc"] = 10918,
@@ -2167,26 +2230,34 @@ local character_entities = {
["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,
- ["PrecedesEqual"] = 10927,
+ ["NotSucceedsEqual"] = {10928, 824},
+ ["SucceedsEqual"] = 10928,
+ ["nsce"] = {10928, 824},
+ ["nsucceq"] = {10928, 824},
["sce"] = 10928,
["succeq"] = 10928,
- ["SucceedsEqual"] = 10928,
["prE"] = 10931,
["scE"] = 10932,
- ["prnE"] = 10933,
["precneqq"] = 10933,
+ ["prnE"] = 10933,
["scnE"] = 10934,
["succneqq"] = 10934,
["prap"] = 10935,
["precapprox"] = 10935,
["scap"] = 10936,
["succapprox"] = 10936,
- ["prnap"] = 10937,
["precnapprox"] = 10937,
+ ["prnap"] = 10937,
["scnap"] = 10938,
["succnapprox"] = 10938,
["Pr"] = 10939,
@@ -2199,16 +2270,24 @@ local character_entities = {
["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,
@@ -2237,6 +2316,7 @@ local character_entities = {
["topcir"] = 10993,
["nhpar"] = 10994,
["parsim"] = 10995,
+ ["nparsl"] = {11005, 8421},
["parsl"] = 11005,
["fflig"] = 64256,
["filig"] = 64257,
@@ -2378,22 +2458,45 @@ local character_entities = {
["zopf"] = 120171,
}
function entities.dec_entity(s)
- return unicode.utf8.char(tonumber(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)
- return unicode.utf8.char(tonumber("0x"..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 n = character_entities[s]
+function entities.hex_entity_with_x_char(x, s)
+ local n = tonumber("0x"..s)
if n == nil then
- return "&" .. s .. ";"
+ 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)
@@ -2441,7 +2544,18 @@ function M.writer.new(options)
if not self.is_writing then return "" end
return "\\markdownRendererInterblockSeparator\n{}"
end
- self.hard_line_break = "\\markdownRendererHardLineBreak\n{}"
+ function self.paragraphsep()
+ if not self.is_writing then return "" end
+ return "\\markdownRendererParagraphSeparator\n{}"
+ 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
@@ -2475,11 +2589,18 @@ function M.writer.new(options)
["|"] = "\\markdownRendererPipe{}",
[entities.hex_entity('0000')] = "\\markdownRendererReplacementCharacter{}",
}
- local escape_typographic_text = util.escaper(
+ 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 = util.escaper(
+ local escape_programmatic_text = create_escaper(
self.escaped_uri_chars, self.escaped_minimal_strings)
- local escape_minimal = util.escaper(
+ local escape_minimal = create_escaper(
{}, self.escaped_minimal_strings)
self.escape = escape_typographic_text
self.math = escape_minimal
@@ -2487,12 +2608,15 @@ function M.writer.new(options)
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,
@@ -2508,6 +2632,7 @@ function M.writer.new(options)
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,
@@ -2525,6 +2650,7 @@ function M.writer.new(options)
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,
@@ -2545,7 +2671,9 @@ function M.writer.new(options)
if not self.is_writing then return "" end
local buffer = {}
for _,item in ipairs(items) do
- buffer[#buffer + 1] = self.bulletitem(item)
+ if item ~= "" then
+ buffer[#buffer + 1] = self.bulletitem(item)
+ end
end
local contents = util.intersperse(buffer,"\n")
if tight and options.tightLists then
@@ -2565,8 +2693,10 @@ function M.writer.new(options)
local buffer = {}
local num = startnum
for _,item in ipairs(items) do
- buffer[#buffer + 1] = self.ordereditem(item,num)
- if num ~= nil then
+ if item ~= "" then
+ buffer[#buffer + 1] = self.ordereditem(item,num)
+ end
+ if num ~= nil and item ~= "" then
num = num + 1
end
end
@@ -2589,14 +2719,11 @@ function M.writer.new(options)
end
end
function self.inline_html_comment(contents)
+ if self.flatten_inlines then return contents end
return {"\\markdownRendererInlineHtmlComment{",contents,"}"}
end
- function self.block_html_comment(contents)
- if not self.is_writing then return "" end
- return {"\\markdownRendererBlockHtmlCommentBegin\n",contents,
- "\n\\markdownRendererBlockHtmlCommentEnd "}
- 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)
@@ -2605,6 +2732,7 @@ function M.writer.new(options)
return {"\\markdownRendererInputBlockHtmlElement{",name,"}"}
end
function self.emphasis(s)
+ if self.flatten_inlines then return s end
return {"\\markdownRendererEmphasis{",s,"}"}
end
function self.tickbox(f)
@@ -2617,10 +2745,11 @@ function M.writer.new(options)
end
end
function self.strong(s)
+ if self.flatten_inlines then return s end
return {"\\markdownRendererStrongEmphasis{",s,"}"}
end
function self.blockquote(s)
- if #util.rope_to_string(s) == 0 then return "" end
+ if not self.is_writing then return "" end
return {"\\markdownRendererBlockQuoteBegin\n",s,
"\n\\markdownRendererBlockQuoteEnd "}
end
@@ -2640,17 +2769,24 @@ function M.writer.new(options)
return buf
end
- function self.attributes(attributes)
- local expanded_attributes = {}
- local key_value_regex = "([^= ]+)%s*=%s*(.*)"
+ 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) do
- if attribute:sub(1, 1) == "#" or attribute:sub(1, 1) == "." then
- table.insert(expanded_attributes, attribute)
+ 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(expanded_attributes, "#" .. value)
+ table.insert(normalized_attributes, "#" .. value)
elseif key:lower() == "class" then
local classes = {}
for class in value:gmatch("%S+") do
@@ -2658,23 +2794,67 @@ function M.writer.new(options)
end
table.sort(classes)
for _, class in ipairs(classes) do
- table.insert(expanded_attributes, "." .. class)
+ table.insert(normalized_attributes, "." .. class)
end
else
- table.insert(expanded_attributes, attribute)
+ table.insert(normalized_attributes, attribute)
end
end
end
- table.sort(expanded_attributes)
- local buf = {}
- local seen = {}
- for _, attribute in ipairs(expanded_attributes) do
- if seen[attribute] ~= nil then
- goto continue -- prevent duplicate attributes
- else
- seen[attribute] = true
+ -- 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), "}"})
@@ -2686,12 +2866,14 @@ function M.writer.new(options)
table.insert(buf, {"\\markdownRendererAttributeKeyValue{",
key, "}{", value, "}"})
end
- ::continue::
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
@@ -2715,6 +2897,9 @@ function M.writer.new(options)
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
@@ -2732,7 +2917,6 @@ function M.writer.new(options)
end
if attributes["#" .. self.slice_begin_identifier] ~= nil and
self.slice_begin_type == "^" then
- self.is_writing = true
table.insert(buf, apply_attributes())
self.is_writing = true
end
@@ -2755,6 +2939,8 @@ function M.writer.new(options)
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
@@ -2775,47 +2961,151 @@ function M.writer.new(options)
end
return buf
end
- local current_heading_level = 0
+local function create_auto_identifier(s)
+ local buffer = {}
+ local prev_space = false
+ local letter_found = false
+
+ for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do
+ local char = utf8.char(code)
+
+ -- Remove everything up to the first letter.
+ if not letter_found then
+ local is_letter = unicode.utf8.match(char, "%a")
+ if is_letter then
+ letter_found = true
+ else
+ goto continue
+ end
+ end
+
+ -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods.
+ if not unicode.utf8.match(char, "[%w_%-%.%s]") then
+ goto continue
+ end
+
+ -- Replace all spaces and newlines with hyphens.
+ if unicode.utf8.match(char, "[%s\n]") then
+ char = "-"
+ if prev_space then
+ goto continue
+ else
+ prev_space = true
+ end
+ else
+ -- Convert all alphabetic characters to lowercase.
+ char = unicode.utf8.lower(char)
+ prev_space = false
+ end
+
+ table.insert(buffer, char)
+
+ ::continue::
+ end
+
+ if prev_space then
+ table.remove(buffer)
+ end
+
+ local identifier = #buffer == 0 and "section" or table.concat(buffer, "")
+ return identifier
+end
+local function create_gfm_auto_identifier(s)
+ local buffer = {}
+ local prev_space = false
+ local letter_found = false
+
+ for _, code in utf8.codes(uni_algos.normalize.NFC(s)) do
+ local char = utf8.char(code)
+
+ -- Remove everything up to the first non-space.
+ if not letter_found then
+ local is_letter = unicode.utf8.match(char, "%S")
+ if is_letter then
+ letter_found = true
+ else
+ goto continue
+ end
+ end
+
+ -- Remove all non-alphanumeric characters, except underscores and hyphens.
+ if not unicode.utf8.match(char, "[%w_%-%s]") then
+ prev_space = false
+ goto continue
+ end
+
+ -- Replace all spaces and newlines with hyphens.
+ if unicode.utf8.match(char, "[%s\n]") then
+ char = "-"
+ if prev_space then
+ goto continue
+ else
+ prev_space = true
+ end
+ else
+ -- Convert all alphabetic characters to lowercase.
+ char = unicode.utf8.lower(char)
+ prev_space = false
+ end
+
+ table.insert(buffer, char)
+
+ ::continue::
+ end
+
+ if prev_space then
+ table.remove(buffer)
+ end
+
+ local identifier = #buffer == 0 and "section" or table.concat(buffer, "")
+ return identifier
+end
function self.heading(s, level, attributes)
local buf = {}
+ local flat_text, inlines = table.unpack(s)
-- push empty attributes for implied sections
- while current_heading_level < level - 1 do
+ while self.attribute_type_levels["heading"] < level - 1 do
table.insert(buf,
self.push_attributes("heading",
nil,
"\\markdownRendererSectionBegin\n",
"\n\\markdownRendererSectionEnd "))
- current_heading_level = current_heading_level + 1
end
-- pop attributes for sections that have ended
- while current_heading_level >= level do
+ while self.attribute_type_levels["heading"] >= level do
table.insert(buf, self.pop_attributes("heading"))
- current_heading_level = current_heading_level - 1
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, "\\markdownRendererSectionBegin\n")
- if options.headerAttributes and attributes ~= nil and #attributes > 0 then
- table.insert(start_output,
- "\\markdownRendererHeaderAttributeContextBegin\n")
- table.insert(start_output, self.attributes(attributes))
- table.insert(end_output,
- "\n\\markdownRendererHeaderAttributeContextEnd ")
- end
table.insert(end_output, "\n\\markdownRendererSectionEnd ")
table.insert(buf, self.push_attributes("heading",
- attributes,
+ normalized_attributes,
start_output,
end_output))
- current_heading_level = current_heading_level + 1
- assert(current_heading_level == level)
+ 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
- -- produce the renderer
local cmd
level = level + options.shiftHeadings
if level <= 1 then
@@ -2834,7 +3124,11 @@ function M.writer.new(options)
cmd = ""
end
if self.is_writing then
- table.insert(buf, {cmd, "{", s, "}"})
+ table.insert(buf, {cmd, "{", inlines, "}"})
+ end
+
+ if self.is_writing and #normalized_attributes > 0 then
+ table.insert(buf, "\\markdownRendererHeaderAttributeContextEnd ")
end
return buf
@@ -2842,6 +3136,7 @@ function M.writer.new(options)
function self.get_state()
return {
is_writing=self.is_writing,
+ flatten_inlines=self.flatten_inlines,
active_attributes={table.unpack(self.active_attributes)},
}
end
@@ -2899,14 +3194,13 @@ parsers.tilde = P("~")
parsers.backslash = P("\\")
parsers.tab = P("\t")
parsers.newline = P("\n")
-parsers.tightblocksep = P("\001")
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^0
+ * (parsers.alphanumeric + parsers.dash)^0
parsers.internal_punctuation = S(":;,.?")
parsers.doubleasterisks = P("**")
@@ -2928,8 +3222,7 @@ parsers.nonspacechar = parsers.any - parsers.spacing
parsers.optionalspace = parsers.spacechar^0
parsers.normalchar = parsers.any - (V("SpecialChar")
- + parsers.spacing
- + parsers.tightblocksep)
+ + parsers.spacing)
parsers.eof = -parsers.any
parsers.nonindentspace = parsers.space^-3 * - parsers.spacechar
parsers.indent = parsers.space^-3 * parsers.tab
@@ -2949,6 +3242,549 @@ 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)
+ local new_index = i
+
+ local preceding_indentation = 0
+ local current_trail = {}
+
+ local blank_starter = left_blank_starter(indent_table)
+
+ 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
+ end
+
+ return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank)
+ 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
+ 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
+ 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}
+ new_index = next_index
+ end
+
+ return true, new_index, current_trail
+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 = traverse_indent(s, i, indent_table, is_optional, is_blank)
+
+ if passes then
+ 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
@@ -2995,6 +3831,7 @@ 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
@@ -3006,10 +3843,16 @@ parsers.attribute_value = ( (parsers.dquote / "")
* (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.period)
- * parsers.attribute_key)
+ + 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)
@@ -3024,7 +3867,7 @@ parsers.attributes = parsers.lbrace
parsers.raw_attribute = parsers.lbrace
* parsers.optionalspace
* parsers.equal
- * C(parsers.attribute_key)
+ * C(parsers.attribute_raw)
* parsers.optionalspace
* parsers.rbrace
@@ -3035,18 +3878,31 @@ parsers.indented_blocks = function(bl)
* (parsers.blankline^1 * parsers.indent * -parsers.blankline * bl)^0
* (parsers.blankline^1 + parsers.eof) )
end
-parsers.bulletchar = C(parsers.plus + parsers.asterisk + parsers.dash)
+local function repeat_between(pattern, min, max)
+ return -pattern^(max + 1) * pattern^min
+end
-parsers.bullet = ( parsers.bulletchar * #parsers.spacing
- * (parsers.tab + parsers.space^-3)
- + parsers.space * parsers.bulletchar * #parsers.spacing
- * (parsers.tab + parsers.space^-2)
- + parsers.space * parsers.space * parsers.bulletchar
- * #parsers.spacing
- * (parsers.tab + parsers.space^-1)
- + parsers.space * parsers.space * parsers.space
- * parsers.bulletchar * #parsers.spacing
- )
+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
@@ -3063,18 +3919,439 @@ local function captures_equal_length(_,i,a,b)
return #a == #b and i
end
-parsers.closeticks = parsers.space^-1
- * Cmt(C(parsers.backtick^1)
- * Cb("ticks"), captures_equal_length)
+parsers.closeticks = Cmt(C(parsers.backtick^1)
+ * Cb("ticks"), captures_equal_length)
-parsers.intickschar = (parsers.any - S(" \n\r`"))
- + (parsers.newline * -parsers.blankline)
- + (parsers.space - parsers.closeticks)
+parsers.intickschar = (parsers.any - S("\n\r`"))
+ + V("NoSoftLineBreakEndline")
+ (parsers.backtick^1 - parsers.closeticks)
-parsers.inticks = parsers.openticks * parsers.space^-1
- * C(parsers.intickschar^0) * parsers.closeticks
-parsers.leader = parsers.space^-3
+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 - P("--")
+ - 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
@@ -3104,161 +4381,89 @@ parsers.dquoted = P{ parsers.dquote * parsers.alphanumeric
) + V(1))^0
* parsers.dquote }
--- bracketed tag for markdown links, allowing nested brackets:
-parsers.tag = parsers.lbracket
+parsers.link_text = parsers.lbracket
* Cs((parsers.alphanumeric^1
- + parsers.bracketed
- + parsers.inticks
- + ( parsers.backslash / "" * parsers.rbracket
- + parsers.any
- - (parsers.rbracket + parsers.blankline^2)))^0)
+ + 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
- + Cs((parsers.inparens + (parsers.anyescaped
- - parsers.spacing
- - parsers.rparent))^1)
-
--- quoted text, possibly with nested quotes:
-parsers.title_s = parsers.squote * Cs(((parsers.anyescaped-parsers.squote)
- + parsers.squoted)^0)
- * parsers.squote
-
-parsers.title_d = parsers.dquote * Cs(((parsers.anyescaped-parsers.dquote)
- + parsers.dquoted)^0)
- * parsers.dquote
+ + -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.inparens + (parsers.anyescaped-parsers.rparent))^0)
+ * 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.spnl * parsers.title * parsers.spacechar^0
+ = parsers.spnlc * parsers.title * parsers.spacechar^0
+ Cc("")
-parsers.indirect_link
- = parsers.tag
- * ( C(parsers.spnl) * parsers.tag
- + Cc(nil) * Cc(nil) -- always produce exactly two captures
- )
-
-parsers.indirect_image
- = parsers.exclamation * parsers.indirect_link
--- 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.block_keyword =
- parsers.keyword_exact("address") + parsers.keyword_exact("blockquote") +
- parsers.keyword_exact("center") + parsers.keyword_exact("del") +
- parsers.keyword_exact("dir") + parsers.keyword_exact("div") +
- parsers.keyword_exact("p") + parsers.keyword_exact("pre") +
- parsers.keyword_exact("li") + parsers.keyword_exact("ol") +
- parsers.keyword_exact("ul") + parsers.keyword_exact("dl") +
- parsers.keyword_exact("dd") + parsers.keyword_exact("form") +
- parsers.keyword_exact("fieldset") + parsers.keyword_exact("isindex") +
- parsers.keyword_exact("ins") + parsers.keyword_exact("menu") +
- parsers.keyword_exact("noframes") + 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("hr") + parsers.keyword_exact("script") +
- parsers.keyword_exact("noscript") + parsers.keyword_exact("table") +
- parsers.keyword_exact("tbody") + parsers.keyword_exact("tfoot") +
- parsers.keyword_exact("thead") + parsers.keyword_exact("th") +
- parsers.keyword_exact("td") + parsers.keyword_exact("tr")
-
--- There is no reason to support bad html, so we expect quoted attributes
-parsers.htmlattributevalue
- = parsers.squote * (parsers.any - (parsers.blankline
- + parsers.squote))^0
- * parsers.squote
- + parsers.dquote * (parsers.any - (parsers.blankline
- + parsers.dquote))^0
- * parsers.dquote
-
-parsers.htmlattribute = parsers.spacing^1
- * (parsers.alphanumeric + S("_-"))^1
- * parsers.sp * parsers.equal * parsers.sp
- * parsers.htmlattributevalue
-
-parsers.htmlcomment = P("<!--")
- * parsers.optionalspace
- * Cs((parsers.any - parsers.optionalspace * P("-->"))^0)
- * parsers.optionalspace
- * P("-->")
-
-parsers.htmlinstruction = P("<?") * (parsers.any - P("?>"))^0 * P("?>")
-
-parsers.openelt_any = parsers.less * parsers.keyword * parsers.htmlattribute^0
- * parsers.sp * parsers.more
-
-parsers.openelt_exact = function(s)
- return parsers.less * parsers.sp * parsers.keyword_exact(s)
- * parsers.htmlattribute^0 * parsers.sp * parsers.more
-end
-
-parsers.openelt_block = parsers.sp * parsers.block_keyword
- * parsers.htmlattribute^0 * parsers.sp * parsers.more
-
-parsers.closeelt_any = parsers.less * parsers.sp * parsers.slash
- * parsers.keyword * parsers.sp * parsers.more
-
-parsers.closeelt_exact = function(s)
- return parsers.less * parsers.sp * parsers.slash * parsers.keyword_exact(s)
- * parsers.sp * parsers.more
-end
-
-parsers.emptyelt_any = parsers.less * parsers.sp * parsers.keyword
- * parsers.htmlattribute^0 * parsers.sp * parsers.slash
- * parsers.more
-
-parsers.emptyelt_block = parsers.less * parsers.sp * parsers.block_keyword
- * parsers.htmlattribute^0 * parsers.sp * parsers.slash
- * parsers.more
-
-parsers.displaytext = (parsers.any - parsers.less)^1
-
--- return content between two matched HTML tags
-parsers.in_matched = function(s)
- return { parsers.openelt_exact(s)
- * (V(1) + parsers.displaytext
- + (parsers.less - parsers.closeelt_exact(s)))^0
- * parsers.closeelt_exact(s) }
-end
-
-local function parse_matched_tags(s,pos)
- local t = string.lower(lpeg.match(C(parsers.keyword),s,pos))
- return lpeg.match(parsers.in_matched(t),s,pos-1)
-end
-
-parsers.in_matched_block_tags = parsers.less
- * Cmt(#parsers.openelt_block, parse_matched_tags)
-
-parsers.hexentity = parsers.ampersand * parsers.hash * S("Xx")
- * C(parsers.hexdigit^1) * parsers.semicolon
-parsers.decentity = parsers.ampersand * parsers.hash
- * C(parsers.digit^1) * parsers.semicolon
-parsers.tagentity = parsers.ampersand * C(parsers.alphanumeric^1)
- * parsers.semicolon
-- parse a reference definition: [foo]: /bar "title"
-parsers.define_reference_parser = parsers.leader * parsers.tag * parsers.colon
- * parsers.spacechar^0 * parsers.url
- * parsers.optionaltitle
+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")
-parsers.IndentedInline = V("IndentedInline")
-- parse many p between starter and ender
parsers.between = function(p, starter, ender)
@@ -3266,41 +4471,34 @@ parsers.between = function(p, starter, ender)
return (starter * #parsers.nonspacechar * Ct(p * (p - ender2)^0) * ender2)
end
-parsers.urlchar = parsers.anyescaped
- - parsers.newline
- - parsers.more
-
-parsers.auto_link_url = parsers.less
- * C( parsers.alphanumeric^1 * P("://")
- * parsers.urlchar^1)
- * parsers.more
-
-parsers.auto_link_email
- = parsers.less
- * C((parsers.alphanumeric + S("-._+"))^1
- * P("@") * parsers.urlchar^1)
- * parsers.more
-
-parsers.auto_link_relative_reference
- = parsers.less
- * C(parsers.urlchar^1)
- * parsers.more
-
parsers.lineof = function(c)
- return (parsers.leader * (P(c) * parsers.optionalspace)^3
- * (parsers.newline * parsers.blankline^1
- + parsers.newline^-1 * parsers.eof))
+ 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.equal^1 * Cc(1) + parsers.dash^1 * Cc(2)
+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]*\n$","")
+ 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 = {}
@@ -3319,7 +4517,7 @@ function M.reader.new(writer, options)
tag = util.rope_to_string(tag)
tag = tag:gsub("[ \n\r\t]+", " ")
tag = tag:gsub("^ ", ""):gsub(" $", "")
- tag = uni_case.casefold(tag, true, false)
+ tag = uni_algos.case.casefold(tag, true, false)
return tag
end
local function iterlines(s, f)
@@ -3386,11 +4584,6 @@ function M.reader.new(writer, options)
return parsers.inlines
end, false)
- self.create_parser("parse_inlines_no_link",
- function()
- return parsers.inlines_no_link
- end, false)
-
self.create_parser("parse_inlines_no_inline_note",
function()
return parsers.inlines_no_inline_note
@@ -3405,139 +4598,826 @@ function M.reader.new(writer, options)
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 = C(parsers.dig^3 * parsers.period) * #parsers.spacing
- + C(parsers.dig^2 * parsers.period) * #parsers.spacing
- * (parsers.tab + parsers.space^1)
- + C(parsers.dig * parsers.period) * #parsers.spacing
- * (parsers.tab + parsers.space^-2)
- + parsers.space * C(parsers.dig^2 * parsers.period)
- * #parsers.spacing
- + parsers.space * C(parsers.dig * parsers.period)
- * #parsers.spacing
- * (parsers.tab + parsers.space^-1)
- + parsers.space * parsers.space * C(parsers.dig^1
- * parsers.period) * #parsers.spacing
- -- strip off leading > and indents, and run through blocks
- parsers.blockquote_body = ((parsers.leader * parsers.more * parsers.space^-1)/""
- * parsers.linechar^0 * parsers.newline)^1
- * (-V("BlockquoteExceptions") * parsers.linechar^1
- * parsers.newline)^0
+ 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.blockquote_body
- * (parsers.blankline^0 / "")
+ 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
+ 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
+
+ local function check_preceding_unicode_punctuation(s, i)
+ return check_unicode_type(s, i, -4, -1, "%p")
+ 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, "%p")
+ 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,
+ function self.register_link(_, tag, url, title,
attributes)
- tag = self.normalize_tag(tag)
- references[tag] = {
- url = url,
- title = title,
- attributes = 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(label, sps, tag,
- attributes)
- local tagpart
- if not tag then
- tag = label
- tagpart = ""
- elseif tag == "" then
- tag = label
- tagpart = "[]"
- else
- tagpart = {
- "[",
- self.parser_functions.parse_inlines(tag),
- "]"
- }
- end
- if sps then
- tagpart = {sps, tagpart}
+ 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
- tag = self.normalize_tag(tag)
- local r = references[tag]
- if r then
- local merged_attributes = {}
- for _, attribute in ipairs(r.attributes or {}) do
- table.insert(merged_attributes, attribute)
+ 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
- for _, attribute in ipairs(attributes or {}) do
- table.insert(merged_attributes, attribute)
+ 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
- if #merged_attributes == 0 then
- merged_attributes = nil
+ 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
- return {
- url = r.url,
- title = r.title,
- attributes = merged_attributes,
- }
- else
- return nil, {
- "[",
- self.parser_functions.parse_inlines(label),
- "]",
- tagpart
- }
end
end
- -- lookup link reference and return a link, if the reference is found,
- -- or a bracketed label otherwise.
- local function indirect_link(label, sps, tag)
- return writer.defer_call(function()
- local r,fallback = self.lookup_reference(label, sps, tag)
- if r then
- return writer.link(
- self.parser_functions.parse_inlines_no_link(label),
- r.url, r.title)
- else
- return fallback
+ 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
end
- -- lookup image reference and return an image, if the reference is found,
- -- or a bracketed label otherwise.
- local function indirect_image(label, sps, tag)
- return writer.defer_call(function()
- local r,fallback = self.lookup_reference(label, sps, tag)
- if r then
- return writer.image(writer.string(label), r.url, r.title)
- else
- return {"!", fallback}
+ 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
- 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
- parsers.direct_link_tail = parsers.spnl
- * parsers.lparent
- * (parsers.url + Cc("")) -- link can be empty [foo]()
- * parsers.optionaltitle
- * parsers.rparent
+ 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)
- parsers.direct_link = (parsers.tag / self.parser_functions.parse_inlines_no_link)
- * parsers.direct_link_tail
+ 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.direct_image = parsers.exclamation
- * (parsers.tag / self.parser_functions.parse_inlines)
- * parsers.direct_link_tail
parsers.Str = (parsers.normalchar * (parsers.normalchar + parsers.at)^0)
/ writer.string
- parsers.Symbol = (V("SpecialChar") - parsers.tightblocksep)
+ parsers.Symbol = (parsers.backtick^1 + V("SpecialChar"))
/ writer.string
parsers.Ellipsis = P("...") / writer.ellipsis
@@ -3549,88 +5429,116 @@ function M.reader.new(writer, options)
if options.blankBeforeBlockquote then
parsers.bqstart = parsers.fail
else
- parsers.bqstart = parsers.more
+ parsers.bqstart = parsers.blockquote_start
end
if options.blankBeforeHeading then
parsers.headerstart = parsers.fail
else
- parsers.headerstart = parsers.hash
- + (parsers.line * (parsers.equal^1 + parsers.dash^1)
- * parsers.optionalspace * parsers.newline)
+ 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.tightblocksep -- nested list
+ parsers.eof -- end of document
+ parsers.bqstart
+ + parsers.thematic_break_lines
+ + parsers.interrupting_bullets
+ + parsers.interrupting_enumerators
+ parsers.headerstart
+ + parsers.html_interrupting
- parsers.Endline = parsers.newline
+ parsers.NoSoftLineBreakEndlineExceptions = parsers.EndlineExceptions
+
+ parsers.endline = parsers.newline
+ * (parsers.check_minimal_indent
+ * -V("EndlineExceptions")
+ + parsers.check_optional_indent
* -V("EndlineExceptions")
- * parsers.spacechar^0
- / (options.hardLineBreaks and writer.hard_line_break
- or writer.space)
+ * -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 / ""
+ 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
- * parsers.optionalspace
- / (options.hardLineBreaks
- and writer.hard_line_break
- or writer.space)
- + parsers.spacechar^1 * parsers.optionalspace
- / writer.space
+ / 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.newline
- * -V("EndlineExceptions")
- * parsers.spacechar^0
- / (options.hardLineBreaks and writer.hard_line_break
- or writer.nbsp)
+ = parsers.endline
+ / writer.soft_line_break
parsers.NonbreakingSpace
- = parsers.spacechar^2 * parsers.Endline / writer.hard_line_break
+ = parsers.spacechar^2 * parsers.Endline
+ / writer.hard_line_break
+ parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / ""
+ parsers.spacechar^1 * parsers.Endline
* parsers.optionalspace
- / (options.hardLineBreaks
- and writer.hard_line_break
- or writer.nbsp)
+ / writer.soft_line_break
+ parsers.spacechar^1 * parsers.optionalspace
/ writer.nbsp
- if options.underscores then
- parsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks,
- parsers.doubleasterisks)
- + parsers.between(parsers.Inline, parsers.doubleunderscores,
- parsers.doubleunderscores)
- ) / writer.strong
-
- parsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk,
- parsers.asterisk)
- + parsers.between(parsers.Inline, parsers.underscore,
- parsers.underscore)
- ) / writer.emphasis
- else
- parsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks,
- parsers.doubleasterisks)
- ) / writer.strong
-
- parsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk,
- parsers.asterisk)
- ) / writer.emphasis
- end
-
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,
@@ -3648,77 +5556,58 @@ end
= parsers.auto_link_relative_reference
/ self.auto_link_url
- parsers.DirectLink = parsers.direct_link
- / writer.link
-
- parsers.IndirectLink = parsers.indirect_link
- / indirect_link
-
- -- parse a link or image (direct or indirect)
- parsers.Link = parsers.DirectLink + parsers.IndirectLink
-
- parsers.DirectImage = parsers.direct_image
- / writer.image
-
- parsers.IndirectImage = parsers.indirect_image
- / indirect_image
-
- parsers.Image = parsers.DirectImage + parsers.IndirectImage
-
- -- avoid parsing long strings of * or _ as emph/strong
- parsers.UlOrStarLine = parsers.asterisk^4 + parsers.underscore^4
- / writer.string
+ 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 = parsers.emptyelt_any / writer.inline_html_tag
- + (parsers.htmlcomment / self.parser_functions.parse_inlines_no_html)
- / writer.inline_html_comment
- + parsers.htmlinstruction
- + parsers.openelt_any / writer.inline_html_tag
- + parsers.closeelt_any / writer.inline_html_tag
-
- parsers.HtmlEntity = parsers.hexentity / entities.hex_entity / writer.string
- + parsers.decentity / entities.dec_entity / writer.string
- + parsers.tagentity / entities.char_entity / writer.string
- parsers.DisplayHtml = (parsers.htmlcomment / self.parser_functions.parse_blocks_nested)
- / writer.block_html_comment
- + parsers.emptyelt_block / writer.block_html_element
- + parsers.openelt_exact("hr") / writer.block_html_element
- + parsers.in_matched_block_tags / writer.block_html_element
- + parsers.htmlinstruction
-
- parsers.Verbatim = Cs( (parsers.blanklines
- * ((parsers.indentedline - parsers.blankline))^1)^1
- ) / self.expandtabs / writer.verbatim
-
- parsers.BlockquoteExceptions = parsers.leader * parsers.more
- + parsers.blankline
-
- parsers.Blockquote = Cs(parsers.blockquote_body^1)
- / self.parser_functions.parse_blocks_nested
+ 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.lineof(parsers.asterisk)
- + parsers.lineof(parsers.dash)
- + parsers.lineof(parsers.underscore)
- ) / writer.thematic_break
+ parsers.ThematicBreak = parsers.thematic_break_lines
+ / writer.thematic_break
parsers.Reference = parsers.define_reference_parser
- * parsers.blankline^1
/ self.register_link
- parsers.Paragraph = parsers.nonindentspace * Ct(parsers.Inline^1)
- * ( parsers.newline
- * ( parsers.blankline^1
- + #V("EndlineExceptions")
- )
- + parsers.eof)
- / writer.paragraph
+ 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
- parsers.starter = parsers.bullet + parsers.enumerator
if options.taskLists then
parsers.tickbox = ( parsers.ticked_box
@@ -3729,43 +5618,117 @@ end
parsers.tickbox = parsers.fail
end
- -- we use \001 as a separator between a tight list item and a
- -- nested list under it.
- parsers.NestedList = Cs((parsers.optionallyindentedline
- - parsers.starter)^1)
- / function(a) return "\001"..a end
-
- parsers.ListBlockLine = parsers.optionallyindentedline
- - parsers.blankline - (parsers.indent^-1
- * parsers.starter)
-
- parsers.ListBlock = parsers.line * parsers.ListBlockLine^0
-
- parsers.ListContinuationBlock = parsers.blanklines * (parsers.indent / "")
- * parsers.ListBlock
+ 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
- * (Cs(starter / "" * parsers.tickbox^-1 * parsers.ListBlock * parsers.NestedList^-1)
- / self.parser_functions.parse_blocks_nested)
- * -(parsers.blanklines * parsers.indent)
+ return -parsers.ThematicBreak
+ * parsers.add_indent(starter, "li")
+ * parsers.indented_content_tight
end
parsers.LooseListItem = function(starter)
- return -parsers.ThematicBreak
- * Cs( starter / "" * parsers.tickbox^-1 * parsers.ListBlock * Cc("\n")
- * (parsers.NestedList + parsers.ListContinuationBlock^0)
- * (parsers.blanklines / "\n\n")
- ) / self.parser_functions.parse_blocks_nested
+ return -parsers.ThematicBreak
+ * parsers.add_indent(starter, "li")
+ * parsers.indented_content_loose
+ * remove_indent("li")
end
- parsers.BulletList = ( Ct(parsers.TightListItem(parsers.bullet)^1) * Cc(true)
- * parsers.skipblanklines * -parsers.bullet
- + Ct(parsers.LooseListItem(parsers.bullet)^1) * Cc(false)
- * parsers.skipblanklines )
- / writer.bulletlist
+ 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,startnum)
+ 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
@@ -3777,33 +5740,64 @@ end
return writer.orderedlist(items,tight,startnum)
end
- parsers.OrderedList = Cg(parsers.enumerator, "listtype") *
- ( Ct(parsers.TightListItem(Cb("listtype"))
- * parsers.TightListItem(parsers.enumerator)^0)
- * Cc(true) * parsers.skipblanklines * -parsers.enumerator
- + Ct(parsers.LooseListItem(Cb("listtype"))
- * parsers.LooseListItem(parsers.enumerator)^0)
- * Cc(false) * parsers.skipblanklines
- ) * Cb("listtype") / ordered_list
+ 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")
- + (parsers.tightblocksep / "\n")
+ 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 = Cg(parsers.heading_start, "level")
- * parsers.optionalspace
- * (C(parsers.line)
+ 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
- / self.parser_functions.parse_inlines)
+ / parsers.parse_heading_text)
* Cb("level")
/ writer.heading
- parsers.SetextHeading = #(parsers.line * S("=-"))
- * Ct(parsers.linechar^1
- / self.parser_functions.parse_inlines)
- * parsers.newline
- * parsers.heading_level
- * parsers.optionalspace
+ 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
@@ -3861,12 +5855,32 @@ end
Blocks = V("InitializeState")
* ( V("ExpectedJekyllData")
- * (V("Blank")^0 / writer.interblocksep))^-1
+ * (V("Blank")^0 / writer.interblocksep)
+ )^-1
* V("Blank")^0
- * V("Block")^-1
- * ( V("Blank")^0 / writer.interblocksep
- * V("Block"))^0
- * V("Blank")^0 * parsers.eof,
+ * ( 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,
@@ -3878,23 +5892,25 @@ end
ThematicBreak = parsers.ThematicBreak,
BulletList = parsers.BulletList,
OrderedList = parsers.OrderedList,
- Heading = parsers.Heading,
DisplayHtml = parsers.DisplayHtml,
+ Heading = parsers.Heading,
Paragraph = parsers.Paragraph,
Plain = parsers.Plain,
EndlineExceptions = parsers.EndlineExceptions,
- BlockquoteExceptions = parsers.BlockquoteExceptions,
+ NoSoftLineBreakEndlineExceptions
+ = parsers.NoSoftLineBreakEndlineExceptions,
Str = parsers.Str,
Space = parsers.Space,
+ NoSoftLineBreakSpace = parsers.NoSoftLineBreakSpace,
OptionalIndent = parsers.OptionalIndent,
Endline = parsers.Endline,
- UlOrStarLine = parsers.UlOrStarLine,
- Strong = parsers.Strong,
- Emph = parsers.Emph,
- Link = parsers.Link,
- Image = parsers.Image,
+ EndlineNoSub = parsers.EndlineNoSub,
+ NoSoftLineBreakEndline
+ = parsers.NoSoftLineBreakEndline,
+ EndlineBreak = parsers.EndlineBreak,
+ LinkAndEmph = parsers.LinkAndEmph,
Code = parsers.Code,
AutoLinkUrl = parsers.AutoLinkUrl,
AutoLinkEmail = parsers.AutoLinkEmail,
@@ -3947,9 +5963,14 @@ end
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(Ct("") / value, name)
+ * 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)
@@ -4004,11 +6025,6 @@ end
assert(output_file:write(output))
assert(output_file:close())
end
- walkable_syntax["IndentedInline"] = util.table_copy(
- walkable_syntax["Inline"])
- self.insert_pattern(
- "IndentedInline instead of Space",
- "OptionalIndent")
for lhs, rule in pairs(walkable_syntax) do
syntax[lhs] = parsers.fail
for _, rhs in ipairs(rule) do
@@ -4056,6 +6072,15 @@ end
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)
@@ -4070,10 +6095,6 @@ end
* parsers.eof / "")
parsers.inlines = Ct(inlines_t)
- local inlines_no_link_t = util.table_copy(inlines_t)
- inlines_no_link_t.Link = parsers.fail
- parsers.inlines_no_link = Ct(inlines_no_link_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)
@@ -4088,6 +6109,11 @@ end
inlines_nbsp_t.Endline = parsers.NonbreakingEndline
inlines_nbsp_t.Space = parsers.NonbreakingSpace
parsers.inlines_nbsp = Ct(inlines_nbsp_t)
+
+ local inlines_no_link_or_emphasis_t = util.table_copy(inlines_t)
+ inlines_no_link_or_emphasis_t.LinkAndEmph = parsers.fail
+ inlines_no_link_or_emphasis_t.EndlineExceptions = parsers.EndlineExceptions - parsers.eof
+ parsers.inlines_no_link_or_emphasis = Ct(inlines_no_link_or_emphasis_t)
return function(input)
input = input:gsub("\r\n?", "\n")
if input:sub(-1) ~= "\n" then
@@ -4145,6 +6171,7 @@ M.extensions.bracketed_spans = function()
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,
@@ -4154,13 +6181,25 @@ M.extensions.bracketed_spans = function()
local parsers = self.parsers
local writer = self.writer
- local Span = parsers.between(parsers.Inline,
- parsers.lbracket,
- parsers.rbracket)
+ 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 after Emph",
+ self.insert_pattern("Inline before LinkAndEmph",
Span, "Span")
end
}
@@ -4170,11 +6209,24 @@ M.extensions.citations = function(citation_nbsps)
name = "built-in citations syntax extension",
extend_writer = function(self)
function self.citations(text_cites, cites)
- local buffer = {"\\markdownRenderer", text_cites and "TextCite" or "Cite",
- "{", #cites, "}"}
- for _,cite in ipairs(cites) do
- buffer[#buffer+1] = {cite.suppress_author and "-" or "+", "{",
- cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"}
+ 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
@@ -4201,42 +6253,51 @@ M.extensions.citations = function(citation_nbsps)
= Cs((parsers.alphanumeric^1
+ parsers.bracketed
+ parsers.inticks
+ + parsers.autolink
+ + V("InlineHtml")
+ + V("Space") + V("Endline")
+ (parsers.anyescaped
- - (parsers.rbracket + parsers.blankline^2))
+ - (parsers.newline + parsers.rbracket + parsers.blankline^2))
- (parsers.spnl * parsers.dash^-1 * parsers.at))^0)
local citation_body_postnote
= Cs((parsers.alphanumeric^1
+ parsers.bracketed
+ parsers.inticks
+ + parsers.autolink
+ + V("InlineHtml")
+ + V("Space") + V("Endline")
+ (parsers.anyescaped
- - (parsers.rbracket + parsers.semicolon
+ - (parsers.newline + parsers.rbracket + parsers.semicolon
+ parsers.blankline^2))
- (parsers.spnl * parsers.rbracket))^0)
local citation_body_chunk
= citation_body_prenote
- * parsers.spnl * citation_name
+ * parsers.spnlc * citation_name
* (parsers.internal_punctuation - parsers.semicolon)^-1
- * parsers.spnl * citation_body_postnote
+ * parsers.spnlc * citation_body_postnote
local citation_body
= citation_body_chunk
- * (parsers.semicolon * parsers.spnl
+ * (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.rbracket + parsers.at
+ - (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.sp * parsers.semicolon * parsers.spnl
+ * (parsers.sp * parsers.semicolon * parsers.spnlc
* citation_body_chunk)^0
local citations
@@ -4265,10 +6326,10 @@ M.extensions.citations = function(citation_nbsps)
end
local TextCitations
- = Ct((parsers.spnl
+ = Ct((parsers.spnlc
* Cc("")
* citation_name
- * ((parsers.spnl
+ * ((parsers.spnlc
* parsers.lbracket
* citation_headless_body
* parsers.rbracket) + Cc("")))^1)
@@ -4277,7 +6338,7 @@ M.extensions.citations = function(citation_nbsps)
end
local ParenthesizedCitations
- = Ct((parsers.spnl
+ = Ct((parsers.spnlc
* parsers.lbracket
* citation_body
* parsers.rbracket)^1)
@@ -4287,7 +6348,7 @@ M.extensions.citations = function(citation_nbsps)
local Citations = TextCitations + ParenthesizedCitations
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline before LinkAndEmph",
Citations, "Citations")
self.add_special_character("@")
@@ -4381,6 +6442,7 @@ M.extensions.content_blocks = function(language_map)
= (parsers.less
* Cs((parsers.anyescaped
- parsers.more
+ - parsers.spacing
- #(parsers.period
* onlineimagesuffix
* parsers.more
@@ -4413,7 +6475,7 @@ M.extensions.content_blocks = function(language_map)
* Cc("localfile")
local ContentBlock
- = parsers.leader
+ = parsers.check_trail_no_rem
* (localfilepath + onlineimageurl)
* contentblock_tail
/ writer.contentblock
@@ -4457,18 +6519,26 @@ M.extensions.definition_lists = function(tight_lists)
local defstartchar = S("~:")
- local defstart = ( defstartchar * #parsers.spacing
+ local defstart = parsers.check_trail_length(0) * defstartchar * #parsers.spacing
* (parsers.tab + parsers.space^-3)
- + parsers.space * defstartchar * #parsers.spacing
+ + parsers.check_trail_length(1) * defstartchar * #parsers.spacing
* (parsers.tab + parsers.space^-2)
- + parsers.space * parsers.space * defstartchar
- * #parsers.spacing
+ + parsers.check_trail_length(2) * defstartchar * #parsers.spacing
* (parsers.tab + parsers.space^-1)
- + parsers.space * parsers.space * parsers.space
- * defstartchar * #parsers.spacing
- )
+ + parsers.check_trail_length(3) * defstartchar * #parsers.spacing
+
+ local indented_line = (parsers.check_minimal_indent / "") * parsers.check_code_trail * parsers.line
- local dlchunk = Cs(parsers.line * (parsers.indentedline - parsers.blankline)^0)
+ 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),
@@ -4476,22 +6546,22 @@ M.extensions.definition_lists = function(tight_lists)
end
local DefinitionListItemLoose
- = C(parsers.line) * parsers.skipblanklines
- * Ct((defstart
- * parsers.indented_blocks(dlchunk)
- / self.parser_functions.parse_blocks_nested)^1)
+ = 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((defstart * dlchunk
- / self.parser_functions.parse_blocks_nested)^1)
+ * 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)
- * (parsers.skipblanklines
+ * (blank^0
* -DefinitionListItemLoose * Cc(true))
) / writer.definitionlist
@@ -4511,8 +6581,10 @@ M.extensions.fancy_lists = function()
local buffer = {}
local num = startnum
for _,item in ipairs(items) do
- buffer[#buffer + 1] = self.fancyitem(item,num)
- if num ~= nil then
+ if item ~= "" then
+ buffer[#buffer + 1] = self.fancyitem(item,num)
+ end
+ if num ~= nil and item ~= "" then
num = num + 1
end
end
@@ -4540,52 +6612,101 @@ M.extensions.fancy_lists = function()
local options = self.options
local writer = self.writer
- local label = parsers.dig + parsers.letter
- local numdelim = parsers.period + parsers.rparent
- local enumerator = C(label^3 * numdelim) * #parsers.spacing
- + C(label^2 * numdelim) * #parsers.spacing
- * (parsers.tab + parsers.space^1)
- + C(label * numdelim) * #parsers.spacing
- * (parsers.tab + parsers.space^-2)
- + parsers.space * C(label^2 * numdelim)
- * #parsers.spacing
- + parsers.space * C(label * numdelim)
- * #parsers.spacing
- * (parsers.tab + parsers.space^-1)
- + parsers.space * parsers.space * C(label^1
- * numdelim) * #parsers.spacing
- local starter = parsers.bullet + enumerator
+ 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 NestedList = Cs((parsers.optionallyindentedline
- - starter)^1)
- / function(a) return "\001"..a 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 ListBlockLine = parsers.optionallyindentedline
- - parsers.blankline - (parsers.indent^-1
- * starter)
+ 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 ListBlock = parsers.line * ListBlockLine^0
+ local lowercase_opening_roman_marker = P("i")
+ local uppercase_opening_roman_marker = P("I")
- local ListContinuationBlock = parsers.blanklines * (parsers.indent / "")
- * ListBlock
+ 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.ThematicBreak
- * (Cs(starter / "" * parsers.tickbox^-1 * ListBlock * NestedList^-1)
- / self.parser_functions.parse_blocks_nested)
- * -(parsers.blanklines * parsers.indent)
+ return parsers.add_indent(starter, "li")
+ * parsers.indented_content_tight
end
local LooseListItem = function(starter)
- return -parsers.ThematicBreak
- * Cs( starter / "" * parsers.tickbox^-1 * ListBlock * Cc("\n")
- * (NestedList + ListContinuationBlock^0)
- * (parsers.blanklines / "\n\n")
- ) / self.parser_functions.parse_blocks_nested
+ return parsers.add_indent(starter, "li")
+ * parsers.indented_content_loose
+ * remove_indent("li")
end
local function roman2number(roman)
- local romans = { ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
+ local romans = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
local numeral = 0
local i = 1
@@ -4604,8 +6725,7 @@ M.extensions.fancy_lists = function()
return numeral
end
- local function sniffstyle(itemprefix)
- local numstr, delimend = itemprefix:match("^([A-Za-z0-9]*)([.)]*)")
+ local function sniffstyle(numstr, delimend)
local numdelim
if delimend == ")" then
numdelim = "OneParen"
@@ -4614,46 +6734,72 @@ M.extensions.fancy_lists = function()
else
numdelim = "Default"
end
- numstr = numstr or itemprefix
local num
- num = numstr:match("^([IVXL]+)")
+ num = numstr:match("^([I])$")
if num then
return roman2number(num), "UpperRoman", numdelim
end
- num = numstr:match("^([ivxl]+)")
+ num = numstr:match("^([i])$")
if num then
return roman2number(string.upper(num)), "LowerRoman", numdelim
end
- num = numstr:match("^([A-Z])")
+ num = numstr:match("^([A-Z])$")
if num then
return string.byte(num) - string.byte("A") + 1, "UpperAlpha", numdelim
end
- num = numstr:match("^([a-z])")
+ 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)
+ local startnum, numstyle, numdelim = sniffstyle(start[2][1], start[2][2])
return writer.fancylist(items,tight,
- options.startNumber and startnum,
+ options.startNumber and startnum or 1,
numstyle or "Decimal",
numdelim or "Default")
end
- local FancyList = Cg(enumerator, "listtype") *
- ( Ct(TightListItem(Cb("listtype"))
- * TightListItem(enumerator)^0)
- * Cc(true) * parsers.skipblanklines * -enumerator
- + Ct(LooseListItem(Cb("listtype"))
- * LooseListItem(enumerator)^0)
- * Cc(false) * parsers.skipblanklines
- ) * Cb("listtype") / fancylist
+ 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
@@ -4675,7 +6821,7 @@ M.extensions.fenced_code = function(blank_before_code_fence,
end
local name = util.cache_verbatim(options.cacheDir, s)
table.insert(buf, {"\\markdownRendererInputFencedCode{",
- name,"}{",self.string(i),"}"})
+ name,"}{",self.string(i),"}{",self.infostring(i),"}"})
if attr ~= nil then
table.insert(buf, "\\markdownRendererFencedCodeAttributeContextEnd")
end
@@ -4699,18 +6845,52 @@ M.extensions.fenced_code = function(blank_before_code_fence,
return #a >= #b and i
end
- local tilde_infostring
- = C((parsers.linechar
- - (parsers.spacechar^1 * parsers.newline))^0)
+ 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
- = C((parsers.linechar
- - (parsers.backtick
- + parsers.spacechar^1 * parsers.newline))^0)
+ 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 C(parsers.nonindentspace) / function(s) fenceindent = #s end
+ return Cmt(Cb("indent_info") * parsers.check_trail, count_fenced_start_indent)
* Cg(char^3, "fencelength")
* parsers.optionalspace
* infostring
@@ -4718,52 +6898,69 @@ M.extensions.fenced_code = function(blank_before_code_fence,
end
local fencetail = function(char)
- return parsers.nonindentspace
+ 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 fencedline = function(char)
- return C(parsers.line - fencetail(char))
- / function(s)
- local i = 1
- local remaining = fenceindent
- while true do
- local c = s:sub(i, i)
- if c == " " and remaining > 0 then
- remaining = remaining - 1
- i = i + 1
- elseif c == "\t" and remaining > 3 then
- remaining = remaining - 4
- i = i + 1
- else
- break
- end
- end
- return s:sub(i)
- 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(fencedline(parsers.tilde)^0)
- * fencetail(parsers.tilde)
+ * 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(fencedline(parsers.backtick)^0)
- * fencetail(parsers.backtick)
+ * 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 infostring_with_attributes
+ = Ct(C((parsers.linechar
+ - ( parsers.optionalspace
+ * parsers.attributes))^0)
+ * parsers.optionalspace
+ * Ct(parsers.attributes))
local FencedCode
- = (TildeFencedCode + BacktickFencedCode)
+ = ((TildeFencedCode + BacktickFencedCode)
/ function(infostring, code)
local expanded_code = self.expandtabs(code)
@@ -4784,7 +6981,7 @@ M.extensions.fenced_code = function(blank_before_code_fence,
end
end
return writer.fencedCode(expanded_code, infostring, attr)
- end
+ end)
self.insert_pattern("Block after Verbatim",
FencedCode, "FencedCode")
@@ -4816,7 +7013,7 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
function self.div_begin(attributes)
local start_output = {"\\markdownRendererFencedDivAttributeContextBegin\n",
self.attributes(attributes)}
- local end_output = {"\n\\markdownRendererFencedDivAttributeContextEnd "}
+ local end_output = {"\\markdownRendererFencedDivAttributeContextEnd "}
return self.push_attributes("div", attributes, start_output, end_output)
end
function self.div_end()
@@ -4856,6 +7053,31 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
, "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)
@@ -4867,14 +7089,9 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
/ writer.div_begin
* increment_div_level(1)
* parsers.skipblanklines
- * Ct( (V("Block") - fenced_div_end)^-1
- * ( parsers.blanklines
- / function()
- return writer.interblocksep
- end
- * (V("Block") - fenced_div_end))^0)
- * parsers.skipblanklines
- * fenced_div_end * increment_div_level(-1)
+ * Ct(content_loop)
+ * parsers.minimally_indented_blank^0
+ * parsers.check_minimal_indent_and_trail * fenced_div_end * increment_div_level(-1)
* (Cc("") / writer.div_end)
self.insert_pattern("Block after Verbatim",
@@ -4890,13 +7107,6 @@ M.extensions.fenced_divs = function(blank_before_div_fence)
local is_inside_div = Cmt(Cb("div_level"), check_div_level)
local fencestart = is_inside_div * fenced_div_end
- self.update_rule("BlockquoteExceptions", function(previous_pattern)
- if previous_pattern == nil then
- previous_pattern = parsers.BlockquoteExceptions
- end
- return previous_pattern + fencestart
- end)
-
if not blank_before_div_fence then
self.update_rule("EndlineExceptions", function(previous_pattern)
if previous_pattern == nil then
@@ -4916,47 +7126,58 @@ M.extensions.header_attributes = function()
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.hash^1
+ - (parsers.attributes
* parsers.optionalspace
- * parsers.attributes^-1
- + parsers.attributes)
- * parsers.optionalspace
- * parsers.newline))
- * (parsers.linechar
- - parsers.hash
- - parsers.lbrace)^0)^1)
- / self.parser_functions.parse_inlines)
+ * parsers.newline))
+ * (parsers.linechar
+ - parsers.lbrace)^0)^1)
+ / strip_atx_end
+ / parsers.parse_heading_text)
* Cg(Ct(parsers.newline
- + (parsers.hash^1
+ + (parsers.attributes
* parsers.optionalspace
- * parsers.attributes^-1
- + parsers.attributes)
- * parsers.optionalspace
- * parsers.newline), "attributes")
+ * parsers.newline)), "attributes")
* Cb("level")
* Cb("attributes")
/ writer.heading
- local SetextHeading = #(parsers.line * S("=-"))
- * (C(((parsers.linechar
- - (parsers.attributes
- * parsers.optionalspace
- * parsers.newline))
- * (parsers.linechar
- - parsers.lbrace)^0)^1)
- / self.parser_functions.parse_inlines)
- * Cg(Ct(parsers.newline
- + (parsers.attributes
- * parsers.optionalspace
- * parsers.newline)), "attributes")
- * parsers.heading_level
- * Cb("attributes")
- * parsers.optionalspace
- * parsers.newline
- / 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)
@@ -5018,95 +7239,55 @@ M.extensions.line_blocks = function()
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 writer = self.writer
local options = self.options
-
- local define_reference_parser = parsers.define_reference_parser
- * ( parsers.spnl
- * Ct(parsers.attributes))^-1
+ 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
- * parsers.blankline^1
/ self.register_link
self.update_rule("Reference", ReferenceWithAttributes)
- local function indirect_link(label, sps, tag,
- attribute_text,
- attributes)
- return writer.defer_call(function()
- local r, fallback = self.lookup_reference(label, sps, tag,
- attributes)
- if r then
- return writer.link(
- self.parser_functions.parse_inlines_no_link(label),
- r.url, r.title, r.attributes)
- else
- local buf = {fallback}
- if attributes then
- table.insert(buf, writer.string(attribute_text))
- end
- return buf
- end
- end)
- end
-
- local DirectLinkWithAttributes = parsers.direct_link
- * (Ct(parsers.attributes))^-1
- / writer.link
-
- local IndirectLinkWithAttributes = parsers.indirect_link
- * (C(Ct(parsers.attributes)))^-1
- / indirect_link
-
- local LinkWithAttributes = DirectLinkWithAttributes
- + IndirectLinkWithAttributes
-
- self.update_rule("Link", LinkWithAttributes)
-
-
- local function indirect_image(label, sps, tag,
- attribute_text,
- attributes)
- return writer.defer_call(function()
- local r, fallback = self.lookup_reference(label, sps, tag,
- attributes)
- if r then
- return writer.image(writer.string(label),
- r.url, r.title, r.attributes)
- else
- local buf = {"!", fallback}
- if attributes then
- table.insert(buf, writer.string(attribute_text))
- end
- return buf
- end
- end)
- end
-
- local DirectImageWithAttributes = parsers.direct_image
- * Ct(parsers.attributes)
- / writer.image
-
- local IndirectImageWithAttributes = parsers.indirect_image
- * C(Ct(parsers.attributes))
- / indirect_image
-
- local ImageWithAttributes = DirectImageWithAttributes
- + IndirectImageWithAttributes
-
- self.insert_pattern("Inline before Image",
- ImageWithAttributes,
- "ImageWithAttributes")
+ 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
@@ -5149,6 +7330,7 @@ M.extensions.notes = function(notes, inline_notes)
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)
@@ -5158,10 +7340,10 @@ M.extensions.notes = function(notes, inline_notes)
if inline_notes then
local InlineNote
= parsers.circumflex
- * (parsers.tag / self.parser_functions.parse_inlines_no_inline_note)
+ * (parsers.link_label / self.parser_functions.parse_inlines_no_inline_note)
/ writer.note
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
InlineNote, "InlineNote")
end
if notes then
@@ -5171,7 +7353,7 @@ M.extensions.notes = function(notes, inline_notes)
local RawNoteRef
= #(parsers.lbracket * parsers.circumflex)
- * parsers.tag / strip_first_char
+ * parsers.link_label / strip_first_char
local rawnotes = {}
@@ -5190,21 +7372,36 @@ M.extensions.notes = function(notes, inline_notes)
end
local function register_note(ref,rawnote)
- rawnotes[self.normalize_tag(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.leader * RawNoteRef * parsers.colon
- * parsers.spnl * parsers.indented_blocks(parsers.chunk)
+ = parsers.check_trail_no_rem * RawNoteRef * parsers.colon
+ * parsers.spnlc * indented_blocks(chunk)
/ register_note
- local Blank = NoteBlock + parsers.Blank
- self.update_rule("Blank", Blank)
+ local Reference = NoteBlock + parsers.Reference
- self.insert_pattern("Inline after Emph",
+ self.update_rule("Reference", Reference)
+ self.insert_pattern("Inline before LinkAndEmph",
NoteRef, "NoteRef")
end
@@ -5212,7 +7409,7 @@ M.extensions.notes = function(notes, inline_notes)
end
}
end
-M.extensions.pipe_tables = function(table_captions)
+M.extensions.pipe_tables = function(table_captions, table_attributes)
local function make_pipe_table_rectangular(rows)
local num_columns = #rows[2]
@@ -5270,10 +7467,18 @@ M.extensions.pipe_tables = function(table_captions)
return {
name = "built-in pipe_tables syntax extension",
extend_writer = function(self)
- function self.table(rows, caption)
+ function self.table(rows, caption, attributes)
if not self.is_writing then return "" end
- local buffer = {"\\markdownRendererTable{",
- caption or "", "}{", #rows - 1, "}{", #rows[1], "}"}
+ 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
@@ -5290,6 +7495,10 @@ M.extensions.pipe_tables = function(table_captions)
end
table.insert(buffer, "}")
end
+ if attributes ~= nil then
+ table.insert(buffer,
+ "\\markdownRendererTableAttributeContextEnd{}")
+ end
return buffer
end
end, extend_reader = function(self)
@@ -5319,31 +7528,65 @@ M.extensions.pipe_tables = function(table_captions)
, table_hline_separator
, table_hline_column)
- local table_caption_beginning = parsers.skipblanklines
- * parsers.nonindentspace
+ 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
- * Ct(parsers.IndentedInline^1)
- * parsers.newline
+ 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
- * table_hline
- * (parsers.newline * table_row)^0)
+ 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
@@ -5361,6 +7604,7 @@ M.extensions.raw_inline = function()
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),"}"}
@@ -5382,6 +7626,7 @@ M.extensions.strike_through = function()
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)
@@ -5393,7 +7638,7 @@ M.extensions.strike_through = function()
parsers.doubletildes)
) / writer.strike_through
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
StrikeThrough, "StrikeThrough")
self.add_special_character("~")
@@ -5405,6 +7650,7 @@ M.extensions.subscripts = function()
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)
@@ -5415,7 +7661,7 @@ M.extensions.subscripts = function()
parsers.between(parsers.Str, parsers.tilde, parsers.tilde)
) / writer.subscript
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
Subscript, "Subscript")
self.add_special_character("~")
@@ -5427,6 +7673,7 @@ M.extensions.superscripts = function()
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)
@@ -5437,7 +7684,7 @@ M.extensions.superscripts = function()
parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex)
) / writer.superscript
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
Superscript, "Superscript")
self.add_special_character("^")
@@ -5451,11 +7698,11 @@ M.extensions.tex_math = function(tex_math_dollars,
name = "built-in tex_math syntax extension",
extend_writer = function(self)
function self.display_math(s)
- if not self.is_writing then return "" end
+ if self.flatten_inlines then return s end
return {"\\markdownRendererDisplayMath{",self.math(s),"}"}
end
function self.inline_math(s)
- if not self.is_writing then return "" end
+ if self.flatten_inlines then return s end
return {"\\markdownRendererInlineMath{",self.math(s),"}"}
end
end, extend_reader = function(self)
@@ -5463,24 +7710,33 @@ M.extensions.tex_math = function(tex_math_dollars,
local writer = self.writer
local function between(p, starter, ender)
- return (starter * C(p * (p - ender)^0) * 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.nonspacechar - parsers.backslash))
- local dollar_math_content = parsers.backslash^-1
- * 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
+ local inline_math_closing_dollars = allowed_before_closing_no_space
* parsers.dollar
* -#(parsers.digit)
- local inline_math_dollars = between(C( dollar_math_content),
+ local inline_math_dollars = between(Cs( dollar_math_content),
inline_math_opening_dollars,
inline_math_closing_dollars)
@@ -5490,59 +7746,66 @@ M.extensions.tex_math = function(tex_math_dollars,
local display_math_closing_dollars = parsers.dollar
* parsers.dollar
- local display_math_dollars = between(C( dollar_math_content),
+ local display_math_dollars = between(Cs( dollar_math_content),
display_math_opening_dollars,
display_math_closing_dollars)
- local backslash_math_content = parsers.any
+ 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
- * #(parsers.nonspacechar)
local inline_math_closing_double = allowed_before_closing
+ * parsers.spacechar^0
* parsers.backslash
* parsers.backslash
* parsers.rparent
- local inline_math_double = between(C( backslash_math_content),
+ 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(C( backslash_math_content),
+ 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
- * #(parsers.nonspacechar)
local inline_math_closing_single = allowed_before_closing
+ * parsers.spacechar^0
* parsers.backslash
* parsers.rparent
- local inline_math_single = between(C( backslash_math_content),
+ 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(C( backslash_math_content),
+ 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
@@ -5566,7 +7829,7 @@ M.extensions.tex_math = function(tex_math_dollars,
local TexMath = display_math / writer.display_math
+ inline_math / writer.inline_math
- self.insert_pattern("Inline after Emph",
+ self.insert_pattern("Inline after LinkAndEmph",
TexMath, "TexMath")
if tex_math_dollars then
@@ -5719,6 +7982,21 @@ 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
@@ -5780,9 +8058,14 @@ function M.new(options)
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.tableCaptions, options.tableAttributes)
table.insert(extensions, pipe_tables_extension)
end
@@ -5816,11 +8099,9 @@ function M.new(options)
table.insert(extensions, tex_math_extension)
end
- if options.footnotes or options.inlineFootnotes or
- options.notes or options.inlineNotes then
+ if options.notes or options.inlineNotes then
local notes_extension = M.extensions.notes(
- options.footnotes or options.notes,
- options.inlineFootnotes or options.inlineNotes)
+ options.notes, options.inlineNotes)
table.insert(extensions, notes_extension)
end
@@ -5901,7 +8182,18 @@ function M.new(options)
local writer = M.writer.new(options)
local reader = M.reader.new(writer, options)
local convert = reader.finalize_grammar(extensions)
-
+ collectgarbage("collect")
+ if options.singletonCache then
+ local singletonCacheOptions = {}
+ for k, v in pairs(options) do
+ singletonCacheOptions[k] = v
+ end
+ setmetatable(singletonCacheOptions,
+ { __index = function (_, key)
+ return defaultOptions[key] end })
+ singletonCache.options = singletonCacheOptions
+ singletonCache.convert = convert
+ end
return convert
end