texlive[42868] Master/texmf-dist: markdown (5jan17)

commits+karl at tug.org commits+karl at tug.org
Thu Jan 5 23:56:40 CET 2017


Revision: 42868
          http://tug.org/svn/texlive?view=revision&revision=42868
Author:   karl
Date:     2017-01-05 23:56:40 +0100 (Thu, 05 Jan 2017)
Log Message:
-----------
markdown (5jan17)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/generic/markdown/markdown.pdf
    trunk/Master/texmf-dist/source/generic/markdown/markdown.dtx
    trunk/Master/texmf-dist/tex/context/third/markdown/t-markdown.tex
    trunk/Master/texmf-dist/tex/generic/markdown/markdown.tex
    trunk/Master/texmf-dist/tex/latex/markdown/markdown.sty
    trunk/Master/texmf-dist/tex/luatex/markdown/markdown.lua

Modified: trunk/Master/texmf-dist/doc/generic/markdown/markdown.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/generic/markdown/markdown.dtx
===================================================================
--- trunk/Master/texmf-dist/source/generic/markdown/markdown.dtx	2017-01-05 21:35:36 UTC (rev 42867)
+++ trunk/Master/texmf-dist/source/generic/markdown/markdown.dtx	2017-01-05 22:56:40 UTC (rev 42868)
@@ -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 @@
 % 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.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 @@
 %  \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 @@
 % \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,6 +778,7 @@
 \let\markdownOptionBlankBeforeBlockquote\undefined
 \let\markdownOptionBlankBeforeCodeFence\undefined
 \let\markdownOptionBlankBeforeHeading\undefined
+\let\markdownOptionBreakableBlockquotes\undefined
 \let\markdownOptionCitations\undefined
 \let\markdownOptionCitationNbsps\undefined
 \let\markdownOptionDefinitionLists\undefined
@@ -745,6 +785,7 @@
 \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 @@
 % 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 @@
 \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 @@
   \def\markdownOptionBlankBeforeCodeFence{#1}}%
 \define at key{markdownOptions}{blankBeforeHeading}[true]{%
   \def\markdownOptionBlankBeforeHeading{#1}}%
+\define at key{markdownOptions}{breakableBlockquotes}[true]{%
+  \def\markdownOptionBreakableBlockquotes{#1}}%
 \define at key{markdownOptions}{citations}[true]{%
   \def\markdownOptionCitations{#1}}%
 \define at key{markdownOptions}{citationNbsps}[true]{%
@@ -1462,6 +1490,8 @@
   \def\markdownOptionFencedCode{#1}}%
 \define at key{markdownOptions}{hashEnumerators}[true]{%
   \def\markdownOptionHashEnumerators{#1}}%
+\define at key{markdownOptions}{html}[true]{%
+  \def\markdownOptionHtml{#1}}%
 \define at key{markdownOptions}{hybrid}[true]{%
   \def\markdownOptionHybrid{#1}}%
 \define at key{markdownOptions}{inlineFootnotes}[true]{%
@@ -1823,10 +1853,10 @@
 %    \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 @@
   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 @@
   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,100 +2768,480 @@
   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.
+% \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 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 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")
 
-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(":;,.#$%&-+?<>~/")
+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(":;,.#$%&-+?<>~/")
 
-local doubleasterisks        = P("**")
-local doubleunderscores      = P("__")
-local fourspaces             = P("    ")
+parsers.doubleasterisks        = P("**")
+parsers.doubleunderscores      = P("__")
+parsers.fourspaces             = P("    ")
 
-local any                    = P(1)
-local fail                   = any - 1
-local always                 = P("")
+parsers.any                    = P(1)
+parsers.fail                   = parsers.any - 1
 
-local escapable              = S("\\`*_{}[]()+_.!<>#-~:^@;")
+parsers.escapable              = S("\\`*_{}[]()+_.!<>#-~:^@;")
+parsers.anyescaped             = P("\\") / "" * parsers.escapable
+                               + parsers.any
 
-local anyescaped             = P("\\") / "" * escapable
-                             + any
+parsers.spacechar              = S("\t ")
+parsers.spacing                = S(" \n\r\t")
+parsers.nonspacechar           = parsers.any - parsers.spacing
+parsers.optionalspace          = parsers.spacechar^0
 
-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")
+parsers.specialchar            = S("*_`&[]<!\\. at -^")
 
-local specialchar            = S("*_`&[]<!\\. at -^")
+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)
 
-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)
+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
 
-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
+parsers.chunk                  = parsers.line * (parsers.optionallyindentedline
+                                                - parsers.blankline)^0
 
-local chunk = line * (optionallyindentedline - 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}
 % 
 % \subsubsection{Markdown Reader}\label{sec:markdownreader}
 % This section documents the \luam{reader} object, which implements the
@@ -2570,7 +3277,7 @@
     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 @@
                  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 syntax
-  local blocks_toplevel
-  local blocks
-  local inlines
-  local inlines_no_link
-  local inlines_no_inline_note
-  local inlines_nbsp
-
+  local larsers    = {}
+%    \end{macrocode}
+% \paragraph{Top-Level Parser Functions}
+%  \begin{macrocode}
   local function create_parser(name, grammar)
     return function(str)
       local res = lpeg.match(grammar(), str)
@@ -2619,197 +3325,106 @@
     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 parse_blocks
+    = create_parser("parse_blocks",
+                    function()
+                      return larsers.blocks
+                    end)
 
-  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_toplevel
+    = create_parser("parse_blocks_toplevel",
+                    function()
+                      return larsers.blocks_toplevel
+                    end)
 
-  if options.hashEnumerators then
-    dig = digit + hash
-  else
-    dig = digit
-  end
+  local parse_inlines
+    = create_parser("parse_inlines",
+                    function()
+                      return larsers.inlines
+                    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}
-%  \begin{macrocode}
-  local openticks   = Cg(backtick^1, "ticks")
+  local parse_inlines_no_link
+    = create_parser("parse_inlines_no_link",
+                    function()
+                      return larsers.inlines_no_link
+                    end)
 
-  local function captures_equal_length(s,i,a,b)
-    return #a == #b and i
-  end
+  local parse_inlines_no_inline_note
+    = create_parser("parse_inlines_no_inline_note",
+                    function()
+                      return larsers.inlines_no_inline_note
+                    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}
+  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}
-  local function captures_geq_length(s,i,a,b)
-    return #a >= #b and i
+  if options.hashEnumerators then
+    larsers.dig = parsers.digit + parsers.hash
+  else
+    larsers.dig = parsers.digit
   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
-
-  local function fencedline(char)
-    return               C(line - fencetail(char))
-                       / function(s)
-                             return s:gsub("^" .. string.rep(" ?",
-                                 fenceindent), "")
-                         end
-  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
 %    \end{macrocode}
-% \paragraph{Tag \acro{peg} Patterns}
+% \paragraph{Parsers Used for Blockquotes (local)}
 %  \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("")
+  -- 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
 %    \end{macrocode}
-% \paragraph{Citation \acro{peg} Patterns}
+% \paragraph{Parsers Used for Citations (local)}
 %  \begin{macrocode}
-  local citation_name = Cs(dash^-1) * at
-                      * Cs(alphanumeric
-                          * (alphanumeric + internal_punctuation
-                              - comma - semicolon)^0)
+  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 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 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{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 @@
     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
 %    \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 @@
       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 @@
     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
+    larsers.fencestart = parsers.fail
+  else
+    larsers.fencestart = parsers.fencehead(parsers.backtick)
+                       + parsers.fencehead(parsers.tilde)
   end
-%    \end{macrocode}
-% \paragraph{String \acro{peg} Rules}
-%  \begin{macrocode}
-  local Inline    = V("Inline")
 
-  local Str       = normalchar^1 / writer.string
+  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
 
-  local Symbol    = (specialchar - tightblocksep) / writer.string
-%    \end{macrocode}
-% \paragraph{Ellipsis \acro{peg} Rules}
-%  \begin{macrocode}
-  local Ellipsis  = P("...") / writer.ellipsis
+  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
 
-  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
+  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
 
-  local Space     = spacechar^2 * Endline / writer.linebreak
-                  + spacechar^1 * Endline^-1 * eof / ""
-                  + spacechar^1 * Endline^-1 * optionalspace / writer.space
+  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
 
-  local NonbreakingEndline
-                  = newline * -( -- newline, but not before...
-                        blankline -- paragraph break
-                      + tightblocksep  -- nested list
-                      + eof       -- end of document
-                      + bqstart
-                      + headerstart
-                      + fencestart
-                    ) * spacechar^0 / writer.nbsp
+  larsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks,
+                                     parsers.doubleasterisks)
+                   + parsers.between(parsers.Inline, parsers.doubleunderscores,
+                                     parsers.doubleunderscores)
+                   ) / writer.strong
 
-  local NonbreakingSpace
-                  = spacechar^2 * Endline / writer.linebreak
-                  + spacechar^1 * Endline^-1 * eof / ""
-                  + spacechar^1 * Endline^-1 * optionalspace / writer.nbsp
+  larsers.Emph   = ( parsers.between(parsers.Inline, parsers.asterisk,
+                                     parsers.asterisk)
+                   + parsers.between(parsers.Inline, parsers.underscore,
+                                     parsers.underscore)
+                   ) / writer.emphasis
 
-  -- 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
-%    \end{macrocode}
-% \paragraph{Emphasis \acro{peg} Rules}
-%  \begin{macrocode}
-  local Strong = ( between(Inline, doubleasterisks, doubleasterisks)
-                 + between(Inline, doubleunderscores, doubleunderscores)
-                 ) / writer.strong
+  larsers.AutoLinkUrl    = parsers.less
+                         * C(parsers.alphanumeric^1 * P("://") * parsers.urlchar^1)
+                         * parsers.more
+                         / function(url)
+                             return writer.link(writer.string(url), url)
+                           end
 
-  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
+  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
 
-  local AutoLinkUrl   = less
-                      * C(alphanumeric^1 * P("://") * urlchar^1)
-                      * more
-                      / function(url)
-                        return writer.link(writer.string(url), url)
-                      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
 
-  local AutoLinkEmail = less
-                      * C((alphanumeric + S("-._+"))^1 * P("@") * urlchar^1)
-                      * more
-                      / function(email)
-                        return writer.link(writer.string(email),
-                                           "mailto:"..email)
-                      end
+  larsers.IndirectLink  = parsers.tag * (C(parsers.spnl) * parsers.tag)^-1
+                        / indirect_link
 
-  local DirectLink    = (tag / parse_inlines_no_link)  -- no links inside links
-                      * spnl
-                      * lparent
-                      * (url + Cc(""))  -- link can be empty [foo]()
-                      * optionaltitle
-                      * rparent
-                      / writer.link
+  -- parse a link or image (direct or indirect)
+  larsers.Link          = larsers.DirectLink + larsers.IndirectLink
 
-  local IndirectLink = tag * (C(spnl) * tag)^-1 / indirect_link
+  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
 
-  -- 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.IndirectImage = parsers.exclamation * parsers.tag
+                        * (C(parsers.spnl) * parsers.tag)^-1 / indirect_image
 
-  local IndirectImage  = exclamation * tag * (C(spnl) * tag)^-1 /
-                         indirect_image
+  larsers.Image         = larsers.DirectImage + larsers.IndirectImage
 
-  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.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 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
+  larsers.Citations     = larsers.TextCitations + larsers.ParenthesizedCitations
 
-      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
+  -- avoid parsing long strings of * or _ as emph/strong
+  larsers.UlOrStarLine  = parsers.asterisk^4 + parsers.underscore^4
+                        / writer.string
 
-  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
-%    \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
+  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{Horizontal Rule \acro{peg} Rules}
+% \paragraph{Lists (local)}
 %  \begin{macrocode}
-  local HorizontalRule = ( lineof(asterisk)
-                         + lineof(dash)
-                         + lineof(underscore)
-                         ) / writer.hrule
-%    \end{macrocode}
-% \paragraph{List \acro{peg} Rules}
-%  \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 @@
     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
+  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 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)
-
   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
+  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 DefinitionListItemTight = C(line)
-                           * Ct((defstart * dlchunk /
-                                            parse_blocks)^1)
-                           * Cc(true)
-                           / definition_list_item
+  larsers.DefinitionListItemTight = C(parsers.line)
+                                  * Ct((parsers.defstart * parsers.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
+  larsers.DefinitionList = ( Ct(larsers.DefinitionListItemLoose^1) * Cc(false)
+                           + Ct(larsers.DefinitionListItemTight^1)
+                           * (parsers.skipblanklines
+                             * -larsers.DefinitionListItemLoose * Cc(true))
+                           ) / writer.definitionlist
 %    \end{macrocode}
-% \paragraph{Blank Line \acro{peg} Rules}
+% \paragraph{Blank (local)}
 %  \begin{macrocode}
-  local Reference      = define_reference_parser / register_link
-  local Blank          = blankline / ""
-                       + NoteBlock
-                       + Reference
-                       + (tightblocksep / "\n")
+  larsers.Blank        = parsers.blankline / ""
+                       + larsers.NoteBlock
+                       + larsers.Reference
+                       + (parsers.tightblocksep / "\n")
 %    \end{macrocode}
-% \paragraph{Paragraph \acro{peg} Rules}
+% \paragraph{Headings (local)}
 %  \begin{macrocode}
-  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
-%    \end{macrocode}
-% \paragraph{Heading \acro{peg} Rules}
-%  \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 @@
                             + 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 @@
                             + 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 @@
 \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 @@
 \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 @@
 \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 @@
 }%
 %    \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 @@
 |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}%
+  \markdownInfo{Using mode 1: Shell escape via os.execute}%
+\fi
 %    \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},
+%
+% 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}
-\def\markdownLuaExecute#1{\directlua{local print = tex.print #1}}%
+\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}
 %
-% 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.
+% 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}
-\begingroup
+\ifnum\markdownMode=0\relax
+  \def\markdownExecuteDirect#1{\immediate\write18{#1}}%
+\else
+  \def\markdownExecuteDirect#1{%
+    \directlua{os.execute("\luaescapestring{#1}")}}%
+\fi
 %    \end{macrocode}
 %
-% To this end, we will make the underscore symbol, the dollar sign, and
-% circumflex symbols ordinary characters,
+% 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}
-    \catcode`\_=12%
-    \catcode`\$=12%
-    \catcode`\^=12%
+\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}
-% swap the category code of the hash symbol with the slash symbol (\t`/`).
+%
+% 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}
-    \catcode`\/=6%
-    \catcode`\#=12%
+\def\markdownLuaExecute#1{%
 %    \end{macrocode}
-% swap the category code of the percent sign with the at symbol (\t`@`).
+% 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}
-    \catcode`\@=14%
-    \catcode`\%=12%
+  \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}
-% swap the category code of the backslash symbol with the pipe symbol (\t`|`),
+% Execute the generated \m{markdownOptionHelperScriptFileName} Lua script using
+% the \TeX{}Lua binary and store the output in the
+% \m{markdownOptionOutputTempFileName} file.
 %  \begin{macrocode}
-    \catcode`|=0@
-    \catcode`\\=12@
+  \markdownInfo{Executing a helper Lua script from the file
+    "\markdownOptionHelperScriptFileName" and storing the result in the
+    file "\markdownOptionOutputTempFileName"}%
+  \markdownExecute{texlua "\markdownOptionHelperScriptFileName" >
+    "\markdownOptionOutputTempFileName"}%
 %    \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.
+% \m{input} the generated \m{markdownOptionOutputTempFileName} file.
 %  \begin{macrocode}
-    |gdef|markdownReadAndConvert/1/2{@
+  \input\markdownOptionOutputTempFileName\relax}%
 %    \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.
+%
+% \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}
-      |def|markdownReadAndConvertAfter{/2}@
-      |markdownInfo{Buffering markdown input and scanning for the
-        closing token sequence "/1"}@
-      |directlua{@
+\else
+\markdownInfo{Using mode 2: Direct Lua access}%
 %    \end{macrocode}
-% Set up an empty Lua table that will serve as our buffer.
+% 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}
-        |markdownPrepare
-        local buffer = {}
-%    \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.
-%  \begin{macrocode}
-        local ending_sequence = "^.-" .. ([[/1]]):gsub(
-          "([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1")
-%    \end{macrocode}
-% Register a callback that will notify you about new lines of input.
-%  \begin{macrocode}
-        |markdownLuaRegisterIBCallback{function(line)
-%    \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{}.
-%  \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]]
-%    \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{}.
-%  \begin{macrocode}
-          else
-            buffer[#buffer+1] = line
-            return [[\]] .. (#buffer == 1 and "fi" or "relax")
-          end
-        end}}@
-%    \end{macrocode}
-% Insert \m{iffalse} after the \m{markdownReadAndConvert} macro in order to
-% consume the rest of the first line of input.
-%  \begin{macrocode}
-      |iffalse}@
-%    \end{macrocode}
-% Reset the character categories back to the former state.
-%  \begin{macrocode}
-  |endgroup
+\def\markdownLuaExecute#1{\directlua{local print = tex.print #1}}%
 \fi
 %    \end{macrocode}
 %
@@ -4128,12 +4589,6 @@
   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 @@
     \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 @@
 %    \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 @@
 %  \begin{macrocode}
 \def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&%
   \do\#\do\^\do\_\do\%\do\~}%
+\input markdown
 %    \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`.
+% 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}
-    if new_callback == false then
-      tex.print(unescape([[|def|markdownMode{1}|input markdown]]))
+\def\markdownMakeOther{%
+  \count0=128\relax
+  \loop
+    \catcode\count0=11\relax
+    \advance\count0 by 1\relax
+  \ifnum\count0<256\repeat
 %    \end{macrocode}
-%
-% If we can set the \t`process_input_buffer` Lua\TeX{} callback, then just load
-% the plain \TeX{} implementation.
-%  \begin{macrocode}
-    else
-      callback.register("process_input_buffer", old_callback)
-      tex.print(unescape("|input markdown"))
-    end}%
-\fi
-%    \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 @@
 \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 @@
   \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.

Modified: trunk/Master/texmf-dist/tex/context/third/markdown/t-markdown.tex
===================================================================
--- trunk/Master/texmf-dist/tex/context/third/markdown/t-markdown.tex	2017-01-05 21:35:36 UTC (rev 42867)
+++ trunk/Master/texmf-dist/tex/context/third/markdown/t-markdown.tex	2017-01-05 22:56:40 UTC (rev 42868)
@@ -51,26 +51,14 @@
 \let\stopmarkdown\relax
 \def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&%
   \do\#\do\^\do\_\do\%\do\~}%
-\ifx\directlua\undefined
-  \input markdown
-\else
-  \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")
-    if new_callback == false then
-      tex.print(unescape([[|def|markdownMode{1}|input markdown]]))
-    else
-      callback.register("process_input_buffer", old_callback)
-      tex.print(unescape("|input markdown"))
-    end}%
-\fi
-\ifnum\markdownMode<2\relax
-  \def\markdownMakeOther{%
-    \catcode`|=12}%
-\fi
+\input markdown
+\def\markdownMakeOther{%
+  \count0=128\relax
+  \loop
+    \catcode\count0=11\relax
+    \advance\count0 by 1\relax
+  \ifnum\count0<256\repeat
+  \catcode`|=12}%
 \def\markdownInfo#1{\writestatus{markdown}{#1.}}%
 \def\markdownWarning#1{\writestatus{markdown\space warn}{#1.}}%
 \begingroup
@@ -132,7 +120,7 @@
 \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}}%

Modified: trunk/Master/texmf-dist/tex/generic/markdown/markdown.tex
===================================================================
--- trunk/Master/texmf-dist/tex/generic/markdown/markdown.tex	2017-01-05 21:35:36 UTC (rev 42867)
+++ trunk/Master/texmf-dist/tex/generic/markdown/markdown.tex	2017-01-05 22:56:40 UTC (rev 42868)
@@ -45,8 +45,8 @@
 %% 
 %% The names of the source files used are shown above.
 %% 
-\def\markdownLastModified{2016/12/09}%
-\def\markdownVersion{2.2.2}%
+\def\markdownLastModified{2017/01/05}%
+\def\markdownVersion{2.3.0}%
 \let\markdownBegin\relax
 \let\markdownEnd\relax
 \let\markdownInput\relax
@@ -57,6 +57,7 @@
 \let\markdownOptionBlankBeforeBlockquote\undefined
 \let\markdownOptionBlankBeforeCodeFence\undefined
 \let\markdownOptionBlankBeforeHeading\undefined
+\let\markdownOptionBreakableBlockquotes\undefined
 \let\markdownOptionCitations\undefined
 \let\markdownOptionCitationNbsps\undefined
 \let\markdownOptionDefinitionLists\undefined
@@ -63,6 +64,7 @@
 \let\markdownOptionFootnotes\undefined
 \let\markdownOptionFencedCode\undefined
 \let\markdownOptionHashEnumerators\undefined
+\let\markdownOptionHtml\undefined
 \let\markdownOptionHybrid\undefined
 \let\markdownOptionInlineFootnotes\undefined
 \let\markdownOptionPreserveTabs\undefined
@@ -237,11 +239,6 @@
 \def\markdownInfo#1{}%
 \def\markdownWarning#1{}%
 \def\markdownError#1{}%
-\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)}%
 \let\markdownMakeOther\relax
 \let\markdownReadAndConvert\relax
 \begingroup
@@ -257,6 +254,8 @@
     \def\markdownMode{2}%
   \fi
 \fi
+\def\markdownLuaRegisterIBCallback#1{\relax}%
+\def\markdownLuaUnregisterIBCallback#1{\relax}%
 \def\markdownInfo#1{%
   \message{(l.\the\inputlineno) markdown.tex info: #1.}}%
 \def\markdownWarning#1{%
@@ -304,7 +303,7 @@
 \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{%
@@ -331,6 +330,9 @@
 \ifx\markdownOptionBlankBeforeHeading\undefined\else
   blankBeforeHeading = \markdownOptionBlankBeforeHeading,
 \fi
+\ifx\markdownOptionBreakableBlockquotes\undefined\else
+  breakableBlockquotes = \markdownOptionBreakableBlockquotes,
+\fi
 \ifx\markdownOptionCacheDir\undefined\else
   cacheDir = "\markdownOptionCacheDir",
 \fi
@@ -352,6 +354,9 @@
 \ifx\markdownOptionHashEnumerators\undefined\else
   hashEnumerators = \markdownOptionHashEnumerators,
 \fi
+\ifx\markdownOptionHtml\undefined\else
+  html = \markdownOptionHtml,
+\fi
 \ifx\markdownOptionHybrid\undefined\else
   hybrid = \markdownOptionHybrid,
 \fi
@@ -380,7 +385,47 @@
 local md = require("markdown")
 local convert = md.new(\markdownLuaOptions)
 }%
-
+\csname newwrite\endcsname\markdownLuaExecuteFileStream
+\begingroup
+  \catcode`\^^I=12%
+  \gdef\markdownReadAndConvertTab{^^I}%
+\endgroup
+\begingroup
+  \catcode`\^^M=13%
+  \catcode`\^^I=13%
+  \catcode`|=0%
+  \catcode`\\=12%
+  |gdef|markdownReadAndConvert#1#2{%
+    |begingroup%
+    |immediate|openout|markdownLuaExecuteFileStream%
+      |markdownOptionInputTempFileName%
+    |markdownInfo{Buffering markdown input into the temporary %
+      input file "|markdownOptionInputTempFileName" and scanning %
+      for the closing token sequence "#1"}%
+    |def|do##1{|catcode`##1=12}|dospecials%
+    |catcode`| =12%
+    |markdownMakeOther%
+    |def|markdownReadAndConvertProcessLine##1#1##2#1##3|relax{%
+      |ifx|relax##3|relax%
+        |immediate|write|markdownLuaExecuteFileStream{##1}%
+      |else%
+        |def^^M{%
+          |markdownInfo{The ending token sequence was found}%
+          |immediate|closeout|markdownLuaExecuteFileStream%
+          |endgroup%
+          |markdownInput|markdownOptionInputTempFileName%
+          #2}%
+      |fi%
+      ^^M}%
+    |catcode`|^^I=13%
+    |def^^I{|markdownReadAndConvertTab}%
+    |catcode`|^^M=13%
+    |def^^M##1^^M{%
+      |def^^M####1^^M{%
+        |markdownReadAndConvertProcessLine####1#1#1|relax}%
+      ^^M}%
+    ^^M}%
+|endgroup
 \ifnum\markdownMode<2\relax
 \ifnum\markdownMode=0\relax
   \markdownInfo{Using mode 0: Shell escape via write18}%
@@ -387,7 +432,6 @@
 \else
   \markdownInfo{Using mode 1: Shell escape via os.execute}%
 \fi
-\csname newwrite\endcsname\markdownLuaExecuteFileStream
 \ifx\pdfshellescape\undefined
   \ifx\shellescape\undefined
     \ifnum\markdownMode=0\relax
@@ -431,82 +475,9 @@
   \markdownExecute{texlua "\markdownOptionHelperScriptFileName" >
     "\markdownOptionOutputTempFileName"}%
   \input\markdownOptionOutputTempFileName\relax}%
-\begingroup
-  \catcode`\^^I=12%
-  \gdef\markdownReadAndConvertTab{^^I}%
-\endgroup
-\begingroup
-  \catcode`\^^M=13%
-  \catcode`\^^I=13%
-  \catcode`|=0%
-  \catcode`\\=12%
-  |gdef|markdownReadAndConvert#1#2{%
-    |begingroup%
-    |immediate|openout|markdownLuaExecuteFileStream%
-      |markdownOptionInputTempFileName%
-    |markdownInfo{Buffering markdown input into the temporary %
-      input file "|markdownOptionInputTempFileName" and scanning %
-      for the closing token sequence "#1"}%
-    |def|do##1{|catcode`##1=12}|dospecials%
-    |catcode`| =12%
-    |markdownMakeOther%
-    |def|markdownReadAndConvertProcessLine##1#1##2#1##3|relax{%
-      |ifx|relax##3|relax%
-        |immediate|write|markdownLuaExecuteFileStream{##1}%
-      |else%
-        |def^^M{%
-          |markdownInfo{The ending token sequence was found}%
-          |immediate|closeout|markdownLuaExecuteFileStream%
-          |endgroup%
-          |markdownInput|markdownOptionInputTempFileName%
-          #2}%
-      |fi%
-      ^^M}%
-    |catcode`|^^I=13%
-    |def^^I{|markdownReadAndConvertTab}%
-    |catcode`|^^M=13%
-    |def^^M##1^^M{%
-      |def^^M####1^^M{%
-        |markdownReadAndConvertProcessLine####1#1#1|relax}%
-      ^^M}%
-    ^^M}%
-|endgroup
 \else
 \markdownInfo{Using mode 2: Direct Lua access}%
 \def\markdownLuaExecute#1{\directlua{local print = tex.print #1}}%
-\begingroup
-    \catcode`\_=12%
-    \catcode`\$=12%
-    \catcode`\^=12%
-    \catcode`\/=6%
-    \catcode`\#=12%
-    \catcode`\@=14%
-    \catcode`\%=12%
-    \catcode`|=0@
-    \catcode`\\=12@
-    |gdef|markdownReadAndConvert/1/2{@
-      |def|markdownReadAndConvertAfter{/2}@
-      |markdownInfo{Buffering markdown input and scanning for the
-        closing token sequence "/1"}@
-      |directlua{@
-        |markdownPrepare
-        local buffer = {}
-        local ending_sequence = "^.-" .. ([[/1]]):gsub(
-          "([%(%)%.%%%+%-%*%?%[%]%^%$])", "%%%1")
-        |markdownLuaRegisterIBCallback{function(line)
-          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]]
-          else
-            buffer[#buffer+1] = line
-            return [[\]] .. (#buffer == 1 and "fi" or "relax")
-          end
-        end}}@
-      |iffalse}@
-  |endgroup
 \fi
 \begingroup
   \catcode`|=0%

Modified: trunk/Master/texmf-dist/tex/latex/markdown/markdown.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/markdown/markdown.sty	2017-01-05 21:35:36 UTC (rev 42867)
+++ trunk/Master/texmf-dist/tex/latex/markdown/markdown.sty	2017-01-05 22:56:40 UTC (rev 42868)
@@ -63,6 +63,8 @@
   \def\markdownOptionBlankBeforeCodeFence{#1}}%
 \define at key{markdownOptions}{blankBeforeHeading}[true]{%
   \def\markdownOptionBlankBeforeHeading{#1}}%
+\define at key{markdownOptions}{breakableBlockquotes}[true]{%
+  \def\markdownOptionBreakableBlockquotes{#1}}%
 \define at key{markdownOptions}{citations}[true]{%
   \def\markdownOptionCitations{#1}}%
 \define at key{markdownOptions}{citationNbsps}[true]{%
@@ -77,6 +79,8 @@
   \def\markdownOptionFencedCode{#1}}%
 \define at key{markdownOptions}{hashEnumerators}[true]{%
   \def\markdownOptionHashEnumerators{#1}}%
+\define at key{markdownOptions}{html}[true]{%
+  \def\markdownOptionHtml{#1}}%
 \define at key{markdownOptions}{hybrid}[true]{%
   \def\markdownOptionHybrid{#1}}%
 \define at key{markdownOptions}{inlineFootnotes}[true]{%
@@ -403,12 +407,6 @@
   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}},
@@ -428,7 +426,19 @@
     \fi},
   horizontalRule = {\noindent\rule[0.5ex]{\linewidth}{1pt}},
   footnote = {\footnote{#1}}}}
-
+\newif\ifmarkdownLATEXStrongEmphasisNested
+\markdownLATEXStrongEmphasisNestedfalse
+\markdownSetup{rendererPrototypes={
+  strongEmphasis = {%
+    \ifmarkdownLATEXStrongEmphasisNested
+      \markdownLATEXStrongEmphasisNestedfalse
+      \textmd{#1}%
+      \markdownLATEXStrongEmphasisNestedtrue
+    \else
+      \markdownLATEXStrongEmphasisNestedtrue
+      \textbf{#1}%
+      \markdownLATEXStrongEmphasisNestedfalse
+    \fi}}}
 \ifx\chapter\undefined
   \markdownSetup{rendererPrototypes = {
     headingOne = {\section{#1}},
@@ -491,24 +501,6 @@
       \markdownLaTeXBibLaTeXTextCitations
       \expandafter{\expandafter}%
     \fi}}}
-\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}
 \newcommand\markdownMakeOther{%
   \count0=128\relax
   \loop

Modified: trunk/Master/texmf-dist/tex/luatex/markdown/markdown.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/markdown/markdown.lua	2017-01-05 21:35:36 UTC (rev 42867)
+++ trunk/Master/texmf-dist/tex/luatex/markdown/markdown.lua	2017-01-05 22:56:40 UTC (rev 42868)
@@ -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 @@
 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 @@
     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 @@
         "\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,92 +678,433 @@
 
   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 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")
 
-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(":;,.#$%&-+?<>~/")
+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(":;,.#$%&-+?<>~/")
 
-local doubleasterisks        = P("**")
-local doubleunderscores      = P("__")
-local fourspaces             = P("    ")
+parsers.doubleasterisks        = P("**")
+parsers.doubleunderscores      = P("__")
+parsers.fourspaces             = P("    ")
 
-local any                    = P(1)
-local fail                   = any - 1
-local always                 = P("")
+parsers.any                    = P(1)
+parsers.fail                   = parsers.any - 1
 
-local escapable              = S("\\`*_{}[]()+_.!<>#-~:^@;")
+parsers.escapable              = S("\\`*_{}[]()+_.!<>#-~:^@;")
+parsers.anyescaped             = P("\\") / "" * parsers.escapable
+                               + parsers.any
 
-local anyescaped             = P("\\") / "" * escapable
-                             + any
+parsers.spacechar              = S("\t ")
+parsers.spacing                = S(" \n\r\t")
+parsers.nonspacechar           = parsers.any - parsers.spacing
+parsers.optionalspace          = parsers.spacechar^0
 
-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")
+parsers.specialchar            = S("*_`&[]<!\\. at -^")
 
-local specialchar            = S("*_`&[]<!\\. at -^")
+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)
 
-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)
+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
 
-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
+parsers.chunk                  = parsers.line * (parsers.optionallyindentedline
+                                                - parsers.blankline)^0
 
-local chunk = line * (optionallyindentedline - 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)
   local self = {}
@@ -518,14 +1127,7 @@
                    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 @@
     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 parse_blocks
+    = create_parser("parse_blocks",
+                    function()
+                      return larsers.blocks
+                    end)
 
-  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_toplevel
+    = create_parser("parse_blocks_toplevel",
+                    function()
+                      return larsers.blocks_toplevel
+                    end)
 
-  if options.hashEnumerators then
-    dig = digit + hash
-  else
-    dig = digit
-  end
+  local parse_inlines
+    = create_parser("parse_inlines",
+                    function()
+                      return larsers.inlines
+                    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 parse_inlines_no_link
+    = create_parser("parse_inlines_no_link",
+                    function()
+                      return larsers.inlines_no_link
+                    end)
 
-  local function captures_equal_length(s,i,a,b)
-    return #a == #b and i
-  end
+  local parse_inlines_no_inline_note
+    = create_parser("parse_inlines_no_inline_note",
+                    function()
+                      return larsers.inlines_no_inline_note
+                    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
+  local parse_inlines_nbsp
+    = create_parser("parse_inlines_nbsp",
+                    function()
+                      return larsers.inlines_nbsp
+                    end)
+  if options.hashEnumerators then
+    larsers.dig = parsers.digit + parsers.hash
+  else
+    larsers.dig = parsers.digit
   end
 
-  local infostring     = (linechar - (backtick + space^1 * (newline + eof)))^0
+  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 fenceindent
-  local function fencehead(char)
-    return               C(nonindentspace) / function(s) fenceindent = #s end
-                       * Cg(char^3, "fencelength")
-                       * optionalspace * C(infostring) * optionalspace
-                       * (newline + 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 fencetail(char)
-    return               nonindentspace
-                       * Cmt(C(char^3) * Cb("fencelength"),
-                             captures_geq_length)
-                       * optionalspace * (newline + eof)
-                       + eof
+      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 function fencedline(char)
-    return               C(line - fencetail(char))
-                       / function(s)
-                             return s:gsub("^" .. string.rep(" ?",
-                                 fenceindent), "")
-                         end
-  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 @@
     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 @@
       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 @@
       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
+    larsers.fencestart = parsers.fail
+  else
+    larsers.fencestart = parsers.fencehead(parsers.backtick)
+                       + parsers.fencehead(parsers.tilde)
   end
-  local Inline    = V("Inline")
 
-  local Str       = normalchar^1 / writer.string
+  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
 
-  local Symbol    = (specialchar - tightblocksep) / writer.string
-  local Ellipsis  = P("...") / writer.ellipsis
+  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
 
-  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
+  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
 
-  local Space     = spacechar^2 * Endline / writer.linebreak
-                  + spacechar^1 * Endline^-1 * eof / ""
-                  + spacechar^1 * Endline^-1 * optionalspace / writer.space
+  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
 
-  local NonbreakingEndline
-                  = newline * -( -- newline, but not before...
-                        blankline -- paragraph break
-                      + tightblocksep  -- nested list
-                      + eof       -- end of document
-                      + bqstart
-                      + headerstart
-                      + fencestart
-                    ) * spacechar^0 / writer.nbsp
+  larsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks,
+                                     parsers.doubleasterisks)
+                   + parsers.between(parsers.Inline, parsers.doubleunderscores,
+                                     parsers.doubleunderscores)
+                   ) / writer.strong
 
-  local NonbreakingSpace
-                  = spacechar^2 * Endline / writer.linebreak
-                  + spacechar^1 * Endline^-1 * eof / ""
-                  + spacechar^1 * Endline^-1 * optionalspace / writer.nbsp
+  larsers.Emph   = ( parsers.between(parsers.Inline, parsers.asterisk,
+                                     parsers.asterisk)
+                   + parsers.between(parsers.Inline, parsers.underscore,
+                                     parsers.underscore)
+                   ) / writer.emphasis
 
-  -- 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
+  larsers.AutoLinkUrl    = parsers.less
+                         * C(parsers.alphanumeric^1 * P("://") * parsers.urlchar^1)
+                         * parsers.more
+                         / function(url)
+                             return writer.link(writer.string(url), url)
+                           end
 
-  local Emph   = ( between(Inline, asterisk, asterisk)
-                 + between(Inline, underscore, underscore)
-                 ) / writer.emphasis
-  local urlchar = anyescaped - newline - more
+  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
 
-  local AutoLinkUrl   = less
-                      * C(alphanumeric^1 * P("://") * urlchar^1)
-                      * more
-                      / function(url)
-                        return writer.link(writer.string(url), url)
-                      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
 
-  local AutoLinkEmail = less
-                      * C((alphanumeric + S("-._+"))^1 * P("@") * urlchar^1)
-                      * more
-                      / function(email)
-                        return writer.link(writer.string(email),
-                                           "mailto:"..email)
-                      end
+  larsers.IndirectLink  = parsers.tag * (C(parsers.spnl) * parsers.tag)^-1
+                        / indirect_link
 
-  local DirectLink    = (tag / parse_inlines_no_link)  -- no links inside links
-                      * spnl
-                      * lparent
-                      * (url + Cc(""))  -- link can be empty [foo]()
-                      * optionaltitle
-                      * rparent
-                      / writer.link
+  -- parse a link or image (direct or indirect)
+  larsers.Link          = larsers.DirectLink + larsers.IndirectLink
 
-  local IndirectLink = tag * (C(spnl) * tag)^-1 / indirect_link
+  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
 
-  -- 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
+  larsers.IndirectImage = parsers.exclamation * parsers.tag
+                        * (C(parsers.spnl) * parsers.tag)^-1 / indirect_image
 
-  local IndirectImage  = exclamation * tag * (C(spnl) * tag)^-1 /
-                         indirect_image
+  larsers.Image         = larsers.DirectImage + larsers.IndirectImage
 
-  local Image         = DirectImage + IndirectImage
-  -- avoid parsing long strings of * or _ as emph/strong
-  local UlOrStarLine  = asterisk^4 + underscore^4 / writer.string
+  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
 
-  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
+  larsers.ParenthesizedCitations
+                        = Ct(parsers.lbracket
+                        * parsers.citation_body
+                        * parsers.rbracket)
+                        / function(raw_cites)
+                            return larsers.citations(false, raw_cites)
+                          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
+  larsers.Citations     = larsers.TextCitations + larsers.ParenthesizedCitations
 
-  local TextCitations = Ct(Cc("")
-                      * citation_name
-                      * ((spnl
-                           * lbracket
-                           * citation_headless_body
-                           * rbracket) + Cc(""))) /
-                        function(raw_cites)
-                            return citations(true, raw_cites)
-                        end
+  -- avoid parsing long strings of * or _ as emph/strong
+  larsers.UlOrStarLine  = parsers.asterisk^4 + parsers.underscore^4
+                        / writer.string
 
-  local ParenthesizedCitations
-                      = Ct(lbracket
-                      * citation_body
-                      * rbracket) /
-                        function(raw_cites)
-                            return citations(false, raw_cites)
-                        end
+  larsers.EscapedChar   = S("\\") * C(parsers.escapable) / writer.string
 
-  local Citations     = TextCitations + ParenthesizedCitations
-  local Block          = V("Block")
+  larsers.InlineHtml    = C(parsers.inlinehtml) / writer.inline_html
 
-  local Verbatim       = Cs( (blanklines
-                           * ((indentedline - blankline))^1)^1
+  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
+
+  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
-  -- 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 @@
     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
+  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 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)
-
   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
+  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 DefinitionListItemTight = C(line)
-                           * Ct((defstart * dlchunk /
-                                            parse_blocks)^1)
-                           * Cc(true)
-                           / definition_list_item
+  larsers.DefinitionListItemTight = C(parsers.line)
+                                  * Ct((parsers.defstart * parsers.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.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
+  larsers.SetextHeading = #(parsers.line * S("=-"))
+                        * Ct(parsers.line / parse_inlines)
+                        * parsers.HeadingLevel
+                        * parsers.optionalspace * parsers.newline
+                        / writer.heading
 
-  local Heading = AtxHeading + SetextHeading
-  syntax =
+  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 @@
                             + 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 @@
                             + 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 = {}



More information about the tex-live-commits mailing list