summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/luatex
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2022-10-01 19:49:44 +0000
committerKarl Berry <karl@freefriends.org>2022-10-01 19:49:44 +0000
commit90ce0701efdeb477c5832baea015d4c20b99d3ee (patch)
tree18315e142e806f8c634358c2750a56ba9514da8a /Master/texmf-dist/tex/luatex
parent0103a9d41473d542650463023e5ac4d4eb18c5ff (diff)
markdown (1oct22)
git-svn-id: svn://tug.org/texlive/trunk@64570 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex')
-rw-r--r--Master/texmf-dist/tex/luatex/markdown/markdown.lua666
1 files changed, 379 insertions, 287 deletions
diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua
index 79b875ea469..2081d0d3ff5 100644
--- a/Master/texmf-dist/tex/luatex/markdown/markdown.lua
+++ b/Master/texmf-dist/tex/luatex/markdown/markdown.lua
@@ -58,7 +58,7 @@
-- those in the standard .ins files.
--
local metadata = {
- version = "2.16.1-0-gf8a4bea",
+ version = "2.17.0-0-g6428569",
comment = "A module for the conversion from markdown to plain TeX",
author = "John MacFarlane, Hans Hagen, Vít Novotný",
copyright = {"2009-2016 John MacFarlane, Hans Hagen",
@@ -79,6 +79,38 @@ local unicode
end)()
local md5 = require("md5")
local M = {metadata = metadata}
+local walkable_syntax = {
+ Block = {
+ "Blockquote",
+ "Verbatim",
+ "HorizontalRule",
+ "BulletList",
+ "OrderedList",
+ "Heading",
+ "DisplayHtml",
+ "Paragraph",
+ "Plain",
+ },
+ Inline = {
+ "Str",
+ "Space",
+ "Endline",
+ "UlOrStarLine",
+ "Strong",
+ "Emph",
+ "Link",
+ "Image",
+ "Code",
+ "AutoLinkUrl",
+ "AutoLinkEmail",
+ "AutoLinkRelativeReference",
+ "InlineHtml",
+ "HtmlEntity",
+ "EscapedChar",
+ "Smart",
+ "Symbol",
+ },
+}
local defaultOptions = {}
defaultOptions.cacheDir = "."
defaultOptions.frozenCacheFileName = "frozenCache.tex"
@@ -93,6 +125,9 @@ defaultOptions.contentBlocks = false
defaultOptions.contentBlocksLanguageMap = "markdown-languages.json"
defaultOptions.definitionLists = false
defaultOptions.eagerCache = true
+metadata.user_extension_api_version = 1
+metadata.grammar_version = 1
+defaultOptions.extensions = {}
defaultOptions.expectJekyllData = false
defaultOptions.fancyLists = false
defaultOptions.fencedCode = false
@@ -138,7 +173,7 @@ function util.cache(dir, string, salt, transform, suffix)
local file = io.open(name, "r")
if file == nil then -- If no cache entry exists, then create a new one.
file = assert(io.open(name, "w"),
- [[could not open file "]] .. name .. [[" for writing]])
+ [[Could not open file "]] .. name .. [[" for writing]])
local result = string
if transform ~= nil then
result = transform(result)
@@ -153,6 +188,20 @@ function util.table_copy(t)
for k, v in pairs(t) do u[k] = v end
return setmetatable(u, getmetatable(t))
end
+util.lookup_files = (function()
+ local ran_ok, kpse = pcall(require, "kpse")
+ if ran_ok then
+ kpse.set_program_name("luatex")
+ else
+ kpse = { lookup = function(f, _) return f end }
+ end
+
+ local function lookup_files(f, options)
+ return kpse.lookup(f, options)
+ end
+
+ return lookup_files
+end)()
function util.expand_tabs_in_line(s, tabstop)
local tab = tabstop or 4
local corr = 0
@@ -3097,7 +3146,7 @@ local function strip_atx_end(s)
return s:gsub("[#%s]*\n$","")
end
M.reader = {}
-function M.reader.new(writer, options, extensions)
+function M.reader.new(writer, options)
local self = {}
self.writer = writer
self.options = options
@@ -3573,236 +3622,234 @@ function M.reader.new(writer, options, extensions)
/ writer.heading
parsers.Heading = parsers.AtxHeading + parsers.SetextHeading
- self.syntax =
- { "Blocks",
-
- Blocks = ( V("ExpectedJekyllData")
- * (V("Blank")^0 / writer.interblocksep)
- )^-1
- * V("Blank")^0
- * V("Block")^-1
- * (V("Blank")^0 / writer.interblocksep
- * V("Block"))^0
- * V("Blank")^0 * parsers.eof,
-
- Blank = parsers.Blank,
-
- UnexpectedJekyllData = parsers.fail,
- ExpectedJekyllData = parsers.fail,
-
- Block = V("ContentBlock")
- + V("UnexpectedJekyllData")
- + V("Blockquote")
- + V("PipeTable")
- + V("Verbatim")
- + V("FencedCode")
- + V("HorizontalRule")
- + V("BulletList")
- + V("OrderedList")
- + V("Heading")
- + V("DefinitionList")
- + V("DisplayHtml")
- + V("Paragraph")
- + V("Plain"),
-
- ContentBlock = parsers.fail,
- Blockquote = parsers.Blockquote,
- Verbatim = parsers.Verbatim,
- FencedCode = parsers.fail,
- HorizontalRule = parsers.HorizontalRule,
- BulletList = parsers.BulletList,
- OrderedList = parsers.OrderedList,
- Heading = parsers.Heading,
- DefinitionList = parsers.fail,
- DisplayHtml = parsers.DisplayHtml,
- Paragraph = parsers.Paragraph,
- PipeTable = parsers.fail,
- Plain = parsers.Plain,
- EndlineExceptions = parsers.EndlineExceptions,
-
- Inline = V("Str")
- + V("Space")
- + V("Endline")
- + V("UlOrStarLine")
- + V("Strong")
- + V("Emph")
- + V("StrikeThrough")
- + V("Superscript")
- + V("Subscript")
- + V("InlineNote")
- + V("NoteRef")
- + V("Citations")
- + V("Link")
- + V("Image")
- + V("Code")
- + V("AutoLinkUrl")
- + V("AutoLinkEmail")
- + V("AutoLinkRelativeReference")
- + V("InlineHtml")
- + V("HtmlEntity")
- + V("EscapedChar")
- + V("Smart")
- + V("Symbol"),
-
- IndentedInline = V("Str")
- + V("OptionalIndent")
- + V("Endline")
- + V("UlOrStarLine")
- + V("Strong")
- + V("Emph")
- + V("StrikeThrough")
- + V("Superscript")
- + V("Subscript")
- + V("InlineNote")
- + V("NoteRef")
- + V("Citations")
- + V("Link")
- + V("Image")
- + V("Code")
- + V("AutoLinkUrl")
- + V("AutoLinkEmail")
- + V("AutoLinkRelativeReference")
- + V("InlineHtml")
- + V("HtmlEntity")
- + V("EscapedChar")
- + V("Smart")
- + V("Symbol"),
-
- Str = parsers.Str,
- Space = parsers.Space,
- OptionalIndent = parsers.OptionalIndent,
- Endline = parsers.Endline,
- UlOrStarLine = parsers.UlOrStarLine,
- Strong = parsers.Strong,
- Emph = parsers.Emph,
- StrikeThrough = parsers.fail,
- Superscript = parsers.fail,
- Subscript = parsers.fail,
- InlineNote = parsers.fail,
- NoteRef = parsers.fail,
- Citations = parsers.fail,
- Link = parsers.Link,
- Image = parsers.Image,
- 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,
- }
- local special_characters = {"*", "`", "[", "]", "<", "!", "\\"}
- self.add_special_character = function(c)
- table.insert(special_characters, c)
- self.syntax.SpecialChar = S(table.concat(special_characters, ""))
- end
- self.syntax.SpecialChar = S(table.concat(special_characters, ""))
- for _, extension in ipairs(extensions) do
- extension.extend_writer(writer)
- extension.extend_reader(self)
- end
+ 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)
+ self.insert_pattern = function(selector, pattern)
+ 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]])
+ if pos == "instead of" then
+ rule[index] = pattern
+ else
+ table.insert(rule, index, pattern)
+ end
+ end
+ local syntax =
+ { "Blocks",
+
+ Blocks = ( V("ExpectedJekyllData")
+ * (V("Blank")^0 / writer.interblocksep)
+ )^-1
+ * V("Blank")^0
+ * V("Block")^-1
+ * (V("Blank")^0 / writer.interblocksep
+ * V("Block"))^0
+ * V("Blank")^0 * parsers.eof,
+
+ ExpectedJekyllData = parsers.fail,
+
+ Blank = parsers.Blank,
+
+ Blockquote = parsers.Blockquote,
+ Verbatim = parsers.Verbatim,
+ HorizontalRule = parsers.HorizontalRule,
+ BulletList = parsers.BulletList,
+ OrderedList = parsers.OrderedList,
+ Heading = parsers.Heading,
+ DisplayHtml = parsers.DisplayHtml,
+ Paragraph = parsers.Paragraph,
+ Plain = parsers.Plain,
+ EndlineExceptions = parsers.EndlineExceptions,
+
+ Str = parsers.Str,
+ Space = parsers.Space,
+ OptionalIndent = parsers.OptionalIndent,
+ Endline = parsers.Endline,
+ UlOrStarLine = parsers.UlOrStarLine,
+ Strong = parsers.Strong,
+ Emph = parsers.Emph,
+ Link = parsers.Link,
+ Image = parsers.Image,
+ 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,
+ }
+ self.update_rule = function(rule_name, pattern)
+ assert(syntax[rule_name] ~= nil,
+ [[Rule ]] .. rule_name .. [[ -> ... does not exist in markdown grammar]])
+ walkable_syntax[rule_name] = { pattern }
+ end
+ local special_characters = {}
+ self.add_special_character = function(c)
+ table.insert(special_characters, c)
+ syntax.SpecialChar = S(table.concat(special_characters, ""))
+ end
- if options.underscores then
- self.add_special_character("_")
- 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.add_special_character("\\")
+ for _, extension in ipairs(extensions) do
+ extension.extend_writer(writer)
+ extension.extend_reader(self)
+ end
+ walkable_syntax["IndentedInline"] = util.table_copy(
+ walkable_syntax["Inline"])
+ self.insert_pattern(
+ "IndentedInline instead of Space",
+ "OptionalIndent")
+ for lhs, rule in pairs(walkable_syntax) do
+ syntax[lhs] = parsers.fail
+ for _, rhs in ipairs(rule) do
+ local pattern
+ if type(rhs) == "string" then
+ pattern = V(rhs)
+ else
+ pattern = rhs
+ end
+ syntax[lhs] = syntax[lhs] + pattern
+ end
+ end
+ if options.underscores then
+ self.add_special_character("_")
+ end
- if not options.codeSpans then
- self.syntax.Code = parsers.fail
- end
+ if not options.codeSpans then
+ syntax.Code = parsers.fail
+ end
- if not options.html then
- self.syntax.DisplayHtml = parsers.fail
- self.syntax.InlineHtml = parsers.fail
- self.syntax.HtmlEntity = 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 options.preserveTabs then
+ options.stripIndent = false
+ end
- if not options.smartEllipses then
- self.syntax.Smart = parsers.fail
- else
- self.add_special_character(".")
- end
+ if not options.smartEllipses then
+ syntax.Smart = parsers.fail
+ else
+ self.add_special_character(".")
+ end
- if not options.relativeReferences then
- self.syntax.AutoLinkRelativeReference = parsers.fail
- end
+ if not options.relativeReferences then
+ syntax.AutoLinkRelativeReference = parsers.fail
+ end
- local blocks_nested_t = util.table_copy(self.syntax)
- blocks_nested_t.ExpectedJekyllData = parsers.fail
- parsers.blocks_nested = Ct(blocks_nested_t)
-
- parsers.blocks = Ct(self.syntax)
-
- local inlines_t = util.table_copy(self.syntax)
- inlines_t[1] = "Inlines"
- inlines_t.Inlines = parsers.Inline^0 * (parsers.spacing^0 * parsers.eof / "")
- parsers.inlines = Ct(inlines_t)
-
- local inlines_no_link_t = util.table_copy(inlines_t)
- inlines_no_link_t.Link = parsers.fail
- parsers.inlines_no_link = Ct(inlines_no_link_t)
-
- local inlines_no_inline_note_t = util.table_copy(inlines_t)
- inlines_no_inline_note_t.InlineNote = parsers.fail
- parsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t)
-
- 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)
- function self.convert(input)
- references = {}
- local opt_string = {}
- for k,_ in pairs(defaultOptions) do
- local v = options[k]
- if k ~= "cacheDir" then
- opt_string[#opt_string+1] = k .. "=" .. tostring(v)
+ 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 = parsers.Inline^0 * (parsers.spacing^0 * parsers.eof / "")
+ parsers.inlines = Ct(inlines_t)
+
+ local inlines_no_link_t = util.table_copy(inlines_t)
+ inlines_no_link_t.Link = parsers.fail
+ parsers.inlines_no_link = Ct(inlines_no_link_t)
+
+ local inlines_no_inline_note_t = util.table_copy(inlines_t)
+ inlines_no_inline_note_t.InlineNote = parsers.fail
+ parsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t)
+
+ 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)
+ return function(input)
+ 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
- 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)
- return util.rope_to_string(writer.document(document))
- 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"
+ 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)
+ return util.rope_to_string(writer.document(document))
+ 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
- mode = "w"
+ output = convert(input)
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())
+ 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
- return output
end
return self
end
@@ -3838,7 +3885,6 @@ M.extensions.citations = function(citation_nbsps)
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local citation_chars
@@ -3946,7 +3992,7 @@ M.extensions.citations = function(citation_nbsps)
local Citations = TextCitations + ParenthesizedCitations
- syntax.Citations = Citations
+ self.insert_pattern("Inline after Emph", Citations)
self.add_special_character("@")
self.add_special_character("-")
@@ -3955,21 +4001,14 @@ M.extensions.citations = function(citation_nbsps)
end
M.extensions.content_blocks = function(language_map)
local languages_json = (function()
- local ran_ok, kpse = pcall(require, "kpse")
- if ran_ok then
- kpse.set_program_name("luatex")
- else
- kpse = {lookup=function(filename, _) return filename end}
- end
local base, prev, curr
- for _, filename in ipairs{kpse.lookup(language_map, { all=true })} do
- local file = io.open(filename, "r")
+ for _, pathname in ipairs{util.lookup_files(language_map, { all=true })} do
+ local file = io.open(pathname, "r")
if not file then goto continue end
- local json = file:read("*all"):gsub('("[^\n]-"):','[%1]=')
- curr = (function()
- local _ENV={ json=json, load=load } -- luacheck: ignore _ENV
- return load("return "..json)()
- 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
@@ -4009,7 +4048,6 @@ M.extensions.content_blocks = function(language_map)
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local contentblock_tail
@@ -4083,7 +4121,7 @@ M.extensions.content_blocks = function(language_map)
* contentblock_tail
/ writer.contentblock
- syntax.ContentBlock = ContentBlock
+ self.insert_pattern("Block before Blockquote", ContentBlock)
end
}
end
@@ -4116,7 +4154,6 @@ M.extensions.definition_lists = function(tight_lists)
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local defstartchar = S("~:")
@@ -4159,7 +4196,7 @@ M.extensions.definition_lists = function(tight_lists)
* -DefinitionListItemLoose * Cc(true))
) / writer.definitionlist
- syntax.DefinitionList = DefinitionList
+ self.insert_pattern("Block after Heading", DefinitionList)
end
}
end
@@ -4176,7 +4213,6 @@ M.extensions.fenced_code = function(blank_before_code_fence)
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local function captures_geq_length(_,i,a,b)
@@ -4239,7 +4275,8 @@ M.extensions.fenced_code = function(blank_before_code_fence)
self.expandtabs(code))
end
- syntax.FencedCode = FencedCode
+ self.insert_pattern("Block after Verbatim",
+ FencedCode)
local fencestart
if blank_before_code_fence then
@@ -4249,8 +4286,8 @@ M.extensions.fenced_code = function(blank_before_code_fence)
+ fencehead(parsers.tilde)
end
- parsers.EndlineExceptions = parsers.EndlineExceptions + fencestart
- syntax.EndlineExceptions = parsers.EndlineExceptions
+ local EndlineExceptions = parsers.EndlineExceptions + fencestart
+ self.update_rule("EndlineExceptions", EndlineExceptions)
self.add_special_character("~")
end
@@ -4265,9 +4302,16 @@ M.extensions.footnotes = function(footnotes, inline_footnotes)
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
+ if inline_footnotes then
+ local InlineNote
+ = parsers.circumflex
+ * (parsers.tag / self.parser_functions.parse_inlines_no_inline_note)
+ / writer.note
+
+ self.insert_pattern("Inline after Emph", InlineNote)
+ end
if footnotes then
local function strip_first_char(s)
return s:sub(2)
@@ -4305,17 +4349,10 @@ M.extensions.footnotes = function(footnotes, inline_footnotes)
* parsers.spnl * parsers.indented_blocks(parsers.chunk)
/ register_note
- parsers.Blank = NoteBlock + parsers.Blank
- syntax.Blank = parsers.Blank
+ local Blank = NoteBlock + parsers.Blank
+ self.update_rule("Blank", Blank)
- syntax.NoteRef = NoteRef
- end
- if inline_footnotes then
- local InlineNote
- = parsers.circumflex
- * (parsers.tag / self.parser_functions.parse_inlines_no_inline_note)
- / writer.note
- syntax.InlineNote = InlineNote
+ self.insert_pattern("Inline after Emph", NoteRef)
end
self.add_special_character("^")
@@ -4327,7 +4364,6 @@ M.extensions.header_attributes = function()
extend_writer = function()
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
parsers.AtxHeading = Cg(parsers.HeadingStart,"level")
@@ -4372,8 +4408,8 @@ M.extensions.header_attributes = function()
* parsers.newline
/ writer.heading
- parsers.Heading = parsers.AtxHeading + parsers.SetextHeading
- syntax.Heading = parsers.Heading
+ local Heading = parsers.AtxHeading + parsers.SetextHeading
+ self.update_rule("Heading", Heading)
end
}
end
@@ -4464,7 +4500,6 @@ M.extensions.jekyll_data = function(expect_jekyll_data)
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local JekyllData
@@ -4500,9 +4535,9 @@ M.extensions.jekyll_data = function(expect_jekyll_data)
* JekyllData
* (P("---") + P("..."))^-1
- syntax.UnexpectedJekyllData = UnexpectedJekyllData
+ self.insert_pattern("Block before Blockquote", UnexpectedJekyllData)
if expect_jekyll_data then
- syntax.ExpectedJekyllData = ExpectedJekyllData
+ self.update_rule("ExpectedJekyllData", ExpectedJekyllData)
end
end
}
@@ -4588,7 +4623,6 @@ M.extensions.pipe_tables = function(table_captions)
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local table_hline_separator = parsers.pipe + parsers.plus
@@ -4643,7 +4677,7 @@ M.extensions.pipe_tables = function(table_captions)
* table_caption^-1
/ writer.table
- syntax.PipeTable = PipeTable
+ self.insert_pattern("Block after Blockquote", PipeTable)
end
}
end
@@ -4655,7 +4689,6 @@ M.extensions.strike_through = function()
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local StrikeThrough = (
@@ -4663,7 +4696,7 @@ M.extensions.strike_through = function()
parsers.doubletildes)
) / writer.strike_through
- syntax.StrikeThrough = StrikeThrough
+ self.insert_pattern("Inline after Emph", StrikeThrough)
self.add_special_character("~")
end
@@ -4677,14 +4710,13 @@ M.extensions.superscripts = function()
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local Superscript = (
parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex)
) / writer.superscript
- syntax.Superscript = Superscript
+ self.insert_pattern("Inline after Emph", Superscript)
self.add_special_character("^")
end
@@ -4698,14 +4730,13 @@ M.extensions.subscripts = function()
end
end, extend_reader = function(self)
local parsers = self.parsers
- local syntax = self.syntax
local writer = self.writer
local Subscript = (
parsers.between(parsers.Str, parsers.tilde, parsers.tilde)
) / writer.subscript
- syntax.Subscript = Subscript
+ self.insert_pattern("Inline after Emph", Subscript)
self.add_special_character("~")
end
@@ -4748,7 +4779,6 @@ M.extensions.fancy_lists = function()
end, extend_reader = function(self)
local parsers = self.parsers
local options = self.options
- local syntax = self.syntax
local writer = self.writer
local label = parsers.dig + parsers.letter
@@ -4864,8 +4894,7 @@ M.extensions.fancy_lists = function()
* Cc(false) * parsers.skipblanklines
) * Cb("listtype") / fancylist
- syntax.OrderedList = FancyList
-
+ self.update_rule("OrderedList", FancyList)
end
}
end
@@ -4875,11 +4904,6 @@ function M.new(options)
return defaultOptions[key] end })
local extensions = {}
- if options.citations then
- local citations_extension = M.extensions.citations(options.citationNbsps)
- table.insert(extensions, citations_extension)
- end
-
if options.contentBlocks then
local content_blocks_extension = M.extensions.content_blocks(
options.contentBlocksLanguageMap)
@@ -4898,12 +4922,6 @@ function M.new(options)
table.insert(extensions, fenced_code_extension)
end
- if options.footnotes or options.inlineFootnotes then
- local footnotes_extension = M.extensions.footnotes(
- options.footnotes, options.inlineFootnotes)
- table.insert(extensions, footnotes_extension)
- end
-
if options.headerAttributes then
local header_attributes_extension = M.extensions.header_attributes()
table.insert(extensions, header_attributes_extension)
@@ -4936,15 +4954,89 @@ function M.new(options)
table.insert(extensions, superscript_extension)
end
+ if options.footnotes or options.inlineFootnotes then
+ local footnotes_extension = M.extensions.footnotes(
+ options.footnotes, options.inlineFootnotes)
+ table.insert(extensions, footnotes_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 = util.lookup_files(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 == 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 = {
+ 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, extensions)
+ local reader = M.reader.new(writer, options)
+ local convert = reader.finalize_grammar(extensions)
- return reader.convert
+ return convert
end
return M