summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/generic/markdown
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/source/generic/markdown
parent34a0db48cc3c734ab21f489d2f710e74a3fda6af (diff)
markdown (5jan17)
git-svn-id: svn://tug.org/texlive/trunk@42868 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/generic/markdown')
-rw-r--r--Master/texmf-dist/source/generic/markdown/markdown.dtx2359
1 files changed, 1387 insertions, 972 deletions
diff --git a/Master/texmf-dist/source/generic/markdown/markdown.dtx b/Master/texmf-dist/source/generic/markdown/markdown.dtx
index 99e9fb04a34..1618ea62478 100644
--- a/Master/texmf-dist/source/generic/markdown/markdown.dtx
+++ b/Master/texmf-dist/source/generic/markdown/markdown.dtx
@@ -46,9 +46,9 @@
\hyperref[lua:#1]{\t`#1`}}
\def\t`#1`{% Inline code
\textcolor{spot}{\text{\texttt{#1}}}}
-\newcommand\Optitem[2][]{% An option item definition
+\newcommand\Optitem[2][]{\penalty -1000\relax % An option item definition
\phantomsection\label{opt:#2}\optitem[#1]{#2}}
-\newcommand\Valitem[2][]{% A value item definition
+\newcommand\Valitem[2][]{\penalty -1000\relax % A value item definition
\phantomsection\label{opt:#2}\valitem[#1]{#2}}
\newcommand\Opt[1]{% An option / value item reference
\hyperref[opt:#1]{\t`#1`}}
@@ -147,10 +147,11 @@
% \fi
% \begin{macrocode}
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
@@ -237,7 +238,20 @@ local md5 = require("md5")
% engine (see \cite[Section~3.3]{luatex16}).
%
% The plain \TeX{} part of the package also requires that either the Lua\TeX{}
-% \m{directlua} primitive or the shell access file stream 18 is available.
+% \m{directlua} primitive or the shell access file stream 18 is available in
+% your \TeX{} engine. If only the shell access file stream is available in your
+% \TeX{} engine (as is the case with \hologo{pdfTeX} and \Hologo{XeTeX}) or if
+% you enforce the use of shell using the \m{markdownMode} macro, then note the
+% following:
+% \begin{itemize}
+% \item Unless your \TeX{} engine is globally configured to enable shell
+% access, you will need to provide the \t`-shell-escape` parameter to your
+% engine when typesetting a document.
+% \item You will need to avoid the use of the \t`-output-directory` \TeX{}
+% parameter when typesetting a document. The parameter causes auxiliary
+% files to be written to a specified output directory, but the shell will
+% be executed in the current directory. Things will not work out.
+% \end{itemize}
%
% \iffalse
%</tex>
@@ -370,6 +384,15 @@ defaultOptions.blankBeforeCodeFence = false
defaultOptions.blankBeforeHeading = false
% \end{macrocode}
%
+% \Optitem[false]{breakableBlockquotes}{\opt{true}, \opt{false}}
+% \begin{valuelist}
+% \item[true] A blank line separates block quotes.
+% \item[false] Blank lines in the middle of a block quote are ignored.
+% \end{valuelist}
+% \begin{macrocode}
+defaultOptions.breakableBlockquotes = false
+% \end{macrocode}
+%
% \Valitem[.]{cacheDir}{directory}
% The path to the directory containing auxiliary cache files.
%
@@ -456,6 +479,22 @@ defaultOptions.definitionLists = false
% \begin{macrocode}
defaultOptions.hashEnumerators = false
% \end{macrocode}
+%
+% \Optitem[false]{html}{\opt{true}, \opt{false}}
+% \begin{valuelist}
+% \item[true] Enable the recognition of \textsc{html} tags, block
+% elements, comments, \textsc{html} instructions, and
+% entities in the input. Tags, block elements (along with
+% contents), \textsc{html} instructions, and comments will
+% be ignored and \textsc{html} entities will be replaced
+% with the corresponding Unicode codepoints.
+% \item[true] Disable the recognition of \textsc{html} markup. Any
+% \textsc{html} markup in the input will be rendered as
+% plain text.
+% \end{valuelist}
+% \begin{macrocode}
+defaultOptions.html = false
+% \end{macrocode}
%
% \Optitem[false]{hybrid}{\opt{true}, \opt{false}}
% \begin{valuelist}
@@ -600,8 +639,8 @@ defaultOptions.tightLists = true
% \ref{sec:luaoptions}) used during the conversion from markdown to plain
% \TeX{}, and for changing the way markdown the tokens are rendered.
% \begin{macrocode}
-\def\markdownLastModified{2016/12/09}%
-\def\markdownVersion{2.2.2}%
+\def\markdownLastModified{2017/01/05}%
+\def\markdownVersion{2.3.0}%
% \end{macrocode}
%
% The plain \TeX{} interface is implemented by the \t`markdown.tex` file that
@@ -739,12 +778,14 @@ defaultOptions.tightLists = true
\let\markdownOptionBlankBeforeBlockquote\undefined
\let\markdownOptionBlankBeforeCodeFence\undefined
\let\markdownOptionBlankBeforeHeading\undefined
+\let\markdownOptionBreakableBlockquotes\undefined
\let\markdownOptionCitations\undefined
\let\markdownOptionCitationNbsps\undefined
\let\markdownOptionDefinitionLists\undefined
\let\markdownOptionFootnotes\undefined
\let\markdownOptionFencedCode\undefined
\let\markdownOptionHashEnumerators\undefined
+\let\markdownOptionHtml\undefined
\let\markdownOptionHybrid\undefined
\let\markdownOptionInlineFootnotes\undefined
\let\markdownOptionPreserveTabs\undefined
@@ -1251,26 +1292,6 @@ defaultOptions.tightLists = true
% error messages.
%
% \subsubsection{Miscellanea}
-% The \mdef{markdownLuaRegisterIBCallback} and
-% \mdef{markdownLuaUnregisterIBCallback} macros specify the Lua code for
-% registering and unregistering a callback for changing the contents of the
-% line input buffer before a \TeX{} engine that supports direct Lua access via
-% the \m{directlua} macro starts looking at it. The first argument of the
-% \m{markdownLuaRegisterIBCallback} macro corresponds to the callback function
-% being registered.
-%
-% Local members defined within \m{markdownLuaRegisterIBCallback} are guaranteed
-% to be visible from \m{markdownLuaUnregisterIBCallback} and the execution of
-% the two macros alternates, so it is not necessary to consider the case, when
-% one of the macros is called twice in a row.
-% \begin{macrocode}
-\def\markdownLuaRegisterIBCallback#1{%
- local old_callback = callback.find("process_input_buffer")
- callback.register("process_input_buffer", #1)}%
-\def\markdownLuaUnregisterIBCallback{%
- callback.register("process_input_buffer", old_callback)}%
-% \end{macrocode}
-%
% The \mdef{markdownMakeOther} macro is used by the package, when a \TeX{}
% engine that does not support direct Lua access is starting to buffer a text.
% The plain \TeX{} implementation changes the category code of plain \TeX{}
@@ -1331,7 +1352,12 @@ defaultOptions.tightLists = true
\fi
% \end{macrocode}
%
-%
+% The following macros are no longer a part of the plain \TeX{} interface and
+% are only defined for backwards compatibility:
+% \begin{macrocode}
+\def\markdownLuaRegisterIBCallback#1{\relax}%
+\def\markdownLuaUnregisterIBCallback#1{\relax}%
+% \end{macrocode}
% \iffalse
%</tex>
%<*latex>
@@ -1448,6 +1474,8 @@ defaultOptions.tightLists = true
\def\markdownOptionBlankBeforeCodeFence{#1}}%
\define@key{markdownOptions}{blankBeforeHeading}[true]{%
\def\markdownOptionBlankBeforeHeading{#1}}%
+\define@key{markdownOptions}{breakableBlockquotes}[true]{%
+ \def\markdownOptionBreakableBlockquotes{#1}}%
\define@key{markdownOptions}{citations}[true]{%
\def\markdownOptionCitations{#1}}%
\define@key{markdownOptions}{citationNbsps}[true]{%
@@ -1462,6 +1490,8 @@ defaultOptions.tightLists = true
\def\markdownOptionFencedCode{#1}}%
\define@key{markdownOptions}{hashEnumerators}[true]{%
\def\markdownOptionHashEnumerators{#1}}%
+\define@key{markdownOptions}{html}[true]{%
+ \def\markdownOptionHtml{#1}}%
\define@key{markdownOptions}{hybrid}[true]{%
\def\markdownOptionHybrid{#1}}%
\define@key{markdownOptions}{inlineFootnotes}[true]{%
@@ -1823,10 +1853,10 @@ local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, any =
% \end{macrocode}
%
% \subsubsection{Utility Functions}
-% This section documents the utility functions used by the Lua code. These
-% functions are encapsulated in the \t`util` object. The functions were
-% originally located in the \t`lunamark/util.lua` file in the Lunamark Lua
-% module.
+% This section documents the utility functions used by the plain \TeX{}
+% writer and the markdown reader. These functions are encapsulated in the
+% \t`util` object. The functions were originally located in the
+% \t`lunamark/util.lua` file in the Lunamark Lua module.
% \begin{macrocode}
local util = {}
% \end{macrocode}
@@ -2059,6 +2089,294 @@ function util.pathname(dir, file)
end
end
% \end{macrocode}
+% \subsubsection{\textsc{html} entities}
+% This section documents the \textsc{html} entities recognized by the
+% markdown reader. These functions are encapsulated in the \t`entities`
+% object. The functions were originally located in the
+% \t`lunamark/entities.lua` file in the Lunamark Lua module.
+% \begin{macrocode}
+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,
+}
+% \end{macrocode}
+%
+% Given a string \t`s` of decimal digits, the \luamdef{entities.dec_entity}
+% returns the corresponding \textsc{utf}8-encoded Unicode codepoint.
+% \begin{macrocode}
+function entities.dec_entity(s)
+ return unicode.utf8.char(tonumber(s))
+end
+% \end{macrocode}
+%
+% Given a string \t`s` of hexadecimal digits, the
+% \luamdef{entities.hex_entity} returns the corresponding
+% \textsc{utf}8-encoded Unicode codepoint.
+% \begin{macrocode}
+function entities.hex_entity(s)
+ return unicode.utf8.char(tonumber("0x"..s))
+end
+% \end{macrocode}
+%
+% Given a character entity name \t`s` (like \t`ouml`), the
+% \luamdef{entities.char_entity} returns the corresponding
+% \textsc{utf}8-encoded Unicode codepoint.
+% \begin{macrocode}
+function entities.char_entity(s)
+ local n = character_entities[s]
+ return unicode.utf8.char(n)
+end
+% \end{macrocode}
+%
% \subsubsection{Plain \TeX{} Writer}\label{sec:texwriter}
% This section documents the \luam{writer} object, which implements the
% routines for producing the \TeX{} output. The object is an amalgamate of the
@@ -2307,6 +2625,15 @@ function M.writer.new(options)
end
% \end{macrocode}
%
+% Define \luamdef{writer->inline_html} and \luamdef{writer->display_html}
+% as functions that will transform an inline or block \textsc{html} element
+% respectively to the output format, where \t`html` is the \textsc{html}
+% input.
+% \begin{macrocode}
+ function self.inline_html(html) return "" end
+ function self.display_html(html) return "" end
+% \end{macrocode}
+%
% Define \luamdef{writer->definitionlist} as a function that will transform an
% input definition list to the output format, where \t`items` is an array of
% tables, each of the form \t`\{ term = t, definitions = defs \}`, where \t`t`
@@ -2441,98 +2768,478 @@ function M.writer.new(options)
return self
end
% \end{macrocode}
-% \subsubsection{Generic \acro{peg} Patterns}
-% These \acro{peg} patterns have been temporarily moved outside the
-% \luam{reader.new} method, which is currently hitting the limit of 200 local
-% variables. To resolve this issue, all the \acro{peg} patterns local to
-% \luam{reader.new} will be moved to a static hash table at some point in the
-% future.
-% \begin{macrocode}
-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
+% \subsubsection{Parsers}
+% The \luamdef{parsers} hash table stores \acro{peg} patterns that are
+% static and can be reused between different \luam{reader} objects.
+% \begin{macrocode}
+local parsers = {}
+% \end{macrocode}
+% \paragraph{Basic Parsers}
+% \begin{macrocode}
+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
+% \end{macrocode}
+% \paragraph{Parsers Used for Markdown Lists}
+% \begin{macrocode}
+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
+ )
+% \end{macrocode}
+% \paragraph{Parsers Used for Markdown Code Spans}
+% \begin{macrocode}
+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
+% \end{macrocode}
+% \paragraph{Parsers Used for Fenced Code Blocks}
+% \begin{macrocode}
+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
+% \end{macrocode}
+% \paragraph{Parsers Used for Markdown Tags and Links}
+% \begin{macrocode}
+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("")
+% \end{macrocode}
+% \paragraph{Parsers Used for Citations}
+% \begin{macrocode}
+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
+% \end{macrocode}
+% \paragraph{Parsers Used for Footnotes}
+% \begin{macrocode}
+local function strip_first_char(s)
+ return s:sub(2)
+end
+
+parsers.RawNoteRef = #(parsers.lbracket * parsers.circumflex)
+ * parsers.tag / strip_first_char
+% \end{macrocode}
+% \paragraph{Parsers Used for \textsc{html}}
+% \begin{macrocode}
+-- 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
+% \end{macrocode}
+% \paragraph{Parsers Used for \textsc{html} entities}
+% \begin{macrocode}
+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
+% \end{macrocode}
+% \paragraph{Helpers for Links and References}
+% \begin{macrocode}
+-- 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
+% \end{macrocode}
+% \paragraph{Inline Elements}
+% \begin{macrocode}
+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
+% \end{macrocode}
+% \paragraph{Block Elements}
+% \begin{macrocode}
+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
+% \end{macrocode}
+% \paragraph{Lists}
+% \begin{macrocode}
+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)
+% \end{macrocode}
+% \paragraph{Headings}
+% \begin{macrocode}
+-- 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
% \end{macrocode}
%
@@ -2570,7 +3277,7 @@ function M.reader.new(writer, options)
return defaultOptions[key] end })
% \end{macrocode}
%
-% \paragraph{Top Level Helper Functions}
+% \paragraph{Top-Level Helper Functions}
% Define \luamdef{normalize_tag} as a function that normalizes a markdown
% reference tag by lowercasing it, and by collapsing any adjacent whitespace
% characters.
@@ -2598,16 +3305,15 @@ function M.reader.new(writer, options)
end
end
% \end{macrocode}
-% \paragraph{Top Level Parsing Functions}
+%
+% The \luamdef{larsers} (as in ``local \luam{parsers}'') hash table stores
+% \acro{peg} patterns that depend on the received \t`options`, which impedes
+% their reuse between different \luam{reader} objects.
+% \begin{macrocode}
+ local larsers = {}
+% \end{macrocode}
+% \paragraph{Top-Level Parser Functions}
% \begin{macrocode}
- local syntax
- local blocks_toplevel
- local blocks
- local inlines
- local inlines_no_link
- local inlines_no_inline_note
- local inlines_nbsp
-
local function create_parser(name, grammar)
return function(str)
local res = lpeg.match(grammar(), str)
@@ -2619,197 +3325,106 @@ 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)
-% \end{macrocode}
-% \paragraph{List \acro{peg} Patterns}
-% \begin{macrocode}
- 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)
+% \end{macrocode}
+% \paragraph{Parsers Used for Markdown Lists (local)}
+% \begin{macrocode}
if options.hashEnumerators then
- dig = digit + hash
+ larsers.dig = parsers.digit + parsers.hash
else
- dig = digit
+ larsers.dig = parsers.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
-% \end{macrocode}
-% \paragraph{Code Span \acro{peg} Patterns}
+ 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
+% \end{macrocode}
+% \paragraph{Parsers Used for Blockquotes (local)}
% \begin{macrocode}
- local openticks = Cg(backtick^1, "ticks")
-
- local function captures_equal_length(s,i,a,b)
- return #a == #b and i
+ -- 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
+
+ if not options.breakableBlockquotes then
+ larsers.blockquote_body = larsers.blockquote_body
+ * (parsers.blankline^0 / "")
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
-% \paragraph{Fenced Code \acro{peg} Patterns}
+% \end{macrocode}
+% \paragraph{Parsers Used for Citations (local)}
% \begin{macrocode}
- 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
-
- local function fencetail(char)
- return nonindentspace
- * Cmt(C(char^3) * Cb("fencelength"),
- captures_geq_length)
- * optionalspace * (newline + eof)
- + eof
- 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
% \end{macrocode}
-% \paragraph{Tag \acro{peg} Patterns}
-% \begin{macrocode}
- 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("")
-% \end{macrocode}
-% \paragraph{Citation \acro{peg} Patterns}
-% \begin{macrocode}
- 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
-% \end{macrocode}
-% \paragraph{Footnote \acro{peg} Patterns}
+% \paragraph{Parsers Used for Footnotes (local)}
% \begin{macrocode}
local rawnotes = {}
- local function strip_first_char(s)
- return s:sub(2)
- end
-
-- like indirect_link
local function lookup_note(ref)
return function()
@@ -2827,19 +3442,18 @@ function M.reader.new(writer, options)
return ""
end
- local RawNoteRef = #(lbracket * circumflex) * tag / strip_first_char
-
- local NoteRef = RawNoteRef / lookup_note
+ larsers.NoteRef = parsers.RawNoteRef / lookup_note
- local NoteBlock = leader * RawNoteRef * colon * spnl
- * indented_blocks(chunk) / register_note
+ larsers.NoteBlock = parsers.leader * parsers.RawNoteRef * parsers.colon
+ * parsers.spnl * parsers.indented_blocks(parsers.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
% \end{macrocode}
-% \paragraph{Link and Image \acro{peg} Patterns}
+% \paragraph{Helpers for Links and References (local)}
% \begin{macrocode}
-- List of references defined in the document
local references
@@ -2850,10 +3464,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)
@@ -2904,273 +3514,247 @@ function M.reader.new(writer, options)
end
end
% \end{macrocode}
-% \paragraph{Spacing \acro{peg} Patterns}
+% \paragraph{Inline Elements (local)}
% \begin{macrocode}
- 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
-% \end{macrocode}
-% \paragraph{String \acro{peg} Rules}
-% \begin{macrocode}
- local Inline = V("Inline")
-
- local Str = normalchar^1 / writer.string
-
- local Symbol = (specialchar - tightblocksep) / writer.string
-% \end{macrocode}
-% \paragraph{Ellipsis \acro{peg} Rules}
-% \begin{macrocode}
- local Ellipsis = P("...") / writer.ellipsis
-
- local Smart = Ellipsis
-% \end{macrocode}
-% \paragraph{Inline Code Block \acro{peg} Rules}
-% \begin{macrocode}
- local Code = inticks / writer.code
-% \end{macrocode}
-% \paragraph{Spacing \acro{peg} Rules}
-% \begin{macrocode}
- 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)
+ larsers.fencestart = parsers.fail
+ else
+ larsers.fencestart = parsers.fencehead(parsers.backtick)
+ + parsers.fencehead(parsers.tilde)
end
-% \end{macrocode}
-% \paragraph{Emphasis \acro{peg} Rules}
-% \begin{macrocode}
- local Strong = ( between(Inline, doubleasterisks, doubleasterisks)
- + between(Inline, doubleunderscores, doubleunderscores)
- ) / writer.strong
-
- local Emph = ( between(Inline, asterisk, asterisk)
- + between(Inline, underscore, underscore)
- ) / writer.emphasis
-% \end{macrocode}
-% \paragraph{Link \acro{peg} Rules}
-% \begin{macrocode}
- 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.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
-% \end{macrocode}
-% \paragraph{Image \acro{peg} Rules}
-% \begin{macrocode}
- local DirectImage = exclamation
- * (tag / parse_inlines)
- * spnl
- * lparent
- * (url + Cc("")) -- link can be empty [foo]()
- * optionaltitle
- * rparent
- / writer.image
+ 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
- local IndirectImage = exclamation * tag * (C(spnl) * tag)^-1 /
- indirect_image
+ larsers.Citations = larsers.TextCitations + larsers.ParenthesizedCitations
- local Image = DirectImage + IndirectImage
-% \end{macrocode}
-% \paragraph{Miscellaneous Inline \acro{peg} Rules}
-% \begin{macrocode}
-- avoid parsing long strings of * or _ as emph/strong
- local UlOrStarLine = asterisk^4 + underscore^4 / writer.string
+ larsers.UlOrStarLine = parsers.asterisk^4 + parsers.underscore^4
+ / writer.string
- local EscapedChar = S("\\") * C(escapable) / writer.string
-% \end{macrocode}
-% \paragraph{Citations \acro{peg} Rules}
-% \begin{macrocode}
- 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
-
- 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 TextCitations = Ct(Cc("")
- * citation_name
- * ((spnl
- * lbracket
- * citation_headless_body
- * rbracket) + Cc(""))) /
- function(raw_cites)
- return citations(true, raw_cites)
- end
+ larsers.EscapedChar = S("\\") * C(parsers.escapable) / writer.string
- local ParenthesizedCitations
- = Ct(lbracket
- * citation_body
- * rbracket) /
- function(raw_cites)
- return citations(false, raw_cites)
- end
-
- local Citations = TextCitations + ParenthesizedCitations
+ larsers.InlineHtml = C(parsers.inlinehtml) / writer.inline_html
+
+ larsers.HtmlEntity = parsers.hexentity / entities.hex_entity / writer.string
+ + parsers.decentity / entities.dec_entity / writer.string
+ + parsers.tagentity / entities.char_entity / writer.string
% \end{macrocode}
-% \paragraph{Code Block \acro{peg} Rules}
+% \paragraph{Block Elements (local)}
% \begin{macrocode}
- local Block = V("Block")
+ larsers.DisplayHtml = C(parsers.displayhtml)
+ / expandtabs / writer.display_html
- local Verbatim = Cs( (blanklines
- * ((indentedline - blankline))^1)^1
+ larsers.Verbatim = Cs( (parsers.blanklines
+ * ((parsers.indentedline - parsers.blankline))^1)^1
) / expandtabs / writer.verbatim
-
- local TildeFencedCode
- = fencehead(tilde)
- * Cs(fencedline(tilde)^0)
- * fencetail(tilde)
- local BacktickFencedCode
- = fencehead(backtick)
- * Cs(fencedline(backtick)^0)
- * fencetail(backtick)
-
- 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
+
+ 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
% \end{macrocode}
-% \paragraph{Blockquote \acro{peg} Patterns}
-% \begin{macrocode}
- -- 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
-% \end{macrocode}
-% \paragraph{Horizontal Rule \acro{peg} Rules}
-% \begin{macrocode}
- local HorizontalRule = ( lineof(asterisk)
- + lineof(dash)
- + lineof(underscore)
- ) / writer.hrule
-% \end{macrocode}
-% \paragraph{List \acro{peg} Rules}
+% \paragraph{Lists (local)}
% \begin{macrocode}
- local starter = bullet + enumerator
+ 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
@@ -3179,119 +3763,74 @@ 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
-% \end{macrocode}
-% \paragraph{Blank Line \acro{peg} Rules}
-% \begin{macrocode}
- local Reference = define_reference_parser / register_link
- local Blank = blankline / ""
- + NoteBlock
- + Reference
- + (tightblocksep / "\n")
-% \end{macrocode}
-% \paragraph{Paragraph \acro{peg} Rules}
-% \begin{macrocode}
- local Paragraph = nonindentspace * Ct(Inline^1) * newline
- * ( blankline^1
- + #hash
- + #(leader * more * space^-1)
- )
- / writer.paragraph
+ larsers.DefinitionListItemLoose = C(parsers.line) * parsers.skipblanklines
+ * Ct((parsers.defstart
+ * parsers.indented_blocks(parsers.dlchunk)
+ / parse_blocks_toplevel)^1)
+ * Cc(false) / definition_list_item
- local ToplevelParagraph
- = nonindentspace * Ct(Inline^1) * (newline
- * ( blankline^1
- + #hash
- + #(leader * more * space^-1)
- + eof
- )
- + eof )
- / writer.paragraph
+ larsers.DefinitionListItemTight = C(parsers.line)
+ * Ct((parsers.defstart * parsers.dlchunk
+ / parse_blocks)^1)
+ * Cc(true) / definition_list_item
- local Plain = nonindentspace * Ct(Inline^1) / writer.plain
+ larsers.DefinitionList = ( Ct(larsers.DefinitionListItemLoose^1) * Cc(false)
+ + Ct(larsers.DefinitionListItemTight^1)
+ * (parsers.skipblanklines
+ * -larsers.DefinitionListItemLoose * Cc(true))
+ ) / writer.definitionlist
% \end{macrocode}
-% \paragraph{Heading \acro{peg} Rules}
+% \paragraph{Blank (local)}
+% \begin{macrocode}
+ larsers.Blank = parsers.blankline / ""
+ + larsers.NoteBlock
+ + larsers.Reference
+ + (parsers.tightblocksep / "\n")
+% \end{macrocode}
+% \paragraph{Headings (local)}
% \begin{macrocode}
- -- 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
-
-- 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
+ larsers.SetextHeading = #(parsers.line * S("=-"))
+ * Ct(parsers.line / parse_inlines)
+ * parsers.HeadingLevel
+ * parsers.optionalspace * parsers.newline
+ / writer.heading
- local Heading = AtxHeading + SetextHeading
+ larsers.Heading = larsers.AtxHeading + larsers.SetextHeading
% \end{macrocode}
-% \paragraph{Top Level \acro{peg} Specification}
+% \paragraph{Syntax Specification}
% \begin{macrocode}
- syntax =
+ 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")
@@ -3301,20 +3840,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")
@@ -3330,78 +3870,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)
% \end{macrocode}
%
% \paragraph{Exported Conversion Function}
@@ -3516,7 +4064,7 @@ return M
\def\markdownRendererDlEndPrototype{}%
\def\markdownRendererDlEndTightPrototype{}%
\def\markdownRendererEmphasisPrototype#1{{\it#1}}%
-\def\markdownRendererStrongEmphasisPrototype#1{{\it#1}}%
+\def\markdownRendererStrongEmphasisPrototype#1{{\bf#1}}%
\def\markdownRendererBlockQuoteBeginPrototype{\par\begingroup\it}%
\def\markdownRendererBlockQuoteEndPrototype{\endgroup\par}%
\def\markdownRendererInputVerbatimPrototype#1{%
@@ -3552,6 +4100,9 @@ return M
\ifx\markdownOptionBlankBeforeHeading\undefined\else
blankBeforeHeading = \markdownOptionBlankBeforeHeading,
\fi
+\ifx\markdownOptionBreakableBlockquotes\undefined\else
+ breakableBlockquotes = \markdownOptionBreakableBlockquotes,
+\fi
\ifx\markdownOptionCacheDir\undefined\else
cacheDir = "\markdownOptionCacheDir",
\fi
@@ -3573,6 +4124,9 @@ return M
\ifx\markdownOptionHashEnumerators\undefined\else
hashEnumerators = \markdownOptionHashEnumerators,
\fi
+\ifx\markdownOptionHtml\undefined\else
+ html = \markdownOptionHtml,
+\fi
\ifx\markdownOptionHybrid\undefined\else
hybrid = \markdownOptionHybrid,
\fi
@@ -3617,128 +4171,18 @@ local convert = md.new(\markdownLuaOptions)
}%
% \end{macrocode}
%
-% \subsubsection{Lua Shell Escape Bridge}\label{sec:luabridge}
-% The following \TeX{} code is intended for \TeX{} engines that do not provide
-% direct access to Lua, but expose the shell of the operating system. This
-% corresponds to the \m{markdownMode} values of \t`0` and \t`1`.
-%
-% The \m{markdownLuaExecute} and \m{markdownReadAndConvert} macros defined here
-% and in Section \ref{sec:directlua} are meant to be transparent to the
-% remaining code.
-%
-% The package assumes that although the user is not using the Lua\TeX{} engine,
-% their TeX distribution contains it, and uses shell access to produce and
-% execute Lua scripts using the \TeX{}Lua interpreter (see
-% \cite[Section~3.1.1]{luatex16}).
-% \begin{macrocode}
-
-\ifnum\markdownMode<2\relax
-\ifnum\markdownMode=0\relax
- \markdownInfo{Using mode 0: Shell escape via write18}%
-\else
- \markdownInfo{Using mode 1: Shell escape via os.execute}%
-\fi
-% \end{macrocode}
-%
+% \subsubsection{Buffering Markdown Input}
% The macro \mdef{markdownLuaExecuteFileStream} contains the number of the output
% file stream that will be used to store the helper Lua script in the file named
% \m{markdownOptionHelperScriptFileName} during the expansion of the macro
-% \m{markdownLuaExecute}, and to store the markdown input in the file
-% named \m{markdownOptionInputTempFileName} during the expansion of the macro
+% \m{markdownLuaExecute} when the Lua shell escape bridge is in use, and to
+% store the markdown input in the file named
+% \m{markdownOptionInputTempFileName} during the expansion of the macro
% \m{markdownReadAndConvert}.
% \begin{macrocode}
\csname newwrite\endcsname\markdownLuaExecuteFileStream
% \end{macrocode}
%
-% The \mdef{markdownExecuteShellEscape} macro contains the numeric value indicating
-% whether the shell access is enabled (\t`1`), disabled (\t`0`), or restricted
-% (\t`2`).
-%
-% Inherit the value of the the \m{pdfshellescape} (Lua\TeX{}, \Hologo{pdfTeX})
-% or the \m{shellescape} (\Hologo{XeTeX}) commands. If neither of these
-% commands is defined and Lua is available, attempt to access the
-% \luam{status.shell_escape} configuration item.
-%
-% If you cannot detect, whether the shell access is enabled, act as if it were.
-% \begin{macrocode}
-\ifx\pdfshellescape\undefined
- \ifx\shellescape\undefined
- \ifnum\markdownMode=0\relax
- \def\markdownExecuteShellEscape{1}%
- \else
- \def\markdownExecuteShellEscape{%
- \directlua{tex.sprint(status.shell_escape or "1")}}%
- \fi
- \else
- \let\markdownExecuteShellEscape\shellescape
- \fi
-\else
- \let\markdownExecuteShellEscape\pdfshellescape
-\fi
-% \end{macrocode}
-%
-% The \mdef{markdownExecuteDirect} macro executes the code it has received as
-% its first argument by writing it to the output file stream 18, if Lua is
-% unavailable, or by using the Lua \luam{markdown.execute} method otherwise.
-% \begin{macrocode}
-\ifnum\markdownMode=0\relax
- \def\markdownExecuteDirect#1{\immediate\write18{#1}}%
-\else
- \def\markdownExecuteDirect#1{%
- \directlua{os.execute("\luaescapestring{#1}")}}%
-\fi
-% \end{macrocode}
-%
-% The \mdef{markdownExecute} macro is a wrapper on top of
-% \m{markdownExecuteDirect} that checks the value of
-% \m{markdownExecuteShellEscape} and prints an error message if the shell is
-% inaccessible.
-% \begin{macrocode}
-\def\markdownExecute#1{%
- \ifnum\markdownExecuteShellEscape=1\relax
- \markdownExecuteDirect{#1}%
- \else
- \markdownError{I can not access the shell}{Either run the TeX
- compiler with the --shell-escape or the --enable-write18 flag,
- or set shell_escape=t in the texmf.cnf file}%
- \fi}%
-% \end{macrocode}
-%
-% The \mdef{markdownLuaExecute} macro executes the Lua code it has received as
-% its first argument. The Lua code may not directly interact with the \TeX{}
-% engine, but it can use the \luam{print} function in the same manner it
-% would use the \luam{tex.print} method.
-% \begin{macrocode}
-\def\markdownLuaExecute#1{%
-% \end{macrocode}
-% Create the file \m{markdownOptionHelperScriptFileName} and fill it with the
-% input Lua code prepended with \pkg{kpathsea} initialization, so that Lua
-% modules from the \TeX{} distribution are available.
-% \begin{macrocode}
- \immediate\openout\markdownLuaExecuteFileStream=%
- \markdownOptionHelperScriptFileName
- \markdownInfo{Writing a helper Lua script to the file
- "\markdownOptionHelperScriptFileName"}%
- \immediate\write\markdownLuaExecuteFileStream{%
- local kpse = require('kpse')
- kpse.set_program_name('luatex') #1}%
- \immediate\closeout\markdownLuaExecuteFileStream
-% \end{macrocode}
-% Execute the generated \m{markdownOptionHelperScriptFileName} Lua script using
-% the \TeX{}Lua binary and store the output in the
-% \m{markdownOptionOutputTempFileName} file.
-% \begin{macrocode}
- \markdownInfo{Executing a helper Lua script from the file
- "\markdownOptionHelperScriptFileName" and storing the result in the
- file "\markdownOptionOutputTempFileName"}%
- \markdownExecute{texlua "\markdownOptionHelperScriptFileName" >
- "\markdownOptionOutputTempFileName"}%
-% \end{macrocode}
-% \m{input} the generated \m{markdownOptionOutputTempFileName} file.
-% \begin{macrocode}
- \input\markdownOptionOutputTempFileName\relax}%
-% \end{macrocode}
-%
% The \mdef{markdownReadAndConvertTab} macro contains the tab character literal.
% \begin{macrocode}
\begingroup
@@ -3834,114 +4278,131 @@ local convert = md.new(\markdownLuaOptions)
|endgroup
% \end{macrocode}
%
-% \subsubsection{Direct Lua Access}\label{sec:directlua}
-% The following \TeX{} code is intended for \TeX{} engines that provide
-% direct access to Lua (Lua\TeX{}). The \m{markdownLuaExecute} and
-% \m{markdownReadAndConvert} defined here and in Section \ref{sec:luabridge}
-% are meant to be transparent to the remaining code. This corresponds to the
-% \m{markdownMode} value of \t`2`.
+% \subsubsection{Lua Shell Escape Bridge}\label{sec:luabridge}
+% The following \TeX{} code is intended for \TeX{} engines that do not provide
+% direct access to Lua, but expose the shell of the operating system. This
+% corresponds to the \m{markdownMode} values of \t`0` and \t`1`.
+%
+% The \m{markdownLuaExecute} macro defined here and in Section
+% \ref{sec:directlua} are meant to be indistinguishable to the remaining code.
+%
+% The package assumes that although the user is not using the Lua\TeX{} engine,
+% their TeX distribution contains it, and uses shell access to produce and
+% execute Lua scripts using the \TeX{}Lua interpreter (see
+% \cite[Section~3.1.1]{luatex16}).
% \begin{macrocode}
+\ifnum\markdownMode<2\relax
+\ifnum\markdownMode=0\relax
+ \markdownInfo{Using mode 0: Shell escape via write18}%
\else
-\markdownInfo{Using mode 2: Direct Lua access}%
-% \end{macrocode}
-% The direct Lua access version of the \m{markdownLuaExecute} macro is defined
-% in terms of the \m{directlua} primitive. The \luam{print} function is set as
-% an alias to the \m{tex.print} method in order to mimic the behaviour of the
-% \m{markdownLuaExecute} definition from Section \ref{sec:luabridge},
-% \begin{macrocode}
-\def\markdownLuaExecute#1{\directlua{local print = tex.print #1}}%
+ \markdownInfo{Using mode 1: Shell escape via os.execute}%
+\fi
% \end{macrocode}
%
-% In the definition of the direct Lua access version of the
-% \m{markdownReadAndConvert} macro, we will be using the hash symbol
-% (\t`\#`), the underscore symbol (\t`_`), the circumflex symbol (\t`\^`), the
-% dollar sign (\t`\$`), the backslash symbol (\t`\textbackslash`), the percent
-% sign (\t`\%`), and the braces (\t`\{\}`) as a part of the Lua syntax.
-% \begin{macrocode}
-\begingroup
-% \end{macrocode}
+% The \mdef{markdownExecuteShellEscape} macro contains the numeric value indicating
+% whether the shell access is enabled (\t`1`), disabled (\t`0`), or restricted
+% (\t`2`).
+%
+% Inherit the value of the the \m{pdfshellescape} (Lua\TeX{}, \Hologo{pdfTeX})
+% or the \m{shellescape} (\Hologo{XeTeX}) commands. If neither of these
+% commands is defined and Lua is available, attempt to access the
+% \luam{status.shell_escape} configuration item.
%
-% To this end, we will make the underscore symbol, the dollar sign, and
-% circumflex symbols ordinary characters,
-% \begin{macrocode}
- \catcode`\_=12%
- \catcode`\$=12%
- \catcode`\^=12%
-% \end{macrocode}
-% swap the category code of the hash symbol with the slash symbol (\t`/`).
-% \begin{macrocode}
- \catcode`\/=6%
- \catcode`\#=12%
-% \end{macrocode}
-% swap the category code of the percent sign with the at symbol (\t`@`).
-% \begin{macrocode}
- \catcode`\@=14%
- \catcode`\%=12%
-% \end{macrocode}
-% swap the category code of the backslash symbol with the pipe symbol (\t`|`),
-% \begin{macrocode}
- \catcode`|=0@
- \catcode`\\=12@
-% \end{macrocode}
-% Braces are a part of the plain \TeX{} syntax, but they are not removed during
-% expansion, so we do not need to bother with changing their category codes.
+% If you cannot detect, whether the shell access is enabled, act as if it were.
% \begin{macrocode}
- |gdef|markdownReadAndConvert/1/2{@
+\ifx\pdfshellescape\undefined
+ \ifx\shellescape\undefined
+ \ifnum\markdownMode=0\relax
+ \def\markdownExecuteShellEscape{1}%
+ \else
+ \def\markdownExecuteShellEscape{%
+ \directlua{tex.sprint(status.shell_escape or "1")}}%
+ \fi
+ \else
+ \let\markdownExecuteShellEscape\shellescape
+ \fi
+\else
+ \let\markdownExecuteShellEscape\pdfshellescape
+\fi
% \end{macrocode}
-% Make the \mdef{markdownReadAndConvertAfter} macro store the token sequence
-% that will be inserted into the document after the ending token sequence has
-% been found.
+%
+% The \mdef{markdownExecuteDirect} macro executes the code it has received as
+% its first argument by writing it to the output file stream 18, if Lua is
+% unavailable, or by using the Lua \luam{markdown.execute} method otherwise.
% \begin{macrocode}
- |def|markdownReadAndConvertAfter{/2}@
- |markdownInfo{Buffering markdown input and scanning for the
- closing token sequence "/1"}@
- |directlua{@
+\ifnum\markdownMode=0\relax
+ \def\markdownExecuteDirect#1{\immediate\write18{#1}}%
+\else
+ \def\markdownExecuteDirect#1{%
+ \directlua{os.execute("\luaescapestring{#1}")}}%
+\fi
% \end{macrocode}
-% Set up an empty Lua table that will serve as our buffer.
+%
+% The \mdef{markdownExecute} macro is a wrapper on top of
+% \m{markdownExecuteDirect} that checks the value of
+% \m{markdownExecuteShellEscape} and prints an error message if the shell is
+% inaccessible.
% \begin{macrocode}
- |markdownPrepare
- local buffer = {}
+\def\markdownExecute#1{%
+ \ifnum\markdownExecuteShellEscape=1\relax
+ \markdownExecuteDirect{#1}%
+ \else
+ \markdownError{I can not access the shell}{Either run the TeX
+ compiler with the --shell-escape or the --enable-write18 flag,
+ or set shell_escape=t in the texmf.cnf file}%
+ \fi}%
% \end{macrocode}
-% Create a regex that will match the ending input sequence. Escape any special
-% regex characters (like a star inside \t`\textbackslash end\{markdown*\}`)
-% inside the input.
+%
+% The \mdef{markdownLuaExecute} macro executes the Lua code it has received as
+% its first argument. The Lua code may not directly interact with the \TeX{}
+% engine, but it can use the \luam{print} function in the same manner it
+% would use the \luam{tex.print} method.
% \begin{macrocode}
- local ending_sequence = "^.-" .. ([[/1]]):gsub(
- "([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1")
+\def\markdownLuaExecute#1{%
% \end{macrocode}
-% Register a callback that will notify you about new lines of input.
+% Create the file \m{markdownOptionHelperScriptFileName} and fill it with the
+% input Lua code prepended with \pkg{kpathsea} initialization, so that Lua
+% modules from the \TeX{} distribution are available.
% \begin{macrocode}
- |markdownLuaRegisterIBCallback{function(line)
+ \immediate\openout\markdownLuaExecuteFileStream=%
+ \markdownOptionHelperScriptFileName
+ \markdownInfo{Writing a helper Lua script to the file
+ "\markdownOptionHelperScriptFileName"}%
+ \immediate\write\markdownLuaExecuteFileStream{%
+ local kpse = require('kpse')
+ kpse.set_program_name('luatex') #1}%
+ \immediate\closeout\markdownLuaExecuteFileStream
% \end{macrocode}
-% When the ending token sequence appears on a line, unregister the callback,
-% convert the contents of our buffer from markdown to plain \TeX{}, and insert
-% the result into the input line buffer of \TeX{}.
+% Execute the generated \m{markdownOptionHelperScriptFileName} Lua script using
+% the \TeX{}Lua binary and store the output in the
+% \m{markdownOptionOutputTempFileName} file.
% \begin{macrocode}
- if line:match(ending_sequence) then
- |markdownLuaUnregisterIBCallback;
- local input = table.concat(buffer, "\n") .. "\n"
- local output = convert(input)
- return [[\markdownInfo{The ending token sequence was found}]] ..
- output .. [[\markdownReadAndConvertAfter]]
+ \markdownInfo{Executing a helper Lua script from the file
+ "\markdownOptionHelperScriptFileName" and storing the result in the
+ file "\markdownOptionOutputTempFileName"}%
+ \markdownExecute{texlua "\markdownOptionHelperScriptFileName" >
+ "\markdownOptionOutputTempFileName"}%
% \end{macrocode}
-% When the ending token sequence does not appear on a line, store the line in
-% our buffer, and insert either \m{fi}, if this is the first line of input,
-% or an empty token list to the input line buffer of \TeX{}.
+% \m{input} the generated \m{markdownOptionOutputTempFileName} file.
% \begin{macrocode}
- else
- buffer[#buffer+1] = line
- return [[\]] .. (#buffer == 1 and "fi" or "relax")
- end
- end}}@
+ \input\markdownOptionOutputTempFileName\relax}%
% \end{macrocode}
-% Insert \m{iffalse} after the \m{markdownReadAndConvert} macro in order to
-% consume the rest of the first line of input.
+%
+% \subsubsection{Direct Lua Access}\label{sec:directlua}
+% The following \TeX{} code is intended for \TeX{} engines that provide
+% direct access to Lua (Lua\TeX{}). The macro \m{markdownLuaExecute} defined
+% here and in Section \ref{sec:luabridge} are meant to be indistinguishable to
+% the remaining code. This corresponds to the \m{markdownMode} value of \t`2`.
% \begin{macrocode}
- |iffalse}@
+\else
+\markdownInfo{Using mode 2: Direct Lua access}%
% \end{macrocode}
-% Reset the character categories back to the former state.
+% The direct Lua access version of the \m{markdownLuaExecute} macro is defined
+% in terms of the \m{directlua} primitive. The \luam{print} function is set as
+% an alias to the \m{tex.print} method in order to mimic the behaviour of the
+% \m{markdownLuaExecute} definition from Section \ref{sec:luabridge},
% \begin{macrocode}
- |endgroup
+\def\markdownLuaExecute#1{\directlua{local print = tex.print #1}}%
\fi
% \end{macrocode}
%
@@ -4128,12 +4589,6 @@ local convert = md.new(\markdownLuaOptions)
dlItem = {\item[#1]},
dlEnd = {\end{description}},
emphasis = {\emph{#1}},
- strongEmphasis = {%
- \ifx\alert\undefined
- \textbf{\emph{#1}}%
- \else % Beamer support
- \alert{\emph{#1}}%
- \fi},
blockQuoteBegin = {\begin{quotation}},
blockQuoteEnd = {\end{quotation}},
inputVerbatim = {\VerbatimInput{#1}},
@@ -4160,7 +4615,27 @@ local convert = md.new(\markdownLuaOptions)
\fi},
horizontalRule = {\noindent\rule[0.5ex]{\linewidth}{1pt}},
footnote = {\footnote{#1}}}}
-
+% \end{macrocode}
+%
+% Support the nesting of strong emphasis.
+% \begin{macrocode}
+\newif\ifmarkdownLATEXStrongEmphasisNested
+\markdownLATEXStrongEmphasisNestedfalse
+\markdownSetup{rendererPrototypes={
+ strongEmphasis = {%
+ \ifmarkdownLATEXStrongEmphasisNested
+ \markdownLATEXStrongEmphasisNestedfalse
+ \textmd{#1}%
+ \markdownLATEXStrongEmphasisNestedtrue
+ \else
+ \markdownLATEXStrongEmphasisNestedtrue
+ \textbf{#1}%
+ \markdownLATEXStrongEmphasisNestedfalse
+ \fi}}}
+% \end{macrocode}
+%
+% Support \LaTeX{} document classes that do not provide chapters.
+% \begin{macrocode}
\ifx\chapter\undefined
\markdownSetup{rendererPrototypes = {
headingOne = {\section{#1}},
@@ -4235,37 +4710,6 @@ local convert = md.new(\markdownLuaOptions)
% \end{macrocode}
%
% \subsubsection{Miscellanea}
-% Unlike base Lua\TeX{}, which only allows for a single registered function per
-% a callback (see \cite[Section~8.1]{luatex16}), the \Hologo{LaTeX2e} format
-% disables the \luam{callback.register} method and exposes the
-% \luam{luatexbase.add_to_callback} and \luam{luatexbase.remove_from_callback}
-% methods that enable the user code to hook several functions on a single
-% callback (see \cite[Section~73.4]{latex16}).
-%
-% To make our code function with the \Hologo{LaTeX2e} format, we need to
-% redefine the \m{markdownLuaRegisterIBCallback} and
-% \m{markdownLuaUnregisterIBCallback} macros accordingly.
-% \begin{macrocode}
-\let\markdownLuaRegisterIBCallbackPrevious
- \markdownLuaRegisterIBCallback
-\let\markdownLuaUnregisterIBCallbackPrevious
- \markdownLuaUnregisterIBCallback
-\renewcommand\markdownLuaRegisterIBCallback[1]{%
- if luatexbase and luatexbase.add_to_callback then
- luatexbase.add_to_callback("process_input_buffer", #1, %
- "The markdown input processor")
- else
- \markdownLuaRegisterIBCallbackPrevious{#1}
- end}
-\renewcommand\markdownLuaUnregisterIBCallback{%
- if luatexbase and luatexbase.add_to_callback then
- luatexbase.remove_from_callback("process_input_buffer",%
- "The markdown input processor")
- else
- \markdownLuaUnregisterIBCallbackPrevious;
- end}
-% \end{macrocode}
-%
% When buffering user input, we should disable the bytes with the high bit set,
% since these are made active by the \pkg{inputenc} package. We will do this by
% redefining the \m{markdownMakeOther} macro accordingly. The code is courtesy
@@ -4293,54 +4737,25 @@ local convert = md.new(\markdownLuaOptions)
% \begin{macrocode}
\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&%
\do\#\do\^\do\_\do\%\do\~}%
-% \end{macrocode}
-% When there is no Lua support, then just load the plain \TeX{} implementation.
-% \begin{macrocode}
-\ifx\directlua\undefined
- \input markdown
-\else
-% \end{macrocode}
-% When there is Lua support, check if we can set the \t`process_input_buffer`
-% Lua\TeX{} callback.
-% \begin{macrocode}
- \directlua{%
- local function unescape(str)
- return (str:gsub("|", string.char(92))) end
- local old_callback = callback.find("process_input_buffer")
- callback.register("process_input_buffer", function() end)
- local new_callback = callback.find("process_input_buffer")
-% \end{macrocode}
-%
-% If we can not, we are probably using ConTeXt Mark IV. In ConTeXt Mark IV, the
-% \t`process_input_buffer` callback is currently frozen (inaccessible from the
-% user code) and, due to the lack of available documentation, it is unclear to
-% me how to emulate it. As a workaround, we will force the plain \TeX{}
-% implementation to use the Lua shell escape bridge (see Section
-% \ref{sec:luabridge}) by setting the \m{markdownMode} macro to the value of
-% \t`1`.
-% \begin{macrocode}
- if new_callback == false then
- tex.print(unescape([[|def|markdownMode{1}|input markdown]]))
+\input markdown
% \end{macrocode}
%
-% If we can set the \t`process_input_buffer` Lua\TeX{} callback, then just load
-% the plain \TeX{} implementation.
+% When buffering user input, we should disable the bytes with the high bit set,
+% since these are made active by the \m{enableregime} macro. We will do this
+% by redefining the \m{markdownMakeOther} macro accordingly. The code is
+% courtesy of Scott Pakin, the creator of the \pkg{filecontents} LaTeX package.
% \begin{macrocode}
- else
- callback.register("process_input_buffer", old_callback)
- tex.print(unescape("|input markdown"))
- end}%
-\fi
+\def\markdownMakeOther{%
+ \count0=128\relax
+ \loop
+ \catcode\count0=11\relax
+ \advance\count0 by 1\relax
+ \ifnum\count0<256\repeat
% \end{macrocode}
-%
-% If the shell escape bridge is being used, define the \m{markdownMakeOther}
-% macro, so that the pipe character (\t`|`) is inactive during the scanning.
+% On top of that, make the pipe character (\t`|`) inactive during the scanning.
% This is necessary, since the character is active in \Hologo{ConTeXt}.
% \begin{macrocode}
-\ifnum\markdownMode<2\relax
- \def\markdownMakeOther{%
- \catcode`|=12}%
-\fi
+ \catcode`|=12}%
% \end{macrocode}
%
% \subsubsection{Logging Facilities}
@@ -4426,7 +4841,7 @@ local convert = md.new(\markdownLuaOptions)
\def\markdownRendererDlEndTightPrototype{%
\stopMarkdownConTeXtDlTightPrototype}%
\def\markdownRendererEmphasisPrototype#1{{\em#1}}%
-\def\markdownRendererStrongEmphasisPrototype#1{{\bf\em#1}}%
+\def\markdownRendererStrongEmphasisPrototype#1{{\bf#1}}%
\def\markdownRendererBlockQuoteBeginPrototype{\startquotation}%
\def\markdownRendererBlockQuoteEndPrototype{\stopquotation}%
\def\markdownRendererInputVerbatimPrototype#1{\typefile{#1}}%
@@ -4436,7 +4851,7 @@ local convert = md.new(\markdownLuaOptions)
\else
% \end{macrocode}
% The code fence infostring is used as a name from the \Hologo{ConTeXt}
-% \m{definetyping} command. This allows the user to set up code highlighting
+% \m{definetyping} macro. This allows the user to set up code highlighting
% mapping as follows:
% \begin{Verbatim}
% % Map the `TEX` syntax highlighter to the `latex` infostring.