summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex/markdown/markdown.lua
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2017-01-05 22:56:40 +0000
committerKarl Berry <karl@freefriends.org>2017-01-05 22:56:40 +0000
commitf78056943ffe36621ca1cf595657f324cda9b13f (patch)
tree99a8c163e460573cbc8d0e27f501edd20177d1d7 /Master/texmf-dist/tex/luatex/markdown/markdown.lua
parent34a0db48cc3c734ab21f489d2f710e74a3fda6af (diff)
markdown (5jan17)
git-svn-id: svn://tug.org/texlive/trunk@42868 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex/markdown/markdown.lua')
-rw-r--r--Master/texmf-dist/tex/luatex/markdown/markdown.lua1685
1 files changed, 1085 insertions, 600 deletions
diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua
index 06f2a2b6169..63e4effda43 100644
--- a/Master/texmf-dist/tex/luatex/markdown/markdown.lua
+++ b/Master/texmf-dist/tex/luatex/markdown/markdown.lua
@@ -58,10 +58,11 @@
-- those in the standard .ins files.
--
local metadata = {
- version = "2.2.2",
+ version = "2.3.0",
comment = "A module for the conversion from markdown to plain TeX",
author = "John MacFarlane, Hans Hagen, Vít Novotný",
- copyright = "2009-2016 John MacFarlane, Hans Hagen; 2016 Vít Novotný",
+ copyright = "2009-2017 John MacFarlane, Hans Hagen; " ..
+ "2016-2017 Vít Novotný",
license = "LPPL 1.3"
}
if not modules then modules = { } end
@@ -74,11 +75,13 @@ local defaultOptions = {}
defaultOptions.blankBeforeBlockquote = false
defaultOptions.blankBeforeCodeFence = false
defaultOptions.blankBeforeHeading = false
+defaultOptions.breakableBlockquotes = false
defaultOptions.cacheDir = "."
defaultOptions.citationNbsps = true
defaultOptions.citations = false
defaultOptions.definitionLists = false
defaultOptions.hashEnumerators = false
+defaultOptions.html = false
defaultOptions.hybrid = false
defaultOptions.fencedCode = false
defaultOptions.footnotes = false
@@ -221,6 +224,269 @@ function util.pathname(dir, file)
return dir .. "/" .. file
end
end
+local entities = {}
+
+local character_entities = {
+ ["quot"] = 0x0022,
+ ["amp"] = 0x0026,
+ ["apos"] = 0x0027,
+ ["lt"] = 0x003C,
+ ["gt"] = 0x003E,
+ ["nbsp"] = 160,
+ ["iexcl"] = 0x00A1,
+ ["cent"] = 0x00A2,
+ ["pound"] = 0x00A3,
+ ["curren"] = 0x00A4,
+ ["yen"] = 0x00A5,
+ ["brvbar"] = 0x00A6,
+ ["sect"] = 0x00A7,
+ ["uml"] = 0x00A8,
+ ["copy"] = 0x00A9,
+ ["ordf"] = 0x00AA,
+ ["laquo"] = 0x00AB,
+ ["not"] = 0x00AC,
+ ["shy"] = 173,
+ ["reg"] = 0x00AE,
+ ["macr"] = 0x00AF,
+ ["deg"] = 0x00B0,
+ ["plusmn"] = 0x00B1,
+ ["sup2"] = 0x00B2,
+ ["sup3"] = 0x00B3,
+ ["acute"] = 0x00B4,
+ ["micro"] = 0x00B5,
+ ["para"] = 0x00B6,
+ ["middot"] = 0x00B7,
+ ["cedil"] = 0x00B8,
+ ["sup1"] = 0x00B9,
+ ["ordm"] = 0x00BA,
+ ["raquo"] = 0x00BB,
+ ["frac14"] = 0x00BC,
+ ["frac12"] = 0x00BD,
+ ["frac34"] = 0x00BE,
+ ["iquest"] = 0x00BF,
+ ["Agrave"] = 0x00C0,
+ ["Aacute"] = 0x00C1,
+ ["Acirc"] = 0x00C2,
+ ["Atilde"] = 0x00C3,
+ ["Auml"] = 0x00C4,
+ ["Aring"] = 0x00C5,
+ ["AElig"] = 0x00C6,
+ ["Ccedil"] = 0x00C7,
+ ["Egrave"] = 0x00C8,
+ ["Eacute"] = 0x00C9,
+ ["Ecirc"] = 0x00CA,
+ ["Euml"] = 0x00CB,
+ ["Igrave"] = 0x00CC,
+ ["Iacute"] = 0x00CD,
+ ["Icirc"] = 0x00CE,
+ ["Iuml"] = 0x00CF,
+ ["ETH"] = 0x00D0,
+ ["Ntilde"] = 0x00D1,
+ ["Ograve"] = 0x00D2,
+ ["Oacute"] = 0x00D3,
+ ["Ocirc"] = 0x00D4,
+ ["Otilde"] = 0x00D5,
+ ["Ouml"] = 0x00D6,
+ ["times"] = 0x00D7,
+ ["Oslash"] = 0x00D8,
+ ["Ugrave"] = 0x00D9,
+ ["Uacute"] = 0x00DA,
+ ["Ucirc"] = 0x00DB,
+ ["Uuml"] = 0x00DC,
+ ["Yacute"] = 0x00DD,
+ ["THORN"] = 0x00DE,
+ ["szlig"] = 0x00DF,
+ ["agrave"] = 0x00E0,
+ ["aacute"] = 0x00E1,
+ ["acirc"] = 0x00E2,
+ ["atilde"] = 0x00E3,
+ ["auml"] = 0x00E4,
+ ["aring"] = 0x00E5,
+ ["aelig"] = 0x00E6,
+ ["ccedil"] = 0x00E7,
+ ["egrave"] = 0x00E8,
+ ["eacute"] = 0x00E9,
+ ["ecirc"] = 0x00EA,
+ ["euml"] = 0x00EB,
+ ["igrave"] = 0x00EC,
+ ["iacute"] = 0x00ED,
+ ["icirc"] = 0x00EE,
+ ["iuml"] = 0x00EF,
+ ["eth"] = 0x00F0,
+ ["ntilde"] = 0x00F1,
+ ["ograve"] = 0x00F2,
+ ["oacute"] = 0x00F3,
+ ["ocirc"] = 0x00F4,
+ ["otilde"] = 0x00F5,
+ ["ouml"] = 0x00F6,
+ ["divide"] = 0x00F7,
+ ["oslash"] = 0x00F8,
+ ["ugrave"] = 0x00F9,
+ ["uacute"] = 0x00FA,
+ ["ucirc"] = 0x00FB,
+ ["uuml"] = 0x00FC,
+ ["yacute"] = 0x00FD,
+ ["thorn"] = 0x00FE,
+ ["yuml"] = 0x00FF,
+ ["OElig"] = 0x0152,
+ ["oelig"] = 0x0153,
+ ["Scaron"] = 0x0160,
+ ["scaron"] = 0x0161,
+ ["Yuml"] = 0x0178,
+ ["fnof"] = 0x0192,
+ ["circ"] = 0x02C6,
+ ["tilde"] = 0x02DC,
+ ["Alpha"] = 0x0391,
+ ["Beta"] = 0x0392,
+ ["Gamma"] = 0x0393,
+ ["Delta"] = 0x0394,
+ ["Epsilon"] = 0x0395,
+ ["Zeta"] = 0x0396,
+ ["Eta"] = 0x0397,
+ ["Theta"] = 0x0398,
+ ["Iota"] = 0x0399,
+ ["Kappa"] = 0x039A,
+ ["Lambda"] = 0x039B,
+ ["Mu"] = 0x039C,
+ ["Nu"] = 0x039D,
+ ["Xi"] = 0x039E,
+ ["Omicron"] = 0x039F,
+ ["Pi"] = 0x03A0,
+ ["Rho"] = 0x03A1,
+ ["Sigma"] = 0x03A3,
+ ["Tau"] = 0x03A4,
+ ["Upsilon"] = 0x03A5,
+ ["Phi"] = 0x03A6,
+ ["Chi"] = 0x03A7,
+ ["Psi"] = 0x03A8,
+ ["Omega"] = 0x03A9,
+ ["alpha"] = 0x03B1,
+ ["beta"] = 0x03B2,
+ ["gamma"] = 0x03B3,
+ ["delta"] = 0x03B4,
+ ["epsilon"] = 0x03B5,
+ ["zeta"] = 0x03B6,
+ ["eta"] = 0x03B7,
+ ["theta"] = 0x03B8,
+ ["iota"] = 0x03B9,
+ ["kappa"] = 0x03BA,
+ ["lambda"] = 0x03BB,
+ ["mu"] = 0x03BC,
+ ["nu"] = 0x03BD,
+ ["xi"] = 0x03BE,
+ ["omicron"] = 0x03BF,
+ ["pi"] = 0x03C0,
+ ["rho"] = 0x03C1,
+ ["sigmaf"] = 0x03C2,
+ ["sigma"] = 0x03C3,
+ ["tau"] = 0x03C4,
+ ["upsilon"] = 0x03C5,
+ ["phi"] = 0x03C6,
+ ["chi"] = 0x03C7,
+ ["psi"] = 0x03C8,
+ ["omega"] = 0x03C9,
+ ["thetasym"] = 0x03D1,
+ ["upsih"] = 0x03D2,
+ ["piv"] = 0x03D6,
+ ["ensp"] = 0x2002,
+ ["emsp"] = 0x2003,
+ ["thinsp"] = 0x2009,
+ ["ndash"] = 0x2013,
+ ["mdash"] = 0x2014,
+ ["lsquo"] = 0x2018,
+ ["rsquo"] = 0x2019,
+ ["sbquo"] = 0x201A,
+ ["ldquo"] = 0x201C,
+ ["rdquo"] = 0x201D,
+ ["bdquo"] = 0x201E,
+ ["dagger"] = 0x2020,
+ ["Dagger"] = 0x2021,
+ ["bull"] = 0x2022,
+ ["hellip"] = 0x2026,
+ ["permil"] = 0x2030,
+ ["prime"] = 0x2032,
+ ["Prime"] = 0x2033,
+ ["lsaquo"] = 0x2039,
+ ["rsaquo"] = 0x203A,
+ ["oline"] = 0x203E,
+ ["frasl"] = 0x2044,
+ ["euro"] = 0x20AC,
+ ["image"] = 0x2111,
+ ["weierp"] = 0x2118,
+ ["real"] = 0x211C,
+ ["trade"] = 0x2122,
+ ["alefsym"] = 0x2135,
+ ["larr"] = 0x2190,
+ ["uarr"] = 0x2191,
+ ["rarr"] = 0x2192,
+ ["darr"] = 0x2193,
+ ["harr"] = 0x2194,
+ ["crarr"] = 0x21B5,
+ ["lArr"] = 0x21D0,
+ ["uArr"] = 0x21D1,
+ ["rArr"] = 0x21D2,
+ ["dArr"] = 0x21D3,
+ ["hArr"] = 0x21D4,
+ ["forall"] = 0x2200,
+ ["part"] = 0x2202,
+ ["exist"] = 0x2203,
+ ["empty"] = 0x2205,
+ ["nabla"] = 0x2207,
+ ["isin"] = 0x2208,
+ ["notin"] = 0x2209,
+ ["ni"] = 0x220B,
+ ["prod"] = 0x220F,
+ ["sum"] = 0x2211,
+ ["minus"] = 0x2212,
+ ["lowast"] = 0x2217,
+ ["radic"] = 0x221A,
+ ["prop"] = 0x221D,
+ ["infin"] = 0x221E,
+ ["ang"] = 0x2220,
+ ["and"] = 0x2227,
+ ["or"] = 0x2228,
+ ["cap"] = 0x2229,
+ ["cup"] = 0x222A,
+ ["int"] = 0x222B,
+ ["there4"] = 0x2234,
+ ["sim"] = 0x223C,
+ ["cong"] = 0x2245,
+ ["asymp"] = 0x2248,
+ ["ne"] = 0x2260,
+ ["equiv"] = 0x2261,
+ ["le"] = 0x2264,
+ ["ge"] = 0x2265,
+ ["sub"] = 0x2282,
+ ["sup"] = 0x2283,
+ ["nsub"] = 0x2284,
+ ["sube"] = 0x2286,
+ ["supe"] = 0x2287,
+ ["oplus"] = 0x2295,
+ ["otimes"] = 0x2297,
+ ["perp"] = 0x22A5,
+ ["sdot"] = 0x22C5,
+ ["lceil"] = 0x2308,
+ ["rceil"] = 0x2309,
+ ["lfloor"] = 0x230A,
+ ["rfloor"] = 0x230B,
+ ["lang"] = 0x27E8,
+ ["rang"] = 0x27E9,
+ ["loz"] = 0x25CA,
+ ["spades"] = 0x2660,
+ ["clubs"] = 0x2663,
+ ["hearts"] = 0x2665,
+ ["diams"] = 0x2666,
+}
+function entities.dec_entity(s)
+ return unicode.utf8.char(tonumber(s))
+end
+function entities.hex_entity(s)
+ return unicode.utf8.char(tonumber("0x"..s))
+end
+function entities.char_entity(s)
+ local n = character_entities[s]
+ return unicode.utf8.char(n)
+end
M.writer = {}
function M.writer.new(options)
local self = {}
@@ -335,6 +601,8 @@ function M.writer.new(options)
"\n\\markdownRendererOlEnd "}
end
end
+ function self.inline_html(html) return "" end
+ function self.display_html(html) return "" end
local function dlitem(term, defs)
local retVal = {"\\markdownRendererDlItem{",term,"}"}
for _, def in ipairs(defs) do
@@ -410,91 +678,432 @@ function M.writer.new(options)
return self
end
-local percent = P("%")
-local at = P("@")
-local comma = P(",")
-local asterisk = P("*")
-local dash = P("-")
-local plus = P("+")
-local underscore = P("_")
-local period = P(".")
-local hash = P("#")
-local ampersand = P("&")
-local backtick = P("`")
-local less = P("<")
-local more = P(">")
-local space = P(" ")
-local squote = P("'")
-local dquote = P('"')
-local lparent = P("(")
-local rparent = P(")")
-local lbracket = P("[")
-local rbracket = P("]")
-local circumflex = P("^")
-local slash = P("/")
-local equal = P("=")
-local colon = P(":")
-local semicolon = P(";")
-local exclamation = P("!")
-local tilde = P("~")
-
-local digit = R("09")
-local hexdigit = R("09","af","AF")
-local letter = R("AZ","az")
-local alphanumeric = R("AZ","az","09")
-local keyword = letter * alphanumeric^0
-local internal_punctuation = S(":;,.#$%&-+?<>~/")
-
-local doubleasterisks = P("**")
-local doubleunderscores = P("__")
-local fourspaces = P(" ")
-
-local any = P(1)
-local fail = any - 1
-local always = P("")
-
-local escapable = S("\\`*_{}[]()+_.!<>#-~:^@;")
-
-local anyescaped = P("\\") / "" * escapable
- + any
-
-local tab = P("\t")
-local spacechar = S("\t ")
-local spacing = S(" \n\r\t")
-local newline = P("\n")
-local nonspacechar = any - spacing
-local tightblocksep = P("\001")
-
-local specialchar = S("*_`&[]<!\\.@-^")
-
-local normalchar = any -
- (specialchar + spacing + tightblocksep)
-local optionalspace = spacechar^0
-local eof = - any
-local nonindentspace = space^-3 * - spacechar
-local indent = space^-3 * tab
- + fourspaces / ""
-local linechar = P(1 - newline)
-
-local blankline = optionalspace * newline / "\n"
-local blanklines = blankline^0
-local skipblanklines = (optionalspace * newline)^0
-local indentedline = indent /"" * C(linechar^1 * newline^-1)
-local optionallyindentedline = indent^-1 /"" * C(linechar^1 * newline^-1)
-local sp = spacing^0
-local spnl = optionalspace * (newline * optionalspace)^-1
-local line = linechar^0 * newline
- + linechar^1 * eof
-local nonemptyline = line - blankline
-
-local chunk = line * (optionallyindentedline - blankline)^0
+local parsers = {}
+parsers.percent = P("%")
+parsers.at = P("@")
+parsers.comma = P(",")
+parsers.asterisk = P("*")
+parsers.dash = P("-")
+parsers.plus = P("+")
+parsers.underscore = P("_")
+parsers.period = P(".")
+parsers.hash = P("#")
+parsers.ampersand = P("&")
+parsers.backtick = P("`")
+parsers.less = P("<")
+parsers.more = P(">")
+parsers.space = P(" ")
+parsers.squote = P("'")
+parsers.dquote = P('"')
+parsers.lparent = P("(")
+parsers.rparent = P(")")
+parsers.lbracket = P("[")
+parsers.rbracket = P("]")
+parsers.circumflex = P("^")
+parsers.slash = P("/")
+parsers.equal = P("=")
+parsers.colon = P(":")
+parsers.semicolon = P(";")
+parsers.exclamation = P("!")
+parsers.tilde = 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.internal_punctuation = S(":;,.#$%&-+?<>~/")
+
+parsers.doubleasterisks = P("**")
+parsers.doubleunderscores = P("__")
+parsers.fourspaces = P(" ")
+
+parsers.any = P(1)
+parsers.fail = parsers.any - 1
+
+parsers.escapable = S("\\`*_{}[]()+_.!<>#-~:^@;")
+parsers.anyescaped = P("\\") / "" * parsers.escapable
+ + parsers.any
+
+parsers.spacechar = S("\t ")
+parsers.spacing = S(" \n\r\t")
+parsers.nonspacechar = parsers.any - parsers.spacing
+parsers.optionalspace = parsers.spacechar^0
+
+parsers.specialchar = S("*_`&[]<!\\.@-^")
+
+parsers.normalchar = parsers.any - (parsers.specialchar
+ + parsers.spacing
+ + parsers.tightblocksep)
+parsers.eof = -parsers.any
+parsers.nonindentspace = parsers.space^-3 * - parsers.spacechar
+parsers.indent = parsers.space^-3 * parsers.tab
+ + parsers.fourspaces / ""
+parsers.linechar = P(1 - parsers.newline)
+
+parsers.blankline = parsers.optionalspace
+ * parsers.newline / "\n"
+parsers.blanklines = parsers.blankline^0
+parsers.skipblanklines = (parsers.optionalspace * parsers.newline)^0
+parsers.indentedline = parsers.indent /""
+ * C(parsers.linechar^1 * parsers.newline^-1)
+parsers.optionallyindentedline = parsers.indent^-1 /""
+ * C(parsers.linechar^1 * parsers.newline^-1)
+parsers.sp = parsers.spacing^0
+parsers.spnl = parsers.optionalspace
+ * (parsers.newline * parsers.optionalspace)^-1
+parsers.line = parsers.linechar^0 * parsers.newline
+ + parsers.linechar^1 * parsers.eof
+parsers.nonemptyline = parsers.line - parsers.blankline
+
+parsers.chunk = parsers.line * (parsers.optionallyindentedline
+ - parsers.blankline)^0
-- block followed by 0 or more optionally
-- indented blocks with first line indented.
-local function indented_blocks(bl)
+parsers.indented_blocks = function(bl)
return Cs( bl
- * (blankline^1 * indent * -blankline * bl)^0
- * (blankline^1 + eof) )
+ * (parsers.blankline^1 * parsers.indent * -parsers.blankline * bl)^0
+ * (parsers.blankline^1 + parsers.eof) )
+end
+parsers.bulletchar = C(parsers.plus + parsers.asterisk + parsers.dash)
+
+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.openticks = Cg(parsers.backtick^1, "ticks")
+
+local function captures_equal_length(s,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.intickschar = (parsers.any - S(" \n\r`"))
+ + (parsers.newline * -parsers.blankline)
+ + (parsers.space - parsers.closeticks)
+ + (parsers.backtick^1 - parsers.closeticks)
+
+parsers.inticks = parsers.openticks * parsers.space^-1
+ * C(parsers.intickschar^0) * parsers.closeticks
+local function captures_geq_length(s,i,a,b)
+ return #a >= #b and i
+end
+
+parsers.infostring = (parsers.linechar - (parsers.backtick
+ + parsers.space^1 * (parsers.newline + parsers.eof)))^0
+
+local fenceindent
+parsers.fencehead = function(char)
+ return C(parsers.nonindentspace) / function(s) fenceindent = #s end
+ * Cg(char^3, "fencelength")
+ * parsers.optionalspace * C(parsers.infostring)
+ * parsers.optionalspace * (parsers.newline + parsers.eof)
+end
+
+parsers.fencetail = function(char)
+ return parsers.nonindentspace
+ * Cmt(C(char^3) * Cb("fencelength"), captures_geq_length)
+ * parsers.optionalspace * (parsers.newline + parsers.eof)
+ + parsers.eof
+end
+
+parsers.fencedline = function(char)
+ return C(parsers.line - parsers.fencetail(char))
+ / function(s)
+ return s:gsub("^" .. string.rep(" ?", fenceindent), "")
+ end
+end
+parsers.leader = parsers.space^-3
+
+-- in balanced brackets, parentheses, quotes:
+parsers.bracketed = P{ parsers.lbracket
+ * ((parsers.anyescaped - (parsers.lbracket
+ + parsers.rbracket
+ + parsers.blankline^2)
+ ) + V(1))^0
+ * parsers.rbracket }
+
+parsers.inparens = P{ parsers.lparent
+ * ((parsers.anyescaped - (parsers.lparent
+ + parsers.rparent
+ + parsers.blankline^2)
+ ) + V(1))^0
+ * parsers.rparent }
+
+parsers.squoted = P{ parsers.squote * parsers.alphanumeric
+ * ((parsers.anyescaped - (parsers.squote
+ + parsers.blankline^2)
+ ) + V(1))^0
+ * parsers.squote }
+
+parsers.dquoted = P{ parsers.dquote * parsers.alphanumeric
+ * ((parsers.anyescaped - (parsers.dquote
+ + parsers.blankline^2)
+ ) + V(1))^0
+ * parsers.dquote }
+
+-- bracketed 'tag' for markdown links, allowing nested brackets:
+parsers.tag = parsers.lbracket
+ * Cs((parsers.alphanumeric^1
+ + parsers.bracketed
+ + parsers.inticks
+ + (parsers.anyescaped - (parsers.rbracket
+ + parsers.blankline^2)))^0)
+ * parsers.rbracket
+
+-- url for markdown links, allowing balanced parentheses:
+parsers.url = parsers.less * Cs((parsers.anyescaped-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.title_p = parsers.lparent
+ * Cs((parsers.inparens + (parsers.anyescaped-parsers.rparent))^0)
+ * parsers.rparent
+
+parsers.title = parsers.title_d + parsers.title_s + parsers.title_p
+
+parsers.optionaltitle
+ = parsers.spnl * parsers.title * parsers.spacechar^0
+ + Cc("")
+parsers.citation_name = Cs(parsers.dash^-1) * parsers.at
+ * Cs(parsers.alphanumeric
+ * (parsers.alphanumeric + parsers.internal_punctuation
+ - parsers.comma - parsers.semicolon)^0)
+
+parsers.citation_body_prenote
+ = Cs((parsers.alphanumeric^1
+ + parsers.bracketed
+ + parsers.inticks
+ + (parsers.anyescaped
+ - (parsers.rbracket + parsers.blankline^2))
+ - (parsers.spnl * parsers.dash^-1 * parsers.at))^0)
+
+parsers.citation_body_postnote
+ = Cs((parsers.alphanumeric^1
+ + parsers.bracketed
+ + parsers.inticks
+ + (parsers.anyescaped
+ - (parsers.rbracket + parsers.semicolon
+ + parsers.blankline^2))
+ - (parsers.spnl * parsers.rbracket))^0)
+
+parsers.citation_body_chunk
+ = parsers.citation_body_prenote
+ * parsers.spnl * parsers.citation_name
+ * (parsers.comma * parsers.spnl)^-1
+ * parsers.citation_body_postnote
+
+parsers.citation_body
+ = parsers.citation_body_chunk
+ * (parsers.semicolon * parsers.spnl
+ * parsers.citation_body_chunk)^0
+
+parsers.citation_headless_body_postnote
+ = Cs((parsers.alphanumeric^1
+ + parsers.bracketed
+ + parsers.inticks
+ + (parsers.anyescaped
+ - (parsers.rbracket + parsers.at
+ + parsers.semicolon + parsers.blankline^2))
+ - (parsers.spnl * parsers.rbracket))^0)
+
+parsers.citation_headless_body
+ = parsers.citation_headless_body_postnote
+ * (parsers.sp * parsers.semicolon * parsers.spnl
+ * parsers.citation_body_chunk)^0
+local function strip_first_char(s)
+ return s:sub(2)
+end
+
+parsers.RawNoteRef = #(parsers.lbracket * parsers.circumflex)
+ * parsers.tag / strip_first_char
+-- 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.any - P("-->"))^0 * 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.displayhtml = parsers.htmlcomment
+ + parsers.emptyelt_block
+ + parsers.openelt_exact("hr")
+ + parsers.in_matched_block_tags
+ + parsers.htmlinstruction
+
+parsers.inlinehtml = parsers.emptyelt_any
+ + parsers.htmlcomment
+ + parsers.htmlinstruction
+ + parsers.openelt_any
+ + parsers.closeelt_any
+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.blankline^1
+parsers.Inline = V("Inline")
+
+-- parse many p between starter and ender
+parsers.between = function(p, starter, ender)
+ local ender2 = B(parsers.nonspacechar) * ender
+ return (starter * #parsers.nonspacechar * Ct(p * (p - ender2)^0) * ender2)
+end
+
+parsers.urlchar = parsers.anyescaped - parsers.newline - parsers.more
+parsers.Block = V("Block")
+
+parsers.TildeFencedCode
+ = parsers.fencehead(parsers.tilde)
+ * Cs(parsers.fencedline(parsers.tilde)^0)
+ * parsers.fencetail(parsers.tilde)
+
+parsers.BacktickFencedCode
+ = parsers.fencehead(parsers.backtick)
+ * Cs(parsers.fencedline(parsers.backtick)^0)
+ * parsers.fencetail(parsers.backtick)
+
+parsers.lineof = function(c)
+ return (parsers.leader * (P(c) * parsers.optionalspace)^3
+ * (parsers.newline * parsers.blankline^1
+ + parsers.newline^-1 * parsers.eof))
+end
+parsers.defstartchar = S("~:")
+parsers.defstart = ( parsers.defstartchar * #parsers.spacing
+ * (parsers.tab + parsers.space^-3)
+ + parsers.space * parsers.defstartchar * #parsers.spacing
+ * (parsers.tab + parsers.space^-2)
+ + parsers.space * parsers.space * parsers.defstartchar
+ * #parsers.spacing
+ * (parsers.tab + parsers.space^-1)
+ + parsers.space * parsers.space * parsers.space
+ * parsers.defstartchar * #parsers.spacing
+ )
+
+parsers.dlchunk = Cs(parsers.line * (parsers.indentedline - parsers.blankline)^0)
+-- parse Atx heading start and return level
+parsers.HeadingStart = #parsers.hash * C(parsers.hash^-6)
+ * -parsers.hash / length
+
+-- parse setext header ending and return level
+parsers.HeadingLevel = parsers.equal^1 * Cc(1) + parsers.dash^1 * Cc(2)
+
+local function strip_atx_end(s)
+ return s:gsub("[#%s]*\n$","")
end
M.reader = {}
function M.reader.new(writer, options)
@@ -518,14 +1127,7 @@ function M.reader.new(writer, options)
end
end
end
- local syntax
- local blocks_toplevel
- local blocks
- local inlines
- local inlines_no_link
- local inlines_no_inline_note
- local inlines_nbsp
-
+ local larsers = {}
local function create_parser(name, grammar)
return function(str)
local res = lpeg.match(grammar(), str)
@@ -537,180 +1139,94 @@ function M.reader.new(writer, options)
end
end
- local parse_blocks = create_parser("parse_blocks",
- function() return blocks end)
- local parse_blocks_toplevel = create_parser("parse_blocks_toplevel",
- function() return blocks_toplevel end)
- local parse_inlines = create_parser("parse_inlines",
- function() return inlines end)
- local parse_inlines_no_link = create_parser("parse_inlines_no_link",
- function() return inlines_no_link end)
- local parse_inlines_no_inline_note = create_parser(
- "parse_inlines_no_inline_note",
- function() return inlines_no_inline_note end)
- local parse_inlines_nbsp = create_parser("parse_inlines_nbsp",
- function() return inlines_nbsp end)
- local bulletchar = C(plus + asterisk + dash)
-
- local bullet = ( Cg(bulletchar, "bulletchar") * #spacing * (tab + space^-3)
- + space * Cg(bulletchar, "bulletchar") * #spacing * (tab + space^-2)
- + space * space * Cg(bulletchar, "bulletchar") * #spacing * (tab + space^-1)
- + space * space * space * Cg(bulletchar, "bulletchar") * #spacing
- )
-
+ local parse_blocks
+ = create_parser("parse_blocks",
+ function()
+ return larsers.blocks
+ end)
+
+ local parse_blocks_toplevel
+ = create_parser("parse_blocks_toplevel",
+ function()
+ return larsers.blocks_toplevel
+ end)
+
+ local parse_inlines
+ = create_parser("parse_inlines",
+ function()
+ return larsers.inlines
+ end)
+
+ local parse_inlines_no_link
+ = create_parser("parse_inlines_no_link",
+ function()
+ return larsers.inlines_no_link
+ end)
+
+ local parse_inlines_no_inline_note
+ = create_parser("parse_inlines_no_inline_note",
+ function()
+ return larsers.inlines_no_inline_note
+ end)
+
+ local parse_inlines_nbsp
+ = create_parser("parse_inlines_nbsp",
+ function()
+ return larsers.inlines_nbsp
+ end)
if options.hashEnumerators then
- dig = digit + hash
+ larsers.dig = parsers.digit + parsers.hash
else
- dig = digit
- end
-
- local enumerator = C(dig^3 * period) * #spacing
- + C(dig^2 * period) * #spacing * (tab + space^1)
- + C(dig * period) * #spacing * (tab + space^-2)
- + space * C(dig^2 * period) * #spacing
- + space * C(dig * period) * #spacing * (tab + space^-1)
- + space * space * C(dig^1 * period) * #spacing
- local openticks = Cg(backtick^1, "ticks")
-
- local function captures_equal_length(s,i,a,b)
- return #a == #b and i
- end
-
- local closeticks = space^-1 *
- Cmt(C(backtick^1) * Cb("ticks"), captures_equal_length)
-
- local intickschar = (any - S(" \n\r`"))
- + (newline * -blankline)
- + (space - closeticks)
- + (backtick^1 - closeticks)
-
- local inticks = openticks * space^-1 * C(intickschar^0) * closeticks
- local function captures_geq_length(s,i,a,b)
- return #a >= #b and i
- end
-
- local infostring = (linechar - (backtick + space^1 * (newline + eof)))^0
-
- local fenceindent
- local function fencehead(char)
- return C(nonindentspace) / function(s) fenceindent = #s end
- * Cg(char^3, "fencelength")
- * optionalspace * C(infostring) * optionalspace
- * (newline + eof)
- end
+ larsers.dig = parsers.digit
+ end
+
+ larsers.enumerator = C(larsers.dig^3 * parsers.period) * #parsers.spacing
+ + C(larsers.dig^2 * parsers.period) * #parsers.spacing
+ * (parsers.tab + parsers.space^1)
+ + C(larsers.dig * parsers.period) * #parsers.spacing
+ * (parsers.tab + parsers.space^-2)
+ + parsers.space * C(larsers.dig^2 * parsers.period)
+ * #parsers.spacing
+ + parsers.space * C(larsers.dig * parsers.period)
+ * #parsers.spacing
+ * (parsers.tab + parsers.space^-1)
+ + parsers.space * parsers.space * C(larsers.dig^1
+ * parsers.period) * #parsers.spacing
+ -- strip off leading > and indents, and run through blocks
+ larsers.blockquote_body = ((parsers.leader * parsers.more * parsers.space^-1)/""
+ * parsers.linechar^0 * parsers.newline)^1
+ * (-(parsers.leader * parsers.more
+ + parsers.blankline) * parsers.linechar^1
+ * parsers.newline)^0
- local function fencetail(char)
- return nonindentspace
- * Cmt(C(char^3) * Cb("fencelength"),
- captures_geq_length)
- * optionalspace * (newline + eof)
- + eof
+ if not options.breakableBlockquotes then
+ larsers.blockquote_body = larsers.blockquote_body
+ * (parsers.blankline^0 / "")
end
+ larsers.citations = function(text_cites, raw_cites)
+ local function normalize(str)
+ if str == "" then
+ str = nil
+ else
+ str = (options.citationNbsps and parse_inlines_nbsp or
+ parse_inlines)(str)
+ end
+ return str
+ end
- local function fencedline(char)
- return C(line - fencetail(char))
- / function(s)
- return s:gsub("^" .. string.rep(" ?",
- fenceindent), "")
- end
+ local cites = {}
+ for i = 1,#raw_cites,4 do
+ cites[#cites+1] = {
+ prenote = normalize(raw_cites[i]),
+ suppress_author = raw_cites[i+1] == "-",
+ name = writer.string(raw_cites[i+2]),
+ postnote = normalize(raw_cites[i+3]),
+ }
+ end
+ return writer.citations(text_cites, cites)
end
- local leader = space^-3
-
- -- in balanced brackets, parentheses, quotes:
- local bracketed = P{ lbracket
- * ((anyescaped - (lbracket + rbracket
- + blankline^2)) + V(1))^0
- * rbracket }
-
- local inparens = P{ lparent
- * ((anyescaped - (lparent + rparent
- + blankline^2)) + V(1))^0
- * rparent }
-
- local squoted = P{ squote * alphanumeric
- * ((anyescaped - (squote + blankline^2))
- + V(1))^0
- * squote }
-
- local dquoted = P{ dquote * alphanumeric
- * ((anyescaped - (dquote + blankline^2))
- + V(1))^0
- * dquote }
-
- -- bracketed 'tag' for markdown links, allowing nested brackets:
- local tag = lbracket
- * Cs((alphanumeric^1
- + bracketed
- + inticks
- + (anyescaped - (rbracket + blankline^2)))^0)
- * rbracket
-
- -- url for markdown links, allowing balanced parentheses:
- local url = less * Cs((anyescaped-more)^0) * more
- + Cs((inparens + (anyescaped-spacing-rparent))^1)
-
- -- quoted text possibly with nested quotes:
- local title_s = squote * Cs(((anyescaped-squote) + squoted)^0) *
- squote
-
- local title_d = dquote * Cs(((anyescaped-dquote) + dquoted)^0) *
- dquote
-
- local title_p = lparent
- * Cs((inparens + (anyescaped-rparent))^0)
- * rparent
-
- local title = title_d + title_s + title_p
-
- local optionaltitle = spnl * title * spacechar^0
- + Cc("")
- local citation_name = Cs(dash^-1) * at
- * Cs(alphanumeric
- * (alphanumeric + internal_punctuation
- - comma - semicolon)^0)
-
- local citation_body_prenote
- = Cs((alphanumeric^1
- + bracketed
- + inticks
- + (anyescaped
- - (rbracket + blankline^2))
- - (spnl * dash^-1 * at))^0)
-
- local citation_body_postnote
- = Cs((alphanumeric^1
- + bracketed
- + inticks
- + (anyescaped
- - (rbracket + semicolon + blankline^2))
- - (spnl * rbracket))^0)
-
- local citation_body_chunk
- = citation_body_prenote
- * spnl * citation_name
- * (comma * spnl)^-1
- * citation_body_postnote
-
- local citation_body = citation_body_chunk
- * (semicolon * spnl * citation_body_chunk)^0
-
- local citation_headless_body_postnote
- = Cs((alphanumeric^1
- + bracketed
- + inticks
- + (anyescaped
- - (rbracket + at + semicolon + blankline^2))
- - (spnl * rbracket))^0)
-
- local citation_headless_body
- = citation_headless_body_postnote
- * (sp * semicolon * spnl * citation_body_chunk)^0
local rawnotes = {}
- local function strip_first_char(s)
- return s:sub(2)
- end
-
-- like indirect_link
local function lookup_note(ref)
return function()
@@ -728,16 +1244,15 @@ function M.reader.new(writer, options)
return ""
end
- local RawNoteRef = #(lbracket * circumflex) * tag / strip_first_char
+ larsers.NoteRef = parsers.RawNoteRef / lookup_note
- local NoteRef = RawNoteRef / lookup_note
+ larsers.NoteBlock = parsers.leader * parsers.RawNoteRef * parsers.colon
+ * parsers.spnl * parsers.indented_blocks(parsers.chunk)
+ / register_note
- local NoteBlock = leader * RawNoteRef * colon * spnl
- * indented_blocks(chunk) / register_note
-
- local InlineNote = circumflex -- no notes inside notes
- * (tag / parse_inlines_no_inline_note)
- / writer.note
+ larsers.InlineNote = parsers.circumflex
+ * (parsers.tag / parse_inlines_no_inline_note) -- no notes inside notes
+ / writer.note
-- List of references defined in the document
local references
@@ -747,10 +1262,6 @@ function M.reader.new(writer, options)
return ""
end
- -- parse a reference definition: [foo]: /bar "title"
- local define_reference_parser =
- leader * tag * colon * spacechar^0 * url * optionaltitle * blankline^1
-
-- lookup link reference and return either
-- the link or nil and fallback text.
local function lookup_reference(label,sps,tag)
@@ -800,232 +1311,239 @@ function M.reader.new(writer, options)
end
end
end
- local bqstart = more
- local headerstart = hash
- + (line * (equal^1 + dash^1) * optionalspace * newline)
- local fencestart = fencehead(backtick) + fencehead(tilde)
+ larsers.Str = parsers.normalchar^1 / writer.string
+
+ larsers.Symbol = (parsers.specialchar - parsers.tightblocksep)
+ / writer.string
+
+ larsers.Ellipsis = P("...") / writer.ellipsis
+
+ larsers.Smart = larsers.Ellipsis
+
+ larsers.Code = parsers.inticks / writer.code
if options.blankBeforeBlockquote then
- bqstart = fail
+ larsers.bqstart = parsers.fail
+ else
+ larsers.bqstart = parsers.more
end
if options.blankBeforeHeading then
- headerstart = fail
+ larsers.headerstart = parsers.fail
+ else
+ larsers.headerstart = parsers.hash
+ + (parsers.line * (parsers.equal^1 + parsers.dash^1)
+ * parsers.optionalspace * parsers.newline)
end
if not options.fencedCode or options.blankBeforeCodeFence then
- fencestart = fail
- end
- local Inline = V("Inline")
-
- local Str = normalchar^1 / writer.string
-
- local Symbol = (specialchar - tightblocksep) / writer.string
- local Ellipsis = P("...") / writer.ellipsis
-
- local Smart = Ellipsis
- local Code = inticks / writer.code
- local Endline = newline * -( -- newline, but not before...
- blankline -- paragraph break
- + tightblocksep -- nested list
- + eof -- end of document
- + bqstart
- + headerstart
- + fencestart
- ) * spacechar^0 / writer.space
-
- local Space = spacechar^2 * Endline / writer.linebreak
- + spacechar^1 * Endline^-1 * eof / ""
- + spacechar^1 * Endline^-1 * optionalspace / writer.space
-
- local NonbreakingEndline
- = newline * -( -- newline, but not before...
- blankline -- paragraph break
- + tightblocksep -- nested list
- + eof -- end of document
- + bqstart
- + headerstart
- + fencestart
- ) * spacechar^0 / writer.nbsp
-
- local NonbreakingSpace
- = spacechar^2 * Endline / writer.linebreak
- + spacechar^1 * Endline^-1 * eof / ""
- + spacechar^1 * Endline^-1 * optionalspace / writer.nbsp
-
- -- parse many p between starter and ender
- local function between(p, starter, ender)
- local ender2 = B(nonspacechar) * ender
- return (starter * #nonspacechar * Ct(p * (p - ender2)^0) * ender2)
- end
- local Strong = ( between(Inline, doubleasterisks, doubleasterisks)
- + between(Inline, doubleunderscores, doubleunderscores)
- ) / writer.strong
-
- local Emph = ( between(Inline, asterisk, asterisk)
- + between(Inline, underscore, underscore)
- ) / writer.emphasis
- local urlchar = anyescaped - newline - more
-
- local AutoLinkUrl = less
- * C(alphanumeric^1 * P("://") * urlchar^1)
- * more
- / function(url)
- return writer.link(writer.string(url), url)
- end
-
- local AutoLinkEmail = less
- * C((alphanumeric + S("-._+"))^1 * P("@") * urlchar^1)
- * more
- / function(email)
- return writer.link(writer.string(email),
- "mailto:"..email)
- end
-
- local DirectLink = (tag / parse_inlines_no_link) -- no links inside links
- * spnl
- * lparent
- * (url + Cc("")) -- link can be empty [foo]()
- * optionaltitle
- * rparent
- / writer.link
-
- local IndirectLink = tag * (C(spnl) * tag)^-1 / indirect_link
+ larsers.fencestart = parsers.fail
+ else
+ larsers.fencestart = parsers.fencehead(parsers.backtick)
+ + parsers.fencehead(parsers.tilde)
+ end
+
+ larsers.Endline = parsers.newline * -( -- newline, but not before...
+ parsers.blankline -- paragraph break
+ + parsers.tightblocksep -- nested list
+ + parsers.eof -- end of document
+ + larsers.bqstart
+ + larsers.headerstart
+ + larsers.fencestart
+ ) * parsers.spacechar^0 / writer.space
+
+ larsers.Space = parsers.spacechar^2 * larsers.Endline / writer.linebreak
+ + parsers.spacechar^1 * larsers.Endline^-1 * parsers.eof / ""
+ + parsers.spacechar^1 * larsers.Endline^-1
+ * parsers.optionalspace / writer.space
+
+ larsers.NonbreakingEndline
+ = parsers.newline * -( -- newline, but not before...
+ parsers.blankline -- paragraph break
+ + parsers.tightblocksep -- nested list
+ + parsers.eof -- end of document
+ + larsers.bqstart
+ + larsers.headerstart
+ + larsers.fencestart
+ ) * parsers.spacechar^0 / writer.nbsp
+
+ larsers.NonbreakingSpace
+ = parsers.spacechar^2 * larsers.Endline / writer.linebreak
+ + parsers.spacechar^1 * larsers.Endline^-1 * parsers.eof / ""
+ + parsers.spacechar^1 * larsers.Endline^-1
+ * parsers.optionalspace / writer.nbsp
+
+ larsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks,
+ parsers.doubleasterisks)
+ + parsers.between(parsers.Inline, parsers.doubleunderscores,
+ parsers.doubleunderscores)
+ ) / writer.strong
+
+ larsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk,
+ parsers.asterisk)
+ + parsers.between(parsers.Inline, parsers.underscore,
+ parsers.underscore)
+ ) / writer.emphasis
+
+ larsers.AutoLinkUrl = parsers.less
+ * C(parsers.alphanumeric^1 * P("://") * parsers.urlchar^1)
+ * parsers.more
+ / function(url)
+ return writer.link(writer.string(url), url)
+ end
+
+ larsers.AutoLinkEmail = parsers.less
+ * C((parsers.alphanumeric + S("-._+"))^1
+ * P("@") * parsers.urlchar^1)
+ * parsers.more
+ / function(email)
+ return writer.link(writer.string(email),
+ "mailto:"..email)
+ end
+
+ larsers.DirectLink = (parsers.tag / parse_inlines_no_link) -- no links inside links
+ * parsers.spnl
+ * parsers.lparent
+ * (parsers.url + Cc("")) -- link can be empty [foo]()
+ * parsers.optionaltitle
+ * parsers.rparent
+ / writer.link
+
+ larsers.IndirectLink = parsers.tag * (C(parsers.spnl) * parsers.tag)^-1
+ / indirect_link
-- parse a link or image (direct or indirect)
- local Link = DirectLink + IndirectLink
- local DirectImage = exclamation
- * (tag / parse_inlines)
- * spnl
- * lparent
- * (url + Cc("")) -- link can be empty [foo]()
- * optionaltitle
- * rparent
- / writer.image
-
- local IndirectImage = exclamation * tag * (C(spnl) * tag)^-1 /
- indirect_image
-
- local Image = DirectImage + IndirectImage
- -- avoid parsing long strings of * or _ as emph/strong
- local UlOrStarLine = asterisk^4 + underscore^4 / writer.string
+ larsers.Link = larsers.DirectLink + larsers.IndirectLink
+
+ larsers.DirectImage = parsers.exclamation
+ * (parsers.tag / parse_inlines)
+ * parsers.spnl
+ * parsers.lparent
+ * (parsers.url + Cc("")) -- link can be empty [foo]()
+ * parsers.optionaltitle
+ * parsers.rparent
+ / writer.image
+
+ larsers.IndirectImage = parsers.exclamation * parsers.tag
+ * (C(parsers.spnl) * parsers.tag)^-1 / indirect_image
+
+ larsers.Image = larsers.DirectImage + larsers.IndirectImage
+
+ larsers.TextCitations = Ct(Cc("")
+ * parsers.citation_name
+ * ((parsers.spnl
+ * parsers.lbracket
+ * parsers.citation_headless_body
+ * parsers.rbracket) + Cc("")))
+ / function(raw_cites)
+ return larsers.citations(true, raw_cites)
+ end
+
+ larsers.ParenthesizedCitations
+ = Ct(parsers.lbracket
+ * parsers.citation_body
+ * parsers.rbracket)
+ / function(raw_cites)
+ return larsers.citations(false, raw_cites)
+ end
+
+ larsers.Citations = larsers.TextCitations + larsers.ParenthesizedCitations
- local EscapedChar = S("\\") * C(escapable) / writer.string
- local function citations(text_cites, raw_cites)
- local function normalize(str)
- if str == "" then
- str = nil
- else
- str = (options.citationNbsps and parse_inlines_nbsp or
- parse_inlines)(str)
- end
- return str
- end
+ -- avoid parsing long strings of * or _ as emph/strong
+ larsers.UlOrStarLine = parsers.asterisk^4 + parsers.underscore^4
+ / writer.string
- local cites = {}
- for i = 1,#raw_cites,4 do
- cites[#cites+1] = {
- prenote = normalize(raw_cites[i]),
- suppress_author = raw_cites[i+1] == "-",
- name = writer.string(raw_cites[i+2]),
- postnote = normalize(raw_cites[i+3]),
- }
- end
- return writer.citations(text_cites, cites)
- end
+ larsers.EscapedChar = S("\\") * C(parsers.escapable) / writer.string
- local TextCitations = Ct(Cc("")
- * citation_name
- * ((spnl
- * lbracket
- * citation_headless_body
- * rbracket) + Cc(""))) /
- function(raw_cites)
- return citations(true, raw_cites)
- end
-
- local ParenthesizedCitations
- = Ct(lbracket
- * citation_body
- * rbracket) /
- function(raw_cites)
- return citations(false, raw_cites)
- end
-
- local Citations = TextCitations + ParenthesizedCitations
- local Block = V("Block")
-
- local Verbatim = Cs( (blanklines
- * ((indentedline - blankline))^1)^1
- ) / expandtabs / writer.verbatim
+ larsers.InlineHtml = C(parsers.inlinehtml) / writer.inline_html
- local TildeFencedCode
- = fencehead(tilde)
- * Cs(fencedline(tilde)^0)
- * fencetail(tilde)
+ larsers.HtmlEntity = parsers.hexentity / entities.hex_entity / writer.string
+ + parsers.decentity / entities.dec_entity / writer.string
+ + parsers.tagentity / entities.char_entity / writer.string
+ larsers.DisplayHtml = C(parsers.displayhtml)
+ / expandtabs / writer.display_html
- local BacktickFencedCode
- = fencehead(backtick)
- * Cs(fencedline(backtick)^0)
- * fencetail(backtick)
+ larsers.Verbatim = Cs( (parsers.blanklines
+ * ((parsers.indentedline - parsers.blankline))^1)^1
+ ) / expandtabs / writer.verbatim
- local FencedCode = (TildeFencedCode + BacktickFencedCode)
+ larsers.FencedCode = (parsers.TildeFencedCode
+ + parsers.BacktickFencedCode)
/ function(infostring, code)
- return writer.fencedCode(
- writer.string(infostring),
- expandtabs(code))
+ return writer.fencedCode(writer.string(infostring),
+ expandtabs(code))
end
- -- strip off leading > and indents, and run through blocks
- local Blockquote = Cs((
- ((leader * more * space^-1)/"" * linechar^0 * newline)^1
- * (-blankline * linechar^1 * newline)^0
- * (blankline^0 / "")
- )^1) / parse_blocks_toplevel / writer.blockquote
-
- local function lineof(c)
- return (leader * (P(c) * optionalspace)^3 * (newline * blankline^1
- + newline^-1 * eof))
- end
- local HorizontalRule = ( lineof(asterisk)
- + lineof(dash)
- + lineof(underscore)
- ) / writer.hrule
- local starter = bullet + enumerator
+
+ larsers.Blockquote = Cs(larsers.blockquote_body^1)
+ / parse_blocks_toplevel / writer.blockquote
+
+ larsers.HorizontalRule = ( parsers.lineof(parsers.asterisk)
+ + parsers.lineof(parsers.dash)
+ + parsers.lineof(parsers.underscore)
+ ) / writer.hrule
+
+ larsers.Reference = parsers.define_reference_parser / register_link
+
+ larsers.Paragraph = parsers.nonindentspace * Ct(parsers.Inline^1)
+ * parsers.newline
+ * ( parsers.blankline^1
+ + #parsers.hash
+ + #(parsers.leader * parsers.more * parsers.space^-1)
+ )
+ / writer.paragraph
+
+ larsers.ToplevelParagraph
+ = parsers.nonindentspace * Ct(parsers.Inline^1)
+ * ( parsers.newline
+ * ( parsers.blankline^1
+ + #parsers.hash
+ + #(parsers.leader * parsers.more * parsers.space^-1)
+ + parsers.eof
+ )
+ + parsers.eof )
+ / writer.paragraph
+
+ larsers.Plain = parsers.nonindentspace * Ct(parsers.Inline^1)
+ / writer.plain
+ larsers.starter = parsers.bullet + larsers.enumerator
-- we use \001 as a separator between a tight list item and a
-- nested list under it.
- local NestedList = Cs((optionallyindentedline - starter)^1)
- / function(a) return "\001"..a end
+ larsers.NestedList = Cs((parsers.optionallyindentedline
+ - larsers.starter)^1)
+ / function(a) return "\001"..a end
- local ListBlockLine = optionallyindentedline
- - blankline - (indent^-1 * starter)
+ larsers.ListBlockLine = parsers.optionallyindentedline
+ - parsers.blankline - (parsers.indent^-1
+ * larsers.starter)
- local ListBlock = line * ListBlockLine^0
+ larsers.ListBlock = parsers.line * larsers.ListBlockLine^0
- local ListContinuationBlock = blanklines * (indent / "") * ListBlock
+ larsers.ListContinuationBlock = parsers.blanklines * (parsers.indent / "")
+ * larsers.ListBlock
- local function TightListItem(starter)
- return -HorizontalRule
- * (Cs(starter / "" * ListBlock * NestedList^-1) /
- parse_blocks)
- * -(blanklines * indent)
+ larsers.TightListItem = function(starter)
+ return -larsers.HorizontalRule
+ * (Cs(starter / "" * larsers.ListBlock * larsers.NestedList^-1)
+ / parse_blocks)
+ * -(parsers.blanklines * parsers.indent)
end
- local function LooseListItem(starter)
- return -HorizontalRule
- * Cs( starter / "" * ListBlock * Cc("\n")
- * (NestedList + ListContinuationBlock^0)
- * (blanklines / "\n\n")
+ larsers.LooseListItem = function(starter)
+ return -larsers.HorizontalRule
+ * Cs( starter / "" * larsers.ListBlock * Cc("\n")
+ * (larsers.NestedList + larsers.ListContinuationBlock^0)
+ * (parsers.blanklines / "\n\n")
) / parse_blocks
end
- local BulletList = ( Ct(TightListItem(bullet)^1)
- * Cc(true) * skipblanklines * -bullet
- + Ct(LooseListItem(bullet)^1)
- * Cc(false) * skipblanklines ) /
- writer.bulletlist
+ larsers.BulletList = ( Ct(larsers.TightListItem(parsers.bullet)^1) * Cc(true)
+ * parsers.skipblanklines * -parsers.bullet
+ + Ct(larsers.LooseListItem(parsers.bullet)^1) * Cc(false)
+ * parsers.skipblanklines )
+ / writer.bulletlist
- local function orderedlist(items,tight,startNumber)
+ local function ordered_list(items,tight,startNumber)
if options.startNumber then
startNumber = tonumber(startNumber) or 1 -- fallback for '#'
else
@@ -1034,107 +1552,65 @@ function M.reader.new(writer, options)
return writer.orderedlist(items,tight,startNumber)
end
- local OrderedList = Cg(enumerator, "listtype") *
- ( Ct(TightListItem(Cb("listtype")) *
- TightListItem(enumerator)^0)
- * Cc(true) * skipblanklines * -enumerator
- + Ct(LooseListItem(Cb("listtype")) *
- LooseListItem(enumerator)^0)
- * Cc(false) * skipblanklines
- ) * Cb("listtype") / orderedlist
-
- local defstartchar = S("~:")
- local defstart = ( defstartchar * #spacing * (tab + space^-3)
- + space * defstartchar * #spacing * (tab + space^-2)
- + space * space * defstartchar * #spacing *
- (tab + space^-1)
- + space * space * space * defstartchar * #spacing
- )
-
- local dlchunk = Cs(line * (indentedline - blankline)^0)
+ larsers.OrderedList = Cg(larsers.enumerator, "listtype") *
+ ( Ct(larsers.TightListItem(Cb("listtype"))
+ * larsers.TightListItem(larsers.enumerator)^0)
+ * Cc(true) * parsers.skipblanklines * -larsers.enumerator
+ + Ct(larsers.LooseListItem(Cb("listtype"))
+ * larsers.LooseListItem(larsers.enumerator)^0)
+ * Cc(false) * parsers.skipblanklines
+ ) * Cb("listtype") / ordered_list
local function definition_list_item(term, defs, tight)
return { term = parse_inlines(term), definitions = defs }
end
- local DefinitionListItemLoose = C(line) * skipblanklines
- * Ct((defstart *
- indented_blocks(dlchunk) /
- parse_blocks_toplevel)^1)
- * Cc(false)
- / definition_list_item
-
- local DefinitionListItemTight = C(line)
- * Ct((defstart * dlchunk /
- parse_blocks)^1)
- * Cc(true)
- / definition_list_item
-
- local DefinitionList = ( Ct(DefinitionListItemLoose^1) * Cc(false)
- + Ct(DefinitionListItemTight^1)
- * (skipblanklines *
- -DefinitionListItemLoose * Cc(true))
- ) / writer.definitionlist
- local Reference = define_reference_parser / register_link
- local Blank = blankline / ""
- + NoteBlock
- + Reference
- + (tightblocksep / "\n")
- local Paragraph = nonindentspace * Ct(Inline^1) * newline
- * ( blankline^1
- + #hash
- + #(leader * more * space^-1)
- )
- / writer.paragraph
-
- local ToplevelParagraph
- = nonindentspace * Ct(Inline^1) * (newline
- * ( blankline^1
- + #hash
- + #(leader * more * space^-1)
- + eof
- )
- + eof )
- / writer.paragraph
-
- local Plain = nonindentspace * Ct(Inline^1) / writer.plain
- -- parse Atx heading start and return level
- local HeadingStart = #hash * C(hash^-6) * -hash / length
-
- -- parse setext header ending and return level
- local HeadingLevel = equal^1 * Cc(1) + dash^1 * Cc(2)
-
- local function strip_atx_end(s)
- return s:gsub("[#%s]*\n$","")
- end
-
+ larsers.DefinitionListItemLoose = C(parsers.line) * parsers.skipblanklines
+ * Ct((parsers.defstart
+ * parsers.indented_blocks(parsers.dlchunk)
+ / parse_blocks_toplevel)^1)
+ * Cc(false) / definition_list_item
+
+ larsers.DefinitionListItemTight = C(parsers.line)
+ * Ct((parsers.defstart * parsers.dlchunk
+ / parse_blocks)^1)
+ * Cc(true) / definition_list_item
+
+ larsers.DefinitionList = ( Ct(larsers.DefinitionListItemLoose^1) * Cc(false)
+ + Ct(larsers.DefinitionListItemTight^1)
+ * (parsers.skipblanklines
+ * -larsers.DefinitionListItemLoose * Cc(true))
+ ) / writer.definitionlist
+ larsers.Blank = parsers.blankline / ""
+ + larsers.NoteBlock
+ + larsers.Reference
+ + (parsers.tightblocksep / "\n")
-- parse atx header
- local AtxHeading = Cg(HeadingStart,"level")
- * optionalspace
- * (C(line) / strip_atx_end / parse_inlines)
+ larsers.AtxHeading = Cg(parsers.HeadingStart,"level")
+ * parsers.optionalspace
+ * (C(parsers.line) / strip_atx_end / parse_inlines)
* Cb("level")
/ writer.heading
-- parse setext header
- local SetextHeading = #(line * S("=-"))
- * Ct(line / parse_inlines)
- * HeadingLevel
- * optionalspace * newline
- / writer.heading
-
- local Heading = AtxHeading + SetextHeading
- syntax =
+ larsers.SetextHeading = #(parsers.line * S("=-"))
+ * Ct(parsers.line / parse_inlines)
+ * parsers.HeadingLevel
+ * parsers.optionalspace * parsers.newline
+ / writer.heading
+
+ larsers.Heading = larsers.AtxHeading + larsers.SetextHeading
+ local syntax =
{ "Blocks",
- Blocks = Blank^0 *
- Block^-1 *
- (Blank^0 / function()
- return writer.interblocksep
- end * Block)^0 *
- Blank^0 *
- eof,
+ Blocks = larsers.Blank^0 * parsers.Block^-1
+ * (larsers.Blank^0 / function()
+ return writer.interblocksep
+ end
+ * parsers.Block)^0
+ * larsers.Blank^0 * parsers.eof,
- Blank = Blank,
+ Blank = larsers.Blank,
Block = V("Blockquote")
+ V("Verbatim")
@@ -1144,20 +1620,21 @@ function M.reader.new(writer, options)
+ V("OrderedList")
+ V("Heading")
+ V("DefinitionList")
+ + V("DisplayHtml")
+ V("Paragraph")
+ V("Plain"),
- Blockquote = Blockquote,
- Verbatim = Verbatim,
- FencedCode = FencedCode,
- HorizontalRule = HorizontalRule,
- BulletList = BulletList,
- OrderedList = OrderedList,
- Heading = Heading,
- DefinitionList = DefinitionList,
- DisplayHtml = DisplayHtml,
- Paragraph = Paragraph,
- Plain = Plain,
+ Blockquote = larsers.Blockquote,
+ Verbatim = larsers.Verbatim,
+ FencedCode = larsers.FencedCode,
+ HorizontalRule = larsers.HorizontalRule,
+ BulletList = larsers.BulletList,
+ OrderedList = larsers.OrderedList,
+ Heading = larsers.Heading,
+ DefinitionList = larsers.DefinitionList,
+ DisplayHtml = larsers.DisplayHtml,
+ Paragraph = larsers.Paragraph,
+ Plain = larsers.Plain,
Inline = V("Str")
+ V("Space")
@@ -1173,78 +1650,86 @@ function M.reader.new(writer, options)
+ V("Code")
+ V("AutoLinkUrl")
+ V("AutoLinkEmail")
+ + V("InlineHtml")
+ + V("HtmlEntity")
+ V("EscapedChar")
+ V("Smart")
+ V("Symbol"),
- Str = Str,
- Space = Space,
- Endline = Endline,
- UlOrStarLine = UlOrStarLine,
- Strong = Strong,
- Emph = Emph,
- InlineNote = InlineNote,
- NoteRef = NoteRef,
- Citations = Citations,
- Link = Link,
- Image = Image,
- Code = Code,
- AutoLinkUrl = AutoLinkUrl,
- AutoLinkEmail = AutoLinkEmail,
- InlineHtml = InlineHtml,
- HtmlEntity = HtmlEntity,
- EscapedChar = EscapedChar,
- Smart = Smart,
- Symbol = Symbol,
+ Str = larsers.Str,
+ Space = larsers.Space,
+ Endline = larsers.Endline,
+ UlOrStarLine = larsers.UlOrStarLine,
+ Strong = larsers.Strong,
+ Emph = larsers.Emph,
+ InlineNote = larsers.InlineNote,
+ NoteRef = larsers.NoteRef,
+ Citations = larsers.Citations,
+ Link = larsers.Link,
+ Image = larsers.Image,
+ Code = larsers.Code,
+ AutoLinkUrl = larsers.AutoLinkUrl,
+ AutoLinkEmail = larsers.AutoLinkEmail,
+ InlineHtml = larsers.InlineHtml,
+ HtmlEntity = larsers.HtmlEntity,
+ EscapedChar = larsers.EscapedChar,
+ Smart = larsers.Smart,
+ Symbol = larsers.Symbol,
}
if not options.definitionLists then
- syntax.DefinitionList = fail
+ syntax.DefinitionList = parsers.fail
end
if not options.fencedCode then
- syntax.FencedCode = fail
+ syntax.FencedCode = parsers.fail
end
if not options.citations then
- syntax.Citations = fail
+ syntax.Citations = parsers.fail
end
if not options.footnotes then
- syntax.NoteRef = fail
+ syntax.NoteRef = parsers.fail
end
if not options.inlineFootnotes then
- syntax.InlineNote = fail
+ syntax.InlineNote = parsers.fail
end
if not options.smartEllipses then
- syntax.Smart = fail
+ syntax.Smart = parsers.fail
+ end
+
+ if not options.html then
+ syntax.DisplayHtml = parsers.fail
+ syntax.InlineHtml = parsers.fail
+ syntax.HtmlEntity = parsers.fail
end
local blocks_toplevel_t = util.table_copy(syntax)
- blocks_toplevel_t.Paragraph = ToplevelParagraph
- blocks_toplevel = Ct(blocks_toplevel_t)
+ blocks_toplevel_t.Paragraph = larsers.ToplevelParagraph
+ larsers.blocks_toplevel = Ct(blocks_toplevel_t)
- blocks = Ct(syntax)
+ larsers.blocks = Ct(syntax)
local inlines_t = util.table_copy(syntax)
inlines_t[1] = "Inlines"
- inlines_t.Inlines = Inline^0 * (spacing^0 * eof / "")
- inlines = Ct(inlines_t)
+ inlines_t.Inlines = parsers.Inline^0 * (parsers.spacing^0 * parsers.eof / "")
+ larsers.inlines = Ct(inlines_t)
local inlines_no_link_t = util.table_copy(inlines_t)
- inlines_no_link_t.Link = fail
- inlines_no_link = Ct(inlines_no_link_t)
+ inlines_no_link_t.Link = parsers.fail
+ larsers.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 = fail
- inlines_no_inline_note = Ct(inlines_no_inline_note_t)
+ inlines_no_inline_note_t.InlineNote = parsers.fail
+ larsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t)
local inlines_nbsp_t = util.table_copy(inlines_t)
- inlines_nbsp_t.Endline = NonbreakingEndline
- inlines_nbsp_t.Space = NonbreakingSpace
- inlines_nbsp = Ct(inlines_nbsp_t)
+ inlines_nbsp_t.Endline = larsers.NonbreakingEndline
+ inlines_nbsp_t.Space = larsers.NonbreakingSpace
+ larsers.inlines_nbsp = Ct(inlines_nbsp_t)
function self.convert(input)
references = {}
local opt_string = {}