texlive[72146] Master: markdown (30aug24)

commits+karl at tug.org commits+karl at tug.org
Fri Aug 30 23:14:17 CEST 2024


Revision: 72146
          https://tug.org/svn/texlive?view=revision&revision=72146
Author:   karl
Date:     2024-08-30 23:14:17 +0200 (Fri, 30 Aug 2024)
Log Message:
-----------
markdown (30aug24)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/generic/markdown/CHANGES.md
    trunk/Master/texmf-dist/doc/generic/markdown/README.md
    trunk/Master/texmf-dist/doc/generic/markdown/VERSION
    trunk/Master/texmf-dist/doc/generic/markdown/markdown.html
    trunk/Master/texmf-dist/doc/generic/markdown/markdown.pdf
    trunk/Master/texmf-dist/doc/optex/markdown/examples/optex.tex
    trunk/Master/texmf-dist/scripts/markdown/markdown-cli.lua
    trunk/Master/texmf-dist/source/generic/markdown/docstrip.cfg
    trunk/Master/texmf-dist/source/generic/markdown/markdown.dtx
    trunk/Master/texmf-dist/source/generic/markdown/markdown.ins
    trunk/Master/texmf-dist/tex/context/third/markdown/t-markdownthemewitiko_markdown_defaults.tex
    trunk/Master/texmf-dist/tex/generic/markdown/markdown.tex
    trunk/Master/texmf-dist/tex/generic/markdown/markdownthemewitiko_markdown_defaults.tex
    trunk/Master/texmf-dist/tex/latex/markdown/markdown.sty
    trunk/Master/texmf-dist/tex/latex/markdown/markdownthemewitiko_dot.sty
    trunk/Master/texmf-dist/tex/latex/markdown/markdownthemewitiko_markdown_defaults.sty
    trunk/Master/texmf-dist/tex/luatex/markdown/markdown.lua
    trunk/Master/tlpkg/tlpsrc/markdown.tlpsrc

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/generic/markdown/DEPENDS.txt
    trunk/Master/texmf-dist/tex/luatex/markdown/markdown-parser.lua

Modified: trunk/Master/texmf-dist/doc/generic/markdown/CHANGES.md
===================================================================
--- trunk/Master/texmf-dist/doc/generic/markdown/CHANGES.md	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/doc/generic/markdown/CHANGES.md	2024-08-30 21:14:17 UTC (rev 72146)
@@ -1,5 +1,58 @@
 # Changes
 
+## 3.7.0 (2024-08-30)
+
+Development:
+
+- Add renderer `jekyllDataProgrammaticString` for processing YAML
+  string scalars that do not contain markdown markup and are not
+  intended for typesetting.
+  (istqborg/istqb_product_base#46, #440, #451, sponsored by @istqborg)
+- Add option `ensureJekyllData` for processing standalone YAML files.
+- Add renderers `warning` and `error` that represent warnings and errors
+  produced by the markdown parser. (#452, #473)
+- Support user-defined LaTeX environments with LaTeX markup.
+  (#20, #477, suggested by @cdupont)
+
+Fixes:
+
+- Produce more meaningful error message when KPathSea fails to
+  locate a file. (#458, #472, 33e6eef2, reported by @Yggdrasil128)
+- Fix soft line breaks in citation prenotes and postnotes.
+  (#483, #484, contributed by @lostenderman)
+
+Speed improvements:
+
+- Precompile snippets to improve the speed of setting them.
+  (#467, #479, inspired by the TUG 2024 talk by @josephwright)
+- Use an optimized parser to determine Unicode punctuation.
+  (#458, #474, #482, 4c1a7de2, co-authored by @Yggdrasil128,
+   sponsored by @istqborg)
+  This improves the speed of parsing markdown input by up to 500%.
+- Enable option `eagerCache` by default. (#468, #490, sponsored by @istqborg)
+  This improves the speed by up to 25% for large documents with many
+  markdown fragments that require multiple compilation runs.
+- Prevent loading the full Markdown package when converting cached markdown
+  fragments. (#487, #491, 40f0803f, 6609d43d, sponsored by @istqborg)
+  This significantly improves the speed for large documents with many
+  markdown fragments that require multiple compilation runs in pdfTeX.
+
+Deprecation:
+
+- Deprecate `jekyllDataString` renderer and renderer prototype.
+  Users should use `jekyllDataTypographicString` instead.
+  (istqborg/istqb_product_base#46, #440, #451, sponsored by @istqborg)
+
+Continuous Integration:
+
+- Ensure that all code lines are shorter than 72 characters.
+  (#478, e7160d60, 3ac95efe)
+
+Experiments:
+
+- Measure the speed of the Markdown package across recent versions.
+  (249552e5, 55ede824, efeaecbe, #474, 7b2b2431, fcf0064c)
+
 ## 3.6.2 (2024-07-14)
 
 Fixes:

Added: trunk/Master/texmf-dist/doc/generic/markdown/DEPENDS.txt
===================================================================
--- trunk/Master/texmf-dist/doc/generic/markdown/DEPENDS.txt	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/generic/markdown/DEPENDS.txt	2024-08-30 21:14:17 UTC (rev 72146)
@@ -0,0 +1,21 @@
+hard l3kernel
+hard lt3luabridge
+hard lua-uni-algos
+soft amsfonts
+soft amsmath
+soft catchfile
+soft csvsimple
+soft epstopdf-pkg
+soft etoolbox
+soft fancyvrb
+soft gobble
+soft graphics
+soft grffile
+soft latex
+soft ltxcmds
+soft paralist
+soft pgf
+soft soul
+soft tools
+soft url
+soft verse


Property changes on: trunk/Master/texmf-dist/doc/generic/markdown/DEPENDS.txt
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/doc/generic/markdown/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/generic/markdown/README.md	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/doc/generic/markdown/README.md	2024-08-30 21:14:17 UTC (rev 72146)
@@ -194,7 +194,7 @@
     - [Attributes in Markdown][tb136],
     - [Markdown 3 at TUG 2023: Reflections from the Q&A session][tb138],
     - Fast Regression Testing of TeX Packages: The Unreasonable Effectiveness of Batching ([preprint][tb139]), and
-    - Markdown Themes in Practice ([preprint][tb140]).
+    - Markdown Themes in Practice ([preprint][tb140-preprint], [work in progress][tb140]).
 5. Journal articles published by [CSTUG Bulletin][csbul] (in Czech and Slovak):
     - [Rendering Markdown inside TeX Documents][10.5300/2016-1-4/78],
     - [Markdown 2.8.1: Boldly Unto the Throne of Lightweight Markup in TeX][10.5300/2020-1-2/48],
@@ -204,11 +204,12 @@
     - [Markdown 3: What's New, What's Next?][10.5300/2023-3-4/111].
 6. Talks:
     - [Five Years of Markdown in LaTeX: What, Why, How, and Whereto][pv212-fall2020] (in Czech),
-    - [Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX][tb131-video] ([slides][tb131-slides]),
+    - [Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX][tb131-video] ([mirror][tb131-video-mirror], [slides][tb131-slides]),
     - [A Self-Publisher's Take on Markdown and TeX][tb134-01-video] ([slides][tb134-01-slides]),
     - [A Gentle Introduction to Markdown for Writers][tb134-02-video] ([slides][tb134-02-slides], [example][tb134-02-example]),
-    - [Markdown 3: What's New, What's Next?][tb137-video] ([slides][tb137-slides]), and
-    - An Implementation of the CommonMark Standard and new Syntax Extensions to the Markdown Package for TeX ([slides][gencur-defense-slides]).
+    - [Markdown 3: What's New, What's Next?][tb137-video] ([mirror][tb137-video-mirror], [slides][tb137-slides]),
+    - An Implementation of the CommonMark Standard and new Syntax Extensions to the Markdown Package for TeX ([slides][gencur-defense-slides]), and
+    - [Markdown Themes in Practice][tb140-video] ([preprint][tb140-preprint], [slides][tb140-slides])
 7. Theses:
     - [Generic TeX Writer for the Pandoc Document Converter][thesis-umhg5]
     - [An implementation of the CommonMark standard into the Markdown package for TeX][thesis-r7z7l]
@@ -220,26 +221,31 @@
  [overleaf-5]: https://www.overleaf.com/latex/examples/writing-posters-with-markdown/jtbgmmgqrqmh       "Writing Posters with Markdown"
  [overleaf-6]: https://www.overleaf.com/latex/examples/using-markdown-in-latex-documents/whdrnpcpnwrm   "Using Markdown in LaTeX documents"
 
- [tb119]: https://www.tug.org/TUGboat/tb38-2/tb119novotny.pdf                                                      "Using Markdown inside TeX documents"
- [tb124]: https://www.tug.org/TUGboat/tb40-1/tb124novotny-markdown.pdf                                             "Markdown 2.7.0: Towards lightweight markup in TeX"
- [tb129]: https://www.tug.org/TUGboat/tb41-3/tb129novotny-frozen.pdf                                               "Making Markdown into a Microwave Meal"
- [tb131]: https://www.tug.org/TUGboat/tb42-2/tb131novotny-markdown.pdf                                             "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX"
- [tb133]: https://www.tug.org/TUGboat/tb43-1/tb133novotny-markdown.pdf                                             "Markdown 2.15.0: What's New?"
- [tb135]: https://www.tug.org/TUGboat/tb43-3/tb135novotny-markdown.pdf                                             "Markdown 2.17.1: What's New, What's Next?"
- [tb136]: https://www.tug.org/TUGboat/tb44-1/tb136novotny-markdown-attr.pdf                                        "Attributes in Markdown"
- [tb138]: https://www.tug.org/TUGboat/tb44-3/tb138starynovotny-markdown-qa.pdf                                     "Markdown 3 at TUG 2023: Reflections from the Q&A session"
- [tb139]: https://github.com/Witiko/fast-regression-testing/releases/download/latest/tb139starynovotny-testing.pdf "Fast Regression Testing of TeX Packages: The Unreasonable Effectiveness of Batching"
- [tb140]: https://www.tug.org/tug2024/preprints/starynovotny-markdown-themes.pdf                                   "Markdown Themes in Practice"
+ [tb119]: https://www.tug.org/TUGboat/tb38-2/tb119novotny.pdf                                                                  "Using Markdown inside TeX documents"
+ [tb124]: https://www.tug.org/TUGboat/tb40-1/tb124novotny-markdown.pdf                                                         "Markdown 2.7.0: Towards lightweight markup in TeX"
+ [tb129]: https://www.tug.org/TUGboat/tb41-3/tb129novotny-frozen.pdf                                                           "Making Markdown into a Microwave Meal"
+ [tb131]: https://www.tug.org/TUGboat/tb42-2/tb131novotny-markdown.pdf                                                         "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX"
+ [tb133]: https://www.tug.org/TUGboat/tb43-1/tb133novotny-markdown.pdf                                                         "Markdown 2.15.0: What's New?"
+ [tb135]: https://www.tug.org/TUGboat/tb43-3/tb135novotny-markdown.pdf                                                         "Markdown 2.17.1: What's New, What's Next?"
+ [tb136]: https://www.tug.org/TUGboat/tb44-1/tb136novotny-markdown-attr.pdf                                                    "Attributes in Markdown"
+ [tb138]: https://www.tug.org/TUGboat/tb44-3/tb138starynovotny-markdown-qa.pdf                                                 "Markdown 3 at TUG 2023: Reflections from the Q&A session"
+ [tb139]: https://github.com/Witiko/fast-regression-testing/releases/download/latest/tb139starynovotny-testing.pdf             "Fast Regression Testing of TeX Packages: The Unreasonable Effectiveness of Batching"
+ [tb140]: https://github.com/Witiko/markdown-themes-in-practice/releases/download/latest/tb140starynovotny-markdown-themes.pdf "Markdown Themes in Practice"
 
- [tb131-slides]:     https://tug.org/tug2021/assets/pdf/tug2021-novotny-slides.pdf                            "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX"
- [tb131-video]:      https://youtu.be/i2GJMnLCZls                                                             "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX"
- [tb134-01-slides]:  https://tug.org/tug2022/assets/served/Lloyd_Prentice-TUG2022-prentice-selfpub-slides.pdf "A Self-Publisher's Take on Markdown and TeX"
- [tb134-01-video]:   https://youtu.be/OhwzT3TcLj8                                                             "A Self-Publisher's Take on Markdown and TeX"
- [tb134-02-slides]:  https://tug.org/tug2022/assets/pdf/Tereza_Vrabcová-TUG2022-slides.pdf                    "A Gentle Introduction to Markdown for Writers"
- [tb134-02-example]: https://tug.org/tug2022/assets/pdf/Tereza_Vrabcová-TUG2022-example.pdf                   "A Gentle Introduction to Markdown for Writers"
- [tb134-02-video]:   https://youtu.be/FhN_x9rsR4M                                                             "A Gentle Introduction to Markdown for Writers"
- [tb137-slides]:     https://tug.org/tug2023/files/sa-03-novotny-markdown3/novotny-markdown3-slides.pdf       "Markdown 3: What's New, What's Next?"
- [tb137-video]:      https://youtu.be/U8XjTOhJkg0                                                             "Markdown 3: What's New, What's Next?"
+ [tb131-slides]:       https://tug.org/tug2021/assets/pdf/tug2021-novotny-slides.pdf                            "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX"
+ [tb131-video]:        https://youtu.be/i2GJMnLCZls                                                             "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX"
+ [tb131-video-mirror]: https://youtu.be/THmPkAncMnc                                                             "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX (mirror)"
+ [tb134-01-slides]:    https://tug.org/tug2022/assets/served/Lloyd_Prentice-TUG2022-prentice-selfpub-slides.pdf "A Self-Publisher's Take on Markdown and TeX"
+ [tb134-01-video]:     https://youtu.be/OhwzT3TcLj8                                                             "A Self-Publisher's Take on Markdown and TeX"
+ [tb134-02-slides]:    https://tug.org/tug2022/assets/pdf/Tereza_Vrabcová-TUG2022-slides.pdf                    "A Gentle Introduction to Markdown for Writers"
+ [tb134-02-example]:   https://tug.org/tug2022/assets/pdf/Tereza_Vrabcová-TUG2022-example.pdf                   "A Gentle Introduction to Markdown for Writers"
+ [tb134-02-video]:     https://youtu.be/FhN_x9rsR4M                                                             "A Gentle Introduction to Markdown for Writers"
+ [tb137-slides]:       https://tug.org/tug2023/files/sa-03-novotny-markdown3/novotny-markdown3-slides.pdf       "Markdown 3: What's New, What's Next?"
+ [tb137-video]:        https://youtu.be/U8XjTOhJkg0                                                             "Markdown 3: What's New, What's Next?"
+ [tb137-video-mirror]: https://youtu.be/W15bBpVTA-c                                                             "Markdown 3: What's New, What's Next? (mirror)"
+ [tb140-preprint]:     https://www.tug.org/tug2024/preprints/starynovotny-markdown-themes.pdf                   "Markdown Themes in Practice"
+ [tb140-video]:        https://youtu.be/d7vTW7PR0B4?t=5h3m10s                                                   "Markdown Themes in Practice"
+ [tb140-slides]:       https://www.tug.org/tug2024/slides/starynovotny-markdown-themes.pdf                      "Markdown Themes in Practice"
 
  [10.5300/2016-1-4/78]:  https://www.doi.org/10.5300/2016-1-4/78  "Rendering Markdown inside TeX Documents"
  [10.5300/2020-1-2/48]:  https://www.doi.org/10.5300/2020-1-2/48  "Markdown 2.8.1: Boldly Unto the Throne of Lightweight Markup in TeX"
@@ -276,7 +282,7 @@
 | [<img width="150" src="https://www.fi.muni.cz/images/fi-logo.png">][fimu] | I gratefully acknowledge the funding from the [Faculty of Informatics][fimu] at the [Masaryk University][mu] in Brno, Czech Republic, for the development of the Markdown package in projects [MUNI/33/12/2015][], [MUNI/33/1784/2020][], [MUNI/33/0776/2021][], [MUNI/33/1654/2022][], and [MUNI/33/1658/2022][]. |
 | [<img width="150" src="https://cdn.overleaf.com/img/ol-brand/overleaf_og_logo.png">][overleaf] | Extensive user documentation for the Markdown package was kindly written by [Lian Tze Lim][liantze] and published by [Overleaf][]. |
 | [<img width="150" src="https://pbs.twimg.com/profile_images/1004769879319334912/6Bh1UthD.jpg">][omedym] | Support for content slicing (Lua options [`shiftHeadings`][option-shift-headings] and [`slice`][option-slice]) and pipe tables (Lua options [`pipeTables`][option-pipe-tables] and [`tableCaptions`][option-table-captions]) was graciously sponsored by [David Vins][dvins] and [Omedym][]. |
-| [<img width="150" src="https://www.istqb.org/static/istqb-logo-1b043e800a580724ad223567f9ea57c0.png">][istqb] | Fixes for issues [#359][issue-359], [#368][issue-368], [#401][issue-401], and [#424][issue-424] were graciously sponsored by the [International Software Testing Qualifications Board (ISTQB)][istqb]. |
+| [<img width="150" src="https://www.istqb.org/static/istqb-logo-1b043e800a580724ad223567f9ea57c0.png">][istqb] | Fixes for issues [#359][issue-359], [#368][issue-368], [#401][issue-401], [#424][issue-424], [#440][issue-440], [#468][issue-468], [#474][issue-474], and [#487][issue-487] were graciously sponsored by the [International Software Testing Qualifications Board (ISTQB)][istqb]. |
 
  [dvins]:  https://github.com/dvins             "David Vins"
  [fimu]:   https://www.fi.muni.cz/index.html.en "Faculty of Informatics, Masaryk University"
@@ -288,6 +294,10 @@
  [issue-368]: https://github.com/witiko/markdown/issues/368 "Tables nested in list items have empty lines"
  [issue-401]: https://github.com/witiko/markdown/issues/401 "Create an unnumbered section"
  [issue-424]: https://github.com/witiko/markdown/issues/424 "E-mail addresses are incorrectly interpreted as bracketed citations"
+ [issue-440]: https://github.com/witiko/markdown/issues/440 "Support programmatic text in YAML metadata values"
+ [issue-468]: https://github.com/witiko/markdown/issues/468 "Enable option eagerCache by default"
+ [issue-474]: https://github.com/witiko/markdown/issues/474 "Improve the speed of the Markdown package"
+ [issue-487]: https://github.com/witiko/markdown/issues/487 "Prevent loading the full Markdown package when converting cached markdown fragments"
 
  [option-pipe-tables]:    https://mirrors.ctan.org/macros/generic/markdown/markdown.html#pipe-tables          "Markdown Package User Manual"
  [option-shift-headings]: https://mirrors.ctan.org/macros/generic/markdown/markdown.html#option-shiftheadings "Markdown Package User Manual"

Modified: trunk/Master/texmf-dist/doc/generic/markdown/VERSION
===================================================================
--- trunk/Master/texmf-dist/doc/generic/markdown/VERSION	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/doc/generic/markdown/VERSION	2024-08-30 21:14:17 UTC (rev 72146)
@@ -1 +1 @@
-3.6.2-0-g6c30af7e (2024-07-14)
+3.7.0-0-g98dece19 (2024-08-30)

Modified: trunk/Master/texmf-dist/doc/generic/markdown/markdown.html
===================================================================
--- trunk/Master/texmf-dist/doc/generic/markdown/markdown.html	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/doc/generic/markdown/markdown.html	2024-08-30 21:14:17 UTC (rev 72146)
@@ -93,7 +93,7 @@
 <header id="title-block-header">
 <h1 class="title">Markdown Package User Manual</h1>
 <p class="author">Vít Starý Novotný, Andrej Genčur</p>
-<p class="date">3.6.2-0-g6c30af7e 2024-07-14</p>
+<p class="date">3.7.0-0-g98dece19 2024-08-30</p>
 </header>
 <nav id="TOC" role="doc-toc">
 <ul>
@@ -227,10 +227,10 @@
 it.</p>
 <p>From <a href="https://github.com/witiko/markdown/releases"
 title="Releases - witiko/markdown">Releases</a>, download <a
-href="https://github.com/witiko/markdown/releases/download/3.6.2/markdown.zip"
-title="Release 3.6.2 - witiko/markdown">an archive
+href="https://github.com/witiko/markdown/releases/download/3.7.0/markdown.zip"
+title="Release 3.7.0 - witiko/markdown">an archive
 <code>markdown.zip</code> for this version of the Markdown package
-(3.6.2)</a> or a different version that you wish to install. Then, unzip
+(3.7.0)</a> or a different version that you wish to install. Then, unzip
 the archive. If you downloaded an archive for a different version of the
 Markdown package, you should now locate a file named
 <code>markdown.html</code> with the user manual for that version, open
@@ -245,7 +245,8 @@
 <p>Either of the two abovelisted approaches should produce the following
 files:</p>
 <ul>
-<li><code>markdown.lua</code>: The Lua module</li>
+<li><code>markdown.lua</code> and <code>markdown-parser.lua</code>: The
+Lua module</li>
 <li><code>libraries/markdown-tinyyaml.lua</code>: An external library
 for reading <abbr>yaml</abbr></li>
 <li><code>markdown-cli.lua</code>: The Lua command-line interface</li>
@@ -275,6 +276,7 @@
 generally where the individual files should be placed:</p>
 <ul>
 <li><code>⟨TEXMF⟩/tex/luatex/markdown/markdown.lua</code></li>
+<li><code>⟨TEXMF⟩/tex/luatex/markdown/markdown-parser.lua</code></li>
 <li><code>⟨TEXMF⟩/tex/luatex/markdown/markdown-tinyyaml.lua</code></li>
 <li><code>⟨TEXMF⟩/scripts/markdown/markdown-cli.lua</code></li>
 <li><code>⟨TEXMF⟩/tex/generic/markdown/markdown.tex</code></li>
@@ -302,6 +304,7 @@
 <p>This is where the individual files should be placed:</p>
 <ul>
 <li><code>./markdown.lua</code></li>
+<li><code>./markdown-parser.lua</code></li>
 <li><code>./markdown-tinyyaml.lua</code></li>
 <li><code>./markdown-cli.lua</code></li>
 <li><code>./markdown/markdown.tex</code></li>
@@ -770,8 +773,8 @@
 class="tex">T<sub>e</sub>X</span> interface, but now the options are
 specified as ⟨<em>key</em>⟩<span
 class="math inline"> = </span>⟨<em>value</em>⟩ pairs and they are passed
-either as package options, in the <code>\markdownSetup</code> command,
-or as parameters for the <code>markdown*</code> <span
+either as package options, in the <code>markdownSetup</code> command, or
+as parameters for the <code>markdown*</code> <span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span> environment.</p>
 <p>Using a text editor, create a text document named
 <code>document.tex</code> with the following content:</p>
@@ -858,19 +861,22 @@
 class="header-section-number">2.2.1.1</span> Option
 <code>eagerCache</code></h4>
 <dl>
-<dt><code>eagerCache</code> (default value: <code>false</code>)</dt>
+<dt><code>eagerCache</code> (default value: <code>true</code>)</dt>
 <dd>
 <dl>
 <dt>true</dt>
 <dd>
 <p>Converted markdown documents will be cached in
-<strong><code>cacheDir</code></strong>. This can be useful for
+<code><strong>cacheDir</strong></code>. This can be useful for
 post-processing the converted documents and for recovering historical
-versions of the documents from the cache. However, it also produces a
-large number of auxiliary files on the disk and obscures the output of
-the Lua command-line interface when it is used for plumbing.</p>
+versions of the documents from the cache. Furthermore, it can also
+significantly improve the processing speed for documents that require
+multiple compilation runs, since each markdown document is only
+converted once. However, it also produces a large number of auxiliary
+files on the disk and obscures the output of the Lua command-line
+interface when it is used for plumbing.</p>
 <p>This behavior will always be used if the
-<strong><code>finalizeCache</code></strong> option is enabled.</p>
+<code><strong>finalizeCache</strong></code> option is enabled.</p>
 </dd>
 </dl>
 </dd>
@@ -880,9 +886,14 @@
 <dd>
 <p>Converted markdown documents will not be cached. This decreases the
 number of auxiliary files that we produce and makes it easier to use the
-Lua command-line interface for plumbing.</p>
+Lua command-line interface for plumbing. However, it makes it impossible
+to post-process the converted documents and recover historical versions
+of the documents from the cache. Furthermore, it can significantly
+reduce the processing speed for documents that require multiple
+compilation runs, since each markdown document is converted multiple
+times needlessly.</p>
 <p>This behavior will only be used when the
-<strong><code>finalizeCache</code></strong> option is disabled.</p>
+<code><strong>finalizeCache</strong></code> option is disabled.</p>
 </dd>
 </dl>
 </dd>
@@ -893,7 +904,7 @@
 <div class="sourceCode" id="cb46"><pre
 class="sourceCode md"><code class="sourceCode markdown"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a>Hello *world*!</span></code></pre></div>
 <p>Next, invoke LuaTeX from the terminal with the
-<strong><code>eagerCache</code></strong> option disabled:</p>
+<code><strong>eagerCache</strong></code> option disabled:</p>
 <div class="sourceCode" id="cb47"><pre
 class="sourceCode sh"><code class="sourceCode bash"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a><span class="ex">texlua</span> ⟨CLI pathname⟩ eagerCache=false <span class="at">--</span> hello.md hello.tex</span></code></pre></div>
 <p>where ⟨<em>CLI pathname</em>⟩ corresponds to the location of the Lua
@@ -912,7 +923,7 @@
 class="sourceCode tex"><code class="sourceCode latex"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>Hello <span class="fu">\markdownRendererEmphasis</span>{world}!<span class="fu">\relax</span></span></code></pre></div>
 <hr />
 <p>Invoke LuaTeX from the terminal again, this time with the
-<strong><code>eagerCache</code></strong> option enabled:</p>
+<code><strong>eagerCache</strong></code> option enabled:</p>
 <div class="sourceCode" id="cb49"><pre
 class="sourceCode tex"><code class="sourceCode latex"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>texlua ⟨CLI pathname⟩ eagerCache=true -- hello.md hello.tex</span></code></pre></div>
 <p>A <span class="tex">T<sub>e</sub>X</span> document named
@@ -935,7 +946,7 @@
 <dt>true</dt>
 <dd>
 <p>Conversion functions produced by the function
-\luamref{new}<code>(options)</code> will be cached in an LRU cache of
+<code>new</code><code>(options)</code> will be cached in an LRU cache of
 size 1 keyed by <code>options</code>. This is more time- and
 space-efficient than always producing a new conversion function but may
 expose bugs related to the idempotence of conversion functions.</p>
@@ -948,11 +959,12 @@
 <dl>
 <dt>false</dt>
 <dd>
-<p>Every call to the function \luamref{new}<code>(options)</code> will
-produce a new conversion function that will not be cached. This is
+<p>Every call to the function <code>new</code><code>(options)</code>
+will produce a new conversion function that will not be cached. This is
 slower than caching conversion functions and may expose bugs related to
-memory leaks in the creation of conversion functions, see also issue <a
-href="https://github.com/witiko/markdown/pull/226#issuecomment-1599641634">#226</a>.</p>
+memory leaks in the creation of conversion functions, see also <a
+href="https://github.com/witiko/markdown/pull/226#issuecomment-1599641634">#226
+(comment)</a>.</p>
 <p>This was the default behavior until version 3.0.0 of the Markdown
 package.</p>
 </dd>
@@ -1003,7 +1015,7 @@
 href="https://unicode.org/faq/normalization.html">Unicode normalization
 forms</a> before conversion. The Unicode normalization norm used is
 determined by option
-<strong><code>unicodeNormalizationForm</code></strong>.</p>
+<code><strong>unicodeNormalizationForm</strong></code>.</p>
 </dd>
 </dl>
 </dd>
@@ -1027,7 +1039,7 @@
 <dl>
 <dt>nfc</dt>
 <dd>
-<p>When option <strong><code>unicodeNormalization</code></strong> has
+<p>When option <code><strong>unicodeNormalization</strong></code> has
 been enabled, markdown documents will be normalized using Unicode
 Normalization Form C (NFC) before conversion.</p>
 </dd>
@@ -1037,7 +1049,7 @@
 <dl>
 <dt>nfd</dt>
 <dd>
-<p>When option <strong><code>unicodeNormalization</code></strong> has
+<p>When option <code><strong>unicodeNormalization</strong></code> has
 been enabled, markdown documents will be normalized using Unicode
 Normalization Form D (NFD) before conversion.</p>
 </dd>
@@ -1047,7 +1059,7 @@
 <dl>
 <dt>nfkc</dt>
 <dd>
-<p>When option <strong><code>unicodeNormalization</code></strong> has
+<p>When option <code><strong>unicodeNormalization</strong></code> has
 been enabled, markdown documents will be normalized using Unicode
 Normalization Form KC (NFKC) before conversion.</p>
 </dd>
@@ -1057,7 +1069,7 @@
 <dl>
 <dt>nfkd</dt>
 <dd>
-<p>When option <strong><code>unicodeNormalization</code></strong> has
+<p>When option <code><strong>unicodeNormalization</strong></code> has
 been enabled, markdown documents will be normalized using Unicode
 Normalization Form KD (NFKD) before conversion.</p>
 </dd>
@@ -1206,7 +1218,7 @@
 <dd>
 <p>The filename of the <abbr>JSON</abbr> file that maps filename
 extensions to programming language names in the iA Writer content blocks
-when the <strong><code>contentBlocks</code></strong> option is
+when the <code><strong>contentBlocks</strong></code> option is
 enabled.</p>
 </dd>
 </dl>
@@ -1370,13 +1382,13 @@
 <code>"debug-extensions.json"</code>)</dt>
 <dd>
 <p>The filename of the <abbr>JSON</abbr> file that will be produced when
-the <strong><code>debugExtensions</code></strong> option is enabled.
+the <code><strong>debugExtensions</strong></code> option is enabled.
 This file will contain the extensible subset of the <abbr>peg</abbr>
 grammar of markdown after built-in syntax extensions (see options
-<strong><code>citations</code></strong>,
-<strong><code>contentBlocks</code></strong>,
-<strong><code>definitionLists</code></strong>, etc.) and user-defined
-syntax extensions (see option <strong><code>extensions</code></strong>)
+<code><strong>citations</strong></code>,
+<code><strong>contentBlocks</strong></code>,
+<code><strong>definitionLists</strong></code>, etc.) and user-defined
+syntax extensions (see option <code><strong>extensions</strong></code>)
 have been applied.</p>
 </dd>
 </dl>
@@ -1388,13 +1400,13 @@
 <code>"frozenCache.tex"</code>)</dt>
 <dd>
 <p>A path to an output file (frozen cache) that will be created when the
-<strong><code>finalizeCache</code></strong> option is enabled and will
+<code><strong>finalizeCache</strong></code> option is enabled and will
 contain a mapping between an enumeration of markdown documents and their
 auxiliary cache files.</p>
 <p>The frozen cache makes it possible to later typeset a plain <span
 class="tex">T<sub>e</sub>X</span> document that contains markdown
 documents without invoking Lua using the
-<strong><code>frozenCache</code></strong> plain <span
+<code><strong>frozenCache</strong></code> plain <span
 class="tex">T<sub>e</sub>X</span> option. As a result, the plain <span
 class="tex">T<sub>e</sub>X</span> document becomes more portable, but
 further changes in the order and the content of markdown documents will
@@ -1611,7 +1623,7 @@
 </dd>
 </dl>
 <p>See also the option
-<strong><code>gfmAutoIdentifiers</code></strong>.</p>
+<code><strong>gfmAutoIdentifiers</strong></code>.</p>
 <h4 data-number="2.2.1.10" id="option-blankbeforeblockquote"><span
 class="header-section-number">2.2.1.10</span> Option
 <code>blankBeforeBlockquote</code></h4>
@@ -3385,13 +3397,13 @@
 <dd>
 <p>Produce a <abbr>JSON</abbr> file that will contain the extensible
 subset of the <abbr>peg</abbr> grammar of markdown after built-in syntax
-extensions (see options <strong><code>citations</code></strong>,
-<strong><code>contentBlocks</code></strong>,
-<strong><code>definitionLists</code></strong>, etc.) and user-defined
-syntax extensions (see option <strong><code>extensions</code></strong>)
+extensions (see options <code><strong>citations</strong></code>,
+<code><strong>contentBlocks</strong></code>,
+<code><strong>definitionLists</strong></code>, etc.) and user-defined
+syntax extensions (see option <code><strong>extensions</strong></code>)
 have been applied. This helps you to see how the different extensions
 interact. The name of the produced <abbr>JSON</abbr> file is controlled
-by the <strong><code>debugExtensionsFileName</code></strong> option.</p>
+by the <code><strong>debugExtensionsFileName</strong></code> option.</p>
 </dd>
 </dl>
 </dd>
@@ -3613,8 +3625,39 @@
 </dd>
 </dl>
 </blockquote>
-<h4 data-number="2.2.1.24" id="option-expectjekylldata"><span
+<h4 data-number="2.2.1.24" id="option-ensurejekylldata"><span
 class="header-section-number">2.2.1.24</span> Option
+<code>ensureJekyllData</code></h4>
+<dl>
+<dt><code>ensureJekyllData</code> (default value:
+<code>false</code>)</dt>
+<dd>
+<dl>
+<dt>false</dt>
+<dd>
+<p>When the <code><strong>jekyllData</strong></code> and
+<code><strong>expectJekyllData</strong></code> options are enabled, then
+a markdown document may begin directly with <abbr>yaml</abbr> metadata
+and may contain nothing but <abbr>yaml</abbr> metadata. Otherwise, the
+markdown document is processed as markdown text.</p>
+</dd>
+</dl>
+</dd>
+<dd>
+<dl>
+<dt>true</dt>
+<dd>
+<p>When the <code><strong>jekyllData</strong></code> and
+<code><strong>expectJekyllData</strong></code> options are enabled, then
+a markdown document must begin directly with <abbr>yaml</abbr> metadata
+and must contain nothing but <abbr>yaml</abbr> metadata. Otherwise, an
+error is produced.</p>
+</dd>
+</dl>
+</dd>
+</dl>
+<h4 data-number="2.2.1.25" id="option-expectjekylldata"><span
+class="header-section-number">2.2.1.25</span> Option
 <code>expectJekyllData</code></h4>
 <dl>
 <dt><code>expectJekyllData</code> (default value:
@@ -3623,7 +3666,7 @@
 <dl>
 <dt>false</dt>
 <dd>
-<p>When the <strong><code>jekyllData</code></strong> option is enabled,
+<p>When the <code><strong>jekyllData</strong></code> option is enabled,
 then a markdown document may begin with <abbr>yaml</abbr> metadata if
 and only if the metadata begin with the end-of-directives marker
 (<code>---</code>) and they end with either the end-of-directives or the
@@ -3655,7 +3698,7 @@
 <dl>
 <dt>true</dt>
 <dd>
-<p>When the <strong><code>jekyllData</code></strong> option is enabled,
+<p>When the <code><strong>jekyllData</strong></code> option is enabled,
 then a markdown document may begin directly with <abbr>yaml</abbr>
 metadata and may contain nothing but <abbr>yaml</abbr> metadata.</p>
 <div class="sourceCode" id="cb217"><pre
@@ -3713,14 +3756,14 @@
 <blockquote>
 <p>Jane Doe is 99 years old.</p>
 </blockquote>
-<h4 data-number="2.2.1.25" id="option-extensions"><span
-class="header-section-number">2.2.1.25</span> Option
+<h4 data-number="2.2.1.26" id="option-extensions"><span
+class="header-section-number">2.2.1.26</span> Option
 <code>extensions</code></h4>
 <dl>
 <dt><code>extensions</code> (default value: <code>{}</code>)</dt>
 <dd>
 <p>The filenames of user-defined syntax extensions that will be applied
-to the markdown reader. If the <strong><code>kpathsea</code></strong>
+to the markdown reader. If the <code><strong>kpathsea</strong></code>
 library is available, files will be searched for not only in the current
 working directory but also in the <span
 class="tex">T<sub>e</sub>X</span> directory structure.</p>
@@ -3773,8 +3816,8 @@
 <blockquote>
 <p>This is <del>a lunar roving vehicle</del> strike-through text.</p>
 </blockquote>
-<h4 data-number="2.2.1.26" id="option-fancylists"><span
-class="header-section-number">2.2.1.26</span> Option
+<h4 data-number="2.2.1.27" id="option-fancylists"><span
+class="header-section-number">2.2.1.27</span> Option
 <code>fancyLists</code></h4>
 <dl>
 <dt><code>fancyLists</code> (default value: <code>false</code>)</dt>
@@ -3854,8 +3897,8 @@
 <li>third item</li>
 </ol>
 </blockquote>
-<h4 data-number="2.2.1.27" id="option-fencedcode"><span
-class="header-section-number">2.2.1.27</span> Option
+<h4 data-number="2.2.1.28" id="option-fencedcode"><span
+class="header-section-number">2.2.1.28</span> Option
 <code>fencedCode</code></h4>
 <dl>
 <dt><code>fencedCode</code> (default value: <code>true</code>)</dt>
@@ -3990,8 +4033,8 @@
 <span id="cb237-7"><a href="#cb237-7" aria-hidden="true" tabindex="-1"></a>  <span class="dt"></</span><span class="kw">code</span><span class="dt">></span></span>
 <span id="cb237-8"><a href="#cb237-8" aria-hidden="true" tabindex="-1"></a><span class="dt"></</span><span class="kw">pre</span><span class="dt">></span></span></code></pre></div>
 </blockquote>
-<h4 data-number="2.2.1.28" id="option-fencedcodeattributes"><span
-class="header-section-number">2.2.1.28</span> Option
+<h4 data-number="2.2.1.29" id="option-fencedcodeattributes"><span
+class="header-section-number">2.2.1.29</span> Option
 <code>fencedCodeAttributes</code></h4>
 <dl>
 <dt><code>fencedCodeAttributes</code> (default value:
@@ -4067,8 +4110,8 @@
 <span id="cb241-2"><a href="#cb241-2" aria-hidden="true" tabindex="-1"></a><span class="fl">2.</span>     <span class="fu">moveShip</span>(<span class="dv">5</span> <span class="op">*</span> gravity<span class="op">,</span> DOWN)<span class="op">;</span></span>
 <span id="cb241-3"><a href="#cb241-3" aria-hidden="true" tabindex="-1"></a><span class="fl">3.</span> }</span></code></pre></div>
 </blockquote>
-<h4 data-number="2.2.1.29" id="fenced-divs"><span
-class="header-section-number">2.2.1.29</span> Option
+<h4 data-number="2.2.1.30" id="fenced-divs"><span
+class="header-section-number">2.2.1.30</span> Option
 <code>fencedDivs</code></h4>
 <dl>
 <dt><code>fencedDivs</code> (default value: <code>false</code>)</dt>
@@ -4123,20 +4166,20 @@
 <blockquote>
 <p>Here is a special paragraph.</p>
 </blockquote>
-<h4 data-number="2.2.1.30" id="option-finalizecache"><span
-class="header-section-number">2.2.1.30</span> Option
+<h4 data-number="2.2.1.31" id="option-finalizecache"><span
+class="header-section-number">2.2.1.31</span> Option
 <code>finalizeCache</code></h4>
 <dl>
 <dt><code>finalizeCache</code> (default value: <code>false</code>)</dt>
 <dd>
 <p>Whether an output file specified with the
-<strong><code>frozenCacheFileName</code></strong> option (frozen cache)
+<code><strong>frozenCacheFileName</strong></code> option (frozen cache)
 that contains a mapping between an enumeration of markdown documents and
 their auxiliary cache files will be created.</p>
 <p>The frozen cache makes it possible to later typeset a plain <span
 class="tex">T<sub>e</sub>X</span> document that contains markdown
 documents without invoking Lua using the
-<strong><code>frozenCache</code></strong> plain <span
+<code><strong>frozenCache</strong></code> plain <span
 class="tex">T<sub>e</sub>X</span> option. As a result, the plain <span
 class="tex">T<sub>e</sub>X</span> document becomes more portable, but
 further changes in the order and the content of markdown documents will
@@ -4255,8 +4298,8 @@
 the document without accessing the shell or invoking Lua, but the change
 in the content of the markdown document from “Hello <em>world</em>!” to
 “Hi <em>world</em>!” was not reflected.</p>
-<h4 data-number="2.2.1.31" id="option-frozencachecounter"><span
-class="header-section-number">2.2.1.31</span> Option
+<h4 data-number="2.2.1.32" id="option-frozencachecounter"><span
+class="header-section-number">2.2.1.32</span> Option
 <code>frozenCacheCounter</code></h4>
 <dl>
 <dt><code>frozenCacheCounter</code> (default value: <code>0</code>)</dt>
@@ -4263,7 +4306,7 @@
 <dd>
 <p>The number of the current markdown document that will be stored in an
 output file (frozen cache) when the
-<strong><code>finalizeCache</code></strong> is enabled. When the
+<code><strong>finalizeCache</strong></code> is enabled. When the
 document number is 0, then a new frozen cache will be created.
 Otherwise, the frozen cache will be appended.</p>
 <p>Each frozen cache entry will define a <span
@@ -4296,8 +4339,8 @@
 contain the text “Hello <em>world</em>!” A frozen cache with two entries
 will also be produced as we requested using the
 <code>frozenCacheCounter</code> option.</p>
-<h4 data-number="2.2.1.32" id="option-gfmautoidentifiers"><span
-class="header-section-number">2.2.1.32</span> Option
+<h4 data-number="2.2.1.33" id="option-gfmautoidentifiers"><span
+class="header-section-number">2.2.1.33</span> Option
 <code>gfmAutoIdentifiers</code></h4>
 <dl>
 <dt><code>gfmAutoIdentifiers</code> (default value:
@@ -4323,9 +4366,9 @@
 </dd>
 </dl>
 <p>See also the option
-<strong><code>autoIdentifiers</code></strong>.</p>
-<h4 data-number="2.2.1.33" id="option-hashenumerators"><span
-class="header-section-number">2.2.1.33</span> Option
+<code><strong>autoIdentifiers</strong></code>.</p>
+<h4 data-number="2.2.1.34" id="option-hashenumerators"><span
+class="header-section-number">2.2.1.34</span> Option
 <code>hashEnumerators</code></h4>
 <dl>
 <dt><code>hashEnumerators</code> (default value:
@@ -4427,8 +4470,8 @@
 <li>Parish</li>
 </ol>
 </blockquote>
-<h4 data-number="2.2.1.34" id="header-attributes"><span
-class="header-section-number">2.2.1.34</span> Option
+<h4 data-number="2.2.1.35" id="header-attributes"><span
+class="header-section-number">2.2.1.35</span> Option
 <code>headerAttributes</code></h4>
 <dl>
 <dt><code>headerAttributes</code> (default value:
@@ -4457,8 +4500,8 @@
 </dl>
 </dd>
 </dl>
-<h4 data-number="2.2.1.35" id="option-html"><span
-class="header-section-number">2.2.1.35</span> Option
+<h4 data-number="2.2.1.36" id="option-html"><span
+class="header-section-number">2.2.1.36</span> Option
 <code>html</code></h4>
 <dl>
 <dt><code>html</code> (default value: <code>true</code>)</dt>
@@ -4695,8 +4738,8 @@
 support.</p>
 <p>There is support. There is support. There is support.</p>
 </blockquote>
-<h4 data-number="2.2.1.36" id="option-hybrid"><span
-class="header-section-number">2.2.1.36</span> Option
+<h4 data-number="2.2.1.37" id="option-hybrid"><span
+class="header-section-number">2.2.1.37</span> Option
 <code>hybrid</code></h4>
 <dl>
 <dt><code>hybrid</code> (default value: <code>false</code>)</dt>
@@ -4887,8 +4930,8 @@
 <p><math><msqrt><mo>−</mo><mn>1</mn></msqrt></math> <em>equals</em>
 <math><mi>i</mi></math>.</p>
 </blockquote>
-<h4 data-number="2.2.1.37" id="option-inlinecodeattributes"><span
-class="header-section-number">2.2.1.37</span> Option
+<h4 data-number="2.2.1.38" id="option-inlinecodeattributes"><span
+class="header-section-number">2.2.1.38</span> Option
 <code>inlineCodeAttributes</code></h4>
 <dl>
 <dt><code>inlineCodeAttributes</code> (default value:
@@ -4962,8 +5005,8 @@
 <p>Here is some <span
 style="color: red"><code>colored text</code></span>.</p>
 </blockquote>
-<h4 data-number="2.2.1.38" id="option-inlinenotes"><span
-class="header-section-number">2.2.1.38</span> Option
+<h4 data-number="2.2.1.39" id="option-inlinenotes"><span
+class="header-section-number">2.2.1.39</span> Option
 <code>inlineNotes</code></h4>
 <dl>
 <dt><code>inlineNotes</code> (default value: <code>false</code>)</dt>
@@ -5036,8 +5079,8 @@
 <p>Here is an inline note.<a href="#fn2" class="footnote-ref"
 id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
 </blockquote>
-<h4 data-number="2.2.1.39" id="option-jekylldata"><span
-class="header-section-number">2.2.1.39</span> Option
+<h4 data-number="2.2.1.40" id="option-jekylldata"><span
+class="header-section-number">2.2.1.40</span> Option
 <code>jekyllData</code></h4>
 <dl>
 <dt><code>jekyllData</code> (default value: <code>false</code>)</dt>
@@ -5175,8 +5218,8 @@
 <blockquote>
 <p>Jane Doe is 99 years old.</p>
 </blockquote>
-<h4 data-number="2.2.1.40" id="option-linkattributes"><span
-class="header-section-number">2.2.1.40</span> Option
+<h4 data-number="2.2.1.41" id="option-linkattributes"><span
+class="header-section-number">2.2.1.41</span> Option
 <code>linkAttributes</code></h4>
 <dl>
 <dt><code>linkAttributes</code> (default value: <code>false</code>)</dt>
@@ -5247,8 +5290,8 @@
 contain an example image (from <a href="https://ctan.org/pkg/mwe"
 title="mwe – Packages and image files for MWEs">Martin Scharrer’s mwe
 package</a>) displayed at size 5cm × 4cm.</p>
-<h4 data-number="2.2.1.41" id="option-lineblocks"><span
-class="header-section-number">2.2.1.41</span> Option
+<h4 data-number="2.2.1.42" id="option-lineblocks"><span
+class="header-section-number">2.2.1.42</span> Option
 <code>lineBlocks</code></h4>
 <dl>
 <dt><code>lineBlocks</code> (default value: <code>false</code>)</dt>
@@ -5354,8 +5397,8 @@
 I have spread my dreams under your feet;<br />
 Tread softly because you tread on my dreams.</div>
 </blockquote>
-<h4 data-number="2.2.1.42" id="option-mark"><span
-class="header-section-number">2.2.1.42</span> Option
+<h4 data-number="2.2.1.43" id="option-mark"><span
+class="header-section-number">2.2.1.43</span> Option
 <code>mark</code></h4>
 <dl>
 <dt><code>mark</code> (default value: <code>false</code>)</dt>
@@ -5402,8 +5445,8 @@
 <blockquote>
 <p>This <mark>is highlighted text.</mark></p>
 </blockquote>
-<h4 data-number="2.2.1.43" id="option-notes"><span
-class="header-section-number">2.2.1.43</span> Option
+<h4 data-number="2.2.1.44" id="option-notes"><span
+class="header-section-number">2.2.1.44</span> Option
 <code>notes</code></h4>
 <dl>
 <dt><code>notes</code> (default value: <code>false</code>)</dt>
@@ -5534,8 +5577,8 @@
 <p>This paragraph won’t be part of the note, because it isn’t
 indented.</p>
 </blockquote>
-<h4 data-number="2.2.1.44" id="pipe-tables"><span
-class="header-section-number">2.2.1.44</span> Option
+<h4 data-number="2.2.1.45" id="pipe-tables"><span
+class="header-section-number">2.2.1.45</span> Option
 <code>pipeTables</code></h4>
 <dl>
 <dt><code>pipeTables</code> (default value: <code>false</code>)</dt>
@@ -5669,8 +5712,8 @@
 </tbody>
 </table>
 </blockquote>
-<h4 data-number="2.2.1.45" id="option-preservetabs"><span
-class="header-section-number">2.2.1.45</span> Option
+<h4 data-number="2.2.1.46" id="option-preservetabs"><span
+class="header-section-number">2.2.1.46</span> Option
 <code>preserveTabs</code></h4>
 <dl>
 <dt><code>preserveTabs</code> (default value: <code>true</code>)</dt>
@@ -5691,8 +5734,8 @@
 </dl>
 </dd>
 </dl>
-<h4 data-number="2.2.1.46" id="option-rawattribute"><span
-class="header-section-number">2.2.1.46</span> Option
+<h4 data-number="2.2.1.47" id="option-rawattribute"><span
+class="header-section-number">2.2.1.47</span> Option
 <code>rawAttribute</code></h4>
 <dl>
 <dt><code>rawAttribute</code> (default value: <code>false</code>)</dt>
@@ -5705,7 +5748,7 @@
 attribute syntax extension</a>:</p>
 <div class="sourceCode" id="cb330"><pre
 class="sourceCode md"><code class="sourceCode markdown"><span id="cb330-1"><a href="#cb330-1" aria-hidden="true" tabindex="-1"></a><span class="in">`$H_2 O$`</span>{=tex} is a liquid.</span></code></pre></div>
-<p>To enable raw blocks, the <strong><code>fencedCode</code></strong>
+<p>To enable raw blocks, the <code><strong>fencedCode</strong></code>
 option must also be enabled:</p>
 <div class="sourceCode" id="cb331"><pre
 class="sourceCode md"><code class="sourceCode markdown"><span id="cb331-1"><a href="#cb331-1" aria-hidden="true" tabindex="-1"></a><span class="an">Here is a mathematical formula:</span></span>
@@ -5717,10 +5760,10 @@
 <span id="cb331-7"><a href="#cb331-7" aria-hidden="true" tabindex="-1"></a><span class="in">    \end{dcases}</span></span>
 <span id="cb331-8"><a href="#cb331-8" aria-hidden="true" tabindex="-1"></a><span class="in">\]</span></span>
 <span id="cb331-9"><a href="#cb331-9" aria-hidden="true" tabindex="-1"></a><span class="in">```</span></span></code></pre></div>
-<p>The <strong><code>rawAttribute</code></strong> option is a good
-alternative to the <strong><code>hybrid</code></strong> option. Unlike
-the <strong><code>hybrid</code></strong> option, which affects the
-entire document, the <strong><code>rawAttribute</code></strong> option
+<p>The <code><strong>rawAttribute</strong></code> option is a good
+alternative to the <code><strong>hybrid</strong></code> option. Unlike
+the <code><strong>hybrid</strong></code> option, which affects the
+entire document, the <code><strong>rawAttribute</strong></code> option
 allows you to isolate the parts of your documents that use TeX:</p>
 </dd>
 </dl>
@@ -5759,8 +5802,8 @@
 <blockquote>
 <p>H<sub>2</sub>O is a liquid.</p>
 </blockquote>
-<h4 data-number="2.2.1.47" id="option-relativereferences"><span
-class="header-section-number">2.2.1.47</span> Option
+<h4 data-number="2.2.1.48" id="option-relativereferences"><span
+class="header-section-number">2.2.1.48</span> Option
 <code>relativeReferences</code></h4>
 <dl>
 <dt><code>relativeReferences</code> (default value:
@@ -5824,8 +5867,8 @@
 <p>In this paper, we have discovered that most grandmas would rather eat
 dinner with their grandchildren than get eaten. Begone, wolf!</p>
 </blockquote>
-<h4 data-number="2.2.1.48" id="option-shiftheadings"><span
-class="header-section-number">2.2.1.48</span> Option
+<h4 data-number="2.2.1.49" id="option-shiftheadings"><span
+class="header-section-number">2.2.1.49</span> Option
 <code>shiftHeadings</code></h4>
 <dl>
 <dt><code>shiftHeadings</code> (default value: <code>0</code>)</dt>
@@ -5926,8 +5969,8 @@
 <h2 id="a-section-7">A section</h2>
 <h3 id="a-section-8">A section</h3>
 </blockquote>
-<h4 data-number="2.2.1.49" id="option-slice"><span
-class="header-section-number">2.2.1.49</span> Option
+<h4 data-number="2.2.1.50" id="option-slice"><span
+class="header-section-number">2.2.1.50</span> Option
 <code>slice</code></h4>
 <dl>
 <dt><code>slice</code> (default value: <code>^ $</code>)</dt>
@@ -6094,8 +6137,8 @@
 <h2 id="act-v-2">Act V</h2>
 <p>Hamlet dies.</p>
 </blockquote>
-<h4 data-number="2.2.1.50" id="option-smartellipses"><span
-class="header-section-number">2.2.1.50</span> Option
+<h4 data-number="2.2.1.51" id="option-smartellipses"><span
+class="header-section-number">2.2.1.51</span> Option
 <code>smartEllipses</code></h4>
 <dl>
 <dt><code>smartEllipses</code> (default value: <code>false</code>)</dt>
@@ -6104,7 +6147,7 @@
 <dt>true</dt>
 <dd>
 <p>Convert any ellipses in the input to the
-<code>\markdownRendererEllipsis</code> <span
+<code>markdownRendererEllipsis</code> <span
 class="tex">T<sub>e</sub>X</span> macro.</p>
 </dd>
 </dl>
@@ -6278,8 +6321,8 @@
 <p>These are just three regular dots …</p>
 <p>. . . and this is a victorian ellipsis.</p>
 </blockquote>
-<h4 data-number="2.2.1.51" id="option-startnumber"><span
-class="header-section-number">2.2.1.51</span> Option
+<h4 data-number="2.2.1.52" id="option-startnumber"><span
+class="header-section-number">2.2.1.52</span> Option
 <code>startNumber</code></h4>
 <dl>
 <dt><code>startNumber</code> (default value: <code>true</code>)</dt>
@@ -6289,7 +6332,7 @@
 <dd>
 <p>Make the number in the first item of an ordered lists significant.
 The item numbers will be passed to the
-<code>\markdownRendererOlItemWithNumber</code> <span
+<code>markdownRendererOlItemWithNumber</code> <span
 class="tex">T<sub>e</sub>X</span> macro.</p>
 </dd>
 </dl>
@@ -6299,7 +6342,7 @@
 <dt>false</dt>
 <dd>
 <p>Ignore the numbers in the ordered list items. Each item will only
-produce a <code>\markdownRendererOlItem</code> <span
+produce a <code>markdownRendererOlItem</code> <span
 class="tex">T<sub>e</sub>X</span> macro.</p>
 </dd>
 </dl>
@@ -6397,8 +6440,8 @@
 <li>fifth item</li>
 </ol>
 </blockquote>
-<h4 data-number="2.2.1.52" id="option-strikethrough"><span
-class="header-section-number">2.2.1.52</span> Option
+<h4 data-number="2.2.1.53" id="option-strikethrough"><span
+class="header-section-number">2.2.1.53</span> Option
 <code>strikeThrough</code></h4>
 <dl>
 <dt><code>strikeThrough</code> (default value: <code>false</code>)</dt>
@@ -6491,8 +6534,8 @@
 <blockquote>
 <p>This is <del>a lunar roving vehicle</del> strike-through text.</p>
 </blockquote>
-<h4 data-number="2.2.1.53" id="option-stripindent"><span
-class="header-section-number">2.2.1.53</span> Option
+<h4 data-number="2.2.1.54" id="option-stripindent"><span
+class="header-section-number">2.2.1.54</span> Option
 <code>stripIndent</code></h4>
 <dl>
 <dt><code>stripIndent</code> (default value: <code>false</code>)</dt>
@@ -6502,7 +6545,7 @@
 <dd>
 <p>Strip the minimal indentation of non-blank lines from all lines in a
 markdown document. Requires that the
-<strong><code>preserveTabs</code></strong> Lua option is disabled:</p>
+<code><strong>preserveTabs</strong></code> Lua option is disabled:</p>
 <div class="sourceCode" id="cb375"><pre
 class="sourceCode tex"><code class="sourceCode latex"><span id="cb375-1"><a href="#cb375-1" aria-hidden="true" tabindex="-1"></a><span class="bu">\documentclass</span>{<span class="ex">article</span>}</span>
 <span id="cb375-2"><a href="#cb375-2" aria-hidden="true" tabindex="-1"></a><span class="bu">\usepackage</span>[stripIndent]{<span class="ex">markdown</span>}</span>
@@ -6574,8 +6617,8 @@
 class="sourceCode sh"><code class="sourceCode bash"><span id="cb381-1"><a href="#cb381-1" aria-hidden="true" tabindex="-1"></a><span class="ex">context</span> <span class="at">--luatex</span> document.tex</span></code></pre></div>
 <p>A PDF document named <code>document.pdf</code> should be produced and
 contain the text “Hello <em>world</em>!”</p>
-<h4 data-number="2.2.1.54" id="option-subscripts"><span
-class="header-section-number">2.2.1.54</span> Option
+<h4 data-number="2.2.1.55" id="option-subscripts"><span
+class="header-section-number">2.2.1.55</span> Option
 <code>subscripts</code></h4>
 <dl>
 <dt><code>subscripts</code> (default value: <code>false</code>)</dt>
@@ -6640,8 +6683,8 @@
 <blockquote>
 <p>H<sub>2</sub>O is a liquid.</p>
 </blockquote>
-<h4 data-number="2.2.1.55" id="option-superscripts"><span
-class="header-section-number">2.2.1.55</span> Option
+<h4 data-number="2.2.1.56" id="option-superscripts"><span
+class="header-section-number">2.2.1.56</span> Option
 <code>superscripts</code></h4>
 <dl>
 <dt><code>superscripts</code> (default value: <code>false</code>)</dt>
@@ -6706,8 +6749,8 @@
 <blockquote>
 <p>2<sup>10</sup> is 1024.</p>
 </blockquote>
-<h4 data-number="2.2.1.56" id="option-tableattributes"><span
-class="header-section-number">2.2.1.56</span> Option
+<h4 data-number="2.2.1.57" id="option-tableattributes"><span
+class="header-section-number">2.2.1.57</span> Option
 <code>tableAttributes</code></h4>
 <dl>
 <dt><code>tableAttributes</code> (default value:
@@ -6829,8 +6872,8 @@
 </tbody>
 </table>
 </blockquote>
-<h4 data-number="2.2.1.57" id="table-captions"><span
-class="header-section-number">2.2.1.57</span> Option
+<h4 data-number="2.2.1.58" id="table-captions"><span
+class="header-section-number">2.2.1.58</span> Option
 <code>tableCaptions</code></h4>
 <dl>
 <dt><code>tableCaptions</code> (default value: <code>false</code>)</dt>
@@ -6978,8 +7021,8 @@
 </tbody>
 </table>
 </blockquote>
-<h4 data-number="2.2.1.58" id="option-tasklists"><span
-class="header-section-number">2.2.1.58</span> Option
+<h4 data-number="2.2.1.59" id="option-tasklists"><span
+class="header-section-number">2.2.1.59</span> Option
 <code>taskLists</code></h4>
 <dl>
 <dt><code>taskLists</code> (default value: <code>false</code>)</dt>
@@ -7068,8 +7111,8 @@
 <li>Yes I can!</li>
 </ul>
 </blockquote>
-<h4 data-number="2.2.1.59" id="option-texcomments"><span
-class="header-section-number">2.2.1.59</span> Option
+<h4 data-number="2.2.1.60" id="option-texcomments"><span
+class="header-section-number">2.2.1.60</span> Option
 <code>texComments</code></h4>
 <dl>
 <dt><code>texComments</code> (default value: <code>false</code>)</dt>
@@ -7087,7 +7130,7 @@
 <span id="cb405-6"><a href="#cb405-6" aria-hidden="true" tabindex="-1"></a>   lo *world*!</span>
 <span id="cb405-7"><a href="#cb405-7" aria-hidden="true" tabindex="-1"></a><span class="kw">\end</span>{<span class="ex">markdown</span>}</span>
 <span id="cb405-8"><a href="#cb405-8" aria-hidden="true" tabindex="-1"></a><span class="kw">\end</span>{<span class="ex">document</span>}</span></code></pre></div>
-<p>Always enabled when <strong><code>hybrid</code></strong> is
+<p>Always enabled when <code><strong>hybrid</strong></code> is
 enabled.</p>
 </dd>
 </dl>
@@ -7155,8 +7198,8 @@
 class="sourceCode sh"><code class="sourceCode bash"><span id="cb411-1"><a href="#cb411-1" aria-hidden="true" tabindex="-1"></a><span class="ex">context</span> <span class="at">--luatex</span> document.tex</span></code></pre></div>
 <p>A PDF document named <code>document.pdf</code> should be produced and
 contain the text “Hello <em>world</em>!”</p>
-<h4 data-number="2.2.1.60" id="option-texmathdollars"><span
-class="header-section-number">2.2.1.60</span> Option
+<h4 data-number="2.2.1.61" id="option-texmathdollars"><span
+class="header-section-number">2.2.1.61</span> Option
 <code>texMathDollars</code></h4>
 <dl>
 <dt><code>texMathDollars</code> (default value: <code>false</code>)</dt>
@@ -7339,8 +7382,8 @@
 <p><span
 class="math display"><em>f̂</em>(<em>ξ</em>) = ∫<sub>−∞</sub><sup>∞</sup><em>f</em>(<em>x</em>)<em>e</em><sup>−<em>i</em>2<em>π</em><em>ξ</em><em>x</em></sup><em>d</em><em>x</em></span></p>
 </blockquote>
-<h4 data-number="2.2.1.61" id="option-texmathdoublebackslash"><span
-class="header-section-number">2.2.1.61</span> Option
+<h4 data-number="2.2.1.62" id="option-texmathdoublebackslash"><span
+class="header-section-number">2.2.1.62</span> Option
 <code>texMathDoubleBackslash</code></h4>
 <dl>
 <dt><code>texMathDoubleBackslash</code> (default value:
@@ -7524,8 +7567,8 @@
 <p><span
 class="math display"><em>f̂</em>(<em>ξ</em>) = ∫<sub>−∞</sub><sup>∞</sup><em>f</em>(<em>x</em>)<em>e</em><sup>−<em>i</em>2<em>π</em><em>ξ</em><em>x</em></sup><em>d</em><em>x</em></span></p>
 </blockquote>
-<h4 data-number="2.2.1.62" id="option-texmathsinglebackslash"><span
-class="header-section-number">2.2.1.62</span> Option
+<h4 data-number="2.2.1.63" id="option-texmathsinglebackslash"><span
+class="header-section-number">2.2.1.63</span> Option
 <code>texMathSingleBackslash</code></h4>
 <dl>
 <dt><code>texMathSingleBackslash</code> (default value:
@@ -7709,8 +7752,8 @@
 <p><span
 class="math display"><em>f̂</em>(<em>ξ</em>) = ∫<sub>−∞</sub><sup>∞</sup><em>f</em>(<em>x</em>)<em>e</em><sup>−<em>i</em>2<em>π</em><em>ξ</em><em>x</em></sup><em>d</em><em>x</em></span></p>
 </blockquote>
-<h4 data-number="2.2.1.63" id="option-tightlists"><span
-class="header-section-number">2.2.1.63</span> Option
+<h4 data-number="2.2.1.64" id="option-tightlists"><span
+class="header-section-number">2.2.1.64</span> Option
 <code>tightLists</code></h4>
 <dl>
 <dt><code>tightLists</code> (default value: <code>true</code>)</dt>
@@ -7806,8 +7849,8 @@
 <li><p>third item</p></li>
 </ul>
 </blockquote>
-<h4 data-number="2.2.1.64" id="option-underscores"><span
-class="header-section-number">2.2.1.64</span> Option
+<h4 data-number="2.2.1.65" id="option-underscores"><span
+class="header-section-number">2.2.1.65</span> Option
 <code>underscores</code></h4>
 <dl>
 <dt><code>underscores</code> (default value: <code>true</code>)</dt>
@@ -7831,7 +7874,7 @@
 <dd>
 <p>Only asterisks can be used to denote emphasis and strong emphasis.
 This makes it easy to write math with the
-<strong><code>hybrid</code></strong> option without the need to
+<code><strong>hybrid</strong></code> option without the need to
 constantly escape subscripts.</p>
 </dd>
 </dl>
@@ -7940,7 +7983,7 @@
 plain <span class="tex">T<sub>e</sub>X</span></h4>
 <p>As a rule of thumb, you can set all Lua options directly from plain
 <span class="tex">T<sub>e</sub>X</span>. For example, to set the
-<strong><code>taskLists</code></strong> Lua option to <code>true</code>,
+<code><strong>taskLists</strong></code> Lua option to <code>true</code>,
 you would include the following code in your plain <span
 class="tex">T<sub>e</sub>X</span> document:</p>
 <div class="sourceCode" id="cb458"><pre
@@ -7948,7 +7991,7 @@
 <p>Alternatively, you can also set plain <span
 class="tex">T<sub>e</sub>X</span> options using the
 <code>\markdownSetup</code> <span class="tex">T<sub>e</sub>X</span>
-macro. For example, to set the <strong><code>taskLists</code></strong>
+macro. For example, to set the <code><strong>taskLists</strong></code>
 Lua option to <code>true</code>, you would include the following code in
 your plain <span class="tex">T<sub>e</sub>X</span> document:</p>
 <div class="sourceCode" id="cb459"><pre
@@ -7956,29 +7999,29 @@
 <h4 data-number="2.2.2.2" id="finalizing-and-freezing-the-cache"><span
 class="header-section-number">2.2.2.2</span> Finalizing and Freezing the
 Cache</h4>
-<p>The <code>\markdownOptionFrozenCache</code> option uses the mapping
+<p>The <code>markdownOptionFrozenCache</code> option uses the mapping
 previously created by the Lua interface
-<strong><code>finalizeCache</code></strong> option, and uses it to
+<code><strong>finalizeCache</strong></code> option, and uses it to
 typeset the plain <span class="tex">T<sub>e</sub>X</span> document
 without invoking Lua. As a result, the plain <span
 class="tex">T<sub>e</sub>X</span> document becomes more portable, but
 further changes in the order and the content of markdown documents will
 not be reflected. It defaults to <code>false</code>.</p>
-<p>The standard usage of the <strong><code>finalizeCache</code></strong>
-and <strong><code>frozenCache</code></strong> options is as follows:</p>
+<p>The standard usage of the <code><strong>finalizeCache</strong></code>
+and <code><strong>frozenCache</strong></code> options is as follows:</p>
 <ol type="1">
-<li>Remove the <strong><code>cacheDir</code></strong> cache directory
+<li>Remove the <code><strong>cacheDir</strong></code> cache directory
 with stale auxiliary cache files.</li>
-<li>Enable the <strong><code>finalizeCache</code></strong> option.</li>
+<li>Enable the <code><strong>finalizeCache</strong></code> option.</li>
 <li>Typeset the plain <span class="tex">T<sub>e</sub>X</span> document
 to populate and finalize the cache.</li>
-<li>Enable the <strong><code>frozenCache</code></strong> option.</li>
+<li>Enable the <code><strong>frozenCache</strong></code> option.</li>
 <li>Publish the source code of the plain <span
 class="tex">T<sub>e</sub>X</span> document and the
-<strong><code>cacheDir</code></strong> directory.</li>
+<code><strong>cacheDir</strong></code> directory.</li>
 </ol>
 <p>For more information, see the examples for the
-<strong><code>finalizeCache</code></strong> option.</p>
+<code><strong>finalizeCache</strong></code> option.</p>
 <h4 data-number="2.2.2.3" id="file-and-directory-names"><span
 class="header-section-number">2.2.2.3</span> File and Directory
 Names</h4>
@@ -8045,7 +8088,7 @@
 class="tex">T<sub>e</sub>X</span>t. Furthermore, the default definitions
 may change at any time, which may pose a problem for maintainers of
 Markdown themes and templates who may require a stable output.</p>
-<p>The <code>\markdownOptionPlain</code> macro specifies whether
+<p>The <code>markdownOptionPlain</code> macro specifies whether
 higher-level <span class="tex">T<sub>e</sub>X</span> formats should only
 use the plain <span class="tex">T<sub>e</sub>X</span> default
 definitions or whether they should also use the format-specific default
@@ -8069,7 +8112,7 @@
 <span id="cb464-2"><a href="#cb464-2" aria-hidden="true" tabindex="-1"></a><span class="fu">\usemodule</span>[t][markdown]</span></code></pre></div>
 <p>The macro must be set before or during the loading of the package.
 Setting the macro after loading the package has no effect.</p>
-<p>The <code>\markdownOptionNoDefaults</code> macro specifies whether we
+<p>The <code>markdownOptionNoDefaults</code> macro specifies whether we
 should prevent the loading of default definitions or not. This is useful
 in contexts, where we want to have total control over how all elements
 are rendered.</p>
@@ -8086,7 +8129,7 @@
 Setting the macro after loading the package has no effect.</p>
 <h4 data-number="2.2.2.5" id="package-documentation"><span
 class="header-section-number">2.2.2.5</span> Package Documentation</h4>
-<p>The <code>\markdownOptionStripPercentSigns</code> macro controls
+<p>The <code>markdownOptionStripPercentSigns</code> macro controls
 whether a percent sign (<code>\%</code>) at the beginning of a line will
 be discarded when reading Markdown input from a <span
 class="tex">T<sub>e</sub>X</span> document. This enables the use of
@@ -8125,10 +8168,10 @@
 <p>Built-in plain <span class="tex">T<sub>e</sub>X</span> themes
 provided with the Markdown package include:</p>
 <dl>
-<dt><strong><code>witiko/tilde</code></strong></dt>
+<dt><code><strong>witiko/tilde</strong></code></dt>
 <dd>
 <p>A theme that makes tilde (<code>~</code>) always typeset the
-non-breaking space even when the <strong><code>hybrid</code></strong>
+non-breaking space even when the <code><strong>hybrid</strong></code>
 Lua option is disabled.</p>
 </dd>
 </dl>
@@ -8153,7 +8196,7 @@
 <p>Bartel·Leendert van·der·Waerden</p>
 </blockquote>
 <dl>
-<dt><strong><code>witiko/markdown/defaults</code></strong></dt>
+<dt><code><strong>witiko/markdown/defaults</strong></code></dt>
 <dd>
 <p>A plain <span class="tex">T<sub>e</sub>X</span> theme with the
 default definitions of token renderer prototypes for plain <span
@@ -8276,7 +8319,7 @@
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span></h4>
 <p>As a rule of thumb, we can set all Lua options directly from <span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span>. For example, to set
-the <strong><code>taskLists</code></strong> Lua option to
+the <code><strong>taskLists</strong></code> Lua option to
 <code>true</code>, we would include the following code in our <span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span> document:</p>
 <div class="sourceCode" id="cb476"><pre
@@ -8318,7 +8361,7 @@
 class="tex">T<sub>e</sub>X</span> formats. To enable code reuse,
 developers can load the <code>.tex</code> theme file from the
 <code>.sty</code> theme file using the
-<code>\markdownLoadPlainTeXTheme</code> macro.</p>
+<code>markdownLoadPlainTeXTheme</code> macro.</p>
 <p>For example, to load themes named <code>witiko/beamer/MU</code> and
 <code>witiko/dot</code>, you would use the following code in the
 preamble of your document:</p>
@@ -8334,7 +8377,7 @@
 <p>Built-in <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span>
 themes provided with the Markdown package include:</p>
 <dl>
-<dt><strong><code>witiko/dot</code></strong></dt>
+<dt><code><strong>witiko/dot</strong></code></dt>
 <dd>
 <p>A theme that typesets fenced code blocks with the <code>dot …</code>
 infostring as images of directed graphs rendered by the Graphviz tools.
@@ -8341,7 +8384,7 @@
 The right tail of the infostring is used as the image title. The theme
 requires a Unix-like operating system with GNU Diffutils and Graphviz
 installed. The theme also requires shell access unless the
-<strong><code>frozenCache</code></strong> plain <span
+<code><strong>frozenCache</strong></code> plain <span
 class="tex">T<sub>e</sub>X</span> option is enabled.</p>
 </dd>
 </dl>
@@ -8400,15 +8443,15 @@
 class="uri">http://ceur-ws.org/Vol-2696/paper_235.pdf</a></p>
 </blockquote>
 <dl>
-<dt><strong><code>witiko/graphicx/http</code></strong></dt>
+<dt><code><strong>witiko/graphicx/http</strong></code></dt>
 <dd>
 <p>A theme that adds support for downloading images whose URL has the
 http or https protocol. The theme requires the
-<strong><code>catchfile</code></strong> <span
+<code><strong>catchfile</strong></code> <span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span> package and a Unix-like
 operating system with GNU Coreutils <code>md5sum</code> and either GNU
 Wget or cURL installed. The theme also requires shell access unless the
-<strong><code>frozenCache</code></strong> plain <span
+<code><strong>frozenCache</strong></code> plain <span
 class="tex">T<sub>e</sub>X</span> option is enabled.</p>
 </dd>
 </dl>
@@ -8438,7 +8481,7 @@
 </figure>
 </blockquote>
 <dl>
-<dt><strong><code>witiko/markdown/defaults</code></strong></dt>
+<dt><code><strong>witiko/markdown/defaults</strong></code></dt>
 <dd>
 <p>A <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> theme with
 the default definitions of token renderer prototypes for plain <span
@@ -8471,7 +8514,7 @@
 class="tex">T<sub>e</sub>X</span> formats. To enable code reuse,
 developers can load the <code>.tex</code> theme file from the
 <code>t-*.tex</code> theme file using the
-<code>\markdownLoadPlainTeXTheme</code> macro.</p>
+<code>markdownLoadPlainTeXTheme</code> macro.</p>
 <p>For example, to load a theme named <code>witiko/tilde</code> in your
 document:</p>
 <div class="sourceCode" id="cb483"><pre
@@ -8480,7 +8523,7 @@
 <p>Built-in Con<span class="tex">T<sub>e</sub>X</span>t themes provided
 with the Markdown package include:</p>
 <dl>
-<dt><strong><code>witiko/markdown/defaults</code></strong></dt>
+<dt><code><strong>witiko/markdown/defaults</strong></code></dt>
 <dd>
 <p>A Con<span class="tex">T<sub>e</sub>X</span>t theme with the default
 definitions of token renderer prototypes for plain <span
@@ -8507,26 +8550,26 @@
 following options for markdown attributes on different elements is
 enabled:</p>
 <ul>
-<li><strong><code>autoIdentifiers</code></strong></li>
-<li><strong><code>fencedCodeAttributes</code></strong></li>
-<li><strong><code>gfmAutoIdentifiers</code></strong></li>
-<li><strong><code>headerAttributes</code></strong></li>
-<li><strong><code>inlineCodeAttributes</code></strong></li>
-<li><strong><code>linkAttributes</code></strong></li>
+<li><code><strong>autoIdentifiers</strong></code></li>
+<li><code><strong>fencedCodeAttributes</strong></code></li>
+<li><code><strong>gfmAutoIdentifiers</strong></code></li>
+<li><code><strong>headerAttributes</strong></code></li>
+<li><code><strong>inlineCodeAttributes</strong></code></li>
+<li><code><strong>linkAttributes</strong></code></li>
 </ul>
-<p><code>\markdownRendererAttributeIdentifier</code> represents the
+<p><code>markdownRendererAttributeIdentifier</code> represents the
 ⟨<em>identifier</em>⟩ of a markdown element
 (<code>id="</code>⟨<em>identifier</em>⟩<code>"</code> in HTML and
 <code>#</code>⟨<em>identifier</em>⟩ in markdown attributes). The macro
 receives a single attribute that corresponds to the
 ⟨<em>identifier</em>⟩.</p>
-<p><code>\markdownRendererAttributeClassName</code> represents the
+<p><code>markdownRendererAttributeClassName</code> represents the
 ⟨<em>class name</em>⟩ of a markdown element
 (<code>class="</code>⟨<em>class name</em>⟩ …<code>"</code> in HTML and
 <code>.</code>⟨<em>class name</em>⟩ in markdown attributes). The macro
 receives a single attribute that corresponds to the ⟨<em>class
 name</em>⟩.</p>
-<p><code>\markdownRendererAttributeKeyValue</code> represents a HTML
+<p><code>markdownRendererAttributeKeyValue</code> represents a HTML
 attribute in the form ⟨<em>key</em>⟩<code>=</code>⟨<em>value</em>⟩ that
 is neither an identifier nor a class name. The macro receives two
 attributes that correspond to the ⟨<em>key</em>⟩ and the
@@ -8583,9 +8626,9 @@
 </blockquote>
 <h4 data-number="2.3.1.2" id="block-quote-renderers"><span
 class="header-section-number">2.3.1.2</span> Block Quote Renderers</h4>
-<p>The <code>\markdownRendererBlockQuoteBegin</code> macro represents
-the beginning of a block quote. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererBlockQuoteEnd</code> macro represents the
+<p>The <code>markdownRendererBlockQuoteBegin</code> macro represents the
+beginning of a block quote. The macro receives no arguments.</p>
+<p>The <code>markdownRendererBlockQuoteEnd</code> macro represents the
 end of a block quote. The macro receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-25">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -8698,12 +8741,11 @@
 class="header-section-number">2.3.1.3</span> Bracketed Spans Attribute
 Context Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>bracketedSpans</code></strong> option is enabled.</p>
-<p>The <code>\markdownRendererBracketedSpanAttributeContextBegin</code>
-and <code>\markdownRendererBracketedSpanAttributeContextEnd</code>
-macros represent the beginning and the end of a context in which the
-attributes of an inline bracketed span apply. The macros receive no
-arguments.</p>
+<code><strong>bracketedSpans</strong></code> option is enabled.</p>
+<p>The <code>markdownRendererBracketedSpanAttributeContextBegin</code>
+and <code>markdownRendererBracketedSpanAttributeContextEnd</code> macros
+represent the beginning and the end of a context in which the attributes
+of an inline bracketed span apply. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="latex-example-58"><span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span> Example</h5>
 <p>Using a text editor, create a text document named
@@ -8734,26 +8776,26 @@
 </blockquote>
 <h4 data-number="2.3.1.4" id="bullet-list-renderers"><span
 class="header-section-number">2.3.1.4</span> Bullet List Renderers</h4>
-<p>The <code>\markdownRendererUlBegin</code> macro represents the
+<p>The <code>markdownRendererUlBegin</code> macro represents the
 beginning of a bulleted list that contains an item with several
 paragraphs of text (the list is not tight). The macro receives no
 arguments.</p>
-<p>The <code>\markdownRendererUlBeginTight</code> macro represents the
+<p>The <code>markdownRendererUlBeginTight</code> macro represents the
 beginning of a bulleted list that contains no item with several
 paragraphs of text (the list is tight). This macro will only be
-produced, when the <strong><code>tightLists</code></strong> option is
+produced, when the <code><strong>tightLists</strong></code> option is
 disabled. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererUlItem</code> macro represents an item in
-a bulleted list. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererUlItemEnd</code> macro represents the end
+<p>The <code>markdownRendererUlItem</code> macro represents an item in a
+bulleted list. The macro receives no arguments.</p>
+<p>The <code>markdownRendererUlItemEnd</code> macro represents the end
 of an item in a bulleted list. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererUlEnd</code> macro represents the end of a
+<p>The <code>markdownRendererUlEnd</code> macro represents the end of a
 bulleted list that contains an item with several paragraphs of text (the
 list is not tight). The macro receives no arguments.</p>
-<p>The <code>\markdownRendererUlEndTight</code> macro represents the end
+<p>The <code>markdownRendererUlEndTight</code> macro represents the end
 of a bulleted list that contains no item with several paragraphs of text
 (the list is tight). This macro will only be produced, when the
-<strong><code>tightLists</code></strong> option is disabled. The macro
+<code><strong>tightLists</strong></code> option is disabled. The macro
 receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-26">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -8949,9 +8991,9 @@
 </blockquote>
 <h4 data-number="2.3.1.5" id="citation-renderers"><span
 class="header-section-number">2.3.1.5</span> Citation Renderers</h4>
-<p>The <code>\markdownRendererCite</code> macro represents a string of
+<p>The <code>markdownRendererCite</code> macro represents a string of
 one or more parenthetical citations. This macro will only be produced,
-when the <strong><code>citations</code></strong> option is enabled. The
+when the <code><strong>citations</strong></code> option is enabled. The
 macro receives the parameter <code>{</code>⟨<em>number of
 citations</em>⟩<code>}</code> followed by ⟨<em>suppress author</em>⟩
 <code>{</code>⟨<em>prenote</em>⟩<code>}{</code>⟨<em>postnote</em>⟩<code>}{</code>⟨<em>name</em>⟩<code>}</code>
@@ -9013,11 +9055,11 @@
 see and a postfix <em>pp. 12</em>, and a citation <em>eijkhout91</em>
 with a postfix <em>pp. 34</em>.</p>
 </blockquote>
-<p>The <code>\markdownRendererTextCite</code> macro represents a string
+<p>The <code>markdownRendererTextCite</code> macro represents a string
 of one or more text citations. This macro will only be produced, when
-the <strong><code>citations</code></strong> option is enabled. The macro
+the <code><strong>citations</strong></code> option is enabled. The macro
 receives parameters in the same format as the
-<code>\markdownRendererCite</code> macro.</p>
+<code>markdownRendererCite</code> macro.</p>
 <h5 class="unnumbered" id="latex-example-61"><span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span> Example</h5>
 <p>Using a text editor, create a text document named
@@ -9075,12 +9117,12 @@
 </blockquote>
 <h4 data-number="2.3.1.6" id="code-block-renderers"><span
 class="header-section-number">2.3.1.6</span> Code Block Renderers</h4>
-<p>The <code>\markdownRendererInputVerbatim</code> macro represents a
+<p>The <code>markdownRendererInputVerbatim</code> macro represents a
 code block. The macro receives a single argument that corresponds to the
 filename of a file containing the code block contents.</p>
-<p>The <code>\markdownRendererInputFencedCode</code> macro represents a
+<p>The <code>markdownRendererInputFencedCode</code> macro represents a
 fenced code block. This macro will only be produced, when the
-<strong><code>fencedCode</code></strong> option is enabled. The macro
+<code><strong>fencedCode</strong></code> option is enabled. The macro
 receives three arguments that correspond to the filename of a file
 containing the code block contents, the fully escaped code fence
 infostring that can be directly typeset, and the raw code fence
@@ -9165,7 +9207,7 @@
 </blockquote>
 <h4 data-number="2.3.1.7" id="code-span-renderer"><span
 class="header-section-number">2.3.1.7</span> Code Span Renderer</h4>
-<p>The <code>\markdownRendererCodeSpan</code> macro represents inline
+<p>The <code>markdownRendererCodeSpan</code> macro represents inline
 code span in the input text. It receives a single argument that
 corresponds to the inline code span.</p>
 <h5 class="unnumbered" id="plain-tex-example-27">Plain <span
@@ -9274,10 +9316,10 @@
 class="header-section-number">2.3.1.8</span> Code Span Attribute Context
 Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>inlineCodeAttributes</code></strong> option is
+<code><strong>inlineCodeAttributes</strong></code> option is
 enabled.</p>
-<p>The <code>\markdownRendererCodeSpanAttributeContextBegin</code> and
-<code>\markdownRendererCodeSpanAttributeContextEnd</code> macros
+<p>The <code>markdownRendererCodeSpanAttributeContextBegin</code> and
+<code>markdownRendererCodeSpanAttributeContextEnd</code> macros
 represent the beginning and the end of a context in which the attributes
 of an inline code span apply. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="latex-example-64"><span
@@ -9312,21 +9354,21 @@
 <h4 data-number="2.3.1.9" id="texcontentblockrenderers"><span
 class="header-section-number">2.3.1.9</span> Content Block
 Renderers</h4>
-<p>The <code>\markdownRendererContentBlock</code> macro represents an
+<p>The <code>markdownRendererContentBlock</code> macro represents an
 iA Writer content block. It receives four arguments: the local file or
 online image filename extension cast to the lower case, the fully
 escaped <abbr>uri</abbr> that can be directly typeset, the raw
 <abbr>uri</abbr> that can be used outside typesetting, and the title of
 the content block.</p>
-<p>The <code>\markdownRendererContentBlockOnlineImage</code> macro
+<p>The <code>markdownRendererContentBlockOnlineImage</code> macro
 represents an iA Writer online image content block. The macro receives
-the same arguments as <code>\markdownRendererContentBlock</code>.</p>
-<p>The <code>\markdownRendererContentBlockCode</code> macro represents
-an iA Writer content block that was recognized as a file in a known
+the same arguments as <code>markdownRendererContentBlock</code>.</p>
+<p>The <code>markdownRendererContentBlockCode</code> macro represents an
+iA Writer content block that was recognized as a file in a known
 programming language by its filename extension <span
 class="math inline"><em>s</em></span>. If any
 <code>markdown-languages.json</code> file found by
-<strong><code>kpathsea</code></strong><a href="#fn3"
+<code><strong>kpathsea</strong></code><a href="#fn3"
 class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a>
 contains a record <span
 class="math inline">(<em>k</em>,<em>v</em>)</span>, then a
@@ -9484,34 +9526,34 @@
 class="header-section-number">2.3.1.10</span> Definition List
 Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>definitionLists</code></strong> option is enabled.</p>
-<p>The <code>\markdownRendererDlBegin</code> macro represents the
+<code><strong>definitionLists</strong></code> option is enabled.</p>
+<p>The <code>markdownRendererDlBegin</code> macro represents the
 beginning of a definition list that contains an item with several
 paragraphs of text (the list is not tight). The macro receives no
 arguments.</p>
-<p>The <code>\markdownRendererDlBeginTight</code> macro represents the
+<p>The <code>markdownRendererDlBeginTight</code> macro represents the
 beginning of a definition list that contains no item with several
 paragraphs of text (the list is tight). This macro will only be
-produced, when the <strong><code>tightLists</code></strong> option is
+produced, when the <code><strong>tightLists</strong></code> option is
 disabled. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererDlItem</code> macro represents a term in a
+<p>The <code>markdownRendererDlItem</code> macro represents a term in a
 definition list. The macro receives a single argument that corresponds
 to the term being defined.</p>
-<p>The <code>\markdownRendererDlItemEnd</code> macro represents the end
+<p>The <code>markdownRendererDlItemEnd</code> macro represents the end
 of a list of definitions for a single term.</p>
-<p>The <code>\markdownRendererDlDefinitionBegin</code> macro represents
+<p>The <code>markdownRendererDlDefinitionBegin</code> macro represents
 the beginning of a definition in a definition list. There can be several
 definitions for a single term.</p>
-<p>The <code>\markdownRendererDlDefinitionEnd</code> macro represents
-the end of a definition in a definition list. There can be several
+<p>The <code>markdownRendererDlDefinitionEnd</code> macro represents the
+end of a definition in a definition list. There can be several
 definitions for a single term.</p>
-<p>The <code>\markdownRendererDlEnd</code> macro represents the end of a
+<p>The <code>markdownRendererDlEnd</code> macro represents the end of a
 definition list that contains an item with several paragraphs of text
 (the list is not tight). The macro receives no arguments.</p>
-<p>The <code>\markdownRendererDlEndTight</code> macro represents the end
+<p>The <code>markdownRendererDlEndTight</code> macro represents the end
 of a definition list that contains no item with several paragraphs of
 text (the list is tight). This macro will only be produced, when the
-<strong><code>tightLists</code></strong> option is disabled. The macro
+<code><strong>tightLists</strong></code> option is disabled. The macro
 receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-29">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -9840,9 +9882,9 @@
 </blockquote>
 <h4 data-number="2.3.1.11" id="ellipsis-renderer"><span
 class="header-section-number">2.3.1.11</span> Ellipsis Renderer</h4>
-<p>The <code>\markdownRendererEllipsis</code> macro replaces any
+<p>The <code>markdownRendererEllipsis</code> macro replaces any
 occurrence of ASCII ellipses in the input text. This macro will only be
-produced, when the <strong><code>smartEllipses</code></strong> option is
+produced, when the <code><strong>smartEllipses</strong></code> option is
 enabled. The macro receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-30">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -9914,10 +9956,10 @@
 </blockquote>
 <h4 data-number="2.3.1.12" id="emphasis-renderers"><span
 class="header-section-number">2.3.1.12</span> Emphasis Renderers</h4>
-<p>The <code>\markdownRendererEmphasis</code> macro represents an
+<p>The <code>markdownRendererEmphasis</code> macro represents an
 emphasized span of text. The macro receives a single argument that
 corresponds to the emphasized span of text.</p>
-<p>The <code>\markdownRendererStrongEmphasis</code> macro represents a
+<p>The <code>markdownRendererStrongEmphasis</code> macro represents a
 strongly emphasized span of text. The macro receives a single argument
 that corresponds to the emphasized span of text.</p>
 <h5 class="unnumbered" id="plain-tex-example-31">Plain <span
@@ -10001,9 +10043,11 @@
 class="header-section-number">2.3.1.13</span> Fenced Code Attribute
 Context Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>fencedCode</code></strong> option is enabled.</p>
-<p>The <code>\markdownRendererFencedCodeAttributeContextBegin</code> and
-<code>\markdownRendererFencedCodeAttributeContextEnd</code> macros
+<code><strong>fencedCode</strong></code> and
+<code><strong>fencedCodeAttributes</strong></code> options are
+enabled.</p>
+<p>The <code>markdownRendererFencedCodeAttributeContextBegin</code> and
+<code>markdownRendererFencedCodeAttributeContextEnd</code> macros
 represent the beginning and the end of a context in which the attributes
 of a fenced code apply. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="latex-example-69"><span
@@ -10057,9 +10101,9 @@
 class="header-section-number">2.3.1.14</span> Fenced Div Attribute
 Context Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>fencedDiv</code></strong> option is enabled.</p>
-<p>The <code>\markdownRendererFencedDivAttributeContextBegin</code> and
-<code>\markdownRendererFencedDivAttributeContextEnd</code> macros
+<code><strong>fencedDiv</strong></code> option is enabled.</p>
+<p>The <code>markdownRendererFencedDivAttributeContextBegin</code> and
+<code>markdownRendererFencedDivAttributeContextEnd</code> macros
 represent the beginning and the end of a context in which the attributes
 of a div apply. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="latex-example-70"><span
@@ -10126,11 +10170,11 @@
 class="header-section-number">2.3.1.15</span> Header Attribute Context
 Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>autoIdentifiers</code></strong>,
-<strong><code>gfmAutoIdentifiers</code></strong>, or
-<strong><code>headerAttributes</code></strong> options are enabled.</p>
-<p>The <code>\markdownRendererHeaderAttributeContextBegin</code> and
-<code>\markdownRendererHeaderAttributeContextEnd</code> macros represent
+<code><strong>autoIdentifiers</strong></code>,
+<code><strong>gfmAutoIdentifiers</strong></code>, or
+<code><strong>headerAttributes</strong></code> options are enabled.</p>
+<p>The <code>markdownRendererHeaderAttributeContextBegin</code> and
+<code>markdownRendererHeaderAttributeContextEnd</code> macros represent
 the beginning and the end of a context in which the attributes of a
 heading apply. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="latex-example-71"><span
@@ -10181,24 +10225,24 @@
 </blockquote>
 <h4 data-number="2.3.1.16" id="heading-renderers"><span
 class="header-section-number">2.3.1.16</span> Heading Renderers</h4>
-<p>The <code>\markdownRendererHeadingOne</code> macro represents a first
+<p>The <code>markdownRendererHeadingOne</code> macro represents a first
 level heading. The macro receives a single argument that corresponds to
 the heading text.</p>
-<p>The <code>\markdownRendererHeadingTwo</code> macro represents a
-second level heading. The macro receives a single argument that
-corresponds to the heading text.</p>
-<p>The <code>\markdownRendererHeadingThree</code> macro represents a
+<p>The <code>markdownRendererHeadingTwo</code> macro represents a second
+level heading. The macro receives a single argument that corresponds to
+the heading text.</p>
+<p>The <code>markdownRendererHeadingThree</code> macro represents a
 third level heading. The macro receives a single argument that
 corresponds to the heading text.</p>
-<p>The <code>\markdownRendererHeadingFour</code> macro represents a
+<p>The <code>markdownRendererHeadingFour</code> macro represents a
 fourth level heading. The macro receives a single argument that
 corresponds to the heading text.</p>
-<p>The <code>\markdownRendererHeadingFive</code> macro represents a
-fifth level heading. The macro receives a single argument that
-corresponds to the heading text.</p>
-<p>The <code>\markdownRendererHeadingSix</code> macro represents a sixth
+<p>The <code>markdownRendererHeadingFive</code> macro represents a fifth
 level heading. The macro receives a single argument that corresponds to
 the heading text.</p>
+<p>The <code>markdownRendererHeadingSix</code> macro represents a sixth
+level heading. The macro receives a single argument that corresponds to
+the heading text.</p>
 <h5 class="unnumbered" id="plain-tex-example-32">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
 <p>Using a text editor, create a text document named
@@ -10296,9 +10340,9 @@
 <h4 data-number="2.3.1.17" id="inline-html-comment-renderer"><span
 class="header-section-number">2.3.1.17</span> Inline HTML Comment
 Renderer</h4>
-<p>The <code>\markdownRendererInlineHtmlComment</code> macro represents
+<p>The <code>markdownRendererInlineHtmlComment</code> macro represents
 the contents of an inline <abbr>HTML</abbr> comment. This macro will
-only be produced, when the <strong><code>html</code></strong> option is
+only be produced, when the <code><strong>html</strong></code> option is
 enabled. The macro receives a single argument that corresponds to the
 contents of the <abbr>HTML</abbr> comment.</p>
 <h5 class="unnumbered" id="latex-example-73"><span
@@ -10339,14 +10383,14 @@
 <h4 data-number="2.3.1.18" id="html-tag-and-element-renderers"><span
 class="header-section-number">2.3.1.18</span> HTML Tag and Element
 Renderers</h4>
-<p>The <code>\markdownRendererInlineHtmlTag</code> macro represents an
+<p>The <code>markdownRendererInlineHtmlTag</code> macro represents an
 opening, closing, or empty inline <abbr>HTML</abbr> tag. This macro will
-only be produced, when the <strong><code>html</code></strong> option is
+only be produced, when the <code><strong>html</strong></code> option is
 enabled. The macro receives a single argument that corresponds to the
 contents of the <abbr>HTML</abbr> tag.</p>
-<p>The <code>\markdownRendererInputBlockHtmlElement</code> macro
+<p>The <code>markdownRendererInputBlockHtmlElement</code> macro
 represents a block <abbr>HTML</abbr> element. This macro will only be
-produced, when the <strong><code>html</code></strong> option is enabled.
+produced, when the <code><strong>html</strong></code> option is enabled.
 The macro receives a single argument that filename of a file containing
 the contents of the <abbr>HTML</abbr> element.</p>
 <h5 class="unnumbered" id="latex-example-74"><span
@@ -10383,7 +10427,7 @@
 </blockquote>
 <h4 data-number="2.3.1.19" id="image-renderer"><span
 class="header-section-number">2.3.1.19</span> Image Renderer</h4>
-<p>The <code>\markdownRendererImage</code> macro represents an image. It
+<p>The <code>markdownRendererImage</code> macro represents an image. It
 receives four arguments: the label, the fully escaped <abbr>uri</abbr>
 that can be directly typeset, the raw <abbr>uri</abbr> that can be used
 outside typesetting, and the title of the link.</p>
@@ -10447,9 +10491,9 @@
 class="header-section-number">2.3.1.20</span> Image Attribute Context
 Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>linkAttributes</code></strong> option is enabled.</p>
-<p>The <code>\markdownRendererImageAttributeContextBegin</code> and
-<code>\markdownRendererImageAttributeContextEnd</code> macros represent
+<code><strong>linkAttributes</strong></code> option is enabled.</p>
+<p>The <code>markdownRendererImageAttributeContextBegin</code> and
+<code>markdownRendererImageAttributeContextEnd</code> macros represent
 the beginning and the end of a context in which the attributes of an
 image apply. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="latex-example-76"><span
@@ -10484,9 +10528,9 @@
 <h4 data-number="2.3.1.21" id="interblock-separator-renderers"><span
 class="header-section-number">2.3.1.21</span> Interblock Separator
 Renderers</h4>
-<p>The <code>\markdownRendererInterblockSeparator</code> macro
-represents an interblock separator between two markdown block elements.
-The macro receives no arguments.</p>
+<p>The <code>markdownRendererInterblockSeparator</code> macro represents
+an interblock separator between two markdown block elements. The macro
+receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-33">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
 <p>Using a text editor, create a text document named
@@ -10586,7 +10630,7 @@
 This produces a paragraph separator instead of an interblock separator.
 Between some blocks, such as markdown paragraphs, a paragraph separator
 is always produced.</p>
-<p>The <code>\markdownRendererParagraphSeparator</code> macro represents
+<p>The <code>markdownRendererParagraphSeparator</code> macro represents
 a paragraph separator. The macro receives no arguments.</p>
 <h5 class="unnumbered" id="latex-example-78"><span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span> Example</h5>
@@ -10624,11 +10668,10 @@
 <h4 data-number="2.3.1.22" id="line-block-renderers"><span
 class="header-section-number">2.3.1.22</span> Line Block Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>lineBlocks</code></strong> option is enabled.</p>
-<p>The <code>\markdownRendererLineBlockBegin</code> and
-<code>\markdownRendererLineBlockEnd</code> macros represent the
-beginning and the end of a line block. The macros receive no
-arguments.</p>
+<code><strong>lineBlocks</strong></code> option is enabled.</p>
+<p>The <code>markdownRendererLineBlockBegin</code> and
+<code>markdownRendererLineBlockEnd</code> macros represent the beginning
+and the end of a line block. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-34">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
 <p>Using a text editor, create a text document named
@@ -10708,7 +10751,7 @@
 </blockquote>
 <h4 data-number="2.3.1.23" id="line-break-renderers"><span
 class="header-section-number">2.3.1.23</span> Line Break Renderers</h4>
-<p>The <code>\markdownRendererSoftLineBreak</code> macro represents a
+<p>The <code>markdownRendererSoftLineBreak</code> macro represents a
 soft line break. The macro receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-35">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -10772,7 +10815,7 @@
 <p><em>(A soft line break)</em></p>
 <p><em>Foo</em> bar!</p>
 </blockquote>
-<p>The <code>\markdownRendererHardLineBreak</code> macro represents a
+<p>The <code>markdownRendererHardLineBreak</code> macro represents a
 hard line break. The macro receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-36">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -10842,7 +10885,7 @@
 </blockquote>
 <h4 data-number="2.3.1.24" id="link-renderer"><span
 class="header-section-number">2.3.1.24</span> Link Renderer</h4>
-<p>The <code>\markdownRendererLink</code> macro represents a hyperlink.
+<p>The <code>markdownRendererLink</code> macro represents a hyperlink.
 It receives four arguments: the label, the fully escaped
 <abbr>uri</abbr> that can be directly typeset, the raw <abbr>uri</abbr>
 that can be used outside typesetting, and the title of the link.</p>
@@ -10935,9 +10978,9 @@
 class="header-section-number">2.3.1.25</span> Link Attribute Context
 Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>linkAttributes</code></strong> option is enabled.</p>
-<p>The <code>\markdownRendererLinkAttributeContextBegin</code> and
-<code>\markdownRendererLinkAttributeContextEnd</code> macros represent
+<code><strong>linkAttributes</strong></code> option is enabled.</p>
+<p>The <code>markdownRendererLinkAttributeContextBegin</code> and
+<code>markdownRendererLinkAttributeContextEnd</code> macros represent
 the beginning and the end of a context in which the attributes of a
 hyperlink apply. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="latex-example-83"><span
@@ -10972,8 +11015,8 @@
 <h4 data-number="2.3.1.26" id="marked-text-renderer"><span
 class="header-section-number">2.3.1.26</span> Marked Text Renderer</h4>
 <p>The following macro is only produced, when the
-<strong><code>mark</code></strong> option is enabled.</p>
-<p>The <code>\markdownRendererMark</code> macro represents a span of
+<code><strong>mark</strong></code> option is enabled.</p>
+<p>The <code>markdownRendererMark</code> macro represents a span of
 marked or highlighted text. The macro receives a single argument that
 corresponds to the marked text.</p>
 <h5 class="unnumbered" id="latex-example-84"><span
@@ -11007,8 +11050,8 @@
 <h4 data-number="2.3.1.27" id="markdown-document-renderers"><span
 class="header-section-number">2.3.1.27</span> Markdown Document
 Renderers</h4>
-<p>The <code>\markdownRendererDocumentBegin</code> and
-<code>\markdownRendererDocumentEnd</code> macros represent the beginning
+<p>The <code>markdownRendererDocumentBegin</code> and
+<code>markdownRendererDocumentEnd</code> macros represent the beginning
 and the end of a <em>markdown</em> document. The macros receive no
 arguments.</p>
 <p>A <span class="tex">T<sub>e</sub>X</span> document may contain any
@@ -11108,8 +11151,8 @@
 <h4 data-number="2.3.1.28" id="non-breaking-space-renderer"><span
 class="header-section-number">2.3.1.28</span> Non-Breaking Space
 Renderer</h4>
-<p>The <code>\markdownRendererNbsp</code> macro represents a
-non-breaking space.</p>
+<p>The <code>markdownRendererNbsp</code> macro represents a non-breaking
+space.</p>
 <h5 class="unnumbered" id="latex-example-86"><span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span> Example</h5>
 <p>Using a text editor, create a text document named
@@ -11160,9 +11203,9 @@
 </blockquote>
 <h4 data-number="2.3.1.29" id="note-renderer"><span
 class="header-section-number">2.3.1.29</span> Note Renderer</h4>
-<p>The <code>\markdownRendererNote</code> macro represents a note. This
+<p>The <code>markdownRendererNote</code> macro represents a note. This
 macro will only be produced, when the
-<strong><code>notes</code></strong> option is enabled. The macro
+<code><strong>notes</strong></code> option is enabled. The macro
 receives a single argument that corresponds to the note text.</p>
 <h5 class="unnumbered" id="plain-tex-example-38">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -11248,21 +11291,21 @@
 <h4 data-number="2.3.1.30" id="ordered-list-renderers"><span
 class="header-section-number">2.3.1.30</span> Ordered List
 Renderers</h4>
-<p>The <code>\markdownRendererOlBegin</code> macro represents the
+<p>The <code>markdownRendererOlBegin</code> macro represents the
 beginning of an ordered list that contains an item with several
 paragraphs of text (the list is not tight). This macro will only be
-produced, when the <strong><code>fancyLists</code></strong> option is
+produced, when the <code><strong>fancyLists</strong></code> option is
 disabled. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererOlBeginTight</code> macro represents the
+<p>The <code>markdownRendererOlBeginTight</code> macro represents the
 beginning of an ordered list that contains no item with several
 paragraphs of text (the list is tight). This macro will only be
-produced, when the <strong><code>tightLists</code></strong> option is
-enabled and the <strong><code>fancyLists</code></strong> option is
+produced, when the <code><strong>tightLists</strong></code> option is
+enabled and the <code><strong>fancyLists</strong></code> option is
 disabled. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererFancyOlBegin</code> macro represents the
+<p>The <code>markdownRendererFancyOlBegin</code> macro represents the
 beginning of a fancy ordered list that contains an item with several
 paragraphs of text (the list is not tight). This macro will only be
-produced, when the <strong><code>fancyLists</code></strong> option is
+produced, when the <code><strong>fancyLists</strong></code> option is
 enabled. The macro receives two arguments: the style of the list item
 labels (<code>Decimal</code>, <code>LowerRoman</code>,
 <code>UpperRoman</code>, <code>LowerAlpha</code>, and
@@ -11269,66 +11312,66 @@
 <code>UpperAlpha</code>), and the style of delimiters between list item
 labels and texts (<code>Default</code>, <code>OneParen</code>, and
 <code>Period</code>).</p>
-<p>The <code>\markdownRendererFancyOlBeginTight</code> macro represents
+<p>The <code>markdownRendererFancyOlBeginTight</code> macro represents
 the beginning of a fancy ordered list that contains no item with several
 paragraphs of text (the list is tight). This macro will only be
-produced, when the <strong><code>fancyLists</code></strong> and
-<strong><code>tightLists</code></strong> options are enabled. The macro
+produced, when the <code><strong>fancyLists</strong></code> and
+<code><strong>tightLists</strong></code> options are enabled. The macro
 receives two arguments: the style of the list item labels, and the style
 of delimiters between list item labels and texts. See the
-<code>\markdownRendererFancyOlBegin</code> macro for the valid style
+<code>markdownRendererFancyOlBegin</code> macro for the valid style
 values.</p>
-<p>The <code>\markdownRendererOlItem</code> macro represents an item in
+<p>The <code>markdownRendererOlItem</code> macro represents an item in
 an ordered list. This macro will only be produced, when the
-<strong><code>startNumber</code></strong> option is disabled and the
-<strong><code>fancyLists</code></strong> option is disabled. The macro
+<code><strong>startNumber</strong></code> option is disabled and the
+<code><strong>fancyLists</strong></code> option is disabled. The macro
 receives no arguments.</p>
-<p>The <code>\markdownRendererOlItemEnd</code> macro represents the end
+<p>The <code>markdownRendererOlItemEnd</code> macro represents the end
 of an item in an ordered list. This macro will only be produced, when
-the <strong><code>fancyLists</code></strong> option is disabled. The
+the <code><strong>fancyLists</strong></code> option is disabled. The
 macro receives no arguments.</p>
-<p>The <code>\markdownRendererOlItemWithNumber</code> macro represents
-an item in an ordered list. This macro will only be produced, when the
-<strong><code>startNumber</code></strong> option is enabled and the
-<strong><code>fancyLists</code></strong> option is disabled. The macro
+<p>The <code>markdownRendererOlItemWithNumber</code> macro represents an
+item in an ordered list. This macro will only be produced, when the
+<code><strong>startNumber</strong></code> option is enabled and the
+<code><strong>fancyLists</strong></code> option is disabled. The macro
 receives a single numeric argument that corresponds to the item
 number.</p>
-<p>The <code>\markdownRendererFancyOlItem</code> macro represents an
-item in a fancy ordered list. This macro will only be produced, when the
-<strong><code>startNumber</code></strong> option is disabled and the
-<strong><code>fancyLists</code></strong> option is enabled. The macro
+<p>The <code>markdownRendererFancyOlItem</code> macro represents an item
+in a fancy ordered list. This macro will only be produced, when the
+<code><strong>startNumber</strong></code> option is disabled and the
+<code><strong>fancyLists</strong></code> option is enabled. The macro
 receives no arguments.</p>
-<p>The <code>\markdownRendererFancyOlItemEnd</code> macro represents the
+<p>The <code>markdownRendererFancyOlItemEnd</code> macro represents the
 end of an item in a fancy ordered list. This macro will only be
-produced, when the <strong><code>fancyLists</code></strong> option is
+produced, when the <code><strong>fancyLists</strong></code> option is
 enabled. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererFancyOlItemWithNumber</code> macro
+<p>The <code>markdownRendererFancyOlItemWithNumber</code> macro
 represents an item in a fancy ordered list. This macro will only be
-produced, when the <strong><code>startNumber</code></strong> and
-<strong><code>fancyLists</code></strong> options are enabled. The macro
+produced, when the <code><strong>startNumber</strong></code> and
+<code><strong>fancyLists</strong></code> options are enabled. The macro
 receives a single numeric argument that corresponds to the item
 number.</p>
-<p>The <code>\markdownRendererOlEnd</code> macro represents the end of
-an ordered list that contains an item with several paragraphs of text
-(the list is not tight). This macro will only be produced, when the
-<strong><code>fancyLists</code></strong> option is disabled. The macro
+<p>The <code>markdownRendererOlEnd</code> macro represents the end of an
+ordered list that contains an item with several paragraphs of text (the
+list is not tight). This macro will only be produced, when the
+<code><strong>fancyLists</strong></code> option is disabled. The macro
 receives no arguments.</p>
-<p>The <code>\markdownRendererOlEndTight</code> macro represents the end
+<p>The <code>markdownRendererOlEndTight</code> macro represents the end
 of an ordered list that contains no item with several paragraphs of text
 (the list is tight). This macro will only be produced, when the
-<strong><code>tightLists</code></strong> option is enabled and the
-<strong><code>fancyLists</code></strong> option is disabled. The macro
+<code><strong>tightLists</strong></code> option is enabled and the
+<code><strong>fancyLists</strong></code> option is disabled. The macro
 receives no arguments.</p>
-<p>The <code>\markdownRendererFancyOlEnd</code> macro represents the end
+<p>The <code>markdownRendererFancyOlEnd</code> macro represents the end
 of a fancy ordered list that contains an item with several paragraphs of
 text (the list is not tight). This macro will only be produced, when the
-<strong><code>fancyLists</code></strong> option is enabled. The macro
+<code><strong>fancyLists</strong></code> option is enabled. The macro
 receives no arguments.</p>
-<p>The <code>\markdownRendererFancyOlEndTight</code> macro represents
-the end of a fancy ordered list that contains no item with several
+<p>The <code>markdownRendererFancyOlEndTight</code> macro represents the
+end of a fancy ordered list that contains no item with several
 paragraphs of text (the list is tight). This macro will only be
-produced, when the <strong><code>fancyLists</code></strong> and
-<strong><code>tightLists</code></strong> options are enabled. The macro
+produced, when the <code><strong>fancyLists</strong></code> and
+<code><strong>tightLists</strong></code> options are enabled. The macro
 receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-39">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -11579,18 +11622,18 @@
 </blockquote>
 <h4 data-number="2.3.1.31" id="raw-content-renderers"><span
 class="header-section-number">2.3.1.31</span> Raw Content Renderers</h4>
-<p>The <code>\markdownRendererInputRawInline</code> macro represents an
+<p>The <code>markdownRendererInputRawInline</code> macro represents an
 inline raw span. The macro receives two arguments: the filename of a
 file containing the inline raw span contents and the raw attribute that
 designates the format of the inline raw span. This macro will only be
-produced, when the <strong><code>rawAttribute</code></strong> option is
+produced, when the <code><strong>rawAttribute</strong></code> option is
 enabled.</p>
-<p>The <code>\markdownRendererInputRawBlock</code> macro represents a
-raw block. The macro receives two arguments: the filename of a file
+<p>The <code>markdownRendererInputRawBlock</code> macro represents a raw
+block. The macro receives two arguments: the filename of a file
 containing the raw block and the raw attribute that designates the
 format of the raw block. This macro will only be produced, when the
-<strong><code>rawAttribute</code></strong> and
-<strong><code>fencedCode</code></strong> options are enabled.</p>
+<code><strong>rawAttribute</strong></code> and
+<code><strong>fencedCode</strong></code> options are enabled.</p>
 <h5 class="unnumbered" id="latex-example-89"><span
 class="latex">L<sup>a</sup>T<sub>e</sub>X</span> Example</h5>
 <p>Using a text editor, create a text document named
@@ -11637,13 +11680,13 @@
 </blockquote>
 <h4 data-number="2.3.1.32" id="section-renderers"><span
 class="header-section-number">2.3.1.32</span> Section Renderers</h4>
-<p>The <code>\markdownRendererSectionBegin</code> and
-<code>\markdownRendererSectionEnd</code> macros represent the beginning
+<p>The <code>markdownRendererSectionBegin</code> and
+<code>markdownRendererSectionEnd</code> macros represent the beginning
 and the end of a section based on headings.</p>
 <h4 data-number="2.3.1.33" id="replacement-character-renderers"><span
 class="header-section-number">2.3.1.33</span> Replacement Character
 Renderers</h4>
-<p>The <code>\markdownRendererReplacementCharacter</code> macro
+<p>The <code>markdownRendererReplacementCharacter</code> macro
 represents the U+0000 and U+FFFD Unicode characters. The macro receives
 no arguments.</p>
 <h4 data-number="2.3.1.34" id="special-character-renderers"><span
@@ -11654,27 +11697,27 @@
 character (<code>|</code>) of Con<span
 class="tex">T<sub>e</sub>X</span>t, in the input text:</p>
 <ul>
-<li><code>\markdownRendererAmpersand</code> replaces the ampersand
+<li><code>markdownRendererAmpersand</code> replaces the ampersand
 (<code>&</code>).</li>
-<li><code>\markdownRendererBackslash</code> replaces the backslash
+<li><code>markdownRendererBackslash</code> replaces the backslash
 (<code>\</code>).</li>
-<li><code>\markdownRendererCircumflex</code> replaces the circumflex
+<li><code>markdownRendererCircumflex</code> replaces the circumflex
 (<code>^</code>).</li>
-<li><code>\markdownRendererDollarSign</code> replaces the dollar sign
+<li><code>markdownRendererDollarSign</code> replaces the dollar sign
 (<code>$</code>).</li>
-<li><code>\markdownRendererHash</code> replaces the hash sign
+<li><code>markdownRendererHash</code> replaces the hash sign
 (<code>#</code>).</li>
-<li><code>\markdownRendererLeftBrace</code> replaces the left brace
+<li><code>markdownRendererLeftBrace</code> replaces the left brace
 (<code>{</code>).</li>
-<li><code>\markdownRendererPercentSign</code> replaces the percent sign
+<li><code>markdownRendererPercentSign</code> replaces the percent sign
 (<code>%</code>).</li>
-<li><code>\markdownRendererPipe</code> replaces the pipe character
+<li><code>markdownRendererPipe</code> replaces the pipe character
 (<code>|</code>).</li>
-<li><code>\markdownRendererRightBrace</code> replaces the right brace
+<li><code>markdownRendererRightBrace</code> replaces the right brace
 (<code>}</code>).</li>
-<li><code>\markdownRendererTilde</code> replaces the tilde
+<li><code>markdownRendererTilde</code> replaces the tilde
 (<code>~</code>).</li>
-<li><code>\markdownRendererUnderscore</code> replaces the underscore
+<li><code>markdownRendererUnderscore</code> replaces the underscore
 (<code>_</code>).</li>
 </ul>
 <h5 class="unnumbered" id="plain-tex-example-40">Plain <span
@@ -11756,10 +11799,10 @@
 <h4 data-number="2.3.1.35" id="strike-through-renderer"><span
 class="header-section-number">2.3.1.35</span> Strike-Through
 Renderer</h4>
-<p>The <code>\markdownRendererStrikeThrough</code> macro represents a
+<p>The <code>markdownRendererStrikeThrough</code> macro represents a
 strike-through span of text. The macro receives a single argument that
 corresponds to the striked-out span of text. This macro will only be
-produced, when the <strong><code>strikeThrough</code></strong> option is
+produced, when the <code><strong>strikeThrough</strong></code> option is
 enabled.</p>
 <h5 class="unnumbered" id="plain-tex-example-41">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -11831,10 +11874,10 @@
 </blockquote>
 <h4 data-number="2.3.1.36" id="subscript-renderer"><span
 class="header-section-number">2.3.1.36</span> Subscript Renderer</h4>
-<p>The <code>\markdownRendererSubscript</code> macro represents a
+<p>The <code>markdownRendererSubscript</code> macro represents a
 subscript span of text. The macro receives a single argument that
 corresponds to the subscript span of text. This macro will only be
-produced, when the <strong><code>subscripts</code></strong> option is
+produced, when the <code><strong>subscripts</strong></code> option is
 enabled.</p>
 <h5 class="unnumbered" id="plain-tex-example-42">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -11904,10 +11947,10 @@
 </blockquote>
 <h4 data-number="2.3.1.37" id="superscript-renderer"><span
 class="header-section-number">2.3.1.37</span> Superscript Renderer</h4>
-<p>The <code>\markdownRendererSuperscript</code> macro represents a
+<p>The <code>markdownRendererSuperscript</code> macro represents a
 superscript span of text. The macro receives a single argument that
 corresponds to the superscript span of text. This macro will only be
-produced, when the <strong><code>superscripts</code></strong> option is
+produced, when the <code><strong>superscripts</strong></code> option is
 enabled.</p>
 <h5 class="unnumbered" id="plain-tex-example-43">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -11979,10 +12022,10 @@
 class="header-section-number">2.3.1.38</span> Table Attribute Context
 Renderers</h4>
 <p>The following macros are only produced, when the
-<strong><code>tableCaptions</code></strong> and
-<strong><code>tableAttributes</code></strong> options are enabled.</p>
-<p>The <code>\markdownRendererTableAttributeContextBegin</code> and
-<code>\markdownRendererTableAttributeContextEnd</code> macros represent
+<code><strong>tableCaptions</strong></code> and
+<code><strong>tableAttributes</strong></code> options are enabled.</p>
+<p>The <code>markdownRendererTableAttributeContextBegin</code> and
+<code>markdownRendererTableAttributeContextEnd</code> macros represent
 the beginning and the end of a context in which the attributes of a
 table apply. The macros receive no arguments.</p>
 <h5 class="unnumbered" id="latex-example-94"><span
@@ -12078,9 +12121,9 @@
 </blockquote>
 <h4 data-number="2.3.1.39" id="table-renderer"><span
 class="header-section-number">2.3.1.39</span> Table Renderer</h4>
-<p>The <code>\markdownRendererTable</code> macro represents a table.
-This macro will only be produced, when the
-<strong><code>pipeTables</code></strong> option is enabled. The macro
+<p>The <code>markdownRendererTable</code> macro represents a table. This
+macro will only be produced, when the
+<code><strong>pipeTables</strong></code> option is enabled. The macro
 receives the parameters
 <code>{</code>⟨<em>caption</em>⟩<code>}{</code>⟨<em>number of
 rows</em>⟩<code>}{</code>⟨<em>number of columns</em>⟩<code>}</code>
@@ -12184,14 +12227,14 @@
 <h4 data-number="2.3.1.40" id="tex-math-renderers"><span
 class="header-section-number">2.3.1.40</span> <span
 class="tex">T<sub>e</sub>X</span> Math Renderers</h4>
-<p>The <code>\markdownRendererInlineMath</code> and
-<code>\markdownRendererDisplayMath</code> macros represent inline and
+<p>The <code>markdownRendererInlineMath</code> and
+<code>markdownRendererDisplayMath</code> macros represent inline and
 display <span class="tex">T<sub>e</sub>X</span> math. Both macros
 receive a single argument that corresponds to the <span
 class="tex">T<sub>e</sub>X</span> math content. These macros will only
-be produced, when the <strong><code>texMathDollars</code></strong>,
-<strong><code>texMathSingleBackslash</code></strong>, or
-<strong><code>texMathDoubleBackslash</code></strong> option are
+be produced, when the <code><strong>texMathDollars</strong></code>,
+<code><strong>texMathSingleBackslash</strong></code>, or
+<code><strong>texMathDoubleBackslash</strong></code> option are
 enabled.</p>
 <h5 class="unnumbered" id="plain-tex-example-44">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -12277,7 +12320,7 @@
 <h4 data-number="2.3.1.41" id="thematic-break-renderer"><span
 class="header-section-number">2.3.1.41</span> Thematic Break
 Renderer</h4>
-<p>The <code>\markdownRendererThematicBreak</code> macro represents a
+<p>The <code>markdownRendererThematicBreak</code> macro represents a
 thematic break. The macro receives no arguments.</p>
 <h5 class="unnumbered" id="plain-tex-example-45">Plain <span
 class="tex">T<sub>e</sub>X</span> Example</h5>
@@ -12363,11 +12406,11 @@
 </blockquote>
 <h4 data-number="2.3.1.42" id="tickbox-renderers"><span
 class="header-section-number">2.3.1.42</span> Tickbox Renderers</h4>
-<p>The macros named <code>\markdownRendererTickedBox</code>,
-<code>\markdownRendererHalfTickedBox</code>, and
-<code>\markdownRendererUntickedBox</code> represent ticked and unticked
+<p>The macros named <code>markdownRendererTickedBox</code>,
+<code>markdownRendererHalfTickedBox</code>, and
+<code>markdownRendererUntickedBox</code> represent ticked and unticked
 boxes, respectively. These macros will either be produced, when the
-<strong><code>taskLists</code></strong> option is enabled, or when the
+<code><strong>taskLists</strong></code> option is enabled, or when the
 Ballot Box with X (☒, U+2612), Hourglass (⌛, U+231B) or Ballot Box (☐,
 U+2610) Unicode characters are encountered in the markdown input,
 respectively.</p>
@@ -12427,62 +12470,91 @@
 <li>Yes I can!</li>
 </ul>
 </blockquote>
-<h4 data-number="2.3.1.43" id="yamlmetadatarenderers"><span
-class="header-section-number">2.3.1.43</span> YAML Metadata
+<h4 data-number="2.3.1.43" id="warning-and-error-renderers"><span
+class="header-section-number">2.3.1.43</span> Warning and Error
 Renderers</h4>
-<p>The <code>\markdownRendererJekyllDataBegin</code> macro represents
-the beginning of a <abbr>yaml</abbr> document. This macro will only be
-produced when the <strong><code>jekyllData</code></strong> option is
+<p>The <code>markdownRendererWarning</code> and
+<code>markdownRendererError</code> macros represent warnings and errors
+produced by the markdown parser. Both macros receive a single parameter
+with the text of the warning or error.</p>
+<h4 data-number="2.3.1.44" id="yamlmetadatarenderers"><span
+class="header-section-number">2.3.1.44</span> YAML Metadata
+Renderers</h4>
+<p>The <code>markdownRendererJekyllDataBegin</code> macro represents the
+beginning of a <abbr>yaml</abbr> document. This macro will only be
+produced when the <code><strong>jekyllData</strong></code> option is
 enabled. The macro receives no arguments.</p>
-<p>The <code>\markdownRendererJekyllDataEnd</code> macro represents the
+<p>The <code>markdownRendererJekyllDataEnd</code> macro represents the
 end of a <abbr>yaml</abbr> document. This macro will only be produced
-when the <strong><code>jekyllData</code></strong> option is enabled. The
+when the <code><strong>jekyllData</strong></code> option is enabled. The
 macro receives no arguments.</p>
-<p>The <code>\markdownRendererJekyllDataMappingBegin</code> macro
+<p>The <code>markdownRendererJekyllDataMappingBegin</code> macro
 represents the beginning of a mapping in a <abbr>yaml</abbr> document.
 This macro will only be produced when the
-<strong><code>jekyllData</code></strong> option is enabled. The macro
+<code><strong>jekyllData</strong></code> option is enabled. The macro
 receives two arguments: the scalar key in the parent structure, cast to
 a string following <abbr>yaml</abbr> serialization rules, and the number
 of items in the mapping.</p>
-<p>The <code>\markdownRendererJekyllDataMappingEnd</code> macro
+<p>The <code>markdownRendererJekyllDataMappingEnd</code> macro
 represents the end of a mapping in a <abbr>yaml</abbr> document. This
 macro will only be produced when the
-<strong><code>jekyllData</code></strong> option is enabled. The macro
+<code><strong>jekyllData</strong></code> option is enabled. The macro
 receives no arguments.</p>
-<p>The <code>\markdownRendererJekyllDataSequenceBegin</code> macro
+<p>The <code>markdownRendererJekyllDataSequenceBegin</code> macro
 represents the beginning of a sequence in a <abbr>yaml</abbr> document.
 This macro will only be produced when the
-<strong><code>jekyllData</code></strong> option is enabled. The macro
+<code><strong>jekyllData</strong></code> option is enabled. The macro
 receives two arguments: the scalar key in the parent structure, cast to
 a string following <abbr>yaml</abbr> serialization rules, and the number
 of items in the sequence.</p>
-<p>The <code>\markdownRendererJekyllDataSequenceEnd</code> macro
+<p>The <code>markdownRendererJekyllDataSequenceEnd</code> macro
 represents the end of a sequence in a <abbr>yaml</abbr> document. This
 macro will only be produced when the
-<strong><code>jekyllData</code></strong> option is enabled. The macro
+<code><strong>jekyllData</strong></code> option is enabled. The macro
 receives no arguments.</p>
-<p>The <code>\markdownRendererJekyllDataBoolean</code> macro represents
-a boolean scalar value in a <abbr>yaml</abbr> document. This macro will
-only be produced when the <strong><code>jekyllData</code></strong>
+<p>The <code>markdownRendererJekyllDataBoolean</code> macro represents a
+boolean scalar value in a <abbr>yaml</abbr> document. This macro will
+only be produced when the <code><strong>jekyllData</strong></code>
 option is enabled. The macro receives two arguments: the scalar key in
 the parent structure, and the scalar value, both cast to a string
 following <abbr>yaml</abbr> serialization rules.</p>
-<p>The <code>\markdownRendererJekyllDataNumber</code> macro represents a
+<p>The <code>markdownRendererJekyllDataNumber</code> macro represents a
 numeric scalar value in a <abbr>yaml</abbr> document. This macro will
-only be produced when the <strong><code>jekyllData</code></strong>
+only be produced when the <code><strong>jekyllData</strong></code>
 option is enabled. The macro receives two arguments: the scalar key in
 the parent structure, and the scalar value, both cast to a string
 following <abbr>yaml</abbr> serialization rules.</p>
-<p>The <code>\markdownRendererJekyllDataString</code> macro represents a
-string scalar value in a <abbr>yaml</abbr> document. This macro will
-only be produced when the <strong><code>jekyllData</code></strong>
-option is enabled. The macro receives two arguments: the scalar key in
-the parent structure, cast to a string following <abbr>yaml</abbr>
-serialization rules, and the scalar value.</p>
-<p>The <code>\markdownRendererJekyllDataEmpty</code> macro represents an
+<p>The <code>markdownRendererJekyllDataTypographicString</code> and
+<code>markdownRendererJekyllDataProgrammaticString</code> macros
+represent string scalar values in a <abbr>yaml</abbr> document. This
+macro will only be produced when the
+<code><strong>jekyllData</strong></code> option is enabled. The macro
+receives two arguments: the scalar key in the parent structure, cast to
+a string following <abbr>yaml</abbr> serialization rules, and the scalar
+value.</p>
+<p>For each string scalar value, both macros are produced. Whereas
+<code>markdownRendererJekyllDataTypographicString</code> receives the
+scalar value after all markdown markup and special <span
+class="tex">T<sub>e</sub>X</span> characters in the string have been
+replaced by <span class="tex">T<sub>e</sub>X</span> macros,
+<code>markdownRendererJekyllDataProgrammaticString</code> receives the
+raw scalar value. Therefore, whereas the
+<code>markdownRendererJekyllDataTypographicString</code> macro is more
+appropriate for texts that are supposed to be typeset with <span
+class="tex">T<sub>e</sub>X</span>, such as document titles, author
+names, or exam questions, the
+<code>markdownRendererJekyllDataProgrammaticString</code> macro is more
+appropriate for identifiers and other programmatic text that won’t be
+typeset by <span class="tex">T<sub>e</sub>X</span>.</p>
+<p>Before Markdown 3.7.0, the
+<code>markdownRendererJekyllDataTypographicString</code> macro was named
+<code>markdownRendererJekyllDataString</code> and the
+<code>markdownRendererJekyllDataProgrammaticString</code> macro was not
+produced. The <code>markdownRendererJekyllDataString</code> has been
+deprecated and will be removed in Markdown 4.0.0.</p>
+<p>The <code>markdownRendererJekyllDataEmpty</code> macro represents an
 empty scalar value in a <abbr>yaml</abbr> document. This macro will only
-be produced when the <strong><code>jekyllData</code></strong> option is
+be produced when the <code><strong>jekyllData</strong></code> option is
 enabled. The macro receives one argument: the scalar key in the parent
 structure, cast to a string following <abbr>yaml</abbr> serialization
 rules.</p>
@@ -12493,7 +12565,7 @@
 <div class="sourceCode" id="cb664"><pre
 class="sourceCode tex"><code class="sourceCode latex"><span id="cb664-1"><a href="#cb664-1" aria-hidden="true" tabindex="-1"></a><span class="fu">\input</span> markdown</span>
 <span id="cb664-2"><a href="#cb664-2" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownOptionJekyllData</span>{true}</span>
-<span id="cb664-3"><a href="#cb664-3" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownRendererJekyllDataString</span>#1#2{<span class="fu">\gdef\name</span>{#2}}</span>
+<span id="cb664-3"><a href="#cb664-3" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownRendererJekyllDataTypographicString</span>#1#2{<span class="fu">\gdef\name</span>{#2}}</span>
 <span id="cb664-4"><a href="#cb664-4" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownRendererJekyllDataNumber</span>#1#2{<span class="fu">\gdef\age</span>{#2}}</span>
 <span id="cb664-5"><a href="#cb664-5" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownRendererJekyllDataEnd</span>{<span class="co">%</span></span>
 <span id="cb664-6"><a href="#cb664-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">\name</span>{} is <span class="fu">\age</span>{} years old.}</span>
@@ -12521,7 +12593,7 @@
 <span id="cb666-2"><a href="#cb666-2" aria-hidden="true" tabindex="-1"></a><span class="bu">\usepackage</span>[jekyllData]{<span class="ex">markdown</span>}</span>
 <span id="cb666-3"><a href="#cb666-3" aria-hidden="true" tabindex="-1"></a><span class="fu">\markdownSetup</span>{</span>
 <span id="cb666-4"><a href="#cb666-4" aria-hidden="true" tabindex="-1"></a>  renderers = {</span>
-<span id="cb666-5"><a href="#cb666-5" aria-hidden="true" tabindex="-1"></a>    jekyllDataString = {<span class="fu">\gdef\name</span>{#2}},</span>
+<span id="cb666-5"><a href="#cb666-5" aria-hidden="true" tabindex="-1"></a>    jekyllDataTypographicString = {<span class="fu">\gdef\name</span>{#2}},</span>
 <span id="cb666-6"><a href="#cb666-6" aria-hidden="true" tabindex="-1"></a>    jekyllDataNumber = {<span class="fu">\gdef\age</span>{#2}},</span>
 <span id="cb666-7"><a href="#cb666-7" aria-hidden="true" tabindex="-1"></a>    jekyllDataEnd = {<span class="fu">\name</span>{} is <span class="fu">\age</span>{} years old.},</span>
 <span id="cb666-8"><a href="#cb666-8" aria-hidden="true" tabindex="-1"></a>  }</span>
@@ -12551,7 +12623,7 @@
 <div class="sourceCode" id="cb668"><pre
 class="sourceCode tex"><code class="sourceCode latex"><span id="cb668-1"><a href="#cb668-1" aria-hidden="true" tabindex="-1"></a><span class="fu">\usemodule</span>[t][markdown]</span>
 <span id="cb668-2"><a href="#cb668-2" aria-hidden="true" tabindex="-1"></a><span class="fu">\setupmarkdown</span>[jekyllData = yes]</span>
-<span id="cb668-3"><a href="#cb668-3" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownRendererJekyllDataString</span>#1#2{<span class="fu">\gdef\name</span>{#2}}</span>
+<span id="cb668-3"><a href="#cb668-3" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownRendererJekyllDataTypographicString</span>#1#2{<span class="fu">\gdef\name</span>{#2}}</span>
 <span id="cb668-4"><a href="#cb668-4" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownRendererJekyllDataNumber</span>#1#2{<span class="fu">\gdef\age</span>{#2}}</span>
 <span id="cb668-5"><a href="#cb668-5" aria-hidden="true" tabindex="-1"></a><span class="fu">\def\markdownRendererJekyllDataEnd</span>{<span class="co">%</span></span>
 <span id="cb668-6"><a href="#cb668-6" aria-hidden="true" tabindex="-1"></a>  <span class="fu">\name</span>{} is <span class="fu">\age</span>{} years old.}</span>

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

Modified: trunk/Master/texmf-dist/doc/optex/markdown/examples/optex.tex
===================================================================
--- trunk/Master/texmf-dist/doc/optex/markdown/examples/optex.tex	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/doc/optex/markdown/examples/optex.tex	2024-08-30 21:14:17 UTC (rev 72146)
@@ -226,7 +226,8 @@
 \_let  \markdownRendererJekyllDataSequenceEnd \_relax
 \_def  \markdownRendererJekyllDataBoolean #1#2{}
 \_def  \markdownRendererJekyllDataNumber #1#2{}
-\_def  \markdownRendererJekyllDataString #1#2{}
+\_def  \markdownRendererJekyllDataProgrammaticString #1#2{}
+\_def  \markdownRendererJekyllDataTypographicString #1#2{}
 \_def  \markdownRendererJekyllDataEmpty #1{}
 
 % Load the Markdown module and set TeX macros for the Markdown module

Modified: trunk/Master/texmf-dist/scripts/markdown/markdown-cli.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/markdown/markdown-cli.lua	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/scripts/markdown/markdown-cli.lua	2024-08-30 21:14:17 UTC (rev 72146)
@@ -9,12 +9,12 @@
 -- permit persons to whom the Software is furnished to do so, subject to
 -- the following conditions:
 --
--- The above copyright notice and this permission notice shall be included
--- in all copies or substantial portions of the Software.
+-- The above copyright notice and this permission notice shall be
+-- included in all copies or substantial portions of the Software.
 --
 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
--- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+-- MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT.
 -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 -- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
@@ -58,9 +58,11 @@
 -- those in the standard .ins files.
 --
 local metadata = {
-    version   = "3.6.2-0-g6c30af7e",
-    comment   = "A module for the conversion from markdown to plain TeX",
-    author    = "John MacFarlane, Hans Hagen, Vít Starý Novotný, Andrej Genčur",
+    version   = "3.7.0-0-g98dece19",
+    comment   = "A module for the conversion from markdown "
+             .. "to plain TeX",
+    author    = "John MacFarlane, Hans Hagen, Vít Starý Novotný, "
+             .. "Andrej Genčur",
     copyright = {"2009-2016 John MacFarlane, Hans Hagen",
                  "2016-2024 Vít Starý Novotný, Andrej Genčur"},
     license   = "LPPL 1.3c"
@@ -67,7 +69,7 @@
 }
 
 local defaultOptions = {}
-defaultOptions.eagerCache = false
+defaultOptions.eagerCache = true
 defaultOptions.singletonCache = true
 defaultOptions.unicodeNormalization = true
 defaultOptions.unicodeNormalizationForm = "nfc"
@@ -90,6 +92,7 @@
 defaultOptions.contentLevel = "block"
 defaultOptions.debugExtensions = false
 defaultOptions.definitionLists = false
+defaultOptions.ensureJekyllData = false
 defaultOptions.expectJekyllData = false
 defaultOptions.extensions = {}
 defaultOptions.fancyLists = false
@@ -153,7 +156,8 @@
 License: ]] .. metadata.license
 
 local function warn(s)
-  io.stderr:write("Warning: " .. s .. "\n") end
+  io.stderr:write("Warning: " .. s .. "\n")
+end
 
 local function error(s)
   io.stderr:write("Error: " .. s .. "\n")
@@ -211,8 +215,8 @@
           if default_type == "nil" then
             warn('Option "' .. key .. '" not recognized.')
           else
-            warn('Option "' .. key .. '" type not recognized, please file ' ..
-                 'a report to the package maintainer.')
+            warn('Option "' .. key .. '" type not recognized, ' ..
+                 'please file a report to the package maintainer.')
           end
           warn('Parsing the ' .. 'value "' .. value ..'" of option "' ..
                key .. '" as a string.')

Modified: trunk/Master/texmf-dist/source/generic/markdown/docstrip.cfg
===================================================================
--- trunk/Master/texmf-dist/source/generic/markdown/docstrip.cfg	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/source/generic/markdown/docstrip.cfg	2024-08-30 21:14:17 UTC (rev 72146)
@@ -11,12 +11,12 @@
 -- permit persons to whom the Software is furnished to do so, subject to^^J%
 -- the following conditions:^^J%
 --^^J%
--- The above copyright notice and this permission notice shall be included^^J%
--- in all copies or substantial portions of the Software.^^J%
+-- The above copyright notice and this permission notice shall be^^J%
+-- included in all copies or substantial portions of the Software.^^J%
 --^^J%
 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,^^J%
 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF^^J%
--- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.^^J%
+-- MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT.^^J%
 -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY^^J%
 -- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,^^J%
 -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE^^J%

Modified: trunk/Master/texmf-dist/source/generic/markdown/markdown.dtx
===================================================================
--- trunk/Master/texmf-dist/source/generic/markdown/markdown.dtx	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/source/generic/markdown/markdown.dtx	2024-08-30 21:14:17 UTC (rev 72146)
@@ -814,7 +814,9 @@
   },
   jekyllDataRenderers = {
     /authors/* = {%
-      \expandafter\gdef\expandafter\ltd at title@author\expandafter{\ltd at title@author, #1}%
+      \expandafter\gdef
+        \expandafter\ltd at title@author
+          \expandafter{\ltd at title@author, #1}%
     },
     title = {%
       \gdef\ltd at title@title{#1}%
@@ -923,13 +925,15 @@
             (A Markdown Interpreter for TeX)
 
 %</manual>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 local metadata = {
     version   = "(((VERSION)))",
-    comment   = "A module for the conversion from markdown to plain TeX",
-    author    = "John MacFarlane, Hans Hagen, Vít Starý Novotný, Andrej Genčur",
+    comment   = "A module for the conversion from markdown "
+             .. "to plain TeX",
+    author    = "John MacFarlane, Hans Hagen, Vít Starý Novotný, "
+             .. "Andrej Genčur",
     copyright = {"2009-2016 John MacFarlane, Hans Hagen",
                  "2016-2024 Vít Starý Novotný, Andrej Genčur"},
     license   = "LPPL 1.3c"
@@ -937,7 +941,7 @@
 
 %    \end{macrocode}
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*lua>
 % \fi
 %  \begin{macrocode}
@@ -989,7 +993,7 @@
 
 Either of the two abovelisted approaches should produce the following files:
 
-* `markdown.lua`: The Lua module
+* `markdown.lua` and `markdown-parser.lua`: The Lua module
 * `libraries/markdown-tinyyaml.lua`: An external library for reading \acro{yaml}
 * `markdown-cli.lua`: The Lua command-line interface
 * `markdown.tex`: The plain \TeX{} macro package
@@ -1010,6 +1014,7 @@
 placed:
 
 * `⟨TEXMF⟩/tex/luatex/markdown/markdown.lua`
+* `⟨TEXMF⟩/tex/luatex/markdown/markdown-parser.lua`
 * `⟨TEXMF⟩/tex/luatex/markdown/markdown-tinyyaml.lua`
 * `⟨TEXMF⟩/scripts/markdown/markdown-cli.lua`
 * `⟨TEXMF⟩/tex/generic/markdown/markdown.tex`
@@ -1036,6 +1041,7 @@
 This is where the individual files should be placed:
 
 * `./markdown.lua`
+* `./markdown-parser.lua`
 * `./markdown-tinyyaml.lua`
 * `./markdown-cli.lua`
 * `./markdown/markdown.tex`
@@ -1104,9 +1110,17 @@
 %     Live${}\geq{}2008$).
 %
 % \end{markdown}
+% \iffalse
+%</lua>
+%<*lua,lua-loader>
+% \fi
 %  \begin{macrocode}
 local md5 = require("md5");
 %    \end{macrocode}
+% \iffalse
+%</lua,lua-loader>
+%<*lua>
+% \fi
 % \begin{markdown}
 %
 % \pkg{Kpathsea}
@@ -1181,7 +1195,7 @@
 %<*depends>
 % \fi
 %  \begin{macrocode}
-# hard lua-tinyyaml  # TODO: Uncomment after TeX Live 2022 has been deprecated.
+# hard lua-tinyyaml  # TODO: Uncomment after TeX Live 2022 deprecation.
 %    \end{macrocode}
 % \iffalse
 %</depends>
@@ -1871,7 +1885,7 @@
              (Lua library for conversion between markup formats)
 
 %</manual-interfaces>
-%<*lua>
+%<*lua,lua-loader>
 % \fi
 % \begin{markdown}
 %
@@ -1886,6 +1900,10 @@
 %  \begin{macrocode}
 local M = {metadata = metadata}
 %    \end{macrocode}
+% \iffalse
+% \fi
+%</lua,lua-loader>
+%<*lua>
 % \par
 % \begin{markdown}
 %
@@ -2162,7 +2180,7 @@
 package.
 
 %</manual-options>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 local defaultOptions = {}
@@ -2171,7 +2189,7 @@
 % \markdownSetup{snippet=lua-options}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 ### Lua
@@ -2205,7 +2223,9 @@
 \prop_new:N \g_@@_default_lua_options_prop
 \seq_new:N \g_@@_option_layers_seq
 \tl_const:Nn \c_@@_option_layer_lua_tl { lua }
-\seq_gput_right:NV \g_@@_option_layers_seq \c_@@_option_layer_lua_tl
+\seq_gput_right:NV
+  \g_@@_option_layers_seq
+  \c_@@_option_layer_lua_tl
 \cs_new:Nn
   \@@_add_lua_option:nnn
   {
@@ -2282,21 +2302,50 @@
 \cs_generate_variant:Nn
   \msg_error:nnnn
   { nnnV }
-\seq_new:N \g_@@_option_types_seq
-\tl_const:Nn \c_@@_option_type_clist_tl { clist }
-\seq_gput_right:NV \g_@@_option_types_seq \c_@@_option_type_clist_tl
-\tl_const:Nn \c_@@_option_type_counter_tl { counter }
-\seq_gput_right:NV \g_@@_option_types_seq \c_@@_option_type_counter_tl
-\tl_const:Nn \c_@@_option_type_boolean_tl { boolean }
-\seq_gput_right:NV \g_@@_option_types_seq \c_@@_option_type_boolean_tl
-\tl_const:Nn \c_@@_option_type_number_tl  { number  }
-\seq_gput_right:NV \g_@@_option_types_seq \c_@@_option_type_number_tl
-\tl_const:Nn \c_@@_option_type_path_tl    { path    }
-\seq_gput_right:NV \g_@@_option_types_seq \c_@@_option_type_path_tl
-\tl_const:Nn \c_@@_option_type_slice_tl   { slice   }
-\seq_gput_right:NV \g_@@_option_types_seq \c_@@_option_type_slice_tl
-\tl_const:Nn \c_@@_option_type_string_tl  { string  }
-\seq_gput_right:NV \g_@@_option_types_seq \c_@@_option_type_string_tl
+\seq_new:N
+  \g_@@_option_types_seq
+\tl_const:Nn
+  \c_@@_option_type_clist_tl
+  { clist }
+\seq_gput_right:NV
+  \g_@@_option_types_seq
+  \c_@@_option_type_clist_tl
+\tl_const:Nn
+  \c_@@_option_type_counter_tl
+  { counter }
+\seq_gput_right:NV
+  \g_@@_option_types_seq
+  \c_@@_option_type_counter_tl
+\tl_const:Nn
+  \c_@@_option_type_boolean_tl
+  { boolean }
+\seq_gput_right:NV
+  \g_@@_option_types_seq
+  \c_@@_option_type_boolean_tl
+\tl_const:Nn
+  \c_@@_option_type_number_tl
+  { number }
+\seq_gput_right:NV
+  \g_@@_option_types_seq
+  \c_@@_option_type_number_tl
+\tl_const:Nn
+  \c_@@_option_type_path_tl
+  { path }
+\seq_gput_right:NV
+  \g_@@_option_types_seq
+  \c_@@_option_type_path_tl
+\tl_const:Nn
+  \c_@@_option_type_slice_tl
+  { slice }
+\seq_gput_right:NV
+  \g_@@_option_types_seq
+  \c_@@_option_type_slice_tl
+\tl_const:Nn
+  \c_@@_option_type_string_tl
+  { string }
+\seq_gput_right:NV
+  \g_@@_option_types_seq
+  \c_@@_option_type_string_tl
 \cs_new:Nn
   \@@_get_option_type:nN
   {
@@ -2518,20 +2567,23 @@
 
 #### Option `eagerCache`
 
-`eagerCache` (default value: `false`)
+`eagerCache` (default value: `true`)
 
 % \fi
 % \begin{markdown}
 %
-% \Optitem[false]{eagerCache}{\opt{true}, \opt{false}}
+% \Optitem[true]{eagerCache}{\opt{true}, \opt{false}}
 %
 :    true
 
      :  Converted markdown documents will be cached in \Opt{cacheDir}. This can be
         useful for post-processing the converted documents and for recovering
-        historical versions of the documents from the cache.  However, it also
-        produces a large number of auxiliary files on the disk and obscures the
-        output of the Lua command-line interface when it is used for plumbing.
+        historical versions of the documents from the cache. Furthermore, it can
+        also significantly improve the processing speed for documents that require
+        multiple compilation runs, since each markdown document is only converted once.
+        However, it also produces a large number of auxiliary files on the disk
+        and obscures the output of the Lua command-line interface when it is
+        used for plumbing.
 
         This behavior will always be used if the \Opt{finalizeCache} option is
         enabled.
@@ -2540,7 +2592,11 @@
 
      :  Converted markdown documents will not be cached. This decreases the number
         of auxiliary files that we produce and makes it easier to use the Lua
-        command-line interface for plumbing.
+        command-line interface for plumbing. However, it makes it impossible to
+        post-process the converted documents and recover historical versions of
+        the documents from the cache. Furthermore, it can significantly reduce
+        the processing speed for documents that require multiple compilation
+        runs, since each markdown document is converted multiple times needlessly.
 
         This behavior will only be used when the \Opt{finalizeCache} option is
         disabled.
@@ -2602,18 +2658,18 @@
 \@@_add_lua_option:nnn
   { eagerCache }
   { boolean }
-  { false }
+  { true }
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
-defaultOptions.eagerCache = false
+defaultOptions.eagerCache = true
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `singletonCache`
@@ -2641,12 +2697,11 @@
      :  Every call to the function \luamref{new}`(options)` will produce a new
         conversion function that will not be cached. This is slower than
         caching conversion functions and may expose bugs related to memory
-        leaks in the creation of conversion functions, see also issue
-        [#226][issue-226].
+        leaks in the creation of conversion functions, see also [#226 (comment)][226-comment].
 
         This was the default behavior until version 3.0.0 of the Markdown package.
 
- [issue-226]: https://github.com/witiko/markdown/pull/226#issuecomment-1599641634
+ [226-comment]: https://github.com/witiko/markdown/pull/226#issuecomment-1599641634
 
 % \end{markdown}
 % \iffalse
@@ -2695,13 +2750,13 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.singletonCache = true
 %    \end{macrocode}
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*lua>
 % \fi
 %  \begin{macrocode}
@@ -2750,7 +2805,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.unicodeNormalization = true
@@ -2757,7 +2812,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `unicodeNormalizationForm`
@@ -2806,7 +2861,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.unicodeNormalizationForm = "nfc"
@@ -2813,7 +2868,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 % \fi
 % \begin{markdown}
@@ -2982,7 +3037,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.cacheDir = "."
@@ -2989,7 +3044,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `contentBlocksLanguageMap`
@@ -3146,7 +3201,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.contentBlocksLanguageMap = "markdown-languages.json"
@@ -3153,7 +3208,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `debugExtensionsFileName`
@@ -3195,7 +3250,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.debugExtensionsFileName = "debug-extensions.json"
@@ -3202,7 +3257,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `frozenCacheFileName`
@@ -3437,7 +3492,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.frozenCacheFileName = "frozenCache.tex"
@@ -3444,7 +3499,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 % \fi
 % \begin{markdown}
@@ -3494,7 +3549,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.autoIdentifiers = false
@@ -3501,7 +3556,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `blankBeforeBlockquote`
@@ -3718,7 +3773,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.blankBeforeBlockquote = false
@@ -3725,7 +3780,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `blankBeforeCodeFence`
@@ -3975,7 +4030,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.blankBeforeCodeFence = false
@@ -3982,7 +4037,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `blankBeforeDivFence`
@@ -4058,7 +4113,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.blankBeforeDivFence = false
@@ -4065,7 +4120,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `blankBeforeHeading`
@@ -4298,7 +4353,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.blankBeforeHeading = false
@@ -4305,7 +4360,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `blankBeforeList`
@@ -4524,7 +4579,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.blankBeforeList = false
@@ -4531,7 +4586,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `bracketedSpans`
@@ -4620,7 +4675,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.bracketedSpans = false
@@ -4627,7 +4682,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `breakableBlockquotes`
@@ -4854,7 +4909,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.breakableBlockquotes = true
@@ -4861,7 +4916,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `citationNbsps`
@@ -4948,7 +5003,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.citationNbsps = true
@@ -4955,7 +5010,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `citations`
@@ -5051,7 +5106,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.citations = false
@@ -5058,7 +5113,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `codeSpans`
@@ -5277,7 +5332,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.codeSpans = true
@@ -5284,7 +5339,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `contentBlocks`
@@ -5451,7 +5506,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.contentBlocks = false
@@ -5458,7 +5513,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `contentLevel`
@@ -5588,7 +5643,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.contentLevel = "block"
@@ -5595,7 +5650,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `debugExtensions`
@@ -5743,7 +5798,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.debugExtensions = false
@@ -5750,7 +5805,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `definitionLists`
@@ -5882,7 +5937,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.definitionLists = false
@@ -5889,9 +5944,55 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
+#### Option `ensureJekyllData`
+
+`ensureJekyllData` (default value: `false`)
+
+% \fi
+% \begin{markdown}
+%
+% \Optitem[false]{ensureJekyllData}{\opt{true}, \opt{false}}
+%
+:    false
+
+     :  When the \Opt{jekyllData} and \Opt{expectJekyllData} options are
+        enabled, then a markdown document may begin directly with \acro{yaml}
+        metadata and may contain nothing but \acro{yaml} metadata. Otherwise,
+        the markdown document is processed as markdown text.
+
+:    true
+
+     :  When the \Opt{jekyllData} and \Opt{expectJekyllData} options are
+        enabled, then a markdown document must begin directly with \acro{yaml}
+        metadata and must contain nothing but \acro{yaml} metadata. Otherwise,
+        an error is produced.
+
+% \end{markdown}
+% \iffalse
+%</manual-options>
+%<*tex>
+% \fi
+%  \begin{macrocode}
+\@@_add_lua_option:nnn
+  { ensureJekyllData }
+  { boolean }
+  { false }
+%    \end{macrocode}
+% \iffalse
+%</tex>
+%<*lua,lua-cli,lua-loader>
+% \fi
+%  \begin{macrocode}
+defaultOptions.ensureJekyllData = false
+%    \end{macrocode}
+% \par
+% \iffalse
+%</lua,lua-cli,lua-loader>
+%<*manual-options>
+
 #### Option `expectJekyllData`
 
 `expectJekyllData` (default value: `false`)
@@ -6006,7 +6107,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.expectJekyllData = false
@@ -6013,7 +6114,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `extensions`
@@ -6157,7 +6258,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.extensions = {}
@@ -6164,7 +6265,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `fancyLists`
@@ -6259,7 +6360,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.fancyLists = false
@@ -6266,7 +6367,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `fencedCode`
@@ -6426,7 +6527,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.fencedCode = true
@@ -6433,7 +6534,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `fencedCodeAttributes`
@@ -6526,7 +6627,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.fencedCodeAttributes = false
@@ -6533,7 +6634,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `fencedDivs` {#fenced-divs}
@@ -6605,7 +6706,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.fencedDivs = false
@@ -6612,7 +6713,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `finalizeCache`
@@ -6769,7 +6870,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.finalizeCache = false
@@ -6776,7 +6877,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `frozenCacheCounter`
@@ -6838,7 +6939,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.frozenCacheCounter = 0
@@ -6845,7 +6946,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `gfmAutoIdentifiers`
@@ -6887,7 +6988,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.gfmAutoIdentifiers = false
@@ -6894,7 +6995,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `hashEnumerators`
@@ -7018,7 +7119,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.hashEnumerators = false
@@ -7025,7 +7126,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `headerAttributes` {#header-attributes}
@@ -7067,7 +7168,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.headerAttributes = false
@@ -7074,7 +7175,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `html`
@@ -7339,7 +7440,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.html = true
@@ -7346,7 +7447,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `hybrid`
@@ -7565,7 +7666,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.hybrid = false
@@ -7572,7 +7673,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `inlineCodeAttributes`
@@ -7661,7 +7762,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.inlineCodeAttributes = false
@@ -7668,7 +7769,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `inlineNotes`
@@ -7762,7 +7863,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.inlineNotes = false
@@ -7769,7 +7870,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `jekyllData`
@@ -7929,7 +8030,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.jekyllData = false
@@ -7936,7 +8037,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `linkAttributes`
@@ -8026,7 +8127,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.linkAttributes = false
@@ -8033,7 +8134,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `lineBlocks`
@@ -8161,7 +8262,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.lineBlocks = false
@@ -8168,7 +8269,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `mark`
@@ -8232,7 +8333,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.mark = false
@@ -8239,7 +8340,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `notes`
@@ -8395,7 +8496,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.notes = false
@@ -8402,7 +8503,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `pipeTables` {#pipe-tables}
@@ -8504,7 +8605,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.pipeTables = false
@@ -8511,7 +8612,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `preserveTabs`
@@ -8544,7 +8645,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.preserveTabs = true
@@ -8551,7 +8652,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `rawAttribute`
@@ -8639,7 +8740,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.rawAttribute = false
@@ -8646,7 +8747,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `relativeReferences`
@@ -8728,7 +8829,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.relativeReferences = false
@@ -8735,7 +8836,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `shiftHeadings`
@@ -8868,7 +8969,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.shiftHeadings = 0
@@ -8875,7 +8976,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `slice`
@@ -9086,7 +9187,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.slice = "^ $"
@@ -9093,7 +9194,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `smartEllipses`
@@ -9301,7 +9402,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.smartEllipses = false
@@ -9308,7 +9409,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `startNumber`
@@ -9437,7 +9538,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.startNumber = true
@@ -9444,7 +9545,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `strikeThrough`
@@ -9559,7 +9660,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.strikeThrough = false
@@ -9566,7 +9667,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `stripIndent`
@@ -9672,7 +9773,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.stripIndent = false
@@ -9679,7 +9780,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `subscripts`
@@ -9764,7 +9865,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.subscripts = false
@@ -9771,7 +9872,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `superscripts`
@@ -9856,7 +9957,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.superscripts = false
@@ -9863,7 +9964,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `tableAttributes`
@@ -9981,7 +10082,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.tableAttributes = false
@@ -9988,7 +10089,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `tableCaptions` {#table-captions}
@@ -10111,7 +10212,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.tableCaptions = false
@@ -10118,7 +10219,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `taskLists`
@@ -10224,7 +10325,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.taskLists = false
@@ -10231,7 +10332,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `texComments`
@@ -10340,7 +10441,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.texComments = false
@@ -10347,7 +10448,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `texMathDollars`
@@ -10551,7 +10652,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.texMathDollars = false
@@ -10558,7 +10659,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `texMathDoubleBackslash`
@@ -10762,7 +10863,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.texMathDoubleBackslash = false
@@ -10769,7 +10870,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `texMathSingleBackslash`
@@ -10973,7 +11074,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.texMathSingleBackslash = false
@@ -10980,7 +11081,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `tightLists`
@@ -11094,7 +11195,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.tightLists = true
@@ -11101,7 +11202,7 @@
 %    \end{macrocode}
 % \par
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*manual-options>
 
 #### Option `underscores`
@@ -11237,7 +11338,7 @@
 %    \end{macrocode}
 % \iffalse
 %</tex>
-%<*lua,lua-cli>
+%<*lua,lua-cli,lua-loader>
 % \fi
 %  \begin{macrocode}
 defaultOptions.underscores = true
@@ -11244,7 +11345,7 @@
 %    \end{macrocode}
 % \endgroup
 % \iffalse
-%</lua,lua-cli>
+%</lua,lua-cli,lua-loader>
 %<*lua-cli>
 % \fi
 % \par
@@ -11330,7 +11431,8 @@
 License: ]] .. metadata.license
 
 local function warn(s)
-  io.stderr:write("Warning: " .. s .. "\n") end
+  io.stderr:write("Warning: " .. s .. "\n")
+end
 
 local function error(s)
   io.stderr:write("Error: " .. s .. "\n")
@@ -11421,8 +11523,8 @@
           if default_type == "nil" then
             warn('Option "' .. key .. '" not recognized.')
           else
-            warn('Option "' .. key .. '" type not recognized, please file ' ..
-                 'a report to the package maintainer.')
+            warn('Option "' .. key .. '" type not recognized, ' ..
+                 'please file a report to the package maintainer.')
           end
           warn('Parsing the ' .. 'value "' .. value ..'" of option "' ..
                key .. '" as a string.')
@@ -11757,7 +11859,9 @@
 %  \begin{macrocode}
 \prop_new:N \g_@@_plain_tex_option_types_prop
 \prop_new:N \g_@@_default_plain_tex_options_prop
-\seq_gput_right:NV \g_@@_option_layers_seq \c_@@_option_layer_plain_tex_tl
+\seq_gput_right:NV
+  \g_@@_option_layers_seq
+  \c_@@_option_layer_plain_tex_tl
 \cs_new:Nn
   \@@_add_plain_tex_option:nnn
   {
@@ -12828,10 +12932,14 @@
           { redefined-snippet }
           \l_tmpa_tl
       }
-    \prop_gput:NVn
+    \keys_precompile:nnN
+      { markdown/options }
+      { #2 }
+      \l_tmpb_tl
+    \prop_gput:NVV
       \g_@@_snippets_prop
       \l_tmpa_tl
-      { #2 }
+      \l_tmpb_tl
   }
 \cs_gset_eq:NN
   \markdownSetupSnippet
@@ -12897,7 +13005,7 @@
             \g_@@_snippets_prop
             \l_tmpa_tl
             \l_tmpb_tl
-          \@@_setup:V
+          \tl_use:N
             \l_tmpb_tl
         }
         {
@@ -12912,9 +13020,6 @@
   { markdown }
   { undefined-snippet }
   { Can't~invoke~undefined~snippet~#1 }
-\cs_generate_variant:Nn
-  \@@_setup:n
-  { V }
 \ExplSyntaxOff
 %    \end{macrocode}
 % \iffalse
@@ -15699,8 +15804,8 @@
 % \begin{markdown}
 
 #### Fenced Code Attribute Context Renderers
-The following macros are only produced, when the \Opt{fencedCode} option is
-enabled.
+The following macros are only produced, when the \Opt{fencedCode} and
+\Opt{fencedCodeAttributes} options are enabled.
 
 The \mdef{markdownRendererFencedCodeAttributeContextBegin} and
 \mdef{markdownRendererFencedCodeAttributeContextEnd} macros represent the
@@ -19956,6 +20061,50 @@
 %
 % \begin{markdown}
 
+#### Warning and Error Renderers
+
+The \mdef{markdownRendererWarning} and \mdef{markdownRendererError} macros
+represent warnings and errors produced by the markdown parser. Both macros
+receive a single parameter with the text of the warning or error.
+
+% \end{markdown}
+%
+% \iffalse
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+%  \begin{macrocode}
+\def\markdownRendererWarning{%
+  \markdownRendererWarningPrototype}%
+\def\markdownRendererError{%
+  \markdownRendererErrorPrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+  \g_@@_renderers_seq
+  { warning }
+\prop_gput:Nnn
+  \g_@@_renderer_arities_prop
+  { warning }
+  { 1 }
+\seq_gput_right:Nn
+  \g_@@_renderers_seq
+  { error }
+\prop_gput:Nnn
+  \g_@@_renderer_arities_prop
+  { error }
+  { 1 }
+\ExplSyntaxOff
+%    \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
 #### YAML Metadata Renderers {#yamlmetadatarenderers}
 
 The \mdef{markdownRendererJekyllDataBegin} macro represents the beginning of a
@@ -20230,12 +20379,24 @@
 %
 % \begin{markdown}
 
-The \mdef{markdownRendererJekyllDataString} macro represents a string scalar
-value in a \acro{yaml} document. This macro will only be produced when the
-\Opt{jekyllData} option is enabled. The macro receives two arguments: the
+The \mdef{markdownRendererJekyllDataTypographicString} and
+\mdef{markdownRendererJekyllDataProgrammaticString} macros represent string
+scalar values in a \acro{yaml} document. This macro will only be produced when
+the \Opt{jekyllData} option is enabled. The macro receives two arguments: the
 scalar key in the parent structure, cast to a string following \acro{yaml}
 serialization rules, and the scalar value.
 
+For each string scalar value, both macros are produced. Whereas
+\mref{markdownRendererJekyllDataTypographicString} receives the scalar value
+after all markdown markup and special \TeX{} characters in the string have been
+replaced by \TeX{} macros, \mref{markdownRendererJekyllDataProgrammaticString}
+receives the raw scalar value. Therefore, whereas the
+\mref{markdownRendererJekyllDataTypographicString} macro is more appropriate
+for texts that are supposed to be typeset with \TeX{}, such as document titles,
+author names, or exam questions, the
+\mref{markdownRendererJekyllDataProgrammaticString} macro is more appropriate
+for identifiers and other programmatic text that won't be typeset by \TeX{}.
+
 % \end{markdown}
 %
 % \iffalse
@@ -20244,11 +20405,83 @@
 % \fi
 %
 %  \begin{macrocode}
-\def\markdownRendererJekyllDataString{%
-  \markdownRendererJekyllDataStringPrototype}%
+\def\markdownRendererJekyllDataTypographicString{%
+  \markdownRendererJekyllDataTypographicStringPrototype}%
+\def\markdownRendererJekyllDataProgrammaticString{%
+  \markdownRendererJekyllDataProgrammaticStringPrototype}%
 \ExplSyntaxOn
 \seq_gput_right:Nn
   \g_@@_renderers_seq
+  { jekyllDataTypographicString }
+\prop_gput:Nnn
+  \g_@@_renderer_arities_prop
+  { jekyllDataTypographicString }
+  { 2 }
+\seq_gput_right:Nn
+  \g_@@_renderers_seq
+  { jekyllDataProgrammaticString }
+\prop_gput:Nnn
+  \g_@@_renderer_arities_prop
+  { jekyllDataProgrammaticString }
+  { 2 }
+\ExplSyntaxOff
+%    \end{macrocode}
+% \par
+%
+% \iffalse
+%</tex>
+%<*manual-tokens>
+% \fi
+%
+% \begin{markdown}
+
+Before Markdown 3.7.0, the \mref{markdownRendererJekyllDataTypographicString}
+macro was named \mref{markdownRendererJekyllDataString} and the
+\mref{markdownRendererJekyllDataProgrammaticString} macro was not produced.
+The \mref{markdownRendererJekyllDataString} has been deprecated and will be
+removed in Markdown 4.0.0.
+
+% \end{markdown}
+%
+% \iffalse
+%</manual-tokens>
+%<*tex>
+% \fi
+%
+%  \begin{macrocode}
+\ExplSyntaxOn
+\cs_gset:Npn
+  \markdownRendererJekyllDataTypographicString
+  {
+    \cs_if_exist:NTF
+      \markdownRendererJekyllDataString
+      {
+        \markdownWarning
+          {
+            The~jekyllDataString~renderer~has~been~deprecated,~
+            to~be~removed~in~Markdown~4.0.0
+          }
+        \markdownRendererJekyllDataString
+      }
+      {
+        \cs_if_exist:NTF
+          \markdownRendererJekyllDataStringPrototype
+          {
+            \markdownWarning
+              {
+                The~jekyllDataString~renderer~prototype~
+                has~been~deprecated,~
+                to~be~removed~in~Markdown~4.0.0
+              }
+            \markdownRendererJekyllDataStringPrototype
+          }
+          {
+            \markdownRendererJekyllDataTypographicStringPrototype
+          }
+      }
+  }
+\seq_gput_right:Nn
+  \g_@@_renderers_seq
   { jekyllDataString }
 \prop_gput:Nnn
   \g_@@_renderer_arities_prop
@@ -20308,7 +20541,7 @@
 ``` tex
 \input markdown
 \def\markdownOptionJekyllData{true}
-\def\markdownRendererJekyllDataString#1#2{\gdef\name{#2}}
+\def\markdownRendererJekyllDataTypographicString#1#2{\gdef\name{#2}}
 \def\markdownRendererJekyllDataNumber#1#2{\gdef\age{#2}}
 \def\markdownRendererJekyllDataEnd{%
   \name{} is \age{} years old.}
@@ -20338,7 +20571,7 @@
 \usepackage[jekyllData]{markdown}
 \markdownSetup{
   renderers = {
-    jekyllDataString = {\gdef\name{#2}},
+    jekyllDataTypographicString = {\gdef\name{#2}},
     jekyllDataNumber = {\gdef\age{#2}},
     jekyllDataEnd = {\name{} is \age{} years old.},
   }
@@ -20370,7 +20603,7 @@
 ``` tex
 \usemodule[t][markdown]
 \setupmarkdown[jekyllData = yes]
-\def\markdownRendererJekyllDataString#1#2{\gdef\name{#2}}
+\def\markdownRendererJekyllDataTypographicString#1#2{\gdef\name{#2}}
 \def\markdownRendererJekyllDataNumber#1#2{\gdef\age{#2}}
 \def\markdownRendererJekyllDataEnd{%
   \name{} is \age{} years old.}
@@ -20412,9 +20645,26 @@
 \ExplSyntaxOn
 \cs_new:Nn \@@_define_renderers:
   {
-    \seq_map_function:NN
+    \seq_map_inline:Nn
       \g_@@_renderers_seq
-      \@@_define_renderer:n
+      {
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% The \mref{markdownRendererJekyllDataString} has been deprecated and will be
+% removed in Markdown 4.0.0.
+%
+% \end{markdown}
+%  \begin{macrocode}
+        \str_if_eq:nnF
+          { ##1 }
+          { jekyllDataString }
+          {
+            \@@_define_renderer:n
+              { ##1 }
+          }
+      }
   }
 \cs_new:Nn \@@_define_renderer:n
   {
@@ -21130,9 +21380,26 @@
 \ExplSyntaxOn
 \cs_new:Nn \@@_define_renderer_prototypes:
   {
-    \seq_map_function:NN
+    \seq_map_inline:Nn
       \g_@@_renderers_seq
-      \@@_define_renderer_prototype:n
+      {
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% The \mref{markdownRendererJekyllDataString} has been deprecated and will be
+% removed in Markdown 4.0.0.
+%
+% \end{markdown}
+%  \begin{macrocode}
+        \str_if_eq:nnF
+          { ##1 }
+          { jekyllDataString }
+          {
+            \@@_define_renderer_prototype:n
+              { ##1 }
+          }
+      }
   }
 \cs_new:Nn \@@_define_renderer_prototype:n
   {
@@ -21612,9 +21879,10 @@
 % \LaTeX{} environments, and redefines the \mref{markinline} and
 % \mref{markdownInput} commands.
 %
+%#### The `markdown` and `markdown*` \LaTeX{} environments
 % The \envmref{markdown} and \envmref{markdown*} \LaTeX{} environments are used
 % to typeset markdown document fragments. Both \LaTeX{} environments accept
-% \LaTeX{} interface options (see ection <#sec:latex-options>) as the only
+% \LaTeX{} interface options (see Section <#sec:latex-options>) as the only
 % argument. This argument is optional for the \envmref{markdown} environment
 % and mandatory for the \envmref{markdown*} environment.
 %
@@ -21651,6 +21919,52 @@
 % \end{document}                     \end{document}
 % ```````
 %
+% You can't directly extend the \envmref{markdown} \LaTeX{} environment by
+% using it in other environments as follows:
+%
+% ``` tex
+% \newenvironment{foo}\%
+%                {code before \begin{markdown}[some, options]}\%
+%                {\end{markdown} code after}
+% ```
+%
+% This is because the implementation looks for the literal string
+% `\end{markdown}` to stop scanning the markdown text. However, you can work
+% around this limitation by using the \mref{markdown} and \mref{markdownEnd}
+% macros directly in the definition as follows:
+%
+% \markdownEnd
+% \begin{markdown}
+%
+% ``` tex
+% \newenvironment{foo}\%
+%                {code before \markdown[some, options]}\%
+%                {\markdownEnd code after}
+% ```
+%
+% Specifically, the \mref{markdown} macro must appear at the end of the
+% replacement text and must be followed by text that has not yet been ingested
+% by \TeX's input processor. Furthermore, using the \mref{markdownEnd} macro is
+% optional and only makes a difference if you redefined it to produce special
+% effects before and after the \envmref{markdown} \LaTeX{} environment. Lastly,
+% you can't nest the other environments. For example, the following definition
+% is incorrect:
+%
+% ``` tex
+% \newenvironment{bar}{\begin{foo}}{\end{foo}}
+% ```
+%
+% In this example, you should use the \mref{markdown} macro directly in the
+% definition of the environment `bar`:
+%
+% ``` tex
+% \newenvironment{bar}{\markdown[some, options]}{\markdownEnd}
+% ```
+%
+% \end{markdown}
+% \markdownBegin
+%
+%#### The `\markinline` and `\markdownInput` macros
 % The \mref{markinline} macro accepts a single mandatory parameter containing
 % inline markdown content and expands to the result of the conversion of the
 % input markdown document to plain \TeX{}.  Unlike the \mref{markinline} macro
@@ -22395,7 +22709,7 @@
 % \end{markdown}
 % \iffalse
 %</manual-options>
-%<*themes-witiko-markdown-defaults-context>
+%<*themes-witiko-markdown-defaults-ctx>
 % \fi
 %  \begin{macrocode}
 \startmodule[markdownthemewitiko_markdown_defaults]
@@ -22402,7 +22716,7 @@
 \unprotect
 %    \end{macrocode}
 % \iffalse
-%</themes-witiko-markdown-defaults-context>
+%</themes-witiko-markdown-defaults-ctx>
 %<*manual-options>
 % \fi
 % \begin{markdown}
@@ -22463,9 +22777,17 @@
 % `lunamark/util.lua` file in the Lunamark Lua module.
 %
 % \end{markdown}
+% \iffalse
+%</lua>
+%<*lua,lua-loader>
+% \fi
 %  \begin{macrocode}
 local util = {}
 %    \end{macrocode}
+% \iffalse
+%</lua,lua-loader>
+%<*lua>
+% \fi
 % \par
 % \begin{markdown}
 %
@@ -22480,13 +22802,17 @@
   os.exit(exit_code or 1)
 end
 %    \end{macrocode}
+% \iffalse
+%</lua>
+%<*lua,lua-loader>
+% \fi
 % \par
 % \begin{markdown}
 %
-% The \luamdef{util.cache} method computes the digest of `string` and
-% `salt`, adds the `suffix` and looks into the directory `dir`, whether a
-% file with such a name exists. If it does not, it gets created with
-% `transform(string)` as its content. The filename is then returned.
+% The \luamdef{util.cache} method used `dir`, `string`, `salt`, and `suffix`
+% to determine a pathname. If a file with such a pathname does not exists,
+% it gets created with `transform(string)` as its content. Regardless, the
+% pathname is then returned.
 %
 % \end{markdown}
 %  \begin{macrocode}
@@ -22494,7 +22820,7 @@
   local digest = md5.sumhexa(string .. (salt or ""))
   local name = util.pathname(dir, digest .. suffix)
   local file = io.open(name, "r")
-  if file == nil then -- If no cache entry exists, then create a new one.
+  if file == nil then -- If no cache entry exists, create a new one.
     file = assert(io.open(name, "w"),
       [[Could not open file "]] .. name .. [[" for writing]])
     local result = string
@@ -22507,6 +22833,10 @@
   return name
 end
 %    \end{macrocode}
+% \iffalse
+%</lua,lua-loader>
+%<*lua>
+% \fi
 % \par
 % \begin{markdown}
 %
@@ -22765,6 +23095,10 @@
   end
 end
 %    \end{macrocode}
+% \iffalse
+%</lua>
+%<*lua,lua-loader>
+% \fi
 % \par
 % \begin{markdown}
 %
@@ -22784,6 +23118,43 @@
 % \par
 % \begin{markdown}
 %
+% The \luamdef{util.salt} method produces cryptographic salt out of a table of
+% options `options`.
+%
+% \end{markdown}
+%  \begin{macrocode}
+function util.salt(options)
+  local opt_string = {}
+  for k, _ in pairs(defaultOptions) do
+    local v = options[k]
+    if type(v) == "table" then
+      for _, i in ipairs(v) do
+        opt_string[#opt_string+1] = k .. "=" .. tostring(i)
+      end
+%    \end{macrocode}
+% \begin{markdown}
+%
+% The \Opt{cacheDir} option is disregarded.
+%
+% \end{markdown}
+%  \begin{macrocode}
+    elseif k ~= "cacheDir" then
+      opt_string[#opt_string+1] = k .. "=" .. tostring(v)
+    end
+  end
+  table.sort(opt_string)
+  local salt = table.concat(opt_string, ",")
+            .. "," .. metadata.version
+  return salt
+end
+%    \end{macrocode}
+% \iffalse
+%</lua,lua-loader>
+%<*lua>
+% \fi
+% \par
+% \begin{markdown}
+%
 %### HTML Entities
 % This section documents the \acro{HTML} entities recognized by the
 % markdown reader.  These functions are encapsulated in the `entities`
@@ -25100,15 +25471,6 @@
 % \par
 % \begin{markdown}
 %
-% Define \luamdef{writer->suffix} as the suffix of the produced cache files.
-%
-% \end{markdown}
-%  \begin{macrocode}
-  self.suffix = ".tex"
-%    \end{macrocode}
-% \par
-% \begin{markdown}
-%
 % Define \luamdef{writer->space} as the output format of a space character.
 %
 % \end{markdown}
@@ -25153,19 +25515,6 @@
 % \par
 % \begin{markdown}
 %
-% Define \luamdef{writer->pack} as a function that will take the filename
-% `name` of the output file prepared by the reader and transform it to the
-% output format.
-%
-% \end{markdown}
-%  \begin{macrocode}
-  function self.pack(name)
-    return [[\input{]] .. name .. [[}\relax]]
-  end
-%    \end{macrocode}
-% \par
-% \begin{markdown}
-%
 % Define \luamdef{writer->interblocksep} as the output format of a block
 % element separator.
 %
@@ -25269,13 +25618,17 @@
     ["{"] = "\\markdownRendererLeftBrace{}",
     ["}"] = "\\markdownRendererRightBrace{}",
     ["\\"] = "\\markdownRendererBackslash{}",
+    ["\r"] = " ",
+    ["\n"] = " ",
   }
   self.escaped_minimal_strings = {
-    ["^^"] = "\\markdownRendererCircumflex\\markdownRendererCircumflex ",
+    ["^^"] = "\\markdownRendererCircumflex"
+          .. "\\markdownRendererCircumflex ",
     ["☒"] = "\\markdownRendererTickedBox{}",
     ["⌛"] = "\\markdownRendererHalfTickedBox{}",
     ["☐"] = "\\markdownRendererUntickedBox{}",
-    [entities.hex_entity('FFFD')] = "\\markdownRendererReplacementCharacter{}",
+    [entities.hex_entity('FFFD')]
+      = "\\markdownRendererReplacementCharacter{}",
   }
 %    \end{macrocode}
 % \par
@@ -25310,7 +25663,8 @@
     ["^"] = "\\markdownRendererCircumflex{}",
     ["~"] = "\\markdownRendererTilde{}",
     ["|"] = "\\markdownRendererPipe{}",
-    [entities.hex_entity('0000')] = "\\markdownRendererReplacementCharacter{}",
+    [entities.hex_entity('0000')]
+      = "\\markdownRendererReplacementCharacter{}",
   }
 %    \end{macrocode}
 % \par
@@ -25318,9 +25672,9 @@
 %
 % Use the \luamref{writer->escaped_chars}, \luamref{writer->escaped_uri_chars},
 % and \luamref{writer->escaped_minimal_strings} tables to create the
-% \luamdef{writer->escape_typographic_text},
-% \luamdef{writer->escape_programmatic_text}, and
-% \luamdef{writer->escape_minimal} escaper functions.
+% \luamdef{escape_typographic_text},
+% \luamdef{escape_programmatic_text}, and
+% \luamdef{escape_minimal} local escaper functions.
 %
 % \end{markdown}
 %  \begin{macrocode}
@@ -25372,6 +25726,30 @@
 % \par
 % \begin{markdown}
 %
+% Define \luamdef{writer->warning} as a function that will transform an input
+% warning `t` to the output format.
+%
+% \end{markdown}
+%  \begin{macrocode}
+  function self.warning(t)
+    return {"\\markdownRendererWarning{", self.identifier(t), "}"}
+  end
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Define \luamdef{writer->error} as a function that will transform an input
+% error `t` to the output format.
+%
+% \end{markdown}
+%  \begin{macrocode}
+  function self.error(t)
+    return {"\\markdownRendererError{", self.identifier(t), "}"}
+  end
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
 % Define \luamdef{writer->code} as a function that will transform an input
 % inline code span `s` with optional attributes `attributes` to the output
 % format.
@@ -25571,7 +25949,8 @@
 %  \begin{macrocode}
   function self.inline_html_tag(contents)
     if self.flatten_inlines then return contents end
-    return {"\\markdownRendererInlineHtmlTag{",self.string(contents),"}"}
+    return {"\\markdownRendererInlineHtmlTag{",
+            self.string(contents),"}"}
   end
 %    \end{macrocode}
 % \par
@@ -25862,8 +26241,10 @@
 %  \begin{macrocode}
   function self.push_attributes(attribute_type, attributes,
                                 start_output, end_output)
-    local attribute_type_level = self.attribute_type_levels[attribute_type]
-    self.attribute_type_levels[attribute_type] = attribute_type_level + 1
+    local attribute_type_level
+      = self.attribute_type_levels[attribute_type]
+    self.attribute_type_levels[attribute_type]
+      = attribute_type_level + 1
 
     -- index attributes in a hash table for easy lookup
     attributes = attributes or {}
@@ -25918,8 +26299,10 @@
       local attributes, _, end_output
       current_attribute_type, attributes, _, end_output = table.unpack(
         self.active_attributes[#self.active_attributes])
-      local attribute_type_level = self.attribute_type_levels[current_attribute_type]
-      self.attribute_type_levels[current_attribute_type] = attribute_type_level - 1
+      local attribute_type_level
+        = self.attribute_type_levels[current_attribute_type]
+      self.attribute_type_levels[current_attribute_type]
+        = attribute_type_level - 1
       if self.is_writing and end_output ~= nil then
         table.insert(buf, end_output)
       end
@@ -25953,7 +26336,8 @@
     local prev_space = false
     local letter_found = false
     local normalized_s = s
-    if not options.unicodeNormalization or options.unicodeNormalizationForm ~= "nfc" then
+    if not options.unicodeNormalization
+       or options.unicodeNormalizationForm ~= "nfc" then
       normalized_s = uni_algos.normalize.NFC(normalized_s)
     end
 
@@ -25970,7 +26354,8 @@
         end
       end
 
-      -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods.
+      -- Remove all non-alphanumeric characters, except underscores,
+      -- hyphens, and periods.
       if not unicode.utf8.match(char, "[%w_%-%.%s]") then
         goto continue
       end
@@ -25998,7 +26383,8 @@
       table.remove(buffer)
     end
 
-    local identifier = #buffer == 0 and "section" or table.concat(buffer, "")
+    local identifier = #buffer == 0 and "section"
+                    or table.concat(buffer, "")
     return identifier
   end
 %    \end{macrocode}
@@ -26013,7 +26399,8 @@
     local prev_space = false
     local letter_found = false
     local normalized_s = s
-    if not options.unicodeNormalization or options.unicodeNormalizationForm ~= "nfc" then
+    if not options.unicodeNormalization
+       or options.unicodeNormalizationForm ~= "nfc" then
       normalized_s = uni_algos.normalize.NFC(normalized_s)
     end
 
@@ -26030,7 +26417,8 @@
         end
       end
 
-      -- Remove all non-alphanumeric characters, except underscores and hyphens.
+      -- Remove all non-alphanumeric characters, except underscores
+      -- and hyphens.
       if not unicode.utf8.match(char, "[%w_%-%s]") then
         prev_space = false
         goto continue
@@ -26059,7 +26447,8 @@
       table.remove(buffer)
     end
 
-    local identifier = #buffer == 0 and "section" or table.concat(buffer, "")
+    local identifier = #buffer == 0 and "section"
+                    or table.concat(buffer, "")
     return identifier
   end
 %    \end{macrocode}
@@ -26098,9 +26487,11 @@
       table.insert(auto_identifiers, create_auto_identifier(flat_text))
     end
     if self.options.gfmAutoIdentifiers then
-      table.insert(auto_identifiers, create_gfm_auto_identifier(flat_text))
+      table.insert(auto_identifiers,
+                   create_gfm_auto_identifier(flat_text))
     end
-    local normalized_attributes = normalize_attributes(attributes, auto_identifiers)
+    local normalized_attributes = normalize_attributes(attributes,
+                                                       auto_identifiers)
 
     -- push attributes for the new section
     local start_output = {}
@@ -26116,7 +26507,8 @@
 
     -- render the heading and its attributes
     if self.is_writing and #normalized_attributes > 0 then
-      table.insert(buf, "\\markdownRendererHeaderAttributeContextBegin\n")
+      table.insert(buf,
+                   "\\markdownRendererHeaderAttributeContextBegin\n")
       table.insert(buf, self.attributes(normalized_attributes, false))
     end
 
@@ -26289,34 +26681,91 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-parsers.punctuation            = {}
-(function()
-  local pathname = kpse.lookup("UnicodeData.txt")
+;(function()
+  local pathname = assert(kpse.find_file("UnicodeData.txt"),
+    [[Could not locate file "UnicodeData.txt"]])
   local file = assert(io.open(pathname, "r"),
     [[Could not open file "UnicodeData.txt"]])
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% In order to minimize the size and speed of the parser, we will first
+% construct a prefix tree of UTF-8 encodings for all codepoints of a
+% given code length.
+%
+% \end{markdown}
+%  \begin{macrocode}
+  local prefix_trees = {}
   for line in file:lines() do
     local codepoint, major_category = line:match("^(%x+);[^;]*;(%a)")
     if major_category == "P" or major_category == "S" then
       local code = unicode.utf8.char(tonumber(codepoint, 16))
-      if parsers.punctuation[#code] == nil then
-        parsers.punctuation[#code] = parsers.fail
+      if prefix_trees[#code] == nil then
+        prefix_trees[#code] = {}
       end
-      local code_parser = parsers.succeed
+      local node = prefix_trees[#code]
       for i = 1, #code do
         local byte = code:sub(i, i)
-        local byte_parser = S(byte)
-        code_parser = code_parser
-                    * byte_parser
+        if i < #code then
+          if node[byte] == nil then
+            node[byte] = {}
+          end
+          node = node[byte]
+        else
+          table.insert(node, byte)
+        end
       end
-      parsers.punctuation[#code] = parsers.punctuation[#code]
-                                 + code_parser
     end
   end
   assert(file:close())
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Next, we will construct a parser out of the prefix tree.
+%
+% \end{markdown}
+%  \begin{macrocode}
+  local function depth_first_search(node, path, visit, leave)
+    visit(node, path)
+    for label, child in pairs(node) do
+      if type(child) == "table" then
+        depth_first_search(child, path .. label, visit, leave)
+      else
+        visit(child, path)
+      end
+    end
+    leave(node, path)
+  end
+
+  parsers.punctuation = {}
+  for length, prefix_tree in pairs(prefix_trees) do
+    local subparsers = {}
+    depth_first_search(prefix_tree, "", function(node, path)
+      if type(node) == "table" then
+        subparsers[path] = parsers.fail
+      else
+        assert(type(node) == "string")
+        subparsers[path] = subparsers[path] + S(node)
+      end
+    end, function(_, path)
+      if #path > 0 then
+        local byte = path:sub(#path, #path)
+        local parent_path = path:sub(1, #path-1)
+        subparsers[parent_path] = subparsers[parent_path]
+                                + S(byte) * subparsers[path]
+      else
+        parsers.punctuation[length] = subparsers[path]
+      end
+    end)
+    assert(parsers.punctuation[length] ~= nil)
+  end
 end)()
 
 parsers.escapable              = parsers.ascii_punctuation
-parsers.anyescaped             = parsers.backslash / "" * parsers.escapable
+parsers.anyescaped             = parsers.backslash / ""
+                               * parsers.escapable
                                + parsers.any
 
 parsers.spacechar              = S("\t ")
@@ -26335,14 +26784,18 @@
 parsers.blankline              = parsers.optionalspace
                                * parsers.newline / "\n"
 parsers.blanklines             = parsers.blankline^0
-parsers.skipblanklines         = (parsers.optionalspace * parsers.newline)^0
+parsers.skipblanklines         = ( parsers.optionalspace
+                                 * parsers.newline)^0
 parsers.indentedline           = parsers.indent    /""
-                               * C(parsers.linechar^1 * parsers.newline^-1)
+                               * C( parsers.linechar^1
+                                  * parsers.newline^-1)
 parsers.optionallyindentedline = parsers.indent^-1 /""
-                               * C(parsers.linechar^1 * parsers.newline^-1)
+                               * C( parsers.linechar^1
+                                  * parsers.newline^-1)
 parsers.sp                     = parsers.spacing^0
 parsers.spnl                   = parsers.optionalspace
-                               * (parsers.newline * parsers.optionalspace)^-1
+                               * ( parsers.newline
+                                 * parsers.optionalspace)^-1
 parsers.line                   = parsers.linechar^0 * parsers.newline
 parsers.nonemptyline           = parsers.line - parsers.blankline
 %    \end{macrocode}
@@ -26425,7 +26878,8 @@
   if add then
     indent_table.indents[#indent_table.indents + 1] = new_indent
   else
-    if indent_table.indents[#indent_table.indents].name == new_indent.name then
+    if indent_table.indents[#indent_table.indents].name
+       == new_indent.name then
       indent_table.indents[#indent_table.indents] = nil
     end
   end
@@ -26441,10 +26895,12 @@
 % \end{markdown}
 %  \begin{macrocode}
 local function remove_indent(name)
-  local function remove_indent_level(s, i, indent_table) -- luacheck: ignore s i
-    indent_table = update_indent_table(indent_table, {name=name}, false)
-    return true, indent_table
-  end
+  local remove_indent_level =
+    function(s, i, indent_table) -- luacheck: ignore s i
+      indent_table = update_indent_table(indent_table, {name=name},
+                                         false)
+      return true, indent_table
+    end
 
   return Cg(Cmt(Cb("indent_info"), remove_indent_level), "indent_info")
 end
@@ -26460,7 +26916,8 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function process_starter_spacing(indent, spacing, minimum, left_strip_length)
+local function process_starter_spacing(indent, spacing,
+                                       minimum, left_strip_length)
   left_strip_length = left_strip_length or 0
 
   local count = 0
@@ -26497,7 +26954,8 @@
         minimum_remainder = minimum_remainder .. character
       elseif (count >= minimum) then
         minimum_found = true
-        minimum_remainder = minimum_remainder .. string.rep(" ", count - minimum)
+        minimum_remainder = minimum_remainder
+                         .. string.rep(" ", count - minimum)
       end
 
       if (code_started) then
@@ -26504,7 +26962,8 @@
         code_start = code_start .. character
       elseif (count >= minimum + 4) then
         code_started = true
-        code_start = code_start .. string.rep(" ", count - (minimum + 4))
+        code_start = code_start
+                  .. string.rep(" ", count - (minimum + 4))
       end
     end
   end
@@ -26571,7 +27030,8 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function process_starter_indent(_, _, indent_table, starter, is_blank, indent_type, breakable)
+local function process_starter_indent(_, _, indent_table, starter,
+                                      is_blank, indent_type, breakable)
   local last_trail = starter[1]
   local delimiter = starter[2]
   local raw_new_trail = starter[3]
@@ -26599,11 +27059,13 @@
   local last_trail_length = #last_trail
   local delimiter_length = total_delimiter_length(delimiter)
 
-  local total_indent_level = preceding_indentation + last_trail_length + delimiter_length
+  local total_indent_level = preceding_indentation + last_trail_length
+                           + delimiter_length
 
   local sp = {}
   if not is_blank then
-    sp = process_starter_spacing(total_indent_level, raw_new_trail, 0, 1)
+    sp = process_starter_spacing(total_indent_level, raw_new_trail,
+                                 0, 1)
   end
 
   local del_trail_length = sp.left_total_stripped
@@ -26613,12 +27075,17 @@
     del_trail_length = del_trail_length + #sp.remainder
   end
 
-  local indent_length = last_trail_length + delimiter_length + del_trail_length
+  local indent_length = last_trail_length + delimiter_length
+                      + del_trail_length
   local new_indent_info = {name=indent_type, length=indent_length}
 
-  indent_table = update_indent_table(indent_table, new_indent_info, true)
-  indent_table = add_trail(indent_table, {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length,
-                                          full_remainder=sp.full_remainder})
+  indent_table = update_indent_table(indent_table, new_indent_info,
+                                     true)
+  indent_table = add_trail(indent_table,
+                           {is_code=sp.is_code,
+                            remainder=sp.remainder,
+                            total_length=sp.total_length,
+                            full_remainder=sp.full_remainder})
 
   return true, indent_table
 end
@@ -26636,7 +27103,8 @@
     delimeter = parsers.more
   end
 
-  return C(parsers.optionalspace) * C(delimeter) * C(parsers.optionalspace) * Cp()
+  return C(parsers.optionalspace) * C(delimeter)
+       * C(parsers.optionalspace) * Cp()
 end
 
 %    \end{macrocode}
@@ -26677,7 +27145,8 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function traverse_indent(s, i, indent_table, is_optional, is_blank, current_line_indents)
+local function traverse_indent(s, i, indent_table, is_optional,
+                               is_blank, current_line_indents)
   local new_index = i
 
   local preceding_indentation = 0
@@ -26696,12 +27165,17 @@
     -- match decoded pattern
     local new_indent_info = lpeg.match(Ct(pattern), s, new_index)
     if new_indent_info == nil then
-      local blankline_end = lpeg.match(Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index)
-      if is_optional or not indent_table.ignore_blockquote_blank or not blankline_end then
-        return is_optional, new_index, current_trail, current_line_indents
+      local blankline_end = lpeg.match(
+        Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index)
+      if is_optional or not indent_table.ignore_blockquote_blank
+         or not blankline_end then
+        return is_optional, new_index, current_trail,
+               current_line_indents
       end
 
-      return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank, current_line_indents)
+      return traverse_indent(s, tonumber(blankline_end.pos),
+                             indent_table, is_optional, is_blank,
+                             current_line_indents)
     end
 
     local raw_last_trail = new_indent_info[1]
@@ -26714,13 +27188,15 @@
     -- check previous trail
     if not space_only and next(current_trail) == nil then
       local sp = process_starter_spacing(0, raw_last_trail, 0, 0)
-      current_trail = {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length,
+      current_trail = {is_code=sp.is_code, remainder=sp.remainder,
+                       total_length=sp.total_length,
                        full_remainder=sp.full_remainder}
     end
 
     if next(current_trail) ~= nil then
       if not space_only and current_trail.is_code then
-        return is_optional, new_index, current_trail, current_line_indents
+        return is_optional, new_index, current_trail,
+               current_line_indents
       end
       if current_trail.internal_remainder ~= nil then
         raw_last_trail = current_trail.internal_remainder
@@ -26735,7 +27211,8 @@
       raw_last_trail_length = #raw_last_trail
     end
 
-    local total_indent_level = preceding_indentation + raw_last_trail_length + delimiter_length
+    local total_indent_level = preceding_indentation
+                             + raw_last_trail_length + delimiter_length
 
     local spacing_to_process
     local minimum = 0
@@ -26749,13 +27226,17 @@
       minimum = value.length
     end
 
-    local sp = process_starter_spacing(total_indent_level, spacing_to_process, minimum, left_strip_length)
+    local sp = process_starter_spacing(total_indent_level,
+                                       spacing_to_process, minimum,
+                                       left_strip_length)
 
     if space_only and not sp.is_minimum then
-      return is_optional or (is_blank and blank_starter <= index), new_index, current_trail, current_line_indents
+      return is_optional or (is_blank and blank_starter <= index),
+             new_index, current_trail, current_line_indents
     end
 
-    local indent_length = raw_last_trail_length + delimiter_length + sp.left_total_stripped
+    local indent_length = raw_last_trail_length + delimiter_length
+                        + sp.left_total_stripped
 
     -- update info for the next pattern
     if not space_only then
@@ -26764,8 +27245,10 @@
       preceding_indentation = preceding_indentation + value.length
     end
 
-    current_trail = {is_code=sp.is_code, remainder=sp.remainder, internal_remainder=sp.minimum_remainder,
-                     total_length=sp.total_length, full_remainder=sp.full_remainder}
+    current_trail = {is_code=sp.is_code, remainder=sp.remainder,
+                     internal_remainder=sp.minimum_remainder,
+                     total_length=sp.total_length,
+                     full_remainder=sp.full_remainder}
 
     current_line_indents[#current_line_indents + 1] = new_indent_info
     new_index = next_index
@@ -26793,35 +27276,37 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function check_trail_joined(s, i, indent_table, spacing, expect_code, omit_remainder) -- luacheck: ignore s i
-  local is_code
-  local remainder
+local check_trail_joined =
+  function(s, i, indent_table, -- luacheck: ignore s i
+           spacing, expect_code, omit_remainder)
+    local is_code
+    local remainder
 
-  if has_trail(indent_table) then
-    local trail = indent_table.trail
-    is_code = trail.is_code
-    if is_code then
-      remainder = trail.remainder
+    if has_trail(indent_table) then
+      local trail = indent_table.trail
+      is_code = trail.is_code
+      if is_code then
+        remainder = trail.remainder
+      else
+        remainder = trail.full_remainder
+      end
     else
-      remainder = trail.full_remainder
+      local sp = process_starter_spacing(0, spacing, 0, 0)
+      is_code = sp.is_code
+      if is_code then
+        remainder = sp.remainder
+      else
+        remainder = sp.full_remainder
+      end
     end
-  else
-    local sp = process_starter_spacing(0, spacing, 0, 0)
-    is_code = sp.is_code
-    if is_code then
-      remainder = sp.remainder
-    else
-      remainder = sp.full_remainder
+
+    local result = check_trail(expect_code, is_code)
+    if omit_remainder then
+      return result
     end
+    return result, remainder
   end
 
-  local result = check_trail(expect_code, is_code)
-  if omit_remainder then
-    return result
-  end
-  return result, remainder
-end
-
 %    \end{macrocode}
 % \begin{markdown}
 %
@@ -26829,23 +27314,25 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function check_trail_length(s, i, indent_table, spacing, min, max) -- luacheck: ignore s i
-  local trail
+local check_trail_length =
+  function(s, i, indent_table, -- luacheck: ignore s i
+           spacing, min, max)
+    local trail
 
-  if has_trail(indent_table) then
-    trail = indent_table.trail
-  else
-    trail = process_starter_spacing(0, spacing, 0, 0)
-  end
+    if has_trail(indent_table) then
+      trail = indent_table.trail
+    else
+      trail = process_starter_spacing(0, spacing, 0, 0)
+    end
 
-  local total_length = trail.total_length
-  if total_length == nil then
-    return false
+    local total_length = trail.total_length
+    if total_length == nil then
+      return false
+    end
+
+    return min <= total_length and total_length <= max
   end
 
-  return min <= total_length and total_length <= max
-end
-
 %    \end{macrocode}
 % \begin{markdown}
 %
@@ -26854,7 +27341,8 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function check_continuation_indentation(s, i, indent_table, is_optional, is_blank)
+local function check_continuation_indentation(s, i, indent_table,
+                                              is_optional, is_blank)
   if not has_indents(indent_table) then
     return true
   end
@@ -26907,27 +27395,29 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function check_trail_type(s, i, trail, spacing, trail_type) -- luacheck: ignore s i
-  if trail == nil then
-    trail = process_starter_spacing(0, spacing, 0, 0)
-  end
+local check_trail_type =
+  function(s, i, -- luacheck: ignore s i
+           trail, spacing, trail_type)
+    if trail == nil then
+      trail = process_starter_spacing(0, spacing, 0, 0)
+    end
 
-  if trail_type == "non-code" then
-    return check_trail(false, trail.is_code)
-  end
-  if trail_type == "code" then
-    return check_trail(true, trail.is_code)
-  end
-  if trail_type == "full-code" then
-    if (trail.is_code) then
-      return i, trail.remainder
+    if trail_type == "non-code" then
+      return check_trail(false, trail.is_code)
     end
-    return i, ""
+    if trail_type == "code" then
+      return check_trail(true, trail.is_code)
+    end
+    if trail_type == "full-code" then
+      if (trail.is_code) then
+        return i, trail.remainder
+      end
+      return i, ""
+    end
+    if trail_type == "full-any" then
+      return i, trail.internal_remainder
+    end
   end
-  if trail_type == "full-any" then
-    return i, trail.internal_remainder
-  end
-end
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -26936,20 +27426,22 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function trail_freezing(s, i, indent_table, is_freezing) -- luacheck: ignore s i
-  if is_freezing then
-    if indent_table.is_trail_frozen then
-      indent_table.trail = indent_table.frozen_trail
+local trail_freezing =
+  function(s, i, -- luacheck: ignore s i
+           indent_table, is_freezing)
+    if is_freezing then
+      if indent_table.is_trail_frozen then
+        indent_table.trail = indent_table.frozen_trail
+      else
+        indent_table.frozen_trail = indent_table.trail
+        indent_table.is_trail_frozen = true
+      end
     else
-      indent_table.frozen_trail = indent_table.trail
-      indent_table.is_trail_frozen = true
+      indent_table.frozen_trail = nil
+      indent_table.is_trail_frozen = false
     end
-  else
-    indent_table.frozen_trail = nil
-    indent_table.is_trail_frozen = false
+    return true, indent_table
   end
-  return true, indent_table
-end
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -26960,53 +27452,59 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-local function check_continuation_indentation_and_trail(s, i, indent_table, is_optional, is_blank, trail_type,
-                                                        reset_rem, omit_remainder)
-  if not has_indents(indent_table) then
-    local spacing, new_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i)
-    local result, remainder = check_trail_type(s, i, indent_table.trail, spacing, trail_type)
-    if remainder == nil then
+local check_continuation_indentation_and_trail =
+  function (s, i, indent_table, is_optional, is_blank, trail_type,
+            reset_rem, omit_remainder)
+    if not has_indents(indent_table) then
+      local spacing, new_index = lpeg.match( C(parsers.spacechar^0)
+                                           * Cp(), s, i)
+      local result, remainder = check_trail_type(s, i,
+        indent_table.trail, spacing, trail_type)
+      if remainder == nil then
+        if result then
+          return new_index
+        end
+        return false
+      end
       if result then
-        return new_index
+        return new_index, remainder
       end
       return false
     end
-    if result then
-      return new_index, remainder
-    end
-    return false
-  end
 
-  local passes, new_index, current_trail = traverse_indent(s, i, indent_table, is_optional, is_blank)
+    local passes, new_index, current_trail = traverse_indent(s, i,
+      indent_table, is_optional, is_blank)
 
-  if passes then
-    local spacing
-    if current_trail == nil then
-      local newer_spacing, newer_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i)
-      current_trail = process_starter_spacing(0, newer_spacing, 0, 0)
-      new_index = newer_index
-      spacing = newer_spacing
-    else
-      spacing = current_trail.remainder
-    end
-    local result, remainder = check_trail_type(s, new_index, current_trail, spacing, trail_type)
-    if remainder == nil or omit_remainder then
+    if passes then
+      local spacing
+      if current_trail == nil then
+        local newer_spacing, newer_index = lpeg.match(
+          C(parsers.spacechar^0) * Cp(), s, i)
+        current_trail = process_starter_spacing(0, newer_spacing, 0, 0)
+        new_index = newer_index
+        spacing = newer_spacing
+      else
+        spacing = current_trail.remainder
+      end
+      local result, remainder = check_trail_type(s, new_index,
+        current_trail, spacing, trail_type)
+      if remainder == nil or omit_remainder then
+        if result then
+          return new_index
+        end
+        return false
+      end
+
+      if is_blank and reset_rem then
+        remainder = remove_remainder_if_blank(indent_table, remainder)
+      end
       if result then
-        return new_index
+        return new_index, remainder
       end
       return false
     end
-
-    if is_blank and reset_rem then
-      remainder = remove_remainder_if_blank(indent_table, remainder)
-    end
-    if result then
-      return new_index, remainder
-    end
     return false
   end
-  return false
-end
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -27015,14 +27513,20 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-parsers.check_trail = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(false), check_trail_joined)
+parsers.check_trail = Cmt( Cb("indent_info") * C(parsers.spacechar^0)
+                         * Cc(false), check_trail_joined)
 
-parsers.check_trail_no_rem = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(false) * Cc(true), check_trail_joined)
+parsers.check_trail_no_rem = Cmt( Cb("indent_info")
+                                * C(parsers.spacechar^0) * Cc(false)
+                                * Cc(true), check_trail_joined)
 
-parsers.check_code_trail  = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(true), check_trail_joined)
+parsers.check_code_trail  = Cmt( Cb("indent_info")
+                               * C(parsers.spacechar^0)
+                               * Cc(true), check_trail_joined)
 
 parsers.check_trail_length_range  = function(min, max)
-  return Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(min) * Cc(max), check_trail_length)
+  return Cmt( Cb("indent_info") * C(parsers.spacechar^0) * Cc(min)
+            * Cc(max), check_trail_length)
 end
 
 parsers.check_trail_length = function(n)
@@ -27037,9 +27541,11 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-parsers.freeze_trail = Cg(Cmt(Cb("indent_info") * Cc(true), trail_freezing), "indent_info")
+parsers.freeze_trail = Cg( Cmt(Cb("indent_info")
+                         * Cc(true), trail_freezing), "indent_info")
 
-parsers.unfreeze_trail = Cg(Cmt(Cb("indent_info") * Cc(false), trail_freezing), "indent_info")
+parsers.unfreeze_trail = Cg(Cmt(Cb("indent_info") * Cc(false),
+                            trail_freezing), "indent_info")
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -27048,11 +27554,16 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-parsers.check_minimal_indent = Cmt(Cb("indent_info") * Cc(false), check_continuation_indentation)
+parsers.check_minimal_indent = Cmt(Cb("indent_info") * Cc(false),
+                                   check_continuation_indentation)
 
-parsers.check_optional_indent = Cmt(Cb("indent_info") * Cc(true), check_continuation_indentation)
+parsers.check_optional_indent = Cmt(Cb("indent_info") * Cc(true),
+                                    check_continuation_indentation)
 
-parsers.check_minimal_blank_indent = Cmt(Cb("indent_info") * Cc(false) * Cc(true), check_continuation_indentation)
+parsers.check_minimal_blank_indent
+  = Cmt( Cb("indent_info") * Cc(false)
+       * Cc(true)
+       , check_continuation_indentation)
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -27063,37 +27574,45 @@
 % \end{markdown}
 %  \begin{macrocode}
 
-parsers.check_minimal_indent_and_trail = Cmt( Cb("indent_info")
-                                            * Cc(false) * Cc(false) * Cc("non-code") * Cc(true),
-                                            check_continuation_indentation_and_trail)
+parsers.check_minimal_indent_and_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(false) * Cc("non-code") * Cc(true)
+     , check_continuation_indentation_and_trail)
 
-parsers.check_minimal_indent_and_code_trail = Cmt( Cb("indent_info")
-                                                 * Cc(false) * Cc(false) * Cc("code") * Cc(false),
-                                                 check_continuation_indentation_and_trail)
+parsers.check_minimal_indent_and_code_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(false) * Cc("code") * Cc(false)
+     , check_continuation_indentation_and_trail)
 
-parsers.check_minimal_blank_indent_and_full_code_trail = Cmt( Cb("indent_info")
-                                                            * Cc(false) * Cc(true) * Cc("full-code") * Cc(true),
-                                                            check_continuation_indentation_and_trail)
+parsers.check_minimal_blank_indent_and_full_code_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(true) * Cc("full-code") * Cc(true)
+     , check_continuation_indentation_and_trail)
 
-parsers.check_minimal_indent_and_any_trail = Cmt( Cb("indent_info")
-                                                * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false),
-                                                check_continuation_indentation_and_trail)
+parsers.check_minimal_indent_and_any_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false)
+     , check_continuation_indentation_and_trail)
 
-parsers.check_minimal_blank_indent_and_any_trail = Cmt( Cb("indent_info")
-                                                      * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false),
-                                                       check_continuation_indentation_and_trail)
+parsers.check_minimal_blank_indent_and_any_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false)
+     , check_continuation_indentation_and_trail)
 
-parsers.check_minimal_blank_indent_and_any_trail_no_rem = Cmt( Cb("indent_info")
-                                                        * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(true),
-                                                        check_continuation_indentation_and_trail)
+parsers.check_minimal_blank_indent_and_any_trail_no_rem =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(true)
+     , check_continuation_indentation_and_trail)
 
-parsers.check_optional_indent_and_any_trail = Cmt( Cb("indent_info")
-                                                * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false),
-                                                check_continuation_indentation_and_trail)
+parsers.check_optional_indent_and_any_trail =
+  Cmt( Cb("indent_info")
+     * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false)
+     , check_continuation_indentation_and_trail)
 
-parsers.check_optional_blank_indent_and_any_trail = Cmt( Cb("indent_info")
-                                                      * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false),
-                                                       check_continuation_indentation_and_trail)
+parsers.check_optional_blank_indent_and_any_trail =
+  Cmt( Cb("indent_info")
+     * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false)
+     , check_continuation_indentation_and_trail)
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -27104,7 +27623,8 @@
 %  \begin{macrocode}
 
 parsers.spnlc_noexc = parsers.optionalspace
-                    * (parsers.newline * parsers.check_minimal_indent_and_any_trail)^-1
+                    * ( parsers.newline
+                      * parsers.check_minimal_indent_and_any_trail)^-1
 
 parsers.spnlc = parsers.optionalspace
               * (V("EndlineNoSub"))^-1
@@ -27112,7 +27632,8 @@
 parsers.spnlc_sep  = parsers.optionalspace * V("EndlineNoSub")
                    + parsers.spacechar^1
 
-parsers.only_blank = parsers.spacechar^0 * (parsers.newline + parsers.eof)
+parsers.only_blank = parsers.spacechar^0
+                   * (parsers.newline + parsers.eof)
 
 %   \end{macrocode}
 % \begin{figure}
@@ -27159,64 +27680,66 @@
                                + parsers.newline
                                - parsers.backslash
                                - parsers.percent
-parsers.commented_line         = Cg(Cc(""), "backslashes")
-                               * ((#(parsers.commented_line_letter
-                                    - parsers.newline)
-                                  * Cb("backslashes")
-                                  * Cs(parsers.commented_line_letter
-                                    - parsers.newline)^1  -- initial
-                                  * Cg(Cc(""), "backslashes"))
-                                 + #(parsers.backslash * parsers.backslash)
-                                 * Cg((parsers.backslash  -- even backslash
-                                      * parsers.backslash)^1, "backslashes")
-                                 + (parsers.backslash
-                                   * (#parsers.percent
-                                     * Cb("backslashes")
-                                     / function(backslashes)
-                                       return string.rep("\\", #backslashes / 2)
-                                     end
-                                     * C(parsers.percent)
-                                     + #parsers.commented_line_letter
-                                     * Cb("backslashes")
-                                     * Cc("\\")
-                                     * C(parsers.commented_line_letter))
-                                   * Cg(Cc(""), "backslashes")))^0
-                               * (#parsers.percent
-                                 * Cb("backslashes")
-                                 / function(backslashes)
-                                   return string.rep("\\", #backslashes / 2)
-                                 end
-                                 * ((parsers.percent  -- comment
-                                    * parsers.line
-                                    * #parsers.blankline) -- blank line
-                                   / "\n"
-                                   + parsers.percent  -- comment
-                                   * parsers.line
-                                   * parsers.optionalspace)  -- leading tabs and spaces
-                                 + #(parsers.newline)
-                                 * Cb("backslashes")
-                                 * C(parsers.newline))
+parsers.commented_line = Cg(Cc(""), "backslashes")
+                       * ((#(parsers.commented_line_letter
+                            - parsers.newline)
+                          * Cb("backslashes")
+                          * Cs(parsers.commented_line_letter
+                            - parsers.newline)^1  -- initial
+                          * Cg(Cc(""), "backslashes"))
+                         + #(parsers.backslash * parsers.backslash)
+                         * Cg((parsers.backslash  -- even backslash
+                              * parsers.backslash)^1, "backslashes")
+                         + (parsers.backslash
+                           * (#parsers.percent
+                             * Cb("backslashes")
+                             / function(backslashes)
+                               return string.rep("\\", #backslashes / 2)
+                             end
+                             * C(parsers.percent)
+                             + #parsers.commented_line_letter
+                             * Cb("backslashes")
+                             * Cc("\\")
+                             * C(parsers.commented_line_letter))
+                           * Cg(Cc(""), "backslashes")))^0
+                       * (#parsers.percent
+                         * Cb("backslashes")
+                         / function(backslashes)
+                           return string.rep("\\", #backslashes / 2)
+                         end
+                         * ((parsers.percent  -- comment
+                            * parsers.line
+                            * #parsers.blankline) -- blank line
+                           / "\n"
+                           + parsers.percent  -- comment
+                           * parsers.line
+                           * parsers.optionalspace)  -- leading spaces
+                         + #(parsers.newline)
+                         * Cb("backslashes")
+                         * C(parsers.newline))
 
-parsers.chunk                  = parsers.line * (parsers.optionallyindentedline
-                                                - parsers.blankline)^0
+parsers.chunk = parsers.line * (parsers.optionallyindentedline
+                                       - parsers.blankline)^0
 
-parsers.attribute_key_char     = parsers.alphanumeric + S("-_:.")
-parsers.attribute_raw_char     = parsers.alphanumeric + S("-_")
-parsers.attribute_key          = (parsers.attribute_key_char
-                                 - parsers.dash - parsers.digit)
-                               * parsers.attribute_key_char^0
-parsers.attribute_value        = ( (parsers.dquote / "")
-                                 * (parsers.anyescaped - parsers.dquote)^0
-                                 * (parsers.dquote / ""))
-                               + ( (parsers.squote / "")
-                                 * (parsers.anyescaped - parsers.squote)^0
-                                 * (parsers.squote / ""))
-                               + ( parsers.anyescaped - parsers.dquote - parsers.rbrace
-                                 - parsers.space)^0
-parsers.attribute_identifier   = parsers.attribute_key_char^1
-parsers.attribute_classname    = parsers.letter
-                               * parsers.attribute_key_char^0
-parsers.attribute_raw          = parsers.attribute_raw_char^1
+parsers.attribute_key_char = parsers.alphanumeric + S("-_:.")
+parsers.attribute_raw_char = parsers.alphanumeric + S("-_")
+parsers.attribute_key = (parsers.attribute_key_char
+                        - parsers.dash - parsers.digit)
+                      * parsers.attribute_key_char^0
+parsers.attribute_value = ( (parsers.dquote / "")
+                          * (parsers.anyescaped - parsers.dquote)^0
+                          * (parsers.dquote / ""))
+                        + ( (parsers.squote / "")
+                          * (parsers.anyescaped - parsers.squote)^0
+                          * (parsers.squote / ""))
+                        + ( parsers.anyescaped
+                          - parsers.dquote
+                          - parsers.rbrace
+                          - parsers.space)^0
+parsers.attribute_identifier = parsers.attribute_key_char^1
+parsers.attribute_classname = parsers.letter
+                            * parsers.attribute_key_char^0
+parsers.attribute_raw = parsers.attribute_raw_char^1
 
 parsers.attribute = (parsers.dash * Cc(".unnumbered"))
                   + C( parsers.hash
@@ -27224,7 +27747,9 @@
                   + C( parsers.period
                      * parsers.attribute_classname)
                   + Cs( parsers.attribute_key
-                      * parsers.optionalspace * parsers.equal * parsers.optionalspace
+                      * parsers.optionalspace
+                      * parsers.equal
+                      * parsers.optionalspace
                       * parsers.attribute_value)
 parsers.attributes = parsers.lbrace
                    * parsers.optionalspace
@@ -27234,7 +27759,6 @@
                    * parsers.optionalspace
                    * parsers.rbrace
 
-
 parsers.raw_attribute = parsers.lbrace
                       * parsers.optionalspace
                       * parsers.equal
@@ -27246,7 +27770,10 @@
 -- indented blocks with first line indented.
 parsers.indented_blocks = function(bl)
   return Cs( bl
-         * (parsers.blankline^1 * parsers.indent * -parsers.blankline * bl)^0
+         * ( parsers.blankline^1
+           * parsers.indent
+           * -parsers.blankline
+           * bl)^0
          * (parsers.blankline^1 + parsers.eof) )
 end
 %    \end{macrocode}
@@ -27262,15 +27789,18 @@
 end
 
 parsers.hexentity = parsers.ampersand * parsers.hash * C(S("Xx"))
-                  * C(repeat_between(parsers.hexdigit, 1, 6)) * parsers.semicolon
+                  * C(repeat_between(parsers.hexdigit, 1, 6))
+                  * parsers.semicolon
 parsers.decentity = parsers.ampersand * parsers.hash
-                  * C(repeat_between(parsers.digit, 1, 7)) * parsers.semicolon
+                  * C(repeat_between(parsers.digit, 1, 7))
+                  * parsers.semicolon
 parsers.tagentity = parsers.ampersand * C(parsers.alphanumeric^1)
                   * parsers.semicolon
 
-parsers.html_entities = parsers.hexentity / entities.hex_entity_with_x_char
-                      + parsers.decentity / entities.dec_entity
-                      + parsers.tagentity / entities.char_entity
+parsers.html_entities
+  = parsers.hexentity / entities.hex_entity_with_x_char
+  + parsers.decentity / entities.dec_entity
+  + parsers.tagentity / entities.char_entity
 %    \end{macrocode}
 % \par
 % \begin{markdown}
@@ -27284,7 +27814,8 @@
   if interrupting then
     allowed_end = C(parsers.spacechar^1) * #parsers.linechar
   else
-    allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
+    allowed_end = C(parsers.spacechar^1)
+                + #(parsers.newline + parsers.eof)
   end
   return parsers.check_trail
         * Ct(C(bullet_char) * Cc(""))
@@ -27342,7 +27873,8 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
--- case-insensitive match (we assume s is lowercase). must be single byte encoding
+-- 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
@@ -27424,13 +27956,14 @@
     parsers.keyword_exact("ul")
 
 -- end conditions
-parsers.html_blankline_end_condition  = parsers.linechar^0
-                                      * ( parsers.newline
-                                        * (parsers.check_minimal_blank_indent_and_any_trail
-                                          * #parsers.blankline
-                                          + parsers.check_minimal_indent_and_any_trail)
-                                        * parsers.linechar^1)^0
-                                      * (parsers.newline^-1 / "")
+parsers.html_blankline_end_condition
+  = parsers.linechar^0
+  * ( parsers.newline
+    * (parsers.check_minimal_blank_indent_and_any_trail
+      * #parsers.blankline
+      + parsers.check_minimal_indent_and_any_trail)
+    * parsers.linechar^1)^0
+  * (parsers.newline^-1 / "")
 
 local function remove_trailing_blank_lines(s)
   return s:gsub("[\n\r]+%s*$", "")
@@ -27452,9 +27985,14 @@
                                 * parsers.optionalspace
                                 + parsers.spacechar^1
 
-parsers.html_attribute_name = (parsers.letter + parsers.colon + parsers.underscore)
-                            * (parsers.alphanumeric + parsers.colon + parsers.underscore
-                            + parsers.period + parsers.dash)^0
+parsers.html_attribute_name = ( parsers.letter
+                              + parsers.colon
+                              + parsers.underscore)
+                            * ( parsers.alphanumeric
+                              + parsers.colon
+                              + parsers.underscore
+                            + parsers.period
+                            + parsers.dash)^0
 
 parsers.html_attribute_value  = parsers.squote
                               * (parsers.linechar - parsers.squote)^0
@@ -27462,9 +28000,15 @@
                               + parsers.dquote
                               * (parsers.linechar - parsers.dquote)^0
                               * parsers.dquote
-                              + ( parsers.any - parsers.spacechar - parsers.newline
-                                - parsers.dquote - parsers.squote - parsers.backtick
-                                - parsers.equal - parsers.less - parsers.more)^1
+                              + ( parsers.any
+                                - parsers.spacechar
+                                - parsers.newline
+                                - parsers.dquote
+                                - parsers.squote
+                                - parsers.backtick
+                                - parsers.equal
+                                - parsers.less
+                                - parsers.more)^1
 
 parsers.html_inline_attribute_value = parsers.squote
                                     * (V("NoSoftLineBreakEndline")
@@ -27478,30 +28022,41 @@
                                       - parsers.blankline^2
                                       - parsers.dquote)^0
                                     * parsers.dquote
-                                    + (parsers.any - parsers.spacechar - parsers.newline
-                                      - parsers.dquote - parsers.squote - parsers.backtick
-                                      - parsers.equal - parsers.less - parsers.more)^1
+                                    + (parsers.any
+                                      - parsers.spacechar
+                                      - parsers.newline
+                                      - parsers.dquote
+                                      - parsers.squote
+                                      - parsers.backtick
+                                      - parsers.equal
+                                      - parsers.less
+                                      - parsers.more)^1
 
-parsers.html_attribute_value_specification  = parsers.optionalspace
-                                            * parsers.equal
-                                            * parsers.optionalspace
-                                            * parsers.html_attribute_value
+parsers.html_attribute_value_specification
+  = parsers.optionalspace
+  * parsers.equal
+  * parsers.optionalspace
+  * parsers.html_attribute_value
 
 parsers.html_spnl = parsers.optionalspace
-                  * (V("NoSoftLineBreakEndline") * parsers.optionalspace)^-1
+                  * (V("NoSoftLineBreakEndline")
+                  * parsers.optionalspace)^-1
 
-parsers.html_inline_attribute_value_specification = parsers.html_spnl
-                                                  * parsers.equal
-                                                  * parsers.html_spnl
-                                                  * parsers.html_inline_attribute_value
+parsers.html_inline_attribute_value_specification
+  = parsers.html_spnl
+  * parsers.equal
+  * parsers.html_spnl
+  * parsers.html_inline_attribute_value
 
-parsers.html_attribute  = parsers.html_attribute_spacing
-                        * parsers.html_attribute_name
-                        * parsers.html_inline_attribute_value_specification^-1
+parsers.html_attribute
+  = parsers.html_attribute_spacing
+  * parsers.html_attribute_name
+  * parsers.html_inline_attribute_value_specification^-1
 
-parsers.html_non_newline_attribute  = parsers.spacechar^1
-                                    * parsers.html_attribute_name
-                                    * parsers.html_attribute_value_specification^-1
+parsers.html_non_newline_attribute
+  = parsers.spacechar^1
+  * parsers.html_attribute_name
+  * parsers.html_attribute_value_specification^-1
 
 parsers.nested_breaking_blank = parsers.newline
                               * parsers.check_minimal_blank_indent
@@ -27511,13 +28066,16 @@
 
 parsers.html_comment_end = P("-->")
 
-parsers.html_comment = Cs( parsers.html_comment_start
-                         * parsers.html_until_end(parsers.html_comment_end))
+parsers.html_comment
+  = Cs( parsers.html_comment_start
+      * parsers.html_until_end(parsers.html_comment_end))
 
 parsers.html_inline_comment = (parsers.html_comment_start / "")
                             * -P(">") * -P("->")
-                            * Cs((V("NoSoftLineBreakEndline") + parsers.any
-                                - parsers.nested_breaking_blank - parsers.html_comment_end)^0)
+                            * Cs(( V("NoSoftLineBreakEndline")
+                                 + parsers.any
+                                 - parsers.nested_breaking_blank
+                                 - parsers.html_comment_end)^0)
                             * (parsers.html_comment_end / "")
 
 parsers.html_cdatasection_start = P("<![CDATA[")
@@ -27524,36 +28082,43 @@
 
 parsers.html_cdatasection_end = P("]]>")
 
-parsers.html_cdatasection = Cs( parsers.html_cdatasection_start
-                              * parsers.html_until_end(parsers.html_cdatasection_end))
+parsers.html_cdatasection
+  = Cs( parsers.html_cdatasection_start
+      * parsers.html_until_end(parsers.html_cdatasection_end))
 
-parsers.html_inline_cdatasection  = parsers.html_cdatasection_start
-                                  * Cs(V("NoSoftLineBreakEndline") + parsers.any
-                                      - parsers.nested_breaking_blank - parsers.html_cdatasection_end)^0
-                                  * parsers.html_cdatasection_end
+parsers.html_inline_cdatasection
+  = parsers.html_cdatasection_start
+  * Cs(V("NoSoftLineBreakEndline") + parsers.any
+      - parsers.nested_breaking_blank - parsers.html_cdatasection_end)^0
+  * parsers.html_cdatasection_end
 
 parsers.html_declaration_start = P("<!") * parsers.letter
 
 parsers.html_declaration_end = P(">")
 
-parsers.html_declaration  = Cs( parsers.html_declaration_start
-                              * parsers.html_until_end(parsers.html_declaration_end))
+parsers.html_declaration
+  = Cs( parsers.html_declaration_start
+      * parsers.html_until_end(parsers.html_declaration_end))
 
-parsers.html_inline_declaration = parsers.html_declaration_start
-                                * Cs(V("NoSoftLineBreakEndline") + parsers.any
-                                    - parsers.nested_breaking_blank - parsers.html_declaration_end)^0
-                                * parsers.html_declaration_end
+parsers.html_inline_declaration
+  = parsers.html_declaration_start
+  * Cs(V("NoSoftLineBreakEndline") + parsers.any
+      - parsers.nested_breaking_blank - parsers.html_declaration_end)^0
+  * parsers.html_declaration_end
 
 parsers.html_instruction_start = P("<?")
 
 parsers.html_instruction_end = P("?>")
 
-parsers.html_instruction  = Cs( parsers.html_instruction_start
-                              * parsers.html_until_end(parsers.html_instruction_end))
+parsers.html_instruction
+  = Cs( parsers.html_instruction_start
+      * parsers.html_until_end(parsers.html_instruction_end))
 
 parsers.html_inline_instruction = parsers.html_instruction_start
-                                * Cs(V("NoSoftLineBreakEndline") + parsers.any
-                                    - parsers.nested_breaking_blank - parsers.html_instruction_end)^0
+                                * Cs( V("NoSoftLineBreakEndline")
+                                    + parsers.any
+                                    - parsers.nested_breaking_blank
+                                    - parsers.html_instruction_end)^0
                                 * parsers.html_instruction_end
 
 parsers.html_blankline  = parsers.newline
@@ -27599,25 +28164,28 @@
                                   + parsers.slash * parsers.more
                                   + #(parsers.newline + parsers.eof)
 
-parsers.incomplete_special_tag_following  = parsers.spacechar
-                                          + parsers.more
-                                          + #(parsers.newline + parsers.eof)
+parsers.incomplete_special_tag_following = parsers.spacechar
+                                         + parsers.more
+                                         + #( parsers.newline
+                                            + parsers.eof)
 
 parsers.html_incomplete_open_tag  = parsers.html_tag_start
                                   * parsers.block_keyword
                                   * parsers.incomplete_tag_following
 
-parsers.html_incomplete_open_special_tag  = parsers.html_tag_start
-                                          * parsers.special_block_keyword
-                                          * parsers.incomplete_special_tag_following
+parsers.html_incomplete_open_special_tag
+  = parsers.html_tag_start
+  * parsers.special_block_keyword
+  * parsers.incomplete_special_tag_following
 
 parsers.html_incomplete_close_tag = parsers.html_tag_closing_start
                                   * parsers.block_keyword
                                   * parsers.incomplete_tag_following
 
-parsers.html_incomplete_close_special_tag = parsers.html_tag_closing_start
-                                          * parsers.special_block_keyword
-                                          * parsers.incomplete_tag_following
+parsers.html_incomplete_close_special_tag
+  = parsers.html_tag_closing_start
+  * parsers.special_block_keyword
+  * parsers.incomplete_tag_following
 
 -- closing tags
 parsers.html_close_tag  = parsers.html_tag_closing_start
@@ -27655,27 +28223,33 @@
                                 * parsers.html_attribute^0
                                 * parsers.html_empty_tag_end
 
-parsers.html_incomplete_blocks  = parsers.html_incomplete_open_tag
-                                + parsers.html_incomplete_open_special_tag
-                                + parsers.html_incomplete_close_tag
+parsers.html_incomplete_blocks
+  = parsers.html_incomplete_open_tag
+  + parsers.html_incomplete_open_special_tag
+  + parsers.html_incomplete_close_tag
 
 -- parse special html blocks
-parsers.html_blankline_ending_special_block_opening = (parsers.html_close_special_tag
-                                                      + parsers.html_empty_special_tag)
-                                                    * #(parsers.optionalspace
-                                                       * (parsers.newline + parsers.eof))
+parsers.html_blankline_ending_special_block_opening
+  = ( parsers.html_close_special_tag
+    + parsers.html_empty_special_tag)
+  * #( parsers.optionalspace
+     * (parsers.newline + parsers.eof))
 
-parsers.html_blankline_ending_special_block = parsers.html_blankline_ending_special_block_opening
-                                            * parsers.html_blankline_end_condition
+parsers.html_blankline_ending_special_block
+  = parsers.html_blankline_ending_special_block_opening
+  * parsers.html_blankline_end_condition
 
-parsers.html_special_block_opening  = parsers.html_incomplete_open_special_tag
-                                    - parsers.html_empty_special_tag
+parsers.html_special_block_opening
+  = parsers.html_incomplete_open_special_tag
+  - parsers.html_empty_special_tag
 
-parsers.html_closing_special_block  = parsers.html_special_block_opening
-                                    * parsers.html_until_end(parsers.html_close_special_tag)
+parsers.html_closing_special_block
+  = parsers.html_special_block_opening
+  * parsers.html_until_end(parsers.html_close_special_tag)
 
-parsers.html_special_block  = parsers.html_blankline_ending_special_block
-                            + parsers.html_closing_special_block
+parsers.html_special_block
+  = parsers.html_blankline_ending_special_block
+  + parsers.html_closing_special_block
 
 -- parse html blocks
 parsers.html_block_opening  = parsers.html_incomplete_open_tag
@@ -27685,10 +28259,11 @@
                     * parsers.html_blankline_end_condition
 
 -- parse any html blocks
-parsers.html_any_block_opening  = (parsers.html_any_open_tag
-                                + parsers.html_any_close_tag
-                                + parsers.html_any_empty_tag)
-                                * #(parsers.optionalspace * (parsers.newline + parsers.eof))
+parsers.html_any_block_opening
+  = ( parsers.html_any_open_tag
+    + parsers.html_any_close_tag
+    + parsers.html_any_empty_tag)
+  * #(parsers.optionalspace * (parsers.newline + parsers.eof))
 
 parsers.html_any_block  = parsers.html_any_block_opening
                         * parsers.html_blankline_end_condition
@@ -27695,8 +28270,10 @@
 
 parsers.html_inline_comment_full  = parsers.html_comment_start
                                   * -P(">") * -P("->")
-                                  * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--")
-                                      - parsers.nested_breaking_blank - parsers.html_comment_end)^0)
+                                  * Cs(( V("NoSoftLineBreakEndline")
+                                       + parsers.any - P("--")
+                                       - parsers.nested_breaking_blank
+                                       - parsers.html_comment_end)^0)
                                   * parsers.html_comment_end
 
 parsers.html_inline_tags  = parsers.html_inline_comment_full
@@ -27729,7 +28306,8 @@
                           * parsers.auto_link_scheme_part^-30
 
 parsers.absolute_uri  = parsers.auto_link_scheme * parsers.colon
-                      * (parsers.any - parsers.spacing - parsers.less - parsers.more)^0
+                      * ( parsers.any - parsers.spacing
+                        - parsers.less - parsers.more)^0
 
 parsers.printable_characters = S(".!#$%&'*+/=?^_`{|}~-")
 
@@ -27736,14 +28314,17 @@
 parsers.email_address_local_part_char = parsers.alphanumeric
                                       + parsers.printable_characters
 
-parsers.email_address_local_part = parsers.email_address_local_part_char^1
+parsers.email_address_local_part
+  = parsers.email_address_local_part_char^1
 
 parsers.email_address_dns_label = parsers.alphanumeric
-                                * (parsers.alphanumeric + parsers.dash)^-62
+                                * ( parsers.alphanumeric
+                                  + parsers.dash)^-62
                                 * B(parsers.alphanumeric)
 
 parsers.email_address_domain  = parsers.email_address_dns_label
-                              * (parsers.period * parsers.email_address_dns_label)^0
+                              * ( parsers.period
+                                * parsers.email_address_dns_label)^0
 
 parsers.email_address = parsers.email_address_local_part
                       * parsers.at
@@ -27799,25 +28380,37 @@
                         + parsers.autolink
                         + V("InlineHtml")
                         + ( parsers.backslash * parsers.backslash)
-                        + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
+                        + ( parsers.backslash
+                          * ( parsers.lbracket
+                            + parsers.rbracket)
                           + V("NoSoftLineBreakSpace")
                           + V("NoSoftLineBreakEndline")
                           + (parsers.any
-                            - (parsers.newline + parsers.lbracket + parsers.rbracket + parsers.blankline^2))))^0)
+                            - ( parsers.newline
+                              + parsers.lbracket
+                              + parsers.rbracket
+                              + parsers.blankline^2))))^0)
                     * parsers.rbracket
 
 parsers.link_label_body = -#(parsers.sp * parsers.rbracket)
-                        * #((parsers.any - parsers.rbracket)^-999 * parsers.rbracket)
+                        * #( ( parsers.any
+                             - parsers.rbracket)^-999
+                           * parsers.rbracket)
                         * Cs((parsers.alphanumeric^1
                             + parsers.inticks
                             + parsers.autolink
                             + V("InlineHtml")
                             + ( parsers.backslash * parsers.backslash)
-                            + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
+                            + ( parsers.backslash
+                              * ( parsers.lbracket
+                                + parsers.rbracket)
                               + V("NoSoftLineBreakSpace")
                               + V("NoSoftLineBreakEndline")
                               + (parsers.any
-                                - (parsers.newline + parsers.lbracket + parsers.rbracket + parsers.blankline^2))))^1)
+                                - ( parsers.newline
+                                  + parsers.lbracket
+                                  + parsers.rbracket
+                                  + parsers.blankline^2))))^1)
 
 parsers.link_label  = parsers.lbracket
                     * parsers.link_label_body
@@ -27847,7 +28440,10 @@
                     * Cs((parsers.html_entities
                          + V("NoSoftLineBreakSpace")
                          + V("NoSoftLineBreakEndline")
-                         + (parsers.anyescaped - parsers.newline - parsers.squote - parsers.blankline^2))^0)
+                         + ( parsers.anyescaped
+                           - parsers.newline
+                           - parsers.squote
+                           - parsers.blankline^2))^0)
                     * parsers.squote
 
 parsers.title_d     = parsers.dquote
@@ -27854,7 +28450,10 @@
                     * Cs((parsers.html_entities
                          + V("NoSoftLineBreakSpace")
                          + V("NoSoftLineBreakEndline")
-                         + (parsers.anyescaped - parsers.newline - parsers.dquote - parsers.blankline^2))^0)
+                         + ( parsers.anyescaped
+                           - parsers.newline
+                           - parsers.dquote
+                           - parsers.blankline^2))^0)
                     * parsers.dquote
 
 parsers.title_p     = parsers.lparent
@@ -27861,15 +28460,18 @@
                     * Cs((parsers.html_entities
                          + V("NoSoftLineBreakSpace")
                          + V("NoSoftLineBreakEndline")
-                         + (parsers.anyescaped - parsers.newline - parsers.lparent - parsers.rparent
+                         + ( parsers.anyescaped
+                           - parsers.newline
+                           - parsers.lparent
+                           - parsers.rparent
                            - parsers.blankline^2))^0)
                     * parsers.rparent
 
-parsers.title       = parsers.title_d + parsers.title_s + parsers.title_p
+parsers.title
+  = parsers.title_d + parsers.title_s + parsers.title_p
 
 parsers.optionaltitle
-                    = parsers.spnlc * parsers.title * parsers.spacechar^0
-                    + Cc("")
+  = parsers.spnlc * parsers.title * parsers.spacechar^0 + Cc("")
 
 %    \end{macrocode}
 % \par
@@ -27880,9 +28482,11 @@
 % \end{markdown}
 %  \begin{macrocode}
 -- parse a reference definition:  [foo]: /bar "title"
-parsers.define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon
+parsers.define_reference_parser = (parsers.check_trail / "")
+                                * parsers.link_label * parsers.colon
                                 * parsers.spnlc * parsers.url
-                                * ( parsers.spnlc_sep * parsers.title * parsers.only_blank
+                                * ( parsers.spnlc_sep * parsers.title
+                                  * parsers.only_blank
                                   + Cc("") * parsers.only_blank)
 %    \end{macrocode}
 % \par
@@ -27897,7 +28501,10 @@
 -- 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)
+  return ( starter
+         * #parsers.nonspacechar
+         * Ct(p * (p - ender2)^0)
+         * ender2)
 end
 
 %    \end{macrocode}
@@ -27909,7 +28516,8 @@
 % \end{markdown}
 %  \begin{macrocode}
 parsers.lineof = function(c)
-    return (parsers.check_trail_no_rem * (P(c) * parsers.optionalspace)^3
+    return ( parsers.check_trail_no_rem
+           * (P(c) * parsers.optionalspace)^3
            * (parsers.newline + parsers.eof))
 end
 
@@ -27929,8 +28537,11 @@
                       * -parsers.hash / length
 
 -- parse setext header ending and return level
-parsers.heading_level = parsers.nonindentspace * parsers.equal^1 * parsers.optionalspace * #parsers.newline * Cc(1)
-                      + parsers.nonindentspace * parsers.dash^1 * parsers.optionalspace * #parsers.newline * Cc(2)
+parsers.heading_level
+  = parsers.nonindentspace * parsers.equal^1
+  * parsers.optionalspace * #parsers.newline * Cc(1)
+  + parsers.nonindentspace * parsers.dash^1
+  * parsers.optionalspace * #parsers.newline * Cc(2)
 
 local function strip_atx_end(s)
   return s:gsub("%s+#*%s*\n$","")
@@ -28102,9 +28713,11 @@
               local prefix = lpeg.match(C(parsers.optionalspace), line)
               local prefix_length = #prefix
               local is_shorter = min_prefix_length == nil
-              is_shorter = is_shorter or prefix_length < min_prefix_length
+              if not is_shorter then
+                is_shorter = prefix_length < min_prefix_length
+              end
               if is_shorter then
-                  min_prefix_length, min_prefix = prefix_length, prefix
+                min_prefix_length, min_prefix = prefix_length, prefix
               end
               return line
           end)
@@ -28126,7 +28739,8 @@
       end
       local res = lpeg.match(grammar(), str)
       if res == nil then
-        error(format("%s failed on:\n%s", name, str:sub(1,20)))
+        return writer.error(format("%s failed on:\n%s",
+                                   name, str:sub(1,20)))
       else
         return res
       end
@@ -28176,35 +28790,47 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  parsers.minimally_indented_blankline = parsers.check_minimal_indent * (parsers.blankline / "")
+  parsers.minimally_indented_blankline
+    = parsers.check_minimal_indent * (parsers.blankline / "")
 
-  parsers.minimally_indented_block = parsers.check_minimal_indent * V("Block")
+  parsers.minimally_indented_block
+    = parsers.check_minimal_indent * V("Block")
 
-  parsers.minimally_indented_block_or_paragraph = parsers.check_minimal_indent * V("BlockOrParagraph")
+  parsers.minimally_indented_block_or_paragraph
+    = parsers.check_minimal_indent * V("BlockOrParagraph")
 
-  parsers.minimally_indented_paragraph = parsers.check_minimal_indent * V("Paragraph")
+  parsers.minimally_indented_paragraph
+    = parsers.check_minimal_indent * V("Paragraph")
 
-  parsers.minimally_indented_plain = parsers.check_minimal_indent * V("Plain")
+  parsers.minimally_indented_plain
+    = parsers.check_minimal_indent * V("Plain")
 
-  parsers.minimally_indented_par_or_plain = parsers.minimally_indented_paragraph
-                                          + parsers.minimally_indented_plain
+  parsers.minimally_indented_par_or_plain
+    = parsers.minimally_indented_paragraph
+    + parsers.minimally_indented_plain
 
-  parsers.minimally_indented_par_or_plain_no_blank  = parsers.minimally_indented_par_or_plain
-                                                    - parsers.minimally_indented_blankline
+  parsers.minimally_indented_par_or_plain_no_blank
+    = parsers.minimally_indented_par_or_plain
+    - parsers.minimally_indented_blankline
 
-  parsers.minimally_indented_ref = parsers.check_minimal_indent * V("Reference")
+  parsers.minimally_indented_ref
+    = parsers.check_minimal_indent * V("Reference")
 
-  parsers.minimally_indented_blank = parsers.check_minimal_indent * V("Blank")
+  parsers.minimally_indented_blank
+    = parsers.check_minimal_indent * V("Blank")
 
-  parsers.conditionally_indented_blankline = parsers.check_minimal_blank_indent * (parsers.blankline / "")
+  parsers.conditionally_indented_blankline
+    = parsers.check_minimal_blank_indent * (parsers.blankline / "")
 
-  parsers.minimally_indented_ref_or_block = parsers.minimally_indented_ref
-                                          + parsers.minimally_indented_block
-                                          - parsers.minimally_indented_blankline
+  parsers.minimally_indented_ref_or_block
+    = parsers.minimally_indented_ref
+    + parsers.minimally_indented_block
+    - parsers.minimally_indented_blankline
 
-  parsers.minimally_indented_ref_or_block_or_par  = parsers.minimally_indented_ref
-                                                  + parsers.minimally_indented_block_or_paragraph
-                                                  - parsers.minimally_indented_blankline
+  parsers.minimally_indented_ref_or_block_or_par
+    = parsers.minimally_indented_ref
+    + parsers.minimally_indented_block_or_paragraph
+    - parsers.minimally_indented_blankline
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -28214,7 +28840,8 @@
 % \end{markdown}
 %  \begin{macrocode}
 
-  parsers.separator_loop = function(separated_block, paragraph, block_separator, paragraph_separator)
+  function parsers.separator_loop(separated_block, paragraph,
+                                  block_separator, paragraph_separator)
     return  separated_block
           + block_separator
             * paragraph
@@ -28223,10 +28850,14 @@
           * paragraph
   end
 
-  parsers.create_loop_body_pair = function(separated_block, paragraph, block_separator, paragraph_separator)
+  function parsers.create_loop_body_pair(separated_block, paragraph,
+                                         block_separator,
+                                         paragraph_separator)
     return {
-      block = parsers.separator_loop(separated_block, paragraph, block_separator, block_separator),
-      par = parsers.separator_loop(separated_block, paragraph, block_separator, paragraph_separator)
+      block = parsers.separator_loop(separated_block, paragraph,
+                                     block_separator, block_separator),
+      par = parsers.separator_loop(separated_block, paragraph,
+                                   block_separator, paragraph_separator)
     }
   end
 
@@ -28249,18 +28880,20 @@
 
   parsers.content_blank = parsers.minimally_indented_blankline
 
-  parsers.ref_or_block_separated  = parsers.sep_group_no_output(parsers.content_blank)
-                                  * ( parsers.minimally_indented_ref
-                                    - parsers.content_blank)
-                                  + parsers.block_sep_group(parsers.content_blank)
-                                  * ( parsers.minimally_indented_block
-                                    - parsers.content_blank)
+  parsers.ref_or_block_separated
+    = parsers.sep_group_no_output(parsers.content_blank)
+    * ( parsers.minimally_indented_ref
+      - parsers.content_blank)
+    + parsers.block_sep_group(parsers.content_blank)
+    * ( parsers.minimally_indented_block
+      - parsers.content_blank)
 
   parsers.loop_body_pair  =
-    parsers.create_loop_body_pair(parsers.ref_or_block_separated,
-                                  parsers.minimally_indented_par_or_plain_no_blank,
-                                  parsers.block_sep_group(parsers.content_blank),
-                                  parsers.par_sep_group(parsers.content_blank))
+    parsers.create_loop_body_pair(
+      parsers.ref_or_block_separated,
+      parsers.minimally_indented_par_or_plain_no_blank,
+      parsers.block_sep_group(parsers.content_blank),
+      parsers.par_sep_group(parsers.content_blank))
 
   parsers.content_loop  = ( V("Block")
                           * parsers.loop_body_pair.block^0
@@ -28284,7 +28917,8 @@
   parsers.add_indent = function(pattern, name, breakable)
     return  Cg(Cmt( Cb("indent_info")
                   * Ct(pattern)
-                  * (#parsers.linechar * Cc(false) + Cc(true)) -- check if starter is blank
+                  * ( #parsers.linechar  -- check if starter is blank
+                    * Cc(false) + Cc(true))
                   * Cc(name)
                   * Cc(breakable),
               process_starter_indent), "indent_info")
@@ -28312,7 +28946,8 @@
       allowed_end = C(parsers.spacechar^1) * #parsers.linechar
     else
       delimiter_range = parsers.dig * parsers.dig^-8
-      allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
+      allowed_end = C(parsers.spacechar^1)
+                  + #(parsers.newline + parsers.eof)
     end
 
     return parsers.check_trail
@@ -28334,16 +28969,21 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  parsers.blockquote_start = parsers.check_trail * C(parsers.more) * C(parsers.spacechar^0)
+  parsers.blockquote_start
+    = parsers.check_trail
+    * C(parsers.more)
+    * C(parsers.spacechar^0)
 
-  parsers.blockquote_body = parsers.add_indent(parsers.blockquote_start, "bq", true)
-                          * parsers.indented_content()
-                          * remove_indent("bq")
+  parsers.blockquote_body
+    = parsers.add_indent(parsers.blockquote_start, "bq", true)
+    * parsers.indented_content()
+    * remove_indent("bq")
 
   if not options.breakableBlockquotes then
-    parsers.blockquote_body = parsers.add_indent(parsers.blockquote_start, "bq", false)
-                            * parsers.indented_content()
-                            * remove_indent("bq")
+    parsers.blockquote_body
+      = parsers.add_indent(parsers.blockquote_start, "bq", false)
+      * parsers.indented_content()
+      * remove_indent("bq")
   end
 %    \end{macrocode}
 % \par
@@ -28357,7 +28997,8 @@
 %  \begin{macrocode}
   local function parse_content_part(content_part)
     local rope = util.rope_to_string(content_part)
-    local parsed = self.parser_functions.parse_inlines_no_link_or_emphasis(rope)
+    local parsed
+      = self.parser_functions.parse_inlines_no_link_or_emphasis(rope)
     parsed.indent_info = nil
     return parsed
   end
@@ -28369,38 +29010,41 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  local function collect_emphasis_content(t, opening_index, closing_index)
-    local content = {}
+  local collect_emphasis_content =
+    function(t, opening_index, closing_index)
+      local content = {}
 
-    local content_part = {}
-    for i = opening_index, closing_index do
-      local value = t[i]
+      local content_part = {}
+      for i = opening_index, closing_index do
+        local value = t[i]
 
-      if value.rendered ~= nil then
-        content[#content + 1] = parse_content_part(content_part)
-        content_part = {}
-        content[#content + 1] = value.rendered
-        value.rendered = nil
-      else
-        if value.type == "delimiter" and value.element == "emphasis" then
-          if value.is_active then
-            content_part[#content_part + 1] = string.rep(value.character, value.current_count)
+        if value.rendered ~= nil then
+          content[#content + 1] = parse_content_part(content_part)
+          content_part = {}
+          content[#content + 1] = value.rendered
+          value.rendered = nil
+        else
+          if value.type == "delimiter"
+              and value.element == "emphasis" then
+            if value.is_active then
+              content_part[#content_part + 1]
+                = string.rep(value.character, value.current_count)
+            end
+          else
+            content_part[#content_part + 1] = value.content
           end
-        else
-          content_part[#content_part + 1] = value.content
+          value.content = ''
+          value.is_active = false
         end
-        value.content = ''
-        value.is_active = false
       end
-    end
 
-    if next(content_part) ~= nil then
-      content[#content + 1] = parse_content_part(content_part)
+      if next(content_part) ~= nil then
+        content[#content + 1] = parse_content_part(content_part)
+      end
+
+      return content
     end
 
-    return content
-  end
-
 %    \end{macrocode}
 % \begin{markdown}
 %
@@ -28410,7 +29054,9 @@
 % \end{markdown}
 %  \begin{macrocode}
   local function fill_emph(t, opening_index, closing_index)
-    local content = collect_emphasis_content(t, opening_index + 1, closing_index - 1)
+    local content
+      = collect_emphasis_content(t, opening_index + 1,
+                                 closing_index - 1)
     t[opening_index + 1].is_active = true
     t[opening_index + 1].rendered = writer.emphasis(content)
   end
@@ -28424,7 +29070,9 @@
 % \end{markdown}
 %  \begin{macrocode}
   local function fill_strong(t, opening_index, closing_index)
-    local content = collect_emphasis_content(t, opening_index + 1, closing_index - 1)
+    local content
+      = collect_emphasis_content(t, opening_index + 1,
+                                 closing_index - 1)
     t[opening_index + 1].is_active = true
     t[opening_index + 1].rendered = writer.strong(content)
   end
@@ -28438,9 +29086,12 @@
 % \end{markdown}
 %  \begin{macrocode}
   local function breaks_three_rule(opening_delimiter, closing_delimiter)
-    return (opening_delimiter.is_closing or closing_delimiter.is_opening) and
-      ((opening_delimiter.original_count + closing_delimiter.original_count) % 3 == 0) and
-      (opening_delimiter.original_count % 3 ~= 0 or closing_delimiter.original_count % 3 ~= 0)
+    return ( opening_delimiter.is_closing
+          or closing_delimiter.is_opening)
+       and (( opening_delimiter.original_count
+            + closing_delimiter.original_count) % 3 == 0)
+       and ( opening_delimiter.original_count % 3 ~= 0
+          or closing_delimiter.original_count % 3 ~= 0)
   end
 
 %    \end{macrocode}
@@ -28452,7 +29103,8 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  local function find_emphasis_opener(t, bottom_index, latest_index, character, closing_delimiter)
+  local find_emphasis_opener = function(t, bottom_index, latest_index,
+                                        character, closing_delimiter)
     for i = latest_index, bottom_index, -1 do
       local value = t[i]
       if value.is_active and
@@ -28516,12 +29168,18 @@
       local is_opening = value.is_opening
       local closing_length_modulo_three = value.original_count % 3
 
-      local current_openers_bottom = openers_bottom[character][is_opening][closing_length_modulo_three + 1]
+      local current_openers_bottom
+        = openers_bottom[character][is_opening]
+                        [closing_length_modulo_three + 1]
 
-      local opener_position = find_emphasis_opener(t, current_openers_bottom, current_position - 1, character, value)
+      local opener_position
+        = find_emphasis_opener(t, current_openers_bottom,
+                               current_position - 1, character, value)
 
       if (opener_position == nil) then
-        openers_bottom[character][is_opening][closing_length_modulo_three + 1] = current_position
+        openers_bottom[character][is_opening]
+                      [closing_length_modulo_three + 1]
+          = current_position
         current_position = current_position + 1
         goto continue
       end
@@ -28531,7 +29189,8 @@
       local current_opening_count = opening_delimiter.current_count
       local current_closing_count = t[current_position].current_count
 
-      if (current_opening_count >= 2) and (current_closing_count >= 2) then
+      if (current_opening_count >= 2)
+        and (current_closing_count >= 2) then
         opening_delimiter.current_count = current_opening_count - 2
         t[current_position].current_count = current_closing_count - 2
         fill_strong(t, opener_position, current_position)
@@ -28615,15 +29274,19 @@
     return check_unicode_type(s, i, 0, 3, "%s")
   end
 
-  parsers.unicode_preceding_punctuation = B(parsers.escapable)
-                                        + Cmt(parsers.succeed, check_preceding_unicode_punctuation)
+  parsers.unicode_preceding_punctuation
+    = B(parsers.escapable)
+    + Cmt(parsers.succeed, check_preceding_unicode_punctuation)
 
-  parsers.unicode_preceding_whitespace = Cmt(parsers.succeed, check_preceding_unicode_whitespace)
+  parsers.unicode_preceding_whitespace
+    = Cmt(parsers.succeed, check_preceding_unicode_whitespace)
 
-  parsers.unicode_following_punctuation = #parsers.escapable
-                                        + Cmt(parsers.succeed, check_following_unicode_punctuation)
+  parsers.unicode_following_punctuation
+    = #parsers.escapable
+    + Cmt(parsers.succeed, check_following_unicode_punctuation)
 
-  parsers.unicode_following_whitespace = Cmt(parsers.succeed, check_following_unicode_whitespace)
+  parsers.unicode_following_whitespace
+    = Cmt(parsers.succeed, check_following_unicode_whitespace)
 
   parsers.delimiter_run = function(character)
     return  (B(parsers.backslash * character) + -B(character))
@@ -28633,12 +29296,14 @@
 
   parsers.left_flanking_delimiter_run = function(character)
     return  (B( parsers.any)
-              * (parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace)
+              * ( parsers.unicode_preceding_punctuation
+                + parsers.unicode_preceding_whitespace)
              + -B(parsers.any))
             * parsers.delimiter_run(character)
             * parsers.unicode_following_punctuation
             + parsers.delimiter_run(character)
-            * -#(parsers.unicode_following_punctuation + parsers.unicode_following_whitespace
+            * -#( parsers.unicode_following_punctuation
+                + parsers.unicode_following_whitespace
                 + parsers.eof)
   end
 
@@ -28645,37 +29310,44 @@
   parsers.right_flanking_delimiter_run = function(character)
     return  parsers.unicode_preceding_punctuation
           * parsers.delimiter_run(character)
-          * (parsers.unicode_following_punctuation + parsers.unicode_following_whitespace
+          * ( parsers.unicode_following_punctuation
+            + parsers.unicode_following_whitespace
             + parsers.eof)
           + (B(parsers.any)
-            * -(parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace))
+            * -( parsers.unicode_preceding_punctuation
+               + parsers.unicode_preceding_whitespace))
           * parsers.delimiter_run(character)
   end
 
   if options.underscores then
-    parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk)
-                      + (-#parsers.right_flanking_delimiter_run(parsers.underscore)
-                          + (parsers.unicode_preceding_punctuation
-                            * #parsers.right_flanking_delimiter_run(parsers.underscore)))
-                      * parsers.left_flanking_delimiter_run(parsers.underscore)
+    parsers.emph_start
+      = parsers.left_flanking_delimiter_run(parsers.asterisk)
+      + ( -#parsers.right_flanking_delimiter_run(parsers.underscore)
+        + ( parsers.unicode_preceding_punctuation
+          * #parsers.right_flanking_delimiter_run(parsers.underscore)))
+      * parsers.left_flanking_delimiter_run(parsers.underscore)
 
-    parsers.emph_end  = parsers.right_flanking_delimiter_run(parsers.asterisk)
-                      + (-#parsers.left_flanking_delimiter_run(parsers.underscore)
-                        + #(parsers.left_flanking_delimiter_run(parsers.underscore)
-                          * parsers.unicode_following_punctuation))
-                      * parsers.right_flanking_delimiter_run(parsers.underscore)
+    parsers.emph_end
+      = parsers.right_flanking_delimiter_run(parsers.asterisk)
+      + ( -#parsers.left_flanking_delimiter_run(parsers.underscore)
+        + #( parsers.left_flanking_delimiter_run(parsers.underscore)
+           * parsers.unicode_following_punctuation))
+      * parsers.right_flanking_delimiter_run(parsers.underscore)
   else
-    parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk)
+    parsers.emph_start
+      = parsers.left_flanking_delimiter_run(parsers.asterisk)
 
-    parsers.emph_end  = parsers.right_flanking_delimiter_run(parsers.asterisk)
+    parsers.emph_end
+      = parsers.right_flanking_delimiter_run(parsers.asterisk)
   end
 
-  parsers.emph_capturing_open_and_close = #parsers.emph_start * #parsers.emph_end
-                                        * Ct( Cg(Cc("delimiter"), "type")
-                                            * Cg(Cc("emphasis"), "element")
-                                            * Cg(C(parsers.emph_start), "content")
-                                            * Cg(Cc(true), "is_opening")
-                                            * Cg(Cc(true), "is_closing"))
+  parsers.emph_capturing_open_and_close
+    = #parsers.emph_start * #parsers.emph_end
+    * Ct( Cg(Cc("delimiter"), "type")
+        * Cg(Cc("emphasis"), "element")
+        * Cg(C(parsers.emph_start), "content")
+        * Cg(Cc(true), "is_opening")
+        * Cg(Cc(true), "is_closing"))
 
   parsers.emph_capturing_open = Ct( Cg(Cc("delimiter"), "type")
                                   * Cg(Cc("emphasis"), "element")
@@ -28765,17 +29437,24 @@
 
   parsers.title_s_direct_ref  = parsers.squote
                               * Cs((parsers.html_entities
-                                  + (parsers.anyescaped - parsers.squote - parsers.blankline^2))^0)
+                                  + ( parsers.anyescaped
+                                    - parsers.squote
+                                    - parsers.blankline^2))^0)
                               * parsers.squote
 
   parsers.title_d_direct_ref  = parsers.dquote
                               * Cs((parsers.html_entities
-                                  + (parsers.anyescaped - parsers.dquote - parsers.blankline^2))^0)
+                                  + ( parsers.anyescaped
+                                    - parsers.dquote
+                                    - parsers.blankline^2))^0)
                               * parsers.dquote
 
   parsers.title_p_direct_ref  = parsers.lparent
                               * Cs((parsers.html_entities
-                                  + (parsers.anyescaped - parsers.lparent - parsers.rparent - parsers.blankline^2))^0)
+                                  + ( parsers.anyescaped
+                                    - parsers.lparent
+                                    - parsers.rparent
+                                    - parsers.blankline^2))^0)
                               * parsers.rparent
 
   parsers.title_direct_ref  = parsers.title_s_direct_ref
@@ -28785,7 +29464,8 @@
   parsers.inline_direct_ref_inside  = parsers.lparent * parsers.spnl
                                     * Cg(parsers.url + Cc(""), "url")
                                     * parsers.spnl
-                                    * Cg(parsers.title_direct_ref + Cc(""), "title")
+                                    * Cg( parsers.title_direct_ref
+                                        + Cc(""), "title")
                                     * parsers.spnl * parsers.rparent
 
   parsers.inline_direct_ref = parsers.lparent * parsers.spnlc
@@ -28813,7 +29493,8 @@
                         * Cg(Cc("inline"), "link_type")
                         + #(parsers.exclamation * parsers.full_link)
                         * Cg(Cc("full"), "link_type")
-                        + #(parsers.exclamation * parsers.collapsed_link)
+                        + #( parsers.exclamation
+                           * parsers.collapsed_link)
                         * Cg(Cc("collapsed"), "link_type")
                         + #(parsers.exclamation * parsers.shortcut_link)
                         * Cg(Cc("shortcut"), "link_type")
@@ -28844,15 +29525,18 @@
 
   local inline_note_element = Cg(Cc("note"), "element")
                             * parsers.note_opening
-                            * Cg(parsers.circumflex * parsers.lbracket, "content")
+                            * Cg( parsers.circumflex
+                                * parsers.lbracket, "content")
 
   local image_element = Cg(Cc("image"), "element")
                       * parsers.image_opening
-                      * Cg(parsers.exclamation * parsers.lbracket, "content")
+                      * Cg( parsers.exclamation
+                          * parsers.lbracket, "content")
 
   local note_element  = Cg(Cc("note"), "element")
                       * parsers.raw_note_opening
-                      * Cg(parsers.lbracket * parsers.circumflex, "content")
+                      * Cg( parsers.lbracket
+                          * parsers.circumflex, "content")
 
   local link_element  = Cg(Cc("link"), "element")
                       * parsers.link_opening
@@ -28882,7 +29566,9 @@
                                   * Cg(Cc(false), "is_opening")
                                   * Cg(Cc(true), "is_closing")
                                   * ( Cg(Cc(true), "is_direct")
-                                    * Cg(parsers.rbracket * #parsers.inline_direct_ref, "content")
+                                    * Cg( parsers.rbracket
+                                        * #parsers.inline_direct_ref,
+                                         "content")
                                     + Cg(Cc(false), "is_direct")
                                     * Cg(parsers.rbracket, "content")))
 
@@ -28906,19 +29592,21 @@
                                   * -parsers.starter) / "")
                                 * parsers.spacechar^0 / "\n"
 
-  parsers.link_and_emph_content = Ct( Cg(Cc("content"), "type")
-                                    * Cg(Cs(( parsers.link_emph_precedence
-                                            + parsers.backslash * parsers.any
-                                            + parsers.link_and_emph_endline
-                                            + (parsers.linechar
-                                              - parsers.blankline^2
-                                              - parsers.link_image_open_or_close
-                                              - parsers.emph_open_or_close))^0), "content"))
+  parsers.link_and_emph_content
+    = Ct( Cg(Cc("content"), "type")
+        * Cg(Cs(( parsers.link_emph_precedence
+                + parsers.backslash * parsers.any
+                + parsers.link_and_emph_endline
+                + (parsers.linechar
+                  - parsers.blankline^2
+                  - parsers.link_image_open_or_close
+                  - parsers.emph_open_or_close))^0), "content"))
 
-  parsers.link_and_emph_table = (parsers.link_image_opening + parsers.emph_open)
-                              * parsers.link_and_emph_content
-                              * ((parsers.link_image_open_or_close + parsers.emph_open_or_close)
-                                * parsers.link_and_emph_content)^1
+  parsers.link_and_emph_table
+    = (parsers.link_image_opening + parsers.emph_open)
+    * parsers.link_and_emph_content
+    * ((parsers.link_image_open_or_close + parsers.emph_open_or_close)
+      * parsers.link_and_emph_content)^1
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -28948,7 +29636,9 @@
       local value = t[i]
       if value.type == "delimiter" and
          value.is_opening and
-         (value.element == "link" or value.element == "image" or value.element == "note")
+         ( value.element == "link"
+        or value.element == "image"
+        or value.element == "note")
          and not value.removed then
         if value.is_active then
           return i
@@ -29029,11 +29719,12 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  local function delete_parsed_content_in_range(t, opening_index, closing_index)
-    for i = opening_index, closing_index do
-      t[i].rendered = nil
+  local delete_parsed_content_in_range =
+    function(t, opening_index, closing_index)
+      for i = opening_index, closing_index do
+        t[i].rendered = nil
+      end
     end
-  end
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -29078,35 +29769,41 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  local function render_link_or_image(t, opening_index, closing_index, content_end_index, reference)
-    process_emphasis(t, opening_index, content_end_index)
-    local mapped = collect_emphasis_content(t, opening_index + 1, content_end_index - 1)
+  local render_link_or_image =
+    function(t, opening_index, closing_index, content_end_index,
+             reference)
+      process_emphasis(t, opening_index, content_end_index)
+      local mapped = collect_emphasis_content(t, opening_index + 1,
+                                              content_end_index - 1)
 
-    local rendered = {}
-    if (t[opening_index].element == "link") then
-      rendered = writer.link(mapped, reference.url, reference.title, reference.attributes)
-    end
+      local rendered = {}
+      if (t[opening_index].element == "link") then
+        rendered = writer.link(mapped, reference.url,
+                               reference.title, reference.attributes)
+      end
 
-    if (t[opening_index].element == "image") then
-      rendered = writer.image(mapped, reference.url, reference.title, reference.attributes)
-    end
+      if (t[opening_index].element == "image") then
+        rendered = writer.image(mapped, reference.url, reference.title,
+                                reference.attributes)
+      end
 
-    if (t[opening_index].element == "note") then
-      if (t[opening_index].link_type == "note_inline") then
-        rendered = writer.note(mapped)
+      if (t[opening_index].element == "note") then
+        if (t[opening_index].link_type == "note_inline") then
+          rendered = writer.note(mapped)
+        end
+        if (t[opening_index].link_type == "raw_note") then
+          rendered = writer.note(reference)
+        end
       end
-      if (t[opening_index].link_type == "raw_note") then
-        rendered = writer.note(reference)
-      end
+
+      t[opening_index].rendered = rendered
+      delete_parsed_content_in_range(t, opening_index + 1,
+                                     closing_index)
+      empty_content_in_range(t, opening_index, closing_index)
+      disable_previous_link_openers(t, opening_index)
+      disable_range(t, opening_index, closing_index)
     end
 
-    t[opening_index].rendered = rendered
-    delete_parsed_content_in_range(t, opening_index + 1, closing_index)
-    empty_content_in_range(t, opening_index, closing_index)
-    disable_previous_link_openers(t, opening_index)
-    disable_range(t, opening_index, closing_index)
-  end
-
 %    \end{macrocode}
 % \begin{markdown}
 %
@@ -29116,52 +29813,56 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  local function resolve_inline_following_content(t, closing_index, match_reference, match_link_attributes)
-    local content = ""
-    for i = closing_index + 1, #t do
-      content = content .. t[i].content
-    end
+  local resolve_inline_following_content =
+    function(t, closing_index, match_reference, match_link_attributes)
+      local content = ""
+      for i = closing_index + 1, #t do
+        content = content .. t[i].content
+      end
 
-    local matching_content = parsers.succeed
+      local matching_content = parsers.succeed
 
-    if match_reference then
-      matching_content = matching_content * parsers.inline_direct_ref_inside
-    end
+      if match_reference then
+        matching_content = matching_content
+                         * parsers.inline_direct_ref_inside
+      end
 
-    if match_link_attributes then
-      matching_content = matching_content * Cg(Ct(parsers.attributes^-1), "attributes")
-    end
+      if match_link_attributes then
+        matching_content = matching_content
+                         * Cg(Ct(parsers.attributes^-1), "attributes")
+      end
 
-    local matched = lpeg.match(Ct(matching_content * Cg(Cp(), "end_position")), content)
+      local matched = lpeg.match(Ct( matching_content
+                                   * Cg(Cp(), "end_position")), content)
 
-    local matched_count = matched.end_position - 1
-    for i = closing_index + 1, #t do
-      local value = t[i]
+      local matched_count = matched.end_position - 1
+      for i = closing_index + 1, #t do
+        local value = t[i]
 
-      local chars_left = matched_count
-      matched_count = matched_count - #value.content
+        local chars_left = matched_count
+        matched_count = matched_count - #value.content
 
-      if matched_count <= 0 then
-        value.content = value.content:sub(chars_left + 1)
-        break
+        if matched_count <= 0 then
+          value.content = value.content:sub(chars_left + 1)
+          break
+        end
+
+        value.content = ''
+        value.is_active = false
       end
 
-      value.content = ''
-      value.is_active = false
-    end
+      local attributes = matched.attributes
+      if attributes == nil or next(attributes) == nil then
+        attributes = nil
+      end
 
-    local attributes = matched.attributes
-    if attributes == nil or next(attributes) == nil then
-      attributes = nil
+      return {
+        url = matched.url or "",
+        title = matched.title or "",
+        attributes = attributes
+      }
     end
 
-    return {
-      url = matched.url or "",
-      title = matched.title or "",
-      attributes = attributes
-    }
-  end
-
 %    \end{macrocode}
 % \begin{markdown}
 %
@@ -29171,8 +29872,11 @@
 % \end{markdown}
 %  \begin{macrocode}
   local function resolve_inline_link(t, opening_index, closing_index)
-    local inline_content = resolve_inline_following_content(t, closing_index, true, t.match_link_attributes)
-    render_link_or_image(t, opening_index, closing_index, closing_index, inline_content)
+    local inline_content
+      = resolve_inline_following_content(t, closing_index, true,
+                                         t.match_link_attributes)
+    render_link_or_image(t, opening_index, closing_index,
+                         closing_index, inline_content)
   end
 
 %    \end{macrocode}
@@ -29183,10 +29887,14 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  local function resolve_note_inline_link(t, opening_index, closing_index)
-    local inline_content = resolve_inline_following_content(t, closing_index, false, false)
-    render_link_or_image(t, opening_index, closing_index, closing_index, inline_content)
-  end
+  local resolve_note_inline_link =
+    function(t, opening_index, closing_index)
+      local inline_content
+        = resolve_inline_following_content(t, closing_index,
+                                           false, false)
+      render_link_or_image(t, opening_index, closing_index,
+                           closing_index, inline_content)
+    end
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -29197,13 +29905,18 @@
 % \end{markdown}
 %  \begin{macrocode}
   local function resolve_shortcut_link(t, opening_index, closing_index)
-    local content = collect_link_content(t, opening_index + 1, closing_index - 1)
+    local content
+      = collect_link_content(t, opening_index + 1, closing_index - 1)
     local r = self.lookup_reference(content)
 
     if r then
-      local inline_content = resolve_inline_following_content(t, closing_index, false, t.match_link_attributes)
-      r.attributes = join_attributes(r.attributes, inline_content.attributes)
-      render_link_or_image(t, opening_index, closing_index, closing_index, r)
+      local inline_content
+        = resolve_inline_following_content(t, closing_index, false,
+                                           t.match_link_attributes)
+      r.attributes
+        = join_attributes(r.attributes, inline_content.attributes)
+      render_link_or_image(t, opening_index, closing_index,
+                           closing_index, r)
     end
   end
 
@@ -29216,12 +29929,14 @@
 % \end{markdown}
 %  \begin{macrocode}
   local function resolve_raw_note_link(t, opening_index, closing_index)
-    local content = collect_link_content(t, opening_index + 1, closing_index - 1)
+    local content
+      = collect_link_content(t, opening_index + 1, closing_index - 1)
     local r = self.lookup_note_reference(content)
 
     if r then
       local parsed_ref = self.parser_functions.parse_blocks_nested(r)
-      render_link_or_image(t, opening_index, closing_index, closing_index, parsed_ref)
+      render_link_or_image(t, opening_index, closing_index,
+                           closing_index, parsed_ref)
     end
   end
 
@@ -29234,15 +29949,22 @@
 % \end{markdown}
 %  \begin{macrocode}
   local function resolve_full_link(t, opening_index, closing_index)
-    local next_link_closing_index = find_next_link_closing_index(t, closing_index + 4)
-    local next_link_content = collect_link_content(t, closing_index + 3, next_link_closing_index - 1)
+    local next_link_closing_index
+      = find_next_link_closing_index(t, closing_index + 4)
+    local next_link_content
+      = collect_link_content(t, closing_index + 3,
+                             next_link_closing_index - 1)
     local r = self.lookup_reference(next_link_content)
 
     if r then
-      local inline_content = resolve_inline_following_content(t, next_link_closing_index, false,
-                                                              t.match_link_attributes)
-      r.attributes = join_attributes(r.attributes, inline_content.attributes)
-      render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r)
+      local inline_content
+        = resolve_inline_following_content(t, next_link_closing_index,
+                                           false,
+                                           t.match_link_attributes)
+      r.attributes
+        = join_attributes(r.attributes, inline_content.attributes)
+      render_link_or_image(t, opening_index, next_link_closing_index,
+                           closing_index, r)
     end
   end
 
@@ -29256,14 +29978,20 @@
 % \end{markdown}
 %  \begin{macrocode}
   local function resolve_collapsed_link(t, opening_index, closing_index)
-    local next_link_closing_index = find_next_link_closing_index(t, closing_index + 4)
-    local content = collect_link_content(t, opening_index + 1, closing_index - 1)
+    local next_link_closing_index
+      = find_next_link_closing_index(t, closing_index + 4)
+    local content
+      = collect_link_content(t, opening_index + 1, closing_index - 1)
     local r = self.lookup_reference(content)
 
     if r then
-      local inline_content = resolve_inline_following_content(t, closing_index, false, t.match_link_attributes)
-      r.attributes = join_attributes(r.attributes, inline_content.attributes)
-      render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r)
+      local inline_content
+        = resolve_inline_following_content(t, closing_index, false,
+                                           t.match_link_attributes)
+      r.attributes
+        = join_attributes(r.attributes, inline_content.attributes)
+      render_link_or_image(t, opening_index, next_link_closing_index,
+                           closing_index, r)
     end
   end
 
@@ -29285,7 +30013,9 @@
     for i,value in ipairs(t) do
       if not value.is_closing
           or value.type ~= "delimiter"
-          or not (value.element == "link" or value.element == "image" or value.element == "note")
+          or not ( value.element == "link"
+                or value.element == "image"
+                or value.element == "note")
           or value.removed then
         goto continue
       end
@@ -29343,7 +30073,8 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-  parsers.Str      = (parsers.normalchar * (parsers.normalchar + parsers.at)^0)
+  parsers.Str      = ( parsers.normalchar
+                     * (parsers.normalchar + parsers.at)^0)
                    / writer.string
 
   parsers.Symbol   = (parsers.backtick^1 + V("SpecialChar"))
@@ -29371,25 +30102,28 @@
     parsers.interrupting_bullets = parsers.fail
     parsers.interrupting_enumerators = parsers.fail
   else
-    parsers.interrupting_bullets  = parsers.bullet(parsers.dash, true)
-                                  + parsers.bullet(parsers.asterisk, true)
-                                  + parsers.bullet(parsers.plus, true)
+    parsers.interrupting_bullets
+      = parsers.bullet(parsers.dash, true)
+      + parsers.bullet(parsers.asterisk, true)
+      + parsers.bullet(parsers.plus, true)
 
-    parsers.interrupting_enumerators  = parsers.enumerator(parsers.period, true)
-                                      + parsers.enumerator(parsers.rparent, true)
+    parsers.interrupting_enumerators
+      = parsers.enumerator(parsers.period, true)
+      + parsers.enumerator(parsers.rparent, true)
   end
 
   if options.html then
-    parsers.html_interrupting = parsers.check_trail
-                              * ( parsers.html_incomplete_open_tag
-                                + parsers.html_incomplete_close_tag
-                                + parsers.html_incomplete_open_special_tag
-                                + parsers.html_comment_start
-                                + parsers.html_cdatasection_start
-                                + parsers.html_declaration_start
-                                + parsers.html_instruction_start
-                                - parsers.html_close_special_tag
-                                - parsers.html_empty_special_tag)
+    parsers.html_interrupting
+      = parsers.check_trail
+      * ( parsers.html_incomplete_open_tag
+        + parsers.html_incomplete_close_tag
+        + parsers.html_incomplete_open_special_tag
+        + parsers.html_comment_start
+        + parsers.html_cdatasection_start
+        + parsers.html_declaration_start
+        + parsers.html_instruction_start
+        - parsers.html_close_special_tag
+        - parsers.html_empty_special_tag)
   else
     parsers.html_interrupting = parsers.fail
   end
@@ -29411,7 +30145,7 @@
                     * -V("EndlineExceptions")
                     + parsers.check_optional_indent
                     * -V("EndlineExceptions")
-                    * -parsers.starter)
+                    * -parsers.starter) / function(_) return end
                   * parsers.spacechar^0
 
   parsers.Endline = parsers.endline
@@ -29437,30 +30171,37 @@
 
   parsers.Space      = parsers.spacechar^2 * parsers.Endline
                                            / writer.hard_line_break
-                     + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / self.expandtabs
+                     + parsers.spacechar^1
+                     * parsers.Endline^-1
+                     * parsers.eof / self.expandtabs
                      + parsers.spacechar^1 * parsers.Endline
                                            / writer.soft_line_break
-                     + parsers.spacechar^1 * -parsers.newline / self.expandtabs
+                     + parsers.spacechar^1
+                     * -parsers.newline / self.expandtabs
 
   parsers.NoSoftLineBreakSpace
                      = parsers.spacechar^2 * parsers.Endline
                                            / writer.hard_line_break
-                     + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / self.expandtabs
+                     + parsers.spacechar^1
+                     * parsers.Endline^-1
+                     * parsers.eof / self.expandtabs
                      + parsers.spacechar^1 * parsers.Endline
                                            / writer.soft_line_break
-                     + parsers.spacechar^1 * -parsers.newline / self.expandtabs
+                     + parsers.spacechar^1
+                     * -parsers.newline / self.expandtabs
 
   parsers.NonbreakingEndline
                     = parsers.endline
-                    / writer.soft_line_break
+                    / writer.nbsp
 
   parsers.NonbreakingSpace
-                  = parsers.spacechar^2 * parsers.Endline
-                                        / writer.hard_line_break
-                  + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / ""
-                  + parsers.spacechar^1 * parsers.Endline
+                  = parsers.spacechar^2 * parsers.endline
+                                        / writer.nbsp
+                  + parsers.spacechar^1
+                  * parsers.endline^-1 * parsers.eof / ""
+                  + parsers.spacechar^1 * parsers.endline
                                         * parsers.optionalspace
-                                        / writer.soft_line_break
+                                        / writer.nbsp
                   + parsers.spacechar^1 * parsers.optionalspace
                                         / writer.nbsp
 
@@ -29509,18 +30250,20 @@
   parsers.LinkAndEmph = Ct(parsers.link_and_emph_table)
                       / self.defer_link_and_emphasis_processing
 
-  parsers.EscapedChar   = parsers.backslash * C(parsers.escapable) / writer.string
+  parsers.EscapedChar = parsers.backslash
+                      * C(parsers.escapable) / writer.string
 
-  parsers.InlineHtml    = Cs(parsers.html_inline_comment) / writer.inline_html_comment
-                        + Cs(parsers.html_any_empty_inline_tag
-                            + parsers.html_inline_instruction
-                            + parsers.html_inline_cdatasection
-                            + parsers.html_inline_declaration
-                            + parsers.html_any_open_inline_tag
-                            + parsers.html_any_close_tag)
-                          / writer.inline_html_tag
+  parsers.InlineHtml = Cs(parsers.html_inline_comment)
+                     / writer.inline_html_comment
+                     + Cs(parsers.html_any_empty_inline_tag
+                         + parsers.html_inline_instruction
+                         + parsers.html_inline_cdatasection
+                         + parsers.html_inline_declaration
+                         + parsers.html_any_open_inline_tag
+                         + parsers.html_any_close_tag)
+                       / writer.inline_html_tag
 
-  parsers.HtmlEntity    = parsers.html_entities / writer.string
+  parsers.HtmlEntity = parsers.html_entities / writer.string
 %    \end{macrocode}
 % \par
 % \begin{markdown}
@@ -29539,15 +30282,18 @@
                             + parsers.html_declaration))
                         / writer.block_html_element
 
-  parsers.indented_non_blank_line = parsers.indentedline - parsers.blankline
+  parsers.indented_non_blank_line = parsers.indentedline
+                                  - parsers.blankline
 
-  parsers.Verbatim  = Cs(
-                        parsers.check_code_trail
-                      * (parsers.line - parsers.blankline)
-                      * ((parsers.check_minimal_blank_indent_and_full_code_trail * parsers.blankline)^0
-                        * ((parsers.check_minimal_indent / "") * parsers.check_code_trail
-                           * (parsers.line - parsers.blankline))^1)^0
-                      ) / self.expandtabs / writer.verbatim
+  parsers.Verbatim
+    = Cs( parsers.check_code_trail
+        * (parsers.line - parsers.blankline)
+        * (( parsers.check_minimal_blank_indent_and_full_code_trail
+           * parsers.blankline)^0
+          * ( (parsers.check_minimal_indent / "")
+            * parsers.check_code_trail
+            * (parsers.line - parsers.blankline))^1)^0)
+    / self.expandtabs / writer.verbatim
 
   parsers.Blockquote   = parsers.blockquote_body
                        / writer.blockquote
@@ -29586,73 +30332,79 @@
 
   parsers.list_blank = parsers.conditionally_indented_blankline
 
-  parsers.ref_or_block_list_separated = parsers.sep_group_no_output(parsers.list_blank)
-                                      * parsers.minimally_indented_ref
-                                      + parsers.block_sep_group(parsers.list_blank)
-                                      * parsers.minimally_indented_block
+  parsers.ref_or_block_list_separated
+    = parsers.sep_group_no_output(parsers.list_blank)
+    * parsers.minimally_indented_ref
+    + parsers.block_sep_group(parsers.list_blank)
+    * parsers.minimally_indented_block
 
-  parsers.ref_or_block_non_separated  = parsers.minimally_indented_ref
-                                      + (parsers.succeed / writer.interblocksep)
-                                      * parsers.minimally_indented_block
-                                      - parsers.minimally_indented_blankline
+  parsers.ref_or_block_non_separated
+    = parsers.minimally_indented_ref
+    + (parsers.succeed / writer.interblocksep)
+    * parsers.minimally_indented_block
+    - parsers.minimally_indented_blankline
 
-  parsers.tight_list_loop_body_pair  =
-    parsers.create_loop_body_pair(parsers.ref_or_block_non_separated,
-                                  parsers.minimally_indented_par_or_plain_no_blank,
-                                  (parsers.succeed / writer.interblocksep),
-                                  (parsers.succeed / writer.paragraphsep))
+  parsers.tight_list_loop_body_pair =
+    parsers.create_loop_body_pair(
+      parsers.ref_or_block_non_separated,
+      parsers.minimally_indented_par_or_plain_no_blank,
+      (parsers.succeed / writer.interblocksep),
+      (parsers.succeed / writer.paragraphsep))
 
-  parsers.loose_list_loop_body_pair  =
-    parsers.create_loop_body_pair(parsers.ref_or_block_list_separated,
-                                  parsers.minimally_indented_par_or_plain,
-                                  parsers.block_sep_group(parsers.list_blank),
-                                  parsers.par_sep_group(parsers.list_blank))
+  parsers.loose_list_loop_body_pair =
+    parsers.create_loop_body_pair(
+      parsers.ref_or_block_list_separated,
+      parsers.minimally_indented_par_or_plain,
+      parsers.block_sep_group(parsers.list_blank),
+      parsers.par_sep_group(parsers.list_blank))
 
-  parsers.tight_list_content_loop = V("Block")
-                                  * parsers.tight_list_loop_body_pair.block^0
-                                  + (V("Paragraph") + V("Plain"))
-                                  * parsers.ref_or_block_non_separated
-                                  * parsers.tight_list_loop_body_pair.block^0
-                                  +  (V("Paragraph") + V("Plain"))
-                                  * parsers.tight_list_loop_body_pair.par^0
+  parsers.tight_list_content_loop
+    = V("Block")
+    * parsers.tight_list_loop_body_pair.block^0
+    + (V("Paragraph") + V("Plain"))
+    * parsers.ref_or_block_non_separated
+    * parsers.tight_list_loop_body_pair.block^0
+    +  (V("Paragraph") + V("Plain"))
+    * parsers.tight_list_loop_body_pair.par^0
 
-  parsers.loose_list_content_loop = V("Block")
-                                  * parsers.loose_list_loop_body_pair.block^0
-                                  + (V("Paragraph") + V("Plain"))
-                                  * parsers.ref_or_block_list_separated
-                                  * parsers.loose_list_loop_body_pair.block^0
-                                  + (V("Paragraph") + V("Plain"))
-                                  * parsers.loose_list_loop_body_pair.par^0
+  parsers.loose_list_content_loop
+    = V("Block")
+    * parsers.loose_list_loop_body_pair.block^0
+    + (V("Paragraph") + V("Plain"))
+    * parsers.ref_or_block_list_separated
+    * parsers.loose_list_loop_body_pair.block^0
+    + (V("Paragraph") + V("Plain"))
+    * parsers.loose_list_loop_body_pair.par^0
 
-  parsers.list_item_tightness_condition = -#( parsers.list_blank^0
-                                            * parsers.minimally_indented_ref_or_block_or_par)
-                                        * remove_indent("li")
-                                        + remove_indent("li")
-                                        * parsers.fail
+  parsers.list_item_tightness_condition
+    = -#( parsers.list_blank^0
+        * parsers.minimally_indented_ref_or_block_or_par)
+    * remove_indent("li")
+    + remove_indent("li")
+    * parsers.fail
 
-  parsers.indented_content_tight  = Ct( (parsers.blankline / "")
-                                      * #parsers.list_blank
-                                      * remove_indent("li")
-                                      + ( (V("Reference") + (parsers.blankline / ""))
-                                        * parsers.check_minimal_indent
-                                        * parsers.tight_list_content_loop
-                                        + (V("Reference") + (parsers.blankline / ""))
-                                        + (parsers.tickbox^-1 / writer.escape)
-                                        * parsers.tight_list_content_loop
-                                        )
-                                      * parsers.list_item_tightness_condition
-                                  )
+  parsers.indented_content_tight
+    = Ct( (parsers.blankline / "")
+        * #parsers.list_blank
+        * remove_indent("li")
+        + ( (V("Reference") + (parsers.blankline / ""))
+          * parsers.check_minimal_indent
+          * parsers.tight_list_content_loop
+          + (V("Reference") + (parsers.blankline / ""))
+          + (parsers.tickbox^-1 / writer.escape)
+          * parsers.tight_list_content_loop
+          )
+        * parsers.list_item_tightness_condition)
 
-  parsers.indented_content_loose  = Ct( (parsers.blankline / "")
-                                      * #parsers.list_blank
-                                      + ( (V("Reference") + (parsers.blankline / ""))
-                                        * parsers.check_minimal_indent
-                                        * parsers.loose_list_content_loop
-                                        + (V("Reference") + (parsers.blankline / ""))
-                                        + (parsers.tickbox^-1 / writer.escape)
-                                        * parsers.loose_list_content_loop
-                                        )
-                                  )
+  parsers.indented_content_loose
+    = Ct( (parsers.blankline / "")
+        * #parsers.list_blank
+        + ( (V("Reference") + (parsers.blankline / ""))
+          * parsers.check_minimal_indent
+          * parsers.loose_list_content_loop
+          + (V("Reference") + (parsers.blankline / ""))
+          + (parsers.tickbox^-1 / writer.escape)
+          * parsers.loose_list_content_loop))
 
   parsers.TightListItem = function(starter)
     return  -parsers.ThematicBreak
@@ -29710,13 +30462,15 @@
     local enumerator = parsers.enumerator(delimiter_type)
     return  Cg(enumerator, "listtype")
           * (Ct( parsers.TightListItem(Cb("listtype"))
-               * ((parsers.check_minimal_indent / "") * parsers.TightListItem(enumerator))^0)
+               * ( (parsers.check_minimal_indent / "")
+                 * parsers.TightListItem(enumerator))^0)
           * Cc(true)
           * -#((parsers.list_blank^0 / "")
               * parsers.check_minimal_indent * enumerator)
           + Ct( parsers.LooseListItem(Cb("listtype"))
               * ((parsers.list_blank^0 / "")
-                * (parsers.check_minimal_indent / "") * parsers.LooseListItem(enumerator))^0)
+                * (parsers.check_minimal_indent / "")
+                * parsers.LooseListItem(enumerator))^0)
           * Cc(false)
           ) * Ct(Cb("listtype")) / ordered_list
   end
@@ -29769,15 +30523,21 @@
                         - parsers.thematic_break_lines
 
   parsers.heading_text = parsers.heading_line
-                       * ((V("Endline") / "\n") * (parsers.heading_line - parsers.heading_level))^0
+                       * ( (V("Endline") / "\n")
+                         * ( parsers.heading_line
+                           - parsers.heading_level))^0
                        * parsers.newline^-1
 
-  parsers.SetextHeading = parsers.freeze_trail * parsers.check_trail_no_rem
-                        * #(parsers.heading_text
-                           * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level)
+  parsers.SetextHeading = parsers.freeze_trail
+                        * parsers.check_trail_no_rem
+                        * #( parsers.heading_text
+                           * parsers.check_minimal_indent
+                           * parsers.check_trail
+                           * parsers.heading_level)
                         * Cs(parsers.heading_text)
                         / parsers.parse_heading_text
-                        * parsers.check_minimal_indent_and_trail * parsers.heading_level
+                        * parsers.check_minimal_indent_and_trail
+                        * parsers.heading_level
                         * parsers.newline
                         * parsers.unfreeze_trail
                         / writer.heading
@@ -29829,14 +30589,18 @@
     local current_extension_name = nil
     self.insert_pattern = function(selector, pattern, pattern_name)
       assert(pattern_name == nil or type(pattern_name) == "string")
-      local _, _, lhs, pos, rhs = selector:find("^(%a+)%s+([%a%s]+%a+)%s+(%a+)$")
+      local _, _, lhs, pos, rhs
+        = selector:find("^(%a+)%s+([%a%s]+%a+)%s+(%a+)$")
       assert(lhs ~= nil,
-        [[Expected selector in form "LHS (before|after|instead of) RHS", not "]]
+        [[Expected selector in form ]]
+        .. [["LHS (before|after|instead of) RHS", not "]]
         .. selector .. [["]])
       assert(walkable_syntax[lhs] ~= nil,
-        [[Rule ]] .. lhs .. [[ -> ... does not exist in markdown grammar]])
+        [[Rule ]] .. lhs
+        .. [[ -> ... does not exist in markdown grammar]])
       assert(pos == "before" or pos == "after" or pos == "instead of",
-        [[Expected positional specifier "before", "after", or "instead of", not "]]
+        [[Expected positional specifier "before", "after", ]]
+        .. [[or "instead of", not "]]
         .. pos .. [["]])
       local rule = walkable_syntax[lhs]
       local index = nil
@@ -29854,10 +30618,12 @@
           .. [[ does not exist in markdown grammar]])
       local accountable_pattern
       if current_extension_name then
-        accountable_pattern = { pattern, current_extension_name, pattern_name }
+        accountable_pattern
+          = {pattern, current_extension_name, pattern_name}
       else
         assert(type(pattern) == "string",
-          [[reader->insert_pattern() was called outside an extension with ]]
+          [[reader->insert_pattern() was called outside ]]
+          .. [[an extension with ]]
           .. [[a PEG pattern instead of a rule name]])
         accountable_pattern = pattern
       end
@@ -29880,11 +30646,9 @@
     local syntax =
       { "Blocks",
 
-        Blocks                = V("InitializeState")
-                              * ( V("ExpectedJekyllData")
-                                * (V("Blank")^0 / writer.interblocksep)
-                                )^-1
-                              * V("Blank")^0
+        Blocks = V("InitializeState")
+               * V("ExpectedJekyllData")
+               * V("Blank")^0
 %    \end{macrocode}
 % \par
 % \begin{markdown}
@@ -29895,71 +30659,72 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-                              * ( V("Block")
-                                * ( V("Blank")^0 * parsers.eof
-                                  + ( V("Blank")^2 / writer.paragraphsep
-                                    + V("Blank")^0 / writer.interblocksep
-                                    )
-                                  )
-                                + ( V("Paragraph") + V("Plain") )
-                                * ( V("Blank")^0 * parsers.eof
-                                  + ( V("Blank")^2 / writer.paragraphsep
-                                    + V("Blank")^0 / writer.interblocksep
-                                    )
-                                  )
-                                * V("Block")
-                                * ( V("Blank")^0 * parsers.eof
-                                  + ( V("Blank")^2 / writer.paragraphsep
-                                    + V("Blank")^0 / writer.interblocksep
-                                    )
-                                  )
-                                + ( V("Paragraph") + V("Plain") )
-                                * ( V("Blank")^0 * parsers.eof
-                                  + V("Blank")^0 / writer.paragraphsep
-                                  )
-                                )^0,
+               * ( V("Block")
+                 * ( V("Blank")^0 * parsers.eof
+                   + ( V("Blank")^2 / writer.paragraphsep
+                     + V("Blank")^0 / writer.interblocksep
+                     )
+                   )
+                 + ( V("Paragraph") + V("Plain") )
+                 * ( V("Blank")^0 * parsers.eof
+                   + ( V("Blank")^2 / writer.paragraphsep
+                     + V("Blank")^0 / writer.interblocksep
+                     )
+                   )
+                 * V("Block")
+                 * ( V("Blank")^0 * parsers.eof
+                   + ( V("Blank")^2 / writer.paragraphsep
+                     + V("Blank")^0 / writer.interblocksep
+                     )
+                   )
+                 + ( V("Paragraph") + V("Plain") )
+                 * ( V("Blank")^0 * parsers.eof
+                   + V("Blank")^0 / writer.paragraphsep
+                   )
+                 )^0,
 
-        ExpectedJekyllData    = parsers.fail,
+        ExpectedJekyllData = parsers.succeed,
 
-        Blank                 = parsers.Blank,
-        Reference             = parsers.Reference,
+        Blank              = parsers.Blank,
+        Reference          = parsers.Reference,
 
-        Blockquote            = parsers.Blockquote,
-        Verbatim              = parsers.Verbatim,
-        ThematicBreak         = parsers.ThematicBreak,
-        BulletList            = parsers.BulletList,
-        OrderedList           = parsers.OrderedList,
-        DisplayHtml           = parsers.DisplayHtml,
-        Heading               = parsers.Heading,
-        Paragraph             = parsers.Paragraph,
-        Plain                 = parsers.Plain,
+        Blockquote         = parsers.Blockquote,
+        Verbatim           = parsers.Verbatim,
+        ThematicBreak      = parsers.ThematicBreak,
+        BulletList         = parsers.BulletList,
+        OrderedList        = parsers.OrderedList,
+        DisplayHtml        = parsers.DisplayHtml,
+        Heading            = parsers.Heading,
+        Paragraph          = parsers.Paragraph,
+        Plain              = parsers.Plain,
 
-        EndlineExceptions     = parsers.EndlineExceptions,
+        EndlineExceptions  = parsers.EndlineExceptions,
         NoSoftLineBreakEndlineExceptions
-                              = parsers.NoSoftLineBreakEndlineExceptions,
+                           = parsers.NoSoftLineBreakEndlineExceptions,
 
-        Str                   = parsers.Str,
-        Space                 = parsers.Space,
-        NoSoftLineBreakSpace  = parsers.NoSoftLineBreakSpace,
-        OptionalIndent        = parsers.OptionalIndent,
-        Endline               = parsers.Endline,
-        EndlineNoSub          = parsers.EndlineNoSub,
+        Str                = parsers.Str,
+        Space              = parsers.Space,
+        NoSoftLineBreakSpace
+                           = parsers.NoSoftLineBreakSpace,
+        OptionalIndent     = parsers.OptionalIndent,
+        Endline            = parsers.Endline,
+        EndlineNoSub       = parsers.EndlineNoSub,
         NoSoftLineBreakEndline
-                              = parsers.NoSoftLineBreakEndline,
-        EndlineBreak          = parsers.EndlineBreak,
-        LinkAndEmph           = parsers.LinkAndEmph,
-        Code                  = parsers.Code,
-        AutoLinkUrl           = parsers.AutoLinkUrl,
-        AutoLinkEmail         = parsers.AutoLinkEmail,
+                           = parsers.NoSoftLineBreakEndline,
+        EndlineBreak       = parsers.EndlineBreak,
+        LinkAndEmph        = parsers.LinkAndEmph,
+        Code               = parsers.Code,
+        AutoLinkUrl        = parsers.AutoLinkUrl,
+        AutoLinkEmail      = parsers.AutoLinkEmail,
         AutoLinkRelativeReference
-                              = parsers.AutoLinkRelativeReference,
-        InlineHtml            = parsers.InlineHtml,
-        HtmlEntity            = parsers.HtmlEntity,
-        EscapedChar           = parsers.EscapedChar,
-        Smart                 = parsers.Smart,
-        Symbol                = parsers.Symbol,
-        SpecialChar           = parsers.fail,
-        InitializeState       = parsers.succeed,
+                           = parsers.AutoLinkRelativeReference,
+        InlineHtml         = parsers.InlineHtml,
+        HtmlEntity         = parsers.HtmlEntity,
+        EscapedChar        = parsers.EscapedChar,
+        Smart              = parsers.Smart,
+        Symbol             = parsers.Symbol,
+        SpecialChar        = parsers.fail,
+        InitializeState    = parsers.succeed,
       }
 %    \end{macrocode}
 % \par
@@ -29977,13 +30742,17 @@
     self.update_rule = function(rule_name, get_pattern)
       assert(current_extension_name ~= nil)
       assert(syntax[rule_name] ~= nil,
-        [[Rule ]] .. rule_name .. [[ -> ... does not exist in markdown grammar]])
+        [[Rule ]] .. rule_name
+        .. [[ -> ... does not exist in markdown grammar]])
       local previous_pattern
       local extension_name
       if walkable_syntax[rule_name] then
-        local previous_accountable_pattern = walkable_syntax[rule_name][1]
+        local previous_accountable_pattern
+          = walkable_syntax[rule_name][1]
         previous_pattern = previous_accountable_pattern[1]
-        extension_name = previous_accountable_pattern[2] .. ", " .. current_extension_name
+        extension_name
+          = previous_accountable_pattern[2]
+          .. ", " .. current_extension_name
       else
         previous_pattern = nil
         extension_name = current_extension_name
@@ -30113,9 +30882,11 @@
               pattern_name = "Anonymous Pattern"
             end
             local extension_name = rhs[2]
-            human_readable_rhs = pattern_name .. [[ (]] .. extension_name .. [[)]]
+            human_readable_rhs = pattern_name .. [[ (]]
+                               .. extension_name .. [[)]]
           end
-          local encoded_rhs = util.encode_json_string(human_readable_rhs)
+          local encoded_rhs
+            = util.encode_json_string(human_readable_rhs)
           local output_line = [[        ]] .. encoded_rhs
           if rhs_index < #rule then
             output_line = output_line .. ","
@@ -30133,7 +30904,8 @@
       local output = table.concat(output_lines, "\n")
       local output_filename = options.debugExtensionsFileName
       local output_file = assert(io.open(output_filename, "w"),
-        [[Could not open file "]] .. output_filename .. [[" for writing]])
+        [[Could not open file "]] .. output_filename
+        .. [[" for writing]])
       assert(output_file:write(output))
       assert(output_file:close())
     end
@@ -30226,7 +30998,7 @@
     end
 
     local blocks_nested_t = util.table_copy(syntax)
-    blocks_nested_t.ExpectedJekyllData = parsers.fail
+    blocks_nested_t.ExpectedJekyllData = parsers.succeed
     parsers.blocks_nested = Ct(blocks_nested_t)
 
     parsers.blocks = Ct(syntax)
@@ -30256,8 +31028,10 @@
 
     local inlines_no_link_or_emphasis_t = util.table_copy(inlines_t)
     inlines_no_link_or_emphasis_t.LinkAndEmph = parsers.fail
-    inlines_no_link_or_emphasis_t.EndlineExceptions = parsers.EndlineExceptions - parsers.eof
-    parsers.inlines_no_link_or_emphasis = Ct(inlines_no_link_or_emphasis_t)
+    inlines_no_link_or_emphasis_t.EndlineExceptions
+      = parsers.EndlineExceptions - parsers.eof
+    parsers.inlines_no_link_or_emphasis
+      = Ct(inlines_no_link_or_emphasis_t)
 %    \end{macrocode}
 % \par
 % \begin{markdown}
@@ -30284,7 +31058,8 @@
         elseif form == "nfkd" then
           input = uni_algos.normalize.NFKD(input)
         else
-          error(format("Unknown normalization form %s", form))
+          return writer.error(
+            format("Unknown normalization form %s", form))
         end
       end
 %    \end{macrocode}
@@ -30300,30 +31075,12 @@
       end
 %    \end{macrocode}
 % \begin{markdown}
-% When determining the name of the cache file, create salt for the hashing
-% function out of the package version and the passed options recognized by the
-% Lua interface (see Section <#sec:lua-options>). The \Opt{cacheDir} option
-% is disregarded.
+% Clear the table of references.
 % \end{markdown}
 %  \begin{macrocode}
       references = {}
-      local opt_string = {}
-      for k, _ in pairs(defaultOptions) do
-        local v = options[k]
-        if type(v) == "table" then
-          for _, i in ipairs(v) do
-            opt_string[#opt_string+1] = k .. "=" .. tostring(i)
-          end
-        elseif k ~= "cacheDir" then
-          opt_string[#opt_string+1] = k .. "=" .. tostring(v)
-        end
-      end
-      table.sort(opt_string)
-      local salt = table.concat(opt_string, ",") .. "," .. metadata.version
-      local output
-      local function convert(input)
-        local document = self.parser_functions.parse_blocks(input)
-        local output = util.rope_to_string(writer.document(document))
+      local document = self.parser_functions.parse_blocks(input)
+      local output = util.rope_to_string(writer.document(document))
 %    \end{macrocode}
 % \begin{markdown}
 % Remove block element / paragraph separators immediately followed by the
@@ -30331,83 +31088,50 @@
 % Then, remove any leftover output of \luamref{writer->undosep}.
 % \end{markdown}
 %  \begin{macrocode}
-        local undosep_start, undosep_end
-        local potential_secend_start, secend_start
-        local potential_sep_start, sep_start
+      local undosep_start, undosep_end
+      local potential_secend_start, secend_start
+      local potential_sep_start, sep_start
+      while true do
+        -- find a `writer->undosep`
+        undosep_start, undosep_end
+          = output:find(writer.undosep_text, 1, true)
+        if undosep_start == nil then break end
+        -- skip any preceding section ends
+        secend_start = undosep_start
         while true do
-          -- find a `writer->undosep`
-          undosep_start, undosep_end = output:find(writer.undosep_text, 1, true)
-          if undosep_start == nil then break end
-          -- skip any preceding section ends
-          secend_start = undosep_start
-          while true do
-            potential_secend_start = secend_start - #writer.secend_text
-            if potential_secend_start < 1
-               or output:sub(potential_secend_start, secend_start - 1) ~= writer.secend_text then
-              break
-            end
-            secend_start = potential_secend_start
+          potential_secend_start = secend_start - #writer.secend_text
+          if potential_secend_start < 1
+             or output:sub(potential_secend_start,
+                           secend_start - 1) ~= writer.secend_text
+             then
+            break
           end
-          -- find an immediately preceding block element / paragraph separator
-          sep_start = secend_start
-          potential_sep_start = sep_start - #writer.interblocksep_text
+          secend_start = potential_secend_start
+        end
+        -- find an immediately preceding
+        -- block element / paragraph separator
+        sep_start = secend_start
+        potential_sep_start = sep_start - #writer.interblocksep_text
+        if potential_sep_start >= 1
+           and output:sub(potential_sep_start,
+                          sep_start - 1) == writer.interblocksep_text
+           then
+          sep_start = potential_sep_start
+        else
+          potential_sep_start = sep_start - #writer.paragraphsep_text
           if potential_sep_start >= 1
-             and output:sub(potential_sep_start, sep_start - 1) == writer.interblocksep_text then
+             and output:sub(potential_sep_start,
+                            sep_start - 1) == writer.paragraphsep_text
+             then
             sep_start = potential_sep_start
-          else
-            potential_sep_start = sep_start - #writer.paragraphsep_text
-            if potential_sep_start >= 1
-               and output:sub(potential_sep_start, sep_start - 1) == writer.paragraphsep_text then
-              sep_start = potential_sep_start
-            end
           end
-          -- remove `writer->undosep` and immediately preceding block element / paragraph separator
-          output = output:sub(1, sep_start - 1)
-                .. output:sub(secend_start, undosep_start - 1)
-                .. output:sub(undosep_end + 1)
         end
-        return output
+        -- remove `writer->undosep` and immediately preceding
+        -- block element / paragraph separator
+        output = output:sub(1, sep_start - 1)
+              .. output:sub(secend_start, undosep_start - 1)
+              .. output:sub(undosep_end + 1)
       end
-%    \end{macrocode}
-% \begin{markdown}
-% If we cache markdown documents, produce the cache file and transform its
-% filename to plain \TeX{} output via the \luamref{writer->pack} method.
-% \end{markdown}
-%  \begin{macrocode}
-      if options.eagerCache or options.finalizeCache then
-        local name = util.cache(options.cacheDir, input, salt, convert,
-                                ".md" .. writer.suffix)
-        output = writer.pack(name)
-%    \end{macrocode}
-% \begin{markdown}
-% Otherwise, return the result of the conversion directly.
-% \end{markdown}
-%  \begin{macrocode}
-      else
-        output = convert(input)
-      end
-%    \end{macrocode}
-% \begin{markdown}
-% If the \Opt{finalizeCache} option is enabled, populate the frozen cache in
-% the file \Opt{frozenCacheFileName} with an entry for markdown document
-% number \Opt{frozenCacheCounter}.
-% \end{markdown}
-%  \begin{macrocode}
-      if options.finalizeCache then
-        local file, mode
-        if options.frozenCacheCounter > 0 then
-          mode = "a"
-        else
-          mode = "w"
-        end
-        file = assert(io.open(options.frozenCacheFileName, mode),
-          [[Could not open file "]] .. options.frozenCacheFileName
-          .. [[" for writing]])
-        assert(file:write([[\expandafter\global\expandafter\def\csname ]]
-          .. [[markdownFrozenCache]] .. options.frozenCacheCounter
-          .. [[\endcsname{]] .. output .. [[}]] .. "\n"))
-        assert(file:close())
-      end
       return output
     end
   end
@@ -30468,10 +31192,13 @@
                               + parsers.autolink
                               + V("InlineHtml")
                               + ( parsers.backslash * parsers.backslash)
-                              + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
+                              + ( parsers.backslash
+                                * (parsers.lbracket + parsers.rbracket)
                                 + V("Space") + V("Endline")
                                 + (parsers.any
-                                  - (parsers.newline + parsers.lbracket + parsers.rbracket
+                                  - ( parsers.newline
+                                    + parsers.lbracket
+                                    + parsers.rbracket
                                     + parsers.blankline^2))))^1)
                           / self.parser_functions.parse_inlines)
                           * parsers.rbracket
@@ -30534,11 +31261,15 @@
             end
           end
         else
-          table.insert(buffer, {"\\markdownRenderer", text_cites and "TextCite" or "Cite",
-            "{", #cites, "}"})
+          table.insert(buffer,
+                       {"\\markdownRenderer",
+                        text_cites and "TextCite" or "Cite",
+                        "{", #cites, "}"})
           for _,cite in ipairs(cites) do
-            table.insert(buffer, {cite.suppress_author and "-" or "+", "{",
-              cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"})
+            table.insert(buffer,
+                         {cite.suppress_author and "-" or "+", "{",
+                          cite.prenote or "", "}{",
+                          cite.postnote or "", "}{", cite.name, "}"})
           end
         end
         return buffer
@@ -30554,12 +31285,16 @@
       local citation_name
                     = Cs(parsers.dash^-1) * parsers.at
                     * Cs(citation_chars
-                        * (((citation_chars + parsers.internal_punctuation
+                        * ((( citation_chars
+                            + parsers.internal_punctuation
                             - parsers.comma - parsers.semicolon)
-                           * -#((parsers.internal_punctuation - parsers.comma
+                           * -#(( parsers.internal_punctuation
+                                - parsers.comma
                                 - parsers.semicolon)^0
-                               * -(citation_chars + parsers.internal_punctuation
-                                  - parsers.comma - parsers.semicolon)))^0
+                               * -( citation_chars
+                                  + parsers.internal_punctuation
+                                  - parsers.comma
+                                  - parsers.semicolon)))^0
                           * citation_chars)^-1)
 
       local citation_body_prenote
@@ -30568,10 +31303,14 @@
                          + parsers.inticks
                          + parsers.autolink
                          + V("InlineHtml")
-                         + V("Space") + V("Endline")
+                         + V("Space") + V("EndlineNoSub")
                          + (parsers.anyescaped
-                           - (parsers.newline + parsers.rbracket + parsers.blankline^2))
-                         - (parsers.spnl * parsers.dash^-1 * parsers.at))^1)
+                           - ( parsers.newline
+                             + parsers.rbracket
+                             + parsers.blankline^2))
+                         - ( parsers.spnl
+                           * parsers.dash^-1
+                           * parsers.at))^1)
 
       local citation_body_postnote
                     = Cs((parsers.alphanumeric^1
@@ -30579,9 +31318,11 @@
                          + parsers.inticks
                          + parsers.autolink
                          + V("InlineHtml")
-                         + V("Space") + V("Endline")
+                         + V("Space") + V("EndlineNoSub")
                          + (parsers.anyescaped
-                           - (parsers.newline + parsers.rbracket + parsers.semicolon
+                           - ( parsers.newline
+                             + parsers.rbracket
+                             + parsers.semicolon
                              + parsers.blankline^2))
                          - (parsers.spnl * parsers.rbracket))^1)
 
@@ -30592,7 +31333,8 @@
                       * parsers.spnlc
                     )
                     * citation_name
-                    * (parsers.internal_punctuation - parsers.semicolon)^-1
+                    * ( parsers.internal_punctuation
+                      - parsers.semicolon)^-1
                     * ( parsers.spnlc / function(_) return end
                       * citation_body_postnote
                       + Cc("")
@@ -30614,7 +31356,9 @@
                          + V("InlineHtml")
                          + V("Space") + V("Endline")
                          + (parsers.anyescaped
-                           - (parsers.newline + parsers.rbracket + parsers.at
+                           - ( parsers.newline
+                             + parsers.rbracket
+                             + parsers.at
                              + parsers.semicolon + parsers.blankline^2))
                          - (parsers.spnl * parsers.rbracket))^0)
 
@@ -30708,7 +31452,8 @@
 %  \begin{macrocode}
   local languages_json = (function()
     local base, prev, curr
-    for _, pathname in ipairs{kpse.lookup(language_map, { all=true })} do
+    for _, pathname in ipairs{kpse.lookup(language_map,
+                                          {all=true})} do
       local file = io.open(pathname, "r")
       if not file then goto continue end
       local input = assert(file:read("*a"))
@@ -30749,20 +31494,20 @@
         suf = suf:lower()
         if type == "onlineimage" then
           return {"\\markdownRendererContentBlockOnlineImage{",suf,"}",
-                                 "{",self.string(src),"}",
-                                 "{",self.uri(src),"}",
-                                 "{",self.string(tit or ""),"}"}
+                  "{",self.string(src),"}",
+                  "{",self.uri(src),"}",
+                  "{",self.string(tit or ""),"}"}
         elseif languages_json[suf] then
           return {"\\markdownRendererContentBlockCode{",suf,"}",
-                                 "{",self.string(languages_json[suf]),"}",
-                                 "{",self.string(src),"}",
-                                 "{",self.uri(src),"}",
-                                 "{",self.string(tit or ""),"}"}
+                  "{",self.string(languages_json[suf]),"}",
+                  "{",self.string(src),"}",
+                  "{",self.uri(src),"}",
+                  "{",self.string(tit or ""),"}"}
         else
           return {"\\markdownRendererContentBlock{",suf,"}",
-                                 "{",self.string(src),"}",
-                                 "{",self.uri(src),"}",
-                                 "{",self.string(tit or ""),"}"}
+                  "{",self.string(src),"}",
+                  "{",self.uri(src),"}",
+                  "{",self.string(tit or ""),"}"}
         end
       end
     end, extend_reader = function(self)
@@ -30797,8 +31542,8 @@
                         return parser
                       end)("png", "jpg", "jpeg", "gif", "tif", "tiff")
 
-      -- online image url for iA Writer content blocks with mandatory suffix,
-      -- allowing nested brackets:
+      -- online image url for iA Writer content blocks with
+      -- mandatory suffix, allowing nested brackets:
       local onlineimageurl
                     = (parsers.less
                       * Cs((parsers.anyescaped
@@ -30876,8 +31621,9 @@
       local function dlitem(term, defs)
         local retVal = {"\\markdownRendererDlItem{",term,"}"}
         for _, def in ipairs(defs) do
-          retVal[#retVal+1] = {"\\markdownRendererDlDefinitionBegin ",def,
-                               "\\markdownRendererDlDefinitionEnd "}
+          retVal[#retVal+1]
+            = {"\\markdownRendererDlDefinitionBegin ",def,
+               "\\markdownRendererDlDefinitionEnd "}
         end
         retVal[#retVal+1] = "\\markdownRendererDlItemEnd "
         return retVal
@@ -30903,17 +31649,26 @@
 
       local defstartchar = S("~:")
 
-      local defstart  = parsers.check_trail_length(0) * defstartchar * #parsers.spacing
-                                      * (parsers.tab + parsers.space^-3)
-                      + parsers.check_trail_length(1) * defstartchar * #parsers.spacing
-                                       * (parsers.tab + parsers.space^-2)
-                      + parsers.check_trail_length(2) * defstartchar * #parsers.spacing
-                                       * (parsers.tab + parsers.space^-1)
-                      + parsers.check_trail_length(3) * defstartchar * #parsers.spacing
+      local defstart
+        = parsers.check_trail_length(0) * defstartchar
+        * #parsers.spacing
+        * (parsers.tab + parsers.space^-3)
+        + parsers.check_trail_length(1)
+        * defstartchar * #parsers.spacing
+        * (parsers.tab + parsers.space^-2)
+        + parsers.check_trail_length(2)
+        * defstartchar * #parsers.spacing
+        * (parsers.tab + parsers.space^-1)
+        + parsers.check_trail_length(3)
+        * defstartchar * #parsers.spacing
 
-      local indented_line = (parsers.check_minimal_indent / "") * parsers.check_code_trail * parsers.line
+      local indented_line
+        = (parsers.check_minimal_indent / "")
+        * parsers.check_code_trail * parsers.line
 
-      local blank = parsers.check_minimal_blank_indent_and_any_trail * parsers.optionalspace * parsers.newline
+      local blank
+        = parsers.check_minimal_blank_indent_and_any_trail
+        * parsers.optionalspace * parsers.newline
 
       local dlchunk = Cs(parsers.line * (indented_line - blank)^0)
 
@@ -30930,24 +31685,24 @@
       end
 
       local DefinitionListItemLoose
-                    = C(parsers.line) * blank^0
-                    * Ct((parsers.check_minimal_indent * (defstart
-                         * indented_blocks(dlchunk)
-                         / self.parser_functions.parse_blocks_nested))^1)
-                    * Cc(false) / definition_list_item
+        = C(parsers.line) * blank^0
+        * Ct((parsers.check_minimal_indent * (defstart
+             * indented_blocks(dlchunk)
+             / self.parser_functions.parse_blocks_nested))^1)
+        * Cc(false) / definition_list_item
 
       local DefinitionListItemTight
-                    = C(parsers.line)
-                    * Ct((parsers.check_minimal_indent * (defstart * dlchunk
-                         / self.parser_functions.parse_blocks_nested))^1)
-                    * Cc(true) / definition_list_item
+        = C(parsers.line)
+        * Ct((parsers.check_minimal_indent * (defstart * dlchunk
+             / self.parser_functions.parse_blocks_nested))^1)
+        * Cc(true) / definition_list_item
 
       local DefinitionList
-                    = ( Ct(DefinitionListItemLoose^1) * Cc(false)
-                      + Ct(DefinitionListItemTight^1)
-                      * (blank^0
-                        * -DefinitionListItemLoose * Cc(true))
-                      ) / writer.definitionlist
+        = ( Ct(DefinitionListItemLoose^1) * Cc(false)
+          + Ct(DefinitionListItemTight^1)
+          * (blank^0
+            * -DefinitionListItemLoose * Cc(true))
+          ) / writer.definitionlist
 
       self.insert_pattern("Block after Heading",
                           DefinitionList, "DefinitionList")
@@ -31032,7 +31787,8 @@
           return {"\\markdownRendererFancyOlItemWithNumber{",num,"}",s,
                   "\\markdownRendererFancyOlItemEnd "}
         else
-          return {"\\markdownRendererFancyOlItem ",s,"\\markdownRendererFancyOlItemEnd "}
+          return {"\\markdownRendererFancyOlItem ",s,
+                  "\\markdownRendererFancyOlItemEnd "}
         end
       end
     end, extend_reader = function(self)
@@ -31053,7 +31809,8 @@
             continuation_marker = marker
           end
           for _,delim in ipairs(delims) do
-            table.insert(markers_table, {start_marker, continuation_marker, delim})
+            table.insert(markers_table,
+                         {start_marker, continuation_marker, delim})
           end
         end
         return markers_table
@@ -31072,7 +31829,8 @@
 
       local roman_marker = function(chars)
         local m, d, c = P(chars[1]), P(chars[2]), P(chars[3])
-        local l, x, v, i = P(chars[4]), P(chars[5]), P(chars[6]), P(chars[7])
+        local l, x, v, i
+          = P(chars[4]), P(chars[5]), P(chars[6]), P(chars[7])
         return  m^-3
                 * (c*m + c*d + d^-1 * c^-3)
                 * (x*c + x*l + l^-1 * x^-3)
@@ -31079,8 +31837,10 @@
                 * (i*x + i*v + v^-1 * i^-3)
       end
 
-      local lowercase_roman_marker  = roman_marker({"m", "d", "c", "l", "x", "v", "i"})
-      local uppercase_roman_marker  = roman_marker({"M", "D", "C", "L", "X", "V", "I"})
+      local lowercase_roman_marker
+        = roman_marker({"m", "d", "c", "l", "x", "v", "i"})
+      local uppercase_roman_marker
+        = roman_marker({"M", "D", "C", "L", "X", "V", "I"})
 
       local lowercase_opening_roman_marker  = P("i")
       local uppercase_opening_roman_marker  = P("I")
@@ -31104,7 +31864,8 @@
 
       local markers_table = combine_markers_and_delims(markers, delims)
 
-      local function enumerator(start_marker, _, delimiter_type, interrupting)
+      local function enumerator(start_marker, _,
+                                delimiter_type, interrupting)
         local delimiter_range
         local allowed_end
         if interrupting then
@@ -31112,7 +31873,8 @@
           allowed_end = C(parsers.spacechar^1) * #parsers.linechar
         else
           delimiter_range = start_marker
-          allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
+          allowed_end = C(parsers.spacechar^1)
+                      + #(parsers.newline + parsers.eof)
         end
 
         return parsers.check_trail
@@ -31134,13 +31896,15 @@
       end
 
       local function roman2number(roman)
-        local romans = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
+        local romans = { ["M"] = 1000, ["D"] = 500, ["C"] = 100,
+                         ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
         local numeral = 0
 
         local i = 1
         local len = string.len(roman)
         while i < len do
-          local z1, z2 = romans[ string.sub(roman, i, i) ], romans[ string.sub(roman, i+1, i+1) ]
+          local z1, z2 = romans[ string.sub(roman, i, i) ],
+                         romans[ string.sub(roman, i+1, i+1) ]
           if z1 < z2 then
               numeral = numeral + (z2 - z1)
               i = i + 2
@@ -31149,7 +31913,9 @@
               i = i + 1
           end
         end
-        if i <= len then numeral = numeral + romans[ string.sub(roman,i,i) ] end
+        if i <= len then
+          numeral = numeral + romans[ string.sub(roman,i,i) ]
+        end
         return numeral
       end
 
@@ -31174,11 +31940,13 @@
         end
         num = numstr:match("^([A-Z])$")
         if num then
-          return string.byte(num) - string.byte("A") + 1, "UpperAlpha", numdelim
+          return string.byte(num) - string.byte("A") + 1,
+                 "UpperAlpha", numdelim
         end
         num = numstr:match("^([a-z])$")
         if num then
-          return string.byte(num) - string.byte("a") + 1, "LowerAlpha", numdelim
+          return string.byte(num) - string.byte("a") + 1,
+                 "LowerAlpha", numdelim
         end
         num = numstr:match("^([IVXLCDM]+)")
         if num then
@@ -31192,7 +31960,8 @@
       end
 
       local function fancylist(items,tight,start)
-        local startnum, numstyle, numdelim = sniffstyle(start[2][1], start[2][2])
+        local startnum, numstyle, numdelim
+          = sniffstyle(start[2][1], start[2][2])
         return writer.fancylist(items,tight,
                                 options.startNumber and startnum or 1,
                                 numstyle or "Decimal",
@@ -31199,23 +31968,31 @@
                                 numdelim or "Default")
       end
 
-      local FancyListOfType = function(start_marker, continuation_marker, delimiter_type)
-        local enumerator_start = enumerator(start_marker, continuation_marker, delimiter_type)
-        local enumerator_cont = enumerator(continuation_marker, continuation_marker, delimiter_type)
-        return Cg(enumerator_start, "listtype")
-             * (Ct( TightListItem(Cb("listtype"))
-                 * ((parsers.check_minimal_indent / "") * TightListItem(enumerator_cont))^0)
-             * Cc(true)
-             * -#((parsers.conditionally_indented_blankline^0 / "")
-                 * parsers.check_minimal_indent * enumerator_cont)
-             + Ct( LooseListItem(Cb("listtype"))
-                 * ((parsers.conditionally_indented_blankline^0 / "")
-                   * (parsers.check_minimal_indent / "") * LooseListItem(enumerator_cont))^0)
-             * Cc(false)
-             ) * Ct(Cb("listtype")) / fancylist
-      end
+      local FancyListOfType
+        = function(start_marker, continuation_marker, delimiter_type)
+          local enumerator_start
+            = enumerator(start_marker, continuation_marker,
+                         delimiter_type)
+          local enumerator_cont
+            = enumerator(continuation_marker, continuation_marker,
+                         delimiter_type)
+          return Cg(enumerator_start, "listtype")
+               * (Ct( TightListItem(Cb("listtype"))
+                   * ((parsers.check_minimal_indent / "")
+                   * TightListItem(enumerator_cont))^0)
+               * Cc(true)
+               * -#((parsers.conditionally_indented_blankline^0 / "")
+                   * parsers.check_minimal_indent * enumerator_cont)
+               + Ct( LooseListItem(Cb("listtype"))
+                   * ((parsers.conditionally_indented_blankline^0 / "")
+                     * (parsers.check_minimal_indent / "")
+                     * LooseListItem(enumerator_cont))^0)
+               * Cc(false)
+               ) * Ct(Cb("listtype")) / fancylist
+        end
 
-      local FancyList = join_table_with_func(FancyListOfType, markers_table)
+      local FancyList
+        = join_table_with_func(FancyListOfType, markers_table)
 
       local Endline   = parsers.newline
                       * (parsers.check_minimal_indent
@@ -31271,14 +32048,17 @@
         s = s:gsub("\n$", "")
         local buf = {}
         if attr ~= nil then
-          table.insert(buf, {"\\markdownRendererFencedCodeAttributeContextBegin",
-                             self.attributes(attr)})
+          table.insert(buf,
+            {"\\markdownRendererFencedCodeAttributeContextBegin",
+             self.attributes(attr)})
         end
         local name = util.cache_verbatim(options.cacheDir, s)
-        table.insert(buf, {"\\markdownRendererInputFencedCode{",
-                           name,"}{",self.string(i),"}{",self.infostring(i),"}"})
+        table.insert(buf,
+          {"\\markdownRendererInputFencedCode{",
+           name,"}{",self.string(i),"}{",self.infostring(i),"}"})
         if attr ~= nil then
-          table.insert(buf, "\\markdownRendererFencedCodeAttributeContextEnd{}")
+          table.insert(buf,
+            "\\markdownRendererFencedCodeAttributeContextEnd{}")
         end
         return buf
       end
@@ -31318,11 +32098,13 @@
                                      - parsers.newline)^0)
                                  / strip_enclosing_whitespaces)
 
-      local backtick_infostring = Cs(Cs((V("HtmlEntity")
-                                        + (-#(parsers.backslash * parsers.backtick) * parsers.anyescaped)
-                                         - parsers.newline
-                                         - parsers.backtick)^0)
-                                    / strip_enclosing_whitespaces)
+      local backtick_infostring
+        = Cs( Cs((V("HtmlEntity")
+            + ( -#(parsers.backslash * parsers.backtick)
+              * parsers.anyescaped)
+              - parsers.newline
+              - parsers.backtick)^0)
+        / strip_enclosing_whitespaces)
 
       local fenceindent
 
@@ -31344,77 +32126,93 @@
         end
       end
 
-      local function count_fenced_start_indent(_, _, indent_table, trail)
-        local last_indent_name = get_last_indent_name(indent_table)
-        fenceindent = 0
-        if last_indent_name ~= "li" then
-          fenceindent = #trail
+      local count_fenced_start_indent =
+        function(_, _, indent_table, trail)
+          local last_indent_name = get_last_indent_name(indent_table)
+          fenceindent = 0
+          if last_indent_name ~= "li" then
+            fenceindent = #trail
+          end
+          return true
         end
-        return true
-      end
 
-      local fencehead      = function(char, infostring)
-        return               Cmt(Cb("indent_info") * parsers.check_trail, count_fenced_start_indent)
-                           * Cg(char^3, "fencelength")
-                           * parsers.optionalspace
-                           * infostring
-                           * (parsers.newline + parsers.eof)
+      local fencehead = function(char, infostring)
+        return Cmt( Cb("indent_info")
+                  * parsers.check_trail, count_fenced_start_indent)
+             * Cg(char^3, "fencelength")
+             * parsers.optionalspace
+             * infostring
+             * (parsers.newline + parsers.eof)
       end
 
-      local fencetail      = function(char)
-        return               parsers.check_trail_no_rem
-                           * Cmt(C(char^3) * Cb("fencelength"), captures_geq_length)
-                           * parsers.optionalspace * (parsers.newline + parsers.eof)
-                           + parsers.eof
+      local fencetail = function(char)
+        return parsers.check_trail_no_rem
+             * Cmt(C(char^3) * Cb("fencelength"), captures_geq_length)
+             * parsers.optionalspace * (parsers.newline + parsers.eof)
+             + parsers.eof
       end
 
-      local function process_fenced_line(s, i, indent_table, line_content, is_blank) -- luacheck: ignore s i
-        local remainder = ""
-        if has_trail(indent_table) then
-          remainder = indent_table.trail.internal_remainder
-        end
+      local process_fenced_line =
+        function(s, i, -- luacheck: ignore s i
+                 indent_table, line_content, is_blank)
+          local remainder = ""
+          if has_trail(indent_table) then
+            remainder = indent_table.trail.internal_remainder
+          end
 
-        if is_blank and get_last_indent_name(indent_table) == "li" then
-          remainder = ""
-        end
+          if is_blank
+             and get_last_indent_name(indent_table) == "li" then
+            remainder = ""
+          end
 
-        local str = remainder .. line_content
-        local index = 1
-        local remaining = fenceindent
+          local str = remainder .. line_content
+          local index = 1
+          local remaining = fenceindent
 
-        while true do
-          local c = str:sub(index, index)
-          if c == " " and remaining > 0 then
-            remaining = remaining - 1
-            index = index + 1
-          elseif c == "\t" and remaining > 3 then
-            remaining = remaining - 4
-            index = index + 1
-          else
-            break
+          while true do
+            local c = str:sub(index, index)
+            if c == " " and remaining > 0 then
+              remaining = remaining - 1
+              index = index + 1
+            elseif c == "\t" and remaining > 3 then
+              remaining = remaining - 4
+              index = index + 1
+            else
+              break
+            end
           end
+
+          return true, str:sub(index)
         end
 
-        return true, str:sub(index)
-      end
-
       local fencedline = function(char)
-        return Cmt(Cb("indent_info") * C(parsers.line - fencetail(char)) * Cc(false), process_fenced_line)
+        return Cmt( Cb("indent_info")
+                  * C(parsers.line - fencetail(char))
+                  * Cc(false), process_fenced_line)
       end
 
-      local blankfencedline = Cmt(Cb("indent_info") * C(parsers.blankline) * Cc(true), process_fenced_line)
+      local blankfencedline
+        = Cmt( Cb("indent_info")
+             * C(parsers.blankline)
+             * Cc(true), process_fenced_line)
 
       local TildeFencedCode
-             = fencehead(parsers.tilde, tilde_infostring)
-             * Cs(((parsers.check_minimal_blank_indent / "") * blankfencedline
-                   + (parsers.check_minimal_indent / "") * fencedline(parsers.tilde))^0)
-             * ((parsers.check_minimal_indent / "") * fencetail(parsers.tilde) + parsers.succeed)
+        = fencehead(parsers.tilde, tilde_infostring)
+        * Cs(( (parsers.check_minimal_blank_indent / "")
+             * blankfencedline
+             + ( parsers.check_minimal_indent / "")
+               * fencedline(parsers.tilde))^0)
+        * ( (parsers.check_minimal_indent / "")
+          * fencetail(parsers.tilde) + parsers.succeed)
 
       local BacktickFencedCode
              = fencehead(parsers.backtick, backtick_infostring)
-             * Cs(((parsers.check_minimal_blank_indent / "") * blankfencedline
-                   + (parsers.check_minimal_indent / "") * fencedline(parsers.backtick))^0)
-             * ((parsers.check_minimal_indent / "") * fencetail(parsers.backtick) + parsers.succeed)
+             * Cs(( (parsers.check_minimal_blank_indent / "")
+                  * blankfencedline
+                  + (parsers.check_minimal_indent / "")
+                  * fencedline(parsers.backtick))^0)
+             * ( (parsers.check_minimal_indent / "")
+               * fencetail(parsers.backtick) + parsers.succeed)
 
       local infostring_with_attributes
                         = Ct(C((parsers.linechar
@@ -31424,28 +32222,28 @@
                             * Ct(parsers.attributes))
 
       local FencedCode
-               = ((TildeFencedCode + BacktickFencedCode)
-               / function(infostring, code)
-                   local expanded_code = self.expandtabs(code)
+        = ((TildeFencedCode + BacktickFencedCode)
+        / function(infostring, code)
+          local expanded_code = self.expandtabs(code)
 
-                   if allow_raw_blocks then
-                     local raw_attr = lpeg.match(parsers.raw_attribute,
-                                                 infostring)
-                     if raw_attr then
-                       return writer.rawBlock(expanded_code, raw_attr)
-                     end
-                   end
+          if allow_raw_blocks then
+            local raw_attr = lpeg.match(parsers.raw_attribute,
+                                        infostring)
+            if raw_attr then
+              return writer.rawBlock(expanded_code, raw_attr)
+            end
+          end
 
-                   local attr = nil
-                   if allow_attributes then
-                     local match = lpeg.match(infostring_with_attributes,
-                                              infostring)
-                     if match then
-                       infostring, attr = table.unpack(match)
-                     end
-                   end
-                   return writer.fencedCode(expanded_code, infostring, attr)
-                 end)
+          local attr = nil
+          if allow_attributes then
+            local match = lpeg.match(infostring_with_attributes,
+                                     infostring)
+            if match then
+              infostring, attr = table.unpack(match)
+            end
+          end
+          return writer.fencedCode(expanded_code, infostring, attr)
+        end)
 
       self.insert_pattern("Block after Verbatim",
                           FencedCode, "FencedCode")
@@ -31497,10 +32295,13 @@
 % \end{markdown}
 %  \begin{macrocode}
       function self.div_begin(attributes)
-        local start_output = {"\\markdownRendererFencedDivAttributeContextBegin\n",
-                              self.attributes(attributes)}
-        local end_output = {"\\markdownRendererFencedDivAttributeContextEnd{}"}
-        return self.push_attributes("div", attributes, start_output, end_output)
+        local start_output
+          = {"\\markdownRendererFencedDivAttributeContextBegin\n",
+             self.attributes(attributes)}
+        local end_output
+          = {"\\markdownRendererFencedDivAttributeContextEnd{}"}
+        return self.push_attributes(
+          "div", attributes, start_output, end_output)
       end
 %    \end{macrocode}
 % \par
@@ -31561,21 +32362,24 @@
       self.initialize_named_group("fenced_div_num_opening_indents")
 
       local function increment_div_level()
-        local function push_indent_table(s, i, indent_table, -- luacheck: ignore s i
-                                         fenced_div_num_opening_indents, fenced_div_level)
-          fenced_div_level = tonumber(fenced_div_level) + 1
-          local num_opening_indents = 0
-          if indent_table.indents ~= nil then
-            num_opening_indents = #indent_table.indents
+        local push_indent_table =
+          function(s, i, indent_table, -- luacheck: ignore s i
+                   fenced_div_num_opening_indents, fenced_div_level)
+            fenced_div_level = tonumber(fenced_div_level) + 1
+            local num_opening_indents = 0
+            if indent_table.indents ~= nil then
+              num_opening_indents = #indent_table.indents
+            end
+            fenced_div_num_opening_indents[fenced_div_level]
+              = num_opening_indents
+            return true, fenced_div_num_opening_indents
           end
-          fenced_div_num_opening_indents[fenced_div_level] = num_opening_indents
-          return true, fenced_div_num_opening_indents
-        end
 
-        local function increment_level(s, i, fenced_div_level) -- luacheck: ignore s i
-          fenced_div_level = tonumber(fenced_div_level) + 1
-          return true, tostring(fenced_div_level)
-        end
+        local increment_level =
+          function(s, i, fenced_div_level) -- luacheck: ignore s i
+            fenced_div_level = tonumber(fenced_div_level) + 1
+            return true, tostring(fenced_div_level)
+          end
 
         return Cg( Cmt( Cb("indent_info")
                       * Cb("fenced_div_num_opening_indents")
@@ -31586,11 +32390,13 @@
       end
 
       local function decrement_div_level()
-        local function pop_indent_table(s, i, fenced_div_indent_table, fenced_div_level) -- luacheck: ignore s i
-          fenced_div_level = tonumber(fenced_div_level)
-          fenced_div_indent_table[fenced_div_level] = nil
-          return true, tostring(fenced_div_level - 1)
-        end
+        local pop_indent_table =
+          function(s, i, -- luacheck: ignore s i
+                   fenced_div_indent_table, fenced_div_level)
+            fenced_div_level = tonumber(fenced_div_level)
+            fenced_div_indent_table[fenced_div_level] = nil
+            return true, tostring(fenced_div_level - 1)
+          end
 
         return Cg( Cmt( Cb("fenced_div_num_opening_indents")
                       * Cb("fenced_div_level"), pop_indent_table)
@@ -31598,21 +32404,24 @@
       end
 
 
-      local non_fenced_div_block  = parsers.check_minimal_indent * V("Block")
-                                  - parsers.check_minimal_indent_and_trail * fenced_div_end
+      local non_fenced_div_block
+        = parsers.check_minimal_indent * V("Block")
+        - parsers.check_minimal_indent_and_trail * fenced_div_end
 
-      local non_fenced_div_paragraph  = parsers.check_minimal_indent * V("Paragraph")
-                                      - parsers.check_minimal_indent_and_trail * fenced_div_end
+      local non_fenced_div_paragraph
+        = parsers.check_minimal_indent * V("Paragraph")
+        - parsers.check_minimal_indent_and_trail * fenced_div_end
 
       local blank = parsers.minimally_indented_blank
 
-      local block_separated  = parsers.block_sep_group(blank)
-                             * non_fenced_div_block
+      local block_separated = parsers.block_sep_group(blank)
+                            * non_fenced_div_block
 
-      local loop_body_pair  = parsers.create_loop_body_pair(block_separated,
-                                                            non_fenced_div_paragraph,
-                                                            parsers.block_sep_group(blank),
-                                                            parsers.par_sep_group(blank))
+      local loop_body_pair
+        = parsers.create_loop_body_pair(block_separated,
+                                        non_fenced_div_paragraph,
+                                        parsers.block_sep_group(blank),
+                                        parsers.par_sep_group(blank))
 
       local content_loop  = ( non_fenced_div_block
                             * loop_body_pair.block^0
@@ -31625,7 +32434,9 @@
 
       local FencedDiv = fenced_div_begin
                       / function (infostring)
-                          local attr = lpeg.match(Ct(parsers.attributes), infostring)
+                          local attr
+                            = lpeg.match(Ct(parsers.attributes),
+                                         infostring)
                           if attr == nil then
                             attr = {"." .. infostring}
                           end
@@ -31636,7 +32447,8 @@
                       * parsers.skipblanklines
                       * Ct(content_loop)
                       * parsers.minimally_indented_blank^0
-                      * parsers.check_minimal_indent_and_trail * fenced_div_end
+                      * parsers.check_minimal_indent_and_trail
+                      * fenced_div_end
                       * decrement_div_level()
                       * (Cc("") / writer.div_end)
 
@@ -31657,23 +32469,27 @@
 % \end{markdown}
 %  \begin{macrocode}
       local function is_inside_div()
-        local function check_div_level(s, i, fenced_div_level) -- luacheck: ignore s i
-          fenced_div_level = tonumber(fenced_div_level)
-          return fenced_div_level > 0
-        end
+        local check_div_level =
+          function(s, i, fenced_div_level) -- luacheck: ignore s i
+            fenced_div_level = tonumber(fenced_div_level)
+            return fenced_div_level > 0
+          end
 
         return Cmt(Cb("fenced_div_level"), check_div_level)
       end
 
       local function check_indent()
-        local function compare_indent(s, i, indent_table, -- luacheck: ignore s i
-                                      fenced_div_num_opening_indents, fenced_div_level)
-          fenced_div_level = tonumber(fenced_div_level)
-          local num_current_indents = (indent_table.current_line_indents ~= nil and
-                                      #indent_table.current_line_indents) or 0
-          local num_opening_indents = fenced_div_num_opening_indents[fenced_div_level]
-          return num_current_indents == num_opening_indents
-        end
+        local compare_indent =
+          function(s, i, indent_table, -- luacheck: ignore s i
+                   fenced_div_num_opening_indents, fenced_div_level)
+            fenced_div_level = tonumber(fenced_div_level)
+            local num_current_indents
+              = ( indent_table.current_line_indents ~= nil and
+                 #indent_table.current_line_indents) or 0
+            local num_opening_indents
+              = fenced_div_num_opening_indents[fenced_div_level]
+            return num_current_indents == num_opening_indents
+          end
 
         return Cmt( Cb("indent_info")
                   * Cb("fenced_div_num_opening_indents")
@@ -31745,26 +32561,31 @@
                               * parsers.newline))^1
                           - parsers.thematic_break_lines
 
-      local heading_text  = heading_line
-                          * ((V("Endline") / "\n") * (heading_line - parsers.heading_level))^0
-                          * parsers.newline^-1
+      local heading_text
+        = heading_line
+        * ( (V("Endline") / "\n")
+          * (heading_line - parsers.heading_level))^0
+        * parsers.newline^-1
 
-      local SetextHeading  = parsers.freeze_trail * parsers.check_trail_no_rem
-                           * #(heading_text
-                              * (parsers.attributes
-                                * parsers.optionalspace
-                                * parsers.newline)^-1
-                              * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level)
-                           * Cs(heading_text) / strip_trailing_spaces
-                           / parsers.parse_heading_text
-                           * Cg(Ct((parsers.attributes
-                                 * parsers.optionalspace
-                                 * parsers.newline)^-1), "attributes")
-                           * parsers.check_minimal_indent_and_trail * parsers.heading_level
-                           * Cb("attributes")
-                           * parsers.newline
-                           * parsers.unfreeze_trail
-                           / writer.heading
+      local SetextHeading
+        = parsers.freeze_trail * parsers.check_trail_no_rem
+        * #(heading_text
+           * (parsers.attributes
+             * parsers.optionalspace
+             * parsers.newline)^-1
+           * parsers.check_minimal_indent
+           * parsers.check_trail
+           * parsers.heading_level)
+        * Cs(heading_text) / strip_trailing_spaces
+        / parsers.parse_heading_text
+        * Cg(Ct((parsers.attributes
+              * parsers.optionalspace
+              * parsers.newline)^-1), "attributes")
+        * parsers.check_minimal_indent_and_trail * parsers.heading_level
+        * Cb("attributes")
+        * parsers.newline
+        * parsers.unfreeze_trail
+        / writer.heading
 
       local Heading = AtxHeading + SetextHeading
       self.update_rule("Heading", Heading)
@@ -31838,18 +32659,18 @@
       local parsers = self.parsers
       local writer = self.writer
 
-      local LineBlock = Ct(
-                        (Cs(
-                          ( (parsers.pipe * parsers.space)/""
-                          * ((parsers.space)/entities.char_entity("nbsp"))^0
-                          * parsers.linechar^0 * (parsers.newline/""))
-                          * (-parsers.pipe
-                            * (parsers.space^1/" ")
-                            * parsers.linechar^1
-                            * (parsers.newline/"")
-                            )^0
-                          * (parsers.blankline/"")^0
-                        ) / self.parser_functions.parse_inlines)^1) / writer.lineblock
+      local LineBlock
+        = Ct((Cs(( (parsers.pipe * parsers.space) / ""
+                 * ((parsers.space)/entities.char_entity("nbsp"))^0
+                 * parsers.linechar^0 * (parsers.newline/""))
+                 * (-parsers.pipe
+                   * (parsers.space^1/" ")
+                   * parsers.linechar^1
+                   * (parsers.newline/"")
+                   )^0
+                 * (parsers.blankline/"")^0)
+             / self.parser_functions.parse_inlines)^1)
+        / writer.lineblock
 
       self.insert_pattern("Block after Blockquote",
                            LineBlock, "LineBlock")
@@ -31889,8 +32710,9 @@
 
       local doubleequals = P("==")
 
-      local Mark = parsers.between(V("Inline"), doubleequals, doubleequals)
-                 / function (inlines) return writer.mark(inlines) end
+      local Mark
+        = parsers.between(V("Inline"), doubleequals, doubleequals)
+        / function (inlines) return writer.mark(inlines) end
 
       self.add_special_character("=")
       self.insert_pattern("Inline before LinkAndEmph",
@@ -31923,13 +32745,18 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-      local define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon
-                                    * parsers.spnlc * parsers.url
-                                    * ( parsers.spnlc_sep * parsers.title * (parsers.spnlc * Ct(parsers.attributes))
-                                      * parsers.only_blank
-                                      + parsers.spnlc_sep * parsers.title * parsers.only_blank
-                                      + Cc("") * (parsers.spnlc * Ct(parsers.attributes)) * parsers.only_blank
-                                      + Cc("") * parsers.only_blank)
+      local define_reference_parser
+        = (parsers.check_trail / "")
+        * parsers.link_label
+        * parsers.colon
+        * parsers.spnlc * parsers.url
+        * ( parsers.spnlc_sep * parsers.title
+          * (parsers.spnlc * Ct(parsers.attributes))
+          * parsers.only_blank
+          + parsers.spnlc_sep * parsers.title * parsers.only_blank
+          + Cc("") * (parsers.spnlc * Ct(parsers.attributes))
+          * parsers.only_blank
+          + Cc("") * parsers.only_blank)
 
       local ReferenceWithAttributes = define_reference_parser
                                     / self.register_link
@@ -31944,8 +32771,10 @@
 % \end{markdown}
 %  \begin{macrocode}
 
-      local LinkWithAttributesAndEmph = Ct(parsers.link_and_emph_table * Cg(Cc(true), "match_link_attributes"))
-                                      / self.defer_link_and_emphasis_processing
+      local LinkWithAttributesAndEmph
+        = Ct(parsers.link_and_emph_table * Cg(Cc(true),
+             "match_link_attributes"))
+        / self.defer_link_and_emphasis_processing
 
       self.update_rule("LinkAndEmph", LinkWithAttributesAndEmph)
 
@@ -32030,9 +32859,10 @@
 
       if inline_notes then
         local InlineNote
-                    = parsers.circumflex
-                    * (parsers.link_label / self.parser_functions.parse_inlines_no_inline_note)
-                    / writer.note
+          = parsers.circumflex
+          * ( parsers.link_label
+            / self.parser_functions.parse_inlines_no_inline_note)
+          / writer.note
 
         self.insert_pattern("Inline after LinkAndEmph",
                             InlineNote, "InlineNote")
@@ -32070,20 +32900,27 @@
 
         local NoteRef = RawNoteRef / lookup_note
 
-        local optionally_indented_line = parsers.check_optional_indent_and_any_trail * parsers.line
+        local optionally_indented_line
+          = parsers.check_optional_indent_and_any_trail * parsers.line
 
-        local blank = parsers.check_optional_blank_indent_and_any_trail * parsers.optionalspace * parsers.newline
+        local blank
+          = parsers.check_optional_blank_indent_and_any_trail
+          * parsers.optionalspace * parsers.newline
 
-        local chunk = Cs(parsers.line * (optionally_indented_line - blank)^0)
+        local chunk
+          = Cs(parsers.line
+          * (optionally_indented_line - blank)^0)
 
         local indented_blocks = function(bl)
           return Cs( bl
-                * (blank^1 * (parsers.check_optional_indent / "")
-                  * parsers.check_code_trail * -parsers.blankline * bl)^0)
+                * ( blank^1 * (parsers.check_optional_indent / "")
+                  * parsers.check_code_trail
+                  * -parsers.blankline * bl)^0)
         end
 
         local NoteBlock
-                    = parsers.check_trail_no_rem * RawNoteRef * parsers.colon
+                    = parsers.check_trail_no_rem
+                    * RawNoteRef * parsers.colon
                     * parsers.spnlc * indented_blocks(chunk)
                     / register_note
 
@@ -32241,24 +33078,26 @@
                                         , table_hline_separator
                                         , table_hline_column)
 
-      local table_caption_beginning = (parsers.check_minimal_blank_indent_and_any_trail_no_rem
-                                      * parsers.optionalspace * parsers.newline)^0
-                                    * parsers.check_minimal_indent_and_trail
-                                    * (P("Table")^-1 * parsers.colon)
-                                    * parsers.optionalspace
+      local table_caption_beginning
+        = ( parsers.check_minimal_blank_indent_and_any_trail_no_rem
+          * parsers.optionalspace * parsers.newline)^0
+        * parsers.check_minimal_indent_and_trail
+        * (P("Table")^-1 * parsers.colon)
+        * parsers.optionalspace
 
       local function strip_trailing_spaces(s)
         return s:gsub("%s*$","")
       end
 
-      local table_row = pipe_table_row(true
-                                      , (C((parsers.linechar - parsers.pipe)^1)
-                                        / strip_trailing_spaces
-                                        / self.parser_functions.parse_inlines)
-                                      , parsers.pipe
-                                      , (C((parsers.linechar - parsers.pipe)^0)
-                                        / strip_trailing_spaces
-                                        / self.parser_functions.parse_inlines))
+      local table_row
+        = pipe_table_row(true
+                        , (C((parsers.linechar - parsers.pipe)^1)
+                          / strip_trailing_spaces
+                          / self.parser_functions.parse_inlines)
+                        , parsers.pipe
+                        , (C((parsers.linechar - parsers.pipe)^0)
+                          / strip_trailing_spaces
+                          / self.parser_functions.parse_inlines))
 
       local table_caption
       if table_captions then
@@ -32275,7 +33114,8 @@
                               + ( parsers.newline
                                 * #( parsers.optionalspace
                                    * parsers.linechar)
-                                * C(parsers.optionalspace) / writer.space))
+                                * C(parsers.optionalspace)
+                                / writer.space))
                              * (parsers.linechar
                                - parsers.lbrace)^0)^1)
                              / self.parser_functions.parse_inlines)
@@ -32289,7 +33129,8 @@
                             + ( parsers.newline
                               * #( parsers.optionalspace
                                  * parsers.linechar)
-                              * C(parsers.optionalspace) / writer.space))^1)
+                              * C(parsers.optionalspace)
+                              / writer.space))^1)
                         / self.parser_functions.parse_inlines
                         * parsers.newline
         end
@@ -32297,12 +33138,15 @@
         table_caption = parsers.fail
       end
 
-      local PipeTable = Ct(table_row * parsers.newline * (parsers.check_minimal_indent_and_trail / {})
-                        * table_hline * parsers.newline
-                        * ((parsers.check_minimal_indent / {}) * table_row * parsers.newline)^0)
-                      / make_pipe_table_rectangular
-                      * table_caption^-1
-                      / writer.table
+      local PipeTable
+        = Ct( table_row * parsers.newline
+            * (parsers.check_minimal_indent_and_trail / {})
+          * table_hline * parsers.newline
+          * ( (parsers.check_minimal_indent / {})
+            * table_row * parsers.newline)^0)
+        / make_pipe_table_rectangular
+        * table_caption^-1
+        / writer.table
 
       self.insert_pattern("Block after Blockquote",
                           PipeTable, "PipeTable")
@@ -32470,7 +33314,8 @@
       local writer = self.writer
 
       local Superscript = (
-        parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex)
+        parsers.between(parsers.Str, parsers.circumflex,
+                        parsers.circumflex)
       ) / writer.superscript
 
       self.insert_pattern("Inline after LinkAndEmph",
@@ -32533,11 +33378,13 @@
         return str:gsub("^%s*(.-)$", "%1")
       end
 
-      local allowed_before_closing = B( parsers.backslash * parsers.any
-                                      + parsers.any * (parsers.any - parsers.backslash))
+      local allowed_before_closing
+        = B( parsers.backslash * parsers.any
+           + parsers.any * (parsers.any - parsers.backslash))
 
-      local allowed_before_closing_no_space = B( parsers.backslash * parsers.any
-                                               + parsers.any * (parsers.nonspacechar - parsers.backslash))
+      local allowed_before_closing_no_space
+        = B( parsers.backslash * parsers.any
+           + parsers.any * (parsers.nonspacechar - parsers.backslash))
 
 %    \end{macrocode}
 % \begin{markdown}
@@ -32546,18 +33393,20 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-      local dollar_math_content = (parsers.newline * (parsers.check_optional_indent / "")
-                                + parsers.backslash^-1
-                                * parsers.linechar)
-                                - parsers.blankline^2
-                                - parsers.dollar
+      local dollar_math_content
+        = (parsers.newline * (parsers.check_optional_indent / "")
+        + parsers.backslash^-1
+        * parsers.linechar)
+        - parsers.blankline^2
+        - parsers.dollar
 
       local inline_math_opening_dollars = parsers.dollar
                                         * #(parsers.nonspacechar)
 
-      local inline_math_closing_dollars = allowed_before_closing_no_space
-                                        * parsers.dollar
-                                        * -#(parsers.digit)
+      local inline_math_closing_dollars
+        = allowed_before_closing_no_space
+        * parsers.dollar
+        * -#(parsers.digit)
 
       local inline_math_dollars = between(Cs( dollar_math_content),
                                           inline_math_opening_dollars,
@@ -32580,9 +33429,10 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-      local backslash_math_content  = (parsers.newline * (parsers.check_optional_indent / "")
-                                    + parsers.linechar)
-                                    - parsers.blankline^2
+      local backslash_math_content
+        = (parsers.newline * (parsers.check_optional_indent / "")
+        + parsers.linechar)
+        - parsers.blankline^2
 %    \end{macrocode}
 % \begin{markdown}
 %
@@ -32702,11 +33552,15 @@
 % \acro{yaml} metadata block syntax extension. When the
 % `expect_jekyll_data` parameter is `true`, then a markdown document
 % may begin directly with \acro{yaml} metadata and may contain nothing
-% but \acro{yaml} metadata.
+% but \acro{yaml} metadata. When both `expect_jekyll_data` and
+% `ensure_jekyll_data` parameters are `true`, then a a markdown document must
+% begin directly with \acro{yaml} metadata and must contain nothing but
+% \acro{yaml} metadata.
 %
 % \end{markdown}
 %  \begin{macrocode}
-M.extensions.jekyll_data = function(expect_jekyll_data)
+M.extensions.jekyll_data = function(expect_jekyll_data,
+                                    ensure_jekyll_data)
   return {
     name = "built-in jekyll_data syntax extension",
     extend_writer = function(self)
@@ -32719,7 +33573,8 @@
 % the key `p` in the parent table; if `p` is nil, then the table has no parent.
 % All scalar keys and values encountered in the table will be cast to a string
 % following \acro{yaml} serialization rules. String values will also be
-% transformed using the function `t`.
+% transformed using the function `t` for the typographic output format used by
+% the \mref{markdownRendererJekyllDataTypographicString} macro.
 %
 % \end{markdown}
 %  \begin{macrocode}
@@ -32764,17 +33619,18 @@
         ::not_a_sequence::
 
         if is_sequence then
-            table.insert(buf, "\\markdownRendererJekyllDataSequenceBegin{")
-            table.insert(buf, self.identifier(p or "null"))
-            table.insert(buf, "}{")
-            table.insert(buf, #keys)
-            table.insert(buf, "}")
+          table.insert(buf,
+            "\\markdownRendererJekyllDataSequenceBegin{")
+          table.insert(buf, self.identifier(p or "null"))
+          table.insert(buf, "}{")
+          table.insert(buf, #keys)
+          table.insert(buf, "}")
         else
-            table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{")
-            table.insert(buf, self.identifier(p or "null"))
-            table.insert(buf, "}{")
-            table.insert(buf, #keys)
-            table.insert(buf, "}")
+          table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{")
+          table.insert(buf, self.identifier(p or "null"))
+          table.insert(buf, "}{")
+          table.insert(buf, #keys)
+          table.insert(buf, "}")
         end
 
         for _, k in ipairs(keys) do
@@ -32802,9 +33658,16 @@
               table.insert(buf, v)
               table.insert(buf, "}")
             elseif typ == "string" then
-              table.insert(buf, "\\markdownRendererJekyllDataString{")
+              table.insert(buf,
+                "\\markdownRendererJekyllDataProgrammaticString{")
               table.insert(buf, k)
               table.insert(buf, "}{")
+              table.insert(buf, self.identifier(v))
+              table.insert(buf, "}")
+              table.insert(buf,
+                "\\markdownRendererJekyllDataTypographicString{")
+              table.insert(buf, k)
+              table.insert(buf, "}{")
               table.insert(buf, t(v))
               table.insert(buf, "}")
             elseif typ == "table" then
@@ -32812,8 +33675,10 @@
               table.insert(buf, k)
               table.insert(buf, "}")
             else
-              error(format("Unexpected type %s for value of " ..
-                           "YAML key %s", typ, k))
+              local error = self.error(format(
+                "Unexpected type %s for value of "
+                .. "YAML key %s", typ, k))
+              table.insert(buf, error)
             end
           end
         end
@@ -32835,39 +33700,50 @@
       local writer = self.writer
 
       local JekyllData
-                    = Cmt( C((parsers.line - P("---") - P("..."))^0)
-                         , function(s, i, text) -- luacheck: ignore s i
-                             local data
-                             local ran_ok, _ = pcall(function()
-                               -- TODO: Replace with `require("tinyyaml")` in TeX Live 2023
-                               local tinyyaml = require("markdown-tinyyaml")
-                               data = tinyyaml.parse(text, {timestamps=false})
-                             end)
-                             if ran_ok and data ~= nil then
-                               return true, writer.jekyllData(data, function(s)
-                                 return self.parser_functions.parse_blocks_nested(s)
-                               end, nil)
-                             else
-                               return false
-                             end
-                           end
-                         )
+        = Cmt( C((parsers.line - P("---") - P("..."))^0)
+             , function(s, i, text) -- luacheck: ignore s i
+                 local data
+                 local ran_ok, _ = pcall(function()
+                   -- TODO: Use `require("tinyyaml")` in TeX Live 2023
+                   local tinyyaml = require("markdown-tinyyaml")
+                   data = tinyyaml.parse(text, {timestamps=false})
+                 end)
+                 if ran_ok and data ~= nil then
+                   return true, writer.jekyllData(data, function(s)
+                     return self.parser_functions.parse_blocks_nested(s)
+                   end, nil)
+                 else
+                   return false
+                 end
+               end
+             )
 
       local UnexpectedJekyllData
-                    = P("---")
-                    * parsers.blankline / 0
-                    * #(-parsers.blankline)  -- if followed by blank, it's thematic break
-                    * JekyllData
-                    * (P("---") + P("..."))
+        = P("---")
+        * parsers.blankline / 0
+        -- if followed by blank, it's thematic break
+        * #(-parsers.blankline)
+        * JekyllData
+        * (P("---") + P("..."))
 
       local ExpectedJekyllData
-                    = ( P("---")
-                      * parsers.blankline / 0
-                      * #(-parsers.blankline)  -- if followed by blank, it's thematic break
-                      )^-1
-                    * JekyllData
-                    * (P("---") + P("..."))^-1
+        = ( P("---")
+          * parsers.blankline / 0
+          -- if followed by blank, it's thematic break
+          * #(-parsers.blankline)
+          )^-1
+        * JekyllData
+        * (P("---") + P("..."))^-1
 
+      if ensure_jekyll_data then
+        ExpectedJekyllData = ExpectedJekyllData
+                           * parsers.eof
+      else
+        ExpectedJekyllData = ( ExpectedJekyllData
+                             * (V("Blank")^0 / writer.interblocksep)
+                             )^-1
+      end
+
       self.insert_pattern("Block before Blockquote",
                           UnexpectedJekyllData, "UnexpectedJekyllData")
       if expect_jekyll_data then
@@ -32881,12 +33757,22 @@
 %
 %### Conversion from Markdown to Plain \TeX{}
 %
-% The \luamref{new} function returns a conversion function that takes a
-% markdown string and turns it into a plain \TeX{} output. See Section
-% <#sec:lua-conversion>.
+% The \luamref{new} function of file `markdown.lua` loads file
+% `markdown-parser.lua` and calls its own function \luamref{new} unless option
+% \Opt{eagerCache} or \Opt{finalizeCache} has been enabled and a cached
+% conversion output exists, in which case it is returned without loading file
+% `markdown-parser.lua`.
 %
 % \end{markdown}
+% \iffalse
+%</lua>
+%<*lua-loader>
+% \fi
 %  \begin{macrocode}
+local function warn(s)
+  io.stderr:write("Warning: " .. s .. "\n")
+end
+
 function M.new(options)
 %    \end{macrocode}
 % \par
@@ -32903,6 +33789,114 @@
 % \par
 % \begin{markdown}
 %
+% Return a conversion function that tries to produce a cached conversion output
+% exists. If no cached conversion output exists, we load the file
+% `markdown-parser.lua` and use it to convert the input.
+%
+% \end{markdown}
+%  \begin{macrocode}
+  local parser_convert = nil
+  return function(input)
+    local function convert(input)
+      if parser_convert == nil then
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Lazy-load `markdown-parser.lua` and check that it originates from the same
+% version of the Markdown package.
+%
+% \end{markdown}
+%  \begin{macrocode}
+        local parser = require("markdown-parser")
+        if metadata.version ~= parser.metadata.version then
+          warn("markdown.lua " .. metadata.version .. " used with " ..
+               "markdown-parser.lua " .. parser.metadata.version .. ".")
+        end
+        parser_convert = parser.new(options)
+      end
+      return parser_convert(input)
+    end
+%    \end{macrocode}
+% \begin{markdown}
+% If we cache markdown documents, produce the cache file and transform its
+% filename to plain \TeX{} output.
+%
+% When determining the name of the cache file, create salt for the hashing
+% function out of the package version and the passed options recognized by the
+% Lua interface (see Section <#sec:lua-options>).
+% \end{markdown}
+%  \begin{macrocode}
+    local output
+    if options.eagerCache or options.finalizeCache then
+      local salt = util.salt(options)
+      local name = util.cache(options.cacheDir, input, salt, convert,
+                              ".md.tex")
+      output = [[\input{]] .. name .. [[}\relax]]
+%    \end{macrocode}
+% \begin{markdown}
+% Otherwise, return the result of the conversion directly.
+% \end{markdown}
+%  \begin{macrocode}
+    else
+      output = convert(input)
+    end
+%    \end{macrocode}
+% \begin{markdown}
+% If the \Opt{finalizeCache} option is enabled, populate the frozen cache in
+% the file \Opt{frozenCacheFileName} with an entry for markdown document
+% number \Opt{frozenCacheCounter}.
+% \end{markdown}
+%  \begin{macrocode}
+    if options.finalizeCache then
+      local file, mode
+      if options.frozenCacheCounter > 0 then
+        mode = "a"
+      else
+        mode = "w"
+      end
+      file = assert(io.open(options.frozenCacheFileName, mode),
+        [[Could not open file "]] .. options.frozenCacheFileName
+        .. [[" for writing]])
+      assert(file:write(
+        [[\expandafter\global\expandafter\def\csname ]]
+        .. [[markdownFrozenCache]] .. options.frozenCacheCounter
+        .. [[\endcsname{]] .. output .. [[}]] .. "\n"))
+      assert(file:close())
+    end
+    return output
+  end
+end
+%    \end{macrocode}
+% \iffalse
+%</lua-loader>
+%<*lua>
+% \fi
+% \par
+% \begin{markdown}
+%
+% The \luamref{new} function from file `markdown-parser.lua` returns a
+% conversion function that takes a markdown string and turns it into a plain
+% \TeX{} output. See Section <#sec:lua-conversion>.
+%
+% \end{markdown}
+%  \begin{macrocode}
+function M.new(options)
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% Make the `options` table inherit from the \luamref{defaultOptions} table.
+%
+% \end{markdown}
+%  \begin{macrocode}
+  options = options or {}
+  setmetatable(options, { __index = function (_, key)
+    return defaultOptions[key] end })
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
 % If the singleton cache contains a conversion function for the same `options`,
 % reuse it.
 %
@@ -32916,7 +33910,15 @@
             goto miss
           end
         end
-      elseif singletonCache.options[k] ~= options[k] then
+%    \end{macrocode}
+% \begin{markdown}
+%
+% The \Opt{cacheDir} option is disregarded.
+%
+% \end{markdown}
+%  \begin{macrocode}
+      elseif k ~= "cacheDir"
+         and singletonCache.options[k] ~= options[k] then
         goto miss
       end
     end
@@ -32977,7 +33979,7 @@
 
   if options.jekyllData then
     local jekyll_data_extension = M.extensions.jekyll_data(
-      options.expectJekyllData)
+      options.expectJekyllData, options.ensureJekyllData)
     table.insert(extensions, jekyll_data_extension)
   end
 
@@ -33040,7 +34042,8 @@
   end
 
   if options.citations then
-    local citations_extension = M.extensions.citations(options.citationNbsps)
+    local citations_extension
+      = M.extensions.citations(options.citationNbsps)
     table.insert(extensions, citations_extension)
   end
 
@@ -33065,7 +34068,9 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-      local pathname = kpse.lookup(filename)
+      local pathname = assert(kpse.find_file(filename),
+        [[Could not locate user-defined syntax extension "]]
+        .. filename)
       local input_file = assert(io.open(pathname, "r"),
         [[Could not open user-defined syntax extension "]]
         .. pathname .. [[" for reading]])
@@ -33095,7 +34100,8 @@
         .. type(user_extension.api_version)
         .. [[" but "number" was expected]])
       assert(user_extension.api_version > 0
-         and user_extension.api_version <= metadata.user_extension_api_version,
+         and user_extension.api_version
+          <= metadata.user_extension_api_version,
         [[User-defined syntax extension "]] .. pathname
         .. [[" uses syntax extension API version "]]
         .. user_extension.api_version .. [[ but markdown.lua ]]
@@ -33113,7 +34119,8 @@
         .. [[" but "number" was expected]])
       assert(user_extension.grammar_version == metadata.grammar_version,
         [[User-defined syntax extension "]] .. pathname
-        .. [[" uses grammar version "]] .. user_extension.grammar_version
+        .. [[" uses grammar version "]]
+        .. user_extension.grammar_version
         .. [[ but markdown.lua ]] .. metadata.version
         .. [[ uses grammar version ]] .. metadata.grammar_version
         .. [[, which is incompatible]])
@@ -33195,11 +34202,16 @@
 %  \begin{macrocode}
   return convert
 end
-
+%    \end{macrocode}
+% \iffalse
+%</lua>
+%<*lua,lua-loader>
+% \fi
+%  \begin{macrocode}
 return M
 %    \end{macrocode}
 % \iffalse
-%</lua>
+%</lua,lua-loader>
 %<*lua-cli>
 % \fi
 % \par
@@ -33553,18 +34565,22 @@
 \def\markdownRendererUlEndTightPrototype{}%
 \def\markdownRendererOlBeginPrototype{}%
 \def\markdownRendererOlBeginTightPrototype{}%
-\def\markdownRendererFancyOlBeginPrototype#1#2{\markdownRendererOlBegin}%
-\def\markdownRendererFancyOlBeginTightPrototype#1#2{\markdownRendererOlBeginTight}%
+\def\markdownRendererFancyOlBeginPrototype#1#2{%
+  \markdownRendererOlBegin}%
+\def\markdownRendererFancyOlBeginTightPrototype#1#2{%
+  \markdownRendererOlBeginTight}%
 \def\markdownRendererOlItemPrototype{}%
 \def\markdownRendererOlItemWithNumberPrototype#1{}%
 \def\markdownRendererOlItemEndPrototype{}%
 \def\markdownRendererFancyOlItemPrototype{\markdownRendererOlItem}%
-\def\markdownRendererFancyOlItemWithNumberPrototype{\markdownRendererOlItemWithNumber}%
+\def\markdownRendererFancyOlItemWithNumberPrototype{%
+  \markdownRendererOlItemWithNumber}%
 \def\markdownRendererFancyOlItemEndPrototype{}%
 \def\markdownRendererOlEndPrototype{}%
 \def\markdownRendererOlEndTightPrototype{}%
 \def\markdownRendererFancyOlEndPrototype{\markdownRendererOlEnd}%
-\def\markdownRendererFancyOlEndTightPrototype{\markdownRendererOlEndTight}%
+\def\markdownRendererFancyOlEndTightPrototype{%
+  \markdownRendererOlEndTight}%
 \def\markdownRendererDlBeginPrototype{}%
 \def\markdownRendererDlBeginTightPrototype{}%
 \def\markdownRendererDlItemPrototype#1{#1}%
@@ -33638,6 +34654,8 @@
 \ExplSyntaxOff
 \def\markdownRendererSectionBeginPrototype{}%
 \def\markdownRendererSectionEndPrototype{}%
+\let\markdownRendererWarningPrototype\markdownWarning
+\let\markdownRendererErrorPrototype\markdownError
 %    \end{macrocode}
 % \par
 % \begin{markdown}
@@ -33652,7 +34670,7 @@
 %  \begin{macrocode}
 \ExplSyntaxOn
 \cs_new:Nn
-  \@@_plain_tex_default_input_raw_inline_renderer_prototype:nn
+  \@@_plain_tex_default_input_raw_inline:nn
   {
     \str_case:nn
       { #2 }
@@ -33662,7 +34680,7 @@
       }
   }
 \cs_new:Nn
-  \@@_plain_tex_default_input_raw_block_renderer_prototype:nn
+  \@@_plain_tex_default_input_raw_block:nn
   {
     \str_case:nn
       { #2 }
@@ -33674,7 +34692,7 @@
 \cs_gset:Npn
   \markdownRendererInputRawInlinePrototype#1#2
   {
-    \@@_plain_tex_default_input_raw_inline_renderer_prototype:nn
+    \@@_plain_tex_default_input_raw_inline:nn
       { #1 }
       { #2 }
   }
@@ -33681,7 +34699,7 @@
 \cs_gset:Npn
   \markdownRendererInputRawBlockPrototype#1#2
   {
-    \@@_plain_tex_default_input_raw_block_renderer_prototype:nn
+    \@@_plain_tex_default_input_raw_block:nn
       { #1 }
       { #2 }
   }
@@ -33918,7 +34936,17 @@
     { #1 }
     { #2 }
 }
-\def\markdownRendererJekyllDataStringPrototype#1#2{
+%    \end{macrocode}
+% \par
+% \begin{markdown}
+%
+% We will process all string scalar values assuming that they may contain
+% markdown markup and are intended for typesetting.
+%
+% \end{markdown}
+%  \begin{macrocode}
+\def\markdownRendererJekyllDataProgrammaticStringPrototype#1#2{}
+\def\markdownRendererJekyllDataTypographicStringPrototype#1#2{
   \markdown_jekyll_data_set_keyvals:nn
     { #1 }
     { #2 }
@@ -34447,12 +35475,14 @@
 \msg_new:nnn
   { markdown }
   { buffering-markinline }
-  { Buffering~inline~markdown~input~into~the~temporary~input~file~"#1". }
+  { Buffering~inline~markdown~input~into~
+    the~temporary~input~file~"#1". }
 \msg_new:nnnn
   { markdown }
   { markinline-peek-failure }
   { Use~of~\iow_char:N \\ markinline~doesn't~match~its~definition }
-  { The~macro~should~be~followed~by~inline~markdown~text~in~curly~braces }
+  { The~macro~should~be~followed~by~inline~
+    markdown~text~in~curly~braces }
 \ExplSyntaxOff
 %    \end{macrocode}
 % \begin{markdown}
@@ -34471,6 +35501,13 @@
   \markdownInput
   #1
   {
+    \@@_if_option:nTF
+      { frozenCache }
+      {
+        \markdownInputRaw
+          { #1 }
+      }
+      {
 %    \end{macrocode}
 % \begin{markdown}
 %
@@ -34480,19 +35517,23 @@
 %
 % \end{markdown}
 %  \begin{macrocode}
-    \file_get_full_name:nNTF
-      { #1 }
-      \l_tmpa_tl
-      {
-        \exp_args:NV
-          \markdownInputRaw
+        \tl_set:Nx
           \l_tmpa_tl
-      }
-      {
-        \msg_error:nnnV
-          { markdown }
-          { markdown-file-does-not-exist }
           { #1 }
+        \file_get_full_name:VNTF
+          \l_tmpa_tl
+          \l_tmpb_tl
+          {
+            \exp_args:NV
+              \markdownInputRaw
+              \l_tmpb_tl
+          }
+          {
+            \msg_error:nnV
+              { markdown }
+              { markdown-file-does-not-exist }
+              \l_tmpa_tl
+          }
       }
   }
 \msg_new:nnn
@@ -34546,7 +35587,8 @@
       |fi
       |markdownInfo{Including markdown document number
         "|the|markdownOptionFrozenCacheCounter" from frozen cache}%
-      |csname markdownFrozenCache|the|markdownOptionFrozenCacheCounter|endcsname
+      |csname markdownFrozenCache%
+        |the|markdownOptionFrozenCacheCounter|endcsname
       |global|advance|markdownOptionFrozenCacheCounter by 1|relax
     }{%
       |markdownInfo{Including markdown document "&1"}%
@@ -34770,13 +35812,26 @@
 % we let the \mref{markdownReadAndConvert} macro process the rest of the
 % \LaTeX{} environment.
 %
+% We also make provision for using the \mref{markdown} command as a part of a
+% different \LaTeX{} environment as follows:
+%
+% ``` tex
+% \newenvironment{foo}\%
+%                {code before \markdown[some, options]}\%
+%                {\markdownEnd code after}
+% ```
+%
 % \end{markdown}
 %  \begin{macrocode}
-        \c { markdownReadAndConvert at markdown } { }
+        \c { exp_args:NV }
+          \c { markdownReadAndConvert@ }
+          \c { @currenvir }
       }
       {
         \group_end:
-        \markdownReadAndConvert at markdown { }
+        \exp_args:NV
+          \markdownReadAndConvert@
+          \@currenvir
       }
   }
   { \markdownEnd }
@@ -34790,7 +35845,8 @@
       { #1 }
     \@@_setup:n
       { #1 }
-    \markdownReadAndConvert at markdown *
+    \markdownReadAndConvert@
+      { markdown* }
   }
   { \markdownEnd }
 \msg_new:nnn
@@ -34800,6 +35856,9 @@
     The~markdown*~LaTeX~environment~has~been~deprecated~and~will~
     be~removed~in~the~next~major~version~of~the~Markdown~package.
   }
+\cs_generate_variant:Nn
+  \@@_setup:n
+  { V }
 \ExplSyntaxOff
 \begingroup
 %    \end{macrocode}
@@ -34813,9 +35872,9 @@
 %  \begin{macrocode}
   \catcode`\|=0\catcode`\<=1\catcode`\>=2%
   \catcode`\\=12|catcode`|{=12|catcode`|}=12%
-  |gdef|markdownReadAndConvert at markdown#1<%
-    |markdownReadAndConvert<\end{markdown#1}>%
-                           <|end<markdown#1>>>%
+  |gdef|markdownReadAndConvert@#1<%
+    |markdownReadAndConvert<\end{#1}>%
+                           <|end<#1>>>%
 |endgroup
 %    \end{macrocode}
 % \par
@@ -35030,7 +36089,8 @@
 % \end{markdown}
 %  \begin{macrocode}
     }{%
-      \markdown at witiko@dot at oldRendererInputFencedCodePrototype{#1}{#2}{#3}%
+      \markdown at witiko@dot at oldRendererInputFencedCodePrototype
+        {#1}{#2}{#3}%
     }%
   }%
   \next#2 \relax}%
@@ -35846,8 +36906,9 @@
   \advance\markdownLaTeXCitationsCounter by 1\relax
   \ifx\relax#4\relax
     \ifx\relax#5\relax
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
-        \cite{#1#2#6}%  Without prenotes and postnotes, just accumulate cites
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+        \relax
+        \cite{#1#2#6}%  No prenotes/postnotes, just accumulate cites
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
         \@gobblethree
@@ -35857,7 +36918,8 @@
         \cite{#1}%
       \fi
       \cite[#5]{#6}%
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+      \relax
       \else
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
@@ -35884,7 +36946,8 @@
       \space  % Insert a space before the prenote in later citations
     \fi
     #4~\expandafter\cite\ifx\relax#5\relax{#6}\else[#5]{#6}\fi
-    \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+    \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+    \relax
     \else
       \expandafter\expandafter\expandafter
       \expandafter\expandafter\expandafter\expandafter
@@ -35904,8 +36967,9 @@
   \advance\markdownLaTeXCitationsCounter by 1\relax
   \ifx\relax#3\relax
     \ifx\relax#4\relax
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
-        \citep{#1,#5}%  Without prenotes and postnotes, just accumulate cites
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+      \relax
+        \citep{#1,#5}%  No prenotes/postnotes, just accumulate cites
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
         \@gobbletwo
@@ -35915,7 +36979,8 @@
         \citep{#1}%
       \fi
       \citep[][#4]{#5}%
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+      \relax
       \else
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
@@ -35935,7 +37000,8 @@
       \citep{#1}%
     \fi
     \citep[#3][#4]{#5}%
-    \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+    \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+    \relax
     \else
       \expandafter\expandafter\expandafter
       \expandafter\expandafter\expandafter\expandafter
@@ -35950,8 +37016,9 @@
   \advance\markdownLaTeXCitationsCounter by 1\relax
   \ifx\relax#3\relax
     \ifx\relax#4\relax
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
-        \citet{#1,#5}%  Without prenotes and postnotes, just accumulate cites
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+      \relax
+        \citet{#1,#5}%  No prenotes/postnotes, just accumulate cites
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
         \@gobbletwo
@@ -35961,10 +37028,13 @@
         \citet{#1}%
       \fi
       , \citet[#3][#4]{#5}%
-      \ifnum\markdownLaTeXCitationsCounter<\markdownLaTeXCitationsTotal\relax
+      \ifnum\markdownLaTeXCitationsCounter<\markdownLaTeXCitationsTotal
+      \relax
         ,
       \else
-        \ifnum\markdownLaTeXCitationsCounter=\markdownLaTeXCitationsTotal\relax
+        \ifnum
+        \markdownLaTeXCitationsCounter=\markdownLaTeXCitationsTotal
+        \relax
           ,
         \fi
       \fi
@@ -35983,10 +37053,13 @@
       \citet{#1}%
     \fi
     , \citet[#3][#4]{#5}%
-    \ifnum\markdownLaTeXCitationsCounter<\markdownLaTeXCitationsTotal\relax
+    \ifnum\markdownLaTeXCitationsCounter<\markdownLaTeXCitationsTotal
+    \relax
       ,
     \else
-      \ifnum\markdownLaTeXCitationsCounter=\markdownLaTeXCitationsTotal\relax
+      \ifnum
+      \markdownLaTeXCitationsCounter=\markdownLaTeXCitationsTotal
+      \relax
         ,
       \fi
     \fi
@@ -36001,13 +37074,15 @@
 % BibLaTeX implementation
 \def\markdownLaTeXBibLaTeXCitations#1#2#3#4#5{%
   \advance\markdownLaTeXCitationsCounter by 1\relax
-  \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+  \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+  \relax
     \autocites#1[#3][#4]{#5}%
     \expandafter\@gobbletwo
   \fi\markdownLaTeXBibLaTeXCitations{#1[#3][#4]{#5}}}
 \def\markdownLaTeXBibLaTeXTextCitations#1#2#3#4#5{%
   \advance\markdownLaTeXCitationsCounter by 1\relax
-  \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+  \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+  \relax
     \textcites#1[#3][#4]{#5}%
     \expandafter\@gobbletwo
   \fi\markdownLaTeXBibLaTeXTextCitations{#1[#3][#4]{#5}}}
@@ -36100,7 +37175,8 @@
     {
       \markdownLaTeXRendererAutolink { #2 } { #3 }
     }{
-      \markdownLaTeXRendererDirectOrIndirectLink { #1 } { #2 } { #3 } { #4 }
+      \markdownLaTeXRendererDirectOrIndirectLink
+        { #1 } { #2 } { #3 } { #4 }
     }
 }
 \def\markdownLaTeXRendererAutolink#1#2{%
@@ -36399,13 +37475,13 @@
       {
         { latex }
           {
-            \@@_plain_tex_default_input_raw_inline_renderer_prototype:nn
+            \@@_plain_tex_default_input_raw_inline:nn
               { #1 }
               { tex }
           }
       }
       {
-        \@@_plain_tex_default_input_raw_inline_renderer_prototype:nn
+        \@@_plain_tex_default_input_raw_inline:nn
           { #1 }
           { #2 }
       }
@@ -36418,13 +37494,13 @@
       {
         { latex }
           {
-            \@@_plain_tex_default_input_raw_block_renderer_prototype:nn
+            \@@_plain_tex_default_input_raw_block:nn
               { #1 }
               { tex }
           }
       }
       {
-        \@@_plain_tex_default_input_raw_block_renderer_prototype:nn
+        \@@_plain_tex_default_input_raw_block:nn
           { #1 }
           { #2 }
       }
@@ -36607,7 +37683,7 @@
 %    \end{macrocode}
 % \iffalse
 %</context>
-%<*themes-witiko-markdown-defaults-context>
+%<*themes-witiko-markdown-defaults-ctx>
 % \fi
 % \par
 % \begin{markdown}
@@ -36789,7 +37865,8 @@
 \def\markdownRendererStrikeThroughPrototype#1{\overstrikes{#1}}
 \def\markdownRendererSuperscriptPrototype#1{\high{#1}}
 \def\markdownRendererSubscriptPrototype#1{\low{#1}}
-\def\markdownRendererDisplayMathPrototype#1{\startformula#1\stopformula}%
+\def\markdownRendererDisplayMathPrototype#1{%
+  \startformula#1\stopformula}%
 %    \end{macrocode}
 % \par
 % \begin{markdown}
@@ -36815,8 +37892,10 @@
       \placetable{#1}{\the\markdownConTeXtTable}}%
   \fi
   \begingroup
-  \setupTABLE[r][each][topframe=off, bottomframe=off, leftframe=off, rightframe=off]
-  \setupTABLE[c][each][topframe=off, bottomframe=off, leftframe=off, rightframe=off]
+  \setupTABLE[r][each][topframe=off, bottomframe=off,
+                       leftframe=off, rightframe=off]
+  \setupTABLE[c][each][topframe=off, bottomframe=off,
+                       leftframe=off, rightframe=off]
   \setupTABLE[r][1][topframe=on, bottomframe=on]
   \setupTABLE[r][#1][bottomframe=on]
   \markdownConTeXtRowCounter=0%
@@ -36854,7 +37933,8 @@
   \fi\if#1r%
     \setupTABLE[c][\the\markdownConTeXtColumnCounter][align=left]
   \fi
-  \ifnum\markdownConTeXtColumnCounter<\markdownConTeXtColumnTotal\relax\else
+  \ifnum\markdownConTeXtColumnCounter<\markdownConTeXtColumnTotal\relax
+  \else
     \expandafter\gobbleoneargument
   \fi\markdownConTeXtReadAlignments}
 \def\markdownConTeXtRenderTableCell#1{%
@@ -36861,7 +37941,8 @@
   \advance\markdownConTeXtColumnCounter by 1\relax
   \markdownConTeXtTable=\expandafter{%
     \the\markdownConTeXtTable\bTD#1\eTD}%
-  \ifnum\markdownConTeXtColumnCounter<\markdownConTeXtColumnTotal\relax\else
+  \ifnum\markdownConTeXtColumnCounter<\markdownConTeXtColumnTotal\relax
+  \else
     \expandafter\gobbleoneargument
   \fi\markdownConTeXtRenderTableCell}
 %    \end{macrocode}
@@ -36884,13 +37965,13 @@
       {
         { latex }
           {
-            \@@_plain_tex_default_input_raw_inline_renderer_prototype:nn
+            \@@_plain_tex_default_input_raw_inline:nn
               { #1 }
               { context }
           }
       }
       {
-        \@@_plain_tex_default_input_raw_inline_renderer_prototype:nn
+        \@@_plain_tex_default_input_raw_inline:nn
           { #1 }
           { #2 }
       }
@@ -36903,13 +37984,13 @@
       {
         { context }
           {
-            \@@_plain_tex_default_input_raw_block_renderer_prototype:nn
+            \@@_plain_tex_default_input_raw_block:nn
               { #1 }
               { tex }
           }
       }
       {
-        \@@_plain_tex_default_input_raw_block_renderer_prototype:nn
+        \@@_plain_tex_default_input_raw_block:nn
           { #1 }
           { #2 }
       }
@@ -36923,7 +38004,7 @@
 \protect
 %    \end{macrocode}
 % \iffalse
-%</themes-witiko-markdown-defaults-context>
+%</themes-witiko-markdown-defaults-ctx>
 %<*context>
 % \fi
 % \par

Modified: trunk/Master/texmf-dist/source/generic/markdown/markdown.ins
===================================================================
--- trunk/Master/texmf-dist/source/generic/markdown/markdown.ins	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/source/generic/markdown/markdown.ins	2024-08-30 21:14:17 UTC (rev 72146)
@@ -2,7 +2,8 @@
 \generate{
   \usepreamble\luapreamble
   \usepostamble\luapostamble
-    \file{markdown.lua}{\from{markdown.dtx}{lua}}
+    \file{markdown.lua}{\from{markdown.dtx}{lua-loader}}
+    \file{markdown-parser.lua}{\from{markdown.dtx}{lua}}
     \file{markdown-cli.lua}{\from{markdown.dtx}{lua-cli}}
   \usepreamble\texpreamble
   \usepostamble\texpostamble
@@ -14,7 +15,7 @@
     \file{markdownthemewitiko_tilde.tex}{\from{markdown.dtx}{themes-witiko-tilde}}
     \file{markdownthemewitiko_markdown_defaults.tex}{\from{markdown.dtx}{themes-witiko-markdown-defaults-tex}}
     \file{markdownthemewitiko_markdown_defaults.sty}{\from{markdown.dtx}{themes-witiko-markdown-defaults-latex}}
-    \file{t-markdownthemewitiko_markdown_defaults.tex}{\from{markdown.dtx}{themes-witiko-markdown-defaults-context}}
+    \file{t-markdownthemewitiko_markdown_defaults.tex}{\from{markdown.dtx}{themes-witiko-markdown-defaults-ctx}}
     \file{markdownthemewitiko_markdown_techdoc.sty}{\from{markdown.dtx}{themes-witiko-markdown-techdoc}}
   \usepreamble\empty
   \usepostamble\empty
@@ -25,6 +26,6 @@
     \file{markdown.css}{\from{markdown.dtx}{manual-css}}
     \file{markdown-figure-block-diagram.tex}{\from{markdown.dtx}{techdoc-block-diagram}}
     \file{markdown.bib}{\from{markdown.dtx}{techdoc-bibliography}}
-    \file{DEPENDS.txt}{\from{markdown.dtx}{depends}}
+    \file{DEPENDS-raw.txt}{\from{markdown.dtx}{depends}}
 }
 \endbatchfile

Modified: trunk/Master/texmf-dist/tex/context/third/markdown/t-markdownthemewitiko_markdown_defaults.tex
===================================================================
--- trunk/Master/texmf-dist/tex/context/third/markdown/t-markdownthemewitiko_markdown_defaults.tex	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/tex/context/third/markdown/t-markdownthemewitiko_markdown_defaults.tex	2024-08-30 21:14:17 UTC (rev 72146)
@@ -4,7 +4,7 @@
 %%
 %% The original source files were:
 %%
-%% markdown.dtx  (with options: `themes-witiko-markdown-defaults-context')
+%% markdown.dtx  (with options: `themes-witiko-markdown-defaults-ctx')
 %% 
 %% Copyright (C) 2016-2024 Vít Starý Novotný, Andrej Genčur
 %% 
@@ -162,7 +162,8 @@
 \def\markdownRendererStrikeThroughPrototype#1{\overstrikes{#1}}
 \def\markdownRendererSuperscriptPrototype#1{\high{#1}}
 \def\markdownRendererSubscriptPrototype#1{\low{#1}}
-\def\markdownRendererDisplayMathPrototype#1{\startformula#1\stopformula}%
+\def\markdownRendererDisplayMathPrototype#1{%
+  \startformula#1\stopformula}%
 \newcount\markdownConTeXtRowCounter
 \newcount\markdownConTeXtRowTotal
 \newcount\markdownConTeXtColumnCounter
@@ -179,8 +180,10 @@
       \placetable{#1}{\the\markdownConTeXtTable}}%
   \fi
   \begingroup
-  \setupTABLE[r][each][topframe=off, bottomframe=off, leftframe=off, rightframe=off]
-  \setupTABLE[c][each][topframe=off, bottomframe=off, leftframe=off, rightframe=off]
+  \setupTABLE[r][each][topframe=off, bottomframe=off,
+                       leftframe=off, rightframe=off]
+  \setupTABLE[c][each][topframe=off, bottomframe=off,
+                       leftframe=off, rightframe=off]
   \setupTABLE[r][1][topframe=on, bottomframe=on]
   \setupTABLE[r][#1][bottomframe=on]
   \markdownConTeXtRowCounter=0%
@@ -218,7 +221,8 @@
   \fi\if#1r%
     \setupTABLE[c][\the\markdownConTeXtColumnCounter][align=left]
   \fi
-  \ifnum\markdownConTeXtColumnCounter<\markdownConTeXtColumnTotal\relax\else
+  \ifnum\markdownConTeXtColumnCounter<\markdownConTeXtColumnTotal\relax
+  \else
     \expandafter\gobbleoneargument
   \fi\markdownConTeXtReadAlignments}
 \def\markdownConTeXtRenderTableCell#1{%
@@ -225,7 +229,8 @@
   \advance\markdownConTeXtColumnCounter by 1\relax
   \markdownConTeXtTable=\expandafter{%
     \the\markdownConTeXtTable\bTD#1\eTD}%
-  \ifnum\markdownConTeXtColumnCounter<\markdownConTeXtColumnTotal\relax\else
+  \ifnum\markdownConTeXtColumnCounter<\markdownConTeXtColumnTotal\relax
+  \else
     \expandafter\gobbleoneargument
   \fi\markdownConTeXtRenderTableCell}
 \ExplSyntaxOn
@@ -237,13 +242,13 @@
       {
         { latex }
           {
-            \__markdown_plain_tex_default_input_raw_inline_renderer_prototype:nn
+            \__markdown_plain_tex_default_input_raw_inline:nn
               { #1 }
               { context }
           }
       }
       {
-        \__markdown_plain_tex_default_input_raw_inline_renderer_prototype:nn
+        \__markdown_plain_tex_default_input_raw_inline:nn
           { #1 }
           { #2 }
       }
@@ -256,13 +261,13 @@
       {
         { context }
           {
-            \__markdown_plain_tex_default_input_raw_block_renderer_prototype:nn
+            \__markdown_plain_tex_default_input_raw_block:nn
               { #1 }
               { tex }
           }
       }
       {
-        \__markdown_plain_tex_default_input_raw_block_renderer_prototype:nn
+        \__markdown_plain_tex_default_input_raw_block:nn
           { #1 }
           { #2 }
       }

Modified: trunk/Master/texmf-dist/tex/generic/markdown/markdown.tex
===================================================================
--- trunk/Master/texmf-dist/tex/generic/markdown/markdown.tex	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/tex/generic/markdown/markdown.tex	2024-08-30 21:14:17 UTC (rev 72146)
@@ -51,7 +51,9 @@
 \prop_new:N \g__markdown_default_lua_options_prop
 \seq_new:N \g__markdown_option_layers_seq
 \tl_const:Nn \c__markdown_option_layer_lua_tl { lua }
-\seq_gput_right:NV \g__markdown_option_layers_seq \c__markdown_option_layer_lua_tl
+\seq_gput_right:NV
+  \g__markdown_option_layers_seq
+  \c__markdown_option_layer_lua_tl
 \cs_new:Nn
   \__markdown_add_lua_option:nnn
   {
@@ -128,21 +130,50 @@
 \cs_generate_variant:Nn
   \msg_error:nnnn
   { nnnV }
-\seq_new:N \g__markdown_option_types_seq
-\tl_const:Nn \c__markdown_option_type_clist_tl { clist }
-\seq_gput_right:NV \g__markdown_option_types_seq \c__markdown_option_type_clist_tl
-\tl_const:Nn \c__markdown_option_type_counter_tl { counter }
-\seq_gput_right:NV \g__markdown_option_types_seq \c__markdown_option_type_counter_tl
-\tl_const:Nn \c__markdown_option_type_boolean_tl { boolean }
-\seq_gput_right:NV \g__markdown_option_types_seq \c__markdown_option_type_boolean_tl
-\tl_const:Nn \c__markdown_option_type_number_tl  { number  }
-\seq_gput_right:NV \g__markdown_option_types_seq \c__markdown_option_type_number_tl
-\tl_const:Nn \c__markdown_option_type_path_tl    { path    }
-\seq_gput_right:NV \g__markdown_option_types_seq \c__markdown_option_type_path_tl
-\tl_const:Nn \c__markdown_option_type_slice_tl   { slice   }
-\seq_gput_right:NV \g__markdown_option_types_seq \c__markdown_option_type_slice_tl
-\tl_const:Nn \c__markdown_option_type_string_tl  { string  }
-\seq_gput_right:NV \g__markdown_option_types_seq \c__markdown_option_type_string_tl
+\seq_new:N
+  \g__markdown_option_types_seq
+\tl_const:Nn
+  \c__markdown_option_type_clist_tl
+  { clist }
+\seq_gput_right:NV
+  \g__markdown_option_types_seq
+  \c__markdown_option_type_clist_tl
+\tl_const:Nn
+  \c__markdown_option_type_counter_tl
+  { counter }
+\seq_gput_right:NV
+  \g__markdown_option_types_seq
+  \c__markdown_option_type_counter_tl
+\tl_const:Nn
+  \c__markdown_option_type_boolean_tl
+  { boolean }
+\seq_gput_right:NV
+  \g__markdown_option_types_seq
+  \c__markdown_option_type_boolean_tl
+\tl_const:Nn
+  \c__markdown_option_type_number_tl
+  { number }
+\seq_gput_right:NV
+  \g__markdown_option_types_seq
+  \c__markdown_option_type_number_tl
+\tl_const:Nn
+  \c__markdown_option_type_path_tl
+  { path }
+\seq_gput_right:NV
+  \g__markdown_option_types_seq
+  \c__markdown_option_type_path_tl
+\tl_const:Nn
+  \c__markdown_option_type_slice_tl
+  { slice }
+\seq_gput_right:NV
+  \g__markdown_option_types_seq
+  \c__markdown_option_type_slice_tl
+\tl_const:Nn
+  \c__markdown_option_type_string_tl
+  { string }
+\seq_gput_right:NV
+  \g__markdown_option_types_seq
+  \c__markdown_option_type_string_tl
 \cs_new:Nn
   \__markdown_get_option_type:nN
   {
@@ -323,7 +354,7 @@
 \__markdown_add_lua_option:nnn
   { eagerCache }
   { boolean }
-  { false }
+  { true }
 \__markdown_add_lua_option:nnn
   { singletonCache }
   { boolean }
@@ -413,6 +444,10 @@
   { boolean }
   { false }
 \__markdown_add_lua_option:nnn
+  { ensureJekyllData }
+  { boolean }
+  { false }
+\__markdown_add_lua_option:nnn
   { expectJekyllData }
   { boolean }
   { false }
@@ -580,8 +615,8 @@
   { boolean }
   { true }
 \ExplSyntaxOff
-\def\markdownLastModified{2024-07-14}%
-\def\markdownVersion{3.6.2-0-g6c30af7e}%
+\def\markdownLastModified{2024-08-30}%
+\def\markdownVersion{3.7.0-0-g98dece19}%
 \let\markdownBegin\relax
 \let\markdownEnd\relax
 \let\markinline\relax
@@ -602,7 +637,9 @@
 \seq_new:N \g__markdown_plain_tex_options_seq
 \prop_new:N \g__markdown_plain_tex_option_types_prop
 \prop_new:N \g__markdown_default_plain_tex_options_prop
-\seq_gput_right:NV \g__markdown_option_layers_seq \c__markdown_option_layer_plain_tex_tl
+\seq_gput_right:NV
+  \g__markdown_option_layers_seq
+  \c__markdown_option_layer_plain_tex_tl
 \cs_new:Nn
   \__markdown_add_plain_tex_option:nnn
   {
@@ -1058,10 +1095,14 @@
           { redefined-snippet }
           \l_tmpa_tl
       }
-    \prop_gput:NVn
+    \keys_precompile:nnN
+      { markdown/options }
+      { #2 }
+      \l_tmpb_tl
+    \prop_gput:NVV
       \g__markdown_snippets_prop
       \l_tmpa_tl
-      { #2 }
+      \l_tmpb_tl
   }
 \cs_gset_eq:NN
   \markdownSetupSnippet
@@ -1112,7 +1153,7 @@
             \g__markdown_snippets_prop
             \l_tmpa_tl
             \l_tmpb_tl
-          \__markdown_setup:V
+          \tl_use:N
             \l_tmpb_tl
         }
         {
@@ -1127,9 +1168,6 @@
   { markdown }
   { undefined-snippet }
   { Can't~invoke~undefined~snippet~#1 }
-\cs_generate_variant:Nn
-  \__markdown_setup:n
-  { V }
 \tl_new:N
   \l__markdown_import_current_theme_tl
 \keys_define:nn
@@ -2384,6 +2422,26 @@
   { untickedBox }
   { 0 }
 \ExplSyntaxOff
+\def\markdownRendererWarning{%
+  \markdownRendererWarningPrototype}%
+\def\markdownRendererError{%
+  \markdownRendererErrorPrototype}%
+\ExplSyntaxOn
+\seq_gput_right:Nn
+  \g__markdown_renderers_seq
+  { warning }
+\prop_gput:Nnn
+  \g__markdown_renderer_arities_prop
+  { warning }
+  { 1 }
+\seq_gput_right:Nn
+  \g__markdown_renderers_seq
+  { error }
+\prop_gput:Nnn
+  \g__markdown_renderer_arities_prop
+  { error }
+  { 1 }
+\ExplSyntaxOff
 \def\markdownRendererJekyllDataBegin{%
   \markdownRendererJekyllDataBeginPrototype}%
 \ExplSyntaxOn
@@ -2472,11 +2530,57 @@
   { jekyllDataNumber }
   { 2 }
 \ExplSyntaxOff
-\def\markdownRendererJekyllDataString{%
-  \markdownRendererJekyllDataStringPrototype}%
+\def\markdownRendererJekyllDataTypographicString{%
+  \markdownRendererJekyllDataTypographicStringPrototype}%
+\def\markdownRendererJekyllDataProgrammaticString{%
+  \markdownRendererJekyllDataProgrammaticStringPrototype}%
 \ExplSyntaxOn
 \seq_gput_right:Nn
   \g__markdown_renderers_seq
+  { jekyllDataTypographicString }
+\prop_gput:Nnn
+  \g__markdown_renderer_arities_prop
+  { jekyllDataTypographicString }
+  { 2 }
+\seq_gput_right:Nn
+  \g__markdown_renderers_seq
+  { jekyllDataProgrammaticString }
+\prop_gput:Nnn
+  \g__markdown_renderer_arities_prop
+  { jekyllDataProgrammaticString }
+  { 2 }
+\cs_gset:Npn
+  \markdownRendererJekyllDataTypographicString
+  {
+    \cs_if_exist:NTF
+      \markdownRendererJekyllDataString
+      {
+        \markdownWarning
+          {
+            The~jekyllDataString~renderer~has~been~deprecated,~
+            to~be~removed~in~Markdown~4.0.0
+          }
+        \markdownRendererJekyllDataString
+      }
+      {
+        \cs_if_exist:NTF
+          \markdownRendererJekyllDataStringPrototype
+          {
+            \markdownWarning
+              {
+                The~jekyllDataString~renderer~prototype~
+                has~been~deprecated,~
+                to~be~removed~in~Markdown~4.0.0
+              }
+            \markdownRendererJekyllDataStringPrototype
+          }
+          {
+            \markdownRendererJekyllDataTypographicStringPrototype
+          }
+      }
+  }
+\seq_gput_right:Nn
+  \g__markdown_renderers_seq
   { jekyllDataString }
 \prop_gput:Nnn
   \g__markdown_renderer_arities_prop
@@ -2495,9 +2599,17 @@
   { 1 }
 \cs_new:Nn \__markdown_define_renderers:
   {
-    \seq_map_function:NN
+    \seq_map_inline:Nn
       \g__markdown_renderers_seq
-      \__markdown_define_renderer:n
+      {
+        \str_if_eq:nnF
+          { ##1 }
+          { jekyllDataString }
+          {
+            \__markdown_define_renderer:n
+              { ##1 }
+          }
+      }
   }
 \cs_new:Nn \__markdown_define_renderer:n
   {
@@ -2818,9 +2930,17 @@
   { nV }
 \cs_new:Nn \__markdown_define_renderer_prototypes:
   {
-    \seq_map_function:NN
+    \seq_map_inline:Nn
       \g__markdown_renderers_seq
-      \__markdown_define_renderer_prototype:n
+      {
+        \str_if_eq:nnF
+          { ##1 }
+          { jekyllDataString }
+          {
+            \__markdown_define_renderer_prototype:n
+              { ##1 }
+          }
+      }
   }
 \cs_new:Nn \__markdown_define_renderer_prototype:n
   {
@@ -3422,29 +3542,42 @@
 \msg_new:nnn
   { markdown }
   { buffering-markinline }
-  { Buffering~inline~markdown~input~into~the~temporary~input~file~"#1". }
+  { Buffering~inline~markdown~input~into~
+    the~temporary~input~file~"#1". }
 \msg_new:nnnn
   { markdown }
   { markinline-peek-failure }
   { Use~of~\iow_char:N \\ markinline~doesn't~match~its~definition }
-  { The~macro~should~be~followed~by~inline~markdown~text~in~curly~braces }
+  { The~macro~should~be~followed~by~inline~
+    markdown~text~in~curly~braces }
 \cs_new:Npn
   \markdownInput
   #1
   {
-    \file_get_full_name:nNTF
-      { #1 }
-      \l_tmpa_tl
+    \__markdown_if_option:nTF
+      { frozenCache }
       {
-        \exp_args:NV
-          \markdownInputRaw
-          \l_tmpa_tl
+        \markdownInputRaw
+          { #1 }
       }
       {
-        \msg_error:nnnV
-          { markdown }
-          { markdown-file-does-not-exist }
+        \tl_set:Nx
+          \l_tmpa_tl
           { #1 }
+        \file_get_full_name:VNTF
+          \l_tmpa_tl
+          \l_tmpb_tl
+          {
+            \exp_args:NV
+              \markdownInputRaw
+              \l_tmpb_tl
+          }
+          {
+            \msg_error:nnV
+              { markdown }
+              { markdown-file-does-not-exist }
+              \l_tmpa_tl
+          }
       }
   }
 \msg_new:nnn
@@ -3470,7 +3603,8 @@
       |fi
       |markdownInfo{Including markdown document number
         "|the|markdownOptionFrozenCacheCounter" from frozen cache}%
-      |csname markdownFrozenCache|the|markdownOptionFrozenCacheCounter|endcsname
+      |csname markdownFrozenCache%
+        |the|markdownOptionFrozenCacheCounter|endcsname
       |global|advance|markdownOptionFrozenCacheCounter by 1|relax
     }{%
       |markdownInfo{Including markdown document "&1"}%

Modified: trunk/Master/texmf-dist/tex/generic/markdown/markdownthemewitiko_markdown_defaults.tex
===================================================================
--- trunk/Master/texmf-dist/tex/generic/markdown/markdownthemewitiko_markdown_defaults.tex	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/tex/generic/markdown/markdownthemewitiko_markdown_defaults.tex	2024-08-30 21:14:17 UTC (rev 72146)
@@ -77,18 +77,22 @@
 \def\markdownRendererUlEndTightPrototype{}%
 \def\markdownRendererOlBeginPrototype{}%
 \def\markdownRendererOlBeginTightPrototype{}%
-\def\markdownRendererFancyOlBeginPrototype#1#2{\markdownRendererOlBegin}%
-\def\markdownRendererFancyOlBeginTightPrototype#1#2{\markdownRendererOlBeginTight}%
+\def\markdownRendererFancyOlBeginPrototype#1#2{%
+  \markdownRendererOlBegin}%
+\def\markdownRendererFancyOlBeginTightPrototype#1#2{%
+  \markdownRendererOlBeginTight}%
 \def\markdownRendererOlItemPrototype{}%
 \def\markdownRendererOlItemWithNumberPrototype#1{}%
 \def\markdownRendererOlItemEndPrototype{}%
 \def\markdownRendererFancyOlItemPrototype{\markdownRendererOlItem}%
-\def\markdownRendererFancyOlItemWithNumberPrototype{\markdownRendererOlItemWithNumber}%
+\def\markdownRendererFancyOlItemWithNumberPrototype{%
+  \markdownRendererOlItemWithNumber}%
 \def\markdownRendererFancyOlItemEndPrototype{}%
 \def\markdownRendererOlEndPrototype{}%
 \def\markdownRendererOlEndTightPrototype{}%
 \def\markdownRendererFancyOlEndPrototype{\markdownRendererOlEnd}%
-\def\markdownRendererFancyOlEndTightPrototype{\markdownRendererOlEndTight}%
+\def\markdownRendererFancyOlEndTightPrototype{%
+  \markdownRendererOlEndTight}%
 \def\markdownRendererDlBeginPrototype{}%
 \def\markdownRendererDlBeginTightPrototype{}%
 \def\markdownRendererDlItemPrototype#1{#1}%
@@ -162,9 +166,11 @@
 \ExplSyntaxOff
 \def\markdownRendererSectionBeginPrototype{}%
 \def\markdownRendererSectionEndPrototype{}%
+\let\markdownRendererWarningPrototype\markdownWarning
+\let\markdownRendererErrorPrototype\markdownError
 \ExplSyntaxOn
 \cs_new:Nn
-  \__markdown_plain_tex_default_input_raw_inline_renderer_prototype:nn
+  \__markdown_plain_tex_default_input_raw_inline:nn
   {
     \str_case:nn
       { #2 }
@@ -174,7 +180,7 @@
       }
   }
 \cs_new:Nn
-  \__markdown_plain_tex_default_input_raw_block_renderer_prototype:nn
+  \__markdown_plain_tex_default_input_raw_block:nn
   {
     \str_case:nn
       { #2 }
@@ -186,7 +192,7 @@
 \cs_gset:Npn
   \markdownRendererInputRawInlinePrototype#1#2
   {
-    \__markdown_plain_tex_default_input_raw_inline_renderer_prototype:nn
+    \__markdown_plain_tex_default_input_raw_inline:nn
       { #1 }
       { #2 }
   }
@@ -193,7 +199,7 @@
 \cs_gset:Npn
   \markdownRendererInputRawBlockPrototype#1#2
   {
-    \__markdown_plain_tex_default_input_raw_block_renderer_prototype:nn
+    \__markdown_plain_tex_default_input_raw_block:nn
       { #1 }
       { #2 }
   }
@@ -310,7 +316,8 @@
     { #1 }
     { #2 }
 }
-\def\markdownRendererJekyllDataStringPrototype#1#2{
+\def\markdownRendererJekyllDataProgrammaticStringPrototype#1#2{}
+\def\markdownRendererJekyllDataTypographicStringPrototype#1#2{
   \markdown_jekyll_data_set_keyvals:nn
     { #1 }
     { #2 }

Modified: trunk/Master/texmf-dist/tex/latex/markdown/markdown.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/markdown/markdown.sty	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/tex/latex/markdown/markdown.sty	2024-08-30 21:14:17 UTC (rev 72146)
@@ -120,11 +120,15 @@
         \c { group_end: }
         \c { tl_set_rescan:Nnn } \c { l_tmpa_tl } { } { \1 }
         \c { __markdown_setup:V } \c { l_tmpa_tl }
-        \c { markdownReadAndConvert at markdown } { }
+        \c { exp_args:NV }
+          \c { markdownReadAndConvert@ }
+          \c { @currenvir }
       }
       {
         \group_end:
-        \markdownReadAndConvert at markdown { }
+        \exp_args:NV
+          \markdownReadAndConvert@
+          \@currenvir
       }
   }
   { \markdownEnd }
@@ -138,7 +142,8 @@
       { #1 }
     \__markdown_setup:n
       { #1 }
-    \markdownReadAndConvert at markdown *
+    \markdownReadAndConvert@
+      { markdown* }
   }
   { \markdownEnd }
 \msg_new:nnn
@@ -148,13 +153,16 @@
     The~markdown*~LaTeX~environment~has~been~deprecated~and~will~
     be~removed~in~the~next~major~version~of~the~Markdown~package.
   }
+\cs_generate_variant:Nn
+  \__markdown_setup:n
+  { V }
 \ExplSyntaxOff
 \begingroup
   \catcode`\|=0\catcode`\<=1\catcode`\>=2%
   \catcode`\\=12|catcode`|{=12|catcode`|}=12%
-  |gdef|markdownReadAndConvert at markdown#1<%
-    |markdownReadAndConvert<\end{markdown#1}>%
-                           <|end<markdown#1>>>%
+  |gdef|markdownReadAndConvert@#1<%
+    |markdownReadAndConvert<\end{#1}>%
+                           <|end<#1>>>%
 |endgroup
 \DeclareOption*{%
   \expandafter\markdownSetup\expandafter{\CurrentOption}}%

Modified: trunk/Master/texmf-dist/tex/latex/markdown/markdownthemewitiko_dot.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/markdown/markdownthemewitiko_dot.sty	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/tex/latex/markdown/markdownthemewitiko_dot.sty	2024-08-30 21:14:17 UTC (rev 72146)
@@ -59,7 +59,8 @@
           fi}}%
       \markdownRendererImage{Graphviz image}{#1.pdf}{#1.pdf}{##2}%
     }{%
-      \markdown at witiko@dot at oldRendererInputFencedCodePrototype{#1}{#2}{#3}%
+      \markdown at witiko@dot at oldRendererInputFencedCodePrototype
+        {#1}{#2}{#3}%
     }%
   }%
   \next#2 \relax}%

Modified: trunk/Master/texmf-dist/tex/latex/markdown/markdownthemewitiko_markdown_defaults.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/markdown/markdownthemewitiko_markdown_defaults.sty	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/tex/latex/markdown/markdownthemewitiko_markdown_defaults.sty	2024-08-30 21:14:17 UTC (rev 72146)
@@ -501,8 +501,9 @@
   \advance\markdownLaTeXCitationsCounter by 1\relax
   \ifx\relax#4\relax
     \ifx\relax#5\relax
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
-        \cite{#1#2#6}%  Without prenotes and postnotes, just accumulate cites
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+        \relax
+        \cite{#1#2#6}%  No prenotes/postnotes, just accumulate cites
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
         \@gobblethree
@@ -512,7 +513,8 @@
         \cite{#1}%
       \fi
       \cite[#5]{#6}%
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+      \relax
       \else
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
@@ -539,7 +541,8 @@
       \space  % Insert a space before the prenote in later citations
     \fi
     #4~\expandafter\cite\ifx\relax#5\relax{#6}\else[#5]{#6}\fi
-    \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+    \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+    \relax
     \else
       \expandafter\expandafter\expandafter
       \expandafter\expandafter\expandafter\expandafter
@@ -558,8 +561,9 @@
   \advance\markdownLaTeXCitationsCounter by 1\relax
   \ifx\relax#3\relax
     \ifx\relax#4\relax
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
-        \citep{#1,#5}%  Without prenotes and postnotes, just accumulate cites
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+      \relax
+        \citep{#1,#5}%  No prenotes/postnotes, just accumulate cites
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
         \@gobbletwo
@@ -569,7 +573,8 @@
         \citep{#1}%
       \fi
       \citep[][#4]{#5}%
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+      \relax
       \else
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
@@ -589,7 +594,8 @@
       \citep{#1}%
     \fi
     \citep[#3][#4]{#5}%
-    \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+    \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+    \relax
     \else
       \expandafter\expandafter\expandafter
       \expandafter\expandafter\expandafter\expandafter
@@ -604,8 +610,9 @@
   \advance\markdownLaTeXCitationsCounter by 1\relax
   \ifx\relax#3\relax
     \ifx\relax#4\relax
-      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
-        \citet{#1,#5}%  Without prenotes and postnotes, just accumulate cites
+      \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+      \relax
+        \citet{#1,#5}%  No prenotes/postnotes, just accumulate cites
         \expandafter\expandafter\expandafter
         \expandafter\expandafter\expandafter\expandafter
         \@gobbletwo
@@ -615,10 +622,13 @@
         \citet{#1}%
       \fi
       , \citet[#3][#4]{#5}%
-      \ifnum\markdownLaTeXCitationsCounter<\markdownLaTeXCitationsTotal\relax
+      \ifnum\markdownLaTeXCitationsCounter<\markdownLaTeXCitationsTotal
+      \relax
         ,
       \else
-        \ifnum\markdownLaTeXCitationsCounter=\markdownLaTeXCitationsTotal\relax
+        \ifnum
+        \markdownLaTeXCitationsCounter=\markdownLaTeXCitationsTotal
+        \relax
           ,
         \fi
       \fi
@@ -637,10 +647,13 @@
       \citet{#1}%
     \fi
     , \citet[#3][#4]{#5}%
-    \ifnum\markdownLaTeXCitationsCounter<\markdownLaTeXCitationsTotal\relax
+    \ifnum\markdownLaTeXCitationsCounter<\markdownLaTeXCitationsTotal
+    \relax
       ,
     \else
-      \ifnum\markdownLaTeXCitationsCounter=\markdownLaTeXCitationsTotal\relax
+      \ifnum
+      \markdownLaTeXCitationsCounter=\markdownLaTeXCitationsTotal
+      \relax
         ,
       \fi
     \fi
@@ -654,13 +667,15 @@
 
 \def\markdownLaTeXBibLaTeXCitations#1#2#3#4#5{%
   \advance\markdownLaTeXCitationsCounter by 1\relax
-  \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+  \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+  \relax
     \autocites#1[#3][#4]{#5}%
     \expandafter\@gobbletwo
   \fi\markdownLaTeXBibLaTeXCitations{#1[#3][#4]{#5}}}
 \def\markdownLaTeXBibLaTeXTextCitations#1#2#3#4#5{%
   \advance\markdownLaTeXCitationsCounter by 1\relax
-  \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal\relax
+  \ifnum\markdownLaTeXCitationsCounter>\markdownLaTeXCitationsTotal
+  \relax
     \textcites#1[#3][#4]{#5}%
     \expandafter\@gobbletwo
   \fi\markdownLaTeXBibLaTeXTextCitations{#1[#3][#4]{#5}}}
@@ -737,7 +752,8 @@
     {
       \markdownLaTeXRendererAutolink { #2 } { #3 }
     }{
-      \markdownLaTeXRendererDirectOrIndirectLink { #1 } { #2 } { #3 } { #4 }
+      \markdownLaTeXRendererDirectOrIndirectLink
+        { #1 } { #2 } { #3 } { #4 }
     }
 }
 \def\markdownLaTeXRendererAutolink#1#2{%
@@ -939,13 +955,13 @@
       {
         { latex }
           {
-            \__markdown_plain_tex_default_input_raw_inline_renderer_prototype:nn
+            \__markdown_plain_tex_default_input_raw_inline:nn
               { #1 }
               { tex }
           }
       }
       {
-        \__markdown_plain_tex_default_input_raw_inline_renderer_prototype:nn
+        \__markdown_plain_tex_default_input_raw_inline:nn
           { #1 }
           { #2 }
       }
@@ -958,13 +974,13 @@
       {
         { latex }
           {
-            \__markdown_plain_tex_default_input_raw_block_renderer_prototype:nn
+            \__markdown_plain_tex_default_input_raw_block:nn
               { #1 }
               { tex }
           }
       }
       {
-        \__markdown_plain_tex_default_input_raw_block_renderer_prototype:nn
+        \__markdown_plain_tex_default_input_raw_block:nn
           { #1 }
           { #2 }
       }

Added: trunk/Master/texmf-dist/tex/luatex/markdown/markdown-parser.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/markdown/markdown-parser.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/tex/luatex/markdown/markdown-parser.lua	2024-08-30 21:14:17 UTC (rev 72146)
@@ -0,0 +1,8990 @@
+--
+-- Copyright (C) 2009-2016 John MacFarlane, Hans Hagen
+--
+-- Permission is hereby granted, free of charge, to any person obtaining
+-- a copy of this software and associated documentation files (the
+-- "Software"), to deal in the Software without restriction, including
+-- without limitation the rights to use, copy, modify, merge, publish,
+-- distribute, sublicense, and/or sell copies of the Software, and to
+-- permit persons to whom the Software is furnished to do so, subject to
+-- the following conditions:
+--
+-- The above copyright notice and this permission notice shall be
+-- included in all copies or substantial portions of the Software.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+-- MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT.
+-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+--
+-- Copyright (C) 2016-2024 Vít Starý Novotný, Andrej Genčur
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+--
+--     http://www.latex-project.org/lppl.txt
+--
+-- and version 1.3 or later is part of all distributions of LaTeX
+-- version 2005/12/01 or later.
+--
+-- This work has the LPPL maintenance status `maintained'.
+-- The Current Maintainer of this work is Vít Starý Novotný.
+--
+-- Send bug reports, requests for additions and questions
+-- either to the GitHub issue tracker at
+--
+--     https://github.com/witiko/markdown/issues
+--
+-- or to the e-mail address <witiko at mail.muni.cz>.
+--
+-- MODIFICATION ADVICE:
+--
+-- If you want to customize this file, it is best to make a copy of
+-- the source file(s) from which it was produced. Use a different
+-- name for your copy(ies) and modify the copy(ies); this will ensure
+-- that your modifications do not get overwritten when you install a
+-- new release of the standard system. You should also ensure that
+-- your modified source file does not generate any modified file with
+-- the same name as a standard file.
+--
+-- You will also need to produce your own, suitably named, .ins file to
+-- control the generation of files from your source file; this file
+-- should contain your own preambles for the files it generates, not
+-- those in the standard .ins files.
+--
+local metadata = {
+    version   = "3.7.0-0-g98dece19",
+    comment   = "A module for the conversion from markdown "
+             .. "to plain TeX",
+    author    = "John MacFarlane, Hans Hagen, Vít Starý Novotný, "
+             .. "Andrej Genčur",
+    copyright = {"2009-2016 John MacFarlane, Hans Hagen",
+                 "2016-2024 Vít Starý Novotný, Andrej Genčur"},
+    license   = "LPPL 1.3c"
+}
+
+if not modules then modules = { } end
+modules['markdown'] = metadata
+local lpeg = require("lpeg")
+local unicode = require("unicode")
+local md5 = require("md5");
+(function()
+  local should_initialize = package.loaded.kpse == nil
+                       or tex.initialize ~= nil
+  kpse = require("kpse")
+  if should_initialize then
+    kpse.set_program_name("luatex")
+  end
+end)()
+local uni_algos = require("lua-uni-algos")
+local M = {metadata = metadata}
+local walkable_syntax = {
+  Block = {
+    "Blockquote",
+    "Verbatim",
+    "ThematicBreak",
+    "BulletList",
+    "OrderedList",
+    "DisplayHtml",
+    "Heading",
+  },
+  BlockOrParagraph = {
+    "Block",
+    "Paragraph",
+    "Plain",
+  },
+  Inline = {
+    "Str",
+    "Space",
+    "Endline",
+    "EndlineBreak",
+    "LinkAndEmph",
+    "Code",
+    "AutoLinkUrl",
+    "AutoLinkEmail",
+    "AutoLinkRelativeReference",
+    "InlineHtml",
+    "HtmlEntity",
+    "EscapedChar",
+    "Smart",
+    "Symbol",
+  },
+}
+local defaultOptions = {}
+defaultOptions.eagerCache = true
+defaultOptions.singletonCache = true
+local singletonCache = {
+  convert = nil,
+  options = nil,
+}
+defaultOptions.unicodeNormalization = true
+defaultOptions.unicodeNormalizationForm = "nfc"
+defaultOptions.cacheDir = "."
+defaultOptions.contentBlocksLanguageMap = "markdown-languages.json"
+defaultOptions.debugExtensionsFileName = "debug-extensions.json"
+defaultOptions.frozenCacheFileName = "frozenCache.tex"
+defaultOptions.autoIdentifiers = false
+defaultOptions.blankBeforeBlockquote = false
+defaultOptions.blankBeforeCodeFence = false
+defaultOptions.blankBeforeDivFence = false
+defaultOptions.blankBeforeHeading = false
+defaultOptions.blankBeforeList = false
+defaultOptions.bracketedSpans = false
+defaultOptions.breakableBlockquotes = true
+defaultOptions.citationNbsps = true
+defaultOptions.citations = false
+defaultOptions.codeSpans = true
+defaultOptions.contentBlocks = false
+defaultOptions.contentLevel = "block"
+defaultOptions.debugExtensions = false
+defaultOptions.definitionLists = false
+defaultOptions.ensureJekyllData = false
+defaultOptions.expectJekyllData = false
+metadata.user_extension_api_version = 2
+metadata.grammar_version = 4
+defaultOptions.extensions = {}
+defaultOptions.fancyLists = false
+defaultOptions.fencedCode = true
+defaultOptions.fencedCodeAttributes = false
+defaultOptions.fencedDivs = false
+defaultOptions.finalizeCache = false
+defaultOptions.frozenCacheCounter = 0
+defaultOptions.gfmAutoIdentifiers = false
+defaultOptions.hashEnumerators = false
+defaultOptions.headerAttributes = false
+defaultOptions.html = true
+defaultOptions.hybrid = false
+defaultOptions.inlineCodeAttributes = false
+defaultOptions.inlineNotes = false
+defaultOptions.jekyllData = false
+defaultOptions.linkAttributes = false
+defaultOptions.lineBlocks = false
+defaultOptions.mark = false
+defaultOptions.notes = false
+defaultOptions.pipeTables = false
+defaultOptions.preserveTabs = true
+defaultOptions.rawAttribute = false
+defaultOptions.relativeReferences = false
+defaultOptions.shiftHeadings = 0
+defaultOptions.slice = "^ $"
+defaultOptions.smartEllipses = false
+defaultOptions.startNumber = true
+defaultOptions.strikeThrough = false
+defaultOptions.stripIndent = false
+defaultOptions.subscripts = false
+defaultOptions.superscripts = false
+defaultOptions.tableAttributes = false
+defaultOptions.tableCaptions = false
+defaultOptions.taskLists = false
+defaultOptions.texComments = false
+defaultOptions.texMathDollars = false
+defaultOptions.texMathDoubleBackslash = false
+defaultOptions.texMathSingleBackslash = false
+defaultOptions.tightLists = true
+defaultOptions.underscores = true
+local upper, format, length =
+  string.upper, string.format, string.len
+local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, Cp, any =
+  lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cg, lpeg.Cb,
+  lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs, lpeg.Cp, lpeg.P(1)
+local util = {}
+function util.err(msg, exit_code)
+  io.stderr:write("markdown.lua: " .. msg .. "\n")
+  os.exit(exit_code or 1)
+end
+function util.cache(dir, string, salt, transform, suffix)
+  local digest = md5.sumhexa(string .. (salt or ""))
+  local name = util.pathname(dir, digest .. suffix)
+  local file = io.open(name, "r")
+  if file == nil then -- If no cache entry exists, create a new one.
+    file = assert(io.open(name, "w"),
+      [[Could not open file "]] .. name .. [[" for writing]])
+    local result = string
+    if transform ~= nil then
+      result = transform(result)
+    end
+    assert(file:write(result))
+    assert(file:close())
+  end
+  return name
+end
+function util.cache_verbatim(dir, string)
+  local name = util.cache(dir, string, nil, nil, ".verbatim")
+  return name
+end
+function util.table_copy(t)
+  local u = { }
+  for k, v in pairs(t) do u[k] = v end
+  return setmetatable(u, getmetatable(t))
+end
+function util.encode_json_string(s)
+  s = s:gsub([[\]], [[\\]])
+  s = s:gsub([["]], [[\"]])
+  return [["]] .. s .. [["]]
+end
+function util.expand_tabs_in_line(s, tabstop)
+  local tab = tabstop or 4
+  local corr = 0
+  return (s:gsub("()\t", function(p)
+            local sp = tab - (p - 1 + corr) % tab
+            corr = corr - 1 + sp
+            return string.rep(" ", sp)
+          end))
+end
+function util.walk(t, f)
+  local typ = type(t)
+  if typ == "string" then
+    f(t)
+  elseif typ == "table" then
+    local i = 1
+    local n
+    n = t[i]
+    while n do
+      util.walk(n, f)
+      i = i + 1
+      n = t[i]
+    end
+  elseif typ == "function" then
+    local ok, val = pcall(t)
+    if ok then
+      util.walk(val,f)
+    end
+  else
+    f(tostring(t))
+  end
+end
+function util.flatten(ary)
+  local new = {}
+  for _,v in ipairs(ary) do
+    if type(v) == "table" then
+      for _,w in ipairs(util.flatten(v)) do
+        new[#new + 1] = w
+      end
+    else
+      new[#new + 1] = v
+    end
+  end
+  return new
+end
+function util.rope_to_string(rope)
+  local buffer = {}
+  util.walk(rope, function(x) buffer[#buffer + 1] = x end)
+  return table.concat(buffer)
+end
+function util.rope_last(rope)
+  if #rope == 0 then
+    return nil
+  else
+    local l = rope[#rope]
+    if type(l) == "table" then
+      return util.rope_last(l)
+    else
+      return l
+    end
+  end
+end
+function util.intersperse(ary, x)
+  local new = {}
+  local l = #ary
+  for i,v in ipairs(ary) do
+    local n = #new
+    new[n + 1] = v
+    if i ~= l then
+      new[n + 2] = x
+    end
+  end
+  return new
+end
+function util.map(ary, f)
+  local new = {}
+  for i,v in ipairs(ary) do
+    new[i] = f(v)
+  end
+  return new
+end
+function util.escaper(char_escapes, string_escapes)
+  local char_escapes_list = ""
+  for i,_ in pairs(char_escapes) do
+    char_escapes_list = char_escapes_list .. i
+  end
+  local escapable = S(char_escapes_list) / char_escapes
+  if string_escapes then
+    for k,v in pairs(string_escapes) do
+      escapable = P(k) / v + escapable
+    end
+  end
+  local escape_string = Cs((escapable + any)^0)
+  return function(s)
+    return lpeg.match(escape_string, s)
+  end
+end
+function util.pathname(dir, file)
+  if #dir == 0 then
+    return file
+  else
+    return dir .. "/" .. file
+  end
+end
+function util.salt(options)
+  local opt_string = {}
+  for k, _ in pairs(defaultOptions) do
+    local v = options[k]
+    if type(v) == "table" then
+      for _, i in ipairs(v) do
+        opt_string[#opt_string+1] = k .. "=" .. tostring(i)
+      end
+    elseif k ~= "cacheDir" then
+      opt_string[#opt_string+1] = k .. "=" .. tostring(v)
+    end
+  end
+  table.sort(opt_string)
+  local salt = table.concat(opt_string, ",")
+            .. "," .. metadata.version
+  return salt
+end
+local entities = {}
+
+local character_entities = {
+  ["Tab"] = 9,
+  ["NewLine"] = 10,
+  ["excl"] = 33,
+  ["QUOT"] = 34,
+  ["quot"] = 34,
+  ["num"] = 35,
+  ["dollar"] = 36,
+  ["percnt"] = 37,
+  ["AMP"] = 38,
+  ["amp"] = 38,
+  ["apos"] = 39,
+  ["lpar"] = 40,
+  ["rpar"] = 41,
+  ["ast"] = 42,
+  ["midast"] = 42,
+  ["plus"] = 43,
+  ["comma"] = 44,
+  ["period"] = 46,
+  ["sol"] = 47,
+  ["colon"] = 58,
+  ["semi"] = 59,
+  ["LT"] = 60,
+  ["lt"] = 60,
+  ["nvlt"] = {60, 8402},
+  ["bne"] = {61, 8421},
+  ["equals"] = 61,
+  ["GT"] = 62,
+  ["gt"] = 62,
+  ["nvgt"] = {62, 8402},
+  ["quest"] = 63,
+  ["commat"] = 64,
+  ["lbrack"] = 91,
+  ["lsqb"] = 91,
+  ["bsol"] = 92,
+  ["rbrack"] = 93,
+  ["rsqb"] = 93,
+  ["Hat"] = 94,
+  ["UnderBar"] = 95,
+  ["lowbar"] = 95,
+  ["DiacriticalGrave"] = 96,
+  ["grave"] = 96,
+  ["fjlig"] = {102, 106},
+  ["lbrace"] = 123,
+  ["lcub"] = 123,
+  ["VerticalLine"] = 124,
+  ["verbar"] = 124,
+  ["vert"] = 124,
+  ["rbrace"] = 125,
+  ["rcub"] = 125,
+  ["NonBreakingSpace"] = 160,
+  ["nbsp"] = 160,
+  ["iexcl"] = 161,
+  ["cent"] = 162,
+  ["pound"] = 163,
+  ["curren"] = 164,
+  ["yen"] = 165,
+  ["brvbar"] = 166,
+  ["sect"] = 167,
+  ["Dot"] = 168,
+  ["DoubleDot"] = 168,
+  ["die"] = 168,
+  ["uml"] = 168,
+  ["COPY"] = 169,
+  ["copy"] = 169,
+  ["ordf"] = 170,
+  ["laquo"] = 171,
+  ["not"] = 172,
+  ["shy"] = 173,
+  ["REG"] = 174,
+  ["circledR"] = 174,
+  ["reg"] = 174,
+  ["macr"] = 175,
+  ["strns"] = 175,
+  ["deg"] = 176,
+  ["PlusMinus"] = 177,
+  ["plusmn"] = 177,
+  ["pm"] = 177,
+  ["sup2"] = 178,
+  ["sup3"] = 179,
+  ["DiacriticalAcute"] = 180,
+  ["acute"] = 180,
+  ["micro"] = 181,
+  ["para"] = 182,
+  ["CenterDot"] = 183,
+  ["centerdot"] = 183,
+  ["middot"] = 183,
+  ["Cedilla"] = 184,
+  ["cedil"] = 184,
+  ["sup1"] = 185,
+  ["ordm"] = 186,
+  ["raquo"] = 187,
+  ["frac14"] = 188,
+  ["frac12"] = 189,
+  ["half"] = 189,
+  ["frac34"] = 190,
+  ["iquest"] = 191,
+  ["Agrave"] = 192,
+  ["Aacute"] = 193,
+  ["Acirc"] = 194,
+  ["Atilde"] = 195,
+  ["Auml"] = 196,
+  ["Aring"] = 197,
+  ["angst"] = 197,
+  ["AElig"] = 198,
+  ["Ccedil"] = 199,
+  ["Egrave"] = 200,
+  ["Eacute"] = 201,
+  ["Ecirc"] = 202,
+  ["Euml"] = 203,
+  ["Igrave"] = 204,
+  ["Iacute"] = 205,
+  ["Icirc"] = 206,
+  ["Iuml"] = 207,
+  ["ETH"] = 208,
+  ["Ntilde"] = 209,
+  ["Ograve"] = 210,
+  ["Oacute"] = 211,
+  ["Ocirc"] = 212,
+  ["Otilde"] = 213,
+  ["Ouml"] = 214,
+  ["times"] = 215,
+  ["Oslash"] = 216,
+  ["Ugrave"] = 217,
+  ["Uacute"] = 218,
+  ["Ucirc"] = 219,
+  ["Uuml"] = 220,
+  ["Yacute"] = 221,
+  ["THORN"] = 222,
+  ["szlig"] = 223,
+  ["agrave"] = 224,
+  ["aacute"] = 225,
+  ["acirc"] = 226,
+  ["atilde"] = 227,
+  ["auml"] = 228,
+  ["aring"] = 229,
+  ["aelig"] = 230,
+  ["ccedil"] = 231,
+  ["egrave"] = 232,
+  ["eacute"] = 233,
+  ["ecirc"] = 234,
+  ["euml"] = 235,
+  ["igrave"] = 236,
+  ["iacute"] = 237,
+  ["icirc"] = 238,
+  ["iuml"] = 239,
+  ["eth"] = 240,
+  ["ntilde"] = 241,
+  ["ograve"] = 242,
+  ["oacute"] = 243,
+  ["ocirc"] = 244,
+  ["otilde"] = 245,
+  ["ouml"] = 246,
+  ["div"] = 247,
+  ["divide"] = 247,
+  ["oslash"] = 248,
+  ["ugrave"] = 249,
+  ["uacute"] = 250,
+  ["ucirc"] = 251,
+  ["uuml"] = 252,
+  ["yacute"] = 253,
+  ["thorn"] = 254,
+  ["yuml"] = 255,
+  ["Amacr"] = 256,
+  ["amacr"] = 257,
+  ["Abreve"] = 258,
+  ["abreve"] = 259,
+  ["Aogon"] = 260,
+  ["aogon"] = 261,
+  ["Cacute"] = 262,
+  ["cacute"] = 263,
+  ["Ccirc"] = 264,
+  ["ccirc"] = 265,
+  ["Cdot"] = 266,
+  ["cdot"] = 267,
+  ["Ccaron"] = 268,
+  ["ccaron"] = 269,
+  ["Dcaron"] = 270,
+  ["dcaron"] = 271,
+  ["Dstrok"] = 272,
+  ["dstrok"] = 273,
+  ["Emacr"] = 274,
+  ["emacr"] = 275,
+  ["Edot"] = 278,
+  ["edot"] = 279,
+  ["Eogon"] = 280,
+  ["eogon"] = 281,
+  ["Ecaron"] = 282,
+  ["ecaron"] = 283,
+  ["Gcirc"] = 284,
+  ["gcirc"] = 285,
+  ["Gbreve"] = 286,
+  ["gbreve"] = 287,
+  ["Gdot"] = 288,
+  ["gdot"] = 289,
+  ["Gcedil"] = 290,
+  ["Hcirc"] = 292,
+  ["hcirc"] = 293,
+  ["Hstrok"] = 294,
+  ["hstrok"] = 295,
+  ["Itilde"] = 296,
+  ["itilde"] = 297,
+  ["Imacr"] = 298,
+  ["imacr"] = 299,
+  ["Iogon"] = 302,
+  ["iogon"] = 303,
+  ["Idot"] = 304,
+  ["imath"] = 305,
+  ["inodot"] = 305,
+  ["IJlig"] = 306,
+  ["ijlig"] = 307,
+  ["Jcirc"] = 308,
+  ["jcirc"] = 309,
+  ["Kcedil"] = 310,
+  ["kcedil"] = 311,
+  ["kgreen"] = 312,
+  ["Lacute"] = 313,
+  ["lacute"] = 314,
+  ["Lcedil"] = 315,
+  ["lcedil"] = 316,
+  ["Lcaron"] = 317,
+  ["lcaron"] = 318,
+  ["Lmidot"] = 319,
+  ["lmidot"] = 320,
+  ["Lstrok"] = 321,
+  ["lstrok"] = 322,
+  ["Nacute"] = 323,
+  ["nacute"] = 324,
+  ["Ncedil"] = 325,
+  ["ncedil"] = 326,
+  ["Ncaron"] = 327,
+  ["ncaron"] = 328,
+  ["napos"] = 329,
+  ["ENG"] = 330,
+  ["eng"] = 331,
+  ["Omacr"] = 332,
+  ["omacr"] = 333,
+  ["Odblac"] = 336,
+  ["odblac"] = 337,
+  ["OElig"] = 338,
+  ["oelig"] = 339,
+  ["Racute"] = 340,
+  ["racute"] = 341,
+  ["Rcedil"] = 342,
+  ["rcedil"] = 343,
+  ["Rcaron"] = 344,
+  ["rcaron"] = 345,
+  ["Sacute"] = 346,
+  ["sacute"] = 347,
+  ["Scirc"] = 348,
+  ["scirc"] = 349,
+  ["Scedil"] = 350,
+  ["scedil"] = 351,
+  ["Scaron"] = 352,
+  ["scaron"] = 353,
+  ["Tcedil"] = 354,
+  ["tcedil"] = 355,
+  ["Tcaron"] = 356,
+  ["tcaron"] = 357,
+  ["Tstrok"] = 358,
+  ["tstrok"] = 359,
+  ["Utilde"] = 360,
+  ["utilde"] = 361,
+  ["Umacr"] = 362,
+  ["umacr"] = 363,
+  ["Ubreve"] = 364,
+  ["ubreve"] = 365,
+  ["Uring"] = 366,
+  ["uring"] = 367,
+  ["Udblac"] = 368,
+  ["udblac"] = 369,
+  ["Uogon"] = 370,
+  ["uogon"] = 371,
+  ["Wcirc"] = 372,
+  ["wcirc"] = 373,
+  ["Ycirc"] = 374,
+  ["ycirc"] = 375,
+  ["Yuml"] = 376,
+  ["Zacute"] = 377,
+  ["zacute"] = 378,
+  ["Zdot"] = 379,
+  ["zdot"] = 380,
+  ["Zcaron"] = 381,
+  ["zcaron"] = 382,
+  ["fnof"] = 402,
+  ["imped"] = 437,
+  ["gacute"] = 501,
+  ["jmath"] = 567,
+  ["circ"] = 710,
+  ["Hacek"] = 711,
+  ["caron"] = 711,
+  ["Breve"] = 728,
+  ["breve"] = 728,
+  ["DiacriticalDot"] = 729,
+  ["dot"] = 729,
+  ["ring"] = 730,
+  ["ogon"] = 731,
+  ["DiacriticalTilde"] = 732,
+  ["tilde"] = 732,
+  ["DiacriticalDoubleAcute"] = 733,
+  ["dblac"] = 733,
+  ["DownBreve"] = 785,
+  ["Alpha"] = 913,
+  ["Beta"] = 914,
+  ["Gamma"] = 915,
+  ["Delta"] = 916,
+  ["Epsilon"] = 917,
+  ["Zeta"] = 918,
+  ["Eta"] = 919,
+  ["Theta"] = 920,
+  ["Iota"] = 921,
+  ["Kappa"] = 922,
+  ["Lambda"] = 923,
+  ["Mu"] = 924,
+  ["Nu"] = 925,
+  ["Xi"] = 926,
+  ["Omicron"] = 927,
+  ["Pi"] = 928,
+  ["Rho"] = 929,
+  ["Sigma"] = 931,
+  ["Tau"] = 932,
+  ["Upsilon"] = 933,
+  ["Phi"] = 934,
+  ["Chi"] = 935,
+  ["Psi"] = 936,
+  ["Omega"] = 937,
+  ["ohm"] = 937,
+  ["alpha"] = 945,
+  ["beta"] = 946,
+  ["gamma"] = 947,
+  ["delta"] = 948,
+  ["epsi"] = 949,
+  ["epsilon"] = 949,
+  ["zeta"] = 950,
+  ["eta"] = 951,
+  ["theta"] = 952,
+  ["iota"] = 953,
+  ["kappa"] = 954,
+  ["lambda"] = 955,
+  ["mu"] = 956,
+  ["nu"] = 957,
+  ["xi"] = 958,
+  ["omicron"] = 959,
+  ["pi"] = 960,
+  ["rho"] = 961,
+  ["sigmaf"] = 962,
+  ["sigmav"] = 962,
+  ["varsigma"] = 962,
+  ["sigma"] = 963,
+  ["tau"] = 964,
+  ["upsi"] = 965,
+  ["upsilon"] = 965,
+  ["phi"] = 966,
+  ["chi"] = 967,
+  ["psi"] = 968,
+  ["omega"] = 969,
+  ["thetasym"] = 977,
+  ["thetav"] = 977,
+  ["vartheta"] = 977,
+  ["Upsi"] = 978,
+  ["upsih"] = 978,
+  ["phiv"] = 981,
+  ["straightphi"] = 981,
+  ["varphi"] = 981,
+  ["piv"] = 982,
+  ["varpi"] = 982,
+  ["Gammad"] = 988,
+  ["digamma"] = 989,
+  ["gammad"] = 989,
+  ["kappav"] = 1008,
+  ["varkappa"] = 1008,
+  ["rhov"] = 1009,
+  ["varrho"] = 1009,
+  ["epsiv"] = 1013,
+  ["straightepsilon"] = 1013,
+  ["varepsilon"] = 1013,
+  ["backepsilon"] = 1014,
+  ["bepsi"] = 1014,
+  ["IOcy"] = 1025,
+  ["DJcy"] = 1026,
+  ["GJcy"] = 1027,
+  ["Jukcy"] = 1028,
+  ["DScy"] = 1029,
+  ["Iukcy"] = 1030,
+  ["YIcy"] = 1031,
+  ["Jsercy"] = 1032,
+  ["LJcy"] = 1033,
+  ["NJcy"] = 1034,
+  ["TSHcy"] = 1035,
+  ["KJcy"] = 1036,
+  ["Ubrcy"] = 1038,
+  ["DZcy"] = 1039,
+  ["Acy"] = 1040,
+  ["Bcy"] = 1041,
+  ["Vcy"] = 1042,
+  ["Gcy"] = 1043,
+  ["Dcy"] = 1044,
+  ["IEcy"] = 1045,
+  ["ZHcy"] = 1046,
+  ["Zcy"] = 1047,
+  ["Icy"] = 1048,
+  ["Jcy"] = 1049,
+  ["Kcy"] = 1050,
+  ["Lcy"] = 1051,
+  ["Mcy"] = 1052,
+  ["Ncy"] = 1053,
+  ["Ocy"] = 1054,
+  ["Pcy"] = 1055,
+  ["Rcy"] = 1056,
+  ["Scy"] = 1057,
+  ["Tcy"] = 1058,
+  ["Ucy"] = 1059,
+  ["Fcy"] = 1060,
+  ["KHcy"] = 1061,
+  ["TScy"] = 1062,
+  ["CHcy"] = 1063,
+  ["SHcy"] = 1064,
+  ["SHCHcy"] = 1065,
+  ["HARDcy"] = 1066,
+  ["Ycy"] = 1067,
+  ["SOFTcy"] = 1068,
+  ["Ecy"] = 1069,
+  ["YUcy"] = 1070,
+  ["YAcy"] = 1071,
+  ["acy"] = 1072,
+  ["bcy"] = 1073,
+  ["vcy"] = 1074,
+  ["gcy"] = 1075,
+  ["dcy"] = 1076,
+  ["iecy"] = 1077,
+  ["zhcy"] = 1078,
+  ["zcy"] = 1079,
+  ["icy"] = 1080,
+  ["jcy"] = 1081,
+  ["kcy"] = 1082,
+  ["lcy"] = 1083,
+  ["mcy"] = 1084,
+  ["ncy"] = 1085,
+  ["ocy"] = 1086,
+  ["pcy"] = 1087,
+  ["rcy"] = 1088,
+  ["scy"] = 1089,
+  ["tcy"] = 1090,
+  ["ucy"] = 1091,
+  ["fcy"] = 1092,
+  ["khcy"] = 1093,
+  ["tscy"] = 1094,
+  ["chcy"] = 1095,
+  ["shcy"] = 1096,
+  ["shchcy"] = 1097,
+  ["hardcy"] = 1098,
+  ["ycy"] = 1099,
+  ["softcy"] = 1100,
+  ["ecy"] = 1101,
+  ["yucy"] = 1102,
+  ["yacy"] = 1103,
+  ["iocy"] = 1105,
+  ["djcy"] = 1106,
+  ["gjcy"] = 1107,
+  ["jukcy"] = 1108,
+  ["dscy"] = 1109,
+  ["iukcy"] = 1110,
+  ["yicy"] = 1111,
+  ["jsercy"] = 1112,
+  ["ljcy"] = 1113,
+  ["njcy"] = 1114,
+  ["tshcy"] = 1115,
+  ["kjcy"] = 1116,
+  ["ubrcy"] = 1118,
+  ["dzcy"] = 1119,
+  ["ensp"] = 8194,
+  ["emsp"] = 8195,
+  ["emsp13"] = 8196,
+  ["emsp14"] = 8197,
+  ["numsp"] = 8199,
+  ["puncsp"] = 8200,
+  ["ThinSpace"] = 8201,
+  ["thinsp"] = 8201,
+  ["VeryThinSpace"] = 8202,
+  ["hairsp"] = 8202,
+  ["NegativeMediumSpace"] = 8203,
+  ["NegativeThickSpace"] = 8203,
+  ["NegativeThinSpace"] = 8203,
+  ["NegativeVeryThinSpace"] = 8203,
+  ["ZeroWidthSpace"] = 8203,
+  ["zwnj"] = 8204,
+  ["zwj"] = 8205,
+  ["lrm"] = 8206,
+  ["rlm"] = 8207,
+  ["dash"] = 8208,
+  ["hyphen"] = 8208,
+  ["ndash"] = 8211,
+  ["mdash"] = 8212,
+  ["horbar"] = 8213,
+  ["Verbar"] = 8214,
+  ["Vert"] = 8214,
+  ["OpenCurlyQuote"] = 8216,
+  ["lsquo"] = 8216,
+  ["CloseCurlyQuote"] = 8217,
+  ["rsquo"] = 8217,
+  ["rsquor"] = 8217,
+  ["lsquor"] = 8218,
+  ["sbquo"] = 8218,
+  ["OpenCurlyDoubleQuote"] = 8220,
+  ["ldquo"] = 8220,
+  ["CloseCurlyDoubleQuote"] = 8221,
+  ["rdquo"] = 8221,
+  ["rdquor"] = 8221,
+  ["bdquo"] = 8222,
+  ["ldquor"] = 8222,
+  ["dagger"] = 8224,
+  ["Dagger"] = 8225,
+  ["ddagger"] = 8225,
+  ["bull"] = 8226,
+  ["bullet"] = 8226,
+  ["nldr"] = 8229,
+  ["hellip"] = 8230,
+  ["mldr"] = 8230,
+  ["permil"] = 8240,
+  ["pertenk"] = 8241,
+  ["prime"] = 8242,
+  ["Prime"] = 8243,
+  ["tprime"] = 8244,
+  ["backprime"] = 8245,
+  ["bprime"] = 8245,
+  ["lsaquo"] = 8249,
+  ["rsaquo"] = 8250,
+  ["OverBar"] = 8254,
+  ["oline"] = 8254,
+  ["caret"] = 8257,
+  ["hybull"] = 8259,
+  ["frasl"] = 8260,
+  ["bsemi"] = 8271,
+  ["qprime"] = 8279,
+  ["MediumSpace"] = 8287,
+  ["ThickSpace"] = {8287, 8202},
+  ["NoBreak"] = 8288,
+  ["ApplyFunction"] = 8289,
+  ["af"] = 8289,
+  ["InvisibleTimes"] = 8290,
+  ["it"] = 8290,
+  ["InvisibleComma"] = 8291,
+  ["ic"] = 8291,
+  ["euro"] = 8364,
+  ["TripleDot"] = 8411,
+  ["tdot"] = 8411,
+  ["DotDot"] = 8412,
+  ["Copf"] = 8450,
+  ["complexes"] = 8450,
+  ["incare"] = 8453,
+  ["gscr"] = 8458,
+  ["HilbertSpace"] = 8459,
+  ["Hscr"] = 8459,
+  ["hamilt"] = 8459,
+  ["Hfr"] = 8460,
+  ["Poincareplane"] = 8460,
+  ["Hopf"] = 8461,
+  ["quaternions"] = 8461,
+  ["planckh"] = 8462,
+  ["hbar"] = 8463,
+  ["hslash"] = 8463,
+  ["planck"] = 8463,
+  ["plankv"] = 8463,
+  ["Iscr"] = 8464,
+  ["imagline"] = 8464,
+  ["Ifr"] = 8465,
+  ["Im"] = 8465,
+  ["image"] = 8465,
+  ["imagpart"] = 8465,
+  ["Laplacetrf"] = 8466,
+  ["Lscr"] = 8466,
+  ["lagran"] = 8466,
+  ["ell"] = 8467,
+  ["Nopf"] = 8469,
+  ["naturals"] = 8469,
+  ["numero"] = 8470,
+  ["copysr"] = 8471,
+  ["weierp"] = 8472,
+  ["wp"] = 8472,
+  ["Popf"] = 8473,
+  ["primes"] = 8473,
+  ["Qopf"] = 8474,
+  ["rationals"] = 8474,
+  ["Rscr"] = 8475,
+  ["realine"] = 8475,
+  ["Re"] = 8476,
+  ["Rfr"] = 8476,
+  ["real"] = 8476,
+  ["realpart"] = 8476,
+  ["Ropf"] = 8477,
+  ["reals"] = 8477,
+  ["rx"] = 8478,
+  ["TRADE"] = 8482,
+  ["trade"] = 8482,
+  ["Zopf"] = 8484,
+  ["integers"] = 8484,
+  ["mho"] = 8487,
+  ["Zfr"] = 8488,
+  ["zeetrf"] = 8488,
+  ["iiota"] = 8489,
+  ["Bernoullis"] = 8492,
+  ["Bscr"] = 8492,
+  ["bernou"] = 8492,
+  ["Cayleys"] = 8493,
+  ["Cfr"] = 8493,
+  ["escr"] = 8495,
+  ["Escr"] = 8496,
+  ["expectation"] = 8496,
+  ["Fouriertrf"] = 8497,
+  ["Fscr"] = 8497,
+  ["Mellintrf"] = 8499,
+  ["Mscr"] = 8499,
+  ["phmmat"] = 8499,
+  ["order"] = 8500,
+  ["orderof"] = 8500,
+  ["oscr"] = 8500,
+  ["alefsym"] = 8501,
+  ["aleph"] = 8501,
+  ["beth"] = 8502,
+  ["gimel"] = 8503,
+  ["daleth"] = 8504,
+  ["CapitalDifferentialD"] = 8517,
+  ["DD"] = 8517,
+  ["DifferentialD"] = 8518,
+  ["dd"] = 8518,
+  ["ExponentialE"] = 8519,
+  ["ee"] = 8519,
+  ["exponentiale"] = 8519,
+  ["ImaginaryI"] = 8520,
+  ["ii"] = 8520,
+  ["frac13"] = 8531,
+  ["frac23"] = 8532,
+  ["frac15"] = 8533,
+  ["frac25"] = 8534,
+  ["frac35"] = 8535,
+  ["frac45"] = 8536,
+  ["frac16"] = 8537,
+  ["frac56"] = 8538,
+  ["frac18"] = 8539,
+  ["frac38"] = 8540,
+  ["frac58"] = 8541,
+  ["frac78"] = 8542,
+  ["LeftArrow"] = 8592,
+  ["ShortLeftArrow"] = 8592,
+  ["larr"] = 8592,
+  ["leftarrow"] = 8592,
+  ["slarr"] = 8592,
+  ["ShortUpArrow"] = 8593,
+  ["UpArrow"] = 8593,
+  ["uarr"] = 8593,
+  ["uparrow"] = 8593,
+  ["RightArrow"] = 8594,
+  ["ShortRightArrow"] = 8594,
+  ["rarr"] = 8594,
+  ["rightarrow"] = 8594,
+  ["srarr"] = 8594,
+  ["DownArrow"] = 8595,
+  ["ShortDownArrow"] = 8595,
+  ["darr"] = 8595,
+  ["downarrow"] = 8595,
+  ["LeftRightArrow"] = 8596,
+  ["harr"] = 8596,
+  ["leftrightarrow"] = 8596,
+  ["UpDownArrow"] = 8597,
+  ["updownarrow"] = 8597,
+  ["varr"] = 8597,
+  ["UpperLeftArrow"] = 8598,
+  ["nwarr"] = 8598,
+  ["nwarrow"] = 8598,
+  ["UpperRightArrow"] = 8599,
+  ["nearr"] = 8599,
+  ["nearrow"] = 8599,
+  ["LowerRightArrow"] = 8600,
+  ["searr"] = 8600,
+  ["searrow"] = 8600,
+  ["LowerLeftArrow"] = 8601,
+  ["swarr"] = 8601,
+  ["swarrow"] = 8601,
+  ["nlarr"] = 8602,
+  ["nleftarrow"] = 8602,
+  ["nrarr"] = 8603,
+  ["nrightarrow"] = 8603,
+  ["nrarrw"] = {8605, 824},
+  ["rarrw"] = 8605,
+  ["rightsquigarrow"] = 8605,
+  ["Larr"] = 8606,
+  ["twoheadleftarrow"] = 8606,
+  ["Uarr"] = 8607,
+  ["Rarr"] = 8608,
+  ["twoheadrightarrow"] = 8608,
+  ["Darr"] = 8609,
+  ["larrtl"] = 8610,
+  ["leftarrowtail"] = 8610,
+  ["rarrtl"] = 8611,
+  ["rightarrowtail"] = 8611,
+  ["LeftTeeArrow"] = 8612,
+  ["mapstoleft"] = 8612,
+  ["UpTeeArrow"] = 8613,
+  ["mapstoup"] = 8613,
+  ["RightTeeArrow"] = 8614,
+  ["map"] = 8614,
+  ["mapsto"] = 8614,
+  ["DownTeeArrow"] = 8615,
+  ["mapstodown"] = 8615,
+  ["hookleftarrow"] = 8617,
+  ["larrhk"] = 8617,
+  ["hookrightarrow"] = 8618,
+  ["rarrhk"] = 8618,
+  ["larrlp"] = 8619,
+  ["looparrowleft"] = 8619,
+  ["looparrowright"] = 8620,
+  ["rarrlp"] = 8620,
+  ["harrw"] = 8621,
+  ["leftrightsquigarrow"] = 8621,
+  ["nharr"] = 8622,
+  ["nleftrightarrow"] = 8622,
+  ["Lsh"] = 8624,
+  ["lsh"] = 8624,
+  ["Rsh"] = 8625,
+  ["rsh"] = 8625,
+  ["ldsh"] = 8626,
+  ["rdsh"] = 8627,
+  ["crarr"] = 8629,
+  ["cularr"] = 8630,
+  ["curvearrowleft"] = 8630,
+  ["curarr"] = 8631,
+  ["curvearrowright"] = 8631,
+  ["circlearrowleft"] = 8634,
+  ["olarr"] = 8634,
+  ["circlearrowright"] = 8635,
+  ["orarr"] = 8635,
+  ["LeftVector"] = 8636,
+  ["leftharpoonup"] = 8636,
+  ["lharu"] = 8636,
+  ["DownLeftVector"] = 8637,
+  ["leftharpoondown"] = 8637,
+  ["lhard"] = 8637,
+  ["RightUpVector"] = 8638,
+  ["uharr"] = 8638,
+  ["upharpoonright"] = 8638,
+  ["LeftUpVector"] = 8639,
+  ["uharl"] = 8639,
+  ["upharpoonleft"] = 8639,
+  ["RightVector"] = 8640,
+  ["rharu"] = 8640,
+  ["rightharpoonup"] = 8640,
+  ["DownRightVector"] = 8641,
+  ["rhard"] = 8641,
+  ["rightharpoondown"] = 8641,
+  ["RightDownVector"] = 8642,
+  ["dharr"] = 8642,
+  ["downharpoonright"] = 8642,
+  ["LeftDownVector"] = 8643,
+  ["dharl"] = 8643,
+  ["downharpoonleft"] = 8643,
+  ["RightArrowLeftArrow"] = 8644,
+  ["rightleftarrows"] = 8644,
+  ["rlarr"] = 8644,
+  ["UpArrowDownArrow"] = 8645,
+  ["udarr"] = 8645,
+  ["LeftArrowRightArrow"] = 8646,
+  ["leftrightarrows"] = 8646,
+  ["lrarr"] = 8646,
+  ["leftleftarrows"] = 8647,
+  ["llarr"] = 8647,
+  ["upuparrows"] = 8648,
+  ["uuarr"] = 8648,
+  ["rightrightarrows"] = 8649,
+  ["rrarr"] = 8649,
+  ["ddarr"] = 8650,
+  ["downdownarrows"] = 8650,
+  ["ReverseEquilibrium"] = 8651,
+  ["leftrightharpoons"] = 8651,
+  ["lrhar"] = 8651,
+  ["Equilibrium"] = 8652,
+  ["rightleftharpoons"] = 8652,
+  ["rlhar"] = 8652,
+  ["nLeftarrow"] = 8653,
+  ["nlArr"] = 8653,
+  ["nLeftrightarrow"] = 8654,
+  ["nhArr"] = 8654,
+  ["nRightarrow"] = 8655,
+  ["nrArr"] = 8655,
+  ["DoubleLeftArrow"] = 8656,
+  ["Leftarrow"] = 8656,
+  ["lArr"] = 8656,
+  ["DoubleUpArrow"] = 8657,
+  ["Uparrow"] = 8657,
+  ["uArr"] = 8657,
+  ["DoubleRightArrow"] = 8658,
+  ["Implies"] = 8658,
+  ["Rightarrow"] = 8658,
+  ["rArr"] = 8658,
+  ["DoubleDownArrow"] = 8659,
+  ["Downarrow"] = 8659,
+  ["dArr"] = 8659,
+  ["DoubleLeftRightArrow"] = 8660,
+  ["Leftrightarrow"] = 8660,
+  ["hArr"] = 8660,
+  ["iff"] = 8660,
+  ["DoubleUpDownArrow"] = 8661,
+  ["Updownarrow"] = 8661,
+  ["vArr"] = 8661,
+  ["nwArr"] = 8662,
+  ["neArr"] = 8663,
+  ["seArr"] = 8664,
+  ["swArr"] = 8665,
+  ["Lleftarrow"] = 8666,
+  ["lAarr"] = 8666,
+  ["Rrightarrow"] = 8667,
+  ["rAarr"] = 8667,
+  ["zigrarr"] = 8669,
+  ["LeftArrowBar"] = 8676,
+  ["larrb"] = 8676,
+  ["RightArrowBar"] = 8677,
+  ["rarrb"] = 8677,
+  ["DownArrowUpArrow"] = 8693,
+  ["duarr"] = 8693,
+  ["loarr"] = 8701,
+  ["roarr"] = 8702,
+  ["hoarr"] = 8703,
+  ["ForAll"] = 8704,
+  ["forall"] = 8704,
+  ["comp"] = 8705,
+  ["complement"] = 8705,
+  ["PartialD"] = 8706,
+  ["npart"] = {8706, 824},
+  ["part"] = 8706,
+  ["Exists"] = 8707,
+  ["exist"] = 8707,
+  ["NotExists"] = 8708,
+  ["nexist"] = 8708,
+  ["nexists"] = 8708,
+  ["empty"] = 8709,
+  ["emptyset"] = 8709,
+  ["emptyv"] = 8709,
+  ["varnothing"] = 8709,
+  ["Del"] = 8711,
+  ["nabla"] = 8711,
+  ["Element"] = 8712,
+  ["in"] = 8712,
+  ["isin"] = 8712,
+  ["isinv"] = 8712,
+  ["NotElement"] = 8713,
+  ["notin"] = 8713,
+  ["notinva"] = 8713,
+  ["ReverseElement"] = 8715,
+  ["SuchThat"] = 8715,
+  ["ni"] = 8715,
+  ["niv"] = 8715,
+  ["NotReverseElement"] = 8716,
+  ["notni"] = 8716,
+  ["notniva"] = 8716,
+  ["Product"] = 8719,
+  ["prod"] = 8719,
+  ["Coproduct"] = 8720,
+  ["coprod"] = 8720,
+  ["Sum"] = 8721,
+  ["sum"] = 8721,
+  ["minus"] = 8722,
+  ["MinusPlus"] = 8723,
+  ["mnplus"] = 8723,
+  ["mp"] = 8723,
+  ["dotplus"] = 8724,
+  ["plusdo"] = 8724,
+  ["Backslash"] = 8726,
+  ["setminus"] = 8726,
+  ["setmn"] = 8726,
+  ["smallsetminus"] = 8726,
+  ["ssetmn"] = 8726,
+  ["lowast"] = 8727,
+  ["SmallCircle"] = 8728,
+  ["compfn"] = 8728,
+  ["Sqrt"] = 8730,
+  ["radic"] = 8730,
+  ["Proportional"] = 8733,
+  ["prop"] = 8733,
+  ["propto"] = 8733,
+  ["varpropto"] = 8733,
+  ["vprop"] = 8733,
+  ["infin"] = 8734,
+  ["angrt"] = 8735,
+  ["ang"] = 8736,
+  ["angle"] = 8736,
+  ["nang"] = {8736, 8402},
+  ["angmsd"] = 8737,
+  ["measuredangle"] = 8737,
+  ["angsph"] = 8738,
+  ["VerticalBar"] = 8739,
+  ["mid"] = 8739,
+  ["shortmid"] = 8739,
+  ["smid"] = 8739,
+  ["NotVerticalBar"] = 8740,
+  ["nmid"] = 8740,
+  ["nshortmid"] = 8740,
+  ["nsmid"] = 8740,
+  ["DoubleVerticalBar"] = 8741,
+  ["par"] = 8741,
+  ["parallel"] = 8741,
+  ["shortparallel"] = 8741,
+  ["spar"] = 8741,
+  ["NotDoubleVerticalBar"] = 8742,
+  ["npar"] = 8742,
+  ["nparallel"] = 8742,
+  ["nshortparallel"] = 8742,
+  ["nspar"] = 8742,
+  ["and"] = 8743,
+  ["wedge"] = 8743,
+  ["or"] = 8744,
+  ["vee"] = 8744,
+  ["cap"] = 8745,
+  ["caps"] = {8745, 65024},
+  ["cup"] = 8746,
+  ["cups"] = {8746, 65024},
+  ["Integral"] = 8747,
+  ["int"] = 8747,
+  ["Int"] = 8748,
+  ["iiint"] = 8749,
+  ["tint"] = 8749,
+  ["ContourIntegral"] = 8750,
+  ["conint"] = 8750,
+  ["oint"] = 8750,
+  ["Conint"] = 8751,
+  ["DoubleContourIntegral"] = 8751,
+  ["Cconint"] = 8752,
+  ["cwint"] = 8753,
+  ["ClockwiseContourIntegral"] = 8754,
+  ["cwconint"] = 8754,
+  ["CounterClockwiseContourIntegral"] = 8755,
+  ["awconint"] = 8755,
+  ["Therefore"] = 8756,
+  ["there4"] = 8756,
+  ["therefore"] = 8756,
+  ["Because"] = 8757,
+  ["becaus"] = 8757,
+  ["because"] = 8757,
+  ["ratio"] = 8758,
+  ["Colon"] = 8759,
+  ["Proportion"] = 8759,
+  ["dotminus"] = 8760,
+  ["minusd"] = 8760,
+  ["mDDot"] = 8762,
+  ["homtht"] = 8763,
+  ["Tilde"] = 8764,
+  ["nvsim"] = {8764, 8402},
+  ["sim"] = 8764,
+  ["thicksim"] = 8764,
+  ["thksim"] = 8764,
+  ["backsim"] = 8765,
+  ["bsim"] = 8765,
+  ["race"] = {8765, 817},
+  ["ac"] = 8766,
+  ["acE"] = {8766, 819},
+  ["mstpos"] = 8766,
+  ["acd"] = 8767,
+  ["VerticalTilde"] = 8768,
+  ["wr"] = 8768,
+  ["wreath"] = 8768,
+  ["NotTilde"] = 8769,
+  ["nsim"] = 8769,
+  ["EqualTilde"] = 8770,
+  ["NotEqualTilde"] = {8770, 824},
+  ["eqsim"] = 8770,
+  ["esim"] = 8770,
+  ["nesim"] = {8770, 824},
+  ["TildeEqual"] = 8771,
+  ["sime"] = 8771,
+  ["simeq"] = 8771,
+  ["NotTildeEqual"] = 8772,
+  ["nsime"] = 8772,
+  ["nsimeq"] = 8772,
+  ["TildeFullEqual"] = 8773,
+  ["cong"] = 8773,
+  ["simne"] = 8774,
+  ["NotTildeFullEqual"] = 8775,
+  ["ncong"] = 8775,
+  ["TildeTilde"] = 8776,
+  ["ap"] = 8776,
+  ["approx"] = 8776,
+  ["asymp"] = 8776,
+  ["thickapprox"] = 8776,
+  ["thkap"] = 8776,
+  ["NotTildeTilde"] = 8777,
+  ["nap"] = 8777,
+  ["napprox"] = 8777,
+  ["ape"] = 8778,
+  ["approxeq"] = 8778,
+  ["apid"] = 8779,
+  ["napid"] = {8779, 824},
+  ["backcong"] = 8780,
+  ["bcong"] = 8780,
+  ["CupCap"] = 8781,
+  ["asympeq"] = 8781,
+  ["nvap"] = {8781, 8402},
+  ["Bumpeq"] = 8782,
+  ["HumpDownHump"] = 8782,
+  ["NotHumpDownHump"] = {8782, 824},
+  ["bump"] = 8782,
+  ["nbump"] = {8782, 824},
+  ["HumpEqual"] = 8783,
+  ["NotHumpEqual"] = {8783, 824},
+  ["bumpe"] = 8783,
+  ["bumpeq"] = 8783,
+  ["nbumpe"] = {8783, 824},
+  ["DotEqual"] = 8784,
+  ["doteq"] = 8784,
+  ["esdot"] = 8784,
+  ["nedot"] = {8784, 824},
+  ["doteqdot"] = 8785,
+  ["eDot"] = 8785,
+  ["efDot"] = 8786,
+  ["fallingdotseq"] = 8786,
+  ["erDot"] = 8787,
+  ["risingdotseq"] = 8787,
+  ["Assign"] = 8788,
+  ["colone"] = 8788,
+  ["coloneq"] = 8788,
+  ["ecolon"] = 8789,
+  ["eqcolon"] = 8789,
+  ["ecir"] = 8790,
+  ["eqcirc"] = 8790,
+  ["circeq"] = 8791,
+  ["cire"] = 8791,
+  ["wedgeq"] = 8793,
+  ["veeeq"] = 8794,
+  ["triangleq"] = 8796,
+  ["trie"] = 8796,
+  ["equest"] = 8799,
+  ["questeq"] = 8799,
+  ["NotEqual"] = 8800,
+  ["ne"] = 8800,
+  ["Congruent"] = 8801,
+  ["bnequiv"] = {8801, 8421},
+  ["equiv"] = 8801,
+  ["NotCongruent"] = 8802,
+  ["nequiv"] = 8802,
+  ["le"] = 8804,
+  ["leq"] = 8804,
+  ["nvle"] = {8804, 8402},
+  ["GreaterEqual"] = 8805,
+  ["ge"] = 8805,
+  ["geq"] = 8805,
+  ["nvge"] = {8805, 8402},
+  ["LessFullEqual"] = 8806,
+  ["lE"] = 8806,
+  ["leqq"] = 8806,
+  ["nlE"] = {8806, 824},
+  ["nleqq"] = {8806, 824},
+  ["GreaterFullEqual"] = 8807,
+  ["NotGreaterFullEqual"] = {8807, 824},
+  ["gE"] = 8807,
+  ["geqq"] = 8807,
+  ["ngE"] = {8807, 824},
+  ["ngeqq"] = {8807, 824},
+  ["lnE"] = 8808,
+  ["lneqq"] = 8808,
+  ["lvertneqq"] = {8808, 65024},
+  ["lvnE"] = {8808, 65024},
+  ["gnE"] = 8809,
+  ["gneqq"] = 8809,
+  ["gvertneqq"] = {8809, 65024},
+  ["gvnE"] = {8809, 65024},
+  ["Lt"] = 8810,
+  ["NestedLessLess"] = 8810,
+  ["NotLessLess"] = {8810, 824},
+  ["ll"] = 8810,
+  ["nLt"] = {8810, 8402},
+  ["nLtv"] = {8810, 824},
+  ["Gt"] = 8811,
+  ["NestedGreaterGreater"] = 8811,
+  ["NotGreaterGreater"] = {8811, 824},
+  ["gg"] = 8811,
+  ["nGt"] = {8811, 8402},
+  ["nGtv"] = {8811, 824},
+  ["between"] = 8812,
+  ["twixt"] = 8812,
+  ["NotCupCap"] = 8813,
+  ["NotLess"] = 8814,
+  ["nless"] = 8814,
+  ["nlt"] = 8814,
+  ["NotGreater"] = 8815,
+  ["ngt"] = 8815,
+  ["ngtr"] = 8815,
+  ["NotLessEqual"] = 8816,
+  ["nle"] = 8816,
+  ["nleq"] = 8816,
+  ["NotGreaterEqual"] = 8817,
+  ["nge"] = 8817,
+  ["ngeq"] = 8817,
+  ["LessTilde"] = 8818,
+  ["lesssim"] = 8818,
+  ["lsim"] = 8818,
+  ["GreaterTilde"] = 8819,
+  ["gsim"] = 8819,
+  ["gtrsim"] = 8819,
+  ["NotLessTilde"] = 8820,
+  ["nlsim"] = 8820,
+  ["NotGreaterTilde"] = 8821,
+  ["ngsim"] = 8821,
+  ["LessGreater"] = 8822,
+  ["lessgtr"] = 8822,
+  ["lg"] = 8822,
+  ["GreaterLess"] = 8823,
+  ["gl"] = 8823,
+  ["gtrless"] = 8823,
+  ["NotLessGreater"] = 8824,
+  ["ntlg"] = 8824,
+  ["NotGreaterLess"] = 8825,
+  ["ntgl"] = 8825,
+  ["Precedes"] = 8826,
+  ["pr"] = 8826,
+  ["prec"] = 8826,
+  ["Succeeds"] = 8827,
+  ["sc"] = 8827,
+  ["succ"] = 8827,
+  ["PrecedesSlantEqual"] = 8828,
+  ["prcue"] = 8828,
+  ["preccurlyeq"] = 8828,
+  ["SucceedsSlantEqual"] = 8829,
+  ["sccue"] = 8829,
+  ["succcurlyeq"] = 8829,
+  ["PrecedesTilde"] = 8830,
+  ["precsim"] = 8830,
+  ["prsim"] = 8830,
+  ["NotSucceedsTilde"] = {8831, 824},
+  ["SucceedsTilde"] = 8831,
+  ["scsim"] = 8831,
+  ["succsim"] = 8831,
+  ["NotPrecedes"] = 8832,
+  ["npr"] = 8832,
+  ["nprec"] = 8832,
+  ["NotSucceeds"] = 8833,
+  ["nsc"] = 8833,
+  ["nsucc"] = 8833,
+  ["NotSubset"] = {8834, 8402},
+  ["nsubset"] = {8834, 8402},
+  ["sub"] = 8834,
+  ["subset"] = 8834,
+  ["vnsub"] = {8834, 8402},
+  ["NotSuperset"] = {8835, 8402},
+  ["Superset"] = 8835,
+  ["nsupset"] = {8835, 8402},
+  ["sup"] = 8835,
+  ["supset"] = 8835,
+  ["vnsup"] = {8835, 8402},
+  ["nsub"] = 8836,
+  ["nsup"] = 8837,
+  ["SubsetEqual"] = 8838,
+  ["sube"] = 8838,
+  ["subseteq"] = 8838,
+  ["SupersetEqual"] = 8839,
+  ["supe"] = 8839,
+  ["supseteq"] = 8839,
+  ["NotSubsetEqual"] = 8840,
+  ["nsube"] = 8840,
+  ["nsubseteq"] = 8840,
+  ["NotSupersetEqual"] = 8841,
+  ["nsupe"] = 8841,
+  ["nsupseteq"] = 8841,
+  ["subne"] = 8842,
+  ["subsetneq"] = 8842,
+  ["varsubsetneq"] = {8842, 65024},
+  ["vsubne"] = {8842, 65024},
+  ["supne"] = 8843,
+  ["supsetneq"] = 8843,
+  ["varsupsetneq"] = {8843, 65024},
+  ["vsupne"] = {8843, 65024},
+  ["cupdot"] = 8845,
+  ["UnionPlus"] = 8846,
+  ["uplus"] = 8846,
+  ["NotSquareSubset"] = {8847, 824},
+  ["SquareSubset"] = 8847,
+  ["sqsub"] = 8847,
+  ["sqsubset"] = 8847,
+  ["NotSquareSuperset"] = {8848, 824},
+  ["SquareSuperset"] = 8848,
+  ["sqsup"] = 8848,
+  ["sqsupset"] = 8848,
+  ["SquareSubsetEqual"] = 8849,
+  ["sqsube"] = 8849,
+  ["sqsubseteq"] = 8849,
+  ["SquareSupersetEqual"] = 8850,
+  ["sqsupe"] = 8850,
+  ["sqsupseteq"] = 8850,
+  ["SquareIntersection"] = 8851,
+  ["sqcap"] = 8851,
+  ["sqcaps"] = {8851, 65024},
+  ["SquareUnion"] = 8852,
+  ["sqcup"] = 8852,
+  ["sqcups"] = {8852, 65024},
+  ["CirclePlus"] = 8853,
+  ["oplus"] = 8853,
+  ["CircleMinus"] = 8854,
+  ["ominus"] = 8854,
+  ["CircleTimes"] = 8855,
+  ["otimes"] = 8855,
+  ["osol"] = 8856,
+  ["CircleDot"] = 8857,
+  ["odot"] = 8857,
+  ["circledcirc"] = 8858,
+  ["ocir"] = 8858,
+  ["circledast"] = 8859,
+  ["oast"] = 8859,
+  ["circleddash"] = 8861,
+  ["odash"] = 8861,
+  ["boxplus"] = 8862,
+  ["plusb"] = 8862,
+  ["boxminus"] = 8863,
+  ["minusb"] = 8863,
+  ["boxtimes"] = 8864,
+  ["timesb"] = 8864,
+  ["dotsquare"] = 8865,
+  ["sdotb"] = 8865,
+  ["RightTee"] = 8866,
+  ["vdash"] = 8866,
+  ["LeftTee"] = 8867,
+  ["dashv"] = 8867,
+  ["DownTee"] = 8868,
+  ["top"] = 8868,
+  ["UpTee"] = 8869,
+  ["bot"] = 8869,
+  ["bottom"] = 8869,
+  ["perp"] = 8869,
+  ["models"] = 8871,
+  ["DoubleRightTee"] = 8872,
+  ["vDash"] = 8872,
+  ["Vdash"] = 8873,
+  ["Vvdash"] = 8874,
+  ["VDash"] = 8875,
+  ["nvdash"] = 8876,
+  ["nvDash"] = 8877,
+  ["nVdash"] = 8878,
+  ["nVDash"] = 8879,
+  ["prurel"] = 8880,
+  ["LeftTriangle"] = 8882,
+  ["vartriangleleft"] = 8882,
+  ["vltri"] = 8882,
+  ["RightTriangle"] = 8883,
+  ["vartriangleright"] = 8883,
+  ["vrtri"] = 8883,
+  ["LeftTriangleEqual"] = 8884,
+  ["ltrie"] = 8884,
+  ["nvltrie"] = {8884, 8402},
+  ["trianglelefteq"] = 8884,
+  ["RightTriangleEqual"] = 8885,
+  ["nvrtrie"] = {8885, 8402},
+  ["rtrie"] = 8885,
+  ["trianglerighteq"] = 8885,
+  ["origof"] = 8886,
+  ["imof"] = 8887,
+  ["multimap"] = 8888,
+  ["mumap"] = 8888,
+  ["hercon"] = 8889,
+  ["intcal"] = 8890,
+  ["intercal"] = 8890,
+  ["veebar"] = 8891,
+  ["barvee"] = 8893,
+  ["angrtvb"] = 8894,
+  ["lrtri"] = 8895,
+  ["Wedge"] = 8896,
+  ["bigwedge"] = 8896,
+  ["xwedge"] = 8896,
+  ["Vee"] = 8897,
+  ["bigvee"] = 8897,
+  ["xvee"] = 8897,
+  ["Intersection"] = 8898,
+  ["bigcap"] = 8898,
+  ["xcap"] = 8898,
+  ["Union"] = 8899,
+  ["bigcup"] = 8899,
+  ["xcup"] = 8899,
+  ["Diamond"] = 8900,
+  ["diam"] = 8900,
+  ["diamond"] = 8900,
+  ["sdot"] = 8901,
+  ["Star"] = 8902,
+  ["sstarf"] = 8902,
+  ["divideontimes"] = 8903,
+  ["divonx"] = 8903,
+  ["bowtie"] = 8904,
+  ["ltimes"] = 8905,
+  ["rtimes"] = 8906,
+  ["leftthreetimes"] = 8907,
+  ["lthree"] = 8907,
+  ["rightthreetimes"] = 8908,
+  ["rthree"] = 8908,
+  ["backsimeq"] = 8909,
+  ["bsime"] = 8909,
+  ["curlyvee"] = 8910,
+  ["cuvee"] = 8910,
+  ["curlywedge"] = 8911,
+  ["cuwed"] = 8911,
+  ["Sub"] = 8912,
+  ["Subset"] = 8912,
+  ["Sup"] = 8913,
+  ["Supset"] = 8913,
+  ["Cap"] = 8914,
+  ["Cup"] = 8915,
+  ["fork"] = 8916,
+  ["pitchfork"] = 8916,
+  ["epar"] = 8917,
+  ["lessdot"] = 8918,
+  ["ltdot"] = 8918,
+  ["gtdot"] = 8919,
+  ["gtrdot"] = 8919,
+  ["Ll"] = 8920,
+  ["nLl"] = {8920, 824},
+  ["Gg"] = 8921,
+  ["ggg"] = 8921,
+  ["nGg"] = {8921, 824},
+  ["LessEqualGreater"] = 8922,
+  ["leg"] = 8922,
+  ["lesg"] = {8922, 65024},
+  ["lesseqgtr"] = 8922,
+  ["GreaterEqualLess"] = 8923,
+  ["gel"] = 8923,
+  ["gesl"] = {8923, 65024},
+  ["gtreqless"] = 8923,
+  ["cuepr"] = 8926,
+  ["curlyeqprec"] = 8926,
+  ["cuesc"] = 8927,
+  ["curlyeqsucc"] = 8927,
+  ["NotPrecedesSlantEqual"] = 8928,
+  ["nprcue"] = 8928,
+  ["NotSucceedsSlantEqual"] = 8929,
+  ["nsccue"] = 8929,
+  ["NotSquareSubsetEqual"] = 8930,
+  ["nsqsube"] = 8930,
+  ["NotSquareSupersetEqual"] = 8931,
+  ["nsqsupe"] = 8931,
+  ["lnsim"] = 8934,
+  ["gnsim"] = 8935,
+  ["precnsim"] = 8936,
+  ["prnsim"] = 8936,
+  ["scnsim"] = 8937,
+  ["succnsim"] = 8937,
+  ["NotLeftTriangle"] = 8938,
+  ["nltri"] = 8938,
+  ["ntriangleleft"] = 8938,
+  ["NotRightTriangle"] = 8939,
+  ["nrtri"] = 8939,
+  ["ntriangleright"] = 8939,
+  ["NotLeftTriangleEqual"] = 8940,
+  ["nltrie"] = 8940,
+  ["ntrianglelefteq"] = 8940,
+  ["NotRightTriangleEqual"] = 8941,
+  ["nrtrie"] = 8941,
+  ["ntrianglerighteq"] = 8941,
+  ["vellip"] = 8942,
+  ["ctdot"] = 8943,
+  ["utdot"] = 8944,
+  ["dtdot"] = 8945,
+  ["disin"] = 8946,
+  ["isinsv"] = 8947,
+  ["isins"] = 8948,
+  ["isindot"] = 8949,
+  ["notindot"] = {8949, 824},
+  ["notinvc"] = 8950,
+  ["notinvb"] = 8951,
+  ["isinE"] = 8953,
+  ["notinE"] = {8953, 824},
+  ["nisd"] = 8954,
+  ["xnis"] = 8955,
+  ["nis"] = 8956,
+  ["notnivc"] = 8957,
+  ["notnivb"] = 8958,
+  ["barwed"] = 8965,
+  ["barwedge"] = 8965,
+  ["Barwed"] = 8966,
+  ["doublebarwedge"] = 8966,
+  ["LeftCeiling"] = 8968,
+  ["lceil"] = 8968,
+  ["RightCeiling"] = 8969,
+  ["rceil"] = 8969,
+  ["LeftFloor"] = 8970,
+  ["lfloor"] = 8970,
+  ["RightFloor"] = 8971,
+  ["rfloor"] = 8971,
+  ["drcrop"] = 8972,
+  ["dlcrop"] = 8973,
+  ["urcrop"] = 8974,
+  ["ulcrop"] = 8975,
+  ["bnot"] = 8976,
+  ["profline"] = 8978,
+  ["profsurf"] = 8979,
+  ["telrec"] = 8981,
+  ["target"] = 8982,
+  ["ulcorn"] = 8988,
+  ["ulcorner"] = 8988,
+  ["urcorn"] = 8989,
+  ["urcorner"] = 8989,
+  ["dlcorn"] = 8990,
+  ["llcorner"] = 8990,
+  ["drcorn"] = 8991,
+  ["lrcorner"] = 8991,
+  ["frown"] = 8994,
+  ["sfrown"] = 8994,
+  ["smile"] = 8995,
+  ["ssmile"] = 8995,
+  ["cylcty"] = 9005,
+  ["profalar"] = 9006,
+  ["topbot"] = 9014,
+  ["ovbar"] = 9021,
+  ["solbar"] = 9023,
+  ["angzarr"] = 9084,
+  ["lmoust"] = 9136,
+  ["lmoustache"] = 9136,
+  ["rmoust"] = 9137,
+  ["rmoustache"] = 9137,
+  ["OverBracket"] = 9140,
+  ["tbrk"] = 9140,
+  ["UnderBracket"] = 9141,
+  ["bbrk"] = 9141,
+  ["bbrktbrk"] = 9142,
+  ["OverParenthesis"] = 9180,
+  ["UnderParenthesis"] = 9181,
+  ["OverBrace"] = 9182,
+  ["UnderBrace"] = 9183,
+  ["trpezium"] = 9186,
+  ["elinters"] = 9191,
+  ["blank"] = 9251,
+  ["circledS"] = 9416,
+  ["oS"] = 9416,
+  ["HorizontalLine"] = 9472,
+  ["boxh"] = 9472,
+  ["boxv"] = 9474,
+  ["boxdr"] = 9484,
+  ["boxdl"] = 9488,
+  ["boxur"] = 9492,
+  ["boxul"] = 9496,
+  ["boxvr"] = 9500,
+  ["boxvl"] = 9508,
+  ["boxhd"] = 9516,
+  ["boxhu"] = 9524,
+  ["boxvh"] = 9532,
+  ["boxH"] = 9552,
+  ["boxV"] = 9553,
+  ["boxdR"] = 9554,
+  ["boxDr"] = 9555,
+  ["boxDR"] = 9556,
+  ["boxdL"] = 9557,
+  ["boxDl"] = 9558,
+  ["boxDL"] = 9559,
+  ["boxuR"] = 9560,
+  ["boxUr"] = 9561,
+  ["boxUR"] = 9562,
+  ["boxuL"] = 9563,
+  ["boxUl"] = 9564,
+  ["boxUL"] = 9565,
+  ["boxvR"] = 9566,
+  ["boxVr"] = 9567,
+  ["boxVR"] = 9568,
+  ["boxvL"] = 9569,
+  ["boxVl"] = 9570,
+  ["boxVL"] = 9571,
+  ["boxHd"] = 9572,
+  ["boxhD"] = 9573,
+  ["boxHD"] = 9574,
+  ["boxHu"] = 9575,
+  ["boxhU"] = 9576,
+  ["boxHU"] = 9577,
+  ["boxvH"] = 9578,
+  ["boxVh"] = 9579,
+  ["boxVH"] = 9580,
+  ["uhblk"] = 9600,
+  ["lhblk"] = 9604,
+  ["block"] = 9608,
+  ["blk14"] = 9617,
+  ["blk12"] = 9618,
+  ["blk34"] = 9619,
+  ["Square"] = 9633,
+  ["squ"] = 9633,
+  ["square"] = 9633,
+  ["FilledVerySmallSquare"] = 9642,
+  ["blacksquare"] = 9642,
+  ["squarf"] = 9642,
+  ["squf"] = 9642,
+  ["EmptyVerySmallSquare"] = 9643,
+  ["rect"] = 9645,
+  ["marker"] = 9646,
+  ["fltns"] = 9649,
+  ["bigtriangleup"] = 9651,
+  ["xutri"] = 9651,
+  ["blacktriangle"] = 9652,
+  ["utrif"] = 9652,
+  ["triangle"] = 9653,
+  ["utri"] = 9653,
+  ["blacktriangleright"] = 9656,
+  ["rtrif"] = 9656,
+  ["rtri"] = 9657,
+  ["triangleright"] = 9657,
+  ["bigtriangledown"] = 9661,
+  ["xdtri"] = 9661,
+  ["blacktriangledown"] = 9662,
+  ["dtrif"] = 9662,
+  ["dtri"] = 9663,
+  ["triangledown"] = 9663,
+  ["blacktriangleleft"] = 9666,
+  ["ltrif"] = 9666,
+  ["ltri"] = 9667,
+  ["triangleleft"] = 9667,
+  ["loz"] = 9674,
+  ["lozenge"] = 9674,
+  ["cir"] = 9675,
+  ["tridot"] = 9708,
+  ["bigcirc"] = 9711,
+  ["xcirc"] = 9711,
+  ["ultri"] = 9720,
+  ["urtri"] = 9721,
+  ["lltri"] = 9722,
+  ["EmptySmallSquare"] = 9723,
+  ["FilledSmallSquare"] = 9724,
+  ["bigstar"] = 9733,
+  ["starf"] = 9733,
+  ["star"] = 9734,
+  ["phone"] = 9742,
+  ["female"] = 9792,
+  ["male"] = 9794,
+  ["spades"] = 9824,
+  ["spadesuit"] = 9824,
+  ["clubs"] = 9827,
+  ["clubsuit"] = 9827,
+  ["hearts"] = 9829,
+  ["heartsuit"] = 9829,
+  ["diamondsuit"] = 9830,
+  ["diams"] = 9830,
+  ["sung"] = 9834,
+  ["flat"] = 9837,
+  ["natur"] = 9838,
+  ["natural"] = 9838,
+  ["sharp"] = 9839,
+  ["check"] = 10003,
+  ["checkmark"] = 10003,
+  ["cross"] = 10007,
+  ["malt"] = 10016,
+  ["maltese"] = 10016,
+  ["sext"] = 10038,
+  ["VerticalSeparator"] = 10072,
+  ["lbbrk"] = 10098,
+  ["rbbrk"] = 10099,
+  ["bsolhsub"] = 10184,
+  ["suphsol"] = 10185,
+  ["LeftDoubleBracket"] = 10214,
+  ["lobrk"] = 10214,
+  ["RightDoubleBracket"] = 10215,
+  ["robrk"] = 10215,
+  ["LeftAngleBracket"] = 10216,
+  ["lang"] = 10216,
+  ["langle"] = 10216,
+  ["RightAngleBracket"] = 10217,
+  ["rang"] = 10217,
+  ["rangle"] = 10217,
+  ["Lang"] = 10218,
+  ["Rang"] = 10219,
+  ["loang"] = 10220,
+  ["roang"] = 10221,
+  ["LongLeftArrow"] = 10229,
+  ["longleftarrow"] = 10229,
+  ["xlarr"] = 10229,
+  ["LongRightArrow"] = 10230,
+  ["longrightarrow"] = 10230,
+  ["xrarr"] = 10230,
+  ["LongLeftRightArrow"] = 10231,
+  ["longleftrightarrow"] = 10231,
+  ["xharr"] = 10231,
+  ["DoubleLongLeftArrow"] = 10232,
+  ["Longleftarrow"] = 10232,
+  ["xlArr"] = 10232,
+  ["DoubleLongRightArrow"] = 10233,
+  ["Longrightarrow"] = 10233,
+  ["xrArr"] = 10233,
+  ["DoubleLongLeftRightArrow"] = 10234,
+  ["Longleftrightarrow"] = 10234,
+  ["xhArr"] = 10234,
+  ["longmapsto"] = 10236,
+  ["xmap"] = 10236,
+  ["dzigrarr"] = 10239,
+  ["nvlArr"] = 10498,
+  ["nvrArr"] = 10499,
+  ["nvHarr"] = 10500,
+  ["Map"] = 10501,
+  ["lbarr"] = 10508,
+  ["bkarow"] = 10509,
+  ["rbarr"] = 10509,
+  ["lBarr"] = 10510,
+  ["dbkarow"] = 10511,
+  ["rBarr"] = 10511,
+  ["RBarr"] = 10512,
+  ["drbkarow"] = 10512,
+  ["DDotrahd"] = 10513,
+  ["UpArrowBar"] = 10514,
+  ["DownArrowBar"] = 10515,
+  ["Rarrtl"] = 10518,
+  ["latail"] = 10521,
+  ["ratail"] = 10522,
+  ["lAtail"] = 10523,
+  ["rAtail"] = 10524,
+  ["larrfs"] = 10525,
+  ["rarrfs"] = 10526,
+  ["larrbfs"] = 10527,
+  ["rarrbfs"] = 10528,
+  ["nwarhk"] = 10531,
+  ["nearhk"] = 10532,
+  ["hksearow"] = 10533,
+  ["searhk"] = 10533,
+  ["hkswarow"] = 10534,
+  ["swarhk"] = 10534,
+  ["nwnear"] = 10535,
+  ["nesear"] = 10536,
+  ["toea"] = 10536,
+  ["seswar"] = 10537,
+  ["tosa"] = 10537,
+  ["swnwar"] = 10538,
+  ["nrarrc"] = {10547, 824},
+  ["rarrc"] = 10547,
+  ["cudarrr"] = 10549,
+  ["ldca"] = 10550,
+  ["rdca"] = 10551,
+  ["cudarrl"] = 10552,
+  ["larrpl"] = 10553,
+  ["curarrm"] = 10556,
+  ["cularrp"] = 10557,
+  ["rarrpl"] = 10565,
+  ["harrcir"] = 10568,
+  ["Uarrocir"] = 10569,
+  ["lurdshar"] = 10570,
+  ["ldrushar"] = 10571,
+  ["LeftRightVector"] = 10574,
+  ["RightUpDownVector"] = 10575,
+  ["DownLeftRightVector"] = 10576,
+  ["LeftUpDownVector"] = 10577,
+  ["LeftVectorBar"] = 10578,
+  ["RightVectorBar"] = 10579,
+  ["RightUpVectorBar"] = 10580,
+  ["RightDownVectorBar"] = 10581,
+  ["DownLeftVectorBar"] = 10582,
+  ["DownRightVectorBar"] = 10583,
+  ["LeftUpVectorBar"] = 10584,
+  ["LeftDownVectorBar"] = 10585,
+  ["LeftTeeVector"] = 10586,
+  ["RightTeeVector"] = 10587,
+  ["RightUpTeeVector"] = 10588,
+  ["RightDownTeeVector"] = 10589,
+  ["DownLeftTeeVector"] = 10590,
+  ["DownRightTeeVector"] = 10591,
+  ["LeftUpTeeVector"] = 10592,
+  ["LeftDownTeeVector"] = 10593,
+  ["lHar"] = 10594,
+  ["uHar"] = 10595,
+  ["rHar"] = 10596,
+  ["dHar"] = 10597,
+  ["luruhar"] = 10598,
+  ["ldrdhar"] = 10599,
+  ["ruluhar"] = 10600,
+  ["rdldhar"] = 10601,
+  ["lharul"] = 10602,
+  ["llhard"] = 10603,
+  ["rharul"] = 10604,
+  ["lrhard"] = 10605,
+  ["UpEquilibrium"] = 10606,
+  ["udhar"] = 10606,
+  ["ReverseUpEquilibrium"] = 10607,
+  ["duhar"] = 10607,
+  ["RoundImplies"] = 10608,
+  ["erarr"] = 10609,
+  ["simrarr"] = 10610,
+  ["larrsim"] = 10611,
+  ["rarrsim"] = 10612,
+  ["rarrap"] = 10613,
+  ["ltlarr"] = 10614,
+  ["gtrarr"] = 10616,
+  ["subrarr"] = 10617,
+  ["suplarr"] = 10619,
+  ["lfisht"] = 10620,
+  ["rfisht"] = 10621,
+  ["ufisht"] = 10622,
+  ["dfisht"] = 10623,
+  ["lopar"] = 10629,
+  ["ropar"] = 10630,
+  ["lbrke"] = 10635,
+  ["rbrke"] = 10636,
+  ["lbrkslu"] = 10637,
+  ["rbrksld"] = 10638,
+  ["lbrksld"] = 10639,
+  ["rbrkslu"] = 10640,
+  ["langd"] = 10641,
+  ["rangd"] = 10642,
+  ["lparlt"] = 10643,
+  ["rpargt"] = 10644,
+  ["gtlPar"] = 10645,
+  ["ltrPar"] = 10646,
+  ["vzigzag"] = 10650,
+  ["vangrt"] = 10652,
+  ["angrtvbd"] = 10653,
+  ["ange"] = 10660,
+  ["range"] = 10661,
+  ["dwangle"] = 10662,
+  ["uwangle"] = 10663,
+  ["angmsdaa"] = 10664,
+  ["angmsdab"] = 10665,
+  ["angmsdac"] = 10666,
+  ["angmsdad"] = 10667,
+  ["angmsdae"] = 10668,
+  ["angmsdaf"] = 10669,
+  ["angmsdag"] = 10670,
+  ["angmsdah"] = 10671,
+  ["bemptyv"] = 10672,
+  ["demptyv"] = 10673,
+  ["cemptyv"] = 10674,
+  ["raemptyv"] = 10675,
+  ["laemptyv"] = 10676,
+  ["ohbar"] = 10677,
+  ["omid"] = 10678,
+  ["opar"] = 10679,
+  ["operp"] = 10681,
+  ["olcross"] = 10683,
+  ["odsold"] = 10684,
+  ["olcir"] = 10686,
+  ["ofcir"] = 10687,
+  ["olt"] = 10688,
+  ["ogt"] = 10689,
+  ["cirscir"] = 10690,
+  ["cirE"] = 10691,
+  ["solb"] = 10692,
+  ["bsolb"] = 10693,
+  ["boxbox"] = 10697,
+  ["trisb"] = 10701,
+  ["rtriltri"] = 10702,
+  ["LeftTriangleBar"] = 10703,
+  ["NotLeftTriangleBar"] = {10703, 824},
+  ["NotRightTriangleBar"] = {10704, 824},
+  ["RightTriangleBar"] = 10704,
+  ["iinfin"] = 10716,
+  ["infintie"] = 10717,
+  ["nvinfin"] = 10718,
+  ["eparsl"] = 10723,
+  ["smeparsl"] = 10724,
+  ["eqvparsl"] = 10725,
+  ["blacklozenge"] = 10731,
+  ["lozf"] = 10731,
+  ["RuleDelayed"] = 10740,
+  ["dsol"] = 10742,
+  ["bigodot"] = 10752,
+  ["xodot"] = 10752,
+  ["bigoplus"] = 10753,
+  ["xoplus"] = 10753,
+  ["bigotimes"] = 10754,
+  ["xotime"] = 10754,
+  ["biguplus"] = 10756,
+  ["xuplus"] = 10756,
+  ["bigsqcup"] = 10758,
+  ["xsqcup"] = 10758,
+  ["iiiint"] = 10764,
+  ["qint"] = 10764,
+  ["fpartint"] = 10765,
+  ["cirfnint"] = 10768,
+  ["awint"] = 10769,
+  ["rppolint"] = 10770,
+  ["scpolint"] = 10771,
+  ["npolint"] = 10772,
+  ["pointint"] = 10773,
+  ["quatint"] = 10774,
+  ["intlarhk"] = 10775,
+  ["pluscir"] = 10786,
+  ["plusacir"] = 10787,
+  ["simplus"] = 10788,
+  ["plusdu"] = 10789,
+  ["plussim"] = 10790,
+  ["plustwo"] = 10791,
+  ["mcomma"] = 10793,
+  ["minusdu"] = 10794,
+  ["loplus"] = 10797,
+  ["roplus"] = 10798,
+  ["Cross"] = 10799,
+  ["timesd"] = 10800,
+  ["timesbar"] = 10801,
+  ["smashp"] = 10803,
+  ["lotimes"] = 10804,
+  ["rotimes"] = 10805,
+  ["otimesas"] = 10806,
+  ["Otimes"] = 10807,
+  ["odiv"] = 10808,
+  ["triplus"] = 10809,
+  ["triminus"] = 10810,
+  ["tritime"] = 10811,
+  ["intprod"] = 10812,
+  ["iprod"] = 10812,
+  ["amalg"] = 10815,
+  ["capdot"] = 10816,
+  ["ncup"] = 10818,
+  ["ncap"] = 10819,
+  ["capand"] = 10820,
+  ["cupor"] = 10821,
+  ["cupcap"] = 10822,
+  ["capcup"] = 10823,
+  ["cupbrcap"] = 10824,
+  ["capbrcup"] = 10825,
+  ["cupcup"] = 10826,
+  ["capcap"] = 10827,
+  ["ccups"] = 10828,
+  ["ccaps"] = 10829,
+  ["ccupssm"] = 10832,
+  ["And"] = 10835,
+  ["Or"] = 10836,
+  ["andand"] = 10837,
+  ["oror"] = 10838,
+  ["orslope"] = 10839,
+  ["andslope"] = 10840,
+  ["andv"] = 10842,
+  ["orv"] = 10843,
+  ["andd"] = 10844,
+  ["ord"] = 10845,
+  ["wedbar"] = 10847,
+  ["sdote"] = 10854,
+  ["simdot"] = 10858,
+  ["congdot"] = 10861,
+  ["ncongdot"] = {10861, 824},
+  ["easter"] = 10862,
+  ["apacir"] = 10863,
+  ["apE"] = 10864,
+  ["napE"] = {10864, 824},
+  ["eplus"] = 10865,
+  ["pluse"] = 10866,
+  ["Esim"] = 10867,
+  ["Colone"] = 10868,
+  ["Equal"] = 10869,
+  ["ddotseq"] = 10871,
+  ["eDDot"] = 10871,
+  ["equivDD"] = 10872,
+  ["ltcir"] = 10873,
+  ["gtcir"] = 10874,
+  ["ltquest"] = 10875,
+  ["gtquest"] = 10876,
+  ["LessSlantEqual"] = 10877,
+  ["NotLessSlantEqual"] = {10877, 824},
+  ["leqslant"] = 10877,
+  ["les"] = 10877,
+  ["nleqslant"] = {10877, 824},
+  ["nles"] = {10877, 824},
+  ["GreaterSlantEqual"] = 10878,
+  ["NotGreaterSlantEqual"] = {10878, 824},
+  ["geqslant"] = 10878,
+  ["ges"] = 10878,
+  ["ngeqslant"] = {10878, 824},
+  ["nges"] = {10878, 824},
+  ["lesdot"] = 10879,
+  ["gesdot"] = 10880,
+  ["lesdoto"] = 10881,
+  ["gesdoto"] = 10882,
+  ["lesdotor"] = 10883,
+  ["gesdotol"] = 10884,
+  ["lap"] = 10885,
+  ["lessapprox"] = 10885,
+  ["gap"] = 10886,
+  ["gtrapprox"] = 10886,
+  ["lne"] = 10887,
+  ["lneq"] = 10887,
+  ["gne"] = 10888,
+  ["gneq"] = 10888,
+  ["lnap"] = 10889,
+  ["lnapprox"] = 10889,
+  ["gnap"] = 10890,
+  ["gnapprox"] = 10890,
+  ["lEg"] = 10891,
+  ["lesseqqgtr"] = 10891,
+  ["gEl"] = 10892,
+  ["gtreqqless"] = 10892,
+  ["lsime"] = 10893,
+  ["gsime"] = 10894,
+  ["lsimg"] = 10895,
+  ["gsiml"] = 10896,
+  ["lgE"] = 10897,
+  ["glE"] = 10898,
+  ["lesges"] = 10899,
+  ["gesles"] = 10900,
+  ["els"] = 10901,
+  ["eqslantless"] = 10901,
+  ["egs"] = 10902,
+  ["eqslantgtr"] = 10902,
+  ["elsdot"] = 10903,
+  ["egsdot"] = 10904,
+  ["el"] = 10905,
+  ["eg"] = 10906,
+  ["siml"] = 10909,
+  ["simg"] = 10910,
+  ["simlE"] = 10911,
+  ["simgE"] = 10912,
+  ["LessLess"] = 10913,
+  ["NotNestedLessLess"] = {10913, 824},
+  ["GreaterGreater"] = 10914,
+  ["NotNestedGreaterGreater"] = {10914, 824},
+  ["glj"] = 10916,
+  ["gla"] = 10917,
+  ["ltcc"] = 10918,
+  ["gtcc"] = 10919,
+  ["lescc"] = 10920,
+  ["gescc"] = 10921,
+  ["smt"] = 10922,
+  ["lat"] = 10923,
+  ["smte"] = 10924,
+  ["smtes"] = {10924, 65024},
+  ["late"] = 10925,
+  ["lates"] = {10925, 65024},
+  ["bumpE"] = 10926,
+  ["NotPrecedesEqual"] = {10927, 824},
+  ["PrecedesEqual"] = 10927,
+  ["npre"] = {10927, 824},
+  ["npreceq"] = {10927, 824},
+  ["pre"] = 10927,
+  ["preceq"] = 10927,
+  ["NotSucceedsEqual"] = {10928, 824},
+  ["SucceedsEqual"] = 10928,
+  ["nsce"] = {10928, 824},
+  ["nsucceq"] = {10928, 824},
+  ["sce"] = 10928,
+  ["succeq"] = 10928,
+  ["prE"] = 10931,
+  ["scE"] = 10932,
+  ["precneqq"] = 10933,
+  ["prnE"] = 10933,
+  ["scnE"] = 10934,
+  ["succneqq"] = 10934,
+  ["prap"] = 10935,
+  ["precapprox"] = 10935,
+  ["scap"] = 10936,
+  ["succapprox"] = 10936,
+  ["precnapprox"] = 10937,
+  ["prnap"] = 10937,
+  ["scnap"] = 10938,
+  ["succnapprox"] = 10938,
+  ["Pr"] = 10939,
+  ["Sc"] = 10940,
+  ["subdot"] = 10941,
+  ["supdot"] = 10942,
+  ["subplus"] = 10943,
+  ["supplus"] = 10944,
+  ["submult"] = 10945,
+  ["supmult"] = 10946,
+  ["subedot"] = 10947,
+  ["supedot"] = 10948,
+  ["nsubE"] = {10949, 824},
+  ["nsubseteqq"] = {10949, 824},
+  ["subE"] = 10949,
+  ["subseteqq"] = 10949,
+  ["nsupE"] = {10950, 824},
+  ["nsupseteqq"] = {10950, 824},
+  ["supE"] = 10950,
+  ["supseteqq"] = 10950,
+  ["subsim"] = 10951,
+  ["supsim"] = 10952,
+  ["subnE"] = 10955,
+  ["subsetneqq"] = 10955,
+  ["varsubsetneqq"] = {10955, 65024},
+  ["vsubnE"] = {10955, 65024},
+  ["supnE"] = 10956,
+  ["supsetneqq"] = 10956,
+  ["varsupsetneqq"] = {10956, 65024},
+  ["vsupnE"] = {10956, 65024},
+  ["csub"] = 10959,
+  ["csup"] = 10960,
+  ["csube"] = 10961,
+  ["csupe"] = 10962,
+  ["subsup"] = 10963,
+  ["supsub"] = 10964,
+  ["subsub"] = 10965,
+  ["supsup"] = 10966,
+  ["suphsub"] = 10967,
+  ["supdsub"] = 10968,
+  ["forkv"] = 10969,
+  ["topfork"] = 10970,
+  ["mlcp"] = 10971,
+  ["Dashv"] = 10980,
+  ["DoubleLeftTee"] = 10980,
+  ["Vdashl"] = 10982,
+  ["Barv"] = 10983,
+  ["vBar"] = 10984,
+  ["vBarv"] = 10985,
+  ["Vbar"] = 10987,
+  ["Not"] = 10988,
+  ["bNot"] = 10989,
+  ["rnmid"] = 10990,
+  ["cirmid"] = 10991,
+  ["midcir"] = 10992,
+  ["topcir"] = 10993,
+  ["nhpar"] = 10994,
+  ["parsim"] = 10995,
+  ["nparsl"] = {11005, 8421},
+  ["parsl"] = 11005,
+  ["fflig"] = 64256,
+  ["filig"] = 64257,
+  ["fllig"] = 64258,
+  ["ffilig"] = 64259,
+  ["ffllig"] = 64260,
+  ["Ascr"] = 119964,
+  ["Cscr"] = 119966,
+  ["Dscr"] = 119967,
+  ["Gscr"] = 119970,
+  ["Jscr"] = 119973,
+  ["Kscr"] = 119974,
+  ["Nscr"] = 119977,
+  ["Oscr"] = 119978,
+  ["Pscr"] = 119979,
+  ["Qscr"] = 119980,
+  ["Sscr"] = 119982,
+  ["Tscr"] = 119983,
+  ["Uscr"] = 119984,
+  ["Vscr"] = 119985,
+  ["Wscr"] = 119986,
+  ["Xscr"] = 119987,
+  ["Yscr"] = 119988,
+  ["Zscr"] = 119989,
+  ["ascr"] = 119990,
+  ["bscr"] = 119991,
+  ["cscr"] = 119992,
+  ["dscr"] = 119993,
+  ["fscr"] = 119995,
+  ["hscr"] = 119997,
+  ["iscr"] = 119998,
+  ["jscr"] = 119999,
+  ["kscr"] = 120000,
+  ["lscr"] = 120001,
+  ["mscr"] = 120002,
+  ["nscr"] = 120003,
+  ["pscr"] = 120005,
+  ["qscr"] = 120006,
+  ["rscr"] = 120007,
+  ["sscr"] = 120008,
+  ["tscr"] = 120009,
+  ["uscr"] = 120010,
+  ["vscr"] = 120011,
+  ["wscr"] = 120012,
+  ["xscr"] = 120013,
+  ["yscr"] = 120014,
+  ["zscr"] = 120015,
+  ["Afr"] = 120068,
+  ["Bfr"] = 120069,
+  ["Dfr"] = 120071,
+  ["Efr"] = 120072,
+  ["Ffr"] = 120073,
+  ["Gfr"] = 120074,
+  ["Jfr"] = 120077,
+  ["Kfr"] = 120078,
+  ["Lfr"] = 120079,
+  ["Mfr"] = 120080,
+  ["Nfr"] = 120081,
+  ["Ofr"] = 120082,
+  ["Pfr"] = 120083,
+  ["Qfr"] = 120084,
+  ["Sfr"] = 120086,
+  ["Tfr"] = 120087,
+  ["Ufr"] = 120088,
+  ["Vfr"] = 120089,
+  ["Wfr"] = 120090,
+  ["Xfr"] = 120091,
+  ["Yfr"] = 120092,
+  ["afr"] = 120094,
+  ["bfr"] = 120095,
+  ["cfr"] = 120096,
+  ["dfr"] = 120097,
+  ["efr"] = 120098,
+  ["ffr"] = 120099,
+  ["gfr"] = 120100,
+  ["hfr"] = 120101,
+  ["ifr"] = 120102,
+  ["jfr"] = 120103,
+  ["kfr"] = 120104,
+  ["lfr"] = 120105,
+  ["mfr"] = 120106,
+  ["nfr"] = 120107,
+  ["ofr"] = 120108,
+  ["pfr"] = 120109,
+  ["qfr"] = 120110,
+  ["rfr"] = 120111,
+  ["sfr"] = 120112,
+  ["tfr"] = 120113,
+  ["ufr"] = 120114,
+  ["vfr"] = 120115,
+  ["wfr"] = 120116,
+  ["xfr"] = 120117,
+  ["yfr"] = 120118,
+  ["zfr"] = 120119,
+  ["Aopf"] = 120120,
+  ["Bopf"] = 120121,
+  ["Dopf"] = 120123,
+  ["Eopf"] = 120124,
+  ["Fopf"] = 120125,
+  ["Gopf"] = 120126,
+  ["Iopf"] = 120128,
+  ["Jopf"] = 120129,
+  ["Kopf"] = 120130,
+  ["Lopf"] = 120131,
+  ["Mopf"] = 120132,
+  ["Oopf"] = 120134,
+  ["Sopf"] = 120138,
+  ["Topf"] = 120139,
+  ["Uopf"] = 120140,
+  ["Vopf"] = 120141,
+  ["Wopf"] = 120142,
+  ["Xopf"] = 120143,
+  ["Yopf"] = 120144,
+  ["aopf"] = 120146,
+  ["bopf"] = 120147,
+  ["copf"] = 120148,
+  ["dopf"] = 120149,
+  ["eopf"] = 120150,
+  ["fopf"] = 120151,
+  ["gopf"] = 120152,
+  ["hopf"] = 120153,
+  ["iopf"] = 120154,
+  ["jopf"] = 120155,
+  ["kopf"] = 120156,
+  ["lopf"] = 120157,
+  ["mopf"] = 120158,
+  ["nopf"] = 120159,
+  ["oopf"] = 120160,
+  ["popf"] = 120161,
+  ["qopf"] = 120162,
+  ["ropf"] = 120163,
+  ["sopf"] = 120164,
+  ["topf"] = 120165,
+  ["uopf"] = 120166,
+  ["vopf"] = 120167,
+  ["wopf"] = 120168,
+  ["xopf"] = 120169,
+  ["yopf"] = 120170,
+  ["zopf"] = 120171,
+}
+function entities.dec_entity(s)
+  local n = tonumber(s)
+  if n == nil then
+    return "&#" .. s .. ";"  -- fallback for unknown entities
+  end
+  return unicode.utf8.char(n)
+end
+function entities.hex_entity(s)
+  local n = tonumber("0x"..s)
+  if n == nil then
+    return "&#x" .. s .. ";"  -- fallback for unknown entities
+  end
+  return unicode.utf8.char(n)
+end
+function entities.hex_entity_with_x_char(x, s)
+  local n = tonumber("0x"..s)
+  if n == nil then
+    return "&#" .. x .. s .. ";"  -- fallback for unknown entities
+  end
+  return unicode.utf8.char(n)
+end
+function entities.char_entity(s)
+  local code_points = character_entities[s]
+  if code_points == nil then
+    return "&" .. s .. ";"
+  end
+  if type(code_points) ~= 'table' then
+    code_points = {code_points}
+  end
+  local char_table = {}
+    for _, code_point in ipairs(code_points) do
+      table.insert(char_table, unicode.utf8.char(code_point))
+    end
+  return table.concat(char_table)
+end
+M.writer = {}
+function M.writer.new(options)
+  local self = {}
+  self.options = options
+  self.flatten_inlines = false
+  local slice_specifiers = {}
+  for specifier in options.slice:gmatch("[^%s]+") do
+    table.insert(slice_specifiers, specifier)
+  end
+
+  if #slice_specifiers == 2 then
+    self.slice_begin, self.slice_end = table.unpack(slice_specifiers)
+    local slice_begin_type = self.slice_begin:sub(1, 1)
+    if slice_begin_type ~= "^" and slice_begin_type ~= "$" then
+      self.slice_begin = "^" .. self.slice_begin
+    end
+    local slice_end_type = self.slice_end:sub(1, 1)
+    if slice_end_type ~= "^" and slice_end_type ~= "$" then
+      self.slice_end = "$" .. self.slice_end
+    end
+  elseif #slice_specifiers == 1 then
+    self.slice_begin = "^" .. slice_specifiers[1]
+    self.slice_end = "$" .. slice_specifiers[1]
+  end
+
+  self.slice_begin_type = self.slice_begin:sub(1, 1)
+  self.slice_begin_identifier = self.slice_begin:sub(2) or ""
+  self.slice_end_type = self.slice_end:sub(1, 1)
+  self.slice_end_identifier = self.slice_end:sub(2) or ""
+
+  if self.slice_begin == "^" and self.slice_end ~= "^" then
+    self.is_writing = true
+  else
+    self.is_writing = false
+  end
+  self.space = " "
+  self.nbsp = "\\markdownRendererNbsp{}"
+  function self.plain(s)
+    return s
+  end
+  function self.paragraph(s)
+    if not self.is_writing then return "" end
+    return s
+  end
+  self.interblocksep_text = "\\markdownRendererInterblockSeparator\n{}"
+  function self.interblocksep()
+    if not self.is_writing then return "" end
+    return self.interblocksep_text
+  end
+  self.paragraphsep_text = "\\markdownRendererParagraphSeparator\n{}"
+  function self.paragraphsep()
+    if not self.is_writing then return "" end
+    return self.paragraphsep_text
+  end
+  self.undosep_text = "\\markdownRendererUndoSeparator\n{}"
+  function self.undosep()
+    if not self.is_writing then return "" end
+    return self.undosep_text
+  end
+  self.soft_line_break = function()
+    if self.flatten_inlines then return "\n" end
+    return "\\markdownRendererSoftLineBreak\n{}"
+  end
+  self.hard_line_break = function()
+    if self.flatten_inlines then return "\n" end
+    return "\\markdownRendererHardLineBreak\n{}"
+  end
+  self.ellipsis = "\\markdownRendererEllipsis{}"
+  function self.thematic_break()
+    if not self.is_writing then return "" end
+    return "\\markdownRendererThematicBreak{}"
+  end
+  self.escaped_uri_chars = {
+    ["{"] = "\\markdownRendererLeftBrace{}",
+    ["}"] = "\\markdownRendererRightBrace{}",
+    ["\\"] = "\\markdownRendererBackslash{}",
+    ["\r"] = " ",
+    ["\n"] = " ",
+  }
+  self.escaped_minimal_strings = {
+    ["^^"] = "\\markdownRendererCircumflex"
+          .. "\\markdownRendererCircumflex ",
+    ["☒"] = "\\markdownRendererTickedBox{}",
+    ["⌛"] = "\\markdownRendererHalfTickedBox{}",
+    ["☐"] = "\\markdownRendererUntickedBox{}",
+    [entities.hex_entity('FFFD')]
+      = "\\markdownRendererReplacementCharacter{}",
+  }
+  self.escaped_strings = util.table_copy(self.escaped_minimal_strings)
+  self.escaped_strings[entities.hex_entity('00A0')] = self.nbsp
+  self.escaped_chars = {
+    ["{"] = "\\markdownRendererLeftBrace{}",
+    ["}"] = "\\markdownRendererRightBrace{}",
+    ["%"] = "\\markdownRendererPercentSign{}",
+    ["\\"] = "\\markdownRendererBackslash{}",
+    ["#"] = "\\markdownRendererHash{}",
+    ["$"] = "\\markdownRendererDollarSign{}",
+    ["&"] = "\\markdownRendererAmpersand{}",
+    ["_"] = "\\markdownRendererUnderscore{}",
+    ["^"] = "\\markdownRendererCircumflex{}",
+    ["~"] = "\\markdownRendererTilde{}",
+    ["|"] = "\\markdownRendererPipe{}",
+    [entities.hex_entity('0000')]
+      = "\\markdownRendererReplacementCharacter{}",
+  }
+  local function create_escaper(char_escapes, string_escapes)
+    local escape = util.escaper(char_escapes, string_escapes)
+    return function(s)
+      if self.flatten_inlines then return s end
+      return escape(s)
+    end
+  end
+  local escape_typographic_text = create_escaper(
+    self.escaped_chars, self.escaped_strings)
+  local escape_programmatic_text = create_escaper(
+    self.escaped_uri_chars, self.escaped_minimal_strings)
+  local escape_minimal = create_escaper(
+    {}, self.escaped_minimal_strings)
+  self.escape = escape_typographic_text
+  self.math = escape_minimal
+  if options.hybrid then
+    self.identifier = escape_minimal
+    self.string = escape_minimal
+    self.uri = escape_minimal
+    self.infostring = escape_minimal
+  else
+    self.identifier = escape_programmatic_text
+    self.string = escape_typographic_text
+    self.uri = escape_programmatic_text
+    self.infostring = escape_programmatic_text
+  end
+  function self.warning(t)
+    return {"\\markdownRendererWarning{", self.identifier(t), "}"}
+  end
+  function self.error(t)
+    return {"\\markdownRendererError{", self.identifier(t), "}"}
+  end
+  function self.code(s, attributes)
+    if self.flatten_inlines then return s end
+    local buf = {}
+    if attributes ~= nil then
+      table.insert(buf,
+                   "\\markdownRendererCodeSpanAttributeContextBegin\n")
+      table.insert(buf, self.attributes(attributes))
+    end
+    table.insert(buf,
+                 {"\\markdownRendererCodeSpan{", self.escape(s), "}"})
+    if attributes ~= nil then
+      table.insert(buf,
+                   "\\markdownRendererCodeSpanAttributeContextEnd{}")
+    end
+    return buf
+  end
+  function self.link(lab, src, tit, attributes)
+    if self.flatten_inlines then return lab end
+    local buf = {}
+    if attributes ~= nil then
+      table.insert(buf,
+                   "\\markdownRendererLinkAttributeContextBegin\n")
+      table.insert(buf, self.attributes(attributes))
+    end
+    table.insert(buf, {"\\markdownRendererLink{",lab,"}",
+                       "{",self.escape(src),"}",
+                       "{",self.uri(src),"}",
+                       "{",self.string(tit or ""),"}"})
+    if attributes ~= nil then
+      table.insert(buf,
+                   "\\markdownRendererLinkAttributeContextEnd{}")
+    end
+    return buf
+  end
+  function self.image(lab, src, tit, attributes)
+    if self.flatten_inlines then return lab end
+    local buf = {}
+    if attributes ~= nil then
+      table.insert(buf,
+                   "\\markdownRendererImageAttributeContextBegin\n")
+      table.insert(buf, self.attributes(attributes))
+    end
+    table.insert(buf, {"\\markdownRendererImage{",lab,"}",
+                       "{",self.string(src),"}",
+                       "{",self.uri(src),"}",
+                       "{",self.string(tit or ""),"}"})
+    if attributes ~= nil then
+      table.insert(buf,
+                   "\\markdownRendererImageAttributeContextEnd{}")
+    end
+    return buf
+  end
+  function self.bulletlist(items,tight)
+    if not self.is_writing then return "" end
+    local buffer = {}
+    for _,item in ipairs(items) do
+      if item ~= "" then
+        buffer[#buffer + 1] = self.bulletitem(item)
+      end
+    end
+    local contents = util.intersperse(buffer,"\n")
+    if tight and options.tightLists then
+      return {"\\markdownRendererUlBeginTight\n",contents,
+        "\n\\markdownRendererUlEndTight "}
+    else
+      return {"\\markdownRendererUlBegin\n",contents,
+        "\n\\markdownRendererUlEnd "}
+    end
+  end
+  function self.bulletitem(s)
+    return {"\\markdownRendererUlItem ",s,
+            "\\markdownRendererUlItemEnd "}
+  end
+  function self.orderedlist(items,tight,startnum)
+    if not self.is_writing then return "" end
+    local buffer = {}
+    local num = startnum
+    for _,item in ipairs(items) do
+      if item ~= "" then
+        buffer[#buffer + 1] = self.ordereditem(item,num)
+      end
+      if num ~= nil and item ~= "" then
+        num = num + 1
+      end
+    end
+    local contents = util.intersperse(buffer,"\n")
+    if tight and options.tightLists then
+      return {"\\markdownRendererOlBeginTight\n",contents,
+              "\n\\markdownRendererOlEndTight "}
+    else
+      return {"\\markdownRendererOlBegin\n",contents,
+              "\n\\markdownRendererOlEnd "}
+    end
+  end
+  function self.ordereditem(s,num)
+    if num ~= nil then
+      return {"\\markdownRendererOlItemWithNumber{",num,"}",s,
+              "\\markdownRendererOlItemEnd "}
+    else
+      return {"\\markdownRendererOlItem ",s,
+              "\\markdownRendererOlItemEnd "}
+    end
+  end
+  function self.inline_html_comment(contents)
+    if self.flatten_inlines then return contents end
+    return {"\\markdownRendererInlineHtmlComment{",contents,"}"}
+  end
+  function self.inline_html_tag(contents)
+    if self.flatten_inlines then return contents end
+    return {"\\markdownRendererInlineHtmlTag{",
+            self.string(contents),"}"}
+  end
+  function self.block_html_element(s)
+    if not self.is_writing then return "" end
+    local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim")
+    return {"\\markdownRendererInputBlockHtmlElement{",name,"}"}
+  end
+  function self.emphasis(s)
+    if self.flatten_inlines then return s end
+    return {"\\markdownRendererEmphasis{",s,"}"}
+  end
+  function self.tickbox(f)
+    if f == 1.0 then
+      return "☒ "
+    elseif f == 0.0 then
+      return "☐ "
+    else
+      return "⌛ "
+    end
+  end
+  function self.strong(s)
+    if self.flatten_inlines then return s end
+    return {"\\markdownRendererStrongEmphasis{",s,"}"}
+  end
+  function self.blockquote(s)
+    if not self.is_writing then return "" end
+    return {"\\markdownRendererBlockQuoteBegin\n",s,
+      "\\markdownRendererBlockQuoteEnd "}
+  end
+  function self.verbatim(s)
+    if not self.is_writing then return "" end
+    s = s:gsub("\n$", "")
+    local name = util.cache_verbatim(options.cacheDir, s)
+    return {"\\markdownRendererInputVerbatim{",name,"}"}
+  end
+  function self.document(d)
+    local buf = {"\\markdownRendererDocumentBegin\n", d}
+
+    -- pop all attributes
+    table.insert(buf, self.pop_attributes())
+
+    table.insert(buf, "\\markdownRendererDocumentEnd")
+
+    return buf
+  end
+  local seen_identifiers = {}
+  local key_value_regex = "([^= ]+)%s*=%s*(.*)"
+  local function normalize_attributes(attributes, auto_identifiers)
+    -- normalize attributes
+    local normalized_attributes = {}
+    local has_explicit_identifiers = false
+    local key, value
+    for _, attribute in ipairs(attributes or {}) do
+      if attribute:sub(1, 1) == "#" then
+        table.insert(normalized_attributes, attribute)
+        has_explicit_identifiers = true
+        seen_identifiers[attribute:sub(2)] = true
+      elseif attribute:sub(1, 1) == "." then
+        table.insert(normalized_attributes, attribute)
+      else
+        key, value = attribute:match(key_value_regex)
+        if key:lower() == "id" then
+          table.insert(normalized_attributes, "#" .. value)
+        elseif key:lower() == "class" then
+          local classes = {}
+          for class in value:gmatch("%S+") do
+            table.insert(classes, class)
+          end
+          table.sort(classes)
+          for _, class in ipairs(classes) do
+            table.insert(normalized_attributes, "." .. class)
+          end
+        else
+          table.insert(normalized_attributes, attribute)
+        end
+      end
+    end
+
+    -- if no explicit identifiers exist, add auto identifiers
+    if not has_explicit_identifiers and auto_identifiers ~= nil then
+      local seen_auto_identifiers = {}
+      for _, auto_identifier in ipairs(auto_identifiers) do
+        if seen_auto_identifiers[auto_identifier] == nil then
+          seen_auto_identifiers[auto_identifier] = true
+          if seen_identifiers[auto_identifier] == nil then
+            seen_identifiers[auto_identifier] = true
+            table.insert(normalized_attributes,
+                         "#" .. auto_identifier)
+          else
+            local auto_identifier_number = 1
+            while true do
+              local numbered_auto_identifier = auto_identifier .. "-"
+                                             .. auto_identifier_number
+              if seen_identifiers[numbered_auto_identifier] == nil then
+                seen_identifiers[numbered_auto_identifier] = true
+                table.insert(normalized_attributes,
+                             "#" .. numbered_auto_identifier)
+                break
+              end
+              auto_identifier_number = auto_identifier_number + 1
+            end
+          end
+        end
+      end
+    end
+
+    -- sort and deduplicate normalized attributes
+    table.sort(normalized_attributes)
+    local seen_normalized_attributes = {}
+    local deduplicated_normalized_attributes = {}
+    for _, attribute in ipairs(normalized_attributes) do
+      if seen_normalized_attributes[attribute] == nil then
+        seen_normalized_attributes[attribute] = true
+        table.insert(deduplicated_normalized_attributes, attribute)
+      end
+    end
+
+    return deduplicated_normalized_attributes
+  end
+
+  function self.attributes(attributes, should_normalize_attributes)
+    local normalized_attributes
+    if should_normalize_attributes == false then
+      normalized_attributes = attributes
+    else
+      normalized_attributes = normalize_attributes(attributes)
+    end
+
+    local buf = {}
+    local key, value
+    for _, attribute in ipairs(normalized_attributes) do
+      if attribute:sub(1, 1) == "#" then
+        table.insert(buf, {"\\markdownRendererAttributeIdentifier{",
+                           attribute:sub(2), "}"})
+      elseif attribute:sub(1, 1) == "." then
+        table.insert(buf, {"\\markdownRendererAttributeClassName{",
+                           attribute:sub(2), "}"})
+      else
+        key, value = attribute:match(key_value_regex)
+        table.insert(buf, {"\\markdownRendererAttributeKeyValue{",
+                           key, "}{", value, "}"})
+      end
+    end
+
+    return buf
+  end
+  self.active_attributes = {}
+  self.attribute_type_levels = {}
+  setmetatable(self.attribute_type_levels,
+               { __index = function() return 0 end })
+  local function apply_attributes()
+    local buf = {}
+    for i = 1, #self.active_attributes do
+      local start_output = self.active_attributes[i][3]
+      if start_output ~= nil then
+        table.insert(buf, start_output)
+      end
+    end
+    return buf
+  end
+
+  local function tear_down_attributes()
+    local buf = {}
+    for i = #self.active_attributes, 1, -1 do
+      local end_output = self.active_attributes[i][4]
+      if end_output ~= nil then
+        table.insert(buf, end_output)
+      end
+    end
+    return buf
+  end
+  function self.push_attributes(attribute_type, attributes,
+                                start_output, end_output)
+    local attribute_type_level
+      = self.attribute_type_levels[attribute_type]
+    self.attribute_type_levels[attribute_type]
+      = attribute_type_level + 1
+
+    -- index attributes in a hash table for easy lookup
+    attributes = attributes or {}
+    for i = 1, #attributes do
+      attributes[attributes[i]] = true
+    end
+
+    local buf = {}
+    -- handle slicing
+    if attributes["#" .. self.slice_end_identifier] ~= nil and
+       self.slice_end_type == "^" then
+      if self.is_writing then
+        table.insert(buf, self.undosep())
+        table.insert(buf, tear_down_attributes())
+      end
+      self.is_writing = false
+    end
+    if attributes["#" .. self.slice_begin_identifier] ~= nil and
+       self.slice_begin_type == "^" then
+      table.insert(buf, apply_attributes())
+      self.is_writing = true
+    end
+    if self.is_writing and start_output ~= nil then
+      table.insert(buf, start_output)
+    end
+    table.insert(self.active_attributes,
+                 {attribute_type, attributes,
+                  start_output, end_output})
+    return buf
+  end
+
+  function self.pop_attributes(attribute_type)
+    local buf = {}
+    -- pop attributes until we find attributes of correct type
+    -- or until no attributes remain
+    local current_attribute_type = false
+    while current_attribute_type ~= attribute_type and
+          #self.active_attributes > 0 do
+      local attributes, _, end_output
+      current_attribute_type, attributes, _, end_output = table.unpack(
+        self.active_attributes[#self.active_attributes])
+      local attribute_type_level
+        = self.attribute_type_levels[current_attribute_type]
+      self.attribute_type_levels[current_attribute_type]
+        = attribute_type_level - 1
+      if self.is_writing and end_output ~= nil then
+        table.insert(buf, end_output)
+      end
+      table.remove(self.active_attributes, #self.active_attributes)
+      -- handle slicing
+      if attributes["#" .. self.slice_end_identifier] ~= nil
+         and self.slice_end_type == "$" then
+        if self.is_writing then
+          table.insert(buf, self.undosep())
+          table.insert(buf, tear_down_attributes())
+        end
+        self.is_writing = false
+      end
+      if attributes["#" .. self.slice_begin_identifier] ~= nil and
+         self.slice_begin_type == "$" then
+        self.is_writing = true
+        table.insert(buf, apply_attributes())
+      end
+    end
+    return buf
+  end
+  local function create_auto_identifier(s)
+    local buffer = {}
+    local prev_space = false
+    local letter_found = false
+    local normalized_s = s
+    if not options.unicodeNormalization
+       or options.unicodeNormalizationForm ~= "nfc" then
+      normalized_s = uni_algos.normalize.NFC(normalized_s)
+    end
+
+    for _, code in utf8.codes(normalized_s) do
+      local char = utf8.char(code)
+
+      -- Remove everything up to the first letter.
+      if not letter_found then
+        local is_letter = unicode.utf8.match(char, "%a")
+        if is_letter then
+          letter_found = true
+        else
+          goto continue
+        end
+      end
+
+      -- Remove all non-alphanumeric characters, except underscores,
+      -- hyphens, and periods.
+      if not unicode.utf8.match(char, "[%w_%-%.%s]") then
+        goto continue
+      end
+
+      -- Replace all spaces and newlines with hyphens.
+      if unicode.utf8.match(char, "[%s\n]") then
+        char = "-"
+        if prev_space then
+          goto continue
+        else
+          prev_space = true
+        end
+      else
+        -- Convert all alphabetic characters to lowercase.
+        char = unicode.utf8.lower(char)
+        prev_space = false
+      end
+
+      table.insert(buffer, char)
+
+      ::continue::
+    end
+
+    if prev_space then
+      table.remove(buffer)
+    end
+
+    local identifier = #buffer == 0 and "section"
+                    or table.concat(buffer, "")
+    return identifier
+  end
+  local function create_gfm_auto_identifier(s)
+    local buffer = {}
+    local prev_space = false
+    local letter_found = false
+    local normalized_s = s
+    if not options.unicodeNormalization
+       or options.unicodeNormalizationForm ~= "nfc" then
+      normalized_s = uni_algos.normalize.NFC(normalized_s)
+    end
+
+    for _, code in utf8.codes(normalized_s) do
+      local char = utf8.char(code)
+
+      -- Remove everything up to the first non-space.
+      if not letter_found then
+        local is_letter = unicode.utf8.match(char, "%S")
+        if is_letter then
+          letter_found = true
+        else
+          goto continue
+        end
+      end
+
+      -- Remove all non-alphanumeric characters, except underscores
+      -- and hyphens.
+      if not unicode.utf8.match(char, "[%w_%-%s]") then
+        prev_space = false
+        goto continue
+      end
+
+      -- Replace all spaces and newlines with hyphens.
+      if unicode.utf8.match(char, "[%s\n]") then
+        char = "-"
+        if prev_space then
+          goto continue
+        else
+          prev_space = true
+        end
+      else
+        -- Convert all alphabetic characters to lowercase.
+        char = unicode.utf8.lower(char)
+        prev_space = false
+      end
+
+      table.insert(buffer, char)
+
+      ::continue::
+    end
+
+    if prev_space then
+      table.remove(buffer)
+    end
+
+    local identifier = #buffer == 0 and "section"
+                    or table.concat(buffer, "")
+    return identifier
+  end
+  self.secbegin_text = "\\markdownRendererSectionBegin\n"
+  self.secend_text = "\n\\markdownRendererSectionEnd "
+  function self.heading(s, level, attributes)
+    local buf = {}
+    local flat_text, inlines = table.unpack(s)
+
+    -- push empty attributes for implied sections
+    while self.attribute_type_levels["heading"] < level - 1 do
+      table.insert(buf,
+                   self.push_attributes("heading",
+                                        nil,
+                                        self.secbegin_text,
+                                        self.secend_text))
+    end
+
+    -- pop attributes for sections that have ended
+    while self.attribute_type_levels["heading"] >= level do
+      table.insert(buf, self.pop_attributes("heading"))
+    end
+
+    -- construct attributes for the new section
+    local auto_identifiers = {}
+    if self.options.autoIdentifiers then
+      table.insert(auto_identifiers, create_auto_identifier(flat_text))
+    end
+    if self.options.gfmAutoIdentifiers then
+      table.insert(auto_identifiers,
+                   create_gfm_auto_identifier(flat_text))
+    end
+    local normalized_attributes = normalize_attributes(attributes,
+                                                       auto_identifiers)
+
+    -- push attributes for the new section
+    local start_output = {}
+    local end_output = {}
+    table.insert(start_output, self.secbegin_text)
+    table.insert(end_output, self.secend_text)
+
+    table.insert(buf, self.push_attributes("heading",
+                                           normalized_attributes,
+                                           start_output,
+                                           end_output))
+    assert(self.attribute_type_levels["heading"] == level)
+
+    -- render the heading and its attributes
+    if self.is_writing and #normalized_attributes > 0 then
+      table.insert(buf,
+                   "\\markdownRendererHeaderAttributeContextBegin\n")
+      table.insert(buf, self.attributes(normalized_attributes, false))
+    end
+
+    local cmd
+    level = level + options.shiftHeadings
+    if level <= 1 then
+      cmd = "\\markdownRendererHeadingOne"
+    elseif level == 2 then
+      cmd = "\\markdownRendererHeadingTwo"
+    elseif level == 3 then
+      cmd = "\\markdownRendererHeadingThree"
+    elseif level == 4 then
+      cmd = "\\markdownRendererHeadingFour"
+    elseif level == 5 then
+      cmd = "\\markdownRendererHeadingFive"
+    elseif level >= 6 then
+      cmd = "\\markdownRendererHeadingSix"
+    else
+      cmd = ""
+    end
+    if self.is_writing then
+      table.insert(buf, {cmd, "{", inlines, "}"})
+    end
+
+    if self.is_writing and #normalized_attributes > 0 then
+      table.insert(buf, "\\markdownRendererHeaderAttributeContextEnd{}")
+    end
+
+    return buf
+  end
+  function self.get_state()
+    return {
+      is_writing=self.is_writing,
+      flatten_inlines=self.flatten_inlines,
+      active_attributes={table.unpack(self.active_attributes)},
+    }
+  end
+  function self.set_state(s)
+    local previous_state = self.get_state()
+    for key, value in pairs(s) do
+      self[key] = value
+    end
+    return previous_state
+  end
+  function self.defer_call(f)
+    local previous_state = self.get_state()
+    return function(...)
+      local state = self.set_state(previous_state)
+      local return_value = f(...)
+      self.set_state(state)
+      return return_value
+    end
+  end
+
+  return self
+end
+local parsers                  = {}
+parsers.percent                = P("%")
+parsers.at                     = P("@")
+parsers.comma                  = P(",")
+parsers.asterisk               = P("*")
+parsers.dash                   = P("-")
+parsers.plus                   = P("+")
+parsers.underscore             = P("_")
+parsers.period                 = P(".")
+parsers.hash                   = P("#")
+parsers.dollar                 = P("$")
+parsers.ampersand              = P("&")
+parsers.backtick               = P("`")
+parsers.less                   = P("<")
+parsers.more                   = P(">")
+parsers.space                  = P(" ")
+parsers.squote                 = P("'")
+parsers.dquote                 = P('"')
+parsers.lparent                = P("(")
+parsers.rparent                = P(")")
+parsers.lbracket               = P("[")
+parsers.rbracket               = P("]")
+parsers.lbrace                 = P("{")
+parsers.rbrace                 = P("}")
+parsers.circumflex             = P("^")
+parsers.slash                  = P("/")
+parsers.equal                  = P("=")
+parsers.colon                  = P(":")
+parsers.semicolon              = P(";")
+parsers.exclamation            = P("!")
+parsers.pipe                   = P("|")
+parsers.tilde                  = P("~")
+parsers.backslash              = P("\\")
+parsers.tab                    = P("\t")
+parsers.newline                = P("\n")
+
+parsers.digit                  = R("09")
+parsers.hexdigit               = R("09","af","AF")
+parsers.letter                 = R("AZ","az")
+parsers.alphanumeric           = R("AZ","az","09")
+parsers.keyword                = parsers.letter
+                               * (parsers.alphanumeric + parsers.dash)^0
+
+parsers.doubleasterisks        = P("**")
+parsers.doubleunderscores      = P("__")
+parsers.doubletildes           = P("~~")
+parsers.fourspaces             = P("    ")
+
+parsers.any                    = P(1)
+parsers.succeed                = P(true)
+parsers.fail                   = P(false)
+
+parsers.internal_punctuation   = S(":;,.?")
+parsers.ascii_punctuation      = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
+;(function()
+  local pathname = assert(kpse.find_file("UnicodeData.txt"),
+    [[Could not locate file "UnicodeData.txt"]])
+  local file = assert(io.open(pathname, "r"),
+    [[Could not open file "UnicodeData.txt"]])
+  local prefix_trees = {}
+  for line in file:lines() do
+    local codepoint, major_category = line:match("^(%x+);[^;]*;(%a)")
+    if major_category == "P" or major_category == "S" then
+      local code = unicode.utf8.char(tonumber(codepoint, 16))
+      if prefix_trees[#code] == nil then
+        prefix_trees[#code] = {}
+      end
+      local node = prefix_trees[#code]
+      for i = 1, #code do
+        local byte = code:sub(i, i)
+        if i < #code then
+          if node[byte] == nil then
+            node[byte] = {}
+          end
+          node = node[byte]
+        else
+          table.insert(node, byte)
+        end
+      end
+    end
+  end
+  assert(file:close())
+  local function depth_first_search(node, path, visit, leave)
+    visit(node, path)
+    for label, child in pairs(node) do
+      if type(child) == "table" then
+        depth_first_search(child, path .. label, visit, leave)
+      else
+        visit(child, path)
+      end
+    end
+    leave(node, path)
+  end
+
+  parsers.punctuation = {}
+  for length, prefix_tree in pairs(prefix_trees) do
+    local subparsers = {}
+    depth_first_search(prefix_tree, "", function(node, path)
+      if type(node) == "table" then
+        subparsers[path] = parsers.fail
+      else
+        assert(type(node) == "string")
+        subparsers[path] = subparsers[path] + S(node)
+      end
+    end, function(_, path)
+      if #path > 0 then
+        local byte = path:sub(#path, #path)
+        local parent_path = path:sub(1, #path-1)
+        subparsers[parent_path] = subparsers[parent_path]
+                                + S(byte) * subparsers[path]
+      else
+        parsers.punctuation[length] = subparsers[path]
+      end
+    end)
+    assert(parsers.punctuation[length] ~= nil)
+  end
+end)()
+
+parsers.escapable              = parsers.ascii_punctuation
+parsers.anyescaped             = parsers.backslash / ""
+                               * parsers.escapable
+                               + parsers.any
+
+parsers.spacechar              = S("\t ")
+parsers.spacing                = S(" \n\r\t")
+parsers.nonspacechar           = parsers.any - parsers.spacing
+parsers.optionalspace          = parsers.spacechar^0
+
+parsers.normalchar             = parsers.any - (V("SpecialChar")
+                                                + parsers.spacing)
+parsers.eof                    = -parsers.any
+parsers.nonindentspace         = parsers.space^-3 * - parsers.spacechar
+parsers.indent                 = parsers.space^-3 * parsers.tab
+                               + parsers.fourspaces / ""
+parsers.linechar               = P(1 - parsers.newline)
+
+parsers.blankline              = parsers.optionalspace
+                               * parsers.newline / "\n"
+parsers.blanklines             = parsers.blankline^0
+parsers.skipblanklines         = ( parsers.optionalspace
+                                 * parsers.newline)^0
+parsers.indentedline           = parsers.indent    /""
+                               * C( parsers.linechar^1
+                                  * parsers.newline^-1)
+parsers.optionallyindentedline = parsers.indent^-1 /""
+                               * C( parsers.linechar^1
+                                  * parsers.newline^-1)
+parsers.sp                     = parsers.spacing^0
+parsers.spnl                   = parsers.optionalspace
+                               * ( parsers.newline
+                                 * parsers.optionalspace)^-1
+parsers.line                   = parsers.linechar^0 * parsers.newline
+parsers.nonemptyline           = parsers.line - parsers.blankline
+
+parsers.leader      = parsers.space^-3
+
+local function has_trail(indent_table)
+  return indent_table ~= nil and
+    indent_table.trail ~= nil and
+    next(indent_table.trail) ~= nil
+end
+
+local function has_indents(indent_table)
+  return indent_table ~= nil and
+    indent_table.indents ~= nil and
+    next(indent_table.indents) ~= nil
+end
+
+local function add_trail(indent_table, trail_info)
+  indent_table.trail = trail_info
+  return indent_table
+end
+
+local function remove_trail(indent_table)
+  indent_table.trail = nil
+  return indent_table
+end
+
+local function update_indent_table(indent_table, new_indent, add)
+  indent_table = remove_trail(indent_table)
+
+  if not has_indents(indent_table) then
+    indent_table.indents = {}
+  end
+
+  if add then
+    indent_table.indents[#indent_table.indents + 1] = new_indent
+  else
+    if indent_table.indents[#indent_table.indents].name
+       == new_indent.name then
+      indent_table.indents[#indent_table.indents] = nil
+    end
+  end
+
+  return indent_table
+end
+
+local function remove_indent(name)
+  local remove_indent_level =
+    function(s, i, indent_table) -- luacheck: ignore s i
+      indent_table = update_indent_table(indent_table, {name=name},
+                                         false)
+      return true, indent_table
+    end
+
+  return Cg(Cmt(Cb("indent_info"), remove_indent_level), "indent_info")
+end
+
+local function process_starter_spacing(indent, spacing,
+                                       minimum, left_strip_length)
+  left_strip_length = left_strip_length or 0
+
+  local count = 0
+  local tab_value = 4 - (indent) % 4
+
+  local code_started, minimum_found = false, false
+  local code_start, minimum_remainder = "", ""
+
+  local left_total_stripped = 0
+  local full_remainder = ""
+
+  if spacing ~= nil then
+    for i = 1, #spacing do
+      local character = spacing:sub(i, i)
+
+      if character == "\t" then
+        count = count + tab_value
+        tab_value = 4
+      elseif character == " " then
+        count = count + 1
+        tab_value = 4 - (1 - tab_value) % 4
+      end
+
+      if (left_strip_length ~= 0) then
+        local possible_to_strip = math.min(count, left_strip_length)
+        count = count - possible_to_strip
+        left_strip_length = left_strip_length - possible_to_strip
+        left_total_stripped = left_total_stripped + possible_to_strip
+      else
+        full_remainder =  full_remainder .. character
+      end
+
+      if (minimum_found) then
+        minimum_remainder = minimum_remainder .. character
+      elseif (count >= minimum) then
+        minimum_found = true
+        minimum_remainder = minimum_remainder
+                         .. string.rep(" ", count - minimum)
+      end
+
+      if (code_started) then
+        code_start = code_start .. character
+      elseif (count >= minimum + 4) then
+        code_started = true
+        code_start = code_start
+                  .. string.rep(" ", count - (minimum + 4))
+      end
+    end
+  end
+
+  local remainder
+  if (code_started) then
+    remainder = code_start
+  else
+    remainder = string.rep(" ", count - minimum)
+  end
+
+  local is_minimum = count >= minimum
+  return {
+    is_code = code_started,
+    remainder = remainder,
+    left_total_stripped = left_total_stripped,
+    is_minimum = is_minimum,
+    minimum_remainder = minimum_remainder,
+    total_length = count,
+    full_remainder = full_remainder
+  }
+end
+
+local function count_indent_tab_level(indent_table)
+  local count = 0
+  if not has_indents(indent_table) then
+    return count
+  end
+
+  for i=1, #indent_table.indents do
+    count = count + indent_table.indents[i].length
+  end
+  return count
+end
+
+local function total_delimiter_length(delimiter)
+  local count = 0
+  if type(delimiter) == "string" then return #delimiter end
+  for _, value in pairs(delimiter) do
+    count = count + total_delimiter_length(value)
+  end
+  return count
+end
+
+local function process_starter_indent(_, _, indent_table, starter,
+                                      is_blank, indent_type, breakable)
+  local last_trail = starter[1]
+  local delimiter = starter[2]
+  local raw_new_trail = starter[3]
+
+  if indent_type == "bq" and not breakable then
+    indent_table.ignore_blockquote_blank = true
+  end
+
+  if has_trail(indent_table) then
+    local trail = indent_table.trail
+    if trail.is_code then
+      return false
+    end
+    last_trail = trail.remainder
+  else
+    local sp = process_starter_spacing(0, last_trail, 0, 0)
+
+    if sp.is_code then
+      return false
+    end
+    last_trail = sp.remainder
+  end
+
+  local preceding_indentation = count_indent_tab_level(indent_table) % 4
+  local last_trail_length = #last_trail
+  local delimiter_length = total_delimiter_length(delimiter)
+
+  local total_indent_level = preceding_indentation + last_trail_length
+                           + delimiter_length
+
+  local sp = {}
+  if not is_blank then
+    sp = process_starter_spacing(total_indent_level, raw_new_trail,
+                                 0, 1)
+  end
+
+  local del_trail_length = sp.left_total_stripped
+  if is_blank then
+    del_trail_length = 1
+  elseif not sp.is_code then
+    del_trail_length = del_trail_length + #sp.remainder
+  end
+
+  local indent_length = last_trail_length + delimiter_length
+                      + del_trail_length
+  local new_indent_info = {name=indent_type, length=indent_length}
+
+  indent_table = update_indent_table(indent_table, new_indent_info,
+                                     true)
+  indent_table = add_trail(indent_table,
+                           {is_code=sp.is_code,
+                            remainder=sp.remainder,
+                            total_length=sp.total_length,
+                            full_remainder=sp.full_remainder})
+
+  return true, indent_table
+end
+
+local function decode_pattern(name)
+  local delimeter = parsers.succeed
+  if name == "bq" then
+    delimeter = parsers.more
+  end
+
+  return C(parsers.optionalspace) * C(delimeter)
+       * C(parsers.optionalspace) * Cp()
+end
+
+local function left_blank_starter(indent_table)
+  local blank_starter_index
+
+  if not has_indents(indent_table) then
+    return
+  end
+
+  for i = #indent_table.indents,1,-1 do
+    local value = indent_table.indents[i]
+    if value.name == "li" then
+      blank_starter_index = i
+    else
+      break
+    end
+  end
+
+  return blank_starter_index
+end
+
+local function traverse_indent(s, i, indent_table, is_optional,
+                               is_blank, current_line_indents)
+  local new_index = i
+
+  local preceding_indentation = 0
+  local current_trail = {}
+
+  local blank_starter = left_blank_starter(indent_table)
+
+  if current_line_indents == nil then
+    current_line_indents = {}
+  end
+
+  for index = 1,#indent_table.indents do
+    local value = indent_table.indents[index]
+    local pattern = decode_pattern(value.name)
+
+    -- match decoded pattern
+    local new_indent_info = lpeg.match(Ct(pattern), s, new_index)
+    if new_indent_info == nil then
+      local blankline_end = lpeg.match(
+        Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index)
+      if is_optional or not indent_table.ignore_blockquote_blank
+         or not blankline_end then
+        return is_optional, new_index, current_trail,
+               current_line_indents
+      end
+
+      return traverse_indent(s, tonumber(blankline_end.pos),
+                             indent_table, is_optional, is_blank,
+                             current_line_indents)
+    end
+
+    local raw_last_trail = new_indent_info[1]
+    local delimiter = new_indent_info[2]
+    local raw_new_trail = new_indent_info[3]
+    local next_index = new_indent_info[4]
+
+    local space_only = delimiter == ""
+
+    -- check previous trail
+    if not space_only and next(current_trail) == nil then
+      local sp = process_starter_spacing(0, raw_last_trail, 0, 0)
+      current_trail = {is_code=sp.is_code, remainder=sp.remainder,
+                       total_length=sp.total_length,
+                       full_remainder=sp.full_remainder}
+    end
+
+    if next(current_trail) ~= nil then
+      if not space_only and current_trail.is_code then
+        return is_optional, new_index, current_trail,
+               current_line_indents
+      end
+      if current_trail.internal_remainder ~= nil then
+        raw_last_trail = current_trail.internal_remainder
+      end
+    end
+
+    local raw_last_trail_length = 0
+    local delimiter_length = 0
+
+    if not space_only then
+      delimiter_length = #delimiter
+      raw_last_trail_length = #raw_last_trail
+    end
+
+    local total_indent_level = preceding_indentation
+                             + raw_last_trail_length + delimiter_length
+
+    local spacing_to_process
+    local minimum = 0
+    local left_strip_length = 0
+
+    if not space_only then
+      spacing_to_process = raw_new_trail
+      left_strip_length = 1
+    else
+      spacing_to_process = raw_last_trail
+      minimum = value.length
+    end
+
+    local sp = process_starter_spacing(total_indent_level,
+                                       spacing_to_process, minimum,
+                                       left_strip_length)
+
+    if space_only and not sp.is_minimum then
+      return is_optional or (is_blank and blank_starter <= index),
+             new_index, current_trail, current_line_indents
+    end
+
+    local indent_length = raw_last_trail_length + delimiter_length
+                        + sp.left_total_stripped
+
+    -- update info for the next pattern
+    if not space_only then
+      preceding_indentation = preceding_indentation + indent_length
+    else
+      preceding_indentation = preceding_indentation + value.length
+    end
+
+    current_trail = {is_code=sp.is_code, remainder=sp.remainder,
+                     internal_remainder=sp.minimum_remainder,
+                     total_length=sp.total_length,
+                     full_remainder=sp.full_remainder}
+
+    current_line_indents[#current_line_indents + 1] = new_indent_info
+    new_index = next_index
+  end
+
+  return true, new_index, current_trail, current_line_indents
+end
+
+local function check_trail(expect_code, is_code)
+  return (expect_code and is_code) or (not expect_code and not is_code)
+end
+
+local check_trail_joined =
+  function(s, i, indent_table, -- luacheck: ignore s i
+           spacing, expect_code, omit_remainder)
+    local is_code
+    local remainder
+
+    if has_trail(indent_table) then
+      local trail = indent_table.trail
+      is_code = trail.is_code
+      if is_code then
+        remainder = trail.remainder
+      else
+        remainder = trail.full_remainder
+      end
+    else
+      local sp = process_starter_spacing(0, spacing, 0, 0)
+      is_code = sp.is_code
+      if is_code then
+        remainder = sp.remainder
+      else
+        remainder = sp.full_remainder
+      end
+    end
+
+    local result = check_trail(expect_code, is_code)
+    if omit_remainder then
+      return result
+    end
+    return result, remainder
+  end
+
+local check_trail_length =
+  function(s, i, indent_table, -- luacheck: ignore s i
+           spacing, min, max)
+    local trail
+
+    if has_trail(indent_table) then
+      trail = indent_table.trail
+    else
+      trail = process_starter_spacing(0, spacing, 0, 0)
+    end
+
+    local total_length = trail.total_length
+    if total_length == nil then
+      return false
+    end
+
+    return min <= total_length and total_length <= max
+  end
+
+local function check_continuation_indentation(s, i, indent_table,
+                                              is_optional, is_blank)
+  if not has_indents(indent_table) then
+    return true
+  end
+
+  local passes, new_index, current_trail, current_line_indents =
+    traverse_indent(s, i, indent_table, is_optional, is_blank)
+
+  if passes then
+    indent_table.current_line_indents = current_line_indents
+    indent_table = add_trail(indent_table, current_trail)
+    return new_index, indent_table
+  end
+  return false
+end
+
+local function get_last_indent_name(indent_table)
+  if has_indents(indent_table) then
+    return indent_table.indents[#indent_table.indents].name
+  end
+end
+
+local function remove_remainder_if_blank(indent_table, remainder)
+  if get_last_indent_name(indent_table) == "li" then
+    return ""
+  end
+  return remainder
+end
+
+local check_trail_type =
+  function(s, i, -- luacheck: ignore s i
+           trail, spacing, trail_type)
+    if trail == nil then
+      trail = process_starter_spacing(0, spacing, 0, 0)
+    end
+
+    if trail_type == "non-code" then
+      return check_trail(false, trail.is_code)
+    end
+    if trail_type == "code" then
+      return check_trail(true, trail.is_code)
+    end
+    if trail_type == "full-code" then
+      if (trail.is_code) then
+        return i, trail.remainder
+      end
+      return i, ""
+    end
+    if trail_type == "full-any" then
+      return i, trail.internal_remainder
+    end
+  end
+
+local trail_freezing =
+  function(s, i, -- luacheck: ignore s i
+           indent_table, is_freezing)
+    if is_freezing then
+      if indent_table.is_trail_frozen then
+        indent_table.trail = indent_table.frozen_trail
+      else
+        indent_table.frozen_trail = indent_table.trail
+        indent_table.is_trail_frozen = true
+      end
+    else
+      indent_table.frozen_trail = nil
+      indent_table.is_trail_frozen = false
+    end
+    return true, indent_table
+  end
+
+local check_continuation_indentation_and_trail =
+  function (s, i, indent_table, is_optional, is_blank, trail_type,
+            reset_rem, omit_remainder)
+    if not has_indents(indent_table) then
+      local spacing, new_index = lpeg.match( C(parsers.spacechar^0)
+                                           * Cp(), s, i)
+      local result, remainder = check_trail_type(s, i,
+        indent_table.trail, spacing, trail_type)
+      if remainder == nil then
+        if result then
+          return new_index
+        end
+        return false
+      end
+      if result then
+        return new_index, remainder
+      end
+      return false
+    end
+
+    local passes, new_index, current_trail = traverse_indent(s, i,
+      indent_table, is_optional, is_blank)
+
+    if passes then
+      local spacing
+      if current_trail == nil then
+        local newer_spacing, newer_index = lpeg.match(
+          C(parsers.spacechar^0) * Cp(), s, i)
+        current_trail = process_starter_spacing(0, newer_spacing, 0, 0)
+        new_index = newer_index
+        spacing = newer_spacing
+      else
+        spacing = current_trail.remainder
+      end
+      local result, remainder = check_trail_type(s, new_index,
+        current_trail, spacing, trail_type)
+      if remainder == nil or omit_remainder then
+        if result then
+          return new_index
+        end
+        return false
+      end
+
+      if is_blank and reset_rem then
+        remainder = remove_remainder_if_blank(indent_table, remainder)
+      end
+      if result then
+        return new_index, remainder
+      end
+      return false
+    end
+    return false
+  end
+
+parsers.check_trail = Cmt( Cb("indent_info") * C(parsers.spacechar^0)
+                         * Cc(false), check_trail_joined)
+
+parsers.check_trail_no_rem = Cmt( Cb("indent_info")
+                                * C(parsers.spacechar^0) * Cc(false)
+                                * Cc(true), check_trail_joined)
+
+parsers.check_code_trail  = Cmt( Cb("indent_info")
+                               * C(parsers.spacechar^0)
+                               * Cc(true), check_trail_joined)
+
+parsers.check_trail_length_range  = function(min, max)
+  return Cmt( Cb("indent_info") * C(parsers.spacechar^0) * Cc(min)
+            * Cc(max), check_trail_length)
+end
+
+parsers.check_trail_length = function(n)
+  return parsers.check_trail_length_range(n, n)
+end
+
+parsers.freeze_trail = Cg( Cmt(Cb("indent_info")
+                         * Cc(true), trail_freezing), "indent_info")
+
+parsers.unfreeze_trail = Cg(Cmt(Cb("indent_info") * Cc(false),
+                            trail_freezing), "indent_info")
+
+parsers.check_minimal_indent = Cmt(Cb("indent_info") * Cc(false),
+                                   check_continuation_indentation)
+
+parsers.check_optional_indent = Cmt(Cb("indent_info") * Cc(true),
+                                    check_continuation_indentation)
+
+parsers.check_minimal_blank_indent
+  = Cmt( Cb("indent_info") * Cc(false)
+       * Cc(true)
+       , check_continuation_indentation)
+
+
+parsers.check_minimal_indent_and_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(false) * Cc("non-code") * Cc(true)
+     , check_continuation_indentation_and_trail)
+
+parsers.check_minimal_indent_and_code_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(false) * Cc("code") * Cc(false)
+     , check_continuation_indentation_and_trail)
+
+parsers.check_minimal_blank_indent_and_full_code_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(true) * Cc("full-code") * Cc(true)
+     , check_continuation_indentation_and_trail)
+
+parsers.check_minimal_indent_and_any_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false)
+     , check_continuation_indentation_and_trail)
+
+parsers.check_minimal_blank_indent_and_any_trail =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false)
+     , check_continuation_indentation_and_trail)
+
+parsers.check_minimal_blank_indent_and_any_trail_no_rem =
+  Cmt( Cb("indent_info")
+     * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(true)
+     , check_continuation_indentation_and_trail)
+
+parsers.check_optional_indent_and_any_trail =
+  Cmt( Cb("indent_info")
+     * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false)
+     , check_continuation_indentation_and_trail)
+
+parsers.check_optional_blank_indent_and_any_trail =
+  Cmt( Cb("indent_info")
+     * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false)
+     , check_continuation_indentation_and_trail)
+
+
+parsers.spnlc_noexc = parsers.optionalspace
+                    * ( parsers.newline
+                      * parsers.check_minimal_indent_and_any_trail)^-1
+
+parsers.spnlc = parsers.optionalspace
+              * (V("EndlineNoSub"))^-1
+
+parsers.spnlc_sep  = parsers.optionalspace * V("EndlineNoSub")
+                   + parsers.spacechar^1
+
+parsers.only_blank = parsers.spacechar^0
+                   * (parsers.newline + parsers.eof)
+
+parsers.commented_line_letter  = parsers.linechar
+                               + parsers.newline
+                               - parsers.backslash
+                               - parsers.percent
+parsers.commented_line = Cg(Cc(""), "backslashes")
+                       * ((#(parsers.commented_line_letter
+                            - parsers.newline)
+                          * Cb("backslashes")
+                          * Cs(parsers.commented_line_letter
+                            - parsers.newline)^1  -- initial
+                          * Cg(Cc(""), "backslashes"))
+                         + #(parsers.backslash * parsers.backslash)
+                         * Cg((parsers.backslash  -- even backslash
+                              * parsers.backslash)^1, "backslashes")
+                         + (parsers.backslash
+                           * (#parsers.percent
+                             * Cb("backslashes")
+                             / function(backslashes)
+                               return string.rep("\\", #backslashes / 2)
+                             end
+                             * C(parsers.percent)
+                             + #parsers.commented_line_letter
+                             * Cb("backslashes")
+                             * Cc("\\")
+                             * C(parsers.commented_line_letter))
+                           * Cg(Cc(""), "backslashes")))^0
+                       * (#parsers.percent
+                         * Cb("backslashes")
+                         / function(backslashes)
+                           return string.rep("\\", #backslashes / 2)
+                         end
+                         * ((parsers.percent  -- comment
+                            * parsers.line
+                            * #parsers.blankline) -- blank line
+                           / "\n"
+                           + parsers.percent  -- comment
+                           * parsers.line
+                           * parsers.optionalspace)  -- leading spaces
+                         + #(parsers.newline)
+                         * Cb("backslashes")
+                         * C(parsers.newline))
+
+parsers.chunk = parsers.line * (parsers.optionallyindentedline
+                                       - parsers.blankline)^0
+
+parsers.attribute_key_char = parsers.alphanumeric + S("-_:.")
+parsers.attribute_raw_char = parsers.alphanumeric + S("-_")
+parsers.attribute_key = (parsers.attribute_key_char
+                        - parsers.dash - parsers.digit)
+                      * parsers.attribute_key_char^0
+parsers.attribute_value = ( (parsers.dquote / "")
+                          * (parsers.anyescaped - parsers.dquote)^0
+                          * (parsers.dquote / ""))
+                        + ( (parsers.squote / "")
+                          * (parsers.anyescaped - parsers.squote)^0
+                          * (parsers.squote / ""))
+                        + ( parsers.anyescaped
+                          - parsers.dquote
+                          - parsers.rbrace
+                          - parsers.space)^0
+parsers.attribute_identifier = parsers.attribute_key_char^1
+parsers.attribute_classname = parsers.letter
+                            * parsers.attribute_key_char^0
+parsers.attribute_raw = parsers.attribute_raw_char^1
+
+parsers.attribute = (parsers.dash * Cc(".unnumbered"))
+                  + C( parsers.hash
+                     * parsers.attribute_identifier)
+                  + C( parsers.period
+                     * parsers.attribute_classname)
+                  + Cs( parsers.attribute_key
+                      * parsers.optionalspace
+                      * parsers.equal
+                      * parsers.optionalspace
+                      * parsers.attribute_value)
+parsers.attributes = parsers.lbrace
+                   * parsers.optionalspace
+                   * parsers.attribute
+                   * (parsers.spacechar^1
+                     * parsers.attribute)^0
+                   * parsers.optionalspace
+                   * parsers.rbrace
+
+parsers.raw_attribute = parsers.lbrace
+                      * parsers.optionalspace
+                      * parsers.equal
+                      * C(parsers.attribute_raw)
+                      * parsers.optionalspace
+                      * parsers.rbrace
+
+-- block followed by 0 or more optionally
+-- indented blocks with first line indented.
+parsers.indented_blocks = function(bl)
+  return Cs( bl
+         * ( parsers.blankline^1
+           * parsers.indent
+           * -parsers.blankline
+           * bl)^0
+         * (parsers.blankline^1 + parsers.eof) )
+end
+local function repeat_between(pattern, min, max)
+  return -pattern^(max + 1) * pattern^min
+end
+
+parsers.hexentity = parsers.ampersand * parsers.hash * C(S("Xx"))
+                  * C(repeat_between(parsers.hexdigit, 1, 6))
+                  * parsers.semicolon
+parsers.decentity = parsers.ampersand * parsers.hash
+                  * C(repeat_between(parsers.digit, 1, 7))
+                  * parsers.semicolon
+parsers.tagentity = parsers.ampersand * C(parsers.alphanumeric^1)
+                  * parsers.semicolon
+
+parsers.html_entities
+  = parsers.hexentity / entities.hex_entity_with_x_char
+  + parsers.decentity / entities.dec_entity
+  + parsers.tagentity / entities.char_entity
+parsers.bullet = function(bullet_char, interrupting)
+  local allowed_end
+  if interrupting then
+    allowed_end = C(parsers.spacechar^1) * #parsers.linechar
+  else
+    allowed_end = C(parsers.spacechar^1)
+                + #(parsers.newline + parsers.eof)
+  end
+  return parsers.check_trail
+        * Ct(C(bullet_char) * Cc(""))
+        * allowed_end
+end
+
+local function tickbox(interior)
+  return parsers.optionalspace * parsers.lbracket
+       * interior * parsers.rbracket * parsers.spacechar^1
+end
+
+parsers.ticked_box = tickbox(S("xX")) * Cc(1.0)
+parsers.halfticked_box = tickbox(S("./")) * Cc(0.5)
+parsers.unticked_box = tickbox(parsers.spacechar^1) * Cc(0.0)
+
+parsers.openticks   = Cg(parsers.backtick^1, "ticks")
+
+local function captures_equal_length(_,i,a,b)
+  return #a == #b and i
+end
+
+parsers.closeticks  = Cmt(C(parsers.backtick^1)
+                          * Cb("ticks"), captures_equal_length)
+
+parsers.intickschar = (parsers.any - S("\n\r`"))
+                    + V("NoSoftLineBreakEndline")
+                    + (parsers.backtick^1 - parsers.closeticks)
+
+local function process_inticks(s)
+  s = s:gsub("\n", " ")
+  s = s:gsub("^ (.*) $", "%1")
+  return s
+end
+
+parsers.inticks = parsers.openticks
+                * C(parsers.space^0)
+                * parsers.closeticks
+                + parsers.openticks
+                * Cs(Cs(parsers.intickschar^0) / process_inticks)
+                * parsers.closeticks
+
+-- case-insensitive match (we assume s is lowercase)
+-- must be single byte encoding
+parsers.keyword_exact = function(s)
+  local parser = P(0)
+  for i=1,#s do
+    local c = s:sub(i,i)
+    local m = c .. upper(c)
+    parser = parser * S(m)
+  end
+  return parser
+end
+
+parsers.special_block_keyword =
+    parsers.keyword_exact("pre") +
+    parsers.keyword_exact("script") +
+    parsers.keyword_exact("style") +
+    parsers.keyword_exact("textarea")
+
+parsers.block_keyword =
+    parsers.keyword_exact("address") +
+    parsers.keyword_exact("article") +
+    parsers.keyword_exact("aside") +
+    parsers.keyword_exact("base") +
+    parsers.keyword_exact("basefont") +
+    parsers.keyword_exact("blockquote") +
+    parsers.keyword_exact("body") +
+    parsers.keyword_exact("caption") +
+    parsers.keyword_exact("center") +
+    parsers.keyword_exact("col") +
+    parsers.keyword_exact("colgroup") +
+    parsers.keyword_exact("dd") +
+    parsers.keyword_exact("details") +
+    parsers.keyword_exact("dialog") +
+    parsers.keyword_exact("dir") +
+    parsers.keyword_exact("div") +
+    parsers.keyword_exact("dl") +
+    parsers.keyword_exact("dt") +
+    parsers.keyword_exact("fieldset") +
+    parsers.keyword_exact("figcaption") +
+    parsers.keyword_exact("figure") +
+    parsers.keyword_exact("footer") +
+    parsers.keyword_exact("form") +
+    parsers.keyword_exact("frame") +
+    parsers.keyword_exact("frameset") +
+    parsers.keyword_exact("h1") +
+    parsers.keyword_exact("h2") +
+    parsers.keyword_exact("h3") +
+    parsers.keyword_exact("h4") +
+    parsers.keyword_exact("h5") +
+    parsers.keyword_exact("h6") +
+    parsers.keyword_exact("head") +
+    parsers.keyword_exact("header") +
+    parsers.keyword_exact("hr") +
+    parsers.keyword_exact("html") +
+    parsers.keyword_exact("iframe") +
+    parsers.keyword_exact("legend") +
+    parsers.keyword_exact("li") +
+    parsers.keyword_exact("link") +
+    parsers.keyword_exact("main") +
+    parsers.keyword_exact("menu") +
+    parsers.keyword_exact("menuitem") +
+    parsers.keyword_exact("nav") +
+    parsers.keyword_exact("noframes") +
+    parsers.keyword_exact("ol") +
+    parsers.keyword_exact("optgroup") +
+    parsers.keyword_exact("option") +
+    parsers.keyword_exact("p") +
+    parsers.keyword_exact("param") +
+    parsers.keyword_exact("section") +
+    parsers.keyword_exact("source") +
+    parsers.keyword_exact("summary") +
+    parsers.keyword_exact("table") +
+    parsers.keyword_exact("tbody") +
+    parsers.keyword_exact("td") +
+    parsers.keyword_exact("tfoot") +
+    parsers.keyword_exact("th") +
+    parsers.keyword_exact("thead") +
+    parsers.keyword_exact("title") +
+    parsers.keyword_exact("tr") +
+    parsers.keyword_exact("track") +
+    parsers.keyword_exact("ul")
+
+-- end conditions
+parsers.html_blankline_end_condition
+  = parsers.linechar^0
+  * ( parsers.newline
+    * (parsers.check_minimal_blank_indent_and_any_trail
+      * #parsers.blankline
+      + parsers.check_minimal_indent_and_any_trail)
+    * parsers.linechar^1)^0
+  * (parsers.newline^-1 / "")
+
+local function remove_trailing_blank_lines(s)
+  return s:gsub("[\n\r]+%s*$", "")
+end
+
+parsers.html_until_end = function(end_marker)
+  return Cs(Cs((parsers.newline
+          * (parsers.check_minimal_blank_indent_and_any_trail
+            * #parsers.blankline
+            + parsers.check_minimal_indent_and_any_trail)
+          + parsers.linechar - end_marker)^0
+          * parsers.linechar^0 * parsers.newline^-1)
+         / remove_trailing_blank_lines)
+end
+
+-- attributes
+parsers.html_attribute_spacing  = parsers.optionalspace
+                                * V("NoSoftLineBreakEndline")
+                                * parsers.optionalspace
+                                + parsers.spacechar^1
+
+parsers.html_attribute_name = ( parsers.letter
+                              + parsers.colon
+                              + parsers.underscore)
+                            * ( parsers.alphanumeric
+                              + parsers.colon
+                              + parsers.underscore
+                            + parsers.period
+                            + parsers.dash)^0
+
+parsers.html_attribute_value  = parsers.squote
+                              * (parsers.linechar - parsers.squote)^0
+                              * parsers.squote
+                              + parsers.dquote
+                              * (parsers.linechar - parsers.dquote)^0
+                              * parsers.dquote
+                              + ( parsers.any
+                                - parsers.spacechar
+                                - parsers.newline
+                                - parsers.dquote
+                                - parsers.squote
+                                - parsers.backtick
+                                - parsers.equal
+                                - parsers.less
+                                - parsers.more)^1
+
+parsers.html_inline_attribute_value = parsers.squote
+                                    * (V("NoSoftLineBreakEndline")
+                                      + parsers.any
+                                      - parsers.blankline^2
+                                      - parsers.squote)^0
+                                    * parsers.squote
+                                    + parsers.dquote
+                                    * (V("NoSoftLineBreakEndline")
+                                      + parsers.any
+                                      - parsers.blankline^2
+                                      - parsers.dquote)^0
+                                    * parsers.dquote
+                                    + (parsers.any
+                                      - parsers.spacechar
+                                      - parsers.newline
+                                      - parsers.dquote
+                                      - parsers.squote
+                                      - parsers.backtick
+                                      - parsers.equal
+                                      - parsers.less
+                                      - parsers.more)^1
+
+parsers.html_attribute_value_specification
+  = parsers.optionalspace
+  * parsers.equal
+  * parsers.optionalspace
+  * parsers.html_attribute_value
+
+parsers.html_spnl = parsers.optionalspace
+                  * (V("NoSoftLineBreakEndline")
+                  * parsers.optionalspace)^-1
+
+parsers.html_inline_attribute_value_specification
+  = parsers.html_spnl
+  * parsers.equal
+  * parsers.html_spnl
+  * parsers.html_inline_attribute_value
+
+parsers.html_attribute
+  = parsers.html_attribute_spacing
+  * parsers.html_attribute_name
+  * parsers.html_inline_attribute_value_specification^-1
+
+parsers.html_non_newline_attribute
+  = parsers.spacechar^1
+  * parsers.html_attribute_name
+  * parsers.html_attribute_value_specification^-1
+
+parsers.nested_breaking_blank = parsers.newline
+                              * parsers.check_minimal_blank_indent
+                              * parsers.blankline
+
+parsers.html_comment_start = P("<!--")
+
+parsers.html_comment_end = P("-->")
+
+parsers.html_comment
+  = Cs( parsers.html_comment_start
+      * parsers.html_until_end(parsers.html_comment_end))
+
+parsers.html_inline_comment = (parsers.html_comment_start / "")
+                            * -P(">") * -P("->")
+                            * Cs(( V("NoSoftLineBreakEndline")
+                                 + parsers.any
+                                 - parsers.nested_breaking_blank
+                                 - parsers.html_comment_end)^0)
+                            * (parsers.html_comment_end / "")
+
+parsers.html_cdatasection_start = P("<![CDATA[")
+
+parsers.html_cdatasection_end = P("]]>")
+
+parsers.html_cdatasection
+  = Cs( parsers.html_cdatasection_start
+      * parsers.html_until_end(parsers.html_cdatasection_end))
+
+parsers.html_inline_cdatasection
+  = parsers.html_cdatasection_start
+  * Cs(V("NoSoftLineBreakEndline") + parsers.any
+      - parsers.nested_breaking_blank - parsers.html_cdatasection_end)^0
+  * parsers.html_cdatasection_end
+
+parsers.html_declaration_start = P("<!") * parsers.letter
+
+parsers.html_declaration_end = P(">")
+
+parsers.html_declaration
+  = Cs( parsers.html_declaration_start
+      * parsers.html_until_end(parsers.html_declaration_end))
+
+parsers.html_inline_declaration
+  = parsers.html_declaration_start
+  * Cs(V("NoSoftLineBreakEndline") + parsers.any
+      - parsers.nested_breaking_blank - parsers.html_declaration_end)^0
+  * parsers.html_declaration_end
+
+parsers.html_instruction_start = P("<?")
+
+parsers.html_instruction_end = P("?>")
+
+parsers.html_instruction
+  = Cs( parsers.html_instruction_start
+      * parsers.html_until_end(parsers.html_instruction_end))
+
+parsers.html_inline_instruction = parsers.html_instruction_start
+                                * Cs( V("NoSoftLineBreakEndline")
+                                    + parsers.any
+                                    - parsers.nested_breaking_blank
+                                    - parsers.html_instruction_end)^0
+                                * parsers.html_instruction_end
+
+parsers.html_blankline  = parsers.newline
+                        * parsers.optionalspace
+                        * parsers.newline
+
+parsers.html_tag_start = parsers.less
+
+parsers.html_tag_closing_start  = parsers.less
+                                * parsers.slash
+
+parsers.html_tag_end  = parsers.html_spnl
+                      * parsers.more
+
+parsers.html_empty_tag_end  = parsers.html_spnl
+                            * parsers.slash
+                            * parsers.more
+
+-- opening tags
+parsers.html_any_open_inline_tag  = parsers.html_tag_start
+                                  * parsers.keyword
+                                  * parsers.html_attribute^0
+                                  * parsers.html_tag_end
+
+parsers.html_any_open_tag = parsers.html_tag_start
+                          * parsers.keyword
+                          * parsers.html_non_newline_attribute^0
+                          * parsers.html_tag_end
+
+parsers.html_open_tag = parsers.html_tag_start
+                      * parsers.block_keyword
+                      * parsers.html_attribute^0
+                      * parsers.html_tag_end
+
+parsers.html_open_special_tag = parsers.html_tag_start
+                              * parsers.special_block_keyword
+                              * parsers.html_attribute^0
+                              * parsers.html_tag_end
+
+-- incomplete tags
+parsers.incomplete_tag_following  = parsers.spacechar
+                                  + parsers.more
+                                  + parsers.slash * parsers.more
+                                  + #(parsers.newline + parsers.eof)
+
+parsers.incomplete_special_tag_following = parsers.spacechar
+                                         + parsers.more
+                                         + #( parsers.newline
+                                            + parsers.eof)
+
+parsers.html_incomplete_open_tag  = parsers.html_tag_start
+                                  * parsers.block_keyword
+                                  * parsers.incomplete_tag_following
+
+parsers.html_incomplete_open_special_tag
+  = parsers.html_tag_start
+  * parsers.special_block_keyword
+  * parsers.incomplete_special_tag_following
+
+parsers.html_incomplete_close_tag = parsers.html_tag_closing_start
+                                  * parsers.block_keyword
+                                  * parsers.incomplete_tag_following
+
+parsers.html_incomplete_close_special_tag
+  = parsers.html_tag_closing_start
+  * parsers.special_block_keyword
+  * parsers.incomplete_tag_following
+
+-- closing tags
+parsers.html_close_tag  = parsers.html_tag_closing_start
+                        * parsers.block_keyword
+                        * parsers.html_tag_end
+
+parsers.html_any_close_tag  = parsers.html_tag_closing_start
+                            * parsers.keyword
+                            * parsers.html_tag_end
+
+parsers.html_close_special_tag = parsers.html_tag_closing_start
+                               * parsers.special_block_keyword
+                               * parsers.html_tag_end
+
+-- empty tags
+parsers.html_any_empty_inline_tag = parsers.html_tag_start
+                                  * parsers.keyword
+                                  * parsers.html_attribute^0
+                                  * parsers.html_empty_tag_end
+
+parsers.html_any_empty_tag  = parsers.html_tag_start
+                            * parsers.keyword
+                            * parsers.html_non_newline_attribute^0
+                            * parsers.optionalspace
+                            * parsers.slash
+                            * parsers.more
+
+parsers.html_empty_tag  = parsers.html_tag_start
+                        * parsers.block_keyword
+                        * parsers.html_attribute^0
+                        * parsers.html_empty_tag_end
+
+parsers.html_empty_special_tag  = parsers.html_tag_start
+                                * parsers.special_block_keyword
+                                * parsers.html_attribute^0
+                                * parsers.html_empty_tag_end
+
+parsers.html_incomplete_blocks
+  = parsers.html_incomplete_open_tag
+  + parsers.html_incomplete_open_special_tag
+  + parsers.html_incomplete_close_tag
+
+-- parse special html blocks
+parsers.html_blankline_ending_special_block_opening
+  = ( parsers.html_close_special_tag
+    + parsers.html_empty_special_tag)
+  * #( parsers.optionalspace
+     * (parsers.newline + parsers.eof))
+
+parsers.html_blankline_ending_special_block
+  = parsers.html_blankline_ending_special_block_opening
+  * parsers.html_blankline_end_condition
+
+parsers.html_special_block_opening
+  = parsers.html_incomplete_open_special_tag
+  - parsers.html_empty_special_tag
+
+parsers.html_closing_special_block
+  = parsers.html_special_block_opening
+  * parsers.html_until_end(parsers.html_close_special_tag)
+
+parsers.html_special_block
+  = parsers.html_blankline_ending_special_block
+  + parsers.html_closing_special_block
+
+-- parse html blocks
+parsers.html_block_opening  = parsers.html_incomplete_open_tag
+                            + parsers.html_incomplete_close_tag
+
+parsers.html_block  = parsers.html_block_opening
+                    * parsers.html_blankline_end_condition
+
+-- parse any html blocks
+parsers.html_any_block_opening
+  = ( parsers.html_any_open_tag
+    + parsers.html_any_close_tag
+    + parsers.html_any_empty_tag)
+  * #(parsers.optionalspace * (parsers.newline + parsers.eof))
+
+parsers.html_any_block  = parsers.html_any_block_opening
+                        * parsers.html_blankline_end_condition
+
+parsers.html_inline_comment_full  = parsers.html_comment_start
+                                  * -P(">") * -P("->")
+                                  * Cs(( V("NoSoftLineBreakEndline")
+                                       + parsers.any - P("--")
+                                       - parsers.nested_breaking_blank
+                                       - parsers.html_comment_end)^0)
+                                  * parsers.html_comment_end
+
+parsers.html_inline_tags  = parsers.html_inline_comment_full
+                          + parsers.html_any_empty_inline_tag
+                          + parsers.html_inline_instruction
+                          + parsers.html_inline_cdatasection
+                          + parsers.html_inline_declaration
+                          + parsers.html_any_open_inline_tag
+                          + parsers.html_any_close_tag
+
+parsers.urlchar = parsers.anyescaped
+                - parsers.newline
+                - parsers.more
+
+parsers.auto_link_scheme_part = parsers.alphanumeric
+                              + parsers.plus
+                              + parsers.period
+                              + parsers.dash
+
+parsers.auto_link_scheme  = parsers.letter
+                          * parsers.auto_link_scheme_part
+                          * parsers.auto_link_scheme_part^-30
+
+parsers.absolute_uri  = parsers.auto_link_scheme * parsers.colon
+                      * ( parsers.any - parsers.spacing
+                        - parsers.less - parsers.more)^0
+
+parsers.printable_characters = S(".!#$%&'*+/=?^_`{|}~-")
+
+parsers.email_address_local_part_char = parsers.alphanumeric
+                                      + parsers.printable_characters
+
+parsers.email_address_local_part
+  = parsers.email_address_local_part_char^1
+
+parsers.email_address_dns_label = parsers.alphanumeric
+                                * ( parsers.alphanumeric
+                                  + parsers.dash)^-62
+                                * B(parsers.alphanumeric)
+
+parsers.email_address_domain  = parsers.email_address_dns_label
+                              * ( parsers.period
+                                * parsers.email_address_dns_label)^0
+
+parsers.email_address = parsers.email_address_local_part
+                      * parsers.at
+                      * parsers.email_address_domain
+
+parsers.auto_link_url = parsers.less
+                      * C(parsers.absolute_uri)
+                      * parsers.more
+
+parsers.auto_link_email = parsers.less
+                        * C(parsers.email_address)
+                        * parsers.more
+
+parsers.auto_link_relative_reference = parsers.less
+                                    * C(parsers.urlchar^1)
+                                    * parsers.more
+
+parsers.autolink  = parsers.auto_link_url
+                  + parsers.auto_link_email
+
+-- content in balanced brackets, parentheses, or quotes:
+parsers.bracketed   = P{ parsers.lbracket
+                       * (( parsers.backslash / "" * parsers.rbracket
+                          + parsers.any - (parsers.lbracket
+                                          + parsers.rbracket
+                                          + parsers.blankline^2)
+                          ) + V(1))^0
+                       * parsers.rbracket }
+
+parsers.inparens    = P{ parsers.lparent
+                       * ((parsers.anyescaped - (parsers.lparent
+                                                + parsers.rparent
+                                                + parsers.blankline^2)
+                          ) + V(1))^0
+                       * parsers.rparent }
+
+parsers.squoted     = P{ parsers.squote * parsers.alphanumeric
+                       * ((parsers.anyescaped - (parsers.squote
+                                                + parsers.blankline^2)
+                          ) + V(1))^0
+                       * parsers.squote }
+
+parsers.dquoted     = P{ parsers.dquote * parsers.alphanumeric
+                       * ((parsers.anyescaped - (parsers.dquote
+                                                + parsers.blankline^2)
+                          ) + V(1))^0
+                       * parsers.dquote }
+
+parsers.link_text  = parsers.lbracket
+                    * Cs((parsers.alphanumeric^1
+                        + parsers.bracketed
+                        + parsers.inticks
+                        + parsers.autolink
+                        + V("InlineHtml")
+                        + ( parsers.backslash * parsers.backslash)
+                        + ( parsers.backslash
+                          * ( parsers.lbracket
+                            + parsers.rbracket)
+                          + V("NoSoftLineBreakSpace")
+                          + V("NoSoftLineBreakEndline")
+                          + (parsers.any
+                            - ( parsers.newline
+                              + parsers.lbracket
+                              + parsers.rbracket
+                              + parsers.blankline^2))))^0)
+                    * parsers.rbracket
+
+parsers.link_label_body = -#(parsers.sp * parsers.rbracket)
+                        * #( ( parsers.any
+                             - parsers.rbracket)^-999
+                           * parsers.rbracket)
+                        * Cs((parsers.alphanumeric^1
+                            + parsers.inticks
+                            + parsers.autolink
+                            + V("InlineHtml")
+                            + ( parsers.backslash * parsers.backslash)
+                            + ( parsers.backslash
+                              * ( parsers.lbracket
+                                + parsers.rbracket)
+                              + V("NoSoftLineBreakSpace")
+                              + V("NoSoftLineBreakEndline")
+                              + (parsers.any
+                                - ( parsers.newline
+                                  + parsers.lbracket
+                                  + parsers.rbracket
+                                  + parsers.blankline^2))))^1)
+
+parsers.link_label  = parsers.lbracket
+                    * parsers.link_label_body
+                    * parsers.rbracket
+
+parsers.inparens_url  = P{ parsers.lparent
+                         * ((parsers.anyescaped - (parsers.lparent
+                                                  + parsers.rparent
+                                                  + parsers.spacing)
+                            ) + V(1))^0
+                         * parsers.rparent }
+
+-- url for markdown links, allowing nested brackets:
+parsers.url         = parsers.less * Cs((parsers.anyescaped
+                                        - parsers.newline
+                                        - parsers.less
+                                        - parsers.more)^0)
+                                   * parsers.more
+                    + -parsers.less
+                    * Cs((parsers.inparens_url + (parsers.anyescaped
+                                                 - parsers.spacing
+                                                 - parsers.lparent
+                                                 - parsers.rparent))^1)
+
+-- quoted text:
+parsers.title_s     = parsers.squote
+                    * Cs((parsers.html_entities
+                         + V("NoSoftLineBreakSpace")
+                         + V("NoSoftLineBreakEndline")
+                         + ( parsers.anyescaped
+                           - parsers.newline
+                           - parsers.squote
+                           - parsers.blankline^2))^0)
+                    * parsers.squote
+
+parsers.title_d     = parsers.dquote
+                    * Cs((parsers.html_entities
+                         + V("NoSoftLineBreakSpace")
+                         + V("NoSoftLineBreakEndline")
+                         + ( parsers.anyescaped
+                           - parsers.newline
+                           - parsers.dquote
+                           - parsers.blankline^2))^0)
+                    * parsers.dquote
+
+parsers.title_p     = parsers.lparent
+                    * Cs((parsers.html_entities
+                         + V("NoSoftLineBreakSpace")
+                         + V("NoSoftLineBreakEndline")
+                         + ( parsers.anyescaped
+                           - parsers.newline
+                           - parsers.lparent
+                           - parsers.rparent
+                           - parsers.blankline^2))^0)
+                    * parsers.rparent
+
+parsers.title
+  = parsers.title_d + parsers.title_s + parsers.title_p
+
+parsers.optionaltitle
+  = parsers.spnlc * parsers.title * parsers.spacechar^0 + Cc("")
+
+-- parse a reference definition:  [foo]: /bar "title"
+parsers.define_reference_parser = (parsers.check_trail / "")
+                                * parsers.link_label * parsers.colon
+                                * parsers.spnlc * parsers.url
+                                * ( parsers.spnlc_sep * parsers.title
+                                  * parsers.only_blank
+                                  + Cc("") * parsers.only_blank)
+parsers.Inline         = V("Inline")
+
+-- parse many p between starter and ender
+parsers.between = function(p, starter, ender)
+  local ender2 = B(parsers.nonspacechar) * ender
+  return ( starter
+         * #parsers.nonspacechar
+         * Ct(p * (p - ender2)^0)
+         * ender2)
+end
+
+parsers.lineof = function(c)
+    return ( parsers.check_trail_no_rem
+           * (P(c) * parsers.optionalspace)^3
+           * (parsers.newline + parsers.eof))
+end
+
+parsers.thematic_break_lines = parsers.lineof(parsers.asterisk)
+                             + parsers.lineof(parsers.dash)
+                             + parsers.lineof(parsers.underscore)
+-- parse Atx heading start and return level
+parsers.heading_start = #parsers.hash * C(parsers.hash^-6)
+                      * -parsers.hash / length
+
+-- parse setext header ending and return level
+parsers.heading_level
+  = parsers.nonindentspace * parsers.equal^1
+  * parsers.optionalspace * #parsers.newline * Cc(1)
+  + parsers.nonindentspace * parsers.dash^1
+  * parsers.optionalspace * #parsers.newline * Cc(2)
+
+local function strip_atx_end(s)
+  return s:gsub("%s+#*%s*\n$","")
+end
+
+parsers.atx_heading = parsers.check_trail_no_rem
+                    * Cg(parsers.heading_start, "level")
+                    * (C( parsers.optionalspace
+                        * parsers.hash^0
+                        * parsers.optionalspace
+                        * parsers.newline)
+                      + parsers.spacechar^1
+                      * C(parsers.line))
+M.reader = {}
+function M.reader.new(writer, options)
+  local self = {}
+  self.writer = writer
+  self.options = options
+  self.parsers = {}
+  (function(parsers)
+    setmetatable(self.parsers, {
+      __index = function (_, key)
+        return parsers[key]
+      end
+    })
+  end)(parsers)
+  local parsers = self.parsers
+  function self.normalize_tag(tag)
+    tag = util.rope_to_string(tag)
+    tag = tag:gsub("[ \n\r\t]+", " ")
+    tag = tag:gsub("^ ", ""):gsub(" $", "")
+    tag = uni_algos.case.casefold(tag, true, false)
+    return tag
+  end
+  local function iterlines(s, f)
+    local rope = lpeg.match(Ct((parsers.line / f)^1), s)
+    return util.rope_to_string(rope)
+  end
+  if options.preserveTabs then
+    self.expandtabs = function(s) return s end
+  else
+    self.expandtabs = function(s)
+                        if s:find("\t") then
+                          return iterlines(s, util.expand_tabs_in_line)
+                        else
+                          return s
+                        end
+                      end
+  end
+  self.parser_functions = {}
+  self.create_parser = function(name, grammar, toplevel)
+    self.parser_functions[name] = function(str)
+      if toplevel and options.stripIndent then
+          local min_prefix_length, min_prefix = nil, ''
+          str = iterlines(str, function(line)
+              if lpeg.match(parsers.nonemptyline, line) == nil then
+                  return line
+              end
+              line = util.expand_tabs_in_line(line)
+              local prefix = lpeg.match(C(parsers.optionalspace), line)
+              local prefix_length = #prefix
+              local is_shorter = min_prefix_length == nil
+              if not is_shorter then
+                is_shorter = prefix_length < min_prefix_length
+              end
+              if is_shorter then
+                min_prefix_length, min_prefix = prefix_length, prefix
+              end
+              return line
+          end)
+          str = str:gsub('^' .. min_prefix, '')
+      end
+      if toplevel and (options.texComments or options.hybrid) then
+        str = lpeg.match(Ct(parsers.commented_line^1), str)
+        str = util.rope_to_string(str)
+      end
+      local res = lpeg.match(grammar(), str)
+      if res == nil then
+        return writer.error(format("%s failed on:\n%s",
+                                   name, str:sub(1,20)))
+      else
+        return res
+      end
+    end
+  end
+
+  self.create_parser("parse_blocks",
+                     function()
+                       return parsers.blocks
+                     end, true)
+
+  self.create_parser("parse_blocks_nested",
+                     function()
+                       return parsers.blocks_nested
+                     end, false)
+
+  self.create_parser("parse_inlines",
+                     function()
+                       return parsers.inlines
+                     end, false)
+
+  self.create_parser("parse_inlines_no_inline_note",
+                     function()
+                       return parsers.inlines_no_inline_note
+                     end, false)
+
+  self.create_parser("parse_inlines_no_html",
+                     function()
+                       return parsers.inlines_no_html
+                     end, false)
+
+  self.create_parser("parse_inlines_nbsp",
+                     function()
+                       return parsers.inlines_nbsp
+                     end, false)
+  self.create_parser("parse_inlines_no_link_or_emphasis",
+                    function()
+                      return parsers.inlines_no_link_or_emphasis
+                    end, false)
+  parsers.minimally_indented_blankline
+    = parsers.check_minimal_indent * (parsers.blankline / "")
+
+  parsers.minimally_indented_block
+    = parsers.check_minimal_indent * V("Block")
+
+  parsers.minimally_indented_block_or_paragraph
+    = parsers.check_minimal_indent * V("BlockOrParagraph")
+
+  parsers.minimally_indented_paragraph
+    = parsers.check_minimal_indent * V("Paragraph")
+
+  parsers.minimally_indented_plain
+    = parsers.check_minimal_indent * V("Plain")
+
+  parsers.minimally_indented_par_or_plain
+    = parsers.minimally_indented_paragraph
+    + parsers.minimally_indented_plain
+
+  parsers.minimally_indented_par_or_plain_no_blank
+    = parsers.minimally_indented_par_or_plain
+    - parsers.minimally_indented_blankline
+
+  parsers.minimally_indented_ref
+    = parsers.check_minimal_indent * V("Reference")
+
+  parsers.minimally_indented_blank
+    = parsers.check_minimal_indent * V("Blank")
+
+  parsers.conditionally_indented_blankline
+    = parsers.check_minimal_blank_indent * (parsers.blankline / "")
+
+  parsers.minimally_indented_ref_or_block
+    = parsers.minimally_indented_ref
+    + parsers.minimally_indented_block
+    - parsers.minimally_indented_blankline
+
+  parsers.minimally_indented_ref_or_block_or_par
+    = parsers.minimally_indented_ref
+    + parsers.minimally_indented_block_or_paragraph
+    - parsers.minimally_indented_blankline
+
+
+  function parsers.separator_loop(separated_block, paragraph,
+                                  block_separator, paragraph_separator)
+    return  separated_block
+          + block_separator
+            * paragraph
+            * separated_block
+          + paragraph_separator
+          * paragraph
+  end
+
+  function parsers.create_loop_body_pair(separated_block, paragraph,
+                                         block_separator,
+                                         paragraph_separator)
+    return {
+      block = parsers.separator_loop(separated_block, paragraph,
+                                     block_separator, block_separator),
+      par = parsers.separator_loop(separated_block, paragraph,
+                                   block_separator, paragraph_separator)
+    }
+  end
+
+  parsers.block_sep_group = function(blank)
+    return  blank^0 * parsers.eof
+          + ( blank^2 / writer.paragraphsep
+            + blank^0 / writer.interblocksep
+            )
+  end
+
+  parsers.par_sep_group = function(blank)
+    return  blank^0 * parsers.eof
+          + blank^0 / writer.paragraphsep
+  end
+
+  parsers.sep_group_no_output = function(blank)
+    return  blank^0 * parsers.eof
+          + blank^0
+  end
+
+  parsers.content_blank = parsers.minimally_indented_blankline
+
+  parsers.ref_or_block_separated
+    = parsers.sep_group_no_output(parsers.content_blank)
+    * ( parsers.minimally_indented_ref
+      - parsers.content_blank)
+    + parsers.block_sep_group(parsers.content_blank)
+    * ( parsers.minimally_indented_block
+      - parsers.content_blank)
+
+  parsers.loop_body_pair  =
+    parsers.create_loop_body_pair(
+      parsers.ref_or_block_separated,
+      parsers.minimally_indented_par_or_plain_no_blank,
+      parsers.block_sep_group(parsers.content_blank),
+      parsers.par_sep_group(parsers.content_blank))
+
+  parsers.content_loop  = ( V("Block")
+                          * parsers.loop_body_pair.block^0
+                          + (V("Paragraph") + V("Plain"))
+                          * parsers.ref_or_block_separated
+                          * parsers.loop_body_pair.block^0
+                          + (V("Paragraph") + V("Plain"))
+                          * parsers.loop_body_pair.par^0)
+                        * parsers.content_blank^0
+
+  parsers.indented_content = function()
+    return  Ct( (V("Reference") + (parsers.blankline / ""))
+              * parsers.content_blank^0
+              * parsers.check_minimal_indent
+              * parsers.content_loop
+              + (V("Reference") + (parsers.blankline / ""))
+              * parsers.content_blank^0
+              + parsers.content_loop)
+  end
+
+  parsers.add_indent = function(pattern, name, breakable)
+    return  Cg(Cmt( Cb("indent_info")
+                  * Ct(pattern)
+                  * ( #parsers.linechar  -- check if starter is blank
+                    * Cc(false) + Cc(true))
+                  * Cc(name)
+                  * Cc(breakable),
+              process_starter_indent), "indent_info")
+  end
+
+  if options.hashEnumerators then
+    parsers.dig = parsers.digit + parsers.hash
+  else
+    parsers.dig = parsers.digit
+  end
+
+  parsers.enumerator = function(delimiter_type, interrupting)
+    local delimiter_range
+    local allowed_end
+    if interrupting then
+      delimiter_range = P("1")
+      allowed_end = C(parsers.spacechar^1) * #parsers.linechar
+    else
+      delimiter_range = parsers.dig * parsers.dig^-8
+      allowed_end = C(parsers.spacechar^1)
+                  + #(parsers.newline + parsers.eof)
+    end
+
+    return parsers.check_trail
+             * Ct(C(delimiter_range) * C(delimiter_type))
+             * allowed_end
+  end
+
+  parsers.starter = parsers.bullet(parsers.dash)
+                  + parsers.bullet(parsers.asterisk)
+                  + parsers.bullet(parsers.plus)
+                  + parsers.enumerator(parsers.period)
+                  + parsers.enumerator(parsers.rparent)
+
+  parsers.blockquote_start
+    = parsers.check_trail
+    * C(parsers.more)
+    * C(parsers.spacechar^0)
+
+  parsers.blockquote_body
+    = parsers.add_indent(parsers.blockquote_start, "bq", true)
+    * parsers.indented_content()
+    * remove_indent("bq")
+
+  if not options.breakableBlockquotes then
+    parsers.blockquote_body
+      = parsers.add_indent(parsers.blockquote_start, "bq", false)
+      * parsers.indented_content()
+      * remove_indent("bq")
+  end
+  local function parse_content_part(content_part)
+    local rope = util.rope_to_string(content_part)
+    local parsed
+      = self.parser_functions.parse_inlines_no_link_or_emphasis(rope)
+    parsed.indent_info = nil
+    return parsed
+  end
+
+  local collect_emphasis_content =
+    function(t, opening_index, closing_index)
+      local content = {}
+
+      local content_part = {}
+      for i = opening_index, closing_index do
+        local value = t[i]
+
+        if value.rendered ~= nil then
+          content[#content + 1] = parse_content_part(content_part)
+          content_part = {}
+          content[#content + 1] = value.rendered
+          value.rendered = nil
+        else
+          if value.type == "delimiter"
+              and value.element == "emphasis" then
+            if value.is_active then
+              content_part[#content_part + 1]
+                = string.rep(value.character, value.current_count)
+            end
+          else
+            content_part[#content_part + 1] = value.content
+          end
+          value.content = ''
+          value.is_active = false
+        end
+      end
+
+      if next(content_part) ~= nil then
+        content[#content + 1] = parse_content_part(content_part)
+      end
+
+      return content
+    end
+
+  local function fill_emph(t, opening_index, closing_index)
+    local content
+      = collect_emphasis_content(t, opening_index + 1,
+                                 closing_index - 1)
+    t[opening_index + 1].is_active = true
+    t[opening_index + 1].rendered = writer.emphasis(content)
+  end
+
+  local function fill_strong(t, opening_index, closing_index)
+    local content
+      = collect_emphasis_content(t, opening_index + 1,
+                                 closing_index - 1)
+    t[opening_index + 1].is_active = true
+    t[opening_index + 1].rendered = writer.strong(content)
+  end
+
+  local function breaks_three_rule(opening_delimiter, closing_delimiter)
+    return ( opening_delimiter.is_closing
+          or closing_delimiter.is_opening)
+       and (( opening_delimiter.original_count
+            + closing_delimiter.original_count) % 3 == 0)
+       and ( opening_delimiter.original_count % 3 ~= 0
+          or closing_delimiter.original_count % 3 ~= 0)
+  end
+
+  local find_emphasis_opener = function(t, bottom_index, latest_index,
+                                        character, closing_delimiter)
+    for i = latest_index, bottom_index, -1 do
+      local value = t[i]
+      if value.is_active and
+         value.is_opening and
+         value.type == "delimiter" and
+         value.element == "emphasis" and
+         (value.character == character) and
+         (value.current_count > 0) then
+        if not breaks_three_rule(value, closing_delimiter) then
+          return i
+        end
+      end
+    end
+  end
+
+  local function process_emphasis(t, opening_index, closing_index)
+    for i = opening_index, closing_index do
+      local value = t[i]
+      if value.type == "delimiter" and value.element == "emphasis" then
+          local delimiter_length = string.len(value.content)
+          value.character = string.sub(value.content, 1, 1)
+          value.current_count = delimiter_length
+          value.original_count = delimiter_length
+      end
+    end
+
+    local openers_bottom = {
+      ['*'] = {
+        [true] = {opening_index, opening_index, opening_index},
+        [false] = {opening_index, opening_index, opening_index}
+      },
+      ['_'] = {
+        [true] = {opening_index, opening_index, opening_index},
+        [false] = {opening_index, opening_index, opening_index}
+      }
+    }
+
+    local current_position = opening_index
+    local max_position = closing_index
+
+    while current_position <= max_position do
+      local value = t[current_position]
+
+      if value.type ~= "delimiter" or
+        value.element ~= "emphasis" or
+        not value.is_active or
+        not value.is_closing or
+        (value.current_count <= 0) then
+        current_position = current_position + 1
+        goto continue
+      end
+
+      local character = value.character
+      local is_opening = value.is_opening
+      local closing_length_modulo_three = value.original_count % 3
+
+      local current_openers_bottom
+        = openers_bottom[character][is_opening]
+                        [closing_length_modulo_three + 1]
+
+      local opener_position
+        = find_emphasis_opener(t, current_openers_bottom,
+                               current_position - 1, character, value)
+
+      if (opener_position == nil) then
+        openers_bottom[character][is_opening]
+                      [closing_length_modulo_three + 1]
+          = current_position
+        current_position = current_position + 1
+        goto continue
+      end
+
+      local opening_delimiter = t[opener_position]
+
+      local current_opening_count = opening_delimiter.current_count
+      local current_closing_count = t[current_position].current_count
+
+      if (current_opening_count >= 2)
+        and (current_closing_count >= 2) then
+        opening_delimiter.current_count = current_opening_count - 2
+        t[current_position].current_count = current_closing_count - 2
+        fill_strong(t, opener_position, current_position)
+      else
+        opening_delimiter.current_count = current_opening_count - 1
+        t[current_position].current_count = current_closing_count - 1
+        fill_emph(t, opener_position, current_position)
+      end
+
+      ::continue::
+    end
+  end
+
+  local cont = lpeg.R("\128\191") -- continuation byte
+
+  local function utf8_by_byte_count(n)
+    if (n == 1) then
+      return lpeg.R("\0\127")
+    end
+    if (n == 2) then
+      return lpeg.R("\194\223") * cont
+    end
+    if (n == 3) then
+      return lpeg.R("\224\239") * cont * cont
+    end
+    if (n == 4) then
+      return lpeg.R("\240\244") * cont * cont * cont
+    end
+  end
+  local function check_unicode_type(s, i, start_pos, end_pos, chartype)
+    local c
+    local char_length
+    for pos = start_pos, end_pos, 1 do
+      if (start_pos < 0) then
+        char_length = -pos
+      else
+        char_length = pos + 1
+      end
+
+      if (chartype == "punctuation") then
+        if lpeg.match(parsers.punctuation[char_length], s, i+pos) then
+          return i
+        end
+      else
+        c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos)
+        if (c ~= nil) and (unicode.utf8.match(c, chartype)) then
+          return i
+        end
+      end
+    end
+  end
+
+  local function check_preceding_unicode_punctuation(s, i)
+    return check_unicode_type(s, i, -4, -1, "punctuation")
+  end
+
+  local function check_preceding_unicode_whitespace(s, i)
+    return check_unicode_type(s, i, -4, -1, "%s")
+  end
+
+  local function check_following_unicode_punctuation(s, i)
+    return check_unicode_type(s, i, 0, 3, "punctuation")
+  end
+
+  local function check_following_unicode_whitespace(s, i)
+    return check_unicode_type(s, i, 0, 3, "%s")
+  end
+
+  parsers.unicode_preceding_punctuation
+    = B(parsers.escapable)
+    + Cmt(parsers.succeed, check_preceding_unicode_punctuation)
+
+  parsers.unicode_preceding_whitespace
+    = Cmt(parsers.succeed, check_preceding_unicode_whitespace)
+
+  parsers.unicode_following_punctuation
+    = #parsers.escapable
+    + Cmt(parsers.succeed, check_following_unicode_punctuation)
+
+  parsers.unicode_following_whitespace
+    = Cmt(parsers.succeed, check_following_unicode_whitespace)
+
+  parsers.delimiter_run = function(character)
+    return  (B(parsers.backslash * character) + -B(character))
+          * character^1
+          * -#character
+  end
+
+  parsers.left_flanking_delimiter_run = function(character)
+    return  (B( parsers.any)
+              * ( parsers.unicode_preceding_punctuation
+                + parsers.unicode_preceding_whitespace)
+             + -B(parsers.any))
+            * parsers.delimiter_run(character)
+            * parsers.unicode_following_punctuation
+            + parsers.delimiter_run(character)
+            * -#( parsers.unicode_following_punctuation
+                + parsers.unicode_following_whitespace
+                + parsers.eof)
+  end
+
+  parsers.right_flanking_delimiter_run = function(character)
+    return  parsers.unicode_preceding_punctuation
+          * parsers.delimiter_run(character)
+          * ( parsers.unicode_following_punctuation
+            + parsers.unicode_following_whitespace
+            + parsers.eof)
+          + (B(parsers.any)
+            * -( parsers.unicode_preceding_punctuation
+               + parsers.unicode_preceding_whitespace))
+          * parsers.delimiter_run(character)
+  end
+
+  if options.underscores then
+    parsers.emph_start
+      = parsers.left_flanking_delimiter_run(parsers.asterisk)
+      + ( -#parsers.right_flanking_delimiter_run(parsers.underscore)
+        + ( parsers.unicode_preceding_punctuation
+          * #parsers.right_flanking_delimiter_run(parsers.underscore)))
+      * parsers.left_flanking_delimiter_run(parsers.underscore)
+
+    parsers.emph_end
+      = parsers.right_flanking_delimiter_run(parsers.asterisk)
+      + ( -#parsers.left_flanking_delimiter_run(parsers.underscore)
+        + #( parsers.left_flanking_delimiter_run(parsers.underscore)
+           * parsers.unicode_following_punctuation))
+      * parsers.right_flanking_delimiter_run(parsers.underscore)
+  else
+    parsers.emph_start
+      = parsers.left_flanking_delimiter_run(parsers.asterisk)
+
+    parsers.emph_end
+      = parsers.right_flanking_delimiter_run(parsers.asterisk)
+  end
+
+  parsers.emph_capturing_open_and_close
+    = #parsers.emph_start * #parsers.emph_end
+    * Ct( Cg(Cc("delimiter"), "type")
+        * Cg(Cc("emphasis"), "element")
+        * Cg(C(parsers.emph_start), "content")
+        * Cg(Cc(true), "is_opening")
+        * Cg(Cc(true), "is_closing"))
+
+  parsers.emph_capturing_open = Ct( Cg(Cc("delimiter"), "type")
+                                  * Cg(Cc("emphasis"), "element")
+                                  * Cg(C(parsers.emph_start), "content")
+                                  * Cg(Cc(true), "is_opening")
+                                  * Cg(Cc(false), "is_closing"))
+
+  parsers.emph_capturing_close = Ct( Cg(Cc("delimiter"), "type")
+                                   * Cg(Cc("emphasis"), "element")
+                                   * Cg(C(parsers.emph_end), "content")
+                                   * Cg(Cc(false), "is_opening")
+                                   * Cg(Cc(true), "is_closing"))
+
+  parsers.emph_open_or_close  = parsers.emph_capturing_open_and_close
+                              + parsers.emph_capturing_open
+                              + parsers.emph_capturing_close
+
+  parsers.emph_open = parsers.emph_capturing_open_and_close
+                    + parsers.emph_capturing_open
+
+  parsers.emph_close  = parsers.emph_capturing_open_and_close
+                      + parsers.emph_capturing_close
+
+  -- List of references defined in the document
+  local references
+
+  -- List of note references defined in the document
+  parsers.rawnotes = {}
+
+  function self.register_link(_, tag, url, title,
+                              attributes)
+    local normalized_tag = self.normalize_tag(tag)
+      if references[normalized_tag] == nil then
+        references[normalized_tag] = {
+          url = url,
+          title = title,
+          attributes = attributes
+        }
+      end
+    return ""
+  end
+
+  function self.lookup_reference(tag)
+    return references[self.normalize_tag(tag)]
+  end
+
+  function self.lookup_note_reference(tag)
+    return parsers.rawnotes[self.normalize_tag(tag)]
+  end
+
+  parsers.title_s_direct_ref  = parsers.squote
+                              * Cs((parsers.html_entities
+                                  + ( parsers.anyescaped
+                                    - parsers.squote
+                                    - parsers.blankline^2))^0)
+                              * parsers.squote
+
+  parsers.title_d_direct_ref  = parsers.dquote
+                              * Cs((parsers.html_entities
+                                  + ( parsers.anyescaped
+                                    - parsers.dquote
+                                    - parsers.blankline^2))^0)
+                              * parsers.dquote
+
+  parsers.title_p_direct_ref  = parsers.lparent
+                              * Cs((parsers.html_entities
+                                  + ( parsers.anyescaped
+                                    - parsers.lparent
+                                    - parsers.rparent
+                                    - parsers.blankline^2))^0)
+                              * parsers.rparent
+
+  parsers.title_direct_ref  = parsers.title_s_direct_ref
+                            + parsers.title_d_direct_ref
+                            + parsers.title_p_direct_ref
+
+  parsers.inline_direct_ref_inside  = parsers.lparent * parsers.spnl
+                                    * Cg(parsers.url + Cc(""), "url")
+                                    * parsers.spnl
+                                    * Cg( parsers.title_direct_ref
+                                        + Cc(""), "title")
+                                    * parsers.spnl * parsers.rparent
+
+  parsers.inline_direct_ref = parsers.lparent * parsers.spnlc
+                            * Cg(parsers.url + Cc(""), "url")
+                            * parsers.spnlc
+                            * Cg(parsers.title + Cc(""), "title")
+                            * parsers.spnlc * parsers.rparent
+
+  parsers.empty_link  = parsers.lbracket
+                      * parsers.rbracket
+
+  parsers.inline_link = parsers.link_text
+                      * parsers.inline_direct_ref
+
+  parsers.full_link = parsers.link_text
+                    * parsers.link_label
+
+  parsers.shortcut_link = parsers.link_label
+                        * -(parsers.empty_link + parsers.link_label)
+
+  parsers.collapsed_link  = parsers.link_label
+                          * parsers.empty_link
+
+  parsers.image_opening = #(parsers.exclamation * parsers.inline_link)
+                        * Cg(Cc("inline"), "link_type")
+                        + #(parsers.exclamation * parsers.full_link)
+                        * Cg(Cc("full"), "link_type")
+                        + #( parsers.exclamation
+                           * parsers.collapsed_link)
+                        * Cg(Cc("collapsed"), "link_type")
+                        + #(parsers.exclamation * parsers.shortcut_link)
+                        * Cg(Cc("shortcut"), "link_type")
+                        + #(parsers.exclamation * parsers.empty_link)
+                        * Cg(Cc("empty"), "link_type")
+
+  parsers.link_opening  = #parsers.inline_link
+                        * Cg(Cc("inline"), "link_type")
+                        + #parsers.full_link
+                        * Cg(Cc("full"), "link_type")
+                        + #parsers.collapsed_link
+                        * Cg(Cc("collapsed"), "link_type")
+                        + #parsers.shortcut_link
+                        * Cg(Cc("shortcut"), "link_type")
+                        + #parsers.empty_link
+                        * Cg(Cc("empty_link"), "link_type")
+                        + #parsers.link_text
+                        * Cg(Cc("link_text"), "link_type")
+
+  parsers.note_opening  = #(parsers.circumflex * parsers.link_text)
+                        * Cg(Cc("note_inline"), "link_type")
+
+  parsers.raw_note_opening  = #( parsers.lbracket
+                               * parsers.circumflex
+                               * parsers.link_label_body
+                               * parsers.rbracket)
+                            * Cg(Cc("raw_note"), "link_type")
+
+  local inline_note_element = Cg(Cc("note"), "element")
+                            * parsers.note_opening
+                            * Cg( parsers.circumflex
+                                * parsers.lbracket, "content")
+
+  local image_element = Cg(Cc("image"), "element")
+                      * parsers.image_opening
+                      * Cg( parsers.exclamation
+                          * parsers.lbracket, "content")
+
+  local note_element  = Cg(Cc("note"), "element")
+                      * parsers.raw_note_opening
+                      * Cg( parsers.lbracket
+                          * parsers.circumflex, "content")
+
+  local link_element  = Cg(Cc("link"), "element")
+                      * parsers.link_opening
+                      * Cg(parsers.lbracket, "content")
+
+  local opening_elements = parsers.fail
+
+  if options.inlineNotes then
+    opening_elements = opening_elements + inline_note_element
+  end
+
+  opening_elements = opening_elements + image_element
+
+  if options.notes then
+    opening_elements = opening_elements + note_element
+  end
+
+  opening_elements = opening_elements + link_element
+
+  parsers.link_image_opening  = Ct( Cg(Cc("delimiter"), "type")
+                                  * Cg(Cc(true), "is_opening")
+                                  * Cg(Cc(false), "is_closing")
+                                  * opening_elements)
+
+  parsers.link_image_closing  = Ct( Cg(Cc("delimiter"), "type")
+                                  * Cg(Cc("link"), "element")
+                                  * Cg(Cc(false), "is_opening")
+                                  * Cg(Cc(true), "is_closing")
+                                  * ( Cg(Cc(true), "is_direct")
+                                    * Cg( parsers.rbracket
+                                        * #parsers.inline_direct_ref,
+                                         "content")
+                                    + Cg(Cc(false), "is_direct")
+                                    * Cg(parsers.rbracket, "content")))
+
+  parsers.link_image_open_or_close  = parsers.link_image_opening
+                                    + parsers.link_image_closing
+
+  if options.html then
+    parsers.link_emph_precedence  = parsers.inticks
+                                  + parsers.autolink
+                                  + parsers.html_inline_tags
+  else
+    parsers.link_emph_precedence  = parsers.inticks
+                                  + parsers.autolink
+  end
+
+  parsers.link_and_emph_endline = parsers.newline
+                                * ((parsers.check_minimal_indent
+                                  * -V("EndlineExceptions")
+                                  + parsers.check_optional_indent
+                                  * -V("EndlineExceptions")
+                                  * -parsers.starter) / "")
+                                * parsers.spacechar^0 / "\n"
+
+  parsers.link_and_emph_content
+    = Ct( Cg(Cc("content"), "type")
+        * Cg(Cs(( parsers.link_emph_precedence
+                + parsers.backslash * parsers.any
+                + parsers.link_and_emph_endline
+                + (parsers.linechar
+                  - parsers.blankline^2
+                  - parsers.link_image_open_or_close
+                  - parsers.emph_open_or_close))^0), "content"))
+
+  parsers.link_and_emph_table
+    = (parsers.link_image_opening + parsers.emph_open)
+    * parsers.link_and_emph_content
+    * ((parsers.link_image_open_or_close + parsers.emph_open_or_close)
+      * parsers.link_and_emph_content)^1
+
+  local function collect_link_content(t, opening_index, closing_index)
+    local content = {}
+    for i = opening_index, closing_index do
+      content[#content + 1] = t[i].content
+    end
+    return util.rope_to_string(content)
+  end
+
+  local function find_link_opener(t, bottom_index, latest_index)
+    for i = latest_index, bottom_index, -1 do
+      local value = t[i]
+      if value.type == "delimiter" and
+         value.is_opening and
+         ( value.element == "link"
+        or value.element == "image"
+        or value.element == "note")
+         and not value.removed then
+        if value.is_active then
+          return i
+        end
+        value.removed = true
+        return nil
+      end
+    end
+  end
+
+  local function find_next_link_closing_index(t, latest_index)
+    for i = latest_index, #t do
+      local value = t[i]
+      if value.is_closing and
+         value.element == "link" and
+         not value.removed then
+        return i
+      end
+    end
+  end
+
+  local function disable_previous_link_openers(t, opening_index)
+    if t[opening_index].element == "image" then
+      return
+    end
+
+    for i = opening_index, 1, -1 do
+      local value = t[i]
+      if value.is_active and
+         value.type == "delimiter" and
+         value.is_opening and
+         value.element == "link" then
+        value.is_active = false
+      end
+    end
+  end
+
+  local function disable_range(t, opening_index, closing_index)
+    for i = opening_index, closing_index do
+      local value = t[i]
+      if value.is_active then
+        value.is_active = false
+        if value.type == "delimiter" then
+          value.removed = true
+        end
+      end
+    end
+  end
+
+  local delete_parsed_content_in_range =
+    function(t, opening_index, closing_index)
+      for i = opening_index, closing_index do
+        t[i].rendered = nil
+      end
+    end
+
+  local function empty_content_in_range(t, opening_index, closing_index)
+    for i = opening_index, closing_index do
+      t[i].content = ''
+    end
+  end
+
+  local function join_attributes(reference_attributes, own_attributes)
+    local merged_attributes = {}
+    for _, attribute in ipairs(reference_attributes or {}) do
+      table.insert(merged_attributes, attribute)
+    end
+    for _, attribute in ipairs(own_attributes or {}) do
+      table.insert(merged_attributes, attribute)
+    end
+    if next(merged_attributes) == nil then
+      merged_attributes = nil
+    end
+    return merged_attributes
+  end
+
+  local render_link_or_image =
+    function(t, opening_index, closing_index, content_end_index,
+             reference)
+      process_emphasis(t, opening_index, content_end_index)
+      local mapped = collect_emphasis_content(t, opening_index + 1,
+                                              content_end_index - 1)
+
+      local rendered = {}
+      if (t[opening_index].element == "link") then
+        rendered = writer.link(mapped, reference.url,
+                               reference.title, reference.attributes)
+      end
+
+      if (t[opening_index].element == "image") then
+        rendered = writer.image(mapped, reference.url, reference.title,
+                                reference.attributes)
+      end
+
+      if (t[opening_index].element == "note") then
+        if (t[opening_index].link_type == "note_inline") then
+          rendered = writer.note(mapped)
+        end
+        if (t[opening_index].link_type == "raw_note") then
+          rendered = writer.note(reference)
+        end
+      end
+
+      t[opening_index].rendered = rendered
+      delete_parsed_content_in_range(t, opening_index + 1,
+                                     closing_index)
+      empty_content_in_range(t, opening_index, closing_index)
+      disable_previous_link_openers(t, opening_index)
+      disable_range(t, opening_index, closing_index)
+    end
+
+  local resolve_inline_following_content =
+    function(t, closing_index, match_reference, match_link_attributes)
+      local content = ""
+      for i = closing_index + 1, #t do
+        content = content .. t[i].content
+      end
+
+      local matching_content = parsers.succeed
+
+      if match_reference then
+        matching_content = matching_content
+                         * parsers.inline_direct_ref_inside
+      end
+
+      if match_link_attributes then
+        matching_content = matching_content
+                         * Cg(Ct(parsers.attributes^-1), "attributes")
+      end
+
+      local matched = lpeg.match(Ct( matching_content
+                                   * Cg(Cp(), "end_position")), content)
+
+      local matched_count = matched.end_position - 1
+      for i = closing_index + 1, #t do
+        local value = t[i]
+
+        local chars_left = matched_count
+        matched_count = matched_count - #value.content
+
+        if matched_count <= 0 then
+          value.content = value.content:sub(chars_left + 1)
+          break
+        end
+
+        value.content = ''
+        value.is_active = false
+      end
+
+      local attributes = matched.attributes
+      if attributes == nil or next(attributes) == nil then
+        attributes = nil
+      end
+
+      return {
+        url = matched.url or "",
+        title = matched.title or "",
+        attributes = attributes
+      }
+    end
+
+  local function resolve_inline_link(t, opening_index, closing_index)
+    local inline_content
+      = resolve_inline_following_content(t, closing_index, true,
+                                         t.match_link_attributes)
+    render_link_or_image(t, opening_index, closing_index,
+                         closing_index, inline_content)
+  end
+
+  local resolve_note_inline_link =
+    function(t, opening_index, closing_index)
+      local inline_content
+        = resolve_inline_following_content(t, closing_index,
+                                           false, false)
+      render_link_or_image(t, opening_index, closing_index,
+                           closing_index, inline_content)
+    end
+
+  local function resolve_shortcut_link(t, opening_index, closing_index)
+    local content
+      = collect_link_content(t, opening_index + 1, closing_index - 1)
+    local r = self.lookup_reference(content)
+
+    if r then
+      local inline_content
+        = resolve_inline_following_content(t, closing_index, false,
+                                           t.match_link_attributes)
+      r.attributes
+        = join_attributes(r.attributes, inline_content.attributes)
+      render_link_or_image(t, opening_index, closing_index,
+                           closing_index, r)
+    end
+  end
+
+  local function resolve_raw_note_link(t, opening_index, closing_index)
+    local content
+      = collect_link_content(t, opening_index + 1, closing_index - 1)
+    local r = self.lookup_note_reference(content)
+
+    if r then
+      local parsed_ref = self.parser_functions.parse_blocks_nested(r)
+      render_link_or_image(t, opening_index, closing_index,
+                           closing_index, parsed_ref)
+    end
+  end
+
+  local function resolve_full_link(t, opening_index, closing_index)
+    local next_link_closing_index
+      = find_next_link_closing_index(t, closing_index + 4)
+    local next_link_content
+      = collect_link_content(t, closing_index + 3,
+                             next_link_closing_index - 1)
+    local r = self.lookup_reference(next_link_content)
+
+    if r then
+      local inline_content
+        = resolve_inline_following_content(t, next_link_closing_index,
+                                           false,
+                                           t.match_link_attributes)
+      r.attributes
+        = join_attributes(r.attributes, inline_content.attributes)
+      render_link_or_image(t, opening_index, next_link_closing_index,
+                           closing_index, r)
+    end
+  end
+
+  local function resolve_collapsed_link(t, opening_index, closing_index)
+    local next_link_closing_index
+      = find_next_link_closing_index(t, closing_index + 4)
+    local content
+      = collect_link_content(t, opening_index + 1, closing_index - 1)
+    local r = self.lookup_reference(content)
+
+    if r then
+      local inline_content
+        = resolve_inline_following_content(t, closing_index, false,
+                                           t.match_link_attributes)
+      r.attributes
+        = join_attributes(r.attributes, inline_content.attributes)
+      render_link_or_image(t, opening_index, next_link_closing_index,
+                           closing_index, r)
+    end
+  end
+
+  local function process_links_and_emphasis(t)
+    for _,value in ipairs(t) do
+      value.is_active = true
+    end
+
+    for i,value in ipairs(t) do
+      if not value.is_closing
+          or value.type ~= "delimiter"
+          or not ( value.element == "link"
+                or value.element == "image"
+                or value.element == "note")
+          or value.removed then
+        goto continue
+      end
+
+      local opener_position = find_link_opener(t, 1, i - 1)
+      if (opener_position == nil) then
+        goto continue
+      end
+
+      local opening_delimiter = t[opener_position]
+      opening_delimiter.removed = true
+
+      local link_type = opening_delimiter.link_type
+
+      if (link_type == "inline") then
+        resolve_inline_link(t, opener_position, i)
+      end
+      if (link_type == "shortcut") then
+        resolve_shortcut_link(t, opener_position, i)
+      end
+      if (link_type == "full") then
+        resolve_full_link(t, opener_position, i)
+      end
+      if (link_type == "collapsed") then
+        resolve_collapsed_link(t, opener_position, i)
+      end
+      if (link_type == "note_inline") then
+        resolve_note_inline_link(t, opener_position, i)
+      end
+      if (link_type == "raw_note") then
+        resolve_raw_note_link(t, opener_position, i)
+      end
+
+      ::continue::
+    end
+
+    t[#t].content = t[#t].content:gsub("%s*$","")
+
+    process_emphasis(t, 1, #t)
+    local final_result = collect_emphasis_content(t, 1, #t)
+    return final_result
+  end
+
+  function self.defer_link_and_emphasis_processing(delimiter_table)
+    return writer.defer_call(function()
+      return process_links_and_emphasis(delimiter_table)
+    end)
+  end
+
+  parsers.Str      = ( parsers.normalchar
+                     * (parsers.normalchar + parsers.at)^0)
+                   / writer.string
+
+  parsers.Symbol   = (parsers.backtick^1 + V("SpecialChar"))
+                   / writer.string
+
+  parsers.Ellipsis = P("...") / writer.ellipsis
+
+  parsers.Smart    = parsers.Ellipsis
+
+  parsers.Code     = parsers.inticks / writer.code
+
+  if options.blankBeforeBlockquote then
+    parsers.bqstart = parsers.fail
+  else
+    parsers.bqstart = parsers.blockquote_start
+  end
+
+  if options.blankBeforeHeading then
+    parsers.headerstart = parsers.fail
+  else
+    parsers.headerstart = parsers.atx_heading
+  end
+
+  if options.blankBeforeList then
+    parsers.interrupting_bullets = parsers.fail
+    parsers.interrupting_enumerators = parsers.fail
+  else
+    parsers.interrupting_bullets
+      = parsers.bullet(parsers.dash, true)
+      + parsers.bullet(parsers.asterisk, true)
+      + parsers.bullet(parsers.plus, true)
+
+    parsers.interrupting_enumerators
+      = parsers.enumerator(parsers.period, true)
+      + parsers.enumerator(parsers.rparent, true)
+  end
+
+  if options.html then
+    parsers.html_interrupting
+      = parsers.check_trail
+      * ( parsers.html_incomplete_open_tag
+        + parsers.html_incomplete_close_tag
+        + parsers.html_incomplete_open_special_tag
+        + parsers.html_comment_start
+        + parsers.html_cdatasection_start
+        + parsers.html_declaration_start
+        + parsers.html_instruction_start
+        - parsers.html_close_special_tag
+        - parsers.html_empty_special_tag)
+  else
+    parsers.html_interrupting = parsers.fail
+  end
+
+  parsers.EndlineExceptions
+                     = parsers.blankline -- paragraph break
+                     + parsers.eof       -- end of document
+                     + parsers.bqstart
+                     + parsers.thematic_break_lines
+                     + parsers.interrupting_bullets
+                     + parsers.interrupting_enumerators
+                     + parsers.headerstart
+                     + parsers.html_interrupting
+
+  parsers.NoSoftLineBreakEndlineExceptions = parsers.EndlineExceptions
+
+  parsers.endline = parsers.newline
+                  * (parsers.check_minimal_indent
+                    * -V("EndlineExceptions")
+                    + parsers.check_optional_indent
+                    * -V("EndlineExceptions")
+                    * -parsers.starter) / function(_) return end
+                  * parsers.spacechar^0
+
+  parsers.Endline = parsers.endline
+                  / writer.soft_line_break
+
+  parsers.EndlineNoSub = parsers.endline
+
+  parsers.NoSoftLineBreakEndline
+                        = parsers.newline
+                        * (parsers.check_minimal_indent
+                          * -V("NoSoftLineBreakEndlineExceptions")
+                          + parsers.check_optional_indent
+                          * -V("NoSoftLineBreakEndlineExceptions")
+                          * -parsers.starter)
+                        * parsers.spacechar^0
+                        / writer.space
+
+  parsers.EndlineBreak = parsers.backslash * parsers.Endline
+                                           / writer.hard_line_break
+
+  parsers.OptionalIndent
+                     = parsers.spacechar^1 / writer.space
+
+  parsers.Space      = parsers.spacechar^2 * parsers.Endline
+                                           / writer.hard_line_break
+                     + parsers.spacechar^1
+                     * parsers.Endline^-1
+                     * parsers.eof / self.expandtabs
+                     + parsers.spacechar^1 * parsers.Endline
+                                           / writer.soft_line_break
+                     + parsers.spacechar^1
+                     * -parsers.newline / self.expandtabs
+
+  parsers.NoSoftLineBreakSpace
+                     = parsers.spacechar^2 * parsers.Endline
+                                           / writer.hard_line_break
+                     + parsers.spacechar^1
+                     * parsers.Endline^-1
+                     * parsers.eof / self.expandtabs
+                     + parsers.spacechar^1 * parsers.Endline
+                                           / writer.soft_line_break
+                     + parsers.spacechar^1
+                     * -parsers.newline / self.expandtabs
+
+  parsers.NonbreakingEndline
+                    = parsers.endline
+                    / writer.nbsp
+
+  parsers.NonbreakingSpace
+                  = parsers.spacechar^2 * parsers.endline
+                                        / writer.nbsp
+                  + parsers.spacechar^1
+                  * parsers.endline^-1 * parsers.eof / ""
+                  + parsers.spacechar^1 * parsers.endline
+                                        * parsers.optionalspace
+                                        / writer.nbsp
+                  + parsers.spacechar^1 * parsers.optionalspace
+                                        / writer.nbsp
+
+function self.auto_link_url(url, attributes)
+  return writer.link(writer.escape(url),
+                     url, nil, attributes)
+end
+function self.auto_link_email(email, attributes)
+  return writer.link(writer.escape(email),
+                     "mailto:"..email,
+                     nil, attributes)
+end
+
+  parsers.AutoLinkUrl = parsers.auto_link_url
+                      / self.auto_link_url
+
+  parsers.AutoLinkEmail
+                      = parsers.auto_link_email
+                      / self.auto_link_email
+
+  parsers.AutoLinkRelativeReference
+                      = parsers.auto_link_relative_reference
+                      / self.auto_link_url
+
+  parsers.LinkAndEmph = Ct(parsers.link_and_emph_table)
+                      / self.defer_link_and_emphasis_processing
+
+  parsers.EscapedChar = parsers.backslash
+                      * C(parsers.escapable) / writer.string
+
+  parsers.InlineHtml = Cs(parsers.html_inline_comment)
+                     / writer.inline_html_comment
+                     + Cs(parsers.html_any_empty_inline_tag
+                         + parsers.html_inline_instruction
+                         + parsers.html_inline_cdatasection
+                         + parsers.html_inline_declaration
+                         + parsers.html_any_open_inline_tag
+                         + parsers.html_any_close_tag)
+                       / writer.inline_html_tag
+
+  parsers.HtmlEntity = parsers.html_entities / writer.string
+  parsers.DisplayHtml = Cs(parsers.check_trail
+                          * ( parsers.html_comment
+                            + parsers.html_special_block
+                            + parsers.html_block
+                            + parsers.html_any_block
+                            + parsers.html_instruction
+                            + parsers.html_cdatasection
+                            + parsers.html_declaration))
+                        / writer.block_html_element
+
+  parsers.indented_non_blank_line = parsers.indentedline
+                                  - parsers.blankline
+
+  parsers.Verbatim
+    = Cs( parsers.check_code_trail
+        * (parsers.line - parsers.blankline)
+        * (( parsers.check_minimal_blank_indent_and_full_code_trail
+           * parsers.blankline)^0
+          * ( (parsers.check_minimal_indent / "")
+            * parsers.check_code_trail
+            * (parsers.line - parsers.blankline))^1)^0)
+    / self.expandtabs / writer.verbatim
+
+  parsers.Blockquote   = parsers.blockquote_body
+                       / writer.blockquote
+
+  parsers.ThematicBreak = parsers.thematic_break_lines
+                        / writer.thematic_break
+
+  parsers.Reference    = parsers.define_reference_parser
+                       / self.register_link
+
+  parsers.Paragraph   = parsers.freeze_trail
+                      * (Ct((parsers.Inline)^1)
+                      * (parsers.newline + parsers.eof)
+                      * parsers.unfreeze_trail
+                      / writer.paragraph)
+
+  parsers.Plain        = parsers.nonindentspace * Ct(parsers.Inline^1)
+                       / writer.plain
+
+  if options.taskLists then
+    parsers.tickbox = ( parsers.ticked_box
+                      + parsers.halfticked_box
+                      + parsers.unticked_box
+                      ) / writer.tickbox
+  else
+     parsers.tickbox = parsers.fail
+  end
+
+  parsers.list_blank = parsers.conditionally_indented_blankline
+
+  parsers.ref_or_block_list_separated
+    = parsers.sep_group_no_output(parsers.list_blank)
+    * parsers.minimally_indented_ref
+    + parsers.block_sep_group(parsers.list_blank)
+    * parsers.minimally_indented_block
+
+  parsers.ref_or_block_non_separated
+    = parsers.minimally_indented_ref
+    + (parsers.succeed / writer.interblocksep)
+    * parsers.minimally_indented_block
+    - parsers.minimally_indented_blankline
+
+  parsers.tight_list_loop_body_pair =
+    parsers.create_loop_body_pair(
+      parsers.ref_or_block_non_separated,
+      parsers.minimally_indented_par_or_plain_no_blank,
+      (parsers.succeed / writer.interblocksep),
+      (parsers.succeed / writer.paragraphsep))
+
+  parsers.loose_list_loop_body_pair =
+    parsers.create_loop_body_pair(
+      parsers.ref_or_block_list_separated,
+      parsers.minimally_indented_par_or_plain,
+      parsers.block_sep_group(parsers.list_blank),
+      parsers.par_sep_group(parsers.list_blank))
+
+  parsers.tight_list_content_loop
+    = V("Block")
+    * parsers.tight_list_loop_body_pair.block^0
+    + (V("Paragraph") + V("Plain"))
+    * parsers.ref_or_block_non_separated
+    * parsers.tight_list_loop_body_pair.block^0
+    +  (V("Paragraph") + V("Plain"))
+    * parsers.tight_list_loop_body_pair.par^0
+
+  parsers.loose_list_content_loop
+    = V("Block")
+    * parsers.loose_list_loop_body_pair.block^0
+    + (V("Paragraph") + V("Plain"))
+    * parsers.ref_or_block_list_separated
+    * parsers.loose_list_loop_body_pair.block^0
+    + (V("Paragraph") + V("Plain"))
+    * parsers.loose_list_loop_body_pair.par^0
+
+  parsers.list_item_tightness_condition
+    = -#( parsers.list_blank^0
+        * parsers.minimally_indented_ref_or_block_or_par)
+    * remove_indent("li")
+    + remove_indent("li")
+    * parsers.fail
+
+  parsers.indented_content_tight
+    = Ct( (parsers.blankline / "")
+        * #parsers.list_blank
+        * remove_indent("li")
+        + ( (V("Reference") + (parsers.blankline / ""))
+          * parsers.check_minimal_indent
+          * parsers.tight_list_content_loop
+          + (V("Reference") + (parsers.blankline / ""))
+          + (parsers.tickbox^-1 / writer.escape)
+          * parsers.tight_list_content_loop
+          )
+        * parsers.list_item_tightness_condition)
+
+  parsers.indented_content_loose
+    = Ct( (parsers.blankline / "")
+        * #parsers.list_blank
+        + ( (V("Reference") + (parsers.blankline / ""))
+          * parsers.check_minimal_indent
+          * parsers.loose_list_content_loop
+          + (V("Reference") + (parsers.blankline / ""))
+          + (parsers.tickbox^-1 / writer.escape)
+          * parsers.loose_list_content_loop))
+
+  parsers.TightListItem = function(starter)
+    return  -parsers.ThematicBreak
+            * parsers.add_indent(starter, "li")
+            * parsers.indented_content_tight
+  end
+
+  parsers.LooseListItem = function(starter)
+    return  -parsers.ThematicBreak
+            * parsers.add_indent(starter, "li")
+            * parsers.indented_content_loose
+            * remove_indent("li")
+  end
+
+  parsers.BulletListOfType = function(bullet_type)
+    local bullet = parsers.bullet(bullet_type)
+    return  ( Ct( parsers.TightListItem(bullet)
+                * ( (parsers.check_minimal_indent / "")
+                  * parsers.TightListItem(bullet)
+                  )^0
+              )
+              * Cc(true)
+              * -#( (parsers.list_blank^0 / "")
+                  * parsers.check_minimal_indent
+                  * (bullet - parsers.ThematicBreak)
+              )
+              + Ct( parsers.LooseListItem(bullet)
+                  * ( (parsers.list_blank^0 / "")
+                    * (parsers.check_minimal_indent / "")
+                    * parsers.LooseListItem(bullet)
+                    )^0
+              )
+              * Cc(false)
+            ) / writer.bulletlist
+  end
+
+  parsers.BulletList = parsers.BulletListOfType(parsers.dash)
+                     + parsers.BulletListOfType(parsers.asterisk)
+                     + parsers.BulletListOfType(parsers.plus)
+
+  local function ordered_list(items,tight,starter)
+    local startnum = starter[2][1]
+    if options.startNumber then
+      startnum = tonumber(startnum) or 1  -- fallback for '#'
+      if startnum ~= nil then
+        startnum = math.floor(startnum)
+      end
+    else
+      startnum = nil
+    end
+    return writer.orderedlist(items,tight,startnum)
+  end
+
+  parsers.OrderedListOfType = function(delimiter_type)
+    local enumerator = parsers.enumerator(delimiter_type)
+    return  Cg(enumerator, "listtype")
+          * (Ct( parsers.TightListItem(Cb("listtype"))
+               * ( (parsers.check_minimal_indent / "")
+                 * parsers.TightListItem(enumerator))^0)
+          * Cc(true)
+          * -#((parsers.list_blank^0 / "")
+              * parsers.check_minimal_indent * enumerator)
+          + Ct( parsers.LooseListItem(Cb("listtype"))
+              * ((parsers.list_blank^0 / "")
+                * (parsers.check_minimal_indent / "")
+                * parsers.LooseListItem(enumerator))^0)
+          * Cc(false)
+          ) * Ct(Cb("listtype")) / ordered_list
+  end
+
+  parsers.OrderedList = parsers.OrderedListOfType(parsers.period)
+                      + parsers.OrderedListOfType(parsers.rparent)
+  parsers.Blank        = parsers.blankline / ""
+                       + V("Reference")
+  function parsers.parse_heading_text(s)
+    local inlines = self.parser_functions.parse_inlines(s)
+    local flatten_inlines = self.writer.flatten_inlines
+    self.writer.flatten_inlines = true
+    local flat_text = self.parser_functions.parse_inlines(s)
+    flat_text = util.rope_to_string(flat_text)
+    self.writer.flatten_inlines = flatten_inlines
+    return {flat_text, inlines}
+  end
+
+  -- parse atx header
+  parsers.AtxHeading = parsers.check_trail_no_rem
+                     * Cg(parsers.heading_start, "level")
+                     * ((C( parsers.optionalspace
+                          * parsers.hash^0
+                          * parsers.optionalspace
+                          * parsers.newline)
+                        + parsers.spacechar^1
+                        * C(parsers.line))
+                       / strip_atx_end
+                       / parsers.parse_heading_text)
+                     * Cb("level")
+                     / writer.heading
+
+  parsers.heading_line  = parsers.linechar^1
+                        - parsers.thematic_break_lines
+
+  parsers.heading_text = parsers.heading_line
+                       * ( (V("Endline") / "\n")
+                         * ( parsers.heading_line
+                           - parsers.heading_level))^0
+                       * parsers.newline^-1
+
+  parsers.SetextHeading = parsers.freeze_trail
+                        * parsers.check_trail_no_rem
+                        * #( parsers.heading_text
+                           * parsers.check_minimal_indent
+                           * parsers.check_trail
+                           * parsers.heading_level)
+                        * Cs(parsers.heading_text)
+                        / parsers.parse_heading_text
+                        * parsers.check_minimal_indent_and_trail
+                        * parsers.heading_level
+                        * parsers.newline
+                        * parsers.unfreeze_trail
+                        / writer.heading
+
+  parsers.Heading = parsers.AtxHeading + parsers.SetextHeading
+  function self.finalize_grammar(extensions)
+    local walkable_syntax = (function(global_walkable_syntax)
+      local local_walkable_syntax = {}
+      for lhs, rule in pairs(global_walkable_syntax) do
+        local_walkable_syntax[lhs] = util.table_copy(rule)
+      end
+      return local_walkable_syntax
+    end)(walkable_syntax)
+    local current_extension_name = nil
+    self.insert_pattern = function(selector, pattern, pattern_name)
+      assert(pattern_name == nil or type(pattern_name) == "string")
+      local _, _, lhs, pos, rhs
+        = selector:find("^(%a+)%s+([%a%s]+%a+)%s+(%a+)$")
+      assert(lhs ~= nil,
+        [[Expected selector in form ]]
+        .. [["LHS (before|after|instead of) RHS", not "]]
+        .. selector .. [["]])
+      assert(walkable_syntax[lhs] ~= nil,
+        [[Rule ]] .. lhs
+        .. [[ -> ... does not exist in markdown grammar]])
+      assert(pos == "before" or pos == "after" or pos == "instead of",
+        [[Expected positional specifier "before", "after", ]]
+        .. [[or "instead of", not "]]
+        .. pos .. [["]])
+      local rule = walkable_syntax[lhs]
+      local index = nil
+      for current_index, current_rhs in ipairs(rule) do
+        if type(current_rhs) == "string" and current_rhs == rhs then
+          index = current_index
+          if pos == "after" then
+            index = index + 1
+          end
+          break
+        end
+      end
+      assert(index ~= nil,
+        [[Rule ]] .. lhs .. [[ -> ]] .. rhs
+          .. [[ does not exist in markdown grammar]])
+      local accountable_pattern
+      if current_extension_name then
+        accountable_pattern
+          = {pattern, current_extension_name, pattern_name}
+      else
+        assert(type(pattern) == "string",
+          [[reader->insert_pattern() was called outside ]]
+          .. [[an extension with ]]
+          .. [[a PEG pattern instead of a rule name]])
+        accountable_pattern = pattern
+      end
+      if pos == "instead of" then
+        rule[index] = accountable_pattern
+      else
+        table.insert(rule, index, accountable_pattern)
+      end
+    end
+    local syntax =
+      { "Blocks",
+
+        Blocks = V("InitializeState")
+               * V("ExpectedJekyllData")
+               * V("Blank")^0
+               * ( V("Block")
+                 * ( V("Blank")^0 * parsers.eof
+                   + ( V("Blank")^2 / writer.paragraphsep
+                     + V("Blank")^0 / writer.interblocksep
+                     )
+                   )
+                 + ( V("Paragraph") + V("Plain") )
+                 * ( V("Blank")^0 * parsers.eof
+                   + ( V("Blank")^2 / writer.paragraphsep
+                     + V("Blank")^0 / writer.interblocksep
+                     )
+                   )
+                 * V("Block")
+                 * ( V("Blank")^0 * parsers.eof
+                   + ( V("Blank")^2 / writer.paragraphsep
+                     + V("Blank")^0 / writer.interblocksep
+                     )
+                   )
+                 + ( V("Paragraph") + V("Plain") )
+                 * ( V("Blank")^0 * parsers.eof
+                   + V("Blank")^0 / writer.paragraphsep
+                   )
+                 )^0,
+
+        ExpectedJekyllData = parsers.succeed,
+
+        Blank              = parsers.Blank,
+        Reference          = parsers.Reference,
+
+        Blockquote         = parsers.Blockquote,
+        Verbatim           = parsers.Verbatim,
+        ThematicBreak      = parsers.ThematicBreak,
+        BulletList         = parsers.BulletList,
+        OrderedList        = parsers.OrderedList,
+        DisplayHtml        = parsers.DisplayHtml,
+        Heading            = parsers.Heading,
+        Paragraph          = parsers.Paragraph,
+        Plain              = parsers.Plain,
+
+        EndlineExceptions  = parsers.EndlineExceptions,
+        NoSoftLineBreakEndlineExceptions
+                           = parsers.NoSoftLineBreakEndlineExceptions,
+
+        Str                = parsers.Str,
+        Space              = parsers.Space,
+        NoSoftLineBreakSpace
+                           = parsers.NoSoftLineBreakSpace,
+        OptionalIndent     = parsers.OptionalIndent,
+        Endline            = parsers.Endline,
+        EndlineNoSub       = parsers.EndlineNoSub,
+        NoSoftLineBreakEndline
+                           = parsers.NoSoftLineBreakEndline,
+        EndlineBreak       = parsers.EndlineBreak,
+        LinkAndEmph        = parsers.LinkAndEmph,
+        Code               = parsers.Code,
+        AutoLinkUrl        = parsers.AutoLinkUrl,
+        AutoLinkEmail      = parsers.AutoLinkEmail,
+        AutoLinkRelativeReference
+                           = parsers.AutoLinkRelativeReference,
+        InlineHtml         = parsers.InlineHtml,
+        HtmlEntity         = parsers.HtmlEntity,
+        EscapedChar        = parsers.EscapedChar,
+        Smart              = parsers.Smart,
+        Symbol             = parsers.Symbol,
+        SpecialChar        = parsers.fail,
+        InitializeState    = parsers.succeed,
+      }
+    self.update_rule = function(rule_name, get_pattern)
+      assert(current_extension_name ~= nil)
+      assert(syntax[rule_name] ~= nil,
+        [[Rule ]] .. rule_name
+        .. [[ -> ... does not exist in markdown grammar]])
+      local previous_pattern
+      local extension_name
+      if walkable_syntax[rule_name] then
+        local previous_accountable_pattern
+          = walkable_syntax[rule_name][1]
+        previous_pattern = previous_accountable_pattern[1]
+        extension_name
+          = previous_accountable_pattern[2]
+          .. ", " .. current_extension_name
+      else
+        previous_pattern = nil
+        extension_name = current_extension_name
+      end
+      local pattern
+      if type(get_pattern) == "function" then
+        pattern = get_pattern(previous_pattern)
+      else
+        assert(previous_pattern == nil,
+               [[Rule ]] .. rule_name ..
+               [[ has already been updated by ]] .. extension_name)
+        pattern = get_pattern
+      end
+      local accountable_pattern = { pattern, extension_name, rule_name }
+      walkable_syntax[rule_name] = { accountable_pattern }
+    end
+    local special_characters = {}
+    self.add_special_character = function(c)
+      table.insert(special_characters, c)
+      syntax.SpecialChar = S(table.concat(special_characters, ""))
+    end
+
+    self.add_special_character("*")
+    self.add_special_character("[")
+    self.add_special_character("]")
+    self.add_special_character("<")
+    self.add_special_character("!")
+    self.add_special_character("\\")
+    self.initialize_named_group = function(name, value)
+      local pattern = Ct("")
+      if value ~= nil then
+        pattern = pattern / value
+      end
+      syntax.InitializeState = syntax.InitializeState
+                             * Cg(pattern, name)
+    end
+    self.initialize_named_group("indent_info")
+    for _, extension in ipairs(extensions) do
+      current_extension_name = extension.name
+      extension.extend_writer(writer)
+      extension.extend_reader(self)
+    end
+    current_extension_name = nil
+    if options.debugExtensions then
+      local sorted_lhs = {}
+      for lhs, _ in pairs(walkable_syntax) do
+        table.insert(sorted_lhs, lhs)
+      end
+      table.sort(sorted_lhs)
+
+      local output_lines = {"{"}
+      for lhs_index, lhs in ipairs(sorted_lhs) do
+        local encoded_lhs = util.encode_json_string(lhs)
+        table.insert(output_lines, [[    ]] ..encoded_lhs .. [[: []])
+        local rule = walkable_syntax[lhs]
+        for rhs_index, rhs in ipairs(rule) do
+          local human_readable_rhs
+          if type(rhs) == "string" then
+            human_readable_rhs = rhs
+          else
+            local pattern_name
+            if rhs[3] then
+              pattern_name = rhs[3]
+            else
+              pattern_name = "Anonymous Pattern"
+            end
+            local extension_name = rhs[2]
+            human_readable_rhs = pattern_name .. [[ (]]
+                               .. extension_name .. [[)]]
+          end
+          local encoded_rhs
+            = util.encode_json_string(human_readable_rhs)
+          local output_line = [[        ]] .. encoded_rhs
+          if rhs_index < #rule then
+            output_line = output_line .. ","
+          end
+          table.insert(output_lines, output_line)
+        end
+        local output_line = "    ]"
+        if lhs_index < #sorted_lhs then
+          output_line = output_line .. ","
+        end
+        table.insert(output_lines, output_line)
+      end
+      table.insert(output_lines, "}")
+
+      local output = table.concat(output_lines, "\n")
+      local output_filename = options.debugExtensionsFileName
+      local output_file = assert(io.open(output_filename, "w"),
+        [[Could not open file "]] .. output_filename
+        .. [[" for writing]])
+      assert(output_file:write(output))
+      assert(output_file:close())
+    end
+    for lhs, rule in pairs(walkable_syntax) do
+      syntax[lhs] = parsers.fail
+      for _, rhs in ipairs(rule) do
+        local pattern
+        if type(rhs) == "string" then
+          pattern = V(rhs)
+        else
+          pattern = rhs[1]
+          if type(pattern) == "string" then
+            pattern = V(pattern)
+          end
+        end
+        syntax[lhs] = syntax[lhs] + pattern
+      end
+    end
+    if options.underscores then
+      self.add_special_character("_")
+    end
+
+    if not options.codeSpans then
+      syntax.Code = parsers.fail
+    else
+      self.add_special_character("`")
+    end
+
+    if not options.html then
+      syntax.DisplayHtml = parsers.fail
+      syntax.InlineHtml = parsers.fail
+      syntax.HtmlEntity  = parsers.fail
+    else
+      self.add_special_character("&")
+    end
+
+    if options.preserveTabs then
+      options.stripIndent = false
+    end
+
+    if not options.smartEllipses then
+      syntax.Smart = parsers.fail
+    else
+      self.add_special_character(".")
+    end
+
+    if not options.relativeReferences then
+      syntax.AutoLinkRelativeReference = parsers.fail
+    end
+
+    if options.contentLevel == "inline" then
+      syntax[1] = "Inlines"
+      syntax.Inlines = V("InitializeState")
+                      * parsers.Inline^0
+                      * ( parsers.spacing^0
+                        * parsers.eof / "")
+      syntax.Space = parsers.Space + parsers.blankline / writer.space
+    end
+
+    local blocks_nested_t = util.table_copy(syntax)
+    blocks_nested_t.ExpectedJekyllData = parsers.succeed
+    parsers.blocks_nested = Ct(blocks_nested_t)
+
+    parsers.blocks = Ct(syntax)
+
+    local inlines_t = util.table_copy(syntax)
+    inlines_t[1] = "Inlines"
+    inlines_t.Inlines = V("InitializeState")
+                      * parsers.Inline^0
+                      * ( parsers.spacing^0
+                        * parsers.eof / "")
+    parsers.inlines = Ct(inlines_t)
+
+    local inlines_no_inline_note_t = util.table_copy(inlines_t)
+    inlines_no_inline_note_t.InlineNote = parsers.fail
+    parsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t)
+
+    local inlines_no_html_t = util.table_copy(inlines_t)
+    inlines_no_html_t.DisplayHtml = parsers.fail
+    inlines_no_html_t.InlineHtml = parsers.fail
+    inlines_no_html_t.HtmlEntity = parsers.fail
+    parsers.inlines_no_html = Ct(inlines_no_html_t)
+
+    local inlines_nbsp_t = util.table_copy(inlines_t)
+    inlines_nbsp_t.Endline = parsers.NonbreakingEndline
+    inlines_nbsp_t.Space = parsers.NonbreakingSpace
+    parsers.inlines_nbsp = Ct(inlines_nbsp_t)
+
+    local inlines_no_link_or_emphasis_t = util.table_copy(inlines_t)
+    inlines_no_link_or_emphasis_t.LinkAndEmph = parsers.fail
+    inlines_no_link_or_emphasis_t.EndlineExceptions
+      = parsers.EndlineExceptions - parsers.eof
+    parsers.inlines_no_link_or_emphasis
+      = Ct(inlines_no_link_or_emphasis_t)
+    return function(input)
+      if options.unicodeNormalization then
+        local form = options.unicodeNormalizationForm
+        if form == "nfc" then
+          input = uni_algos.normalize.NFC(input)
+        elseif form == "nfd" then
+          input = uni_algos.normalize.NFD(input)
+        elseif form == "nfkc" then
+          input = uni_algos.normalize.NFKC(input)
+        elseif form == "nfkd" then
+          input = uni_algos.normalize.NFKD(input)
+        else
+          return writer.error(
+            format("Unknown normalization form %s", form))
+        end
+      end
+      input = input:gsub("\r\n?", "\n")
+      if input:sub(-1) ~= "\n" then
+        input = input .. "\n"
+      end
+      references = {}
+      local document = self.parser_functions.parse_blocks(input)
+      local output = util.rope_to_string(writer.document(document))
+      local undosep_start, undosep_end
+      local potential_secend_start, secend_start
+      local potential_sep_start, sep_start
+      while true do
+        -- find a `writer->undosep`
+        undosep_start, undosep_end
+          = output:find(writer.undosep_text, 1, true)
+        if undosep_start == nil then break end
+        -- skip any preceding section ends
+        secend_start = undosep_start
+        while true do
+          potential_secend_start = secend_start - #writer.secend_text
+          if potential_secend_start < 1
+             or output:sub(potential_secend_start,
+                           secend_start - 1) ~= writer.secend_text
+             then
+            break
+          end
+          secend_start = potential_secend_start
+        end
+        -- find an immediately preceding
+        -- block element / paragraph separator
+        sep_start = secend_start
+        potential_sep_start = sep_start - #writer.interblocksep_text
+        if potential_sep_start >= 1
+           and output:sub(potential_sep_start,
+                          sep_start - 1) == writer.interblocksep_text
+           then
+          sep_start = potential_sep_start
+        else
+          potential_sep_start = sep_start - #writer.paragraphsep_text
+          if potential_sep_start >= 1
+             and output:sub(potential_sep_start,
+                            sep_start - 1) == writer.paragraphsep_text
+             then
+            sep_start = potential_sep_start
+          end
+        end
+        -- remove `writer->undosep` and immediately preceding
+        -- block element / paragraph separator
+        output = output:sub(1, sep_start - 1)
+              .. output:sub(secend_start, undosep_start - 1)
+              .. output:sub(undosep_end + 1)
+      end
+      return output
+    end
+  end
+  return self
+end
+M.extensions = {}
+M.extensions.bracketed_spans = function()
+  return {
+    name = "built-in bracketed_spans syntax extension",
+    extend_writer = function(self)
+      function self.span(s, attr)
+        if self.flatten_inlines then return s end
+        return {"\\markdownRendererBracketedSpanAttributeContextBegin",
+                self.attributes(attr),
+                s,
+                "\\markdownRendererBracketedSpanAttributeContextEnd{}"}
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local span_label  = parsers.lbracket
+                          * (Cs((parsers.alphanumeric^1
+                              + parsers.inticks
+                              + parsers.autolink
+                              + V("InlineHtml")
+                              + ( parsers.backslash * parsers.backslash)
+                              + ( parsers.backslash
+                                * (parsers.lbracket + parsers.rbracket)
+                                + V("Space") + V("Endline")
+                                + (parsers.any
+                                  - ( parsers.newline
+                                    + parsers.lbracket
+                                    + parsers.rbracket
+                                    + parsers.blankline^2))))^1)
+                          / self.parser_functions.parse_inlines)
+                          * parsers.rbracket
+
+      local Span = span_label
+                 * Ct(parsers.attributes)
+                 / writer.span
+
+      self.insert_pattern("Inline before LinkAndEmph",
+                          Span, "Span")
+    end
+  }
+end
+M.extensions.citations = function(citation_nbsps)
+  return {
+    name = "built-in citations syntax extension",
+    extend_writer = function(self)
+      function self.citations(text_cites, cites)
+        local buffer = {}
+        if self.flatten_inlines then
+          for _,cite in ipairs(cites) do
+            if cite.prenote then
+              table.insert(buffer, {cite.prenote, " "})
+            end
+            table.insert(buffer, cite.name)
+            if cite.postnote then
+              table.insert(buffer, {" ", cite.postnote})
+            end
+          end
+        else
+          table.insert(buffer,
+                       {"\\markdownRenderer",
+                        text_cites and "TextCite" or "Cite",
+                        "{", #cites, "}"})
+          for _,cite in ipairs(cites) do
+            table.insert(buffer,
+                         {cite.suppress_author and "-" or "+", "{",
+                          cite.prenote or "", "}{",
+                          cite.postnote or "", "}{", cite.name, "}"})
+          end
+        end
+        return buffer
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local citation_chars
+                    = parsers.alphanumeric
+                    + S("#$%&-+<>~/_")
+
+      local citation_name
+                    = Cs(parsers.dash^-1) * parsers.at
+                    * Cs(citation_chars
+                        * ((( citation_chars
+                            + parsers.internal_punctuation
+                            - parsers.comma - parsers.semicolon)
+                           * -#(( parsers.internal_punctuation
+                                - parsers.comma
+                                - parsers.semicolon)^0
+                               * -( citation_chars
+                                  + parsers.internal_punctuation
+                                  - parsers.comma
+                                  - parsers.semicolon)))^0
+                          * citation_chars)^-1)
+
+      local citation_body_prenote
+                    = Cs((parsers.alphanumeric^1
+                         + parsers.bracketed
+                         + parsers.inticks
+                         + parsers.autolink
+                         + V("InlineHtml")
+                         + V("Space") + V("EndlineNoSub")
+                         + (parsers.anyescaped
+                           - ( parsers.newline
+                             + parsers.rbracket
+                             + parsers.blankline^2))
+                         - ( parsers.spnl
+                           * parsers.dash^-1
+                           * parsers.at))^1)
+
+      local citation_body_postnote
+                    = Cs((parsers.alphanumeric^1
+                         + parsers.bracketed
+                         + parsers.inticks
+                         + parsers.autolink
+                         + V("InlineHtml")
+                         + V("Space") + V("EndlineNoSub")
+                         + (parsers.anyescaped
+                           - ( parsers.newline
+                             + parsers.rbracket
+                             + parsers.semicolon
+                             + parsers.blankline^2))
+                         - (parsers.spnl * parsers.rbracket))^1)
+
+      local citation_body_chunk
+                    = ( citation_body_prenote
+                      * parsers.spnlc_sep
+                      + Cc("")
+                      * parsers.spnlc
+                    )
+                    * citation_name
+                    * ( parsers.internal_punctuation
+                      - parsers.semicolon)^-1
+                    * ( parsers.spnlc / function(_) return end
+                      * citation_body_postnote
+                      + Cc("")
+                      * parsers.spnlc
+                    )
+
+      local citation_body
+                    = citation_body_chunk
+                    * ( parsers.semicolon
+                      * parsers.spnlc
+                      * citation_body_chunk
+                    )^0
+
+      local citation_headless_body_postnote
+                    = Cs((parsers.alphanumeric^1
+                         + parsers.bracketed
+                         + parsers.inticks
+                         + parsers.autolink
+                         + V("InlineHtml")
+                         + V("Space") + V("Endline")
+                         + (parsers.anyescaped
+                           - ( parsers.newline
+                             + parsers.rbracket
+                             + parsers.at
+                             + parsers.semicolon + parsers.blankline^2))
+                         - (parsers.spnl * parsers.rbracket))^0)
+
+      local citation_headless_body
+                    = citation_headless_body_postnote
+                    * ( parsers.semicolon
+                      * parsers.spnlc
+                      * citation_body_chunk
+                    )^0
+
+      local citations
+                    = function(text_cites, raw_cites)
+          local function normalize(str)
+              if str == "" then
+                  str = nil
+              else
+                  str = (citation_nbsps and
+                    self.parser_functions.parse_inlines_nbsp or
+                    self.parser_functions.parse_inlines)(str)
+              end
+              return str
+          end
+
+          local cites = {}
+          for i = 1,#raw_cites,4 do
+              cites[#cites+1] = {
+                  prenote = normalize(raw_cites[i]),
+                  suppress_author = raw_cites[i+1] == "-",
+                  name = writer.identifier(raw_cites[i+2]),
+                  postnote = normalize(raw_cites[i+3]),
+              }
+          end
+          return writer.citations(text_cites, cites)
+      end
+
+      local TextCitations
+                    = Ct((parsers.spnlc
+                    * Cc("")
+                    * citation_name
+                    * ((parsers.spnlc
+                        * parsers.lbracket
+                        * citation_headless_body
+                        * parsers.rbracket) + Cc("")))^1)
+                    / function(raw_cites)
+                        return citations(true, raw_cites)
+                      end
+
+      local ParenthesizedCitations
+                    = Ct((parsers.spnlc
+                    * parsers.lbracket
+                    * citation_body
+                    * parsers.rbracket)^1)
+                    / function(raw_cites)
+                        return citations(false, raw_cites)
+                      end
+
+      local Citations = TextCitations + ParenthesizedCitations
+
+      self.insert_pattern("Inline before LinkAndEmph",
+                          Citations, "Citations")
+
+      self.add_special_character("@")
+      self.add_special_character("-")
+    end
+  }
+end
+M.extensions.content_blocks = function(language_map)
+  local languages_json = (function()
+    local base, prev, curr
+    for _, pathname in ipairs{kpse.lookup(language_map,
+                                          {all=true})} do
+      local file = io.open(pathname, "r")
+      if not file then goto continue end
+      local input = assert(file:read("*a"))
+      assert(file:close())
+      local json = input:gsub('("[^\n]-"):','[%1]=')
+      curr = load("_ENV = {}; return "..json)()
+      if type(curr) == "table" then
+        if base == nil then
+          base = curr
+        else
+          setmetatable(prev, { __index = curr })
+        end
+        prev = curr
+      end
+      ::continue::
+    end
+    return base or {}
+  end)()
+
+  return {
+    name = "built-in content_blocks syntax extension",
+    extend_writer = function(self)
+      function self.contentblock(src,suf,type,tit)
+        if not self.is_writing then return "" end
+        src = src.."."..suf
+        suf = suf:lower()
+        if type == "onlineimage" then
+          return {"\\markdownRendererContentBlockOnlineImage{",suf,"}",
+                  "{",self.string(src),"}",
+                  "{",self.uri(src),"}",
+                  "{",self.string(tit or ""),"}"}
+        elseif languages_json[suf] then
+          return {"\\markdownRendererContentBlockCode{",suf,"}",
+                  "{",self.string(languages_json[suf]),"}",
+                  "{",self.string(src),"}",
+                  "{",self.uri(src),"}",
+                  "{",self.string(tit or ""),"}"}
+        else
+          return {"\\markdownRendererContentBlock{",suf,"}",
+                  "{",self.string(src),"}",
+                  "{",self.uri(src),"}",
+                  "{",self.string(tit or ""),"}"}
+        end
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local contentblock_tail
+                    = parsers.optionaltitle
+                    * (parsers.newline + parsers.eof)
+
+      -- case insensitive online image suffix:
+      local onlineimagesuffix
+                    = (function(...)
+                        local parser = nil
+                        for _, suffix in ipairs({...}) do
+                          local pattern=nil
+                          for i=1,#suffix do
+                            local char=suffix:sub(i,i)
+                            char = S(char:lower()..char:upper())
+                            if pattern == nil then
+                              pattern = char
+                            else
+                              pattern = pattern * char
+                            end
+                          end
+                          if parser == nil then
+                            parser = pattern
+                          else
+                            parser = parser + pattern
+                          end
+                        end
+                        return parser
+                      end)("png", "jpg", "jpeg", "gif", "tif", "tiff")
+
+      -- online image url for iA Writer content blocks with
+      -- mandatory suffix, allowing nested brackets:
+      local onlineimageurl
+                    = (parsers.less
+                      * Cs((parsers.anyescaped
+                           - parsers.more
+                           - parsers.spacing
+                           - #(parsers.period
+                              * onlineimagesuffix
+                              * parsers.more
+                              * contentblock_tail))^0)
+                      * parsers.period
+                      * Cs(onlineimagesuffix)
+                      * parsers.more
+                      + (Cs((parsers.inparens
+                            + (parsers.anyescaped
+                              - parsers.spacing
+                              - parsers.rparent
+                              - #(parsers.period
+                                 * onlineimagesuffix
+                                 * contentblock_tail)))^0)
+                        * parsers.period
+                        * Cs(onlineimagesuffix))
+                      ) * Cc("onlineimage")
+
+      -- filename for iA Writer content blocks with mandatory suffix:
+      local localfilepath
+                    = parsers.slash
+                    * Cs((parsers.anyescaped
+                         - parsers.tab
+                         - parsers.newline
+                         - #(parsers.period
+                            * parsers.alphanumeric^1
+                            * contentblock_tail))^1)
+                    * parsers.period
+                    * Cs(parsers.alphanumeric^1)
+                    * Cc("localfile")
+
+      local ContentBlock
+                    = parsers.check_trail_no_rem
+                    * (localfilepath + onlineimageurl)
+                    * contentblock_tail
+                    / writer.contentblock
+
+      self.insert_pattern("Block before Blockquote",
+                          ContentBlock, "ContentBlock")
+    end
+  }
+end
+M.extensions.definition_lists = function(tight_lists)
+  return {
+    name = "built-in definition_lists syntax extension",
+    extend_writer = function(self)
+      local function dlitem(term, defs)
+        local retVal = {"\\markdownRendererDlItem{",term,"}"}
+        for _, def in ipairs(defs) do
+          retVal[#retVal+1]
+            = {"\\markdownRendererDlDefinitionBegin ",def,
+               "\\markdownRendererDlDefinitionEnd "}
+        end
+        retVal[#retVal+1] = "\\markdownRendererDlItemEnd "
+        return retVal
+      end
+
+      function self.definitionlist(items,tight)
+        if not self.is_writing then return "" end
+        local buffer = {}
+        for _,item in ipairs(items) do
+          buffer[#buffer + 1] = dlitem(item.term, item.definitions)
+        end
+        if tight and tight_lists then
+          return {"\\markdownRendererDlBeginTight\n", buffer,
+            "\n\\markdownRendererDlEndTight"}
+        else
+          return {"\\markdownRendererDlBegin\n", buffer,
+            "\n\\markdownRendererDlEnd"}
+        end
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local defstartchar = S("~:")
+
+      local defstart
+        = parsers.check_trail_length(0) * defstartchar
+        * #parsers.spacing
+        * (parsers.tab + parsers.space^-3)
+        + parsers.check_trail_length(1)
+        * defstartchar * #parsers.spacing
+        * (parsers.tab + parsers.space^-2)
+        + parsers.check_trail_length(2)
+        * defstartchar * #parsers.spacing
+        * (parsers.tab + parsers.space^-1)
+        + parsers.check_trail_length(3)
+        * defstartchar * #parsers.spacing
+
+      local indented_line
+        = (parsers.check_minimal_indent / "")
+        * parsers.check_code_trail * parsers.line
+
+      local blank
+        = parsers.check_minimal_blank_indent_and_any_trail
+        * parsers.optionalspace * parsers.newline
+
+      local dlchunk = Cs(parsers.line * (indented_line - blank)^0)
+
+      local indented_blocks = function(bl)
+        return Cs( bl
+              * (blank^1 * (parsers.check_minimal_indent / "")
+                * parsers.check_code_trail * -parsers.blankline * bl)^0
+              * (blank^1 + parsers.eof))
+      end
+
+      local function definition_list_item(term, defs, _)
+        return { term = self.parser_functions.parse_inlines(term),
+                 definitions = defs }
+      end
+
+      local DefinitionListItemLoose
+        = C(parsers.line) * blank^0
+        * Ct((parsers.check_minimal_indent * (defstart
+             * indented_blocks(dlchunk)
+             / self.parser_functions.parse_blocks_nested))^1)
+        * Cc(false) / definition_list_item
+
+      local DefinitionListItemTight
+        = C(parsers.line)
+        * Ct((parsers.check_minimal_indent * (defstart * dlchunk
+             / self.parser_functions.parse_blocks_nested))^1)
+        * Cc(true) / definition_list_item
+
+      local DefinitionList
+        = ( Ct(DefinitionListItemLoose^1) * Cc(false)
+          + Ct(DefinitionListItemTight^1)
+          * (blank^0
+            * -DefinitionListItemLoose * Cc(true))
+          ) / writer.definitionlist
+
+      self.insert_pattern("Block after Heading",
+                          DefinitionList, "DefinitionList")
+    end
+  }
+end
+M.extensions.fancy_lists = function()
+  return {
+    name = "built-in fancy_lists syntax extension",
+    extend_writer = function(self)
+      local options = self.options
+
+      function self.fancylist(items,tight,startnum,numstyle,numdelim)
+        if not self.is_writing then return "" end
+        local buffer = {}
+        local num = startnum
+        for _,item in ipairs(items) do
+          if item ~= "" then
+            buffer[#buffer + 1] = self.fancyitem(item,num)
+          end
+          if num ~= nil and item ~= "" then
+            num = num + 1
+          end
+        end
+        local contents = util.intersperse(buffer,"\n")
+        if tight and options.tightLists then
+          return {"\\markdownRendererFancyOlBeginTight{",
+                  numstyle,"}{",numdelim,"}",contents,
+                  "\n\\markdownRendererFancyOlEndTight "}
+        else
+          return {"\\markdownRendererFancyOlBegin{",
+                  numstyle,"}{",numdelim,"}",contents,
+                  "\n\\markdownRendererFancyOlEnd "}
+        end
+      end
+      function self.fancyitem(s,num)
+        if num ~= nil then
+          return {"\\markdownRendererFancyOlItemWithNumber{",num,"}",s,
+                  "\\markdownRendererFancyOlItemEnd "}
+        else
+          return {"\\markdownRendererFancyOlItem ",s,
+                  "\\markdownRendererFancyOlItemEnd "}
+        end
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local options = self.options
+      local writer = self.writer
+
+      local function combine_markers_and_delims(markers, delims)
+        local markers_table = {}
+        for _,marker in ipairs(markers) do
+          local start_marker
+          local continuation_marker
+          if type(marker) == "table" then
+            start_marker = marker[1]
+            continuation_marker = marker[2]
+          else
+            start_marker = marker
+            continuation_marker = marker
+          end
+          for _,delim in ipairs(delims) do
+            table.insert(markers_table,
+                         {start_marker, continuation_marker, delim})
+          end
+        end
+        return markers_table
+      end
+
+      local function join_table_with_func(func, markers_table)
+        local pattern = func(table.unpack(markers_table[1]))
+        for i = 2, #markers_table do
+          pattern = pattern + func(table.unpack(markers_table[i]))
+        end
+        return pattern
+      end
+
+      local lowercase_letter_marker = R("az")
+      local uppercase_letter_marker = R("AZ")
+
+      local roman_marker = function(chars)
+        local m, d, c = P(chars[1]), P(chars[2]), P(chars[3])
+        local l, x, v, i
+          = P(chars[4]), P(chars[5]), P(chars[6]), P(chars[7])
+        return  m^-3
+                * (c*m + c*d + d^-1 * c^-3)
+                * (x*c + x*l + l^-1 * x^-3)
+                * (i*x + i*v + v^-1 * i^-3)
+      end
+
+      local lowercase_roman_marker
+        = roman_marker({"m", "d", "c", "l", "x", "v", "i"})
+      local uppercase_roman_marker
+        = roman_marker({"M", "D", "C", "L", "X", "V", "I"})
+
+      local lowercase_opening_roman_marker  = P("i")
+      local uppercase_opening_roman_marker  = P("I")
+
+      local digit_marker = parsers.dig * parsers.dig^-8
+
+      local markers = {
+        {lowercase_opening_roman_marker, lowercase_roman_marker},
+        {uppercase_opening_roman_marker, uppercase_roman_marker},
+        lowercase_letter_marker,
+        uppercase_letter_marker,
+        lowercase_roman_marker,
+        uppercase_roman_marker,
+        digit_marker
+      }
+
+      local delims = {
+        parsers.period,
+        parsers.rparent
+      }
+
+      local markers_table = combine_markers_and_delims(markers, delims)
+
+      local function enumerator(start_marker, _,
+                                delimiter_type, interrupting)
+        local delimiter_range
+        local allowed_end
+        if interrupting then
+          delimiter_range = P("1")
+          allowed_end = C(parsers.spacechar^1) * #parsers.linechar
+        else
+          delimiter_range = start_marker
+          allowed_end = C(parsers.spacechar^1)
+                      + #(parsers.newline + parsers.eof)
+        end
+
+        return parsers.check_trail
+                * Ct(C(delimiter_range) * C(delimiter_type))
+                * allowed_end
+      end
+
+      local starter = join_table_with_func(enumerator, markers_table)
+
+      local TightListItem = function(starter)
+        return  parsers.add_indent(starter, "li")
+                * parsers.indented_content_tight
+      end
+
+      local LooseListItem = function(starter)
+        return  parsers.add_indent(starter, "li")
+                * parsers.indented_content_loose
+                * remove_indent("li")
+      end
+
+      local function roman2number(roman)
+        local romans = { ["M"] = 1000, ["D"] = 500, ["C"] = 100,
+                         ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
+        local numeral = 0
+
+        local i = 1
+        local len = string.len(roman)
+        while i < len do
+          local z1, z2 = romans[ string.sub(roman, i, i) ],
+                         romans[ string.sub(roman, i+1, i+1) ]
+          if z1 < z2 then
+              numeral = numeral + (z2 - z1)
+              i = i + 2
+          else
+              numeral = numeral + z1
+              i = i + 1
+          end
+        end
+        if i <= len then
+          numeral = numeral + romans[ string.sub(roman,i,i) ]
+        end
+        return numeral
+      end
+
+      local function sniffstyle(numstr, delimend)
+        local numdelim
+        if delimend == ")" then
+          numdelim = "OneParen"
+        elseif delimend == "." then
+          numdelim = "Period"
+        else
+          numdelim = "Default"
+        end
+
+        local num
+        num = numstr:match("^([I])$")
+        if num then
+          return roman2number(num), "UpperRoman", numdelim
+        end
+        num = numstr:match("^([i])$")
+        if num then
+          return roman2number(string.upper(num)), "LowerRoman", numdelim
+        end
+        num = numstr:match("^([A-Z])$")
+        if num then
+          return string.byte(num) - string.byte("A") + 1,
+                 "UpperAlpha", numdelim
+        end
+        num = numstr:match("^([a-z])$")
+        if num then
+          return string.byte(num) - string.byte("a") + 1,
+                 "LowerAlpha", numdelim
+        end
+        num = numstr:match("^([IVXLCDM]+)")
+        if num then
+          return roman2number(num), "UpperRoman", numdelim
+        end
+        num = numstr:match("^([ivxlcdm]+)")
+        if num then
+          return roman2number(string.upper(num)), "LowerRoman", numdelim
+        end
+        return math.floor(tonumber(numstr) or 1), "Decimal", numdelim
+      end
+
+      local function fancylist(items,tight,start)
+        local startnum, numstyle, numdelim
+          = sniffstyle(start[2][1], start[2][2])
+        return writer.fancylist(items,tight,
+                                options.startNumber and startnum or 1,
+                                numstyle or "Decimal",
+                                numdelim or "Default")
+      end
+
+      local FancyListOfType
+        = function(start_marker, continuation_marker, delimiter_type)
+          local enumerator_start
+            = enumerator(start_marker, continuation_marker,
+                         delimiter_type)
+          local enumerator_cont
+            = enumerator(continuation_marker, continuation_marker,
+                         delimiter_type)
+          return Cg(enumerator_start, "listtype")
+               * (Ct( TightListItem(Cb("listtype"))
+                   * ((parsers.check_minimal_indent / "")
+                   * TightListItem(enumerator_cont))^0)
+               * Cc(true)
+               * -#((parsers.conditionally_indented_blankline^0 / "")
+                   * parsers.check_minimal_indent * enumerator_cont)
+               + Ct( LooseListItem(Cb("listtype"))
+                   * ((parsers.conditionally_indented_blankline^0 / "")
+                     * (parsers.check_minimal_indent / "")
+                     * LooseListItem(enumerator_cont))^0)
+               * Cc(false)
+               ) * Ct(Cb("listtype")) / fancylist
+        end
+
+      local FancyList
+        = join_table_with_func(FancyListOfType, markers_table)
+
+      local Endline   = parsers.newline
+                      * (parsers.check_minimal_indent
+                        * -parsers.EndlineExceptions
+                        + parsers.check_optional_indent
+                        * -parsers.EndlineExceptions
+                        * -starter)
+                      * parsers.spacechar^0
+                      / writer.soft_line_break
+
+      self.update_rule("OrderedList", FancyList)
+      self.update_rule("Endline", Endline)
+    end
+  }
+end
+M.extensions.fenced_code = function(blank_before_code_fence,
+                                    allow_attributes,
+                                    allow_raw_blocks)
+  return {
+    name = "built-in fenced_code syntax extension",
+    extend_writer = function(self)
+      local options = self.options
+
+      function self.fencedCode(s, i, attr)
+        if not self.is_writing then return "" end
+        s = s:gsub("\n$", "")
+        local buf = {}
+        if attr ~= nil then
+          table.insert(buf,
+            {"\\markdownRendererFencedCodeAttributeContextBegin",
+             self.attributes(attr)})
+        end
+        local name = util.cache_verbatim(options.cacheDir, s)
+        table.insert(buf,
+          {"\\markdownRendererInputFencedCode{",
+           name,"}{",self.string(i),"}{",self.infostring(i),"}"})
+        if attr ~= nil then
+          table.insert(buf,
+            "\\markdownRendererFencedCodeAttributeContextEnd{}")
+        end
+        return buf
+      end
+
+      if allow_raw_blocks then
+        function self.rawBlock(s, attr)
+          if not self.is_writing then return "" end
+          s = s:gsub("\n$", "")
+          local name = util.cache_verbatim(options.cacheDir, s)
+          return {"\\markdownRendererInputRawBlock{",
+                  name,"}{", self.string(attr),"}"}
+        end
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local function captures_geq_length(_,i,a,b)
+        return #a >= #b and i
+      end
+
+      local function strip_enclosing_whitespaces(str)
+        return str:gsub("^%s*(.-)%s*$", "%1")
+      end
+
+      local tilde_infostring = Cs(Cs((V("HtmlEntity")
+                                     + parsers.anyescaped
+                                     - parsers.newline)^0)
+                                 / strip_enclosing_whitespaces)
+
+      local backtick_infostring
+        = Cs( Cs((V("HtmlEntity")
+            + ( -#(parsers.backslash * parsers.backtick)
+              * parsers.anyescaped)
+              - parsers.newline
+              - parsers.backtick)^0)
+        / strip_enclosing_whitespaces)
+
+      local fenceindent
+
+      local function has_trail(indent_table)
+        return indent_table ~= nil and
+          indent_table.trail ~= nil and
+          next(indent_table.trail) ~= nil
+      end
+
+      local function has_indents(indent_table)
+        return indent_table ~= nil and
+          indent_table.indents ~= nil and
+          next(indent_table.indents) ~= nil
+      end
+
+      local function get_last_indent_name(indent_table)
+        if has_indents(indent_table) then
+          return indent_table.indents[#indent_table.indents].name
+        end
+      end
+
+      local count_fenced_start_indent =
+        function(_, _, indent_table, trail)
+          local last_indent_name = get_last_indent_name(indent_table)
+          fenceindent = 0
+          if last_indent_name ~= "li" then
+            fenceindent = #trail
+          end
+          return true
+        end
+
+      local fencehead = function(char, infostring)
+        return Cmt( Cb("indent_info")
+                  * parsers.check_trail, count_fenced_start_indent)
+             * Cg(char^3, "fencelength")
+             * parsers.optionalspace
+             * infostring
+             * (parsers.newline + parsers.eof)
+      end
+
+      local fencetail = function(char)
+        return parsers.check_trail_no_rem
+             * Cmt(C(char^3) * Cb("fencelength"), captures_geq_length)
+             * parsers.optionalspace * (parsers.newline + parsers.eof)
+             + parsers.eof
+      end
+
+      local process_fenced_line =
+        function(s, i, -- luacheck: ignore s i
+                 indent_table, line_content, is_blank)
+          local remainder = ""
+          if has_trail(indent_table) then
+            remainder = indent_table.trail.internal_remainder
+          end
+
+          if is_blank
+             and get_last_indent_name(indent_table) == "li" then
+            remainder = ""
+          end
+
+          local str = remainder .. line_content
+          local index = 1
+          local remaining = fenceindent
+
+          while true do
+            local c = str:sub(index, index)
+            if c == " " and remaining > 0 then
+              remaining = remaining - 1
+              index = index + 1
+            elseif c == "\t" and remaining > 3 then
+              remaining = remaining - 4
+              index = index + 1
+            else
+              break
+            end
+          end
+
+          return true, str:sub(index)
+        end
+
+      local fencedline = function(char)
+        return Cmt( Cb("indent_info")
+                  * C(parsers.line - fencetail(char))
+                  * Cc(false), process_fenced_line)
+      end
+
+      local blankfencedline
+        = Cmt( Cb("indent_info")
+             * C(parsers.blankline)
+             * Cc(true), process_fenced_line)
+
+      local TildeFencedCode
+        = fencehead(parsers.tilde, tilde_infostring)
+        * Cs(( (parsers.check_minimal_blank_indent / "")
+             * blankfencedline
+             + ( parsers.check_minimal_indent / "")
+               * fencedline(parsers.tilde))^0)
+        * ( (parsers.check_minimal_indent / "")
+          * fencetail(parsers.tilde) + parsers.succeed)
+
+      local BacktickFencedCode
+             = fencehead(parsers.backtick, backtick_infostring)
+             * Cs(( (parsers.check_minimal_blank_indent / "")
+                  * blankfencedline
+                  + (parsers.check_minimal_indent / "")
+                  * fencedline(parsers.backtick))^0)
+             * ( (parsers.check_minimal_indent / "")
+               * fencetail(parsers.backtick) + parsers.succeed)
+
+      local infostring_with_attributes
+                        = Ct(C((parsers.linechar
+                              - ( parsers.optionalspace
+                                * parsers.attributes))^0)
+                            * parsers.optionalspace
+                            * Ct(parsers.attributes))
+
+      local FencedCode
+        = ((TildeFencedCode + BacktickFencedCode)
+        / function(infostring, code)
+          local expanded_code = self.expandtabs(code)
+
+          if allow_raw_blocks then
+            local raw_attr = lpeg.match(parsers.raw_attribute,
+                                        infostring)
+            if raw_attr then
+              return writer.rawBlock(expanded_code, raw_attr)
+            end
+          end
+
+          local attr = nil
+          if allow_attributes then
+            local match = lpeg.match(infostring_with_attributes,
+                                     infostring)
+            if match then
+              infostring, attr = table.unpack(match)
+            end
+          end
+          return writer.fencedCode(expanded_code, infostring, attr)
+        end)
+
+      self.insert_pattern("Block after Verbatim",
+                          FencedCode, "FencedCode")
+
+      local fencestart
+      if blank_before_code_fence then
+        fencestart = parsers.fail
+      else
+        fencestart = fencehead(parsers.backtick, backtick_infostring)
+                   + fencehead(parsers.tilde, tilde_infostring)
+      end
+
+      self.update_rule("EndlineExceptions", function(previous_pattern)
+        if previous_pattern == nil then
+          previous_pattern = parsers.EndlineExceptions
+        end
+        return previous_pattern + fencestart
+      end)
+
+      self.add_special_character("`")
+      self.add_special_character("~")
+    end
+  }
+end
+M.extensions.fenced_divs = function(blank_before_div_fence)
+  return {
+    name = "built-in fenced_divs syntax extension",
+    extend_writer = function(self)
+      function self.div_begin(attributes)
+        local start_output
+          = {"\\markdownRendererFencedDivAttributeContextBegin\n",
+             self.attributes(attributes)}
+        local end_output
+          = {"\\markdownRendererFencedDivAttributeContextEnd{}"}
+        return self.push_attributes(
+          "div", attributes, start_output, end_output)
+      end
+      function self.div_end()
+        return self.pop_attributes("div")
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+      local fenced_div_infostring
+                             = C((parsers.linechar
+                                - ( parsers.spacechar^1
+                                  * parsers.colon^1))^1)
+
+      local fenced_div_begin = parsers.nonindentspace
+                             * parsers.colon^3
+                             * parsers.optionalspace
+                             * fenced_div_infostring
+                             * ( parsers.spacechar^1
+                               * parsers.colon^1)^0
+                             * parsers.optionalspace
+                             * (parsers.newline + parsers.eof)
+
+      local fenced_div_end = parsers.nonindentspace
+                           * parsers.colon^3
+                           * parsers.optionalspace
+                           * (parsers.newline + parsers.eof)
+      self.initialize_named_group("fenced_div_level", "0")
+      self.initialize_named_group("fenced_div_num_opening_indents")
+
+      local function increment_div_level()
+        local push_indent_table =
+          function(s, i, indent_table, -- luacheck: ignore s i
+                   fenced_div_num_opening_indents, fenced_div_level)
+            fenced_div_level = tonumber(fenced_div_level) + 1
+            local num_opening_indents = 0
+            if indent_table.indents ~= nil then
+              num_opening_indents = #indent_table.indents
+            end
+            fenced_div_num_opening_indents[fenced_div_level]
+              = num_opening_indents
+            return true, fenced_div_num_opening_indents
+          end
+
+        local increment_level =
+          function(s, i, fenced_div_level) -- luacheck: ignore s i
+            fenced_div_level = tonumber(fenced_div_level) + 1
+            return true, tostring(fenced_div_level)
+          end
+
+        return Cg( Cmt( Cb("indent_info")
+                      * Cb("fenced_div_num_opening_indents")
+                      * Cb("fenced_div_level"), push_indent_table)
+                 , "fenced_div_num_opening_indents")
+             * Cg( Cmt( Cb("fenced_div_level"), increment_level)
+                 , "fenced_div_level")
+      end
+
+      local function decrement_div_level()
+        local pop_indent_table =
+          function(s, i, -- luacheck: ignore s i
+                   fenced_div_indent_table, fenced_div_level)
+            fenced_div_level = tonumber(fenced_div_level)
+            fenced_div_indent_table[fenced_div_level] = nil
+            return true, tostring(fenced_div_level - 1)
+          end
+
+        return Cg( Cmt( Cb("fenced_div_num_opening_indents")
+                      * Cb("fenced_div_level"), pop_indent_table)
+                 , "fenced_div_level")
+      end
+
+      local non_fenced_div_block
+        = parsers.check_minimal_indent * V("Block")
+        - parsers.check_minimal_indent_and_trail * fenced_div_end
+
+      local non_fenced_div_paragraph
+        = parsers.check_minimal_indent * V("Paragraph")
+        - parsers.check_minimal_indent_and_trail * fenced_div_end
+
+      local blank = parsers.minimally_indented_blank
+
+      local block_separated = parsers.block_sep_group(blank)
+                            * non_fenced_div_block
+
+      local loop_body_pair
+        = parsers.create_loop_body_pair(block_separated,
+                                        non_fenced_div_paragraph,
+                                        parsers.block_sep_group(blank),
+                                        parsers.par_sep_group(blank))
+
+      local content_loop  = ( non_fenced_div_block
+                            * loop_body_pair.block^0
+                            + non_fenced_div_paragraph
+                            * block_separated
+                            * loop_body_pair.block^0
+                            + non_fenced_div_paragraph
+                            * loop_body_pair.par^0)
+                          * blank^0
+
+      local FencedDiv = fenced_div_begin
+                      / function (infostring)
+                          local attr
+                            = lpeg.match(Ct(parsers.attributes),
+                                         infostring)
+                          if attr == nil then
+                            attr = {"." .. infostring}
+                          end
+                          return attr
+                        end
+                      / writer.div_begin
+                      * increment_div_level()
+                      * parsers.skipblanklines
+                      * Ct(content_loop)
+                      * parsers.minimally_indented_blank^0
+                      * parsers.check_minimal_indent_and_trail
+                      * fenced_div_end
+                      * decrement_div_level()
+                      * (Cc("") / writer.div_end)
+
+      self.insert_pattern("Block after Verbatim",
+                          FencedDiv, "FencedDiv")
+
+      self.add_special_character(":")
+
+      local function is_inside_div()
+        local check_div_level =
+          function(s, i, fenced_div_level) -- luacheck: ignore s i
+            fenced_div_level = tonumber(fenced_div_level)
+            return fenced_div_level > 0
+          end
+
+        return Cmt(Cb("fenced_div_level"), check_div_level)
+      end
+
+      local function check_indent()
+        local compare_indent =
+          function(s, i, indent_table, -- luacheck: ignore s i
+                   fenced_div_num_opening_indents, fenced_div_level)
+            fenced_div_level = tonumber(fenced_div_level)
+            local num_current_indents
+              = ( indent_table.current_line_indents ~= nil and
+                 #indent_table.current_line_indents) or 0
+            local num_opening_indents
+              = fenced_div_num_opening_indents[fenced_div_level]
+            return num_current_indents == num_opening_indents
+          end
+
+        return Cmt( Cb("indent_info")
+                  * Cb("fenced_div_num_opening_indents")
+                  * Cb("fenced_div_level"), compare_indent)
+      end
+
+      local fencestart = is_inside_div()
+                       * fenced_div_end
+                       * check_indent()
+
+      if not blank_before_div_fence then
+        self.update_rule("EndlineExceptions", function(previous_pattern)
+          if previous_pattern == nil then
+            previous_pattern = parsers.EndlineExceptions
+          end
+          return previous_pattern + fencestart
+        end)
+      end
+    end
+  }
+end
+M.extensions.header_attributes = function()
+  return {
+    name = "built-in header_attributes syntax extension",
+    extend_writer = function()
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local function strip_atx_end(s)
+        return s:gsub("%s+#*%s*$","")
+      end
+
+      local AtxHeading = Cg(parsers.heading_start, "level")
+                       * parsers.optionalspace
+                       * (C(((parsers.linechar
+                              - (parsers.attributes
+                                * parsers.optionalspace
+                                * parsers.newline))
+                             * (parsers.linechar
+                               - parsers.lbrace)^0)^1)
+                             / strip_atx_end
+                             / parsers.parse_heading_text)
+                       * Cg(Ct(parsers.newline
+                              + (parsers.attributes
+                                * parsers.optionalspace
+                                * parsers.newline)), "attributes")
+                       * Cb("level")
+                       * Cb("attributes")
+                       / writer.heading
+
+      local function strip_trailing_spaces(s)
+        return s:gsub("%s*$","")
+      end
+
+      local heading_line  = (parsers.linechar
+                            - (parsers.attributes
+                              * parsers.optionalspace
+                              * parsers.newline))^1
+                          - parsers.thematic_break_lines
+
+      local heading_text
+        = heading_line
+        * ( (V("Endline") / "\n")
+          * (heading_line - parsers.heading_level))^0
+        * parsers.newline^-1
+
+      local SetextHeading
+        = parsers.freeze_trail * parsers.check_trail_no_rem
+        * #(heading_text
+           * (parsers.attributes
+             * parsers.optionalspace
+             * parsers.newline)^-1
+           * parsers.check_minimal_indent
+           * parsers.check_trail
+           * parsers.heading_level)
+        * Cs(heading_text) / strip_trailing_spaces
+        / parsers.parse_heading_text
+        * Cg(Ct((parsers.attributes
+              * parsers.optionalspace
+              * parsers.newline)^-1), "attributes")
+        * parsers.check_minimal_indent_and_trail * parsers.heading_level
+        * Cb("attributes")
+        * parsers.newline
+        * parsers.unfreeze_trail
+        / writer.heading
+
+      local Heading = AtxHeading + SetextHeading
+      self.update_rule("Heading", Heading)
+    end
+  }
+end
+M.extensions.inline_code_attributes = function()
+  return {
+    name = "built-in inline_code_attributes syntax extension",
+    extend_writer = function()
+    end, extend_reader = function(self)
+      local writer = self.writer
+
+      local CodeWithAttributes = parsers.inticks
+                               * Ct(parsers.attributes)
+                               / writer.code
+
+      self.insert_pattern("Inline before Code",
+                          CodeWithAttributes,
+                          "CodeWithAttributes")
+    end
+  }
+end
+M.extensions.line_blocks = function()
+  return {
+    name = "built-in line_blocks syntax extension",
+    extend_writer = function(self)
+      function self.lineblock(lines)
+        if not self.is_writing then return "" end
+        local buffer = {}
+        for i = 1, #lines - 1 do
+          buffer[#buffer + 1] = { lines[i], self.hard_line_break }
+        end
+        buffer[#buffer + 1] = lines[#lines]
+
+        return {"\\markdownRendererLineBlockBegin\n"
+                  ,buffer,
+                  "\n\\markdownRendererLineBlockEnd "}
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local LineBlock
+        = Ct((Cs(( (parsers.pipe * parsers.space) / ""
+                 * ((parsers.space)/entities.char_entity("nbsp"))^0
+                 * parsers.linechar^0 * (parsers.newline/""))
+                 * (-parsers.pipe
+                   * (parsers.space^1/" ")
+                   * parsers.linechar^1
+                   * (parsers.newline/"")
+                   )^0
+                 * (parsers.blankline/"")^0)
+             / self.parser_functions.parse_inlines)^1)
+        / writer.lineblock
+
+      self.insert_pattern("Block after Blockquote",
+                           LineBlock, "LineBlock")
+    end
+  }
+end
+M.extensions.mark = function()
+  return {
+    name = "built-in mark syntax extension",
+    extend_writer = function(self)
+      function self.mark(s)
+        if self.flatten_inlines then return s end
+        return {"\\markdownRendererMark{", s, "}"}
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local doubleequals = P("==")
+
+      local Mark
+        = parsers.between(V("Inline"), doubleequals, doubleequals)
+        / function (inlines) return writer.mark(inlines) end
+
+      self.add_special_character("=")
+      self.insert_pattern("Inline before LinkAndEmph",
+                          Mark, "Mark")
+    end
+  }
+end
+M.extensions.link_attributes = function()
+  return {
+    name = "built-in link_attributes syntax extension",
+    extend_writer = function()
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local options = self.options
+
+      local define_reference_parser
+        = (parsers.check_trail / "")
+        * parsers.link_label
+        * parsers.colon
+        * parsers.spnlc * parsers.url
+        * ( parsers.spnlc_sep * parsers.title
+          * (parsers.spnlc * Ct(parsers.attributes))
+          * parsers.only_blank
+          + parsers.spnlc_sep * parsers.title * parsers.only_blank
+          + Cc("") * (parsers.spnlc * Ct(parsers.attributes))
+          * parsers.only_blank
+          + Cc("") * parsers.only_blank)
+
+      local ReferenceWithAttributes = define_reference_parser
+                                    / self.register_link
+
+      self.update_rule("Reference", ReferenceWithAttributes)
+
+
+      local LinkWithAttributesAndEmph
+        = Ct(parsers.link_and_emph_table * Cg(Cc(true),
+             "match_link_attributes"))
+        / self.defer_link_and_emphasis_processing
+
+      self.update_rule("LinkAndEmph", LinkWithAttributesAndEmph)
+
+      local AutoLinkUrlWithAttributes
+                      = parsers.auto_link_url
+                      * Ct(parsers.attributes)
+                      / self.auto_link_url
+
+      self.insert_pattern("Inline before AutoLinkUrl",
+                          AutoLinkUrlWithAttributes,
+                          "AutoLinkUrlWithAttributes")
+
+      local AutoLinkEmailWithAttributes
+                      = parsers.auto_link_email
+                      * Ct(parsers.attributes)
+                      / self.auto_link_email
+
+      self.insert_pattern("Inline before AutoLinkEmail",
+                          AutoLinkEmailWithAttributes,
+                          "AutoLinkEmailWithAttributes")
+
+      if options.relativeReferences then
+
+        local AutoLinkRelativeReferenceWithAttributes
+                        = parsers.auto_link_relative_reference
+                        * Ct(parsers.attributes)
+                        / self.auto_link_url
+
+        self.insert_pattern(
+          "Inline before AutoLinkRelativeReference",
+          AutoLinkRelativeReferenceWithAttributes,
+          "AutoLinkRelativeReferenceWithAttributes")
+
+      end
+
+    end
+  }
+end
+M.extensions.notes = function(notes, inline_notes)
+  assert(notes or inline_notes)
+  return {
+    name = "built-in notes syntax extension",
+    extend_writer = function(self)
+      function self.note(s)
+        if self.flatten_inlines then return "" end
+        return {"\\markdownRendererNote{",s,"}"}
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local rawnotes = parsers.rawnotes
+
+      if inline_notes then
+        local InlineNote
+          = parsers.circumflex
+          * ( parsers.link_label
+            / self.parser_functions.parse_inlines_no_inline_note)
+          / writer.note
+
+        self.insert_pattern("Inline after LinkAndEmph",
+                            InlineNote, "InlineNote")
+      end
+      if notes then
+        local function strip_first_char(s)
+          return s:sub(2)
+        end
+
+        local RawNoteRef
+                      = #(parsers.lbracket * parsers.circumflex)
+                      * parsers.link_label / strip_first_char
+
+        -- like indirect_link
+        local function lookup_note(ref)
+          return writer.defer_call(function()
+            local found = rawnotes[self.normalize_tag(ref)]
+            if found then
+              return writer.note(
+                self.parser_functions.parse_blocks_nested(found))
+            else
+              return {"[",
+                self.parser_functions.parse_inlines("^" .. ref), "]"}
+            end
+          end)
+        end
+
+        local function register_note(ref,rawnote)
+          local normalized_tag = self.normalize_tag(ref)
+          if rawnotes[normalized_tag] == nil then
+            rawnotes[normalized_tag] = rawnote
+          end
+          return ""
+        end
+
+        local NoteRef = RawNoteRef / lookup_note
+
+        local optionally_indented_line
+          = parsers.check_optional_indent_and_any_trail * parsers.line
+
+        local blank
+          = parsers.check_optional_blank_indent_and_any_trail
+          * parsers.optionalspace * parsers.newline
+
+        local chunk
+          = Cs(parsers.line
+          * (optionally_indented_line - blank)^0)
+
+        local indented_blocks = function(bl)
+          return Cs( bl
+                * ( blank^1 * (parsers.check_optional_indent / "")
+                  * parsers.check_code_trail
+                  * -parsers.blankline * bl)^0)
+        end
+
+        local NoteBlock
+                    = parsers.check_trail_no_rem
+                    * RawNoteRef * parsers.colon
+                    * parsers.spnlc * indented_blocks(chunk)
+                    / register_note
+
+        local Reference = NoteBlock + parsers.Reference
+
+        self.update_rule("Reference", Reference)
+        self.insert_pattern("Inline before LinkAndEmph",
+                            NoteRef, "NoteRef")
+      end
+
+      self.add_special_character("^")
+    end
+  }
+end
+M.extensions.pipe_tables = function(table_captions, table_attributes)
+
+  local function make_pipe_table_rectangular(rows)
+    local num_columns = #rows[2]
+    local rectangular_rows = {}
+    for i = 1, #rows do
+      local row = rows[i]
+      local rectangular_row = {}
+      for j = 1, num_columns do
+        rectangular_row[j] = row[j] or ""
+      end
+      table.insert(rectangular_rows, rectangular_row)
+    end
+    return rectangular_rows
+  end
+
+  local function pipe_table_row(allow_empty_first_column
+                               , nonempty_column
+                               , column_separator
+                               , column)
+    local row_beginning
+    if allow_empty_first_column then
+      row_beginning = -- empty first column
+                      #(parsers.spacechar^4
+                       * column_separator)
+                    * parsers.optionalspace
+                    * column
+                    * parsers.optionalspace
+                    -- non-empty first column
+                    + parsers.nonindentspace
+                    * nonempty_column^-1
+                    * parsers.optionalspace
+    else
+      row_beginning = parsers.nonindentspace
+                    * nonempty_column^-1
+                    * parsers.optionalspace
+    end
+
+    return Ct(row_beginning
+             * (-- single column with no leading pipes
+                #(column_separator
+                 * parsers.optionalspace
+                 * parsers.newline)
+               * column_separator
+               * parsers.optionalspace
+               -- single column with leading pipes or
+               -- more than a single column
+               + (column_separator
+                 * parsers.optionalspace
+                 * column
+                 * parsers.optionalspace)^1
+               * (column_separator
+                 * parsers.optionalspace)^-1))
+  end
+
+  return {
+    name = "built-in pipe_tables syntax extension",
+    extend_writer = function(self)
+      function self.table(rows, caption, attributes)
+        if not self.is_writing then return "" end
+        local buffer = {}
+        if attributes ~= nil then
+          table.insert(buffer,
+                       "\\markdownRendererTableAttributeContextBegin\n")
+          table.insert(buffer, self.attributes(attributes))
+        end
+        table.insert(buffer,
+                     {"\\markdownRendererTable{",
+                      caption or "", "}{", #rows - 1, "}{",
+                      #rows[1], "}"})
+        local temp = rows[2] -- put alignments on the first row
+        rows[2] = rows[1]
+        rows[1] = temp
+        for i, row in ipairs(rows) do
+          table.insert(buffer, "{")
+          for _, column in ipairs(row) do
+            if i > 1 then -- do not use braces for alignments
+              table.insert(buffer, "{")
+            end
+            table.insert(buffer, column)
+            if i > 1 then
+              table.insert(buffer, "}")
+            end
+          end
+          table.insert(buffer, "}")
+        end
+        if attributes ~= nil then
+          table.insert(buffer,
+                       "\\markdownRendererTableAttributeContextEnd{}")
+        end
+        return buffer
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local table_hline_separator = parsers.pipe + parsers.plus
+
+      local table_hline_column = (parsers.dash
+                                 - #(parsers.dash
+                                    * (parsers.spacechar
+                                      + table_hline_separator
+                                      + parsers.newline)))^1
+                               * (parsers.colon * Cc("r")
+                                 + parsers.dash * Cc("d"))
+                               + parsers.colon
+                               * (parsers.dash
+                                 - #(parsers.dash
+                                    * (parsers.spacechar
+                                      + table_hline_separator
+                                      + parsers.newline)))^1
+                               * (parsers.colon * Cc("c")
+                                 + parsers.dash * Cc("l"))
+
+      local table_hline = pipe_table_row(false
+                                        , table_hline_column
+                                        , table_hline_separator
+                                        , table_hline_column)
+
+      local table_caption_beginning
+        = ( parsers.check_minimal_blank_indent_and_any_trail_no_rem
+          * parsers.optionalspace * parsers.newline)^0
+        * parsers.check_minimal_indent_and_trail
+        * (P("Table")^-1 * parsers.colon)
+        * parsers.optionalspace
+
+      local function strip_trailing_spaces(s)
+        return s:gsub("%s*$","")
+      end
+
+      local table_row
+        = pipe_table_row(true
+                        , (C((parsers.linechar - parsers.pipe)^1)
+                          / strip_trailing_spaces
+                          / self.parser_functions.parse_inlines)
+                        , parsers.pipe
+                        , (C((parsers.linechar - parsers.pipe)^0)
+                          / strip_trailing_spaces
+                          / self.parser_functions.parse_inlines))
+
+      local table_caption
+      if table_captions then
+        table_caption = #table_caption_beginning
+                      * table_caption_beginning
+        if table_attributes then
+          table_caption = table_caption
+                        * (C(((( parsers.linechar
+                               - (parsers.attributes
+                                 * parsers.optionalspace
+                                 * parsers.newline
+                                 * -#( parsers.optionalspace
+                                     * parsers.linechar)))
+                              + ( parsers.newline
+                                * #( parsers.optionalspace
+                                   * parsers.linechar)
+                                * C(parsers.optionalspace)
+                                / writer.space))
+                             * (parsers.linechar
+                               - parsers.lbrace)^0)^1)
+                             / self.parser_functions.parse_inlines)
+                        * (parsers.newline
+                          + ( Ct(parsers.attributes)
+                            * parsers.optionalspace
+                            * parsers.newline))
+        else
+          table_caption = table_caption
+                        * C(( parsers.linechar
+                            + ( parsers.newline
+                              * #( parsers.optionalspace
+                                 * parsers.linechar)
+                              * C(parsers.optionalspace)
+                              / writer.space))^1)
+                        / self.parser_functions.parse_inlines
+                        * parsers.newline
+        end
+      else
+        table_caption = parsers.fail
+      end
+
+      local PipeTable
+        = Ct( table_row * parsers.newline
+            * (parsers.check_minimal_indent_and_trail / {})
+          * table_hline * parsers.newline
+          * ( (parsers.check_minimal_indent / {})
+            * table_row * parsers.newline)^0)
+        / make_pipe_table_rectangular
+        * table_caption^-1
+        / writer.table
+
+      self.insert_pattern("Block after Blockquote",
+                          PipeTable, "PipeTable")
+    end
+  }
+end
+M.extensions.raw_inline = function()
+  return {
+    name = "built-in raw_inline syntax extension",
+    extend_writer = function(self)
+      local options = self.options
+
+      function self.rawInline(s, attr)
+        if not self.is_writing then return "" end
+        if self.flatten_inlines then return s end
+        local name = util.cache_verbatim(options.cacheDir, s)
+        return {"\\markdownRendererInputRawInline{",
+                name,"}{", self.string(attr),"}"}
+      end
+    end, extend_reader = function(self)
+      local writer = self.writer
+
+      local RawInline = parsers.inticks
+                      * parsers.raw_attribute
+                      / writer.rawInline
+
+      self.insert_pattern("Inline before Code",
+                          RawInline, "RawInline")
+    end
+  }
+end
+M.extensions.strike_through = function()
+  return {
+    name = "built-in strike_through syntax extension",
+    extend_writer = function(self)
+      function self.strike_through(s)
+        if self.flatten_inlines then return s end
+        return {"\\markdownRendererStrikeThrough{",s,"}"}
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local StrikeThrough = (
+        parsers.between(parsers.Inline, parsers.doubletildes,
+                        parsers.doubletildes)
+      ) / writer.strike_through
+
+      self.insert_pattern("Inline after LinkAndEmph",
+                          StrikeThrough, "StrikeThrough")
+
+      self.add_special_character("~")
+    end
+  }
+end
+M.extensions.subscripts = function()
+  return {
+    name = "built-in subscripts syntax extension",
+    extend_writer = function(self)
+      function self.subscript(s)
+        if self.flatten_inlines then return s end
+        return {"\\markdownRendererSubscript{",s,"}"}
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local Subscript = (
+        parsers.between(parsers.Str, parsers.tilde, parsers.tilde)
+      ) / writer.subscript
+
+      self.insert_pattern("Inline after LinkAndEmph",
+                          Subscript, "Subscript")
+
+      self.add_special_character("~")
+    end
+  }
+end
+M.extensions.superscripts = function()
+  return {
+    name = "built-in superscripts syntax extension",
+    extend_writer = function(self)
+      function self.superscript(s)
+        if self.flatten_inlines then return s end
+        return {"\\markdownRendererSuperscript{",s,"}"}
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local Superscript = (
+        parsers.between(parsers.Str, parsers.circumflex,
+                        parsers.circumflex)
+      ) / writer.superscript
+
+      self.insert_pattern("Inline after LinkAndEmph",
+                          Superscript, "Superscript")
+
+      self.add_special_character("^")
+    end
+  }
+end
+M.extensions.tex_math = function(tex_math_dollars,
+                                 tex_math_single_backslash,
+                                 tex_math_double_backslash)
+  return {
+    name = "built-in tex_math syntax extension",
+    extend_writer = function(self)
+      function self.display_math(s)
+        if self.flatten_inlines then return s end
+        return {"\\markdownRendererDisplayMath{",self.math(s),"}"}
+      end
+      function self.inline_math(s)
+        if self.flatten_inlines then return s end
+        return {"\\markdownRendererInlineMath{",self.math(s),"}"}
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local function between(p, starter, ender)
+        return (starter * Cs(p * (p - ender)^0) * ender)
+      end
+
+      local function strip_preceding_whitespaces(str)
+        return str:gsub("^%s*(.-)$", "%1")
+      end
+
+      local allowed_before_closing
+        = B( parsers.backslash * parsers.any
+           + parsers.any * (parsers.any - parsers.backslash))
+
+      local allowed_before_closing_no_space
+        = B( parsers.backslash * parsers.any
+           + parsers.any * (parsers.nonspacechar - parsers.backslash))
+
+      local dollar_math_content
+        = (parsers.newline * (parsers.check_optional_indent / "")
+        + parsers.backslash^-1
+        * parsers.linechar)
+        - parsers.blankline^2
+        - parsers.dollar
+
+      local inline_math_opening_dollars = parsers.dollar
+                                        * #(parsers.nonspacechar)
+
+      local inline_math_closing_dollars
+        = allowed_before_closing_no_space
+        * parsers.dollar
+        * -#(parsers.digit)
+
+      local inline_math_dollars = between(Cs( dollar_math_content),
+                                          inline_math_opening_dollars,
+                                          inline_math_closing_dollars)
+
+      local display_math_opening_dollars  = parsers.dollar
+                                          * parsers.dollar
+
+      local display_math_closing_dollars  = parsers.dollar
+                                          * parsers.dollar
+
+      local display_math_dollars = between(Cs( dollar_math_content),
+                                           display_math_opening_dollars,
+                                           display_math_closing_dollars)
+      local backslash_math_content
+        = (parsers.newline * (parsers.check_optional_indent / "")
+        + parsers.linechar)
+        - parsers.blankline^2
+      local inline_math_opening_double  = parsers.backslash
+                                        * parsers.backslash
+                                        * parsers.lparent
+
+      local inline_math_closing_double  = allowed_before_closing
+                                        * parsers.spacechar^0
+                                        * parsers.backslash
+                                        * parsers.backslash
+                                        * parsers.rparent
+
+      local inline_math_double  = between(Cs( backslash_math_content),
+                                          inline_math_opening_double,
+                                          inline_math_closing_double)
+                                / strip_preceding_whitespaces
+
+      local display_math_opening_double = parsers.backslash
+                                        * parsers.backslash
+                                        * parsers.lbracket
+
+      local display_math_closing_double = allowed_before_closing
+                                        * parsers.spacechar^0
+                                        * parsers.backslash
+                                        * parsers.backslash
+                                        * parsers.rbracket
+
+      local display_math_double = between(Cs( backslash_math_content),
+                                          display_math_opening_double,
+                                          display_math_closing_double)
+                                / strip_preceding_whitespaces
+      local inline_math_opening_single  = parsers.backslash
+                                        * parsers.lparent
+
+      local inline_math_closing_single  = allowed_before_closing
+                                        * parsers.spacechar^0
+                                        * parsers.backslash
+                                        * parsers.rparent
+
+      local inline_math_single  = between(Cs( backslash_math_content),
+                                          inline_math_opening_single,
+                                          inline_math_closing_single)
+                                / strip_preceding_whitespaces
+
+      local display_math_opening_single = parsers.backslash
+                                        * parsers.lbracket
+
+      local display_math_closing_single = allowed_before_closing
+                                        * parsers.spacechar^0
+                                        * parsers.backslash
+                                        * parsers.rbracket
+
+      local display_math_single = between(Cs( backslash_math_content),
+                                          display_math_opening_single,
+                                          display_math_closing_single)
+                                / strip_preceding_whitespaces
+
+      local display_math = parsers.fail
+
+      local inline_math = parsers.fail
+
+      if tex_math_dollars then
+        display_math = display_math + display_math_dollars
+        inline_math = inline_math + inline_math_dollars
+      end
+
+      if tex_math_double_backslash then
+        display_math = display_math + display_math_double
+        inline_math = inline_math + inline_math_double
+      end
+
+      if tex_math_single_backslash then
+        display_math = display_math + display_math_single
+        inline_math = inline_math + inline_math_single
+      end
+
+      local TexMath = display_math / writer.display_math
+                    + inline_math / writer.inline_math
+
+      self.insert_pattern("Inline after LinkAndEmph",
+                          TexMath, "TexMath")
+
+      if tex_math_dollars then
+        self.add_special_character("$")
+      end
+
+      if tex_math_single_backslash or tex_math_double_backslash then
+        self.add_special_character("\\")
+        self.add_special_character("[")
+        self.add_special_character("]")
+        self.add_special_character(")")
+        self.add_special_character("(")
+      end
+    end
+  }
+end
+M.extensions.jekyll_data = function(expect_jekyll_data,
+                                    ensure_jekyll_data)
+  return {
+    name = "built-in jekyll_data syntax extension",
+    extend_writer = function(self)
+      function self.jekyllData(d, t, p)
+        if not self.is_writing then return "" end
+
+        local buf = {}
+
+        local keys = {}
+        for k, _ in pairs(d) do
+          table.insert(keys, k)
+        end
+        table.sort(keys, function(first, second)
+          if type(first) ~= type(second) then
+            return type(first) < type(second)
+          else
+            return first < second
+          end
+        end)
+
+        if not p then
+          table.insert(buf, "\\markdownRendererJekyllDataBegin")
+        end
+
+        local is_sequence = false
+        if #d > 0 and #d == #keys then
+          for i=1, #d do
+            if d[i] == nil then
+              goto not_a_sequence
+            end
+          end
+          is_sequence = true
+        end
+        ::not_a_sequence::
+
+        if is_sequence then
+          table.insert(buf,
+            "\\markdownRendererJekyllDataSequenceBegin{")
+          table.insert(buf, self.identifier(p or "null"))
+          table.insert(buf, "}{")
+          table.insert(buf, #keys)
+          table.insert(buf, "}")
+        else
+          table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{")
+          table.insert(buf, self.identifier(p or "null"))
+          table.insert(buf, "}{")
+          table.insert(buf, #keys)
+          table.insert(buf, "}")
+        end
+
+        for _, k in ipairs(keys) do
+          local v = d[k]
+          local typ = type(v)
+          k = tostring(k or "null")
+          if typ == "table" and next(v) ~= nil then
+            table.insert(
+              buf,
+              self.jekyllData(v, t, k)
+            )
+          else
+            k = self.identifier(k)
+            v = tostring(v)
+            if typ == "boolean" then
+              table.insert(buf, "\\markdownRendererJekyllDataBoolean{")
+              table.insert(buf, k)
+              table.insert(buf, "}{")
+              table.insert(buf, v)
+              table.insert(buf, "}")
+            elseif typ == "number" then
+              table.insert(buf, "\\markdownRendererJekyllDataNumber{")
+              table.insert(buf, k)
+              table.insert(buf, "}{")
+              table.insert(buf, v)
+              table.insert(buf, "}")
+            elseif typ == "string" then
+              table.insert(buf,
+                "\\markdownRendererJekyllDataProgrammaticString{")
+              table.insert(buf, k)
+              table.insert(buf, "}{")
+              table.insert(buf, self.identifier(v))
+              table.insert(buf, "}")
+              table.insert(buf,
+                "\\markdownRendererJekyllDataTypographicString{")
+              table.insert(buf, k)
+              table.insert(buf, "}{")
+              table.insert(buf, t(v))
+              table.insert(buf, "}")
+            elseif typ == "table" then
+              table.insert(buf, "\\markdownRendererJekyllDataEmpty{")
+              table.insert(buf, k)
+              table.insert(buf, "}")
+            else
+              local error = self.error(format(
+                "Unexpected type %s for value of "
+                .. "YAML key %s", typ, k))
+              table.insert(buf, error)
+            end
+          end
+        end
+
+        if is_sequence then
+          table.insert(buf, "\\markdownRendererJekyllDataSequenceEnd")
+        else
+          table.insert(buf, "\\markdownRendererJekyllDataMappingEnd")
+        end
+
+        if not p then
+          table.insert(buf, "\\markdownRendererJekyllDataEnd")
+        end
+
+        return buf
+      end
+    end, extend_reader = function(self)
+      local parsers = self.parsers
+      local writer = self.writer
+
+      local JekyllData
+        = Cmt( C((parsers.line - P("---") - P("..."))^0)
+             , function(s, i, text) -- luacheck: ignore s i
+                 local data
+                 local ran_ok, _ = pcall(function()
+                   -- TODO: Use `require("tinyyaml")` in TeX Live 2023
+                   local tinyyaml = require("markdown-tinyyaml")
+                   data = tinyyaml.parse(text, {timestamps=false})
+                 end)
+                 if ran_ok and data ~= nil then
+                   return true, writer.jekyllData(data, function(s)
+                     return self.parser_functions.parse_blocks_nested(s)
+                   end, nil)
+                 else
+                   return false
+                 end
+               end
+             )
+
+      local UnexpectedJekyllData
+        = P("---")
+        * parsers.blankline / 0
+        -- if followed by blank, it's thematic break
+        * #(-parsers.blankline)
+        * JekyllData
+        * (P("---") + P("..."))
+
+      local ExpectedJekyllData
+        = ( P("---")
+          * parsers.blankline / 0
+          -- if followed by blank, it's thematic break
+          * #(-parsers.blankline)
+          )^-1
+        * JekyllData
+        * (P("---") + P("..."))^-1
+
+      if ensure_jekyll_data then
+        ExpectedJekyllData = ExpectedJekyllData
+                           * parsers.eof
+      else
+        ExpectedJekyllData = ( ExpectedJekyllData
+                             * (V("Blank")^0 / writer.interblocksep)
+                             )^-1
+      end
+
+      self.insert_pattern("Block before Blockquote",
+                          UnexpectedJekyllData, "UnexpectedJekyllData")
+      if expect_jekyll_data then
+        self.update_rule("ExpectedJekyllData", ExpectedJekyllData)
+      end
+    end
+  }
+end
+function M.new(options)
+  options = options or {}
+  setmetatable(options, { __index = function (_, key)
+    return defaultOptions[key] end })
+  if options.singletonCache and singletonCache.convert then
+    for k, v in pairs(defaultOptions) do
+      if type(v) == "table" then
+        for i = 1, math.max(#singletonCache.options[k], #options[k]) do
+          if singletonCache.options[k][i] ~= options[k][i] then
+            goto miss
+          end
+        end
+      elseif k ~= "cacheDir"
+         and singletonCache.options[k] ~= options[k] then
+        goto miss
+      end
+    end
+    return singletonCache.convert
+  end
+  ::miss::
+  local extensions = {}
+
+  if options.bracketedSpans then
+    local bracketed_spans_extension = M.extensions.bracketed_spans()
+    table.insert(extensions, bracketed_spans_extension)
+  end
+
+  if options.contentBlocks then
+    local content_blocks_extension = M.extensions.content_blocks(
+      options.contentBlocksLanguageMap)
+    table.insert(extensions, content_blocks_extension)
+  end
+
+  if options.definitionLists then
+    local definition_lists_extension = M.extensions.definition_lists(
+      options.tightLists)
+    table.insert(extensions, definition_lists_extension)
+  end
+
+  if options.fencedCode then
+    local fenced_code_extension = M.extensions.fenced_code(
+      options.blankBeforeCodeFence,
+      options.fencedCodeAttributes,
+      options.rawAttribute)
+    table.insert(extensions, fenced_code_extension)
+  end
+
+  if options.fencedDivs then
+    local fenced_div_extension = M.extensions.fenced_divs(
+      options.blankBeforeDivFence)
+    table.insert(extensions, fenced_div_extension)
+  end
+
+  if options.headerAttributes then
+    local header_attributes_extension = M.extensions.header_attributes()
+    table.insert(extensions, header_attributes_extension)
+  end
+
+  if options.inlineCodeAttributes then
+    local inline_code_attributes_extension =
+      M.extensions.inline_code_attributes()
+    table.insert(extensions, inline_code_attributes_extension)
+  end
+
+  if options.jekyllData then
+    local jekyll_data_extension = M.extensions.jekyll_data(
+      options.expectJekyllData, options.ensureJekyllData)
+    table.insert(extensions, jekyll_data_extension)
+  end
+
+  if options.linkAttributes then
+    local link_attributes_extension =
+      M.extensions.link_attributes()
+    table.insert(extensions, link_attributes_extension)
+  end
+
+  if options.lineBlocks then
+    local line_block_extension = M.extensions.line_blocks()
+    table.insert(extensions, line_block_extension)
+  end
+
+  if options.mark then
+    local mark_extension = M.extensions.mark()
+    table.insert(extensions, mark_extension)
+  end
+
+  if options.pipeTables then
+    local pipe_tables_extension = M.extensions.pipe_tables(
+      options.tableCaptions, options.tableAttributes)
+    table.insert(extensions, pipe_tables_extension)
+  end
+
+  if options.rawAttribute then
+    local raw_inline_extension = M.extensions.raw_inline()
+    table.insert(extensions, raw_inline_extension)
+  end
+
+  if options.strikeThrough then
+    local strike_through_extension = M.extensions.strike_through()
+    table.insert(extensions, strike_through_extension)
+  end
+
+  if options.subscripts then
+    local subscript_extension = M.extensions.subscripts()
+    table.insert(extensions, subscript_extension)
+  end
+
+  if options.superscripts then
+    local superscript_extension = M.extensions.superscripts()
+    table.insert(extensions, superscript_extension)
+  end
+
+  if options.texMathDollars or
+     options.texMathSingleBackslash or
+     options.texMathDoubleBackslash then
+    local tex_math_extension = M.extensions.tex_math(
+      options.texMathDollars,
+      options.texMathSingleBackslash,
+      options.texMathDoubleBackslash)
+    table.insert(extensions, tex_math_extension)
+  end
+
+  if options.notes or options.inlineNotes then
+    local notes_extension = M.extensions.notes(
+      options.notes, options.inlineNotes)
+    table.insert(extensions, notes_extension)
+  end
+
+  if options.citations then
+    local citations_extension
+      = M.extensions.citations(options.citationNbsps)
+    table.insert(extensions, citations_extension)
+  end
+
+  if options.fancyLists then
+    local fancy_lists_extension = M.extensions.fancy_lists()
+    table.insert(extensions, fancy_lists_extension)
+  end
+  for _, user_extension_filename in ipairs(options.extensions) do
+    local user_extension = (function(filename)
+      local pathname = assert(kpse.find_file(filename),
+        [[Could not locate user-defined syntax extension "]]
+        .. filename)
+      local input_file = assert(io.open(pathname, "r"),
+        [[Could not open user-defined syntax extension "]]
+        .. pathname .. [[" for reading]])
+      local input = assert(input_file:read("*a"))
+      assert(input_file:close())
+      local user_extension, err = load([[
+        local sandbox = {}
+        setmetatable(sandbox, {__index = _G})
+        _ENV = sandbox
+      ]] .. input)()
+      assert(user_extension,
+        [[Failed to compile user-defined syntax extension "]]
+        .. pathname .. [[": ]] .. (err or [[]]))
+      assert(user_extension.api_version ~= nil,
+        [[User-defined syntax extension "]] .. pathname
+        .. [[" does not specify mandatory field "api_version"]])
+      assert(type(user_extension.api_version) == "number",
+        [[User-defined syntax extension "]] .. pathname
+        .. [[" specifies field "api_version" of type "]]
+        .. type(user_extension.api_version)
+        .. [[" but "number" was expected]])
+      assert(user_extension.api_version > 0
+         and user_extension.api_version
+          <= metadata.user_extension_api_version,
+        [[User-defined syntax extension "]] .. pathname
+        .. [[" uses syntax extension API version "]]
+        .. user_extension.api_version .. [[ but markdown.lua ]]
+        .. metadata.version .. [[ uses API version ]]
+        .. metadata.user_extension_api_version
+        .. [[, which is incompatible]])
+
+      assert(user_extension.grammar_version ~= nil,
+        [[User-defined syntax extension "]] .. pathname
+        .. [[" does not specify mandatory field "grammar_version"]])
+      assert(type(user_extension.grammar_version) == "number",
+        [[User-defined syntax extension "]] .. pathname
+        .. [[" specifies field "grammar_version" of type "]]
+        .. type(user_extension.grammar_version)
+        .. [[" but "number" was expected]])
+      assert(user_extension.grammar_version == metadata.grammar_version,
+        [[User-defined syntax extension "]] .. pathname
+        .. [[" uses grammar version "]]
+        .. user_extension.grammar_version
+        .. [[ but markdown.lua ]] .. metadata.version
+        .. [[ uses grammar version ]] .. metadata.grammar_version
+        .. [[, which is incompatible]])
+
+      assert(user_extension.finalize_grammar ~= nil,
+        [[User-defined syntax extension "]] .. pathname
+        .. [[" does not specify mandatory "finalize_grammar" field]])
+      assert(type(user_extension.finalize_grammar) == "function",
+        [[User-defined syntax extension "]] .. pathname
+        .. [[" specifies field "finalize_grammar" of type "]]
+        .. type(user_extension.finalize_grammar)
+        .. [[" but "function" was expected]])
+      local extension = {
+        name = [[user-defined "]] .. pathname .. [[" syntax extension]],
+        extend_reader = user_extension.finalize_grammar,
+        extend_writer = function() end,
+      }
+      return extension
+    end)(user_extension_filename)
+    table.insert(extensions, user_extension)
+  end
+  local writer = M.writer.new(options)
+  local reader = M.reader.new(writer, options)
+  local convert = reader.finalize_grammar(extensions)
+  collectgarbage("collect")
+  if options.singletonCache then
+    local singletonCacheOptions = {}
+    for k, v in pairs(options) do
+      singletonCacheOptions[k] = v
+    end
+    setmetatable(singletonCacheOptions,
+      { __index = function (_, key)
+        return defaultOptions[key] end })
+    singletonCache.options = singletonCacheOptions
+    singletonCache.convert = convert
+  end
+  return convert
+end
+return M


Property changes on: trunk/Master/texmf-dist/tex/luatex/markdown/markdown-parser.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/tex/luatex/markdown/markdown.lua
===================================================================
--- trunk/Master/texmf-dist/tex/luatex/markdown/markdown.lua	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/texmf-dist/tex/luatex/markdown/markdown.lua	2024-08-30 21:14:17 UTC (rev 72146)
@@ -9,12 +9,12 @@
 -- permit persons to whom the Software is furnished to do so, subject to
 -- the following conditions:
 --
--- The above copyright notice and this permission notice shall be included
--- in all copies or substantial portions of the Software.
+-- The above copyright notice and this permission notice shall be
+-- included in all copies or substantial portions of the Software.
 --
 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
--- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+-- MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT.
 -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 -- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
@@ -58,68 +58,21 @@
 -- those in the standard .ins files.
 --
 local metadata = {
-    version   = "3.6.2-0-g6c30af7e",
-    comment   = "A module for the conversion from markdown to plain TeX",
-    author    = "John MacFarlane, Hans Hagen, Vít Starý Novotný, Andrej Genčur",
+    version   = "3.7.0-0-g98dece19",
+    comment   = "A module for the conversion from markdown "
+             .. "to plain TeX",
+    author    = "John MacFarlane, Hans Hagen, Vít Starý Novotný, "
+             .. "Andrej Genčur",
     copyright = {"2009-2016 John MacFarlane, Hans Hagen",
                  "2016-2024 Vít Starý Novotný, Andrej Genčur"},
     license   = "LPPL 1.3c"
 }
 
-if not modules then modules = { } end
-modules['markdown'] = metadata
-local lpeg = require("lpeg")
-local unicode = require("unicode")
 local md5 = require("md5");
-(function()
-  local should_initialize = package.loaded.kpse == nil
-                       or tex.initialize ~= nil
-  kpse = require("kpse")
-  if should_initialize then
-    kpse.set_program_name("luatex")
-  end
-end)()
-local uni_algos = require("lua-uni-algos")
 local M = {metadata = metadata}
-local walkable_syntax = {
-  Block = {
-    "Blockquote",
-    "Verbatim",
-    "ThematicBreak",
-    "BulletList",
-    "OrderedList",
-    "DisplayHtml",
-    "Heading",
-  },
-  BlockOrParagraph = {
-    "Block",
-    "Paragraph",
-    "Plain",
-  },
-  Inline = {
-    "Str",
-    "Space",
-    "Endline",
-    "EndlineBreak",
-    "LinkAndEmph",
-    "Code",
-    "AutoLinkUrl",
-    "AutoLinkEmail",
-    "AutoLinkRelativeReference",
-    "InlineHtml",
-    "HtmlEntity",
-    "EscapedChar",
-    "Smart",
-    "Symbol",
-  },
-}
 local defaultOptions = {}
-defaultOptions.eagerCache = false
+defaultOptions.eagerCache = true
 defaultOptions.singletonCache = true
-local singletonCache = {
-  convert = nil,
-  options = nil,
-}
 defaultOptions.unicodeNormalization = true
 defaultOptions.unicodeNormalizationForm = "nfc"
 defaultOptions.cacheDir = "."
@@ -141,9 +94,8 @@
 defaultOptions.contentLevel = "block"
 defaultOptions.debugExtensions = false
 defaultOptions.definitionLists = false
+defaultOptions.ensureJekyllData = false
 defaultOptions.expectJekyllData = false
-metadata.user_extension_api_version = 2
-metadata.grammar_version = 4
 defaultOptions.extensions = {}
 defaultOptions.fancyLists = false
 defaultOptions.fencedCode = true
@@ -184,21 +136,12 @@
 defaultOptions.texMathSingleBackslash = false
 defaultOptions.tightLists = true
 defaultOptions.underscores = true
-local upper, format, length =
-  string.upper, string.format, string.len
-local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs, Cp, any =
-  lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cg, lpeg.Cb,
-  lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs, lpeg.Cp, lpeg.P(1)
 local util = {}
-function util.err(msg, exit_code)
-  io.stderr:write("markdown.lua: " .. msg .. "\n")
-  os.exit(exit_code or 1)
-end
 function util.cache(dir, string, salt, transform, suffix)
   local digest = md5.sumhexa(string .. (salt or ""))
   local name = util.pathname(dir, digest .. suffix)
   local file = io.open(name, "r")
-  if file == nil then -- If no cache entry exists, then create a new one.
+  if file == nil then -- If no cache entry exists, create a new one.
     file = assert(io.open(name, "w"),
       [[Could not open file "]] .. name .. [[" for writing]])
     local result = string
@@ -210,116 +153,6 @@
   end
   return name
 end
-function util.cache_verbatim(dir, string)
-  local name = util.cache(dir, string, nil, nil, ".verbatim")
-  return name
-end
-function util.table_copy(t)
-  local u = { }
-  for k, v in pairs(t) do u[k] = v end
-  return setmetatable(u, getmetatable(t))
-end
-function util.encode_json_string(s)
-  s = s:gsub([[\]], [[\\]])
-  s = s:gsub([["]], [[\"]])
-  return [["]] .. s .. [["]]
-end
-function util.expand_tabs_in_line(s, tabstop)
-  local tab = tabstop or 4
-  local corr = 0
-  return (s:gsub("()\t", function(p)
-            local sp = tab - (p - 1 + corr) % tab
-            corr = corr - 1 + sp
-            return string.rep(" ", sp)
-          end))
-end
-function util.walk(t, f)
-  local typ = type(t)
-  if typ == "string" then
-    f(t)
-  elseif typ == "table" then
-    local i = 1
-    local n
-    n = t[i]
-    while n do
-      util.walk(n, f)
-      i = i + 1
-      n = t[i]
-    end
-  elseif typ == "function" then
-    local ok, val = pcall(t)
-    if ok then
-      util.walk(val,f)
-    end
-  else
-    f(tostring(t))
-  end
-end
-function util.flatten(ary)
-  local new = {}
-  for _,v in ipairs(ary) do
-    if type(v) == "table" then
-      for _,w in ipairs(util.flatten(v)) do
-        new[#new + 1] = w
-      end
-    else
-      new[#new + 1] = v
-    end
-  end
-  return new
-end
-function util.rope_to_string(rope)
-  local buffer = {}
-  util.walk(rope, function(x) buffer[#buffer + 1] = x end)
-  return table.concat(buffer)
-end
-function util.rope_last(rope)
-  if #rope == 0 then
-    return nil
-  else
-    local l = rope[#rope]
-    if type(l) == "table" then
-      return util.rope_last(l)
-    else
-      return l
-    end
-  end
-end
-function util.intersperse(ary, x)
-  local new = {}
-  local l = #ary
-  for i,v in ipairs(ary) do
-    local n = #new
-    new[n + 1] = v
-    if i ~= l then
-      new[n + 2] = x
-    end
-  end
-  return new
-end
-function util.map(ary, f)
-  local new = {}
-  for i,v in ipairs(ary) do
-    new[i] = f(v)
-  end
-  return new
-end
-function util.escaper(char_escapes, string_escapes)
-  local char_escapes_list = ""
-  for i,_ in pairs(char_escapes) do
-    char_escapes_list = char_escapes_list .. i
-  end
-  local escapable = S(char_escapes_list) / char_escapes
-  if string_escapes then
-    for k,v in pairs(string_escapes) do
-      escapable = P(k) / v + escapable
-    end
-  end
-  local escape_string = Cs((escapable + any)^0)
-  return function(s)
-    return lpeg.match(escape_string, s)
-  end
-end
 function util.pathname(dir, file)
   if #dir == 0 then
     return file
@@ -327,8134 +160,70 @@
     return dir .. "/" .. file
   end
 end
-local entities = {}
-
-local character_entities = {
-  ["Tab"] = 9,
-  ["NewLine"] = 10,
-  ["excl"] = 33,
-  ["QUOT"] = 34,
-  ["quot"] = 34,
-  ["num"] = 35,
-  ["dollar"] = 36,
-  ["percnt"] = 37,
-  ["AMP"] = 38,
-  ["amp"] = 38,
-  ["apos"] = 39,
-  ["lpar"] = 40,
-  ["rpar"] = 41,
-  ["ast"] = 42,
-  ["midast"] = 42,
-  ["plus"] = 43,
-  ["comma"] = 44,
-  ["period"] = 46,
-  ["sol"] = 47,
-  ["colon"] = 58,
-  ["semi"] = 59,
-  ["LT"] = 60,
-  ["lt"] = 60,
-  ["nvlt"] = {60, 8402},
-  ["bne"] = {61, 8421},
-  ["equals"] = 61,
-  ["GT"] = 62,
-  ["gt"] = 62,
-  ["nvgt"] = {62, 8402},
-  ["quest"] = 63,
-  ["commat"] = 64,
-  ["lbrack"] = 91,
-  ["lsqb"] = 91,
-  ["bsol"] = 92,
-  ["rbrack"] = 93,
-  ["rsqb"] = 93,
-  ["Hat"] = 94,
-  ["UnderBar"] = 95,
-  ["lowbar"] = 95,
-  ["DiacriticalGrave"] = 96,
-  ["grave"] = 96,
-  ["fjlig"] = {102, 106},
-  ["lbrace"] = 123,
-  ["lcub"] = 123,
-  ["VerticalLine"] = 124,
-  ["verbar"] = 124,
-  ["vert"] = 124,
-  ["rbrace"] = 125,
-  ["rcub"] = 125,
-  ["NonBreakingSpace"] = 160,
-  ["nbsp"] = 160,
-  ["iexcl"] = 161,
-  ["cent"] = 162,
-  ["pound"] = 163,
-  ["curren"] = 164,
-  ["yen"] = 165,
-  ["brvbar"] = 166,
-  ["sect"] = 167,
-  ["Dot"] = 168,
-  ["DoubleDot"] = 168,
-  ["die"] = 168,
-  ["uml"] = 168,
-  ["COPY"] = 169,
-  ["copy"] = 169,
-  ["ordf"] = 170,
-  ["laquo"] = 171,
-  ["not"] = 172,
-  ["shy"] = 173,
-  ["REG"] = 174,
-  ["circledR"] = 174,
-  ["reg"] = 174,
-  ["macr"] = 175,
-  ["strns"] = 175,
-  ["deg"] = 176,
-  ["PlusMinus"] = 177,
-  ["plusmn"] = 177,
-  ["pm"] = 177,
-  ["sup2"] = 178,
-  ["sup3"] = 179,
-  ["DiacriticalAcute"] = 180,
-  ["acute"] = 180,
-  ["micro"] = 181,
-  ["para"] = 182,
-  ["CenterDot"] = 183,
-  ["centerdot"] = 183,
-  ["middot"] = 183,
-  ["Cedilla"] = 184,
-  ["cedil"] = 184,
-  ["sup1"] = 185,
-  ["ordm"] = 186,
-  ["raquo"] = 187,
-  ["frac14"] = 188,
-  ["frac12"] = 189,
-  ["half"] = 189,
-  ["frac34"] = 190,
-  ["iquest"] = 191,
-  ["Agrave"] = 192,
-  ["Aacute"] = 193,
-  ["Acirc"] = 194,
-  ["Atilde"] = 195,
-  ["Auml"] = 196,
-  ["Aring"] = 197,
-  ["angst"] = 197,
-  ["AElig"] = 198,
-  ["Ccedil"] = 199,
-  ["Egrave"] = 200,
-  ["Eacute"] = 201,
-  ["Ecirc"] = 202,
-  ["Euml"] = 203,
-  ["Igrave"] = 204,
-  ["Iacute"] = 205,
-  ["Icirc"] = 206,
-  ["Iuml"] = 207,
-  ["ETH"] = 208,
-  ["Ntilde"] = 209,
-  ["Ograve"] = 210,
-  ["Oacute"] = 211,
-  ["Ocirc"] = 212,
-  ["Otilde"] = 213,
-  ["Ouml"] = 214,
-  ["times"] = 215,
-  ["Oslash"] = 216,
-  ["Ugrave"] = 217,
-  ["Uacute"] = 218,
-  ["Ucirc"] = 219,
-  ["Uuml"] = 220,
-  ["Yacute"] = 221,
-  ["THORN"] = 222,
-  ["szlig"] = 223,
-  ["agrave"] = 224,
-  ["aacute"] = 225,
-  ["acirc"] = 226,
-  ["atilde"] = 227,
-  ["auml"] = 228,
-  ["aring"] = 229,
-  ["aelig"] = 230,
-  ["ccedil"] = 231,
-  ["egrave"] = 232,
-  ["eacute"] = 233,
-  ["ecirc"] = 234,
-  ["euml"] = 235,
-  ["igrave"] = 236,
-  ["iacute"] = 237,
-  ["icirc"] = 238,
-  ["iuml"] = 239,
-  ["eth"] = 240,
-  ["ntilde"] = 241,
-  ["ograve"] = 242,
-  ["oacute"] = 243,
-  ["ocirc"] = 244,
-  ["otilde"] = 245,
-  ["ouml"] = 246,
-  ["div"] = 247,
-  ["divide"] = 247,
-  ["oslash"] = 248,
-  ["ugrave"] = 249,
-  ["uacute"] = 250,
-  ["ucirc"] = 251,
-  ["uuml"] = 252,
-  ["yacute"] = 253,
-  ["thorn"] = 254,
-  ["yuml"] = 255,
-  ["Amacr"] = 256,
-  ["amacr"] = 257,
-  ["Abreve"] = 258,
-  ["abreve"] = 259,
-  ["Aogon"] = 260,
-  ["aogon"] = 261,
-  ["Cacute"] = 262,
-  ["cacute"] = 263,
-  ["Ccirc"] = 264,
-  ["ccirc"] = 265,
-  ["Cdot"] = 266,
-  ["cdot"] = 267,
-  ["Ccaron"] = 268,
-  ["ccaron"] = 269,
-  ["Dcaron"] = 270,
-  ["dcaron"] = 271,
-  ["Dstrok"] = 272,
-  ["dstrok"] = 273,
-  ["Emacr"] = 274,
-  ["emacr"] = 275,
-  ["Edot"] = 278,
-  ["edot"] = 279,
-  ["Eogon"] = 280,
-  ["eogon"] = 281,
-  ["Ecaron"] = 282,
-  ["ecaron"] = 283,
-  ["Gcirc"] = 284,
-  ["gcirc"] = 285,
-  ["Gbreve"] = 286,
-  ["gbreve"] = 287,
-  ["Gdot"] = 288,
-  ["gdot"] = 289,
-  ["Gcedil"] = 290,
-  ["Hcirc"] = 292,
-  ["hcirc"] = 293,
-  ["Hstrok"] = 294,
-  ["hstrok"] = 295,
-  ["Itilde"] = 296,
-  ["itilde"] = 297,
-  ["Imacr"] = 298,
-  ["imacr"] = 299,
-  ["Iogon"] = 302,
-  ["iogon"] = 303,
-  ["Idot"] = 304,
-  ["imath"] = 305,
-  ["inodot"] = 305,
-  ["IJlig"] = 306,
-  ["ijlig"] = 307,
-  ["Jcirc"] = 308,
-  ["jcirc"] = 309,
-  ["Kcedil"] = 310,
-  ["kcedil"] = 311,
-  ["kgreen"] = 312,
-  ["Lacute"] = 313,
-  ["lacute"] = 314,
-  ["Lcedil"] = 315,
-  ["lcedil"] = 316,
-  ["Lcaron"] = 317,
-  ["lcaron"] = 318,
-  ["Lmidot"] = 319,
-  ["lmidot"] = 320,
-  ["Lstrok"] = 321,
-  ["lstrok"] = 322,
-  ["Nacute"] = 323,
-  ["nacute"] = 324,
-  ["Ncedil"] = 325,
-  ["ncedil"] = 326,
-  ["Ncaron"] = 327,
-  ["ncaron"] = 328,
-  ["napos"] = 329,
-  ["ENG"] = 330,
-  ["eng"] = 331,
-  ["Omacr"] = 332,
-  ["omacr"] = 333,
-  ["Odblac"] = 336,
-  ["odblac"] = 337,
-  ["OElig"] = 338,
-  ["oelig"] = 339,
-  ["Racute"] = 340,
-  ["racute"] = 341,
-  ["Rcedil"] = 342,
-  ["rcedil"] = 343,
-  ["Rcaron"] = 344,
-  ["rcaron"] = 345,
-  ["Sacute"] = 346,
-  ["sacute"] = 347,
-  ["Scirc"] = 348,
-  ["scirc"] = 349,
-  ["Scedil"] = 350,
-  ["scedil"] = 351,
-  ["Scaron"] = 352,
-  ["scaron"] = 353,
-  ["Tcedil"] = 354,
-  ["tcedil"] = 355,
-  ["Tcaron"] = 356,
-  ["tcaron"] = 357,
-  ["Tstrok"] = 358,
-  ["tstrok"] = 359,
-  ["Utilde"] = 360,
-  ["utilde"] = 361,
-  ["Umacr"] = 362,
-  ["umacr"] = 363,
-  ["Ubreve"] = 364,
-  ["ubreve"] = 365,
-  ["Uring"] = 366,
-  ["uring"] = 367,
-  ["Udblac"] = 368,
-  ["udblac"] = 369,
-  ["Uogon"] = 370,
-  ["uogon"] = 371,
-  ["Wcirc"] = 372,
-  ["wcirc"] = 373,
-  ["Ycirc"] = 374,
-  ["ycirc"] = 375,
-  ["Yuml"] = 376,
-  ["Zacute"] = 377,
-  ["zacute"] = 378,
-  ["Zdot"] = 379,
-  ["zdot"] = 380,
-  ["Zcaron"] = 381,
-  ["zcaron"] = 382,
-  ["fnof"] = 402,
-  ["imped"] = 437,
-  ["gacute"] = 501,
-  ["jmath"] = 567,
-  ["circ"] = 710,
-  ["Hacek"] = 711,
-  ["caron"] = 711,
-  ["Breve"] = 728,
-  ["breve"] = 728,
-  ["DiacriticalDot"] = 729,
-  ["dot"] = 729,
-  ["ring"] = 730,
-  ["ogon"] = 731,
-  ["DiacriticalTilde"] = 732,
-  ["tilde"] = 732,
-  ["DiacriticalDoubleAcute"] = 733,
-  ["dblac"] = 733,
-  ["DownBreve"] = 785,
-  ["Alpha"] = 913,
-  ["Beta"] = 914,
-  ["Gamma"] = 915,
-  ["Delta"] = 916,
-  ["Epsilon"] = 917,
-  ["Zeta"] = 918,
-  ["Eta"] = 919,
-  ["Theta"] = 920,
-  ["Iota"] = 921,
-  ["Kappa"] = 922,
-  ["Lambda"] = 923,
-  ["Mu"] = 924,
-  ["Nu"] = 925,
-  ["Xi"] = 926,
-  ["Omicron"] = 927,
-  ["Pi"] = 928,
-  ["Rho"] = 929,
-  ["Sigma"] = 931,
-  ["Tau"] = 932,
-  ["Upsilon"] = 933,
-  ["Phi"] = 934,
-  ["Chi"] = 935,
-  ["Psi"] = 936,
-  ["Omega"] = 937,
-  ["ohm"] = 937,
-  ["alpha"] = 945,
-  ["beta"] = 946,
-  ["gamma"] = 947,
-  ["delta"] = 948,
-  ["epsi"] = 949,
-  ["epsilon"] = 949,
-  ["zeta"] = 950,
-  ["eta"] = 951,
-  ["theta"] = 952,
-  ["iota"] = 953,
-  ["kappa"] = 954,
-  ["lambda"] = 955,
-  ["mu"] = 956,
-  ["nu"] = 957,
-  ["xi"] = 958,
-  ["omicron"] = 959,
-  ["pi"] = 960,
-  ["rho"] = 961,
-  ["sigmaf"] = 962,
-  ["sigmav"] = 962,
-  ["varsigma"] = 962,
-  ["sigma"] = 963,
-  ["tau"] = 964,
-  ["upsi"] = 965,
-  ["upsilon"] = 965,
-  ["phi"] = 966,
-  ["chi"] = 967,
-  ["psi"] = 968,
-  ["omega"] = 969,
-  ["thetasym"] = 977,
-  ["thetav"] = 977,
-  ["vartheta"] = 977,
-  ["Upsi"] = 978,
-  ["upsih"] = 978,
-  ["phiv"] = 981,
-  ["straightphi"] = 981,
-  ["varphi"] = 981,
-  ["piv"] = 982,
-  ["varpi"] = 982,
-  ["Gammad"] = 988,
-  ["digamma"] = 989,
-  ["gammad"] = 989,
-  ["kappav"] = 1008,
-  ["varkappa"] = 1008,
-  ["rhov"] = 1009,
-  ["varrho"] = 1009,
-  ["epsiv"] = 1013,
-  ["straightepsilon"] = 1013,
-  ["varepsilon"] = 1013,
-  ["backepsilon"] = 1014,
-  ["bepsi"] = 1014,
-  ["IOcy"] = 1025,
-  ["DJcy"] = 1026,
-  ["GJcy"] = 1027,
-  ["Jukcy"] = 1028,
-  ["DScy"] = 1029,
-  ["Iukcy"] = 1030,
-  ["YIcy"] = 1031,
-  ["Jsercy"] = 1032,
-  ["LJcy"] = 1033,
-  ["NJcy"] = 1034,
-  ["TSHcy"] = 1035,
-  ["KJcy"] = 1036,
-  ["Ubrcy"] = 1038,
-  ["DZcy"] = 1039,
-  ["Acy"] = 1040,
-  ["Bcy"] = 1041,
-  ["Vcy"] = 1042,
-  ["Gcy"] = 1043,
-  ["Dcy"] = 1044,
-  ["IEcy"] = 1045,
-  ["ZHcy"] = 1046,
-  ["Zcy"] = 1047,
-  ["Icy"] = 1048,
-  ["Jcy"] = 1049,
-  ["Kcy"] = 1050,
-  ["Lcy"] = 1051,
-  ["Mcy"] = 1052,
-  ["Ncy"] = 1053,
-  ["Ocy"] = 1054,
-  ["Pcy"] = 1055,
-  ["Rcy"] = 1056,
-  ["Scy"] = 1057,
-  ["Tcy"] = 1058,
-  ["Ucy"] = 1059,
-  ["Fcy"] = 1060,
-  ["KHcy"] = 1061,
-  ["TScy"] = 1062,
-  ["CHcy"] = 1063,
-  ["SHcy"] = 1064,
-  ["SHCHcy"] = 1065,
-  ["HARDcy"] = 1066,
-  ["Ycy"] = 1067,
-  ["SOFTcy"] = 1068,
-  ["Ecy"] = 1069,
-  ["YUcy"] = 1070,
-  ["YAcy"] = 1071,
-  ["acy"] = 1072,
-  ["bcy"] = 1073,
-  ["vcy"] = 1074,
-  ["gcy"] = 1075,
-  ["dcy"] = 1076,
-  ["iecy"] = 1077,
-  ["zhcy"] = 1078,
-  ["zcy"] = 1079,
-  ["icy"] = 1080,
-  ["jcy"] = 1081,
-  ["kcy"] = 1082,
-  ["lcy"] = 1083,
-  ["mcy"] = 1084,
-  ["ncy"] = 1085,
-  ["ocy"] = 1086,
-  ["pcy"] = 1087,
-  ["rcy"] = 1088,
-  ["scy"] = 1089,
-  ["tcy"] = 1090,
-  ["ucy"] = 1091,
-  ["fcy"] = 1092,
-  ["khcy"] = 1093,
-  ["tscy"] = 1094,
-  ["chcy"] = 1095,
-  ["shcy"] = 1096,
-  ["shchcy"] = 1097,
-  ["hardcy"] = 1098,
-  ["ycy"] = 1099,
-  ["softcy"] = 1100,
-  ["ecy"] = 1101,
-  ["yucy"] = 1102,
-  ["yacy"] = 1103,
-  ["iocy"] = 1105,
-  ["djcy"] = 1106,
-  ["gjcy"] = 1107,
-  ["jukcy"] = 1108,
-  ["dscy"] = 1109,
-  ["iukcy"] = 1110,
-  ["yicy"] = 1111,
-  ["jsercy"] = 1112,
-  ["ljcy"] = 1113,
-  ["njcy"] = 1114,
-  ["tshcy"] = 1115,
-  ["kjcy"] = 1116,
-  ["ubrcy"] = 1118,
-  ["dzcy"] = 1119,
-  ["ensp"] = 8194,
-  ["emsp"] = 8195,
-  ["emsp13"] = 8196,
-  ["emsp14"] = 8197,
-  ["numsp"] = 8199,
-  ["puncsp"] = 8200,
-  ["ThinSpace"] = 8201,
-  ["thinsp"] = 8201,
-  ["VeryThinSpace"] = 8202,
-  ["hairsp"] = 8202,
-  ["NegativeMediumSpace"] = 8203,
-  ["NegativeThickSpace"] = 8203,
-  ["NegativeThinSpace"] = 8203,
-  ["NegativeVeryThinSpace"] = 8203,
-  ["ZeroWidthSpace"] = 8203,
-  ["zwnj"] = 8204,
-  ["zwj"] = 8205,
-  ["lrm"] = 8206,
-  ["rlm"] = 8207,
-  ["dash"] = 8208,
-  ["hyphen"] = 8208,
-  ["ndash"] = 8211,
-  ["mdash"] = 8212,
-  ["horbar"] = 8213,
-  ["Verbar"] = 8214,
-  ["Vert"] = 8214,
-  ["OpenCurlyQuote"] = 8216,
-  ["lsquo"] = 8216,
-  ["CloseCurlyQuote"] = 8217,
-  ["rsquo"] = 8217,
-  ["rsquor"] = 8217,
-  ["lsquor"] = 8218,
-  ["sbquo"] = 8218,
-  ["OpenCurlyDoubleQuote"] = 8220,
-  ["ldquo"] = 8220,
-  ["CloseCurlyDoubleQuote"] = 8221,
-  ["rdquo"] = 8221,
-  ["rdquor"] = 8221,
-  ["bdquo"] = 8222,
-  ["ldquor"] = 8222,
-  ["dagger"] = 8224,
-  ["Dagger"] = 8225,
-  ["ddagger"] = 8225,
-  ["bull"] = 8226,
-  ["bullet"] = 8226,
-  ["nldr"] = 8229,
-  ["hellip"] = 8230,
-  ["mldr"] = 8230,
-  ["permil"] = 8240,
-  ["pertenk"] = 8241,
-  ["prime"] = 8242,
-  ["Prime"] = 8243,
-  ["tprime"] = 8244,
-  ["backprime"] = 8245,
-  ["bprime"] = 8245,
-  ["lsaquo"] = 8249,
-  ["rsaquo"] = 8250,
-  ["OverBar"] = 8254,
-  ["oline"] = 8254,
-  ["caret"] = 8257,
-  ["hybull"] = 8259,
-  ["frasl"] = 8260,
-  ["bsemi"] = 8271,
-  ["qprime"] = 8279,
-  ["MediumSpace"] = 8287,
-  ["ThickSpace"] = {8287, 8202},
-  ["NoBreak"] = 8288,
-  ["ApplyFunction"] = 8289,
-  ["af"] = 8289,
-  ["InvisibleTimes"] = 8290,
-  ["it"] = 8290,
-  ["InvisibleComma"] = 8291,
-  ["ic"] = 8291,
-  ["euro"] = 8364,
-  ["TripleDot"] = 8411,
-  ["tdot"] = 8411,
-  ["DotDot"] = 8412,
-  ["Copf"] = 8450,
-  ["complexes"] = 8450,
-  ["incare"] = 8453,
-  ["gscr"] = 8458,
-  ["HilbertSpace"] = 8459,
-  ["Hscr"] = 8459,
-  ["hamilt"] = 8459,
-  ["Hfr"] = 8460,
-  ["Poincareplane"] = 8460,
-  ["Hopf"] = 8461,
-  ["quaternions"] = 8461,
-  ["planckh"] = 8462,
-  ["hbar"] = 8463,
-  ["hslash"] = 8463,
-  ["planck"] = 8463,
-  ["plankv"] = 8463,
-  ["Iscr"] = 8464,
-  ["imagline"] = 8464,
-  ["Ifr"] = 8465,
-  ["Im"] = 8465,
-  ["image"] = 8465,
-  ["imagpart"] = 8465,
-  ["Laplacetrf"] = 8466,
-  ["Lscr"] = 8466,
-  ["lagran"] = 8466,
-  ["ell"] = 8467,
-  ["Nopf"] = 8469,
-  ["naturals"] = 8469,
-  ["numero"] = 8470,
-  ["copysr"] = 8471,
-  ["weierp"] = 8472,
-  ["wp"] = 8472,
-  ["Popf"] = 8473,
-  ["primes"] = 8473,
-  ["Qopf"] = 8474,
-  ["rationals"] = 8474,
-  ["Rscr"] = 8475,
-  ["realine"] = 8475,
-  ["Re"] = 8476,
-  ["Rfr"] = 8476,
-  ["real"] = 8476,
-  ["realpart"] = 8476,
-  ["Ropf"] = 8477,
-  ["reals"] = 8477,
-  ["rx"] = 8478,
-  ["TRADE"] = 8482,
-  ["trade"] = 8482,
-  ["Zopf"] = 8484,
-  ["integers"] = 8484,
-  ["mho"] = 8487,
-  ["Zfr"] = 8488,
-  ["zeetrf"] = 8488,
-  ["iiota"] = 8489,
-  ["Bernoullis"] = 8492,
-  ["Bscr"] = 8492,
-  ["bernou"] = 8492,
-  ["Cayleys"] = 8493,
-  ["Cfr"] = 8493,
-  ["escr"] = 8495,
-  ["Escr"] = 8496,
-  ["expectation"] = 8496,
-  ["Fouriertrf"] = 8497,
-  ["Fscr"] = 8497,
-  ["Mellintrf"] = 8499,
-  ["Mscr"] = 8499,
-  ["phmmat"] = 8499,
-  ["order"] = 8500,
-  ["orderof"] = 8500,
-  ["oscr"] = 8500,
-  ["alefsym"] = 8501,
-  ["aleph"] = 8501,
-  ["beth"] = 8502,
-  ["gimel"] = 8503,
-  ["daleth"] = 8504,
-  ["CapitalDifferentialD"] = 8517,
-  ["DD"] = 8517,
-  ["DifferentialD"] = 8518,
-  ["dd"] = 8518,
-  ["ExponentialE"] = 8519,
-  ["ee"] = 8519,
-  ["exponentiale"] = 8519,
-  ["ImaginaryI"] = 8520,
-  ["ii"] = 8520,
-  ["frac13"] = 8531,
-  ["frac23"] = 8532,
-  ["frac15"] = 8533,
-  ["frac25"] = 8534,
-  ["frac35"] = 8535,
-  ["frac45"] = 8536,
-  ["frac16"] = 8537,
-  ["frac56"] = 8538,
-  ["frac18"] = 8539,
-  ["frac38"] = 8540,
-  ["frac58"] = 8541,
-  ["frac78"] = 8542,
-  ["LeftArrow"] = 8592,
-  ["ShortLeftArrow"] = 8592,
-  ["larr"] = 8592,
-  ["leftarrow"] = 8592,
-  ["slarr"] = 8592,
-  ["ShortUpArrow"] = 8593,
-  ["UpArrow"] = 8593,
-  ["uarr"] = 8593,
-  ["uparrow"] = 8593,
-  ["RightArrow"] = 8594,
-  ["ShortRightArrow"] = 8594,
-  ["rarr"] = 8594,
-  ["rightarrow"] = 8594,
-  ["srarr"] = 8594,
-  ["DownArrow"] = 8595,
-  ["ShortDownArrow"] = 8595,
-  ["darr"] = 8595,
-  ["downarrow"] = 8595,
-  ["LeftRightArrow"] = 8596,
-  ["harr"] = 8596,
-  ["leftrightarrow"] = 8596,
-  ["UpDownArrow"] = 8597,
-  ["updownarrow"] = 8597,
-  ["varr"] = 8597,
-  ["UpperLeftArrow"] = 8598,
-  ["nwarr"] = 8598,
-  ["nwarrow"] = 8598,
-  ["UpperRightArrow"] = 8599,
-  ["nearr"] = 8599,
-  ["nearrow"] = 8599,
-  ["LowerRightArrow"] = 8600,
-  ["searr"] = 8600,
-  ["searrow"] = 8600,
-  ["LowerLeftArrow"] = 8601,
-  ["swarr"] = 8601,
-  ["swarrow"] = 8601,
-  ["nlarr"] = 8602,
-  ["nleftarrow"] = 8602,
-  ["nrarr"] = 8603,
-  ["nrightarrow"] = 8603,
-  ["nrarrw"] = {8605, 824},
-  ["rarrw"] = 8605,
-  ["rightsquigarrow"] = 8605,
-  ["Larr"] = 8606,
-  ["twoheadleftarrow"] = 8606,
-  ["Uarr"] = 8607,
-  ["Rarr"] = 8608,
-  ["twoheadrightarrow"] = 8608,
-  ["Darr"] = 8609,
-  ["larrtl"] = 8610,
-  ["leftarrowtail"] = 8610,
-  ["rarrtl"] = 8611,
-  ["rightarrowtail"] = 8611,
-  ["LeftTeeArrow"] = 8612,
-  ["mapstoleft"] = 8612,
-  ["UpTeeArrow"] = 8613,
-  ["mapstoup"] = 8613,
-  ["RightTeeArrow"] = 8614,
-  ["map"] = 8614,
-  ["mapsto"] = 8614,
-  ["DownTeeArrow"] = 8615,
-  ["mapstodown"] = 8615,
-  ["hookleftarrow"] = 8617,
-  ["larrhk"] = 8617,
-  ["hookrightarrow"] = 8618,
-  ["rarrhk"] = 8618,
-  ["larrlp"] = 8619,
-  ["looparrowleft"] = 8619,
-  ["looparrowright"] = 8620,
-  ["rarrlp"] = 8620,
-  ["harrw"] = 8621,
-  ["leftrightsquigarrow"] = 8621,
-  ["nharr"] = 8622,
-  ["nleftrightarrow"] = 8622,
-  ["Lsh"] = 8624,
-  ["lsh"] = 8624,
-  ["Rsh"] = 8625,
-  ["rsh"] = 8625,
-  ["ldsh"] = 8626,
-  ["rdsh"] = 8627,
-  ["crarr"] = 8629,
-  ["cularr"] = 8630,
-  ["curvearrowleft"] = 8630,
-  ["curarr"] = 8631,
-  ["curvearrowright"] = 8631,
-  ["circlearrowleft"] = 8634,
-  ["olarr"] = 8634,
-  ["circlearrowright"] = 8635,
-  ["orarr"] = 8635,
-  ["LeftVector"] = 8636,
-  ["leftharpoonup"] = 8636,
-  ["lharu"] = 8636,
-  ["DownLeftVector"] = 8637,
-  ["leftharpoondown"] = 8637,
-  ["lhard"] = 8637,
-  ["RightUpVector"] = 8638,
-  ["uharr"] = 8638,
-  ["upharpoonright"] = 8638,
-  ["LeftUpVector"] = 8639,
-  ["uharl"] = 8639,
-  ["upharpoonleft"] = 8639,
-  ["RightVector"] = 8640,
-  ["rharu"] = 8640,
-  ["rightharpoonup"] = 8640,
-  ["DownRightVector"] = 8641,
-  ["rhard"] = 8641,
-  ["rightharpoondown"] = 8641,
-  ["RightDownVector"] = 8642,
-  ["dharr"] = 8642,
-  ["downharpoonright"] = 8642,
-  ["LeftDownVector"] = 8643,
-  ["dharl"] = 8643,
-  ["downharpoonleft"] = 8643,
-  ["RightArrowLeftArrow"] = 8644,
-  ["rightleftarrows"] = 8644,
-  ["rlarr"] = 8644,
-  ["UpArrowDownArrow"] = 8645,
-  ["udarr"] = 8645,
-  ["LeftArrowRightArrow"] = 8646,
-  ["leftrightarrows"] = 8646,
-  ["lrarr"] = 8646,
-  ["leftleftarrows"] = 8647,
-  ["llarr"] = 8647,
-  ["upuparrows"] = 8648,
-  ["uuarr"] = 8648,
-  ["rightrightarrows"] = 8649,
-  ["rrarr"] = 8649,
-  ["ddarr"] = 8650,
-  ["downdownarrows"] = 8650,
-  ["ReverseEquilibrium"] = 8651,
-  ["leftrightharpoons"] = 8651,
-  ["lrhar"] = 8651,
-  ["Equilibrium"] = 8652,
-  ["rightleftharpoons"] = 8652,
-  ["rlhar"] = 8652,
-  ["nLeftarrow"] = 8653,
-  ["nlArr"] = 8653,
-  ["nLeftrightarrow"] = 8654,
-  ["nhArr"] = 8654,
-  ["nRightarrow"] = 8655,
-  ["nrArr"] = 8655,
-  ["DoubleLeftArrow"] = 8656,
-  ["Leftarrow"] = 8656,
-  ["lArr"] = 8656,
-  ["DoubleUpArrow"] = 8657,
-  ["Uparrow"] = 8657,
-  ["uArr"] = 8657,
-  ["DoubleRightArrow"] = 8658,
-  ["Implies"] = 8658,
-  ["Rightarrow"] = 8658,
-  ["rArr"] = 8658,
-  ["DoubleDownArrow"] = 8659,
-  ["Downarrow"] = 8659,
-  ["dArr"] = 8659,
-  ["DoubleLeftRightArrow"] = 8660,
-  ["Leftrightarrow"] = 8660,
-  ["hArr"] = 8660,
-  ["iff"] = 8660,
-  ["DoubleUpDownArrow"] = 8661,
-  ["Updownarrow"] = 8661,
-  ["vArr"] = 8661,
-  ["nwArr"] = 8662,
-  ["neArr"] = 8663,
-  ["seArr"] = 8664,
-  ["swArr"] = 8665,
-  ["Lleftarrow"] = 8666,
-  ["lAarr"] = 8666,
-  ["Rrightarrow"] = 8667,
-  ["rAarr"] = 8667,
-  ["zigrarr"] = 8669,
-  ["LeftArrowBar"] = 8676,
-  ["larrb"] = 8676,
-  ["RightArrowBar"] = 8677,
-  ["rarrb"] = 8677,
-  ["DownArrowUpArrow"] = 8693,
-  ["duarr"] = 8693,
-  ["loarr"] = 8701,
-  ["roarr"] = 8702,
-  ["hoarr"] = 8703,
-  ["ForAll"] = 8704,
-  ["forall"] = 8704,
-  ["comp"] = 8705,
-  ["complement"] = 8705,
-  ["PartialD"] = 8706,
-  ["npart"] = {8706, 824},
-  ["part"] = 8706,
-  ["Exists"] = 8707,
-  ["exist"] = 8707,
-  ["NotExists"] = 8708,
-  ["nexist"] = 8708,
-  ["nexists"] = 8708,
-  ["empty"] = 8709,
-  ["emptyset"] = 8709,
-  ["emptyv"] = 8709,
-  ["varnothing"] = 8709,
-  ["Del"] = 8711,
-  ["nabla"] = 8711,
-  ["Element"] = 8712,
-  ["in"] = 8712,
-  ["isin"] = 8712,
-  ["isinv"] = 8712,
-  ["NotElement"] = 8713,
-  ["notin"] = 8713,
-  ["notinva"] = 8713,
-  ["ReverseElement"] = 8715,
-  ["SuchThat"] = 8715,
-  ["ni"] = 8715,
-  ["niv"] = 8715,
-  ["NotReverseElement"] = 8716,
-  ["notni"] = 8716,
-  ["notniva"] = 8716,
-  ["Product"] = 8719,
-  ["prod"] = 8719,
-  ["Coproduct"] = 8720,
-  ["coprod"] = 8720,
-  ["Sum"] = 8721,
-  ["sum"] = 8721,
-  ["minus"] = 8722,
-  ["MinusPlus"] = 8723,
-  ["mnplus"] = 8723,
-  ["mp"] = 8723,
-  ["dotplus"] = 8724,
-  ["plusdo"] = 8724,
-  ["Backslash"] = 8726,
-  ["setminus"] = 8726,
-  ["setmn"] = 8726,
-  ["smallsetminus"] = 8726,
-  ["ssetmn"] = 8726,
-  ["lowast"] = 8727,
-  ["SmallCircle"] = 8728,
-  ["compfn"] = 8728,
-  ["Sqrt"] = 8730,
-  ["radic"] = 8730,
-  ["Proportional"] = 8733,
-  ["prop"] = 8733,
-  ["propto"] = 8733,
-  ["varpropto"] = 8733,
-  ["vprop"] = 8733,
-  ["infin"] = 8734,
-  ["angrt"] = 8735,
-  ["ang"] = 8736,
-  ["angle"] = 8736,
-  ["nang"] = {8736, 8402},
-  ["angmsd"] = 8737,
-  ["measuredangle"] = 8737,
-  ["angsph"] = 8738,
-  ["VerticalBar"] = 8739,
-  ["mid"] = 8739,
-  ["shortmid"] = 8739,
-  ["smid"] = 8739,
-  ["NotVerticalBar"] = 8740,
-  ["nmid"] = 8740,
-  ["nshortmid"] = 8740,
-  ["nsmid"] = 8740,
-  ["DoubleVerticalBar"] = 8741,
-  ["par"] = 8741,
-  ["parallel"] = 8741,
-  ["shortparallel"] = 8741,
-  ["spar"] = 8741,
-  ["NotDoubleVerticalBar"] = 8742,
-  ["npar"] = 8742,
-  ["nparallel"] = 8742,
-  ["nshortparallel"] = 8742,
-  ["nspar"] = 8742,
-  ["and"] = 8743,
-  ["wedge"] = 8743,
-  ["or"] = 8744,
-  ["vee"] = 8744,
-  ["cap"] = 8745,
-  ["caps"] = {8745, 65024},
-  ["cup"] = 8746,
-  ["cups"] = {8746, 65024},
-  ["Integral"] = 8747,
-  ["int"] = 8747,
-  ["Int"] = 8748,
-  ["iiint"] = 8749,
-  ["tint"] = 8749,
-  ["ContourIntegral"] = 8750,
-  ["conint"] = 8750,
-  ["oint"] = 8750,
-  ["Conint"] = 8751,
-  ["DoubleContourIntegral"] = 8751,
-  ["Cconint"] = 8752,
-  ["cwint"] = 8753,
-  ["ClockwiseContourIntegral"] = 8754,
-  ["cwconint"] = 8754,
-  ["CounterClockwiseContourIntegral"] = 8755,
-  ["awconint"] = 8755,
-  ["Therefore"] = 8756,
-  ["there4"] = 8756,
-  ["therefore"] = 8756,
-  ["Because"] = 8757,
-  ["becaus"] = 8757,
-  ["because"] = 8757,
-  ["ratio"] = 8758,
-  ["Colon"] = 8759,
-  ["Proportion"] = 8759,
-  ["dotminus"] = 8760,
-  ["minusd"] = 8760,
-  ["mDDot"] = 8762,
-  ["homtht"] = 8763,
-  ["Tilde"] = 8764,
-  ["nvsim"] = {8764, 8402},
-  ["sim"] = 8764,
-  ["thicksim"] = 8764,
-  ["thksim"] = 8764,
-  ["backsim"] = 8765,
-  ["bsim"] = 8765,
-  ["race"] = {8765, 817},
-  ["ac"] = 8766,
-  ["acE"] = {8766, 819},
-  ["mstpos"] = 8766,
-  ["acd"] = 8767,
-  ["VerticalTilde"] = 8768,
-  ["wr"] = 8768,
-  ["wreath"] = 8768,
-  ["NotTilde"] = 8769,
-  ["nsim"] = 8769,
-  ["EqualTilde"] = 8770,
-  ["NotEqualTilde"] = {8770, 824},
-  ["eqsim"] = 8770,
-  ["esim"] = 8770,
-  ["nesim"] = {8770, 824},
-  ["TildeEqual"] = 8771,
-  ["sime"] = 8771,
-  ["simeq"] = 8771,
-  ["NotTildeEqual"] = 8772,
-  ["nsime"] = 8772,
-  ["nsimeq"] = 8772,
-  ["TildeFullEqual"] = 8773,
-  ["cong"] = 8773,
-  ["simne"] = 8774,
-  ["NotTildeFullEqual"] = 8775,
-  ["ncong"] = 8775,
-  ["TildeTilde"] = 8776,
-  ["ap"] = 8776,
-  ["approx"] = 8776,
-  ["asymp"] = 8776,
-  ["thickapprox"] = 8776,
-  ["thkap"] = 8776,
-  ["NotTildeTilde"] = 8777,
-  ["nap"] = 8777,
-  ["napprox"] = 8777,
-  ["ape"] = 8778,
-  ["approxeq"] = 8778,
-  ["apid"] = 8779,
-  ["napid"] = {8779, 824},
-  ["backcong"] = 8780,
-  ["bcong"] = 8780,
-  ["CupCap"] = 8781,
-  ["asympeq"] = 8781,
-  ["nvap"] = {8781, 8402},
-  ["Bumpeq"] = 8782,
-  ["HumpDownHump"] = 8782,
-  ["NotHumpDownHump"] = {8782, 824},
-  ["bump"] = 8782,
-  ["nbump"] = {8782, 824},
-  ["HumpEqual"] = 8783,
-  ["NotHumpEqual"] = {8783, 824},
-  ["bumpe"] = 8783,
-  ["bumpeq"] = 8783,
-  ["nbumpe"] = {8783, 824},
-  ["DotEqual"] = 8784,
-  ["doteq"] = 8784,
-  ["esdot"] = 8784,
-  ["nedot"] = {8784, 824},
-  ["doteqdot"] = 8785,
-  ["eDot"] = 8785,
-  ["efDot"] = 8786,
-  ["fallingdotseq"] = 8786,
-  ["erDot"] = 8787,
-  ["risingdotseq"] = 8787,
-  ["Assign"] = 8788,
-  ["colone"] = 8788,
-  ["coloneq"] = 8788,
-  ["ecolon"] = 8789,
-  ["eqcolon"] = 8789,
-  ["ecir"] = 8790,
-  ["eqcirc"] = 8790,
-  ["circeq"] = 8791,
-  ["cire"] = 8791,
-  ["wedgeq"] = 8793,
-  ["veeeq"] = 8794,
-  ["triangleq"] = 8796,
-  ["trie"] = 8796,
-  ["equest"] = 8799,
-  ["questeq"] = 8799,
-  ["NotEqual"] = 8800,
-  ["ne"] = 8800,
-  ["Congruent"] = 8801,
-  ["bnequiv"] = {8801, 8421},
-  ["equiv"] = 8801,
-  ["NotCongruent"] = 8802,
-  ["nequiv"] = 8802,
-  ["le"] = 8804,
-  ["leq"] = 8804,
-  ["nvle"] = {8804, 8402},
-  ["GreaterEqual"] = 8805,
-  ["ge"] = 8805,
-  ["geq"] = 8805,
-  ["nvge"] = {8805, 8402},
-  ["LessFullEqual"] = 8806,
-  ["lE"] = 8806,
-  ["leqq"] = 8806,
-  ["nlE"] = {8806, 824},
-  ["nleqq"] = {8806, 824},
-  ["GreaterFullEqual"] = 8807,
-  ["NotGreaterFullEqual"] = {8807, 824},
-  ["gE"] = 8807,
-  ["geqq"] = 8807,
-  ["ngE"] = {8807, 824},
-  ["ngeqq"] = {8807, 824},
-  ["lnE"] = 8808,
-  ["lneqq"] = 8808,
-  ["lvertneqq"] = {8808, 65024},
-  ["lvnE"] = {8808, 65024},
-  ["gnE"] = 8809,
-  ["gneqq"] = 8809,
-  ["gvertneqq"] = {8809, 65024},
-  ["gvnE"] = {8809, 65024},
-  ["Lt"] = 8810,
-  ["NestedLessLess"] = 8810,
-  ["NotLessLess"] = {8810, 824},
-  ["ll"] = 8810,
-  ["nLt"] = {8810, 8402},
-  ["nLtv"] = {8810, 824},
-  ["Gt"] = 8811,
-  ["NestedGreaterGreater"] = 8811,
-  ["NotGreaterGreater"] = {8811, 824},
-  ["gg"] = 8811,
-  ["nGt"] = {8811, 8402},
-  ["nGtv"] = {8811, 824},
-  ["between"] = 8812,
-  ["twixt"] = 8812,
-  ["NotCupCap"] = 8813,
-  ["NotLess"] = 8814,
-  ["nless"] = 8814,
-  ["nlt"] = 8814,
-  ["NotGreater"] = 8815,
-  ["ngt"] = 8815,
-  ["ngtr"] = 8815,
-  ["NotLessEqual"] = 8816,
-  ["nle"] = 8816,
-  ["nleq"] = 8816,
-  ["NotGreaterEqual"] = 8817,
-  ["nge"] = 8817,
-  ["ngeq"] = 8817,
-  ["LessTilde"] = 8818,
-  ["lesssim"] = 8818,
-  ["lsim"] = 8818,
-  ["GreaterTilde"] = 8819,
-  ["gsim"] = 8819,
-  ["gtrsim"] = 8819,
-  ["NotLessTilde"] = 8820,
-  ["nlsim"] = 8820,
-  ["NotGreaterTilde"] = 8821,
-  ["ngsim"] = 8821,
-  ["LessGreater"] = 8822,
-  ["lessgtr"] = 8822,
-  ["lg"] = 8822,
-  ["GreaterLess"] = 8823,
-  ["gl"] = 8823,
-  ["gtrless"] = 8823,
-  ["NotLessGreater"] = 8824,
-  ["ntlg"] = 8824,
-  ["NotGreaterLess"] = 8825,
-  ["ntgl"] = 8825,
-  ["Precedes"] = 8826,
-  ["pr"] = 8826,
-  ["prec"] = 8826,
-  ["Succeeds"] = 8827,
-  ["sc"] = 8827,
-  ["succ"] = 8827,
-  ["PrecedesSlantEqual"] = 8828,
-  ["prcue"] = 8828,
-  ["preccurlyeq"] = 8828,
-  ["SucceedsSlantEqual"] = 8829,
-  ["sccue"] = 8829,
-  ["succcurlyeq"] = 8829,
-  ["PrecedesTilde"] = 8830,
-  ["precsim"] = 8830,
-  ["prsim"] = 8830,
-  ["NotSucceedsTilde"] = {8831, 824},
-  ["SucceedsTilde"] = 8831,
-  ["scsim"] = 8831,
-  ["succsim"] = 8831,
-  ["NotPrecedes"] = 8832,
-  ["npr"] = 8832,
-  ["nprec"] = 8832,
-  ["NotSucceeds"] = 8833,
-  ["nsc"] = 8833,
-  ["nsucc"] = 8833,
-  ["NotSubset"] = {8834, 8402},
-  ["nsubset"] = {8834, 8402},
-  ["sub"] = 8834,
-  ["subset"] = 8834,
-  ["vnsub"] = {8834, 8402},
-  ["NotSuperset"] = {8835, 8402},
-  ["Superset"] = 8835,
-  ["nsupset"] = {8835, 8402},
-  ["sup"] = 8835,
-  ["supset"] = 8835,
-  ["vnsup"] = {8835, 8402},
-  ["nsub"] = 8836,
-  ["nsup"] = 8837,
-  ["SubsetEqual"] = 8838,
-  ["sube"] = 8838,
-  ["subseteq"] = 8838,
-  ["SupersetEqual"] = 8839,
-  ["supe"] = 8839,
-  ["supseteq"] = 8839,
-  ["NotSubsetEqual"] = 8840,
-  ["nsube"] = 8840,
-  ["nsubseteq"] = 8840,
-  ["NotSupersetEqual"] = 8841,
-  ["nsupe"] = 8841,
-  ["nsupseteq"] = 8841,
-  ["subne"] = 8842,
-  ["subsetneq"] = 8842,
-  ["varsubsetneq"] = {8842, 65024},
-  ["vsubne"] = {8842, 65024},
-  ["supne"] = 8843,
-  ["supsetneq"] = 8843,
-  ["varsupsetneq"] = {8843, 65024},
-  ["vsupne"] = {8843, 65024},
-  ["cupdot"] = 8845,
-  ["UnionPlus"] = 8846,
-  ["uplus"] = 8846,
-  ["NotSquareSubset"] = {8847, 824},
-  ["SquareSubset"] = 8847,
-  ["sqsub"] = 8847,
-  ["sqsubset"] = 8847,
-  ["NotSquareSuperset"] = {8848, 824},
-  ["SquareSuperset"] = 8848,
-  ["sqsup"] = 8848,
-  ["sqsupset"] = 8848,
-  ["SquareSubsetEqual"] = 8849,
-  ["sqsube"] = 8849,
-  ["sqsubseteq"] = 8849,
-  ["SquareSupersetEqual"] = 8850,
-  ["sqsupe"] = 8850,
-  ["sqsupseteq"] = 8850,
-  ["SquareIntersection"] = 8851,
-  ["sqcap"] = 8851,
-  ["sqcaps"] = {8851, 65024},
-  ["SquareUnion"] = 8852,
-  ["sqcup"] = 8852,
-  ["sqcups"] = {8852, 65024},
-  ["CirclePlus"] = 8853,
-  ["oplus"] = 8853,
-  ["CircleMinus"] = 8854,
-  ["ominus"] = 8854,
-  ["CircleTimes"] = 8855,
-  ["otimes"] = 8855,
-  ["osol"] = 8856,
-  ["CircleDot"] = 8857,
-  ["odot"] = 8857,
-  ["circledcirc"] = 8858,
-  ["ocir"] = 8858,
-  ["circledast"] = 8859,
-  ["oast"] = 8859,
-  ["circleddash"] = 8861,
-  ["odash"] = 8861,
-  ["boxplus"] = 8862,
-  ["plusb"] = 8862,
-  ["boxminus"] = 8863,
-  ["minusb"] = 8863,
-  ["boxtimes"] = 8864,
-  ["timesb"] = 8864,
-  ["dotsquare"] = 8865,
-  ["sdotb"] = 8865,
-  ["RightTee"] = 8866,
-  ["vdash"] = 8866,
-  ["LeftTee"] = 8867,
-  ["dashv"] = 8867,
-  ["DownTee"] = 8868,
-  ["top"] = 8868,
-  ["UpTee"] = 8869,
-  ["bot"] = 8869,
-  ["bottom"] = 8869,
-  ["perp"] = 8869,
-  ["models"] = 8871,
-  ["DoubleRightTee"] = 8872,
-  ["vDash"] = 8872,
-  ["Vdash"] = 8873,
-  ["Vvdash"] = 8874,
-  ["VDash"] = 8875,
-  ["nvdash"] = 8876,
-  ["nvDash"] = 8877,
-  ["nVdash"] = 8878,
-  ["nVDash"] = 8879,
-  ["prurel"] = 8880,
-  ["LeftTriangle"] = 8882,
-  ["vartriangleleft"] = 8882,
-  ["vltri"] = 8882,
-  ["RightTriangle"] = 8883,
-  ["vartriangleright"] = 8883,
-  ["vrtri"] = 8883,
-  ["LeftTriangleEqual"] = 8884,
-  ["ltrie"] = 8884,
-  ["nvltrie"] = {8884, 8402},
-  ["trianglelefteq"] = 8884,
-  ["RightTriangleEqual"] = 8885,
-  ["nvrtrie"] = {8885, 8402},
-  ["rtrie"] = 8885,
-  ["trianglerighteq"] = 8885,
-  ["origof"] = 8886,
-  ["imof"] = 8887,
-  ["multimap"] = 8888,
-  ["mumap"] = 8888,
-  ["hercon"] = 8889,
-  ["intcal"] = 8890,
-  ["intercal"] = 8890,
-  ["veebar"] = 8891,
-  ["barvee"] = 8893,
-  ["angrtvb"] = 8894,
-  ["lrtri"] = 8895,
-  ["Wedge"] = 8896,
-  ["bigwedge"] = 8896,
-  ["xwedge"] = 8896,
-  ["Vee"] = 8897,
-  ["bigvee"] = 8897,
-  ["xvee"] = 8897,
-  ["Intersection"] = 8898,
-  ["bigcap"] = 8898,
-  ["xcap"] = 8898,
-  ["Union"] = 8899,
-  ["bigcup"] = 8899,
-  ["xcup"] = 8899,
-  ["Diamond"] = 8900,
-  ["diam"] = 8900,
-  ["diamond"] = 8900,
-  ["sdot"] = 8901,
-  ["Star"] = 8902,
-  ["sstarf"] = 8902,
-  ["divideontimes"] = 8903,
-  ["divonx"] = 8903,
-  ["bowtie"] = 8904,
-  ["ltimes"] = 8905,
-  ["rtimes"] = 8906,
-  ["leftthreetimes"] = 8907,
-  ["lthree"] = 8907,
-  ["rightthreetimes"] = 8908,
-  ["rthree"] = 8908,
-  ["backsimeq"] = 8909,
-  ["bsime"] = 8909,
-  ["curlyvee"] = 8910,
-  ["cuvee"] = 8910,
-  ["curlywedge"] = 8911,
-  ["cuwed"] = 8911,
-  ["Sub"] = 8912,
-  ["Subset"] = 8912,
-  ["Sup"] = 8913,
-  ["Supset"] = 8913,
-  ["Cap"] = 8914,
-  ["Cup"] = 8915,
-  ["fork"] = 8916,
-  ["pitchfork"] = 8916,
-  ["epar"] = 8917,
-  ["lessdot"] = 8918,
-  ["ltdot"] = 8918,
-  ["gtdot"] = 8919,
-  ["gtrdot"] = 8919,
-  ["Ll"] = 8920,
-  ["nLl"] = {8920, 824},
-  ["Gg"] = 8921,
-  ["ggg"] = 8921,
-  ["nGg"] = {8921, 824},
-  ["LessEqualGreater"] = 8922,
-  ["leg"] = 8922,
-  ["lesg"] = {8922, 65024},
-  ["lesseqgtr"] = 8922,
-  ["GreaterEqualLess"] = 8923,
-  ["gel"] = 8923,
-  ["gesl"] = {8923, 65024},
-  ["gtreqless"] = 8923,
-  ["cuepr"] = 8926,
-  ["curlyeqprec"] = 8926,
-  ["cuesc"] = 8927,
-  ["curlyeqsucc"] = 8927,
-  ["NotPrecedesSlantEqual"] = 8928,
-  ["nprcue"] = 8928,
-  ["NotSucceedsSlantEqual"] = 8929,
-  ["nsccue"] = 8929,
-  ["NotSquareSubsetEqual"] = 8930,
-  ["nsqsube"] = 8930,
-  ["NotSquareSupersetEqual"] = 8931,
-  ["nsqsupe"] = 8931,
-  ["lnsim"] = 8934,
-  ["gnsim"] = 8935,
-  ["precnsim"] = 8936,
-  ["prnsim"] = 8936,
-  ["scnsim"] = 8937,
-  ["succnsim"] = 8937,
-  ["NotLeftTriangle"] = 8938,
-  ["nltri"] = 8938,
-  ["ntriangleleft"] = 8938,
-  ["NotRightTriangle"] = 8939,
-  ["nrtri"] = 8939,
-  ["ntriangleright"] = 8939,
-  ["NotLeftTriangleEqual"] = 8940,
-  ["nltrie"] = 8940,
-  ["ntrianglelefteq"] = 8940,
-  ["NotRightTriangleEqual"] = 8941,
-  ["nrtrie"] = 8941,
-  ["ntrianglerighteq"] = 8941,
-  ["vellip"] = 8942,
-  ["ctdot"] = 8943,
-  ["utdot"] = 8944,
-  ["dtdot"] = 8945,
-  ["disin"] = 8946,
-  ["isinsv"] = 8947,
-  ["isins"] = 8948,
-  ["isindot"] = 8949,
-  ["notindot"] = {8949, 824},
-  ["notinvc"] = 8950,
-  ["notinvb"] = 8951,
-  ["isinE"] = 8953,
-  ["notinE"] = {8953, 824},
-  ["nisd"] = 8954,
-  ["xnis"] = 8955,
-  ["nis"] = 8956,
-  ["notnivc"] = 8957,
-  ["notnivb"] = 8958,
-  ["barwed"] = 8965,
-  ["barwedge"] = 8965,
-  ["Barwed"] = 8966,
-  ["doublebarwedge"] = 8966,
-  ["LeftCeiling"] = 8968,
-  ["lceil"] = 8968,
-  ["RightCeiling"] = 8969,
-  ["rceil"] = 8969,
-  ["LeftFloor"] = 8970,
-  ["lfloor"] = 8970,
-  ["RightFloor"] = 8971,
-  ["rfloor"] = 8971,
-  ["drcrop"] = 8972,
-  ["dlcrop"] = 8973,
-  ["urcrop"] = 8974,
-  ["ulcrop"] = 8975,
-  ["bnot"] = 8976,
-  ["profline"] = 8978,
-  ["profsurf"] = 8979,
-  ["telrec"] = 8981,
-  ["target"] = 8982,
-  ["ulcorn"] = 8988,
-  ["ulcorner"] = 8988,
-  ["urcorn"] = 8989,
-  ["urcorner"] = 8989,
-  ["dlcorn"] = 8990,
-  ["llcorner"] = 8990,
-  ["drcorn"] = 8991,
-  ["lrcorner"] = 8991,
-  ["frown"] = 8994,
-  ["sfrown"] = 8994,
-  ["smile"] = 8995,
-  ["ssmile"] = 8995,
-  ["cylcty"] = 9005,
-  ["profalar"] = 9006,
-  ["topbot"] = 9014,
-  ["ovbar"] = 9021,
-  ["solbar"] = 9023,
-  ["angzarr"] = 9084,
-  ["lmoust"] = 9136,
-  ["lmoustache"] = 9136,
-  ["rmoust"] = 9137,
-  ["rmoustache"] = 9137,
-  ["OverBracket"] = 9140,
-  ["tbrk"] = 9140,
-  ["UnderBracket"] = 9141,
-  ["bbrk"] = 9141,
-  ["bbrktbrk"] = 9142,
-  ["OverParenthesis"] = 9180,
-  ["UnderParenthesis"] = 9181,
-  ["OverBrace"] = 9182,
-  ["UnderBrace"] = 9183,
-  ["trpezium"] = 9186,
-  ["elinters"] = 9191,
-  ["blank"] = 9251,
-  ["circledS"] = 9416,
-  ["oS"] = 9416,
-  ["HorizontalLine"] = 9472,
-  ["boxh"] = 9472,
-  ["boxv"] = 9474,
-  ["boxdr"] = 9484,
-  ["boxdl"] = 9488,
-  ["boxur"] = 9492,
-  ["boxul"] = 9496,
-  ["boxvr"] = 9500,
-  ["boxvl"] = 9508,
-  ["boxhd"] = 9516,
-  ["boxhu"] = 9524,
-  ["boxvh"] = 9532,
-  ["boxH"] = 9552,
-  ["boxV"] = 9553,
-  ["boxdR"] = 9554,
-  ["boxDr"] = 9555,
-  ["boxDR"] = 9556,
-  ["boxdL"] = 9557,
-  ["boxDl"] = 9558,
-  ["boxDL"] = 9559,
-  ["boxuR"] = 9560,
-  ["boxUr"] = 9561,
-  ["boxUR"] = 9562,
-  ["boxuL"] = 9563,
-  ["boxUl"] = 9564,
-  ["boxUL"] = 9565,
-  ["boxvR"] = 9566,
-  ["boxVr"] = 9567,
-  ["boxVR"] = 9568,
-  ["boxvL"] = 9569,
-  ["boxVl"] = 9570,
-  ["boxVL"] = 9571,
-  ["boxHd"] = 9572,
-  ["boxhD"] = 9573,
-  ["boxHD"] = 9574,
-  ["boxHu"] = 9575,
-  ["boxhU"] = 9576,
-  ["boxHU"] = 9577,
-  ["boxvH"] = 9578,
-  ["boxVh"] = 9579,
-  ["boxVH"] = 9580,
-  ["uhblk"] = 9600,
-  ["lhblk"] = 9604,
-  ["block"] = 9608,
-  ["blk14"] = 9617,
-  ["blk12"] = 9618,
-  ["blk34"] = 9619,
-  ["Square"] = 9633,
-  ["squ"] = 9633,
-  ["square"] = 9633,
-  ["FilledVerySmallSquare"] = 9642,
-  ["blacksquare"] = 9642,
-  ["squarf"] = 9642,
-  ["squf"] = 9642,
-  ["EmptyVerySmallSquare"] = 9643,
-  ["rect"] = 9645,
-  ["marker"] = 9646,
-  ["fltns"] = 9649,
-  ["bigtriangleup"] = 9651,
-  ["xutri"] = 9651,
-  ["blacktriangle"] = 9652,
-  ["utrif"] = 9652,
-  ["triangle"] = 9653,
-  ["utri"] = 9653,
-  ["blacktriangleright"] = 9656,
-  ["rtrif"] = 9656,
-  ["rtri"] = 9657,
-  ["triangleright"] = 9657,
-  ["bigtriangledown"] = 9661,
-  ["xdtri"] = 9661,
-  ["blacktriangledown"] = 9662,
-  ["dtrif"] = 9662,
-  ["dtri"] = 9663,
-  ["triangledown"] = 9663,
-  ["blacktriangleleft"] = 9666,
-  ["ltrif"] = 9666,
-  ["ltri"] = 9667,
-  ["triangleleft"] = 9667,
-  ["loz"] = 9674,
-  ["lozenge"] = 9674,
-  ["cir"] = 9675,
-  ["tridot"] = 9708,
-  ["bigcirc"] = 9711,
-  ["xcirc"] = 9711,
-  ["ultri"] = 9720,
-  ["urtri"] = 9721,
-  ["lltri"] = 9722,
-  ["EmptySmallSquare"] = 9723,
-  ["FilledSmallSquare"] = 9724,
-  ["bigstar"] = 9733,
-  ["starf"] = 9733,
-  ["star"] = 9734,
-  ["phone"] = 9742,
-  ["female"] = 9792,
-  ["male"] = 9794,
-  ["spades"] = 9824,
-  ["spadesuit"] = 9824,
-  ["clubs"] = 9827,
-  ["clubsuit"] = 9827,
-  ["hearts"] = 9829,
-  ["heartsuit"] = 9829,
-  ["diamondsuit"] = 9830,
-  ["diams"] = 9830,
-  ["sung"] = 9834,
-  ["flat"] = 9837,
-  ["natur"] = 9838,
-  ["natural"] = 9838,
-  ["sharp"] = 9839,
-  ["check"] = 10003,
-  ["checkmark"] = 10003,
-  ["cross"] = 10007,
-  ["malt"] = 10016,
-  ["maltese"] = 10016,
-  ["sext"] = 10038,
-  ["VerticalSeparator"] = 10072,
-  ["lbbrk"] = 10098,
-  ["rbbrk"] = 10099,
-  ["bsolhsub"] = 10184,
-  ["suphsol"] = 10185,
-  ["LeftDoubleBracket"] = 10214,
-  ["lobrk"] = 10214,
-  ["RightDoubleBracket"] = 10215,
-  ["robrk"] = 10215,
-  ["LeftAngleBracket"] = 10216,
-  ["lang"] = 10216,
-  ["langle"] = 10216,
-  ["RightAngleBracket"] = 10217,
-  ["rang"] = 10217,
-  ["rangle"] = 10217,
-  ["Lang"] = 10218,
-  ["Rang"] = 10219,
-  ["loang"] = 10220,
-  ["roang"] = 10221,
-  ["LongLeftArrow"] = 10229,
-  ["longleftarrow"] = 10229,
-  ["xlarr"] = 10229,
-  ["LongRightArrow"] = 10230,
-  ["longrightarrow"] = 10230,
-  ["xrarr"] = 10230,
-  ["LongLeftRightArrow"] = 10231,
-  ["longleftrightarrow"] = 10231,
-  ["xharr"] = 10231,
-  ["DoubleLongLeftArrow"] = 10232,
-  ["Longleftarrow"] = 10232,
-  ["xlArr"] = 10232,
-  ["DoubleLongRightArrow"] = 10233,
-  ["Longrightarrow"] = 10233,
-  ["xrArr"] = 10233,
-  ["DoubleLongLeftRightArrow"] = 10234,
-  ["Longleftrightarrow"] = 10234,
-  ["xhArr"] = 10234,
-  ["longmapsto"] = 10236,
-  ["xmap"] = 10236,
-  ["dzigrarr"] = 10239,
-  ["nvlArr"] = 10498,
-  ["nvrArr"] = 10499,
-  ["nvHarr"] = 10500,
-  ["Map"] = 10501,
-  ["lbarr"] = 10508,
-  ["bkarow"] = 10509,
-  ["rbarr"] = 10509,
-  ["lBarr"] = 10510,
-  ["dbkarow"] = 10511,
-  ["rBarr"] = 10511,
-  ["RBarr"] = 10512,
-  ["drbkarow"] = 10512,
-  ["DDotrahd"] = 10513,
-  ["UpArrowBar"] = 10514,
-  ["DownArrowBar"] = 10515,
-  ["Rarrtl"] = 10518,
-  ["latail"] = 10521,
-  ["ratail"] = 10522,
-  ["lAtail"] = 10523,
-  ["rAtail"] = 10524,
-  ["larrfs"] = 10525,
-  ["rarrfs"] = 10526,
-  ["larrbfs"] = 10527,
-  ["rarrbfs"] = 10528,
-  ["nwarhk"] = 10531,
-  ["nearhk"] = 10532,
-  ["hksearow"] = 10533,
-  ["searhk"] = 10533,
-  ["hkswarow"] = 10534,
-  ["swarhk"] = 10534,
-  ["nwnear"] = 10535,
-  ["nesear"] = 10536,
-  ["toea"] = 10536,
-  ["seswar"] = 10537,
-  ["tosa"] = 10537,
-  ["swnwar"] = 10538,
-  ["nrarrc"] = {10547, 824},
-  ["rarrc"] = 10547,
-  ["cudarrr"] = 10549,
-  ["ldca"] = 10550,
-  ["rdca"] = 10551,
-  ["cudarrl"] = 10552,
-  ["larrpl"] = 10553,
-  ["curarrm"] = 10556,
-  ["cularrp"] = 10557,
-  ["rarrpl"] = 10565,
-  ["harrcir"] = 10568,
-  ["Uarrocir"] = 10569,
-  ["lurdshar"] = 10570,
-  ["ldrushar"] = 10571,
-  ["LeftRightVector"] = 10574,
-  ["RightUpDownVector"] = 10575,
-  ["DownLeftRightVector"] = 10576,
-  ["LeftUpDownVector"] = 10577,
-  ["LeftVectorBar"] = 10578,
-  ["RightVectorBar"] = 10579,
-  ["RightUpVectorBar"] = 10580,
-  ["RightDownVectorBar"] = 10581,
-  ["DownLeftVectorBar"] = 10582,
-  ["DownRightVectorBar"] = 10583,
-  ["LeftUpVectorBar"] = 10584,
-  ["LeftDownVectorBar"] = 10585,
-  ["LeftTeeVector"] = 10586,
-  ["RightTeeVector"] = 10587,
-  ["RightUpTeeVector"] = 10588,
-  ["RightDownTeeVector"] = 10589,
-  ["DownLeftTeeVector"] = 10590,
-  ["DownRightTeeVector"] = 10591,
-  ["LeftUpTeeVector"] = 10592,
-  ["LeftDownTeeVector"] = 10593,
-  ["lHar"] = 10594,
-  ["uHar"] = 10595,
-  ["rHar"] = 10596,
-  ["dHar"] = 10597,
-  ["luruhar"] = 10598,
-  ["ldrdhar"] = 10599,
-  ["ruluhar"] = 10600,
-  ["rdldhar"] = 10601,
-  ["lharul"] = 10602,
-  ["llhard"] = 10603,
-  ["rharul"] = 10604,
-  ["lrhard"] = 10605,
-  ["UpEquilibrium"] = 10606,
-  ["udhar"] = 10606,
-  ["ReverseUpEquilibrium"] = 10607,
-  ["duhar"] = 10607,
-  ["RoundImplies"] = 10608,
-  ["erarr"] = 10609,
-  ["simrarr"] = 10610,
-  ["larrsim"] = 10611,
-  ["rarrsim"] = 10612,
-  ["rarrap"] = 10613,
-  ["ltlarr"] = 10614,
-  ["gtrarr"] = 10616,
-  ["subrarr"] = 10617,
-  ["suplarr"] = 10619,
-  ["lfisht"] = 10620,
-  ["rfisht"] = 10621,
-  ["ufisht"] = 10622,
-  ["dfisht"] = 10623,
-  ["lopar"] = 10629,
-  ["ropar"] = 10630,
-  ["lbrke"] = 10635,
-  ["rbrke"] = 10636,
-  ["lbrkslu"] = 10637,
-  ["rbrksld"] = 10638,
-  ["lbrksld"] = 10639,
-  ["rbrkslu"] = 10640,
-  ["langd"] = 10641,
-  ["rangd"] = 10642,
-  ["lparlt"] = 10643,
-  ["rpargt"] = 10644,
-  ["gtlPar"] = 10645,
-  ["ltrPar"] = 10646,
-  ["vzigzag"] = 10650,
-  ["vangrt"] = 10652,
-  ["angrtvbd"] = 10653,
-  ["ange"] = 10660,
-  ["range"] = 10661,
-  ["dwangle"] = 10662,
-  ["uwangle"] = 10663,
-  ["angmsdaa"] = 10664,
-  ["angmsdab"] = 10665,
-  ["angmsdac"] = 10666,
-  ["angmsdad"] = 10667,
-  ["angmsdae"] = 10668,
-  ["angmsdaf"] = 10669,
-  ["angmsdag"] = 10670,
-  ["angmsdah"] = 10671,
-  ["bemptyv"] = 10672,
-  ["demptyv"] = 10673,
-  ["cemptyv"] = 10674,
-  ["raemptyv"] = 10675,
-  ["laemptyv"] = 10676,
-  ["ohbar"] = 10677,
-  ["omid"] = 10678,
-  ["opar"] = 10679,
-  ["operp"] = 10681,
-  ["olcross"] = 10683,
-  ["odsold"] = 10684,
-  ["olcir"] = 10686,
-  ["ofcir"] = 10687,
-  ["olt"] = 10688,
-  ["ogt"] = 10689,
-  ["cirscir"] = 10690,
-  ["cirE"] = 10691,
-  ["solb"] = 10692,
-  ["bsolb"] = 10693,
-  ["boxbox"] = 10697,
-  ["trisb"] = 10701,
-  ["rtriltri"] = 10702,
-  ["LeftTriangleBar"] = 10703,
-  ["NotLeftTriangleBar"] = {10703, 824},
-  ["NotRightTriangleBar"] = {10704, 824},
-  ["RightTriangleBar"] = 10704,
-  ["iinfin"] = 10716,
-  ["infintie"] = 10717,
-  ["nvinfin"] = 10718,
-  ["eparsl"] = 10723,
-  ["smeparsl"] = 10724,
-  ["eqvparsl"] = 10725,
-  ["blacklozenge"] = 10731,
-  ["lozf"] = 10731,
-  ["RuleDelayed"] = 10740,
-  ["dsol"] = 10742,
-  ["bigodot"] = 10752,
-  ["xodot"] = 10752,
-  ["bigoplus"] = 10753,
-  ["xoplus"] = 10753,
-  ["bigotimes"] = 10754,
-  ["xotime"] = 10754,
-  ["biguplus"] = 10756,
-  ["xuplus"] = 10756,
-  ["bigsqcup"] = 10758,
-  ["xsqcup"] = 10758,
-  ["iiiint"] = 10764,
-  ["qint"] = 10764,
-  ["fpartint"] = 10765,
-  ["cirfnint"] = 10768,
-  ["awint"] = 10769,
-  ["rppolint"] = 10770,
-  ["scpolint"] = 10771,
-  ["npolint"] = 10772,
-  ["pointint"] = 10773,
-  ["quatint"] = 10774,
-  ["intlarhk"] = 10775,
-  ["pluscir"] = 10786,
-  ["plusacir"] = 10787,
-  ["simplus"] = 10788,
-  ["plusdu"] = 10789,
-  ["plussim"] = 10790,
-  ["plustwo"] = 10791,
-  ["mcomma"] = 10793,
-  ["minusdu"] = 10794,
-  ["loplus"] = 10797,
-  ["roplus"] = 10798,
-  ["Cross"] = 10799,
-  ["timesd"] = 10800,
-  ["timesbar"] = 10801,
-  ["smashp"] = 10803,
-  ["lotimes"] = 10804,
-  ["rotimes"] = 10805,
-  ["otimesas"] = 10806,
-  ["Otimes"] = 10807,
-  ["odiv"] = 10808,
-  ["triplus"] = 10809,
-  ["triminus"] = 10810,
-  ["tritime"] = 10811,
-  ["intprod"] = 10812,
-  ["iprod"] = 10812,
-  ["amalg"] = 10815,
-  ["capdot"] = 10816,
-  ["ncup"] = 10818,
-  ["ncap"] = 10819,
-  ["capand"] = 10820,
-  ["cupor"] = 10821,
-  ["cupcap"] = 10822,
-  ["capcup"] = 10823,
-  ["cupbrcap"] = 10824,
-  ["capbrcup"] = 10825,
-  ["cupcup"] = 10826,
-  ["capcap"] = 10827,
-  ["ccups"] = 10828,
-  ["ccaps"] = 10829,
-  ["ccupssm"] = 10832,
-  ["And"] = 10835,
-  ["Or"] = 10836,
-  ["andand"] = 10837,
-  ["oror"] = 10838,
-  ["orslope"] = 10839,
-  ["andslope"] = 10840,
-  ["andv"] = 10842,
-  ["orv"] = 10843,
-  ["andd"] = 10844,
-  ["ord"] = 10845,
-  ["wedbar"] = 10847,
-  ["sdote"] = 10854,
-  ["simdot"] = 10858,
-  ["congdot"] = 10861,
-  ["ncongdot"] = {10861, 824},
-  ["easter"] = 10862,
-  ["apacir"] = 10863,
-  ["apE"] = 10864,
-  ["napE"] = {10864, 824},
-  ["eplus"] = 10865,
-  ["pluse"] = 10866,
-  ["Esim"] = 10867,
-  ["Colone"] = 10868,
-  ["Equal"] = 10869,
-  ["ddotseq"] = 10871,
-  ["eDDot"] = 10871,
-  ["equivDD"] = 10872,
-  ["ltcir"] = 10873,
-  ["gtcir"] = 10874,
-  ["ltquest"] = 10875,
-  ["gtquest"] = 10876,
-  ["LessSlantEqual"] = 10877,
-  ["NotLessSlantEqual"] = {10877, 824},
-  ["leqslant"] = 10877,
-  ["les"] = 10877,
-  ["nleqslant"] = {10877, 824},
-  ["nles"] = {10877, 824},
-  ["GreaterSlantEqual"] = 10878,
-  ["NotGreaterSlantEqual"] = {10878, 824},
-  ["geqslant"] = 10878,
-  ["ges"] = 10878,
-  ["ngeqslant"] = {10878, 824},
-  ["nges"] = {10878, 824},
-  ["lesdot"] = 10879,
-  ["gesdot"] = 10880,
-  ["lesdoto"] = 10881,
-  ["gesdoto"] = 10882,
-  ["lesdotor"] = 10883,
-  ["gesdotol"] = 10884,
-  ["lap"] = 10885,
-  ["lessapprox"] = 10885,
-  ["gap"] = 10886,
-  ["gtrapprox"] = 10886,
-  ["lne"] = 10887,
-  ["lneq"] = 10887,
-  ["gne"] = 10888,
-  ["gneq"] = 10888,
-  ["lnap"] = 10889,
-  ["lnapprox"] = 10889,
-  ["gnap"] = 10890,
-  ["gnapprox"] = 10890,
-  ["lEg"] = 10891,
-  ["lesseqqgtr"] = 10891,
-  ["gEl"] = 10892,
-  ["gtreqqless"] = 10892,
-  ["lsime"] = 10893,
-  ["gsime"] = 10894,
-  ["lsimg"] = 10895,
-  ["gsiml"] = 10896,
-  ["lgE"] = 10897,
-  ["glE"] = 10898,
-  ["lesges"] = 10899,
-  ["gesles"] = 10900,
-  ["els"] = 10901,
-  ["eqslantless"] = 10901,
-  ["egs"] = 10902,
-  ["eqslantgtr"] = 10902,
-  ["elsdot"] = 10903,
-  ["egsdot"] = 10904,
-  ["el"] = 10905,
-  ["eg"] = 10906,
-  ["siml"] = 10909,
-  ["simg"] = 10910,
-  ["simlE"] = 10911,
-  ["simgE"] = 10912,
-  ["LessLess"] = 10913,
-  ["NotNestedLessLess"] = {10913, 824},
-  ["GreaterGreater"] = 10914,
-  ["NotNestedGreaterGreater"] = {10914, 824},
-  ["glj"] = 10916,
-  ["gla"] = 10917,
-  ["ltcc"] = 10918,
-  ["gtcc"] = 10919,
-  ["lescc"] = 10920,
-  ["gescc"] = 10921,
-  ["smt"] = 10922,
-  ["lat"] = 10923,
-  ["smte"] = 10924,
-  ["smtes"] = {10924, 65024},
-  ["late"] = 10925,
-  ["lates"] = {10925, 65024},
-  ["bumpE"] = 10926,
-  ["NotPrecedesEqual"] = {10927, 824},
-  ["PrecedesEqual"] = 10927,
-  ["npre"] = {10927, 824},
-  ["npreceq"] = {10927, 824},
-  ["pre"] = 10927,
-  ["preceq"] = 10927,
-  ["NotSucceedsEqual"] = {10928, 824},
-  ["SucceedsEqual"] = 10928,
-  ["nsce"] = {10928, 824},
-  ["nsucceq"] = {10928, 824},
-  ["sce"] = 10928,
-  ["succeq"] = 10928,
-  ["prE"] = 10931,
-  ["scE"] = 10932,
-  ["precneqq"] = 10933,
-  ["prnE"] = 10933,
-  ["scnE"] = 10934,
-  ["succneqq"] = 10934,
-  ["prap"] = 10935,
-  ["precapprox"] = 10935,
-  ["scap"] = 10936,
-  ["succapprox"] = 10936,
-  ["precnapprox"] = 10937,
-  ["prnap"] = 10937,
-  ["scnap"] = 10938,
-  ["succnapprox"] = 10938,
-  ["Pr"] = 10939,
-  ["Sc"] = 10940,
-  ["subdot"] = 10941,
-  ["supdot"] = 10942,
-  ["subplus"] = 10943,
-  ["supplus"] = 10944,
-  ["submult"] = 10945,
-  ["supmult"] = 10946,
-  ["subedot"] = 10947,
-  ["supedot"] = 10948,
-  ["nsubE"] = {10949, 824},
-  ["nsubseteqq"] = {10949, 824},
-  ["subE"] = 10949,
-  ["subseteqq"] = 10949,
-  ["nsupE"] = {10950, 824},
-  ["nsupseteqq"] = {10950, 824},
-  ["supE"] = 10950,
-  ["supseteqq"] = 10950,
-  ["subsim"] = 10951,
-  ["supsim"] = 10952,
-  ["subnE"] = 10955,
-  ["subsetneqq"] = 10955,
-  ["varsubsetneqq"] = {10955, 65024},
-  ["vsubnE"] = {10955, 65024},
-  ["supnE"] = 10956,
-  ["supsetneqq"] = 10956,
-  ["varsupsetneqq"] = {10956, 65024},
-  ["vsupnE"] = {10956, 65024},
-  ["csub"] = 10959,
-  ["csup"] = 10960,
-  ["csube"] = 10961,
-  ["csupe"] = 10962,
-  ["subsup"] = 10963,
-  ["supsub"] = 10964,
-  ["subsub"] = 10965,
-  ["supsup"] = 10966,
-  ["suphsub"] = 10967,
-  ["supdsub"] = 10968,
-  ["forkv"] = 10969,
-  ["topfork"] = 10970,
-  ["mlcp"] = 10971,
-  ["Dashv"] = 10980,
-  ["DoubleLeftTee"] = 10980,
-  ["Vdashl"] = 10982,
-  ["Barv"] = 10983,
-  ["vBar"] = 10984,
-  ["vBarv"] = 10985,
-  ["Vbar"] = 10987,
-  ["Not"] = 10988,
-  ["bNot"] = 10989,
-  ["rnmid"] = 10990,
-  ["cirmid"] = 10991,
-  ["midcir"] = 10992,
-  ["topcir"] = 10993,
-  ["nhpar"] = 10994,
-  ["parsim"] = 10995,
-  ["nparsl"] = {11005, 8421},
-  ["parsl"] = 11005,
-  ["fflig"] = 64256,
-  ["filig"] = 64257,
-  ["fllig"] = 64258,
-  ["ffilig"] = 64259,
-  ["ffllig"] = 64260,
-  ["Ascr"] = 119964,
-  ["Cscr"] = 119966,
-  ["Dscr"] = 119967,
-  ["Gscr"] = 119970,
-  ["Jscr"] = 119973,
-  ["Kscr"] = 119974,
-  ["Nscr"] = 119977,
-  ["Oscr"] = 119978,
-  ["Pscr"] = 119979,
-  ["Qscr"] = 119980,
-  ["Sscr"] = 119982,
-  ["Tscr"] = 119983,
-  ["Uscr"] = 119984,
-  ["Vscr"] = 119985,
-  ["Wscr"] = 119986,
-  ["Xscr"] = 119987,
-  ["Yscr"] = 119988,
-  ["Zscr"] = 119989,
-  ["ascr"] = 119990,
-  ["bscr"] = 119991,
-  ["cscr"] = 119992,
-  ["dscr"] = 119993,
-  ["fscr"] = 119995,
-  ["hscr"] = 119997,
-  ["iscr"] = 119998,
-  ["jscr"] = 119999,
-  ["kscr"] = 120000,
-  ["lscr"] = 120001,
-  ["mscr"] = 120002,
-  ["nscr"] = 120003,
-  ["pscr"] = 120005,
-  ["qscr"] = 120006,
-  ["rscr"] = 120007,
-  ["sscr"] = 120008,
-  ["tscr"] = 120009,
-  ["uscr"] = 120010,
-  ["vscr"] = 120011,
-  ["wscr"] = 120012,
-  ["xscr"] = 120013,
-  ["yscr"] = 120014,
-  ["zscr"] = 120015,
-  ["Afr"] = 120068,
-  ["Bfr"] = 120069,
-  ["Dfr"] = 120071,
-  ["Efr"] = 120072,
-  ["Ffr"] = 120073,
-  ["Gfr"] = 120074,
-  ["Jfr"] = 120077,
-  ["Kfr"] = 120078,
-  ["Lfr"] = 120079,
-  ["Mfr"] = 120080,
-  ["Nfr"] = 120081,
-  ["Ofr"] = 120082,
-  ["Pfr"] = 120083,
-  ["Qfr"] = 120084,
-  ["Sfr"] = 120086,
-  ["Tfr"] = 120087,
-  ["Ufr"] = 120088,
-  ["Vfr"] = 120089,
-  ["Wfr"] = 120090,
-  ["Xfr"] = 120091,
-  ["Yfr"] = 120092,
-  ["afr"] = 120094,
-  ["bfr"] = 120095,
-  ["cfr"] = 120096,
-  ["dfr"] = 120097,
-  ["efr"] = 120098,
-  ["ffr"] = 120099,
-  ["gfr"] = 120100,
-  ["hfr"] = 120101,
-  ["ifr"] = 120102,
-  ["jfr"] = 120103,
-  ["kfr"] = 120104,
-  ["lfr"] = 120105,
-  ["mfr"] = 120106,
-  ["nfr"] = 120107,
-  ["ofr"] = 120108,
-  ["pfr"] = 120109,
-  ["qfr"] = 120110,
-  ["rfr"] = 120111,
-  ["sfr"] = 120112,
-  ["tfr"] = 120113,
-  ["ufr"] = 120114,
-  ["vfr"] = 120115,
-  ["wfr"] = 120116,
-  ["xfr"] = 120117,
-  ["yfr"] = 120118,
-  ["zfr"] = 120119,
-  ["Aopf"] = 120120,
-  ["Bopf"] = 120121,
-  ["Dopf"] = 120123,
-  ["Eopf"] = 120124,
-  ["Fopf"] = 120125,
-  ["Gopf"] = 120126,
-  ["Iopf"] = 120128,
-  ["Jopf"] = 120129,
-  ["Kopf"] = 120130,
-  ["Lopf"] = 120131,
-  ["Mopf"] = 120132,
-  ["Oopf"] = 120134,
-  ["Sopf"] = 120138,
-  ["Topf"] = 120139,
-  ["Uopf"] = 120140,
-  ["Vopf"] = 120141,
-  ["Wopf"] = 120142,
-  ["Xopf"] = 120143,
-  ["Yopf"] = 120144,
-  ["aopf"] = 120146,
-  ["bopf"] = 120147,
-  ["copf"] = 120148,
-  ["dopf"] = 120149,
-  ["eopf"] = 120150,
-  ["fopf"] = 120151,
-  ["gopf"] = 120152,
-  ["hopf"] = 120153,
-  ["iopf"] = 120154,
-  ["jopf"] = 120155,
-  ["kopf"] = 120156,
-  ["lopf"] = 120157,
-  ["mopf"] = 120158,
-  ["nopf"] = 120159,
-  ["oopf"] = 120160,
-  ["popf"] = 120161,
-  ["qopf"] = 120162,
-  ["ropf"] = 120163,
-  ["sopf"] = 120164,
-  ["topf"] = 120165,
-  ["uopf"] = 120166,
-  ["vopf"] = 120167,
-  ["wopf"] = 120168,
-  ["xopf"] = 120169,
-  ["yopf"] = 120170,
-  ["zopf"] = 120171,
-}
-function entities.dec_entity(s)
-  local n = tonumber(s)
-  if n == nil then
-    return "&#" .. s .. ";"  -- fallback for unknown entities
-  end
-  return unicode.utf8.char(n)
-end
-function entities.hex_entity(s)
-  local n = tonumber("0x"..s)
-  if n == nil then
-    return "&#x" .. s .. ";"  -- fallback for unknown entities
-  end
-  return unicode.utf8.char(n)
-end
-function entities.hex_entity_with_x_char(x, s)
-  local n = tonumber("0x"..s)
-  if n == nil then
-    return "&#" .. x .. s .. ";"  -- fallback for unknown entities
-  end
-  return unicode.utf8.char(n)
-end
-function entities.char_entity(s)
-  local code_points = character_entities[s]
-  if code_points == nil then
-    return "&" .. s .. ";"
-  end
-  if type(code_points) ~= 'table' then
-    code_points = {code_points}
-  end
-  local char_table = {}
-    for _, code_point in ipairs(code_points) do
-      table.insert(char_table, unicode.utf8.char(code_point))
-    end
-  return table.concat(char_table)
-end
-M.writer = {}
-function M.writer.new(options)
-  local self = {}
-  self.options = options
-  self.flatten_inlines = false
-  local slice_specifiers = {}
-  for specifier in options.slice:gmatch("[^%s]+") do
-    table.insert(slice_specifiers, specifier)
-  end
-
-  if #slice_specifiers == 2 then
-    self.slice_begin, self.slice_end = table.unpack(slice_specifiers)
-    local slice_begin_type = self.slice_begin:sub(1, 1)
-    if slice_begin_type ~= "^" and slice_begin_type ~= "$" then
-      self.slice_begin = "^" .. self.slice_begin
-    end
-    local slice_end_type = self.slice_end:sub(1, 1)
-    if slice_end_type ~= "^" and slice_end_type ~= "$" then
-      self.slice_end = "$" .. self.slice_end
-    end
-  elseif #slice_specifiers == 1 then
-    self.slice_begin = "^" .. slice_specifiers[1]
-    self.slice_end = "$" .. slice_specifiers[1]
-  end
-
-  self.slice_begin_type = self.slice_begin:sub(1, 1)
-  self.slice_begin_identifier = self.slice_begin:sub(2) or ""
-  self.slice_end_type = self.slice_end:sub(1, 1)
-  self.slice_end_identifier = self.slice_end:sub(2) or ""
-
-  if self.slice_begin == "^" and self.slice_end ~= "^" then
-    self.is_writing = true
-  else
-    self.is_writing = false
-  end
-  self.suffix = ".tex"
-  self.space = " "
-  self.nbsp = "\\markdownRendererNbsp{}"
-  function self.plain(s)
-    return s
-  end
-  function self.paragraph(s)
-    if not self.is_writing then return "" end
-    return s
-  end
-  function self.pack(name)
-    return [[\input{]] .. name .. [[}\relax]]
-  end
-  self.interblocksep_text = "\\markdownRendererInterblockSeparator\n{}"
-  function self.interblocksep()
-    if not self.is_writing then return "" end
-    return self.interblocksep_text
-  end
-  self.paragraphsep_text = "\\markdownRendererParagraphSeparator\n{}"
-  function self.paragraphsep()
-    if not self.is_writing then return "" end
-    return self.paragraphsep_text
-  end
-  self.undosep_text = "\\markdownRendererUndoSeparator\n{}"
-  function self.undosep()
-    if not self.is_writing then return "" end
-    return self.undosep_text
-  end
-  self.soft_line_break = function()
-    if self.flatten_inlines then return "\n" end
-    return "\\markdownRendererSoftLineBreak\n{}"
-  end
-  self.hard_line_break = function()
-    if self.flatten_inlines then return "\n" end
-    return "\\markdownRendererHardLineBreak\n{}"
-  end
-  self.ellipsis = "\\markdownRendererEllipsis{}"
-  function self.thematic_break()
-    if not self.is_writing then return "" end
-    return "\\markdownRendererThematicBreak{}"
-  end
-  self.escaped_uri_chars = {
-    ["{"] = "\\markdownRendererLeftBrace{}",
-    ["}"] = "\\markdownRendererRightBrace{}",
-    ["\\"] = "\\markdownRendererBackslash{}",
-  }
-  self.escaped_minimal_strings = {
-    ["^^"] = "\\markdownRendererCircumflex\\markdownRendererCircumflex ",
-    ["☒"] = "\\markdownRendererTickedBox{}",
-    ["⌛"] = "\\markdownRendererHalfTickedBox{}",
-    ["☐"] = "\\markdownRendererUntickedBox{}",
-    [entities.hex_entity('FFFD')] = "\\markdownRendererReplacementCharacter{}",
-  }
-  self.escaped_strings = util.table_copy(self.escaped_minimal_strings)
-  self.escaped_strings[entities.hex_entity('00A0')] = self.nbsp
-  self.escaped_chars = {
-    ["{"] = "\\markdownRendererLeftBrace{}",
-    ["}"] = "\\markdownRendererRightBrace{}",
-    ["%"] = "\\markdownRendererPercentSign{}",
-    ["\\"] = "\\markdownRendererBackslash{}",
-    ["#"] = "\\markdownRendererHash{}",
-    ["$"] = "\\markdownRendererDollarSign{}",
-    ["&"] = "\\markdownRendererAmpersand{}",
-    ["_"] = "\\markdownRendererUnderscore{}",
-    ["^"] = "\\markdownRendererCircumflex{}",
-    ["~"] = "\\markdownRendererTilde{}",
-    ["|"] = "\\markdownRendererPipe{}",
-    [entities.hex_entity('0000')] = "\\markdownRendererReplacementCharacter{}",
-  }
-  local function create_escaper(char_escapes, string_escapes)
-    local escape = util.escaper(char_escapes, string_escapes)
-    return function(s)
-      if self.flatten_inlines then return s end
-      return escape(s)
-    end
-  end
-  local escape_typographic_text = create_escaper(
-    self.escaped_chars, self.escaped_strings)
-  local escape_programmatic_text = create_escaper(
-    self.escaped_uri_chars, self.escaped_minimal_strings)
-  local escape_minimal = create_escaper(
-    {}, self.escaped_minimal_strings)
-  self.escape = escape_typographic_text
-  self.math = escape_minimal
-  if options.hybrid then
-    self.identifier = escape_minimal
-    self.string = escape_minimal
-    self.uri = escape_minimal
-    self.infostring = escape_minimal
-  else
-    self.identifier = escape_programmatic_text
-    self.string = escape_typographic_text
-    self.uri = escape_programmatic_text
-    self.infostring = escape_programmatic_text
-  end
-  function self.code(s, attributes)
-    if self.flatten_inlines then return s end
-    local buf = {}
-    if attributes ~= nil then
-      table.insert(buf,
-                   "\\markdownRendererCodeSpanAttributeContextBegin\n")
-      table.insert(buf, self.attributes(attributes))
-    end
-    table.insert(buf,
-                 {"\\markdownRendererCodeSpan{", self.escape(s), "}"})
-    if attributes ~= nil then
-      table.insert(buf,
-                   "\\markdownRendererCodeSpanAttributeContextEnd{}")
-    end
-    return buf
-  end
-  function self.link(lab, src, tit, attributes)
-    if self.flatten_inlines then return lab end
-    local buf = {}
-    if attributes ~= nil then
-      table.insert(buf,
-                   "\\markdownRendererLinkAttributeContextBegin\n")
-      table.insert(buf, self.attributes(attributes))
-    end
-    table.insert(buf, {"\\markdownRendererLink{",lab,"}",
-                       "{",self.escape(src),"}",
-                       "{",self.uri(src),"}",
-                       "{",self.string(tit or ""),"}"})
-    if attributes ~= nil then
-      table.insert(buf,
-                   "\\markdownRendererLinkAttributeContextEnd{}")
-    end
-    return buf
-  end
-  function self.image(lab, src, tit, attributes)
-    if self.flatten_inlines then return lab end
-    local buf = {}
-    if attributes ~= nil then
-      table.insert(buf,
-                   "\\markdownRendererImageAttributeContextBegin\n")
-      table.insert(buf, self.attributes(attributes))
-    end
-    table.insert(buf, {"\\markdownRendererImage{",lab,"}",
-                       "{",self.string(src),"}",
-                       "{",self.uri(src),"}",
-                       "{",self.string(tit or ""),"}"})
-    if attributes ~= nil then
-      table.insert(buf,
-                   "\\markdownRendererImageAttributeContextEnd{}")
-    end
-    return buf
-  end
-  function self.bulletlist(items,tight)
-    if not self.is_writing then return "" end
-    local buffer = {}
-    for _,item in ipairs(items) do
-      if item ~= "" then
-        buffer[#buffer + 1] = self.bulletitem(item)
+function util.salt(options)
+  local opt_string = {}
+  for k, _ in pairs(defaultOptions) do
+    local v = options[k]
+    if type(v) == "table" then
+      for _, i in ipairs(v) do
+        opt_string[#opt_string+1] = k .. "=" .. tostring(i)
       end
+    elseif k ~= "cacheDir" then
+      opt_string[#opt_string+1] = k .. "=" .. tostring(v)
     end
-    local contents = util.intersperse(buffer,"\n")
-    if tight and options.tightLists then
-      return {"\\markdownRendererUlBeginTight\n",contents,
-        "\n\\markdownRendererUlEndTight "}
-    else
-      return {"\\markdownRendererUlBegin\n",contents,
-        "\n\\markdownRendererUlEnd "}
-    end
   end
-  function self.bulletitem(s)
-    return {"\\markdownRendererUlItem ",s,
-            "\\markdownRendererUlItemEnd "}
-  end
-  function self.orderedlist(items,tight,startnum)
-    if not self.is_writing then return "" end
-    local buffer = {}
-    local num = startnum
-    for _,item in ipairs(items) do
-      if item ~= "" then
-        buffer[#buffer + 1] = self.ordereditem(item,num)
-      end
-      if num ~= nil and item ~= "" then
-        num = num + 1
-      end
-    end
-    local contents = util.intersperse(buffer,"\n")
-    if tight and options.tightLists then
-      return {"\\markdownRendererOlBeginTight\n",contents,
-              "\n\\markdownRendererOlEndTight "}
-    else
-      return {"\\markdownRendererOlBegin\n",contents,
-              "\n\\markdownRendererOlEnd "}
-    end
-  end
-  function self.ordereditem(s,num)
-    if num ~= nil then
-      return {"\\markdownRendererOlItemWithNumber{",num,"}",s,
-              "\\markdownRendererOlItemEnd "}
-    else
-      return {"\\markdownRendererOlItem ",s,
-              "\\markdownRendererOlItemEnd "}
-    end
-  end
-  function self.inline_html_comment(contents)
-    if self.flatten_inlines then return contents end
-    return {"\\markdownRendererInlineHtmlComment{",contents,"}"}
-  end
-  function self.inline_html_tag(contents)
-    if self.flatten_inlines then return contents end
-    return {"\\markdownRendererInlineHtmlTag{",self.string(contents),"}"}
-  end
-  function self.block_html_element(s)
-    if not self.is_writing then return "" end
-    local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim")
-    return {"\\markdownRendererInputBlockHtmlElement{",name,"}"}
-  end
-  function self.emphasis(s)
-    if self.flatten_inlines then return s end
-    return {"\\markdownRendererEmphasis{",s,"}"}
-  end
-  function self.tickbox(f)
-    if f == 1.0 then
-      return "☒ "
-    elseif f == 0.0 then
-      return "☐ "
-    else
-      return "⌛ "
-    end
-  end
-  function self.strong(s)
-    if self.flatten_inlines then return s end
-    return {"\\markdownRendererStrongEmphasis{",s,"}"}
-  end
-  function self.blockquote(s)
-    if not self.is_writing then return "" end
-    return {"\\markdownRendererBlockQuoteBegin\n",s,
-      "\\markdownRendererBlockQuoteEnd "}
-  end
-  function self.verbatim(s)
-    if not self.is_writing then return "" end
-    s = s:gsub("\n$", "")
-    local name = util.cache_verbatim(options.cacheDir, s)
-    return {"\\markdownRendererInputVerbatim{",name,"}"}
-  end
-  function self.document(d)
-    local buf = {"\\markdownRendererDocumentBegin\n", d}
-
-    -- pop all attributes
-    table.insert(buf, self.pop_attributes())
-
-    table.insert(buf, "\\markdownRendererDocumentEnd")
-
-    return buf
-  end
-  local seen_identifiers = {}
-  local key_value_regex = "([^= ]+)%s*=%s*(.*)"
-  local function normalize_attributes(attributes, auto_identifiers)
-    -- normalize attributes
-    local normalized_attributes = {}
-    local has_explicit_identifiers = false
-    local key, value
-    for _, attribute in ipairs(attributes or {}) do
-      if attribute:sub(1, 1) == "#" then
-        table.insert(normalized_attributes, attribute)
-        has_explicit_identifiers = true
-        seen_identifiers[attribute:sub(2)] = true
-      elseif attribute:sub(1, 1) == "." then
-        table.insert(normalized_attributes, attribute)
-      else
-        key, value = attribute:match(key_value_regex)
-        if key:lower() == "id" then
-          table.insert(normalized_attributes, "#" .. value)
-        elseif key:lower() == "class" then
-          local classes = {}
-          for class in value:gmatch("%S+") do
-            table.insert(classes, class)
-          end
-          table.sort(classes)
-          for _, class in ipairs(classes) do
-            table.insert(normalized_attributes, "." .. class)
-          end
-        else
-          table.insert(normalized_attributes, attribute)
-        end
-      end
-    end
-
-    -- if no explicit identifiers exist, add auto identifiers
-    if not has_explicit_identifiers and auto_identifiers ~= nil then
-      local seen_auto_identifiers = {}
-      for _, auto_identifier in ipairs(auto_identifiers) do
-        if seen_auto_identifiers[auto_identifier] == nil then
-          seen_auto_identifiers[auto_identifier] = true
-          if seen_identifiers[auto_identifier] == nil then
-            seen_identifiers[auto_identifier] = true
-            table.insert(normalized_attributes,
-                         "#" .. auto_identifier)
-          else
-            local auto_identifier_number = 1
-            while true do
-              local numbered_auto_identifier = auto_identifier .. "-"
-                                             .. auto_identifier_number
-              if seen_identifiers[numbered_auto_identifier] == nil then
-                seen_identifiers[numbered_auto_identifier] = true
-                table.insert(normalized_attributes,
-                             "#" .. numbered_auto_identifier)
-                break
-              end
-              auto_identifier_number = auto_identifier_number + 1
-            end
-          end
-        end
-      end
-    end
-
-    -- sort and deduplicate normalized attributes
-    table.sort(normalized_attributes)
-    local seen_normalized_attributes = {}
-    local deduplicated_normalized_attributes = {}
-    for _, attribute in ipairs(normalized_attributes) do
-      if seen_normalized_attributes[attribute] == nil then
-        seen_normalized_attributes[attribute] = true
-        table.insert(deduplicated_normalized_attributes, attribute)
-      end
-    end
-
-    return deduplicated_normalized_attributes
-  end
-
-  function self.attributes(attributes, should_normalize_attributes)
-    local normalized_attributes
-    if should_normalize_attributes == false then
-      normalized_attributes = attributes
-    else
-      normalized_attributes = normalize_attributes(attributes)
-    end
-
-    local buf = {}
-    local key, value
-    for _, attribute in ipairs(normalized_attributes) do
-      if attribute:sub(1, 1) == "#" then
-        table.insert(buf, {"\\markdownRendererAttributeIdentifier{",
-                           attribute:sub(2), "}"})
-      elseif attribute:sub(1, 1) == "." then
-        table.insert(buf, {"\\markdownRendererAttributeClassName{",
-                           attribute:sub(2), "}"})
-      else
-        key, value = attribute:match(key_value_regex)
-        table.insert(buf, {"\\markdownRendererAttributeKeyValue{",
-                           key, "}{", value, "}"})
-      end
-    end
-
-    return buf
-  end
-  self.active_attributes = {}
-  self.attribute_type_levels = {}
-  setmetatable(self.attribute_type_levels,
-               { __index = function() return 0 end })
-  local function apply_attributes()
-    local buf = {}
-    for i = 1, #self.active_attributes do
-      local start_output = self.active_attributes[i][3]
-      if start_output ~= nil then
-        table.insert(buf, start_output)
-      end
-    end
-    return buf
-  end
-
-  local function tear_down_attributes()
-    local buf = {}
-    for i = #self.active_attributes, 1, -1 do
-      local end_output = self.active_attributes[i][4]
-      if end_output ~= nil then
-        table.insert(buf, end_output)
-      end
-    end
-    return buf
-  end
-  function self.push_attributes(attribute_type, attributes,
-                                start_output, end_output)
-    local attribute_type_level = self.attribute_type_levels[attribute_type]
-    self.attribute_type_levels[attribute_type] = attribute_type_level + 1
-
-    -- index attributes in a hash table for easy lookup
-    attributes = attributes or {}
-    for i = 1, #attributes do
-      attributes[attributes[i]] = true
-    end
-
-    local buf = {}
-    -- handle slicing
-    if attributes["#" .. self.slice_end_identifier] ~= nil and
-       self.slice_end_type == "^" then
-      if self.is_writing then
-        table.insert(buf, self.undosep())
-        table.insert(buf, tear_down_attributes())
-      end
-      self.is_writing = false
-    end
-    if attributes["#" .. self.slice_begin_identifier] ~= nil and
-       self.slice_begin_type == "^" then
-      table.insert(buf, apply_attributes())
-      self.is_writing = true
-    end
-    if self.is_writing and start_output ~= nil then
-      table.insert(buf, start_output)
-    end
-    table.insert(self.active_attributes,
-                 {attribute_type, attributes,
-                  start_output, end_output})
-    return buf
-  end
-
-  function self.pop_attributes(attribute_type)
-    local buf = {}
-    -- pop attributes until we find attributes of correct type
-    -- or until no attributes remain
-    local current_attribute_type = false
-    while current_attribute_type ~= attribute_type and
-          #self.active_attributes > 0 do
-      local attributes, _, end_output
-      current_attribute_type, attributes, _, end_output = table.unpack(
-        self.active_attributes[#self.active_attributes])
-      local attribute_type_level = self.attribute_type_levels[current_attribute_type]
-      self.attribute_type_levels[current_attribute_type] = attribute_type_level - 1
-      if self.is_writing and end_output ~= nil then
-        table.insert(buf, end_output)
-      end
-      table.remove(self.active_attributes, #self.active_attributes)
-      -- handle slicing
-      if attributes["#" .. self.slice_end_identifier] ~= nil
-         and self.slice_end_type == "$" then
-        if self.is_writing then
-          table.insert(buf, self.undosep())
-          table.insert(buf, tear_down_attributes())
-        end
-        self.is_writing = false
-      end
-      if attributes["#" .. self.slice_begin_identifier] ~= nil and
-         self.slice_begin_type == "$" then
-        self.is_writing = true
-        table.insert(buf, apply_attributes())
-      end
-    end
-    return buf
-  end
-  local function create_auto_identifier(s)
-    local buffer = {}
-    local prev_space = false
-    local letter_found = false
-    local normalized_s = s
-    if not options.unicodeNormalization or options.unicodeNormalizationForm ~= "nfc" then
-      normalized_s = uni_algos.normalize.NFC(normalized_s)
-    end
-
-    for _, code in utf8.codes(normalized_s) do
-      local char = utf8.char(code)
-
-      -- Remove everything up to the first letter.
-      if not letter_found then
-        local is_letter = unicode.utf8.match(char, "%a")
-        if is_letter then
-          letter_found = true
-        else
-          goto continue
-        end
-      end
-
-      -- Remove all non-alphanumeric characters, except underscores, hyphens, and periods.
-      if not unicode.utf8.match(char, "[%w_%-%.%s]") then
-        goto continue
-      end
-
-      -- Replace all spaces and newlines with hyphens.
-      if unicode.utf8.match(char, "[%s\n]") then
-        char = "-"
-        if prev_space then
-          goto continue
-        else
-          prev_space = true
-        end
-      else
-        -- Convert all alphabetic characters to lowercase.
-        char = unicode.utf8.lower(char)
-        prev_space = false
-      end
-
-      table.insert(buffer, char)
-
-      ::continue::
-    end
-
-    if prev_space then
-      table.remove(buffer)
-    end
-
-    local identifier = #buffer == 0 and "section" or table.concat(buffer, "")
-    return identifier
-  end
-  local function create_gfm_auto_identifier(s)
-    local buffer = {}
-    local prev_space = false
-    local letter_found = false
-    local normalized_s = s
-    if not options.unicodeNormalization or options.unicodeNormalizationForm ~= "nfc" then
-      normalized_s = uni_algos.normalize.NFC(normalized_s)
-    end
-
-    for _, code in utf8.codes(normalized_s) do
-      local char = utf8.char(code)
-
-      -- Remove everything up to the first non-space.
-      if not letter_found then
-        local is_letter = unicode.utf8.match(char, "%S")
-        if is_letter then
-          letter_found = true
-        else
-          goto continue
-        end
-      end
-
-      -- Remove all non-alphanumeric characters, except underscores and hyphens.
-      if not unicode.utf8.match(char, "[%w_%-%s]") then
-        prev_space = false
-        goto continue
-      end
-
-      -- Replace all spaces and newlines with hyphens.
-      if unicode.utf8.match(char, "[%s\n]") then
-        char = "-"
-        if prev_space then
-          goto continue
-        else
-          prev_space = true
-        end
-      else
-        -- Convert all alphabetic characters to lowercase.
-        char = unicode.utf8.lower(char)
-        prev_space = false
-      end
-
-      table.insert(buffer, char)
-
-      ::continue::
-    end
-
-    if prev_space then
-      table.remove(buffer)
-    end
-
-    local identifier = #buffer == 0 and "section" or table.concat(buffer, "")
-    return identifier
-  end
-  self.secbegin_text = "\\markdownRendererSectionBegin\n"
-  self.secend_text = "\n\\markdownRendererSectionEnd "
-  function self.heading(s, level, attributes)
-    local buf = {}
-    local flat_text, inlines = table.unpack(s)
-
-    -- push empty attributes for implied sections
-    while self.attribute_type_levels["heading"] < level - 1 do
-      table.insert(buf,
-                   self.push_attributes("heading",
-                                        nil,
-                                        self.secbegin_text,
-                                        self.secend_text))
-    end
-
-    -- pop attributes for sections that have ended
-    while self.attribute_type_levels["heading"] >= level do
-      table.insert(buf, self.pop_attributes("heading"))
-    end
-
-    -- construct attributes for the new section
-    local auto_identifiers = {}
-    if self.options.autoIdentifiers then
-      table.insert(auto_identifiers, create_auto_identifier(flat_text))
-    end
-    if self.options.gfmAutoIdentifiers then
-      table.insert(auto_identifiers, create_gfm_auto_identifier(flat_text))
-    end
-    local normalized_attributes = normalize_attributes(attributes, auto_identifiers)
-
-    -- push attributes for the new section
-    local start_output = {}
-    local end_output = {}
-    table.insert(start_output, self.secbegin_text)
-    table.insert(end_output, self.secend_text)
-
-    table.insert(buf, self.push_attributes("heading",
-                                           normalized_attributes,
-                                           start_output,
-                                           end_output))
-    assert(self.attribute_type_levels["heading"] == level)
-
-    -- render the heading and its attributes
-    if self.is_writing and #normalized_attributes > 0 then
-      table.insert(buf, "\\markdownRendererHeaderAttributeContextBegin\n")
-      table.insert(buf, self.attributes(normalized_attributes, false))
-    end
-
-    local cmd
-    level = level + options.shiftHeadings
-    if level <= 1 then
-      cmd = "\\markdownRendererHeadingOne"
-    elseif level == 2 then
-      cmd = "\\markdownRendererHeadingTwo"
-    elseif level == 3 then
-      cmd = "\\markdownRendererHeadingThree"
-    elseif level == 4 then
-      cmd = "\\markdownRendererHeadingFour"
-    elseif level == 5 then
-      cmd = "\\markdownRendererHeadingFive"
-    elseif level >= 6 then
-      cmd = "\\markdownRendererHeadingSix"
-    else
-      cmd = ""
-    end
-    if self.is_writing then
-      table.insert(buf, {cmd, "{", inlines, "}"})
-    end
-
-    if self.is_writing and #normalized_attributes > 0 then
-      table.insert(buf, "\\markdownRendererHeaderAttributeContextEnd{}")
-    end
-
-    return buf
-  end
-  function self.get_state()
-    return {
-      is_writing=self.is_writing,
-      flatten_inlines=self.flatten_inlines,
-      active_attributes={table.unpack(self.active_attributes)},
-    }
-  end
-  function self.set_state(s)
-    local previous_state = self.get_state()
-    for key, value in pairs(s) do
-      self[key] = value
-    end
-    return previous_state
-  end
-  function self.defer_call(f)
-    local previous_state = self.get_state()
-    return function(...)
-      local state = self.set_state(previous_state)
-      local return_value = f(...)
-      self.set_state(state)
-      return return_value
-    end
-  end
-
-  return self
+  table.sort(opt_string)
+  local salt = table.concat(opt_string, ",")
+            .. "," .. metadata.version
+  return salt
 end
-local parsers                  = {}
-parsers.percent                = P("%")
-parsers.at                     = P("@")
-parsers.comma                  = P(",")
-parsers.asterisk               = P("*")
-parsers.dash                   = P("-")
-parsers.plus                   = P("+")
-parsers.underscore             = P("_")
-parsers.period                 = P(".")
-parsers.hash                   = P("#")
-parsers.dollar                 = P("$")
-parsers.ampersand              = P("&")
-parsers.backtick               = P("`")
-parsers.less                   = P("<")
-parsers.more                   = P(">")
-parsers.space                  = P(" ")
-parsers.squote                 = P("'")
-parsers.dquote                 = P('"')
-parsers.lparent                = P("(")
-parsers.rparent                = P(")")
-parsers.lbracket               = P("[")
-parsers.rbracket               = P("]")
-parsers.lbrace                 = P("{")
-parsers.rbrace                 = P("}")
-parsers.circumflex             = P("^")
-parsers.slash                  = P("/")
-parsers.equal                  = P("=")
-parsers.colon                  = P(":")
-parsers.semicolon              = P(";")
-parsers.exclamation            = P("!")
-parsers.pipe                   = P("|")
-parsers.tilde                  = P("~")
-parsers.backslash              = P("\\")
-parsers.tab                    = P("\t")
-parsers.newline                = P("\n")
-
-parsers.digit                  = R("09")
-parsers.hexdigit               = R("09","af","AF")
-parsers.letter                 = R("AZ","az")
-parsers.alphanumeric           = R("AZ","az","09")
-parsers.keyword                = parsers.letter
-                               * (parsers.alphanumeric + parsers.dash)^0
-
-parsers.doubleasterisks        = P("**")
-parsers.doubleunderscores      = P("__")
-parsers.doubletildes           = P("~~")
-parsers.fourspaces             = P("    ")
-
-parsers.any                    = P(1)
-parsers.succeed                = P(true)
-parsers.fail                   = P(false)
-
-parsers.internal_punctuation   = S(":;,.?")
-parsers.ascii_punctuation      = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
-parsers.punctuation            = {}
-(function()
-  local pathname = kpse.lookup("UnicodeData.txt")
-  local file = assert(io.open(pathname, "r"),
-    [[Could not open file "UnicodeData.txt"]])
-  for line in file:lines() do
-    local codepoint, major_category = line:match("^(%x+);[^;]*;(%a)")
-    if major_category == "P" or major_category == "S" then
-      local code = unicode.utf8.char(tonumber(codepoint, 16))
-      if parsers.punctuation[#code] == nil then
-        parsers.punctuation[#code] = parsers.fail
-      end
-      local code_parser = parsers.succeed
-      for i = 1, #code do
-        local byte = code:sub(i, i)
-        local byte_parser = S(byte)
-        code_parser = code_parser
-                    * byte_parser
-      end
-      parsers.punctuation[#code] = parsers.punctuation[#code]
-                                 + code_parser
-    end
-  end
-  assert(file:close())
-end)()
-
-parsers.escapable              = parsers.ascii_punctuation
-parsers.anyescaped             = parsers.backslash / "" * parsers.escapable
-                               + parsers.any
-
-parsers.spacechar              = S("\t ")
-parsers.spacing                = S(" \n\r\t")
-parsers.nonspacechar           = parsers.any - parsers.spacing
-parsers.optionalspace          = parsers.spacechar^0
-
-parsers.normalchar             = parsers.any - (V("SpecialChar")
-                                                + parsers.spacing)
-parsers.eof                    = -parsers.any
-parsers.nonindentspace         = parsers.space^-3 * - parsers.spacechar
-parsers.indent                 = parsers.space^-3 * parsers.tab
-                               + parsers.fourspaces / ""
-parsers.linechar               = P(1 - parsers.newline)
-
-parsers.blankline              = parsers.optionalspace
-                               * parsers.newline / "\n"
-parsers.blanklines             = parsers.blankline^0
-parsers.skipblanklines         = (parsers.optionalspace * parsers.newline)^0
-parsers.indentedline           = parsers.indent    /""
-                               * C(parsers.linechar^1 * parsers.newline^-1)
-parsers.optionallyindentedline = parsers.indent^-1 /""
-                               * C(parsers.linechar^1 * parsers.newline^-1)
-parsers.sp                     = parsers.spacing^0
-parsers.spnl                   = parsers.optionalspace
-                               * (parsers.newline * parsers.optionalspace)^-1
-parsers.line                   = parsers.linechar^0 * parsers.newline
-parsers.nonemptyline           = parsers.line - parsers.blankline
-
-parsers.leader      = parsers.space^-3
-
-local function has_trail(indent_table)
-  return indent_table ~= nil and
-    indent_table.trail ~= nil and
-    next(indent_table.trail) ~= nil
+local function warn(s)
+  io.stderr:write("Warning: " .. s .. "\n")
 end
 
-local function has_indents(indent_table)
-  return indent_table ~= nil and
-    indent_table.indents ~= nil and
-    next(indent_table.indents) ~= nil
-end
-
-local function add_trail(indent_table, trail_info)
-  indent_table.trail = trail_info
-  return indent_table
-end
-
-local function remove_trail(indent_table)
-  indent_table.trail = nil
-  return indent_table
-end
-
-local function update_indent_table(indent_table, new_indent, add)
-  indent_table = remove_trail(indent_table)
-
-  if not has_indents(indent_table) then
-    indent_table.indents = {}
-  end
-
-  if add then
-    indent_table.indents[#indent_table.indents + 1] = new_indent
-  else
-    if indent_table.indents[#indent_table.indents].name == new_indent.name then
-      indent_table.indents[#indent_table.indents] = nil
-    end
-  end
-
-  return indent_table
-end
-
-local function remove_indent(name)
-  local function remove_indent_level(s, i, indent_table) -- luacheck: ignore s i
-    indent_table = update_indent_table(indent_table, {name=name}, false)
-    return true, indent_table
-  end
-
-  return Cg(Cmt(Cb("indent_info"), remove_indent_level), "indent_info")
-end
-
-local function process_starter_spacing(indent, spacing, minimum, left_strip_length)
-  left_strip_length = left_strip_length or 0
-
-  local count = 0
-  local tab_value = 4 - (indent) % 4
-
-  local code_started, minimum_found = false, false
-  local code_start, minimum_remainder = "", ""
-
-  local left_total_stripped = 0
-  local full_remainder = ""
-
-  if spacing ~= nil then
-    for i = 1, #spacing do
-      local character = spacing:sub(i, i)
-
-      if character == "\t" then
-        count = count + tab_value
-        tab_value = 4
-      elseif character == " " then
-        count = count + 1
-        tab_value = 4 - (1 - tab_value) % 4
-      end
-
-      if (left_strip_length ~= 0) then
-        local possible_to_strip = math.min(count, left_strip_length)
-        count = count - possible_to_strip
-        left_strip_length = left_strip_length - possible_to_strip
-        left_total_stripped = left_total_stripped + possible_to_strip
-      else
-        full_remainder =  full_remainder .. character
-      end
-
-      if (minimum_found) then
-        minimum_remainder = minimum_remainder .. character
-      elseif (count >= minimum) then
-        minimum_found = true
-        minimum_remainder = minimum_remainder .. string.rep(" ", count - minimum)
-      end
-
-      if (code_started) then
-        code_start = code_start .. character
-      elseif (count >= minimum + 4) then
-        code_started = true
-        code_start = code_start .. string.rep(" ", count - (minimum + 4))
-      end
-    end
-  end
-
-  local remainder
-  if (code_started) then
-    remainder = code_start
-  else
-    remainder = string.rep(" ", count - minimum)
-  end
-
-  local is_minimum = count >= minimum
-  return {
-    is_code = code_started,
-    remainder = remainder,
-    left_total_stripped = left_total_stripped,
-    is_minimum = is_minimum,
-    minimum_remainder = minimum_remainder,
-    total_length = count,
-    full_remainder = full_remainder
-  }
-end
-
-local function count_indent_tab_level(indent_table)
-  local count = 0
-  if not has_indents(indent_table) then
-    return count
-  end
-
-  for i=1, #indent_table.indents do
-    count = count + indent_table.indents[i].length
-  end
-  return count
-end
-
-local function total_delimiter_length(delimiter)
-  local count = 0
-  if type(delimiter) == "string" then return #delimiter end
-  for _, value in pairs(delimiter) do
-    count = count + total_delimiter_length(value)
-  end
-  return count
-end
-
-local function process_starter_indent(_, _, indent_table, starter, is_blank, indent_type, breakable)
-  local last_trail = starter[1]
-  local delimiter = starter[2]
-  local raw_new_trail = starter[3]
-
-  if indent_type == "bq" and not breakable then
-    indent_table.ignore_blockquote_blank = true
-  end
-
-  if has_trail(indent_table) then
-    local trail = indent_table.trail
-    if trail.is_code then
-      return false
-    end
-    last_trail = trail.remainder
-  else
-    local sp = process_starter_spacing(0, last_trail, 0, 0)
-
-    if sp.is_code then
-      return false
-    end
-    last_trail = sp.remainder
-  end
-
-  local preceding_indentation = count_indent_tab_level(indent_table) % 4
-  local last_trail_length = #last_trail
-  local delimiter_length = total_delimiter_length(delimiter)
-
-  local total_indent_level = preceding_indentation + last_trail_length + delimiter_length
-
-  local sp = {}
-  if not is_blank then
-    sp = process_starter_spacing(total_indent_level, raw_new_trail, 0, 1)
-  end
-
-  local del_trail_length = sp.left_total_stripped
-  if is_blank then
-    del_trail_length = 1
-  elseif not sp.is_code then
-    del_trail_length = del_trail_length + #sp.remainder
-  end
-
-  local indent_length = last_trail_length + delimiter_length + del_trail_length
-  local new_indent_info = {name=indent_type, length=indent_length}
-
-  indent_table = update_indent_table(indent_table, new_indent_info, true)
-  indent_table = add_trail(indent_table, {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length,
-                                          full_remainder=sp.full_remainder})
-
-  return true, indent_table
-end
-
-local function decode_pattern(name)
-  local delimeter = parsers.succeed
-  if name == "bq" then
-    delimeter = parsers.more
-  end
-
-  return C(parsers.optionalspace) * C(delimeter) * C(parsers.optionalspace) * Cp()
-end
-
-local function left_blank_starter(indent_table)
-  local blank_starter_index
-
-  if not has_indents(indent_table) then
-    return
-  end
-
-  for i = #indent_table.indents,1,-1 do
-    local value = indent_table.indents[i]
-    if value.name == "li" then
-      blank_starter_index = i
-    else
-      break
-    end
-  end
-
-  return blank_starter_index
-end
-
-local function traverse_indent(s, i, indent_table, is_optional, is_blank, current_line_indents)
-  local new_index = i
-
-  local preceding_indentation = 0
-  local current_trail = {}
-
-  local blank_starter = left_blank_starter(indent_table)
-
-  if current_line_indents == nil then
-    current_line_indents = {}
-  end
-
-  for index = 1,#indent_table.indents do
-    local value = indent_table.indents[index]
-    local pattern = decode_pattern(value.name)
-
-    -- match decoded pattern
-    local new_indent_info = lpeg.match(Ct(pattern), s, new_index)
-    if new_indent_info == nil then
-      local blankline_end = lpeg.match(Ct(parsers.blankline * Cg(Cp(), "pos")), s, new_index)
-      if is_optional or not indent_table.ignore_blockquote_blank or not blankline_end then
-        return is_optional, new_index, current_trail, current_line_indents
-      end
-
-      return traverse_indent(s, tonumber(blankline_end.pos), indent_table, is_optional, is_blank, current_line_indents)
-    end
-
-    local raw_last_trail = new_indent_info[1]
-    local delimiter = new_indent_info[2]
-    local raw_new_trail = new_indent_info[3]
-    local next_index = new_indent_info[4]
-
-    local space_only = delimiter == ""
-
-    -- check previous trail
-    if not space_only and next(current_trail) == nil then
-      local sp = process_starter_spacing(0, raw_last_trail, 0, 0)
-      current_trail = {is_code=sp.is_code, remainder=sp.remainder, total_length=sp.total_length,
-                       full_remainder=sp.full_remainder}
-    end
-
-    if next(current_trail) ~= nil then
-      if not space_only and current_trail.is_code then
-        return is_optional, new_index, current_trail, current_line_indents
-      end
-      if current_trail.internal_remainder ~= nil then
-        raw_last_trail = current_trail.internal_remainder
-      end
-    end
-
-    local raw_last_trail_length = 0
-    local delimiter_length = 0
-
-    if not space_only then
-      delimiter_length = #delimiter
-      raw_last_trail_length = #raw_last_trail
-    end
-
-    local total_indent_level = preceding_indentation + raw_last_trail_length + delimiter_length
-
-    local spacing_to_process
-    local minimum = 0
-    local left_strip_length = 0
-
-    if not space_only then
-      spacing_to_process = raw_new_trail
-      left_strip_length = 1
-    else
-      spacing_to_process = raw_last_trail
-      minimum = value.length
-    end
-
-    local sp = process_starter_spacing(total_indent_level, spacing_to_process, minimum, left_strip_length)
-
-    if space_only and not sp.is_minimum then
-      return is_optional or (is_blank and blank_starter <= index), new_index, current_trail, current_line_indents
-    end
-
-    local indent_length = raw_last_trail_length + delimiter_length + sp.left_total_stripped
-
-    -- update info for the next pattern
-    if not space_only then
-      preceding_indentation = preceding_indentation + indent_length
-    else
-      preceding_indentation = preceding_indentation + value.length
-    end
-
-    current_trail = {is_code=sp.is_code, remainder=sp.remainder, internal_remainder=sp.minimum_remainder,
-                     total_length=sp.total_length, full_remainder=sp.full_remainder}
-
-    current_line_indents[#current_line_indents + 1] = new_indent_info
-    new_index = next_index
-  end
-
-  return true, new_index, current_trail, current_line_indents
-end
-
-local function check_trail(expect_code, is_code)
-  return (expect_code and is_code) or (not expect_code and not is_code)
-end
-
-local function check_trail_joined(s, i, indent_table, spacing, expect_code, omit_remainder) -- luacheck: ignore s i
-  local is_code
-  local remainder
-
-  if has_trail(indent_table) then
-    local trail = indent_table.trail
-    is_code = trail.is_code
-    if is_code then
-      remainder = trail.remainder
-    else
-      remainder = trail.full_remainder
-    end
-  else
-    local sp = process_starter_spacing(0, spacing, 0, 0)
-    is_code = sp.is_code
-    if is_code then
-      remainder = sp.remainder
-    else
-      remainder = sp.full_remainder
-    end
-  end
-
-  local result = check_trail(expect_code, is_code)
-  if omit_remainder then
-    return result
-  end
-  return result, remainder
-end
-
-local function check_trail_length(s, i, indent_table, spacing, min, max) -- luacheck: ignore s i
-  local trail
-
-  if has_trail(indent_table) then
-    trail = indent_table.trail
-  else
-    trail = process_starter_spacing(0, spacing, 0, 0)
-  end
-
-  local total_length = trail.total_length
-  if total_length == nil then
-    return false
-  end
-
-  return min <= total_length and total_length <= max
-end
-
-local function check_continuation_indentation(s, i, indent_table, is_optional, is_blank)
-  if not has_indents(indent_table) then
-    return true
-  end
-
-  local passes, new_index, current_trail, current_line_indents =
-    traverse_indent(s, i, indent_table, is_optional, is_blank)
-
-  if passes then
-    indent_table.current_line_indents = current_line_indents
-    indent_table = add_trail(indent_table, current_trail)
-    return new_index, indent_table
-  end
-  return false
-end
-
-local function get_last_indent_name(indent_table)
-  if has_indents(indent_table) then
-    return indent_table.indents[#indent_table.indents].name
-  end
-end
-
-local function remove_remainder_if_blank(indent_table, remainder)
-  if get_last_indent_name(indent_table) == "li" then
-    return ""
-  end
-  return remainder
-end
-
-local function check_trail_type(s, i, trail, spacing, trail_type) -- luacheck: ignore s i
-  if trail == nil then
-    trail = process_starter_spacing(0, spacing, 0, 0)
-  end
-
-  if trail_type == "non-code" then
-    return check_trail(false, trail.is_code)
-  end
-  if trail_type == "code" then
-    return check_trail(true, trail.is_code)
-  end
-  if trail_type == "full-code" then
-    if (trail.is_code) then
-      return i, trail.remainder
-    end
-    return i, ""
-  end
-  if trail_type == "full-any" then
-    return i, trail.internal_remainder
-  end
-end
-
-local function trail_freezing(s, i, indent_table, is_freezing) -- luacheck: ignore s i
-  if is_freezing then
-    if indent_table.is_trail_frozen then
-      indent_table.trail = indent_table.frozen_trail
-    else
-      indent_table.frozen_trail = indent_table.trail
-      indent_table.is_trail_frozen = true
-    end
-  else
-    indent_table.frozen_trail = nil
-    indent_table.is_trail_frozen = false
-  end
-  return true, indent_table
-end
-
-local function check_continuation_indentation_and_trail(s, i, indent_table, is_optional, is_blank, trail_type,
-                                                        reset_rem, omit_remainder)
-  if not has_indents(indent_table) then
-    local spacing, new_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i)
-    local result, remainder = check_trail_type(s, i, indent_table.trail, spacing, trail_type)
-    if remainder == nil then
-      if result then
-        return new_index
-      end
-      return false
-    end
-    if result then
-      return new_index, remainder
-    end
-    return false
-  end
-
-  local passes, new_index, current_trail = traverse_indent(s, i, indent_table, is_optional, is_blank)
-
-  if passes then
-    local spacing
-    if current_trail == nil then
-      local newer_spacing, newer_index = lpeg.match(C(parsers.spacechar^0) * Cp(), s, i)
-      current_trail = process_starter_spacing(0, newer_spacing, 0, 0)
-      new_index = newer_index
-      spacing = newer_spacing
-    else
-      spacing = current_trail.remainder
-    end
-    local result, remainder = check_trail_type(s, new_index, current_trail, spacing, trail_type)
-    if remainder == nil or omit_remainder then
-      if result then
-        return new_index
-      end
-      return false
-    end
-
-    if is_blank and reset_rem then
-      remainder = remove_remainder_if_blank(indent_table, remainder)
-    end
-    if result then
-      return new_index, remainder
-    end
-    return false
-  end
-  return false
-end
-
-parsers.check_trail = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(false), check_trail_joined)
-
-parsers.check_trail_no_rem = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(false) * Cc(true), check_trail_joined)
-
-parsers.check_code_trail  = Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(true), check_trail_joined)
-
-parsers.check_trail_length_range  = function(min, max)
-  return Cmt(Cb("indent_info") * C(parsers.spacechar^0) * Cc(min) * Cc(max), check_trail_length)
-end
-
-parsers.check_trail_length = function(n)
-  return parsers.check_trail_length_range(n, n)
-end
-
-parsers.freeze_trail = Cg(Cmt(Cb("indent_info") * Cc(true), trail_freezing), "indent_info")
-
-parsers.unfreeze_trail = Cg(Cmt(Cb("indent_info") * Cc(false), trail_freezing), "indent_info")
-
-parsers.check_minimal_indent = Cmt(Cb("indent_info") * Cc(false), check_continuation_indentation)
-
-parsers.check_optional_indent = Cmt(Cb("indent_info") * Cc(true), check_continuation_indentation)
-
-parsers.check_minimal_blank_indent = Cmt(Cb("indent_info") * Cc(false) * Cc(true), check_continuation_indentation)
-
-
-parsers.check_minimal_indent_and_trail = Cmt( Cb("indent_info")
-                                            * Cc(false) * Cc(false) * Cc("non-code") * Cc(true),
-                                            check_continuation_indentation_and_trail)
-
-parsers.check_minimal_indent_and_code_trail = Cmt( Cb("indent_info")
-                                                 * Cc(false) * Cc(false) * Cc("code") * Cc(false),
-                                                 check_continuation_indentation_and_trail)
-
-parsers.check_minimal_blank_indent_and_full_code_trail = Cmt( Cb("indent_info")
-                                                            * Cc(false) * Cc(true) * Cc("full-code") * Cc(true),
-                                                            check_continuation_indentation_and_trail)
-
-parsers.check_minimal_indent_and_any_trail = Cmt( Cb("indent_info")
-                                                * Cc(false) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false),
-                                                check_continuation_indentation_and_trail)
-
-parsers.check_minimal_blank_indent_and_any_trail = Cmt( Cb("indent_info")
-                                                      * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false),
-                                                       check_continuation_indentation_and_trail)
-
-parsers.check_minimal_blank_indent_and_any_trail_no_rem = Cmt( Cb("indent_info")
-                                                        * Cc(false) * Cc(true) * Cc("full-any") * Cc(true) * Cc(true),
-                                                        check_continuation_indentation_and_trail)
-
-parsers.check_optional_indent_and_any_trail = Cmt( Cb("indent_info")
-                                                * Cc(true) * Cc(false) * Cc("full-any") * Cc(true) * Cc(false),
-                                                check_continuation_indentation_and_trail)
-
-parsers.check_optional_blank_indent_and_any_trail = Cmt( Cb("indent_info")
-                                                      * Cc(true) * Cc(true) * Cc("full-any") * Cc(true) * Cc(false),
-                                                       check_continuation_indentation_and_trail)
-
-
-parsers.spnlc_noexc = parsers.optionalspace
-                    * (parsers.newline * parsers.check_minimal_indent_and_any_trail)^-1
-
-parsers.spnlc = parsers.optionalspace
-              * (V("EndlineNoSub"))^-1
-
-parsers.spnlc_sep  = parsers.optionalspace * V("EndlineNoSub")
-                   + parsers.spacechar^1
-
-parsers.only_blank = parsers.spacechar^0 * (parsers.newline + parsers.eof)
-
-parsers.commented_line_letter  = parsers.linechar
-                               + parsers.newline
-                               - parsers.backslash
-                               - parsers.percent
-parsers.commented_line         = Cg(Cc(""), "backslashes")
-                               * ((#(parsers.commented_line_letter
-                                    - parsers.newline)
-                                  * Cb("backslashes")
-                                  * Cs(parsers.commented_line_letter
-                                    - parsers.newline)^1  -- initial
-                                  * Cg(Cc(""), "backslashes"))
-                                 + #(parsers.backslash * parsers.backslash)
-                                 * Cg((parsers.backslash  -- even backslash
-                                      * parsers.backslash)^1, "backslashes")
-                                 + (parsers.backslash
-                                   * (#parsers.percent
-                                     * Cb("backslashes")
-                                     / function(backslashes)
-                                       return string.rep("\\", #backslashes / 2)
-                                     end
-                                     * C(parsers.percent)
-                                     + #parsers.commented_line_letter
-                                     * Cb("backslashes")
-                                     * Cc("\\")
-                                     * C(parsers.commented_line_letter))
-                                   * Cg(Cc(""), "backslashes")))^0
-                               * (#parsers.percent
-                                 * Cb("backslashes")
-                                 / function(backslashes)
-                                   return string.rep("\\", #backslashes / 2)
-                                 end
-                                 * ((parsers.percent  -- comment
-                                    * parsers.line
-                                    * #parsers.blankline) -- blank line
-                                   / "\n"
-                                   + parsers.percent  -- comment
-                                   * parsers.line
-                                   * parsers.optionalspace)  -- leading tabs and spaces
-                                 + #(parsers.newline)
-                                 * Cb("backslashes")
-                                 * C(parsers.newline))
-
-parsers.chunk                  = parsers.line * (parsers.optionallyindentedline
-                                                - parsers.blankline)^0
-
-parsers.attribute_key_char     = parsers.alphanumeric + S("-_:.")
-parsers.attribute_raw_char     = parsers.alphanumeric + S("-_")
-parsers.attribute_key          = (parsers.attribute_key_char
-                                 - parsers.dash - parsers.digit)
-                               * parsers.attribute_key_char^0
-parsers.attribute_value        = ( (parsers.dquote / "")
-                                 * (parsers.anyescaped - parsers.dquote)^0
-                                 * (parsers.dquote / ""))
-                               + ( (parsers.squote / "")
-                                 * (parsers.anyescaped - parsers.squote)^0
-                                 * (parsers.squote / ""))
-                               + ( parsers.anyescaped - parsers.dquote - parsers.rbrace
-                                 - parsers.space)^0
-parsers.attribute_identifier   = parsers.attribute_key_char^1
-parsers.attribute_classname    = parsers.letter
-                               * parsers.attribute_key_char^0
-parsers.attribute_raw          = parsers.attribute_raw_char^1
-
-parsers.attribute = (parsers.dash * Cc(".unnumbered"))
-                  + C( parsers.hash
-                     * parsers.attribute_identifier)
-                  + C( parsers.period
-                     * parsers.attribute_classname)
-                  + Cs( parsers.attribute_key
-                      * parsers.optionalspace * parsers.equal * parsers.optionalspace
-                      * parsers.attribute_value)
-parsers.attributes = parsers.lbrace
-                   * parsers.optionalspace
-                   * parsers.attribute
-                   * (parsers.spacechar^1
-                     * parsers.attribute)^0
-                   * parsers.optionalspace
-                   * parsers.rbrace
-
-parsers.raw_attribute = parsers.lbrace
-                      * parsers.optionalspace
-                      * parsers.equal
-                      * C(parsers.attribute_raw)
-                      * parsers.optionalspace
-                      * parsers.rbrace
-
--- block followed by 0 or more optionally
--- indented blocks with first line indented.
-parsers.indented_blocks = function(bl)
-  return Cs( bl
-         * (parsers.blankline^1 * parsers.indent * -parsers.blankline * bl)^0
-         * (parsers.blankline^1 + parsers.eof) )
-end
-local function repeat_between(pattern, min, max)
-  return -pattern^(max + 1) * pattern^min
-end
-
-parsers.hexentity = parsers.ampersand * parsers.hash * C(S("Xx"))
-                  * C(repeat_between(parsers.hexdigit, 1, 6)) * parsers.semicolon
-parsers.decentity = parsers.ampersand * parsers.hash
-                  * C(repeat_between(parsers.digit, 1, 7)) * parsers.semicolon
-parsers.tagentity = parsers.ampersand * C(parsers.alphanumeric^1)
-                  * parsers.semicolon
-
-parsers.html_entities = parsers.hexentity / entities.hex_entity_with_x_char
-                      + parsers.decentity / entities.dec_entity
-                      + parsers.tagentity / entities.char_entity
-parsers.bullet = function(bullet_char, interrupting)
-  local allowed_end
-  if interrupting then
-    allowed_end = C(parsers.spacechar^1) * #parsers.linechar
-  else
-    allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
-  end
-  return parsers.check_trail
-        * Ct(C(bullet_char) * Cc(""))
-        * allowed_end
-end
-
-local function tickbox(interior)
-  return parsers.optionalspace * parsers.lbracket
-       * interior * parsers.rbracket * parsers.spacechar^1
-end
-
-parsers.ticked_box = tickbox(S("xX")) * Cc(1.0)
-parsers.halfticked_box = tickbox(S("./")) * Cc(0.5)
-parsers.unticked_box = tickbox(parsers.spacechar^1) * Cc(0.0)
-
-parsers.openticks   = Cg(parsers.backtick^1, "ticks")
-
-local function captures_equal_length(_,i,a,b)
-  return #a == #b and i
-end
-
-parsers.closeticks  = Cmt(C(parsers.backtick^1)
-                          * Cb("ticks"), captures_equal_length)
-
-parsers.intickschar = (parsers.any - S("\n\r`"))
-                    + V("NoSoftLineBreakEndline")
-                    + (parsers.backtick^1 - parsers.closeticks)
-
-local function process_inticks(s)
-  s = s:gsub("\n", " ")
-  s = s:gsub("^ (.*) $", "%1")
-  return s
-end
-
-parsers.inticks = parsers.openticks
-                * C(parsers.space^0)
-                * parsers.closeticks
-                + parsers.openticks
-                * Cs(Cs(parsers.intickschar^0) / process_inticks)
-                * parsers.closeticks
-
--- case-insensitive match (we assume s is lowercase). must be single byte encoding
-parsers.keyword_exact = function(s)
-  local parser = P(0)
-  for i=1,#s do
-    local c = s:sub(i,i)
-    local m = c .. upper(c)
-    parser = parser * S(m)
-  end
-  return parser
-end
-
-parsers.special_block_keyword =
-    parsers.keyword_exact("pre") +
-    parsers.keyword_exact("script") +
-    parsers.keyword_exact("style") +
-    parsers.keyword_exact("textarea")
-
-parsers.block_keyword =
-    parsers.keyword_exact("address") +
-    parsers.keyword_exact("article") +
-    parsers.keyword_exact("aside") +
-    parsers.keyword_exact("base") +
-    parsers.keyword_exact("basefont") +
-    parsers.keyword_exact("blockquote") +
-    parsers.keyword_exact("body") +
-    parsers.keyword_exact("caption") +
-    parsers.keyword_exact("center") +
-    parsers.keyword_exact("col") +
-    parsers.keyword_exact("colgroup") +
-    parsers.keyword_exact("dd") +
-    parsers.keyword_exact("details") +
-    parsers.keyword_exact("dialog") +
-    parsers.keyword_exact("dir") +
-    parsers.keyword_exact("div") +
-    parsers.keyword_exact("dl") +
-    parsers.keyword_exact("dt") +
-    parsers.keyword_exact("fieldset") +
-    parsers.keyword_exact("figcaption") +
-    parsers.keyword_exact("figure") +
-    parsers.keyword_exact("footer") +
-    parsers.keyword_exact("form") +
-    parsers.keyword_exact("frame") +
-    parsers.keyword_exact("frameset") +
-    parsers.keyword_exact("h1") +
-    parsers.keyword_exact("h2") +
-    parsers.keyword_exact("h3") +
-    parsers.keyword_exact("h4") +
-    parsers.keyword_exact("h5") +
-    parsers.keyword_exact("h6") +
-    parsers.keyword_exact("head") +
-    parsers.keyword_exact("header") +
-    parsers.keyword_exact("hr") +
-    parsers.keyword_exact("html") +
-    parsers.keyword_exact("iframe") +
-    parsers.keyword_exact("legend") +
-    parsers.keyword_exact("li") +
-    parsers.keyword_exact("link") +
-    parsers.keyword_exact("main") +
-    parsers.keyword_exact("menu") +
-    parsers.keyword_exact("menuitem") +
-    parsers.keyword_exact("nav") +
-    parsers.keyword_exact("noframes") +
-    parsers.keyword_exact("ol") +
-    parsers.keyword_exact("optgroup") +
-    parsers.keyword_exact("option") +
-    parsers.keyword_exact("p") +
-    parsers.keyword_exact("param") +
-    parsers.keyword_exact("section") +
-    parsers.keyword_exact("source") +
-    parsers.keyword_exact("summary") +
-    parsers.keyword_exact("table") +
-    parsers.keyword_exact("tbody") +
-    parsers.keyword_exact("td") +
-    parsers.keyword_exact("tfoot") +
-    parsers.keyword_exact("th") +
-    parsers.keyword_exact("thead") +
-    parsers.keyword_exact("title") +
-    parsers.keyword_exact("tr") +
-    parsers.keyword_exact("track") +
-    parsers.keyword_exact("ul")
-
--- end conditions
-parsers.html_blankline_end_condition  = parsers.linechar^0
-                                      * ( parsers.newline
-                                        * (parsers.check_minimal_blank_indent_and_any_trail
-                                          * #parsers.blankline
-                                          + parsers.check_minimal_indent_and_any_trail)
-                                        * parsers.linechar^1)^0
-                                      * (parsers.newline^-1 / "")
-
-local function remove_trailing_blank_lines(s)
-  return s:gsub("[\n\r]+%s*$", "")
-end
-
-parsers.html_until_end = function(end_marker)
-  return Cs(Cs((parsers.newline
-          * (parsers.check_minimal_blank_indent_and_any_trail
-            * #parsers.blankline
-            + parsers.check_minimal_indent_and_any_trail)
-          + parsers.linechar - end_marker)^0
-          * parsers.linechar^0 * parsers.newline^-1)
-         / remove_trailing_blank_lines)
-end
-
--- attributes
-parsers.html_attribute_spacing  = parsers.optionalspace
-                                * V("NoSoftLineBreakEndline")
-                                * parsers.optionalspace
-                                + parsers.spacechar^1
-
-parsers.html_attribute_name = (parsers.letter + parsers.colon + parsers.underscore)
-                            * (parsers.alphanumeric + parsers.colon + parsers.underscore
-                            + parsers.period + parsers.dash)^0
-
-parsers.html_attribute_value  = parsers.squote
-                              * (parsers.linechar - parsers.squote)^0
-                              * parsers.squote
-                              + parsers.dquote
-                              * (parsers.linechar - parsers.dquote)^0
-                              * parsers.dquote
-                              + ( parsers.any - parsers.spacechar - parsers.newline
-                                - parsers.dquote - parsers.squote - parsers.backtick
-                                - parsers.equal - parsers.less - parsers.more)^1
-
-parsers.html_inline_attribute_value = parsers.squote
-                                    * (V("NoSoftLineBreakEndline")
-                                      + parsers.any
-                                      - parsers.blankline^2
-                                      - parsers.squote)^0
-                                    * parsers.squote
-                                    + parsers.dquote
-                                    * (V("NoSoftLineBreakEndline")
-                                      + parsers.any
-                                      - parsers.blankline^2
-                                      - parsers.dquote)^0
-                                    * parsers.dquote
-                                    + (parsers.any - parsers.spacechar - parsers.newline
-                                      - parsers.dquote - parsers.squote - parsers.backtick
-                                      - parsers.equal - parsers.less - parsers.more)^1
-
-parsers.html_attribute_value_specification  = parsers.optionalspace
-                                            * parsers.equal
-                                            * parsers.optionalspace
-                                            * parsers.html_attribute_value
-
-parsers.html_spnl = parsers.optionalspace
-                  * (V("NoSoftLineBreakEndline") * parsers.optionalspace)^-1
-
-parsers.html_inline_attribute_value_specification = parsers.html_spnl
-                                                  * parsers.equal
-                                                  * parsers.html_spnl
-                                                  * parsers.html_inline_attribute_value
-
-parsers.html_attribute  = parsers.html_attribute_spacing
-                        * parsers.html_attribute_name
-                        * parsers.html_inline_attribute_value_specification^-1
-
-parsers.html_non_newline_attribute  = parsers.spacechar^1
-                                    * parsers.html_attribute_name
-                                    * parsers.html_attribute_value_specification^-1
-
-parsers.nested_breaking_blank = parsers.newline
-                              * parsers.check_minimal_blank_indent
-                              * parsers.blankline
-
-parsers.html_comment_start = P("<!--")
-
-parsers.html_comment_end = P("-->")
-
-parsers.html_comment = Cs( parsers.html_comment_start
-                         * parsers.html_until_end(parsers.html_comment_end))
-
-parsers.html_inline_comment = (parsers.html_comment_start / "")
-                            * -P(">") * -P("->")
-                            * Cs((V("NoSoftLineBreakEndline") + parsers.any
-                                - parsers.nested_breaking_blank - parsers.html_comment_end)^0)
-                            * (parsers.html_comment_end / "")
-
-parsers.html_cdatasection_start = P("<![CDATA[")
-
-parsers.html_cdatasection_end = P("]]>")
-
-parsers.html_cdatasection = Cs( parsers.html_cdatasection_start
-                              * parsers.html_until_end(parsers.html_cdatasection_end))
-
-parsers.html_inline_cdatasection  = parsers.html_cdatasection_start
-                                  * Cs(V("NoSoftLineBreakEndline") + parsers.any
-                                      - parsers.nested_breaking_blank - parsers.html_cdatasection_end)^0
-                                  * parsers.html_cdatasection_end
-
-parsers.html_declaration_start = P("<!") * parsers.letter
-
-parsers.html_declaration_end = P(">")
-
-parsers.html_declaration  = Cs( parsers.html_declaration_start
-                              * parsers.html_until_end(parsers.html_declaration_end))
-
-parsers.html_inline_declaration = parsers.html_declaration_start
-                                * Cs(V("NoSoftLineBreakEndline") + parsers.any
-                                    - parsers.nested_breaking_blank - parsers.html_declaration_end)^0
-                                * parsers.html_declaration_end
-
-parsers.html_instruction_start = P("<?")
-
-parsers.html_instruction_end = P("?>")
-
-parsers.html_instruction  = Cs( parsers.html_instruction_start
-                              * parsers.html_until_end(parsers.html_instruction_end))
-
-parsers.html_inline_instruction = parsers.html_instruction_start
-                                * Cs(V("NoSoftLineBreakEndline") + parsers.any
-                                    - parsers.nested_breaking_blank - parsers.html_instruction_end)^0
-                                * parsers.html_instruction_end
-
-parsers.html_blankline  = parsers.newline
-                        * parsers.optionalspace
-                        * parsers.newline
-
-parsers.html_tag_start = parsers.less
-
-parsers.html_tag_closing_start  = parsers.less
-                                * parsers.slash
-
-parsers.html_tag_end  = parsers.html_spnl
-                      * parsers.more
-
-parsers.html_empty_tag_end  = parsers.html_spnl
-                            * parsers.slash
-                            * parsers.more
-
--- opening tags
-parsers.html_any_open_inline_tag  = parsers.html_tag_start
-                                  * parsers.keyword
-                                  * parsers.html_attribute^0
-                                  * parsers.html_tag_end
-
-parsers.html_any_open_tag = parsers.html_tag_start
-                          * parsers.keyword
-                          * parsers.html_non_newline_attribute^0
-                          * parsers.html_tag_end
-
-parsers.html_open_tag = parsers.html_tag_start
-                      * parsers.block_keyword
-                      * parsers.html_attribute^0
-                      * parsers.html_tag_end
-
-parsers.html_open_special_tag = parsers.html_tag_start
-                              * parsers.special_block_keyword
-                              * parsers.html_attribute^0
-                              * parsers.html_tag_end
-
--- incomplete tags
-parsers.incomplete_tag_following  = parsers.spacechar
-                                  + parsers.more
-                                  + parsers.slash * parsers.more
-                                  + #(parsers.newline + parsers.eof)
-
-parsers.incomplete_special_tag_following  = parsers.spacechar
-                                          + parsers.more
-                                          + #(parsers.newline + parsers.eof)
-
-parsers.html_incomplete_open_tag  = parsers.html_tag_start
-                                  * parsers.block_keyword
-                                  * parsers.incomplete_tag_following
-
-parsers.html_incomplete_open_special_tag  = parsers.html_tag_start
-                                          * parsers.special_block_keyword
-                                          * parsers.incomplete_special_tag_following
-
-parsers.html_incomplete_close_tag = parsers.html_tag_closing_start
-                                  * parsers.block_keyword
-                                  * parsers.incomplete_tag_following
-
-parsers.html_incomplete_close_special_tag = parsers.html_tag_closing_start
-                                          * parsers.special_block_keyword
-                                          * parsers.incomplete_tag_following
-
--- closing tags
-parsers.html_close_tag  = parsers.html_tag_closing_start
-                        * parsers.block_keyword
-                        * parsers.html_tag_end
-
-parsers.html_any_close_tag  = parsers.html_tag_closing_start
-                            * parsers.keyword
-                            * parsers.html_tag_end
-
-parsers.html_close_special_tag = parsers.html_tag_closing_start
-                               * parsers.special_block_keyword
-                               * parsers.html_tag_end
-
--- empty tags
-parsers.html_any_empty_inline_tag = parsers.html_tag_start
-                                  * parsers.keyword
-                                  * parsers.html_attribute^0
-                                  * parsers.html_empty_tag_end
-
-parsers.html_any_empty_tag  = parsers.html_tag_start
-                            * parsers.keyword
-                            * parsers.html_non_newline_attribute^0
-                            * parsers.optionalspace
-                            * parsers.slash
-                            * parsers.more
-
-parsers.html_empty_tag  = parsers.html_tag_start
-                        * parsers.block_keyword
-                        * parsers.html_attribute^0
-                        * parsers.html_empty_tag_end
-
-parsers.html_empty_special_tag  = parsers.html_tag_start
-                                * parsers.special_block_keyword
-                                * parsers.html_attribute^0
-                                * parsers.html_empty_tag_end
-
-parsers.html_incomplete_blocks  = parsers.html_incomplete_open_tag
-                                + parsers.html_incomplete_open_special_tag
-                                + parsers.html_incomplete_close_tag
-
--- parse special html blocks
-parsers.html_blankline_ending_special_block_opening = (parsers.html_close_special_tag
-                                                      + parsers.html_empty_special_tag)
-                                                    * #(parsers.optionalspace
-                                                       * (parsers.newline + parsers.eof))
-
-parsers.html_blankline_ending_special_block = parsers.html_blankline_ending_special_block_opening
-                                            * parsers.html_blankline_end_condition
-
-parsers.html_special_block_opening  = parsers.html_incomplete_open_special_tag
-                                    - parsers.html_empty_special_tag
-
-parsers.html_closing_special_block  = parsers.html_special_block_opening
-                                    * parsers.html_until_end(parsers.html_close_special_tag)
-
-parsers.html_special_block  = parsers.html_blankline_ending_special_block
-                            + parsers.html_closing_special_block
-
--- parse html blocks
-parsers.html_block_opening  = parsers.html_incomplete_open_tag
-                            + parsers.html_incomplete_close_tag
-
-parsers.html_block  = parsers.html_block_opening
-                    * parsers.html_blankline_end_condition
-
--- parse any html blocks
-parsers.html_any_block_opening  = (parsers.html_any_open_tag
-                                + parsers.html_any_close_tag
-                                + parsers.html_any_empty_tag)
-                                * #(parsers.optionalspace * (parsers.newline + parsers.eof))
-
-parsers.html_any_block  = parsers.html_any_block_opening
-                        * parsers.html_blankline_end_condition
-
-parsers.html_inline_comment_full  = parsers.html_comment_start
-                                  * -P(">") * -P("->")
-                                  * Cs((V("NoSoftLineBreakEndline") + parsers.any - P("--")
-                                      - parsers.nested_breaking_blank - parsers.html_comment_end)^0)
-                                  * parsers.html_comment_end
-
-parsers.html_inline_tags  = parsers.html_inline_comment_full
-                          + parsers.html_any_empty_inline_tag
-                          + parsers.html_inline_instruction
-                          + parsers.html_inline_cdatasection
-                          + parsers.html_inline_declaration
-                          + parsers.html_any_open_inline_tag
-                          + parsers.html_any_close_tag
-
-parsers.urlchar = parsers.anyescaped
-                - parsers.newline
-                - parsers.more
-
-parsers.auto_link_scheme_part = parsers.alphanumeric
-                              + parsers.plus
-                              + parsers.period
-                              + parsers.dash
-
-parsers.auto_link_scheme  = parsers.letter
-                          * parsers.auto_link_scheme_part
-                          * parsers.auto_link_scheme_part^-30
-
-parsers.absolute_uri  = parsers.auto_link_scheme * parsers.colon
-                      * (parsers.any - parsers.spacing - parsers.less - parsers.more)^0
-
-parsers.printable_characters = S(".!#$%&'*+/=?^_`{|}~-")
-
-parsers.email_address_local_part_char = parsers.alphanumeric
-                                      + parsers.printable_characters
-
-parsers.email_address_local_part = parsers.email_address_local_part_char^1
-
-parsers.email_address_dns_label = parsers.alphanumeric
-                                * (parsers.alphanumeric + parsers.dash)^-62
-                                * B(parsers.alphanumeric)
-
-parsers.email_address_domain  = parsers.email_address_dns_label
-                              * (parsers.period * parsers.email_address_dns_label)^0
-
-parsers.email_address = parsers.email_address_local_part
-                      * parsers.at
-                      * parsers.email_address_domain
-
-parsers.auto_link_url = parsers.less
-                      * C(parsers.absolute_uri)
-                      * parsers.more
-
-parsers.auto_link_email = parsers.less
-                        * C(parsers.email_address)
-                        * parsers.more
-
-parsers.auto_link_relative_reference = parsers.less
-                                    * C(parsers.urlchar^1)
-                                    * parsers.more
-
-parsers.autolink  = parsers.auto_link_url
-                  + parsers.auto_link_email
-
--- content in balanced brackets, parentheses, or quotes:
-parsers.bracketed   = P{ parsers.lbracket
-                       * (( parsers.backslash / "" * parsers.rbracket
-                          + parsers.any - (parsers.lbracket
-                                          + parsers.rbracket
-                                          + parsers.blankline^2)
-                          ) + V(1))^0
-                       * parsers.rbracket }
-
-parsers.inparens    = P{ parsers.lparent
-                       * ((parsers.anyescaped - (parsers.lparent
-                                                + parsers.rparent
-                                                + parsers.blankline^2)
-                          ) + V(1))^0
-                       * parsers.rparent }
-
-parsers.squoted     = P{ parsers.squote * parsers.alphanumeric
-                       * ((parsers.anyescaped - (parsers.squote
-                                                + parsers.blankline^2)
-                          ) + V(1))^0
-                       * parsers.squote }
-
-parsers.dquoted     = P{ parsers.dquote * parsers.alphanumeric
-                       * ((parsers.anyescaped - (parsers.dquote
-                                                + parsers.blankline^2)
-                          ) + V(1))^0
-                       * parsers.dquote }
-
-parsers.link_text  = parsers.lbracket
-                    * Cs((parsers.alphanumeric^1
-                        + parsers.bracketed
-                        + parsers.inticks
-                        + parsers.autolink
-                        + V("InlineHtml")
-                        + ( parsers.backslash * parsers.backslash)
-                        + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
-                          + V("NoSoftLineBreakSpace")
-                          + V("NoSoftLineBreakEndline")
-                          + (parsers.any
-                            - (parsers.newline + parsers.lbracket + parsers.rbracket + parsers.blankline^2))))^0)
-                    * parsers.rbracket
-
-parsers.link_label_body = -#(parsers.sp * parsers.rbracket)
-                        * #((parsers.any - parsers.rbracket)^-999 * parsers.rbracket)
-                        * Cs((parsers.alphanumeric^1
-                            + parsers.inticks
-                            + parsers.autolink
-                            + V("InlineHtml")
-                            + ( parsers.backslash * parsers.backslash)
-                            + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
-                              + V("NoSoftLineBreakSpace")
-                              + V("NoSoftLineBreakEndline")
-                              + (parsers.any
-                                - (parsers.newline + parsers.lbracket + parsers.rbracket + parsers.blankline^2))))^1)
-
-parsers.link_label  = parsers.lbracket
-                    * parsers.link_label_body
-                    * parsers.rbracket
-
-parsers.inparens_url  = P{ parsers.lparent
-                         * ((parsers.anyescaped - (parsers.lparent
-                                                  + parsers.rparent
-                                                  + parsers.spacing)
-                            ) + V(1))^0
-                         * parsers.rparent }
-
--- url for markdown links, allowing nested brackets:
-parsers.url         = parsers.less * Cs((parsers.anyescaped
-                                        - parsers.newline
-                                        - parsers.less
-                                        - parsers.more)^0)
-                                   * parsers.more
-                    + -parsers.less
-                    * Cs((parsers.inparens_url + (parsers.anyescaped
-                                                 - parsers.spacing
-                                                 - parsers.lparent
-                                                 - parsers.rparent))^1)
-
--- quoted text:
-parsers.title_s     = parsers.squote
-                    * Cs((parsers.html_entities
-                         + V("NoSoftLineBreakSpace")
-                         + V("NoSoftLineBreakEndline")
-                         + (parsers.anyescaped - parsers.newline - parsers.squote - parsers.blankline^2))^0)
-                    * parsers.squote
-
-parsers.title_d     = parsers.dquote
-                    * Cs((parsers.html_entities
-                         + V("NoSoftLineBreakSpace")
-                         + V("NoSoftLineBreakEndline")
-                         + (parsers.anyescaped - parsers.newline - parsers.dquote - parsers.blankline^2))^0)
-                    * parsers.dquote
-
-parsers.title_p     = parsers.lparent
-                    * Cs((parsers.html_entities
-                         + V("NoSoftLineBreakSpace")
-                         + V("NoSoftLineBreakEndline")
-                         + (parsers.anyescaped - parsers.newline - parsers.lparent - parsers.rparent
-                           - parsers.blankline^2))^0)
-                    * parsers.rparent
-
-parsers.title       = parsers.title_d + parsers.title_s + parsers.title_p
-
-parsers.optionaltitle
-                    = parsers.spnlc * parsers.title * parsers.spacechar^0
-                    + Cc("")
-
--- parse a reference definition:  [foo]: /bar "title"
-parsers.define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon
-                                * parsers.spnlc * parsers.url
-                                * ( parsers.spnlc_sep * parsers.title * parsers.only_blank
-                                  + Cc("") * parsers.only_blank)
-parsers.Inline         = V("Inline")
-
--- parse many p between starter and ender
-parsers.between = function(p, starter, ender)
-  local ender2 = B(parsers.nonspacechar) * ender
-  return (starter * #parsers.nonspacechar * Ct(p * (p - ender2)^0) * ender2)
-end
-
-parsers.lineof = function(c)
-    return (parsers.check_trail_no_rem * (P(c) * parsers.optionalspace)^3
-           * (parsers.newline + parsers.eof))
-end
-
-parsers.thematic_break_lines = parsers.lineof(parsers.asterisk)
-                             + parsers.lineof(parsers.dash)
-                             + parsers.lineof(parsers.underscore)
--- parse Atx heading start and return level
-parsers.heading_start = #parsers.hash * C(parsers.hash^-6)
-                      * -parsers.hash / length
-
--- parse setext header ending and return level
-parsers.heading_level = parsers.nonindentspace * parsers.equal^1 * parsers.optionalspace * #parsers.newline * Cc(1)
-                      + parsers.nonindentspace * parsers.dash^1 * parsers.optionalspace * #parsers.newline * Cc(2)
-
-local function strip_atx_end(s)
-  return s:gsub("%s+#*%s*\n$","")
-end
-
-parsers.atx_heading = parsers.check_trail_no_rem
-                    * Cg(parsers.heading_start, "level")
-                    * (C( parsers.optionalspace
-                        * parsers.hash^0
-                        * parsers.optionalspace
-                        * parsers.newline)
-                      + parsers.spacechar^1
-                      * C(parsers.line))
-M.reader = {}
-function M.reader.new(writer, options)
-  local self = {}
-  self.writer = writer
-  self.options = options
-  self.parsers = {}
-  (function(parsers)
-    setmetatable(self.parsers, {
-      __index = function (_, key)
-        return parsers[key]
-      end
-    })
-  end)(parsers)
-  local parsers = self.parsers
-  function self.normalize_tag(tag)
-    tag = util.rope_to_string(tag)
-    tag = tag:gsub("[ \n\r\t]+", " ")
-    tag = tag:gsub("^ ", ""):gsub(" $", "")
-    tag = uni_algos.case.casefold(tag, true, false)
-    return tag
-  end
-  local function iterlines(s, f)
-    local rope = lpeg.match(Ct((parsers.line / f)^1), s)
-    return util.rope_to_string(rope)
-  end
-  if options.preserveTabs then
-    self.expandtabs = function(s) return s end
-  else
-    self.expandtabs = function(s)
-                        if s:find("\t") then
-                          return iterlines(s, util.expand_tabs_in_line)
-                        else
-                          return s
-                        end
-                      end
-  end
-  self.parser_functions = {}
-  self.create_parser = function(name, grammar, toplevel)
-    self.parser_functions[name] = function(str)
-      if toplevel and options.stripIndent then
-          local min_prefix_length, min_prefix = nil, ''
-          str = iterlines(str, function(line)
-              if lpeg.match(parsers.nonemptyline, line) == nil then
-                  return line
-              end
-              line = util.expand_tabs_in_line(line)
-              local prefix = lpeg.match(C(parsers.optionalspace), line)
-              local prefix_length = #prefix
-              local is_shorter = min_prefix_length == nil
-              is_shorter = is_shorter or prefix_length < min_prefix_length
-              if is_shorter then
-                  min_prefix_length, min_prefix = prefix_length, prefix
-              end
-              return line
-          end)
-          str = str:gsub('^' .. min_prefix, '')
-      end
-      if toplevel and (options.texComments or options.hybrid) then
-        str = lpeg.match(Ct(parsers.commented_line^1), str)
-        str = util.rope_to_string(str)
-      end
-      local res = lpeg.match(grammar(), str)
-      if res == nil then
-        error(format("%s failed on:\n%s", name, str:sub(1,20)))
-      else
-        return res
-      end
-    end
-  end
-
-  self.create_parser("parse_blocks",
-                     function()
-                       return parsers.blocks
-                     end, true)
-
-  self.create_parser("parse_blocks_nested",
-                     function()
-                       return parsers.blocks_nested
-                     end, false)
-
-  self.create_parser("parse_inlines",
-                     function()
-                       return parsers.inlines
-                     end, false)
-
-  self.create_parser("parse_inlines_no_inline_note",
-                     function()
-                       return parsers.inlines_no_inline_note
-                     end, false)
-
-  self.create_parser("parse_inlines_no_html",
-                     function()
-                       return parsers.inlines_no_html
-                     end, false)
-
-  self.create_parser("parse_inlines_nbsp",
-                     function()
-                       return parsers.inlines_nbsp
-                     end, false)
-  self.create_parser("parse_inlines_no_link_or_emphasis",
-                    function()
-                      return parsers.inlines_no_link_or_emphasis
-                    end, false)
-  parsers.minimally_indented_blankline = parsers.check_minimal_indent * (parsers.blankline / "")
-
-  parsers.minimally_indented_block = parsers.check_minimal_indent * V("Block")
-
-  parsers.minimally_indented_block_or_paragraph = parsers.check_minimal_indent * V("BlockOrParagraph")
-
-  parsers.minimally_indented_paragraph = parsers.check_minimal_indent * V("Paragraph")
-
-  parsers.minimally_indented_plain = parsers.check_minimal_indent * V("Plain")
-
-  parsers.minimally_indented_par_or_plain = parsers.minimally_indented_paragraph
-                                          + parsers.minimally_indented_plain
-
-  parsers.minimally_indented_par_or_plain_no_blank  = parsers.minimally_indented_par_or_plain
-                                                    - parsers.minimally_indented_blankline
-
-  parsers.minimally_indented_ref = parsers.check_minimal_indent * V("Reference")
-
-  parsers.minimally_indented_blank = parsers.check_minimal_indent * V("Blank")
-
-  parsers.conditionally_indented_blankline = parsers.check_minimal_blank_indent * (parsers.blankline / "")
-
-  parsers.minimally_indented_ref_or_block = parsers.minimally_indented_ref
-                                          + parsers.minimally_indented_block
-                                          - parsers.minimally_indented_blankline
-
-  parsers.minimally_indented_ref_or_block_or_par  = parsers.minimally_indented_ref
-                                                  + parsers.minimally_indented_block_or_paragraph
-                                                  - parsers.minimally_indented_blankline
-
-
-  parsers.separator_loop = function(separated_block, paragraph, block_separator, paragraph_separator)
-    return  separated_block
-          + block_separator
-            * paragraph
-            * separated_block
-          + paragraph_separator
-          * paragraph
-  end
-
-  parsers.create_loop_body_pair = function(separated_block, paragraph, block_separator, paragraph_separator)
-    return {
-      block = parsers.separator_loop(separated_block, paragraph, block_separator, block_separator),
-      par = parsers.separator_loop(separated_block, paragraph, block_separator, paragraph_separator)
-    }
-  end
-
-  parsers.block_sep_group = function(blank)
-    return  blank^0 * parsers.eof
-          + ( blank^2 / writer.paragraphsep
-            + blank^0 / writer.interblocksep
-            )
-  end
-
-  parsers.par_sep_group = function(blank)
-    return  blank^0 * parsers.eof
-          + blank^0 / writer.paragraphsep
-  end
-
-  parsers.sep_group_no_output = function(blank)
-    return  blank^0 * parsers.eof
-          + blank^0
-  end
-
-  parsers.content_blank = parsers.minimally_indented_blankline
-
-  parsers.ref_or_block_separated  = parsers.sep_group_no_output(parsers.content_blank)
-                                  * ( parsers.minimally_indented_ref
-                                    - parsers.content_blank)
-                                  + parsers.block_sep_group(parsers.content_blank)
-                                  * ( parsers.minimally_indented_block
-                                    - parsers.content_blank)
-
-  parsers.loop_body_pair  =
-    parsers.create_loop_body_pair(parsers.ref_or_block_separated,
-                                  parsers.minimally_indented_par_or_plain_no_blank,
-                                  parsers.block_sep_group(parsers.content_blank),
-                                  parsers.par_sep_group(parsers.content_blank))
-
-  parsers.content_loop  = ( V("Block")
-                          * parsers.loop_body_pair.block^0
-                          + (V("Paragraph") + V("Plain"))
-                          * parsers.ref_or_block_separated
-                          * parsers.loop_body_pair.block^0
-                          + (V("Paragraph") + V("Plain"))
-                          * parsers.loop_body_pair.par^0)
-                        * parsers.content_blank^0
-
-  parsers.indented_content = function()
-    return  Ct( (V("Reference") + (parsers.blankline / ""))
-              * parsers.content_blank^0
-              * parsers.check_minimal_indent
-              * parsers.content_loop
-              + (V("Reference") + (parsers.blankline / ""))
-              * parsers.content_blank^0
-              + parsers.content_loop)
-  end
-
-  parsers.add_indent = function(pattern, name, breakable)
-    return  Cg(Cmt( Cb("indent_info")
-                  * Ct(pattern)
-                  * (#parsers.linechar * Cc(false) + Cc(true)) -- check if starter is blank
-                  * Cc(name)
-                  * Cc(breakable),
-              process_starter_indent), "indent_info")
-  end
-
-  if options.hashEnumerators then
-    parsers.dig = parsers.digit + parsers.hash
-  else
-    parsers.dig = parsers.digit
-  end
-
-  parsers.enumerator = function(delimiter_type, interrupting)
-    local delimiter_range
-    local allowed_end
-    if interrupting then
-      delimiter_range = P("1")
-      allowed_end = C(parsers.spacechar^1) * #parsers.linechar
-    else
-      delimiter_range = parsers.dig * parsers.dig^-8
-      allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
-    end
-
-    return parsers.check_trail
-             * Ct(C(delimiter_range) * C(delimiter_type))
-             * allowed_end
-  end
-
-  parsers.starter = parsers.bullet(parsers.dash)
-                  + parsers.bullet(parsers.asterisk)
-                  + parsers.bullet(parsers.plus)
-                  + parsers.enumerator(parsers.period)
-                  + parsers.enumerator(parsers.rparent)
-
-  parsers.blockquote_start = parsers.check_trail * C(parsers.more) * C(parsers.spacechar^0)
-
-  parsers.blockquote_body = parsers.add_indent(parsers.blockquote_start, "bq", true)
-                          * parsers.indented_content()
-                          * remove_indent("bq")
-
-  if not options.breakableBlockquotes then
-    parsers.blockquote_body = parsers.add_indent(parsers.blockquote_start, "bq", false)
-                            * parsers.indented_content()
-                            * remove_indent("bq")
-  end
-  local function parse_content_part(content_part)
-    local rope = util.rope_to_string(content_part)
-    local parsed = self.parser_functions.parse_inlines_no_link_or_emphasis(rope)
-    parsed.indent_info = nil
-    return parsed
-  end
-
-  local function collect_emphasis_content(t, opening_index, closing_index)
-    local content = {}
-
-    local content_part = {}
-    for i = opening_index, closing_index do
-      local value = t[i]
-
-      if value.rendered ~= nil then
-        content[#content + 1] = parse_content_part(content_part)
-        content_part = {}
-        content[#content + 1] = value.rendered
-        value.rendered = nil
-      else
-        if value.type == "delimiter" and value.element == "emphasis" then
-          if value.is_active then
-            content_part[#content_part + 1] = string.rep(value.character, value.current_count)
-          end
-        else
-          content_part[#content_part + 1] = value.content
+function M.new(options)
+  options = options or {}
+  setmetatable(options, { __index = function (_, key)
+    return defaultOptions[key] end })
+  local parser_convert = nil
+  return function(input)
+    local function convert(input)
+      if parser_convert == nil then
+        local parser = require("markdown-parser")
+        if metadata.version ~= parser.metadata.version then
+          warn("markdown.lua " .. metadata.version .. " used with " ..
+               "markdown-parser.lua " .. parser.metadata.version .. ".")
         end
-        value.content = ''
-        value.is_active = false
+        parser_convert = parser.new(options)
       end
+      return parser_convert(input)
     end
-
-    if next(content_part) ~= nil then
-      content[#content + 1] = parse_content_part(content_part)
-    end
-
-    return content
-  end
-
-  local function fill_emph(t, opening_index, closing_index)
-    local content = collect_emphasis_content(t, opening_index + 1, closing_index - 1)
-    t[opening_index + 1].is_active = true
-    t[opening_index + 1].rendered = writer.emphasis(content)
-  end
-
-  local function fill_strong(t, opening_index, closing_index)
-    local content = collect_emphasis_content(t, opening_index + 1, closing_index - 1)
-    t[opening_index + 1].is_active = true
-    t[opening_index + 1].rendered = writer.strong(content)
-  end
-
-  local function breaks_three_rule(opening_delimiter, closing_delimiter)
-    return (opening_delimiter.is_closing or closing_delimiter.is_opening) and
-      ((opening_delimiter.original_count + closing_delimiter.original_count) % 3 == 0) and
-      (opening_delimiter.original_count % 3 ~= 0 or closing_delimiter.original_count % 3 ~= 0)
-  end
-
-  local function find_emphasis_opener(t, bottom_index, latest_index, character, closing_delimiter)
-    for i = latest_index, bottom_index, -1 do
-      local value = t[i]
-      if value.is_active and
-         value.is_opening and
-         value.type == "delimiter" and
-         value.element == "emphasis" and
-         (value.character == character) and
-         (value.current_count > 0) then
-        if not breaks_three_rule(value, closing_delimiter) then
-          return i
-        end
-      end
-    end
-  end
-
-  local function process_emphasis(t, opening_index, closing_index)
-    for i = opening_index, closing_index do
-      local value = t[i]
-      if value.type == "delimiter" and value.element == "emphasis" then
-          local delimiter_length = string.len(value.content)
-          value.character = string.sub(value.content, 1, 1)
-          value.current_count = delimiter_length
-          value.original_count = delimiter_length
-      end
-    end
-
-    local openers_bottom = {
-      ['*'] = {
-        [true] = {opening_index, opening_index, opening_index},
-        [false] = {opening_index, opening_index, opening_index}
-      },
-      ['_'] = {
-        [true] = {opening_index, opening_index, opening_index},
-        [false] = {opening_index, opening_index, opening_index}
-      }
-    }
-
-    local current_position = opening_index
-    local max_position = closing_index
-
-    while current_position <= max_position do
-      local value = t[current_position]
-
-      if value.type ~= "delimiter" or
-        value.element ~= "emphasis" or
-        not value.is_active or
-        not value.is_closing or
-        (value.current_count <= 0) then
-        current_position = current_position + 1
-        goto continue
-      end
-
-      local character = value.character
-      local is_opening = value.is_opening
-      local closing_length_modulo_three = value.original_count % 3
-
-      local current_openers_bottom = openers_bottom[character][is_opening][closing_length_modulo_three + 1]
-
-      local opener_position = find_emphasis_opener(t, current_openers_bottom, current_position - 1, character, value)
-
-      if (opener_position == nil) then
-        openers_bottom[character][is_opening][closing_length_modulo_three + 1] = current_position
-        current_position = current_position + 1
-        goto continue
-      end
-
-      local opening_delimiter = t[opener_position]
-
-      local current_opening_count = opening_delimiter.current_count
-      local current_closing_count = t[current_position].current_count
-
-      if (current_opening_count >= 2) and (current_closing_count >= 2) then
-        opening_delimiter.current_count = current_opening_count - 2
-        t[current_position].current_count = current_closing_count - 2
-        fill_strong(t, opener_position, current_position)
-      else
-        opening_delimiter.current_count = current_opening_count - 1
-        t[current_position].current_count = current_closing_count - 1
-        fill_emph(t, opener_position, current_position)
-      end
-
-      ::continue::
-    end
-  end
-
-  local cont = lpeg.R("\128\191") -- continuation byte
-
-  local function utf8_by_byte_count(n)
-    if (n == 1) then
-      return lpeg.R("\0\127")
-    end
-    if (n == 2) then
-      return lpeg.R("\194\223") * cont
-    end
-    if (n == 3) then
-      return lpeg.R("\224\239") * cont * cont
-    end
-    if (n == 4) then
-      return lpeg.R("\240\244") * cont * cont * cont
-    end
-  end
-  local function check_unicode_type(s, i, start_pos, end_pos, chartype)
-    local c
-    local char_length
-    for pos = start_pos, end_pos, 1 do
-      if (start_pos < 0) then
-        char_length = -pos
-      else
-        char_length = pos + 1
-      end
-
-      if (chartype == "punctuation") then
-        if lpeg.match(parsers.punctuation[char_length], s, i+pos) then
-          return i
-        end
-      else
-        c = lpeg.match({ C(utf8_by_byte_count(char_length)) },s,i+pos)
-        if (c ~= nil) and (unicode.utf8.match(c, chartype)) then
-          return i
-        end
-      end
-    end
-  end
-
-  local function check_preceding_unicode_punctuation(s, i)
-    return check_unicode_type(s, i, -4, -1, "punctuation")
-  end
-
-  local function check_preceding_unicode_whitespace(s, i)
-    return check_unicode_type(s, i, -4, -1, "%s")
-  end
-
-  local function check_following_unicode_punctuation(s, i)
-    return check_unicode_type(s, i, 0, 3, "punctuation")
-  end
-
-  local function check_following_unicode_whitespace(s, i)
-    return check_unicode_type(s, i, 0, 3, "%s")
-  end
-
-  parsers.unicode_preceding_punctuation = B(parsers.escapable)
-                                        + Cmt(parsers.succeed, check_preceding_unicode_punctuation)
-
-  parsers.unicode_preceding_whitespace = Cmt(parsers.succeed, check_preceding_unicode_whitespace)
-
-  parsers.unicode_following_punctuation = #parsers.escapable
-                                        + Cmt(parsers.succeed, check_following_unicode_punctuation)
-
-  parsers.unicode_following_whitespace = Cmt(parsers.succeed, check_following_unicode_whitespace)
-
-  parsers.delimiter_run = function(character)
-    return  (B(parsers.backslash * character) + -B(character))
-          * character^1
-          * -#character
-  end
-
-  parsers.left_flanking_delimiter_run = function(character)
-    return  (B( parsers.any)
-              * (parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace)
-             + -B(parsers.any))
-            * parsers.delimiter_run(character)
-            * parsers.unicode_following_punctuation
-            + parsers.delimiter_run(character)
-            * -#(parsers.unicode_following_punctuation + parsers.unicode_following_whitespace
-                + parsers.eof)
-  end
-
-  parsers.right_flanking_delimiter_run = function(character)
-    return  parsers.unicode_preceding_punctuation
-          * parsers.delimiter_run(character)
-          * (parsers.unicode_following_punctuation + parsers.unicode_following_whitespace
-            + parsers.eof)
-          + (B(parsers.any)
-            * -(parsers.unicode_preceding_punctuation + parsers.unicode_preceding_whitespace))
-          * parsers.delimiter_run(character)
-  end
-
-  if options.underscores then
-    parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk)
-                      + (-#parsers.right_flanking_delimiter_run(parsers.underscore)
-                          + (parsers.unicode_preceding_punctuation
-                            * #parsers.right_flanking_delimiter_run(parsers.underscore)))
-                      * parsers.left_flanking_delimiter_run(parsers.underscore)
-
-    parsers.emph_end  = parsers.right_flanking_delimiter_run(parsers.asterisk)
-                      + (-#parsers.left_flanking_delimiter_run(parsers.underscore)
-                        + #(parsers.left_flanking_delimiter_run(parsers.underscore)
-                          * parsers.unicode_following_punctuation))
-                      * parsers.right_flanking_delimiter_run(parsers.underscore)
-  else
-    parsers.emph_start = parsers.left_flanking_delimiter_run(parsers.asterisk)
-
-    parsers.emph_end  = parsers.right_flanking_delimiter_run(parsers.asterisk)
-  end
-
-  parsers.emph_capturing_open_and_close = #parsers.emph_start * #parsers.emph_end
-                                        * Ct( Cg(Cc("delimiter"), "type")
-                                            * Cg(Cc("emphasis"), "element")
-                                            * Cg(C(parsers.emph_start), "content")
-                                            * Cg(Cc(true), "is_opening")
-                                            * Cg(Cc(true), "is_closing"))
-
-  parsers.emph_capturing_open = Ct( Cg(Cc("delimiter"), "type")
-                                  * Cg(Cc("emphasis"), "element")
-                                  * Cg(C(parsers.emph_start), "content")
-                                  * Cg(Cc(true), "is_opening")
-                                  * Cg(Cc(false), "is_closing"))
-
-  parsers.emph_capturing_close = Ct( Cg(Cc("delimiter"), "type")
-                                   * Cg(Cc("emphasis"), "element")
-                                   * Cg(C(parsers.emph_end), "content")
-                                   * Cg(Cc(false), "is_opening")
-                                   * Cg(Cc(true), "is_closing"))
-
-  parsers.emph_open_or_close  = parsers.emph_capturing_open_and_close
-                              + parsers.emph_capturing_open
-                              + parsers.emph_capturing_close
-
-  parsers.emph_open = parsers.emph_capturing_open_and_close
-                    + parsers.emph_capturing_open
-
-  parsers.emph_close  = parsers.emph_capturing_open_and_close
-                      + parsers.emph_capturing_close
-
-  -- List of references defined in the document
-  local references
-
-  -- List of note references defined in the document
-  parsers.rawnotes = {}
-
-  function self.register_link(_, tag, url, title,
-                              attributes)
-    local normalized_tag = self.normalize_tag(tag)
-      if references[normalized_tag] == nil then
-        references[normalized_tag] = {
-          url = url,
-          title = title,
-          attributes = attributes
-        }
-      end
-    return ""
-  end
-
-  function self.lookup_reference(tag)
-    return references[self.normalize_tag(tag)]
-  end
-
-  function self.lookup_note_reference(tag)
-    return parsers.rawnotes[self.normalize_tag(tag)]
-  end
-
-  parsers.title_s_direct_ref  = parsers.squote
-                              * Cs((parsers.html_entities
-                                  + (parsers.anyescaped - parsers.squote - parsers.blankline^2))^0)
-                              * parsers.squote
-
-  parsers.title_d_direct_ref  = parsers.dquote
-                              * Cs((parsers.html_entities
-                                  + (parsers.anyescaped - parsers.dquote - parsers.blankline^2))^0)
-                              * parsers.dquote
-
-  parsers.title_p_direct_ref  = parsers.lparent
-                              * Cs((parsers.html_entities
-                                  + (parsers.anyescaped - parsers.lparent - parsers.rparent - parsers.blankline^2))^0)
-                              * parsers.rparent
-
-  parsers.title_direct_ref  = parsers.title_s_direct_ref
-                            + parsers.title_d_direct_ref
-                            + parsers.title_p_direct_ref
-
-  parsers.inline_direct_ref_inside  = parsers.lparent * parsers.spnl
-                                    * Cg(parsers.url + Cc(""), "url")
-                                    * parsers.spnl
-                                    * Cg(parsers.title_direct_ref + Cc(""), "title")
-                                    * parsers.spnl * parsers.rparent
-
-  parsers.inline_direct_ref = parsers.lparent * parsers.spnlc
-                            * Cg(parsers.url + Cc(""), "url")
-                            * parsers.spnlc
-                            * Cg(parsers.title + Cc(""), "title")
-                            * parsers.spnlc * parsers.rparent
-
-  parsers.empty_link  = parsers.lbracket
-                      * parsers.rbracket
-
-  parsers.inline_link = parsers.link_text
-                      * parsers.inline_direct_ref
-
-  parsers.full_link = parsers.link_text
-                    * parsers.link_label
-
-  parsers.shortcut_link = parsers.link_label
-                        * -(parsers.empty_link + parsers.link_label)
-
-  parsers.collapsed_link  = parsers.link_label
-                          * parsers.empty_link
-
-  parsers.image_opening = #(parsers.exclamation * parsers.inline_link)
-                        * Cg(Cc("inline"), "link_type")
-                        + #(parsers.exclamation * parsers.full_link)
-                        * Cg(Cc("full"), "link_type")
-                        + #(parsers.exclamation * parsers.collapsed_link)
-                        * Cg(Cc("collapsed"), "link_type")
-                        + #(parsers.exclamation * parsers.shortcut_link)
-                        * Cg(Cc("shortcut"), "link_type")
-                        + #(parsers.exclamation * parsers.empty_link)
-                        * Cg(Cc("empty"), "link_type")
-
-  parsers.link_opening  = #parsers.inline_link
-                        * Cg(Cc("inline"), "link_type")
-                        + #parsers.full_link
-                        * Cg(Cc("full"), "link_type")
-                        + #parsers.collapsed_link
-                        * Cg(Cc("collapsed"), "link_type")
-                        + #parsers.shortcut_link
-                        * Cg(Cc("shortcut"), "link_type")
-                        + #parsers.empty_link
-                        * Cg(Cc("empty_link"), "link_type")
-                        + #parsers.link_text
-                        * Cg(Cc("link_text"), "link_type")
-
-  parsers.note_opening  = #(parsers.circumflex * parsers.link_text)
-                        * Cg(Cc("note_inline"), "link_type")
-
-  parsers.raw_note_opening  = #( parsers.lbracket
-                               * parsers.circumflex
-                               * parsers.link_label_body
-                               * parsers.rbracket)
-                            * Cg(Cc("raw_note"), "link_type")
-
-  local inline_note_element = Cg(Cc("note"), "element")
-                            * parsers.note_opening
-                            * Cg(parsers.circumflex * parsers.lbracket, "content")
-
-  local image_element = Cg(Cc("image"), "element")
-                      * parsers.image_opening
-                      * Cg(parsers.exclamation * parsers.lbracket, "content")
-
-  local note_element  = Cg(Cc("note"), "element")
-                      * parsers.raw_note_opening
-                      * Cg(parsers.lbracket * parsers.circumflex, "content")
-
-  local link_element  = Cg(Cc("link"), "element")
-                      * parsers.link_opening
-                      * Cg(parsers.lbracket, "content")
-
-  local opening_elements = parsers.fail
-
-  if options.inlineNotes then
-    opening_elements = opening_elements + inline_note_element
-  end
-
-  opening_elements = opening_elements + image_element
-
-  if options.notes then
-    opening_elements = opening_elements + note_element
-  end
-
-  opening_elements = opening_elements + link_element
-
-  parsers.link_image_opening  = Ct( Cg(Cc("delimiter"), "type")
-                                  * Cg(Cc(true), "is_opening")
-                                  * Cg(Cc(false), "is_closing")
-                                  * opening_elements)
-
-  parsers.link_image_closing  = Ct( Cg(Cc("delimiter"), "type")
-                                  * Cg(Cc("link"), "element")
-                                  * Cg(Cc(false), "is_opening")
-                                  * Cg(Cc(true), "is_closing")
-                                  * ( Cg(Cc(true), "is_direct")
-                                    * Cg(parsers.rbracket * #parsers.inline_direct_ref, "content")
-                                    + Cg(Cc(false), "is_direct")
-                                    * Cg(parsers.rbracket, "content")))
-
-  parsers.link_image_open_or_close  = parsers.link_image_opening
-                                    + parsers.link_image_closing
-
-  if options.html then
-    parsers.link_emph_precedence  = parsers.inticks
-                                  + parsers.autolink
-                                  + parsers.html_inline_tags
-  else
-    parsers.link_emph_precedence  = parsers.inticks
-                                  + parsers.autolink
-  end
-
-  parsers.link_and_emph_endline = parsers.newline
-                                * ((parsers.check_minimal_indent
-                                  * -V("EndlineExceptions")
-                                  + parsers.check_optional_indent
-                                  * -V("EndlineExceptions")
-                                  * -parsers.starter) / "")
-                                * parsers.spacechar^0 / "\n"
-
-  parsers.link_and_emph_content = Ct( Cg(Cc("content"), "type")
-                                    * Cg(Cs(( parsers.link_emph_precedence
-                                            + parsers.backslash * parsers.any
-                                            + parsers.link_and_emph_endline
-                                            + (parsers.linechar
-                                              - parsers.blankline^2
-                                              - parsers.link_image_open_or_close
-                                              - parsers.emph_open_or_close))^0), "content"))
-
-  parsers.link_and_emph_table = (parsers.link_image_opening + parsers.emph_open)
-                              * parsers.link_and_emph_content
-                              * ((parsers.link_image_open_or_close + parsers.emph_open_or_close)
-                                * parsers.link_and_emph_content)^1
-
-  local function collect_link_content(t, opening_index, closing_index)
-    local content = {}
-    for i = opening_index, closing_index do
-      content[#content + 1] = t[i].content
-    end
-    return util.rope_to_string(content)
-  end
-
-  local function find_link_opener(t, bottom_index, latest_index)
-    for i = latest_index, bottom_index, -1 do
-      local value = t[i]
-      if value.type == "delimiter" and
-         value.is_opening and
-         (value.element == "link" or value.element == "image" or value.element == "note")
-         and not value.removed then
-        if value.is_active then
-          return i
-        end
-        value.removed = true
-        return nil
-      end
-    end
-  end
-
-  local function find_next_link_closing_index(t, latest_index)
-    for i = latest_index, #t do
-      local value = t[i]
-      if value.is_closing and
-         value.element == "link" and
-         not value.removed then
-        return i
-      end
-    end
-  end
-
-  local function disable_previous_link_openers(t, opening_index)
-    if t[opening_index].element == "image" then
-      return
-    end
-
-    for i = opening_index, 1, -1 do
-      local value = t[i]
-      if value.is_active and
-         value.type == "delimiter" and
-         value.is_opening and
-         value.element == "link" then
-        value.is_active = false
-      end
-    end
-  end
-
-  local function disable_range(t, opening_index, closing_index)
-    for i = opening_index, closing_index do
-      local value = t[i]
-      if value.is_active then
-        value.is_active = false
-        if value.type == "delimiter" then
-          value.removed = true
-        end
-      end
-    end
-  end
-
-  local function delete_parsed_content_in_range(t, opening_index, closing_index)
-    for i = opening_index, closing_index do
-      t[i].rendered = nil
-    end
-  end
-
-  local function empty_content_in_range(t, opening_index, closing_index)
-    for i = opening_index, closing_index do
-      t[i].content = ''
-    end
-  end
-
-  local function join_attributes(reference_attributes, own_attributes)
-    local merged_attributes = {}
-    for _, attribute in ipairs(reference_attributes or {}) do
-      table.insert(merged_attributes, attribute)
-    end
-    for _, attribute in ipairs(own_attributes or {}) do
-      table.insert(merged_attributes, attribute)
-    end
-    if next(merged_attributes) == nil then
-      merged_attributes = nil
-    end
-    return merged_attributes
-  end
-
-  local function render_link_or_image(t, opening_index, closing_index, content_end_index, reference)
-    process_emphasis(t, opening_index, content_end_index)
-    local mapped = collect_emphasis_content(t, opening_index + 1, content_end_index - 1)
-
-    local rendered = {}
-    if (t[opening_index].element == "link") then
-      rendered = writer.link(mapped, reference.url, reference.title, reference.attributes)
-    end
-
-    if (t[opening_index].element == "image") then
-      rendered = writer.image(mapped, reference.url, reference.title, reference.attributes)
-    end
-
-    if (t[opening_index].element == "note") then
-      if (t[opening_index].link_type == "note_inline") then
-        rendered = writer.note(mapped)
-      end
-      if (t[opening_index].link_type == "raw_note") then
-        rendered = writer.note(reference)
-      end
-    end
-
-    t[opening_index].rendered = rendered
-    delete_parsed_content_in_range(t, opening_index + 1, closing_index)
-    empty_content_in_range(t, opening_index, closing_index)
-    disable_previous_link_openers(t, opening_index)
-    disable_range(t, opening_index, closing_index)
-  end
-
-  local function resolve_inline_following_content(t, closing_index, match_reference, match_link_attributes)
-    local content = ""
-    for i = closing_index + 1, #t do
-      content = content .. t[i].content
-    end
-
-    local matching_content = parsers.succeed
-
-    if match_reference then
-      matching_content = matching_content * parsers.inline_direct_ref_inside
-    end
-
-    if match_link_attributes then
-      matching_content = matching_content * Cg(Ct(parsers.attributes^-1), "attributes")
-    end
-
-    local matched = lpeg.match(Ct(matching_content * Cg(Cp(), "end_position")), content)
-
-    local matched_count = matched.end_position - 1
-    for i = closing_index + 1, #t do
-      local value = t[i]
-
-      local chars_left = matched_count
-      matched_count = matched_count - #value.content
-
-      if matched_count <= 0 then
-        value.content = value.content:sub(chars_left + 1)
-        break
-      end
-
-      value.content = ''
-      value.is_active = false
-    end
-
-    local attributes = matched.attributes
-    if attributes == nil or next(attributes) == nil then
-      attributes = nil
-    end
-
-    return {
-      url = matched.url or "",
-      title = matched.title or "",
-      attributes = attributes
-    }
-  end
-
-  local function resolve_inline_link(t, opening_index, closing_index)
-    local inline_content = resolve_inline_following_content(t, closing_index, true, t.match_link_attributes)
-    render_link_or_image(t, opening_index, closing_index, closing_index, inline_content)
-  end
-
-  local function resolve_note_inline_link(t, opening_index, closing_index)
-    local inline_content = resolve_inline_following_content(t, closing_index, false, false)
-    render_link_or_image(t, opening_index, closing_index, closing_index, inline_content)
-  end
-
-  local function resolve_shortcut_link(t, opening_index, closing_index)
-    local content = collect_link_content(t, opening_index + 1, closing_index - 1)
-    local r = self.lookup_reference(content)
-
-    if r then
-      local inline_content = resolve_inline_following_content(t, closing_index, false, t.match_link_attributes)
-      r.attributes = join_attributes(r.attributes, inline_content.attributes)
-      render_link_or_image(t, opening_index, closing_index, closing_index, r)
-    end
-  end
-
-  local function resolve_raw_note_link(t, opening_index, closing_index)
-    local content = collect_link_content(t, opening_index + 1, closing_index - 1)
-    local r = self.lookup_note_reference(content)
-
-    if r then
-      local parsed_ref = self.parser_functions.parse_blocks_nested(r)
-      render_link_or_image(t, opening_index, closing_index, closing_index, parsed_ref)
-    end
-  end
-
-  local function resolve_full_link(t, opening_index, closing_index)
-    local next_link_closing_index = find_next_link_closing_index(t, closing_index + 4)
-    local next_link_content = collect_link_content(t, closing_index + 3, next_link_closing_index - 1)
-    local r = self.lookup_reference(next_link_content)
-
-    if r then
-      local inline_content = resolve_inline_following_content(t, next_link_closing_index, false,
-                                                              t.match_link_attributes)
-      r.attributes = join_attributes(r.attributes, inline_content.attributes)
-      render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r)
-    end
-  end
-
-  local function resolve_collapsed_link(t, opening_index, closing_index)
-    local next_link_closing_index = find_next_link_closing_index(t, closing_index + 4)
-    local content = collect_link_content(t, opening_index + 1, closing_index - 1)
-    local r = self.lookup_reference(content)
-
-    if r then
-      local inline_content = resolve_inline_following_content(t, closing_index, false, t.match_link_attributes)
-      r.attributes = join_attributes(r.attributes, inline_content.attributes)
-      render_link_or_image(t, opening_index, next_link_closing_index, closing_index, r)
-    end
-  end
-
-  local function process_links_and_emphasis(t)
-    for _,value in ipairs(t) do
-      value.is_active = true
-    end
-
-    for i,value in ipairs(t) do
-      if not value.is_closing
-          or value.type ~= "delimiter"
-          or not (value.element == "link" or value.element == "image" or value.element == "note")
-          or value.removed then
-        goto continue
-      end
-
-      local opener_position = find_link_opener(t, 1, i - 1)
-      if (opener_position == nil) then
-        goto continue
-      end
-
-      local opening_delimiter = t[opener_position]
-      opening_delimiter.removed = true
-
-      local link_type = opening_delimiter.link_type
-
-      if (link_type == "inline") then
-        resolve_inline_link(t, opener_position, i)
-      end
-      if (link_type == "shortcut") then
-        resolve_shortcut_link(t, opener_position, i)
-      end
-      if (link_type == "full") then
-        resolve_full_link(t, opener_position, i)
-      end
-      if (link_type == "collapsed") then
-        resolve_collapsed_link(t, opener_position, i)
-      end
-      if (link_type == "note_inline") then
-        resolve_note_inline_link(t, opener_position, i)
-      end
-      if (link_type == "raw_note") then
-        resolve_raw_note_link(t, opener_position, i)
-      end
-
-      ::continue::
-    end
-
-    t[#t].content = t[#t].content:gsub("%s*$","")
-
-    process_emphasis(t, 1, #t)
-    local final_result = collect_emphasis_content(t, 1, #t)
-    return final_result
-  end
-
-  function self.defer_link_and_emphasis_processing(delimiter_table)
-    return writer.defer_call(function()
-      return process_links_and_emphasis(delimiter_table)
-    end)
-  end
-
-  parsers.Str      = (parsers.normalchar * (parsers.normalchar + parsers.at)^0)
-                   / writer.string
-
-  parsers.Symbol   = (parsers.backtick^1 + V("SpecialChar"))
-                   / writer.string
-
-  parsers.Ellipsis = P("...") / writer.ellipsis
-
-  parsers.Smart    = parsers.Ellipsis
-
-  parsers.Code     = parsers.inticks / writer.code
-
-  if options.blankBeforeBlockquote then
-    parsers.bqstart = parsers.fail
-  else
-    parsers.bqstart = parsers.blockquote_start
-  end
-
-  if options.blankBeforeHeading then
-    parsers.headerstart = parsers.fail
-  else
-    parsers.headerstart = parsers.atx_heading
-  end
-
-  if options.blankBeforeList then
-    parsers.interrupting_bullets = parsers.fail
-    parsers.interrupting_enumerators = parsers.fail
-  else
-    parsers.interrupting_bullets  = parsers.bullet(parsers.dash, true)
-                                  + parsers.bullet(parsers.asterisk, true)
-                                  + parsers.bullet(parsers.plus, true)
-
-    parsers.interrupting_enumerators  = parsers.enumerator(parsers.period, true)
-                                      + parsers.enumerator(parsers.rparent, true)
-  end
-
-  if options.html then
-    parsers.html_interrupting = parsers.check_trail
-                              * ( parsers.html_incomplete_open_tag
-                                + parsers.html_incomplete_close_tag
-                                + parsers.html_incomplete_open_special_tag
-                                + parsers.html_comment_start
-                                + parsers.html_cdatasection_start
-                                + parsers.html_declaration_start
-                                + parsers.html_instruction_start
-                                - parsers.html_close_special_tag
-                                - parsers.html_empty_special_tag)
-  else
-    parsers.html_interrupting = parsers.fail
-  end
-
-  parsers.EndlineExceptions
-                     = parsers.blankline -- paragraph break
-                     + parsers.eof       -- end of document
-                     + parsers.bqstart
-                     + parsers.thematic_break_lines
-                     + parsers.interrupting_bullets
-                     + parsers.interrupting_enumerators
-                     + parsers.headerstart
-                     + parsers.html_interrupting
-
-  parsers.NoSoftLineBreakEndlineExceptions = parsers.EndlineExceptions
-
-  parsers.endline = parsers.newline
-                  * (parsers.check_minimal_indent
-                    * -V("EndlineExceptions")
-                    + parsers.check_optional_indent
-                    * -V("EndlineExceptions")
-                    * -parsers.starter)
-                  * parsers.spacechar^0
-
-  parsers.Endline = parsers.endline
-                  / writer.soft_line_break
-
-  parsers.EndlineNoSub = parsers.endline
-
-  parsers.NoSoftLineBreakEndline
-                        = parsers.newline
-                        * (parsers.check_minimal_indent
-                          * -V("NoSoftLineBreakEndlineExceptions")
-                          + parsers.check_optional_indent
-                          * -V("NoSoftLineBreakEndlineExceptions")
-                          * -parsers.starter)
-                        * parsers.spacechar^0
-                        / writer.space
-
-  parsers.EndlineBreak = parsers.backslash * parsers.Endline
-                                           / writer.hard_line_break
-
-  parsers.OptionalIndent
-                     = parsers.spacechar^1 / writer.space
-
-  parsers.Space      = parsers.spacechar^2 * parsers.Endline
-                                           / writer.hard_line_break
-                     + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / self.expandtabs
-                     + parsers.spacechar^1 * parsers.Endline
-                                           / writer.soft_line_break
-                     + parsers.spacechar^1 * -parsers.newline / self.expandtabs
-
-  parsers.NoSoftLineBreakSpace
-                     = parsers.spacechar^2 * parsers.Endline
-                                           / writer.hard_line_break
-                     + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / self.expandtabs
-                     + parsers.spacechar^1 * parsers.Endline
-                                           / writer.soft_line_break
-                     + parsers.spacechar^1 * -parsers.newline / self.expandtabs
-
-  parsers.NonbreakingEndline
-                    = parsers.endline
-                    / writer.soft_line_break
-
-  parsers.NonbreakingSpace
-                  = parsers.spacechar^2 * parsers.Endline
-                                        / writer.hard_line_break
-                  + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / ""
-                  + parsers.spacechar^1 * parsers.Endline
-                                        * parsers.optionalspace
-                                        / writer.soft_line_break
-                  + parsers.spacechar^1 * parsers.optionalspace
-                                        / writer.nbsp
-
-function self.auto_link_url(url, attributes)
-  return writer.link(writer.escape(url),
-                     url, nil, attributes)
-end
-function self.auto_link_email(email, attributes)
-  return writer.link(writer.escape(email),
-                     "mailto:"..email,
-                     nil, attributes)
-end
-
-  parsers.AutoLinkUrl = parsers.auto_link_url
-                      / self.auto_link_url
-
-  parsers.AutoLinkEmail
-                      = parsers.auto_link_email
-                      / self.auto_link_email
-
-  parsers.AutoLinkRelativeReference
-                      = parsers.auto_link_relative_reference
-                      / self.auto_link_url
-
-  parsers.LinkAndEmph = Ct(parsers.link_and_emph_table)
-                      / self.defer_link_and_emphasis_processing
-
-  parsers.EscapedChar   = parsers.backslash * C(parsers.escapable) / writer.string
-
-  parsers.InlineHtml    = Cs(parsers.html_inline_comment) / writer.inline_html_comment
-                        + Cs(parsers.html_any_empty_inline_tag
-                            + parsers.html_inline_instruction
-                            + parsers.html_inline_cdatasection
-                            + parsers.html_inline_declaration
-                            + parsers.html_any_open_inline_tag
-                            + parsers.html_any_close_tag)
-                          / writer.inline_html_tag
-
-  parsers.HtmlEntity    = parsers.html_entities / writer.string
-  parsers.DisplayHtml = Cs(parsers.check_trail
-                          * ( parsers.html_comment
-                            + parsers.html_special_block
-                            + parsers.html_block
-                            + parsers.html_any_block
-                            + parsers.html_instruction
-                            + parsers.html_cdatasection
-                            + parsers.html_declaration))
-                        / writer.block_html_element
-
-  parsers.indented_non_blank_line = parsers.indentedline - parsers.blankline
-
-  parsers.Verbatim  = Cs(
-                        parsers.check_code_trail
-                      * (parsers.line - parsers.blankline)
-                      * ((parsers.check_minimal_blank_indent_and_full_code_trail * parsers.blankline)^0
-                        * ((parsers.check_minimal_indent / "") * parsers.check_code_trail
-                           * (parsers.line - parsers.blankline))^1)^0
-                      ) / self.expandtabs / writer.verbatim
-
-  parsers.Blockquote   = parsers.blockquote_body
-                       / writer.blockquote
-
-  parsers.ThematicBreak = parsers.thematic_break_lines
-                        / writer.thematic_break
-
-  parsers.Reference    = parsers.define_reference_parser
-                       / self.register_link
-
-  parsers.Paragraph   = parsers.freeze_trail
-                      * (Ct((parsers.Inline)^1)
-                      * (parsers.newline + parsers.eof)
-                      * parsers.unfreeze_trail
-                      / writer.paragraph)
-
-  parsers.Plain        = parsers.nonindentspace * Ct(parsers.Inline^1)
-                       / writer.plain
-
-  if options.taskLists then
-    parsers.tickbox = ( parsers.ticked_box
-                      + parsers.halfticked_box
-                      + parsers.unticked_box
-                      ) / writer.tickbox
-  else
-     parsers.tickbox = parsers.fail
-  end
-
-  parsers.list_blank = parsers.conditionally_indented_blankline
-
-  parsers.ref_or_block_list_separated = parsers.sep_group_no_output(parsers.list_blank)
-                                      * parsers.minimally_indented_ref
-                                      + parsers.block_sep_group(parsers.list_blank)
-                                      * parsers.minimally_indented_block
-
-  parsers.ref_or_block_non_separated  = parsers.minimally_indented_ref
-                                      + (parsers.succeed / writer.interblocksep)
-                                      * parsers.minimally_indented_block
-                                      - parsers.minimally_indented_blankline
-
-  parsers.tight_list_loop_body_pair  =
-    parsers.create_loop_body_pair(parsers.ref_or_block_non_separated,
-                                  parsers.minimally_indented_par_or_plain_no_blank,
-                                  (parsers.succeed / writer.interblocksep),
-                                  (parsers.succeed / writer.paragraphsep))
-
-  parsers.loose_list_loop_body_pair  =
-    parsers.create_loop_body_pair(parsers.ref_or_block_list_separated,
-                                  parsers.minimally_indented_par_or_plain,
-                                  parsers.block_sep_group(parsers.list_blank),
-                                  parsers.par_sep_group(parsers.list_blank))
-
-  parsers.tight_list_content_loop = V("Block")
-                                  * parsers.tight_list_loop_body_pair.block^0
-                                  + (V("Paragraph") + V("Plain"))
-                                  * parsers.ref_or_block_non_separated
-                                  * parsers.tight_list_loop_body_pair.block^0
-                                  +  (V("Paragraph") + V("Plain"))
-                                  * parsers.tight_list_loop_body_pair.par^0
-
-  parsers.loose_list_content_loop = V("Block")
-                                  * parsers.loose_list_loop_body_pair.block^0
-                                  + (V("Paragraph") + V("Plain"))
-                                  * parsers.ref_or_block_list_separated
-                                  * parsers.loose_list_loop_body_pair.block^0
-                                  + (V("Paragraph") + V("Plain"))
-                                  * parsers.loose_list_loop_body_pair.par^0
-
-  parsers.list_item_tightness_condition = -#( parsers.list_blank^0
-                                            * parsers.minimally_indented_ref_or_block_or_par)
-                                        * remove_indent("li")
-                                        + remove_indent("li")
-                                        * parsers.fail
-
-  parsers.indented_content_tight  = Ct( (parsers.blankline / "")
-                                      * #parsers.list_blank
-                                      * remove_indent("li")
-                                      + ( (V("Reference") + (parsers.blankline / ""))
-                                        * parsers.check_minimal_indent
-                                        * parsers.tight_list_content_loop
-                                        + (V("Reference") + (parsers.blankline / ""))
-                                        + (parsers.tickbox^-1 / writer.escape)
-                                        * parsers.tight_list_content_loop
-                                        )
-                                      * parsers.list_item_tightness_condition
-                                  )
-
-  parsers.indented_content_loose  = Ct( (parsers.blankline / "")
-                                      * #parsers.list_blank
-                                      + ( (V("Reference") + (parsers.blankline / ""))
-                                        * parsers.check_minimal_indent
-                                        * parsers.loose_list_content_loop
-                                        + (V("Reference") + (parsers.blankline / ""))
-                                        + (parsers.tickbox^-1 / writer.escape)
-                                        * parsers.loose_list_content_loop
-                                        )
-                                  )
-
-  parsers.TightListItem = function(starter)
-    return  -parsers.ThematicBreak
-            * parsers.add_indent(starter, "li")
-            * parsers.indented_content_tight
-  end
-
-  parsers.LooseListItem = function(starter)
-    return  -parsers.ThematicBreak
-            * parsers.add_indent(starter, "li")
-            * parsers.indented_content_loose
-            * remove_indent("li")
-  end
-
-  parsers.BulletListOfType = function(bullet_type)
-    local bullet = parsers.bullet(bullet_type)
-    return  ( Ct( parsers.TightListItem(bullet)
-                * ( (parsers.check_minimal_indent / "")
-                  * parsers.TightListItem(bullet)
-                  )^0
-              )
-              * Cc(true)
-              * -#( (parsers.list_blank^0 / "")
-                  * parsers.check_minimal_indent
-                  * (bullet - parsers.ThematicBreak)
-              )
-              + Ct( parsers.LooseListItem(bullet)
-                  * ( (parsers.list_blank^0 / "")
-                    * (parsers.check_minimal_indent / "")
-                    * parsers.LooseListItem(bullet)
-                    )^0
-              )
-              * Cc(false)
-            ) / writer.bulletlist
-  end
-
-  parsers.BulletList = parsers.BulletListOfType(parsers.dash)
-                     + parsers.BulletListOfType(parsers.asterisk)
-                     + parsers.BulletListOfType(parsers.plus)
-
-  local function ordered_list(items,tight,starter)
-    local startnum = starter[2][1]
-    if options.startNumber then
-      startnum = tonumber(startnum) or 1  -- fallback for '#'
-      if startnum ~= nil then
-        startnum = math.floor(startnum)
-      end
+    local output
+    if options.eagerCache or options.finalizeCache then
+      local salt = util.salt(options)
+      local name = util.cache(options.cacheDir, input, salt, convert,
+                              ".md.tex")
+      output = [[\input{]] .. name .. [[}\relax]]
     else
-      startnum = nil
+      output = convert(input)
     end
-    return writer.orderedlist(items,tight,startnum)
-  end
-
-  parsers.OrderedListOfType = function(delimiter_type)
-    local enumerator = parsers.enumerator(delimiter_type)
-    return  Cg(enumerator, "listtype")
-          * (Ct( parsers.TightListItem(Cb("listtype"))
-               * ((parsers.check_minimal_indent / "") * parsers.TightListItem(enumerator))^0)
-          * Cc(true)
-          * -#((parsers.list_blank^0 / "")
-              * parsers.check_minimal_indent * enumerator)
-          + Ct( parsers.LooseListItem(Cb("listtype"))
-              * ((parsers.list_blank^0 / "")
-                * (parsers.check_minimal_indent / "") * parsers.LooseListItem(enumerator))^0)
-          * Cc(false)
-          ) * Ct(Cb("listtype")) / ordered_list
-  end
-
-  parsers.OrderedList = parsers.OrderedListOfType(parsers.period)
-                      + parsers.OrderedListOfType(parsers.rparent)
-  parsers.Blank        = parsers.blankline / ""
-                       + V("Reference")
-  function parsers.parse_heading_text(s)
-    local inlines = self.parser_functions.parse_inlines(s)
-    local flatten_inlines = self.writer.flatten_inlines
-    self.writer.flatten_inlines = true
-    local flat_text = self.parser_functions.parse_inlines(s)
-    flat_text = util.rope_to_string(flat_text)
-    self.writer.flatten_inlines = flatten_inlines
-    return {flat_text, inlines}
-  end
-
-  -- parse atx header
-  parsers.AtxHeading = parsers.check_trail_no_rem
-                     * Cg(parsers.heading_start, "level")
-                     * ((C( parsers.optionalspace
-                          * parsers.hash^0
-                          * parsers.optionalspace
-                          * parsers.newline)
-                        + parsers.spacechar^1
-                        * C(parsers.line))
-                       / strip_atx_end
-                       / parsers.parse_heading_text)
-                     * Cb("level")
-                     / writer.heading
-
-  parsers.heading_line  = parsers.linechar^1
-                        - parsers.thematic_break_lines
-
-  parsers.heading_text = parsers.heading_line
-                       * ((V("Endline") / "\n") * (parsers.heading_line - parsers.heading_level))^0
-                       * parsers.newline^-1
-
-  parsers.SetextHeading = parsers.freeze_trail * parsers.check_trail_no_rem
-                        * #(parsers.heading_text
-                           * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level)
-                        * Cs(parsers.heading_text)
-                        / parsers.parse_heading_text
-                        * parsers.check_minimal_indent_and_trail * parsers.heading_level
-                        * parsers.newline
-                        * parsers.unfreeze_trail
-                        / writer.heading
-
-  parsers.Heading = parsers.AtxHeading + parsers.SetextHeading
-  function self.finalize_grammar(extensions)
-    local walkable_syntax = (function(global_walkable_syntax)
-      local local_walkable_syntax = {}
-      for lhs, rule in pairs(global_walkable_syntax) do
-        local_walkable_syntax[lhs] = util.table_copy(rule)
-      end
-      return local_walkable_syntax
-    end)(walkable_syntax)
-    local current_extension_name = nil
-    self.insert_pattern = function(selector, pattern, pattern_name)
-      assert(pattern_name == nil or type(pattern_name) == "string")
-      local _, _, lhs, pos, rhs = selector:find("^(%a+)%s+([%a%s]+%a+)%s+(%a+)$")
-      assert(lhs ~= nil,
-        [[Expected selector in form "LHS (before|after|instead of) RHS", not "]]
-        .. selector .. [["]])
-      assert(walkable_syntax[lhs] ~= nil,
-        [[Rule ]] .. lhs .. [[ -> ... does not exist in markdown grammar]])
-      assert(pos == "before" or pos == "after" or pos == "instead of",
-        [[Expected positional specifier "before", "after", or "instead of", not "]]
-        .. pos .. [["]])
-      local rule = walkable_syntax[lhs]
-      local index = nil
-      for current_index, current_rhs in ipairs(rule) do
-        if type(current_rhs) == "string" and current_rhs == rhs then
-          index = current_index
-          if pos == "after" then
-            index = index + 1
-          end
-          break
-        end
-      end
-      assert(index ~= nil,
-        [[Rule ]] .. lhs .. [[ -> ]] .. rhs
-          .. [[ does not exist in markdown grammar]])
-      local accountable_pattern
-      if current_extension_name then
-        accountable_pattern = { pattern, current_extension_name, pattern_name }
+    if options.finalizeCache then
+      local file, mode
+      if options.frozenCacheCounter > 0 then
+        mode = "a"
       else
-        assert(type(pattern) == "string",
-          [[reader->insert_pattern() was called outside an extension with ]]
-          .. [[a PEG pattern instead of a rule name]])
-        accountable_pattern = pattern
+        mode = "w"
       end
-      if pos == "instead of" then
-        rule[index] = accountable_pattern
-      else
-        table.insert(rule, index, accountable_pattern)
-      end
-    end
-    local syntax =
-      { "Blocks",
-
-        Blocks                = V("InitializeState")
-                              * ( V("ExpectedJekyllData")
-                                * (V("Blank")^0 / writer.interblocksep)
-                                )^-1
-                              * V("Blank")^0
-                              * ( V("Block")
-                                * ( V("Blank")^0 * parsers.eof
-                                  + ( V("Blank")^2 / writer.paragraphsep
-                                    + V("Blank")^0 / writer.interblocksep
-                                    )
-                                  )
-                                + ( V("Paragraph") + V("Plain") )
-                                * ( V("Blank")^0 * parsers.eof
-                                  + ( V("Blank")^2 / writer.paragraphsep
-                                    + V("Blank")^0 / writer.interblocksep
-                                    )
-                                  )
-                                * V("Block")
-                                * ( V("Blank")^0 * parsers.eof
-                                  + ( V("Blank")^2 / writer.paragraphsep
-                                    + V("Blank")^0 / writer.interblocksep
-                                    )
-                                  )
-                                + ( V("Paragraph") + V("Plain") )
-                                * ( V("Blank")^0 * parsers.eof
-                                  + V("Blank")^0 / writer.paragraphsep
-                                  )
-                                )^0,
-
-        ExpectedJekyllData    = parsers.fail,
-
-        Blank                 = parsers.Blank,
-        Reference             = parsers.Reference,
-
-        Blockquote            = parsers.Blockquote,
-        Verbatim              = parsers.Verbatim,
-        ThematicBreak         = parsers.ThematicBreak,
-        BulletList            = parsers.BulletList,
-        OrderedList           = parsers.OrderedList,
-        DisplayHtml           = parsers.DisplayHtml,
-        Heading               = parsers.Heading,
-        Paragraph             = parsers.Paragraph,
-        Plain                 = parsers.Plain,
-
-        EndlineExceptions     = parsers.EndlineExceptions,
-        NoSoftLineBreakEndlineExceptions
-                              = parsers.NoSoftLineBreakEndlineExceptions,
-
-        Str                   = parsers.Str,
-        Space                 = parsers.Space,
-        NoSoftLineBreakSpace  = parsers.NoSoftLineBreakSpace,
-        OptionalIndent        = parsers.OptionalIndent,
-        Endline               = parsers.Endline,
-        EndlineNoSub          = parsers.EndlineNoSub,
-        NoSoftLineBreakEndline
-                              = parsers.NoSoftLineBreakEndline,
-        EndlineBreak          = parsers.EndlineBreak,
-        LinkAndEmph           = parsers.LinkAndEmph,
-        Code                  = parsers.Code,
-        AutoLinkUrl           = parsers.AutoLinkUrl,
-        AutoLinkEmail         = parsers.AutoLinkEmail,
-        AutoLinkRelativeReference
-                              = parsers.AutoLinkRelativeReference,
-        InlineHtml            = parsers.InlineHtml,
-        HtmlEntity            = parsers.HtmlEntity,
-        EscapedChar           = parsers.EscapedChar,
-        Smart                 = parsers.Smart,
-        Symbol                = parsers.Symbol,
-        SpecialChar           = parsers.fail,
-        InitializeState       = parsers.succeed,
-      }
-    self.update_rule = function(rule_name, get_pattern)
-      assert(current_extension_name ~= nil)
-      assert(syntax[rule_name] ~= nil,
-        [[Rule ]] .. rule_name .. [[ -> ... does not exist in markdown grammar]])
-      local previous_pattern
-      local extension_name
-      if walkable_syntax[rule_name] then
-        local previous_accountable_pattern = walkable_syntax[rule_name][1]
-        previous_pattern = previous_accountable_pattern[1]
-        extension_name = previous_accountable_pattern[2] .. ", " .. current_extension_name
-      else
-        previous_pattern = nil
-        extension_name = current_extension_name
-      end
-      local pattern
-      if type(get_pattern) == "function" then
-        pattern = get_pattern(previous_pattern)
-      else
-        assert(previous_pattern == nil,
-               [[Rule ]] .. rule_name ..
-               [[ has already been updated by ]] .. extension_name)
-        pattern = get_pattern
-      end
-      local accountable_pattern = { pattern, extension_name, rule_name }
-      walkable_syntax[rule_name] = { accountable_pattern }
-    end
-    local special_characters = {}
-    self.add_special_character = function(c)
-      table.insert(special_characters, c)
-      syntax.SpecialChar = S(table.concat(special_characters, ""))
-    end
-
-    self.add_special_character("*")
-    self.add_special_character("[")
-    self.add_special_character("]")
-    self.add_special_character("<")
-    self.add_special_character("!")
-    self.add_special_character("\\")
-    self.initialize_named_group = function(name, value)
-      local pattern = Ct("")
-      if value ~= nil then
-        pattern = pattern / value
-      end
-      syntax.InitializeState = syntax.InitializeState
-                             * Cg(pattern, name)
-    end
-    self.initialize_named_group("indent_info")
-    for _, extension in ipairs(extensions) do
-      current_extension_name = extension.name
-      extension.extend_writer(writer)
-      extension.extend_reader(self)
-    end
-    current_extension_name = nil
-    if options.debugExtensions then
-      local sorted_lhs = {}
-      for lhs, _ in pairs(walkable_syntax) do
-        table.insert(sorted_lhs, lhs)
-      end
-      table.sort(sorted_lhs)
-
-      local output_lines = {"{"}
-      for lhs_index, lhs in ipairs(sorted_lhs) do
-        local encoded_lhs = util.encode_json_string(lhs)
-        table.insert(output_lines, [[    ]] ..encoded_lhs .. [[: []])
-        local rule = walkable_syntax[lhs]
-        for rhs_index, rhs in ipairs(rule) do
-          local human_readable_rhs
-          if type(rhs) == "string" then
-            human_readable_rhs = rhs
-          else
-            local pattern_name
-            if rhs[3] then
-              pattern_name = rhs[3]
-            else
-              pattern_name = "Anonymous Pattern"
-            end
-            local extension_name = rhs[2]
-            human_readable_rhs = pattern_name .. [[ (]] .. extension_name .. [[)]]
-          end
-          local encoded_rhs = util.encode_json_string(human_readable_rhs)
-          local output_line = [[        ]] .. encoded_rhs
-          if rhs_index < #rule then
-            output_line = output_line .. ","
-          end
-          table.insert(output_lines, output_line)
-        end
-        local output_line = "    ]"
-        if lhs_index < #sorted_lhs then
-          output_line = output_line .. ","
-        end
-        table.insert(output_lines, output_line)
-      end
-      table.insert(output_lines, "}")
-
-      local output = table.concat(output_lines, "\n")
-      local output_filename = options.debugExtensionsFileName
-      local output_file = assert(io.open(output_filename, "w"),
-        [[Could not open file "]] .. output_filename .. [[" for writing]])
-      assert(output_file:write(output))
-      assert(output_file:close())
-    end
-    for lhs, rule in pairs(walkable_syntax) do
-      syntax[lhs] = parsers.fail
-      for _, rhs in ipairs(rule) do
-        local pattern
-        if type(rhs) == "string" then
-          pattern = V(rhs)
-        else
-          pattern = rhs[1]
-          if type(pattern) == "string" then
-            pattern = V(pattern)
-          end
-        end
-        syntax[lhs] = syntax[lhs] + pattern
-      end
-    end
-    if options.underscores then
-      self.add_special_character("_")
-    end
-
-    if not options.codeSpans then
-      syntax.Code = parsers.fail
-    else
-      self.add_special_character("`")
-    end
-
-    if not options.html then
-      syntax.DisplayHtml = parsers.fail
-      syntax.InlineHtml = parsers.fail
-      syntax.HtmlEntity  = parsers.fail
-    else
-      self.add_special_character("&")
-    end
-
-    if options.preserveTabs then
-      options.stripIndent = false
-    end
-
-    if not options.smartEllipses then
-      syntax.Smart = parsers.fail
-    else
-      self.add_special_character(".")
-    end
-
-    if not options.relativeReferences then
-      syntax.AutoLinkRelativeReference = parsers.fail
-    end
-
-    if options.contentLevel == "inline" then
-      syntax[1] = "Inlines"
-      syntax.Inlines = V("InitializeState")
-                      * parsers.Inline^0
-                      * ( parsers.spacing^0
-                        * parsers.eof / "")
-      syntax.Space = parsers.Space + parsers.blankline / writer.space
-    end
-
-    local blocks_nested_t = util.table_copy(syntax)
-    blocks_nested_t.ExpectedJekyllData = parsers.fail
-    parsers.blocks_nested = Ct(blocks_nested_t)
-
-    parsers.blocks = Ct(syntax)
-
-    local inlines_t = util.table_copy(syntax)
-    inlines_t[1] = "Inlines"
-    inlines_t.Inlines = V("InitializeState")
-                      * parsers.Inline^0
-                      * ( parsers.spacing^0
-                        * parsers.eof / "")
-    parsers.inlines = Ct(inlines_t)
-
-    local inlines_no_inline_note_t = util.table_copy(inlines_t)
-    inlines_no_inline_note_t.InlineNote = parsers.fail
-    parsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t)
-
-    local inlines_no_html_t = util.table_copy(inlines_t)
-    inlines_no_html_t.DisplayHtml = parsers.fail
-    inlines_no_html_t.InlineHtml = parsers.fail
-    inlines_no_html_t.HtmlEntity = parsers.fail
-    parsers.inlines_no_html = Ct(inlines_no_html_t)
-
-    local inlines_nbsp_t = util.table_copy(inlines_t)
-    inlines_nbsp_t.Endline = parsers.NonbreakingEndline
-    inlines_nbsp_t.Space = parsers.NonbreakingSpace
-    parsers.inlines_nbsp = Ct(inlines_nbsp_t)
-
-    local inlines_no_link_or_emphasis_t = util.table_copy(inlines_t)
-    inlines_no_link_or_emphasis_t.LinkAndEmph = parsers.fail
-    inlines_no_link_or_emphasis_t.EndlineExceptions = parsers.EndlineExceptions - parsers.eof
-    parsers.inlines_no_link_or_emphasis = Ct(inlines_no_link_or_emphasis_t)
-    return function(input)
-      if options.unicodeNormalization then
-        local form = options.unicodeNormalizationForm
-        if form == "nfc" then
-          input = uni_algos.normalize.NFC(input)
-        elseif form == "nfd" then
-          input = uni_algos.normalize.NFD(input)
-        elseif form == "nfkc" then
-          input = uni_algos.normalize.NFKC(input)
-        elseif form == "nfkd" then
-          input = uni_algos.normalize.NFKD(input)
-        else
-          error(format("Unknown normalization form %s", form))
-        end
-      end
-      input = input:gsub("\r\n?", "\n")
-      if input:sub(-1) ~= "\n" then
-        input = input .. "\n"
-      end
-      references = {}
-      local opt_string = {}
-      for k, _ in pairs(defaultOptions) do
-        local v = options[k]
-        if type(v) == "table" then
-          for _, i in ipairs(v) do
-            opt_string[#opt_string+1] = k .. "=" .. tostring(i)
-          end
-        elseif k ~= "cacheDir" then
-          opt_string[#opt_string+1] = k .. "=" .. tostring(v)
-        end
-      end
-      table.sort(opt_string)
-      local salt = table.concat(opt_string, ",") .. "," .. metadata.version
-      local output
-      local function convert(input)
-        local document = self.parser_functions.parse_blocks(input)
-        local output = util.rope_to_string(writer.document(document))
-        local undosep_start, undosep_end
-        local potential_secend_start, secend_start
-        local potential_sep_start, sep_start
-        while true do
-          -- find a `writer->undosep`
-          undosep_start, undosep_end = output:find(writer.undosep_text, 1, true)
-          if undosep_start == nil then break end
-          -- skip any preceding section ends
-          secend_start = undosep_start
-          while true do
-            potential_secend_start = secend_start - #writer.secend_text
-            if potential_secend_start < 1
-               or output:sub(potential_secend_start, secend_start - 1) ~= writer.secend_text then
-              break
-            end
-            secend_start = potential_secend_start
-          end
-          -- find an immediately preceding block element / paragraph separator
-          sep_start = secend_start
-          potential_sep_start = sep_start - #writer.interblocksep_text
-          if potential_sep_start >= 1
-             and output:sub(potential_sep_start, sep_start - 1) == writer.interblocksep_text then
-            sep_start = potential_sep_start
-          else
-            potential_sep_start = sep_start - #writer.paragraphsep_text
-            if potential_sep_start >= 1
-               and output:sub(potential_sep_start, sep_start - 1) == writer.paragraphsep_text then
-              sep_start = potential_sep_start
-            end
-          end
-          -- remove `writer->undosep` and immediately preceding block element / paragraph separator
-          output = output:sub(1, sep_start - 1)
-                .. output:sub(secend_start, undosep_start - 1)
-                .. output:sub(undosep_end + 1)
-        end
-        return output
-      end
-      if options.eagerCache or options.finalizeCache then
-        local name = util.cache(options.cacheDir, input, salt, convert,
-                                ".md" .. writer.suffix)
-        output = writer.pack(name)
-      else
-        output = convert(input)
-      end
-      if options.finalizeCache then
-        local file, mode
-        if options.frozenCacheCounter > 0 then
-          mode = "a"
-        else
-          mode = "w"
-        end
-        file = assert(io.open(options.frozenCacheFileName, mode),
-          [[Could not open file "]] .. options.frozenCacheFileName
-          .. [[" for writing]])
-        assert(file:write([[\expandafter\global\expandafter\def\csname ]]
-          .. [[markdownFrozenCache]] .. options.frozenCacheCounter
-          .. [[\endcsname{]] .. output .. [[}]] .. "\n"))
-        assert(file:close())
-      end
-      return output
-    end
-  end
-  return self
-end
-M.extensions = {}
-M.extensions.bracketed_spans = function()
-  return {
-    name = "built-in bracketed_spans syntax extension",
-    extend_writer = function(self)
-      function self.span(s, attr)
-        if self.flatten_inlines then return s end
-        return {"\\markdownRendererBracketedSpanAttributeContextBegin",
-                self.attributes(attr),
-                s,
-                "\\markdownRendererBracketedSpanAttributeContextEnd{}"}
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local span_label  = parsers.lbracket
-                          * (Cs((parsers.alphanumeric^1
-                              + parsers.inticks
-                              + parsers.autolink
-                              + V("InlineHtml")
-                              + ( parsers.backslash * parsers.backslash)
-                              + ( parsers.backslash * (parsers.lbracket + parsers.rbracket)
-                                + V("Space") + V("Endline")
-                                + (parsers.any
-                                  - (parsers.newline + parsers.lbracket + parsers.rbracket
-                                    + parsers.blankline^2))))^1)
-                          / self.parser_functions.parse_inlines)
-                          * parsers.rbracket
-
-      local Span = span_label
-                 * Ct(parsers.attributes)
-                 / writer.span
-
-      self.insert_pattern("Inline before LinkAndEmph",
-                          Span, "Span")
-    end
-  }
-end
-M.extensions.citations = function(citation_nbsps)
-  return {
-    name = "built-in citations syntax extension",
-    extend_writer = function(self)
-      function self.citations(text_cites, cites)
-        local buffer = {}
-        if self.flatten_inlines then
-          for _,cite in ipairs(cites) do
-            if cite.prenote then
-              table.insert(buffer, {cite.prenote, " "})
-            end
-            table.insert(buffer, cite.name)
-            if cite.postnote then
-              table.insert(buffer, {" ", cite.postnote})
-            end
-          end
-        else
-          table.insert(buffer, {"\\markdownRenderer", text_cites and "TextCite" or "Cite",
-            "{", #cites, "}"})
-          for _,cite in ipairs(cites) do
-            table.insert(buffer, {cite.suppress_author and "-" or "+", "{",
-              cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"})
-          end
-        end
-        return buffer
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local citation_chars
-                    = parsers.alphanumeric
-                    + S("#$%&-+<>~/_")
-
-      local citation_name
-                    = Cs(parsers.dash^-1) * parsers.at
-                    * Cs(citation_chars
-                        * (((citation_chars + parsers.internal_punctuation
-                            - parsers.comma - parsers.semicolon)
-                           * -#((parsers.internal_punctuation - parsers.comma
-                                - parsers.semicolon)^0
-                               * -(citation_chars + parsers.internal_punctuation
-                                  - parsers.comma - parsers.semicolon)))^0
-                          * citation_chars)^-1)
-
-      local citation_body_prenote
-                    = Cs((parsers.alphanumeric^1
-                         + parsers.bracketed
-                         + parsers.inticks
-                         + parsers.autolink
-                         + V("InlineHtml")
-                         + V("Space") + V("Endline")
-                         + (parsers.anyescaped
-                           - (parsers.newline + parsers.rbracket + parsers.blankline^2))
-                         - (parsers.spnl * parsers.dash^-1 * parsers.at))^1)
-
-      local citation_body_postnote
-                    = Cs((parsers.alphanumeric^1
-                         + parsers.bracketed
-                         + parsers.inticks
-                         + parsers.autolink
-                         + V("InlineHtml")
-                         + V("Space") + V("Endline")
-                         + (parsers.anyescaped
-                           - (parsers.newline + parsers.rbracket + parsers.semicolon
-                             + parsers.blankline^2))
-                         - (parsers.spnl * parsers.rbracket))^1)
-
-      local citation_body_chunk
-                    = ( citation_body_prenote
-                      * parsers.spnlc_sep
-                      + Cc("")
-                      * parsers.spnlc
-                    )
-                    * citation_name
-                    * (parsers.internal_punctuation - parsers.semicolon)^-1
-                    * ( parsers.spnlc / function(_) return end
-                      * citation_body_postnote
-                      + Cc("")
-                      * parsers.spnlc
-                    )
-
-      local citation_body
-                    = citation_body_chunk
-                    * ( parsers.semicolon
-                      * parsers.spnlc
-                      * citation_body_chunk
-                    )^0
-
-      local citation_headless_body_postnote
-                    = Cs((parsers.alphanumeric^1
-                         + parsers.bracketed
-                         + parsers.inticks
-                         + parsers.autolink
-                         + V("InlineHtml")
-                         + V("Space") + V("Endline")
-                         + (parsers.anyescaped
-                           - (parsers.newline + parsers.rbracket + parsers.at
-                             + parsers.semicolon + parsers.blankline^2))
-                         - (parsers.spnl * parsers.rbracket))^0)
-
-      local citation_headless_body
-                    = citation_headless_body_postnote
-                    * ( parsers.semicolon
-                      * parsers.spnlc
-                      * citation_body_chunk
-                    )^0
-
-      local citations
-                    = function(text_cites, raw_cites)
-          local function normalize(str)
-              if str == "" then
-                  str = nil
-              else
-                  str = (citation_nbsps and
-                    self.parser_functions.parse_inlines_nbsp or
-                    self.parser_functions.parse_inlines)(str)
-              end
-              return str
-          end
-
-          local cites = {}
-          for i = 1,#raw_cites,4 do
-              cites[#cites+1] = {
-                  prenote = normalize(raw_cites[i]),
-                  suppress_author = raw_cites[i+1] == "-",
-                  name = writer.identifier(raw_cites[i+2]),
-                  postnote = normalize(raw_cites[i+3]),
-              }
-          end
-          return writer.citations(text_cites, cites)
-      end
-
-      local TextCitations
-                    = Ct((parsers.spnlc
-                    * Cc("")
-                    * citation_name
-                    * ((parsers.spnlc
-                        * parsers.lbracket
-                        * citation_headless_body
-                        * parsers.rbracket) + Cc("")))^1)
-                    / function(raw_cites)
-                        return citations(true, raw_cites)
-                      end
-
-      local ParenthesizedCitations
-                    = Ct((parsers.spnlc
-                    * parsers.lbracket
-                    * citation_body
-                    * parsers.rbracket)^1)
-                    / function(raw_cites)
-                        return citations(false, raw_cites)
-                      end
-
-      local Citations = TextCitations + ParenthesizedCitations
-
-      self.insert_pattern("Inline before LinkAndEmph",
-                          Citations, "Citations")
-
-      self.add_special_character("@")
-      self.add_special_character("-")
-    end
-  }
-end
-M.extensions.content_blocks = function(language_map)
-  local languages_json = (function()
-    local base, prev, curr
-    for _, pathname in ipairs{kpse.lookup(language_map, { all=true })} do
-      local file = io.open(pathname, "r")
-      if not file then goto continue end
-      local input = assert(file:read("*a"))
+      file = assert(io.open(options.frozenCacheFileName, mode),
+        [[Could not open file "]] .. options.frozenCacheFileName
+        .. [[" for writing]])
+      assert(file:write(
+        [[\expandafter\global\expandafter\def\csname ]]
+        .. [[markdownFrozenCache]] .. options.frozenCacheCounter
+        .. [[\endcsname{]] .. output .. [[}]] .. "\n"))
       assert(file:close())
-      local json = input:gsub('("[^\n]-"):','[%1]=')
-      curr = load("_ENV = {}; return "..json)()
-      if type(curr) == "table" then
-        if base == nil then
-          base = curr
-        else
-          setmetatable(prev, { __index = curr })
-        end
-        prev = curr
-      end
-      ::continue::
     end
-    return base or {}
-  end)()
-
-  return {
-    name = "built-in content_blocks syntax extension",
-    extend_writer = function(self)
-      function self.contentblock(src,suf,type,tit)
-        if not self.is_writing then return "" end
-        src = src.."."..suf
-        suf = suf:lower()
-        if type == "onlineimage" then
-          return {"\\markdownRendererContentBlockOnlineImage{",suf,"}",
-                                 "{",self.string(src),"}",
-                                 "{",self.uri(src),"}",
-                                 "{",self.string(tit or ""),"}"}
-        elseif languages_json[suf] then
-          return {"\\markdownRendererContentBlockCode{",suf,"}",
-                                 "{",self.string(languages_json[suf]),"}",
-                                 "{",self.string(src),"}",
-                                 "{",self.uri(src),"}",
-                                 "{",self.string(tit or ""),"}"}
-        else
-          return {"\\markdownRendererContentBlock{",suf,"}",
-                                 "{",self.string(src),"}",
-                                 "{",self.uri(src),"}",
-                                 "{",self.string(tit or ""),"}"}
-        end
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local contentblock_tail
-                    = parsers.optionaltitle
-                    * (parsers.newline + parsers.eof)
-
-      -- case insensitive online image suffix:
-      local onlineimagesuffix
-                    = (function(...)
-                        local parser = nil
-                        for _, suffix in ipairs({...}) do
-                          local pattern=nil
-                          for i=1,#suffix do
-                            local char=suffix:sub(i,i)
-                            char = S(char:lower()..char:upper())
-                            if pattern == nil then
-                              pattern = char
-                            else
-                              pattern = pattern * char
-                            end
-                          end
-                          if parser == nil then
-                            parser = pattern
-                          else
-                            parser = parser + pattern
-                          end
-                        end
-                        return parser
-                      end)("png", "jpg", "jpeg", "gif", "tif", "tiff")
-
-      -- online image url for iA Writer content blocks with mandatory suffix,
-      -- allowing nested brackets:
-      local onlineimageurl
-                    = (parsers.less
-                      * Cs((parsers.anyescaped
-                           - parsers.more
-                           - parsers.spacing
-                           - #(parsers.period
-                              * onlineimagesuffix
-                              * parsers.more
-                              * contentblock_tail))^0)
-                      * parsers.period
-                      * Cs(onlineimagesuffix)
-                      * parsers.more
-                      + (Cs((parsers.inparens
-                            + (parsers.anyescaped
-                              - parsers.spacing
-                              - parsers.rparent
-                              - #(parsers.period
-                                 * onlineimagesuffix
-                                 * contentblock_tail)))^0)
-                        * parsers.period
-                        * Cs(onlineimagesuffix))
-                      ) * Cc("onlineimage")
-
-      -- filename for iA Writer content blocks with mandatory suffix:
-      local localfilepath
-                    = parsers.slash
-                    * Cs((parsers.anyescaped
-                         - parsers.tab
-                         - parsers.newline
-                         - #(parsers.period
-                            * parsers.alphanumeric^1
-                            * contentblock_tail))^1)
-                    * parsers.period
-                    * Cs(parsers.alphanumeric^1)
-                    * Cc("localfile")
-
-      local ContentBlock
-                    = parsers.check_trail_no_rem
-                    * (localfilepath + onlineimageurl)
-                    * contentblock_tail
-                    / writer.contentblock
-
-      self.insert_pattern("Block before Blockquote",
-                          ContentBlock, "ContentBlock")
-    end
-  }
-end
-M.extensions.definition_lists = function(tight_lists)
-  return {
-    name = "built-in definition_lists syntax extension",
-    extend_writer = function(self)
-      local function dlitem(term, defs)
-        local retVal = {"\\markdownRendererDlItem{",term,"}"}
-        for _, def in ipairs(defs) do
-          retVal[#retVal+1] = {"\\markdownRendererDlDefinitionBegin ",def,
-                               "\\markdownRendererDlDefinitionEnd "}
-        end
-        retVal[#retVal+1] = "\\markdownRendererDlItemEnd "
-        return retVal
-      end
-
-      function self.definitionlist(items,tight)
-        if not self.is_writing then return "" end
-        local buffer = {}
-        for _,item in ipairs(items) do
-          buffer[#buffer + 1] = dlitem(item.term, item.definitions)
-        end
-        if tight and tight_lists then
-          return {"\\markdownRendererDlBeginTight\n", buffer,
-            "\n\\markdownRendererDlEndTight"}
-        else
-          return {"\\markdownRendererDlBegin\n", buffer,
-            "\n\\markdownRendererDlEnd"}
-        end
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local defstartchar = S("~:")
-
-      local defstart  = parsers.check_trail_length(0) * defstartchar * #parsers.spacing
-                                      * (parsers.tab + parsers.space^-3)
-                      + parsers.check_trail_length(1) * defstartchar * #parsers.spacing
-                                       * (parsers.tab + parsers.space^-2)
-                      + parsers.check_trail_length(2) * defstartchar * #parsers.spacing
-                                       * (parsers.tab + parsers.space^-1)
-                      + parsers.check_trail_length(3) * defstartchar * #parsers.spacing
-
-      local indented_line = (parsers.check_minimal_indent / "") * parsers.check_code_trail * parsers.line
-
-      local blank = parsers.check_minimal_blank_indent_and_any_trail * parsers.optionalspace * parsers.newline
-
-      local dlchunk = Cs(parsers.line * (indented_line - blank)^0)
-
-      local indented_blocks = function(bl)
-        return Cs( bl
-              * (blank^1 * (parsers.check_minimal_indent / "")
-                * parsers.check_code_trail * -parsers.blankline * bl)^0
-              * (blank^1 + parsers.eof))
-      end
-
-      local function definition_list_item(term, defs, _)
-        return { term = self.parser_functions.parse_inlines(term),
-                 definitions = defs }
-      end
-
-      local DefinitionListItemLoose
-                    = C(parsers.line) * blank^0
-                    * Ct((parsers.check_minimal_indent * (defstart
-                         * indented_blocks(dlchunk)
-                         / self.parser_functions.parse_blocks_nested))^1)
-                    * Cc(false) / definition_list_item
-
-      local DefinitionListItemTight
-                    = C(parsers.line)
-                    * Ct((parsers.check_minimal_indent * (defstart * dlchunk
-                         / self.parser_functions.parse_blocks_nested))^1)
-                    * Cc(true) / definition_list_item
-
-      local DefinitionList
-                    = ( Ct(DefinitionListItemLoose^1) * Cc(false)
-                      + Ct(DefinitionListItemTight^1)
-                      * (blank^0
-                        * -DefinitionListItemLoose * Cc(true))
-                      ) / writer.definitionlist
-
-      self.insert_pattern("Block after Heading",
-                          DefinitionList, "DefinitionList")
-    end
-  }
-end
-M.extensions.fancy_lists = function()
-  return {
-    name = "built-in fancy_lists syntax extension",
-    extend_writer = function(self)
-      local options = self.options
-
-      function self.fancylist(items,tight,startnum,numstyle,numdelim)
-        if not self.is_writing then return "" end
-        local buffer = {}
-        local num = startnum
-        for _,item in ipairs(items) do
-          if item ~= "" then
-            buffer[#buffer + 1] = self.fancyitem(item,num)
-          end
-          if num ~= nil and item ~= "" then
-            num = num + 1
-          end
-        end
-        local contents = util.intersperse(buffer,"\n")
-        if tight and options.tightLists then
-          return {"\\markdownRendererFancyOlBeginTight{",
-                  numstyle,"}{",numdelim,"}",contents,
-                  "\n\\markdownRendererFancyOlEndTight "}
-        else
-          return {"\\markdownRendererFancyOlBegin{",
-                  numstyle,"}{",numdelim,"}",contents,
-                  "\n\\markdownRendererFancyOlEnd "}
-        end
-      end
-      function self.fancyitem(s,num)
-        if num ~= nil then
-          return {"\\markdownRendererFancyOlItemWithNumber{",num,"}",s,
-                  "\\markdownRendererFancyOlItemEnd "}
-        else
-          return {"\\markdownRendererFancyOlItem ",s,"\\markdownRendererFancyOlItemEnd "}
-        end
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local options = self.options
-      local writer = self.writer
-
-      local function combine_markers_and_delims(markers, delims)
-        local markers_table = {}
-        for _,marker in ipairs(markers) do
-          local start_marker
-          local continuation_marker
-          if type(marker) == "table" then
-            start_marker = marker[1]
-            continuation_marker = marker[2]
-          else
-            start_marker = marker
-            continuation_marker = marker
-          end
-          for _,delim in ipairs(delims) do
-            table.insert(markers_table, {start_marker, continuation_marker, delim})
-          end
-        end
-        return markers_table
-      end
-
-      local function join_table_with_func(func, markers_table)
-        local pattern = func(table.unpack(markers_table[1]))
-        for i = 2, #markers_table do
-          pattern = pattern + func(table.unpack(markers_table[i]))
-        end
-        return pattern
-      end
-
-      local lowercase_letter_marker = R("az")
-      local uppercase_letter_marker = R("AZ")
-
-      local roman_marker = function(chars)
-        local m, d, c = P(chars[1]), P(chars[2]), P(chars[3])
-        local l, x, v, i = P(chars[4]), P(chars[5]), P(chars[6]), P(chars[7])
-        return  m^-3
-                * (c*m + c*d + d^-1 * c^-3)
-                * (x*c + x*l + l^-1 * x^-3)
-                * (i*x + i*v + v^-1 * i^-3)
-      end
-
-      local lowercase_roman_marker  = roman_marker({"m", "d", "c", "l", "x", "v", "i"})
-      local uppercase_roman_marker  = roman_marker({"M", "D", "C", "L", "X", "V", "I"})
-
-      local lowercase_opening_roman_marker  = P("i")
-      local uppercase_opening_roman_marker  = P("I")
-
-      local digit_marker = parsers.dig * parsers.dig^-8
-
-      local markers = {
-        {lowercase_opening_roman_marker, lowercase_roman_marker},
-        {uppercase_opening_roman_marker, uppercase_roman_marker},
-        lowercase_letter_marker,
-        uppercase_letter_marker,
-        lowercase_roman_marker,
-        uppercase_roman_marker,
-        digit_marker
-      }
-
-      local delims = {
-        parsers.period,
-        parsers.rparent
-      }
-
-      local markers_table = combine_markers_and_delims(markers, delims)
-
-      local function enumerator(start_marker, _, delimiter_type, interrupting)
-        local delimiter_range
-        local allowed_end
-        if interrupting then
-          delimiter_range = P("1")
-          allowed_end = C(parsers.spacechar^1) * #parsers.linechar
-        else
-          delimiter_range = start_marker
-          allowed_end = C(parsers.spacechar^1) + #(parsers.newline + parsers.eof)
-        end
-
-        return parsers.check_trail
-                * Ct(C(delimiter_range) * C(delimiter_type))
-                * allowed_end
-      end
-
-      local starter = join_table_with_func(enumerator, markers_table)
-
-      local TightListItem = function(starter)
-        return  parsers.add_indent(starter, "li")
-                * parsers.indented_content_tight
-      end
-
-      local LooseListItem = function(starter)
-        return  parsers.add_indent(starter, "li")
-                * parsers.indented_content_loose
-                * remove_indent("li")
-      end
-
-      local function roman2number(roman)
-        local romans = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
-        local numeral = 0
-
-        local i = 1
-        local len = string.len(roman)
-        while i < len do
-          local z1, z2 = romans[ string.sub(roman, i, i) ], romans[ string.sub(roman, i+1, i+1) ]
-          if z1 < z2 then
-              numeral = numeral + (z2 - z1)
-              i = i + 2
-          else
-              numeral = numeral + z1
-              i = i + 1
-          end
-        end
-        if i <= len then numeral = numeral + romans[ string.sub(roman,i,i) ] end
-        return numeral
-      end
-
-      local function sniffstyle(numstr, delimend)
-        local numdelim
-        if delimend == ")" then
-          numdelim = "OneParen"
-        elseif delimend == "." then
-          numdelim = "Period"
-        else
-          numdelim = "Default"
-        end
-
-        local num
-        num = numstr:match("^([I])$")
-        if num then
-          return roman2number(num), "UpperRoman", numdelim
-        end
-        num = numstr:match("^([i])$")
-        if num then
-          return roman2number(string.upper(num)), "LowerRoman", numdelim
-        end
-        num = numstr:match("^([A-Z])$")
-        if num then
-          return string.byte(num) - string.byte("A") + 1, "UpperAlpha", numdelim
-        end
-        num = numstr:match("^([a-z])$")
-        if num then
-          return string.byte(num) - string.byte("a") + 1, "LowerAlpha", numdelim
-        end
-        num = numstr:match("^([IVXLCDM]+)")
-        if num then
-          return roman2number(num), "UpperRoman", numdelim
-        end
-        num = numstr:match("^([ivxlcdm]+)")
-        if num then
-          return roman2number(string.upper(num)), "LowerRoman", numdelim
-        end
-        return math.floor(tonumber(numstr) or 1), "Decimal", numdelim
-      end
-
-      local function fancylist(items,tight,start)
-        local startnum, numstyle, numdelim = sniffstyle(start[2][1], start[2][2])
-        return writer.fancylist(items,tight,
-                                options.startNumber and startnum or 1,
-                                numstyle or "Decimal",
-                                numdelim or "Default")
-      end
-
-      local FancyListOfType = function(start_marker, continuation_marker, delimiter_type)
-        local enumerator_start = enumerator(start_marker, continuation_marker, delimiter_type)
-        local enumerator_cont = enumerator(continuation_marker, continuation_marker, delimiter_type)
-        return Cg(enumerator_start, "listtype")
-             * (Ct( TightListItem(Cb("listtype"))
-                 * ((parsers.check_minimal_indent / "") * TightListItem(enumerator_cont))^0)
-             * Cc(true)
-             * -#((parsers.conditionally_indented_blankline^0 / "")
-                 * parsers.check_minimal_indent * enumerator_cont)
-             + Ct( LooseListItem(Cb("listtype"))
-                 * ((parsers.conditionally_indented_blankline^0 / "")
-                   * (parsers.check_minimal_indent / "") * LooseListItem(enumerator_cont))^0)
-             * Cc(false)
-             ) * Ct(Cb("listtype")) / fancylist
-      end
-
-      local FancyList = join_table_with_func(FancyListOfType, markers_table)
-
-      local Endline   = parsers.newline
-                      * (parsers.check_minimal_indent
-                        * -parsers.EndlineExceptions
-                        + parsers.check_optional_indent
-                        * -parsers.EndlineExceptions
-                        * -starter)
-                      * parsers.spacechar^0
-                      / writer.soft_line_break
-
-      self.update_rule("OrderedList", FancyList)
-      self.update_rule("Endline", Endline)
-    end
-  }
-end
-M.extensions.fenced_code = function(blank_before_code_fence,
-                                    allow_attributes,
-                                    allow_raw_blocks)
-  return {
-    name = "built-in fenced_code syntax extension",
-    extend_writer = function(self)
-      local options = self.options
-
-      function self.fencedCode(s, i, attr)
-        if not self.is_writing then return "" end
-        s = s:gsub("\n$", "")
-        local buf = {}
-        if attr ~= nil then
-          table.insert(buf, {"\\markdownRendererFencedCodeAttributeContextBegin",
-                             self.attributes(attr)})
-        end
-        local name = util.cache_verbatim(options.cacheDir, s)
-        table.insert(buf, {"\\markdownRendererInputFencedCode{",
-                           name,"}{",self.string(i),"}{",self.infostring(i),"}"})
-        if attr ~= nil then
-          table.insert(buf, "\\markdownRendererFencedCodeAttributeContextEnd{}")
-        end
-        return buf
-      end
-
-      if allow_raw_blocks then
-        function self.rawBlock(s, attr)
-          if not self.is_writing then return "" end
-          s = s:gsub("\n$", "")
-          local name = util.cache_verbatim(options.cacheDir, s)
-          return {"\\markdownRendererInputRawBlock{",
-                  name,"}{", self.string(attr),"}"}
-        end
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local function captures_geq_length(_,i,a,b)
-        return #a >= #b and i
-      end
-
-      local function strip_enclosing_whitespaces(str)
-        return str:gsub("^%s*(.-)%s*$", "%1")
-      end
-
-      local tilde_infostring = Cs(Cs((V("HtmlEntity")
-                                     + parsers.anyescaped
-                                     - parsers.newline)^0)
-                                 / strip_enclosing_whitespaces)
-
-      local backtick_infostring = Cs(Cs((V("HtmlEntity")
-                                        + (-#(parsers.backslash * parsers.backtick) * parsers.anyescaped)
-                                         - parsers.newline
-                                         - parsers.backtick)^0)
-                                    / strip_enclosing_whitespaces)
-
-      local fenceindent
-
-      local function has_trail(indent_table)
-        return indent_table ~= nil and
-          indent_table.trail ~= nil and
-          next(indent_table.trail) ~= nil
-      end
-
-      local function has_indents(indent_table)
-        return indent_table ~= nil and
-          indent_table.indents ~= nil and
-          next(indent_table.indents) ~= nil
-      end
-
-      local function get_last_indent_name(indent_table)
-        if has_indents(indent_table) then
-          return indent_table.indents[#indent_table.indents].name
-        end
-      end
-
-      local function count_fenced_start_indent(_, _, indent_table, trail)
-        local last_indent_name = get_last_indent_name(indent_table)
-        fenceindent = 0
-        if last_indent_name ~= "li" then
-          fenceindent = #trail
-        end
-        return true
-      end
-
-      local fencehead      = function(char, infostring)
-        return               Cmt(Cb("indent_info") * parsers.check_trail, count_fenced_start_indent)
-                           * Cg(char^3, "fencelength")
-                           * parsers.optionalspace
-                           * infostring
-                           * (parsers.newline + parsers.eof)
-      end
-
-      local fencetail      = function(char)
-        return               parsers.check_trail_no_rem
-                           * Cmt(C(char^3) * Cb("fencelength"), captures_geq_length)
-                           * parsers.optionalspace * (parsers.newline + parsers.eof)
-                           + parsers.eof
-      end
-
-      local function process_fenced_line(s, i, indent_table, line_content, is_blank) -- luacheck: ignore s i
-        local remainder = ""
-        if has_trail(indent_table) then
-          remainder = indent_table.trail.internal_remainder
-        end
-
-        if is_blank and get_last_indent_name(indent_table) == "li" then
-          remainder = ""
-        end
-
-        local str = remainder .. line_content
-        local index = 1
-        local remaining = fenceindent
-
-        while true do
-          local c = str:sub(index, index)
-          if c == " " and remaining > 0 then
-            remaining = remaining - 1
-            index = index + 1
-          elseif c == "\t" and remaining > 3 then
-            remaining = remaining - 4
-            index = index + 1
-          else
-            break
-          end
-        end
-
-        return true, str:sub(index)
-      end
-
-      local fencedline = function(char)
-        return Cmt(Cb("indent_info") * C(parsers.line - fencetail(char)) * Cc(false), process_fenced_line)
-      end
-
-      local blankfencedline = Cmt(Cb("indent_info") * C(parsers.blankline) * Cc(true), process_fenced_line)
-
-      local TildeFencedCode
-             = fencehead(parsers.tilde, tilde_infostring)
-             * Cs(((parsers.check_minimal_blank_indent / "") * blankfencedline
-                   + (parsers.check_minimal_indent / "") * fencedline(parsers.tilde))^0)
-             * ((parsers.check_minimal_indent / "") * fencetail(parsers.tilde) + parsers.succeed)
-
-      local BacktickFencedCode
-             = fencehead(parsers.backtick, backtick_infostring)
-             * Cs(((parsers.check_minimal_blank_indent / "") * blankfencedline
-                   + (parsers.check_minimal_indent / "") * fencedline(parsers.backtick))^0)
-             * ((parsers.check_minimal_indent / "") * fencetail(parsers.backtick) + parsers.succeed)
-
-      local infostring_with_attributes
-                        = Ct(C((parsers.linechar
-                              - ( parsers.optionalspace
-                                * parsers.attributes))^0)
-                            * parsers.optionalspace
-                            * Ct(parsers.attributes))
-
-      local FencedCode
-               = ((TildeFencedCode + BacktickFencedCode)
-               / function(infostring, code)
-                   local expanded_code = self.expandtabs(code)
-
-                   if allow_raw_blocks then
-                     local raw_attr = lpeg.match(parsers.raw_attribute,
-                                                 infostring)
-                     if raw_attr then
-                       return writer.rawBlock(expanded_code, raw_attr)
-                     end
-                   end
-
-                   local attr = nil
-                   if allow_attributes then
-                     local match = lpeg.match(infostring_with_attributes,
-                                              infostring)
-                     if match then
-                       infostring, attr = table.unpack(match)
-                     end
-                   end
-                   return writer.fencedCode(expanded_code, infostring, attr)
-                 end)
-
-      self.insert_pattern("Block after Verbatim",
-                          FencedCode, "FencedCode")
-
-      local fencestart
-      if blank_before_code_fence then
-        fencestart = parsers.fail
-      else
-        fencestart = fencehead(parsers.backtick, backtick_infostring)
-                   + fencehead(parsers.tilde, tilde_infostring)
-      end
-
-      self.update_rule("EndlineExceptions", function(previous_pattern)
-        if previous_pattern == nil then
-          previous_pattern = parsers.EndlineExceptions
-        end
-        return previous_pattern + fencestart
-      end)
-
-      self.add_special_character("`")
-      self.add_special_character("~")
-    end
-  }
-end
-M.extensions.fenced_divs = function(blank_before_div_fence)
-  return {
-    name = "built-in fenced_divs syntax extension",
-    extend_writer = function(self)
-      function self.div_begin(attributes)
-        local start_output = {"\\markdownRendererFencedDivAttributeContextBegin\n",
-                              self.attributes(attributes)}
-        local end_output = {"\\markdownRendererFencedDivAttributeContextEnd{}"}
-        return self.push_attributes("div", attributes, start_output, end_output)
-      end
-      function self.div_end()
-        return self.pop_attributes("div")
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-      local fenced_div_infostring
-                             = C((parsers.linechar
-                                - ( parsers.spacechar^1
-                                  * parsers.colon^1))^1)
-
-      local fenced_div_begin = parsers.nonindentspace
-                             * parsers.colon^3
-                             * parsers.optionalspace
-                             * fenced_div_infostring
-                             * ( parsers.spacechar^1
-                               * parsers.colon^1)^0
-                             * parsers.optionalspace
-                             * (parsers.newline + parsers.eof)
-
-      local fenced_div_end = parsers.nonindentspace
-                           * parsers.colon^3
-                           * parsers.optionalspace
-                           * (parsers.newline + parsers.eof)
-      self.initialize_named_group("fenced_div_level", "0")
-      self.initialize_named_group("fenced_div_num_opening_indents")
-
-      local function increment_div_level()
-        local function push_indent_table(s, i, indent_table, -- luacheck: ignore s i
-                                         fenced_div_num_opening_indents, fenced_div_level)
-          fenced_div_level = tonumber(fenced_div_level) + 1
-          local num_opening_indents = 0
-          if indent_table.indents ~= nil then
-            num_opening_indents = #indent_table.indents
-          end
-          fenced_div_num_opening_indents[fenced_div_level] = num_opening_indents
-          return true, fenced_div_num_opening_indents
-        end
-
-        local function increment_level(s, i, fenced_div_level) -- luacheck: ignore s i
-          fenced_div_level = tonumber(fenced_div_level) + 1
-          return true, tostring(fenced_div_level)
-        end
-
-        return Cg( Cmt( Cb("indent_info")
-                      * Cb("fenced_div_num_opening_indents")
-                      * Cb("fenced_div_level"), push_indent_table)
-                 , "fenced_div_num_opening_indents")
-             * Cg( Cmt( Cb("fenced_div_level"), increment_level)
-                 , "fenced_div_level")
-      end
-
-      local function decrement_div_level()
-        local function pop_indent_table(s, i, fenced_div_indent_table, fenced_div_level) -- luacheck: ignore s i
-          fenced_div_level = tonumber(fenced_div_level)
-          fenced_div_indent_table[fenced_div_level] = nil
-          return true, tostring(fenced_div_level - 1)
-        end
-
-        return Cg( Cmt( Cb("fenced_div_num_opening_indents")
-                      * Cb("fenced_div_level"), pop_indent_table)
-                 , "fenced_div_level")
-      end
-
-      local non_fenced_div_block  = parsers.check_minimal_indent * V("Block")
-                                  - parsers.check_minimal_indent_and_trail * fenced_div_end
-
-      local non_fenced_div_paragraph  = parsers.check_minimal_indent * V("Paragraph")
-                                      - parsers.check_minimal_indent_and_trail * fenced_div_end
-
-      local blank = parsers.minimally_indented_blank
-
-      local block_separated  = parsers.block_sep_group(blank)
-                             * non_fenced_div_block
-
-      local loop_body_pair  = parsers.create_loop_body_pair(block_separated,
-                                                            non_fenced_div_paragraph,
-                                                            parsers.block_sep_group(blank),
-                                                            parsers.par_sep_group(blank))
-
-      local content_loop  = ( non_fenced_div_block
-                            * loop_body_pair.block^0
-                            + non_fenced_div_paragraph
-                            * block_separated
-                            * loop_body_pair.block^0
-                            + non_fenced_div_paragraph
-                            * loop_body_pair.par^0)
-                          * blank^0
-
-      local FencedDiv = fenced_div_begin
-                      / function (infostring)
-                          local attr = lpeg.match(Ct(parsers.attributes), infostring)
-                          if attr == nil then
-                            attr = {"." .. infostring}
-                          end
-                          return attr
-                        end
-                      / writer.div_begin
-                      * increment_div_level()
-                      * parsers.skipblanklines
-                      * Ct(content_loop)
-                      * parsers.minimally_indented_blank^0
-                      * parsers.check_minimal_indent_and_trail * fenced_div_end
-                      * decrement_div_level()
-                      * (Cc("") / writer.div_end)
-
-      self.insert_pattern("Block after Verbatim",
-                          FencedDiv, "FencedDiv")
-
-      self.add_special_character(":")
-
-      local function is_inside_div()
-        local function check_div_level(s, i, fenced_div_level) -- luacheck: ignore s i
-          fenced_div_level = tonumber(fenced_div_level)
-          return fenced_div_level > 0
-        end
-
-        return Cmt(Cb("fenced_div_level"), check_div_level)
-      end
-
-      local function check_indent()
-        local function compare_indent(s, i, indent_table, -- luacheck: ignore s i
-                                      fenced_div_num_opening_indents, fenced_div_level)
-          fenced_div_level = tonumber(fenced_div_level)
-          local num_current_indents = (indent_table.current_line_indents ~= nil and
-                                      #indent_table.current_line_indents) or 0
-          local num_opening_indents = fenced_div_num_opening_indents[fenced_div_level]
-          return num_current_indents == num_opening_indents
-        end
-
-        return Cmt( Cb("indent_info")
-                  * Cb("fenced_div_num_opening_indents")
-                  * Cb("fenced_div_level"), compare_indent)
-      end
-
-      local fencestart = is_inside_div()
-                       * fenced_div_end
-                       * check_indent()
-
-      if not blank_before_div_fence then
-        self.update_rule("EndlineExceptions", function(previous_pattern)
-          if previous_pattern == nil then
-            previous_pattern = parsers.EndlineExceptions
-          end
-          return previous_pattern + fencestart
-        end)
-      end
-    end
-  }
-end
-M.extensions.header_attributes = function()
-  return {
-    name = "built-in header_attributes syntax extension",
-    extend_writer = function()
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local function strip_atx_end(s)
-        return s:gsub("%s+#*%s*$","")
-      end
-
-      local AtxHeading = Cg(parsers.heading_start, "level")
-                       * parsers.optionalspace
-                       * (C(((parsers.linechar
-                              - (parsers.attributes
-                                * parsers.optionalspace
-                                * parsers.newline))
-                             * (parsers.linechar
-                               - parsers.lbrace)^0)^1)
-                             / strip_atx_end
-                             / parsers.parse_heading_text)
-                       * Cg(Ct(parsers.newline
-                              + (parsers.attributes
-                                * parsers.optionalspace
-                                * parsers.newline)), "attributes")
-                       * Cb("level")
-                       * Cb("attributes")
-                       / writer.heading
-
-      local function strip_trailing_spaces(s)
-        return s:gsub("%s*$","")
-      end
-
-      local heading_line  = (parsers.linechar
-                            - (parsers.attributes
-                              * parsers.optionalspace
-                              * parsers.newline))^1
-                          - parsers.thematic_break_lines
-
-      local heading_text  = heading_line
-                          * ((V("Endline") / "\n") * (heading_line - parsers.heading_level))^0
-                          * parsers.newline^-1
-
-      local SetextHeading  = parsers.freeze_trail * parsers.check_trail_no_rem
-                           * #(heading_text
-                              * (parsers.attributes
-                                * parsers.optionalspace
-                                * parsers.newline)^-1
-                              * parsers.check_minimal_indent * parsers.check_trail * parsers.heading_level)
-                           * Cs(heading_text) / strip_trailing_spaces
-                           / parsers.parse_heading_text
-                           * Cg(Ct((parsers.attributes
-                                 * parsers.optionalspace
-                                 * parsers.newline)^-1), "attributes")
-                           * parsers.check_minimal_indent_and_trail * parsers.heading_level
-                           * Cb("attributes")
-                           * parsers.newline
-                           * parsers.unfreeze_trail
-                           / writer.heading
-
-      local Heading = AtxHeading + SetextHeading
-      self.update_rule("Heading", Heading)
-    end
-  }
-end
-M.extensions.inline_code_attributes = function()
-  return {
-    name = "built-in inline_code_attributes syntax extension",
-    extend_writer = function()
-    end, extend_reader = function(self)
-      local writer = self.writer
-
-      local CodeWithAttributes = parsers.inticks
-                               * Ct(parsers.attributes)
-                               / writer.code
-
-      self.insert_pattern("Inline before Code",
-                          CodeWithAttributes,
-                          "CodeWithAttributes")
-    end
-  }
-end
-M.extensions.line_blocks = function()
-  return {
-    name = "built-in line_blocks syntax extension",
-    extend_writer = function(self)
-      function self.lineblock(lines)
-        if not self.is_writing then return "" end
-        local buffer = {}
-        for i = 1, #lines - 1 do
-          buffer[#buffer + 1] = { lines[i], self.hard_line_break }
-        end
-        buffer[#buffer + 1] = lines[#lines]
-
-        return {"\\markdownRendererLineBlockBegin\n"
-                  ,buffer,
-                  "\n\\markdownRendererLineBlockEnd "}
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local LineBlock = Ct(
-                        (Cs(
-                          ( (parsers.pipe * parsers.space)/""
-                          * ((parsers.space)/entities.char_entity("nbsp"))^0
-                          * parsers.linechar^0 * (parsers.newline/""))
-                          * (-parsers.pipe
-                            * (parsers.space^1/" ")
-                            * parsers.linechar^1
-                            * (parsers.newline/"")
-                            )^0
-                          * (parsers.blankline/"")^0
-                        ) / self.parser_functions.parse_inlines)^1) / writer.lineblock
-
-      self.insert_pattern("Block after Blockquote",
-                           LineBlock, "LineBlock")
-    end
-  }
-end
-M.extensions.mark = function()
-  return {
-    name = "built-in mark syntax extension",
-    extend_writer = function(self)
-      function self.mark(s)
-        if self.flatten_inlines then return s end
-        return {"\\markdownRendererMark{", s, "}"}
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local doubleequals = P("==")
-
-      local Mark = parsers.between(V("Inline"), doubleequals, doubleequals)
-                 / function (inlines) return writer.mark(inlines) end
-
-      self.add_special_character("=")
-      self.insert_pattern("Inline before LinkAndEmph",
-                          Mark, "Mark")
-    end
-  }
-end
-M.extensions.link_attributes = function()
-  return {
-    name = "built-in link_attributes syntax extension",
-    extend_writer = function()
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local options = self.options
-
-      local define_reference_parser = (parsers.check_trail / "") * parsers.link_label * parsers.colon
-                                    * parsers.spnlc * parsers.url
-                                    * ( parsers.spnlc_sep * parsers.title * (parsers.spnlc * Ct(parsers.attributes))
-                                      * parsers.only_blank
-                                      + parsers.spnlc_sep * parsers.title * parsers.only_blank
-                                      + Cc("") * (parsers.spnlc * Ct(parsers.attributes)) * parsers.only_blank
-                                      + Cc("") * parsers.only_blank)
-
-      local ReferenceWithAttributes = define_reference_parser
-                                    / self.register_link
-
-      self.update_rule("Reference", ReferenceWithAttributes)
-
-
-      local LinkWithAttributesAndEmph = Ct(parsers.link_and_emph_table * Cg(Cc(true), "match_link_attributes"))
-                                      / self.defer_link_and_emphasis_processing
-
-      self.update_rule("LinkAndEmph", LinkWithAttributesAndEmph)
-
-      local AutoLinkUrlWithAttributes
-                      = parsers.auto_link_url
-                      * Ct(parsers.attributes)
-                      / self.auto_link_url
-
-      self.insert_pattern("Inline before AutoLinkUrl",
-                          AutoLinkUrlWithAttributes,
-                          "AutoLinkUrlWithAttributes")
-
-      local AutoLinkEmailWithAttributes
-                      = parsers.auto_link_email
-                      * Ct(parsers.attributes)
-                      / self.auto_link_email
-
-      self.insert_pattern("Inline before AutoLinkEmail",
-                          AutoLinkEmailWithAttributes,
-                          "AutoLinkEmailWithAttributes")
-
-      if options.relativeReferences then
-
-        local AutoLinkRelativeReferenceWithAttributes
-                        = parsers.auto_link_relative_reference
-                        * Ct(parsers.attributes)
-                        / self.auto_link_url
-
-        self.insert_pattern(
-          "Inline before AutoLinkRelativeReference",
-          AutoLinkRelativeReferenceWithAttributes,
-          "AutoLinkRelativeReferenceWithAttributes")
-
-      end
-
-    end
-  }
-end
-M.extensions.notes = function(notes, inline_notes)
-  assert(notes or inline_notes)
-  return {
-    name = "built-in notes syntax extension",
-    extend_writer = function(self)
-      function self.note(s)
-        if self.flatten_inlines then return "" end
-        return {"\\markdownRendererNote{",s,"}"}
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local rawnotes = parsers.rawnotes
-
-      if inline_notes then
-        local InlineNote
-                    = parsers.circumflex
-                    * (parsers.link_label / self.parser_functions.parse_inlines_no_inline_note)
-                    / writer.note
-
-        self.insert_pattern("Inline after LinkAndEmph",
-                            InlineNote, "InlineNote")
-      end
-      if notes then
-        local function strip_first_char(s)
-          return s:sub(2)
-        end
-
-        local RawNoteRef
-                      = #(parsers.lbracket * parsers.circumflex)
-                      * parsers.link_label / strip_first_char
-
-        -- like indirect_link
-        local function lookup_note(ref)
-          return writer.defer_call(function()
-            local found = rawnotes[self.normalize_tag(ref)]
-            if found then
-              return writer.note(
-                self.parser_functions.parse_blocks_nested(found))
-            else
-              return {"[",
-                self.parser_functions.parse_inlines("^" .. ref), "]"}
-            end
-          end)
-        end
-
-        local function register_note(ref,rawnote)
-          local normalized_tag = self.normalize_tag(ref)
-          if rawnotes[normalized_tag] == nil then
-            rawnotes[normalized_tag] = rawnote
-          end
-          return ""
-        end
-
-        local NoteRef = RawNoteRef / lookup_note
-
-        local optionally_indented_line = parsers.check_optional_indent_and_any_trail * parsers.line
-
-        local blank = parsers.check_optional_blank_indent_and_any_trail * parsers.optionalspace * parsers.newline
-
-        local chunk = Cs(parsers.line * (optionally_indented_line - blank)^0)
-
-        local indented_blocks = function(bl)
-          return Cs( bl
-                * (blank^1 * (parsers.check_optional_indent / "")
-                  * parsers.check_code_trail * -parsers.blankline * bl)^0)
-        end
-
-        local NoteBlock
-                    = parsers.check_trail_no_rem * RawNoteRef * parsers.colon
-                    * parsers.spnlc * indented_blocks(chunk)
-                    / register_note
-
-        local Reference = NoteBlock + parsers.Reference
-
-        self.update_rule("Reference", Reference)
-        self.insert_pattern("Inline before LinkAndEmph",
-                            NoteRef, "NoteRef")
-      end
-
-      self.add_special_character("^")
-    end
-  }
-end
-M.extensions.pipe_tables = function(table_captions, table_attributes)
-
-  local function make_pipe_table_rectangular(rows)
-    local num_columns = #rows[2]
-    local rectangular_rows = {}
-    for i = 1, #rows do
-      local row = rows[i]
-      local rectangular_row = {}
-      for j = 1, num_columns do
-        rectangular_row[j] = row[j] or ""
-      end
-      table.insert(rectangular_rows, rectangular_row)
-    end
-    return rectangular_rows
+    return output
   end
-
-  local function pipe_table_row(allow_empty_first_column
-                               , nonempty_column
-                               , column_separator
-                               , column)
-    local row_beginning
-    if allow_empty_first_column then
-      row_beginning = -- empty first column
-                      #(parsers.spacechar^4
-                       * column_separator)
-                    * parsers.optionalspace
-                    * column
-                    * parsers.optionalspace
-                    -- non-empty first column
-                    + parsers.nonindentspace
-                    * nonempty_column^-1
-                    * parsers.optionalspace
-    else
-      row_beginning = parsers.nonindentspace
-                    * nonempty_column^-1
-                    * parsers.optionalspace
-    end
-
-    return Ct(row_beginning
-             * (-- single column with no leading pipes
-                #(column_separator
-                 * parsers.optionalspace
-                 * parsers.newline)
-               * column_separator
-               * parsers.optionalspace
-               -- single column with leading pipes or
-               -- more than a single column
-               + (column_separator
-                 * parsers.optionalspace
-                 * column
-                 * parsers.optionalspace)^1
-               * (column_separator
-                 * parsers.optionalspace)^-1))
-  end
-
-  return {
-    name = "built-in pipe_tables syntax extension",
-    extend_writer = function(self)
-      function self.table(rows, caption, attributes)
-        if not self.is_writing then return "" end
-        local buffer = {}
-        if attributes ~= nil then
-          table.insert(buffer,
-                       "\\markdownRendererTableAttributeContextBegin\n")
-          table.insert(buffer, self.attributes(attributes))
-        end
-        table.insert(buffer,
-                     {"\\markdownRendererTable{",
-                      caption or "", "}{", #rows - 1, "}{",
-                      #rows[1], "}"})
-        local temp = rows[2] -- put alignments on the first row
-        rows[2] = rows[1]
-        rows[1] = temp
-        for i, row in ipairs(rows) do
-          table.insert(buffer, "{")
-          for _, column in ipairs(row) do
-            if i > 1 then -- do not use braces for alignments
-              table.insert(buffer, "{")
-            end
-            table.insert(buffer, column)
-            if i > 1 then
-              table.insert(buffer, "}")
-            end
-          end
-          table.insert(buffer, "}")
-        end
-        if attributes ~= nil then
-          table.insert(buffer,
-                       "\\markdownRendererTableAttributeContextEnd{}")
-        end
-        return buffer
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local table_hline_separator = parsers.pipe + parsers.plus
-
-      local table_hline_column = (parsers.dash
-                                 - #(parsers.dash
-                                    * (parsers.spacechar
-                                      + table_hline_separator
-                                      + parsers.newline)))^1
-                               * (parsers.colon * Cc("r")
-                                 + parsers.dash * Cc("d"))
-                               + parsers.colon
-                               * (parsers.dash
-                                 - #(parsers.dash
-                                    * (parsers.spacechar
-                                      + table_hline_separator
-                                      + parsers.newline)))^1
-                               * (parsers.colon * Cc("c")
-                                 + parsers.dash * Cc("l"))
-
-      local table_hline = pipe_table_row(false
-                                        , table_hline_column
-                                        , table_hline_separator
-                                        , table_hline_column)
-
-      local table_caption_beginning = (parsers.check_minimal_blank_indent_and_any_trail_no_rem
-                                      * parsers.optionalspace * parsers.newline)^0
-                                    * parsers.check_minimal_indent_and_trail
-                                    * (P("Table")^-1 * parsers.colon)
-                                    * parsers.optionalspace
-
-      local function strip_trailing_spaces(s)
-        return s:gsub("%s*$","")
-      end
-
-      local table_row = pipe_table_row(true
-                                      , (C((parsers.linechar - parsers.pipe)^1)
-                                        / strip_trailing_spaces
-                                        / self.parser_functions.parse_inlines)
-                                      , parsers.pipe
-                                      , (C((parsers.linechar - parsers.pipe)^0)
-                                        / strip_trailing_spaces
-                                        / self.parser_functions.parse_inlines))
-
-      local table_caption
-      if table_captions then
-        table_caption = #table_caption_beginning
-                      * table_caption_beginning
-        if table_attributes then
-          table_caption = table_caption
-                        * (C(((( parsers.linechar
-                               - (parsers.attributes
-                                 * parsers.optionalspace
-                                 * parsers.newline
-                                 * -#( parsers.optionalspace
-                                     * parsers.linechar)))
-                              + ( parsers.newline
-                                * #( parsers.optionalspace
-                                   * parsers.linechar)
-                                * C(parsers.optionalspace) / writer.space))
-                             * (parsers.linechar
-                               - parsers.lbrace)^0)^1)
-                             / self.parser_functions.parse_inlines)
-                        * (parsers.newline
-                          + ( Ct(parsers.attributes)
-                            * parsers.optionalspace
-                            * parsers.newline))
-        else
-          table_caption = table_caption
-                        * C(( parsers.linechar
-                            + ( parsers.newline
-                              * #( parsers.optionalspace
-                                 * parsers.linechar)
-                              * C(parsers.optionalspace) / writer.space))^1)
-                        / self.parser_functions.parse_inlines
-                        * parsers.newline
-        end
-      else
-        table_caption = parsers.fail
-      end
-
-      local PipeTable = Ct(table_row * parsers.newline * (parsers.check_minimal_indent_and_trail / {})
-                        * table_hline * parsers.newline
-                        * ((parsers.check_minimal_indent / {}) * table_row * parsers.newline)^0)
-                      / make_pipe_table_rectangular
-                      * table_caption^-1
-                      / writer.table
-
-      self.insert_pattern("Block after Blockquote",
-                          PipeTable, "PipeTable")
-    end
-  }
 end
-M.extensions.raw_inline = function()
-  return {
-    name = "built-in raw_inline syntax extension",
-    extend_writer = function(self)
-      local options = self.options
-
-      function self.rawInline(s, attr)
-        if not self.is_writing then return "" end
-        if self.flatten_inlines then return s end
-        local name = util.cache_verbatim(options.cacheDir, s)
-        return {"\\markdownRendererInputRawInline{",
-                name,"}{", self.string(attr),"}"}
-      end
-    end, extend_reader = function(self)
-      local writer = self.writer
-
-      local RawInline = parsers.inticks
-                      * parsers.raw_attribute
-                      / writer.rawInline
-
-      self.insert_pattern("Inline before Code",
-                          RawInline, "RawInline")
-    end
-  }
-end
-M.extensions.strike_through = function()
-  return {
-    name = "built-in strike_through syntax extension",
-    extend_writer = function(self)
-      function self.strike_through(s)
-        if self.flatten_inlines then return s end
-        return {"\\markdownRendererStrikeThrough{",s,"}"}
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local StrikeThrough = (
-        parsers.between(parsers.Inline, parsers.doubletildes,
-                        parsers.doubletildes)
-      ) / writer.strike_through
-
-      self.insert_pattern("Inline after LinkAndEmph",
-                          StrikeThrough, "StrikeThrough")
-
-      self.add_special_character("~")
-    end
-  }
-end
-M.extensions.subscripts = function()
-  return {
-    name = "built-in subscripts syntax extension",
-    extend_writer = function(self)
-      function self.subscript(s)
-        if self.flatten_inlines then return s end
-        return {"\\markdownRendererSubscript{",s,"}"}
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local Subscript = (
-        parsers.between(parsers.Str, parsers.tilde, parsers.tilde)
-      ) / writer.subscript
-
-      self.insert_pattern("Inline after LinkAndEmph",
-                          Subscript, "Subscript")
-
-      self.add_special_character("~")
-    end
-  }
-end
-M.extensions.superscripts = function()
-  return {
-    name = "built-in superscripts syntax extension",
-    extend_writer = function(self)
-      function self.superscript(s)
-        if self.flatten_inlines then return s end
-        return {"\\markdownRendererSuperscript{",s,"}"}
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local Superscript = (
-        parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex)
-      ) / writer.superscript
-
-      self.insert_pattern("Inline after LinkAndEmph",
-                          Superscript, "Superscript")
-
-      self.add_special_character("^")
-    end
-  }
-end
-M.extensions.tex_math = function(tex_math_dollars,
-                                 tex_math_single_backslash,
-                                 tex_math_double_backslash)
-  return {
-    name = "built-in tex_math syntax extension",
-    extend_writer = function(self)
-      function self.display_math(s)
-        if self.flatten_inlines then return s end
-        return {"\\markdownRendererDisplayMath{",self.math(s),"}"}
-      end
-      function self.inline_math(s)
-        if self.flatten_inlines then return s end
-        return {"\\markdownRendererInlineMath{",self.math(s),"}"}
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local function between(p, starter, ender)
-        return (starter * Cs(p * (p - ender)^0) * ender)
-      end
-
-      local function strip_preceding_whitespaces(str)
-        return str:gsub("^%s*(.-)$", "%1")
-      end
-
-      local allowed_before_closing = B( parsers.backslash * parsers.any
-                                      + parsers.any * (parsers.any - parsers.backslash))
-
-      local allowed_before_closing_no_space = B( parsers.backslash * parsers.any
-                                               + parsers.any * (parsers.nonspacechar - parsers.backslash))
-
-      local dollar_math_content = (parsers.newline * (parsers.check_optional_indent / "")
-                                + parsers.backslash^-1
-                                * parsers.linechar)
-                                - parsers.blankline^2
-                                - parsers.dollar
-
-      local inline_math_opening_dollars = parsers.dollar
-                                        * #(parsers.nonspacechar)
-
-      local inline_math_closing_dollars = allowed_before_closing_no_space
-                                        * parsers.dollar
-                                        * -#(parsers.digit)
-
-      local inline_math_dollars = between(Cs( dollar_math_content),
-                                          inline_math_opening_dollars,
-                                          inline_math_closing_dollars)
-
-      local display_math_opening_dollars  = parsers.dollar
-                                          * parsers.dollar
-
-      local display_math_closing_dollars  = parsers.dollar
-                                          * parsers.dollar
-
-      local display_math_dollars = between(Cs( dollar_math_content),
-                                           display_math_opening_dollars,
-                                           display_math_closing_dollars)
-      local backslash_math_content  = (parsers.newline * (parsers.check_optional_indent / "")
-                                    + parsers.linechar)
-                                    - parsers.blankline^2
-      local inline_math_opening_double  = parsers.backslash
-                                        * parsers.backslash
-                                        * parsers.lparent
-
-      local inline_math_closing_double  = allowed_before_closing
-                                        * parsers.spacechar^0
-                                        * parsers.backslash
-                                        * parsers.backslash
-                                        * parsers.rparent
-
-      local inline_math_double  = between(Cs( backslash_math_content),
-                                          inline_math_opening_double,
-                                          inline_math_closing_double)
-                                / strip_preceding_whitespaces
-
-      local display_math_opening_double = parsers.backslash
-                                        * parsers.backslash
-                                        * parsers.lbracket
-
-      local display_math_closing_double = allowed_before_closing
-                                        * parsers.spacechar^0
-                                        * parsers.backslash
-                                        * parsers.backslash
-                                        * parsers.rbracket
-
-      local display_math_double = between(Cs( backslash_math_content),
-                                          display_math_opening_double,
-                                          display_math_closing_double)
-                                / strip_preceding_whitespaces
-      local inline_math_opening_single  = parsers.backslash
-                                        * parsers.lparent
-
-      local inline_math_closing_single  = allowed_before_closing
-                                        * parsers.spacechar^0
-                                        * parsers.backslash
-                                        * parsers.rparent
-
-      local inline_math_single  = between(Cs( backslash_math_content),
-                                          inline_math_opening_single,
-                                          inline_math_closing_single)
-                                / strip_preceding_whitespaces
-
-      local display_math_opening_single = parsers.backslash
-                                        * parsers.lbracket
-
-      local display_math_closing_single = allowed_before_closing
-                                        * parsers.spacechar^0
-                                        * parsers.backslash
-                                        * parsers.rbracket
-
-      local display_math_single = between(Cs( backslash_math_content),
-                                          display_math_opening_single,
-                                          display_math_closing_single)
-                                / strip_preceding_whitespaces
-
-      local display_math = parsers.fail
-
-      local inline_math = parsers.fail
-
-      if tex_math_dollars then
-        display_math = display_math + display_math_dollars
-        inline_math = inline_math + inline_math_dollars
-      end
-
-      if tex_math_double_backslash then
-        display_math = display_math + display_math_double
-        inline_math = inline_math + inline_math_double
-      end
-
-      if tex_math_single_backslash then
-        display_math = display_math + display_math_single
-        inline_math = inline_math + inline_math_single
-      end
-
-      local TexMath = display_math / writer.display_math
-                    + inline_math / writer.inline_math
-
-      self.insert_pattern("Inline after LinkAndEmph",
-                          TexMath, "TexMath")
-
-      if tex_math_dollars then
-        self.add_special_character("$")
-      end
-
-      if tex_math_single_backslash or tex_math_double_backslash then
-        self.add_special_character("\\")
-        self.add_special_character("[")
-        self.add_special_character("]")
-        self.add_special_character(")")
-        self.add_special_character("(")
-      end
-    end
-  }
-end
-M.extensions.jekyll_data = function(expect_jekyll_data)
-  return {
-    name = "built-in jekyll_data syntax extension",
-    extend_writer = function(self)
-      function self.jekyllData(d, t, p)
-        if not self.is_writing then return "" end
-
-        local buf = {}
-
-        local keys = {}
-        for k, _ in pairs(d) do
-          table.insert(keys, k)
-        end
-        table.sort(keys, function(first, second)
-          if type(first) ~= type(second) then
-            return type(first) < type(second)
-          else
-            return first < second
-          end
-        end)
-
-        if not p then
-          table.insert(buf, "\\markdownRendererJekyllDataBegin")
-        end
-
-        local is_sequence = false
-        if #d > 0 and #d == #keys then
-          for i=1, #d do
-            if d[i] == nil then
-              goto not_a_sequence
-            end
-          end
-          is_sequence = true
-        end
-        ::not_a_sequence::
-
-        if is_sequence then
-            table.insert(buf, "\\markdownRendererJekyllDataSequenceBegin{")
-            table.insert(buf, self.identifier(p or "null"))
-            table.insert(buf, "}{")
-            table.insert(buf, #keys)
-            table.insert(buf, "}")
-        else
-            table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{")
-            table.insert(buf, self.identifier(p or "null"))
-            table.insert(buf, "}{")
-            table.insert(buf, #keys)
-            table.insert(buf, "}")
-        end
-
-        for _, k in ipairs(keys) do
-          local v = d[k]
-          local typ = type(v)
-          k = tostring(k or "null")
-          if typ == "table" and next(v) ~= nil then
-            table.insert(
-              buf,
-              self.jekyllData(v, t, k)
-            )
-          else
-            k = self.identifier(k)
-            v = tostring(v)
-            if typ == "boolean" then
-              table.insert(buf, "\\markdownRendererJekyllDataBoolean{")
-              table.insert(buf, k)
-              table.insert(buf, "}{")
-              table.insert(buf, v)
-              table.insert(buf, "}")
-            elseif typ == "number" then
-              table.insert(buf, "\\markdownRendererJekyllDataNumber{")
-              table.insert(buf, k)
-              table.insert(buf, "}{")
-              table.insert(buf, v)
-              table.insert(buf, "}")
-            elseif typ == "string" then
-              table.insert(buf, "\\markdownRendererJekyllDataString{")
-              table.insert(buf, k)
-              table.insert(buf, "}{")
-              table.insert(buf, t(v))
-              table.insert(buf, "}")
-            elseif typ == "table" then
-              table.insert(buf, "\\markdownRendererJekyllDataEmpty{")
-              table.insert(buf, k)
-              table.insert(buf, "}")
-            else
-              error(format("Unexpected type %s for value of " ..
-                           "YAML key %s", typ, k))
-            end
-          end
-        end
-
-        if is_sequence then
-          table.insert(buf, "\\markdownRendererJekyllDataSequenceEnd")
-        else
-          table.insert(buf, "\\markdownRendererJekyllDataMappingEnd")
-        end
-
-        if not p then
-          table.insert(buf, "\\markdownRendererJekyllDataEnd")
-        end
-
-        return buf
-      end
-    end, extend_reader = function(self)
-      local parsers = self.parsers
-      local writer = self.writer
-
-      local JekyllData
-                    = Cmt( C((parsers.line - P("---") - P("..."))^0)
-                         , function(s, i, text) -- luacheck: ignore s i
-                             local data
-                             local ran_ok, _ = pcall(function()
-                               -- TODO: Replace with `require("tinyyaml")` in TeX Live 2023
-                               local tinyyaml = require("markdown-tinyyaml")
-                               data = tinyyaml.parse(text, {timestamps=false})
-                             end)
-                             if ran_ok and data ~= nil then
-                               return true, writer.jekyllData(data, function(s)
-                                 return self.parser_functions.parse_blocks_nested(s)
-                               end, nil)
-                             else
-                               return false
-                             end
-                           end
-                         )
-
-      local UnexpectedJekyllData
-                    = P("---")
-                    * parsers.blankline / 0
-                    * #(-parsers.blankline)  -- if followed by blank, it's thematic break
-                    * JekyllData
-                    * (P("---") + P("..."))
-
-      local ExpectedJekyllData
-                    = ( P("---")
-                      * parsers.blankline / 0
-                      * #(-parsers.blankline)  -- if followed by blank, it's thematic break
-                      )^-1
-                    * JekyllData
-                    * (P("---") + P("..."))^-1
-
-      self.insert_pattern("Block before Blockquote",
-                          UnexpectedJekyllData, "UnexpectedJekyllData")
-      if expect_jekyll_data then
-        self.update_rule("ExpectedJekyllData", ExpectedJekyllData)
-      end
-    end
-  }
-end
-function M.new(options)
-  options = options or {}
-  setmetatable(options, { __index = function (_, key)
-    return defaultOptions[key] end })
-  if options.singletonCache and singletonCache.convert then
-    for k, v in pairs(defaultOptions) do
-      if type(v) == "table" then
-        for i = 1, math.max(#singletonCache.options[k], #options[k]) do
-          if singletonCache.options[k][i] ~= options[k][i] then
-            goto miss
-          end
-        end
-      elseif singletonCache.options[k] ~= options[k] then
-        goto miss
-      end
-    end
-    return singletonCache.convert
-  end
-  ::miss::
-  local extensions = {}
-
-  if options.bracketedSpans then
-    local bracketed_spans_extension = M.extensions.bracketed_spans()
-    table.insert(extensions, bracketed_spans_extension)
-  end
-
-  if options.contentBlocks then
-    local content_blocks_extension = M.extensions.content_blocks(
-      options.contentBlocksLanguageMap)
-    table.insert(extensions, content_blocks_extension)
-  end
-
-  if options.definitionLists then
-    local definition_lists_extension = M.extensions.definition_lists(
-      options.tightLists)
-    table.insert(extensions, definition_lists_extension)
-  end
-
-  if options.fencedCode then
-    local fenced_code_extension = M.extensions.fenced_code(
-      options.blankBeforeCodeFence,
-      options.fencedCodeAttributes,
-      options.rawAttribute)
-    table.insert(extensions, fenced_code_extension)
-  end
-
-  if options.fencedDivs then
-    local fenced_div_extension = M.extensions.fenced_divs(
-      options.blankBeforeDivFence)
-    table.insert(extensions, fenced_div_extension)
-  end
-
-  if options.headerAttributes then
-    local header_attributes_extension = M.extensions.header_attributes()
-    table.insert(extensions, header_attributes_extension)
-  end
-
-  if options.inlineCodeAttributes then
-    local inline_code_attributes_extension =
-      M.extensions.inline_code_attributes()
-    table.insert(extensions, inline_code_attributes_extension)
-  end
-
-  if options.jekyllData then
-    local jekyll_data_extension = M.extensions.jekyll_data(
-      options.expectJekyllData)
-    table.insert(extensions, jekyll_data_extension)
-  end
-
-  if options.linkAttributes then
-    local link_attributes_extension =
-      M.extensions.link_attributes()
-    table.insert(extensions, link_attributes_extension)
-  end
-
-  if options.lineBlocks then
-    local line_block_extension = M.extensions.line_blocks()
-    table.insert(extensions, line_block_extension)
-  end
-
-  if options.mark then
-    local mark_extension = M.extensions.mark()
-    table.insert(extensions, mark_extension)
-  end
-
-  if options.pipeTables then
-    local pipe_tables_extension = M.extensions.pipe_tables(
-      options.tableCaptions, options.tableAttributes)
-    table.insert(extensions, pipe_tables_extension)
-  end
-
-  if options.rawAttribute then
-    local raw_inline_extension = M.extensions.raw_inline()
-    table.insert(extensions, raw_inline_extension)
-  end
-
-  if options.strikeThrough then
-    local strike_through_extension = M.extensions.strike_through()
-    table.insert(extensions, strike_through_extension)
-  end
-
-  if options.subscripts then
-    local subscript_extension = M.extensions.subscripts()
-    table.insert(extensions, subscript_extension)
-  end
-
-  if options.superscripts then
-    local superscript_extension = M.extensions.superscripts()
-    table.insert(extensions, superscript_extension)
-  end
-
-  if options.texMathDollars or
-     options.texMathSingleBackslash or
-     options.texMathDoubleBackslash then
-    local tex_math_extension = M.extensions.tex_math(
-      options.texMathDollars,
-      options.texMathSingleBackslash,
-      options.texMathDoubleBackslash)
-    table.insert(extensions, tex_math_extension)
-  end
-
-  if options.notes or options.inlineNotes then
-    local notes_extension = M.extensions.notes(
-      options.notes, options.inlineNotes)
-    table.insert(extensions, notes_extension)
-  end
-
-  if options.citations then
-    local citations_extension = M.extensions.citations(options.citationNbsps)
-    table.insert(extensions, citations_extension)
-  end
-
-  if options.fancyLists then
-    local fancy_lists_extension = M.extensions.fancy_lists()
-    table.insert(extensions, fancy_lists_extension)
-  end
-  for _, user_extension_filename in ipairs(options.extensions) do
-    local user_extension = (function(filename)
-      local pathname = kpse.lookup(filename)
-      local input_file = assert(io.open(pathname, "r"),
-        [[Could not open user-defined syntax extension "]]
-        .. pathname .. [[" for reading]])
-      local input = assert(input_file:read("*a"))
-      assert(input_file:close())
-      local user_extension, err = load([[
-        local sandbox = {}
-        setmetatable(sandbox, {__index = _G})
-        _ENV = sandbox
-      ]] .. input)()
-      assert(user_extension,
-        [[Failed to compile user-defined syntax extension "]]
-        .. pathname .. [[": ]] .. (err or [[]]))
-      assert(user_extension.api_version ~= nil,
-        [[User-defined syntax extension "]] .. pathname
-        .. [[" does not specify mandatory field "api_version"]])
-      assert(type(user_extension.api_version) == "number",
-        [[User-defined syntax extension "]] .. pathname
-        .. [[" specifies field "api_version" of type "]]
-        .. type(user_extension.api_version)
-        .. [[" but "number" was expected]])
-      assert(user_extension.api_version > 0
-         and user_extension.api_version <= metadata.user_extension_api_version,
-        [[User-defined syntax extension "]] .. pathname
-        .. [[" uses syntax extension API version "]]
-        .. user_extension.api_version .. [[ but markdown.lua ]]
-        .. metadata.version .. [[ uses API version ]]
-        .. metadata.user_extension_api_version
-        .. [[, which is incompatible]])
-
-      assert(user_extension.grammar_version ~= nil,
-        [[User-defined syntax extension "]] .. pathname
-        .. [[" does not specify mandatory field "grammar_version"]])
-      assert(type(user_extension.grammar_version) == "number",
-        [[User-defined syntax extension "]] .. pathname
-        .. [[" specifies field "grammar_version" of type "]]
-        .. type(user_extension.grammar_version)
-        .. [[" but "number" was expected]])
-      assert(user_extension.grammar_version == metadata.grammar_version,
-        [[User-defined syntax extension "]] .. pathname
-        .. [[" uses grammar version "]] .. user_extension.grammar_version
-        .. [[ but markdown.lua ]] .. metadata.version
-        .. [[ uses grammar version ]] .. metadata.grammar_version
-        .. [[, which is incompatible]])
-
-      assert(user_extension.finalize_grammar ~= nil,
-        [[User-defined syntax extension "]] .. pathname
-        .. [[" does not specify mandatory "finalize_grammar" field]])
-      assert(type(user_extension.finalize_grammar) == "function",
-        [[User-defined syntax extension "]] .. pathname
-        .. [[" specifies field "finalize_grammar" of type "]]
-        .. type(user_extension.finalize_grammar)
-        .. [[" but "function" was expected]])
-      local extension = {
-        name = [[user-defined "]] .. pathname .. [[" syntax extension]],
-        extend_reader = user_extension.finalize_grammar,
-        extend_writer = function() end,
-      }
-      return extension
-    end)(user_extension_filename)
-    table.insert(extensions, user_extension)
-  end
-  local writer = M.writer.new(options)
-  local reader = M.reader.new(writer, options)
-  local convert = reader.finalize_grammar(extensions)
-  collectgarbage("collect")
-  if options.singletonCache then
-    local singletonCacheOptions = {}
-    for k, v in pairs(options) do
-      singletonCacheOptions[k] = v
-    end
-    setmetatable(singletonCacheOptions,
-      { __index = function (_, key)
-        return defaultOptions[key] end })
-    singletonCache.options = singletonCacheOptions
-    singletonCache.convert = convert
-  end
-  return convert
-end
-
 return M

Modified: trunk/Master/tlpkg/tlpsrc/markdown.tlpsrc
===================================================================
--- trunk/Master/tlpkg/tlpsrc/markdown.tlpsrc	2024-08-30 21:13:21 UTC (rev 72145)
+++ trunk/Master/tlpkg/tlpsrc/markdown.tlpsrc	2024-08-30 21:14:17 UTC (rev 72146)
@@ -0,0 +1,21 @@
+depend l3kernel
+depend lt3luabridge
+depend lua-uni-algos
+#soft amsfonts
+#soft amsmath
+#soft catchfile
+#soft csvsimple
+#soft epstopdf-pkg
+#soft etoolbox
+#soft fancyvrb
+#soft gobble
+#soft graphics
+#soft grffile
+#soft latex
+#soft ltxcmds
+#soft paralist
+#soft pgf
+#soft soul
+#soft tools
+#soft url
+#soft verse



More information about the tex-live-commits mailing list.